From ad3bd91ec39bc78248277d1fa8e024426ad12862 Mon Sep 17 00:00:00 2001 From: zpc Date: Sun, 25 Jan 2026 19:52:40 +0800 Subject: [PATCH] 321 --- .kiro/settings/mcp.json | 6 +- .kiro/specs/wechat-pay-v3-upgrade/design.md | 630 ++++++++++++++++++ .../wechat-pay-v3-upgrade/requirements.md | 101 +++ .kiro/specs/wechat-pay-v3-upgrade/tasks.md | 154 +++++ honey_box/common/env.js | 4 +- honey_box/common/platform/BasePlatform.js | 26 + honey_box/common/request.js | 78 +-- honey_box/common/router.js | 78 ++- honey_box/pages/user/index.vue | 27 +- .../微信支付商户号/1738725801.txt | 1 + .../微信支付公钥/pub_key.pem | 9 + .../微信支付公钥/公钥ID.txt | 1 + .../解密回调APIV3密钥.txt | 1 + 13 files changed, 1037 insertions(+), 79 deletions(-) create mode 100644 .kiro/specs/wechat-pay-v3-upgrade/design.md create mode 100644 .kiro/specs/wechat-pay-v3-upgrade/requirements.md create mode 100644 .kiro/specs/wechat-pay-v3-upgrade/tasks.md create mode 100644 server/HoneyBox/微信支付商户号/1738725801.txt create mode 100644 server/HoneyBox/微信支付商户号/微信支付公钥/pub_key.pem create mode 100644 server/HoneyBox/微信支付商户号/微信支付公钥/公钥ID.txt create mode 100644 server/HoneyBox/微信支付商户号/解密回调APIv3密钥/解密回调APIV3密钥.txt diff --git a/.kiro/settings/mcp.json b/.kiro/settings/mcp.json index 19d6a71f..df158fef 100644 --- a/.kiro/settings/mcp.json +++ b/.kiro/settings/mcp.json @@ -46,7 +46,7 @@ "mysql": { "command": "node", "args": [ - "D:/CodeManage/HaniBlindBox/server/scripts/mysql-mcp-server/index.js" + "D:/outsource/HaniBlindBox/server/scripts/mysql-mcp-server/index.js" ], "env": { "MYSQL_HOST": "192.168.195.16", @@ -62,7 +62,7 @@ "sqlserver": { "command": "node", "args": [ - "D:/CodeManage/HaniBlindBox/server/scripts/mssql-mcp-server/index.js" + "D:/outsource/HaniBlindBox/server/scripts/mssql-mcp-server/index.js" ], "env": { "MSSQL_SERVER": "192.168.195.15", @@ -78,7 +78,7 @@ "admin-sqlserver": { "command": "node", "args": [ - "D:/CodeManage/HaniBlindBox/server/scripts/mssql-mcp-server/index.js" + "D:/outsource/HaniBlindBox/server/scripts/mssql-mcp-server/index.js" ], "env": { "MSSQL_SERVER": "192.168.195.15", diff --git a/.kiro/specs/wechat-pay-v3-upgrade/design.md b/.kiro/specs/wechat-pay-v3-upgrade/design.md new file mode 100644 index 00000000..9109e0c3 --- /dev/null +++ b/.kiro/specs/wechat-pay-v3-upgrade/design.md @@ -0,0 +1,630 @@ +# Design Document: 微信支付 V3 升级 + +## Overview + +本设计文档描述将微信支付从 V2 版本升级到 V3 版本的技术方案。V3 版本使用更安全的 RSA-SHA256 签名算法和 AES-256-GCM 加密,替代 V2 的 MD5 签名和 XML 格式。 + +### 核心变更 + +| 特性 | V2 版本 | V3 版本 | +|------|---------|---------| +| 数据格式 | XML | JSON | +| 签名算法 | MD5 | RSA-SHA256 | +| 认证方式 | API密钥 | 商户API证书 + 微信支付公钥 | +| 回调解密 | 无加密 | AES-256-GCM | + +## Architecture + +### 整体架构 + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ 小程序前端 │ +│ (honey_box/common/server/pay.js) │ +└─────────────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────┐ +│ API 层 │ +│ (HoneyBox.Api) │ +│ ┌─────────────────────────────────────────────────────────┐ │ +│ │ PayController │ │ +│ │ - CreatePayment() → 根据配置选择 V2/V3 │ │ +│ │ - NotifyCallback() → 自动识别 V2/V3 格式 │ │ +│ └─────────────────────────────────────────────────────────┘ │ +└─────────────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────┐ +│ Core 层 │ +│ (HoneyBox.Core) │ +│ ┌──────────────────────┐ ┌──────────────────────┐ │ +│ │ IWechatPayService │ │ IWechatPayV3Service │ (新增) │ +│ │ (V2 实现) │ │ (V3 实现) │ │ +│ └──────────────────────┘ └──────────────────────┘ │ +│ │ │ │ +│ └────────────┬───────────┘ │ +│ ▼ │ +│ ┌─────────────────────────────────────────────────────────┐ │ +│ │ IWechatPayConfigService │ │ +│ │ - GetMerchantByOrderNo() → 返回包含 PayVersion 的配置 │ │ +│ └─────────────────────────────────────────────────────────┘ │ +└─────────────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────┐ +│ 微信支付平台 │ +│ V2: https://api.mch.weixin.qq.com/pay/unifiedorder │ +│ V3: https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi │ +└─────────────────────────────────────────────────────────────────┘ +``` + +### 版本路由策略 + +``` +支付请求 → 获取商户配置 → 检查 PayVersion + │ + ┌───────────────┴───────────────┐ + ▼ ▼ + PayVersion = "V2" PayVersion = "V3" + │ │ + ▼ ▼ + WechatPayService.CreatePaymentAsync() WechatPayV3Service.CreateJsapiOrderAsync() + │ │ + ▼ ▼ + XML + MD5签名 JSON + RSA签名 +``` + +## Components and Interfaces + +### 1. 配置模型扩展 + +#### WeixinPayMerchant (扩展) + +```csharp +// 文件: HoneyBox.Admin.Business/Models/Config/ConfigModels.cs + +public class WeixinPayMerchant +{ + // 现有字段保持不变 + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; + + [JsonPropertyName("mch_id")] + public string MchId { get; set; } = string.Empty; + + [JsonPropertyName("order_prefix")] + public string OrderPrefix { get; set; } = string.Empty; + + [JsonPropertyName("api_key")] + public string? ApiKey { get; set; } + + [JsonPropertyName("is_enabled")] + public string? IsEnabled { get; set; } + + // ===== V3 新增字段 ===== + + /// + /// 支付版本: "V2" 或 "V3",默认 "V2" + /// + [JsonPropertyName("pay_version")] + public string PayVersion { get; set; } = "V2"; + + /// + /// APIv3 密钥(32位字符串) + /// + [JsonPropertyName("api_v3_key")] + public string? ApiV3Key { get; set; } + + /// + /// 商户API证书序列号 + /// + [JsonPropertyName("cert_serial_no")] + public string? CertSerialNo { get; set; } + + /// + /// 商户私钥文件路径 + /// + [JsonPropertyName("private_key_path")] + public string? PrivateKeyPath { get; set; } + + /// + /// 微信支付公钥ID + /// + [JsonPropertyName("wechat_public_key_id")] + public string? WechatPublicKeyId { get; set; } + + /// + /// 微信支付公钥文件路径 + /// + [JsonPropertyName("wechat_public_key_path")] + public string? WechatPublicKeyPath { get; set; } +} +``` + +#### WechatPayMerchantConfig (扩展) + +```csharp +// 文件: HoneyBox.Model/Models/Payment/PaymentModels.cs + +public class WechatPayMerchantConfig +{ + // 现有字段保持不变 + public string Name { get; set; } = string.Empty; + public string MchId { get; set; } = string.Empty; + public string AppId { get; set; } = string.Empty; + public string Key { get; set; } = string.Empty; + public string OrderPrefix { get; set; } = string.Empty; + public int Weight { get; set; } = 1; + public string NotifyUrl { get; set; } = string.Empty; + + // ===== V3 新增字段 ===== + public string PayVersion { get; set; } = "V2"; + public string? ApiV3Key { get; set; } + public string? CertSerialNo { get; set; } + public string? PrivateKeyPath { get; set; } + public string? WechatPublicKeyId { get; set; } + public string? WechatPublicKeyPath { get; set; } +} +``` + +### 2. V3 支付服务接口 + +```csharp +// 文件: HoneyBox.Core/Interfaces/IWechatPayV3Service.cs (新建) + +public interface IWechatPayV3Service +{ + /// + /// 创建 JSAPI 下单(小程序支付) + /// + Task CreateJsapiOrderAsync(WechatPayRequest request); + + /// + /// 查询订单状态 + /// + Task QueryOrderAsync(string orderNo); + + /// + /// 关闭订单 + /// + Task CloseOrderAsync(string orderNo); + + /// + /// 申请退款 + /// + Task RefundAsync(WechatPayV3RefundRequest request); + + /// + /// 验证回调签名 + /// + bool VerifyNotifySignature(string timestamp, string nonce, string body, string signature, string serialNo); + + /// + /// 解密回调数据 + /// + string DecryptNotifyResource(string ciphertext, string nonce, string associatedData, string apiV3Key); + + /// + /// 生成 V3 请求签名 + /// + string GenerateSignature(string method, string url, string timestamp, string nonce, string body, string privateKey); +} +``` + +### 3. V3 请求/响应模型 + +```csharp +// 文件: HoneyBox.Model/Models/Payment/WechatPayV3Models.cs (新建) + +/// +/// V3 JSAPI 下单请求 +/// +public class WechatPayV3JsapiRequest +{ + [JsonPropertyName("appid")] + public string AppId { get; set; } = string.Empty; + + [JsonPropertyName("mchid")] + public string MchId { get; set; } = string.Empty; + + [JsonPropertyName("description")] + public string Description { get; set; } = string.Empty; + + [JsonPropertyName("out_trade_no")] + public string OutTradeNo { get; set; } = string.Empty; + + [JsonPropertyName("notify_url")] + public string NotifyUrl { get; set; } = string.Empty; + + [JsonPropertyName("amount")] + public WechatPayV3Amount Amount { get; set; } = new(); + + [JsonPropertyName("payer")] + public WechatPayV3Payer Payer { get; set; } = new(); + + [JsonPropertyName("attach")] + public string? Attach { get; set; } +} + +/// +/// V3 金额信息 +/// +public class WechatPayV3Amount +{ + [JsonPropertyName("total")] + public int Total { get; set; } + + [JsonPropertyName("currency")] + public string Currency { get; set; } = "CNY"; +} + +/// +/// V3 支付者信息 +/// +public class WechatPayV3Payer +{ + [JsonPropertyName("openid")] + public string OpenId { get; set; } = string.Empty; +} + +/// +/// V3 下单响应 +/// +public class WechatPayV3JsapiResponse +{ + [JsonPropertyName("prepay_id")] + public string PrepayId { get; set; } = string.Empty; +} + +/// +/// V3 回调通知 +/// +public class WechatPayV3Notification +{ + [JsonPropertyName("id")] + public string Id { get; set; } = string.Empty; + + [JsonPropertyName("create_time")] + public string CreateTime { get; set; } = string.Empty; + + [JsonPropertyName("event_type")] + public string EventType { get; set; } = string.Empty; + + [JsonPropertyName("resource_type")] + public string ResourceType { get; set; } = string.Empty; + + [JsonPropertyName("resource")] + public WechatPayV3Resource Resource { get; set; } = new(); +} + +/// +/// V3 回调资源(加密数据) +/// +public class WechatPayV3Resource +{ + [JsonPropertyName("algorithm")] + public string Algorithm { get; set; } = string.Empty; + + [JsonPropertyName("ciphertext")] + public string Ciphertext { get; set; } = string.Empty; + + [JsonPropertyName("nonce")] + public string Nonce { get; set; } = string.Empty; + + [JsonPropertyName("associated_data")] + public string AssociatedData { get; set; } = string.Empty; +} + +/// +/// V3 解密后的支付结果 +/// +public class WechatPayV3PaymentResult +{ + [JsonPropertyName("appid")] + public string AppId { get; set; } = string.Empty; + + [JsonPropertyName("mchid")] + public string MchId { get; set; } = string.Empty; + + [JsonPropertyName("out_trade_no")] + public string OutTradeNo { get; set; } = string.Empty; + + [JsonPropertyName("transaction_id")] + public string TransactionId { get; set; } = string.Empty; + + [JsonPropertyName("trade_state")] + public string TradeState { get; set; } = string.Empty; + + [JsonPropertyName("trade_state_desc")] + public string TradeStateDesc { get; set; } = string.Empty; + + [JsonPropertyName("success_time")] + public string SuccessTime { get; set; } = string.Empty; + + [JsonPropertyName("payer")] + public WechatPayV3Payer Payer { get; set; } = new(); + + [JsonPropertyName("amount")] + public WechatPayV3PaymentAmount Amount { get; set; } = new(); +} + +/// +/// V3 支付金额(回调) +/// +public class WechatPayV3PaymentAmount +{ + [JsonPropertyName("total")] + public int Total { get; set; } + + [JsonPropertyName("payer_total")] + public int PayerTotal { get; set; } + + [JsonPropertyName("currency")] + public string Currency { get; set; } = "CNY"; +} + +/// +/// V3 退款请求 +/// +public class WechatPayV3RefundRequest +{ + public string OrderNo { get; set; } = string.Empty; + public string RefundNo { get; set; } = string.Empty; + public string? Reason { get; set; } + public int TotalAmount { get; set; } + public int RefundAmount { get; set; } +} + +/// +/// V3 查询结果 +/// +public class WechatPayV3QueryResult +{ + public bool Success { get; set; } + public string TradeState { get; set; } = string.Empty; + public string TradeStateDesc { get; set; } = string.Empty; + public string? TransactionId { get; set; } + public string? ErrorCode { get; set; } + public string? ErrorMessage { get; set; } +} + +/// +/// V3 关闭结果 +/// +public class WechatPayV3CloseResult +{ + public bool Success { get; set; } + public string? ErrorCode { get; set; } + public string? ErrorMessage { get; set; } +} + +/// +/// V3 退款结果 +/// +public class WechatPayV3RefundResult +{ + public bool Success { get; set; } + public string? RefundId { get; set; } + public string? Status { get; set; } + public string? ErrorCode { get; set; } + public string? ErrorMessage { get; set; } +} +``` + +## Data Models + +### 数据库配置存储 + +配置存储在 `config` 表中,key 为 `weixinpay_setting`,value 为 JSON 格式: + +```json +{ + "merchants": [ + { + "name": "商户名称", + "mch_id": "1738725801", + "order_prefix": "MYH", + "is_enabled": "1", + "api_key": "V2密钥(兼容)", + + "pay_version": "V3", + "api_v3_key": "d1cxc0vXCUH2984901DxddPJMYqcwcnd", + "cert_serial_no": "证书序列号", + "private_key_path": "certs/1738725801/apiclient_key.pem", + "wechat_public_key_id": "PUB_KEY_ID_0117387258012026012500291641000801", + "wechat_public_key_path": "certs/1738725801/pub_key.pem" + } + ] +} +``` + +### 证书文件存储 + +证书文件存储在服务器的 `certs` 目录下: + +``` +server/HoneyBox/certs/ +└── 1738725801/ # 商户号目录 + ├── apiclient_key.pem # 商户私钥 + └── pub_key.pem # 微信支付公钥 +``` + +## Correctness Properties + +*A property is a characteristic or behavior that should hold true across all valid executions of a system-essentially, a formal statement about what the system should do. Properties serve as the bridge between human-readable specifications and machine-verifiable correctness guarantees.* + +### Property 1: 配置序列化 Round-Trip + +*For any* 有效的 `WeixinPayMerchant` 配置对象(包含 V2 或 V3 字段),序列化为 JSON 后再反序列化,应该得到与原始对象等价的配置。 + +**Validates: Requirements 1.4, 1.5** + +### Property 2: V3 配置字段完整性 + +*For any* PayVersion 为 "V3" 的商户配置,当配置加载成功时,ApiV3Key、CertSerialNo、PrivateKeyPath、WechatPublicKeyId、WechatPublicKeyPath 字段应该都能正确读取(非空或有默认值)。 + +**Validates: Requirements 1.1, 1.2** + +### Property 3: V2 向后兼容性 + +*For any* PayVersion 为 "V2" 的商户配置,ApiKey 字段应该能正确读取,且 V3 字段不影响 V2 功能。 + +**Validates: Requirements 1.3** + +### Property 4: 版本路由正确性 + +*For any* 支付请求,当商户配置的 PayVersion 为 "V3" 时,应该调用 V3 接口;当 PayVersion 为 "V2" 时,应该调用 V2 接口。 + +**Validates: Requirements 3.1, 3.5** + +### Property 5: V3 请求签名正确性 + +*For any* V3 请求数据,使用相同的私钥和参数生成的签名应该是确定性的(相同输入产生相同输出),且签名格式符合 RSA-SHA256 规范。 + +**Validates: Requirements 3.3** + +### Property 6: V3 请求字段完整性 + +*For any* V3 JSAPI 下单请求,构建的请求体应该包含所有必要字段:appid、mchid、description、out_trade_no、notify_url、amount、payer。 + +**Validates: Requirements 3.2** + +### Property 7: V3 支付参数完整性 + +*For any* 成功的 V3 下单响应,返回给前端的支付参数应该包含:timeStamp、nonceStr、package、signType(RSA)、paySign。 + +**Validates: Requirements 3.4** + +### Property 8: 回调格式识别正确性 + +*For any* 支付回调请求,当请求体为 JSON 格式且包含 resource 字段时,应该使用 V3 解密流程;当请求体为 XML 格式时,应该使用 V2 解密流程。 + +**Validates: Requirements 4.1, 4.5** + +### Property 9: V3 回调解密 Round-Trip + +*For any* 有效的支付结果数据,使用 AES-256-GCM 加密后再解密,应该得到与原始数据等价的结果。 + +**Validates: Requirements 4.3** + +### Property 10: V3 回调签名验证 + +*For any* V3 回调请求,使用正确的微信支付公钥验证签名应该返回 true;使用错误的公钥或篡改的数据应该返回 false。 + +**Validates: Requirements 4.2** + +### Property 11: 回调响应格式正确性 + +*For any* 回调处理结果,V3 回调应该返回 JSON 格式响应,V2 回调应该返回 XML 格式响应。 + +**Validates: Requirements 4.6** + +### Property 12: 部分退款金额正确性 + +*For any* 部分退款请求,退款金额应该小于等于订单总金额,且退款请求中的金额字段应该正确设置。 + +**Validates: Requirements 7.4** + +## Error Handling + +### 配置错误处理 + +| 错误场景 | 处理方式 | +|---------|---------| +| V3 配置缺少必要字段 | 记录警告日志,回退到 V2 | +| 私钥文件不存在 | 抛出配置异常,阻止启动 | +| 公钥文件不存在 | 抛出配置异常,阻止启动 | +| APIv3 密钥格式错误 | 记录错误日志,返回配置错误 | + +### 支付错误处理 + +| 错误场景 | 处理方式 | +|---------|---------| +| V3 签名生成失败 | 记录错误日志,返回系统错误 | +| V3 下单请求失败 | 解析错误码,返回友好提示 | +| V3 回调验签失败 | 记录警告日志,返回失败响应 | +| V3 回调解密失败 | 记录错误日志,返回失败响应 | + +### 错误码映射 + +```csharp +private static readonly Dictionary V3ErrorMessages = new() +{ + { "PARAM_ERROR", "参数错误" }, + { "OUT_TRADE_NO_USED", "订单号已使用" }, + { "ORDER_NOT_EXIST", "订单不存在" }, + { "ORDER_CLOSED", "订单已关闭" }, + { "SIGN_ERROR", "签名错误" }, + { "MCH_NOT_EXISTS", "商户号不存在" }, + { "APPID_MCHID_NOT_MATCH", "AppID和商户号不匹配" }, + { "FREQUENCY_LIMITED", "请求频率超限" }, + { "SYSTEM_ERROR", "系统错误" } +}; +``` + +## Testing Strategy + +### 单元测试 + +1. **配置模型测试** + - 测试 V3 字段序列化/反序列化 + - 测试 V2 向后兼容性 + - 测试配置验证逻辑 + +2. **签名算法测试** + - 测试 RSA-SHA256 签名生成 + - 测试签名验证 + - 测试签名字符串构建 + +3. **加解密测试** + - 测试 AES-256-GCM 加密 + - 测试 AES-256-GCM 解密 + - 测试解密失败场景 + +### 属性测试 + +使用 FsCheck 进行属性测试: + +```csharp +// 配置 round-trip 测试 +[Property] +public Property ConfigRoundTrip() +{ + return Prop.ForAll( + Arb.From(), + config => + { + var json = JsonSerializer.Serialize(config); + var deserialized = JsonSerializer.Deserialize(json); + return config.Equals(deserialized); + }); +} + +// 签名确定性测试 +[Property] +public Property SignatureDeterministic() +{ + return Prop.ForAll( + Arb.From(), // method + Arb.From(), // url + Arb.From(), // body + (method, url, body) => + { + var sign1 = service.GenerateSignature(method, url, timestamp, nonce, body, privateKey); + var sign2 = service.GenerateSignature(method, url, timestamp, nonce, body, privateKey); + return sign1 == sign2; + }); +} +``` + +### 集成测试 + +1. **支付流程测试** + - 测试 V3 下单流程(使用 Mock) + - 测试 V3 回调处理 + - 测试版本路由逻辑 + +2. **后台配置测试** + - 测试配置保存和加载 + - 测试 V2/V3 切换 diff --git a/.kiro/specs/wechat-pay-v3-upgrade/requirements.md b/.kiro/specs/wechat-pay-v3-upgrade/requirements.md new file mode 100644 index 00000000..b6131f13 --- /dev/null +++ b/.kiro/specs/wechat-pay-v3-upgrade/requirements.md @@ -0,0 +1,101 @@ +# Requirements Document + +## Introduction + +将现有的微信支付从 V2 版本升级到 V3 版本,以支持更安全的 RSA-SHA256 签名和 AES-GCM 加密。当前系统使用 V2 版本(XML格式、MD5签名),需要升级到 V3 版本(JSON格式、RSA-SHA256签名)以满足微信支付的最新安全要求。 + +## Glossary + +- **WechatPay_V2**: 微信支付 V2 版本,使用 XML 数据格式和 MD5 签名算法 +- **WechatPay_V3**: 微信支付 V3 版本,使用 JSON 数据格式和 RSA-SHA256 签名算法 +- **APIv3_Key**: 微信支付 V3 版本的 API 密钥,用于 AES-GCM 解密回调通知 +- **Merchant_Private_Key**: 商户 API 私钥,用于请求签名 +- **Wechat_Public_Key**: 微信支付平台公钥,用于验证回调签名 +- **JSAPI_Payment**: 小程序/公众号内支付方式 +- **Payment_Callback**: 微信支付结果异步通知 +- **Config_System**: 后台配置管理系统,存储支付参数到数据库 + +## Requirements + +### Requirement 1: V3 配置模型扩展 + +**User Story:** As a 开发者, I want 后端配置模型支持 V3 字段, so that 系统能够存储和读取 V3 支付配置。 + +#### Acceptance Criteria + +1. WHEN 系统加载微信支付配置 THEN THE Config_System SHALL 支持读取 PayVersion 字段(值为 "V2" 或 "V3") +2. WHEN PayVersion 为 "V3" THEN THE Config_System SHALL 读取以下字段:ApiV3Key、CertSerialNo、PrivateKeyPath、WechatPublicKeyId、WechatPublicKeyPath +3. WHEN PayVersion 为 "V2" THEN THE Config_System SHALL 保持原有字段兼容(ApiKey) +4. WHEN 配置保存时 THEN THE Config_System SHALL 正确序列化 V3 字段到 JSON 格式 +5. WHEN 配置加载时 THEN THE Config_System SHALL 正确反序列化 V3 字段 + +### Requirement 2: 后台管理页面配置 + +**User Story:** As a 系统管理员, I want 在后台管理页面配置微信支付 V3 参数, so that 系统能够使用 V3 接口进行支付。 + +#### Acceptance Criteria + +1. WHEN 管理员打开微信支付配置页面 THEN THE Admin_System SHALL 显示「支付版本」选择项(V2/V3) +2. WHEN 管理员选择 V3 版本 THEN THE Admin_System SHALL 显示 V3 专属配置项:APIv3密钥、证书序列号、商户私钥路径、微信支付公钥ID、微信支付公钥路径 +3. WHEN 管理员选择 V2 版本 THEN THE Admin_System SHALL 隐藏 V3 配置项并显示 V2 配置项 +4. WHEN 管理员保存配置 THEN THE Admin_System SHALL 验证必填字段并保存到数据库 +5. WHEN 页面加载时 THEN THE Admin_System SHALL 正确回显已保存的配置值 + +### Requirement 3: V3 JSAPI 下单接口 + +**User Story:** As a 小程序用户, I want 使用 V3 接口发起支付, so that 完成商品购买。 + +#### Acceptance Criteria + +1. WHEN 用户发起支付请求且商户配置为 V3 THEN THE WechatPay_V3 SHALL 调用 V3 JSAPI 下单接口 +2. WHEN 构建 V3 请求 THEN THE WechatPay_V3 SHALL 使用 JSON 格式并包含:appid、mchid、description、out_trade_no、notify_url、amount、payer +3. WHEN 签名 V3 请求 THEN THE WechatPay_V3 SHALL 使用 RSA-SHA256 算法对请求进行签名 +4. WHEN 下单成功 THEN THE WechatPay_V3 SHALL 返回小程序调起支付所需参数:timeStamp、nonceStr、package、signType(RSA)、paySign +5. WHEN 商户配置为 V2 THEN THE WechatPay_V3 SHALL 回退到 V2 接口处理 + +### Requirement 4: V3 支付回调处理 + +**User Story:** As a 系统, I want 正确解密 V3 支付回调通知, so that 更新订单支付状态。 + +#### Acceptance Criteria + +1. WHEN 收到 V3 格式回调(JSON格式且包含 resource 字段)THEN THE Payment_Callback SHALL 使用 V3 解密流程 +2. WHEN 验证 V3 回调签名 THEN THE Payment_Callback SHALL 使用微信支付公钥验证 Wechatpay-Signature 头 +3. WHEN 解密 V3 回调数据 THEN THE Payment_Callback SHALL 使用 AES-256-GCM 算法和 APIv3 密钥解密 resource.ciphertext +4. WHEN 解密成功 THEN THE Payment_Callback SHALL 提取订单号和支付状态并更新订单 +5. WHEN 收到 V2 格式回调(XML格式)THEN THE Payment_Callback SHALL 使用 V2 解密流程 +6. WHEN 回调处理成功 THEN THE Payment_Callback SHALL 返回正确的响应格式(V3: JSON, V2: XML) + +### Requirement 5: V3 订单查询 + +**User Story:** As a 系统, I want 使用 V3 接口查询订单状态, so that 处理支付超时和异常情况。 + +#### Acceptance Criteria + +1. WHEN 查询订单且商户配置为 V3 THEN THE WechatPay_V3 SHALL 调用 V3 订单查询接口 +2. WHEN 构建查询请求 THEN THE WechatPay_V3 SHALL 使用商户订单号作为路径参数 +3. WHEN 查询成功 THEN THE WechatPay_V3 SHALL 解析返回的订单状态(SUCCESS、NOTPAY、CLOSED 等) +4. IF 查询失败 THEN THE WechatPay_V3 SHALL 返回错误信息并记录日志 + +### Requirement 6: V3 订单关闭 + +**User Story:** As a 系统, I want 使用 V3 接口关闭未支付订单, so that 释放库存和资源。 + +#### Acceptance Criteria + +1. WHEN 关闭订单且商户配置为 V3 THEN THE WechatPay_V3 SHALL 调用 V3 订单关闭接口 +2. WHEN 构建关闭请求 THEN THE WechatPay_V3 SHALL 使用商户订单号作为路径参数并包含 mchid +3. WHEN 关闭成功 THEN THE WechatPay_V3 SHALL 返回成功状态(HTTP 204) +4. IF 关闭失败 THEN THE WechatPay_V3 SHALL 返回错误信息并记录日志 + +### Requirement 7: V3 退款接口 + +**User Story:** As a 系统管理员, I want 使用 V3 接口发起退款, so that 处理用户退款请求。 + +#### Acceptance Criteria + +1. WHEN 发起退款且商户配置为 V3 THEN THE WechatPay_V3 SHALL 调用 V3 退款接口 +2. WHEN 构建退款请求 THEN THE WechatPay_V3 SHALL 包含:out_trade_no、out_refund_no、reason、notify_url、amount +3. WHEN 退款成功 THEN THE WechatPay_V3 SHALL 返回退款单号和状态 +4. THE WechatPay_V3 SHALL 支持部分退款(退款金额小于订单金额) +5. IF 退款失败 THEN THE WechatPay_V3 SHALL 返回错误信息并记录日志 diff --git a/.kiro/specs/wechat-pay-v3-upgrade/tasks.md b/.kiro/specs/wechat-pay-v3-upgrade/tasks.md new file mode 100644 index 00000000..5c675747 --- /dev/null +++ b/.kiro/specs/wechat-pay-v3-upgrade/tasks.md @@ -0,0 +1,154 @@ +# Implementation Plan: 微信支付 V3 升级 + +## Overview + +本实现计划将微信支付从 V2 升级到 V3,采用增量开发方式,确保 V2 功能不受影响。实现顺序:配置模型 → V3 服务 → 回调处理 → 后台管理页面。 + +## Tasks + +- [ ] 1. 扩展配置模型支持 V3 字段 + - [ ] 1.1 扩展 WeixinPayMerchant 模型添加 V3 字段 + - 在 `HoneyBox.Admin.Business/Models/Config/ConfigModels.cs` 中添加 V3 字段 + - 字段:PayVersion、ApiV3Key、CertSerialNo、PrivateKeyPath、WechatPublicKeyId、WechatPublicKeyPath + - _Requirements: 1.1, 1.2, 1.3_ + - [ ] 1.2 扩展 WechatPayMerchantConfig 模型添加 V3 字段 + - 在 `HoneyBox.Model/Models/Payment/PaymentModels.cs` 中添加对应字段 + - _Requirements: 1.1, 1.2_ + - [ ] 1.3 更新 WechatPayConfigService 支持 V3 配置映射 + - 在配置加载时映射 V3 字段 + - _Requirements: 1.4, 1.5_ + - [ ] 1.4 编写配置序列化 round-trip 属性测试 + - **Property 1: 配置序列化 Round-Trip** + - **Validates: Requirements 1.4, 1.5** + +- [ ] 2. 创建 V3 支付数据模型 + - [ ] 2.1 创建 WechatPayV3Models.cs 文件 + - 在 `HoneyBox.Model/Models/Payment/` 目录下创建 + - 包含:WechatPayV3JsapiRequest、WechatPayV3Amount、WechatPayV3Payer、WechatPayV3JsapiResponse + - _Requirements: 3.2_ + - [ ] 2.2 创建 V3 回调通知模型 + - 包含:WechatPayV3Notification、WechatPayV3Resource、WechatPayV3PaymentResult + - _Requirements: 4.3, 4.4_ + - [ ] 2.3 创建 V3 查询、关闭、退款结果模型 + - 包含:WechatPayV3QueryResult、WechatPayV3CloseResult、WechatPayV3RefundResult、WechatPayV3RefundRequest + - _Requirements: 5.3, 6.3, 7.3_ + +- [ ] 3. 实现 V3 支付服务核心功能 + - [ ] 3.1 创建 IWechatPayV3Service 接口 + - 在 `HoneyBox.Core/Interfaces/` 目录下创建 + - 定义:CreateJsapiOrderAsync、QueryOrderAsync、CloseOrderAsync、RefundAsync + - _Requirements: 3.1, 5.1, 6.1, 7.1_ + - [ ] 3.2 实现 V3 签名生成方法 + - 实现 RSA-SHA256 签名算法 + - 签名字符串格式:HTTP方法\nURL\n时间戳\n随机串\n请求体\n + - _Requirements: 3.3_ + - [ ] 3.3 编写签名确定性属性测试 + - **Property 5: V3 请求签名正确性** + - **Validates: Requirements 3.3** + - [ ] 3.4 实现 CreateJsapiOrderAsync 方法 + - 构建 V3 JSAPI 下单请求 + - 调用微信 V3 API + - 返回小程序支付参数 + - _Requirements: 3.2, 3.4_ + - [ ] 3.5 编写请求字段完整性属性测试 + - **Property 6: V3 请求字段完整性** + - **Validates: Requirements 3.2** + +- [ ] 4. Checkpoint - 确保 V3 下单功能测试通过 + - 运行所有测试,确保通过 + - 如有问题请询问用户 + +- [ ] 5. 实现 V3 回调处理 + - [ ] 5.1 实现回调签名验证方法 + - 使用微信支付公钥验证 Wechatpay-Signature 头 + - _Requirements: 4.2_ + - [ ] 5.2 实现 AES-256-GCM 解密方法 + - 解密 resource.ciphertext 字段 + - 使用 APIv3 密钥作为解密密钥 + - _Requirements: 4.3_ + - [ ] 5.3 编写解密 round-trip 属性测试 + - **Property 9: V3 回调解密 Round-Trip** + - **Validates: Requirements 4.3** + - [ ] 5.4 实现回调格式自动识别 + - JSON 格式且包含 resource 字段 → V3 流程 + - XML 格式 → V2 流程 + - _Requirements: 4.1, 4.5_ + - [ ] 5.5 编写回调格式识别属性测试 + - **Property 8: 回调格式识别正确性** + - **Validates: Requirements 4.1, 4.5** + - [ ] 5.6 更新 PaymentNotifyService 支持 V3 回调 + - 在现有回调处理中添加 V3 分支 + - _Requirements: 4.4, 4.6_ + +- [ ] 6. 实现 V3 订单查询和关闭 + - [ ] 6.1 实现 QueryOrderAsync 方法 + - 调用 V3 订单查询接口 + - 解析订单状态 + - _Requirements: 5.1, 5.2, 5.3_ + - [ ] 6.2 实现 CloseOrderAsync 方法 + - 调用 V3 订单关闭接口 + - 处理 HTTP 204 响应 + - _Requirements: 6.1, 6.2, 6.3_ + +- [ ] 7. 实现 V3 退款接口 + - [ ] 7.1 实现 RefundAsync 方法 + - 调用 V3 退款接口 + - 支持部分退款 + - _Requirements: 7.1, 7.2, 7.3, 7.4_ + - [ ] 7.2 编写部分退款金额属性测试 + - **Property 12: 部分退款金额正确性** + - **Validates: Requirements 7.4** + +- [ ] 8. 实现版本路由逻辑 + - [ ] 8.1 更新 WechatPayService 支持版本路由 + - 根据商户配置的 PayVersion 选择 V2 或 V3 服务 + - _Requirements: 3.1, 3.5_ + - [ ] 8.2 编写版本路由属性测试 + - **Property 4: 版本路由正确性** + - **Validates: Requirements 3.1, 3.5** + - [ ] 8.3 注册 V3 服务到依赖注入容器 + - 在 ServiceModule.cs 中注册 IWechatPayV3Service + - _Requirements: 3.1_ + +- [ ] 9. Checkpoint - 确保后端 V3 功能完整 + - 运行所有测试,确保通过 + - 如有问题请询问用户 + +- [ ] 10. 更新后台管理页面 + - [ ] 10.1 更新前端配置接口类型定义 + - 在 `admin-web/src/api/business/config.ts` 中添加 V3 字段 + - _Requirements: 2.1_ + - [ ] 10.2 更新微信商户配置表单组件 + - 在 `admin-web/src/views/business/config/components/WeixinMerchantForm.vue` 中添加 V3 配置项 + - 添加支付版本选择(V2/V3) + - 根据版本显示/隐藏对应配置项 + - _Requirements: 2.1, 2.2, 2.3_ + - [ ] 10.3 实现配置验证逻辑 + - V3 版本时验证必填字段 + - _Requirements: 2.4_ + - [ ] 10.4 测试配置保存和回显 + - 确保配置正确保存到数据库 + - 确保页面加载时正确回显 + - _Requirements: 2.4, 2.5_ + +- [ ] 11. 准备证书文件 + - [ ] 11.1 创建证书目录结构 + - 创建 `server/HoneyBox/certs/1738725801/` 目录 + - _Requirements: 1.2_ + - [ ] 11.2 解压并放置证书文件 + - 从 `微信支付商户号/商户API证书/` 解压获取 apiclient_key.pem + - 从 `微信支付商户号/微信支付公钥/` 复制 pub_key.pem + - _Requirements: 1.2_ + +- [ ] 12. Final Checkpoint - 完整功能验证 + - 运行所有测试,确保通过 + - 验证 V2 功能不受影响 + - 如有问题请询问用户 + +## Notes + +- 所有任务均为必需,包括属性测试任务 +- 每个任务都引用了具体的需求条款以便追溯 +- Checkpoint 任务用于阶段性验证 +- 属性测试验证通用正确性属性 +- 单元测试验证具体示例和边界情况 diff --git a/honey_box/common/env.js b/honey_box/common/env.js index 13f1ca9d..f192d080 100644 --- a/honey_box/common/env.js +++ b/honey_box/common/env.js @@ -11,8 +11,8 @@ // 测试环境配置 - .NET 10 后端 const testing = { - // baseUrl: 'https://app.zpc-xy.com/honey/api', - baseUrl: 'http://192.168.1.24:5238', + baseUrl: 'https://app.zpc-xy.com/honey/api', + // baseUrl: 'http://192.168.1.24:5238', imageUrl: 'https://youdas-1308826010.cos.ap-shanghai.myqcloud.com', loginPage: '', wxAppId: '' diff --git a/honey_box/common/platform/BasePlatform.js b/honey_box/common/platform/BasePlatform.js index 1789787e..0a0b09e9 100644 --- a/honey_box/common/platform/BasePlatform.js +++ b/honey_box/common/platform/BasePlatform.js @@ -212,6 +212,32 @@ class BasePlatform { * @param {Object} item 菜单项 */ navigateToPath(item) { + // 需要登录才能访问的页面路径 + const needLoginPaths = [ + '/pages/other/order_list', // 消费记录 + '/package/mine/collect', // 我的收藏 + '/pages/user/coupon', // 优惠券 + '/pages/user/tui-guang', // 邀请好友 + '/pages/user/cancel-account-page', // 注销账号 + ]; + + // 检查是否需要登录 + const needLogin = needLoginPaths.some(path => item.path.startsWith(path)); + if (needLogin) { + const token = uni.getStorageSync('token'); + if (!token) { + uni.setStorageSync('redirect', item.path); + uni.showToast({ + title: '请先登录', + icon: 'none' + }); + setTimeout(() => { + navigateTo('/pages/user/login'); + }, 100); + return; + } + } + navigateTo(item.path); } diff --git a/honey_box/common/request.js b/honey_box/common/request.js index a6c44d35..57de238f 100644 --- a/honey_box/common/request.js +++ b/honey_box/common/request.js @@ -400,19 +400,32 @@ class RequestManager { // 处理 HTTP 401 未授权(Token 过期或无效) if (res.statusCode === 401) { - console.log('Token过期或无效,尝试自动刷新'); + console.log('Token过期或无效'); - // 白名单接口不进行自动刷新,直接拒绝 + // 白名单接口直接返回错误,不处理 if (RequestManager.isUrlInWhitelist(requestUrl)) { reject({ status: -1, msg: '登录已过期' }); return; } + // 检查是否有 token(用户是否曾经登录过) + const currentToken = uni.getStorageSync('token'); + if (!currentToken) { + // 用户从未登录过,直接返回错误,不跳转 + console.log('用户未登录,返回错误'); + reject({ status: -1, msg: '请先登录' }); + return; + } + // 检查是否有 refreshToken const refreshToken = uni.getStorageSync('refreshToken'); if (!refreshToken) { - console.log('没有 refreshToken,直接跳转登录页'); - RequestManager.clearTokensAndRedirect(); + console.log('没有 refreshToken,清除token并返回错误'); + // 清除过期的token,但不跳转登录页 + uni.removeStorageSync('token'); + uni.removeStorageSync('accessToken'); + uni.removeStorageSync('tokenExpireTime'); + uni.removeStorageSync('userinfo'); reject({ status: -1, msg: '登录已过期' }); return; } @@ -443,9 +456,13 @@ class RequestManager { // 处理队列中的其他请求 RequestManager.processRefreshQueue(true); } else { - console.log('Token 刷新失败,跳转登录页'); - // 刷新失败,清除 Token 并跳转登录页 - RequestManager.clearTokensAndRedirect(); + console.log('Token 刷新失败'); + // 刷新失败,清除 Token,但不跳转登录页 + uni.removeStorageSync('token'); + uni.removeStorageSync('accessToken'); + uni.removeStorageSync('refreshToken'); + uni.removeStorageSync('tokenExpireTime'); + uni.removeStorageSync('userinfo'); reject({ status: -1, msg: '登录已过期' }); // 拒绝队列中的所有请求 @@ -455,7 +472,11 @@ class RequestManager { .catch(error => { console.error('Token 刷新异常:', error); RequestManager.isRefreshing = false; - RequestManager.clearTokensAndRedirect(); + uni.removeStorageSync('token'); + uni.removeStorageSync('accessToken'); + uni.removeStorageSync('refreshToken'); + uni.removeStorageSync('tokenExpireTime'); + uni.removeStorageSync('userinfo'); reject({ status: -1, msg: '登录已过期' }); RequestManager.processRefreshQueue(false); }); @@ -516,55 +537,22 @@ class RequestManager { } } - - // 获取当前页面路径和参数 - var currentPage = pages[pages.length - 1]; - if (currentPage) { - var currentRoute = currentPage.route; - var currentParams = currentPage.options || {}; - - // 只有非登录页面才保存重定向信息 - if (currentRoute && currentRoute !== 'pages/user/login') { - // 构建完整的重定向URL - var redirectPath = '/' + currentRoute; - - // 如果有参数,拼接参数 - if (Object.keys(currentParams).length > 0) { - var paramString = Object.keys(currentParams) - .map(key => `${encodeURIComponent(key)}=${encodeURIComponent(currentParams[key])}`) - .join('&'); - redirectPath += '?' + paramString; - } - - // 保存重定向URL到缓存 - console.log('保存重定向URL:', redirectPath); - uni.setStorageSync('redirect', redirectPath); - } - } - + // 白名单接口直接返回错误,不跳转登录页 console.log(requestUrl); if (RequestManager.isUrlInWhitelist(requestUrl)) { reject(res.data) return; } - setTimeout(() => { - uni.showToast({ - title: '请先登录', - icon: 'none' - }) - }, 100) + // 清除所有Token相关存储 uni.removeStorageSync('token'); uni.removeStorageSync('accessToken'); uni.removeStorageSync('refreshToken'); uni.removeStorageSync('tokenExpireTime'); uni.removeStorageSync('userinfo'); - // 使用新的路由守卫方法进行跳转 - RouterManager.navigateTo('/pages/user/login', {}, 'navigateTo') - .catch(err => { - console.error('登录页面跳转失败:', err); - }); + // 不再自动跳转登录页,只返回错误让调用方处理 + // 调用方可以根据业务需要决定是否跳转登录页 reject(res.data) } else { reject(res.data) diff --git a/honey_box/common/router.js b/honey_box/common/router.js index 9b9778c9..23d57150 100644 --- a/honey_box/common/router.js +++ b/honey_box/common/router.js @@ -98,29 +98,9 @@ export function routerTo(options) { url += (url.indexOf('?') === -1 ? '?' : '&') + queryString; } - // 检查是否需要登录 - const needLogin = !isInWhiteList(url); - - if (needLogin && !isLogin()) { - // 需要登录但未登录,跳转到登录页面 - // 先保存当前URL用于登录后重定向 - uni.setStorageSync('redirect', url); - - // 使用navigateTo而非redirectTo,保留页面栈 - uni.navigateTo({ - url: '/pages/user/login', - success: () => { - console.log('跳转到登录页面成功,保存的重定向地址:', url); - }, - fail: (err) => { - console.error('跳转到登录页面失败:', err); - reject(err); - } - }); - - // 拒绝当前的导航请求 - return reject(new Error('需要登录')); - } + // 移除路由层面的登录拦截 + // 允许用户浏览所有页面,只在需要执行特定操作时才要求登录 + // 登录检查应该在具体的业务操作中进行(如抽奖、下单等) // 根据type选择跳转方式 if (type) { @@ -251,5 +231,53 @@ export default { navigateBack, collectWhitePath, getCollectedWhitePaths, - clearCollectedWhitePaths -}; \ No newline at end of file + clearCollectedWhitePaths, + requireLogin, + isLogin +}; + +/** + * 检查登录状态,未登录则跳转登录页 + * 用于需要登录才能执行的操作(如抽奖、下单等) + * @param {String} message 提示消息,可选 + * @returns {Boolean} true表示已登录,false表示未登录(会跳转登录页) + */ +export function requireLogin(message = '请先登录') { + if (isLogin()) { + return true; + } + + // 保存当前页面用于登录后跳转 + const pages = getCurrentPages(); + const currentPage = pages[pages.length - 1]; + if (currentPage) { + const currentRoute = currentPage.route; + const currentParams = currentPage.options || {}; + + if (currentRoute && currentRoute !== 'pages/user/login') { + let redirectPath = '/' + currentRoute; + if (Object.keys(currentParams).length > 0) { + const paramString = Object.keys(currentParams) + .map(key => `${encodeURIComponent(key)}=${encodeURIComponent(currentParams[key])}`) + .join('&'); + redirectPath += '?' + paramString; + } + uni.setStorageSync('redirect', redirectPath); + } + } + + // 显示提示 + uni.showToast({ + title: message, + icon: 'none' + }); + + // 跳转登录页 + setTimeout(() => { + uni.navigateTo({ + url: '/pages/user/login' + }); + }, 100); + + return false; +} \ No newline at end of file diff --git a/honey_box/pages/user/index.vue b/honey_box/pages/user/index.vue index 55201c18..b58e6d84 100644 --- a/honey_box/pages/user/index.vue +++ b/honey_box/pages/user/index.vue @@ -51,25 +51,25 @@ - + {{ $config.getAppSetting('currency1_name') }} {{ formatNumber(userinfo.integral) }} - + {{ $config.getAppSetting('currency2_name') }} {{ formatNumber(userinfo.money2) }} - + {{ $config.getAppSetting('balance_name') }} - 充值 + 充值 {{ formatNumber(userinfo.money) }} @@ -367,6 +367,25 @@ }) } }, + /** + * 跳转到财务相关页面(需要登录) + * @param {String} url - 页面路径 + */ + toFinancePage(url) { + const token = uni.getStorageSync('token'); + if (!token) { + uni.setStorageSync('redirect', '/pages/user/index'); + uni.showToast({ + title: '请先登录', + icon: 'none' + }); + setTimeout(() => { + this.$c.nav('/pages/user/login'); + }, 100); + return; + } + this.$c.to({ url }); + }, /** * 格式化数字,如果小数部分是.00则只显示整数部分 * @param {Number|String} num - 要格式化的数字 diff --git a/server/HoneyBox/微信支付商户号/1738725801.txt b/server/HoneyBox/微信支付商户号/1738725801.txt new file mode 100644 index 00000000..b87733b8 --- /dev/null +++ b/server/HoneyBox/微信支付商户号/1738725801.txt @@ -0,0 +1 @@ +商户号:1738725801 \ No newline at end of file diff --git a/server/HoneyBox/微信支付商户号/微信支付公钥/pub_key.pem b/server/HoneyBox/微信支付商户号/微信支付公钥/pub_key.pem new file mode 100644 index 00000000..d32530b0 --- /dev/null +++ b/server/HoneyBox/微信支付商户号/微信支付公钥/pub_key.pem @@ -0,0 +1,9 @@ +-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0KeKMd6Yxovf4kPI0c1Q +Islyq9fi/Wg60dodzPNkRRoraqmqbbW7uQcKHkHvIZi5Z9fK8SGkezyhcjiR3o8z +uwnH5QiFuMw6P+1XB1koFfbxxCc6Eh0iuRI5BqNfyRwXwn9wIEUNwfF/SAPJGTkk +hCzViil3tOmnJDMxQUJitt4RsnL6BvQ3afWcm7oqt7MLlcIhIW8jAsSFeWPuZcW5 +Hj+o2udrTUaTRkw7AEsHr9xyePhsqYjGxbi9fTlghkUYnRUNikSydtQoHbGHP70Q +tz4HbPqH4gpsCqabPVuANFGH5a8uidOH3XKq2iPLggbPci1nFI8xMmHMaT88u/o5 +GQIDAQAB +-----END PUBLIC KEY----- diff --git a/server/HoneyBox/微信支付商户号/微信支付公钥/公钥ID.txt b/server/HoneyBox/微信支付商户号/微信支付公钥/公钥ID.txt new file mode 100644 index 00000000..dca63aa5 --- /dev/null +++ b/server/HoneyBox/微信支付商户号/微信支付公钥/公钥ID.txt @@ -0,0 +1 @@ +PUB_KEY_ID_0117387258012026012500291641000801 \ No newline at end of file diff --git a/server/HoneyBox/微信支付商户号/解密回调APIv3密钥/解密回调APIV3密钥.txt b/server/HoneyBox/微信支付商户号/解密回调APIv3密钥/解密回调APIV3密钥.txt new file mode 100644 index 00000000..2daa7419 --- /dev/null +++ b/server/HoneyBox/微信支付商户号/解密回调APIv3密钥/解密回调APIV3密钥.txt @@ -0,0 +1 @@ +d1cxc0vXCUH2984901DxddPJMYqcwcnd \ No newline at end of file