Inter API Documentation

Version 2.0 - Enhanced Partner Integration Guide

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

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:

After canceling order:


Support