Obtener Link de Pago
Obtén los detalles de un link de pago específico usando su hashUrl.
Endpoint
GET /v1/payment-links/{hashUrl}Parámetros de ruta
| Parámetro | Tipo | Descripción |
|---|---|---|
hashUrl | string | Hash de 24 caracteres del link: el último segmento de paymentLinkUrl (para https://pay.zelta.dev/Xk3v9Q_aB7mN2pL8rT4wYz1c es Xk3v9Q_aB7mN2pL8rT4wYz1c). El id del link no sirve aquí |
Autenticación
Requiere el header X-API-Key. Consulta la .
Una API key de la cuenta puede leer cualquier link de pago de la cuenta. Una API key de proyecto solo puede leer los links de su proyecto (los creados con la API key de ese proyecto); cualquier otro link responde el mismo 404 con ERR_PAYMENT_LINK_NOT_FOUND que un link que no existe. Consulta el .
Ejemplos de solicitud
curl -X GET "https://api-pay.zelta.dev/v1/payment-links/Xk3v9Q_aB7mN2pL8rT4wYz1c" \
-H "X-API-Key: tu-api-key-aqui" \
-H "Content-Type: application/json"Formato de respuesta
{
"success": true,
"data": {
"paymentLink": {
"id": "7312345678901234567",
"paymentLinkUrl": "https://pay.zelta.dev/Xk3v9Q_aB7mN2pL8rT4wYz1c",
"customerName": "Maria Garcia",
"customerEmail": "[email protected]",
"concept": "Pago de servicio mensual",
"amount": 5000,
"createdAt": "2026-03-13T10:30:00.000Z",
"expiresAt": "2026-03-16T10:30:00.000Z",
"status": "completed",
"completedAt": "2026-03-13T14:02:11.000Z",
"isTest": false,
"redirectUrl": "https://mitienda.com/confirmacion",
"metadata": {
"orderId": "ORD-2026-0042"
},
"paymentMethods": ["card", "paypal"],
"requestCustomerEmail": false
}
},
"message": "Operation completed successfully",
"timestamp": "2026-03-13T15:00:00.000Z"
}Campos de la respuesta
| Campo | Tipo | Descripción |
|---|---|---|
id | string | Identificador único del link de pago (cadena numérica) |
paymentLinkUrl | string | URL de la página de pago |
customerName | string | Nombre del cliente |
customerEmail | string | Email del cliente guardado en el link. Se omite si no existe |
concept | string | Concepto o descripción del cobro |
amount | integer | Monto en centavos |
createdAt | string | Fecha de creación (ISO 8601) |
expiresAt | string | Fecha de vencimiento (ISO 8601) |
status | string | Estado guardado: pending, processing, completed, cancelled o expired (ver ) |
isTest | boolean | Si es un link de prueba |
redirectUrl | string | URL de redirección después del pago. Se omite si no existe |
metadata | object | Datos personalizados asociados al link. Se omite si no existe |
paymentMethods | array | Métodos de pago permitidos al crear el link ("card", "yappy", "paypal"). Se omite si no se especificaron (se ofrecen todos los métodos activos) |
requestCustomerEmail | boolean | Si la página de pago pide el email al cliente |
completedAt | string | Fecha en que se completó el pago (ISO 8601). Se omite mientras no exista |
cancelledAt | string | Fecha de cancelación (ISO 8601). Se omite mientras no exista |
Los campos opcionales se omiten cuando no tienen valor; nunca llegan como null. La respuesta no incluye el método de pago ni el proveedor con que se pagó: esos datos llegan en el webhook .
Estados del link
| Estado | Significado |
|---|---|
pending | Esperando el pago |
processing | Hay un pago en confirmación. Con PayPal puede durar desde minutos hasta varios días mientras PayPal revisa el pago. Mientras tanto el link no se puede pagar de nuevo ni cancelar |
completed | Pago completado |
cancelled | Link cancelado |
expired | Link vencido sin pago y guardado como expirado |
Este endpoint devuelve el estado guardado y nunca lo actualiza. Zelta Pay no marca los links como vencidos en el momento en que vencen ni envía un webhook: un link pending cuyo expiresAt ya pasó se guarda como expired solo cuando un listado (GET /v1/payment-links o el dashboard) lo devuelve en una página, así que hasta entonces este endpoint sigue devolviendo pending. Compara expiresAt con la hora actual para saber si el link todavía se puede pagar.
Un link en processing normalmente termina en completed (recibes el webhook payment.success), o vuelve a pending si el pago se rechaza o se abandona, para que el cliente pueda intentarlo de nuevo.
Códigos de estado
| Código | Descripción |
|---|---|
200 | Link obtenido exitosamente |
401 | Falta el header X-API-Key |
403 | Cuenta suspendida o proyecto inactivo |
404 | API key desconocida (ERR_ACCOUNT_API_KEY_NOT_FOUND), o el link no existe o la API key no lo alcanza (ERR_PAYMENT_LINK_NOT_FOUND): es de otra cuenta o, con una API key de proyecto, no es de ese proyecto |
429 | Rate limit excedido (cuerpo en texto plano; espera Retry-After segundos) |
Respuestas de error
Link no encontrado
{
"success": false,
"message": "Payment link not found",
"error": {
"code": "ERR_PAYMENT_LINK_NOT_FOUND"
},
"timestamp": "2026-03-13T10:30:00.000Z"
}API key faltante
{
"success": false,
"message": "Missing X-API-Key header",
"error": {
"code": "ERR_API_KEY_MISSING"
},
"timestamp": "2026-03-13T10:30:00.000Z"
}API key inválida
{
"success": false,
"message": "Api key not found",
"error": {
"code": "ERR_ACCOUNT_API_KEY_NOT_FOUND"
},
"timestamp": "2026-03-13T10:30:00.000Z"
}Ejemplos de implementación
Obtener un link con manejo de errores
async function getPaymentLink(hashUrl) {
try {
const response = await fetch(
`https://api-pay.zelta.dev/v1/payment-links/${hashUrl}`,
{
headers: {
'X-API-Key': process.env.ZELTA_API_KEY,
'Content-Type': 'application/json'
}
}
);
if (response.status === 429) {
console.error('Rate limit: reintenta en', response.headers.get('Retry-After'), 's');
return null;
}
const result = await response.json();
if (!result.success) {
console.error('Error:', result.error.code, result.message);
return null;
}
return result.data.paymentLink;
} catch (error) {
console.error('Error de red:', error.message);
return null;
}
}
const link = await getPaymentLink('Xk3v9Q_aB7mN2pL8rT4wYz1c');
if (link) {
console.log(`Estado: ${link.status}, Monto: $${(link.amount / 100).toFixed(2)}`);
}Verificar el estado real de un link
Como un link vencido puede seguir mostrando pending, combina status con expiresAt:
async function checkPaymentStatus(hashUrl) {
const link = await getPaymentLink(hashUrl);
if (!link) {
return { found: false };
}
const isPastExpiry = new Date(link.expiresAt) <= new Date();
return {
found: true,
status: link.status,
isPaid: link.status === 'completed',
isConfirming: link.status === 'processing',
isPayable: link.status === 'pending' && !isPastExpiry,
isExpired: link.status === 'expired' || (link.status === 'pending' && isPastExpiry),
isCancelled: link.status === 'cancelled',
amount: link.amount,
customerName: link.customerName
};
}Verificar estado de una orden
async function checkOrderStatus(hashUrl) {
const link = await getPaymentLink(hashUrl);
if (!link) {
return { error: 'Link de pago no encontrado' };
}
const isPastExpiry = new Date(link.expiresAt) <= new Date();
switch (link.status) {
case 'completed':
return {
status: 'paid',
message: `Pago completado por ${link.customerName}`,
orderId: link.metadata?.orderId
};
case 'processing':
return {
status: 'confirming',
message: 'El pago se está confirmando'
};
case 'pending':
if (isPastExpiry) {
return { status: 'expired', message: 'El link de pago venció sin recibir pago' };
}
return {
status: 'awaiting_payment',
message: 'Esperando pago del cliente',
paymentUrl: link.paymentLinkUrl,
expiresAt: link.expiresAt
};
case 'expired':
return {
status: 'expired',
message: 'El link de pago venció sin recibir pago'
};
case 'cancelled':
return {
status: 'cancelled',
message: 'El link de pago fue cancelado'
};
}
}Casos de uso
Página de estado de orden
Usa este endpoint para mostrar el estado actualizado de un pago en tu sitio web:
app.get('/ordenes/:hashUrl/estado', async (req, res) => {
const link = await getPaymentLink(req.params.hashUrl);
if (!link) {
return res.status(404).json({ error: 'Orden no encontrada' });
}
res.json({
orderId: link.metadata?.orderId,
status: link.status,
amount: `$${(link.amount / 100).toFixed(2)}`,
customerName: link.customerName
});
});Verificación de pago
Verifica si un pago se completó antes de entregar un producto o servicio:
async function verifyPayment(hashUrl) {
const link = await getPaymentLink(hashUrl);
if (!link) {
throw new Error('Link de pago no encontrado');
}
if (link.status !== 'completed') {
throw new Error(`Pago no completado. Estado actual: ${link.status}`);
}
return {
verified: true,
amount: link.amount,
customerName: link.customerName,
metadata: link.metadata
};
}Atención al cliente
Consulta los detalles de un link de pago para resolver dudas de tus clientes:
async function getPaymentDetails(hashUrl) {
const link = await getPaymentLink(hashUrl);
if (!link) {
return 'Link de pago no encontrado';
}
return {
cliente: link.customerName,
email: link.customerEmail || 'No proporcionado',
concepto: link.concept,
monto: `$${(link.amount / 100).toFixed(2)}`,
estado: link.status,
creado: link.createdAt,
vence: link.expiresAt,
urlPago: link.paymentLinkUrl,
esPrueba: link.isTest
};
}Siguientes pasos
- — Obtén una lista de todos tus links
- — Crea un nuevo link de pago
- — Cancela un link pendiente
- — Recibe notificaciones automáticas de estado