Use cases

Banking test scenarios — failed payments, chargebacks and frozen accounts

Count the payment failures, chargebacks, fraud cases and account states in a synthetic banking bundle and check that every fraud case points at a real transaction.

Use case

Banking systems are defined by what happens when things go wrong. A payment to a closed account must be returned with the right reason. A disputed card transaction must open a case that still points at the transaction weeks later. A frozen account must refuse outgoing transfers but still accept a reversal. Test data copied from yesterday's production mostly contains the ordinary days, and the failures it does contain belong to real customers whose data should not be in a test environment.

The scenario

The payments team is changing how outgoing payments are retried. Their tests need payments that fail for different reasons on different rails, card transactions that were declined, reversed or charged back, fraud cases linked to those transactions, accounts in non-open states and loans in different stages. Every reference must resolve: a fraud case pointing at a transaction that is not in the dataset turns a business test into a data-quality incident.

The Financial Services pack generates these situations synthetically. The script below inventories its public sample and checks the relationships the payments tests depend on.

Run it

Python 3.10 or later, standard library only.

import csv
import io
import os
import urllib.request
from collections import Counter

BASE = os.environ.get("DATANIVRA_DOWNLOADS", "https://www.datanivra.com/downloads")
BUNDLE = f"{BASE}/packs/financial-services/2.1.0/synthetic"


def rows(name):
    with urllib.request.urlopen(f"{BUNDLE}/{name}") as response:
        return list(csv.DictReader(io.TextIOWrapper(response, encoding="utf-8")))


accounts = rows("core_banking.accounts.csv")
transactions = rows("payments.transactions.csv")
payments = rows("payments.payments.csv")
fraud = rows("fraud.fraud_cases.csv")
loans = rows("lending.loans.csv")

tx_ids = {t["transaction_id"] for t in transactions}
account_ids = {a["account_id"] for a in accounts}
print(
    "transaction status:",
    ", ".join(f"{k}={v}" for k, v in sorted(Counter(t["status"] for t in transactions).items())),
)
print(
    "payment status:",
    ", ".join(f"{k}={v}" for k, v in sorted(Counter(p["status"] for p in payments).items())),
)
for p in payments:
    if p["failure_reason"]:
        print(f"  failed payment {p['payment_id']} on {p['payment_rail']}: {p['failure_reason']}")
print(
    "fraud case types:",
    ", ".join(f"{k}={v}" for k, v in sorted(Counter(f["case_type"] for f in fraud).items())),
)
linked = sum(1 for f in fraud if f["transaction_ref"] in tx_ids)
print(f"fraud cases linked to an existing transaction: {linked} of {len(fraud)}")
print(
    "account status:",
    ", ".join(f"{k}={v}" for k, v in sorted(Counter(a["status"] for a in accounts).items())),
)
for loan in loans:
    print(f"loan {loan['loan_id']}: {loan['status']}, {loan['days_past_due']} days past due")
orphans = sum(1 for t in transactions if t["account_ref"] not in account_ids)
print(f"transactions without an account: {orphans}")

Expected output

transaction status: CHARGED_BACK=3, DECLINED=2, PENDING=5, POSTED=43, REVERSED=1
payment status: COMPLETED=7, FAILED=4
  failed payment 1 on ZZ_WIRE: BENEFICIARY_ACCOUNT_INVALID
  failed payment 6 on ZZ_INSTANT: LIMIT_EXCEEDED
  failed payment 8 on ZZ_WIRE: ACCOUNT_CLOSED
  failed payment 11 on ZZ_WIRE: BENEFICIARY_ACCOUNT_INVALID
fraud case types: CHARGEBACK=3, FIRST_PARTY=2
fraud cases linked to an existing transaction: 5 of 5
account status: FROZEN=1, OPEN=11
loan 1: PAID_OFF, 0 days past due
transactions without an account: 0

Four of eleven outgoing payments fail, each with a distinct, enumerated reason; three card transactions were charged back and each has a matching chargeback case; one account is frozen. Payment rails carry a ZZ_ prefix so no test can mistake them for a real scheme identifier.

Schema

Scenario (pack code)TableSignal
FS_FAILED_PAYMENTSpayments.paymentsstatus = FAILED with a failure_reason
FS_CHARGEBACKSpayments.transactions, fraud.fraud_casesCHARGED_BACK transactions with CHARGEBACK cases
FS_ACCOUNT_STATUS_CHANGEScore_banking.accountsfrozen, dormant or closed with status_changed_at
FS_LOAN_DELINQUENCYlending.loansdays_past_due 30–180, balance outstanding
FS_BALANCE_BOUNDARIEScore_banking.accountsbalances at zero and at the overdraft limit, one cent either side

The sample is small: delinquent loans and balance boundaries are scenarios you request at generation time rather than cases present in every bundle.

What DataNivra does with your own data

Activated in your organisation, the pack's policy templates classify card numbers, account numbers, tax ids and beneficiary details, and a dataset request can combine a masked subset of your core-banking source with synthetic scenario rows such as failed payments or chargebacks. The agent does the row-level work in your network; deterministic masking keeps an account number consistent across the core ledger, the card system and the fraud system so cross-system tests still join. Certification gates — including referential integrity and orphan detection — must pass before a dataset can be provisioned. Certification here means DataNivra-certified against configured policy gates; it is not a regulatory or third-party certification and does not make a system compliant with any regulation.

Limits to plan around

  • The sample's single loan is paid off; delinquency, default and boundary balances appear when those scenarios are requested at a larger scale.
  • Rails, merchant categories and reason codes are synthetic enumerations. Tests against a real scheme's message formats need a mapping to the scheme's codes.
  • Synthetic transaction patterns are modelled, not learned from your customers; fraud-model evaluation needs masked real data or carefully designed synthetic distributions.

Next steps

Read the financial services test data tutorial and the banking test data guide, and browse the Financial Services pack.

Synthetic downloads

Files from the Financial Services pack 2.1.0 bundle. Everything in it is synthetic, generated from a fixed seed, and listed with its SHA-256 digest in the bundle's MANIFEST.json.

Industry pack: Financial Services — its entities, scenarios, policy templates and the complete asset bundle.

Try it with DataNivra

The synthetic playground walks through discovery, subsetting, masking, validation and certification in your browser, with no account. Starting free gives you the hosted synthetic sandbox; your own sources need an agent in your network.