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.
POST /v1/auth — reserve funds (no settlement)POST /v1/debit — auth + capture in one stepPOST /v1/capture — capture an existing authPOST /v1/refund — refund a captured tx (full or partial)POST /v1/void — void an unsettled authPOST /v1/cancel — cancel a captured txPOST /v1/status — look up a tx by id or merchantRefAll API requests must include the shared secret header X-Sim-Key.
ev:…) matches by the card.bin and card.last4 sent beside it.approved.| PAN | Status | 3DS | Description |
|---|---|---|---|
4242424242424242 | approved | Approved (Visa, no 3DS) | |
4111111111111111 | approved | Approved (Visa, no 3DS) | |
5555555555554444 | approved | Approved (Mastercard, no 3DS) | |
378282246310005 | approved | Approved (Amex, no 3DS) | |
4000000000003220 | approved | yes | Approved after 3DS challenge |
4000002500003155 | approved | yes | Approved after 3DS challenge (Visa) |
5200828282828210 | approved | yes | Approved after 3DS challenge (Mastercard) |
4000000000000002 | do_not_honor | Do not honor | |
4000000000009995 | insufficient_funds | Insufficient funds | |
4000000000000069 | expired_card | Expired card | |
4000000000000127 | invalid_cvv | Invalid CVV | |
4000000000000119 | psp_malfunction | Acquirer system malfunction | |
4100000000000019 | suspected_fraud | Suspected fraud | |
4000000000000259 | issuer_err | Issuer or switch is inoperative | |
4000000000000341 | 3ds_auth_failure | yes | 3DS authentication failure |
4000000000000051 | psp_timeout | Acquirer timeout |
| Amount ends in | Status | 3DS | Description |
|---|---|---|---|
| …12 | invalid_amt | Invalid amount | |
| …13 | invalid_amt | Invalid amount | |
| …14 | invalid_account | Invalid card number | |
| …33 | expired_card | Expired card | |
| …51 | insufficient_funds | Insufficient funds | |
| …54 | expired_card | Expired card | |
| …59 | suspected_fraud | Suspected fraud | |
| …65 | do_not_honor | Exceeds withdrawal frequency | |
| …79 | invalid_cvv | Invalid CVV | |
| …91 | issuer_err | Issuer unavailable | |
| …96 | psp_malfunction | Acquirer system malfunction | |
| …98 | 3ds_auth_failure | yes | 3DS challenge, then authentication failure (amount-rule: ends in 98) |
| …99 | approved | yes | Approved after 3DS challenge (amount-rule: ends in 99) |
| …00 | approved | Approved (amount-rule: ends in 00) | |
| …01 | do_not_honor | Refer to card issuer | |
| …02 | do_not_honor | Do not honor | |
| …05 | insufficient_funds | Insufficient funds |
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).
PUT /swish-cpcapi/api/v2/paymentrequests/{instructionUUID}, POST /swish-cpcapi/api/v1/paymentrequests — 201 with Location, plus PaymentRequestToken when there is no payerAlias (m-commerce)GET and PATCH (cancel, application/json-patch+json) /swish-cpcapi/api/v1/paymentrequests/{id}PUT /swish-cpcapi/api/v2/refunds/{instructionUUID}, POST /swish-cpcapi/api/v1/refunds, GET /swish-cpcapi/api/v1/refunds/{id} — VALIDATED, DEBITED after 2 s, PAID after 4 sPOST /qrg-swish/api/v1/commerce — the QR code for a token (format: "svg" only)/swish/checkout/{token}?callbackurl=… — the payer: on a computer the merchant's QR page with the phone beside it (scan, Swish, BankID, confirmation); on a phone the Swish app and BankID. &device=mobile|desktop forces either/swish/app/{payerAlias} — e-commerce: the payer opening the app to the request waiting for their numberCallbacks 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.
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:
FF08 RP03 BE18 RP01 PA02 AM06 AM02 AM03 RP02 RP06 ACMT03 ACMT01 ACMT07 PA01 (403); for e-commerce also VR01 VR02RF07 BANKIDCL FF10 TM01 DS24 (and VR01 VR02 for m-commerce)FF08 RP03 PA02 AM06 RF08 AM03 RP01 RP02 ACMT07 ACMT01 RF02 RF03 RF04 RF06 BE18 PA01 (403)RF07 BANKIDCL; after it: FF10 DS24Otherwise the amount decides (a payment request only):
| Amount ends in | Code | When | errorMessage |
|---|---|---|---|
| …10 öre | FF10 | ERROR once the payer signs | Bank system processing error |
| …24 öre | DS24 | ERROR once the payer signs | Swish timed out waiting for an answer from the banks after payment was started |
| …01 öre | TM01 | ERROR once the payer signs | Swish timed out before the payment was started |
| …02 öre | AM02 | 422 at create | Amount value is too large |
| …03 öre | ACMT03 | ERROR once the payer signs | Payer not Enrolled |
| …06 öre | AM06 | 422 at create | Specified transaction amount is less than agreed minimum |
| …07 öre | RF07 | ERROR once the payer signs | Transaction declined |
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).
POST /vipps/accesstoken/get — a Bearer token for an hour, whatever client_id, client_secret and Ocp-Apim-Subscription-Key are sent (Merchant-Serial-Number names the sales unit). The paths below are under /vipps as well.POST /epayment/v1/payments (WEB_REDIRECT or NATIVE_REDIRECT, Idempotency-Key required) — 201 with the landing page as redirectUrlGET /epayment/v1/payments/{reference} and …/eventsPOST /epayment/v1/payments/{reference}/capture, …/refund (modificationAmount) and …/cancelPOST /epayment/v1/test/payments/{reference}/approve — the test environment's force approvePOST, GET /webhooks/v1/webhooks, DELETE /webhooks/v1/webhooks/{id}/vipps/landing?token=… — the payer: phone number on a computer (the app at once on a phone), then the Vipps app: pay, cancel, or let it expireStates 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.
| Payment amount | In 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 amount | Refused 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 |
| Amount | Effect |
|---|---|
| …28 øre, from 10 kr | The first capture fails: 6280 Capture failed, please try again later; a retry captures |
| …05 øre, from 10 kr | Every capture is refused: 6260 Funds unavailable |
| …06 øre, from 10 kr | Create refused: 422, 5070 Payment cannot be created |
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.
| MSN | Market | Currency | Minimum | The payer | Test user |
|---|---|---|---|---|---|
45…, e.g. 451234 | MobilePay Denmark | DKK | 1 øre | /mobilepay/landing, Danish; picks the card first | +45 12 34 56 78 |
358…, e.g. 358123 | MobilePay Finland | EUR | 1 cent | /mobilepay/landing, Finnish | +358 40 123 4567 |
any other, e.g. 123456 | Vipps Norway | NOK | 100 ø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.
+45 or +358), with the phone drawn beside it; the MobilePay app at once on a phone. The app is MobilePay-blue and in Danish or Finnish; Face ID takes two seconds; "Stryg for at betale" / "Pyyhkäise maksaaksesi" pays. ?device=mobile|desktop forces either flow, and the test banner is in Danish or Finnish.197 and 202 are 3-D Secure, which Vipps documents as Norway only: in Denmark and Finland they are ordinary amounts. The lab rules from 10 kr. / € 10 apply as for Vipps.| Market | Limit | In the lab |
|---|---|---|
| Finland | Payments 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). |
| Denmark | None of MobilePay's own on payments to businesses: they follow the card's | Visa/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. |
| Cashier | Market | MobilePay deposits | Withdrawals |
|---|---|---|---|
| Danske Spil | DK | Card, MobilePay, Apple Pay and Google Pay; no bank transfer | To NemKonto, at most one payout a day, within 32 h |
| bet365 DK | DK | Enter the mobile number, choose the card in the app, instant; 100–50 000 kr. (unverified) | By bank transfer |
| Veikkaus | FI | Since 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 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.
POST /ideal2/merchanttoken — the acquirer: client_credentials with a JWT client assertion (iss = sub = the merchant id, aud = this host); an access token for an hourPOST /v2/merchant-cpsp/transactions — Request-ID, Authorization: Bearer and a detached-JWS Signature with every https://idealapi.nl/* claim; 201 with iDEAL | Wero's payment page as links.redirectUrl (the bank's own page when issuerId is sent)GET /v2/merchant-cpsp/transactions/{transactionId} — the status, and on SUCCESS the debtor and guaranteedAmountRequest-ID and X-Sender: iDEAL; iDEAL wants a 204/ideal/pay/{token} — the payer: on a computer the QR code (a real phone's camera can scan it) with the payer's phone drawn beside it, or "Kies je bank" for internet banking; on a phone the bank list, then a banking app. ?device=mobile|desktop forces eitherStatuses 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.
| Amount | Effect |
|---|---|
€ 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 |
| Amount | Effect |
|---|---|
| …24 cents, from € 10 | the transaction-callback arrives 20 seconds late |
| …29 cents, from € 10 | the first create is refused 429 TOO_MANY_REQUESTS; iDEAL's one retry goes through |
| …41 cents, from € 10 | no transaction-callback at all: only a GET tells the outcome |
| …53 cents, from € 10 | the create is refused: 503 SERVICE_UNAVAILABLE |
| …05 cents, from € 10 | the bank refuses the payment, insufficient balance (FAILURE) |
| …07 cents, from € 10 | the create is refused: 500 TECHNICAL_ERROR |
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.
POST /payu/pl/standard/user/oauth/authorize — client_credentials (and trusted_merchant), form-encoded; a bearer token for 43199 sPOST /payu/api/v2_1/orders — payMethods.payMethod {type:"PBL", value:"blik"}: 302 with redirectUri (PayU's BLIK page) in the body and Location; {type:"BLIK_AUTHORIZATION_CODE", value:"<6 digits>"}: 201, the order PENDING while the payer confirms (a refused code: 400 ERROR_AUTHORIZATION_CODE with the order's id); {type:"BLIK_TOKEN"} for a One Click tokenGET / DELETE /payu/api/v2_1/orders/{orderId}, GET …/transactions (each BLIK attempt and its resultCode)PUT …/status (orderStatus: COMPLETED) and POST …/captures — receive an order WAITING_FOR_CONFIRMATIONPOST / GET …/refunds, GET …/refunds/{refundId} — PENDING, FINALIZED three seconds later (or CANCELED); PayU's 9101–9115 refusalsGET /payu/api/v2_1/paymethods — with a trusted_merchant token, the buyer's One Click tokensnotifyUrl on PENDING, WAITING_FOR_CONFIRMATION, COMPLETED and CANCELED, and on a refund's FINALIZED / CANCELED; PayU wants an HTTP 200/payu/pay?orderId=…&token=… — the payer: PayU's BLIK page; on a computer with the payer's phone beside it (the BLIK tile, the code with its two-minute countdown, the push with Zatwierdź / Odrzuć, the PIN for two seconds); on a phone the page alone and the banking app at /payu/bank. &device=mobile|desktop forces either/payu/bank?pos=300746 — the payer's banking app on its own: its codes work for a merchant that sends BLIK_AUTHORIZATION_CODE, and the push appears thereStatuses 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.
| Code | Effect |
|---|---|
777xxx | authorised (no token registered): COMPLETED three seconds later |
200201 | authorised, and a One Click (UID) token is registered |
500500 | refused by the bank (negative authorization): CANCELED |
700701 | code expired: ERROR_AUTHORIZATION_CODE / AUTH_CODE_EXPIRED |
700702 | code cancelled: ERROR_AUTHORIZATION_CODE / AUTH_CODE_CANCEL |
700703 | code 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.
| Amount | Effect |
|---|---|
| 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) |
| Amount | Effect |
|---|---|
| …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 grosze | accepted (PENDING), then CANCELED: statusError BANK_DECLINED |
| refund amount …02 grosze | refused at create: 9102 NO_BALANCE (the shop's balance is short) |
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:
| Bank | Daily BLIK limit online |
|---|---|
| PKO Bank Polski | 3 000,00 zł |
| mBank | 3 000,00 zł |
| ING Bank Śląski | 2 000,00 zł |
| Bank Pekao | 1 000,00 zł |
| Bank Millennium | 1 000,00 zł |
| VeloBank | 1 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.
| HTTP | statusCode | Meaning |
|---|---|---|
| 200 | SUCCESS | Request processed |
| 201 | SUCCESS | Order created with a BLIK code or token in payMethods |
| 302 | SUCCESS | Order created: redirectUri in the body and the Location header |
| 302 | WARNING_CONTINUE_REDIRECT | Transparent pay-by-link: redirect the buyer (not BLIK) |
| 400 | ERROR_SYNTAX | The body is not JSON |
| 400 | ERROR_VALUE_INVALID | A value is wrong (a BLIK code that is not six digits, an amount BLIK does not take…) |
| 400 | ERROR_VALUE_MISSING | A required value is missing |
| 400 | ERROR_ORDER_NOT_UNIQUE | extOrderId already used at this POS |
| 400 | ERROR_INCONSISTENT_CURRENCIES | The order's currency is not the POS's (PLN) |
| 400 | ERROR_AUTHORIZATION_CODE | The BLIK code was refused (expired, cancelled, used, unknown, over the payer's limit) |
| 401 | UNAUTHORIZED | No or a wrong access token |
| 403 | ERROR_VALUE_INVALID | The token's POS is not merchantPosId (INVALID_AUTH_FOR_THIS_ORDER) |
| 404 | DATA_NOT_FOUND | No such order or refund at this POS |
| 500 | ERROR_INTERNAL | PayU unavailable |
| 503 | SERVICE_NOT_AVAILABLE | PayU 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' 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.
POST /redsys/sis/realizarPago — the redirection: the payer's browser posts Ds_SignatureVersion, Ds_MerchantParameters (Base64 JSON: DS_MERCHANT_AMOUNT, _ORDER, _MERCHANTCODE, _TERMINAL, _CURRENCY 978, _TRANSACTIONTYPE 0, _PAYMETHODS z, _MERCHANTURL, _URLOK, _URLKO, _MERCHANTDATA…) and Ds_Signature; Redsys' Bizum page opens. Refusals show Redsys' error page with the SIS0xxx code in its source (SIS0051 for an order used before; the same form posted again resumes it)POST /redsys/sis/rest/trataPeticionREST — the triplet as JSON; refunds (DS_MERCHANT_TRANSACTIONTYPE 3, the payment's own order) answer the signed triplet with Ds_Response 0900, or {"errorCode":"SIS0xxx"}POST /redsys/apl02/services/SerClsWSConsulta — the SOAP query service (SOAPAction: consultaOperaciones): Transaction, Monitor and Detail by order, answered with Ds_State and Ds_Response per operation, signed; XML0024 for an order Redsys never saw/redsys/bizum/{token} — the payer (lab paths): Redsys' page with the phone number (+34), then Request to Pay (the bank app's push and a 4-minute countdown) or the legacy Clave Bizum and SMS code; on a computer the payer's phone is drawn beside it. ?device=mobile|desktop forces either; POST /redsys/sis/realizarPago?device=… tooThe 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).
| Phone | Bank | Effect |
|---|---|---|
600000000 | Banco de pruebas | Paylands' KO number: denied once the payer confirms (9966) |
611111111 | BBVA | BBVA: €3,000 per operation, €3,000 a day, €5,000 a month (9966 beyond) |
622222222 | CaixaBank | CaixaBank: €500 per operation, €2,000 a day, €5,000 a month (9966 beyond) |
633333333 | Caja Rural | Caja Rural, still on Clave Bizum: the legacy flow (Clave, then the SMS code) |
644444444 | Unicaja | Unicaja, €50 in the account: more than that is denied (9677) |
699999999 | Banco de pruebas | Not registered in Bizum: Redsys asks for another number |
700000000 | Banco de pruebas | Paylands' 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/7xx | by the last digit | A 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 | Per operation | Per day | Per month |
|---|---|---|---|
| BBVA | 3.000,00 € | 3.000,00 € | 5.000,00 € |
| CaixaBank | 500,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.
| Amount | Effect |
|---|---|
| …91 cents, from €10 | The online notification arrives 30 seconds after the payer is back |
| …92 cents, from €10 | No online notification at all: the merchant has to ask the query service |
| …93 cents, from €10 | The payer's bank does not answer: denied with 9912 (Emisor no disponible) |
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.
| Code | Meaning |
|---|---|
9485 | BIZUM ha devuelto un KO en la devolución |
9677 | Saldo disponible insuficiente |
9912 | Emisor no disponible |
9915 | A petición del usuario se ha cancelado el pago |
9966 | BIZUM ha devuelto un KO en la autorización |
9998 | Operación en proceso de solicitud de datos de tarjeta |
0000 | Transacción autorizada para pagos y preautorizaciones |
0900 | Transacción autorizada para devoluciones y confirmaciones |
0184 | Error en la autenticación del titular |
0190 | Denegación del emisor sin especificar motivo |
Ds_ProcessedPayMethod 68, DS_MERCHANT_BIZUM_MOBILENUMBER prefilling the phone, a notification for a refund, the SOAP envelope's exact namespaces, the 10 % refund allowance and the lab rules. Bizum's REST push (R2P without redirection) is not simulated (SIS0112).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.
POST /brite/api/merchant.authorize (public_key, secret) — access_token, expires (six hours), refresh_token; merchant.token renews them. Every other call wants Authorization: Bearer … (401 without one); a token the lab did not issue is taken, for one shared merchantPOST /brite/api/bank_account.create (country_id, iban, holder_firstname, holder_lastname, holder_dob, holder_address) — {id}; the same IBAN again updates the holder and keeps the id. bank_account.get shows it maskedPOST /brite/api/transaction.create_withdrawal (amount, bank_account_id, currency_id, merchant_reference, statement_reference, brand_name, callbacks) — {id, eta}. The same merchant_reference with the same amount answers the first payout again; with another amount it is refusedPOST /brite/api/transaction.get, transaction.get_by_merchant_reference, transaction.statePOST /brite/api/country.list, currency.listPOST /brite/api/lab.set_returned_funds_url (url) — lab only: Brite keeps the returned-funds notification URL in its Back Office; here a merchant sets it with this callA 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.
| Amount | Brite's case | Effect |
|---|---|---|
11.01 | PO-03 | Aborted payout: STATE_ABORTED (2) after a second, error_code bank_rejected_transaction |
11.02 | PO-03 | Aborted payout: STATE_ABORTED (2) after a second, error_code risk_engine_rejection |
12.01 | PO-04 | Failed payout: STATE_FAILED (3) after a second, no error_code (Brite: the reason is unknown) |
13.01 | PO-05 | Delayed payout: STATE_COMPLETED (4) for five minutes, then CREDIT (5) and SETTLED (6) |
14.01 | PO-07 | Returned funds: SETTLED (6), then a minute later the money comes back (RETURNED_TRANSACTION) |
| Value | Effect |
|---|---|
15.01 | Refused at create: the merchant's Brite balance is too low (400 BadRequest) |
16.01 | Stays STATE_PENDING (1) for two minutes, as Swedish payouts sometimes do, then COMPLETED, CREDIT and SETTLED |
IBAN ending 9901 | The payee's bank refuses the payout: STATE_ABORTED (2), error_code bank_rejected_transaction |
IBAN ending 9902 | The account is closed: the payout SETTLES, then the money comes back a minute later (RETURNED_TRANSACTION) |
holder_dob under 18 years | Brite's risk engine refuses the payee: STATE_ABORTED (2), error_code risk_engine_rejection |
| Country | Settles | Refused by the bank (…9901) | Returned (…9902) |
|---|---|---|---|
| DE | DE89370400440532013000 | DE02370400440532019901 | DE72370400440532019902 |
| NL | NL91ABNA0417164300 | NL87ABNA0417169901 | NL60ABNA0417169902 |
| FI | FI9821421000005678 | FI5221421000009901 | FI2521421000009902 |
| SE | SE4550000000058398257466 | SE6650000000058398259901 | SE3950000000058398259902 |
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.
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).
POST /paysafe/paymenthub/v1/paymenthandles — transactionType PAYMENT (INITIATED, action: REDIRECT, a redirect_payment link valid 15 minutes), STANDALONE_CREDIT or VERIFICATION (PAYABLE at once, action: NONE); GET …/paymenthandles/{id}, GET …/paymenthandles?merchantRefNum=POST /paysafe/paymenthub/v1/payments with a PAYABLE handle's paymentHandleToken — COMPLETED; GET …/payments/{id}, ?merchantRefNum=; POST …/payments/{id}/settlements (paysafecard with settleWithAuth: false)POST /paysafe/paymenthub/v1/settlements/{id}/refunds — PENDING, then COMPLETED after 3 s (a payment settled with auth is its own settlement); GET …/refunds/{id}POST /paysafe/paymenthub/v1/standalonecredits — a withdrawal with a STANDALONE_CREDIT handle's token; GET …/standalonecredits/{id}POST /paysafe/paymenthub/v1/verifications — Neteller/Skrill account check: MATCH / NO_MATCH per field and verificationLevel 00/01/10/11GET /paysafe/paymenthub/v1/balances?paymentType=¤cyCode=/paysafe-lab/pay/{token} — the payer: paysafecard's panel (a PIN tab, 4×4 boxes, up to 10 PINs, and a my paysafecard tab), Skrill's and Neteller's login, 2-step verification and review; on a computer the phone with the brand's app beside the page, on a phone full-screen. ?device=mobile|desktop forces either/paysafe-lab/portal?merchant={username} — the stand-in for the Business Portal, where Paysafe merchants subscribe webhooks (Paysafe has no API for it): a page, and JSON at GET|PUT|PATCH /paysafe-lab/portal/settings ({mcc, businessName, webhooks:[{url, events?}]}; PATCH takes addWebhook / removeWebhook). The merchant is the Basic username or ?merchant=. MCC 7995 (the default) is a gambling merchantWebhooks, 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.
7740228278243675 holds 1,000.00. Lab PINs: six zeros, then the value in cents (0000 0000 0000 2500 holds 25.00; all zeros is used up). 9999 9999 9999 9999 is blocked. Any other PIN is invalid. Up to 10 PINs combine; balances are not used up.profile gives):
| Brand | Holder | What it shows | |
|---|---|---|---|
| PAYSAFECARD | psc.at@example.com | Anna Huber, 1990-04-12, AT | Austria, fully verified |
| PAYSAFECARD | psc.de@example.com | Lukas Schmidt, 1985-09-30, DE | Germany, fully verified, confirms in the my paysafecard app; payouts €30,000 a year |
| PAYSAFECARD | psc.uk@example.com | Oliver Smith, 1992-02-17, GB | UK Standard: payouts £250 a month |
| PAYSAFECARD | psc.simple@example.com | Mia Weber, 1995-07-01, AT | simple KYC only: refused where the merchant asks for FULL |
| PAYSAFECARD | psc.minor@example.com | Jonas Bauer, 2010-05-05, AT | 16 years old, unverified: refused by an age or KYC restriction |
| SKRILL | skrill.player@example.com | Maria Rossi, 1988-03-22, IT | verified |
| SKRILL | skrill.uk@example.com | James Brown, 1979-11-08, GB | verified, UK |
| SKRILL | skrill.unverified@example.com | Sofia Garcia, 1999-12-01, ES | unverified: pays at most €250 |
| SKRILL | skrill.blocked@example.com | Tomas Novak, 1983-06-14, CZ | gambling block on: no payments to gambling merchants (payouts still arrive) |
| NETELLER | neteller.player@example.com | Erik Larsen, 1986-01-30, NO | verified, with a verified card (level 11) |
| NETELLER | neteller.uk@example.com | Emily Clarke, 1991-08-19, GB | verified, no verified instrument (level 10) |
| NETELLER | neteller.unverified@example.com | Piotr Kowalski, 1997-04-03, PL | unverified (level 00): pays at most €250 |
| NETELLER | neteller.negative@example.com | Victor Lazlo, 1975-10-10, DE | in Paysafe's negative database: payments and payouts refused (4001) |
1001 with 4003 in details.kycLevelRestriction or minAgeRestriction, or a payer in Germany at a gambling merchant, takes accounts only (Germany since October 2020; Entain everywhere since September 2021).3195; no account 3162), only in Paysafe's 29 payout countries; Germany €30,000 a year, UK Standard £250 a month (Unlimited £24,000 a year), as running totals.3419): pay the player with a standalone credit. paysafecard refunds are full only (3418); a PIN payment's refund needs customerAccountId.| Amount (minor units) | Effect |
|---|---|
| …15, from 10.00 | a withdrawal is PENDING, then COMPLETED ten seconds later |
| …16, from 10.00 | a withdrawal is PENDING, then FAILED ten seconds later |
| …17, from 10.00 | a refund is PENDING, then FAILED |
| …24, from 10.00 | every webhook arrives 20 seconds late |
| …41, from 10.00 | no webhooks at all: only a GET tells the outcome |
| …53, from 10.00 | the handle create is refused: 500, 1000 internal error |
| …05, from 10.00 | the wallet's balance is short: Pay fails, the handle FAILED (3022 insufficient funds) |
| …06, from 10.00 | Paysafe's risk checks decline it: the handle FAILED (4002) |
| …07, from 10.00 | the handle create is refused: 502, 1001 External Gateway Error |
| …08, from 10.00 | the payer confirms too late: the handle EXPIRED |
| …09, from 10.00 | the 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.
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.
sightline: {consumerId}, the merchant's Loyalty Membership Number for the player (and optionally ssn, served back as last4ssn). The payer signs in to Play+ (any password), types the code Play+ texts to the phone drawn beside the page, and confirms. A Play+ account is enrolled under the consumerId by its first deposit; a payout to a consumerId never seen answers 1001 IM (Invalid Member Number). Refunds work as for the wallets.vippreferred: {consumerId} is the player's Social Security number (nine digits, served back as its last four only); profile (firstName, lastName, email, phone) and billingDetails (country, zip) are required. The payer confirms their date of birth and the SSN's last four, picks a registered bank account or links one (sign in to online banking with any username and password, then the code the bank texts), and authorises the eCheck. The payment echoes the bank account with its bank token (vippreferred.achBankAccounts[].paymentHandleToken); a payout names it as vippreferred.paymentHandleToken. POST …/verifications with a VERIFICATION handle lists a player's accounts. No refunds (3419): pay back with a standalone credit.playplus.player@example.com (New Jersey, $2,500 on the account, a linked bank tops up a short balance); playplus.low@example.com (Pennsylvania, $25 on the account and no linked bank: more is refused on the confirm page); playplus.suspended@example.com (account suspended: the payment fails (4001)); any other email is a fresh account holding $1,000.00.| VIP Preferred SSN | Player | Registered accounts (bank token) | What it shows |
|---|---|---|---|
111223333 | Morgan Lee, born 1984-06-15, NJ | JPMorgan Chase Bank, N.A. ••21 (BA9e4dnkCP3S3ZKQ)Wells Fargo Bank, N.A. ••07 ( BARQyFfooxlO5zGC) | registered, two bank accounts |
111224444 | Riley Grant, born 1990-12-03, PA | PNC Bank, N.A. ••55 (BA0WdHWAXlK4wxPk) | registered; the bank's balance check fails (3022, R01 insufficient funds) |
111225555 | Avery Quinn, born 1976-03-28, MI | Citibank, 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.
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).
ITAU (BRL up to 57,000), SANTANDER (BRL up to 57,000), BANCO_DO_BRASIL (BRL up to 57,000), BRADESCO (BRL up to 57,000)BANCO_ESTADO (CLP up to 8,500,000), BANCO_BCI (CLP up to 500,000), SCOTIABANK_CHILE (CLP up to 8,500,000)ONLINE_BANK_PAYMENT_BANCOLOMBIA_COLOMBIA (COP 1–40,000,000), BBVA_COLOMBIA (COP 1–40,000,000), BANCO_DE_BOGOTA (COP 1–40,000,000), BANCO_DAVIVIENDA (COP 1–40,000,000), NEQUI_COLOMBIA (COP 1–40,000,000), BANCO_CAJA_SOCIAL (COP 10,000–40,000,000)BANCO_NACIONAL_DE_COSTA_RICA (CRC up to 6,200,000)BANCO_DE_CREDITO (PEN up to 43,000), BBVA_CONTINENTAL (PEN up to 43,000), INTERBANK (PEN up to 43,000), SCOTIABANK (PEN up to 43,000), BCP_CUOTEALO (PEN up to 7,000)LOTERICAS (BRL up to 57,000), BRADESCO (BRL up to 5,000)BANCO_ESTADO (CLP up to 8,500,000), CAJA_VECINA (CLP up to 8,500,000), WALMART_CHILE (CLP up to 3,000,000), LIDER (CLP up to 3,000,000)EFECTY (COP up to 6,000,000), SURED (COP up to 4,000,000), GANA (COP up to 4,000,000), PUNTO_RED (COP up to 800,000)BANCO_NACIONAL_DE_COSTA_RICA (CRC 637–6,200,000)BANCO_DE_CREDITO (PEN up to 1,000), BBVA_CONTINENTAL (PEN up to 1,900), INTERBANK (PEN up to 700), WESTERN_UNION (PEN up to 43,000), TAMBO (PEN 1–500), KASNET (PEN 1–1,000)/paysafe-lab/pay/{token}/cashdesk (the lab's cash desk), linked from the code page and the drawer.details: 5010 21519 no country, 5068 21505 unknown country, 5001 21513 currency not of the country, 5068 21526 bank not in the country, 5003 10105 amount outside every offered bank's limits, 5068 21653 invalid profile.email.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.
1000 500 An internal error occurred.1001 502 External Gateway Error1007 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'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.
POST /trustly/api/1: Deposit (Pay N Play with RequestKYC: "1"), Refund, AccountPayout,
Withdraw, ApproveWithdrawal, DenyWithdrawal, GetWithdrawals (payouts, withdrawals, and a deposit's refunds),
Balance, AccountLedger/trustly/checkout?OrderID=…&SessionID=… — the payer: country and bank, the bank's login (SE Mobile BankID with a real QR code;
EE/LV/LT Smart-ID; DE login and pushTAN; elsewhere the bank's app), the account with its balance, confirm, sign, and back to
SuccessURL / FailURL. On a computer the payer's phone is drawn beside the page; ?device=mobile|desktop forces either flow.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.
Trustly's test checkout names these cases; what each does here is the lab's reading of it.
| Case | Name | Effect |
|---|---|---|
D1 | Credit, then debit | credit as usual, then 30 s later a debit notification: the bank took the deposit back |
D2 | Deprecated | deprecated by Trustly; the lab treats it as a normal deposit |
D3 | Pending, then cancel | pending once the payer signs, then the bank rejects the transfer: cancel after 10 s, no credit |
D4 | Delayed credit | pending once the payer signs; the credit arrives 60 s later (a bank without instant payments) |
D5 | Declined by Trustly | Trustly's risk check declines the order when the payer confirms: cancel, the payer goes to FailURL |
W1 | Payout returned | the withdrawal is confirmed, then returned by the receiving bank 20 s later: a credit notification gives the money back |
G1 | Late notifications | every notification arrives 20 s late |
G2 | Duplicate notifications | every notification is sent twice, with the same notificationid |
G3 | Hold notifications | nothing is sent until the lab drawer's notification tool releases the held notifications |
| Deposit amount | Effect |
|---|---|
| …13, from 10.00 | Trustly's risk engine suggests a lower amount (half) at confirm; the payer accepts it or cancels |
| …24, from 10.00 | every notification arrives 20 s late |
| …41, from 10.00 | pending once the payer signs, then the bank never confirms: cancel after 2 minutes |
| …53, from 10.00 | Deposit refused: HTTP 503, no JSON-RPC answer (Trustly unavailable) |
| …05, from 10.00 | the payer's bank refuses at signing, insufficient funds: cancel |
| …07, from 10.00 | Deposit refused: 620 ERROR_UNKNOWN |
| AccountPayout / Refund amount | Effect |
|---|---|
| …13, from 10.00 | payoutconfirmation, then 20 s later the receiving bank returns it: a credit notification |
| …17, from 10.00 | the payout fails before it is confirmed (FAILED): a credit notification gives the money back |
| …41, from 10.00 | EXECUTED for 2 minutes before the payoutconfirmation |
| …05, from 10.00 | AccountPayout (and Refund) answered result "0": not accepted |
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).
| Country | Currency | Login | Banks (limit per payment) |
|---|---|---|---|
| SE | SEK | Mobile BankID | Swedbank 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 |
| FI | EUR | the bank's app | OP 15,000, Nordea 10,000, Danske Bank 10,000, S-Pankki 5,000, Aktia 5,000 |
| EE | EUR | Smart-ID | Swedbank 15,000, SEB 15,000, LHV 10,000, Luminor 10,000, Citadele 5,000 |
| LV | EUR | Smart-ID | Swedbank 15,000, SEB 15,000, LHV 10,000, Luminor 10,000, Citadele 5,000 |
| LT | EUR | Smart-ID | Swedbank 15,000, SEB 15,000, LHV 10,000, Luminor 10,000, Citadele 5,000 |
| DE | EUR | login + pushTAN | Sparkasse 5,000, Deutsche Bank 10,000, Commerzbank 10,000, ING 10,000 |
| NL | EUR | the bank's app | ING 10,000, ABN AMRO 50,000, Rabobank 10,000 |
| DK | DKK | the bank's app | Danske Bank 50,000, Nordea 50,000, Jyske Bank 50,000 |
| PL | PLN | the bank's app | PKO BP 20,000, mBank 50,000, Pekao 20,000 |
| NO | NOK | the bank's app | DNB 100,000, SpareBank 1 100,000, Nordea 100,000 |
| GB | GBP | the bank's app | Barclays 25,000, HSBC 25,000, Lloyds 25,000, NatWest 50,000, Monzo 25,000 |
| CZ | CZK | the bank's app | Česká spořitelna 200,000, ČSOB 200,000, Komerční banka 200,000, Air Bank 200,000 |
| Code | Name | When | Source |
|---|---|---|---|
616 | ERROR_INVALID_CREDENTIALS | Username unknown or Password wrong | Trustly's |
620 | ERROR_UNKNOWN | Something went wrong at Trustly | Trustly's |
623 | ERROR_INVALID_PARAMETERS | A parameter is missing or malformed | Trustly's |
636 | ERROR_UNABLE_TO_VERIFY_RSA_SIGNATURE | The request's Signature does not verify | modeled |
637 | ERROR_INVALID_FUNCTION | The method is not one the lab serves | modeled |
638 | ERROR_MALFORMED_NOTIFICATIONURL | NotificationURL is not https, or carries a '?' | modeled |
639 | ERROR_DUPLICATE_MESSAGE_ID | An AccountPayout with this MessageID exists already: a payout is never made twice | modeled |
640 | ERROR_INVALID_AMOUNT | Amount is not digits with two decimals, or not above zero | modeled |
641 | ERROR_CURRENCY_NOT_SUPPORTED | Not one of SEK, EUR, DKK, NOK, PLN, GBP, CZK | modeled |
642 | ERROR_INVALID_ORDER_ID | No such order for this merchant | modeled |
643 | ERROR_ACCOUNT_NOT_FOUND | AccountID never came from this merchant's account notifications | modeled |
644 | ERROR_NOT_ALLOWED | The order is not in a state that allows this | modeled |
645 | ERROR_INSUFFICIENT_FUNDS | The merchant's Trustly balance cannot cover the payout | modeled |
646 | ERROR_REFUND_AMOUNT_TOO_HIGH | The refund exceeds what is left of the deposit | modeled |
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.
POST /circoflow/api/v1[/test]/payment/create — customer, billing address, amount, currency, merchant_transaction_id, card,
return_url, webhook_url, every one required. Answers {status, reason, merchant_transaction_id, transaction_id[, 3ds_url]}:
success, processing with a 3ds_url, pending, or a refusal (declined / blocked, nothing filed).
Validation failures are Circoflow's Laravel messages on a declined status ("The phone field is required."); a card number failing Luhn is HTTP 400 "Invalid card number."POST /circoflow/api/v1[/test]/payment/status {merchant_transaction_id} — {success: true, data: {transaction_id, merchant_transaction_id, status, amount, currency, message, customer_info, payment_info, created_at, processed_at}},
or 404 {success: false, error: "Transaction not found."} (also for a refused create-payment, and for __test_connection__)GET /circoflow/3ds/authenticate/{token} — the hosted 3-D Secure page: the card issuer's challenge. Confirmed, the payer goes back to return_url
(a GET, nothing in the URL) and the payment is pending ("Payment pending.") for 3 s, then success. Failed: declined "3ds_rejected"; cancelled: declined
"Payment attempt failed or cancelled"; opened and unanswered 10 minutes: declined "3ds_timeout". Never opened, it stays processing.webhook_url as {event: "payment.notify", data: {…}}, unsigned (Circoflow signs nothing; BabelPay reads the payment back).| Card | Brand | Effect |
|---|---|---|
4242 4242 4242 4242 | Visa | success "Payment completed." (frictionless) |
5555 5555 5555 4444 | Mastercard | success "Payment completed." (frictionless) |
4111 1111 1111 1111 | Visa | success "Payment completed." (frictionless) |
4000 0000 0000 3220 | Visa | processing "Payment authentication required." with a 3ds_url; authenticated: pending "Payment pending.", success 3 s later |
5200 8282 8282 8210 | Mastercard | processing with a 3ds_url; authenticated: pending, success 3 s later |
4000 0000 0000 0341 | Visa | processing with a 3ds_url; declined "3ds_rejected" however the challenge is answered |
4000 0000 0000 0077 | Visa | pending "Payment pending." with no 3ds_url; success 5 s later, by webhook |
4000 0000 0000 0002 | Visa | declined "Payment failed." |
4000 0000 0000 9995 | Visa | declined "Insufficient funds." |
4000 0000 0000 0069 | Visa | declined "Card expired." |
4000 0000 0000 0127 | Visa | declined "CVV verification failed." |
4100 0000 0000 0019 | Visa | declined "suspected_fraud" |
4000 0000 0000 0259 | Visa | declined "no appropriate payment routes found" |
4000 0000 0000 0051 | Visa | blocked "Max 2 declines reached for this card in 24 hrs. Please retry after 24 hours." (Circoflow's own velocity rule) |
4000 0000 0000 0119 | Visa | declined "Acquirer Side integration error, …" (a gateway fault) |
3782 8224 6310 005 | Amex | declined "Payment method not supported" |
An encrypted card (ev:…) is recognised by the BIN and last four BabelPay sends in X-Bistromathic-Card-Bin / -Last4.
| Amount | Effect |
|---|---|
| …21 | blocked "Max 2 declines reached for this card in 24 hrs. …" |
| …22 | declined "no appropriate payment routes found" |
| …23 | declined "mid_at_capacity" |
| …54 | declined "Card expired." |
| …59 | declined "suspected_fraud" |
| …77 | pending, then success 5 s later by webhook |
| …78 | pending, then declined "Declined by Issuer" 5 s later by webhook |
| …96 | HTTP 500 {"message":"Server Error"}; nothing is filed |
| …98 | 3DS challenge, then declined "3ds_rejected" however it is answered |
| …99 | 3DS challenge; authenticated: pending, success 3 s later |
| …02 | declined "Declined by Issuer" |
| …05 | declined "Insufficient funds." |
| …06 | declined in the acquirer's frame "P0002||Payment failed!|Insufficient funds||billaddress:…" |
| …07 | declined "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.
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.
POST /cc7995/api/v2/payments — create payment (§5.1.1): user_id, amount (major units, a JSON number), currency, success_url, fail_url, timeout (minutes, default 60), additional_data (≤ 100), beneficiary_url, … → {data:{payment_url, payment_id, user_id}}, the payment NEW; payment_url is the method web formPOST /cc7995/api/v2/payments/card — host-to-host bankcard payment (§5.1.2): payment_id, card_number, validity_month 1–12, validity_year 22–99, verification_code, cardholder_name (Latin, two words), payer_email, display_language → {data:{payment_url, …}}, the payment confirmation page. A second card on one payment: 203GET /cc7995/api/v2/payments?payment_ids=<id1>,<id2>… — status (§5.1.3), at most 20 ids, newest first; an unknown id is left out → {data:[…], total, response_date}/cc7995-lab/confirm/{token} — the payment confirmation page: Pay (two seconds), then the issuer's 3-D Secure challenge for a card that asks for one (the lab's shared issuer page), or Cancel payment; then back to success_url (COMPLETED) or fail_url (DECLINED). ?device=mobile|desktop forces either layout/cc7995-lab/pay/{token} — the method web form (a bank card form), which BabelPay never shows/cc7995-lab/admin?client_id={ClientId} — the stand-in for the platform administration, where a calling system's PaymentCallbackUrl is registered (CC7995 has no API for it): a page, and JSON at GET|PUT|PATCH /cc7995-lab/admin/settings ({paymentCallbackUrl, merchantName}; the ClientId is X-Api-ClientId or ?client_id=). A simulated BabelPay connection registers its ownPostbacks (§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).
Any future expiry, any CVV, a cardholder name of two Latin words.
| Card | Brand | What it does |
|---|---|---|
4242 4242 4242 4242 | VISA | COMPLETED, frictionless (the confirmation page only) |
5555 5555 5555 4444 | MASTERCARD | COMPLETED, frictionless |
4000 0000 0000 3220 | VISA | 3-D Secure challenge, then COMPLETED |
5200 8282 8282 8210 | MASTERCARD | 3-D Secure challenge, then COMPLETED |
4000 0000 0000 0341 | VISA | 3-D Secure challenge that fails however it is answered: DECLINED "User Authentication Failed" |
4000 0000 0000 0002 | VISA | DECLINED "Authorization declined 05 DECLINED" (do_not_honor) |
4000 0000 0000 9995 | VISA | DECLINED "Insufficient Funds" |
4000 0000 0000 0069 | VISA | DECLINED "transaction declined (wrong expiry date)" (expired_card) |
4000 0000 0000 0127 | VISA | DECLINED "invalid CVV/brand combination" |
4100 0000 0000 0019 | VISA | DECLINED "Blocked due to risk score" (risk_block) |
4000 0000 0000 0259 | VISA | DECLINED "Restricted card" |
4000 0000 0000 0119 | VISA | DECLINED "error response from connector/acquirer" (unable_to_authorize) |
4000 0000 0000 0051 | VISA | DECLINED "risk management transaction timeout" (unable_to_authorize) |
4000 0000 0000 6975 | VISA | DECLINED "transaction declined (maximum transaction frequency exceeded)" |
4000 0000 0000 4954 | VISA | DECLINED "transaction declined (suspecting manipulation)" (suspected_fraud) |
3782 8224 6310 005 | AMEX | Refused at the card payment: HTTP 400, error 209 Unsupported card (any card but Visa or Mastercard) |
| Amount (minor units) | Effect |
|---|---|
| …51 | DECLINED "Blocked due to risk score" |
| …98 | 3-D Secure challenge that fails: DECLINED "User Authentication Failed" |
| …99 | 3-D Secure challenge, then COMPLETED |
| …02 | DECLINED "Authorization declined 05 DECLINED" |
| …05 | DECLINED "Insufficient Funds" |
| anything else | COMPLETED, without a challenge |
212 at create payment. Any currency of three letters is taken (CC7995's sandbox channel is EUR only).204, expiry format or in the past 205, CVV 3–4 digits 206, cardholder name 207, email 208, any brand but Visa or Mastercard 209 (answered in the gateway's observed shape: the code as a string under code and error_code, the text under error, error_message and errors).timeout: "Transaction timed out" (the alarm; the drawer's Let it expire now does it at once).additional_data as one already COMPLETED: DECLINED "Duplicate transaction Duplicate transaction ID: <additional_data>".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.
1 Authentication failed or access denied5 X-Api-Signature verification failed6 Request header X-Api-RequestDate is out of date50 Beneficiary URL required51 Beneficiary URL is of incorrect format or invalid200 Creating payments not allowed for the specified ClientID201 Card payments not allowed for the specified ClientID202 Card payments not allowed for the recipient203 Payment with the specified payment_id already processed204 Card number (PAN) is of incorrect format or invalid205 Card expiration date is of incorrect format or invalid206 Card cvv/cvc2 is of incorrect format or invalid207 Cardholder name is of incorrect format or invalid208 Payer email is of incorrect format or invalid209 Unsupported card. Try another one210 URL not allowed211 Payer country not allowed212 Amount not allowed1000 General error
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.
POST /argent/tokenise/payment-instruments — {public_key, type: card, card_number, cvv, expiry_month, expiry_year}, JSON or form; 201 {id, type, bin, last_four}. A clear number must pass Luhn and the expiry must not be past, else 400 {"invalid-request": "The provided card details did not pass verification checks. This may be due to an invalid card number or expiry date."}. An ev: ciphertext is taken as it is and known by the X-Bistromathic-Card-Bin / -Last4 hint BabelPay sends beside it.POST /argent/payment-intents — immediate capture: 201 completed (with payment) or 201 failed (the processor's reason in latest_payment_attempt.error.technical_message), or 200 {requires_action: true, type, payment_intent_id, token, action_data} for 3-D Secure. GET /argent/payment-intents/{id}; GET /argent/payment-intents newest first, 20 a page, with an absolute next cursor URL on the lab's host.PATCH /argent/payment-intents/{id} — {public_key, token, action_data} with the one-time token: before the payer has finished, the same action again (no token); after the challenge, the payment is made and the intent comes back. A wrong or spent token: 400 {"token": ["Invalid token."]}.GET /argent/payments, /payments/{id} — balance, status paid / partially_refunded / refunded.POST /argent/refunds — {payment, amount?} (no amount: the whole balance), up to the balance; 201 RefundDetail and a refund_succeeded webhook. GET /argent/refunds, /refunds/{id}.GET|POST /argent/webhooks, GET|PATCH|DELETE /argent/webhooks/{id} — company must be the merchant's company id (the dashboard shows it). /balance-transactions, /disputes and /payouts list nothing./argent-lab/dashboard?key={secret key} (or ?merchant=) — the stand-in for Argent's dashboard, where its docs configure webhooks: the company and its name, the signing secret, webhook URLs, the latest intents; JSON at GET|PATCH /argent-lab/dashboard/settings ({company_name, add_webhook: {url}, remove_webhook}, merchant by Basic auth). A simulated BabelPay connection subscribes its callback URL there itself.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).
| Card | What it does | Processor | Source |
|---|---|---|---|
4242424242424242 | Payment succeeds | fake | Argent's Testing docs |
4000000000000002 | Payment fails: "Your card was declined." | stripe_standard | Argent's Testing docs |
4000000000003220 | 3-D Secure challenge: confirm to pay; fail or cancel it and the payment fails | lab_3ds | Argent's sandbox (observed) |
4000002500003155 | 3-D Secure challenge, as 4000000000003220 | lab_3ds | Argent's sandbox (observed) |
4000002760003184 | 3-D Secure challenge, as 4000000000003220 | lab_3ds | Argent's sandbox (observed) |
4000000000003055 | 3-D Secure frictionless: the issuer authenticates without a challenge and the payment succeeds | stripe_standard | Stripe's test number |
4000000000003253 | 3DS method (device data) step first, then the challenge | lab_3ds | the lab |
4000008400001629 | 3-D Secure challenge; once authenticated the payment fails: "Your card was declined." | lab_3ds | Stripe's test number |
4000008260003178 | 3-D Secure challenge; once authenticated the payment fails: "Your card has insufficient funds." | lab_3ds | Stripe's test number |
4000000000009995 | Payment fails: "Your card has insufficient funds." | stripe_standard | Stripe's test number |
4000000000000069 | Payment fails: "Your card has expired." | stripe_standard | Stripe's test number |
4000000000000127 | Payment fails: "Your card's security code is incorrect." | stripe_standard | Stripe's test number |
4000000000000119 | Payment fails: "An error occurred while processing your card. Try again in a little bit." | stripe_standard | Stripe's test number |
| any other valid number | Any other valid card number fails: "Your card was declined." | stripe_standard | Argent's Testing docs |
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.
{"currency_code": ["Unsupported currency: XXX"]} (Argent's own words; its sandbox company took USD only).reference up to 255 characters, descriptor up to 18 letters, digits and spaces, return_url up to 255. browser_info, when sent, needs all eight fields.merchant_initiated: true (Argent's retry calendar and MIT test cards), flow: hosted (the hosted payment page), Apple Pay and Google Pay instruments, and a PATCH replacing payment_instrument. Records are kept a week.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 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.
POST /happycoins/api/payment/url — {customer, order:{fiatAmount, fiatCurrency}, paymentRequestId, redirectUrl, paymentMethod} (fiat in MAJOR units) answers
{success:true, message:"OK", data:{paymentUrl, paymentReference, paymentRequestId}}; a refusal is 422 {"error":true,"message":"Reason: … (and N more errors)"}.
paymentRequestId (a string or a number) must be unique per merchant; merchant_id, merchant_channel_id and paymentMethodGrant are accepted and keptGET /happycoins/api/transaction/{paymentRequestId} — keyed by the merchant's id, not by paymentReference (that is a 404 {"success":false,"message":"Not Found"}):
{payment_reference, request_id, status, created_at, order_step, crypto_transactions:[{transaction_hash, crypto_currency, crypto_amount, crypto_conversion_rate, network_fee, transaction_type:"RECEIVE", status}], fiat_transaction:{fiat_amount, fiat_currency, fiat_conversion_rate, status, remarks}}/happycoins/api/… path: 404 {"uuid":…,"message":"RESOURCE_IN_REQUEST_NOT_FOUND"}. Outside /api (bare /happycoins/transaction/{id} too): a 200 HTML page, as HappyCoins' web root answers/happycoins-lab/checkout?p1=…&p2=… — the hosted checkout (the paymentUrl, single-use, opaque): order and crypto, identity verification, details, order summary and card
(3-D Secure through the issuer's page), the purchase, and the buyer's choice to pay the merchant or keep the crypto; then back to redirectUrl with nothing on itStatus 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.
| Card | Outcome |
|---|---|
4242 4242 4242 4242 | approved, no 3-D Secure (any other valid card number too) |
5555 5555 5555 4444 | approved, no 3-D Secure |
4000 0000 0000 3220 | 3-D Secure challenge; Confirm approves, Fail authentication is FAILED |
5200 8282 8282 8210 | 3-D Secure challenge, then approved |
4000 0000 0000 0341 | 3-D Secure challenge that fails however it is answered: FAILED (do_not_honor) |
4000 0000 0000 0002 | declined, do not honor: DECLINED (do_not_honor) |
4000 0000 0000 9995 | declined, insufficient funds: DECLINED |
4000 0000 0000 0119 | acquirer malfunction: ERROR (psp_malfunction) |
example.com, .test, .invalid, …), a residence in
US or GB, a fiat currency outside EUR, USD, GBP, CHF, SEK, NOK, DKK, PLN, CZK, CAD, AUD (MODELED list), a paymentMethod other than CARD, a repeated paymentRequestId.| Amount | Effect |
|---|---|
| …03 | Identity verification fails every attempt: the third ends the order KYC_ATTEMPT_EXCEEDED (risk_block) |
| …04 | HappyCoins rejects the order at the order summary, before the card: REJECTED (do_not_honor) |
| …06 | The card is charged, then buying the crypto fails: ERROR (psp_malfunction) |
| …07 | After 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 |
| …08 | Open 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'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.
POST /worldpay/jsp/merchant/xml/paymentService.jsp — submit/order with CARD-SSL, PAYWITHGOOGLE-SSL or
EMVCO_TOKEN-SSL (Apple Pay / Google Pay network tokens), additional3DSData for 3DS Flex, third-party MPI
info3DSecure, dynamic3DS, fundingTransfer (AFT, recorded); the 3DS second message
(completedAuthentication, or paResponse + echoData) with the machine cookie and the same session id;
modify/orderModification capture (partial), refund (partial, reference echoed in the journal), cancel, cancelOrRefund — each answered
<ok><…Received>; inquiry/orderInquiry — the payment and its journal (bookingDate, accountTx amounts).
Fast Access card payouts: submit/order with FF_DISBURSE-SSL (recipient/paymentInstrument/cardDetails, the recipient's
address, purposeOfPayment), answered VDFF_DISBURSE-SSL (Visa Direct) or MCS_DISBURSE-SSL (Mastercard Send) with
PUSH_APPROVED, PUSH_REFUSED, ERROR or PUSH_PENDING, schemeResponse (actionCode, responseCode, transactionIdentifier) and fastFunds.POST /worldpay/shopper/3ds/challenge.html — 3DS Flex step-up: JWT (HS256/HS512) + MD; on to the issuer's ACS, then back to
the JWT's ReturnUrl with TransactionId, Response (Cardinal's JWT) and MD. A JWT signed with neither published key, or
with a claim Cardinal does not permit, is an empty 400. POST /worldpay/shopper/3ds/ddc.html — device data collection (JWT, Bin):
posts profile.completed with a SessionId to the parent page./worldpay-lab/acs/{token} — the issuer (the shared ACS page) for 3DS Flex (creq) and legacy 3DS (PaReq, TermUrl, MD; back with PaRes)./worldpay-lab/mai?merchant={code} — the stand-in for the Merchant Admin Interface: where order notifications go, the name the issuer's page shows, the latest
orders with their journals and the modifications WPG acknowledged and ignored. JSON at GET|PUT|PATCH /worldpay-lab/mai/settings
({notificationUrl, merchantName}; merchant = Basic username or ?merchant=). A connection in simulated flow sets both itself.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.
AUTHORISED, REFUSED (ISO 5), REFUSED<n> (ISO n; REFUSEDR1/R3 = 973/975), ERROR.
ISO wording: 4 HOLD CARD · 5 REFUSED · 8 APPROVE AFTER IDENTIFICATION · 13 INVALID AMOUNT · 15 INVALID CARD ISSUER · 17 ANNULATION BY CLIENT · 28 ACCESS DENIED · 29 IMPOSSIBLE REFERENCE NUMBER · 33 CARD EXPIRED · 34 FRAUD SUSPICION · 38 SECURITY CODE EXPIRED · 41 LOST CARD · 43 STOLEN CARD, PICK UP · 51 LIMIT EXCEEDED · 55 INVALID SECURITY CODE · 56 UNKNOWN CARD · 57 ILLEGAL TRANSACTION · 62 RESTRICTED CARD · 63 SECURITY RULES VIOLATED · 75 SECURITY CODE INVALID · 76 CARD BLOCKED · 85 REJECTED BY CARD ISSUER · 973 Revocation of authorization order · 975 Revocation of all authorizations order. Any other name authorises.3D — legacy 3DS: request3DSecure (paRequest, issuerURL, echoData), the issuer's page, PaRes; 3D.REFUSED13 then refuses. NO3D (and NO3D.<value>) — "Authentication Offered but not Used" (Amex: "Ecommerce").additional3DSData; without it they authorise without 3DS):3DS_V2_FRICTIONLESS_IDENTIFIED | Frictionless, cardholder authenticated |
3DS_V2_FRICTIONLESS_NOT_IDENTIFIED | Frictionless attempted: offered but not used |
3DS_V2_FRICTIONLESS_REJECTED | Frictionless rejected: REFUSED, not sent for authorisation |
3DS_V2_FRICTIONLESS_VALID_ERROR | Frictionless, authentication unavailable |
3DS_V2_CHALLENGE_IDENTIFIED | Challenge, cardholder authenticated |
3DS_V2_CHALLENGE_VALID_ERROR | Challenge, then authentication unavailable |
3DS_V2_CHALLENGE_UNKNOWN_IDENTITY | Challenge, authentication attempted but failed: REFUSED |
3DS_BYPASSED | 3DS bypassed |
3DS_V2_BYPASSED_AFTER_CHALLENGE | Challenge, then bypassed |
3DS_V1_CHALLENGE_IDENTIFIED | 3DS1 challenge, cardholder authenticated |
3DS_V1_CHALLENGE_UNKNOWN_IDENTITY | 3DS1 challenge, authentication failed: REFUSED |
additional3DSData), the name then steering the authorisation:
| # | Case | Visa | Mastercard | Amex |
|---|---|---|---|---|
| 1 | Successful frictionless | 4000000000001000 | 5200000000001005 | 340000000001007 |
| 2 | Failed frictionless: REFUSED | 4000000000001018 | 5200000000001013 | 340000000001015 |
| 3 | Attempts stand-in | 4000000000001026 | 5200000000001021 | 340000000001023 |
| 4 | Issuer unavailable | 4000000000001034 | 5200000000001039 | 340000000001031 |
| 5 | Issuer rejected: REFUSED | 4000000000001042 | 5200000000001047 | 340000000001049 |
| 6 | Not available on lookup | 4000000000001059 | 5200000000001054 | 340000000001056 |
| 7 | Error on lookup | 4000000000001067 | 5200000000001062 | 340000000001064 |
| 8 | Lookup timeout | 4000000000001075 | 5200000000001070 | 340000000001072 |
| 9 | Bypassed authentication | 4000000000001083 | 5200000000001088 | 340000000001080 |
| 10 | Successful step-up (challenge) | 4000000000001091 | 5200000000001096 | 340000000001098 |
| 11 | Failed step-up (challenge): REFUSED | 4000000000001109 | 5200000000001104 | 340000000001106 |
| 12 | Step-up unavailable (challenge) | 4000000000001117 | 5200000000001112 | 340000000001114 |
| 13 | Error on authentication (challenge) | 4000000000001125 | 5200000000001120 | 340000000001122 |
4444333322221111 Visa (VISA-SSL), 4917610000000000 Visa Debit (VISA_DEBIT-SSL), 4462030000000000 Visa Debit (VISA_DEBIT-SSL), 5555555555554444 Mastercard (ECMC-SSL), 5454545454545454 Mastercard (ECMC-SSL), 2221000000000009 Mastercard (2-series) (ECMC-SSL), 5163613613613613 Mastercard Debit (ECMC_DEBIT-SSL), 6759649826438453 Maestro (MAESTRO-SSL), 343434343434343 American Express (AMEX-SSL), 4562686000117661 Cartes Bancaires (CB-SSL), 36700102000000 Diners (DINERS-SSL), 36148900647913 Diners (DINERS-SSL), 6011000400000000 Discover (DISCOVER-SSL), 3528000700000000 JCB (JCB-SSL), 6221260000000000 UnionPay (CHINAUNIONPAY-SSL), 122000000000003 Airplus (AIRPLUS-SSL). Any other clear card number must pass Luhn (error 7 otherwise); an expired card is error 7 Expiry date = MM/YYYY.CVCResultCode): 111/222/333 not sent / no response / not checked, 444 FAILED, 555 APPROVED (Amex 1111…6666), blank NOT SUPPLIED BY SHOPPER.
AVS from the postal code: AAAAA APPROVED … JJJJJ FAILED … MMMMM UNKNOWN.description (BabelPay sends Payment <merchant reference>; the lab reads its last word).
Network tokens: the token's cardHolderName or the description.cardHolderName: APPROVED | PUSH_APPROVED, action code 00 |
REFUSED | PUSH_REFUSED, action code 01 |
REFUSED14 | PUSH_REFUSED, action code 14 (invalid account number) |
REFUSED54 | PUSH_REFUSED, action code 54 (expired card) |
REFUSED57 | PUSH_REFUSED, action code 57 (not permitted to cardholder) |
REFUSED61 | PUSH_REFUSED, action code 61 (exceeds amount limit) |
REFUSED62 | PUSH_REFUSED, action code 62 (restricted card) |
PENDINGAPPROVED | PUSH_PENDING, then PUSH_APPROVED 15 s later, with an order notification |
PENDINGREFUSED | PUSH_PENDING, then PUSH_REFUSED (action code 05) 15 s later, with an order notification |
ERROR | ERROR |
REFUSED<nn> refuses with any other two-digit action code too (the lab's generalisation). Any other name is approved
(Worldpay's token-based test flow answers ERROR; the lab approves, so a payout to a real-looking name just works).4917300800000000 Visa credit, GB, GBP: no fast funds, 4462030000000000 Visa credit, NL, EUR: no fast funds, 4484070000000000 Visa credit, AU, AUD: no fast funds, 4485602020200027 Visa credit, HK, HKD: no fast funds, 4499232020200027 Visa credit, SG, SGD: no fast funds, 4000765339615288 Visa credit, US, USD: no fast funds, 4000765339615296 Visa credit, US, USD: no fast funds, 5551593000060026 Mastercard credit, GB, GBP: no fast funds, 5555555555554451 Mastercard credit, GB, GBP: no fast funds, 5555555555554477 Mastercard credit, NL, EUR: no fast funds, 5555555555554493 Mastercard credit, AU, AUD: no fast funds, 5454545454545751 Mastercard credit, issuer country unknown: no fast funds, 4895070000006687 "Invalid billing card currency code received from Visa.", 4895070000007685 "Invalid issuer country code received from Visa."; any other card has fast funds.dynamic3DS do3DS or challengePreference
challengeRequested/Mandated asks for a challenge; no3DS skips 3DS.| …51, from 10.00 | REFUSED, ISO 51 LIMIT EXCEEDED |
| …99, from 10.00 | Google Pay with 3DS data (a card saved in the Google account): a 3DS Flex challenge |
| …05, from 10.00 | REFUSED, ISO 5 REFUSED |
0 / DEFAULT: CAPTURED 10 s after AUTHORISED (WPG: minutes to an hour); 1–14: after that many days; OFF: never.
A refund is SENT_FOR_REFUND, then REFUNDED 10 s later.<ok> and ignored.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'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.
TransTypeID 0 Purchase — approved (0), declined (bank codes below 500), refused by the gateway (field codes 500–556, 568, 597; TransTypeID 0, TransactionID "0"), or 600 Redirect required with a RedirectURL3 Capture and 4 Cancel — an open authorisation, in full (560–562, 589 when MerchantRef is not the original's)5 Refund — a sale or a captured authorisation, by the purchase's or the capture's TransactionID; Amount optional (the rest); 563, 564, 629, 6307 Recurring — re-bills an approved purchase without the card; 621 on a 3D MID, 622 without an approved initial purchase8 Status — the latest purchase with that MerchantRef: TransTypeID 6 + 999 Transaction Pending during a redirect, 99 + 999 MerchantRef not found, or the purchase's own answer/citigate/redirect/processing.aspx?{token} — the RedirectURL: Citigate's "Transaction in progress", then the issuer's challenge (/citigate-lab/acs, the lab's shared 3-D Secure page) when the card asks for one, then "Your transaction is being finalized" (/citigate/redirect/complete.aspx), which POSTs the callback, waits for its answer, and returns the payer to SuccessURL (approved) or FailURL with the same fields as a form. &device=mobile|desktop forces either layoutCallback 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.
Dummy1 (non-3D MID, authorisations, the amount decides: USD/EUR up to 999.99 approves, 1000.00 or more declines with 5) and Dummy2 (3D MID, sales, every purchase redirects; the challenge decides: confirm → 0, fail → 103, cancel → 106). Password p@s5w0Rd123, not checked.simulated-<connection id>) is the lab's MID: authorisations, the same USD/EUR amount rule, then the cards below; any other Luhn-valid card approves with no redirect. Partial refunds are on.| Card | Brand | 3-D Secure | What happens (lab's MID) |
|---|---|---|---|
4111111111111111 | VISA | none | Approved, no redirect (Citigate's own example card; so is any other Luhn-valid card) |
5555555555554444 | MASTERCARD | none | Approved, no redirect |
378282246310005 | AMEX | none | Approved, no redirect |
4000000000003220 | VISA | challenge | 600 Redirect required; the issuer's 3-D Secure challenge, then approved |
5200828282828210 | MASTERCARD | challenge | 600 Redirect required; the issuer's challenge, then approved |
4000000000003253 | VISA | challenge | 600 Redirect required; the challenge, then the bank declines: 51 Not sufficient funds |
4000000000003063 | VISA | frictionless | 600 Redirect required; frictionless 3-D Secure (no challenge), then approved |
4000000000000341 | VISA | frictionless | 600 Redirect required; the issuer refuses without a challenge: 103 3D authentication failure |
4000000000000002 | VISA | none | Declined: 5 Do not honour |
4000000000009995 | VISA | none | Declined: 51 Not sufficient funds |
4000000000000069 | VISA | none | Declined: 54 Expired card |
4000000000000127 | VISA | none | Declined: 79 Invalid CVV2/CVC2 |
4100000000000019 | VISA | none | Declined: 59 Suspected fraud |
4000000000009235 | VISA | none | Declined: 102 General risk decline |
4000000000000259 | VISA | none | Declined: 91 Issuer or switch is inoperative |
4000000000000101 | VISA | none | Declined: 105 Timeout at acquirer |
4000000000000119 | VISA | none | Declined: 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.
Country=US StateProvince, Telephone and DateOfBirth (525/531/533).SuccessURL, FailURL, CallbackURL (584–586) whenever the purchase redirects.597: a successful purchase with the same card, MID, currency and amount in the last 30 seconds.700), 15 more to come back (800), each with the signed callback; open authorisations auto-captured after 48 hours.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).
0 Approved1 Refer to Card Issuer2 Refer to Card Issuer's Special Condition3 Invalid Merchant4 Pick-up5 Do not honour12 Invalid transaction13 Invalid amount14 Invalid card number15 No such issuer19 Re-enter transaction20 Invalid response21 No Transaction30 Format error31 Bank not supported33 Expired card, pick-up34 Issuer suspects fraud, pick-up35 Contact acquirer, pick-up36 Restricted card, pick-up39 No credit account41 Lost card, pick-up43 Stolen card, pickup50 Maintenance in progress51 Not sufficient funds54 Expired card56 No card record57 Transaction not permitted to cardholder58 Transaction not permitted to terminal59 Suspected fraud61 Exceeds withdrawal amount limit62 Restricted card63 Security violation65 Exceeds withdrawal frequency78 Invalid account specified79 Invalid CVV2/CVC291 Issuer or switch is inoperative92 Destination cannot be found for routing93 Transaction cannot be completed; violation of law94 Duplicate transaction96 Acquirer system malfunction101 Unable to authorize102 General risk decline103 3D authentication failure104 Acquirer limits reached105 Timeout at acquirer106 User aborted transaction107 Missing or invalid client information108 AVS Response109 Risk Decline - 3rd Party110 Risk Decline - 3rd Party500 TransTypeID not supplied or invalid501 MerchantReference not supplied502 MerchantReference invalid503 Currency not supplied504 Currency invalid505 Amount not supplied506 Amount invalid507 MerchantName not supplied508 MerchantName invalid509 MerchantPassword not supplied510 MerchantPassword invalid511 Brand not supplied512 Brand invalid513 Firstname not supplied514 Firstname invalid515 Surname not supplied516 Surname invalid517 StreetLine1 not supplied518 StreetLine1 invalid519 StreetLine2 not supplied520 StreetLine2 invalid521 City not supplied522 City invalid523 PostalCode not supplied524 PostalCode invalid525 StateProvince not supplied526 StateProvince invalid527 Country not supplied528 Country invalid529 Email not supplied530 Email invalid531 Telephone not supplied532 Telephone invalid533 DateOfBirth not supplied534 DateOfBirth invalid535 UserIP not supplied536 UserIP invalid537 CardholderName not supplied538 CardholderName invalid539 CardNo not supplied540 CardNo invalid541 ExpiryYear not supplied542 ExpiryYear invalid543 ExpiryMonth not supplied544 ExpiryMonth invalid545 CVV not supplied546 CVV invalid553 Failed Luhn check554 Card expired555 TransactionID not supplied556 TransactionID invalid557 Invalid MerchantName and MerchantPassword combination559 Error looking up original transaction info560 No corresponding successful authorisation561 Transaction has already been captured562 Transaction has already been cancelled563 Transaction has already been refunded564 No corresponding successful sale or capture567 Unknown Brand568 Unknown Currency573 TransTypeID not supported on current MID584 SuccessURL invalid585 FailURL invalid586 CallbackURL invalid589 Invalid reference combination590 Invalid payload received597 Duplicate transaction (successful purchase in last 30 secs with same card, MID, currency and amount)600 Redirect required621 Recurring transactions not possible with 3D MIDs622 No corresponding purchase or capture629 Partial refund not supported630 Partial refund amount exceeds available amount700 No user redirect (timeout)800 No user return (timeout)999 Transaction Pending
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.
POST /blueemi/payments — a card source: 201 Authorized or Declined (response_code, response_summary), or with
3ds.enabled and a card with a 3DS profile 202 Pending with _links.redirect. capture (default true) captures behind the authorization.
Amount 0 is a card verification. Authentication done elsewhere (3ds.eci + cryptogram) is authorized as sent. 422 {request_id, error_type: "request_invalid", error_codes}.GET /blueemi/payments/{pay_…|sid_…} — the payment; actions (latest first) only when looked up by the cko-session-id, which expires after 15 minutes.
GET /blueemi/payments?reference=, GET /blueemi/payments/{id}/actions. Unknown: 404, no body.POST /blueemi/payments/{id}/captures (partial; capture_type Final closes the rest) · /voids (before the capture) · /refunds (up to what was captured) —
202 {action_id, reference, _links}; 403 with no body when the action is not allowed; 422 capture_value_greater_than_authorized, void_amount_invalid, amount_exceeds_balance./blueemi-lab/3ds/{token} — the hosted 3DS page: two seconds of device data, then frictionless or the issuer's challenge (Confirm / Cancel; "Fail authentication" in the drawer),
then back to success_url or failure_url with ?cko-session-id=sid_… (303). The drawer also lets the session expire. ?device=mobile|desktop forces either.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.
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.
| Card number | Scheme | What it does |
|---|---|---|
4543474002249996 | Visa, US | Approved (the card in Checkout's token example) |
4000020000000000 | Visa, US | Approved |
4242424242424242 | Visa, GB | Approved; amount 101 (1.01) declines 20051 Insufficient funds |
4273149019799094 | Visa, TR | Card verifications only: approves, but captures and voids are declined |
4539467987109256 | Visa, ES | Declined 20005 Do not honour |
4024007181869214 | Visa, US | Declined 20005 Do not honour |
4916301720257093 | Visa, US | Declined 20020 Invalid response |
4485899805156040 | Visa, US | Declined 200R1 Stop payment for this authorization |
4659105569051157 | Visa, GB | Approved (debit) |
4095254802642505 | Visa, US | Declined 20068 Response received too late / timeout (debit) |
4000148147058142 | Visa | Declined 20020 Invalid response (prepaid) |
5436031030606378 | Mastercard, MU | Approved |
5585076576791786 | Mastercard, HU | Declined 20005 Do not honour |
5305484748800098 | Mastercard, DE | Approved (debit) |
341829238058580 | American Express, US | Declined 20005 Do not honour |
6011111111111117 | Discover, US | Approved |
6011111111111118 | Discover, US | Declined 20005 Do not honour |
6011201559638046790 | Discover, US | Declined 20005 Do not honour |
6445979375892355607 | Discover, US | Approved |
36160940933914 | Diners Club International, KR | Approved |
36160912071610 | Diners Club International, KR | Declined 20005 Do not honour |
3582069482684995 | JCB, JP | Approved |
3528257085901927 | JCB, JP | Declined 20005 Do not honour |
3566027046765007 | JCB, JP | Approved |
4276038578596818 | Visa, US | Declined 2005C Transaction not supported / blocked by issuer |
4152812521833588 | Visa, US | Declined 2009G Blocked by cardholder |
4734868958733862 | Visa, US | Declined 200N7 Decline for CVV2 failure |
4567361325981788 | Visa, GB | Declined 200R3 Stop payment for all authorizations |
4111111111111129 | Visa, US | Declined 20001 Refer to card issuer |
4024007103573027 | Visa, US | Declined 20012 Invalid transaction |
4158596042924182 | Visa, GB | Declined 20012 Invalid transaction |
4485381577182090 | Visa, US | Declined 20014 Invalid card number |
4544249167673670 | Visa, GB | Declined 20051 Insufficient funds |
4659465888705671 | Visa, GB | An account funding transaction declines 20057 Not permitted to cardholder; anything else approves |
4151512736305999 | Visa, GB | Declined 20057 Transaction not permitted to cardholder |
4897453568485113 | Visa, US | Declined 20059 Suspected fraud |
4000144348235050 | Visa | Declined 20059 Suspected fraud (prepaid) |
4556294593757189 | Visa, UY | Declined 20061 Activity amount limit exceeded |
4818924250131070 | Visa, GB | Declined 20062 Restricted card |
4556253752712245 | Visa, US | Declined 20063 Security violation |
4532819413487978 | Visa, US | Declined 20078 Blocked, first use |
4732789363145785 | Visa, PR | Declined 20082 No security model |
4000140542610783 | Visa | Declined 20082 No security model (prepaid) |
4500622868341387 | Visa, CA | Declined 20154 3DS authentication required |
4539257838438511 | Visa, IE | Declined 20154 3DS authentication required |
4941208378238219 | Visa, GB | Declined 20154 3DS authentication required |
4024007162185267 | Visa, US | Declined 30015 No such issuer |
4655710037275959 | Visa, GB | Declined 30015 No such issuer |
4941202060999329 | Visa, GB | Declined 30041 Lost card - pick up |
4658620059821613 | Visa, GB | Declined 30041 Lost card - pick up |
4539253655711767 | Visa, IE | Declined 30043 Stolen card - pick up |
4745820750643143 | Visa, GB | Declined 30043 Stolen card - pick up |
4000140543766022 | Visa | Declined 20109 Transaction already reversed |
5510696303117979 | Mastercard | Declined 20003 Invalid merchant |
5148447461737269 | Mastercard, US | Declined 20012 Invalid transaction |
5510696303117987 | Mastercard | Declined 20030 Format error |
5437821135399682 | Mastercard, TW | Declined 20051 Insufficient funds |
5355224542121849 | Mastercard, GB | An account funding transaction declines 20057 Not permitted to cardholder; anything else approves |
5355222710253469 | Mastercard, GB | Declined 20057 Transaction not permitted to cardholder |
5420951756276171 | Mastercard, TN | Declined 20061 Activity amount limit exceeded |
5279988405398834 | Mastercard, JP | Declined 20062 Restricted card |
5265162270587964 | Mastercard, US | Declined 20068 Response received too late / timeout |
5525192142230028 | Mastercard | Declined 20072 Account not yet activated |
5347965091482021 | Mastercard, BR | Declined 20182 Policy |
5357219827207436 | Mastercard, BG | Declined 20183 Security |
5363450180402239 | Mastercard, PH | Declined 30041 Lost card - pick up |
5355222710255852 | Mastercard, GB | Declined 30043 Stolen card - pick up |
5525192142230002 | Mastercard | Declined 30046 Closed account |
5355222710255878 | Mastercard, GB | Declined 30046 Closed account |
5333170000000008 | Mastercard, IT | Approved up to 100.00; more declines 20051 Insufficient funds |
5318773000000008 | Mastercard, US | Approved up to 100.00; more declines 20051 Insufficient funds |
4644968546281686 | Visa, GB | Verifies (amount 0); an authorization declines 20012 |
5355228287185489 | Mastercard, GB | Verifies (amount 0); an authorization declines 20012 |
4546381219393284 | Visa, GB | Verifies (amount 0); an authorization declines 20051 |
5355229757805879 | Mastercard, GB | Verifies (amount 0); an authorization declines 20051 |
4325350188287824 | Visa, GB | Verifies (amount 0); an authorization declines 20051 |
4543611423334994 | Visa, GB | Verifies (amount 0); an authorization declines 20061 |
5355222589678168 | Mastercard, GB | Verifies (amount 0); an authorization declines 20061 |
4394666298116207 | Visa, GB | Verifies (amount 0); an authorization declines 20061 |
4857662623668665 | Visa, GB | Verifies (amount 0); an authorization declines 20062 |
5355225201525238 | Mastercard, GB | Verifies (amount 0); an authorization declines 20062 |
4021277034700287 | Visa, GB | Verifies (amount 0); an authorization declines 20062 |
4407108123937239 | Visa, GB | Verifies (amount 0); an authorization declines 20063 |
5355226042898974 | Mastercard, GB | Verifies (amount 0); an authorization declines 20063 |
3530111333300000 | JCB, JP | Not 3DS-enabled: 20150 with 3DS; approves without (CVV 100) |
5352151570003404 | Mastercard, FR | Not 3DS-enabled: 20150 with 3DS; approves without (CVV 100) |
4532432452900131 | Visa, DE | Not 3DS-enabled: 20150 with 3DS; approves without (CVV 257) |
378282246310005 | American Express, US | 3DS: card not enrolled (attempt_n3d: authorized without 3DS) |
5132562600000045 | Cartes Bancaires, FR | 3DS: card not enrolled (attempt_n3d: authorized without 3DS) |
4022050100000182 | Cartes Bancaires, FR | 3DS: card not enrolled (attempt_n3d: authorized without 3DS) |
4484070000035519 | Visa, GB | 3DS: card not enrolled (attempt_n3d: authorized without 3DS) |
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.
| Card number | Scheme | What it does |
|---|---|---|
345678901234564 | American Express, ES | 3DS frictionless: authentication successful |
5137210000000018 | Cartes Bancaires, FR | 3DS frictionless: authentication successful |
4010056200000018 | Cartes Bancaires, FR | 3DS frictionless: authentication successful |
5588686116426417 | Mastercard, CN | 3DS frictionless: authentication successful |
6224120000000094 | UnionPay, CN | 3DS frictionless: authentication successful |
4485040371536584 | Visa, US | 3DS frictionless: authentication successful |
371064645462927 | American Express, US | 3DS frictionless: not authenticated |
5132562600000029 | Cartes Bancaires, FR | 3DS frictionless: not authenticated |
4022050100000000 | Cartes Bancaires, FR | 3DS frictionless: not authenticated |
5309961755464047 | Mastercard, BR | 3DS frictionless: not authenticated |
6224120000000086 | UnionPay, CN | 3DS frictionless: not authenticated |
4539628347117863 | Visa, AD | 3DS frictionless: not authenticated |
344282679519362 | American Express, US | 3DS frictionless: authentication could not be performed |
5132562600000011 | Cartes Bancaires, FR | 3DS frictionless: authentication could not be performed |
4022050100000018 | Cartes Bancaires, FR | 3DS frictionless: authentication could not be performed |
5234106378657904 | Mastercard, KR | 3DS frictionless: authentication could not be performed |
6224120000000078 | UnionPay, CN | 3DS frictionless: authentication could not be performed |
4024007186645015 | Visa, US | 3DS frictionless: authentication could not be performed |
4022050100000026 | Cartes Bancaires, FR | 3DS frictionless: attempted authentication |
5558468902774508 | Mastercard, KE | 3DS frictionless: attempted authentication |
6224120000000029 | UnionPay, CN | 3DS frictionless: attempted authentication |
4556574722325580 | Visa, PT | 3DS frictionless: attempted authentication |
343397288296672 | American Express, US | 3DS frictionless: authentication rejected |
5596061690670931 | Mastercard, IN | 3DS frictionless: authentication rejected |
6224120000000011 | UnionPay, CN | 3DS frictionless: authentication rejected |
4275765574319271 | Visa, JP | 3DS frictionless: authentication rejected |
378702153281139 | American Express, US | 3DS frictionless: error during scheme communication |
5132562600000060 | Cartes Bancaires, FR | 3DS frictionless: error during scheme communication |
4022050100000133 | Cartes Bancaires, FR | 3DS frictionless: error during scheme communication |
5291144083573579 | Mastercard, US | 3DS frictionless: error during scheme communication |
4452927588210665 | Visa, US | 3DS frictionless: error during scheme communication |
340282533032846 | American Express, US | 3DS frictionless: authentication successful (no 3DS method URL) |
36747880216043 | Diners Club International, US | 3DS frictionless: authentication successful (no 3DS method URL) |
3528982710432481 | JCB, JP | 3DS frictionless: authentication successful (no 3DS method URL) |
5121073611487018 | Mastercard, US | 3DS frictionless: authentication successful (no 3DS method URL) |
6224120000000110 | UnionPay, CN | 3DS frictionless: authentication successful (no 3DS method URL) |
4556085951507152 | Visa, ES | 3DS frictionless: authentication successful (no 3DS method URL) |
| Card number | Scheme | What it does |
|---|---|---|
372688581899681 | American Express, US | 3DS challenge: authentication successful |
5137210000000158 | Cartes Bancaires, FR | 3DS challenge: authentication successful |
4010061700000021 | Cartes Bancaires, FR | 3DS challenge: authentication successful |
5385308360135181 | Mastercard, US | 3DS challenge: authentication successful |
6224120000000003 | UnionPay, CN | 3DS challenge: authentication successful |
346126139326850 | American Express, US | 3DS challenge: attempted authentication |
5341034800000008 | Cartes Bancaires, FR | 3DS challenge: attempted authentication |
4150561000000001 | Cartes Bancaires, FR | 3DS challenge: attempted authentication |
5518832400606463 | Mastercard, US | 3DS challenge: attempted authentication |
6224120000000060 | UnionPay, CN | 3DS challenge: attempted authentication |
4152868552773614 | Visa, BH | 3DS challenge: attempted authentication |
375982239796002 | American Express, US | 3DS challenge: authentication rejected |
5341034800000016 | Cartes Bancaires, FR | 3DS challenge: authentication rejected |
6224120000000052 | UnionPay, CN | 3DS challenge: authentication rejected |
4150561000000019 | Cartes Bancaires, FR | 3DS challenge: authentication rejected |
347411540178242 | American Express, US | 3DS challenge: not authenticated |
5341034800000024 | Cartes Bancaires, FR | 3DS challenge: not authenticated |
4150561000000027 | Cartes Bancaires, FR | 3DS challenge: not authenticated |
5258901507741160 | Mastercard, CA | 3DS challenge: not authenticated |
6224120000000045 | UnionPay, CN | 3DS challenge: not authenticated |
4243754271700719 | Visa, US | 3DS challenge: not authenticated |
377990956548835 | American Express, US | 3DS challenge: authentication could not be performed |
5341034800000032 | Cartes Bancaires, FR | 3DS challenge: authentication could not be performed |
4150561000000035 | Cartes Bancaires, FR | 3DS challenge: authentication could not be performed |
5569757734785691 | Mastercard, US | 3DS challenge: authentication could not be performed |
6224120000000037 | UnionPay, CN | 3DS challenge: authentication could not be performed |
4447336775378848 | Visa, US | 3DS challenge: authentication could not be performed |
5341034800000040 | Cartes Bancaires, FR | 3DS challenge: error during scheme communication |
4150561000000043 | Cartes Bancaires, FR | 3DS challenge: error during scheme communication |
5572408422189334 | Mastercard, IE | 3DS challenge: error during scheme communication |
4539929207120660 | Visa, IN | 3DS challenge: error during scheme communication |
372310461284948 | American Express, US | 3DS challenge: authentication successful (no 3DS method URL) |
5341034800000057 | Cartes Bancaires, FR | 3DS challenge: authentication successful (no 3DS method URL) |
4150561000000183 | Cartes Bancaires, FR | 3DS challenge: authentication successful (no 3DS method URL) |
36138956596288 | Diners Club International, AT | 3DS challenge: authentication successful (no 3DS method URL) |
3528682570029332 | JCB, JP | 3DS challenge: authentication successful (no 3DS method URL) |
5259410220714099 | Mastercard, US | 3DS challenge: authentication successful (no 3DS method URL) |
6224120000000128 | UnionPay, CN | 3DS challenge: authentication successful (no 3DS method URL) |
4485597929486000 | Visa, US | 3DS challenge: authentication successful (no 3DS method URL) |
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 authorization2013 → 200R3 Issuer initiated a stop payment (revocation order) for all authorizations4001 → 40101 Risk Blocked Transaction4008 → 40108 Gateway Reject - Post code failed9998 → 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'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.
POST /paynl/v1/orders — Order:Create: status 20, links.redirect to the hosted checkout; serviceId (SL-####-####) and amount.value (minor units) are checked, with paymentMethod.id, currency per method, Klarna's addressGET /paynl/v1/orders/{id}/status — Order:Status, with payments[] naming the payer (customerType/customerId/customerName, an IBAN for iDEAL and Pay by Bank, the email for PayPal)PATCH /paynl/v1/orders/{id}/capture, …/capture/amount, …/void (−61), …/abort (−90), …/approve, …/decline (−64) — the order's answer, Order.OutputPATCH /paynl/v2/transactions/{orderId | EX code}/refund — Transaction:Refund with {amount: {value, currency}}; partial refunds up to what was captured; the legacy order id (52305407020X2325), never the order UUID/paynl-checkout/to/order/{token} — the payer: Pay.nl's checkout and the method's own screens (card form with the issuer's 3-D Secure, iDEAL's bank list and the bank, Pay by Bank in NL, BE, DE, AT, PayPal, Klarna, Bancontact with its app, Apple Pay, Google Pay, the sandbox of method 613). ?device=mobile|desktop forces eitherExchanges, 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.
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):
| Card | Outcome |
|---|---|
4111 1111 1111 1111 | Paid (100), no challenge; so is any other valid card number |
5555 5555 5555 4444 | Paid (100), no challenge |
4000 0000 0000 3220 | 3-D Secure challenge, then Paid (100) |
5200 8282 8282 8210 | 3-D Secure challenge, then Paid (100) |
4000 0000 0000 0341 | 3-D Secure challenge that fails: Denied (-63) |
4000 0000 0000 0002 | Declined by the issuer: Denied (-63) |
4000 0000 0000 9995 | Insufficient funds: Denied (-63) |
4000 0000 0000 0119 | Processor error: Failure (-60) |
| Amount (minor units) | Effect |
|---|---|
| …50, from 10.00 | the payment stays PENDING (50) for 15 seconds, then is PAID (100), with an exchange each time it ends |
| …53, from 10.00 | Order:Create is refused: 503 PAY-1503 |
| …60, from 10.00 | FAILURE (-60): an error at the payment method |
| …63, from 10.00 | DENIED (-63): the payment processor rejects it (for Klarna: the credit check fails) |
| …85, from 10.00 | VERIFY (85): held for review; Order:Approve or Order:Decline settles it |
| Refund …03, from 10.00 | 403 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-1400 Bad requestPAY-1404 The requested resource is not foundPAY-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'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.
POST /orchid/v2/payment/card/sale, /card/auth — acknowledged ({status: "success", request_id, project_id, payment_id}); the outcome follows in callbacksPOST /orchid/v2/payment/card/3ds_check_iframe, /card/3ds_result — the 3-D Secure continuationsPOST /orchid/v2/payment/card/capture, /card/cancel (a hold only), /card/refund; /v2/payment/applepay/sale and /applepay/refundPOST /orchid/v2/payment/card/payout — a payout to a card, never 3-D SecurePOST /orchid/v2/payment/status — authoritative, signed; an unknown payment is {payment:{status:"error"}, errors:[{code:"3061"}]}/orchid-lab/acs/{project}/… — the card issuer's ACS: the 3DS Method (in the merchant's hidden iframe; it notifies acs_return_url.3ds_notification_url), the challenge (CReq), the basic workflow's PaReq; the result goes back to acs_return_url.return_url as cres + threeDSSessionData (basic: PaRes + MD to the TermUrl)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.
processing (9999), the outcome after 1.5 s.acs_return_url): after 3 s awaiting 3ds result and the callback with threeds2.iframe;
1.5 s after 3ds_check_iframe (or 30 s without it, as the method had not completed) the frictionless outcome, or the callback with threeds2.redirect (creq, threeDSSessionData);
the outcome 1 s after 3ds_result. No 3ds_result within 30 min: declined 108.acs_return_url.return_url): the callback with acs {acs_url, pa_req, md}, then 3ds_result with pares.3ds_check_iframe on a payment already concluded drops it back to processing with 3060 and the acquirer's 11206, for good — as Orchid did to prod tx ae9c5ded.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.
| Card | Brand | Purchase |
|---|---|---|
4000 0000 0000 0077 | VISA | success, no 3-D Secure |
4111 1111 1111 1111 | VISA | decline, no 3-D Secure (10000 General decline) |
4000 0000 0000 0119 | VISA | success, no 3-D Secure (Orchid's COF registration card; COF debits are not simulated) |
4000 0000 0000 0135 | VISA | success, no 3-D Secure (Orchid's COF registration card; COF debits are not simulated) |
4539 1214 1116 8251 | VISA | decline after a delay at the issuer: answers after 10 s with 10602 Time-out |
4314 2200 0000 0056 | VISA | success after the challenge (the ACS page asks for the code) |
4477 0000 0000 0006 | VISA | success, 3-D Secure frictionless (device fingerprint, no challenge) |
5413 3300 0000 0019 | MASTERCARD | success after the challenge (the ACS page asks for the code) |
5544 3300 0000 0045 | MASTERCARD | decline after the challenge, whatever is answered (10114 Declined by 3DS Check) |
5252 0000 0000 0004 | MASTERCARD | success, 3-D Secure frictionless (device fingerprint, no challenge) |
| Card | Brand | Payout |
|---|---|---|
4242 4242 4242 4242 | VISA | payout success |
4314 2200 0000 0072 | VISA | payout decline (10000 General decline) |
5413 3300 0000 0019 | MASTERCARD | payout success |
5413 3300 0000 0035 | MASTERCARD | payout decline (10000 General decline) |
| Amount | Effect |
|---|---|
| …14 | decline: 101021 Incorrect PAN |
| …34 | refused by RCS at creation: 1434 Duplicate operation |
| …41 | refused by RCS at creation: 1401 PAN is Blacklisted |
| …54 | decline: 10106 Card expired |
| …59 | decline: 10402 Suspicious operation |
| …79 | decline: 10103 Incorrect PIN or CVV |
| …88 | 3-D Secure frictionless (device fingerprint), then success |
| …91 | decline: 10202 Issuer inoperative |
| …96 | payment status error: 10505 PSP system is inoperative |
| …97 | decline after 10 s: 10602 Time-out |
| …98 | challenge, then authentication fails whatever is answered: 104 |
| …99 | challenge, then success |
| …02 | decline: 10000 General decline |
| …05 | decline: 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'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.
POST /nuvei/ppp/api/v1/getSessionToken.do, openOrder.do — a session token (sessions live 30 minutes, modeled)POST /nuvei/ppp/api/v1/initPayment.do — InitAuth3D: threeD.v2supported says whether the card is enrolledPOST /nuvei/ppp/api/v1/payment.do — a card payment: non-3DS, 3DS (threeD and relatedTransactionId = the InitAuth3D: frictionless Sale, or an Auth3D REDIRECT with threeD.acsUrl and cReq), the liability shift after a challenge (relatedTransactionId = the Auth3D, no threeD), or an APM (paymentOption.alternativePaymentMethod: REDIRECT to paymentOption.redirectUrl). transactionType Auth authorises onlyPOST /nuvei/ppp/api/v1/refundTransaction.do, voidTransaction.do, settleTransaction.do — on relatedTransactionId; refunds partial or full up to the paymentPOST /nuvei/ppp/api/v1/payout.do — an OCT to userPaymentOption.userPaymentOptionId (a UPO an approved payment made for the userTokenId) or to cardData (the cardholder name required, filter 1210); up to 10000.00 (filter 1126 above, modeled)POST /nuvei/ppp/api/v1/getPaymentStatus.do (the session's last payment), getTransactionDetails.do (by transactionId or the latest with a clientUniqueId), getPayoutStatus.do (by the payout's clientRequestId)POST /nuvei/ThreeDSACSEmulatorChallenge/api/ThreeDSACSChallengeController/ChallengePage?<token> — the issuer's challenge (the shared ACS page); the CRes is posted to threeD.notificationURL as cres. Unanswered for 10 minutes, the authentication expires (filter 1278)/nuvei/apm/Home?PaymentToken=… — the iDEAL page: the bank list (on a computer, the QR code and the payer's phone beside it); Nuvei's sandbox bank TESTNL2A offers the outcome to pick. 15 minutes to pay (modeled), then a DECLINED DMN. ?device=mobile|desktop forces either flowDMNs 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).
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.
| Card | Brand | 3DS | Issuer's answer |
|---|---|---|---|
4761344136141390 | VISA debit (SG) | not enrolled in 3DS | Approved |
4244951901005043 | VISA debit (GB) | not enrolled in 3DS | Approved |
4159129252458086 | VISA credit (SV) | not enrolled in 3DS | Approved |
4001888687412469 | VISA credit (DE) | not enrolled in 3DS | Approved |
4444493318246892 | VISA credit (MX) | not enrolled in 3DS | Approved |
4000287447386587 | VISA debit (US) | not enrolled in 3DS | Approved |
4263704637473241 | VISA debit (CA) | not enrolled in 3DS | Approved |
4815163523263534 | VISA debit (MX) | not enrolled in 3DS | Approved |
5221744250525131 | MASTERCARD credit (MX) | not enrolled in 3DS | Approved |
5550343875851690 | MASTERCARD credit (ZA) | not enrolled in 3DS | Approved |
5333395610225642 | MASTERCARD credit (HN) | not enrolled in 3DS | Approved |
5203821142568313 | MASTERCARD credit (CN) | not enrolled in 3DS | Approved |
5256787172322309 | MASTERCARD debit (MX) | not enrolled in 3DS | Approved |
5413037340736315 | MASTERCARD credit (DK) | not enrolled in 3DS | Approved |
375510513169537 | AMEX credit (IL) | not enrolled in 3DS | Approved |
374245171846495 | AMEX credit (GB) | not enrolled in 3DS | Approved |
6011405433455453 | DISCOVER credit (EC) | not enrolled in 3DS | Approved |
3612569856088370 | DINERS credit (DE) | not enrolled in 3DS | Approved |
6233052703225398 | UNIONPAY credit (JP) | not enrolled in 3DS | Approved |
4107652651950141 | VISA debit (CZ) | not enrolled in 3DS | Decline |
4083595312691240 | VISA debit (CZ) | not enrolled in 3DS | Soft Decline - Authentication is Advised |
4916562347530945 | VISA credit (BR) | not enrolled in 3DS | External Error in Processing |
5333438937354537 | MASTERCARD debit (US) | not enrolled in 3DS | Acquirer Validation |
4485076191294772 | VISA credit (US) | not enrolled in 3DS | Lost/Stolen |
4344141510671174 | VISA debit (IN) | not enrolled in 3DS | Do Not Honor |
5525873084077200 | MASTERCARD credit (CN) | not enrolled in 3DS | Do Not Honor |
4000173946194872 | VISA debit (IN) | not enrolled in 3DS | Insufficient Funds |
5221422486574534 | MASTERCARD credit (ZA) | not enrolled in 3DS | Insufficient Funds |
4000187443195604 | VISA debit (IN) | not enrolled in 3DS | Exceeds Withdrawal Limit |
4000195795445766 | VISA debit (IN) | not enrolled in 3DS | Exceeds Withdrawal Frequency |
5168723718989159 | MASTERCARD debit (CZ) | not enrolled in 3DS | Invalid Transaction |
4107647509690329 | VISA debit (CZ) | not enrolled in 3DS | Format Error |
5588341543306531 | MASTERCARD credit (VE) | not enrolled in 3DS | Issuer or Switch Inoperative |
4000237713987816 | VISA debit (US) | not enrolled in 3DS | Timeout/Retry |
4000248973221309 | VISA credit (US) | not enrolled in 3DS | Expired Card |
4603171563067000 | VISA debit (CZ) | not enrolled in 3DS | Transaction Not Permitted To Cardholder |
4917013255931104 | VISA debit (CZ) | not enrolled in 3DS | Transaction Not Permitted on Terminal |
4645798619106711 | VISA debit (AU) | not enrolled in 3DS | Restricted Card |
5333582039422930 | MASTERCARD debit (US) | not enrolled in 3DS | Invalid CVV |
4521107938796906 | VISA credit (TT) | not enrolled in 3DS | Invalid CVV2 |
4105876029788196 | VISA debit (ZA) | not enrolled in 3DS | Closed Account |
6011206953470801 | DISCOVER credit (US) | not enrolled in 3DS | Do not honor |
4000023164892261 | VISA credit (GB) | 3DS (the cardholder name picks the flow) | Approved |
4005241818985068 | VISA credit (IN) | 3DS (the cardholder name picks the flow) | Approved |
4037073766091520 | VISA credit (MX) | 3DS (the cardholder name picks the flow) | Approved |
4021937195658109 | VISA credit (GB) | 3DS (the cardholder name picks the flow) | Soft Decline - Authentication is Advised |
4742101426751112 | VISA debit (PL) | 3DS (the cardholder name picks the flow) | Approved |
4008370896662369 | VISA debit (GB) | 3DS (the cardholder name picks the flow) | Decline |
4000164166749263 | VISA debit (IN) | 3DS (the cardholder name picks the flow) | Do not honor |
4871040116543210 | VISA debit (BE) | 3DS (the cardholder name picks the flow) | Stolen card |
5408458947067358 | MASTERCARD credit (MX) | 3DS (the cardholder name picks the flow) | Approved |
5333418445863914 | MASTERCARD credit (MX) | 3DS (the cardholder name picks the flow) | Decline |
5359438180895896 | MASTERCARD credit (MX) | 3DS (the cardholder name picks the flow) | Declined by the bank |
5353029602254618 | MASTERCARD debit (DE) | 3DS (the cardholder name picks the flow) | Approved |
5127880173456783 | MASTERCARD debit (BE) | 3DS (the cardholder name picks the flow) | Approved |
5109486948867999 | MASTERCARD debit (US) | 3DS (the cardholder name picks the flow) | Soft Decline - Authentication is Advised |
5333463046218753 | MASTERCARD debit (US) | 3DS (the cardholder name picks the flow) | Do not honor |
4000027891380961 | VISA credit (GB) | 3DS (the cardholder name picks the flow) | Approved |
4000020951595032 | VISA credit (GB) | 3DS (the cardholder name picks the flow) | Approved |
2221008123677736 | MASTERCARD credit (US) | 3DS (the cardholder name picks the flow) | Approved |
5333302221254276 | MASTERCARD credit (US) | 3DS (the cardholder name picks the flow) | Approved |
5545060700001113 | MASTERCARD credit (US) | 3DS, frictionless at 83.10 | Approved |
4567491000001113 | VISA credit (US) | 3DS, frictionless at 83.10 | Approved |
5545060700002228 | MASTERCARD credit (US) | 3DS, challenge at 115.20 | Approved |
4567491000002228 | VISA credit (US) | 3DS, challenge at 115.20 | Approved |
| Amount (last two digits in minor units) | Effect |
|---|---|
| …55, from 10.00 | A payout stays PENDING for 10 s, then approves (a DMN tells) |
| …77, from 10.00 | A refund of this amount is declined by the acquirer (gwErrorReason Decline) |
| …91, from 10.00 | The DMN arrives 30 s late: the response, the payer's return or a status read tell the outcome first |
| …92, from 10.00 | No DMN at all: only the response and a status read tell the outcome |
| gwExtendedErrorCode | Meaning |
|---|---|
1001 | Invalid Expiration Date |
1002 | Expiration Date Too Old |
1106 | Credit Amount Exceed Total Charges |
1112 | Sale/Settle was already credited |
1126 | Credit amount exceeds ceiling |
1155 | 3D Related transaction is missing or incorrect |
1167 | Transaction already settled |
1168 | Transaction already voided |
1176 | Cannot void an auth transaction |
1177 | Cannot void a credit transaction |
1185 | Related transaction must be Auth3D, APPROVED, same 3DS version and same merchant |
1186 | Related transaction must be 3D authenticated |
1206 | Settle amount exceeds total charges |
1210 | NameOnCard is missing |
1272 | Card number must be equal for all transaction in a specific 3DS flow |
1273 | Card number, Currency and Amount must be equal in a specific 3DS flow |
1274 | Requested Auth3D 2.0 but card number is not supported with 3DS 2.0 |
1277 | DS responded with an error, please verify request and try again |
1278 | Authentication is expired, new InitAuth3d is required |
1279 | 3D related transaction was already used for Sale/Auth |
1281 | Authentication has not completed |
advanceResponseChecksum, the 3DS server-to-server flow (initPayment, payment, CReq/CRes, the liability shift), the test cards and cardholder names, the 83.10 / 115.20 amount cards, errCodes and gateway filter codes, the iDEAL BIC list and its sandbox bank TESTNL2A.responsechecksum (deprecated), the lab rules, no 3DS fingerprinting (methodUrl is empty).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.
POST /mifinity/api/gateway/init-iframe/: the session, initializationToken and, with entry: "hosted",
iframeEntrypoint (/mifinity/iframe2#initToken=…). The token lasts 30 minutes. /mifinity/widgets/sgpg.js serves
showPaymentIframe for the embedded widget.GET /mifinity/api/gateway/payment-status/{validationKey}: NOT_COMPLETED, PENDING, SUCCESSFUL (with
paymentResponse) or FAILED (with detailedStatus).returnUrl (the payment response, customerDob as [y, m, d]), a failed one to
errorUrl (ABANDONED_BY_USER, REFUSED_BY_RISK, FAILED). MiFinity signs neither: the
validationKey the merchant made comes back in them. responseCode is an HMAC-SHA256 under the published secret
bistromathic-lab-mifinity-response-code-2026-11-12 over traceId|validationKey|transactionReference (the lab's; MiFinity's is undocumented).POST /mifinity/api/payments/acct2acct/: synchronous, to an email or account number; 200 with status: "OK" means paid.
POST /mifinity/api/payments/pab: PayAnyBank to a SEPA IBAN (IBAN, BIC, BANK_NAME,
CUSTOMER_NAME, _ADDRESS, _CITY, _ZIP): Received, Submitted after 1 s, Processed by acquirer after 6 s
(MiFinity: the next day). Notification callbacks (statuses 3, 5, 6) go to the URL set with PUT /mifinity-lab/portal?apiKey=…
{"callbackUrl", "merchantName"} (lab only: MiFinity's integration team sets it up by email) and want 200 with no body.GET /mifinity/api/transactions/status/{traceId}, /api/transactions/{reference}/status (1, 2, 3, 5, 6, 7, 8; 404
ELEMENT_NOT_FOUND), GET /mifinity/api/currencies.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.
wrong fails)| Accounts | Effect | |
|---|---|---|
player@mifinity.test | 5001000001213949, 5001000001213950, 5001000001213951 | an existing, verified user: EUR 5,000, USD 2,500 and GBP 2,000 in the eWallet, a verified Visa •••• 1111 |
lowbalance@mifinity.test | 5001000001214001 | an existing user with EUR 5.00: the balance cannot pay, a card can |
unverified@mifinity.test | 5001000001214002 | an existing user whose card •••• 4242 is not verified: nothing can be paid until it is (code 1234) |
risk@mifinity.test | 5001000001214003 | an existing user whose deposits MiFinity's risk engine refuses (REFUSED_BY_RISK) |
| any other email | a new account | creates the account (accept the Terms), pays by card, then sets a password |
| Amount | Effect |
|---|---|
| deposit …13 | MiFinity fails the deposit (FAILED, TRANSACTION_FAILED) once the payer pays |
| deposit …14 | MiFinity's risk engine refuses the deposit (FAILED, REFUSED_BY_RISK) |
| deposit …15 | the processor takes 30 seconds: PENDING, then SUCCESSFUL (the callback comes after the payer is back) |
| deposit …16 | PENDING for 10 seconds, then FAILED |
| payout …13 | acct2acct: declined (400 TRANSACTION_FAILED); PayAnyBank: Rejected (6) after it is submitted |
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.
POST /truelayer/v3/payments (bank_transfer, user_selected or preselected provider, merchant_account or external_account beneficiary,
hosted_page), GET /v3/payments/{id}, POST /v3/payments/{id}/actions/cancel,
POST|GET /v3/payments/{id}/refunds, GET /v3/payments/{id}/refunds/{refund_id},
POST /v3/payouts (payment_source, external_account by IBAN or sort code and account number), GET /v3/payouts/{id},
GET /v3/merchant-accounts, /{id}, /{id}/payment-sources?user_id=/truelayer/checkout/payments#payment_id=…&resource_token=…&return_uri=… — the hosted payment page: the bank picker with
search, consent, on a computer a real QR code to continue in the banking app (the phone is drawn beside the page; a real phone's camera works too),
the bank's app or website, TrueLayer's mock bank, the result, and back to return_uri?payment_id=…
(&error=tl_hpp_abandoned when the payer cancelled). ?device=mobile|desktop forces either flow.PUT /truelayer-lab/console/settings {"webhook_uri": "…", "merchant_name": "…"} (lab only) — what TrueLayer Console
holds: the client's webhook URI. Without one, no webhooks are sent.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.
mock-payments-*-redirect)| Username | Effect |
|---|---|
test_executed | Execution: the payment executes (and settles) |
test_authorisation_failed | Auth failure: failed, authorization_failed |
test_execution_rejected | Execution rejection: failed after authorisation, provider_rejected |
| Deposit amount | Effect |
|---|---|
| …13, from 10.00 | executed, then settled two minutes later |
| …24, from 10.00 | authorised, executed a minute later |
| …05, from 10.00 | the bank refuses: failed, insufficient_funds (stage authorizing) |
| …06, from 10.00 | over the payer's limit at the bank: failed, payment_limit_exceeded |
| …07, from 10.00 | authorised, then rejected at execution: failed, provider_rejected (stage authorized) |
| Payout amount | Effect |
|---|---|
| …13, from 10.00 | executed, then returned by the beneficiary's bank 20 s later: failed, returned |
| …17, from 10.00 | failed, scheme_error |
| …41, from 10.00 | pending for two minutes, then executed |
| …05, from 10.00 | failed, blocked (sanctions screening) |
| Refund amount | Effect |
|---|---|
| …05, from 10.00 | failed, 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).
| Country | Currency | Banks |
|---|---|---|
| GB | GBP | Barclays 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 |
| IE | EUR | AIB 15,000, Bank of Ireland 15,000, PTSB 10,000, Revolut 100,000, mock-payments-ie-redirect |
| DE | EUR | Sparkasse 5,000, Deutsche Bank 10,000, Commerzbank 10,000, ING 10,000, N26 50,000, mock-payments-de-redirect |
| FR | EUR | BNP 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 |
| ES | EUR | Banco Santander 15,000, BBVA 15,000, CaixaBank 15,000, Banco Sabadell 10,000, mock-payments-es-redirect |
| IT | EUR | Intesa Sanpaolo 15,000, UniCredit 15,000, Fineco 30,000, mock-payments-it-redirect |
| NL | EUR | ING 10,000, ABN AMRO 50,000, Rabobank 10,000, mock-payments-nl-redirect |
| LT | EUR | Swedbank 15,000, SEB 15,000, Luminor 10,000, mock-payments-lt-redirect |
| PT | EUR | Millennium bcp 10,000, Caixa Geral de Depósitos 10,000, mock-payments-pt-redirect |
| FI | EUR | OP 15,000, Nordea 10,000, mock-payments-fi-redirect |
| AT | EUR | Erste Bank 10,000, Raiffeisen 10,000 |
| BE | EUR | KBC 10,000, BNP Paribas Fortis 10,000, Belfius 10,000 |
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.
POST /jeton/v2/payments (200, checkout.url; the checkout expires after 15 minutes),
GET /v2/payments, GET /v2/payments/{id}POST /jeton/v2/payouts (202; jeton_account by customer number, email or phone, completed inside the request;
bank_account on sepa_instant or swift; crypto_wallet in USDT on TRX or ETH),
GET /v2/payouts, GET /v2/payouts/{id}, POST /v2/payouts/requirementsGET /jeton/v2/accounts, /accounts/{id}, /accounts/{id}/transactions, /payment-methods,
/me, /events, /events/{id}POST /jeton/v2/simulator/payments/{id}/complete {"checkoutUrl"} (the test customer
4815162342 pays), POST /v2/simulator/accounts/{id}/deposits, GET /v2/simulator/counterparties
(customer 4815162342, business 4455667788)/jeton/checkout/{token}: the hosted checkout. Log in with the Jeton account's email or phone (any password), approve the
login in the Jeton app (drawn beside the page on a computer) or type its six-digit code, review, Pay; back to returnUrl.
?device=mobile|desktop forces either flow.GET|PUT /jeton-lab/portal/webhook?apiKey=… {"url", "enabled"} (lab only): the merchant portal's v2 webhook address.
Without one, events stay in GET /events. POST /jeton-lab/portal/payments/{id}/refund: a refund made in the portal
(Jeton's API has no refund; the payment reads refunded).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.
| Destination | Value | Outcome |
|---|---|---|
| bank_account IBAN | DE09000000000000000001 | completed |
| bank_account IBAN | DE79000000000000000002 | failed |
| crypto_wallet, an EVM network | 0x0000000000000000000000000000000000000001 | completed |
| crypto_wallet, an EVM network | 0x0000000000000000000000000000000000000002 | failed |
| crypto_wallet, TRON | T9yD14Nj9j7xAB4dbGeiX9h8unkKLxmGkn | completed |
| crypto_wallet, TRON | T9yD14Nj9j7xAB4dbGeiX9h8unkKT76qbH | failed |
| jeton_account customer number (lab) | ending 01 / 02 | completed / failed |
| Login | Customer number | Effect |
|---|---|---|
alex.morgan@example.com | 4815162342 | the sandbox's test customer (4815162342): 5,000.00 in any currency |
low.balance@example.com | 7340012905 | 20.00: paying more fails the attempt (insufficient balance) and the payment stays pending |
suspended@example.com | 7340012913 | the login is refused: the account is suspended |
payout.fails@example.com | 7340012902 | customer number ends 02: pays as usual, and a payout back to it fails |
| Deposit amount | Effect |
|---|---|
| …13, from 10.00 | Paid, 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'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.
POST /astropay/v1/partners/oauth/token (Basic client_id:client_secret, grant_type=client_credentials;
a 43199-second Bearer token)POST /astropay/v1/payments (method: CHECKOUT, requested_payment_method: ASTROPAY; PENDING with
redirect_url; expires after 20 minutes), GET /v1/payments/{id}?method=CHECKOUT,
POST /v1/payments/refund (full or partial; 412 PAYMENT_NOT_FOUND, INVALID_AUTHORIZATION_STATUS,
REFUND_AMOUNT_EXCEEDED, PAYMENT_PROCESSING_ERROR), GET /v1/certificates. Currencies:
USD, EUR, GBP, CAD, BRL, ARS, CLP, COP, MXN, PEN, UYU, INR.callback_notification, else the merchant's URL, signed with
Partner-Signature (SHA256withRSA over requestId:timestamp:body) by the lab's published key; its
certificate, serial 1A57C0DE20261111, is what /v1/certificates answers.POST /astropay/merchant/v1/cashout to a user id or a phone: APPROVED at once to an AstroPay user; PENDING to a phone
with no account, CANCELLED after 24 hours (a phone ending 0077 signs up 30 seconds later: APPROVED).
GET /merchant/v1/cashout/{id}/status, GET /merchant/v2/cashout/external/{merchant_cashout_id}/status.POST /astropay/merchant/v2/cashout/init (Direct Withdrawal; each country's required fields), GET
/merchant/v2/cashout/{cashout_external_id}/status, GET /merchant/v1/cashout/payout/banks/{country}.
CREATED, PENDING after a second, APPROVED (SPCD_1000-Txn successful) three seconds later. Brazil's staging CPFs:
12345678909 created, 22345678909 refused for its status.Signature, the hex HMAC-SHA256 of the body under the published secret
bistromathic-lab-astropay-simulation-secret-key./astropay/checkout/payment/{id}: AstroPay's checkout. The AstroPay account's phone number, the six-digit code sent by SMS
(on a computer it arrives on the phone drawn beside the page), the confirmation, Pay; back to redirect_success_url,
redirect_error_url or cancel_url. ?device=mobile|desktop forces either flow.GET|PUT /astropay-lab/merchant?app_id=… {"merchant_name", "callback_url"} (lab only): what AstroPay sets at
onboarding. POST /astropay-lab/users {"phone"}: the phone signs up, and its PENDING cashouts are paid.| Phone | User id | Effect |
|---|---|---|
+5511911223344 | Vx0H4pdAtPCz | 5,000.00 USD. Pays; cashouts back are APPROVED at once |
+5491123456789 | Lm7Qa2Zx9KpR | 5,000.00 USD (Argentina). Pays |
+525512345678 | Sh4Mx8Tq2LwN | 5,000.00 USD (Mexico). Pays |
+5511900000005 | Bl0Wb5Lc3VnE | 20.00 USD: paying more is refused for the balance; the payment stays PENDING |
+5511900000009 | Cd9Bk1Ld7XqS | The account is blocked: the login is refused |
| any other | made up from the phone | A fresh account holding 5,000.00 USD |
| Country | Currency | Methods |
|---|---|---|
| AR | ARS | TR Bank transfer (CBU / CVU) |
| BR | BRL | PIX PIX |
| CL | CLP | TR Bank transfer |
| CO | COP | TR Bank transfer, BREB Bre-B |
| EC | USD | TY Bank transfer |
| IN | INR | TR Bank transfer |
| MX | MXN | TR SPEI (CLABE) |
| PE | PEN | TR Bank transfer, YP Yape |
| Amount | Effect |
|---|---|
| deposit …13 | Paid, then PENDING for a minute before COMPLETED |
| deposit …33 | COMPLETED, but no callback is sent: only the status endpoint tells |
| deposit …05 | REJECTED once the payer confirms (AstroPay's risk check refuses it) |
| withdrawal …13 | PENDING for two minutes, then APPROVED |
| withdrawal …02 | CANCELLED, INVALID_BANK_ACCOUNT (Brazil: INVALID_PIX_KEY) |
| withdrawal …03 | CANCELLED, ACCOUNT_BLOCKED (Argentina: INVALID_ALIAS; elsewhere GENERIC_ERROR) |
| withdrawal …04 | CANCELLED, CPF_MISMATCH (Brazil; elsewhere TRX_COULD_NOT_BE_PROCESSED) |
| refund …07 | 412 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 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.
POST /coinspaid/api/v2/addresses/take (201): a deposit address for a user (foreign_id, end_user_reference,
sender_type and sender_data are required, as MiCA has them), optionally converting deposits (convert_to).
A new address each call; a merchant reuses one per user and currency.POST /coinspaid/api/v2/withdrawal/crypto (201, status: processing): to any address, with conversion from another
balance (convert_to, amount or amount_to); sender and receiver data required.GET /coinspaid/api/v2/transactions/info by id, foreign_id (withdrawals) or txid: a JSON body as
the docs send it, or the same as query parameters (a Worker cannot send a GET with a body).POST /coinspaid/api/payments/v1/requests (201): a payment request; its payment_link is the lab's hosted page
(pick a currency, Proceed to payment, pay the address); callbacks processing, paid, failed, expired.POST /v2/accounts/list, /v2/currencies/list, /v2/currencies/rates, GET /v2/ping.
Exchanges and instant withdrawals are not simulated.GET|PUT /coinspaid-lab/portal/callback?key=… {"url"} (lab only): the portal's callback URL for that key./coinspaid/deposit?m=<key>&a=<address id>&amount=&cy=&return=: the deposit page a merchant shows
(CryptoProcessing hosts none for addresses): the address, a real QR code (it opens the wallet on a phone), the network and the amount; the
player's wallet drawn beside it on a computer. ?device=mobile|desktop forces either flow.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.
| API code | Asset | Confirmations | Min deposit | Min withdrawal | EUR |
|---|---|---|---|---|---|
BTC | BTC on Bitcoin | 1 | 0.0001 | 0.0002 | 85000 |
ETH | ETH on Ethereum | 2 | 0.002 | 0.002 | 3000 |
ETHA | ETH on Arbitrum | 12 | 0.002 | 0.002 | 3000 |
ETHBASE | ETH on Base | 12 | 0.002 | 0.002 | 3000 |
USDC | USDC on Ethereum | 2 | 5 | 5 | 0.87 |
USDCA | USDC on Arbitrum | 12 | 5 | 5 | 0.87 |
USDCBASE | USDC on Base | 12 | 5 | 5 | 0.87 |
USDCB | USDC on BNB Smart Chain | 10 | 5 | 5 | 0.87 |
USDCP | USDC on Polygon | 10 | 5 | 5 | 0.87 |
USDCS | USDC on Solana | 1 | 5 | 5 | 0.87 |
EURCE | EURC on Ethereum | 2 | 5 | 5 | 1 |
EURCBASE | EURC on Base | 12 | 5 | 5 | 1 |
EURCS | EURC on Solana | 1 | 5 | 5 | 1 |
USDG | USDG on Ethereum | 2 | 5 | 5 | 0.87 |
USDGS | USDG on Solana | 1 | 5 | 5 | 0.87 |
LTC | LTC on Litecoin | 2 | 0.01 | 0.01 | 80 |
BCH | BCH on Bitcoin Cash | 2 | 0.001 | 0.001 | 400 |
DOGE | DOGE on Dogecoin | 3 | 1 | 0.01 | 0.15 |
TRX | TRX on Tron | 3 | 10 | 10 | 0.25 |
SOL | SOL on Solana | 1 | 0.05 | 0.05 | 140 |
XRP | XRP on XRP Ledger | 1 | 4 | 0.001 | 2 |
ADA | ADA on Cardano | 15 | 2 | 2 | 0.6 |
BNB-BSC | BNB on BNB Smart Chain | 10 | 0.01 | 0.01 | 600 |
POL | POL on Polygon | 10 | 10 | 10 | 0.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.
| Amount | Effect |
|---|---|
| …02 | cancelled: "Not enough money on balance" |
| …03 | pending (held for approval), then declined: "Declined by user ID:777" |
| …04 | pending (held for approval), then approved and confirmed |
| …09 | stays not_confirmed: never sent to the blockchain |
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.
POST /shift4/tokens, GET /shift4/tokens/{id} — a card token (one use, 24 hours)POST /shift4/3d-secure — { enrolled, version, redirectUrl, token }: the payer at redirectUrl (/shift4/3d-secure/start/…, the issuer's challenge on the shared ACS page), then back at returnUrl?token=…&liabilityShift=successful|failed; the charge names the token. Unanswered for 10 minutes (modeled), the liability shift fails (abandoned)POST /shift4/charges — with card a token or the card itself; captured=false authorizes only; threeDSecure.external carries a merchant's own 3DS result. A charge on an enrolled card without a successful liability shift fails (authentication_required), as requireSuccessfulLiabilityShiftForEnrolledCard (default true) saysPOST /shift4/charges/{id}/capture — within 5 days, partial or fullPOST /shift4/refunds — partial or full on a captured charge; an authorization is released in fullPOST /shift4/credits — funds to a card (the token or the card); up to 25,000.00 (modeled, limit_exceeded above)GET lists and objects: /charges, /refunds?chargeId=…, /credits, /events (newest first, limit, created[gte] …, startingAfterId)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.
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.
| Card | Brand | Outcome |
|---|---|---|
4012000100000007 | Visa | Successful charge |
4242424242424242 | Visa | Successful charge |
4012888888881881 | Visa | Successful charge |
4000056655665556 | Visa (debit card) | Successful charge (debit) |
4426034383518716 | Visa | Successful charge (GB) |
4909069612259316 | Visa | Successful charge (ES) |
5555555555554444 | MasterCard | Successful charge |
5200828282828210 | MasterCard (debit card) | Successful charge (debit) |
5105105105105100 | MasterCard | Successful charge |
2222000000000008 | MasterCard | Successful charge |
5294008684328401 | MasterCard | Successful charge (GB) |
5125646148694267 | MasterCard | Successful charge (ES) |
6759649826438453 | MasterCard (debit card) | Successful charge (Maestro) |
378282246310005 | American Express | Successful charge |
371449635398431 | American Express | Successful charge |
6011111111111117 | Discover | Successful charge |
6011000990139424 | Discover | Successful charge |
30569309025904 | Diners Club | Successful charge |
38520000023237 | Diners Club | Successful charge |
3530111333300000 | JCB | Successful charge |
3566002020360505 | JCB | Successful charge |
4024007102349866 | Visa | invalid_number |
4532873294814636 | Visa | invalid_expiry_month |
4532582477951947 | Visa | invalid_expiry_year |
4024007189368227 | Visa | invalid_cvc |
4916487051294548 | Visa | expired_card |
4024007134364842 | Visa | Declined: incorrect_cvc |
4929225021529113 | Visa | Declined: incorrect_zip |
4242000000000323 | Visa | Declined: incorrect_address |
4024007118468684 | Visa | Declined: insufficient_funds |
4024007114621187 | Visa | Declined: lost_or_stolen |
4024007155502486 | Visa | Declined: suspected_fraud |
4916018475814056 | Visa | Declined: card_declined |
4916449457024978 | Visa | Declined: authentication_required |
4024007114166316 | Visa | Declined: processing_error |
4242000000000083 | Visa | Successful only with CVC 123, else incorrect_cvc |
4242000000000018 | Visa | Successful; Shift4 disputes it (chargeback) |
4242000000000026 | Visa | Successful; Shift4 disputes it (retrieval request) |
4242000000000042 | Visa | Successful, fraud check safe |
4242000000000059 | Visa | Successful, fraud check suspicious |
4242000000000067 | Visa | Successful, fraud check fraudulent |
4242000000000208 | Visa | Successful, fraud check fraudulent (with a fraud warning) |
4242000000000075 | Visa | Successful, fraud check unknown |
4012001800000016 | Visa | Enrolled for 3D Secure 2 |
5428000221101226 | MasterCard | Enrolled for 3D Secure 2 |
370000000000002 | American Express | Enrolled for 3D Secure 2 |
36000000000008 | Diners Club | Enrolled for 3D Secure 2 |
6011000990191003 | Discover | Enrolled for 3D Secure 2 |
3528000111101116 | JCB | Enrolled for 3D Secure 2 |
4242000000000513 | Visa | ANI check: cardholder name "Ani check" full_match |
4242000000000315 | Visa | AVS check: zip 00000 full_match, 00001 no_match, 00002 partial_match, 00003 not_provided |
4111100000000006 | Visa | CVV check: 111 match, 222 no_match, 333 not_verified, 444 not_provided, 555 not_supported |
4242000000011114 | Visa | A credit to it is a fast credit |
4242000000011148 | Visa | AFT funding supported |
| Amount (last two digits in minor units) | On | Effect |
|---|---|---|
| …51, from 10.00 | credit | The credit fails: card_declined |
| …77, from 10.00 | refund | The refund fails (status failed) |
| …91, from 10.00 | charge | The charge's webhooks arrive 30 s late |
| …92, from 10.00 | charge | The charge sends no webhooks |
/3d-secure, redirectUrl, returnUrl with token and liabilityShift, the charge with the token), the test cards, idempotency, events and unsigned webhooks, the 24-hour token and 5-day capture windows.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.
POST /pxp/api/v1/transactions — transactionMethod.intent Authorisation, EstimatedAuthorisation, Purchase, Verification, Payout or Refund; the card itself or a gatewayTokenId; threeDSecureData.authenticationId (PXP's own 3DS) or a third party's CAVV and ECI; psd2Data.scaExemption. Answers state (Authorised, Captured, Refused, Error), stateData (CRD codes) and a gateway token. A repeated merchantTransactionId answers the original when it succeeded and is processed again when it failedPOST /pxp/api/v1/transactions/{systemTransactionId}/modifications — operation Capture, FinalCapture, Refund (partial or full, on a captured transaction) or Void (an uncaptured authorisation); each answers a new systemTransactionIdGET /pxp/api/v1/transactions/{merchant}/{site}?merchantTransactionId=…|systemTransactionId=…&fundingType=Card — an array of zero or one transactionPOST /pxp/api/v1/threedsecure/integrated/authentications, PUT …/{id}/browser-authentication, GET …/{id}/outcome, GET …/{id}/assessment — PXP's integrated 3-D Secure: frictionless, or PendingCustomerChallenge with acsUrl and challengeData; the payer posts creq to the ACS (the shared issuer page), which posts cres to the challengeCallbackUrl. Unanswered for 10 minutes, the challenge fails (U, reason 14)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).
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.
| Card | Scheme | Outcome |
|---|---|---|
4111111111111111 | Visa | Approved, no challenge |
5555555555554444 | MasterCard | Approved, no challenge |
378282246310005 | American Express | Approved, no challenge |
4000000000000002 | Visa | Challenge required, then approved |
4000000000000077 | Visa | Refused: not sufficient funds (CRD116) |
4000000000000051 | Visa | Refused: do not honour (CRD100) |
4000000000000069 | Visa | Refused: expired card (CRD101) |
4000000000000101 | Visa | Refused: CVC check failed (CRD100, CVC not matched) |
4570020000000851 | Visa | 3DS frictionless, authenticated (Y); SCA mandated; no fingerprinting |
4570020000000067 | Visa | 3DS frictionless, attempted (A) |
4570010000000076 | Visa | 3DS failed (N) |
4570020000000083 | Visa | 3DS failed, could not be performed (U) |
4570010000000092 | Visa | 3DS rejected (R) |
4570020000000018 | Visa | 3DS challenge (C) |
4570020000000109 | Visa | TRA exemption at 135.50 |
5266001055000588 | MasterCard | 3DS frictionless, authenticated (Y); device fingerprinting |
5266001055000075 | MasterCard | 3DS frictionless, attempted (A) |
5266001055000083 | MasterCard | 3DS failed (N) |
5266001055000091 | MasterCard | 3DS failed, could not be performed (U) |
5266001056000108 | MasterCard | 3DS rejected (R) |
5266001055000026 | MasterCard | 3DS challenge (C) |
5266001055000117 | MasterCard | LVP exemption at 13.50; TRA at 13.50 or 135.50 |
375987130000321 | American Express | 3DS frictionless, authenticated (Y) |
375987130000073 | American Express | 3DS frictionless, attempted (A) |
375987140000089 | American Express | 3DS failed (N) |
375987130000107 | American Express | 3DS failed, could not be performed (U) |
375987130000099 | American Express | 3DS rejected (R) |
375987140000022 | American Express | 3DS challenge (C) |
384444444400077 | Diners | 3DS frictionless, authenticated (Y) |
3833333333000075 | Diners | 3DS frictionless, attempted (A) |
3844444444000081 | Diners | 3DS failed (N) |
3833333333000091 | Diners | 3DS failed, could not be performed (U) |
3844444444000107 | Diners | 3DS rejected (R) |
3844444444000024 | Diners | 3DS challenge (C) |
3844444444000073 | Diners | SCA not mandated (PSD2) |
6011060000000560 | Discover | 3DS frictionless, authenticated (Y) |
6011030000000076 | Discover | 3DS frictionless, attempted (A) |
6011060000000081 | Discover | 3DS failed (N) |
6011030000000092 | Discover | 3DS failed, could not be performed (U) |
6011060000000107 | Discover | 3DS rejected (R) |
6011060000000024 | Discover | 3DS challenge (C) |
| Amount (last two digits in minor units) | On | Effect |
|---|---|---|
| …23, from 10.00 | payout | The payout is refused: exceeds withdrawal frequency limit (CRD123) |
| …51, from 10.00 | payout | The payout is refused: do not honour (CRD100) |
| …77, from 10.00 | refund | The refund is refused (CRD100) |
| …83, from 10.00 | payment | Error: timeout / malfunction at the issuer (CRD183) |
| …91, from 10.00 | payment | The webhooks arrive 30 s late |
| …92, from 10.00 | payment | No webhooks |
| …02, from 10.00 | payment | Error: suspected fraud, PXP's risk refusal (CRD102) |
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.
POST /volt/payments (X-Volt-Initiation-Channel: hosted; OPEN_BANKING_EU, OPEN_BANKING_UK,
NPP_PAY_TO_AU), GET /volt/payments/{id}, POST /volt/payments/{id}/cancel, GET /volt/institutions,
/institutions/{id}, /institutions/countriesGET /volt/accounts, /accounts/{id}, /accounts/{id}/beneficiaries (whoever paid in during the last 12 months),
POST /volt/accounts/{id}/transactions (PAYOUT, closed loop only; REFUND of an incoming transaction),
GET /volt/transactions (?gatewayPaymentId=, sourceTransactionId, type[], …), /transactions/{id},
/account-holder-verifications/{id}, …/approve, …/reject/volt/checkout/{paymentId}?auth=… — the hosted checkout: the bank picker with search, PayTo's PayID or BSB (Australia), the summary,
on a computer a real QR code to continue in the banking app (the phone is drawn beside the page; a real phone's camera works too), the bank's app or
website, the sandbox's Synthetic Redirect page and Synthetic Embedded login (one-time code 123456), the
status page, and back to the return URL. ?device=mobile|desktop forces either flow.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.
| Deposit amount | Effect |
|---|---|
| …13, from 10.00 | DELAYED_AT_BANK for two minutes, then COMPLETED |
| …41, from 10.00 | FAILED (a technical failure in the banking network) |
| …05, from 10.00 | REFUSED_BY_BANK (insufficient funds at the bank) |
| …06, from 10.00 | ERROR_AT_BANK |
| …07, from 10.00 | REFUSED_BY_RISK (Circuit Breaker, before the bank) |
| exactly 2050 | COMPLETED, then NOT_RECEIVED (Volt's sandbox value) |
| Payout amount | Effect |
|---|---|
| exactly 1570 | REJECTED, ACCOUNT_INSUFFICIENT_FUNDS (the legacy sandbox's PAYOUT_FAILED) |
| exactly 2570 | REJECTED, TRANSACTION_REJECTED_BY_BANKING_PROVIDER (the legacy sandbox's PAYOUT_REJECTED_BY_PROVIDER) |
| …17, from 10.00 | REJECTED, PROCESSING_ERROR |
| …41, from 10.00 | PROCESSING for two minutes, then COMPLETED |
| to an account that never paid in | REJECTED, TRANSACTION_BENEFICIARY_CHECK_REJECTED |
| under another name | account holder verification CLOSE_MATCH or NO_MATCH: waits for approve or reject (10 min, then TRANSACTION_APPROVAL_TIMEOUT) |
| Refund amount | Effect |
|---|---|
| exactly 1177 | REJECTED, TRANSACTION_REJECTED_BY_BANKING_PROVIDER (the legacy sandbox's REFUND_FAILED) |
| …05, from 10.00 | REJECTED, ACCOUNT_INSUFFICIENT_FUNDS |
X-Volt-Testing-Scenario | Effect |
|---|---|
PAYOUT_REJECTED | REJECTED, TRANSACTION_REJECTED_BY_BANKING_PROVIDER |
ACCOUNT_HOLDER_VERIFICATION_CLOSE_MATCH | account holder verification CLOSE_MATCH: waits for approve or reject |
ACCOUNT_HOLDER_VERIFICATION_NO_MATCH | account holder verification NO_MATCH: waits for approve or reject |
ACCOUNT_HOLDER_VERIFICATION_ERROR | account holder verification ERROR: waits for approve or reject |
| Country | Currency | Payment system | Banks |
|---|---|---|---|
| GB | GBP | OPEN_BANKING_UK | Barclays 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 |
| DE | EUR | OPEN_BANKING_EU | Sparkasse 5,000, Deutsche Bank 10,000, Commerzbank 10,000, ING 10,000, N26 50,000, Synthetic Redirect, Synthetic Embedded |
| AT | EUR | OPEN_BANKING_EU | Erste Bank und Sparkassen 10,000, Raiffeisen 10,000, Bank Austria 10,000, Synthetic Redirect, Synthetic Embedded |
| NL | EUR | OPEN_BANKING_EU | ING 10,000, ABN AMRO 50,000, Rabobank 10,000, bunq 25,000 |
| FR | EUR | OPEN_BANKING_EU | BNP Paribas 15,000, Crédit Agricole 15,000, Société Générale 15,000, BoursoBank 30,000 |
| ES | EUR | OPEN_BANKING_EU | Banco Santander 15,000, BBVA 15,000, CaixaBank 15,000 |
| IT | EUR | OPEN_BANKING_EU | Intesa Sanpaolo 15,000, UniCredit 15,000, Fineco 30,000 |
| IE | EUR | OPEN_BANKING_EU | AIB 15,000, Bank of Ireland 15,000, PTSB 10,000 |
| BE | EUR | OPEN_BANKING_EU | KBC 10,000, BNP Paribas Fortis 10,000, Belfius 10,000 |
| FI | EUR | OPEN_BANKING_EU | OP 15,000, Nordea 10,000 |
| PT | EUR | OPEN_BANKING_EU | Millennium bcp 10,000, Caixa Geral de Depósitos 10,000 |
| LT | EUR | OPEN_BANKING_EU | Swedbank 15,000, SEB 15,000, Luminor 10,000 |
| LV | EUR | OPEN_BANKING_EU | Swedbank 15,000, Citadele 10,000 |
| EE | EUR | OPEN_BANKING_EU | Swedbank 15,000, LHV 15,000, SEB 15,000 |
| SK | EUR | OPEN_BANKING_EU | Slovenská sporiteľňa 10,000, Tatra banka 10,000 |
| AU | AUD | NPP_PAY_TO_AU | CommBank 25,000, Westpac 25,000, ANZ 25,000, NAB 25,000 |
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.
POST /d24/v3/deposits (OneShot; X-Idempotency-Key honoured): ONE_SHOT with payment_info
and redirect_url, or HOSTED when the payer's document, email or name is missing. GET /v3/deposits/{deposit_id},
GET /v3/payment_methods?country=. Errors as D24's API Codes: 201 BEAN_VALIDATION_ERROR, 300 USER_BLACKLISTED, 400 INVALID_AMOUNT
(under USD 2), 401 PAYMENT_METHOD_NOT_FOUND (Boleto BL, retired 2024-12-23), 402 INVOICE_ALREADY_USED, 408 / 410 method limits.POST /d24/v3/refunds (a COMPLETED deposit; bank_account required: 804 MISSING_BANK_ACCOUNT), GET /v3/refunds/{refund_id}:
PENDING, DELIVERED, then COMPLETED or CANCELLED.{"deposit_id"} / {"refund_id"}, with X-Date, X-Login and
Authorization: D24 <hex HMAC-SHA256 of X-Date + X-Login + body> under the published deposits API Signature
d24-lab-deposits-signature-0a4c19e2b7.POST /d24/v3/cashout (each country's validations: Pix keys 10000–10003 and banks in Brazil, a CLABE in Mexico, Bre-B keys
20000–20003, Efecty 10003, Nequi, Daviplata and banks in Colombia), POST /v3/cashout/status, DELETE /v3/cashout/cancel,
GET /v3/banks?country=. PENDING (0), DELIVERED (4) a second later, then COMPLETED (1), REJECTED (3) or CANCELLED (2).
Notifications are form posts with control = upper-case HMAC-SHA256 of Be4<external_id>Bo7 under the
published cashouts API Signature d24-lab-cashouts-signature-5f81d03c6e./d24/v1/checkout/{token}: D24's checkout. Pix: a real BR Code as QR and "Copia e Cola", the bank app beside it. SPEI: the CLABE.
OXXO and Efecty: a cash voucher with a real Interleaved 2 of 5 barcode and the lab's counter. PSE: the bank picker, then online banking with
the bank app's dynamic key. ?device=mobile|desktop forces either flow.GET|PUT /d24-lab/merchant?x_login=… {"merchant_name", "notification_url"} (lab only): the Merchant Panel's settings.| Code | Method (option) | Country | Type | Limits (local, modeled) | Expiry (modeled) |
|---|---|---|---|---|---|
IX | Pix (pix) | BR | BANK_TRANSFER | 1–50,000 | 12 h |
SE | Spei (spei) | MX | BANK_TRANSFER | 10–500,000 | 3 days |
OX | OXXO (oxxo) | MX | VOUCHER | 10–10,000 | 6 days |
PC | PSE (pse) | CO | BANK_DEPOSIT | 5,000–20,000,000 | 1 h |
EY | Efecty (efecty) | CO | VOUCHER | 5,000–5,000,000 | 3 days |
| Amount | Effect |
|---|---|
deposit 1 | Paid: COMPLETED about 2 seconds after the payer pays |
deposit 2 | Paid: COMPLETED about 3 minutes after the payer pays |
deposit 3 | Paid: COMPLETED about 5 minutes after the payer pays, with no notification (poll the status) |
deposit 4 | Refused at once: INVALID_CARD_NUMBER, no deposit |
deposit 5 | CANCELLED about 3 minutes after the payer pays (MOCK_ERROR) |
deposit 6 | FOR_REVIEW about 1 minute after the payer pays (terminal), then refunded automatically |
deposit 41 | Refused at once: INSUFFICIENT_FUNDS, no deposit |
deposit 51 | CANCELLED about 8 minutes after the payer pays (PROCESSING_ERROR) |
| deposit, any other | Paid: COMPLETED about 1 minute after the payer pays |
refund 1 | COMPLETED about 2 seconds later |
refund 4 | Refused at once: DUPLICATE_TRANSACTION |
refund 5 | CANCELLED about 3 minutes later (MOCK_ERROR) |
refund 6 | COMPLETED about 2 seconds later (the under-review deposit's automatic refund) |
cashout 1 | COMPLETED about 2 seconds later |
cashout 2 | COMPLETED about 3 minutes later |
cashout 3 | COMPLETED about 5 minutes later, with no notification |
cashout 4 | Refused at once: INVALID_BENEFICIARY_ACCOUNT |
cashout 5 | CANCELLED about 3 minutes later (PROCESSING_FAILED) |
cashout 41 | Refused at once: MERCHANT_INSUFFICIENT_BALANCE |
cashout 51 | CANCELLED about 8 minutes later (MOCK_ERROR) |
| cashout document …0800 | REJECTED 800 ERROR_ACCOUNT_INCORRECT (lab rule) |
| cashout document …0812 | REJECTED 812 BLOCKED_FROZEN_ACCOUNT (lab rule) |
| cashout document …0813 | REJECTED 813 DOCUMENT_ACCOUNT_MISMATCH (lab rule) |
| Country | Codes |
|---|---|
| BR | 1 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 |
| MX | 2 BANAMEX, 12 BBVA MEXICO, 14 SANTANDER, 21 HSBC, 72 BANORTE, 127 AZTECA, 646 STP |
| CO | 1007 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'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.
POST /zota/api/v1/deposit/request/{EndpointID}/: an order, CREATED, and its depositUrl, Zota's payment form
(/zota/api/v1/deposit/init/{orderID}/), where the payer picks a local bank, the country's QR code, an e-wallet or mobile
money. An order nobody submits is DECLINED after 20 minutes.GET /zota/api/v1/query/order-status/?merchantID&merchantOrderID&orderID×tamp&signature (a timestamp
older than 15 minutes is refused); GET /zota/api/v1/query/current-balance/.POST /zota/api/v1/payout/request/{EndpointID}/ to a local bank account (customerBankCode,
customerBankAccountNumber, customerBankAccountName; a wallet's or mobile money's code and number):
AUTHORIZED, PROCESSING a second later, APPROVED three seconds after that. The Payout Form ("none") is not enabled.redirectUrl with status, orderID, merchantOrderID and
signature = SHA-256(status + orderID + merchantOrderID + secret); every final status is POSTed to callbackUrl
with signature = SHA-256(EndpointID + orderID + merchantOrderID + status + amount + customerEmail + secret). The lab's
published secret is BISTROMATHIC-LAB-ZOTA-SIMULATION-SECRET.?device=mobile|desktop forces
either flow.GET|PUT /zota-lab/endpoints/{EndpointID} {"merchant_name"} (lab only): the merchant's name on the form, as
Zota sets it at onboarding. Another EndpointID than the published ones takes the currency of its first order.| Country | Currency | Endpoints | Limits | Banks | QR | Wallets, mobile money |
|---|---|---|---|---|---|---|
| TH | THB | 1050 / 2050 | 100–200,000 | BBL Bangkok Bank, KBANK Kasikornbank, SCB Siam Commercial Bank, KTB Krungthai Bank, BAY Krungsri, TTB TMBThanachart | PROMPTPAY PromptPay QR | TRUEMONEY TrueMoney Wallet |
| VN | VND | 1051 / 2051 | 50,000–300,000,000 | VCB Vietcombank, TCB Techcombank, BIDV BIDV, CTG VietinBank, ACB ACB, MB MB Bank | VIETQR VietQR | MOMO MoMo, ZALOPAY ZaloPay |
| MY | MYR | 1052 / 2052 | 10–50,000 | MBB Maybank, CIMB CIMB Bank, PBB Public Bank, RHB RHB Bank, HLB Hong Leong Bank | DUITNOW DuitNow QR | TNG Touch 'n Go eWallet, BOOST Boost |
| ID | IDR | 1053 / 2053 | 10,000–100,000,000 | BCA Bank Central Asia (BCA), MANDIRI Bank Mandiri, BNI BNI, BRI BRI, CIMBN CIMB Niaga | QRIS QRIS | OVO OVO, DANA DANA, GOPAY GoPay |
| PH | PHP | 1054 / 2054 | 100–500,000 | BDO BDO Unibank, BPI BPI, MBTC Metrobank, UBP UnionBank, LBP Landbank | QRPH QR Ph | GCASH GCash, MAYA Maya |
| IN | INR | 1055 / 2055 | 100–200,000 | HDFC HDFC Bank, ICICI ICICI Bank, SBI State Bank of India, AXIS Axis Bank, KOTAK Kotak Mahindra Bank | UPI UPI QR | PAYTM Paytm, PHONEPE PhonePe |
| CN | CNY | 1056 / 2056 | 100–50,000 | ICBC ICBC, CCB China Construction Bank, ABC Agricultural Bank of China, BOC Bank of China, CMB China Merchants Bank | UNIONPAYQR UnionPay QR | ALIPAY Alipay, WECHAT WeChat Pay |
| NG | NGN | 1057 / 2057 | 1,000–5,000,000 | ACCESS Access Bank, GTB GTBank, ZENITH Zenith Bank, FBN First Bank, UBA UBA | — | OPAY OPay, PALMPAY PalmPay |
| KE | KES | 1058 / 2058 | 100–250,000 | EQUITY Equity Bank, KCB KCB Bank, COOP Co-operative Bank | — | MPESA M-PESA, AIRTEL Airtel Money |
| GH | GHS | 1059 / 2059 | 10–50,000 | GCB GCB Bank, ECOBANK Ecobank Ghana, STANBIC Stanbic Bank | — | MTN MTN MoMo, TELECEL Telecel Cash, ATMONEY AT Money |
| EG | EGP | 1060 / 2060 | 50–100,000 | NBE National Bank of Egypt, CIB CIB, BANQUEMISR Banque Misr | INSTAPAY InstaPay QR | VFCASH Vodafone Cash |
| MX | MXN | 1061 / 2061 | 50–100,000 | BBVA BBVA México, BANORTE Banorte, SANTANDER Santander, BANAMEX Banamex | CODI CoDi | MERCADOPAGO Mercado Pago |
| BR | BRL | 1062 / 2062 | 10–50,000 | ITAU Itaú, BRADESCO Bradesco, BB Banco do Brasil, NUBANK Nubank | PIX Pix | PICPAY PicPay |
| Amount | Effect |
|---|---|
| deposit …13 | PROCESSING for a minute after the payer approves, then APPROVED |
| deposit …21 | UNKNOWN for two minutes after the payer approves (not final), then APPROVED |
| deposit …33 | APPROVED with no callback: only the order status (or reconciliation) tells |
| deposit …02 | DECLINED once the payer approves: insufficient funds at the bank |
| deposit …03 | FILTERED once the payer approves: declined by Zota's fraud prevention |
| deposit …04 | ERROR once the payer approves: a processor error (final) |
| payout …13 | PROCESSING for two minutes, then APPROVED |
| payout …02 | DECLINED: invalid bank account |
| payout …03 | FILTERED: declined by fraud prevention |
| payout …04 | ERROR: 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'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.
POST /ppro/v1/payment-charges (EPS, P24, BLIK, BANCONTACT, TWINT,
ALIPAY), GET /ppro/v1/payment-charges[/{id}], POST …/{id}/authorizations (BLIK's code),
…/refunds, …/captures and …/voids (none of these methods has separate captures: an approved charge is
CAPTURED at once), GET /ppro/v1/payment-instruments/{id} (eps reports the payer's IBAN and name)/ppro/checkout/{token} — PPRO's hosted page (REDIRECT's requestUrl): eps' bank picker, the bank's login (PSA BANK GROUP:
1006044 / PPRO1234$$) and TAN; Przelewy24's bank picker and the dummy "My Bank"; BLIK's code entry and the push in the
banking app; Bancontact's, TWINT's and Alipay's real QR code with the app on the phone drawn beside it (a real phone's camera works too:
/ppro/q/{token}), the app at once on a phone, and Bancontact's card form. ?device=mobile|desktop forces either flow.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.
| Method | Name | Consumers | Currencies | Authentication | Timeout |
|---|---|---|---|---|---|
BANCONTACT | Bancontact | BE | EUR | REDIRECT SCAN_CODE APP_INTENT | 60 min |
BLIK | BLIK | PL | PLN | REDIRECT MULTI_FACTOR | 30 min |
EPS | eps Überweisung | AT | EUR | REDIRECT | 60 min |
P24 | Przelewy24 | PL | EUR, PLN | REDIRECT | 60 min |
TWINT | TWINT | CH | CHF | REDIRECT SCAN_CODE | 60 min |
ALIPAY | Alipay | CN | AUD, CAD, CHF, CNY, EUR, GBP, HKD, JPY, NZD, SGD, USD | REDIRECT | 720 min |
| Value | Effect |
|---|---|
BLIK code 999009, 777123 (777xxx), or the code the lab's banking app shows | accepted; the payer confirms in the app |
BLIK code 999001 AUTHENTICATION_FAILED, 999002 INSUFFICIENT_FUNDS | FAILED |
Bancontact 60600599999899023 / 60600599999899080, 01/2035 | CAPTURED / FAILED, AUTHENTICATION_FAILED |
| 3000.00 – 3999.99 (the mock authenticator) | FAILED after the payer approves: …02 INSUFFICIENT_FUNDS, otherwise GENERIC_DECLINE |
| …13, from 10.00 | AUTHORIZATION_ASYNC, then CAPTURED when the provider confirms 20 seconds later |
| …14, from 10.00 | AUTHORIZATION_ASYNC, then FAILED 20 seconds later |
| …53, from 10.00 | 504 from the create: nothing is created |
| …05, from 10.00 | the bank declines after the payer approves (GENERIC_DECLINE) |
| …06, from 10.00 | the bank declines after the payer approves (INSUFFICIENT_FUNDS) |
| …07, from 10.00 | FAILED at once: PPRO's risk check (BLOCKED_ACCOUNT) |
| refund …77 | PENDING, 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.
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.
https://eu-test.oppwa.com (test), https://eu-prod.oppwa.com (live)https://sandbox-card.peachpayments.com (test), https://card.peachpayments.com (live)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": …}.
POST /oppwa/v1/payments — PA, DB or CD with card.*; with shopperResultUrl, an enrolled card answers 000.200.000 pending with redirect (the issuer's challenge on the shared ACS page, after a 3DS Method in a hidden iframe), and the payer comes back to shopperResultUrl?id=…&resourcePath=/v1/payments/…. Unanswered for 30 minutes, the payment times out (100.396.103). createRegistration=true stores the card (registrationId)POST /oppwa/v1/payments/{id} — CP (capture a PA, partial or full), RF (refund a DB, CP or CD, partial or full), RV (reverse a PA, DB or CD with nothing captured or refunded)GET /oppwa/v1/payments/{id}?entityId=… — the payment; two reads a minute per payment, then 800.120.100 (HTTP 429), as OPPWA throttles itPOST /oppwa/v1/registrations, POST /oppwa/v1/registrations/{id}/payments, DELETE /oppwa/v1/registrations/{id} — tokensGET /oppwa/v3/query/{id} (100 a minute; includeLinkedTransactions, paymentTypes), GET /oppwa/v3/query?merchantTransactionId=… (30 a minute), and the v1 queries (2 a minute)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.
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.
| Card | Brand | Outcome |
|---|---|---|
4200000000000000 | VISA | Not enrolled (no 3-D): successful |
4711100000000000 | VISA | 3-D enrolled: the issuer's challenge |
4012888888881881 | VISA | 3-D enrolled: the issuer's challenge |
4111111111111111 | VISA | Simulates external mode (no 3-D here): successful |
4242424242424242 | VISA | 3-D disabled: successful |
5454545454545454 | MASTER | Not enrolled (no 3-D): successful |
5212345678901234 | MASTER | 3-D enrolled: the issuer's challenge |
5105105105105100 | MASTER | 3-D disabled: successful |
377777777777770 | AMEX | Not enrolled (no 3-D): successful |
375987000000005 | AMEX | 3-D enrolled: the issuer's challenge |
30082246403846 | DINERS | 3-D disabled: successful |
4200000000000091 | VISA | 3-D Secure 2 frictionless (Y): successful |
5200000000000007 | MASTER | 3-D Secure 2 frictionless (Y): successful |
6761301000993772 | MAESTRO | 3-D Secure 2 frictionless (Y): successful |
374500262001008 | AMEX | 3-D Secure 2 frictionless (Y): successful |
4200000000000109 | VISA | 3-D Secure 2 frictionless attempt (A): successful |
5200000000000023 | MASTER | 3-D Secure 2 frictionless attempt (A): successful |
6706981111111113 | MAESTRO | 3-D Secure 2 frictionless attempt (A): successful |
377277081382243 | AMEX | 3-D Secure 2 frictionless attempt (A): successful |
4200000000000026 | VISA | 3-D Secure 2 frictionless (Y): successful (no 3DS Method) |
5200000000000056 | MASTER | 3-D Secure 2 frictionless (Y): successful (no 3DS Method) |
6799851000000032 | MAESTRO | 3-D Secure 2 frictionless (Y): successful (no 3DS Method) |
375987000000062 | AMEX | 3-D Secure 2 frictionless (Y): successful (no 3DS Method) |
4200000000000059 | VISA | 3-D Secure 2 frictionless attempt (A): successful (no 3DS Method) |
5200000000000106 | MASTER | 3-D Secure 2 frictionless attempt (A): successful (no 3DS Method) |
6007930123456780 | MAESTRO | 3-D Secure 2 frictionless attempt (A): successful (no 3DS Method) |
373953192351004 | AMEX | 3-D Secure 2 frictionless attempt (A): successful (no 3DS Method) |
4012001037461114 | VISA | 3-D Secure 2 technical error (U): rejected |
5434580000000006 | MASTER | 3-D Secure 2 technical error (U): rejected |
6761301000941201 | MAESTRO | 3-D Secure 2 technical error (U): rejected |
375987000169875 | AMEX | 3-D Secure 2 technical error (U): rejected |
4012001037141112 | VISA | 3-D Secure 2 user not enrolled (N): rejected |
5457350076543210 | MASTER | 3-D Secure 2 user not enrolled (N): rejected |
6761301000946341 | MAESTRO | 3-D Secure 2 user not enrolled (N): rejected |
375987000169883 | AMEX | 3-D Secure 2 user not enrolled (N): rejected |
4532497088771651 | VISA | 3-D Secure 2 card not participating: rejected |
5497260847316287 | MASTER | 3-D Secure 2 card not participating: rejected |
6761257707836567 | MAESTRO | 3-D Secure 2 card not participating: rejected |
343923092050144 | AMEX | 3-D Secure 2 card not participating: rejected |
4200000000000042 | VISA | 3-D Secure 2 challenge: the cardholder decides |
5200000000000015 | MASTER | 3-D Secure 2 challenge: the cardholder decides |
6799998900000060018 | MAESTRO | 3-D Secure 2 challenge: the cardholder decides |
343434343434343 | AMEX | 3-D Secure 2 challenge: the cardholder decides |
4200000000000067 | VISA | 3-D Secure 2 challenge: the cardholder decides |
5200000000000049 | MASTER | 3-D Secure 2 challenge: the cardholder decides |
6773670009114879 | MAESTRO | 3-D Secure 2 challenge: the cardholder decides |
375987000000021 | AMEX | 3-D Secure 2 challenge: the cardholder decides |
4200000000000018 | VISA | 3-D Secure 2 challenge: the cardholder decides (no 3DS Method) |
5200000000000064 | MASTER | 3-D Secure 2 challenge: the cardholder decides (no 3DS Method) |
67034200554565015 | MAESTRO | 3-D Secure 2 challenge: the cardholder decides (no 3DS Method) |
375987000169867 | AMEX | 3-D Secure 2 challenge: the cardholder decides (no 3DS Method) |
4200000000000075 | VISA | 3-D Secure 2 challenge: the cardholder decides (no 3DS Method) |
5200000000000072 | MASTER | 3-D Secure 2 challenge: the cardholder decides (no 3DS Method) |
6759888888888888 | MAESTRO | 3-D Secure 2 challenge: the cardholder decides (no 3DS Method) |
371449635398431 | AMEX | 3-D Secure 2 challenge: the cardholder decides (no 3DS Method) |
| Amount (last two digits) | On | Effect |
|---|---|---|
| …14, from 10.00 | payment | 800.100.151 transaction declined (invalid card) |
| …51, from 10.00 | payment | 800.100.203 Insufficient Funds |
| …52, from 10.00 | credit | 800.100.180 transaction declined (not supported by issuer): the card takes no credits |
| …54, from 10.00 | payment | 800.100.157 transaction declined (wrong expiry date) |
| …57, from 10.00 | payment | 800.100.170 transaction declined (transaction not permitted) |
| …59, from 10.00 | payment | 800.100.159 transaction declined (stolen card) |
| …61, from 10.00 | payment | 800.100.162 transaction declined (limit exceeded) |
| …62, from 10.00 | payment | 800.100.168 transaction declined (restricted card) |
| …77, from 10.00 | refund | 700.400.200 cannot refund |
| …82, from 10.00 | payment | 800.100.153 transaction declined (invalid CVV) |
| …91, from 10.00 | payment | Successful; the payment's webhooks arrive 30 s late |
| …92, from 10.00 | payment | Successful; the payment's webhooks never arrive |
| …05, from 10.00 | payment | 800.100.183 transaction declined (Do not honor) |
redirect, preconditions, shopperResultUrl with resourcePath), the test cards and their 3DS outcomes, the 30-minute pending timeout, the status and query throttles, the webhook format and its AES-256-GCM encryption.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).
POST /paypal/v2/checkout/orders (CAPTURE or AUTHORIZE; with payment_source.paypal.experience_context
it is PAYER_ACTION_REQUIRED with a payer-action link, with application_context CREATED with an
approve link), GET /v2/checkout/orders/{id}, POST …/{id}/capture, POST …/{id}/authorizeGET /paypal/v2/payments/authorizations/{id}, POST …/capture, POST …/void;
GET /v2/payments/captures/{id}, POST …/refund; GET /v2/payments/refunds/{id}POST /paypal/v1/payments/payouts (201, batch PENDING; EMAIL, PAYPAL_ID or PHONE
receivers; a sender_batch_id is unique for 30 days), GET /v1/payments/payouts/{id},
GET /v1/payments/payouts-item/{id}, POST …/payouts-item/{id}/cancel (an UNCLAIMED item is RETURNED)POST|GET /paypal/v1/notifications/webhooks, GET|DELETE …/webhooks/{id},
POST /v1/notifications/verify-webhook-signature, GET /v1/notifications/certs/CERT-360caa42-fca2a594-b15720a6/paypal/checkoutnow?token={order id}&sid=…: PayPal's pages. On a computer the login (email, then any password or a
one-time code texted to the phone drawn beside it), the review with the funding source (PayPal balance or the card) and Agree & Pay,
then return_url?token=…&PayerID=…; Cancel goes to cancel_url?token=…. On a phone the PayPal app opens (an app
switch: Face ID, the review, Pay), or the payer logs in on the web. ?device=mobile|desktop forces either flow.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).
| Payer id | Effect | |
|---|---|---|
sb-buyer@personal.example.com | 4NC59AJ93HPM6 | The sandbox's personal account: a PayPal balance of 5,000.00 and a Visa. Pays; payouts back succeed |
declined@personal.example.com | 3SLEDCFBAVXHA | Only a Visa that declines: the capture fails with INSTRUMENT_DECLINED |
cannot.pay@personal.example.com | LWEWER3WEGHS7 | Approves, but the capture fails with PAYER_CANNOT_PAY |
review@personal.example.com | 9F4DWRQNQ8C4E | The capture is PENDING (PENDING_REVIEW) for a minute, then COMPLETED |
limited@personal.example.com | KUNDS7XP2A3YH | The login is refused: the account is limited |
otp@personal.example.com | NR66CYVKERVEG | Always asks for the one-time code texted to the phone |
payout.locked@personal.example.com | 32YVBPFSZ2HBE | Pays; a payout back to it fails (RECEIVER_ACCOUNT_LOCKED) |
payout.unclaimed@personal.example.com | DLKKG2RME4R24 | Pays; a payout back to it stays UNCLAIMED (RECEIVER_UNREGISTERED) until it is cancelled, or returned after 30 days |
| any other email | its own | A fresh, verified account with 5,000.00 in any currency |
| Deposit amount | Effect |
|---|---|
| …13, from 10.00 | The capture is PENDING (PENDING_REVIEW) for a minute, then COMPLETED |
| …05, from 10.00 | The capture fails with INSTRUMENT_DECLINED (422) |
| …09, from 10.00 | The 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, 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).
POST /gigadat/api/payment-token/{campaignId} (type CPI, ETI or RFM for a deposit, ETO for an e-Transfer out;
userId, transactionId, name, email, mobile, site, userIp,
currency CAD, amount, language, sandbox) → {token, data: {transactionId, type}};
refusals {err} (400, 404, 409 for a reused transactionId)GET /gigadat/webflow?transaction=…&token=… — the payer: Interac Request Money or e-Transfer (CPI), the bank picker
(16 Canadian banks on plain monograms), the bank's login (any), the request to accept (or the pre-filled e-Transfer) from Chequing
or Savings, the review, Confirm; then the success or failure URL with ?transaction=&status=. On a computer the bank's app runs on the
phone drawn beside the page; on a phone it fills the screen. ?device=mobile|desktop forces either.GET /gigadat/api/transactions/{id} → {id, transactionId, type, finalType, status, amount, currency, interac_bank_name,
data: {name, email, mobile}}POST /gigadat/refunds {amount, transaction_id, campaign_id} (Idempotency-Key) → {success, data: {transactionId},
amount, currency}; refusals {error: [{code, detail}], message} (404, 422 NOT_REFUNDABLE, AMOUNT_EXCEEDS)POST /gigadat/webflow?transaction=&token= (created), GET
/gigadat/webflow/deposit?transaction=&token= (sent: STATUS_PENDING) → {id, status, data: {transactionId, type}}. The payee
deposits it from /gigadat/inbox?to=<email>: the notification, Deposit your money, their bank, the
account. Not deposited in 30 days: STATUS_EXPIRED.GET/PUT /gigadat-lab/campaigns/{campaignId} {merchant_name, listener_url, success_url, failure_url} —
lab only: the campaign's settings in Gigadat's back office, used when a request names none.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).
| Deposit amount | Effect |
|---|---|
| …13, from 10.00 | STATUS_PENDING for a minute after the payer confirms, then STATUS_SUCCESS |
| …33, from 10.00 | STATUS_SUCCESS with no listener notification: only the transaction lookup (or reconciliation) tells |
| …02, from 10.00 | STATUS_REJECTED once the payer confirms: the bank declined (insufficient funds) |
| …03, from 10.00 | STATUS_REJECTED1 once the payer confirms: Gigadat's checks (the name Interac reports is not the player's) |
| …04, from 10.00 | STATUS_FAILED once the payer confirms: a technical failure |
| Payout amount | Effect |
|---|---|
| …01, from 10.00 | The payee has Autodeposit: deposited two seconds after it is sent (STATUS_SUCCESS) |
| …02, from 10.00 | STATUS_REJECTED: the payee's bank refused the e-Transfer |
| …04, from 10.00 | STATUS_FAILED: a technical failure |
| any other | STATUS_PENDING until the payee deposits it (inbox) or declines it |
| Refund amount | Effect |
|---|---|
| …05, from 10.00 | 422 REFUND_REJECTED: Gigadat refuses the refund |
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.
POST /aeropay/v2/user, /v2/confirmUser (any code sent, or 000000), GET /v2/userGET /aeropay/v2/aggregatorCredentials?aggregator=aerosync, POST /v2/linkAccountFromAggregator, GET /v2/bankAccounts,
PATCH /v2/userBankAccount/{id}POST /aeropay/v2/transaction (with exceptionScenario), GET /v2/transaction/{id}, GET /v2/transaction/{id}/refunds,
POST /v2/reverseTransaction, POST /v2/payoutTransaction (rtp to an RTP bank is processed at once); an
Idempotency-Key answers the first response again for 24 hoursPOST|GET|DELETE /aeropay/v2/webhook, POST /v2/createWebhookSigningKey/aeropay/checkout?merchant=…&location=…&uuid=…&total=12.34&redirect=…&payee=… — the checkout the Aeropay SDK opens:
the payer's mobile number or email (a new user signs up), the one-time code (on the drawn phone on a computer, a banner on a phone), their linked
accounts or the Aerosync widget (the bank picker with search, the bank's login, the account), and Pay; then back to redirect with
aeropay=success|error|cancelled|expired, uuid and, for an error, code and message.
?device=mobile|desktop forces either flow.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.
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.
exceptionScenario | Effect |
|---|---|
returnInsufficient | pending, then declined 3 s later: an R01 (Insufficient Funds) return, status nsf; the user is suspended |
returnAccount | pending, then declined 3 s later: an R02 (Account Closed) return; the user is suspended |
returnDispute | pending, then declined 3 s later: an R07 (Customer Revoked Authorization) return; the user is suspended |
delinquent | AP109: the user is rejected for delinquent activity |
riskEngine | AP307: Aeropay's risk engine declines the payment (reasonCodes ["exceptionScenario"]) |
balanceInsufficient | AP302: the user's balance does not cover the payment |
| Checkout amount | Effect |
|---|---|
| …07, from 10.00 | AP307, declined by the risk engine (reasonCodes A1): the checkout shows the decline |
| …02, from 10.00 | AP302 Insufficient funds: the checkout shows the decline |
| …09, from 10.00 | AP109 delinquent activity: the checkout shows the decline |
| …01, from 10.00 | Approved, then an ACH return R01 3 s later (transaction_declined, status nsf) |
| …03, from 10.00 | Approved, then an ACH return R02 3 s later (transaction_declined) |
| …05, from 10.00 | Approved, then an ACH return R07 3 s later (transaction_declined) |
| over 5 000.00 | AP304 |
| Payout amount | Effect |
|---|---|
| …15, from 10.00 | AP315 Payment rejected: threshold or suspended bank |
| …02, from 10.00 | AP302 Insufficient funds |
| …04, from 10.00 | AP304 Transaction amount exceeds limit |
| …09, from 10.00 | pending, then voided 3 s later (transaction_voided: the payout did not go out) |
| over 10 000.00 | AP304 |
| Refund amount | Effect |
|---|---|
| …10, from 10.00 | AP310 Credit transaction declined |
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).
POST /paynearme/oauth/token (client credentials, 24 h). POST /paynearme/json-api/create_order,
get_smart_token (flow_debit, flow_credit, flow_ACH, flow_trustly,
flow_retailer, flow_paypal, flow_venmo; payment_amount with payment_field_fixed;
flow_pay_url, flow_no_pay_url, …), find_orders, cancel_order, create_payment_method,
find_payment_methods, make_payment, find_payment, refund_payment (once per payment),
simulate_payment (cash, sandbox), create_push_order (with a card or bank account and send_payment=true,
or then make_payment with a token).PUT /paynearme-lab/site?site_identifier=… {"callback_url", "site_name"}, the
Business Portal's setting): Confirmation, Push Confirmation and Reverse, as JSON, signed in signature (the sorted parameters,
HMAC-SHA256) with the published secret pnm-lab-callback-secret-3e8b1d07c5. The acknowledgement must name the
order (payment_confirmation_response / payment_reverse_response)./paynearme/ssl/{token}: the MoneyLine page. The method picker when the link shows several; cash: the pay slip with a real
Interleaved 2 of 5 barcode, the stores (7-Eleven, CVS, Walgreens, Walmart, Family Dollar, Dollar General, Casey's, Kroger, Ace Cash Express) and the lab's store counter; bank: the bank
picker, the bank's login and account choice; card: the card form (the lab's card core); PayPal and Venmo: their login.
?device=mobile|desktop forces either flow.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.
| Amount | Code | Message |
|---|---|---|
213.89 | 05 | This card is not permitted to make this payment. |
213.83 | 12 | This payment with this card is invalid. |
213.79 | 14 | This card number is invalid. |
213.73 | 51 | There are insufficient funds on this card to complete this payment. |
213.71 | 54 | This card is expired. |
213.67 | 57 | This card is not permitted to make this kind of payment. |
213.61 | 61 | This payment exceeds this card’s withdrawal limit. (disbursements only) |
213.59 | 62 | This card is restricted and cannot make this payment. |
213.53 | NZ | This payment didn't complete because the security code was invalid. |
172.01 | P9 | Unable to process this payment. Please try a different payment method. |
172.02 | 10 | Unable to process this payment for the requested amount. Please try a different payment method. |
172.03 | 20 | Unable to process this payment right now. Please try again later or use a different payment method. |
172.04 | 60 | This payment could not be authorized. Please contact your card issuer or try a different payment method. |
172.05 | 68 | Unable to receive response from card issuer. Please try again in a few minutes. |
172.06 | 6P | The security information could not be verified. Please check your card information and try again. |
172.07 | SG | Account could not be found. Please check your card information or try a different payment method. |
44.02 | 9G | Your bank didn’t approve this transaction. Please review your account settings or contact your bank. |
44.01 | 5C | Your bank couldn’t approve this transaction. Please contact your bank for more information. |
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.
POST /bancontact/v3/payments (PENDING, with _links.self, deeplink, qrcode, cancel,
checkout), GET and DELETE /bancontact/v3/payments/{id}, POST …/search, …/acknowledge,
GET …/{id}/debtor/refundIban (the payer's IBAN once SUCCEEDED), POST …/{id}/refunds (Idempotency-Key) and
GET …/{id}/refunds/{refundId} (PENDING, then REFUNDED)./bancontact/checkout?paymentId=…&token=… — Bancontact's checkout page: on a computer a Bancontact Pay tab with a real QR code
(a real phone's camera opens /bancontact/pay/2/{id}, and the page follows) and a card tab (card reader or banking app), with the
payer's phone drawn beside it; on a phone a switch to the app, and Bancontact's fallback list of banking apps (Bancontact Pay, KBC Mobile, BNP Paribas Fortis Easy Banking, Belfius, ING Banking, Argenta, Crelan).
?device=mobile|desktop and ?lang=nl|fr|en force either./bancontact/qrcode?c=… — the QR code generator (SVG only), /bancontact/.well-known/jwks — the lab's signing key.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).
| Value | Effect |
|---|---|
5127 8829 9999 9715, 11/29 | Always authorised |
5127 8829 9999 9723, 11/29 | Insufficient funds |
5127 8829 9999 9731, 11/29 | Card refused by the issuer |
| Any other valid card, a future expiry | Authorised |
| …13, from 10.00 | AUTHORIZED after the payer confirms, then SUCCEEDED a minute later |
| …22, from 10.00 | The create answers 422 UNABLE_TO_PAY_CREDITOR: nothing is created |
| …53, from 10.00 | The create answers 503 TRY_AGAIN_LATER: nothing is created |
| …05, from 10.00 | AUTHORIZATION_FAILED after the payer confirms (insufficient funds) |
| …06, from 10.00 | FAILED after the payer confirms (a technical error) |
| refund …05 | PENDING, then FAILED |
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": …}.
POST /sibs/api/v2/payments — the checkout: merchant (terminalId, channel, merchantTransactionId), customer.customerInfo, transaction (transactionTimestamp, paymentType PURS, paymentMethod ["MBWAY"] or ["REFERENCE"], amount, and for a reference paymentReference: initialDatetime, finalDatetime, minAmount, maxAmount, entity). Answers transactionID, transactionSignature, formContext and expiry (30 minutes)POST /sibs/api/v2/payments/{transactionID}/mbway-id/purchase (also /mbway/purchase) — {"customerPhone": "351#912345678"}: the push to the payer's MB WAY app; paymentStatus Pending, four minutes to confirmPOST /sibs/api/v2/payments/{transactionID}/service-reference/generate — the Multibanco reference (an empty body, or expiration_date, amount, currency): paymentReference with entity (the checkout's, else 40200), nine-digit reference, expireDate and status UNPAIDGET /sibs/api/v2/payments/{transactionID}/status — paymentStatus Pending, Success, Declined, Timeout; a refund by its own idPOST /sibs/api/v2/payments/{transactionID}/refund — merchant, transaction.amount, transaction.originalTransaction: MB WAY only, Success at once/sibs/pay/{transactionID} — the merchant's page (lab paths): MB WAY's phone number (+351), then "Confirme o pagamento na app MB WAY" with a 4:00 countdown; Multibanco's voucher with copy buttons. On a computer the payer's phone is drawn beside it: the MB WAY push, the payment with a card or account to choose, the six-digit PIN; or a banking app's "Pagamento de serviços". ?return= is where the page goes when the payment ends (with it the phone number goes back there as sibsPhone, for the merchant's server to send the purchase; without it the page sends the purchase itself, as SPG's payment form does). ?device=mobile|desktop forces either flow/sibs/app/{id}, /sibs/bank/{id} — the MB WAY app and the banking app, full screen; /sibs/atm — a Multibanco ATM: Pagamentos, Pagamento de serviços, Entidade, Referência, Montante| customerPhone | Effect |
|---|---|
351#911111112 | Approved in the app about 30 seconds later |
351#911111113 | Not an MB WAY number: the purchase is refused (E0506, the alias does not exist) |
351#911111114 | SPG declines a few seconds later (10.106.0009) |
351#911111115 | Nobody answers: the request expires (Timeout) about 10 seconds later, the four minutes made short |
351#911111116 | The payer refuses in the app a few seconds later (01.106.0001) |
any other 351#9xxxxxxxx | The 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 mobile | E0506 (the alias does not exist) or E0504 (invalid format) |
initialDatetime to finalDatetime, seven days when the checkout names no window…,12: the reference is cancelled as it is generated — the sandbox's "Expired Reference" scenario (E0612, with its webhook)…,41: no webhook for the outcome; a status inquiry learns it…,66: declined (E0524)X-IBM-Client-Id and Digest authentication, the checkout, purchase, generation, status and refund payloads, paymentStatus and paymentReference.status, the API-level codes (E01xx, E05xx, E06xx) and the detailed ones (01.106.0001, 10.106.0009, 00.110.1601), the webhook payloads and their AES-GCM encryption and acknowledgement, MB WAY's four minutes, the sandbox's Multibanco scenarios (generate 000, Expired Reference E0612).merchantTransactionId values from its Postman collection, which is not public.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.
POST /przelewy24/api/v1/transaction/register (sign over sessionId, merchantId, amount, currency, crc) → {"data":{"token"}}; the
payer goes to /przelewy24/trnRequest/{token}PUT /przelewy24/api/v1/transaction/verify — required after the notification: until then the payment is an advance payment (status 1)
and the notification repeats after 3, 5, 15, 30, 60, 150 and 450 minutesGET /przelewy24/api/v1/transaction/by/sessionId/{sessionId} (status 0 no payment, 1 advance payment, 2 payment made, 3 returned),
GET /payment/methods/{pl|en}?amount=¤cy=, GET /testAccessPOST /przelewy24/api/v1/transaction/refund (201; 409 per item; 400 Request already exists) and
GET /refund/by/orderId/{orderId} (1 completed, 2 pending, 3 awaiting P24's approval, 4 rejected)/przelewy24/express/v1/…: access/check, user/balance, bank/availability, payment/methods,
bank/check?iban=, transfer/request (wallet mode only; p24 and p24mobile 1013), transfer/p24/refund (with
overrefund), transfer/history; the same id again answers 200 with the first resultPATCH /przelewy24/lab/panel {"merchantName"} and PATCH /przelewy24/express/lab/panel {"notificationUrl"}:
the lab's stand-ins for P24's and Ekspres' web panels (the shop's name the payer sees; where Ekspres notifications go)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.
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 amount | Effect |
|---|---|
| …49, from 10.00 | P24 refuses the payment at the panel: err49, the risk-assessment score is too high |
| …51, from 10.00 | The bank refuses the transfer at authorisation (no funds): err53, the payer can try another method |
| …02, from 10.00 | Paid, but the bank's confirmation (and P24's notification) comes 60 seconds later |
| Refund amount | Effect |
|---|---|
| …13, from 10.00 | Accepted, then rejected by P24 (status 4; the refund notification says status 1) |
| …14, from 10.00 | 409: Insufficient funds available |
| Ekspres payout amount | Effect |
|---|---|
| …02, from 10.00 | 400, error 2002: insufficient funds in the wallet |
| …09, from 10.00 | 400, error 2009: transfer limit exceeded |
| …03, from 10.00 | Paid (4), then 3: error while sending to processing; the money goes back to the wallet |
| …07, from 10.00 | Paid (4), sent (2), then 7: the bank refused it; the money goes back to the wallet |
| below 1.00 / over 500,000.00 PLN | 1008 / 2003 |