Payment Dispute Workflow
Old fragile flow
POST /api/disputes
→ create dispute row
→ notify risk team
→ pull charge from Stripe
→ request evidence from merchant
→ update status in three tables
→ maybe lose track when Stripe webhook arrives lateCauset version
POST /api/disputes → submit open_payment_dispute
Stripe webhook → submit record_dispute_update (idempotent)
Causet → durable dispute timeline
App DB → updated from events / projectionsSample intents
actions:
open_payment_dispute:
state: payment_dispute
entity_id_expr: intent.dispute_id
input:
dispute_id: { type: string, required: true }
charge_id: { type: string, required: true }
reason: { type: string, required: true }
record_dispute_update:
state: payment_dispute
entity_id_expr: intent.dispute_id
input:
dispute_id: { type: string, required: true }
provider_status: { type: string, required: true }
provider_event_id: { type: string, required: true }Sample events
payment_dispute_opened
evidence_requested
evidence_submitted
dispute_won
dispute_lostSample query
queries:
dispute_status:
from: payment_dispute_status
where:
dispute_id: { eq: input.dispute_id }Timeline output
open_payment_dispute (dp_88)
✓ payment_dispute_opened
✓ evidence_requested
record_dispute_update (Stripe webhook)
✓ evidence_submitted
✓ dispute_wonWhat you can inspect
- Correlation between Stripe event IDs and the dispute entity
- Whether evidence was requested before the provider closed the case
- Duplicate webhook deliveries rejected via idempotency key
What you can replay or repair
- Re-process a missed provider update on a fork
- Rebuild
payment_dispute_statusafter a projection bug - Re-notify risk without double-charging state
Related: Webhooks · Billing Events.