diff --git a/README.md b/README.md index c5348bf..7d6a8d0 100644 --- a/README.md +++ b/README.md @@ -97,6 +97,7 @@ If you want to execute the Private API, you need to create a client with the API | API | Method Name |Description | | :--- | :--- | :--- | | GET /api/bank_accounts | [GetBankAccounts()](https://pkg.go.dev/github.com/nao1215/coincheck#Client.GetBankAccounts) | Display list of bank account you registered (withdrawal).| +| GET /api/accounts/balance | [GetAccountsBalance()](https://pkg.go.dev/github.com/nao1215/coincheck#Client.GetAccountsBalance) | Get the balance of your account. | ## License diff --git a/balance.go b/balance.go new file mode 100644 index 0000000..6cfc054 --- /dev/null +++ b/balance.go @@ -0,0 +1,57 @@ +package coincheck + +import ( + "context" + "net/http" +) + +// GetAccountsBalanceResponse represents the response from the GetAccountsBalance method. +type GetAccountsBalanceResponse struct { + // Success is true if the request was successful. + Success bool `json:"success"` + // JPY is the balance of JPY. + JPY string `json:"jpy"` + // BTC is the balance of BTC. + BTC string `json:"btc"` + // JPYReserved is amount of JPY for unsettled buying order + JPYReserved string `json:"jpy_reserved"` + // BTCReserved is amount of BTC for unsettled selling order + BTCReserved string `json:"btc_reserved"` + // JPYLendInUse is JPY amount you are applying for lending (We don't allow you to loan JPY.) + JPYLendInUse string `json:"jpy_lend_in_use"` + // BTCLendInUse is BTC Amount you are applying for lending (We don't allow you to loan BTC.) + BTCLendInUse string `json:"btc_lend_in_use"` + // JPYLent is JPY lending amount (Currently, we don't allow you to loan JPY.) + JPYLent string `json:"jpy_lent"` + // BTCLent is BTC lending amount (Currently, we don't allow you to loan BTC.) + BTCLent string `json:"btc_lent"` + // JPYDebt is JPY borrowing amount + JPYDebt string `json:"jpy_debt"` + // BTCDebt is BTC borrowing amount + BTCDebt string `json:"btc_debt"` + // JPYTsumitate is JPY reserving amount + JPYTsumitate string `json:"jpy_tsumitate"` + // BTCTsumitate is BTC reserving amount + BTCTsumitate string `json:"btc_tsumitate"` +} + +// GetAccountsBalance returns the balance of the account. +// API: GET /api/accounts/balance +// Visibility: Private +// It doesn't include jpy_reserved use unsettled orders (it's GET /api/exchange/orders/opens) in jpy btc. +func (c *Client) GetAccountsBalance(ctx context.Context) (*GetAccountsBalanceResponse, error) { + req, err := c.createRequest(ctx, createRequestInput{ + method: http.MethodGet, + path: "/api/accounts/balance", + private: true, + }) + if err != nil { + return nil, err + } + + var output GetAccountsBalanceResponse + if err := c.do(req, &output); err != nil { + return nil, err + } + return &output, nil +} diff --git a/balance_test.go b/balance_test.go new file mode 100644 index 0000000..45b7532 --- /dev/null +++ b/balance_test.go @@ -0,0 +1,111 @@ +package coincheck + +import ( + "context" + "encoding/json" + "errors" + "net/http" + "net/http/httptest" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestClient_GetBalance(t *testing.T) { + t.Run("GetBalance returns the balance", func(t *testing.T) { + testServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + wantMethod := http.MethodGet + if diff := cmp.Diff(wantMethod, r.Method); diff != "" { + printDiff(t, diff) + } + + wantEndpoint := "/api/accounts/balance" + if diff := cmp.Diff(wantEndpoint, r.URL.Path); diff != "" { + printDiff(t, diff) + } + + result := GetAccountsBalanceResponse{ + Success: true, + JPY: "0.8401", + BTC: "7.75052654", + JPYReserved: "3000.0", + BTCReserved: "3.5002", + JPYLendInUse: "1.1", + BTCLendInUse: "0.3", + JPYLent: "0", + BTCLent: "1.2", + JPYDebt: "0", + BTCDebt: "0", + JPYTsumitate: "10000.0", + BTCTsumitate: "0.43034", + } + if err := json.NewEncoder(w).Encode(result); err != nil { + t.Fatal(err) + } + })) + + client, err := NewClient( + WithBaseURL(testServer.URL), + WithCredentials("api_key", "api_secret"), + ) + if err != nil { + t.Fatal(err) + } + + got, err := client.GetAccountsBalance(context.Background()) + if err != nil { + t.Fatal(err) + } + + want := &GetAccountsBalanceResponse{ + Success: true, + JPY: "0.8401", + BTC: "7.75052654", + JPYReserved: "3000.0", + BTCReserved: "3.5002", + JPYLendInUse: "1.1", + BTCLendInUse: "0.3", + JPYLent: "0", + BTCLent: "1.2", + JPYDebt: "0", + BTCDebt: "0", + JPYTsumitate: "10000.0", + BTCTsumitate: "0.43034", + } + if diff := cmp.Diff(want, got); diff != "" { + printDiff(t, diff) + } + }) + + t.Run("GetBalance returns an error if the server returns an error", func(t *testing.T) { + testServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + w.WriteHeader(http.StatusInternalServerError) + })) + + client, err := NewClient( + WithBaseURL(testServer.URL), + WithCredentials("api_key", "api_secret"), + ) + if err != nil { + t.Fatal(err) + } + + _, err = client.GetAccountsBalance(context.Background()) + if err == nil { + t.Fatal("GetBalance did not return an error") + } + }) + t.Run("GetBankAccounts returns an error if client does not set credentials", func(t *testing.T) { + // Create a new client + client, err := NewClient(WithBaseURL("https://example.com")) + if err != nil { + t.Fatal(err) + } + + // Start testing + _, err = client.GetAccountsBalance(context.Background()) + if !errors.Is(err, ErrNoCredentials) { + t.Errorf("error is not ErrNoCredentials: %v", err) + } + }) +} diff --git a/coincheck.go b/coincheck.go index 94691d3..9a61ce5 100644 --- a/coincheck.go +++ b/coincheck.go @@ -79,6 +79,7 @@ type createRequestInput struct { 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. @@ -108,6 +109,12 @@ func (c *Client) createRequest(ctx context.Context, input createRequestInput) (* 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 } diff --git a/withdraw.go b/withdraw.go index 2234077..6dca7f3 100644 --- a/withdraw.go +++ b/withdraw.go @@ -34,15 +34,13 @@ type BankAccount struct { // Visibility: Private func (c *Client) GetBankAccounts(ctx context.Context) (*GetBankAccountsResponse, error) { req, err := c.createRequest(ctx, createRequestInput{ - method: http.MethodGet, - path: "/api/bank_accounts", + method: http.MethodGet, + path: "/api/bank_accounts", + private: true, }) if err != nil { return nil, err } - if err := c.setAuthHeaders(req, ""); err != nil { - return nil, err - } var output GetBankAccountsResponse if err := c.do(req, &output); err != nil {