Files
junhong_cmp_fiber/pkg/fuiou/notify.go
huang 045aa4fa3a
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 7m5s
fix: 修复微信/富友支付回调配置加载错误
- WechatPayCallback 移除对永远为 nil 的单例 wechatPayment 的依赖,
  改为动态按 payment_config_id 加载创建订单时所用的精确配置
- PaymentV2Service 新增 VerifyCallback()(v2 XML 验签)和 PeekOrderNo()(不验签预解析)
- FuiouPayCallback 由 GetActiveConfig 改为 GetConfigForCallback,
  通过 ParseNotify 预解析订单号后精确加载配置,防止切换配置后旧订单验签失败
- wechat_config.Service 注入 assetRechargeStore/agentRechargeStore,
  新增 GetConfigForCallback():按订单号前缀查 payment_config_id,
  GetByIDUnscoped 加载配置(含已停用/软删除记录),找不到则回退激活配置
- 修复微信 v2 回调响应格式:从 JSON 改为 XML,避免微信持续重试
2026-03-31 19:17:45 +08:00

108 lines
3.0 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package fuiou
import (
"encoding/xml"
"fmt"
"net/url"
"strings"
)
// ParseNotify 解析回调通知(不验签)
// 用于在验签前提取订单号以确定使用哪套支付配置
func ParseNotify(rawBody []byte) (*NotifyRequest, error) {
content := string(rawBody)
if strings.HasPrefix(content, "req=") {
content = content[4:]
}
decoded, err := url.QueryUnescape(content)
if err != nil {
return nil, fmt.Errorf("URL 解码回调数据失败: %w", err)
}
utf8Bytes, err := GBKToUTF8([]byte(decoded))
if err != nil {
return nil, fmt.Errorf("GBK 转 UTF-8 失败: %w", err)
}
utf8Str := strings.Replace(string(utf8Bytes), `encoding="GBK"`, `encoding="UTF-8"`, 1)
var notify NotifyRequest
if err := xml.Unmarshal([]byte(utf8Str), &notify); err != nil {
return nil, fmt.Errorf("XML 解析回调数据失败: %w", err)
}
return &notify, nil
}
// VerifyNotify 验证回调通知并解析数据
// rawBody: 原始请求 body可能包含 req= 前缀的 URL 编码 GBK XML
func (c *Client) VerifyNotify(rawBody []byte) (*NotifyRequest, error) {
content := string(rawBody)
// 处理 req= 前缀(富友回调可能以 form 格式发送)
if strings.HasPrefix(content, "req=") {
content = content[4:]
}
// URL 解码
decoded, err := url.QueryUnescape(content)
if err != nil {
return nil, fmt.Errorf("URL 解码回调数据失败: %w", err)
}
// GBK → UTF-8
utf8Bytes, err := GBKToUTF8([]byte(decoded))
if err != nil {
return nil, fmt.Errorf("GBK 转 UTF-8 失败: %w", err)
}
// 替换编码声明
utf8Str := strings.Replace(string(utf8Bytes), `encoding="GBK"`, `encoding="UTF-8"`, 1)
// XML 解析
var notify NotifyRequest
if err := xml.Unmarshal([]byte(utf8Str), &notify); err != nil {
return nil, fmt.Errorf("XML 解析回调数据失败: %w", err)
}
// 验证签名
if err := c.Verify(&notify, notify.Sign); err != nil {
return nil, fmt.Errorf("回调签名验证失败: %w", err)
}
// 检查结果码
if notify.ResultCode != "000000" {
return &notify, fmt.Errorf("回调结果非成功: %s - %s", notify.ResultCode, notify.ResultMsg)
}
return &notify, nil
}
// BuildNotifySuccessResponse 构建成功响应GBK 编码的 XML
func BuildNotifySuccessResponse() []byte {
return buildNotifyResponse("000000", "success")
}
// BuildNotifyFailResponse 构建失败响应GBK 编码的 XML
func BuildNotifyFailResponse(msg string) []byte {
return buildNotifyResponse("999999", msg)
}
// buildNotifyResponse 构建回调响应 XMLGBK 编码)
func buildNotifyResponse(code, msg string) []byte {
resp := NotifyResponse{
ResultCode: code,
ResultMsg: msg,
}
xmlBytes, err := xml.Marshal(resp)
if err != nil {
return []byte(`<?xml version="1.0" encoding="GBK"?><xml><result_code>999999</result_code><result_msg>internal error</result_msg></xml>`)
}
xmlStr := `<?xml version="1.0" encoding="GBK"?>` + string(xmlBytes)
gbkBytes, err := utf8ToGBK([]byte(xmlStr))
if err != nil {
return []byte(xmlStr)
}
return gbkBytes
}