Skip to content

Commit

Permalink
Improve support for multi tenant setups (#391)
Browse files Browse the repository at this point in the history
Improve support for multi tenant setups, where the API key / OAuth token is not a static value
  • Loading branch information
Viincenttt authored Aug 17, 2024
1 parent 112d89a commit b41461e
Show file tree
Hide file tree
Showing 27 changed files with 202 additions and 55 deletions.
5 changes: 5 additions & 0 deletions src/Mollie.Api/Client/BalanceClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@
using System.Globalization;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Mollie.Api.Client.Abstract;
using Mollie.Api.Extensions;
using Mollie.Api.Framework.Authentication.Abstract;
using Mollie.Api.Models.Balance.Response;
using Mollie.Api.Models.Balance.Response.BalanceReport;
using Mollie.Api.Models.Balance.Response.BalanceTransaction;
Expand All @@ -16,6 +18,9 @@ public class BalanceClient : BaseMollieClient, IBalanceClient {
public BalanceClient(string apiKey, HttpClient? httpClient = null) : base(apiKey, httpClient) {
}

public BalanceClient(IMollieSecretManager mollieSecretManager, HttpClient? httpClient = null) : base(mollieSecretManager, httpClient) {
}

public async Task<BalanceResponse> GetBalanceAsync(string balanceId) {
ValidateRequiredUrlParameter(nameof(balanceId), balanceId);
return await GetAsync<BalanceResponse>($"balances/{balanceId}").ConfigureAwait(false);
Expand Down
20 changes: 15 additions & 5 deletions src/Mollie.Api/Client/BaseMollieClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
using System.Threading.Tasks;
using Mollie.Api.Extensions;
using Mollie.Api.Framework;
using Mollie.Api.Framework.Authentication;
using Mollie.Api.Framework.Authentication.Abstract;
using Mollie.Api.Framework.Idempotency;
using Mollie.Api.Models.Error;
using Mollie.Api.Models.Url;
Expand All @@ -17,7 +19,7 @@ namespace Mollie.Api.Client {
public abstract class BaseMollieClient : IDisposable {
public const string ApiEndPoint = "https://api.mollie.com/v2/";
private readonly string _apiEndpoint = ApiEndPoint;
private readonly string _apiKey;
private readonly IMollieSecretManager _mollieSecretManager;
private readonly HttpClient _httpClient;
private readonly JsonConverterService _jsonConverterService;

Expand All @@ -33,15 +35,22 @@ protected BaseMollieClient(string apiKey, HttpClient? httpClient = null) {
_jsonConverterService = new JsonConverterService();
_createdHttpClient = httpClient == null;
_httpClient = httpClient ?? new HttpClient();
_apiKey = apiKey;
_mollieSecretManager = new DefaultMollieSecretManager(apiKey);
}

protected BaseMollieClient(IMollieSecretManager mollieSecretManager, HttpClient? httpClient = null) {
_jsonConverterService = new JsonConverterService();
_createdHttpClient = httpClient == null;
_httpClient = httpClient ?? new HttpClient();
_mollieSecretManager = mollieSecretManager;
}

protected BaseMollieClient(HttpClient? httpClient = null, string apiEndpoint = ApiEndPoint) {
_apiEndpoint = apiEndpoint;
_jsonConverterService = new JsonConverterService();
_createdHttpClient = httpClient == null;
_httpClient = httpClient ?? new HttpClient();
_apiKey = string.Empty;
_mollieSecretManager = new DefaultMollieSecretManager(string.Empty);
}

public IDisposable WithIdempotencyKey(string value) {
Expand Down Expand Up @@ -99,7 +108,8 @@ private async Task<T> ProcessHttpResponseMessage<T>(HttpResponseMessage response
}

protected void ValidateApiKeyIsOauthAccesstoken(bool isConstructor = false) {
if (!_apiKey.StartsWith("access_")) {
string apiKey = _mollieSecretManager.GetBearerToken();
if (!apiKey.StartsWith("access_")) {
if (isConstructor) {
throw new InvalidOperationException(
"The provided token isn't an oauth token. You have invoked the method with oauth parameters thus an oauth accesstoken is required.");
Expand All @@ -124,7 +134,7 @@ private void ValidateUrlLink(UrlLink urlObject) {
protected virtual HttpRequestMessage CreateHttpRequest(HttpMethod method, string relativeUri, HttpContent? content = null) {
HttpRequestMessage httpRequest = new HttpRequestMessage(method, new Uri(new Uri(_apiEndpoint), relativeUri));
httpRequest.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
httpRequest.Headers.Authorization = new AuthenticationHeaderValue("Bearer", _apiKey);
httpRequest.Headers.Authorization = new AuthenticationHeaderValue("Bearer", _mollieSecretManager.GetBearerToken());
httpRequest.Headers.Add("User-Agent", GetUserAgent());
var idemPotencyKey = _idempotencyKey.Value ?? Guid.NewGuid().ToString();
httpRequest.Headers.Add("Idempotency-Key", idemPotencyKey);
Expand Down
5 changes: 5 additions & 0 deletions src/Mollie.Api/Client/CaptureClient.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
using System.Collections.Generic;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Mollie.Api.Client.Abstract;
using Mollie.Api.Extensions;
using Mollie.Api.Framework.Authentication.Abstract;
using Mollie.Api.Models.Capture.Request;
using Mollie.Api.Models.Capture.Response;
using Mollie.Api.Models.List.Response;
Expand All @@ -14,6 +16,9 @@ public class CaptureClient : BaseMollieClient, ICaptureClient {
public CaptureClient(string apiKey, HttpClient? httpClient = null) : base(apiKey, httpClient) {
}

public CaptureClient(IMollieSecretManager mollieSecretManager, HttpClient? httpClient = null) : base(mollieSecretManager, httpClient) {
}

public async Task<CaptureResponse> GetCaptureAsync(string paymentId, string captureId, bool testmode = false) {
ValidateRequiredUrlParameter(nameof(paymentId), paymentId);
ValidateRequiredUrlParameter(nameof(captureId), captureId);
Expand Down
5 changes: 5 additions & 0 deletions src/Mollie.Api/Client/ChargebackClient.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
using System.Collections.Generic;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Mollie.Api.Client.Abstract;
using Mollie.Api.Extensions;
using Mollie.Api.Framework.Authentication.Abstract;
using Mollie.Api.Models.Chargeback.Response;
using Mollie.Api.Models.List.Response;
using Mollie.Api.Models.Url;
Expand All @@ -12,6 +14,9 @@ public class ChargebackClient : BaseMollieClient, IChargebackClient {
public ChargebackClient(string apiKey, HttpClient? httpClient = null) : base(apiKey, httpClient) {
}

public ChargebackClient(IMollieSecretManager mollieSecretManager, HttpClient? httpClient = null) : base(mollieSecretManager, httpClient) {
}

public async Task<ChargebackResponse> GetChargebackAsync(string paymentId, string chargebackId, bool testmode = false) {
ValidateRequiredUrlParameter(nameof(paymentId), paymentId);
ValidateRequiredUrlParameter(nameof(chargebackId), chargebackId);
Expand Down
7 changes: 7 additions & 0 deletions src/Mollie.Api/Client/ClientLinkClient.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
using System.Collections.Generic;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Mollie.Api.Client.Abstract;
using Mollie.Api.Extensions;
using Mollie.Api.Framework.Authentication.Abstract;
using Mollie.Api.Models.ClientLink.Request;
using Mollie.Api.Models.ClientLink.Response;

Expand All @@ -17,6 +19,11 @@ public ClientLinkClient(string clientId, string oauthAccessToken, HttpClient? ht
_clientId = clientId;
}

public ClientLinkClient(string clientId, IMollieSecretManager mollieSecretManager, HttpClient? httpClient = null)
: base(mollieSecretManager, httpClient) {
_clientId = clientId;
}

public async Task<ClientLinkResponse> CreateClientLinkAsync(ClientLinkRequest request)
{
return await PostAsync<ClientLinkResponse>($"client-links", request)
Expand Down
4 changes: 4 additions & 0 deletions src/Mollie.Api/Client/CustomerClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.Threading.Tasks;
using Mollie.Api.Client.Abstract;
using Mollie.Api.Extensions;
using Mollie.Api.Framework.Authentication.Abstract;
using Mollie.Api.Models;
using Mollie.Api.Models.Customer.Request;
using Mollie.Api.Models.Customer.Response;
Expand All @@ -16,6 +17,9 @@ public class CustomerClient : BaseMollieClient, ICustomerClient {
public CustomerClient(string apiKey, HttpClient? httpClient = null) : base(apiKey, httpClient) {
}

public CustomerClient(IMollieSecretManager mollieSecretManager, HttpClient? httpClient = null) : base(mollieSecretManager, httpClient) {
}

public async Task<CustomerResponse> CreateCustomerAsync(CustomerRequest request) {
return await PostAsync<CustomerResponse>($"customers", request).ConfigureAwait(false);
}
Expand Down
4 changes: 4 additions & 0 deletions src/Mollie.Api/Client/InvoiceClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System.Threading.Tasks;
using Mollie.Api.Client.Abstract;
using Mollie.Api.Extensions;
using Mollie.Api.Framework.Authentication.Abstract;
using Mollie.Api.Models.Invoice.Response;
using Mollie.Api.Models.List.Response;
using Mollie.Api.Models.Url;
Expand All @@ -13,6 +14,9 @@ public class InvoiceClient : OauthBaseMollieClient, IInvoiceClient {
public InvoiceClient(string oauthAccessToken, HttpClient? httpClient = null) : base(oauthAccessToken, httpClient) {
}

public InvoiceClient(IMollieSecretManager mollieSecretManager, HttpClient? httpClient = null) : base(mollieSecretManager, httpClient) {
}

public async Task<InvoiceResponse> GetInvoiceAsync(string invoiceId) {
ValidateRequiredUrlParameter(nameof(invoiceId), invoiceId);
return await GetAsync<InvoiceResponse>($"invoices/{invoiceId}").ConfigureAwait(false);
Expand Down
4 changes: 4 additions & 0 deletions src/Mollie.Api/Client/MandateClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.Threading.Tasks;
using Mollie.Api.Client.Abstract;
using Mollie.Api.Extensions;
using Mollie.Api.Framework.Authentication.Abstract;
using Mollie.Api.Models;
using Mollie.Api.Models.List.Response;
using Mollie.Api.Models.Mandate.Request;
Expand All @@ -14,6 +15,9 @@ public class MandateClient : BaseMollieClient, IMandateClient {
public MandateClient(string apiKey, HttpClient? httpClient = null) : base(apiKey, httpClient) {
}

public MandateClient(IMollieSecretManager mollieSecretManager, HttpClient? httpClient = null) : base(mollieSecretManager, httpClient) {
}

public async Task<MandateResponse> GetMandateAsync(string customerId, string mandateId, bool testmode = false) {
ValidateRequiredUrlParameter(nameof(customerId), customerId);
ValidateRequiredUrlParameter(nameof(mandateId), mandateId);
Expand Down
13 changes: 12 additions & 1 deletion src/Mollie.Api/Client/OauthBaseMollieClient.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,20 @@
using System;
using System.Net.Http;
using Mollie.Api.Framework.Authentication.Abstract;

namespace Mollie.Api.Client {
public class OauthBaseMollieClient : BaseMollieClient {
public OauthBaseMollieClient(string oauthAccessToken, HttpClient? httpClient = null) : base(oauthAccessToken, httpClient) {
protected OauthBaseMollieClient(string oauthAccessToken, HttpClient? httpClient = null)
: base(oauthAccessToken, httpClient) {
ValidateApiKeyIsOauthAccesstoken(oauthAccessToken);
}

protected OauthBaseMollieClient(IMollieSecretManager mollieSecretManager, HttpClient? httpClient = null)
: base(mollieSecretManager, httpClient) {
ValidateApiKeyIsOauthAccesstoken(mollieSecretManager.GetBearerToken());
}

private void ValidateApiKeyIsOauthAccesstoken(string oauthAccessToken) {
if (string.IsNullOrWhiteSpace(oauthAccessToken)) {
throw new ArgumentNullException(nameof(oauthAccessToken), "Mollie API key cannot be empty");
}
Expand Down
4 changes: 4 additions & 0 deletions src/Mollie.Api/Client/OnboardingClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,16 @@
using Mollie.Api.Models.Onboarding.Response;
using System.Net.Http;
using System.Threading.Tasks;
using Mollie.Api.Framework.Authentication.Abstract;

namespace Mollie.Api.Client {
public class OnboardingClient : BaseMollieClient, IOnboardingClient {
public OnboardingClient(string apiKey, HttpClient? httpClient = null) : base(apiKey, httpClient) {
}

public OnboardingClient(IMollieSecretManager mollieSecretManager, HttpClient? httpClient = null) : base(mollieSecretManager, httpClient) {
}

public async Task<OnboardingStatusResponse> GetOnboardingStatusAsync() {
return await GetAsync<OnboardingStatusResponse>("onboarding/me").ConfigureAwait(false);
}
Expand Down
4 changes: 4 additions & 0 deletions src/Mollie.Api/Client/OrderClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.Threading.Tasks;
using Mollie.Api.Client.Abstract;
using Mollie.Api.Extensions;
using Mollie.Api.Framework.Authentication.Abstract;
using Mollie.Api.Models;
using Mollie.Api.Models.List.Response;
using Mollie.Api.Models.Order.Request;
Expand All @@ -16,6 +17,9 @@ public class OrderClient : BaseMollieClient, IOrderClient {
public OrderClient(string apiKey, HttpClient? httpClient = null) : base(apiKey, httpClient) {
}

public OrderClient(IMollieSecretManager mollieSecretManager, HttpClient? httpClient = null) : base(mollieSecretManager, httpClient) {
}

public async Task<OrderResponse> CreateOrderAsync(OrderRequest orderRequest) {
return await PostAsync<OrderResponse>("orders", orderRequest).ConfigureAwait(false);
}
Expand Down
4 changes: 4 additions & 0 deletions src/Mollie.Api/Client/OrganizationClient.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System.Net.Http;
using System.Threading.Tasks;
using Mollie.Api.Client.Abstract;
using Mollie.Api.Framework.Authentication.Abstract;
using Mollie.Api.Models.List.Response;
using Mollie.Api.Models.Organization;
using Mollie.Api.Models.Url;
Expand All @@ -10,6 +11,9 @@ public class OrganizationClient : OauthBaseMollieClient, IOrganizationClient {
public OrganizationClient(string oauthAccessToken, HttpClient? httpClient = null) : base(oauthAccessToken, httpClient) {
}

public OrganizationClient(IMollieSecretManager mollieSecretManager, HttpClient? httpClient = null) : base(mollieSecretManager, httpClient) {
}

public async Task<OrganizationResponse> GetCurrentOrganizationAsync() {
return await GetAsync<OrganizationResponse>($"organizations/me").ConfigureAwait(false);
}
Expand Down
4 changes: 4 additions & 0 deletions src/Mollie.Api/Client/PaymentClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.Threading.Tasks;
using Mollie.Api.Client.Abstract;
using Mollie.Api.Extensions;
using Mollie.Api.Framework.Authentication.Abstract;
using Mollie.Api.Models;
using Mollie.Api.Models.List.Response;
using Mollie.Api.Models.Payment.Request;
Expand All @@ -14,6 +15,9 @@ public class PaymentClient : BaseMollieClient, IPaymentClient {

public PaymentClient(string apiKey, HttpClient? httpClient = null) : base(apiKey, httpClient) { }

public PaymentClient(IMollieSecretManager mollieSecretManager, HttpClient? httpClient = null) : base(mollieSecretManager, httpClient) {
}

public async Task<PaymentResponse> CreatePaymentAsync(PaymentRequest paymentRequest, bool includeQrCode = false) {
if (!string.IsNullOrWhiteSpace(paymentRequest.ProfileId) || paymentRequest.Testmode.HasValue || paymentRequest.ApplicationFee != null) {
ValidateApiKeyIsOauthAccesstoken();
Expand Down
4 changes: 4 additions & 0 deletions src/Mollie.Api/Client/PaymentLinkClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.Threading.Tasks;
using Mollie.Api.Client.Abstract;
using Mollie.Api.Extensions;
using Mollie.Api.Framework.Authentication.Abstract;
using Mollie.Api.Models;
using Mollie.Api.Models.List.Response;
using Mollie.Api.Models.Payment.Response;
Expand All @@ -16,6 +17,9 @@ public class PaymentLinkClient : BaseMollieClient, IPaymentLinkClient
{
public PaymentLinkClient(string apiKey, HttpClient? httpClient = null) : base(apiKey, httpClient) { }

public PaymentLinkClient(IMollieSecretManager mollieSecretManager, HttpClient? httpClient = null) : base(mollieSecretManager, httpClient) {
}

public async Task<PaymentLinkResponse> CreatePaymentLinkAsync(PaymentLinkRequest paymentLinkRequest)
{
if (!string.IsNullOrWhiteSpace(paymentLinkRequest.ProfileId) || paymentLinkRequest.Testmode.HasValue)
Expand Down
4 changes: 4 additions & 0 deletions src/Mollie.Api/Client/PaymentMethodClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.Threading.Tasks;
using Mollie.Api.Client.Abstract;
using Mollie.Api.Extensions;
using Mollie.Api.Framework.Authentication.Abstract;
using Mollie.Api.Models;
using Mollie.Api.Models.List.Response;
using Mollie.Api.Models.Payment;
Expand All @@ -16,6 +17,9 @@ public class PaymentMethodClient : BaseMollieClient, IPaymentMethodClient
public PaymentMethodClient(string apiKey, HttpClient? httpClient = null) : base(apiKey, httpClient) {
}

public PaymentMethodClient(IMollieSecretManager mollieSecretManager, HttpClient? httpClient = null) : base(mollieSecretManager, httpClient) {
}

public async Task<PaymentMethodResponse> GetPaymentMethodAsync(
string paymentMethod,
bool includeIssuers = false,
Expand Down
4 changes: 4 additions & 0 deletions src/Mollie.Api/Client/PermissionClient.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System.Net.Http;
using System.Threading.Tasks;
using Mollie.Api.Client.Abstract;
using Mollie.Api.Framework.Authentication.Abstract;
using Mollie.Api.Models.List.Response;
using Mollie.Api.Models.Permission.Response;
using Mollie.Api.Models.Url;
Expand All @@ -10,6 +11,9 @@ public class PermissionClient : OauthBaseMollieClient, IPermissionClient {
public PermissionClient(string oauthAccessToken, HttpClient? httpClient = null) : base(oauthAccessToken, httpClient) {
}

public PermissionClient(IMollieSecretManager mollieSecretManager, HttpClient? httpClient = null) : base(mollieSecretManager, httpClient) {
}

public async Task<PermissionResponse> GetPermissionAsync(string permissionId) {
ValidateRequiredUrlParameter(nameof(permissionId), permissionId);
return await GetAsync<PermissionResponse>($"permissions/{permissionId}").ConfigureAwait(false);
Expand Down
4 changes: 4 additions & 0 deletions src/Mollie.Api/Client/ProfileClient.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System.Net.Http;
using System.Threading.Tasks;
using Mollie.Api.Client.Abstract;
using Mollie.Api.Framework.Authentication.Abstract;
using Mollie.Api.Models.List.Response;
using Mollie.Api.Models.PaymentMethod.Response;
using Mollie.Api.Models.Profile.Request;
Expand All @@ -12,6 +13,9 @@ public class ProfileClient : BaseMollieClient, IProfileClient {
public ProfileClient(string apiKey, HttpClient? httpClient = null) : base(apiKey, httpClient) {
}

public ProfileClient(IMollieSecretManager mollieSecretManager, HttpClient? httpClient = null) : base(mollieSecretManager, httpClient) {
}

public async Task<ProfileResponse> CreateProfileAsync(ProfileRequest request) {
return await PostAsync<ProfileResponse>("profiles", request).ConfigureAwait(false);
}
Expand Down
4 changes: 4 additions & 0 deletions src/Mollie.Api/Client/RefundClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.Threading.Tasks;
using Mollie.Api.Client.Abstract;
using Mollie.Api.Extensions;
using Mollie.Api.Framework.Authentication.Abstract;
using Mollie.Api.Models.List.Response;
using Mollie.Api.Models.Order.Request;
using Mollie.Api.Models.Order.Response;
Expand All @@ -15,6 +16,9 @@ public class RefundClient : BaseMollieClient, IRefundClient {
public RefundClient(string apiKey, HttpClient? httpClient = null) : base(apiKey, httpClient) {
}

public RefundClient(IMollieSecretManager mollieSecretManager, HttpClient? httpClient = null) : base(mollieSecretManager, httpClient) {
}

public async Task<RefundResponse> CreatePaymentRefundAsync(string paymentId, RefundRequest refundRequest) {
ValidateRequiredUrlParameter(nameof(paymentId), paymentId);

Expand Down
Loading

0 comments on commit b41461e

Please sign in to comment.