Skip to main content

Disputes & Chargebacks

Comprehensive guide to understanding, managing, and responding to payment disputes and chargebacks to protect your revenue and maintain compliance.

Overviewโ€‹

A dispute (also called a chargeback) occurs when a customer questions a charge with their card issuer instead of contacting you directly. The card issuer withdraws funds from your account and launches an investigation. Understanding the dispute process and responding effectively is critical to recovering revenue and maintaining a low chargeback ratio.

Key Concepts:

  • โœ… Dispute - Customer questions a charge with their bank
  • โœ… Chargeback - Bank reverses the transaction
  • โœ… Evidence - Documentation proving transaction validity
  • โœ… Representment - Your response to dispute with evidence
  • โœ… Chargeback ratio - Disputes / total transactions (keep < 1%)
  • โœ… Inquiry - Pre-dispute customer question (can prevent dispute)

Dispute Lifecycleโ€‹

Common Dispute Reasonsโ€‹

1. Fraudulent (Fraud)โ€‹

Customer claim: "I didn't make this purchase"

Common causes:

  • Card was stolen
  • Account compromised
  • Family member made unauthorized purchase
  • True fraud

Prevention:

  • Enable 3D Secure
  • Verify shipping address
  • Check for suspicious patterns
  • Use fraud protection tools

Evidence needed:

  • Proof of delivery
  • IP address logs
  • Customer communication
  • Device fingerprints

2. Unrecognized (No Authorization)โ€‹

Customer claim: "I don't recognize this charge"

Common causes:

  • Billing descriptor unclear
  • Forgot about purchase
  • Subscription renewal forgotten

Prevention:

  • Use clear billing descriptor
  • Send email confirmations
  • Reminder emails for subscriptions
  • Clear communication

Evidence needed:

  • Purchase confirmation emails
  • Account activity logs
  • Service usage records
  • Customer login history

3. Product Not Received (Non-Receipt)โ€‹

Customer claim: "I never received the product"

Common causes:

  • Delivery failed
  • Wrong address
  • Delayed shipping
  • Customer moved

Prevention:

  • Use tracked shipping
  • Confirm delivery address
  • Send shipping notifications
  • Provide tracking numbers

Evidence needed:

  • Tracking information
  • Delivery confirmation (signature)
  • Shipping labels
  • Communication about delivery

4. Product Unacceptable (Not as Described)โ€‹

Customer claim: "Product is defective/not as described"

Common causes:

  • Product damaged
  • Wrong item sent
  • Misleading description
  • Quality issues

Prevention:

  • Accurate product descriptions
  • Clear photos
  • Quality control
  • Easy return policy

Evidence needed:

  • Product descriptions
  • Photos/videos
  • Return policy
  • Customer communication about issue

5. Duplicate Chargeโ€‹

Customer claim: "I was charged twice"

Common causes:

  • Technical error
  • Customer submitted twice
  • Subscription + one-time charge
  • Authorization + capture confusion

Prevention:

  • Idempotency keys
  • Clear subscription terms
  • Transaction deduplication
  • User-friendly checkout

Evidence needed:

  • Transaction logs
  • Explanation of charges
  • Proof of separate purchases
  • Refund of duplicate (if applicable)

6. Credit Not Processedโ€‹

Customer claim: "I returned item but wasn't refunded"

Common causes:

  • Refund delayed
  • Refund to different card
  • Partial refund confusion
  • Processing time not communicated

Prevention:

  • Process refunds promptly
  • Clear refund timeline
  • Confirmation emails
  • Check refund status

Evidence needed:

  • Refund confirmation
  • Refund timing explanation
  • Return receipt
  • Communication about refund

7. Canceled Serviceโ€‹

Customer claim: "I canceled but was still charged"

Common causes:

  • Cancellation not processed
  • Billing cycle already started
  • Prorated charges
  • Trial period ended

Prevention:

  • Immediate cancellation confirmation
  • Clear cancellation policy
  • Prorated refunds
  • Grace periods

Evidence needed:

  • Cancellation date
  • Billing cycle explanation
  • Terms of service
  • Service usage logs

Dispute Statusesโ€‹

StatusMeaningAction Required
openDispute filed, awaiting evidenceSubmit evidence immediately
pendingEvidence submitted, under reviewWait for decision (45-90 days)
wonYou won the disputeFunds reinstated to account
lostCustomer won the disputeFunds remain withdrawn
charge_refundedYou refunded before rulingNo further action
closedDispute resolved (various reasons)Review outcome

