How to Accept Crypto Payments: A Developer's Guide to Self-Hosted Infrastructure
A complete, step-by-step walkthrough for accepting cryptocurrency payments using a self-hosted, non-custodial gateway. From Docker deployment to webhook integration — everything you need to start accepting USDT, USDC, and other crypto assets with zero transaction fees and full control over your private keys.
Why Accept Crypto Payments?
Cryptocurrency payments have evolved from a niche experiment to a mainstream payment rail processing billions of dollars daily. For merchants, accepting crypto is no longer about speculation — it is about accessing a faster, cheaper, and more inclusive financial system. Here is why businesses are adding crypto checkout to their payment stack:
Global Reach
Crypto payments work across borders with no currency conversion, no cross-border fees, and no bank account requirements. Anyone with a wallet can pay you, regardless of their country, banking status, or credit score. This opens your business to the 1.4 billion unbanked adults worldwide and the rapidly growing crypto-native consumer base.
Low Fees
Traditional payment processors charge 1.5–3.5% plus a per-transaction fee. A self-hosted crypto gateway charges 0% transaction fees — you only pay network gas fees. On TRON, a USDT transfer costs ~$0.02–$0.10. On an L2 like Arbitrum, it is often under $0.01. For a business processing $100k/month, the savings exceed $20k/year.
No Chargebacks
Blockchain transactions are irreversible. Once a payment is confirmed on-chain, it cannot be reversed or charged back. This eliminates the #1 cost for online merchants: payment fraud and dispute fees. For digital goods merchants where chargeback rates can exceed 5%, switching to crypto can dramatically improve bottom-line margins.
Financial Privacy
With a self-hosted, non-custodial gateway, you retain complete control over your funds and financial data. No KYC requirements, no payment processor account freezes, and no third party with the power to restrict your access. Your customers' payment data stays between them and the blockchain — no payment processor databses to leak or monetize.
Self-Hosted vs Third-Party Gateways
Before diving into setup, it is important to understand the tradeoffs between self-hosted and hosted (third-party) crypto payment gateways. Each model serves different needs:
| Aspect | Third-Party (Hosted) | Self-Hosted |
|---|---|---|
| Setup Time | Minutes (SaaS signup) | 30 minutes (Docker deploy) |
| Transaction Fees | 1 – 3% per transaction | 0% (gas only) |
| Monthly Cost | $30 – $300+ plan fees | $10 – $20 (VPS hosting) |
| Key Custody | Custodial (processor holds keys) | Non-custodial (you hold keys) |
| Settlement | Delayed (daily/weekly batches) | Instant (direct to wallet) |
| KYC Required | Yes (business verification) | No (self-sovereign) |
| Account Freeze Risk | Yes (processor decides) | None (you control access) |
| White-Label Checkout | Processor branding required | Full white-label (your brand) |
| Source Access | Closed-source | Fully auditable |
| Maintenance | None (managed service) | You manage updates/monitoring |
Self-hosted gateways like XPay Labs are ideal for developers and businesses that want maximum control, minimal fees, and sovereign infrastructure. Third-party gateways make sense for non-technical users who prefer a fully managed solution and are willing to pay for convenience.
Prerequisites
Before you begin, ensure you have the following:
- 1A VPS or cloud server — Linux machine with at least 1 GB RAM, 20 GB storage, and a public IP. Providers like Hetzner ($5/month), DigitalOcean ($12/month), or AWS EC2 work well.
- 2Docker & Docker Compose — installed on your server. Most VPS images come with Docker pre-installed, or you can use the official install script.
- 3A domain name — pointed to your server IP for the gateway API and checkout page with SSL via Let's Encrypt or a reverse proxy like Caddy/Nginx.
- 4RPC API keys — for the blockchains you want to support. TronGrid for TRON, Alchemy or Infura for EVM chains, and a SUI RPC provider. Most have free tiers.
- 5Basic DevOps knowledge — familiarity with the command line, environment variables, and the ability to configure a reverse proxy for TLS termination.
Step 1: Deploy the Core Node
The fastest way to start accepting crypto payments is with Docker Compose. Create a directory on your VPS and save the following as docker-compose.yml:
version: '3.8'
services:
xpay-gateway:
image: ghcr.io/xpay-labs/gateway:latest
container_name: xpay_core
restart: unless-stopped
ports:
- "8080:8080"
environment:
- XPAY_SEED_PHRASE=${XPAY_SEED_PHRASE}
- XPAY_HMAC_SECRET=${XPAY_HMAC_SECRET}
- XPAY_TRON_RPC=https://api.trongrid.io
- XPAY_EVM_RPC=https://eth-mainnet.g.alchemy.com/v2/${ALCHEMY_KEY}
- XPAY_WEBHOOK_URL=https://api.yourdomain.com/webhooks/xpay
volumes:
- ./data:/app/data
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"Run docker compose up -d to start the gateway. On your first launch, XPay Labs generates a new HD wallet seed if XPAY_SEED_PHRASE is not set, or derives addresses from your existing seed. The gateway exposes a REST API on port 8080 and immediately begins scanning configured chains for incoming transactions.
Step 2: Configure Environment
The gateway requires several environment variables to operate. Create a .env file in the same directory as your docker-compose.yml:
| Variable | Required | Description |
|---|---|---|
| XPAY_SEED_PHRASE | Yes | BIP-39 mnemonic seed (12–24 words) for HD wallet derivation. Generate offline with 'openssl rand -hex 16 | xargs -I echo ' or use a hardware wallet. |
| XPAY_HMAC_SECRET | Yes | HMAC key for signing webhook payloads. Use a 64-character hex string. Verify webhooks on your server using this secret. |
| XPAY_TRON_RPC | Optional | TRON full node URL. Defaults to https://api.trongrid.io. For production, use your own TronGrid API key or a dedicated node. |
| XPAY_EVM_RPC | Optional | EVM RPC URL for the chain you want to support. For multi-chain, specify each chain separately (e.g., XPAY_EVM_RPC_ETH, XPAY_EVM_RPC_BNB). |
| XPAY_WEBHOOK_URL | Optional | Default callback URL for all payments. Can be overridden per invoice. Your server must return 200 OK to confirm receipt. |
| XPAY_CONFIRMATIONS | Optional | Number of block confirmations required before triggering a webhook. Default: 19 for TRON, 12 for EVM chains. Lower for faster but riskier detection. |
# .env
XPAY_SEED_PHRASE="abandon abandon apple ... twelve words here"
XPAY_HMAC_SECRET="a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1"
XPAY_TRON_RPC=https://api.trongrid.io
XPAY_EVM_RPC_ETH=https://eth-mainnet.g.alchemy.com/v2/your_key
XPAY_EVM_RPC_BNB=https://bsc-dataseed.binance.org
XPAY_WEBHOOK_URL=https://api.yourdomain.com/webhooks/xpay
XPAY_CONFIRMATIONS=19Step 3: Generate API Keys
With the gateway running, generate a merchant API key to authenticate your payment requests. Use the admin endpoint:
curl -X POST "http://localhost:8080/v1/admin/api-keys" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ${XPAY_ADMIN_TOKEN}" \
-d '{
"label": "production-checkout",
"permissions": ["payments:create", "payments:read"]
}'The response includes an API key prefixed with xpay_live_. Store this securely — it replaces username/password authentication for all payment API calls. You can generate multiple keys with different permission scopes for development, staging, and production environments.
// Example response
{
"id": "key_7f3d9c2a1b",
"label": "production-checkout",
"key": "xpay_live_8f3a9d7219bc4e5f6a7b8c9d0e1f2a3b4c5d6e7f",
"permissions": ["payments:create", "payments:read"],
"created_at": "2026-05-22T10:30:00Z"
}Step 4: Create Your First Payment
Now you can create a payment invoice. Your backend calls this endpoint when a customer reaches checkout:
curl -X POST "https://gateway.yourdomain.com/v1/payments" \
-H "Authorization: Bearer xpay_live_8f3a9d7219bc" \
-H "Content-Type: application/json" \
-d '{
"amount": "100.00",
"currency": "USDT",
"chain": "TRON",
"order_id": "order_783120",
"callback_url": "https://api.merchant.com/v1/webhooks/xpay",
"customer_email": "[email protected]",
"metadata": {
"product_id": "prod_456",
"plan": "premium_annual"
}
}'The gateway responds with a unique deposit address derived from your seed, an invoice ID, and the current payment status:
{
"id": "pay_9f3b8c2a1d",
"order_id": "order_783120",
"amount": "100.00",
"currency": "USDT",
"chain": "TRON",
"deposit_address": "TYpSq7f8MubE8bK6vG7m8F7WbA9c3DxE1F",
"qr_code": "https://gateway.yourdomain.com/v1/qr/pay_9f3b8c2a1d",
"status": "pending",
"expires_at": "2026-05-22T11:00:00Z",
"created_at": "2026-05-22T10:00:00Z"
}Display the deposit address and QR code to your customer. The gateway monitors the blockchain for incoming transactions to this address. Once detected and confirmed, it sends a webhook to your callback URL and updates the invoice status to "confirmed".
Step 5: Handle Webhook Callbacks
XPay Labs sends payment confirmation webhooks to your callback_url via POST with an HMAC-SHA256 signature in the X-Signature header. Here is how to verify and process the payload in Node.js:
import crypto from 'crypto';
// Your HMAC secret from XPAY_HMAC_SECRET
const HMAC_SECRET = process.env.XPAY_HMAC_SECRET;
export async function POST(request: Request) {
const body = await request.text();
const signature = request.headers.get('x-signature');
// Verify HMAC-SHA256 signature
const expected = crypto
.createHmac('sha256', HMAC_SECRET)
.update(body, 'utf8')
.digest('hex');
if (signature !== expected) {
return Response.json({ error: 'invalid signature' }, { status: 401 });
}
const payload = JSON.parse(body);
// payload structure:
// {
// "event": "payment.confirmed",
// "payment": {
// "id": "pay_9f3b8c2a1d",
// "order_id": "order_783120",
// "amount": "100.00",
// "currency": "USDT",
// "chain": "TRON",
// "tx_id": "b4f7c3a12d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2",
// "from_address": "TXYZ...",
// "to_address": "TABC...",
// "confirmations": 32,
// "status": "confirmed",
// "timestamp": 1715875200
// }
// }
if (payload.event === 'payment.confirmed') {
// Fulfill the order
await fulfillOrder(payload.payment.order_id, payload.payment);
return Response.json({ received: true });
}
return Response.json({ received: true });
}Webhooks are sent with exponential backoff retry (3 attempts: 10s, 60s, 300s) until your server returns a 200 OK. Implement idempotency checks using the payment.id to prevent double-fulfillment in case of duplicate webhook deliveries.
Supported Chains & Tokens
XPay Labs supports three blockchain ecosystems out of the box, covering the vast majority of stablecoin and altcoin payment volume. Each chain has unique characteristics that affect transaction speed, cost, and integration approach:
TRON (TRC-20)
TRON is the dominant chain for stablecoin payments, hosting over $50 billion in TRC-20 USDT — more than any other blockchain. Its 3-second block time and sub-cent transaction fees make it ideal for high-volume merchant payments. TRC-20 USDT accounts for the majority of all on-chain stablecoin transfer volume.
- •Block time: ~3 seconds, finality at 19 blocks (~57s)
- •Fee per USDT transfer: ~0.2–1 TRX ($0.02–$0.10) with energy management
- •Supported tokens: USDT, USDC, USDD, TUSD, and any TRC-20 token
- •RPC: TronGrid API (free tier: 10k requests/day) or your own full node
- •Address format: Base58 T-addresses (starts with "T")
EVM Chains (ERC-20)
XPay Labs supports all EVM-compatible chains including Ethereum, BNB Chain, Polygon, Arbitrum, Optimism, and Base. While Ethereum mainnet fees can be high during congestion, L2 networks like Arbitrum and Base offer sub-cent transaction costs with Ethereum-level security guarantees.
- •Block time: 2–15s depending on chain (12s ETH mainnet, 2s Polygon, 0.25s Arbitrum)
- •Fee per USDC transfer: ~$0.50–$5 (ETH), ~$0.0003 (Polygon), ~$0.01 (Arbitrum)
- •Supported tokens: USDT, USDC, DAI, and any ERC-20 token per chain
- •RPC: Alchemy, Infura, public nodes, or your own execution client
- •Address format: 0x-hex addresses (same across all EVM chains)
SUI
SUI is an emerging Layer 1 blockchain built on the Move language with parallel execution and sub-second finality. While its stablecoin ecosystem is still growing, SUI offers the fastest transaction finality of any supported chain and is gaining traction among crypto-native merchants and users.
- •Block time: ~1 second (immediate finality via Narwhal/Bullshark consensus)
- •Fee per transaction: ~0.001 SUI (~$0.001) — fractions of a cent
- •Supported tokens: SUI, USDC (native), and custom Move-based assets
- •RPC: SUI public endpoints or your own full node
- •Address format: 0x-prefixed hex addresses
multi_chain_scan: true. The gateway normalizes amounts based on token decimals and fires the webhook once any configured chain receives the correct payment.Next Steps
You now have a fully functional self-hosted crypto payment gateway. Here is what to explore next to get the most out of your infrastructure:
Deployment Guide
Full documentation covering Docker deployment, environment configuration, CLI reference, and production hardening.
XPay vs BitPay
Detailed comparison between self-hosted XPay Labs and hosted BitPay — fees, custody models, chain support, and migration guide.
TRON Payment Gateway
Deep dive into TRON-specific configuration: TRC-20 token support, energy management, TronGrid integration, and HD derivation.
XPay Labs Overview
Learn about the full XPay Labs platform: multi-chain architecture, non-custodial security model, and developer APIs.
Ready to Accept Crypto Payments?
Deploy XPay Labs on your own infrastructure in under 30 minutes. Zero transaction fees, non-custodial security, and support for TRON, EVM, and SUI chains. No signup required — just Docker.
