Coinbase

Coinbase Business Checkouts, crypto payments on Coinbase's hosted checkout page; balance is credited by the signed webhook.

What you need

This integration uses the Coinbase Business Checkouts API (the old Coinbase Commerce "charge" API has been shut down; its X-CC-Api-Key keys do not work here). You need a Coinbase Business account; payments settle into it as USDC.

1. API key: Coinbase Developer Platform → API Keys → Secret API KeysCreate API key. The View permission is enough (the Checkouts API is in that scope). Either signature algorithm works, Ed25519 (the default) or ECDSA.

  • api_key: the API key ID (a UUID for Ed25519 keys; organizations/…/apiKeys/… for ECDSA keys)
  • secret_key: the key's secret (one-line base64 for Ed25519; for ECDSA the PEM starting with -----BEGIN EC PRIVATE KEY-----, you may paste the line breaks as \n)

2. Webhook subscription: Coinbase sends checkout status changes only to a webhook subscription, and that subscription must be created on your side. The way Coinbase documents is the CDP CLI:

cdp data webhooks subscriptions create \
  description="Panel checkout webhook" \
  'eventTypes:=["checkout.payment.success","checkout.payment.failed","checkout.payment.expired","checkout.refund.success","checkout.refund.failed"]' \
  target.url=https://<your-panel-domain>/payments/coinbase/callback \
  target.method=POST \
  isEnabled:=true

The metadata.secret value in the response is:

  • webhook_secret: the webhook signing secret

In your panel choose Settings → Payments → Add method → Coinbase and enter the three values.

There is no sandbox: Coinbase documents no separate test host for Business Checkouts; requests always go to business.coinbase.com and the "test mode" box has no effect. Try it with a small real payment.

Flow

  1. The customer enters an amount and picks Coinbase. The panel creates a single-use checkout in your panel's currency; Coinbase converts a fiat amount to USDC at the rate of that moment (valid for 24 hours).
  2. The customer is sent to Coinbase's hosted payment page and pays from a wallet or a Coinbase account.
  3. Afterwards the customer is redirected back to the panel. That return credits nothing: per Coinbase's docs the checkout may still be ACTIVE at that point.
  4. Once Coinbase sees the payment on-chain it sends the checkout.payment.success webhook signed with X-Hook0-Signature. The panel verifies the signature with webhook_secret, credits the balance if the payment reference in metadata.publicRef is ours, and answers Coinbase with OK.
  5. checkout.payment.failed marks the payment failed, checkout.payment.expired marks it expired.

A repeated delivery of the same event for the same checkout is ignored; balance is never credited twice.

Statuses

Coinbase event Panel
checkout.payment.success completed, balance credited
checkout.payment.failed failed
checkout.payment.expired expired
checkout.refund.success / checkout.refund.failed record unchanged; refunds are managed in Coinbase Business, adjust the balance by hand under Admin → Payments

The Checkouts API has no "underpaid" state: Coinbase either completes the payment or marks it FAILED. The credited amount is the fiat amount we created the checkout with (the USDC conversion and Coinbase's fee show up in your Coinbase account and do not affect the customer's balance).

Currencies

The checkout is opened in your panel's currency. Coinbase's docs name USDC plus USD, EUR, GBP, SGD and "other fiat currencies" without publishing the full list. A checkout cannot be opened in a currency Coinbase does not accept; Coinbase's error message is shown when the payment is started. The amount must be between 0.01 and 100,000,000 USD with at most 2 decimals.

Common problems

  • "The request is not properly authenticated (unauthorized)": api_key/secret_key do not match, or the key belongs to a different CDP project. Paste the secret in full (for a PEM including the BEGIN/END lines); if you set an IP allowlist, your server's IP must be on it.
  • Paid but no balance: no webhook subscription, the wrong target URL, or a different webhook_secret. Inspect deliveries with cdp data webhooks subscriptions events <ID>; when the signature does not match the panel answers 400 and Coinbase retries.
  • Customer is back but the balance takes a few minutes: normal; the return page credits nothing, the webhook arrives once Coinbase confirms the payment on-chain and the record completes then.
  • Coinbase rejects the currency (invalid_request): your panel currency is not among those Coinbase accepts; use the Coinbase method only in a supported currency.