<|start_filename|>error.go<|end_filename|>
package bunq

import (
	"encoding/json"
	"fmt"
	"io"
	"strings"
)

// Error represents an error returned by the bunq API.
type Error struct {
	ErrorDescription           string
	ErrorDescriptionTranslated string
}

// Errors is an array of Error structs.
type Errors []Error

func (e Errors) Error() string {
	errs := make([]string, len(e))
	for i, err := range e {
		errs[i] = err.ErrorDescription
	}

	return strings.Join(errs, ", ")
}

func decodeError(r io.Reader) error {
	var apiError struct {
		Error []struct {
			ErrorDescription           string `json:"error_description"`
			ErrorDescriptionTranslated string `json:"error_description_translated"`
		} `json:"Error"`
	}
	err := json.NewDecoder(r).Decode(&apiError)
	if err != nil {
		return fmt.Errorf("could not decode errors from json: %v", err)
	}

	var errors Errors
	for i := range apiError.Error {
		errors = append(errors, Error{
			ErrorDescription:           apiError.Error[i].ErrorDescription,
			ErrorDescriptionTranslated: apiError.Error[i].ErrorDescriptionTranslated,
		})
	}

	return errors
}


<|start_filename|>user.go<|end_filename|>
package bunq

import (
	"encoding/json"
	"errors"
	"fmt"
	"net/http"
	"strconv"
)

type userResponse struct {
	Response []struct {
		UserCompany *UserCompany `json:"UserCompany"`
	} `json:"Response"`
}

// ErrUserNotFound is returned when a single User resource was not found.
var ErrUserNotFound = errors.New("user not found")

// GetUser gets a User resource at the bunq API.
func (c *Client) GetUser(id int) (interface{}, error) {
	httpMethod := http.MethodGet
	endpoint := apiVersion + "/user/" + strconv.Itoa(id)
	req, err := http.NewRequest(httpMethod, fmt.Sprintf("%v/%v", c.BaseURL, endpoint), nil)
	if err != nil {
		return nil, fmt.Errorf("bunq: could not create new request: %v", err)
	}
	setCommonHeaders(req)
	req.Header.Set("X-Bunq-Client-Authentication", c.Token)
	if err = c.addSignature(req, fmt.Sprintf("%v /%v", httpMethod, endpoint), ""); err != nil {
		return nil, fmt.Errorf("bunq: could not add signature: %v", err)
	}

	resp, err := c.HTTPClient.Do(req)
	if err != nil {
		return nil, fmt.Errorf("bunq: could not send HTTP request: %v", err)
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("bunq: request was unsuccessful: %v", decodeError(resp.Body))
	}

	var userResp userResponse
	if err = json.NewDecoder(resp.Body).Decode(&userResp); err != nil {
		return nil, fmt.Errorf("bunq: could not decode HTTP response: %v", err)
	}

	users, err := userResp.users()
	if err != nil {
		return nil, fmt.Errorf("bunq: could not parse API response: %v", err)
	}
	if len(users) == 0 {
		return nil, ErrUserNotFound
	}

	return users[0], nil
}

// ListUsers gets a list of User resources at the bunq API.
func (c *Client) ListUsers() ([]interface{}, error) {
	httpMethod := http.MethodGet
	endpoint := apiVersion + "/user"
	req, err := http.NewRequest(httpMethod, fmt.Sprintf("%v/%v", c.BaseURL, endpoint), nil)
	if err != nil {
		return nil, fmt.Errorf("bunq: could not create new request: %v", err)
	}
	setCommonHeaders(req)
	req.Header.Set("X-Bunq-Client-Authentication", c.Token)
	if err = c.addSignature(req, fmt.Sprintf("%v /%v", httpMethod, endpoint), ""); err != nil {
		return nil, fmt.Errorf("bunq: could not add signature: %v", err)
	}

	resp, err := c.HTTPClient.Do(req)
	if err != nil {
		return nil, fmt.Errorf("bunq: could not send HTTP request: %v", err)
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("bunq: request was unsuccessful: %v", decodeError(resp.Body))
	}

	var userResp userResponse
	if err = json.NewDecoder(resp.Body).Decode(&userResp); err != nil {
		return nil, fmt.Errorf("bunq: could not decode HTTP response: %v", err)
	}

	users, err := userResp.users()
	if err != nil {
		return nil, fmt.Errorf("bunq: could not parse API response: %v", err)
	}

	return users, nil
}

func (userResp *userResponse) users() ([]interface{}, error) {
	users := make([]interface{}, len(userResp.Response))
	for i := range userResp.Response {
		if userResp.Response[i].UserCompany != nil {
			users[i] = *userResp.Response[i].UserCompany
		}
	}

	return users, nil
}


<|start_filename|>session_server.go<|end_filename|>
package bunq

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"net/http"
)

type sessionServerResponse struct {
	Response []struct {
		ID *struct {
			ID int `json:"id"`
		} `json:"Id,omitempty"`
		Token *struct {
			ID    int    `json:"id"`
			Token string `json:"token"`
		} `json:"Token,omitempty"`
		UserCompany *UserCompany `json:"UserCompany,omitempty"`
	} `json:"Response"`
}

// A SessionToken is used for authenticating requests to the bunq API.
type SessionToken struct {
	ID    int
	Token string
}

// A Session represents a SessionServer at the bunq API.
type Session struct {
	ID          int
	Token       SessionToken
	UserCompany UserCompany
}

