UnifiWallet Payments

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',
});
OutcomeWhat you should already handle
completedThe happy path.
settledSeen on chain, not yet deep enough. Do not ship on this one.
underpaidLess arrived than required. Do not ship.
overpaidMore arrived at a deposit address. It was forwarded; the excess is yours to return.
expiredNobody paid in time.
failedThe transaction reverted.
reorgedA confirmed payment was undone. This is why you wait for completed.
settlement_failedMoney moved but settlement did not complete.
late_settledThe 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:

GET /v1/status/detail

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?