Jerry пре 6 година
родитељ
комит
1dd06a98f6
6 измењених фајлова са 188 додато и 100 уклоњено
  1. 4 3
      README.md
  2. 2 1
      release_note.txt
  3. 97 78
      wechat/client.go
  4. 46 2
      wechat/client_test.go
  5. 35 14
      wechat/model.go
  6. 4 2
      wechat/service_api.go

+ 4 - 3
README.md

@@ -42,20 +42,21 @@ func main() {
     * APP - app支付
     * APP - app支付
     * MWEB - H5支付
     * MWEB - H5支付
 * 提交付款码支付:client.Micropay()
 * 提交付款码支付:client.Micropay()
-* 授权码查询OpenId:client.AuthCodeToOpenId()
 * 查询订单:client.QueryOrder()
 * 查询订单:client.QueryOrder()
 * 关闭订单:client.CloseOrder()
 * 关闭订单:client.CloseOrder()
 * 撤销订单:client.Reverse()
 * 撤销订单:client.Reverse()
 * 申请退款:client.Refund()
 * 申请退款:client.Refund()
 * 查询退款:client.QueryRefund()
 * 查询退款:client.QueryRefund()
 * 下载对账单:client.DownloadBill()
 * 下载对账单:client.DownloadBill()
-* 下载资金账单:client.DownloadFundFlow()
+* 下载资金账单(正式):client.DownloadFundFlow()
 * 交易保障:client.Report()
 * 交易保障:client.Report()
-* 拉取订单评价数据:client.BatchQueryComment()
+* 拉取订单评价数据(正式):client.BatchQueryComment()
 * 企业向微信用户个人付款(正式):client.Transfer()
 * 企业向微信用户个人付款(正式):client.Transfer()
+* 授权码查询OpenId(正式):client.AuthCodeToOpenId()
 * 公众号纯签约(正式):client.EntrustPublic()
 * 公众号纯签约(正式):client.EntrustPublic()
 * APP纯签约-预签约接口-获取预签约ID(正式):client.EntrustAppPre()
 * APP纯签约-预签约接口-获取预签约ID(正式):client.EntrustAppPre()
 * H5纯签约(正式):client.EntrustH5()
 * H5纯签约(正式):client.EntrustH5()
+* 支付中签约(正式):client.EntrustPaying()
 
 
 ### 微信公共API
 ### 微信公共API
 
 

+ 2 - 1
release_note.txt

@@ -1,11 +1,12 @@
 版本号:Release 2.0.3
 版本号:Release 2.0.3
 发布时间:2019/12/18 16:25
 发布时间:2019/12/18 16:25
 修改记录:
 修改记录:
-   (1) 微信:新增Client方法:client.AuthCodeToOpenId(),授权码查询OpenId
+   (1) 微信:新增Client方法:client.AuthCodeToOpenId(),授权码查询OpenId(正式)
    (2) 微信:新增Client方法:client.Report(),交易保障
    (2) 微信:新增Client方法:client.Report(),交易保障
    (3) 微信:新增Client方法:client.EntrustPublic(),公众号纯签约(正式)
    (3) 微信:新增Client方法:client.EntrustPublic(),公众号纯签约(正式)
    (4) 微信:新增Client方法:client.EntrustAppPre(),APP纯签约-预签约接口-获取预签约ID(正式)
    (4) 微信:新增Client方法:client.EntrustAppPre(),APP纯签约-预签约接口-获取预签约ID(正式)
    (5) 微信:新增Client方法:client.EntrustH5(),H5纯签约(正式)
    (5) 微信:新增Client方法:client.EntrustH5(),H5纯签约(正式)
+   (5) 微信:新增Client方法:client.EntrustPaying(),支付中签约(正式)
 
 
 版本号:Release 2.0.2
 版本号:Release 2.0.2
 发布时间:2019/12/13 23:25
 发布时间:2019/12/13 23:25

+ 97 - 78
wechat/client.go

@@ -45,9 +45,10 @@ func (w *Client) Micropay(bm gopay.BodyMap) (wxRsp *MicropayResponse, err error)
 	}
 	}
 	var bs []byte
 	var bs []byte
 	if w.IsProd {
 	if w.IsProd {
-		bs, err = w.doWeChatPost(bm, microPay, nil)
+		bs, err = w.doWeChatPostProd(bm, microPay, nil)
 	} else {
 	} else {
-		bs, err = w.doWeChatPost(bm, sandboxMicroPay, nil)
+		bm.Set("total_fee", 1)
+		bs, err = w.doWeChatPostSanBox(bm, sandboxMicroPay)
 	}
 	}
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
@@ -59,19 +60,15 @@ func (w *Client) Micropay(bm gopay.BodyMap) (wxRsp *MicropayResponse, err error)
 	return wxRsp, nil
 	return wxRsp, nil
 }
 }
 
 
