Skip to content

Commit

Permalink
Create payment specific mandate response types (#364)
Browse files Browse the repository at this point in the history
* Add payment specific response methods in mandate api

* Create Json converter to parse mandate payment specific responses

* Generate a new PaypalBillingAgreementId every time the CanCreatePayPalMandate integration test runs

* Remove PayPal integration test, it is not supported on our account
  • Loading branch information
Viincenttt committed Jun 4, 2024
1 parent d7800b6 commit 14dcc12
Show file tree
Hide file tree
Showing 11 changed files with 141 additions and 53 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
@using Mollie.WebApplication.Blazor.Models.Mandate
@using Mollie.Api.Client
@using Mollie.Api.Models.Mandate.Request
@using Mollie.Api.Models.Mandate.Request.PaymentSpecificParameters

@inject IMandateClient MandateClient
@inject NavigationManager NavigationManager
Expand Down
25 changes: 25 additions & 0 deletions src/Mollie.Api/Framework/Factories/MandateResponseFactory.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using System;
using Mollie.Api.Models.Mandate.Response;
using Mollie.Api.Models.Mandate.Response.PaymentSpecificParameters;
using Mollie.Api.Models.Payment;

namespace Mollie.Api.Framework.Factories;

internal class MandateResponseFactory {
public MandateResponse Create(string? paymentMethod) {
if (string.IsNullOrEmpty(paymentMethod)) {
return Activator.CreateInstance<MandateResponse>();
}

switch (paymentMethod) {
case PaymentMethod.PayPal:
return Activator.CreateInstance<PayPalMandateResponse>();
case PaymentMethod.DirectDebit:
return Activator.CreateInstance<SepaDirectDebitMandateResponse>();
case PaymentMethod.CreditCard:
return Activator.CreateInstance<CreditCardMandateResponse>();
default:
return Activator.CreateInstance<MandateResponse>();
}
}
}
2 changes: 2 additions & 0 deletions src/Mollie.Api/Framework/JsonConverterService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ internal class JsonConverterService {
new BalanceReportResponseJsonConverter(new BalanceReportResponseFactory()),
// Add a special converter for the balance transaction responses, because we need to create specific classes based on the transaction type
new BalanceTransactionJsonConverter(new BalanceTransactionFactory()),
// Add a special converter for mandate responses, because we need to create specific classes based on the payment method
new MandateResponseConverter(new MandateResponseFactory())
}
};
}
Expand Down
31 changes: 31 additions & 0 deletions src/Mollie.Api/JsonConverters/MandateResponseConverter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using System;
using Mollie.Api.Framework.Factories;
using Mollie.Api.Models.Mandate.Response;
using Newtonsoft.Json.Linq;

namespace Mollie.Api.JsonConverters;

