StableOps
Concepts

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
   |
canceled
  • created: 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 in created (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.created
  • payment.detected
  • payment.confirmed
  • payment.finalized
  • payment.expired
  • payment.reverted

Fulfill from payment.finalized unless your product explicitly accepts the remaining reorg risk of payment.confirmed.

On this page