From df837bff89e9db7441ba543d7eb24ac581f953d1 Mon Sep 17 00:00:00 2001 From: Imre 'Rover' Racz Date: Sat, 9 Mar 2019 18:32:37 +0200 Subject: [PATCH 01/14] feat(app): App related calls --- .gitignore | 2 + Gopkg.lock | 33 +++++++ Gopkg.toml | 34 ++++++++ Makefile | 7 ++ paylike.go | 223 ++++++++++++++++++++++++++++++++++++++++++++++++ paylike_test.go | 59 +++++++++++++ 6 files changed, 358 insertions(+) create mode 100644 Gopkg.lock create mode 100644 Gopkg.toml create mode 100644 Makefile create mode 100644 paylike.go create mode 100644 paylike_test.go diff --git a/.gitignore b/.gitignore index f1c181e..9b60f02 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,5 @@ # Output of the go coverage tool, specifically when used with LiteIDE *.out + +vendor diff --git a/Gopkg.lock b/Gopkg.lock new file mode 100644 index 0000000..316f347 --- /dev/null +++ b/Gopkg.lock @@ -0,0 +1,33 @@ +# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. + + +[[projects]] + digest = "1:ffe9824d294da03b391f44e1ae8281281b4afc1bdaa9588c9097785e3af10cec" + name = "github.com/davecgh/go-spew" + packages = ["spew"] + pruneopts = "UT" + revision = "8991bc29aa16c548c550c7ff78260e27b9ab7c73" + version = "v1.1.1" + +[[projects]] + digest = "1:0028cb19b2e4c3112225cd871870f2d9cf49b9b4276531f03438a88e94be86fe" + name = "github.com/pmezard/go-difflib" + packages = ["difflib"] + pruneopts = "UT" + revision = "792786c7400a136282c1664665ae0a8db921c6c2" + version = "v1.0.0" + +[[projects]] + digest = "1:972c2427413d41a1e06ca4897e8528e5a1622894050e2f527b38ddf0f343f759" + name = "github.com/stretchr/testify" + packages = ["assert"] + pruneopts = "UT" + revision = "ffdc059bfe9ce6a4e144ba849dbedead332c6053" + version = "v1.3.0" + +[solve-meta] + analyzer-name = "dep" + analyzer-version = 1 + input-imports = ["github.com/stretchr/testify/assert"] + solver-name = "gps-cdcl" + solver-version = 1 diff --git a/Gopkg.toml b/Gopkg.toml new file mode 100644 index 0000000..b426f1c --- /dev/null +++ b/Gopkg.toml @@ -0,0 +1,34 @@ +# Gopkg.toml example +# +# Refer to https://golang.github.io/dep/docs/Gopkg.toml.html +# for detailed Gopkg.toml documentation. +# +# required = ["github.com/user/thing/cmd/thing"] +# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"] +# +# [[constraint]] +# name = "github.com/user/project" +# version = "1.0.0" +# +# [[constraint]] +# name = "github.com/user/project2" +# branch = "dev" +# source = "github.com/myfork/project2" +# +# [[override]] +# name = "github.com/x/y" +# version = "2.4.0" +# +# [prune] +# non-go = false +# go-tests = true +# unused-packages = true + + +[prune] + go-tests = true + unused-packages = true + +[[constraint]] + name = "github.com/stretchr/testify" + version = "1.3.0" diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..563c3df --- /dev/null +++ b/Makefile @@ -0,0 +1,7 @@ +test: ## Runs tests + go test ./... +.PHONY: help +help: + @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' + +.DEFAULT_GOAL := help diff --git a/paylike.go b/paylike.go new file mode 100644 index 0000000..63cbdcd --- /dev/null +++ b/paylike.go @@ -0,0 +1,223 @@ +package paylike + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "io/ioutil" + "net/http" +) + +// Client describes all information regarding the API +type Client struct { + Key string + client *http.Client + baseAPI string +} + +// App describes information about the application +type App struct { + ID string + Name string + Key string +} + +// Identity describes information about the current application that has +// been created +type Identity struct { + ID string + Name string + Created string +} + +// MerchantCreateDTO describes options for creating a merchant +type MerchantCreateDTO struct { + Name string `json:"name,omitempty"` // optional + Currency string `json:"currency"` // required, three letter ISO + Test bool `json:"test,omitempty"` // optional, defaults to false + Email string `json:"email"` // required, contact email + Website string `json:"website"` // required, website with implementation + Descriptor string `json:"descriptor"` // required, text on client bank statements + Company *MerchantCompany `json:"company"` // required + Bank *MerchantBank `json:"bank,omitempty"` // optional +} + +// Amount ... +type Amount struct { + Currency string + Amount float64 +} + +// MerchantTransfer ... +type MerchantTransfer struct { + ToCard Pricing +} + +// Pricing ... +type Pricing struct { + Rate float64 + Flat Amount + Dispute Amount +} + +// MerchantPricing ... +type MerchantPricing struct { + Pricing + Transfer MerchantTransfer +} + +// MerchantTDS ... +type MerchantTDS struct { + Mode string +} + +// Merchant ... +type Merchant struct { + ID string + Company MerchantCompany + Claim MerchantClaim + Pricing MerchantPricing + Currency string + Email string + TDS MerchantTDS + Key string + Bank MerchantBank + Created string + Test bool + Descriptor string + Website string + Balance float64 +} + +// MerchantClaim ... +type MerchantClaim struct { + CanChargeCard bool + CanSaveCard bool + CanTransferToCard bool + CanCapture bool + CanRefund bool + CanVoid bool +} + +// MerchantCompany ... +type MerchantCompany struct { + Country string `json:"country"` // required, ISO 3166 code (e.g. DK) + Number string `json:"number,omitempty"` // optional, registration number ("CVR" in Denmark) +} + +// MerchantBank ... +type MerchantBank struct { + Iban string `json:"iban,omitempty"` // optional, (format: XX00000000, XX is country code, length varies) +} + +// NewClient creates a new client +func NewClient(key string) *Client { + return &Client{key, &http.Client{}, "https://api.paylike.io"} +} + +// SetKey provides an elegent way to deal with +// setting the key and calling other methods after that +func (c *Client) SetKey(key string) *Client { + c.Key = key + return c +} + +// CreateApp creates a new application +// https://github.com/paylike/api-docs#create-an-app +func (c Client) CreateApp() (*App, error) { + return c.createApp(nil) +} + +// CreateAppWithName creates a new application with the given name +// https://github.com/paylike/api-docs#create-an-app +func (c Client) CreateAppWithName(name string) (*App, error) { + return c.createApp( + bytes.NewBuffer([]byte(fmt.Sprintf(`{"name":"%s"}`, name))), + ) +} + +// GetCurrentApp is to fetch information about the current application +// https://api.paylike.io/me +func (c Client) GetCurrentApp() (*Identity, error) { + return c.getCurrentApp() +} + +// CreateMerchant ... +// https://github.com/paylike/api-docs#create-a-merchant +func (c Client) CreateMerchant(dto MerchantCreateDTO) (*Merchant, error) { + b, err := json.Marshal(dto) + if err != nil { + return nil, err + } + return c.createMerchant(bytes.NewBuffer(b)) +} + +// getURL is to build the base API url along with the given dynamic route path +func (c Client) getURL(url string) string { + return fmt.Sprintf("%s%s", c.baseAPI, url) +} + +// createApp handles the API calls towards Paylike API +func (c Client) createApp(body io.Reader) (*App, error) { + resp, err := c.client.Post(c.getURL("/apps"), "application/json", body) + if err != nil { + return nil, err + } + b, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, err + } + var marshalled map[string]*App + if err := json.Unmarshal(b, &marshalled); err != nil { + return nil, err + } + return marshalled["app"], nil +} + +// getCurrentApp executes the request for fetching the current application +// along with marshalling the response +func (c Client) getCurrentApp() (*Identity, error) { + req, err := http.NewRequest("GET", c.getURL("/me"), nil) + if err != nil { + return nil, err + } + req.SetBasicAuth("", c.Key) + resp, err := c.client.Do(req) + if err != nil { + return nil, err + } + b, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, err + } + var marshalled map[string]*Identity + if err := json.Unmarshal(b, &marshalled); err != nil { + return nil, err + } + return marshalled["identity"], nil +} + +// createMerchant handles the underlying logic of executing the API requests +// towards the merchant creation API +func (c Client) createMerchant(body io.Reader) (*Merchant, error) { + req, err := http.NewRequest("POST", c.getURL("/merchants"), body) + if err != nil { + return nil, err + } + req.SetBasicAuth("", c.Key) + req.Header.Set("Content-Type", "application/json") + resp, err := c.client.Do(req) + if err != nil { + return nil, err + } + b, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, err + } + var marshalled map[string]*Merchant + if err := json.Unmarshal(b, &marshalled); err != nil { + return nil, err + } + return marshalled["merchant"], nil +} diff --git a/paylike_test.go b/paylike_test.go new file mode 100644 index 0000000..2b6877d --- /dev/null +++ b/paylike_test.go @@ -0,0 +1,59 @@ +package paylike + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +const TestKey = "4c8453c3-8285-4984-ab72-216e324372e6" +const TestEmail = "john@example.com" +const TestSite = "https://example.com" + +func TestCreateApp(t *testing.T) { + client := NewClient(TestKey) + app, err := client.CreateApp() + assert.Nil(t, err) + assert.NotEmpty(t, app) + assert.Empty(t, app.Name) +} + +func TestCreateAppWithName(t *testing.T) { + client := NewClient(TestKey) + app, err := client.CreateAppWithName("Macilaci") + assert.Nil(t, err) + assert.NotEmpty(t, app) + assert.Equal(t, "Macilaci", app.Name) +} + +func TestGetApp(t *testing.T) { + client := NewClient(TestKey) + app, err := client.CreateApp() + assert.Nil(t, err) + assert.NotEmpty(t, app) + + identity, err := client.SetKey(app.Key).GetCurrentApp() + assert.Nil(t, err) + assert.NotEmpty(t, identity) +} + +func TestCreateMerchant(t *testing.T) { + client := NewClient(TestKey) + app, err := client.CreateApp() + assert.Nil(t, err) + assert.NotEmpty(t, app) + + dto := MerchantCreateDTO{ + Test: true, + Currency: "HUF", + Email: TestEmail, + Website: TestSite, + Descriptor: "1234567897891234", + Company: &MerchantCompany{ + Country: "HU", + }, + } + merchant, err := client.SetKey(app.Key).CreateMerchant(dto) + assert.Nil(t, err) + assert.NotEmpty(t, merchant) +} From 7167e6b2244c6a2996a38ee4c694279fc5278f5e Mon Sep 17 00:00:00 2001 From: Imre 'Rover' Racz Date: Tue, 12 Mar 2019 23:32:35 +0100 Subject: [PATCH 02/14] feat(merchant): Fetching works correctly --- paylike.go | 79 ++++++++++++++++++++++++++++++++++++++++++++++++- paylike_test.go | 27 +++++++++++++++++ 2 files changed, 105 insertions(+), 1 deletion(-) diff --git a/paylike.go b/paylike.go index 63cbdcd..ce59482 100644 --- a/paylike.go +++ b/paylike.go @@ -6,7 +6,10 @@ import ( "fmt" "io" "io/ioutil" + "log" "net/http" + + "github.com/davecgh/go-spew/spew" ) // Client describes all information regarding the API @@ -153,6 +156,18 @@ func (c Client) CreateMerchant(dto MerchantCreateDTO) (*Merchant, error) { return c.createMerchant(bytes.NewBuffer(b)) } +// GetMerchant ... +// https://github.com/paylike/api-docs#fetch-a-merchant +func (c Client) GetMerchant(id string) (*Merchant, error) { + return c.getMerchant(id) +} + +// FetchMerchants ... +// https://github.com/paylike/api-docs#fetch-all-merchants +func (c Client) FetchMerchants(appID string, limit int) ([]*Merchant, error) { + return c.fetchMerchants(appID, limit) +} + // getURL is to build the base API url along with the given dynamic route path func (c Client) getURL(url string) string { return fmt.Sprintf("%s%s", c.baseAPI, url) @@ -164,6 +179,7 @@ func (c Client) createApp(body io.Reader) (*App, error) { if err != nil { return nil, err } + defer resp.Body.Close() b, err := ioutil.ReadAll(resp.Body) if err != nil { return nil, err @@ -182,11 +198,12 @@ func (c Client) getCurrentApp() (*Identity, error) { if err != nil { return nil, err } - req.SetBasicAuth("", c.Key) + c.setContentHeader(req) resp, err := c.client.Do(req) if err != nil { return nil, err } + defer resp.Body.Close() b, err := ioutil.ReadAll(resp.Body) if err != nil { return nil, err @@ -205,12 +222,63 @@ func (c Client) createMerchant(body io.Reader) (*Merchant, error) { if err != nil { return nil, err } + c.setContentHeader(req) + resp, err := c.client.Do(req) + if err != nil { + return nil, err + } + defer resp.Body.Close() + b, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, err + } + var marshalled map[string]*Merchant + if err := json.Unmarshal(b, &marshalled); err != nil { + return nil, err + } + return marshalled["merchant"], nil +} + +func (c Client) setContentHeader(req *http.Request) { req.SetBasicAuth("", c.Key) req.Header.Set("Content-Type", "application/json") +} + +func (c Client) fetchMerchants(appID string, limit int) ([]*Merchant, error) { + path := fmt.Sprintf("/identities/%s/merchants?limit=%d", appID, limit) + req, err := http.NewRequest("GET", c.getURL(path), nil) + if err != nil { + return nil, err + } + c.setContentHeader(req) + resp, err := c.client.Do(req) + if err != nil { + return nil, err + } + defer resp.Body.Close() + b, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, err + } + var marshalled []*Merchant + if err := json.Unmarshal(b, &marshalled); err != nil { + return nil, err + } + return marshalled, nil +} + +func (c Client) getMerchant(id string) (*Merchant, error) { + path := fmt.Sprintf("/merchants/%s", id) + req, err := http.NewRequest("GET", c.getURL(path), nil) + if err != nil { + return nil, err + } + c.setContentHeader(req) resp, err := c.client.Do(req) if err != nil { return nil, err } + defer resp.Body.Close() b, err := ioutil.ReadAll(resp.Body) if err != nil { return nil, err @@ -221,3 +289,12 @@ func (c Client) createMerchant(body io.Reader) (*Merchant, error) { } return marshalled["merchant"], nil } + +// temporary function +func (c Client) exploreAPI(b []byte) { + var t interface{} + if err := json.Unmarshal(b, &t); err != nil { + log.Fatal(err) + } + spew.Dump(t) +} diff --git a/paylike_test.go b/paylike_test.go index 2b6877d..37db9af 100644 --- a/paylike_test.go +++ b/paylike_test.go @@ -57,3 +57,30 @@ func TestCreateMerchant(t *testing.T) { assert.Nil(t, err) assert.NotEmpty(t, merchant) } + +func TestFetchMerchants(t *testing.T) { + client := NewClient(TestKey) + app, err := client.CreateApp() + assert.Nil(t, err) + assert.NotEmpty(t, app) + + dto := MerchantCreateDTO{ + Test: true, + Currency: "HUF", + Email: TestEmail, + Website: TestSite, + Descriptor: "1234567897891234", + Company: &MerchantCompany{ + Country: "HU", + }, + } + merchant, err := client.SetKey(app.Key).CreateMerchant(dto) + assert.Nil(t, err) + assert.NotEmpty(t, merchant) + + merchants, err := client.FetchMerchants(app.ID, 5) + assert.Nil(t, err) + assert.NotEmpty(t, merchants) + + assert.Equal(t, merchant, merchants[0]) +} From 9c01d89a5e4f6cf0b18752a8cca1cb8af03fe294 Mon Sep 17 00:00:00 2001 From: Imre 'Rover' Racz Date: Tue, 12 Mar 2019 23:43:11 +0100 Subject: [PATCH 03/14] refactor(cleanup): Simplifying and documentation --- paylike.go | 103 +++++++++++++++--------------------------------- paylike_test.go | 1 - 2 files changed, 32 insertions(+), 72 deletions(-) diff --git a/paylike.go b/paylike.go index ce59482..e2b6dde 100644 --- a/paylike.go +++ b/paylike.go @@ -146,7 +146,7 @@ func (c Client) GetCurrentApp() (*Identity, error) { return c.getCurrentApp() } -// CreateMerchant ... +// CreateMerchant creates a new merchant under a given app // https://github.com/paylike/api-docs#create-a-merchant func (c Client) CreateMerchant(dto MerchantCreateDTO) (*Merchant, error) { b, err := json.Marshal(dto) @@ -156,13 +156,13 @@ func (c Client) CreateMerchant(dto MerchantCreateDTO) (*Merchant, error) { return c.createMerchant(bytes.NewBuffer(b)) } -// GetMerchant ... +// GetMerchant gets a merchant based on it's ID // https://github.com/paylike/api-docs#fetch-a-merchant func (c Client) GetMerchant(id string) (*Merchant, error) { return c.getMerchant(id) } -// FetchMerchants ... +// FetchMerchants fetches all merchants for given app ID // https://github.com/paylike/api-docs#fetch-all-merchants func (c Client) FetchMerchants(appID string, limit int) ([]*Merchant, error) { return c.fetchMerchants(appID, limit) @@ -173,46 +173,28 @@ func (c Client) getURL(url string) string { return fmt.Sprintf("%s%s", c.baseAPI, url) } -// createApp handles the API calls towards Paylike API +// createApp handles the underlying logic of executing the API requests +// towards the app creation API func (c Client) createApp(body io.Reader) (*App, error) { - resp, err := c.client.Post(c.getURL("/apps"), "application/json", body) - if err != nil { - return nil, err - } - defer resp.Body.Close() - b, err := ioutil.ReadAll(resp.Body) + req, err := http.NewRequest("POST", c.getURL("/apps"), body) if err != nil { return nil, err } var marshalled map[string]*App - if err := json.Unmarshal(b, &marshalled); err != nil { - return nil, err - } - return marshalled["app"], nil + err = c.executeRequestAndMarshal(req, &marshalled) + return marshalled["app"], err } -// getCurrentApp executes the request for fetching the current application -// along with marshalling the response +// getCurrentApp handles the underlying logic of executing the API requests +// towards the app API to get the currently used app func (c Client) getCurrentApp() (*Identity, error) { req, err := http.NewRequest("GET", c.getURL("/me"), nil) if err != nil { return nil, err } - c.setContentHeader(req) - resp, err := c.client.Do(req) - if err != nil { - return nil, err - } - defer resp.Body.Close() - b, err := ioutil.ReadAll(resp.Body) - if err != nil { - return nil, err - } var marshalled map[string]*Identity - if err := json.Unmarshal(b, &marshalled); err != nil { - return nil, err - } - return marshalled["identity"], nil + err = c.executeRequestAndMarshal(req, &marshalled) + return marshalled["identity"], err } // createMerchant handles the underlying logic of executing the API requests @@ -222,72 +204,51 @@ func (c Client) createMerchant(body io.Reader) (*Merchant, error) { if err != nil { return nil, err } - c.setContentHeader(req) - resp, err := c.client.Do(req) - if err != nil { - return nil, err - } - defer resp.Body.Close() - b, err := ioutil.ReadAll(resp.Body) - if err != nil { - return nil, err - } var marshalled map[string]*Merchant - if err := json.Unmarshal(b, &marshalled); err != nil { - return nil, err - } - return marshalled["merchant"], nil -} - -func (c Client) setContentHeader(req *http.Request) { - req.SetBasicAuth("", c.Key) - req.Header.Set("Content-Type", "application/json") + err = c.executeRequestAndMarshal(req, &marshalled) + return marshalled["merchant"], err } +// fetchMerchants handles the underlying logic of executing the API requests +// towards the merchant fetching API func (c Client) fetchMerchants(appID string, limit int) ([]*Merchant, error) { path := fmt.Sprintf("/identities/%s/merchants?limit=%d", appID, limit) req, err := http.NewRequest("GET", c.getURL(path), nil) if err != nil { return nil, err } - c.setContentHeader(req) - resp, err := c.client.Do(req) - if err != nil { - return nil, err - } - defer resp.Body.Close() - b, err := ioutil.ReadAll(resp.Body) - if err != nil { - return nil, err - } var marshalled []*Merchant - if err := json.Unmarshal(b, &marshalled); err != nil { - return nil, err - } - return marshalled, nil + return marshalled, c.executeRequestAndMarshal(req, &marshalled) } +// getMerchant handles the underlying logic of executing the API requests +// towards the merchant API and gets a merchant based on it's ID func (c Client) getMerchant(id string) (*Merchant, error) { path := fmt.Sprintf("/merchants/%s", id) req, err := http.NewRequest("GET", c.getURL(path), nil) if err != nil { return nil, err } - c.setContentHeader(req) + var marshalled map[string]*Merchant + err = c.executeRequestAndMarshal(req, &marshalled) + return marshalled["merchant"], err +} + +// executeRequestAndMarshal sets the correct headers, then executes the request and tries to marshal +// the response from the body into the given interface{} value +func (c Client) executeRequestAndMarshal(req *http.Request, value interface{}) error { + req.SetBasicAuth("", c.Key) + req.Header.Set("Content-Type", "application/json") resp, err := c.client.Do(req) if err != nil { - return nil, err + return err } defer resp.Body.Close() b, err := ioutil.ReadAll(resp.Body) if err != nil { - return nil, err - } - var marshalled map[string]*Merchant - if err := json.Unmarshal(b, &marshalled); err != nil { - return nil, err + return err } - return marshalled["merchant"], nil + return json.Unmarshal(b, &value) } // temporary function diff --git a/paylike_test.go b/paylike_test.go index 37db9af..45e032b 100644 --- a/paylike_test.go +++ b/paylike_test.go @@ -81,6 +81,5 @@ func TestFetchMerchants(t *testing.T) { merchants, err := client.FetchMerchants(app.ID, 5) assert.Nil(t, err) assert.NotEmpty(t, merchants) - assert.Equal(t, merchant, merchants[0]) } From 50b27d51ec91968e9d7b1f96f43c99c4e28bc980 Mon Sep 17 00:00:00 2001 From: Imre 'Rover' Racz Date: Wed, 13 Mar 2019 00:04:13 +0100 Subject: [PATCH 04/14] feat(merchant): Updates is now available and tested --- paylike.go | 42 ++++++++++++++++++++++++++++++--- paylike_test.go | 63 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 102 insertions(+), 3 deletions(-) diff --git a/paylike.go b/paylike.go index e2b6dde..8c9eb0e 100644 --- a/paylike.go +++ b/paylike.go @@ -36,14 +36,22 @@ type Identity struct { // MerchantCreateDTO describes options for creating a merchant type MerchantCreateDTO struct { - Name string `json:"name,omitempty"` // optional + Name string `json:"name,omitempty"` // optional, name of merchant Currency string `json:"currency"` // required, three letter ISO Test bool `json:"test,omitempty"` // optional, defaults to false Email string `json:"email"` // required, contact email Website string `json:"website"` // required, website with implementation Descriptor string `json:"descriptor"` // required, text on client bank statements - Company *MerchantCompany `json:"company"` // required - Bank *MerchantBank `json:"bank,omitempty"` // optional + Company *MerchantCompany `json:"company"` // required, company information + Bank *MerchantBank `json:"bank,omitempty"` // optional, bank information +} + +// MerchantUpdateDTO describes options to update a given merchant +// If you cannot find your desired option here, create a new merchant instead +type MerchantUpdateDTO struct { + Name string `json:"name,omitempty"` // optional, name of merchant + Email string `json:"email,omitempty"` // optional, contact email + Descriptor string `json:"descriptor,omitempty"` // optional, text on client bank statements } // Amount ... @@ -78,6 +86,7 @@ type MerchantTDS struct { // Merchant ... type Merchant struct { ID string + Name string Company MerchantCompany Claim MerchantClaim Pricing MerchantPricing @@ -168,6 +177,16 @@ func (c Client) FetchMerchants(appID string, limit int) ([]*Merchant, error) { return c.fetchMerchants(appID, limit) } +// UpdateMerchant updates a merchant with given parameters +// https://github.com/paylike/api-docs#update-a-merchant +func (c Client) UpdateMerchant(id string, dto MerchantUpdateDTO) error { + b, err := json.Marshal(dto) + if err != nil { + return err + } + return c.updateMerchant(id, bytes.NewBuffer(b)) +} + // getURL is to build the base API url along with the given dynamic route path func (c Client) getURL(url string) string { return fmt.Sprintf("%s%s", c.baseAPI, url) @@ -234,6 +253,17 @@ func (c Client) getMerchant(id string) (*Merchant, error) { return marshalled["merchant"], err } +// updateMerchant handles the underlying logic of executing the API requests +// towards the merchant API and updates a given merchant +func (c Client) updateMerchant(id string, body io.Reader) error { + path := fmt.Sprintf("/merchants/%s", id) + req, err := http.NewRequest("PUT", c.getURL(path), body) + if err != nil { + return nil + } + return c.executeRequestAndMarshal(req, nil) +} + // executeRequestAndMarshal sets the correct headers, then executes the request and tries to marshal // the response from the body into the given interface{} value func (c Client) executeRequestAndMarshal(req *http.Request, value interface{}) error { @@ -244,10 +274,16 @@ func (c Client) executeRequestAndMarshal(req *http.Request, value interface{}) e return err } defer resp.Body.Close() + // Skip marshaling if the interface value is nil to avoid + // unnecessary errors + if value == nil { + return nil + } b, err := ioutil.ReadAll(resp.Body) if err != nil { return err } + // c.exploreAPI(b) return json.Unmarshal(b, &value) } diff --git a/paylike_test.go b/paylike_test.go index 45e032b..e886b6b 100644 --- a/paylike_test.go +++ b/paylike_test.go @@ -1,6 +1,7 @@ package paylike import ( + "fmt" "testing" "github.com/stretchr/testify/assert" @@ -83,3 +84,65 @@ func TestFetchMerchants(t *testing.T) { assert.NotEmpty(t, merchants) assert.Equal(t, merchant, merchants[0]) } + +func TestGetMerchant(t *testing.T) { + client := NewClient(TestKey) + app, err := client.CreateApp() + assert.Nil(t, err) + assert.NotEmpty(t, app) + + dto := MerchantCreateDTO{ + Test: true, + Currency: "HUF", + Email: TestEmail, + Website: TestSite, + Descriptor: "1234567897891234", + Company: &MerchantCompany{ + Country: "HU", + }, + } + merchant, err := client.SetKey(app.Key).CreateMerchant(dto) + assert.Nil(t, err) + assert.NotEmpty(t, merchant) + + fetchedMerchant, err := client.GetMerchant(merchant.ID) + assert.Nil(t, err) + assert.NotEmpty(t, fetchedMerchant) + assert.Equal(t, fetchedMerchant, merchant) +} + +func TestUpdateMerchant(t *testing.T) { + client := NewClient(TestKey) + app, err := client.CreateApp() + assert.Nil(t, err) + assert.NotEmpty(t, app) + + dto := MerchantCreateDTO{ + Name: "NotTest", + Test: true, + Currency: "HUF", + Email: TestEmail, + Website: TestSite, + Descriptor: "1234567897891234", + Company: &MerchantCompany{ + Country: "HU", + }, + } + merchant, err := client.SetKey(app.Key).CreateMerchant(dto) + assert.Nil(t, err) + assert.NotEmpty(t, merchant) + + updateDTO := MerchantUpdateDTO{ + Name: "Test", + Descriptor: "NotNumbers", + Email: fmt.Sprintf("not_%s", dto.Email), + } + err = client.UpdateMerchant(merchant.ID, updateDTO) + assert.Nil(t, err) + updatedMerchant, err := client.GetMerchant(merchant.ID) + assert.Nil(t, err) + assert.NotEmpty(t, updatedMerchant) + assert.Equal(t, updatedMerchant.Email, updateDTO.Email) + assert.Equal(t, updatedMerchant.Name, updateDTO.Name) + assert.Equal(t, updatedMerchant.Descriptor, updateDTO.Descriptor) +} From 255d26317eb2f479866d0f2eb52be9e355afd77e Mon Sep 17 00:00:00 2001 From: Imre 'Rover' Racz Date: Wed, 13 Mar 2019 01:32:49 +0100 Subject: [PATCH 05/14] feat(merchant): Users are now fetchable and tested --- paylike.go | 75 ++++++++++++++++++++++++++++++++++++++----------- paylike_test.go | 57 +++++++++++++++++++++++++++++++++++++ 2 files changed, 116 insertions(+), 16 deletions(-) diff --git a/paylike.go b/paylike.go index 8c9eb0e..9ceb0a3 100644 --- a/paylike.go +++ b/paylike.go @@ -6,10 +6,7 @@ import ( "fmt" "io" "io/ioutil" - "log" "net/http" - - "github.com/davecgh/go-spew/spew" ) // Client describes all information regarding the API @@ -54,6 +51,12 @@ type MerchantUpdateDTO struct { Descriptor string `json:"descriptor,omitempty"` // optional, text on client bank statements } +// InviteUserToMerchantResponse describes the response when a user +// is being invited to a given merchant +type InviteUserToMerchantResponse struct { + IsMember bool +} + // Amount ... type Amount struct { Currency string @@ -112,6 +115,12 @@ type MerchantClaim struct { CanVoid bool } +// User describes a user in the system +type User struct { + ID string `json:"id"` + Email string `json:"email"` +} + // MerchantCompany ... type MerchantCompany struct { Country string `json:"country"` // required, ISO 3166 code (e.g. DK) @@ -177,6 +186,18 @@ func (c Client) FetchMerchants(appID string, limit int) ([]*Merchant, error) { return c.fetchMerchants(appID, limit) } +// InviteUserToMerchant invites given user to use the given merchant account +// https://github.com/paylike/api-docs#invite-user-to-a-merchant +func (c Client) InviteUserToMerchant(merchantID string, email string) (*InviteUserToMerchantResponse, error) { + return c.inviteUserToMerchant(merchantID, email) +} + +// FetchUsersToMerchant fetches users for a given merchant +// https://github.com/paylike/api-docs#fetch-all-users-on-a-merchant +func (c Client) FetchUsersToMerchant(merchantID string, limit int) ([]*User, error) { + return c.fetchUsersToMerchant(merchantID, limit) +} + // UpdateMerchant updates a merchant with given parameters // https://github.com/paylike/api-docs#update-a-merchant func (c Client) UpdateMerchant(id string, dto MerchantUpdateDTO) error { @@ -264,6 +285,34 @@ func (c Client) updateMerchant(id string, body io.Reader) error { return c.executeRequestAndMarshal(req, nil) } +// inviteUserToMerchant handles the underlying logic of executing the API requests +// towards the merchant API and invites a given user in the system +// to use the given merchant +func (c Client) inviteUserToMerchant(id string, email string) (*InviteUserToMerchantResponse, error) { + path := fmt.Sprintf("/merchants/%s/users", id) + data := []byte(fmt.Sprintf(`{"email":"%s"}`, email)) + req, err := http.NewRequest("POST", c.getURL(path), bytes.NewBuffer(data)) + if err != nil { + return nil, nil + } + var marshalled InviteUserToMerchantResponse + err = c.executeRequestAndMarshal(req, &marshalled) + return &marshalled, err +} + +// fetchUsersToMerchant handles the underlying logic of executing the API requests +// towards the merchant API and lists all users that are related for the given merchant +// curl -i https://api.paylike.io/merchants//users?limit= \ +func (c Client) fetchUsersToMerchant(id string, limit int) ([]*User, error) { + path := fmt.Sprintf("/merchants/%s/users?limit=%d", id, limit) + req, err := http.NewRequest("GET", c.getURL(path), nil) + if err != nil { + return nil, nil + } + var marshalled []*User + return marshalled, c.executeRequestAndMarshal(req, &marshalled) +} + // executeRequestAndMarshal sets the correct headers, then executes the request and tries to marshal // the response from the body into the given interface{} value func (c Client) executeRequestAndMarshal(req *http.Request, value interface{}) error { @@ -274,24 +323,18 @@ func (c Client) executeRequestAndMarshal(req *http.Request, value interface{}) e return err } defer resp.Body.Close() - // Skip marshaling if the interface value is nil to avoid - // unnecessary errors - if value == nil { - return nil - } b, err := ioutil.ReadAll(resp.Body) if err != nil { return err } - // c.exploreAPI(b) + if len(b) == 0 { + return nil + } return json.Unmarshal(b, &value) } -// temporary function -func (c Client) exploreAPI(b []byte) { - var t interface{} - if err := json.Unmarshal(b, &t); err != nil { - log.Fatal(err) - } - spew.Dump(t) +// Temporary function +func (c Client) exploreResponse(resp *http.Response, b []byte) { + fmt.Println(resp.Status) + fmt.Println(string(b)) } diff --git a/paylike_test.go b/paylike_test.go index e886b6b..98e3624 100644 --- a/paylike_test.go +++ b/paylike_test.go @@ -146,3 +146,60 @@ func TestUpdateMerchant(t *testing.T) { assert.Equal(t, updatedMerchant.Name, updateDTO.Name) assert.Equal(t, updatedMerchant.Descriptor, updateDTO.Descriptor) } + +func TestInviteUserToMerchant(t *testing.T) { + client := NewClient(TestKey) + app, err := client.CreateApp() + assert.Nil(t, err) + assert.NotEmpty(t, app) + + dto := MerchantCreateDTO{ + Name: "NotTest", + Test: true, + Currency: "HUF", + Email: TestEmail, + Website: TestSite, + Descriptor: "1234567897891234", + Company: &MerchantCompany{ + Country: "HU", + }, + } + merchant, err := client.SetKey(app.Key).CreateMerchant(dto) + assert.Nil(t, err) + assert.NotEmpty(t, merchant) + + data, err := client.InviteUserToMerchant(merchant.ID, "one@example.com") + assert.Nil(t, err) + assert.False(t, data.IsMember) +} + +func TestFetchUsersToMerchant(t *testing.T) { + client := NewClient(TestKey) + app, err := client.CreateApp() + assert.Nil(t, err) + assert.NotEmpty(t, app) + + dto := MerchantCreateDTO{ + Name: "NotTest", + Test: true, + Currency: "HUF", + Email: TestEmail, + Website: TestSite, + Descriptor: "1234567897891234", + Company: &MerchantCompany{ + Country: "HU", + }, + } + merchant, err := client.SetKey(app.Key).CreateMerchant(dto) + assert.Nil(t, err) + assert.NotEmpty(t, merchant) + + data, err := client.InviteUserToMerchant(merchant.ID, "one@example.com") + assert.Nil(t, err) + assert.False(t, data.IsMember) + + users, err := client.FetchUsersToMerchant(merchant.ID, 3) + assert.Nil(t, err) + assert.NotEmpty(t, users) + assert.Equal(t, "one@example.com", users[0].Email) +} From 2a14d04891c49d545d105d3e70a2478bcfc2c3b2 Mon Sep 17 00:00:00 2001 From: Imre 'Rover' Racz Date: Wed, 13 Mar 2019 01:37:43 +0100 Subject: [PATCH 06/14] feat(merchant): Users now can be revoked from merchants --- paylike.go | 18 +++++++++++++++++- paylike_test.go | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 1 deletion(-) diff --git a/paylike.go b/paylike.go index 9ceb0a3..60c531f 100644 --- a/paylike.go +++ b/paylike.go @@ -198,6 +198,12 @@ func (c Client) FetchUsersToMerchant(merchantID string, limit int) ([]*User, err return c.fetchUsersToMerchant(merchantID, limit) } +// RevokeUserFromMerchant revokes a given user from a given merchant +// https://github.com/paylike/api-docs#revoke-user-from-a-merchant +func (c Client) RevokeUserFromMerchant(merchantID string, userID string) error { + return c.revokeUserFromMerchant(merchantID, userID) +} + // UpdateMerchant updates a merchant with given parameters // https://github.com/paylike/api-docs#update-a-merchant func (c Client) UpdateMerchant(id string, dto MerchantUpdateDTO) error { @@ -302,7 +308,6 @@ func (c Client) inviteUserToMerchant(id string, email string) (*InviteUserToMerc // fetchUsersToMerchant handles the underlying logic of executing the API requests // towards the merchant API and lists all users that are related for the given merchant -// curl -i https://api.paylike.io/merchants//users?limit= \ func (c Client) fetchUsersToMerchant(id string, limit int) ([]*User, error) { path := fmt.Sprintf("/merchants/%s/users?limit=%d", id, limit) req, err := http.NewRequest("GET", c.getURL(path), nil) @@ -313,6 +318,17 @@ func (c Client) fetchUsersToMerchant(id string, limit int) ([]*User, error) { return marshalled, c.executeRequestAndMarshal(req, &marshalled) } +// revokeUserFromMerchant handles the underlying logic of executing the API requests +// towards the merchant API and revokes a given user from a given merchant +func (c Client) revokeUserFromMerchant(merchantID string, userID string) error { + path := fmt.Sprintf("/merchants/%s/users/%s", merchantID, userID) + req, err := http.NewRequest("DELETE", c.getURL(path), nil) + if err != nil { + return err + } + return c.executeRequestAndMarshal(req, nil) +} + // executeRequestAndMarshal sets the correct headers, then executes the request and tries to marshal // the response from the body into the given interface{} value func (c Client) executeRequestAndMarshal(req *http.Request, value interface{}) error { diff --git a/paylike_test.go b/paylike_test.go index 98e3624..ef130ec 100644 --- a/paylike_test.go +++ b/paylike_test.go @@ -203,3 +203,41 @@ func TestFetchUsersToMerchant(t *testing.T) { assert.NotEmpty(t, users) assert.Equal(t, "one@example.com", users[0].Email) } + +func TestRevokeUserFromMerchant(t *testing.T) { + client := NewClient(TestKey) + app, err := client.CreateApp() + assert.Nil(t, err) + assert.NotEmpty(t, app) + + dto := MerchantCreateDTO{ + Name: "NotTest", + Test: true, + Currency: "HUF", + Email: TestEmail, + Website: TestSite, + Descriptor: "1234567897891234", + Company: &MerchantCompany{ + Country: "HU", + }, + } + merchant, err := client.SetKey(app.Key).CreateMerchant(dto) + assert.Nil(t, err) + assert.NotEmpty(t, merchant) + + data, err := client.InviteUserToMerchant(merchant.ID, "one@example.com") + assert.Nil(t, err) + assert.False(t, data.IsMember) + + users, err := client.FetchUsersToMerchant(merchant.ID, 3) + assert.Nil(t, err) + assert.NotEmpty(t, users) + assert.Equal(t, "one@example.com", users[0].Email) + + err = client.RevokeUserFromMerchant(merchant.ID, users[0].ID) + assert.Nil(t, err) + + users, err = client.FetchUsersToMerchant(merchant.ID, 3) + assert.Nil(t, err) + assert.Empty(t, users) +} From 6b1ded43a80d61a76053157290fe844cd77b72ad Mon Sep 17 00:00:00 2001 From: Imre 'Rover' Racz Date: Wed, 13 Mar 2019 01:53:48 +0100 Subject: [PATCH 07/14] feat(merchant): App is now an available resource --- paylike.go | 72 +++++++++++++++++++++++++++++++++++----- paylike_test.go | 87 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 150 insertions(+), 9 deletions(-) diff --git a/paylike.go b/paylike.go index 60c531f..4a72abc 100644 --- a/paylike.go +++ b/paylike.go @@ -186,6 +186,16 @@ func (c Client) FetchMerchants(appID string, limit int) ([]*Merchant, error) { return c.fetchMerchants(appID, limit) } +// UpdateMerchant updates a merchant with given parameters +// https://github.com/paylike/api-docs#update-a-merchant +func (c Client) UpdateMerchant(id string, dto MerchantUpdateDTO) error { + b, err := json.Marshal(dto) + if err != nil { + return err + } + return c.updateMerchant(id, bytes.NewBuffer(b)) +} + // InviteUserToMerchant invites given user to use the given merchant account // https://github.com/paylike/api-docs#invite-user-to-a-merchant func (c Client) InviteUserToMerchant(merchantID string, email string) (*InviteUserToMerchantResponse, error) { @@ -204,14 +214,22 @@ func (c Client) RevokeUserFromMerchant(merchantID string, userID string) error { return c.revokeUserFromMerchant(merchantID, userID) } -// UpdateMerchant updates a merchant with given parameters -// https://github.com/paylike/api-docs#update-a-merchant -func (c Client) UpdateMerchant(id string, dto MerchantUpdateDTO) error { - b, err := json.Marshal(dto) - if err != nil { - return err - } - return c.updateMerchant(id, bytes.NewBuffer(b)) +// AddAppToMerchant revokes a given user from a given merchant +// https://github.com/paylike/api-docs#add-app-to-a-merchant +func (c Client) AddAppToMerchant(merchantID string, appID string) error { + return c.addAppToMerchant(merchantID, appID) +} + +// FetchAppsToMerchant fetches apps for a given merchant +// https://github.com/paylike/api-docs#fetch-all-apps-on-a-merchant +func (c Client) FetchAppsToMerchant(merchantID string, limit int) ([]*App, error) { + return c.fetchAppsToMerchant(merchantID, limit) +} + +// RevokeAppFromMerchant revokes a given app from a given merchant +// https://github.com/paylike/api-docs#revoke-app-from-a-merchant +func (c Client) RevokeAppFromMerchant(merchantID string, appID string) error { + return c.revokeAppFromMerchant(merchantID, appID) } // getURL is to build the base API url along with the given dynamic route path @@ -295,8 +313,8 @@ func (c Client) updateMerchant(id string, body io.Reader) error { // towards the merchant API and invites a given user in the system // to use the given merchant func (c Client) inviteUserToMerchant(id string, email string) (*InviteUserToMerchantResponse, error) { - path := fmt.Sprintf("/merchants/%s/users", id) data := []byte(fmt.Sprintf(`{"email":"%s"}`, email)) + path := fmt.Sprintf("/merchants/%s/users", id) req, err := http.NewRequest("POST", c.getURL(path), bytes.NewBuffer(data)) if err != nil { return nil, nil @@ -329,6 +347,41 @@ func (c Client) revokeUserFromMerchant(merchantID string, userID string) error { return c.executeRequestAndMarshal(req, nil) } +// addAppToMerchant handles the underlying logic of executing the API requests +// towards the merchant API and adds the given app to the given merchant +func (c Client) addAppToMerchant(merchantID string, appID string) error { + data := []byte(fmt.Sprintf(`{"appId":"%s"}`, appID)) + path := fmt.Sprintf("/merchants/%s/apps", merchantID) + req, err := http.NewRequest("POST", c.getURL(path), bytes.NewBuffer(data)) + if err != nil { + return err + } + return c.executeRequestAndMarshal(req, nil) +} + +// fetchAppsToMerchant handles the underlying logic of executing the API requests +// towards the merchant API and lists all apps related to the merchant +func (c Client) fetchAppsToMerchant(merchantID string, limit int) ([]*App, error) { + path := fmt.Sprintf("/merchants/%s/apps?limit=%d", merchantID, limit) + req, err := http.NewRequest("GET", c.getURL(path), nil) + if err != nil { + return nil, err + } + var marshalled []*App + return marshalled, c.executeRequestAndMarshal(req, &marshalled) +} + +// revokeAppFromMerchant handles the underlying logic of executing the API requests +// towards the merchant API and revokes a given app from a given merchant +func (c Client) revokeAppFromMerchant(merchantID string, appID string) error { + path := fmt.Sprintf("/merchants/%s/apps/%s", merchantID, appID) + req, err := http.NewRequest("DELETE", c.getURL(path), nil) + if err != nil { + return err + } + return c.executeRequestAndMarshal(req, nil) +} + // executeRequestAndMarshal sets the correct headers, then executes the request and tries to marshal // the response from the body into the given interface{} value func (c Client) executeRequestAndMarshal(req *http.Request, value interface{}) error { @@ -343,6 +396,7 @@ func (c Client) executeRequestAndMarshal(req *http.Request, value interface{}) e if err != nil { return err } + c.exploreResponse(resp, b) if len(b) == 0 { return nil } diff --git a/paylike_test.go b/paylike_test.go index ef130ec..4e75eae 100644 --- a/paylike_test.go +++ b/paylike_test.go @@ -241,3 +241,90 @@ func TestRevokeUserFromMerchant(t *testing.T) { assert.Nil(t, err) assert.Empty(t, users) } + +func TestAddAppToMerchant(t *testing.T) { + client := NewClient(TestKey) + app, err := client.CreateApp() + assert.Nil(t, err) + assert.NotEmpty(t, app) + + dto := MerchantCreateDTO{ + Name: "NotTest", + Test: true, + Currency: "HUF", + Email: TestEmail, + Website: TestSite, + Descriptor: "1234567897891234", + Company: &MerchantCompany{ + Country: "HU", + }, + } + merchant, err := client.SetKey(app.Key).CreateMerchant(dto) + assert.Nil(t, err) + assert.NotEmpty(t, merchant) + + err = client.AddAppToMerchant(merchant.ID, app.ID) + assert.Nil(t, err) +} + +func TestFetchAppsToMerchant(t *testing.T) { + client := NewClient(TestKey) + app, err := client.CreateApp() + assert.Nil(t, err) + assert.NotEmpty(t, app) + + dto := MerchantCreateDTO{ + Name: "NotTest", + Test: true, + Currency: "HUF", + Email: TestEmail, + Website: TestSite, + Descriptor: "1234567897891234", + Company: &MerchantCompany{ + Country: "HU", + }, + } + merchant, err := client.SetKey(app.Key).CreateMerchant(dto) + assert.Nil(t, err) + assert.NotEmpty(t, merchant) + + err = client.AddAppToMerchant(merchant.ID, app.ID) + assert.Nil(t, err) + + apps, err := client.FetchAppsToMerchant(merchant.ID, 2) + assert.Nil(t, err) + assert.NotEmpty(t, apps) + assert.Equal(t, app, apps[0]) +} + +func TestRevokeAppFromMerchant(t *testing.T) { + client := NewClient(TestKey) + app, err := client.CreateApp() + assert.Nil(t, err) + assert.NotEmpty(t, app) + + dto := MerchantCreateDTO{ + Name: "NotTest", + Test: true, + Currency: "HUF", + Email: TestEmail, + Website: TestSite, + Descriptor: "1234567897891234", + Company: &MerchantCompany{ + Country: "HU", + }, + } + merchant, err := client.SetKey(app.Key).CreateMerchant(dto) + assert.Nil(t, err) + assert.NotEmpty(t, merchant) + + err = client.AddAppToMerchant(merchant.ID, app.ID) + assert.Nil(t, err) + + err = client.RevokeAppFromMerchant(merchant.ID, app.ID) + assert.Nil(t, err) + + apps, err := client.FetchAppsToMerchant(merchant.ID, 2) + assert.Nil(t, err) + assert.Empty(t, apps) +} From 5f1a5c9df696eec5a7da92b17928e67eca941607 Mon Sep 17 00:00:00 2001 From: Imre 'Rover' Racz Date: Wed, 13 Mar 2019 02:28:15 +0100 Subject: [PATCH 08/14] feat(lines): Lines are now fetchable and tested --- paylike.go | 31 +++++++++++++++++++++++++++++++ paylike_test.go | 8 ++++++++ 2 files changed, 39 insertions(+) diff --git a/paylike.go b/paylike.go index 4a72abc..733ef99 100644 --- a/paylike.go +++ b/paylike.go @@ -132,6 +132,19 @@ type MerchantBank struct { Iban string `json:"iban,omitempty"` // optional, (format: XX00000000, XX is country code, length varies) } +// Line desccribes a given item in the history of the merchant balance +type Line struct { + ID string `json:"id"` + Created string `json:"created"` + MerchantID string `json:"merchantId"` + Balance int `json:"balance"` + Fee int `json:"fee"` + TransactionID string `json:"transactionId"` + Amount Amount `json:"amount"` + Refund bool `json:"refund"` + Test bool `json:"test"` +} + // NewClient creates a new client func NewClient(key string) *Client { return &Client{key, &http.Client{}, "https://api.paylike.io"} @@ -232,6 +245,12 @@ func (c Client) RevokeAppFromMerchant(merchantID string, appID string) error { return c.revokeAppFromMerchant(merchantID, appID) } +// FetchLinesToMerchant fetches the history that makes up a given merchant's balance +// https://github.com/paylike/api-docs#merchants-lines +func (c Client) FetchLinesToMerchant(merchantID string, limit int) ([]*Line, error) { + return c.fetchLinesToMerchant(merchantID, limit) +} + // getURL is to build the base API url along with the given dynamic route path func (c Client) getURL(url string) string { return fmt.Sprintf("%s%s", c.baseAPI, url) @@ -382,6 +401,18 @@ func (c Client) revokeAppFromMerchant(merchantID string, appID string) error { return c.executeRequestAndMarshal(req, nil) } +// fetchLinesToMerchant handles the underlying logic of executing the API requests +// towards the merchant API and fetches all lines related to a merchant's history +func (c Client) fetchLinesToMerchant(merchantID string, limit int) ([]*Line, error) { + path := fmt.Sprintf("/merchants/%s/lines?limit=%d", merchantID, limit) + req, err := http.NewRequest("GET", c.getURL(path), nil) + if err != nil { + return nil, err + } + var marshalled []*Line + return marshalled, c.executeRequestAndMarshal(req, &marshalled) +} + // executeRequestAndMarshal sets the correct headers, then executes the request and tries to marshal // the response from the body into the given interface{} value func (c Client) executeRequestAndMarshal(req *http.Request, value interface{}) error { diff --git a/paylike_test.go b/paylike_test.go index 4e75eae..9f6853e 100644 --- a/paylike_test.go +++ b/paylike_test.go @@ -328,3 +328,11 @@ func TestRevokeAppFromMerchant(t *testing.T) { assert.Nil(t, err) assert.Empty(t, apps) } + +func TestFetchLinesToMerchant(t *testing.T) { + client := NewClient(TestKey).SetKey("4ff7de37-dddf-4e51-8cc9-48b61a102923") + lines, err := client.FetchLinesToMerchant("55006bdfe0308c4cbfdbd0e1", 1) + assert.Nil(t, err) + assert.NotEmpty(t, lines) + assert.Len(t, lines, 1) +} From 94c1de2fc48b4e185d658c1f9cacd05cc8cde6a4 Mon Sep 17 00:00:00 2001 From: Imre 'Rover' Racz Date: Wed, 13 Mar 2019 20:07:28 +0100 Subject: [PATCH 09/14] feat(merchant): Transaction creation --- paylike.go | 38 ++++++++++++++++++++++++++++++++++++++ paylike_test.go | 13 +++++++++++++ 2 files changed, 51 insertions(+) diff --git a/paylike.go b/paylike.go index 733ef99..8e65c68 100644 --- a/paylike.go +++ b/paylike.go @@ -145,6 +145,22 @@ type Line struct { Test bool `json:"test"` } +// TransactionDTO describes options in terms of the transaction +// creation API +type TransactionDTO struct { + TransactionID string `json:"transactionId"` // required + Descriptor string `json:"descriptor,omitempty"` // optional, will fallback to merchant descriptor + Currency string `json:"currency"` // required, three letter ISO + Amount int `json:"amount"` // required, amount in minor units + Custom interface{} `json:"custom,omitempty"` // optional, any custom data + +} + +// TransactionID describes the ID for a given unique transaction used for referencing +type TransactionID struct { + ID string `json:"id"` +} + // NewClient creates a new client func NewClient(key string) *Client { return &Client{key, &http.Client{}, "https://api.paylike.io"} @@ -251,6 +267,16 @@ func (c Client) FetchLinesToMerchant(merchantID string, limit int) ([]*Line, err return c.fetchLinesToMerchant(merchantID, limit) } +// CreateTransaction creates a new transaction based on previous transaction informations +// https://github.com/paylike/api-docs#using-a-previous-transaction +func (c Client) CreateTransaction(merchantID string, dto TransactionDTO) (*TransactionID, error) { + b, err := json.Marshal(dto) + if err != nil { + return nil, err + } + return c.createTransaction(merchantID, bytes.NewBuffer(b)) +} + // getURL is to build the base API url along with the given dynamic route path func (c Client) getURL(url string) string { return fmt.Sprintf("%s%s", c.baseAPI, url) @@ -413,6 +439,18 @@ func (c Client) fetchLinesToMerchant(merchantID string, limit int) ([]*Line, err return marshalled, c.executeRequestAndMarshal(req, &marshalled) } +// createTransaction handles the underlying logic of executing the API requests +// towards the merchant API and creates a new transaction +func (c Client) createTransaction(merchantID string, body io.Reader) (*TransactionID, error) { + path := fmt.Sprintf("/merchants/%s/transactions", merchantID) + req, err := http.NewRequest("POST", c.getURL(path), body) + if err != nil { + return nil, err + } + var marshalled map[string]*TransactionID + return marshalled["transaction"], c.executeRequestAndMarshal(req, &marshalled) +} + // executeRequestAndMarshal sets the correct headers, then executes the request and tries to marshal // the response from the body into the given interface{} value func (c Client) executeRequestAndMarshal(req *http.Request, value interface{}) error { diff --git a/paylike_test.go b/paylike_test.go index 9f6853e..99931b3 100644 --- a/paylike_test.go +++ b/paylike_test.go @@ -336,3 +336,16 @@ func TestFetchLinesToMerchant(t *testing.T) { assert.NotEmpty(t, lines) assert.Len(t, lines, 1) } + +func TestCreateTransaction(t *testing.T) { + client := NewClient(TestKey).SetKey("4ff7de37-dddf-4e51-8cc9-48b61a102923") + dto := TransactionDTO{ + TransactionID: "560fd96b7973ff3d2362a78c", + Currency: "EUR", + Amount: 200, + Custom: struct{ Source string }{"go client test"}, + } + data, err := client.CreateTransaction("55006bdfe0308c4cbfdbd0e1", dto) + assert.Nil(t, err) + assert.NotEmpty(t, data) +} From b5ebd6dce706605251ce2f9c529c9f0f172d4c03 Mon Sep 17 00:00:00 2001 From: Imre 'Rover' Racz Date: Tue, 19 Mar 2019 16:06:52 +0100 Subject: [PATCH 10/14] feat(transaction): Capture is now available --- paylike.go | 112 +++++++++++++++++++++++++++++++++++++++++++++--- paylike_test.go | 35 ++++++++++++++- 2 files changed, 141 insertions(+), 6 deletions(-) diff --git a/paylike.go b/paylike.go index 8e65c68..bc7bc71 100644 --- a/paylike.go +++ b/paylike.go @@ -148,11 +148,12 @@ type Line struct { // TransactionDTO describes options in terms of the transaction // creation API type TransactionDTO struct { - TransactionID string `json:"transactionId"` // required - Descriptor string `json:"descriptor,omitempty"` // optional, will fallback to merchant descriptor - Currency string `json:"currency"` // required, three letter ISO - Amount int `json:"amount"` // required, amount in minor units - Custom interface{} `json:"custom,omitempty"` // optional, any custom data + CardID string `json:"cardId,omitempty"` // required if no TransactionID is present + TransactionID string `json:"transactionId,omitempty"` // required if no CardID is present + Descriptor string `json:"descriptor,omitempty"` // optional, will fallback to merchant descriptor + Currency string `json:"currency"` // required, three letter ISO + Amount int `json:"amount"` // required, amount in minor units + Custom map[string]string `json:"custom,omitempty"` // optional, any custom data } @@ -161,6 +162,67 @@ type TransactionID struct { ID string `json:"id"` } +// TransactionCaptureDTO describes information about the the capturing amount +type TransactionCaptureDTO struct { + Amount int `json:"amount"` // required, amount in minor units (100 = DKK 1,00) + Currency string `json:"currency"` // optional, expected currency (for additional verification) + Descriptor string `json:"descriptor"` // optional, text on client bank statement +} + +// CardCode describes if a given code is present to the card or not +type CardCode struct { + Present bool `json:"present"` +} + +// Card describes card information that can be found in transactions +type Card struct { + Bin string `json:"bin"` + Last4 string `json:"last4"` + Expiry string `json:"expiry"` + Scheme string `json:"scheme"` + Code CardCode `json:"code"` +} + +// Transaction describes information about a given transaction +type Transaction struct { + TransactionID + Test bool `json:"test"` + MerchantID string `json:"merchantId"` + Created string `json:"created"` + Amount int `json:"amount"` + RefundedAmount int `json:"refundedAmount"` + CapturedAmount int `json:"capturedAmount"` + VoidedAmount int `json:"voidedAmount"` + PendingAmount int `json:"pendingAmount"` + DisputedAmount int `json:"disputedAmount"` + Card Card `json:"card"` + TDS string `json:"tds"` + Currency string `json:"currency"` + Custom map[string]string `json:"custom"` + Recurring bool `json:"recurring"` + Successful bool `json:"successful"` + Error bool `json:"error"` + Descriptor string `json:"descriptor"` + Trail []*TransactionTrail `json:"trail"` +} + +// TransactionTrailFee describes fee included in the given trail +type TransactionTrailFee struct { + Flat int `json:"flat"` + Rate int `json:"rate"` +} + +// TransactionTrail describes a given trail element in the transactions +type TransactionTrail struct { + Fee TransactionTrailFee `json:"fee"` + Amount int `json:"amount"` + Balance int `json:"balance"` + Created string `json:"created"` + Capture bool `json:"captrue"` + Descriptor string `json:"descriptor"` + LineID string `json:"lineId"` +} + // NewClient creates a new client func NewClient(key string) *Client { return &Client{key, &http.Client{}, "https://api.paylike.io"} @@ -277,6 +339,22 @@ func (c Client) CreateTransaction(merchantID string, dto TransactionDTO) (*Trans return c.createTransaction(merchantID, bytes.NewBuffer(b)) } +// ListTransactions lists all transactions available under the given merchantID +// https://github.com/paylike/api-docs#fetch-all-transactions +func (c Client) ListTransactions(merchantID string, limit int) ([]*Transaction, error) { + return c.listTransactions(merchantID, limit) +} + +// CaptureTransaction captures a new amount for the given transaction +// https://github.com/paylike/api-docs#capture-a-transaction +func (c Client) CaptureTransaction(transactionID string, dto TransactionCaptureDTO) (*Transaction, error) { + b, err := json.Marshal(dto) + if err != nil { + return nil, err + } + return c.captureTransaction(transactionID, bytes.NewBuffer(b)) +} + // getURL is to build the base API url along with the given dynamic route path func (c Client) getURL(url string) string { return fmt.Sprintf("%s%s", c.baseAPI, url) @@ -451,6 +529,30 @@ func (c Client) createTransaction(merchantID string, body io.Reader) (*Transacti return marshalled["transaction"], c.executeRequestAndMarshal(req, &marshalled) } +// listTransactions handles the underlying logic of executing the API requests +// towards the merchant API and lists all related transactions +func (c Client) listTransactions(merchantID string, limit int) ([]*Transaction, error) { + path := fmt.Sprintf("/merchants/%s/transactions?limit=%d", merchantID, limit) + req, err := http.NewRequest("GET", c.getURL(path), nil) + if err != nil { + return nil, err + } + var marshalled []*Transaction + return marshalled, c.executeRequestAndMarshal(req, &marshalled) +} + +// captureTransaction handles the underlying logic of executing the API requests +// towards the merchant API and captures a new amount for a given transaction +func (c Client) captureTransaction(transactionID string, body io.Reader) (*Transaction, error) { + path := fmt.Sprintf("/transactions/%s/captures", transactionID) + req, err := http.NewRequest("POST", c.getURL(path), body) + if err != nil { + return nil, err + } + var marshalled map[string]*Transaction + return marshalled["transaction"], c.executeRequestAndMarshal(req, &marshalled) +} + // executeRequestAndMarshal sets the correct headers, then executes the request and tries to marshal // the response from the body into the given interface{} value func (c Client) executeRequestAndMarshal(req *http.Request, value interface{}) error { diff --git a/paylike_test.go b/paylike_test.go index 99931b3..f224682 100644 --- a/paylike_test.go +++ b/paylike_test.go @@ -343,9 +343,42 @@ func TestCreateTransaction(t *testing.T) { TransactionID: "560fd96b7973ff3d2362a78c", Currency: "EUR", Amount: 200, - Custom: struct{ Source string }{"go client test"}, + Custom: map[string]string{"source": "test"}, } data, err := client.CreateTransaction("55006bdfe0308c4cbfdbd0e1", dto) assert.Nil(t, err) assert.NotEmpty(t, data) } + +func TestListTransactions(t *testing.T) { + client := NewClient(TestKey).SetKey("4ff7de37-dddf-4e51-8cc9-48b61a102923") + transactions, err := client.ListTransactions("55006bdfe0308c4cbfdbd0e1", 20) + assert.Nil(t, err) + assert.NotEmpty(t, transactions) + assert.Len(t, transactions, 20) +} + +func TestCaptureTransactions(t *testing.T) { + client := NewClient(TestKey).SetKey("4ff7de37-dddf-4e51-8cc9-48b61a102923") + transactionDTO := TransactionDTO{ + TransactionID: "560fd96b7973ff3d2362a78c", + Currency: "EUR", + Amount: 200, + Custom: map[string]string{"source": "test"}, + } + data, err := client.CreateTransaction("55006bdfe0308c4cbfdbd0e1", transactionDTO) + assert.Nil(t, err) + assert.NotEmpty(t, data) + + captureDTO := TransactionCaptureDTO{ + Amount: 2, + Currency: "EUR", + Descriptor: "Testing", + } + transaction, err := client.CaptureTransaction(data.ID, captureDTO) + assert.Nil(t, err) + assert.NotEmpty(t, transaction) + assert.Len(t, transaction.Trail, 1) + assert.Equal(t, transaction.Trail[0].Amount, captureDTO.Amount) + assert.Equal(t, transaction.Trail[0].Descriptor, captureDTO.Descriptor) +} From 384ddd496f1d1cf52a0c3cd9c26cf699f555e3af Mon Sep 17 00:00:00 2001 From: Imre 'Rover' Racz Date: Tue, 19 Mar 2019 16:58:26 +0100 Subject: [PATCH 11/14] feat(transaction): Refund and void --- paylike.go | 56 +++++++++++++++++++++++++++++++++++----- paylike_test.go | 68 +++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 116 insertions(+), 8 deletions(-) diff --git a/paylike.go b/paylike.go index bc7bc71..1ed296c 100644 --- a/paylike.go +++ b/paylike.go @@ -162,11 +162,11 @@ type TransactionID struct { ID string `json:"id"` } -// TransactionCaptureDTO describes information about the the capturing amount -type TransactionCaptureDTO struct { - Amount int `json:"amount"` // required, amount in minor units (100 = DKK 1,00) - Currency string `json:"currency"` // optional, expected currency (for additional verification) - Descriptor string `json:"descriptor"` // optional, text on client bank statement +// TransactionTrailDTO describes information about the the capturing / refunding / voiding amount +type TransactionTrailDTO struct { + Amount int `json:"amount"` // required, amount in minor units (100 = DKK 1,00) + Currency string `json:"currency,omitempty"` // optional, expected currency (for additional verification) + Descriptor string `json:"descriptor,omitempty"` // optional, text on client bank statement } // CardCode describes if a given code is present to the card or not @@ -347,7 +347,7 @@ func (c Client) ListTransactions(merchantID string, limit int) ([]*Transaction, // CaptureTransaction captures a new amount for the given transaction // https://github.com/paylike/api-docs#capture-a-transaction -func (c Client) CaptureTransaction(transactionID string, dto TransactionCaptureDTO) (*Transaction, error) { +func (c Client) CaptureTransaction(transactionID string, dto TransactionTrailDTO) (*Transaction, error) { b, err := json.Marshal(dto) if err != nil { return nil, err @@ -355,6 +355,26 @@ func (c Client) CaptureTransaction(transactionID string, dto TransactionCaptureD return c.captureTransaction(transactionID, bytes.NewBuffer(b)) } +// RefundTransaction refunds a given amount for the given transaction +// https://github.com/paylike/api-docs#refund-a-transaction +func (c Client) RefundTransaction(transactionID string, dto TransactionTrailDTO) (*Transaction, error) { + b, err := json.Marshal(dto) + if err != nil { + return nil, err + } + return c.refundTransaction(transactionID, bytes.NewBuffer(b)) +} + +// VoidTransaction cancels a given amount completely or partially +// https://github.com/paylike/api-docs#void-a-transaction +func (c Client) VoidTransaction(transactionID string, dto TransactionTrailDTO) (*Transaction, error) { + b, err := json.Marshal(dto) + if err != nil { + return nil, err + } + return c.voidTransaction(transactionID, bytes.NewBuffer(b)) +} + // getURL is to build the base API url along with the given dynamic route path func (c Client) getURL(url string) string { return fmt.Sprintf("%s%s", c.baseAPI, url) @@ -553,6 +573,30 @@ func (c Client) captureTransaction(transactionID string, body io.Reader) (*Trans return marshalled["transaction"], c.executeRequestAndMarshal(req, &marshalled) } +// refundTransaction handles the underlying logic of executing the API requests +// towards the merchant API and refunds a given amount for a given transaction +func (c Client) refundTransaction(transactionID string, body io.Reader) (*Transaction, error) { + path := fmt.Sprintf("/transactions/%s/refunds", transactionID) + req, err := http.NewRequest("POST", c.getURL(path), body) + if err != nil { + return nil, err + } + var marshalled map[string]*Transaction + return marshalled["transaction"], c.executeRequestAndMarshal(req, &marshalled) +} + +// voidTransaction handles the underlying logic of executing the API requests +// towards the merchant API and cancels a given amount payment partially or completely +func (c Client) voidTransaction(transactionID string, body io.Reader) (*Transaction, error) { + path := fmt.Sprintf("/transactions/%s/voids", transactionID) + req, err := http.NewRequest("POST", c.getURL(path), body) + if err != nil { + return nil, err + } + var marshalled map[string]*Transaction + return marshalled["transaction"], c.executeRequestAndMarshal(req, &marshalled) +} + // executeRequestAndMarshal sets the correct headers, then executes the request and tries to marshal // the response from the body into the given interface{} value func (c Client) executeRequestAndMarshal(req *http.Request, value interface{}) error { diff --git a/paylike_test.go b/paylike_test.go index f224682..9dd3c1d 100644 --- a/paylike_test.go +++ b/paylike_test.go @@ -358,7 +358,7 @@ func TestListTransactions(t *testing.T) { assert.Len(t, transactions, 20) } -func TestCaptureTransactions(t *testing.T) { +func TestCaptureTransaction(t *testing.T) { client := NewClient(TestKey).SetKey("4ff7de37-dddf-4e51-8cc9-48b61a102923") transactionDTO := TransactionDTO{ TransactionID: "560fd96b7973ff3d2362a78c", @@ -370,7 +370,7 @@ func TestCaptureTransactions(t *testing.T) { assert.Nil(t, err) assert.NotEmpty(t, data) - captureDTO := TransactionCaptureDTO{ + captureDTO := TransactionTrailDTO{ Amount: 2, Currency: "EUR", Descriptor: "Testing", @@ -382,3 +382,67 @@ func TestCaptureTransactions(t *testing.T) { assert.Equal(t, transaction.Trail[0].Amount, captureDTO.Amount) assert.Equal(t, transaction.Trail[0].Descriptor, captureDTO.Descriptor) } + +func TestRefundTransaction(t *testing.T) { + client := NewClient(TestKey).SetKey("4ff7de37-dddf-4e51-8cc9-48b61a102923") + transactionDTO := TransactionDTO{ + TransactionID: "560fd96b7973ff3d2362a78c", + Currency: "EUR", + Amount: 200, + Custom: map[string]string{"source": "test"}, + } + data, err := client.CreateTransaction("55006bdfe0308c4cbfdbd0e1", transactionDTO) + assert.Nil(t, err) + assert.NotEmpty(t, data) + + captureDTO := TransactionTrailDTO{ + Amount: 2, + Currency: "EUR", + Descriptor: "Testing Capture", + } + transaction, err := client.CaptureTransaction(data.ID, captureDTO) + assert.Nil(t, err) + assert.NotEmpty(t, transaction) + + refundDTO := TransactionTrailDTO{ + Amount: 1, + Descriptor: "Testing Refund", + } + transaction, err = client.RefundTransaction(data.ID, refundDTO) + assert.Nil(t, err) + assert.NotEmpty(t, transaction) + assert.Len(t, transaction.Trail, 2) + assert.Equal(t, transaction.Trail[1].Amount, refundDTO.Amount) + assert.Equal(t, transaction.Trail[1].Descriptor, refundDTO.Descriptor) +} + +func TestVoidTransaction(t *testing.T) { + client := NewClient(TestKey).SetKey("4ff7de37-dddf-4e51-8cc9-48b61a102923") + transactionDTO := TransactionDTO{ + TransactionID: "560fd96b7973ff3d2362a78c", + Currency: "EUR", + Amount: 200, + Custom: map[string]string{"source": "test"}, + } + data, err := client.CreateTransaction("55006bdfe0308c4cbfdbd0e1", transactionDTO) + assert.Nil(t, err) + assert.NotEmpty(t, data) + + captureDTO := TransactionTrailDTO{ + Amount: 2, + Currency: "EUR", + Descriptor: "Testing Capture", + } + transaction, err := client.CaptureTransaction(data.ID, captureDTO) + assert.Nil(t, err) + assert.NotEmpty(t, transaction) + + voidDTO := TransactionTrailDTO{ + Amount: 1, + } + transaction, err = client.VoidTransaction(data.ID, voidDTO) + assert.Nil(t, err) + assert.NotEmpty(t, transaction) + assert.Len(t, transaction.Trail, 2) + assert.Equal(t, transaction.Trail[1].Amount, voidDTO.Amount) +} From 29bbbe3d1fe9f10f3964792fc3eadc5ab8607e38 Mon Sep 17 00:00:00 2001 From: Imre 'Rover' Racz Date: Tue, 19 Mar 2019 17:13:24 +0100 Subject: [PATCH 12/14] fix(transaction): Custom fields are now handled better --- paylike.go | 74 +++++++++++++++++++++++++++++++++---------------- paylike_test.go | 37 ++++++++++++++++++++++--- 2 files changed, 83 insertions(+), 28 deletions(-) diff --git a/paylike.go b/paylike.go index 1ed296c..0c4a8c7 100644 --- a/paylike.go +++ b/paylike.go @@ -148,12 +148,12 @@ type Line struct { // TransactionDTO describes options in terms of the transaction // creation API type TransactionDTO struct { - CardID string `json:"cardId,omitempty"` // required if no TransactionID is present - TransactionID string `json:"transactionId,omitempty"` // required if no CardID is present - Descriptor string `json:"descriptor,omitempty"` // optional, will fallback to merchant descriptor - Currency string `json:"currency"` // required, three letter ISO - Amount int `json:"amount"` // required, amount in minor units - Custom map[string]string `json:"custom,omitempty"` // optional, any custom data + CardID string `json:"cardId,omitempty"` // required if no TransactionID is present + TransactionID string `json:"transactionId,omitempty"` // required if no CardID is present + Descriptor string `json:"descriptor,omitempty"` // optional, will fallback to merchant descriptor + Currency string `json:"currency"` // required, three letter ISO + Amount int `json:"amount"` // required, amount in minor units + Custom map[string]interface{} `json:"custom,omitempty"` // optional, any custom data } @@ -186,24 +186,24 @@ type Card struct { // Transaction describes information about a given transaction type Transaction struct { TransactionID - Test bool `json:"test"` - MerchantID string `json:"merchantId"` - Created string `json:"created"` - Amount int `json:"amount"` - RefundedAmount int `json:"refundedAmount"` - CapturedAmount int `json:"capturedAmount"` - VoidedAmount int `json:"voidedAmount"` - PendingAmount int `json:"pendingAmount"` - DisputedAmount int `json:"disputedAmount"` - Card Card `json:"card"` - TDS string `json:"tds"` - Currency string `json:"currency"` - Custom map[string]string `json:"custom"` - Recurring bool `json:"recurring"` - Successful bool `json:"successful"` - Error bool `json:"error"` - Descriptor string `json:"descriptor"` - Trail []*TransactionTrail `json:"trail"` + Test bool `json:"test"` + MerchantID string `json:"merchantId"` + Created string `json:"created"` + Amount int `json:"amount"` + RefundedAmount int `json:"refundedAmount"` + CapturedAmount int `json:"capturedAmount"` + VoidedAmount int `json:"voidedAmount"` + PendingAmount int `json:"pendingAmount"` + DisputedAmount int `json:"disputedAmount"` + Card Card `json:"card"` + TDS string `json:"tds"` + Currency string `json:"currency"` + Custom map[string]interface{} `json:"custom"` + Recurring bool `json:"recurring"` + Successful bool `json:"successful"` + Error bool `json:"error"` + Descriptor string `json:"descriptor"` + Trail []*TransactionTrail `json:"trail"` } // TransactionTrailFee describes fee included in the given trail @@ -221,6 +221,14 @@ type TransactionTrail struct { Capture bool `json:"captrue"` Descriptor string `json:"descriptor"` LineID string `json:"lineId"` + Dispute TrailDispute `json:"dispute"` +} + +// TrailDispute describes a given dispute in a given trail +type TrailDispute struct { + ID string `json:"id"` + Won bool `json:"won,omitempty"` + Lost bool `json:"lost,omitempty"` } // NewClient creates a new client @@ -375,6 +383,12 @@ func (c Client) VoidTransaction(transactionID string, dto TransactionTrailDTO) ( return c.voidTransaction(transactionID, bytes.NewBuffer(b)) } +// FindTransaction finds the given transaction by ID +// https://github.com/paylike/api-docs#fetch-a-transaction +func (c Client) FindTransaction(transactionID string) (*Transaction, error) { + return c.findTransaction(transactionID) +} + // getURL is to build the base API url along with the given dynamic route path func (c Client) getURL(url string) string { return fmt.Sprintf("%s%s", c.baseAPI, url) @@ -597,6 +611,18 @@ func (c Client) voidTransaction(transactionID string, body io.Reader) (*Transact return marshalled["transaction"], c.executeRequestAndMarshal(req, &marshalled) } +// findTransaction handles the underlying logic of executing the API requests +// towards the merchant API and tries to search for a given transaction +func (c Client) findTransaction(transactionID string) (*Transaction, error) { + path := fmt.Sprintf("/transactions/%s", transactionID) + req, err := http.NewRequest("GET", c.getURL(path), nil) + if err != nil { + return nil, err + } + var marshalled map[string]*Transaction + return marshalled["transaction"], c.executeRequestAndMarshal(req, &marshalled) +} + // executeRequestAndMarshal sets the correct headers, then executes the request and tries to marshal // the response from the body into the given interface{} value func (c Client) executeRequestAndMarshal(req *http.Request, value interface{}) error { diff --git a/paylike_test.go b/paylike_test.go index 9dd3c1d..6262c15 100644 --- a/paylike_test.go +++ b/paylike_test.go @@ -343,7 +343,7 @@ func TestCreateTransaction(t *testing.T) { TransactionID: "560fd96b7973ff3d2362a78c", Currency: "EUR", Amount: 200, - Custom: map[string]string{"source": "test"}, + Custom: map[string]interface{}{"source": "test"}, } data, err := client.CreateTransaction("55006bdfe0308c4cbfdbd0e1", dto) assert.Nil(t, err) @@ -364,7 +364,7 @@ func TestCaptureTransaction(t *testing.T) { TransactionID: "560fd96b7973ff3d2362a78c", Currency: "EUR", Amount: 200, - Custom: map[string]string{"source": "test"}, + Custom: map[string]interface{}{"source": "test"}, } data, err := client.CreateTransaction("55006bdfe0308c4cbfdbd0e1", transactionDTO) assert.Nil(t, err) @@ -389,7 +389,7 @@ func TestRefundTransaction(t *testing.T) { TransactionID: "560fd96b7973ff3d2362a78c", Currency: "EUR", Amount: 200, - Custom: map[string]string{"source": "test"}, + Custom: map[string]interface{}{"source": "test"}, } data, err := client.CreateTransaction("55006bdfe0308c4cbfdbd0e1", transactionDTO) assert.Nil(t, err) @@ -422,7 +422,7 @@ func TestVoidTransaction(t *testing.T) { TransactionID: "560fd96b7973ff3d2362a78c", Currency: "EUR", Amount: 200, - Custom: map[string]string{"source": "test"}, + Custom: map[string]interface{}{"source": "test"}, } data, err := client.CreateTransaction("55006bdfe0308c4cbfdbd0e1", transactionDTO) assert.Nil(t, err) @@ -446,3 +446,32 @@ func TestVoidTransaction(t *testing.T) { assert.Len(t, transaction.Trail, 2) assert.Equal(t, transaction.Trail[1].Amount, voidDTO.Amount) } + +func TestFindTransaction(t *testing.T) { + client := NewClient(TestKey).SetKey("4ff7de37-dddf-4e51-8cc9-48b61a102923") + transactionDTO := TransactionDTO{ + TransactionID: "560fd96b7973ff3d2362a78c", + Currency: "EUR", + Amount: 200, + Custom: map[string]interface{}{"source": "test"}, + } + data, err := client.CreateTransaction("55006bdfe0308c4cbfdbd0e1", transactionDTO) + assert.Nil(t, err) + assert.NotEmpty(t, data) + + captureDTO := TransactionTrailDTO{ + Amount: 2, + Currency: "EUR", + Descriptor: "Testing Capture", + } + transaction, err := client.CaptureTransaction(data.ID, captureDTO) + assert.Nil(t, err) + assert.NotEmpty(t, transaction) + + foundTransaction, err := client.FindTransaction(data.ID) + assert.Nil(t, err) + assert.NotEmpty(t, transaction) + assert.Len(t, foundTransaction.Trail, 1) + assert.Equal(t, foundTransaction.Trail[0].Amount, captureDTO.Amount) + assert.Equal(t, transaction, foundTransaction) +} From 11b7427c0b8b24501d7e062834128a9c4683b5d9 Mon Sep 17 00:00:00 2001 From: Imre 'Rover' Racz Date: Tue, 19 Mar 2019 17:31:27 +0100 Subject: [PATCH 13/14] feat(card): Card endpoints are now available --- paylike.go | 65 ++++++++++++++++++++++++++++++++++++++++++++++--- paylike_test.go | 15 ++++++++++++ 2 files changed, 77 insertions(+), 3 deletions(-) diff --git a/paylike.go b/paylike.go index 0c4a8c7..508d2c3 100644 --- a/paylike.go +++ b/paylike.go @@ -174,8 +174,8 @@ type CardCode struct { Present bool `json:"present"` } -// Card describes card information that can be found in transactions -type Card struct { +// TransactionCard describes card information that can be found in transactions +type TransactionCard struct { Bin string `json:"bin"` Last4 string `json:"last4"` Expiry string `json:"expiry"` @@ -195,7 +195,7 @@ type Transaction struct { VoidedAmount int `json:"voidedAmount"` PendingAmount int `json:"pendingAmount"` DisputedAmount int `json:"disputedAmount"` - Card Card `json:"card"` + Card TransactionCard `json:"card"` TDS string `json:"tds"` Currency string `json:"currency"` Custom map[string]interface{} `json:"custom"` @@ -231,6 +231,25 @@ type TrailDispute struct { Lost bool `json:"lost,omitempty"` } +// Card describes the full information about a given card +type Card struct { + TransactionCard + CardID + MerchantID string `json:"merchantId"` + Created string `json:"created"` +} + +// CardDTO describes required information to create a new card +type CardDTO struct { + TransactionID string `json:"transactionId"` + Notes string `json:"notes"` +} + +// CardID describes a given card's ID +type CardID struct { + ID string `json:"id"` +} + // NewClient creates a new client func NewClient(key string) *Client { return &Client{key, &http.Client{}, "https://api.paylike.io"} @@ -389,6 +408,22 @@ func (c Client) FindTransaction(transactionID string) (*Transaction, error) { return c.findTransaction(transactionID) } +// FetchCard finds the given card by ID +// https://github.com/paylike/api-docs#fetch-a-card +func (c Client) FetchCard(cardID string) (*Card, error) { + return c.fetchCard(cardID) +} + +// CreateCard saves a new record for a given card +// https://github.com/paylike/api-docs#save-a-card +func (c Client) CreateCard(merchantID string, dto CardDTO) (*CardID, error) { + b, err := json.Marshal(dto) + if err != nil { + return nil, err + } + return c.createCard(merchantID, bytes.NewBuffer(b)) +} + // getURL is to build the base API url along with the given dynamic route path func (c Client) getURL(url string) string { return fmt.Sprintf("%s%s", c.baseAPI, url) @@ -623,6 +658,30 @@ func (c Client) findTransaction(transactionID string) (*Transaction, error) { return marshalled["transaction"], c.executeRequestAndMarshal(req, &marshalled) } +// fetchCard handles the underlying logic of executing the API requests +// towards the cards API and tries to find a given card by ID +func (c Client) fetchCard(cardID string) (*Card, error) { + path := fmt.Sprintf("/cards/%s", cardID) + req, err := http.NewRequest("GET", c.getURL(path), nil) + if err != nil { + return nil, err + } + var marshalled map[string]*Card + return marshalled["card"], c.executeRequestAndMarshal(req, &marshalled) +} + +// createCard handles the underlying logic of executing the API requests +// towards the cards API and tries to find a given card by ID +func (c Client) createCard(merchantID string, body io.Reader) (*CardID, error) { + path := fmt.Sprintf("/merchants/%s/cards", merchantID) + req, err := http.NewRequest("POST", c.getURL(path), body) + if err != nil { + return nil, err + } + var marshalled map[string]*CardID + return marshalled["card"], c.executeRequestAndMarshal(req, &marshalled) +} + // executeRequestAndMarshal sets the correct headers, then executes the request and tries to marshal // the response from the body into the given interface{} value func (c Client) executeRequestAndMarshal(req *http.Request, value interface{}) error { diff --git a/paylike_test.go b/paylike_test.go index 6262c15..14605d6 100644 --- a/paylike_test.go +++ b/paylike_test.go @@ -475,3 +475,18 @@ func TestFindTransaction(t *testing.T) { assert.Equal(t, foundTransaction.Trail[0].Amount, captureDTO.Amount) assert.Equal(t, transaction, foundTransaction) } + +func TestFetchCard(t *testing.T) { + client := NewClient(TestKey).SetKey("4ff7de37-dddf-4e51-8cc9-48b61a102923") + dto := CardDTO{ + TransactionID: "560fd96b7973ff3d2362a78c", + } + data, err := client.CreateCard("55006bdfe0308c4cbfdbd0e1", dto) + assert.Nil(t, err) + assert.NotEmpty(t, data) + + card, err := client.FetchCard(data.ID) + assert.Nil(t, err) + assert.NotEmpty(t, card) + assert.Equal(t, card.ID, data.ID) +} From a356679e549fef84b3c56143dcc6e0ce619a727f Mon Sep 17 00:00:00 2001 From: Imre 'Rover' Racz Date: Tue, 19 Mar 2019 18:24:07 +0100 Subject: [PATCH 14/14] refactor(cleanup): Cleaned up some tests --- README.md | 141 +++++++++++++++++++++++++++++++++++++++++++++++- paylike.go | 59 +++++++++----------- paylike_test.go | 63 +++++++++++----------- 3 files changed, 197 insertions(+), 66 deletions(-) diff --git a/README.md b/README.md index 2aeee83..08f4a06 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,139 @@ -# go-api -Golang SDK for Paylike API +# Paylike client (Go) + +Writing your own client? Checkout the raw [HTTP service](https://github.com/paylike/api-docs). + +**Make sure to [subscribe to our mailling list](http://eepurl.com/bCGmg1) for +deprecation notices, API changes and new features** + +[Godoc for the project](https://godoc.org/github.com/paylike/go-api) + +## Getting an API key + +An API key can be obtained by creating a merchant and adding an app through +our [dashboard](https://app.paylike.io). If your app's target audience is +third parties, please reach out and we will make your app's API key hidden. + +## Install + +```shell +dep ensure -add github.com/paylike/node-api +``` + +```golang +import paylike "github.com/paylike/go-api" + +client := paylike.NewClient(os.Getenv("PAYLIKE_APP_KEY")) +``` + +## Methods + +```golang +// change key for authentication +client.SetKey("key") + +// this command is also chainable +app, err := client.SetKey("key").FetchApp() + +// create an app (requires no authentication) +createdApp, err := client.CreateApp() + +// create an app with a dedicated name +createdAppWithName, err := client.CreateAppWithName("myApplication") + +// fetch current app (based on key) +app, err := client.FetchApp() + +// list app's merchants with limit +merchants, err := client.FetchMerchants("appID", 10) + +// create merchant +merchant, err := client.CreateMerchant(MerchantCreateDTO{ + Test: true, + Currency: "HUF", + Email: TestEmail, + Website: TestSite, + Descriptor: "1234567897891234", + Company: &MerchantCompany{ + Country: "HU", + }, +}) + +// update merchant +err := client.UpdateMerchant(MerchantUpdateDTO{ + Name: "Test", + Descriptor: "Test", + Email: "test@test.com", +}) + +// get merchant +fetchedMerchant, err := client.GetMerchant(merchant.ID) + +// add users +data, err := client.InviteUserToMerchant(merchant.ID, "test@test.com") + +// revoke users +err := client.RevokeUserFromMerchant(merchant.ID, users[0].ID) + +// fetch users with limit +users, err := client.FetchUsersToMerchant(merchant.ID, 3) + +// add app +err := client.AddAppToMerchant(merchant.ID, app.ID) + +// revoke app +err := client.RevokeAppFromMerchant(merchant.ID, app.ID) + +// fetch apps with limit +apps, err := client.FetchAppsToMerchant(merchant.ID, 2) + +// fetch lines with limit +lines, err := client.FetchLinesToMerchant(merchant.ID, 1) + +// create transaction +data, err := client.CreateTransaction(TestMerchant, TransactionDTO{ + TransactionID: "560fd96b7973ff3d2362a78c", + Currency: "EUR", + Amount: 200, + Custom: map[string]interface{}{"source": "test"}, +}) + +// fetch transactions with limit +transactions, err := client.ListTransactions(merchant.ID, 20) + +// transaction capture +dto := TransactionTrailDTO{ + Amount: 2, + Currency: "EUR", + Descriptor: "Testing", +} +transaction, err := client.CaptureTransaction(transaction.ID, dto) + +// transaction refund +dto := TransactionTrailDTO{ + Amount: 1, + Descriptor: "Testing Refund", +} +transaction, err := client.RefundTransaction(data.ID, dto) + +// transaction void +dto := TransactionTrailDTO{ + Amount: 1, +} +transaction, err := client.VoidTransaction(data.ID, dto) + +// transaction find +transaction, err := client.FindTransaction(data.ID) + +// card create +dto := CardDTO{ + TransactionID: "560fd96b7973ff3d2362a78c", +} +data, err := client.CreateCard(TestMerchant, dto) + +// card find +card, err := client.FetchCard(data.ID) +``` + +A webshop would typically need only `CaptureTransaction`, `RefundTransaction` and `VoidTransaction`. Some might +as well use `ListTransactions` and for recurring subscriptions +`CreateTransaction`. diff --git a/paylike.go b/paylike.go index 508d2c3..e07d216 100644 --- a/paylike.go +++ b/paylike.go @@ -57,36 +57,36 @@ type InviteUserToMerchantResponse struct { IsMember bool } -// Amount ... -type Amount struct { +// PricingAmount describes the currency and the amount +type PricingAmount struct { Currency string Amount float64 } -// MerchantTransfer ... +// MerchantTransfer describes a transfer to a given card type MerchantTransfer struct { ToCard Pricing } -// Pricing ... +// Pricing describes the exact amounts for a given item type Pricing struct { Rate float64 - Flat Amount - Dispute Amount + Flat PricingAmount + Dispute PricingAmount } -// MerchantPricing ... +// MerchantPricing describes a pricing included in the merchant type MerchantPricing struct { Pricing Transfer MerchantTransfer } -// MerchantTDS ... +// MerchantTDS either "attempt" or "full" based on 3-D secure type MerchantTDS struct { Mode string } -// Merchant ... +// Merchant describes information about a given merchant type Merchant struct { ID string Name string @@ -105,7 +105,7 @@ type Merchant struct { Balance float64 } -// MerchantClaim ... +// MerchantClaim describes claims for a given merchant type MerchantClaim struct { CanChargeCard bool CanSaveCard bool @@ -121,28 +121,28 @@ type User struct { Email string `json:"email"` } -// MerchantCompany ... +// MerchantCompany describes the company of a given merchant type MerchantCompany struct { Country string `json:"country"` // required, ISO 3166 code (e.g. DK) Number string `json:"number,omitempty"` // optional, registration number ("CVR" in Denmark) } -// MerchantBank ... +// MerchantBank describes a bank for a given merchant type MerchantBank struct { Iban string `json:"iban,omitempty"` // optional, (format: XX00000000, XX is country code, length varies) } // Line desccribes a given item in the history of the merchant balance type Line struct { - ID string `json:"id"` - Created string `json:"created"` - MerchantID string `json:"merchantId"` - Balance int `json:"balance"` - Fee int `json:"fee"` - TransactionID string `json:"transactionId"` - Amount Amount `json:"amount"` - Refund bool `json:"refund"` - Test bool `json:"test"` + ID string `json:"id"` + Created string `json:"created"` + MerchantID string `json:"merchantId"` + Balance int `json:"balance"` + Fee int `json:"fee"` + TransactionID string `json:"transactionId"` + Amount PricingAmount `json:"amount"` + Refund bool `json:"refund"` + Test bool `json:"test"` } // TransactionDTO describes options in terms of the transaction @@ -276,10 +276,10 @@ func (c Client) CreateAppWithName(name string) (*App, error) { ) } -// GetCurrentApp is to fetch information about the current application +// FetchApp is to fetch information about the current application // https://api.paylike.io/me -func (c Client) GetCurrentApp() (*Identity, error) { - return c.getCurrentApp() +func (c Client) FetchApp() (*Identity, error) { + return c.fetchApp() } // CreateMerchant creates a new merchant under a given app @@ -441,9 +441,9 @@ func (c Client) createApp(body io.Reader) (*App, error) { return marshalled["app"], err } -// getCurrentApp handles the underlying logic of executing the API requests +// fetchApp handles the underlying logic of executing the API requests // towards the app API to get the currently used app -func (c Client) getCurrentApp() (*Identity, error) { +func (c Client) fetchApp() (*Identity, error) { req, err := http.NewRequest("GET", c.getURL("/me"), nil) if err != nil { return nil, err @@ -696,15 +696,8 @@ func (c Client) executeRequestAndMarshal(req *http.Request, value interface{}) e if err != nil { return err } - c.exploreResponse(resp, b) if len(b) == 0 { return nil } return json.Unmarshal(b, &value) } - -// Temporary function -func (c Client) exploreResponse(resp *http.Response, b []byte) { - fmt.Println(resp.Status) - fmt.Println(string(b)) -} diff --git a/paylike_test.go b/paylike_test.go index 14605d6..1a7c55e 100644 --- a/paylike_test.go +++ b/paylike_test.go @@ -7,12 +7,13 @@ import ( "github.com/stretchr/testify/assert" ) -const TestKey = "4c8453c3-8285-4984-ab72-216e324372e6" +const TestKey = "4ff7de37-dddf-4e51-8cc9-48b61a102923" const TestEmail = "john@example.com" const TestSite = "https://example.com" +const TestMerchant = "55006bdfe0308c4cbfdbd0e1" func TestCreateApp(t *testing.T) { - client := NewClient(TestKey) + client := NewClient("") app, err := client.CreateApp() assert.Nil(t, err) assert.NotEmpty(t, app) @@ -20,7 +21,7 @@ func TestCreateApp(t *testing.T) { } func TestCreateAppWithName(t *testing.T) { - client := NewClient(TestKey) + client := NewClient("") app, err := client.CreateAppWithName("Macilaci") assert.Nil(t, err) assert.NotEmpty(t, app) @@ -28,18 +29,18 @@ func TestCreateAppWithName(t *testing.T) { } func TestGetApp(t *testing.T) { - client := NewClient(TestKey) + client := NewClient("") app, err := client.CreateApp() assert.Nil(t, err) assert.NotEmpty(t, app) - identity, err := client.SetKey(app.Key).GetCurrentApp() + identity, err := client.SetKey(app.Key).FetchApp() assert.Nil(t, err) assert.NotEmpty(t, identity) } func TestCreateMerchant(t *testing.T) { - client := NewClient(TestKey) + client := NewClient("") app, err := client.CreateApp() assert.Nil(t, err) assert.NotEmpty(t, app) @@ -60,7 +61,7 @@ func TestCreateMerchant(t *testing.T) { } func TestFetchMerchants(t *testing.T) { - client := NewClient(TestKey) + client := NewClient("") app, err := client.CreateApp() assert.Nil(t, err) assert.NotEmpty(t, app) @@ -86,7 +87,7 @@ func TestFetchMerchants(t *testing.T) { } func TestGetMerchant(t *testing.T) { - client := NewClient(TestKey) + client := NewClient("") app, err := client.CreateApp() assert.Nil(t, err) assert.NotEmpty(t, app) @@ -112,7 +113,7 @@ func TestGetMerchant(t *testing.T) { } func TestUpdateMerchant(t *testing.T) { - client := NewClient(TestKey) + client := NewClient("") app, err := client.CreateApp() assert.Nil(t, err) assert.NotEmpty(t, app) @@ -148,7 +149,7 @@ func TestUpdateMerchant(t *testing.T) { } func TestInviteUserToMerchant(t *testing.T) { - client := NewClient(TestKey) + client := NewClient("") app, err := client.CreateApp() assert.Nil(t, err) assert.NotEmpty(t, app) @@ -174,7 +175,7 @@ func TestInviteUserToMerchant(t *testing.T) { } func TestFetchUsersToMerchant(t *testing.T) { - client := NewClient(TestKey) + client := NewClient("") app, err := client.CreateApp() assert.Nil(t, err) assert.NotEmpty(t, app) @@ -205,7 +206,7 @@ func TestFetchUsersToMerchant(t *testing.T) { } func TestRevokeUserFromMerchant(t *testing.T) { - client := NewClient(TestKey) + client := NewClient("") app, err := client.CreateApp() assert.Nil(t, err) assert.NotEmpty(t, app) @@ -243,7 +244,7 @@ func TestRevokeUserFromMerchant(t *testing.T) { } func TestAddAppToMerchant(t *testing.T) { - client := NewClient(TestKey) + client := NewClient("") app, err := client.CreateApp() assert.Nil(t, err) assert.NotEmpty(t, app) @@ -268,7 +269,7 @@ func TestAddAppToMerchant(t *testing.T) { } func TestFetchAppsToMerchant(t *testing.T) { - client := NewClient(TestKey) + client := NewClient("") app, err := client.CreateApp() assert.Nil(t, err) assert.NotEmpty(t, app) @@ -298,7 +299,7 @@ func TestFetchAppsToMerchant(t *testing.T) { } func TestRevokeAppFromMerchant(t *testing.T) { - client := NewClient(TestKey) + client := NewClient("") app, err := client.CreateApp() assert.Nil(t, err) assert.NotEmpty(t, app) @@ -330,43 +331,43 @@ func TestRevokeAppFromMerchant(t *testing.T) { } func TestFetchLinesToMerchant(t *testing.T) { - client := NewClient(TestKey).SetKey("4ff7de37-dddf-4e51-8cc9-48b61a102923") - lines, err := client.FetchLinesToMerchant("55006bdfe0308c4cbfdbd0e1", 1) + client := NewClient(TestKey) + lines, err := client.FetchLinesToMerchant(TestMerchant, 1) assert.Nil(t, err) assert.NotEmpty(t, lines) assert.Len(t, lines, 1) } func TestCreateTransaction(t *testing.T) { - client := NewClient(TestKey).SetKey("4ff7de37-dddf-4e51-8cc9-48b61a102923") + client := NewClient(TestKey) dto := TransactionDTO{ TransactionID: "560fd96b7973ff3d2362a78c", Currency: "EUR", Amount: 200, Custom: map[string]interface{}{"source": "test"}, } - data, err := client.CreateTransaction("55006bdfe0308c4cbfdbd0e1", dto) + data, err := client.CreateTransaction(TestMerchant, dto) assert.Nil(t, err) assert.NotEmpty(t, data) } func TestListTransactions(t *testing.T) { - client := NewClient(TestKey).SetKey("4ff7de37-dddf-4e51-8cc9-48b61a102923") - transactions, err := client.ListTransactions("55006bdfe0308c4cbfdbd0e1", 20) + client := NewClient(TestKey) + transactions, err := client.ListTransactions(TestMerchant, 20) assert.Nil(t, err) assert.NotEmpty(t, transactions) assert.Len(t, transactions, 20) } func TestCaptureTransaction(t *testing.T) { - client := NewClient(TestKey).SetKey("4ff7de37-dddf-4e51-8cc9-48b61a102923") + client := NewClient(TestKey) transactionDTO := TransactionDTO{ TransactionID: "560fd96b7973ff3d2362a78c", Currency: "EUR", Amount: 200, Custom: map[string]interface{}{"source": "test"}, } - data, err := client.CreateTransaction("55006bdfe0308c4cbfdbd0e1", transactionDTO) + data, err := client.CreateTransaction(TestMerchant, transactionDTO) assert.Nil(t, err) assert.NotEmpty(t, data) @@ -384,14 +385,14 @@ func TestCaptureTransaction(t *testing.T) { } func TestRefundTransaction(t *testing.T) { - client := NewClient(TestKey).SetKey("4ff7de37-dddf-4e51-8cc9-48b61a102923") + client := NewClient(TestKey) transactionDTO := TransactionDTO{ TransactionID: "560fd96b7973ff3d2362a78c", Currency: "EUR", Amount: 200, Custom: map[string]interface{}{"source": "test"}, } - data, err := client.CreateTransaction("55006bdfe0308c4cbfdbd0e1", transactionDTO) + data, err := client.CreateTransaction(TestMerchant, transactionDTO) assert.Nil(t, err) assert.NotEmpty(t, data) @@ -417,14 +418,14 @@ func TestRefundTransaction(t *testing.T) { } func TestVoidTransaction(t *testing.T) { - client := NewClient(TestKey).SetKey("4ff7de37-dddf-4e51-8cc9-48b61a102923") + client := NewClient(TestKey) transactionDTO := TransactionDTO{ TransactionID: "560fd96b7973ff3d2362a78c", Currency: "EUR", Amount: 200, Custom: map[string]interface{}{"source": "test"}, } - data, err := client.CreateTransaction("55006bdfe0308c4cbfdbd0e1", transactionDTO) + data, err := client.CreateTransaction(TestMerchant, transactionDTO) assert.Nil(t, err) assert.NotEmpty(t, data) @@ -448,14 +449,14 @@ func TestVoidTransaction(t *testing.T) { } func TestFindTransaction(t *testing.T) { - client := NewClient(TestKey).SetKey("4ff7de37-dddf-4e51-8cc9-48b61a102923") + client := NewClient(TestKey) transactionDTO := TransactionDTO{ TransactionID: "560fd96b7973ff3d2362a78c", Currency: "EUR", Amount: 200, Custom: map[string]interface{}{"source": "test"}, } - data, err := client.CreateTransaction("55006bdfe0308c4cbfdbd0e1", transactionDTO) + data, err := client.CreateTransaction(TestMerchant, transactionDTO) assert.Nil(t, err) assert.NotEmpty(t, data) @@ -477,11 +478,11 @@ func TestFindTransaction(t *testing.T) { } func TestFetchCard(t *testing.T) { - client := NewClient(TestKey).SetKey("4ff7de37-dddf-4e51-8cc9-48b61a102923") + client := NewClient(TestKey) dto := CardDTO{ TransactionID: "560fd96b7973ff3d2362a78c", } - data, err := client.CreateCard("55006bdfe0308c4cbfdbd0e1", dto) + data, err := client.CreateCard(TestMerchant, dto) assert.Nil(t, err) assert.NotEmpty(t, data)