440 lines
14 KiB
Markdown
440 lines
14 KiB
Markdown
# Membership Status Definitions & Transitions
|
|
|
|
This document defines all user membership statuses, their meanings, valid transitions, and automated rules.
|
|
|
|
## Status Overview
|
|
|
|
| Status | Type | Description | Member Access |
|
|
|--------|------|-------------|---------------|
|
|
| `pending_email` | Registration | User registered, awaiting email verification | None |
|
|
| `pending_validation` | Registration | Email verified, awaiting event attendance | Newsletter only |
|
|
| `pre_validated` | Registration | Attended event or referred, ready for admin validation | Newsletter only |
|
|
| `payment_pending` | Registration | Admin validated, awaiting payment | Newsletter only |
|
|
| `active` | Active | Payment completed, full member access | Full access |
|
|
| `inactive` | Inactive | Membership deactivated manually | None |
|
|
| `canceled` | Terminated | User or admin canceled membership | None |
|
|
| `expired` | Terminated | Subscription ended without renewal | Limited (historical) |
|
|
| `abandoned` | Terminated | Incomplete registration after reminders | None |
|
|
|
|
---
|
|
|
|
## Detailed Status Definitions
|
|
|
|
### 1. pending_email
|
|
|
|
**Definition:** User has registered but not verified their email address.
|
|
|
|
**How User Enters:**
|
|
- User completes registration form (Step 1-4)
|
|
- System creates user account with `pending_email` status
|
|
|
|
**Valid Transitions:**
|
|
- → `pending_validation` (email verified)
|
|
- → `pre_validated` (email verified + referred by member)
|
|
- → `abandoned` (optional: 30 days without verification after reminders)
|
|
|
|
**Member Access:**
|
|
- Cannot login
|
|
- Cannot access any member features
|
|
- Not subscribed to newsletter
|
|
|
|
**Reminder Schedule:**
|
|
- Day 3: First reminder email
|
|
- Day 7: Second reminder email
|
|
- Day 14: Third reminder email
|
|
- Day 30: Final reminder (optional: transition to abandoned)
|
|
|
|
**Admin Actions:**
|
|
- Can manually resend verification email
|
|
- Can manually verify email (bypass)
|
|
- Can delete user account
|
|
|
|
---
|
|
|
|
### 2. pending_validation
|
|
|
|
**Definition:** Email verified, user needs to attend an event within 90 days (per LOAF policy).
|
|
|
|
**How User Enters:**
|
|
- Email verification successful (from `pending_email`)
|
|
- 90-day countdown timer starts
|
|
|
|
**Valid Transitions:**
|
|
- → `pre_validated` (attended event marked by admin)
|
|
- → `abandoned` (90 days without event attendance - per policy)
|
|
|
|
**Member Access:**
|
|
- Can login to view dashboard
|
|
- Subscribed to newsletter
|
|
- Cannot access member-only features
|
|
- Can view public events
|
|
|
|
**Reminder Schedule:**
|
|
- Day 30: "You have 60 days remaining to attend an event"
|
|
- Day 60: "You have 30 days remaining to attend an event"
|
|
- Day 80: "Reminder: 10 days left to attend an event"
|
|
- Day 85: "Final reminder: 5 days left"
|
|
- Day 90: Transition to `abandoned`, remove from newsletter
|
|
|
|
**Admin Actions:**
|
|
- Can mark event attendance (triggers transition to `pre_validated`)
|
|
- Can manually transition to `pre_validated` (bypass event requirement)
|
|
- Can extend deadline
|
|
|
|
---
|
|
|
|
### 3. pre_validated
|
|
|
|
**Definition:** User attended event or was referred, awaiting admin validation.
|
|
|
|
**How User Enters:**
|
|
- Admin marked event attendance (from `pending_validation`)
|
|
- User registered with valid member referral (skipped `pending_validation`)
|
|
|
|
**Valid Transitions:**
|
|
- → `payment_pending` (admin validates application)
|
|
- → `inactive` (admin rejects application - rare)
|
|
|
|
**Member Access:**
|
|
- Can login to view dashboard
|
|
- Subscribed to newsletter
|
|
- Cannot access member-only features
|
|
- Can view public events
|
|
|
|
**Automated Rules:**
|
|
- None (requires admin action)
|
|
|
|
**Admin Actions:**
|
|
- Review application in Validation Queue
|
|
- Validate → transition to `payment_pending` (sends payment email)
|
|
- Reject → transition to `inactive` (sends rejection email)
|
|
|
|
---
|
|
|
|
### 4. payment_pending
|
|
|
|
**Definition:** Admin validated application, user needs to complete payment.
|
|
|
|
**How User Enters:**
|
|
- Admin validates application (from `pre_validated`)
|
|
- Payment email sent with Stripe Checkout link
|
|
|
|
**Valid Transitions:**
|
|
- → `active` (payment successful via Stripe webhook)
|
|
- → `abandoned` (optional: 60 days without payment after reminders)
|
|
|
|
**Member Access:**
|
|
- Can login to view dashboard
|
|
- Subscribed to newsletter
|
|
- Cannot access member-only features
|
|
- Can view subscription plans page
|
|
|
|
**Reminder Schedule:**
|
|
- Day 7: First payment reminder
|
|
- Day 14: Second payment reminder
|
|
- Day 21: Third payment reminder
|
|
- Day 30: Fourth payment reminder
|
|
- Day 45: Fifth payment reminder
|
|
- Day 60: Final reminder (optional: transition to abandoned)
|
|
|
|
**Note:** Since admin already validated this user, consider keeping them in this status indefinitely rather than auto-abandoning.
|
|
|
|
**Admin Actions:**
|
|
- Can manually activate membership (for offline payments: cash, check, bank transfer)
|
|
- Can resend payment email
|
|
|
|
---
|
|
|
|
### 5. active
|
|
|
|
**Definition:** Payment completed, full membership access granted.
|
|
|
|
**How User Enters:**
|
|
- Stripe payment successful (from `payment_pending`)
|
|
- Admin manually activated (offline payment)
|
|
|
|
**Valid Transitions:**
|
|
- → `expired` (subscription end date reached without renewal)
|
|
- → `canceled` (user or admin cancels membership)
|
|
- → `inactive` (admin manually deactivates)
|
|
|
|
**Member Access:**
|
|
- Full member dashboard access
|
|
- All member-only features
|
|
- Event RSVP and attendance tracking
|
|
- Member directory listing
|
|
- Newsletter subscribed
|
|
|
|
**Renewal Reminder Schedule:**
|
|
- 60 days before expiration: First renewal reminder
|
|
- 30 days before expiration: Second renewal reminder
|
|
- 14 days before expiration: Third renewal reminder
|
|
- 7 days before expiration: Final renewal reminder
|
|
- On expiration: Transition to `expired`
|
|
|
|
**Admin Actions:**
|
|
- Can cancel membership → `canceled`
|
|
- Can manually deactivate → `inactive`
|
|
- Can extend subscription end_date
|
|
|
|
---
|
|
|
|
### 6. inactive
|
|
|
|
**Definition:** Membership manually deactivated by admin.
|
|
|
|
**How User Enters:**
|
|
- Admin manually sets status to `inactive`
|
|
- Used for temporary suspensions or admin rejections
|
|
|
|
**Valid Transitions:**
|
|
- → `active` (admin reactivates)
|
|
- → `payment_pending` (admin prompts for payment)
|
|
|
|
**Member Access:**
|
|
- Can login but no member features
|
|
- Not subscribed to newsletter
|
|
- Cannot access member-only content
|
|
|
|
**Automated Rules:**
|
|
- None (requires admin action to exit)
|
|
|
|
**Admin Actions:**
|
|
- Reactivate membership → `active`
|
|
- Prompt for payment → `payment_pending`
|
|
- Delete user account
|
|
|
|
---
|
|
|
|
### 7. canceled
|
|
|
|
**Definition:** Membership canceled by user or admin.
|
|
|
|
**How User Enters:**
|
|
- User cancels subscription via Stripe portal
|
|
- Admin cancels membership
|
|
- Stripe webhook: `customer.subscription.deleted`
|
|
|
|
**Valid Transitions:**
|
|
- → `payment_pending` (user requests to rejoin)
|
|
- → `active` (admin reactivates with new subscription)
|
|
|
|
**Member Access:**
|
|
- Can login to view dashboard (historical data)
|
|
- Not subscribed to newsletter
|
|
- Cannot access current member-only features
|
|
- Can view historical event attendance
|
|
|
|
**Automated Rules:**
|
|
- Stripe webhook triggers automatic transition
|
|
|
|
**Admin Actions:**
|
|
- Can invite user to rejoin → `payment_pending`
|
|
- Can manually reactivate → `active` (if subscription still valid)
|
|
|
|
---
|
|
|
|
### 8. expired
|
|
|
|
**Definition:** Subscription ended without renewal.
|
|
|
|
**How User Enters:**
|
|
- Subscription `end_date` reached without renewal
|
|
- Automated check runs daily
|
|
|
|
**Valid Transitions:**
|
|
- → `payment_pending` (user chooses to renew)
|
|
- → `active` (admin manually renews/extends)
|
|
|
|
**Member Access:**
|
|
- Can login to view dashboard (historical data)
|
|
- Not subscribed to newsletter
|
|
- Cannot access current member-only features
|
|
- Can view historical event attendance
|
|
- Shown renewal prompts
|
|
|
|
**Automated Rules:**
|
|
- Daily check for subscriptions past `end_date` → transition to `expired`
|
|
- Send renewal invitation email on transition
|
|
|
|
**Post-Expiration Reminders:**
|
|
- Immediate: Expiration notification + renewal link
|
|
- 7 days after: Renewal reminder
|
|
- 30 days after: Final renewal reminder
|
|
- 90 days after: Optional cleanup/archive
|
|
|
|
**Admin Actions:**
|
|
- Manually extend subscription → `active`
|
|
- Send renewal invitation → `payment_pending`
|
|
|
|
---
|
|
|
|
### 9. abandoned
|
|
|
|
**Definition:** User failed to complete registration process after multiple reminders.
|
|
|
|
**How User Enters:**
|
|
- From `pending_email`: 30 days without verification (optional - after 4 reminders)
|
|
- From `pending_validation`: 90 days without event attendance (after 4 reminders)
|
|
- From `payment_pending`: 60 days without payment (optional - after 6 reminders)
|
|
|
|
**Valid Transitions:**
|
|
- → `pending_email` (admin resets application, resends verification)
|
|
- → `pending_validation` (admin resets, manually verifies email)
|
|
- → `payment_pending` (admin resets, bypasses requirements)
|
|
|
|
**Member Access:**
|
|
- Cannot login
|
|
- Not subscribed to newsletter
|
|
- All access revoked
|
|
|
|
**Automated Rules:**
|
|
- Send "incomplete application" notification email on transition
|
|
- Optional: Purge from database after 180 days (configurable)
|
|
|
|
**Admin Actions:**
|
|
- Can reset application → return to appropriate pending state
|
|
- Can delete user account
|
|
- Can view abandoned applications in admin dashboard
|
|
|
|
---
|
|
|
|
## State Transition Diagram
|
|
|
|
```
|
|
┌──────────────┐
|
|
│ Registration │
|
|
│ (Guest) │
|
|
└──────────────┘
|
|
│
|
|
↓
|
|
┌───────────────┐ (30 days) ┌──────────┐
|
|
│ pending_email │──────────────────→│abandoned │
|
|
└───────────────┘ └──────────┘
|
|
│ ↑
|
|
(verify email) │
|
|
│ │
|
|
↓ │
|
|
┌────────────────────┐ (90 days) │
|
|
│pending_validation │───────────────────┘
|
|
│ (or pre_validated) │
|
|
└────────────────────┘
|
|
│
|
|
(event/admin)
|
|
│
|
|
↓
|
|
┌────────────────┐
|
|
│ pre_validated │
|
|
└────────────────┘
|
|
│
|
|
(admin validates)
|
|
│
|
|
↓
|
|
┌─────────────────┐ (60 days) ┌──────────┐
|
|
│payment_pending │──────────────────→│abandoned │
|
|
└─────────────────┘ └──────────┘
|
|
│
|
|
(payment)
|
|
│
|
|
↓
|
|
┌─────────┐
|
|
│ active │←────────────┐
|
|
└─────────┘ │
|
|
│ │
|
|
├────(expires)────→┌─────────┐
|
|
│ │expired │
|
|
├────(cancels)────→├─────────┤
|
|
│ │canceled │
|
|
└──(deactivate)───→├─────────┤
|
|
│inactive │
|
|
└─────────┘
|
|
│
|
|
(renew/reactivate)
|
|
│
|
|
└──────────┘
|
|
```
|
|
|
|
---
|
|
|
|
## Email Notification Summary
|
|
|
|
| Trigger | Emails Sent |
|
|
|---------|-------------|
|
|
| Registration complete | Verification email (immediate) |
|
|
| pending_email day 3, 7, 14, 30 | Verification reminders |
|
|
| Email verified | Welcome + event attendance instructions |
|
|
| pending_validation day 30, 60, 80, 85 | Event attendance reminders |
|
|
| Admin validates | Payment instructions |
|
|
| payment_pending day 7, 14, 21, 30, 45, 60 | Payment reminders |
|
|
| Payment successful | Membership activation confirmation |
|
|
| active: 60, 30, 14, 7 days before expiry | Renewal reminders |
|
|
| Subscription expires | Expiration notice + renewal link |
|
|
| expired: 7, 30, 90 days after | Post-expiration renewal reminders |
|
|
| Status → abandoned | Incomplete application notice |
|
|
| Admin cancels | Cancellation confirmation |
|
|
|
|
---
|
|
|
|
## Implementation Notes
|
|
|
|
### Configuration Options
|
|
|
|
All timeout periods should be configurable via environment variables:
|
|
|
|
```bash
|
|
# Abandonment timeouts (in days, 0 = never auto-abandon)
|
|
EMAIL_VERIFICATION_TIMEOUT=30
|
|
EVENT_ATTENDANCE_TIMEOUT=90
|
|
PAYMENT_TIMEOUT=0 # Don't auto-abandon payment_pending
|
|
|
|
# Reminder schedules (comma-separated days)
|
|
EMAIL_REMINDERS=3,7,14,30
|
|
EVENT_REMINDERS=30,60,80,85
|
|
PAYMENT_REMINDERS=7,14,21,30,45,60
|
|
RENEWAL_REMINDERS=60,30,14,7
|
|
EXPIRED_REMINDERS=7,30,90
|
|
```
|
|
|
|
### Background Jobs Required
|
|
|
|
1. **Daily Status Check** (runs at 00:00 UTC)
|
|
- Check for expired subscriptions → `expired`
|
|
- Check for abandonment timeouts (if enabled)
|
|
|
|
2. **Hourly Reminder Check** (runs every hour)
|
|
- Calculate days since status change
|
|
- Send appropriate reminder emails based on schedule
|
|
|
|
### Database Indexes
|
|
|
|
```sql
|
|
CREATE INDEX idx_users_status ON users(status);
|
|
CREATE INDEX idx_users_created_at ON users(created_at);
|
|
CREATE INDEX idx_users_updated_at ON users(updated_at);
|
|
CREATE INDEX idx_subscriptions_end_date ON subscriptions(end_date) WHERE status = 'active';
|
|
```
|
|
|
|
---
|
|
|
|
## Testing Checklist
|
|
|
|
- [ ] Reminder emails sent on correct schedule
|
|
- [ ] Abandonment timeouts respect configuration
|
|
- [ ] Manual status transitions work correctly
|
|
- [ ] Role updates on status change
|
|
- [ ] Newsletter subscription/unsubscription on status change
|
|
- [ ] Email notifications use correct templates
|
|
- [ ] Stripe webhook integration for cancellations/expirations
|
|
- [ ] Admin can bypass requirements and manually transition
|
|
- [ ] Users can complete registration even after reminders stop
|
|
|
|
---
|
|
|
|
## Future Enhancements
|
|
|
|
1. **Audit Logging**: Create `user_status_log` table to track all transitions
|
|
2. **Re-engagement Campaigns**: Target abandoned users with special offers
|
|
3. **Flexible Timeout Periods**: Per-user timeout overrides for special cases
|
|
4. **A/B Testing**: Test different reminder schedules for better completion rates
|
|
5. **SMS Reminders**: Optional SMS for critical reminders (payment due, expiration)
|