Responding to Disputesโ€‹

Step 1: Review Disputeโ€‹

// Retrieve dispute details via API
const dispute = await omise.disputes.retrieve('dspt_test_...');

console.log('Reason:', dispute.reason_code);
console.log('Amount:', dispute.amount / 100);
console.log('Due date:', dispute.closed_at);
console.log('Transaction:', dispute.charge);

Step 2: Gather Evidenceโ€‹

Universal evidence for all disputes:

  • Customer name and email
  • Billing and shipping address
  • Purchase date and time
  • Transaction ID
  • Product/service description
  • Customer IP address
  • Communication history

Specific evidence by type:

For fraud disputes:

  • Proof of delivery with signature
  • AVS and CVV match results
  • IP address geolocation
  • Device fingerprints
  • Previous purchase history
  • Photos of delivered item (if available)

For non-receipt:

  • Tracking number
  • Delivery confirmation
  • Carrier name
  • Delivery date and time
  • Signed delivery receipt
  • Photos of package at address

For not-as-described:

  • Product photos and descriptions
  • Return policy
  • Communications about product
  • Quality assurance records
  • Proof of resolution attempt

Step 3: Submit Evidenceโ€‹

// Submit evidence via API
const evidence = await omise.disputes.update('dspt_test_...', {
message: 'Customer received product as confirmed by tracking',
metadata: {
tracking_number: '1Z999AA10123456784',
delivery_date: '2025-01-15',
carrier: 'DHL'
}
});

// Upload document
const document = await omise.disputes.upload('dspt_test_...', {
file: fs.readFileSync('/path/to/delivery-proof.pdf'),
description: 'Delivery confirmation with signature'
});

Via Dashboardโ€‹

  1. Log in to Omise Dashboard
  2. Navigate to Disputes
  3. Select the dispute
  4. Click "Submit Evidence"
  5. Fill in evidence form:
    • Write explanation
    • Upload documents (PDFs, images)
    • Provide tracking information
  6. Review and submit

Step 4: Monitor Statusโ€‹

app.post('/webhooks/omise', (req, res) => {
const event = req.body;

switch (event.key) {
case 'dispute.create':
handleNewDispute(event.data);
break;

case 'dispute.update':
handleDisputeUpdate(event.data);
break;

case 'dispute.close':
handleDisputeClosed(event.data);
break;
}

res.sendStatus(200);
});

async function handleDisputeClosed(dispute) {
if (dispute.status === 'won') {
console.log('Won dispute!', dispute.id);
notifyTeam('Dispute won', dispute);
} else if (dispute.status === 'lost') {
console.log('Lost dispute', dispute.id);
analyzeFailureReason(dispute);
}
}

Best Practicesโ€‹

1. Respond Quicklyโ€‹

// Set up alerts
async function handleNewDispute(dispute) {
// Immediately notify team
await sendSlackNotification({
text: `โš ๏ธ New dispute filed! Amount: ${dispute.amount / 100} ${dispute.currency}`,
dispute_id: dispute.id,
reason: dispute.reason_code,
due_date: dispute.closed_at
});

// Create task
await createJiraTicket(dispute);

// Send email to support team
await emailSupportTeam(dispute);
}

Timeline:

  • Submit evidence within 7-10 days for best results
  • Final deadline: Varies by card network (14-21 days)
  • Late submissions: May be rejected automatically

2. Maintain Detailed Recordsโ€‹

// Log everything
async function createCharge(orderData) {
const charge = await omise.charges.create({
amount: orderData.amount,
currency: 'THB',
card: orderData.tokenId,
metadata: {
order_id: orderData.orderId,
customer_email: orderData.email,
customer_phone: orderData.phone,
billing_address: JSON.stringify(orderData.billingAddress),
shipping_address: JSON.stringify(orderData.shippingAddress),
ip_address: orderData.ipAddress,
user_agent: orderData.userAgent,
product_description: orderData.productDescription,
shipping_method: orderData.shippingMethod,
tracking_number: orderData.trackingNumber // Add when available
}
});

// Store in database
await db.transactions.create({
charge_id: charge.id,
customer_id: orderData.customerId,
session_data: orderData.sessionData,
timestamp: new Date()
});

return charge;
}

3. Clear Communicationโ€‹