// CreateSession creates a new session for a DeviceServer at the bunq API.
func (c *Client) CreateSession() (*Session, error) {
	body := struct {
		Secret string `json:"secret"`
	}{c.APIKey}

	bodyJSON, err := json.Marshal(body)
	if err != nil {
		return nil, fmt.Errorf("bunq: could not encode request body into JSON: %v", err)
	}

	httpMethod := http.MethodPost
	endpoint := apiVersion + "/session-server"
	req, err := http.NewRequest(httpMethod, fmt.Sprintf("%v/%v", c.BaseURL, endpoint), bytes.NewReader(bodyJSON))
	if err != nil {
		return nil, fmt.Errorf("bunq: could not create new request: %v", err)
	}
	setCommonHeaders(req)
	req.Header.Set("X-Bunq-Client-Authentication", c.Token)
	if err = c.addSignature(req, fmt.Sprintf("%v /%v", httpMethod, endpoint), string(bodyJSON)); err != nil {
		return nil, fmt.Errorf("bunq: could not add signature: %v", err)
	}

	resp, err := c.HTTPClient.Do(req)
	if err != nil {
		return nil, fmt.Errorf("bunq: could not send HTTP request: %v", err)
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("bunq: request was unsuccessful: %v", decodeError(resp.Body))
	}

	var ssr sessionServerResponse
	if err = json.NewDecoder(resp.Body).Decode(&ssr); err != nil {
		return nil, fmt.Errorf("bunq: could not decode HTTP response: %v", err)
	}

	if len(ssr.Response) == 0 {
		return nil, errors.New("bunq: api response did not contain results")
	}

	session, err := ssr.session()
	if err != nil {
		return nil, fmt.Errorf("bunq: could not parse API response: %v", err)
	}

	return session, nil
}

func (ssr *sessionServerResponse) session() (*Session, error) {
	session := &Session{}
	for i := range ssr.Response {
		if ssr.Response[i].ID != nil {
			session.ID = ssr.Response[i].ID.ID
			continue
		}
		if ssr.Response[i].Token != nil {
			session.Token.ID = ssr.Response[i].Token.ID
			session.Token.Token = ssr.Response[i].Token.Token
			continue
		}
		if ssr.Response[i].UserCompany != nil {
			session.UserCompany = *ssr.Response[i].UserCompany
		}
	}

	return session, nil
}


<|start_filename|>installation.go<|end_filename|>
package bunq

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"net/http"
	"strconv"
	"time"
)

type installationResponse struct {
	Response []struct {
		ID *struct {
			ID int `json:"id"`
		} `json:"Id,omitempty"`
		Token *struct {
			ID      int    `json:"id"`
			Created Time   `json:"created"`
			Updated Time   `json:"updated"`
			Token   string `json:"token"`
		} `json:"Token,omitempty"`
		ServerPublicKey *struct {
			ServerPublicKey string `json:"server_public_key"`
		} `json:"ServerPublicKey,omitempty"`
	} `json:"Response"`
}

// An InstallationToken is used for authenticating requests to the bunq API.
type InstallationToken struct {
	ID        int
	CreatedAt time.Time
	UpdatedAt time.Time
	Token     string
}

// An Installation represents an installation resource at the bunq API.
type Installation struct {
	ID              int
	Token           InstallationToken
	ServerPublicKey string
}

// ErrInstallationNotFound is returned when a single Installation resource was
// not found.
var ErrInstallationNotFound = errors.New("installation not found")

// CreateInstallation creates an installation resource at the bunq API.
func (c *Client) CreateInstallation() (*Installation, error) {
	publicKey, err := c.publicKey()
	if err != nil {
		return nil, fmt.Errorf("bunq: could not get public key: %v", err)
	}

	body := struct {
		Field string `json:"client_public_key"`
	}{string(publicKey)}
	bodyJSON, err := json.Marshal(body)
	if err != nil {
		return nil, fmt.Errorf("bunq: could not encode request body into JSON: %v", err)
	}

	endpoint := apiVersion + "/installation"
	req, err := http.NewRequest(http.MethodPost, fmt.Sprintf("%v/%v", c.BaseURL, endpoint), bytes.NewReader(bodyJSON))
	if err != nil {
		return nil, fmt.Errorf("bunq: could not create new request: %v", err)
	}
	setCommonHeaders(req)

	resp, err := c.HTTPClient.Do(req)
	if err != nil {
		return nil, fmt.Errorf("bunq: could not send HTTP request: %v", err)
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("bunq: request was unsuccessful: %v", decodeError(resp.Body))
	}

	var insResp installationResponse
	if err = json.NewDecoder(resp.Body).Decode(&insResp); err != nil {
		return nil, fmt.Errorf("bunq: could not decode HTTP response: %v", err)
	}

	if len(insResp.Response) == 0 {
		return nil, errors.New("bunq: api response did not contain results")
	}

	installation, err := insResp.installation()
	if err != nil {
		return nil, fmt.Errorf("bunq: could not parse API response: %v", err)
	}

	return installation, nil
}

// GetInstallation gets an Installation resource at the bunq API.
func (c *Client) GetInstallation(id int) (*Installation, error) {
	httpMethod := http.MethodGet
	endpoint := apiVersion + "/installation/" + strconv.Itoa(id)
	req, err := http.NewRequest(httpMethod, fmt.Sprintf("%v/%v", c.BaseURL, endpoint), nil)
	if err != nil {
		return nil, fmt.Errorf("bunq: could not create new request: %v", err)
	}
	setCommonHeaders(req)
	req.Header.Set("X-Bunq-Client-Authentication", c.Token)
	if err = c.addSignature(req, fmt.Sprintf("%v /%v", httpMethod, endpoint), ""); err != nil {
		return nil, fmt.Errorf("bunq: could not add signature: %v", err)
	}

	resp, err := c.HTTPClient.Do(req)
	if err != nil {
		return nil, fmt.Errorf("bunq: could not send HTTP request: %v", err)
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("bunq: request was unsuccessful: %v", decodeError(resp.Body))
	}

	var insResp installationResponse
	if err = json.NewDecoder(resp.Body).Decode(&insResp); err != nil {
		return nil, fmt.Errorf("bunq: could not decode HTTP response: %v", err)
	}

	if len(insResp.Response) == 0 {
		return nil, ErrInstallationNotFound
	}

	installation, err := insResp.installation()
	if err != nil {
		return nil, fmt.Errorf("bunq: could not parse API response: %v", err)
	}

	return installation, nil
}

