Appearance
Promotions & Referrals
Phase 2 includes a full promotions engine supporting percentage and fixed-amount discounts, promotional codes, usage limits, validity windows, and a referral program.
Promotions
A Promotion defines a discount rule that can be applied during enrollment or subscription renewal.
| Field | Description |
|---|---|
Name | Display name |
Description | Optional description |
DiscountType | percentage or fixed_amount |
DiscountValue | Amount or percentage to deduct |
SpecificPlanId | Restrict to a single plan (null = all plans) |
ValidFrom / ValidUntil | Optional date window |
MaxTotalUses | Optional global usage cap |
MaxUsesPerUser | Optional per-member cap |
IsActive | Whether the promotion is available |
Discount Types
| Type | Code | Example |
|---|---|---|
| Percentage | percentage | 20% off first month |
| Fixed amount | fixed_amount | €10 off |
Validation Flow
When a promo code or promotion ID is submitted at enrollment:
- The promotion is looked up and validated:
IsActive = true- Within
ValidFrom/ValidUntilwindow CurrentUses < MaxTotalUses(if set)- Member usage
< MaxUsesPerUser(if set) - Plan matches
SpecificPlanId(if set)
- If valid, the discount is applied to the computed price
- A
PromotionUsagerecord is created to track the application
Promotional Codes
Promo codes are standalone codes that map to a Promotion. They allow distributing specific discounts externally (marketing campaigns, events, referrals).
| Method | Path | Permission | Description |
|---|---|---|---|
GET | /api/promo-codes | promo_codes:read | List codes |
POST | /api/promo-codes | promo_codes:write | Create a code |
GET | /api/promo-codes/{id} | promo_codes:read | Get code details |
DELETE | /api/promo-codes/{id} | promo_codes:delete | Deactivate code |
POST | /api/promo-codes/validate | — (public) | Validate a code before use |
Promotions API
| Method | Path | Permission | Description |
|---|---|---|---|
GET | /api/promotions | promotions:read | List promotions |
POST | /api/promotions | promotions:write | Create promotion |
GET | /api/promotions/{id} | promotions:read | Get promotion details |
PUT | /api/promotions/{id} | promotions:write | Update promotion |
DELETE | /api/promotions/{id} | promotions:delete | Deactivate promotion |
Referral Program
Every member receives a unique ReferralCode upon enrollment. When a new member enrolls using an existing member's referral code:
- A
Referralrecord is created linking referrer → referee - An optional reward is applied (configured per referral program settings)
- The referrer is notified
Referral Entities
| Entity | Description |
|---|---|
ReferralCode | Unique code per member (auto-generated at enrollment) |
Referral | Records a successful referral — who referred whom and when |
Referral API
| Method | Path | Permission | Description |
|---|---|---|---|
GET | /api/referrals | referrals:read | List all referrals |
GET | /api/referrals/leaderboard | referrals:read | Top referrers leaderboard |
GET | /api/users/me/referral-code | — (authenticated) | Get own referral code |
GET | /api/users/me/referrals | — (authenticated) | Get own referral history |
Cleanup Automation
CleanupExpiredPromoCodesJob runs nightly to deactivate promotional codes whose ValidUntil date has passed. This keeps the code list clean without requiring manual housekeeping.
Frontend Routes
| Route | View | Permission |
|---|---|---|
/club/promotions | PromotionsView | promotions:read |
/club/promotions/new | PromotionCreateView | promotions:write |
/club/promotions/:id | PromotionDetailView | promotions:read |
/club/referrals | ReferralsView | referrals:read |
/club/referrals/leaderboard | ReferralLeaderboardView | referrals:read |