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.
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) 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):
// 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 -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.
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_typedebe serfixed_price;local_price.currencydebe ser USD (USDC / USDT a 1:1).no_pricey otras monedas fiat devuelven 400.- La dirección aparece cuando el cliente elige moneda y red en la caja:
addressescontiene solo esa red,pricingsololocalmássettlementuna vez elegida; no hayexchange_rates,checkoutni campos de marca. metadatase 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.