Stablecoin Payment Flow
This guide walks you through the full lifecycle of accepting a customer’s stablecoin payment — from initiation to confirmation and final settlement.
1. Initiate a Payment
Use the POST /payments/initiate
endpoint to create a new payment.
Request Body
{
"merchant_id": "merch_xyz",
"amount": "10.00",
"currency": "USDC",
"chain": "best",
"metadata": {
"order_id": "order_123"
}
}
merchant_id
: Celar-assigned ID for the merchant.amount
: Stablecoin amount expected (e.g.,10.00
).currency
:USDC
orUSDT
.chain
: Choose frombase
,polygon
,arbitrum
, or use"best"
to auto-select.metadata
: Optional structured data for reference.
Response
{
"payment_id": "pay_abc123",
"intermediary_wallet": "0x123abc...",
"magic_link": "https://pay.celar.io/pay_abc123",
"status": "pending"
}
2. Customer Sends Funds
Your customer pays the exact amount to the intermediary_wallet
provided.
- The magic link can be shared directly with the customer to show live payment instructions and status.
- Celar monitors the destination wallet in real time.
3. Receive Confirmation
Once the funds arrive on-chain:
- Celar emits a
payment.confirmed
webhook to your registered endpoint. - Alternatively, you can poll the status via:
GET /payments/:payment_id
- The response includes status, amount received, KYT verdict, and more.
4. Settlement
After confirmation:
- Celar automatically does internal settlement.
- The settlement is gasless and triggered internally — no action needed from your side.
- Once complete, a
payment.settled
webhook is sent with full settlement metadata:
{
"event": "payment.settled",
"payment_id": "pay_abc123",
"psp_amount": "9.50",
"treasury_fee": "0.50",
"tx_hash": "0xabc..."
}
5. Handle Mismatches or Failures
payment.mismatched
: Sent when overpayment or underpayment is detected.payment.failed
: Sent if payment fails for some reason, is blocked by KYT or expires (default: 20 minutes).
To accept payment again after expiry or failure, re-initiate using the POST /payments/initiate
endpoint.