> ## Documentation Index
> Fetch the complete documentation index at: https://docs.useaxra.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Testing

> Use sandbox API keys, test cards, and local webhook tunnels to fully validate your Axra Pay integration before switching to live credentials.

Before you go live, you should exercise every payment flow your integration supports — successful charges, declines, 3DS challenges, refunds, and webhook delivery — against the Axra sandbox environment. The sandbox uses the same API surface as production, so code that works in testing will work in production without modification. This page walks you through the test environment setup, available test cards, and a pre-launch checklist to make sure nothing is missed.

## Test environment

Axra provides a fully isolated sandbox environment. Test mode does not process real payments and will never charge a real card.

| Setting             | Value                             |
| ------------------- | --------------------------------- |
| Test base URL       | `https://staging-api.useaxra.com` |
| Production base URL | `https://api.useaxra.com/api/v1`  |
| API key prefix      | `bk_test_`                        |

Obtain your sandbox API keys from the **API Keys** section of the [Axra dashboard](https://app.useaxra.com). Test keys are prefixed with `bk_test_` to distinguish them from live keys (`bk_live_`).

<Warning>
  Never use a live API key in a test or development environment, and never use a test API key in production. Test keys are rate-limited differently and will not process real funds.
</Warning>

## Test cards

Use these card numbers in the sandbox to trigger specific payment outcomes. For all test cards, use any future expiry date and any 3-digit CVV.

| Card number           | Result                                 |
| --------------------- | -------------------------------------- |
| `4242 4242 4242 4242` | Successful charge, no 3DS required     |
| `4000 0000 0000 0002` | Card declined                          |
| `4000 0025 0000 3155` | 3DS authentication required — succeeds |
| `4000 0082 6000 3178` | 3DS authentication required — fails    |
| `4000 0000 0000 9995` | Insufficient funds                     |
| `4000 0000 0000 3220` | 3DS2 required                          |

<Tip>
  In test mode, 3DS challenges are auto-completed by the sandbox — you are not redirected to a real bank page. This lets you test the full authentication flow without any manual steps.
</Tip>

## Testing the full checkout flow

Use this flow to verify your end-to-end checkout session integration:

<Steps>
  <Step title="Create a checkout session">
    Call `POST /business/payment/session` with your test API key. Use a valid amount and currency.
  </Step>

  <Step title="Open the checkout URL">
    Open the `checkoutUrl` from the response in a browser. You will see Axra's hosted payment page.
  </Step>

  <Step title="Enter a test card">
    Enter one of the test card numbers from the table above. Use any future expiry date and any 3-digit CVV.
  </Step>

  <Step title="Complete the payment">
    Submit the form. If you used a 3DS card, the sandbox will auto-complete the authentication challenge.
  </Step>

  <Step title="Verify the webhook">
    Confirm that your server receives a `payment.completed` webhook event. Check that you return a `2xx` response within 30 seconds, and that your application correctly updates the order or fulfillment status.
  </Step>
</Steps>

## Testing a direct charge

For server-to-server (S2S) charging, you can test the charge endpoint directly with `curl`:

```bash theme={null}
# Test a successful charge
curl -X POST https://staging-api.useaxra.com/business/payment/charge \
  -H "x-api-key: bk_test_your_test_key" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": 49.99,
    "currency": "usd",
    "card": {
      "number": "4242424242424242",
      "expiryMonth": 12,
      "expiryYear": 2028,
      "cvv": "123"
    },
    "email": "test@example.com"
  }'
```

Replace `bk_test_your_test_key` with your actual sandbox API key. Swap out the card number to trigger a different outcome — for example, use `4000000000000002` to test your declined-card handling.

<Note>
  S2S charging must be enabled for your business in the Axra dashboard before you can use the charge endpoint. If you receive a `400` with a message about S2S not being enabled, contact [support@useaxra.com](mailto:support@useaxra.com).
</Note>

## Testing webhooks locally

Your local development server is not reachable from the internet by default. Use [ngrok](https://ngrok.com) to create a public tunnel to your local machine.

<Steps>
  <Step title="Install and start ngrok">
    Download ngrok from [ngrok.com](https://ngrok.com), then expose your local server:

    ```bash theme={null}
    ngrok http 3000
    ```

    ngrok will print a public URL such as `https://abc123.ngrok.io`.
  </Step>

  <Step title="Set your webhook URL">
    In the Axra dashboard, open your business profile settings and set the **Webhook URL** to your ngrok tunnel with your webhook path appended — for example:

    ```
    https://abc123.ngrok.io/webhook
    ```
  </Step>

  <Step title="Trigger a test payment">
    Create a checkout session or fire a test charge. Axra will send webhook events to your ngrok URL, which forwards them to your local server.
  </Step>

  <Step title="Inspect the delivery">
    The ngrok web interface at `http://localhost:4040` shows every request and response in real time. Use it to inspect the payload and debug your signature verification logic.
  </Step>
</Steps>

<Warning>
  ngrok URLs change each time you restart the tunnel (unless you have a paid plan with reserved domains). Update your webhook URL in the dashboard whenever your tunnel URL changes.
</Warning>

## Verifying idempotency

Axra deduplicates charge requests within a 1-minute window based on the request parameters. To verify this is working:

1. Send the same charge request twice with identical parameters within 60 seconds.
2. Confirm that both responses return the **same `paymentId`** — this means the second request returned the cached result rather than creating a new charge.
3. Check that no duplicate payment appears in your dashboard.

This behavior protects against double-charges caused by network retries. Make sure your application handles idempotent responses correctly — do not treat a repeated `paymentId` as an error.

## Pre-launch checklist

Before switching to your live API key, verify that your integration handles each of these scenarios correctly:

| Scenario                               | What to verify                                                         |
| -------------------------------------- | ---------------------------------------------------------------------- |
| Successful payment flow                | `payment.completed` webhook received; order fulfilled                  |
| 3DS authentication flow                | Customer redirected and authenticated; payment confirmed via webhook   |
| Card declined                          | User shown an appropriate error; no order created                      |
| Insufficient funds                     | User shown an appropriate error; no order created                      |
| Webhook signature verification         | Your endpoint rejects requests with an invalid `X-Axra-Signature`      |
| Refund flow                            | Refund created via API; `payment.refunded` webhook received            |
| Checkout session creation and redirect | Session created; `checkoutUrl` opens the hosted payment page correctly |

<Tip>
  Run through the full checklist with both a success card (`4242424242424242`) and a decline card (`4000000000000002`) to make sure your application responds correctly to both outcomes.
</Tip>
