Connect the wallets you already run to bank deposits and local payouts: register addresses, open virtual accounts, quote, authorize, and track webhooks.
To add stablecoin payments to an app that already has wallets, register each customer's wallet address with a payments API, route bank deposits into it through virtual accounts, and pay out from it with a quote that the wallet authorizes. The wallet keeps holding keys and signing. The payments layer handles bank rails, conversion, and compliance.
This tutorial assumes the wallets already exist, from an embedded SDK, a wallet API, or your users' own wallets. How to integrate a stablecoin API covers the full flow from a blank project. This guide covers the seams between the wallet you have and the payments you're adding: address registration, who signs what, and how failures land back in the wallet.
Outcome: you know which direction money moves, and what the wallet has to do in each direction.
A pay-in collects bank money and delivers stablecoins to a wallet. A payout takes stablecoins from a wallet and delivers local currency to a bank account. Most products need both eventually, but the first release rarely does.
| Flow | Money moves | The wallet's job | Wallet signs? |
|---|---|---|---|
| Pay-in (collect) | Bank deposit, converted to USDC or USDT, delivered to the wallet | Receive | No |
| Payout (send) | Stablecoins from the wallet, converted, sent to a bank account | Authorize the quoted amount | Yes, once per payout |
| Both | A contractor platform funding by ACH and paying out over Pix | Receive, then authorize | For payouts only |
Pay-ins are the easier first release, because the wallet never has to sign anything. If your wallets are held by users on phones, starting with pay-ins lets you ship before you've designed a single signing prompt. What is crypto wallet integration explains the layers if you need the wider view.
Outcome: every customer is verified before a quote can be created for them.
KYC, know your customer, verifies an individual's identity. KYB, know your business, verifies a company, its registration, and the people who own and control it. Stablecoin payment providers run both because they move money between bank accounts and blockchains for someone else.
On BlindPay, each of your users becomes a customer with a kyc_status. New customers start at verifying. With Standard KYC, the status typically moves to approved or rejected automatically within about 60 seconds, unless the compliance team needs a manual review. Payin and payout quotes require approved or approved_rfi, so gate your payment screens on that status, not on your own signup state.
Store the customer ID next to your internal user ID and the wallet provider's wallet ID. You'll need all three in every support ticket. How to automate KYC and KYB covers the verification flow in detail.
Outcome: each customer wallet is registered with the payments layer, and you've confirmed it belongs to that customer.
The payments layer needs to know which address to deliver pay-ins to and which address will fund payouts. BlindPay calls this a blockchain wallet (bw_...): an external wallet the customer controls, whose keys BlindPay never holds. There are two ways to register one, and the right one depends on the wallet type.
| Wallet type | Registration method | Why |
|---|---|---|
| EVM wallet that can sign messages (most embedded, browser, and MPC wallets) | Signed message, is_account_abstraction: false | BlindPay recovers the address from the signature, which proves control |
| Smart-contract wallet (ERC-4337 account, multisig) | Direct address, is_account_abstraction: true | Contract wallets validate signatures through EIP-1271 and can't produce the standard signed message |
| Stellar, Solana, or Tron wallet | Direct address, is_account_abstraction: true | The signed-message flow is EVM-only |
For the signed-message flow, fetch the message, have the wallet sign it, and submit the signature. The request below is illustrative, with field names from the docs; check the API reference for the full list.
The direct-address flow checks the address format, not ownership. So never take the address from a text field. Read it server-side from your wallet provider's API, where you already know which user owns it. A stablecoin delivered to the wrong address can't be recovered by anyone. A blockchainWallet.new webhook fires when the wallet is created.
Outcome: the customer has US bank details, and every deposit lands in their wallet as USDC or USDT.
A virtual account is a bank account issued in the customer's name, with its own routing and account number. Each virtual account belongs to one customer and settles to one wallet, set by blockchain_wallet_id. A customer can hold more than one. Each deposit creates a payin, which you track like any other payment.
Plan for a review before the account works. A new virtual account starts in pending_review for compliance review, moves to verifying for the banking partner's approval, and only shows rail numbers once approved. US virtual accounts accept ACH, wire, and SWIFT deposits, and each costs $1.50 per month per account (virtual accounts). If you settle in USDT, the linked wallet has to be on a network that supports it.
For one-off collections instead of a standing account, create a payin quote with blockchain_wallet_id and share the payment instructions it returns, such as a Pix code or a SPEI CLABE. Stablecoin virtual accounts explained compares the two.
Outcome: the user sees the exact rate, fee, and received amount before anything moves.
A quote locks the exchange rate and fees for a payout. On BlindPay, a payout quote is created against a bank account and a network and token pair, and it expires 5 minutes after creation. Amounts are integers in minor units, so 10000 means $100.00.
Quote-then-execute prevents surprise pricing. The user confirms a firm number, the rate can't drift between the confirmation screen and execution, and an expired quote costs nothing: request a new one, since no funds moved. Show the fee breakdown from the response, not a number you computed yourself. Stablecoin API quotes explained covers expiry, fee direction, and minor units.
Outcome: the wallet authorizes exactly the quoted amount, and local currency goes out on the recipient's rail.
This is the step where your wallet integration and the payments layer meet. The authorization method depends on the chain.
| Network | How the wallet authorizes | Then |
|---|---|---|
| EVM: Ethereum, Polygon, Base, Arbitrum, Tempo, Arc | An ERC-20 approve for the quoted amount, using the token and contract details returned in the quote | Create the payout at /payouts/evm with quote_id and sender_wallet_address |
| Stellar | /payouts/stellar/authorize returns an unsigned transaction for the wallet to sign | Create the payout at /payouts/stellar with the signed transaction |
| Solana | /prepare-delegate-solana returns a token delegation transaction for the wallet to sign and submit | Create the payout once the delegation confirms |
Who signs depends on your integration model:
Gas is the other detail. An EVM approve is an on-chain transaction, so the wallet needs the network's gas token, except on Arc, where USDC is the gas token. Payouts then go out over Pix, SPEI, ACH, RTP, SEPA, SWIFT (POBO/COBO), TED, ACH Colombia, and Transfers 3.0, depending on the recipient's bank account.
If your wallet can't sign contract approvals at all, because of policy limits or a custody setup that only sends plain transfers, use an off-ramp wallet instead: a deposit address that converts each deposit and pays the linked bank account.
Outcome: your order state follows the payment, without polling.
Payins and payouts move through the same five statuses, and each change fires a webhook: payin.new, payin.update, and payin.complete for deposits, and payout.new, payout.update, and payout.complete for payouts. Every call is signed, so verify the svix-id, svix-timestamp, and svix-signature headers before trusting a payload (webhooks).
| Status | Payin meaning | Payout meaning | Terminal | Your order state |
|---|---|---|---|---|
processing | Waiting for the deposit, or converting and sending stablecoins | Pulling stablecoins, fiat in flight | No | Pending |
on_hold | Held for manual risk or compliance review | Held for review; all SWIFT payouts start here | No | In review |
completed | Stablecoins delivered to the wallet | Fiat landed in the recipient's account | Yes | Paid |
failed | The payin did not go through | The payout did not complete | Yes | Failed, needs follow-up |
refunded | The deposit was returned to the sender | Stablecoins returned to the funding wallet | Yes | Returned |
Webhooks can arrive twice or out of order. Store each event ID, ignore duplicates, and never move an order backward from a terminal state. Send an Idempotency-Key header on every POST, so a retried request replays the original response instead of creating a second payout. Stablecoin API webhooks covers reconciliation, and payout statuses explained covers each status in depth.
Outcome: every failure path ends in a known state with a known next action.
Payments that can't settle don't all come back the same way, so don't promise users that they do. Handle each case on its own:
Outcome: every path, including failures, has run end to end before a real dollar moves.
A BlindPay development instance runs on testnets with USDB, a test stablecoin with no mainnet deployment. Payins complete automatically about 30 seconds after creation. Payout quotes for $666.00 end failed and $777.00 end refunded, so you can exercise the unhappy paths on purpose. On stellar_testnet and solana_devnet you can mint USDB straight into a wallet. Tron has no testnet, so test Tron flows carefully in production with small amounts.
Before launch, check each item:
Idempotency-Key.failed and refunded payouts both land in the right order state.Sandbox vs production lists what testing on testnets can't catch.
If you build with an AI coding assistant, point it at the same flow. BlindPay's MCP server (npx -y @blindpay/mcp) exposes the API as tools, the agent skills teach it the rails and gotchas, and the prompt library has ready-to-paste prompts for payin and payout quickstarts. Build with AI has the setup.
Start with step 1 and ship pay-ins first: register wallets, open virtual accounts, and listen for payin.complete. Once deposits land reliably, add payouts one chain at a time, beginning with the chain most of your wallets already use.
The blockchain wallets and payouts docs have every field for steps 3 and 6.
AP2, ACP, and x402 each verify that an AI agent had permission to spend. Here is what every protocol covers, who backs it, and the reconciliation gap none of them close.
Seven stablecoin payment platforms compared for US fintechs in 2026: what makes an API production-ready, how each provider handles compliance, settlement speed against ACH, and how to run the evaluation.
How to choose a stablecoin payment provider in 2026: the four provider types, a comparison of 10 options, and the questions that decide the fit.