# API compatible Coinbase Commerce

> Passez de Coinbase Commerce à blockwallets en changeant l’URL de base du SDK et les clés : endpoints charge pris en charge, webhooks et signatures, et différences.
>
> HTML: https://blockwallets.io/docs/coinbase-commerce?lang=fr · English: https://blockwallets.io/docs/coinbase-commerce.md?lang=en

## API compatible Coinbase Commerce

Les endpoints charge, l’en-tête `X-CC-Api-Key`, l’objet charge et les webhooks signés avec `X-CC-Webhook-Signature` suivent la documentation de Coinbase Commerce : les SDK officiels fonctionnent donc après le changement d’une seule ligne. Coinbase Commerce a cessé de servir les marchands hors des États-Unis et de Singapour le 2026-03-31. Vous venez d’ailleurs ? Consultez l’[aperçu de la migration](https://blockwallets.io/docs/migrate?lang=fr).

Dessous, c’est une facture blockwallets ordinaire : les adresses de réception sont les vôtres, la page de paiement est notre caisse hébergée, et un paiement ne compte qu’une fois confirmé on-chain et recoupé avec un second nœud. Ci-dessous : trois étapes, puis ce que chaque API prend en charge et **en quoi elle diffère de l’originale** — les paramètres que nous n’implémentons pas renvoient une erreur au lieu d’être ignorés en silence.

### Trois étapes

- Obtenez un compte marchand ([demandez-le sur la page d’accueil](https://blockwallets.io/?lang=fr#join)) et ajoutez vos domaines dans « Domaines du site web », sous « Développeurs › Intégration et clés » du tableau de bord. Les URL de callback (`ipn_callback_url`) et de redirection (`success_url` / `cancel_url` / `redirect_url`) doivent se trouver sur ces domaines ou sur le domaine de votre URL de callback — une clé divulguée ne peut pas rediriger les webhooks ailleurs.

- Générez des identifiants compatibles dans le tableau de bord marchand, sous « Développeurs › Intégration et clés », un jeu pour live et un pour test : NOWPayments reçoit une **API key + IPN secret**, Coinbase Commerce une **API key + webhook shared secret**. Ils ne sont affichés qu’une fois ; nous ne conservons que des hashs / du texte chiffré.

- Pointez votre code vers l’URL de base ci-dessous et utilisez les nouvelles clés. Lancez d’abord sur les testnets avec les identifiants de test, puis passez en live.

| Avant | Après | En-tête d’authentification | Signature du webhook |
| --- | --- | --- | --- |
| `https://api.commerce.coinbase.com` | `https://blockwallets.io/coinbase/` | `X-CC-Api-Key` | `X-CC-Webhook-Signature` (HMAC-SHA256) |

### Coinbase Commerce

Tous les SDK officiels acceptent une URL de base — changez une ligne (attention au `/` final) :

```sdk
// Node(coinbase-commerce-node)
const { Client } = require("coinbase-commerce-node");
- Client.init(process.env.COINBASE_COMMERCE_API_KEY);
+ Client.init(process.env.COINBASE_COMMERCE_API_KEY, "https://blockwallets.io/coinbase/");

# Python(coinbase-commerce)
- client = Client(api_key=API_KEY)
+ client = Client(api_key=API_KEY, base_api_uri="https://blockwallets.io/coinbase/")

// PHP(coinbase/coinbase-commerce)
- ApiClient::init($apiKey);
+ ApiClient::init($apiKey, 'https://blockwallets.io/coinbase/');
```

```curl
curl -X POST https://blockwallets.io/coinbase/charges \
  -H "X-CC-Api-Key: $COINBASE_COMMERCE_API_KEY" -H "X-CC-Version: 2018-03-22" \
  -H "content-type: application/json" \
  -d '{"name":"Order #12345","description":"Pro plan","pricing_type":"fixed_price",
       "local_price":{"amount":"100.00","currency":"USD"},
       "redirect_url":"https://shop.example/success","cancel_url":"https://shop.example/cancel",
       "metadata":{"order_id":"12345"}}'
# → 201 {"data": {"id": "…", "code": "7C7V5ECK", "hosted_url": "https://blockwallets.io/pay/inv_…", "timeline": [{"status": "NEW", …}], …}}
```

#### Endpoints pris en charge

| Endpoint | Remarques |
| --- | --- |
| `POST /charges` | Crée une charge (charge) ; `hosted_url` est notre caisse hébergée ; valable 1 heure |
| `GET /charges/:code_or_id` | Recherche par code de 8 caractères ou par id |
| `GET /charges` | Liste : `limit` (≤100) / `starting_after` / `ending_before` / `order`, avec le même objet `pagination` |
| `POST /charges/:code_or_id/cancel` | Annule une charge (charge) sans aucun paiement vu pour l’instant |

Non disponibles : `/charges/:code/resolve` (400 — traitez les charges sous-payées dans le tableau de bord), `/checkouts`, `/events`, `/invoices` (404).

#### Webhooks

Les webhooks de Coinbase sont au niveau du compte, et les nôtres aussi : ils vont à l’**URL de callback** définie dans votre tableau de bord (les charges créées avec des identifiants de test vont à l’URL de callback de test). Le corps est celui de Coinbase, `{id, scheduled_for, attempt_number, event: {id, type, api_version, created_at, data}}`, avec la charge complète dans `data` ; `X-CC-Webhook-Signature` = HMAC-SHA256(shared secret, corps brut), de sorte que le `Webhook.verifyEventBody` des SDK fonctionne tel quel. Lors des nouvelles tentatives, `event.id` reste identique et `attempt_number` augmente — dédoublonnez sur `event.id`.

```nodejs
import crypto from "node:crypto";

// Same as the SDK's Webhook.verifySigHeader: HMAC-SHA256(shared secret, raw body)
function verifyWebhook(rawBody, sig, sharedSecret) {
  const hmac = crypto.createHmac("sha256", sharedSecret).update(rawBody, "utf8").digest("hex");
  return crypto.timingSafeEqual(Buffer.from(hmac), Buffer.from(sig || ""));
}
// verifyWebhook(rawBody, req.headers["x-cc-webhook-signature"], process.env.COINBASE_WEBHOOK_SECRET)
// or with the official SDK:Webhook.verifyEventBody(rawBody, signature, sharedSecret)
```

| Événement | Quand | Dernière entrée de la timeline |
| --- | --- | --- |
| `charge:confirmed` | Payé (confirmé on-chain et recoupé ; le trop-perçu compte) | `COMPLETED` |
| `charge:failed` | Expiré sans paiement / annulé par vous / expiré sous-payé | `EXPIRED` / `CANCELED` / `UNRESOLVED` (contexte `UNDERPAID`) |

Lors d’une requête, vous verrez aussi `NEW` (en attente de paiement) et `PENDING` (paiement vu on-chain, en cours de confirmation). **Livrez sur `charge:confirmed`** : nous n’envoyons ni `charge:created`, ni `charge:pending`, ni `charge:delayed`, ni `charge:resolved`.

#### Différences avec Coinbase Commerce

- `pricing_type` doit être `fixed_price` ; `local_price.currency` doit être USD (USDC / USDT à 1:1). `no_price` et les autres monnaies fiat renvoient 400.

- L’adresse apparaît une fois que le client a choisi crypto et réseau à la caisse : `addresses` ne contient que ce réseau, `pricing` seulement `local` plus `settlement` une fois le choix fait ; pas de `exchange_rates`, de `checkout` ni de champs de marque.

- `metadata` est stocké et renvoyé tel quel (jusqu’à 4 Ko).

- Les remboursements n’apparaissent pas dans la timeline ; consultez-les dans le tableau de bord.

### Mode test

Les charges et paiements créés avec des identifiants de test fonctionnent sur le testnet de chaque chaîne et leurs webhooks sont signés avec l’IPN secret / shared secret de test — totalement séparés du live : une clé de test ne voit pas les paiements live.

Tout le reste (caisse, confirmations, réseaux et minimums, remboursements) fonctionne comme dans la [documentation principale](https://blockwallets.io/docs/cryptomus?lang=fr).