-// 授权码查询openid
+// 授权码查询openid(正式)
 //    文档地址:https://pay.weixin.qq.com/wiki/doc/api/micropay.php?chapter=9_13&index=9
 //    文档地址:https://pay.weixin.qq.com/wiki/doc/api/micropay.php?chapter=9_13&index=9
 func (w *Client) AuthCodeToOpenId(bm gopay.BodyMap) (wxRsp *AuthCodeToOpenIdResponse, err error) {
 func (w *Client) AuthCodeToOpenId(bm gopay.BodyMap) (wxRsp *AuthCodeToOpenIdResponse, err error) {
 	err = bm.CheckEmptyError("nonce_str", "auth_code")
 	err = bm.CheckEmptyError("nonce_str", "auth_code")
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
-	var bs []byte
-	if w.IsProd {
-		bs, err = w.doWeChatPost(bm, authCodeToOpenid, nil)
-	} else {
-		bs, err = w.doWeChatPost(bm, sandboxAuthCodeToOpenid, nil)
-	}
+
+	bs, err := w.doWeChatPostProd(bm, authCodeToOpenid, nil)
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
@@ -91,10 +88,10 @@ func (w *Client) UnifiedOrder(bm gopay.BodyMap) (wxRsp *UnifiedOrderResponse, er
 	}
 	}
 	var bs []byte
 	var bs []byte
 	if w.IsProd {
 	if w.IsProd {
-		bs, err = w.doWeChatPost(bm, unifiedOrder, nil)
+		bs, err = w.doWeChatPostProd(bm, unifiedOrder, nil)
 	} else {
 	} else {
 		bm.Set("total_fee", 101)
 		bm.Set("total_fee", 101)
-		bs, err = w.doWeChatPost(bm, sandboxUnifiedOrder, nil)
+		bs, err = w.doWeChatPostSanBox(bm, sandboxUnifiedOrder)
 	}
 	}
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
@@ -118,9 +115,9 @@ func (w *Client) QueryOrder(bm gopay.BodyMap) (wxRsp *QueryOrderResponse, err er
 	}
 	}
 	var bs []byte
 	var bs []byte
 	if w.IsProd {
 	if w.IsProd {
-		bs, err = w.doWeChatPost(bm, orderQuery, nil)
+		bs, err = w.doWeChatPostProd(bm, orderQuery, nil)
 	} else {
 	} else {
-		bs, err = w.doWeChatPost(bm, sandboxOrderQuery, nil)
+		bs, err = w.doWeChatPostSanBox(bm, sandboxOrderQuery)
 	}
 	}
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
@@ -141,9 +138,9 @@ func (w *Client) CloseOrder(bm gopay.BodyMap) (wxRsp *CloseOrderResponse, err er
 	}
 	}
 	var bs []byte
 	var bs []byte
 	if w.IsProd {
 	if w.IsProd {
-		bs, err = w.doWeChatPost(bm, closeOrder, nil)
+		bs, err = w.doWeChatPostProd(bm, closeOrder, nil)
 	} else {
 	} else {
-		bs, err = w.doWeChatPost(bm, sandboxCloseOrder, nil)
+		bs, err = w.doWeChatPostSanBox(bm, sandboxCloseOrder)
 	}
 	}
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
@@ -171,9 +168,9 @@ func (w *Client) Reverse(bm gopay.BodyMap, certFilePath, keyFilePath, pkcs12File
 		if tlsConfig, err = w.addCertConfig(certFilePath, keyFilePath, pkcs12FilePath); err != nil {
 		if tlsConfig, err = w.addCertConfig(certFilePath, keyFilePath, pkcs12FilePath); err != nil {
 			return nil, err
 			return nil, err
 		}
 		}
-		bs, err = w.doWeChatPost(bm, reverse, tlsConfig)
+		bs, err = w.doWeChatPostProd(bm, reverse, tlsConfig)
 	} else {
 	} else {
-		bs, err = w.doWeChatPost(bm, sandboxReverse, nil)
+		bs, err = w.doWeChatPostSanBox(bm, sandboxReverse)
 	}
 	}
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
@@ -204,9 +201,9 @@ func (w *Client) Refund(bm gopay.BodyMap, certFilePath, keyFilePath, pkcs12FileP
 		if tlsConfig, err = w.addCertConfig(certFilePath, keyFilePath, pkcs12FilePath); err != nil {
 		if tlsConfig, err = w.addCertConfig(certFilePath, keyFilePath, pkcs12FilePath); err != nil {
 			return nil, err
 			return nil, err
 		}
 		}
-		bs, err = w.doWeChatPost(bm, refund, tlsConfig)
+		bs, err = w.doWeChatPostProd(bm, refund, tlsConfig)
 	} else {
 	} else {
-		bs, err = w.doWeChatPost(bm, sandboxRefund, nil)
+		bs, err = w.doWeChatPostSanBox(bm, sandboxRefund)
 	}
 	}
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
@@ -230,9 +227,9 @@ func (w *Client) QueryRefund(bm gopay.BodyMap) (wxRsp *QueryRefundResponse, err
 	}
 	}
 	var bs []byte
 	var bs []byte
 	if w.IsProd {
 	if w.IsProd {
-		bs, err = w.doWeChatPost(bm, refundQuery, nil)
+		bs, err = w.doWeChatPostProd(bm, refundQuery, nil)
 	} else {
 	} else {
-		bs, err = w.doWeChatPost(bm, sandboxRefundQuery, nil)
+		bs, err = w.doWeChatPostSanBox(bm, sandboxRefundQuery)
 	}
 	}
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
@@ -257,9 +254,9 @@ func (w *Client) DownloadBill(bm gopay.BodyMap) (wxRsp string, err error) {
 	}
 	}
 	var bs []byte
 	var bs []byte
 	if w.IsProd {
 	if w.IsProd {
-		bs, err = w.doWeChatPost(bm, downloadBill, nil)
+		bs, err = w.doWeChatPostProd(bm, downloadBill, nil)
 	} else {
 	} else {
-		bs, err = w.doWeChatPost(bm, sandboxDownloadBill, nil)
+		bs, err = w.doWeChatPostSanBox(bm, sandboxDownloadBill)
 	}
 	}
 	if err != nil {
 	if err != nil {
 		return gopay.NULL, err
 		return gopay.NULL, err
@@ -267,7 +264,7 @@ func (w *Client) DownloadBill(bm gopay.BodyMap) (wxRsp string, err error) {
 	return string(bs), nil
 	return string(bs), nil
 }
 }
 
 
-// 下载资金账单
+// 下载资金账单(正式)
 //    注意:如已使用client.AddCertFilePath()添加过证书,参数certFilePath、keyFilePath、pkcs12FilePath全传空字符串 "",否则,3证书Path均不可空
 //    注意:如已使用client.AddCertFilePath()添加过证书,参数certFilePath、keyFilePath、pkcs12FilePath全传空字符串 "",否则,3证书Path均不可空
 //    貌似不支持沙箱环境,因为沙箱环境默认需要用MD5签名,但是此接口仅支持HMAC-SHA256签名
 //    貌似不支持沙箱环境,因为沙箱环境默认需要用MD5签名,但是此接口仅支持HMAC-SHA256签名
 //    文档地址:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_18&index=7
 //    文档地址:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_18&index=7
@@ -280,19 +277,12 @@ func (w *Client) DownloadFundFlow(bm gopay.BodyMap, certFilePath, keyFilePath, p
 	if accountType != "Basic" && accountType != "Operation" && accountType != "Fees" {
 	if accountType != "Basic" && accountType != "Operation" && accountType != "Fees" {
 		return gopay.NULL, errors.New("account_type error, please reference: https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_18&index=7")
 		return gopay.NULL, errors.New("account_type error, please reference: https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_18&index=7")
 	}
 	}
-	var (
-		bs        []byte
-		tlsConfig *tls.Config
-	)
 	bm.Set("sign_type", SignType_HMAC_SHA256)
 	bm.Set("sign_type", SignType_HMAC_SHA256)
-	if w.IsProd {
-		if tlsConfig, err = w.addCertConfig(certFilePath, keyFilePath, pkcs12FilePath); err != nil {
-			return gopay.NULL, err
-		}
-		bs, err = w.doWeChatPost(bm, downloadFundFlow, tlsConfig)
-	} else {
-		bs, err = w.doWeChatPost(bm, sandboxDownloadFundFlow, nil)
+	tlsConfig, err := w.addCertConfig(certFilePath, keyFilePath, pkcs12FilePath)
+	if err != nil {
+		return gopay.NULL, err
 	}
 	}
+	bs, err := w.doWeChatPostProd(bm, downloadFundFlow, tlsConfig)
 	if err != nil {
 	if err != nil {
 		return gopay.NULL, err
 		return gopay.NULL, err
 	}
 	}
@@ -314,9 +304,9 @@ func (w *Client) Report(bm gopay.BodyMap) (wxRsp *ReportResponse, err error) {
 	}
 	}
 	var bs []byte
 	var bs []byte
 	if w.IsProd {
 	if w.IsProd {
-		bs, err = w.doWeChatPost(bm, report, nil)
+		bs, err = w.doWeChatPostProd(bm, report, nil)
 	} else {
 	} else {
-		bs, err = w.doWeChatPost(bm, sandboxReport, nil)
+		bs, err = w.doWeChatPostSanBox(bm, sandboxReport)
 	}
 	}
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
@@ -328,7 +318,7 @@ func (w *Client) Report(bm gopay.BodyMap) (wxRsp *ReportResponse, err error) {
 	return wxRsp, nil
 	return wxRsp, nil
 }
 }
 
 
