Skip to content

Commit 2e87fcc

Browse files
[ACL-96] Add cancel payment endpoint (#218)
1 parent 1c05046 commit 2e87fcc

File tree

3 files changed

+59
-0
lines changed

3 files changed

+59
-0
lines changed

src/TrueLayer/Payments/IPaymentsApi.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,5 +120,17 @@ Task<ApiResponse<ListPaymentRefundsResponse>> ListPaymentRefunds(string paymentI
120120
Task<ApiResponse<RefundUnion>> GetPaymentRefund(string paymentId,
121121
string refundId,
122122
CancellationToken cancellationToken = default);
123+
124+
/// <summary>
125+
/// Cancel a payment.
126+
/// </summary>
127+
/// <param name="paymentId">The payment identifier</param>
128+
/// <param name="idempotencyKey">
129+
/// An idempotency key to allow safe retrying without the operation being performed multiple times.
130+
/// 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.
131+
/// </param>
132+
/// <param name="cancellationToken">The cancellation token to cancel the operation</param>
133+
/// <returns>HTTP 202 Accepted if successful, otherwise problem details.</returns>
134+
Task<ApiResponse> CancelPayment(string paymentId, string idempotencyKey, CancellationToken cancellationToken = default);
123135
}
124136
}

src/TrueLayer/Payments/PaymentsApi.cs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,5 +198,26 @@ public async Task<ApiResponse<RefundUnion>> GetPaymentRefund(string paymentId,
198198
cancellationToken: cancellationToken
199199
);
200200
}
201+
202+
public async Task<ApiResponse> CancelPayment(string paymentId, string idempotencyKey, CancellationToken cancellationToken = default)
203+
{
204+
paymentId.NotNullOrWhiteSpace(nameof(paymentId));
205+
idempotencyKey.NotNullOrWhiteSpace(nameof(idempotencyKey));
206+
207+
ApiResponse<GetAuthTokenResponse> authResponse = await _auth.GetAuthToken(
208+
new GetAuthTokenRequest("payments"), cancellationToken);
209+
210+
if (!authResponse.IsSuccessful)
211+
{
212+
return new ApiResponse(authResponse.StatusCode, authResponse.TraceId);
213+
}
214+
215+
return await _apiClient.PostAsync(
216+
_baseUri.Append(paymentId).Append("/actions/cancel"),
217+
idempotencyKey: idempotencyKey,
218+
accessToken: authResponse.Data!.AccessToken,
219+
signingKey: _options.Payments!.SigningKey,
220+
cancellationToken: cancellationToken);
221+
}
201222
}
202223
}

test/TrueLayer.AcceptanceTests/PaymentTests.cs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,32 @@ public async Task Can_create_and_list_payment_refunds()
227227
listPaymentRefundsResponse.Data!.Items.Count.ShouldBe(1);
228228
}
229229

230+
[Fact]
231+
public async Task Can_cancel_a_payment()
232+
{
233+
// arrange
234+
var paymentRequest = CreateTestPaymentRequest();
235+
var payment = await _fixture.Client.Payments.CreatePayment(
236+
paymentRequest, idempotencyKey: Guid.NewGuid().ToString());
237+
238+
payment.IsSuccessful.ShouldBeTrue();
239+
var paymentId = payment.Data.AsT0.Id;
240+
241+
// act
242+
var cancelPaymentResponse = await _fixture.Client.Payments.CancelPayment(
243+
paymentId,
244+
idempotencyKey: Guid.NewGuid().ToString());
245+
246+
var getPaymentResponse = await _fixture.Client.Payments.GetPayment(paymentId);
247+
248+
// assert
249+
cancelPaymentResponse.IsSuccessful.ShouldBeTrue();
250+
cancelPaymentResponse.StatusCode.ShouldBe(HttpStatusCode.Accepted);
251+
getPaymentResponse.IsSuccessful.ShouldBeTrue();
252+
getPaymentResponse.Data.AsT5.ShouldNotBeNull();
253+
getPaymentResponse.Data.AsT5.FailureReason.ShouldBe("canceled");
254+
}
255+
230256
private static void AssertSchemeSelection(
231257
PaymentsSchemeSelectionUnion? actualSchemeSelection,
232258
PaymentsSchemeSelectionUnion? expectedSchemeSelection,

0 commit comments

Comments
 (0)