โ† Pattern library

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.

commercerefundreturncustomer-servicemoney-back
โœจ Built using these library patterns:
refund

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

    StateDescriptionTransitions
    Eligible to requestOrder within refund window
    • Request submittedโ†’Pending review
    Pending reviewAwaiting auto-decision or human review
    • Auto-approvedโ†’Processing
    • Sent to manual reviewโ†’Manual review
    Manual reviewSupport team evaluating
    • Approvedโ†’Processing
    • Deniedโ†’Denied
    ProcessingProvider executing refund
    • Completedโ†’Refunded
    • Provider errorโ†’Manual review
    RefundedMoney returned to userterminal
    DeniedRefund not granted, user notifiedterminal

    Easy-to-miss situations

    The kinds of edge cases that break demos.

    • What if a user requests refund AFTER receiving physical goods?

      medium

      Need 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)?

      medium

      Some 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?

      medium

      Full 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?

      high

      User 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?

      low

      User 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.

    Build a flow starting from this pattern โ†’