Testing
Produce every outcome — including reorgs and underpayment — with no wallet, no chain and no money.
Test keys move no money and need no wallet. Everything in the lifecycle, including the parts nobody builds for, can be produced on demand.
Test and live are separate
A key is one or the other, and the data does not mix. Test payments never appear in live reporting and a test key cannot touch a live payment. Creating a live key needs a verified email and business details; you can integrate fully before any of that.
Produce the awkward outcomes
// Drive a test payment to any outcome, with no wallet and no chain.
await call('POST', `/v1/test/payments/${payment.id}/simulate`, {
outcome: 'underpaid',
});
curl "$BASE/v1/test/outcomes" -H "..." # every outcome you can produce
| Outcome | What you should already handle |
|---|---|
completed | The happy path. |
settled | Seen on chain, not yet deep enough. Do not ship on this one. |
underpaid | Less arrived than required. Do not ship. |
overpaid | More arrived at a deposit address. It was forwarded; the excess is yours to return. |
expired | Nobody paid in time. |
failed | The transaction reverted. |
reorged | A confirmed payment was undone. This is why you wait for completed. |
settlement_failed | Money moved but settlement did not complete. |
late_settled | The buyer paid after the payment expired or you cancelled it. The money is yours; fulfil or refund. |
The reorg case is the one worth building for deliberately. It is rare, it is
real, and a shop that ships on settled discovers it by losing
stock.
Webhooks in development
Endpoints must be public HTTPS — we refuse private and internal hosts, which
means localhost will not work. Use a tunnel, and point the
endpoint at it while you build.
The panel shows every delivery attempt with the response your server returned. When a signature will not verify, that page usually shows why in one line: your framework parsed the body before you got to it.
Checking your signing before anything else
The fastest way to know your request signing is right is a read that needs no setup:
A 401 means the signature is wrong. A 403 means it
is right and the key lacks the scope — which is a different problem, and good
news for your signing code.
Before you go live
Work through these, because each one is a real incident somebody has had:
- An event arrives twice. Does your handler ship twice?
- Your webhook endpoint is down for an hour. Do you recover the missed events?
- A payment is underpaid by one cent. What does the buyer see?
- A buyer pays after your
expires_in. What happens to the money? - You retry a create after a timeout. Do you get one payment or two?
- A refund is requested for a payment that already refunded. Do you owe it twice?