// Send immediate confirmation
async function sendOrderConfirmation(order) {
await sendEmail({
to: order.customer_email,
subject: `Order Confirmation #${order.id}`,
html: `
<h2>Thank you for your order!</h2>
<p><strong>Order #:</strong> ${order.id}</p>
<p><strong>Total:</strong> $${order.amount / 100}</p>
<p><strong>Billing descriptor:</strong> YourCompany.com</p>

<h3>What's Next?</h3>
<ul>
<li>Processing: 1-2 business days</li>
<li>Shipping: 3-5 business days</li>
<li>Tracking: Will be emailed when shipped</li>
</ul>

<p>Questions? Reply to this email or visit our support page.</p>
<p><strong>Note:</strong> The charge will appear as "YourCompany.com" on your statement.</p>
`
});
}

4. Prevent Disputes Proactivelyโ€‹

// Identify high-risk orders
function calculateDisputeRisk(order) {
let risk = 0;

// High-value order
if (order.amount > 500000) risk += 20;

// First-time customer
if (order.customer.first_order) risk += 15;

// International shipping
if (order.shipping_country !== order.billing_country) risk += 25;

// Different billing/shipping address
if (order.shipping_address !== order.billing_address) risk += 15;

// Express shipping (rush orders often disputed)
if (order.shipping_method === 'express') risk += 10;

return risk;
}

// Take action based on risk
async function processHighRiskOrder(order) {
const risk = calculateDisputeRisk(order);

if (risk > 50) {
// Require additional verification
await requestPhoneVerification(order);
await enable3DSecure(order);
await addManualReview(order);
} else if (risk > 30) {
// Extra tracking
await useSignatureShipping(order);
await sendExtraConfirmations(order);
}
}

5. Accept When Appropriateโ€‹

// Don't fight unwinnable disputes
async function evaluateDispute(dispute) {
const charge = await omise.charges.retrieve(dispute.charge);

// Check if we can win
const canWin = await assessEvidence(dispute, charge);

if (!canWin) {
// Accept the dispute
// Save time and resources for winnable cases
console.log('Insufficient evidence, accepting dispute');

// Analyze to prevent future
await analyzeDisputePattern(dispute);
await updateFraudRules(dispute);

return; // Don't submit evidence
}

// Fight with strong evidence
await submitCompellingEvidence(dispute, charge);
}

6. Monitor Chargeback Ratioโ€‹

// Calculate monthly chargeback ratio
async function calculateChargebackRatio(month) {
const totalTransactions = await db.charges.count({
created: { $gte: month.start, $lt: month.end }
});

const disputes = await db.disputes.count({
created: { $gte: month.start, $lt: month.end }
});

const ratio = (disputes / totalTransactions) * 100;

console.log(`Chargeback ratio: ${ratio.toFixed(2)}%`);

// Alert if exceeding thresholds
if (ratio > 1.0) {
await alertManagement('High chargeback ratio!', ratio);
}

return ratio;
}

Industry thresholds:

  • < 0.65%: Healthy
  • 0.65% - 1.0%: Monitor closely
  • > 1.0%: High risk (card networks may penalize)
  • > 1.5%: Excessive (risk of losing processing)

FAQโ€‹

How long do I have to respond to a dispute?

You typically have 7-21 days depending on the card network. However, respond within 7-10 days for best results. Check the dispute's closed_at date in the dashboard or API.

What happens if I don't respond?

If you don't submit evidence by the deadline, you automatically lose the dispute and the funds remain withdrawn. Always respond, even with limited evidence.

Can I refund to avoid a dispute?

Yes! If you receive a dispute and determine the customer has a valid claim, you can issue a refund. This resolves the dispute and may help maintain goodwill.

How often do merchants win disputes?

Win rates vary widely (10-40%) depending on evidence quality and dispute reason. Fraudulent disputes are hardest to win without strong delivery proof.

Does 3D Secure prevent disputes?

3D Secure shifts liability for fraud disputes to the card issuer, meaning you won't lose funds even if a fraud dispute is filed. However, customers can still file non-fraud disputes.

Learn more about 3D Secure โ†’

What's a good chargeback ratio?
  • Excellent: < 0.5%
  • Good: 0.5% - 0.75%
  • Acceptable: 0.75% - 1.0%
  • High Risk: > 1.0%

Card networks may impose penalties above 1%.

Can customers file disputes after receiving a refund?

Unfortunately, yes. Some customers file disputes without realizing they've been refunded, or file both a dispute and request a refund. Always check for existing disputes before processing refunds.

Next Stepsโ€‹

  1. Enable dispute webhooks
  2. Set up fraud protection
  3. Implement 3D Secure
  4. Create evidence templates
  5. Monitor chargeback ratio
  6. Train support team