// GetInstallationID gets the installation ID of the Installation used for the
// current session.
func (c *Client) GetInstallationID() (int, error) {
	httpMethod := http.MethodGet
	endpoint := apiVersion + "/installation"
	req, err := http.NewRequest(httpMethod, fmt.Sprintf("%v/%v", c.BaseURL, endpoint), nil)
	if err != nil {
		return 0, fmt.Errorf("bunq: could not create new request: %v", err)
	}
	setCommonHeaders(req)
	req.Header.Set("X-Bunq-Client-Authentication", c.Token)
	if err = c.addSignature(req, fmt.Sprintf("%v /%v", httpMethod, endpoint), ""); err != nil {
		return 0, fmt.Errorf("bunq: could not add signature: %v", err)
	}

	resp, err := c.HTTPClient.Do(req)
	if err != nil {
		return 0, fmt.Errorf("bunq: could not send HTTP request: %v", err)
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return 0, fmt.Errorf("bunq: request was unsuccessful: %v", decodeError(resp.Body))
	}

	var insResp installationResponse
	if err = json.NewDecoder(resp.Body).Decode(&insResp); err != nil {
		return 0, fmt.Errorf("bunq: could not decode HTTP response: %v", err)
	}

	if len(insResp.Response) == 0 {
		return 0, ErrInstallationNotFound
	}

	installation, err := insResp.installation()
	if err != nil {
		return 0, fmt.Errorf("bunq: could not parse API response: %v", err)
	}

	return installation.ID, nil
}

func (insResp *installationResponse) installation() (*Installation, error) {
	installation := &Installation{}
	for i := range insResp.Response {
		if insResp.Response[i].ID != nil {
			installation.ID = insResp.Response[i].ID.ID
			continue
		}
		if insResp.Response[i].Token != nil {
			installation.Token.ID = insResp.Response[i].Token.ID
			installation.Token.Token = insResp.Response[i].Token.Token
			installation.Token.CreatedAt = time.Time(insResp.Response[i].Token.Created)
			installation.Token.UpdatedAt = time.Time(insResp.Response[i].Token.Updated)
			continue
		}
		if insResp.Response[i].ServerPublicKey != nil {
			installation.ServerPublicKey = insResp.Response[i].ServerPublicKey.ServerPublicKey
		}
	}

	return installation, nil
}


<|start_filename|>installation_test.go<|end_filename|>
package bunq

import (
	"fmt"
	"net/http"
	"net/http/httptest"
	"reflect"
	"strings"
	"testing"
	"time"
)

func TestCreateInstallation(t *testing.T) {
	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintln(w, `{"Response":[{"Id":{"id":4971}},{"Token":{"id":1<PASSWORD>,"created":"2017-03-11 12:40:03.613887","updated":"2017-03-11 12:40:03.613887","token":"2237d5026c1d70121ac6fef73bbe937c1a8bc7340ef0acd253d6d9f85a6dbfda"}},{"ServerPublicKey":{"server_public_key":"-----<KEY>"}}]}`)
	}))
	defer ts.Close()

	client := NewClient()
	client.BaseURL = ts.URL

	privKey := `-----<KEY>`
	r := strings.NewReader(privKey)
	if err := client.SetPrivateKey(r); err != nil {
		t.Fatal(err)
	}

	got, err := client.CreateInstallation()
	if err != nil {
		t.Fatal(err)
	}

	exp := &Installation{
		ID: 4971,
		Token: InstallationToken{
			ID:        11352,
			Token:     "<PASSWORD>",
			CreatedAt: time.Unix(1489236003, 613887000).UTC(),
			UpdatedAt: time.Unix(1489236003, 613887000).UTC(),
		},
		ServerPublicKey: `-----<KEY>
-----END PUBLIC KEY-----
`,
	}

	if eq := reflect.DeepEqual(exp, got); !eq {
		t.Errorf("Expected: `%#v`, got: `%#v`", exp, got)
	}
}

func TestGetInstallation(t *testing.T) {
	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintln(w, `{"Response":[{"Id":{"id":12}}]}`)
	}))
	defer ts.Close()

	client := NewClient()
	client.BaseURL = ts.URL

	privKey := `-----<KEY>
-----END RSA PRIVATE KEY-----`
	r := strings.NewReader(privKey)
	if err := client.SetPrivateKey(r); err != nil {
		t.Fatal(err)
	}

	got, err := client.GetInstallation(42)
	if err != nil {
		t.Fatal(err)
	}

	exp := &Installation{
		ID: 12,
	}

	if eq := reflect.DeepEqual(exp, got); !eq {
		t.Errorf("Expected: `%#v`, got: `%#v`", exp, got)
	}
}

func TestGetInstallationID(t *testing.T) {
	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintln(w, `{"Response":[{"Id":{"id":72}}]}`)
	}))
	defer ts.Close()

	client := NewClient()
	client.BaseURL = ts.URL

	privKey := `-----<KEY>
-----END RSA PRIVATE KEY-----`
	r := strings.NewReader(privKey)
	if err := client.SetPrivateKey(r); err != nil {
		t.Fatal(err)
	}

	exp := 72
	got, err := client.GetInstallationID()
	if err != nil {
		t.Fatal(err)
	}

	if eq := reflect.DeepEqual(exp, got); !eq {
		t.Errorf("Expected: `%#v`, got: `%#v`", exp, got)
	}
}


<|start_filename|>device_server.go<|end_filename|>
package bunq

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"net"
	"net/http"
	"strconv"
	"time"
)

type deviceServerResponse struct {
	Response []struct {
		ID *struct {
			ID int `json:"id"`
		} `json:"Id,omitempty"`
		DeviceServer *struct {
			ID          int    `json:"id"`
			Created     Time   `json:"created"`
			Updated     Time   `json:"updated"`
			Description string `json:"description"`
			IP          string `json:"ip"`
			Status      string `json:"status"`
		} `json:"DeviceServer,omitempty"`
	} `json:"Response"`
}

