-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathclient.go
146 lines (119 loc) · 3.61 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
145
146
package coinpayments
import (
"crypto/hmac"
"crypto/sha512"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"net/url"
"strconv"
"strings"
)
//ClientOption is an option used to modify a client
type ClientOption func(client *Client)
//OptionalValue is an option used to add values to an api request
type OptionalValue func(values *url.Values)
//Client allows programmatic access to the coinpayments api
type Client struct {
client *http.Client
privateKey string
publicKey string
ipnSecret string
}
//NewClient returns a new Client with the applied options
func NewClient(publicKey, privateKey string, options ...ClientOption) *Client {
client := &Client{
privateKey: privateKey,
publicKey: publicKey,
client: http.DefaultClient,
}
for _, o := range options {
o(client)
}
return client
}
//WithHTTPClient is an option that makes the Client use the provided http client
func WithHTTPClient(httpClient *http.Client) ClientOption {
return func(client *Client) {
client.client = httpClient
}
}
//WithIPNSecret is an option that makes the Client use the provided secret
func WithIPNSecret(secret string) ClientOption {
return func(client *Client) {
client.ipnSecret = secret
}
}
//WithOptionalValue is an option that adds values to an api request
func WithOptionalValue(key, value string) OptionalValue {
return func(values *url.Values) {
values.Set(key, value)
}
}
func (c *Client) call(cmd string, values *url.Values, response interface{}) error {
values.Add("key", c.publicKey)
values.Add("version", apiVersion)
values.Add("cmd", cmd)
values.Add("format", apiFormat)
sData := values.Encode()
dataHMAC, err := c.makeHMAC(sData)
if err != nil {
return fmt.Errorf("coinpayments: error making HMAC - %v", err)
}
req, err := http.NewRequest("POST", apiURL, strings.NewReader(sData))
if err != nil {
return fmt.Errorf("coinpayments: error making api request - %v", err)
}
req.Header.Add("HMAC", dataHMAC)
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
req.Header.Add("Content-Length", strconv.Itoa(len(sData)))
req.Header.Add("User-Agent", "github.com/aidenesco/coinpayments")
req.Header.Add("Accept", "application/json")
resp, err := c.client.Do(req)
if err != nil {
return fmt.Errorf("coinpayments: error doing api request - %v", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("coinpayments: api call returned unexpected status: %v", resp.StatusCode)
}
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return fmt.Errorf("coinpayments: error reading api response body - %v", err)
}
var errResp errResponse
if err := json.Unmarshal(body, &errResp); err != nil {
return fmt.Errorf("coinpayments: error unmarshaling api error response - %v", err)
}
if errResp.Error != apiSuccess {
return fmt.Errorf("coinpayments: api error - %v", errResp.Error)
}
err = json.Unmarshal(body, response)
if err != nil {
return fmt.Errorf("coinpayments: error unmarshaling response json - %v", err)
}
return nil
}
func (c *Client) makeHMAC(data string) (string, error) {
hash := hmac.New(sha512.New, []byte(c.privateKey))
if _, err := hash.Write([]byte(data)); err != nil {
return "", err
}
return fmt.Sprintf("%x", hash.Sum(nil)), nil
}
func (c *Client) makeIPNHMAC(data string) (string, error) {
hash := hmac.New(sha512.New, []byte(c.ipnSecret))
if _, err := hash.Write([]byte(data)); err != nil {
return "", err
}
return fmt.Sprintf("%x", hash.Sum(nil)), nil
}
func addOptionals(opts []OptionalValue, values *url.Values) {
for _, v := range opts {
v(values)
}
}
type errResponse struct {
Error string `json:"error"`
}