package weixin import ( "bytes" "crypto/tls" "errors" "fmt" "io" "net/http" "os" ) var ( ErrUserPaying = errors.New("USERPAYING") ) type WxpayPartnerConfig struct { MchId string AppId string ApiKeyV2 string CertFile string KeyFile string } type WxpayPartner struct { config *WxpayPartnerConfig stdClient *http.Client tlsClient *http.Client } type MicroPayRequest struct { SubMchId string SubAppId string GoodsName string OutTradeNo string TotalFee int64 AuthCode string ServerIp string Attach string } type ReversePayRequest struct { SubMchId string SubAppId string OutTradeNo string } func NewWxpayPartner(cfg *WxpayPartnerConfig) *WxpayPartner { if cfg.MchId != "" { } c := &WxpayPartner{ config: cfg, stdClient: &http.Client{}, } var err error if cfg.KeyFile != "" && cfg.CertFile != "" { certBody, _ := os.ReadFile(cfg.CertFile) keyBody, _ := os.ReadFile(cfg.KeyFile) c.tlsClient, err = c.withCert(certBody, keyBody) if err != nil { panic(err) } } return c } func (p *WxpayPartner) MicroPay(req *MicroPayRequest) error { params := make(Params) params.Set("appid", p.config.AppId) params.Set("mch_id", p.config.MchId) params.Set("sub_mch_id", req.SubMchId) params.Set("nonce_str", randomStr(32)) params.Set("body", req.GoodsName) params.Set("out_trade_no", req.OutTradeNo) params.Set("total_fee", req.TotalFee) params.Set("auth_code", req.AuthCode) if req.ServerIp == "" { params.Set("spbill_create_ip", "113.248.223.215") } else { params.Set("spbill_create_ip", req.ServerIp) } if req.Attach != "" { params.Set("attach", req.Attach) } if req.SubAppId != "" { params.Set("sub_appid", req.SubAppId) } params.Set("sign", params.SignMd5(p.config.ApiKeyV2)) reqUrl := "https://api.mch.weixin.qq.com/pay/micropay" body, err := p.post(reqUrl, params, false) if err != nil { return err } result := make(Params) result.Decode(body) if result.GetString("return_code") != "SUCCESS" { return errors.New(string(body)) } if result.GetString("result_code") != "SUCCESS" { if result.GetString("err_code") == "USERPAYING" { return ErrUserPaying } return fmt.Errorf("%s:%s", result.GetString("err_code"), result.GetString("err_code_des")) } return nil } func (p *WxpayPartner) Reverse(req *ReversePayRequest) error { params := make(Params) params.Set("appid", p.config.AppId) params.Set("mch_id", p.config.MchId) params.Set("sub_mch_id", req.SubMchId) params.Set("nonce_str", randomStr(32)) params.Set("out_trade_no", req.OutTradeNo) if req.SubAppId != "" { params.Set("sub_appid", req.SubAppId) } params.Set("sign", params.SignMd5(p.config.ApiKeyV2)) reqUrl := "https://api.mch.weixin.qq.com/secapi/pay/reverse" body, err := p.post(reqUrl, params, true) if err != nil { return err } result := make(Params) result.Decode(body) if result.GetString("return_code") != "SUCCESS" { return errors.New(string(body)) } if result.GetString("result_code") != "SUCCESS" { if result.GetString("err_code") == "USERPAYING" { return ErrUserPaying } return fmt.Errorf("%s:%s", result.GetString("err_code"), result.GetString("err_code_des")) } return nil } // 附着商户证书 func (c *WxpayPartner) withCert(cert, key []byte) (*http.Client, error) { tlsCert, err := tls.X509KeyPair(cert, key) if err != nil { return nil, err } conf := &tls.Config{ Certificates: []tls.Certificate{tlsCert}, } trans := &http.Transport{ TLSClientConfig: conf, } return &http.Client{ Transport: trans, }, nil } // 发送请求 func (c *WxpayPartner) post(url string, params Params, tls bool) ([]byte, error) { var httpc *http.Client if tls { if c.tlsClient == nil { return nil, errors.New("tls wepay is not initialized") } httpc = c.tlsClient } else { httpc = c.stdClient } buf := bytes.NewBuffer(params.Encode()) resp, err := httpc.Post(url, "application/xml; charset=utf-8", buf) if err != nil { return nil, err } body, err := io.ReadAll(resp.Body) if err != nil { return nil, err } return body, nil }