Skip to main content
Glama

Vaani Pay Assistant

一个安全、实时、多用户**双语(英语 + 印地语)**的支付支持聊天机器人:用户使用自己的账户注册/登录,然后可以咨询自己的支付、订单、退款、交易、欺诈风险和统计数据——通过MCP工具层强制执行严格的每用户数据隔离,使用真实的SQLite数据库,并通过实时WebSocket状态流展示代理正在做什么。

该项目最初是黑客松的静态演示构建(硬编码用户、固定令牌、仅支持英语),现已升级为一个数据库驱动、多用户、安全、双语的平台,且没有改变那些已经正常工作的部分:WebSocket协议、MCP工具架构和核心代理逻辑与之前完全相同——只是底层的数据源和认证模型发生了变化。

架构

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. 账户与数据隐私

  • 真实账户。 POST /auth/register 在SQLite中创建一个用户行,密码经过安全哈希处理(PBKDF2-HMAC-SHA256,每个密码独立的随机盐,26万次迭代——参见 app/security.py)。明文密码永远不会存储或记录在任何地方。

  • 真实登录会话。 POST /auth/login 验证凭据,并颁发一个不透明、不可猜测的会话令牌(app/security.pygenerate_token(),256位熵),存储在 sessions 表中并带有过期时间(.env 中的 SESSION_TTL_HOURS,默认为24小时)。过期或未知的令牌在所有检查处都会被拒绝。

  • 每个查找特定资源的MCP工具get_payment_statusget_order_detailsget_refund_statuscheck_fraud_risk)都需要一个 requesting_user_id 参数,并在 mcp_server/data_layer.py 中验证该资源确实属于该用户——现在通过参数化SQL WHERE ... AND user_id = ? 子句——然后才返回任何内容。

  • 如果资源属于其他人,或者根本不存在,两种情况都会返回相同的通用响应:"Access denied. You are not authorized to access this information." 如果对"未找到"和"他人的数据"返回不同的消息,用户就可以通过观察收到哪个错误来枚举有效ID——这关闭了该侧信道。

  • requesting_user_id 始终是调用方的已认证身份(在WebSocket认证时/REST请求时一次性解析——参见 app/auth.py),绝不是从聊天消息、URL参数或请求体中解析出来的值。app/nlu.py 的提取模式中根本没有 user_id 字段,因此任何消息(即使是对抗性的)都无法将其他身份偷偷塞入工具调用中。

  • get_customer_detailsget_transaction_historyget_payment_statistics 完全不接受资源ID——它们始终返回调用方自己的数据,因此这三个工具完全没有任何ID操纵的攻击面。

  • 账户删除DELETE /users/me)需要重新输入当前密码作为确认,然后删除该用户行——ON DELETE CASCADE 外键会随之删除该用户的所有会话、聊天记录、支付、订单、退款和交易。

直接验证数据隔离:

python3 test_offline.py

这会以两个不同的演示用户身份运行真实的工具调用(针对实际的MCP服务器,从真实的SQLite数据库读取数据),并断言:跨用户访问尝试会被拒绝、每个用户的交易历史只包含自己的数据、双语回复能正确渲染。

2. 注册、登录与账户管理

  • POST /auth/register — 姓名、邮箱、密码、可选电话,以及语言偏好。密码必须至少8个字符,并且包含字母和数字的组合(app/security.py)。

  • POST /auth/login — 返回一个会话令牌 + 用户资料。

  • POST /auth/logout — 在服务端撤销当前会话令牌。

  • GET /users/me / PUT /users/me — 查看/更新资料(姓名、电话)。

  • POST /users/me/change-password — 需要当前密码;更改后会使所有现有会话失效(强制所有地方重新登录),因此泄露的旧令牌将不再有效。

  • GET /users/me/preferences / PUT /users/me/preferences — 读取/更新语言偏好(en/hi),持久化存储在数据库中,因此在退出/登录后仍然保留。

  • DELETE /users/me — 永久删除账户(需要密码 + 显式的 confirm: true)。

所有这些功能也可以通过聊天界面本身顶部的⚙️按钮访问(资料查看/编辑、语言切换、更改密码、退出登录、删除账户)。

3. 聊天界面

