Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
d86aea2
feature/OBPv5.1.0 added cardaro payment
hongwei1 Jul 17, 2025
76ec1aa
refactor/enhanced the error handling for NewStyle.tryons method
hongwei1 Jul 22, 2025
2f78d9a
refactor/comment the soap in pom
hongwei1 Jul 22, 2025
47a2e6b
feature/add Cardano and Lovelace currency entries to ISO currency codes
hongwei1 Jul 22, 2025
5a0083a
refactor/improve error handling in createCounterparty method
hongwei1 Jul 22, 2025
0557efb
refactor/update InvalidISOCurrencyCode error message for clarity
hongwei1 Jul 22, 2025
1bc1014
feature/add support for Cardano transactions and enhance transaction …
hongwei1 Jul 24, 2025
9b0b49d
refactor/improve error handling and logging in makePaymentv210 method
hongwei1 Jul 24, 2025
9883c14
feature/add detailed validation for Cardano payment processing and en…
hongwei1 Jul 29, 2025
c169f4d
feature/add support for building payments array and metadata for Card…
hongwei1 Jul 29, 2025
eecadf4
feature/require amount in Cardano payment structure and enhance valid…
hongwei1 Jul 29, 2025
26f12ce
feature/add support for Cardano transaction requests and enhance tran…
hongwei1 Jul 29, 2025
dd61704
refactor/Update Jakarta Activation dependency for Java 11 compatibility
hongwei1 Aug 1, 2025
68a2089
Merge remote-tracking branch 'refs/remotes/Hongwei/bugfix/addedMailSe…
hongwei1 Aug 1, 2025
ec71be2
refactor/Update Jakarta Activation and add Jakarta Mail dependencies …
hongwei1 Aug 1, 2025
b4deaa1
Merge remote-tracking branch 'refs/remotes/Hongwei/bugfix/addedMailSe…
hongwei1 Aug 1, 2025
4fb6ce2
Merge remote-tracking branch 'Simon/develop' into develop
hongwei1 Aug 18, 2025
c67a0ba
refactor/improve error handling in LocalMappedConnector for account n…
hongwei1 Aug 18, 2025
a893eb0
refactor/update .gitignore to include additional project-specific fil…
hongwei1 Aug 20, 2025
eeb53b8
Merge branch 'develop-Simon' into develop
hongwei1 Aug 20, 2025
96ed2bf
Merge remote-tracking branch 'Simon/develop' into feature/cardanoTest
hongwei1 Aug 20, 2025
7f50400
refactor/ update dependencies in pom.xml and clean up NewStyle.scala
hongwei1 Aug 20, 2025
bdb3f7c
refactor/ convert asset_name to hex format in CardanoConnector_vJun2025
hongwei1 Aug 21, 2025
7c73150
refactor/ convert asset_name to hex format in CardanoConnector_vJun2025
hongwei1 Aug 21, 2025
73d7cd3
feature/ introduce v6.0.0 API version with Cardano transaction reques…
hongwei1 Aug 21, 2025
55047c9
Merge remote-tracking branch 'Hongwei/feature/cardanoTest' into featu…
hongwei1 Aug 21, 2025
1c53327
feature/enhance v6.0.0 API with new Cardano transaction request handling
hongwei1 Aug 21, 2025
8393040
refactor/ enhance isAutoValidate logic in OBPRestHelper to support v4…
hongwei1 Aug 21, 2025
b07d080
refactor/ implement unit tests for isAutoValidate method in OBPRestHe…
hongwei1 Aug 21, 2025
d6c01e4
Merge remote-tracking branch 'Simon/develop' into feature/cardanoTest
hongwei1 Aug 25, 2025
e5f2f78
Refactor/ Revert MappedTransactionRequest fields length
hongwei1 Aug 25, 2025
8e41be8
refactor/Add migration for altering MappedTransactionRequest fields l…
hongwei1 Aug 25, 2025
b4bfc70
refactor/Enhance CardanoTransactionRequestTest with additional import…
hongwei1 Aug 25, 2025
196cc8c
refactor/Remove commented-out Cardano transaction handling code in Lo…
hongwei1 Aug 25, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,10 @@ obp-api/src/main/scala/code/api/v3_0_0/custom/

