← Back to Blog
·9 min read

Crypto Payment API Integration Guide for Developers (2026)

crypto payment APIdeveloper guidewebhookBitcoin integrationpayment gateway

Crypto Payment API Integration Guide for Developers (2026)


Why Integrate Crypto Payments?

BenefitWhat it means for you
No processor riskFunds settle directly to your wallet; you never hold fiat with a third‑party acquirer.
Global reachAnyone with an internet connection and a crypto wallet can pay — no country‑specific banking limits.
Lower feesChainPay charges a flat 0.8 % per transaction versus 2.9 % + typical card processors.
No chargebacksCrypto payments are irreversible once confirmed, eliminating fraud‑related disputes.
Instant settlement (optional)Choose to sweep received funds to your hot or cold wallet immediately after confirmation.

These advantages make crypto a compelling addition — especially for SaaS, digital goods, gaming, and cross‑border marketplaces.


Core Concepts Before You Start### Payment address generation (HD wallets, unique per order)

ChainPay derives a fresh receiving address from a hierarchical deterministic (HD) wallet for every orderId. This guarantees privacy and prevents address reuse attacks. The address is tied to the order until it expires or is marked as confirmed.

Blockchain confirmation (how many confirms = safe for which amounts)

CryptoRecommended confirmsTypical time (mainnet)
Bitcoin (BTC)3–610‑30 min
Ethereum (ETH)12–202‑5 min
USDT‑TRC201< 10 sec
USDC‑ERC20122‑5 min
OthersCheck ChainPay docsVaries

For low‑value orders (< $50) a single confirmation on fast chains (TRC20, SOL) is usually enough. High‑value orders should wait for the chain‑specific threshold above.

Webhooks vs polling (always use webhooks)

Polling introduces latency and unnecessary load. ChainPay pushes a signed JSON payload to your webhookUrl whenever an order’s state changes (pending → confirmed, expired, underpaid, etc.). Webhooks guarantee near‑real‑time notification and let you keep your backend lightweight.

Handling underpaid/overpaid orders

  • Underpaid: You can automatically accept the shortfall (if within a tolerance you define) or reject and notify the user to send the remainder. ChainPay returns amountReceived so you can decide.
  • Overpaid: The excess stays in the receiving address. You may refund it manually (see Step 5) or let the user keep it as a credit — just be clear about your policy in the UI.

Step 1: Get Your API Keys

  1. Sign up at chainpay.pro (free tier available).
  2. In the dashboard, create a new App → give it a name, select the environments (sandbox/production).
  3. Copy the API Key (Bearer token) and the Webhook Secret (used to verify signatures).
  4. Store these securely — never commit them to source control. Use environment variables or a secret manager.

Step 2: Create a Payment Order

The POST /api/v1/orders endpoint returns a unique crypto address, a QR‑code image (base64 PNG), and an expiration timestamp.

// Node.js (≥18) – using native fetch
async function createOrder() {
  const response = 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: 49.99,          // fiat amount you want to receive
      currency: 'USD',        // fiat denomination for pricing
      crypto: 'USDT_TRC20',   // settlement chain/token
      orderId: 'ORDER_123',   // your internal identifier
      webhookUrl: 'https://yourapp.com/webhook/payment',
    }),
  });

  if (!response.ok) {
    const err = await response.json();
    throw new Error(`Order creation failed: ${err.message}`);
  }

  const { paymentAddress, qrCode, expiresAt } = await response.json();
  return { paymentAddress, qrCode, expiresAt };
}
FieldTypeDescription
paymentAddressstringDestination address for the user to send funds
qrCodestring (base64)PNG image ready to embed in an <img src="data:image/png;base64,…">
expiresAtISO‑8601 timestampWhen the address will no longer accept payments

Step 3: Display Payment to User

Show the address, QR code, and a live countdown timer. Convert the fiat amount to crypto using the rate ChainPay provides (you can also fetch it from a market API if you prefer).

<div id="payment-widget">
  <h2>Pay $49.99 with USDT (TRC20)</h2>
  <p>Amount in crypto: <span id="cryptoAmount">…</span> USDT</p>
  <p>Address: <code id="addr">…</code></p>
  <img id="qr" alt="QR code" />
  <p>Expires in: <span id="timer">…</span></p>
</div>

<script>
async function loadPayment() {
  const { paymentAddress, qrCode, expiresAt } = await fetch('/api/create-order')
    .then(r => r.json());

  document.getElementById('addr').textContent = paymentAddress;
  document.getElementById('qr').src = `data:image/png;base64,${qrCode}`;

  // Optional: fetch crypto amount from ChainPay's rate endpoint  const rateResp = await fetch(
    `https://chainpay.pro/api/v1/rate?from=USD&to=USDT_TRC20&amount=49.99`
  );
  const { cryptoAmount } = await rateResp.json();
  document.getElementById('cryptoAmount').textContent = cryptoAmount.toFixed(6);

  // Countdown  const expires = new Date(expiresAt);
  function updateTimer() {
    const now = new Date();
    const diff = expires - now;
    if (diff <= 0) {
      document.getElementById('timer').textContent = 'Expired';
      return;
    }
    const mins = Math.floor(diff / 60000);
    const secs = Math.floor((diff % 60000) / 1000);
    document.getElementById('timer').textContent = `${mins}m ${secs}s`;
    setTimeout(updateTimer, 1000);
  }
  updateTimer();
}
loadPayment();
</script>

Step 4: Handle Webhook Notifications

ChainPay signs each payload with HMAC‑SHA256 using your webhook secret. Verifying the signature prevents spoofed callbacks.

// Express.js example
const crypto = require('crypto');
const express = require('express');
const app = express();
app.use(express.json());

const WEBHOOK_SECRET = process.env.CHAINPAY_WEBHOOK_SECRET;

// Helper: verify signature
function verifySignature(req) {
  const sig = req.headers['x-chainpay-signature'];
  if (!sig) return false;
  const hm = crypto.createHmac('sha256', WEBHOOK_SECRET)
    .update(JSON.stringify(req.body))
    .digest('hex');
  return sig === hm;
}

app.post('/webhook/payment', (req, res) => {
  if (!verifySignature(req)) {
    console.warn('Invalid webhook signature');
    return res.status(401).end();
  }

  const