Dispute Resolution
Disputes allow buyers or merchants to pause fund release when there's a disagreement about delivery, quality, or other aspects of the transaction.
When to Raise a Dispute
Buyer Disputes
- Item not received: Tracking shows delivered but item not received
- Item damaged: Product arrived damaged or defective
- Not as described: Item doesn't match the description
- Wrong item: Different item received than ordered
Merchant Disputes
- False non-delivery claim: Buyer claims non-delivery but tracking confirms delivery
- Fraudulent claim: Suspected fraudulent dispute
- Delivery proof rejected: Buyer rejects valid delivery proof
Raising a Dispute
await raiseDispute(intentId, {
reason: 'Item not delivered as described. Package was damaged upon arrival.',
raisedBy: buyerAddress,
disputeWindow: '604800', // 7 days
});
Dispute Window
The dispute window determines how long the dispute can remain open:
- 1 day:
86400- Quick resolution for digital goods - 3 days:
259200- Standard for most transactions - 7 days:
604800- Default, good for physical goods - 14 days:
1209600- Complex disputes - 30 days:
2592000- High-value items
Dispute Process
1. Dispute Raised
- Order status changes to
DISPUTED - Fund release is paused
- Dispute window starts counting down
- Timeout job is scheduled
2. Resolution Period
During the dispute window:
- Both parties can provide evidence
- Admin or oracle can review
- Manual resolution possible
- Automatic timeout if not resolved
3. Resolution Outcomes
- MERCHANT_WON: Merchant receives full payment
- BUYER_WON: Buyer receives full refund
- PARTIAL_REFUND: Partial refund to buyer, remainder to merchant
4. Automatic Timeout
If dispute window expires without resolution:
- Default action is taken (configurable)
- Usually favors buyer for consumer protection
- Funds are released or refunded accordingly
Dispute Status
Check dispute status:
const conditionalPayment = await getConditionalPayment(intentId);
if (conditionalPayment.orderStatus === 'DISPUTED') {
console.log('Dispute raised at:', conditionalPayment.disputeRaisedAt);
console.log('Dispute reason:', conditionalPayment.disputeReason);
console.log('Raised by:', conditionalPayment.disputeRaisedBy);
console.log('Dispute window:', conditionalPayment.disputeWindow);
}
Best Practices
For Buyers
- Raise promptly: Don't wait until deadline to dispute
- Provide details: Include photos, tracking info, descriptions
- Be specific: Clear reason helps resolution
- Document everything: Keep receipts, photos, communications
For Merchants
- Respond quickly: Address disputes promptly
- Provide evidence: Shipping confirmations, tracking, delivery proof
- Professional communication: Clear, factual responses
- Prevent disputes: Accurate descriptions, good packaging, reliable shipping
Dispute Reasons
Good reasons:
- ✅ "Item not delivered. Tracking shows delivered but package not received at address."
- ✅ "Item damaged upon arrival. Photos attached showing damage."
- ✅ "Item not as described. Received different model than ordered."
Poor reasons:
- ❌ "Bad" (too vague)
- ❌ "Didn't like it" (not a valid dispute reason)
- ❌ "Changed my mind" (not covered by dispute system)
Code Examples
Raise Dispute
const dispute = await raiseDispute(intentId, {
reason: 'Item not delivered. Tracking shows delivered on Jan 15, but package not received. Checked with neighbors and building management.',
raisedBy: buyerAddress,
disputeWindow: '604800', // 7 days
});
console.log('Dispute raised:', dispute.status);
Check Dispute Status
const conditionalPayment = await getConditionalPayment(intentId);
if (conditionalPayment.orderStatus === 'DISPUTED') {
const disputeInfo = {
raisedAt: new Date(parseInt(conditionalPayment.disputeRaisedAt) * 1000),
reason: conditionalPayment.disputeReason,
raisedBy: conditionalPayment.disputeRaisedBy,
window: parseInt(conditionalPayment.disputeWindow),
};
const timeRemaining = disputeInfo.window -
(Math.floor(Date.now() / 1000) - parseInt(conditionalPayment.disputeRaisedAt));
console.log(`Dispute: ${disputeInfo.reason}`);
console.log(`Time remaining: ${timeRemaining} seconds`);
}
Dispute Resolution Timeline
T+0: Dispute raised
Order status → DISPUTED
Fund release paused
T+1d: Evidence collection period
Both parties provide documentation
T+3d: Review period
Admin/oracle reviews evidence
T+7d: Dispute window expires
Automatic resolution if not resolved
Funds released or refunded
Error Handling
Dispute Already Raised
try {
await raiseDispute(intentId, {...});
} catch (error) {
if (error.code === 'invalid_request') {
console.log('Dispute already raised for this order');
// Check existing dispute
const conditionalPayment = await getConditionalPayment(intentId);
}
}
Invalid Order Status
const conditionalPayment = await getConditionalPayment(intentId);
if (conditionalPayment.orderStatus === 'COMPLETED') {
console.log('Cannot raise dispute: conditional payment already completed');
// Disputes can only be raised before completion
}