Payment Orders
Understanding payment orders - the core unit of stablecoin payment collection.
A payment order asks a customer to send an exact stablecoin amount to a chain-specific address. StableOps allocates an address, watches the chain, tracks confirmations, and sends signed webhooks as the order moves forward.
Lifecycle
created -> detected -> confirmed -> finalized
| | |
expired reverted reverted
|
canceledcreated: address allocated; waiting for funds.detected: matching transfer seen on-chain.confirmed: chain-specific confirmation threshold reached.finalized: finality threshold reached; safest fulfillment point.expired: no matching transfer arrived before expiration.canceled: manually canceled while still increated(before any deposit is detected).reverted: receipt failed or block hash no longer matches the stored event.
Create an order
const order = await client.paymentOrders.create(
{
merchantOrderId: 'order_123',
amount: '10.00',
settlementAsset: 'USDC',
acceptedAssets: [
{ chain: 'base', asset: 'USDC' },
{ chain: 'ethereum', asset: 'USDC' },
],
expiresAt: new Date(Date.now() + 30 * 60 * 1000).toISOString(),
metadata: { customerId: 'cus_123' },
},
{ idempotencyKey: 'order_123:create-payment-order' },
)
console.log(order.paymentInstructions)
// [
// { chain: 'base', asset: 'USDC', address: '0x...' },
// { chain: 'ethereum', asset: 'USDC', address: '0x...' },
// ]amount is a decimal asset-unit string. Chain events use smallest-unit integer
strings, and StableOps converts them internally using token decimals before
matching.
Address allocation
Single-address mode gives each open order one available address until the order is finalized, reverted, expired, or canceled. Shared-address mode can reuse one address for multiple open orders and matches by exact amount, chain, asset, organization, and environment.
On create, StableOps allocates one available candidate address per (chain, asset) pair listed in acceptedAssets and returns them in
paymentInstructions. Once the order reaches a terminal state (finalized,
reverted, expired, or canceled), any candidate that was not used is released
back to the pool.
Canceling an order
Only orders in created can be canceled. Once an order reaches detected,
confirmed, or finalized it can no longer be canceled — refunds happen out of
band. Canceling releases the order's still-held candidate addresses back to
available.
Idempotency and uniqueness
Idempotency-Key controls request replay. merchantOrderId is a separate
business reference and must be unique per organization and environment. Reusing
merchantOrderId with a new idempotency key returns a conflict instead of the
old response.
Webhooks
Common events are:
payment_order.createdpayment.detectedpayment.confirmedpayment.finalizedpayment.expiredpayment.reverted
Fulfill from payment.finalized unless your product explicitly accepts the
remaining reorg risk of payment.confirmed.