Inicio / Desarrolladores · API
Partner API · v1

Cobros por SPEI para tu plataforma

Integra cobro por transferencia SPEI para tus clientes. Pides una CLABE por cada cliente tuyo, recibes un webhook firmado cuando cae un pago, y consultas pagos para reconciliar. Tus datos siguen siendo tuyos: nos mandas tu propia referencia y te la regresamos en cada evento.

REST · JSONMontos en centavosWebhooks firmadosAislamiento por tokenSandbox disponible

Concepto

Tú operas la relación con tus clientes; nosotros somos la infraestructura de cobro. El puente entre ambos mundos es external_ref: el identificador de tu cliente en tu sistema. Lo guardamos junto a la CLABE que emitimos y te lo devolvemos en cada webhook y consulta — así nunca necesitas importar tus datos a nuestro sistema.

PasoQué haces
1Por cada cliente tuyo, pides una CLABE con tu external_ref.
2Le das esa CLABE a tu cliente para que te pague por SPEI.
3Cuando cae el pago, te llega un payment.received firmado con el external_ref.
4Reconcilias (o haces backfill) con GET /v1/payments.

Autenticación

Cada solicitud lleva tu token en el header. El business_id y los permisos salen siempre del token, nunca del cuerpo del request. Guardamos solo el hash del token, nunca el token en claro.

Header
Authorization: Bearer <tu_token>
Te entregamos el token al darte de alta. Trátalo como secreto: variables de entorno o bóveda cifrada, nunca en el repositorio ni en el front.

01 Emitir / obtener una CLABE

POST/v1/clabesidempotente por external_ref

Si ya existe la CLABE de ese cliente, te la devuelve — no crea otra. Puedes reintentar sin duplicar.

Request · cURL
curl -X POST https://hailanerp.site/api/finaxapay/v1/clabes \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "external_ref": "CLI-4821", "name": "Cliente Ejemplo SA" }'
Response · 200
{ "clabe": "646180157000000004", "external_ref": "CLI-4821", "status": "active" }
CampoReq.Notas
external_refEl id de tu cliente en tu sistema (1–128 caracteres). Es la llave: úsalo consistente.
namenoAlias legible del cliente (≤160).
Guarda el par clabe ↔ tu cliente en tu base. Esa CLABE se la das al cliente para que te pague por SPEI.

02 Consultar CLABEs

GET/v1/clabes/{external_ref}

Una CLABE por tu referencia. Responde 404 si no existe.

GET/v1/clabes?limit=100&offset=0

Lista paginada de tus CLABEs.

Ejemplos
# una CLABE por external_ref (404 si no existe)
GET /v1/clabes/CLI-4821

# lista de tus CLABEs
GET /v1/clabes?limit=100&offset=0

03 Webhook: payment.received

Cuando cae un pago a una de tus CLABEs, hacemos POST a la URL que configuraste, con firma HMAC. Es la vía principal para enterarte de los cobros en tiempo real.

Payload que te enviamos
{
  "event": "payment.received",
  "payment_id": "c8be760d-…",
  "external_ref": "CLI-4821",
  "clabe": "646180157000000004",
  "amount_cents": 150000,
  "currency": "MXN",
  "tracking_key": "…",
  "transaction_date": "2026-08-02T18:04:00Z",
  "cep_url": "https://…/cep"
}

Verificar la firma OBLIGATORIO

Header Finaxa-Signature: t=<unix>,v1=<hmac_hex>. La firma es HMAC-SHA256(secret, "<t>.<rawBody>") en hex, donde rawBody es el cuerpo crudo tal como llegó (no lo re-serialices).

Node.js
import crypto from 'crypto'

function verify(rawBody, header, secret, toleranceSec = 300) {
  const parts = Object.fromEntries(header.split(',').map(kv => kv.split('=')))
  const t = Number(parts.t)
  // anti-replay: rechaza firmas viejas
  if (!t || Math.abs(Date.now() / 1000 - t) > toleranceSec) return false
  const expected = crypto.createHmac('sha256', secret).update(`${t}.${rawBody}`).digest('hex')
  return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1))
}

Reglas de entrega

  • Responde 2xx rápido. Si no, reintentamos con backoff: 2, 4, 8, 16, 32 min, hasta 6 intentos.
  • Idempotencia: la entrega es at-least-once — puedes recibir el mismo payment.received más de una vez. Deduplica por payment_id (es estable por pago).
  • Si perdiste un webhook, reconcilia con GET /v1/payments.

04 Consultar pagos

Para reconciliar o hacer backfill si perdiste un webhook.

GET/v1/payments
Request
GET /v1/payments?external_ref=CLI-4821&since=2026-08-01T00:00:00Z&limit=100&offset=0
Response
{ "data": [ {
  "payment_id": "…", "external_ref": "CLI-4821", "clabe": "…",
  "amount_cents": 150000, "currency": "MXN", "status": "succeeded",
  "tracking_key": "…", "transaction_date": "…", "cep_url": "…"
} ] }

Filtros opcionales: external_ref, since (ISO 8601), limit (1–200), offset.

05 Errores y límites

CódigoSignificado
400Datos inválidos (revisa el campo error).
401Token faltante, inválido o revocado.
403El token no tiene permiso para esa operación.
404El recurso no existe (o no es tuyo).
429Demasiadas solicitudes — reintenta con backoff.
502No pudimos emitir la CLABE en ese momento — reintenta.
Rate limit (por token): creación de CLABEs ~60/min, lecturas ~300/min. Diseña tu cliente con reintentos y backoff.

06 Alta (onboarding)

El alta la hacemos nosotros. Te entregamos:

  • Tu token (Bearer) de acceso.
  • Configuramos tu URL de webhook y te damos su secret de firma — se muestra una sola vez.
  • Acceso al ambiente de pruebas (sandbox) para integrar de punta a punta antes de producción.

07 Notas de seguridad

  • Nunca publiques el token ni el secret del webhook.
  • Los montos son centavos. No los dividas antes de firmar/verificar.
  • Los datos de tus clientes se quedan en tu sistema; nosotros solo guardamos external_ref y te lo regresamos.
  • Verifica siempre la firma del webhook antes de confiar en el payload.
Este documento describe únicamente el servicio de cobros. La dispersión (envío de SPEI) no forma parte de esta API.