static/index.html — 一个单页应用:

  • 在可以进行任何聊天之前,会显示登录/注册选项卡。

  • 资料与设置面板(姓名/电话编辑、更改密码、语言切换、退出登录、带确认步骤的账户删除)。

  • 输入框上方可折叠的建议菜单,使用与界面其余部分相同的翻译字符串字典渲染。

  • 聊天气泡、实时状态行和顶部状态指示器——与原始设计保持一致。

4. 实时通信

聊天仍然通过单个WebSocket(/ws)进行——协议形态不变,只是认证令牌现在是一个由数据库支持的真实会话令牌,而不是静态值:

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

对于每条聊天消息,服务器按以下顺序流式发送状态事件,然后给出最终的(本地化的)答案:

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

每一轮聊天(用户消息和助手消息)也会持久化到 chat_history 表中(app/main.py_persist_chat_turn),作用域限定为已认证用户。

5. 基于MCP的架构

mcp_server/server.py 正好公开了这7个工具,拆分为 mcp_server/tools/ 下的领域模块:

工具

文件

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

所有这些都由SQLite数据库(mcp_server/data_layer.pyapp/db.py)支持,而不是静态JSON。工具签名、代理和前端与原始设计保持一致——只有 data_layer.py 底层的数据源发生了变化,这正是原始架构设计所允许的。

6. 双语支持(英语 + 印地语)

  • 界面字符串app/i18n.pyUI_STRINGS 字典,通过 GET /i18n/{lang} 提供。前端在加载时以及每次语言切换时获取一次,并通过 data-i18n/data-i18n-placeholder 属性应用——HTML/JS中没有硬编码任何翻译字符串。

  • AI助手回复app/i18n.pyAGENT_STRINGS(固定消息,如问候语)和 REPLY_TEMPLATES(插值消息,如支付状态)。app/agent.py 通过它们渲染每一条回复——代理中没有直接硬编码的英文文本。

  • NLUapp/nlu.py 的提示词明确要求Grok模型处理印地语/英语/混合输入,并在内部始终翻译为英语以进行意图/实体提取,因此助手无论用哪种语言提问都能理解问题,并以用户偏好的语言回复。

  • 持久化:语言偏好存储在数据库的 users.language 字段中(注册时设置,可随时通过 PUT /users/me/preferences 更改),因此在退出/登录后仍然保留。

  • 动态切换:在设置中更改语言会立即更新界面并重新连接WebSocket,因此下一条聊天回复就会以新语言返回——无需重新加载页面。

7. 安全要求

需求

实现位置

身份验证

app/auth.py —— 密码哈希、带过期时间的会话令牌。在令牌验证通过之前,WebSocket 不会处理任何聊天消息,也没有任何 REST 端点返回数据。

授权

mcp_server/data_layer.py —— 所有资源查询都在 SQL 本身中按 user_id 过滤。

用户/会话隔离

app/session_store.py —— 每个 WebSocket 连接都有自己独立的内存对话状态;user_id/language 在身份验证时设置一次,之后永远不会被聊天文本覆盖。

MCP 级权限检查