// A DeviceServer represents a DeviceServe at the bunq API.
type DeviceServer struct {
	ID          int
	CreatedAt   time.Time
	UpdatedAt   time.Time
	Description string
	IP          net.IP
	Status      string
}

// ErrDeviceServerNotFound is returned when a single DeviceServer resource was
// not found.
var ErrDeviceServerNotFound = errors.New("device server not found")

// CreateDeviceServer creates a DeviceServer resource at the bunq API.
func (c *Client) CreateDeviceServer(description string, permittedIPs []net.IP) (*DeviceServer, error) {
	var ips []string
	for i := range permittedIPs {
		ips = append(ips, permittedIPs[i].String())
	}
	body := struct {
		Description  string   `json:"description"`
		Secret       string   `json:"secret"`
		PermittedIPs []string `json:"permitted_ips,omitempty"`
	}{description, c.APIKey, ips}

	bodyJSON, err := json.Marshal(body)
	if err != nil {
		return nil, fmt.Errorf("bunq: could not encode request body into JSON: %v", err)
	}

	httpMethod := http.MethodPost
	endpoint := apiVersion + "/device-server"
	req, err := http.NewRequest(httpMethod, fmt.Sprintf("%v/%v", c.BaseURL, endpoint), bytes.NewReader(bodyJSON))
	if err != nil {
		return nil, fmt.Errorf("bunq: could not create new request: %v", err)
	}
	setCommonHeaders(req)
	req.Header.Set("X-Bunq-Client-Authentication", c.Token)
	if err = c.addSignature(req, fmt.Sprintf("%v /%v", httpMethod, endpoint), string(bodyJSON)); err != nil {
		return nil, fmt.Errorf("bunq: could not add signature: %v", err)
	}

	resp, err := c.HTTPClient.Do(req)
	if err != nil {
		return nil, fmt.Errorf("bunq: could not send HTTP request: %v", err)
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("bunq: request was unsuccessful: %v", decodeError(resp.Body))
	}

	var dsr deviceServerResponse
	if err = json.NewDecoder(resp.Body).Decode(&dsr); err != nil {
		return nil, fmt.Errorf("bunq: could not decode HTTP response: %v", err)
	}

	deviceServers, err := dsr.deviceServers()
	if err != nil {
		return nil, fmt.Errorf("bunq: could not parse API response: %v", err)
	}
	if len(deviceServers) == 0 {
		return nil, errors.New("bunq: api response did not contain results")
	}

	return deviceServers[0], nil
}

// GetDeviceServer gets a DeviceServer resource at the bunq API.
func (c *Client) GetDeviceServer(id int) (*DeviceServer, error) {
	httpMethod := http.MethodGet
	endpoint := apiVersion + "/device-server/" + strconv.Itoa(id)
	req, err := http.NewRequest(httpMethod, fmt.Sprintf("%v/%v", c.BaseURL, endpoint), nil)
	if err != nil {
		return nil, fmt.Errorf("bunq: could not create new request: %v", err)
	}
	setCommonHeaders(req)
	req.Header.Set("X-Bunq-Client-Authentication", c.Token)
	if err = c.addSignature(req, fmt.Sprintf("%v /%v", httpMethod, endpoint), ""); err != nil {
		return nil, fmt.Errorf("bunq: could not add signature: %v", err)
	}

	resp, err := c.HTTPClient.Do(req)
	if err != nil {
		return nil, fmt.Errorf("bunq: could not send HTTP request: %v", err)
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("bunq: request was unsuccessful: %v", decodeError(resp.Body))
	}

	var dsr deviceServerResponse
	if err = json.NewDecoder(resp.Body).Decode(&dsr); err != nil {
		return nil, fmt.Errorf("bunq: could not decode HTTP response: %v", err)
	}

	deviceServers, err := dsr.deviceServers()
	if err != nil {
		return nil, fmt.Errorf("bunq: could not parse API response: %v", err)
	}
	if len(deviceServers) == 0 {
		return nil, ErrDeviceServerNotFound
	}

	return deviceServers[0], nil
}

// ListDeviceServers gets a list of DeviceServer resources at the bunq API.
func (c *Client) ListDeviceServers() ([]*DeviceServer, error) {
	httpMethod := http.MethodGet
	endpoint := apiVersion + "/device-server"
	req, err := http.NewRequest(httpMethod, fmt.Sprintf("%v/%v", c.BaseURL, endpoint), nil)
	if err != nil {
		return nil, fmt.Errorf("bunq: could not create new request: %v", err)
	}
	setCommonHeaders(req)
	req.Header.Set("X-Bunq-Client-Authentication", c.Token)
	if err = c.addSignature(req, fmt.Sprintf("%v /%v", httpMethod, endpoint), ""); err != nil {
		return nil, fmt.Errorf("bunq: could not add signature: %v", err)
	}

	resp, err := c.HTTPClient.Do(req)
	if err != nil {
		return nil, fmt.Errorf("bunq: could not send HTTP request: %v", err)
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("bunq: request was unsuccessful: %v", decodeError(resp.Body))
	}

	var dsr deviceServerResponse
	if err = json.NewDecoder(resp.Body).Decode(&dsr); err != nil {
		return nil, fmt.Errorf("bunq: could not decode HTTP response: %v", err)
	}

	deviceServers, err := dsr.deviceServers()
	if err != nil {
		return nil, fmt.Errorf("bunq: could not parse API response: %v", err)
	}

	return deviceServers, nil
}

func (dsr *deviceServerResponse) deviceServers() ([]*DeviceServer, error) {
	var deviceServers []*DeviceServer
	for i := range dsr.Response {
		deviceServer := &DeviceServer{}
		if dsr.Response[i].ID != nil {
			deviceServer.ID = dsr.Response[i].ID.ID
			deviceServers = append(deviceServers, deviceServer)
		}
		if dsr.Response[i].DeviceServer != nil {
			deviceServer.ID = dsr.Response[i].DeviceServer.ID
			deviceServer.Description = dsr.Response[i].DeviceServer.Description
			deviceServer.CreatedAt = time.Time(dsr.Response[i].DeviceServer.Created)
			deviceServer.UpdatedAt = time.Time(dsr.Response[i].DeviceServer.Updated)
			deviceServer.Status = dsr.Response[i].DeviceServer.Status
			deviceServer.IP = net.ParseIP(dsr.Response[i].DeviceServer.IP)
			deviceServers = append(deviceServers, deviceServer)
		}
	}

	return deviceServers, nil
}


