Saltar al contenido

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" }
}
CampoTipoNotas
titlestring, 1–200Encabezado de la página de cobro. Obligatorio.
descriptionstring, ≤ 2000Texto opcional bajo el título.
items[]1–20 conceptosCada 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).
invoiceablebooleanPermite 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.
currencyISO 4217Por 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.
providersstring[]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.
customerEmailemailPrellena el correo en el checkout.
customerNamestring, 1–200Prellena el nombre.
expiresAtISO 8601Tras ese instante el enlace no se puede pagar. Un checkout no puede iniciar a menos de 30 minutos del vencimiento.
metadataobjeto planoTus 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

Genera un UUID por intención de cobro y reutilízalo si reintentas. La respuesta se conserva 24 horas. Detalles en Idempotencia y reintentos.

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 conceptoValoresNotas
satProductCode8 dígitosClave de producto o servicio del SAT (c_ClaveProdServ).
satUnitCodeE48, H87, ACT…Clave de unidad del SAT (c_ClaveUnidad).
taxiva_included, iva_added, exemptPrecio con 16 % de IVA incluido, IVA sumado encima del precio (aumenta el total) o sin IVA.
  • GET /v1/payments/{id}/invoice devuelve la página de facturación del cliente (invoiceUrl), las formas de pago SAT posibles y la última factura. POST a la misma ruta factura por tu cliente.
  • Los eventos payment.invoiced, payment.invoice_failed y payment.invoice_cancelled avisan 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 con tax: "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

statusSignificado
activeSe puede pagar.
processingUn cliente tiene un checkout abierto. Espera al resultado.
paidCobrado. paidPaymentId apunta al pago en succeeded.
expiredPasó expiresAt sin pago.
disabledLo cerraste tú, una clave o un agente.

Listar, consultar y desactivar

OperaciónRutaNotas
ListarGET /v1/payment_linkslimit (1–100, 20 por defecto), starting_after (id del mismo modo) y status opcional.
ConsultarGET /v1/payment_links/{id}Estado actual y conceptos.
DesactivarPOST /v1/payment_links/{id}/disableImpide 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_transfer u other (por defecto).
  • reference es opcional: una imagen PNG, JPEG o WebP, o un PDF de hasta 700 KB como data URL. Se descarga después con GET /v1/payments/{id}/reference; el JSON del pago solo indica hasReference.
  • 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.