mcpField
E
EVS Meter Tools

EVS Meter Tools

远程服务
nusaircon-production.up.railway.app其他2今天更新

关于 EVS Meter Tools

EVS Meter Tools 是面向 NUS 和 SUTD 部分电表的网页应用与 Telegram 机器人,可查询余额、用量和充值记录,并在支持时通过银行卡充值。其只读 MCP 服务让 AI 助手查询电表信息并提供付款链接,不处理付款或银行卡信息。

主要功能

  1. 01查询 NUS 和 SUTD 电表余额
  2. 02查看用量与近期充值记录
  3. 03银行卡充值链接
  4. 04Telegram 机器人与网页应用
  5. 05只读电表工具
  6. 06MCP 不接收银行卡信息

适用场景

  1. 01查询 NUS 或 SUTD 电表余额
  2. 02查看近期用量和充值记录
  3. 03获取支持电表的充值链接
  4. 04让 AI 助手检查余额是否偏低

MCP 相关

公开的只读 MCP 服务提供查询电表余额、NUS 用量、近期充值记录和充值链接的工具。它不能进行付款或接收银行卡信息,且响应不包含房间地址。

配置

远程服务无需本地安装。选择你使用的客户端,复制配置或使用一键安装。

服务地址https://nusaircon-production.up.railway.app/mcp
一键复制安装命令
Claude CodeTerminal
Cursor.cursor/mcp.json
VS Code.vscode/mcp.json
Claude Desktopclaude_desktop_config.json
Windsurf~/.codeium/windsurf/mcp_config.json

命令在终端中运行;JSON 粘贴到对应客户端的 MCP 配置后重启。需要授权的服务会在首次连接时提示登录。

工具

详细介绍

EVS 电表工具

面向受支持 EVS 电表的 Telegram 机器人和 Web 应用。受支持的 NUS 和 SUTD 电表可通过信用卡充值;在可用时,受支持的查询流程会显示余额、用量和充值历史。

支持的系统

宿舍组EVS 系统
NUS 宿舍,包括 PGPR、Houses @ PGP、Residential Colleges、NUS College、UTown Residences、RVRC、Valour Housecp2nus.evs.com.sg
SUTDSUTDMain / EVSSUTDWebPOS

旧版 NUS CP2 备用方案(cp2.evs.com.sg)在 Web 应用和机器人中均不可见。其后端路由和服务仍保留,但未关联到任何 UI;请参阅 CP2 — 旧版 NUS 备用方案(隐藏)。

功能

  • 在 Telegram 中查看电表余额、7 天用量历史和近期充值记录
  • 通过信用卡为受支持的 NUS 电表充值(SGD $6-$50)
  • 通过信用卡为受支持的 SUTD 电表充值(SGD $10-$50)
  • 使用 RSA 加密输入银行卡信息——银行卡信息不会以明文形式离开浏览器
  • 可作为 Telegram Mini App 使用,也可作为独立网站使用
  • 跨系统防护:cp2nus 引导流程会在发起支付前拒绝属于旧版 cp2 系统的电表
  • SUTD 余额、充值历史和充值支持
  • 在整个流程中跟踪分析数据并捕获错误
  • Telegram 低余额提醒(需主动开启),并提供开始充值的按钮
  • 只读 MCP 服务,用户可连接自己的 Claude 并设置自己的指令

架构

