forked from silenceper/wechat
-
Notifications
You must be signed in to change notification settings - Fork 0
/
pay.go
212 lines (199 loc) · 5.58 KB
/
pay.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
package pay
import (
"bytes"
"encoding/xml"
"errors"
"sort"
"strconv"
"github.com/chrisho/wechat/context"
"github.com/chrisho/wechat/util"
)
var payGateway = "https://api.mch.weixin.qq.com/pay/unifiedorder"
// Pay struct extends context
type Pay struct {
*context.Context
}
// Params was NEEDED when request unifiedorder
// 传入的参数,用于生成 prepay_id 的必需参数
type Params struct {
TotalFee string
CreateIP string
Body string
OutTradeNo string
OpenID string
TradeType string
}
// Config 是传出用于 jsdk 用的参数
type Config struct {
Timestamp int64
NonceStr string
PrePayID string
SignType string
Sign string
}
// PreOrder 是 unifie order 接口的返回
type PreOrder struct {
ReturnCode string `xml:"return_code"`
ReturnMsg string `xml:"return_msg"`
AppID string `xml:"appid,omitempty"`
MchID string `xml:"mch_id,omitempty"`
NonceStr string `xml:"nonce_str,omitempty"`
Sign string `xml:"sign,omitempty"`
ResultCode string `xml:"result_code,omitempty"`
TradeType string `xml:"trade_type,omitempty"`
PrePayID string `xml:"prepay_id,omitempty"`
CodeURL string `xml:"code_url,omitempty"`
ErrCode string `xml:"err_code,omitempty"`
ErrCodeDes string `xml:"err_code_des,omitempty"`
}
//payRequest 接口请求参数
type payRequest struct {
AppID string `xml:"appid"`
MchID string `xml:"mch_id"`
DeviceInfo string `xml:"device_info,omitempty"`
NonceStr string `xml:"nonce_str"`
Sign string `xml:"sign"`
SignType string `xml:"sign_type,omitempty"`
Body string `xml:"body"`
Detail string `xml:"detail,omitempty"`
Attach string `xml:"attach,omitempty"` //附加数据
OutTradeNo string `xml:"out_trade_no"` //商户订单号
FeeType string `xml:"fee_type,omitempty"` //标价币种
TotalFee string `xml:"total_fee"` //标价金额
SpbillCreateIP string `xml:"spbill_create_ip"` //终端IP
TimeStart string `xml:"time_start,omitempty"` //交易起始时间
TimeExpire string `xml:"time_expire,omitempty"` //交易结束时间
GoodsTag string `xml:"goods_tag,omitempty"` //订单优惠标记
NotifyURL string `xml:"notify_url"` //通知地址
TradeType string `xml:"trade_type"` //交易类型
ProductID string `xml:"product_id,omitempty"` //商品ID
LimitPay string `xml:"limit_pay,omitempty"` //
OpenID string `xml:"openid,omitempty"` //用户标识
SceneInfo string `xml:"scene_info,omitempty"` //场景信息
}
// NewPay return an instance of Pay package
func NewPay(ctx *context.Context) *Pay {
pay := Pay{Context: ctx}
return &pay
}
// PrePayOrder return data for invoke wechat payment
func (pcf *Pay) PrePayOrder(p *Params) (payOrder PreOrder, err error) {
nonceStr := util.RandomStr(32)
param := make(map[string]interface{})
param["appid"] = pcf.AppID
param["body"] = p.Body
param["mch_id"] = pcf.PayMchID
param["nonce_str"] =nonceStr
param["notify_url"] =pcf.PayNotifyURL
param["out_trade_no"] =p.OutTradeNo
param["spbill_create_ip"] =p.CreateIP
param["total_fee"] =p.TotalFee
param["trade_type"] =p.TradeType
param["openid"] = p.OpenID
bizKey := "&key="+pcf.PayKey
str := orderParam(param,bizKey)
sign := util.MD5Sum(str)
request := payRequest{
AppID: pcf.AppID,
MchID: pcf.PayMchID,
NonceStr: nonceStr,
Sign: sign,
Body: p.Body,
OutTradeNo: p.OutTradeNo,
TotalFee: p.TotalFee,
SpbillCreateIP: p.CreateIP,
NotifyURL: pcf.PayNotifyURL,
TradeType: p.TradeType,
OpenID: p.OpenID,
}
rawRet, err := util.PostXML(payGateway, request)
if err != nil {
return
}
err = xml.Unmarshal(rawRet, &payOrder)
if err != nil {
return
}
if payOrder.ReturnCode == "SUCCESS" {
//pay success
if payOrder.ResultCode == "SUCCESS" {
err = nil
return
}
err = errors.New(payOrder.ErrCode + payOrder.ErrCodeDes)
return
}
err = errors.New("[msg : xmlUnmarshalError] [rawReturn : " + string(rawRet) + "] [params : " + str + "] [sign : " + sign + "]")
return
}
// PrePayID will request wechat merchant api and request for a pre payment order id
func (pcf *Pay) PrePayID(p *Params) (prePayID string, err error) {
order, err := pcf.PrePayOrder(p)
if err != nil {
return
}
if order.PrePayID == "" {
err = errors.New("empty prepayid")
}
prePayID = order.PrePayID
return
}
// order params
func orderParam(source interface{}, bizKey string) (returnStr string) {
switch v := source.(type) {
case map[string]string:
keys := make([]string, 0, len(v))
for k := range v {
if k == "sign" {
continue
}
keys = append(keys, k)
}
sort.Strings(keys)
var buf bytes.Buffer
for _, k := range keys {
if v[k] == "" {
continue
}
if buf.Len() > 0 {
buf.WriteByte('&')
}
buf.WriteString(k)
buf.WriteByte('=')
buf.WriteString(v[k])
}
buf.WriteString(bizKey)
returnStr = buf.String()
case map[string]interface{}:
keys := make([]string, 0, len(v))
for k := range v {
if k == "sign" {
continue
}
keys = append(keys, k)
}
sort.Strings(keys)
var buf bytes.Buffer
for _, k := range keys {
if v[k] == "" {
continue
}
if buf.Len() > 0 {
buf.WriteByte('&')
}
buf.WriteString(k)
buf.WriteByte('=')
switch vv := v[k].(type) {
case string:
buf.WriteString(vv)
case int:
buf.WriteString(strconv.FormatInt(int64(vv), 10))
default:
panic("params type not supported")
}
}
buf.WriteString(bizKey)
returnStr = buf.String()
}
return
}