Handle Stripe chargebacks (disputes) by responding promptly through Dashboard → Disputes with compelling evidence. Each dispute carries a $15 fee (returned if you win). The dispute lifecycle is: charge.dispute.created → submit evidence within 7-21 days → bank decides. Prevent chargebacks by using clear statement descriptors, sending receipts, and issuing proactive refunds.
Understanding Chargebacks and Disputes in Stripe
A chargeback (Stripe calls it a dispute) happens when a customer contests a charge with their bank. The bank pulls the funds from your account, charges a $15 dispute fee, and gives you a window to submit evidence proving the charge was legitimate. If you win, the funds and fee are returned. If you lose (or do not respond), the customer keeps the money. High dispute rates above 0.75% can trigger card network monitoring programs and potentially account closure.
Prerequisites
- A Stripe account (disputes can be simulated in test mode)
- Understanding of your product delivery and refund policies
- Node.js 18 or newer installed (for webhook handling)
- Your Stripe secret key and webhook signing secret
Step-by-step guide
Understand the dispute lifecycle
Understand the dispute lifecycle
When a customer disputes a charge: (1) The bank notifies Stripe, (2) Stripe deducts the disputed amount + $15 fee from your balance, (3) Stripe creates a Dispute object and sends you an email, (4) You have 7-21 days (depending on card network) to submit evidence, (5) The bank reviews evidence and makes a final decision.
Expected result: You understand the timeline and can plan your response accordingly.
View and respond to disputes in the Dashboard
View and respond to disputes in the Dashboard
Go to Payments → Disputes in the Stripe Dashboard. Click the dispute to see details including the reason code, disputed amount, and evidence deadline. Click 'Submit evidence' to upload your supporting documents.
Expected result: You submit evidence before the deadline. The dispute status changes to 'under_review'.
Submit evidence via the API
Submit evidence via the API
For automated dispute handling, use the API to submit evidence programmatically. This is useful when you can automatically gather order details and delivery proof.
1const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);23await stripe.disputes.update('dp_ABC123', {4 evidence: {5 product_description: 'Annual SaaS subscription for project management tool',6 customer_email_address: 'customer@example.com',7 customer_name: 'Jane Smith',8 billing_address: '123 Main St, San Francisco, CA 94105',9 receipt: 'file_RECEIPT123', // uploaded file ID10 service_date: '2025-01-15',11 access_activity_log: 'Customer logged in 47 times between Jan 15 - Feb 15, 2025. Last login: Feb 14 at 3:42 PM UTC.',12 cancellation_policy: 'Full refund within 30 days. Customer disputed after 45 days.',13 refund_policy: 'file_POLICY456',14 },15 submit: true, // Set to true to submit; false to save as draft16});Expected result: Evidence is submitted to the bank for review. The dispute status changes to 'under_review'.
Handle dispute webhooks
Handle dispute webhooks
Set up webhook listeners for dispute events to respond programmatically and alert your team immediately when a dispute is created.
1app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {2 const sig = req.headers['stripe-signature'];3 let event;45 try {6 event = stripe.webhooks.constructEvent(7 req.body,8 sig,9 process.env.STRIPE_WEBHOOK_SECRET10 );11 } catch (err) {12 return res.status(400).send(`Webhook Error: ${err.message}`);13 }1415 switch (event.type) {16 case 'charge.dispute.created':17 const dispute = event.data.object;18 console.log(`New dispute: ${dispute.id}, Amount: $${dispute.amount / 100}`);19 console.log(`Reason: ${dispute.reason}, Deadline: ${dispute.evidence_details.due_by}`);20 // Alert your team, gather evidence automatically21 break;22 case 'charge.dispute.closed':23 const closedDispute = event.data.object;24 console.log(`Dispute ${closedDispute.id} closed: ${closedDispute.status}`);25 // Status: won, lost, or warning_closed26 break;27 }2829 res.json({ received: true });30});Expected result: Your server receives real-time notifications when disputes are created and resolved.
Test disputes in test mode
Test disputes in test mode
Simulate a dispute in test mode by creating a charge with a special test token, then triggering the dispute via the Stripe CLI or Dashboard.
1// Create a charge that will be disputed:2const paymentIntent = await stripe.paymentIntents.create({3 amount: 10000, // $100.004 currency: 'usd',5 payment_method: 'pm_card_createDispute', // triggers dispute6 confirm: true,7 automatic_payment_methods: {8 enabled: true,9 allow_redirects: 'never',10 },11});1213// A dispute will be created automatically on this chargeExpected result: A dispute is created on the test charge, simulating the full dispute lifecycle.
Complete working example
1const express = require('express');2const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);34const app = express();56// Webhook handler7app.post('/webhook',8 express.raw({ type: 'application/json' }),9 async (req, res) => {10 const sig = req.headers['stripe-signature'];11 let event;1213 try {14 event = stripe.webhooks.constructEvent(15 req.body,16 sig,17 process.env.STRIPE_WEBHOOK_SECRET18 );19 } catch (err) {20 return res.status(400).send(`Webhook Error: ${err.message}`);21 }2223 switch (event.type) {24 case 'charge.dispute.created': {25 const dispute = event.data.object;26 console.log(`Dispute created: ${dispute.id}`);27 console.log(`Amount: $${dispute.amount / 100}`);28 console.log(`Reason: ${dispute.reason}`);29 const deadline = new Date(dispute.evidence_details.due_by * 1000);30 console.log(`Evidence due by: ${deadline.toISOString()}`);31 // TODO: alert team, auto-gather evidence32 break;33 }34 case 'charge.dispute.closed': {35 const result = event.data.object;36 console.log(`Dispute ${result.id} closed: ${result.status}`);37 if (result.status === 'won') {38 console.log('Funds and $15 fee returned.');39 } else if (result.status === 'lost') {40 console.log('Funds lost. Consider process improvements.');41 }42 break;43 }44 }4546 res.json({ received: true });47 }48);4950app.use(express.json());5152// Submit evidence for a dispute53app.post('/api/disputes/:disputeId/evidence', async (req, res) => {54 try {55 const { disputeId } = req.params;56 const { evidence, submit } = req.body;5758 const dispute = await stripe.disputes.update(disputeId, {59 evidence,60 submit: submit || false,61 });6263 res.json({64 id: dispute.id,65 status: dispute.status,66 evidenceSubmitted: submit,67 });68 } catch (err) {69 res.status(500).json({ error: err.message });70 }71});7273const PORT = process.env.PORT || 3000;74app.listen(PORT, () => console.log(`Server on port ${PORT}`));Common mistakes when handling chargebacks in Stripe
Why it's a problem: Not responding to disputes before the deadline
How to avoid: You automatically lose if you miss the evidence deadline. Set up charge.dispute.created webhooks and alert your team immediately.
Why it's a problem: Submitting weak or generic evidence
How to avoid: Include specific evidence: delivery tracking, access logs, signed agreements, customer communication, and your refund policy. Generic statements rarely win.
Why it's a problem: Refunding a charge after a dispute is filed
How to avoid: Once a dispute is created, do not issue a refund — it complicates the dispute process. Accept the dispute instead if you agree the customer should get their money back.
Why it's a problem: Ignoring dispute rate monitoring
How to avoid: Card networks flag accounts with dispute rates above 0.75%. Monitor your rate in Dashboard → Analytics and address root causes proactively.
Best practices
- Respond to every dispute — even if you expect to lose, responding demonstrates good faith
- Set up charge.dispute.created webhooks to get immediate notification
- Use clear statement descriptors so customers recognize charges on their bank statements
- Send email receipts for every payment to create a paper trail
- Issue proactive refunds for unhappy customers before they dispute — a refund is cheaper than a dispute
- Keep detailed records of orders, deliveries, and customer communications
- Monitor your dispute rate and investigate recurring dispute reasons
- Use Stripe Radar to block suspicious payments before they result in disputes
Still stuck?
Copy one of these prompts to get a personalized, step-by-step explanation.
Write a Node.js webhook handler for Stripe dispute events. Handle charge.dispute.created to log dispute details and deadline, and charge.dispute.closed to log the outcome. Use stripe.webhooks.constructEvent with raw body for signature verification.
Add chargeback handling to my app. Set up a webhook to receive dispute notifications, auto-gather order evidence, and create an endpoint to submit dispute evidence via the Stripe API. Include deadline tracking.
Frequently asked questions
How much does a Stripe dispute cost?
Each dispute carries a $15 non-refundable fee. If you win the dispute, the $15 fee is returned along with the disputed amount. If you lose, you forfeit both.
What is the average dispute win rate?
Across the industry, merchants win about 20-30% of disputes. With strong evidence (delivery confirmation, access logs, signed agreements), win rates can reach 50%+.
Can I prevent disputes entirely?
No, but you can significantly reduce them. Use clear statement descriptors, send receipts, offer easy refunds, respond to customer complaints quickly, and use Stripe Radar for fraud prevention.
What happens if my dispute rate is too high?
Card networks (Visa, Mastercard) place merchants with dispute rates above 0.75% into monitoring programs. Continued high rates can result in fines, increased processing fees, or account termination.
Can I appeal a lost dispute?
No. Dispute decisions are final. Once the issuing bank makes a decision, it cannot be reversed through Stripe. Some card networks allow a second representment in rare cases.
What if I need help building a dispute management and prevention system?
For businesses with high transaction volumes that need automated evidence gathering, dispute analytics, and prevention strategies, the RapidDev team can help build a comprehensive dispute management system.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation