From be361cd89931fd26ba72cfb6111b4bec9b599d9b Mon Sep 17 00:00:00 2001 From: hongwei Date: Sun, 15 Dec 2024 22:21:56 +0100 Subject: [PATCH] feature/V510 added new endpoint getTransactionRequests --- .../SwaggerDefinitionsJSON.scala | 3 +- .../main/scala/code/api/util/NewStyle.scala | 2 +- .../code/api/v4_0_0/JSONFactory4.0.0.scala | 12 +++- .../scala/code/api/v5_1_0/APIMethods510.scala | 72 +++++++++++++++++++ .../code/api/v5_1_0/JSONFactory5.1.0.scala | 61 +++++++++++++++- 5 files changed, 144 insertions(+), 6 deletions(-) diff --git a/obp-api/src/main/scala/code/api/ResourceDocs1_4_0/SwaggerDefinitionsJSON.scala b/obp-api/src/main/scala/code/api/ResourceDocs1_4_0/SwaggerDefinitionsJSON.scala index 29a6159df..cbfd3b61c 100644 --- a/obp-api/src/main/scala/code/api/ResourceDocs1_4_0/SwaggerDefinitionsJSON.scala +++ b/obp-api/src/main/scala/code/api/ResourceDocs1_4_0/SwaggerDefinitionsJSON.scala @@ -4864,7 +4864,8 @@ object SwaggerDefinitionsJSON { start_date = DateWithDayExampleObject, end_date = DateWithDayExampleObject, challenges = List(challengeJsonV400), - charge = transactionRequestChargeJsonV200 + charge = transactionRequestChargeJsonV200, + attributes=Some(List(bankAttributeBankResponseJsonV400)), ) val postSimpleCounterpartyJson400 = PostSimpleCounterpartyJson400( diff --git a/obp-api/src/main/scala/code/api/util/NewStyle.scala b/obp-api/src/main/scala/code/api/util/NewStyle.scala index 7ba8b99bb..963ab8426 100644 --- a/obp-api/src/main/scala/code/api/util/NewStyle.scala +++ b/obp-api/src/main/scala/code/api/util/NewStyle.scala @@ -3212,7 +3212,7 @@ object NewStyle extends MdcLoggable{ Connector.connector.vend.createTransactionRequestAttributes( bankId: BankId, transactionRequestId: TransactionRequestId, - transactionRequestAttributes: List[TransactionRequestAttributeTrait], + transactionRequestAttributes: List[TransactionRequestAttributeJsonV400], callContext: Option[CallContext] ) map { i => (connectorEmptyResponse(i._1, callContext), i._2) diff --git a/obp-api/src/main/scala/code/api/v4_0_0/JSONFactory4.0.0.scala b/obp-api/src/main/scala/code/api/v4_0_0/JSONFactory4.0.0.scala index 003c74904..fe681955a 100644 --- a/obp-api/src/main/scala/code/api/v4_0_0/JSONFactory4.0.0.scala +++ b/obp-api/src/main/scala/code/api/v4_0_0/JSONFactory4.0.0.scala @@ -137,7 +137,8 @@ case class TransactionRequestWithChargeJSON400( start_date: Date, end_date: Date, challenges: List[ChallengeJsonV400], - charge : TransactionRequestChargeJsonV200 + charge : TransactionRequestChargeJsonV200, + attributes: Option[List[BankAttributeBankResponseJsonV400]] ) case class PostHistoricalTransactionAtBankJson( from_account_id: String, @@ -1255,7 +1256,7 @@ object JSONFactory400 { ) def createTransactionRequestWithChargeJSON(tr : TransactionRequest, challenges: List[ChallengeTrait], transactionRequestAttribute: List[TransactionRequestAttributeTrait]) : TransactionRequestWithChargeJSON400 = { - new TransactionRequestWithChargeJSON400( + TransactionRequestWithChargeJSON400( id = stringOrNull(tr.id.value), `type` = stringOrNull(tr.`type`), from = try{TransactionRequestAccountJsonV140 ( @@ -1316,7 +1317,12 @@ object JSONFactory400 { charge = try {TransactionRequestChargeJsonV200 (summary = stringOrNull(tr.charge.summary), value = AmountOfMoneyJsonV121(currency = stringOrNull(tr.charge.value.currency), amount = stringOrNull(tr.charge.value.amount)) - )} catch {case _ : Throwable => null} + )} catch {case _ : Throwable => null}, + attributes = if(transactionRequestAttribute.isEmpty) None else Some(transactionRequestAttribute + .map(attribute =>BankAttributeBankResponseJsonV400( + attribute.name, + attribute.value + ))) ) } diff --git a/obp-api/src/main/scala/code/api/v5_1_0/APIMethods510.scala b/obp-api/src/main/scala/code/api/v5_1_0/APIMethods510.scala index f33b971fe..62da0e31c 100644 --- a/obp-api/src/main/scala/code/api/v5_1_0/APIMethods510.scala +++ b/obp-api/src/main/scala/code/api/v5_1_0/APIMethods510.scala @@ -2753,7 +2753,79 @@ trait APIMethods510 { } } + resourceDocs += ResourceDoc( + getTransactionRequests, + implementedInApiVersion, + nameOf(getTransactionRequests), + "GET", + "/banks/BANK_ID/accounts/ACCOUNT_ID/VIEW_ID/transaction-requests", + "Get Transaction Requests." , + """Returns transaction requests for account specified by ACCOUNT_ID at bank specified by BANK_ID. + | + |The VIEW_ID specified must be 'owner' and the user must have access to this view. + | + |Version 2.0.0 now returns charge information. + | + |Transaction Requests serve to initiate transactions that may or may not proceed. They contain information including: + | + |* Transaction Request Id + |* Type + |* Status (INITIATED, COMPLETED) + |* Challenge (in order to confirm the request) + |* From Bank / Account + |* Details including Currency, Value, Description and other initiation information specific to each type. (Could potentialy include a list of future transactions.) + |* Related Transactions + | + |PSD2 Context: PSD2 requires transparency of charges to the customer. + |This endpoint provides the charge that would be applied if the Transaction Request proceeds - and a record of that charge there after. + |The customer can proceed with the Transaction by answering the security challenge. + | + """.stripMargin, + EmptyBody, + transactionRequestWithChargeJSONs210, + List( + UserNotLoggedIn, + BankNotFound, + BankAccountNotFound, + UserNoPermissionAccessView, + ViewDoesNotPermitAccess, + GetTransactionRequestsException, + UnknownError + ), + List(apiTagTransactionRequest, apiTagPSD2PIS)) + lazy val getTransactionRequests: OBPEndpoint = { + case "banks" :: BankId(bankId) :: "accounts" :: AccountId(accountId) :: ViewId(viewId) :: "transaction-requests" :: Nil JsonGet _ => { + cc => implicit val ec = EndpointContext(Some(cc)) + for { + (Full(u), callContext) <- authenticatedAccess(cc) + _ <- NewStyle.function.isEnabledTransactionRequests(callContext) + (_, callContext) <- NewStyle.function.getBank(bankId, callContext) + (fromAccount, callContext) <- NewStyle.function.checkBankAccountExists(bankId, accountId, callContext) + view <- NewStyle.function.checkAccountAccessAndGetView(viewId, BankIdAccountId(bankId, accountId), Full(u), callContext) + _ <- Helper.booleanToFuture( + s"${ErrorMessages.ViewDoesNotPermitAccess} You need the `${StringHelpers.snakify(nameOf(ViewDefinition.canSeeTransactionRequests_)).dropRight(1)}` permission on the View(${viewId.value})", + cc=callContext){ + view.canSeeTransactionRequests + } + (transactionRequests, callContext) <- Future(Connector.connector.vend.getTransactionRequests210(u, fromAccount, callContext)) map { + unboxFullOrFail(_, callContext, GetTransactionRequestsException) + } + (transactionRequestIds, callContext) <- if(true) NewStyle.function.getTransactionRequestIdsByAttributeNameValues(bankId, Map("String"->List("String")), callContext) else Future{(Nil,callContext)} + + transactionRequestsFiltered = if(transactionRequestIds.nonEmpty) transactionRequests.filter(transactionRequest => transactionRequestIds.contains(transactionRequest.id.value)) else transactionRequests + + //TODO should be here, prepare the transactionRequests and transactionRequestAttributes pair. + + + } yield { + val json = JSONFactory510.createTransactionRequestJSONs(transactionRequestsFiltered) + + (json, HttpCode.`200`(callContext)) + } + } + } + staticResourceDocs += ResourceDoc( getAccountAccessByUserId, implementedInApiVersion, diff --git a/obp-api/src/main/scala/code/api/v5_1_0/JSONFactory5.1.0.scala b/obp-api/src/main/scala/code/api/v5_1_0/JSONFactory5.1.0.scala index bb0f98848..b3be4b127 100644 --- a/obp-api/src/main/scala/code/api/v5_1_0/JSONFactory5.1.0.scala +++ b/obp-api/src/main/scala/code/api/v5_1_0/JSONFactory5.1.0.scala @@ -30,7 +30,9 @@ import code.api.Constant import code.api.util.APIUtil.{DateWithDay, DateWithSeconds, gitCommit, stringOrNull} import code.api.util._ import code.api.v1_2_1.BankRoutingJsonV121 -import code.api.v1_4_0.JSONFactory1_4_0.{LocationJsonV140, MetaJsonV140, transformToLocationFromV140, transformToMetaFromV140} +import code.api.v1_4_0.JSONFactory1_4_0.{LocationJsonV140, MetaJsonV140, transformToLocationFromV140, transformToMetaFromV140, + TransactionRequestAccountJsonV140,ChallengeJsonV140} +import code.api.v2_0_0.TransactionRequestChargeJsonV200 import code.api.v2_1_0.ResourceUserJSON import code.api.v3_0_0.JSONFactory300.{createLocationJson, createMetaJson, transformToAddressFromV300} import code.api.v3_0_0.{AddressJsonV300, OpeningTimesV300} @@ -555,8 +557,65 @@ case class ConsumerLogoUrlJson( logo_url: String ) +case class TransactionRequestWithChargeJsonV510( + transaction_request_id: String, + transaction_request_type: String, + from: TransactionRequestAccountJsonV140, + details: TransactionRequestBodyAllTypes, + transaction_ids: List[String], + status: String, + start_date: Date, + end_date: Date, + challenge: ChallengeJsonV140, + charge : TransactionRequestChargeJsonV200, + attributes: List[TransactionRequestAttributeJsonV400] +) + +case class TransactionRequestsWithChargeJsonV510( + transaction_requests_with_charges : List[TransactionRequestWithChargeJsonV510] +) + object JSONFactory510 extends CustomJsonFormats { + def createTransactionRequestWithChargeJson(tr : TransactionRequest, transactionRequestAttributes: List[TransactionRequestAttributeTrait] ) : TransactionRequestWithChargeJsonV510 = { + TransactionRequestWithChargeJsonV510( + transaction_request_id = stringOrNull(tr.id.value), + transaction_request_type = stringOrNull(tr.`type`), + from = try{TransactionRequestAccountJsonV140 ( + bank_id = stringOrNull(tr.from.bank_id), + account_id = stringOrNull(tr.from.account_id) + )} catch {case _ : Throwable => null}, + details = try{tr.body} catch {case _ : Throwable => null}, + transaction_ids = tr.transaction_ids::Nil, + status = stringOrNull(tr.status), + start_date = tr.start_date, + end_date = tr.end_date, + // Some (mapped) data might not have the challenge. TODO Make this nicer + challenge = { + try {ChallengeJsonV140 (id = stringOrNull(tr.challenge.id), allowed_attempts = tr.challenge.allowed_attempts, challenge_type = stringOrNull(tr.challenge.challenge_type))} + // catch { case _ : Throwable => ChallengeJSON (id = "", allowed_attempts = 0, challenge_type = "")} + catch { case _ : Throwable => null} + }, + charge = try {TransactionRequestChargeJsonV200 (summary = stringOrNull(tr.charge.summary), + value = AmountOfMoneyJsonV121(currency = stringOrNull(tr.charge.value.currency), + amount = stringOrNull(tr.charge.value.amount)) + )} catch {case _ : Throwable => null}, + attributes = transactionRequestAttributes.map(transactionRequestAttribute => TransactionRequestAttributeJsonV400( + transactionRequestAttribute.name, + transactionRequestAttribute.attributeType.toString, + transactionRequestAttribute.value + )) + ) + } + + def createTransactionRequestJSONs(transactionRequests : List[TransactionRequest]) : TransactionRequestsWithChargeJsonV510 = { + TransactionRequestsWithChargeJsonV510( + transactionRequests.map( + transactionRequest => + createTransactionRequestWithChargeJson(transactionRequest, List.empty[TransactionRequestAttributeTrait]) + )) + } + def createViewJson(view: View): CustomViewJsonV510 = { val alias = if (view.usePublicAliasIfOneExists)