-
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcoincheck.go
137 lines (118 loc) · 3.81 KB
/
coincheck.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
// Package coincheck provides a client for using the coincheck API.
// Ref. https://coincheck.com/documents/exchange/api
//
// The coincheck allows roughly two kinds of APIs; Public API and Private API.
// Public API allows you to browse order status and order book.
// Private API allows you to create and cancel new orders, and to check your balance.
// If you use Private API, you need to get your API key and API secret from the coincheck website.
package coincheck
import (
"context"
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
)
const (
// BaseURL is the base URL for the coincheck API.
BaseURL = "https://coincheck.com"
)
// Client represents a coincheck client.
type Client struct {
// client is HTTP client that used to communicate with the Coincheck API.
client *http.Client
// baseURL is the base URL for the coincheck API.
baseURL *url.URL
// credentials is the credentials used to authenticate with the coincheck API.
credentials *credentials
}
// NewClient returns a new coincheck client.
func NewClient(opts ...Option) (*Client, error) {
c := &Client{
client: http.DefaultClient,
}
baseURL, err := url.Parse(BaseURL)
if err != nil {
return nil, withPrefixError(err)
}
c.baseURL = baseURL
for _, opt := range opts {
if err := opt(c); err != nil {
return nil, err
}
}
return c, nil
}
// hasCredentials returns true if the client has credentials.
func (c *Client) hasCredentials() bool {
return c.credentials != nil
}
// setAuthHeaders sets the authentication headers to the request.
// If you use Private API, you need to get your API key and API secret from the coincheck website.
func (c *Client) setAuthHeaders(req *http.Request, body string) error {
if !c.hasCredentials() {
return ErrNoCredentials
}
headers, err := c.credentials.generateRequestHeaders(req.URL, body)
if err != nil {
return withPrefixError(err)
}
req.Header.Set("ACCESS-KEY", headers.AccessKey)
req.Header.Set("ACCESS-NONCE", headers.AccessNonce)
req.Header.Set("ACCESS-SIGNATURE", headers.AccessSignature)
return nil
}
// createRequestInput represents the input parameters for createRequest.
type createRequestInput struct {
method string // HTTP method (e.g. GET, POST)
path string // API path (e.g. /api/orders)
body io.Reader // Request body. If you don't need it, set nil.
queryParam map[string]string // Query parameters (e.g. {"pair": "btc_jpy"}) If you don't need it, set nil.
private bool // If true, it's a private API.
}
// createRequest creates a new HTTP request.
func (c *Client) createRequest(ctx context.Context, input createRequestInput) (*http.Request, error) {
u, err := url.JoinPath(c.baseURL.String(), input.path)
if err != nil {
return nil, withPrefixError(err)
}
endpoint, err := url.Parse(u)
if err != nil {
return nil, withPrefixError(err)
}
if input.queryParam != nil {
q := endpoint.Query()
for k, v := range input.queryParam {
q.Set(k, v)
}
endpoint.RawQuery = q.Encode()
}
req, err := http.NewRequestWithContext(ctx, input.method, endpoint.String(), input.body)
if err != nil {
return nil, withPrefixError(err)
}
req.Header.Add("content-type", "application/json")
req.Header.Add("cache-control", "no-cache")
if input.private {
if err := c.setAuthHeaders(req, ""); err != nil {
return nil, err
}
}
return req, nil
}
// Do sends an HTTP request and returns an HTTP response.
func (c *Client) do(req *http.Request, output any) error {
resp, err := c.client.Do(req)
if err != nil {
return withPrefixError(err)
}
defer resp.Body.Close() //nolint: errcheck // ignore error
if resp.StatusCode != http.StatusOK {
return withPrefixError(fmt.Errorf("unexpected status code=%d", resp.StatusCode))
}
if err := json.NewDecoder(resp.Body).Decode(output); err != nil {
return withPrefixError(err)
}
return nil
}