Integraciones

Pagos cripto para backends propios

Un recorrido, independiente del stack, para integrar Qvard en tu propio servidor: el modelo de pedido, crear el pago, el webhook, la conciliación y una lista de comprobación para salir en vivo, con ejemplos en Python.

Si tu tienda, servicio o plataforma funciona sobre tu propio backend, no necesitas un plugin: Qvard necesita una llamada que cree un pago y un endpoint que reciba el resultado. Esta guía recorre la integración de un modo que encaja con cualquier lenguaje y framework, con ejemplos en Python, y cubre lo que la hace fiable: el modelo de pedido, la idempotencia, la conciliación y una breve lista de comprobación para salir en vivo.

Qué necesitas

  • Una cuenta de Qvard con un proyecto, una clave API y la callback URL de tu webhook guardada en el proyecto.
  • Un endpoint HTTPS público en tu servidor para el webhook.
  • Una tabla de pedidos (o su equivalente) en la que un pago pueda asociarse a un pedido mediante tu propio identificador.

Todavía no hay SDK ni sandbox: la integración es HTTP simple y las pruebas se hacen con importes reales pequeños. La referencia de los formatos de solicitud es API de pagos cripto para desarrolladores; esta guía muestra cómo encajan.

Un modelo de pedido mínimo

Tus propios datos deciden cuán limpia es la integración. Un conjunto mínimo de campos de un pedido:

  • id: tu número de pedido o factura. Es el id que envías a Qvard y la clave que vuelve en el webhook.
  • amount_usd: el importe en dólares estadounidenses, la unidad en que se crea el pago.
  • state: por ejemplo awaiting_payment, paid, cancelled. Marca como pagado solo desde el webhook.
  • paid_at y, si quieres, el id del pago de Qvard de la respuesta para soporte.

Evita meter toda la lógica de pago en la clase del pedido. Un módulo pequeño que cree el pago y un endpoint que aplique el webhook mantienen el código comprobable.

Crear el pago

Cuando el cliente elige pagar, crea el pago desde tu backend con un único POST a https://api.qvard.com/api/v1/payment. Envía la clave en la cabecera Authorization: Bearer y un JSON con tu número de pedido como id (una cadena), el importe como price en dólares estadounidenses y, opcionalmente, un paymentSystemId para abrir la página de pago con un activo y una red seleccionados.

La respuesta es 201 con redirectUrl, la dirección de la página de pago alojada. Redirige al cliente allí, o pon el enlace en un correo. Reglas que conviene codificar en el cliente:

  • Tiempo de espera y reintento. Usa un tiempo de espera y reintenta ante un tiempo agotado o un 5xx. Enviar el mismo id para el mismo método de pago devuelve el pago existente, así que un reintento no puede crear un segundo.
  • No reintentes un 400 («Error validation», con un mensaje por campo) ni un 401 (clave incorrecta) sin cambiar algo.
  • Importes en dólares. Si tu tienda fija precios en otra moneda, convierte antes de la llamada.
Pythonqvard_client.py
import os
import requests

QVARD_URL = "https://api.qvard.com/api/v1/payment"

def create_payment(order_id: str, amount_usd: float) -> str:
    """Create a Qvard payment for an order and return the hosted payment page URL."""
    response = requests.post(
        QVARD_URL,
        headers={"Authorization": "Bearer " + os.environ["QVARD_API_KEY"]},
        json={"id": order_id, "price": amount_usd},   # id as a string, price in US dollars
        timeout=15,
    )
    if response.status_code == 201:
        return response.json()["redirectUrl"]
    # 400 = "Error validation" with a per-field message, 401 = wrong API key
    raise RuntimeError(f"Qvard {response.status_code}: {response.text}")

Recibir el webhook

