coinpayments.*Client.newResponse   A
last analyzed

Complexity

Conditions 3

Size

Total Lines 15
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 10
nop 1
dl 0
loc 15
rs 9.9
c 0
b 0
f 0
1
package coinpayments
2
3
import (
4
	"context"
5
	"crypto/hmac"
6
	"crypto/sha512"
7
	"fmt"
8
	"io"
9
	"io/ioutil"
10
	"net/http"
11
	"net/url"
12
	"strconv"
13
	"strings"
14
15
	"github.com/davecgh/go-spew/spew"
16
)
17
18
type service struct {
19
	client *Client
20
}
21
22
const (
23
	// HeaderNameHMAC is used for authentication
24
	HeaderNameHMAC = "HMAC"
25
)
26
27
// Client is the coinpayments API client.
28
// Do not instantiate this client with Client{}. Use the New method instead.
29
type Client struct {
30
	httpClient *http.Client
31
	common     service
32
	baseURL    string
33
	apiKey     string
34
	apiSecret  string
35
	version    string
36
	ipnSecret  string
37
38
	Payment *paymentService
39
}
40
41
// New creates and returns a new Client from a slice of Option.
42
func New(options ...Option) *Client {
43
	config := defaultClientConfig()
44
45
	for _, option := range options {
46
		option.apply(config)
47
	}
48
49
	client := &Client{
50
		apiKey:     config.apiKey,
51
		version:    config.version,
52
		apiSecret:  config.apiSecret,
53
		httpClient: config.httpClient,
54
		baseURL:    config.baseURL,
55
		ipnSecret:  config.ipnSecret,
56
	}
57
58
	client.common.client = client
59
	client.Payment = (*paymentService)(&client.common)
60
	return client
61
}
62
63
// newRequest creates an API request. A relative URL can be provided in uri,
64
// in which case it is resolved relative to the BaseURL of the Client.
65
// URI's should always be specified without a preceding slash.
66
func (client *Client) newRequest(ctx context.Context, method, cmd string, body url.Values) (*http.Request, error) {
67
	body.Add("cmd", cmd)
68
	body.Add("key", client.apiKey)
69
	body.Add("format", "json")
70
	body.Add("version", client.version)
71
72
	req, err := http.NewRequestWithContext(ctx, method, client.baseURL, strings.NewReader(body.Encode()))
73
	if err != nil {
74
		return nil, err
75
	}
76
77
	// generate hmac hash of data and private key
78
	hash, err := client.computeHMAC(body.Encode(), client.apiSecret)
79
	if err != nil {
80
		return nil, err
81
	}
82
83
	spew.Dump(hash)
84
	req.Header.Add(HeaderNameHMAC, hash)
85
	req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
86
	req.Header.Add("Content-Length", strconv.Itoa(len(body.Encode())))
87
88
	return req, nil
89
}
90
91
// do carries out an HTTP request and returns a Response
92
func (client *Client) do(req *http.Request) (*Response, error) {
93
	if req == nil {
94
		return nil, fmt.Errorf("%T cannot be nil", req)
95
	}
96
97
	httpResponse, err := client.httpClient.Do(req)
98
	if err != nil {
99
		return nil, err
100
	}
101
102
	defer func() { _ = httpResponse.Body.Close() }()
103
104
	resp, err := client.newResponse(httpResponse)
105
	if err != nil {
106
		return resp, err
107
	}
108
109
	_, err = io.Copy(ioutil.Discard, httpResponse.Body)
110
	if err != nil {
111
		return resp, err
112
	}
113
114
	return resp, nil
115
}
116
117
// newResponse converts an *http.Response to *Response
118
func (client *Client) newResponse(httpResponse *http.Response) (*Response, error) {
119
	if httpResponse == nil {
120
		return nil, fmt.Errorf("%T cannot be nil", httpResponse)
121
	}
122
123
	resp := new(Response)
124
	resp.HTTPResponse = httpResponse
125
126
	buf, err := ioutil.ReadAll(resp.HTTPResponse.Body)
127
	if err != nil {
128
		return nil, err
129
	}
130
	resp.Body = &buf
131
132
	return resp, resp.Error()
133
}
134
135
// computeHMAC returns the hmac hash of the data
136
func (client *Client) computeHMAC(data string, secret string) (string, error) {
137
	hash := hmac.New(sha512.New, []byte(secret))
138
	if _, err := hash.Write([]byte(data)); err != nil {
139
		return "", err
140
	}
141
	return fmt.Sprintf("%x", hash.Sum(nil)), nil
142
}
143
144
// IpnHMAC returns the hmac hash of the data
145
func (client *Client) IpnHMAC(data string) (string, error) {
146
	return client.computeHMAC(data, client.ipnSecret)
147
}
148