Overview
The Cancel One-Time Payment endpoint allows you to cancel an active payment link before it is used or expires. Once cancelled, the payment link cannot be completed by customers and cannot be reactivated.
Common use cases:
- Customer requests to cancel an order
- Duplicate payment links were created by mistake
- Order or service was cancelled before payment
- Payment details need to be corrected (cancel and create new link)
Cancellation is permanent and irreversible. Once a payment link is cancelled, it cannot be reactivated. If you need to accept payment again, you must create a new payment link.
Authentication
This endpoint requires API key authentication using the x-api-key header.
Your API key can be found in your organization settings. Keep it secure and never expose it in client-side code.
Cancellation Rules
You can only cancel a payment link if all of these conditions are met:
Payment status is PENDING
The payment must be in PENDING status. You cannot cancel payments that are already CONFIRMED or CANCELLED.
Payment has not expired
The payment link must not have passed its expiration date (expiresAt). Expired payments cannot be cancelled.
Payment belongs to your organization
You can only cancel payment links that belong to your organization.
The unique identifier (UUID) of the one-time payment to cancel.
The endpoint returns the cancelled payment object with status: "CANCELLED":
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"status": "CANCELLED",
"description": "Payment for order #1234",
"amount": 100,
"currency": "MXN",
"externalId": "order-1234",
"expiresAt": "2026-01-11T10:30:00Z",
"createdAt": "2026-01-09T10:30:00Z",
"updatedAt": "2026-01-12T14:25:00Z"
}
Notice the updatedAt timestamp reflects when the cancellation occurred.
Examples
Cancel a payment link
curl -X PATCH 'https://api.harmony.compago.com/api/one-time-payment/550e8400-e29b-41d4-a716-446655440000/cancel' \
-H 'x-api-key: YOUR_API_KEY'
Cancel with error handling
async function cancelPayment(paymentId) {
try {
const response = await fetch(
`https://api.harmony.compago.com/api/one-time-payment/${paymentId}/cancel`,
{
method: 'PATCH',
headers: { 'x-api-key': 'YOUR_API_KEY' }
}
);
if (!response.ok) {
if (response.status === 404) {
throw new Error('Payment not found');
} else if (response.status === 400) {
const error = await response.json();
throw new Error(`Cannot cancel payment: ${error.message}`);
} else if (response.status === 401) {
throw new Error('Invalid API key');
}
throw new Error(`Unexpected error: ${response.status}`);
}
const cancelledPayment = await response.json();
return cancelledPayment;
} catch (error) {
console.error('Error cancelling payment:', error.message);
throw error;
}
}
// Usage
try {
const result = await cancelPayment('550e8400-e29b-41d4-a716-446655440000');
console.log('Successfully cancelled:', result);
} catch (error) {
console.error('Cancellation failed:', error.message);
}
Check status before cancelling
async function cancelPaymentSafely(paymentId) {
// First, get the current payment status
const getResponse = await fetch(
`https://api.harmony.compago.com/api/one-time-payment/${paymentId}`,
{
method: 'GET',
headers: { 'x-api-key': 'YOUR_API_KEY' }
}
);
if (!getResponse.ok) {
throw new Error('Payment not found');
}
const payment = await getResponse.json();
// Check if payment can be cancelled
if (payment.status !== 'PENDING') {
throw new Error(`Cannot cancel payment with status: ${payment.status}`);
}
if (payment.expired) {
throw new Error('Cannot cancel expired payment');
}
// Payment can be cancelled, proceed
const cancelResponse = await fetch(
`https://api.harmony.compago.com/api/one-time-payment/${paymentId}/cancel`,
{
method: 'PATCH',
headers: { 'x-api-key': 'YOUR_API_KEY' }
}
);
if (!cancelResponse.ok) {
throw new Error('Failed to cancel payment');
}
return await cancelResponse.json();
}
// Usage
try {
const cancelled = await cancelPaymentSafely('550e8400-e29b-41d4-a716-446655440000');
console.log('Payment cancelled:', cancelled);
} catch (error) {
console.error('Error:', error.message);
}
Error Responses
Cannot cancel payment - The payment is not in PENDING status or has already expired.Common reasons:
- Payment status is
CONFIRMED (already paid)
- Payment status is
CANCELLED (already cancelled)
- Payment has passed its
expiresAt date
Payment not found - No payment with the specified ID exists in your organization.
Unauthorized - Missing or invalid API key.
Best Practices
Check status first - Before cancelling, fetch the payment details to verify it can be cancelled. This prevents unnecessary API calls and provides better error messages.
Log cancellations - Keep audit logs of cancelled payments including the reason and who initiated the cancellation.
Notify customers - If a customer initiated a payment link that you’re cancelling, notify them via email that the payment link is no longer valid.
Handle race conditions - If a customer completes a payment at the same time you cancel it, the cancellation will fail with a 400 error. The payment platform uses database locks to prevent race conditions.
Common Use Cases
Order Cancellation Workflow
async function handleOrderCancellation(orderId) {
// 1. Find the payment link for this order
const params = new URLSearchParams({
status: 'PENDING',
page: '1',
pageSize: '100'
});
const listResponse = await fetch(
`https://api.harmony.compago.com/api/one-time-payment?${params}`,
{ headers: { 'x-api-key': 'YOUR_API_KEY' } }
);
const { items } = await listResponse.json();
const payment = items.find(p => p.externalId === orderId);
if (!payment) {
console.log('No pending payment found for this order');
return;
}
// 2. Cancel the payment link
try {
const cancelResponse = await fetch(
`https://api.harmony.compago.com/api/one-time-payment/${payment.id}/cancel`,
{
method: 'PATCH',
headers: { 'x-api-key': 'YOUR_API_KEY' }
}
);
if (cancelResponse.ok) {
console.log(`Payment link for order ${orderId} cancelled`);
// 3. Notify the customer
await sendEmailNotification(payment.billingInformation.email, {
subject: 'Order Cancelled',
message: `Your order ${orderId} has been cancelled. The payment link is no longer valid.`
});
}
} catch (error) {
console.error('Failed to cancel payment:', error);
}
}
Bulk Cancellation
async function cancelExpiringSoonPayments() {
// Find all pending payments
const params = new URLSearchParams({
status: 'PENDING',
page: '1',
pageSize: '500'
});
const listResponse = await fetch(
`https://api.harmony.compago.com/api/one-time-payment?${params}`,
{ headers: { 'x-api-key': 'YOUR_API_KEY' } }
);
const { items } = await listResponse.json();
// Filter payments expiring in next hour
const oneHourFromNow = new Date(Date.now() + 60 * 60 * 1000);
const expiringSoon = items.filter(payment => {
const expiresAt = new Date(payment.expiresAt);
return expiresAt <= oneHourFromNow;
});
console.log(`Found ${expiringSoon.length} payments expiring soon`);
// Cancel them in parallel
const cancellationPromises = expiringSoon.map(payment =>
fetch(
`https://api.harmony.compago.com/api/one-time-payment/${payment.id}/cancel`,
{
method: 'PATCH',
headers: { 'x-api-key': 'YOUR_API_KEY' }
}
).catch(error => {
console.error(`Failed to cancel ${payment.id}:`, error);
return null;
})
);
const results = await Promise.all(cancellationPromises);
const successful = results.filter(r => r?.ok).length;
console.log(`Successfully cancelled ${successful}/${expiringSoon.length} payments`);
}
Duplicate Detection and Cancellation
async function findAndCancelDuplicates(externalId) {
// Find all pending payments with the same externalId
const params = new URLSearchParams({
status: 'PENDING',
page: '1',
pageSize: '500'
});
const listResponse = await fetch(
`https://api.harmony.compago.com/api/one-time-payment?${params}`,
{ headers: { 'x-api-key': 'YOUR_API_KEY' } }
);
const { items } = await listResponse.json();
const duplicates = items.filter(p => p.externalId === externalId);
if (duplicates.length <= 1) {
console.log('No duplicates found');
return;
}
console.log(`Found ${duplicates.length} payments with externalId: ${externalId}`);
// Keep the most recent, cancel the rest
const sorted = duplicates.sort((a, b) =>
new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()
);
const toCancel = sorted.slice(1); // All except the first (most recent)
for (const payment of toCancel) {
try {
await fetch(
`https://api.harmony.compago.com/api/one-time-payment/${payment.id}/cancel`,
{
method: 'PATCH',
headers: { 'x-api-key': 'YOUR_API_KEY' }
}
);
console.log(`Cancelled duplicate: ${payment.id}`);
} catch (error) {
console.error(`Failed to cancel ${payment.id}:`, error);
}
}
console.log(`Kept payment: ${sorted[0].id}`);
}
Integration Tips
Webhook Integration - If you use webhooks for payment notifications, ensure your system can handle cancellation events to keep your order status in sync.
Idempotency - Attempting to cancel an already cancelled payment will return a 400 error. Check the error message to determine if the payment was previously cancelled.
External ID Tracking - Use the externalId field to easily find and cancel payment links associated with specific orders or transactions in your system.
What Happens After Cancellation
After a payment link is successfully cancelled:
- Status Change - The payment status changes from
PENDING to CANCELLED
- Customer Access - Customers can no longer access or complete the payment using the payment link URL
- Irreversible - The cancellation cannot be undone
- New Payment Required - If payment is still needed, you must create a new payment link
Cancelled payments remain in your system for record-keeping and can be retrieved via the List or Get endpoints.
Next Steps