Product: BabyFX Pay

Settlement & sweeps

After a payment is confirmed, the invoice's 2BABY still sits in the deposit address. pay/sweep moves it to your settlement_address, minus gas.

Why sweep and not let merchants custody their own deposits? Two reasons: (1) the deposit address is derived from a server-held seed so the merchant never sees the private key, and (2) sweeping once per invoice is cheaper than asking the customer to fund a separate gas budget for a future payout.

The sweep lifecycle

stateDiagram-v2
    [*] --> PENDING : invoice created
    PENDING --> PAID : tx in block
    PAID --> CONFIRMED : N confirmations
    CONFIRMED --> SWEEP_QUEUED : sweep scheduled
    SWEEP_QUEUED --> SWEPT : sweep tx mined
    SWEEP_QUEUED --> FAILED : gas error / RPC error
    FAILED --> SWEEP_QUEUED : manual retry
    SWEPT --> [*]

Gas strategy

The sweep wallet holds a small native balance for gas. When pay/sweep notices the gas reserve is low, it tops itself up from a configured GAS_FUNDER_PRIVATE_KEY (an EOA controlled by the operator).

The gas funder is an EOA in the local dev stack. For testnet and beyond, this must become a hosted wallet or a Paymaster. Sweep failures on insufficient gas are the most common production incident in this design.

When sweeps run

Trigger Behavior
New CONFIRMED invoice Sweep is enqueued
Sweep failed (RPC error) Retried with backoff (30s, 2m, 10m, 1h, 6h)
Sweep wallet gas low Top-up runs before next sweep
Manual POST /v1/admin/sweeps/retry Operator-triggered retry of all FAILED invoices

What "confirmed" means here

The confirmation threshold is configurable per merchant (default: 12). It is exposed as confirmations_required on the merchant record.

confirmations_required Use case
1 dev only — useful for end-to-end tests, never use in prod
6 low-value retail (coffee, transit)
12 default — most retail payments
64 high-value or B2B
Reorg behavior. If a reorg drops the paying block, the invoice goes back to PAID (or PENDING if the reorg is deep). A re-sweep attempt will re-fire on the next confirmations pass. Webhooks are not re-sent on reorg, by design — your "confirmed" webhook is a one-shot.