How to add blockchain payments to a mobile fintech app: architecture, onboarding, quotes, and push notifications

Add stablecoin payins and payouts to an iOS or Android app: keep API keys on your server, onboard users in-app, show live quotes, and push webhook updates.

To add blockchain payments to a mobile fintech app, put your own backend between the app and the payments API. The server holds the API key, creates customers, requests quotes, and executes payins and payouts. The app collects input, shows a live quote with a countdown, and receives push notifications that your server sends after verifying each webhook.

None of this needs the user to understand a blockchain. The blockchain payments vs crypto payments guide covers why: the stablecoin is the settlement layer, and the user sees dollars, reais, or pesos.

Key takeaways

  • The API key lives on your server. A key in an app bundle is a key an attacker can extract.
  • Onboarding is a hosted terms of service page plus KYC data from your own screens.
  • Quotes expire. Count down from the server's expires_at, and re-quote rather than retry.
  • Mobile networks drop requests. One idempotency key per confirm tap stops duplicate payouts.
  • Webhooks go to your server first, then become push notifications.

What does the architecture look like?

Three parts: the app, your backend, and the payments API. The app never talks to the payments API directly.

ComponentRuns whereHoldsDoes
Mobile appUser's phoneYour own session tokenCollects input, shows quotes and statuses
Your backendYour serversPayments API key, webhook secretCalls the API, stores state, sends pushes
Payments APIProviderCustomers, quotes, payoutsConverts, settles, pays out
Push serviceApple or GoogleDevice tokensDelivers notifications

Definition: A backend for frontend is a thin server layer that exists only to serve one app. It authenticates your user, calls third-party APIs with secrets the app must not hold, and returns only the fields a screen needs.

This is the same shape you would use for any card or bank API. The blockchain part sits behind the payments API, so the app code looks like any other fintech app.

Why can't the app hold the API key?

Because anything inside an app bundle can be read by someone who downloads it. BlindPay's API key docs say it directly: never expose API keys in client-side code or mobile apps.

The stakes are high for payments. A BlindPay key carries full read and write access to its instance, so a leaked key could create payouts. OWASP ranks improper credential usage as M1 in its 2024 Mobile Top 10, and hardcoded credentials are the first example it gives.

On the server side, two extra controls help. Restrict each key to your servers' IPv4 addresses with ip_whitelist, and keep separate keys for your development and production instances. BlindPay also ships official SDKs for Node.js, Python, Go, PHP, and Swift; whichever one you use, run it where the key is safe.

How does onboarding work on a phone?

The user accepts terms of service on a hosted page, then your server creates the customer with the KYC data your app collected. Two screens of your own, one hosted page.

  1. Generate the terms link. Your server calls BlindPay's terms of service endpoint and gets back a URL, per the terms of service docs. The page shows your instance logo and name, and uses your accent color on the accept button.
  2. Open it in an in-app browser. The terms can only be accepted by the user, client side, on app.blindpay.com, so the page must load on the phone. Set a redirect_url you control. On mobile, a verified link such as an iOS universal link or an Android App Link can send the user straight back into your app.
  3. Catch the tos_id. The redirect carries a tos_id query parameter. Send it to your server.
  4. Create the customer. Your server creates the customer with the tos_id and the identity fields from your screens.
  5. Wait for the review. KYC Standard takes about 60 seconds and is automatic. KYC Enhanced and KYB Standard take 3 hours to 1 business day, per the sandbox vs production guide. A customer.update webhook tells your server when the status changes.

Design the waiting state. A user who finishes onboarding at 11pm and gets a manual review needs a screen that says what happens next, and a push when it clears.

How should a quote look on a small screen?

Lead with the amount the recipient gets, then fees, then a countdown. Users read the top number and the timer, so put the firm numbers there.

BlindPay payout quotes expire 5 minutes after creation and return expires_at as epoch milliseconds, per the payout quotes docs. Start the countdown from that value, not from when the phone received the response, since a slow network can eat 10 seconds. When the timer hits zero, disable the confirm button and fetch a new quote.

