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.
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:
- Receipt —
GET /api/sales/{id}/receipt.pdf— shows the line items, totals, taxes, customer (if attached) - Audit trail —
SELECT * 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
transactionsrow ofkind='refund'should be created automatically by our webhook handler (or manually if the webhook missed it)
Common dispute reasons
| Reason | Helm 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