internal class MandateResponseConverter : JsonCreationConverter<MandateResponse> {
private readonly MandateResponseFactory _mandateResponseFactory;

public MandateResponseConverter(MandateResponseFactory mandateResponseFactory) {
_mandateResponseFactory = mandateResponseFactory;
}

protected override MandateResponse Create(Type objectType, JObject jObject) {
string? paymentMethod = GetPaymentMethod(jObject);

return _mandateResponseFactory.Create(paymentMethod);
}

private string? GetPaymentMethod(JObject jObject) {
if (FieldExists("method", jObject)) {
string paymentMethodValue = (string) jObject["method"]!;
if (!string.IsNullOrEmpty(paymentMethodValue)) {
return paymentMethodValue;
}
}

return null;
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace Mollie.Api.Models.Mandate.Request
namespace Mollie.Api.Models.Mandate.Request.PaymentSpecificParameters
{
public record PayPalMandateRequest : MandateRequest
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace Mollie.Api.Models.Mandate.Request
namespace Mollie.Api.Models.Mandate.Request.PaymentSpecificParameters
{
public record SepaDirectDebitMandateRequest : MandateRequest
{
Expand Down
47 changes: 0 additions & 47 deletions src/Mollie.Api/Models/Mandate/Response/MandateResponse.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,6 @@ public record MandateResponse {
/// </summary>
public required string Method { get; set; }

/// <summary>
/// Mandate details that are different per payment method. Available fields depend on that payment method.
/// </summary>
public MandateDetails? Details { get; set; }

/// <summary>
/// The mandate’s custom reference, if this was provided when creating the mandate.
/// </summary>
Expand All @@ -50,46 +45,4 @@ public record MandateResponse {
[JsonProperty("_links")]
public required MandateResponseLinks Links { get; set; }
}

public class MandateDetails {
/// <summary>
/// The direct debit account holder's name.
/// </summary>
public string? ConsumerName { get; set; }

/// <summary>
/// The direct debit account IBAN.
/// </summary>
public string? ConsumerAccount { get; set; }

/// <summary>
/// The direct debit account BIC.
/// </summary>
public string? ConsumerBic { get; set; }

/// <summary>
/// The credit card holder's name.
/// </summary>
public string? CardHolder { get; set; }

/// <summary>
/// The last four digits of the credit card number.
/// </summary>
public string? CardNumber { get; set; }

/// <summary>
/// The credit card's label. Note that not all labels can be acquired through Mollie.
/// </summary>
public string? CardLabel { get; set; }

/// <summary>
/// Unique alphanumeric representation of credit card, usable for identifying returning customers.
/// </summary>
public string? CardFingerprint { get; set; }

/// <summary>
/// Expiry date of the credit card card in YYYY-MM-DD format.
/// </summary>
public string? CardExpiryDate { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
namespace Mollie.Api.Models.Mandate.Response.PaymentSpecificParameters;

public record CreditCardMandateResponse : MandateResponse {
public required CreditCardMandateResponseDetails Details { get; set; }
}

public record CreditCardMandateResponseDetails {
/// <summary>
/// The credit card holder's name.
/// </summary>
public string? CardHolder { get; set; }

/// <summary>
/// The last four digits of the credit card number.
/// </summary>
public string? CardNumber { get; set; }

/// <summary>
/// The credit card's label. Note that not all labels can be acquired through Mollie.
/// </summary>
public string? CardLabel { get; set; }

/// <summary>
/// Unique alphanumeric representation of credit card, usable for identifying returning customers.
/// </summary>
public string? CardFingerprint { get; set; }

/// <summary>
/// Expiry date of the credit card card in YYYY-MM-DD format.
/// </summary>
public string? CardExpiryDate { get; set; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
namespace Mollie.Api.Models.Mandate.Response.PaymentSpecificParameters;

public record PayPalMandateResponse : MandateResponse {
public required PayPalMandateResponseDetails Details { get; set; }
}

public record PayPalMandateResponseDetails {
/// <summary>
/// Only available if the payment has been completed – The consumer's name.
/// </summary>
public string? ConsumerName { get; set; }

/// <summary>
/// Only available if the payment has been completed – The consumer's IBAN.
/// </summary>
public string? ConsumerAccount { get; set; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
namespace Mollie.Api.Models.Mandate.Response.PaymentSpecificParameters;

public record SepaDirectDebitMandateResponse : MandateResponse {
public required SepaDirectDebitMandateResponseDetails Details { get; set; }
}

public record SepaDirectDebitMandateResponseDetails {
/// <summary>
/// Only available if the payment has been completed – The consumer's name.
/// </summary>
public string? ConsumerName { get; set; }

/// <summary>
/// Only available if the payment has been completed – The consumer's IBAN.
/// </summary>
public string? ConsumerAccount { get; set; }

/// <summary>
/// Only available if the payment has been completed – The consumer's bank's BIC.
/// </summary>
public string? ConsumerBic { get; set; }
}
13 changes: 9 additions & 4 deletions tests/Mollie.Tests.Integration/Api/MandateTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,11 @@
using Mollie.Api.Models.Customer.Response;
using Mollie.Api.Models.List.Response;
using Mollie.Api.Models.Mandate.Request;
using Mollie.Api.Models.Mandate.Request.PaymentSpecificParameters;
using Mollie.Api.Models.Mandate.Response;
using Mollie.Api.Models.Mandate.Response.PaymentSpecificParameters;
using Mollie.Api.Models.Payment;
using Mollie.Api.Models.Payment.Response.PaymentSpecificParameters;
using Mollie.Tests.Integration.Framework;

namespace Mollie.Tests.Integration.Api;
Expand Down Expand Up @@ -55,12 +58,12 @@ public class MandateTests : BaseMollieApiTestClass, IDisposable {
}

[DefaultRetryFact]
public async Task CanCreateMandate() {
public async Task CanCreateSepaDirectDebitMandate() {
// We can only test this if there are customers
ListResponse<CustomerResponse> customers = await _customerClient.GetCustomerListAsync();
if (customers.Count > 0) {
// If: We create a new mandate request
SepaDirectDebitMandateRequest mandateRequest = new SepaDirectDebitMandateRequest {
SepaDirectDebitMandateRequest mandateRequest = new () {
ConsumerAccount = "NL26ABNA0516682814",
ConsumerName = "John Doe",
Method = PaymentMethod.DirectDebit
Expand All @@ -70,8 +73,10 @@ public class MandateTests : BaseMollieApiTestClass, IDisposable {
MandateResponse mandateResponse = await _mandateClient.CreateMandateAsync(customers.Items.First().Id, mandateRequest);

// Then: Make sure we created a new mandate
mandateResponse.Details!.ConsumerAccount.Should().Be(mandateRequest.ConsumerAccount);
mandateResponse.Details.ConsumerName.Should().Be(mandateRequest.ConsumerName);
mandateResponse.Should().BeOfType<SepaDirectDebitMandateResponse>();
var sepaDirectDebitResponse = (SepaDirectDebitMandateResponse)mandateResponse;
sepaDirectDebitResponse.Details.ConsumerAccount.Should().Be(mandateRequest.ConsumerAccount);
sepaDirectDebitResponse.Details.ConsumerName.Should().Be(mandateRequest.ConsumerName);
}
}

Expand Down

0 comments on commit 14dcc12

Please sign in to comment.