Screen elementSource fieldNote
Recipient getsreceiver_amountMinor units, so 10000 means 100.00
You sendsender_amountIncludes fees if the sender covers them
Countdownexpires_atEpoch milliseconds
Fee linesflat_fee, partner_fee_amount, billing_fee_amountShow each one, not only a total
Rateblindpay_quotationThe rate net of BlindPay's fee

Two quote settings change the copy. currency_type: receiver fixes the amount that lands, which suits "pay exactly 500 reais". cover_fees: true adds fees on top of what the sender pays. The quote screen guide covers fee disclosure, and handling expired quotes has the re-quote logic in TypeScript.

How do you stop a flaky connection from paying twice?

With an idempotency key per user action, reused on every retry. Phones switch between Wi-Fi and cellular mid-request all the time, and a timeout does not tell you whether the payout was created.

Generate the key when the user taps confirm, send it to your server, and have the server pass it as the Idempotency-Key header. BlindPay replays the original response for the same key and an identical body, for 24 hours, per its idempotency docs. The body check is byte for byte, so your server must store and resend the exact body, not rebuild it.

SituationWhat the server does
Request timed outRetry with the same key and same bytes
409 idempotency_key_in_flightWait for the Retry-After seconds, then retry
422 idempotency_key_payload_mismatchBug: the body changed. Stop and log
User taps confirm again after a new quoteNew action, new key

The idempotency keys guide goes deeper on key design.

Do your users need a crypto wallet?

No. Most mobile fintech users never see one. Bank transfers in and local bank payouts out cover the common flows, and the stablecoin settles between them.

If your app holds stablecoin balances, you pick one of two models. A wallet the user controls means the app asks for an on-chain authorization per payout: an ERC-20 approve on EVM chains, a signed transaction on Stellar, or a token delegation on Solana. A BlindPay-managed wallet (beta) needs no signing on the phone, but it is custodial, so read the custodial vs non-custodial wallet comparison before choosing.

For user-controlled EVM wallets, register the address with a signed message rather than a pasted address. BlindPay recovers the address from the signature, so a typo cannot send funds to the wrong place.

How do webhooks become push notifications?

Your server receives the webhook, verifies it, stores the status, and then sends the push. The phone never receives the raw webhook.

Verify the svix-id, svix-timestamp, and svix-signature headers against the raw body before trusting a payload, and use svix-id to drop duplicate deliveries, per the webhook verification docs. Then map events to messages:

EventPush copy example
customer.update"Your account is verified" (or "We need one more document")
virtualAccount.complete"Your US account details are ready"
payin.complete"Your deposit arrived"
payout.new"Your transfer is on its way"
payout.updateRead status: on hold, failed, or in progress
payout.completeRead status: completed or refunded

Keep pushes short and let the app fetch details from your server. Lock screens show the first line to anyone nearby, so leave amounts and names out of the notification text. Payout statuses explained lists what each status means for the user.

What should a mobile team look for in a blockchain payments provider?

Look for a server-side API with clear mobile guidance, not a mobile SDK that wants your key on the phone. A short checklist:

  • Server-side auth with IP allowlisting and separate keys per environment.
  • Hosted onboarding pieces that open in an in-app browser and redirect back.
  • Quotes with an explicit expiry you can count down from.
  • Idempotency on every mutating call.
  • Signed webhooks for every state change, so pushes are timely.
  • A development environment with test tokens and forced outcomes.

BlindPay covers each of these. Development instances auto-approve KYC, use USDB on testnets, and complete payouts automatically. A payout quote with request_amount of 66600 forces a failed payout and 77700 forces a refund, so you can test every push and screen before launch. The blockchain payment API checklist has the full evaluation list.

What to do next

Sketch your three-box architecture, then wire one flow end to end on a development instance: terms, customer, quote, payout, push. Start with the BlindPay payout quickstart and run it from your backend, not the app.

FAQ