Cancel Payment Link
Cancel a payment link to prevent further payments.
Endpoint
Section titled “Endpoint”PATCH /v1/payment-links/{hashUrl}/cancelAuthentication
Section titled “Authentication”Requires API key authentication via X-API-Key header.
Path Parameters
Section titled “Path Parameters”| Parameter | Type | Required | Description |
|---|---|---|---|
hashUrl | string | Yes | The hash URL of the payment link |
Request Example
Section titled “Request Example”cURL Example
Section titled “cURL Example”curl -X PATCH "https://api-pay.zelta.dev/v1/payment-links/abc123/cancel" \ -H "X-API-Key: your-api-key-here" \ -H "Content-Type: application/json"Node.js Example
Section titled “Node.js Example”const hashUrl = 'abc123';const response = await fetch(`https://api-pay.zelta.dev/v1/payment-links/${hashUrl}/cancel`, { method: 'PATCH', headers: { 'X-API-Key': 'your-api-key-here', 'Content-Type': 'application/json' }});
const result = await response.json();console.log(result);Python Example
Section titled “Python Example”import requests
hash_url = 'abc123'headers = { 'X-API-Key': 'your-api-key-here', 'Content-Type': 'application/json'}
response = requests.patch( f'https://api-pay.zelta.dev/v1/payment-links/{hash_url}/cancel', headers=headers)
result = response.json()print(result)PHP Example
Section titled “PHP Example”$hashUrl = 'abc123';$headers = [ 'X-API-Key: your-api-key-here', 'Content-Type: application/json'];
$ch = curl_init();curl_setopt($ch, CURLOPT_URL, "https://api-pay.zelta.dev/v1/payment-links/{$hashUrl}/cancel");curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PATCH');curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);curl_close($ch);
$result = json_decode($response, true);Response Format
Section titled “Response Format”Success Response
Section titled “Success Response”{ "success": true, "data": { "paymentLink": { "id": "pl_1234567890abcdef", "paymentLinkUrl": "https://pay.zelta.dev/abc123", "customerName": "John Doe", "concept": "Consulting Service", "amount": 2500, "status": "cancelled", "createdAt": "2024-01-15T10:30:00.000Z", "expiresAt": "2024-01-22T10:30:00.000Z", "cancelledAt": "2024-01-15T12:00:00.000Z", "completedAt": null, "isTest": true, "redirectUrl": "https://myapp.com/success", "metadata": { "orderId": "ORD-001", "service": "consulting" } } }, "message": "Payment link cancelled successfully", "timestamp": "2024-01-15T12:00:00.000Z"}Response Fields
Section titled “Response Fields”| Field | Type | Description |
|---|---|---|
id | string | Unique payment link identifier |
paymentLinkUrl | string | URL for the payment link |
customerName | string | Name of the customer |
customerEmail | string|null | Customer’s email address |
concept | string | Description of what’s being paid for |
amount | integer | Amount in cents |
status | string | Payment link status (now “cancelled”) |
createdAt | string | ISO 8601 timestamp of creation |
expiresAt | string | ISO 8601 timestamp of expiration |
cancelledAt | string | ISO 8601 timestamp of cancellation |
completedAt | string|null | ISO 8601 timestamp of completion |
isTest | boolean | Whether this is a test payment |
redirectUrl | string|null | Custom redirect URL |
metadata | object|null | Custom metadata |
Status Codes
Section titled “Status Codes”| Code | Description |
|---|---|
200 | OK - Payment link cancelled successfully |
400 | Bad Request - Invalid state for cancellation |
401 | Unauthorized - Missing or invalid API key |
403 | Forbidden - Account suspended |
404 | Not Found - Payment link not found or not owned by account |
429 | Too Many Requests - Rate limit exceeded |
Error Responses
Section titled “Error Responses”Payment Link Not Found
Section titled “Payment Link Not Found”{ "success": false, "message": "Payment link not found", "error": { "code": "ERR_PAYMENT_LINK_NOT_FOUND" }, "timestamp": "2024-01-15T12:00:00.000Z"}Invalid State for Cancellation
Section titled “Invalid State for Cancellation”{ "success": false, "message": "Payment link cannot be cancelled", "error": { "code": "ERR_INVALID_STATE", "details": "Payment link is already completed" }, "timestamp": "2024-01-15T12:00:00.000Z"}Missing API Key
Section titled “Missing API Key”{ "success": false, "message": "Missing X-API-Key header", "error": { "code": "ERR_MISSING_API_KEY" }, "timestamp": "2024-01-15T12:00:00.000Z"}Cancellation Rules
Section titled “Cancellation Rules”Allowed States
Section titled “Allowed States”Payment links can only be cancelled if they are in the following states:
pending- Waiting for paymentexpired- Expired due to time limit
Not Allowed States
Section titled “Not Allowed States”Payment links cannot be cancelled if they are in the following states:
completed- Payment successfulcancelled- Already cancelled
Implementation Examples
Section titled “Implementation Examples”Basic Cancellation
Section titled “Basic Cancellation”Node.js Example
Section titled “Node.js Example”async function cancelPaymentLink(apiKey, hashUrl) { try { const response = await fetch(`https://api-pay.zelta.dev/v1/payment-links/${hashUrl}/cancel`, { method: 'PATCH', headers: { 'X-API-Key': apiKey, 'Content-Type': 'application/json' } });
const result = await response.json();
if (!response.ok) { throw new Error(result.message); }
return result.data.paymentLink; } catch (error) { console.error('Error cancelling payment link:', error); throw error; }}
// Usageconst cancelledLink = await cancelPaymentLink('your-api-key', 'abc123');console.log('Payment link cancelled:', cancelledLink);Python Example
Section titled “Python Example”import requests
def cancel_payment_link(api_key, hash_url): try: headers = { 'X-API-Key': api_key, 'Content-Type': 'application/json' }
response = requests.patch( f'https://api-pay.zelta.dev/v1/payment-links/{hash_url}/cancel', headers=headers )
result = response.json()
if not response.ok: raise Exception(result['message'])
return result['data']['paymentLink'] except Exception as error: print(f'Error cancelling payment link: {error}') raise
# Usagecancelled_link = cancel_payment_link('your-api-key', 'abc123')print('Payment link cancelled:', cancelled_link)Safe Cancellation with State Check
Section titled “Safe Cancellation with State Check”Node.js Example
Section titled “Node.js Example”async function safeCancelPaymentLink(apiKey, hashUrl) { try { // First, check the current status const getResponse = await fetch(`https://api-pay.zelta.dev/v1/payment-links/${hashUrl}`, { headers: { 'X-API-Key': apiKey, 'Content-Type': 'application/json' } });
const getResult = await getResponse.json();
if (!getResponse.ok) { throw new Error(getResult.message); }
const paymentLink = getResult.data.paymentLink;
// Check if cancellation is allowed if (paymentLink.status === 'completed') { throw new Error('Cannot cancel completed payment link'); }
if (paymentLink.status === 'cancelled') { console.log('Payment link is already cancelled'); return paymentLink; }
// Proceed with cancellation return await cancelPaymentLink(apiKey, hashUrl); } catch (error) { console.error('Error in safe cancellation:', error); throw error; }}
// Usageconst result = await safeCancelPaymentLink('your-api-key', 'abc123');console.log('Cancellation result:', result);Use Cases
Section titled “Use Cases”1. Order Cancellation
Section titled “1. Order Cancellation”Cancel payment links when orders are cancelled:
async function cancelOrderPaymentLink(apiKey, orderId) { try { // Get the payment link for the order const paymentLinks = await getAllPaymentLinks(apiKey); const orderPaymentLink = paymentLinks.find(link => link.metadata?.orderId === orderId );
if (!orderPaymentLink) { throw new Error('Payment link not found for order'); }
// Cancel the payment link const cancelledLink = await cancelPaymentLink(apiKey, orderPaymentLink.paymentLinkUrl.split('/').pop());
// Update order status await updateOrderStatus(orderId, 'cancelled');
return cancelledLink; } catch (error) { console.error('Error cancelling order payment link:', error); throw error; }}2. Expired Link Cleanup
Section titled “2. Expired Link Cleanup”Cancel expired payment links:
async function cleanupExpiredPaymentLinks(apiKey) { try { const allLinks = await getAllPaymentLinks(apiKey); const expiredLinks = allLinks.filter(link => link.status === 'expired' || new Date(link.expiresAt) < new Date() );
const results = await Promise.allSettled( expiredLinks.map(link => cancelPaymentLink(apiKey, link.paymentLinkUrl.split('/').pop()) ) );
const successful = results.filter(result => result.status === 'fulfilled'); const failed = results.filter(result => result.status === 'rejected');
console.log(`Cancelled ${successful.length} expired payment links`); if (failed.length > 0) { console.log(`Failed to cancel ${failed.length} payment links`); }
return { successful: successful.length, failed: failed.length }; } catch (error) { console.error('Error cleaning up expired payment links:', error); throw error; }}3. Customer Request Cancellation
Section titled “3. Customer Request Cancellation”Handle customer requests to cancel payments:
async function handleCustomerCancellationRequest(apiKey, customerId, paymentLinkId) { try { // Verify the payment link belongs to the customer const paymentLink = await getPaymentLink(apiKey, paymentLinkId);
if (paymentLink.metadata?.customerId !== customerId) { throw new Error('Payment link does not belong to customer'); }
// Check if cancellation is allowed if (paymentLink.status === 'completed') { throw new Error('Cannot cancel completed payment'); }
if (paymentLink.status === 'cancelled') { return { message: 'Payment link is already cancelled', paymentLink }; }
// Cancel the payment link const cancelledLink = await cancelPaymentLink(apiKey, paymentLinkId);
// Send cancellation confirmation await sendCancellationConfirmationEmail(customerId, cancelledLink);
return { message: 'Payment link cancelled successfully', paymentLink: cancelledLink }; } catch (error) { console.error('Error handling customer cancellation:', error); throw error; }}Best Practices
Section titled “Best Practices”1. Check Status Before Cancellation
Section titled “1. Check Status Before Cancellation”Always check the current status before attempting cancellation:
async function cancelPaymentLinkSafely(apiKey, hashUrl) { try { const paymentLink = await getPaymentLink(apiKey, hashUrl);
if (paymentLink.status === 'completed') { throw new Error('Cannot cancel completed payment link'); }
if (paymentLink.status === 'cancelled') { console.log('Payment link is already cancelled'); return paymentLink; }
return await cancelPaymentLink(apiKey, hashUrl); } catch (error) { console.error('Error in safe cancellation:', error); throw error; }}2. Handle Errors Gracefully
Section titled “2. Handle Errors Gracefully”Implement proper error handling for different scenarios:
async function cancelPaymentLinkWithErrorHandling(apiKey, hashUrl) { try { return await cancelPaymentLink(apiKey, hashUrl); } catch (error) { if (error.message.includes('not found')) { return { success: false, error: 'Payment link not found' }; }
if (error.message.includes('invalid state')) { return { success: false, error: 'Payment link cannot be cancelled' }; }
throw error; }}3. Log Cancellation Events
Section titled “3. Log Cancellation Events”Log all cancellation events for audit purposes:
async function cancelPaymentLinkWithLogging(apiKey, hashUrl, reason = 'Manual cancellation') { try { const paymentLink = await cancelPaymentLink(apiKey, hashUrl);
// Log the cancellation event console.log('Payment link cancelled:', { paymentLinkId: paymentLink.id, hashUrl: hashUrl, reason: reason, cancelledAt: paymentLink.cancelledAt, timestamp: new Date().toISOString() });
return paymentLink; } catch (error) { console.error('Failed to cancel payment link:', { hashUrl: hashUrl, reason: reason, error: error.message, timestamp: new Date().toISOString() }); throw error; }}4. Rate Limiting for Batch Operations
Section titled “4. Rate Limiting for Batch Operations”Implement rate limiting for batch cancellation operations:
async function cancelPaymentLinksWithRateLimit(apiKey, hashUrls, delay = 1000) { const results = [];
for (const hashUrl of hashUrls) { try { const paymentLink = await cancelPaymentLink(apiKey, hashUrl); results.push({ hashUrl, success: true, paymentLink }); } catch (error) { results.push({ hashUrl, success: false, error: error.message }); }
// Rate limiting delay await new Promise(resolve => setTimeout(resolve, delay)); }
return results;}Related Endpoints
Section titled “Related Endpoints”- List Payment Links - Retrieve multiple payment links
- Get Payment Link - Retrieve a single payment link
- Create Payment Link - Create a new payment link