package wechat import ( "crypto/md5" "crypto/rand" "encoding/hex" "encoding/xml" "fmt" "io" "net/http" "reflect" "sort" "strings" "time" ) // APIClient 微信支付API客户端 type APIClient struct { config Config client *http.Client } // NewAPIClient 创建微信支付API客户端 func NewAPIClient(config Config) *APIClient { return &APIClient{ config: config, client: &http.Client{ Timeout: 10 * time.Second, }, } } // UnifiedOrderRequest 统一下单请求参数 type UnifiedOrderRequest struct { AppID string `xml:"appid"` // 公众号ID MchID string `xml:"mch_id"` // 商户号 NonceStr string `xml:"nonce_str"` // 随机字符串 Body string `xml:"body"` // 商品描述 OutTradeNo string `xml:"out_trade_no"` // 商户订单号 TotalFee string `xml:"total_fee"` // 订单金额 SpbillCreateIP string `xml:"spbill_create_ip"` // 终端IP NotifyURL string `xml:"notify_url"` // 通知地址 TradeType string `xml:"trade_type"` // 交易类型 Sign string `xml:"sign"` // 签名 } // UnifiedOrderResponse 统一下单响应参数 type UnifiedOrderResponse struct { ReturnCode string `xml:"return_code"` // 返回状态码 ReturnMsg string `xml:"return_msg"` // 返回信息 ResultCode string `xml:"result_code"` // 业务结果 ErrCode string `xml:"err_code"` // 错误代码 ErrCodeDes string `xml:"err_code_des"` // 错误代码描述 PrepayID string `xml:"prepay_id"` // 预支付交易会话标识 TradeType string `xml:"trade_type"` // 交易类型 CodeURL string `xml:"code_url"` // 二维码链接 Sign string `xml:"sign"` // 签名 } // UnifiedOrder 统一下单 func (c *APIClient) UnifiedOrder(req *UnifiedOrderRequest) (*UnifiedOrderResponse, error) { // 设置公共参数 req.AppID = c.config.AppID req.MchID = c.config.MchID req.NonceStr = generateNonceStr() req.SpbillCreateIP = "127.0.0.1" // 客户端IP req.NotifyURL = c.config.NotifyURL req.TradeType = "NATIVE" // 生成二维码支付链接 // 生成签名 req.Sign = c.generateSign(req) // 构建XML请求 xmlData := buildXML(req) // 发送请求 resp, err := c.client.Post("https://api.mch.weixin.qq.com/pay/unifiedorder", "application/xml", strings.NewReader(xmlData)) if err != nil { return nil, fmt.Errorf("请求微信支付接口失败: %v", err) } defer resp.Body.Close() // 读取响应 body, err := io.ReadAll(resp.Body) if err != nil { return nil, fmt.Errorf("读取响应失败: %v", err) } // 解析响应 var result UnifiedOrderResponse if err := xml.Unmarshal(body, &result); err != nil { return nil, fmt.Errorf("解析响应失败: %v", err) } // 验证签名 if !c.verifySign(&result) { return nil, fmt.Errorf("签名验证失败") } return &result, nil } // generateSign 生成签名 func (c *APIClient) generateSign(v interface{}) string { // 将结构体转换为map params := make(map[string]string) val := reflect.ValueOf(v).Elem() typ := val.Type() for i := 0; i < val.NumField(); i++ { field := typ.Field(i) tag := field.Tag.Get("xml") if tag == "" || tag == "sign" { continue } params[tag] = fmt.Sprintf("%v", val.Field(i).Interface()) } // 按键排序 var keys []string for k := range params { if k != "sign" && params[k] != "" { keys = append(keys, k) } } sort.Strings(keys) // 拼接字符串 var builder strings.Builder for i, k := range keys { if i > 0 { builder.WriteString("&") } builder.WriteString(k) builder.WriteString("=") builder.WriteString(params[k]) } builder.WriteString("&key=") builder.WriteString(c.config.APIKey) // MD5加密 h := md5.New() h.Write([]byte(builder.String())) return strings.ToUpper(hex.EncodeToString(h.Sum(nil))) } // verifySign 验证签名 func (c *APIClient) verifySign(v interface{}) bool { // TODO: 实现签名验证逻辑 return true } // generateNonceStr 生成随机字符串 func generateNonceStr() string { const length = 32 bytes := make([]byte, length) rand.Read(bytes) return hex.EncodeToString(bytes) } // buildXML 构建XML请求 func buildXML(v interface{}) string { data, err := xml.Marshal(v) if err != nil { return "" } return string(data) }