有效天数问题,充值单回调问题

This commit is contained in:
2026-05-08 11:46:34 +08:00
parent 7c85a8cf29
commit a93acc0784
12 changed files with 402 additions and 87 deletions

View File

@@ -5,8 +5,10 @@ import (
"crypto/md5"
"crypto/rand"
"crypto/rsa"
"crypto/sha256"
"crypto/x509"
"encoding/base64"
"encoding/hex"
"encoding/pem"
"encoding/xml"
"fmt"
@@ -50,6 +52,9 @@ func NewClient(insCd, mchntCd, termId, apiURL, notifyURL, privateKeyBase64, publ
if err != nil {
return nil, fmt.Errorf("解析富友公钥失败: %w", err)
}
if rsaPublicKeysEqual(pubKey, &privKey.PublicKey) {
return nil, fmt.Errorf("富友公钥配置错误:当前 fy_public_key 与商户私钥匹配,请配置富友平台提供的验签公钥")
}
return &Client{
InsCd: insCd,
@@ -91,6 +96,13 @@ func (c *Client) Sign(data interface{}) (string, error) {
// Verify 验证签名
func (c *Client) Verify(data interface{}, sign string) error {
signStr := buildSignString(data)
return c.verifySignString(signStr, sign)
}
// verifySignString 使用指定签名原文验证 RSA 签名。
func (c *Client) verifySignString(signStr, sign string) error {
c.logger.Debug("富友验签原文", zap.String("sign_str", signStr))
c.logger.Debug("富友签名", zap.String("sign", sign))
gbkBytes, err := utf8ToGBK([]byte(signStr))
if err != nil {
@@ -104,7 +116,17 @@ func (c *Client) Verify(data interface{}, sign string) error {
return fmt.Errorf("Base64 解码签名失败: %w", err)
}
return rsa.VerifyPKCS1v15(c.publicKey, crypto.MD5, hash[:], sigBytes)
if err := rsa.VerifyPKCS1v15(c.publicKey, crypto.MD5, hash[:], sigBytes); err != nil {
c.logger.Error("富友验签失败诊断",
zap.Error(err),
zap.Int("signature_len", len(sigBytes)),
zap.Int("public_key_bits", c.publicKey.N.BitLen()),
zap.String("public_key_sha256", rsaPublicKeyFingerprint(c.publicKey)),
zap.Bool("public_key_matches_private_key", rsaPublicKeysEqual(c.publicKey, &c.privateKey.PublicKey)),
)
return err
}
return nil
}
// DoRequest 发送 HTTP 请求到富友
@@ -175,10 +197,13 @@ func (c *Client) DoRequest(path string, req interface{}, resp interface{}) error
}
// buildSignString 构建签名原文
// 提取非空字段(排除 sign 和 reserved_ 开头字段),按字典序拼接为 key=value&key=value
// 提取字段(排除 sign 和 reserved 开头字段),按字典序拼接为 key=value&key=value
func buildSignString(data interface{}) string {
fields := structToMap(data)
return buildSignStringFromMap(structToMap(data))
}
// buildSignStringFromMap 按富友规则将字段集合拼接为签名原文。
func buildSignStringFromMap(fields map[string]string) string {
// 按 key 字典序排列
keys := make([]string, 0, len(fields))
for k := range fields {
@@ -195,8 +220,8 @@ func buildSignString(data interface{}) string {
return strings.Join(pairs, "&")
}
// structToMap 通过反射提取结构体的 xml tag 和值
// 排除 sign 字段reserved_ 开头字段空值字段
// structToMap 通过反射提取结构体的 xml tag 和值
// 排除 sign 字段reserved 开头字段,保留空值字段参与签名。
func structToMap(data interface{}) map[string]string {
result := make(map[string]string)
@@ -226,8 +251,8 @@ func structToMap(data interface{}) map[string]string {
continue
}
// 排除 reserved_ 开头字段
if strings.HasPrefix(tag, "reserved_") {
// 排除 reserved 开头字段
if strings.HasPrefix(tag, "reserved") {
continue
}
@@ -342,3 +367,20 @@ func parseDERPublicKey(derBytes []byte) (*rsa.PublicKey, error) {
return rsaPub, nil
}
// rsaPublicKeyFingerprint 返回 RSA 公钥的 SHA256 指纹,避免日志输出完整密钥。
func rsaPublicKeyFingerprint(pub *rsa.PublicKey) string {
derBytes, err := x509.MarshalPKIXPublicKey(pub)
if err != nil {
return ""
}
sum := sha256.Sum256(derBytes)
return hex.EncodeToString(sum[:])
}
func rsaPublicKeysEqual(a, b *rsa.PublicKey) bool {
if a == nil || b == nil {
return false
}
return a.E == b.E && a.N.Cmp(b.N) == 0
}