-
Notifications
You must be signed in to change notification settings - Fork 3
/
pc.go
378 lines (326 loc) · 10.6 KB
/
pc.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
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
package main
import (
"bytes"
"crypto"
"crypto/rand"
"crypto/rsa"
"crypto/sha256"
"crypto/x509"
"encoding/base64"
"encoding/json"
"encoding/pem"
"errors"
"fmt"
"io/ioutil"
"log"
"net/http"
"net/url"
"sort"
"strconv"
"strings"
"time"
)
/*** 请填写以下配置信息 ***/
var appid string = "xxxxx" //https://open.alipay.com 账户中心->密钥管理->开放平台密钥,填写添加了电脑网站支付的应用的APPID
var returnUrl string = "http://www.example.com/callback" //付款成功后的同步回调地址
var notifyUrl string = "http://www.example.com/notify" //付款成功后的异步回调地址
var payAmount float64 = 0.01 //付款金额,单位:元
var orderName string = "支付测试" //订单标题
var signType string = "RSA2" //签名算法类型,支持RSA2和RSA,推荐使用RSA2
//商户私钥,填写对应签名算法类型的私钥,如何生成密钥参考:https://docs.open.alipay.com/291/105971和https://docs.open.alipay.com/200/105310
var rsaPrivateKey string = "xxxxx"
//支付宝公钥,登录支付宝开放平台,账户中心->密钥管理->开放平台密钥,找到对应的应用,在接口内容加密方式处查看支付宝公钥
var alipayPublicKey string = "xxxxx"
/*** 配置结束 ***/
type AlipayService struct {
appId string
returnUrl string
notifyUrl string
charset string
rsaPrivateKey *rsa.PrivateKey
alipayPublicKey *rsa.PublicKey
totalFee float64
outTradeNo string
orderName string
}
func (this *AlipayService) SetAppId(appId string) {
this.appId = appId
}
func (this *AlipayService) SetReturnUrl(returnUrl string) {
this.returnUrl = returnUrl
}
func (this *AlipayService) SetNotifyUrl(notifyUrl string) {
this.notifyUrl = notifyUrl
}
func (this *AlipayService) SetCharset(charset string) {
this.charset = charset
}
func (this *AlipayService) SetRsaPrivateKey(keyString string) {
privateKey, err := ParsePrivateKey(FormatPrivateKey(keyString))
if err != nil {
panic(err)
}
this.rsaPrivateKey = privateKey
}
func (this *AlipayService) SetAlipayPublicKey(keyString string) {
alipayPublicKey, err := ParsePublicKey(FormatPublicKey(keyString))
if err != nil {
panic(err)
}
this.alipayPublicKey = alipayPublicKey
}
func (this *AlipayService) SetTotalFee(totalFee float64) {
this.totalFee = totalFee
}
func (this *AlipayService) SetOutTradeNo(outTradeNo string) {
this.outTradeNo = outTradeNo
}
func (this *AlipayService) SetOrderName(orderName string) {
this.orderName = orderName
}
// GenSign 产生签名
func (this *AlipayService) GenSign(m map[string]string) string {
var data []string
var encryptedBytes []byte
for k, v := range m {
if v != "" && k != "sign" {
data = append(data, fmt.Sprintf(`%s=%s`, k, v))
}
}
sort.Strings(data)
signData := strings.Join(data, "&")
s := sha256.New()
_, err := s.Write([]byte(signData))
if err != nil {
panic(err)
}
hashByte := s.Sum(nil)
hashs := crypto.SHA256
rsaPrivateKey := this.rsaPrivateKey
if encryptedBytes, err = rsa.SignPKCS1v15(rand.Reader, rsaPrivateKey, hashs, hashByte); err != nil {
panic(err)
}
return base64.StdEncoding.EncodeToString(encryptedBytes)
}
func (this *AlipayService) buildRequestForm(m map[string]string) string {
var buf []string
buf = append(buf, fmt.Sprintf("正在跳转至支付页面...<form id='alipaysubmit' name='alipaysubmit' action='https://openapi.alipay.com/gateway.do?charset=%s' method='POST'>", this.charset))
for k, v := range m {
buf = append(buf, fmt.Sprintf("<input type='hidden' name='%s' value='%s'/>", k, v))
}
buf = append(buf, "<input type='submit' value='ok' style='display:none;''></form>")
buf = append(buf, "<script>document.forms['alipaysubmit'].submit();</script>")
return strings.Join(buf, "")
}
func (this AlipayService) DoPay() (string, error) {
//请求参数
var bizContent = make(map[string]string)
bizContent["out_trade_no"] = this.outTradeNo
bizContent["product_code"] = "FAST_INSTANT_TRADE_PAY"
bizContent["total_amount"] = strconv.FormatFloat(float64(this.totalFee), 'f', 2, 64) //2表示保留2位小数
bizContent["subject"] = this.orderName
bizContentJson, err := json.Marshal(bizContent)
if err != nil {
return "", errors.New("json.Marshal: " + err.Error())
}
//公共参数
var m = make(map[string]string)
m["app_id"] = this.appId
m["method"] = "alipay.trade.page.pay" //接口名称
m["format"] = "JSON"
m["return_url"] = this.returnUrl
m["charset"] = this.charset
m["sign_type"] = "RSA2"
m["timestamp"] = time.Now().Format("2006-01-02 15:04:05")
m["version"] = "1.0"
m["notify_url"] = this.notifyUrl
m["biz_content"] = string(bizContentJson)
//获取签名
sign := this.GenSign(m)
m["sign"] = sign
return this.buildRequestForm(m), nil
}
func (this *AlipayService) VerifySign(data url.Values) (ok bool, err error) {
return verifySign(data, this.alipayPublicKey)
}
func ParsePrivateKey(data []byte) (key *rsa.PrivateKey, err error) {
var block *pem.Block
block, _ = pem.Decode(data)
if block == nil {
return nil, errors.New("private key failed to load")
}
var priInterface interface{}
priInterface, err = x509.ParsePKCS1PrivateKey(block.Bytes)
if err != nil {
return nil, err
}
key, ok := priInterface.(*rsa.PrivateKey)
if !ok {
return nil, errors.New("private key failed to load")
}
return key, err
}
func ParsePublicKey(data []byte) (key *rsa.PublicKey, err error) {
var block *pem.Block
block, _ = pem.Decode(data)
if block == nil {
return nil, errors.New("alipay public key failed to load")
}
var pubInterface interface{}
pubInterface, err = x509.ParsePKIXPublicKey(block.Bytes)
if err != nil {
return nil, err
}
key, ok := pubInterface.(*rsa.PublicKey)
if !ok {
return nil, errors.New("alipay public key failed to load")
}
return key, err
}
func FormatPublicKey(raw string) []byte {
return formatKey(raw, "-----BEGIN PUBLIC KEY-----", "-----END PUBLIC KEY-----", 64)
}
func FormatPrivateKey(raw string) []byte {
return formatKey(raw, "-----BEGIN RSA PRIVATE KEY-----", "-----END RSA PRIVATE KEY-----", 64)
}
func formatKey(raw, prefix, suffix string, lineCount int) []byte {
if raw == "" {
return nil
}
raw = strings.Replace(raw, prefix, "", 1)
raw = strings.Replace(raw, suffix, "", 1)
raw = strings.Replace(raw, " ", "", -1)
raw = strings.Replace(raw, "\n", "", -1)
raw = strings.Replace(raw, "\r", "", -1)
raw = strings.Replace(raw, "\t", "", -1)
var sl = len(raw)
var c = sl / lineCount
if sl%lineCount > 0 {
c = c + 1
}
var buf bytes.Buffer
buf.WriteString(prefix + "\n")
for i := 0; i < c; i++ {
var b = i * lineCount
var e = b + lineCount
if e > sl {
buf.WriteString(raw[b:])
} else {
buf.WriteString(raw[b:e])
}
buf.WriteString("\n")
}
buf.WriteString(suffix)
return buf.Bytes()
}
func verifySign(data url.Values, key *rsa.PublicKey) (ok bool, err error) {
sign := data.Get("sign")
var keys = make([]string, 0, 0)
for key := range data {
if key == "sign" || key == "sign_type" {
continue
}
keys = append(keys, key)
}
sort.Strings(keys)
var pList = make([]string, 0, 0)
for _, key := range keys {
pList = append(pList, key+"="+data.Get(key))
}
var s = strings.Join(pList, "&")
return verifyData([]byte(s), sign, key)
}
func verifyData(data []byte, sign string, key *rsa.PublicKey) (ok bool, err error) {
signBytes, err := base64.StdEncoding.DecodeString(sign)
if err != nil {
return false, err
}
var h = crypto.SHA256.New()
h.Write(data)
var hashed = h.Sum(nil)
if err = rsa.VerifyPKCS1v15(key, crypto.SHA256, hashed, signBytes); err != nil {
return false, err
}
return true, nil
}
//生成订单号
func Uniqid() string {
now := time.Now()
return fmt.Sprintf("%s%08x%05x", "", now.Unix(), now.UnixNano()%0x100000)
}
//发起支付
func pcPay(w http.ResponseWriter, r *http.Request) {
params := r.URL.Query()
if params.Get("total_fee") != "" {
payAmount, _ = strconv.ParseFloat(params.Get("total_fee"), 64)
}
outTradeNo := Uniqid()
if params.Get("out_trade_no") != "" {
outTradeNo = params.Get("out_trade_no")
}
if params.Get("order_name") != "" {
orderName = params.Get("order_name")
}
aliPay := AlipayService{}
aliPay.SetAppId(appid)
aliPay.SetReturnUrl(returnUrl)
aliPay.SetNotifyUrl(notifyUrl)
aliPay.SetCharset("utf-8")
aliPay.SetRsaPrivateKey(rsaPrivateKey)
aliPay.SetTotalFee(payAmount)
aliPay.SetOutTradeNo(outTradeNo)
aliPay.SetOrderName(orderName)
sHtml, err := aliPay.DoPay()
if err != nil {
panic(err)
}
w.Header().Set("Content-Type", "text/html; charset=utf-8")
fmt.Fprintf(w, sHtml)
}
//异步回调通知处理
func notify(w http.ResponseWriter, r *http.Request) {
defer r.Body.Close()
//接收post参数
r.ParseForm()
formdata := r.PostForm
aliPay := AlipayService{}
aliPay.SetCharset("utf-8")
aliPay.SetAlipayPublicKey(alipayPublicKey)
_, err := aliPay.VerifySign(formdata)
if err != nil {
fmt.Fprintf(w, "error:"+err.Error())
return
}
//处理你的逻辑,例如获取订单号formdata["out_trade_no"][0],订单金额formdata["total_amount"][0]等
//例如这里将回调数据写入到nofity.txt文本中
ioutil.WriteFile("nofity.txt", []byte(formdata.Encode()), 0644)
//程序执行完后必须打印输出“success”(不包含引号)。如果商户反馈给支付宝的字符不是success这7个字符,支付宝服务器会不断重发通知,直到超过24小时22分钟。一般情况下,25小时以内完成8次通知(通知的间隔频率一般是:4m,10m,10m,1h,2h,6h,15h);
fmt.Fprintf(w, "success")
}
//同步回调
func callback(w http.ResponseWriter, r *http.Request) {
defer r.Body.Close()
//接收get参数
params := r.URL.Query()
aliPay := AlipayService{}
aliPay.SetCharset("utf-8")
aliPay.SetAlipayPublicKey(alipayPublicKey)
_, err := aliPay.VerifySign(params)
if err != nil {
fmt.Fprintf(w, "error:"+err.Error())
return
}
fmt.Fprintf(w, "支付成功!订单号:%s", params.Get("out_trade_no"))
}
func main() {
//开启web服务器
port := "8080"
http.HandleFunc("/", pcPay)
http.HandleFunc("/notify", notify)
http.HandleFunc("/callback", callback)
log.Println("服务器启动成功,监听端口:", port)
err := http.ListenAndServe(":"+port, nil)
if err != nil {
log.Fatal("ListenAndServe:", err)
}
}