How payments work on SDP

Wallets, tokens, server-side signing, fees, and the data model that backs SDP payments.

SDP payments move value between Solana accounts: a custody wallet sends tokens to a destination address and SDP records the resulting transfer. This page introduces the building blocks — wallets, tokens, execution, fees, and the data model — that the rest of the section assumes.

Custody wallets

Every SDP payment is anchored to a custody wallet — an SDP-managed Solana wallet, scoped to an organization and (optionally) a project. The wallet's key is held and signed for by a custody provider; SDP presents a stable Solana address and routes signing to whichever provider holds the key. Wallets sign outbound transfers on your behalf and contribute to balances and policies for the addresses you control.

You provision custody wallets up front (see Set Up Wallets). Transfer requests use the exact SDP Wallet id (cwlt_…) as sourceCustodyWalletId; responses expose it as custodyWalletId and retain the provider's identifier separately as providerWalletId evidence. The destination remains the recipient's on-chain Solana address.

Ramp quote endpoints use the same public shape for every provider: destinationWallet (onramp) or sourceWallet (offramp) identifies a custody wallet you control by its provider wallet ID or Solana address. Provider-specific identifiers such as customer and payout-account IDs are resolved server-side from the Counterparty's Provider Requirements — see Ramps.

Tokens and token accounts

Payments transfer SPL tokens — stablecoins, tokenized assets, or any mint your project supports. The token field on POST /v1/payments/transfers requires the on-chain mint address for SPL tokens; the literal string SOL is accepted as the native-SOL shorthand. Symbols like USDC are not resolved at request time. The token filter on GET /v1/payments/transfers does an exact match against the Transfer.token value as stored — API-created transfers store the mint you supplied (or SOL), while indexed/observed transfers run mint-to-symbol resolution and may store a resolved symbol (e.g. USDC). Filter using the same label you see on the response, or query without token and filter client-side. Balance responses always attempt symbol resolution and return the symbol whenever the mint is known — so a wallet holding USDC reports USDC rather than the mint string.

Amounts on the wire are UI-unit decimal strings"100.00" means 100 tokens, regardless of the mint's decimal count. This applies to issuance operations (mint, burn, seize, force-burn) as well as payments transfers. SDP handles the smallest-unit conversion when it builds the on-chain transaction. The one exception is wallet-balance responses, which return both a raw amount (smallest unit) and a uiAmount (decimal string) so balance consumers can pick whichever fits.

Transfer execution

Outbound wallet transfers are server-executed through POST /v1/payments/transfers. SDP builds the transaction, signs with the source wallet's custody key, submits it on the configured Solana cluster, and records the resulting transfer lifecycle.

The rest of this section assumes transfers are submitted by SDP from a custody wallet you control.

Fees and sponsorship

Solana fees are paid in SOL by the transaction's fee payer. On wallet transfers, the source custody wallet is the fee payer by default; SDP can be configured to sponsor fees so end-user wallets do not need a SOL balance.

The transfer data model

Every successful or attempted transfer creates a Transfer record. The fields that matter for most flows:

FieldMeaning
idSDP-internal transfer identifier (use this in API URLs).
statusWallet transfers: processing, confirmed, finalized, failed. Ramp transfers: pending, awaiting_payment, settling, completed, canceled, expired, failed. See Verifying a payment.
directioninbound (someone paid you) or outbound (you paid someone).
source, destinationOn-chain addresses.
token, amountWhat moved and how much. amount is a UI-unit decimal string (e.g. "100.00").
memoOptional UTF-8 string, up to 256 chars. See Payment with memo.
signatureSolana transaction signature once confirmed. Unique across SDP — the natural dedup key.
slot, blockTime, feeOn-chain settlement details, populated as the transaction lands.
errorSet if the transfer failed; the rest of the record explains what reached the network.
riskOptional risk-score metadata, when a risk provider is configured.

Outbound wallet transfers start at processing and move to confirmedfinalized. Inbound transfers discovered on-chain surface directly at confirmed. Ramp transfers can start at pending while provider-side steps are still underway. See Verifying a payment for the full state table.

What SDP does and does not give you

SDP exposes everything you need to send, track, and reconcile payments at the transfer level: status polling, filtered lists, wallet-level balances, exact-wallet Idempotency-Key replay, and a unique on-chain signature once populated (see Basic payment → Deduplication). It does not ship higher-level commerce primitives such as a checkout session or invoice object. The Accept payments section shows how to build those abstractions on top of the transfer model.

Is this page helpful?