Verifying a payment
Transfer-status semantics, finality on Solana, and recommended polling cadence for SDP transfers.
When a payment lands on a wallet you control, the inbound transfer moves through a handful of status states. This page describes those states, what each one means about on-chain finality, and how to decide when it is safe to ship goods or release funds.
Status lifecycle
The status field on the Transfer record takes one of five values:
| Status | Meaning |
|---|---|
pending | Prepare-mode outbound transfers — POST /v1/payments/transfers/prepare saves the serialized transaction and the record stays pending until the client submits the signed tx (then it moves to processing) or the blockhash expires (then the track-pending-transfers job marks it failed). No signature yet. |
processing | The transfer is in flight. For Execute-mode this is the initial state at record creation: the request has been accepted and the transaction is being built and submitted; the signature field is not yet populated when the record is first written. Prepare-mode transfers also enter processing once the signed transaction is submitted. |
confirmed | The cluster has confirmed the transaction. signature and slot are populated. blockTime may also be set, but is not guaranteed at this stage — treat it as optional until finalized. |
finalized | The slot containing the transaction is locked in by the network — finality at the Solana cluster's strongest guarantee. signature and slot are populated; blockTime is best-effort and may remain null for Execute-mode transfers whose finalization is observed by the track-pending-transfers job (which updates status and slot but does not backfill block time). When you need a definitive timestamp, resolve it from the on-chain signature via Solana RPC. Safe to treat as irreversible. |
failed | The transaction was built and submitted but rejected by the network (insufficient funds, frozen account, expired blockhash, etc.). The error field describes what reached the chain. |
A transfer moves forward through processing → confirmed → finalized for Execute-mode, or pending → processing → confirmed → finalized for Prepare-mode (with pending → failed if the blockhash expires before submission). Inbound transfers discovered via on-chain signature history surface directly at confirmed (or failed if the on-chain transaction errored) — they never appear as pending. Any state can transition to failed; failed is terminal.
Finality on Solana
For real-money flows, treat finalized as the safe-to-act-on signal. confirmed is durable enough for most non-adversarial workflows (think: showing a "paid" badge in your dashboard) but a sufficiently motivated cluster reorganization can theoretically roll back a confirmed transaction; this does not happen for finalized transactions.
Use confirmed to release low-stakes side effects (status emails, UI updates) and finalized for irreversible side effects (releasing custody, shipping high-value goods, posting to your general ledger).
Polling cadence
SDP does not currently emit settlement webhooks. Status is observed by polling:
GET /v1/payments/transfers/{id}for a single transfer's current state.GET /v1/payments/transfers?direction=inbound&from=…for a windowed list — see Indexing and reconciliation.
Cadence depends on how quickly you need to see state transitions and how aggressive your retry budget is. A reasonable starting range:
- Active checkout (customer is staring at a payment screen): every 2–5 seconds for up to a minute, then back off.
- Background reconciliation (no one is waiting): every 15–30 seconds, or whenever a worker tick fires.
- Long-tail catch-up (status reads after the fact): single one-shot reads when your application needs to act, no polling loop at all.
The exact recommended cadence will be tightened in a follow-up once the backend confirms a target; treat the ranges above as defensible defaults today.
Independent verification
Every transfer that reaches processing or beyond carries enough information to verify against the network directly:
signature— the Solana transaction signature; paste into Solscan or Solana Explorer to see the on-chain record.slot— the slot the transaction landed in.blockTime— ISO 8601 timestamp of slot finality.fee— fee paid, in lamports.
If you maintain your own indexer (getSignatureStatuses against a Solana RPC), cross-reference SDP's signature to your indexer's view to confirm SDP's status reflects what your nodes see.
Risk metadata
When a risk-screening provider is configured on the organization, transfers may carry a risk field with provider, score, level (low | medium | high | unknown), and evaluatedAt. Use this to gate downstream side effects on a risk threshold; the field is absent when no risk provider is wired up.
Risk evaluation is not blocking by default — a transfer can complete with a high-risk score. If you want a transfer to halt on risk threshold, enforce that in your application logic on top of the risk.level value.
Failure handling
When status settles to failed:
- Read the
errorfield. SDP returns the chain-level error message; common causes are insufficient source funds, account frozen by token authority, or stale blockhash. - Decide whether to retry. The original transfer record is terminal; a retry creates a new transfer with a new ID and (eventually) a new signature.
- Surface the failure to whoever needs to know — the merchant, the customer, your operations team.
Related
- Accept overview — the broader inbound-payment flow.
- Indexing and reconciliation — list-based status checking across many transfers.
- Concepts: the transfer data model — full field reference.