Skip to content

Commit

Permalink
#396: Fix issue where ApplicationFee was set on wrong level (#399)
Browse files Browse the repository at this point in the history
* #396: Fix issue where ApplicationFee was set on the OrderRequest instead of the OrderPaymentParameters
  • Loading branch information
Viincenttt authored Sep 29, 2024
1 parent a7f0ea3 commit cdbe820
Show file tree
Hide file tree
Showing 14 changed files with 83 additions and 36 deletions.
9 changes: 2 additions & 7 deletions src/Mollie.Api/Models/Order/Request/OrderRequest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Linq;
using Mollie.Api.JsonConverters;
using Mollie.Api.Models.Order.Request.PaymentSpecificParameters;
using Newtonsoft.Json;

namespace Mollie.Api.Models.Order.Request {
Expand Down Expand Up @@ -101,7 +102,7 @@ public string? Method {
/// <summary>
/// Optional - Any payment specific properties can be passed here.
/// </summary>
public PaymentSpecificParameters.PaymentSpecificParameters? Payment { get; set; }
public OrderPaymentParameters? Payment { get; set; }

/// <summary>
/// Provide any data you like, and we will save the data alongside the subscription. Whenever you fetch the subscription
Expand All @@ -122,12 +123,6 @@ public string? Method {
/// </summary>
public bool? ShopperCountryMustMatchBillingCountry { get; set; }

/// <summary>
/// Adding an application fee allows you to charge the merchant for the payment and transfer this to your own account.
/// </summary>
[JsonProperty("payment.applicationFee")]
public ApplicationFee? ApplicationFee { get; set; }

/// <summary>
/// Oauth only - The payment profile's unique identifier, for example pfl_3RkSN1zuPE.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
namespace Mollie.Api.Models.Order.Request.PaymentSpecificParameters {
public record ApplePaySpecificParameters : PaymentSpecificParameters {
public record ApplePaySpecificParameters : OrderPaymentParameters {
/// <summary>
/// Optional - The Apple Pay Payment Token object (encoded as JSON) that is part of the result of authorizing a payment
/// request. The token contains the payment information needed to authorize the payment.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
namespace Mollie.Api.Models.Order.Request.PaymentSpecificParameters {
public record BillieSpecificParameters : PaymentSpecificParameters {
public record BillieSpecificParameters : OrderPaymentParameters {
/// <summary>
/// Billie is a B2B payment method, thus it requires some extra information to identify the business
/// that is creating the order. It is recommended to include these parameters as part of the create
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
namespace Mollie.Api.Models.Order.Request.PaymentSpecificParameters {
public record CreditCardSpecificParameters : PaymentSpecificParameters {
public record CreditCardSpecificParameters : OrderPaymentParameters {
/// <summary>
/// The card token you get from Mollie Components. The token contains the card information
/// (such as card holder, card number and expiry date) needed to complete the payment.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
namespace Mollie.Api.Models.Order.Request.PaymentSpecificParameters {
public record GiftcardSpecificParameters : PaymentSpecificParameters {
public record GiftcardSpecificParameters : OrderPaymentParameters {
/// <summary>
/// The gift card brand to use for the payment. These issuers are not dynamically available through the Issuers API,
/// but can be retrieved by using the issuers include in the Methods API. If you need a brand not in the list, contact
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
namespace Mollie.Api.Models.Order.Request.PaymentSpecificParameters {
public record IDealSpecificParameters : PaymentSpecificParameters {
public record IDealSpecificParameters : OrderPaymentParameters {
/// <summary>
/// Optional - iDEAL issuer id. The id could for example be ideal_INGBNL2A. The returned paymentUrl will then directly
/// point to the ING web site.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
namespace Mollie.Api.Models.Order.Request.PaymentSpecificParameters {
public record KbcSpecificParameters : PaymentSpecificParameters {
public record KbcSpecificParameters : OrderPaymentParameters {
/// <summary>
/// The issuer to use for the KBC/CBC payment. These issuers are not dynamically available through the Issuers API,
/// but can be retrieved by using the issuers include in the Methods API. See the Mollie.Api.Models.Payment.Request.KbcIssuer
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
namespace Mollie.Api.Models.Order.Request.PaymentSpecificParameters {
public record KlarnaSpecificParameters<T> : PaymentSpecificParameters where T : class {
public record KlarnaSpecificParameters<T> : OrderPaymentParameters where T : class {
public T? ExtraMerchantData { get; set; }
}
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
namespace Mollie.Api.Models.Order.Request.PaymentSpecificParameters {
public record PaymentSpecificParameters {
public record OrderPaymentParameters {
public string? CustomerId { get; set; }
/// <summary>
/// See the Mollie.Api.Models.Payment.SequenceType class for a full list of known values.
/// </summary>
public string? SequenceType { get; set; }
public string? WebhookUrl { get; set; }

/// <summary>
/// Adding an application fee allows you to charge the merchant for the payment and transfer this to your own account.
/// </summary>
public ApplicationFee? ApplicationFee { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
namespace Mollie.Api.Models.Order.Request.PaymentSpecificParameters {
public record PaySafeCardSpecificParameters : PaymentSpecificParameters {
public record PaySafeCardSpecificParameters : OrderPaymentParameters {
/// <summary>
/// Used for consumer identification. For example, you could use the consumer’s IP address.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
namespace Mollie.Api.Models.Order.Request.PaymentSpecificParameters {
public record SepaDirectDebitSpecificParameters : PaymentSpecificParameters {
public record SepaDirectDebitSpecificParameters : OrderPaymentParameters {
/// <summary>
/// Optional - IBAN of the account holder.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,11 @@ public record SubscriptionResponse {
/// </summary>
public string? WebhookUrl { get; set; }

/// <summary>
/// The customer this subscription belongs to.
/// </summary>
public required string CustomerId { get; init; }

/// <summary>
/// The optional metadata you provided upon subscription creation. Metadata can for example be used to link a plan to a
/// subscription.
Expand Down
32 changes: 31 additions & 1 deletion tests/Mollie.Tests.Integration/Api/OrderTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,36 @@ public async Task CreateOrderAsync_OrderWithRequiredFields_OrderIsCreated() {
orderResponseLine.Metadata.Should().Be(expectedMetadataString);
}

[DefaultRetryFact]
public async Task CreateOrderAsync_OrderWithApplicationFee_OrderIsCreated() {
// If: we create a order request with only the required parameters
OrderRequest orderRequest = CreateOrder() with {
Payment = new OrderPaymentParameters {
ApplicationFee = new ApplicationFee {
Amount = new Amount(Currency.EUR, 0.25m),
Description = "Test"
}
}
};

// When: We send the order request to Mollie
OrderResponse result = await _orderClient.CreateOrderAsync(orderRequest);

// Then: Make sure we get a valid response
result.Should().NotBeNull();
result.Amount.Should().Be(orderRequest.Amount);
result.OrderNumber.Should().Be(orderRequest.OrderNumber);
result.Lines.Should().HaveCount(orderRequest.Lines.Count());
result.Links.Should().NotBeNull();
OrderLineRequest orderLineRequest = orderRequest.Lines.First();
OrderLineResponse orderResponseLine = result.Lines.First();
orderResponseLine.Type.Should().Be(orderLineRequest.Type);
orderResponseLine.Links.ImageUrl!.Href.Should().Be(orderLineRequest.ImageUrl);
orderResponseLine.Links.ProductUrl!.Href.Should().Be(orderLineRequest.ProductUrl);
var expectedMetadataString = result.Lines.First().Metadata;
orderResponseLine.Metadata.Should().Be(expectedMetadataString);
}

[DefaultRetryFact]
public async Task CreateOrderAsync_WithMultiplePaymentMethods_OrderIsCreated() {
// When: we create a order request and specify multiple payment methods
Expand Down Expand Up @@ -179,7 +209,7 @@ public async Task CreateOrderAsync_WithSinglePaymentMethod_OrderIsCreated() {
[DefaultRetryTheory]
[MemberData(nameof(PaymentSpecificParameters))]
public async Task CreateOrderAsync_WithPaymentSpecificParameters_OrderIsCreated(
PaymentSpecificParameters paymentSpecificParameters) {
OrderPaymentParameters paymentSpecificParameters) {

// If: we create a order request with payment specific parameters
OrderRequest orderRequest = CreateOrder();
Expand Down
46 changes: 29 additions & 17 deletions tests/Mollie.Tests.Integration/Api/SubscriptionTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -89,40 +89,38 @@ public async Task CanCreateSubscription() {
}

[DefaultRetryFact]
public async Task CanCancelSubscription() {
public async Task CanUpdateSubscription() {
// Given
string customerId = await GetFirstCustomerWithValidMandate();
ListResponse<SubscriptionResponse> subscriptions = await _subscriptionClient.GetSubscriptionListAsync(customerId);
var activeSubscription = await GetActiveSubscription();

// When
SubscriptionResponse subscriptionToCancel = subscriptions.Items
.FirstOrDefault(s => s.Status != SubscriptionStatus.Canceled);
if (subscriptionToCancel != null) {
await _subscriptionClient.CancelSubscriptionAsync(customerId, subscriptionToCancel.Id);
SubscriptionResponse cancelledSubscription = await _subscriptionClient.GetSubscriptionAsync(customerId, subscriptionToCancel.Id);
if (activeSubscription != null) {
var customerId = activeSubscription.CustomerId;
SubscriptionUpdateRequest request = new SubscriptionUpdateRequest() {
Description = $"Updated subscription {Guid.NewGuid()}"
};
SubscriptionResponse response = await _subscriptionClient.UpdateSubscriptionAsync(customerId, activeSubscription.Id, request);

// Then
cancelledSubscription.Status.Should().Be(SubscriptionStatus.Canceled);
response.Description.Should().Be(request.Description);
}
}

[DefaultRetryFact]
public async Task CanUpdateSubscription() {
public async Task CanCancelSubscription() {
// Given
string customerId = await GetFirstCustomerWithValidMandate();
ListResponse<SubscriptionResponse> subscriptions = await _subscriptionClient.GetSubscriptionListAsync(customerId);

// When
SubscriptionResponse subscriptionToUpdate = subscriptions.Items
SubscriptionResponse subscriptionToCancel = subscriptions.Items
.FirstOrDefault(s => s.Status != SubscriptionStatus.Canceled);
if (subscriptionToUpdate != null) {
SubscriptionUpdateRequest request = new SubscriptionUpdateRequest() {
Description = $"Updated subscription {Guid.NewGuid()}"
};
SubscriptionResponse response = await _subscriptionClient.UpdateSubscriptionAsync(customerId, subscriptionToUpdate.Id, request);
if (subscriptionToCancel != null) {
await _subscriptionClient.CancelSubscriptionAsync(customerId, subscriptionToCancel.Id);
SubscriptionResponse cancelledSubscription = await _subscriptionClient.GetSubscriptionAsync(customerId, subscriptionToCancel.Id);

// Then
response.Description.Should().Be(request.Description);
cancelledSubscription.Status.Should().Be(SubscriptionStatus.Canceled);
}
}

Expand Down Expand Up @@ -163,6 +161,20 @@ private async Task<string> GetFirstCustomerWithValidMandate() {
return null;
}

private async Task<SubscriptionResponse?> GetActiveSubscription() {

Check warning on line 164 in tests/Mollie.Tests.Integration/Api/SubscriptionTests.cs

View workflow job for this annotation

GitHub Actions / build

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

Check warning on line 164 in tests/Mollie.Tests.Integration/Api/SubscriptionTests.cs

View workflow job for this annotation

GitHub Actions / build

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.
ListResponse<CustomerResponse> customers = await _customerClient.GetCustomerListAsync();

foreach (CustomerResponse customer in customers.Items.OrderByDescending(x => x.CreatedAt)) {
ListResponse<SubscriptionResponse> subscriptions = await _subscriptionClient.GetSubscriptionListAsync(customer.Id);
var activeSubscription = subscriptions.Items.FirstOrDefault(x => x.Status == SubscriptionStatus.Active);
if (activeSubscription != null) {
return activeSubscription;
}
}

return null;
}

public void Dispose()
{
_subscriptionClient?.Dispose();
Expand Down

0 comments on commit cdbe820

Please sign in to comment.