service/vendor/github.com/go-pay/gopay/wechat/v3/client.go

326 lines
11 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package wechat
import (
"context"
"crypto/rsa"
"fmt"
"net/http"
"sync"
"time"
"github.com/go-pay/gopay"
"github.com/go-pay/gopay/pkg/util"
"github.com/go-pay/gopay/pkg/xhttp"
"github.com/go-pay/gopay/pkg/xlog"
"github.com/go-pay/gopay/pkg/xpem"
)
// ClientV3 微信支付 V3
type ClientV3 struct {
Mchid string
ApiV3Key []byte
SerialNo string
WxSerialNo string
autoSign bool
bodySize int // http response body size(MB), default is 10MB
rwMu sync.RWMutex
privateKey *rsa.PrivateKey
wxPublicKey *rsa.PublicKey
ctx context.Context
DebugSwitch gopay.DebugSwitch
SnCertMap map[string]*rsa.PublicKey // key: serial_no
}
// NewClientV3 初始化微信客户端 V3
// mchid商户ID 或者服务商模式的 sp_mchid
// serialNo商户API证书的证书序列号
// apiV3KeyAPIv3Key商户平台获取
// privateKey商户API证书下载后私钥 apiclient_key.pem 读取后的字符串内容
func NewClientV3(mchid, serialNo, apiV3Key, privateKey string) (client *ClientV3, err error) {
if mchid == util.NULL || serialNo == util.NULL || apiV3Key == util.NULL || privateKey == util.NULL {
return nil, gopay.MissWechatInitParamErr
}
priKey, err := xpem.DecodePrivateKey([]byte(privateKey))
if err != nil {
return nil, err
}
client = &ClientV3{
Mchid: mchid,
SerialNo: serialNo,
ApiV3Key: []byte(apiV3Key),
privateKey: priKey,
ctx: context.Background(),
DebugSwitch: gopay.DebugOff,
}
return client, nil
}
// AutoVerifySign 开启请求完自动验签功能(默认不开启,推荐开启)
// 开启自动验签自动开启每12小时一次轮询请求最新证书操作
func (c *ClientV3) AutoVerifySign(autoRefresh ...bool) (err error) {
wxSerialNo, certMap, err := c.GetAndSelectNewestCert()
if err != nil {
return err
}
if len(c.SnCertMap) <= 0 {
c.SnCertMap = make(map[string]*rsa.PublicKey)
}
for sn, cert := range certMap {
// decode cert
pubKey, err := xpem.DecodePublicKey([]byte(cert))
if err != nil {
return err
}
c.SnCertMap[sn] = pubKey
}
c.WxSerialNo = wxSerialNo
c.wxPublicKey = c.SnCertMap[wxSerialNo]
if len(autoRefresh) == 1 && !autoRefresh[0] {
return
}
c.autoSign = true
go c.autoCheckCertProc()
return
}
// SetBodySize 设置http response body size(MB)
func (c *ClientV3) SetBodySize(sizeMB int) {
if sizeMB > 0 {
c.bodySize = sizeMB
}
}
func (c *ClientV3) doProdPostWithHeader(ctx context.Context, headerMap map[string]string, bm gopay.BodyMap, path, authorization string) (res *http.Response, si *SignInfo, bs []byte, err error) {
var url = v3BaseUrlCh + path
httpClient := xhttp.NewClient()
if c.bodySize > 0 {
httpClient.SetBodySize(c.bodySize)
}
if c.DebugSwitch == gopay.DebugOn {
xlog.Debugf("Wechat_V3_RequestBody: %s", bm.JsonBody())
xlog.Debugf("Wechat_V3_Authorization: %s", authorization)
}
for k, v := range headerMap {
httpClient.Header.Add(k, v)
}
httpClient.Header.Add(HeaderAuthorization, authorization)
httpClient.Header.Add(HeaderRequestID, fmt.Sprintf("%s-%d", util.RandomString(21), time.Now().Unix()))
httpClient.Header.Add(HeaderSerial, c.WxSerialNo)
httpClient.Header.Add("Accept", "*/*")
res, bs, err = httpClient.Type(xhttp.TypeJSON).Post(url).SendBodyMap(bm).EndBytes(ctx)
if err != nil {
return nil, nil, nil, err
}
si = &SignInfo{
HeaderTimestamp: res.Header.Get(HeaderTimestamp),
HeaderNonce: res.Header.Get(HeaderNonce),
HeaderSignature: res.Header.Get(HeaderSignature),
HeaderSerial: res.Header.Get(HeaderSerial),
SignBody: string(bs),
}
if c.DebugSwitch == gopay.DebugOn {
xlog.Debugf("Wechat_Response: %d > %s", res.StatusCode, string(bs))
xlog.Debugf("Wechat_Headers: %#v", res.Header)
xlog.Debugf("Wechat_SignInfo: %#v", si)
}
return res, si, bs, nil
}
func (c *ClientV3) doProdPost(ctx context.Context, bm gopay.BodyMap, path, authorization string) (res *http.Response, si *SignInfo, bs []byte, err error) {
var url = v3BaseUrlCh + path
httpClient := xhttp.NewClient()
if c.bodySize > 0 {
httpClient.SetBodySize(c.bodySize)
}
if c.DebugSwitch == gopay.DebugOn {
xlog.Debugf("Wechat_V3_RequestBody: %s", bm.JsonBody())
xlog.Debugf("Wechat_V3_Authorization: %s", authorization)
}
httpClient.Header.Add(HeaderAuthorization, authorization)
httpClient.Header.Add(HeaderRequestID, fmt.Sprintf("%s-%d", util.RandomString(21), time.Now().Unix()))
httpClient.Header.Add(HeaderSerial, c.WxSerialNo)
httpClient.Header.Add("Accept", "*/*")
res, bs, err = httpClient.Type(xhttp.TypeJSON).Post(url).SendBodyMap(bm).EndBytes(ctx)
if err != nil {
return nil, nil, nil, err
}
si = &SignInfo{
HeaderTimestamp: res.Header.Get(HeaderTimestamp),
HeaderNonce: res.Header.Get(HeaderNonce),
HeaderSignature: res.Header.Get(HeaderSignature),
HeaderSerial: res.Header.Get(HeaderSerial),
SignBody: string(bs),
}
if c.DebugSwitch == gopay.DebugOn {
xlog.Debugf("Wechat_Response: %d > %s", res.StatusCode, string(bs))
xlog.Debugf("Wechat_Headers: %#v", res.Header)
xlog.Debugf("Wechat_SignInfo: %#v", si)
}
return res, si, bs, nil
}
func (c *ClientV3) doProdGet(ctx context.Context, uri, authorization string) (res *http.Response, si *SignInfo, bs []byte, err error) {
var url = v3BaseUrlCh + uri
httpClient := xhttp.NewClient()
if c.bodySize > 0 {
httpClient.SetBodySize(c.bodySize)
}
if c.DebugSwitch == gopay.DebugOn {
xlog.Debugf("Wechat_V3_Url: %s", url)
xlog.Debugf("Wechat_V3_Authorization: %s", authorization)
}
httpClient.Header.Add(HeaderAuthorization, authorization)
httpClient.Header.Add(HeaderRequestID, fmt.Sprintf("%s-%d", util.RandomString(21), time.Now().Unix()))
httpClient.Header.Add(HeaderSerial, c.WxSerialNo)
httpClient.Header.Add("Accept", "*/*")
res, bs, err = httpClient.Type(xhttp.TypeJSON).Get(url).EndBytes(ctx)
if err != nil {
return nil, nil, nil, err
}
si = &SignInfo{
HeaderTimestamp: res.Header.Get(HeaderTimestamp),
HeaderNonce: res.Header.Get(HeaderNonce),
HeaderSignature: res.Header.Get(HeaderSignature),
HeaderSerial: res.Header.Get(HeaderSerial),
SignBody: string(bs),
}
if c.DebugSwitch == gopay.DebugOn {
xlog.Debugf("Wechat_Response: %d > %s", res.StatusCode, string(bs))
xlog.Debugf("Wechat_Headers: %#v", res.Header)
xlog.Debugf("Wechat_SignInfo: %#v", si)
}
return res, si, bs, nil
}
func (c *ClientV3) doProdPut(ctx context.Context, bm gopay.BodyMap, path, authorization string) (res *http.Response, si *SignInfo, bs []byte, err error) {
var url = v3BaseUrlCh + path
httpClient := xhttp.NewClient()
if c.bodySize > 0 {
httpClient.SetBodySize(c.bodySize)
}
if c.DebugSwitch == gopay.DebugOn {
xlog.Debugf("Wechat_V3_RequestBody: %s", bm.JsonBody())
xlog.Debugf("Wechat_V3_Authorization: %s", authorization)
}
httpClient.Header.Add(HeaderAuthorization, authorization)
httpClient.Header.Add(HeaderRequestID, fmt.Sprintf("%s-%d", util.RandomString(21), time.Now().Unix()))
httpClient.Header.Add(HeaderSerial, c.WxSerialNo)
httpClient.Header.Add("Accept", "*/*")
res, bs, err = httpClient.Type(xhttp.TypeJSON).Put(url).SendBodyMap(bm).EndBytes(ctx)
if err != nil {
return nil, nil, nil, err
}
si = &SignInfo{
HeaderTimestamp: res.Header.Get(HeaderTimestamp),
HeaderNonce: res.Header.Get(HeaderNonce),
HeaderSignature: res.Header.Get(HeaderSignature),
HeaderSerial: res.Header.Get(HeaderSerial),
SignBody: string(bs),
}
if c.DebugSwitch == gopay.DebugOn {
xlog.Debugf("Wechat_Response: %d > %s", res.StatusCode, string(bs))
xlog.Debugf("Wechat_Headers: %#v", res.Header)
xlog.Debugf("Wechat_SignInfo: %#v", si)
}
return res, si, bs, nil
}
func (c *ClientV3) doProdDelete(ctx context.Context, bm gopay.BodyMap, path, authorization string) (res *http.Response, si *SignInfo, bs []byte, err error) {
var url = v3BaseUrlCh + path
httpClient := xhttp.NewClient()
if c.bodySize > 0 {
httpClient.SetBodySize(c.bodySize)
}
if c.DebugSwitch == gopay.DebugOn {
xlog.Debugf("Wechat_V3_RequestBody: %s", bm.JsonBody())
xlog.Debugf("Wechat_V3_Authorization: %s", authorization)
}
httpClient.Header.Add(HeaderAuthorization, authorization)
httpClient.Header.Add(HeaderRequestID, fmt.Sprintf("%s-%d", util.RandomString(21), time.Now().Unix()))
httpClient.Header.Add(HeaderSerial, c.WxSerialNo)
httpClient.Header.Add("Accept", "*/*")
res, bs, err = httpClient.Type(xhttp.TypeJSON).Delete(url).SendBodyMap(bm).EndBytes(ctx)
if err != nil {
return nil, nil, nil, err
}
si = &SignInfo{
HeaderTimestamp: res.Header.Get(HeaderTimestamp),
HeaderNonce: res.Header.Get(HeaderNonce),
HeaderSignature: res.Header.Get(HeaderSignature),
HeaderSerial: res.Header.Get(HeaderSerial),
SignBody: string(bs),
}
if c.DebugSwitch == gopay.DebugOn {
xlog.Debugf("Wechat_Response: %d > %s", res.StatusCode, string(bs))
xlog.Debugf("Wechat_Headers: %#v", res.Header)
xlog.Debugf("Wechat_SignInfo: %#v", si)
}
return res, si, bs, nil
}
func (c *ClientV3) doProdPostFile(ctx context.Context, bm gopay.BodyMap, path, authorization string) (res *http.Response, si *SignInfo, bs []byte, err error) {
var url = v3BaseUrlCh + path
httpClient := xhttp.NewClient()
if c.bodySize > 0 {
httpClient.SetBodySize(c.bodySize)
}
if c.DebugSwitch == gopay.DebugOn {
xlog.Debugf("Wechat_V3_RequestBody: %s", bm.GetString("meta"))
xlog.Debugf("Wechat_V3_Authorization: %s", authorization)
}
httpClient.Header.Add(HeaderAuthorization, authorization)
httpClient.Header.Add(HeaderRequestID, fmt.Sprintf("%s-%d", util.RandomString(21), time.Now().Unix()))
httpClient.Header.Add(HeaderSerial, c.WxSerialNo)
httpClient.Header.Add("Accept", "*/*")
res, bs, err = httpClient.Type(xhttp.TypeMultipartFormData).Post(url).SendMultipartBodyMap(bm).EndBytes(ctx)
if err != nil {
return nil, nil, nil, err
}
si = &SignInfo{
HeaderTimestamp: res.Header.Get(HeaderTimestamp),
HeaderNonce: res.Header.Get(HeaderNonce),
HeaderSignature: res.Header.Get(HeaderSignature),
HeaderSerial: res.Header.Get(HeaderSerial),
SignBody: string(bs),
}
if c.DebugSwitch == gopay.DebugOn {
xlog.Debugf("Wechat_Response: %d > %s", res.StatusCode, string(bs))
xlog.Debugf("Wechat_Headers: %#v", res.Header)
xlog.Debugf("Wechat_SignInfo: %#v", si)
}
return res, si, bs, nil
}
func (c *ClientV3) doProdPatch(ctx context.Context, bm gopay.BodyMap, path, authorization string) (res *http.Response, si *SignInfo, bs []byte, err error) {
var url = v3BaseUrlCh + path
httpClient := xhttp.NewClient()
if c.bodySize > 0 {
httpClient.SetBodySize(c.bodySize)
}
if c.DebugSwitch == gopay.DebugOn {
xlog.Debugf("Wechat_V3_RequestBody: %s", bm.JsonBody())
xlog.Debugf("Wechat_V3_Authorization: %s", authorization)
}
httpClient.Header.Add(HeaderAuthorization, authorization)
httpClient.Header.Add(HeaderRequestID, fmt.Sprintf("%s-%d", util.RandomString(21), time.Now().Unix()))
httpClient.Header.Add(HeaderSerial, c.WxSerialNo)
httpClient.Header.Add("Accept", "*/*")
res, bs, err = httpClient.Type(xhttp.TypeJSON).Patch(url).SendBodyMap(bm).EndBytes(ctx)
if err != nil {
return nil, nil, nil, err
}
si = &SignInfo{
HeaderTimestamp: res.Header.Get(HeaderTimestamp),
HeaderNonce: res.Header.Get(HeaderNonce),
HeaderSignature: res.Header.Get(HeaderSignature),
HeaderSerial: res.Header.Get(HeaderSerial),
SignBody: string(bs),
}
if c.DebugSwitch == gopay.DebugOn {
xlog.Debugf("Wechat_Response: %d > %s", res.StatusCode, string(bs))
xlog.Debugf("Wechat_Headers: %#v", res.Header)
xlog.Debugf("Wechat_SignInfo: %#v", si)
}
return res, si, bs, nil
}