refactor/Add type safe transaction statuses of Berlin Group

This commit is contained in:
Marko Milić 2025-04-15 13:47:14 +02:00
parent 796cc24758
commit f316a9f55f
5 changed files with 130 additions and 62 deletions

View File

@ -1,22 +1,20 @@
package code.api.berlin.group.v1_3
import java.text.SimpleDateFormat
import java.util.Date
import code.api.berlin.group.v1_3.model.TransactionStatus.mapTransactionStatus
import code.api.berlin.group.v1_3.model._
import code.api.util.APIUtil._
import code.api.util.ErrorMessages.MissingPropsValueAtThisInstance
import code.api.util.{APIUtil, ConsentJWT, CustomJsonFormats, JwtUtil}
import code.bankconnectors.Connector
import code.consent.ConsentTrait
import code.model.ModeratedTransaction
import com.openbankproject.commons.model.enums.AccountRoutingScheme
import com.openbankproject.commons.model.{BankAccount, TransactionRequest, User, _}
import com.openbankproject.commons.model._
import net.liftweb.common.Box.tryo
import net.liftweb.common.{Box, Full}
import net.liftweb.json
import net.liftweb.json.{JValue, parse}
import scala.collection.immutable.List
import java.text.SimpleDateFormat
import java.util.Date
case class JvalueCaseClass(jvalueToCaseclass: JValue)
@ -649,10 +647,7 @@ object JSONFactory_BERLIN_GROUP_1_3 extends CustomJsonFormats {
val scaRedirectUrl = getPropsValue("psu_make_payment_sca_redirect_url")
.openOr(MissingPropsValueAtThisInstance + "psu_make_payment_sca_redirect_url")
InitiatePaymentResponseJson(
transactionStatus = transactionRequest.status match {
case "COMPLETED" => "ACCP"
case "INITIATED" => "RCVD"
},
transactionStatus = mapTransactionStatus(transactionRequest.status),
paymentId = paymentId,
_links = InitiatePaymentResponseLinks(
scaRedirect = LinkHrefJson(s"$scaRedirectUrl/$paymentId"),

View File

@ -1,27 +1,22 @@
package code.api.builder.PaymentInitiationServicePISApi
import code.api.Constant
import code.api.berlin.group.v1_3.JSONFactory_BERLIN_GROUP_1_3.{CancelPaymentResponseJson, CancelPaymentResponseLinks, LinkHrefJson, UpdatePaymentPsuDataJson, checkAuthorisationConfirmation, checkSelectPsuAuthenticationMethod, checkTransactionAuthorisation, checkUpdatePsuAuthentication, createCancellationTransactionRequestJson}
import code.api.berlin.group.v1_3.{JSONFactory_BERLIN_GROUP_1_3, JvalueCaseClass, OBP_BERLIN_GROUP_1_3}
import code.api.berlin.group.v1_3.model.TransactionStatus.mapTransactionStatus
import code.api.berlin.group.v1_3.model._
import code.api.berlin.group.v1_3.{JSONFactory_BERLIN_GROUP_1_3, JvalueCaseClass}
import code.api.util.APIUtil._
import code.api.util.ApiTag._
import code.api.util.ErrorMessages._
import code.api.util.NewStyle.HttpCode
import code.api.util.{ApiRole, ApiTag, CallContext, NewStyle}
import code.api.berlin.group.v1_3.model._
import code.bankconnectors.Connector
import code.api.util.{ApiTag, CallContext, NewStyle}
import code.fx.fx
import code.api.Constant._
import code.util.Helper
import code.views.Views
import com.github.dwickern.macros.NameOf.nameOf
import com.openbankproject.commons.ExecutionContext.Implicits.global
import com.openbankproject.commons.model._
import com.openbankproject.commons.model.enums.ChallengeType.BERLIN_GROUP_PAYMENT_CHALLENGE
import com.openbankproject.commons.model.enums.TransactionRequestStatus._
import com.openbankproject.commons.model.enums.{ChallengeType, StrongCustomerAuthenticationStatus, SuppliedAnswerType, TransactionRequestStatus,TransactionRequestTypes,PaymentServiceTypes}
import com.openbankproject.commons.model.enums.TransactionRequestTypes._
import com.openbankproject.commons.model.enums.PaymentServiceTypes._
import com.openbankproject.commons.model.enums.{TransactionRequestStatus, _}
import com.openbankproject.commons.util.ApiVersion
import net.liftweb
import net.liftweb.common.Box.tryo
@ -29,10 +24,8 @@ import net.liftweb.common.Full
import net.liftweb.http.js.JE.JsRaw
import net.liftweb.http.rest.RestHelper
import net.liftweb.json
import net.liftweb.json.Serialization.write
import net.liftweb.json._
import scala.collection.immutable.Nil
import scala.collection.mutable.ArrayBuffer
import scala.concurrent.Future
@ -136,7 +129,7 @@ or * access method is generally applicable, but further authorisation processes
(canBeCancelled, _, startSca) <- transactionRequestTypes match {
case TransactionRequestTypes.SEPA_CREDIT_TRANSFERS => {
transactionRequest.status.toUpperCase() match {
case "COMPLETED" =>
case TransactionStatus.ACCP.code =>
NewStyle.function.cancelPaymentV400(TransactionId(transactionRequest.transaction_ids), callContext) map {
x => x._1 match {
case CancelPayment(true, Some(startSca)) if startSca == true =>
@ -399,8 +392,8 @@ This method returns the SCA status of a payment initiation's authorisation sub-r
s"""${mockedDataText(false)}
Check the transaction status of a payment initiation.""",
EmptyBody,
json.parse("""{
"transactionStatus": "ACCP"
json.parse(s"""{
"transactionStatus": "${TransactionStatus.ACCP.code}"
}"""),
List(UserNotLoggedIn, UnknownError),
ApiTag("Payment Initiation Service (PIS)") :: apiTagBerlinGroupM :: Nil
@ -420,12 +413,9 @@ Check the transaction status of a payment initiation.""",
}
(transactionRequest, callContext) <- NewStyle.function.getTransactionRequestImpl(TransactionRequestId(paymentId), callContext)
transactionRequestStatus = transactionRequest.status match {
case "COMPLETED" => "ACCP"
case "INITIATED" => "RCVD"
}
transactionRequestStatus = mapTransactionStatus(transactionRequest.status)
transactionRequestAmount <- NewStyle.function.tryons(s"${UnknownError} transction request amount can not convert to a Decimal",400, callContext) {
transactionRequestAmount <- NewStyle.function.tryons(s"${UnknownError} transaction request amount can not convert to a Decimal",400, callContext) {
BigDecimal(transactionRequest.body.to_sepa_credit_transfers.get.instructedAmount.amount)
}
transactionRequestCurrency <- NewStyle.function.tryons(s"${UnknownError} can not get currency from this paymentId(${paymentId})",400, callContext) {
@ -450,7 +440,7 @@ Check the transaction status of a payment initiation.""",
fundsAvailable = (fromAccountBalance >= requestChangedCurrencyAmount)
transactionRequestStatusChekedFunds = if(fundsAvailable) transactionRequestStatus else "RCVD"
transactionRequestStatusChekedFunds = if(fundsAvailable) transactionRequestStatus else TransactionStatus.RCVD.code
} yield {
(json.parse(s"""{
@ -534,7 +524,7 @@ Check the transaction status of a payment initiation.""",
}
//Berlin Group PaymentProduct is OBP transaction request type
transacitonRequestType <- NewStyle.function.tryons(checkPaymentProductError(paymentProduct), 400, callContext) {
transactionRequestType <- NewStyle.function.tryons(checkPaymentProductError(paymentProduct), 400, callContext) {
TransactionRequestTypes.withName(paymentProduct.replaceAll("-", "_").toUpperCase)
}
@ -565,13 +555,13 @@ Check the transaction status of a payment initiation.""",
_ <- NewStyle.function.isEnabledTransactionRequests(callContext)
(createdTransactionRequest, callContext) <- transacitonRequestType match {
(createdTransactionRequest, callContext) <- transactionRequestType match {
case TransactionRequestTypes.SEPA_CREDIT_TRANSFERS => {
for {
(createdTransactionRequest, callContext) <- NewStyle.function.createTransactionRequestBGV1(
initiator = u,
paymentServiceType,
transacitonRequestType,
transactionRequestType,
transactionRequestBody = sepaCreditTransfersBerlinGroupV13,
callContext
)
@ -606,7 +596,7 @@ Check the transaction status of a payment initiation.""",
"creditorName": "70charname"
}"""),
json.parse(s"""{
"transactionStatus": "RCVD",
"transactionStatus": "${TransactionStatus.RCVD.code}",
"paymentId": "1234-wertiq-983",
"_links":
{
@ -655,7 +645,7 @@ Check the transaction status of a payment initiation.""",
"dayOfExecution": "01"
}"""),
json.parse(s"""{
"transactionStatus": "RCVD",
"transactionStatus": "${TransactionStatus.RCVD.code}",
"paymentId": "1234-wertiq-983",
"_links":
{
@ -717,7 +707,7 @@ Check the transaction status of a payment initiation.""",
]
}"""),
json.parse(s"""{
"transactionStatus": "RCVD",
"transactionStatus": "${TransactionStatus.RCVD.code}",
"paymentId": "1234-wertiq-983",
"_links":
{
@ -1449,7 +1439,7 @@ There are the following request types on this access path:
transactionRequestId = TransactionRequestId(paymentId)
(existingTransactionRequest, callContext) <- NewStyle.function.getTransactionRequestImpl(transactionRequestId, callContext)
_ <- Helper.booleanToFuture(failMsg= CannotUpdatePSUData, cc=callContext) {
existingTransactionRequest.status == TransactionRequestStatus.INITIATED.toString
existingTransactionRequest.status == TransactionStatus.RCVD.code
}
(_, callContext) <- NewStyle.function.getChallenge(authorisationId, callContext)
(challenge, callContext) <- NewStyle.function.validateChallengeAnswerC4(

View File

@ -11,8 +11,98 @@
*/
package code.api.berlin.group.v1_3.model
sealed trait TransactionStatus {
def code: String
def description: String
}
object TransactionStatus extends ApiModel {
case object ACCC extends TransactionStatus {
val code = "ACCC"
val description = "AcceptedSettlementCompleted - Settlement on the creditor's account has been completed."
}
case object ACCP extends TransactionStatus {
val code = "ACCP"
val description = "AcceptedCustomerProfile - Technical validation and customer profile check successful."
}
case object ACSC extends TransactionStatus {
val code = "ACSC"
val description = "AcceptedSettlementCompleted - Settlement on the debtors account has been completed."
}
case object ACSP extends TransactionStatus {
val code = "ACSP"
val description = "AcceptedSettlementInProcess - Payment initiation accepted for execution."
}
case object ACTC extends TransactionStatus {
val code = "ACTC"
val description = "AcceptedTechnicalValidation - Authentication and validation successful."
}
case object ACWC extends TransactionStatus {
val code = "ACWC"
val description = "AcceptedWithChange - Instruction accepted but changes made (e.g. date or remittance)."
}
case object ACWP extends TransactionStatus {
val code = "ACWP"
val description = "AcceptedWithoutPosting - Accepted but not posted to creditors account."
}
case object RCVD extends TransactionStatus {
val code = "RCVD"
val description = "Received - Payment initiation received by receiving agent."
}
case object PDNG extends TransactionStatus {
val code = "PDNG"
val description = "Pending - Further checks pending before completion."
}
case object RJCT extends TransactionStatus {
val code = "RJCT"
val description = "Rejected - Payment initiation or transaction has been rejected."
}
case object CANC extends TransactionStatus {
val code = "CANC"
val description = "Cancelled - Payment initiation cancelled before execution."
}
case object ACFC extends TransactionStatus {
val code = "ACFC"
val description = "AcceptedFundsChecked - Technical, profile, and funds check successful."
}
case object PATC extends TransactionStatus {
val code = "PATC"
val description = "PartiallyAcceptedTechnical - Some required authentications performed."
}
case object PART extends TransactionStatus {
val code = "PART"
val description = "PartiallyAccepted - Some transactions accepted in a bulk payment."
}
val values: List[TransactionStatus] = List(
ACCC, ACCP, ACSC, ACSP, ACTC, ACWC, ACWP, RCVD,
PDNG, RJCT, CANC, ACFC, PATC, PART
)
def fromCode(code: String): Option[TransactionStatus] = values.find(_.code == code)
def mapTransactionStatus(status: String): String = {
status match {
case "COMPLETED" => TransactionStatus.ACCP.code
case "INITIATED" => TransactionStatus.RCVD.code
case other => other
}
}
}
case class TransactionStatus (
) extends ApiModel

View File

@ -2,6 +2,7 @@ package code.bankconnectors
import code.fx.fx.TTL
import code.api.Constant._
import code.api.berlin.group.v1_3.model.TransactionStatus.mapTransactionStatus
import code.api.cache.Caching
import code.api.util.APIUtil._
import code.api.util.ErrorMessages._
@ -153,7 +154,7 @@ object LocalMappedConnectorInternal extends MdcLoggable {
""
),
transDetailsSerialized,
status.toString,
mapTransactionStatus(status.toString),
charge,
"", // chargePolicy is not used in BG so far.
Some(paymentServiceType.toString),

View File

@ -1,31 +1,23 @@
package code.api.berlin.group.v1_3
import code.api.BerlinGroup.ScaStatus
import code.api.Constant
import code.api.Constant.{SYSTEM_INITIATE_PAYMENTS_BERLIN_GROUP_VIEW_ID, SYSTEM_READ_TRANSACTIONS_BERLIN_GROUP_VIEW_ID}
import code.api.berlin.group.v1_3.JSONFactory_BERLIN_GROUP_1_3.{CancellationJsonV13, InitiatePaymentResponseJson, StartPaymentAuthorisationJson}
import code.api.berlin.group.v1_3.model.{PsuData, ScaStatusResponse, UpdatePsuAuthenticationResponse}
import code.api.Constant.SYSTEM_INITIATE_PAYMENTS_BERLIN_GROUP_VIEW_ID
import code.api.berlin.group.v1_3.JSONFactory_BERLIN_GROUP_1_3.{CancellationJsonV13, ErrorMessagesBG, InitiatePaymentResponseJson, StartPaymentAuthorisationJson}
import code.api.berlin.group.v1_3.model.{ScaStatusResponse, TransactionStatus, UpdatePsuAuthenticationResponse}
import code.api.builder.PaymentInitiationServicePISApi.APIMethods_PaymentInitiationServicePISApi
import code.api.util.APIUtil.OAuth._
import code.api.util.APIUtil.extractErrorMessageCode
import code.api.util.ErrorMessages.{AuthorisationNotFound, InvalidJsonFormat, NotPositiveAmount, _}
import code.api.util.ErrorMessages._
import code.model.dataAccess.{BankAccountRouting, MappedBankAccount}
import code.setup.{APIResponse, DefaultUsers}
import com.openbankproject.commons.model.enums.TransactionRequestTypes
import com.openbankproject.commons.model.enums.TransactionRequestTypes._
import com.openbankproject.commons.model.enums.PaymentServiceTypes
import com.openbankproject.commons.model.enums.PaymentServiceTypes._
import code.views.Views
import com.github.dwickern.macros.NameOf.nameOf
import com.openbankproject.commons.model.enums.AccountRoutingScheme
import com.openbankproject.commons.model.{ErrorMessage, SepaCreditTransfers, SepaCreditTransfersBerlinGroupV13, ViewId}
import com.openbankproject.commons.model.enums.{AccountRoutingScheme, PaymentServiceTypes, TransactionRequestTypes}
import com.openbankproject.commons.model.{SepaCreditTransfers, SepaCreditTransfersBerlinGroupV13, ViewId}
import net.liftweb.json.Serialization.write
import net.liftweb.mapper.By
import org.scalatest.Tag
import scala.collection.immutable.List
class PaymentInitiationServicePISApiTest extends BerlinGroupServerSetupV1_3 with DefaultUsers {
object PIS extends Tag("Payment Initiation Service (PIS)")
@ -137,7 +129,7 @@ class PaymentInitiationServicePISApiTest extends BerlinGroupServerSetupV1_3 with
Then("We should get a 201 ")
response.code should equal(201)
val payment = response.body.extract[InitiatePaymentResponseJson]
payment.transactionStatus should be ("ACCP")
payment.transactionStatus should be (TransactionStatus.ACCP.code)
payment.paymentId should not be null
payment._links.scaStatus should not be null
@ -190,7 +182,7 @@ class PaymentInitiationServicePISApiTest extends BerlinGroupServerSetupV1_3 with
Then("We should get a 201 ")
response.code should equal(201)
val payment = response.body.extract[InitiatePaymentResponseJson]
payment.transactionStatus should be ("RCVD")
payment.transactionStatus should be (TransactionStatus.RCVD.code)
payment.paymentId should not be null
payment._links.scaStatus should not be null
@ -248,7 +240,7 @@ class PaymentInitiationServicePISApiTest extends BerlinGroupServerSetupV1_3 with
Then("We should get a 201 ")
response.code should equal(201)
val payment = response.body.extract[InitiatePaymentResponseJson]
payment.transactionStatus should be ("ACCP")
payment.transactionStatus should be (TransactionStatus.ACCP.code)
payment.paymentId should not be null
Then(s"we test the ${getPaymentInformation.name}")
@ -292,7 +284,7 @@ class PaymentInitiationServicePISApiTest extends BerlinGroupServerSetupV1_3 with
Then("We should get a 201 ")
response.code should equal(201)
val payment = response.body.extract[InitiatePaymentResponseJson]
payment.transactionStatus should be ("RCVD")
payment.transactionStatus should be (TransactionStatus.RCVD.code)
payment.paymentId should not be null
payment._links.scaStatus should not be null
@ -301,7 +293,7 @@ class PaymentInitiationServicePISApiTest extends BerlinGroupServerSetupV1_3 with
val requestGet = (V1_3_BG / PaymentServiceTypes.payments.toString / TransactionRequestTypes.SEPA_CREDIT_TRANSFERS.toString / paymentId / "status").GET <@ (user1)
val responseGet: APIResponse = makeGetRequest(requestGet)
responseGet.code should be (200)
(responseGet.body \ "transactionStatus").extract[String] should be ("RCVD")
(responseGet.body \ "transactionStatus").extract[String] should be (TransactionStatus.RCVD.code)
(responseGet.body \ "fundsAvailable").extract[Boolean] should be (true)
}
}
@ -351,7 +343,7 @@ class PaymentInitiationServicePISApiTest extends BerlinGroupServerSetupV1_3 with
Then("We should get a 201 ")
responseInitiatePaymentJson.code should equal(201)
val paymentResponseInitiatePaymentJson = responseInitiatePaymentJson.body.extract[InitiatePaymentResponseJson]
paymentResponseInitiatePaymentJson.transactionStatus should be ("RCVD")
paymentResponseInitiatePaymentJson.transactionStatus should be (TransactionStatus.RCVD.code)
paymentResponseInitiatePaymentJson.paymentId should not be null
val paymentId = paymentResponseInitiatePaymentJson.paymentId
@ -502,7 +494,7 @@ class PaymentInitiationServicePISApiTest extends BerlinGroupServerSetupV1_3 with
Then("We should get a 201 ")
responseInitiatePaymentJson.code should equal(201)
val paymentResponseInitiatePaymentJson = responseInitiatePaymentJson.body.extract[InitiatePaymentResponseJson]
paymentResponseInitiatePaymentJson.transactionStatus should be ("ACCP")
paymentResponseInitiatePaymentJson.transactionStatus should be (TransactionStatus.ACCP.code)
val paymentId = paymentResponseInitiatePaymentJson.paymentId