-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathwxpay.go
216 lines (208 loc) · 6.27 KB
/
wxpay.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
package mcommon
import (
"crypto/tls"
"encoding/xml"
"fmt"
"time"
"github.com/gin-gonic/gin"
"github.com/parnurzeal/gorequest"
)
// StWeChatCbBody 回调信息
type StWeChatCbBody struct {
XMLName xml.Name `xml:"xml"`
Text string `xml:",chardata"`
Appid string `xml:"appid"`
Attach string `xml:"attach"`
BankType string `xml:"bank_type"`
FeeType string `xml:"fee_type"`
IsSubscribe string `xml:"is_subscribe"`
MchID string `xml:"mch_id"`
NonceStr string `xml:"nonce_str"`
Openid string `xml:"openid"`
OutTradeNo string `xml:"out_trade_no"`
ResultCode string `xml:"result_code"`
ReturnCode string `xml:"return_code"`
Sign string `xml:"sign"`
TimeEnd string `xml:"time_end"`
TotalFee string `xml:"total_fee"`
CouponFee string `xml:"coupon_fee"`
CouponCount string `xml:"coupon_count"`
CouponType string `xml:"coupon_type"`
CouponID string `xml:"coupon_id"`
TradeType string `xml:"trade_type"`
TransactionID string `xml:"transaction_id"`
}
// StRefundRespXML 回复内容
type StRefundRespXML struct {
XMLName xml.Name `xml:"xml"`
Text string `xml:",chardata"`
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"`
TransactionID string `xml:"transaction_id"`
OutTradeNo string `xml:"out_trade_no"`
OutRefundNo string `xml:"out_refund_no"`
RefundID string `xml:"refund_id"`
RefundChannel string `xml:"refund_channel"`
RefundFee int64 `xml:"refund_fee"`
CouponRefundFee int64 `xml:"coupon_refund_fee"`
TotalFee int64 `xml:"total_fee"`
CashFee int64 `xml:"cash_fee"`
CouponRefundCount int64 `xml:"coupon_refund_count"`
CashRefundFee int64 `xml:"cash_refund_fee"`
}
// WechatGetPrepay 获取预支付信息
func WechatGetPrepay(appID, mchID, mchKey, payBody, outTradeNo, clientIP, cbURL, tradeType, openID string, timeExpire, totalFee int64) (gin.H, error) {
t := time.Unix(timeExpire, 0)
cstSh := time.FixedZone("CST", 8*3600)
tStr := t.In(cstSh).Format("20060102150405")
retryCount := 0
nonce := GetUUIDStr()
sendBody := gin.H{
"appid": appID,
"mch_id": mchID,
"nonce_str": nonce,
"body": payBody,
"out_trade_no": outTradeNo,
"total_fee": totalFee,
"spbill_create_ip": clientIP,
"time_expire": tStr,
"notify_url": cbURL,
"trade_type": tradeType,
"openid": openID,
}
sendBody["sign"] = WechatGetSign(mchKey, sendBody)
sendBodyBs, err := xml.Marshal(sendBody)
if err != nil {
return nil, err
}
GotoHttpRetry:
_, body, errs := gorequest.New().
Post("https://api.mch.weixin.qq.com/pay/unifiedorder").
Set("Content-Type", "application/xml").
Send(string(sendBodyBs)).
EndBytes()
if errs != nil {
retryCount++
if retryCount < 3 {
goto GotoHttpRetry
}
return nil, errs[0]
}
respMap, err := XMLWalk(body)
if err != nil {
return nil, err
}
if !WechatCheckSign(mchKey, respMap) {
return nil, fmt.Errorf("sign error: %s", body)
}
type stRespXMLXML struct {
XMLName xml.Name `xml:"xml"`
Text string `xml:",chardata"`
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"`
PrepayID string `xml:"prepay_id"`
TradeType string `xml:"trade_type"`
}
var respXML stRespXMLXML
err = xml.Unmarshal(body, &respXML)
if err != nil {
return nil, err
}
if respXML.ResultCode != "SUCCESS" {
return nil, fmt.Errorf("resp result code error")
}
jsMap := gin.H{
"appId": appID,
"timeStamp": time.Now().Unix(),
"nonceStr": GetUUIDStr(),
"package": fmt.Sprintf("%s=%s", "prepay_id", respXML.PrepayID),
"signType": "MD5",
}
jsMap["paySign"] = WechatGetSign(mchKey, jsMap)
return jsMap, nil
}
// WechatCheckCb 验证回调
func WechatCheckCb(mchKey string, body []byte) (*StWeChatCbBody, error) {
bodyMap, err := XMLWalk(body)
if err != nil {
// 返回数据
return nil, err
}
if !WechatCheckSign(mchKey, bodyMap) {
return nil, fmt.Errorf("sign error")
}
var bodyXML StWeChatCbBody
err = xml.Unmarshal(body, &bodyXML)
if err != nil {
return nil, err
}
if bodyXML.ResultCode != "SUCCESS" {
return nil, fmt.Errorf("result code error")
}
return &bodyXML, nil
}
// WechatRefund 申请退款
func WechatRefund(cer tls.Certificate, appID, mchID, mchKey, transactionID, outRefundNo, cbURL string, totalFee, refundFee int64) (*StRefundRespXML, error) {
retryCount := 0
nonce := GetUUIDStr()
sendBody := gin.H{
"appid": appID,
"mch_id": mchID,
"nonce_str": nonce,
"transaction_id": transactionID,
"out_refund_no": outRefundNo,
"total_fee": totalFee,
"refund_fee": refundFee,
"notify_url": cbURL,
}
sendBody["sign"] = WechatGetSign(mchKey, sendBody)
sendBodyBs, err := xml.Marshal(sendBody)
if err != nil {
return nil, err
}
GotoHttpRetry:
_, body, errs := gorequest.New().
Post("https://api.mch.weixin.qq.com/secapi/pay/refund").
TLSClientConfig(&tls.Config{Certificates: []tls.Certificate{cer}}).
Set("Content-Type", "application/xml").
Send(string(sendBodyBs)).
EndBytes()
if errs != nil {
retryCount++
if retryCount < 3 {
goto GotoHttpRetry
}
return nil, errs[0]
}
respMap, err := XMLWalk(body)
if err != nil {
return nil, err
}
if !WechatCheckSign(mchKey, respMap) {
return nil, fmt.Errorf("sign error: %s", body)
}
var respXML StRefundRespXML
err = xml.Unmarshal(body, &respXML)
if err != nil {
return nil, err
}
if respXML.ReturnCode != "SUCCESS" {
Log.Errorf("refund err: %s", body)
return nil, fmt.Errorf("resp return code error %s", respXML.ReturnCode)
}
if respXML.ResultCode != "SUCCESS" {
Log.Errorf("refund err: %s", body)
return nil, fmt.Errorf("resp result code error %s", respXML.ResultCode)
}
return &respXML, nil
}