Telegram Bot (telegraf)          Website (React, /app/)
    │                                │
    ├── /topup                       └── HomePage
    ├── /balance                          └── system selection
    ├── /usage                                + meter ID + amount
    └── /topups
          │ webhook in production/Railway,       │
          │ polling in development               │
          ▼                                       ▼
    Express (server.js)  ←────────────────────────────
          │
          ├── POST /telegram/webhook/* — Telegram webhook receivers
          ├── GET  /webapp              — legacy CP2 (hidden from UI): fetches meter summary, redirects to React
          ├── GET  /webapp/bootstrap    — runs full payment init, returns token
          ├── GET  /webapp/session      — returns session data as JSON for React
          ├── GET  /webapp/pay          — redirects to React card entry page
          ├── POST /webapp/enets_pay    — proxies encrypted card data to eNETS
          ├── POST /webapp/notify       — fallback Telegram payment notification
          └── GET  /webapp/result       — redirects to React result page

    React Frontend (/app/)
          ├── /                 — HomePage (system + meter ID + amount)
          ├── /balance          — HomePage opened in balance-check mode
          ├── /cp2nus           — HomePage preselected for primary NUS CP2NUS flow
          ├── /cp2nus/loading   — cp2nus variant
          ├── /cp2nus/pay       — cp2nus variant
          ├── /cp2nus/result    — cp2nus variant
          ├── /sutd/loading     — SUTD variant
          ├── /sutd/pay         — SUTD variant
          └── /sutd/result      — SUTD variant

支付流程

CP2 — 旧版 NUS 备用方案(隐藏)

隐藏。 此流程已不再通过 Web 应用或机器人提供。frontend/src/App.jsx 中的 React 路由 /app/cp2、/app/loading、/app/pay 和 /app/result 已注释掉,/app/cp2 已从 sitemap 和 robots.txt 中排除,机器人也不再显示宿舍选择器。以下后端路由和服务仍予以保留,供参考或日后重新启用。

该流程会抓取旧版 EVS WebPOS 门户以创建交易,然后通过 eNETS 代理支付。它仅用于尚未迁移到 CP2NUS 的 NUS 电表。

  1. /webapp — 从 ORE 获取电表地址和余额,并将地址和余额作为查询参数传递到 React 加载页面
  2. 引导流程(/webapp/bootstrap)— 并行运行 runPurchaseFlow 和 getMeterSummary:
    • GET /EVSWebPOS/ → 登录 → POST /loginServlet(电表验证)
    • POST /selectOfferServlet(金额选择)
    • GET /paymentServlet → 提取 merchant_txn_ref
    • POST creditpayment.jsp → 提取 eNETS message
    • POST /enets2/PaymentListener.do → 提取 RSA 公钥(n、e)、netsMid、netsTxnRef
    • 创建支付会话(TTL 为 10 分钟),重定向到 React 银行卡页面
  3. 银行卡页面(/app/pay)— React 组件;通过 /webapp/session 获取会话;在客户端使用 eNETS RSA 脚本加密 cardNo + cvv
  4. 支付代理(/webapp/enets_pay):
    • POST https://www.enets.sg/GW2/uCredit/pay
    • **首选路径:**提取 EVS 回调表单 → POST /EVSWebPOS/transSumServlet → parseEvsTransactionSummary
    • **备用方案:**直接抓取 eNETS 收据 HTML,由 parseEnetsResult 解析
    • 将结果(status、merchantTxnRef、reason)写回服务端会话
  5. 结果页面(/app/result)— React 组件;通过 /webapp/session 从服务端会话读取结果
CP2NUS — NUS 宿舍主流程

直接使用 EVS JSON API 和 eNETS Payment Page(enetspp)主机。这是适用于已迁移电表的 NUS 宿舍主流程。

  1. /webapp — 与 cp2 相同;获取电表信息,并重定向到 /app/cp2nus/loading
  2. 引导流程(/cp2nus/webapp/bootstrap)— runBootstrap 按顺序运行:
    • 电表系统检查 — isCp2Meter() 守卫:拒绝 cp2 电表并返回 WRONG_SYSTEM 错误;网络故障时跳过检查并继续流程
    • init_pay — POST /enets/init_pay → { txn_identifier, req, sign }
    • meter_info — getMeterSummary → buildPayDisplayAddress
    • enetspp_pay — buildEnetsPayUrl → GET enetspp/pay?p=… → 提取 txnReq、keyId、hmac
    • TxnReqListener — POST /GW2/TxnReqListener → RSA 密钥、netsTxnRef、netsMid、paymtNetsMid、txnRand、keyId、hmac
  3. 银行卡页面(/app/cp2nus/pay)— 使用相同的 RSA 加密;panSubmitForm 使用 paymtNetsMid(收单 MID),而非顶层的 netsMid
  4. 支付代理(/cp2nus/webapp/enets_pay):
    • GET /GW2/pluginpages/env.jsp → 创建 JSESSIONID 种子
    • POST /GW2/credit/init;jsessionid=…
    • POST /GW2/credit/panSubmitForm
    • **首选路径:**响应中的 netsTxnStatus → preParsed 结果(不调用 b2s)
    • 回退路径:POST /enets/b2s → 303 重定向 → parsePayResult 对参数进行 base64 解码
  5. 结果页面(/app/cp2nus/result)— 从服务端会话中读取结果
SUTD

使用 SUTD EVS WebPOS 页面和 SUTD 信用卡支付桥接服务。

  1. /sutd/webapp — 获取 SUTD 电表余额,并重定向到 /app/sutd/loading
  2. 引导流程(/sutd/webapp/bootstrap)— 运行 SUTD WebPOS 流程:
    • 使用电表 ID 执行 POST /EVSSUTDWebPOS/loginServlet
    • 使用选定金额执行 POST /EVSSUTDWebPOS/selectOfferServlet
    • GET /EVSSUTDWebPOS/paymentServlet → 提取 merchant_txn_ref
    • POST http://120.50.44.233/payment_sutd_credit/creditpayment.jsp
    • POST /enets2/PaymentListener.do → 提取 RSA 公钥、netsMid、netsTxnRef
  3. 银行卡页面(/app/sutd/pay)— 与 NUS 流程相同,使用 RSA 加密输入银行卡信息
  4. 支付代理(/sutd/webapp/enets_pay)— 将加密后的银行卡数据提交到 eNETS,然后将回调提交到 /EVSSUTDWebPOS/transSumServlet
  5. 结果页面(/app/sutd/result)— 从服务端会话中读取结果

设置

前置条件
  • Node.js 20.19+(Node 22 需 22.12+)
  • Telegram 机器人令牌(从 @BotFather 获取)
  • 可公开访问的 HTTPS 服务器(Telegram WebApp 按钮必需)
安装
# Install backend dependencies
npm install

# Install and build the frontend
npm run build:frontend
环境变量

创建一个 .env 文件:

TELEGRAM_BOT_TOKEN=your_bot_token_here      # legacy single-bot token
NUS_TELEGRAM_BOT_TOKEN=your_nus_bot_token   # optional, for concurrent nusairconbot
SUTD_TELEGRAM_BOT_TOKEN=your_sutd_bot_token # optional, for concurrent sutdairconbot
PAYMENT_SESSION_SECRET=replace_with_openssl_rand_hex_32 # stable secret for signed payment/result tokens
SERVER_URL=https://your-public-server.example.com
OWNER_CHAT_ID=your_telegram_chat_id   # receives feedback notifications
MAINTENANCE_MODE=false                # set to "true" to put the app and bot in global maintenance mode
MAINTENANCE_MESSAGE=                  # optional custom global maintenance message
OWNER_STATUS_TIMEOUT_MS=4000          # optional timeout for owner /status EVS/eNETS checks
TOPUP_DISABLED=false                  # set to "true" to show maintenance message
DB_DIR=.                              # local SQLite dir; use /data on Railway with a mounted volume
TELEGRAM_BOT_MODE=                    # production/Railway defaults to webhook; dev defaults to polling
TELEGRAM_BOT_AUDIENCE=nus             # legacy single-bot mode: "nus" or "sutd"
LOW_BALANCE_CRON_SECRET=              # optional, enables the low-balance check endpoint
MCP_ALLOWED_ORIGINS=                  # optional, comma-separated extra browser origins allowed to call /mcp
GOOGLE_SITE_VERIFICATION_FILE=        # optional Search Console HTML file name, e.g. googleabc123.html
GOOGLE_SITE_VERIFICATION_CONTENT=     # optional exact file body if Google provides non-default content

SERVER_URL 必须使用 HTTPS,Telegram WebApp 付款按钮才能正常工作。如果使用 HTTP,机器人会改用普通浏览器链接。使用 Railway 时,将其设为 https://${{RAILWAY_PUBLIC_DOMAIN}},也可以留空,让应用从 RAILWAY_PUBLIC_DOMAIN 推导该值。

PAYMENT_SESSION_SECRET 应为稳定的随机值,例如使用 openssl rand -hex 32 生成。如果省略该变量,应用会回退使用已配置的机器人令牌;只要机器人令牌不变,即使 Railway 休眠,该值也会保持不变。

如需通过 Google Search Console 的网址前缀验证,请使用 HTML 文件验证方式,并将 GOOGLE_SITE_VERIFICATION_FILE 设为下载的文件名。默认响应正文为 google-site-verification: <file name>。如果 Google 下载的文件包含不同文本,请将 GOOGLE_SITE_VERIFICATION_CONTENT 设为文件正文的准确内容。

在生产环境中,或设置了 RAILWAY_PUBLIC_DOMAIN 时,每个已配置的机器人默认使用 Telegram Webhook,以便 Railway Serverless 可以休眠,并在收到 Telegram 请求时唤醒。只有在持续运行的部署中,才将 TELEGRAM_BOT_MODE=polling。单机器人模式下的可选 Webhook 变量为 TELEGRAM_WEBHOOK_PATH、TELEGRAM_WEBHOOK_SECRET 和 TELEGRAM_DROP_PENDING_UPDATES。对于并发机器人,只有需要自定义路径时才使用 NUS_TELEGRAM_WEBHOOK_PATH、NUS_TELEGRAM_WEBHOOK_SECRET、SUTD_TELEGRAM_WEBHOOK_PATH 和 SUTD_TELEGRAM_WEBHOOK_SECRET;否则会自动生成唯一的路径。

如需在同一次部署中运行多个 Telegram 机器人,请同时设置 NUS_TELEGRAM_BOT_TOKEN 和 SUTD_TELEGRAM_BOT_TOKEN。同一服务会启动 nusairconbot 和 sutdairconbot,两者分别使用独立的命令菜单、Webhook 路径、内存会话和付款通知。单机器人部署仍可使用 TELEGRAM_BOT_TOKEN 和 TELEGRAM_BOT_AUDIENCE。

运行
# Development (two terminals)
npm run dev            # Express backend on :3000
npm run dev:frontend   # Vite dev server on :5173

# Production
npm run build:frontend
npm start

在生产环境中,Express 会在 /app/ 提供前端。在开发环境中,Vite 会将 /webapp、/cp2nus 和 /sutd 代理到后端。

本地开发时,请保留 DB_DIR=. 或省略该变量。在 Railway 上,只有在挂载到 /data 的卷已就绪后,才将 DB_DIR=/data。

Railway Serverless

仓库中包含 railway.json,其中定义了生产构建、启动命令和 /health 检查。需要在控制面板中启用 Railway Serverless:

  1. 为服务添加公共域名。
  2. 对于单个机器人,设置 TELEGRAM_BOT_TOKEN;对于并发机器人,设置 NUS_TELEGRAM_BOT_TOKEN 和 SUTD_TELEGRAM_BOT_TOKEN。同时设置 PAYMENT_SESSION_SECRET、SERVER_URL=https://${{RAILWAY_PUBLIC_DOMAIN}},以及 OWNER_CHAT_ID 等可选变量。
  3. 挂载到 /data 的卷,并将 DB_DIR=/data,以便保存的用户数据在重启后仍然保留。
  4. 前往服务设置 > Deploy > Serverless,并启用 Serverless。
  5. 部署。启动时会自动注册 Telegram Webhook。

冷启动可能会导致首次 Telegram 或网页请求变慢;Railway 唤醒服务时,可能会在首次请求中返回 502。机器人会话和所有者回复线程保存在内存中,重启或休眠后会重置;付款/结果令牌使用 PAYMENT_SESSION_SECRET 加密封装,重启后仍可读取,直到 TTL 到期。已保存的电表 ID 会保留在 SQLite 中。

测试
# Backend tests
npm test

# Frontend tests
cd frontend && npm test

# Lint backend and frontend
npm run lint

连接你自己的 Claude(MCP)

该服务在 POST /mcp 提供公开的只读 MCP 服务,因此用户可以连接自己的 AI 助手,并自行决定其行为。无需账户或 API 密钥。

工具功能
get_meter_balance当前余额(NUS 和 SUTD)
get_meter_usage过去 7 天的用量和分析(仅 NUS)
get_recent_topups最近最多 10 笔充值(NUS 和 SUTD)
get_topup_link充值页面链接,用户可自行使用银行卡付款

这些工具无法付款,也不会接收银行卡信息。响应不会包含房间地址,因为响应会传递给用户的 AI 提供商。

要连接,请在 Claude 设置中将 https://<your-domain>/mcp 添加为自定义连接器。在 Claude Code 中运行:

claude mcp add --transport http evs-meter https://<your-domain>/mcp

每个人都可以在自己的助手中编写指令,例如在 Claude Project 中:

My NUS meter ID is 12345678. When I ask about aircon credit, check the balance
and work out how many days it will last from my recent usage. If it will last
under 3 days, suggest a top-up amount and give me the top-up link.

该服务是无状态的,因此可与 Railway Serverless 配合使用。每个 IP 地址每 5 分钟最多可调用工具 120 次,且无法批量发送调用。托管客户端会共享 IP 地址,因此该限制高于网站的查询限制。

工具错误以 JSON 格式返回,因此 Agent 可以决定下一步操作:

{ "code": "METER_NOT_FOUND", "message": "EVS has no NUS meter with this ID. ...", "retryable": false }
代码可重试含义
METER_NOT_FOUND否EVS 无法识别该系统上的电表
INVALID_AMOUNT否金额超出该系统的充值限制
TOPUPS_PAUSED否目前已关闭充值功能
EVS_UNAVAILABLE是无法连接 EVS,或 EVS 未返回详细信息
RATE_LIMITED是此 IP 地址的调用次数过多;包含 retryAfterSeconds

get_topup_link 会先检查电表。如果无法连接 EVS,仍会返回链接,但 meterVerified 为 false。

每个工具都声明了输出架构,并返回 structuredContent;对于无法读取该字段的客户端,也会以文本形式返回相同的 JSON。

如需让 Agent 每天检查余额,并在余额不足时向你发送付款链接,请参阅定时余额检查。如需运行自己的副本,或基于此服务构建自己的自动化流程,请参阅自托管指南。

机器人命令

命令说明
/start显示主菜单
/topup开始电费充值
/balance查询电表余额
/usage显示过去 7 天的用量
/topups显示最近的充值记录
/alerts显示或更改低余额提醒
/feedback提交反馈或报告问题
/cancel取消当前流程
/help显示帮助和支持的系统
/status仅限所有者查看实时服务状态
/stats仅限所有者查看运行时快照

低余额提醒

用户可以通过 /alerts on 选择开启提醒,该设置适用于此机器人中已保存的电表。用户在机器人中开始充值时,电表会被保存。每天会检查每个已开启提醒的电表;如果余额低于 SGD 5,机器人就会向对应的聊天发送消息。消息中有一个按钮,可以启动机器人的常规充值流程,因此用户仍可自行选择金额并使用银行卡付款。

每个电表在每次低余额期间只会收到一次提醒。检查发现余额恢复到 SGD 5 或以上后,之后再次低于此金额时可以再次提醒。查询失败时会跳过,绝不会视为余额为零。如果用户屏蔽了机器人,提醒就会停止;/forget 会连同电表一起删除提醒。

要开启每日检查:

  1. 使用 openssl rand -hex 32 生成密钥。
  2. 在 Railway 上将其设置为 LOW_BALANCE_CRON_SECRET。
  3. 在 GitHub 上将相同的值添加为同名的仓库密钥。

随后,.github/workflows/low-balance-check.yml 会在新加坡时间 09:00 调用 POST /internal/low-balance-check;如果 Railway Serverless 已让服务进入休眠,此调用也会唤醒服务。要测试此功能,请在 GitHub Actions 选项卡中手动运行该工作流。GitHub 可能会延迟计划运行;如果仓库 60 天没有活动,GitHub 会禁用计划运行。

机器人会话流程

会话存储在内存中,TTL 为 15 分钟。同一聊天中的所有消息都会通过该聊天专属的锁按顺序处理,以防止竞态条件。在无服务器部署中,Railway 停止服务并在之后重新启动时,会话会重置。充值流程的阶段如下:

idle
  → awaiting_meter_id          (8-digit ID; prefetches balance + 7-day usage;
                                NUS always uses cp2nus, no hostel picker)
  → awaiting_amount            ($6–$50 SGD)
  → awaiting_payment           (WebApp Pay button; re-prompts on text)
  → idle                       (reset after WebApp closes)

  → awaiting_meter_id_balance  (/balance with no saved meter)
  → awaiting_meter_id_usage    (/usage with no saved meter)
  → awaiting_meter_id_topups   (/topups with no saved meter)

  → awaiting_feedback_rating   (/feedback — star rating keyboard)
  → awaiting_feedback_text     (free-text or ⏭ Skip)
  → idle

/balance、/usage 和 /topups 使用单步阶段,在收到一次回复后返回空闲状态。

支付会话

支付会话(由 /webapp/bootstrap 创建)是密封的加密令牌,TTL 为 10 分钟,与机器人会话分开。令牌包含支付所需的电表 ID、金额、地址、余额和 eNETS 网关字段。这样,即使 Railway Free 在新进程中唤醒服务,也不会丢失正在进行的支付会话。

/webapp/enets_pay 得出最终结果后,服务器会立即发送 Telegram 支付通知,并返回一个密封的结果令牌,TTL 为 24 小时。支付令牌和结果令牌都带有内部 tokenKind;支付路由会拒绝结果令牌,结果路由也会拒绝支付令牌。React 结果页面通过 GET /webapp/session?token= 读取结果数据;绝不会信任查询参数中的支付结果。CP2NUS 收据 PDF 仍保存在易失的内存缓存中,因此只有在缓存条目可用时,结果页面才会显示收据链接。

用户存储

已保存的电表 ID 和宿舍会使用 better-sqlite3 持久化到 SQLite 数据库(evs_users.db)。数据库写入 DB_DIR(如果存在,则使用 Railway Volume /data;否则使用项目根目录)。与机器人会话和支付会话不同,用户存储在重启后仍会保留。

所有者回复串接

用户提交反馈时,机器人会将通知转发到 OWNER_CHAT_ID。所有者可以直接回复这条 Telegram 通知,机器人会将回复转发给用户。回复串接通过带有 7 天 TTL 的内存 pendingReplies 映射进行跟踪。

注意:串接只会跟踪对原始通知消息的回复。如果所有者回复了自己的回复,该消息不会自动转发给用户——只有对原始转发通知的回复才会被拦截。

项目结构

├── server.js                        # Express entry point; serves React at /app/
├── routes/
│   ├── cp2.js                    # WebApp + API routes for legacy cp2 (hidden from UI)
│   ├── cp2nus.js                 # WebApp + API routes for cp2nus
│   ├── sutd.js                   # WebApp + API routes for SUTD
│   ├── mcp.js                    # Read-only MCP server for AI assistants
│   ├── alerts.js                 # Secret-protected trigger for the low-balance check
│   └── website.js                # Standalone website lookup + feedback API
├── services/
│   ├── cp2Service.js             # Legacy cp2 purchase flow: EVS WebPOS scraping + eNETS proxy
│   ├── cp2nusService.js          # Purchase flow: EVS JSON API + eNETS PP + NETS API
│   ├── sutdService.js            # SUTD balance, history, and WebPOS purchase flow
│   ├── ore.js                    # ORE API: meter summary, usage history, top-up history
│   ├── meterLookup.js            # Balance, usage, and top-up lookups shared by the website and MCP
│   ├── paymentSession.js         # Sealed payment/result tokens and receipt cache
│   ├── paymentNotification.js    # Telegram payment result notification helper
│   ├── paymentSubmitLock.js      # Short-lived duplicate payment submit guard
│   ├── utils.js                  # HTML parsing, result normalisation, XSS escaping
│   ├── validators.js             # Meter ID validation and strict amount parsing
│   ├── config.js                 # Base URLs and shared HTTP headers
│   └── analytics.js              # Event tracking and exception capture
├── bot/
│   ├── index.js                  # Telegraf handlers + webhook/polling runtime
│   ├── handlers/                 # Command and text message handlers
│   ├── services/                 # Bot session, user store, lookup helpers
│   └── constants.js              # Stage names, keyboards, shared messages
├── views/
│   └── errorPage.js              # Shared HTML error page for Express error responses
└── frontend/                     # React + Vite frontend
    ├── src/
    │   ├── App.jsx               # React Router routes
    │   ├── pages/
    │   │   ├── HomePage.jsx      # System selection + meter ID + amount entry
    │   │   ├── LoadingPage.jsx   # Spinner; calls /webapp/bootstrap
    │   │   ├── CardPaymentPage.jsx  # RSA card form; calls /webapp/enets_pay
    │   │   ├── ResultPage.jsx    # Payment outcome
    │   │   └── TermsPage.jsx     # Terms of Use
    │   ├── components/           # Card, DetailRow, Logo, ErrorCard
    │   └── lib/                  # rsa.js, cardBrand.js, validation.js
    └── __tests__/                # Vitest + Testing Library tests

备注

  • Bot 会话存储在内存中,并会在 15 分钟后过期。支付令牌会经过密封处理;只要 PAYMENT_SESSION_SECRET 保持稳定,服务重启后仍可使用;待处理的支付令牌会在 10 分钟后过期,已完成的结果令牌会在 24 小时后过期。用户存储(已保存的 meter ID)由 SQLite 提供支持,并会在服务重启后保留。
  • /webapp/enets_pay 使用进程本地提交锁,并以商户交易参考编号为键,拒绝重复的进行中支付提交,返回 HTTP 409。
  • 卡片详情会在浏览器中经过 RSA 加密后再发送到服务端。服务端不会接触明文卡号或 CVV。
  • cp2nus 流程会区分顶层的 netsMid(UMID_xxx)和 paymtNetsMid(来自 paymtSvcInfoList[0] 的收单 MID)。使用错误的 MID 会导致支付失败,且不会显示错误。
  • ORE(ore.evs.com.sg)和 eNETS 初始化端点(p-1.evs.com.sg)目前会在 Authorization: Bearer 请求头为空的情况下调用,并且能够成功——不需要令牌。EVS2 门户还运行着用户身份验证服务(evs2u.evs.com.sg/login),可签发有效期较短(约 1 小时)的 JWT,因此 EVS 未来可能会开始要求这些端点使用真实令牌。如果查询或充值开始返回 401/403,这很可能就是原因;届时需要在流程中获取并附加 JWT。
  • 可接受的充值金额输入格式严格:纯数字以及常见货币前缀,例如 $10 或 S$ 10.00;格式错误的混合字符串会被拒绝。NUS 的最低充值金额为 $6.00 SGD,SUTD 的最低充值金额为 $10.00 SGD,目前所有系统的充值上限均为 $50.00 SGD。
  • 网站入口(/app/)和 Telegram Mini App 使用相同的 Express 路由和 React 页面,没有单独的代码路径。
  • 可通过 /maintenanceon(所有者命令)在运行时启用全局维护模式,也可在启动时设置 MAINTENANCE_MODE=true 启用。用户会在应用和 Bot 中看到全局维护消息;/health 和 Telegram webhook 仍保持可用,以确保服务可以访问,且所有者可以运行 /maintenanceoff。设置 MAINTENANCE_MESSAGE 可自定义提示文本。GLOBAL_MAINTENANCE=true 也可作为旧版别名使用。
  • 可通过 /topupoff(所有者命令)在运行时禁用充值,也可在启动时设置 TOPUP_DISABLED=true 禁用。如果 Bot 用户在设置该标志时正处于充值会话中,其会话将被重置,并显示维护消息。禁用充值期间,网站的支付开始、加载、卡片、bootstrap 和卡片提交路由也会返回维护响应;查询、条款、结果和反馈路由仍可使用。

许可协议

MIT。这是一个非官方工具,与 NUS、SUTD、EVS 或 eNETS 均无关联。

评论

支持 Markdown

第一次接触 MCP?

从协议是什么,到在客户端里装上第一个服务,这几条回答覆盖了大多数入门问题。

一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。