-
Notifications
You must be signed in to change notification settings - Fork 27
/
Copy pathhttp.go
187 lines (153 loc) · 5.16 KB
/
http.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
package wxpay
import (
"bytes"
"crypto/tls"
"errors"
"fmt"
"io/ioutil"
"net/http"
)
// AppTrans is abstact of Transaction handler. With AppTrans, we can get prepay id
type AppTrans struct {
Config *WxConfig
}
// Initialized the AppTrans with specific config
func NewAppTrans(cfg *WxConfig) (*AppTrans, error) {
if cfg.AppId == "" ||
cfg.MchId == "" ||
cfg.AppKey == "" ||
cfg.NotifyUrl == "" ||
cfg.QueryOrderUrl == "" ||
cfg.PlaceOrderUrl == "" ||
cfg.TradeType == "" {
return &AppTrans{Config: cfg}, errors.New("config field canot empty string")
}
return &AppTrans{Config: cfg}, nil
}
// Submit the order to weixin pay and return the prepay id if success,
// Prepay id is used for app to start a payment
// If fail, error is not nil, check error for more information
func (this *AppTrans) Submit(orderId string, amount float64, desc string, clientIp string) (string, error) {
odrInXml := this.signedOrderRequestXmlString(orderId, fmt.Sprintf("%.0f", amount), desc, clientIp)
resp, err := doHttpPost(this.Config.PlaceOrderUrl, []byte(odrInXml))
if err != nil {
return "", err
}
placeOrderResult, err := ParsePlaceOrderResult(resp)
if err != nil {
return "", err
}
//Verify the sign of response
resultInMap := placeOrderResult.ToMap()
wantSign := Sign(resultInMap, this.Config.AppKey)
gotSign := resultInMap["sign"]
if wantSign != gotSign {
return "", fmt.Errorf("sign not match, want:%s, got:%s", wantSign, gotSign)
}
if placeOrderResult.ReturnCode != "SUCCESS" {
return "", fmt.Errorf("return code:%s, return desc:%s", placeOrderResult.ReturnCode, placeOrderResult.ReturnMsg)
}
if placeOrderResult.ResultCode != "SUCCESS" {
return "", fmt.Errorf("resutl code:%s, result desc:%s", placeOrderResult.ErrCode, placeOrderResult.ErrCodeDesc)
}
return placeOrderResult.PrepayId, nil
}
func (this *AppTrans) newQueryXml(transId string) string {
param := make(map[string]string)
param["appid"] = this.Config.AppId
param["mch_id"] = this.Config.MchId
param["transaction_id"] = transId
param["nonce_str"] = NewNonceString()
sign := Sign(param, this.Config.AppKey)
param["sign"] = sign
return ToXmlString(param)
}
// Query the order from weixin pay server by transaction id of weixin pay
func (this *AppTrans) Query(transId string) (QueryOrderResult, error) {
queryOrderResult := QueryOrderResult{}
queryXml := this.newQueryXml(transId)
// fmt.Println(queryXml)
resp, err := doHttpPost(this.Config.QueryOrderUrl, []byte(queryXml))
if err != nil {
return queryOrderResult, nil
}
queryOrderResult, err = ParseQueryOrderResult(resp)
if err != nil {
return queryOrderResult, err
}
//verity sign of response
resultInMap := queryOrderResult.ToMap()
wantSign := Sign(resultInMap, this.Config.AppKey)
gotSign := resultInMap["sign"]
if wantSign != gotSign {
return queryOrderResult, fmt.Errorf("sign not match, want:%s, got:%s", wantSign, gotSign)
}
return queryOrderResult, nil
}
// NewPaymentRequest build the payment request structure for app to start a payment.
// Return stuct of PaymentRequest, please refer to http://pay.weixin.qq.com/wiki/doc/api/app.php?chapter=9_12&index=2
func (this *AppTrans) NewPaymentRequest(prepayId string) PaymentRequest {
noncestr := NewNonceString()
timestamp := NewTimestampString()
param := make(map[string]string)
param["appid"] = this.Config.AppId
param["partnerid"] = this.Config.MchId
param["prepayid"] = prepayId
param["package"] = "Sign=WXPay"
param["noncestr"] = noncestr
param["timestamp"] = timestamp
sign := Sign(param, this.Config.AppKey)
payRequest := PaymentRequest{
AppId: this.Config.AppId,
PartnerId: this.Config.MchId,
PrepayId: prepayId,
Package: "Sign=WXPay",
NonceStr: noncestr,
Timestamp: timestamp,
Sign: sign,
}
return payRequest
}
func (this *AppTrans) newOrderRequest(orderId, amount, desc, clientIp string) map[string]string {
param := make(map[string]string)
param["appid"] = this.Config.AppId
param["attach"] = "透传字段" //optional
param["body"] = desc
param["mch_id"] = this.Config.MchId
param["nonce_str"] = NewNonceString()
param["notify_url"] = this.Config.NotifyUrl
param["out_trade_no"] = orderId
param["spbill_create_ip"] = clientIp
param["total_fee"] = amount
param["trade_type"] = this.Config.TradeType
return param
}
func (this *AppTrans) signedOrderRequestXmlString(orderId, amount, desc, clientIp string) string {
order := this.newOrderRequest(orderId, amount, desc, clientIp)
sign := Sign(order, this.Config.AppKey)
// fmt.Println(sign)
order["sign"] = sign
return ToXmlString(order)
}
// doRequest post the order in xml format with a sign
func doHttpPost(targetUrl string, body []byte) ([]byte, error) {
req, err := http.NewRequest("POST", targetUrl, bytes.NewBuffer([]byte(body)))
if err != nil {
return []byte(""), err
}
req.Header.Add("Content-type", "application/x-www-form-urlencoded;charset=UTF-8")
tr := &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: false},
}
client := &http.Client{Transport: tr}
resp, err := client.Do(req)
if err != nil {
return []byte(""), err
}
defer resp.Body.Close()
respData, err := ioutil.ReadAll(resp.Body)
if err != nil {
return []byte(""), err
}
return respData, nil
}