Coinbase Commerce Integration

We design and develop full-cycle blockchain solutions: from smart contract architecture to launching DeFi protocols, NFT marketplaces and crypto exchanges. Security audits, tokenomics, integration with existing infrastructure.
Showing 1 of 1 servicesAll 1306 services
Coinbase Commerce Integration
Simple
~2-3 business days
FAQ
Blockchain Development Services
Blockchain Development Stages
Latest works
  • image_web-applications_feedme_466_0.webp
    Development of a web application for FEEDME
    1161
  • image_ecommerce_furnoro_435_0.webp
    Development of an online store for the company FURNORO
    1051
  • image_logo-advance_0.png
    B2B Advance company logo design
    561
  • image_crm_enviok_479_0.webp
    Development of a web application for Enviok
    827
  • image_logo-aider_0.jpg
    AIDER company logo development
    762
  • image_crm_chasseurs_493_0.webp
    CRM development for Chasseurs
    850

Coinbase Commerce Integration

Coinbase Commerce — non-custodial payment processing: funds go directly to your wallet, bypassing Coinbase. Supports BTC, ETH, USDC, DAI, LTC, BCH and other assets. Key difference from custodial processors: Coinbase doesn't hold your money, no KYC for receiver, no risk of funds freezing from processor.

What We Integrate

Two main API objects:

  • Charge — one-time payment request with fixed amount, tied to specific order
  • Checkout — reusable payment page (suitable for donations where amount is arbitrary)

For e-commerce we use Charges.

Creating 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,
        "X-CC-Version": "2018-03-22",
      },
    }
  );

  return response.data.data; // contains hosted_url, code, addresses
}

hosted_url — ready Coinbase Commerce page, redirect user there. Coinbase shows addresses in different networks for user choice, QR, countdown timer (15 minutes by default for crypto rate).

Webhook Handling

Most important part. Coinbase Commerce sends events on every status change:

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;

  // Verify signature — 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 should be raw Buffer for signature verification — don't parse via express.json() before verification, otherwise signature won't match.

Charge State Machine

NEW → PENDING (first transaction received) → CONFIRMED → RESOLVED
                                           → EXPIRED (timer expired)
                                           → FAILED (underpayment, timeout)
                                           → UNRESOLVED (requires manual review)

CONFIRMED happens after sufficient confirmations (depends on network: Bitcoin — 3, Ethereum — 12). For most goods CONFIRMED is enough. RESOLVED — final status, means complete processing including overpayment refunds.

Polling as Fallback

Webhook may not arrive — set up periodic sync. Coinbase Commerce API lets you get Charge status by 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);
    }
  }
}

What to Implement

  • Charge creation endpoint and redirect to hosted_url
  • Webhook handler with HMAC verification
  • Save charge.code in database for reconciliation
  • Fallback polling for pending charges
  • UI waiting page with polling status (GET /charges/:code every 10 sec)