Skip to content

Commit

Permalink
Add support for new PaymentLink get payments api method (#382)
Browse files Browse the repository at this point in the history
  • Loading branch information
Viincenttt committed Jul 9, 2024
1 parent 06ba339 commit b75101b
Show file tree
Hide file tree
Showing 5 changed files with 200 additions and 37 deletions.
19 changes: 19 additions & 0 deletions src/Mollie.Api/Client/Abstract/IPaymentLinkClient.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using System.Threading.Tasks;
using Mollie.Api.Models;
using Mollie.Api.Models.List.Response;
using Mollie.Api.Models.Payment.Response;
using Mollie.Api.Models.PaymentLink.Request;
using Mollie.Api.Models.PaymentLink.Response;
using Mollie.Api.Models.Url;
Expand Down Expand Up @@ -73,5 +75,22 @@ Task<ListResponse<PaymentLinkResponse>> GetPaymentLinkListAsync(
/// <param name="url">The URL from which to retrieve the payment link</param>
/// <returns></returns>
Task<PaymentLinkResponse> GetPaymentLinkAsync(UrlObjectLink<PaymentLinkResponse> url);

/// <summary>
/// Retrieve the list of payments for a specific payment link.
/// </summary>
/// <param name="paymentLinkId">Provide the ID of the item you want to perform this operation on.</param>
/// <param name="from">Provide an ID to start the result set from the item with the given ID and onwards. This
/// allows you to paginate the result set.</param>
/// <param name="limit">The maximum number of items to return. Defaults to 50 items.</param>
/// <param name="testmode">Most API credentials are specifically created for either live mode or test mode. In
/// those cases the testmode query parameter can be omitted. For organization-level credentials such as OAuth access
/// tokens, you can enable test mode by setting the testmode query parameter to true. Test entities cannot be
/// retrieved when the endpoint is set to live mode, and vice versa.</param>
/// <param name="sort">Used for setting the direction of the result set. Defaults to descending order, meaning
/// the results are ordered from newest to oldest.</param>
/// <returns></returns>
Task<ListResponse<PaymentResponse>> GetPaymentLinkPaymentListAsync(
string paymentLinkId, string? from = null, int? limit = null, bool testmode = false, SortDirection? sort = null);
}
}
24 changes: 22 additions & 2 deletions src/Mollie.Api/Client/PaymentLinkClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
using System.Threading.Tasks;
using Mollie.Api.Client.Abstract;
using Mollie.Api.Extensions;
using Mollie.Api.Models;
using Mollie.Api.Models.List.Response;
using Mollie.Api.Models.Payment.Response;
using Mollie.Api.Models.PaymentLink.Request;
using Mollie.Api.Models.PaymentLink.Response;
using Mollie.Api.Models.Url;
Expand Down Expand Up @@ -65,7 +67,8 @@ public async Task<ListResponse<PaymentLinkResponse>> GetPaymentLinkListAsync(Url
return await GetAsync(url).ConfigureAwait(false);
}

