Area: Monetization (audit p10) · Surface: /api/stripe-webhook/course-sale/{slug}, course purchase lifecycle · Dimension: feature-improvement · Severity: minor
Teachable and Thinkific (and Stripe's own abandoned-cart recovery) email buyers who start but don't finish a course checkout, recovering a measurable slice of lost sales. We already capture buyer_email at session-create time (CourseCatalogController.php:132-138), so the data is in hand — but an expired session just becomes a dead 'failed' row with no nudge. This leaves money on the table on the highest-intent users.
Evidence
CourseCatalogController::handleCheckoutExpired (CourseCatalogController.php:482-493) on checkout.session.expired just flips the purchase to status='failed' with failed_reason='checkout_expired' — no follow-up email, no recovery link. grep for abandon|cart_recovery|recovery_email|remind.*checkout across src/ finds no recovery path for purchases.
Suggested fix. On checkout.session.expired, queue a recovery email to buyer_email with a one-click link back to /learn/enrol/{slug}; cap to one reminder, respect notification prefs.
Filed by the automated tenant-app audit and adversarially evidence-verified. Status: verified. Open — not yet actioned.
Patrick Bass
@mobieus