Skip to Content
Referencia de la APIAutenticación

Autenticación

InfraIO Pay tiene dos superficies de API con modelos de auth distintos. Elige la que coincida con quién llama:

SuperficiePrefijo de rutaAudienciaAuth
B2B del comerciante/b2b/v1/*Tu servidorFirma de petición HMAC-SHA256
DashboardPor servicio: /auth/*, /payment/*, /merchant/*, /event/*, /user/*, …Sesiones de navegador para el dashboard del comercianteBearer JWT

Esta página cubre la superficie B2B: la que llamas desde tu servidor con un par de claves de API. Si estás incrustando el dashboard de InfraIO o construyendo tooling interno, usa la superficie del dashboard (docs separadas, aún no públicas).

El gateway enruta cada superficie por un prefijo inicial que elimina antes de reenviar: /b2b/v1/checkout-sessions/quick llega a payment-service como /v1/checkout-sessions/quick, y el /payment/v1/orders del dashboard le llega como /v1/orders. Así que si ves rutas /v1/* desnudas en otro sitio, esa es la ruta interna del backend después de que se haya eliminado el prefijo público: tu cliente siempre envía la forma con prefijo. (Una consecuencia para la firma: la cadena canónica B2B firma la ruta con el prefijo /b2b aún adjunto: ver abajo.)

Endpoints

EntornoBase URL
Testhttps://api-dev.infraio.xyz
Livehttps://api.infraio.xyz

Mismo patrón de URL: el entorno se controla mediante el prefijo de la clave (pk_test_… vs pk_live_…), no la URL.

Par de claves

Obtienes dos valores del dashboard del comerciante (Developers → API keys → + Add key):

  • Publishable key (pk_test_… o pk_live_…): identifica tu cuenta. Se envía como X-Client-ID. Seguro de incrustar en tu bundle de navegador (el SDK ya lo hace).
  • Secret key (sk_test_… o sk_live_…): la clave de firma HMAC. Solo servidor. Trátala como una contraseña de base de datos.

Si una clave secreta alguna vez aterriza en un bundle de navegador, un repo de git, una línea de log o un chat compartido, revócala inmediatamente desde el dashboard. No hay ventana de solapamiento; la revocación es instantánea. Emite una nueva clave y redespliega.

Firmar una petición

Cada llamada a /b2b/v1/* lleva tres cabeceras:

X-Client-ID: pk_live_70d0becbd7a53047b0dfe88a960d10ad X-Timestamp: 1715990400 X-Signature: 9a8b7c6d… (hex HMAC-SHA256)

La firma se calcula sobre una cadena canónica:

METHOD + "\n" + PATH + "\n" + TIMESTAMP + "\n" + BODY
  • METHOD: verbo HTTP en mayúsculas (POST, GET, …).
  • PATH: ruta de la petición incluyendo el prefijo /b2b, sin el host y sin la query string (p. ej. /b2b/v1/checkout-sessions/quick). El gateway verifica la firma sobre la ruta entrante cruda antes de eliminar /b2b, así que el prefijo debe estar presente. Los parámetros de query no se firman: para un GET …?cursor=…&limit=20, firma solo la ruta, no la parte ?….
  • TIMESTAMP: unix-segundos, como cadena decimal (p. ej. "1715990400"), coincidiendo exactamente con X-Timestamp.
  • BODY: bytes crudos del request body. Cadena vacía para GET/DELETE.

Firma con HMAC-SHA256 con la clave secreta como llave, salida hex:

import { createHmac } from "node:crypto"; function sign({ method, path, body, secret }: { method: string; path: string; body: string; secret: string; }) { const timestamp = Math.floor(Date.now() / 1000).toString(); const input = [method.toUpperCase(), path, timestamp, body].join("\n"); const signature = createHmac("sha256", secret).update(input).digest("hex"); return { timestamp, signature }; }

¿Por qué HMAC y no Bearer?

Una API Bearer-token simple envía tu único secreto por el cable en cada petición. Cualquiera que capture un log de proxy con TLS terminado obtiene las llaves de tu cuenta. La firma HMAC significa que el secreto nunca viaja, solo su firma derivada, que es de un solo uso (atada a esa petición exacta + ese minuto exacto).

El compromiso: tienes que calcular la firma para cada llamada. Un SDK del servidor ocultaría esto; hasta que publiquemos uno, el helper de arriba son ~15 líneas por lenguaje.

Tolerancia de timestamp

La tolerancia vinculante es ±5 minutos (300 segundos), aplicada por merchant-service cuando verifica la firma. El propio gateway es ligeramente más laxo (310s) como defensa en profundidad, pero una petición que pasa el gateway y falla la comprobación interna aún termina en 401 invalid_signature: asume 300s como el contrato. Dos implicaciones:

  1. Sincroniza el reloj de tu servidor con NTP. Un cron de larga duración con un reloj desviado fallará intermitentemente.
  2. No precalcules y encoles firmas. Si una petición se queda en una cola de reintento durante más de 5 min, su firma expira.

Scopes de clave

Las claves secretas llevan uno o más de estos paquetes de scope:

ScopeUso previsto
readListar/leer órdenes, sesiones, reembolsos
write_orderCrear sesiones de checkout, órdenes
write_refundEmitir reembolsos, emitir tokens de solicitud de reembolso
webhook_manageCrear/actualizar/eliminar endpoints de webhook

El dashboard emite una clave de “acceso total” por defecto (los cuatro scopes). Puedes emitir una clave con scope restringido desde Developers → API keys → + Add key y marcar solo los scopes que necesite la integración.

La aplicación de scopes es actualmente consultiva, no obligatoria. Los scopes se registran en la clave y se te muestran de vuelta en el dashboard, pero el middleware del gateway aún no rechaza llamadas fuera de scope: cualquier clave sk_… válida se comporta como acceso total hoy. El gating de scope por endpoint llega en el siguiente release. No te apoyes aún en los scopes como límite de seguridad; trátalos como etiquetas y rota / revoca claves para restringir el acceso mientras tanto.

Dónde se verifica la firma

La validación HMAC ocurre una vez, en el gateway. El gateway:

  1. Lee X-Client-ID, X-Timestamp, X-Signature.
  2. Busca el comerciante + secreto por pk_…, ejecuta la comprobación de ventana de timestamp, recalcula la firma, compara en tiempo constante.
  3. En éxito, elimina las cabeceras de auth, sella la petición con cabeceras internas (X-B2B-Auth: 1, X-Merchant-ID, X-Merchant-Domain) y reenvía al servicio aguas abajo (payment-service, merchant-service, etc.). El entorno + scopes resueltos NO se inyectan hoy: el código aguas abajo que necesita el entorno lo deriva del request body / config por comerciante, no de las cabeceras.
  4. En fallo, devuelve 401 INVALID_SIGNATURE sin tocar el backend.

Los servicios aguas abajo no re-ejecutan HMAC: confían en las cabeceras inyectadas por el gateway y actúan sobre el comerciante que el gateway resolvió. Tampoco hacen scope-gating por endpoint: como se ha mencionado, el scope de la clave no se inyecta, así que cualquier sk_… autenticada alcanza cualquier endpoint para su comerciante (la aplicación de scopes es consultiva hoy: ver el callout bajo Scopes de clave). Esto importa en dos sentidos:

  • Si operas tu propio reverse proxy delante de InfraIO Pay, no elimines X-B2B-Auth / X-Merchant-ID (y no las falsifiques tampoco: el gateway rechaza peticiones entrantes que las lleven en el borde público).
  • Las rutas de red pública (/b2b/v1/*) son la única superficie que ejecuta el paso HMAC. El gRPC interno entre nuestros servicios usa mTLS: un modelo de confianza distinto que no acepta X-Client-ID.

Qué sigue

  • Errores: forma de la respuesta en 4xx/5xx.
  • Seguridad → Claves de API: rotación, revocación, qué hacer si una clave secreta se filtra.
  • Webhooks → Verificación de firma: usa un esquema HMAC diferente (cabecera X-Signature: sha256=…, firma X-Timestamp + "." + raw_body, más un X-Signature-Prev opcional durante la ventana de gracia de rotación de 24 horas). No mezcles los esquemas: comparten el algoritmo de hash pero los bytes firmados y la familia de secretos (whsec_… vs sk_…) son diferentes.