Jerry 7 tahun lalu
induk
melakukan
1f6ffefc30
13 mengubah file dengan 484 tambahan dan 345 penghapusan
  1. 66 46
      README.md
  2. 0 0
      alipay_client.go
  3. 0 0
      alipay_client_test.go
  4. 14 14
      constant.go
  5. 66 22
      util.go
  6. 154 0
      wechat_client.go
  7. 62 0
      wechat_client_test.go
  8. 52 72
      wechat_params.go
  9. 0 109
      wechat_pay.go
  10. 0 50
      wechat_pay_test.go
  11. 5 0
      wechat_req.go
  12. 0 32
      wechat_response.go
  13. 65 0
      wechat_rsp.go

+ 66 - 46
README.md

@@ -15,7 +15,7 @@
     * NATIVE - Native支付
     * APP - app支付
     * MWEB - H5支付
-* 查询订单(开发中)
+* 查询订单
 * 关闭订单(开发中)
 * 申请退款(开发中)
 * 查询退款(开发中)
@@ -34,62 +34,82 @@ $ go get github.com/iGoogle-ink/gopay
 
 未完成
 
-## 微信统一下单 example
+## 微信
+
+<font color='#0088ff'>注意:具体参数根据请求的不同而不同,请参考微信官方文档的参数说明!</font>
+
+参考文档:[微信支付文档](https://pay.weixin.qq.com/wiki/doc/api/index.html)
 
-* 初始化客户端
-    * 参数:AppId:应用ID
-    * 参数:mchID:商户ID
-    * 参数:secretKey:Key值
-    * 参数:isProd:是否正式环境
+
+### 统一下单
 ```go
-//正式环境 
+//初始化微信客户端
+//    appId:应用ID
+//    mchID:商户ID
+//    secretKey:Key值
+//    isProd:是否是正式环境
 client := gopay.NewWeChatClient("wxd678efh567hg6787", "1230000109", "192006250b4c09247ec02edce69f6a2d", true)
 
-//沙箱环境
-client := gopay.NewWeChatClient("wxd678efh567hg6787", "1230000109", "192006250b4c09247ec02edce69f6a2d", false)
+//初始化参数Map
+body := make(gopay.BodyMap)
+body.Set("nonce_str", gopay.GetRandomString(32))
+body.Set("body", "测试支付")
+number := gopay.GetRandomString(32)
+log.Println("Number:", number)
+body.Set("out_trade_no", number)
+body.Set("total_fee", 1)
+body.Set("spbill_create_ip", "127.0.0.1")   //终端IP
+body.Set("notify_url", "http://www.igoogle.ink")
+body.Set("trade_type", gopay.TradeType_JsApi)
+body.Set("device_info", "WEB")
+body.Set("sign_type", gopay.SignType_MD5)
+//body.Set("scene_info", `{"h5_info": {"type":"Wap","wap_url": "http://www.igoogle.ink","wap_name": "测试支付"}}`)
+body.Set("openid", "o0Df70H2Q0fY8JXh1aFPIRyOBgu6")
+
+//发起下单请求
+wxRsp, err := client.UnifiedOrder(body)
+if err != nil {
+	fmt.Println("Error:", err)
+	return
+}
+fmt.Println("ReturnCode:", wxRsp.ReturnCode)
+fmt.Println("ReturnMsg:", wxRsp.ReturnMsg)
+fmt.Println("Appid:", wxRsp.Appid)
+fmt.Println("MchId:", wxRsp.MchId)
+fmt.Println("DeviceInfo:", wxRsp.DeviceInfo)
+fmt.Println("NonceStr:", wxRsp.NonceStr)
+fmt.Println("Sign:", wxRsp.Sign)
+fmt.Println("ResultCode:", wxRsp.ResultCode)
+fmt.Println("ErrCode:", wxRsp.ErrCode)
+fmt.Println("ErrCodeDes:", wxRsp.ErrCodeDes)
+fmt.Println("PrepayId:", wxRsp.PrepayId)
+fmt.Println("TradeType:", wxRsp.TradeType)
+fmt.Println("CodeUrl:", wxRsp.CodeUrl)
+fmt.Println("MwebUrl:", wxRsp.MwebUrl)
 ```
 
-* 初始化统一下单参数
-> 以下参数设置皆为必选参数,如需其他参数,请参考API文档。
->
-> 参考文档:[微信支付文档](https://pay.weixin.qq.com/wiki/doc/api/index.html)
+### 查询订单
 ```go
-params := new(gopay.WeChatPayParams)
-params.NonceStr = "dyUNIkNS29hvDUC1CmoF0alSdfCQGg9I"
-params.Body = "支付测试"
-params.OutTradeNo = "GYsadfjk4dhg3fkh3ffgnlsdkf"
-params.TotalFee = 10 //单位为分,如沙箱环境,则默认为101
-params.SpbillCreateIp = "127.0.0.1"
-params.NotifyUrl = "http://www.igoogle.ink"
-params.TradeType = gopay.WX_PayType_JsApi
-params.DeviceInfo = "WEB"
-params.SignType = gopay.WX_SignType_HMAC_SHA256 //如不设置此参数,默认为MD5,如沙箱环境,则默认为MD5
-params.Openid = "o0Df70H2Q0fY8JXh1aFPIRyOBgu8" //JSAPI 方式时,此参数必填
-```
+//初始化微信客户端
+//    appId:应用ID
+//    mchID:商户ID
+//    secretKey:Key值
+//    isProd:是否是正式环境
+client := gopay.NewWeChatClient("wxd678efh567hg6787", "1230000109", "192006250b4c09247ec02edce69f6a2d", true)
 
-* 发起统一下单请求
-    * 参数:param:统一下单请求参数
-> 请求成功后,获取下单结果
-```go
-wxRsp, err := client.UnifiedOrder(params)
+//初始化参数结构体
+body := make(gopay.BodyMap)
+body.Set("out_trade_no", "CC68aTofMIwVKkVR5UruoBLFFXTAqBfv")
+body.Set("nonce_str", gopay.GetRandomString(32))
+body.Set("sign_type", gopay.SignType_MD5)
+
+//请求订单查询
+wxRsp, err := client.QueryOrder(body)
 if err != nil {
 	fmt.Println("Error:", err)
-} else {
-	fmt.Println("ReturnCode:", wxRsp.ReturnCode)
-	fmt.Println("ReturnMsg:", wxRsp.ReturnMsg)
-	fmt.Println("Appid:", wxRsp.Appid)
-	fmt.Println("MchId:", wxRsp.MchId)
-	fmt.Println("DeviceInfo:", wxRsp.DeviceInfo)
-	fmt.Println("NonceStr:", wxRsp.NonceStr)
-	fmt.Println("Sign:", wxRsp.Sign)
-	fmt.Println("ResultCode:", wxRsp.ResultCode)
-	fmt.Println("ErrCode:", wxRsp.ErrCode)
-	fmt.Println("ErrCodeDes:", wxRsp.ErrCodeDes)
-	fmt.Println("PrepayId:", wxRsp.PrepayId)
-	fmt.Println("TradeType:", wxRsp.TradeType)
-	fmt.Println("CodeUrl:", wxRsp.CodeUrl)
-	fmt.Println("MwebUrl:", wxRsp.MwebUrl)
+	return
 }
+fmt.Println("Response:", wxRsp)
 ```
 
 ## 支付宝支付 example

+ 0 - 0
ali_pay.go → alipay_client.go


+ 0 - 0
ali_pay_test.go → alipay_client_test.go


+ 14 - 14
constant.go

@@ -5,25 +5,25 @@ const (
 	wx_base_url        = "https://api.mch.weixin.qq.com/"
 	wx_sanbox_base_url = "https://api.mch.weixin.qq.com/sandboxnew/"
 
-	wxURL_unifiedOrder = wx_base_url + "pay/unifiedorder"
-	wxURL_orderquery   = wx_base_url + "pay/orderquery"
-	wxURL_closeorder   = wx_base_url + "pay/closeorder"
+	wxURL_UnifiedOrder = wx_base_url + "pay/unifiedorder"
+	wxURL_OrderQuery   = wx_base_url + "pay/orderquery"
+	wxURL_CloseOrder   = wx_base_url + "pay/closeorder"
 
-	wxURL_sanbox_getsignkey   = wx_sanbox_base_url + "pay/getsignkey"
-	wxURL_sanbox_unifiedOrder = wx_sanbox_base_url + "pay/unifiedorder"
-	wxURL_sanbox_orderquery   = wx_sanbox_base_url + "pay/orderquery"
-	wxURL_sanbox_closeorder   = wx_sanbox_base_url + "pay/closeorder"
+	wxURL_SanBox_GetSignKey   = wx_sanbox_base_url + "pay/getsignkey"
+	wxURL_SanBox_UnifiedOrder = wx_sanbox_base_url + "pay/unifiedorder"
+	wxURL_SanBox_OrderQuery   = wx_sanbox_base_url + "pay/orderquery"
+	wxURL_SanBox_CloseOrder   = wx_sanbox_base_url + "pay/closeorder"
 
 	//支付类型
-	WX_PayType_Mini   = "JSAPI"
-	WX_PayType_JsApi  = "JSAPI"
-	WX_PayType_App    = "APP"
-	WX_PayType_H5     = "MWEB"
-	WX_PayType_Native = "NATIVE"
+	TradeType_Mini   = "JSAPI"
+	TradeType_JsApi  = "JSAPI"
+	TradeType_App    = "APP"
+	TradeType_H5     = "MWEB"
+	TradeType_Native = "NATIVE"
 
 	//签名方式
-	WX_SignType_MD5         = "MD5"
-	WX_SignType_HMAC_SHA256 = "HMAC-SHA256"
+	SignType_MD5         = "MD5"
+	SignType_HMAC_SHA256 = "HMAC-SHA256"
 
 	//Debug数据
 	secretKey = "GFDS8j98rewnmgl45wHTt980jg543wmg"

+ 66 - 22
util.go

@@ -2,29 +2,39 @@ package gopay
 
 import (
 	"bytes"
+	"encoding/xml"
+	"errors"
+	"github.com/parnurzeal/gorequest"
 	"math/rand"
+	"sort"
+	"strconv"
 	"time"
 )
 
-type requestBody map[string]string
+type BodyMap map[string]interface{}
 
 //设置参数
-func (w requestBody) Set(key string, value string) {
-	w[key] = value
+func (bm BodyMap) Set(key string, value interface{}) {
+	bm[key] = value
 }
 
 //获取参数
-func (w requestBody) Get(key string) string {
-	if w == nil {
+func (bm BodyMap) Get(key string) string {
+	if bm == nil {
 		return ""
 	}
-	ws := w[key]
-	return ws
+	v := bm[key]
+	value, ok := v.(int)
+	if ok {
+		value := strconv.Itoa(value)
+		return value
+	}
+	return v.(string)
 }
 
 //删除参数
-func (w requestBody) Remove(key string) {
-	delete(w, key)
+func (bm BodyMap) Remove(key string) {
+	delete(bm, key)
 }
 
 //获取随机字符串
@@ -40,20 +50,54 @@ func GetRandomString(length int) string {
 	return string(result)
 }
 
-func generateXml(w requestBody) (reqXml string) {
+//获取根据Key排序后的请求参数字符串
+func sortSignParams(secretKey string, body BodyMap) string {
+	keyList := make([]string, 0)
+	for k := range body {
+		keyList = append(keyList, k)
+	}
+	sort.Strings(keyList)
 	buffer := new(bytes.Buffer)
-	buffer.WriteString("<xml>")
-
-	for k, v := range w {
-		buffer.WriteString("<")
+	for _, k := range keyList {
 		buffer.WriteString(k)
-		buffer.WriteString("><![CDATA[")
-		buffer.WriteString(v)
-		buffer.WriteString("]]></")
-		buffer.WriteString(k)
-		buffer.WriteString(">")
+		buffer.WriteString("=")
+		value, ok := body[k].(int)
+		if ok {
+			value := strconv.Itoa(value)
+			buffer.WriteString(value)
+		} else {
+			buffer.WriteString(body[k].(string))
+		}
+		buffer.WriteString("&")
+	}
+	buffer.WriteString("key=")
+	buffer.WriteString(secretKey)
+	return buffer.String()
+}
+
+//从微信提供的接口获取:SandboxSignkey
+func getSanBoxSignKey(mchId, nonceStr, sign string) (key string, err error) {
+	reqs := make(BodyMap)
+	reqs.Set("mch_id", mchId)
+	reqs.Set("nonce_str", nonceStr)
+	reqs.Set("sign", sign)
+
+	reqXml := generateXml(reqs)
+	//fmt.Println("req:::", reqXml)
+	_, byteList, errorList := gorequest.New().
+		Post(wxURL_SanBox_GetSignKey).
+		Type("xml").
+		SendString(reqXml).EndBytes()
+	if len(errorList) > 0 {
+		return "", errorList[0]
+	}
+	keyResponse := new(getSignKeyResponse)
+	err = xml.Unmarshal(byteList, keyResponse)
+	if err != nil {
+		return "", err
+	}
+	if keyResponse.ReturnCode == "FAIL" {
+		return "", errors.New(keyResponse.Retmsg)
 	}
-	buffer.WriteString("</xml>")
-	reqXml = buffer.String()
-	return
+	return keyResponse.SandboxSignkey, nil
 }

+ 154 - 0
wechat_client.go

@@ -0,0 +1,154 @@
+package gopay
+
+import (
+	"encoding/xml"
+	"fmt"
+	"github.com/parnurzeal/gorequest"
+)
+
+type weChatClient struct {
+	AppId     string
+	MchId     string
+	secretKey string
+	isProd    bool
+}
+
+//初始化微信客户端
+//    appId:应用ID
+//    mchID:商户ID
+//    secretKey:Key值
+//    isProd:是否是正式环境
+func NewWeChatClient(appId, mchId, secretKey string, isProd bool) *weChatClient {
+	client := new(weChatClient)
+	client.AppId = appId
+	client.MchId = mchId
+	client.secretKey = secretKey
+	client.isProd = isProd
+	return client
+}
+
+//统一下单
+func (this *weChatClient) UnifiedOrder(body BodyMap) (wxRsp *weChatUnifiedOrderResponse, err error) {
+	var sign string
+	body.Set("appid", this.AppId)
+	body.Set("mch_id", this.MchId)
+	//===============生成参数===================
+	if !this.isProd {
+		//沙箱环境
+		body.Set("total_fee", 101)
+		body.Set("sign_type", SignType_MD5)
+		//从微信接口获取SanBoxSignKey
+		key, err := getSanBoxSign(this.MchId, body.Get("nonce_str"), this.secretKey, body.Get("sign_type"))
+		if err != nil {
+			return nil, err
+		}
+		sign = getLocalSign(key, body.Get("sign_type"), body)
+	} else {
+		//正式环境
+		//本地计算Sign
+		sign = getLocalSign(this.secretKey, body.Get("sign_type"), body)
+	}
+
+	body.Set("sign", sign)
+
+	reqXML := generateXml(body)
+	fmt.Println("req:::", reqXML)
+	//===============发起请求===================
+	agent := gorequest.New()
+	if this.isProd {
+		agent.Post(wxURL_UnifiedOrder)
+	} else {
+		agent.Post(wxURL_SanBox_UnifiedOrder)
+	}
+	agent.Type("xml")
+	agent.SendString(reqXML)
+	response, bytes, errs := agent.EndBytes()
+	defer response.Body.Close()
+	if len(errs) > 0 {
+		return nil, errs[0]
+	}
+	//fmt.Println("bytes:", string(bytes))
+	wxRsp = new(weChatUnifiedOrderResponse)
+	err = xml.Unmarshal(bytes, wxRsp)
+	if err != nil {
+		return nil, err
+	}
+	return wxRsp, nil
+}
+
+//查询订单
+func (this *weChatClient) QueryOrder(body BodyMap) (wxRsp *weChatQueryOrderResponse, err error) {
+	var sign string
+	body.Set("appid", this.AppId)
+	body.Set("mch_id", this.MchId)
+	//===============生成参数===================
+	if !this.isProd {
+		//沙箱环境
+		body.Set("sign_type", SignType_MD5)
+		//从微信接口获取SanBoxSignKey
+		key, err := getSanBoxSign(this.MchId, body.Get("nonce_str"), this.secretKey, body.Get("sign_type"))
+		if err != nil {
+			return nil, err
+		}
+		sign = getLocalSign(key, body.Get("sign_type"), body)
+	} else {
+		//正式环境
+		//本地计算Sign
+		sign = getLocalSign(this.secretKey, body.Get("sign_type"), body)
+	}
+	body.Set("sign", sign)
+
+	reqXML := generateXml(body)
+	//fmt.Println("req:::", reqXML)
+	//===============发起请求===================
+	agent := gorequest.New()
+	if this.isProd {
+		agent.Post(wxURL_OrderQuery)
+	} else {
+		agent.Post(wxURL_SanBox_OrderQuery)
+	}
+	agent.Type("xml")
+	agent.SendString(reqXML)
+	response, bytes, errs := agent.EndBytes()
+	defer response.Body.Close()
+	if len(errs) > 0 {
+		return nil, errs[0]
+	}
+	//fmt.Println("bytes:", string(bytes))
+	wxRsp = new(weChatQueryOrderResponse)
+	err = xml.Unmarshal(bytes, wxRsp)
+	if err != nil {
+		return nil, err
+	}
+	return wxRsp, nil
+}
+
+//关闭订单
+func (this *weChatClient) CloseOrder() {
+
+}
+
+//申请退款
+func (this *weChatClient) Refund() {
+
+}
+
+//查询退款
+func (this *weChatClient) QueryRefund() {
+
+}
+
+//下载对账单
+func (this *weChatClient) DownloadBill() {
+
+}
+
+//下载资金账单
+func (this *weChatClient) DownloadFundFlow() {
+
+}
+
+//拉取订单评价数据
+func (this *weChatClient) BatchQueryComment() {
+
+}

+ 62 - 0
wechat_client_test.go

@@ -0,0 +1,62 @@
+package gopay
+
+import (
+	"fmt"
+	"log"
+	"testing"
+)
+
+func TestWeChatClient_UnifiedOrder(t *testing.T) {
+
+	//初始化微信客户端
+	//    appId:应用ID
+	//    mchID:商户ID
+	//    secretKey:Key值
+	//    isProd:是否是正式环境
+	client := NewWeChatClient(appID, mchID, secretKey, true)
+
+	//初始化参数Map
+	body := make(BodyMap)
+	body.Set("nonce_str", GetRandomString(32))
+	body.Set("body", "测试支付")
+	number := GetRandomString(32)
+	log.Println("Number:", number)
+	body.Set("out_trade_no", number)
+	body.Set("total_fee", 10)
+	body.Set("spbill_create_ip", "180.171.101.212")
+	body.Set("notify_url", "http://www.igoogle.ink")
+	body.Set("trade_type", TradeType_JsApi)
+	//body.Set("device_info", "WEB")
+	body.Set("sign_type", SignType_MD5)
+	//body.Set("scene_info", `{"h5_info": {"type":"Wap","wap_url": "http://www.igoogle.ink","wap_name": "测试支付"}}`)
+	//body.Set("openid", openID)
+
+	//请求支付下单,成功后得到结果
+	wxRsp, err := client.UnifiedOrder(body)
+	if err != nil {
+		fmt.Println("Error:", err)
+	}
+	fmt.Println("Response:", wxRsp)
+}
+
+func TestWeChatClient_QueryOrder(t *testing.T) {
+	//初始化微信客户端
+	//    appId:应用ID
+	//    mchID:商户ID
+	//    secretKey:Key值
+	//    isProd:是否是正式环境
+	client := NewWeChatClient(appID, mchID, secretKey, true)
+
+	//初始化参数结构体
+	params := make(BodyMap)
+	params.Set("out_trade_no", "CC68aTofMIwVKkVR5UruoBLFFXTAqBfv")
+	params.Set("nonce_str", GetRandomString(32))
+	params.Set("sign_type", SignType_MD5)
+
+	//请求订单查询,成功后得到结果
+	wxRsp, err := client.QueryOrder(params)
+	if err != nil {
+		fmt.Println("Error:", err)
+	}
+	fmt.Println("Response:", wxRsp)
+}

+ 52 - 72
wechat_params.go

@@ -6,23 +6,25 @@ import (
 	"crypto/md5"
 	"crypto/sha256"
 	"encoding/hex"
-	"encoding/xml"
-	"errors"
-	"github.com/parnurzeal/gorequest"
-	"sort"
 	"strconv"
 	"strings"
 )
 
 //获取请求支付的参数
-func (w *WeChatPayParams) getRequestBody(appId, mchId string, params *WeChatPayParams) (reqs requestBody) {
-	reqs = make(requestBody)
+func getRequestBody(appId, mchId string, params *WeChatPayParams) (reqs BodyMap) {
+	reqs = make(BodyMap)
 	reqs.Set("appid", appId)
 	reqs.Set("mch_id", mchId)
 	reqs.Set("nonce_str", params.NonceStr)
-	reqs.Set("body", params.Body)
-	reqs.Set("out_trade_no", params.OutTradeNo)
-	reqs.Set("total_fee", strconv.Itoa(params.TotalFee))
+	if params.Body != "" {
+		reqs.Set("body", params.Body)
+	}
+	if params.OutTradeNo != "" {
+		reqs.Set("out_trade_no", params.OutTradeNo)
+	}
+	if params.TotalFee != -1 {
+		reqs.Set("total_fee", strconv.Itoa(params.TotalFee))
+	}
 	reqs.Set("spbill_create_ip", params.SpbillCreateIp)
 	reqs.Set("notify_url", params.NotifyUrl)
 	reqs.Set("trade_type", params.TradeType)
@@ -66,37 +68,20 @@ func (w *WeChatPayParams) getRequestBody(appId, mchId string, params *WeChatPayP
 		reqs.Set("receipt", params.Receipt)
 	}
 	if params.SceneInfo != "" {
-		//marshal, _ := json.Marshal(params.SceneInfo)
-		//reqs.Set("scene_info", string(marshal))
 		reqs.Set("scene_info", params.SceneInfo)
 	}
-	return reqs
-}
-
-//获取SanBox秘钥
-func (w *WeChatPayParams) getSanBoxSignKey(mchId, nonceStr, secretKey, signType string) (key string, err error) {
-	body := make(requestBody)
-	body.Set("mch_id", mchId)
-	body.Set("nonce_str", nonceStr)
-
-	//计算沙箱参数Sign
-	sanboxSign := getSign(secretKey, signType, body)
-	//沙箱环境:获取key后,重新计算Sign
-	key, err = getSanBoxSignKey(mchId, nonceStr, sanboxSign)
-	if err != nil {
-		return "", err
+	if params.TransactionId != "" {
+		reqs.Set("transaction_id", params.TransactionId)
 	}
-	return
+	return reqs
 }
 
-//获取Sign签名和请求支付的参数
-func getSign(secretKey string, signType string, body requestBody) (sign string) {
-
-	signStr := getSignString(secretKey, body)
+//本地通过支付参数计算Sign值
+func getLocalSign(secretKey string, signType string, body BodyMap) (sign string) {
+	signStr := sortSignParams(secretKey, body)
 	//fmt.Println("signStr:", signStr)
 	var hashSign []byte
-	if signType == WX_SignType_MD5 {
-
+	if signType == SignType_MD5 {
 		hash := md5.New()
 		hash.Write([]byte(signStr))
 		hashSign = hash.Sum(nil)
@@ -109,48 +94,43 @@ func getSign(secretKey string, signType string, body requestBody) (sign string)
 	return
 }
 
-//获取根据Key排序后的请求参数字符串
-func getSignString(secretKey string, body requestBody) string {
-	keyList := make([]string, 0)
-	for k := range body {
-		keyList = append(keyList, k)
-	}
-	sort.Strings(keyList)
-	buffer := new(bytes.Buffer)
-	for _, k := range keyList {
-		buffer.WriteString(k)
-		buffer.WriteString("=")
-		buffer.WriteString(body[k])
-		buffer.WriteString("&")
-	}
-	buffer.WriteString("key=")
-	buffer.WriteString(secretKey)
-	return buffer.String()
-}
-
-//获取SanboxKey
-func getSanBoxSignKey(mchId, nonceStr, sign string) (key string, err error) {
-	reqs := make(requestBody)
-	reqs.Set("mch_id", mchId)
-	reqs.Set("nonce_str", nonceStr)
-	reqs.Set("sign", sign)
+//从微信提供的接口获取:SandboxSignKey
+func getSanBoxSign(mchId, nonceStr, secretKey, signType string) (key string, err error) {
+	body := make(BodyMap)
+	body.Set("mch_id", mchId)
+	body.Set("nonce_str", nonceStr)
 
-	reqXml := generateXml(reqs)
-	//fmt.Println("req:::", reqXml)
-	_, byteList, errorList := gorequest.New().
-		Post(wxURL_sanbox_getsignkey).
-		Type("xml").
-		SendString(reqXml).EndBytes()
-	if len(errorList) > 0 {
-		return "", errorList[0]
-	}
-	keyResponse := new(getSignKeyResponse)
-	err = xml.Unmarshal(byteList, keyResponse)
+	//计算沙箱参数Sign
+	sanboxSign := getLocalSign(secretKey, signType, body)
+	//沙箱环境:获取key后,重新计算Sign
+	key, err = getSanBoxSignKey(mchId, nonceStr, sanboxSign)
 	if err != nil {
 		return "", err
 	}
-	if keyResponse.ReturnCode == "FAIL" {
-		return "", errors.New(keyResponse.Retmsg)
+	return
+}
+
+//生成请求XML的Body体
+func generateXml(bm BodyMap) (reqXml string) {
+	buffer := new(bytes.Buffer)
+	buffer.WriteString("<xml>")
+
+	for k, v := range bm {
+		buffer.WriteString("<")
+		buffer.WriteString(k)
+		buffer.WriteString("><![CDATA[")
+		value, ok := v.(int)
+		if ok {
+			value := strconv.Itoa(value)
+			buffer.WriteString(value)
+		} else {
+			buffer.WriteString(v.(string))
+		}
+		buffer.WriteString("]]></")
+		buffer.WriteString(k)
+		buffer.WriteString(">")
 	}
-	return keyResponse.SandboxSignkey, nil
+	buffer.WriteString("</xml>")
+	reqXml = buffer.String()
+	return
 }

+ 0 - 109
wechat_pay.go

@@ -1,109 +0,0 @@
-package gopay
-
-import (
-	"encoding/xml"
-	"github.com/parnurzeal/gorequest"
-)
-
-type weChatClient struct {
-	AppId     string
-	MchId     string
-	secretKey string
-	isProd    bool
-}
-
-//初始化微信客户端
-//    appId:应用ID
-//    mchID:商户ID
-//    secretKey:Key值
-//    isProd:是否是正式环境
-func NewWeChatClient(appId, mchId, secretKey string, isProd bool) *weChatClient {
-	client := new(weChatClient)
-	client.AppId = appId
-	client.MchId = mchId
-	client.secretKey = secretKey
-	client.isProd = isProd
-	return client
-}
-
-//统一下单
-func (this *weChatClient) UnifiedOrder(param *WeChatPayParams) (wxRsp *weChatPayResponse, err error) {
-	var reqs requestBody
-	var sign string
-	//生成下单请求参数
-	if !this.isProd {
-		//沙箱环境
-		param.TotalFee = 101
-		param.SignType = WX_SignType_MD5
-		reqs = param.getRequestBody(this.AppId, this.MchId, param)
-		key, err := param.getSanBoxSignKey(this.MchId, param.NonceStr, this.secretKey, param.SignType)
-		if err != nil {
-			return nil, err
-		}
-		sign = getSign(key, param.SignType, reqs)
-	} else {
-		reqs = param.getRequestBody(this.AppId, this.MchId, param)
-		//计算Sign
-		sign = getSign(this.secretKey, param.SignType, reqs)
-	}
-
-	reqs.Set("sign", sign)
-
-	reqXML := generateXml(reqs)
-	//fmt.Println("req:::", reqXML)
-	agent := gorequest.New()
-	if this.isProd {
-		agent.Post(wxURL_unifiedOrder)
-	} else {
-		agent.Post(wxURL_sanbox_unifiedOrder)
-	}
-	agent.Type("xml")
-	agent.SendString(reqXML)
-	response, bytes, errs := agent.EndBytes()
-	defer response.Body.Close()
-	if len(errs) > 0 {
-		return nil, errs[0]
-	}
-	//fmt.Println("bytes:", string(bytes))
-	wxRsp = new(weChatPayResponse)
-	err = xml.Unmarshal(bytes, wxRsp)
-	if err != nil {
-		return nil, err
-	}
-	return wxRsp, nil
-}
-
-//查询订单
-func (this *weChatClient) QueryOrder() {
-
-}
-
-//关闭订单
-func (this *weChatClient) CloseOrder() {
-
-}
-
-//申请退款
-func (this *weChatClient) Refund() {
-
-}
-
-//查询退款
-func (this *weChatClient) QueryRefund() {
-
-}
-
-//下载对账单
-func (this *weChatClient) DownloadBill() {
-
-}
-
-//下载资金账单
-func (this *weChatClient) DownloadFundFlow() {
-
-}
-
-//拉取订单评价数据
-func (this *weChatClient) BatchQueryComment() {
-
-}

+ 0 - 50
wechat_pay_test.go

@@ -1,50 +0,0 @@
-package gopay
-
-import (
-	"fmt"
-	"testing"
-)
-
-func TestWXPay(t *testing.T) {
-
-	//初始化微信客户端
-	//    appId:应用ID
-	//    mchID:商户ID
-	//    secretKey:Key值
-	//    isProd:是否是正式环境
-	client := NewWeChatClient(appID, mchID, secretKey, true)
-
-	//初始化参数结构体
-	params := new(WeChatPayParams)
-	params.NonceStr = "dyUNIkNS29hvDUC1CmoF0alSdfCQGg9I"
-	params.Body = "测试充值"
-	params.OutTradeNo = "GYsadfjk4dhg3fk13ffgnlsdkf"
-	params.TotalFee = 10 //单位为分
-	params.SpbillCreateIp = "127.0.0.1"
-	params.NotifyUrl = "http://www.igoogle.ink"
-	params.TradeType = WX_PayType_JsApi //目前只支持JSAPI有效
-	params.DeviceInfo = "WEB"
-	params.SignType = WX_SignType_HMAC_SHA256 //如不设置此参数,默认为 MD5
-	params.Openid = openID
-
-	//请求支付下单,成功后得到结果
-	wxRsp, err := client.UnifiedOrder(params)
-	if err != nil {
-		fmt.Println("Error:", err)
-	} else {
-		fmt.Println("ReturnCode:", wxRsp.ReturnCode)
-		fmt.Println("ReturnMsg:", wxRsp.ReturnMsg)
-		fmt.Println("Appid:", wxRsp.Appid)
-		fmt.Println("MchId:", wxRsp.MchId)
-		fmt.Println("DeviceInfo:", wxRsp.DeviceInfo)
-		fmt.Println("NonceStr:", wxRsp.NonceStr)
-		fmt.Println("Sign:", wxRsp.Sign)
-		fmt.Println("ResultCode:", wxRsp.ResultCode)
-		fmt.Println("ErrCode:", wxRsp.ErrCode)
-		fmt.Println("ErrCodeDes:", wxRsp.ErrCodeDes)
-		fmt.Println("PrepayId:", wxRsp.PrepayId)
-		fmt.Println("TradeType:", wxRsp.TradeType)
-		fmt.Println("CodeUrl:", wxRsp.CodeUrl)
-		fmt.Println("MwebUrl:", wxRsp.MwebUrl)
-	}
-}

+ 5 - 0
wechat_request_body.go → wechat_req.go

@@ -30,7 +30,10 @@ package gopay
 //    Openid: 用户标识: trade_type=JSAPI,此参数必传,用户在商户appid下的唯一标识.
 //    Receipt: Y,传入Y时,支付成功消息和支付详情页将出现开票入口。需要在微信支付商户平台或微信公众平台开通电子发票功能,传此字段才可生效.
 //    SceneInfo: 该字段常用于线下活动时的场景信息上报,支持上报实际门店信息,商户也可以按需求自己上报相关信息。该字段为JSON对象数据,对象格式为{"store_info":{"id": "门店ID","name": "名称","area_code": "编码","address": "地址" }}.
+//    TransactionId:微信的订单号,建议优先使用,与OutTradeNo二选一
+//
 type WeChatPayParams struct {
+	//统一下单
 	NonceStr       string `xml:"nonce_str"`
 	Body           string `xml:"body"`
 	OutTradeNo     string `xml:"out_trade_no"`
@@ -51,6 +54,8 @@ type WeChatPayParams struct {
 	Openid         string `xml:"openid"`
 	Receipt        string `xml:"receipt"`
 	SceneInfo      string `xml:"scene_info"`
+	//查询订单
+	TransactionId string `json:"transaction_id"`
 }
 
 //StoreInfo: SceneInfo 的字段信息

+ 0 - 32
wechat_response.go

@@ -1,32 +0,0 @@
-//==================================
-//  * Name:Jerry
-//  * Tel:18017448610
-//  * DateTime:2019/1/13 14:03
-//==================================
-package gopay
-
-type weChatPayResponse struct {
-	ReturnCode string `xml:"return_code"`
-	ReturnMsg  string `xml:"return_msg"`
-	Appid      string `xml:"appid"`
-	MchId      string `xml:"mch_id"`
-	DeviceInfo string `xml:"device_info"`
-	NonceStr   string `xml:"nonce_str"`
-	Sign       string `xml:"sign"`
-	ResultCode string `xml:"result_code"`
-	ErrCode    string `xml:"err_code"`
-	ErrCodeDes string `xml:"err_code_des"`
-	TradeType  string `xml:"trade_type"`
-	PrepayId   string `xml:"prepay_id"`
-	CodeUrl    string `xml:"code_url"`
-	MwebUrl    string `xml:"mweb_url"`
-}
-
-type getSignKeyResponse struct {
-	ReturnCode     string `xml:"return_code"`
-	ReturnMsg      string `xml:"return_msg"`
-	Retmsg         string `xml:"retmsg"`
-	Retcode        string `xml:"retcode"`
-	MchId          string `xml:"mch_id"`
-	SandboxSignkey string `xml:"sandbox_signkey"`
-}

+ 65 - 0
wechat_rsp.go

@@ -0,0 +1,65 @@
+//==================================
+//  * Name:Jerry
+//  * Tel:18017448610
+//  * DateTime:2019/1/13 14:03
+//==================================
+package gopay
+
+type weChatUnifiedOrderResponse struct {
+	ReturnCode string `xml:"return_code"`
+	ReturnMsg  string `xml:"return_msg"`
+	Appid      string `xml:"appid"`
+	MchId      string `xml:"mch_id"`
+	DeviceInfo string `xml:"device_info"`
+	NonceStr   string `xml:"nonce_str"`
+	Sign       string `xml:"sign"`
+	ResultCode string `xml:"result_code"`
+	ErrCode    string `xml:"err_code"`
+	ErrCodeDes string `xml:"err_code_des"`
+	TradeType  string `xml:"trade_type"`
+	PrepayId   string `xml:"prepay_id"`
+	CodeUrl    string `xml:"code_url"`
+	MwebUrl    string `xml:"mweb_url"`
+}
+
+type weChatQueryOrderResponse struct {
+	ReturnCode         string `xml:"return_code"`
+	ReturnMsg          string `xml:"return_msg"`
+	Appid              string `xml:"appid"`
+	MchId              string `xml:"mch_id"`
+	NonceStr           string `xml:"nonce_str"`
+	Sign               string `xml:"sign"`
+	ResultCode         string `xml:"result_code"`
+	ErrCode            string `xml:"err_code"`
+	ErrCodeDes         string `xml:"err_code_des"`
+	DeviceInfo         string `xml:"device_info"`
+	Openid             string `xml:"openid"`
+	IsSubscribe        string `xml:"is_subscribe"`
+	TradeType          string `xml:"trade_type"`
+	TradeState         string `xml:"trade_state"`
+	BankType           string `xml:"bank_type"`
+	TotalFee           int    `xml:"total_fee"`
+	SettlementTotalFee int    `xml:"settlement_total_fee"`
+	FeeType            string `xml:"fee_type"`
+	CashFee            int    `xml:"cash_fee"`
+	CashFeeType        string `xml:"cash_fee_type"`
+	CouponFee          int    `xml:"coupon_fee"`
+	CouponCount        int    `xml:"coupon_count"`
+	CouponType0        string `xml:"coupon_type_$0"`
+	CouponId0          string `xml:"coupon_id_$0"`
+	CouponFee0         int    `xml:"coupon_fee_$0"`
+	TransactionId      string `xml:"transaction_id"`
+	OutTradeNo         string `xml:"out_trade_no"`
+	Attach             string `xml:"attach"`
+	TimeEnd            string `xml:"time_end"`
+	TradeStateDesc     string `xml:"trade_state_desc"`
+}
+
+type getSignKeyResponse struct {
+	ReturnCode     string `xml:"return_code"`
+	ReturnMsg      string `xml:"return_msg"`
+	Retmsg         string `xml:"retmsg"`
+	Retcode        string `xml:"retcode"`
+	MchId          string `xml:"mch_id"`
+	SandboxSignkey string `xml:"sandbox_signkey"`
+}