Test Cards
Use these test card numbers in sandbox to simulate different payment scenarios. All test cards only work with test API keys (sk_test_ / pk_test_).
For all test cards: use any future expiry date (e.g., 12/30) and any 3-digit CVC (e.g., 123).
Test cards
The sandbox test adapter picks the outcome from the last 4 digits.
| Scenario | Card Number | Expected Result |
|---|---|---|
| Successful payment | 4242 4242 4242 0000 | Payment succeeds (APPROVED) |
| Successful payment (variant) | 4242 4242 4242 0026 | Payment succeeds (APPROVED) |
| 3DS required | 4242 4242 4242 0018 | Enters pending_user_action, returns paymentAction.type = 'redirect' to the 3DS challenge page |
| Decline — insufficient funds | 4242 4242 4242 0034 | Payment fails with INSUFFICIENT_FUNDS |
| Decline — do not honor | 4242 4242 4242 0042 | Payment fails with DO_NOT_HONOR |
| Pending (async) | 4242 4242 4242 0059 | Enters async processing, resolves via callback |
| Gateway timeout | 4242 4242 4242 0091 | Retryable provider error (GATEWAY_TIMEOUT) |
Use these exact numbers, prefix included. On a hosted payment page the card never reaches the adapter — the Vault tokenizes it first, and only the token travels on. The Vault recognises the reserved 4242 4242 4242 ____ range and carries the scenario inside the token it issues; any other PAN gets an ordinary random token, and the payment succeeds no matter what it ends with.
Calling the API directly with a raw PAN or a tok_test_NNNN token you built yourself, the suffix alone is enough — the adapter reads the last four digits. The prefix rule is what makes the two paths behave the same, so follow it either way.
Payment lifecycle per scenario
Each scenario produces a different sequence of status transitions. Use these to test your webhook handling and UI state management.
Successful payment:
created --> processing --> succeededDeclined payment:
created --> processing --> failed3DS required (success):
created --> processing --> pending_user_action --> processing --> succeededWhen a payment enters pending_user_action, the API response includes a paymentAction with type: 'redirect' and a sandbox 3DS challenge URL. After the cardholder completes the challenge, call POST /v1/payments/{id}/complete-action with redirectResult to resume processing.
3DS required (failed):
created --> processing --> pending_user_action --> failedPending (async):
created --> processing --> pending --> succeededThe pending state resolves automatically via a test callback after a short delay.
3DS test scenarios
Use the card suffix 0018 to trigger a 3DS redirect. The payment enters pending_user_action status and the response includes a paymentAction with type: 'redirect' pointing to the sandbox 3DS challenge page.
After the cardholder completes (or abandons) the challenge, call POST /v1/payments/{id}/complete-action with a redirectResult value. The test adapter maps redirectResult to a realistic 3DS outcome:
redirectResult value | Outcome | Description |
|---|---|---|
success | Payment succeeds | 3DS challenge authenticated. Liability shift = yes. |
failure | Payment fails | 3DS challenge failed (not_authenticated). Error: AUTHENTICATION_REQUIRED. |
rejected | Payment fails | 3DS authentication explicitly rejected by issuer. Error: AUTHENTICATION_REQUIRED. |
attempted | Payment succeeds | 3DS challenge attempted but not fully authenticated. Liability shift = no. |
frictionless | Payment succeeds | 3DS performed silently — no challenge shown. Liability shift = yes. |
unavailable | Payment succeeds | 3DS service unavailable. Proceeds without 3DS. |
not_enrolled | Payment succeeds | Card not enrolled in 3DS. Proceeds without 3DS. |
The sandbox 3DS challenge page is hosted locally at http://localhost:3002/3ds-challenge during development. It presents buttons for each redirectResult value so you can simulate different 3DS outcomes.
Refund test scenarios
Refunds in sandbox follow the same flow as live. Create a payment with a success card, then refund it:
# 1. Create a successful payment (card ending 0000)
# 2. Refund it
curl -X POST https://<api-host>/v1/payments/pay_abc123/refunds \
-H "Authorization: Bearer sk_test_your_secret_key" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: test-refund-001" \
-d '{
"reason": "Testing refund flow"
}'Refund status transitions in sandbox:
pending --> processing --> succeededYou will receive refund.created and refund.succeeded webhook events, identical to live.
Magic amounts
Magic amounts are not currently active. Only magic card suffixes control test outcomes. The rules below describe the planned magic amount behavior.
In addition to card numbers, the test adapter will also check the last two digits of the amount (in minor units) for special behavior:
| Amount ending | Outcome | Description |
|---|---|---|
66 | Decline | Amount-based decline trigger |
77 | 3DS required | Amount-based 3DS trigger |
For example, an amount of 1066 (10.66 USD) will trigger a decline regardless of the card number used.
When both a magic card rule and a magic amount rule apply, the card rule will take precedence.
Important notes
- Test cards only work with test API keys (
sk_test_). Using them with a live key will result in a rejected transaction. - Never use real card numbers in sandbox. The sandbox is not PCI-scoped for real card data. Use the test card numbers on this page.
- Test cards are rejected in live mode. When you switch to live keys, you must use real card numbers.
Related
- Sandbox — how the sandbox environment works
- Environments — sandbox vs. live comparison
- 3D Secure — 3DS integration guide
- Refunds — full refund documentation