Crypto Payment Gateway Website Integration
Typical task: there's a working website with a cart and regular payment processing, need to add cryptocurrency payment. The main mistake here is doing it yourself from scratch: generating addresses, listening to transactions, processing confirmations. For most e-commerce projects this is excessive and creates operational burden. The right approach — use established processing (Coinbase Commerce, NOWPayments, CryptoPay, BTCPay Server) and integrate via webhook.
Choosing Processing
| Solution | Custody | Commission | Self-hosted | Suitable for |
|---|---|---|---|---|
| Coinbase Commerce | Non-custodial | 1% | No | Quick start, USD market |
| NOWPayments | Custodial (auto-conversion) | 0.5–1% | No | Many coins, auto-conversion to fiat |
| BTCPay Server | Non-custodial | 0% | Yes | Full control, BTC/LN only |
| CoinGate | Custodial | 1% | No | E-commerce plugins |
| Pure custom | Non-custodial | 0% | — | High load, special requirements |
For most sites optimal: NOWPayments (wide coin list, auto-conversion to USDT/USDC) or Coinbase Commerce (if audience is mostly American).
Integration Scheme
Basic flow through any processor is the same:
- User clicks "Pay with cryptocurrency"
- Your site backend creates invoice via processor API
- User receives address and amount to pay
- Processor monitors blockchain and notifies your backend via webhook
- Backend verifies webhook, marks order as paid
Example: NOWPayments
# Payment creation
curl -X POST https://api.nowpayments.io/v1/payment \
-H "x-api-key: YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"price_amount": 99.99,
"price_currency": "usd",
"pay_currency": "eth",
"order_id": "ORDER-12345",
"ipn_callback_url": "https://yoursite.com/api/crypto-webhook"
}'
Response contains pay_address, pay_amount (in ETH at current rate), payment_id. Show user the address and amount, optionally — QR code.
Webhook Handling (Critical)
Webhooks cannot be accepted naively — signature verification is needed:
// PHP example for NOWPayments
public function handleWebhook(Request $request): JsonResponse
{
$payload = $request->getContent();
$signature = $request->header('x-nowpayments-sig');
// Verify HMAC-SHA512
$expected = hash_hmac(
'sha512',
$this->sortPayload($payload),
config('services.nowpayments.ipn_secret')
);
if (!hash_equals($expected, $signature)) {
return response()->json(['error' => 'Invalid signature'], 401);
}
$data = json_decode($payload, true);
// Process only final statuses
if ($data['payment_status'] === 'finished') {
$this->orderService->markAsPaid($data['order_id'], [
'tx_hash' => $data['outcome_hash'],
'amount_paid' => $data['actually_paid'],
'currency' => $data['pay_currency'],
]);
}
return response()->json(['status' => 'ok']);
}
Statuses to handle: waiting → confirming → confirmed → sending → finished. Status failed and expired — separate logic (unlock goods, notify user).
Underpayment and Exchange Rate Risks
Users often pay slightly less than needed (forgot gas, rounded). Need explicit policy: acceptable deviation ±1–2%, or require additional payment. NOWPayments has is_fixed_rate parameter — fixes rate for 20 minutes while user pays. This reduces exchange rate risks for both sides.
What to Implement on Your Side
- Payment creation endpoint (POST /api/create-crypto-payment)
- Webhook handler with signature verification
- Payment status waiting page (poll every 10–15 sec)
- Expired payment handling (timeout 15–60 min depending on network)
- Logging of all webhook events for reconciliation
Complete integration from scratch to production takes 2–3 days including testnet/sandbox testing and edge cases.







