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

Issue:
Missing api route for getting payments of a payment link
**Is your feature request related to a problem? Please describe.**
I was working with the payment links of Mollie when I discovered that the route `/v2/payment-links/{id}/payments` was missing in this package. View the docs for the endpoint here: https://docs.mollie.com/reference/get-payment-link-payments

**Describe the solution you'd like**
I think the best selution is to add the route to the `PaymentLinksService`. In my fork I created a quick patch to use it in my project. View my changes here: https://github.com/tomblixt/mollie-api-go/commit/39bcf196be280df44fd6be3fec0b0d95ba9d08ea


Pull request:
Add method to retrieve payments associated with a payment link.
<!--- Provide a general summary of your changes in the Title above -->

# Description

- Adds a fixed type (`SortDirection`) for sorting values when retrieving lists.
- Adds a method to the PaymentListsService that retrieves a list of payments associated with a unique payment link.

### Other
- Removes deprecated flags from `golangci-lint` used in the `Taskfile.yaml`.

## Motivation and context

Closes #431 

## How has this been tested?

Please describe in detail how you tested your changes.

Include details of your testing environment, and the tests you ran to
see how your change affects other areas of the code, etc.

- [x] Unit tests added / updated
- [x] The tests run on docker (using `task test`)
- [x] The required `test data` has been added / updated

If you are running the tests locally, please specify:

- OS: macos Sonoma
- Go version: 1.24

## 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)
- [ ] This change requires a documentation update

## 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] 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/Taskfile.yml b/Taskfile.yml
index 605fbb1..40bb2e0 100644
--- a/Taskfile.yml
+++ b/Taskfile.yml
@@ -16,19 +16,19 @@ tasks:
   ci-lint:
     desc: "Run all linters used in the CI pipeline"
     cmds:
-      - golangci-lint run --out-format=tab --issues-exit-code=0 --sort-results --skip-dirs-use-default --tests=false --presets=bugs,complexity,format,performance,style,unused
+      - golangci-lint run --out-format=tab --issues-exit-code=0 --sort-results --tests=false --presets=bugs,complexity,format,performance,style,unused
     silent: true
 
   pr-lint:
     desc: "Run all linters used in the PR pipeline"
     cmds:
-      - golangci-lint run --issues-exit-code=0 --out-format=github-actions --new=true --sort-results --skip-dirs-use-default --tests=false --presets=bugs,complexity,format,performance,style,unused
+      - golangci-lint run --issues-exit-code=0 --out-format=colored-line-number --new=true --sort-results  --tests=false --presets=bugs,complexity,format,performance,style,unused
     silent: true
 
   ci-all-presets:
     desc: "Run all linters' presets available in golangci-lint"
     cmds:
-      - golangci-lint run --out-format=tab --issues-exit-code=0 --sort-results --skip-dirs-use-default --tests=false --presets=bugs,comment,complexity,error,format,import,metalinter,module,performance,sql,style,test,unused
+      - golangci-lint run --out-format=tab --issues-exit-code=0 --sort-results --tests=false --presets=bugs,comment,complexity,error,format,import,metalinter,module,performance,sql,style,test,unused
     silent: true
 
   lint:
