# Test mode & test cards (/en/get-started/test-mode)

Build and test your XPay integration end to end without moving real money.

Test mode is a fully isolated environment that mirrors live: same API surface, same webhook payloads, same dashboard UI, separate everything else. New accounts start in test mode and stay there until your business is approved for live payments.

Test mode is where you build, test, and break things. Nothing you do in test mode touches a real card, your live customers, or your live balance.

## What's separate, what's the same [#whats-separate-whats-the-same]

| In test mode                                         | In live mode                             |
| ---------------------------------------------------- | ---------------------------------------- |
| `pk_test_...` and `sk_test_...` API keys             | `pk_live_...` and `sk_live_...` API keys |
| Test customers, transactions, payouts                | Live customers, transactions, payouts    |
| Test balance ledger, separate from live              | Live balance ledger                      |
| Test webhook endpoints (set up separately)           | Live webhook endpoints                   |
| **Same** API request and response shapes             | Same                                     |
| **Same** webhook events and the data they carry      | Same                                     |
| **Same** dashboard UI, with a banner and orange tint | Same UI, no banner                       |

When you graduate to live mode, you don't change your code. You just swap your test keys for live keys and set up live webhook endpoints.

## What you can test [#what-you-can-test]

* **Card payments** with predictable success and failure outcomes (see the table below).
* **3D Secure flows**, including success, failure, cancellation, and timeout (via the ACS emulator).
* **Refunds**, full or partial, from the dashboard or the API.
* **Webhooks** to a real or local endpoint, including retries and replay from the dashboard.
* **The full hosted checkout, drop-in, and Elements UI** end to end.
* **Payouts**, simulated on the test schedule.

## Test cards [#test-cards]

XPay accepts four test cards in test mode. The card number determines the issuer and the brand; the **expiry date** determines the payment outcome.

| Brand      | Issuer                 | Card number           | Currency |
| ---------- | ---------------------- | --------------------- | -------- |
| Mastercard | National Bank of Egypt | `5123 4500 0000 0008` | EGP      |
| Visa       | Banque Misr            | `4508 7500 1574 1019` | EGP      |
| Mastercard | Credit Agricole Egypt  | `5111 1111 1111 1118` | EGP      |
| Mastercard | International          | `2223 0000 0000 0007` | USD      |

Any other card number is rejected in test mode. The international card is the one to use when you want to test the international fee tier.

## Payment outcome by expiry [#payment-outcome-by-expiry]

In test mode the **expiry date** decides whether the payment succeeds or fails, and how it fails. The card number itself does not.

| Expiry  | Outcome                 |
| ------- | ----------------------- |
| `01/39` | APPROVED                |
| `05/39` | DECLINED                |
| `01/27` | INSUFFICIENT\_FUNDS     |
| `04/27` | EXPIRED\_CARD           |
| `08/28` | TIMED\_OUT              |
| `01/37` | ACQUIRER\_SYSTEM\_ERROR |
| `02/37` | UNSPECIFIED\_FAILURE    |
| `05/37` | UNKNOWN                 |

Use `01/39` for the happy path. Use the others to validate how your code handles real-world failures: the Payment Intent moves to `payment_failed`, the Charge gets a `failure_code`, and you receive the matching webhooks (`payment_intent.payment_failed`, `charge.failed`).

## CVV outcome by CVV value [#cvv-outcome-by-cvv-value]

The **CVV** controls the CVV match result reported on the resulting Charge.

| CVV   | Result         |
| ----- | -------------- |
| `100` | MATCH          |
| `101` | NOT\_PROCESSED |
| `102` | NO\_MATCH      |

Use `100` unless you're testing CVV-mismatch handling.

## 3D Secure in test mode [#3d-secure-in-test-mode]

Every test payment runs the full 3D Secure 2 challenge flow through XPay's sandbox emulator. After you submit the card form, an ACS (Access Control Server) page pops up where you choose the authentication outcome:

* **Success.** Finishes the payment along the happy path.
* **Rejected.** The issuer denies the challenge. The payment fails with `authentication_required` or `card_declined` depending on the bank's response.
* **Cancelled.** The customer dismisses the challenge.
* **Timeout.** No response within the issuer's window.

This is how you test the 3DS-failure branches of your integration without finding a real failing card.

## Where to next [#where-to-next]

<Cards>
  <Card icon="<Rocket />" title="Quickstart" href="/get-started/quickstart">
    Haven't run a test payment yet? Take one end to end in five minutes.
  </Card>

  <Card icon="<GitBranch />" title="Choose your integration" href="/get-started/choose-your-integration">
    Pick the integration pattern you'll build with for real.
  </Card>

  <Card icon="<Webhook />" title="Set up a webhook endpoint" href="/integrate/webhooks/setting-up-an-endpoint">
    Subscribe to test events and verify the signature before you deploy.
  </Card>

  <Card icon="<Cable />" title="Local webhook development" href="/integrate/webhooks/local-development">
    Tunnel test webhooks to your laptop while you build.
  </Card>
</Cards>