Skip to main content
Glama

Vaani Pay Assistant

Un chatbot de asistencia para pagos multiusuario, bilingüe (inglés + hindi), seguro y en tiempo real: los usuarios se registran/inician sesión con su propia cuenta y después preguntan por sus propios pagos, pedidos, reembolsos, transacciones, riesgo de fraude y estadísticas — con un estricto aislamiento de datos por usuario aplicado en la capa de herramientas MCP, una base de datos SQLite real y un flujo de estado WebSocket en vivo que muestra lo que el agente está haciendo.

Empezó como una construcción de hackatón de demostración estática (usuarios codificados, tokens fijos, solo inglés) y se ha actualizado a una plataforma basada en bases de datos, multiusuario, segura y bilingüe sin cambiar las partes funcionales que ya funcionaban: el protocolo WebSocket, la arquitectura de herramientas MCP y la lógica central del agente tienen la misma forma que antes — solo han cambiado la fuente de datos y el modelo de autenticación subyacentes.

Arquitectura

Browser (chat UI + login/signup/profile)
      │ REST (/auth, /users/me, /transactions)      │ WebSocket (/ws)
      ▼                                              ▼
FastAPI — auth endpoints, profile endpoints    FastAPI — WebSocket handler
      │                                              │
      ▼                                              ▼
app/auth.py  (register / login / sessions)     AI Agent (app/agent.py)
      │                                           NLU (Grok API) → intent + entities
      │                                           Tool selection → MCP tool
      ▼                                              │
app/db.py — SQLite                                   │ MCP (stdio transport)
  users, sessions, chat_history,                      ▼
  payments, orders, refunds, transactions        MCP Server (mcp_server/server.py)
      ▲                                           get_payment_status │ get_order_details
      │                                           get_refund_status │ get_customer_details
      └───────────── same DB, same ownership ──── get_transaction_history │ check_fraud_risk
                      checks on every query        get_payment_statistics
                                                        │
                                                        ▼
                                              mcp_server/data_layer.py
                                              — ownership check on every lookup,
                                                now backed by SQLite instead of JSON

Related MCP server: nexi-xpay-mcp-server

1. Cuentas y privacidad de datos

  • Cuentas reales. POST /auth/register crea una fila de usuario en SQLite con una contraseña hash segura (PBKDF2-HMAC-SHA256, sal aleatoria por contraseña, 260k iteraciones — ver app/security.py). Las contraseñas en texto plano nunca se almacenan ni se registran en ningún sitio.

  • Sesiones de inicio de sesión reales. POST /auth/login verifica las credenciales y emite un token de sesión opaco e imposible de adivinar (generate_token() de app/security.py, 256 bits de entropía) almacenado en la tabla sessions con una caducidad (SESSION_TTL_HOURS en .env, por defecto 24h). Los tokens caducados o desconocidos se rechazan en todos los puntos donde se comprueban.

  • Cada herramienta MCP que busca un recurso específico (get_payment_status, get_order_details, get_refund_status, check_fraud_risk) requiere un parámetro requesting_user_id y verifica, en mcp_server/data_layer.py, que el recurso pertenece realmente a ese usuario — ahora mediante una cláusula SQL parametrizada WHERE ... AND user_id = ? — antes de devolver nada.

  • Si el recurso pertenece a otra persona o no existe en absoluto, se devuelve la misma respuesta genérica en ambos casos: "Access denied. You are not authorized to access this information." Devolver mensajes diferentes para «no encontrado» frente a «datos de otra persona» permitiría a un usuario enumerar ID válidos observando qué error recibe — esto cierra ese canal auxiliar.

  • requesting_user_id es siempre la identidad autenticada de quien llama (resuelta una sola vez en el momento de la autenticación WebSocket / de la petición REST — ver app/auth.py), nunca un valor extraído de un mensaje de chat, un parámetro de URL o el cuerpo de la petición. El esquema de extracción de app/nlu.py no tiene ningún campo user_id, por lo que no hay manera de que un mensaje (ni siquiera uno malintencionado) cuele una identidad diferente en una llamada de herramienta.

  • get_customer_details, get_transaction_history y get_payment_statistics no reciben ningún ID de recurso — siempre devuelven los datos propios de quien llama, por lo que no hay ninguna superficie de manipulación de ID para estas tres herramientas.

  • Eliminación de la cuenta (DELETE /users/me) requiere volver a introducir la contraseña actual como confirmación y, a continuación, elimina la fila del usuario: las claves foráneas con ON DELETE CASCADE eliminan también todas las sesiones, el historial de chat, los pagos, los pedidos, los reembolsos y las transacciones de ese usuario.

