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

Issue:
Implement Subscriptions API resource
**Description**
Implement the API endpoints related to the subscriptions 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/subscriptions-api/get-subscription

Some of the endpoints are nested resources for a `customer`, so this task has a dependency to #14 

Pull request:
subscription resource implementation
<!--- Provide a general summary of your changes in the Title above -->

## Description

Implementations of Mollie's subscriptions resource

## Motivation and context

Fixes #23 

## How has this been tested?

Unit tests

## Types of changes

What types of changes does your code introduce? Put an `x` in all the boxes that apply:
- [ ] 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:

Go over all the following points, and put an `x` in all the boxes that apply.

Please, please, please, don't send your pull request until all of the boxes are ticked. Once your pull request is created, it will trigger a build on our continuous integration server to make sure your [tests and code style pass](https://help.github.com/articles/about-required-status-checks/).

- [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/mollie/mollie.go b/mollie/mollie.go
index 2d12817..36c7027 100644
--- a/mollie/mollie.go
+++ b/mollie/mollie.go
@@ -47,6 +47,7 @@ type Client struct {
 	Orders        *OrdersService
 	Settlements   *SettlementsService
 	Captures      *CapturesService
+	Subscriptions *SubscriptionsService
 }
 
 type service struct {
@@ -163,6 +164,7 @@ func NewClient(baseClient *http.Client, c *Config) (mollie *Client, err error) {
 	mollie.Orders = (*OrdersService)(&mollie.common)
 	mollie.Captures = (*CapturesService)(&mollie.common)
 	mollie.Settlements = (*SettlementsService)(&mollie.common)
+	mollie.Subscriptions = (*SubscriptionsService)(&mollie.common)
 
 	// Parse authorization from environment
 	if tkn, ok := os.LookupEnv(APITokenEnv); ok {
diff --git a/mollie/subscriptions.go b/mollie/subscriptions.go
new file mode 100644
index 0000000..a957941
--- /dev/null
+++ b/mollie/subscriptions.go
@@ -0,0 +1,238 @@
+package mollie
+
+import (
+	"encoding/json"
+	"fmt"
+	"net/http"
+	"time"
+
+	"github.com/google/go-querystring/query"
+)
+
+// SubscriptionsService operates over subscriptions resource
+type SubscriptionsService service
+
+// SubscriptionStatus contains references to valid subscription statuses
+type SubscriptionStatus string
+
+// Available subscription statuses
+const (
+	SubscriptionStatusPending   SubscriptionStatus = "pending"
+	SubscriptionStatusActive    SubscriptionStatus = "active"
+	SubscriptionStatusCanceled  SubscriptionStatus = "canceled"
+	SubscriptionStatusSuspended SubscriptionStatus = "suspended"
+	SubscriptionStatusCompleted SubscriptionStatus = "completed"
+)
+
+// SubscriptionLinks contains several URL objects relevant to the subscription
+type SubscriptionLinks struct {
+	Self          URL `json:"self,omitempty"`
+	Customer      URL `json:"customer,omitempty"`
+	Payments      URL `json:"payments,omitempty"`
+	Documentation URL `json:"documentation,omitempty"`
+}
+
+// Subscription contains information about a customer subscription
+type Subscription struct {
+	Resource        string                 `json:"resource,omitempty"`
+	ID              string                 `json:"id,omitempty"`
+	Mode            Mode                   `json:"mode,omitempty"`
+	CreatedAT       *time.Time             `json:"createdAt,omitempty"`
+	Status          SubscriptionStatus     `json:"status,omitempty"`
+	Amount          Amount                 `json:"amount,omitempty"`
+	Times           int                    `json:"times,omitempty"`
+	TimesRemaining  int                    `json:"timesRemaining,omitempty"`
+	Interval        string                 `json:"interval,omitempty"`
+	StartDate       *ShortDate             `json:"startDate,omitempty"`
+	NextPaymentDate *ShortDate             `json:"nextPaymentDate,omitempty"`
+	Description     string                 `json:"description,omitempty"`
+	Method          PaymentMethod          `json:"method,omitempty"`
+	CanceledAt      *time.Time             `json:"canceledAt,omitempty"`
+	WebhookURL      string                 `json:"webhookUrl,omitempty"`
+	Metadata        map[string]interface{} `json:"metadata,omitempty"`
+	ApplicationFee  ApplicationFee         `json:"applicationFee,omitempty"`
+	Links           SubscriptionLinks      `json:"_links,omitempty"`
+}
+
+// SubscriptionList describes the response for subscription list endpoints
+type SubscriptionList struct {
+	Count    int `json:"count,omitempty"`
+	Embedded struct {
+		Subscriptions []Subscription
+	} `json:"_embedded,omitempty"`
+	Links PaginationLinks `json:"_links,omitempty"`
+}
+
+// SubscriptionListOptions holds query string parameters valid for subscription lists
+type SubscriptionListOptions struct {
+	From      string `url:"from,omitempty"`
+	Limit     int    `url:"limit,omitempty"`
+	ProfileID string `url:"profileId,omitempty"`
+}
+
+// Get retrieves a customer's subscription
+//
+// See: https://docs.mollie.com/reference/v2/subscriptions-api/get-subscription
+func (ss *SubscriptionsService) Get(cID, sID string) (s *Subscription, err error) {
+	u := fmt.Sprintf("v2/customers/%s/subscriptions/%s", cID, sID)
+	req, err := ss.client.NewAPIRequest(http.MethodGet, u, nil)
+	if err != nil {
+		return
+	}
+
+	res, err := ss.client.Do(req)
+	if err != nil {
+		return
+	}
+
+	if err = json.Unmarshal(res.content, &s); err != nil {
+		return
+	}
+	return
+}
+
+// Create stores a new subscription for a given customer
+//
+// See: https://docs.mollie.com/reference/v2/subscriptions-api/create-subscription
+func (ss *SubscriptionsService) Create(cID string, sc *Subscription) (s *Subscription, err error) {
+	u := fmt.Sprintf("v2/customers/%s/subscriptions", cID)
+
+	req, err := ss.client.NewAPIRequest(http.MethodPost, u, sc)
+	if err != nil {
+		return
+	}
+
+	res, err := ss.client.Do(req)
+	if err != nil {
+		return
+	}
+
+	if err = json.Unmarshal(res.content, &s); err != nil {
+		return
+	}
+	return
+}
+
+// Update changes fields on a subscription object
+//
+// See: https://docs.mollie.com/reference/v2/subscriptions-api/update-subscription
+func (ss *SubscriptionsService) Update(cID, sID string, sc *Subscription) (s *Subscription, err error) {
+	u := fmt.Sprintf("v2/customers/%s/subscriptions/%s", cID, sID)
+
+	req, err := ss.client.NewAPIRequest(http.MethodPatch, u, sc)
+	if err != nil {
+		return
+	}
+
+	res, err := ss.client.Do(req)
+	if err != nil {
+		return
+	}
+
+	if err = json.Unmarshal(res.content, &s); err != nil {
+		return
+	}
+	return
+}
+
+// Delete cancels a subscription
+//
+// See: https://docs.mollie.com/reference/v2/subscriptions-api/cancel-subscription
+func (ss *SubscriptionsService) Delete(cID, sID string) (s *Subscription, err error) {
+	u := fmt.Sprintf("v2/customers/%s/subscriptions/%s", cID, sID)
+	req, err := ss.client.NewAPIRequest(http.MethodDelete, u, nil)
+	if err != nil {
+		return
+	}
+
+	res, err := ss.client.Do(req)
+	if err != nil {
+		return
+	}
+
+	if err = json.Unmarshal(res.content, &s); err != nil {
+		return
+	}
+	return
+}
+
+// All retrieves all subscriptions, ordered from newest to oldest.
+// By using an API key all the subscriptions created with the current website profile will be returned.
+// In the case of an OAuth Access Token relies the website profile on the profileId field
+//
+// See: https://docs.mollie.com/reference/v2/subscriptions-api/list-all-subscriptions
+func (ss *SubscriptionsService) All(options *SubscriptionListOptions) (sl *SubscriptionList, err error) {
+	u := fmt.Sprintf("v2/subscriptions")
+
+	if options != nil {
+		v, _ := query.Values(options)
+		u = fmt.Sprintf("%s?%s", u, v.Encode())
+	}
+
+	res, err := ss.list(u)
+	if err != nil {
+		return
+	}
+
+	if err = json.Unmarshal(res.content, &sl); err != nil {
+		return
+	}
+	return
+}
+
+// List retrieves all subscriptions of a customer
+//
+// See: https://docs.mollie.com/reference/v2/subscriptions-api/list-subscriptions
+func (ss *SubscriptionsService) List(cID string, options *SubscriptionListOptions) (sl *SubscriptionList, err error) {
+	u := fmt.Sprintf("v2/customers/%s/subscriptions", cID)
+
+	if options != nil {
+		v, _ := query.Values(options)
+		u = fmt.Sprintf("%s?%s", u, v.Encode())
+	}
+
+	res, err := ss.list(u)
+	if err != nil {
+		return
+	}
+
+	if err = json.Unmarshal(res.content, &sl); err != nil {
+		return
+	}
+	return
+}
+
+// GetPayments retrieves all payments of a specific subscriptions of a customer
+//
+// See: https://docs.mollie.com/reference/v2/subscriptions-api/list-subscriptions-payments
+func (ss *SubscriptionsService) GetPayments(cID, sID string, options *SubscriptionListOptions) (sl *PaymentList, err error) {
+	u := fmt.Sprintf("v2/customers/%s/subscriptions/%s/payments", cID, sID)
+
+	if options != nil {
+		v, _ := query.Values(options)
+		u = fmt.Sprintf("%s?%s", u, v.Encode())
+	}
+
+	res, err := ss.list(u)
+	if err != nil {
+		return
+	}
+
+	if err = json.Unmarshal(res.content, &sl); err != nil {
+		return
+	}
+	return
+}
+
+func (ss *SubscriptionsService) list(uri string) (r *Response, err error) {
+	req, err := ss.client.NewAPIRequest(http.MethodGet, uri, nil)
+	if err != nil {
+		return
+	}
+
+	r, err = ss.client.Do(req)
+	if err != nil {
+		return
+	}
+	return
+}


Test patch:
diff --git a/mollie/subscriptions_test.go b/mollie/subscriptions_test.go
new file mode 100644
index 0000000..2a2d0f4
--- /dev/null
+++ b/mollie/subscriptions_test.go
@@ -0,0 +1,289 @@
+package mollie
+
+import (
+	"fmt"
+	"net/http"
+	"net/url"
+	"strings"
+	"testing"
+
+	"github.com/VictorAvelar/mollie-api-go/testdata"
+)
+
+func TestSubscriptionsService_Get(t *testing.T) {
+	setup()
+	defer teardown()
+
+	cID := "cst_stTC2WHAuS"
+	sID := "sub_rVKGtNd6s3"
+	u := fmt.Sprintf("/v2/customers/%s/subscriptions/%s", cID, sID)
+	_ = tClient.WithAuthenticationValue("test_token")
+	tMux.HandleFunc(u, func(w http.ResponseWriter, r *http.Request) {
+		testHeader(t, r, AuthHeader, "Bearer test_token")
+		testMethod(t, r, http.MethodGet)
+
+		if _, ok := r.Header[AuthHeader]; !ok {
+			w.WriteHeader(http.StatusUnauthorized)
+		}
+
+		w.WriteHeader(http.StatusOK)
+		_, _ = w.Write([]byte(testdata.GetSubscriptionResponse))
+	})
+
+	sub, err := tClient.Subscriptions.Get(cID, sID)
+	if err != nil {
+		t.Error(err)
+	}
+
+	if sub.ID != sID {
+		t.Errorf("unexpected response: got %v, want %v", sub.ID, sID)
+	}
+}
+
+func TestSubscriptionsService_Create(t *testing.T) {
+	setup()
+	defer teardown()
+
+	cID := "cst_stTC2WHAuS"
+	sID := "sub_rVKGtNd6s3"
+	u := fmt.Sprintf("/v2/customers/%s/subscriptions", cID)
+	_ = tClient.WithAuthenticationValue("test_token")
+	tMux.HandleFunc(u, func(w http.ResponseWriter, r *http.Request) {
+		testHeader(t, r, AuthHeader, "Bearer test_token")
+		testMethod(t, r, http.MethodPost)
+
+		if _, ok := r.Header[AuthHeader]; !ok {
+			w.WriteHeader(http.StatusUnauthorized)
+		}
+
+		w.WriteHeader(http.StatusOK)
+		_, _ = w.Write([]byte(testdata.GetSubscriptionResponse))
+	})
+
+	s := Subscription{
+		Amount: Amount{
+			Currency: "EUR",
+			Value:    "11.99",
+		},
+		Interval: "12 months",
+	}
+
+	sub, err := tClient.Subscriptions.Create(cID, &s)
+	if err != nil {
+		t.Error(err)
+	}
+
+	if sub.ID != sID {
+		t.Errorf("unexpected response: got %v, want %v", sub.ID, sID)
+	}
+}
+
+func TestSubscriptionsService_Update(t *testing.T) {
+	setup()
+	defer teardown()
+
+	cID := "cst_stTC2WHAuS"
+	sID := "sub_rVKGtNd6s3"
+	u := fmt.Sprintf("/v2/customers/%s/subscriptions/%s", cID, sID)
+	_ = tClient.WithAuthenticationValue("test_token")
+	tMux.HandleFunc(u, func(w http.ResponseWriter, r *http.Request) {
+		testHeader(t, r, AuthHeader, "Bearer test_token")
+		testMethod(t, r, http.MethodPatch)
+
+		if _, ok := r.Header[AuthHeader]; !ok {
+			w.WriteHeader(http.StatusUnauthorized)
+		}
+
+		w.WriteHeader(http.StatusOK)
+		_, _ = w.Write([]byte(testdata.GetSubscriptionResponse))
+	})
+
+	s := Subscription{
+		Amount: Amount{
+			Currency: "EUR",
+			Value:    "11.99",
+		},
+		Interval: "12 months",
+	}
+
+	sub, err := tClient.Subscriptions.Update(cID, sID, &s)
+	if err != nil {
+		t.Error(err)
+	}
+
+	if sub.ID != sID {
+		t.Errorf("unexpected response: got %v, want %v", sub.ID, sID)
+	}
+}
+
+func TestSubscriptionsService_Delete(t *testing.T) {
+	setup()
+	defer teardown()
+
+	cID := "cst_stTC2WHAuS"
+	sID := "sub_rVKGtNd6s3"
+	u := fmt.Sprintf("/v2/customers/%s/subscriptions/%s", cID, sID)
+	_ = tClient.WithAuthenticationValue("test_token")
+	tMux.HandleFunc(u, func(w http.ResponseWriter, r *http.Request) {
+		testHeader(t, r, AuthHeader, "Bearer test_token")
+		testMethod(t, r, http.MethodDelete)
+
+		if _, ok := r.Header[AuthHeader]; !ok {
+			w.WriteHeader(http.StatusUnauthorized)
+		}
+
+		w.WriteHeader(http.StatusOK)
+		_, _ = w.Write([]byte(testdata.GetSubscriptionResponse))
+	})
+
+	sub, err := tClient.Subscriptions.Delete(cID, sID)
+	if err != nil {
+		t.Error(err)
+	}
+
+	if sub.ID != sID {
+		t.Errorf("unexpected response: got %v, want %v", sub.ID, sID)
+	}
+}
+
+func TestSubscriptionsService_List(t *testing.T) {
+	setup()
+	defer teardown()
+
+	cID := "cst_stTC2WHAuS"
+	u := fmt.Sprintf("/v2/customers/%s/subscriptions", cID)
+	_ = tClient.WithAuthenticationValue("test_token")
+	tMux.HandleFunc(u, func(w http.ResponseWriter, r *http.Request) {
+		testHeader(t, r, AuthHeader, "Bearer test_token")
+		testMethod(t, r, http.MethodGet)
+
+		if _, ok := r.Header[AuthHeader]; !ok {
+			w.WriteHeader(http.StatusUnauthorized)
+		}
+
+		w.WriteHeader(http.StatusOK)
+		_, _ = w.Write([]byte(testdata.ListAllSubscriptionsResponse))
+	})
+
+	sub, err := tClient.Subscriptions.List(cID, &SubscriptionListOptions{Limit: 6})
+	if err != nil {
+		t.Error(err)
+	}
+
+	if sub.Count != 3 {
+		t.Errorf("unexpected response: got %v, want 3", sub.Count)
+	}
+}
+
+func TestSubscriptionsService_All(t *testing.T) {
+	setup()
+	defer teardown()
+	_ = tClient.WithAuthenticationValue("test_token")
+	tMux.HandleFunc("/v2/subscriptions", func(w http.ResponseWriter, r *http.Request) {
+		testHeader(t, r, AuthHeader, "Bearer test_token")
+		testMethod(t, r, http.MethodGet)
+
+		if _, ok := r.Header[AuthHeader]; !ok {
+			w.WriteHeader(http.StatusUnauthorized)
+		}
+
+		w.WriteHeader(http.StatusOK)
+		_, _ = w.Write([]byte(testdata.ListAllSubscriptionsResponse))
+	})
+
+	sub, err := tClient.Subscriptions.All(&SubscriptionListOptions{Limit: 6})
+	if err != nil {
+		t.Error(err)
+	}
+
+	if sub.Count != 3 {
+		t.Errorf("unexpected response: got %v, want 3", sub.Count)
+	}
+}
+
+func TestSubscriptionsService_GetPayments(t *testing.T) {
+	setup()
+	defer teardown()
+	cID := "cst_stTC2WHAuS"
+	sID := "sub_rVKGtNd6s3"
+	u := fmt.Sprintf("/v2/customers/%s/subscriptions/%s/payments", cID, sID)
+	_ = tClient.WithAuthenticationValue("test_token")
+	tMux.HandleFunc(u, func(w http.ResponseWriter, r *http.Request) {
+		testHeader(t, r, AuthHeader, "Bearer test_token")
+		testMethod(t, r, http.MethodGet)
+
+		if _, ok := r.Header[AuthHeader]; !ok {
+			w.WriteHeader(http.StatusUnauthorized)
+		}
+
+		w.WriteHeader(http.StatusOK)
+		_, _ = w.Write([]byte(testdata.ListPaymentsResponse))
+	})
+	sub, err := tClient.Subscriptions.GetPayments(cID, sID, &SubscriptionListOptions{Limit: 6})
+	if err != nil {
+		t.Error(err)
+	}
+	if sub.Count != 5 {
+		t.Errorf("unexpected response: got %v, want 5", sub.Count)
+	}
+}
+
+func TestSubscriptionsService_HTTPErrors(t *testing.T) {
+	setup()
+	defer teardown()
+
+	tMux.HandleFunc("/v2/", errorHandler)
+	tests := forceSubscriptionsErrors()
+
+	for _, tt := range tests {
+		if !strings.Contains(tt.Error(), "Internal Server Error") {
+			t.Error(tt)
+		}
+	}
+}
+
+func TestSubscriptionsService_JsonEncodingErrors(t *testing.T) {
+	setup()
+	defer teardown()
+
+	tMux.HandleFunc("/v2/", encodingHandler)
+
+	tests := forceSubscriptionsErrors()
+
+	for _, tt := range tests {
+		if tt == nil {
+			t.Error(tt)
+		} else if !strings.Contains(tt.Error(), "invalid character") {
+			t.Errorf("unexpected error %v", tt)
+		}
+	}
+}
+
+func TestSubscriptionsService_NewAPIRequestErrors(t *testing.T) {
+	setup()
+	defer teardown()
+	u, _ := url.Parse(tServer.URL)
+	tClient.BaseURL = u
+
+	tMux.HandleFunc("/v2/", errorHandler)
+
+	tests := forceSubscriptionsErrors()
+
+	for _, tt := range tests {
+		if tt != errBadBaseURL {
+			t.Error(tt)
+		}
+	}
+}
+
+func forceSubscriptionsErrors() []error {
+	_, gerr := tClient.Subscriptions.Get("6s7d89a", "sd9af8ds")
+	_, cerr := tClient.Subscriptions.Create("6s7d89a", &Subscription{Mode: LiveMode})
+	_, uerr := tClient.Subscriptions.Update("6s7d89a", "0re9w0e", &Subscription{Mode: LiveMode})
+	_, derr := tClient.Subscriptions.Delete("6s7d89a", "0re9w0e")
+	_, lerr := tClient.Subscriptions.List("6s7d89a", nil)
+	_, aerr := tClient.Subscriptions.All(nil)
+	_, perr := tClient.Subscriptions.GetPayments("6s7d89a", "0re9w0e", nil)
+
+	return []error{gerr, cerr, uerr, derr, lerr, aerr, perr}
+}
diff --git a/testdata/subscriptions.go b/testdata/subscriptions.go
new file mode 100644
index 0000000..1d3885a
--- /dev/null
+++ b/testdata/subscriptions.go
@@ -0,0 +1,140 @@
+package testdata
+
+// GetSubscriptionResponse example
+const GetSubscriptionResponse = `{
+    "resource": "subscription",
+    "id": "sub_rVKGtNd6s3",
+    "mode": "live",
+    "createdAt": "2016-06-01T12:23:34+00:00",
+    "status": "active",
+    "amount": {
+        "value": "25.00",
+        "currency": "EUR"
+    },
+    "times": 4,
+    "timesRemaining": 4,
+    "interval": "3 months",
+    "startDate": "2016-06-01",
+    "nextPaymentDate": "2016-09-01",
+    "description": "Quarterly payment",
+    "method": null,
+    "mandateId": "mdt_38HS4fsS",
+    "webhookUrl": "https://webshop.example.org/payments/webhook",
+    "metadata": {
+        "plan": "small"
+    },
+    "_links": {
+        "self": {
+            "href": "https://api.mollie.com/v2/customers/cst_stTC2WHAuS/subscriptions/sub_rVKGtNd6s3",
+            "type": "application/hal+json"
+        },
+        "customer": {
+            "href": "https://api.mollie.com/v2/customers/cst_stTC2WHAuS",
+            "type": "application/hal+json"
+        },
+        "profile": {
+            "href": "https://api.mollie.com/v2/profiles/pfl_URR55HPMGx",
+            "type": "application/hal+json"
+        },
+       "payments": {
+            "href": "https://api.mollie.com/v2/customers/cst_stTC2WHAuS/subscriptions/sub_rVKGtNd6s3/payments",
+            "type": "application/hal+json"
+        },
+        "documentation": {
+            "href": "https://docs.mollie.com/reference/v2/subscriptions-api/get-subscription",
+            "type": "text/html"
+        }
+    }
+}`
+
+// DeleteSubscriptionResponse example
+const DeleteSubscriptionResponse = `{
+    "resource": "subscription",
+    "id": "sub_rVKGtNd6s3",
+    "mode": "live",
+    "createdAt": "2018-06-01T12:23:34+00:00",
+    "status": "canceled",
+    "amount": {
+        "value": "25.00",
+        "currency": "EUR"
+    },
+    "times": 4,
+    "interval": "3 months",
+    "nextPaymentDate": null,
+    "description": "Quarterly payment",
+    "method": null,
+    "startDate": "2016-06-01",
+    "webhookUrl": "https://webshop.example.org/payments/webhook",
+    "canceledAt": "2018-08-01T11:04:21+00:00",
+    "_links": {
+        "self": {
+            "href": "https://api.mollie.com/v2/customers/cst_stTC2WHAuS/subscriptions/sub_rVKGtNd6s3",
+            "type": "application/hal+json"
+        },
+        "customer": {
+            "href": "https://api.mollie.com/v2/customers/cst_stTC2WHAuS",
+            "type": "application/hal+json"
+        },
+        "documentation": {
+            "href": "https://docs.mollie.com/reference/v2/subscriptions-api/cancel-subscription",
+            "type": "text/html"
+        }
+    }
+}`
+
+// ListAllSubscriptionsResponse example
+const ListAllSubscriptionsResponse = `{
+    "count": 3,
+    "_embedded": {
+        "subscriptions": [
+            {
+                "resource": "subscription",
+                "id": "sub_rVKGtNd6s3",
+                "mode": "live",
+                "createdAt": "2018-06-01T12:23:34+00:00",
+                "status": "active",
+                "amount": {
+                    "value": "25.00",
+                    "currency": "EUR"
+                },
+                "times": 4,
+                "timesRemaining": 3,
+                "interval": "3 months",
+                "startDate": "2016-06-01",
+                "nextPaymentDate": "2016-09-01",
+                "description": "Quarterly payment",
+                "method": null,
+                "webhookUrl": "https://webshop.example.org/subscriptions/webhook",
+                "_links": {
+                    "self": {
+                        "href": "https://api.mollie.com/v2/customers/cst_stTC2WHAuS/subscriptions/sub_rVKGtNd6s3",
+                        "type": "application/hal+json"
+                    },
+                    "profile": {
+                        "href": "https://api.mollie.com/v2/profiles/pfl_URR55HPMGx",
+                        "type": "application/hal+json"
+                    },
+                    "customer": {
+                        "href": "https://api.mollie.com/v2/customers/cst_stTC2WHAuS",
+                        "type": "application/hal+json"
+                    }
+                }
+            }
+        ]
+    },
+    "_links": {
+        "self": {
+            "href": "https://api.mollie.com/v2/subscriptions",
+            "type": "application/hal+json"
+        },
+        "previous": null,
+        "next": {
+            "href": "https://api.mollie.com/v2/subscriptions?from=sub_mnfbwhMfvo",
+            "type": "application/hal+json"
+        },
+        "documentation": {
+            "href": "https://docs.mollie.com/reference/v2/subscriptions-api/list-all-subscriptions",
+            "type": "text/html"
+        }
+    }
+}`