v0.18.0
Referral program — buyers bring buyers
- New /dashboard/marketing/referrals — per-merchant referral program. Configure reward type (percent, fixed, shipping-percent, shipping-fixed), separate values for the referrer and the new buyer, min-purchase floor, reward code TTL (30-365 days), attribution window (7-180 days), optional per-referrer lifetime cap, and program terms fine print.
- Auto-issued reward codes reuse the existing DiscountCode machinery — so ledger, P&L, discount-usage reports pick them up with no extra plumbing. Codes are tagged source="referral_referrer" / "referral_referee" and are excluded from the storefront's public applicable-codes list so personal rewards don't leak to other buyers.
- Every signed-in buyer gets a unique 8-char url-safe referral code at their storefront's /s/<slug>/account/referrals page — shareable link, suggested message, one-click copy, native share / WhatsApp / X buttons, live stats (clicks, signups, rewards earned).
- Cookie-based attribution: clicking /s/<slug>/r/<code> drops storlaunch_ref_<accountId> (path-scoped per merchant so cross-merchant visits can't bleed), 302s to the storefront. Deep-linked ?ref= capture works too via a client-side component that strips the param after recording. Attribution commits when a brand-new buyer signs up via OTP under a matching cookie.
- Reward fulfillment fires on payment-completed (both Xendit and PayPal webhook branches), transactional — either both sides get their code or neither does. Idempotent against webhook retries via attribution.status gate and DB-level @unique on CheckoutSession.referralAttributionId.
- Refund clawback: when a qualifying checkout is refunded and neither issued code has been redeemed yet, both codes deactivate. If either was already used, codes stay live and the attribution is marked voided with reason refunded_after_use so reports can surface the recovery leak.
- Guards against the usual abuse: self-referral blocked (same customerId or same email after trim/case-normalize), one attribution per new buyer per merchant, per-referrer reward cap enforced at fulfillment, cookie is per-merchant so a buyer on Shop A can never redeem Shop B's code.
- Free tier is blocked from enabling the program (anti-spam — keeps the referral system from being used as an outreach/affiliate vehicle by unverified accounts). Pro and Business unlock it.
- POST /api/v1/cron/referral-expiry-sweep flips pending attributions past their window to status=expired. Schedule alongside abandoned-cart-sweep.
- CLI 0.15.0: sell referral-program {get, update, links, attributions, stats} — update merges with current state so you only pass the flags you want to change. buy referral {get-link, rewards} — a signed-in buyer can see their link + earned codes from the terminal.