在 MCP 工具内部强制执行(mcp_server/tools/*.pydata_layer.py),而不仅仅在应用边缘执行——请参阅 diagnose_setup.pytest_offline.py,它们直接调用 MCP 服务器并确认拒绝。

输入验证

app/main.py(每个 REST 请求体上的 Pydantic 模型;WebSocket 上的消息类型/长度检查)和 app/auth.py(电子邮件格式、密码策略)。

SQL 注入防护

app/db.py / mcp_server/data_layer.py 中的每个查询都使用参数化的 ? 占位符——任何地方都没有字符串拼接的 SQL。

速率限制

app/main.py —— 对 /auth/register/auth/login 按 IP 进行滚动窗口限流。

安全的 CORS

app/main.py —— 显式白名单(.env 中的 CORS_ALLOWED_ORIGINS),默认仅限 localhost;绝不使用带凭据的 *

安全的密码哈希

app/security.py —— PBKDF2-HMAC-SHA256,每个密码使用随机盐,260k 次迭代。

令牌过期

app/auth.py —— 会话在 SESSION_TTL_HOURS 后过期;修改密码会使所有现有会话失效。

通用的身份验证错误消息

app/auth.py —— 对于“没有此电子邮件”和“密码错误”返回相同的错误;对于“未找到”和“他人的资源”也返回相同的错误。

安全的错误处理

app/main.py_safe_error_message() / 全局异常处理程序——意外错误会在服务器端完整记录,客户端只会收到一条通用消息。

防止 ID 篡改

用户可以输入任何 payment_id/order_id/refund_id——工具只有在数据属于其已认证账户时才会返回(由 SQL 强制执行)。

8. 数据库模式

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

要获取包含外键和索引的完整 DDL,请参阅 app/db.pySCHEMA

9. 钱包:支付账户、充值与转账

每个注册用户都会得到一个真实、可用的钱包——而不仅仅是一个交易历史查看器。这是在数据库/身份验证升级之上最大的一项新增功能,它作为独立模块(app/wallet.py)构建,REST API 和 AI/MCP 工具都会调用它,因此只有这一处地方强制执行资金移动规则。

自动创建账户。 POST /auth/register同一个数据库事务中创建用户行和 payment_accounts 行(参见 app/auth.pyregister() 调用 app/wallet.pyinsert_account_row())——用户不可能在没有钱包的情况下存在,钱包也绝不会作为一个独立的、可单独失败的步骤被创建。每个账户都会获得:

  • 一个唯一的支付 IDPAY...,内部标识符),

  • 一个唯一的 12 位账号

  • 一个固定的IFSCVPAY0000001 —— Vaani Pay 是一个单分支虚拟钱包,所以每个账户共享同一个 IFSC,就像真正的数字银行的虚拟账户通常那样),

  • 起始余额为 ₹0

注册响应中会包含一条 message: "Your payment account has been successfully created." 确认消息,以及新的账户详情,这些信息会立即显示给用户(既出现在 API 响应中,也出现在注册页面的确认消息中)。

充值。 POST /wallet/add-money(或钱包界面中的“充值”按钮,或要求 AI 助手“向我的账户充值 ₹5,000”)——校验金额(> ₹0,每笔交易 ≤ ₹2,00,000 —— app/wallet.py 中的 MAX_ADD_MONEY),然后以原子方式更新余额,并向 wallet_transactions 追加一行 CREDIT。hackathon 版本没有接入真正的支付网关——这是一个明确模拟的充值,符合需求文档中“安全的模拟资金流转”的要求。

转账——始终需要两步确认。 REST API 和 AI 助手都不会在一次调用中就移动资金:

  1. POST /wallet/transfersapp/wallet.py 中的 initiate_transfer)校验收款人和发送者的余额,并创建一行 PENDING 状态的 wallet_transactions ——暂不变动余额。它会返回一个确认预览(收款人、掩码后的账号、IFSC、金额、手续费、总扣款)——这就是“确认转账”界面所渲染的内容。

  2. POST /wallet/transfers/{id}/confirmconfirm_transfer)是唯一真正移动资金的调用。它会在确认时重新校验发送者的余额和账户状态(而不仅仅在发起时校验,以防期间发生变化——例如连续发起两笔转账),然后在由进程级锁保护的一个原子 SQLite 事务中,扣减发送者;如果收款人是真实的 Vaani Pay 账户,则同时为其入账。如果中途任何一步失败,整个事务都会回滚——一笔转账永远不会出现“已扣款但未入账”的情况。

  3. POST /wallet/transfers/{id}/cancel 取消一笔仍处于 PENDING 状态的转账,且不会影响任何余额。

向系统中不存在的账号转账仍然会成功(作为一次模拟的外部转账——发送者被扣款,只是没有 Vaani Pay 账户可入账),这符合需求文档中“如果收款人在模拟系统中存在,则为其余额入账”的要求。

收款人校验。 POST /wallet/validate-recipientvalidate_recipient)会检查:账号格式(9–18 位数字)、IFSC 格式(^[A-Z]{4}0[A-Z0-9]{6}$)、如果收款人是内部账户则其 IFSC 必须与账号匹配,以及——关键的一点——发送者不能向自己的账号转账。如果只提供了收款人姓名(没有账号),则会在调用者自己保存的收款人列表中查找该姓名,如果恰好只有一个匹配项,就自动解析出来。

已保存的收款人。 转账成功后,UI 会提供“保存此收款人?”—— POST /beneficiaries 仅将收款人保存给已认证用户本人(绝不做全局/共享存储),这样下次用户(或 AI 助手,当被要求“给 Rahul 转账 ₹2,000”时)就可以仅凭姓名完成转账。

交易历史与筛选。 GET /wallet/transactions?filter=...all / add_money / sent / received / failed / pending)——全部实时从 wallet_transactions 计算得出,绝不硬编码。钱包界面的“历史”标签页和 AI 的“显示我的钱包交易”/“我这个月花了多少钱”都读取完全相同的函数(app/wallet.py 中的 get_wallet_transactions / get_spending_summary)。

余额始终是派生出来的,绝不会被直接设置。 代码库中有意不提供任何 set_balance() 函数——余额发生变化的唯一方式是通过 add_money()confirm_transfer() 的副作用,而这两者都会在同一个原子步骤中追加一行不可变的 wallet_transactions。前端只会显示 GET /wallet/account 返回的内容;它无法影响余额。

钱包安全(具体说明)

规则

执行方式

用户永远不能直接修改自己的余额

除了 Add Money / confirm_transfer 的副作用之外,没有任何公共函数会设置余额;两者都会对金额进行校验,并生成一条审计记录

用户永远不能修改其他用户的余额

每个钱包函数都会获取调用者认证后的 user_id,并通过 WHERE user_id = ? 查询 payment_accounts——绝不使用客户端提供的账户 id

用户永远不能确认/取消他人的转账

confirm_transfer/cancel_transfer 会验证 PENDING 交易的发送方账户属于调用用户;无论该交易不存在还是属于其他人,都返回相同的通用“未找到”响应(已在 test_offline.pydiagnose_setup.py 中验证)

自我转账会被阻止

validate_recipient 在允许转账继续之前,会将收款人账号与发送方自己的账号进行比较

金额无法在处理过程中被操纵

confirm_transfer 时实际借记/贷记所使用的金额,是 initiate_transfer 时创建的 PENDING 行上存储的金额——绝不会从确认请求中重新读取

未经明确确认,AI 无法转移资金

create_transfer/add_money MCP 工具本身绝不会借记/贷记;app/agent.py 的对话状态机要求先对显示的确认信息明确回复“yes”,之后才会调用 confirm_transfer

原子性

confirm_transfer 在同一个 SQLite 事务(app/db.pytx())内执行余额检查 + 两次余额更新 + 状态更新,并配合一个进程级锁——参见 app/wallet.py 的模块文档字符串

10. 双语支付流程

钱包完全支持双语,使用与应用程序其他部分相同的 app/i18n.py 机制——Add Money、Send Money(全部三个步骤)、确认界面、交易状态,以及所有关于余额/转账的 AI 响应,都通过 t()/tpl() 渲染,在 app/wallet.pyapp/agent.pystatic/index.html 的钱包 UI 中没有任何硬编码的英文。例如,用印地语/印英混合语向 AI 说 "Rahul ko ₹2,000 bhejo",会经历与英文版完全相同的解析 → 确认 → 执行流程,所有消息——包括确认界面——都以印地语渲染。

设置

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)

数据库会在首次运行时自动创建(并且仅在数据库为空时,预置两个演示用户——见下文);本地开发无需单独的迁移步骤。如需提前显式创建:

python3 -m app.db

在打开浏览器之前,请先验证:

python3 diagnose_setup.py

运行

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

打开 http://localhost:8000。注册一个新账户,或使用某个内置演示账户登录:

邮箱

密码

ramesh@example.com

Demo@1234

priya@example.com

Demo@1234

然后可以试试建议菜单,询问类似 "check payment status pay_1001""मेरा भुगतान pay_1001 का स्टेटस क्या है?" 的问题,在 Settings 中切换语言,或者尝试以某个用户身份登录后,询问另一个用户的支付/订单/退款 ID(pay_1003ord_2002rfnd_3002 属于 Priya),以查看访问被拒绝的响应。

要试用钱包:点击标题栏中的 💰 Wallet 按钮。两个演示账户都有初始余额(Ramesh ₹8,500,Priya ₹8,000),并且各自的历史记录中已有一条演示转账。可以试试 "Add Money",或向另一个演示账户的账号(可在其各自的 Wallet 界面中看到)"Send Money",也可以直接询问 AI 助手:"what's my balance?""add ₹5,000 to my account""send ₹2,000 to Priya Stores"(第一次它会要求提供她的账号 + IFSC,转账成功后会提议将她保存为收款人——之后只需提供她的名字即可),或用印地语说 "Mera current balance kitna hai?"

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