From 5d7eca5de3633c9110c4319bdbc436087203f154 Mon Sep 17 00:00:00 2001 From: Blizzard Date: Fri, 17 Jul 2026 11:12:26 +0800 Subject: [PATCH] =?UTF-8?q?fix(billing):=20=E5=BE=AE=E4=BF=A1=E6=94=AF?= =?UTF-8?q?=E4=BB=98=E9=92=89=E6=AD=BB=E3=80=8C=E5=BE=AE=E4=BF=A1=E6=94=AF?= =?UTF-8?q?=E4=BB=98=E5=85=AC=E9=92=A5=E3=80=8D=E9=AA=8C=E7=AD=BE=E4=BD=93?= =?UTF-8?q?=E7=B3=BB=20=E2=80=94=E2=80=94=20=E5=95=86=E6=88=B7=202025-09?= =?UTF-8?q?=20=E5=BC=80=E6=88=B7=E6=B2=A1=E6=9C=89=E5=B9=B3=E5=8F=B0?= =?UTF-8?q?=E8=AF=81=E4=B9=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 用户拿旧项目代码对出来的真问题:我此前用 WithWechatPayAutoAuthCipher(平台证书 模式,APIv3 密钥自动下载平台证书验签),但 2024 起新注册商户只发「微信支付公钥」 (PUB_KEY_ID_ 开头)、没有平台证书——在该商户号上初始化/回调验签都会挂。 - 改 WithWechatPayPublicKeyAuthCipher(商户私钥+公钥ID+公钥文件);回调验签用 NewSHA256WithRSAPubkeyVerifier;平台证书模式不留双模式赘肉(YAGNI)。 - Config 增 public_key_path/public_key_id(必填,公钥文件同样只存路径); admin 卡片补两字段;env 兜底加 WECHAT_PUBLIC_KEY(_ID)。 - 顺手修 live 撞出的真 bug:sundynix_setting.value 是 varchar(255), 支付配置 JSON(含加密密钥)一条就超(SQLSTATE 22001)→ 改 text。 live:列类型已迁 text;缺公钥两项报「配置不全,缺: public_key_path, public_key_id」;GET 回显含新字段。go 6 包测试+tsc+41 vitest 全绿。 Co-Authored-By: Claude Opus 4.8 --- sundynix-admin/src/api.ts | 4 ++++ .../src/components/TopupChannels.tsx | 6 ++++- .../internal/handler/payment_admin.go | 6 +++++ sundynix-gateway/internal/payment/manager.go | 13 +++++++++-- sundynix-gateway/internal/payment/wechat.go | 22 ++++++++++++++----- sundynix-gateway/internal/store/setting.go | 2 +- 6 files changed, 43 insertions(+), 10 deletions(-) diff --git a/sundynix-admin/src/api.ts b/sundynix-admin/src/api.ts index 758c149..0b5bdc7 100644 --- a/sundynix-admin/src/api.ts +++ b/sundynix-admin/src/api.ts @@ -231,6 +231,8 @@ export interface WechatPayConfig { mchid: string; cert_serial: string; private_key_path: string; + public_key_path: string; // 微信支付公钥(2024 起新商户体系;本商户 2025-09 开户) + public_key_id: string; // PUB_KEY_ID_ 开头 appid: string; notify_url: string; has_apiv3_key: boolean; @@ -248,6 +250,8 @@ export async function saveWechatPay(body: { mchid: string; cert_serial: string; private_key_path: string; + public_key_path: string; + public_key_id: string; apiv3_key: string; appid: string; notify_url: string; diff --git a/sundynix-admin/src/components/TopupChannels.tsx b/sundynix-admin/src/components/TopupChannels.tsx index 4268cad..4d7eec1 100644 --- a/sundynix-admin/src/components/TopupChannels.tsx +++ b/sundynix-admin/src/components/TopupChannels.tsx @@ -29,7 +29,7 @@ export function TopupChannels() { // APIv3 密钥只写不回显(密文入库,与模型 API Key 同一把密钥加密); // 商户证书私钥文件放服务器磁盘,这里只填路径。 function WechatConfigBlock() { - const empty: WechatPayConfig = { mchid: "", cert_serial: "", private_key_path: "", appid: "", notify_url: "", has_apiv3_key: false }; + const empty: WechatPayConfig = { mchid: "", cert_serial: "", private_key_path: "", public_key_path: "", public_key_id: "", appid: "", notify_url: "", has_apiv3_key: false }; const [cfg, setCfg] = useState(empty); const [apiv3, setApiv3] = useState(""); // 留空=沿用已存 const [enabled, setEnabled] = useState(false); @@ -56,6 +56,8 @@ function WechatConfigBlock() { mchid: cfg.mchid, cert_serial: cfg.cert_serial, private_key_path: cfg.private_key_path, + public_key_path: cfg.public_key_path, + public_key_id: cfg.public_key_id, apiv3_key: apiv3, // 空串=后端沿用旧密钥 appid: cfg.appid, notify_url: cfg.notify_url, @@ -102,6 +104,8 @@ function WechatConfigBlock() { {field("API 证书序列号", "cert_serial", "5157F09E…")} {field("appid(公众号/小程序)", "appid", "wx88888888")} {field("商户私钥文件路径(服务器磁盘)", "private_key_path", "/etc/sundynix/wechat/apiclient_key.pem", "md:col-span-2")} + {field("公钥 ID(PUB_KEY_ID_ 开头)", "public_key_id", "PUB_KEY_ID_01…")} + {field("微信支付公钥文件路径(服务器磁盘)", "public_key_path", "/etc/sundynix/wechat/pub_key.pem", "md:col-span-2")}