Ir al contenido

Para desarrolladoresGuía

Recibir pagos con criptomonedas en Python

Recibe Bitcoin y USDT en una app Python: firma solicitudes a la API con hmac, crea un pago con requests, redirige a la página de pago y verifica el webhook en Flask.

Una aplicación Python recibe cripto con CryptoLike en tres pasos: firma cada solicitud a la API con los módulos estándar hmac y hashlib, crea un pago para el pedido y verifica el webhook firmado antes de marcar el pedido como pagado. Los ejemplos usan requests y Flask; la misma lógica sirve para Django, FastAPI y otros frameworks.

Una tienda en WooCommerce, OpenCart, PrestaShop o Magento 2 no necesita código: revisa las integraciones. Esta guía es para tu propio backend.

Qué necesitas

Una cuenta de CryptoLike, Python 3.10 o superior y una URL pública https:// para los webhooks. Guarda los secretos en el entorno: los ejemplos leen CRYPTOLIKE_API_KEY_ID, CRYPTOLIKE_API_SECRET y CRYPTOLIKE_WEBHOOK_SECRET, además de CRYPTOLIKE_API_URL para la URL de la API de prueba (si no está definida, se usa producción). Empieza con claves de prueba y redes de prueba.

1. Crea una clave API de prueba

En el panel abre Desarrolladores → Claves API → Crear clave con los permisos create_invoice y read. Una clave ck_test_ funciona con redes de prueba, donde las monedas no tienen valor. Las claves de prueba se crean en el entorno de prueba: el soporte de CryptoLike te da acceso a su panel, donde creas la misma clave, y la URL de su API (CRYPTOLIKE_API_URL en los ejemplos). El panel de producción emite solo claves reales, y en la red principal una clave de prueba se rechaza con 401 auth.test_key. El secreto se muestra una sola vez; ponlo de inmediato en tu entorno o en tu gestor de secretos.

2. Firma las solicitudes a la API con hmac

La firma es un HMAC-SHA256 de cuatro líneas —la marca de tiempo Unix en segundos, el método, la ruta con su query y el SHA-256 del cuerpo— con el secreto de la API como clave:

python
import hashlib, hmac, json, os, time
import requests

def cryptolike(method: str, path: str, payload: dict | None = None, idempotency_key: str | None = None) -> dict:
    body = b"" if payload is None else json.dumps(payload, separators=(",", ":")).encode()
    ts = str(int(time.time()))
    canonical = "\n".join([ts, method.upper(), path, hashlib.sha256(body).hexdigest()])
    headers = {
        "X-API-Key": os.environ["CRYPTOLIKE_API_KEY_ID"],
        "X-Timestamp": ts,
        "X-Signature": hmac.new(os.environ["CRYPTOLIKE_API_SECRET"].encode(), canonical.encode(), hashlib.sha256).hexdigest(),
        "Content-Type": "application/json",
    }
    if idempotency_key:
        headers["Idempotency-Key"] = idempotency_key
    r = requests.request(method, os.environ.get("CRYPTOLIKE_API_URL", "https://api.cryptolike.net") + path, data=body or None, headers=headers, timeout=30)
    data = r.json()
    if r.status_code >= 400:
        raise RuntimeError(f"{data.get('error', {}).get('code', r.status_code)}: {data.get('error', {}).get('message', '')}")
    return data

Envía los mismos bytes de los que calculaste el hash: pasa data=body, no json=payload, que volvería a serializar el cuerpo. Compara la función con el vector de referencia de autenticación de la API.

3. Crea el pago de un pedido

Crea el pago en el servidor cuando se hace el pedido. Ponle precio en USD o EUR y deja que el cliente elija la moneda cripto, o pasa un coin_id de GET /v1/currencies. Sin coin_id, la página muestra las monedas disponibles para facturas, cada una con un monto aproximado marcado con ≈; agrega allowed_coin_ids (id con invoice_enabled: true en GET /v1/currencies firmado) para ofrecer solo algunas. Con el ID del pedido como clave de idempotencia, un reintento devuelve el mismo pago:

python
invoice = cryptolike("POST", "/v1/invoices", {
    "amount": "49.90",
    "currency": "USD",
    "order_id": order.id,
    "idempotency_by_order_id": True,
    "return_url": f"https://example.com/orders/{order.id}",
}, idempotency_key=f"order-{order.id}")

order.invoice_id = invoice["id"]

Mantén los montos como cadenas o Decimal, nunca float.

4. Envía al cliente a la página de pago

