diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 80a83337..92cf836c 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -17,6 +17,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+ filter: tree:0
- name: Setup .NET 9.0
uses: actions/setup-dotnet@v2
with:
@@ -29,10 +32,6 @@ jobs:
uses: actions/setup-dotnet@v2
with:
dotnet-version: 6.0.x
- - name: Setup .NET 3.1
- uses: actions/setup-dotnet@v2
- with:
- dotnet-version: 3.1.x
- name: Restore tools
run: dotnet tool restore
- name: Run the build script
diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml
index 33961d4b..47bafee1 100644
--- a/.github/workflows/publish.yml
+++ b/.github/workflows/publish.yml
@@ -23,6 +23,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+ filter: tree:0
- name: Setup .NET 9.0
uses: actions/setup-dotnet@v2
with:
@@ -35,10 +38,6 @@ jobs:
uses: actions/setup-dotnet@v2
with:
dotnet-version: 6.0.x
- - name: Setup .NET 3.1
- uses: actions/setup-dotnet@v2
- with:
- dotnet-version: 3.1.x
- name: Restore tools
run: dotnet tool restore
- name: Run the build script
diff --git a/src/TrueLayer/Mandates/IMandatesApi.cs b/src/TrueLayer/Mandates/IMandatesApi.cs
index 41d0c65a..4e8e1ab3 100644
--- a/src/TrueLayer/Mandates/IMandatesApi.cs
+++ b/src/TrueLayer/Mandates/IMandatesApi.cs
@@ -1,3 +1,4 @@
+using System;
using System.Threading;
using System.Threading.Tasks;
using OneOf;
@@ -28,11 +29,15 @@ public interface IMandatesApi
///
/// An idempotency key to allow safe retrying without the operation being performed multiple times.
/// The value should be unique for each operation, e.g. a UUID, with the same key being sent on a retry of the same request.
+ /// If not provided an idempotency key is automatically generated.
+ ///
+ /// The cancellation token to cancel the operation
///
- /// The cancellation token to cancel the operation
/// An API response that includes details of the created mandate if successful, otherwise problem details
Task> CreateMandate(
- CreateMandateRequest mandateRequest, string idempotencyKey, CancellationToken cancellationToken = default);
+ CreateMandateRequest mandateRequest,
+ string? idempotencyKey = null,
+ CancellationToken cancellationToken = default);
///
/// Gets a mandate
@@ -42,7 +47,24 @@ Task> CreateMandate(
/// The cancellation token to cancel the operation
/// An API response that includes details of the mandate if successful, otherwise problem details
Task> GetMandate(
- string mandateId, MandateType mandateType, CancellationToken cancellationToken = default);
+ string mandateId,
+ MandateType mandateType,
+ CancellationToken cancellationToken = default);
+
+ ///
+ /// Generates a link to the TrueLayer hosted payment page
+ ///
+ /// The mandate identifier
+ /// The resource token, returned from
+ ///
+ /// Your return URI to which the end user will be redirected after the mandate is completed.
+ /// Note this should be configured in the TrueLayer console under your application settings.
+ ///
+ /// The HPP link you can redirect the end user to
+ string CreateHostedPaymentPageLink(
+ string mandateId,
+ string resourceToken,
+ Uri returnUri);
///
/// Lists mandates for a user
@@ -52,7 +74,9 @@ Task> GetMandate(
/// The cancellation token to cancel the operation
/// An API response that includes details of the mandate if successful, otherwise problem details
Task>> ListMandates(
- ListMandatesQuery query, MandateType mandateType, CancellationToken cancellationToken = default);
+ ListMandatesQuery query,
+ MandateType mandateType,
+ CancellationToken cancellationToken = default);
///
/// Start the authorization flow for a mandate.
@@ -63,11 +87,16 @@ Task>> ListMandates(
///
/// An idempotency key to allow safe retrying without the operation being performed multiple times.
/// The value should be unique for each operation, e.g. a UUID, with the same key being sent on a retry of the same request.
+ /// If not provided an idempotency key is automatically generated.
///
/// The cancellation token to cancel the operation
/// An API response that includes details of the mandate if successful, otherwise problem details
Task> StartAuthorizationFlow(
- string mandateId, StartAuthorizationFlowRequest request, string idempotencyKey, MandateType mandateType, CancellationToken cancellationToken = default);
+ string mandateId,
+ StartAuthorizationFlowRequest request,
+ string? idempotencyKey,
+ MandateType mandateType,
+ CancellationToken cancellationToken = default);
///
/// Submit the provider details selected by the PSU.
@@ -78,11 +107,16 @@ Task> StartAuthorizationFlow(
///
/// An idempotency key to allow safe retrying without the operation being performed multiple times.
/// The value should be unique for each operation, e.g. a UUID, with the same key being sent on a retry of the same request.
+ /// If not provided an idempotency key is automatically generated.
///
/// The cancellation token to cancel the operation
/// An API response that includes details of the mandate if successful, otherwise problem details
Task> SubmitProviderSelection(
- string mandateId, SubmitProviderSelectionRequest request, string idempotencyKey, MandateType mandateType, CancellationToken cancellationToken = default);
+ string mandateId,
+ SubmitProviderSelectionRequest request,
+ string? idempotencyKey,
+ MandateType mandateType,
+ CancellationToken cancellationToken = default);
///
/// Submit the consent given by the user
@@ -95,7 +129,11 @@ Task> SubmitProviderSelection(
///
/// The cancellation token to cancel the operation
/// An API response that includes the authorization flow action details if successful, otherwise problem details
- Task> SubmitConsent(string mandateId, string idempotencyKey, MandateType mandateType, CancellationToken cancellationToken = default);
+ Task> SubmitConsent(
+ string mandateId,
+ string? idempotencyKey,
+ MandateType mandateType,
+ CancellationToken cancellationToken = default);
///
/// Get Confirmation Of Funds
@@ -107,7 +145,11 @@ Task> SubmitProviderSelection(
/// The cancellation token to cancel the operation
/// An API response that includes details of the mandate if successful, otherwise problem details
Task> GetConfirmationOfFunds(
- string mandateId, int amountInMinor, string currency, MandateType mandateType, CancellationToken cancellationToken = default);
+ string mandateId,
+ int amountInMinor,
+ string currency,
+ MandateType mandateType,
+ CancellationToken cancellationToken = default);
///
/// Gets a mandates constraints
@@ -117,18 +159,26 @@ Task> GetConfirmationOfFunds(
/// The cancellation token to cancel the operation
/// An API response that includes details of the mandate if successful, otherwise problem details
Task> GetMandateConstraints(
- string mandateId, MandateType mandateType, CancellationToken cancellationToken = default);
+ string mandateId,
+ MandateType mandateType,
+ CancellationToken cancellationToken = default);
///
/// Revoke mandate
///
/// The id of the mandate
+ /// The type of the mandate. Either sweeping or commercial
///
/// An idempotency key to allow safe retrying without the operation being performed multiple times.
/// The value should be unique for each operation, e.g. a UUID, with the same key being sent on a retry of the same request.
+ /// If not provided an idempotency key is automatically generated.
///
/// The cancellation token to cancel the operation
/// An API response that includes the payment details if successful, otherwise problem details
- Task RevokeMandate(string mandateId, string idempotencyKey, MandateType mandateType, CancellationToken cancellationToken = default);
+ Task RevokeMandate(
+ string mandateId,
+ string? idempotencyKey,
+ MandateType mandateType,
+ CancellationToken cancellationToken = default);
}
}
diff --git a/src/TrueLayer/Mandates/MandatesApi.cs b/src/TrueLayer/Mandates/MandatesApi.cs
index 505fe239..b79524be 100644
--- a/src/TrueLayer/Mandates/MandatesApi.cs
+++ b/src/TrueLayer/Mandates/MandatesApi.cs
@@ -6,6 +6,7 @@
using TrueLayer.Extensions;
using TrueLayer.Mandates.Model;
using TrueLayer.Models;
+using TrueLayer.Payments;
namespace TrueLayer.Mandates
{
@@ -25,12 +26,14 @@ internal class MandatesApi : IMandatesApi
private readonly TrueLayerOptions _options;
private readonly Uri _baseUri;
private readonly IAuthApi _auth;
+ private readonly HppLinkBuilder _hppLinkBuilder;
public MandatesApi(IApiClient apiClient, IAuthApi auth, TrueLayerOptions options)
{
_apiClient = apiClient.NotNull(nameof(apiClient));
_options = options.NotNull(nameof(options));
_auth = auth.NotNull(nameof(auth));
+ _hppLinkBuilder = new HppLinkBuilder(options.Payments?.HppUri, options.UseSandbox ?? true);
options.Payments.NotNull(nameof(options.Payments))!.Validate();
@@ -39,10 +42,13 @@ public MandatesApi(IApiClient apiClient, IAuthApi auth, TrueLayerOptions options
}
///
- public async Task> CreateMandate(CreateMandateRequest mandateRequest, string idempotencyKey, CancellationToken cancellationToken = default)
+ public async Task> CreateMandate(
+ CreateMandateRequest mandateRequest,
+ string? idempotencyKey = null,
+ CancellationToken cancellationToken = default)
{
mandateRequest.NotNull(nameof(mandateRequest));
- idempotencyKey.NotNullOrWhiteSpace(nameof(idempotencyKey));
+
var mandateType = mandateRequest.Mandate.Match(
vrpCommercial => vrpCommercial.Type,
vrpSweeping => vrpSweeping.Type);
@@ -56,7 +62,7 @@ public async Task> CreateMandate(CreateMandat
return await _apiClient.PostAsync(
_baseUri,
mandateRequest,
- idempotencyKey,
+ idempotencyKey ?? Guid.NewGuid().ToString(),
authResponse.Data!.AccessToken,
_options.Payments!.SigningKey,
cancellationToken
@@ -65,7 +71,10 @@ public async Task> CreateMandate(CreateMandat
//TODO: is it correct that this method expects a mandate type?
///
- public async Task> GetMandate(string mandateId, MandateType mandateType, CancellationToken cancellationToken = default)
+ public async Task> GetMandate(
+ string mandateId,
+ MandateType mandateType,
+ CancellationToken cancellationToken = default)
{
mandateId.NotNullOrWhiteSpace(nameof(mandateId));
mandateId.NotAUrl(nameof(mandateId));
@@ -85,7 +94,14 @@ public async Task> GetMandate(string mandateId,
}
///
- public async Task>> ListMandates(ListMandatesQuery query, MandateType mandateType, CancellationToken cancellationToken = default)
+ public string CreateHostedPaymentPageLink(string mandateId, string resourceToken, Uri returnUri)
+ => _hppLinkBuilder.Build(mandateId, resourceToken, returnUri);
+
+ ///
+ public async Task>> ListMandates(
+ ListMandatesQuery query,
+ MandateType mandateType,
+ CancellationToken cancellationToken = default)
{
var authResponse = await _auth.GetAuthToken(new GetAuthTokenRequest($"recurring_payments:{mandateType.AsString()}"), cancellationToken);
@@ -108,12 +124,17 @@ public async Task>> ListManda
}
///
- public async Task> StartAuthorizationFlow(string mandateId, StartAuthorizationFlowRequest request, string idempotencyKey, MandateType mandateType, CancellationToken cancellationToken = default)
+ public async Task> StartAuthorizationFlow(
+ string mandateId,
+ StartAuthorizationFlowRequest request,
+ string? idempotencyKey,
+ MandateType mandateType,
+ CancellationToken cancellationToken = default)
{
mandateId.NotNullOrWhiteSpace(nameof(mandateId));
mandateId.NotAUrl(nameof(mandateId));
request.NotNull(nameof(request));
- idempotencyKey.NotNullOrWhiteSpace(nameof(idempotencyKey));
+
var authResponse = await _auth.GetAuthToken(new GetAuthTokenRequest($"recurring_payments:{mandateType.AsString()}"), cancellationToken);
if (!authResponse.IsSuccessful)
@@ -124,7 +145,7 @@ public async Task> StartAuthorizationFlo
return await _apiClient.PostAsync(
_baseUri.Append(mandateId).Append(MandatesEndpoints.AuthorizationFlow),
request,
- idempotencyKey,
+ idempotencyKey ?? Guid.NewGuid().ToString(),
authResponse.Data!.AccessToken,
_options.Payments!.SigningKey,
cancellationToken
@@ -132,12 +153,17 @@ public async Task> StartAuthorizationFlo
}
///
- public async Task> SubmitProviderSelection(string mandateId, SubmitProviderSelectionRequest request, string idempotencyKey, MandateType mandateType, CancellationToken cancellationToken = default)
+ public async Task> SubmitProviderSelection(
+ string mandateId,
+ SubmitProviderSelectionRequest request,
+ string? idempotencyKey,
+ MandateType mandateType,
+ CancellationToken cancellationToken = default)
{
mandateId.NotNullOrWhiteSpace(nameof(mandateId));
mandateId.NotAUrl(nameof(mandateId));
request.NotNull(nameof(request));
- idempotencyKey.NotNullOrWhiteSpace(nameof(idempotencyKey));
+
var authResponse = await _auth.GetAuthToken(new GetAuthTokenRequest($"recurring_payments:{mandateType.AsString()}"), cancellationToken);
if (!authResponse.IsSuccessful)
@@ -148,18 +174,22 @@ public async Task> SubmitProviderSelecti
return await _apiClient.PostAsync(
_baseUri.Append(mandateId).Append(MandatesEndpoints.ProviderSelection),
request,
- idempotencyKey,
+ idempotencyKey ?? Guid.NewGuid().ToString(),
authResponse.Data!.AccessToken,
_options.Payments!.SigningKey,
cancellationToken
);
}
- public async Task> SubmitConsent(string mandateId, string idempotencyKey, MandateType mandateType, CancellationToken cancellationToken = default)
+ public async Task> SubmitConsent(
+ string mandateId,
+ string? idempotencyKey,
+ MandateType mandateType,
+ CancellationToken cancellationToken = default)
{
mandateId.NotNullOrWhiteSpace(nameof(mandateId));
mandateId.NotAUrl(nameof(mandateId));
- idempotencyKey.NotNullOrWhiteSpace(nameof(idempotencyKey));
+
var authResponse = await _auth.GetAuthToken(new GetAuthTokenRequest($"recurring_payments:{mandateType.AsString()}"), cancellationToken);
if (!authResponse.IsSuccessful)
@@ -170,7 +200,7 @@ public async Task> SubmitConsent(string
return await _apiClient.PostAsync(
_baseUri.Append(mandateId).Append(MandatesEndpoints.Consent),
null,
- idempotencyKey,
+ idempotencyKey ?? Guid.NewGuid().ToString(),
authResponse.Data!.AccessToken,
_options.Payments!.SigningKey,
cancellationToken
@@ -178,7 +208,12 @@ public async Task> SubmitConsent(string
}
///
- public async Task> GetConfirmationOfFunds(string mandateId, int amountInMinor, string currency, MandateType mandateType, CancellationToken cancellationToken = default)
+ public async Task> GetConfirmationOfFunds(
+ string mandateId,
+ int amountInMinor,
+ string currency,
+ MandateType mandateType,
+ CancellationToken cancellationToken = default)
{
mandateId.NotNullOrWhiteSpace(nameof(mandateId));
mandateId.NotAUrl(nameof(mandateId));
@@ -198,7 +233,10 @@ public async Task> GetConfirmationOf
}
///
- public async Task> GetMandateConstraints(string mandateId, MandateType mandateType, CancellationToken cancellationToken = default)
+ public async Task> GetMandateConstraints(
+ string mandateId,
+ MandateType mandateType,
+ CancellationToken cancellationToken = default)
{
mandateId.NotNullOrWhiteSpace(nameof(mandateId));
mandateId.NotAUrl(nameof(mandateId));
@@ -218,7 +256,11 @@ public async Task> GetMandateConstraints(str
}
///
- public async Task RevokeMandate(string mandateId, string idempotencyKey, MandateType mandateType, CancellationToken cancellationToken = default)
+ public async Task RevokeMandate(
+ string mandateId,
+ string? idempotencyKey,
+ MandateType mandateType,
+ CancellationToken cancellationToken = default)
{
mandateId.NotNullOrWhiteSpace(nameof(mandateId));
mandateId.NotAUrl(nameof(mandateId));
@@ -233,7 +275,7 @@ public async Task RevokeMandate(string mandateId, string idempotenc
return await _apiClient.PostAsync(
_baseUri.Append(mandateId).Append(MandatesEndpoints.Revoke),
null,
- idempotencyKey,
+ idempotencyKey ?? Guid.NewGuid().ToString(),
authResponse.Data!.AccessToken,
_options.Payments!.SigningKey,
cancellationToken
diff --git a/src/TrueLayer/Payments/IPaymentsApi.cs b/src/TrueLayer/Payments/IPaymentsApi.cs
index a8ff9a59..e2acba80 100644
--- a/src/TrueLayer/Payments/IPaymentsApi.cs
+++ b/src/TrueLayer/Payments/IPaymentsApi.cs
@@ -42,11 +42,14 @@ public interface IPaymentsApi
///
/// An idempotency key to allow safe retrying without the operation being performed multiple times.
/// The value should be unique for each operation, e.g. a UUID, with the same key being sent on a retry of the same request.
+ /// If not provided an idempotency key is automatically generated.
///
/// The cancellation token to cancel the operation
/// An API response that includes details of the created payment if successful, otherwise problem details
Task> CreatePayment(
- CreatePaymentRequest paymentRequest, string idempotencyKey, CancellationToken cancellationToken = default);
+ CreatePaymentRequest paymentRequest,
+ string? idempotencyKey = null,
+ CancellationToken cancellationToken = default);
///
/// Gets the details of an existing payment
@@ -54,7 +57,9 @@ Task> CreatePayment(
/// The payment identifier
/// The cancellation token to cancel the operation
/// An API response that includes the payment details if successful, otherwise problem details
- Task> GetPayment(string id, CancellationToken cancellationToken = default);
+ Task> GetPayment(
+ string id,
+ CancellationToken cancellationToken = default);
///
/// Generates a link to the TrueLayer hosted payment page
@@ -66,7 +71,10 @@ Task> CreatePayment(
/// Note this should be configured in the TrueLayer console under your application settings.
///
/// The HPP link you can redirect the end user to
- string CreateHostedPaymentPageLink(string paymentId, string paymentToken, Uri returnUri);
+ string CreateHostedPaymentPageLink(
+ string paymentId,
+ string paymentToken,
+ Uri returnUri);
///
/// Start the authorization flow for a payment.
@@ -75,13 +83,14 @@ Task> CreatePayment(
///
/// An idempotency key to allow safe retrying without the operation being performed multiple times.
/// The value should be unique for each operation, e.g. a UUID, with the same key being sent on a retry of the same request.
+ /// If not provided an idempotency key is automatically generated.
///
/// The start authorization request details
/// The cancellation token to cancel the operation
///
Task> StartAuthorizationFlow(
string paymentId,
- string idempotencyKey,
+ string? idempotencyKey,
StartAuthorizationFlowRequest request,
CancellationToken cancellationToken = default);
@@ -92,12 +101,14 @@ Task> StartAuthorizationFlow(
///
/// An idempotency key to allow safe retrying without the operation being performed multiple times.
/// The value should be unique for each operation, e.g. a UUID, with the same key being sent on a retry of the same request.
+ /// If not provided an idempotency key is automatically generated.
///
/// The create payment refund request
/// The cancellation token to cancel the operation
/// The id of the created refund
- Task> CreatePaymentRefund(string paymentId,
- string idempotencyKey,
+ Task> CreatePaymentRefund(
+ string paymentId,
+ string? idempotencyKey,
CreatePaymentRefundRequest request,
CancellationToken cancellationToken = default);
@@ -107,7 +118,8 @@ Task> CreatePaymentRefund(string paymen
/// The payment identifier
/// The cancellation token to cancel the operation
/// The list of refunds for a payment.
- Task> ListPaymentRefunds(string paymentId,
+ Task> ListPaymentRefunds(
+ string paymentId,
CancellationToken cancellationToken = default);
///
@@ -117,7 +129,8 @@ Task> ListPaymentRefunds(string paymentI
/// The refund identifier
/// The cancellation token to cancel the operation
/// The details of the selected refund
- Task> GetPaymentRefund(string paymentId,
+ Task> GetPaymentRefund(
+ string paymentId,
string refundId,
CancellationToken cancellationToken = default);
@@ -128,9 +141,13 @@ Task> GetPaymentRefund(string paymentId,
///
/// An idempotency key to allow safe retrying without the operation being performed multiple times.
/// The value should be unique for each operation, e.g. a UUID, with the same key being sent on a retry of the same request.
+ /// If not provided an idempotency key is automatically generated.
///
/// The cancellation token to cancel the operation
/// HTTP 202 Accepted if successful, otherwise problem details.
- Task CancelPayment(string paymentId, string idempotencyKey, CancellationToken cancellationToken = default);
+ Task CancelPayment(
+ string paymentId,
+ string? idempotencyKey = null,
+ CancellationToken cancellationToken = default);
}
}
diff --git a/src/TrueLayer/Payments/Model/CreatePaymentRefundRequest.cs b/src/TrueLayer/Payments/Model/CreatePaymentRefundRequest.cs
index b6140063..d3a84cab 100644
--- a/src/TrueLayer/Payments/Model/CreatePaymentRefundRequest.cs
+++ b/src/TrueLayer/Payments/Model/CreatePaymentRefundRequest.cs
@@ -2,6 +2,7 @@
namespace TrueLayer.Payments.Model;
-public record CreatePaymentRefundRequest(string Reference,
+public record CreatePaymentRefundRequest(
+ string Reference,
uint? AmountInMinor = null,
Dictionary? Metadata = null);
diff --git a/src/TrueLayer/Payments/PaymentsApi.cs b/src/TrueLayer/Payments/PaymentsApi.cs
index 958a6ecd..d5e092b9 100644
--- a/src/TrueLayer/Payments/PaymentsApi.cs
+++ b/src/TrueLayer/Payments/PaymentsApi.cs
@@ -55,10 +55,9 @@ public PaymentsApi(IApiClient apiClient, IAuthApi auth, TrueLayerOptions options
}
///
- public async Task> CreatePayment(CreatePaymentRequest paymentRequest, string idempotencyKey, CancellationToken cancellationToken = default)
+ public async Task> CreatePayment(CreatePaymentRequest paymentRequest, string? idempotencyKey = null, CancellationToken cancellationToken = default)
{
paymentRequest.NotNull(nameof(paymentRequest));
- idempotencyKey.NotNullOrWhiteSpace(nameof(idempotencyKey));
var authResponse = await _auth.GetAuthToken(new GetAuthTokenRequest(AuthorizationScope.Payments), cancellationToken);
@@ -70,7 +69,7 @@ public async Task> CreatePayment(CreatePaymentRe
return await _apiClient.PostAsync(
_baseUri,
paymentRequest,
- idempotencyKey,
+ idempotencyKey ?? Guid.NewGuid().ToString(),
authResponse.Data!.AccessToken,
_options.Payments!.SigningKey,
cancellationToken
@@ -105,12 +104,11 @@ public string CreateHostedPaymentPageLink(string paymentId, string paymentToken,
///
public async Task> StartAuthorizationFlow(
string paymentId,
- string idempotencyKey,
+ string? idempotencyKey,
StartAuthorizationFlowRequest request,
CancellationToken cancellationToken = default)
{
paymentId.NotNullOrWhiteSpace(nameof(paymentId));
- idempotencyKey.NotNullOrWhiteSpace(nameof(idempotencyKey));
request.NotNull(nameof(request));
var authResponse = await _auth.GetAuthToken(
@@ -124,15 +122,18 @@ public async Task> StartAuthorizationFlo
return await _apiClient.PostAsync(
_baseUri.Append(paymentId).Append(PaymentsEndpoints.AuthorizationFlow),
request,
- idempotencyKey,
+ idempotencyKey ?? Guid.NewGuid().ToString(),
authResponse.Data!.AccessToken,
_options.Payments!.SigningKey,
cancellationToken
);
}
- public async Task> CreatePaymentRefund(string paymentId,
- string idempotencyKey, CreatePaymentRefundRequest request, CancellationToken cancellationToken = default)
+ public async Task> CreatePaymentRefund(
+ string paymentId,
+ string? idempotencyKey,
+ CreatePaymentRefundRequest request,
+ CancellationToken cancellationToken = default)
{
paymentId.NotNullOrWhiteSpace(nameof(paymentId));
request.NotNull(nameof(request));
@@ -148,14 +149,15 @@ public async Task> CreatePaymentRefund(
return await _apiClient.PostAsync(
_baseUri.Append(paymentId).Append(PaymentsEndpoints.Refunds),
request,
- idempotencyKey,
+ idempotencyKey ?? Guid.NewGuid().ToString(),
authResponse.Data!.AccessToken,
_options.Payments!.SigningKey,
cancellationToken
);
}
- public async Task> ListPaymentRefunds(string paymentId,
+ public async Task> ListPaymentRefunds(
+ string paymentId,
CancellationToken cancellationToken = default)
{
paymentId.NotNullOrWhiteSpace(nameof(paymentId));
@@ -175,8 +177,10 @@ public async Task> ListPaymentRefunds(st
);
}
- public async Task> GetPaymentRefund(string paymentId,
- string refundId, CancellationToken cancellationToken = default)
+ public async Task> GetPaymentRefund(
+ string paymentId,
+ string refundId,
+ CancellationToken cancellationToken = default)
{
paymentId.NotNullOrWhiteSpace(nameof(paymentId));
refundId.NotNullOrWhiteSpace(nameof(refundId));
@@ -196,10 +200,12 @@ public async Task> GetPaymentRefund(string paymentId,
);
}
- public async Task CancelPayment(string paymentId, string idempotencyKey, CancellationToken cancellationToken = default)
+ public async Task CancelPayment(
+ string paymentId,
+ string? idempotencyKey = null,
+ CancellationToken cancellationToken = default)
{
paymentId.NotNullOrWhiteSpace(nameof(paymentId));
- idempotencyKey.NotNullOrWhiteSpace(nameof(idempotencyKey));
var authResponse = await _auth.GetAuthToken(
new GetAuthTokenRequest(AuthorizationScope.Payments), cancellationToken);
@@ -211,7 +217,7 @@ public async Task CancelPayment(string paymentId, string idempotenc
return await _apiClient.PostAsync(
_baseUri.Append(paymentId).Append(PaymentsEndpoints.Cancel),
- idempotencyKey: idempotencyKey,
+ idempotencyKey: idempotencyKey ?? Guid.NewGuid().ToString(),
accessToken: authResponse.Data!.AccessToken,
signingKey: _options.Payments!.SigningKey,
cancellationToken: cancellationToken);
diff --git a/src/TrueLayer/Payouts/IPayoutsApi.cs b/src/TrueLayer/Payouts/IPayoutsApi.cs
index d1266268..6ecb7021 100644
--- a/src/TrueLayer/Payouts/IPayoutsApi.cs
+++ b/src/TrueLayer/Payouts/IPayoutsApi.cs
@@ -25,11 +25,14 @@ public interface IPayoutsApi
///
/// An idempotency key to allow safe retrying without the operation being performed multiple times.
/// The value should be unique for each operation, e.g. a UUID, with the same key being sent on a retry of the same request.
+ /// If not provided an idempotency key is automatically generated.
///
/// The cancellation token to cancel the operation
/// An API response that includes details of the created payout if successful, otherwise problem details
Task> CreatePayout(
- CreatePayoutRequest payoutRequest, string idempotencyKey, CancellationToken cancellationToken = default);
+ CreatePayoutRequest payoutRequest,
+ string? idempotencyKey = null,
+ CancellationToken cancellationToken = default);
///
/// Gets the details of an existing payment
@@ -37,6 +40,8 @@ Task> CreatePayout(
/// The payout identifier
/// The cancellation token to cancel the operation
/// An API response that includes the payout details if successful, otherwise problem details
- Task> GetPayout(string id, CancellationToken cancellationToken = default);
+ Task> GetPayout(
+ string id,
+ CancellationToken cancellationToken = default);
}
}
diff --git a/src/TrueLayer/Payouts/PayoutsApi.cs b/src/TrueLayer/Payouts/PayoutsApi.cs
index 937a7041..a39232a4 100644
--- a/src/TrueLayer/Payouts/PayoutsApi.cs
+++ b/src/TrueLayer/Payouts/PayoutsApi.cs
@@ -37,10 +37,12 @@ public PayoutsApi(IApiClient apiClient, IAuthApi auth, TrueLayerOptions options)
}
///
- public async Task> CreatePayout(CreatePayoutRequest payoutRequest, string idempotencyKey, CancellationToken cancellationToken = default)
+ public async Task> CreatePayout(
+ CreatePayoutRequest payoutRequest,
+ string? idempotencyKey = null,
+ CancellationToken cancellationToken = default)
{
payoutRequest.NotNull(nameof(payoutRequest));
- idempotencyKey.NotNullOrWhiteSpace(nameof(idempotencyKey));
var authResponse = await _auth.GetAuthToken(new GetAuthTokenRequest(AuthorizationScope.Payments), cancellationToken);
@@ -52,14 +54,16 @@ public async Task> CreatePayout(CreatePayoutRe
return await _apiClient.PostAsync(
_baseUri,
payoutRequest,
- idempotencyKey,
+ idempotencyKey ?? Guid.NewGuid().ToString(),
authResponse.Data!.AccessToken,
_options.Payments!.SigningKey,
cancellationToken
);
}
- public async Task> GetPayout(string id, CancellationToken cancellationToken = default)
+ public async Task> GetPayout(
+ string id,
+ CancellationToken cancellationToken = default)
{
id.NotNullOrWhiteSpace(nameof(id));
id.NotAUrl(nameof(id));
diff --git a/test/TrueLayer.AcceptanceTests/MandatesTests.cs b/test/TrueLayer.AcceptanceTests/MandatesTests.cs
index ddfc1b26..07ac54b1 100644
--- a/test/TrueLayer.AcceptanceTests/MandatesTests.cs
+++ b/test/TrueLayer.AcceptanceTests/MandatesTests.cs
@@ -38,10 +38,10 @@ public class MandatesTests : IClassFixture
{
private readonly ApiTestFixture _fixture;
private TrueLayerOptions configuration;
- public string RETURN_URI = "http://localhost:3000/callback";
- public static string PROVIDER_ID = "ob-uki-mock-bank-sbox"; // Beta provider in closed access, requires a whitelisted ClientId.
- public static string COMMERCIAL_PROVIDER_ID = "ob-natwest-vrp-sandbox"; // Provider to satisfy commercial mandates creation.
- public static AccountIdentifier.SortCodeAccountNumber accountIdentifier = new("140662", "10003957");
+ private const string ReturnUri = "http://localhost:3000/callback";
+ private const string ProviderId = "ob-uki-mock-bank-sbox"; // Beta provider in closed access, requires a whitelisted ClientId.
+ private const string CommercialProviderId = "mock-payments-gb-redirect"; // Provider to satisfy commercial mandates creation.
+ private static AccountIdentifier.SortCodeAccountNumber accountIdentifier = new("140662", "10003957");
public MandatesTests(ApiTestFixture fixture)
{
@@ -117,7 +117,7 @@ public async Task Can_start_authorization(CreateMandateRequest mandateRequest)
var mandateId = createResponse.Data!.Id;
StartAuthorizationFlowRequest authorizationRequest = new(
new ProviderSelectionRequest(),
- new Redirect(new Uri(RETURN_URI)));
+ new Redirect(new Uri(ReturnUri)));
// Act
var response = await _fixture.Client.Mandates.StartAuthorizationFlow(
@@ -140,10 +140,10 @@ public async Task Can_submit_provider_selection(CreateMandateRequest mandateRequ
var createResponse = await _fixture.Client.Mandates.CreateMandate(
mandateRequest, idempotencyKey: Guid.NewGuid().ToString());
var mandateId = createResponse.Data!.Id;
- SubmitProviderSelectionRequest request = new(COMMERCIAL_PROVIDER_ID);
+ SubmitProviderSelectionRequest request = new(CommercialProviderId);
StartAuthorizationFlowRequest authorizationRequest = new(
new ProviderSelectionRequest(),
- new Redirect(new Uri(RETURN_URI)));
+ new Redirect(new Uri(ReturnUri)));
await _fixture.Client.Mandates.StartAuthorizationFlow(
mandateId, authorizationRequest, idempotencyKey: Guid.NewGuid().ToString(), MandateType.Sweeping);
// Act
@@ -161,23 +161,23 @@ await _fixture.Client.Mandates.StartAuthorizationFlow(
public async Task Can_submit_consent(CreateMandateRequest mandateRequest)
{
// Arrange
- var createResponse = await _fixture.Client.Mandates.CreateMandate(
- mandateRequest, idempotencyKey: Guid.NewGuid().ToString());
+ var createResponse = await _fixture.Client.Mandates.CreateMandate(mandateRequest);
var mandateId = createResponse.Data!.Id;
var mandateType = mandateRequest.Mandate.IsT0 ? MandateType.Commercial : MandateType.Sweeping;
- StartAuthorizationFlowRequest authorizationRequest = new(
+ var authorizationRequest = new StartAuthorizationFlowRequest(
new ProviderSelectionRequest(),
- new Redirect(new Uri(RETURN_URI)),
+ new Redirect(new Uri(ReturnUri)),
new Consent());
await _fixture.Client.Mandates.StartAuthorizationFlow(
mandateId, authorizationRequest, idempotencyKey: Guid.NewGuid().ToString(), mandateType);
- SubmitProviderSelectionRequest submitProviderRequest = new(mandateRequest.Mandate.Match(
- c => COMMERCIAL_PROVIDER_ID,
- s => PROVIDER_ID));
+ var submitProviderRequest = new SubmitProviderSelectionRequest(
+ mandateRequest.Mandate.Match(
+ commercial => CommercialProviderId,
+ sweeping => ProviderId));
await _fixture.Client.Mandates.SubmitProviderSelection(
mandateId, submitProviderRequest, idempotencyKey: Guid.NewGuid().ToString(), mandateType);
@@ -200,7 +200,7 @@ public async Task Can_Get_Funds(CreateMandateRequest mandateRequest)
var mandateId = createResponse.Data!.Id;
StartAuthorizationFlowRequest authorizationRequest = new(
new ProviderSelectionRequest(),
- new Redirect(new Uri(RETURN_URI)));
+ new Redirect(new Uri(ReturnUri)));
// Act
var response = await _fixture.Client.Mandates.StartAuthorizationFlow(
@@ -356,9 +356,9 @@ private async Task CreateAuthorizedSweepingMandate(CreateMandateRequest
createResponse.StatusCode.Should().Be(HttpStatusCode.Created);
- StartAuthorizationFlowRequest authorizationRequest = new(
+ var authorizationRequest = new StartAuthorizationFlowRequest(
new ProviderSelectionRequest(),
- new Redirect(new Uri(RETURN_URI)));
+ new Redirect(new Uri(ReturnUri)));
var startAuthResponse = await _fixture.Client.Mandates.StartAuthorizationFlow(
mandateId, authorizationRequest, idempotencyKey: Guid.NewGuid().ToString(), MandateType.Sweeping);
@@ -369,64 +369,64 @@ private async Task CreateAuthorizedSweepingMandate(CreateMandateRequest
public static IEnumerable