diff --git a/README.md b/README.md index cf3f243a..98b31547 100644 --- a/README.md +++ b/README.md @@ -94,7 +94,8 @@ * gopay.AliPaySystemOauthToken() => 换取授权访问令牌(得到access_token,user_id等信息) * gopay.FormatPrivateKey() => 格式化应用私钥 * gopay.FormatAliPayPublicKey() => 格式化支付宝公钥 -* gopay.ParseAliPayNotifyResult() => 解析并返回支付宝支付异步通知的参数 +* gopay.ParseAliPayNotifyResult() => 解析支付宝支付异步通知的参数到Struct +* gopay.ParseAliPayNotifyResultToBodyMap() => 解析支付宝支付异步通知的参数到BodyMap * gopay.VerifyAliPaySign() => 支付宝同步返回参数验签或异步通知参数验签 * gopay.DecryptAliPayOpenDataToStruct() => 支付宝小程序敏感加密数据解析到结构体 @@ -145,28 +146,42 @@ QQ群: --- -## 1、初始化GoPay客户端并做配置(所有https请求,已忽略https双向认证:InsecureSkipVerify: true) +## 1、初始化GoPay客户端并做配置(HTTP请求均设置tls.Config{InsecureSkipVerify: true}) -* #### 微信客户端,如无需更改Appid、Mchid和ApiKey,推荐在 init()方法中初始化,全局适用 +* #### 微信客户端,如无需更改Appid、Mchid和ApiKey等参数,可在init()方法中初始化,全局适用 微信官方文档:[官方文档](https://pay.weixin.qq.com/wiki/doc/api/index.html) ```go -//初始化微信客户端 +// 初始化微信客户端 // appId:应用ID // mchId:商户ID // apiKey:API秘钥值 // isProd:是否是正式环境 client := gopay.NewWeChatClient("wxdaa2ab9ef87b5497", mchId, apiKey, false) -//设置国家:不设置默认 中国国内 +// 设置国家:不设置默认 中国国内 // gopay.China:中国国内 // gopay.China2:中国国内备用 // gopay.SoutheastAsia:东南亚 // gopay.Other:其他国家 client.SetCountry(gopay.China) + +// 添加微信证书 Byte 数组 +// certFile:apiclient_cert.pem byte数组 +// keyFile:apiclient_key.pem byte数组 +// pkcs12File:apiclient_cert.p12 byte数组 +client.AddCertFileByte() + +// 添加微信证书 Path 路径 +// certFilePath:apiclient_cert.pem 路径 +// keyFilePath:apiclient_key.pem 路径 +// pkcs12FilePath:apiclient_cert.p12 路径 +// 返回err +client.AddCertFilePath() + ``` -* #### 支付宝,如无需更改Appid和PrivateKey,推荐在 init()方法中初始化,全局适用 +* #### 支付宝,如无需更改Appid和PrivateKey等参数,可在init()方法中初始化,全局适用 支付宝官方文档:[官方文档](https://docs.open.alipay.com/catalog) diff --git a/alipay_client.go b/alipay_client.go index b46bfbd5..57d0335f 100644 --- a/alipay_client.go +++ b/alipay_client.go @@ -5,6 +5,7 @@ import ( "errors" "fmt" "strings" + "sync" "time" "github.com/parnurzeal/gorequest" @@ -22,6 +23,7 @@ type AliPayClient struct { AppAuthToken string AuthToken string IsProd bool + mu sync.RWMutex } // 初始化支付宝客户端 diff --git a/alipay_params.go b/alipay_params.go index 0599a628..de419a3a 100644 --- a/alipay_params.go +++ b/alipay_params.go @@ -39,58 +39,74 @@ type OpenApiRoyaltyDetailInfoPojo struct { // 设置 应用公钥证书SN // appCertSN:应用公钥证书SN,通过 gopay.GetCertSN() 获取 func (a *AliPayClient) SetAppCertSN(appCertSN string) (client *AliPayClient) { + a.mu.Lock() a.AppCertSN = appCertSN + a.mu.Unlock() return a } // 设置 支付宝根证书SN // alipayRootCertSN:支付宝根证书SN,通过 gopay.GetCertSN() 获取 func (a *AliPayClient) SetAliPayRootCertSN(alipayRootCertSN string) (client *AliPayClient) { + a.mu.Lock() a.AlipayRootCertSN = alipayRootCertSN + a.mu.Unlock() return a } // 设置支付后的ReturnUrl func (a *AliPayClient) SetReturnUrl(url string) (client *AliPayClient) { + a.mu.Lock() a.ReturnUrl = url + a.mu.Unlock() return a } // 设置支付宝服务器主动通知商户服务器里指定的页面http/https路径。 func (a *AliPayClient) SetNotifyUrl(url string) (client *AliPayClient) { + a.mu.Lock() a.NotifyUrl = url + a.mu.Unlock() return a } // 设置编码格式,如utf-8,gbk,gb2312等,默认推荐使用 utf-8 func (a *AliPayClient) SetCharset(charset string) (client *AliPayClient) { + a.mu.Lock() if charset == null { a.Charset = "utf-8" } else { a.Charset = charset } + a.mu.Unlock() return a } // 设置签名算法类型,目前支持RSA2和RSA,默认推荐使用 RSA2 func (a *AliPayClient) SetSignType(signType string) (client *AliPayClient) { + a.mu.Lock() if signType == null { a.SignType = "RSA2" } else { a.SignType = signType } + a.mu.Unlock() return a } // 设置应用授权 func (a *AliPayClient) SetAppAuthToken(appAuthToken string) (client *AliPayClient) { + a.mu.Lock() a.AppAuthToken = appAuthToken + a.mu.Unlock() return a } // 设置用户信息授权 func (a *AliPayClient) SetAuthToken(authToken string) (client *AliPayClient) { + a.mu.Lock() a.AuthToken = authToken + a.mu.Unlock() return a } diff --git a/alipay_service_api.go b/alipay_service_api.go index 5d7c8d6e..0be625a1 100644 --- a/alipay_service_api.go +++ b/alipay_service_api.go @@ -34,7 +34,7 @@ var allowSignatureAlgorithm = map[string]bool{ "SHA512-RSAPSS": true, } -// 解析支付宝支付异步通知的结果到BodyMap +// 解析支付宝支付异步通知的参数到BodyMap // req:*http.Request // 返回参数bm:Notify请求的参数 // 返回参数err:错误信息 @@ -53,7 +53,7 @@ func ParseAliPayNotifyResultToBodyMap(req *http.Request) (bm BodyMap, err error) return } -// ParseAliPayNotifyResult 解析支付宝支付完成后的Notify信息 +// 解析支付宝支付异步通知的参数到Struct // req:*http.Request // 返回参数notifyReq:Notify请求的参数 // 返回参数err:错误信息 diff --git a/constant.go b/constant.go index 69be7274..e726f703 100644 --- a/constant.go +++ b/constant.go @@ -4,7 +4,7 @@ const ( null string = "" TimeLayout string = "2006-01-02 15:04:05" DateLayout string = "2006-01-02" - Version string = "1.4.3" + Version string = "1.4.4" // 微信 // =========================================================================================== diff --git a/examples/wechat/wx_Transfer.go b/examples/wechat/wx_Transfer.go index ba5b1761..163245d3 100644 --- a/examples/wechat/wx_Transfer.go +++ b/examples/wechat/wx_Transfer.go @@ -1,8 +1,3 @@ -//================================== -// * Name:Jerry -// * DateTime:2019/8/21 21:02 -// * Desc: -//================================== package wechat import ( @@ -12,34 +7,36 @@ import ( ) func Transfer() { - //初始化微信客户端 + // 初始化微信客户端 // appId:应用ID // MchID:商户ID // ApiKey:Key值 // isProd:是否是正式环境(企业转账到个人账户,默认正式环境) - client := gopay.NewWeChatClient("wxdaa2ab9ef87b5497", "1368139502", "GFDS8j98rewnmgl45wHTt980jg543abc", true) + client := gopay.NewWeChatClient("wxdaa2ab9ef87b5497", "1368139502", "GFDS8j98rewnmgl45wHTt980jg543abc", false) - nonceStr := gopay.GetRandomString(32) - partnerTradeNo := gopay.GetRandomString(32) + err := client.AddCertFilePath("iguiyu_cert/apiclient_cert.pem", "iguiyu_cert/apiclient_key.pem", "iguiyu_cert/apiclient_cert.p12") + if err != nil { + fmt.Println("client.AddCertFilePath err:", err) + return + } - fmt.Println("partnerTradeNo:", partnerTradeNo) - //初始化参数结构体 + // 初始化参数结构体 body := make(gopay.BodyMap) - body.Set("nonce_str", nonceStr) - body.Set("partner_trade_no", partnerTradeNo) - body.Set("openid", "oMlss5F06l97UpwtB-8jvZd6Yabc") + body.Set("nonce_str", gopay.GetRandomString(32)) + body.Set("partner_trade_no", gopay.GetRandomString(32)) + body.Set("openid", "o0Df70H2Q0fY8JXh1aFPIRyOBgu8") body.Set("check_name", "FORCE_CHECK") // NO_CHECK:不校验真实姓名 , FORCE_CHECK:强校验真实姓名 - body.Set("re_user_name", "付明明") //收款用户真实姓名。 如果check_name设置为FORCE_CHECK,则必填用户真实姓名 - body.Set("amount", 30) //企业付款金额,单位为分 - body.Set("desc", "测试转账") //企业付款备注,必填。注意:备注中的敏感词会被转成字符* + body.Set("re_user_name", "付明明") // 收款用户真实姓名。 如果check_name设置为FORCE_CHECK,则必填用户真实姓名 + body.Set("amount", 30) // 企业付款金额,单位为分 + body.Set("desc", "测试转账") // 企业付款备注,必填。注意:备注中的敏感词会被转成字符* body.Set("spbill_create_ip", "127.0.0.1") - //请求申请退款(沙箱环境下,证书路径参数可传空) + // 请求申请退款(沙箱环境下,证书路径参数可传空) // body:参数Body // certFilePath:cert证书路径 // keyFilePath:Key证书路径 // pkcs12FilePath:p12证书路径 - wxRsp, err := client.Transfer(body, "iguiyu_cert/apiclient_cert.pem", "iguiyu_cert/apiclient_key.pem", "iguiyu_cert/apiclient_cert.p12") + wxRsp, err := client.Transfer(body, "", "", "") if err != nil { fmt.Println("Error:", err) return diff --git a/release_note.txt b/release_note.txt index dee57fe6..cf0df9e8 100644 --- a/release_note.txt +++ b/release_note.txt @@ -1,3 +1,15 @@ +版本号:Release 1.4.4 +发布时间:2019/11/16 15:56 +修改记录: + (1) 支付宝:新增公共API方法:gopay.ParseAliPayNotifyResultToBodyMap(),解析支付宝支付异步通知的参数到BodyMap + (2) 支付宝:修改公共API方法:gopay.VerifyAliPaySign(),支付宝异步验签支持传入 BodyMap + (3) 微信:新增Client方法:client.AddCertFileByte(),添加微信证书 Byte 数组 + (4) 微信:新增Client方法:client.AddCertFilePath(),添加微信证书 Path 路径 + (5) 微信:微信Client需要证书的方法,如已使用client.AddCertFilePath()或client.AddCertFileByte()添加过证书,参数certFilePath、keyFilePath、pkcs12FilePath全传空字符串 "",如方法需单独使用证书,则传证书Path + (6) BodyMap 的Set方法去掉switch判断,直接赋值 + (7) WeChatClient、AliPayClient 加锁 + (8) 修改部分小问题和部分样式 + 版本号:Release 1.4.3 发布时间:2019/11/12 01:15 修改记录: diff --git a/util.go b/util.go index b5e0f67e..2005a5e6 100644 --- a/util.go +++ b/util.go @@ -38,10 +38,10 @@ func (bm BodyMap) Get(key string) string { if v, ok = value.(string); ok { return v } - return jsonToString(value) + return convertToString(value) } -func jsonToString(v interface{}) (str string) { +func convertToString(v interface{}) (str string) { if v == nil { return null } diff --git a/wechat_client.go b/wechat_client.go index e6f94319..f8fedabc 100644 --- a/wechat_client.go +++ b/wechat_client.go @@ -2,12 +2,11 @@ package gopay import ( "crypto/tls" - "crypto/x509" "encoding/xml" "errors" "fmt" - "io/ioutil" "strings" + "sync" "github.com/parnurzeal/gorequest" ) @@ -21,9 +20,10 @@ type WeChatClient struct { KeyFile []byte Pkcs12File []byte IsProd bool + mu sync.RWMutex } -// 初始化微信客户端 ok +// 初始化微信客户端 // appId:应用ID // mchId:商户ID // ApiKey:API秘钥值 @@ -36,7 +36,7 @@ func NewWeChatClient(appId, mchId, apiKey string, isProd bool) (client *WeChatCl IsProd: isProd} } -// 提交付款码支付 ok +// 提交付款码支付 // 文档地址:https://pay.weixin.qq.com/wiki/doc/api/micropay.php?chapter=9_10&index=1 func (w *WeChatClient) Micropay(body BodyMap) (wxRsp *WeChatMicropayResponse, err error) { var bs []byte @@ -55,7 +55,7 @@ func (w *WeChatClient) Micropay(body BodyMap) (wxRsp *WeChatMicropayResponse, er return } -// 统一下单 ok +// 统一下单 // 文档地址:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1 func (w *WeChatClient) UnifiedOrder(body BodyMap) (wxRsp *WeChatUnifiedOrderResponse, err error) { var bs []byte @@ -75,7 +75,7 @@ func (w *WeChatClient) UnifiedOrder(body BodyMap) (wxRsp *WeChatUnifiedOrderResp return } -// 查询订单 ok +// 查询订单 // 文档地址:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_2 func (w *WeChatClient) QueryOrder(body BodyMap) (wxRsp *WeChatQueryOrderResponse, err error) { var bs []byte @@ -94,7 +94,7 @@ func (w *WeChatClient) QueryOrder(body BodyMap) (wxRsp *WeChatQueryOrderResponse return } -// 关闭订单 ok +// 关闭订单 // 文档地址:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_3 func (w *WeChatClient) CloseOrder(body BodyMap) (wxRsp *WeChatCloseOrderResponse, err error) { var bs []byte @@ -113,28 +113,18 @@ func (w *WeChatClient) CloseOrder(body BodyMap) (wxRsp *WeChatCloseOrderResponse return } -// 撤销订单 ok +// 撤销订单 +// 注意:如已使用client.AddCertFilePath()或client.AddCertFileByte()添加过证书,参数certFilePath、keyFilePath、pkcs12FilePath全传空字符串 "",如方法需单独使用证书,则传证书Path // 文档地址:https://pay.weixin.qq.com/wiki/doc/api/micropay.php?chapter=9_11&index=3 func (w *WeChatClient) Reverse(body BodyMap, certFilePath, keyFilePath, pkcs12FilePath string) (wxRsp *WeChatReverseResponse, err error) { var ( - bs, pkcs []byte - pkcsPool *x509.CertPool - certificate tls.Certificate - tlsConfig *tls.Config + bs []byte + tlsConfig *tls.Config ) if w.IsProd { - pkcsPool = x509.NewCertPool() - if pkcs, err = ioutil.ReadFile(pkcs12FilePath); err != nil { - return nil, fmt.Errorf("ioutil.ReadFile:%s", err.Error()) + if tlsConfig, err = w.addCertConfig(certFilePath, keyFilePath, pkcs12FilePath); err != nil { + return nil, err } - pkcsPool.AppendCertsFromPEM(pkcs) - if certificate, err = tls.LoadX509KeyPair(certFilePath, keyFilePath); err != nil { - return nil, fmt.Errorf("tls.LoadX509KeyPair:%s", err.Error()) - } - tlsConfig = &tls.Config{ - Certificates: []tls.Certificate{certificate}, - RootCAs: pkcsPool, - InsecureSkipVerify: true} bs, err = w.doWeChat(body, wxReverse, tlsConfig) } else { bs, err = w.doWeChat(body, wxSandboxReverse) @@ -149,28 +139,18 @@ func (w *WeChatClient) Reverse(body BodyMap, certFilePath, keyFilePath, pkcs12Fi return } -// 申请退款 ok +// 申请退款 +// 注意:如已使用client.AddCertFilePath()或client.AddCertFileByte()添加过证书,参数certFilePath、keyFilePath、pkcs12FilePath全传空字符串 "",如方法需单独使用证书,则传证书Path // 文档地址:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_4 func (w *WeChatClient) Refund(body BodyMap, certFilePath, keyFilePath, pkcs12FilePath string) (wxRsp *WeChatRefundResponse, err error) { var ( - bs, pkcs []byte - pkcsPool *x509.CertPool - certificate tls.Certificate - tlsConfig *tls.Config + bs []byte + tlsConfig *tls.Config ) if w.IsProd { - pkcsPool = x509.NewCertPool() - if pkcs, err = ioutil.ReadFile(pkcs12FilePath); err != nil { - return nil, fmt.Errorf("ioutil.ReadFile:%s", err.Error()) - } - pkcsPool.AppendCertsFromPEM(pkcs) - if certificate, err = tls.LoadX509KeyPair(certFilePath, keyFilePath); err != nil { - return nil, fmt.Errorf("tls.LoadX509KeyPair:%s", err.Error()) + if tlsConfig, err = w.addCertConfig(certFilePath, keyFilePath, pkcs12FilePath); err != nil { + return nil, err } - tlsConfig = &tls.Config{ - Certificates: []tls.Certificate{certificate}, - RootCAs: pkcsPool, - InsecureSkipVerify: true} bs, err = w.doWeChat(body, wxRefund, tlsConfig) } else { bs, err = w.doWeChat(body, wxSandboxRefund) @@ -185,7 +165,7 @@ func (w *WeChatClient) Refund(body BodyMap, certFilePath, keyFilePath, pkcs12Fil return } -// 查询退款 ok +// 查询退款 // 文档地址:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_5 func (w *WeChatClient) QueryRefund(body BodyMap) (wxRsp *WeChatQueryRefundResponse, err error) { var bs []byte @@ -204,7 +184,7 @@ func (w *WeChatClient) QueryRefund(body BodyMap) (wxRsp *WeChatQueryRefundRespon return } -// 下载对账单 ok +// 下载对账单 // 文档地址:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_6 func (w *WeChatClient) DownloadBill(body BodyMap) (wxRsp string, err error) { var bs []byte @@ -220,29 +200,19 @@ func (w *WeChatClient) DownloadBill(body BodyMap) (wxRsp string, err error) { return } -// 下载资金账单 ok +// 下载资金账单 +// 注意:如已使用client.AddCertFilePath()或client.AddCertFileByte()添加过证书,参数certFilePath、keyFilePath、pkcs12FilePath全传空字符串 "",如方法需单独使用证书,则传证书Path +// 貌似不支持沙箱环境,因为沙箱环境默认需要用MD5签名,但是此接口仅支持HMAC-SHA256签名 // 文档地址:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_18&index=7 -// 好像不支持沙箱环境,因为沙箱环境默认需要用MD5签名,但是此接口仅支持HMAC-SHA256签名 func (w *WeChatClient) DownloadFundFlow(body BodyMap, certFilePath, keyFilePath, pkcs12FilePath string) (wxRsp string, err error) { var ( - bs, pkcs []byte - pkcsPool *x509.CertPool - certificate tls.Certificate - tlsConfig *tls.Config + bs []byte + tlsConfig *tls.Config ) if w.IsProd { - pkcsPool = x509.NewCertPool() - if pkcs, err = ioutil.ReadFile(pkcs12FilePath); err != nil { - return null, fmt.Errorf("ioutil.ReadFile:%s", err.Error()) + if tlsConfig, err = w.addCertConfig(certFilePath, keyFilePath, pkcs12FilePath); err != nil { + return null, err } - pkcsPool.AppendCertsFromPEM(pkcs) - if certificate, err = tls.LoadX509KeyPair(certFilePath, keyFilePath); err != nil { - return null, fmt.Errorf("tls.LoadX509KeyPair:%s", err.Error()) - } - tlsConfig = &tls.Config{ - Certificates: []tls.Certificate{certificate}, - RootCAs: pkcsPool, - InsecureSkipVerify: true} bs, err = w.doWeChat(body, wxDownloadfundflow, tlsConfig) } else { bs, err = w.doWeChat(body, wxSandboxDownloadfundflow) @@ -255,29 +225,19 @@ func (w *WeChatClient) DownloadFundFlow(body BodyMap, certFilePath, keyFilePath, } // 拉取订单评价数据 +// 注意:如已使用client.AddCertFilePath()或client.AddCertFileByte()添加过证书,参数certFilePath、keyFilePath、pkcs12FilePath全传空字符串 "",如方法需单独使用证书,则传证书Path +// 貌似不支持沙箱环境,因为沙箱环境默认需要用MD5签名,但是此接口仅支持HMAC-SHA256签名 // 文档地址:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_17&index=11 -// 好像不支持沙箱环境,因为沙箱环境默认需要用MD5签名,但是此接口仅支持HMAC-SHA256签名 func (w *WeChatClient) BatchQueryComment(body BodyMap, certFilePath, keyFilePath, pkcs12FilePath string) (wxRsp string, err error) { var ( - bs, pkcs []byte - pkcsPool *x509.CertPool - certificate tls.Certificate - tlsConfig *tls.Config + bs []byte + tlsConfig *tls.Config ) if w.IsProd { body.Set("sign_type", SignType_HMAC_SHA256) - pkcsPool = x509.NewCertPool() - if pkcs, err = ioutil.ReadFile(pkcs12FilePath); err != nil { - return null, fmt.Errorf("ioutil.ReadFile:%s", err.Error()) - } - pkcsPool.AppendCertsFromPEM(pkcs) - if certificate, err = tls.LoadX509KeyPair(certFilePath, keyFilePath); err != nil { - return null, fmt.Errorf("tls.LoadX509KeyPair:%s", err.Error()) + if tlsConfig, err = w.addCertConfig(certFilePath, keyFilePath, pkcs12FilePath); err != nil { + return null, err } - tlsConfig = &tls.Config{ - Certificates: []tls.Certificate{certificate}, - RootCAs: pkcsPool, - InsecureSkipVerify: true} bs, err = w.doWeChat(body, wxBatchquerycomment, tlsConfig) } else { bs, err = w.doWeChat(body, wxSandboxBatchquerycomment) @@ -290,32 +250,22 @@ func (w *WeChatClient) BatchQueryComment(body BodyMap, certFilePath, keyFilePath } // 企业向微信用户个人付款 -// 文档地址:https://pay.weixin.qq.com/wiki/doc/api/tools/mch_pay.php?chapter=14_1 +// 注意:如已使用client.AddCertFilePath()或client.AddCertFileByte()添加过证书,参数certFilePath、keyFilePath、pkcs12FilePath全传空字符串 "",如方法需单独使用证书,则传证书Path // 注意:此方法未支持沙箱环境,默认正式环境,转账请慎重 +// 文档地址:https://pay.weixin.qq.com/wiki/doc/api/tools/mch_pay.php?chapter=14_1 func (w *WeChatClient) Transfer(body BodyMap, certFilePath, keyFilePath, pkcs12FilePath string) (wxRsp *WeChatTransfersResponse, err error) { body.Set("mch_appid", w.AppId) body.Set("mchid", w.MchId) var ( - bs, pkcs []byte - pkcsPool *x509.CertPool - certificate tls.Certificate - tlsConfig *tls.Config - agent *gorequest.SuperAgent - errs []error - res gorequest.Response + bs []byte + tlsConfig *tls.Config + agent *gorequest.SuperAgent + errs []error + res gorequest.Response ) - pkcsPool = x509.NewCertPool() - if pkcs, err = ioutil.ReadFile(pkcs12FilePath); err != nil { - return nil, fmt.Errorf("ioutil.ReadFile:%s", err.Error()) - } - pkcsPool.AppendCertsFromPEM(pkcs) - if certificate, err = tls.LoadX509KeyPair(certFilePath, keyFilePath); err != nil { - return nil, fmt.Errorf("tls.LoadX509KeyPair:%s", err.Error()) - } - tlsConfig = &tls.Config{ - Certificates: []tls.Certificate{certificate}, - RootCAs: pkcsPool, - InsecureSkipVerify: true} + if tlsConfig, err = w.addCertConfig(certFilePath, keyFilePath, pkcs12FilePath); err != nil { + return nil, err + } body.Set("sign", getWeChatReleaseSign(w.ApiKey, SignType_MD5, body)) agent = HttpAgent().TLSClientConfig(tlsConfig) if w.BaseURL != null { @@ -344,7 +294,7 @@ func (w *WeChatClient) EntrustPublic(body BodyMap) (bs []byte, err error) { return nil, nil } -// 向微信发送请求 ok +// 向微信发送请求 func (w *WeChatClient) doWeChat(body BodyMap, path string, tlsConfig ...*tls.Config) (bytes []byte, err error) { body.Set("appid", w.AppId) body.Set("mch_id", w.MchId) diff --git a/wechat_params.go b/wechat_params.go index 97cb4f45..c09d78c3 100644 --- a/wechat_params.go +++ b/wechat_params.go @@ -4,9 +4,12 @@ import ( "crypto/hmac" "crypto/md5" "crypto/sha256" + "crypto/tls" + "crypto/x509" "encoding/hex" "encoding/xml" "errors" + "fmt" "hash" "io/ioutil" "strings" @@ -18,6 +21,7 @@ type Country int // 根据支付地区情况设置国家 // country: func (w *WeChatClient) SetCountry(country Country) (client *WeChatClient) { + w.mu.Lock() switch country { case China: w.BaseURL = wxBaseUrlCh @@ -30,27 +34,78 @@ func (w *WeChatClient) SetCountry(country Country) (client *WeChatClient) { default: w.BaseURL = wxBaseUrlCh } + w.mu.Unlock() return w } -// 添加微信证书Bytes -func (w *WeChatClient) AddCertFileBytes(certFile, keyFile, pkcs12File []byte) { +// 添加微信证书 Byte 数组 +// certFile:apiclient_cert.pem byte数组 +// keyFile:apiclient_key.pem byte数组 +// pkcs12File:apiclient_cert.p12 byte数组 +func (w *WeChatClient) AddCertFileByte(certFile, keyFile, pkcs12File []byte) { + w.mu.Lock() w.CertFile = certFile w.KeyFile = keyFile w.Pkcs12File = pkcs12File + w.mu.Unlock() } -// 添加微信证书Path路径 -func (w *WeChatClient) AddCertFilePath(certFilePath, keyFilePath, pkcs12FilePath string) { - if cert, err := ioutil.ReadFile(certFilePath); err == nil { - w.CertFile = cert +// 添加微信证书 Path 路径 +// certFilePath:apiclient_cert.pem 路径 +// keyFilePath:apiclient_key.pem 路径 +// pkcs12FilePath:apiclient_cert.p12 路径 +// 返回err +func (w *WeChatClient) AddCertFilePath(certFilePath, keyFilePath, pkcs12FilePath string) (err error) { + w.mu.Lock() + defer w.mu.Unlock() + var ( + cert, key, pkcs []byte + ) + if cert, err = ioutil.ReadFile(certFilePath); err != nil { + return } - if key, err := ioutil.ReadFile(keyFilePath); err == nil { - w.KeyFile = key + if key, err = ioutil.ReadFile(keyFilePath); err != nil { + return } - if pkcs, err := ioutil.ReadFile(pkcs12FilePath); err == nil { - w.Pkcs12File = pkcs + if pkcs, err = ioutil.ReadFile(pkcs12FilePath); err != nil { + return } + w.CertFile = cert + w.KeyFile = key + w.Pkcs12File = pkcs + return +} + +func (w *WeChatClient) addCertConfig(certFilePath, keyFilePath, pkcs12FilePath string) (tlsConfig *tls.Config, err error) { + var ( + pkcs []byte + certificate tls.Certificate + pkcsPool = x509.NewCertPool() + ) + if certFilePath != null && keyFilePath != null && pkcs12FilePath != null { + if pkcs, err = ioutil.ReadFile(pkcs12FilePath); err != nil { + return nil, fmt.Errorf("ioutil.ReadFile:%s", err.Error()) + } + pkcsPool.AppendCertsFromPEM(pkcs) + if certificate, err = tls.LoadX509KeyPair(certFilePath, keyFilePath); err != nil { + return nil, fmt.Errorf("tls.LoadX509KeyPair:%s", err.Error()) + } + tlsConfig = &tls.Config{ + Certificates: []tls.Certificate{certificate}, + RootCAs: pkcsPool, + InsecureSkipVerify: true} + return + } + + pkcsPool.AppendCertsFromPEM(w.Pkcs12File) + if certificate, err = tls.X509KeyPair(w.CertFile, w.KeyFile); err != nil { + return nil, fmt.Errorf("tls.X509KeyPair:%s", err.Error()) + } + tlsConfig = &tls.Config{ + Certificates: []tls.Certificate{certificate}, + RootCAs: pkcsPool, + InsecureSkipVerify: true} + return } // 获取微信支付正式环境Sign值 @@ -83,11 +138,11 @@ func getWeChatSignBoxSign(mchId, apiKey string, bm BodyMap) (sign string, err er // 从微信提供的接口获取:SandboxSignKey func getSanBoxKey(mchId, nonceStr, apiKey, signType string) (key string, err error) { - body := make(BodyMap) - body.Set("mch_id", mchId) - body.Set("nonce_str", nonceStr) + bm := make(BodyMap) + bm.Set("mch_id", mchId) + bm.Set("nonce_str", nonceStr) //沙箱环境:获取沙箱环境ApiKey - if key, err = getSanBoxSignKey(mchId, nonceStr, getWeChatReleaseSign(apiKey, signType, body)); err != nil { + if key, err = getSanBoxSignKey(mchId, nonceStr, getWeChatReleaseSign(apiKey, signType, bm)); err != nil { return } return