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).
Basic test cards
The sandbox test adapter determines the outcome based on the last 4 digits of the card number. Use any Luhn-valid card number — only the suffix matters.
| 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 | Redirects to 3DS challenge page |
| Decline — insufficient funds | 4242 4242 4242 0034 | Payment fails (INSUFFICIENT_FUNDS) |
| Decline — do not honor | 4242 4242 4242 0042 | Payment fails (DO_NOT_HONOR) |
| Pending (async) | 4242 4242 4242 0059 | Payment enters async processing |
| Gateway timeout | 4242 4242 4242 0091 | Retryable provider error (GATEWAY_TIMEOUT) |
Magic card rules
The test adapter determines the outcome based on the last 4 digits of the card number. Use any Luhn-valid PAN — the prefix does not matter, only the suffix.
| Last 4 Digits | Outcome | Description |
|---|---|---|
0000 | Success | Payment approved immediately |
0018 | 3DS Required | Returns paymentAction.type = 'redirect' with sandbox 3DS challenge URL |
0026 | Success (variant) | Payment approved immediately (alternate success card) |
0034 | Decline — Insufficient Funds | Payment fails with error code INSUFFICIENT_FUNDS |
0042 | Decline — Do Not Honor | Payment fails with error code DO_NOT_HONOR |
0059 | Pending | Payment enters async processing state, resolves via callback |
0091 | Error — Gateway Timeout | Returns a retryable GATEWAY_TIMEOUT error |
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.miracle.com/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