Redirige a invoice["checkout_url"] (redirect(invoice["checkout_url"], code=303) en Flask). La página muestra el monto, la red, la dirección con código QR, el tiempo restante y el estado en vivo, y luego devuelve al cliente a return_url. Para que el cliente se quede en tu sitio, incrusta la página con el widget de pago.

5. Verifica el webhook en Flask

Agrega un endpoint de webhook en Desarrolladores → Webhooks con los eventos invoice.*. Verifica sobre el cuerpo sin procesar de request.get_data(), descarta duplicados por event_id y responde rápido:

python
from flask import Flask, request

app = Flask(__name__)

@app.post("/webhooks/cryptolike")
def cryptolike_webhook():
    raw = request.get_data()
    ts = request.headers.get("X-Timestamp", "")
    got = request.headers.get("X-Signature", "")
    secret = os.environ.get("CRYPTOLIKE_WEBHOOK_SECRET", "")
    want = "sha256=" + hmac.new(secret.encode(), ts.encode() + b"." + raw, hashlib.sha256).hexdigest()
    if not secret or not ts.isdigit() or abs(time.time() - int(ts)) > 300 or not hmac.compare_digest(got, want):
        return "", 401
    event = json.loads(raw)
    if first_time(event["event_id"]) and event["type"] == "invoice.paid":
        mark_order_paid(event["data"]["invoice"]["order_id"])
    return "", 200

Cada evento se entrega al menos una vez y puede llegar en desorden: guíate por el estado dentro de data. El procedimiento completo está en cómo verificar los webhooks de pagos cripto.

6. Prueba en una red de prueba y pasa a producción

Crea un pago con la clave de prueba y págalo con monedas de prueba en la red que indica la página: primero llega invoice.pending y después invoice.paid. Maneja también invoice.underpaid, invoice.expired e invoice.reverted; luego cambia a una clave real y sigue la lista de verificación para producción de la guía para desarrolladores.

Errores comunes y cómo corregirlos

Error Qué pasó Qué hacer
401 auth.invalid_signature El cuerpo de la solicitud no coincide con lo que se firmó Envía los mismos bytes que firmaste, con data= y no con json=
401 auth.expired_timestamp El reloj tiene un desfase de más de 5 minutos Sincroniza el reloj del servidor con NTP
401 en tu webhook El cuerpo se parseó o decodificó antes de la verificación Verifica sobre request.get_data()
400 validation_error Falta un campo o tiene un formato incorrecto Lee details.field; envía los montos como cadenas
409 conflict El pedido ya tiene un pago Usa details.invoice_id en lugar de crear otro
422 no_allowed_coins Ninguna moneda de allowed_coin_ids está disponible para facturas en esta divisa Envía otros id o quita el campo para ofrecer todas las monedas

¿Prefieres un SDK?

El SDK oficial de Python para Python 3.10+ envuelve estas llamadas: solicitudes firmadas, reintentos ante 429 y 5xx, un Idempotency-Key en cada POST, paginación y verificación de webhooks. El código de arriba es lo que el SDK hace por dentro, así que puedes cambiar más adelante sin modificar el flujo.

Preguntas frecuentes

¿Por qué falla la firma cuando uso el argumento json= de requests?

json=payload vuelve a serializar el cuerpo con sus propios espacios, así que los bytes enviados son distintos de los bytes de los que calculaste el hash. Serializa una sola vez, calcula el hash de esos bytes y pasa los mismos bytes con data=. Lo mismo aplica del lado del webhook: verifica sobre request.get_data() antes de parsear nada.

¿Puedo usar Django o FastAPI en lugar de Flask?

Sí. La lógica es la misma en todos los frameworks: lee el cuerpo sin procesar, calcula sha256= más el HMAC de la marca de tiempo, un punto y el cuerpo, compara con hmac.compare_digest, rechaza las marcas de tiempo que se alejen más de 300 segundos de tu reloj y después parsea. En Django el cuerpo sin procesar es request.body; en FastAPI, await request.body().

¿Cómo pruebo sin gastar cripto real?

Usa una clave ck_test_ con la URL de la API de prueba que te da el soporte de CryptoLike. Las claves de prueba funcionan con redes de prueba, donde las monedas de prueba no tienen valor, y todo el flujo —página de pago, confirmaciones, webhooks firmados— se comporta igual que en producción. Guarda las claves de prueba y las reales en configuraciones separadas para que nunca se mezclen.

Términos usados aquí

Sigue leyendo

Empieza con un pago de prueba

Crea una cuenta, pide a soporte el entorno de prueba y mira cómo un pago pasa de pending a paid en una red de prueba.