Bistromathic Lab INTERNAL TEST ONLY

Numbers on a restaurant bill obey their own laws of physics; so do payments. The Bistromathic Lab is an internal PSP simulator that mimics a real card processor (auth / debit / capture / refund / void / cancel) so end-to-end tests don't have to depend on Citigate / Circoflow / CC7995.

Not available in production.

HTTP API

All API requests must include the shared secret header X-Sim-Key.

Outcome rules (priority order)

  1. Test card match — see table below. An encrypted PAN (ev:…) matches by the card.bin and card.last4 sent beside it.
  2. Amount last-2-digits fallback.
  3. Default: approved.

Test cards

PANStatus3DSDescription
4242424242424242approvedApproved (Visa, no 3DS)
4111111111111111approvedApproved (Visa, no 3DS)
5555555555554444approvedApproved (Mastercard, no 3DS)
378282246310005approvedApproved (Amex, no 3DS)
4000000000003220approvedyesApproved after 3DS challenge
4000002500003155approvedyesApproved after 3DS challenge (Visa)
5200828282828210approvedyesApproved after 3DS challenge (Mastercard)
4000000000000002do_not_honorDo not honor
4000000000009995insufficient_fundsInsufficient funds
4000000000000069expired_cardExpired card
4000000000000127invalid_cvvInvalid CVV
4000000000000119psp_malfunctionAcquirer system malfunction
4100000000000019suspected_fraudSuspected fraud
4000000000000259issuer_errIssuer or switch is inoperative
40000000000003413ds_auth_failureyes3DS authentication failure
4000000000000051psp_timeoutAcquirer timeout

Amount-last-2-digits fallback

Amount ends inStatus3DSDescription
…12invalid_amtInvalid amount
…13invalid_amtInvalid amount
…14invalid_accountInvalid card number
…33expired_cardExpired card
…51insufficient_fundsInsufficient funds
…54expired_cardExpired card
…59suspected_fraudSuspected fraud
…65do_not_honorExceeds withdrawal frequency
…79invalid_cvvInvalid CVV
…91issuer_errIssuer unavailable
…96psp_malfunctionAcquirer system malfunction
…983ds_auth_failureyes3DS challenge, then authentication failure (amount-rule: ends in 98)
…99approvedyesApproved after 3DS challenge (amount-rule: ends in 99)
…00approvedApproved (amount-rule: ends in 00)
…01do_not_honorRefer to card issuer
…02do_not_honorDo not honor
…05insufficient_fundsInsufficient funds

Swish

