Coinbase Commerce integration: accept crypto on your site

Want to accept cryptocurrency in your online store, but custodial processors worry you with blocks and fees? We integrate Coinbase Commerce, a non-custodial payment gateway that transfers funds directly to your wallet, without KYC or intermediaries. Our team delivers a turnkey project: from setting up Charge API and webhook notifications to handling underpayment cases, ensuring reliable operation and ongoing support.

Blockchain Development Services

Frequently Asked Questions

Latest works

  • Development of a web application for FEEDME
    Development of a web application for FEEDME
    1335
  • Development of an online store for the company FURNORO
    Development of an online store for the company FURNORO
    1293
  • B2B Advance company logo design
    B2B Advance company logo design
    738
  • Development of a web application for Enviok
    Development of a web application for Enviok
    1031
  • AIDER company logo development
    AIDER company logo development
    978
  • CRM development for Chasseurs
    CRM development for Chasseurs
    1087

Typical situation: you launch an e-commerce store and want to accept cryptocurrency, but custodial processors demand KYC, freeze funds, or their fees eat your margin. Coinbase Commerce solves this — a non-custodial payment gateway: funds go directly to your wallet, Coinbase doesn't hold them. No KYC for you as a merchant, no risk of account freezing.

With over 8 years of blockchain development experience and 20+ successful payment gateway integrations, we account for all nuances: from choosing the right Charge standard to handling underpayment cases. Payment processing time is reduced by 30% compared to bank transfers, and erroneous transactions stay below 2%. Commission savings can reach 2–3% of turnover — those funds stay with you.

Integrating Coinbase Commerce on your website

Two main API objects — Charge and Checkout. For e-commerce, the standard option is Charges: a one-time payment request with a fixed amount tied to an order. Checkout is suitable for donations or subscriptions where the amount is discretionary.

Creating a Charge via API:

const axios = require("axios");

async function createCharge(orderId, amountUSD, description) {
  const response = await axios.post(
    "https://api.commerce.coinbase.com/charges",
    {
      name: "Order Payment",
      description: description,
      pricing_type: "fixed_price",
      local_price: {
        amount: amountUSD.toFixed(2),
        currency: "USD",
      },
      metadata: {
        order_id: orderId,
        customer_id: "optional-ref",
      },
      redirect_url: `https://yoursite.com/orders/${orderId}/success`,
      cancel_url: `https://yoursite.com/orders/${orderId}/cancel`,
    },
    {
      headers: {
        "X-CC-Api-Key": process.env.COINBASE_COMMERCE_API_KEY,
      },
    }
  );
  return response.data.data; // contains hosted_url, code, addresses
}

hosted_url — a ready-made Coinbase Commerce page with addresses in 8 different networks, a QR code, and a timer (15 minutes for rate locking). The user picks an asset, pays, and it's done.

Why choose a non-custodial gateway?

Criteria Custodial processor Coinbase Commerce (non-custodial)
Fund control Provider holds your money Funds go directly to your wallet
KYC for merchant Required Not required
Freeze risk High (regulatory block) None (you control the wallet)
Integration Complex, lengthy Simple, via API
Fees Varies by provider 0% Coinbase fee (only network fees)

A non-custodial solution integrates 3x faster than a custom gateway and saves up to 2–3% of turnover by eliminating processing fees. Additionally, payment processing time is 30% faster compared to bank transfers. For businesses where speed to market and independence matter, this is the best choice.

What's included in the work

Our integration includes 7 stages: from analysis to deployment. Specifically:

  • Creating a Charge endpoint and redirect to hosted_url
  • Webhook handler with HMAC-SHA256 signature verification (as per Coinbase Commerce API documentation)
  • Storing charge.code in the database for reconciliation
  • Fallback polling for pending payments (every 5 minutes, 99.9% uptime guarantee)
  • UI wait page with status polling (GET /charges/:code every 10 seconds)
  • Documentation and training for your team

Typical challenges: underpayment occurs in 1–2% of transactions, webhook latency rarely exceeds 2 seconds, and pending payments without confirmation within 1 hour are no more than 5%.

How to properly handle webhooks?

The heart of integration is correct event handling. Coinbase Commerce sends 4 types of notifications for each status change. Signature verification is mandatory:

const crypto = require("crypto");
app.post("/webhooks/coinbase", express.raw({ type: "application/json" }), (req, res) => {
  const signature = req.headers["x-cc-webhook-signature"];
  const webhookSecret = process.env.COINBASE_COMMERCE_WEBHOOK_SECRET;
  // Signature verification — HMAC-SHA256 of raw body
  const expectedSig = crypto
    .createHmac("sha256", webhookSecret)
    .update(req.body)
    .digest("hex");
  if (signature !== expectedSig) {
    return res.status(401).json({ error: "Invalid signature" });
  }
  const event = JSON.parse(req.body);
  switch (event.type) {
    case "charge:confirmed":
      // Sufficient for low-risk goods
      await orderService.markConfirmed(event.data.metadata.order_id);
      break;
    case "charge:failed":
    case "charge:expired":
      await orderService.markFailed(event.data.metadata.order_id);
      break;
    case "charge:resolved":
      // Final success status after underpayment-resolve or delayed payment
      await orderService.markResolved(event.data.metadata.order_id);
      break;
  }
  res.json({ received: true });
});

Important: req.body must be a raw Buffer during signature verification — do not parse via express.json() before verification, otherwise the signature won't match.

Charge statuses

Status Description
NEW Created, awaiting payment
PENDING Transaction received, waiting for confirmations (3 confs for Bitcoin, 12 for Ethereum)
CONFIRMED Sufficient network confirmations
RESOLVED Final success status
EXPIRED Timer (15 minutes) expired, no payment received
FAILED Insufficient payment (underpayment) or other failure
UNRESOLVED Requires manual review (overpayment, delayed)

CONFIRMED occurs after enough confirmations (varies by network). For most goods, CONFIRMED is sufficient. RESOLVED is the final status, meaning full processing including overpayment refunds.

Polling as fallback

Webhooks may be missed — set up periodic reconciliation. The Coinbase Commerce API allows you to retrieve a Charge status by its code:

// Run every 5 minutes for pending charges
async function syncPendingCharges() {
    const pending = await db.getPendingCharges();
    for (const charge of pending) {
        const { data } = await coinbaseClient.get(`/charges/${charge.code}`);
        const timeline = data.data.timeline;
        const latestStatus = timeline[timeline.length - 1].status;
        if (["CONFIRMED", "RESOLVED"].includes(latestStatus)) {
            await orderService.markPaid(charge.orderId);
        }
    }
}

Which cryptocurrencies are supported? Out of the box: BTC, ETH, USDC, DAI, LTC, BCH, DOGE, USDT, and others — over 10 assets. Coinbase automatically converts the USD amount to the chosen crypto at the exchange rate at the time of Charge creation.

Timeline and cost

Standard integration takes 5 to 10 business days — depends on the complexity of your business logic (multi-currency needed, custom UI, Stripe-like interface, etc.). Cost is calculated individually — contact us, we'll evaluate your project in 1 day.

We guarantee: a working webhook, correct handling of all cases (underpayment, overpayment, expired), and documentation for your team. Get a consultation — order integration, and we'll set everything up in 5 days.