| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444 |
- package gopay
- import (
- "crypto/tls"
- "crypto/x509"
- "encoding/xml"
- "errors"
- "fmt"
- "io/ioutil"
- "strings"
- )
- type weChatClient struct {
- AppId string
- MchId string
- apiKey string
- baseURL string
- isProd bool
- }
- //初始化微信客户端 ok
- // appId:应用ID
- // mchId:商户ID
- // apiKey:API秘钥值
- // isProd:是否是正式环境
- func NewWeChatClient(appId, mchId, apiKey string, isProd bool) (client *weChatClient) {
- client = new(weChatClient)
- client.AppId = appId
- client.MchId = mchId
- client.apiKey = apiKey
- client.isProd = isProd
- return client
- }
- //提交付款码支付 ok
- // 文档地址:https://pay.weixin.qq.com/wiki/doc/api/micropay.php?chapter=9_10&index=1
- func (this *weChatClient) Micropay(body BodyMap) (wxRsp *WeChatMicropayResponse, err error) {
- var bytes []byte
- if this.isProd {
- //正式环境
- bytes, err = this.doWeChat(body, wx_Micropay)
- if err != nil {
- return nil, err
- }
- } else {
- bytes, err = this.doWeChat(body, wx_SanBox_Micropay)
- if err != nil {
- return nil, err
- }
- }
- wxRsp = new(WeChatMicropayResponse)
- err = xml.Unmarshal(bytes, wxRsp)
- if err != nil {
- return nil, fmt.Errorf("xml.Unmarshal:%v", err.Error())
- }
- return wxRsp, nil
- }
- //统一下单 ok
- // 文档地址:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1
- func (this *weChatClient) UnifiedOrder(body BodyMap) (wxRsp *WeChatUnifiedOrderResponse, err error) {
- var bytes []byte
- if this.isProd {
- //正式环境
- bytes, err = this.doWeChat(body, wx_UnifiedOrder)
- if err != nil {
- return nil, err
- }
- } else {
- body.Set("total_fee", 101)
- bytes, err = this.doWeChat(body, wx_SanBox_UnifiedOrder)
- if err != nil {
- return nil, err
- }
- }
- wxRsp = new(WeChatUnifiedOrderResponse)
- //fmt.Println("bytes:", string(bytes))
- err = xml.Unmarshal(bytes, wxRsp)
- if err != nil {
- //fmt.Println("xml.Unmarshal.Err:", err)
- return nil, fmt.Errorf("xml.Unmarshal:%v", err.Error())
- }
- return wxRsp, nil
- }
- //查询订单 ok
- // 文档地址:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_2
- func (this *weChatClient) QueryOrder(body BodyMap) (wxRsp *WeChatQueryOrderResponse, err error) {
- var bytes []byte
- if this.isProd {
- //正式环境
- bytes, err = this.doWeChat(body, wx_OrderQuery)
- if err != nil {
- return nil, err
- }
- } else {
- bytes, err = this.doWeChat(body, wx_SanBox_OrderQuery)
- if err != nil {
- return nil, err
- }
- }
- wxRsp = new(WeChatQueryOrderResponse)
- err = xml.Unmarshal(bytes, wxRsp)
- if err != nil {
- return nil, fmt.Errorf("xml.Unmarshal:%v", err.Error())
- }
- return wxRsp, nil
- }
- //关闭订单 ok
- // 文档地址:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_3
- func (this *weChatClient) CloseOrder(body BodyMap) (wxRsp *WeChatCloseOrderResponse, err error) {
- var bytes []byte
- if this.isProd {
- //正式环境
- bytes, err = this.doWeChat(body, wx_CloseOrder)
- if err != nil {
- return nil, err
- }
- } else {
- bytes, err = this.doWeChat(body, wx_SanBox_CloseOrder)
- if err != nil {
- return nil, err
- }
- }
- wxRsp = new(WeChatCloseOrderResponse)
- err = xml.Unmarshal(bytes, wxRsp)
- if err != nil {
- return nil, fmt.Errorf("xml.Unmarshal:%v", err.Error())
- }
- return wxRsp, nil
- }
- //撤销订单 ok
- // 文档地址:https://pay.weixin.qq.com/wiki/doc/api/micropay.php?chapter=9_11&index=3
- func (this *weChatClient) Reverse(body BodyMap, certFilePath, keyFilePath, pkcs12FilePath string) (wxRsp *WeChatReverseResponse, err error) {
- var bytes []byte
- if this.isProd {
- //正式环境
- pkcsPool := x509.NewCertPool()
- pkcs, err := ioutil.ReadFile(pkcs12FilePath)
- if err != nil {
- return nil, fmt.Errorf("ioutil.ReadFile:%v", err.Error())
- }
- pkcsPool.AppendCertsFromPEM(pkcs)
- certificate, err := tls.LoadX509KeyPair(certFilePath, keyFilePath)
- if err != nil {
- return nil, fmt.Errorf("tls.LoadX509KeyPair:%v", err.Error())
- }
- tlsConfig := new(tls.Config)
- tlsConfig.Certificates = []tls.Certificate{certificate}
- tlsConfig.RootCAs = pkcsPool
- tlsConfig.InsecureSkipVerify = true
- bytes, err = this.doWeChat(body, wx_Reverse, tlsConfig)
- if err != nil {
- return nil, err
- }
- } else {
- bytes, err = this.doWeChat(body, wx_SanBox_Reverse)
- if err != nil {
- return nil, err
- }
- }
- wxRsp = new(WeChatReverseResponse)
- err = xml.Unmarshal(bytes, wxRsp)
- if err != nil {
- return nil, fmt.Errorf("xml.Unmarshal:%v", err.Error())
- }
- return wxRsp, nil
- }
- //申请退款 ok
- // 文档地址:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_4
- func (this *weChatClient) Refund(body BodyMap, certFilePath, keyFilePath, pkcs12FilePath string) (wxRsp *WeChatRefundResponse, err error) {
- var bytes []byte
- if this.isProd {
- //正式环境
- pkcsPool := x509.NewCertPool()
- pkcs, err := ioutil.ReadFile(pkcs12FilePath)
- if err != nil {
- return nil, fmt.Errorf("ioutil.ReadFile:%v", err.Error())
- }
- pkcsPool.AppendCertsFromPEM(pkcs)
- certificate, err := tls.LoadX509KeyPair(certFilePath, keyFilePath)
- if err != nil {
- return nil, fmt.Errorf("tls.LoadX509KeyPair:%v", err.Error())
- }
- tlsConfig := new(tls.Config)
- tlsConfig.Certificates = []tls.Certificate{certificate}
- tlsConfig.RootCAs = pkcsPool
- tlsConfig.InsecureSkipVerify = true
- bytes, err = this.doWeChat(body, wx_Refund, tlsConfig)
- if err != nil {
- return nil, err
- }
- } else {
- bytes, err = this.doWeChat(body, wx_SanBox_Refund)
- if err != nil {
- return nil, err
- }
- }
- wxRsp = new(WeChatRefundResponse)
- err = xml.Unmarshal(bytes, wxRsp)
- if err != nil {
- return nil, fmt.Errorf("xml.Unmarshal:%v", err.Error())
- }
- return wxRsp, nil
- }
- //查询退款 ok
- // 文档地址:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_5
- func (this *weChatClient) QueryRefund(body BodyMap) (wxRsp *WeChatQueryRefundResponse, err error) {
- var bytes []byte
- if this.isProd {
- //正式环境
- bytes, err = this.doWeChat(body, wx_RefundQuery)
- if err != nil {
- return nil, err
- }
- } else {
- bytes, err = this.doWeChat(body, wx_SanBox_RefundQuery)
- if err != nil {
- return nil, err
- }
- }
- wxRsp = new(WeChatQueryRefundResponse)
- err = xml.Unmarshal(bytes, wxRsp)
- if err != nil {
- return nil, fmt.Errorf("xml.Unmarshal:%v", err.Error())
- }
- return wxRsp, nil
- }
- //下载对账单 ok
- // 文档地址:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_6
- func (this *weChatClient) DownloadBill(body BodyMap) (wxRsp string, err error) {
- var bytes []byte
- if this.isProd {
- //正式环境
- bytes, err = this.doWeChat(body, wx_DownloadBill)
- } else {
- bytes, err = this.doWeChat(body, wx_SanBox_DownloadBill)
- }
- wxRsp = string(bytes)
- if err != nil {
- return wxRsp, err
- }
- return wxRsp, nil
- }
- //下载资金账单 ok
- // 文档地址:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_18&index=7
- // 好像不支持沙箱环境,因为沙箱环境默认需要用MD5签名,但是此接口仅支持HMAC-SHA256签名
- func (this *weChatClient) DownloadFundFlow(body BodyMap, certFilePath, keyFilePath, pkcs12FilePath string) (wxRsp string, err error) {
- var bytes []byte
- if this.isProd {
- //正式环境
- pkcsPool := x509.NewCertPool()
- pkcs, err := ioutil.ReadFile(pkcs12FilePath)
- if err != nil {
- return null, fmt.Errorf("ioutil.ReadFile:%v", err.Error())
- }
- pkcsPool.AppendCertsFromPEM(pkcs)
- certificate, err := tls.LoadX509KeyPair(certFilePath, keyFilePath)
- if err != nil {
- return null, fmt.Errorf("tls.LoadX509KeyPair:%v", err.Error())
- }
- tlsConfig := new(tls.Config)
- tlsConfig.Certificates = []tls.Certificate{certificate}
- tlsConfig.RootCAs = pkcsPool
- tlsConfig.InsecureSkipVerify = true
- bytes, err = this.doWeChat(body, wx_DownloadFundFlow, tlsConfig)
- } else {
- bytes, err = this.doWeChat(body, wx_SanBox_DownloadFundFlow)
- }
- if err != nil {
- return null, err
- }
- wxRsp = string(bytes)
- return wxRsp, nil
- }
- //拉取订单评价数据
- // 文档地址:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_17&index=11
- // 好像不支持沙箱环境,因为沙箱环境默认需要用MD5签名,但是此接口仅支持HMAC-SHA256签名
- func (this *weChatClient) BatchQueryComment(body BodyMap, certFilePath, keyFilePath, pkcs12FilePath string) (wxRsp string, err error) {
- var bytes []byte
- if this.isProd {
- //正式环境
- body.Set("sign_type", SignType_HMAC_SHA256)
- pkcsPool := x509.NewCertPool()
- pkcs, err := ioutil.ReadFile(pkcs12FilePath)
- if err != nil {
- return null, fmt.Errorf("ioutil.ReadFile:%v", err.Error())
- }
- pkcsPool.AppendCertsFromPEM(pkcs)
- certificate, err := tls.LoadX509KeyPair(certFilePath, keyFilePath)
- if err != nil {
- return null, fmt.Errorf("tls.LoadX509KeyPair:%v", err.Error())
- }
- tlsConfig := new(tls.Config)
- tlsConfig.Certificates = []tls.Certificate{certificate}
- tlsConfig.RootCAs = pkcsPool
- tlsConfig.InsecureSkipVerify = true
- bytes, err = this.doWeChat(body, wx_BatchQueryComment, tlsConfig)
- } else {
- bytes, err = this.doWeChat(body, wx_SanBox_BatchQueryComment)
- }
- if err != nil {
- return null, err
- }
- wxRsp = string(bytes)
- return wxRsp, nil
- }
- //企业向微信用户个人付款
- // 文档地址:https://pay.weixin.qq.com/wiki/doc/api/tools/mch_pay.php?chapter=14_1
- // 注意:此方法未支持沙箱环境,默认正式环境,转账请慎重
- func (this *weChatClient) Transfer(body BodyMap, certFilePath, keyFilePath, pkcs12FilePath string) (wxRsp *WeChatTransfersResponse, err error) {
- var bytes []byte
- var sign string
- body.Set("mch_appid", this.AppId)
- body.Set("mchid", this.MchId)
- agent := HttpAgent()
- //正式环境
- pkcsPool := x509.NewCertPool()
- pkcs, err := ioutil.ReadFile(pkcs12FilePath)
- if err != nil {
- return nil, fmt.Errorf("ioutil.ReadFile:%v", err.Error())
- }
- pkcsPool.AppendCertsFromPEM(pkcs)
- certificate, err := tls.LoadX509KeyPair(certFilePath, keyFilePath)
- if err != nil {
- return nil, fmt.Errorf("tls.LoadX509KeyPair:%v", err.Error())
- }
- tlsConfig := new(tls.Config)
- tlsConfig.Certificates = []tls.Certificate{certificate}
- tlsConfig.RootCAs = pkcsPool
- tlsConfig.InsecureSkipVerify = true
- agent.TLSClientConfig(tlsConfig)
- //本地计算Sign
- sign = getWeChatReleaseSign(this.apiKey, SignType_MD5, body)
- body.Set("sign", sign)
- reqXML := generateXml(body)
- if this.baseURL != null {
- agent.Post(this.baseURL + wx_Transfers)
- } else {
- agent.Post(wx_base_url_ch + wx_Transfers)
- }
- agent.Type("xml")
- agent.SendString(reqXML)
- _, bytes, errs := agent.EndBytes()
- if len(errs) > 0 {
- return nil, errs[0]
- }
- wxRsp = new(WeChatTransfersResponse)
- err = xml.Unmarshal(bytes, wxRsp)
- if err != nil {
- return nil, fmt.Errorf("xml.Unmarshal:%v", err.Error())
- }
- return wxRsp, nil
- }
- //向微信发送请求 ok
- func (this *weChatClient) doWeChat(body BodyMap, path string, tlsConfig ...*tls.Config) (bytes []byte, err error) {
- var sign string
- body.Set("appid", this.AppId)
- body.Set("mch_id", this.MchId)
- //===============生成参数===================
- if body.Get("sign") != null {
- goto GoRequest
- }
- //计算Sign值
- if !this.isProd {
- //沙箱环境
- body.Set("sign_type", SignType_MD5)
- //沙箱环境Sign值
- sign, err = getWeChatSignBoxSign(this.MchId, this.apiKey, body)
- if err != nil {
- //fmt.Println("getWeChatSignBoxSign:", err)
- return nil, err
- }
- } else {
- //正式环境
- sign = getWeChatReleaseSign(this.apiKey, body.Get("sign_type"), body)
- }
- body.Set("sign", sign)
- GoRequest:
- reqXML := generateXml(body)
- //fmt.Println("reqXML:",reqXML)
- //===============发起请求===================
- agent := HttpAgent()
- if this.isProd && tlsConfig != nil {
- agent.TLSClientConfig(tlsConfig[0])
- }
- if this.baseURL != null {
- agent.Post(this.baseURL + path)
- } else {
- agent.Post(wx_base_url_ch + path)
- }
- agent.Type("xml")
- agent.SendString(reqXML)
- res, bytes, errs := agent.EndBytes()
- if len(errs) > 0 {
- //fmt.Println("errs[0]:", errs[0])
- return nil, errs[0]
- }
- //fmt.Println("res:", res.StatusCode)
- if res.StatusCode != 200 {
- return nil, fmt.Errorf("HTTP Request Error, StatusCode = %v", res.StatusCode)
- }
- if strings.Contains(string(bytes), "HTML") {
- return nil, errors.New(string(bytes))
- }
- //fmt.Println("bytes:", string(bytes))
- return bytes, nil
- }
|