# API compatible con Coinbase Commerce

> Pasa de Coinbase Commerce a blockwallets cambiando la URL base del SDK y las claves: endpoints de charges admitidos, webhooks y firmas, y diferencias.
>
> HTML: https://blockwallets.io/docs/coinbase-commerce?lang=es · English: https://blockwallets.io/docs/coinbase-commerce.md?lang=en

## API compatible con Coinbase Commerce

Los endpoints de charges, la cabecera `X-CC-Api-Key`, el objeto charge y los webhooks firmados con `X-CC-Webhook-Signature` siguen la documentación de Coinbase Commerce, así que los SDK oficiales funcionan tras cambiar una sola línea. Coinbase Commerce dejó de dar servicio a comercios fuera de EE. UU. y Singapur el 2026-03-31. ¿Vienes de otra pasarela? Consulta el [resumen de migración](https://blockwallets.io/docs/migrate?lang=es).

Por debajo es una factura normal de blockwallets: las direcciones de cobro son tuyas, la página de pago es nuestra caja alojada, y un pago solo cuenta cuando está confirmado en la cadena y contrastado con un segundo nodo. A continuación: tres pasos y después qué admite cada API y **en qué se diferencia de la original**: los parámetros que no implementamos devuelven un error en lugar de ignorarse en silencio.

### Tres pasos

- Consigue una cuenta de comercio ([solicítala en la página de inicio](https://blockwallets.io/?lang=es#join)) y añade tus dominios en “Dominios del sitio web”, en “Desarrolladores › Integración y claves” del panel. Las URL de callback (`ipn_callback_url`) y de redirección (`success_url` / `cancel_url` / `redirect_url`) deben estar en esos dominios o en el dominio de tu URL de callback: una clave filtrada no puede apuntar los webhooks a otro sitio.

- Genera credenciales compatibles en el panel del comercio, en “Desarrolladores › Integración y claves”, un juego para live y otro para test: NOWPayments recibe una **API key + IPN secret**, Coinbase Commerce una **API key + webhook shared secret**. Se muestran una sola vez; nosotros solo guardamos hashes / texto cifrado.

- Apunta tu código a la URL base de abajo y usa las nuevas claves. Pruébalo primero en testnets con las credenciales de test y después pasa a live.

| Antes | Después | Cabecera de autenticación | Firma del webhook |
| --- | --- | --- | --- |
| `https://api.commerce.coinbase.com` | `https://blockwallets.io/coinbase/` | `X-CC-Api-Key` | `X-CC-Webhook-Signature` (HMAC-SHA256) |

### Coinbase Commerce

Todos los SDK oficiales aceptan una URL base: cambia una línea (cuidado con la `/` 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 admitidos

| Endpoint | Notas |
| --- | --- |
| `POST /charges` | Crea un cargo (charge); `hosted_url` es nuestra caja alojada; válido durante 1 hora |
| `GET /charges/:code_or_id` | Búsqueda por código de 8 caracteres o por id |
| `GET /charges` | Listado: `limit` (≤100) / `starting_after` / `ending_before` / `order`, con el mismo objeto `pagination` |
| `POST /charges/:code_or_id/cancel` | Cancela un cargo (charge) sin ningún pago visto todavía |

No disponibles: `/charges/:code/resolve` (400: gestiona los cargos pagados de menos en el panel), `/checkouts`, `/events`, `/invoices` (404).

#### Webhooks

Los webhooks de Coinbase son a nivel de cuenta, y los nuestros también: van a la **URL de callback** definida en tu panel (los cargos creados con credenciales de test van a la URL de callback de test). El cuerpo es el de Coinbase, `{id, scheduled_for, attempt_number, event: {id, type, api_version, created_at, data}}`, con el cargo completo en `data`; `X-CC-Webhook-Signature` = HMAC-SHA256(shared secret, cuerpo bruto), así que el `Webhook.verifyEventBody` de los SDK funciona tal cual. En los reintentos `event.id` se mantiene y `attempt_number` sube: deduplica por `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)
```

| Evento | Cuándo | Última entrada de la línea de tiempo |
| --- | --- | --- |
| `charge:confirmed` | Pagado (confirmado en la cadena y contrastado; con pago de más también cuenta) | `COMPLETED` |
| `charge:failed` | Caducado sin pagar / cancelado por ti / caducado pagado de menos | `EXPIRED` / `CANCELED` / `UNRESOLVED` (contexto `UNDERPAID`) |

Al consultar también verás `NEW` (pendiente de pago) y `PENDING` (pago visto en la cadena, confirmando). **Entrega el pedido con `charge:confirmed`**: no enviamos `charge:created`, `charge:pending`, `charge:delayed` ni `charge:resolved`.

#### Diferencias con Coinbase Commerce

- `pricing_type` debe ser `fixed_price`; `local_price.currency` debe ser USD (USDC / USDT a 1:1). `no_price` y otras monedas fiat devuelven 400.

- La dirección aparece cuando el cliente elige moneda y red en la caja: `addresses` contiene solo esa red, `pricing` solo `local` más `settlement` una vez elegida; no hay `exchange_rates`, `checkout` ni campos de marca.

- `metadata` se guarda y se devuelve tal cual (hasta 4 KB).

- Los reembolsos no aparecen en la línea de tiempo; consúltalos en el panel.

### Modo de prueba

Los cargos y pagos creados con credenciales de test funcionan en la testnet de cada cadena y sus webhooks se firman con el IPN secret / shared secret de test, totalmente separados de live: una clave de test no puede ver pagos de live.

Todo lo demás (caja, confirmaciones, redes y mínimos, reembolsos) funciona como en la [documentación principal](https://blockwallets.io/docs/cryptomus?lang=es).