public async Task<ListResponse<PaymentLinkResponse>> GetPaymentLinkListAsync(string? from = null, int? limit = null, string? profileId = null, bool testmode = false)
public async Task<ListResponse<PaymentLinkResponse>> GetPaymentLinkListAsync(
string? from = null, int? limit = null, string? profileId = null, bool testmode = false)
{
if (!string.IsNullOrWhiteSpace(profileId) || testmode)
{
Expand All @@ -79,17 +82,34 @@ public async Task<ListResponse<PaymentLinkResponse>> GetPaymentLinkListAsync(str
return await GetListAsync<ListResponse<PaymentLinkResponse>>("payment-links", from, limit, queryParameters).ConfigureAwait(false);
}

public async Task<ListResponse<PaymentResponse>> GetPaymentLinkPaymentListAsync(
string paymentLinkId, string? from = null, int? limit = null, bool testmode = false, SortDirection? sort = null)
{
ValidateRequiredUrlParameter(nameof(paymentLinkId), paymentLinkId);
if (testmode)
{
ValidateApiKeyIsOauthAccesstoken();
}

var queryParameters = BuildQueryParameters(
testmode: testmode,
sort: sort);

return await GetListAsync<ListResponse<PaymentResponse>>($"payment-links/{paymentLinkId}/payments", from, limit, queryParameters).ConfigureAwait(false);
}

private Dictionary<string, string> BuildQueryParameters(bool testmode = false) {
var result = new Dictionary<string, string>();
result.AddValueIfTrue("testmode", testmode);
return result;
}

private Dictionary<string, string> BuildQueryParameters(string? profileId = null, bool testmode = false)
private Dictionary<string, string> BuildQueryParameters(string? profileId = null, bool testmode = false, SortDirection? sort = null)
{
var result = new Dictionary<string, string>();
result.AddValueIfTrue(nameof(testmode), testmode);
result.AddValueIfNotNullOrEmpty(nameof(profileId), profileId);
result.AddValueIfNotNullOrEmpty(nameof(sort), sort?.ToString()?.ToLowerInvariant());
return result;
}
}
Expand Down
24 changes: 22 additions & 2 deletions tests/Mollie.Tests.Integration/Api/PaymentLinkTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ public async Task CanCreatePaymentLinkWithNullAmount() {
verifyPaymentLinkResponse(retrievePaymentLinkResponse);
}

[Fact]
[DefaultRetryFact]
public async Task CanUpdatePaymentLink() {
// Given: We create a new payment link
PaymentLinkRequest paymentLinkRequest = new() {
Expand All @@ -115,7 +115,7 @@ public async Task CanUpdatePaymentLink() {
updatedPaymentLinkResponse.Archived.Should().Be(paymentLinkUpdateRequest.Archived);
}

[Fact]
[DefaultRetryFact]
public async Task CanDeletePaymentLink() {
// Given: We create a new payment link
PaymentLinkRequest paymentLinkRequest = new() {
Expand All @@ -137,6 +137,26 @@ public async Task CanDeletePaymentLink() {
exception.Details.Detail.Should().Be("Payment link does not exists.");
}

[DefaultRetryFact]
public async Task CanListPaymentLinkPayments() {
// Given: We create a new payment link
PaymentLinkRequest paymentLinkRequest = new() {
Description = "Test",
Amount = new Amount(Currency.EUR, 50),
WebhookUrl = DefaultWebhookUrl,
RedirectUrl = DefaultRedirectUrl,
ExpiresAt = DateTime.Now.AddDays(1)
};
var createdPaymentLinkResponse = await _paymentLinkClient.CreatePaymentLinkAsync(paymentLinkRequest);

// When: We get the payment list of the payment link
var result = await _paymentLinkClient.GetPaymentLinkPaymentListAsync(createdPaymentLinkResponse.Id);

// Then: We expect the payment list to be returned
result.Should().NotBeNull();
result.Items.Should().HaveCount(0);
}

public void Dispose()
{
_paymentLinkClient?.Dispose();
Expand Down
1 change: 0 additions & 1 deletion tests/Mollie.Tests.Unit/Client/OrderClientTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
using System.Net.Http;
using System.Threading.Tasks;
using FluentAssertions;
using FluentAssertions.Extensions;
using Mollie.Api.Models.Order.Request;
using Mollie.Api.Models.Order.Request.ManageOrderLines;
using Mollie.Api.Models.Order.Response;
Expand Down
169 changes: 137 additions & 32 deletions tests/Mollie.Tests.Unit/Client/PaymentLinkClientTests.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
using System;
using System.Globalization;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using FluentAssertions;
using Mollie.Api.Client;
using Mollie.Api.Models;
using Mollie.Api.Models.List.Response;
using Mollie.Api.Models.Payment.Response;
using Mollie.Api.Models.PaymentLink.Request;
using Mollie.Api.Models.PaymentLink.Response;
using RichardSzalay.MockHttp;
Expand All @@ -17,38 +20,6 @@ public class PaymentLinkClientTests : BaseClientTests {
private const string DefaultDescription = "A car";
private const string DefaultWebhookUrl = "https://www.mollie.com";

private readonly string _defaultPaymentLinkJsonResponse = @$"{{
""resource"": ""payment-link"",
""id"": ""{DefaultPaymentLinkId}"",
""mode"": ""test"",
""profileId"": ""pfl_QkEhN94Ba"",
""createdAt"": ""2021-03-20T09:13:37+00:00"",
""paidAt"": null,
""updatedAt"": null,
""expiresAt"": ""2021-06-06T11:00:00+00:00"",
""amount"": {{
""value"": ""{DefaultPaymentAmount.ToString(CultureInfo.InvariantCulture)}"",
""currency"": ""EUR""
}},
""description"": ""{DefaultDescription}"",
""redirectUrl"": ""{DefaultRedirectUrl}"",
""webhookUrl"": ""{DefaultWebhookUrl}"",
""_links"": {{
""self"": {{
""href"": ""https://api.mollie.com/v2/payment-links/pl_4Y0eZitmBnQ6IDoMqZQKh"",
""type"": ""application/json""
}},
""paymentLink"": {{
""href"": ""https://useplink.com/payment/4Y0eZitmBnQ6IDoMqZQKh/"",
""type"": ""text/html""
}},
""documentation"": {{
""href"": ""https://docs.mollie.com/reference/v2/payment-links-api/create-payment-link"",
""type"": ""text/html""
}}
}}
}}";

[Fact]
public async Task CreatePaymentLinkAsync_PaymentLinkWithRequiredParameters_ResponseIsDeserializedInExpectedFormat() {
// Given: we create a payment link request with only the required parameters
Expand Down Expand Up @@ -107,12 +78,146 @@ public async Task GetPaymentLinkAsync_NoPaymentLinkIdIsGiven_ArgumentExceptionIs
exception.Message.Should().Be("Required URL argument 'paymentLinkId' is null or empty");
}

[Theory]
[InlineData(null, null, false, null, "")]
[InlineData("from", null, false, null, "?from=from")]
[InlineData("from", 50, false, null, "?from=from&limit=50")]
[InlineData(null, null, true, null, "?testmode=true")]
[InlineData(null, null, true, SortDirection.Desc, "?testmode=true&sort=desc")]
[InlineData(null, null, true, SortDirection.Asc, "?testmode=true&sort=asc")]
public async Task GetPaymentLinkPaymentListAsync_QueryParameterOptions_CorrectParametersAreAdded(
string from,
int? limit,
bool testmode,
SortDirection? sortDirection,
string expectedQueryString) {
// Given: We make a request to retrieve the list of orders
var mockHttp = CreateMockHttpMessageHandler(
HttpMethod.Get,
$"{BaseMollieClient.ApiEndPoint}payment-links/{DefaultPaymentLinkId}/payments{expectedQueryString}",
_defaultPaymentLinkPaymentsJsonResponse);
HttpClient httpClient = mockHttp.ToHttpClient();
var paymentLinkClient = new PaymentLinkClient("access_abcde", httpClient);

// When: We send the request
await paymentLinkClient.GetPaymentLinkPaymentListAsync(DefaultPaymentLinkId, from, limit, testmode, sortDirection);

// Then
mockHttp.VerifyNoOutstandingRequest();
}

[Fact]
public async Task GetPaymentLinkPaymentListAsync_ResponseIsDeserializedInExpectedFormat() {
// Given: We make a request to retrieve the list of orders
var mockHttp = CreateMockHttpMessageHandler(
HttpMethod.Get,
$"{BaseMollieClient.ApiEndPoint}payment-links/{DefaultPaymentLinkId}/payments",
_defaultPaymentLinkPaymentsJsonResponse);
HttpClient httpClient = mockHttp.ToHttpClient();
var paymentLinkClient = new PaymentLinkClient("abcde", httpClient);

// When: We send the request
ListResponse<PaymentResponse> result = await paymentLinkClient.GetPaymentLinkPaymentListAsync(DefaultPaymentLinkId);

// Then
mockHttp.VerifyNoOutstandingRequest();
result.Should().NotBeNull();
result.Count.Should().Be(1);
PaymentResponse payment = result.Items.Single();
payment.Id.Should().Be("tr_7UhSN1zuXS");
payment.Amount.Value.Should().Be(DefaultPaymentAmount.ToString(CultureInfo.InvariantCulture));
payment.Description.Should().Be(DefaultDescription);
payment.RedirectUrl.Should().Be(DefaultRedirectUrl);
payment.WebhookUrl.Should().Be(DefaultWebhookUrl);
}

private void VerifyPaymentLinkResponse(PaymentLinkResponse response) {
response.Amount.Value.Should().Be(DefaultPaymentAmount.ToString(CultureInfo.InvariantCulture));
response.Description.Should().Be(DefaultDescription);
response.Id.Should().Be(DefaultPaymentLinkId);
response.RedirectUrl.Should().Be(DefaultRedirectUrl);
response.WebhookUrl.Should().Be(DefaultWebhookUrl);
}

private readonly string _defaultPaymentLinkPaymentsJsonResponse = $@"{{
""count"": 1,
""_embedded"": {{
""payments"": [
{{
""resource"": ""payment"",
""id"": ""tr_7UhSN1zuXS"",
""mode"": ""live"",
""status"": ""open"",
""isCancelable"": false,
""amount"": {{
""value"": ""{DefaultPaymentAmount.ToString(CultureInfo.InvariantCulture)}"",
""currency"": ""EUR""
}},
""description"": ""{DefaultDescription}"",
""method"": ""ideal"",
""metadata"": null,
""details"": null,
""profileId"": ""pfl_QkEhN94Ba"",
""redirectUrl"": ""{DefaultRedirectUrl}"",
""webhookUrl"": ""{DefaultWebhookUrl}"",
""createdAt"": ""2024-02-12T11:58:35.0Z"",
""expiresAt"": ""2024-02-12T12:13:35.0Z"",
""_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_7UhSN1zuXS"",
""type"": ""text/html""
}}
}}
}}
]
}},
""_links"": {{
""previous"": null,
""next"": {{
""href"": ""https://api.mollie.com/v2/payment-links/pl_4Y0eZitmBnQ6IDoMqZQKh/payments?from=tr_SDkzMggpvx&limit=5"",
""type"": ""application/hal+json""
}}
}}
}}";