<|start_filename|>device.go<|end_filename|>
package bunq

import (
	"encoding/json"
	"errors"
	"fmt"
	"net"
	"net/http"
	"strconv"
	"time"
)

type deviceResponse struct {
	Response []struct {
		DevicePhone *struct {
			ID          int    `json:"id"`
			Created     Time   `json:"created"`
			Updated     Time   `json:"updated"`
			Description string `json:"description"`
			PhoneNumber string `json:"phone_number"`
			OS          string `json:"os"`
			Status      string `json:"status"`
		} `json:"DevicePhone,omitempty"`
		DeviceServer *struct {
			ID          int    `json:"id"`
			Created     Time   `json:"created"`
			Updated     Time   `json:"updated"`
			Description string `json:"description"`
			IP          string `json:"ip"`
			Status      string `json:"status"`
		} `json:"DeviceServer,omitempty"`
	} `json:"Response"`
}

// A DevicePhone represents a Device at the bunq API.
type DevicePhone struct {
	ID          int
	CreatedAt   time.Time
	UpdatedAt   time.Time
	Description string
	PhoneNumber string
	OS          string
	Status      string
}

// ErrDeviceNotFound is returned when a single Device resource was
// not found.
var ErrDeviceNotFound = errors.New("device not found")

// GetDevice gets a Device resource at the bunq API.
func (c *Client) GetDevice(id int) (interface{}, error) {
	httpMethod := http.MethodGet
	endpoint := apiVersion + "/device/" + strconv.Itoa(id)
	req, err := http.NewRequest(httpMethod, fmt.Sprintf("%v/%v", c.BaseURL, endpoint), nil)
	if err != nil {
		return nil, fmt.Errorf("bunq: could not create new request: %v", err)
	}
	setCommonHeaders(req)
	req.Header.Set("X-Bunq-Client-Authentication", c.Token)
	if err = c.addSignature(req, fmt.Sprintf("%v /%v", httpMethod, endpoint), ""); err != nil {
		return nil, fmt.Errorf("bunq: could not add signature: %v", err)
	}

	resp, err := c.HTTPClient.Do(req)
	if err != nil {
		return nil, fmt.Errorf("bunq: could not send HTTP request: %v", err)
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("bunq: request was unsuccessful: %v", decodeError(resp.Body))
	}

	var devResp deviceResponse
	if err = json.NewDecoder(resp.Body).Decode(&devResp); err != nil {
		return nil, fmt.Errorf("bunq: could not decode HTTP response: %v", err)
	}

	devices, err := devResp.devices()
	if err != nil {
		return nil, fmt.Errorf("bunq: could not parse API response: %v", err)
	}
	if len(devices) == 0 {
		return nil, ErrDeviceNotFound
	}

	return devices[0], nil
}

// ListDevices gets a list of Device resources at the bunq API.
func (c *Client) ListDevices() ([]interface{}, error) {
	httpMethod := http.MethodGet
	endpoint := apiVersion + "/device"
	req, err := http.NewRequest(httpMethod, fmt.Sprintf("%v/%v", c.BaseURL, endpoint), nil)
	if err != nil {
		return nil, fmt.Errorf("bunq: could not create new request: %v", err)
	}
	setCommonHeaders(req)
	req.Header.Set("X-Bunq-Client-Authentication", c.Token)
	if err = c.addSignature(req, fmt.Sprintf("%v /%v", httpMethod, endpoint), ""); err != nil {
		return nil, fmt.Errorf("bunq: could not add signature: %v", err)
	}

	resp, err := c.HTTPClient.Do(req)
	if err != nil {
		return nil, fmt.Errorf("bunq: could not send HTTP request: %v", err)
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("bunq: request was unsuccessful: %v", decodeError(resp.Body))
	}

	var devResp deviceResponse
	if err = json.NewDecoder(resp.Body).Decode(&devResp); err != nil {
		return nil, fmt.Errorf("bunq: could not decode HTTP response: %v", err)
	}

	devices, err := devResp.devices()
	if err != nil {
		return nil, fmt.Errorf("bunq: could not parse API response: %v", err)
	}

	return devices, nil
}

func (devResp *deviceResponse) devices() ([]interface{}, error) {
	devices := make([]interface{}, len(devResp.Response))
	for i := range devResp.Response {
		if devResp.Response[i].DevicePhone != nil {
			device := DevicePhone{}
			device.ID = devResp.Response[i].DevicePhone.ID
			device.CreatedAt = time.Time(devResp.Response[i].DevicePhone.Created)
			device.UpdatedAt = time.Time(devResp.Response[i].DevicePhone.Updated)
			device.Description = devResp.Response[i].DevicePhone.Description
			device.PhoneNumber = devResp.Response[i].DevicePhone.PhoneNumber
			device.OS = devResp.Response[i].DevicePhone.OS
			device.Status = devResp.Response[i].DevicePhone.Status
			devices[i] = device
		}
		if devResp.Response[i].DeviceServer != nil {
			device := DeviceServer{}
			device.ID = devResp.Response[i].DeviceServer.ID
			device.Description = devResp.Response[i].DeviceServer.Description
			device.CreatedAt = time.Time(devResp.Response[i].DeviceServer.Created)
			device.UpdatedAt = time.Time(devResp.Response[i].DeviceServer.Updated)
			device.Status = devResp.Response[i].DeviceServer.Status
			device.IP = net.ParseIP(devResp.Response[i].DeviceServer.IP)
			devices[i] = device
		}
	}

	return devices, nil
}


<|start_filename|>time.go<|end_filename|>
package bunq

