diff --git a/client.go b/client.go index 70f2a01f95c1adf9ff132a3dc83673457d46b44d..61ea29a67bbda3b3a3c108811ef0a3c4d787fd38 100644 --- a/client.go +++ b/client.go @@ -1,13 +1,17 @@ package wechat +import ( + "net/http" +) + type Client struct { - config Config // 配置信息 - serviceType int // 服务模式 - apiKey string // API Key - certFilepath string // 证书目录 - certData []byte // 证书内容 - isProd bool // 是否是生产环境 - isMch bool // 是否是特殊的商户接口(微信找零) + config Config // 配置信息 + serviceType int // 服务模式 + apiKey string // API Key + certFilepath string // 证书目录 + certClient *http.Client // 带证书的http连接池 + isProd bool // 是否是生产环境 + isMch bool // 是否是特殊的商户接口(微信找零) } // 是否是服务商模式 diff --git a/client_certificate.go b/client_certificate.go index 8596f9daba56da9b25eb2799b7b06c949692f991..e18ef3bac637392c381d0c637397a25c4a1f685f 100644 --- a/client_certificate.go +++ b/client_certificate.go @@ -4,40 +4,57 @@ import ( "crypto/tls" "encoding/pem" "io/ioutil" + "net" "net/http" + "time" "golang.org/x/crypto/pkcs12" ) -func (c *Client) setCertData(certPath string) (transport *http.Transport) { - if c.certData != nil && len(c.certData) > 0 { +func (c *Client) setCertData(certPath string) (err error) { + if c.certClient != nil { return } certData, err := ioutil.ReadFile(certPath) - if err == nil { - c.certData = certData - transport = c.buildTransport() + if err != nil { + return + } + client, err = c.buildClient(certData) + if err != nil { + return } + c.certClient = client return } -func (c *Client) buildTransport() (transport *http.Transport) { +func (c *Client) buildClient(data []byte) (client *http.Client, err error) { // 将pkcs12证书转成pem - cert, err := c.pkc12ToPerm() + cert, err := c.pkc12ToPerm(data) if err != nil { return } // tls配置 config := &tls.Config{Certificates: []tls.Certificate{cert}} - transport = &http.Transport{ - TLSClientConfig: config, - DisableCompression: true, + // 带证书的客户端 + client = &http.Client{ + Timeout: 30 * time.Second, + Transport: &http.Transport{ + IdleConnTimeout: 3 * time.Minute, + TLSHandshakeTimeout: 10 * time.Second, + DialContext: (&net.Dialer{ + Timeout: 30 * time.Second, + KeepAlive: 10 * time.Minute, + DualStack: true, + }).DialContext, + TLSClientConfig: config, + DisableCompression: true, + }, } return } -func (c *Client) pkc12ToPerm() (cert tls.Certificate, err error) { - blocks, err := pkcs12.ToPEM(c.certData, c.config.MchId) +func (c *Client) pkc12ToPerm(data []byte) (cert tls.Certificate, err error) { + blocks, err := pkcs12.ToPEM(data, c.config.MchId) if err != nil { return } diff --git a/client_request.go b/client_request.go index 15c70223b42cfe085df5e8543fd9e9b59ec4e924..c01f826f3443a5cb676c000c045cd355e472173d 100644 --- a/client_request.go +++ b/client_request.go @@ -59,12 +59,11 @@ func (c *Client) doWeChatWithCert(relativeUrl string, bodyObj interface{}) (byte if err != nil { return } - // 设置证书 - transport := c.setCertData(c.certFilepath) - if transport == nil { + // 设置证书和连接池 + if err = c.setCertData(c.certFilepath); err != nil { return } // 发起请求 - bytes, err = httpPostWithCert(c.url(relativeUrl), GenerateXml(body), transport) + bytes, err = httpPostWithCert(c.url(relativeUrl), GenerateXml(body), c.certClient) return } diff --git a/network.go b/network.go index e11361dc6ba6a54548b9cda8e6c0008b179f3dea..d654e76fe94547b85d741ae07668885317d6baa4 100644 --- a/network.go +++ b/network.go @@ -2,13 +2,32 @@ package wechat import ( "io/ioutil" + "net" "net/http" "strings" + "time" ) +var client *http.Client + +func init() { + client = &http.Client{ + Timeout: 30 * time.Second, + Transport: &http.Transport{ + IdleConnTimeout: 3 * time.Minute, + TLSHandshakeTimeout: 10 * time.Second, + DialContext: (&net.Dialer{ + Timeout: 30 * time.Second, + KeepAlive: 10 * time.Minute, + DualStack: true, + }).DialContext, + }, + } +} + // 发送Get请求 func httpGet(url string) (body []byte, err error) { - resp, err := http.Get(url) + resp, err := client.Get(url) if err != nil { return } @@ -19,7 +38,7 @@ func httpGet(url string) (body []byte, err error) { // 发送Post请求,参数是XML格式的字符串 func httpPost(url string, xmlBody string) (body []byte, err error) { - resp, err := http.Post(url, "application/xml", strings.NewReader(xmlBody)) + resp, err := client.Post(url, "application/xml", strings.NewReader(xmlBody)) if err != nil { return } @@ -29,9 +48,8 @@ func httpPost(url string, xmlBody string) (body []byte, err error) { } // 发送带证书的Post请求,参数是XML格式的字符串 -func httpPostWithCert(url string, xmlBody string, transport *http.Transport) (body []byte, err error) { - h := &http.Client{Transport: transport} - resp, err := h.Post(url, "application/xml", strings.NewReader(xmlBody)) +func httpPostWithCert(url string, xmlBody string, client *http.Client) (body []byte, err error) { + resp, err := client.Post(url, "application/xml", strings.NewReader(xmlBody)) if err != nil { return } diff --git a/util.go b/util.go index ac0531b304a116fec1833b77c72507f0ef4a0d8b..bb67a4e50a3ff7bd61bd0e726a27264fb5915e5d 100644 --- a/util.go +++ b/util.go @@ -6,6 +6,7 @@ import ( "fmt" "math/rand" "net/url" + "regexp" "time" ) @@ -60,3 +61,10 @@ func PKCS7UnPadding(plainText []byte) []byte { unpadding := int(plainText[length-1]) // 找到Byte数组最后的填充byte return plainText[:(length - unpadding)] // 只截取返回有效数字内的byte数组 } + +// 18位纯数字,以10、11、12、13、14、15开头 +func IsValidAuthCode(authcode string) (ok bool) { + pattern := "^1[0-5][0-9]{16}$" + ok, _ = regexp.MatchString(pattern, authcode) + return +} diff --git a/wx_micropay_test.go b/wx_micropay_test.go index 8658741b6dc6b93d276bc9f0822b38d00adfe8ed..46a5702dec248b851372a7c970cc412a3152d36a 100644 --- a/wx_micropay_test.go +++ b/wx_micropay_test.go @@ -5,6 +5,12 @@ import ( "testing" ) +func TestAuthcode(t *testing.T) { + code := "165007001630843683" + ok := IsValidAuthCode(code) + t.Logf("%s is : %v", code, ok) +} + func TestMicropay(t *testing.T) { testMicropay(t) }