Swish's Commerce API on Swish's own paths, as its Merchant Integration Guide describes it, so a Swish client can be pointed here instead of at Swish. X-Sim-Key stands in for the client certificate (401, empty body, without it). SEK only; the merchant Swish number is any 123xxxxxxx (Swish's simulator uses 1234679304).

Callbacks POST the payment request or refund as GET returns it, with the callbackIdentifier header when one was given, retried after 5 and 10 seconds until a 200. A payment calls back once it is final; a refund on DEBITED and again on PAID (or ERROR). A request nobody answers ends in ERROR TM01 after three minutes.

Outcomes

In the app, Avbryt declines (DECLINED); Betala hands over to BankID, which signs after two seconds and pays, unless a rule below chose an error; the page's lab drawer cancels in BankID (ERROR BANKIDCL) or lets the request time out. As in Swish's own simulator, a message equal to an error code forces it:

Otherwise the amount decides (a payment request only):

Amount ends inCodeWhenerrorMessage
…10 öreFF10ERROR once the payer signsBank system processing error
…24 öreDS24ERROR once the payer signsSwish timed out waiting for an answer from the banks after payment was started
…01 öreTM01ERROR once the payer signsSwish timed out before the payment was started
…02 öreAM02422 at createAmount value is too large
…03 öreACMT03ERROR once the payer signsPayer not Enrolled
…06 öreAM06422 at createSpecified transaction amount is less than agreed minimum
…07 öreRF07ERROR once the payer signsTransaction declined

Vipps

Vipps MobilePay's ePayment API (v1), with its Access Token and Webhooks APIs, on apitest.vipps.no's own paths under /vipps: a Vipps MobilePay client works here by swapping https://apitest.vipps.no for https://<this host>/vipps. The same API answers at the root too (/accesstoken/get, /epayment/v1/…, /webhooks/v1/…). No credentials are checked: any client_id, client_secret and Ocp-Apim-Subscription-Key get a token, and any Bearer token is taken, so a connection's real Vipps keys work unchanged. The Merchant-Serial-Number (five to seven digits) names the sales unit, each its own. A Norwegian sales unit takes NOK only, from 1 kr; an MSN starting 45 or 358 is MobilePay's (see MobilePay).

States as at Vipps: CREATED, then AUTHORIZED, ABORTED (the payer cancelled), EXPIRED (ten minutes) or TERMINATED (the merchant cancelled first). An authorized payment stays AUTHORIZED; its aggregate says what was captured, cancelled and refunded. Webhooks are signed with HMAC-SHA256 as Vipps signs them and retried after 2, 4 and 6 seconds until acknowledged. Errors are RFC 7807 problems with Vipps' error codes.

Vipps' test amounts

Payment amountIn the app
1,51 kr (151)Every card declined in the app: Insufficient funds
1,82 kr (182)Every card declined in the app: Refused by issuer
1,83 kr (183)Every card declined in the app: Suspected fraud
1,84 kr (184)Every card declined in the app: Withdrawal limit exceeded
1,86 kr (186)Every card declined in the app: Expired card
1,87 kr (187)Every card declined in the app: Invalid card
1,97 kr (197)3-D Secure denied: the card is declined
2,01 kr (201)After the payer pays, unknown for an hour (CREATED), then AUTHORIZED
2,02 kr (202)3-D Secure asked for, and passable
Refund amountRefused with
1,23 kr (123)5100 Can't refund single transferred payments. User is deleted or does not have a receiving account.
1,24 kr (124)6220 Refund period expired

Lab rules

AmountEffect
…28 øre, from 10 krThe first capture fails: 6280 Capture failed, please try again later; a retry captures
…05 øre, from 10 krEvery capture is refused: 6260 Funds unavailable
…06 øre, from 10 krCreate refused: 422, 5070 Payment cannot be created

MobilePay (Denmark, Finland)

MobilePay is Vipps MobilePay's other brand on the same ePayment API as Vipps above: the same paths, keys, states, RFC 7807 errors, force approve and signed webhooks. A sales unit belongs to one market, and every payment it takes must be in that market's currency. Vipps knows a sales unit's market from the merchant agreement; the lab reads it from the MSN, by this convention: the MSN starts with the market's country calling code.

MSNMarketCurrencyMinimumThe payerTest user
45…, e.g. 451234MobilePay DenmarkDKK1 øre/mobilepay/landing, Danish; picks the card first+45 12 34 56 78
358…, e.g. 358123MobilePay FinlandEUR1 cent/mobilepay/landing, Finnish+358 40 123 4567
any other, e.g. 123456Vipps NorwayNOK100 øre/vipps/landing, Norwegian+47 91 23 45 67

Another currency is refused with 5040 Invalid currency for merchant; less than the minimum with 6010 Amount too small.

MobilePay's limits

MarketLimitIn the lab
FinlandPayments to companies: €40,000 per 30 days per payer (Vipps MobilePay's help centre, FI)A running total per payer (phone number) at the sales unit over the last 30 days. A payment that would pass it is declined in the app, whatever card pays: the payer can only cancel (ABORTED) or let it expire. Force approve answers 10010 Payment limit reached. To see it at once, pay € 40 000,01 (4000001).
DenmarkNone of MobilePay's own on payments to businesses: they follow the card'sVisa/Dankort ••9012's bank allows 1.000 kr. per 24 hours: over that the app declines that card ("Du har nået kortets beløbsgrænse") and another card pays. Pay 1.500 kr. (150000) with it to see it.

Who takes MobilePay (for a showcase design)

CashierMarketMobilePay depositsWithdrawals
Danske SpilDKCard, MobilePay, Apple Pay and Google Pay; no bank transferTo NemKonto, at most one payout a day, within 32 h
bet365 DKDKEnter the mobile number, choose the card in the app, instant; 100–50 000 kr. (unverified)By bank transfer
VeikkausFISince April 2025, from € 15 (unverified; bank buttons from € 10)To a bank account

Real or modeled. The API is Vipps MobilePay's real ePayment API, as for Vipps, and so are the per-market currencies and minimums, the Norway-only 3-D Secure amounts and Finland's € 40 000. Modeled by the lab: the MSN convention, the payer's cards and the Danish card limit, running totals kept per sales unit (MobilePay counts across every business), and the Danish and Finnish wording of the screens.

iDEAL

iDEAL 2.0's Merchant/CPSP API (2.0.6) on the iDEAL Hub's own paths, with the acquirer's token endpoint, so an iDEAL 2.0 client can be pointed here instead of at merchant-cpsp-mtls.ext.idealapi.nl. The payer sees iDEAL | Wero's payment page. SIM_API_KEY stands in for every key and certificate iDEAL uses: the client assertion, the acquirer's access token, the request Signature and the Hub's own signatures are HS256 under it (iDEAL signs ES256 with certificates). Any nine-digit merchant id will do, each one its own creditor. EUR only.

Statuses as at iDEAL: OPEN, IDENTIFIED (the code was scanned or a bank chosen), then SUCCESS, CANCELLED, FAILURE or EXPIRED (after expirationPeriod, 1200 seconds by default). Errors are iDEAL's {code, message, traceId}. iDEAL has no refunds. The banks offered: ABN AMRO, ASN Bank, bunq, Finom, Handelsbanken, ING, Knab, N26, Nationale-Nederlanden, Rabobank, RegioBank, Revolut, SNS, Triodos Bank, Van Lanschot Kempen, Yoursafe; their screens are a plain banking app, no bank's design.

iDEAL's test amounts

AmountEffect
€ 2,00 (200)the bank cancels the payment (CANCELLED)
€ 3,00 (300)the payment expires at the bank (EXPIRED)
€ 4,00 (400)the bank never answers: IDENTIFIED until the expiry period ends, then EXPIRED
€ 5,00 (500)the bank refuses the payment (FAILURE)
€ 7,00 (700)the create is refused: 500 TECHNICAL_ERROR

Lab rules

AmountEffect
…24 cents, from € 10the transaction-callback arrives 20 seconds late
…29 cents, from € 10the first create is refused 429 TOO_MANY_REQUESTS; iDEAL's one retry goes through
…41 cents, from € 10no transaction-callback at all: only a GET tells the outcome
…53 cents, from € 10the create is refused: 503 SERVICE_UNAVAILABLE
…05 cents, from € 10the bank refuses the payment, insufficient balance (FAILURE)
…07 cents, from € 10the create is refused: 500 TECHNICAL_ERROR

BLIK (PayU)

BLIK, Poland's bank-app payment, on PayU's REST API v2.1, on PayU's own paths under /payu: a PayU client swaps https://secure.snd.payu.com (or https://secure.payu.com) for https://<this host>/payu and changes nothing else. PLN only. Each merchantPosId is a POS of its own (PayU's public sandbox POS is 300746).

Credentials are ignored. The OAuth endpoint gives a token to any client_id / client_secret, and no call is refused for its bearer token. What PayU signs, the lab signs PayU's way: notifications carry OpenPayu-Signature: sender=checkout;signature=<MD5(body + second key)>;algorithm=MD5;content=DOCUMENT with the published simulation key bistromathic-lab-simulated-flow as the second key.

Statuses as at PayU: NEW, PENDING (the code is in, the payer confirms in the app), WAITING_FOR_CONFIRMATION (a POS without auto-receive), COMPLETED, CANCELED. A code is valid two minutes and once; the payer has a minute to confirm (BLIK publishes no figure). A NEW order is CANCELED when its validityTime (86400 s by default) runs out. On the payment page a refused code leaves the order NEW for another try; "Anuluj płatność" cancels it and returns to continueUrl with error=501, as does any failure.

PayU's BLIK test codes

CodeEffect
777xxxauthorised (no token registered): COMPLETED three seconds later
200201authorised, and a One Click (UID) token is registered
500500refused by the bank (negative authorization): CANCELED
700701code expired: ERROR_AUTHORIZATION_CODE / AUTH_CODE_EXPIRED
700702code cancelled: ERROR_AUTHORIZATION_CODE / AUTH_CODE_CANCEL
700703code already used: ERROR_AUTHORIZATION_CODE / AUTH_CODE_USED

A code from the lab's banking app is a real one: valid two minutes, once (AUTH_CODE_EXPIRED, AUTH_CODE_USED), refused over the payer's bank limit (AUTH_CODE_LIMIT_EXCEEDED), and confirmed or rejected by the payer in the app. Six digits nobody issued: AUTH_CODE_INVALID.

Tpay's failure amounts, as lab rules (exact amounts)

AmountEffect
96,00 złlimit exceeded: the bank refuses after the payer confirms (AUT_ERROR_LIMIT_EXCEEDED)
120,00 złinsufficient funds: the bank refuses after the payer confirms (AUT_ERROR_INSUFFICIENT_FUNDS)
144,00 złrejected by the payer in the bank app (AUT_ERROR_USER_REJECTED)
168,00 złrejected by the issuer after the payer confirms (AUT_ERROR_NO_AUTHORIZATION)
216,00 złrefused for security reasons after the payer confirms (AUT_ERROR_SECURITY)
312,00 złtimeout: the push never arrives; CANCELED when the confirmation time runs out (AUT_ERROR_TIMEOUT)

Lab rules

AmountEffect
…24 grosze, from 10 złevery notification arrives 20 seconds late
…41 grosze, from 10 złno notifications at all: only a GET tells the outcome
…53 grosze, from 10 złthe create is refused: 503 SERVICE_NOT_AVAILABLE
…77 grosze, from 10 złthe POS does not receive automatically: WAITING_FOR_CONFIRMATION until the merchant captures (or cancels)
refund amount …66 groszeaccepted (PENDING), then CANCELED: statusError BANK_DECLINED
refund amount …02 groszerefused at create: 9102 NO_BALANCE (the shop's balance is short)

Limits

BLIK has no scheme limit; each bank sets its own. The payer's phone can belong to any of these banks (the lab drawer switches), each with its default daily limit for BLIK payments online, summed per Warsaw day:

BankDaily BLIK limit online
PKO Bank Polski3 000,00 zł
mBank3 000,00 zł
ING Bank Śląski2 000,00 zł
Bank Pekao1 000,00 zł
Bank Millennium1 000,00 zł
VeloBank1 000,00 zł

A single payment above 999 999,99 zł is refused (ERROR_VALUE_INVALID): PayU publishes no BLIK maximum, this is the largest a Polish BLIK PSP publishes. Merchants cap lower themselves: Totalbet takes 1–1 000 zł per BLIK deposit, which is the cashier's rule, not PayU's. BLIK is deposit-only: refunds go back to the account the payment came from, and BLIK reports no IBAN.

PayU's status codes

HTTPstatusCodeMeaning
200SUCCESSRequest processed
201SUCCESSOrder created with a BLIK code or token in payMethods
302SUCCESSOrder created: redirectUri in the body and the Location header
302WARNING_CONTINUE_REDIRECTTransparent pay-by-link: redirect the buyer (not BLIK)
400ERROR_SYNTAXThe body is not JSON
400ERROR_VALUE_INVALIDA value is wrong (a BLIK code that is not six digits, an amount BLIK does not take…)
400ERROR_VALUE_MISSINGA required value is missing
400ERROR_ORDER_NOT_UNIQUEextOrderId already used at this POS
400ERROR_INCONSISTENT_CURRENCIESThe order's currency is not the POS's (PLN)
400ERROR_AUTHORIZATION_CODEThe BLIK code was refused (expired, cancelled, used, unknown, over the payer's limit)
401UNAUTHORIZEDNo or a wrong access token
403ERROR_VALUE_INVALIDThe token's POS is not merchantPosId (INVALID_AUTH_FOR_THIS_ORDER)
404DATA_NOT_FOUNDNo such order or refund at this POS
500ERROR_INTERNALPayU unavailable
503SERVICE_NOT_AVAILABLEPayU unavailable

Real vs modeled. Real: the paths, OAuth, the order and refund shapes, statuses and status codes, the 302 with redirectUri, the refund error codes, the notification and its MD5 signature, BLIK's test codes. Modeled: PayU's tokens are opaque ids it keeps (the lab's carry the POS and grant in the open); the HTTP status of a refused BLIK code (400); AUTH_CODE_LIMIT_EXCEEDED read as "over the payer's bank limit"; the resultCode literals AUT_ERROR_USER_REJECTED, AUT_ERROR_TIMEOUT and AUT_ERROR_SECURITY (PayU's own are AUT_ERROR_NO_AUTHORIZATION, _LIMIT_EXCEEDED, _INSUFFICIENT_FUNDS); the refusal of a DELETE on a COMPLETED order; the order id's first ten characters, which carry the POS; the payment page and the banking app (redrawn, a generic bank). Notifications are tried twice, 5 s apart (PayU retries for 72 hours).

Redsys (Bizum)

Redsys' TPV Virtual, on the real hosts' own paths under /redsys: a client of https://sis-t.redsys.es:25443 (test) or https://sis.redsys.es (production) is pointed at <lab>/redsys instead. Bizum only (Ds_Merchant_PayMethods z), euros only, sold in one step (Ds_Merchant_TransactionType 0). Any merchant code (FUC) is a merchant of its own; Redsys' public test merchant is 999008881, terminal 1.

Credentials are ignored: no request is refused for its Ds_Signature. What Redsys signs — the online notification, the Ds_* parameters on the way back to URLOK/URLKO, the REST answers and the query answers — the lab signs Redsys' way (HMAC_SHA256_V1: a per-order key, 3DES-CBC of Ds_Order under the secret key, then HMAC-SHA256 of the Base64 parameters) with the simulation key YmlzdHJvbWF0aGljLWxhYi1zaW11bGF0: Base64 of the first 24 bytes of the lab's published simulation secret bistromathic-lab-simulated-flow. A connection's "Simulated flow" uses it as its secret key.

The online notification is a form POST of the signed triplet to DS_MERCHANT_MERCHANTURL on every final state (Redsys takes any HTTP 200), retried once after five seconds; refunds notify too when they name a MerchantURL. URLOK (authorised) or URLKO (anything else) gets the same signed triplet as query parameters. The query service reports Ds_State S (page open), P (with the payer's bank), F (final) or T with 9998 (sin finalizar: the payer's time ran out — four minutes once the push is sent, 10 minutes on the page or in Clave Bizum).

Payers

PhoneBankEffect
600000000Banco de pruebasPaylands' KO number: denied once the payer confirms (9966)
611111111BBVABBVA: €3,000 per operation, €3,000 a day, €5,000 a month (9966 beyond)
622222222CaixaBankCaixaBank: €500 per operation, €2,000 a day, €5,000 a month (9966 beyond)
633333333Caja RuralCaja Rural, still on Clave Bizum: the legacy flow (Clave, then the SMS code)
644444444UnicajaUnicaja, €50 in the account: more than that is denied (9677)
699999999Banco de pruebasNot registered in Bizum: Redsys asks for another number
700000000Banco de pruebasPaylands' OK number. Clave Bizum (forced from the drawer): paid. Request to Pay: Paylands' push bands — under €5 paid; €5–9.99 denied (9966); €10–15 no R2P, falls back to Clave Bizum, paid; over €15 the same fallback, denied (9966)
any other 6xx/7xxby the last digitA Bizum user at the bank its last digit picks (0 Santander, 1 BBVA, 2 CaixaBank, …): the payer confirms or rejects in the bank app

Clave Bizum: any four to eight characters (1234 in Redsys' sandbox). SMS code: 123456; 3 wrong codes deny with 0184. The bank app's "Rechazar" and Redsys' "Cancelar" end with 9915.

Bank limits (e-commerce, as published)

BankPer operationPer dayPer month
BBVA3.000,00 €3.000,00 €5.000,00 €
CaixaBank500,00 €2.000,00 €5.000,00 €

Beyond a limit the bank says no and Redsys reports 9966 (Bizum's KO); the bank app says which limit. Bizum's €1,000 per operation is its person-to-person limit, not e-commerce: shops are capped by the payer's bank. Other banks publish no e-commerce limit and the lab enforces none.

Lab rules

AmountEffect
…91 cents, from €10The online notification arrives 30 seconds after the payer is back
…92 cents, from €10No online notification at all: the merchant has to ask the query service
…93 cents, from €10The payer's bank does not answer: denied with 9912 (Emisor no disponible)

Refunds

Within 365 days, partial or full, up to 10 % over the payment in total (Bizum lets a refund exceed the original by the acquirer's percentage); beyond that SIS0057. A refund whose amount ends in 85 cents is refused by Bizum (9485). Bizum pays nothing out to a payer otherwise: it is deposit-only.

Ds_Response

CodeMeaning
9485BIZUM ha devuelto un KO en la devolución
9677Saldo disponible insuficiente
9912Emisor no disponible
9915A petición del usuario se ha cancelado el pago
9966BIZUM ha devuelto un KO en la autorización
9998Operación en proceso de solicitud de datos de tarjeta
0000Transacción autorizada para pagos y preautorizaciones
0900Transacción autorizada para devoluciones y confirmaciones
0184Error en la autenticación del titular
0190Denegación del emisor sin especificar motivo

Real or modeled

Brite (Instant Payouts)

Brite's merchant API, the payout half, on Brite's own paths under /brite: a Brite client works here by swapping https://sandbox.britepaymentgroup.com (or https://production.britepaymentgroup.com) for https://<this host>/brite. It is the bank rail BabelPay pays withdrawals out on when the deposit method cannot receive money (iDEAL, MobilePay, BLIK, Bizum…): a pay-by-bank credit to the player's IBAN. No credentials are checked: any public_key and secret get a token, and the token names the merchant (its public key), each with its own accounts and payouts. Brite deposits (the Brite Client) are not simulated.

A payout is PENDING (1) at once, then COMPLETED (4), CREDIT (5) and SETTLED (6) a second apart. Callbacks POST {merchant_id, transaction_id, transaction_state} with User-Agent: Brite Callback to each URL subscribed to a state the payout reaches (2, 3, 4, 5 or 6), unsigned, as Brite's are: the merchant reads the payout back with transaction.get. ABORTED (2) carries an error_code; FAILED (3) none. When a settled payout's money comes back, a Returned Funds transaction (type 13, related_transaction_id = the payout) appears and {merchant_id, transaction_id, original_transaction_id, notification_type: "RETURNED_TRANSACTION", country_id, amount} goes to the returned-funds URL.

Checked as Brite checks: amount a number of at least 1.0 with at most two decimals (Brite's own error text); the bank account must exist; currency_id (CHF, DKK, EUR, GBP, HUF, NOK, RON, SEK, Brite's list: no PLN) must be the account's currency; statement_reference is refused for Swedish and Finnish accounts and over 105 characters; callbacks only for states 2–6. Errors are Brite's: HTTP 400 {error_name, error_message, state: "APPLICATION_ERROR"} (TransactionInvalid, NotFound, InvalidRequest, NotAvailable, BadRequest). A bank account takes a valid IBAN (mod 97) in its country_id, one of: AT, BE, CH, DE, DK, EE, ES, FI, FR, GB, HR, HU, IE, IT, LT, LU, LV, NL, NO, PT, RO, SE, SI, SK.

Brite's sandbox amounts

AmountBrite's caseEffect
11.01PO-03Aborted payout: STATE_ABORTED (2) after a second, error_code bank_rejected_transaction
11.02PO-03Aborted payout: STATE_ABORTED (2) after a second, error_code risk_engine_rejection
12.01PO-04Failed payout: STATE_FAILED (3) after a second, no error_code (Brite: the reason is unknown)
13.01PO-05Delayed payout: STATE_COMPLETED (4) for five minutes, then CREDIT (5) and SETTLED (6)
14.01PO-07Returned funds: SETTLED (6), then a minute later the money comes back (RETURNED_TRANSACTION)

Lab rules

ValueEffect
15.01Refused at create: the merchant's Brite balance is too low (400 BadRequest)
16.01Stays STATE_PENDING (1) for two minutes, as Swedish payouts sometimes do, then COMPLETED, CREDIT and SETTLED
IBAN ending 9901The payee's bank refuses the payout: STATE_ABORTED (2), error_code bank_rejected_transaction
IBAN ending 9902The account is closed: the payout SETTLES, then the money comes back a minute later (RETURNED_TRANSACTION)
holder_dob under 18 yearsBrite's risk engine refuses the payee: STATE_ABORTED (2), error_code risk_engine_rejection
CountrySettlesRefused by the bank (…9901)Returned (…9902)
DEDE89370400440532013000DE02370400440532019901DE72370400440532019902
NLNL91ABNA0417164300NL87ABNA0417169901NL60ABNA0417169902
FIFI9821421000005678FI5221421000009901FI2521421000009902
SESE4550000000058398257466SE6650000000058398259901SE3950000000058398259902

Real or modeled. Brite's: the paths, the token exchange, the request and response fields, the transaction states and their order, callback bodies, returned-funds notifications, the merchant_reference idempotency, the statement_reference rules, the currency list, the error body and the amount error's text, and the sandbox amounts 11.01–14.01. Modeled by the lab: which error code 11.01 and 11.02 carry, the other error texts, a second between states, the list of payout countries, the IBAN and age rules, the 401, the low-balance and slow-pending amounts, the bank name (Brite's sandbox "Test Bank"), and the Back Office setting as an API call. Callbacks are tried twice (now and after five seconds); Brite retries after 10, 30 and 60 minutes.

Paysafe: paysafecard, Skrill, Neteller, Play+, VIP Preferred, SafetyPay

The Paysafe Payments API (paymenthub v1) on the real host's paths, under /paysafe: a client of https://api.test.paysafe.com (or https://api.paysafe.com) reaches the lab at https://<lab>/paysafe and changes nothing else. One engine for every brand (paymentType PAYSAFECARD, SKRILL, NETELLER; the US gaming rails and SafetyPay Express below). Credentials are ignored: any HTTP Basic API key is taken; its username only names the merchant, each username a merchant of its own (no credentials: lab-merchant).

Webhooks, as Paysafe sends them: the envelope {payload, eventType, eventName, attemptNumber, resourceId, eventDate, links, mode}, signed in a Signature header = Base64(HMAC-SHA256(key, raw body)). The key is the published simulation key bistromathic-lab-simulated-flow (a real merchant's is its Business Portal HMAC key). Events: PAYMENT_HANDLE_PAYABLE / _FAILED / _EXPIRED / _COMPLETED, PAYMENT_COMPLETED / _FAILED, SETTLEMENT_COMPLETED, REFUND_COMPLETED / _FAILED, SA_CREDIT_PENDING / _COMPLETED / _FAILED; each to every subscribed URL.

Magic values

Limits, enforced with Paysafe's errors

Lab rules

Amount (minor units)Effect
…15, from 10.00a withdrawal is PENDING, then COMPLETED ten seconds later
…16, from 10.00a withdrawal is PENDING, then FAILED ten seconds later
…17, from 10.00a refund is PENDING, then FAILED
…24, from 10.00every webhook arrives 20 seconds late
…41, from 10.00no webhooks at all: only a GET tells the outcome
…53, from 10.00the handle create is refused: 500, 1000 internal error
…05, from 10.00the wallet's balance is short: Pay fails, the handle FAILED (3022 insufficient funds)
…06, from 10.00Paysafe's risk checks decline it: the handle FAILED (4002)
…07, from 10.00the handle create is refused: 502, 1001 External Gateway Error
…08, from 10.00the payer confirms too late: the handle EXPIRED
…09, from 10.00the payments call is declined: 402, 5021 (PAYMENT_FAILED)

Real vs modeled. Real (developer.paysafe.com): the paths, the payment-handle → payment flow, statuses, the 15-minute handle, the field names, the webhook envelope and signature, Paysafe's error codes and body {error:{code, message, details, fieldErrors}}, paysafecard's test PIN, caps and payout rules, Skrill's refund rule, Neteller's verification levels. Modeled: which paymenthub code carries a paysafecard gateway refusal (1001, with the gateway's code in details), PENDING refunds settling after 3 s, the unverified-wallet cap, the personas, the PINs, the lab rules, the /monitor check, and the Business Portal stand-in. Rapid Transfer and Skrill 1-Tap are not simulated.

Play+ and VIP Preferred (US gaming rails)

The same API, paymentType SIGHTLINE (Play+) and VIPPREFERRED (VIP Preferred eCheck, Pavilion Payments), USD only. A deposit is a PAYMENT handle with action: REDIRECT to the method's hosted page, PAYABLE once the payer confirms, then /payments. A withdrawal is a STANDALONE_CREDIT handle, PAYABLE at once, then /standalonecredits.

VIP Preferred SSNPlayerRegistered accounts (bank token)What it shows
111223333Morgan Lee, born 1984-06-15, NJJPMorgan Chase Bank, N.A. ••21 (BA9e4dnkCP3S3ZKQ)
Wells Fargo Bank, N.A. ••07 (BARQyFfooxlO5zGC)
registered, two bank accounts
111224444Riley Grant, born 1990-12-03, PAPNC Bank, N.A. ••55 (BA0WdHWAXlK4wxPk)registered; the bank's balance check fails (3022, R01 insufficient funds)
111225555Avery Quinn, born 1976-03-28, MICitibank, N.A. ••90 (BAPouvZaClXcbP2S)in Paysafe's negative database: payments and payouts refused (4001)

Limits (modeled; Paysafe's pages give none): Play+ $10,000 per deposit, VIP Preferred $5,000 per eCheck, refused with 3027. Gambling cashiers offer both in the states with regulated online casino gaming (NJ, PA, MI, WV, DE, CT, RI) and for sports betting in more; the lab leaves the state to the operator's geolocation.

SafetyPay Express (LatAm bank transfer and cash)

paymentType ONLINE_BANK_TRANSFER with paymentDetails: {countryCode, bankNameCodes?}, or SAFETYPAY_CASH with safetypayCash: {countryCode, bankNameCodes?}; profile.email required; paymentExpiryMinutes (default 15). Amounts are in Paysafe's two-decimal minor units, CLP and COP included (CLP 123 is 12300). The handle is PAYABLE once the payer has picked a bank or cash point and has a payment code (the handle's gatewayResponse.operationId); /payments then answers PROCESSING until the code is paid (PAYMENT_COMPLETED webhook), COMPLETED when it already is, and the payment FAILS when the code expires unpaid (1001 with PAYMENT_EXPIRED in details, modeled). A PAYABLE handle nobody uses is paid by Paysafe itself when it expires: PAYMENT_HANDLE_EXPIRED with statusReason: AUTO_SETTLE_EXPIRED_PAYMENT_HANDLE, then the payment. Payments only: no refunds (3419), no standalone credits (5040).

Real vs modeled. Real (Paysafe's Play+, VIP Preferred and SafetyPay Express pages and its OpenAPI file): the payment types, the brand objects, the redirect → PAYABLE → payment and standalone-credit sequences, VIP Preferred's bank accounts, bank tokens and Verification API, SafetyPay's countries, currencies, bank codes and limits, its PROCESSING payments, auto-settle and error pairs. Modeled: every payer page (Paysafe hosts them and documents none), the Play+ and VIP Preferred caps, the personas, the SMS codes, the expired-code error and the cash desk.

Paysafe's errors the lab answers

1000 500 An internal error occurred.
1001 502 External Gateway Error
1007 500 An internal error occurred.
3022 402 The card has been declined due to insufficient funds.
3027 402 The external processing gateway has reported a limit has been exceeded.
3029 402 The external processing gateway has rejected the transaction.
3402 402 The requested Refund amount exceeds the remaining Settlement amount.
3404 402 The Settlement has already been fully refunded.
3406 402 The Settlement you are attempting to Refund has not been batched yet. There are no settled funds available to refund.
3416 402 The external processing gateway for which your merchant account is configured does not support partial Settlements.
3418 402 The external processing gateway for which your merchant account is configured does not support partial Credits.
3419 402 This type of transaction cannot be refunded.
4001 402 The card number or email address associated with this transaction is in our negative database.
4002 402 The transaction was declined by our Risk Management department.
5001 400 The submitted currency code is invalid or your account does not support this currency.
5003 400 You submitted an invalid amount with your request.
5010 400 The submitted country code is invalid.
5021 402 Your transaction request has been declined.
5023 400 The request is not parsable.
5031 409 The transaction you have submitted has already been processed.
5040 401 Your merchant account is not configured for the transaction you attempted.
5042 400 The merchant reference number is missing or invalid or it exceeds the maximum permissible length.
5068 400 Field error(s)
5269 404 The ID(s) specified in the URL do not correspond to the values in the system.
5272 406 The Content-Type of the request is not supported.
5273 404 The URL is invalid.
5279 401 The authentication credentials are invalid.
5280 401 The required authentication credentials were not provided.
5281 405 The HTTP method is not supported by the requested resource.

Trustly

Trustly's JSON-RPC 1.1 API on test.trustly.com's own path, under /trustly: a Trustly client swaps https://test.trustly.com for this host's /trustly. Credentials are ignored: any Username, Password and request Signature will do. The Username names the merchant: each one has its own orders, ledger and balance (1 000 000.00 in each currency to start). Answers and notifications are signed the Trustly way (SHA1withRSA over method + UUID + Trustly's serialisation of data, Base64) with the lab's published test key in place of Trustly's: its public key is in bistromathic-lab/src/trustly/trustly-simulation-keys.ts, and a connector trusts it only in its simulated flow. SEK, EUR, DKK, NOK, PLN, GBP and CZK.

Notifications go to NotificationURL as Trustly sends them: account (when the payer picks an account: accountid, verified, clearinghouse, bank, descriptor, lastdigits, name; with KYC also personid, address, zipcode, city), kyc (Pay N Play, sent while the payer waits: an answer with "status": "FINISH" aborts), pending (signed), credit (2 s later), cancel, debit (a deposit taken back), payoutconfirmation (3 s after an AccountPayout; a credit if the money comes back). Trustly's HoldNotifications: "1" holds them for the lab drawer's notification tool. An order nobody finishes is cancelled after 30 minutes. Refunds answer at once (a pending deposit's refund is queued until the credit) and send nothing. Each notification is tried twice; Trustly wants its signed {"status": "OK"} back, and the lab logs any other 2xx.

Test cases ("Set order options", in the lab drawer)

Trustly's test checkout names these cases; what each does here is the lab's reading of it.

CaseNameEffect
D1Credit, then debitcredit as usual, then 30 s later a debit notification: the bank took the deposit back
D2Deprecateddeprecated by Trustly; the lab treats it as a normal deposit
D3Pending, then cancelpending once the payer signs, then the bank rejects the transfer: cancel after 10 s, no credit
D4Delayed creditpending once the payer signs; the credit arrives 60 s later (a bank without instant payments)
D5Declined by TrustlyTrustly's risk check declines the order when the payer confirms: cancel, the payer goes to FailURL
W1Payout returnedthe withdrawal is confirmed, then returned by the receiving bank 20 s later: a credit notification gives the money back
G1Late notificationsevery notification arrives 20 s late
G2Duplicate notificationsevery notification is sent twice, with the same notificationid
G3Hold notificationsnothing is sent until the lab drawer's notification tool releases the held notifications

Lab rules (when no test case is set)

Deposit amountEffect
…13, from 10.00Trustly's risk engine suggests a lower amount (half) at confirm; the payer accepts it or cancels
…24, from 10.00every notification arrives 20 s late
…41, from 10.00pending once the payer signs, then the bank never confirms: cancel after 2 minutes
…53, from 10.00Deposit refused: HTTP 503, no JSON-RPC answer (Trustly unavailable)
…05, from 10.00the payer's bank refuses at signing, insufficient funds: cancel
…07, from 10.00Deposit refused: 620 ERROR_UNKNOWN
AccountPayout / Refund amountEffect
…13, from 10.00payoutconfirmation, then 20 s later the receiving bank returns it: a credit notification
…17, from 10.00the payout fails before it is confirmed (FAILED): a credit notification gives the money back
…41, from 10.00EXECUTED for 2 minutes before the payoutconfirmation
…05, from 10.00AccountPayout (and Refund) answered result "0": not accepted

Markets, banks and limits

Trustly has no fixed limit: the payer's bank decides. One payment over the bank's limit (below, in the market's currency) is refused at signing (cancel). * from a third-party table (unverified); the rest are the lab's. A deposit an account cannot cover greys that account out. The payers are invented, one per market (Sweden also has a 16-year-old, for Pay N Play's age check).

CountryCurrencyLoginBanks (limit per payment)
SESEKMobile BankIDSwedbank 500,000*, SEB 150,000, Handelsbanken 50,000*, Nordea 100,000, Danske Bank 100,000, Länsförsäkringar 150,000, ICA Banken 60,000
FIEURthe bank's appOP 15,000, Nordea 10,000, Danske Bank 10,000, S-Pankki 5,000, Aktia 5,000
EEEURSmart-IDSwedbank 15,000, SEB 15,000, LHV 10,000, Luminor 10,000, Citadele 5,000
LVEURSmart-IDSwedbank 15,000, SEB 15,000, LHV 10,000, Luminor 10,000, Citadele 5,000
LTEURSmart-IDSwedbank 15,000, SEB 15,000, LHV 10,000, Luminor 10,000, Citadele 5,000
DEEURlogin + pushTANSparkasse 5,000, Deutsche Bank 10,000, Commerzbank 10,000, ING 10,000
NLEURthe bank's appING 10,000, ABN AMRO 50,000, Rabobank 10,000
DKDKKthe bank's appDanske Bank 50,000, Nordea 50,000, Jyske Bank 50,000
PLPLNthe bank's appPKO BP 20,000, mBank 50,000, Pekao 20,000
NONOKthe bank's appDNB 100,000, SpareBank 1 100,000, Nordea 100,000
GBGBPthe bank's appBarclays 25,000, HSBC 25,000, Lloyds 25,000, NatWest 50,000, Monzo 25,000
CZCZKthe bank's appČeská spořitelna 200,000, ČSOB 200,000, Komerční banka 200,000, Air Bank 200,000

Errors

CodeNameWhenSource
616ERROR_INVALID_CREDENTIALSUsername unknown or Password wrongTrustly's
620ERROR_UNKNOWNSomething went wrong at TrustlyTrustly's
623ERROR_INVALID_PARAMETERSA parameter is missing or malformedTrustly's
636ERROR_UNABLE_TO_VERIFY_RSA_SIGNATUREThe request's Signature does not verifymodeled
637ERROR_INVALID_FUNCTIONThe method is not one the lab servesmodeled
638ERROR_MALFORMED_NOTIFICATIONURLNotificationURL is not https, or carries a '?'modeled
639ERROR_DUPLICATE_MESSAGE_IDAn AccountPayout with this MessageID exists already: a payout is never made twicemodeled
640ERROR_INVALID_AMOUNTAmount is not digits with two decimals, or not above zeromodeled
641ERROR_CURRENCY_NOT_SUPPORTEDNot one of SEK, EUR, DKK, NOK, PLN, GBP, CZKmodeled
642ERROR_INVALID_ORDER_IDNo such order for this merchantmodeled
643ERROR_ACCOUNT_NOT_FOUNDAccountID never came from this merchant's account notificationsmodeled
644ERROR_NOT_ALLOWEDThe order is not in a state that allows thismodeled
645ERROR_INSUFFICIENT_FUNDSThe merchant's Trustly balance cannot cover the payoutmodeled
646ERROR_REFUND_AMOUNT_TOO_HIGHThe refund exceeds what is left of the depositmodeled

CircoFlows (Circoflow)

CircoFlows' card gateway on the real host's paths, under /circoflow: a client of https://gateway.circoflows.com/api/v1/test (sandbox) or …/api/v1 (production) reaches the lab at https://<lab>/circoflow/api/v1/test or …/circoflow/api/v1 and changes nothing else. JSON, snake_case, amounts as decimal strings, lowercase statuses. Credentials are ignored: any Authorization: Bearer key is taken and only names the merchant (no key: lab-merchant); sandbox and production keep separate books.

Test cards (the lab's own; any future expiry, any CVV)

CardBrandEffect
4242 4242 4242 4242Visasuccess "Payment completed." (frictionless)
5555 5555 5555 4444Mastercardsuccess "Payment completed." (frictionless)
4111 1111 1111 1111Visasuccess "Payment completed." (frictionless)
4000 0000 0000 3220Visaprocessing "Payment authentication required." with a 3ds_url; authenticated: pending "Payment pending.", success 3 s later
5200 8282 8282 8210Mastercardprocessing with a 3ds_url; authenticated: pending, success 3 s later
4000 0000 0000 0341Visaprocessing with a 3ds_url; declined "3ds_rejected" however the challenge is answered
4000 0000 0000 0077Visapending "Payment pending." with no 3ds_url; success 5 s later, by webhook
4000 0000 0000 0002Visadeclined "Payment failed."
4000 0000 0000 9995Visadeclined "Insufficient funds."
4000 0000 0000 0069Visadeclined "Card expired."
4000 0000 0000 0127Visadeclined "CVV verification failed."
4100 0000 0000 0019Visadeclined "suspected_fraud"
4000 0000 0000 0259Visadeclined "no appropriate payment routes found"
4000 0000 0000 0051Visablocked "Max 2 declines reached for this card in 24 hrs. Please retry after 24 hours." (Circoflow's own velocity rule)
4000 0000 0000 0119Visadeclined "Acquirer Side integration error, …" (a gateway fault)
3782 8224 6310 005Amexdeclined "Payment method not supported"

An encrypted card (ev:…) is recognised by the BIN and last four BabelPay sends in X-Bistromathic-Card-Bin / -Last4.

Lab rules: the amount's last two digits (minor units), any other card

AmountEffect
…21blocked "Max 2 declines reached for this card in 24 hrs. …"
…22declined "no appropriate payment routes found"
…23declined "mid_at_capacity"
…54declined "Card expired."
…59declined "suspected_fraud"
…77pending, then success 5 s later by webhook
…78pending, then declined "Declined by Issuer" 5 s later by webhook
…96HTTP 500 {"message":"Server Error"}; nothing is filed
…983DS challenge, then declined "3ds_rejected" however it is answered
…993DS challenge; authenticated: pending, success 3 s later
…02declined "Declined by Issuer"
…05declined "Insufficient funds."
…06declined in the acquirer's frame "P0002||Payment failed!|Insufficient funds||billaddress:…"
…07declined "Issuer Decline-Invalid CVV"

Any other amount is approved. Also: an expiry in the past is declined "Card expired."; an expiry year after 2050, an empty field or a malformed one is a validation message; a merchant_transaction_id used before is declined "Duplicate merchant_transaction_id not allowed".

Real vs modeled. CircoFlows publishes no API document; the API is reverse-documented from BabelPay's integration and the answers it has seen. Real (as observed): the two endpoints, the field names, amounts as strings, the statuses and their casing, the sandbox's and production's reason strings (including the acquirer's pipe frame and the "Issuer Decline-" prefix), blocked, "Transaction not found." for a refused payment, asynchronous settlement after 3DS, a never-opened 3DS page never expiring, the unsigned payment.notify webhook, created_at without a zone. Modeled: which card or amount gives which outcome, the HTTP statuses of validation answers, the exact wording of validation messages other than the observed ones, the 3 s and 5 s settlement delays, the 10-minute challenge timeout, a webhook for a payment approved at create, the masked payment_info, and the velocity block (answered by magic values, not by counting declines). Circoflow offers no refund, capture or void to BabelPay, so the lab serves none.

CC7995 (cards)

The CC7995 API (the bundled CC7995 API.pdf, the only reference) on the real host's paths, under /cc7995: a client of https://api.7995-endpoint-b.com reaches the lab at https://<lab>/cc7995 and changes nothing else. Credentials are ignored: X-Api-ClientId only names the calling system (each its own payments; none: lab-client), X-Api-Signature is never checked; an X-Api-RequestDate more than ten minutes off is refused with 6, as CC7995 does. A card arrives as vault ciphertext on a simulated BabelPay connection (no Evervault relay in front of the lab); the lab knows its test cards by BabelPay's X-Bistromathic-Card-Bin / -Last4 hint.

Postbacks (§6), as CC7995 sends them: POST JSON {data:{…the status item…}} to the PaymentCallbackUrl, with X-Signature = lower-case hex SHA-512(ClientId + body + ClientSecret); the ClientSecret is the published simulation secret bistromathic-lab-simulated-flow, the ClientId the payment's own. Delivered on HTTP 200; sent when a payment is decided (COMPLETED or DECLINED).

Test cards (the lab's: CC7995 publishes none)

Any future expiry, any CVV, a cardholder name of two Latin words.

CardBrandWhat it does
4242 4242 4242 4242VISACOMPLETED, frictionless (the confirmation page only)
5555 5555 5555 4444MASTERCARDCOMPLETED, frictionless
4000 0000 0000 3220VISA3-D Secure challenge, then COMPLETED
5200 8282 8282 8210MASTERCARD3-D Secure challenge, then COMPLETED
4000 0000 0000 0341VISA3-D Secure challenge that fails however it is answered: DECLINED "User Authentication Failed"
4000 0000 0000 0002VISADECLINED "Authorization declined 05 DECLINED" (do_not_honor)
4000 0000 0000 9995VISADECLINED "Insufficient Funds"
4000 0000 0000 0069VISADECLINED "transaction declined (wrong expiry date)" (expired_card)
4000 0000 0000 0127VISADECLINED "invalid CVV/brand combination"
4100 0000 0000 0019VISADECLINED "Blocked due to risk score" (risk_block)
4000 0000 0000 0259VISADECLINED "Restricted card"
4000 0000 0000 0119VISADECLINED "error response from connector/acquirer" (unable_to_authorize)
4000 0000 0000 0051VISADECLINED "risk management transaction timeout" (unable_to_authorize)
4000 0000 0000 6975VISADECLINED "transaction declined (maximum transaction frequency exceeded)"
4000 0000 0000 4954VISADECLINED "transaction declined (suspecting manipulation)" (suspected_fraud)
3782 8224 6310 005AMEXRefused at the card payment: HTTP 400, error 209 Unsupported card (any card but Visa or Mastercard)

Lab rules, for any other card

Amount (minor units)Effect
…51DECLINED "Blocked due to risk score"
…983-D Secure challenge that fails: DECLINED "User Authentication Failed"
…993-D Secure challenge, then COMPLETED
…02DECLINED "Authorization declined 05 DECLINED"
…05DECLINED "Insufficient Funds"
anything elseCOMPLETED, without a challenge

Real vs modeled. Real (the PDF, and the live gateway as BabelPay's integration notes record it): the paths, headers, field names and validity rules, the two-step debit, the status codes and texts, the decline reasons (verbatim from production), the error codes and texts, 209's shape, the postback and its signature, the default one-hour timeout, 20 ids per status call, HTTP 400 for a status call without payment_ids (the live gateway has no listing form) and HTTP 502 for an id that is not a CC7995 payment_id. Modeled: the test cards and amount rules, the confirmation page's look (CC7995 publishes none), the HTTP statuses of errors (401 for 1/5/6, 400 otherwise; §3 says only "4xx"), the error for an unknown payment_id (1000), the ten-minute request-date window, the 25,000 cap, transaction_id and payment_method_* values, postbacks only on a final status, the duplicate rule, and the platform administration stand-in. Not simulated: 50 (no ClientId requires a beneficiary URL), 200–202, 211, recurring payments, the method web form's other methods.

CC7995's errors (§3)

1 Authentication failed or access denied
5 X-Api-Signature verification failed
6 Request header X-Api-RequestDate is out of date
50 Beneficiary URL required
51 Beneficiary URL is of incorrect format or invalid
200 Creating payments not allowed for the specified ClientID
201 Card payments not allowed for the specified ClientID
202 Card payments not allowed for the recipient
203 Payment with the specified payment_id already processed
204 Card number (PAN) is of incorrect format or invalid
205 Card expiration date is of incorrect format or invalid
206 Card cvv/cvc2 is of incorrect format or invalid
207 Cardholder name is of incorrect format or invalid
208 Payer email is of incorrect format or invalid
209 Unsupported card. Try another one
210 URL not allowed
211 Payer country not allowed
212 Amount not allowed
1000 General error

Argent Platforms: cards

The Argent Platforms API on its two hosts' real paths: a client of https://api.argentplatforms.com reaches the lab at https://<lab>/argent, and of https://tokenise.argentplatforms.com at https://<lab>/argent/tokenise, and changes nothing else. Credentials are ignored: HTTP Basic with the secret key as username is taken whatever it holds; the key (hashed) only names the merchant, each key a company of its own (no credentials: lab-merchant). public_key must be present, as Argent requires it, but any value is taken. Every answer says live: false.

3-D Secure. The action is a 3-D Secure 2 challenge: action_data carries acs_url, a creq and three_ds_session_data (the method card first gives three_ds_method_url and three_ds_method_data; continue with threeDSCompInd Y, N or U in action_data and the challenge follows). The payer posts the CReq to /argent-lab/acs/{token}, the issuer's challenge (two seconds to verify), whose CRes goes through the browser to Argent's 3DS server and on to the merchant's return_url with payment_intent_id and status appended. Without can_handle_3ds: true a challenge card fails ("This transaction requires authentication").

Webhooks, to every URL subscribed to the event: {id, event, created_at, live, data} (a refund's data is its RefundDetail), with X-ArgentPlatforms-Signature = hex HMAC-SHA256 of the raw body under the published simulation secret bistromathic-lab-simulated-flow (a real company's is its own signing secret).

Test cards

CardWhat it doesProcessorSource
4242424242424242Payment succeedsfakeArgent's Testing docs
4000000000000002Payment fails: "Your card was declined."stripe_standardArgent's Testing docs
40000000000032203-D Secure challenge: confirm to pay; fail or cancel it and the payment failslab_3dsArgent's sandbox (observed)
40000025000031553-D Secure challenge, as 4000000000003220lab_3dsArgent's sandbox (observed)
40000027600031843-D Secure challenge, as 4000000000003220lab_3dsArgent's sandbox (observed)
40000000000030553-D Secure frictionless: the issuer authenticates without a challenge and the payment succeedsstripe_standardStripe's test number
40000000000032533DS method (device data) step first, then the challengelab_3dsthe lab
40000084000016293-D Secure challenge; once authenticated the payment fails: "Your card was declined."lab_3dsStripe's test number
40000082600031783-D Secure challenge; once authenticated the payment fails: "Your card has insufficient funds."lab_3dsStripe's test number
4000000000009995Payment fails: "Your card has insufficient funds."stripe_standardStripe's test number
4000000000000069Payment fails: "Your card has expired."stripe_standardStripe's test number
4000000000000127Payment fails: "Your card's security code is incorrect."stripe_standardStripe's test number
4000000000000119Payment fails: "An error occurred while processing your card. Try again in a little bit."stripe_standardStripe's test number
any other valid numberAny other valid card number fails: "Your card was declined."stripe_standardArgent's Testing docs

Lab rule

An amount ending in 5.03 (5.03, 15.03, …): the intent is created and runs as its card says, but the create answer is 503 Service Unavailable, a lost answer for reconciliation to find by reference.

Limits

Real vs modeled. Real (argent-openapi.yaml and BabelPay's sandbox run of 2026-10-27): the hosts and paths, Basic auth with the secret key, public_key in tokenise, create and continue, the tokenise → intent → PATCH → GET flow, the statuses, the 200 action answer's shape, absent values as null, string amounts, the nested currency, payment and error objects, the id prefixes, DRF's error bodies, the tokenise refusal, "Unsupported currency", payment_intent_id and status on the return, cursor pagination, the webhook events and the signature header. Modeled: the action itself (the sandbox's is a Stripe PaymentIntent for Stripe.js, which the lab cannot be), the webhook body, the refund's id prefix and refusal wording, the processors' names for the lab's cards, Stripe's decline numbers and reasons, the method card, the lost-answer rule, the currency list, the dashboard stand-in.

HappyCoins (Onramp Pay): crypto onramp

HappyCoins' Onramp Pay API on the real host's paths, under /happycoins: a client of https://dev-api-onramp-pay.up.railway.app (or https://api.onramp-pay.happycoins.com) reaches the lab at https://<lab>/happycoins and changes nothing else. The apiKey header is never checked: it only names the merchant, each key a merchant of its own (no key: lab-merchant). HappyCoins has no webhook: the merchant polls.

Status and step move together: STARTED/INITIATED at create; IN_PROCESS through URL_VISITED, WALLET_ADDRESS, KYC_STARTED; VERIFIED/KYC_DONE; IN_PROCESS through FORM_VISITED, FORM_VISITED_DOB, FORM_SUBMITTED, ORDER_SUMMARY; PENDING/FIAT_PAYMENT while the card is charged (2 s); IN_PROCESS/PAYMENT_DONE; IN_PROCESS/SEND_TO_CUSTOMER once the crypto is in the buyer's wallet (3 s); then COMPLETED/SEND_TO_CUSTOMER when the buyer pays the merchant (where HappyCoins' paid orders stay) or COMPLETED/KEPT_BY_CUSTOMER when they keep it. DECLINED, FAILED, REJECTED, ERROR, KYC_ATTEMPT_EXCEEDED and TIMEOUT end an order where it stands; opening the link again mid-flow is RELOADED.

Magic values

Lab rules (the fiat amount's last two digits in minor units)

AmountEffect
…03Identity verification fails every attempt: the third ends the order KYC_ATTEMPT_EXCEEDED (risk_block)
…04HappyCoins rejects the order at the order summary, before the card: REJECTED (do_not_honor)
…06The card is charged, then buying the crypto fails: ERROR (psp_malfunction)
…07After the buyer pays the merchant, delivery follows HappyCoins' documented tail: SEND_TO_CUSTOMER, then RECEIVED_TO_CUSTOMER (the hash appears), SEND_TO_MERCHANT and COMPLETED, four seconds apart
…08Open question 3(h): COMPLETED/SEND_TO_CUSTOMER is served while the buyer is still choosing whether to pay the merchant or keep the crypto

Real vs modeled. Real (HappyCoins' guide rev. 2026-08-19, the status endpoint and vocabularies of 2026-08-24, sandbox traffic to 2026-08-31): the paths, the apiKey header, the request and response fields, major-unit amounts, the UUID paymentRequestId echoed back and its uniqueness, the 422 and 404 shapes and the messages quoted above, the restricted residences and reserved emails, the lookup by request id, the status and step vocabularies, COMPLETED/SEND_TO_CUSTOMER as a paid order's last state and KEPT_BY_CUSTOMER under COMPLETED, the crypto leg's shape and ALGO at EUR 0.0795 (1257.861635 ALGO for EUR 100.00). Modeled: the hosted checkout's screens and wording, which status accompanies each step, the other validation messages, the fiat currencies, the prices other than ALGO's, the card's test cards (HappyCoins publishes none) and the 3-D Secure step, the crypto leg's status values (In_Process, Completed, Failed), the 15-minute TIMEOUT (HappyCoins' is hours), the lab rules, and the documented delivery tail of rule …07, which HappyCoins' sandbox has never been seen to reach. crypto-accept is unspecified and not simulated.

Worldpay (WPG, Direct XML)

Worldpay's paymentService XML API (DTD v1.4) on secure-test's paths, under /worldpay: a client of https://secure-test.worldpay.com reaches the lab at https://<lab>/worldpay and changes nothing else. Credentials are ignored: any HTTP Basic merchant code and XML password are taken; the paymentService merchantCode attribute names the merchant (else the Basic username), each code a merchant of its own.

Order notifications, as WPG sends them: <notify><orderStatusEvent orderCode> with the payment and the journal entry, POSTed as text/xml, unsigned, for AUTHORISED, REFUSED, ERROR, CAPTURED, CANCELLED, SENT_FOR_REFUND, REFUNDED and REFUND_FAILED, and for a payout's PUSH_PENDING, PUSH_APPROVED and PUSH_REFUSED (the scheme named VisaDirect / MastercardSend, as Worldpay's samples spell it). Worldpay expects [OK] in the answer; the lab logs an answer without it.

3DS Flex keys: challenge and DDC JWTs verify with the simulation secret bistromathic-lab-simulated-flow (what a connection in simulated flow signs with) or Worldpay's published secure-test credentials (iss 5bd9e0e4444dce153428c940, OrgUnitId 5bd9b55e4444761ac0af1c80, MAC key fa2daee2-1fbb-45ff-4444-52805d5cd9e0). The Response JWT is signed with the same key.

Magic values (Worldpay's)

Fast Access payouts (Worldpay's disbursement test values)

Lab rules

Real vs modeled. Real (docs.worldpay.com): the path, the XML elements and reply shapes the client reads (orderStatus/payment/lastEvent, ISO8583ReturnCode, ThreeDSecureResult with eci/cavv, challengeRequired/threeDSChallengeDetails, request3DSecure + echoData, the machine cookie, <ok> receipts, journals), the error codes (2 parse, 3 amount, 5 duplicate order / not found / currency, 7 payment details), the magic names, cards, amounts, CVC and AVS values, Cardinal's cases, the step-up's claims and empty 400, the published test JWT credentials; for payouts, the FF_DISBURSE-SSL request (the DTD's), the PUSH_* statuses, schemeResponse and fastFunds, the disbursement magic names and cards, Mastercard Send's validation messages. Modeled: the wording of the lab's own error 5 messages (machine cookie, session id, 3DS not completed), out-of-band modification failures (booked as nothing, a refund over the captured amount as REFUND_FAILED), the capture, refund and pending-push timings, the lab's frictionless default and wallet rules, the issuer's page, the MAI stand-in, an ordinary payout name approved, the action code 05 of a PENDINGREFUSED, the error-simulation cards answered as error 5. Not simulated: "Order not ready" inquiry lag, HPP, tokenisation (payouts to a token), card checks, Money Transfer OCTs (FF_MONEYTRANSFER-SSL), batch orders, settlement (SETTLED, SETTLED_BY_MERCHANT), chargebacks.

Citigate (Orion card gateway)

Citigate's JSON interface (Integration Specification, Credit Card v1.27) on the real host's path, under /citigate: a client of https://gw-test.cgate.tech/orion/interface/json.ashx (or gw.cgate.tech) reaches the lab at https://<lab>/citigate/orion/interface/json.ashx and changes nothing else. One POST, the operation picked by TransTypeID; every answer is HTTP 200 with Citigate's fields (TransactionID as text, TransTypeID, Amount and ResponseCode as numbers). Credentials are ignored: MerchantName only names the merchant (each its own MID; none: lab-merchant), MerchantPassword is never read.

Callback and user return, as Citigate sends them on the JSON interface: a JSON POST to CallbackURL with every response field as text plus Signature = lowercase hex SHA-1 of MerchantPassword + TransactionID + MerchantRef + Currency + Amount + ResponseCode. The lab signs with the published simulation secret bistromathic-lab-simulated-flow in the MerchantPassword's place; a connection in simulated flow verifies with it.

Magic values

CardBrand3-D SecureWhat happens (lab's MID)
4111111111111111VISAnoneApproved, no redirect (Citigate's own example card; so is any other Luhn-valid card)
5555555555554444MASTERCARDnoneApproved, no redirect
378282246310005AMEXnoneApproved, no redirect
4000000000003220VISAchallenge600 Redirect required; the issuer's 3-D Secure challenge, then approved
5200828282828210MASTERCARDchallenge600 Redirect required; the issuer's challenge, then approved
4000000000003253VISAchallenge600 Redirect required; the challenge, then the bank declines: 51 Not sufficient funds
4000000000003063VISAfrictionless600 Redirect required; frictionless 3-D Secure (no challenge), then approved
4000000000000341VISAfrictionless600 Redirect required; the issuer refuses without a challenge: 103 3D authentication failure
4000000000000002VISAnoneDeclined: 5 Do not honour
4000000000009995VISAnoneDeclined: 51 Not sufficient funds
4000000000000069VISAnoneDeclined: 54 Expired card
4000000000000127VISAnoneDeclined: 79 Invalid CVV2/CVC2
4100000000000019VISAnoneDeclined: 59 Suspected fraud
4000000000009235VISAnoneDeclined: 102 General risk decline
4000000000000259VISAnoneDeclined: 91 Issuer or switch is inoperative
4000000000000101VISAnoneDeclined: 105 Timeout at acquirer
4000000000000119VISAnoneDeclined: 96 Acquirer system malfunction

An encrypted card (ev:…) is recognised by BabelPay's BIN and last-four headers. Expiry any future month, CVV any 3 or 4 digits.

Checked, with Citigate's codes

Real vs modeled. Real (Citigate.pdf v1.27): the endpoint, the TransTypeIDs, the field names, Appendix 1's codes and texts, the 600 → redirect → callback → user-return sequence with the callback joined before the return, the SHA-1 signature, the pending/unknown status answers, Dummy1/Dummy2 and the amount rule, the 30-second duplicate guard, partial refunds off by default, auto-capture. Modeled: the decline code of the amount rule (5), TransTypeID 0 / TransactionID "0" on a refused request, the timeout lengths, 589 for a MerchantRef that is not the original's, a refund answering the refunded amount, the lab's MID and test cards, Brand not required of an encrypted card, the payer's name and address fields not required (Citigate marks them mandatory; the sandbox's enforcement is not documented). Not served: the SOAP, XML and NVP interfaces, the Refund and Fraud Notifications (an optional push, off by default), and RaaS (TransTypeID 31 answers 573).

Citigate's response codes

0 Approved
1 Refer to Card Issuer
2 Refer to Card Issuer's Special Condition
3 Invalid Merchant
4 Pick-up
5 Do not honour
12 Invalid transaction
13 Invalid amount
14 Invalid card number
15 No such issuer
19 Re-enter transaction
20 Invalid response
21 No Transaction
30 Format error
31 Bank not supported
33 Expired card, pick-up
34 Issuer suspects fraud, pick-up
35 Contact acquirer, pick-up
36 Restricted card, pick-up
39 No credit account
41 Lost card, pick-up
43 Stolen card, pickup
50 Maintenance in progress
51 Not sufficient funds
54 Expired card
56 No card record
57 Transaction not permitted to cardholder
58 Transaction not permitted to terminal
59 Suspected fraud
61 Exceeds withdrawal amount limit
62 Restricted card
63 Security violation
65 Exceeds withdrawal frequency
78 Invalid account specified
79 Invalid CVV2/CVC2
91 Issuer or switch is inoperative
92 Destination cannot be found for routing
93 Transaction cannot be completed; violation of law
94 Duplicate transaction
96 Acquirer system malfunction
101 Unable to authorize
102 General risk decline
103 3D authentication failure
104 Acquirer limits reached
105 Timeout at acquirer
106 User aborted transaction
107 Missing or invalid client information
108 AVS Response
109 Risk Decline - 3rd Party
110 Risk Decline - 3rd Party
500 TransTypeID not supplied or invalid
501 MerchantReference not supplied
502 MerchantReference invalid
503 Currency not supplied
504 Currency invalid
505 Amount not supplied
506 Amount invalid
507 MerchantName not supplied
508 MerchantName invalid
509 MerchantPassword not supplied
510 MerchantPassword invalid
511 Brand not supplied
512 Brand invalid
513 Firstname not supplied
514 Firstname invalid
515 Surname not supplied
516 Surname invalid
517 StreetLine1 not supplied
518 StreetLine1 invalid
519 StreetLine2 not supplied
520 StreetLine2 invalid
521 City not supplied
522 City invalid
523 PostalCode not supplied
524 PostalCode invalid
525 StateProvince not supplied
526 StateProvince invalid
527 Country not supplied
528 Country invalid
529 Email not supplied
530 Email invalid
531 Telephone not supplied
532 Telephone invalid
533 DateOfBirth not supplied
534 DateOfBirth invalid
535 UserIP not supplied
536 UserIP invalid
537 CardholderName not supplied
538 CardholderName invalid
539 CardNo not supplied
540 CardNo invalid
541 ExpiryYear not supplied
542 ExpiryYear invalid
543 ExpiryMonth not supplied
544 ExpiryMonth invalid
545 CVV not supplied
546 CVV invalid
553 Failed Luhn check
554 Card expired
555 TransactionID not supplied
556 TransactionID invalid
557 Invalid MerchantName and MerchantPassword combination
559 Error looking up original transaction info
560 No corresponding successful authorisation
561 Transaction has already been captured
562 Transaction has already been cancelled
563 Transaction has already been refunded
564 No corresponding successful sale or capture
567 Unknown Brand
568 Unknown Currency
573 TransTypeID not supported on current MID
584 SuccessURL invalid
585 FailURL invalid
586 CallbackURL invalid
589 Invalid reference combination
590 Invalid payload received
597 Duplicate transaction (successful purchase in last 30 secs with same card, MID, currency and amount)
600 Redirect required
621 Recurring transactions not possible with 3D MIDs
622 No corresponding purchase or capture
629 Partial refund not supported
630 Partial refund amount exceeds available amount
700 No user redirect (timeout)
800 No user return (timeout)
999 Transaction Pending

Blueemi (Checkout.com's Unified Payments API)

Blueemi is a white-label of Checkout.com's Unified Payments API, and the lab serves that API on the real host's paths, under /blueemi: BabelPay's blueemi connector in simulated flow swaps the Evervault relay in front of api(.sandbox).checkout.com for https://<lab>/blueemi and changes nothing else. Credentials are ignored: any Authorization: Bearer key is taken and only names the merchant (a simulated connection sends simulated-<connection id>; any other key names its merchant by a hash and is never kept). The card arrives as vault ciphertext with its BIN and last four in X-Bistromathic-Card-Bin / -Last4, or in clear.

Webhooks, Checkout.com's envelope {id: "evt_…", type, version, created_on, data: {id, action_id, reference, amount, currency, response_code, response_summary, balances, source, …}, _links}, signed blueemi-Signature = hex HMAC-SHA256(key, raw body), the key being the published simulation key bistromathic-lab-simulated-flow. The lab has no dashboard: they go to the callback_url in the payment's metadata. Events: payment_approved, payment_declined, payment_captured / _capture_declined, payment_voided / _void_declined, payment_refunded, card_verified / card_verification_declined; a refund's or capture's event carries that action's id, amount and reference.

Test cards (Checkout.com's): the issuer's answer

Any future expiry (a past one: 422 card_expired); a 3-digit CVV, 4 for American Express. A card with no 3DS profile is not enrolled: with attempt_n3d it is authorized at once without 3DS (3ds.downgraded: true), without it declined 20150.

91 cards
Card numberSchemeWhat it does
4543474002249996Visa, USApproved (the card in Checkout's token example)
4000020000000000Visa, USApproved
4242424242424242Visa, GBApproved; amount 101 (1.01) declines 20051 Insufficient funds
4273149019799094Visa, TRCard verifications only: approves, but captures and voids are declined
4539467987109256Visa, ESDeclined 20005 Do not honour
4024007181869214Visa, USDeclined 20005 Do not honour
4916301720257093Visa, USDeclined 20020 Invalid response
4485899805156040Visa, USDeclined 200R1 Stop payment for this authorization
4659105569051157Visa, GBApproved (debit)
4095254802642505Visa, USDeclined 20068 Response received too late / timeout (debit)
4000148147058142VisaDeclined 20020 Invalid response (prepaid)
5436031030606378Mastercard, MUApproved
5585076576791786Mastercard, HUDeclined 20005 Do not honour
5305484748800098Mastercard, DEApproved (debit)
341829238058580American Express, USDeclined 20005 Do not honour
6011111111111117Discover, USApproved
6011111111111118Discover, USDeclined 20005 Do not honour
6011201559638046790Discover, USDeclined 20005 Do not honour
6445979375892355607Discover, USApproved
36160940933914Diners Club International, KRApproved
36160912071610Diners Club International, KRDeclined 20005 Do not honour
3582069482684995JCB, JPApproved
3528257085901927JCB, JPDeclined 20005 Do not honour
3566027046765007JCB, JPApproved
4276038578596818Visa, USDeclined 2005C Transaction not supported / blocked by issuer
4152812521833588Visa, USDeclined 2009G Blocked by cardholder
4734868958733862Visa, USDeclined 200N7 Decline for CVV2 failure
4567361325981788Visa, GBDeclined 200R3 Stop payment for all authorizations
4111111111111129Visa, USDeclined 20001 Refer to card issuer
4024007103573027Visa, USDeclined 20012 Invalid transaction
4158596042924182Visa, GBDeclined 20012 Invalid transaction
4485381577182090Visa, USDeclined 20014 Invalid card number
4544249167673670Visa, GBDeclined 20051 Insufficient funds
4659465888705671Visa, GBAn account funding transaction declines 20057 Not permitted to cardholder; anything else approves
4151512736305999Visa, GBDeclined 20057 Transaction not permitted to cardholder
4897453568485113Visa, USDeclined 20059 Suspected fraud
4000144348235050VisaDeclined 20059 Suspected fraud (prepaid)
4556294593757189Visa, UYDeclined 20061 Activity amount limit exceeded
4818924250131070Visa, GBDeclined 20062 Restricted card
4556253752712245Visa, USDeclined 20063 Security violation
4532819413487978Visa, USDeclined 20078 Blocked, first use
4732789363145785Visa, PRDeclined 20082 No security model
4000140542610783VisaDeclined 20082 No security model (prepaid)
4500622868341387Visa, CADeclined 20154 3DS authentication required
4539257838438511Visa, IEDeclined 20154 3DS authentication required
4941208378238219Visa, GBDeclined 20154 3DS authentication required
4024007162185267Visa, USDeclined 30015 No such issuer
4655710037275959Visa, GBDeclined 30015 No such issuer
4941202060999329Visa, GBDeclined 30041 Lost card - pick up
4658620059821613Visa, GBDeclined 30041 Lost card - pick up
4539253655711767Visa, IEDeclined 30043 Stolen card - pick up
4745820750643143Visa, GBDeclined 30043 Stolen card - pick up
4000140543766022VisaDeclined 20109 Transaction already reversed
5510696303117979MastercardDeclined 20003 Invalid merchant
5148447461737269Mastercard, USDeclined 20012 Invalid transaction
5510696303117987MastercardDeclined 20030 Format error
5437821135399682Mastercard, TWDeclined 20051 Insufficient funds
5355224542121849Mastercard, GBAn account funding transaction declines 20057 Not permitted to cardholder; anything else approves
5355222710253469Mastercard, GBDeclined 20057 Transaction not permitted to cardholder
5420951756276171Mastercard, TNDeclined 20061 Activity amount limit exceeded
5279988405398834Mastercard, JPDeclined 20062 Restricted card
5265162270587964Mastercard, USDeclined 20068 Response received too late / timeout
5525192142230028MastercardDeclined 20072 Account not yet activated
5347965091482021Mastercard, BRDeclined 20182 Policy
5357219827207436Mastercard, BGDeclined 20183 Security
5363450180402239Mastercard, PHDeclined 30041 Lost card - pick up
5355222710255852Mastercard, GBDeclined 30043 Stolen card - pick up
5525192142230002MastercardDeclined 30046 Closed account
5355222710255878Mastercard, GBDeclined 30046 Closed account
5333170000000008Mastercard, ITApproved up to 100.00; more declines 20051 Insufficient funds
5318773000000008Mastercard, USApproved up to 100.00; more declines 20051 Insufficient funds
4644968546281686Visa, GBVerifies (amount 0); an authorization declines 20012
5355228287185489Mastercard, GBVerifies (amount 0); an authorization declines 20012
4546381219393284Visa, GBVerifies (amount 0); an authorization declines 20051
5355229757805879Mastercard, GBVerifies (amount 0); an authorization declines 20051
4325350188287824Visa, GBVerifies (amount 0); an authorization declines 20051
4543611423334994Visa, GBVerifies (amount 0); an authorization declines 20061
5355222589678168Mastercard, GBVerifies (amount 0); an authorization declines 20061
4394666298116207Visa, GBVerifies (amount 0); an authorization declines 20061
4857662623668665Visa, GBVerifies (amount 0); an authorization declines 20062
5355225201525238Mastercard, GBVerifies (amount 0); an authorization declines 20062
4021277034700287Visa, GBVerifies (amount 0); an authorization declines 20062
4407108123937239Visa, GBVerifies (amount 0); an authorization declines 20063
5355226042898974Mastercard, GBVerifies (amount 0); an authorization declines 20063
3530111333300000JCB, JPNot 3DS-enabled: 20150 with 3DS; approves without (CVV 100)
5352151570003404Mastercard, FRNot 3DS-enabled: 20150 with 3DS; approves without (CVV 100)
4532432452900131Visa, DENot 3DS-enabled: 20150 with 3DS; approves without (CVV 257)
378282246310005American Express, US3DS: card not enrolled (attempt_n3d: authorized without 3DS)
5132562600000045Cartes Bancaires, FR3DS: card not enrolled (attempt_n3d: authorized without 3DS)
4022050100000182Cartes Bancaires, FR3DS: card not enrolled (attempt_n3d: authorized without 3DS)
4484070000035519Visa, GB3DS: card not enrolled (attempt_n3d: authorized without 3DS)

3DS test cards

Authenticated (Y) or attempted (A): authorized, then the amount rules decide. Not authenticated (N) or rejected (R): declined 20151. Could not be performed (U) or a scheme error (E): with attempt_n3d authorized without liability shift (ECI 07/00), otherwise 20153. A challenge cancelled or failed is not authenticated.

Frictionless
Card numberSchemeWhat it does
345678901234564American Express, ES3DS frictionless: authentication successful
5137210000000018Cartes Bancaires, FR3DS frictionless: authentication successful
4010056200000018Cartes Bancaires, FR3DS frictionless: authentication successful
5588686116426417Mastercard, CN3DS frictionless: authentication successful
6224120000000094UnionPay, CN3DS frictionless: authentication successful
4485040371536584Visa, US3DS frictionless: authentication successful
371064645462927American Express, US3DS frictionless: not authenticated
5132562600000029Cartes Bancaires, FR3DS frictionless: not authenticated
4022050100000000Cartes Bancaires, FR3DS frictionless: not authenticated
5309961755464047Mastercard, BR3DS frictionless: not authenticated
6224120000000086UnionPay, CN3DS frictionless: not authenticated
4539628347117863Visa, AD3DS frictionless: not authenticated
344282679519362American Express, US3DS frictionless: authentication could not be performed
5132562600000011Cartes Bancaires, FR3DS frictionless: authentication could not be performed
4022050100000018Cartes Bancaires, FR3DS frictionless: authentication could not be performed
5234106378657904Mastercard, KR3DS frictionless: authentication could not be performed
6224120000000078UnionPay, CN3DS frictionless: authentication could not be performed
4024007186645015Visa, US3DS frictionless: authentication could not be performed
4022050100000026Cartes Bancaires, FR3DS frictionless: attempted authentication
5558468902774508Mastercard, KE3DS frictionless: attempted authentication
6224120000000029UnionPay, CN3DS frictionless: attempted authentication
4556574722325580Visa, PT3DS frictionless: attempted authentication
343397288296672American Express, US3DS frictionless: authentication rejected
5596061690670931Mastercard, IN3DS frictionless: authentication rejected
6224120000000011UnionPay, CN3DS frictionless: authentication rejected
4275765574319271Visa, JP3DS frictionless: authentication rejected
378702153281139American Express, US3DS frictionless: error during scheme communication
5132562600000060Cartes Bancaires, FR3DS frictionless: error during scheme communication
4022050100000133Cartes Bancaires, FR3DS frictionless: error during scheme communication
5291144083573579Mastercard, US3DS frictionless: error during scheme communication
4452927588210665Visa, US3DS frictionless: error during scheme communication
340282533032846American Express, US3DS frictionless: authentication successful (no 3DS method URL)
36747880216043Diners Club International, US3DS frictionless: authentication successful (no 3DS method URL)
3528982710432481JCB, JP3DS frictionless: authentication successful (no 3DS method URL)
5121073611487018Mastercard, US3DS frictionless: authentication successful (no 3DS method URL)
6224120000000110UnionPay, CN3DS frictionless: authentication successful (no 3DS method URL)
4556085951507152Visa, ES3DS frictionless: authentication successful (no 3DS method URL)
Challenge
Card numberSchemeWhat it does
372688581899681American Express, US3DS challenge: authentication successful
5137210000000158Cartes Bancaires, FR3DS challenge: authentication successful
4010061700000021Cartes Bancaires, FR3DS challenge: authentication successful
5385308360135181Mastercard, US3DS challenge: authentication successful
6224120000000003UnionPay, CN3DS challenge: authentication successful
346126139326850American Express, US3DS challenge: attempted authentication
5341034800000008Cartes Bancaires, FR3DS challenge: attempted authentication
4150561000000001Cartes Bancaires, FR3DS challenge: attempted authentication
5518832400606463Mastercard, US3DS challenge: attempted authentication
6224120000000060UnionPay, CN3DS challenge: attempted authentication
4152868552773614Visa, BH3DS challenge: attempted authentication
375982239796002American Express, US3DS challenge: authentication rejected
5341034800000016Cartes Bancaires, FR3DS challenge: authentication rejected
6224120000000052UnionPay, CN3DS challenge: authentication rejected
4150561000000019Cartes Bancaires, FR3DS challenge: authentication rejected
347411540178242American Express, US3DS challenge: not authenticated
5341034800000024Cartes Bancaires, FR3DS challenge: not authenticated
4150561000000027Cartes Bancaires, FR3DS challenge: not authenticated
5258901507741160Mastercard, CA3DS challenge: not authenticated
6224120000000045UnionPay, CN3DS challenge: not authenticated
4243754271700719Visa, US3DS challenge: not authenticated
377990956548835American Express, US3DS challenge: authentication could not be performed
5341034800000032Cartes Bancaires, FR3DS challenge: authentication could not be performed
4150561000000035Cartes Bancaires, FR3DS challenge: authentication could not be performed
5569757734785691Mastercard, US3DS challenge: authentication could not be performed
6224120000000037UnionPay, CN3DS challenge: authentication could not be performed
4447336775378848Visa, US3DS challenge: authentication could not be performed
5341034800000040Cartes Bancaires, FR3DS challenge: error during scheme communication
4150561000000043Cartes Bancaires, FR3DS challenge: error during scheme communication
5572408422189334Mastercard, IE3DS challenge: error during scheme communication
4539929207120660Visa, IN3DS challenge: error during scheme communication
372310461284948American Express, US3DS challenge: authentication successful (no 3DS method URL)
5341034800000057Cartes Bancaires, FR3DS challenge: authentication successful (no 3DS method URL)
4150561000000183Cartes Bancaires, FR3DS challenge: authentication successful (no 3DS method URL)
36138956596288Diners Club International, AT3DS challenge: authentication successful (no 3DS method URL)
3528682570029332JCB, JP3DS challenge: authentication successful (no 3DS method URL)
5259410220714099Mastercard, US3DS challenge: authentication successful (no 3DS method URL)
6224120000000128UnionPay, CN3DS challenge: authentication successful (no 3DS method URL)
4485597929486000Visa, US3DS challenge: authentication successful (no 3DS method URL)

Amount rules (any other card, and 3DS cards after authentication)

From Checkout.com's earlier sandbox documentation, in minor units; an expiry of 01/2099 skips them. Any other amount approves.

2011 → 200R1 Issuer initiated a stop payment (revocation order) for this authorization
2013 → 200R3 Issuer initiated a stop payment (revocation order) for all authorizations
4001 → 40101 Risk Blocked Transaction
4008 → 40108 Gateway Reject - Post code failed
9998 → 20068 Response received too late / Timeout
…12 → 20012 Invalid transaction
…14 → 20014 Invalid account number (no such number)
…33 → 30033 Expired card - Pick up
…51 → 20051 Insufficient funds
…54 → 20087 Bad track data (invalid CVV and/or expiry date)
…62 → 20062 Restricted card
…63 → 20063 Security violation
…05 → 20005 Declined - Do not honour (from 1.00)
With 3DS on, any other card: 6900 or ending in 150 → 20150, 5000–5029 → 20153, 6510/6520/6530/6540 → 20154, at once.

Real vs modeled. Real (checkout.com/docs, api-reference.checkout.com): the paths, the 201/202/403/404/422 answers and bodies, statuses and their casing, pay_/act_/sid_/src_/cus_/evt_ ids, the balances, actions only by session id, the 15-minute session, the webhook envelope and its hex signature, the test cards and response codes. Modeled: the hosted 3DS page's look (Checkout's sandbox shows its own simulator, password Checkout1!), which cards have a 3DS profile (only the 3DS tables'), what U/E results and a cancelled challenge lead to, the earlier sandbox's amount rules applied to every other card, webhooks to metadata.callback_url (two attempts, not Checkout's eight over 24 hours), captures/voids/refunds settling at once. Payouts, tokens, instruments, customers' API, incremental authorizations and idempotency keys are not simulated.

Pay.nl

Pay.nl's Order API (the TGU, connect.pay.nl/v1) and its Transaction:Refund (rest.pay.nl/v2) on the real hosts' paths, under /paynl: a client of https://connect.pay.nl (or its failover core connect.payments.nl) and https://rest.pay.nl reaches the lab at https://<lab>/paynl for both and changes nothing else. Credentials are ignored: any HTTP Basic pair is taken; the username (the AT code) only names the merchant, each its own (no credentials: lab-merchant). The sales location's boarded methods: 10 iDEAL (EUR only), 436 Bancontact (EUR only), 11 Creditcard, 138 PayPal, 1717 Klarna, 2970 Pay by Bank (EUR only), 2277 Apple Pay, 2558 Google Pay, 613 Sandbox.

Exchanges, as SIGNED_JSON_POST: {event: "status_changed", type: "order", version: 1, id, object: <the order>} to the order's exchangeUrl (with type, id, event added to its query, for logging), headers signature = hex HMAC-SHA256 of the raw body, signature-method: HMAC, signature-algorithm: SHA256, signature-keyid: <SL code>. The key is the published simulation secret bistromathic-lab-simulated-flow (a real merchant's is its sales location's secret). Sent when an order is paid, authorised, held for VERIFY, voided, denied, failed, cancelled or expired (not for PENDING); Pay.nl wants {"result": true} back. A refund call with its own exchangeUrl gets refund:add and refund:received.

Magic values

Pay.nl's own test mode is its sandbox: "What should be the next payment status?" — Captured / Paid (100), Authorised (95), Aborted (-90), Pending (50), Cancelled (-90), Verify (85), Failed (-60), Denied (-64), Declined (-64), Expired (-80). The lab has it in the drawer of every screen (and as the page of payment method 613), and Pay.nl publishes no other test values. Cards (Pay.nl publishes none; the lab's own, typed on the hosted page):

CardOutcome
4111 1111 1111 1111Paid (100), no challenge; so is any other valid card number
5555 5555 5555 4444Paid (100), no challenge
4000 0000 0000 32203-D Secure challenge, then Paid (100)
5200 8282 8282 82103-D Secure challenge, then Paid (100)
4000 0000 0000 03413-D Secure challenge that fails: Denied (-63)
4000 0000 0000 0002Declined by the issuer: Denied (-63)
4000 0000 0000 9995Insufficient funds: Denied (-63)
4000 0000 0000 0119Processor error: Failure (-60)

Lab rules

Amount (minor units)Effect
…50, from 10.00the payment stays PENDING (50) for 15 seconds, then is PAID (100), with an exchange each time it ends
…53, from 10.00Order:Create is refused: 503 PAY-1503
…60, from 10.00FAILURE (-60): an error at the payment method
…63, from 10.00DENIED (-63): the payment processor rejects it (for Klarna: the credit check fails)
…85, from 10.00VERIFY (85): held for review; Order:Approve or Order:Decline settles it
Refund …03, from 10.00403 with an empty body: an AT token without refund rights

Limits and checks, with Pay.nl's errors ({type, code, title, detail, violations}; the refund API's {type, title, detail, violations[{propertyPath, message, code}]}): an order of 0.01 to 50,000 (PAY-2002 / PAY-2003); iDEAL, Bancontact and Pay by Bank in EUR only (PAY-2123); Klarna needs the email and invoice address (PAY-2119); capture and void only on an authorisation (95/97), abort only while open, approve/decline only in VERIFY; a refund only once paid, up to what was captured (PAY-2125), and the same refund twice within 15 seconds is "Refund too fast" (PAY-3000). Orders expire after 15 minutes unless expire says otherwise; authorisations after 7 days.

Real vs modeled. Real (developer.pay.nl): the paths and methods, Order.Output and its ids' shapes, the status codes, the sandbox's statuses, the exchange body and signature, the refund response, the error bodies and codes, Klarna as an authorisation. Modeled: the lower-case status names beyond new/pending/captured, the method screens (Pay.nl's test mode shows only its sandbox), which errors answer 400 or 422, the 15-minute expiry, one payment attempt per order (a declined card ends the order), Pay by Bank's 5 seconds at 98, the refund rate limit's window, the cards, the lab rules. Refunds leave the order's status at 100 (Pay.nl's −81/−82 are transaction statuses).

Pay.nl's errors the lab answers

PAY-1400 Bad request
PAY-1404 The requested resource is not found
PAY-1503 The server is unable to handle your request, try again later.
PAY-2001 The requested order could not be found.
PAY-2002 The order amount is too low.
PAY-2003 The order amount is too high.
PAY-2004 The order cannot be approved.
PAY-2005 The order cannot be declined.
PAY-2006 Order cannot be captured, the amount is invalid.
PAY-2007 The order is already captured.
PAY-2008 The order cannot be captured.
PAY-2009 The order cannot be voided.
PAY-2010 The order cannot be aborted.
PAY-2012 The requested order has already ended.
PAY-2101 The requested payment method is not enabled.
PAY-2104 The requested payment method is invalid.
PAY-2119 There is missing required data in the provided input.
PAY-2123 The requested payment method is not valid for this order.
PAY-2124 Card number invalid or not found.
PAY-2125 The payment cannot be refunded.
PAY-2130 The capture amount is too low.
PAY-2131 The capture amount is too high.

Orchid (Gate API)

Orchid's Gate API on the real host's paths, under /orchid: a client of https://api.orchid.works reaches the lab at https://<lab>/orchid and changes nothing else. Every request is JSON with a general object; general.project_id names the project (each a merchant of its own). Credentials are ignored: any project id is a project, and general.signature must be present (3262 when empty) but is never checked.

Callbacks go to general.merchant_callback_url, signed the Gate way (every parameter flattened, natural sort, HMAC-SHA512, Base64, top-level signature) with the published simulation secret bistromathic-lab-simulated-flow, which a connection in simulated flow uses as its project secret key. The same signs status answers.

The clock

Orchid's test cards

Decided by the card number alone (any holder, CVV, future expiry). Encrypted cards are recognised by the BIN and last four BabelPay sends beside them.

CardBrandPurchase
4000 0000 0000 0077VISAsuccess, no 3-D Secure
4111 1111 1111 1111VISAdecline, no 3-D Secure (10000 General decline)
4000 0000 0000 0119VISAsuccess, no 3-D Secure (Orchid's COF registration card; COF debits are not simulated)
4000 0000 0000 0135VISAsuccess, no 3-D Secure (Orchid's COF registration card; COF debits are not simulated)
4539 1214 1116 8251VISAdecline after a delay at the issuer: answers after 10 s with 10602 Time-out
4314 2200 0000 0056VISAsuccess after the challenge (the ACS page asks for the code)
4477 0000 0000 0006VISAsuccess, 3-D Secure frictionless (device fingerprint, no challenge)
5413 3300 0000 0019MASTERCARDsuccess after the challenge (the ACS page asks for the code)
5544 3300 0000 0045MASTERCARDdecline after the challenge, whatever is answered (10114 Declined by 3DS Check)
5252 0000 0000 0004MASTERCARDsuccess, 3-D Secure frictionless (device fingerprint, no challenge)
CardBrandPayout
4242 4242 4242 4242VISApayout success
4314 2200 0000 0072VISApayout decline (10000 General decline)
5413 3300 0000 0019MASTERCARDpayout success
5413 3300 0000 0035MASTERCARDpayout decline (10000 General decline)

Lab rules (other cards: last two digits of the amount in minor units)

AmountEffect
…14decline: 101021 Incorrect PAN
…34refused by RCS at creation: 1434 Duplicate operation
…41refused by RCS at creation: 1401 PAN is Blacklisted
…54decline: 10106 Card expired
…59decline: 10402 Suspicious operation
…79decline: 10103 Incorrect PIN or CVV
…883-D Secure frictionless (device fingerprint), then success
…91decline: 10202 Issuer inoperative
…96payment status error: 10505 PSP system is inoperative
…97decline after 10 s: 10602 Time-out
…98challenge, then authentication fails whatever is answered: 104
…99challenge, then success
…02decline: 10000 General decline
…05decline: 10105 Insufficient funds on card

A refund amount ending in …28 is declined 3028 (merchant balance). At the issuer's page, Confirm authenticates, "Fail authentication" declines 104, Cancel declines 301.

Real vs modeled. Real (developers.orchid.works, gate-spec.json): the paths, the general object, the ack, the error envelope and info codes, the payment and operation statuses, the callback and status bodies, the signature, the 3-D Secure callbacks (threeds2.iframe with 3DSMethodData/threeDSMethodData, threeds2.redirect with creq/threeDSSessionData, acs), the 30-minute ACS window and 108, the test cards, the refund rules, a payout on /card/payout. From BabelPay's own production record: the Apple Pay decrypted-token form, the 702 callback with errors for a request Gate cannot process (e.g. merchant-side authentication data without xid, which Gate's schema requires), the de-finalizing 3ds_check_iframe. Modeled: the timings, which code a test card declines with where Orchid names none (10000, 10114, 10602), 104 / 301 at the ACS, refunds after the business day (refunded, never same-day reversed), the lab rules, the ACS pages. Not simulated: the edge's HTML 400/429 under bursts, credential-on-file, tokens, Google Pay's encrypted etoken, AVS and clarification.

Nuvei

Nuvei's (ex-SafeCharge) REST API 1.0, on the real hosts' own paths under /nuvei: a client of https://ppp-test.nuvei.com/ppp/api/v1 (integration) or https://secure.safecharge.com/ppp/api/v1 (production) is pointed at <lab>/nuvei/ppp/api/v1 instead. Cards server to server with 3-D Secure 2, iDEAL (apmgw_iDeal) as a redirect APM, refunds, voids, settles and card payouts. Every merchantId is a merchant of its own.

Credentials are ignored: every request must carry merchantId, merchantSiteId, a 14-digit timeStamp and a checksum (SHA-256 of the documented fields and the merchant secret key), but no request is refused for the checksum's value. The DMN's advanceResponseChecksum — SHA-256 of the secret key, totalAmount, currency, responseTimeStamp, PPP_TransactionID, Status and productId — is signed with the published simulation secret bistromathic-lab-simulated-flow, which a connection's "Simulated flow" takes as its merchant secret key.

DMNs are POSTed form-encoded to urlDetails.notificationUrl (Nuvei takes HTTP 200, retried once after five seconds here): every final card payment, refund, void, settle and payout; an APM payment's PENDING at once and its end later. Duplicate clientRequestIds are refused (1089).

Test cards (Nuvei's own)

Any CVV and future expiry. For a 3DS card the cardholder name picks the flow: FL-BRW1 (FL-…) frictionless, CL-BRW1 (CL-…) a challenge, ERR-BRW1 (ERR-…) the Directory Server's error (filter 1277); any other name is frictionless. Any card number not listed is a 3DS card that approves.

CardBrand3DSIssuer's answer
4761344136141390VISA debit (SG)not enrolled in 3DSApproved
4244951901005043VISA debit (GB)not enrolled in 3DSApproved
4159129252458086VISA credit (SV)not enrolled in 3DSApproved
4001888687412469VISA credit (DE)not enrolled in 3DSApproved
4444493318246892VISA credit (MX)not enrolled in 3DSApproved
4000287447386587VISA debit (US)not enrolled in 3DSApproved
4263704637473241VISA debit (CA)not enrolled in 3DSApproved
4815163523263534VISA debit (MX)not enrolled in 3DSApproved
5221744250525131MASTERCARD credit (MX)not enrolled in 3DSApproved
5550343875851690MASTERCARD credit (ZA)not enrolled in 3DSApproved
5333395610225642MASTERCARD credit (HN)not enrolled in 3DSApproved
5203821142568313MASTERCARD credit (CN)not enrolled in 3DSApproved
5256787172322309MASTERCARD debit (MX)not enrolled in 3DSApproved
5413037340736315MASTERCARD credit (DK)not enrolled in 3DSApproved
375510513169537AMEX credit (IL)not enrolled in 3DSApproved
374245171846495AMEX credit (GB)not enrolled in 3DSApproved
6011405433455453DISCOVER credit (EC)not enrolled in 3DSApproved
3612569856088370DINERS credit (DE)not enrolled in 3DSApproved
6233052703225398UNIONPAY credit (JP)not enrolled in 3DSApproved
4107652651950141VISA debit (CZ)not enrolled in 3DSDecline
4083595312691240VISA debit (CZ)not enrolled in 3DSSoft Decline - Authentication is Advised
4916562347530945VISA credit (BR)not enrolled in 3DSExternal Error in Processing
5333438937354537MASTERCARD debit (US)not enrolled in 3DSAcquirer Validation
4485076191294772VISA credit (US)not enrolled in 3DSLost/Stolen
4344141510671174VISA debit (IN)not enrolled in 3DSDo Not Honor
5525873084077200MASTERCARD credit (CN)not enrolled in 3DSDo Not Honor
4000173946194872VISA debit (IN)not enrolled in 3DSInsufficient Funds
5221422486574534MASTERCARD credit (ZA)not enrolled in 3DSInsufficient Funds
4000187443195604VISA debit (IN)not enrolled in 3DSExceeds Withdrawal Limit
4000195795445766VISA debit (IN)not enrolled in 3DSExceeds Withdrawal Frequency
5168723718989159MASTERCARD debit (CZ)not enrolled in 3DSInvalid Transaction
4107647509690329VISA debit (CZ)not enrolled in 3DSFormat Error
5588341543306531MASTERCARD credit (VE)not enrolled in 3DSIssuer or Switch Inoperative
4000237713987816VISA debit (US)not enrolled in 3DSTimeout/Retry
4000248973221309VISA credit (US)not enrolled in 3DSExpired Card
4603171563067000VISA debit (CZ)not enrolled in 3DSTransaction Not Permitted To Cardholder
4917013255931104VISA debit (CZ)not enrolled in 3DSTransaction Not Permitted on Terminal
4645798619106711VISA debit (AU)not enrolled in 3DSRestricted Card
5333582039422930MASTERCARD debit (US)not enrolled in 3DSInvalid CVV
4521107938796906VISA credit (TT)not enrolled in 3DSInvalid CVV2
4105876029788196VISA debit (ZA)not enrolled in 3DSClosed Account
6011206953470801DISCOVER credit (US)not enrolled in 3DSDo not honor
4000023164892261VISA credit (GB)3DS (the cardholder name picks the flow)Approved
4005241818985068VISA credit (IN)3DS (the cardholder name picks the flow)Approved
4037073766091520VISA credit (MX)3DS (the cardholder name picks the flow)Approved
4021937195658109VISA credit (GB)3DS (the cardholder name picks the flow)Soft Decline - Authentication is Advised
4742101426751112VISA debit (PL)3DS (the cardholder name picks the flow)Approved
4008370896662369VISA debit (GB)3DS (the cardholder name picks the flow)Decline
4000164166749263VISA debit (IN)3DS (the cardholder name picks the flow)Do not honor
4871040116543210VISA debit (BE)3DS (the cardholder name picks the flow)Stolen card
5408458947067358MASTERCARD credit (MX)3DS (the cardholder name picks the flow)Approved
5333418445863914MASTERCARD credit (MX)3DS (the cardholder name picks the flow)Decline
5359438180895896MASTERCARD credit (MX)3DS (the cardholder name picks the flow)Declined by the bank
5353029602254618MASTERCARD debit (DE)3DS (the cardholder name picks the flow)Approved
5127880173456783MASTERCARD debit (BE)3DS (the cardholder name picks the flow)Approved
5109486948867999MASTERCARD debit (US)3DS (the cardholder name picks the flow)Soft Decline - Authentication is Advised
5333463046218753MASTERCARD debit (US)3DS (the cardholder name picks the flow)Do not honor
4000027891380961VISA credit (GB)3DS (the cardholder name picks the flow)Approved
4000020951595032VISA credit (GB)3DS (the cardholder name picks the flow)Approved
2221008123677736MASTERCARD credit (US)3DS (the cardholder name picks the flow)Approved
5333302221254276MASTERCARD credit (US)3DS (the cardholder name picks the flow)Approved
5545060700001113MASTERCARD credit (US)3DS, frictionless at 83.10Approved
4567491000001113VISA credit (US)3DS, frictionless at 83.10Approved
5545060700002228MASTERCARD credit (US)3DS, challenge at 115.20Approved
4567491000002228VISA credit (US)3DS, challenge at 115.20Approved

Lab rules

Amount (last two digits in minor units)Effect
…55, from 10.00A payout stays PENDING for 10 s, then approves (a DMN tells)
…77, from 10.00A refund of this amount is declined by the acquirer (gwErrorReason Decline)
…91, from 10.00The DMN arrives 30 s late: the response, the payer's return or a status read tell the outcome first
…92, from 10.00No DMN at all: only the response and a status read tell the outcome

Gateway filter errors (gwErrorCode -1100)

gwExtendedErrorCodeMeaning
1001Invalid Expiration Date
1002Expiration Date Too Old
1106Credit Amount Exceed Total Charges
1112Sale/Settle was already credited
1126Credit amount exceeds ceiling
11553D Related transaction is missing or incorrect
1167Transaction already settled
1168Transaction already voided
1176Cannot void an auth transaction
1177Cannot void a credit transaction
1185Related transaction must be Auth3D, APPROVED, same 3DS version and same merchant
1186Related transaction must be 3D authenticated
1206Settle amount exceeds total charges
1210NameOnCard is missing
1272Card number must be equal for all transaction in a specific 3DS flow
1273Card number, Currency and Amount must be equal in a specific 3DS flow
1274Requested Auth3D 2.0 but card number is not supported with 3DS 2.0
1277DS responded with an error, please verify request and try again
1278Authentication is expired, new InitAuth3d is required
12793D related transaction was already used for Sale/Auth
1281Authentication has not completed

Real or modeled

MiFinity

MiFinity's eWallet API on its own paths, under /mifinity: a MiFinity client swaps https://demo.mifinity.com for this host's /mifinity. The API is real, built from MiFinity's public docs (mifinity.readme.io and its llms.txt). Credentials are ignored: any key header is taken and names the merchant (its sessions, payouts, MiFinity users and balances); x-api-version and X-MiFinity-Signature are not checked. The cashier is redrawn from the screenshots in MiFinity's docs; the MiFinity app on the phone beside it is the lab's (the docs show none). Errors whose codes MiFinity does not publish (validation, limits, an unknown payee, insufficient funds) are the lab's.

Deposit limits (MiFinity's): EUR 10–2,500, GBP 10–2,500, USD 10–2,500, AUD 10–4,000, CAD 10–3,750, CZK 250–65,000, NZD 10–4,000, PLN 50–10,500, SEK 100–25,000, CHF 10–2,500, DKK 75–18,500, ZAR 175–42,500, JPY 1500–325,000, NOK 100–23,750, RUB 850–200,000, CNY 10–2,500, INR 875–200,000; other currencies convert to EUR. PayAnyBank's limits are MiFinity's too (EUR 10–10,000). The merchant's account opens with 100,000.00 in each eWallet currency; payouts carry the lab's 1 % fee. The demo's card verification code is 1234.

MiFinity users (the lab's; any password, wrong fails)

EmailAccountsEffect
player@mifinity.test5001000001213949, 5001000001213950, 5001000001213951an existing, verified user: EUR 5,000, USD 2,500 and GBP 2,000 in the eWallet, a verified Visa •••• 1111
lowbalance@mifinity.test5001000001214001an existing user with EUR 5.00: the balance cannot pay, a card can
unverified@mifinity.test5001000001214002an existing user whose card •••• 4242 is not verified: nothing can be paid until it is (code 1234)
risk@mifinity.test5001000001214003an existing user whose deposits MiFinity's risk engine refuses (REFUSED_BY_RISK)
any other emaila new accountcreates the account (accept the Terms), pays by card, then sets a password

Lab rules (amount, from 10.00, last two digits)

AmountEffect
deposit …13MiFinity fails the deposit (FAILED, TRANSACTION_FAILED) once the payer pays
deposit …14MiFinity's risk engine refuses the deposit (FAILED, REFUSED_BY_RISK)
deposit …15the processor takes 30 seconds: PENDING, then SUCCESSFUL (the callback comes after the payer is back)
deposit …16PENDING for 10 seconds, then FAILED
payout …13acct2acct: declined (400 TRANSACTION_FAILED); PayAnyBank: Rejected (6) after it is submitted

TrueLayer

TrueLayer's Payments API v3 on its own paths, under /truelayer: a TrueLayer client swaps each TrueLayer host (auth.truelayer-sandbox.com, api.truelayer-sandbox.com, payment.truelayer-sandbox.com, webhooks.truelayer-sandbox.com) for this host's /truelayer. Credentials are ignored: any client_id and client_secret get a token at POST /truelayer/connect/token, and the token names the client (its payments, its GBP and EUR merchant accounts with 1 000 000.00 each, its webhook URI). Tl-Signature on requests is not checked; Idempotency-Key is required and honoured (the same key with another body is 422 Idempotency-Key Reuse). The API is real: built from TrueLayer's public OpenAPI (payments-api-v3.json), signing spec and docs. The real banks' provider ids and limits, the lab's amount rules and the timings are the lab's.

Webhooks go to the webhook URI with X-Tl-Webhook-Timestamp and Tl-Signature (ES512, tl_headers: X-Tl-Webhook-Timestamp, over POST and the URI's path) by the lab's published key, whose JWKS is at /truelayer/.well-known/jwks (the jku): payment_executed, payment_settled (with payment_source.id and user_id, for closed-loop payouts), payment_failed, refund_executed, refund_failed, payout_executed, payout_failed. A payment executes 1.5 s after the bank approves and settles 2 s later; a payout or refund executes 2 s after it is authorised (a refund once its payment has settled). The resource token lasts 15 minutes.

TrueLayer's mock bank (mock-payments-*-redirect)

UsernameEffect
test_executedExecution: the payment executes (and settles)
test_authorisation_failedAuth failure: failed, authorization_failed
test_execution_rejectedExecution rejection: failed after authorisation, provider_rejected

Lab rules (at a real bank)

Deposit amountEffect
…13, from 10.00executed, then settled two minutes later
…24, from 10.00authorised, executed a minute later
…05, from 10.00the bank refuses: failed, insufficient_funds (stage authorizing)
…06, from 10.00over the payer's limit at the bank: failed, payment_limit_exceeded
…07, from 10.00authorised, then rejected at execution: failed, provider_rejected (stage authorized)
Payout amountEffect
…13, from 10.00executed, then returned by the beneficiary's bank 20 s later: failed, returned
…17, from 10.00failed, scheme_error
…41, from 10.00pending for two minutes, then executed
…05, from 10.00failed, blocked (sanctions screening)
Refund amountEffect
…05, from 10.00failed, insufficient_funds

A payout to an IBAN ending 9901 fails invalid_iban; to an account number ending 9902, invalid_scan. Payments over 5 000 000 in minor units to a merchant account are refused (TrueLayer's sandbox limit); GBP payouts over 100 000 000 (Faster Payments' £1M).

Markets, banks and the payer's limit per payment

CountryCurrencyBanks
GBGBPBarclays 25,000, HSBC UK 25,000, Lloyds 25,000, NatWest 50,000, Santander 25,000, Nationwide 10,000, Halifax 25,000, Monzo 25,000, Starling Bank 50,000, Revolut 250,000, mock-payments-gb-redirect, mock-payments-gb-redirect-unhealthy
IEEURAIB 15,000, Bank of Ireland 15,000, PTSB 10,000, Revolut 100,000, mock-payments-ie-redirect
DEEURSparkasse 5,000, Deutsche Bank 10,000, Commerzbank 10,000, ING 10,000, N26 50,000, mock-payments-de-redirect
FREURBNP Paribas 15,000, Crédit Agricole 15,000, Société Générale 15,000, LCL 10,000, BoursoBank 30,000, mock-payments-fr-redirect
ESEURBanco Santander 15,000, BBVA 15,000, CaixaBank 15,000, Banco Sabadell 10,000, mock-payments-es-redirect
ITEURIntesa Sanpaolo 15,000, UniCredit 15,000, Fineco 30,000, mock-payments-it-redirect
NLEURING 10,000, ABN AMRO 50,000, Rabobank 10,000, mock-payments-nl-redirect
LTEURSwedbank 15,000, SEB 15,000, Luminor 10,000, mock-payments-lt-redirect
PTEURMillennium bcp 10,000, Caixa Geral de Depósitos 10,000, mock-payments-pt-redirect
FIEUROP 15,000, Nordea 10,000, mock-payments-fi-redirect
ATEURErste Bank 10,000, Raiffeisen 10,000
BEEURKBC 10,000, BNP Paribas Fortis 10,000, Belfius 10,000

Jeton

Jeton's Merchant API v2 on its own paths, under /jeton: a Jeton client swaps https://merchant-api-sandbox.jeton.com for this host's /jeton. The API is real, built from Jeton's public OpenAPI 3.1 (openapi.yaml) and its sandbox guide. Credentials are ignored: any Jeton-Api-Key is taken and names the merchant (its payments, payouts, accounts in 15 currencies and USDT, events and webhook address); Jeton-Timestamp, Jeton-Nonce and Jeton-Signature are not checked. Idempotency-Key is required on the money-moving POSTs and honoured (the same request replays with Idempotent-Replayed: true; another request under the key is idempotency_key_reused). Money is a decimal string; errors are application/problem+json with Jeton's codes. FX and transfers are not simulated.

Webhooks carry the outcomes (payment.completed, .expired, .cancelled, .refunded, payout.completed, .failed), each an Event envelope, signed as Standard Webhooks (webhook-id, webhook-timestamp, webhook-signature: v1,…) with the published secret whsec_YmlzdHJvbWF0aGljLWxhYi1qZXRvbi13ZWJob29rLXNpbXVsYXRpb24=. A paid payment completes 1.5 s after the payer pays; a bank payout reaches its rail after 1 s and ends 2 s later; a crypto payout in 5 s.

The sandbox's magic payout destinations

DestinationValueOutcome
bank_account IBANDE09000000000000000001completed
bank_account IBANDE79000000000000000002failed
crypto_wallet, an EVM network0x0000000000000000000000000000000000000001completed
crypto_wallet, an EVM network0x0000000000000000000000000000000000000002failed
crypto_wallet, TRONT9yD14Nj9j7xAB4dbGeiX9h8unkKLxmGkncompleted
crypto_wallet, TRONT9yD14Nj9j7xAB4dbGeiX9h8unkKT76qbHfailed
jeton_account customer number (lab)ending 01 / 02completed / failed

Jeton accounts at the checkout (the lab's)

LoginCustomer numberEffect
alex.morgan@example.com4815162342the sandbox's test customer (4815162342): 5,000.00 in any currency
low.balance@example.com734001290520.00: paying more fails the attempt (insufficient balance) and the payment stays pending
suspended@example.com7340012913the login is refused: the account is suspended
payout.fails@example.com7340012902customer number ends 02: pays as usual, and a payout back to it fails

Lab rules

Deposit amountEffect
…13, from 10.00Paid, then processing for a minute before it completes

Limits (the lab's): a payment from about 1 to 50,000 euros' worth, a payout up to 25,000 (amount_below_minimum, limit_exceeded); a payout beyond the account's balance is insufficient_funds. Each fiat account opens with 100,000.00. The fees and these limits are the lab's; Jeton publishes neither.

AstroPay

AstroPay's Checkout (Partners API) and its OneTouch merchant API (wallet cashouts, Direct Withdrawal) on their own paths, under /astropay: an AstroPay client swaps https://partners-api-sandbox.astropay.com and https://onetouch-api-sandbox.astropay.com for this host's /astropay. The API is real where AstroPay's public guides document it (developers.astropay.com) and modeled where they do not: AstroPay's regulated-gambling and wallet-checkout sections are private (77 of 185 pages), so the payer's AstroPay user id on a deposit (deposit_user_id, from the public closed-loop guide), the checkout's 20-minute expiry, the payer's screens and the cashout API's error codes (AstroPay documents only their descriptions) are the lab's. Credentials are ignored: the Basic client id (or the lab's Bearer token) names the partner, Merchant-Gateway-Api-Key or Merchants-Backend-Api-Key the merchant; no Signature is checked.

AstroPay users at the checkout

PhoneUser idEffect
+5511911223344Vx0H4pdAtPCz5,000.00 USD. Pays; cashouts back are APPROVED at once
+5491123456789Lm7Qa2Zx9KpR5,000.00 USD (Argentina). Pays
+525512345678Sh4Mx8Tq2LwN5,000.00 USD (Mexico). Pays
+5511900000005Bl0Wb5Lc3VnE20.00 USD: paying more is refused for the balance; the payment stays PENDING
+5511900000009Cd9Bk1Ld7XqSThe account is blocked: the login is refused
any othermade up from the phoneA fresh account holding 5,000.00 USD

Direct Withdrawal countries

CountryCurrencyMethods
ARARSTR Bank transfer (CBU / CVU)
BRBRLPIX PIX
CLCLPTR Bank transfer
COCOPTR Bank transfer, BREB Bre-B
ECUSDTY Bank transfer
ININRTR Bank transfer
MXMXNTR SPEI (CLABE)
PEPENTR Bank transfer, YP Yape

Lab rules (from 10.00, the amount's last two digits)

AmountEffect
deposit …13Paid, then PENDING for a minute before COMPLETED
deposit …33COMPLETED, but no callback is sent: only the status endpoint tells
deposit …05REJECTED once the payer confirms (AstroPay's risk check refuses it)
withdrawal …13PENDING for two minutes, then APPROVED
withdrawal …02CANCELLED, INVALID_BANK_ACCOUNT (Brazil: INVALID_PIX_KEY)
withdrawal …03CANCELLED, ACCOUNT_BLOCKED (Argentina: INVALID_ALIAS; elsewhere GENERIC_ERROR)
withdrawal …04CANCELLED, CPF_MISMATCH (Brazil; elsewhere TRX_COULD_NOT_BE_PROCESSED)
refund …07412 PAYMENT_PROCESSING_ERROR

Brazil's betting ban (MP 1.394/2026) stops regulated gambling payments there; the lab still takes BRL and pays out by PIX.

CryptoProcessing (CoinsPaid)

CryptoProcessing by CoinsPaid's merchant API on its own paths, under /coinspaid: a client swaps https://app.sandbox.cryptoprocessing.com for this host's /coinspaid. The API is real, built from the public docs (docs.cryptoprocessing.com: an OpenAPI 3.1 block on every endpoint page, and the Postman collection). Credentials are ignored: any X-Processing-Key is taken and names the merchant; X-Processing-Signature must be there but is not checked. Validation messages, status codes and callbacks are CryptoProcessing's. Callbacks carry X-Processing-Key and X-Processing-Signature, the hex HMAC-SHA512 of the body under the published secret bistromathic-lab-coinspaid-simulation-secret.

A deposit is seen 2 s after the wallet sends it (not_confirmed) and confirmed after 1.5 s per confirmation the currency needs, at most 9 s. One below the minimum deposit is cancelled; the wallet can also double-spend (cancelled) or never confirm. A withdrawal confirms after 3 s; to 0x000000000000000000000000000000000000dEaD it is cancelled as forbidden.

Currencies (the lab's rates in EUR)

API codeAssetConfirmationsMin depositMin withdrawalEUR
BTCBTC on Bitcoin10.00010.000285000
ETHETH on Ethereum20.0020.0023000
ETHAETH on Arbitrum120.0020.0023000
ETHBASEETH on Base120.0020.0023000
USDCUSDC on Ethereum2550.87
USDCAUSDC on Arbitrum12550.87
USDCBASEUSDC on Base12550.87
USDCBUSDC on BNB Smart Chain10550.87
USDCPUSDC on Polygon10550.87
USDCSUSDC on Solana1550.87
EURCEEURC on Ethereum2551
EURCBASEEURC on Base12551
EURCSEURC on Solana1551
USDGUSDG on Ethereum2550.87
USDGSUSDG on Solana1550.87
LTCLTC on Litecoin20.010.0180
BCHBCH on Bitcoin Cash20.0010.001400
DOGEDOGE on Dogecoin310.010.15
TRXTRX on Tron310100.25
SOLSOL on Solana10.050.05140
XRPXRP on XRP Ledger140.0012
ADAADA on Cardano15220.6
BNB-BSCBNB on BNB Smart Chain100.010.01600
POLPOL on Polygon1010100.4

No USDT: CryptoProcessing no longer lists it. Fiat for conversion: EUR, USD, GBP, CHF, SEK, NOK, DKK, PLN, CZK, HUF, CAD, AUD, NZD, JPY, TRY, BRL, MXN, ZAR, INR, ARS, AZN, BDT, CLP, CNY, IDR, KRW, MDL, MYR, NGN, PEN, PHP, THB, VND. Each merchant starts with 100000 EUR, 50000 USD, 2 BTC, 30 ETH, 100000 USDC, 50000 USDCS, 50000 EURCE, 500 LTC, 200000 TRX, 300 SOL, 20000 XRP.

Lab rules for withdrawals (the amount's last two decimals)

AmountEffect
…02cancelled: "Not enough money on balance"
…03pending (held for approval), then declined: "Declined by user ID:777"
…04pending (held for approval), then approved and confirmed
…09stays not_confirmed: never sent to the blockchain

Shift4

Shift4's Gateway API (ex-SecurionPay), on the real host's own paths under /shift4: a client of https://api.shift4.com is pointed at <lab>/shift4 instead. Cards server to server with 3-D Secure 2, charges captured or authorized, captures, refunds, credits (funds to a card) and events. Every secret key is a merchant of its own. Only test mode is played.

Credentials are ignored: Shift4 authenticates with HTTP Basic, the secret key as the username and an empty password; the lab never refuses a key, it only tells merchants apart by it. Bodies may be form-encoded with bracketed keys, as Shift4 documents, or JSON, as its SDKs send. Webhooks are not signed, as Shift4's are not: Shift4 tells the receiver to take only the event's id and read the event back with GET /events/{id}, which the lab serves. Webhook URLs are set in Shift4's dashboard; the lab's stand-in is POST /shift4-lab/dashboard/webhooks with {"url": …} and the merchant's key.

Events (CHARGE_SUCCEEDED, CHARGE_FAILED, CHARGE_CAPTURED, CHARGE_REFUNDED, REFUND_UPDATED, CREDIT_SUCCEEDED, CREDIT_FAILED) are POSTed as JSON to the dashboard's webhook URLs; Shift4 takes HTTP 200. A POST with an Idempotency-Key answers its first answer again for 24 hours (Idempotent-Replayed: true). Charges above 50,000.00 (modeled) fail with limit_exceeded.

Test cards (Shift4's own)

Any future expiry and any 3 or 4 digit CVC. A card number not listed is approved and not enrolled in 3DS. A card arriving as vault ciphertext is recognised by BabelPay's BIN and last-four headers; its CVC cannot be read, so the CVC-123 card approves then.

CardBrandOutcome
4012000100000007VisaSuccessful charge
4242424242424242VisaSuccessful charge
4012888888881881VisaSuccessful charge
4000056655665556Visa (debit card)Successful charge (debit)
4426034383518716VisaSuccessful charge (GB)
4909069612259316VisaSuccessful charge (ES)
5555555555554444MasterCardSuccessful charge
5200828282828210MasterCard (debit card)Successful charge (debit)
5105105105105100MasterCardSuccessful charge
2222000000000008MasterCardSuccessful charge
5294008684328401MasterCardSuccessful charge (GB)
5125646148694267MasterCardSuccessful charge (ES)
6759649826438453MasterCard (debit card)Successful charge (Maestro)
378282246310005American ExpressSuccessful charge
371449635398431American ExpressSuccessful charge
6011111111111117DiscoverSuccessful charge
6011000990139424DiscoverSuccessful charge
30569309025904Diners ClubSuccessful charge
38520000023237Diners ClubSuccessful charge
3530111333300000JCBSuccessful charge
3566002020360505JCBSuccessful charge
4024007102349866Visainvalid_number
4532873294814636Visainvalid_expiry_month
4532582477951947Visainvalid_expiry_year
4024007189368227Visainvalid_cvc
4916487051294548Visaexpired_card
4024007134364842VisaDeclined: incorrect_cvc
4929225021529113VisaDeclined: incorrect_zip
4242000000000323VisaDeclined: incorrect_address
4024007118468684VisaDeclined: insufficient_funds
4024007114621187VisaDeclined: lost_or_stolen
4024007155502486VisaDeclined: suspected_fraud
4916018475814056VisaDeclined: card_declined
4916449457024978VisaDeclined: authentication_required
4024007114166316VisaDeclined: processing_error
4242000000000083VisaSuccessful only with CVC 123, else incorrect_cvc
4242000000000018VisaSuccessful; Shift4 disputes it (chargeback)
4242000000000026VisaSuccessful; Shift4 disputes it (retrieval request)
4242000000000042VisaSuccessful, fraud check safe
4242000000000059VisaSuccessful, fraud check suspicious
4242000000000067VisaSuccessful, fraud check fraudulent
4242000000000208VisaSuccessful, fraud check fraudulent (with a fraud warning)
4242000000000075VisaSuccessful, fraud check unknown
4012001800000016VisaEnrolled for 3D Secure 2
5428000221101226MasterCardEnrolled for 3D Secure 2
370000000000002American ExpressEnrolled for 3D Secure 2
36000000000008Diners ClubEnrolled for 3D Secure 2
6011000990191003DiscoverEnrolled for 3D Secure 2
3528000111101116JCBEnrolled for 3D Secure 2
4242000000000513VisaANI check: cardholder name "Ani check" full_match
4242000000000315VisaAVS check: zip 00000 full_match, 00001 no_match, 00002 partial_match, 00003 not_provided
4111100000000006VisaCVV check: 111 match, 222 no_match, 333 not_verified, 444 not_provided, 555 not_supported
4242000000011114VisaA credit to it is a fast credit
4242000000011148VisaAFT funding supported

Lab rules

Amount (last two digits in minor units)OnEffect
…51, from 10.00creditThe credit fails: card_declined
…77, from 10.00refundThe refund fails (status failed)
…91, from 10.00chargeThe charge's webhooks arrive 30 s late
…92, from 10.00chargeThe charge sends no webhooks

Real or modeled

PXP

PXP's Unity API (pxp.io, ex-PXP Financial: regulated gaming in 23 US states, the EU and Canada), on the real hosts' own paths under /pxp: a client of https://api-services.test.pxp.io is pointed at <lab>/pxp instead. Cards server to server with PXP's integrated 3-D Secure 2, authorisations, purchases, captures, refunds, voids and card payouts, on one transaction endpoint. Every client id is a merchant group of its own. Only the sandbox is played.

Credentials are ignored: PXP signs every request (Authorization: PXP-UST1 {tokenId}:{timestamp}:{HMAC}, an upper-case hex HMAC-SHA256 of timestamp, X-Request-Id, path and body under the token value, with X-Client-Id); the lab never checks the signature and tells merchant groups apart by the client id. Webhooks are signed as PXP signs them: X-Signature is the Base64 HMAC-SHA256 of X-Request-Id + X-Signature-Timestamp + the body, under the published simulation key bistromathic-lab-simulated-flow. Webhook URLs are set in PXP's Unity Portal; the lab's stand-in is POST /pxp-lab/portal/webhooks with {"url": …} and the merchant group's X-Client-Id.

Webhooks (eventCategory transaction: Authorised, Captured, Cancelled, Refunded, Refused TXN_RFU, Error TXN_ERR; authentication: the challenge completed) are POSTed as JSON to the portal's URLs; PXP retries 10 times within 10 seconds. A transaction above 50,000.00 is refused (CRD110), a payout above 25,000.00 too (CRD121), both modeled. An amount with more decimals than its currency is refused (CAR0010).

Test cards (PXP's own)

Any future expiry and any valid CVC. A card number not listed authenticates frictionless and is approved. A 3-D Secure test card charged without a successful authentication is soft declined (CRD180, SCA required) unless an exemption PXP grants applies. A card arriving as vault ciphertext is recognised by BabelPay's BIN and last-four headers.

CardSchemeOutcome
4111111111111111VisaApproved, no challenge
5555555555554444MasterCardApproved, no challenge
378282246310005American ExpressApproved, no challenge
4000000000000002VisaChallenge required, then approved
4000000000000077VisaRefused: not sufficient funds (CRD116)
4000000000000051VisaRefused: do not honour (CRD100)
4000000000000069VisaRefused: expired card (CRD101)
4000000000000101VisaRefused: CVC check failed (CRD100, CVC not matched)
4570020000000851Visa3DS frictionless, authenticated (Y); SCA mandated; no fingerprinting
4570020000000067Visa3DS frictionless, attempted (A)
4570010000000076Visa3DS failed (N)
4570020000000083Visa3DS failed, could not be performed (U)
4570010000000092Visa3DS rejected (R)
4570020000000018Visa3DS challenge (C)
4570020000000109VisaTRA exemption at 135.50
5266001055000588MasterCard3DS frictionless, authenticated (Y); device fingerprinting
5266001055000075MasterCard3DS frictionless, attempted (A)
5266001055000083MasterCard3DS failed (N)
5266001055000091MasterCard3DS failed, could not be performed (U)
5266001056000108MasterCard3DS rejected (R)
5266001055000026MasterCard3DS challenge (C)
5266001055000117MasterCardLVP exemption at 13.50; TRA at 13.50 or 135.50
375987130000321American Express3DS frictionless, authenticated (Y)
375987130000073American Express3DS frictionless, attempted (A)
375987140000089American Express3DS failed (N)
375987130000107American Express3DS failed, could not be performed (U)
375987130000099American Express3DS rejected (R)
375987140000022American Express3DS challenge (C)
384444444400077Diners3DS frictionless, authenticated (Y)
3833333333000075Diners3DS frictionless, attempted (A)
3844444444000081Diners3DS failed (N)
3833333333000091Diners3DS failed, could not be performed (U)
3844444444000107Diners3DS rejected (R)
3844444444000024Diners3DS challenge (C)
3844444444000073DinersSCA not mandated (PSD2)
6011060000000560Discover3DS frictionless, authenticated (Y)
6011030000000076Discover3DS frictionless, attempted (A)
6011060000000081Discover3DS failed (N)
6011030000000092Discover3DS failed, could not be performed (U)
6011060000000107Discover3DS rejected (R)
6011060000000024Discover3DS challenge (C)

Lab rules

Amount (last two digits in minor units)OnEffect
…23, from 10.00payoutThe payout is refused: exceeds withdrawal frequency limit (CRD123)
…51, from 10.00payoutThe payout is refused: do not honour (CRD100)
…77, from 10.00refundThe refund is refused (CRD100)
…83, from 10.00paymentError: timeout / malfunction at the issuer (CRD183)
…91, from 10.00paymentThe webhooks arrive 30 s late
…92, from 10.00paymentNo webhooks
…02, from 10.00paymentError: suspected fraud, PXP's risk refusal (CRD102)

Real or modeled

Volt

Volt's current ("Global") APIs on their own paths, under /volt: a Volt client swaps gateway.sandbox.volt.io (OAuth and the Global Payments API) and accounts.sandbox.volt.io (the Accounts API v2) for this host's /volt, and the hosted checkout (checkout.volt.io) is /volt/checkout. Credentials are ignored: any client id, secret, username and password get a token at POST /volt/oauth (the password grant, JSON or form), and the token names the merchant (its EUR, GBP and AUD Volt accounts with 1 000 000.00 each, its payments and beneficiaries). X-JWS-Signature is required on outgoing transactions and not checked; Idempotency-Key and X-Volt-Api-Version: 1 are required, and a key used before is 409 IDEMPOTENCY_CONFLICT, as Volt does. The API is real: built from Volt's public OpenAPI files (the Global Payments API on docs.volt.io, the Accounts API in github.com/volt-io/volt-io-accounts) and docs. The real banks' ids and limits, the lab's amount rules, the timings and the euro countries offered are the lab's. The legacy Gateway and Accounts APIs are not simulated.

Notifications go to the request's communication.notifications.url, signed X-Volt-Signed (hex HMAC-SHA256 of body|X-Volt-Timed|version) under the published secret bistromathic-lab-volt-notification-secret: gateway notifications (User-Agent: Volt/1.0; PENDING, COMPLETED, FAILED with a detailedStatus), Volt Connect's payment_received and payment_not_received, and the Accounts API's outgoing_transaction_completed, outgoing_transaction_rejected and account_holder_verification_result_completed (Volt/2.0, X-Volt-Type). A payment completes 1.5 s after the bank approves and is RECEIVED into the Volt account 2 s later (an incoming GATEWAY_PAYMENT transaction; the payer is now a beneficiary); an outgoing transaction is PROCESSING after 1 s and COMPLETED 2 s later. A payment nobody finishes is ABANDONED_BY_USER after its validityPeriod.

Lab rules (any bank)

Deposit amountEffect
…13, from 10.00DELAYED_AT_BANK for two minutes, then COMPLETED
…41, from 10.00FAILED (a technical failure in the banking network)
…05, from 10.00REFUSED_BY_BANK (insufficient funds at the bank)
…06, from 10.00ERROR_AT_BANK
…07, from 10.00REFUSED_BY_RISK (Circuit Breaker, before the bank)
exactly 2050COMPLETED, then NOT_RECEIVED (Volt's sandbox value)
Payout amountEffect
exactly 1570REJECTED, ACCOUNT_INSUFFICIENT_FUNDS (the legacy sandbox's PAYOUT_FAILED)
exactly 2570REJECTED, TRANSACTION_REJECTED_BY_BANKING_PROVIDER (the legacy sandbox's PAYOUT_REJECTED_BY_PROVIDER)
…17, from 10.00REJECTED, PROCESSING_ERROR
…41, from 10.00PROCESSING for two minutes, then COMPLETED
to an account that never paid inREJECTED, TRANSACTION_BENEFICIARY_CHECK_REJECTED
under another nameaccount holder verification CLOSE_MATCH or NO_MATCH: waits for approve or reject (10 min, then TRANSACTION_APPROVAL_TIMEOUT)
Refund amountEffect
exactly 1177REJECTED, TRANSACTION_REJECTED_BY_BANKING_PROVIDER (the legacy sandbox's REFUND_FAILED)
…05, from 10.00REJECTED, ACCOUNT_INSUFFICIENT_FUNDS
X-Volt-Testing-ScenarioEffect
PAYOUT_REJECTEDREJECTED, TRANSACTION_REJECTED_BY_BANKING_PROVIDER
ACCOUNT_HOLDER_VERIFICATION_CLOSE_MATCHaccount holder verification CLOSE_MATCH: waits for approve or reject
ACCOUNT_HOLDER_VERIFICATION_NO_MATCHaccount holder verification NO_MATCH: waits for approve or reject
ACCOUNT_HOLDER_VERIFICATION_ERRORaccount holder verification ERROR: waits for approve or reject

Markets, banks and the payer's limit per payment

CountryCurrencyPayment systemBanks
GBGBPOPEN_BANKING_UKBarclays 25,000, HSBC UK 25,000, Lloyds Bank 25,000, NatWest 50,000, Santander UK 25,000, Nationwide 10,000, Monzo 25,000, Revolut 250,000, Synthetic Redirect, Synthetic Embedded
DEEUROPEN_BANKING_EUSparkasse 5,000, Deutsche Bank 10,000, Commerzbank 10,000, ING 10,000, N26 50,000, Synthetic Redirect, Synthetic Embedded
ATEUROPEN_BANKING_EUErste Bank und Sparkassen 10,000, Raiffeisen 10,000, Bank Austria 10,000, Synthetic Redirect, Synthetic Embedded
NLEUROPEN_BANKING_EUING 10,000, ABN AMRO 50,000, Rabobank 10,000, bunq 25,000
FREUROPEN_BANKING_EUBNP Paribas 15,000, Crédit Agricole 15,000, Société Générale 15,000, BoursoBank 30,000
ESEUROPEN_BANKING_EUBanco Santander 15,000, BBVA 15,000, CaixaBank 15,000
ITEUROPEN_BANKING_EUIntesa Sanpaolo 15,000, UniCredit 15,000, Fineco 30,000
IEEUROPEN_BANKING_EUAIB 15,000, Bank of Ireland 15,000, PTSB 10,000
BEEUROPEN_BANKING_EUKBC 10,000, BNP Paribas Fortis 10,000, Belfius 10,000
FIEUROPEN_BANKING_EUOP 15,000, Nordea 10,000
PTEUROPEN_BANKING_EUMillennium bcp 10,000, Caixa Geral de Depósitos 10,000
LTEUROPEN_BANKING_EUSwedbank 15,000, SEB 15,000, Luminor 10,000
LVEUROPEN_BANKING_EUSwedbank 15,000, Citadele 10,000
EEEUROPEN_BANKING_EUSwedbank 15,000, LHV 15,000, SEB 15,000
SKEUROPEN_BANKING_EUSlovenská sporiteľňa 10,000, Tatra banka 10,000
AUAUDNPP_PAY_TO_AUCommBank 25,000, Westpac 25,000, ANZ 25,000, NAB 25,000

D24 (Directa24)

D24's Deposits API and Cashouts API (v3) on their own paths, under /d24: a D24 client swaps https://api-stg.directa24.com for this host's /d24. The API is real (docs.d24.com, public): the bodies, statuses, error codes and notifications, and D24's own Mock PSP scenarios by amount. Modeled: the method limits and expiry times (the merchant account's, unpublished), the notification headers (X-Date, X-Login, Authorization, as D24's security guide says notifications are signed), and the payer's screens. Credentials are ignored: X-Login names the deposits merchant, the body's login the cashouts merchant; no Authorization or Payload-Signature is checked.

Methods

CodeMethod (option)CountryTypeLimits (local, modeled)Expiry (modeled)
IXPix (pix)BRBANK_TRANSFER1–50,00012 h
SESpei (spei)MXBANK_TRANSFER10–500,0003 days
OXOXXO (oxxo)MXVOUCHER10–10,0006 days
PCPSE (pse)COBANK_DEPOSIT5,000–20,000,0001 h
EYEfecty (efecty)COVOUCHER5,000–5,000,0003 days

D24's Mock PSP: the amount's leading digits (trailing zeros ignored)

AmountEffect
deposit 1Paid: COMPLETED about 2 seconds after the payer pays
deposit 2Paid: COMPLETED about 3 minutes after the payer pays
deposit 3Paid: COMPLETED about 5 minutes after the payer pays, with no notification (poll the status)
deposit 4Refused at once: INVALID_CARD_NUMBER, no deposit
deposit 5CANCELLED about 3 minutes after the payer pays (MOCK_ERROR)
deposit 6FOR_REVIEW about 1 minute after the payer pays (terminal), then refunded automatically
deposit 41Refused at once: INSUFFICIENT_FUNDS, no deposit
deposit 51CANCELLED about 8 minutes after the payer pays (PROCESSING_ERROR)
deposit, any otherPaid: COMPLETED about 1 minute after the payer pays
refund 1COMPLETED about 2 seconds later
refund 4Refused at once: DUPLICATE_TRANSACTION
refund 5CANCELLED about 3 minutes later (MOCK_ERROR)
refund 6COMPLETED about 2 seconds later (the under-review deposit's automatic refund)
cashout 1COMPLETED about 2 seconds later
cashout 2COMPLETED about 3 minutes later
cashout 3COMPLETED about 5 minutes later, with no notification
cashout 4Refused at once: INVALID_BENEFICIARY_ACCOUNT
cashout 5CANCELLED about 3 minutes later (PROCESSING_FAILED)
cashout 41Refused at once: MERCHANT_INSUFFICIENT_BALANCE
cashout 51CANCELLED about 8 minutes later (MOCK_ERROR)
cashout document …0800REJECTED 800 ERROR_ACCOUNT_INCORRECT (lab rule)
cashout document …0812REJECTED 812 BLOCKED_FROZEN_ACCOUNT (lab rule)
cashout document …0813REJECTED 813 DOCUMENT_ACCOUNT_MISMATCH (lab rule)

Cashout bank codes

CountryCodes
BR1 BANCO DO BRASIL S.A., 33 BANCO SANTANDER BRASIL S.A., 104 CAIXA ECONOMICA FEDERAL - CEF, 237 BANCO BRADESCO S.A., 260 NU PAGAMENTOS, 341 ITAU UNIBANCO S.A., 10000 Pix Key Phone, 10001 Pix Key Email, 10002 Pix Key Document, 10003 Pix Key Random
MX2 BANAMEX, 12 BBVA MEXICO, 14 SANTANDER, 21 HSBC, 72 BANORTE, 127 AZTECA, 646 STP
CO1007 BANCOLOMBIA, 1001 BANCO DE BOGOTA, 1051 BANCO DAVIVIENDA, 1013 BBVA COLOMBIA, 1507 NEQUI, 1551 DAVIPLATA, 10003 EFECTY, 20000 BREB Key - Phone, 20001 BREB Key - Email, 20002 BREB Key - Document, 20003 BREB Key - Random

Brazil's betting ban (MP 1.394/2026) stops regulated gambling payments there; the lab still takes BRL and pays out by Pix.

Zota

Zota's (ex-Zotapay) MG Deposit API 1.0 and MG Payout API 1.1 on their own paths, under /zota: a Zota client swaps https://api.zotapay-sandbox.com for this host's /zota. The API is real, from Zota's public docs (docs.zota.com): the paths, the fields and their lengths, the {code, message} refusals (400 missing arguments, bad request, endpoint currency mismatch, timestamp too old; 404 not found; 409 order already created), the order statuses and the SHA-256 signatures. Each country's methods, the limits, the payment form's screens and the payer's account in a deposit's extraData are modeled: Zota sets them per merchant endpoint and does not publish them. Credentials are ignored: no signature is checked, and any MerchantID is the lab's one merchant.

Markets and published endpoints (deposit / payout)

CountryCurrencyEndpointsLimitsBanksQRWallets, mobile money
THTHB1050 / 2050100–200,000BBL Bangkok Bank, KBANK Kasikornbank, SCB Siam Commercial Bank, KTB Krungthai Bank, BAY Krungsri, TTB TMBThanachartPROMPTPAY PromptPay QRTRUEMONEY TrueMoney Wallet
VNVND1051 / 205150,000–300,000,000VCB Vietcombank, TCB Techcombank, BIDV BIDV, CTG VietinBank, ACB ACB, MB MB BankVIETQR VietQRMOMO MoMo, ZALOPAY ZaloPay
MYMYR1052 / 205210–50,000MBB Maybank, CIMB CIMB Bank, PBB Public Bank, RHB RHB Bank, HLB Hong Leong BankDUITNOW DuitNow QRTNG Touch 'n Go eWallet, BOOST Boost
IDIDR1053 / 205310,000–100,000,000BCA Bank Central Asia (BCA), MANDIRI Bank Mandiri, BNI BNI, BRI BRI, CIMBN CIMB NiagaQRIS QRISOVO OVO, DANA DANA, GOPAY GoPay
PHPHP1054 / 2054100–500,000BDO BDO Unibank, BPI BPI, MBTC Metrobank, UBP UnionBank, LBP LandbankQRPH QR PhGCASH GCash, MAYA Maya
ININR1055 / 2055100–200,000HDFC HDFC Bank, ICICI ICICI Bank, SBI State Bank of India, AXIS Axis Bank, KOTAK Kotak Mahindra BankUPI UPI QRPAYTM Paytm, PHONEPE PhonePe
CNCNY1056 / 2056100–50,000ICBC ICBC, CCB China Construction Bank, ABC Agricultural Bank of China, BOC Bank of China, CMB China Merchants BankUNIONPAYQR UnionPay QRALIPAY Alipay, WECHAT WeChat Pay
NGNGN1057 / 20571,000–5,000,000ACCESS Access Bank, GTB GTBank, ZENITH Zenith Bank, FBN First Bank, UBA UBA—OPAY OPay, PALMPAY PalmPay
KEKES1058 / 2058100–250,000EQUITY Equity Bank, KCB KCB Bank, COOP Co-operative Bank—MPESA M-PESA, AIRTEL Airtel Money
GHGHS1059 / 205910–50,000GCB GCB Bank, ECOBANK Ecobank Ghana, STANBIC Stanbic Bank—MTN MTN MoMo, TELECEL Telecel Cash, ATMONEY AT Money
EGEGP1060 / 206050–100,000NBE National Bank of Egypt, CIB CIB, BANQUEMISR Banque MisrINSTAPAY InstaPay QRVFCASH Vodafone Cash
MXMXN1061 / 206150–100,000BBVA BBVA México, BANORTE Banorte, SANTANDER Santander, BANAMEX BanamexCODI CoDiMERCADOPAGO Mercado Pago
BRBRL1062 / 206210–50,000ITAU Itaú, BRADESCO Bradesco, BB Banco do Brasil, NUBANK NubankPIX PixPICPAY PicPay

Lab rules (from the market's minimum, the amount's last two digits)

AmountEffect
deposit …13PROCESSING for a minute after the payer approves, then APPROVED
deposit …21UNKNOWN for two minutes after the payer approves (not final), then APPROVED
deposit …33APPROVED with no callback: only the order status (or reconciliation) tells
deposit …02DECLINED once the payer approves: insufficient funds at the bank
deposit …03FILTERED once the payer approves: declined by Zota's fraud prevention
deposit …04ERROR once the payer approves: a processor error (final)
payout …13PROCESSING for two minutes, then APPROVED
payout …02DECLINED: invalid bank account
payout …03FILTERED: declined by fraud prevention
payout …04ERROR: a processor error (final)

Zota has no refund API. Brazil's betting ban (MP 1.394/2026) stops regulated gambling payments there; the lab still takes BRL.

PPRO

PPRO's Payments API ("Payment Charges") on its own paths, under /ppro: a PPRO client swaps api.sandbox.eu.ppro.com for this host's /ppro. Credentials are ignored: any Authorization: Bearer … is taken, and Merchant-Id names the merchant. Request-Idempotency-Key answers a repeat with the first answer (Request-Idempotency: true) and a different body with 409, as PPRO does. The API is real: built from PPRO's public developer hub (developerhub.ppro.com, its OpenAPI per endpoint and each method's page). The banks, the lab's amount rules and the timings are the lab's.

Webhooks (CloudEvents 1.0.2: PAYMENT_CHARGE_CREATED, …_AUTHENTICATION_PENDING, …_PROVIDER_CONFIRMATION_PENDING, …_AUTHORIZATION_SUCCEEDED, …_AUTHORIZATION_FAILED, …_CAPTURE_SUCCEEDED, …_DISCARDED, …_REFUND_PENDING, …_REFUND_SUCCEEDED, …_REFUND_FAILED) go to the charge's webhooksUrl, signed PPRO-Signature: t=…,s=… (hex HMAC-SHA256 of t + "." + body) under the published secret bistromathic-lab-ppro-webhook-secret.

MethodNameConsumersCurrenciesAuthenticationTimeout
BANCONTACTBancontactBEEURREDIRECT SCAN_CODE APP_INTENT60 min
BLIKBLIKPLPLNREDIRECT MULTI_FACTOR30 min
EPSeps ÜberweisungATEURREDIRECT60 min
P24Przelewy24PLEUR, PLNREDIRECT60 min
TWINTTWINTCHCHFREDIRECT SCAN_CODE60 min
ALIPAYAlipayCNAUD, CAD, CHF, CNY, EUR, GBP, HKD, JPY, NZD, SGD, USDREDIRECT720 min

Test values

ValueEffect
BLIK code 999009, 777123 (777xxx), or the code the lab's banking app showsaccepted; the payer confirms in the app
BLIK code 999001 AUTHENTICATION_FAILED, 999002 INSUFFICIENT_FUNDSFAILED
Bancontact 60600599999899023 / 60600599999899080, 01/2035CAPTURED / FAILED, AUTHENTICATION_FAILED
3000.00 – 3999.99 (the mock authenticator)FAILED after the payer approves: …02 INSUFFICIENT_FUNDS, otherwise GENERIC_DECLINE
…13, from 10.00AUTHORIZATION_ASYNC, then CAPTURED when the provider confirms 20 seconds later
…14, from 10.00AUTHORIZATION_ASYNC, then FAILED 20 seconds later
…53, from 10.00504 from the create: nothing is created
…05, from 10.00the bank declines after the payer approves (GENERIC_DECLINE)
…06, from 10.00the bank declines after the payer approves (INSUFFICIENT_FUNDS)
…07, from 10.00FAILED at once: PPRO's risk check (BLOCKED_ACCOUNT)
refund …77PENDING, then FAILED (REFUND_NOT_SUPPORTED)

eps banks: PSA BANK GROUP, Erste Bank und Sparkassen, Raiffeisen Bankengruppe, UniCredit Bank Austria, BAWAG, Volksbank, easybank, Oberbank, BKS Bank, BTV Bank für Tirol und Vorarlberg, Hypo Tirol Bank, Bankhaus Schelhammer. Przelewy24's documented bankCodes: 25 mBank - mTransfer, 94 Euro Bank, 102 Raiffeisen Bank PBL, 135 Płacę z IKO, 146 Płacę z Orange, 154 BLIK, 177 Użyj przedpłaty, 178 Przekaz tradycyjny, 1000 Przekaz/Przelew tradycyjny.

OPPWA (ACI PAY.ON, Peach Payments)

The Open Payment Platform's server-to-server API (ACI's ex-PAY.ON "OPPWA"), on the real hosts' own paths under /oppwa. One white-label protocol behind several PSPs, played once for every tenant: a client of any of these hosts is pointed at <lab>/oppwa instead.

Cards with 3-D Secure 2 inside the payment (paymentType DB, or PA then CP), back-office refunds (RF) and reversals (RV), credits to a card (CD, the gaming payout), registrations (tokens) and the transaction reports. Every entity id is a merchant of its own. Only the test system is played.

Credentials are ignored: OPPWA authenticates with Authorization: Bearer <access token> and the entityId parameter; the lab never checks the token and tells merchants apart by the entity. Bodies are form-encoded. Webhooks are encrypted, as OPPWA's are: AES-256-GCM under the webhook's 64-hex secret, the ciphertext in hex as the body (or {"encryptedBody": …}), the IV and tag in X-Initialization-Vector and X-Authentication-Tag. The lab's published secret is ac2f9341ede05efc330a3b3733a9d8d15511d88be980c52b845cd98e24f89601. Webhook URLs are set in OPPWA's backoffice; the lab's stand-in is POST /oppwa-lab/backoffice/webhooks with {"entityId": …, "url": …}.

Answers are OPPWA's JSON: result.code and result.description (the documented regex groups decide success, pending and rejection), HTTP 200 for a success or a pending payment, 400 for a rejection or a malformed request (200.300.404 with parameterErrors), 404 for an unknown payment. A successful test payment answers 000.100.110 ("Merchant in Integrator Test Mode"), or 000.100.112 with testMode=EXTERNAL. Payments above 50000.00 and credits above 25000.00 (modeled) are rejected with 100.550.312.

Test cards (OPPWA's own)

Any future expiry, any 3 digit CVV (4 for Amex). Every listed card is approved unless 3-D Secure rejects it; a card number not listed is not enrolled and is approved, unless customParameters[3DS2_enrolled]=true. customParameters[3DS2_flow]=challenge|frictionless and threeDSecure.challengeIndicator=04 force the flow, as OPPWA's testing guide says. A card arriving as vault ciphertext is recognised by BabelPay's BIN and last-four headers.

CardBrandOutcome
4200000000000000VISANot enrolled (no 3-D): successful
4711100000000000VISA3-D enrolled: the issuer's challenge
4012888888881881VISA3-D enrolled: the issuer's challenge
4111111111111111VISASimulates external mode (no 3-D here): successful
4242424242424242VISA3-D disabled: successful
5454545454545454MASTERNot enrolled (no 3-D): successful
5212345678901234MASTER3-D enrolled: the issuer's challenge
5105105105105100MASTER3-D disabled: successful
377777777777770AMEXNot enrolled (no 3-D): successful
375987000000005AMEX3-D enrolled: the issuer's challenge
30082246403846DINERS3-D disabled: successful
4200000000000091VISA3-D Secure 2 frictionless (Y): successful
5200000000000007MASTER3-D Secure 2 frictionless (Y): successful
6761301000993772MAESTRO3-D Secure 2 frictionless (Y): successful
374500262001008AMEX3-D Secure 2 frictionless (Y): successful
4200000000000109VISA3-D Secure 2 frictionless attempt (A): successful
5200000000000023MASTER3-D Secure 2 frictionless attempt (A): successful
6706981111111113MAESTRO3-D Secure 2 frictionless attempt (A): successful
377277081382243AMEX3-D Secure 2 frictionless attempt (A): successful
4200000000000026VISA3-D Secure 2 frictionless (Y): successful (no 3DS Method)
5200000000000056MASTER3-D Secure 2 frictionless (Y): successful (no 3DS Method)
6799851000000032MAESTRO3-D Secure 2 frictionless (Y): successful (no 3DS Method)
375987000000062AMEX3-D Secure 2 frictionless (Y): successful (no 3DS Method)
4200000000000059VISA3-D Secure 2 frictionless attempt (A): successful (no 3DS Method)
5200000000000106MASTER3-D Secure 2 frictionless attempt (A): successful (no 3DS Method)
6007930123456780MAESTRO3-D Secure 2 frictionless attempt (A): successful (no 3DS Method)
373953192351004AMEX3-D Secure 2 frictionless attempt (A): successful (no 3DS Method)
4012001037461114VISA3-D Secure 2 technical error (U): rejected
5434580000000006MASTER3-D Secure 2 technical error (U): rejected
6761301000941201MAESTRO3-D Secure 2 technical error (U): rejected
375987000169875AMEX3-D Secure 2 technical error (U): rejected
4012001037141112VISA3-D Secure 2 user not enrolled (N): rejected
5457350076543210MASTER3-D Secure 2 user not enrolled (N): rejected
6761301000946341MAESTRO3-D Secure 2 user not enrolled (N): rejected
375987000169883AMEX3-D Secure 2 user not enrolled (N): rejected
4532497088771651VISA3-D Secure 2 card not participating: rejected
5497260847316287MASTER3-D Secure 2 card not participating: rejected
6761257707836567MAESTRO3-D Secure 2 card not participating: rejected
343923092050144AMEX3-D Secure 2 card not participating: rejected
4200000000000042VISA3-D Secure 2 challenge: the cardholder decides
5200000000000015MASTER3-D Secure 2 challenge: the cardholder decides
6799998900000060018MAESTRO3-D Secure 2 challenge: the cardholder decides
343434343434343AMEX3-D Secure 2 challenge: the cardholder decides
4200000000000067VISA3-D Secure 2 challenge: the cardholder decides
5200000000000049MASTER3-D Secure 2 challenge: the cardholder decides
6773670009114879MAESTRO3-D Secure 2 challenge: the cardholder decides
375987000000021AMEX3-D Secure 2 challenge: the cardholder decides
4200000000000018VISA3-D Secure 2 challenge: the cardholder decides (no 3DS Method)
5200000000000064MASTER3-D Secure 2 challenge: the cardholder decides (no 3DS Method)
67034200554565015MAESTRO3-D Secure 2 challenge: the cardholder decides (no 3DS Method)
375987000169867AMEX3-D Secure 2 challenge: the cardholder decides (no 3DS Method)
4200000000000075VISA3-D Secure 2 challenge: the cardholder decides (no 3DS Method)
5200000000000072MASTER3-D Secure 2 challenge: the cardholder decides (no 3DS Method)
6759888888888888MAESTRO3-D Secure 2 challenge: the cardholder decides (no 3DS Method)
371449635398431AMEX3-D Secure 2 challenge: the cardholder decides (no 3DS Method)

Lab rules

Amount (last two digits)OnEffect
…14, from 10.00payment800.100.151 transaction declined (invalid card)
…51, from 10.00payment800.100.203 Insufficient Funds
…52, from 10.00credit800.100.180 transaction declined (not supported by issuer): the card takes no credits
…54, from 10.00payment800.100.157 transaction declined (wrong expiry date)
…57, from 10.00payment800.100.170 transaction declined (transaction not permitted)
…59, from 10.00payment800.100.159 transaction declined (stolen card)
…61, from 10.00payment800.100.162 transaction declined (limit exceeded)
…62, from 10.00payment800.100.168 transaction declined (restricted card)
…77, from 10.00refund700.400.200 cannot refund
…82, from 10.00payment800.100.153 transaction declined (invalid CVV)
…91, from 10.00paymentSuccessful; the payment's webhooks arrive 30 s late
…92, from 10.00paymentSuccessful; the payment's webhooks never arrive
…05, from 10.00payment800.100.183 transaction declined (Do not honor)

Real or modeled

PayPal

PayPal's REST APIs on their own paths, under /paypal: a PayPal client swaps https://api-m.sandbox.paypal.com for this host's /paypal. The API is real, built from PayPal's public OpenAPI specifications (paypal-rest-api-specifications) and its developer docs. Credentials are ignored: POST /paypal/v1/oauth2/token gives any client id and secret a token, and the client id names the merchant (its orders, payouts, webhooks and balances); any bearer token is taken. PayPal-Request-Id replays an answer, Prefer: return=representation gets the full resource, and the sandbox's negative testing works: PayPal-Mock-Response: {"mock_application_codes": "INSTRUMENT_DECLINED"} on Orders and Payments, and the Payouts test values (ERRPYO005 in items[0].note, GET /v1/payments/payouts/ERRPYO022).

Webhooks go to every subscribed URL (CHECKOUT.ORDER.APPROVED, PAYMENT.CAPTURE.COMPLETED, .PENDING, .DECLINED, .REFUNDED, PAYMENT.AUTHORIZATION.CREATED, .VOIDED, PAYMENT.PAYOUTSBATCH.PROCESSING, .SUCCESS, PAYMENT.PAYOUTS-ITEM.SUCCEEDED, .FAILED, .UNCLAIMED, .RETURNED, .CANCELED), signed as PayPal signs them: PAYPAL-TRANSMISSION-SIG is SHA256withRSA over transmission id|transmission time|webhook id|CRC32 of the body with the lab's published key, and PAYPAL-CERT-URL names its certificate. The lab's verify-webhook-signature answers SUCCESS only for webhooks it signed. Payout items end 4 seconds after the batch is created (it is PROCESSING after 1).

PayPal accounts at the login (the lab's; any password)

EmailPayer idEffect
sb-buyer@personal.example.com4NC59AJ93HPM6The sandbox's personal account: a PayPal balance of 5,000.00 and a Visa. Pays; payouts back succeed
declined@personal.example.com3SLEDCFBAVXHAOnly a Visa that declines: the capture fails with INSTRUMENT_DECLINED
cannot.pay@personal.example.comLWEWER3WEGHS7Approves, but the capture fails with PAYER_CANNOT_PAY
review@personal.example.com9F4DWRQNQ8C4EThe capture is PENDING (PENDING_REVIEW) for a minute, then COMPLETED
limited@personal.example.comKUNDS7XP2A3YHThe login is refused: the account is limited
otp@personal.example.comNR66CYVKERVEGAlways asks for the one-time code texted to the phone
payout.locked@personal.example.com32YVBPFSZ2HBEPays; a payout back to it fails (RECEIVER_ACCOUNT_LOCKED)
payout.unclaimed@personal.example.comDLKKG2RME4R24Pays; a payout back to it stays UNCLAIMED (RECEIVER_UNREGISTERED) until it is cancelled, or returned after 30 days
any other emailits ownA fresh, verified account with 5,000.00 in any currency

Lab rules

Deposit amountEffect
…13, from 10.00The capture is PENDING (PENDING_REVIEW) for a minute, then COMPLETED
…05, from 10.00The capture fails with INSTRUMENT_DECLINED (422)
…09, from 10.00The capture is DECLINED by PayPal's risk filters (201, capture status DECLINED)

Limits: PayPal's maximum per payment (AUD 12,500, BRL 20,000, CAD 12,500, CZK 240,000, DKK 60,000, EUR 8,000, HKD 80,000, HUF 2,000,000, ILS 40,000, JPY 1,000,000, MYR 40,000, MXN 200,000, TWD 330,000, NZD 15,000, NOK 70,000, PHP 500,000, PLN 32,000, GBP 5,500, SGD 16,000, SEK 80,000, CHF 13,000, THB 360,000, USD 10,000; above it the order is refused with TRANSACTION_REFUSED, the lab's choice of issue), 60,000 per payout item (TRANSACTION_LIMIT_EXCEEDED), and the merchant's balance (INSUFFICIENT_FUNDS, REFUND_FAILED_INSUFFICIENT_FUNDS; 100,000.00 per currency to start). An unapproved order expires after 3 hours, an approved one must be captured within 3 hours, an authorization lasts 29 days, a capture can be refunded for 180 days. The fees are the lab's (3.49 % + 0.39 per capture, 2 % up to 20.00 per payout item).

Gambling (shown, not enforced): PayPal approves gambling merchants market by market. bwin UK takes no PayPal first deposit since 5 September 2024; Ladbrokes restricts the sportsbook until KYC after a PayPal first deposit; withdrawals to PayPal need a PayPal deposit within the last 6 months.

Gigadat (Interac e-Transfer, Request Money)

Gigadat, Canada's iGaming Interac processor, under /gigadat: a Gigadat client swaps https://interac.express-connect.com for this host's /gigadat. The API is modeled: Gigadat's documentation comes only with a contract. The paths, fields, statuses and types are those of Hyperswitch's open-source Gigadat connector (juspay/hyperswitch) and its field probe, and of merchants' published integrations; the credentials (access token, security token, campaign ID) and the campaign's listener and redirect URLs are from Corefy's, Rebilly's, BridgerPay's and Too Much Media's guides. The banks' screens, the limits, the timers, the amount rules, the signature on notifications, the per-request listener and redirect URLs (listenerUrl, successUrl, failureUrl) and the payee's side are the lab's. Credentials are ignored: the Basic auth's user only tells merchants apart; any campaign ID works (a simulated connection without one uses bistromathic-lab).

Listener notifications: POST <listener>?transaction=<transactionId>&status=<STATUS_…>, a form body (transactionId, id, type, cpiType, finalType, amount, currency, userId, name, email, mobile, userIp, site, campaign, financialInstitution), on STATUS_PENDING and every final status, signed X-Gigadat-Signature (hex HMAC-SHA256 of transaction|status|body) under the published security token bistromathic-lab-gigadat-simulation-security-token. A deposit nobody finishes is STATUS_EXPIRED after 20 minutes.

Limits: 1.00 to 10000.00 CAD per deposit and 1.00 to 10000.00 per e-Transfer out at Gigadat; the payer's bank takes at most 3000.00 per transfer (STATUS_REJECTED).

Lab rules

Deposit amountEffect
…13, from 10.00STATUS_PENDING for a minute after the payer confirms, then STATUS_SUCCESS
…33, from 10.00STATUS_SUCCESS with no listener notification: only the transaction lookup (or reconciliation) tells
…02, from 10.00STATUS_REJECTED once the payer confirms: the bank declined (insufficient funds)
…03, from 10.00STATUS_REJECTED1 once the payer confirms: Gigadat's checks (the name Interac reports is not the player's)
…04, from 10.00STATUS_FAILED once the payer confirms: a technical failure
Payout amountEffect
…01, from 10.00The payee has Autodeposit: deposited two seconds after it is sent (STATUS_SUCCESS)
…02, from 10.00STATUS_REJECTED: the payee's bank refused the e-Transfer
…04, from 10.00STATUS_FAILED: a technical failure
any otherSTATUS_PENDING until the payee deposits it (inbox) or declines it
Refund amountEffect
…05, from 10.00422 REFUND_REJECTED: Gigadat refuses the refund

Aeropay

Aeropay's v2 API (US pay by bank: ACH and RTP, for gaming among others) on its own paths, under /aeropay: an Aeropay client swaps api.sandbox-pay.aero.inc for this host's /aeropay. Credentials are ignored: POST /aeropay/v2/token gives any apiKey and apiSecret a 30-minute token for the merchant id (scope merchant), or for one of its users (userForMerchant, who must have confirmed their code there: AP002 otherwise). Each merchant id is one simulated merchant, with its users and their linked banks, its transactions and its webhooks. The API is real: built from Aeropay's public reference (dev.aero.inc, an OpenAPI definition per endpoint) and guides. Modeled, where Aeropay publishes nothing: the checkout's pages (the SDK's popup is Aeropay's own), the lab's banks, rules, timings and limits, and that the SDK's uuid is the transaction's id.

Webhooks go to the URL subscribed for their topic (payloadVersion 2.0: transaction_completed a second after a payment or payout, transaction_declined and user_suspended on a return, transaction_voided, transaction_refunded). Once a merchant has called createWebhookSigningKey they carry ap-signature: the hex HMAC-SHA256 of the body plus the registered url, as Python's json.dumps writes it, under the published key bistromathic-lab-aeropay-webhook-signing-key. A payment is pending (Aeropay guarantees it) until the lab's batch a minute later clears it to processed; a full refund before then voids it, a partial one is queued until it clears.

Test values

Phones: 5550173 AP115, 3105550187 AP115, +65-718-555-0134 AP119, +657185550134 AP119, +14045550124 AP115, +13125550198 AP115, +19175550163 AP115. Emails: no-authenticated-user@aeropay.com AP101, unable-to-create-user@aeropay.com AP102, cannot-extract-user-info@aeropay.com AP104, user-already-exists@aeropay.com AP107, error-adding-user@aeropay.com AP108. Aerobank: sync / no_mfa, or mfa with the answer correct. Every other bank takes any login. A savings account is refused when paying (AP411). Banks: Aerobank (RTP), Aerobank (OAuth) (RTP), Chase (RTP), Bank of America (RTP), Wells Fargo (RTP), Citibank (RTP), U.S. Bank (RTP), Capital One, PNC Bank (RTP), Truist (RTP), TD Bank, Navy Federal Credit Union, Fifth Third Bank (RTP), Huntington Bank.

exceptionScenarioEffect
returnInsufficientpending, then declined 3 s later: an R01 (Insufficient Funds) return, status nsf; the user is suspended
returnAccountpending, then declined 3 s later: an R02 (Account Closed) return; the user is suspended
returnDisputepending, then declined 3 s later: an R07 (Customer Revoked Authorization) return; the user is suspended
delinquentAP109: the user is rejected for delinquent activity
riskEngineAP307: Aeropay's risk engine declines the payment (reasonCodes ["exceptionScenario"])
balanceInsufficientAP302: the user's balance does not cover the payment
Checkout amountEffect
…07, from 10.00AP307, declined by the risk engine (reasonCodes A1): the checkout shows the decline
…02, from 10.00AP302 Insufficient funds: the checkout shows the decline
…09, from 10.00AP109 delinquent activity: the checkout shows the decline
…01, from 10.00Approved, then an ACH return R01 3 s later (transaction_declined, status nsf)
…03, from 10.00Approved, then an ACH return R02 3 s later (transaction_declined)
…05, from 10.00Approved, then an ACH return R07 3 s later (transaction_declined)
over 5 000.00AP304
Payout amountEffect
…15, from 10.00AP315 Payment rejected: threshold or suspended bank
…02, from 10.00AP302 Insufficient funds
…04, from 10.00AP304 Transaction amount exceeds limit
…09, from 10.00pending, then voided 3 s later (transaction_voided: the payout did not go out)
over 10 000.00AP304
Refund amountEffect
…10, from 10.00AP310 Credit transaction declined

PayNearMe (MoneyLine)

PayNearMe's API v3.0 on its own paths, under /paynearme: a PayNearMe client swaps https://api.paynearme-sandbox.com (and the login host) for this host's /paynearme. The API is real (apidocs.paynearme.com, public, OpenAPI 3.0.1 per endpoint): the calls, parameters, the {"status", "errors"} envelope answered with HTTP 200, the signature scheme, the sandbox test cards and decline amounts, the callbacks. Modeled (PayNearMe's keys need an NDA, and the docs leave them out): the MoneyLine page's screens, the limits and timers, the ACH, PayPal and Venmo test values, and a decline's details. Credentials are ignored: any OAuth client gets a token, no signature is checked; site_identifier names the site. All of PayNearMe lives in one LabMerchant object (paynearme:world).

Test cards (any amount but a decline amount succeeds): 9999925615744358 (debit), 9999924503863867 (debit), 9999926275252757 (debit), 9999926481617389 (debit), 9999926863362265 (debit), 9999916516806651 (credit), 9999914004280406 (credit), 9999915317077884 (credit), 9999915568082161 (credit), 9999915852074023 (credit), and the lab's shared table. Limits per payment (modeled): cash 1.00–1500.00, card 1.00–10000.00, ach 1.00–25000.00, paypal 1.00–10000.00, venmo 1.00–5000.00, push 1.00–25000.00.

Decline amounts (PayNearMe's sandbox, any test card)

AmountCodeMessage
213.8905This card is not permitted to make this payment.
213.8312This payment with this card is invalid.
213.7914This card number is invalid.
213.7351There are insufficient funds on this card to complete this payment.
213.7154This card is expired.
213.6757This card is not permitted to make this kind of payment.
213.6161This payment exceeds this card’s withdrawal limit. (disbursements only)
213.5962This card is restricted and cannot make this payment.
213.53NZThis payment didn't complete because the security code was invalid.
172.01P9Unable to process this payment. Please try a different payment method.
172.0210Unable to process this payment for the requested amount. Please try a different payment method.
172.0320Unable to process this payment right now. Please try again later or use a different payment method.
172.0460This payment could not be authorized. Please contact your card issuer or try a different payment method.
172.0568Unable to receive response from card issuer. Please try again in a few minutes.
172.066PThe security information could not be verified. Please check your card information and try again.
172.07SGAccount could not be found. Please check your card information or try a different payment method.
44.029GYour bank didn’t approve this transaction. Please review your account settings or contact your bank.
44.015CYour bank couldn’t approve this transaction. Please contact your bank for more information.

Bancontact

Bancontact Pro's Payment V3 API (3.6.5) and Payment Refund Service API (3.0.3) on their own paths, under /bancontact: a Bancontact client swaps merchant.api.preprod.bancontact.net for this host's /bancontact. Credentials are ignored: any Authorization is taken and names the merchant; a request's Signature (JWS) is never checked. The API is real: built from Bancontact Pro's public developer portal (docs.bancontactpro.com, its OpenAPI files and guides). Modeled: the checkout page's card tab (Bancontact Pro's own page offers the app; the card's entry, its card reader and banking-app confirmation are modeled on the PSPs' Bancontact pages), the app's screens, the payers' accounts and the lab's rules.

Callbacks (MerchantCallback, every final status; IDENTIFIED to an identifyCallbackUrl) carry signature: a detached JWS, ES256, with the https://payconiq.com/* critical claims, signed with the published key 49cc69eb-393a-4a2c-896f-0a29d82d0cd5. A payment request is valid for 20 minutes, then EXPIRED. Limits: Bancontact Pay 1,500.00 EUR per payment and 2,500.00 EUR a day per card, the card 2,500.00 EUR per seven days (AUTHORIZATION_FAILED).

Test values

ValueEffect
5127 8829 9999 9715, 11/29Always authorised
5127 8829 9999 9723, 11/29Insufficient funds
5127 8829 9999 9731, 11/29Card refused by the issuer
Any other valid card, a future expiryAuthorised
…13, from 10.00AUTHORIZED after the payer confirms, then SUCCEEDED a minute later
…22, from 10.00The create answers 422 UNABLE_TO_PAY_CREDITOR: nothing is created
…53, from 10.00The create answers 503 TRY_AGAIN_LATER: nothing is created
…05, from 10.00AUTHORIZATION_FAILED after the payer confirms (insufficient funds)
…06, from 10.00FAILED after the payer confirms (a technical error)
refund …05PENDING, then FAILED

SIBS Gateway (MB WAY, Multibanco)

SIBS Gateway (SPG v2), Portugal's payment gateway, on its own paths under /sibs: a client of https://spg.qly.site1.sibs.pt/api/v2 (sandbox) or https://api.sibspayments.com/api/v2 (production) is pointed at <lab>/sibs/api/v2 instead (/api/v1 answers the same). MB WAY and Multibanco Reference only, euros only, one-off purchases (paymentType PURS). Any terminal is a merchant of its own; a simulated connection uses 11111.

Credentials are ignored: no call is refused for its Authorization: Bearer token, its X-IBM-Client-Id, or the Authorization: Digest <transactionSignature> of a purchase or a generation. Webhooks are encrypted, as SPG's are: AES-256-GCM under the Backoffice's secret (Base64), the ciphertext in Base64 as a text/plain body, the IV and the tag in Base64 in X-Initialization-Vector and X-Authentication-Tag. The lab's published key is 9GUP06mwAS++Q8TmXeLoZlCk/oMN2lcW+SRbctJD+XY=. SPG wants each one answered HTTP 200 with {"statusCode":"000","statusMsg":"Success","notificationID":…}. Webhook URLs are set in the SPG Backoffice; the lab's stand-in is POST /sibs-lab/backoffice/webhooks with {"terminalId": …, "url": …}.

MB WAY test numbers

customerPhoneEffect
351#911111112Approved in the app about 30 seconds later
351#911111113Not an MB WAY number: the purchase is refused (E0506, the alias does not exist)
351#911111114SPG declines a few seconds later (10.106.0009)
351#911111115Nobody answers: the request expires (Timeout) about 10 seconds later, the four minutes made short
351#911111116The payer refuses in the app a few seconds later (01.106.0001)
any other 351#9xxxxxxxxThe payer answers on the drawn phone (or in the lab drawer): Confirmar with any six-digit PIN approves, Recusar declines (01.106.0001), nothing for four minutes is a Timeout
another country, or not a Portuguese mobileE0506 (the alias does not exist) or E0504 (invalid format)

Limits

Lab rules (the amount's cents, from 1 €)

Real or modeled

Przelewy24

Przelewy24's REST API (Poland's pay-by-link aggregator: BLIK and the banks' fast transfers) and Ekspres Przelewy24's (its payouts, the rail Betclic and Superbet pay withdrawals on), on their own paths under /przelewy24: a client of sandbox.przelewy24.pl swaps the host for this host's /przelewy24. Credentials are ignored: Basic auth must be there, but its user only names the account (P24: the POS id; Ekspres: the API key), and no request's sign is checked. The APIs are real: built from P24's public OpenAPI (developers.przelewy24.pl, 1.0.18) and Ekspres Przelewy24's (developers.przelewy24.pl/ekspres, 1.0.6). Modeled, where P24 publishes nothing: the panel's and the banks' screens, which banks a merchant is offered and their method ids (P24's own for mBank 25, Raiffeisen 102, IKO 135 and BLIK 154), the methods' amount limits, the payer's accounts, the Ekspres wallet, its limits and timings, and the lab's rules.

The panel: the method tiles (BLIK, then the banks, with a search; P24's terms to accept unless regulationAccept), BLIK's code and the push in the bank's app, or the bank's login, the account (with balances), the pre-filled transfer that cannot be edited, and the bank's SMS code or its app; then P24's result and back to urlReturn (cancel too, err161/err162). On a computer the SMS and the app arrive on the phone drawn beside the page; on a phone the app fills the screen. ?device=mobile|desktop forces either flow. P24 notifies urlStatus only for a paid transaction, signed SHA-384 over merchantId, posId, sessionId, amount, originAmount, currency, orderId, methodId, statement and the published CRC bistromathic-lab-przelewy24-crc; a refund's notification likewise; Ekspres' transfer notifications (type pay, then payout: 4 paid, 2 sent, 5 finished; 3 or 7 failed) over type, id, status and bistromathic-lab-przelewy24-ekspres-crc.

Test values

BLIK 777XXX pays (P24's sandbox codes), any other six digits do not. Any bank login. SMS: the code on the phone, or 111111 (three wrong and the bank refuses). Banks: mBank (25, app), PKO Bank Polski (135, app), Bank Pekao (52, app), ING Bank Śląski (112, app), Santander Bank Polska (20, app), Bank Millennium (65, SMS), Alior Bank (64, app), BNP Paribas (70, SMS), Credit Agricole (45, SMS), Citi Handlowy (46, SMS), Inteligo (38, SMS), VeloBank (87, SMS), Nest Bank (72, SMS), Bank Pocztowy (90, SMS), Raiffeisen Digital Bank (102, SMS), Plus Bank (222, SMS), Bank Ochrony Środowiska (223, SMS), Toyota Bank (224, SMS), Bank BPS (225, SMS), SGB-Bank (226, SMS); BLIK 154.

Deposit amountEffect
…49, from 10.00P24 refuses the payment at the panel: err49, the risk-assessment score is too high
…51, from 10.00The bank refuses the transfer at authorisation (no funds): err53, the payer can try another method
…02, from 10.00Paid, but the bank's confirmation (and P24's notification) comes 60 seconds later
Refund amountEffect
…13, from 10.00Accepted, then rejected by P24 (status 4; the refund notification says status 1)
…14, from 10.00409: Insufficient funds available
Ekspres payout amountEffect
…02, from 10.00400, error 2002: insufficient funds in the wallet
…09, from 10.00400, error 2009: transfer limit exceeded
…03, from 10.00Paid (4), then 3: error while sending to processing; the money goes back to the wallet
…07, from 10.00Paid (4), sent (2), then 7: the bank refused it; the money goes back to the wallet
below 1.00 / over 500,000.00 PLN1008 / 2003