On this page
Testing
Test invoice and withdrawal state changes with dedicated test keys, signed webhooks, and simulation endpoints that move no real funds.
Test your integration end-to-end in sandbox. Same API surface as production, simulated blockchain, signed webhooks, no real money at risk.
Sandbox vs production
| Feature | Sandbox | Production |
|---|---|---|
| Base URL | api.paymos.io/v1 |
api.paymos.io/v1 |
| API Keys | pk_test_ / rk_test_ IDs with sk_test_ secrets |
pk_live_ / rk_live_ IDs with sk_live_ secrets |
| Transactions | Simulated | Real blockchain |
| Webhooks | Sent normally | Sent normally |
| Funds | No real value | Real crypto |
Simulating payments
Sandbox payments are not auto-confirmed by a timer. Trigger them explicitly:
Body: { "stage": "paid" } — stage is one of paid, overpaid, underpay, or cancel. Authenticate it like any other signed /v1 call; it acts on sandbox invoices only, so a production key is rejected with 403 not_sandbox. It fires the same lifecycle and webhook events as a real on-chain payment, so your verification path stays identical between sandbox and production.
Simulating withdrawals
Sandbox withdrawals don't settle on a real chain. Mark a pending sandbox withdrawal as completed to exercise your full payout flow:
No request body. Authenticate it like any other /v1 call with your Payout key; it acts on sandbox withdrawals only. It transitions the withdrawal to completed and emits the same withdrawal.completed webhook as a real on-chain payout, so your reconciliation path stays identical between sandbox and production.
Simulating channel deposits
Sandbox payment channels derive their addresses locally and watch no chain, so deposits are created explicitly:
Body: { "amount": "100", "currency": "USDT", "network": "TRC20", "stage": "confirmed" } — stage is one of confirming, reorged or confirmed and defaults to confirmed. Sign it with your sandbox Payment key; a production key is rejected before anything is created. Every call emits payment_channel.deposit.confirming first, then the terminal event for the stage you asked for, so your webhook path is identical between sandbox and production. See Simulate Channel Deposit for the full contract.
Testing webhooks locally
Expose your local server through a tunnel:
ngrok http 3000
Set the webhook URL in Dashboard → Developer → Webhooks:
https://abc123.ngrok.io/api/paymos-webhook
Webhook playground
The documentation includes a sandbox-only Webhook Playground that runs the real delivery path: it creates a sandbox invoice, drives it to the outcome you picked, and lets the delivery worker call your registered endpoint. Nothing is posted straight to your URL, so signature handling is verified against the same signing and retry code production uses.
- 01Go to Dashboard → Developer → Documentation in the sandbox environment
- 02Open Webhooks → Testing, then select Playground
- 03Pick an event type (
invoice.paid,invoice.paid_over,invoice.underpaid, orinvoice.cancelled), set the amount and currency, then Simulate - 04Check your server logs for the received event and the
X-Webhook-Signatureheader
It needs an active sandbox webhook endpoint and a sandbox Payment key. Production sending is disabled there by design.
Checklist before going live
- ☐Webhook signature verification works
- ☐Invoice creation returns correct data
- ☐Payment flow redirects properly
- ☐Error handling covers non-2xx responses (4xx validation, 409 conflicts)
- ☐Idempotency with
external_order_idworks - ☐Switch API key IDs from
pk_test_/rk_test_topk_live_/rk_live_, and secrets fromsk_test_tosk_live_