Skip to content

Commit 35f19e7

Browse files
authored
Merge pull request #2749 from hongwei1/feature/ttksandbox
Feature/tweaked SigningBasket endpoints
2 parents cf56697 + 55ed03b commit 35f19e7

File tree

3 files changed

+96
-9
lines changed

3 files changed

+96
-9
lines changed

obp-api/src/main/scala/code/api/berlin/group/v1_3/SigningBasketsApi.scala

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -495,7 +495,14 @@ There are the following request types on this access path:
495495
val basket = SigningBasketX.signingBasketProvider.vend.getSigningBasketByBasketId(basketId)
496496
val existAll: Box[Boolean] =
497497
basket.flatMap(_.payments.map(_.forall(i => Connector.connector.vend.getTransactionRequestImpl(TransactionRequestId(i), callContext).isDefined)))
498-
if (existAll.getOrElse(false)) {
498+
val alreadyCompleted: List[String] =
499+
basket.flatMap(_.payments).getOrElse(Nil).filter { i =>
500+
Connector.connector.vend.getTransactionRequestImpl(TransactionRequestId(i), callContext)
501+
.exists(_._1.status == COMPLETED.toString)
502+
}
503+
if (alreadyCompleted.nonEmpty) {
504+
unboxFullOrFail(Empty, callContext, s"$InvalidConnectorResponse Some of paymentIds [${alreadyCompleted.mkString(",")}] are already completed")
505+
} else if (existAll.getOrElse(false)) {
499506
basket.map { i =>
500507
i.payments.map(_.map { i =>
501508
NewStyle.function.saveTransactionRequestStatusImpl(TransactionRequestId(i), COMPLETED.toString, callContext)

obp-api/src/test/scala/code/api/berlin/group/v1_3/SigningBasketServiceSBSApiTest.scala

Lines changed: 78 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,19 @@
11
package code.api.berlin.group.v1_3
22

3+
import code.api.Constant.SYSTEM_INITIATE_PAYMENTS_BERLIN_GROUP_VIEW_ID
34
import code.api.berlin.group.ConstantsBG
4-
import code.api.berlin.group.v1_3.JSONFactory_BERLIN_GROUP_1_3.{AuthorisationJsonV13, ErrorMessagesBG, PostSigningBasketJsonV13, ScaStatusJsonV13, SigningBasketGetResponseJson, SigningBasketResponseJson, StartPaymentAuthorisationJson}
5+
import code.api.berlin.group.v1_3.JSONFactory_BERLIN_GROUP_1_3.{AuthorisationJsonV13, ErrorMessagesBG, InitiatePaymentResponseJson, PostSigningBasketJsonV13, ScaStatusJsonV13, SigningBasketGetResponseJson, SigningBasketResponseJson, StartPaymentAuthorisationJson}
6+
import code.api.berlin.group.v1_3.model.TransactionStatus
57
import code.api.builder.SigningBasketsApi.APIMethods_SigningBasketsApi
68
import code.api.util.APIUtil.OAuth._
79
import code.api.util.ErrorMessages._
10+
import code.model.dataAccess.BankAccountRouting
811
import code.setup.{APIResponse, DefaultUsers}
12+
import code.views.Views
913
import com.github.dwickern.macros.NameOf.nameOf
10-
import com.openbankproject.commons.model.enums.StrongCustomerAuthenticationStatus
14+
import com.openbankproject.commons.model.ViewId
15+
import com.openbankproject.commons.model.enums.{AccountRoutingScheme, PaymentServiceTypes, StrongCustomerAuthenticationStatus, TransactionRequestTypes}
16+
import net.liftweb.mapper.By
1117
import org.scalatest.Tag
1218

1319
class SigningBasketServiceSBSApiTest extends BerlinGroupServerSetupV1_3 with DefaultUsers {
@@ -21,6 +27,29 @@ class SigningBasketServiceSBSApiTest extends BerlinGroupServerSetupV1_3 with Def
2127
object getSigningBasketAuthorisation extends Tag(nameOf(APIMethods_SigningBasketsApi.getSigningBasketAuthorisation))
2228
object updateSigningBasketPsuData extends Tag(nameOf(APIMethods_SigningBasketsApi.updateSigningBasketPsuData))
2329

30+
// Helper: create a real SEPA payment via BG PIS API and return its paymentId
31+
private def createRealPaymentId(): String = {
32+
val accountsRoutingIban = BankAccountRouting.findAll(By(BankAccountRouting.AccountRoutingScheme, AccountRoutingScheme.IBAN.toString))
33+
val ibanFrom = accountsRoutingIban.head
34+
val ibanTo = accountsRoutingIban.last
35+
Views.views.vend.systemView(ViewId(SYSTEM_INITIATE_PAYMENTS_BERLIN_GROUP_VIEW_ID)).foreach(view =>
36+
Views.views.vend.grantAccessToSystemView(ibanFrom.bankId, ibanFrom.accountId, view, resourceUser1)
37+
)
38+
val initiatePaymentJson =
39+
s"""{
40+
| "debtorAccount": { "iban": "${ibanFrom.accountRouting.address}" },
41+
| "instructedAmount": { "currency": "EUR", "amount": "10" },
42+
| "creditorAccount": { "iban": "${ibanTo.accountRouting.address}" },
43+
| "creditorName": "TestCreditor"
44+
|}""".stripMargin
45+
val requestPost = (V1_3_BG / PaymentServiceTypes.payments.toString / TransactionRequestTypes.SEPA_CREDIT_TRANSFERS.toString).POST <@ (user1)
46+
val response: APIResponse = makePostRequest(requestPost, initiatePaymentJson)
47+
response.code should equal(201)
48+
val payment = response.body.extract[InitiatePaymentResponseJson]
49+
payment.transactionStatus should be(TransactionStatus.ACCP.code)
50+
payment.paymentId
51+
}
52+
2453
feature(s"test the BG v1.3 - ${createSigningBasket.name}") {
2554
scenario("Failed Case - Unauthenticated Access", BerlinGroupV1_3, SBS, createSigningBasket) {
2655
val postJson =
@@ -61,21 +90,26 @@ class SigningBasketServiceSBSApiTest extends BerlinGroupServerSetupV1_3 with Def
6190
}
6291
}
6392

64-
// TODO Add check that paymentId is an existing transaction request
6593
feature(s"test the BG v1.3 -${createSigningBasket.name}") {
66-
scenario("Failed Case - Successful", BerlinGroupV1_3, SBS, createSigningBasket) {
94+
scenario("Success Case - 201 with real paymentId and response body validation", BerlinGroupV1_3, SBS, createSigningBasket) {
95+
val realPaymentId = createRealPaymentId()
6796
val postJson =
6897
s"""{
6998
| "paymentIds": [
70-
| "123qwert456789",
71-
| "12345qwert7899"
99+
| "${realPaymentId}"
72100
| ]
73101
|}""".stripMargin
74102

75103
val requestPost = (V1_3_BG / "signing-baskets").POST <@ (user1)
76104
val response: APIResponse = makePostRequest(requestPost, postJson)
77-
Then("We should get a 201 ")
105+
Then("We should get a 201")
78106
response.code should equal(201)
107+
val createdBasket = response.body.extract[SigningBasketResponseJson]
108+
createdBasket.basketId should not be empty
109+
createdBasket.transactionStatus should be(ConstantsBG.SigningBasketsStatus.RCVD.toString.toLowerCase())
110+
createdBasket._links.self.href should not be empty
111+
createdBasket._links.status.href should not be empty
112+
createdBasket._links.startAuthorisation.href should not be empty
79113
}
80114
}
81115

@@ -90,6 +124,43 @@ class SigningBasketServiceSBSApiTest extends BerlinGroupServerSetupV1_3 with Def
90124
And("error should be " + error)
91125
responseGet.body.extract[ErrorMessagesBG].tppMessages.head.text should startWith(error)
92126
}
127+
scenario("Success Case - 200 with multiple real paymentIds and payment status validation", BerlinGroupV1_3, SBS, getSigningBasket) {
128+
// Create two real payments, then create a basket referencing both
129+
val paymentId1 = createRealPaymentId()
130+
val paymentId2 = createRealPaymentId()
131+
val postJson =
132+
s"""{
133+
| "paymentIds": [
134+
| "${paymentId1}",
135+
| "${paymentId2}"
136+
| ]
137+
|}""".stripMargin
138+
val requestPost = (V1_3_BG / "signing-baskets").POST <@ (user1)
139+
val responsePost: APIResponse = makePostRequest(requestPost, postJson)
140+
responsePost.code should equal(201)
141+
val basketId = responsePost.body.extract[SigningBasketResponseJson].basketId
142+
143+
// Verify basket GET returns correct data including both real paymentIds
144+
val requestGet = (V1_3_BG / "signing-baskets" / basketId).GET <@ (user1)
145+
val responseGet = makeGetRequest(requestGet)
146+
Then("We should get a 200")
147+
responseGet.code should be(200)
148+
val basket = responseGet.body.extract[SigningBasketGetResponseJson]
149+
basket.transactionStatus should be(ConstantsBG.SigningBasketsStatus.RCVD.toString.toLowerCase())
150+
basket.payments.isDefined should be(true)
151+
basket.payments.get should contain(paymentId1)
152+
basket.payments.get should contain(paymentId2)
153+
154+
// Verify each paymentId in the basket has ACCP status
155+
Then("Each payment in the basket should return ACCP status")
156+
basket.payments.get.foreach { pid =>
157+
val requestPaymentStatus = (V1_3_BG / PaymentServiceTypes.payments.toString / TransactionRequestTypes.SEPA_CREDIT_TRANSFERS.toString / pid / "status").GET <@ (user1)
158+
val responsePaymentStatus = makeGetRequest(requestPaymentStatus)
159+
responsePaymentStatus.code should be(200)
160+
val txStatus = (responsePaymentStatus.body \ "transactionStatus").extract[String]
161+
txStatus should be(TransactionStatus.ACCP.code)
162+
}
163+
}
93164
}
94165

95166
feature(s"test the BG v1.3 - ${getSigningBasketStatus.name}") {

obp-api/src/test/scala/code/setup/ServerSetup.scala

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,9 +60,18 @@ trait ServerSetup extends FeatureSpec with SendServerRequests
6060
setPropsValues("starConnector_supported_types" -> "mapped,internal,cardano_vJun2025")
6161
setPropsValues("connector" -> "star")
6262

63-
// Berlin Group
63+
// Berlin Group - set in trait body for initial setup
6464
setPropsValues("berlin_group_mandatory_headers" -> "")
6565
setPropsValues("berlin_group_mandatory_header_consent" -> "")
66+
67+
override def beforeEach(): Unit = {
68+
super.beforeEach()
69+
// Re-apply Berlin Group props after each PropsReset.afterEach() restores lockedProviders
70+
setPropsValues(
71+
"berlin_group_mandatory_headers" -> "",
72+
"berlin_group_mandatory_header_consent" -> ""
73+
)
74+
}
6675

6776
// Set system properties to force Pekko to use random available ports
6877
// This prevents conflicts when both RunWebApp and tests are running

0 commit comments

Comments
 (0)