feat: 新增富友支付 SDK(RSA 签名、GBK 编解码、XML 协议、回调验签)

- pkg/fuiou/types.go: WxPreCreateRequest/Response、NotifyRequest 等 XML 结构体
- pkg/fuiou/client.go: Client 结构体、NewClient、字典序+GBK+MD5+RSA 签名/验签、HTTP 请求
- pkg/fuiou/wxprecreate.go: WxPreCreate 方法,支持公众号 JSAPI(JSAPI)和小程序(LETPAY)
- pkg/fuiou/notify.go: VerifyNotify(GBK→UTF-8+XML 解析+RSA 验签)、BuildNotifyResponse

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
2026-03-16 23:28:42 +08:00
parent b0da71bd25
commit a308ee228b
4 changed files with 518 additions and 0 deletions

56
pkg/fuiou/wxprecreate.go Normal file
View File

@@ -0,0 +1,56 @@
package fuiou
import (
"fmt"
"go.uber.org/zap"
)
// WxPreCreate 微信预下单公众号JSAPI + 小程序)
// tradeType: "JSAPI"(公众号)或 "LETPAY"(小程序)
// subAppid: 公众号或小程序 AppID
// subOpenid: 用户在对应应用下的 OpenID
func (c *Client) WxPreCreate(orderNo, amount, goodsDesc, termIP, tradeType, subAppid, subOpenid string) (*WxPreCreateResponse, error) {
req := &WxPreCreateRequest{
Version: "1.0",
InsCd: c.InsCd,
MchntCd: c.MchntCd,
TermId: c.TermId,
RandomStr: generateRandomStr(),
MchntOrderNo: orderNo,
TradeType: tradeType,
OrderAmt: amount,
GoodsDesc: goodsDesc,
TermIp: termIP,
NotifyUrl: c.NotifyURL,
SubAppid: subAppid,
SubOpenid: subOpenid,
}
sign, err := c.Sign(req)
if err != nil {
return nil, fmt.Errorf("签名失败: %w", err)
}
req.Sign = sign
var resp WxPreCreateResponse
if err := c.DoRequest("/wxPreCreate", req, &resp); err != nil {
return nil, fmt.Errorf("请求富友失败: %w", err)
}
if resp.ResultCode != "000000" {
c.logger.Error("富友预下单失败",
zap.String("order_no", orderNo),
zap.String("result_code", resp.ResultCode),
zap.String("result_msg", resp.ResultMsg),
)
return nil, fmt.Errorf("富友预下单失败: %s", resp.ResultMsg)
}
c.logger.Info("富友预下单成功",
zap.String("order_no", orderNo),
zap.String("trade_type", tradeType),
)
return &resp, nil
}