Repository: VictorAvelar/mollie-api-go (go)

Issue:
Implement Chargebacks API resource
**Description**
Implement the API endpoints related to the chargebacks resource and provide test coverage, as an example you might take a look at #6 

**Additional context**
The API reference for refunds can be found here: https://docs.mollie.com/reference/v2/chargebacks-api/


Pull request:
Feature/chargebacks resource implementation
<!--- Provide a general summary of your changes in the Title above -->

## Description

I have added a chargebacks resource with 3 functions:
`Get` - Gets a single chargeback for a given payment ID
`List` - Lists all chargebacks
`ListForPayment` - Lists all chargebacks for a given payment ID

## Motivation and context

Why is this change required? What problem does it solve?

This PR fixes #10

## How has this been tested?

I have added a unit-level test following the style of the payments test and have run a successful `go test -v -race ./...` to ensure no damage to existing functionality.

## Screenshots (if appropriate)

## Types of changes

What types of changes does your code introduce?
- [ ] Bug fix (non-breaking change which fixes an issue)
- [x] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to change)

## Checklist:

- [X] I have read the **[CONTRIBUTING](CONTRIBUTING.md)** document.
- [X] My pull request addresses exactly one patch/feature.
- [X] I have created a branch for this patch/feature.
- [X] Each individual commit in the pull request is meaningful.
- [X] I have added tests to cover my changes.
- [X] If my change requires a change to the documentation, I have updated it accordingly.

If you're unsure about any of these, don't hesitate to ask. We're here to help!


