Test Cards
Use these card numbers in test mode to simulate different payment outcomes. Any future expiry date and any 3-digit CVC will work (Amex takes a 4-digit CID).
These are the canonical industry numbers supported across major card-acceptance sandboxes (Stripe, Adyen, Worldpay, etc.). The same numbers work whether your sandbox merchant routes through a real-processor sandbox OR the Vonpay-owned sandbox iframe served via vora.js for embedded checkout.
For the hosted-checkout sandbox today (mock gateway), outcomes are chosen by session amount — see Sandbox & Test Mode → Sandbox outcomes.
For the embedded-checkout sandbox (sandbox iframe via vora.js), outcomes are chosen by card number per the table below. The sandbox iframe locks down to these numbers only — any other input is rejected, both as a real-card safety lock and to keep outcomes deterministic. Embedded fields are collected and submitted through the canonical collection path — const elements = vora.elements.create(); const card = elements.create("card", {}); card.mount("#card-element"); const result = await elements.submit(); — not a single-field tokenize() call.
Card Numbers
| Card | Brand | Outcome | Webhook event | Use for |
|---|---|---|---|---|
4242 4242 4242 4242 | Visa | Success | payment_intent.succeeded | Happy path |
5555 5555 5555 4444 | Mastercard | Success | payment_intent.succeeded | Mastercard testing |
3782 822463 10005 | Amex | Success | payment_intent.succeeded | Amex testing |
4000 0000 0000 0002 | Visa | Generic decline | payment_intent.failed (failure_code: card_declined) | Generic error handling |
4000 0000 0000 9995 | Visa | Insufficient funds | payment_intent.failed (failure_code: insufficient_funds) | Buyer-fundable error path |
4000 0000 0000 0069 | Visa | Expired card | payment_intent.failed (failure_code: expired_card) | Re-prompt UX |
4000 0000 0000 0119 | Visa | Processing error | payment_intent.failed (failure_code: processing_error) | Transient-failure UX |
4000 0027 6000 3184 | Visa | 3DS required → success | payment_intent.succeeded | Happy-path 3DS |
4000 0084 0000 0029 | Visa | 3DS required → fail (fraud) | payment_intent.failed (failure_code: fraudulent) | 3DS challenge rejection |
Synthetic token shape (embedded sandbox)
When the embedded sandbox submits a test card and the charge succeeds, the result carries a final synthetic vp_pmt_test_* token (tokenIsFinal: true) with the outcome encoded in the middle segment. The sandbox iframe mints this synthetic token on every success branch — it does not emulate the production binder's guest charge-only path. Still read the submit result defensively as the 3-way { token } | { charged: true } | { error } union, never result.token unconditionally, so the same handler works against a live binder:
| Card outcome | Token shape |
|---|---|
| Success | vp_pmt_test_success_<random> |
| Decline | vp_pmt_test_decline_<reason>_<random> (e.g. vp_pmt_test_decline_insufficient_funds_abc123) |
| 3DS required | vp_pmt_test_3ds_success_<random> or vp_pmt_test_3ds_fail_<random> |
The encoded outcome means the server-side mock binder recognises the intended payment-intent result from the token prefix alone — no DB lookup, no card-number round-trip. The canonical map is in the @vonpay/test-cards workspace package; both the iframe and the server import from there so the list cannot drift.
vp_pmt_test_* tokens are sandbox-only — a test token used with a live-mode key is rejected with HTTP 400 payment_method_mode_mismatch (the cross-mode guard). (Don't confuse this with payment_method_inactive, which is HTTP 422 and means the token was revoked.)
Lifecycle ops with synthetic tokens
Every existing payment-intent lifecycle op works against a synthetic success token. The same mock binder that handles hosted sandbox today extends to embedded via the token-prefix recognition:
| Operation | Sandbox behavior with vp_pmt_test_success_* | Webhook fired |
|---|---|---|
POST /v1/payment_intents (auto-capture) | → succeeded | payment_intent.succeeded then charge.succeeded |
POST /v1/payment_intents (capture_method=manual) | → authorized | payment_intent.succeeded (the authorization; capture is separate) |
POST /v1/payment_intents/:id/capture | → succeeded | charge.succeeded |
POST /v1/payment_intents/:id/void | → voided | payment_intent.cancelled |
POST /v1/refunds | → refund settled | charge.refunded |
Access via MCP
AI agents can retrieve test cards using the vonpay_checkout_list_test_cards tool. See MCP Server for setup.