Links de Colección (API)
Los links de colección son páginas alojadas por Zelta Pay que capturan la tarjeta del cliente, la tokenizan con el procesador activo de la cuenta (NMI o Emetec) y crean un cliente vaulteado. Son el punto de entrada del flujo de integración: una vez capturada la tarjeta, tu software mueve el dinero por API.
Existen dos variantes:
- Link de colección (
POST /v1/collections/links): solo captura y vaultea la tarjeta y crea el cliente. No cobra nada. - Link de pago de cliente (
POST /v1/collections/payment-links): cobra unamounty vaultea la tarjeta en el mismo checkout; requiere unproductCode.
Ambas variantes son solo tarjeta — Yappy no puede almacenar una credencial. Todos los endpoints requieren tu API key de proyecto en el header X-API-Key.
Crear un link de colección
Crea una página alojada que únicamente recolecta la tarjeta: se tokeniza con el procesador activo de tu cuenta (NMI o Emetec) y se crea un cliente. Sin producto, sin monto, sin cobro.
POST /v1/collections/linksCuerpo de la solicitud:
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
customer.name | string | Sí | Nombre del cliente (1-120 caracteres) |
customer.email | string | No | Correo del cliente |
isTest | boolean | No | Entorno sandbox (por defecto false) |
redirectUrl | string | No | URL a la que redirigir al cliente tras una captura exitosa |
metadata | object | No | Se devuelve en el webhook customer.created |
curl -X POST https://api-pay.zelta.dev/v1/collections/links \
-H "X-API-Key: tu-api-key-de-proyecto" \
-H "Content-Type: application/json" \
-d '{
"customer": { "name": "John Doe", "email": "john@example.com" },
"redirectUrl": "https://tu-app.com/billing/payment-method-saved",
"metadata": { "userId": "usr_123" }
}'Respuesta (201):
{
"success": true,
"data": {
"id": "1234567890",
"paymentLinkUrl": "https://pay.zelta.dev/AbCdEf123",
"hashUrl": "AbCdEf123",
"customerName": "John Doe",
"customerEmail": "john@example.com",
"status": "pending",
"isTest": false,
"expiresAt": "2026-06-11T00:00:00.000Z",
"createdAt": "2026-06-04T00:00:00.000Z",
"redirectUrl": "https://tu-app.com/billing/payment-method-saved"
},
"message": "Customer collection link created"
}Envía al cliente a paymentLinkUrl. Cuando guarde su tarjeta recibirás el webhook en el webhook del proyecto — guarda customer.id y los metadatos de la tarjeta, y luego mueve las transacciones por API.
Crear un link de pago de cliente
Igual que un link de colección, pero la página alojada cobra amount en el mismo checkout en que almacena la tarjeta y crea el cliente. Úsalo para un primer pago que además inscribe al cliente para cobros futuros (p. ej. una compra inicial + una tarjeta guardada para una suscripción). Solo tarjeta — Yappy no puede almacenar una credencial.
POST /v1/collections/payment-linksCuerpo de la solicitud:
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
customer.name | string | Sí | Nombre del cliente (1-120 caracteres) |
customer.email | string | No | Correo del cliente |
concept | string | Sí | Qué se está pagando (se muestra en el checkout) |
amount | integer | Sí | Monto en centavos (100–1,000,000, es decir $1–$10,000) |
productCode | string | Sí | Código de un producto one_time del proyecto. El pago liquidado se registra como un cargo de este producto contra el nuevo cliente y se emite un webhook charge.succeeded (después de customer.created) |
isTest | boolean | No | Entorno sandbox (por defecto false) |
redirectUrl | string | No | URL a la que redirigir al cliente tras un pago exitoso |
metadata | object | No | Se devuelve en los webhooks payment.success y customer.created |
curl -X POST https://api-pay.zelta.dev/v1/collections/payment-links \
-H "X-API-Key: tu-api-key-de-proyecto" \
-H "Content-Type: application/json" \
-d '{
"customer": { "name": "John Doe", "email": "john@example.com" },
"concept": "Plan Pro — primer mes",
"amount": 2999,
"productCode": "activacion",
"redirectUrl": "https://tu-app.com/billing/welcome",
"metadata": { "userId": "usr_123" }
}'Respuesta (201):
{
"success": true,
"data": {
"id": "1234567890",
"paymentLinkUrl": "https://pay.zelta.dev/AbCdEf123",
"hashUrl": "AbCdEf123",
"customerName": "John Doe",
"customerEmail": "john@example.com",
"concept": "Plan Pro — primer mes",
"amount": 2999,
"status": "pending",
"isTest": false,
"expiresAt": "2026-06-11T00:00:00.000Z",
"createdAt": "2026-06-04T00:00:00.000Z",
"redirectUrl": "https://tu-app.com/billing/welcome"
},
"message": "Customer payment link created"
}Envía al cliente a paymentLinkUrl. En un checkout exitoso recibirás ambos webhooks: (por el cargo) y (con customer.id y los metadatos de la tarjeta almacenada) — guarda el cliente y luego mueve más cargos/suscripciones por API. Si el pago es rechazado no se crea ningún cliente.
El pago liquidado se registra como un cargo de productCode contra el nuevo cliente (aparece en GET /v1/charges y en la vista de cargos del proyecto) y se emite además un webhook charge.succeeded después de customer.created, con la misma forma que un cargo movido por API — de modo que el primer pago se reconcilia por el mismo camino que tus llamadas posteriores a POST /v1/charges.
Abrir el link en una ventana flotante (estilo PayPal)
Un link alojado es una URL normal, así que una página no puede forzarse a abrirse como popup — la ventana la abre tu código. Para mantener al cliente en tu sitio (estilo PayPal), abre paymentLinkUrl con window.open en lugar de navegar fuera. La página alojada detecta que fue abierta de esta forma (window.opener) y, al tener éxito, publica un mensaje a tu página y se cierra sola.
function openZeltaCheckout(paymentLinkUrl, onDone) {
const w = 480;
const h = 720;
const left = window.screenX + (window.outerWidth - w) / 2;
const top = window.screenY + (window.outerHeight - h) / 2;
const popup = window.open(
paymentLinkUrl,
"zeltapay-checkout",
`width=${w},height=${h},left=${left},top=${top},resizable,scrollbars`,
);
function handleMessage(event) {
// Solo confía en mensajes de la página alojada de ZeltaPay.
if (event.origin !== "https://pay.zelta.dev") return;
const { type } = event.data || {};
if (type === "zeltapay:collected" || type === "zeltapay:paid") {
window.removeEventListener("message", handleMessage);
popup?.close();
onDone(event.data);
}
}
window.addEventListener("message", handleMessage);
}El mensaje publicado es solo para mostrar (nunca contiene ningún secreto):
{
"type": "zeltapay:collected",
"status": "collected",
"orderId": null,
"customerEmail": "john@example.com",
"card": { "brand": "visa", "last4": "4242" }
}type es zeltapay:collected para un link de colección y zeltapay:paid para un link de pago de cliente. Si el popup se bloquea o se abre como pestaña normal, la página recurre a redirigir a redirectUrl.
El webhook es la fuente de verdad
El mensaje de postMessage es solo una señal de UI para cerrar la ventana y refrescar. El webhook sigue siendo la fuente de verdad: confirma el resultado desde los webhooks customer.created / payment.success, no desde el mensaje de la ventana.
Siguientes pasos
- — consulta el cliente creado tras la captura
- — cobra al cliente vaulteado
- — suscribe al cliente a un plan recurrente
- —
customer.createdypayment.success