Cuando un pago se completa, Qvard envía un POST a tu callback URL con el id y el status, firmado en la cabecera X-Qvard-Signature. Tu endpoint debe:

  1. Verificar la firma: HMAC-SHA256 con tu clave secreta sobre la marca de tiempo y el cuerpo sin procesar, comparada en tiempo constante; si no coincide, responder 400.
  2. Actuar solo con el estado completed.
  3. Actualizar el pedido de forma condicional, «pagado si aún espera pago», de modo que las repeticiones no hagan nada.
  4. Encolar el trabajo lento y responder exactamente HTTP 200.

Qvard reintenta cualquier otra respuesta, un tiempo agotado o una redirección, cinco intentos en unos seis minutos, así que las entregas repetidas son normales. Los detalles de los reintentos están en estrategia de reintentos de webhooks en sistemas de pago, y las reglas de seguridad en cómo proteger los webhooks de pagos cripto.

Pythonwebhook.py
import os
from fastapi import FastAPI, Request, Response
from qvard import Qvard

app = FastAPI()
qvard = Qvard(os.environ["QVARD_API_KEY"])

@app.post("/webhooks/qvard")
async def qvard_webhook(request: Request):
    try:
        body = qvard.webhooks.construct_event(await request.body(), request.headers.get("X-Qvard-Signature"), os.environ["QVARD_API_KEY"])
    except ValueError:
        return Response(status_code=401)

    if body.get("status") == "completed":
        # Only changes an order that is not paid yet, so repeated deliveries are harmless.
        changed = await db.execute(
            "UPDATE orders SET state = 'paid', paid_at = now() WHERE id = :id AND state = 'awaiting_payment'",
            {"id": body.get("id")},
        )
        if changed:
            await queue.enqueue("fulfil_order", body["id"])

    return Response(status_code=200)

Pagos parciales, fallos y la vista del cliente

Decide qué significa cada estado del pago para un pedido:

  • wait: a la espera del pago.
  • partial: llegó menos del importe completo; no se envía el webhook de finalización. Mantén el pedido abierto, indica al cliente cuánto falta, o cancela y reembolsa.
  • completed: llegó el importe completo; marca el pedido como pagado.
  • rejected: ofrece otra forma de pago.

Todavía no hay URL de retorno tras el pago, así que el cliente vuelve por su cuenta: dale una página de pedido que muestre el estado real y se actualice mientras espera. Un reembolso es una transferencia que envías tú, porque un pago cripto no se puede revertir.

Conciliación y lista de comprobación para salir en vivo

Añade una comprobación diaria que compare los pedidos que consideras pagados o no con los pagos y el saldo de tu panel de Qvard; un pago que aparece completado mientras tu pedido está sin pagar es una notificación perdida, que puedes reenviar desde el panel.

Antes de salir en vivo:

  • la clave API está en una variable de entorno o un gestor de secretos, nunca en el repositorio;
  • la callback URL está guardada, es pública, usa HTTPS y es accesible desde fuera de tu red;
  • el endpoint responde exactamente 200, rápido, y es seguro si se ejecuta dos veces;
  • pagaste un pequeño importe real y viste que el pedido pasó a pagado;
  • hiciste fallar el endpoint una vez y viste a Qvard reintentar sin un doble cumplimiento;
  • retiraste un pequeño importe a tu billetera. El retiro cuesta solo la comisión de red; el retiro a una cuenta bancaria en moneda fiat no está disponible.

Las condiciones comerciales están en la página de precios, y una factura de demostración muestra lo que ve tu cliente.

FAQ

¿Hay un SDK para mi lenguaje?

No. La integración es HTTP simple: un POST para crear un pago y un webhook para recibir el resultado. Los ejemplos de esta guía muestran el patrón en Python, y se traslada a cualquier lenguaje.

¿Cómo asocio un pago a un pedido?

Envía tu propio número de pedido como id al crear el pago. El mismo id vuelve en el webhook.

¿Hay un sandbox para probar?

Todavía no. Usa importes reales pequeños y una factura de demostración para ver la página de pago.

¿Y si mi servidor está caído cuando se completa un pago?

Qvard reintenta cinco veces durante unos seis minutos. Después, reenvía la notificación del pago desde el panel; un manejador idempotente lo hace seguro.

Artículos relacionados

Artículos relacionados