import (
	"fmt"
	"strings"
	"time"
)

// Time represents a parsed timestamp returned by the bunq API.
type Time time.Time

// UnmarshalJSON is being used to parse timestamps from the bunq API.
// Bunq timestamps come into this format: "2015-06-13 23:19:16.215235"
func (ariTime *Time) UnmarshalJSON(input []byte) error {
	strInput := string(input)
	strInput = strings.Trim(strInput, `"`)
	var pattern string
	if len(strInput) == 10 {
		pattern = "2006-01-02"
	} else {
		pattern = "2006-01-02 15:04:05.000000"
	}
	newTime, err := time.Parse(pattern, strInput)
	if err != nil {
		return fmt.Errorf("Error parsing Time: %s", err)
	}
	*ariTime = Time(newTime)

	return nil
}

// MarshalText implements the encoding.MarshalText interface for custom time type.
func (ariTime *Time) MarshalText() ([]byte, error) {
	t := time.Time(*ariTime)
	return []byte(t.Format("2006-01-02 15:04:05.000000")), nil
}


<|start_filename|>bunq.go<|end_filename|>
package bunq

import (
	"crypto/rsa"
	"net/http"

	"github.com/satori/go.uuid"
)

const (
	baseURL       = "https://api.bunq.com"
	apiVersion    = "v1"
	clientVersion = "1.0.0"
	userAgent     = "go-bunq/" + clientVersion
)

// Client is the API client for the public bunq API.
type Client struct {
	HTTPClient *http.Client
	BaseURL    string
	APIKey     string
	Token      string
	PrivateKey *rsa.PrivateKey
}

// NewClient returns a new Client.
func NewClient() *Client {
	return &Client{
		HTTPClient: http.DefaultClient,
		BaseURL:    baseURL,
	}
}

func setCommonHeaders(r *http.Request) {
	r.Header.Set("Cache-Control", "no-cache")
	r.Header.Set("User-Agent", userAgent)
	r.Header.Set("X-Bunq-Client-Request-Id", uuid.NewV4().String())
	r.Header.Set("X-Bunq-Geolocation", "0 0 0 0 NL")
	r.Header.Set("X-Bunq-Language", "en_US")
	r.Header.Set("X-Bunq-Region", "en_US")
}


<|start_filename|>device_test.go<|end_filename|>
package bunq

import (
	"fmt"
	"net"
	"net/http"
	"net/http/httptest"
	"reflect"
	"strings"
	"testing"
	"time"
)

func TestGetDevice(t *testing.T) {
	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintln(w, `{"Response":[{"DevicePhone":{"id":42,"created":"2015-06-13 23:19:16.215235","updated":"2015-06-30 09:12:31.981573","description":"Kees' iPhone","phone_number":"+31612345678","os":"IOS","status":"ACTIVE"}}]}`)
	}))
	defer ts.Close()

	client := NewClient()
	client.BaseURL = ts.URL

	privKey := `-----BEGIN RSA PRIVATE KEY-----
<KEY>
-----END RSA PRIVATE KEY-----`
	r := strings.NewReader(privKey)
	if err := client.SetPrivateKey(r); err != nil {
		t.Fatal(err)
	}

	got, err := client.GetDevice(42)
	if err != nil {
		t.Fatal(err)
	}

	exp := DevicePhone{
		ID:          42,
		CreatedAt:   time.Unix(1434237556, 215235000).UTC(),
		UpdatedAt:   time.Unix(1435655551, 981573000).UTC(),
		Description: "Kees' iPhone",
		PhoneNumber: "+31612345678",
		OS:          "IOS",
		Status:      "ACTIVE",
	}

	if eq := reflect.DeepEqual(exp, got); !eq {
		t.Errorf("Expected: `%#v`, got: `%#v`", exp, got)
	}
}

func TestListDevices(t *testing.T) {
	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintln(w, `{"Response":[{"DevicePhone":{"id":42,"created":"2015-06-13 23:19:16.215235","updated":"2015-06-30 09:12:31.981573","description":"Kees' iPhone","phone_number":"+31612345678","os":"IOS","status":"ACTIVE"}},{"DeviceServer":{"id":42,"created":"2015-06-13 23:19:16.215235","updated":"2015-06-30 09:12:31.981573","description":"Mainframe23 in Amsterdam","ip":"255.255.255.255","status":"ACTIVE"}}]}`)
	}))
	defer ts.Close()

	client := NewClient()
	client.BaseURL = ts.URL

	privKey := `-----<KEY>
<KEY>
-----END RSA PRIVATE KEY-----`
	r := strings.NewReader(privKey)
	if err := client.SetPrivateKey(r); err != nil {
		t.Fatal(err)
	}

	got, err := client.ListDevices()
	if err != nil {
		t.Fatal(err)
	}

	exp := []interface{}{
		DevicePhone{
			ID:          42,
			CreatedAt:   time.Unix(1434237556, 215235000).UTC(),
			UpdatedAt:   time.Unix(1435655551, 981573000).UTC(),
			Description: "Kees' iPhone",
			PhoneNumber: "+31612345678",
			OS:          "IOS",
			Status:      "ACTIVE",
		},
		DeviceServer{
			ID:          42,
			CreatedAt:   time.Unix(1434237556, 215235000).UTC(),
			UpdatedAt:   time.Unix(1435655551, 981573000).UTC(),
			Description: "Mainframe23 in Amsterdam",
			IP:          net.IPv4bcast,
			Status:      "ACTIVE",
		},
	}

	if eq := reflect.DeepEqual(exp, got); !eq {
		t.Errorf("Expected: `%#v`, got: `%#v`", exp, got)
	}
}


<|start_filename|>device_server_test.go<|end_filename|>
package bunq

import (
	"fmt"
	"net"
	"net/http"
	"net/http/httptest"
	"reflect"
	"strings"
	"testing"
	"time"
)

