Feature: Referral Ledger System (FEAT-54)
Metadata
- Issue ID: FEAT-54
- Status: Done
- Owner: beansint
- Related PRs: pending PR from branch
54-referral-code-comission-payout
Overview
FEAT-54 introduces referral attribution and commission-ledger infrastructure integrated into signup, checkout, and Stripe payment-confirmation webhooks. The system is payout-ready but does not execute payouts.
Frontend Behavior
- Signup accepts referral code input and
?ref=prefill. - Pricing checkout accepts referral code input and
?ref=prefill. - Admin console has a referral operations page for settings, code controls, lifecycle run, and commission overrides.
Backend Behavior
- New tables:
referral_settings,referral_codes,referrals,payment_events,commissions,commission_status_events,payout_batches. - Commission lifecycle:
pending -> locked -> ready_for_payout;cancelledon refund/fraud;paidreserved for future payout engine. - Stripe webhook integration now creates payment ledger rows and referral commission rows on successful invoices.
- Admin endpoints under
/admin/referrals/*manage settings/reporting/overrides.
QA Test Scenarios
| Scenario ID | Description | Steps | Input | Expected Result |
|---|---|---|---|---|
| FEAT-54-01 | Signup attribution happy path | Open /signup?ref=<active_code>, complete signup | Valid new user + valid active code | User is created, referral attribution row created with attribution_source=registration, user receives auto referral code |
| FEAT-54-02 | Checkout fallback attribution | Login without existing attribution, submit checkout with referral code | Authenticated free user + valid code | Attribution row created with attribution_source=checkout before checkout URL response |
| FEAT-54-03 | Registration precedence over checkout | Signup with code, then submit checkout with another code | Existing attributed user | Original attribution remains unchanged; checkout does not overwrite referral |
| FEAT-54-04 | Invalid or inactive code rejection | Attempt signup or checkout with malformed/inactive code | referralCode=INVALID@@ or deactivated code | Request rejected with validation/business error; no attribution row |
| FEAT-54-05 | Self-referral block | User attempts to use own referral code | Referred user == referrer user | Request rejected; no referral row created |
| FEAT-54-06 | Duplicate-account heuristic block | Attempt referral where canonicalized emails match | first.last+tag@example.com vs firstlast@example.com | Attribution rejected and security event logged |
| FEAT-54-07 | Commission creation on successful payment | Trigger invoice.payment_succeeded webhook for referred user | Valid Stripe invoice event | payment_events row inserted, commissions row inserted in pending, commission_status_events row written |
| FEAT-54-08 | Webhook idempotency | Replay same Stripe event id | Duplicate event_id | Second processing is skipped; no duplicate payment/commission rows |
| FEAT-54-09 | Pending to locked transition | Run lifecycle transition after fraud window elapses | Commission with status=pending, available_at <= now | Status moves to locked; transition recorded in commission_status_events |
| FEAT-54-10 | Admin sets ready_for_payout | Call admin override endpoint on locked commission | status=ready_for_payout | Commission status updated and transition event recorded with admin actor |
| FEAT-54-11 | Refund cancellation | Emit charge.refunded for charge tied to referral commission | Stripe refund event | Matching eligible commission rows move to cancelled; status event rows added |
| FEAT-54-12 | Fraud dispute cancellation | Emit charge.dispute.created for charge tied to referral commission | Stripe dispute event | Matching eligible commission rows move to cancelled; payment event marked fraud flag |
| FEAT-54-13 | Unauthorized admin access blocked | Hit /admin/referrals/* without valid admin auth | No auth or non-admin role | 401/403 response; no state mutation |
| FEAT-54-14 | Session/auth dependency behavior (FEAT-31) | Expire session then call protected referral endpoints | Expired/invalid auth cookies | Request rejected per FEAT-31 auth/session model |
| FEAT-54-15 | Logout/revocation behavior | Logout then retry referral/admin protected endpoints | Logged-out session | Access denied until re-authentication |
| FEAT-54-16 | Abuse/rate-limit behavior | Repeated invalid referral attempts through auth/checkout routes | Burst invalid requests | Existing auth throttles/abuse controls engage and block progression |
Edge Cases
- Existing attribution is never replaced by checkout input.
- Fingerprint conflicts cancel commission but do not block payment capture.
- Payout threshold is stored, not enforced yet.
Notes
- Canonical feature doc:
docs/features/FEAT-54-referral-ledger-system.md. - Migration:
backend/prisma/migrations/202605120900_add_referral_ledger_system/migration.sql. - FEAT-31 auth/session dependency applies to protected endpoints.