Testing and go-live checklist
Testing your integration
Section titled “Testing your integration”Test keys and live keys use the same base URL (https://api.example.com/v1), and only slip verification returns simulated results (if the team gave you the URL of a separate test environment, use that instead). Test in this order:
- Check your signer against the request test vectors and the webhook test vector, without calling the API.
- Call read-only endpoints:
GET /v1/accountandGET /v1/banksconfirm your key, your signing and the IP allow-list. - Test slip verification with a test key. Ask the team for a test key (
pk_test_...) and test mode, then usepayloadvalues starting withTESTFAIL/TESTDUPto cover every branch (details). - Run real payments with small amounts. Pay-ins and payouts are always real, even with a test key. Use your own team’s bank accounts as payers and the minimum amounts from your account
limits.
Cases to test
Section titled “Cases to test”| Case | How | Expected |
|---|---|---|
| Successful payment | Create a pay-in and transfer amount | payin.succeeded webhook; the order is marked paid |
| Expiry | Create a pay-in and do not pay | payin.expired webhook |
| Cancel | POST /v1/payins/{id}/cancel | Response with status canceled, no webhook |
| Slip | Upload a slip for a pending pay-in | matched: true and a payin.succeeded webhook |
| Payout | Create a payout to your team’s account | payout.succeeded webhook |
| Duplicate | Create a pay-in for a payer with an open one | 409 pending_payin_exists |
| Returning payer | Create two pay-ins (one after the other) with the same inline payer account | Both responses have the same payerId |
| Name changed | Send an inline payer with a known account but a different name | Pay-in created, warnings contains payer_name_differs; you log it |
| Wrong amount | Below payinMinimum | 422 amount_below_minimum |
| Bad signature | Change the body after signing | 401 invalid_signature |
| Repeated webhook | Click Send callback again in the console | Your system does not credit twice |
| Webhook outage | Stop your endpoint while a payment succeeds | Your reconciliation job catches up |
Go-live checklist
Section titled “Go-live checklist”Keys and security
Section titled “Keys and security”- Production uses the live key (
pk_live_...); no test key left anywhere. - The secret lives in a secret manager or environment variable, never in code, repositories or logs.
- A new secret was created before launch if the testing secret was ever shared or pasted elsewhere.
- The API is called from servers only, never from a browser or an app.
- Server clocks are synced with NTP.
- If you use an IP allow-list, the team has all your production outgoing IPs.
API calls
Section titled “API calls”- Every request, including retries, gets a new timestamp, nonce and signature.
- Amounts are sent as strings, and payers are shown the pay-in’s
amount(notrequestedAmount). - Each pay-in and payout sends exactly one of
payer(inline bank account) orpayerId, andwarningsin responses are logged. - If you show your own QR code, it is rendered from
payTo.qrPayload; if you show the ready-made image, the request uses?include=paymentImage(it is not returned by default). - Every payment has a unique
reference, and you search byreferencebefore re-creating after a timeout. - Your code branches on
error.codeand handlespending_payin_exists,insufficient_balance,not_available_nowandmaintenance. - The
requestIdof every response is logged. - Amounts are checked against
limitsfromGET /v1/accountbefore creating payments.
Webhooks
Section titled “Webhooks”- The endpoint is public HTTPS and verifies the signature on the raw body.
- Events with a timestamp older than 5 minutes are rejected.
- You reply
2xxwithin 10 seconds and do heavy work in the background. - Duplicates are handled with
data.id+data.status, and finished orders never move back. - Unknown event types get a
2xx. - A scheduled reconciliation job runs, since there are no automatic retries.
Operations
Section titled “Operations”- Your team knows how to use Send callback again and read the callback log in the console.
- Your available balance (
balance.available) is monitored so payouts do not fail. - You know how to reach the team: support@example.com, always with the
requestIdand the paymentid.