On this page
Key takeaways
- Accepting stablecoins is less about the chain and more about the operating model around it.
- You need five things: an intent model, a network watcher, a confirmation policy, signed events, and a reconciliation routine.
- Decide keys, networks, and confirmation policy before writing integration code.
- Start with one network and one token, and go live with a runbook rather than a demo.
Most guides to stablecoin payments start with a wallet address and end with a screenshot of a transaction. That is the easy ten percent. The other ninety is what happens after a customer clicks pay: tracking, confirming, telling your application, and explaining the result to finance.
This guide walks through the full path for a team that wants to accept stablecoin payments on infrastructure it controls. It is written for engineers and technical founders. It is not legal, tax, or compliance advice — those questions belong to your own advisers.
What you are actually building
Strip away the branding and a payment gateway does five jobs. Every option — building it, buying it, or renting it — has to cover all five.
| Job | The question it answers | What goes wrong if it is skipped |
|---|---|---|
| Create | What is this payment for, and how much? | Payments cannot be matched to orders |
| Track | Did value actually arrive? | You poll a chain explorer by hand |
| Confirm | Is it final enough to act on? | Goods released for reversible payments |
| Notify | How does the app find out? | Missed events and stuck orders |
| Reconcile | Do all records agree? | Finance stitches exports on Sunday |
Before you write code: five decisions
- 01
Who holds the keys?
If your organisation holds them, you operate software. If a vendor does, you use a service. This choice drives your legal, security, and operational posture.
- 02
Which network and which token?
Start with one of each. Choose what your customers already hold, and only enable what your software actually implements and tests.
- 03
What does "confirmed" mean?
Confirmation is a policy: the point at which your business treats a payment as paid. Write it down per network, per amount tier.
- 04
How will the application find out?
Signed webhooks with a replay path, not polling. Decide who verifies, who deduplicates, and who is on call.
- 05
Who reconciles, and how often?
Name the person and the routine before the first live payment, not after the first incident.
The reference architecture
A self-hosted deployment is smaller than most people expect. The pieces are ordinary: an API, a database, an indexer that watches the network, and an operator dashboard.
| Layer | Responsibility | Where it runs |
|---|---|---|
| Checkout and dashboard | Payment surfaces and operator UI | Inside your deployment |
| API and webhooks | Create intents, emit signed lifecycle events | Inside your deployment |
| Database and indexer | Payment state and confirmation tracking | PostgreSQL you operate |
| Networks | The chains, tokens, and RPC providers you run | Providers you choose |
| Signing | Authority to move funds | Your secret store, not the vendor's |
What this is not
This architecture describes StablePay, which is payment gateway software you deploy. It is not a hosted processor, a custodian, or a bank, and the studio does not hold your funds.
The integration, end to end
1. Create the intent
Your application creates a payment intent with its own reference — usually the order id — and an amount. Use an idempotency key derived from the order so retries never create duplicates.
2. Show the payer what to do
The checkout surface presents the amount, network, and address or link. Keep the instructions unambiguous: wrong-network payments are the most common support ticket in this category.
3. Listen for signed events
The gateway emits events as the payment moves through its lifecycle. Your handler verifies the signature on the raw body, deduplicates on the event id, stores the event, and returns 2xx quickly.
export async function POST(req: Request) {
const raw = await req.text();
if (!verifySignature(raw, req.headers.get("x-signature") ?? "", SECRET)) {
return new Response("invalid", { status: 401 });
}
const event = JSON.parse(raw);
if (await alreadyProcessed(event.id)) return new Response(null, { status: 204 });
await db.transaction(async (tx) => {
await tx.recordEvent(event.id);
if (event.type === "payment.confirmed") await tx.markOrderPaid(event.reference);
});
return new Response(null, { status: 204 });
}4. Fulfil only on your confirmation policy
Do not fulfil on "seen". Fulfil when the gateway reports the payment as confirmed under your policy.
5. Reconcile daily
Run a query for payments confirmed at the gateway but not fulfilled by the application. It should return nothing. When it does not, that is your first warning of a broken handler.
Common mistakes
What costs teams a weekend
- Fulfilling on the first sighting of a transaction
- Verifying a re-serialised body, not the raw one
- One global confirmation number for every amount
- No plan for underpayments or wrong-network payments
- Keys in an environment file in a repository
What holds up
- A written confirmation policy per network and tier
- Signature verification on the raw body
- Idempotency keys and event-id deduplication
- A runbook entry for every named exception
- Keys in a secret store with logged access
A realistic launch plan
- Deploy to staging with the same secrets architecture you will use in production.
- Run test payments through every state, including failures and expiries.
- Fire-drill the runbook: break a webhook and recover with a replay.
- Go live with low limits and a named person on call.
- Review reconciliation daily for the first two weeks, then weekly.
Go-live checklist
- Confirmation policy written and signed off by the business.
- Keys in a secret store; access restricted and logged.
- Signed webhooks verified against the raw body.
- Duplicate events ignored; handlers are idempotent.
- Runbook covers silent confirmations, underpayments, and expired links.
- A named person owns reconciliation and patch releases.
Choosing networks and tokens with care
Start with what your customers already hold and what your software actually implements. Every additional network multiplies your operational surface: another RPC provider, another confirmation policy, another set of failure modes, another runbook entry. One network and one token is a legitimate launch scope.
| Consideration | Question | Why it matters |
|---|---|---|
| Customer holdings | Which token and network do payers already use? | A payment option nobody holds is not an option |
| Fees and speed | What does a transfer cost and how long until it is final? | Fees affect small payments most; finality drives your policy |
| Token standard | Is it a standard fungible token with predictable transfer events? | Non-standard behaviour complicates indexing |
| Decimals | How many decimal places on this network? | The same token can use different decimals on different networks [1] |
| Provider maturity | Are there reliable RPC providers, and can you run a node? | Your view of the chain depends on them |
| Implemented and tested | Does your gateway support it today? | Aspirational support is a liability |
Read decimals per token and per network
Do not hard-code six or eighteen. The ERC-20 standard treats
decimalsas an optional convenience [1], and stablecoins are deployed with different values across networks. Read it from the token contract for the network in question and store amounts as integer minor units.
Address strategy: how a payment finds its order
The core design question of any crypto checkout is how to know which payment belongs to which order. There are three common approaches, each with trade-offs.
| Approach | How it works | Pros | Cons |
|---|---|---|---|
| One address per payment | Derive a fresh address for each intent and watch it | Unambiguous matching; no reference needed | More addresses to sweep and monitor |
| Shared address plus reference | One address; the payer includes a reference | Simple to operate | Depends on payers entering the reference correctly |
| Shared address plus exact amount | Unique amount per intent (for example a small offset) | No extra payer input | Collisions and underpayments are harder to match |
Per-payment addresses are the most robust for most business use, at the cost of a sweeping and monitoring process. Whichever you choose, write down the matching rule and how it fails, because the failures are what your support team will see.
Underpayments, overpayments, and the messy middle
Real payers send the wrong amount, send twice, pay after expiry, or pay on the wrong network. Decide the policy for each case before launch, and make the software's states match.
| Case | Suggested default | Who decides |
|---|---|---|
| Underpayment | Mark partially paid; show the shortfall; let the payer top up | Business owner sets tolerance |
| Overpayment | Fulfil the order; record the excess; queue a refund decision | Finance |
| Double payment | Fulfil once; queue the duplicate for refund | Finance |
| Paid after expiry | Hold in suspense; contact the payer; decide honour or refund | Operations |
| Wrong network | Do not improvise; follow the documented recovery path | Payments lead, with counsel if unsure |
Testing before real money
- 01
Run against a test network
Use a public test network and test tokens to exercise every state, including failures.
- 02
Simulate the ugly cases
Send an underpayment, an overpayment, a late payment, and a payment to an expired link.
- 03
Break the webhook
Return errors from your endpoint and confirm retries, dead-lettering, and replay all work.
- 04
Kill the provider
Block your RPC provider and verify failover and alerting.
- 05
Restore from backup
Prove you can rebuild the gateway and reconcile afterwards.
Security hardening in five moves
Before go-live
- Secrets in a secret store, never in the repository or image [2].
- The gateway and database are not reachable from the public internet except the intended endpoints.
- Webhook secrets rotated on a schedule, with a dual-secret overlap.
- Least-privilege credentials for the database and the RPC provider.
- Patch releases have an owner and a cadence.
The OWASP cheat sheets on secrets management [2] and on securing Docker deployments [3] are a practical baseline; the twelve-factor app [4] is a useful reference for the configuration and logging conventions around the deployment.
What to monitor from day one
| Signal | Why | Threshold idea |
|---|---|---|
| Indexer lag | Late confirmations | Alert when lag approaches your confirmation window |
| Webhook failure rate | Silent confirmations | Alert on sustained non-2xx from your endpoint |
| Unmatched payments | Reconciliation drift | Alert on any older than a set age |
| Provider errors | Impending outage | Alert on error and rate-limit spikes |
| Unexpected signing activity | Possible compromise | Alert on any signature outside policy |
Frequently asked questions
Do I need to run my own blockchain node?
Not necessarily. Many teams start with reputable RPC providers, ideally two for redundancy. Running your own node gives you more control and independence and becomes attractive as value at risk grows.
How long does a first integration take?
In our engagements the median first release of a focused system is around six weeks, and Vellum reached a first live payment on StablePay in seventeen days. The variance comes from the confirmation policy, the approval flow, and the runbook — not from the API calls.
Can we accept several tokens at once?
Yes, but add them one at a time, each with its own tested confirmation policy and runbook entries.
Where to go next
If you would rather not assemble this yourself, StablePay is the software version of everything above — deployed on your infrastructure, with the event contract, dashboard, and runbooks included. If you would rather build it, the same five jobs and five decisions apply.
References & further reading
- 1EIP-20: Token Standard — Fabian Vogelsteller, Vitalik Buterin, Ethereum Improvement Proposals
- 2Secrets Management Cheat Sheet — OWASP Cheat Sheet Series
- 3Docker Security Cheat Sheet — OWASP Cheat Sheet Series
- 4The Twelve-Factor App — Adam Wiggins, 12factor.net
- 5JSON-RPC API — ethereum.org developer documentation
The product behind this post
StablePay
Self-hosted stablecoin payment infrastructure.
From $4,800 one-time license
About the author
Product behaviour described here reflects what is implemented and tested; anything else is marked as planned. Code samples are illustrative.
All writing