# 📘 Bill Payments Widgets ## 🌐Introducción Los widgets de Bill Payments permiten a cualquier Partner integrar rápidamente funcionalidades clave del ecosistema de pagos de servicios, como búsqueda de deudas, recargas móviles y tracking de transacciones. Su diseño modular permite integrar solo los componentes necesarios, reduciendo el esfuerzo de desarrollo y acelerando el time-to-market. --- ## 📦 ¿Qué es un widget? Un **widget** es un componente visual y funcional autónomo que se puede integrar fácilmente dentro de cualquier aplicación o sitio web. Actúa como una "mini-aplicación" embebida, que encapsula toda la lógica, interfaz y comportamiento necesarios para cumplir una tarea específica. En el contexto de Remitee, los widgets permiten a cualquier partner acceder a funcionalidades como: - Consulta y cotización de deudas - Recargas móviles o de otros servicios prepagos - Visualización y seguimiento de transacciones Nuestros widgets se caracterizan por ser: - **Autocontenidos**: no requieren lógica externa para funcionar. - **Personalizables**: se adaptan visualmente a la marca del partner. - **Reutilizables**: pueden insertarse en múltiples entornos (web, mobile, CMS). - **Extremadamente simples de integrar**: requieren solo unas pocas líneas de código. --- ## 🎯 Objetivo El sistema de widgets de Remitee tiene como objetivo: 1. **Acelerar el desarrollo e integración**: - Ofreciendo componentes listos para usar en Web, SPA o WebView. - Eliminando la necesidad de construir pantallas o manejar flujos complejos en el frontend o backend. 2. **Garantizar consistencia visual y operativa**: - Brindando una UI coherente y localizada, adaptable a través de temas CSS o parámetros. - Asegurando una experiencia white-label y alineada a la marca del partner. - Gestionando los recursos de idioma para múltiples países de forma automática, evitando nuevo desarrollo. 3. **Reducir riesgo y complejidad operativa**: - Centralizando la lógica de cotización y validaciones en los sistemas de Remitee. - Aislando al partner de requisitos regulatorios y de seguridad transaccional. --- ## 🧩 Widgets disponibles ### 🔍 Debt‑Search: Buscar y Cotizar Deuda Este widget es el punto de entrada para pagos de servicios. Permite: 1. Seleccionar el país donde se encuentra el servicio. 2. Buscar una empresa (ej. luz, agua, universidad). 3. Ingresar identificadores de cliente (ej. número de cuenta). 4. Consultar deudas disponibles. 5. Cotizar el monto a pagar (convertido si es necesario). 6. Emitir una cotización (quote), que es el último paso antes de generar el pago. > Ideal para habilitar pagos de servicios crossborder desde una misma aplicación. #### 🔄 Lógica de cotización La cotización puede obtenerse de dos formas: - ✅ **API interna de Remitee** (default): se usan tipos de cambio configurados automáticamente. - 🔄 **Endpoint del partner**: si el partner desea usar su propio tipo de cambio, puede definir un endpoint que será invocado por el widget. Esta lógica se maneja internamente, sin necesidad de código adicional en el frontend. ##### Visual del flujo de uso del widget: selección de país, búsqueda de servicio, identificación del cliente y visualización de deuda. <div style="display: flex; flex-wrap: wrap; gap: 12px; justify-content: center; align-items: flex-start; page-break-inside: avoid; break-inside: avoid;"> <img src="https://hackmd.io/_uploads/HyhychrZZl.png" alt="Paises" width="180"> <img src="https://hackmd.io/_uploads/H1pfq2BW-e.png" alt="Categorias" width="180"> <img src="https://hackmd.io/_uploads/Sk94qhSZWg.png" alt="Identificar cliente" width="180"> <img src="https://hackmd.io/_uploads/rkRL92B-Wg.png" alt="Buscar por tel" width="180"> <img src="https://hackmd.io/_uploads/ryVKqhHW-l.png" alt="Deuda encontrada" width="180"> <img src="https://hackmd.io/_uploads/Hy399hB-Zg.png" alt="Deuda encontrada" width="180"> </div> --- ### 📱 Top-Ups: Recargas instantáneas Diseñado para operaciones prepagas como: - Recarga de saldo móvil (airtime). - Tarjetas de regalo (gift cards). - Planes eSIM. El widget permite: 1. Seleccionar país y producto. 2. Cotizar en moneda local o destino. 3. Enviar automáticamente al `Checkout` tras el `on-complete`. > Soporta recargas internacionales (crossborder) desde múltiples países. --- ### 🔁 Tracking: Estado de transacciones El widget `Tracking` muestra al usuario el **historial de transacciones**, junto con el estado actual de cada una. - Basado en el `userId` configurado. - Consulta la base de datos de operaciones de Remitee. - Ideal para autogestión, soporte o seguimiento post-pago. ##### Visual del flujo de uso del widget: Visualización de historial de movimientos y detalle de movimiento. <div style="display: flex; flex-wrap: wrap; gap: 12px; justify-content: center; align-items: flex-start; page-break-inside: avoid; break-inside: avoid;"> <img src="https://hackmd.io/_uploads/ryaRePLGee.png" alt="Historial" width="150"> <img src="https://hackmd.io/_uploads/SykzZP8Mll.png" alt="Detalle" width="150"> </div> --- ## 💡 Introducción al flujo completo de BillPayments El ecosistema de widgets de Remitee permite a cualquier partner ofrecer pagos de servicios, recargas y suscripciones de forma integrada y simple, reduciendo tiempo de desarrollo y asegurando una experiencia unificada. Este flujo combina tres funcionalidades clave: - **Consulta y cotización de pagos** mediante los widgets `DebtSearch` o `Top-Ups`. - **Ejecución del pago** por parte del partner, usando el `quote` generado. - **Seguimiento de operaciones** mediante el widget `Tracker`. La solución está pensada para integrarse fácilmente en aplicaciones móviles, web, SPAs o WebView. ![Screenshot 2025-06-09 at 2.26.51 PM](https://hackmd.io/_uploads/S1tPY54mxg.png) --- ## 🔁 Resumen del Flujo Completo ### 1. 🧾 `DebtSearch` o `TopUps`: Generación del `quote` Estos widgets permiten al usuario consultar servicios pendientes de pago, recargas o suscripciones, y generan un objeto de cotización (`quote`) que encapsula toda la información necesaria para realizar el pago. - El usuario selecciona país, servicio y proveedor. - Ingresar el dato de búsqueda en caso que corresponda (número de cuenta, teléfono, etc.). - Muestra el detalle del servicio y el monto a pagar. - Al presionar continuar, genera un `quote` . #### 📤 Evento emitido: `on-complete` Al finalizar la selección y confirmar, el widget emite un evento `on-complete(quote)` con un `quote` que incluye todos los datos necesarios para autorizar y procesar el pago. #### 📦 Ejemplo de `quote` generado ```json { "id": "6838b07d2c4e0ffe111d9d52", "companyCode": "U1ZDLUNPLVMtMDAxMzYtQ09M", "companyName": "Claro Colombia", "productType": "SERVICE", "externalClientId": "user123", "targetCurrency": "COP", "targetCountry": "COL", "sourceCurrency": "ARS", "amount": { "targetAmount": 9000, "costAmount": 4.29 }, "costCurrency": "USD", "sourceValue": 3700, "expirationDate": "2025-05-29T19:17:41.862Z", "exchangeRate": 0.0004762823 } ``` > Este objeto puede ser almacenado, autorizado por el partner (ej: validando el saldo del usuario), y luego enviado a la API de Remitee para ejecutar el pago. > 🔄 **Evento emitido:** `on-complete(quote)` 🧾 Explicación de campos clave | Campo | Descripción | | --- | --- | | `id` | ID único de la cotización generada. | | `companyCode` / `companyName` | Compañía seleccionada para la operación. | | `productType` | Tipo de producto (`SERVICE`, `RECHARGE`, `VOUCHER`). | | `externalClientId` | Identificador del usuario final. | | `targetCurrency` | Moneda del servicio a pagar. | | `targetCountry` | País donde se presta el servicio. | | `sourceCurrency` | Moneda configurada del usuario (header `user-currency`). | | `amount.targetAmount` | Monto expresado en la moneda destino. | | `amount.costAmount` | Monto que se debitará de la cuenta del partner en la moneda definida por `costCurrency`. Este valor es el que debe usarse para empujar el pago contra `/payments`. | | `costCurrency` | Moneda de la cuenta del partner desde la cual se hará el débito. | | `sourceValue` | Monto equivalente en moneda del usuario, si se requiere mostrarlo o auditarlo. | | `expirationDate` | Vencimiento del quote. | | `exchangeRate` | Tasa de cambio utilizada. | --- #### 💳 Lógica de uso de `costAmount` y `costCurrency` El `costAmount` representa el valor que será **debitado de la cuenta del partner** para ejecutar el pago, y siempre está expresado en la moneda `costCurrency`. - Si la moneda del usuario (`user-currency`, tomada del header) **coincide** con la moneda del servicio (`targetCurrency`), el `costCurrency` será esa misma y el monto será el mismo que el `targetValue`. - Si son **distintas**, se cobrará en la cuenta del partner en USD, utilizando la tasa de cambio configurada. En ese caso, el `costCurrency` será `"USD"` y el `costAmount` será el valor equivalente en dólares. > 🧠 Este valor (`costAmount` en `costCurrency`) es el que se debe enviar posteriormente al endpoint `POST /payments` para ejecutar la operación. --- ### 2. 💳 Ejecución del pago por el Partner Una vez autorizado el `quote`, el partner debe realizar la llamada al endpoint de pago correspondiente en la API de Remitee, dependiendo del tipo de operación: #### 🧾 Endpoints disponibles | Tipo | Endpoint | | --- | --- | | Servicios | `POST /services/quotes/{id}/payments` | | Recargas | `POST /topups/recharges/quotes/{id}/payments` | | Suscripciones | `POST /topups/vouchers/quotes/{id}/payments` | #### 📤 Ejemplo de Request **POST** `/services/quotes/{id}/payments` ```json { "externalReferenceId": "mbpe521kwd71rclut2", "externalPaymentMethod": "DEBIT", "amount": 9000, "costAmount": 4.29 } ``` > `id` ID de la cotización. > `externalReferenceId` Valor generado aleatoriamente. > `externalPaymentMethod` es un identificador que representa cómo fue pagado, valores aceptados: DEBIT, ACCOUNT, or CREDIT. > `amount` Valor recibido en quote "amount.targetAmount". > `costAmount` Valor recibido en quote "amount.costAmount". #### ✅ Respuesta esperada ```json { "id": "68472278a436072416dd609b", "createdAt": "2025-06-09T18:05:47.1093505Z", "settledAt": "2025-06-09T18:05:44.9555138Z", "reversedAt": "2025-06-09T18:05:44.9555138Z", "status": "SETTLED", "companyCode": "U1ZDLUNPLVMtMDAxMzYtQ09M", "externalClientId": "user123", "externalPaymentMethod": "DEBIT", "externalReferenceId": "mbpekjeyfskqybyi8iv", "currency": "COP", "costCurrency": "USD", "amount": { "sourceValue": 9000, "costAmount": 4.29 }, "activatePIN": "xxxxxxx", "company": { "code": "U1ZDLUNPLVMtMDAxMzYtQ09M", "name": "Agua", "logo": "https://www.xxx.com/logo?code=KDRaVBxXSgFUARADBwQDJwoeL25eVgAJS1pFBBsDD0lNJwoMJG9PVkAZBF5NWgIBCQ", "country": "COL" }, "exchangeRate": 0.00047628233063995673, } ``` #### 📡 Webhook para cambios de estado Es posible configurar un **webhook** para recibir **notificaciones automáticas** cuando el estado de un pago cambia. Esto permite a los sistemas del partner reaccionar en tiempo real sin necesidad de consultar periódicamente los endpoints como `/payments/{id}`. #### 🔔 Notificaciones disponibles: - Estado final del pago: - `COMPLETED` - `REVERSED` - `REVOKED` Este mecanismo mejora la eficiencia e integración con backends externos que necesitan conocer con precisión cuándo un pago fue completado, cancelado o revertido. 🛠 **Para configurar tu webhook**, contactá al equipo técnico de Remitee o a tu ejecutivo de cuenta. ### 3. 📦 `Tracker`: Seguimiento del Pago - El partner puede lanzar el widget `Tracker` para mostrar el estado de la transacción. - Se le pasa el `id` recibido en el paso anterior como `paymentId` . ```jsx <rmt-tracker userId="user-001" paymentId="68472278a436072416dd609b" /> ``` Ejemplo: <div style="display: flex; flex-wrap: wrap; gap: 12px; justify-content: center; align-items: flex-start; page-break-inside: avoid; break-inside: avoid;"> <img src="https://hackmd.io/_uploads/Hy-dRPpUlg.png"> </div> --- - El widget consulta el estado (SETTLED, PROCESSING, COMPLETED, REVERSED o REVOKED) - Muestra todos los detalles al usuario: empresa, monto, estado, fechas, etc. #### ✅ **Opcional:** si no se pasa un `paymentId`, el widget muestra el historial de pagos del cliente. --- ## 🧱 Modularidad y simplicidad Todos los widgets de Remitee son: - **Autónomos**: funcionan por sí solos. - **Modulares**: pueden combinarse en flujos completos, o usarse por separado. - **Integrables con lógica propia del partner**: el partner puede encargarse del cobro, autorización y gestión de usuarios. Esto permite adaptar la experiencia de pago a distintos niveles de complejidad técnica y de negocio, sin comprometer la seguridad ni la experiencia del usuario. --- ## ⚡ Props y Eventos ### ¿Qué son? Los widgets aceptan **props** (parámetros de configuración) y emiten **eventos** para comunicar estados clave como cotizaciones listas, resultados de pagos o salidas del flujo. Esto permite integrarlos sin acoplarlos a la lógica interna de la app. --- ### Props comunes | Prop | Tipo | Descripción | | --- | --- | --- | | `clientId` | `string` | Identificador del partner. Se envía en el header. Requerido. | | `userId` | `string` | ID del usuario final. Se envía en el header. Requerido. | | `userCurrency` | `string` | Moneda origen desde la cual se operará (ej: `"ARS"`, `"USD"`). Se envía en el header. Requerido. | | `theme` | `ThemeProps` | Colores, bordes, fuentes, modo dark/light, etc. | | `plain` | `boolean` | Si `true`, se omite el contenedor tipo card. | --- ### Props específicas #### 🧾 DebtSearch | Prop | Tipo | Descripción | | --- | --- | --- | | `userCountry` | `string` | País de origen de la operación (ISO3). Requerido. | | `showReminder?` | `boolean` | Muestra u oculta la agenda de servicios guardados. | | `on-complete` | `(quote) => void` | Retorna cotización lista para autorizar. | | `on-exit` | `() => void` | Evento emitido al salir del widget (ej: cerrar o volver atrás). | --- #### ⚡ TopUps | Prop | Tipo | Descripción | | --- | --- | --- | | `userCountry` | `string` | País de origen de la operación (ISO3). Requerido. | | `module?` | `string[]` | Opcional, en caso de querer habilitar uno o varios modulos: `'VOUCHER', 'RECHARGE'`. Por defecto, se muestran todos los modulos. | | `on-complete` | `(quote) => void` | Retorna cotización lista para autorizar. | | `on-exit` | `() => void` | Evento emitido al salir del widget. | --- #### 🔎 Tracker | Prop | Tipo | Descripción | | --- | --- | --- | | `paymentId?` | `string` | Si se indica, se abre directamente el detalle de esa transacción. | | `on-exit` | `() => void` | Evento emitido al salir del widget. | --- ### Eventos | Widget | Eventos emitidos | | --- | --- | | Debt‑Search | `on-complete(quote)` – Retorna cotización lista para autorizar.<br>`on-exit()` – Se produce el cierre/salida del widget. | | Top‑Ups | `on-complete(quote)` – Retorna cotización lista para autorizar.<br>`on-exit()` – Se produce el cierre/salida del widget. | | Tracker | `on-exit()` – Se produce el cierre/salida del widget. | --- #### 🎨 Personalización de Tema (`theme`) Todos los widgets permiten aplicar un tema visual mediante la prop `theme`, que acepta una serie de propiedades opcionales para adaptar la experiencia visual al estilo del partner o aplicación. Podés controlar: | Propiedad | Tipo | Descripción | | --- | --- | --- | | `mode` | `"light"` \\| `"dark"` | Modo general del widget: claro u oscuro. | | `typography` | `string` | Fuente principal del texto (ej: `'Roboto'`, `'Inter'`, `'Urbanist'`, `'Fredoka'`). Debe estar disponible en el DOM. | | `primaryColor` | `string` | Color primario del widget (ej: botones, títulos). Ej: `#ff5500` | | `primaryContrastColor` | `string` | Color del texto sobre el color primario. Ej: `#ffffff` | | `secondaryColor` | `string` | Color secundario (íconos, acentos). | | `secondaryContrastColor` | `string` | Color del texto sobre el secundario. | | `borderWidth` | `number` | Grosor general de bordes (en px). | | `borderColor` | `string` | Color de los bordes generales. | | `borderRadius` | `number` | Radio de borde general (en px). | | `inputBorderWidth` | `number` | Grosor del borde de los inputs. | | `inputBorderColor` | `string` | Color del borde de los inputs. | | `inputBorderRadius` | `number` | Radio de borde de los inputs. | | `buttonBorderRadius` | `number` | Radio de borde de los botones. | | `bgColor` | `string` | Color de fondo general del widget. | | `fgColor` | `string` | Color del texto general. | | `shadow` | `string` | Sombra aplicada a tarjetas o contenedores. Ej: `'0 2px 6px rgba(0,0,0,0.1)'` | --- ## 🔧 Formas de integrar widgets ### 1. **React (con `rmt-base`)** Para SPAs en React: ```jsx import { DebtSearch, TopUps, Tracker } from 'rmt-base'; <DebtSearch clientId="fa6097cd-90ea-4a56-a901-1d3ce18fb386" userId="u123" userCurrency="ARS" userCountry="ARG" onComplete={(quote) => console.log('Cotización:', quote)} onExit={() => console.log('Widget cerrado')} theme={{ '--rmt-primary': '#ff5500' }} /> <TopUps clientId="fa6097cd-90ea-4a56-a901-1d3ce18fb386" userId="u123" userCurrency="ARS" userCountry="ARG" modules={['RECHARGE', 'VOUCHER']} onComplete={(quote) => console.log('Recarga:', quote)} onExit={() => console.log('TopUps cerrado')} theme={{ '--rmt-primary': '#ff5500' }} /> <Tracker clientId="fa6097cd-90ea-4a56-a901-1d3ce18fb386" userId="u123" userCurrency="USD" paymentId="687f9030655c12882a0a6d16" onExit={() => console.log('Volvió del tracker')} theme={{ '--rmt-primary': '#ff5500' }} /> ``` --- ### 2. **HTML (Web simple o CMS con Custom Elements)** ```jsx <rmt-debt-search user-id="u123" user-country="ARG" ></rmt-debt-search> <script> rmt.id = 'fa6097cd-90ea-4a56-a901-1d3ce18fb386'; rmt.userCurrency = 'ARS'; rmt.theme = { 'primary-color': '#ff5500' }; document .querySelector('rmt-debt-search') .addEventListener('on-complete', (e) => console.log('Quote listo:', e.detail)); document .querySelector('rmt-debt-search') .addEventListener('on-exit', () => console.log('Usuario salió del widget')); </script> ``` --- ### 3. **Iframe embebido** Ideal para portales donde no se puede instalar scripts directamente: ```jsx <iframe src="https://widgets.remitee.com/debt-search?clientId=fa6097cd-90ea-4a56-a901-1d3ce18fb386&userId=u123&userCountry=ARG" width="100%" height="600" style="border: none;" ></iframe> <script> window.addEventListener('message', (event) => { const { event: eventName, payload } = JSON.parse(event.data); if (eventName === 'on-complete') { console.log('Cotización:', payload); } if (eventName === 'on-exit') { console.log('Usuario cerró el widget'); } }); </script> ``` --- ### 4. **WebView en app móvil** Usás el mismo iframe dentro de un componente WebView (React Native, Flutter, etc.): **React Native (ejemplo con `react-native-webview`)**: ```jsx <WebView source={{ uri: 'https://widgets.remitee.com/debt-search?clientId=fa6097cd-90ea-4a56-a901-1d3ce18fb386&userId=u123&userCountry=ARG', headers: { 'User-Currency': 'ARS' } }} onMessage={(event) => { const data = JSON.parse(event.nativeEvent.data); if (data.event === 'on-complete') { console.log('Quote:', data.payload); } if (data.event === 'on-exit') { console.log('Salida del widget'); } }} /> ``` --- ## 📊 Dashboard administrativo para partners Los partners disponen de un portal web de administración diseñado para brindar trazabilidad, control y autonomía sobre todas las operaciones relacionadas a los widgets integrados. Este dashboard es ideal para los equipos de operaciones, finanzas, soporte y cumplimiento. ### Funcionalidades clave: - 💳 **Consulta de saldo disponible** en tiempo real. - 🔢 **Historial de transacciones** con estados detallados (pagada, procesando, rechazada). - ⚖️ Herramientas para conciliación contable y seguimiento operativo. - ⚡ Filtros por rango de fechas y tipo de servicio. ![Screenshot 2025-07-22 at 5.49.24 PM](https://hackmd.io/_uploads/r1mPY_pLee.png) ---