4.2 KiB
4.2 KiB
| name | description | version | author | platforms | metadata | |||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| unipay | Sage unipay 支付模块开发 — 微信Native/H5、转账邮件、QR生成、DSPY编写 | 1.0.0 | Hermes Agent |
|
|
unipay 支付模块开发
微信支付
Native 扫码 vs H5 跳转
- Native (
/v3/pay/transactions/native): 返回code_url(weixin://...),不能被浏览器直接打开。需要生成二维码让用户扫码。 - H5 (
/v3/pay/transactions/h5): 返回h5_url,浏览器可直接打开跳转。
二维码生成
# 在 init.py 的 create_payment 中,对 wechat 生成 QR:
if provider == 'wechat':
qrpath = await env.data2qrpath(res) # res = Native code_url
res = env.entire_url('/idfile') + '?path=' + env.quote(qrpath)
data2qrpath() 已注册在 ServerEnv,生成 files/ 目录下的 PNG,返回 webpath。
recharge.dspy 前端
微信返回 VBox+Image 显示二维码;支付宝/转账返回 NewWindow 跳转。
if params_kw.provider == 'wechat':
return {"widgettype": "VBox", ..., "subwidgets": [
{"widgettype": "Image", "options": {"url": url}},
{"widgettype": "Text", "options": {"text": "请使用微信扫描二维码完成支付"}}
]}
return {"widgettype": "NewWindow", "options": {"url": url, ...}}
转账邮件处理
邮件正则
# 金额: "交易金额:¥1.00" — 注意 ¥ 符号
match = re.search(r'交易金额\s*[::]\s*¥?\s*(\d+(?:\.\d+)?)', mail.body)
# 转账码: "摘要:1912412" — 7 位数字
match = re.search(r'摘要[::]\s*(\d{7})(?!\d)', mail.body)
check_transfer 逻辑
- provider.check_transfer(tcode, env) — 返回
(title, message) - 先查
transfercode(status='1')→ 已入账直接返回 - 扫描最近 2 天邮件,新→旧,遇旧邮件
break - 所有匹配 code 且
transfercode.status='0'的都入账 - DSPY 只调用
provider.check_transfer(tcode, env),逻辑全在 Python
后台轮询
add_startup(run)会阻塞服务启动(无限循环)- 改为手动按钮触发:
transfer_info.ui→ "我已完成转账" →transfer_check.dspy
EmailClient
在 transfer.py 中定义,DSPY 无法直接访问。必须在 Python 方法中封装逻辑,DSPY 通过 env.PROVIDERS['transfer'] 间接调用。
Provider 注册
所有 provider 通过 load_unipay() 注册到 PROVIDERS dict,然后暴露到 ServerEnv:
env.PROVIDERS = PROVIDERS
# DSPY 中通过 env.PROVIDERS.get('transfer') 访问
微信支付故障排查:APPID_MCHID_NOT_MATCH\n\n### 症状\n微信 Native 支付返回 APPID_MCHID_NOT_MATCH:\n\nException: ret={'code': 'APPID_MCHID_NOT_MATCH', 'message': 'appid和mch_id不匹配'}\n\n\n### 排查步骤\n\n1. 用 xxd 验证环境变量无隐藏字符:\n bash\n grep 'WXP_APPID' start.sh | xxd | head -3\n # 正常:3d 77 78 38 39 32 ... (=wx892...) → 无 BOM、无特殊空格\n \n\n2. 检查证书归属:\n bash\n openssl x509 -in apiclient_cert.pem -noout -subject -serial\n # subject 中 CN = <商户号> — 证书绑定商户号,非 appid\n # serial 必须匹配 WXP_SERIAL 环境变量\n \n\n3. 确认微信商户平台 AppID 绑定:\n - 登录 pay.weixin.qq.com → 产品中心 → AppID账号管理\n - 确认目标 appid 状态为"已授权"(非"待授权")\n - 同一商户号可绑定多个 appid(不同应用),每个都需独立授权\n\n4. 用已验证的 appid 对比测试:临时切到已知可用的 appid,确认 unipay 模块本身没问题,再排查 appid 绑定。\n\n### 常见根因\n- 环境变量配了正确的 appid 但该 appid 与商户号的绑定未在微信平台完成\n- 环境变量有不可见字符(BOM、全角空格)— xxd 可检出\n- 证书的商户号与请求中的 mchid 不一致(证书绑定商户,非 appid)\n\n## DSPY 编写规则
见 supplychain-pitfalls 技能。关键:
- 禁止 import
- 禁止 f-string(exec 上下文中
{}与模板语法冲突,报SyntaxError: '{' was never closed) - 用
+拼接字符串或.format()