# Marketing diagram generation outputs
marketing_diagram_generation/outputs/*

.bloop
.bsp
.specstory
project/project
coursier
*.code-workspace
5 changes: 3 additions & 2 deletions obp-api/src/main/scala/bootstrap/liftweb/Boot.scala
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ import code.accountattribute.MappedAccountAttribute
import code.accountholders.MapperAccountHolders
import code.actorsystem.ObpActorSystem
import code.api.Constant._
import code.api.ResourceDocs1_4_0.ResourceDocs300.{ResourceDocs310, ResourceDocs400, ResourceDocs500, ResourceDocs510}
import code.api.ResourceDocs1_4_0.ResourceDocs300.{ResourceDocs310, ResourceDocs400, ResourceDocs500, ResourceDocs510, ResourceDocs600}
import code.api.ResourceDocs1_4_0._
import code.api._
import code.api.attributedefinition.AttributeDefinition
Expand All @@ -46,7 +46,6 @@ import code.api.util.ApiRole.CanCreateEntitlementAtAnyBank
import code.api.util.ErrorMessages.MandatoryPropertyIsNotSet
import code.api.util._
import code.api.util.migration.Migration
import code.api.util.CommonsEmailWrapper
import code.api.util.migration.Migration.DbFunction
import code.apicollection.ApiCollection
import code.apicollectionendpoint.ApiCollectionEndpoint
Expand Down Expand Up @@ -467,6 +466,7 @@ class Boot extends MdcLoggable {
enableVersionIfAllowed(ApiVersion.v4_0_0)
enableVersionIfAllowed(ApiVersion.v5_0_0)
enableVersionIfAllowed(ApiVersion.v5_1_0)
enableVersionIfAllowed(ApiVersion.v6_0_0)
enableVersionIfAllowed(ApiVersion.`dynamic-endpoint`)
enableVersionIfAllowed(ApiVersion.`dynamic-entity`)

Expand Down Expand Up @@ -525,6 +525,7 @@ class Boot extends MdcLoggable {
LiftRules.statelessDispatch.append(ResourceDocs400)
LiftRules.statelessDispatch.append(ResourceDocs500)
LiftRules.statelessDispatch.append(ResourceDocs510)
LiftRules.statelessDispatch.append(ResourceDocs600)
////////////////////////////////////////////////////


Expand Down
45 changes: 29 additions & 16 deletions obp-api/src/main/scala/code/api/OBPRestHelper.scala
Original file line number Diff line number Diff line change
Expand Up @@ -27,36 +27,32 @@ TESOBE (http://www.tesobe.com/)

package code.api

import java.net.URLDecoder
import code.api.Constant._
import code.api.OAuthHandshake._
import code.api.builder.AccountInformationServiceAISApi.APIMethods_AccountInformationServiceAISApi
import code.api.util.APIUtil.{getClass, _}
import code.api.util.APIUtil._
import code.api.util.ErrorMessages.{InvalidDAuthHeaderToken, UserIsDeleted, UsernameHasBeenLocked, attemptedToOpenAnEmptyBox}
import code.api.util._
import code.api.v3_0_0.APIMethods300
import code.api.v3_1_0.APIMethods310
import code.api.v4_0_0.{APIMethods400, OBPAPI4_0_0}
import code.api.v4_0_0.OBPAPI4_0_0
import code.api.v5_0_0.OBPAPI5_0_0
import code.api.v5_1_0.OBPAPI5_1_0
import code.api.v6_0_0.OBPAPI6_0_0
import code.loginattempts.LoginAttempt
import code.model.dataAccess.AuthUser
import code.util.Helper.{MdcLoggable, ObpS}
import com.alibaba.ttl.TransmittableThreadLocal
import com.openbankproject.commons.model.ErrorMessage
import com.openbankproject.commons.util.{ApiVersion, ReflectUtils, ScannedApiVersion}
import net.liftweb.common.{Box, Full, _}
import net.liftweb.common._
import net.liftweb.http.rest.RestHelper
import net.liftweb.http.{JsonResponse, LiftResponse, LiftRules, Req, S, TransientRequestMemoize}
import net.liftweb.json.Extraction
import net.liftweb.json.JsonAST.JValue
import net.liftweb.util.{Helpers, NamedPF, Props, ThreadGlobal}
import net.liftweb.util.Helpers.tryo
import net.liftweb.util.{Helpers, NamedPF, Props, ThreadGlobal}

import java.net.URLDecoder
import java.util.{Locale, ResourceBundle}
import scala.collection.immutable.List
import scala.collection.mutable.ArrayBuffer
import scala.math.Ordering
import scala.util.control.NoStackTrace
import scala.xml.{Node, NodeSeq}

Expand Down Expand Up @@ -642,23 +638,40 @@ trait OBPRestHelper extends RestHelper with MdcLoggable {
result
}

def isAutoValidate(doc: ResourceDoc, autoValidateAll: Boolean): Boolean = { //note: auto support v4.0.0 and later versions
doc.isValidateEnabled || (autoValidateAll && !doc.isValidateDisabled && {
// Auto support v4.0.0 and all later versions
val docVersion = doc.implementedInApiVersion
// Check if the version is v4.0.0 or later by comparing the version string
docVersion match {
case v: ScannedApiVersion =>
// Extract version numbers and compare
val versionStr = v.apiShortVersion.replace("v", "")
val parts = versionStr.split("\\.")
if (parts.length >= 2) {
val major = parts(0).toInt
val minor = parts(1).toInt
major > 4 || (major == 4 && minor >= 0)
} else {
false
}
case _ => false
}
})
}

protected def registerRoutes(routes: List[OBPEndpoint],
allResourceDocs: ArrayBuffer[ResourceDoc],
apiPrefix:OBPEndpoint => OBPEndpoint,
autoValidateAll: Boolean = false): Unit = {

def isAutoValidate(doc: ResourceDoc): Boolean = { //note: only support v5.1.0, v5.0.0 and v4.0.0 at the moment.
doc.isValidateEnabled || (autoValidateAll && !doc.isValidateDisabled && List(OBPAPI5_1_0.version,OBPAPI5_0_0.version,OBPAPI4_0_0.version).contains(doc.implementedInApiVersion))
}

for(route <- routes) {
// one endpoint can have multiple ResourceDocs, so here use filter instead of find, e.g APIMethods400.Implementations400.createTransactionRequest
val resourceDocs = allResourceDocs.filter(_.partialFunction == route)

if(resourceDocs.isEmpty) {
oauthServe(apiPrefix(route), None)
} else {
val (autoValidateDocs, other) = resourceDocs.partition(isAutoValidate)
val (autoValidateDocs, other) = resourceDocs.partition(isAutoValidate(_, autoValidateAll))
// autoValidateAll or doc isAutoValidate, just wrapped to auth check endpoint
autoValidateDocs.foreach { doc =>
val wrappedEndpoint = doc.wrappedWithAuthCheck(route)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package code.api.ResourceDocs1_4_0

import code.api.OBPRestHelper
import com.openbankproject.commons.util.{ApiVersion,ApiVersionStatus}
import code.util.Helper.MdcLoggable
import com.openbankproject.commons.util.{ApiVersion, ApiVersionStatus}


object ResourceDocs140 extends OBPRestHelper with ResourceDocsAPIMethods with MdcLoggable {
Expand Down Expand Up @@ -136,5 +136,21 @@ object ResourceDocs300 extends OBPRestHelper with ResourceDocsAPIMethods with Md
})
})
}

object ResourceDocs600 extends OBPRestHelper with ResourceDocsAPIMethods with MdcLoggable {
val version: ApiVersion = ApiVersion.v6_0_0
val versionStatus = ApiVersionStatus.BLEEDING_EDGE.toString
val routes = List(
ImplementationsResourceDocs.getResourceDocsObpV400,
ImplementationsResourceDocs.getResourceDocsSwagger,
ImplementationsResourceDocs.getBankLevelDynamicResourceDocsObp,
// ImplementationsResourceDocs.getStaticResourceDocsObp
)
routes.foreach(route => {
oauthServe(apiPrefix {
route
})
})
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import code.api.v3_1_0._
import code.api.v4_0_0._
import code.api.v5_0_0._
import code.api.v5_1_0._
import code.api.v6_0_0._
import code.branches.Branches.{Branch, DriveUpString, LobbyString}
import code.connectormethod.{JsonConnectorMethod, JsonConnectorMethodMethodBody}
import code.consent.ConsentStatus
Expand Down Expand Up @@ -5697,6 +5698,47 @@ object SwaggerDefinitionsJSON {
permission_name = CAN_GRANT_ACCESS_TO_VIEWS,
extra_data = Some(List(SYSTEM_ACCOUNTANT_VIEW_ID, SYSTEM_AUDITOR_VIEW_ID))
)


lazy val cardanoPaymentJsonV600 = CardanoPaymentJsonV600(
address = "addr_test1qpv3se9ghq87ud29l0a8asy8nlqwd765e5zt4rc2z4mktqulwagn832cuzcjknfyxwzxz2p2kumx6n58tskugny6mrqs7fd12",
amount = CardanoAmountJsonV600(
quantity = 1000000,
unit = "lovelace"
),
assets = Some(List(CardanoAssetJsonV600(
policy_id = "policy1234567890abcdef",
asset_name = "4f47435241",
quantity = 10
)))
)

// Example for Send ADA with Token only (no ADA amount)
lazy val cardanoPaymentTokenOnlyJsonV510 = CardanoPaymentJsonV600(
address = "addr_test1qpv3se9ghq87ud29l0a8asy8nlqwd765e5zt4rc2z4mktqulwagn832cuzcjknfyxwzxz2p2kumx6n58tskugny6mrqs7fd12",
amount = CardanoAmountJsonV600(
quantity = 0,
unit = "lovelace"
),
assets = Some(List(CardanoAssetJsonV600(
policy_id = "policy1234567890abcdef",
asset_name = "4f47435241",
quantity = 10
)))
)

lazy val cardanoMetadataStringJsonV600 = CardanoMetadataStringJsonV600(
string = "Hello Cardano"
)

lazy val transactionRequestBodyCardanoJsonV600 = TransactionRequestBodyCardanoJsonV600(
to = cardanoPaymentJsonV600,
value = amountOfMoneyJsonV121,
passphrase = "password1234!",
description = descriptionExample.value,
metadata = Some(Map("202507022319" -> cardanoMetadataStringJsonV600))
)

//The common error or success format.
//Just some helper format to use in Json
case class NotSupportedYet()
Expand Down
4 changes: 2 additions & 2 deletions obp-api/src/main/scala/code/api/util/APIUtil.scala
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,6 @@ import code.util.{Helper, JsonSchemaUtil}
import code.views.system.AccountAccess
import code.views.{MapperViews, Views}
import code.webuiprops.MappedWebUiPropsProvider.getWebUiPropsValue
import javassist.CannotCompileException
import com.github.dwickern.macros.NameOf.{nameOf, nameOfType}
import com.openbankproject.commons.ExecutionContext.Implicits.global
import com.openbankproject.commons.model._
Expand All @@ -81,7 +80,7 @@ import com.openbankproject.commons.util.Functions.Implicits._
import com.openbankproject.commons.util._
import dispatch.url
import javassist.expr.{ExprEditor, MethodCall}
import javassist.{ClassPool, LoaderClassPath}
import javassist.{CannotCompileException, ClassPool, LoaderClassPath}
import net.liftweb.actor.LAFuture
import net.liftweb.common._
import net.liftweb.http._
Expand Down Expand Up @@ -2747,6 +2746,7 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{
case ApiVersion.v4_0_0 => LiftRules.statelessDispatch.append(v4_0_0.OBPAPI4_0_0)
case ApiVersion.v5_0_0 => LiftRules.statelessDispatch.append(v5_0_0.OBPAPI5_0_0)
case ApiVersion.v5_1_0 => LiftRules.statelessDispatch.append(v5_1_0.OBPAPI5_1_0)
case ApiVersion.v6_0_0 => LiftRules.statelessDispatch.append(v6_0_0.OBPAPI6_0_0)
case ApiVersion.`dynamic-endpoint` => LiftRules.statelessDispatch.append(OBPAPIDynamicEndpoint)
case ApiVersion.`dynamic-entity` => LiftRules.statelessDispatch.append(OBPAPIDynamicEntity)
case version: ScannedApiVersion => LiftRules.statelessDispatch.append(ScannedApis.versionMapScannedApis(version))
Expand Down
2 changes: 1 addition & 1 deletion obp-api/src/main/scala/code/api/util/ErrorMessages.scala
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ object ErrorMessages {
// General messages (OBP-10XXX)
val InvalidJsonFormat = "OBP-10001: Incorrect json format."
val InvalidNumber = "OBP-10002: Invalid Number. Could not convert value to a number."
val InvalidISOCurrencyCode = "OBP-10003: Invalid Currency Value. It should be three letters ISO Currency Code. "
val InvalidISOCurrencyCode = """OBP-10003: Invalid Currency Value. Expected a 3-letter ISO Currency Code (e.g., 'USD', 'EUR') or 'lovelace' for Cardano transactions.""".stripMargin
val FXCurrencyCodeCombinationsNotSupported = "OBP-10004: ISO Currency code combination not supported for FX. Please modify the FROM_CURRENCY_CODE or TO_CURRENCY_CODE. "
val InvalidDateFormat = "OBP-10005: Invalid Date Format. Could not convert value to a Date."
val InvalidCurrency = "OBP-10006: Invalid Currency Value."
Expand Down
40 changes: 30 additions & 10 deletions obp-api/src/main/scala/code/api/util/NewStyle.scala
Original file line number Diff line number Diff line change
Expand Up @@ -769,17 +769,37 @@ object NewStyle extends MdcLoggable{
def isEnabledTransactionRequests(callContext: Option[CallContext]): Future[Box[Unit]] = Helper.booleanToFuture(failMsg = TransactionRequestsNotEnabled, cc=callContext)(APIUtil.getPropsAsBoolValue("transactionRequests_enabled", false))

/**
* Wraps a Future("try") block around the function f and
* @param f - the block of code to evaluate
* @return <ul>
* <li>Future(result of the evaluation of f) if f doesn't throw any exception
* <li>a Failure if f throws an exception with message = failMsg and code = failCode
* </ul>
*/
def tryons[T](failMsg: String, failCode: Int = 400, callContext: Option[CallContext])(f: => T)(implicit m: Manifest[T]): Future[T]= {
* Wraps a computation `f` in a Future, capturing exceptions and returning detailed error messages.
*
* @param failMsg Base error message to return if the computation fails.
* @param failCode HTTP status code to return on failure (default: 400).
* @param callContext Optional call context for logging or metadata.
* @param f The computation to execute (call-by-name to defer evaluation).
* @param m Implicit Manifest for type `T` (handled by Scala compiler).
* @return Future[T] Success: Result of `f`; Failure: Detailed error message.
*/
def tryons[T](
failMsg: String,
failCode: Int = 400,
callContext: Option[CallContext]
)(f: => T)(implicit m: Manifest[T]): Future[T] = {
Future {
tryo {
f
try {
// Attempt to execute `f` and wrap the result in `Full` (success) or `Failure` (error)
tryo(f) match {
case Full(result) =>
Full(result) // Success: Forward the result
case Failure(msg, _, _) =>
// `tryo` encountered an exception (e.g., validation error)
Failure(s"$failMsg. Details: $msg", Empty, Empty)
case Empty =>
// Edge case: Empty result (unlikely but handled defensively)
Failure(s"$failMsg. Details: Empty result", Empty, Empty)
}
} catch {
case e: Exception =>
// Directly caught exception (e.g., JSON parsing error)
Failure(s"$failMsg. Details: ${e.getMessage}", Full(e), Empty)
}
} map {
x => unboxFullOrFail(x, callContext, failMsg, failCode)
Expand Down
14 changes: 14 additions & 0 deletions obp-api/src/main/scala/code/api/util/migration/Migration.scala
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ object Migration extends MdcLoggable {
addFastFirehoseAccountsView(startedBeforeSchemifier)
addFastFirehoseAccountsMaterializedView(startedBeforeSchemifier)
alterUserAuthContextColumnKeyAndValueLength(startedBeforeSchemifier)
alterMappedTransactionRequestFieldsLengthMigration(startedBeforeSchemifier)
dropIndexAtColumnUsernameAtTableAuthUser(startedBeforeSchemifier)
dropIndexAtUserAuthContext()
alterWebhookColumnUrlLength()
Expand Down Expand Up @@ -403,6 +404,19 @@ object Migration extends MdcLoggable {
}
}
}

private def alterMappedTransactionRequestFieldsLengthMigration(startedBeforeSchemifier: Boolean): Boolean = {
if(startedBeforeSchemifier == true) {
logger.warn(s"Migration.database.alterMappedTransactionRequestFieldsLengthMigration(true) cannot be run before Schemifier.")
true
} else {
val name = nameOf(alterMappedTransactionRequestFieldsLengthMigration(startedBeforeSchemifier))
runOnce(name) {
MigrationOfMappedTransactionRequestFieldsLength.alterMappedTransactionRequestFieldsLength(name)
}
}
}

private def dropIndexAtColumnUsernameAtTableAuthUser(startedBeforeSchemifier: Boolean): Boolean = {
if(startedBeforeSchemifier == true) {
logger.warn(s"Migration.database.dropIndexAtColumnUsernameAtTableAuthUser(true) cannot be run before Schemifier.")
Expand Down
Loading