Two dispute scopes, one workspace
Disputes are stored in payment_disputes with a scope of either:
- gym_member — a member charged back a gym. The gym works it in their own dispute workspace; platform staff assist.
- platform_subscription — a gym charged back their Bliply subscription. We work it.
Platform staff use Disputes → Subscription Disputes (/platform/disputes, Admin+). The workspace is the same component the gym sees, opened with the platform scope, so anything you learn here applies to helping a gym with theirs.
Dispute state
State is derived, not stored as a free-text status (src/lib/disputeState.ts):
| State | Meaning |
|---|---|
| Needs response | Not closed, evidence not submitted, resubmission cap not reached, deadline not passed. This is the only actionable state. |
| Under review | Evidence submitted, or the cap is reached, or the deadline has passed. Stripe and the card issuer now decide. |
| Closed | Won or lost. Acknowledge it to clear it from the queue. |
The evidence workflow
- Prefill — the
prefill-dispute-evidencefunction pulls what the account already holds (contract, payment history, service records) into the evidence form. - Upload — supporting files go to the private
dispute-evidencebucket and are listed inpayment_dispute_evidence_files. - Readiness gate — submission is blocked until five things exist: a signed contract, a payment-history PDF, a service-summary PDF, a product description and a service explanation.
- Submit —
submit-dispute-evidencesends it to Stripe. Each submission incrementsresubmission_count; oncesubmission_cap_reachedis true no further evidence can be sent. - Auto-submit — by default a dispute with evidence in place is submitted automatically the morning of its deadline by the daily 09:00 job. Turn this off per dispute with the auto-submit toggle only when you intend to keep editing right up to the deadline.
Money movement
Stripe withdraws the disputed amount when the dispute opens and reinstates it if it is won. Those movements are recorded on the dispute (funds_withdrawn_at, funds_reinstated_at) and itemised in payment_dispute_ledger_entries, shown as the financial ledger in the workspace. Use it when a gym asks why their payout is short.
Early warnings
payment_dispute_early_warnings captures Stripe inquiries and fraud alerts that have not yet become disputes. Dismiss one only once it has been acted on — dismissal is recorded.
Scheduled jobs
reconcile-stripe-disputes— hourly, 7-day lookback, pulls dispute state from Stripe. Also runnable on demand from the list page when something looks stale.auto-submit-disputes— daily at 09:00.
Watch out for
- Submitting early burns a resubmission. If more evidence is coming, wait — auto-submit protects the deadline.
- A dispute that disappears from the queue has usually been acknowledged, not resolved. Filter by closed to find it.
- Winning a dispute does not refund the Stripe dispute fee.