private readonly string _defaultPaymentLinkJsonResponse = @$"{{
""resource"": ""payment-link"",
""id"": ""{DefaultPaymentLinkId}"",
""mode"": ""test"",
""profileId"": ""pfl_QkEhN94Ba"",
""createdAt"": ""2021-03-20T09:13:37+00:00"",
""paidAt"": null,
""updatedAt"": null,
""expiresAt"": ""2021-06-06T11:00:00+00:00"",
""amount"": {{
""value"": ""{DefaultPaymentAmount.ToString(CultureInfo.InvariantCulture)}"",
""currency"": ""EUR""
}},
""description"": ""{DefaultDescription}"",
""redirectUrl"": ""{DefaultRedirectUrl}"",
""webhookUrl"": ""{DefaultWebhookUrl}"",
""_links"": {{
""self"": {{
""href"": ""https://api.mollie.com/v2/payment-links/pl_4Y0eZitmBnQ6IDoMqZQKh"",
""type"": ""application/json""
}},
""paymentLink"": {{
""href"": ""https://useplink.com/payment/4Y0eZitmBnQ6IDoMqZQKh/"",
""type"": ""text/html""
}},
""documentation"": {{
""href"": ""https://docs.mollie.com/reference/v2/payment-links-api/create-payment-link"",
""type"": ""text/html""
}}
}}
}}";
}
}

0 comments on commit b75101b

Please sign in to comment.