diff --git a/src/Mollie.Api/Client/Abstract/IBalanceClient.cs b/src/Mollie.Api/Client/Abstract/IBalanceClient.cs index 952c3149..ecfa31b4 100644 --- a/src/Mollie.Api/Client/Abstract/IBalanceClient.cs +++ b/src/Mollie.Api/Client/Abstract/IBalanceClient.cs @@ -7,7 +7,7 @@ using Mollie.Api.Models.Url; namespace Mollie.Api.Client.Abstract { - public interface IBalanceClient : IDisposable { + public interface IBalanceClient : IBaseMollieClient { Task GetBalanceAsync(string balanceId); Task GetBalanceAsync(UrlObjectLink url); Task GetPrimaryBalanceAsync(); diff --git a/src/Mollie.Api/Client/Abstract/IBaseMollieClient.cs b/src/Mollie.Api/Client/Abstract/IBaseMollieClient.cs new file mode 100644 index 00000000..f64da5fe --- /dev/null +++ b/src/Mollie.Api/Client/Abstract/IBaseMollieClient.cs @@ -0,0 +1,9 @@ +using System; + +namespace Mollie.Api.Client.Abstract +{ + public interface IBaseMollieClient : IDisposable + { + IDisposable WithIdempotencyKey(string value); + } +} \ No newline at end of file diff --git a/src/Mollie.Api/Client/Abstract/ICaptureClient.cs b/src/Mollie.Api/Client/Abstract/ICaptureClient.cs index 0fd49447..ce8d8db3 100644 --- a/src/Mollie.Api/Client/Abstract/ICaptureClient.cs +++ b/src/Mollie.Api/Client/Abstract/ICaptureClient.cs @@ -1,12 +1,11 @@ -using System; -using System.Threading.Tasks; +using System.Threading.Tasks; using Mollie.Api.Models.List; using Mollie.Api.Models.Capture; using Mollie.Api.Models.Capture.Request; using Mollie.Api.Models.Url; namespace Mollie.Api.Client.Abstract { - public interface ICaptureClient : IDisposable { + public interface ICaptureClient : IBaseMollieClient { Task GetCaptureAsync(string paymentId, string captureId, bool testmode = false); Task GetCaptureAsync(UrlObjectLink url); Task> GetCapturesListAsync(string paymentId, bool testmode = false); diff --git a/src/Mollie.Api/Client/Abstract/IChargebacksClient.cs b/src/Mollie.Api/Client/Abstract/IChargebacksClient.cs index 9e676b1c..0db8ad3e 100644 --- a/src/Mollie.Api/Client/Abstract/IChargebacksClient.cs +++ b/src/Mollie.Api/Client/Abstract/IChargebacksClient.cs @@ -1,11 +1,10 @@ -using System; -using System.Threading.Tasks; +using System.Threading.Tasks; using Mollie.Api.Models.Chargeback; using Mollie.Api.Models.List; using Mollie.Api.Models.Url; namespace Mollie.Api.Client.Abstract { - public interface IChargebacksClient : IDisposable { + public interface IChargebacksClient : IBaseMollieClient { Task GetChargebackAsync(string paymentId, string chargebackId, bool testmode = false); Task> GetChargebacksListAsync(string paymentId, string from = null, int? limit = null, bool testmode = false); Task> GetChargebacksListAsync(string profileId = null, bool testmode = false); diff --git a/src/Mollie.Api/Client/Abstract/ICustomerClient.cs b/src/Mollie.Api/Client/Abstract/ICustomerClient.cs index 70e848ff..4bbddffa 100644 --- a/src/Mollie.Api/Client/Abstract/ICustomerClient.cs +++ b/src/Mollie.Api/Client/Abstract/ICustomerClient.cs @@ -1,5 +1,4 @@ -using System; -using System.Threading.Tasks; +using System.Threading.Tasks; using Mollie.Api.Models.Customer; using Mollie.Api.Models.List; using Mollie.Api.Models.Payment.Request; @@ -7,7 +6,7 @@ using Mollie.Api.Models.Url; namespace Mollie.Api.Client.Abstract { - public interface ICustomerClient : IDisposable { + public interface ICustomerClient : IBaseMollieClient { Task CreateCustomerAsync(CustomerRequest request); Task UpdateCustomerAsync(string customerId, CustomerRequest request); Task DeleteCustomerAsync(string customerId, bool testmode = false); diff --git a/src/Mollie.Api/Client/Abstract/IInvoicesClient.cs b/src/Mollie.Api/Client/Abstract/IInvoicesClient.cs index b99d9981..ca69b014 100644 --- a/src/Mollie.Api/Client/Abstract/IInvoicesClient.cs +++ b/src/Mollie.Api/Client/Abstract/IInvoicesClient.cs @@ -1,12 +1,11 @@ -using System; -using System.Threading.Tasks; +using System.Threading.Tasks; using Mollie.Api.Models.Invoice; using Mollie.Api.Models.List; using Mollie.Api.Models.Url; namespace Mollie.Api.Client.Abstract { - public interface IInvoicesClient : IDisposable { + public interface IInvoicesClient : IBaseMollieClient { Task GetInvoiceAsync(string invoiceId); Task GetInvoiceAsync(UrlObjectLink url); Task> GetInvoiceListAsync( diff --git a/src/Mollie.Api/Client/Abstract/IMandateClient.cs b/src/Mollie.Api/Client/Abstract/IMandateClient.cs index 9ea03015..7dcebee6 100644 --- a/src/Mollie.Api/Client/Abstract/IMandateClient.cs +++ b/src/Mollie.Api/Client/Abstract/IMandateClient.cs @@ -1,11 +1,10 @@ -using System; -using System.Threading.Tasks; +using System.Threading.Tasks; using Mollie.Api.Models.List; using Mollie.Api.Models.Mandate; using Mollie.Api.Models.Url; namespace Mollie.Api.Client.Abstract { - public interface IMandateClient : IDisposable { + public interface IMandateClient : IBaseMollieClient { Task GetMandateAsync(string customerId, string mandateId, bool testmode = false); Task> GetMandateListAsync(string customerId, string from = null, int? limit = null, bool testmode = false); Task CreateMandateAsync(string customerId, MandateRequest request); diff --git a/src/Mollie.Api/Client/Abstract/IOnboardingClient.cs b/src/Mollie.Api/Client/Abstract/IOnboardingClient.cs index 8ded51f4..3d626e66 100644 --- a/src/Mollie.Api/Client/Abstract/IOnboardingClient.cs +++ b/src/Mollie.Api/Client/Abstract/IOnboardingClient.cs @@ -1,10 +1,9 @@ -using System; -using Mollie.Api.Models.Onboarding.Request; +using Mollie.Api.Models.Onboarding.Request; using Mollie.Api.Models.Onboarding.Response; using System.Threading.Tasks; namespace Mollie.Api.Client.Abstract { - public interface IOnboardingClient : IDisposable { + public interface IOnboardingClient : IBaseMollieClient { /// /// Get the status of onboarding of the authenticated organization. /// diff --git a/src/Mollie.Api/Client/Abstract/IOrderClient.cs b/src/Mollie.Api/Client/Abstract/IOrderClient.cs index 87a713e5..c411fb2c 100644 --- a/src/Mollie.Api/Client/Abstract/IOrderClient.cs +++ b/src/Mollie.Api/Client/Abstract/IOrderClient.cs @@ -1,5 +1,4 @@ -using System; -using System.Threading.Tasks; +using System.Threading.Tasks; using Mollie.Api.Models; using Mollie.Api.Models.List; using Mollie.Api.Models.Order; @@ -9,7 +8,7 @@ using Mollie.Api.Models.Url; namespace Mollie.Api.Client.Abstract { - public interface IOrderClient : IDisposable { + public interface IOrderClient : IBaseMollieClient { Task CreateOrderAsync(OrderRequest orderRequest); Task GetOrderAsync( string orderId, bool embedPayments = false, bool embedRefunds = false, bool embedShipments = false, bool testmode = false); diff --git a/src/Mollie.Api/Client/Abstract/IOrganizationsClient.cs b/src/Mollie.Api/Client/Abstract/IOrganizationsClient.cs index feaefe41..18e9ab9a 100644 --- a/src/Mollie.Api/Client/Abstract/IOrganizationsClient.cs +++ b/src/Mollie.Api/Client/Abstract/IOrganizationsClient.cs @@ -1,11 +1,10 @@ -using System; -using System.Threading.Tasks; +using System.Threading.Tasks; using Mollie.Api.Models.List; using Mollie.Api.Models.Organization; using Mollie.Api.Models.Url; namespace Mollie.Api.Client.Abstract { - public interface IOrganizationsClient : IDisposable { + public interface IOrganizationsClient : IBaseMollieClient { Task GetCurrentOrganizationAsync(); Task GetOrganizationAsync(string organizationId); Task> GetOrganizationsListAsync(string from = null, int? limit = null); diff --git a/src/Mollie.Api/Client/Abstract/IPaymentClient.cs b/src/Mollie.Api/Client/Abstract/IPaymentClient.cs index c2ef6a0c..4378b365 100644 --- a/src/Mollie.Api/Client/Abstract/IPaymentClient.cs +++ b/src/Mollie.Api/Client/Abstract/IPaymentClient.cs @@ -1,5 +1,4 @@ -using System; -using System.Threading.Tasks; +using System.Threading.Tasks; using Mollie.Api.Models; using Mollie.Api.Models.List; using Mollie.Api.Models.Payment.Request; @@ -7,7 +6,7 @@ using Mollie.Api.Models.Url; namespace Mollie.Api.Client.Abstract { - public interface IPaymentClient : IDisposable { + public interface IPaymentClient : IBaseMollieClient { Task CreatePaymentAsync(PaymentRequest paymentRequest, bool includeQrCode = false); /// diff --git a/src/Mollie.Api/Client/Abstract/IPaymentLinkClient.cs b/src/Mollie.Api/Client/Abstract/IPaymentLinkClient.cs index a30cf887..c6359c22 100644 --- a/src/Mollie.Api/Client/Abstract/IPaymentLinkClient.cs +++ b/src/Mollie.Api/Client/Abstract/IPaymentLinkClient.cs @@ -1,12 +1,11 @@ -using System; -using System.Threading.Tasks; +using System.Threading.Tasks; using Mollie.Api.Models.List; using Mollie.Api.Models.PaymentLink.Request; using Mollie.Api.Models.PaymentLink.Response; using Mollie.Api.Models.Url; namespace Mollie.Api.Client.Abstract { - public interface IPaymentLinkClient : IDisposable { + public interface IPaymentLinkClient : IBaseMollieClient { Task CreatePaymentLinkAsync(PaymentLinkRequest paymentLinkRequest); /// diff --git a/src/Mollie.Api/Client/Abstract/IPaymentMethodClient.cs b/src/Mollie.Api/Client/Abstract/IPaymentMethodClient.cs index fe2dbfc7..79f7e4a0 100644 --- a/src/Mollie.Api/Client/Abstract/IPaymentMethodClient.cs +++ b/src/Mollie.Api/Client/Abstract/IPaymentMethodClient.cs @@ -1,5 +1,4 @@ -using System; -using System.Threading.Tasks; +using System.Threading.Tasks; using Mollie.Api.Models; using Mollie.Api.Models.List; using Mollie.Api.Models.Payment; @@ -7,7 +6,7 @@ using Mollie.Api.Models.Url; namespace Mollie.Api.Client.Abstract { - public interface IPaymentMethodClient : IDisposable { + public interface IPaymentMethodClient : IBaseMollieClient { Task GetPaymentMethodAsync( string paymentMethod, bool includeIssuers = false, diff --git a/src/Mollie.Api/Client/Abstract/IPermissionsClient.cs b/src/Mollie.Api/Client/Abstract/IPermissionsClient.cs index 9e1f3e05..8d4f9fb9 100644 --- a/src/Mollie.Api/Client/Abstract/IPermissionsClient.cs +++ b/src/Mollie.Api/Client/Abstract/IPermissionsClient.cs @@ -1,11 +1,10 @@ -using System; -using System.Threading.Tasks; +using System.Threading.Tasks; using Mollie.Api.Models.List; using Mollie.Api.Models.Permission; using Mollie.Api.Models.Url; namespace Mollie.Api.Client.Abstract { - public interface IPermissionsClient : IDisposable { + public interface IPermissionsClient : IBaseMollieClient { Task GetPermissionAsync(string permissionId); Task GetPermissionAsync(UrlObjectLink url); Task> GetPermissionListAsync(); diff --git a/src/Mollie.Api/Client/Abstract/IProfileClient.cs b/src/Mollie.Api/Client/Abstract/IProfileClient.cs index 0f2d5bfe..e083eacc 100644 --- a/src/Mollie.Api/Client/Abstract/IProfileClient.cs +++ b/src/Mollie.Api/Client/Abstract/IProfileClient.cs @@ -1,5 +1,4 @@ -using System; -using System.Threading.Tasks; +using System.Threading.Tasks; using Mollie.Api.Models.List; using Mollie.Api.Models.PaymentMethod; using Mollie.Api.Models.Profile.Request; @@ -7,7 +6,7 @@ using Mollie.Api.Models.Url; namespace Mollie.Api.Client.Abstract { - public interface IProfileClient : IDisposable { + public interface IProfileClient : IBaseMollieClient { Task CreateProfileAsync(ProfileRequest request); Task GetProfileAsync(string profileId); Task GetProfileAsync(UrlObjectLink url); diff --git a/src/Mollie.Api/Client/Abstract/IRefundClient.cs b/src/Mollie.Api/Client/Abstract/IRefundClient.cs index b47fb415..3223da69 100644 --- a/src/Mollie.Api/Client/Abstract/IRefundClient.cs +++ b/src/Mollie.Api/Client/Abstract/IRefundClient.cs @@ -1,11 +1,10 @@ -using System; -using System.Threading.Tasks; +using System.Threading.Tasks; using Mollie.Api.Models.List; using Mollie.Api.Models.Refund; using Mollie.Api.Models.Url; namespace Mollie.Api.Client.Abstract { - public interface IRefundClient : IDisposable { + public interface IRefundClient : IBaseMollieClient { Task CancelRefundAsync(string paymentId, string refundId, bool testmode = false); Task CreateRefundAsync(string paymentId, RefundRequest refundRequest); Task GetRefundAsync(string paymentId, string refundId, bool testmode = false); diff --git a/src/Mollie.Api/Client/Abstract/ISettlementsClient.cs b/src/Mollie.Api/Client/Abstract/ISettlementsClient.cs index dec04c2c..e272fbb3 100644 --- a/src/Mollie.Api/Client/Abstract/ISettlementsClient.cs +++ b/src/Mollie.Api/Client/Abstract/ISettlementsClient.cs @@ -9,7 +9,7 @@ using Mollie.Api.Models.Url; namespace Mollie.Api.Client.Abstract { - public interface ISettlementsClient : IDisposable { + public interface ISettlementsClient : IBaseMollieClient { Task GetSettlementAsync(string settlementId); Task GetNextSettlement(); Task GetOpenSettlement(); diff --git a/src/Mollie.Api/Client/Abstract/IShipmentClient.cs b/src/Mollie.Api/Client/Abstract/IShipmentClient.cs index 5c22ecef..36acccac 100644 --- a/src/Mollie.Api/Client/Abstract/IShipmentClient.cs +++ b/src/Mollie.Api/Client/Abstract/IShipmentClient.cs @@ -1,11 +1,10 @@ -using System; -using System.Threading.Tasks; +using System.Threading.Tasks; using Mollie.Api.Models.List; using Mollie.Api.Models.Shipment; using Mollie.Api.Models.Url; namespace Mollie.Api.Client.Abstract { - public interface IShipmentClient : IDisposable { + public interface IShipmentClient : IBaseMollieClient { Task CreateShipmentAsync(string orderId, ShipmentRequest shipmentRequest); Task GetShipmentAsync(string orderId, string shipmentId, bool testmode = false); Task GetShipmentAsync(UrlObjectLink url); diff --git a/src/Mollie.Api/Client/Abstract/ISubscriptionClient.cs b/src/Mollie.Api/Client/Abstract/ISubscriptionClient.cs index 95945d45..6b37f40a 100644 --- a/src/Mollie.Api/Client/Abstract/ISubscriptionClient.cs +++ b/src/Mollie.Api/Client/Abstract/ISubscriptionClient.cs @@ -1,12 +1,11 @@ -using System; -using System.Threading.Tasks; +using System.Threading.Tasks; using Mollie.Api.Models.List; using Mollie.Api.Models.Payment.Response; using Mollie.Api.Models.Subscription; using Mollie.Api.Models.Url; namespace Mollie.Api.Client.Abstract { - public interface ISubscriptionClient : IDisposable { + public interface ISubscriptionClient : IBaseMollieClient { Task CancelSubscriptionAsync(string customerId, string subscriptionId, bool testmode = false); Task CreateSubscriptionAsync(string customerId, SubscriptionRequest request); Task GetSubscriptionAsync(string customerId, string subscriptionId, bool testmode = false); diff --git a/src/Mollie.Api/Client/Abstract/ITerminalClient.cs b/src/Mollie.Api/Client/Abstract/ITerminalClient.cs index 625979f7..21548137 100644 --- a/src/Mollie.Api/Client/Abstract/ITerminalClient.cs +++ b/src/Mollie.Api/Client/Abstract/ITerminalClient.cs @@ -1,5 +1,4 @@ -using System; -using System.Threading.Tasks; +using System.Threading.Tasks; using Mollie.Api.Models.List; using Mollie.Api.Models.Terminal; using Mollie.Api.Models.Url; @@ -8,7 +7,7 @@ namespace Mollie.Api.Client.Abstract { /// /// Calls in this class are documented in https://docs.mollie.com/reference/v2/terminals-api/overview /// - public interface ITerminalClient : IDisposable { + public interface ITerminalClient : IBaseMollieClient { Task GetTerminalAsync(string terminalId); Task GetTerminalAsync(UrlObjectLink url); Task> GetTerminalListAsync(string from = null, int? limit = null, string profileId = null, bool testmode = false); diff --git a/src/Mollie.Api/Client/Abstract/IWalletClient.cs b/src/Mollie.Api/Client/Abstract/IWalletClient.cs index 27d31216..313f4310 100644 --- a/src/Mollie.Api/Client/Abstract/IWalletClient.cs +++ b/src/Mollie.Api/Client/Abstract/IWalletClient.cs @@ -3,7 +3,7 @@ using Mollie.Api.Models.Wallet.Response; namespace Mollie.Api.Client.Abstract { - public interface IWalletClient { + public interface IWalletClient : IBaseMollieClient { Task RequestApplePayPaymentSessionAsync(ApplePayPaymentSessionRequest request); } } \ No newline at end of file diff --git a/src/Mollie.Api/Client/BaseMollieClient.cs b/src/Mollie.Api/Client/BaseMollieClient.cs index 67dfe22c..def79834 100644 --- a/src/Mollie.Api/Client/BaseMollieClient.cs +++ b/src/Mollie.Api/Client/BaseMollieClient.cs @@ -8,6 +8,7 @@ using System.Threading.Tasks; using Mollie.Api.Extensions; using Mollie.Api.Framework; +using Mollie.Api.Framework.Idempotency; using Mollie.Api.Models.Url; namespace Mollie.Api.Client { @@ -17,6 +18,8 @@ public abstract class BaseMollieClient : IDisposable { private readonly string _apiKey; private readonly HttpClient _httpClient; private readonly JsonConverterService _jsonConverterService; + + private readonly AsyncLocalVariable _idempotencyKey = new AsyncLocalVariable(null); private readonly bool _createdHttpClient = false; @@ -37,6 +40,11 @@ protected BaseMollieClient(HttpClient httpClient = null, string apiEndpoint = Ap this._createdHttpClient = httpClient == null; this._httpClient = httpClient ?? new HttpClient(); } + + public IDisposable WithIdempotencyKey(string value) { + _idempotencyKey.Value = value; + return _idempotencyKey; + } private async Task SendHttpRequest(HttpMethod httpMethod, string relativeUri, object data = null) { HttpRequestMessage httpRequest = this.CreateHttpRequest(httpMethod, relativeUri); @@ -127,7 +135,8 @@ protected virtual HttpRequestMessage CreateHttpRequest(HttpMethod method, string httpRequest.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); httpRequest.Headers.Authorization = new AuthenticationHeaderValue("Bearer", this._apiKey); httpRequest.Headers.Add("User-Agent", this.GetUserAgent()); - httpRequest.Headers.Add("Idempotency-Key", Guid.NewGuid().ToString()); + var idemPotencyKey = _idempotencyKey.Value ?? Guid.NewGuid().ToString(); + httpRequest.Headers.Add("Idempotency-Key", idemPotencyKey); httpRequest.Content = content; return httpRequest; diff --git a/src/Mollie.Api/Framework/Idempotency/AsyncLocalVariable.cs b/src/Mollie.Api/Framework/Idempotency/AsyncLocalVariable.cs new file mode 100644 index 00000000..329f3aed --- /dev/null +++ b/src/Mollie.Api/Framework/Idempotency/AsyncLocalVariable.cs @@ -0,0 +1,26 @@ +using System; +using System.Threading; + +namespace Mollie.Api.Framework.Idempotency +{ + public class AsyncLocalVariable : IDisposable where T : class + { + private readonly AsyncLocal _asyncLocal = new AsyncLocal(); + + public T Value + { + get => _asyncLocal.Value; + set => _asyncLocal.Value = value; + } + + public AsyncLocalVariable(T value) + { + _asyncLocal.Value = value; + } + + public void Dispose() + { + _asyncLocal.Value = null; + } + } +} \ No newline at end of file diff --git a/src/Mollie.Api/Mollie.Api.csproj b/src/Mollie.Api/Mollie.Api.csproj index 9bf51458..0e3d4f35 100644 --- a/src/Mollie.Api/Mollie.Api.csproj +++ b/src/Mollie.Api/Mollie.Api.csproj @@ -18,8 +18,7 @@ - - + diff --git a/tests/Mollie.Tests.Integration/Api/PaymentTests.cs b/tests/Mollie.Tests.Integration/Api/PaymentTests.cs index 6ce27b2e..cfd1d42e 100644 --- a/tests/Mollie.Tests.Integration/Api/PaymentTests.cs +++ b/tests/Mollie.Tests.Integration/Api/PaymentTests.cs @@ -102,6 +102,26 @@ public async Task CanCreateDefaultPaymentWithOnlyRequiredFields() { result.Description.Should().Be(paymentRequest.Description); result.RedirectUrl.Should().Be(paymentRequest.RedirectUrl); } + + [DefaultRetryFact] + public async Task CanCreateDefaultPaymentWithCustomIdempotencyKey() { + // Given: we create a payment request with only the required parameters + PaymentRequest paymentRequest = new PaymentRequest() { + Amount = new Amount(Currency.EUR, "100.00"), + Description = "Description", + RedirectUrl = DefaultRedirectUrl + }; + + // When: We send the payment request to Mollie + using (_paymentClient.WithIdempotencyKey("my-idempotency-key")) + { + PaymentResponse firstAttempt = await _paymentClient.CreatePaymentAsync(paymentRequest); + PaymentResponse secondAttempt = await _paymentClient.CreatePaymentAsync(paymentRequest); + + // Then: Make sure the responses have the same payment Id + firstAttempt.Id.Should().Be(secondAttempt.Id); + } + } [DefaultRetryFact] public async Task CanCreateDefaultPaymentWithAllFields() { diff --git a/tests/Mollie.Tests.Unit/Client/PaymentClientTests.cs b/tests/Mollie.Tests.Unit/Client/PaymentClientTests.cs index 2ea5c082..297f227d 100644 --- a/tests/Mollie.Tests.Unit/Client/PaymentClientTests.cs +++ b/tests/Mollie.Tests.Unit/Client/PaymentClientTests.cs @@ -6,6 +6,7 @@ using Mollie.Api.Models.Payment.Response; using RichardSzalay.MockHttp; using System.Collections.Generic; +using System.Linq; using System.Net.Http; using System.Threading.Tasks; using FluentAssertions; @@ -16,6 +17,43 @@ namespace Mollie.Tests.Unit.Client; public class PaymentClientTests : BaseClientTests { + [Fact] + public async Task CreatePaymentAsync_WithCustomIdempotencyKey_CustomIdemPotencyKeyIsSent() + { + // Given: We create a payment request with only the required parameters + PaymentRequest paymentRequest = new PaymentRequest() + { + Amount = new Amount(Currency.EUR, "100.00"), + Description = "Description", + RedirectUrl = "http://www.mollie.com" + }; + const string customIdempotencyKey1 = "my-idempotency-key-1"; + const string customIdempotencyKey2 = "my-idempotency-key-2"; + const string jsonToReturnInMockResponse = defaultPaymentJsonResponse; + var mockHttp = new MockHttpMessageHandler(); + mockHttp.Expect(HttpMethod.Post, $"{BaseMollieClient.ApiEndPoint}*") + .WithHeaders("Idempotency-Key", customIdempotencyKey1) + .Respond("application/json", jsonToReturnInMockResponse); + mockHttp.Expect(HttpMethod.Post, $"{BaseMollieClient.ApiEndPoint}*") + .WithHeaders("Idempotency-Key", customIdempotencyKey2) + .Respond("application/json", jsonToReturnInMockResponse); + HttpClient httpClient = mockHttp.ToHttpClient(); + PaymentClient paymentClient = new PaymentClient("abcde", httpClient); + + // Act + using (paymentClient.WithIdempotencyKey(customIdempotencyKey1)) + { + await paymentClient.CreatePaymentAsync(paymentRequest); + } + using (paymentClient.WithIdempotencyKey(customIdempotencyKey2)) + { + await paymentClient.CreatePaymentAsync(paymentRequest); + } + + // Assert + mockHttp.VerifyNoOutstandingExpectation(); + } + [Fact] public async Task CreatePaymentAsync_PaymentWithRequiredParameters_ResponseIsDeserializedInExpectedFormat() { // Given: we create a payment request with only the required parameters diff --git a/tests/Mollie.Tests.Unit/DependencyInjectionTests.cs b/tests/Mollie.Tests.Unit/DependencyInjectionTests.cs index 5d29fba2..01b3b177 100644 --- a/tests/Mollie.Tests.Unit/DependencyInjectionTests.cs +++ b/tests/Mollie.Tests.Unit/DependencyInjectionTests.cs @@ -34,8 +34,11 @@ public void AddMollieClient_ShouldRegisterAllApiInterfaces() .Where(type => type.Namespace == typeof(IPaymentClient).Namespace); foreach (var apiClientInterface in apiClientInterfaces) { - var apiClientImplementation = serviceProvider.GetService(apiClientInterface); - apiClientImplementation.Should().NotBeNull(); + if (apiClientInterface != typeof(IBaseMollieClient)) + { + var apiClientImplementation = serviceProvider.GetService(apiClientInterface); + apiClientImplementation.Should().NotBeNull(); + } } } } \ No newline at end of file