Documentación
Enlaces de pago
Un enlace de pago es una solicitud de cobro con conceptos, importe y vencimiento. Tu cliente lo abre, ve tu marca y paga en el checkout del proveedor conectado.
Crear un enlace
POST https://api.pagos.express/v1/payment_links
Authorization: Bearer sk_test_...
Idempotency-Key: 6f1d2c1e-0d4a-4b2b-9f0e-7c3b1f2a9d10
Content-Type: application/json
{
"title": "Consultoría septiembre",
"description": "Dos sesiones de estrategia",
"items": [
{ "name": "Sesión de estrategia", "quantity": 2, "unitPriceCentavos": 75000 }
],
"currency": "MXN",
"customerEmail": "cliente@ejemplo.com",
"customerName": "Ana López",
"expiresAt": "2026-10-01T00:00:00Z",
"metadata": { "order_id": "1042", "source": "shopify" }
}| Campo | Tipo | Notas |
|---|---|---|
title | string, 1–200 | Encabezado de la página de cobro. Obligatorio. |
description | string, ≤ 2000 | Texto opcional bajo el título. |
items[] | 1–20 conceptos | Cada uno con name (1–200), quantity (1–999) y unitPriceCentavos (entero ≥ 1). El total del enlace es la suma. Para facturar, cada concepto lleva además satProductCode, satUnitCode y tax (ver Facturación). |
invoiceable | boolean | Permite que tu cliente pida su factura (CFDI) al pagar. Requiere CFDI Express conectado con un emisor en el modo de tu clave; si no, 409 invoicing_unavailable. |
currency | ISO 4217 | Por defecto MXN. Solo monedas de dos decimales que Stripe pueda presentar (USD, EUR…). JPY, KRW y KWD se rechazan. eCart Pay solo se ofrece cuando la moneda coincide con la de su cuenta. |
providers | string[] | Pasarelas que verá el cliente, en orden: stripe, ecartpay o ambas. Vacío ofrece todas las conectadas. Cada una necesita una conexión activa en el modo de tu clave o la creación responde 409 no_active_connection. Con varias, la página deja elegir; eCart Pay pide el correo del cliente. |
customerEmail | Prellena el correo en el checkout. | |
customerName | string, 1–200 | Prellena el nombre. |
expiresAt | ISO 8601 | Tras ese instante el enlace no se puede pagar. Un checkout no puede iniciar a menos de 30 minutos del vencimiento. |
metadata | objeto plano | Tus propios pares clave/valor (número de pedido, ID en tu CRM…). Hasta 50 claves de 1–40 caracteres; valores string de hasta 500 caracteres, números o booleanos. No se aceptan objetos anidados ni arreglos. Se devuelve en el enlace, en sus pagos y en cada webhook; tu cliente no lo ve. |
Idempotency-Key es obligatorio
Facturación (CFDI)
Conecta CFDI Express en Conexiones y elige el emisor (RFC) que factura. Después crea el enlace con invoiceable: true. Cuando el pago es succeeded, la página de confirmación ofrece “Facturar esta compra”: tu cliente escribe sus datos fiscales y CFDI Express timbra la factura con tu emisor. Cada timbre se descuenta de tu saldo en CFDI Express.
| Campo del concepto | Valores | Notas |
|---|---|---|
satProductCode | 8 dígitos | Clave de producto o servicio del SAT (c_ClaveProdServ). |
satUnitCode | E48, H87, ACT… | Clave de unidad del SAT (c_ClaveUnidad). |
tax | iva_included, iva_added, exempt | Precio con 16 % de IVA incluido, IVA sumado encima del precio (aumenta el total) o sin IVA. |
GET /v1/payments/{id}/invoicedevuelve la página de facturación del cliente (invoiceUrl), las formas de pago SAT posibles y la última factura.POSTa la misma ruta factura por tu cliente.- Los eventos
payment.invoiced,payment.invoice_failedypayment.invoice_cancelledavisan cada cambio por webhook. - Un precio con IVA incluido no siempre se divide en base e IVA a dos decimales (por ejemplo $1.05); entonces la factura puede diferir un centavo del cobro por concepto.
Importes y límites
- Todo en unidades menores: MX$1,500.00 =
150000. No envíes totales aparte; manda los conceptos que vas a cobrar. Solo los conceptos contax: "iva_added"suman el 16 % de IVA al total. - Totales que Stripe rechazaría se rechazan al crear, no en el checkout: por debajo del mínimo por moneda (MX$10.00, US$0.50, €0.50…) o por encima de su máximo de ocho dígitos.
- Cobros en otra moneda liquidan en la moneda de la cuenta conectada al tipo de cambio y comisión de Stripe.
Estados del enlace
| status | Significado |
|---|---|
active | Se puede pagar. |
processing | Un cliente tiene un checkout abierto. Espera al resultado. |
paid | Cobrado. paidPaymentId apunta al pago en succeeded. |
expired | Pasó expiresAt sin pago. |
disabled | Lo cerraste tú, una clave o un agente. |
Listar, consultar y desactivar
| Operación | Ruta | Notas |
|---|---|---|
| Listar | GET /v1/payment_links | limit (1–100, 20 por defecto), starting_after (id del mismo modo) y status opcional. |
| Consultar | GET /v1/payment_links/{id} | Estado actual y conceptos. |
| Desactivar | POST /v1/payment_links/{id}/disable | Impide nuevos pagos. No reembolsa. Si hay un checkout abierto y sin pagar, se expira antes de cerrar el enlace; un checkout ya pagado devuelve 409 link_already_paid. |
Conectar o reconectar Stripe o eCart Pay es una acción del propietario en el dashboard. La API solo lee GET /v1/connections para saber si hay una conexión active en el modo de tu clave. Si el comercio aún no tiene cuenta de eCart Pay, puede crearla con nuestro enlace de partner.
Editar un enlace ya compartido
POST /v1/payment_links/{id} cambia title, description, customerEmail, customerName, expiresAt o metadata mientras el enlace esté active. Úsalo, por ejemplo, para agregar los datos bancarios cuando el cliente prefiere transferir. Los conceptos y el importe no cambian: si necesitas cobrar otra cosa, crea otro enlace. Un enlace pagado responde 409 link_already_paid; uno vencido, 410 link_expired.
POST /v1/payment_links/{id}
Authorization: Bearer sk_test_...
Content-Type: application/json
{ "description": "Transferencia SPEI: CLABE 0123 4567 8901 2345 67 · Banco X" }metadata reemplaza el objeto completo: envía todas las claves que quieras conservar. null o {} lo vacía.
Marcar como pagado fuera del checkout
Si el cliente te pagó por transferencia, efectivo u otro medio, registra el cobro con POST /v1/payment_links/{id}/mark_paid. El enlace pasa a paid, se crea un pago con provider: "manual" en succeeded y se disparan los webhooks payment.succeeded y payment_link.paid, igual que con una tarjeta. No se cobra ni se reembolsa nada.
POST /v1/payment_links/{id}/mark_paid
Idempotency-Key: 2b1f…
{
"method": "spei_transfer",
"note": "Depósito recibido en BBVA, referencia 998877",
"paidAt": "2026-09-10T15:30:00Z",
"reference": "data:image/png;base64,iVBORw0KGgo…"
}method:card,oxxo_cash,spei_transferuother(por defecto).referencees opcional: una imagen PNG, JPEG o WebP, o un PDF de hasta 700 KB como data URL. Se descarga después conGET /v1/payments/{id}/reference; el JSON del pago solo indicahasReference.- Funciona con enlaces activos, vencidos y desactivados. Con un checkout en curso responde
409 request_in_flight: espera el resultado de la pasarela o desactiva el enlace primero.