-// 拉取订单评价数据
+// 拉取订单评价数据(正式)
 //    注意:如已使用client.AddCertFilePath()添加过证书,参数certFilePath、keyFilePath、pkcs12FilePath全传空字符串 "",否则,3证书Path均不可空
 //    注意:如已使用client.AddCertFilePath()添加过证书,参数certFilePath、keyFilePath、pkcs12FilePath全传空字符串 "",否则,3证书Path均不可空
 //    貌似不支持沙箱环境,因为沙箱环境默认需要用MD5签名,但是此接口仅支持HMAC-SHA256签名
 //    貌似不支持沙箱环境,因为沙箱环境默认需要用MD5签名,但是此接口仅支持HMAC-SHA256签名
 //    文档地址:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_17&index=11
 //    文档地址:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_17&index=11
@@ -337,19 +327,12 @@ func (w *Client) BatchQueryComment(bm gopay.BodyMap, certFilePath, keyFilePath,
 	if err != nil {
 	if err != nil {
 		return gopay.NULL, err
 		return gopay.NULL, err
 	}
 	}
-	var (
-		bs        []byte
-		tlsConfig *tls.Config
-	)
 	bm.Set("sign_type", SignType_HMAC_SHA256)
 	bm.Set("sign_type", SignType_HMAC_SHA256)
-	if w.IsProd {
-		if tlsConfig, err = w.addCertConfig(certFilePath, keyFilePath, pkcs12FilePath); err != nil {
-			return gopay.NULL, err
-		}
-		bs, err = w.doWeChatPost(bm, batchQueryComment, tlsConfig)
-	} else {
-		bs, err = w.doWeChatPost(bm, sandboxBatchQueryComment, nil)
+	tlsConfig, err := w.addCertConfig(certFilePath, keyFilePath, pkcs12FilePath)
+	if err != nil {
+		return gopay.NULL, err
 	}
 	}
+	bs, err := w.doWeChatPostProd(bm, batchQueryComment, tlsConfig)
 	if err != nil {
 	if err != nil {
 		return gopay.NULL, err
 		return gopay.NULL, err
 	}
 	}
@@ -400,7 +383,7 @@ func (w *Client) EntrustPublic(bm gopay.BodyMap) (wxRsp *EntrustPublicResponse,
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
-	bs, err := w.doWeChatGet(bm, entrustPublic, SignType_MD5)
+	bs, err := w.doWeChatGetProd(bm, entrustPublic, SignType_MD5)
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
@@ -418,23 +401,13 @@ func (w *Client) EntrustAppPre(bm gopay.BodyMap) (wxRsp *EntrustAppPreResponse,
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
-	var url = baseUrlCh + entrustApp
-	bm.Set("appid", w.AppId)
-	bm.Set("mch_id", w.MchId)
-	bm.Set("sign", getReleaseSign(w.ApiKey, bm.Get("sign_type"), bm))
-	httpClient := gopay.NewHttpClient().Type(gopay.TypeXML)
-	if w.BaseURL != gopay.NULL {
-		w.mu.RLock()
-		url = w.BaseURL + entrustApp
-		w.mu.RUnlock()
+	bs, err := w.doWeChatPostProd(bm, entrustApp, nil)
+	if err != nil {
+		return nil, err
 	}
 	}
 	wxRsp = new(EntrustAppPreResponse)
 	wxRsp = new(EntrustAppPreResponse)
-	res, errs := httpClient.Post(url).SendString(generateXml(bm)).EndStruct(wxRsp)
-	if len(errs) > 0 {
-		return nil, errs[0]
-	}
-	if res.StatusCode != 200 {
-		return nil, fmt.Errorf("HTTP Request Error, StatusCode = %d", res.StatusCode)
+	if err = xml.Unmarshal(bs, wxRsp); err != nil {
+		return nil, fmt.Errorf("xml.Unmarshal(%s):%w", string(bs), err)
 	}
 	}
 	return wxRsp, nil
 	return wxRsp, nil
 }
 }
@@ -446,7 +419,7 @@ func (w *Client) EntrustH5(bm gopay.BodyMap) (wxRsp *EntrustH5Response, err erro
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
-	bs, err := w.doWeChatGet(bm, entrustH5, SignType_HMAC_SHA256)
+	bs, err := w.doWeChatGetProd(bm, entrustH5, SignType_HMAC_SHA256)
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
@@ -457,27 +430,73 @@ func (w *Client) EntrustH5(bm gopay.BodyMap) (wxRsp *EntrustH5Response, err erro
 	return wxRsp, nil
 	return wxRsp, nil
 }
 }
 
 
+// 支付中签约(正式)
+//    文档地址:https://pay.weixin.qq.com/wiki/doc/api/pap.php?chapter=18_13&index=5
+func (w *Client) EntrustPaying(bm gopay.BodyMap) (wxRsp *EntrustPayingResponse, err error) {
+	err = bm.CheckEmptyError("contract_mchid", "contract_appid",
+		"out_trade_no", "nonce_str", "body", "notify_url", "total_fee",
+		"spbill_create_ip", "trade_type", "plan_id", "contract_code",
+		"request_serial", "contract_display_account", "contract_notify_url")
+	if err != nil {
+		return nil, err
+	}
+	bs, err := w.doWeChatPostProd(bm, entrustPaying, nil)
+	if err != nil {
+		return nil, err
+	}
+	wxRsp = new(EntrustPayingResponse)
+	if err = xml.Unmarshal(bs, wxRsp); err != nil {
+		return nil, fmt.Errorf("xml.Unmarshal(%s):%w", string(bs), err)
+	}
+	return wxRsp, nil
+}
+
 // Post请求
 // Post请求
-func (w *Client) doWeChatPost(bm gopay.BodyMap, path string, tlsConfig *tls.Config) (bs []byte, err error) {
+func (w *Client) doWeChatPostSanBox(bm gopay.BodyMap, path string) (bs []byte, err error) {
 	var url = baseUrlCh + path
 	var url = baseUrlCh + path
 	w.mu.RLock()
 	w.mu.RLock()
 	defer w.mu.RUnlock()
 	defer w.mu.RUnlock()
 	bm.Set("appid", w.AppId)
 	bm.Set("appid", w.AppId)
 	bm.Set("mch_id", w.MchId)
 	bm.Set("mch_id", w.MchId)
+
 	if bm.Get("sign") == gopay.NULL {
 	if bm.Get("sign") == gopay.NULL {
-		var sign string
-		if !w.IsProd {
-			bm.Set("sign_type", SignType_MD5)
-			sign, err = getSignBoxSign(w.MchId, w.ApiKey, bm)
-			if err != nil {
-				return nil, err
-			}
-		} else {
-			sign = getReleaseSign(w.ApiKey, bm.Get("sign_type"), bm)
+		bm.Set("sign_type", SignType_MD5)
+		sign, err := getSignBoxSign(w.MchId, w.ApiKey, bm)
+		if err != nil {
+			return nil, err
 		}
 		}
 		bm.Set("sign", sign)
 		bm.Set("sign", sign)
 	}
 	}
 
 
+	if w.BaseURL != gopay.NULL {
+		url = w.BaseURL + path
+	}
+	res, bs, errs := gopay.NewHttpClient().Type(gopay.TypeXML).Post(url).SendString(generateXml(bm)).EndBytes()
+	if len(errs) > 0 {
+		return nil, errs[0]
+	}
+	if res.StatusCode != 200 {
+		return nil, fmt.Errorf("HTTP Request Error, StatusCode = %d", res.StatusCode)
+	}
+	if strings.Contains(string(bs), "HTML") || strings.Contains(string(bs), "html") {
+		return nil, errors.New(string(bs))
+	}
+	return bs, nil
+}
+
+// Post请求、正式
+func (w *Client) doWeChatPostProd(bm gopay.BodyMap, path string, tlsConfig *tls.Config) (bs []byte, err error) {
+	var url = baseUrlCh + path
+	w.mu.RLock()
+	defer w.mu.RUnlock()
+	bm.Set("appid", w.AppId)
+	bm.Set("mch_id", w.MchId)
+
+	if bm.Get("sign") == gopay.NULL {
+		sign := getReleaseSign(w.ApiKey, bm.Get("sign_type"), bm)
+		bm.Set("sign", sign)
+	}
+
 	httpClient := gopay.NewHttpClient()
 	httpClient := gopay.NewHttpClient()
 	if w.IsProd && tlsConfig != nil {
 	if w.IsProd && tlsConfig != nil {
 		httpClient.SetTLSConfig(tlsConfig)
 		httpClient.SetTLSConfig(tlsConfig)
@@ -498,8 +517,8 @@ func (w *Client) doWeChatPost(bm gopay.BodyMap, path string, tlsConfig *tls.Conf
 	return bs, nil
 	return bs, nil
 }
 }
 
 
-// Get请求
-func (w *Client) doWeChatGet(bm gopay.BodyMap, path, signType string) (bs []byte, err error) {
+// Get请求、正式
+func (w *Client) doWeChatGetProd(bm gopay.BodyMap, path, signType string) (bs []byte, err error) {
 	var url = baseUrlCh + path
 	var url = baseUrlCh + path
 	w.mu.RLock()
 	w.mu.RLock()
 	defer w.mu.RUnlock()
 	defer w.mu.RUnlock()

+ 46 - 2
wechat/client_test.go

@@ -146,12 +146,25 @@ func TestClient_Micropay(t *testing.T) {
 		return
 		return
 	}
 	}
 	fmt.Println("Response:", *wxRsp)
 	fmt.Println("Response:", *wxRsp)
-
 	ok, err := VerifySign(apiKey, SignType_MD5, wxRsp)
 	ok, err := VerifySign(apiKey, SignType_MD5, wxRsp)
 	if err != nil {
 	if err != nil {
 		fmt.Println("err:", err)
 		fmt.Println("err:", err)
 	}
 	}
-	fmt.Println("同步验签结果:", ok)
+	fmt.Println("同步验签结果:", ok) // 沙箱环境验签失败请用正式环境测
+}
+
+func TestClient_AuthCodeToOpenId(t *testing.T) {
+	// 初始化参数Map
+	bm := make(gopay.BodyMap)
+	bm.Set("nonce_str", gopay.GetRandomString(32))
+	bm.Set("auth_code", "134753997737645794")
+
+	wxRsp, err := client.AuthCodeToOpenId(bm)
+	if err != nil {
+		fmt.Println("Error:", err)
+		return
+	}
+	fmt.Println("Response:", *wxRsp)
 }
 }
 
 
 func TestClient_Refund(t *testing.T) {
 func TestClient_Refund(t *testing.T) {
@@ -353,6 +366,37 @@ func TestClient_EntrustH5(t *testing.T) {
 	fmt.Println("wxRsp:", wxRsp)
 	fmt.Println("wxRsp:", wxRsp)
 }
 }
 
 
+func TestClient_EntrustPaying(t *testing.T) {
+	number := gopay.GetRandomString(32)
+	fmt.Println("out_trade_no:", number)
+	// 初始化参数结构体
+	bm := make(gopay.BodyMap)
+	bm.Set("contract_mchid", mchId)
+	bm.Set("contract_appid", appId)
+	bm.Set("out_trade_no", number)
+	bm.Set("nonce_str", gopay.GetRandomString(32))
+	bm.Set("body", "测试签约")
+	bm.Set("total_fee", 1)
+	bm.Set("spbill_create_ip", "127.0.0.1")
+	bm.Set("trade_type", TradeType_App)
+	bm.Set("plan_id", "12535")
+	bm.Set("contract_code", "100000")
+	bm.Set("request_serial", "1000")
+	bm.Set("contract_display_account", "微信代扣")
+	bm.Set("notify_url", "https://www.igoogle.ink")
+	bm.Set("contract_notify_url", "https://www.igoogle.ink")
+
+	//bm.Set("openid", "o0Df70H2Q0fY8JXh1aFPIRyOBgu8")
+
+	// 支付中签约
+	wxRsp, err := client.EntrustPaying(bm)
+	if err != nil {
+		fmt.Println("Error:", err)
+		return
+	}
+	fmt.Println("wxRsp:", wxRsp)
+}
+
 // =======================
 // =======================
 
 
 func TestDecryptOpenDataToStruct(t *testing.T) {
 func TestDecryptOpenDataToStruct(t *testing.T) {

+ 35 - 14
wechat/model.go

@@ -37,20 +37,16 @@ const (
 	entrustQueryOrder = "pay/paporderquery"                     // 查询扣款订单
 	entrustQueryOrder = "pay/paporderquery"                     // 查询扣款订单
 
 
 	// SanBox
 	// SanBox
-	sandboxGetSignKey        = "https://api.mch.weixin.qq.com/sandboxnew/pay/getsignkey"
-	sandboxMicroPay          = "sandboxnew/pay/micropay"
-	sandboxUnifiedOrder      = "sandboxnew/pay/unifiedorder"
-	sandboxOrderQuery        = "sandboxnew/pay/orderquery"
-	sandboxCloseOrder        = "sandboxnew/pay/closeorder"
-	sandboxRefund            = "sandboxnew/pay/refund"
-	sandboxReverse           = "sandboxnew/pay/reverse"
-	sandboxRefundQuery       = "sandboxnew/pay/refundquery"
-	sandboxDownloadBill      = "sandboxnew/pay/downloadbill"
-	sandboxDownloadFundFlow  = "sandboxnew/pay/downloadfundflow"
-	sandboxReport            = "sandboxnew/payitil/report"
-	sandboxBatchQueryComment = "sandboxnew/billcommentsp/batchquerycomment"
-	sandboxAuthCodeToOpenid  = "sandboxnew/tools/authcodetoopenid"
-	sandboxEntrustH5         = "sandboxnew/papay/h5entrustweb"
+	sandboxGetSignKey   = "https://api.mch.weixin.qq.com/sandboxnew/pay/getsignkey"
+	sandboxMicroPay     = "sandboxnew/pay/micropay"
+	sandboxUnifiedOrder = "sandboxnew/pay/unifiedorder"
+	sandboxOrderQuery   = "sandboxnew/pay/orderquery"
+	sandboxCloseOrder   = "sandboxnew/pay/closeorder"
+	sandboxRefund       = "sandboxnew/pay/refund"
+	sandboxReverse      = "sandboxnew/pay/reverse"
+	sandboxRefundQuery  = "sandboxnew/pay/refundquery"
+	sandboxDownloadBill = "sandboxnew/pay/downloadbill"
+	sandboxReport       = "sandboxnew/payitil/report"
 
 
 	// 支付类型
 	// 支付类型
 	TradeType_Mini   = "JSAPI"  // 小程序支付
 	TradeType_Mini   = "JSAPI"  // 小程序支付
@@ -354,6 +350,31 @@ type EntrustH5Response struct {
 	RedirectUrl string `xml:"redirect_url,omitempty" json:"redirect_url,omitempty"`
 	RedirectUrl string `xml:"redirect_url,omitempty" json:"redirect_url,omitempty"`
 }
 }
 
 
+type EntrustPayingResponse struct {
+	ReturnCode             string `xml:"return_code,omitempty" json:"return_code,omitempty"`
+	ReturnMsg              string `xml:"return_msg,omitempty" json:"return_msg,omitempty"`
+	ResultCode             string `xml:"result_code,omitempty" json:"result_code,omitempty"`
+	Appid                  string `xml:"appid,omitempty" json:"appid,omitempty"`
+	MchId                  string `xml:"mch_id,omitempty" json:"mch_id,omitempty"`
+	DeviceInfo             string `xml:"device_info,omitempty" json:"device_info,omitempty"`
+	NonceStr               string `xml:"nonce_str,omitempty" json:"nonce_str,omitempty"`
+	Sign                   string `xml:"sign,omitempty" json:"sign,omitempty"`
+	ErrCode                string `xml:"err_code,omitempty" json:"err_code,omitempty"`
+	ErrCodeDes             string `xml:"err_code_des,omitempty" json:"err_code_des,omitempty"`
+	ContractResultCode     string `xml:"contract_result_code,omitempty" json:"contract_result_code,omitempty"`
+	ContractErrCode        string `xml:"contract_err_code,omitempty" json:"contract_err_code,omitempty"`
+	ContractErrCodeDes     string `xml:"contract_err_code_des,omitempty" json:"contract_err_code_des,omitempty"`
+	PrepayId               string `xml:"prepay_id,omitempty" json:"prepay_id,omitempty"`
+	TradeType              string `xml:"trade_type,omitempty" json:"trade_type,omitempty"`
+	CodeUrl                string `xml:"code_url,omitempty" json:"code_url,omitempty"`
+	PlanId                 int    `xml:"plan_id,omitempty" json:"plan_id,omitempty"`
+	RequestSerial          int    `xml:"request_serial,omitempty" json:"request_serial,omitempty"`
+	ContractCode           string `xml:"contract_code,omitempty" json:"contract_code,omitempty"`
+	ContractDisplayAccount string `xml:"contract_display_account,omitempty" json:"contract_display_account,omitempty"`
+	MwebUrl                string `xml:"mweb_url,omitempty" json:"mweb_url,omitempty"`
+	OutTradeNo             string `xml:"out_trade_no,omitempty" json:"out_trade_no,omitempty"`
+}
+
 type getSignKeyResponse struct {
 type getSignKeyResponse struct {
 	ReturnCode     string `xml:"return_code,omitempty" json:"return_code,omitempty"`
 	ReturnCode     string `xml:"return_code,omitempty" json:"return_code,omitempty"`
 	ReturnMsg      string `xml:"return_msg,omitempty" json:"return_msg,omitempty"`
 	ReturnMsg      string `xml:"return_msg,omitempty" json:"return_msg,omitempty"`

+ 4 - 2
wechat/service_api.go

@@ -174,7 +174,8 @@ func VerifySign(apiKey, signType string, bean interface{}) (ok bool, err error)
 		bm := bean.(gopay.BodyMap)
 		bm := bean.(gopay.BodyMap)
 		bodySign := bm.Get("sign")
 		bodySign := bm.Get("sign")
 		bm.Remove("sign")
 		bm.Remove("sign")
-		return getReleaseSign(apiKey, signType, bm) == bodySign, nil
+		sign := getReleaseSign(apiKey, signType, bm)
+		return sign == bodySign, nil
 	}
 	}
 
 
 	bs, err := json.Marshal(bean)
 	bs, err := json.Marshal(bean)
@@ -187,7 +188,8 @@ func VerifySign(apiKey, signType string, bean interface{}) (ok bool, err error)
 	}
 	}
 	bodySign := bm.Get("sign")
 	bodySign := bm.Get("sign")
 	bm.Remove("sign")
 	bm.Remove("sign")
-	return getReleaseSign(apiKey, signType, bm) == bodySign, nil
+	sign := getReleaseSign(apiKey, signType, bm)
+	return sign == bodySign, nil
 }
 }
 
 
 type NotifyResponse struct {
 type NotifyResponse struct {