@npmtapi/embed
v1.7.0
Published
TapiPay Embed SDK — carga y controla el widget de pago embebido
Readme
@npmtapi/embed
SDK para integrar el widget de pago de TapiPay en cualquier sitio web o aplicación.
Instalación
# npm
npm install @npmtapi/embed
# pnpm
pnpm add @npmtapi/embed
# yarn
yarn add @npmtapi/embedEntrypoints
| Import | Contenido |
|--------|-----------|
| @npmtapi/embed | loadTapipay() + tipos TypeScript |
| @npmtapi/embed/react | <TapipayWidget> + useTapipay() |
Los tipos están incluidos — no necesitas @types/tapipay.
Uso rápido
Script tag (sin bundler)
Se requiere container + identifier + uno de organization o companyCode (si se pasan ambos, companyCode tiene prioridad).
<script src="https://app.tapipay.la/embed.js"></script>
<div id="widget"></div>
<script>
tapipay.initialize({
container: "#widget",
organization: "acme", // o companyCode: "acme-mx"
identifier: "CLI-001"
});
tapipay.on("paymentWindowOpened", function() {
console.log("Ventana de pago abierta");
});
tapipay.on("paymentSubmitted", function(data) {
console.log("Pago enviado:", data);
});
</script>Data-attrs (cero JavaScript)
<script src="https://app.tapipay.la/embed.js"></script>
<div
data-tapipay-widget
data-organization="acme"
data-identifier="CLI-001"
></div>React
import { TapipayWidget } from "@npmtapi/embed/react";
export function PagoPage({ clienteId }: { clienteId: string }) {
return (
<TapipayWidget
organization="acme"
identifier={clienteId}
onPaymentWindowOpened={() => console.log("Ventana de pago abierta")}
onPaymentSubmitted={(data) => console.log("Pago enviado:", data)}
onCancelled={() => console.log("Pago cancelado por el usuario")}
/>
);
}Props
Se requiere identifier + uno de organization o companyCode. Si se pasan ambos, companyCode tiene prioridad.
| Prop | Tipo | Requerido | Por defecto | Descripción |
|------|------|-----------|-------------|-------------|
| organization | string | Uno de los dos | — | Alias de la organización en TapiPay. Requerido si no se pasa companyCode |
| companyCode | string | Uno de los dos | — | Código de compañía en TapiPay. Alternativa a organization. Si se pasan ambos, companyCode tiene prioridad |
| identifier | string | Sí | — | Identificador del cliente |
| externalRequestId | string | No | — | ID de deuda a preseleccionar. Tiene precedencia sobre selectionStrategy |
| selectionStrategy | "oldestCreated" | No | más reciente | Estrategia de selección automática de deuda pendiente. "oldestCreated": la más antigua. Por defecto (omitido): la más reciente |
| view | 'payments' \| 'autopay' | No | 'payments' | Vista a montar. 'autopay' muestra la gestión de adhesiones (ver domiciliación + desadherirse) |
| environment | 'production' \| 'homo' | No | 'production' | Entorno del SDK |
| onPaymentWindowOpened | () => void | No | — | Callback cuando el popup de Yuno se abre exitosamente |
| onPaymentSubmitted | (data) => void | No | — | Callback cuando el usuario completó el formulario de Yuno y fue redirigido al callback |
| onCancelled | () => void | No | — | Callback cuando el usuario cierra el popup sin completar el pago |
| onAutopayLoaded | (data) => void | No | — | (Vista autopay) Callback con las adhesiones activas; se reinvoca cuando la lista cambia |
| onAutopayActivated | (data) => void | No | — | (Vista autopay) Callback cuando una nueva domiciliación queda activa (una sola vez por adhesión nueva) |
| onAutopayRemoved | (data) => void | No | — | (Vista autopay) Callback cuando el usuario desadhiere un medio con éxito |
| onAutopayError | (data) => void | No | — | (Vista autopay) Callback ante un error de carga o de desadhesión |
| onDomiciliationBankSelected | (data) => void | No | — | (Vista autopay) El usuario eligió su banco en el alta de domiciliación |
| onDomiciliationFormCompleted | (data) => void | No | — | (Vista autopay) Los datos de cuenta pasaron la validación del backend. Dispara al confirmar el método de verificación, no al salir del formulario |
| onDomiciliationVerificationCompleted | (data) => void | No | — | (Vista autopay) La verificación llegó a un desenlace. Solo emite al outcome final: un intento fallido con reintentos disponibles no dispara el evento |
| onDomiciliationConfirmed | () => void | No | — | (Vista autopay) El usuario confirmó la domiciliación. No significa que quedó registrada — la confirmación real llega por onAutopayActivated |
| className | string | No | — | Clases CSS del contenedor |
| style | CSSProperties | No | — | Estilos inline del contenedor |
Next.js App Router
"use client";
import { TapipayWidget } from "@npmtapi/embed/react";
import { useRouter } from "next/navigation";
export default function PagarPage() {
const router = useRouter();
return (
<TapipayWidget
organization={process.env.NEXT_PUBLIC_TAPIPAY_ORGANIZATION!}
identifier="CLI-001"
onPaymentSubmitted={() => router.push("/procesando")}
/>
);
}Vue 3
<template>
<div ref="widgetRef"></div>
</template>
<script setup>
import { loadTapipay } from "@npmtapi/embed";
import { onMounted, onUnmounted, ref } from "vue";
const props = defineProps({ organization: String, identifier: String });
const widgetRef = ref(null);
let tapipay = null;
onMounted(async () => {
tapipay = await loadTapipay();
tapipay.initialize({
container: widgetRef.value,
organization: props.organization,
identifier: props.identifier,
});
tapipay.on("paymentSubmitted", (data) => console.log("Pago enviado:", data));
});
onUnmounted(() => tapipay?.destroy(widgetRef.value));
</script>Angular
import { loadTapipay, TapipayInstance } from "@npmtapi/embed";
import { Component, ElementRef, Input, OnDestroy, OnInit, ViewChild } from "@angular/core";
@Component({
selector: "app-tapipay-widget",
template: `<div #container></div>`,
})
export class TapipayWidgetComponent implements OnInit, OnDestroy {
@Input() organization!: string;
@Input() identifier!: string;
@ViewChild("container", { static: true }) containerRef!: ElementRef<HTMLDivElement>;
private tapipay: TapipayInstance | null = null;
async ngOnInit() {
this.tapipay = await loadTapipay();
this.tapipay.initialize({
container: this.containerRef.nativeElement,
organization: this.organization,
identifier: this.identifier,
});
}
ngOnDestroy() {
this.tapipay?.destroy(this.containerRef.nativeElement);
}
}Opciones
loadTapipay(options?)
| Parámetro | Tipo | Requerido | Por defecto | Descripción |
|-----------|------|-----------|-------------|-------------|
| environment | 'production' \| 'homo' | No | 'production' | Entorno del SDK a cargar |
initialize(options)
Se requiere container + identifier + uno de organization o companyCode. Si se pasan ambos, companyCode tiene prioridad.
| Parámetro | Tipo | Requerido | Por defecto | Descripción |
|-----------|------|-----------|-------------|-------------|
| container | string \| HTMLElement | Sí | — | Selector CSS o referencia DOM |
| organization | string | Uno de los dos | — | Alias de la organización en TapiPay. Requerido si no se pasa companyCode |
| companyCode | string | Uno de los dos | — | Código de compañía en TapiPay. Alternativa a organization. Si se pasan ambos, companyCode tiene prioridad |
| identifier | string | Sí | — | Identificador del cliente (DNI, número de cuenta, etc.) |
| externalRequestId | string | No | — | ID externo de una deuda específica a preseleccionar. Tiene precedencia sobre selectionStrategy |
| selectionStrategy | "oldestCreated" | No | más reciente | Estrategia de selección automática de deuda pendiente. "oldestCreated": la más antigua. Por defecto (omitido): la más reciente |
| view | 'payments' \| 'autopay' | No | 'payments' | Vista a montar. 'autopay' muestra la gestión de adhesiones |
Eventos
// El popup de Yuno se abrió exitosamente (útil para mostrar un overlay mientras el usuario paga)
tapipay.on("paymentWindowOpened", function() {
console.log("Ventana de pago abierta");
// Mostrar overlay/spinner mientras el usuario completa el formulario
});
// El usuario completó el formulario de Yuno y fue redirigido al callback
tapipay.on("paymentSubmitted", function(data) {
console.log("Pago enviado:", data);
// Mostrar pantalla de "procesando" y esperar webhook
});
// El usuario cerró el popup sin completar el pago
tapipay.on("paymentCancelled", function() {
console.log("Pago cancelado por el usuario");
});
// Remover un listener
tapipay.off("paymentSubmitted", handler);TapipayPaymentWindowOpenedEvent
interface TapipayPaymentWindowOpenedEvent {
type: "paymentWindowOpened";
}TapipayPaymentSubmittedEvent
interface TapipayPaymentSubmittedEvent {
type: string; // "TAPIPAY_CALLBACK"
status: "pending";
data: Record<string, string>; // Parámetros que Yuno haya enviado (pueden estar vacíos)
}TapipayPaymentCancelledEvent
interface TapipayPaymentCancelledEvent {
// sin campos adicionales
}Vista de gestión de adhesiones (autopay)
Pasando view: "autopay" el widget monta la gestión de adhesiones: el usuario final ve su domiciliación (tarjeta o cuenta CLABE/bancaria) y puede desadherirse, sin entrar al portal completo.
Privacidad. Los eventos de autopay exponen solo lo mínimo: el tipo de medio (
mediaType) y los últimos 4 dígitos (lastFour). Elides un hash opaco del identificador interno. Nunca se exponen titular, número/CLABE completos, marca, banco ni tokens.
Script tag
<script src="https://app.tapipay.la/embed.js"></script>
<div id="autopay"></div>
<script>
tapipay.initialize({
container: "#autopay",
organization: "acme",
identifier: "CLI-001",
view: "autopay"
});
tapipay.on("autopayLoaded", function(data) {
console.log("Adhesiones activas:", data.count);
});
tapipay.on("autopayActivated", function(data) {
console.log("Nueva adhesión activa:", data.mediaType, data.lastFour);
});
tapipay.on("autopayRemoved", function(data) {
console.log("Medio desadherido:", data.mediaType, data.lastFour);
});
tapipay.on("autopayError", function(data) {
console.log("Error de autopay:", data.stage, data.message);
});
</script>React
import { TapipayWidget } from "@npmtapi/embed/react";
export function AdhesionesPage({ clienteId }: { clienteId: string }) {
return (
<TapipayWidget
view="autopay"
organization="acme"
identifier={clienteId}
onAutopayLoaded={(data) => console.log("Adhesiones activas:", data.count)}
onAutopayActivated={(data) => console.log("Nueva adhesión activa:", data.mediaType)}
onAutopayRemoved={(data) => console.log("Desadherido:", data.mediaType)}
onAutopayError={(data) => console.log("Error:", data.stage)}
/>
);
}Eventos de autopay
interface TapipayAutopayLoadedEvent {
count: number; // adhesiones activas
mediaTypes: Array<"card" | "bank_account">; // tipos presentes (sin duplicados)
adhesions: Array<{
id: string; // hash opaco
mediaType: "card" | "bank_account";
lastFour: string | null; // últimos 4 dígitos
}>;
}
interface TapipayAutopayActivatedEvent {
id: string; // hash opaco
mediaType: "card" | "bank_account";
lastFour: string | null; // últimos 4 dígitos
}
interface TapipayAutopayRemovedEvent {
id: string;
mediaType: "card" | "bank_account";
lastFour: string | null;
}
interface TapipayAutopayErrorEvent {
stage: "load" | "remove";
message: string; // neutral, sin PII
}
autopayLoadedse dispara al cargar y cada vez que la lista de adhesiones cambia (tras una desadhesión, refetch o polling), así que el host puede mantener su conteo consumiendo siempre el últimopayload.
autopayActivatedse dispara una sola vez por adhesión nueva, cuando una nueva domiciliación queda activa. A diferencia deautopayLoaded, no se repite en refetch ni polling.
Entorno de homologación
Para apuntar al entorno de homologación en lugar de producción:
Vía npm (loadTapipay)
import { loadTapipay } from "@npmtapi/embed";
const tapipay = await loadTapipay({ environment: "homo" });
tapipay.initialize({ container: "#widget", organization: "acme", identifier: "CLI-001" });Vía React
<TapipayWidget
environment="homo"
organization="acme"
identifier="CLI-001"
/>Vía script tag
Apunta directamente al script del entorno deseado:
<!-- Homologación -->
<script src="https://homo.tapipay.la/embed.js"></script>
<!-- Producción -->
<script src="https://app.tapipay.la/embed.js"></script>Nota: No se soporta cargar dos entornos distintos en la misma página, ya que
window.tapipayes un objeto global compartido.
