# Sandbox & testing
Source: https://paylinky.vercel.app/docs/sandbox

Simulate every payment outcome without moving money.

# Sandbox

Use a `plk_test_…` key (or **Developers → Sandbox** in the app, which runs real requests with your account).

Sandbox payments:

- never call the payment provider and never move funds,
- return `sandbox: true` and `simulation: true`,
- are listed in your dashboard under **Transactions → Sandbox**,
- fire real webhooks to your endpoint when you simulate a result.

## Test phone numbers

The scenario is chosen by the **last digit** of a number ending in `00000000N` and works with every active operator of the country. Any country code works.

| Number (example CM) | HTTP | Result |
| - | - | - |
| `+237000000001` | `200` | `status: "success"` — confirmed immediately |
| `+237000000002` | `202` | `status: "pending"` — stays pending until you simulate |
| `+237000000003` | `200` | `status: "failed"` |
| `+237000000004` | `400` | `otp_required` — retry with `otp: "000000"` → `202 pending` |
| `+237000000005` | `200` | `status: "cancelled"` |

Other numbers return `202 pending`.

| Country | Code | Prefix |
| - | - | - |
| Cameroon | CM | +237 |
| Chad | TD | +235 |
| Central African Rep. | CF | +236 |
| Congo | CG | +242 |
| Gabon | GA | +241 |
| Equatorial Guinea | GQ | +240 |
| Senegal | SN | +221 |
| Côte d'Ivoire | CI | +225 |
| Mali | ML | +223 |
| Burkina Faso | BF | +226 |
| Niger | NE | +227 |
| Togo | TG | +228 |
| Benin | BJ | +229 |
| Guinea-Bissau | GW | +245 |
| DR Congo | CD | +243 |
| Nigeria | NG | +234 |
| Ghana | GH | +233 |
| Kenya | KE | +254 |

## OTP simulation

```json
{
  "error": "otp_required",
  "sandbox": true,
  "simulation": true,
  "reference": "PLK-SBX-…",
  "ussd_code": "#SANDBOX#",
  "message": "Demo OTP required. Use 000000 with the same request."
}
```

Re-send with `"reference": "<returned reference>"` and `"otp": "000000"`. Any other OTP returns `invalid_otp`.

## Finishing pending payments

```bash
curl -X POST https://paylinky.vercel.app/api/v1/sandbox/simulate \
  -H "Authorization: Bearer $PAYLINKY_TEST_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "transaction_id": "0b9c…", "status": "success" }'
```

This updates the transaction and sends `payment.completed` (or `payment.failed`) to your webhook.

## Crypto in the sandbox

`/crypto/collect` returns a clearly fake address and stays `pending` until you call `/sandbox/simulate`.

## Test checklist

- `200` on `/countries` and `/crypto/assets`
- one `pending`, one `success`, one `failed`, one `cancelled`
- the OTP round-trip
- webhook received, signature verified, duplicate delivery ignored
- timeout → status check instead of a second payment
- errors `400`, `401`, `403`, `429`
