-
Notifications
You must be signed in to change notification settings - Fork 0
/
client.go
144 lines (133 loc) · 3.43 KB
/
client.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
package vip_gosdk
import (
"crypto/hmac"
"crypto/md5"
"encoding/hex"
"encoding/json"
"fmt"
"github.com/fatih/structs"
jsoniter "github.com/json-iterator/go"
"github.com/spf13/cast"
"net/http"
"net/url"
"reflect"
"sort"
"strings"
"time"
)
// The client saves some private required fields.
// Before executing the request, a new Client must be created.
type Client struct {
appKey string
secret string
userNumber string
}
// NewClient returns a new Client.
// appKey, secret, userNumber can not be empty.
func NewClient(appKey, secret, userNumber string) *Client {
if appKey == "" || secret == "" || userNumber == "" {
panic("required parameters cannot be empty")
}
return &Client{
appKey: appKey,
secret: secret,
userNumber: userNumber,
}
}
// The Request describes a request's method and request request type
type Request interface {
ApiMethod() string
RequestMethod() string
}
// The Request describes a request's return information
type Response interface{}
// General request.
func (client *Client) Do(req Request, params map[string]interface{}, res Response) (err error) {
r := map[string]interface{}{"request": params}
// Body
body, err := jsoniter.MarshalToString(r)
if err != nil {
return
}
// Initialization timestamp
t := cast.ToString(time.Now().Unix())
// Get signature
sign := client.getVipSign(body, req.ApiMethod(), t)
// Request URL: k-v key-value pair splicing
v := url.Values{}
v.Set("appKey", client.appKey)
v.Set("format", Format)
v.Set("method", req.ApiMethod())
v.Set("service", Service)
v.Set("sign", sign)
v.Set("timestamp", t)
v.Set("version", Version)
domain := Domain
_url, err := url.Parse(domain)
if err != nil {
return
}
_url.RawQuery = v.Encode()
urlPath := _url.String()
// NewRequest
request, err := http.NewRequest(req.RequestMethod(), urlPath, strings.NewReader(body))
if err != nil {
return
}
request.Header.Set("Content-Type", "application/json;charset=UTF-8")
resp, err := http.DefaultClient.Do(request)
if err != nil {
return
}
defer resp.Body.Close()
err = json.NewDecoder(resp.Body).Decode(res)
if err != nil {
return
}
return
}
// Signature encryption string.
type VipApiEncrypt struct {
AppKey string `json:"appKey"`
Format string `json:"format"`
Method string `json:"method"`
Service string `json:"service"`
Timestamp string `json:"timestamp"`
Version string `json:"version"`
}
// Obtain VIP signature, internal implementation.
func (client *Client) getVipSign(reqString, apiMethod, t string) (signString string) {
p := VipApiEncrypt{
AppKey: client.appKey,
Format: Format,
Method: apiMethod,
Service: Service,
Timestamp: t,
Version: Version,
}
resMap := structs.Map(p)
sortedKeys := make([]string, 0, len(resMap))
for k := range resMap {
sortedKeys = append(sortedKeys, k)
}
sort.Strings(sortedKeys)
for _, k := range sortedKeys {
value := fmt.Sprintf("%v", cast.ToString(resMap[k]))
if value != "" {
signString += getKeyName(k, &p) + value
}
}
return strings.ToUpper(Hmac(client.secret, signString+reqString))
}
// Generate hmac-md 5 encryption.
func Hmac(key, data string) string {
h := hmac.New(md5.New, []byte(key))
h.Write([]byte(data))
return hex.EncodeToString(h.Sum([]byte("")))
}
// Get the corresponding Key Name according to the tag name.
func getKeyName(key string, req *VipApiEncrypt) string {
s := reflect.TypeOf(req).Elem()
f, _ := s.FieldByName(key)
return f.Tag.Get("json")
}