diff --git a/obp-api/src/main/scala/bootstrap/liftweb/Boot.scala b/obp-api/src/main/scala/bootstrap/liftweb/Boot.scala index b4d0725bec..c370280e6c 100644 --- a/obp-api/src/main/scala/bootstrap/liftweb/Boot.scala +++ b/obp-api/src/main/scala/bootstrap/liftweb/Boot.scala @@ -110,6 +110,7 @@ import code.regulatedentities.MappedRegulatedEntity import code.remotedata.RemotedataActors import code.scheduler.{DataBaseCleanerScheduler, DatabaseDriverScheduler, JobScheduler, MetricsArchiveScheduler} import code.scope.{MappedScope, MappedUserScope} +import code.signingbaskets.{MappedSigningBasket, MappedSigningBasketConsent, MappedSigningBasketPayment} import code.snippet.{OAuthAuthorisation, OAuthWorkedThanks} import code.socialmedia.MappedSocialMedia import code.standingorders.StandingOrder @@ -1040,6 +1041,9 @@ object ToSchemify { AuthUser, JobScheduler, MappedETag, + MappedSigningBasket, + MappedSigningBasketPayment, + MappedSigningBasketConsent, MappedRegulatedEntity, AtmAttribute, Admin, diff --git a/obp-api/src/main/scala/code/api/berlin/group/ConstantsBG.scala b/obp-api/src/main/scala/code/api/berlin/group/ConstantsBG.scala new file mode 100644 index 0000000000..0058a6fe1c --- /dev/null +++ b/obp-api/src/main/scala/code/api/berlin/group/ConstantsBG.scala @@ -0,0 +1,14 @@ +package code.api.berlin.group + +object ConstantsBG { + object SigningBasketsStatus extends Enumeration { + type SigningBasketsStatus = Value + // Only the codes + // 1) RCVD (Received), + // 2) PATC (PartiallyAcceptedTechnical Correct) The payment initiation needs multiple authentications, where some but not yet all have been performed. Syntactical and semantical validations are successful., + // 3) ACTC (AcceptedTechnicalValidation) , + // 4) CANC (Cancelled) and + // 5) RJCT (Rejected) are supported for signing baskets. + val RCVD, PATC, ACTC, CANC, RJCT = Value + } +} diff --git a/obp-api/src/main/scala/code/api/berlin/group/v1_3/AccountInformationServiceAISApi.scala b/obp-api/src/main/scala/code/api/berlin/group/v1_3/AccountInformationServiceAISApi.scala index 8004e8193d..0c7d68ea60 100644 --- a/obp-api/src/main/scala/code/api/berlin/group/v1_3/AccountInformationServiceAISApi.scala +++ b/obp-api/src/main/scala/code/api/berlin/group/v1_3/AccountInformationServiceAISApi.scala @@ -1090,7 +1090,7 @@ using the extended forms as indicated above. } (challenges, callContext) <- NewStyle.function.createChallengesC2( List(u.userId), - ChallengeType.BERLINGROUP_CONSENT_CHALLENGE, + ChallengeType.BERLIN_GROUP_CONSENT_CHALLENGE, None, getSuggestedDefaultScaMethod(), Some(StrongCustomerAuthenticationStatus.received), @@ -1260,7 +1260,7 @@ Maybe in a later version the access path will change. challenges.filter(_.challengeId == authorisationId).size == 1 } (challenge, callContext) <- NewStyle.function.validateChallengeAnswerC2( - ChallengeType.BERLINGROUP_CONSENT_CHALLENGE, + ChallengeType.BERLIN_GROUP_CONSENT_CHALLENGE, None, Some(consentId), challenges.filter(_.challengeId == authorisationId).head.challengeId, diff --git a/obp-api/src/main/scala/code/api/berlin/group/v1_3/JSONFactory_BERLIN_GROUP_1_3.scala b/obp-api/src/main/scala/code/api/berlin/group/v1_3/JSONFactory_BERLIN_GROUP_1_3.scala index 70baf53481..5683e417b6 100644 --- a/obp-api/src/main/scala/code/api/berlin/group/v1_3/JSONFactory_BERLIN_GROUP_1_3.scala +++ b/obp-api/src/main/scala/code/api/berlin/group/v1_3/JSONFactory_BERLIN_GROUP_1_3.scala @@ -21,7 +21,28 @@ case class JvalueCaseClass(jvalueToCaseclass: JValue) object JSONFactory_BERLIN_GROUP_1_3 extends CustomJsonFormats { - + case class ErrorMessageBG(category: String, code: Int, path: String, text: String) + case class ErrorMessagesBG(tppMessages: List[ErrorMessageBG]) + + case class PostSigningBasketJsonV13( + paymentIds: Option[List[String]], + consentIds: Option[List[String]] + ) + + case class SigningBasketLinksV13( + self: LinkHrefJson, + status: LinkHrefJson, + startAuthorisation: LinkHrefJson + ) + case class SigningBasketResponseJson( + transactionStatus: String, + basketId: String, + _links: SigningBasketLinksV13) + case class SigningBasketGetResponseJson( + transactionStatus: String, + payments: Option[List[String]], + consents: Option[List[String]] + ) case class LinkHrefJson( href: String ) @@ -637,7 +658,7 @@ object JSONFactory_BERLIN_GROUP_1_3 extends CustomJsonFormats { ) ) } - + def createStartPaymentInitiationCancellationAuthorisation( challenge: ChallengeTrait, paymentService: String, @@ -654,6 +675,44 @@ object JSONFactory_BERLIN_GROUP_1_3 extends CustomJsonFormats { ) } + + def createStartSigningBasketAuthorisationJson(basketId: String, challenge: ChallengeTrait): StartPaymentAuthorisationJson = { + StartPaymentAuthorisationJson( + scaStatus = challenge.scaStatus.map(_.toString).getOrElse(""), + authorisationId = challenge.challengeId, + psuMessage = "Please check your SMS at a mobile device.", + _links = ScaStatusJsonV13(s"/v1.3/signing-baskets/${basketId}/authorisations/${challenge.challengeId}") + ) + } + + def createSigningBasketResponseJson(basket: SigningBasketTrait): SigningBasketResponseJson = { + SigningBasketResponseJson( + basketId = basket.basketId, + transactionStatus = basket.status.toLowerCase(), + _links = SigningBasketLinksV13( + self = LinkHrefJson(s"/v1.3/signing-baskets/${basket.basketId}"), + status = LinkHrefJson(s"/v1.3/signing-baskets/${basket.basketId}/status"), + startAuthorisation = LinkHrefJson(s"/v1.3/signing-baskets/${basket.basketId}/authorisations") + ) + ) + } + + def getSigningBasketResponseJson(basket: SigningBasketContent): SigningBasketGetResponseJson = { + SigningBasketGetResponseJson( + transactionStatus = basket.basket.status.toLowerCase(), + payments = basket.payments, + consents = basket.consents, + ) + } + + def getSigningBasketStatusResponseJson(basket: SigningBasketContent): SigningBasketGetResponseJson = { + SigningBasketGetResponseJson( + transactionStatus = basket.basket.status.toLowerCase(), + payments = None, + consents = None, + ) + } + def checkTransactionAuthorisation(JsonPost: JValue) = tryo { JsonPost.extract[TransactionAuthorisation] }.isDefined diff --git a/obp-api/src/main/scala/code/api/berlin/group/v1_3/PaymentInitiationServicePISApi.scala b/obp-api/src/main/scala/code/api/berlin/group/v1_3/PaymentInitiationServicePISApi.scala index 6028d42379..d2d26ad118 100644 --- a/obp-api/src/main/scala/code/api/berlin/group/v1_3/PaymentInitiationServicePISApi.scala +++ b/obp-api/src/main/scala/code/api/berlin/group/v1_3/PaymentInitiationServicePISApi.scala @@ -1,7 +1,7 @@ package code.api.builder.PaymentInitiationServicePISApi import code.api.ResourceDocs1_4_0.SwaggerDefinitionsJSON.sepaCreditTransfersBerlinGroupV13 -import code.api.berlin.group.v1_3.JSONFactory_BERLIN_GROUP_1_3.{CancelPaymentResponseJson, +import code.api.berlin.group.v1_3.JSONFactory_BERLIN_GROUP_1_3.{CancelPaymentResponseJson, CancelPaymentResponseLinks, LinkHrefJson, UpdatePaymentPsuDataJson, checkUpdatePsuAuthentication,checkAuthorisationConfirmation, checkTransactionAuthorisation, checkSelectPsuAuthenticationMethod, createCancellationTransactionRequestJson} import code.api.berlin.group.v1_3.{JSONFactory_BERLIN_GROUP_1_3, JvalueCaseClass, OBP_BERLIN_GROUP_1_3} @@ -21,7 +21,7 @@ import code.views.Views import com.github.dwickern.macros.NameOf.nameOf import com.openbankproject.commons.ExecutionContext.Implicits.global import com.openbankproject.commons.model._ -import com.openbankproject.commons.model.enums.ChallengeType.BERLINGROUP_PAYMENT_CHALLENGE +import com.openbankproject.commons.model.enums.ChallengeType.BERLIN_GROUP_PAYMENT_CHALLENGE import com.openbankproject.commons.model.enums.TransactionRequestStatus._ import com.openbankproject.commons.model.enums.{ChallengeType, StrongCustomerAuthenticationStatus, TransactionRequestStatus} import com.openbankproject.commons.util.ApiVersion @@ -44,9 +44,15 @@ object APIMethods_PaymentInitiationServicePISApi extends RestHelper { val apiRelations = ArrayBuffer[ApiRelation]() protected implicit def JvalueToSuper(what: JValue): JvalueCaseClass = JvalueCaseClass(what) - def checkPaymentServerError(paymentService: String) = s"${InvalidTransactionRequestType.replaceAll("TRANSACTION_REQUEST_TYPE", "PAYMENT_SERVICE in the URL.")}: '${paymentService}'.It should be `payments` for now, will support (bulk-payments, periodic-payments) soon" + def checkPaymentServerError(paymentService: String) = { + val ccc = "" + s"${InvalidTransactionRequestType.replaceAll("TRANSACTION_REQUEST_TYPE", "PAYMENT_SERVICE in the URL.")}: '${paymentService}'.It should be `payments` for now, will support (bulk-payments, periodic-payments) soon" + } def checkPaymentProductError(paymentProduct: String) = s"${InvalidTransactionRequestType.replaceAll("TRANSACTION_REQUEST_TYPE", "PAYMENT_PRODUCT in the URL.")}: '${paymentProduct}'.It should be `sepa-credit-transfers`for now, will support (instant-sepa-credit-transfers, target-2-payments, cross-border-credit-transfers) soon." + def checkPaymentServiceType(paymentService: String) = tryo { + PaymentServiceTypes.withName(paymentService.replaceAll("-","_")) + }.isDefined val endpoints = cancelPayment :: @@ -230,7 +236,7 @@ Returns the content of a payment object""", ) lazy val getPaymentInformation : OBPEndpoint = { - case paymentService :: paymentProduct :: paymentId :: Nil JsonGet _ => { + case paymentService :: paymentProduct :: paymentId :: Nil JsonGet _ if checkPaymentServiceType(paymentService) => { cc => for { (Full(u), callContext) <- authenticatedAccess(cc) @@ -473,13 +479,13 @@ Check the transaction status of a payment initiation.""", private val generalPaymentSummary = s"""${mockedDataText(false)} This method is used to initiate a payment at the ASPSP. - + ## Variants of Payment Initiation Requests - + This method to initiate a payment initiation at the ASPSP can be sent with either a JSON body or an pain.001 body depending on the payment product in the path. - + There are the following **payment products**: - + - Payment products with payment information in *JSON* format: - ***sepa-credit-transfers*** - ***instant-sepa-credit-transfers*** @@ -490,35 +496,35 @@ Check the transaction status of a payment initiation.""", - ***pain.001-instant-sepa-credit-transfers*** - ***pain.001-target-2-payments*** - ***pain.001-cross-border-credit-transfers*** - + - Furthermore the request body depends on the **payment-service** - ***payments***: A single payment initiation request. - ***bulk-payments***: A collection of several payment iniatiation requests. In case of a *pain.001* message there are more than one payments contained in the *pain.001 message. In case of a *JSON* there are several JSON payment blocks contained in a joining list. - - ***periodic-payments***: - Create a standing order initiation resource for recurrent i.e. periodic payments addressable under {paymentId} - with all data relevant for the corresponding payment product and the execution of the standing order contained in a JSON body. - + - ***periodic-payments***: + Create a standing order initiation resource for recurrent i.e. periodic payments addressable under {paymentId} + with all data relevant for the corresponding payment product and the execution of the standing order contained in a JSON body. + This is the first step in the API to initiate the related recurring/periodic payment. - + ## Single and mulitilevel SCA Processes - - The Payment Initiation Requests are independent from the need of one ore multilevel - SCA processing, i.e. independent from the number of authorisations needed for the execution of payments. - - But the response messages are specific to either one SCA processing or multilevel SCA processing. - - For payment initiation with multilevel SCA, this specification requires an explicit start of the authorisation, - i.e. links directly associated with SCA processing like 'scaRedirect' or 'scaOAuth' cannot be contained in the - response message of a Payment Initation Request for a payment, where multiple authorisations are needed. - Also if any data is needed for the next action, like selecting an SCA method is not supported in the response, - since all starts of the multiple authorisations are fully equal. + + The Payment Initiation Requests are independent from the need of one ore multilevel + SCA processing, i.e. independent from the number of authorisations needed for the execution of payments. + + But the response messages are specific to either one SCA processing or multilevel SCA processing. + + For payment initiation with multilevel SCA, this specification requires an explicit start of the authorisation, + i.e. links directly associated with SCA processing like 'scaRedirect' or 'scaOAuth' cannot be contained in the + response message of a Payment Initation Request for a payment, where multiple authorisations are needed. + Also if any data is needed for the next action, like selecting an SCA method is not supported in the response, + since all starts of the multiple authorisations are fully equal. In these cases, first an authorisation sub-resource has to be generated following the 'startAuthorisation' link. - - + + $additionalInstructions - + """ def initiatePaymentImplementation(paymentService: String, paymentProduct: String, json: liftweb.json.JValue, cc: CallContext) = { for { @@ -584,7 +590,7 @@ Check the transaction status of a payment initiation.""", for { (createdTransactionRequest, callContext) <- NewStyle.function.createTransactionRequestv400( u, - ViewId("Owner"), //This is the default + ViewId("Owner"), //This is the default fromAccount, toAccount, TransactionRequestType(transactionRequestTypes.toString), @@ -594,7 +600,7 @@ Check the transaction status of a payment initiation.""", ), transDetailsSerialized, "", - Some(BERLINGROUP_PAYMENT_CHALLENGE), + Some(BERLIN_GROUP_PAYMENT_CHALLENGE), None, None, Some(transDetailsJson), @@ -608,7 +614,7 @@ Check the transaction status of a payment initiation.""", } } - + resourceDocs += ResourceDoc( initiatePayments, apiVersion, @@ -632,15 +638,15 @@ Check the transaction status of a payment initiation.""", List(UserNotLoggedIn, UnknownError), ApiTag("Payment Initiation Service (PIS)") :: apiTagBerlinGroupM :: Nil ) - + lazy val initiatePayments : OBPEndpoint = { case "payments" :: paymentProduct :: Nil JsonPost json -> _ => { cc => initiatePaymentImplementation("payments", paymentProduct, json, cc) } } - - + + resourceDocs += ResourceDoc( initiatePeriodicPayments, apiVersion, @@ -681,14 +687,14 @@ Check the transaction status of a payment initiation.""", List(UserNotLoggedIn, UnknownError), ApiTag("Payment Initiation Service (PIS)") :: apiTagBerlinGroupM :: Nil ) - + lazy val initiatePeriodicPayments : OBPEndpoint = { case "periodic-payments" :: paymentProduct :: Nil JsonPost json -> _ => { cc => initiatePaymentImplementation("periodic-payments", paymentProduct, json, cc) } } - + resourceDocs += ResourceDoc( initiateBulkPayments, apiVersion, @@ -743,14 +749,14 @@ Check the transaction status of a payment initiation.""", List(UserNotLoggedIn, UnknownError), ApiTag("Payment Initiation Service (PIS)") :: apiTagBerlinGroupM :: Nil ) - + lazy val initiateBulkPayments : OBPEndpoint = { case "bulk-payments" :: paymentProduct :: Nil JsonPost json -> _ => { cc => initiatePaymentImplementation("bulk-payments", paymentProduct, json, cc) } } - + def generalStartPaymentAuthorisationSummary(isMockedDate: Boolean) = s"""${mockedDataText(isMockedDate)} Create an authorisation sub-resource and start the authorisation process. The message might in addition transmit authentication and authorisation related data. @@ -785,7 +791,7 @@ This applies in the following scenarios: executing the cancellation. * The signing basket needs to be authorised yet. """ - + resourceDocs += ResourceDoc( startPaymentAuthorisationUpdatePsuAuthentication, apiVersion, @@ -795,7 +801,7 @@ This applies in the following scenarios: "Start the authorisation process for a payment initiation (updatePsuAuthentication)", generalStartPaymentAuthorisationSummary(true), json.parse( - """{ + """{ | "scaStatus": "finalised", | "_links":{ | "status": {"href":"/v1/payments/sepa-credit-transfers/qwer3456tzui7890/status"} @@ -815,7 +821,7 @@ This applies in the following scenarios: ApiTag("Payment Initiation Service (PIS)") :: apiTagBerlinGroupM :: Nil ) - + lazy val startPaymentAuthorisationUpdatePsuAuthentication : OBPEndpoint = { case paymentService :: paymentProduct :: paymentId :: "authorisations" :: Nil JsonPost json -> _ if checkUpdatePsuAuthentication(json) => { cc => @@ -835,7 +841,7 @@ This applies in the following scenarios: } } } - + resourceDocs += ResourceDoc( startPaymentAuthorisationSelectPsuAuthenticationMethod, apiVersion, @@ -879,7 +885,7 @@ This applies in the following scenarios: } } } - + resourceDocs += ResourceDoc( startPaymentAuthorisationTransactionAuthorisation, apiVersion, @@ -903,7 +909,7 @@ This applies in the following scenarios: ApiTag("Payment Initiation Service (PIS)") :: apiTagBerlinGroupM :: Nil ) - + lazy val startPaymentAuthorisationTransactionAuthorisation : OBPEndpoint = { case paymentService :: paymentProduct :: paymentId :: "authorisations" :: Nil JsonPost json -> _ if checkTransactionAuthorisation(json) => { cc => @@ -920,7 +926,7 @@ This applies in the following scenarios: (challenges, callContext) <- NewStyle.function.createChallengesC2( List(u.userId), - ChallengeType.BERLINGROUP_PAYMENT_CHALLENGE, + ChallengeType.BERLIN_GROUP_PAYMENT_CHALLENGE, Some(paymentId), getScaMethodAtInstance(SEPA_CREDIT_TRANSFERS.toString).toOption, Some(StrongCustomerAuthenticationStatus.received), @@ -937,7 +943,7 @@ This applies in the following scenarios: } } } - + def generalStartPaymentInitiationCancellationAuthorisationSummary (isMockedDate:Boolean) = s"""${mockedDataText(isMockedDate)} Creates an authorisation sub-resource and start the authorisation process of the cancellation of the addressed payment. @@ -957,19 +963,19 @@ or cancellation sub-resource. This applies in the following scenarios: -* The ASPSP has indicated with an 'startAuthorisation' hyperlink in the preceeding Payment - Initiation Response that an explicit start of the authorisation process is needed by the TPP. - The 'startAuthorisation' hyperlink can transport more information about data which needs to be +* The ASPSP has indicated with an 'startAuthorisation' hyperlink in the preceeding Payment + Initiation Response that an explicit start of the authorisation process is needed by the TPP. + The 'startAuthorisation' hyperlink can transport more information about data which needs to be uploaded by using the extended forms. - * 'startAuthorisationWithPsuIdentfication', + * 'startAuthorisationWithPsuIdentfication', * 'startAuthorisationWithPsuAuthentication' #TODO - * 'startAuthorisationWithAuthentciationMethodSelection' + * 'startAuthorisationWithAuthentciationMethodSelection' * The related payment initiation cannot yet be executed since a multilevel SCA is mandated. -* The ASPSP has indicated with an 'startAuthorisation' hyperlink in the preceeding - Payment Cancellation Response that an explicit start of the authorisation process is needed by the TPP. - The 'startAuthorisation' hyperlink can transport more information about data which needs to be uploaded +* The ASPSP has indicated with an 'startAuthorisation' hyperlink in the preceeding + Payment Cancellation Response that an explicit start of the authorisation process is needed by the TPP. + The 'startAuthorisation' hyperlink can transport more information about data which needs to be uploaded by using the extended forms as indicated above. -* The related payment cancellation request cannot be applied yet since a multilevel SCA is mandate for +* The related payment cancellation request cannot be applied yet since a multilevel SCA is mandate for executing the cancellation. * The signing basket needs to be authorised yet. """ @@ -1015,7 +1021,7 @@ This applies in the following scenarios: } (challenges, callContext) <- NewStyle.function.createChallengesC2( List(u.userId), - ChallengeType.BERLINGROUP_PAYMENT_CHALLENGE, + ChallengeType.BERLIN_GROUP_PAYMENT_CHALLENGE, Some(paymentId), getScaMethodAtInstance(SEPA_CREDIT_TRANSFERS.toString).toOption, Some(StrongCustomerAuthenticationStatus.received), @@ -1129,7 +1135,7 @@ This applies in the following scenarios: } } } - + def generalUpdatePaymentCancellationPsuDataSummary (isMockedData: Boolean)= s"""${mockedDataText(isMockedData)} This method updates PSU data on the cancellation authorisation resource if needed. @@ -1144,14 +1150,14 @@ There are several possible Update PSU Data requests in the context of a cancella which depends on the SCA approach: * Redirect SCA Approach: -A specific Update PSU Data Request is applicable for +A specific Update PSU Data Request is applicable for * the selection of authentication methods, before choosing the actual SCA approach. * Decoupled SCA Approach: A specific Update PSU Data Request is only applicable for * adding the PSU Identification, if not provided yet in the Payment Initiation Request or the Account Information Consent Request, or if no OAuth2 access token is used, or * the selection of authentication methods. * Embedded SCA Approach: -The Update PSU Data Request might be used +The Update PSU Data Request might be used * to add credentials as a first factor authentication data of the PSU and * to select the authentication method and * transaction authorisation. @@ -1164,13 +1170,13 @@ For that reason, the following possible Update PSU Data request can apply to all There are the following request types on this access path: * Update PSU Identification * Update PSU Authentication -* Select PSU Autorization Method - WARNING: This method need a reduced header, - therefore many optional elements are not present. +* Select PSU Autorization Method + WARNING: This method need a reduced header, + therefore many optional elements are not present. Maybe in a later version the access path will change. * Transaction Authorisation - WARNING: This method need a reduced header, - therefore many optional elements are not present. + WARNING: This method need a reduced header, + therefore many optional elements are not present. Maybe in a later version the access path will change. """ @@ -1193,7 +1199,7 @@ There are the following request types on this access path: List(UserNotLoggedIn, UnknownError), ApiTag("Payment Initiation Service (PIS)") :: apiTagBerlinGroupM :: Nil ) - + lazy val updatePaymentCancellationPsuDataTransactionAuthorisation : OBPEndpoint = { case paymentService :: paymentProduct :: paymentId:: "cancellation-authorisations" :: authorisationId :: Nil JsonPut json -> _ if checkTransactionAuthorisation(json) => { cc => @@ -1221,7 +1227,7 @@ There are the following request types on this access path: } (_, callContext) <- NewStyle.function.getTransactionRequestImpl(TransactionRequestId(paymentId), callContext) (challenge, callContext) <- NewStyle.function.validateChallengeAnswerC2( - ChallengeType.BERLINGROUP_PAYMENT_CHALLENGE, + ChallengeType.BERLIN_GROUP_PAYMENT_CHALLENGE, Some(paymentId), None, authorisationId, @@ -1288,7 +1294,7 @@ There are the following request types on this access path: } } } - + resourceDocs += ResourceDoc( updatePaymentCancellationPsuDataSelectPsuAuthenticationMethod, apiVersion, @@ -1336,7 +1342,7 @@ There are the following request types on this access path: } } } - + resourceDocs += ResourceDoc( updatePaymentCancellationPsuDataAuthorisationConfirmation, apiVersion, @@ -1372,8 +1378,8 @@ There are the following request types on this access path: } } } - - + + def generalUpdatePaymentPsuDataSumarry(isMockedData: Boolean) = s"""${mockedDataText(isMockedData)} This methods updates PSU data on the authorisation resource if needed. @@ -1386,14 +1392,14 @@ There are several possible Update PSU Data requests in the context of payment in which depends on the SCA approach: * Redirect SCA Approach: -A specific Update PSU Data Request is applicable for +A specific Update PSU Data Request is applicable for * the selection of authentication methods, before choosing the actual SCA approach. * Decoupled SCA Approach: A specific Update PSU Data Request is only applicable for * adding the PSU Identification, if not provided yet in the Payment Initiation Request or the Account Information Consent Request, or if no OAuth2 access token is used, or * the selection of authentication methods. * Embedded SCA Approach: -The Update PSU Data Request might be used +The Update PSU Data Request might be used * to add credentials as a first factor authentication data of the PSU and * to select the authentication method and * transaction authorisation. @@ -1406,19 +1412,19 @@ For that reason, the following possible Update PSU Data request can apply to all There are the following request types on this access path: * Update PSU Identification * Update PSU Authentication -* Select PSU Autorization Method - WARNING: This method need a reduced header, - therefore many optional elements are not present. +* Select PSU Autorization Method + WARNING: This method need a reduced header, + therefore many optional elements are not present. Maybe in a later version the access path will change. * Transaction Authorisation - WARNING: This method need a reduced header, - therefore many optional elements are not present. + WARNING: This method need a reduced header, + therefore many optional elements are not present. Maybe in a later version the access path will change. - + NOTE: For this endpoint, for sandbox mode, the `scaAuthenticationData` is fixed value: 123. To make the process work. Normally the app use will get SMS/EMAIL to get the value for this process. - -""" + +""" resourceDocs += ResourceDoc( updatePaymentPsuDataTransactionAuthorisation, @@ -1464,7 +1470,7 @@ There are the following request types on this access path: existingTransactionRequest.status == TransactionRequestStatus.INITIATED.toString } (challenge, callContext) <- NewStyle.function.validateChallengeAnswerC2( - ChallengeType.BERLINGROUP_PAYMENT_CHALLENGE, + ChallengeType.BERLIN_GROUP_PAYMENT_CHALLENGE, Some(paymentId), None, authorisationid, @@ -1493,7 +1499,7 @@ There are the following request types on this access path: } } } - + resourceDocs += ResourceDoc( updatePaymentPsuDataUpdatePsuAuthentication, apiVersion, @@ -1518,7 +1524,7 @@ There are the following request types on this access path: cc => for { (Full(u), callContext) <- authenticatedAccess(cc) - + } yield { (liftweb.json.parse( """{ @@ -1530,7 +1536,7 @@ There are the following request types on this access path: } } } - + resourceDocs += ResourceDoc( updatePaymentPsuDataSelectPsuAuthenticationMethod, apiVersion, @@ -1562,7 +1568,7 @@ There are the following request types on this access path: cc => for { (Full(u), callContext) <- authenticatedAccess(cc) - + } yield { (liftweb.json.parse( """{ @@ -1580,7 +1586,7 @@ There are the following request types on this access path: } } } - + resourceDocs += ResourceDoc( updatePaymentPsuDataAuthorisationConfirmation, apiVersion, @@ -1606,7 +1612,7 @@ There are the following request types on this access path: cc => for { (Full(u), callContext) <- authenticatedAccess(cc) - + } yield { (liftweb.json.parse( """{ diff --git a/obp-api/src/main/scala/code/api/berlin/group/v1_3/SigningBasketsApi.scala b/obp-api/src/main/scala/code/api/berlin/group/v1_3/SigningBasketsApi.scala index d8afdbd3f0..312fc7de8b 100644 --- a/obp-api/src/main/scala/code/api/berlin/group/v1_3/SigningBasketsApi.scala +++ b/obp-api/src/main/scala/code/api/berlin/group/v1_3/SigningBasketsApi.scala @@ -1,27 +1,30 @@ package code.api.builder.SigningBasketsApi -import code.api.APIFailureNewStyle -import code.api.berlin.group.v1_3.{JSONFactory_BERLIN_GROUP_1_3, JvalueCaseClass, OBP_BERLIN_GROUP_1_3} -import net.liftweb.json -import net.liftweb.json._ -import code.api.util.APIUtil.{defaultBankId, _} -import code.api.util.{ApiTag, NewStyle} -import code.api.util.ErrorMessages._ +import code.api.berlin.group.ConstantsBG +import code.api.berlin.group.v1_3.JSONFactory_BERLIN_GROUP_1_3.{PostSigningBasketJsonV13, UpdatePaymentPsuDataJson, createSigningBasketResponseJson, createStartSigningBasketAuthorisationJson, getSigningBasketResponseJson, getSigningBasketStatusResponseJson} +import code.api.berlin.group.v1_3.{JSONFactory_BERLIN_GROUP_1_3, JvalueCaseClass} +import code.api.util.APIUtil._ import code.api.util.ApiTag._ +import code.api.util.ErrorMessages._ +import code.api.util.NewStyle import code.api.util.NewStyle.HttpCode +import code.api.util.newstyle.SigningBasketNewStyle import code.bankconnectors.Connector -import code.model._ -import code.util.Helper -import code.views.Views -import net.liftweb.common.Full -import net.liftweb.http.rest.RestHelper +import code.signingbaskets.SigningBasketX +import code.util.Helper.booleanToFuture import com.github.dwickern.macros.NameOf.nameOf - -import scala.collection.immutable.Nil -import scala.collection.mutable.ArrayBuffer import com.openbankproject.commons.ExecutionContext.Implicits.global +import com.openbankproject.commons.model.enums.TransactionRequestStatus.{COMPLETED, REJECTED} +import com.openbankproject.commons.model.enums.{ChallengeType, StrongCustomerAuthenticationStatus} +import com.openbankproject.commons.model.{ChallengeTrait, TransactionRequestId} import com.openbankproject.commons.util.ApiVersion +import net.liftweb.common.{Box, Empty, Full} +import net.liftweb.http.js.JE.JsRaw +import net.liftweb.http.rest.RestHelper +import net.liftweb.json +import net.liftweb.json._ +import scala.collection.mutable.ArrayBuffer import scala.concurrent.Future object APIMethods_SigningBasketsApi extends RestHelper { @@ -53,10 +56,7 @@ object APIMethods_SigningBasketsApi extends RestHelper { Create a signing basket resource for authorising several transactions with one SCA method. The resource identifications of these transactions are contained in the payload of this access method """, - json.parse("""{ - "consentIds" : "", - "paymentIds" : "" -}"""), + PostSigningBasketJsonV13(paymentIds = Some(List("123qwert456789", "12345qwert7899")), None), json.parse("""{ "basketId" : "1234-basket-567", "challengeData" : { @@ -97,54 +97,34 @@ The resource identifications of these transactions are contained in the payload "psuMessage" : { } }"""), List(UserNotLoggedIn, UnknownError), - ApiTag("Signing Baskets") :: apiTagMockedData :: Nil + apiTagSigningBaskets :: Nil ) lazy val createSigningBasket : OBPEndpoint = { - case "signing-baskets" :: Nil JsonPost _ => { + case "signing-baskets" :: Nil JsonPost jsonPost -> _ => { cc => for { (Full(u), callContext) <- authenticatedAccess(cc) - } yield { - (json.parse("""{ - "basketId" : "1234-basket-567", - "challengeData" : { - "otpMaxLength" : 0, - "additionalInformation" : "additionalInformation", - "image" : "image", - "imageLink" : "http://example.com/aeiou", - "otpFormat" : "characters", - "data" : "data" - }, - "scaMethods" : "", - "tppMessages" : [ { - "path" : "path", - "code" : { }, - "text" : { }, - "category" : { } - }, { - "path" : "path", - "code" : { }, - "text" : { }, - "category" : { } - } ], - "_links" : { - "scaStatus" : "/v1.3/payments/sepa-credit-transfers/1234-wertiq-983", - "startAuthorisationWithEncryptedPsuAuthentication" : "/v1.3/payments/sepa-credit-transfers/1234-wertiq-983", - "scaRedirect" : "/v1.3/payments/sepa-credit-transfers/1234-wertiq-983", - "startAuthorisationWithAuthenticationMethodSelection" : "/v1.3/payments/sepa-credit-transfers/1234-wertiq-983", - "startAuthorisationWithPsuAuthentication" : "/v1.3/payments/sepa-credit-transfers/1234-wertiq-983", - "scaOAuth" : "/v1.3/payments/sepa-credit-transfers/1234-wertiq-983", - "self" : "/v1.3/payments/sepa-credit-transfers/1234-wertiq-983", - "startAuthorisationWithPsuIdentification" : "/v1.3/payments/sepa-credit-transfers/1234-wertiq-983", - "startAuthorisation" : "/v1.3/payments/sepa-credit-transfers/1234-wertiq-983", - "startAuthorisationWithTransactionAuthorisation" : "/v1.3/payments/sepa-credit-transfers/1234-wertiq-983", - "status" : "/v1.3/payments/sepa-credit-transfers/1234-wertiq-983" - }, - "chosenScaMethod" : "", - "transactionStatus" : "ACCP", - "psuMessage" : { } -}"""), callContext) + _ <- passesPsd2Pisp(callContext) + failMsg = s"$InvalidJsonFormat The Json body should be the $PostSigningBasketJsonV13 " + postJson <- NewStyle.function.tryons(failMsg, 400, callContext) { + jsonPost.extract[PostSigningBasketJsonV13] + } + _ <- booleanToFuture(failMsg, cc = callContext) { + // One of them MUST be defined. Otherwise post json is treated as empty one. + !(jsonPost.extract[PostSigningBasketJsonV13].paymentIds.isEmpty && + jsonPost.extract[PostSigningBasketJsonV13].consentIds.isEmpty) + } + signingBasket <- Future { + SigningBasketX.signingBasketProvider.vend.createSigningBasket( + postJson.paymentIds, + postJson.consentIds, + ) + } map { + i => connectorEmptyResponse(i, callContext) + } + } yield { + (createSigningBasketResponseJson(signingBasket), HttpCode.`201`(callContext)) } } } @@ -166,7 +146,7 @@ Nevertheless, single transactions might be cancelled on an individual basis on t emptyObjectJson, emptyObjectJson, List(UserNotLoggedIn, UnknownError), - ApiTag("Signing Baskets") :: apiTagMockedData :: Nil + apiTagSigningBaskets :: Nil ) lazy val deleteSigningBasket : OBPEndpoint = { @@ -174,8 +154,14 @@ Nevertheless, single transactions might be cancelled on an individual basis on t cc => for { (Full(u), callContext) <- authenticatedAccess(cc) - } yield { - (NotImplemented, callContext) + _ <- passesPsd2Pisp(callContext) + _ <- Future { + SigningBasketX.signingBasketProvider.vend.deleteSigningBasket(basketid) + } map { + i => connectorEmptyResponse(i, callContext) + } + } yield { + (JsRaw(""), HttpCode.`204`(callContext)) } } } @@ -196,7 +182,7 @@ Returns the content of an signing basket object.""", "consents" : "" }"""), List(UserNotLoggedIn, UnknownError), - ApiTag("Signing Baskets") :: apiTagMockedData :: Nil + apiTagSigningBaskets :: Nil ) lazy val getSigningBasket : OBPEndpoint = { @@ -204,12 +190,14 @@ Returns the content of an signing basket object.""", cc => for { (Full(u), callContext) <- authenticatedAccess(cc) - } yield { - (json.parse("""{ - "transactionStatus" : "ACCP", - "payments" : "", - "consents" : "" -}"""), callContext) + _ <- passesPsd2Pisp(callContext) + basket <- Future { + SigningBasketX.signingBasketProvider.vend.getSigningBasketByBasketId(basketid) + } map { + i => connectorEmptyResponse(i, callContext) + } + } yield { + (getSigningBasketResponseJson(basket), HttpCode.`200`(callContext)) } } } @@ -231,7 +219,7 @@ This function returns an array of hyperlinks to all generated authorisation sub- "authorisationIds" : "" }"""), List(UserNotLoggedIn, UnknownError), - ApiTag("Signing Baskets") :: apiTagMockedData :: Nil + apiTagSigningBaskets :: Nil ) lazy val getSigningBasketAuthorisation : OBPEndpoint = { @@ -239,10 +227,10 @@ This function returns an array of hyperlinks to all generated authorisation sub- cc => for { (Full(u), callContext) <- authenticatedAccess(cc) - } yield { - (json.parse("""{ - "authorisationIds" : "" -}"""), callContext) + _ <- passesPsd2Pisp(callContext) + (challenges, callContext) <- NewStyle.function.getChallengesByBasketId(basketid, callContext) + } yield { + (JSONFactory_BERLIN_GROUP_1_3.AuthorisationJsonV13(challenges.map(_.challengeId)), HttpCode.`200`(callContext)) } } } @@ -262,21 +250,26 @@ This method returns the SCA status of a signing basket's authorisation sub-resou "scaStatus" : "psuAuthenticated" }"""), List(UserNotLoggedIn, UnknownError), - ApiTag("Signing Baskets") :: apiTagMockedData :: Nil + apiTagSigningBaskets :: Nil ) lazy val getSigningBasketScaStatus : OBPEndpoint = { - case "signing-baskets" :: basketid:: "authorisations" :: authorisationid :: Nil JsonGet _ => { + case "signing-baskets" :: basketId:: "authorisations" :: authorisationId :: Nil JsonGet _ => { cc => for { (Full(u), callContext) <- authenticatedAccess(cc) - } yield { - (json.parse("""{ - "scaStatus" : "psuAuthenticated" -}"""), callContext) + _ <- passesPsd2Pisp(callContext) + _ <- Future(SigningBasketX.signingBasketProvider.vend.getSigningBasketByBasketId(basketId)) map { + unboxFullOrFail(_, callContext, s"$ConsentNotFound ($basketId)") + } + (challenges, callContext) <- NewStyle.function.getChallengesByBasketId(basketId, callContext) + } yield { + val challengeStatus = challenges.filter(_.challengeId == authorisationId) + .flatMap(_.scaStatus).headOption.map(_.toString).getOrElse("None") + (JSONFactory_BERLIN_GROUP_1_3.ScaStatusJsonV13(challengeStatus), HttpCode.`200`(callContext)) } } - } + } resourceDocs += ResourceDoc( getSigningBasketStatus, @@ -293,20 +286,23 @@ Returns the status of a signing basket object. "transactionStatus" : "RCVD" }"""), List(UserNotLoggedIn, UnknownError), - ApiTag("Signing Baskets") :: apiTagMockedData :: Nil + apiTagSigningBaskets :: Nil ) lazy val getSigningBasketStatus : OBPEndpoint = { - case "signing-baskets" :: basketid:: "status" :: Nil JsonGet _ => { + case "signing-baskets" :: basketid:: "status" :: Nil JsonGet _ => cc => for { (Full(u), callContext) <- authenticatedAccess(cc) - } yield { - (json.parse("""{ - "transactionStatus" : "RCVD" -}"""), callContext) + _ <- passesPsd2Pisp(callContext) + basket <- Future { + SigningBasketX.signingBasketProvider.vend.getSigningBasketByBasketId(basketid) + } map { + i => connectorEmptyResponse(i, callContext) + } + } yield { + (getSigningBasketStatusResponseJson(basket), HttpCode.`200`(callContext)) } - } } resourceDocs += ResourceDoc( @@ -376,39 +372,32 @@ This applies in the following scenarios: "psuMessage" : { } }"""), List(UserNotLoggedIn, UnknownError), - ApiTag("Signing Baskets") :: apiTagMockedData :: Nil + apiTagSigningBaskets :: Nil ) lazy val startSigningBasketAuthorisation : OBPEndpoint = { - case "signing-baskets" :: basketid:: "authorisations" :: Nil JsonPost _ => { + case "signing-baskets" :: basketId :: "authorisations" :: Nil JsonPost _ => { cc => for { (Full(u), callContext) <- authenticatedAccess(cc) - } yield { - (json.parse("""{ - "challengeData" : { - "otpMaxLength" : 0, - "additionalInformation" : "additionalInformation", - "image" : "image", - "imageLink" : "http://example.com/aeiou", - "otpFormat" : "characters", - "data" : "data" - }, - "scaMethods" : "", - "scaStatus" : "psuAuthenticated", - "_links" : { - "scaStatus" : "/v1.3/payments/sepa-credit-transfers/1234-wertiq-983", - "startAuthorisationWithEncryptedPsuAuthentication" : "/v1.3/payments/sepa-credit-transfers/1234-wertiq-983", - "scaRedirect" : "/v1.3/payments/sepa-credit-transfers/1234-wertiq-983", - "selectAuthenticationMethod" : "/v1.3/payments/sepa-credit-transfers/1234-wertiq-983", - "startAuthorisationWithPsuAuthentication" : "/v1.3/payments/sepa-credit-transfers/1234-wertiq-983", - "authoriseTransaction" : "/v1.3/payments/sepa-credit-transfers/1234-wertiq-983", - "scaOAuth" : "/v1.3/payments/sepa-credit-transfers/1234-wertiq-983", - "updatePsuIdentification" : "/v1.3/payments/sepa-credit-transfers/1234-wertiq-983" - }, - "chosenScaMethod" : "", - "psuMessage" : { } -}"""), callContext) + _ <- passesPsd2Pisp(callContext) + (challenges, callContext) <- NewStyle.function.createChallengesC3( + List(u.userId), + ChallengeType.BERLIN_GROUP_SIGNING_BASKETS_CHALLENGE, + None, + getSuggestedDefaultScaMethod(), + Some(StrongCustomerAuthenticationStatus.received), + None, + Some(basketId), + None, + callContext + ) + //NOTE: in OBP it support multiple challenges, but in Berlin Group it has only one challenge. The following guard is to make sure it return the 1st challenge properly. + challenge <- NewStyle.function.tryons(InvalidConnectorResponseForCreateChallenge, 400, callContext) { + challenges.head + } + } yield { + (createStartSigningBasketAuthorisationJson(basketId, challenge), HttpCode.`201`(callContext)) } } } @@ -462,21 +451,82 @@ There are the following request types on this access path: therefore many optional elements are not present. Maybe in a later version the access path will change. """, - emptyObjectJson, - json.parse(""""""""), + json.parse("""{"scaAuthenticationData":"123"}"""), + json.parse("""{ + "scaStatus":"finalised", + "authorisationId":"4f4a8b7f-9968-4183-92ab-ca512b396bfc", + "psuMessage":"Please check your SMS at a mobile device.", + "_links":{ + "scaStatus":"/v1.3/payments/sepa-credit-transfers/PAYMENT_ID/4f4a8b7f-9968-4183-92ab-ca512b396bfc" + } + }"""), List(UserNotLoggedIn, UnknownError), - ApiTag("Signing Baskets") :: apiTagMockedData :: Nil + apiTagSigningBaskets :: Nil ) lazy val updateSigningBasketPsuData : OBPEndpoint = { - case "signing-baskets" :: basketid:: "authorisations" :: authorisationid :: Nil JsonPut _ => { + case "signing-baskets" :: basketId:: "authorisations" :: authorisationId :: Nil JsonPut json -> _ => cc => for { (Full(u), callContext) <- authenticatedAccess(cc) - } yield { - (json.parse(""""""""), callContext) + _ <- passesPsd2Pisp(callContext) + failMsg = s"$InvalidJsonFormat The Json body should be the $UpdatePaymentPsuDataJson " + updateBasketPsuDataJson <- NewStyle.function.tryons(failMsg, 400, callContext) { + json.extract[UpdatePaymentPsuDataJson] + } + _ <- SigningBasketNewStyle.checkSigningBasketPayments(basketId, callContext) + // Validate a challenge answer and get an error if any + (boxedChallenge: Box[ChallengeTrait], callContext) <- NewStyle.function.validateChallengeAnswerC3( + ChallengeType.BERLIN_GROUP_SIGNING_BASKETS_CHALLENGE, + None, + None, + Some(basketId), + authorisationId, + updateBasketPsuDataJson.scaAuthenticationData, + callContext + ) + // Get the challenge after validation + (challenge: ChallengeTrait, callContext) <- NewStyle.function.getChallenge(authorisationId, callContext) + _ <- challenge.scaStatus match { + case Some(status) if status.toString == StrongCustomerAuthenticationStatus.finalised.toString => // finalised + Future { + val basket = SigningBasketX.signingBasketProvider.vend.getSigningBasketByBasketId(basketId) + val existAll: Box[Boolean] = + basket.flatMap(_.payments.map(_.forall(i => Connector.connector.vend.getTransactionRequestImpl(TransactionRequestId(i), callContext).isDefined))) + if (existAll.getOrElse(false)) { + basket.map { i => + i.payments.map(_.map { i => + Connector.connector.vend.saveTransactionRequestStatusImpl(TransactionRequestId(i), COMPLETED.toString) + Connector.connector.vend.getTransactionRequestImpl(TransactionRequestId(i), callContext) map { t => + Connector.connector.vend.makePaymentV400(t._1, None, callContext) + } + }) + } + SigningBasketX.signingBasketProvider.vend.saveSigningBasketStatus(basketId, ConstantsBG.SigningBasketsStatus.ACTC.toString) + unboxFullOrFail(boxedChallenge, callContext, s"$InvalidConnectorResponse ") + } else { // Fail due to unexisting payment + val paymentIds = basket.flatMap(_.payments).getOrElse(Nil).mkString(",") + unboxFullOrFail(Empty, callContext, s"$InvalidConnectorResponse Some of paymentIds [${paymentIds}] are invalid") + } + } + case Some(status) if status.toString == StrongCustomerAuthenticationStatus.failed.toString => // failed + Future { + // Reject all related transaction requests + val basket = SigningBasketX.signingBasketProvider.vend.getSigningBasketByBasketId(basketId) + basket.map { i => + i.payments.map(_.map { i => + Connector.connector.vend.saveTransactionRequestStatusImpl(TransactionRequestId(i), REJECTED.toString) + }) + } + // Fail in case of an error message + unboxFullOrFail(boxedChallenge, callContext, s"$InvalidConnectorResponse ") + } + case _ => // Fail in case of an error message + Future(unboxFullOrFail(Empty, callContext, s"$InvalidConnectorResponse ")) + } + } yield { + (JSONFactory_BERLIN_GROUP_1_3.createStartPaymentAuthorisationJson(challenge), callContext) } - } } } diff --git a/obp-api/src/main/scala/code/api/constant/constant.scala b/obp-api/src/main/scala/code/api/constant/constant.scala index bc0163d0a1..86a601cfe8 100644 --- a/obp-api/src/main/scala/code/api/constant/constant.scala +++ b/obp-api/src/main/scala/code/api/constant/constant.scala @@ -22,7 +22,7 @@ object Constant extends MdcLoggable { final val h2DatabaseDefaultUrlValue = "jdbc:h2:mem:OBPTest_H2_v2.1.214;NON_KEYWORDS=VALUE;DB_CLOSE_DELAY=10" - final val HostName = APIUtil.getPropsValue("hostname").openOrThrowException(ErrorMessages.HostnameNotSpecified) + def HostName = APIUtil.getPropsValue("hostname").openOrThrowException(ErrorMessages.HostnameNotSpecified) final val ApiInstanceId = { val apiInstanceIdFromProps = APIUtil.getPropsValue("api_instance_id") diff --git a/obp-api/src/main/scala/code/api/util/APIUtil.scala b/obp-api/src/main/scala/code/api/util/APIUtil.scala index 16105575fa..bb7070f21f 100644 --- a/obp-api/src/main/scala/code/api/util/APIUtil.scala +++ b/obp-api/src/main/scala/code/api/util/APIUtil.scala @@ -28,13 +28,13 @@ TESOBE (http://www.tesobe.com/) package code.api.util import bootstrap.liftweb.CustomDBVendor + import java.io.InputStream import java.net.URLDecoder import java.nio.charset.Charset import java.text.{ParsePosition, SimpleDateFormat} import java.util.concurrent.ConcurrentHashMap import java.util.{Calendar, Date, TimeZone, UUID} - import code.UserRefreshes.UserRefreshes import code.accountholders.AccountHolders import code.api.Constant._ @@ -57,6 +57,7 @@ import code.api.v2_0_0.CreateEntitlementJSON import code.api.dynamic.endpoint.helper.DynamicEndpointHelper import code.api.dynamic.entity.OBPAPIDynamicEntity import code.api._ +import code.api.berlin.group.v1_3.JSONFactory_BERLIN_GROUP_1_3.{ErrorMessageBG, ErrorMessagesBG} import code.api.dynamic.entity.helper.DynamicEntityHelper import code.api.v5_0_0.OBPAPI5_0_0 import code.api.v5_1_0.OBPAPI5_1_0 @@ -114,9 +115,9 @@ import javassist.{ClassPool, LoaderClassPath} import javassist.expr.{ExprEditor, MethodCall} import org.apache.commons.io.IOUtils import org.apache.commons.lang3.StringUtils + import java.security.AccessControlException import java.util.regex.Pattern - import code.api.util.FutureUtil.{EndpointContext, EndpointTimeout} import code.api.v2_1_0.OBPAPI2_1_0.Implementations2_1_0 import code.api.v2_2_0.OBPAPI2_2_0.Implementations2_2_0 @@ -728,7 +729,16 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{ case _ => (httpCode, getHeaders() ::: headers.list) } - val errorMessageAst: json.JValue = Extraction.decompose(ErrorMessage(message = message, code = code)) + def composeErrorMessage() = { + val path = callContextLight.map(_.url).getOrElse("") + if (path.contains("berlin-group")) { + val errorMessagesBG = ErrorMessagesBG(tppMessages = List(ErrorMessageBG(category = "ERROR", code = code, path = callContextLight.map(_.url).getOrElse(""), text = message))) + Extraction.decompose(errorMessagesBG) + } else { + Extraction.decompose(ErrorMessage(message = message, code = code)) + } + } + val errorMessageAst: json.JValue = composeErrorMessage() val httpBody = JsonAST.compactRender(errorMessageAst) val jwsHeaders: CustomResponseHeaders = getSignRequestHeadersError(callContextLight, httpBody) JsonResponse(errorMessageAst, responseHeaders ::: jwsHeaders.list, Nil, code) diff --git a/obp-api/src/main/scala/code/api/util/ApiTag.scala b/obp-api/src/main/scala/code/api/util/ApiTag.scala index 59fc4b65c1..998109a2ed 100644 --- a/obp-api/src/main/scala/code/api/util/ApiTag.scala +++ b/obp-api/src/main/scala/code/api/util/ApiTag.scala @@ -61,6 +61,7 @@ object ApiTag { val apiTagMetric = ResourceDocTag("Metric") val apiTagDocumentation = ResourceDocTag("Documentation") val apiTagBerlinGroup = ResourceDocTag("Berlin-Group") + val apiTagSigningBaskets = ResourceDocTag("Signing Baskets") val apiTagUKOpenBanking = ResourceDocTag("UKOpenBanking") val apiTagMXOpenFinance = ResourceDocTag("MXOpenFinance") val apiTagApiBuilder = ResourceDocTag("API-Builder") diff --git a/obp-api/src/main/scala/code/api/util/ExampleValue.scala b/obp-api/src/main/scala/code/api/util/ExampleValue.scala index 8777fcab92..2ce0791848 100644 --- a/obp-api/src/main/scala/code/api/util/ExampleValue.scala +++ b/obp-api/src/main/scala/code/api/util/ExampleValue.scala @@ -1491,7 +1491,10 @@ object ExampleValue { glossaryItems += makeGlossaryItem("direct_debit_id", directDebitIdExample) lazy val consentIdExample = ConnectorField(NoExampleProvided,NoDescriptionProvided) - glossaryItems += makeGlossaryItem("consent_id", consentIdExample) + glossaryItems += makeGlossaryItem("consent_id", consentIdExample) + + lazy val basketIdExample = ConnectorField(NoExampleProvided,NoDescriptionProvided) + glossaryItems += makeGlossaryItem("basket_id", basketIdExample) lazy val consentRequestPayloadExample = ConnectorField( """{ 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 5042e44026..af2a498072 100644 --- a/obp-api/src/main/scala/code/api/util/NewStyle.scala +++ b/obp-api/src/main/scala/code/api/util/NewStyle.scala @@ -1340,9 +1340,9 @@ object NewStyle extends MdcLoggable{ hashOfSuppliedAnswer: String, callContext: Option[CallContext] ): OBPReturnType[ChallengeTrait] = { - if(challengeType == ChallengeType.BERLINGROUP_PAYMENT_CHALLENGE && transactionRequestId.isEmpty ){ + if(challengeType == ChallengeType.BERLIN_GROUP_PAYMENT_CHALLENGE && transactionRequestId.isEmpty ){ Future{ throw new Exception(s"$UnknownError The following parameters can not be empty for BERLINGROUP_PAYMENT_CHALLENGE challengeType: paymentId($transactionRequestId) ")} - }else if(challengeType == ChallengeType.BERLINGROUP_CONSENT_CHALLENGE && consentId.isEmpty ){ + }else if(challengeType == ChallengeType.BERLIN_GROUP_CONSENT_CHALLENGE && consentId.isEmpty ){ Future{ throw new Exception(s"$UnknownError The following parameters can not be empty for BERLINGROUP_CONSENT_CHALLENGE challengeType: consentId($consentId) ")} }else{ Connector.connector.vend.validateChallengeAnswerC2( @@ -1360,6 +1360,32 @@ object NewStyle extends MdcLoggable{ } } } + def validateChallengeAnswerC3( + challengeType: ChallengeType.Value, + transactionRequestId: Option[String], + consentId: Option[String], + basketId: Option[String], + challengeId: String, + hashOfSuppliedAnswer: String, + callContext: Option[CallContext] + ): OBPReturnType[Box[ChallengeTrait]] = { + if(challengeType == ChallengeType.BERLIN_GROUP_PAYMENT_CHALLENGE && transactionRequestId.isEmpty ){ + Future{ throw new Exception(s"$UnknownError The following parameters can not be empty for BERLINGROUP_PAYMENT_CHALLENGE challengeType: paymentId($transactionRequestId) ")} + } else if(challengeType == ChallengeType.BERLIN_GROUP_CONSENT_CHALLENGE && consentId.isEmpty ){ + Future{ throw new Exception(s"$UnknownError The following parameters can not be empty for BERLINGROUP_CONSENT_CHALLENGE challengeType: consentId($consentId) ")} + } else if(challengeType == ChallengeType.BERLIN_GROUP_SIGNING_BASKETS_CHALLENGE && basketId.isEmpty ){ + Future{ throw new Exception(s"$UnknownError The following parameters can not be empty for BERLINGROUP_CONSENT_CHALLENGE challengeType: basketId($basketId) ")} + } else { + Connector.connector.vend.validateChallengeAnswerC3( + transactionRequestId: Option[String], + consentId: Option[String], + basketId: Option[String], + challengeId: String, + hashOfSuppliedAnswer: String, + callContext: Option[CallContext] + ) + } + } /** * @@ -1383,9 +1409,9 @@ object NewStyle extends MdcLoggable{ authenticationMethodId: Option[String], callContext: Option[CallContext] ) : OBPReturnType[List[ChallengeTrait]] = { - if(challengeType == ChallengeType.BERLINGROUP_PAYMENT_CHALLENGE && (transactionRequestId.isEmpty || scaStatus.isEmpty || scaMethod.isEmpty)){ + if(challengeType == ChallengeType.BERLIN_GROUP_PAYMENT_CHALLENGE && (transactionRequestId.isEmpty || scaStatus.isEmpty || scaMethod.isEmpty)){ Future{ throw new Exception(s"$UnknownError The following parameters can not be empty for BERLINGROUP_PAYMENT challengeType: paymentId($transactionRequestId), scaStatus($scaStatus), scaMethod($scaMethod) ")} - }else if(challengeType == ChallengeType.BERLINGROUP_CONSENT_CHALLENGE && (consentId.isEmpty || scaStatus.isEmpty || scaMethod.isEmpty)){ + }else if(challengeType == ChallengeType.BERLIN_GROUP_CONSENT_CHALLENGE && (consentId.isEmpty || scaStatus.isEmpty || scaMethod.isEmpty)){ Future{ throw new Exception(s"$UnknownError The following parameters can not be empty for BERLINGROUP_CONSENT challengeType: consentId($consentId), scaStatus($scaStatus), scaMethod($scaMethod) ")} }else{ Connector.connector.vend.createChallengesC2( @@ -1402,6 +1428,53 @@ object NewStyle extends MdcLoggable{ } } } + + /** + * + * @param userIds OBP support multiple challenges, we can ask different users to answer different challenges + * @param challengeType OBP support different challenge types, @see the Enum ChallengeType + * @param scaMethod @see the Enum StrongCustomerAuthentication + * @param scaStatus @see the Enum StrongCustomerAuthenticationStatus + * @param transactionRequestId it is also the BelinGroup PaymentId + * @param consentId + * @param basketId + * @param authenticationMethodId this is used for BelinGroup Consent + * @param callContext + * @return + */ + def createChallengesC3( + userIds: List[String], + challengeType: ChallengeType.Value, + transactionRequestId: Option[String], + scaMethod: Option[SCA], + scaStatus: Option[SCAStatus],//Only use for BerlinGroup Now + consentId: Option[String], // Note: consentId and transactionRequestId and basketId are exclusive here. + basketId: Option[String], // Note: consentId and transactionRequestId and basketId are exclusive here. + authenticationMethodId: Option[String], + callContext: Option[CallContext] + ) : OBPReturnType[List[ChallengeTrait]] = { + if(challengeType == ChallengeType.BERLIN_GROUP_PAYMENT_CHALLENGE && (transactionRequestId.isEmpty || scaStatus.isEmpty || scaMethod.isEmpty)){ + Future{ throw new Exception(s"$UnknownError The following parameters can not be empty for BERLINGROUP_PAYMENT challengeType: paymentId($transactionRequestId), scaStatus($scaStatus), scaMethod($scaMethod) ")} + } else if(challengeType == ChallengeType.BERLIN_GROUP_CONSENT_CHALLENGE && (consentId.isEmpty || scaStatus.isEmpty || scaMethod.isEmpty)){ + Future{ throw new Exception(s"$UnknownError The following parameters can not be empty for BERLINGROUP_CONSENT challengeType: consentId($consentId), scaStatus($scaStatus), scaMethod($scaMethod) ")} + } else if(challengeType == ChallengeType.BERLIN_GROUP_SIGNING_BASKETS_CHALLENGE && (basketId.isEmpty || scaStatus.isEmpty || scaMethod.isEmpty)){ + Future{ throw new Exception(s"$UnknownError The following parameters can not be empty for BERLINGROUP_CONSENT challengeType: basketId($basketId), scaStatus($scaStatus), scaMethod($scaMethod) ")} + } else { + Connector.connector.vend.createChallengesC3( + userIds: List[String], + challengeType: ChallengeType.Value, + transactionRequestId: Option[String], + scaMethod: Option[SCA], + scaStatus: Option[SCAStatus],//Only use for BerlinGroup Now + consentId: Option[String], // Note: consentId and transactionRequestId and consentId are exclusive here. + basketId: Option[String], // Note: consentId and transactionRequestId and consentId are exclusive here. + authenticationMethodId: Option[String], + callContext: Option[CallContext] + ) map { i => + (unboxFullOrFail(i._1, callContext, s"$InvalidConnectorResponseForCreateChallenge ", 400), i._2) + } + } + } def getChallengesByTransactionRequestId( transactionRequestId: String, @@ -1425,6 +1498,17 @@ object NewStyle extends MdcLoggable{ (unboxFullOrFail(i._1, callContext, s"$InvalidChallengeTransactionRequestId Current transactionRequestId($consentId) ", 400), i._2) } } + def getChallengesByBasketId( + basketId: String, + callContext: Option[CallContext] + ): OBPReturnType[List[ChallengeTrait]] = { + Connector.connector.vend.getChallengesByBasketId( + basketId: String, + callContext: Option[CallContext] + ) map { i => + (unboxFullOrFail(i._1, callContext, s"$InvalidChallengeTransactionRequestId Current basketId($basketId) ", 400), i._2) + } + } def getChallenge( challengeId: String, diff --git a/obp-api/src/main/scala/code/api/util/newstyle/SigningBasketNewStyle.scala b/obp-api/src/main/scala/code/api/util/newstyle/SigningBasketNewStyle.scala new file mode 100644 index 0000000000..d0ce26305e --- /dev/null +++ b/obp-api/src/main/scala/code/api/util/newstyle/SigningBasketNewStyle.scala @@ -0,0 +1,38 @@ +package code.api.util.newstyle + +import code.api.util.APIUtil.{OBPReturnType, unboxFullOrFail} +import code.api.util.CallContext +import code.api.util.ErrorMessages.{InvalidConnectorResponse, RegulatedEntityNotDeleted} +import code.bankconnectors.Connector +import code.signingbaskets.SigningBasketX +import com.openbankproject.commons.model.TransactionRequestId +import net.liftweb.common.{Box, Empty} + +import scala.concurrent.Future + +object SigningBasketNewStyle { + + import com.openbankproject.commons.ExecutionContext.Implicits.global + + def checkSigningBasketPayments(basketId: String, + callContext: Option[CallContext] + ): OBPReturnType[Boolean] = { + Future { + val basket = SigningBasketX.signingBasketProvider.vend.getSigningBasketByBasketId(basketId) + val existAll: Box[Boolean] = + basket.flatMap(_.payments.map(_.forall(i => Connector.connector.vend.getTransactionRequestImpl(TransactionRequestId(i), callContext).isDefined))) + if (existAll.getOrElse(false)) { + Some(true) + } else { // Fail due to unexisting payment + val paymentIds = basket.flatMap(_.payments).getOrElse(Nil).mkString(",") + unboxFullOrFail(Empty, callContext, s"$InvalidConnectorResponse Some of paymentIds [${paymentIds}] are invalid") + } + } map { + (_, callContext) + } map { + x => (unboxFullOrFail(x._1, callContext, RegulatedEntityNotDeleted, 400), x._2) + } + } + + +} diff --git a/obp-api/src/main/scala/code/api/v4_0_0/APIMethods400.scala b/obp-api/src/main/scala/code/api/v4_0_0/APIMethods400.scala index 855044d64e..04e503d180 100644 --- a/obp-api/src/main/scala/code/api/v4_0_0/APIMethods400.scala +++ b/obp-api/src/main/scala/code/api/v4_0_0/APIMethods400.scala @@ -4399,7 +4399,8 @@ trait APIMethods400 extends MdcLoggable { postJson <- NewStyle.function.tryons(failMsg, 400, cc.callContext) { json.extract[PostAccountAccessJsonV400] } - _ <- Helper.booleanToFuture(UserLacksPermissionCanGrantAccessToViewForTargetAccount, cc = cc.callContext) { + msg = UserLacksPermissionCanGrantAccessToViewForTargetAccount + s"Current ViewId(${postJson.view.view_id}) and current UserId(${u.userId})" + _ <- Helper.booleanToFuture(msg, cc = cc.callContext) { APIUtil.canGrantAccessToView(bankId, accountId, ViewId(postJson.view.view_id), u, callContext) } (user, callContext) <- NewStyle.function.findByUserId(postJson.user_id, callContext) @@ -4461,7 +4462,8 @@ trait APIMethods400 extends MdcLoggable { postJson.provider.startsWith("dauth.") } viewIdList = postJson.views.map(view =>ViewId(view.view_id)) - _ <- Helper.booleanToFuture(s"$UserLacksPermissionCanGrantAccessToViewForTargetAccount ", 403, cc = Some(cc)) { + msg = UserLacksPermissionCanGrantAccessToViewForTargetAccount + s"Current ViewIds(${viewIdList.mkString}) and current UserId(${u.userId})" + _ <- Helper.booleanToFuture(msg, 403, cc = Some(cc)) { APIUtil.canGrantAccessToMultipleViews(bankId, accountId, viewIdList, u, callContext) } (targetUser, callContext) <- NewStyle.function.getOrCreateResourceUser(postJson.provider, postJson.username, cc.callContext) @@ -4512,7 +4514,8 @@ trait APIMethods400 extends MdcLoggable { json.extract[PostAccountAccessJsonV400] } viewId = ViewId(postJson.view.view_id) - _ <- Helper.booleanToFuture(UserLacksPermissionCanGrantAccessToViewForTargetAccount, cc = cc.callContext) { + msg = UserLacksPermissionCanGrantAccessToViewForTargetAccount + s"Current ViewId(${viewId}) and current UserId(${u.userId})" + _ <- Helper.booleanToFuture(msg, cc = cc.callContext) { APIUtil.canRevokeAccessToView(bankId, accountId, viewId, u, callContext) } (user, callContext) <- NewStyle.function.findByUserId(postJson.user_id, cc.callContext) @@ -4566,7 +4569,8 @@ trait APIMethods400 extends MdcLoggable { postJson <- NewStyle.function.tryons(failMsg, 400, cc.callContext) { json.extract[PostRevokeGrantAccountAccessJsonV400] } - _ <- Helper.booleanToFuture(UserLacksPermissionCanGrantAccessToViewForTargetAccount, cc = cc.callContext) { + msg = UserLacksPermissionCanGrantAccessToViewForTargetAccount + s"Current ViewIds(${postJson.views.mkString}) and current UserId(${u.userId})" + _ <- Helper.booleanToFuture(msg, cc = cc.callContext) { APIUtil.canRevokeAccessToAllViews(bankId, accountId, u, callContext) } _ <- Future(Views.views.vend.revokeAccountAccessByUser(bankId, accountId, u, callContext)) map { 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 09cd23b1fd..d05fe45747 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 @@ -1801,7 +1801,7 @@ trait APIMethods510 { | Please note that JWT must be signed with the counterpart private kew of the public key used to establish mTLS | |""", - ConsumerJwtPostJsonV510("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJkZXNjcmlwdGlvbiI6IkRlc2NyaXB0aW9uIn0.qDnzk1dGK8akdLFRl8fmJV_SeoDjRTDG_eMogCIzZ7M"), + ConsumerJwtPostJsonV510("eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJkZXNjcmlwdGlvbiI6IlRQUCBkZXNjcmlwdGlvbiJ9.c5gPPsyUmnVW774y7h2xyLXg0wdtu25nbU2AvOmyzcWa7JTdCKuuy3CblxueGwqYkQDDQIya1Qny4blyAvh_a1Q28LgzEKBcH7Em9FZXerhkvR9v4FWbCC5AgNLdQ7sR8-rUQdShmJcGDKdVmsZjuO4XhY2Zx0nFnkcvYfsU9bccoAvkKpVJATXzwBqdoEOuFlplnbxsMH1wWbAd3hbcPPWTdvO43xavNZTB5ybgrXVDEYjw8D-98_ZkqxS0vfvhJ4cGefHViaFzp6zXm7msdBpcE__O9rFbdl9Gvup_bsMbrHJioIrmc2d15Yc-tTNTF9J4qjD_lNxMRlx5o2TZEw"), consumerJsonV510, List( InvalidJsonFormat, diff --git a/obp-api/src/main/scala/code/bankconnectors/Connector.scala b/obp-api/src/main/scala/code/bankconnectors/Connector.scala index 08278efb5d..077c481c68 100644 --- a/obp-api/src/main/scala/code/bankconnectors/Connector.scala +++ b/obp-api/src/main/scala/code/bankconnectors/Connector.scala @@ -421,7 +421,19 @@ trait Connector extends MdcLoggable { consentId: Option[String], // Note: consentId and transactionRequestId are exclusive here. authenticationMethodId: Option[String], callContext: Option[CallContext]) : OBPReturnType[Box[List[ChallengeTrait]]]= Future{(Failure(setUnimplementedError), callContext)} - + + // now, we try to share the same challenges for obp payments, berlin group payments, berlin group consents and signing baskets + def createChallengesC3( + userIds: List[String], + challengeType: ChallengeType.Value, + transactionRequestId: Option[String], + scaMethod: Option[SCA], + scaStatus: Option[SCAStatus],//Only use for BerlinGroup Now + consentId: Option[String], // Note: consentId and transactionRequestId and basketId are exclusive here. + basketId: Option[String], // Note: consentId and transactionRequestId and basketId are exclusive here. + authenticationMethodId: Option[String], + callContext: Option[CallContext]) : OBPReturnType[Box[List[ChallengeTrait]]]= Future{(Failure(setUnimplementedError), callContext)} + // Validates an answer for a challenge and returns if the answer is correct or not def validateChallengeAnswer(challengeId: String, hashOfSuppliedAnswer: String, callContext: Option[CallContext]): OBPReturnType[Box[Boolean]] = Future{(Full(true), callContext)} @@ -439,11 +451,20 @@ trait Connector extends MdcLoggable { hashOfSuppliedAnswer: String, callContext: Option[CallContext] ): OBPReturnType[Box[ChallengeTrait]] = Future{(Failure(setUnimplementedError), callContext)} + def validateChallengeAnswerC3( + transactionRequestId: Option[String], + consentId: Option[String], + basketId: Option[String], + challengeId: String, + hashOfSuppliedAnswer: String, + callContext: Option[CallContext] + ): OBPReturnType[Box[ChallengeTrait]] = Future{(Failure(setUnimplementedError), callContext)} def getChallengesByTransactionRequestId(transactionRequestId: String, callContext: Option[CallContext]): OBPReturnType[Box[List[ChallengeTrait]]] = Future{(Failure(setUnimplementedError), callContext)} def getChallengesByConsentId(consentId: String, callContext: Option[CallContext]): OBPReturnType[Box[List[ChallengeTrait]]] = Future{(Failure(setUnimplementedError), callContext)} - + def getChallengesByBasketId(basketId: String, callContext: Option[CallContext]): OBPReturnType[Box[List[ChallengeTrait]]] = Future{(Failure(setUnimplementedError), callContext)} + def getChallenge(challengeId: String, callContext: Option[CallContext]): OBPReturnType[Box[ChallengeTrait]] = Future{(Failure(setUnimplementedError), callContext)} //gets a particular bank handled by this connector @@ -1428,7 +1449,7 @@ trait Connector extends MdcLoggable { didSaveTransId <- Future{saveTransactionRequestTransaction(transactionRequestId, transactionId).openOrThrowException(attemptedToOpenAnEmptyBox)} didSaveStatus <- Future{saveTransactionRequestStatusImpl(transactionRequestId, TransactionRequestStatus.COMPLETED.toString).openOrThrowException(attemptedToOpenAnEmptyBox)} //After `makePaymentv200` and update data for request, we get the new requqest from database again. - (transactionRequest, callContext) <- NewStyle.function.getTransactionRequestImpl(transactionRequestId, callContext) + (transactionRequest: TransactionRequest, callContext) <- NewStyle.function.getTransactionRequestImpl(transactionRequestId, callContext) } yield { (Full(transactionRequest), callContext) diff --git a/obp-api/src/main/scala/code/bankconnectors/ConnectorBuilderUtil.scala b/obp-api/src/main/scala/code/bankconnectors/ConnectorBuilderUtil.scala index bdce90a3fb..70610ebaec 100644 --- a/obp-api/src/main/scala/code/bankconnectors/ConnectorBuilderUtil.scala +++ b/obp-api/src/main/scala/code/bankconnectors/ConnectorBuilderUtil.scala @@ -365,11 +365,13 @@ object ConnectorBuilderUtil { "checkExternalUserCredentials", "checkExternalUserExists", "createChallengesC2", + "createChallengesC3", "getChallenge", "getChallengesByTransactionRequestId", "getChallengesByConsentId", "validateAndCheckIbanNumber", "validateChallengeAnswerC2", + "validateChallengeAnswerC3", "getCounterpartyByIbanAndBankAccountId", ).distinct diff --git a/obp-api/src/main/scala/code/bankconnectors/LocalMappedConnector.scala b/obp-api/src/main/scala/code/bankconnectors/LocalMappedConnector.scala index a963fe9859..70d8be3460 100644 --- a/obp-api/src/main/scala/code/bankconnectors/LocalMappedConnector.scala +++ b/obp-api/src/main/scala/code/bankconnectors/LocalMappedConnector.scala @@ -266,6 +266,7 @@ object LocalMappedConnector extends Connector with MdcLoggable { None, //there are only for new version, set the empty here. None,//there are only for new version, set the empty here. None,//there are only for new version, set the empty here. + None,//there are only for new version, set the empty here. challengeType = OBP_TRANSACTION_REQUEST_CHALLENGE.toString, callContext: Option[CallContext]) (challenge._1.map(_.challengeId),challenge._2) @@ -296,6 +297,7 @@ object LocalMappedConnector extends Connector with MdcLoggable { None, //there are only for new version, set the empty here. None,//there are only for new version, set the empty here. None,//there are only for new version, set the empty here. + None,//there are only for new version, set the empty here. challengeType = OBP_TRANSACTION_REQUEST_CHALLENGE.toString, callContext ) @@ -323,24 +325,43 @@ object LocalMappedConnector extends Connector with MdcLoggable { scaMethod, scaStatus, consentId, + None, // Signing Baskets are introduced in case of version createChallengesC3 authenticationMethodId, challengeType = OBP_TRANSACTION_REQUEST_CHALLENGE.toString, callContext ) challengeId.toList } + (Full(challenges.flatten), callContext) + } - //We use obp MappedExpectedChallengeAnswer instead of Authorisations now. - // please also check Challenges.ChallengeProvider.vend.saveChallenge -// Authorisations.authorisationProvider.vend.createAuthorization( -// transactionRequestId.getOrElse(""), -// consentId.getOrElse(""), -// AuthenticationType.SMS_OTP.toString, -// "", -// ScaStatus.received.toString, -// "12345" // TODO Implement SMS sending -// ) - + override def createChallengesC3( + userIds: List[String], + challengeType: ChallengeType.Value, + transactionRequestId: Option[String], // Note: consentId and transactionRequestId and basketId are exclusive here. + scaMethod: Option[SCA], + scaStatus: Option[SCAStatus],//Only use for BerlinGroup Now + consentId: Option[String], // Note: consentId and transactionRequestId and basketId are exclusive here. + basketId: Option[String], // Note: consentId and transactionRequestId and basketId are exclusive here. + authenticationMethodId: Option[String], + callContext: Option[CallContext] + ): OBPReturnType[Box[List[ChallengeTrait]]] = Future { + val challenges = for { + userId <- userIds + } yield { + val (challengeId, _) = createChallengeInternal( + userId, + transactionRequestId.getOrElse(""), + scaMethod, + scaStatus, + consentId, + basketId, + authenticationMethodId, + challengeType = challengeType.toString, + callContext + ) + challengeId.toList + } (Full(challenges.flatten), callContext) } @@ -349,7 +370,8 @@ object LocalMappedConnector extends Connector with MdcLoggable { transactionRequestId: String, scaMethod: Option[SCA], scaStatus: Option[SCAStatus], //Only use for BerlinGroup Now - consentId: Option[String], // Note: consentId and transactionRequestId are exclusive here. + consentId: Option[String], // Note: consentId and transactionRequestId and BasketId are exclusive here. + basketId: Option[String], // Note: consentId and transactionRequestId and BasketId are exclusive here. authenticationMethodId: Option[String], challengeType: String, callContext: Option[CallContext] @@ -367,6 +389,7 @@ object LocalMappedConnector extends Connector with MdcLoggable { scaMethod, scaStatus, consentId, + basketId, authenticationMethodId, challengeType), callContext) } @@ -436,12 +459,27 @@ object LocalMappedConnector extends Connector with MdcLoggable { (Challenges.ChallengeProvider.vend.validateChallenge(challengeId, hashOfSuppliedAnswer, userId), callContext) } } + override def validateChallengeAnswerC3( + transactionRequestId: Option[String], + consentId: Option[String], + basketId: Option[String], + challengeId: String, + hashOfSuppliedAnswer: String, + callContext: Option[CallContext] + ) = Future { + Future { + val userId = callContext.map(_.user.map(_.userId).openOrThrowException(s"$UserNotLoggedIn Can not find the userId here.")) + (Challenges.ChallengeProvider.vend.validateChallenge(challengeId, hashOfSuppliedAnswer, userId), callContext) + } + } override def getChallengesByTransactionRequestId(transactionRequestId: String, callContext: Option[CallContext]): OBPReturnType[Box[List[ChallengeTrait]]] = Future {(Challenges.ChallengeProvider.vend.getChallengesByTransactionRequestId(transactionRequestId), callContext)} override def getChallengesByConsentId(consentId: String, callContext: Option[CallContext]): OBPReturnType[Box[List[ChallengeTrait]]] = Future {(Challenges.ChallengeProvider.vend.getChallengesByConsentId(consentId), callContext)} + override def getChallengesByBasketId(basketId: String, callContext: Option[CallContext]): OBPReturnType[Box[List[ChallengeTrait]]] = + Future {(Challenges.ChallengeProvider.vend.getChallengesByBasketId(basketId), callContext)} override def getChallenge(challengeId: String, callContext: Option[CallContext]): OBPReturnType[Box[ChallengeTrait]] = diff --git a/obp-api/src/main/scala/code/bankconnectors/akka/AkkaConnector_vDec2018.scala b/obp-api/src/main/scala/code/bankconnectors/akka/AkkaConnector_vDec2018.scala index 9e6ca7c2c2..c9712ced55 100644 --- a/obp-api/src/main/scala/code/bankconnectors/akka/AkkaConnector_vDec2018.scala +++ b/obp-api/src/main/scala/code/bankconnectors/akka/AkkaConnector_vDec2018.scala @@ -308,7 +308,7 @@ object AkkaConnector_vDec2018 extends Connector with AkkaConnectorActorInit { //---------------- dynamic start -------------------please don't modify this line -// ---------- created on 2022-03-11T18:42:02Z +// ---------- created on 2024-01-29T13:57:05Z messageDocs += validateAndCheckIbanNumberDoc def validateAndCheckIbanNumberDoc = MessageDoc( @@ -332,7 +332,7 @@ object AkkaConnector_vDec2018 extends Connector with AkkaConnectorActorInit { city=cityExample.value, zip="string", phone=phoneExample.value, - country="string", + country=countryExample.value, countryIso="string", sepaCreditTransfer=sepaCreditTransferExample.value, sepaDirectDebit=sepaDirectDebitExample.value, @@ -545,9 +545,11 @@ object AkkaConnector_vDec2018 extends Connector with AkkaConnectorActorInit { successful=true, challengeType=challengeTypeExample.value, consentId=Some(consentIdExample.value), + basketId=Some(basketIdExample.value), scaMethod=Some(com.openbankproject.commons.model.enums.StrongCustomerAuthentication.SMS), scaStatus=Some(com.openbankproject.commons.model.enums.StrongCustomerAuthenticationStatus.example), - authenticationMethodId=Some("string")))) + authenticationMethodId=Some("string"), + attemptCounter=123))) ), adapterImplementation = Some(AdapterImplementation("- Core", 1)) ) @@ -559,6 +561,51 @@ object AkkaConnector_vDec2018 extends Connector with AkkaConnectorActorInit { response.map(convertToTuple[List[ChallengeCommons]](callContext)) } + messageDocs += createChallengesC3Doc + def createChallengesC3Doc = MessageDoc( + process = "obp.createChallengesC3", + messageFormat = messageFormat, + description = "Create Challenges C3", + outboundTopic = None, + inboundTopic = None, + exampleOutboundMessage = ( + OutBoundCreateChallengesC3(outboundAdapterCallContext=MessageDocsSwaggerDefinitions.outboundAdapterCallContext, + userIds=listExample.value.split("[,;]").toList, + challengeType=com.openbankproject.commons.model.enums.ChallengeType.example, + transactionRequestId=Some(transactionRequestIdExample.value), + scaMethod=Some(com.openbankproject.commons.model.enums.StrongCustomerAuthentication.SMS), + scaStatus=Some(com.openbankproject.commons.model.enums.StrongCustomerAuthenticationStatus.example), + consentId=Some(consentIdExample.value), + basketId=Some(basketIdExample.value), + authenticationMethodId=Some("string")) + ), + exampleInboundMessage = ( + InBoundCreateChallengesC3(inboundAdapterCallContext=MessageDocsSwaggerDefinitions.inboundAdapterCallContext, + status=MessageDocsSwaggerDefinitions.inboundStatus, + data=List( ChallengeCommons(challengeId=challengeIdExample.value, + transactionRequestId=transactionRequestIdExample.value, + expectedAnswer="string", + expectedUserId="string", + salt="string", + successful=true, + challengeType=challengeTypeExample.value, + consentId=Some(consentIdExample.value), + basketId=Some(basketIdExample.value), + scaMethod=Some(com.openbankproject.commons.model.enums.StrongCustomerAuthentication.SMS), + scaStatus=Some(com.openbankproject.commons.model.enums.StrongCustomerAuthenticationStatus.example), + authenticationMethodId=Some("string"), + attemptCounter=123))) + ), + adapterImplementation = Some(AdapterImplementation("- Core", 1)) + ) + + override def createChallengesC3(userIds: List[String], challengeType: ChallengeType.Value, transactionRequestId: Option[String], scaMethod: Option[StrongCustomerAuthentication.SCA], scaStatus: Option[SCAStatus], consentId: Option[String], basketId: Option[String], authenticationMethodId: Option[String], callContext: Option[CallContext]): OBPReturnType[Box[List[ChallengeTrait]]] = { + import com.openbankproject.commons.dto.{InBoundCreateChallengesC3 => InBound, OutBoundCreateChallengesC3 => OutBound} + val req = OutBound(callContext.map(_.toOutboundAdapterCallContext).orNull, userIds, challengeType, transactionRequestId, scaMethod, scaStatus, consentId, basketId, authenticationMethodId) + val response: Future[Box[InBound]] = (southSideActor ? req).mapTo[InBound].recoverWith(recoverFunction).map(Box !! _) + response.map(convertToTuple[List[ChallengeCommons]](callContext)) + } + messageDocs += validateChallengeAnswerDoc def validateChallengeAnswerDoc = MessageDoc( process = "obp.validateChallengeAnswer", @@ -611,9 +658,11 @@ object AkkaConnector_vDec2018 extends Connector with AkkaConnectorActorInit { successful=true, challengeType=challengeTypeExample.value, consentId=Some(consentIdExample.value), + basketId=Some(basketIdExample.value), scaMethod=Some(com.openbankproject.commons.model.enums.StrongCustomerAuthentication.SMS), scaStatus=Some(com.openbankproject.commons.model.enums.StrongCustomerAuthenticationStatus.example), - authenticationMethodId=Some("string"))) + authenticationMethodId=Some("string"), + attemptCounter=123)) ), adapterImplementation = Some(AdapterImplementation("- Core", 1)) ) @@ -625,6 +674,48 @@ object AkkaConnector_vDec2018 extends Connector with AkkaConnectorActorInit { response.map(convertToTuple[ChallengeCommons](callContext)) } + messageDocs += validateChallengeAnswerC3Doc + def validateChallengeAnswerC3Doc = MessageDoc( + process = "obp.validateChallengeAnswerC3", + messageFormat = messageFormat, + description = "Validate Challenge Answer C3", + outboundTopic = None, + inboundTopic = None, + exampleOutboundMessage = ( + OutBoundValidateChallengeAnswerC3(outboundAdapterCallContext=MessageDocsSwaggerDefinitions.outboundAdapterCallContext, + transactionRequestId=Some(transactionRequestIdExample.value), + consentId=Some(consentIdExample.value), + basketId=Some(basketIdExample.value), + challengeId=challengeIdExample.value, + hashOfSuppliedAnswer=hashOfSuppliedAnswerExample.value) + ), + exampleInboundMessage = ( + InBoundValidateChallengeAnswerC3(inboundAdapterCallContext=MessageDocsSwaggerDefinitions.inboundAdapterCallContext, + status=MessageDocsSwaggerDefinitions.inboundStatus, + data= ChallengeCommons(challengeId=challengeIdExample.value, + transactionRequestId=transactionRequestIdExample.value, + expectedAnswer="string", + expectedUserId="string", + salt="string", + successful=true, + challengeType=challengeTypeExample.value, + consentId=Some(consentIdExample.value), + basketId=Some(basketIdExample.value), + scaMethod=Some(com.openbankproject.commons.model.enums.StrongCustomerAuthentication.SMS), + scaStatus=Some(com.openbankproject.commons.model.enums.StrongCustomerAuthenticationStatus.example), + authenticationMethodId=Some("string"), + attemptCounter=123)) + ), + adapterImplementation = Some(AdapterImplementation("- Core", 1)) + ) + + override def validateChallengeAnswerC3(transactionRequestId: Option[String], consentId: Option[String], basketId: Option[String], challengeId: String, hashOfSuppliedAnswer: String, callContext: Option[CallContext]): OBPReturnType[Box[ChallengeTrait]] = { + import com.openbankproject.commons.dto.{InBoundValidateChallengeAnswerC3 => InBound, OutBoundValidateChallengeAnswerC3 => OutBound} + val req = OutBound(callContext.map(_.toOutboundAdapterCallContext).orNull, transactionRequestId, consentId, basketId, challengeId, hashOfSuppliedAnswer) + val response: Future[Box[InBound]] = (southSideActor ? req).mapTo[InBound].recoverWith(recoverFunction).map(Box !! _) + response.map(convertToTuple[ChallengeCommons](callContext)) + } + messageDocs += getChallengesByTransactionRequestIdDoc def getChallengesByTransactionRequestIdDoc = MessageDoc( process = "obp.getChallengesByTransactionRequestId", @@ -647,9 +738,11 @@ object AkkaConnector_vDec2018 extends Connector with AkkaConnectorActorInit { successful=true, challengeType=challengeTypeExample.value, consentId=Some(consentIdExample.value), + basketId=Some(basketIdExample.value), scaMethod=Some(com.openbankproject.commons.model.enums.StrongCustomerAuthentication.SMS), scaStatus=Some(com.openbankproject.commons.model.enums.StrongCustomerAuthenticationStatus.example), - authenticationMethodId=Some("string")))) + authenticationMethodId=Some("string"), + attemptCounter=123))) ), adapterImplementation = Some(AdapterImplementation("- Core", 1)) ) @@ -683,9 +776,11 @@ object AkkaConnector_vDec2018 extends Connector with AkkaConnectorActorInit { successful=true, challengeType=challengeTypeExample.value, consentId=Some(consentIdExample.value), + basketId=Some(basketIdExample.value), scaMethod=Some(com.openbankproject.commons.model.enums.StrongCustomerAuthentication.SMS), scaStatus=Some(com.openbankproject.commons.model.enums.StrongCustomerAuthenticationStatus.example), - authenticationMethodId=Some("string")))) + authenticationMethodId=Some("string"), + attemptCounter=123))) ), adapterImplementation = Some(AdapterImplementation("- Core", 1)) ) @@ -719,9 +814,11 @@ object AkkaConnector_vDec2018 extends Connector with AkkaConnectorActorInit { successful=true, challengeType=challengeTypeExample.value, consentId=Some(consentIdExample.value), + basketId=Some(basketIdExample.value), scaMethod=Some(com.openbankproject.commons.model.enums.StrongCustomerAuthentication.SMS), scaStatus=Some(com.openbankproject.commons.model.enums.StrongCustomerAuthenticationStatus.example), - authenticationMethodId=Some("string"))) + authenticationMethodId=Some("string"), + attemptCounter=123)) ), adapterImplementation = Some(AdapterImplementation("- Core", 1)) ) @@ -958,7 +1055,7 @@ object AkkaConnector_vDec2018 extends Connector with AkkaConnectorActorInit { adapterImplementation = Some(AdapterImplementation("- Core", 1)) ) - override def getBankAccountByRoutingLegacy(bankId: Option[BankId], scheme: String, address: String, callContext: Option[CallContext]): Box[(BankAccount, Option[CallContext])] = { + override def getBankAccountByRouting(bankId: Option[BankId], scheme: String, address: String, callContext: Option[CallContext]): OBPReturnType[Box[BankAccount]] = { import com.openbankproject.commons.dto.{InBoundGetBankAccountByRouting => InBound, OutBoundGetBankAccountByRouting => OutBound} val req = OutBound(callContext.map(_.toOutboundAdapterCallContext).orNull, bankId, scheme, address) val response: Future[Box[InBound]] = (southSideActor ? req).mapTo[InBound].recoverWith(recoverFunction).map(Box !! _) @@ -1431,8 +1528,9 @@ object AkkaConnector_vDec2018 extends Connector with AkkaConnectorActorInit { reasonRequested=com.openbankproject.commons.model.PinResetReason.FORGOT)), collected=Some(CardCollectionInfo(toDate(collectedExample))), posted=Some(CardPostedInfo(toDate(postedExample))), - customerId=customerIdExample.value - ))) + customerId=customerIdExample.value, + cvv=Some(cvvExample.value), + brand=Some(brandExample.value)))) ), adapterImplementation = Some(AdapterImplementation("- Core", 1)) ) @@ -1498,8 +1596,9 @@ object AkkaConnector_vDec2018 extends Connector with AkkaConnectorActorInit { reasonRequested=com.openbankproject.commons.model.PinResetReason.FORGOT)), collected=Some(CardCollectionInfo(toDate(collectedExample))), posted=Some(CardPostedInfo(toDate(postedExample))), - customerId=customerIdExample.value - )) + customerId=customerIdExample.value, + cvv=Some(cvvExample.value), + brand=Some(brandExample.value))) ), adapterImplementation = Some(AdapterImplementation("- Core", 1)) ) @@ -1613,7 +1712,9 @@ object AkkaConnector_vDec2018 extends Connector with AkkaConnectorActorInit { reasonRequested=com.openbankproject.commons.model.PinResetReason.FORGOT)), collected=Some(CardCollectionInfo(toDate(collectedExample))), posted=Some(CardPostedInfo(toDate(postedExample))), - customerId=customerIdExample.value))) + customerId=customerIdExample.value, + cvv=Some(cvvExample.value), + brand=Some(brandExample.value)))) ), adapterImplementation = Some(AdapterImplementation("- Core", 1)) ) @@ -1656,8 +1757,8 @@ object AkkaConnector_vDec2018 extends Connector with AkkaConnectorActorInit { collected=Some(CardCollectionInfo(toDate(collectedExample))), posted=Some(CardPostedInfo(toDate(postedExample))), customerId=customerIdExample.value, - cvv = cvvExample.value, - brand = brandExample.value) + cvv=cvvExample.value, + brand=brandExample.value) ), exampleInboundMessage = ( InBoundCreatePhysicalCard(inboundAdapterCallContext=MessageDocsSwaggerDefinitions.inboundAdapterCallContext, @@ -1701,17 +1802,14 @@ object AkkaConnector_vDec2018 extends Connector with AkkaConnectorActorInit { reasonRequested=com.openbankproject.commons.model.PinResetReason.FORGOT)), collected=Some(CardCollectionInfo(toDate(collectedExample))), posted=Some(CardPostedInfo(toDate(postedExample))), - customerId=customerIdExample.value)) + customerId=customerIdExample.value, + cvv=Some(cvvExample.value), + brand=Some(brandExample.value))) ), adapterImplementation = Some(AdapterImplementation("- Core", 1)) ) - override def createPhysicalCard(bankCardNumber: String, nameOnCard: String, cardType: String, issueNumber: String, - serialNumber: String, validFrom: Date, expires: Date, enabled: Boolean, cancelled: Boolean, onHotList: Boolean, - technology: String, networks: List[String], allows: List[String], accountId: String, bankId: String, - replacement: Option[CardReplacementInfo], pinResets: List[PinResetInfo], collected: Option[CardCollectionInfo], - posted: Option[CardPostedInfo], customerId: String, cvv: String, brand: String, - callContext: Option[CallContext]): OBPReturnType[Box[PhysicalCard]] = { + override def createPhysicalCard(bankCardNumber: String, nameOnCard: String, cardType: String, issueNumber: String, serialNumber: String, validFrom: Date, expires: Date, enabled: Boolean, cancelled: Boolean, onHotList: Boolean, technology: String, networks: List[String], allows: List[String], accountId: String, bankId: String, replacement: Option[CardReplacementInfo], pinResets: List[PinResetInfo], collected: Option[CardCollectionInfo], posted: Option[CardPostedInfo], customerId: String, cvv: String, brand: String, callContext: Option[CallContext]): OBPReturnType[Box[PhysicalCard]] = { import com.openbankproject.commons.dto.{InBoundCreatePhysicalCard => InBound, OutBoundCreatePhysicalCard => OutBound} val req = OutBound(callContext.map(_.toOutboundAdapterCallContext).orNull, bankCardNumber, nameOnCard, cardType, issueNumber, serialNumber, validFrom, expires, enabled, cancelled, onHotList, technology, networks, allows, accountId, bankId, replacement, pinResets, collected, posted, customerId, cvv, brand) val response: Future[Box[InBound]] = (southSideActor ? req).mapTo[InBound].recoverWith(recoverFunction).map(Box !! _) @@ -1793,7 +1891,9 @@ object AkkaConnector_vDec2018 extends Connector with AkkaConnectorActorInit { reasonRequested=com.openbankproject.commons.model.PinResetReason.FORGOT)), collected=Some(CardCollectionInfo(toDate(collectedExample))), posted=Some(CardPostedInfo(toDate(postedExample))), - customerId=customerIdExample.value)) + customerId=customerIdExample.value, + cvv=Some(cvvExample.value), + brand=Some(brandExample.value))) ), adapterImplementation = Some(AdapterImplementation("- Core", 1)) ) @@ -1950,6 +2050,14 @@ object AkkaConnector_vDec2018 extends Connector with AkkaConnectorActorInit { account_id=account_idExample.value)), to_sepa=Some(TransactionRequestIban(transactionRequestIban.value)), to_counterparty=Some(TransactionRequestCounterpartyId(transactionRequestCounterpartyIdExample.value)), + to_simple=Some( TransactionRequestSimple(otherBankRoutingScheme=otherBankRoutingSchemeExample.value, + otherBankRoutingAddress=otherBankRoutingAddressExample.value, + otherBranchRoutingScheme=otherBranchRoutingSchemeExample.value, + otherBranchRoutingAddress=otherBranchRoutingAddressExample.value, + otherAccountRoutingScheme=otherAccountRoutingSchemeExample.value, + otherAccountRoutingAddress=otherAccountRoutingAddressExample.value, + otherAccountSecondaryRoutingScheme=otherAccountSecondaryRoutingSchemeExample.value, + otherAccountSecondaryRoutingAddress=otherAccountSecondaryRoutingAddressExample.value)), to_transfer_to_phone=Some( TransactionRequestTransferToPhone(value= AmountOfMoneyJsonV121(currency=currencyExample.value, amount=amountExample.value), description=descriptionExample.value, @@ -2127,6 +2235,14 @@ object AkkaConnector_vDec2018 extends Connector with AkkaConnectorActorInit { account_id=account_idExample.value)), to_sepa=Some(TransactionRequestIban(transactionRequestIban.value)), to_counterparty=Some(TransactionRequestCounterpartyId(transactionRequestCounterpartyIdExample.value)), + to_simple=Some( TransactionRequestSimple(otherBankRoutingScheme=otherBankRoutingSchemeExample.value, + otherBankRoutingAddress=otherBankRoutingAddressExample.value, + otherBranchRoutingScheme=otherBranchRoutingSchemeExample.value, + otherBranchRoutingAddress=otherBranchRoutingAddressExample.value, + otherAccountRoutingScheme=otherAccountRoutingSchemeExample.value, + otherAccountRoutingAddress=otherAccountRoutingAddressExample.value, + otherAccountSecondaryRoutingScheme=otherAccountSecondaryRoutingSchemeExample.value, + otherAccountSecondaryRoutingAddress=otherAccountSecondaryRoutingAddressExample.value)), to_transfer_to_phone=Some( TransactionRequestTransferToPhone(value= AmountOfMoneyJsonV121(currency=currencyExample.value, amount=amountExample.value), description=descriptionExample.value, @@ -2245,6 +2361,14 @@ object AkkaConnector_vDec2018 extends Connector with AkkaConnectorActorInit { account_id=account_idExample.value)), to_sepa=Some(TransactionRequestIban(transactionRequestIban.value)), to_counterparty=Some(TransactionRequestCounterpartyId(transactionRequestCounterpartyIdExample.value)), + to_simple=Some( TransactionRequestSimple(otherBankRoutingScheme=otherBankRoutingSchemeExample.value, + otherBankRoutingAddress=otherBankRoutingAddressExample.value, + otherBranchRoutingScheme=otherBranchRoutingSchemeExample.value, + otherBranchRoutingAddress=otherBranchRoutingAddressExample.value, + otherAccountRoutingScheme=otherAccountRoutingSchemeExample.value, + otherAccountRoutingAddress=otherAccountRoutingAddressExample.value, + otherAccountSecondaryRoutingScheme=otherAccountSecondaryRoutingSchemeExample.value, + otherAccountSecondaryRoutingAddress=otherAccountSecondaryRoutingAddressExample.value)), to_transfer_to_phone=Some( TransactionRequestTransferToPhone(value= AmountOfMoneyJsonV121(currency=currencyExample.value, amount=amountExample.value), description=descriptionExample.value, @@ -2336,6 +2460,14 @@ object AkkaConnector_vDec2018 extends Connector with AkkaConnectorActorInit { account_id=account_idExample.value)), to_sepa=Some(TransactionRequestIban(transactionRequestIban.value)), to_counterparty=Some(TransactionRequestCounterpartyId(transactionRequestCounterpartyIdExample.value)), + to_simple=Some( TransactionRequestSimple(otherBankRoutingScheme=otherBankRoutingSchemeExample.value, + otherBankRoutingAddress=otherBankRoutingAddressExample.value, + otherBranchRoutingScheme=otherBranchRoutingSchemeExample.value, + otherBranchRoutingAddress=otherBranchRoutingAddressExample.value, + otherAccountRoutingScheme=otherAccountRoutingSchemeExample.value, + otherAccountRoutingAddress=otherAccountRoutingAddressExample.value, + otherAccountSecondaryRoutingScheme=otherAccountSecondaryRoutingSchemeExample.value, + otherAccountSecondaryRoutingAddress=otherAccountSecondaryRoutingAddressExample.value)), to_transfer_to_phone=Some( TransactionRequestTransferToPhone(value= AmountOfMoneyJsonV121(currency=currencyExample.value, amount=amountExample.value), description=descriptionExample.value, @@ -2440,6 +2572,14 @@ object AkkaConnector_vDec2018 extends Connector with AkkaConnectorActorInit { account_id=account_idExample.value)), to_sepa=Some(TransactionRequestIban(transactionRequestIban.value)), to_counterparty=Some(TransactionRequestCounterpartyId(transactionRequestCounterpartyIdExample.value)), + to_simple=Some( TransactionRequestSimple(otherBankRoutingScheme=otherBankRoutingSchemeExample.value, + otherBankRoutingAddress=otherBankRoutingAddressExample.value, + otherBranchRoutingScheme=otherBranchRoutingSchemeExample.value, + otherBranchRoutingAddress=otherBranchRoutingAddressExample.value, + otherAccountRoutingScheme=otherAccountRoutingSchemeExample.value, + otherAccountRoutingAddress=otherAccountRoutingAddressExample.value, + otherAccountSecondaryRoutingScheme=otherAccountSecondaryRoutingSchemeExample.value, + otherAccountSecondaryRoutingAddress=otherAccountSecondaryRoutingAddressExample.value)), to_transfer_to_phone=Some( TransactionRequestTransferToPhone(value= AmountOfMoneyJsonV121(currency=currencyExample.value, amount=amountExample.value), description=descriptionExample.value, @@ -2510,6 +2650,14 @@ object AkkaConnector_vDec2018 extends Connector with AkkaConnectorActorInit { account_id=account_idExample.value)), to_sepa=Some(TransactionRequestIban(transactionRequestIban.value)), to_counterparty=Some(TransactionRequestCounterpartyId(transactionRequestCounterpartyIdExample.value)), + to_simple=Some( TransactionRequestSimple(otherBankRoutingScheme=otherBankRoutingSchemeExample.value, + otherBankRoutingAddress=otherBankRoutingAddressExample.value, + otherBranchRoutingScheme=otherBranchRoutingSchemeExample.value, + otherBranchRoutingAddress=otherBranchRoutingAddressExample.value, + otherAccountRoutingScheme=otherAccountRoutingSchemeExample.value, + otherAccountRoutingAddress=otherAccountRoutingAddressExample.value, + otherAccountSecondaryRoutingScheme=otherAccountSecondaryRoutingSchemeExample.value, + otherAccountSecondaryRoutingAddress=otherAccountSecondaryRoutingAddressExample.value)), to_transfer_to_phone=Some( TransactionRequestTransferToPhone(value= AmountOfMoneyJsonV121(currency=currencyExample.value, amount=amountExample.value), description=descriptionExample.value, @@ -3016,7 +3164,9 @@ object AkkaConnector_vDec2018 extends Connector with AkkaConnectorActorInit { siteName=Some("string"), cashWithdrawalNationalFee=Some(cashWithdrawalNationalFeeExample.value), cashWithdrawalInternationalFee=Some(cashWithdrawalInternationalFeeExample.value), - balanceInquiryFee=Some(balanceInquiryFeeExample.value))) + balanceInquiryFee=Some(balanceInquiryFeeExample.value), + atmType=Some(atmTypeExample.value), + phone=Some(phoneExample.value))) ), adapterImplementation = Some(AdapterImplementation("- Core", 1)) ) @@ -3095,7 +3245,9 @@ object AkkaConnector_vDec2018 extends Connector with AkkaConnectorActorInit { siteName=Some("string"), cashWithdrawalNationalFee=Some(cashWithdrawalNationalFeeExample.value), cashWithdrawalInternationalFee=Some(cashWithdrawalInternationalFeeExample.value), - balanceInquiryFee=Some(balanceInquiryFeeExample.value)))) + balanceInquiryFee=Some(balanceInquiryFeeExample.value), + atmType=Some(atmTypeExample.value), + phone=Some(phoneExample.value)))) ), adapterImplementation = Some(AdapterImplementation("- Core", 1)) ) @@ -3190,6 +3342,14 @@ object AkkaConnector_vDec2018 extends Connector with AkkaConnectorActorInit { account_id=account_idExample.value)), to_sepa=Some(TransactionRequestIban(transactionRequestIban.value)), to_counterparty=Some(TransactionRequestCounterpartyId(transactionRequestCounterpartyIdExample.value)), + to_simple=Some( TransactionRequestSimple(otherBankRoutingScheme=otherBankRoutingSchemeExample.value, + otherBankRoutingAddress=otherBankRoutingAddressExample.value, + otherBranchRoutingScheme=otherBranchRoutingSchemeExample.value, + otherBranchRoutingAddress=otherBranchRoutingAddressExample.value, + otherAccountRoutingScheme=otherAccountRoutingSchemeExample.value, + otherAccountRoutingAddress=otherAccountRoutingAddressExample.value, + otherAccountSecondaryRoutingScheme=otherAccountSecondaryRoutingSchemeExample.value, + otherAccountSecondaryRoutingAddress=otherAccountSecondaryRoutingAddressExample.value)), to_transfer_to_phone=Some( TransactionRequestTransferToPhone(value= AmountOfMoneyJsonV121(currency=currencyExample.value, amount=amountExample.value), description=descriptionExample.value, @@ -3447,6 +3607,14 @@ object AkkaConnector_vDec2018 extends Connector with AkkaConnectorActorInit { account_id=account_idExample.value)), to_sepa=Some(TransactionRequestIban(transactionRequestIban.value)), to_counterparty=Some(TransactionRequestCounterpartyId(transactionRequestCounterpartyIdExample.value)), + to_simple=Some( TransactionRequestSimple(otherBankRoutingScheme=otherBankRoutingSchemeExample.value, + otherBankRoutingAddress=otherBankRoutingAddressExample.value, + otherBranchRoutingScheme=otherBranchRoutingSchemeExample.value, + otherBranchRoutingAddress=otherBranchRoutingAddressExample.value, + otherAccountRoutingScheme=otherAccountRoutingSchemeExample.value, + otherAccountRoutingAddress=otherAccountRoutingAddressExample.value, + otherAccountSecondaryRoutingScheme=otherAccountSecondaryRoutingSchemeExample.value, + otherAccountSecondaryRoutingAddress=otherAccountSecondaryRoutingAddressExample.value)), to_transfer_to_phone=Some( TransactionRequestTransferToPhone(value= AmountOfMoneyJsonV121(currency=currencyExample.value, amount=amountExample.value), description=descriptionExample.value, @@ -3533,6 +3701,14 @@ object AkkaConnector_vDec2018 extends Connector with AkkaConnectorActorInit { account_id=account_idExample.value)), to_sepa=Some(TransactionRequestIban(transactionRequestIban.value)), to_counterparty=Some(TransactionRequestCounterpartyId(transactionRequestCounterpartyIdExample.value)), + to_simple=Some( TransactionRequestSimple(otherBankRoutingScheme=otherBankRoutingSchemeExample.value, + otherBankRoutingAddress=otherBankRoutingAddressExample.value, + otherBranchRoutingScheme=otherBranchRoutingSchemeExample.value, + otherBranchRoutingAddress=otherBranchRoutingAddressExample.value, + otherAccountRoutingScheme=otherAccountRoutingSchemeExample.value, + otherAccountRoutingAddress=otherAccountRoutingAddressExample.value, + otherAccountSecondaryRoutingScheme=otherAccountSecondaryRoutingSchemeExample.value, + otherAccountSecondaryRoutingAddress=otherAccountSecondaryRoutingAddressExample.value)), to_transfer_to_phone=Some( TransactionRequestTransferToPhone(value= AmountOfMoneyJsonV121(currency=currencyExample.value, amount=amountExample.value), description=descriptionExample.value, @@ -4491,8 +4667,7 @@ object AkkaConnector_vDec2018 extends Connector with AkkaConnectorActorInit { key=keyExample.value, value=valueExample.value, timeStamp=toDate(timeStampExample), - consumerId=consumerIdExample.value - )) + consumerId=consumerIdExample.value)) ), adapterImplementation = Some(AdapterImplementation("- Core", 1)) ) @@ -4526,8 +4701,7 @@ object AkkaConnector_vDec2018 extends Connector with AkkaConnectorActorInit { value=valueExample.value, challenge=challengeExample.value, status=statusExample.value, - consumerId=consumerIdExample.value - )) + consumerId=consumerIdExample.value)) ), adapterImplementation = Some(AdapterImplementation("- Core", 1)) ) @@ -4609,7 +4783,7 @@ object AkkaConnector_vDec2018 extends Connector with AkkaConnectorActorInit { userId=userIdExample.value, key=keyExample.value, value=valueExample.value, - timeStamp=toDate(timeStampExample), + timeStamp=toDate(timeStampExample), consumerId=consumerIdExample.value))) ), adapterImplementation = Some(AdapterImplementation("- Core", 1)) @@ -4771,7 +4945,8 @@ object AkkaConnector_vDec2018 extends Connector with AkkaConnectorActorInit { accountAttributeId=accountAttributeIdExample.value, name=nameExample.value, attributeType=com.openbankproject.commons.model.enums.AccountAttributeType.example, - value=valueExample.value)) + value=valueExample.value, + productInstanceCode=Some("string"))) ), adapterImplementation = Some(AdapterImplementation("- Core", 1)) ) @@ -4829,7 +5004,8 @@ object AkkaConnector_vDec2018 extends Connector with AkkaConnectorActorInit { productAttributeId=Some(productAttributeIdExample.value), name=nameExample.value, accountAttributeType=com.openbankproject.commons.model.enums.AccountAttributeType.example, - value=valueExample.value) + value=valueExample.value, + productInstanceCode=Some("string")) ), exampleInboundMessage = ( InBoundCreateOrUpdateAccountAttribute(inboundAdapterCallContext=MessageDocsSwaggerDefinitions.inboundAdapterCallContext, @@ -4840,15 +5016,15 @@ object AkkaConnector_vDec2018 extends Connector with AkkaConnectorActorInit { accountAttributeId=accountAttributeIdExample.value, name=nameExample.value, attributeType=com.openbankproject.commons.model.enums.AccountAttributeType.example, - value=valueExample.value)) + value=valueExample.value, + productInstanceCode=Some("string"))) ), adapterImplementation = Some(AdapterImplementation("- Core", 1)) ) - override def createOrUpdateAccountAttribute(bankId: BankId, accountId: AccountId, productCode: ProductCode, productAttributeId: Option[String], name: String, accountAttributeType: AccountAttributeType.Value, value: String, - productInstanceCode: Option[String],callContext: Option[CallContext]): OBPReturnType[Box[AccountAttribute]] = { + override def createOrUpdateAccountAttribute(bankId: BankId, accountId: AccountId, productCode: ProductCode, productAttributeId: Option[String], name: String, accountAttributeType: AccountAttributeType.Value, value: String, productInstanceCode: Option[String], callContext: Option[CallContext]): OBPReturnType[Box[AccountAttribute]] = { import com.openbankproject.commons.dto.{InBoundCreateOrUpdateAccountAttribute => InBound, OutBoundCreateOrUpdateAccountAttribute => OutBound} - val req = OutBound(callContext.map(_.toOutboundAdapterCallContext).orNull, bankId, accountId, productCode, productAttributeId, name, accountAttributeType, value) + val req = OutBound(callContext.map(_.toOutboundAdapterCallContext).orNull, bankId, accountId, productCode, productAttributeId, name, accountAttributeType, value, productInstanceCode) val response: Future[Box[InBound]] = (southSideActor ? req).mapTo[InBound].recoverWith(recoverFunction).map(Box !! _) response.map(convertToTuple[AccountAttributeCommons](callContext)) } @@ -4943,7 +5119,8 @@ object AkkaConnector_vDec2018 extends Connector with AkkaConnectorActorInit { name=nameExample.value, attributeType=com.openbankproject.commons.model.enums.ProductAttributeType.example, value=valueExample.value, - isActive=Some(isActiveExample.value.toBoolean)))) + isActive=Some(isActiveExample.value.toBoolean))), + productInstanceCode=Some("string")) ), exampleInboundMessage = ( InBoundCreateAccountAttributes(inboundAdapterCallContext=MessageDocsSwaggerDefinitions.inboundAdapterCallContext, @@ -4954,15 +5131,15 @@ object AkkaConnector_vDec2018 extends Connector with AkkaConnectorActorInit { accountAttributeId=accountAttributeIdExample.value, name=nameExample.value, attributeType=com.openbankproject.commons.model.enums.AccountAttributeType.example, - value=valueExample.value))) + value=valueExample.value, + productInstanceCode=Some("string")))) ), adapterImplementation = Some(AdapterImplementation("- Core", 1)) ) - override def createAccountAttributes(bankId: BankId, accountId: AccountId, productCode: ProductCode, accountAttributes: List[ProductAttribute], - productInstanceCode: Option[String], callContext: Option[CallContext]): OBPReturnType[Box[List[AccountAttribute]]] = { + override def createAccountAttributes(bankId: BankId, accountId: AccountId, productCode: ProductCode, accountAttributes: List[ProductAttribute], productInstanceCode: Option[String], callContext: Option[CallContext]): OBPReturnType[Box[List[AccountAttribute]]] = { import com.openbankproject.commons.dto.{InBoundCreateAccountAttributes => InBound, OutBoundCreateAccountAttributes => OutBound} - val req = OutBound(callContext.map(_.toOutboundAdapterCallContext).orNull, bankId, accountId, productCode, accountAttributes) + val req = OutBound(callContext.map(_.toOutboundAdapterCallContext).orNull, bankId, accountId, productCode, accountAttributes, productInstanceCode) val response: Future[Box[InBound]] = (southSideActor ? req).mapTo[InBound].recoverWith(recoverFunction).map(Box !! _) response.map(convertToTuple[List[AccountAttributeCommons]](callContext)) } @@ -4988,7 +5165,8 @@ object AkkaConnector_vDec2018 extends Connector with AkkaConnectorActorInit { accountAttributeId=accountAttributeIdExample.value, name=nameExample.value, attributeType=com.openbankproject.commons.model.enums.AccountAttributeType.example, - value=valueExample.value))) + value=valueExample.value, + productInstanceCode=Some("string")))) ), adapterImplementation = Some(AdapterImplementation("- Core", 1)) ) @@ -6091,7 +6269,8 @@ object AkkaConnector_vDec2018 extends Connector with AkkaConnectorActorInit { date=toDate(dateExample), message=messageExample.value, fromDepartment=fromDepartmentExample.value, - fromPerson=fromPersonExample.value)) + fromPerson=fromPersonExample.value, + transport=Some(transportExample.value))) ), adapterImplementation = Some(AdapterImplementation("- Core", 1)) ) @@ -6240,6 +6419,6 @@ object AkkaConnector_vDec2018 extends Connector with AkkaConnectorActorInit { response.map(convertToTuple[Boolean](callContext)) } -// ---------- created on 2022-03-11T18:42:02Z -//---------------- dynamic end ---------------------please don't modify this line +// ---------- created on 2024-01-29T13:57:05Z +//---------------- dynamic end ---------------------please don't modify this line } diff --git a/obp-api/src/main/scala/code/bankconnectors/rest/RestConnector_vMar2019.scala b/obp-api/src/main/scala/code/bankconnectors/rest/RestConnector_vMar2019.scala index cc8f4c8f98..137023df80 100644 --- a/obp-api/src/main/scala/code/bankconnectors/rest/RestConnector_vMar2019.scala +++ b/obp-api/src/main/scala/code/bankconnectors/rest/RestConnector_vMar2019.scala @@ -96,7 +96,7 @@ trait RestConnector_vMar2019 extends Connector with KafkaHelper with MdcLoggable //---------------- dynamic start -------------------please don't modify this line -// ---------- created on 2023-06-01T16:45:32Z +// ---------- created on 2024-01-29T13:59:34Z messageDocs += getAdapterInfoDoc def getAdapterInfoDoc = MessageDoc( @@ -367,6 +367,7 @@ trait RestConnector_vMar2019 extends Connector with KafkaHelper with MdcLoggable successful=true, challengeType=challengeTypeExample.value, consentId=Some(consentIdExample.value), + basketId=Some(basketIdExample.value), scaMethod=Some(com.openbankproject.commons.model.enums.StrongCustomerAuthentication.SMS), scaStatus=Some(com.openbankproject.commons.model.enums.StrongCustomerAuthenticationStatus.example), authenticationMethodId=Some("string"), @@ -382,6 +383,51 @@ trait RestConnector_vMar2019 extends Connector with KafkaHelper with MdcLoggable response.map(convertToTuple[List[ChallengeCommons]](callContext)) } + messageDocs += createChallengesC3Doc + def createChallengesC3Doc = MessageDoc( + process = "obp.createChallengesC3", + messageFormat = messageFormat, + description = "Create Challenges C3", + outboundTopic = None, + inboundTopic = None, + exampleOutboundMessage = ( + OutBoundCreateChallengesC3(outboundAdapterCallContext=MessageDocsSwaggerDefinitions.outboundAdapterCallContext, + userIds=listExample.value.split("[,;]").toList, + challengeType=com.openbankproject.commons.model.enums.ChallengeType.example, + transactionRequestId=Some(transactionRequestIdExample.value), + scaMethod=Some(com.openbankproject.commons.model.enums.StrongCustomerAuthentication.SMS), + scaStatus=Some(com.openbankproject.commons.model.enums.StrongCustomerAuthenticationStatus.example), + consentId=Some(consentIdExample.value), + basketId=Some(basketIdExample.value), + authenticationMethodId=Some("string")) + ), + exampleInboundMessage = ( + InBoundCreateChallengesC3(inboundAdapterCallContext=MessageDocsSwaggerDefinitions.inboundAdapterCallContext, + status=MessageDocsSwaggerDefinitions.inboundStatus, + data=List( ChallengeCommons(challengeId=challengeIdExample.value, + transactionRequestId=transactionRequestIdExample.value, + expectedAnswer="string", + expectedUserId="string", + salt="string", + successful=true, + challengeType=challengeTypeExample.value, + consentId=Some(consentIdExample.value), + basketId=Some(basketIdExample.value), + scaMethod=Some(com.openbankproject.commons.model.enums.StrongCustomerAuthentication.SMS), + scaStatus=Some(com.openbankproject.commons.model.enums.StrongCustomerAuthenticationStatus.example), + authenticationMethodId=Some("string"), + attemptCounter=123))) + ), + adapterImplementation = Some(AdapterImplementation("- Core", 1)) + ) + + override def createChallengesC3(userIds: List[String], challengeType: ChallengeType.Value, transactionRequestId: Option[String], scaMethod: Option[StrongCustomerAuthentication.SCA], scaStatus: Option[SCAStatus], consentId: Option[String], basketId: Option[String], authenticationMethodId: Option[String], callContext: Option[CallContext]): OBPReturnType[Box[List[ChallengeTrait]]] = { + import com.openbankproject.commons.dto.{InBoundCreateChallengesC3 => InBound, OutBoundCreateChallengesC3 => OutBound} + val req = OutBound(callContext.map(_.toOutboundAdapterCallContext).orNull, userIds, challengeType, transactionRequestId, scaMethod, scaStatus, consentId, basketId, authenticationMethodId) + val response: Future[Box[InBound]] = sendRequest[InBound](getUrl(callContext, "createChallengesC3"), HttpMethods.POST, req, callContext) + response.map(convertToTuple[List[ChallengeCommons]](callContext)) + } + messageDocs += validateChallengeAnswerDoc def validateChallengeAnswerDoc = MessageDoc( process = "obp.validateChallengeAnswer", @@ -434,6 +480,7 @@ trait RestConnector_vMar2019 extends Connector with KafkaHelper with MdcLoggable successful=true, challengeType=challengeTypeExample.value, consentId=Some(consentIdExample.value), + basketId=Some(basketIdExample.value), scaMethod=Some(com.openbankproject.commons.model.enums.StrongCustomerAuthentication.SMS), scaStatus=Some(com.openbankproject.commons.model.enums.StrongCustomerAuthenticationStatus.example), authenticationMethodId=Some("string"), @@ -449,6 +496,48 @@ trait RestConnector_vMar2019 extends Connector with KafkaHelper with MdcLoggable response.map(convertToTuple[ChallengeCommons](callContext)) } + messageDocs += validateChallengeAnswerC3Doc + def validateChallengeAnswerC3Doc = MessageDoc( + process = "obp.validateChallengeAnswerC3", + messageFormat = messageFormat, + description = "Validate Challenge Answer C3", + outboundTopic = None, + inboundTopic = None, + exampleOutboundMessage = ( + OutBoundValidateChallengeAnswerC3(outboundAdapterCallContext=MessageDocsSwaggerDefinitions.outboundAdapterCallContext, + transactionRequestId=Some(transactionRequestIdExample.value), + consentId=Some(consentIdExample.value), + basketId=Some(basketIdExample.value), + challengeId=challengeIdExample.value, + hashOfSuppliedAnswer=hashOfSuppliedAnswerExample.value) + ), + exampleInboundMessage = ( + InBoundValidateChallengeAnswerC3(inboundAdapterCallContext=MessageDocsSwaggerDefinitions.inboundAdapterCallContext, + status=MessageDocsSwaggerDefinitions.inboundStatus, + data= ChallengeCommons(challengeId=challengeIdExample.value, + transactionRequestId=transactionRequestIdExample.value, + expectedAnswer="string", + expectedUserId="string", + salt="string", + successful=true, + challengeType=challengeTypeExample.value, + consentId=Some(consentIdExample.value), + basketId=Some(basketIdExample.value), + scaMethod=Some(com.openbankproject.commons.model.enums.StrongCustomerAuthentication.SMS), + scaStatus=Some(com.openbankproject.commons.model.enums.StrongCustomerAuthenticationStatus.example), + authenticationMethodId=Some("string"), + attemptCounter=123)) + ), + adapterImplementation = Some(AdapterImplementation("- Core", 1)) + ) + + override def validateChallengeAnswerC3(transactionRequestId: Option[String], consentId: Option[String], basketId: Option[String], challengeId: String, hashOfSuppliedAnswer: String, callContext: Option[CallContext]): OBPReturnType[Box[ChallengeTrait]] = { + import com.openbankproject.commons.dto.{InBoundValidateChallengeAnswerC3 => InBound, OutBoundValidateChallengeAnswerC3 => OutBound} + val req = OutBound(callContext.map(_.toOutboundAdapterCallContext).orNull, transactionRequestId, consentId, basketId, challengeId, hashOfSuppliedAnswer) + val response: Future[Box[InBound]] = sendRequest[InBound](getUrl(callContext, "validateChallengeAnswerC3"), HttpMethods.POST, req, callContext) + response.map(convertToTuple[ChallengeCommons](callContext)) + } + messageDocs += getChallengesByTransactionRequestIdDoc def getChallengesByTransactionRequestIdDoc = MessageDoc( process = "obp.getChallengesByTransactionRequestId", @@ -471,6 +560,7 @@ trait RestConnector_vMar2019 extends Connector with KafkaHelper with MdcLoggable successful=true, challengeType=challengeTypeExample.value, consentId=Some(consentIdExample.value), + basketId=Some(basketIdExample.value), scaMethod=Some(com.openbankproject.commons.model.enums.StrongCustomerAuthentication.SMS), scaStatus=Some(com.openbankproject.commons.model.enums.StrongCustomerAuthenticationStatus.example), authenticationMethodId=Some("string"), @@ -508,6 +598,7 @@ trait RestConnector_vMar2019 extends Connector with KafkaHelper with MdcLoggable successful=true, challengeType=challengeTypeExample.value, consentId=Some(consentIdExample.value), + basketId=Some(basketIdExample.value), scaMethod=Some(com.openbankproject.commons.model.enums.StrongCustomerAuthentication.SMS), scaStatus=Some(com.openbankproject.commons.model.enums.StrongCustomerAuthenticationStatus.example), authenticationMethodId=Some("string"), @@ -545,6 +636,7 @@ trait RestConnector_vMar2019 extends Connector with KafkaHelper with MdcLoggable successful=true, challengeType=challengeTypeExample.value, consentId=Some(consentIdExample.value), + basketId=Some(basketIdExample.value), scaMethod=Some(com.openbankproject.commons.model.enums.StrongCustomerAuthentication.SMS), scaStatus=Some(com.openbankproject.commons.model.enums.StrongCustomerAuthenticationStatus.example), authenticationMethodId=Some("string"), @@ -6516,8 +6608,8 @@ trait RestConnector_vMar2019 extends Connector with KafkaHelper with MdcLoggable response.map(convertToTuple[Boolean](callContext)) } -// ---------- created on 2023-06-01T16:45:32Z -//---------------- dynamic end ---------------------please don't modify this line +// ---------- created on 2024-01-29T13:59:34Z +//---------------- dynamic end ---------------------please don't modify this line private val availableOperation = DynamicEntityOperation.values.map(it => s""""$it"""").mkString("[", ", ", "]") diff --git a/obp-api/src/main/scala/code/bankconnectors/storedprocedure/StoredProcedureConnector_vDec2019.scala b/obp-api/src/main/scala/code/bankconnectors/storedprocedure/StoredProcedureConnector_vDec2019.scala index 304c4d0e27..9ab651e8e6 100644 --- a/obp-api/src/main/scala/code/bankconnectors/storedprocedure/StoredProcedureConnector_vDec2019.scala +++ b/obp-api/src/main/scala/code/bankconnectors/storedprocedure/StoredProcedureConnector_vDec2019.scala @@ -75,7 +75,7 @@ trait StoredProcedureConnector_vDec2019 extends Connector with MdcLoggable { val connectorName = "stored_procedure_vDec2019" //---------------- dynamic start -------------------please don't modify this line -// ---------- created on 2023-06-01T16:47:09Z +// ---------- created on 2024-01-29T13:58:37Z messageDocs += getAdapterInfoDoc def getAdapterInfoDoc = MessageDoc( @@ -346,6 +346,7 @@ trait StoredProcedureConnector_vDec2019 extends Connector with MdcLoggable { successful=true, challengeType=challengeTypeExample.value, consentId=Some(consentIdExample.value), + basketId=Some(basketIdExample.value), scaMethod=Some(com.openbankproject.commons.model.enums.StrongCustomerAuthentication.SMS), scaStatus=Some(com.openbankproject.commons.model.enums.StrongCustomerAuthenticationStatus.example), authenticationMethodId=Some("string"), @@ -361,6 +362,51 @@ trait StoredProcedureConnector_vDec2019 extends Connector with MdcLoggable { response.map(convertToTuple[List[ChallengeCommons]](callContext)) } + messageDocs += createChallengesC3Doc + def createChallengesC3Doc = MessageDoc( + process = "obp.createChallengesC3", + messageFormat = messageFormat, + description = "Create Challenges C3", + outboundTopic = None, + inboundTopic = None, + exampleOutboundMessage = ( + OutBoundCreateChallengesC3(outboundAdapterCallContext=MessageDocsSwaggerDefinitions.outboundAdapterCallContext, + userIds=listExample.value.split("[,;]").toList, + challengeType=com.openbankproject.commons.model.enums.ChallengeType.example, + transactionRequestId=Some(transactionRequestIdExample.value), + scaMethod=Some(com.openbankproject.commons.model.enums.StrongCustomerAuthentication.SMS), + scaStatus=Some(com.openbankproject.commons.model.enums.StrongCustomerAuthenticationStatus.example), + consentId=Some(consentIdExample.value), + basketId=Some(basketIdExample.value), + authenticationMethodId=Some("string")) + ), + exampleInboundMessage = ( + InBoundCreateChallengesC3(inboundAdapterCallContext=MessageDocsSwaggerDefinitions.inboundAdapterCallContext, + status=MessageDocsSwaggerDefinitions.inboundStatus, + data=List( ChallengeCommons(challengeId=challengeIdExample.value, + transactionRequestId=transactionRequestIdExample.value, + expectedAnswer="string", + expectedUserId="string", + salt="string", + successful=true, + challengeType=challengeTypeExample.value, + consentId=Some(consentIdExample.value), + basketId=Some(basketIdExample.value), + scaMethod=Some(com.openbankproject.commons.model.enums.StrongCustomerAuthentication.SMS), + scaStatus=Some(com.openbankproject.commons.model.enums.StrongCustomerAuthenticationStatus.example), + authenticationMethodId=Some("string"), + attemptCounter=123))) + ), + adapterImplementation = Some(AdapterImplementation("- Core", 1)) + ) + + override def createChallengesC3(userIds: List[String], challengeType: ChallengeType.Value, transactionRequestId: Option[String], scaMethod: Option[StrongCustomerAuthentication.SCA], scaStatus: Option[SCAStatus], consentId: Option[String], basketId: Option[String], authenticationMethodId: Option[String], callContext: Option[CallContext]): OBPReturnType[Box[List[ChallengeTrait]]] = { + import com.openbankproject.commons.dto.{InBoundCreateChallengesC3 => InBound, OutBoundCreateChallengesC3 => OutBound} + val req = OutBound(callContext.map(_.toOutboundAdapterCallContext).orNull, userIds, challengeType, transactionRequestId, scaMethod, scaStatus, consentId, basketId, authenticationMethodId) + val response: Future[Box[InBound]] = sendRequest[InBound]("obp_create_challenges_c3", req, callContext) + response.map(convertToTuple[List[ChallengeCommons]](callContext)) + } + messageDocs += validateChallengeAnswerDoc def validateChallengeAnswerDoc = MessageDoc( process = "obp.validateChallengeAnswer", @@ -413,6 +459,7 @@ trait StoredProcedureConnector_vDec2019 extends Connector with MdcLoggable { successful=true, challengeType=challengeTypeExample.value, consentId=Some(consentIdExample.value), + basketId=Some(basketIdExample.value), scaMethod=Some(com.openbankproject.commons.model.enums.StrongCustomerAuthentication.SMS), scaStatus=Some(com.openbankproject.commons.model.enums.StrongCustomerAuthenticationStatus.example), authenticationMethodId=Some("string"), @@ -428,6 +475,48 @@ trait StoredProcedureConnector_vDec2019 extends Connector with MdcLoggable { response.map(convertToTuple[ChallengeCommons](callContext)) } + messageDocs += validateChallengeAnswerC3Doc + def validateChallengeAnswerC3Doc = MessageDoc( + process = "obp.validateChallengeAnswerC3", + messageFormat = messageFormat, + description = "Validate Challenge Answer C3", + outboundTopic = None, + inboundTopic = None, + exampleOutboundMessage = ( + OutBoundValidateChallengeAnswerC3(outboundAdapterCallContext=MessageDocsSwaggerDefinitions.outboundAdapterCallContext, + transactionRequestId=Some(transactionRequestIdExample.value), + consentId=Some(consentIdExample.value), + basketId=Some(basketIdExample.value), + challengeId=challengeIdExample.value, + hashOfSuppliedAnswer=hashOfSuppliedAnswerExample.value) + ), + exampleInboundMessage = ( + InBoundValidateChallengeAnswerC3(inboundAdapterCallContext=MessageDocsSwaggerDefinitions.inboundAdapterCallContext, + status=MessageDocsSwaggerDefinitions.inboundStatus, + data= ChallengeCommons(challengeId=challengeIdExample.value, + transactionRequestId=transactionRequestIdExample.value, + expectedAnswer="string", + expectedUserId="string", + salt="string", + successful=true, + challengeType=challengeTypeExample.value, + consentId=Some(consentIdExample.value), + basketId=Some(basketIdExample.value), + scaMethod=Some(com.openbankproject.commons.model.enums.StrongCustomerAuthentication.SMS), + scaStatus=Some(com.openbankproject.commons.model.enums.StrongCustomerAuthenticationStatus.example), + authenticationMethodId=Some("string"), + attemptCounter=123)) + ), + adapterImplementation = Some(AdapterImplementation("- Core", 1)) + ) + + override def validateChallengeAnswerC3(transactionRequestId: Option[String], consentId: Option[String], basketId: Option[String], challengeId: String, hashOfSuppliedAnswer: String, callContext: Option[CallContext]): OBPReturnType[Box[ChallengeTrait]] = { + import com.openbankproject.commons.dto.{InBoundValidateChallengeAnswerC3 => InBound, OutBoundValidateChallengeAnswerC3 => OutBound} + val req = OutBound(callContext.map(_.toOutboundAdapterCallContext).orNull, transactionRequestId, consentId, basketId, challengeId, hashOfSuppliedAnswer) + val response: Future[Box[InBound]] = sendRequest[InBound]("obp_validate_challenge_answer_c3", req, callContext) + response.map(convertToTuple[ChallengeCommons](callContext)) + } + messageDocs += getChallengesByTransactionRequestIdDoc def getChallengesByTransactionRequestIdDoc = MessageDoc( process = "obp.getChallengesByTransactionRequestId", @@ -450,6 +539,7 @@ trait StoredProcedureConnector_vDec2019 extends Connector with MdcLoggable { successful=true, challengeType=challengeTypeExample.value, consentId=Some(consentIdExample.value), + basketId=Some(basketIdExample.value), scaMethod=Some(com.openbankproject.commons.model.enums.StrongCustomerAuthentication.SMS), scaStatus=Some(com.openbankproject.commons.model.enums.StrongCustomerAuthenticationStatus.example), authenticationMethodId=Some("string"), @@ -487,6 +577,7 @@ trait StoredProcedureConnector_vDec2019 extends Connector with MdcLoggable { successful=true, challengeType=challengeTypeExample.value, consentId=Some(consentIdExample.value), + basketId=Some(basketIdExample.value), scaMethod=Some(com.openbankproject.commons.model.enums.StrongCustomerAuthentication.SMS), scaStatus=Some(com.openbankproject.commons.model.enums.StrongCustomerAuthenticationStatus.example), authenticationMethodId=Some("string"), @@ -524,6 +615,7 @@ trait StoredProcedureConnector_vDec2019 extends Connector with MdcLoggable { successful=true, challengeType=challengeTypeExample.value, consentId=Some(consentIdExample.value), + basketId=Some(basketIdExample.value), scaMethod=Some(com.openbankproject.commons.model.enums.StrongCustomerAuthentication.SMS), scaStatus=Some(com.openbankproject.commons.model.enums.StrongCustomerAuthenticationStatus.example), authenticationMethodId=Some("string"), @@ -6495,8 +6587,8 @@ trait StoredProcedureConnector_vDec2019 extends Connector with MdcLoggable { response.map(convertToTuple[Boolean](callContext)) } -// ---------- created on 2023-06-01T16:47:09Z -//---------------- dynamic end ---------------------please don't modify this line +// ---------- created on 2024-01-29T13:58:37Z +//---------------- dynamic end ---------------------please don't modify this line private val availableOperation = DynamicEntityOperation.values.map(it => s""""$it"""").mkString("[", ", ", "]") diff --git a/obp-api/src/main/scala/code/remotedata/RemotedataChallenges.scala b/obp-api/src/main/scala/code/remotedata/RemotedataChallenges.scala index 868f4f1117..cf0b86ea45 100644 --- a/obp-api/src/main/scala/code/remotedata/RemotedataChallenges.scala +++ b/obp-api/src/main/scala/code/remotedata/RemotedataChallenges.scala @@ -20,12 +20,13 @@ object RemotedataChallenges extends ObpActorInit with ChallengeProvider { expectedUserId: String, scaMethod: Option[SCA], scaStatus: Option[SCAStatus], - consentId: Option[String], // Note: consentId and transactionRequestId are exclusive here. + consentId: Option[String], // Note: consentId and transactionRequestId and basketId are exclusive here. + basketId: Option[String], // Note: consentId and transactionRequestId and basketId are exclusive here. authenticationMethodId: Option[String], challengeType: String, ): Box[ChallengeTrait] = getValueFromFuture( - (actor ? cc.saveChallenge(challengeId, transactionRequestId, salt, expectedAnswer, expectedUserId, scaMethod, scaStatus, consentId, authenticationMethodId, challengeType)) + (actor ? cc.saveChallenge(challengeId, transactionRequestId, salt, expectedAnswer, expectedUserId, scaMethod, scaStatus, consentId, basketId, authenticationMethodId, challengeType)) .mapTo[Box[ChallengeTrait]] ) @@ -44,5 +45,8 @@ object RemotedataChallenges extends ObpActorInit with ChallengeProvider { override def getChallengesByConsentId(consentId: String): Box[List[ChallengeTrait]] = getValueFromFuture( (actor ? cc.getChallengesByConsentId(consentId)).mapTo[Box[List[ChallengeTrait]]] ) + override def getChallengesByBasketId(basketId: String): Box[List[ChallengeTrait]] = getValueFromFuture( + (actor ? cc.getChallengesByConsentId(basketId )).mapTo[Box[List[ChallengeTrait]]] + ) } diff --git a/obp-api/src/main/scala/code/remotedata/RemotedataChallengesActor.scala b/obp-api/src/main/scala/code/remotedata/RemotedataChallengesActor.scala index f26c530c74..1d5f141da2 100644 --- a/obp-api/src/main/scala/code/remotedata/RemotedataChallengesActor.scala +++ b/obp-api/src/main/scala/code/remotedata/RemotedataChallengesActor.scala @@ -17,12 +17,13 @@ class RemotedataChallengesActor extends Actor with ObpActorHelper with MdcLoggab case cc.saveChallenge(challengeId: String, transactionRequestId: String, salt: String, expectedAnswer: String, expectedUserId: String, scaMethod: Option[SCA], scaStatus: Option[SCAStatus], - consentId: Option[String], // Note: consentId and transactionRequestId are exclusive here. + consentId: Option[String], // Note: consentId and transactionRequestId and basketId are exclusive here. + basketId: Option[String], // Note: consentId and transactionRequestId and basketId are exclusive here. authenticationMethodId: Option[String], challengeType: String, ) => logger.debug(s"saveChallenge($challengeId, $transactionRequestId, $salt, $expectedAnswer, $expectedUserId)") - sender ! (mapper.saveChallenge(challengeId, transactionRequestId, salt, expectedAnswer, expectedUserId, scaMethod, scaStatus, consentId, authenticationMethodId, challengeType: String)) + sender ! (mapper.saveChallenge(challengeId, transactionRequestId, salt, expectedAnswer, expectedUserId, scaMethod, scaStatus, consentId, basketId, authenticationMethodId, challengeType: String)) case cc.getChallenge(challengeId: String) => logger.debug(s"getChallenge($challengeId)") diff --git a/obp-api/src/main/scala/code/signingbaskets/MappedSigningBasketProvider.scala b/obp-api/src/main/scala/code/signingbaskets/MappedSigningBasketProvider.scala new file mode 100644 index 0000000000..eb2ab958c5 --- /dev/null +++ b/obp-api/src/main/scala/code/signingbaskets/MappedSigningBasketProvider.scala @@ -0,0 +1,117 @@ +package code.signingbaskets + +import code.api.berlin.group.ConstantsBG +import code.util.MappedUUID +import com.openbankproject.commons.model.{SigningBasketConsentTrait, SigningBasketContent, SigningBasketPaymentTrait, SigningBasketTrait} +import net.liftweb.common.Box +import net.liftweb.common.Box.tryo +import net.liftweb.mapper._ + +object MappedSigningBasketProvider extends SigningBasketProvider { + def getSigningBaskets(): List[SigningBasketTrait] = { + MappedSigningBasket.findAll() + } + + override def getSigningBasketByBasketId(entityId: String): Box[SigningBasketContent] = { + val basket: Box[MappedSigningBasket] = MappedSigningBasket.find(By(MappedSigningBasket.BasketId, entityId)) + val payments = MappedSigningBasketPayment.findAll(By(MappedSigningBasketPayment.BasketId, entityId)).map(_.basketId) match { + case Nil => None + case head :: tail => Some(head :: tail) + } + val consents = MappedSigningBasketConsent.findAll(By(MappedSigningBasketConsent.BasketId, entityId)).map(_.basketId) match { + case Nil => None + case head :: tail => Some(head :: tail) + } + basket.map( i => SigningBasketContent(basket = i, payments = payments, consents = consents)) + } + override def saveSigningBasketStatus(entityId: String, status: String): Box[SigningBasketContent] = { + val basket: Box[MappedSigningBasket] = MappedSigningBasket.find(By(MappedSigningBasket.BasketId, entityId)).map(_.Status(status).saveMe) + val payments = MappedSigningBasketPayment.findAll(By(MappedSigningBasketPayment.BasketId, entityId)).map(_.basketId) match { + case Nil => None + case head :: tail => Some(head :: tail) + } + val consents = MappedSigningBasketConsent.findAll(By(MappedSigningBasketConsent.BasketId, entityId)).map(_.basketId) match { + case Nil => None + case head :: tail => Some(head :: tail) + } + basket.map( i => SigningBasketContent(basket = i, payments = payments, consents = consents)) + } + + override def createSigningBasket(paymentIds: Option[List[String]], + consentIds: Option[List[String]] + ): Box[SigningBasketTrait] = { + tryo { + val entity = MappedSigningBasket.create + entity.Status(ConstantsBG.SigningBasketsStatus.RCVD.toString) + + if (entity.validate.isEmpty) { + entity.saveMe() + } else { + throw new Error(entity.validate.map(_.msg.toString()).mkString(";")) + } + paymentIds.getOrElse(Nil).map { paymentId => + MappedSigningBasketPayment.create.BasketId(entity.basketId).PaymentId(paymentId)saveMe() + } + consentIds.getOrElse(Nil).map { consentId => + MappedSigningBasketConsent.create.BasketId(entity.basketId).ConsentId(consentId).saveMe() + } + entity + } + } + + override def deleteSigningBasket(id: String): Box[Boolean] = { + MappedSigningBasket.find(By(MappedSigningBasket.BasketId, id)) map { + _.Status(ConstantsBG.SigningBasketsStatus.CANC.toString).save + } + } + +} + +class MappedSigningBasket extends SigningBasketTrait with LongKeyedMapper[MappedSigningBasket] with IdPK { + override def getSingleton = MappedSigningBasket + object BasketId extends MappedUUID(this) + object Status extends MappedString(this, 50) + + + + override def basketId: String = BasketId.get + override def status: String = Status.get + +} + +object MappedSigningBasket extends MappedSigningBasket with LongKeyedMetaMapper[MappedSigningBasket] { + override def dbTableName = "signingbasket" // define the DB table name + override def dbIndexes = Index(BasketId) :: super.dbIndexes +} + + +class MappedSigningBasketPayment extends SigningBasketPaymentTrait with LongKeyedMapper[MappedSigningBasketPayment] with IdPK { + override def getSingleton = MappedSigningBasketPayment + object BasketId extends MappedUUID(this) + object PaymentId extends MappedUUID(this) + + + override def basketId: String = BasketId.get + override def paymentId: String = PaymentId.get + +} +object MappedSigningBasketPayment extends MappedSigningBasketPayment with LongKeyedMetaMapper[MappedSigningBasketPayment] { + override def dbTableName = "SigningBasketPayment" // define the DB table name + override def dbIndexes = Index(BasketId, PaymentId) :: super.dbIndexes +} + +class MappedSigningBasketConsent extends SigningBasketConsentTrait with LongKeyedMapper[MappedSigningBasketConsent] with IdPK { + override def getSingleton = MappedSigningBasketConsent + object BasketId extends MappedUUID(this) + object ConsentId extends MappedUUID(this) + + + override def basketId: String = BasketId.get + override def consentId: String = ConsentId.get + +} +object MappedSigningBasketConsent extends MappedSigningBasketConsent with LongKeyedMetaMapper[MappedSigningBasketConsent] { + override def dbTableName = "SigningBasketConsent" // define the DB table name + override def dbIndexes = Index(BasketId, ConsentId) :: super.dbIndexes +} + diff --git a/obp-api/src/main/scala/code/signingbaskets/SigningBasket.scala b/obp-api/src/main/scala/code/signingbaskets/SigningBasket.scala new file mode 100644 index 0000000000..b5bb12b208 --- /dev/null +++ b/obp-api/src/main/scala/code/signingbaskets/SigningBasket.scala @@ -0,0 +1,27 @@ +package code.signingbaskets + +import com.openbankproject.commons.model.{SigningBasketContent, SigningBasketTrait} +import net.liftweb.common.{Box, Logger} +import net.liftweb.util.SimpleInjector + +object SigningBasketX extends SimpleInjector { + val signingBasketProvider: SigningBasketX.Inject[SigningBasketProvider] = new Inject(buildOne _) {} + private def buildOne: SigningBasketProvider = MappedSigningBasketProvider +} + +trait SigningBasketProvider { + + private val logger = Logger(classOf[SigningBasketProvider]) + + def getSigningBaskets(): List[SigningBasketTrait] + + def getSigningBasketByBasketId(entityId: String): Box[SigningBasketContent] + def saveSigningBasketStatus(entityId: String, status: String): Box[SigningBasketContent] + + def createSigningBasket(paymentIds: Option[List[String]], + consentIds: Option[List[String]], + ): Box[SigningBasketTrait] + + def deleteSigningBasket(id: String): Box[Boolean] + +} \ No newline at end of file diff --git a/obp-api/src/main/scala/code/transactionChallenge/ChallengeProvider.scala b/obp-api/src/main/scala/code/transactionChallenge/ChallengeProvider.scala index 84437e5e5d..5c8ea19d8b 100644 --- a/obp-api/src/main/scala/code/transactionChallenge/ChallengeProvider.scala +++ b/obp-api/src/main/scala/code/transactionChallenge/ChallengeProvider.scala @@ -10,13 +10,14 @@ import net.liftweb.common.Box trait ChallengeProvider { def saveChallenge( challengeId: String, - transactionRequestId: String, + transactionRequestId: String, // Note: basketId, consentId and transactionRequestId are exclusive here. salt: String, expectedAnswer: String, expectedUserId: String, scaMethod: Option[SCA], scaStatus: Option[SCAStatus], - consentId: Option[String], // Note: consentId and transactionRequestId are exclusive here. + consentId: Option[String], // Note: basketId, consentId and transactionRequestId are exclusive here. + basketId: Option[String], // Note: basketId, consentId and transactionRequestId are exclusive here. authenticationMethodId: Option[String], challengeType: String, ): Box[ChallengeTrait] @@ -26,7 +27,8 @@ trait ChallengeProvider { def getChallengesByTransactionRequestId(transactionRequestId: String): Box[List[ChallengeTrait]] def getChallengesByConsentId(consentId: String): Box[List[ChallengeTrait]] - + def getChallengesByBasketId(basketId: String): Box[List[ChallengeTrait]] + /** * There is another method: Connector.validateChallengeAnswer, it validate the challenge over Kafka. * This method, will validate the answer in OBP side. @@ -45,7 +47,8 @@ class RemotedataChallengeProviderCaseClasses { expectedUserId: String, scaMethod: Option[SCA], scaStatus: Option[SCAStatus], - consentId: Option[String], // Note: consentId and transactionRequestId are exclusive here. + consentId: Option[String], // Note: consentId and transactionRequestId and basketId are exclusive here. + basketId: Option[String], // Note: consentId and transactionRequestId and basketId are exclusive here. authenticationMethodId: Option[String], challengeType: String, ) diff --git a/obp-api/src/main/scala/code/transactionChallenge/MappedChallengeProvider.scala b/obp-api/src/main/scala/code/transactionChallenge/MappedChallengeProvider.scala index 0006d6f5e7..654af20e9f 100644 --- a/obp-api/src/main/scala/code/transactionChallenge/MappedChallengeProvider.scala +++ b/obp-api/src/main/scala/code/transactionChallenge/MappedChallengeProvider.scala @@ -19,40 +19,44 @@ object MappedChallengeProvider extends ChallengeProvider { override def saveChallenge( challengeId: String, - transactionRequestId: String, + transactionRequestId: String, // Note: consentId and transactionRequestId and basketId are exclusive here. salt: String, expectedAnswer: String, expectedUserId: String, scaMethod: Option[SCA], scaStatus: Option[SCAStatus], - consentId: Option[String], // Note: consentId and transactionRequestId are exclusive here. + consentId: Option[String], // Note: consentId and transactionRequestId and basketId are exclusive here. + basketId: Option[String], // Note: consentId and transactionRequestId and basketId are exclusive here. authenticationMethodId: Option[String], challengeType: String, ): Box[ChallengeTrait] = tryo ( MappedExpectedChallengeAnswer .create - .mChallengeId(challengeId) - .mChallengeType(challengeType) - .mTransactionRequestId(transactionRequestId) - .mSalt(salt) - .mExpectedAnswer(expectedAnswer) - .mExpectedUserId(expectedUserId) - .mScaMethod(scaMethod.map(_.toString).getOrElse("")) - .mScaStatus(scaStatus.map(_.toString).getOrElse("")) - .mConsentId(consentId.getOrElse("")) - .mAuthenticationMethodId(expectedUserId) + .ChallengeId(challengeId) + .ChallengeType(challengeType) + .TransactionRequestId(transactionRequestId) + .Salt(salt) + .ExpectedAnswer(expectedAnswer) + .ExpectedUserId(expectedUserId) + .ScaMethod(scaMethod.map(_.toString).getOrElse("")) + .ScaStatus(scaStatus.map(_.toString).getOrElse("")) + .ConsentId(consentId.getOrElse("")) + .BasketId(basketId.getOrElse("")) + .AuthenticationMethodId(expectedUserId) .saveMe() ) override def getChallenge(challengeId: String): Box[MappedExpectedChallengeAnswer] = - MappedExpectedChallengeAnswer.find(By(MappedExpectedChallengeAnswer.mChallengeId,challengeId)) + MappedExpectedChallengeAnswer.find(By(MappedExpectedChallengeAnswer.ChallengeId,challengeId)) override def getChallengesByTransactionRequestId(transactionRequestId: String): Box[List[ChallengeTrait]] = - Full(MappedExpectedChallengeAnswer.findAll(By(MappedExpectedChallengeAnswer.mTransactionRequestId,transactionRequestId))) + Full(MappedExpectedChallengeAnswer.findAll(By(MappedExpectedChallengeAnswer.TransactionRequestId,transactionRequestId))) override def getChallengesByConsentId(consentId: String): Box[List[ChallengeTrait]] = - Full(MappedExpectedChallengeAnswer.findAll(By(MappedExpectedChallengeAnswer.mConsentId,consentId))) + Full(MappedExpectedChallengeAnswer.findAll(By(MappedExpectedChallengeAnswer.ConsentId,consentId))) + override def getChallengesByBasketId(basketId: String): Box[List[ChallengeTrait]] = + Full(MappedExpectedChallengeAnswer.findAll(By(MappedExpectedChallengeAnswer.BasketId,basketId))) override def validateChallenge( challengeId: String, @@ -63,7 +67,7 @@ object MappedChallengeProvider extends ChallengeProvider { challenge <- getChallenge(challengeId) ?~! s"${ErrorMessages.InvalidTransactionRequestChallengeId}" currentAttemptCounterValue = challenge.attemptCounter //We update the counter anyway. - _ = challenge.mAttemptCounter(currentAttemptCounterValue+1).saveMe() + _ = challenge.AttemptCounter(currentAttemptCounterValue+1).saveMe() createDateTime = challenge.createdAt.get challengeTTL : Long = Helpers.seconds(APIUtil.transactionRequestChallengeTtl) @@ -76,7 +80,7 @@ object MappedChallengeProvider extends ChallengeProvider { userId match { case None => if(currentHashedAnswer==expectedHashedAnswer) { - tryo{challenge.mSuccessful(true).mScaStatus(StrongCustomerAuthenticationStatus.finalised.toString).saveMe()} + tryo{challenge.Successful(true).ScaStatus(StrongCustomerAuthenticationStatus.finalised.toString).saveMe()} } else { Failure(s"${ s"${ @@ -87,7 +91,7 @@ object MappedChallengeProvider extends ChallengeProvider { } case Some(id) => if(currentHashedAnswer==expectedHashedAnswer && id==challenge.expectedUserId) { - tryo{challenge.mSuccessful(true).mScaStatus(StrongCustomerAuthenticationStatus.finalised.toString).saveMe()} + tryo{challenge.Successful(true).ScaStatus(StrongCustomerAuthenticationStatus.finalised.toString).saveMe()} } else { Failure(s"${ s"${ diff --git a/obp-api/src/main/scala/code/transactionChallenge/MappedExpectedChallengeAnswer.scala b/obp-api/src/main/scala/code/transactionChallenge/MappedExpectedChallengeAnswer.scala index 3fd5683431..38bbf97dbe 100644 --- a/obp-api/src/main/scala/code/transactionChallenge/MappedExpectedChallengeAnswer.scala +++ b/obp-api/src/main/scala/code/transactionChallenge/MappedExpectedChallengeAnswer.scala @@ -2,9 +2,9 @@ package code.transactionChallenge import code.util.MappedUUID import com.openbankproject.commons.model.ChallengeTrait -import com.openbankproject.commons.model.enums.{StrongCustomerAuthentication, StrongCustomerAuthenticationStatus} import com.openbankproject.commons.model.enums.StrongCustomerAuthentication.SCA import com.openbankproject.commons.model.enums.StrongCustomerAuthenticationStatus.SCAStatus +import com.openbankproject.commons.model.enums.{StrongCustomerAuthentication, StrongCustomerAuthenticationStatus} import net.liftweb.mapper._ class MappedExpectedChallengeAnswer extends ChallengeTrait with LongKeyedMapper[MappedExpectedChallengeAnswer] with IdPK with CreatedUpdated { @@ -12,36 +12,39 @@ class MappedExpectedChallengeAnswer extends ChallengeTrait with LongKeyedMapper[ def getSingleton = MappedExpectedChallengeAnswer // Unique - object mChallengeId extends MappedUUID(this) - object mChallengeType extends MappedString(this, 100) - object mTransactionRequestId extends MappedUUID(this) - object mExpectedAnswer extends MappedString(this,50) - object mExpectedUserId extends MappedUUID(this) - object mSalt extends MappedString(this, 50) - object mSuccessful extends MappedBoolean(this) - - object mScaMethod extends MappedString(this,100) - object mScaStatus extends MappedString(this,100) - object mConsentId extends MappedString(this,100) - object mAuthenticationMethodId extends MappedString(this,100) - object mAttemptCounter extends MappedInt(this){ + object ChallengeId extends MappedUUID(this) + object ChallengeType extends MappedString(this, 100) + object TransactionRequestId extends MappedUUID(this) + object ExpectedAnswer extends MappedString(this,50) + object ExpectedUserId extends MappedUUID(this) + object Salt extends MappedString(this, 50) + object Successful extends MappedBoolean(this) + + object ScaMethod extends MappedString(this,100) + object ScaStatus extends MappedString(this,100) + object ConsentId extends MappedString(this,100) + object BasketId extends MappedString(this,100) + object AuthenticationMethodId extends MappedString(this,100) + object AttemptCounter extends MappedInt(this){ override def defaultValue = 0 } - - override def challengeId: String = mChallengeId.get - override def challengeType: String = mChallengeType.get - override def transactionRequestId: String = mTransactionRequestId.get - override def expectedAnswer: String = mExpectedAnswer.get - override def expectedUserId: String = mExpectedUserId.get - override def salt: String = mSalt.get - override def successful: Boolean = mSuccessful.get - override def consentId: Option[String] = Option(mConsentId.get) - override def scaMethod: Option[SCA] = Option(StrongCustomerAuthentication.withName(mScaMethod.get)) - override def scaStatus: Option[SCAStatus] = Option(StrongCustomerAuthenticationStatus.withName(mScaStatus.get)) - override def authenticationMethodId: Option[String] = Option(mAuthenticationMethodId.get) - override def attemptCounter: Int = mAttemptCounter.get + + override def challengeId: String = ChallengeId.get + override def challengeType: String = ChallengeType.get + override def transactionRequestId: String = TransactionRequestId.get + override def expectedAnswer: String = ExpectedAnswer.get + override def expectedUserId: String = ExpectedUserId.get + override def salt: String = Salt.get + override def successful: Boolean = Successful.get + override def consentId: Option[String] = Option(ConsentId.get) + override def basketId: Option[String] = Option(BasketId.get) + override def scaMethod: Option[SCA] = Option(StrongCustomerAuthentication.withName(ScaMethod.get)) + override def scaStatus: Option[SCAStatus] = Option(StrongCustomerAuthenticationStatus.withName(ScaStatus.get)) + override def authenticationMethodId: Option[String] = Option(AuthenticationMethodId.get) + override def attemptCounter: Int = AttemptCounter.get } object MappedExpectedChallengeAnswer extends MappedExpectedChallengeAnswer with LongKeyedMetaMapper[MappedExpectedChallengeAnswer] { - override def dbIndexes = UniqueIndex(mChallengeId):: super.dbIndexes + override def dbTableName = "ExpectedChallengeAnswer" // define the DB table name + override def dbIndexes = UniqueIndex(ChallengeId):: super.dbIndexes } \ No newline at end of file diff --git a/obp-api/src/main/scripts/migrate/migrate_00000011.sql b/obp-api/src/main/scripts/migrate/migrate_00000011.sql index 15336ccf81..acb2bfcbc3 100644 --- a/obp-api/src/main/scripts/migrate/migrate_00000011.sql +++ b/obp-api/src/main/scripts/migrate/migrate_00000011.sql @@ -1 +1,6 @@ -update viewimpl set isFirehose_=TRUE; \ No newline at end of file +update + viewimpl +set + isFirehose_ = TRUE +where + isFirehose_ <> TRUE; diff --git a/obp-api/src/main/scripts/migrate/migrate_00000013.sql b/obp-api/src/main/scripts/migrate/migrate_00000013.sql index 1c822f6d9c..969920f80f 100644 --- a/obp-api/src/main/scripts/migrate/migrate_00000013.sql +++ b/obp-api/src/main/scripts/migrate/migrate_00000013.sql @@ -1,6 +1,14 @@ -UPDATE consumer SET - perhourcalllimit = -1 , - perdaycalllimit = -1 , - perweekcalllimit = -1 , - permonthcalllimit = -1 , - perminutecalllimit = -1; \ No newline at end of file +UPDATE + consumer +SET + perhourcalllimit = -1, + perdaycalllimit = -1, + perweekcalllimit = -1, + permonthcalllimit = -1, + perminutecalllimit = -1 +WHERE + perhourcalllimit <> -1 + OR perdaycalllimit <> -1 + OR perweekcalllimit <> -1 + OR permonthcalllimit <> -1 + OR perminutecalllimit <> -1; diff --git a/obp-api/src/main/scripts/migrate/migrate_00000014.sql b/obp-api/src/main/scripts/migrate/migrate_00000014.sql index bbf1a8a543..ac8ad2734e 100644 --- a/obp-api/src/main/scripts/migrate/migrate_00000014.sql +++ b/obp-api/src/main/scripts/migrate/migrate_00000014.sql @@ -1 +1,6 @@ -UPDATE consumer SET persecondcalllimit = -1; \ No newline at end of file +UPDATE + consumer +SET + persecondcalllimit = -1 +where + persecondcalllimit <> -1; diff --git a/obp-api/src/test/scala/code/api/berlin/group/v1_3/AccountInformationServiceAISApiTest.scala b/obp-api/src/test/scala/code/api/berlin/group/v1_3/AccountInformationServiceAISApiTest.scala index 807d68467c..053be11881 100644 --- a/obp-api/src/test/scala/code/api/berlin/group/v1_3/AccountInformationServiceAISApiTest.scala +++ b/obp-api/src/test/scala/code/api/berlin/group/v1_3/AccountInformationServiceAISApiTest.scala @@ -59,7 +59,7 @@ class AccountInformationServiceAISApiTest extends BerlinGroupServerSetupV1_3 wit Then("We should get a 401 ") response.code should equal(401) - response.body.extract[ErrorMessage].message should startWith(UserNotLoggedIn) + response.body.extract[ErrorMessagesBG].tppMessages.head.text should startWith(UserNotLoggedIn) } scenario("Authentication User, test failed", BerlinGroupV1_3, getAccountList) { @@ -68,7 +68,7 @@ class AccountInformationServiceAISApiTest extends BerlinGroupServerSetupV1_3 wit Then("We should get a 403 ") response.code should equal(403) - response.body.extract[ErrorMessage].message should startWith(NoViewReadAccountsBerlinGroup) + response.body.extract[ErrorMessagesBG].tppMessages.head.text should startWith(NoViewReadAccountsBerlinGroup) } } @@ -79,7 +79,7 @@ class AccountInformationServiceAISApiTest extends BerlinGroupServerSetupV1_3 wit Then("We should get a 401 ") response.code should equal(401) - response.body.extract[ErrorMessage].message should startWith(UserNotLoggedIn) + response.body.extract[ErrorMessagesBG].tppMessages.head.text should startWith(UserNotLoggedIn) } scenario("Authentication User, test succeed", BerlinGroupV1_3, readAccountDetails) { @@ -109,7 +109,7 @@ class AccountInformationServiceAISApiTest extends BerlinGroupServerSetupV1_3 wit val requestGetFailed = (V1_3_BG / "accounts" / testAccountId1.value / "balances").GET <@ (user1) val responseGetFailed: APIResponse = makeGetRequest(requestGetFailed) responseGetFailed.code should equal(403) - responseGetFailed.body.extract[ErrorMessage].message should startWith(NoViewReadAccountsBerlinGroup) + responseGetFailed.body.extract[ErrorMessagesBG].tppMessages.head.text should startWith(NoViewReadAccountsBerlinGroup) grantUserAccessToViewViaEndpoint( bankId, @@ -137,7 +137,7 @@ class AccountInformationServiceAISApiTest extends BerlinGroupServerSetupV1_3 wit val responseGetFailed: APIResponse = makeGetRequest(requestGetFailed) Then("We should get a 403 ") responseGetFailed.code should equal(403) - responseGetFailed.body.extract[ErrorMessage].message should startWith(UserNoPermissionAccessView) + responseGetFailed.body.extract[ErrorMessagesBG].tppMessages.head.text should startWith(UserNoPermissionAccessView) val bankId = MappedBankAccount.find(By(MappedBankAccount.theAccountId, testAccountId.value)).map(_.bankId.value).getOrElse("") grantUserAccessToViewViaEndpoint( @@ -166,7 +166,7 @@ class AccountInformationServiceAISApiTest extends BerlinGroupServerSetupV1_3 wit val responseGetFailed: APIResponse = makeGetRequest(requestGetFailed) Then("We should get a 403 ") responseGetFailed.code should equal(403) - responseGetFailed.body.extract[ErrorMessage].message should startWith(UserNoPermissionAccessView) + responseGetFailed.body.extract[ErrorMessagesBG].tppMessages.head.text should startWith(UserNoPermissionAccessView) val bankId = MappedBankAccount.find(By(MappedBankAccount.theAccountId, testAccountId.value)).map(_.bankId.value).getOrElse("") grantUserAccessToViewViaEndpoint( @@ -200,7 +200,7 @@ class AccountInformationServiceAISApiTest extends BerlinGroupServerSetupV1_3 wit val responseGetFailed: APIResponse = makeGetRequest(requestGetFailed) Then("We should get a 403 ") responseGetFailed.code should equal(403) - responseGetFailed.body.extract[ErrorMessage].message should startWith(UserNoPermissionAccessView) + responseGetFailed.body.extract[ErrorMessagesBG].tppMessages.head.text should startWith(UserNoPermissionAccessView) val bankId = MappedBankAccount.find(By(MappedBankAccount.theAccountId, testAccountId.value)).map(_.bankId.value).getOrElse("") grantUserAccessToViewViaEndpoint( diff --git a/obp-api/src/test/scala/code/api/berlin/group/v1_3/ConfirmationOfFundsServicePIISApiTest.scala b/obp-api/src/test/scala/code/api/berlin/group/v1_3/ConfirmationOfFundsServicePIISApiTest.scala index 57885795c9..08e0c6f58c 100644 --- a/obp-api/src/test/scala/code/api/berlin/group/v1_3/ConfirmationOfFundsServicePIISApiTest.scala +++ b/obp-api/src/test/scala/code/api/berlin/group/v1_3/ConfirmationOfFundsServicePIISApiTest.scala @@ -1,5 +1,6 @@ package code.api.berlin.group.v1_3 +import code.api.berlin.group.v1_3.JSONFactory_BERLIN_GROUP_1_3.ErrorMessagesBG import com.openbankproject.commons.model.ErrorMessage import code.api.builder.ConfirmationOfFundsServicePIISApi.APIMethods_ConfirmationOfFundsServicePIISApi import code.api.util.APIUtil.OAuth._ @@ -33,8 +34,7 @@ class ConfirmationOfFundsServicePIISApiTest extends BerlinGroupServerSetupV1_3 w Then("We should get a 404 ") response.code should equal(404) - response.body.extract[ErrorMessage] - .message should startWith(BankAccountNotFoundByIban) + response.body.extract[ErrorMessagesBG].tppMessages.head.text should startWith(BankAccountNotFoundByIban) } scenario("Failed Case, invalid post json", BerlinGroupV1_3, PIIS, checkAvailabilityOfFunds) { @@ -43,8 +43,7 @@ class ConfirmationOfFundsServicePIISApiTest extends BerlinGroupServerSetupV1_3 w Then("We should get a 400 ") response.code should equal(400) - response.body.extract[ErrorMessage] - .message should startWith(InvalidJsonFormat) + response.body.extract[ErrorMessagesBG].tppMessages.head.text should startWith(InvalidJsonFormat) } scenario("Success case - Enough Funds", BerlinGroupV1_3, PIIS, checkAvailabilityOfFunds) { diff --git a/obp-api/src/test/scala/code/api/berlin/group/v1_3/PaymentInitiationServicePISApiTest.scala b/obp-api/src/test/scala/code/api/berlin/group/v1_3/PaymentInitiationServicePISApiTest.scala index 57d27118f7..4114ef160a 100644 --- a/obp-api/src/test/scala/code/api/berlin/group/v1_3/PaymentInitiationServicePISApiTest.scala +++ b/obp-api/src/test/scala/code/api/berlin/group/v1_3/PaymentInitiationServicePISApiTest.scala @@ -5,6 +5,7 @@ import code.api.Constant import code.api.Constant.SYSTEM_READ_TRANSACTIONS_BERLIN_GROUP_VIEW_ID import code.api.berlin.group.v1_3.JSONFactory_BERLIN_GROUP_1_3.{CancellationJsonV13, InitiatePaymentResponseJson, StartPaymentAuthorisationJson} import code.api.berlin.group.v1_3.model.{PsuData, ScaStatusResponse, UpdatePsuAuthenticationResponse} +import code.api.berlin.group.v1_3.JSONFactory_BERLIN_GROUP_1_3.{CancellationJsonV13, ErrorMessagesBG, InitiatePaymentResponseJson, StartPaymentAuthorisationJson} import code.api.builder.PaymentInitiationServicePISApi.APIMethods_PaymentInitiationServicePISApi import code.api.util.APIUtil.OAuth._ import code.api.util.APIUtil.extractErrorMessageCode @@ -71,7 +72,7 @@ class PaymentInitiationServicePISApiTest extends BerlinGroupServerSetupV1_3 with response.code should equal(400) val error = s"$InvalidJsonFormat The Json body should be the $SepaCreditTransfersBerlinGroupV13 " And("error should be " + error) - response.body.extract[ErrorMessage].message should startWith (error) + response.body.extract[ErrorMessagesBG].tppMessages.head.text should startWith (error) } scenario("Failed Case - wrong amount", BerlinGroupV1_3, PIS, initiatePayment) { val wrongAmountInitiatePaymentJson = @@ -95,7 +96,7 @@ class PaymentInitiationServicePISApiTest extends BerlinGroupServerSetupV1_3 with response.code should equal(400) val error = s"${NotPositiveAmount} Current input is: '-1234'" And("error should be " + error) - response.body.extract[ErrorMessage].message contains extractErrorMessageCode(NotPositiveAmount) should be (true) + response.body.extract[ErrorMessagesBG].tppMessages.head.text contains extractErrorMessageCode(NotPositiveAmount) should be (true) } scenario("Successful case - small amount -- change the balance", BerlinGroupV1_3, PIS, initiatePayment) { val accountsRoutingIban = BankAccountRouting.findAll(By(BankAccountRouting.AccountRoutingScheme, AccountRoutingScheme.IBAN.toString)) @@ -307,7 +308,7 @@ class PaymentInitiationServicePISApiTest extends BerlinGroupServerSetupV1_3 with val response: APIResponse = makePostRequest(requestPost, """{"scaAuthenticationData":"123"}""".stripMargin) Then("We should get a 400 ") response.code should equal(400) - response.body.extract[ErrorMessage].message should startWith (InvalidTransactionRequestId) + response.body.extract[ErrorMessagesBG].tppMessages.head.text should startWith (InvalidTransactionRequestId) } scenario(s"Successful Case ", BerlinGroupV1_3, PIS, startPaymentAuthorisationTransactionAuthorisation) { val accountsRoutingIban = BankAccountRouting.findAll(By(BankAccountRouting.AccountRoutingScheme, AccountRoutingScheme.IBAN.toString)).filterNot(_.bankId.value == "DEFAULT_BANK_ID_NOT_SET") @@ -407,17 +408,17 @@ class PaymentInitiationServicePISApiTest extends BerlinGroupServerSetupV1_3 with } } - + feature(s"test the BG v1.3 ${updatePaymentPsuDataUpdatePsuAuthentication} and ${updatePaymentPsuDataUpdatePsuAuthentication.name}") { scenario(s"${startPaymentAuthorisationTransactionAuthorisation.name}" , BerlinGroupV1_3, PIS, updatePaymentPsuDataUpdatePsuAuthentication) { - + val requestPost = (V1_3_BG / PaymentServiceTypes.payments.toString / TransactionRequestTypes.SEPA_CREDIT_TRANSFERS.toString / "PAYMENT_ID" / "authorisations" / "AUTHORISATION_ID").PUT <@ (user1) val response: APIResponse = makePutRequest(requestPost, """{"psuData": {"password": "start12"}}""".stripMargin) Then("We should get a 200 ") response.code should equal(200) } } - + feature(s"test the BG v1.3 ${updatePaymentPsuDataSelectPsuAuthenticationMethod} and ${updatePaymentPsuDataSelectPsuAuthenticationMethod.name}") { scenario(s"${startPaymentAuthorisationTransactionAuthorisation.name}" , BerlinGroupV1_3, PIS, updatePaymentPsuDataSelectPsuAuthenticationMethod) { val requestPut = (V1_3_BG / PaymentServiceTypes.payments.toString / TransactionRequestTypes.SEPA_CREDIT_TRANSFERS.toString / "PAYMENT_ID" / "authorisations" / "AUTHORISATION_ID").PUT <@ (user1) @@ -426,21 +427,21 @@ class PaymentInitiationServicePISApiTest extends BerlinGroupServerSetupV1_3 with response.code should equal(200) } } - + feature(s"test the BG v1.3 ${updatePaymentPsuDataAuthorisationConfirmation} and ${updatePaymentPsuDataAuthorisationConfirmation.name}") { scenario(s"${startPaymentAuthorisationTransactionAuthorisation.name}" , BerlinGroupV1_3, PIS, updatePaymentPsuDataAuthorisationConfirmation) { - + val requestPost = (V1_3_BG / PaymentServiceTypes.payments.toString / TransactionRequestTypes.SEPA_CREDIT_TRANSFERS.toString / "PAYMENT_ID" / "authorisations"/"AUTHORISATION_ID").PUT <@ (user1) val response: APIResponse = makePutRequest(requestPost, """{"confirmationCode":"confirmationCode"}""".stripMargin) Then("We should get a 200 ") response.code should equal(200) } } - - + + feature(s"test the BG v1.3 ${startPaymentAuthorisationUpdatePsuAuthentication.name}") { scenario(s"${startPaymentAuthorisationUpdatePsuAuthentication.name} ", BerlinGroupV1_3, PIS, startPaymentAuthorisationUpdatePsuAuthentication) { - + val requestPost = (V1_3_BG / PaymentServiceTypes.payments.toString / TransactionRequestTypes.SEPA_CREDIT_TRANSFERS.toString / "PAYMENT_ID" / "authorisations").POST <@ (user1) val response: APIResponse = makePostRequest(requestPost, """{ "psuData":{"password":"start12" }}""".stripMargin) Then("We should get a 201") @@ -449,14 +450,14 @@ class PaymentInitiationServicePISApiTest extends BerlinGroupServerSetupV1_3 with } feature(s"test the BG v1.3 ${startPaymentAuthorisationSelectPsuAuthenticationMethod.name}") { scenario(s"${startPaymentAuthorisationSelectPsuAuthenticationMethod.name} ", BerlinGroupV1_3, PIS, startPaymentAuthorisationSelectPsuAuthenticationMethod) { - + val requestPost = (V1_3_BG / PaymentServiceTypes.payments.toString / TransactionRequestTypes.SEPA_CREDIT_TRANSFERS.toString / "PAYMENT_ID" / "authorisations").POST <@ (user1) val response: APIResponse = makePostRequest(requestPost, """{"authenticationMethodId":""}""".stripMargin) Then("We should get a 201") response.code should equal(201) } } - + feature(s"test the BG v1.3 ${startPaymentInitiationCancellationAuthorisationTransactionAuthorisation.name} " + s"and ${getPaymentInitiationCancellationAuthorisationInformation.name} " + s"and ${getPaymentCancellationScaStatus.name}" + @@ -467,7 +468,7 @@ class PaymentInitiationServicePISApiTest extends BerlinGroupServerSetupV1_3 with val response: APIResponse = makePostRequest(requestPost, """{"scaAuthenticationData":""}""") Then("We should get a 400 ") response.code should equal(400) - response.body.extract[ErrorMessage].message should startWith (InvalidTransactionRequestId) + response.body.extract[ErrorMessagesBG].tppMessages.head.text should startWith (InvalidTransactionRequestId) } scenario(s"Successful Case ", BerlinGroupV1_3, PIS) { @@ -603,7 +604,7 @@ class PaymentInitiationServicePISApiTest extends BerlinGroupServerSetupV1_3 with response.code should equal(400) val error = s"$InvalidTransactionRequestId Current TransactionRequestId(PAYMENT_ID) " And("error should be " + error) - response.body.extract[ErrorMessage].message should equal (error) + response.body.extract[ErrorMessagesBG].tppMessages.head.text should equal (error) } } feature("test the BG v1.3 getPaymentInitiationAuthorisation") { @@ -619,7 +620,7 @@ class PaymentInitiationServicePISApiTest extends BerlinGroupServerSetupV1_3 with response.code should equal(400) val error = s"$InvalidTransactionRequestId Current TransactionRequestId(NON_EXISTING_PAYMENT_ID) " And("error should be " + error) - response.body.extract[ErrorMessage].message should equal (error) + response.body.extract[ErrorMessagesBG].tppMessages.head.text should equal (error) } } feature("test the BG v1.3 getPaymentInitiationCancellationAuthorisationInformation") { diff --git a/obp-api/src/test/scala/code/api/berlin/group/v1_3/SigningBasketServiceSBSApiTest.scala b/obp-api/src/test/scala/code/api/berlin/group/v1_3/SigningBasketServiceSBSApiTest.scala new file mode 100644 index 0000000000..490e886ffe --- /dev/null +++ b/obp-api/src/test/scala/code/api/berlin/group/v1_3/SigningBasketServiceSBSApiTest.scala @@ -0,0 +1,250 @@ +package code.api.berlin.group.v1_3 + +import code.api.berlin.group.ConstantsBG +import code.api.berlin.group.v1_3.JSONFactory_BERLIN_GROUP_1_3.{AuthorisationJsonV13, ErrorMessagesBG, PostSigningBasketJsonV13, ScaStatusJsonV13, SigningBasketGetResponseJson, SigningBasketResponseJson, StartPaymentAuthorisationJson} +import code.api.builder.SigningBasketsApi.APIMethods_SigningBasketsApi +import code.api.util.APIUtil.OAuth._ +import code.api.util.ErrorMessages._ +import code.setup.{APIResponse, DefaultUsers} +import com.github.dwickern.macros.NameOf.nameOf +import com.openbankproject.commons.model.enums.StrongCustomerAuthenticationStatus +import org.scalatest.Tag + +class SigningBasketServiceSBSApiTest extends BerlinGroupServerSetupV1_3 with DefaultUsers { + object SBS extends Tag("Signing Baskets Service (SBS)") + object createSigningBasket extends Tag(nameOf(APIMethods_SigningBasketsApi.createSigningBasket)) + object getSigningBasket extends Tag(nameOf(APIMethods_SigningBasketsApi.getSigningBasket)) + object getSigningBasketStatus extends Tag(nameOf(APIMethods_SigningBasketsApi.getSigningBasketStatus)) + object deleteSigningBasket extends Tag(nameOf(APIMethods_SigningBasketsApi.deleteSigningBasket)) + object startSigningBasketAuthorisation extends Tag(nameOf(APIMethods_SigningBasketsApi.startSigningBasketAuthorisation)) + object getSigningBasketScaStatus extends Tag(nameOf(APIMethods_SigningBasketsApi.getSigningBasketScaStatus)) + object getSigningBasketAuthorisation extends Tag(nameOf(APIMethods_SigningBasketsApi.getSigningBasketAuthorisation)) + object updateSigningBasketPsuData extends Tag(nameOf(APIMethods_SigningBasketsApi.updateSigningBasketPsuData)) + + feature(s"test the BG v1.3 - ${createSigningBasket.name}") { + scenario("Failed Case - Unauthenticated Access", BerlinGroupV1_3, SBS, createSigningBasket) { + val postJson = + s"""{ + | "consentIds": [ + | "123qwert456789", + | "12345qwert7899" + | ] + |}""".stripMargin + + val requestPost = (V1_3_BG / "signing-baskets").POST + val response: APIResponse = makePostRequest(requestPost, postJson) + Then("We should get a 401 ") + response.code should equal(401) + val error = s"$UserNotLoggedIn" + And("error should be " + error) + response.body.extract[ErrorMessagesBG].tppMessages.head.text should startWith(error) + } + } + + feature(s"test the BG v1.3 -${createSigningBasket.name}") { + scenario("Failed Case - Wrong Json format Body", BerlinGroupV1_3, SBS, createSigningBasket) { + val wrongFieldNameJson = + s"""{ + | "wrongFieldName": [ + | "123qwert456789", + | "12345qwert7899" + | ] + |}""".stripMargin + + val requestPost = (V1_3_BG / "signing-baskets").POST <@ (user1) + val response: APIResponse = makePostRequest(requestPost, wrongFieldNameJson) + Then("We should get a 400 ") + response.code should equal(400) + val error = s"$InvalidJsonFormat The Json body should be the $PostSigningBasketJsonV13 " + And("error should be " + error) + response.body.extract[ErrorMessagesBG].tppMessages.head.text should startWith (error) + } + } + + // TODO Add check that paymentId is an existing transaction request + feature(s"test the BG v1.3 -${createSigningBasket.name}") { + scenario("Failed Case - Successful", BerlinGroupV1_3, SBS, createSigningBasket) { + val postJson = + s"""{ + | "paymentIds": [ + | "123qwert456789", + | "12345qwert7899" + | ] + |}""".stripMargin + + val requestPost = (V1_3_BG / "signing-baskets").POST <@ (user1) + val response: APIResponse = makePostRequest(requestPost, postJson) + Then("We should get a 201 ") + response.code should equal(201) + } + } + + + feature(s"test the BG v1.3 - ${getSigningBasket.name}") { + scenario("Failed Case - Unauthenticated Access", BerlinGroupV1_3, SBS, getSigningBasket) { + val requestGet = (V1_3_BG / "signing-baskets" / "basketId").GET + val responseGet = makeGetRequest(requestGet) + Then("We should get a 401 ") + responseGet.code should equal(401) + val error = s"$UserNotLoggedIn" + And("error should be " + error) + responseGet.body.extract[ErrorMessagesBG].tppMessages.head.text should startWith(error) + } + } + + feature(s"test the BG v1.3 - ${getSigningBasketStatus.name}") { + scenario("Failed Case - Unauthenticated Access", BerlinGroupV1_3, SBS, getSigningBasketStatus) { + val requestGet = (V1_3_BG / "signing-baskets" / "basketId" / "status").GET + val responseGet = makeGetRequest(requestGet) + Then("We should get a 401 ") + responseGet.code should equal(401) + val error = s"$UserNotLoggedIn" + And("error should be " + error) + responseGet.body.extract[ErrorMessagesBG].tppMessages.head.text should startWith(error) + } + } + + feature(s"test the BG v1.3 - ${deleteSigningBasket.name}") { + scenario("Failed Case - Unauthenticated Access", BerlinGroupV1_3, SBS, deleteSigningBasket) { + val request = (V1_3_BG / "signing-baskets" / "basketId").DELETE + val response = makeDeleteRequest(request) + Then("We should get a 401 ") + response.code should equal(401) + val error = s"$UserNotLoggedIn" + And("error should be " + error) + response.body.extract[ErrorMessagesBG].tppMessages.head.text should startWith(error) + } + } + + feature(s"test the BG v1.3 - ${startSigningBasketAuthorisation.name}") { + scenario("Failed Case - Unauthenticated Access", BerlinGroupV1_3, SBS, startSigningBasketAuthorisation) { + val postJson = s"""{}""".stripMargin + val request = (V1_3_BG / "signing-baskets" / "basketId" / "authorisations").POST + val response = makePostRequest(request, postJson) + Then("We should get a 401 ") + response.code should equal(401) + val error = s"$UserNotLoggedIn" + And("error should be " + error) + response.body.extract[ErrorMessagesBG].tppMessages.head.text should startWith(error) + } + } + + feature(s"test the BG v1.3 - ${getSigningBasketScaStatus.name}") { + scenario("Failed Case - Unauthenticated Access", BerlinGroupV1_3, SBS, getSigningBasketScaStatus) { + val requestGet = (V1_3_BG / "signing-baskets" / "basketId" / "authorisations" / "authorisationId").GET + val responseGet = makeGetRequest(requestGet) + Then("We should get a 401 ") + responseGet.code should equal(401) + val error = s"$UserNotLoggedIn" + And("error should be " + error) + responseGet.body.extract[ErrorMessagesBG].tppMessages.head.text should startWith(error) + } + } + + feature(s"test the BG v1.3 - ${getSigningBasketAuthorisation.name}") { + scenario("Failed Case - Unauthenticated Access", BerlinGroupV1_3, SBS, getSigningBasketAuthorisation) { + val requestGet = (V1_3_BG / "signing-baskets" / "basketId" / "authorisations").GET + val responseGet = makeGetRequest(requestGet) + Then("We should get a 401 ") + responseGet.code should equal(401) + val error = s"$UserNotLoggedIn" + And("error should be " + error) + responseGet.body.extract[ErrorMessagesBG].tppMessages.head.text should startWith(error) + } + } + + feature(s"test the BG v1.3 - ${updateSigningBasketPsuData.name}") { + scenario("Failed Case - Unauthenticated Access", BerlinGroupV1_3, SBS, updateSigningBasketPsuData) { + val putJson = s"""{"scaAuthenticationData":"123"}""".stripMargin + val request = (V1_3_BG / "signing-baskets" / "basketId" / "authorisations" / "authorisationId").PUT + val response = makePutRequest(request, putJson) + Then("We should get a 401 ") + response.code should equal(401) + val error = s"$UserNotLoggedIn" + And("error should be " + error) + response.body.extract[ErrorMessagesBG].tppMessages.head.text should startWith(error) + } + } + + + feature(s"BG v1.3 - $createSigningBasket, $getSigningBasket, $getSigningBasketStatus, $deleteSigningBasket, $startSigningBasketAuthorisation, $getSigningBasketAuthorisation, $updateSigningBasketPsuData") { + scenario("Authentication User, test succeed", BerlinGroupV1_3, SBS, createSigningBasket, getSigningBasket, getSigningBasketStatus, deleteSigningBasket, startSigningBasketAuthorisation, getSigningBasketAuthorisation, updateSigningBasketPsuData) { + // Create Signing Basket + val postJson = + s"""{ + | "paymentIds": [ + | "123qwert456789", + | "12345qwert7899" + | ] + |}""".stripMargin + + val requestPost = (V1_3_BG / "signing-baskets").POST <@ (user1) + val response: APIResponse = makePostRequest(requestPost, postJson) + Then("We should get a 201 ") + response.code should equal(201) + + val basketId = response.body.extract[SigningBasketResponseJson].basketId + + // Get Signing Basket + Then(s"We test the $getSigningBasket") + val requestGet = (V1_3_BG / "signing-baskets" / basketId).GET <@ (user1) + val responseGet = makeGetRequest(requestGet) + responseGet.code should be(200) + responseGet.body.extract[SigningBasketGetResponseJson].transactionStatus should + be(ConstantsBG.SigningBasketsStatus.RCVD.toString.toLowerCase()) + + // Get Signing Basket Status + Then(s"We test the $getSigningBasketStatus") + val requestGetStatus = (V1_3_BG / "signing-baskets" / basketId / "status").GET <@ (user1) + var responseGetStatus = makeGetRequest(requestGetStatus) + responseGetStatus.code should be(200) + responseGetStatus.body.extract[SigningBasketGetResponseJson].transactionStatus should + be(ConstantsBG.SigningBasketsStatus.RCVD.toString.toLowerCase()) + + // Delete Signing Basket + val requestDelete = (V1_3_BG / "signing-baskets" / basketId).DELETE <@ (user1) + val responseDelete = makeDeleteRequest(requestDelete) + responseDelete.code should be(204) + + responseGetStatus = makeGetRequest(requestGetStatus) + responseGetStatus.code should be(200) + responseGetStatus.body.extract[SigningBasketGetResponseJson].transactionStatus should + be(ConstantsBG.SigningBasketsStatus.CANC.toString.toLowerCase()) + + // Start Signing Basket Auth Flow + val postJsonAuth = s"""{}""".stripMargin + val requestAuth = (V1_3_BG / "signing-baskets" / basketId / "authorisations").POST <@ (user1) + val responseAuth = makePostRequest(requestAuth, postJsonAuth) + Then("We should get a 201 ") + responseAuth.code should equal(201) + responseAuth.body.extract[StartPaymentAuthorisationJson].scaStatus should + be(StrongCustomerAuthenticationStatus.received.toString) + val authorisationId = responseAuth.body.extract[StartPaymentAuthorisationJson].authorisationId + + // Get Signing Basket Auth Flow Status + val requestAuthStatus = (V1_3_BG / "signing-baskets" / basketId / "authorisations" / authorisationId).GET <@ (user1) + val responseAuthStatus = makeGetRequest(requestAuthStatus) + Then("We should get a 200 ") + responseAuthStatus.code should equal(200) + responseAuthStatus.body.extract[ScaStatusJsonV13].scaStatus should + be(responseAuth.body.extract[StartPaymentAuthorisationJson].scaStatus) + + // Get Signing Basket Authorisations + val requestGetAuths = (V1_3_BG / "signing-baskets" / "basketId" / "authorisations").GET <@ (user1) + val responseGetAuths = makeGetRequest(requestGetAuths) + Then("We should get a 200 ") + responseGetAuths.code should equal(200) + responseGetAuths.body.extract[AuthorisationJsonV13] + + // Failed due to unexisting paymentIds + val putJson = s"""{"scaAuthenticationData":"123"}""".stripMargin + val requestPut = (V1_3_BG / "signing-baskets" / basketId / "authorisations" / authorisationId).PUT <@ (user1) + val responsePut = makePutRequest(requestPut, putJson) + val error = s"$InvalidConnectorResponse" + And("error should be " + error) + responsePut.body.extract[ErrorMessagesBG].tppMessages.head.text should startWith(error) + } + } + + +} \ No newline at end of file diff --git a/obp-api/src/test/scala/code/connector/RestConnector_vMar2019_frozen_meta_data b/obp-api/src/test/scala/code/connector/RestConnector_vMar2019_frozen_meta_data index 5a1f839fd6..58c888c9b5 100644 Binary files a/obp-api/src/test/scala/code/connector/RestConnector_vMar2019_frozen_meta_data and b/obp-api/src/test/scala/code/connector/RestConnector_vMar2019_frozen_meta_data differ diff --git a/obp-api/src/test/scala/code/util/MappedClassNameTest.scala b/obp-api/src/test/scala/code/util/MappedClassNameTest.scala index 06dc3040fa..347bc6ec08 100644 --- a/obp-api/src/test/scala/code/util/MappedClassNameTest.scala +++ b/obp-api/src/test/scala/code/util/MappedClassNameTest.scala @@ -116,6 +116,9 @@ class MappedClassNameTest extends FeatureSpec { "code.UserRefreshes.MappedUserRefreshes", "code.DynamicEndpoint.DynamicEndpoint", "code.regulatedentities.MappedRegulatedEntity", + "code.signingbaskets.MappedSigningBasketConsent", + "code.signingbaskets.MappedSigningBasket", + "code.signingbaskets.MappedSigningBasketPayment", "code.CustomerDependants.MappedCustomerDependant") val newMappedTypes = ClassScanUtils.findTypes{ info => diff --git a/obp-commons/src/main/scala/com/openbankproject/commons/dto/JsonsTransfer.scala b/obp-commons/src/main/scala/com/openbankproject/commons/dto/JsonsTransfer.scala index deaf224f02..16e932fc73 100644 --- a/obp-commons/src/main/scala/com/openbankproject/commons/dto/JsonsTransfer.scala +++ b/obp-commons/src/main/scala/com/openbankproject/commons/dto/JsonsTransfer.scala @@ -1334,8 +1334,19 @@ case class OutBoundCreateChallengesC2( scaStatus: Option[SCAStatus], consentId: Option[String], authenticationMethodId: Option[String]) extends TopicTrait +case class OutBoundCreateChallengesC3( + outboundAdapterCallContext: OutboundAdapterCallContext, + userIds: List[String], + challengeType: ChallengeType.Value, + transactionRequestId: Option[String], + scaMethod: Option[SCA], + scaStatus: Option[SCAStatus], + consentId: Option[String], + basketId: Option[String], + authenticationMethodId: Option[String]) extends TopicTrait case class InBoundCreateChallengesC2(inboundAdapterCallContext: InboundAdapterCallContext, status: Status, data: List[ChallengeCommons]) extends InBoundTrait[List[ChallengeCommons]] +case class InBoundCreateChallengesC3(inboundAdapterCallContext: InboundAdapterCallContext, status: Status, data: List[ChallengeCommons]) extends InBoundTrait[List[ChallengeCommons]] case class OutBoundValidateChallengeAnswerC2( outboundAdapterCallContext: OutboundAdapterCallContext, @@ -1345,7 +1356,17 @@ case class OutBoundValidateChallengeAnswerC2( hashOfSuppliedAnswer: String ) extends TopicTrait +case class OutBoundValidateChallengeAnswerC3( + outboundAdapterCallContext: OutboundAdapterCallContext, + transactionRequestId: Option[String], + consentId: Option[String], + basketId: Option[String], + challengeId: String, + hashOfSuppliedAnswer: String +) extends TopicTrait + case class InBoundValidateChallengeAnswerC2(inboundAdapterCallContext: InboundAdapterCallContext, status: Status, data: ChallengeCommons) extends InBoundTrait[ChallengeCommons] +case class InBoundValidateChallengeAnswerC3(inboundAdapterCallContext: InboundAdapterCallContext, status: Status, data: ChallengeCommons) extends InBoundTrait[ChallengeCommons] case class OutBoundValidateAndCheckIbanNumber( outboundAdapterCallContext: OutboundAdapterCallContext, diff --git a/obp-commons/src/main/scala/com/openbankproject/commons/model/CommonModel.scala b/obp-commons/src/main/scala/com/openbankproject/commons/model/CommonModel.scala index 31844b7af1..3b869c13b9 100644 --- a/obp-commons/src/main/scala/com/openbankproject/commons/model/CommonModel.scala +++ b/obp-commons/src/main/scala/com/openbankproject/commons/model/CommonModel.scala @@ -532,6 +532,7 @@ case class ChallengeCommons( override val challengeType: String, override val consentId: Option[String], + override val basketId: Option[String] = None, override val scaMethod: Option[SCA], override val scaStatus: Option[SCAStatus], override val authenticationMethodId: Option[String] , diff --git a/obp-commons/src/main/scala/com/openbankproject/commons/model/CommonModelTrait.scala b/obp-commons/src/main/scala/com/openbankproject/commons/model/CommonModelTrait.scala index 7707a607b9..06161ca899 100644 --- a/obp-commons/src/main/scala/com/openbankproject/commons/model/CommonModelTrait.scala +++ b/obp-commons/src/main/scala/com/openbankproject/commons/model/CommonModelTrait.scala @@ -96,6 +96,23 @@ trait AccountApplication { def status: String } +trait SigningBasketTrait { + def basketId: String + def status: String +} +case class SigningBasketContent( + basket: SigningBasketTrait, + payments: Option[List[String]], + consents: Option[List[String]] + ) +trait SigningBasketPaymentTrait { + def basketId: String + def paymentId: String +} +trait SigningBasketConsentTrait { + def basketId: String + def consentId: String +} trait RegulatedEntityTrait { def entityId: String @@ -590,6 +607,7 @@ trait ChallengeTrait { //NOTE: following are from BerlinGroup, we try to share the same challenges for different standard. //for OBP standard, all the following can be Optional: def consentId: Option[String] // Note: consentId and transactionRequestId are exclusive here. + def basketId: Option[String] // Note: consentId and transactionRequestId are exclusive here. def scaMethod: Option[SCA] def scaStatus: Option[SCAStatus] def authenticationMethodId: Option[String] diff --git a/obp-commons/src/main/scala/com/openbankproject/commons/model/enums/Enumerations.scala b/obp-commons/src/main/scala/com/openbankproject/commons/model/enums/Enumerations.scala index 8623e99a02..35bd148f68 100644 --- a/obp-commons/src/main/scala/com/openbankproject/commons/model/enums/Enumerations.scala +++ b/obp-commons/src/main/scala/com/openbankproject/commons/model/enums/Enumerations.scala @@ -125,8 +125,9 @@ sealed trait ChallengeType extends EnumValue object ChallengeType extends OBPEnumeration[ChallengeType] { object OBP_TRANSACTION_REQUEST_CHALLENGE extends Value object OBP_CONSENT_CHALLENGE extends Value - object BERLINGROUP_PAYMENT_CHALLENGE extends Value - object BERLINGROUP_CONSENT_CHALLENGE extends Value + object BERLIN_GROUP_PAYMENT_CHALLENGE extends Value + object BERLIN_GROUP_CONSENT_CHALLENGE extends Value + object BERLIN_GROUP_SIGNING_BASKETS_CHALLENGE extends Value } sealed trait PemCertificateRole extends EnumValue diff --git a/release_notes.md b/release_notes.md index 489530bf20..76a124111a 100644 --- a/release_notes.md +++ b/release_notes.md @@ -3,6 +3,7 @@ ### Most recent changes at top of file ``` Date Commit Action +12/01/2024 e14946b8 Renamed table MappedExpectedChallengeAnswer => ExpectedChallengeAnswer and removed prefix 'm' from fields. 16/11/2023 2b8811dc Added show_used_connector_methods, default is false. 30/10/2023 4e82c66c Added createLocalisedResourceDocJson.cache.ttl.seconds, default is 3600 13/10/2023 d87c99d8 Added props hikari.connectionTimeout, default is from hikari.