Patch:
diff --git a/README.md b/README.md
index 7aea67d..a4b3a81 100644
--- a/README.md
+++ b/README.md
@@ -24,3 +24,23 @@ To use the Mollie API client, the following things are required:
 + Up-to-date OpenSSL (or other SSL/TLS toolkit)
 
 For leveraging [Mollie Connect](https://docs.mollie.com/oauth/overview) (advanced use cases only), it is recommended to be familiar with the OAuth2 protocol.
+
+## Roadmap
+- [x] Project setup
+- [x] Base HTTP Client
+- [x] Authentication
+    - [x] API token
+    - [x] Organization token
+- [ ] Payments
+    - [ ] Recurring payments
+    - [ ] Multicurrency payments
+    - [ ] QR Codes
+    - [ ] Gift cards
+    - [ ] Webhook (Status changes)
+- [ ] Orders
+    - [ ] Discounts
+- [ ] Wallets
+    - [ ] Apple pay
+- [ ] Mollie Connect
+    - [ ] App fees
+    - [ ] Onboarding
\ No newline at end of file
diff --git a/docs/README.md b/docs/README.md
index 54762c4..2b0828f 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -86,21 +86,117 @@ type ApplicationFee struct {
 ApplicationFee allows you to split a payment between a platform and connected
 merchant accounts.
 
+#### type Chargeback
+```go
+type Chargeback struct {
+	Resource         string           `json:"resource,omitempty"`
+	ID               string           `json:"id,omitempty"`
+	Amount           *Amount          `json:"amount,omitempty"`
+	SettlementAmount *Amount          `json:"settlementAmount,omitempty"`
+	CreatedAt        *time.Time       `json:"createdAt,omitempty"`
+	ReversedAt       *time.Time       `json:"reversedAt,omitempty"`
+	ChargebackID     string           `json:"chargebackId,omitempty"`
+	Links            *ChargebackLinks `json:"_links,omitempty"`
+}
+```
+
+Chargeback describes a forced transaction reversal initiated by the cardholder's bank
+
+#### type ChargebackLinks
+
+```go
+type ChargebackLinks struct {
+	Self          URL `json:"self,omitempty"`
+	Chargeback    URL `json:"chargeback,omitempty"`
+	Settlement    URL `json:"settlement,omitempty"`
+	Documentation URL `json:"documentation,omitempty"`
+}
+```
+
+ChargebackLinks describes all the possible links to be returned with
+a chargeback object.
+
+#### type ChargebackList
+
+```go
+type ChargebackList struct {
+	Count    int `json:"count,omitempty"`
+	Embedded struct {
+		Chargebacks []Chargeback
+	} `json:"_embedded,omitempty"`
+	Links PaginationLinks `json:"_links,omitempty"`
+}
+```
+
+ChargebackList describes how a list of chargebacks will be retrieved by Mollie.
+
+#### type ChargebackOptions
+
+```go
+type ChargebackOptions struct {
+	Include string `url:"include,omitempty"`
+	Embed   string `url:"embed,omitempty"`
+}
+```
+
+ChargebackOptions describes chargeback endpoint valid query string parameters.
+
+See: https://docs.mollie.com/reference/v2/chargebacks-api/get-chargeback
+
+#### type ChargebacksService
+
+```go
+type ChargebacksService service
+```
+
+ChargebacksService instance operates over chargeback resources
+
+#### func (\*ChargebacksService) Get
+
+```go
+func (cs *ChargebacksService) Get(paymentID, chargebackID string, options *ChargebackOptions) (p Chargeback, err error)
+```
+
+Get retrieves a single chargeback by its ID. Note the original payment’s ID is needed as well.
+
+If you do not know the original payment’s ID, you can use the List function
+
+#### func (\*ChargebacksService) List
+
+```go
+func (cs *ChargebacksService) List(options *ListChargebackOptions) (pl ChargebackList, err error)
+```
+
+List retrieves a list of chargebacks associated with your account/organization.
+
+See: https://docs.mollie.com/reference/v2/chargebacks-api/list-chargebacks
+
+#### func (\*ChargebacksService) ListForPayment
+
+```go
+func (cs *ChargebacksService) ListForPayment(paymentID string, options *ListChargebackOptions) (pl ChargebackList, err error)
+```
+
+ListForPayment retrieves a list of chargebacks associated with a single payment.
+
+See: https://docs.mollie.com/reference/v2/chargebacks-api/list-chargebacks
+
 #### type Client
 
 ```go
 type Client struct {
 	BaseURL *url.URL
 
-	// Services
-	Payments *PaymentsService
-	Methods  *MethodsService
+  // Services
+	Payments    *PaymentsService
+	Chargebacks *ChargebacksService
+	Methods     *MethodsService
 }
 ```
 
 Client manages communication with Mollie's API.
 
-#### func  NewClient
+#### func NewClient
 
 ```go
 func NewClient(baseClient *http.Client, c *Config) (mollie *Client, err error)
@@ -115,7 +211,7 @@ to the provided Config object.
 You can also set the token values programmatically by using the Client
 WithAPIKey and WithOrganizationKey functions.
 
-#### func (*Client) Do
+#### func (\*Client) Do
 
 ```go
 func (c *Client) Do(req *http.Request) (*Response, error)
@@ -123,7 +219,7 @@ func (c *Client) Do(req *http.Request) (*Response, error)
 Do sends an API request and returns the API response or returned as an error if
 an API error has occurred.
 
-#### func (*Client) NewAPIRequest
+#### func (\*Client) NewAPIRequest
 
 ```go
 func (c *Client) NewAPIRequest(method string, uri string, body interface{}) (req *http.Request, err error)
@@ -133,7 +229,7 @@ NewAPIRequest is a wrapper around the http.NewRequest function.
 It will setup the authentication headers/parameters according to the client
 config.
 
-#### func (*Client) WithAuthenticationValue
+#### func (\*Client) WithAuthenticationValue
 
 ```go
 func (c *Client) WithAuthenticationValue(k string) error
@@ -178,7 +274,7 @@ HTTP request is shown in the status field of the HTTP response header, which
 contains standard HTTP status codes: - a 2xx code for success - a 4xx or 5xx
 code for failure
 
-#### func (*Error) Error
+#### func (\*Error) Error
 
 ```go
 func (e *Error) Error() string
@@ -240,6 +336,18 @@ type Image struct {
 
 Image describes a generic image resource retrieved by Mollie.
 
+#### type ListChargebackOptions
+
+```go
+type ListChargebackOptions struct {
+	Include   string `url:"include,omitempty"`
+	Embed     string `url:"embed,omitempty"`
+	ProfileID string `url:"profileId,omitempty"`
+}
+```
+
+ListChargebackOptions describes list chargebacks endpoint valid query string parameters.
+
 #### type ListMethods
 
 ```go
@@ -341,7 +449,7 @@ type MethodsService service
 
 MethodsService operates on methods endpoints
 
-#### func (*MethodsService) All
+#### func (\*MethodsService) All
 
 ```go
 func (ms *MethodsService) All(options *MethodsOptions) (pm *ListMethods, err error)
@@ -350,7 +458,7 @@ All retrieves all the payment methods enabled for your account/organization
 
 See: https://docs.mollie.com/reference/v2/methods-api/list-all-methods
 
-#### func (*MethodsService) Get
+#### func (\*MethodsService) Get
 
 ```go
 func (ms *MethodsService) Get(id string, options *MethodsOptions) (pmi *PaymentMethodInfo, err error)
@@ -361,7 +469,7 @@ parameters
 
 See: https://docs.mollie.com/reference/v2/methods-api/get-method
 
-#### func (*MethodsService) List
+#### func (\*MethodsService) List
 
 ```go
 func (ms *MethodsService) List(options *MethodsOptions) (pm *ListMethods, err error)
@@ -606,7 +714,7 @@ type PaymentsService service
 
 PaymentsService instance operates over payment resources
 
-#### func (*PaymentsService) Cancel
+#### func (\*PaymentsService) Cancel
 
 ```go
 func (ps *PaymentsService) Cancel(id string) (p Payment, err error)
@@ -615,7 +723,7 @@ Cancel removes a payment (if possible) from your Mollie account.
 
 See: https://docs.mollie.com/reference/v2/payments-api/cancel-payment
 
-#### func (*PaymentsService) Create
+#### func (\*PaymentsService) Create
 
 ```go
 func (ps *PaymentsService) Create(p Payment) (np Payment, err error)
@@ -624,14 +732,14 @@ Create stores a new payment object attached to your Mollie account.
 
 See: https://docs.mollie.com/reference/v2/payments-api/create-payment#
 
-#### func (*PaymentsService) Get
+#### func (\*PaymentsService) Get
 
 ```go
 func (ps *PaymentsService) Get(id string, options *PaymentOptions) (p Payment, err error)
 ```
 Get retrieves a single payment object by its payment token.
 
-#### func (*PaymentsService) List
+#### func (\*PaymentsService) List
 
 ```go
 func (ps *PaymentsService) List(options *ListPaymentOptions) (pl PaymentList, err error)
@@ -640,7 +748,7 @@ List retrieves a list of payments associated with your account/organization.
 
 See: https://docs.mollie.com/reference/v2/payments-api/list-payments
 
-#### func (*PaymentsService) Update
+#### func (\*PaymentsService) Update
 
 ```go
 func (ps *PaymentsService) Update(id string, up Payment) (p Payment, err error)
@@ -708,7 +816,7 @@ type ShortDate struct {
 
 ShortDate is a string representing a date in YYYY-MM-DD format.
 
-#### func (*ShortDate) UnmarshalJSON
+#### func (\*ShortDate) UnmarshalJSON
 
 ```go
 func (d *ShortDate) UnmarshalJSON(b []byte) error
diff --git a/mollie/chargebacks.go b/mollie/chargebacks.go
new file mode 100644
index 0000000..f8627a7
--- /dev/null
+++ b/mollie/chargebacks.go
@@ -0,0 +1,127 @@
+package mollie
+
+import (
+	"encoding/json"
+	"fmt"
+	"net/http"
+	"time"
+
+	"github.com/google/go-querystring/query"
+)
+
+// Chargeback describes a forced transaction reversal initiated by the cardholder's bank
+type Chargeback struct {
+	Resource         string           `json:"resource,omitempty"`
+	ID               string           `json:"id,omitempty"`
+	Amount           *Amount          `json:"amount,omitempty"`
+	SettlementAmount *Amount          `json:"settlementAmount,omitempty"`
+	CreatedAt        *time.Time       `json:"createdAt,omitempty"`
+	ReversedAt       *time.Time       `json:"reversedAt,omitempty"`
+	ChargebackID     string           `json:"chargebackId,omitempty"`
+	Links            *ChargebackLinks `json:"_links,omitempty"`
+}
+
+// ChargebackLinks describes all the possible links to be returned with
+// a chargeback object.
+type ChargebackLinks struct {
+	Self          URL `json:"self,omitempty"`
+	Chargeback    URL `json:"chargeback,omitempty"`
+	Settlement    URL `json:"settlement,omitempty"`
+	Documentation URL `json:"documentation,omitempty"`
+}
+
+// ChargebackOptions describes chargeback endpoint valid query string parameters.
+//
+// See: https://docs.mollie.com/reference/v2/chargebacks-api/get-chargeback
+type ChargebackOptions struct {
+	Include string `url:"include,omitempty"`
+	Embed   string `url:"embed,omitempty"`
+}
+
+// ListChargebackOptions describes list chargebacks endpoint valid query string parameters.
+type ListChargebackOptions struct {
+	Include   string `url:"include,omitempty"`
+	Embed     string `url:"embed,omitempty"`
+	ProfileID string `url:"profileId,omitempty"`
+}
+
+// ChargebackList describes how a list of chargebacks will be retrieved by Mollie.
+type ChargebackList struct {
+	Count    int `json:"count,omitempty"`
+	Embedded struct {
+		Chargebacks []Chargeback
+	} `json:"_embedded,omitempty"`
+	Links PaginationLinks `json:"_links,omitempty"`
+}
+
+// ChargebacksService instance operates over chargeback resources
+type ChargebacksService service
+
+// Get retrieves a single chargeback by its ID. Note the original payment’s ID is needed as well.
+//
+//If you do not know the original payment’s ID, you can use the List function
+func (cs *ChargebacksService) Get(paymentID, chargebackID string, options *ChargebackOptions) (p Chargeback, err error) {
+	u := fmt.Sprintf("v2/payments/%s/chargebacks/%s", paymentID, chargebackID)
+	if options != nil {
+		v, _ := query.Values(options)
+		u = fmt.Sprintf("%s?%s", u, v.Encode())
+	}
+	req, err := cs.client.NewAPIRequest(http.MethodGet, u, nil)
+	if err != nil {
+		return
+	}
+	res, err := cs.client.Do(req)
+	if err != nil {
+		return
+	}
+	if err = json.Unmarshal(res.content, &p); err != nil {
+		return
+	}
+	return
+}
+
+// List retrieves a list of chargebacks associated with your account/organization.
+//
+// See: https://docs.mollie.com/reference/v2/chargebacks-api/list-chargebacks
+func (cs *ChargebacksService) List(options *ListChargebackOptions) (pl ChargebackList, err error) {
+	u := fmt.Sprint("v2/chargebacks")
+	if options != nil {
+		v, _ := query.Values(options)
+		u = fmt.Sprintf("%s?%s", u, v.Encode())
+	}
+	req, err := cs.client.NewAPIRequest(http.MethodGet, u, nil)
+	if err != nil {
+		return
+	}
+	res, err := cs.client.Do(req)
+	if err != nil {
+		return
+	}
+	if err = json.Unmarshal(res.content, &pl); err != nil {
+		return
+	}
+	return
+}
+
+// ListForPayment retrieves a list of chargebacks associated with a single payment.
+//
+// See: https://docs.mollie.com/reference/v2/chargebacks-api/list-chargebacks
+func (cs *ChargebacksService) ListForPayment(paymentID string, options *ListChargebackOptions) (pl ChargebackList, err error) {
+	u := fmt.Sprintf("v2/payments/%s/chargebacks", paymentID)
+	if options != nil {
+		v, _ := query.Values(options)
+		u = fmt.Sprintf("%s?%s", u, v.Encode())
+	}
+	req, err := cs.client.NewAPIRequest(http.MethodGet, u, nil)
+	if err != nil {
+		return
+	}
+	res, err := cs.client.Do(req)
+	if err != nil {
+		return
+	}
+	if err = json.Unmarshal(res.content, &pl); err != nil {
+		return
+	}
+	return
+}
diff --git a/mollie/mollie.go b/mollie/mollie.go
index fe3e8fc..6278e19 100644
--- a/mollie/mollie.go
+++ b/mollie/mollie.go
@@ -36,8 +36,9 @@ type Client struct {
 	common         service // Reuse a single struct instead of allocating one for each service on the heap.
 	config         *Config
 	// Services
-	Payments *PaymentsService
-	Methods  *MethodsService
+	Payments    *PaymentsService
+	Chargebacks *ChargebacksService
+	Methods     *MethodsService
 }
 
 type service struct {
@@ -147,6 +148,7 @@ func NewClient(baseClient *http.Client, c *Config) (mollie *Client, err error) {
 
 	// services for resources
 	mollie.Payments = (*PaymentsService)(&mollie.common)
+	mollie.Chargebacks = (*ChargebacksService)(&mollie.common)
 	mollie.Methods = (*MethodsService)(&mollie.common)
 
 	// Parse authorization from environment


Test patch:
diff --git a/mollie/chargebacks_test.go b/mollie/chargebacks_test.go
new file mode 100644
index 0000000..e17119a
--- /dev/null
+++ b/mollie/chargebacks_test.go
@@ -0,0 +1,226 @@
+package mollie
+
+import (
+	"net/http"
+	"net/url"
+	"strings"
+	"testing"
+
+	"github.com/VictorAvelar/mollie-api-go/testdata"
+)
+
+func TestChargebacksService_Get(t *testing.T) {
+	setup()
+	defer teardown()
+	paymentID := "tr_WDqYK6vllg"
+	chargebackID := "chb_n9z0tp"
+	_ = tClient.WithAuthenticationValue("test_token")
+	tMux.HandleFunc("/v2/payments/"+paymentID+"/chargebacks/"+chargebackID, func(w http.ResponseWriter, r *http.Request) {
+		testHeader(t, r, AuthHeader, "Bearer test_token")
+		testMethod(t, r, "GET")
+		if _, ok := r.Header[AuthHeader]; !ok {
+			w.WriteHeader(http.StatusUnauthorized)
+		}
+
+		w.WriteHeader(http.StatusOK)
+		_, _ = w.Write([]byte(testdata.GetChargebackResponse))
+	})
+
+	opt := &ChargebackOptions{
+		Include: "details.qrCode",
+	}
+
+	res, err := tClient.Chargebacks.Get(paymentID, chargebackID, opt)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	if res.ID != chargebackID {
+		t.Errorf("mismatching info. want %v, got %v", chargebackID, res.ID)
+	}
+}
+
+func TestChargebacksService_List(t *testing.T) {
+	setup()
+	defer teardown()
+	_ = tClient.WithAuthenticationValue("test_token")
+	tMux.HandleFunc("/v2/chargebacks", func(w http.ResponseWriter, r *http.Request) {
+		testHeader(t, r, AuthHeader, "Bearer test_token")
+		testMethod(t, r, "GET")
+		if _, ok := r.Header[AuthHeader]; !ok {
+			w.WriteHeader(http.StatusUnauthorized)
+		}
+
+		w.WriteHeader(http.StatusOK)
+		_, _ = w.Write([]byte(testdata.ListChargebacksResponse))
+	})
+
+	res, err := tClient.Chargebacks.List(nil)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	if res.Count == 0 {
+		t.Errorf("mismatching info. want %v, got %v", 0, res.Count)
+	}
+}
+
+func TestChargebacksService_ListForPayment(t *testing.T) {
+	setup()
+	defer teardown()
+	paymentID := "tr_WDqYK6vllg"
+	_ = tClient.WithAuthenticationValue("test_token")
+	tMux.HandleFunc("/v2/payments/"+paymentID+"/chargebacks", func(w http.ResponseWriter, r *http.Request) {
+		testHeader(t, r, AuthHeader, "Bearer test_token")
+		testMethod(t, r, "GET")
+		if _, ok := r.Header[AuthHeader]; !ok {
+			w.WriteHeader(http.StatusUnauthorized)
+		}
+
+		w.WriteHeader(http.StatusOK)
+		_, _ = w.Write([]byte(testdata.ListChargebacksResponse))
+	})
+
+	res, err := tClient.Chargebacks.ListForPayment(paymentID, nil)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	if res.Count == 0 {
+		t.Errorf("mismatching info. want %v, got %v", 0, res.Count)
+	}
+}
+
+func TestChargebacksService_ListWithOptions(t *testing.T) {
+	setup()
+	defer teardown()
+	_ = tClient.WithAuthenticationValue("test_token")
+	tMux.HandleFunc("/v2/chargebacks", func(w http.ResponseWriter, r *http.Request) {
+		testHeader(t, r, AuthHeader, "Bearer test_token")
+		testMethod(t, r, "GET")
+		if _, ok := r.Header[AuthHeader]; !ok {
+			w.WriteHeader(http.StatusUnauthorized)
+		}
+
+		w.WriteHeader(http.StatusOK)
+		_, _ = w.Write([]byte(testdata.ListChargebacksResponse))
+	})
+
+	options := &ListChargebackOptions{
+		ProfileID: "pfl_QkEhN94Ba",
+	}
+
+	res, err := tClient.Chargebacks.List(options)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	if res.Count == 0 {
+		t.Errorf("mismatching info. want %v, got %v", 0, res.Count)
+	}
+}
+
+func TestChargebacksService_ListForPaymentWithOptions(t *testing.T) {
+	setup()
+	defer teardown()
+	paymentID := "tr_WDqYK6vllg"
+	_ = tClient.WithAuthenticationValue("test_token")
+	tMux.HandleFunc("/v2/payments/"+paymentID+"/chargebacks", func(w http.ResponseWriter, r *http.Request) {
+		testHeader(t, r, AuthHeader, "Bearer test_token")
+		testMethod(t, r, "GET")
+		if _, ok := r.Header[AuthHeader]; !ok {
+			w.WriteHeader(http.StatusUnauthorized)
+		}
+
+		w.WriteHeader(http.StatusOK)
+		_, _ = w.Write([]byte(testdata.ListChargebacksResponse))
+	})
+
+	options := &ListChargebackOptions{
+		ProfileID: "pfl_QkEhN94Ba",
+	}
+
+	res, err := tClient.Chargebacks.ListForPayment(paymentID, options)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	if res.Count == 0 {
+		t.Errorf("mismatching info. want %v, got %v", 0, res.Count)
+	}
+}
+
+func TestChargebacksService_HttpRequestErrors(t *testing.T) {
+	setup()
+	defer teardown()
+	tMux.HandleFunc("/v2/chargebacks/", errorHandler)
+
+	_, rerr := tClient.Chargebacks.List(nil)
+	_, lerr := tClient.Chargebacks.ListForPayment("1212", nil)
+	_, gerr := tClient.Chargebacks.Get("1212", "3232", nil)
+
+	tests := []error{rerr, gerr, lerr}
+
+	for _, tt := range tests {
+		if tt == nil {
+			t.Fail()
+		}
+	}
+}
+
+func TestChargebacksService_NewAPIRequestErrors(t *testing.T) {
+	setup()
+	defer teardown()
+	u, _ := url.Parse(tServer.URL)
+	tClient.BaseURL = u
+	tMux.HandleFunc("/v2/chargebacks/", errorHandler)
+
+	_, rerr := tClient.Chargebacks.List(nil)
+	_, lerr := tClient.Chargebacks.ListForPayment("1212", nil)
+	_, gerr := tClient.Chargebacks.Get("1212", "3232", nil)
+
+	tests := []error{rerr, gerr, lerr}
+
+	for _, tt := range tests {
+		if tt != errBadBaseURL {
+			t.Fail()
+		}
+	}
+}
+
+func TestChargebacksService_EncodingResponseErrors(t *testing.T) {
+	setup()
+	defer teardown()
+	tMux.HandleFunc("/v2/payments/1212/chargebacks/", encodingHandler)
+
+	_, rerr := tClient.Chargebacks.ListForPayment("1212", nil)
+	_, gerr := tClient.Chargebacks.Get("1212", "3232", nil)
+
+	tests := []error{rerr, gerr}
+
+	for _, tt := range tests {
+		if tt == nil {
+			t.Fail()
+		} else if !strings.Contains(tt.Error(), "invalid character") {
+			t.Errorf("unexpected error %v", tt)
+		}
+	}
+}
+
+func TestChargebacksService_EncodingResponseErrors_List(t *testing.T) {
+	setup()
+	defer teardown()
+	tMux.HandleFunc("/v2/chargebacks/", encodingHandler)
+
+	_, rerr := tClient.Chargebacks.List(nil)
+
+	tests := []error{rerr}
+
+	for _, tt := range tests {
+		if tt == nil {
+			t.Fail()
+		} else if !strings.Contains(tt.Error(), "invalid character") {
+			t.Errorf("unexpected error %v", tt)
+		}
+	}
+}
diff --git a/testdata/chargebacks.go b/testdata/chargebacks.go
new file mode 100644
index 0000000..38a6d06
--- /dev/null
+++ b/testdata/chargebacks.go
@@ -0,0 +1,81 @@
+package testdata
+
+const (
+	GetChargebackResponse = `{
+    "resource": "chargeback",
+    "id": "chb_n9z0tp",
+    "amount": {
+        "currency": "USD",
+        "value": "43.38"
+    },
+    "settlementAmount": {
+        "currency": "EUR",
+        "value": "-35.07"
+    },
+    "createdAt": "2018-03-14T17:00:52.0Z",
+    "reversedAt": null,
+    "paymentId": "tr_WDqYK6vllg",
+    "_links": {
+        "self": {
+            "href": "https://api.mollie.com/v2/payments/tr_WDqYK6vllg/chargebacks/chb_n9z0tp",
+            "type": "application/hal+json"
+        },
+        "payment": {
+            "href": "https://api.mollie.com/v2/payments/tr_WDqYK6vllg",
+            "type": "application/hal+json"
+        },
+        "documentation": {
+            "href": "https://docs.mollie.com/reference/v2/chargebacks-api/get-chargeback",
+            "type": "text/html"
+        }
+    }
+}`
+	ListChargebacksResponse = `{
+    "count": 3,
+    "_embedded": {
+        "chargebacks": [
+            {
+                "resource": "chargeback",
+                "id": "chb_n9z0tp",
+                "amount": {
+                    "currency": "USD",
+                    "value": "43.38"
+                },
+                "settlementAmount": {
+                    "currency": "EUR",
+                    "value": "35.07"
+                },
+                "createdAt": "2018-03-14T17:00:52.0Z",
+                "reversedAt": null,
+                "paymentId": "tr_WDqYK6vllg",
+                "_links": {
+                    "self": {
+                        "href": "https://api.mollie.com/v2/payments/tr_WDqYK6vllg/chargebacks/chb_n9z0tp",
+                        "type": "application/hal+json"
+                    },
+                    "payment": {
+                        "href": "https://api.mollie.com/v2/payments/tr_WDqYK6vllg",
+                        "type": "application/hal+json"
+                    },
+                    "documentation": {
+                        "href": "https://docs.mollie.com/reference/v2/chargebacks-api/get-chargeback",
+                        "type": "text/html"
+                    }
+                }
+            },
+            { },
+            { }
+        ]
+    },
+    "_links": {
+        "self": {
+            "href": "https://api.mollie.com/v2/payments/tr_7UhSN1zuXS/chargebacks",
+            "type": "application/hal+json"
+        },
+        "documentation": {
+            "href": "https://docs.mollie.com/reference/v2/chargebacks-api/list-chargebacks",
+            "type": "text/html"
+        }
+    }
+}`
+)