Skip to main content

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:
1

Payment status is PENDING

The payment must be in PENDING status. You cannot cancel payments that are already CONFIRMED or CANCELLED.
2

Payment has not expired

The payment link must not have passed its expiration date (expiresAt). Expired payments cannot be cancelled.
3

Payment belongs to your organization

You can only cancel payment links that belong to your organization.

Request Format

id
string
required
The unique identifier (UUID) of the one-time payment to cancel.

Response Format

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

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

400
error
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
404
error
Payment not found - No payment with the specified ID exists in your organization.
401
error
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:
  1. Status Change - The payment status changes from PENDING to CANCELLED
  2. Customer Access - Customers can no longer access or complete the payment using the payment link URL
  3. Irreversible - The cancellation cannot be undone
  4. 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