Verifica el aislamiento de datos directamente:

python3 test_offline.py

2. Registro, inicio de sesión y gestión de cuentas

  • POST /auth/register — nombre, correo electrónico, contraseña, teléfono opcional y preferencia de idioma. Las contraseñas deben tener al menos 8 caracteres y contener una combinación de letras y números (app/security.py).

  • POST /auth/login — devuelve un token de sesión + perfil de usuario.

  • POST /auth/logout — revoca el token de sesión actual en el servidor.

  • GET /users/me / PUT /users/me — ver/actualizar el perfil (nombre, teléfono).

  • POST /users/me/change-password — requiere la contraseña actual; cambiarla invalida todas las sesiones existentes (obliga a iniciar sesión de nuevo en todas partes), de modo que un token antiguo filtrado deja de funcionar.

  • GET /users/me/preferences / PUT /users/me/preferences — leer/actualizar la preferencia de idioma (en/hi), persistida en la base de datos para que sobreviva al cierre/inicio de sesión.

  • DELETE /users/me — eliminación permanente de la cuenta (requiere contraseña + confirm: true explícito).

Todo esto también es accesible desde la propia interfaz de chat mediante el botón ⚙️ de la cabecera (ver/editar perfil, selector de idioma, cambiar contraseña, cerrar sesión, eliminar cuenta).

3. Interfaz de chat

static/index.html — una aplicación de una sola página:

  • Pestañas de Iniciar sesión / Registrarse que se muestran antes de que sea posible chatear.

  • Panel de Perfil y Configuración (edición de nombre/teléfono, cambio de contraseña, selector de idioma, cierre de sesión, eliminación de cuenta con un paso de confirmación).

  • Menú de sugerencias plegable sobre el campo de entrada, generado a partir del mismo diccionario de cadenas traducidas que el resto de la interfaz.

  • Burbujas de chat, línea de estado en vivo e indicador de estado en la cabecera — sin cambios respecto al diseño original.

4. Comunicación en tiempo real

El chat sigue realizándose a través de un único WebSocket (/ws) — la forma del protocolo no ha cambiado; solo el token de autenticación es ahora un token de sesión real respaldado por la base de datos en lugar de un valor estático:

{"type": "auth", "token": "<session token from /auth/login>"}
      ↓
{"type": "auth_success", "user_id": "...", "name": "...", "language": "en"}

Para cada mensaje de chat, el servidor transmite eventos de estado en este orden y, a continuación, la respuesta final (localizada):

🔍 Understanding your request...
🔧 Checking payment information...
✓ Payment information retrieved
🤖 Generating response...
<final answer, in the user's selected language>

Cada turno de chat (tanto los mensajes del usuario como los del asistente) también se persiste en la tabla chat_history (_persist_chat_turn de app/main.py), limitado al usuario autenticado.

5. Arquitectura basada en MCP

mcp_server/server.py expone exactamente estas 7 herramientas, divididas en módulos de dominio en mcp_server/tools/:

Herramienta

Archivo

get_payment_status

payment_tools.py

check_fraud_risk

payment_tools.py

get_order_details

order_tools.py

get_refund_status

refund_tools.py

get_customer_details

customer_tools.py

get_transaction_history

customer_tools.py

get_payment_statistics

analytics_tools.py

get_balance

wallet_tools.py

add_money

wallet_tools.py

get_transactions

wallet_tools.py

validate_recipient

wallet_tools.py

create_transfer

wallet_tools.py

confirm_transfer

wallet_tools.py

cancel_transfer

wallet_tools.py

get_spending_summary

