forked from go-pay/gopay
-
Notifications
You must be signed in to change notification settings - Fork 0
/
common_api.go
223 lines (213 loc) · 7.25 KB
/
common_api.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
package alipay
import (
"crypto/aes"
"crypto/cipher"
"crypto/rsa"
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"net/url"
"reflect"
"time"
"github.com/cedarwu/gopay"
xaes "github.com/cedarwu/gopay/pkg/aes"
"github.com/cedarwu/gopay/pkg/util"
"github.com/cedarwu/gopay/pkg/xhttp"
"github.com/cedarwu/gopay/pkg/xpem"
"github.com/cedarwu/gopay/pkg/xrsa"
)
// 格式化请求URL参数
func FormatURLParam(body gopay.BodyMap) (urlParam string) {
v := url.Values{}
for key, value := range body {
v.Add(key, value.(string))
}
return v.Encode()
}
// DecryptOpenDataToStruct 解密支付宝开放数据到 结构体
// encryptedData:包括敏感数据在内的完整用户信息的加密数据
// secretKey:AES密钥,支付宝管理平台配置
// beanPtr:需要解析到的结构体指针
// 文档:https://opendocs.alipay.com/mini/introduce/aes
// 文档:https://opendocs.alipay.com/open/common/104567
func DecryptOpenDataToStruct(encryptedData, secretKey string, beanPtr interface{}) (err error) {
if encryptedData == util.NULL || secretKey == util.NULL {
return errors.New("encryptedData or secretKey is null")
}
beanValue := reflect.ValueOf(beanPtr)
if beanValue.Kind() != reflect.Ptr {
return errors.New("传入参数类型必须是以指针形式")
}
if beanValue.Elem().Kind() != reflect.Struct {
return errors.New("传入interface{}必须是结构体")
}
var (
block cipher.Block
blockMode cipher.BlockMode
originData []byte
)
aesKey, _ := base64.StdEncoding.DecodeString(secretKey)
ivKey := []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
secretData, _ := base64.StdEncoding.DecodeString(encryptedData)
if block, err = aes.NewCipher(aesKey); err != nil {
return fmt.Errorf("aes.NewCipher:%w", err)
}
if len(secretData)%len(aesKey) != 0 {
return errors.New("encryptedData is error")
}
blockMode = cipher.NewCBCDecrypter(block, ivKey)
originData = make([]byte, len(secretData))
blockMode.CryptBlocks(originData, secretData)
if len(originData) > 0 {
originData = xaes.PKCS5UnPadding(originData)
}
if err = json.Unmarshal(originData, beanPtr); err != nil {
return fmt.Errorf("json.Unmarshal(%s):%w", string(originData), err)
}
return nil
}
// DecryptOpenDataToBodyMap 解密支付宝开放数据到 BodyMap
// encryptedData:包括敏感数据在内的完整用户信息的加密数据
// secretKey:AES密钥,支付宝管理平台配置
// 文档:https://opendocs.alipay.com/mini/introduce/aes
// 文档:https://opendocs.alipay.com/open/common/104567
func DecryptOpenDataToBodyMap(encryptedData, secretKey string) (bm gopay.BodyMap, err error) {
if encryptedData == util.NULL || secretKey == util.NULL {
return nil, errors.New("encryptedData or secretKey is null")
}
var (
aesKey, originData []byte
ivKey = []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
block cipher.Block
blockMode cipher.BlockMode
)
aesKey, _ = base64.StdEncoding.DecodeString(secretKey)
secretData, _ := base64.StdEncoding.DecodeString(encryptedData)
if block, err = aes.NewCipher(aesKey); err != nil {
return nil, fmt.Errorf("aes.NewCipher:%w", err)
}
if len(secretData)%len(aesKey) != 0 {
return nil, errors.New("encryptedData is error")
}
blockMode = cipher.NewCBCDecrypter(block, ivKey)
originData = make([]byte, len(secretData))
blockMode.CryptBlocks(originData, secretData)
if len(originData) > 0 {
originData = xaes.PKCS5UnPadding(originData)
}
bm = make(gopay.BodyMap)
if err = json.Unmarshal(originData, &bm); err != nil {
return nil, fmt.Errorf("json.Unmarshal(%s):%w", string(originData), err)
}
return
}
// SystemOauthToken 换取授权访问令牌(默认使用utf-8,RSA2)
// appId:应用ID
// privateKey:应用私钥
// grantType:值为 authorization_code 时,代表用code换取;值为 refresh_token 时,代表用refresh_token换取,传空默认code换取
// codeOrToken:支付宝授权码或refresh_token
// signType:签名方式 RSA 或 RSA2,默认 RSA2
// 文档:https://opendocs.alipay.com/apis/api_9/alipay.system.oauth.token
func SystemOauthToken(appId string, privateKey, grantType, codeOrToken, signType string) (rsp *SystemOauthTokenResponse, err error) {
key := xrsa.FormatAlipayPrivateKey(privateKey)
priKey, err := xpem.DecodePrivateKey([]byte(key))
if err != nil {
return nil, err
}
var bs []byte
bm := make(gopay.BodyMap)
switch grantType {
case "authorization_code":
bm.Set("grant_type", "authorization_code")
bm.Set("code", codeOrToken)
case "refresh_token":
bm.Set("grant_type", "refresh_token")
bm.Set("refresh_token", codeOrToken)
default:
bm.Set("grant_type", "authorization_code")
bm.Set("code", codeOrToken)
}
if bs, err = systemOauthToken(appId, priKey, bm, "alipay.system.oauth.token", true, signType); err != nil {
return
}
rsp = new(SystemOauthTokenResponse)
if err = json.Unmarshal(bs, rsp); err != nil {
return nil, fmt.Errorf("json.Unmarshal(%s):%w", string(bs), err)
}
if rsp.Response.AccessToken == "" {
return nil, errors.New("access_token is NULL")
}
return
}
// systemOauthToken 向支付宝发送请求
func systemOauthToken(appId string, privateKey *rsa.PrivateKey, bm gopay.BodyMap, method string, isProd bool, signType string) (bs []byte, err error) {
bm.Set("app_id", appId)
bm.Set("method", method)
bm.Set("format", "JSON")
bm.Set("charset", "utf-8")
if signType == util.NULL {
bm.Set("sign_type", RSA2)
} else {
bm.Set("sign_type", signType)
}
bm.Set("timestamp", time.Now().Format(util.TimeLayout))
bm.Set("version", "1.0")
var (
sign string
baseUrl = baseUrlUtf8
)
if sign, err = GetRsaSign(bm, bm.GetString("sign_type"), privateKey); err != nil {
return nil, err
}
bm.Set("sign", sign)
if !isProd {
baseUrl = sandboxBaseUrlUtf8
}
_, bs, errs := xhttp.NewClient().Type(xhttp.TypeForm).Post(baseUrl).SendString(bm.EncodeURLParams()).EndBytes()
if len(errs) > 0 {
return nil, errs[0]
}
return bs, nil
}
// monitor.heartbeat.syn(验签接口)
// appId:应用ID
// privateKey:应用私钥,支持PKCS1和PKCS8
// signType:签名方式 alipay.RSA 或 alipay.RSA2,默认 RSA2
// bizContent:验签时该参数不做任何处理,{任意值},此参数具体看文档
// 文档:https://opendocs.alipay.com/apis/api_9/monitor.heartbeat.syn
func MonitorHeartbeatSyn(appId string, privateKey, signType, bizContent string) (rsp *MonitorHeartbeatSynResponse, err error) {
key := xrsa.FormatAlipayPrivateKey(privateKey)
priKey, err := xpem.DecodePrivateKey([]byte(key))
if err != nil {
return nil, err
}
var bs []byte
bm := make(gopay.BodyMap)
bm.Set("biz_content", bizContent)
bm.Set("app_id", appId)
bm.Set("method", "monitor.heartbeat.syn")
bm.Set("format", "JSON")
bm.Set("charset", "utf-8")
if signType == util.NULL {
bm.Set("sign_type", RSA2)
} else {
bm.Set("sign_type", signType)
}
bm.Set("timestamp", time.Now().Format(util.TimeLayout))
bm.Set("version", "1.0")
sign, err := GetRsaSign(bm, bm.GetString("sign_type"), priKey)
if err != nil {
return nil, err
}
bm.Set("sign", sign)
_, bs, errs := xhttp.NewClient().Type(xhttp.TypeForm).Post(baseUrlUtf8).SendString(bm.EncodeURLParams()).EndBytes()
if len(errs) > 0 {
return nil, errs[0]
}
rsp = new(MonitorHeartbeatSynResponse)
if err = json.Unmarshal(bs, rsp); err != nil {
return nil, err
}
return rsp, nil
}