SDK de JavaScript / Navegador
@lartech/infraio-checkout-js es el único SDK que publicamos hoy.
Corre en el navegador y abre nuestro checkout alojado. Los SDKs de
backend (Node, Go, Python) están en el roadmap; hasta entonces,
habla directamente con el gateway: consulta el
Inicio rápido para un helper de firma
HMAC.
- Versión actual:
0.1.1-beta.17(pre-1.0; espera rupturas menores) - Formatos: ESM (
index.js), CJS (index.cjs), IIFE (index.global.js) - Tipos incluidos (
index.d.ts) - Cero peers en runtime: sin React, jQuery u otras dependencias
No hay entry point del lado servidor. Los helpers de
verificación de firma para webhooks no se incluyen: impleméntalos
tú mismo con crypto (la página de verificación de firma
tiene código copia-pega en 4 lenguajes).
Instalación
npm
npm install @lartech/infraio-checkout-jsloadInfraIo(publicKey, options?)
Devuelve un Promise<InfraIoInstance>.
import { loadInfraIo } from "@lartech/infraio-checkout-js";
const sdk = await loadInfraIo("pk_live_yourkeyhere", {
// Opcional. Sobreescribe solo cuando apuntes a un entorno no-prod.
checkoutUrl: "https://checkout-dev.infraio.xyz",
});| Param | Tipo | Requerido | Notas |
|---|---|---|---|
publicKey | string | ✓ | Debe coincidir con pk_(live|test)_… |
options.checkoutUrl | string | — | Sobreescribe la URL base del checkout. Por defecto: https://checkout.infraio.xyz. La URL del gateway correspondiente se determina por la página de checkout en runtime: cada deploy de checkout(-dev).infraio.xyz lleva su NEXT_PUBLIC_API_URL de tiempo de compilación, así que elegir el hostname correcto aquí elige el backend correcto automáticamente. No hay una opción gatewayUrl separada. |
sdk.checkout({ … })
Abre el checkout alojado. Devuelve void (usa callbacks para el
estado).
| Campo | Tipo | Requerido | Notas |
|---|---|---|---|
sessionId | string | ✓ | session_key devuelta por POST /b2b/v1/checkout-sessions/quick |
checkoutUrl | string | — | URL completa devuelta por el mismo endpoint. Si se omite, el SDK la construye a partir del checkoutUrl de loadInfraIo() (o el por defecto) + sessionId |
mode | "popup" | "redirect" | "embed" | — | Por defecto "popup" |
container | string | HTMLElement | solo embed | Selector CSS o elemento DOM donde se monta el iframe |
width | number | — | Solo popup. Por defecto 560. Acotado a [320, 1280] |
height | number | — | Solo popup. Por defecto 780. Acotado a [400, 1000] |
timeoutMs | number | — | Solo popup. Timeout de carga del iframe. Por defecto 30000. Pasa 0 para deshabilitar |
locale | string | — | Tag BCP-47 reenviado como ?locale= a la página de checkout (en, ja, zh-CN, zh-TW) |
hideSummary | boolean | — | Oculta la columna de resumen de orden. Por defecto false |
hideHeader | boolean | — | Oculta el header de InfraIO y el botón de wallet-connect integrado. Por defecto false. Combina con walletAddress para white-label total |
walletAddress | string | — | Pre-conecta una wallet de comprador. Requiere onSignRequest |
walletChainId | number | — | Chain ID EVM para la wallet pre-conectada |
onReady | () => void | — | Se dispara una vez que el iframe es interactivo. Solo popup/embed |
onSignRequest | (req: { method: string; params: unknown[] }) => Promise<string> | cuando walletAddress está fijado | El SDK reenvía los RPCs de wallet a tu handler; devuelve el hex firmado |
onSuccess | ({ sessionId }) => void | — | Se dispara en pago exitoso. No es autoritativo: el webhook lo es |
onCancel | () => void | — | Se dispara cuando el comprador cierra el popup/embed sin pagar |
onError | (err: InfraIoError) => void | — | Se dispara en fallo de carga del iframe (modos popup + embed). Los argumentos inválidos se lanzan de forma síncrona, no se entregan aquí. El modo redirect no tiene superficie de error en runtime: el fallo se observa en la página redirigida. |
onSuccess no es autoritativo. Puede dispararse incluso cuando
el webhook luego determina que el pago falló (reorgs de testnet,
timing del lado comprador). Úsalo solo para UX (mostrar “¡Gracias!”,
redirigir). Confirma siempre vía webhook antes de cumplir.
sdk.close()
Descarta programáticamente un popup o embed abierto. No-op para el modo redirect.
sdk.checkout({ sessionId, mode: "popup", /* … */ });
// más tarde, p. ej. cuando el usuario navega fuera
sdk.close();sdk.openRefundRequest({ … })
Abre el formulario de solicitud de reembolso alojado para un
token de un solo uso que tu backend emitió vía
POST /b2b/v1/merchants/{merchant_id}/refund-requests. El comprador
rellena su dirección de destino del reembolso + razón + metadatos
(opcional) en nuestra página; tu página solo maneja el ciclo de
vida de apertura/cierre. Devuelve una función close():
llámala para descartar programáticamente el popup o separar el
iframe embed. En modo redirect la función devuelta es un no-op.
El formulario vive en https://checkout.infraio.xyz/refund-request/:token.
Este método solo envuelve esa URL en un popup / redirect / embed
para que el comprador nunca deje tu dominio (en popup / embed) o
regrese automáticamente (en redirect). El endpoint que tu backend
pega para emitir el token es
POST /b2b/v1/merchants/{merchant_id}/refund-requests: firmado con
HMAC con tu clave secreta, misma auth que el resto de la superficie
B2B. Consulta
Conceptos → Reembolsos.
import { loadInfraIo } from "@lartech/infraio-checkout-js";
const sdk = await loadInfraIo("pk_live_yourkeyhere");
// Emite el token en el lado servidor, luego entrégaselo al SDK en el navegador.
const { token } = await fetch("/api/mint-refund-token", { method: "POST" }).then(r => r.json());
const close = sdk.openRefundRequest({
token,
mode: "popup",
onSuccess: ({ linkToken, refundId }) => {
// El comprador envió el formulario.
// linkToken → página de estado /r/:linkToken (compartir con el comprador).
// refundId → usar con la API B2B para aprobar / rechazar.
window.location.href = `/r/${linkToken}`;
},
onCancel: () => { /* el comprador cerró sin enviar */ },
});
// Descartar programáticamente el popup más tarde si es necesario:
// close();| Campo | Tipo | Requerido | Notas |
|---|---|---|---|
token | string | ✓ | El token rfqt_… devuelto por POST /b2b/v1/merchants/{merchant_id}/refund-requests |
mode | "popup" | "redirect" | "embed" | — | Por defecto "popup". Misma semántica de superficie que sdk.checkout(): consulta Notas de modo |
container | string | HTMLElement | solo embed | Selector CSS o elemento DOM donde se monta el iframe |
locale | string | — | Tag BCP-47 reenviado como ?locale= (en, ja, zh-CN, zh-TW) |
hideHeader | boolean | — | Oculta el header de InfraIO dentro del iframe. En popup/embed el SDK dibuja su propio chrome de modal, así que el header de la página es típicamente ruido. Por defecto false |
hideSummary | boolean | — | Oculta la columna de Resumen de Orden, mostrando solo el formulario de reembolso. Por defecto false |
walletAddress | string | — | Pre-rellena el campo de wallet de destino (?wallet_address=). Deja que un comerciante que ya conoce la wallet del comprador salte el re-tipeo manual |
onSuccess | (data: { linkToken: string; refundId: string }) => void | — | Se dispara tras el envío del formulario por el comprador. linkToken → página de estado /r/:linkToken para compartir con el comprador. refundId → usar con la API B2B para aprobar / rechazar |
onCancel | () => void | — | Se dispara cuando el comprador cierra el popup/embed sin enviar |
onError | (err: InfraIoError) => void | — | Se dispara en fallo de carga del iframe o args inválidos. La expiración / cancelación del token la maneja la página alojada, no vía onError |
Estados del token expuestos vía onError
Si el comprador abre un token caducado, la propia página maneja la
visualización (renderiza un prompt “Expirado: solicita un enlace
nuevo”, etc.), y el SDK no dispara onError para esos casos:
el comprador está dentro del flujo del formulario y tu código no
necesita reaccionar. onError solo se dispara para cosas sobre las
que tu código puede actuar (argumentos incorrectos, fallo de red al
cargar el iframe).
openRefundRequest registra la intención del reembolso: no
mueve fondos. Tras onSuccess, la fila de reembolso es PENDING
(o APPROVED si tu config de comerciante auto-aprueba reembolsos
de cliente). Aún necesitas firmar y difundir la transferencia
on-chain desde tu wallet de comerciante, luego publicar el tx hash
en POST /b2b/v1/refunds/:id/submit-tx. Consulta
Conceptos → Reembolsos para el ciclo de
vida completo.
Clase de error
import { InfraIoError } from "@lartech/infraio-checkout-js";
sdk.checkout({
sessionId,
onError: (err: InfraIoError) => {
switch (err.code) {
case "invalid_request_error": /* sessionId / args malos */ break;
case "iframe_load_error": /* el iframe falló al cargar */ break;
case "iframe_timeout_error": /* excedió timeoutMs */ break;
case "already_open_error": /* otro checkout ya está abierto */ break;
case "network_error": /* problema de red transitorio hablando con el origin del checkout */ break;
case "api_error": /* el backend devolvió un no-2xx para una llamada emitida por el SDK */ break;
}
},
});sdk.openRefundRequest() solo lanza invalid_request_error
(token / args ausentes o inválidos), de forma síncrona.
iframe_load_error (el iframe de solicitud de reembolso falló al
cargar) se entrega de forma asíncrona vía onError, no se
lanza. No emite iframe_timeout_error ni already_open_error:
el popup de solicitud de reembolso no tiene timeout de carga y
permite múltiples popups concurrentes.
Notas de modo
Popup
- Overlay centrado con un fondo oscuro semi-transparente
z-index: 2147483647(max int32): se sienta encima de todo lo demás- El scroll del body se bloquea mientras está abierto; se restaura al cerrar
- El botón de cerrar recibe el foco inicial; Tab queda atrapado en el popup
- Se cierra por: botón de cerrar, Escape, clic fuera,
sdk.close(). Todos ellos llaman aonCancel - La página de checkout puede solicitar redimensión vía
postMessage: el SDK acota dentro de los límites dewidth/height
Redirect
- Navegación dura vía
window.location.href - Añade automáticamente
?return_url=<página-actual>para que el comprador regrese de donde vino. Si tusuccess_url/cancel_urlen la sesión ya cubren esto, el viaje de ida y vuelta ignorareturn_url
Embed
- iframe con
allow="payment; clipboard-write"(directivas de Feature Policy HTML5, no el atributosandbox). El iframe se sirve desde el origin del checkout, así que los popups de wallet y las escrituras al portapapeles desde el lado del comprador funcionan sin opt-in adicional. - El ancho del contenedor es 100%; la altura se autodimensiona vía
postMessage
INFRAIO_RESIZE, acotado a[200, 2000]px - Sin aislamiento CSS más allá del borde del iframe: los estilos de tu página padre no se filtran
- Cablea siempre
onReadypara que puedas ocultar tu propio estado de carga cuando el checkout se vuelva interactivo
TypeScript
Todos los tipos están incluidos. Las exportaciones más útiles:
import type {
CheckoutOptions,
RefundRequestOptions,
LoadOptions,
InfraIoInstance,
InfraIoErrorCode,
} from "@lartech/infraio-checkout-js";
import { loadInfraIo, InfraIoError, VERSION } from "@lartech/infraio-checkout-js";VERSION es la cadena de versión del propio SDK: útil en reportes
de bug.
Qué sigue
- Wrappers de framework React / Vue: planificados para la
iteración post-lanzamiento una vez que la superficie vanilla JS
se estabilice entre los comerciantes piloto. El SDK vanilla
funciona bien dentro de React hoy; los wrappers solo eliminarán
la plomería manual de
useRef+ ciclo de vida. - Reanudación de sesión entre pestañas: abre el checkout en la pestaña A, termina en la pestaña B. Útil cuando un comprador sigue un magic-link a mitad de flujo. Rastreado para el próximo release menor.
- Theming vía variables CSS: exponer un set pequeño de design tokens (radio, color de acento) en el iframe para que los comerciantes puedan igualar su marca sin forkear la página.
- Helpers del lado servidor: un pequeño paquete
@lartech/infraio-serverexponiendoverifyWebhook()+signedRequest()para que el código de backend no necesite copiar el ritual de HMAC. Hasta que se envíe, la página de verificación de firma lista implementaciones drop-in en TypeScript, Go, Python y Ruby.