func TestCreateDeviceServer(t *testing.T) {
	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintln(w, `{"Response":[{"Id":{"id":83}}]}`)
	}))
	defer ts.Close()

	client := NewClient()
	client.BaseURL = ts.URL

	privKey := `-----<KEY>
-----END RSA PRIVATE KEY-----`
	r := strings.NewReader(privKey)
	if err := client.SetPrivateKey(r); err != nil {
		t.Fatal(err)
	}

	got, err := client.CreateDeviceServer("Foobar", []net.IP{})
	if err != nil {
		t.Fatal(err)
	}

	exp := &DeviceServer{
		ID: 83,
	}

	if eq := reflect.DeepEqual(exp, got); !eq {
		t.Errorf("Expected: `%#v`, got: `%#v`", exp, got)
	}
}

func TestGetDeviceServer(t *testing.T) {
	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintln(w, `{"Response":[{"DeviceServer":{"id":42,"created":"2015-06-13 23:19:16.215235","updated":"2015-06-30 09:12:31.981573","description":"Mainframe23 in Amsterdam","ip":"255.255.255.255","status":"ACTIVE"}}]}`)
	}))
	defer ts.Close()

	client := NewClient()
	client.BaseURL = ts.URL

	privKey := `-----<KEY>`
	r := strings.NewReader(privKey)
	if err := client.SetPrivateKey(r); err != nil {
		t.Fatal(err)
	}

	got, err := client.GetDeviceServer(42)
	if err != nil {
		t.Fatal(err)
	}

	exp := &DeviceServer{
		ID:          42,
		CreatedAt:   time.Unix(1434237556, 215235000).UTC(),
		UpdatedAt:   time.Unix(1435655551, 981573000).UTC(),
		Description: "Mainframe23 in Amsterdam",
		IP:          net.IPv4bcast,
		Status:      "ACTIVE",
	}

	if eq := reflect.DeepEqual(exp, got); !eq {
		t.Errorf("Expected: `%#v`, got: `%#v`", exp, got)
	}
}

func TestListDeviceServers(t *testing.T) {
	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintln(w, `{"Response":[{"DeviceServer":{"id":42,"created":"2015-06-13 23:19:16.215235","updated":"2015-06-30 09:12:31.981573","description":"Mainframe23 in Amsterdam","ip":"255.255.255.255","status":"ACTIVE"}},{"DeviceServer":{"id":43,"created":"2015-06-13 23:19:16.215235","updated":"2015-06-30 09:12:31.981573","description":"Mainframe24 in Amsterdam","ip":"192.168.127.12","status":"ACTIVE"}}]}`)
	}))
	defer ts.Close()

	client := NewClient()
	client.BaseURL = ts.URL

	privKey := `-----<KEY>
-----END RSA PRIVATE KEY-----`
	r := strings.NewReader(privKey)
	if err := client.SetPrivateKey(r); err != nil {
		t.Fatal(err)
	}

	got, err := client.ListDeviceServers()
	if err != nil {
		t.Fatal(err)
	}

	exp := []*DeviceServer{
		&DeviceServer{
			ID:          42,
			CreatedAt:   time.Unix(1434237556, 215235000).UTC(),
			UpdatedAt:   time.Unix(1435655551, 981573000).UTC(),
			Description: "Mainframe23 in Amsterdam",
			IP:          net.IPv4bcast,
			Status:      "ACTIVE",
		},
		&DeviceServer{
			ID:          43,
			CreatedAt:   time.Unix(1434237556, 215235000).UTC(),
			UpdatedAt:   time.Unix(1435655551, 981573000).UTC(),
			Description: "Mainframe24 in Amsterdam",
			IP:          net.ParseIP("1.2.3.4"),
			Status:      "ACTIVE",
		},
	}

	if eq := reflect.DeepEqual(exp, got); !eq {
		t.Errorf("Expected: `%#v`, got: `%#v`", exp, got)
	}
}


<|start_filename|>crypto.go<|end_filename|>
package bunq

import (
	"crypto"
	"crypto/rand"
	"crypto/rsa"
	"crypto/sha256"
	"crypto/x509"
	"encoding/base64"
	"encoding/pem"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"sort"
)

type header struct {
	Key   string
	Value string
}

// byKey implements sort.Interface for []header based on the key field.
type byKey []header

func (k byKey) Len() int           { return len(k) }
func (k byKey) Swap(i, j int)      { k[i], k[j] = k[j], k[i] }
func (k byKey) Less(i, j int) bool { return k[i].Key < k[j].Key }

func (c *Client) addSignature(req *http.Request, endpoint, body string) error {
	if c.PrivateKey == nil {
		return errors.New("bunq: private key cannot be nil")
	}
	var headers []header
	for key, val := range req.Header {
		for j := range val {
			headers = append(headers, header{
				Key:   key,
				Value: val[j],
			})
		}
	}
	sort.Sort(byKey(headers))

	message := endpoint + "\n"
	for i := range headers {
		if string([]byte(headers[i].Key)[:7]) == "X-Bunq-" || headers[i].Key == "User-Agent" || headers[i].Key == "Cache-Control" {
			message += headers[i].Key + ": " + headers[i].Value + "\n"
		}
	}
	message += "\n" + body

	h := sha256.New()
	_, err := h.Write([]byte(message))
	if err != nil {
		return err
	}

	signature, err := rsa.SignPKCS1v15(rand.Reader, c.PrivateKey, crypto.SHA256, h.Sum(nil))
	if err != nil {
		return err
	}

	req.Header.Set("X-Bunq-Client-Signature", base64.StdEncoding.EncodeToString(signature))

	return nil
}

// SetPrivateKey reads and parses private key data into a private key.
func (c *Client) SetPrivateKey(r io.Reader) error {
	pemData, err := ioutil.ReadAll(r)
	if err != nil {
		return fmt.Errorf("bunq: error reading PEM data: %v", err)
	}

	pemBlock, _ := pem.Decode(pemData)
	if pemBlock == nil {
		return errors.New("bunq: no PEM block found in data")
	}
	if pemBlock.Type != "RSA PRIVATE KEY" {
		return errors.New("bunq: invalid key type found, expected `RSA PRIVATE KEY`")
	}
	privKey, err := x509.ParsePKCS1PrivateKey(pemBlock.Bytes)
	if err != nil {
		return fmt.Errorf("bunq: error parsing PEM block into private key")
	}
	c.PrivateKey = privKey

	return nil
}

