diff --git a/.env.example b/.env.example index 2541c9bd..9c7b02ed 100644 --- a/.env.example +++ b/.env.example @@ -171,13 +171,24 @@ POLYWEATHER_BOT_DEB_QUERY_COST=1 # Payments POLYWEATHER_PAYMENT_ENABLED=false +# Default / legacy checkout chain. Keep Polygon as the default because the +# deployed checkout contract currently lives there. POLYWEATHER_PAYMENT_CHAIN_ID=137 POLYWEATHER_PAYMENT_RPC_URL=https://polygon-rpc.com POLYWEATHER_PAYMENT_RPC_URLS=https://polygon-rpc.com +# Optional multi-chain RPC map. Required when accepting non-default chain +# transfers such as Ethereum mainnet USDC. +# Example: +# POLYWEATHER_PAYMENT_RPC_URLS_BY_CHAIN_JSON={"137":["https://polygon-rpc.com"],"1":["https://ethereum-rpc.example"]} +POLYWEATHER_PAYMENT_RPC_URLS_BY_CHAIN_JSON= POLYWEATHER_PAYMENT_RECEIVER_CONTRACT= POLYWEATHER_PAYMENT_DIRECT_RECEIVER_ADDRESS= POLYWEATHER_PAYMENT_TOKEN_ADDRESS=0x3c499c542cef5e3811e1192ce70d8cc03d5c3359 POLYWEATHER_PAYMENT_TOKEN_DECIMALS=6 +# Multi-token / multi-chain payment routes. Token rows can include: +# chain_id, chain_code, chain_name, receiver_contract, direct_receiver_address, +# supports_contract_checkout, supports_direct_transfer, confirmations, +# explorer_tx_url. POLYWEATHER_PAYMENT_ACCEPTED_TOKENS_JSON= POLYWEATHER_PAYMENT_CONFIRMATIONS=2 POLYWEATHER_PAYMENT_INTENT_TTL_SEC=1800 diff --git a/.env.secrets.example b/.env.secrets.example index 205942fe..0ec610e0 100644 --- a/.env.secrets.example +++ b/.env.secrets.example @@ -34,6 +34,7 @@ METEOBLUE_API_KEY= # Wallet / payments ######################################## NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID= +POLYWEATHER_PAYMENT_RPC_URLS_BY_CHAIN_JSON= POLYWEATHER_PAYMENT_RECEIVER_CONTRACT= POLYWEATHER_PAYMENT_DIRECT_RECEIVER_ADDRESS= POLYWEATHER_PAYMENT_ACCEPTED_TOKENS_JSON= diff --git a/README.md b/README.md index bb733fad..73d1ad20 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ Public docs center: `/docs/intro` on the main site (bilingual product documentat - Points system live: earn via group chat, welcome bonus (+20), first-message-of-day bonus (+2), weekly participation rewards. - `/city` and `/deb` now free (daily cap 10 each); points redeemable for payment discount (`500 pts = 1 USDC`, max `3 USDC`). - Weekly leaderboard rewards restructured: smaller point bonuses for winners (200/100/50), all active users receive participation rewards. -- Onchain checkout live: Polygon contract checkout (USDC / USDC.e). +- Onchain checkout live: Polygon contract checkout (USDC / USDC.e) plus Ethereum mainnet USDC direct-transfer confirmation. - Auto-reconciliation live: event listener + periodic confirm loop. - Ops dashboard live: `/ops` for memberships, leaderboard, manual point grants, and payment incident triage. - Lightweight observability live: `/healthz`, `/api/system/status`, `/metrics`. @@ -102,7 +102,7 @@ flowchart LR API --> SSE["SSE /api/events"] WX --> SSE SSE --> EVENT["Redis Stream / SQLite Event Log"] - ANA --> PAY["Payment State (Intent + Event + Confirm Loop)"] + ANA --> PAY["Payment State (Multi-chain Intent + Event + Confirm Loop)"] ANA --> STATE["SQLite runtime state"] ``` @@ -198,6 +198,10 @@ docker compose logs -f polyweather | egrep "payment event loop started|payment c curl http://127.0.0.1:8000/api/payments/runtime ``` +### Payment chains + +Production payment routes are configured by the backend. Polygon remains the default checkout-contract chain, while Ethereum mainnet USDC can be enabled as a direct-transfer route so users who pay on their wallet default network are still confirmed by `intent.chain_id`. + ## Telegram Commands | Command | Purpose | diff --git a/README_ZH.md b/README_ZH.md index 3cd2b69d..0a355444 100644 --- a/README_ZH.md +++ b/README_ZH.md @@ -14,13 +14,13 @@ ![PolyWeather Telegram 跑道推送](frontend/public/static/tel.png) -## 当前产品状态(2026-05-28) +## 当前产品状态(2026-05-29) - 已上线订阅制:`Pro 月付 10 USDC`。 - 已上线积分体系:群内发言赚分 + 首次发言欢迎奖励 (+20) + 每日首条消息奖励 (+2) + 每周全员参与奖。 - `/city` 与 `/deb` 已改为免费(每日各 10 次);积分可用于支付抵扣(`500 分 = 1 USDC`,最多抵 `3 USDC`)。 - 周榜奖励已改造:降低赢家积分加成 (200/100/50),所有周活跃用户均享参与奖。 -- 已上线链上支付:Polygon 合约支付(USDC / USDC.e)。 +- 已上线链上支付:Polygon 合约支付(USDC / USDC.e)+ Ethereum 主网 USDC 直转确认。 - 已上线自动补单:事件监听 + 周期确认双链路。 - 已上线支付运行态与审计接口:`/api/payments/runtime`。 - 已上线轻量运营后台:`/ops`(会员、周榜、补分、支付异常单)。 diff --git a/docs/API_ZH.md b/docs/API_ZH.md index 7ed07ff8..10d5374d 100644 --- a/docs/API_ZH.md +++ b/docs/API_ZH.md @@ -238,6 +238,13 @@ DEB hourly consensus 是当前图表与峰值窗口的优先小时路径。 ### 支付状态建议 +`POST /api/payments/intents` 支持的关键字段: + +- `plan_code`:套餐,例如 `pro_monthly` +- `payment_mode`:`wallet` 或 `direct` +- `chain_id`:可选;多链支付时前端传用户选择的链,例如 Polygon `137` 或 Ethereum `1` +- `token_address`:可选;指定该链上的 USDC / USDC.e 合约地址 + 前端流程建议: 1. `POST /intents` diff --git a/docs/COMMERCIALIZATION.md b/docs/COMMERCIALIZATION.md index bf6e92c6..593eda2e 100644 --- a/docs/COMMERCIALIZATION.md +++ b/docs/COMMERCIALIZATION.md @@ -1,6 +1,6 @@ # 商业化说明(Production) -最后更新:`2026-05-28` +最后更新:`2026-05-29` ## 1. 定位 @@ -21,7 +21,8 @@ PolyWeather 是面向温度结算场景的气象决策层,不是通用天气 | 订阅套餐(Pro 月付) | 已上线 | `10 USDC / 30天` | | 积分抵扣 | 已上线 | `500分=1U`,最多 `3U` | | 合约支付 | 已上线 | Polygon,USDC + USDC.e | -| 支付自动确认 | 已上线 | Event Loop + Confirm Loop | +| 多链直转支付 | 已上线 | Ethereum 主网 USDC 直转确认 | +| 支付自动确认 | 已上线 | Event Loop + Confirm Loop,按 intent.chain_id 查链 | | 钱包绑定 | 已上线 | 浏览器钱包 + WalletConnect | | 私有频道推送 | 已上线 | 可拆分业务频道 | | 实时终端 | 已上线 | SSE Patch + Redis Stream replay | diff --git a/docs/CONFIGURATION_ZH.md b/docs/CONFIGURATION_ZH.md index f88375f2..3e324622 100644 --- a/docs/CONFIGURATION_ZH.md +++ b/docs/CONFIGURATION_ZH.md @@ -110,6 +110,7 @@ PolyWeather 的环境变量很多,但不是所有变量都属于同一层级 - `POLYWEATHER_EVENT_STORE` - `POLYWEATHER_REDIS_REQUIRED` - `POLYWEATHER_PAYMENT_ENABLED` +- `POLYWEATHER_PAYMENT_RPC_URLS_BY_CHAIN_JSON` - `POLYGON_WALLET_WATCH_ENABLED` - `TELEGRAM_ALERT_PUSH_ENABLED` - `TELEGRAM_MARKET_FOCUS_DIGEST_ENABLED` @@ -131,6 +132,7 @@ PolyWeather 的环境变量很多,但不是所有变量都属于同一层级 - `TELEGRAM_MARKET_FOCUS_DIGEST_INTERVAL_SEC` - `TELEGRAM_MARKET_FOCUS_DIGEST_TOP_N` - `POLYWEATHER_PAYMENT_RPC_URLS` +- `POLYWEATHER_PAYMENT_RPC_URLS_BY_CHAIN_JSON` - `TAF_CACHE_TTL_SEC` - `POLYWEATHER_REDIS_URL` - `POLYWEATHER_REDIS_STREAM_KEY` @@ -259,7 +261,8 @@ POLYWEATHER_BACKEND_URL=http://polyweather_web:8000 - `POLYWEATHER_STATE_STORAGE_MODE` 当前线上推荐直接使用 `sqlite`。 - `POLYWEATHER_EVENT_STORE=redis` 表示实时观测 patch 使用 Redis Stream 做短窗口 replay 和多 worker fanout;本地或单进程可改为 `sqlite`。 - `POLYWEATHER_REDIS_REQUIRED=true` 表示 Redis 不可用时后端启动失败,避免生产环境广播不可 replay 的实时事件;开发环境可设为 `false` 允许回退 SQLite。 -- `POLYWEATHER_PAYMENT_RPC_URLS` 支持逗号分隔多个 RPC;如果暂时只用单 RPC,也可以继续只配 `POLYWEATHER_PAYMENT_RPC_URL`。 +- `POLYWEATHER_PAYMENT_RPC_URLS` 支持默认链的逗号分隔多个 RPC;如果暂时只用单 RPC,也可以继续只配 `POLYWEATHER_PAYMENT_RPC_URL`。 +- `POLYWEATHER_PAYMENT_RPC_URLS_BY_CHAIN_JSON` 用于多链支付,例如同时支持 Polygon 和 Ethereum 主网 USDC。 - 机器人市场监控包含 `关键提醒` 与 `关注清单`:关键提醒逐城判断并受冷却控制,关注清单每轮先扫描完整城市列表,再按全局 Top N 推送;同一轮已经触发关键提醒的城市不会重复出现在关注清单里。 - `TELEGRAM_MARKET_FOCUS_DIGEST_INTERVAL_SEC` 表示主动推送间隔,默认 `1800` 秒(30 分钟)。 @@ -267,6 +270,32 @@ POLYWEATHER_BACKEND_URL=http://polyweather_web:8000 - 这层只负责把结构化信号改写成短摘要,不替代真实模型、机场锚点和结算逻辑。 +### 6.3 支付多链配置示例 + +当前生产推荐: + +- 默认链:Polygon `chain_id=137`,继续承载 checkout 合约支付。 +- 补充链:Ethereum Mainnet `chain_id=1`,正式支持 USDC 直转确认。 +- 前端创建支付 intent 时会提交用户选择的 `chain_id`;后端确认时按 intent 的链和 token 查询对应 RPC。 + +```env +POLYWEATHER_PAYMENT_ENABLED=true +POLYWEATHER_PAYMENT_CHAIN_ID=137 +POLYWEATHER_PAYMENT_RPC_URL=https://polygon-rpc.com +POLYWEATHER_PAYMENT_RPC_URLS=https://polygon-rpc.com,https://polygon-bor-rpc.publicnode.com +POLYWEATHER_PAYMENT_RPC_URLS_BY_CHAIN_JSON={"137":["https://polygon-rpc.com","https://polygon-bor-rpc.publicnode.com"],"1":["https://ethereum-rpc.example"]} +POLYWEATHER_PAYMENT_RECEIVER_CONTRACT=0x +POLYWEATHER_PAYMENT_DIRECT_RECEIVER_ADDRESS=0x +POLYWEATHER_PAYMENT_ACCEPTED_TOKENS_JSON=[{"code":"usdc_polygon","symbol":"USDC","name":"USDC on Polygon","chain_id":137,"chain_code":"polygon","chain_name":"Polygon","address":"0x3c499c542cef5e3811e1192ce70d8cc03d5c3359","decimals":6,"receiver_contract":"0x","direct_receiver_address":"0x","is_default":true},{"code":"usdc_ethereum","symbol":"USDC","name":"USDC on Ethereum","chain_id":1,"chain_code":"ethereum","chain_name":"Ethereum Mainnet","address":"0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48","decimals":6,"direct_receiver_address":"0x","supports_contract_checkout":false,"supports_direct_transfer":true,"explorer_tx_url":"https://etherscan.io/tx/{tx_hash}"}] +``` + +注意: + +- `POLYWEATHER_PAYMENT_CHAIN_ID` 只是默认链,不代表只支持这一条链。 +- `POLYWEATHER_PAYMENT_ACCEPTED_TOKENS_JSON` 里每个 token 必须有明确 `chain_id`。 +- Ethereum 行如果没有部署 checkout 合约,必须设置 `supports_contract_checkout=false`,前端会显示手动转账并阻止钱包合约支付。 +- 私有 RPC URL 带 API key 时应放入真实 `.env` 或密钥管理,不要提交。 + ### 6.5 机器人市场监控建议配置 这套配置围绕市场本身做两类推送: diff --git a/docs/FRONTEND_DEPLOYMENT_ZH.md b/docs/FRONTEND_DEPLOYMENT_ZH.md index 677631d0..1e26c1f4 100644 --- a/docs/FRONTEND_DEPLOYMENT_ZH.md +++ b/docs/FRONTEND_DEPLOYMENT_ZH.md @@ -1,6 +1,6 @@ # 前端部署配置(Vercel) -最后更新:`2026-05-28` +最后更新:`2026-05-29` 本文只覆盖 `frontend` 目录对应的 Next.js 前端部署。 @@ -127,12 +127,15 @@ NEXT_PUBLIC_POLYWEATHER_EAGER_CITY_SUMMARIES=false 1. 用户点击支付前,前端会重新请求 `/api/payments/config` 2. 若发现 `receiver_contract` 与页面旧状态不一致,会自动切换到最新地址 3. 若后端返回的 `tx_payload.to` 与最新 `receiver_contract` 不一致,会直接阻断支付 +4. 多链支付时,前端会展示后端返回的网络列表,并把用户选择的 `chain_id` 传给后端创建 intent +5. Ethereum 主网 USDC 当前走手动直转确认,前端不会把它当成 Polygon checkout 合约支付 这层防护的目的,是降低以下事故概率: - 用户使用长期未刷新的旧标签页 - 命中旧 deployment URL - 页面本地状态残留旧收款地址 +- 用户在钱包默认网络(例如 Ethereum)付款,但系统按 Polygon intent 查账 如果你变更过支付收款地址,建议同步执行: diff --git a/docs/SERVICES_ZH.md b/docs/SERVICES_ZH.md index 46dd82fb..d28c6466 100644 --- a/docs/SERVICES_ZH.md +++ b/docs/SERVICES_ZH.md @@ -1,6 +1,6 @@ # 外部服务依赖总览 -最后更新:`2026-05-28` +最后更新:`2026-05-29` 项目调用外部天气、鉴权、支付和实时事件服务。原则是:核心链路必须有明确健康检查;可选数据源不可拖垮已可用城市;实时事件层可从 Redis Stream 降级到 SQLite event log。 @@ -52,7 +52,8 @@ | --- | --- | --- | | MiMo (xiaomimimo) | 城市分析 AI 评论 | ✅ 当前使用 | | DeepSeek | AI fallback | 备用 | -| Polygon RPC | 链上支付、自动确认 | ✅ | +| Polygon RPC | checkout 合约支付、Polygon USDC / USDC.e 自动确认 | ✅ | +| Ethereum RPC | Ethereum 主网 USDC 直转确认 | ✅(启用多链支付时必须) | | WalletConnect | 前端钱包连接 | ⚠️ 未配 key 时钱包入口降级 | ## 运维口径 @@ -61,3 +62,4 @@ - 本地或单进程兜底:`POLYWEATHER_EVENT_STORE=sqlite`。 - Redis 只负责短窗口 replay 与多 worker fanout,不是长期天气历史库。 - DEB hourly consensus 依赖 Open-Meteo 多模型小时曲线;若上游限流,图表应保留已有 snapshot 和实测 patch,不把缺失模型误报为实测缺失。 +- 支付多链确认依赖 `POLYWEATHER_PAYMENT_RPC_URLS_BY_CHAIN_JSON`;如果启用 Ethereum 主网 USDC,必须配置 `chain_id=1` 的 RPC,否则用户提交 Ethereum tx hash 后无法自动确认。 diff --git a/docs/SUPABASE_SETUP_ZH.md b/docs/SUPABASE_SETUP_ZH.md index f96d6d9a..16c88e4c 100644 --- a/docs/SUPABASE_SETUP_ZH.md +++ b/docs/SUPABASE_SETUP_ZH.md @@ -1,12 +1,12 @@ # Supabase + 登录 + 支付接入说明(v1.8.1) -最后更新:`2026-03-14` +最后更新:`2026-05-29` ## 1. 目标 - 前端支持 Google 一键登录 + 邮箱注册/登录。 - 后端支持 Supabase JWT 鉴权。 -- 支持 Polygon 合约支付(USDC / USDC.e)并自动确认开通订阅。 +- 支持 Polygon 合约支付(USDC / USDC.e)和 Ethereum 主网 USDC 直转支付,并自动确认开通订阅。 ## 2. Supabase 控制台配置 @@ -66,20 +66,24 @@ SUPABASE_SERVICE_ROLE_KEY= SUPABASE_HTTP_TIMEOUT_SEC=8 POLYWEATHER_PAYMENT_ENABLED=true +# 默认链仍是 Polygon,因为当前 checkout 合约部署在 Polygon。 POLYWEATHER_PAYMENT_CHAIN_ID=137 POLYWEATHER_PAYMENT_RPC_URL=https://polygon-bor-rpc.publicnode.com +POLYWEATHER_PAYMENT_RPC_URLS_BY_CHAIN_JSON={"137":["https://polygon-bor-rpc.publicnode.com"],"1":["https://ethereum-rpc.example"]} POLYWEATHER_PAYMENT_RECEIVER_CONTRACT=0x +POLYWEATHER_PAYMENT_DIRECT_RECEIVER_ADDRESS=0x POLYWEATHER_PAYMENT_CONFIRMATIONS=2 POLYWEATHER_PAYMENT_INTENT_TTL_SEC=1800 POLYWEATHER_PAYMENT_WALLET_CHALLENGE_TTL_SEC=600 POLYWEATHER_PAYMENT_POLL_INTERVAL_SEC=4 POLYWEATHER_PAYMENT_MAX_WAIT_SEC=50 -# 支持双币种(示例) -POLYWEATHER_PAYMENT_ACCEPTED_TOKENS_JSON=[{"code":"usdc_e","symbol":"USDC.e","name":"USDC.e (PoS)","address":"0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174","decimals":6,"receiver_contract":"0x","is_default":true},{"code":"usdc","symbol":"USDC","name":"Native USDC","address":"0x3c499c542cef5e3811e1192ce70d8cc03d5c3359","decimals":6,"receiver_contract":"0x"}] +# 支持多链多币种(示例) +# Ethereum 主网 USDC 当前建议只开 direct transfer,不走 Polygon checkout 合约。 +POLYWEATHER_PAYMENT_ACCEPTED_TOKENS_JSON=[{"code":"usdc_polygon","symbol":"USDC","name":"USDC on Polygon","chain_id":137,"chain_code":"polygon","chain_name":"Polygon","address":"0x3c499c542cef5e3811e1192ce70d8cc03d5c3359","decimals":6,"receiver_contract":"0x","direct_receiver_address":"0x","is_default":true},{"code":"usdc_e_polygon","symbol":"USDC.e","name":"USDC.e on Polygon","chain_id":137,"chain_code":"polygon","chain_name":"Polygon","address":"0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174","decimals":6,"receiver_contract":"0x","direct_receiver_address":"0x"},{"code":"usdc_ethereum","symbol":"USDC","name":"USDC on Ethereum","chain_id":1,"chain_code":"ethereum","chain_name":"Ethereum Mainnet","address":"0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48","decimals":6,"direct_receiver_address":"0x","supports_contract_checkout":false,"supports_direct_transfer":true,"confirmations":2,"explorer_tx_url":"https://etherscan.io/tx/{tx_hash}"}] # 套餐(当前只保留月付) -POLYWEATHER_PAYMENT_PLAN_CATALOG_JSON={"pro_monthly":{"plan_id":101,"amount_usdc":"5","duration_days":30}} +POLYWEATHER_PAYMENT_PLAN_CATALOG_JSON={"pro_monthly":{"plan_id":101,"amount_usdc":"10","duration_days":30}} POLYWEATHER_PAYMENT_ALLOWED_PLAN_CODES=pro_monthly # 积分抵扣 @@ -100,10 +104,16 @@ POLYWEATHER_PAYMENT_CONFIRM_LOOP_ENABLED=true - `POST /api/payments/wallets/challenge` - `POST /api/payments/wallets/verify` 4. 支付流程: - - `POST /api/payments/intents` + - `POST /api/payments/intents`;多链时前端会带 `chain_id` 和 `token_address` - 发链上交易 - `POST /api/payments/intents/{id}/submit` - `POST /api/payments/intents/{id}/confirm` 5. 若前端显示 pending,轮询: - `GET /api/payments/intents/{id}` 6. 确认订阅:`/api/auth/me` 返回 `subscription_active=true`。 + +## 6. 多链支付口径 + +- Polygon 是默认链,仍支持钱包合约支付和手动直转确认。 +- Ethereum 主网 USDC 是正式支付链路,但当前建议只走手动直转:用户选择 Ethereum 后,前端展示收款钱包、金额、代币合约和 Etherscan 链接,用户提交 tx hash 后后端按 `intent.chain_id=1` 查询 Ethereum RPC。 +- 不要只依赖前端文案阻止错链付款;后端必须把每笔 intent 的 `chain_id`、`token_address`、`receiver_address` 落库,并在确认时按该链校验 `Transfer` 事件。 diff --git a/docs/deep-research-report.md b/docs/deep-research-report.md index 8385a5ec..f92b0e88 100644 --- a/docs/deep-research-report.md +++ b/docs/deep-research-report.md @@ -2,9 +2,9 @@ ## 执行摘要 -PolyWeather(仓库:`yangyuan-zhen/PolyWeather`)定位为**面向温度类结算预测市场(如 Polymarket 的温度结算合约)**的”生产级气象情报系统”,核心在于把多源天气观测/预报转化为**结算导向的概率桶(μ + bucket distribution)**;同时提供 Web 仪表盘与 Telegram Bot 两套交互入口,并包含 Polygon 链上 USDC/USDC.e 支付、自动补单与订阅/积分体系。项目 README 现明确仓库代码采用 `AGPL-3.0-only`,同时将品牌、商标、生产私有数据与运营阈值保留在代码许可证之外。 +PolyWeather(仓库:`yangyuan-zhen/PolyWeather`)定位为**面向温度类结算预测市场(如 Polymarket 的温度结算合约)**的”生产级气象情报系统”,核心在于把多源天气观测/预报转化为**结算导向的概率桶(μ + bucket distribution)**;同时提供 Web 仪表盘与 Telegram Bot 两套交互入口,并包含 Polygon 链上 USDC/USDC.e 支付、Ethereum 主网 USDC 直转确认、自动补单与订阅/积分体系。项目 README 现明确仓库代码采用 `AGPL-3.0-only`,同时将品牌、商标、生产私有数据与运营阈值保留在代码许可证之外。 -> **2026-05-28 更新(v1.8.1)**:终端图表已切换为 HTTP snapshot + SSE Patch + Redis Stream/SQLite replay 架构;DEB hourly consensus (`deb_hourly_consensus.v1`) 成为峰值窗口和 DEB 曲线展示的优先小时路径;README 产品截图迁移到 `frontend/public/static`。 +> **2026-05-29 更新**:支付 intent 已支持多链 `chain_id`,Polygon 继续承载 checkout 合约,Ethereum 主网 USDC 作为直转确认通道;终端图表继续使用 HTTP snapshot + SSE Patch + Redis Stream/SQLite replay 架构。 ## 项目概览 PolyWeather 的目标与范围在 README/README_ZH 中定义得较清楚:为温度结算市场提供气象情报(多源采集→融合→概率→对照市场报价),并提供“官方看板(Vercel 前端)+ VPS 后端 + Telegram Bot”。 @@ -15,7 +15,7 @@ DEB(Dynamic Error Balancing)基于过去 N 天模型误差(MAE)倒数加 趋势/概率引擎在 `trend_engine.py` 中实现:综合“集合预报区间→σ/μ→高温窗口→死盘判定→温度桶概率分布→边界提示”等,用于 bot 展示与 web 结构化数据输出。 **城市决策层(Scan Terminal / 结构化实况层)**:地图点击城市后加入城市决策卡,前端拉取 full detail、多模型区间、最新 METAR/官方站点/跑道观测,并通过 `/api/city/{name}/detail` 生成城市级结构化数据。终端图表使用 HTTP snapshot + SSE patch + Redis/SQLite replay,最高温中枢优先使用 DEB hourly consensus,再结合多模型中心、日内 pace 和当前实测。 **市场层(Polymarket 行情对照)**:*[v1.7.0 已移除]* 原先从 Gamma API 发现市场、从 CLOB 读取价格/盘口并计算”模型-市场差”,已于 2026-05-23 随 Polymarket 价格拉取层一并删除。当前 `market_scan` 返回空。 -**商业化与支付**:订阅(`Pro Monthly 10 USDC`)、积分抵扣、Polygon 链上收款合约(USDC/USDC.e),并提供“事件监听 + 周期确认”的自动补单机制。 +**商业化与支付**:订阅(`Pro Monthly 10 USDC`)、积分抵扣、Polygon 链上收款合约(USDC/USDC.e)、Ethereum 主网 USDC 直转确认,并提供“事件监听 + 周期确认”的自动补单机制。 **支持的数据集/数据源**:项目不是传统“训练数据集+模型训练”的机器学习仓库;其“数据集”本质是外部实时/预报 API 与站点观测数据。对外部数据的使用需要遵守来源方的访问与速率限制,例如 AviationWeather Data API 明确限制请求频率(含每分钟请求上限/建议降低频率与使用缓存文件)。 **许可证**:仓库根目录 `LICENSE` 当前为 `AGPL-3.0-only`。同时 README 与策略文档明确:品牌、商标、生产私有数据与运营策略不随代码许可证一并授权。 (插图:项目 README 中包含产品截图,可用于快速理解实时终端与 Telegram 推送形态) @@ -71,7 +71,7 @@ JSON[Legacy JSON files
migration/export/explicit fallback only] HKO[data.weather.gov.hk] CWA[opendata.cwa.gov.tw] SB[Supabase Auth/REST] - RPC[Polygon RPC] + RPC[Polygon / Ethereum RPC] end subgraph Payments diff --git a/docs/payments/PAYMENT_AUDIT_ZH.md b/docs/payments/PAYMENT_AUDIT_ZH.md index 025a0bfa..a2b1e730 100644 --- a/docs/payments/PAYMENT_AUDIT_ZH.md +++ b/docs/payments/PAYMENT_AUDIT_ZH.md @@ -1,6 +1,6 @@ # PolyWeather 支付审计与防护说明 -最后更新:`2026-03-21` +最后更新:`2026-05-29` ## 1. 当前已落地的防护 @@ -19,9 +19,9 @@ ### 事件确认边界 -- 后端只认链上 `OrderPaid` 事件。 +- 后端只认链上可验证事件:checkout 合约路径认 `OrderPaid`,直转路径认对应链上 USDC `Transfer`。 - 前端提交 intent 不会直接视为支付完成。 -- `confirm_loop` 会再次按链上交易与确认数校验 intent。 +- `confirm_loop` 会再次按 intent 的 `chain_id`、`token_address`、收款地址、金额与确认数校验链上交易。 - 若确认失败,当前会明确把 intent / transaction 落为失败态,而不是长期停留在 `submitted`。 当前已显式识别的失败原因包括: @@ -29,19 +29,23 @@ - `receiver_mismatch` - `sender_mismatch` - `event_mismatch` +- `token_mismatch` - `tx_reverted` ### RPC 多节点容灾 -- 支持 `POLYWEATHER_PAYMENT_RPC_URLS` +- 支持默认链 `POLYWEATHER_PAYMENT_RPC_URLS` +- 支持多链 `POLYWEATHER_PAYMENT_RPC_URLS_BY_CHAIN_JSON` - 格式示例: ```env POLYWEATHER_PAYMENT_RPC_URLS=https://polygon-rpc.com,https://polygon-bor-rpc.publicnode.com +POLYWEATHER_PAYMENT_RPC_URLS_BY_CHAIN_JSON={"137":["https://polygon-rpc.com","https://polygon-bor-rpc.publicnode.com"],"1":["https://ethereum-rpc.example"]} ``` - 启动时按顺序探活。 - 当前节点断连或收据查询失败时,会自动切换到下一个可用 RPC。 +- 多链支付确认不会用默认链硬查交易;每笔 intent 会按自己的 `chain_id` 选择 RPC。 ### 事件重放 @@ -87,8 +91,24 @@ python scripts/replay_payment_events.py --from-block 10000000 --to-block 1000100 - 已付款但未开通 - 打到旧收款地址 - 交易事件不匹配 +- 用户在钱包默认 Ethereum 网络付款,但旧系统只按 Polygon intent 查账 -## 2. 当前合约的授权边界 +## 2. 当前支付链路边界 + +当前支持两类链路: + +1. Polygon checkout 合约 +- 前端钱包支付会先切到 Polygon。 +- 后端创建 intent 后给出合约 `tx_payload`。 +- 确认时校验 `OrderPaid(orderId, payer, planId, amount, token)`。 + +2. Ethereum 主网 USDC 直转 +- 前端展示 Ethereum 网络、USDC 合约、收款钱包和金额。 +- 用户提交 tx hash 后,后端按 `intent.chain_id=1` 查询 Ethereum RPC。 +- 确认时校验 USDC `Transfer(from, to, amount)` 的 `to`、`token_address` 和金额。 +- 这条链路不依赖 Polygon checkout 合约,适合处理“用户钱包默认网络付款”的真实行为。 + +## 3. 当前合约的授权边界 合约源码: - [PolyWeatherCheckout.sol](/E:/web/PolyWeather/contracts/PolyWeatherCheckout.sol) @@ -110,7 +130,7 @@ python scripts/replay_payment_events.py --from-block 10000000 --to-block 1000100 4. 订单边界 - 同一个 `orderId` 只能成功支付一次 -## 3. 重入与重复支付判断 +## 4. 重入与重复支付判断 当前合约的 `pay` 逻辑顺序是: @@ -136,7 +156,7 @@ python scripts/replay_payment_events.py --from-block 10000000 --to-block 1000100 - **最小可用支付合约** - 不是“全功能强防护合约” -## 4. 当前静态审计结论 +## 5. 当前静态审计结论 已提供脚本: - [check_payment_contract_security.py](/E:/web/PolyWeather/scripts/check_payment_contract_security.py) @@ -160,7 +180,7 @@ python scripts/check_payment_contract_security.py - 是否使用 SafeERC20 - 是否在链上绑定套餐价格 -## 5. 当前主要剩余风险 +## 6. 当前主要剩余风险 1. 单地址 owner - 建议把 `owner` 迁移到多签钱包 @@ -175,7 +195,7 @@ python scripts/check_payment_contract_security.py - 当前使用 `IERC20.transferFrom` - 升级版合约更建议改为 OpenZeppelin `SafeERC20` -## 6. 推荐操作 +## 7. 推荐操作 ### 每次支付配置变更后 @@ -233,7 +253,7 @@ docker compose exec polyweather_web python scripts/reconcile_subscription_by_ema - 用户声称已付费但未开通 - 需要快速确认最近一笔 intent 是否能自动恢复 -## 7. 下一版合约建议 +## 8. 下一版合约建议 如果后续升级合约,优先级建议: diff --git a/frontend/README.md b/frontend/README.md index 90a2a617..1b8fe348 100644 --- a/frontend/README.md +++ b/frontend/README.md @@ -45,6 +45,7 @@ PolyWeather Pro 的生产前端工程。 - 城市决策卡展示结构化实况、模型区间、市场温度桶和模型-市场差,不再请求 AI 解读 - 市场价格层使用完整 `all_buckets` 匹配温度桶,并把 `模型-市场差` 解释为 `模型概率 - 市场隐含概率` - 概率区展示当前生产概率引擎输出(legacy 高斯或 EMOS),模型共识只作为辅助参考 +- 账户中心支付区支持后端下发的多链网络选择;Polygon 继续走 checkout 合约,Ethereum 主网 USDC 走手动直转确认 - 缓存桶状态与 summary cache hit/miss ## 本地开发 @@ -176,7 +177,8 @@ Ops: 2. 若 `receiver_contract` 已更新,先切到最新地址 3. 若后端返回的 `tx_payload.to` 与最新地址不一致,直接阻断支付 4. 仅允许在 `NEXT_PUBLIC_PAYMENT_ALLOWED_HOSTS` 白名单域名上创建 payment intent -5. 支付区会明确显示当前账号、付款钱包和收款合约,避免账号/钱包/地址混淆 +5. 支付区会明确显示当前账号、付款钱包、支付网络和收款合约/钱包,避免账号/钱包/地址/链混淆 +6. 多链支付时,前端会把用户选择的 `chain_id` 和 `token_address` 发给后端,由后端按 intent 的链确认交易 这意味着: @@ -206,4 +208,4 @@ Ops: 详见根目录策略文档:`docs/OPEN_CORE_POLICY.md` -最后更新:`2026-05-28` +最后更新:`2026-05-29` diff --git a/frontend/components/account/AccountCenter.tsx b/frontend/components/account/AccountCenter.tsx index 2b574d40..6e9a5b2e 100644 --- a/frontend/components/account/AccountCenter.tsx +++ b/frontend/components/account/AccountCenter.tsx @@ -124,6 +124,7 @@ export function AccountCenter() { boundWallets, walletAddress, selectedPlanCode, + selectedPaymentChainId, selectedTokenAddress, selectedWallet, providerMode, @@ -133,6 +134,7 @@ export function AccountCenter() { // Shared setters setSelectedTokenAddress, + setSelectedPaymentChainId, setSelectedWallet, setSelectedInjectedProviderKey, setProviderMode, @@ -149,6 +151,8 @@ export function AccountCenter() { selectedPaymentToken, selectedTokenLabel, availableTokenList, + availableChainList, + selectedPaymentChain, effectivePlanList, resolvedSelectedTokenAddress, paymentReceiverAddress, @@ -810,7 +814,7 @@ export function AccountCenter() { errorText={paymentError || undefined} infoText={paymentInfo || undefined} txHash={lastTxHash || undefined} - chainId={paymentConfig?.chain_id || 137} + chainId={selectedPaymentChainId || paymentConfig?.chain_id || 137} paymentTokenLabel={selectedTokenLabel} faqHref={SUBSCRIPTION_HELP_HREF} telegramGroupUrl="" @@ -959,7 +963,10 @@ export function AccountCenter() { + {availableChainList.length > 1 && ( +
+

+ {copy.paymentNetwork} +

+
+ {availableChainList.map((chain) => { + const active = chain.chain_id === selectedPaymentChainId; + return ( + + ); + })} +
+
+ )} {availableTokenList.length > 0 && (

@@ -984,7 +1042,7 @@ export function AccountCenter() { return (

- {copy.polygonChain} + {chainIdToDisplayName(w.chain_id)}