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.