CancelOrder Method
Overview
The CancelOrder method cancels a previously created order that has not yet been paid to the recipient. Once an order is canceled, it cannot be reversed, and any funds collected from the sender will be refunded according to your refund policy.
Use Cases
- Customer Request: Customer wants to cancel their order
- Duplicate Order: Cancel duplicate orders created by mistake
- Incorrect Information: Cancel and recreate order with correct details
- Fraud Prevention: Cancel suspicious orders
- Compliance Issues: Cancel orders that fail compliance checks
Request Fields
| Field Name | Type (Length) | Option | Description |
|---|---|---|---|
| AgentID | Integer (9) | R | Your agent ID |
| PartnerID | Integer (9) | R | Your partner ID |
| OrderID | Integer (10) | C | Pontual's unique order ID. Required if AgentOrderReference is not provided |
| AgentOrderReference | Alpha (25) | C | Your unique order reference. Required if OrderID is not provided |
Note: You must provide either OrderID or AgentOrderReference.
Legend: R = Required, C = Conditional (one of OrderID or AgentOrderReference must be provided)
Response Fields
Success Response
| Field Name | Type (Length) | Description |
|---|---|---|
| ResultCode | Alpha (10) | "SUCCESS" if cancellation was successful |
| ResultMessage | Alpha (200) | Confirmation message |
| OrderID | Integer (10) | Pontual's order ID |
| StatusCode | Integer (9) | New status (6 = Canceled) |
Error Response
| Field Name | Type (Length) | Description |
|---|---|---|
| ResultCodes | Alpha (200) | Error code(s) |
| ResultMessage | Alpha (200) | Error description |
Cancellation Rules
Orders That CAN Be Canceled
| Status Code | Status | Can Cancel? | Refund Policy |
|---|---|---|---|
| 1 | Incomplete | ✅ Yes | Full refund (no processing yet) |
| 2 | On Hold | ✅ Yes | Full refund minus compliance check fees |
| 3 | Open | ✅ Yes | Full refund minus processing fees |
| 8 | Verifying | ✅ Yes | Full refund (compliance check in progress) |
Orders That CANNOT Be Canceled
| Status Code | Status | Can Cancel? | Reason |
|---|---|---|---|
| 4 | Processing | ❌ No | Payment already sent to recipient |
| 5 | Paid | ❌ No | Recipient already received funds |
| 6 | Canceled | ❌ No | Already canceled |
| 7 | Problem | ⚠️ Contact Support | Requires manual review |
SOAP Request/Response Examples
Example 1: Successful Cancellation
Request:
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<CancelOrder xmlns="http://tempuri.org/">
<AgentID>12345</AgentID>
<PartnerID>67890</PartnerID>
<AgentOrderReference>TXN-2025-001234</AgentOrderReference>
<OrderID></OrderID>
</CancelOrder>
</soap:Body>
</soap:Envelope>
Response:
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<CancelOrderResponse xmlns="http://tempuri.org/">
<CancelOrderResult>
<ResultCode>SUCCESS</ResultCode>
<ResultMessage>Order canceled successfully</ResultMessage>
<OrderID>9876543</OrderID>
<StatusCode>6</StatusCode>
<CancellationTime>2025-11-05T15:30:00</CancellationTime>
</CancelOrderResult>
</CancelOrderResponse>
</soap:Body>
</soap:Envelope>
Example 2: Cannot Cancel - Order Already Processing
Request:
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<CancelOrder xmlns="http://tempuri.org/">
<AgentID>12345</AgentID>
<PartnerID>67890</PartnerID>
<AgentOrderReference></AgentOrderReference>
<OrderID>9876544</OrderID>
</CancelOrder>
</soap:Body>
</soap:Envelope>
Response:
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<CancelOrderResponse xmlns="http://tempuri.org/">
<CancelOrderResult>
<ResultCodes>4001</ResultCodes>
<ResultMessage>Order cannot be canceled - payment is already processing</ResultMessage>
<OrderID>9876544</OrderID>
<StatusCode>4</StatusCode>
</CancelOrderResult>
</CancelOrderResponse>
</soap:Body>
</soap:Envelope>
Example 3: Order Not Found
Request:
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<CancelOrder xmlns="http://tempuri.org/">
<AgentID>12345</AgentID>
<PartnerID>67890</PartnerID>
<AgentOrderReference>INVALID-REF</AgentOrderReference>
<OrderID></OrderID>
</CancelOrder>
</soap:Body>
</soap:Envelope>
Response:
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<CancelOrderResponse xmlns="http://tempuri.org/">
<CancelOrderResult>
<ResultCodes>2001</ResultCodes>
<ResultMessage>Order not found</ResultMessage>
</CancelOrderResult>
</CancelOrderResponse>
</soap:Body>
</soap:Envelope>
Integration Best Practices
1. Check Order Status Before Canceling
// Always check status before attempting cancellation
async function cancelOrderSafely(orderReference) {
try {
// 1. Get current status
const status = await soapClient.GetOrderStatus({
AgentID: config.agentID,
PartnerID: config.partnerID,
AgentOrderReference: orderReference,
OrderID: ''
});
// 2. Check if cancellation is allowed
const cancelableStatuses = [1, 2, 3, 8]; // Incomplete, On Hold, Open, Verifying
if (!cancelableStatuses.includes(status.StatusCode)) {
return {
success: false,
message: `Cannot cancel order with status ${getStatusText(status.StatusCode)}`,
statusCode: status.StatusCode
};
}
// 3. Attempt cancellation
const result = await soapClient.CancelOrder({
AgentID: config.agentID,
PartnerID: config.partnerID,
AgentOrderReference: orderReference,
OrderID: ''
});
return {
success: result.ResultCode === 'SUCCESS',
message: result.ResultMessage,
orderID: result.OrderID
};
} catch (error) {
console.error('Cancellation error:', error);
return {
success: false,
message: error.message
};
}
}
function getStatusText(statusCode) {
const statuses = {
1: 'Incomplete',
2: 'On Hold',
3: 'Open',
4: 'Processing',
5: 'Paid',
6: 'Canceled',
7: 'Problem',
8: 'Verifying'
};
return statuses[statusCode] || 'Unknown';
}
2. Handle Cancellation Workflow
// Complete cancellation workflow with customer notification
async function handleCancellationRequest(orderReference, reason) {
console.log(`Processing cancellation request for ${orderReference}...`);
// 1. Get order details
const orderDetails = await soapClient.GetOrderDetails({
AgentID: config.agentID,
PartnerID: config.partnerID,
AgentOrderReference: orderReference,
OrderID: ''
});
if (!orderDetails) {
throw new Error('Order not found');
}
// 2. Check if cancellation is allowed
const result = await cancelOrderSafely(orderReference);
if (!result.success) {
throw new Error(result.message);
}
// 3. Update local database
await db.orders.update(
{ orderReference: orderReference },
{
status: 'canceled',
canceledAt: new Date(),
cancellationReason: reason
}
);
// 4. Process refund
const refundAmount = await processRefund(orderDetails, reason);
// 5. Notify customer
await notifyCustomer(orderDetails.SenderEmail, {
orderReference: orderReference,
status: 'canceled',
refundAmount: refundAmount,
reason: reason
});
console.log(`Order ${orderReference} canceled successfully`);
return {
success: true,
orderReference: orderReference,
refundAmount: refundAmount
};
}
async function processRefund(orderDetails, reason) {
// Calculate refund based on order status and reason
const totalCollected = parseFloat(orderDetails.TotalCollectedFromSender);
let refundAmount = totalCollected;
// Apply fees based on status
if (orderDetails.StatusCode === 3) {
// Open status - deduct processing fee
refundAmount -= 5.00; // $5 processing fee
} else if (orderDetails.StatusCode === 2) {
// On Hold - deduct compliance check fee
refundAmount -= 10.00; // $10 compliance fee
}
// Process refund via payment gateway
await paymentGateway.refund({
orderReference: orderDetails.AgentOrderReference,
amount: refundAmount,
reason: reason
});
return refundAmount;
}
3. Bulk Cancellation (with Rate Limiting)
// Cancel multiple orders with rate limiting
async function bulkCancelOrders(orderReferences, reason) {
const results = [];
for (const orderRef of orderReferences) {
try {
const result = await handleCancellationRequest(orderRef, reason);
results.push({
orderReference: orderRef,
success: true,
refundAmount: result.refundAmount
});
} catch (error) {
results.push({
orderReference: orderRef,
success: false,
error: error.message
});
}
// Rate limiting - wait 1 second between cancellations
await sleep(1000);
}
// Generate summary report
const summary = {
total: results.length,
successful: results.filter(r => r.success).length,
failed: results.filter(r => !r.success).length,
totalRefunded: results
.filter(r => r.success)
.reduce((sum, r) => sum + r.refundAmount, 0)
};
console.log('Bulk cancellation summary:', summary);
return { results, summary };
}
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
4. Customer-Facing Cancellation UI
// Build customer-friendly cancellation interface
function buildCancellationUI(orderDetails) {
const canCancel = [1, 2, 3, 8].includes(orderDetails.StatusCode);
if (!canCancel) {
return `
<div class="cancellation-not-allowed">
<h3>⚠️ Cancellation Not Available</h3>
<p>This order cannot be canceled because it is currently <strong>${getStatusText(orderDetails.StatusCode)}</strong>.</p>
${orderDetails.StatusCode === 4 ? `
<p>The payment is being processed and will be delivered to the recipient shortly.</p>
` : ''}
${orderDetails.StatusCode === 5 ? `
<p>The payment has already been delivered to the recipient.</p>
<p>If you need assistance, please contact support at remittance@inter.co</p>
` : ''}
</div>
`;
}
// Calculate estimated refund
const totalCollected = parseFloat(orderDetails.TotalCollectedFromSender);
let estimatedRefund = totalCollected;
if (orderDetails.StatusCode === 3) {
estimatedRefund -= 5.00; // Processing fee
} else if (orderDetails.StatusCode === 2) {
estimatedRefund -= 10.00; // Compliance fee
}
return `
<div class="cancellation-form">
<h3>Cancel Order</h3>
<div class="order-summary">
<p><strong>Order Reference:</strong> ${orderDetails.AgentOrderReference}</p>
<p><strong>Amount Sent:</strong> ${orderDetails.NetAmountSent} USD</p>
<p><strong>Total Paid:</strong> ${orderDetails.TotalCollectedFromSender} USD</p>
<p><strong>Recipient:</strong> ${orderDetails.RecipientFirstName} ${orderDetails.RecipientLastName}</p>
<p><strong>Status:</strong> ${getStatusText(orderDetails.StatusCode)}</p>
</div>
<div class="refund-info">
<h4>Refund Information</h4>
<p><strong>Estimated Refund:</strong> ${estimatedRefund.toFixed(2)} USD</p>
${estimatedRefund < totalCollected ? `
<p class="fee-notice">
<small>A processing fee of ${(totalCollected - estimatedRefund).toFixed(2)} will be deducted.</small>
</p>
` : ''}
<p><small>Refund will be processed within 3-5 business days.</small></p>
</div>
<form id="cancellation-form">
<label for="reason">Reason for Cancellation:</label>
<select id="reason" name="reason" required>
<option value="">Select a reason...</option>
<option value="customer_request">Customer request</option>
<option value="duplicate">Duplicate order</option>
<option value="incorrect_info">Incorrect information</option>
<option value="changed_mind">Changed mind</option>
<option value="other">Other</option>
</select>
<label for="comments">Additional Comments (optional):</label>
<textarea id="comments" name="comments" rows="3"></textarea>
<div class="confirmation">
<input type="checkbox" id="confirm" required>
<label for="confirm">
I understand that this cancellation is final and cannot be reversed.
</label>
</div>
<button type="submit" class="btn-cancel">Cancel Order</button>
<button type="button" class="btn-back" onclick="history.back()">Go Back</button>
</form>
</div>
`;
}
5. Automatic Cancellation for Stuck Orders
// Automatically cancel orders stuck in certain statuses
async function autoCancel StuckOrders() {
// Find orders stuck in "Incomplete" for more than 24 hours
const cutoffDate = new Date();
cutoffDate.setHours(cutoffDate.getHours() - 24);
const stuckOrders = await db.orders.find({
status: 'incomplete',
createdAt: { $lt: cutoffDate }
});
console.log(`Found ${stuckOrders.length} stuck orders to cancel`);
for (const order of stuckOrders) {
try {
await handleCancellationRequest(
order.orderReference,
'Automatic cancellation - incomplete order timeout'
);
console.log(`Auto-canceled order: ${order.orderReference}`);
} catch (error) {
console.error(`Failed to auto-cancel ${order.orderReference}:`, error);
}
// Rate limiting
await sleep(2000);
}
}
// Run daily at 2:00 AM
// cron.schedule('0 2 * * *', autoCancelStuckOrders);
Testing
Related Methods
Before calling CancelOrder:
- GetOrderStatus - Check if order can be canceled
- GetOrderDetails - Get full order information
After canceling order:
- GetCancelationsByCanceledDate - Reconcile canceled orders
- CreateNewOrder - Create a new order if needed
Support
- remittance@inter.co
- marcos.lanzoni@inter.co