Commerce
Refund Request
Lets the user request a refund (full or partial), evaluates the request against your policy, processes via the payment provider, and updates the order status.
When to use this
Required for any commerce app. The presence of a clear refund flow increases trust and conversion at checkout.
What I assumed
I made these guesses to fill gaps. Let me know if any are wrong.
Flow diagram
Step-by-step recipe
Copy this and paste into Cursor, Claude Code, or v0.
PATTERN: Refund Request
INPUT: order_id, refund_reason, refund_amount (optional, default full)
OUTPUT: refund_completed | refund_denied | manual_review
STEPS:
1. User opens order in their account, clicks "Request refund"
2. Show eligible-for-refund check (within window? not yet shipped?)
3. IF outside policy โ show "This order is no longer eligible โ contact support" (composable_with: human-escalation)
4. Collect refund reason (dropdown + free text)
5. Show confirmation: "Refund $X to your card ending in YYYY (5-10 business days)"
6. User confirms
7. Auto-approve OR queue for manual review based on:
- Auto: small amount, low risk, clear reason
- Manual: high amount, repeated requests, suspicious pattern
8. IF auto โ call provider API (Stripe refund), update order status
9. IF manual โ notify support team, show "Under review, we'll respond within 24h"
10. On provider success webhook โ mark refund complete, decrement revenue
11. Send confirmation email with timeline expectation
12. IF physical goods โ generate return shipping label (separate flow)
ERROR_HANDLING:
- Provider refund fails (charge too old) โ escalate to manual, support contacts user
- Order partially fulfilled โ offer partial refund matching what's not shipped
- Refund amount > original charge โ reject with clear error
- Duplicate refund request โ show "Refund already in progress" with status
EXTENSION_POINTS:
- Subscription cancellation triggers prorated refund (composable_with: ["subscription"])
- Activity feed entry for support visibility (composable_with: ["activity-feed"])
- Auto-block for fraud-pattern detection
- Store credit option as alternative to refund
States โ how things change
| State | Description | Transitions |
|---|---|---|
| Eligible to request | Order within refund window |
|
| Pending review | Awaiting auto-decision or human review |
|
| Manual review | Support team evaluating |
|
| Processing | Provider executing refund |
|
| Refunded | Money returned to user | terminal |
| Denied | Refund not granted, user notified | terminal |
Easy-to-miss situations
The kinds of edge cases that break demos.
What if a user requests refund AFTER receiving physical goods?
mediumNeed to ensure the goods come back before refunding.
Suggested handling: Generate prepaid return label. Refund only after warehouse confirms receipt. Track shipment with tracking number. Show user "Refund will process when we receive the item."
What if a user repeatedly requests refunds (refund abuse)?
mediumSome users buy, use, then refund โ bleeds margin.
Suggested handling: Track refund rate per user. After N refunds in 90 days, route ALL their requests to manual review. Consider account flags for repeat offenders.
What if the refund is for a subscription that was already used part of the period?
mediumFull vs prorated refund decision.
Suggested handling: Default to prorated refund (industry standard). Make policy explicit on cancel page: "Refund $X for unused N days". Allow exceptions for support team.
What if the refund webhook is missed?
highUser charged-back already but our DB still shows revenue.
Suggested handling: Daily reconciliation job: query Stripe refund list, compare with our refund records, flag mismatches. Handle webhooks idempotently so safe to replay.
What if currency exchange rate changed between purchase and refund?
lowUser refunded different amount than charged in their currency.
Suggested handling: Refund original currency amount (Stripe handles this). Provider absorbs FX. Note in receipt that local currency amount may differ slightly.
Composes well with
Combine these patterns when you need a richer flow.