func (c *Client) publicKey() ([]byte, error) {
	if c.PrivateKey == nil {
		return nil, errors.New("private key cannot be nil")
	}
	pubKeyDer, err := x509.MarshalPKIXPublicKey(c.PrivateKey.Public())
	if err != nil {
		return nil, fmt.Errorf("error serializing public key to DER-encoded PKIX format: %v", err)
	}
	pubKeyPemBlock := pem.Block{
		Type:    "PUBLIC KEY",
		Headers: nil,
		Bytes:   pubKeyDer,
	}

	return pem.EncodeToMemory(&pubKeyPemBlock), nil
}


<|start_filename|>examples/register/register.go<|end_filename|>
package main

import (
	"fmt"
	"net"
	"os"

	"github.com/dstotijn/go-bunq"
)

func main() {
	client := bunq.NewClient()
	client.APIKey = os.Getenv("BUNQ_API_KEY")
	client.BaseURL = "https://sandbox.public.api.bunq.com"

	file, err := os.Open("/Users/david/.keys/bunq.key")
	if err != nil {
		fmt.Fprintf(os.Stderr, "%v\n", err)
		os.Exit(1)
	}
	if err := client.SetPrivateKey(file); err != nil {
		fmt.Fprintf(os.Stderr, "%v\n", err)
		os.Exit(1)
	}

	fmt.Printf("* Creating installation...\n\n")
	installation, err := client.CreateInstallation()
	if err != nil {
		fmt.Fprintf(os.Stderr, "%v\n", err)
		os.Exit(1)
	}
	fmt.Printf("Created installation: %#v\n", installation)

	fmt.Printf("\n* Creating DeviceServer...\n\n")
	client.Token = installation.Token.Token
	deviceServer, err := client.CreateDeviceServer("Foobar", []net.IP{})
	if err != nil {
		fmt.Fprintf(os.Stderr, "%v\n", err)
		os.Exit(1)
	}
	fmt.Printf("Created DeviceServer: %#v\n", deviceServer)

	fmt.Printf("\n* Getting DeviceServer...\n\n")
	deviceServer, err = client.GetDeviceServer(deviceServer.ID)
	if err != nil {
		fmt.Fprintf(os.Stderr, "%v\n", err)
		os.Exit(1)
	}
	fmt.Printf("Retrieved DeviceServer: %#v\n", deviceServer)

	fmt.Printf("\n* Creating Session...\n\n")
	session, err := client.CreateSession()
	if err != nil {
		fmt.Fprintf(os.Stderr, "%v\n", err)
		os.Exit(1)
	}
	fmt.Printf("Created Session: %#v\n", session)
}


<|start_filename|>user_company.go<|end_filename|>
package bunq

type Alias struct {
	Type  string `json:"type"`
	Value string `json:"value"`
	Name  string `json:"name"`
}

type AvatarImage struct {
	AttachmentPublicUUID string `json:"attachment_public_uuid"`
	ContentType          string `json:"content_type"`
	Height               int    `json:"height"`
	Width                int    `json:"width"`
}
type UBO struct {
	Name        string `json:"name"`
	DateOfBirth Time   `json:"date_of_birth"`
	Nationality string `json:"nationality"`
}
type NotificationFilter struct {
	NotificationDeliveryMethod string `json:"notification_delivery_method"`
	NotificationTarget         string `json:"notification_target"`
	Category                   string `json:"category"`
}

type Address struct {
	Street      string `json:"street"`
	HouseNumber string `json:"house_number"`
	POBox       string `json:"po_box"`
	PostalCode  string `json:"postal_code"`
	City        string `json:"city"`
	Country     string `json:"country"`
}

type Avatar struct {
	UUID       string        `json:"uuid"`
	AnchorUUID string        `json:"anchor_uuid"`
	Image      []AvatarImage `json:"image"`
}

type DirectorAlias struct {
	UUID           string `json:"uuid"`
	Avatar         Avatar `json:"avatar"`
	PublicNickName string `json:"public_nick_name"`
	DisplayName    string `json:"display_name"`
	Country        string `json:"country"`
}

type Limit struct {
	Value    string `json:"value"`
	Currency string `json:"currency"`
}

type UserCompany struct {
	ID                                 int                  `json:"id"`
	CreatedAt                          Time                 `json:"created"`
	UpdatedAt                          Time                 `json:"updated"`
	PublicUUID                         string               `json:"public_uuid"`
	Name                               string               `json:"name"`
	DisplayName                        string               `json:"display_name"`
	PublicNickName                     string               `json:"public_nick_name"`
	Alias                              []Alias              `json:"alias"`
	ChamberOfCommerceNumber            string               `json:"chamber_of_commerce_number"`
	TypeOfBusinessEntity               string               `json:"type_of_business_entity"`
	SectorOfIndustry                   string               `json:"sector_of_industry"`
	CounterBankIBAN                    string               `json:"counter_bank_IBAN"`
	Avatar                             Avatar               `json:"avatar"`
	AddressMain                        Address              `json:"address_main"`
	AddressPostal                      Address              `json:"address_postal"`
	VersionTermsOfService              string               `json:"version_terms_of_service"`
	DirectorAlias                      DirectorAlias        `json:"director_alias"`
	Language                           string               `json:"language"`
	Region                             string               `json:"region"`
	UBO                                []UBO                `json:"UBO"`
	Status                             string               `json:"status"`
	SubStatus                          string               `json:"sub_status"`
	SessionTimeout                     int                  `json:"session_timeout"`
	DailyLimitWithoutConfirmationLogin Limit                `json:"daily_limit_without_confirmation_login"`
	NotificationFilters                []NotificationFilter `json:"notification_filters"`
}