diff --git a/docs/README.md b/docs/README.md
index 7a7b869..248f553 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -240,6 +240,8 @@ The Mollie API is a straightforward REST API. This means all endpoints either cr
 - [type PaymentLink](<#PaymentLink>)
 - [type PaymentLinkLinks](<#PaymentLinkLinks>)
 - [type PaymentLinkOptions](<#PaymentLinkOptions>)
+- [type PaymentLinkPaymentsList](<#PaymentLinkPaymentsList>)
+- [type PaymentLinkPaymentsListOptions](<#PaymentLinkPaymentsListOptions>)
 - [type PaymentLinks](<#PaymentLinks>)
 - [type PaymentLinksList](<#PaymentLinksList>)
 - [type PaymentLinksService](<#PaymentLinksService>)
@@ -247,6 +249,7 @@ The Mollie API is a straightforward REST API. This means all endpoints either cr
   - [func \(pls \*PaymentLinksService\) Delete\(ctx context.Context, id string\) \(res \*Response, err error\)](<#PaymentLinksService.Delete>)
   - [func \(pls \*PaymentLinksService\) Get\(ctx context.Context, id string\) \(res \*Response, pl \*PaymentLink, err error\)](<#PaymentLinksService.Get>)
   - [func \(pls \*PaymentLinksService\) List\(ctx context.Context, opts \*PaymentLinkOptions\) \(res \*Response, pl \*PaymentLinksList, err error\)](<#PaymentLinksService.List>)
+  - [func \(pls \*PaymentLinksService\) Payments\(ctx context.Context, id string, opts \*PaymentLinkPaymentsListOptions\) \(res \*Response, pl \*PaymentLinkPaymentsList, err error\)](<#PaymentLinksService.Payments>)
   - [func \(pls \*PaymentLinksService\) Update\(ctx context.Context, id string, p UpdatePaymentLinks\) \(res \*Response, pl \*PaymentLink, err error\)](<#PaymentLinksService.Update>)
 - [type PaymentList](<#PaymentList>)
 - [type PaymentMethod](<#PaymentMethod>)
@@ -353,6 +356,7 @@ The Mollie API is a straightforward REST API. This means all endpoints either cr
 - [type ShortDate](<#ShortDate>)
   - [func \(d \*ShortDate\) MarshalJSON\(\) \(\[\]byte, error\)](<#ShortDate.MarshalJSON>)
   - [func \(d \*ShortDate\) UnmarshalJSON\(b \[\]byte\) error](<#ShortDate.UnmarshalJSON>)
+- [type SortDirection](<#SortDirection>)
 - [type Subscription](<#Subscription>)
 - [type SubscriptionAccessTokenFields](<#SubscriptionAccessTokenFields>)
 - [type SubscriptionLinks](<#SubscriptionLinks>)
@@ -4142,6 +4146,34 @@ type PaymentLinkOptions struct {
 }
 ```
 
+<a name="PaymentLinkPaymentsList"></a>
+## type [PaymentLinkPaymentsList](<https://github.com/VictorAvelar/mollie-api-go/blob/master/mollie/payment_links.go#L63-L69>)
+
+PaymentLinkPaymentsList retrieves a list of payment associated with a specific payment link.
+
+```go
+type PaymentLinkPaymentsList struct {
+    Count    int              `json:"count,omitempty"`
+    Links    PaymentLinkLinks `json:"_links,omitempty"`
+    Embedded struct {
+        Payments []*Payment `json:"payments,omitempty"`
+    }   `json:"_embedded,omitempty"`
+}
+```
+
+<a name="PaymentLinkPaymentsListOptions"></a>
+## type [PaymentLinkPaymentsListOptions](<https://github.com/VictorAvelar/mollie-api-go/blob/master/mollie/payment_links.go#L73-L77>)
+
+PaymentLinkPaymentsListOptions represents query string parameters to modify the payment link payments list requests.
+
+```go
+type PaymentLinkPaymentsListOptions struct {
+    Limit    int           `url:"limit,omitempty"`
+    Sort     SortDirection `url:"sort,omitempty"`
+    TestMode bool          `url:"testmode,omitempty"`
+}
+```
+
 <a name="PaymentLinks"></a>
 ## type [PaymentLinks](<https://github.com/VictorAvelar/mollie-api-go/blob/master/mollie/payments.go#L269-L285>)
 
@@ -4183,7 +4215,7 @@ type PaymentLinksList struct {
 ```
 
 <a name="PaymentLinksService"></a>
-## type [PaymentLinksService](<https://github.com/VictorAvelar/mollie-api-go/blob/master/mollie/payment_links.go#L69>)
+## type [PaymentLinksService](<https://github.com/VictorAvelar/mollie-api-go/blob/master/mollie/payment_links.go#L87>)
 
 PaymentLinksService operates over the payment link resource.
 
@@ -4192,7 +4224,7 @@ type PaymentLinksService service
 ```
 
 <a name="PaymentLinksService.Create"></a>
-### func \(\*PaymentLinksService\) [Create](<https://github.com/VictorAvelar/mollie-api-go/blob/master/mollie/payment_links.go#L90-L94>)
+### func \(\*PaymentLinksService\) [Create](<https://github.com/VictorAvelar/mollie-api-go/blob/master/mollie/payment_links.go#L108-L112>)
 
 ```go
 func (pls *PaymentLinksService) Create(ctx context.Context, p PaymentLink, opts *PaymentLinkOptions) (res *Response, np *PaymentLink, err error)
@@ -4203,7 +4235,7 @@ Create generates payment links that by default, unlike regular payments, do not
 See: https://docs.mollie.com/reference/create-payment-link
 
 <a name="PaymentLinksService.Delete"></a>
-### func \(\*PaymentLinksService\) [Delete](<https://github.com/VictorAvelar/mollie-api-go/blob/master/mollie/payment_links.go#L151>)
+### func \(\*PaymentLinksService\) [Delete](<https://github.com/VictorAvelar/mollie-api-go/blob/master/mollie/payment_links.go#L169>)
 
 ```go
 func (pls *PaymentLinksService) Delete(ctx context.Context, id string) (res *Response, err error)
@@ -4214,7 +4246,7 @@ Delete removes a payment link from the website profile.
 See: https://docs.mollie.com/reference/delete-payment-link
 
 <a name="PaymentLinksService.Get"></a>
-### func \(\*PaymentLinksService\) [Get](<https://github.com/VictorAvelar/mollie-api-go/blob/master/mollie/payment_links.go#L74>)
+### func \(\*PaymentLinksService\) [Get](<https://github.com/VictorAvelar/mollie-api-go/blob/master/mollie/payment_links.go#L92>)
 
 ```go
 func (pls *PaymentLinksService) Get(ctx context.Context, id string) (res *Response, pl *PaymentLink, err error)
@@ -4225,7 +4257,7 @@ Get retrieves a single payment link object by its id/token.
 See: https://docs.mollie.com/reference/get-payment-link
 
 <a name="PaymentLinksService.List"></a>
-### func \(\*PaymentLinksService\) [List](<https://github.com/VictorAvelar/mollie-api-go/blob/master/mollie/payment_links.go#L111-L115>)
+### func \(\*PaymentLinksService\) [List](<https://github.com/VictorAvelar/mollie-api-go/blob/master/mollie/payment_links.go#L129-L133>)
 
 ```go
 func (pls *PaymentLinksService) List(ctx context.Context, opts *PaymentLinkOptions) (res *Response, pl *PaymentLinksList, err error)
@@ -4235,8 +4267,19 @@ List retrieves all payments links created with the current website profile, orde
 
 See: https://docs.mollie.com/reference/list-payment-links
 
+<a name="PaymentLinksService.Payments"></a>
+### func \(\*PaymentLinksService\) [Payments](<https://github.com/VictorAvelar/mollie-api-go/blob/master/mollie/payment_links.go#L181-L185>)
+
+```go
+func (pls *PaymentLinksService) Payments(ctx context.Context, id string, opts *PaymentLinkPaymentsListOptions) (res *Response, pl *PaymentLinkPaymentsList, err error)
+```
+
+Payments retrieves all payments associated with a specific payment link.
+
+See: https://docs.mollie.com/reference/get-payment-link-payments
+
 <a name="PaymentLinksService.Update"></a>
-### func \(\*PaymentLinksService\) [Update](<https://github.com/VictorAvelar/mollie-api-go/blob/master/mollie/payment_links.go#L131-L135>)
+### func \(\*PaymentLinksService\) [Update](<https://github.com/VictorAvelar/mollie-api-go/blob/master/mollie/payment_links.go#L149-L153>)
 
 ```go
 func (pls *PaymentLinksService) Update(ctx context.Context, id string, p UpdatePaymentLinks) (res *Response, pl *PaymentLink, err error)
@@ -5627,6 +5670,24 @@ func (d *ShortDate) UnmarshalJSON(b []byte) error
 
 UnmarshalJSON overrides the default unmarshal action for the Date struct, as we need links to be pointers to the time.Time struct.
 
+<a name="SortDirection"></a>
+## type [SortDirection](<https://github.com/VictorAvelar/mollie-api-go/blob/master/mollie/common_types.go#L371>)
+
+SortDirection describes the direction of sorting when querying a list of resources.
+
+```go
+type SortDirection string
+```
+
+<a name="Ascending"></a>Supported sort directions.
+
+```go
+const (
+    Ascending  SortDirection = "asc"
+    Descending SortDirection = "desc"
+)
+```
+
 <a name="Subscription"></a>
 ## type [Subscription](<https://github.com/VictorAvelar/mollie-api-go/blob/master/mollie/subscriptions.go#L67-L87>)
 
@@ -6066,7 +6127,7 @@ type UpdatePayment struct {
 ```
 
 <a name="UpdatePaymentLinks"></a>
-## type [UpdatePaymentLinks](<https://github.com/VictorAvelar/mollie-api-go/blob/master/mollie/payment_links.go#L63-L66>)
+## type [UpdatePaymentLinks](<https://github.com/VictorAvelar/mollie-api-go/blob/master/mollie/payment_links.go#L81-L84>)
 
 UpdatePaymentLinks describes certain details of an existing payment link that can be updated.
 
diff --git a/mollie/common_types.go b/mollie/common_types.go
index 77e9956..13e7cb3 100644
--- a/mollie/common_types.go
+++ b/mollie/common_types.go
@@ -366,3 +366,12 @@ type Owner struct {
 	FamilyName string `json:"familyName,omitempty"`
 	Locale     Locale `json:"locale,omitempty"`
 }
+
+// SortDirection describes the direction of sorting when querying a list of resources.
+type SortDirection string
+
+// Supported sort directions.
+const (
+	Ascending  SortDirection = "asc"
+	Descending SortDirection = "desc"
+)
diff --git a/mollie/payment_links.go b/mollie/payment_links.go
index f8e752b..94f9079 100644
--- a/mollie/payment_links.go
+++ b/mollie/payment_links.go
@@ -58,6 +58,24 @@ type PaymentLinksList struct {
 	} `json:"_embedded,omitempty"`
 }
 
+// PaymentLinkPaymentsList retrieves a list of payment associated with a specific
+// payment link.
+type PaymentLinkPaymentsList struct {
+	Count    int              `json:"count,omitempty"`
+	Links    PaymentLinkLinks `json:"_links,omitempty"`
+	Embedded struct {
+		Payments []*Payment `json:"payments,omitempty"`
+	} `json:"_embedded,omitempty"`
+}
+
+// PaymentLinkPaymentsListOptions represents query string parameters to modify
+// the payment link payments list requests.
+type PaymentLinkPaymentsListOptions struct {
+	Limit    int           `url:"limit,omitempty"`
+	Sort     SortDirection `url:"sort,omitempty"`
+	TestMode bool          `url:"testmode,omitempty"`
+}
+
 // UpdatePaymentLinks describes certain details of an existing payment link
 // that can be updated.
 type UpdatePaymentLinks struct {
@@ -156,3 +174,23 @@ func (pls *PaymentLinksService) Delete(ctx context.Context, id string) (res *Res
 
 	return
 }
+
+// Payments retrieves all payments associated with a specific payment link.
+//
+// See: https://docs.mollie.com/reference/get-payment-link-payments
+func (pls *PaymentLinksService) Payments(ctx context.Context, id string, opts *PaymentLinkPaymentsListOptions) (
+	res *Response,
+	pl *PaymentLinkPaymentsList,
+	err error,
+) {
+	res, err = pls.client.get(ctx, fmt.Sprintf("v2/payment-links/%s/payments", id), opts)
+	if err != nil {
+		return
+	}
+
+	if err = json.Unmarshal(res.content, &pl); err != nil {
+		return
+	}
+
+	return
+}


Test patch:
diff --git a/mollie/payment_links_test.go b/mollie/payment_links_test.go
index 0c48812..5233a86 100644
--- a/mollie/payment_links_test.go
+++ b/mollie/payment_links_test.go
@@ -499,3 +499,121 @@ func TestPaymentLinkService_Delete(t *testing.T) {
 		})
 	}
 }
+
+func TestPaymentLinkService_Payments(t *testing.T) {
+	setEnv()
+	defer unsetEnv()
+
+	type args struct {
+		ctx         context.Context
+		paymentLink string
+		opts        *PaymentLinkPaymentsListOptions
+	}
+
+	cases := []struct {
+		name    string
+		args    args
+		wantErr bool
+		err     error
+		pre     func()
+		handler http.HandlerFunc
+	}{
+		{
+			"list payment link payments works as expected.",
+			args{
+				context.Background(),
+				"pl_ka21123129",
+				nil,
+			},
+			false,
+			nil,
+			noPre,
+			func(w http.ResponseWriter, r *http.Request) {
+				testHeader(t, r, AuthHeader, "Bearer token_X12b31ggg23")
+				testMethod(t, r, "GET")
+
+				if _, ok := r.Header[AuthHeader]; !ok {
+					w.WriteHeader(http.StatusUnauthorized)
+				}
+				_, _ = w.Write([]byte(testdata.ListPaymentLinkPaymentsResponse))
+			},
+		},
+		{
+			"list payment link payments with options works as expected.",
+			args{
+				context.Background(),
+				"pl_ka21123129",
+				&PaymentLinkPaymentsListOptions{Limit: 10},
+			},
+			false,
+			nil,
+			noPre,
+			func(w http.ResponseWriter, r *http.Request) {
+				testHeader(t, r, AuthHeader, "Bearer token_X12b31ggg23")
+				testMethod(t, r, "GET")
+				testQuery(t, r, "limit=10")
+
+				if _, ok := r.Header[AuthHeader]; !ok {
+					w.WriteHeader(http.StatusUnauthorized)
+				}
+				_, _ = w.Write([]byte(testdata.ListPaymentLinkPaymentsResponse))
+			},
+		},
+		{
+			"list payment link payments, an error is returned from the server",
+			args{
+				context.Background(),
+				"pl_ka21123129",
+				nil,
+			},
+			true,
+			fmt.Errorf("500 Internal Server Error: An internal server error occurred while processing your request."),
+			noPre,
+			errorHandler,
+		},
+		{
+			"list payment link payments, an error occurs when parsing json",
+			args{
+				context.Background(),
+				"pl_ka21123129",
+				nil,
+			},
+			true,
+			fmt.Errorf("invalid character 'h' looking for beginning of object key string"),
+			noPre,
+			encodingHandler,
+		},
+		{
+			"list payment link payments, invalid url when building request",
+			args{
+				context.Background(),
+				"pl_ka21123129",
+				nil,
+			},
+			true,
+			errBadBaseURL,
+			crashSrv,
+			errorHandler,
+		},
+	}
+
+	for _, c := range cases {
+		setup()
+		defer teardown()
+
+		t.Run(c.name, func(t *testing.T) {
+			c.pre()
+			tMux.HandleFunc(fmt.Sprintf("/v2/payment-links/%s/payments", c.args.paymentLink), c.handler)
+
+			res, m, err := tClient.PaymentLinks.Payments(c.args.ctx, c.args.paymentLink, c.args.opts)
+			if c.wantErr {
+				assert.NotNil(t, err)
+				assert.EqualError(t, err, c.err.Error())
+			} else {
+				assert.Nil(t, err)
+				assert.IsType(t, &PaymentLinkPaymentsList{}, m)
+				assert.IsType(t, &http.Response{}, res.Response)
+			}
+		})
+	}
+}
diff --git a/testdata/payment_links.go b/testdata/payment_links.go
index 77f3a36..11c6605 100644
--- a/testdata/payment_links.go
+++ b/testdata/payment_links.go
@@ -151,3 +151,60 @@ const UpdatePaymentLinksResponse = `{
     }
   }
 }`
+
+const ListPaymentLinkPaymentsResponse = `{
+  "count": 1,
+  "_embedded": {
+    "payments": [
+      {
+        "resource": "payment",
+        "id": "tr_5B8cwPMGnU6qLbRvo7qEZo",
+        "mode": "live",
+        "status": "open",
+        "isCancelable": false,
+        "amount": {
+          "value": "75.00",
+          "currency": "GBP"
+        },
+        "description": "Order #12345",
+        "method": "ideal",
+        "metadata": null,
+        "details": null,
+        "profileId": "pfl_QkEhN94Ba",
+        "redirectUrl": "https://webshop.example.org/order/12345/",
+        "createdAt": "2024-02-12T11:58:35.0Z",
+        "expiresAt": "2024-02-12T12:13:35.0Z",
+        "reusable": false,
+        "_links": {
+          "self": {
+            "href": "...",
+            "type": "application/hal+json"
+          },
+          "checkout": {
+            "href": "https://www.mollie.com/checkout/issuer/select/ideal/7UhSN1zuXS",
+            "type": "text/html"
+          },
+          "dashboard": {
+            "href": "https://www.mollie.com/dashboard/org_12345678/payments/tr_5B8cwPMGnU6qLbRvo7qEZo",
+            "type": "text/html"
+          }
+        }
+      }
+    ]
+  },
+  "_links": {
+    "self": {
+      "href": "...",
+      "type": "application/hal+json"
+    },
+    "previous": null,
+    "next": {
+      "href": "https://api.mollie.com/v2/payment-links/pl_4Y0eZitmBnQ6IDoMqZQKh/payments?from=tr_SDkzMggpvx&limit=5",
+      "type": "application/hal+json"
+    },
+    "documentation": {
+      "href": "...",
+      "type": "text/html"
+    }
+  }
+}`