Payments
The payment lifecycle, every state it can be in, and the four calls that drive it. This is the heart of the API; everything else is plumbing around it.
The lifecycle
+-----------+
API | created | intent exists; no asset chosen yet
---------->+-----+-----+
| customer picks asset + network:
| quote locked (15 min), unique amount reserved
+-----v-------------+
+-------------+ awaiting_payment +------------+
| TTL expires +-----+-------------+ | you cancel
v | watcher sees transfer v
+-----------+ v +-----------+
+------+ expired | +-----------+ | canceled | (terminal)
| +-----------+ | detected | 0-conf: show "processing"
| matching payment +-----+-----+ DO NOT SHIP
| arrives within 24h | >= 1 confirmation
v v
+-----------+ +------------+
| paid_late | (review) | confirming +---- reverted / reorged out ---+
+-----------+ +-----+------+ |
| required depth reached v
+-------------------+------------------+ +----------+
v v v | failed |
+-----------+ +-----------+ +-----------+ +----------+
| underpaid | | confirmed | | overpaid |
| (action | | (TERMINAL | | (TERMINAL |
| needed) | | SUCCESS) | | SUCCESS, |
+-----+-----+ +-----------+ | surplus) |
| remainder arrives, or +-----------+
+--- you accept as paid -----> confirmedEvery state, and what to do in it
| Status | Means | You should |
|---|---|---|
created | The intent exists; no asset chosen, nothing to pay to yet. | Redirect the customer to checkout_url. |
awaiting_payment | A quote is locked and a unique amount reserved. The 15-minute clock is running. | Show the address, exact amount and countdown. |
detected | A matching transfer was seen at 0 confirmations. | Show "processing". Do not ship. |
confirming | At least one confirmation, working toward the required depth. | Keep showing "processing". |
confirmed | Terminal success. | Ship the order here. |
underpaid | Confirmed, but less than the amount due arrived. The money is safe in your wallet. | Show the remaining amount, or accept as paid. |
overpaid | Terminal success with a surplus recorded. | Ship the order. Refund tooling is Phase 2. |
paid_late | A matching transfer arrived after expiry, inside the 24-hour monitoring window. | Resolve it from the review queue. |
expired | The 15-minute quote elapsed with nothing detected. | Offer a new intent. Keep watching for 24 hours. |
canceled | Terminal. Canceled before any transfer was detected. | Nothing. |
failed | The matched transaction reverted, was reorged out, or failed verification. No funds moved. | Offer a new intent. |
Create a payment intent
curl -X POST https://api.payaider.com/v1/payment_intents \
-H "Authorization: Bearer sk_test_YOUR_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{ "amount": 10000, "currency": "USD", "metadata": { "order_id": "1234" } }'Put your own order id in metadata. It is echoed on every read and on every webhook delivery, so event.data.metadata.order_id is what your fulfilment code keys on.
Retrieve and list
curl https://api.payaider.com/v1/payment_intents/pi_7Fk2Qd9RmTs4Vb \
-H "Authorization: Bearer sk_test_YOUR_KEY"
# Cursor pagination: keep going while has_more is true.
curl "https://api.payaider.com/v1/payment_intents?limit=25&starting_after=pi_7Fk2Qd9RmTs4Vb" \
-H "Authorization: Bearer sk_test_YOUR_KEY"Retrieving is also the recovery path: webhook delivery is at-least-once and unordered, so if you think you missed something, re-read the payment. Do not poll every payment on a timer — that is how you meet the rate limiter.
Selecting a payment method
Hosted checkout calls this for you. Call it yourself only if you are building your own checkout surface.
curl -X POST https://api.payaider.com/v1/payment_intents/pi_7Fk2Qd9RmTs4Vb/select_method \
-H "Authorization: Bearer sk_test_YOUR_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{ "token_id": "usdt-tron", "wallet_id": "wal_5Rm2Ht9Jc4Nq" }'Three things happen at once: an FX quote locks for 15 minutes from at least two independent sources, a globally unique amount is reserved for that (wallet, token), and payment_method is filled in with the address, the exact amount, the token contract and the pay URI.
Cancelling
curl -X POST https://api.payaider.com/v1/payment_intents/pi_7Fk2Qd9RmTs4Vb/cancel \
-H "Authorization: Bearer sk_test_YOUR_KEY" \
-H "Idempotency-Key: $(uuidgen)"Allowed from created and awaiting_payment only. Once a transfer is detected there is no edge to canceled — money on its way must never be cancelled out from under the customer — so that attempt returns 409 invalid_state_transition.
Idempotency
Send an Idempotency-Key on every POST. The first response — status and body, including errors — is stored for 24 hours against (api_key, key) and replayed byte-for-byte.
| Situation | Response |
|---|---|
| Same key, same body, after the first completed | The stored response, byte-for-byte. |
| Same key, different body | 409 idempotency_error — permanent. Use a new key. |
| Same key, first request still in flight | 409 idempotency_key_in_use — retryable. Wait and retry. |
| Request failed validation | Nothing was persisted; the key is unused. |
Underpayment and overpayment
Real payments arrive wrong: amount-less QR codes, hand-typed rounding, and exchanges that deduct the network fee from the amount sent. Payaider attributes those anyway, then classifies the result.
- Underpaid — the payment sits in
underpaidand the money is safe in your wallet. Checkout shows the customer the remaining amount, and a second transfer completes it cumulatively. You may also accept it as paid, which is an audited action. - Overpaid — confirmed with the surplus recorded. It counts as paid for fulfilment.
- Payment tolerance — a per-merchant knob,
0by default (exact-or-better), configurable up to 1%, that decides whether a small shortfall auto-confirms instead of enteringunderpaid.