Merge pull request #1373 from hongwei1/develop

Berlin Group
This commit is contained in:
Simon Redfern 2019-07-10 12:38:51 +02:00 committed by GitHub
commit 3dcb585bad
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
30 changed files with 791 additions and 5381 deletions

View File

@ -80,8 +80,13 @@
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpg-jdk16</artifactId>
<version>1.46</version>
<artifactId>bcpg-jdk15on</artifactId>
<version>1.47</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk15on</artifactId>
<version>1.62</version>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>

View File

@ -313,6 +313,10 @@ webui_api_documentation_url = https://github.com/OpenBankProject/OBP-API/wiki
# We currently use this to display example customer login in sandbox etc.
webui_login_page_special_instructions=
# To display the introduction page for sandbox.It supports the markdown format.It will show the introduction OBP-API home
# page `INTRODUCTION` page and also for Glossary `Sandbox Introduction`.
webui_sandbox_introduction=
# Link for SDKs
webui_sdks_url = https://github.com/OpenBankProject/OBP-API/wiki/OAuth-Client-SDKS

View File

@ -691,7 +691,7 @@ where the consent was directly managed between ASPSP and PSU e.g. in a re-direct
(Full(u), callContext) <- authorizedAccess(cc)
_ <- passesPsd2Aisp(callContext)
consent <- Future(Consents.consentProvider.vend.getConsentByConsentId(consentId)) map {
i => connectorEmptyResponse(i, callContext)
unboxFullOrFail(_, callContext, s"$ConsentNotFound ($consentId)")
}
} yield {
(createGetConsentResponseJson(consent), HttpCode.`200`(callContext))
@ -735,7 +735,7 @@ This method returns the SCA status of a consent initiation's authorisation sub-r
(_, callContext) <- authorizedAccess(cc)
_ <- passesPsd2Aisp(callContext)
_ <- Future(Consents.consentProvider.vend.getConsentByConsentId(consentId)) map {
unboxFullOrFail(_, callContext, ConsentNotFound)
unboxFullOrFail(_, callContext, s"$ConsentNotFound ($consentId)")
}
authorisation <- Future(Authorisations.authorisationProvider.vend.getAuthorizationByAuthorizationId(
authorisationId
@ -788,16 +788,17 @@ This method returns the SCA status of a consent initiation's authorisation sub-r
getTransactionDetails,
apiVersion,
nameOf(getTransactionDetails),
"GET",
"/accounts/ACCOUNT_ID/transactions/RESOURCEID",
"GET",
"/accounts/ACCOUNT_ID/transactions/TRANSACTIONID",
"Read Transaction Details",
s"""${mockedDataText(true)}
Reads transaction details from a given transaction addressed by "resourceId" on a given account addressed by "account-id".
This call is only available on transactions as reported in a JSON format.
Reads transaction details from a given transaction addressed by "transactionId" on a given account addressed
by "account-id". This call is only available on transactions as reported in a JSON format.
**Remark:** Please note that the PATH might be already given in detail by the corresponding entry of the response
of the "Read Transaction List" call within the _links subfield.
**Remark:** Please note that the PATH might be already given in detail by the corresponding entry of the response of the
"Read Transaction List" call within the _links subfield.
""",
""",
json.parse(""""""),
json.parse("""{
"debtorAccount" : {
@ -819,8 +820,8 @@ This call is only available on transactions as reported in a JSON format.
"valueDate" : "2000-01-23",
"endToEndId" : "endToEndId",
"transactionId" : "transactionId",
"currencyExchange" : "",
"ultimateDebtor" : "Ultimate Debtor",
"exchangeRate" : "",
"creditorAccount" : {
"bban" : "BARC12345612345678",
"maskedPan" : "123456xxxxxx1234",
@ -837,7 +838,7 @@ This call is only available on transactions as reported in a JSON format.
},
"proprietaryBankTransactionCode" : { },
"bookingDate" : { },
"remittanceInformationUnstructured" : "remittanceInformationUnstructured",
"remittanceInformationUnstructured" : "Ref Number Merchant",
"checkId" : "checkId",
"creditorId" : "creditorId",
"entryReference" : "entryReference"
@ -848,7 +849,7 @@ This call is only available on transactions as reported in a JSON format.
)
lazy val getTransactionDetails : OBPEndpoint = {
case "accounts" :: account_id:: "transactions" :: resourceid :: Nil JsonGet _ => {
case "accounts" :: account_id:: "transactions" :: transactionid :: Nil JsonGet _ => {
cc =>
for {
(Full(u), callContext) <- authorizedAccess(cc)
@ -873,8 +874,8 @@ This call is only available on transactions as reported in a JSON format.
"valueDate" : "2000-01-23",
"endToEndId" : "endToEndId",
"transactionId" : "transactionId",
"currencyExchange" : "",
"ultimateDebtor" : "Ultimate Debtor",
"exchangeRate" : "",
"creditorAccount" : {
"bban" : "BARC12345612345678",
"maskedPan" : "123456xxxxxx1234",
@ -891,7 +892,7 @@ This call is only available on transactions as reported in a JSON format.
},
"proprietaryBankTransactionCode" : { },
"bookingDate" : { },
"remittanceInformationUnstructured" : "remittanceInformationUnstructured",
"remittanceInformationUnstructured" : "Ref Number Merchant",
"checkId" : "checkId",
"creditorId" : "creditorId",
"entryReference" : "entryReference"
@ -899,7 +900,7 @@ This call is only available on transactions as reported in a JSON format.
}
}
}
resourceDocs += ResourceDoc(
getTransactionList,
apiVersion,
@ -908,11 +909,12 @@ This call is only available on transactions as reported in a JSON format.
"/accounts/ACCOUNT_ID/transactions",
"Read transaction list of an account",
s"""${mockedDataText(false)}
Read transaction reports or transaction lists of a given account ddressed by "account-id", depending on the steering parameter "bookingStatus" together with balances.
Read transaction reports or transaction lists of a given account ddressed by "account-id",
depending on the steering parameter "bookingStatus" together with balances.
For a given account, additional parameters are e.g. the attributes "dateFrom" and "dateTo".
The ASPSP might add balance information, if transaction lists without balances are not supported.
For a given account, additional parameters are e.g. the attributes "dateFrom" and "dateTo".
The ASPSP might add balance information, if transaction lists without balances are not supported.
""",
""",
json.parse(""""""),
json.parse("""{
"account": {
@ -966,7 +968,7 @@ The ASPSP might add balance information, if transaction lists without balances a
],
"_links": {
"account": {
"href": "/v1.3/accounts/3dc3d5b3-7023-4848-9853- f5400a64e80f"
"href": "/v1.3/accounts/3dc3d5b3-7023-4848-9853-f5400a64e80f"
}
}
}
@ -1020,19 +1022,14 @@ The ASPSP might add balance information, if transaction lists without balances a
"/accounts/ACCOUNT_ID",
"Read Account Details",
s"""${mockedDataText(true)}
Reads details about an account, with balances where required.
It is assumed that a consent of the PSU to
this access is already given and stored on the ASPSP system.
The addressed details of this account depends then on the stored consent addressed by consentId,
respectively the OAuth2 access token.
Reads details about an account, with balances where required.
It is assumed that a consent of the PSU to this access is already given and stored on the ASPSP system.
The addressed details of this account depends then on the stored consent addressed by consentId,
respectively the OAuth2 access token. **NOTE:** The account-id can represent a multicurrency account.
In this case the currency code is set to "XXX". Give detailed information about the addressed account.
Give detailed information about the addressed account together with balance information
**NOTE:** The account-id can represent a multicurrency account.
In this case the currency code is set to "XXX".
Give detailed information about the addressed account.
Give detailed information about the addressed account together with balance information
""",
""",
json.parse(""""""),
json.parse("""{
"cashAccountType" : { },
@ -1097,12 +1094,12 @@ Give detailed information about the addressed account together with balance info
"/card-accounts/ACCOUNT_ID",
"Reads details about a card account",
s"""${mockedDataText(true)}
Reads details about a card account.
It is assumed that a consent of the PSU to this access is already given
and stored on the ASPSP system. The addressed details of this account depends
then on the stored consent addressed by consentId, respectively the OAuth2
access token.
""",
Reads details about a card account.
It is assumed that a consent of the PSU to this access is already given and stored on the ASPSP system.
The addressed details of this account depends then on the stored consent addressed by consentId,
respectively the OAuth2 access token.
""",
json.parse(""""""),
json.parse("""{
"balances" : "",
@ -1156,7 +1153,7 @@ access token.
}
}
}
resourceDocs += ResourceDoc(
startConsentAuthorisation,
apiVersion,
@ -1165,38 +1162,33 @@ access token.
"/consents/CONSENTID/authorisations",
"Start the authorisation process for a consent",
s"""${mockedDataText(false)}
Create an authorisation sub-resource and start the authorisation process of a consent.
The message might in addition transmit authentication and authorisation related data.
Create an authorisation sub-resource and start the authorisation process of a consent.
The message might in addition transmit authentication and authorisation related data.
his method is iterated n times for a n times SCA authorisation in a corporate context,
each creating an own authorisation sub-endpoint for the corresponding PSU authorising the consent.
The ASPSP might make the usage of this access method unnecessary, since the related authorisation
resource will be automatically created by the ASPSP after the submission of the consent data with the
first POST consents call. The start authorisation process is a process which is needed for creating
a new authorisation or cancellation sub-resource.
This applies in the following scenarios: * The ASPSP has indicated with an 'startAuthorisation' hyperlink
in the preceding 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',
* 'startAuthorisationWithPsuAuthentication'
* 'startAuthorisationWithEncryptedPsuAuthentication'
* '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 preceding 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 executing the cancellation.
* The signing basket needs to be authorised yet.
his method is iterated n times for a n times SCA authorisation in a
corporate context, each creating an own authorisation sub-endpoint for
the corresponding PSU authorising the consent.
The ASPSP might make the usage of this access method unnecessary,
since the related authorisation resource will be automatically created by
the ASPSP after the submission of the consent data with the first POST consents call.
The start authorisation process is a process which is needed for creating a new authorisation
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
uploaded by using the extended forms.
* 'startAuthorisationWithPsuIdentfication',
* 'startAuthorisationWithPsuAuthentication' #TODO
* '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
by using the extended forms as indicated above.
* 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.
""",
""",
json.parse(""""""),
json.parse("""{
"scaStatus": "received",
@ -1244,47 +1236,32 @@ This applies in the following scenarios:
"/consents/CONSENTID/authorisations/AUTHORISATIONID",
"Update PSU Data for consents",
s"""${mockedDataText(false)}
This method update PSU data on the consents resource if needed.
It may authorise a consent within the Embedded SCA Approach where needed.
This method update PSU data on the consents resource if needed. It may authorise a consent within the Embedded
SCA Approach where needed. Independently from the SCA Approach it supports
e.g. the selection of the authentication method and a non-SCA PSU authentication.
This methods updates PSU data on the cancellation authorisation resource if needed.
There are several possible Update PSU Data requests in the context of a consent request if needed,
which depends on the SCA approach: * Redirect SCA Approach: 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
* to add credentials as a first factor authentication data of the PSU and
* to select the authentication method and
* transaction authorisation.
The SCA Approach might depend on the chosen SCA method. For that reason,
the following possible Update PSU Data request can apply to all SCA approaches:
* Select an SCA method in case of several SCA methods are available for the customer. 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.
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.
Maybe in a later version the access path will change.
Independently from the SCA Approach it supports e.g. the selection of
the authentication method and a non-SCA PSU authentication.
This methods updates PSU data on the cancellation authorisation resource if needed.
There are several possible Update PSU Data requests in the context of a consent request if needed,
which depends on the SCA approach:
* Redirect SCA Approach:
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
* to add credentials as a first factor authentication data of the PSU and
* to select the authentication method and
* transaction authorisation.
The SCA Approach might depend on the chosen SCA method.
For that reason, the following possible Update PSU Data request can apply to all SCA approaches:
* Select an SCA method in case of several SCA methods are available for the customer.
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.
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.
Maybe in a later version the access path will change.
""",
""",
json.parse("""{
"access": {"accounts": []},
"recurringIndicator": false,
@ -1292,7 +1269,7 @@ There are the following request types on this access path:
"frequencyPerDay": 4,
"combinedServiceIndicator": false
}"""),
json.parse(""""""""),
json.parse(""""""),
List(UserNotLoggedIn, UnknownError),
Catalogs(notCore, notPSD2, notOBWG),
ApiTag("Account Information Service (AIS)") :: apiTagBerlinGroupM :: Nil

View File

@ -42,9 +42,11 @@ object APIMethods_ConfirmationOfFundsServicePIISApi extends RestHelper {
"POST",
"/funds-confirmations",
"Confirmation of Funds Request",
s"""
Creates a confirmation of funds request at the ASPSP. Checks whether a specific amount is available at point of
time of the request on an account linked to a given tuple card issuer(TPP)/card number, or addressed by IBAN and TPP respectively""",
s""" ${mockedDataText(false)}
Creates a confirmation of funds request at the ASPSP. Checks whether a specific amount is available at point
of time of the request on an account linked to a given tuple card issuer(TPP)/card number, or addressed by
IBAN and TPP respectively. If the related extended services are used a conditional Consent-ID is contained
in the header. This field is contained but commented out in this specification. """,
json.parse(
"""{
"instructedAmount" : {

View File

@ -20,13 +20,15 @@ case class JvalueCaseClass(jvalueToCaseclass: JValue)
object JSONFactory_BERLIN_GROUP_1_3 extends CustomJsonFormats {
trait links
case class Balances(balances: String) extends links
case class Transactions(trasactions: String) extends links
case class ViewAccount(viewAccount: String) extends links
case class AdditionalProp1(additionalProp1: String) extends links
case class AdditionalProp2(additionalProp2: String) extends links
case class AdditionalProp3(additionalProp3: String) extends links
case class LinkHrefJson(
href: String
)
case class CoreAccountLinksJsonV13(
balances: LinkHrefJson //,
// trasactions: LinkHrefJson // These links are only supported, when the corresponding consent has been already granted.
)
case class CoreAccountBalancesJson(
balanceAmount:AmountOfMoneyV13 = AmountOfMoneyV13("EUR","123"),
@ -49,7 +51,7 @@ object JSONFactory_BERLIN_GROUP_1_3 extends CustomJsonFormats {
// usage: String ="PRIV",
// details: String ="",
balances: CoreAccountBalancesJson,
_links: List[links],
_links: CoreAccountLinksJsonV13,
)
case class CoreAccountsJsonV13(accounts: List[CoreAccountJsonV13])
@ -67,14 +69,14 @@ object JSONFactory_BERLIN_GROUP_1_3 extends CustomJsonFormats {
)
case class FromAccount(
iban : String = "FR7612345987650123456789014"
iban : String
)
case class CardBalanceAccount(
maskedPan: String,
)
case class AccountBalancesV13(
account:FromAccount= FromAccount(),
`balances`: List[AccountBalance] = AccountBalance() :: Nil
account:FromAccount,
`balances`: List[AccountBalance]
)
case class TransactionsLinksV13(
account: String
@ -86,18 +88,9 @@ object JSONFactory_BERLIN_GROUP_1_3 extends CustomJsonFormats {
account: LinkHrefJson ,
)
case class ExchangeRateJson(
sourceCurrency: String = "EUR",
rate: String = "string",
unitCurrency: String = "string",
targetCurrency: String = "EUR",
rateDate: String = "string",
rateContract: String = "string"
)
case class CreditorAccountJson(
iban: String,
)
case class TransactionJsonV13(
transactionId: String,
creditorName: String,
@ -200,9 +193,6 @@ object JSONFactory_BERLIN_GROUP_1_3 extends CustomJsonFormats {
_links: ScaStatusJsonV13
)
case class LinkHrefJson(
href: String
)
case class InitiatePaymentResponseLinks(
scaRedirect: LinkHrefJson,
self: LinkHrefJson,
@ -253,14 +243,13 @@ object JSONFactory_BERLIN_GROUP_1_3 extends CustomJsonFormats {
bban = bBan,
currency = x.currency,
name = x.label,
bic = getBicFromBankId(x.bankId.value),
cashAccountType = x.accountType,
product = x.accountType,
balances = balance,
bic = getBicFromBankId(x.bankId.value),
_links = Balances(s"/${OBP_BERLIN_GROUP_1_3.version}/accounts/${x.accountId.value}/balances")
:: Nil
_links = CoreAccountLinksJsonV13(LinkHrefJson(s"/${OBP_BERLIN_GROUP_1_3.version}/accounts/${x.accountId.value}/balances"))
)
}
}
)
}
@ -476,10 +465,10 @@ object JSONFactory_BERLIN_GROUP_1_3 extends CustomJsonFormats {
},
paymentId = paymentId,
_links = InitiatePaymentResponseLinks(
scaRedirect = LinkHrefJson("answer transaction request url"),
scaRedirect = LinkHrefJson(s"$getServerUrl/otp?flow=payment&paymentService=payments&paymentProduct=sepa_credit_transfers&paymentId=$paymentId"),
self = LinkHrefJson(s"/v1.3/payments/sepa-credit-transfers/$paymentId"),
status = LinkHrefJson(s"/v1.3/payments/$paymentId/status"),
scaStatus = LinkHrefJson(s"/v1.3/payments/$paymentId/authorisations/${paymentId}xx")
scaStatus = LinkHrefJson(s"/v1.3/payments/$paymentId/authorisations/${paymentId}")
)
)
}

View File

@ -63,10 +63,10 @@ object OBP_BERLIN_GROUP_1_3 extends OBPRestHelper with MdcLoggable with ScannedA
override val allResourceDocs: ArrayBuffer[ResourceDoc] =
APIMethods_AccountInformationServiceAISApi.resourceDocs ++
APIMethods_ConfirmationOfFundsServicePIISApi.resourceDocs ++
APIMethods_PaymentInitiationServicePISApi.resourceDocs ++
APIMethods_SigningBasketsApi.resourceDocs ++
APIMethods_CommonServicesApi.resourceDocs
APIMethods_ConfirmationOfFundsServicePIISApi.resourceDocs ++
APIMethods_PaymentInitiationServicePISApi.resourceDocs ++
APIMethods_SigningBasketsApi.resourceDocs ++
APIMethods_CommonServicesApi.resourceDocs
private[this] def findResourceDoc(pf: OBPEndpoint): Option[ResourceDoc] = {
allResourceDocs.find(_.partialFunction==pf)

View File

@ -12,6 +12,7 @@ import code.consent.ConsentStatus
import code.database.authorisation.Authorisations
import code.fx.fx
import code.model._
import code.transactionrequests.TransactionRequests.TransactionRequestTypes.{TRANSFER_TO_ACCOUNT, TRANSFER_TO_ATM, TRANSFER_TO_PHONE}
import code.transactionrequests.TransactionRequests.{PaymentServiceTypes, TransactionRequestTypes}
import code.util.Helper
import com.github.dwickern.macros.NameOf.nameOf
@ -61,19 +62,16 @@ object APIMethods_PaymentInitiationServicePISApi extends RestHelper {
"/PAYMENT_SERVICE/PAYMENT_PRODUCT/PAYMENTID",
"Payment Cancellation Request",
s"""${mockedDataText(true)}
This method initiates the cancellation of a payment.
Depending on the payment-service, the payment-product and the ASPSP's implementation,
this TPP call might be sufficient to cancel a payment.
If an authorisation of the payment cancellation is mandated by the ASPSP,
a corresponding hyperlink will be contained in the response message.
This method initiates the cancellation of a payment. Depending on the payment-service, the payment-product
and the ASPSP's implementation, this TPP call might be sufficient to cancel a payment. If an authorisation
of the payment cancellation is mandated by the ASPSP, a corresponding hyperlink will be contained in the
response message. Cancels the addressed payment with resource identification paymentId if applicable to the
payment-service, payment-product and received in product related timelines (e.g. before end of business day
for scheduled payments of the last business day before the scheduled execution day). The response to this
DELETE command will tell the TPP whether the * access method was rejected * access method was successful,
or * access method is generally applicable, but further authorisation processes are needed.
Cancels the addressed payment with resource identification paymentId if applicable to the payment-service, payment-product and received in product related timelines (e.g. before end of business day for scheduled payments of the last business day before the scheduled execution day).
The response to this DELETE command will tell the TPP whether the
* access method was rejected
* access method was successful, or
* access method is generally applicable, but further authorisation processes are needed.
""",
""",
json.parse(""""""),
json.parse("""{
"challengeData" : {
@ -82,7 +80,7 @@ The response to this DELETE command will tell the TPP whether the
"image" : "image",
"imageLink" : "http://example.com/aeiou",
"otpFormat" : "characters",
"data" : "data"
"data" : [ "data", "data" ]
},
"scaMethods" : "",
"_links" : {
@ -523,12 +521,12 @@ $additionalInstructions
},
"creditorName": "70charname"
}"""),
json.parse("""{
json.parse(s"""{
"transactionStatus": "RCVD",
"paymentId": "1234-wertiq-983",
"_links":
{
"scaRedirect": {"href": "answer transaction request url"},
"scaRedirect": {"href": "$getServerUrl/otp?flow=payment&paymentService=payments&paymentProduct=sepa_credit_transfers&paymentId=b0472c21-6cea-4ee0-b036-3e253adb3b0b"},
"self": {"href": "/v1.3/payments/sepa-credit-transfers/1234-wertiq-983"},
"status": {"href": "/v1.3/payments/1234-wertiq-983/status"},
"scaStatus": {"href": "/v1.3/payments/1234-wertiq-983/authorisations/123auth456"}
@ -939,7 +937,7 @@ There are the following request types on this access path:
""",
json.parse("""{"scaAuthenticationData":"12345"}"""),
json.parse("""{
"scaStatus": "received",
"scaStatus": "finalised",
"authorisationId": "88695566-6642-46d5-9985-0d824624f507",
"psuMessage": "Please check your SMS at a mobile device.",
"_links": {
@ -955,7 +953,7 @@ There are the following request types on this access path:
case paymentService :: paymentProduct :: paymentid:: "authorisations" :: authorisationid :: Nil JsonPut json -> _ => {
cc =>
for {
(_, callContext) <- authorizedAccess(cc)
(Full(u), callContext) <- authorizedAccess(cc)
_ <- passesPsd2Pisp(callContext)
failMsg = s"$InvalidJsonFormat The Json body should be the $UpdatePaymentPsuDataJson "
updatePaymentPsuDataJson <- NewStyle.function.tryons(failMsg, 400, callContext) {
@ -971,6 +969,22 @@ There are the following request types on this access path:
authorisation <- Future(Authorisations.authorisationProvider.vend.checkAnswer(paymentid,authorisationid, updatePaymentPsuDataJson.scaAuthenticationData))map {
i => connectorEmptyResponse(i, callContext)
}
//Map obp transacition request id with BerlinGroup PaymentId
transactionRequestId = TransactionRequestId(paymentid)
(existingTransactionRequest, callContext) <- NewStyle.function.getTransactionRequestImpl(transactionRequestId, callContext)
(fromAccount, callContext) <- NewStyle.function.checkBankAccountExists(
BankId(existingTransactionRequest.from.bank_id),
AccountId(existingTransactionRequest.from.account_id),
callContext
)
_ <- if(authorisation.scaStatus =="finalised")
NewStyle.function.createTransactionAfterChallengeV210(fromAccount, existingTransactionRequest, callContext)
else //If it is not `finalised`, just return the `authorisation` back, without any payments
Future{true}
} yield {
(JSONFactory_BERLIN_GROUP_1_3.createStartPaymentAuthorisationJson(authorisation), callContext)
}

View File

@ -63,7 +63,7 @@ The resource identifications of these transactions are contained in the payload
"image" : "image",
"imageLink" : "http://example.com/aeiou",
"otpFormat" : "characters",
"data" : "data"
"data" : [ "data", "data" ]
},
"scaMethods" : "",
"tppMessages" : [ {

View File

@ -1,76 +0,0 @@
package code.api.berlin.group.v1_3_1
import code.api.APIFailureNewStyle
import code.api.berlin.group.v1_3.JvalueCaseClass
import net.liftweb.json
import net.liftweb.json._
import code.api.util.APIUtil.{defaultBankId, _}
import code.api.util.{ApiVersion, NewStyle}
import code.api.util.ErrorMessages._
import code.api.util.ApiTag._
import code.api.util.NewStyle.HttpCode
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 com.github.dwickern.macros.NameOf.nameOf
import scala.collection.immutable.Nil
import scala.collection.mutable.ArrayBuffer
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
import code.api.berlin.group.v1_3_1.JSONFactory_BERLIN_GROUP_1_3_3
import code.api.util.ApiTag
object APIMethods_ConfirmationOfFundsServicePIISApi extends RestHelper {
val apiVersion = JSONFactory_BERLIN_GROUP_1_3_3.apiVersion
val resourceDocs = ArrayBuffer[ResourceDoc]()
val apiRelations = ArrayBuffer[ApiRelation]()
protected implicit def JvalueToSuper(what: JValue): JvalueCaseClass = JvalueCaseClass(what)
val endpoints =
checkAvailabilityOfFunds ::
Nil
resourceDocs += ResourceDoc(
checkAvailabilityOfFunds,
apiVersion,
nameOf(checkAvailabilityOfFunds),
"POST",
"/funds-confirmations",
"Confirmation of Funds Request",
s"""${mockedDataText(true)}
Creates a confirmation of funds request at the ASPSP. Checks whether a specific amount is available at point
of time of the request on an account linked to a given tuple card issuer(TPP)/card number, or addressed by
IBAN and TPP respectively. If the related extended services are used a conditional Consent-ID is contained
in the header. This field is contained but commented out in this specification.
""",
json.parse(""""""),
json.parse("""{
"fundsAvailable" : { }
}"""),
List(UserNotLoggedIn, UnknownError),
Catalogs(notCore, notPSD2, notOBWG),
ApiTag("Confirmation of Funds Service (PIIS)") :: apiTagMockedData :: Nil
)
lazy val checkAvailabilityOfFunds : OBPEndpoint = {
case "funds-confirmations" :: Nil JsonPost _ => {
cc =>
for {
(Full(u), callContext) <- authorizedAccess(cc, UserNotLoggedIn)
} yield {
(json.parse("""{
"fundsAvailable" : { }
}"""), callContext)
}
}
}
}

View File

@ -1,85 +0,0 @@
/**
* Open Bank Project - API
* Copyright (C) 2011-2018, TESOBE Ltd
**
*This program is free software: you can redistribute it and/or modify
*it under the terms of the GNU Affero General Public License as published by
*the Free Software Foundation, either version 3 of the License, or
*(at your option) any later version.
**
*This program is distributed in the hope that it will be useful,
*but WITHOUT ANY WARRANTY; without even the implied warranty of
*MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
*GNU Affero General Public License for more details.
**
*You should have received a copy of the GNU Affero General Public License
*along with this program. If not, see <http://www.gnu.org/licenses/>.
**
*Email: contact@tesobe.com
*TESOBE Ltd
*Osloerstrasse 16/17
*Berlin 13359, Germany
**
*This product includes software developed at
*TESOBE (http://www.tesobe.com/)
* by
*Simon Redfern : simon AT tesobe DOT com
*Stefan Bethge : stefan AT tesobe DOT com
*Everett Sochowski : everett AT tesobe DOT com
*Ayoub Benali: ayoub AT tesobe DOT com
*
*/
package code.api.berlin.group.v1_3_1
import code.api.OBPRestHelper
import code.api.util.APIUtil.{OBPEndpoint, ResourceDoc, getAllowedEndpoints}
import code.api.util.{ScannedApiVersion, ScannedApis}
import code.util.Helper.MdcLoggable
//import code.api.berlin.group.v1_3_1.APIMethods_AccountInformationServiceAISApi
//import code.api.berlin.group.v1_3_1.APIMethods_CommonServicesApi
import code.api.berlin.group.v1_3_1.APIMethods_ConfirmationOfFundsServicePIISApi
//import code.api.berlin.group.v1_3_1.APIMethods_PaymentInitiationServicePISApi
//import code.api.berlin.group.v1_3_1.APIMethods_SigningBasketsSBSApi
import scala.collection.mutable.ArrayBuffer
/*
This file defines which endpoints from all the versions are available in v1
*/
object JSONFactory_BERLIN_GROUP_1_3_3 extends OBPRestHelper with MdcLoggable with ScannedApis {
//please modify these three parameter if it is not correct.
override val apiVersion = ScannedApiVersion("berlin-group", "BG", "v1.3.1")
val versionStatus = "DRAFT"
private[this] val endpoints =
APIMethods_AccountInformationServiceAISApi.endpoints ++
APIMethods_CommonServicesApi.endpoints ++
APIMethods_ConfirmationOfFundsServicePIISApi.endpoints ++
APIMethods_PaymentInitiationServicePISApi.endpoints ++
APIMethods_SigningBasketsSBSApi.endpoints
override val allResourceDocs: ArrayBuffer[ResourceDoc] =
APIMethods_AccountInformationServiceAISApi.resourceDocs ++
APIMethods_CommonServicesApi.resourceDocs ++
APIMethods_ConfirmationOfFundsServicePIISApi.resourceDocs ++
APIMethods_PaymentInitiationServicePISApi.resourceDocs ++
APIMethods_SigningBasketsSBSApi.resourceDocs
private[this] def findResourceDoc(pf: OBPEndpoint): Option[ResourceDoc] = {
allResourceDocs.find(_.partialFunction==pf)
}
// Filter the possible endpoints by the disabled / enabled Props settings and add them together
override val routes : List[OBPEndpoint] = getAllowedEndpoints(endpoints, allResourceDocs)
// Make them available for use!
routes.foreach(route => {
oauthServe((apiVersion.urlPrefix / version.vDottedApiVersion()).oPrefix{route}, findResourceDoc(route))
})
logger.info(s"version $version has been run! There are ${routes.length} routes.")
}

View File

@ -1,828 +0,0 @@
package code.api.berlin.group.v1_3_1
import code.api.APIFailureNewStyle
import code.api.berlin.group.v1_3.JvalueCaseClass
import net.liftweb.json
import net.liftweb.json._
import code.api.util.APIUtil.{defaultBankId, _}
import code.api.util.{ApiVersion, NewStyle}
import code.api.util.ErrorMessages._
import code.api.util.ApiTag._
import code.api.util.NewStyle.HttpCode
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 com.github.dwickern.macros.NameOf.nameOf
import scala.collection.immutable.Nil
import scala.collection.mutable.ArrayBuffer
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
import code.api.berlin.group.v1_3_1.JSONFactory_BERLIN_GROUP_1_3_3
import code.api.util.ApiTag
object APIMethods_PaymentInitiationServicePISApi extends RestHelper {
val apiVersion = JSONFactory_BERLIN_GROUP_1_3_3.apiVersion
val resourceDocs = ArrayBuffer[ResourceDoc]()
val apiRelations = ArrayBuffer[ApiRelation]()
protected implicit def JvalueToSuper(what: JValue): JvalueCaseClass = JvalueCaseClass(what)
val endpoints =
cancelPayment ::
getPaymentCancellationScaStatus ::
getPaymentInformation ::
getPaymentInitiationAuthorisation ::
getPaymentInitiationCancellationAuthorisationInformation ::
getPaymentInitiationScaStatus ::
getPaymentInitiationStatus ::
initiatePayment ::
startPaymentAuthorisation ::
startPaymentInitiationCancellationAuthorisation ::
updatePaymentCancellationPsuData ::
updatePaymentPsuData ::
Nil
resourceDocs += ResourceDoc(
cancelPayment,
apiVersion,
nameOf(cancelPayment),
"DELETE",
"/PAYMENT_SERVICE/PAYMENT_PRODUCT/PAYMENTID",
"Payment Cancellation Request",
s"""${mockedDataText(true)}
This method initiates the cancellation of a payment. Depending on the payment-service, the payment-product
and the ASPSP's implementation, this TPP call might be sufficient to cancel a payment. If an authorisation
of the payment cancellation is mandated by the ASPSP, a corresponding hyperlink will be contained in the
response message. Cancels the addressed payment with resource identification paymentId if applicable to the
payment-service, payment-product and received in product related timelines (e.g. before end of business day
for scheduled payments of the last business day before the scheduled execution day). The response to this
DELETE command will tell the TPP whether the * access method was rejected * access method was successful,
or * access method is generally applicable, but further authorisation processes are needed.
""",
json.parse(""""""),
json.parse("""{
"challengeData" : {
"otpMaxLength" : 0,
"additionalInformation" : "additionalInformation",
"image" : "image",
"imageLink" : "http://example.com/aeiou",
"otpFormat" : "characters",
"data" : [ "data", "data" ]
},
"scaMethods" : "",
"_links" : {
"startAuthorisationWithEncryptedPsuAuthentication" : {
"href" : "/v1/payments/sepa-credit-transfers/1234-wertiq-983"
},
"startAuthorisationWithAuthenticationMethodSelection" : {
"href" : "/v1/payments/sepa-credit-transfers/1234-wertiq-983"
},
"startAuthorisationWithPsuAuthentication" : {
"href" : "/v1/payments/sepa-credit-transfers/1234-wertiq-983"
},
"startAuthorisationWithPsuIdentification" : {
"href" : "/v1/payments/sepa-credit-transfers/1234-wertiq-983"
},
"startAuthorisation" : {
"href" : "/v1/payments/sepa-credit-transfers/1234-wertiq-983"
}
},
"chosenScaMethod" : "",
"transactionStatus" : "ACCP"
}"""),
List(UserNotLoggedIn, UnknownError),
Catalogs(notCore, notPSD2, notOBWG),
ApiTag("Payment Initiation Service (PIS)") :: apiTagMockedData :: Nil
)
lazy val cancelPayment : OBPEndpoint = {
case payment_service :: payment_product :: paymentid :: Nil JsonDelete _ => {
cc =>
for {
(Full(u), callContext) <- authorizedAccess(cc, UserNotLoggedIn)
} yield {
(json.parse("""{
"challengeData" : {
"otpMaxLength" : 0,
"additionalInformation" : "additionalInformation",
"image" : "image",
"imageLink" : "http://example.com/aeiou",
"otpFormat" : "characters",
"data" : [ "data", "data" ]
},
"scaMethods" : "",
"_links" : {
"startAuthorisationWithEncryptedPsuAuthentication" : {
"href" : "/v1/payments/sepa-credit-transfers/1234-wertiq-983"
},
"startAuthorisationWithAuthenticationMethodSelection" : {
"href" : "/v1/payments/sepa-credit-transfers/1234-wertiq-983"
},
"startAuthorisationWithPsuAuthentication" : {
"href" : "/v1/payments/sepa-credit-transfers/1234-wertiq-983"
},
"startAuthorisationWithPsuIdentification" : {
"href" : "/v1/payments/sepa-credit-transfers/1234-wertiq-983"
},
"startAuthorisation" : {
"href" : "/v1/payments/sepa-credit-transfers/1234-wertiq-983"
}
},
"chosenScaMethod" : "",
"transactionStatus" : "ACCP"
}"""), callContext)
}
}
}
resourceDocs += ResourceDoc(
getPaymentCancellationScaStatus,
apiVersion,
nameOf(getPaymentCancellationScaStatus),
"GET",
"/PAYMENT_SERVICE/PAYMENT_PRODUCT/PAYMENTID/cancellation-authorisations/CANCELLATIONID",
"Read the SCA status of the payment cancellation's authorisation.",
s"""${mockedDataText(true)}
This method returns the SCA status of a payment initiation's authorisation sub-resource.
""",
json.parse(""""""),
json.parse("""{
"scaStatus" : "psuAuthenticated"
}"""),
List(UserNotLoggedIn, UnknownError),
Catalogs(notCore, notPSD2, notOBWG),
ApiTag("Payment Initiation Service (PIS)") ::ApiTag("Common Services") :: apiTagMockedData :: Nil
)
lazy val getPaymentCancellationScaStatus : OBPEndpoint = {
case payment_service :: payment_product :: paymentid:: "cancellation-authorisations" :: cancellationid :: Nil JsonGet _ => {
cc =>
for {
(Full(u), callContext) <- authorizedAccess(cc, UserNotLoggedIn)
} yield {
(json.parse("""{
"scaStatus" : "psuAuthenticated"
}"""), callContext)
}
}
}
resourceDocs += ResourceDoc(
getPaymentInformation,
apiVersion,
nameOf(getPaymentInformation),
"GET",
"/PAYMENT_SERVICE/PAYMENT_PRODUCT/PAYMENTID",
"Get Payment Information",
s"""${mockedDataText(true)}
Returns the content of a payment object
""",
json.parse(""""""),
json.parse(""""""""),
List(UserNotLoggedIn, UnknownError),
Catalogs(notCore, notPSD2, notOBWG),
ApiTag("Payment Initiation Service (PIS)") :: apiTagMockedData :: Nil
)
lazy val getPaymentInformation : OBPEndpoint = {
case payment_service :: payment_product :: paymentid :: Nil JsonGet _ => {
cc =>
for {
(Full(u), callContext) <- authorizedAccess(cc, UserNotLoggedIn)
} yield {
(json.parse(""""""""), callContext)
}
}
}
resourceDocs += ResourceDoc(
getPaymentInitiationAuthorisation,
apiVersion,
nameOf(getPaymentInitiationAuthorisation),
"GET",
"/PAYMENT_SERVICE/PAYMENT_PRODUCT/PAYMENTID/authorisations",
"Get Payment Initiation Authorisation Sub-Resources Request",
s"""${mockedDataText(true)}
Read a list of all authorisation subresources IDs which have been created. This function returns an array
of hyperlinks to all generated authorisation sub-resources.
""",
json.parse(""""""),
json.parse("""{
"authorisationIds" : ""
}"""),
List(UserNotLoggedIn, UnknownError),
Catalogs(notCore, notPSD2, notOBWG),
ApiTag("Payment Initiation Service (PIS)") ::ApiTag("Common Services") :: apiTagMockedData :: Nil
)
lazy val getPaymentInitiationAuthorisation : OBPEndpoint = {
case payment_service :: payment_product :: paymentid:: "authorisations" :: Nil JsonGet _ => {
cc =>
for {
(Full(u), callContext) <- authorizedAccess(cc, UserNotLoggedIn)
} yield {
(json.parse("""{
"authorisationIds" : ""
}"""), callContext)
}
}
}
resourceDocs += ResourceDoc(
getPaymentInitiationCancellationAuthorisationInformation,
apiVersion,
nameOf(getPaymentInitiationCancellationAuthorisationInformation),
"GET",
"/PAYMENT_SERVICE/PAYMENT_PRODUCT/PAYMENTID/cancellation-authorisations",
"Will deliver an array of resource identifications to all generated cancellation authorisation sub-resources.",
s"""${mockedDataText(true)}
Retrieve a list of all created cancellation authorisation sub-resources.
""",
json.parse(""""""),
json.parse(""""""""),
List(UserNotLoggedIn, UnknownError),
Catalogs(notCore, notPSD2, notOBWG),
ApiTag("Payment Initiation Service (PIS)") :: apiTagMockedData :: Nil
)
lazy val getPaymentInitiationCancellationAuthorisationInformation : OBPEndpoint = {
case payment_service :: payment_product :: paymentid:: "cancellation-authorisations" :: Nil JsonGet _ => {
cc =>
for {
(Full(u), callContext) <- authorizedAccess(cc, UserNotLoggedIn)
} yield {
(json.parse(""""""""), callContext)
}
}
}
resourceDocs += ResourceDoc(
getPaymentInitiationScaStatus,
apiVersion,
nameOf(getPaymentInitiationScaStatus),
"GET",
"/PAYMENT_SERVICE/PAYMENT_PRODUCT/PAYMENTID/authorisations/AUTHORISATIONID",
"Read the SCA Status of the payment authorisation",
s"""${mockedDataText(true)}
This method returns the SCA status of a payment initiation's authorisation sub-resource.
""",
json.parse(""""""),
json.parse("""{
"scaStatus" : "psuAuthenticated"
}"""),
List(UserNotLoggedIn, UnknownError),
Catalogs(notCore, notPSD2, notOBWG),
ApiTag("Payment Initiation Service (PIS)") ::ApiTag("Common Services") :: apiTagMockedData :: Nil
)
lazy val getPaymentInitiationScaStatus : OBPEndpoint = {
case payment_service :: payment_product :: paymentid:: "authorisations" :: authorisationid :: Nil JsonGet _ => {
cc =>
for {
(Full(u), callContext) <- authorizedAccess(cc, UserNotLoggedIn)
} yield {
(json.parse("""{
"scaStatus" : "psuAuthenticated"
}"""), callContext)
}
}
}
resourceDocs += ResourceDoc(
getPaymentInitiationStatus,
apiVersion,
nameOf(getPaymentInitiationStatus),
"GET",
"/PAYMENT_SERVICE/PAYMENT_PRODUCT/PAYMENTID/status",
"Payment initiation status request",
s"""${mockedDataText(true)}
Check the transaction status of a payment initiation.
""",
json.parse(""""""),
json.parse("""{
"transactionStatus" : "ACCP",
"fundsAvailable" : { }
}"""),
List(UserNotLoggedIn, UnknownError),
Catalogs(notCore, notPSD2, notOBWG),
ApiTag("Payment Initiation Service (PIS)") :: apiTagMockedData :: Nil
)
lazy val getPaymentInitiationStatus : OBPEndpoint = {
case payment_service :: payment_product :: paymentid:: "status" :: Nil JsonGet _ => {
cc =>
for {
(Full(u), callContext) <- authorizedAccess(cc, UserNotLoggedIn)
} yield {
(json.parse("""{
"transactionStatus" : "ACCP",
"fundsAvailable" : { }
}"""), callContext)
}
}
}
resourceDocs += ResourceDoc(
initiatePayment,
apiVersion,
nameOf(initiatePayment),
"POST",
"/PAYMENT_SERVICE/PAYMENT_PRODUCT",
"Payment initiation request",
s"""${mockedDataText(true)}
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***
- ***target-2-payments***
- ***cross-border-credit-transfers***
- Payment products with payment information in *pain.001* XML format:
- ***pain.001-sepa-credit-transfers***
- ***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. 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. In these cases, first an authorisation sub-resource has to be
generated following the 'startAuthorisation' link.
""",
json.parse(""""""),
json.parse("""{
"challengeData" : {
"otpMaxLength" : 0,
"additionalInformation" : "additionalInformation",
"image" : "image",
"imageLink" : "http://example.com/aeiou",
"otpFormat" : "characters",
"data" : [ "data", "data" ]
},
"scaMethods" : "",
"tppMessages" : [ {
"path" : "path",
"code" : { },
"text" : { },
"category" : { }
}, {
"path" : "path",
"code" : { },
"text" : { },
"category" : { }
} ],
"_links" : {
"scaRedirect" : {
"href" : "https://www.testbank.com/asdfasdfasdf"
},
"self" : {
"href" : "/v1/payments/sepa-credit-transfers/1234-wertiq-983"
}
},
"chosenScaMethod" : "",
"transactionStatus" : "ACCP",
"paymentId" : "1234-wertiq-983",
"psuMessage" : { },
"transactionFeeIndicator" : { },
"transactionFees" : {
"amount" : "123",
"currency" : "EUR"
}
}"""),
List(UserNotLoggedIn, UnknownError),
Catalogs(notCore, notPSD2, notOBWG),
ApiTag("Payment Initiation Service (PIS)") :: apiTagMockedData :: Nil
)
lazy val initiatePayment : OBPEndpoint = {
case payment_service :: payment_product :: Nil JsonPost _ => {
cc =>
for {
(Full(u), callContext) <- authorizedAccess(cc, UserNotLoggedIn)
} yield {
(json.parse("""{
"challengeData" : {
"otpMaxLength" : 0,
"additionalInformation" : "additionalInformation",
"image" : "image",
"imageLink" : "http://example.com/aeiou",
"otpFormat" : "characters",
"data" : [ "data", "data" ]
},
"scaMethods" : "",
"tppMessages" : [ {
"path" : "path",
"code" : { },
"text" : { },
"category" : { }
}, {
"path" : "path",
"code" : { },
"text" : { },
"category" : { }
} ],
"_links" : {
"scaRedirect" : {
"href" : "https://www.testbank.com/asdfasdfasdf"
},
"self" : {
"href" : "/v1/payments/sepa-credit-transfers/1234-wertiq-983"
}
},
"chosenScaMethod" : "",
"transactionStatus" : "ACCP",
"paymentId" : "1234-wertiq-983",
"psuMessage" : { },
"transactionFeeIndicator" : { },
"transactionFees" : {
"amount" : "123",
"currency" : "EUR"
}
}"""), callContext)
}
}
}
resourceDocs += ResourceDoc(
startPaymentAuthorisation,
apiVersion,
nameOf(startPaymentAuthorisation),
"POST",
"/PAYMENT_SERVICE/PAYMENT_PRODUCT/PAYMENTID/authorisations",
"Start the authorisation process for a payment initiation",
s"""${mockedDataText(true)}
Create an authorisation sub-resource and start the authorisation process. The message might in addition
transmit authentication and authorisation related data. This method is iterated n times for a n times
SCA authorisation in a corporate context, each creating an own authorisation sub-endpoint for the corresponding
PSU authorising the transaction. The ASPSP might make the usage of this access method unnecessary in case of
only one SCA process needed, since the related authorisation resource might be automatically created by the
ASPSP after the submission of the payment data with the first POST payments/{payment-product} call.
The start authorisation process is a process which is needed for creating a new authorisation or cancellation
sub-resource. This applies in the following scenarios: * The ASPSP has indicated with an 'startAuthorisation'
hyperlink in the preceding 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',
* 'startAuthorisationWithPsuAuthentication'
* 'startAuthorisationWithEncryptedPsuAuthentication'
* '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 preceding
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 executing the cancellation. * The signing basket needs
to be authorised yet.
""",
json.parse(""""""),
json.parse("""{
"authorisationId" : "123auth456",
"challengeData" : {
"otpMaxLength" : 0,
"additionalInformation" : "additionalInformation",
"image" : "image",
"imageLink" : "http://example.com/aeiou",
"otpFormat" : "characters",
"data" : [ "data", "data" ]
},
"scaMethods" : "",
"scaStatus" : "psuAuthenticated",
"_links" : {
"scaStatus" : {
"href" : "/v1/payments/sepa-credit-transfers/1234-wertiq-983"
},
"startAuthorisationWithEncryptedPsuAuthentication" : {
"href" : "/v1/payments/sepa-credit-transfers/1234-wertiq-983"
},
"scaRedirect" : {
"href" : "/v1/payments/sepa-credit-transfers/1234-wertiq-983"
},
"selectAuthenticationMethod" : {
"href" : "/v1/payments/sepa-credit-transfers/1234-wertiq-983"
},
"startAuthorisationWithPsuAuthentication" : {
"href" : "/v1/payments/sepa-credit-transfers/1234-wertiq-983"
},
"authoriseTransaction" : {
"href" : "/v1/payments/sepa-credit-transfers/1234-wertiq-983"
},
"scaOAuth" : {
"href" : "/v1/payments/sepa-credit-transfers/1234-wertiq-983"
},
"updatePsuIdentification" : {
"href" : "/v1/payments/sepa-credit-transfers/1234-wertiq-983"
}
},
"chosenScaMethod" : "",
"psuMessage" : { }
}"""),
List(UserNotLoggedIn, UnknownError),
Catalogs(notCore, notPSD2, notOBWG),
ApiTag("Payment Initiation Service (PIS)") ::ApiTag("Common Services") :: apiTagMockedData :: Nil
)
lazy val startPaymentAuthorisation : OBPEndpoint = {
case payment_service :: payment_product :: paymentid:: "authorisations" :: Nil JsonPost _ => {
cc =>
for {
(Full(u), callContext) <- authorizedAccess(cc, UserNotLoggedIn)
} yield {
(json.parse("""{
"authorisationId" : "123auth456",
"challengeData" : {
"otpMaxLength" : 0,
"additionalInformation" : "additionalInformation",
"image" : "image",
"imageLink" : "http://example.com/aeiou",
"otpFormat" : "characters",
"data" : [ "data", "data" ]
},
"scaMethods" : "",
"scaStatus" : "psuAuthenticated",
"_links" : {
"scaStatus" : {
"href" : "/v1/payments/sepa-credit-transfers/1234-wertiq-983"
},
"startAuthorisationWithEncryptedPsuAuthentication" : {
"href" : "/v1/payments/sepa-credit-transfers/1234-wertiq-983"
},
"scaRedirect" : {
"href" : "/v1/payments/sepa-credit-transfers/1234-wertiq-983"
},
"selectAuthenticationMethod" : {
"href" : "/v1/payments/sepa-credit-transfers/1234-wertiq-983"
},
"startAuthorisationWithPsuAuthentication" : {
"href" : "/v1/payments/sepa-credit-transfers/1234-wertiq-983"
},
"authoriseTransaction" : {
"href" : "/v1/payments/sepa-credit-transfers/1234-wertiq-983"
},
"scaOAuth" : {
"href" : "/v1/payments/sepa-credit-transfers/1234-wertiq-983"
},
"updatePsuIdentification" : {
"href" : "/v1/payments/sepa-credit-transfers/1234-wertiq-983"
}
},
"chosenScaMethod" : "",
"psuMessage" : { }
}"""), callContext)
}
}
}
resourceDocs += ResourceDoc(
startPaymentInitiationCancellationAuthorisation,
apiVersion,
nameOf(startPaymentInitiationCancellationAuthorisation),
"POST",
"/PAYMENT_SERVICE/PAYMENT_PRODUCT/PAYMENTID/cancellation-authorisations",
"Start the authorisation process for the cancellation of the addressed payment",
s"""${mockedDataText(true)}
Creates an authorisation sub-resource and start the authorisation process of the cancellation of the addressed
payment. The message might in addition transmit authentication and authorisation related data. This method is
iterated n times for a n times SCA authorisation in a corporate context, each creating an own authorisation
sub-endpoint for the corresponding PSU authorising the cancellation-authorisation. The ASPSP might make the
usage of this access method unnecessary in case of only one SCA process needed, since the related authorisation
resource might be automatically created by the ASPSP after the submission of the payment data with the first
POST payments/{payment-product} call. The start authorisation process is a process which is needed for
creating a new authorisation or cancellation sub-resource. This applies in the following scenarios:
* The ASPSP has indicated with an 'startAuthorisation' hyperlink in the preceding 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',
* 'startAuthorisationWithPsuAuthentication' * '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 preceding 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 executing the cancellation. * The signing basket needs to be authorised yet.
""",
json.parse(""""""),
json.parse("""{
"authorisationId" : "123auth456",
"challengeData" : {
"otpMaxLength" : 0,
"additionalInformation" : "additionalInformation",
"image" : "image",
"imageLink" : "http://example.com/aeiou",
"otpFormat" : "characters",
"data" : [ "data", "data" ]
},
"scaMethods" : "",
"scaStatus" : "psuAuthenticated",
"_links" : {
"scaStatus" : {
"href" : "/v1/payments/sepa-credit-transfers/1234-wertiq-983"
},
"startAuthorisationWithEncryptedPsuAuthentication" : {
"href" : "/v1/payments/sepa-credit-transfers/1234-wertiq-983"
},
"scaRedirect" : {
"href" : "/v1/payments/sepa-credit-transfers/1234-wertiq-983"
},
"selectAuthenticationMethod" : {
"href" : "/v1/payments/sepa-credit-transfers/1234-wertiq-983"
},
"startAuthorisationWithPsuAuthentication" : {
"href" : "/v1/payments/sepa-credit-transfers/1234-wertiq-983"
},
"authoriseTransaction" : {
"href" : "/v1/payments/sepa-credit-transfers/1234-wertiq-983"
},
"scaOAuth" : {
"href" : "/v1/payments/sepa-credit-transfers/1234-wertiq-983"
},
"updatePsuIdentification" : {
"href" : "/v1/payments/sepa-credit-transfers/1234-wertiq-983"
}
},
"chosenScaMethod" : "",
"psuMessage" : { }
}"""),
List(UserNotLoggedIn, UnknownError),
Catalogs(notCore, notPSD2, notOBWG),
ApiTag("Payment Initiation Service (PIS)") ::ApiTag("Common Services") :: apiTagMockedData :: Nil
)
lazy val startPaymentInitiationCancellationAuthorisation : OBPEndpoint = {
case payment_service :: payment_product :: paymentid:: "cancellation-authorisations" :: Nil JsonPost _ => {
cc =>
for {
(Full(u), callContext) <- authorizedAccess(cc, UserNotLoggedIn)
} yield {
(json.parse("""{
"authorisationId" : "123auth456",
"challengeData" : {
"otpMaxLength" : 0,
"additionalInformation" : "additionalInformation",
"image" : "image",
"imageLink" : "http://example.com/aeiou",
"otpFormat" : "characters",
"data" : [ "data", "data" ]
},
"scaMethods" : "",
"scaStatus" : "psuAuthenticated",
"_links" : {
"scaStatus" : {
"href" : "/v1/payments/sepa-credit-transfers/1234-wertiq-983"
},
"startAuthorisationWithEncryptedPsuAuthentication" : {
"href" : "/v1/payments/sepa-credit-transfers/1234-wertiq-983"
},
"scaRedirect" : {
"href" : "/v1/payments/sepa-credit-transfers/1234-wertiq-983"
},
"selectAuthenticationMethod" : {
"href" : "/v1/payments/sepa-credit-transfers/1234-wertiq-983"
},
"startAuthorisationWithPsuAuthentication" : {
"href" : "/v1/payments/sepa-credit-transfers/1234-wertiq-983"
},
"authoriseTransaction" : {
"href" : "/v1/payments/sepa-credit-transfers/1234-wertiq-983"
},
"scaOAuth" : {
"href" : "/v1/payments/sepa-credit-transfers/1234-wertiq-983"
},
"updatePsuIdentification" : {
"href" : "/v1/payments/sepa-credit-transfers/1234-wertiq-983"
}
},
"chosenScaMethod" : "",
"psuMessage" : { }
}"""), callContext)
}
}
}
resourceDocs += ResourceDoc(
updatePaymentCancellationPsuData,
apiVersion,
nameOf(updatePaymentCancellationPsuData),
"PUT",
"/PAYMENT_SERVICE/PAYMENT_PRODUCT/PAYMENTID/cancellation-authorisations/CANCELLATIONID",
"Update PSU Data for payment initiation cancellation",
s"""${mockedDataText(true)}
This method updates PSU data on the cancellation authorisation resource if needed. It may authorise a cancellation
of the payment within the Embedded SCA Approach where needed. Independently from the SCA Approach it supports
e.g. the selection of the authentication method and a non-SCA PSU authentication. This methods updates PSU data
on the cancellation authorisation resource if needed. There are several possible Update PSU Data requests in
the context of a cancellation authorisation within the payment initiation services needed, which depends on
the SCA approach: * Redirect SCA Approach: 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
* to add credentials as a first factor authentication data of the PSU and * to select the authentication method and
* transaction authorisation. The SCA Approach might depend on the chosen SCA method. For that reason, the following
possible Update PSU Data request can apply to all SCA approaches: * Select an SCA method in case of several SCA methods
are available for the customer. 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. 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.
Maybe in a later version the access path will change.
""",
json.parse(""""""),
json.parse(""""""""),
List(UserNotLoggedIn, UnknownError),
Catalogs(notCore, notPSD2, notOBWG),
ApiTag("Payment Initiation Service (PIS)") ::ApiTag("Common Services") :: apiTagMockedData :: Nil
)
lazy val updatePaymentCancellationPsuData : OBPEndpoint = {
case payment_service :: payment_product :: paymentid:: "cancellation-authorisations" :: cancellationid :: Nil JsonPut _ => {
cc =>
for {
(Full(u), callContext) <- authorizedAccess(cc, UserNotLoggedIn)
} yield {
(json.parse(""""""""), callContext)
}
}
}
resourceDocs += ResourceDoc(
updatePaymentPsuData,
apiVersion,
nameOf(updatePaymentPsuData),
"PUT",
"/PAYMENT_SERVICE/PAYMENT_PRODUCT/PAYMENTID/authorisations/AUTHORISATIONID",
"Update PSU data for payment initiation",
s"""${mockedDataText(true)}
This methods updates PSU data on the authorisation resource if needed. It may authorise a payment within the
Embedded SCA Approach where needed. Independently from the SCA Approach it supports e.g. the selection of
the authentication method and a non-SCA PSU authentication. There are several possible Update PSU Data requests
in the context of payment initiation services needed, which depends on the SCA approach:
* Redirect SCA Approach: 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 * to add credentials as a first factor authentication data of the PSU and
* to select the authentication method and * transaction authorisation.
The SCA Approach might depend on the chosen SCA method. For that reason, the following possible Update
PSU Data request can apply to all SCA approaches: * Select an SCA method in case of several SCA methods
are available for the customer. 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. 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. Maybe in a later version the access path will change.
""",
json.parse(""""""),
json.parse(""""""""),
List(UserNotLoggedIn, UnknownError),
Catalogs(notCore, notPSD2, notOBWG),
ApiTag("Payment Initiation Service (PIS)") ::ApiTag("Common Services") :: apiTagMockedData :: Nil
)
lazy val updatePaymentPsuData : OBPEndpoint = {
case payment_service :: payment_product :: paymentid:: "authorisations" :: authorisationid :: Nil JsonPut _ => {
cc =>
for {
(Full(u), callContext) <- authorizedAccess(cc, UserNotLoggedIn)
} yield {
(json.parse(""""""""), callContext)
}
}
}
}

View File

@ -1,614 +0,0 @@
package code.api.berlin.group.v1_3_1
import code.api.APIFailureNewStyle
import code.api.berlin.group.v1_3.JvalueCaseClass
import net.liftweb.json
import net.liftweb.json._
import code.api.util.APIUtil.{defaultBankId, _}
import code.api.util.{ApiVersion, NewStyle}
import code.api.util.ErrorMessages._
import code.api.util.ApiTag._
import code.api.util.NewStyle.HttpCode
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 com.github.dwickern.macros.NameOf.nameOf
import scala.collection.immutable.Nil
import scala.collection.mutable.ArrayBuffer
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
import code.api.berlin.group.v1_3_1.JSONFactory_BERLIN_GROUP_1_3_3
import code.api.util.ApiTag
object APIMethods_SigningBasketsSBSApi extends RestHelper {
val apiVersion = JSONFactory_BERLIN_GROUP_1_3_3.apiVersion
val resourceDocs = ArrayBuffer[ResourceDoc]()
val apiRelations = ArrayBuffer[ApiRelation]()
protected implicit def JvalueToSuper(what: JValue): JvalueCaseClass = JvalueCaseClass(what)
val endpoints =
createSigningBasket ::
deleteSigningBasket ::
getSigningBasket ::
getSigningBasketAuthorisation ::
getSigningBasketScaStatus ::
getSigningBasketStatus ::
startSigningBasketAuthorisation ::
updateSigningBasketPsuData ::
Nil
resourceDocs += ResourceDoc(
createSigningBasket,
apiVersion,
nameOf(createSigningBasket),
"POST",
"/signing-baskets",
"Create a signing basket resource",
s"""${mockedDataText(true)}
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(""""""),
json.parse("""{
"basketId" : "1234-basket-567",
"challengeData" : {
"otpMaxLength" : 0,
"additionalInformation" : "additionalInformation",
"image" : "image",
"imageLink" : "http://example.com/aeiou",
"otpFormat" : "characters",
"data" : [ "data", "data" ]
},
"scaMethods" : "",
"tppMessages" : [ {
"path" : "path",
"code" : { },
"text" : { },
"category" : { }
}, {
"path" : "path",
"code" : { },
"text" : { },
"category" : { }
} ],
"_links" : {
"scaStatus" : {
"href" : "/v1/payments/sepa-credit-transfers/1234-wertiq-983"
},
"startAuthorisationWithEncryptedPsuAuthentication" : {
"href" : "/v1/payments/sepa-credit-transfers/1234-wertiq-983"
},
"scaRedirect" : {
"href" : "/v1/payments/sepa-credit-transfers/1234-wertiq-983"
},
"startAuthorisationWithAuthenticationMethodSelection" : {
"href" : "/v1/payments/sepa-credit-transfers/1234-wertiq-983"
},
"startAuthorisationWithPsuAuthentication" : {
"href" : "/v1/payments/sepa-credit-transfers/1234-wertiq-983"
},
"scaOAuth" : {
"href" : "/v1/payments/sepa-credit-transfers/1234-wertiq-983"
},
"self" : {
"href" : "/v1/payments/sepa-credit-transfers/1234-wertiq-983"
},
"startAuthorisationWithPsuIdentification" : {
"href" : "/v1/payments/sepa-credit-transfers/1234-wertiq-983"
},
"startAuthorisation" : {
"href" : "/v1/payments/sepa-credit-transfers/1234-wertiq-983"
},
"startAuthorisationWithTransactionAuthorisation" : {
"href" : "/v1/payments/sepa-credit-transfers/1234-wertiq-983"
},
"status" : {
"href" : "/v1/payments/sepa-credit-transfers/1234-wertiq-983"
}
},
"chosenScaMethod" : "",
"transactionStatus" : "RCVD",
"psuMessage" : { }
}"""),
List(UserNotLoggedIn, UnknownError),
Catalogs(notCore, notPSD2, notOBWG),
ApiTag("Signing Baskets (SBS)") :: apiTagMockedData :: Nil
)
lazy val createSigningBasket : OBPEndpoint = {
case "signing-baskets" :: Nil JsonPost _ => {
cc =>
for {
(Full(u), callContext) <- authorizedAccess(cc, UserNotLoggedIn)
} yield {
(json.parse("""{
"basketId" : "1234-basket-567",
"challengeData" : {
"otpMaxLength" : 0,
"additionalInformation" : "additionalInformation",
"image" : "image",
"imageLink" : "http://example.com/aeiou",
"otpFormat" : "characters",
"data" : [ "data", "data" ]
},
"scaMethods" : "",
"tppMessages" : [ {
"path" : "path",
"code" : { },
"text" : { },
"category" : { }
}, {
"path" : "path",
"code" : { },
"text" : { },
"category" : { }
} ],
"_links" : {
"scaStatus" : {
"href" : "/v1/payments/sepa-credit-transfers/1234-wertiq-983"
},
"startAuthorisationWithEncryptedPsuAuthentication" : {
"href" : "/v1/payments/sepa-credit-transfers/1234-wertiq-983"
},
"scaRedirect" : {
"href" : "/v1/payments/sepa-credit-transfers/1234-wertiq-983"
},
"startAuthorisationWithAuthenticationMethodSelection" : {
"href" : "/v1/payments/sepa-credit-transfers/1234-wertiq-983"
},
"startAuthorisationWithPsuAuthentication" : {
"href" : "/v1/payments/sepa-credit-transfers/1234-wertiq-983"
},
"scaOAuth" : {
"href" : "/v1/payments/sepa-credit-transfers/1234-wertiq-983"
},
"self" : {
"href" : "/v1/payments/sepa-credit-transfers/1234-wertiq-983"
},
"startAuthorisationWithPsuIdentification" : {
"href" : "/v1/payments/sepa-credit-transfers/1234-wertiq-983"
},
"startAuthorisation" : {
"href" : "/v1/payments/sepa-credit-transfers/1234-wertiq-983"
},
"startAuthorisationWithTransactionAuthorisation" : {
"href" : "/v1/payments/sepa-credit-transfers/1234-wertiq-983"
},
"status" : {
"href" : "/v1/payments/sepa-credit-transfers/1234-wertiq-983"
}
},
"chosenScaMethod" : "",
"transactionStatus" : "RCVD",
"psuMessage" : { }
}"""), callContext)
}
}
}
resourceDocs += ResourceDoc(
deleteSigningBasket,
apiVersion,
nameOf(deleteSigningBasket),
"DELETE",
"/signing-baskets/BASKETID",
"Delete the signing basket",
s"""${mockedDataText(true)}
Delete the signing basket structure as long as no (partial) authorisation has yet been applied.
The undlerying transactions are not affected by this deletion. Remark: The signing basket as such is not
deletable after a first (partial) authorisation has been applied. Nevertheless, single transactions might
be cancelled on an individual basis on the XS2A interface.
""",
json.parse(""""""),
json.parse(""""""),
List(UserNotLoggedIn, UnknownError),
Catalogs(notCore, notPSD2, notOBWG),
ApiTag("Signing Baskets (SBS)") ::ApiTag("Common Services") :: apiTagMockedData :: Nil
)
lazy val deleteSigningBasket : OBPEndpoint = {
case "signing-baskets" :: basketid :: Nil JsonDelete _ => {
cc =>
for {
(Full(u), callContext) <- authorizedAccess(cc, UserNotLoggedIn)
} yield {
(json.parse(""""""), callContext)
}
}
}
resourceDocs += ResourceDoc(
getSigningBasket,
apiVersion,
nameOf(getSigningBasket),
"GET",
"/signing-baskets/BASKETID",
"Returns the content of an signing basket object.",
s"""${mockedDataText(true)}
Returns the content of an signing basket object.
""",
json.parse(""""""),
json.parse("""{
"_links" : {
"scaStatus" : {
"href" : "/v1/payments/sepa-credit-transfers/1234-wertiq-983"
},
"startAuthorisationWithEncryptedPsuAuthentication" : {
"href" : "/v1/payments/sepa-credit-transfers/1234-wertiq-983"
},
"scaRedirect" : {
"href" : "/v1/payments/sepa-credit-transfers/1234-wertiq-983"
},
"startAuthorisationWithAuthenticationMethodSelection" : {
"href" : "/v1/payments/sepa-credit-transfers/1234-wertiq-983"
},
"startAuthorisationWithPsuAuthentication" : {
"href" : "/v1/payments/sepa-credit-transfers/1234-wertiq-983"
},
"scaOAuth" : {
"href" : "/v1/payments/sepa-credit-transfers/1234-wertiq-983"
},
"self" : {
"href" : "/v1/payments/sepa-credit-transfers/1234-wertiq-983"
},
"startAuthorisationWithPsuIdentification" : {
"href" : "/v1/payments/sepa-credit-transfers/1234-wertiq-983"
},
"startAuthorisation" : {
"href" : "/v1/payments/sepa-credit-transfers/1234-wertiq-983"
},
"startAuthorisationWithTransactionAuthorisation" : {
"href" : "/v1/payments/sepa-credit-transfers/1234-wertiq-983"
},
"status" : {
"href" : "/v1/payments/sepa-credit-transfers/1234-wertiq-983"
}
},
"transactionStatus" : "RCVD",
"payments" : "",
"consents" : ""
}"""),
List(UserNotLoggedIn, UnknownError),
Catalogs(notCore, notPSD2, notOBWG),
ApiTag("Signing Baskets (SBS)") :: apiTagMockedData :: Nil
)
lazy val getSigningBasket : OBPEndpoint = {
case "signing-baskets" :: basketid :: Nil JsonGet _ => {
cc =>
for {
(Full(u), callContext) <- authorizedAccess(cc, UserNotLoggedIn)
} yield {
(json.parse("""{
"_links" : {
"scaStatus" : {
"href" : "/v1/payments/sepa-credit-transfers/1234-wertiq-983"
},
"startAuthorisationWithEncryptedPsuAuthentication" : {
"href" : "/v1/payments/sepa-credit-transfers/1234-wertiq-983"
},
"scaRedirect" : {
"href" : "/v1/payments/sepa-credit-transfers/1234-wertiq-983"
},
"startAuthorisationWithAuthenticationMethodSelection" : {
"href" : "/v1/payments/sepa-credit-transfers/1234-wertiq-983"
},
"startAuthorisationWithPsuAuthentication" : {
"href" : "/v1/payments/sepa-credit-transfers/1234-wertiq-983"
},
"scaOAuth" : {
"href" : "/v1/payments/sepa-credit-transfers/1234-wertiq-983"
},
"self" : {
"href" : "/v1/payments/sepa-credit-transfers/1234-wertiq-983"
},
"startAuthorisationWithPsuIdentification" : {
"href" : "/v1/payments/sepa-credit-transfers/1234-wertiq-983"
},
"startAuthorisation" : {
"href" : "/v1/payments/sepa-credit-transfers/1234-wertiq-983"
},
"startAuthorisationWithTransactionAuthorisation" : {
"href" : "/v1/payments/sepa-credit-transfers/1234-wertiq-983"
},
"status" : {
"href" : "/v1/payments/sepa-credit-transfers/1234-wertiq-983"
}
},
"transactionStatus" : "RCVD",
"payments" : "",
"consents" : ""
}"""), callContext)
}
}
}
resourceDocs += ResourceDoc(
getSigningBasketAuthorisation,
apiVersion,
nameOf(getSigningBasketAuthorisation),
"GET",
"/signing-baskets/BASKETID/authorisations",
"Get Signing Basket Authorisation Sub-Resources Request",
s"""${mockedDataText(true)}
Read a list of all authorisation subresources IDs which have been created. This function returns an array
of hyperlinks to all generated authorisation sub-resources.
""",
json.parse(""""""),
json.parse("""{
"authorisationIds" : ""
}"""),
List(UserNotLoggedIn, UnknownError),
Catalogs(notCore, notPSD2, notOBWG),
ApiTag("Signing Baskets (SBS)") ::ApiTag("Common Services") :: apiTagMockedData :: Nil
)
lazy val getSigningBasketAuthorisation : OBPEndpoint = {
case "signing-baskets" :: basketid:: "authorisations" :: Nil JsonGet _ => {
cc =>
for {
(Full(u), callContext) <- authorizedAccess(cc, UserNotLoggedIn)
} yield {
(json.parse("""{
"authorisationIds" : ""
}"""), callContext)
}
}
}
resourceDocs += ResourceDoc(
getSigningBasketScaStatus,
apiVersion,
nameOf(getSigningBasketScaStatus),
"GET",
"/signing-baskets/BASKETID/authorisations/AUTHORISATIONID",
"Read the SCA status of the signing basket authorisation",
s"""${mockedDataText(true)}
This method returns the SCA status of a signing basket's authorisation sub-resource.
""",
json.parse(""""""),
json.parse("""{
"scaStatus" : "psuAuthenticated"
}"""),
List(UserNotLoggedIn, UnknownError),
Catalogs(notCore, notPSD2, notOBWG),
ApiTag("Signing Baskets (SBS)") ::ApiTag("Common Services") :: apiTagMockedData :: Nil
)
lazy val getSigningBasketScaStatus : OBPEndpoint = {
case "signing-baskets" :: basketid:: "authorisations" :: authorisationid :: Nil JsonGet _ => {
cc =>
for {
(Full(u), callContext) <- authorizedAccess(cc, UserNotLoggedIn)
} yield {
(json.parse("""{
"scaStatus" : "psuAuthenticated"
}"""), callContext)
}
}
}
resourceDocs += ResourceDoc(
getSigningBasketStatus,
apiVersion,
nameOf(getSigningBasketStatus),
"GET",
"/signing-baskets/BASKETID/status",
"Read the status of the signing basket",
s"""${mockedDataText(true)}
Returns the status of a signing basket object.
""",
json.parse(""""""),
json.parse("""{
"transactionStatus" : "RCVD"
}"""),
List(UserNotLoggedIn, UnknownError),
Catalogs(notCore, notPSD2, notOBWG),
ApiTag("Signing Baskets (SBS)") ::ApiTag("Common Services") :: apiTagMockedData :: Nil
)
lazy val getSigningBasketStatus : OBPEndpoint = {
case "signing-baskets" :: basketid:: "status" :: Nil JsonGet _ => {
cc =>
for {
(Full(u), callContext) <- authorizedAccess(cc, UserNotLoggedIn)
} yield {
(json.parse("""{
"transactionStatus" : "RCVD"
}"""), callContext)
}
}
}
resourceDocs += ResourceDoc(
startSigningBasketAuthorisation,
apiVersion,
nameOf(startSigningBasketAuthorisation),
"POST",
"/signing-baskets/BASKETID/authorisations",
"Start the authorisation process for a signing basket",
s"""${mockedDataText(true)}
Create an authorisation sub-resource and start the authorisation process of a signing basket. The message
might in addition transmit authentication and authorisation related data. This method is iterated n times
for a n times SCA authorisation in a corporate context, each creating an own authorisation sub-endpoint for
the corresponding PSU authorising the signing-baskets. The ASPSP might make the usage of this access method
unnecessary in case of only one SCA process needed, since the related authorisation resource might be
automatically created by the ASPSP after the submission of the payment data with the first POST signing
basket call. The start authorisation process is a process which is needed for creating a new authorisation
or cancellation sub-resource. This applies in the following scenarios: * The ASPSP has indicated with
an 'startAuthorisation' hyperlink in the preceding 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',
* 'startAuthorisationWithPsuAuthentication'
* 'startAuthorisationWithEncryptedPsuAuthentication'
* '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 preceding 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 executing the cancellation. * The signing basket needs to be authorised yet.
""",
json.parse(""""""),
json.parse("""{
"authorisationId" : "123auth456",
"challengeData" : {
"otpMaxLength" : 0,
"additionalInformation" : "additionalInformation",
"image" : "image",
"imageLink" : "http://example.com/aeiou",
"otpFormat" : "characters",
"data" : [ "data", "data" ]
},
"scaMethods" : "",
"scaStatus" : "psuAuthenticated",
"_links" : {
"scaStatus" : {
"href" : "/v1/payments/sepa-credit-transfers/1234-wertiq-983"
},
"startAuthorisationWithEncryptedPsuAuthentication" : {
"href" : "/v1/payments/sepa-credit-transfers/1234-wertiq-983"
},
"scaRedirect" : {
"href" : "/v1/payments/sepa-credit-transfers/1234-wertiq-983"
},
"selectAuthenticationMethod" : {
"href" : "/v1/payments/sepa-credit-transfers/1234-wertiq-983"
},
"startAuthorisationWithPsuAuthentication" : {
"href" : "/v1/payments/sepa-credit-transfers/1234-wertiq-983"
},
"authoriseTransaction" : {
"href" : "/v1/payments/sepa-credit-transfers/1234-wertiq-983"
},
"scaOAuth" : {
"href" : "/v1/payments/sepa-credit-transfers/1234-wertiq-983"
},
"updatePsuIdentification" : {
"href" : "/v1/payments/sepa-credit-transfers/1234-wertiq-983"
}
},
"chosenScaMethod" : "",
"psuMessage" : { }
}"""),
List(UserNotLoggedIn, UnknownError),
Catalogs(notCore, notPSD2, notOBWG),
ApiTag("Signing Baskets (SBS)") ::ApiTag("Common Services") :: apiTagMockedData :: Nil
)
lazy val startSigningBasketAuthorisation : OBPEndpoint = {
case "signing-baskets" :: basketid:: "authorisations" :: Nil JsonPost _ => {
cc =>
for {
(Full(u), callContext) <- authorizedAccess(cc, UserNotLoggedIn)
} yield {
(json.parse("""{
"authorisationId" : "123auth456",
"challengeData" : {
"otpMaxLength" : 0,
"additionalInformation" : "additionalInformation",
"image" : "image",
"imageLink" : "http://example.com/aeiou",
"otpFormat" : "characters",
"data" : [ "data", "data" ]
},
"scaMethods" : "",
"scaStatus" : "psuAuthenticated",
"_links" : {
"scaStatus" : {
"href" : "/v1/payments/sepa-credit-transfers/1234-wertiq-983"
},
"startAuthorisationWithEncryptedPsuAuthentication" : {
"href" : "/v1/payments/sepa-credit-transfers/1234-wertiq-983"
},
"scaRedirect" : {
"href" : "/v1/payments/sepa-credit-transfers/1234-wertiq-983"
},
"selectAuthenticationMethod" : {
"href" : "/v1/payments/sepa-credit-transfers/1234-wertiq-983"
},
"startAuthorisationWithPsuAuthentication" : {
"href" : "/v1/payments/sepa-credit-transfers/1234-wertiq-983"
},
"authoriseTransaction" : {
"href" : "/v1/payments/sepa-credit-transfers/1234-wertiq-983"
},
"scaOAuth" : {
"href" : "/v1/payments/sepa-credit-transfers/1234-wertiq-983"
},
"updatePsuIdentification" : {
"href" : "/v1/payments/sepa-credit-transfers/1234-wertiq-983"
}
},
"chosenScaMethod" : "",
"psuMessage" : { }
}"""), callContext)
}
}
}
resourceDocs += ResourceDoc(
updateSigningBasketPsuData,
apiVersion,
nameOf(updateSigningBasketPsuData),
"PUT",
"/signing-baskets/BASKETID/authorisations/AUTHORISATIONID",
"Update PSU Data for signing basket",
s"""${mockedDataText(true)}
This method update PSU data on the signing basket resource if needed. It may authorise a igning basket within
the Embedded SCA Approach where needed. Independently from the SCA Approach it supports e.g. the selection of
the authentication method and a non-SCA PSU authentication. This methods updates PSU data on the cancellation
authorisation resource if needed. There are several possible Update PSU Data requests in the context of a consent
request if needed, which depends on the SCA approach: * Redirect SCA Approach: 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
* to add credentials as a first factor authentication data of the PSU and
* to select the authentication method and * transaction authorisation.
The SCA Approach might depend on the chosen SCA method. For that reason,
the following possible Update PSU Data request can apply to all SCA approaches:
* Select an SCA method in case of several SCA methods are available for the customer.
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. 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. Maybe in a later version the access path will change.
""",
json.parse(""""""),
json.parse(""""""""),
List(UserNotLoggedIn, UnknownError),
Catalogs(notCore, notPSD2, notOBWG),
ApiTag("Signing Baskets (SBS)") ::ApiTag("Common Services") :: apiTagMockedData :: Nil
)
lazy val updateSigningBasketPsuData : OBPEndpoint = {
case "signing-baskets" :: basketid:: "authorisations" :: authorisationid :: Nil JsonPut _ => {
cc =>
for {
(Full(u), callContext) <- authorizedAccess(cc, UserNotLoggedIn)
} yield {
(json.parse(""""""""), callContext)
}
}
}
}

View File

@ -2557,9 +2557,11 @@ Returns a string showed to the developer
val validatedPem = X509.validate(pem)
validatedPem match {
case Full(true) =>
Full(X509.getRoles(pem).contains(serviceProvider)) match {
val roles = X509.extractPsd2Roles(pem).map(_.exists(_ == serviceProvider))
roles match {
case Full(true) => Full(true)
case Full(false) => Failure(X509ActionIsNotAllowed)
case _ => roles
}
case _ =>
validatedPem
@ -2569,7 +2571,7 @@ Returns a string showed to the developer
}
result
}
def passesPsd2ServiceProvider(cc: Option[CallContext], serviceProvider: String): OBPReturnType[Box[Boolean]] = {
val result = passesPsd2ServiceProviderCommon(cc, serviceProvider)
Future(result) map {
@ -2577,16 +2579,16 @@ Returns a string showed to the developer
}
}
def passesPsd2Aisp(cc: Option[CallContext]): OBPReturnType[Box[Boolean]] = {
passesPsd2ServiceProvider(cc, PemCertificateRole.psp_ai.toString())
passesPsd2ServiceProvider(cc, PemCertificateRole.PSP_AI.toString())
}
def passesPsd2Pisp(cc: Option[CallContext]): OBPReturnType[Box[Boolean]] = {
passesPsd2ServiceProvider(cc, PemCertificateRole.psp_pi.toString())
passesPsd2ServiceProvider(cc, PemCertificateRole.PSP_PI.toString())
}
def passesPsd2Icsp(cc: Option[CallContext]): OBPReturnType[Box[Boolean]] = {
passesPsd2ServiceProvider(cc, PemCertificateRole.psp_ic.toString())
passesPsd2ServiceProvider(cc, PemCertificateRole.PSP_IC.toString())
}
def passesPsd2Assp(cc: Option[CallContext]): OBPReturnType[Box[Boolean]] = {
passesPsd2ServiceProvider(cc, PemCertificateRole.psp_as.toString())
passesPsd2ServiceProvider(cc, PemCertificateRole.PSP_AS.toString())
}
@ -2594,23 +2596,30 @@ Returns a string showed to the developer
passesPsd2ServiceProviderCommon(cc, serviceProvider) ?~! X509GeneralError
}
def passesPsd2AispOldStyle(cc: Option[CallContext]): Box[Boolean] = {
passesPsd2ServiceProviderOldStyle(cc, PemCertificateRole.psp_ai.toString())
passesPsd2ServiceProviderOldStyle(cc, PemCertificateRole.PSP_AI.toString())
}
def passesPsd2PispOldStyle(cc: Option[CallContext]): Box[Boolean] = {
passesPsd2ServiceProviderOldStyle(cc, PemCertificateRole.psp_pi.toString())
passesPsd2ServiceProviderOldStyle(cc, PemCertificateRole.PSP_PI.toString())
}
def passesPsd2IcspOldStyle(cc: Option[CallContext]): Box[Boolean] = {
passesPsd2ServiceProviderOldStyle(cc, PemCertificateRole.psp_ic.toString())
passesPsd2ServiceProviderOldStyle(cc, PemCertificateRole.PSP_IC.toString())
}
def passesPsd2AsspOldStyle(cc: Option[CallContext]): Box[Boolean] = {
passesPsd2ServiceProviderOldStyle(cc, PemCertificateRole.psp_as.toString())
passesPsd2ServiceProviderOldStyle(cc, PemCertificateRole.PSP_AS.toString())
}
def getMaskedPrimaryAccountNumber(accountNumber: String): String = {
val (first, second) = accountNumber.splitAt(accountNumber.size/2)
first.substring(0, first.size - 3) + "***" + "***" + second.substring(3)
if(first.length >=3 && second.length>=3)
first.substring(0, first.size - 3) + "***" + "***" + second.substring(3)
else if (first.length >=3 && second.length< 3)
first.substring(0, first.size - 3) + "***" + "***" + second
else if (first.length <3 && second.length>= 3)
first + "***" + "***" + second.substring(3)
else
first+ "***" + "***" + second
}
def getBicFromBankId(bankId: String)= {

View File

@ -8,8 +8,8 @@ object StrongCustomerAuthentication extends Enumeration {
object PemCertificateRole extends Enumeration {
type ROLE = Value
val psp_as = Value
val psp_ic = Value
val psp_ai = Value
val psp_pi = Value
val PSP_AS = Value
val PSP_IC = Value
val PSP_AI = Value
val PSP_PI = Value
}

View File

@ -152,6 +152,7 @@ object ErrorMessages {
val X509CannotGetECPublicKey = "OBP-20305: EC public key cannot be found at PEM Encoded Certificate."
val X509CannotGetCertificate = "OBP-20306: PEM Encoded Certificate cannot be found at request header."
val X509ActionIsNotAllowed = "OBP-20307: PEM Encoded Certificate does not provide the proper role for the action has been taken."
val X509ThereAreNoPsd2Roles = "OBP-20308: PEM Encoded Certificate does not contain PSD2 roles."
// Resource related messages (OBP-30XXX)
val BankNotFound = "OBP-30001: Bank not found. Please specify a valid value for BANK_ID."
@ -310,6 +311,7 @@ object ErrorMessages {
//Authorisations
val AuthorisationNotFound = "OBP-36001: Authorisation not found. Please specify valid values for PAYMENT_ID and AUTHORISATION_ID. "
val InvalidAuthorisationStatus = "OBP-36002: Authorisation Status is Invalid"
// General Resource related messages above here

View File

@ -3,20 +3,25 @@ package code.api.util
import java.security.PublicKey
import java.security.cert.{CertificateExpiredException, CertificateNotYetValidException, X509Certificate}
import java.security.interfaces.{ECPublicKey, RSAPublicKey}
import java.io.ByteArrayInputStream
import com.github.dwickern.macros.NameOf
import com.nimbusds.jose.jwk.RSAKey
import com.nimbusds.jose.util.X509CertUtils
import net.liftweb.common.{Box, Failure, Full}
import org.bouncycastle.asn1.x509.Extension
import org.bouncycastle.asn1.{ASN1Encodable, ASN1InputStream, ASN1ObjectIdentifier, ASN1Sequence, DEROctetString}
import org.bouncycastle.asn1.x509.qualified.QCStatement
object X509 {
object OID {
lazy val role = "2.5.4.72"
lazy val etsiPsd2QcStatement = new ASN1ObjectIdentifier("0.4.0.19495.2")
}
case class SubjectAttribute(key: String, value: String)
private def extractSubjectAttributes(encodedCert: String): List[SubjectAttribute] = {
// Parse X.509 certificate
val cert: X509Certificate = X509CertUtils.parse(encodedCert)
@ -24,14 +29,16 @@ object X509 {
// Parsing failed
Nil
} else {
cert.getSubjectDN().getName().split(",").toList.map {
cert.getSubjectDN().getName().split(",").toList.map {
attribute => attribute.trim.split("=").toList match {
case key :: value :: Nil => SubjectAttribute(key, value)
}
}
}
}
def getRoles(encodedCert: String): String = {
extractSubjectAttributes(encodedCert).filter{
attribute => attribute.key.contains(OID.role) || attribute.key.contains(NameOf.nameOf(OID.role))
@ -40,8 +47,61 @@ object X509 {
case _ => ""
}
}
def extractQcStatements(cert: X509Certificate): ASN1Sequence = {
val qcStatementBytes: Array[Byte] = cert.getExtensionValue(Extension.qCStatements.getId)
val inputStream = new ASN1InputStream(new ByteArrayInputStream(qcStatementBytes))
val dEROctetString = inputStream.readObject().asInstanceOf[DEROctetString]
val qcInputStream = new ASN1InputStream(dEROctetString.getOctets)
val qcStatements = qcInputStream.readObject().asInstanceOf[ASN1Sequence]
qcStatements
}
def extractPsd2QcStatements(qcstatements: ASN1Sequence) = {
val encodable: Array[ASN1Encodable] = qcstatements.toArray.filter(QCStatement.getInstance(_).getStatementId.getId.equals(X509.OID.etsiPsd2QcStatement.getId))
encodable
}
def getPsd2Roles(asn1encodable: Array[ASN1Encodable]): List[String] = {
var psd2Roles: Set[String] = Set()
for (i <- asn1encodable.indices) {
val psd2Sequence = ASN1Sequence.getInstance(asn1encodable(i))
val psd2TypesEncodable: ASN1Encodable = psd2Sequence.getObjectAt(1)
val psd2TypesSequence = ASN1Sequence.getInstance(psd2TypesEncodable)
val psd2RolesEncodable: ASN1Encodable = psd2TypesSequence.getObjectAt(0)
val psd2RolesSequence = ASN1Sequence.getInstance(psd2RolesEncodable)
for (y <- 0 until (psd2RolesSequence.size() - 1)){
val psd2RoleEncodable = psd2RolesSequence.getObjectAt(y)
val psd2RoleSequence = ASN1Sequence.getInstance(psd2RoleEncodable)
psd2Roles += (psd2RoleSequence.getObjectAt(1).toASN1Primitive.toString)
}
}
org.scalameta.logger.elem(psd2Roles.toList)
psd2Roles.toList
}
def extractPsd2Roles(pem: String): Box[List[String]] = {
// Parse X.509 certificate
val cert: X509Certificate = X509CertUtils.parse(pem)
if (cert == null) {
// Parsing failed
Failure(ErrorMessages.X509ParsingFailed)
} else {
try {
val qcstatements = extractQcStatements(cert)
val asn1encodable = extractPsd2QcStatements(qcstatements)
Full(getPsd2Roles(asn1encodable: Array[ASN1Encodable]))
}
catch {
case _ =>
Failure(ErrorMessages.X509ThereAreNoPsd2Roles)
}
}
}
/**
* The certificate must be validated before it may be used.
@ -67,7 +127,7 @@ object X509 {
}
}
}
/**
* If the certificate passed validation and can be trusted,
* you can proceed by extracting the public key (RSA or EC) that comes with it,

View File

@ -1129,7 +1129,7 @@ trait APIMethods310 {
cc =>
for {
(user, callContext) <- authorizedAccess(cc)
_ <- passesPsd2Aisp(callContext)
_ <- passesPsd2Pisp(callContext)
(_, callContext) <- NewStyle.function.getBank(bankId, callContext)
(account, callContext) <- NewStyle.function.checkBankAccountExists(bankId, accountId, callContext)
view <- NewStyle.function.view(viewId, BankIdAccountId(account.bankId, account.accountId), callContext)

View File

@ -1086,6 +1086,29 @@ trait Connector extends MdcLoggable with CustomJsonFormats{
}yield{
(transactionId,callContext)
}
case sepa_credit_transfers => for{
toSepaCreditTransfers <- NewStyle.function.tryons(s"$TransactionRequestDetailsExtractException It can not extract to $TransactionRequestBodySandBoxTanJSON ", 400, callContext){
body.to_sepa_credit_transfers.get
}
toAccountId = toSepaCreditTransfers.debtorAccount.iban
(toAccount, callContext) <- NewStyle.function.getBankAccountByIban(toAccountId, callContext)
(createdTransactionId, callContext) <- NewStyle.function.makePaymentv210(
fromAccount,
toAccount,
TransactionRequestCommonBodyJSONCommons(
toSepaCreditTransfers.instructedAmount,
""
),
BigDecimal(toSepaCreditTransfers.instructedAmount.amount),
"", //This is empty for BerlinGroup sepa_credit_transfers type now.
TransactionRequestType(transactionRequestType),
transactionRequest.charge_policy,
callContext
)
}yield{
(createdTransactionId,callContext)
}
case transactionRequestType => Future((throw new Exception(s"${InvalidTransactionRequestType}: '${transactionRequestType}'. Not supported in this version.")), callContext)
}

View File

@ -80,8 +80,8 @@ object MappedAuthorisationProvider extends AuthorisationProvider {
case value if value == ScaStatus.received.toString =>
val status = if (authorisation.challengeData == challengeData) ScaStatus.finalised.toString else ScaStatus.failed.toString
tryo(authorisation.ScaStatus(status).saveMe())
case _ =>
Full(authorisation)
case _ => //make sure, only `reveived` can be processed, all others are invalid .
Failure(s"${ErrorMessages.InvalidAuthorisationStatus}.It should be `received`, but now it is `${authorisation.scaStatus}`")
}
case Empty =>
Empty ?~! s"${ErrorMessages.AuthorisationNotFound} Current PAYMENT_ID($paymentId) and AUTHORISATION_ID ($authorizationId),"

View File

@ -77,7 +77,7 @@ class PaymentOTP extends MdcLoggable {
}
result.map(json.parse(_).extract[StartPaymentAuthorisationJson]) match {
case Right(v) if(v.scaStatus == "received")=> {
case Right(v) if(v.scaStatus == "finalised")=> {
"#form_otp" #> "" &
"#otp-validate-success p *" #> "OTP validate success." &
"#otp-validate-errors" #> ""

View File

@ -153,7 +153,7 @@ class WebUI extends MdcLoggable{
}
def sandboxIntroductionLink: CssSel = {
"#sandbox-introduction-link [href]" #> scala.xml.Unparsed(s"${getServerUrl}/introduction")
"#sandbox-introduction-link [href]" #> scala.xml.Unparsed(getWebUiPropsValue("webui_api_documentation_url",s"${getServerUrl}/introduction"))
}
def apiDocumentation: CssSel = {

View File

@ -0,0 +1,253 @@
package code.api.berlin.group.v1_3
import code.api.ErrorMessage
import code.api.berlin.group.v1_3.JSONFactory_BERLIN_GROUP_1_3._
import code.api.builder.AccountInformationServiceAISApi.APIMethods_AccountInformationServiceAISApi
import code.api.util.APIUtil.OAuth._
import code.api.util.ErrorMessages._
import code.setup.{APIResponse, DefaultUsers}
import com.github.dwickern.macros.NameOf.nameOf
import net.liftweb.json.Serialization.write
import org.scalatest.Tag
class AccountInformationServiceAISApiTest extends BerlinGroupServerSetupV1_3 with DefaultUsers {
object getAccountList extends Tag(nameOf(APIMethods_AccountInformationServiceAISApi.getAccountList))
object getBalances extends Tag(nameOf(APIMethods_AccountInformationServiceAISApi.getBalances))
object getTransactionList extends Tag(nameOf(APIMethods_AccountInformationServiceAISApi.getTransactionList))
object getCardAccountTransactionList extends Tag(nameOf(APIMethods_AccountInformationServiceAISApi.getCardAccountTransactionList))
object createConsent extends Tag(nameOf(APIMethods_AccountInformationServiceAISApi.createConsent))
object deleteConsent extends Tag(nameOf(APIMethods_AccountInformationServiceAISApi.deleteConsent))
object getConsentInformation extends Tag(nameOf(APIMethods_AccountInformationServiceAISApi.getConsentInformation))
object getConsentStatus extends Tag(nameOf(APIMethods_AccountInformationServiceAISApi.getConsentStatus))
object startConsentAuthorisation extends Tag(nameOf(APIMethods_AccountInformationServiceAISApi.startConsentAuthorisation))
object getConsentAuthorisation extends Tag(nameOf(APIMethods_AccountInformationServiceAISApi.getConsentAuthorisation))
object getConsentScaStatus extends Tag(nameOf(APIMethods_AccountInformationServiceAISApi.getConsentScaStatus))
object updateConsentsPsuData extends Tag(nameOf(APIMethods_AccountInformationServiceAISApi.updateConsentsPsuData))
feature(s"BG v1.3 - $getAccountList") {
scenario("Not Authentication User, test failed ", BerlinGroupV1_3, getAccountList) {
val requestGet = (V1_3_BG / "accounts").GET
val response = makeGetRequest(requestGet)
Then("We should get a 400 ")
response.code should equal(400)
response.body.extract[ErrorMessage].message should startWith(UserNotLoggedIn)
}
scenario("Authentication User, test succeed", BerlinGroupV1_3, getAccountList) {
val requestGet = (V1_3_BG / "accounts").GET <@ (user1)
val response = makeGetRequest(requestGet)
Then("We should get a 200 ")
response.code should equal(200)
response.body.extract[CoreAccountsJsonV13].accounts.length > 1 should be (true)
}
}
feature(s"BG v1.3 - $getBalances") {
scenario("Authentication User, test succeed", BerlinGroupV1_3, getBalances) {
val testBankId = testAccountId1
val requestGet = (V1_3_BG / "accounts" /testBankId.value/ "balances").GET <@ (user1)
val response: APIResponse = makeGetRequest(requestGet)
Then("We should get a 200 ")
response.code should equal(200)
response.body.extract[AccountBalancesV13].`balances`.length > 0 should be (true)
response.body.extract[AccountBalancesV13].account.iban should be ("")
}
}
feature(s"BG v1.3 - $getTransactionList") {
scenario("Authentication User, test succeed", BerlinGroupV1_3, getTransactionList) {
val testBankId = testAccountId1
val requestGet = (V1_3_BG / "accounts" /testBankId.value/ "transactions").GET <@ (user1)
val response: APIResponse = makeGetRequest(requestGet)
Then("We should get a 200 ")
response.code should equal(200)
response.body.extract[TransactionsJsonV13].account.iban should be ("")
response.body.extract[TransactionsJsonV13].transactions.booked.length >0 should be (true)
response.body.extract[TransactionsJsonV13].transactions.pending.length >0 should be (true)
}
}
feature(s"BG v1.3 - $getCardAccountTransactionList") {
scenario("Authentication User, test succeed", BerlinGroupV1_3, getCardAccountTransactionList) {
val testBankId = testAccountId1
val requestGet = (V1_3_BG / "card-accounts" /testBankId.value/ "transactions").GET <@ (user1)
val response: APIResponse = makeGetRequest(requestGet)
Then("We should get a 200 ")
response.code should equal(200)
response.body.extract[CardTransactionsJsonV13].cardAccount.maskedPan.length >0 should be (true)
response.body.extract[CardTransactionsJsonV13].transactions.booked.length >0 should be (true)
}
}
feature(s"BG v1.3 - $createConsent") {
scenario("Authentication User, test succeed", BerlinGroupV1_3, createConsent) {
val testBankId = testAccountId1
val postJsonBody = APIMethods_AccountInformationServiceAISApi
.resourceDocs
.filter( _.partialFunction == APIMethods_AccountInformationServiceAISApi.createConsent)
.head.exampleRequestBody.asInstanceOf[JvalueCaseClass] //All the Json String convert to JvalueCaseClass implicitly
.jvalueToCaseclass
val requestPost = (V1_3_BG / "consents" ).POST <@ (user1)
val response: APIResponse = makePostRequest(requestPost, write(postJsonBody))
Then("We should get a 201 ")
response.code should equal(201)
response.body.extract[PostConsentResponseJson].consentId should not be (empty)
response.body.extract[PostConsentResponseJson].consentStatus should be ("received")
}
}
feature(s"BG v1.3 - $createConsent and $deleteConsent") {
scenario("Authentication User, test succeed", BerlinGroupV1_3, createConsent) {
val testBankId = testAccountId1
val postJsonBody = APIMethods_AccountInformationServiceAISApi
.resourceDocs
.filter( _.partialFunction == APIMethods_AccountInformationServiceAISApi.createConsent)
.head.exampleRequestBody.asInstanceOf[JvalueCaseClass] //All the Json String convert to JvalueCaseClass implicitly
.jvalueToCaseclass
val requestPost = (V1_3_BG / "consents" ).POST <@ (user1)
val response: APIResponse = makePostRequest(requestPost, write(postJsonBody))
Then("We should get a 201 ")
response.code should equal(201)
response.body.extract[PostConsentResponseJson].consentId should not be (empty)
val consentId =response.body.extract[PostConsentResponseJson].consentId
Then("We test the delete consent ")
val requestDelete = (V1_3_BG / "consents"/consentId ).DELETE <@ (user1)
val responseDelete = makeDeleteRequest(requestDelete)
responseDelete.code should be (204)
//TODO We can not delete one consent two time, will fix it later.
// val responseDeleteSecondTime = makeDeleteRequest(requestDelete)
// responseDeleteSecondTime.code should be (400)
}
}
feature(s"BG v1.3 - $createConsent and $getConsentInformation and $getConsentStatus") {
scenario("Authentication User, test succeed", BerlinGroupV1_3, createConsent) {
val testBankId = testAccountId1
val postJsonBody = APIMethods_AccountInformationServiceAISApi
.resourceDocs
.filter( _.partialFunction == APIMethods_AccountInformationServiceAISApi.createConsent)
.head.exampleRequestBody.asInstanceOf[JvalueCaseClass] //All the Json String convert to JvalueCaseClass implicitly
.jvalueToCaseclass
val requestPost = (V1_3_BG / "consents" ).POST <@ (user1)
val response: APIResponse = makePostRequest(requestPost, write(postJsonBody))
Then("We should get a 201 ")
response.code should equal(201)
response.body.extract[PostConsentResponseJson].consentId should not be (empty)
val consentId =response.body.extract[PostConsentResponseJson].consentId
Then(s"We test the $getConsentInformation")
val requestGet = (V1_3_BG / "consents"/consentId ).GET <@ (user1)
val responseGet = makeGetRequest(requestGet)
responseGet.code should be (200)
responseGet.body.extract[GetConsentResponseJson].consentStatus should be ("received")
Then(s"We test the $getConsentStatus")
val requestGetStatus = (V1_3_BG / "consents"/consentId /"status" ).GET <@ (user1)
val responseGetStatus = makeGetRequest(requestGetStatus)
responseGetStatus.code should be (200)
responseGetStatus.body.extract[ConsentStatusJsonV13].consentStatus should be ("received")
}
}
feature(s"BG v1.3 - ${startConsentAuthorisation.name} ") {
scenario("Authentication User, test succeed", BerlinGroupV1_3, startConsentAuthorisation) {
val postJsonBody = APIMethods_AccountInformationServiceAISApi
.resourceDocs
.filter( _.partialFunction == APIMethods_AccountInformationServiceAISApi.createConsent)
.head.exampleRequestBody.asInstanceOf[JvalueCaseClass] //All the Json String convert to JvalueCaseClass implicitly
.jvalueToCaseclass
val requestPost = (V1_3_BG / "consents" ).POST <@ (user1)
val response: APIResponse = makePostRequest(requestPost, write(postJsonBody))
Then("We should get a 201 ")
response.code should equal(201)
response.body.extract[PostConsentResponseJson].consentId should not be (empty)
val consentId =response.body.extract[PostConsentResponseJson].consentId
Then(s"We test the $startConsentAuthorisation")
val requestStartConsentAuthorisation = (V1_3_BG / "consents"/consentId /"authorisations" ).POST <@ (user1)
val responseStartConsentAuthorisation = makePostRequest(requestStartConsentAuthorisation)
responseStartConsentAuthorisation.code should be (201)
responseStartConsentAuthorisation.body.extract[StartConsentAuthorisationJson].scaStatus should be ("received")
}
}
feature(s"BG v1.3 - ${startConsentAuthorisation.name} and ${getConsentAuthorisation.name} and ${getConsentScaStatus.name} and ${updateConsentsPsuData.name}") {
scenario("Authentication User, test succeed", BerlinGroupV1_3, startConsentAuthorisation) {
val postJsonBody = APIMethods_AccountInformationServiceAISApi
.resourceDocs
.filter( _.partialFunction == APIMethods_AccountInformationServiceAISApi.createConsent)
.head.exampleRequestBody.asInstanceOf[JvalueCaseClass] //All the Json String convert to JvalueCaseClass implicitly
.jvalueToCaseclass
val requestPost = (V1_3_BG / "consents" ).POST <@ (user1)
val response: APIResponse = makePostRequest(requestPost, write(postJsonBody))
Then("We should get a 201 ")
response.code should equal(201)
response.body.extract[PostConsentResponseJson].consentId should not be (empty)
val consentId =response.body.extract[PostConsentResponseJson].consentId
Then(s"We test the $startConsentAuthorisation")
val requestStartConsentAuthorisation = (V1_3_BG / "consents"/consentId /"authorisations" ).POST <@ (user1)
val responseStartConsentAuthorisation = makePostRequest(requestStartConsentAuthorisation)
responseStartConsentAuthorisation.code should be (201)
responseStartConsentAuthorisation.body.extract[StartConsentAuthorisationJson].scaStatus should be ("received")
Then(s"We test the $getConsentAuthorisation")
val requestGetConsentAuthorisation = (V1_3_BG / "consents"/consentId /"authorisations" ).POST <@ (user1)
val responseGetConsentAuthorisation = makeGetRequest(requestGetConsentAuthorisation)
responseGetConsentAuthorisation.code should be (200)
responseGetConsentAuthorisation.body.extract[AuthorisationJsonV13].authorisationIds.length > 0 should be (true)
Then(s"We test the $getConsentScaStatus")
val authorisationId = responseGetConsentAuthorisation.body.extract[AuthorisationJsonV13].authorisationIds.head
val requestGetConsentScaStatus = (V1_3_BG / "consents"/consentId /"authorisations"/authorisationId ).POST <@ (user1)
val responseGetConsentScaStatus = makeGetRequest(requestGetConsentScaStatus)
responseGetConsentScaStatus.code should be (200)
responseGetConsentScaStatus.body.extract[ScaStatusJsonV13].scaStatus should be ("received")
Then(s"We test the $updateConsentsPsuData")
val updateConsentsPsuDataJsonBody = APIMethods_AccountInformationServiceAISApi
.resourceDocs
.filter( _.partialFunction == APIMethods_AccountInformationServiceAISApi.updateConsentsPsuData)
.head.exampleRequestBody.asInstanceOf[JvalueCaseClass] //All the Json String convert to JvalueCaseClass implicitly
.jvalueToCaseclass
val requestUpdateConsentsPsuData = (V1_3_BG / "consents"/consentId /"authorisations"/ authorisationId).PUT <@ (user1)
val responseUpdateConsentsPsuData = makePutRequest(requestUpdateConsentsPsuData, write(updateConsentsPsuDataJsonBody))
responseUpdateConsentsPsuData.code should be (200)
responseUpdateConsentsPsuData.body.extract[PostConsentResponseJson].consentStatus should be ("received")
}
}
}

View File

@ -1,7 +1,9 @@
package code.api.berlin.group.v1_3
import code.setup.ServerSetupWithTestData
import org.scalatest.Tag
trait BerlinGroupServerSetupV1_3 extends ServerSetupWithTestData {
val urpPrefix = baseRequest / "berlin-group" / "v1.3"
object BerlinGroupV1_3 extends Tag("BerlinGroup_v1_3")
val V1_3_BG = baseRequest / "berlin-group" / "v1.3"
}

View File

@ -1,99 +0,0 @@
package code.api.berlin.group.v1_3
import code.api.ErrorMessage
import code.api.util.APIUtil.OAuth._
import code.api.util.ErrorMessages._
import code.model.dataAccess.MappedBankAccount
import code.setup.{APIResponse, DefaultUsers}
import code.transactionrequests.TransactionRequests.{PaymentServiceTypes, TransactionRequestTypes}
import org.scalatest.Tag
class BerlinGroupTestsV1_3 extends BerlinGroupServerSetupV1_3 with DefaultUsers {
object BerlinGroupConcentsV1_3 extends Tag("berlinGroup_concents_v1_3")
object BerlinGroupPaymentV1_3 extends Tag("berlinGroup_Payment_v1_3")
val consentJsonForPost =
"""{
| "access": {
| "accounts": [
| {
| "iban": "FR7612345987650123456789014",
| "bban": "BARC12345612345678",
| "pan": "5409050000000000",
| "maskedPan": "123456xxxxxx1234",
| "msisdn": "+49 170 1234567",
| "currency": "EUR"
| }
| ]
| },
| "recurringIndicator": false,
| "validUntil": "2020-12-31",
| "frequencyPerDay": 4,
| "combinedServiceIndicator": false
|}""".stripMargin
feature("test the BG v1.3 consent endpoints") {
scenario("Fail call endpoint createConsent, because accounts is not empty", BerlinGroupConcentsV1_3) {
When("Post consent json with no empty accounts")
val requestPost = (urpPrefix / "consents").POST <@ (user1)
val response: APIResponse = makePostRequest(requestPost, consentJsonForPost)
Then("We should get a 400 ")
response.code should equal(400)
response.body.extract[ErrorMessage]
.message should startWith(InvalidJsonContent)
}
scenario("Successful call endpoint createConsent", BerlinGroupConcentsV1_3) {
When("Post consent json with no empty accounts")
val requestPost = (urpPrefix / "consents").POST <@ (user1)
val emptyAccountsJson = consentJsonForPost.replaceFirst("""(?s)("accounts"\s*\:\s*\[).*?(\])""", "$1 $2")
val response: APIResponse = makePostRequest(requestPost, emptyAccountsJson)
Then("We should get a 201 ")
response.code should equal(201)
response.body \ "consentId" should not be null
response.body \ "consentStatus" should not be null
response.body \ "_links" \ "startAuthorisation" should not be null
}
}
feature("test the BG v1.3 Payment endpoints") {
scenario("Successful call endpoint initiatePayment", BerlinGroupPaymentV1_3) {
When("Post empty to call initiatePayment")
val accounts = MappedBankAccount.findAll().map(_.accountIban.get).filter(_ != null)
val ibanFrom = accounts.head
val ibanTo = accounts.last
val initiatePaymentJson =
s"""{
| "debtorAccount": {
| "iban": "${ibanFrom}"
| },
|"instructedAmount": {
| "currency": "EUR",
| "amount": "1234"
|},
|"creditorAccount": {
| "iban": "${ibanTo}"
|},
|"creditorName": "70charname"
}""".stripMargin
val requestPost = (urpPrefix / PaymentServiceTypes.bulk_payments.toString / TransactionRequestTypes.sepa_credit_transfers.toString).POST <@ (user1)
val response: APIResponse = makePostRequest(requestPost, initiatePaymentJson)
Then("We should get a 201 ")
response.code should equal(201)
response.body \ "transactionStatus" should not be null
response.body \ "paymentId" should not be null
response.body \ "_links" should not be null
response.body \ "_links" \ "scaRedirect" should not be null
response.body \ "_links" \ "self" should not be null
response.body \ "_links" \ "status" should not be null
response.body \ "_links" \ "scaStatus" should not be null
}
}
}

View File

@ -0,0 +1,63 @@
package code.api.berlin.group.v1_3
import code.api.ErrorMessage
import code.api.builder.ConfirmationOfFundsServicePIISApi.APIMethods_ConfirmationOfFundsServicePIISApi
import code.api.util.APIUtil.OAuth._
import code.api.util.ErrorMessages._
import code.setup.{APIResponse, DefaultUsers}
import com.github.dwickern.macros.NameOf.nameOf
import org.scalatest.Tag
class ConfirmationOfFundsServicePIISApiTest extends BerlinGroupServerSetupV1_3 with DefaultUsers {
object PIIS extends Tag("Confirmation of Funds Service (PIIS)")
object checkAvailabilityOfFunds extends Tag(nameOf(APIMethods_ConfirmationOfFundsServicePIISApi.checkAvailabilityOfFunds))
val consentJsonForPost =
"""{
| "access": {
| "accounts": [
| {
| "iban": "FR7612345987650123456789014",
| "bban": "BARC12345612345678",
| "pan": "5409050000000000",
| "maskedPan": "123456xxxxxx1234",
| "msisdn": "+49 170 1234567",
| "currency": "EUR"
| }
| ]
| },
| "recurringIndicator": false,
| "validUntil": "2020-12-31",
| "frequencyPerDay": 4,
| "combinedServiceIndicator": false
|}""".stripMargin
feature("test the BG v1.3 consent endpoints") {
scenario("Fail call endpoint createConsent, because accounts is not empty", PIIS) {
When("Post consent json with no empty accounts")
val requestPost = (V1_3_BG / "consents").POST <@ (user1)
val response: APIResponse = makePostRequest(requestPost, consentJsonForPost)
Then("We should get a 400 ")
response.code should equal(400)
response.body.extract[ErrorMessage]
.message should startWith(InvalidJsonContent)
}
scenario("Successful call endpoint createConsent", PIIS) {
When("Post consent json with no empty accounts")
val requestPost = (V1_3_BG / "consents").POST <@ (user1)
val emptyAccountsJson = consentJsonForPost.replaceFirst("""(?s)("accounts"\s*\:\s*\[).*?(\])""", "$1 $2")
val response: APIResponse = makePostRequest(requestPost, emptyAccountsJson)
Then("We should get a 201 ")
response.code should equal(201)
response.body \ "consentId" should not be null
response.body \ "consentStatus" should not be null
response.body \ "_links" \ "startAuthorisation" should not be null
}
}
}

View File

@ -0,0 +1,145 @@
package code.api.berlin.group.v1_3
import code.api.ErrorMessage
import code.api.berlin.group.v1_3.JSONFactory_BERLIN_GROUP_1_3.{InitiatePaymentResponseJson, StartPaymentAuthorisationJson}
import code.api.builder.PaymentInitiationServicePISApi.APIMethods_PaymentInitiationServicePISApi
import code.api.util.APIUtil.OAuth._
import code.api.util.ErrorMessages.{InvalidJsonFormat, NotPositiveAmount}
import code.model.dataAccess.MappedBankAccount
import code.setup.{APIResponse, DefaultUsers}
import code.transactionrequests.TransactionRequests.{PaymentServiceTypes, TransactionRequestTypes}
import com.github.dwickern.macros.NameOf.nameOf
import com.openbankproject.commons.model.SepaCreditTransfers
import org.scalatest.Tag
class PaymentInitiationServicePISApiTest extends BerlinGroupServerSetupV1_3 with DefaultUsers {
object PIS extends Tag("Payment Initiation Service (PIS)")
object initiatePayment extends Tag(nameOf(APIMethods_PaymentInitiationServicePISApi.initiatePayment))
object getPaymentCancellationScaStatus extends Tag(nameOf(APIMethods_PaymentInitiationServicePISApi.getPaymentCancellationScaStatus))
object getPaymentInitiationAuthorisation extends Tag(nameOf(APIMethods_PaymentInitiationServicePISApi.getPaymentInitiationAuthorisation))
object getPaymentInformation extends Tag(nameOf(APIMethods_PaymentInitiationServicePISApi.getPaymentInformation))
object getPaymentInitiationCancellationAuthorisationInformation extends Tag(nameOf(APIMethods_PaymentInitiationServicePISApi.getPaymentInitiationCancellationAuthorisationInformation))
object getPaymentInitiationScaStatus extends Tag(nameOf(APIMethods_PaymentInitiationServicePISApi.getPaymentInitiationScaStatus))
object getPaymentInitiationStatus extends Tag(nameOf(APIMethods_PaymentInitiationServicePISApi.getPaymentInitiationStatus))
object startPaymentAuthorisation extends Tag(nameOf(APIMethods_PaymentInitiationServicePISApi.startPaymentAuthorisation))
object startPaymentInitiationCancellationAuthorisation extends Tag(nameOf(APIMethods_PaymentInitiationServicePISApi.startPaymentInitiationCancellationAuthorisation))
object updatePaymentCancellationPsuData extends Tag(nameOf(APIMethods_PaymentInitiationServicePISApi.updatePaymentCancellationPsuData))
object updatePaymentPsuData extends Tag(nameOf(APIMethods_PaymentInitiationServicePISApi.updatePaymentPsuData))
lazy val accounts = MappedBankAccount.findAll().map(_.accountIban.get).filter(_ != null)
lazy val ibanFrom = accounts.head
lazy val ibanTo = accounts.last
lazy val wrongInitiatePaymentJson =
s"""{
|"instructedAmount1": {
| "currency": "EUR",
| "amount": "1234"
|},
|"creditorAccount": {
| "iban": "${ibanTo}"
|},
|"creditorName": "70charname"
}""".stripMargin
lazy val initiatePaymentJson =
s"""{
| "debtorAccount": {
| "iban": "${ibanFrom}"
| },
|"instructedAmount": {
| "currency": "EUR",
| "amount": "1234"
|},
|"creditorAccount": {
| "iban": "${ibanTo}"
|},
|"creditorName": "70charname"
}""".stripMargin
lazy val wrongAmountInitiatePaymentJson =
s"""{
| "debtorAccount": {
| "iban": "${ibanFrom}"
| },
|"instructedAmount": {
| "currency": "EUR",
| "amount": "-1234"
|},
|"creditorAccount": {
| "iban": "${ibanTo}"
|},
|"creditorName": "70charname"
}""".stripMargin
feature("test the BG v1.3 Payment endpoints- wrong json") {
scenario("Unsuccessful call endpoint initiatePayment", BerlinGroupV1_3, PIS, initiatePayment) {
When("Post empty to call initiatePayment")
val requestPost = (V1_3_BG / PaymentServiceTypes.bulk_payments.toString / TransactionRequestTypes.sepa_credit_transfers.toString).POST <@ (user1)
val response: APIResponse = makePostRequest(requestPost, wrongInitiatePaymentJson)
Then("We should get a 400 ")
response.code should equal(400)
val error = s"$InvalidJsonFormat The Json body should be the $SepaCreditTransfers "
And("error should be " + error)
response.body.extract[ErrorMessage].message should equal (error)
}
scenario("Unsuccessful call endpoint initiatePayment - wrong amount", BerlinGroupV1_3, PIS, initiatePayment) {
When("Post empty to call initiatePayment")
val requestPost = (V1_3_BG / PaymentServiceTypes.bulk_payments.toString / TransactionRequestTypes.sepa_credit_transfers.toString).POST <@ (user1)
val response: APIResponse = makePostRequest(requestPost, wrongAmountInitiatePaymentJson)
Then("We should get a 400 ")
response.code should equal(400)
val error = s"${NotPositiveAmount} Current input is: '-1234'"
And("error should be " + error)
response.body.extract[ErrorMessage].message should equal (error)
}
scenario("Successful call endpoint initiatePayment", BerlinGroupV1_3, PIS, initiatePayment) {
When("Post empty to call initiatePayment")
val requestPost = (V1_3_BG / PaymentServiceTypes.bulk_payments.toString / TransactionRequestTypes.sepa_credit_transfers.toString).POST <@ (user1)
val response: APIResponse = makePostRequest(requestPost, initiatePaymentJson)
Then("We should get a 201 ")
response.code should equal(201)
val payment = response.body.extract[InitiatePaymentResponseJson]
payment.transactionStatus should not be null
payment.paymentId should not be null
payment._links should not be null
payment._links.scaRedirect should not be null
payment._links.self should not be null
payment._links.status should not be null
payment._links.scaStatus should not be null
}
}
feature("test the BG v1.3 startPaymentInitiationCancellationAuthorisation") {
scenario("Successful call endpoint startPaymentInitiationCancellationAuthorisation", BerlinGroupV1_3, PIS, startPaymentInitiationCancellationAuthorisation) {
When("Post empty to call initiatePayment")
val requestPost = (V1_3_BG / PaymentServiceTypes.bulk_payments.toString / TransactionRequestTypes.sepa_credit_transfers.toString / "PAYMENT_ID" / "cancellation-authorisations").POST <@ (user1)
val response: APIResponse = makePostRequest(requestPost, """""")
Then("We should get a 200 ")
response.code should equal(200)
org.scalameta.logger.elem(response)
val payment = response.body.extract[StartPaymentAuthorisationJson]
payment.authorisationId should not be null
payment.psuMessage should not be null
payment.scaStatus should not be null
payment._links.scaStatus should not be null
}
}
feature("test the BG v1.3 startPaymentAuthorisation") {
scenario("Successful call endpoint startPaymentAuthorisation", BerlinGroupV1_3, PIS, startPaymentInitiationCancellationAuthorisation) {
When("Post empty to call initiatePayment")
val requestPost = (V1_3_BG / PaymentServiceTypes.bulk_payments.toString / TransactionRequestTypes.sepa_credit_transfers.toString / "PAYMENT_ID" / "authorisations").POST <@ (user1)
val response: APIResponse = makePostRequest(requestPost, """""")
Then("We should get a 200 ")
response.code should equal(200)
org.scalameta.logger.elem(response)
val payment = response.body.extract[StartPaymentAuthorisationJson]
payment.authorisationId should not be null
payment.psuMessage should not be null
payment.scaStatus should not be null
payment._links.scaStatus should not be null
}
}
}

View File

@ -3,6 +3,9 @@
### Most recent changes at top of file
```
Date Commit Action
05/07/2019 7032ce3 Added props: webui_sandbox_introduction, To display the introduction page for sandbox.
It supports the markdown format.It will show the introduction OBP-API home page `INTRODUCTION`
page and also for Glossary `Sandbox Introduction`.
14/06/2019 7a1c453 Added props: sca_phone_api_key and sca_phone_api_secret. We For now, OBP-API use `nexmo` server
as the SMS provider. Please check `nexmo` website, and get the api key and value there.
03/06/2019 5194b48 The table viewimpl is replaced with a table viewdefinition