Skip to main content

Respond to a Stripe dispute

A customer's bank chargebacks a transaction. Stripe notifies the shop. The shop has a deadline (usually 7-21 days) to respond with evidence. Helm has receipts, audit logs, signatures, and photos — most of what Stripe asks for.

Drafted from planning · v0.1

Step 1: Find the disputed transaction

When the dispute lands in Stripe's dashboard, note the PaymentIntent ID. In Helm:

SELECT * FROM transactions WHERE stripe_pi_id = ?;

This gives you the transaction row, the customer, the date, the lines.

Step 2: Gather Helm's evidence

The shop should attach to Stripe's response:

  • ReceiptGET /api/sales/{id}/receipt.pdf — shows the line items, totals, taxes, customer (if attached)
  • Audit trailSELECT * FROM audit_events WHERE entity_type='transactions' AND entity_id=? — shows who rang the sale and when
  • Customer card — print or screenshot the customer profile; shows the customer was a known repeat customer (if applicable)
  • Service ticket (if it was a service-cashout) — the signed work order with the issue and the labor performed
  • Signature — if Stripe Terminal captured a signature, it's in the transaction's signature_url field; download from R2 and attach
  • Photo evidence — for service tickets, before/after photos demonstrate work was done

Step 3: Compose the response

Stripe's interface asks for free-form text. Use:

Transaction T-N0123 was rung at Swicked Cycles on 2026-04-15 by Robbie. The customer, Jane Doe (phone 250-555-1234), purchased a service-tune-up ticket on her Specialized Sirrus 4.0 (serial XYZ123). She signed the work authorization (attached). The bike was returned to her on 2026-04-17 with the work complete (before/after photos attached). She has been a regular customer for 4 years (8 prior transactions, none disputed).

Attached:

  • Receipt T-N0123.pdf
  • Work authorization signature
  • Service ticket #2506 with labor lines
  • Before/after photos
  • Audit trail showing the transaction lifecycle

Step 4: Submit through Stripe dashboard

The shop owner submits through Stripe's dashboard with the evidence attached. Stripe weighs the evidence; outcome varies.

Step 5: Record the outcome

When Stripe issues the verdict (won/lost):

  • Won: parent transaction stays paid; no further action
  • Lost: Stripe debits the funds; a transactions row of kind='refund' should be created automatically by our webhook handler (or manually if the webhook missed it)

Common dispute reasons

ReasonHelm evidence
"Product not received"Service ticket pickup record, signature, receipt
"Unauthorized charge"Customer profile (returning customer); signature at terminal; cashier identity
"Duplicate charge"Audit trail shows only one transaction; idempotency key
"Wrong amount"Receipt with line items and totals
"Not as described"Service ticket with detailed work; photos before/after

Prevention

  • Always attach the customer to in-shop transactions (when possible) — turns "unauthorized charge" disputes into "we know this customer" evidence
  • Use Stripe Terminal's signature capture for service tickets > $100
  • Photograph before/after for service work
  • Keep the audit trail clean — every transaction has a who/when/what

See also