Crypto payments
Accept stablecoins; settle to your own wallet.
Accept USDC on Ethereum and Base and USDT on TRON. Same API keys, same webhook engine, same idempotency as the rest of CleonPay. Deposits settle to a wallet you register — you never hold custody in your integration.
403 crypto_not_enabled until then.Amounts are the token amount, not fiat
amount is the number of tokens to
collect: "25.00" means 25 USDC/USDT. Stablecoins are USD-pegged
and CleonPay does no FX, so read one token as one US dollar. There is no
minimum or maximum.Create a payment
| Field | Type | Notes |
|---|---|---|
chain | string | Required. ethereum, base or tron. |
asset | string | Required. USDC (ethereum, base) or USDT (tron). |
amount | string | Required. Token amount, e.g. "25.00". |
order_reference | string | Your order id. Searchable. |
hold_minutes | integer | How long the address is held (1–1440; default 30). |
metadata | object | Your own key/values, returned untouched on every read. |
Send an Idempotency-Key header so a retried create returns the
same payment and deposit address rather than reserving a new one. See
Idempotency.
curl https://api.cleonpay.com/v1/crypto/payments \
-H "Authorization: Bearer $CLEONPAY_KEY" \
-H "Idempotency-Key: order-8891" \
-H "Content-Type: application/json" \
-d '{ "chain": "base", "asset": "USDC", "amount": "25.00", "order_reference": "order-8891" }'
{
"id": "9b1c0f2e-…",
"object": "crypto_payment",
"status": "pending",
"chain": "base",
"asset": "USDC",
"network": "Base network",
"deposit_address": "0xea28…8836",
"amount": "25.00",
"amount_base": "25000000",
"order_reference": "order-8891",
"expires_at": "2026-09-21T12: 30: 00.000Z",
"created_at": "2026-09-21T12: 00: 00.000Z",
"qr": "data:image/png;base64,…",
"checkout_url": "https://pay.cleonpay.com/crypto/checkout/9b1c0f2e-…"
}
Show the customer the deposit_address and amount,
or send them to checkout_url — a hosted page with the QR, the
exact amount and live confirmation status.
Let the payer choose the network
Same body as a payment but without chain/asset.
Returns deposit_options — one payment (its own id,
checkout_url and webhooks) per supported network, sharing your
order_reference. Show them all, or a picker; whichever address is
paid fires that payment's crypto.payment.received.
Reading a payment
Adds received_amount, confirmations,
confirmations_required and tx_hash (the on-chain
deposit) as they appear.
Lists your crypto payments newest first. Accepts status,
reference, limit and created_before
for keyset pagination — the same shape as GET /v1/payments.
Cancels a still-pending payment the customer never funded.
Once a deposit is seen it can no longer be cancelled.
Balances and settlements
Your available balance per network (net of fees).
The payouts we've sent to your wallet — net, fee,
gross, to_address, tx_hash, status
— keyset-paged like the payments list.
Testing in the sandbox
With a test key, drive a payment through to
paid without sending real testnet coin:
Runs the exact ingest path a real deposit would — the status transition,
ledger credit and crypto.payment.received webhook are all real.
Pass { "amount": "10.00" } (less than due) to test
underpaid instead. Live keys are rejected.
curl -X POST https://api.cleonpay.com/v1/crypto/payments/9b1c…/simulate \
-H "Authorization: Bearer $CLEONPAY_TEST_KEY"
Statuses
| Status | Meaning |
|---|---|
pending | Waiting for a deposit |
detected / confirming | Deposit seen, gaining confirmations |
paid | Confirmed in full. Fulfil here. |
underpaid | Confirmed, but less than due |
held | Held for review — the sender failed sanctions screening |
swept / settled | Collected to treasury / paid out to your wallet |
expired | No deposit before the window closed |
Sanctions screening
Every deposit's source address is screened against sanctions lists before
it's credited. A hit puts the payment in held — never credited,
swept or settled — and fires crypto.payment.held. Treat
held like a decline: do not fulfil. This runs on
every deposit, however the payment was created.
Webhooks
Listen for crypto.payment.received (fulfil here),
crypto.payment.underpaid and crypto.payment.expired;
and crypto.settlement.completed / crypto.settlement.failed
when we pay your balance out to your wallet. Their data carries
crypto_payment_id (or crypto_settlement_id), not
payment_id. See Webhooks.
Settlement
Received deposits settle to the wallet you register per network on the Crypto Processing tab, net of fees. CleonPay handles every on-chain movement.