wallet_tools.py

Todo respaldado por la base de datos SQLite (mcp_server/data_layer.pyapp/db.py) en lugar de JSON estático. Las firmas de las herramientas, el agente y el frontend no han cambiado con respecto al diseño original; solo ha cambiado la fuente de datos subyacente a data_layer.py, exactamente como la arquitectura original fue diseñada para permitir.

6. Soporte bilingüe (inglés + hindi)

  • Cadenas de la interfaz: el diccionario UI_STRINGS de app/i18n.py, servido a través de GET /i18n/{lang}. El frontend lo obtiene una vez al cargar y en cada cambio de idioma, y lo aplica mediante los atributos data-i18n/data-i18n-placeholder — ninguna cadena traducida está codificada de forma fija en el HTML/JS.

  • Respuestas del asistente de IA: AGENT_STRINGS de app/i18n.py (mensajes fijos como saludos) y REPLY_TEMPLATES (mensajes interpolados como el estado del pago). app/agent.py genera cada respuesta a través de estos — nada en el agente codifica texto en inglés directamente.

  • NLU: el prompt de app/nlu.py pide explícitamente al modelo Grok que procese entrada en hindi/inglés/mixta y que traduzca siempre internamente al inglés para la extracción de intenciones/entidades, de modo que el asistente entienda una pregunta en cualquier caso y responda en el idioma preferido del usuario.

  • Persistencia: la preferencia de idioma reside en users.language en la base de datos (se establece al registrarse y se puede cambiar en cualquier momento mediante PUT /users/me/preferences), de modo que sobrevive al cierre/inicio de sesión.

  • Cambio dinámico: cambiar el idioma en Configuración actualiza la interfaz inmediatamente y reconecta el WebSocket para que la siguiente respuesta de chat llegue ya en el nuevo idioma — sin necesidad de recargar la página.

7. Requisitos de seguridad

Requisito

Dónde se implementa

Autenticación

app/auth.py: hash de contraseñas, tokens de sesión con caducidad. WebSocket no procesará ningún mensaje de chat, y ningún endpoint REST devuelve datos, hasta que un token se verifica.

Autorización

mcp_server/data_layer.py: cada búsqueda de recursos filtra por user_id en el propio SQL.

Aislamiento de usuario/sesión

app/session_store.py: cada conexión WebSocket recibe su propio estado de conversación en memoria; user_id/language se establecen una vez en la autenticación y nunca se sobrescriben desde el texto del chat.

Comprobaciones de permisos a nivel de MCP

