Skip to main content

Founder approves and processes a refund

Persona

John Moelker reviews refund requests from dissatisfied or cancelling customers. When a church requests a refund (via email, support form, or chatbot escalation), an agent surfaces it as a P1 action item with subscription context and policy guidance. John's job: verify the refund is legitimate, decide amount (full / prorated / deny), execute in Stripe, and communicate to the customer. The system surfaces all context needed — no manual investigation required.

Entry points

  1. Morning brief action item — "Refund request pending approval: [Church Name] $50 (Reason: Feature missing) · [Approve in Stripe]."
  2. Action items dashboardhttps://churchwiseai.com/founder/[token]/action-itemstype: refund.
  3. Customer email — Email triage agent surfaces it as P1 with link to action item.
  4. Chatbot escalation — Care agent escalates "I need a refund" as P1 action item.

Click-through flow

Steps

  1. Receives the refund request — Action item summary card: church name + email, plan ("Starter Voice $49.95/mo"), last charge date, requested amount, reason, policy guidance ("Policy allows full refund within 30 days of first charge for service issues — this customer is on day 5 — full refund justified"), subscription history (first charge ever, no prior refunds, 1 support email). Action buttons: "Approve full refund" · "Approve partial refund (custom amount)" · "Deny refund" · "Defer decision."

  2. Reviews subscription and billing details — Expandable section: Stripe customer ID and subscription ID (links to Stripe), current plan, billing cycle, payment method last 4 digits, prior refunds, charges in past 90 days. Founder confirms the refund request matches the actual charge.

  3. Checks refund policy — Policy summary read from knowledge/data/policies.yaml (fetched fresh — never cached). Example rules: "Starter/Pro: full refund if requested within 30 days of first charge for documented service issue." "Setup fees ($49.95): non-refundable." "Recurring customers >60 days: no retroactive refunds; prorated if cancelling mid-cycle." If request fits a clear rule, the matching button is highlighted.

  4. Approves the refund decisionFull refund: confirmation dialog "Issue full refund of $49.95 to [email]? Appears on card in 5–10 business days." Partial refund: input for custom amount in dollars, then confirm. Deny: moves to Step 5 (draft denial email).

  5. Stripe refund is created — Route POST /api/founder/action-items/[id]/approve-refund executes:

    stripe refunds create --payment-intent pi_xxxx --api-key $STRIPE_LIVE_SECRET_KEY

    (or with --amount 4995 for partial). Stripe returns refund object with status: succeeded. Route waits for webhook confirmation (charge.refunded) before updating action item with refund_stripe_id and refund_status: succeeded. If Stripe returns an error, shows error dialog — never returns 200 silently.

  6. Reviews and sends customer confirmation email — Draft pre-populated: To (customer email), Subject "Your refund has been processed — ChurchWiseAI," Body (brief, empathetic, no policy defense). Denial draft: brief policy explanation and access reminder. Founder reads, optionally personalizes, clicks "Send email."

  7. Action item closes — After email sent, action item moves to completed. Popup: "Refund processed: $49.95 to [email] · Email sent · Action closed." founder_action_items updated: status: completed, refund_stripe_id, completed_at. Customer also receives Stripe's automatic refund confirmation email.

Acceptance spec

No external acceptance spec — internal operational routine. Governed by:

  • Refund policy: knowledge/data/policies.yaml (canonical, fetched fresh on every action-item load)
  • Runbook: knowledge/runbooks/customer-ops/refund.md
  • Database: founder_action_items (type, status, priority, church_id, reason, requested_amount, refund_approved_amount, refund_stripe_id, refund_status)
  • Code files: churchwiseai-web/src/app/founder/[token]/action-items/[id]/page.tsx, churchwiseai-web/src/app/api/founder/action-items/[id]/approve-refund/route.ts

Success criteria

  1. Refund request arrives with all context in under 2 seconds.
  2. Policy guidance is accurate and current (no stale text).
  3. Decision made and executed in under 2 minutes for straightforward requests.
  4. Customer receives a clear, empathetic email.
  5. Action item closes automatically after refund confirmed.
  6. If refund fails, founder sees the error immediately — never a silent failure.

Known failure modes

  • Stale policy text. Policy must be fetched fresh on every load. Log the policy version snapshot in the action item's JSON metadata so the decision is auditable.

  • Stripe refund fails silently. Route must NOT return 200 on error. Show: "Refund failed: [Stripe error message]. Try again?"

  • Duplicate refunds from double-click. Disable button after first click ("Processing..."). Use idempotency key on the API request.

  • Setup fee refunded by accident. Pre-calculate the refundable amount and default to it. If founder overrides (goodwill), show warning: "Policy allows $49.95; you're refunding $99.90. Confirm?"

  • Chargeback filed after refund. If a dispute is already filed, block refund approval: "Customer has a chargeback filed. Refund denied pending dispute resolution."

  • Wrong customer refunded. Before approval, UI shows Stripe customer ID and last 4 of card. Founder must confirm: "Refund $X to [card ending 4242]?"