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
- Morning brief action item — "Refund request pending approval: [Church Name] $50 (Reason: Feature missing) · [Approve in Stripe]."
- Action items dashboard —
https://churchwiseai.com/founder/[token]/action-items→type: refund. - Customer email — Email triage agent surfaces it as P1 with link to action item.
- Chatbot escalation — Care agent escalates "I need a refund" as P1 action item.
Click-through flow
Steps
-
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."
-
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.
-
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. -
Approves the refund decision — Full 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).
-
Stripe refund is created — Route
POST /api/founder/action-items/[id]/approve-refundexecutes:stripe refunds create --payment-intent pi_xxxx --api-key $STRIPE_LIVE_SECRET_KEY(or with
--amount 4995for partial). Stripe returns refund object withstatus: succeeded. Route waits for webhook confirmation (charge.refunded) before updating action item withrefund_stripe_idandrefund_status: succeeded. If Stripe returns an error, shows error dialog — never returns 200 silently. -
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."
-
Action item closes — After email sent, action item moves to
completed. Popup: "Refund processed: $49.95 to [email] · Email sent · Action closed."founder_action_itemsupdated: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
- Refund request arrives with all context in under 2 seconds.
- Policy guidance is accurate and current (no stale text).
- Decision made and executed in under 2 minutes for straightforward requests.
- Customer receives a clear, empathetic email.
- Action item closes automatically after refund confirmed.
- 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]?"