Se aplican dentro de las propias herramientas MCP (mcp_server/tools/*.pydata_layer.py), no solo en el borde de la aplicación; véanse diagnose_setup.py y test_offline.py, que llaman directamente al servidor MCP y confirman la denegación.

Validación de entrada

app/main.py (modelos Pydantic en cada cuerpo REST; comprobaciones de tipo/longitud de mensaje en WebSocket) y app/auth.py (formato de email, política de contraseñas).

Protección contra inyección SQL

Cada consulta en app/db.py / mcp_server/data_layer.py usa marcadores ? parametrizados; no hay SQL construido mediante cadenas en ningún sitio.

Límite de peticiones

app/main.py: limitador por IP con ventana deslizante en /auth/register y /auth/login.

CORS seguro

app/main.py: lista blanca explícita (CORS_ALLOWED_ORIGINS en .env), por defecto solo localhost; nunca * con credenciales.

Hash seguro de contraseñas

app/security.py: PBKDF2-HMAC-SHA256, sal aleatoria por contraseña, 260 000 iteraciones.

Caducidad de tokens

app/auth.py: las sesiones caducan tras SESSION_TTL_HOURS; cambiar la contraseña invalida todas las sesiones existentes.

Mensajes de error de autenticación genéricos

app/auth.py: mismo error para «no existe ese email» y «contraseña incorrecta»; mismo error para «no encontrado» y «recurso de otro usuario».

Gestión segura de errores

El _safe_error_message() de app/main.py / el controlador global de excepciones: los errores inesperados se registran completos en el servidor; el cliente solo recibe un mensaje genérico.

Protección contra la manipulación de IDs

Un usuario puede escribir cualquier payment_id/order_id/refund_id; la herramienta solo devuelve datos si pertenecen a su cuenta autenticada (aplicado por SQL).

8. Esquema de la base de datos

users            id, name, email, phone, password_hash, language, created_at, updated_at, last_login
sessions         token, user_id, created_at, expires_at
chat_history     id, user_id, conversation_id, role, message, timestamp
payments         payment_id, user_id, status, amount, method, failure_reason, date
orders           order_id, user_id, status, total, items (JSON), date
refunds          refund_id, user_id, payment_id, amount, status, date
transactions     txn_id, user_id, type, amount, status, date

-- Wallet: the real money-movement system (see section 9 below)
payment_accounts     id, user_id, payment_id, account_number, ifsc, balance, currency, status, created_at
wallet_transactions  id, transaction_id, sender_account_id, receiver_account_id, amount, transaction_type,
                     status, description, sender_name, receiver_name, recipient_account_number,
                     recipient_ifsc, failure_reason, created_at, updated_at
beneficiaries        id, user_id, recipient_name, account_number, ifsc, created_at

Véase SCHEMA en app/db.py para el DDL completo con claves foráneas e índices.

9. Monedero: cuentas de pago, añadir dinero y enviar dinero

Cada usuario registrado recibe un monedero real y utilizable, no solo un visor de historial de pagos. Es, con diferencia, la mayor incorporación sobre la actualización de base de datos/autenticación, y está construido como módulo propio (app/wallet.py) al que llaman tanto la API REST como las herramientas de IA/MCP, de modo que hay exactamente un lugar donde se aplican las reglas de movimiento de dinero.

Creación automática de cuenta. POST /auth/register crea la fila del usuario Y una fila de payment_accounts en la misma transacción de base de datos (véase register() en app/auth.py al llamar a insert_account_row() en app/wallet.py): un usuario nunca puede existir sin monedero, y un monedero nunca se crea como un paso separado que pueda fallar de forma independiente. Cada cuenta recibe:

  • un ID de pago único (PAY..., identificador interno),

  • un número de cuenta único de 12 dígitos,

  • un IFSC fijo (VPAY0000001 — Vaani Pay es un monedero virtual de una sola sucursal, por lo que todas las cuentas comparten un mismo IFSC, igual que suelen hacer las cuentas virtuales de un neobanco real),

  • un saldo inicial de ₹0.

La respuesta del registro incluye un mensaje: «Tu cuenta de pago se ha creado correctamente», que sirve de confirmación, además de los detalles de la nueva cuenta, mostrados al usuario de inmediato (tanto en la respuesta de la API como en el mensaje de confirmación de la pantalla de registro).

Añadir dinero. POST /wallet/add-money (o el botón «Añadir dinero» en la pantalla del monedero, o pedir al asistente de IA «añade ₹5,000 a mi cuenta») valida el importe (> ₹0, ≤ ₹2,00,000 por transacción — MAX_ADD_MONEY en app/wallet.py), y después actualiza el saldo de forma atómica y añade una fila CREDIT a wallet_transactions. No hay ninguna pasarela de pago real conectada en la versión del hackathon: se trata de una recarga explícitamente simulada, que cumple el requisito del brief de «flujo de fondos simulado seguro».

Enviar dinero: siempre una confirmación en dos pasos. Ni la API REST ni el asistente de IA mueven dinero nunca en una sola llamada:

  1. POST /wallet/transfers (initiate_transfer en app/wallet.py) valida el destinatario y el saldo del remitente, y crea una fila PENDING en wallet_transactionsaún no hay cambios de saldo. Devuelve una vista previa de confirmación (destinatario, número de cuenta enmascarado, IFSC, importe, comisión, débito total), que es lo que muestra la pantalla «Confirmar transferencia».

  2. POST /wallet/transfers/{id}/confirm (confirm_transfer) es la única llamada que mueve dinero de verdad. Vuelve a validar el saldo del remitente y el estado de la cuenta en el momento de confirmar (no solo en el momento de iniciar, por si algo ha cambiado entre medias — p. ej., dos transferencias iniciadas una detrás de otra), y después debita al remitente y, si el destinatario es una cuenta real de Vaani Pay, lo acredita, dentro de una única transacción atómica de SQLite protegida por un bloqueo a nivel de proceso. Si algo falla a mitad de camino, todo se revierte: una transferencia nunca puede terminar debitada pero no acreditada.

  3. POST /wallet/transfers/{id}/cancel cancela una transferencia aún PENDING sin tocar ningún saldo.

Enviar a un número de cuenta que no está en nuestro sistema también funciona (como transferencia externa simulada: se debita al remitente, pero no hay ninguna cuenta de Vaani Pay a la que acreditar), lo que cumple el requisito del brief de «acreditar el saldo del destinatario si el destinatario existe en el sistema simulado».

Validación del destinatario. POST /wallet/validate-recipient (validate_recipient) comprueba: el formato del número de cuenta (9–18 dígitos), el formato del IFSC (^[A-Z]{4}0[A-Z0-9]{6}$), que el IFSC coincida con el número de cuenta si es una cuenta interna y — algo crucial — que el remitente no se esté enviando dinero a su propio número de cuenta. Si solo se proporciona el nombre del destinatario (sin número de cuenta), busca ese nombre entre los beneficiarios guardados del propio solicitante y lo resuelve automáticamente si hay exactamente una coincidencia.

Beneficiarios guardados. Tras una transferencia correcta, la interfaz ofrece «¿Guardar este destinatario?» — POST /beneficiaries lo guarda solo para el usuario autenticado (nunca global ni compartido), de modo que la próxima vez el usuario (o el asistente de IA, cuando se le pida «envía ₹2,000 a Rahul») pueda resolver una transferencia solo con el nombre.

Historial de transacciones y filtros. GET /wallet/transactions?filter=... (all / add_money / sent / received / failed / pending): todo se calcula en vivo desde wallet_transactions, nunca está fijo en el código. La pestaña Historial de la pantalla del monedero y las consultas al asistente de IA «muestra mis transacciones del monedero» / «cuánto he gastado este mes» leen exactamente de la misma función (get_wallet_transactions / get_spending_summary en app/wallet.py).

El saldo siempre se deriva, nunca se establece directamente. A propósito, no existe ninguna función set_balance() en todo el código: las únicas formas de que cambie un saldo son como efecto secundario de add_money() o confirm_transfer(), que además añaden una fila inmutable a wallet_transactions en el mismo paso atómico. El frontend solo muestra lo que devuelve GET /wallet/account; no puede influir en ello.

Seguridad específica del monedero

Regla

Cómo se aplica

Un usuario nunca puede modificar su propio saldo directamente

Ninguna función pública establece el saldo excepto como efecto secundario de Add Money / confirm_transfer, ambas validan el importe y generan una fila de auditoría

Un usuario nunca puede modificar el saldo de otro usuario

Toda función de la cartera toma el user_id autenticado de quien llama y busca payment_accounts mediante WHERE user_id = ? — nunca mediante un id de cuenta proporcionado por el cliente

Un usuario nunca puede confirmar/cancelar la transferencia de otra persona

confirm_transfer/cancel_transfer verifican que la cuenta remitente de la transacción PENDING pertenezca al usuario que llama, usando la misma respuesta genérica de "no encontrado" tanto si la transacción no existe como si pertenece a otra persona (verificado en test_offline.py y diagnose_setup.py)

Las autotransferencias están bloqueadas

validate_recipient compara el número de cuenta del destinatario con el del remitente antes de permitir que la transferencia continúe

Los importes no pueden manipularse durante el proceso

El importe utilizado para debitar/acreditar realmente en el momento de confirm_transfer es el almacenado en la fila PENDING creada en el momento de initiate_transfer — nunca se vuelve a leer de la solicitud de confirmación

La IA no puede mover dinero sin confirmación explícita

Las herramientas MCP create_transfer/add_money nunca debitan/acreditan por sí solas; la máquina de estados de conversación de app/agent.py requiere una respuesta explícita de "sí" a una confirmación mostrada antes de llamar a confirm_transfer

Atomicidad

confirm_transfer ejecuta la comprobación de saldo + ambas actualizaciones de saldo + la actualización de estado dentro de una sola transacción SQLite (tx() de app/db.py), además de un bloqueo a nivel de proceso — consulte el docstring del módulo app/wallet.py

10. Flujo de pago bilingüe

La cartera es totalmente bilingüe, usando el mismo mecanismo app/i18n.py que el resto de la aplicación — Add Money, Send Money (los tres pasos), la pantalla de confirmación, los estados de las transacciones y cada respuesta de la IA sobre saldos/transferencias se renderizan a través de t()/tpl() — sin inglés hardcodeado en ningún lugar de app/wallet.py, app/agent.py o la interfaz de cartera de static/index.html. Por ejemplo, pedirle a la IA "Rahul ko ₹2,000 bhejo" (Hindi/Hinglish) recorre exactamente el mismo flujo de resolver → confirmar → ejecutar que la versión en inglés, con cada mensaje — incluida la pantalla de confirmación — renderizado en hindi.

Configuración

python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt

cp .env.example .env
# Add your Grok API key (get one at https://console.x.ai)

La base de datos se crea automáticamente en la primera ejecución (y, solo si está vacía, se precarga con dos usuarios de demostración — ver más abajo); no se requiere ningún paso de migración separado para el desarrollo local. Para crearla explícitamente con antelación:

python3 -m app.db

Verifique antes de tocar el navegador:

python3 diagnose_setup.py

Ejecución

uvicorn app.main:app --reload --port 8000

Abra http://localhost:8000. Regístrese para crear una cuenta nueva, o inicie sesión con una de las cuentas de demostración precargadas:

Email

Contraseña

ramesh@example.com

Demo@1234

priya@example.com

Demo@1234

Luego pruebe el menú de sugerencias, haga preguntas como "check payment status pay_1001" o "मेरा भुगतान pay_1001 का स्टेटस क्या है?", cambie de idioma desde Settings, o intente iniciar sesión como un usuario y preguntar por los IDs de pago/pedido/reembolso del otro usuario (pay_1003, ord_2002, rfnd_3002 pertenecen a Priya) para ver la respuesta de acceso denegado.

Para probar la cartera: abra el botón 💰 Wallet en el encabezado. Ambas cuentas de demostración comienzan con un saldo (₹8.500 para Ramesh, ₹8.000 para Priya) y una transferencia de demostración ya en su historial. Pruebe "Add Money", o "Send Money" al número de cuenta de la otra cuenta de demostración (visible en su propia pantalla de Wallet), o pregúntele directamente al asistente de IA: "what's my balance?", "add ₹5,000 to my account", "send ₹2,000 to Priya Stores" (la primera vez pedirá su número de cuenta + IFSC, y luego ofrecerá guardarla como beneficiaria después de una transferencia exitosa — después de eso, basta con su nombre), o "Mera current balance kitna hai?" en hindi.

F
license - not found
Not graded
quality - not tested
C
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Servers

  • A
    license
    Not graded
    quality
    A
    maintenance
    Enables AI agents to interact with Juspay's payment processing APIs and merchant dashboard for managing orders, transactions, refunds, customers, gateways, and reporting through natural language.
    21
    Apache 2.0
  • A
    license
    A
    quality
    D
    maintenance
    Enables AI assistants to query orders, transaction details, warnings/anomalies, and payment methods from your Nexi XPay merchant account.
    4
    MIT
  • A
    license
    A
    quality
    D
    maintenance
    Integrates Ant International's AlipayPlus payment APIs, enabling AI assistants to handle payment and refund operations seamlessly.
    6
    8
    MIT
  • F
    license
    Not graded
    quality
    C
    maintenance
    Enables AI to query a business database for customers, orders, and revenue using natural language through safe, well-defined tools.

View all related MCP servers

Related MCP Connectors

View all MCP Connectors

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/divyaupadhyay56/Vaani-Pay'

If you have feedback or need assistance with the MCP directory API, please join our Discord server