Paso 1 — Configuración inicial
- Solicita tu
X-API-Keya cucu. - Registra tu URL de webhook:
curl -X POST https://crypto.cucu.ai/api/v1/merchants/me/webhook \
-H "X-API-Key: sk_live_xxxx" \
-H "Content-Type: application/json" \
-d '{
"url": "https://tu-servidor.com/webhooks/cucu-crypto",
"events": ["payment.confirmed", "payment.failed", "charge.expired"]
}'
-
Guarda el
secretde la respuesta en tu gestor de secretos. - Envía el evento de prueba:
curl -X POST https://crypto.cucu.ai/api/v1/merchants/me/webhook/test \
-H "X-API-Key: sk_live_xxxx"
Paso 2 — Flujo de pago
Tu Backend cucu Crypto Usuario
│ │ │
│ POST /merchants/me/charges │ │
│─────────────────────────────►│ │
│ │ │
│ { charge_id, checkout_url, │ │
│ qrcode_link, expires_at }│ │
│◄─────────────────────────────│ │
│ │ │
│ Muestra QR / redirect │ │
│──────────────────────────────────────────────────────────►
│ │ │
│ │ Usuario paga en USDT │
│ │◄──────────────────────────│
│ │ │
│ POST webhook │ │
│ payment.confirmed │ │
│◄─────────────────────────────│ │
│ │ │
│ Acredita el pedido │ │
│──────────────────────────────────────────────────────────►
Paso 3 — Implementa el receptor de webhooks
# FastAPI example
from fastapi import FastAPI, Request, Header, HTTPException
import hmac, hashlib, base64
app = FastAPI()
WEBHOOK_SECRET = "wh_sk_xxxxxxxxxxxxxxxxxxxx" # desde variable de entorno
@app.post("/webhooks/cucu-crypto")
async def recibir_webhook(
request: Request,
webhook_id: str = Header(...),
webhook_timestamp: str = Header(...),
webhook_signature: str = Header(...),
webhook_event: str = Header(...),
):
body = await request.body()
# 1. Verificar firma
mensaje = f"{webhook_id}.{webhook_timestamp}.{body.decode()}"
firma = base64.b64encode(
hmac.new(WEBHOOK_SECRET.encode(), mensaje.encode(), hashlib.sha256).digest()
).decode()
if not hmac.compare_digest(firma, webhook_signature.removeprefix("v1,")):
raise HTTPException(status_code=401, detail="Firma inválida")
# 2. Idempotencia por webhook_id
if ya_procesado(webhook_id):
return {"ok": True} # 200 para que CUCU no reintente
# 3. Procesar según el evento
payload = await request.json()
if webhook_event == "payment.confirmed":
acreditar_pedido(payload["metadata"]["invoice_id"])
elif webhook_event == "charge.expired":
liberar_stock(payload["charge_id"])
marcar_procesado(webhook_id)
return {"ok": True}
Consideraciones
- Deduplica por
webhook-id: un evento puede llegar más de una vez. - Responde en menos de 30 s: encola y devuelve
200. - Verifica siempre la firma HMAC.
- POS:
expiration_minutes≤ 15.
Ambientes
| Ambiente | Base URL |
|---|---|
| Producción | https://crypto.cucu.ai/api/v1 |