From 453bb36271acd2c66e9d7240b530d24292095db1 Mon Sep 17 00:00:00 2001 From: Sakthivel Balasubramaniam Date: Fri, 11 Sep 2026 17:09:51 +0200 Subject: [PATCH 1/6] feat(payments): add wire type constants and function registry Register US Cross Border Wire, US FX Foreign Wire, and FCY Wire in CommonConstants and PaymentsFunctionService with EUR currency mapping for FX/FCY approval and limit configuration. Co-authored-by: Cursor Co-authored-by: AI-SDLC --- .../ct/bbfuel/data/CommonConstants.java | 6 ++++++ .../service/PaymentsFunctionService.java | 19 ++++++++++++++++--- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/backbase/ct/bbfuel/data/CommonConstants.java b/src/main/java/com/backbase/ct/bbfuel/data/CommonConstants.java index ab212ab2..4ffc8bac 100644 --- a/src/main/java/com/backbase/ct/bbfuel/data/CommonConstants.java +++ b/src/main/java/com/backbase/ct/bbfuel/data/CommonConstants.java @@ -35,6 +35,9 @@ private CommonConstants() { public static final String US_DOMESTIC_WIRE_FUNCTION_NAME = "US Domestic Wire"; public static final String US_DOMESTIC_WIRE_INTRACOMPANY_FUNCTION_NAME = "US Domestic Wire - Intracompany"; public static final String US_FOREIGN_WIRE_FUNCTION_NAME = "US Foreign Wire"; + public static final String US_CROSS_BORDER_WIRE_FUNCTION_NAME = "US Cross Border Wire"; + public static final String US_FX_FOREIGN_WIRE_FUNCTION_NAME = "US FX Foreign Wire"; + public static final String FCY_WIRE_FUNCTION_NAME = "FCY Wire"; public static final String PRODUCT_SUMMARY_RESOURCE_NAME = "Product Summary"; public static final String PAYMENTS_RESOURCE_NAME = "Payments"; public static final String CONTACTS_RESOURCE_NAME = "Contacts"; @@ -116,6 +119,9 @@ private CommonConstants() { public static final String PAYMENT_TYPE_US_DOMESTIC_WIRE = "US_DOMESTIC_WIRE"; public static final String PAYMENT_TYPE_ACH_DEBIT = "ACH_DEBIT"; public static final String PAYMENT_TYPE_US_FOREIGN_WIRE = "US_FOREIGN_WIRE"; + public static final String PAYMENT_TYPE_US_CROSS_BORDER_WIRE = "US_CROSS_BORDER_WIRE"; + public static final String PAYMENT_TYPE_US_FX_FOREIGN_WIRE = "US_FX_FOREIGN_WIRE"; + public static final String PAYMENT_TYPE_FCY_WIRE = "FCY_WIRE"; public static final String PROPERTY_PAYMENTS_OOTB_TYPES = "payments.ootb.types"; // Contacts diff --git a/src/main/java/com/backbase/ct/bbfuel/service/PaymentsFunctionService.java b/src/main/java/com/backbase/ct/bbfuel/service/PaymentsFunctionService.java index 4ad6c285..7595c541 100644 --- a/src/main/java/com/backbase/ct/bbfuel/service/PaymentsFunctionService.java +++ b/src/main/java/com/backbase/ct/bbfuel/service/PaymentsFunctionService.java @@ -13,7 +13,10 @@ import static com.backbase.ct.bbfuel.data.CommonConstants.SEPA_CT_INTRACOMPANY_FUNCTION_NAME; import static com.backbase.ct.bbfuel.data.CommonConstants.US_DOMESTIC_WIRE_FUNCTION_NAME; import static com.backbase.ct.bbfuel.data.CommonConstants.US_DOMESTIC_WIRE_INTRACOMPANY_FUNCTION_NAME; +import static com.backbase.ct.bbfuel.data.CommonConstants.FCY_WIRE_FUNCTION_NAME; +import static com.backbase.ct.bbfuel.data.CommonConstants.US_CROSS_BORDER_WIRE_FUNCTION_NAME; import static com.backbase.ct.bbfuel.data.CommonConstants.US_FOREIGN_WIRE_FUNCTION_NAME; +import static com.backbase.ct.bbfuel.data.CommonConstants.US_FX_FOREIGN_WIRE_FUNCTION_NAME; import static java.util.Arrays.asList; import static java.util.Collections.unmodifiableList; @@ -37,6 +40,9 @@ public final class PaymentsFunctionService { US_DOMESTIC_WIRE_FUNCTION_NAME, US_DOMESTIC_WIRE_INTRACOMPANY_FUNCTION_NAME, US_FOREIGN_WIRE_FUNCTION_NAME, + US_CROSS_BORDER_WIRE_FUNCTION_NAME, + US_FX_FOREIGN_WIRE_FUNCTION_NAME, + FCY_WIRE_FUNCTION_NAME, ACH_DEBIT_FUNCTION_NAME)); /** @@ -55,14 +61,21 @@ public final class PaymentsFunctionService { ); /** - * Simple logic is used to determine the currency from the business function name. - * If it contains SEPA (case insensitive) it is EUR otherwise USD. + * Determines the currency from the business function name. + * SEPA, US FX Foreign Wire, and FCY Wire use EUR; other payment functions use USD. * * @param functionName business function name * @return currency */ public static String determineCurrencyForFunction(String functionName) { - return functionName.toUpperCase().contains("SEPA") ? "EUR" : "USD"; + if (functionName.toUpperCase().contains("SEPA")) { + return "EUR"; + } + if (US_FX_FOREIGN_WIRE_FUNCTION_NAME.equals(functionName) + || FCY_WIRE_FUNCTION_NAME.equals(functionName)) { + return "EUR"; + } + return "USD"; } } From cbb45d73971429ff70772ec949e19033d8992321 Mon Sep 17 00:00:00 2001 From: Sakthivel Balasubramaniam Date: Fri, 11 Sep 2026 17:10:13 +0200 Subject: [PATCH 2/6] feat(entitlements): grant privileges for new US wire types Add US Cross Border Wire, US FX Foreign Wire, and FCY Wire to job profiles mirroring US Foreign Wire and extend test entitlement CSV fixtures with bootstrap-job function IDs 1267-1269. Co-authored-by: Cursor Co-authored-by: AI-SDLC --- src/main/resources/data/job-profiles.json | 147 ++++++++++++++++++ .../data/minimal-bank/job-profiles.json | 33 ++++ .../resources/data/retail/job-profiles.json | 33 ++++ ...tlements-applicable-function-privilege.csv | 18 +++ .../dbs-entitlements-business-functions.csv | 3 + 5 files changed, 234 insertions(+) diff --git a/src/main/resources/data/job-profiles.json b/src/main/resources/data/job-profiles.json index 7eea1380..f7b547ac 100644 --- a/src/main/resources/data/job-profiles.json +++ b/src/main/resources/data/job-profiles.json @@ -169,6 +169,39 @@ "cancel" ] }, + { + "businessFunction": "US Cross Border Wire", + "privileges": [ + "view", + "create", + "edit", + "delete", + "approve", + "cancel" + ] + }, + { + "businessFunction": "US FX Foreign Wire", + "privileges": [ + "view", + "create", + "edit", + "delete", + "approve", + "cancel" + ] + }, + { + "businessFunction": "FCY Wire", + "privileges": [ + "view", + "create", + "edit", + "delete", + "approve", + "cancel" + ] + }, { "businessFunction": "Payment Templates", "privileges": [ @@ -491,6 +524,39 @@ "cancel" ] }, + { + "businessFunction": "US Cross Border Wire", + "privileges": [ + "view", + "create", + "edit", + "delete", + "approve", + "cancel" + ] + }, + { + "businessFunction": "US FX Foreign Wire", + "privileges": [ + "view", + "create", + "edit", + "delete", + "approve", + "cancel" + ] + }, + { + "businessFunction": "FCY Wire", + "privileges": [ + "view", + "create", + "edit", + "delete", + "approve", + "cancel" + ] + }, { "businessFunction": "US Billpay Payees-Search", "privileges": [ @@ -834,6 +900,36 @@ "approve" ] }, + { + "businessFunction": "US Cross Border Wire", + "privileges": [ + "view", + "create", + "edit", + "delete", + "approve" + ] + }, + { + "businessFunction": "US FX Foreign Wire", + "privileges": [ + "view", + "create", + "edit", + "delete", + "approve" + ] + }, + { + "businessFunction": "FCY Wire", + "privileges": [ + "view", + "create", + "edit", + "delete", + "approve" + ] + }, { "businessFunction": "Payment Templates", "privileges": [ @@ -1283,6 +1379,39 @@ "cancel" ] }, + { + "businessFunction": "US Cross Border Wire", + "privileges": [ + "view", + "create", + "edit", + "delete", + "approve", + "cancel" + ] + }, + { + "businessFunction": "US FX Foreign Wire", + "privileges": [ + "view", + "create", + "edit", + "delete", + "approve", + "cancel" + ] + }, + { + "businessFunction": "FCY Wire", + "privileges": [ + "view", + "create", + "edit", + "delete", + "approve", + "cancel" + ] + }, { "businessFunction": "US Billpay Payees-Search", "privileges": [ @@ -1709,6 +1838,24 @@ "view" ] }, + { + "businessFunction": "US Cross Border Wire", + "privileges": [ + "view" + ] + }, + { + "businessFunction": "US FX Foreign Wire", + "privileges": [ + "view" + ] + }, + { + "businessFunction": "FCY Wire", + "privileges": [ + "view" + ] + }, { "businessFunction": "ACH Debit", "privileges": [ diff --git a/src/main/resources/data/minimal-bank/job-profiles.json b/src/main/resources/data/minimal-bank/job-profiles.json index e8c371af..16b24cc0 100644 --- a/src/main/resources/data/minimal-bank/job-profiles.json +++ b/src/main/resources/data/minimal-bank/job-profiles.json @@ -151,6 +151,39 @@ "cancel" ] }, + { + "businessFunction": "US Cross Border Wire", + "privileges": [ + "view", + "create", + "edit", + "delete", + "approve", + "cancel" + ] + }, + { + "businessFunction": "US FX Foreign Wire", + "privileges": [ + "view", + "create", + "edit", + "delete", + "approve", + "cancel" + ] + }, + { + "businessFunction": "FCY Wire", + "privileges": [ + "view", + "create", + "edit", + "delete", + "approve", + "cancel" + ] + }, { "businessFunction": "US Billpay Payees-Search", "privileges": [ diff --git a/src/main/resources/data/retail/job-profiles.json b/src/main/resources/data/retail/job-profiles.json index f32e496c..b625d334 100644 --- a/src/main/resources/data/retail/job-profiles.json +++ b/src/main/resources/data/retail/job-profiles.json @@ -515,6 +515,39 @@ "cancel" ] }, + { + "businessFunction": "US Cross Border Wire", + "privileges": [ + "view", + "create", + "edit", + "delete", + "approve", + "cancel" + ] + }, + { + "businessFunction": "US FX Foreign Wire", + "privileges": [ + "view", + "create", + "edit", + "delete", + "approve", + "cancel" + ] + }, + { + "businessFunction": "FCY Wire", + "privileges": [ + "view", + "create", + "edit", + "delete", + "approve", + "cancel" + ] + }, { "businessFunction": "US Domestic Wire - Intracompany", "privileges": [ diff --git a/src/test/resources/csv/dbs-entitlements-applicable-function-privilege.csv b/src/test/resources/csv/dbs-entitlements-applicable-function-privilege.csv index d360c5c8..01829b24 100644 --- a/src/test/resources/csv/dbs-entitlements-applicable-function-privilege.csv +++ b/src/test/resources/csv/dbs-entitlements-applicable-function-privilege.csv @@ -112,5 +112,23 @@ 118,ACH Debit,Payments,delete,0,1046,5 119,ACH Debit,Payments,approve,0,1046,6 120,ACH Debit,Payments,cancel,0,1046,7 +121,US Cross Border Wire,Payments,view,0,1267,2 +122,US Cross Border Wire,Payments,create,1,1267,3 +123,US Cross Border Wire,Payments,edit,0,1267,4 +124,US Cross Border Wire,Payments,delete,0,1267,5 +125,US Cross Border Wire,Payments,approve,0,1267,6 +126,US Cross Border Wire,Payments,cancel,0,1267,7 +127,US FX Foreign Wire,Payments,view,0,1268,2 +128,US FX Foreign Wire,Payments,create,1,1268,3 +129,US FX Foreign Wire,Payments,edit,0,1268,4 +130,US FX Foreign Wire,Payments,delete,0,1268,5 +131,US FX Foreign Wire,Payments,approve,0,1268,6 +132,US FX Foreign Wire,Payments,cancel,0,1268,7 +133,FCY Wire,Payments,view,0,1269,2 +134,FCY Wire,Payments,create,1,1269,3 +135,FCY Wire,Payments,edit,0,1269,4 +136,FCY Wire,Payments,delete,0,1269,5 +137,FCY Wire,Payments,approve,0,1269,6 +138,FCY Wire,Payments,cancel,0,1269,7 diff --git a/src/test/resources/csv/dbs-entitlements-business-functions.csv b/src/test/resources/csv/dbs-entitlements-business-functions.csv index 806e3106..e1111f65 100644 --- a/src/test/resources/csv/dbs-entitlements-business-functions.csv +++ b/src/test/resources/csv/dbs-entitlements-business-functions.csv @@ -13,6 +13,9 @@ 1016,manage.statements,Manage Statements,account.statements,Account Statements 1017,us.domestic.wire,US Domestic Wire,payments,Payments 1018,us.foreign.wire,US Foreign Wire,payments,Payments +1267,us.cross.border.wire,US Cross Border Wire,payments,Payments +1268,us.fx.foreign.wire,US FX Foreign Wire,payments,Payments +1269,fcy.wire,FCY Wire,payments,Payments 1019,manage.data.groups,Manage Data Groups,entitlements,Entitlements 1020,manage.function.groups,Manage Function Groups,entitlements,Entitlements 1021,us.billpay.payments,US Billpay Payments,payments,Payments From 80c94e4a8cb1dc4c856605b7d74fa8c332a98da7 Mon Sep 17 00:00:00 2001 From: Sakthivel Balasubramaniam Date: Fri, 11 Sep 2026 17:10:26 +0200 Subject: [PATCH 3/6] feat(data): configure US productized seed for new wire types Add EUR to Finance International product group and register three new payment types in US productized payments.ootb.types. Co-authored-by: Cursor Co-authored-by: AI-SDLC --- .../data/us-productized-dataset/product-group-seed.json | 3 ++- .../data/us-productized-dataset/us-productized-data.properties | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/resources/data/us-productized-dataset/product-group-seed.json b/src/main/resources/data/us-productized-dataset/product-group-seed.json index 8495cbc8..b6323bb0 100644 --- a/src/main/resources/data/us-productized-dataset/product-group-seed.json +++ b/src/main/resources/data/us-productized-dataset/product-group-seed.json @@ -31,7 +31,8 @@ { "productGroupName": "Finance International", "currencies": [ - "USD" + "USD", + "EUR" ], "currentAccountNames": [ "Assets", diff --git a/src/main/resources/data/us-productized-dataset/us-productized-data.properties b/src/main/resources/data/us-productized-dataset/us-productized-data.properties index a82f079e..a5bcf866 100644 --- a/src/main/resources/data/us-productized-dataset/us-productized-data.properties +++ b/src/main/resources/data/us-productized-dataset/us-productized-data.properties @@ -72,7 +72,7 @@ payments.min=10 payments.max=30 # Available OOTB payment types: SEPA_CREDIT_TRANSFER/US_DOMESTIC_WIRE/ACH_DEBIT/US_FOREIGN_WIRE # This is a comma (,) separated value -payments.ootb.types=US_DOMESTIC_WIRE,US_FOREIGN_WIRE +payments.ootb.types=US_DOMESTIC_WIRE,US_FOREIGN_WIRE,US_CROSS_BORDER_WIRE,US_FX_FOREIGN_WIRE,FCY_WIRE # Number of messages ingest.messages=false From be1bc1c25e716faa97dead4f859c7480ce464c22 Mon Sep 17 00:00:00 2001 From: Sakthivel Balasubramaniam Date: Fri, 11 Sep 2026 17:10:47 +0200 Subject: [PATCH 4/6] feat(payments): seed payment orders for new US wire types Add arrangement queries for three new wire types, refactor payment ingestion to a type-aware gate, and use originator currency for FX/FCY wire payloads. Co-authored-by: Cursor Co-authored-by: AI-SDLC --- .../ProductSummaryPresentationRestClient.java | 34 ++++--- .../configurator/PaymentsConfigurator.java | 93 ++++++++++++------- .../ct/bbfuel/data/PaymentsDataGenerator.java | 8 ++ 3 files changed, 86 insertions(+), 49 deletions(-) diff --git a/src/main/java/com/backbase/ct/bbfuel/client/productsummary/ProductSummaryPresentationRestClient.java b/src/main/java/com/backbase/ct/bbfuel/client/productsummary/ProductSummaryPresentationRestClient.java index e98970a5..dd25a27d 100644 --- a/src/main/java/com/backbase/ct/bbfuel/client/productsummary/ProductSummaryPresentationRestClient.java +++ b/src/main/java/com/backbase/ct/bbfuel/client/productsummary/ProductSummaryPresentationRestClient.java @@ -7,8 +7,11 @@ import static com.backbase.ct.bbfuel.data.CommonConstants.PRODUCT_SUMMARY_FUNCTION_NAME; import static com.backbase.ct.bbfuel.data.CommonConstants.PRODUCT_SUMMARY_RESOURCE_NAME; import static com.backbase.ct.bbfuel.data.CommonConstants.SEPA_CT_FUNCTION_NAME; +import static com.backbase.ct.bbfuel.data.CommonConstants.FCY_WIRE_FUNCTION_NAME; +import static com.backbase.ct.bbfuel.data.CommonConstants.US_CROSS_BORDER_WIRE_FUNCTION_NAME; import static com.backbase.ct.bbfuel.data.CommonConstants.US_DOMESTIC_WIRE_FUNCTION_NAME; import static com.backbase.ct.bbfuel.data.CommonConstants.US_FOREIGN_WIRE_FUNCTION_NAME; +import static com.backbase.ct.bbfuel.data.CommonConstants.US_FX_FOREIGN_WIRE_FUNCTION_NAME; import static org.apache.http.HttpStatus.SC_OK; import com.backbase.ct.bbfuel.client.common.RestClient; @@ -79,18 +82,7 @@ public List getSepaCtArrangements() { } public List getUsDomesticWireArrangements() { - return Arrays.asList(getProductSummaryContextArrangements(new ProductSummaryQueryParameters() - .withBusinessFunction(US_DOMESTIC_WIRE_FUNCTION_NAME) - .withResourceName(PAYMENTS_RESOURCE_NAME) - .withPrivilege(PRIVILEGE_CREATE) - .withDebitAccount(true) - .withCreditAccount(true) - .withSize(999) - .withOrderBy("name")) - .then() - .statusCode(SC_OK) - .extract() - .as(ProductSummaryItem[].class)); + return getPaymentArrangements(US_DOMESTIC_WIRE_FUNCTION_NAME); } public List getAchDebitArrangements() { @@ -114,8 +106,24 @@ private static boolean isValidCurrencyForAchDebit(String currency) { } public List getUSForeignWireArrangements() { + return getPaymentArrangements(US_FOREIGN_WIRE_FUNCTION_NAME); + } + + public List getUsCrossBorderWireArrangements() { + return getPaymentArrangements(US_CROSS_BORDER_WIRE_FUNCTION_NAME); + } + + public List getUsFxForeignWireArrangements() { + return getPaymentArrangements(US_FX_FOREIGN_WIRE_FUNCTION_NAME); + } + + public List getFcyWireArrangements() { + return getPaymentArrangements(FCY_WIRE_FUNCTION_NAME); + } + + private List getPaymentArrangements(String businessFunction) { return Arrays.asList(getProductSummaryContextArrangements(new ProductSummaryQueryParameters() - .withBusinessFunction(US_FOREIGN_WIRE_FUNCTION_NAME) + .withBusinessFunction(businessFunction) .withResourceName(PAYMENTS_RESOURCE_NAME) .withPrivilege(PRIVILEGE_CREATE) .withDebitAccount(true) diff --git a/src/main/java/com/backbase/ct/bbfuel/configurator/PaymentsConfigurator.java b/src/main/java/com/backbase/ct/bbfuel/configurator/PaymentsConfigurator.java index 3d4b476b..309aa463 100644 --- a/src/main/java/com/backbase/ct/bbfuel/configurator/PaymentsConfigurator.java +++ b/src/main/java/com/backbase/ct/bbfuel/configurator/PaymentsConfigurator.java @@ -14,13 +14,19 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; +import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.stream.IntStream; import static com.backbase.ct.bbfuel.data.CommonConstants.PAYMENT_TYPE_ACH_DEBIT; +import static com.backbase.ct.bbfuel.data.CommonConstants.PAYMENT_TYPE_FCY_WIRE; import static com.backbase.ct.bbfuel.data.CommonConstants.PAYMENT_TYPE_SEPA_CREDIT_TRANSFER; +import static com.backbase.ct.bbfuel.data.CommonConstants.PAYMENT_TYPE_US_CROSS_BORDER_WIRE; import static com.backbase.ct.bbfuel.data.CommonConstants.PAYMENT_TYPE_US_DOMESTIC_WIRE; import static com.backbase.ct.bbfuel.data.CommonConstants.PAYMENT_TYPE_US_FOREIGN_WIRE; +import static com.backbase.ct.bbfuel.data.CommonConstants.PAYMENT_TYPE_US_FX_FOREIGN_WIRE; import static com.backbase.ct.bbfuel.data.CommonConstants.PROPERTY_PAYMENTS_OOTB_TYPES; import static com.backbase.ct.bbfuel.util.CommonHelpers.getRandomFromList; import static org.apache.http.HttpStatus.SC_ACCEPTED; @@ -43,47 +49,62 @@ public void ingestPaymentOrders(String externalUserId) { loginRestClient.login(externalUserId, externalUserId); userContextPresentationRestClient.selectContextBasedOnMasterServiceAgreement(); - List sepaCtArrangements = productSummaryPresentationRestClient - .getSepaCtArrangements(); - List usDomesticWireArrangements = productSummaryPresentationRestClient - .getUsDomesticWireArrangements(); - List achDebitArrangements = productSummaryPresentationRestClient - .getAchDebitArrangements(); - List usForeignWireArrangements = productSummaryPresentationRestClient - .getUSForeignWireArrangements(); + + Map> arrangementsByPaymentType = new HashMap<>(); + for (String paymentType : ootbPaymentTypes) { + List arrangements = getArrangementsForPaymentType(paymentType); + if (!isEmpty(arrangements)) { + arrangementsByPaymentType.put(paymentType, arrangements); + } + } + + if (arrangementsByPaymentType.isEmpty()) { + return; + } + + List availablePaymentTypes = new ArrayList<>(arrangementsByPaymentType.keySet()); int randomAmount = CommonHelpers .generateRandomNumberInRange(globalProperties.getInt(CommonConstants.PROPERTY_PAYMENTS_MIN), globalProperties.getInt(CommonConstants.PROPERTY_PAYMENTS_MAX)); - if (!isEmpty(sepaCtArrangements) && !isEmpty(usDomesticWireArrangements) - && !isEmpty(achDebitArrangements) && !isEmpty(usForeignWireArrangements)) { - - IntStream.range(0, randomAmount).parallel().forEach(randomNumber -> { - String paymentType = getRandomFromList(ootbPaymentTypes); - ProductSummaryItem randomArrangement; - - if (PAYMENT_TYPE_SEPA_CREDIT_TRANSFER.equals(paymentType)) { - randomArrangement = getRandomFromList(sepaCtArrangements); - } else if (PAYMENT_TYPE_ACH_DEBIT.equals(paymentType)) { - randomArrangement = getRandomFromList(achDebitArrangements); - } else if (PAYMENT_TYPE_US_FOREIGN_WIRE.equals(paymentType)) { - randomArrangement = getRandomFromList(usForeignWireArrangements); - } else if (PAYMENT_TYPE_US_DOMESTIC_WIRE.equals(paymentType)) { - randomArrangement = getRandomFromList(usDomesticWireArrangements); - } else { - throw new IllegalArgumentException("Unknown payment type " + paymentType); - } - - InitiatePaymentOrderWithId initiatePaymentOrder = PaymentsDataGenerator - .generateInitiatePaymentOrder(randomArrangement.getId(), randomArrangement.getCurrency(), paymentType); - paymentOrderPresentationRestClient.initiatePaymentOrder(initiatePaymentOrder) - .then() - .statusCode(SC_ACCEPTED); - - log.info("Payment order ingested for originator account [{}] for user [{}]", - initiatePaymentOrder.getOriginatorAccount().getIdentification().getIdentification(), externalUserId); - }); + IntStream.range(0, randomAmount).parallel().forEach(randomNumber -> { + String paymentType = getRandomFromList(availablePaymentTypes); + ProductSummaryItem randomArrangement = getRandomFromList(arrangementsByPaymentType.get(paymentType)); + + InitiatePaymentOrderWithId initiatePaymentOrder = PaymentsDataGenerator + .generateInitiatePaymentOrder(randomArrangement.getId(), randomArrangement.getCurrency(), paymentType); + paymentOrderPresentationRestClient.initiatePaymentOrder(initiatePaymentOrder) + .then() + .statusCode(SC_ACCEPTED); + + log.info("Payment order ingested for originator account [{}] for user [{}]", + initiatePaymentOrder.getOriginatorAccount().getIdentification().getIdentification(), externalUserId); + }); + } + + private List getArrangementsForPaymentType(String paymentType) { + if (PAYMENT_TYPE_SEPA_CREDIT_TRANSFER.equals(paymentType)) { + return productSummaryPresentationRestClient.getSepaCtArrangements(); + } + if (PAYMENT_TYPE_ACH_DEBIT.equals(paymentType)) { + return productSummaryPresentationRestClient.getAchDebitArrangements(); + } + if (PAYMENT_TYPE_US_FOREIGN_WIRE.equals(paymentType)) { + return productSummaryPresentationRestClient.getUSForeignWireArrangements(); + } + if (PAYMENT_TYPE_US_DOMESTIC_WIRE.equals(paymentType)) { + return productSummaryPresentationRestClient.getUsDomesticWireArrangements(); + } + if (PAYMENT_TYPE_US_CROSS_BORDER_WIRE.equals(paymentType)) { + return productSummaryPresentationRestClient.getUsCrossBorderWireArrangements(); + } + if (PAYMENT_TYPE_US_FX_FOREIGN_WIRE.equals(paymentType)) { + return productSummaryPresentationRestClient.getUsFxForeignWireArrangements(); + } + if (PAYMENT_TYPE_FCY_WIRE.equals(paymentType)) { + return productSummaryPresentationRestClient.getFcyWireArrangements(); } + throw new IllegalArgumentException("Unknown payment type " + paymentType); } } diff --git a/src/main/java/com/backbase/ct/bbfuel/data/PaymentsDataGenerator.java b/src/main/java/com/backbase/ct/bbfuel/data/PaymentsDataGenerator.java index f4d76fdb..0f193b96 100644 --- a/src/main/java/com/backbase/ct/bbfuel/data/PaymentsDataGenerator.java +++ b/src/main/java/com/backbase/ct/bbfuel/data/PaymentsDataGenerator.java @@ -1,7 +1,9 @@ package com.backbase.ct.bbfuel.data; import static com.backbase.ct.bbfuel.data.CommonConstants.PAYMENT_TYPE_ACH_DEBIT; +import static com.backbase.ct.bbfuel.data.CommonConstants.PAYMENT_TYPE_FCY_WIRE; import static com.backbase.ct.bbfuel.data.CommonConstants.PAYMENT_TYPE_SEPA_CREDIT_TRANSFER; +import static com.backbase.ct.bbfuel.data.CommonConstants.PAYMENT_TYPE_US_FX_FOREIGN_WIRE; import static com.backbase.ct.bbfuel.util.CommonHelpers.getRandomFromEnumValues; import static com.backbase.ct.bbfuel.util.CommonHelpers.getRandomFromList; @@ -72,6 +74,12 @@ public static InitiatePaymentOrderWithId generateInitiatePaymentOrder(String ori counterpartyBank = generateCounterpartyBank(); currency.setCurrencyCode(originatorArrangementCurrency); identification = generateBbanIdentification(); + } else if (PAYMENT_TYPE_US_FX_FOREIGN_WIRE.equals(paymentType) + || PAYMENT_TYPE_FCY_WIRE.equals(paymentType)) { + counterpartyBank = generateCounterpartyBank(); + correspondentBank = generateCorrespondentBank(); + currency.setCurrencyCode(originatorArrangementCurrency); + identification = generateBbanIdentification(); } else { counterpartyBank = generateCounterpartyBank(); correspondentBank = generateCorrespondentBank(); From 04eebbdeeeba9c2587c7ebf5fbd9146240c08e65 Mon Sep 17 00:00:00 2001 From: Sakthivel Balasubramaniam Date: Fri, 11 Sep 2026 17:11:01 +0200 Subject: [PATCH 5/6] docs(payments): document new US wire types in capability setup List US Cross Border Wire, US FX Foreign Wire, and FCY Wire in limits configuration alongside existing US wire payment functions. Co-authored-by: Cursor Co-authored-by: AI-SDLC --- docs/CAPABILITY_DATA_SETUP.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/CAPABILITY_DATA_SETUP.md b/docs/CAPABILITY_DATA_SETUP.md index 870f3fb4..249d6f3d 100644 --- a/docs/CAPABILITY_DATA_SETUP.md +++ b/docs/CAPABILITY_DATA_SETUP.md @@ -68,7 +68,7 @@ Periodic limit per service agreement, for function group "Admin": - Transactional limit with amount of 1,000,000 - Privileges "create" and "approve" -- Functions SEPA CT, ACH Debit, US Domestic Wire and US Foreign Wire +- Functions SEPA CT, ACH Debit, US Domestic Wire, US Foreign Wire, US Cross Border Wire, US FX Foreign Wire, and FCY Wire ## Notifications setup Global notifications are only created by the bank manager using approvals From 9d3f845a9df2efe720598ab9fb45502ab2665682 Mon Sep 17 00:00:00 2001 From: Sakthivel Balasubramaniam Date: Mon, 14 Sep 2026 08:58:22 +0200 Subject: [PATCH 6/6] fix(payments): tighten US wire seeding and approval currency bounds Filter arrangement pools by USD/EUR per wire type, guarantee one seeded order per configured type, log missing pools, and apply currency bounds on small-SA zero-approval policies. Co-authored-by: Cursor --- .../ProductSummaryPresentationRestClient.java | 18 +++++----- .../configurator/ApprovalsConfigurator.java | 17 ++++++++-- .../configurator/PaymentsConfigurator.java | 34 +++++++++++++------ .../us-productized-data.properties | 2 +- 4 files changed, 50 insertions(+), 21 deletions(-) diff --git a/src/main/java/com/backbase/ct/bbfuel/client/productsummary/ProductSummaryPresentationRestClient.java b/src/main/java/com/backbase/ct/bbfuel/client/productsummary/ProductSummaryPresentationRestClient.java index dd25a27d..8c3a261f 100644 --- a/src/main/java/com/backbase/ct/bbfuel/client/productsummary/ProductSummaryPresentationRestClient.java +++ b/src/main/java/com/backbase/ct/bbfuel/client/productsummary/ProductSummaryPresentationRestClient.java @@ -82,7 +82,7 @@ public List getSepaCtArrangements() { } public List getUsDomesticWireArrangements() { - return getPaymentArrangements(US_DOMESTIC_WIRE_FUNCTION_NAME); + return getPaymentArrangements(US_DOMESTIC_WIRE_FUNCTION_NAME, "USD"); } public List getAchDebitArrangements() { @@ -106,23 +106,23 @@ private static boolean isValidCurrencyForAchDebit(String currency) { } public List getUSForeignWireArrangements() { - return getPaymentArrangements(US_FOREIGN_WIRE_FUNCTION_NAME); + return getPaymentArrangements(US_FOREIGN_WIRE_FUNCTION_NAME, "USD"); } public List getUsCrossBorderWireArrangements() { - return getPaymentArrangements(US_CROSS_BORDER_WIRE_FUNCTION_NAME); + return getPaymentArrangements(US_CROSS_BORDER_WIRE_FUNCTION_NAME, "USD"); } public List getUsFxForeignWireArrangements() { - return getPaymentArrangements(US_FX_FOREIGN_WIRE_FUNCTION_NAME); + return getPaymentArrangements(US_FX_FOREIGN_WIRE_FUNCTION_NAME, "EUR"); } public List getFcyWireArrangements() { - return getPaymentArrangements(FCY_WIRE_FUNCTION_NAME); + return getPaymentArrangements(FCY_WIRE_FUNCTION_NAME, "EUR"); } - private List getPaymentArrangements(String businessFunction) { - return Arrays.asList(getProductSummaryContextArrangements(new ProductSummaryQueryParameters() + private List getPaymentArrangements(String businessFunction, String currency) { + return Arrays.stream(getProductSummaryContextArrangements(new ProductSummaryQueryParameters() .withBusinessFunction(businessFunction) .withResourceName(PAYMENTS_RESOURCE_NAME) .withPrivilege(PRIVILEGE_CREATE) @@ -133,7 +133,9 @@ private List getPaymentArrangements(String businessFunction) .then() .statusCode(SC_OK) .extract() - .as(ProductSummaryItem[].class)); + .as(ProductSummaryItem[].class)) + .filter(arrangement -> currency.equals(arrangement.getCurrency())) + .collect(Collectors.toList()); } private Response getProductSummaryContextArrangements(ProductSummaryQueryParameters queryParameters) { diff --git a/src/main/java/com/backbase/ct/bbfuel/configurator/ApprovalsConfigurator.java b/src/main/java/com/backbase/ct/bbfuel/configurator/ApprovalsConfigurator.java index 421337b2..41cb6425 100644 --- a/src/main/java/com/backbase/ct/bbfuel/configurator/ApprovalsConfigurator.java +++ b/src/main/java/com/backbase/ct/bbfuel/configurator/ApprovalsConfigurator.java @@ -148,8 +148,9 @@ private void assignCurrencyBoundPolicies(String externalServiceAgreementId, int List generatedItems; if (numberOfUsers < 3) { - generatedItems = getPolicyAssignmentsBasedOnZeroApprovalPolicyOnly( - externalServiceAgreementId, resource, functionName); + generatedItems = getZeroApprovalCurrencyBoundPolicyAssignments( + externalServiceAgreementId, resource, functionName, + determineCurrencyForFunction(functionName)); } else { generatedItems = getCurrencyPolicyAssignments(externalServiceAgreementId, resource, functionName, determineCurrencyForFunction(functionName)); @@ -225,6 +226,18 @@ private List getPolicyAssignmentsBasedOnZero singletonList(createPolicyAssignmentRequestBounds(policyZeroId, null)))); } + private List getZeroApprovalCurrencyBoundPolicyAssignments( + String externalServiceAgreementId, String resource, String businessFunction, String currencyCode) { + Currency upperBound = new Currency() + .withCurrencyCode(currencyCode) + .withAmount(UPPER_BOUND_HUNDRED); + return singletonList(createPolicyAssignmentRequest( + externalServiceAgreementId, + resource, + businessFunction, + singletonList(createPolicyAssignmentRequestBounds(policyZeroId, upperBound)))); + } + private List getPolicyAssignments( String externalServiceAgreementId, String resource, String businessFunction) { return singletonList(createPolicyAssignmentRequest( diff --git a/src/main/java/com/backbase/ct/bbfuel/configurator/PaymentsConfigurator.java b/src/main/java/com/backbase/ct/bbfuel/configurator/PaymentsConfigurator.java index 309aa463..e71c8d64 100644 --- a/src/main/java/com/backbase/ct/bbfuel/configurator/PaymentsConfigurator.java +++ b/src/main/java/com/backbase/ct/bbfuel/configurator/PaymentsConfigurator.java @@ -55,32 +55,46 @@ public void ingestPaymentOrders(String externalUserId) { List arrangements = getArrangementsForPaymentType(paymentType); if (!isEmpty(arrangements)) { arrangementsByPaymentType.put(paymentType, arrangements); + } else { + log.warn("No debit arrangements available for configured payment type [{}]", paymentType); } } if (arrangementsByPaymentType.isEmpty()) { + log.warn("Payment order ingestion skipped for user [{}]: no arrangements found for any configured type", + externalUserId); return; } List availablePaymentTypes = new ArrayList<>(arrangementsByPaymentType.keySet()); + for (String paymentType : availablePaymentTypes) { + ingestPaymentOrder(externalUserId, paymentType, arrangementsByPaymentType.get(paymentType)); + } + int randomAmount = CommonHelpers .generateRandomNumberInRange(globalProperties.getInt(CommonConstants.PROPERTY_PAYMENTS_MIN), globalProperties.getInt(CommonConstants.PROPERTY_PAYMENTS_MAX)); - IntStream.range(0, randomAmount).parallel().forEach(randomNumber -> { + int additionalOrders = Math.max(0, randomAmount - availablePaymentTypes.size()); + IntStream.range(0, additionalOrders).parallel().forEach(randomNumber -> { String paymentType = getRandomFromList(availablePaymentTypes); - ProductSummaryItem randomArrangement = getRandomFromList(arrangementsByPaymentType.get(paymentType)); + ingestPaymentOrder(externalUserId, paymentType, arrangementsByPaymentType.get(paymentType)); + }); + } - InitiatePaymentOrderWithId initiatePaymentOrder = PaymentsDataGenerator - .generateInitiatePaymentOrder(randomArrangement.getId(), randomArrangement.getCurrency(), paymentType); - paymentOrderPresentationRestClient.initiatePaymentOrder(initiatePaymentOrder) - .then() - .statusCode(SC_ACCEPTED); + private void ingestPaymentOrder(String externalUserId, String paymentType, + List arrangements) { + ProductSummaryItem randomArrangement = getRandomFromList(arrangements); - log.info("Payment order ingested for originator account [{}] for user [{}]", - initiatePaymentOrder.getOriginatorAccount().getIdentification().getIdentification(), externalUserId); - }); + InitiatePaymentOrderWithId initiatePaymentOrder = PaymentsDataGenerator + .generateInitiatePaymentOrder(randomArrangement.getId(), randomArrangement.getCurrency(), paymentType); + paymentOrderPresentationRestClient.initiatePaymentOrder(initiatePaymentOrder) + .then() + .statusCode(SC_ACCEPTED); + + log.info("Payment order ingested for originator account [{}] for user [{}]", + initiatePaymentOrder.getOriginatorAccount().getIdentification().getIdentification(), externalUserId); } private List getArrangementsForPaymentType(String paymentType) { diff --git a/src/main/resources/data/us-productized-dataset/us-productized-data.properties b/src/main/resources/data/us-productized-dataset/us-productized-data.properties index a5bcf866..483d8995 100644 --- a/src/main/resources/data/us-productized-dataset/us-productized-data.properties +++ b/src/main/resources/data/us-productized-dataset/us-productized-data.properties @@ -70,7 +70,7 @@ contacts.sanctioned.countries=false ingest.payments=false payments.min=10 payments.max=30 -# Available OOTB payment types: SEPA_CREDIT_TRANSFER/US_DOMESTIC_WIRE/ACH_DEBIT/US_FOREIGN_WIRE +# Available OOTB payment types: SEPA_CREDIT_TRANSFER/US_DOMESTIC_WIRE/ACH_DEBIT/US_FOREIGN_WIRE/US_CROSS_BORDER_WIRE/US_FX_FOREIGN_WIRE/FCY_WIRE # This is a comma (,) separated value payments.ootb.types=US_DOMESTIC_WIRE,US_FOREIGN_WIRE,US_CROSS_BORDER_WIRE,US_FX_FOREIGN_WIRE,FCY_WIRE