Skip to content

Inicio Rápido

Bienvenido a la API de Zelta Pay. Esta guía te ayudará a crear tu primer link de pago y entender los fundamentos de nuestra API.

¿Prefieres usar el dashboard?

Si prefieres crear links de pago sin código, consulta la guía del y .

Requisitos previos

Antes de comenzar, asegúrate de tener:

  • Una cuenta de Zelta Pay
  • Una API key (obtenida desde tu dashboard)
  • Conocimiento básico de solicitudes HTTP
  • Un entorno de desarrollo listo

Paso 1: Obtén tu API Key

  1. Inicia sesión en tu
  2. Navega a API Keys
  3. Ingresa un alias descriptivo y haz clic en + Crear
  4. Dale un alias descriptivo (ej. mi-app-produccion)
  5. Copia la API key generada y guárdala de forma segura

Buena práctica de seguridad

Nunca guardes API keys en el control de versiones. Usa variables de entorno o gestión segura de secretos.

Paso 2: Haz tu primera llamada a la API

Probemos tu API key listando tus links de pago existentes:

:: tab cURL

bash
curl -X GET "https://api-pay.zelta.dev/v1/payment-links" \
  -H "X-API-Key: tu-api-key-aqui" \
  -H "Content-Type: application/json"

::

:: tab Node.js

javascript
const response = await fetch('https://api-pay.zelta.dev/v1/payment-links', {
  method: 'GET',
  headers: {
    'X-API-Key': 'tu-api-key-aqui',
    'Content-Type': 'application/json'
  }
});

const result = await response.json();
console.log(result);

::

:: tab Cloudflare Workers

javascript
export default {
  async fetch(request, env, ctx) {
    const response = await fetch('https://api-pay.zelta.dev/v1/payment-links', {
      method: 'GET',
      headers: {
        'X-API-Key': env.ZELTA_API_KEY,
        'Content-Type': 'application/json'
      }
    });

    const result = await response.json();
    return new Response(JSON.stringify(result), {
      headers: { 'Content-Type': 'application/json' }
    });
  }
};

::

:: tab Hono

typescript
import { Hono } from 'hono';
import { cors } from 'hono/cors';

type Bindings = {
  ZELTA_API_KEY: string;
};

const app = new Hono<{ Bindings: Bindings }>();

app.use('*', cors());

app.get('/payment-links', async (c) => {
  const response = await fetch('https://api-pay.zelta.dev/v1/payment-links', {
    method: 'GET',
    headers: {
      'X-API-Key': c.env.ZELTA_API_KEY,
      'Content-Type': 'application/json'
    }
  });

  const result = await response.json();
  return c.json(result);
});

export default app;

::

:: tab Python

python
import requests

response = requests.get(
    'https://api-pay.zelta.dev/v1/payment-links',
    headers={
        'X-API-Key': 'tu-api-key-aqui',
        'Content-Type': 'application/json'
    }
)

result = response.json()
print(result)

::

Ahora creemos un link de pago:

:: tab cURL

bash
curl -X POST "https://api-pay.zelta.dev/v1/payment-links" \
  -H "X-API-Key: tu-api-key-aqui" \
  -H "Content-Type: application/json" \
  -d '{
    "concept": "Pago de prueba",
    "amount": 100,
    "customerName": "Juan Pérez",
    "isTest": true
  }'

::

:: tab Node.js

javascript
const response = await fetch('https://api-pay.zelta.dev/v1/payment-links', {
  method: 'POST',
  headers: {
    'X-API-Key': 'tu-api-key-aqui',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    concept: 'Pago de prueba',
    amount: 100,
    customerName: 'Juan Pérez',
    isTest: true
  })
});

const result = await response.json();
console.log(result);

::

:: tab Cloudflare Workers

javascript
export default {
  async fetch(request, env, ctx) {
    const response = await fetch('https://api-pay.zelta.dev/v1/payment-links', {
      method: 'POST',
      headers: {
        'X-API-Key': env.ZELTA_API_KEY,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        concept: 'Pago de prueba',
        amount: 100,
        customerName: 'Juan Pérez',
        isTest: true
      })
    });

    const result = await response.json();
    return new Response(JSON.stringify(result), {
      headers: { 'Content-Type': 'application/json' }
    });
  }
};

::

:: tab Hono

typescript
import { Hono } from 'hono';
import { cors } from 'hono/cors';

type Bindings = {
  ZELTA_API_KEY: string;
};

const app = new Hono<{ Bindings: Bindings }>();

app.use('*', cors());

app.post('/payment-links', async (c) => {
  const response = await fetch('https://api-pay.zelta.dev/v1/payment-links', {
    method: 'POST',
    headers: {
      'X-API-Key': c.env.ZELTA_API_KEY,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      concept: 'Pago de prueba',
      amount: 100,
      customerName: 'Juan Pérez',
      isTest: true
    })
  });

  const result = await response.json();
  return c.json(result);
});

export default app;

::

:: tab Python

python
import requests

response = requests.post(
    'https://api-pay.zelta.dev/v1/payment-links',
    headers={
        'X-API-Key': 'tu-api-key-aqui',
        'Content-Type': 'application/json'
    },
    json={
        'concept': 'Pago de prueba',
        'amount': 100,
        'customerName': 'Juan Pérez',
        'isTest': True
    }
)

