How to Accept Bitcoin Payments on Your Website in 2026
How to Accept Bitcoin Payments on Your Website in 2026
Bitcoin has been a payment method for over a decade, but actually accepting it on a website has historically required either technical complexity or expensive third-party processors. In 2026, that's changed.
This guide walks through why you'd accept Bitcoin, what the trade-offs are, and the fastest way to integrate BTC payments into any web application.
Why Accept Bitcoin Payments?
1. No Chargebacks — Ever
Bitcoin transactions are irreversible once they reach a sufficient number of confirmations. For merchants selling digital goods, software licenses, or services, this eliminates the chargeback fraud that costs card-accepting businesses an estimated 1-2% of revenue. When a Bitcoin payment confirms, it's final.
2. No Geographic Restrictions
Stripe blocks hundreds of countries. PayPal freezes accounts. Bank wires take days and cost $25-45 per transaction. Bitcoin works the same way whether your customer is in San Francisco, Jakarta, or Lagos. There's no application process, no compliance review, no geographic exclusion.
3. Lower Fees at Higher Volume
Card processors charge 2.9% plus a per-transaction fee. Bitcoin network fees are paid by the sender and are based on blockchain congestion, not transaction value — meaning a $10,000 Bitcoin transfer costs roughly the same to process as a $100 one. At high transaction values, the fee advantage is significant.
4. Immediate Settlement
With traditional payment processors, funds sit in a pending state for 2-7 business days before you can use them. Bitcoin settles to your wallet once confirmations clear — typically 30-60 minutes for 3 confirmations. No payment processor holds your money.
How Bitcoin Payments Work (Technically)
Understanding the mechanics helps you build a better integration.
Step 1: Address Generation A unique Bitcoin address is generated for each payment. This address is derived from a master HD wallet (Hierarchical Deterministic) using a path like m/84'/0'/0'/0/{index}. Each order gets its own index, ensuring payments never overlap.
Step 2: Customer Sends BTC The customer sends Bitcoin to the generated address. On the blockchain, this appears as an unconfirmed transaction immediately, then starts accumulating confirmations as new blocks are mined.
Step 3: Confirmation Monitoring The payment system monitors the blockchain via an API (BlockCypher, Electrum, or a Bitcoin node) and checks the confirmation count on each pending transaction.
Step 4: Completion Trigger Once a threshold is reached (typically 3 confirmations, roughly 30 minutes), the order is marked complete and a webhook fires to your backend.
Why 3 confirmations? Each block added after the payment makes reversing the transaction exponentially harder. After 3 blocks, a double-spend attack would require controlling over 50% of Bitcoin's hashrate — economically infeasible.
The Real Challenges of Bitcoin Payments
Before showing you how to accept BTC, it's worth being honest about the challenges.
Price Volatility
Bitcoin's value fluctuates. If you price in USD and accept BTC, there's a window between order creation and payment confirmation where the rate can move. Solutions:
- Short expiry windows (30 minutes) limit exposure
- Stablecoins (USDT) eliminate volatility entirely
- Instant conversion to fiat reduces risk but adds complexity
Slower Confirmation Times
Bitcoin averages one block every 10 minutes, and 3 confirmations takes roughly 30 minutes. This is slower than card payments (instant) or USDT on TRON (under 2 minutes). For time-sensitive purchases, this matters.
Customer Friction
Not everyone owns Bitcoin. If your audience is non-technical, requiring a crypto wallet adds friction. Bitcoin works best for audiences already familiar with crypto — developers, traders, crypto-native users.
Integrating Bitcoin Payments with ChainPay
ChainPay handles all the complexity: HD wallet derivation, blockchain monitoring, and webhook delivery. Your integration is three API calls.
Step 1: Create a Bitcoin Order
curl -X POST https://chainpay.pro/api/v1/orders \
-H "Authorization: Bearer sk_live_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"amount": "50.00",
"currency": "BTC",
"chain": "btc",
"externalId": "order_12345",
"metadata": { "userId": "user_789", "plan": "pro" }
}'
Response:
{
"orderId": "ord_abc123",
"payAddress": "bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh",
"cryptoAmount": "0.00073",
"currency": "BTC",
"chain": "btc",
"checkoutUrl": "https://chainpay.pro/pay/ord_abc123",
"expiresAt": "2026-03-22T11:30:00Z",
"status": "pending"
}
ChainPay fetches the real-time BTC/USD rate from CoinGecko and calculates the exact BTC amount your customer needs to send.
Step 2: Redirect to Checkout
The checkoutUrl points to ChainPay's hosted checkout page. Your customer sees:
- The exact BTC amount to send
- A QR code for mobile wallet scanning
- The bc1... wallet address (copyable)
- A countdown timer (30 minutes)
- Live status updates as confirmations accumulate
You don't need to build any payment UI.
// In your backend route handler
const order = await fetch('https://chainpay.pro/api/v1/orders', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.CHAINPAY_API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
amount: cart.totalUSD.toString(),
currency: 'BTC',
chain: 'btc',
externalId: `order_${cart.id}`
})
}).then(r => r.json());
// Redirect customer to checkout
return redirect(order.checkoutUrl);
Step 3: Handle the Webhook
When 3 confirmations are reached, ChainPay sends a signed POST to your webhook URL:
{
"event": "payment.completed",
"orderId": "ord_abc123",
"externalId": "order_12345",
"amount": "50.00",
"currency": "BTC",
"chain": "btc",
"txHash": "3a9b7c...",
"netAmount": "0.000724",
"completedAt": "2026-03-22T11:02:17Z"
}
Verify the signature and activate the order:
const crypto = require('crypto');
app.post('/webhooks/chainpay', express.raw({ type: 'application/json' }), (req, res) => {
const sig = req.headers['x-chainpay-signature'];
const expected = 'sha256=' + crypto
.createHmac('sha256', process.env.CHAINPAY_WEBHOOK_SECRET)
.update(req.body)
.digest('hex');
if (sig !== expected) return res.status(401).end();
const event = JSON.parse(req.body);
if (event.event === 'payment.completed' && event.chain === 'btc') {
// Activate the order
await activateOrder(event.externalId);
console.log(`BTC payment confirmed: ${event.txHash}`);
}
res.status(200).end();
});
BTC vs USDT: Which Should You Accept?
| Bitcoin (BTC) | USDT TRC20 | |
|---|---|---|
| Confirmation time | ~30 minutes (3 blocks) | ~1 minute (20 blocks) |
| Price volatility | High | None (stablecoin) |
| Network fee (sender) | $1-10 depending on congestion | < $1 |
| Customer familiarity | Very high | Growing |
| Chargeback risk | None | None |
| Best for | Larger transactions, crypto-native audiences | Speed-sensitive, all audiences |
For most use cases, USDT TRC20 is the better default — faster, cheaper, and no volatility. Add BTC as an option for customers who prefer it or for larger transaction amounts where the 30-minute wait is acceptable.
ChainPay supports both simultaneously. Create separate orders per currency, or let customers choose at checkout.
Handling the 30-Minute Wait
The biggest UX challenge with Bitcoin is the confirmation window. Here's how to handle it well:
Show progress, not just a spinner. ChainPay's hosted checkout shows the confirmation count in real-time (0/3, 1/3, 2/3, 3/3). This is more reassuring than a generic "processing" message.
Set expectations upfront. On your payment selection screen, note that Bitcoin typically takes 30-60 minutes. Customers who choose it know what to expect.
Send a confirmation email at 0 confirmations. The moment a transaction appears in the mempool (unconfirmed), you can notify the customer that payment was received and is being confirmed. This reduces support inquiries.
Use the confirming status. ChainPay webhooks include a payment.confirming event at 0 confirmations. Use this to send an interim notification:
if (event.event === 'payment.confirming') {
await sendEmail(customer.email, 'Payment Received — Confirming');
}
Settlement: Getting BTC to Your Wallet
ChainPay receives Bitcoin into a managed HD wallet and settles to your configured payout address daily at 02:00 UTC.
To configure your BTC payout wallet:
- Go to Dashboard → Settings → Payout Wallets
- Enter your native SegWit address (bc1q...)
- Save
Funds accumulated throughout the day are swept to your wallet in a single daily transaction, net of the 0.8% platform fee.
Note: ChainPay currently settles TRC20 and ERC20 automatically. BTC and SOL settlement is implemented — contact support if you need manual settlement for large accumulations.
Getting Started
- Register at chainpay.pro — no documents, instant
- Generate your API key in Settings
- Add your Bitcoin payout address
- Make one
POST /api/v1/orderscall withchain: 'btc' - Handle the
payment.completedwebhook
The full integration takes under an hour for most developers.