result = response.json()
print(result)

::

Paso 4: Entiende la respuesta

Una respuesta exitosa se verá así:

json
{
  "success": true,
  "data": {
    "paymentLink": {
      "id": "pl_1234567890abcdef",
      "paymentLinkUrl": "https://pay.zelta.dev/abc123",
      "customerName": "Juan Pérez",
      "concept": "Pago de prueba",
      "amount": 100,
      "status": "pending",
      "createdAt": "2024-01-15T10:30:00.000Z",
      "expiresAt": "2024-01-18T10:30:00.000Z",
      "isTest": true
    }
  },
  "message": "Payment link created successfully",
  "timestamp": "2024-01-15T10:30:00.000Z"
}

Campos clave de la respuesta

  • paymentLinkUrl: La URL que tu cliente usará para pagar
  • id: Identificador único del link de pago
  • status: Estado actual (siempre "pending" para links nuevos)
  • expiresAt: Cuándo expira el link (3 días desde la creación vía API)

Paso 5: Gestiona el pago

Una vez que tengas la URL del link de pago, puedes:

  1. Enviarlo a tu cliente por correo, SMS o mostrarlo en tu sitio web
  2. Redirigirlo al portal de pago
  3. Monitorear el estado llamando a la API o configurando webhooks
javascript
// Redirigir al cliente a la página de pago
window.location.href = result.data.paymentLink.paymentLinkUrl;

Paso 6: Configura webhooks (opcional)

Para recibir notificaciones en tiempo real cuando se completen los pagos:

  1. Ve a Webhooks en tu dashboard
  2. Agrega la URL de tu webhook (debe ser HTTPS)
  3. Selecciona los eventos que quieres recibir
  4. Prueba tu endpoint de webhook

:: tab Node.js (Express)

javascript
app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
  const signature = req.headers['zeltapay-signature'];
  const timestamp = req.headers['zeltapay-timestamp'];

  // Verificar firma (importante para seguridad)
  if (!verifySignature(req.body.toString(), timestamp, signature)) {
    return res.status(401).json({ error: 'Invalid signature' });
  }

  const payload = JSON.parse(req.body);

  if (payload.type === 'payment.success') {
    console.log('Pago completado:', payload.transaction.amount);
    // Actualiza tu base de datos, envía correo de confirmación, etc.
  }

  res.status(200).json({ received: true });
});

::

:: tab Cloudflare Workers

javascript
export default {
  async fetch(request, env, ctx) {
    if (request.method !== 'POST') {
      return new Response('Method not allowed', { status: 405 });
    }

    try {
      const signature = request.headers.get('zeltapay-signature');
      const timestamp = request.headers.get('zeltapay-timestamp');
      const rawBody = await request.text();

      if (!await verifySignature(rawBody, timestamp, signature, env.WEBHOOK_SECRET)) {
        return new Response(JSON.stringify({ error: 'Invalid signature' }), {
          status: 401,
          headers: { 'Content-Type': 'application/json' }
        });
      }

      const payload = JSON.parse(rawBody);

      if (payload.type === 'payment.success') {
        console.log('Pago completado:', payload.transaction.amount);
      }

      return new Response(JSON.stringify({ received: true }), {
        status: 200,
        headers: { 'Content-Type': 'application/json' }
      });
    } catch (error) {
      return new Response(JSON.stringify({ error: 'Processing failed' }), {
        status: 500,
        headers: { 'Content-Type': 'application/json' }
      });
    }
  }
};

::

:: tab Hono

typescript
import { Hono } from 'hono';

type Bindings = {
  WEBHOOK_SECRET: string;
};

const app = new Hono<{ Bindings: Bindings }>();

app.post('/webhook', async (c) => {
  try {
    const signature = c.req.header('zeltapay-signature');
    const timestamp = c.req.header('zeltapay-timestamp');

    if (!signature || !timestamp) {
      return c.json({ error: 'Missing required headers' }, 400);
    }

    const rawBody = await c.req.text();

    if (!await verifySignature(rawBody, timestamp, signature, c.env.WEBHOOK_SECRET)) {
      return c.json({ error: 'Invalid signature' }, 401);
    }

    const payload = JSON.parse(rawBody);

    if (payload.type === 'payment.success') {
      console.log('Pago completado:', payload.transaction.amount);
    }

    return c.json({ received: true });
  } catch (error) {
    return c.json({ error: 'Processing failed' }, 500);
  }
});

export default app;

::

Siguientes pasos

  • — API keys y rate limiting
  • — Opciones avanzadas y validación
  • — Notificaciones de pago en tiempo real
  • — Documentación completa de endpoints

Problemas comunes

401 Unauthorized

  • Verifica que tu API key sea correcta
  • Asegúrate de incluir el header X-API-Key
  • Confirma que la API key esté activa en tu dashboard

400 Bad Request

  • Verifica que todos los campos requeridos estén incluidos
  • Asegúrate de que el monto esté en centavos (ej. 100 = $1.00)
  • Confirma que el formato JSON sea correcto

429 Too Many Requests

  • Has excedido el rate limit (60 solicitudes por minuto)
  • Espera antes de hacer más solicitudes
  • Considera implementar cola de solicitudes

Documentación oficial de Zelta