400 lines
12 KiB
Markdown
400 lines
12 KiB
Markdown
# Series Ticketing Implementation
|
|
|
|
## Overview
|
|
Comprehensive ticketing system for event series that allows users to purchase a single pass for all events in a series, or optionally register for individual events (drop-in model).
|
|
|
|
## Two Ticketing Models Supported
|
|
|
|
### 1. Series-Level Ticketing (Default)
|
|
**Intent:** Registrants attend all sessions in the series
|
|
|
|
**Use cases:**
|
|
- Workshop series (e.g., 4-week game dev workshop)
|
|
- Courses
|
|
- Multi-day events
|
|
- Any series where commitment to all sessions is expected
|
|
|
|
**How it works:**
|
|
- Purchase ONE series pass that grants access to ALL events
|
|
- Single payment covers the entire series
|
|
- Automatic registration for all events upon purchase
|
|
- User receives single confirmation email with full schedule
|
|
|
|
### 2. Event-Level Ticketing (Drop-in)
|
|
**Intent:** Each event can be attended independently
|
|
|
|
**Use cases:**
|
|
- Recurring meetups (e.g., monthly community calls)
|
|
- Tournaments where sessions are standalone
|
|
- Drop-in events where flexibility is key
|
|
|
|
**How it works:**
|
|
- Each event has its own ticket configuration
|
|
- Users register per event, not for the series
|
|
- Can attend just one, some, or all events
|
|
- Standard event ticketing applies
|
|
|
|
## What Was Built
|
|
|
|
### 1. Data Models
|
|
|
|
#### New Series Model (`server/models/series.js`)
|
|
Dedicated model for managing event series with:
|
|
- Series metadata (title, description, type, dates)
|
|
- Full ticket configuration (member/public pricing, capacity)
|
|
- Circle-specific pricing overrides
|
|
- Early bird pricing support
|
|
- Waitlist functionality
|
|
- Series registrations tracking
|
|
- Event registration references
|
|
|
|
#### Enhanced Event Model
|
|
Added fields to `server/models/event.js`:
|
|
- `tickets.requiresSeriesTicket` - Flag to indicate series pass is required
|
|
- `tickets.seriesTicketReference` - Reference to Series model
|
|
- `registrations.ticketType` - Added "series_pass" option
|
|
- `registrations.isSeriesTicketHolder` - Boolean flag
|
|
- `registrations.seriesTicketId` - Reference to series registration
|
|
|
|
### 2. Backend Logic
|
|
|
|
#### Series Ticket Utilities (`server/utils/tickets.js`)
|
|
Added 8 new functions:
|
|
- `calculateSeriesTicketPrice()` - Pricing with member/circle overrides
|
|
- `checkSeriesTicketAvailability()` - Real-time availability checking
|
|
- `validateSeriesTicketPurchase()` - Pre-purchase validation
|
|
- `reserveSeriesTicket()` - Temporary reservation during checkout
|
|
- `releaseSeriesTicket()` - Release abandoned reservations
|
|
- `completeSeriesTicketPurchase()` - Finalize purchase and update counts
|
|
- `checkUserSeriesPass()` - Verify if user has valid pass
|
|
- `registerForAllSeriesEvents()` - Bulk registration across all events
|
|
|
|
### 3. API Endpoints
|
|
|
|
#### Series Ticket Endpoints
|
|
- `GET /api/series/[id]/tickets/available` - Check pass availability and pricing
|
|
- `POST /api/series/[id]/tickets/purchase` - Complete series pass purchase
|
|
- `POST /api/series/[id]/tickets/check-eligibility` - Verify member status
|
|
- `GET /api/events/[id]/check-series-access` - Verify series pass ownership for event
|
|
|
|
### 4. Frontend Components
|
|
|
|
#### EventSeriesTicketCard.vue
|
|
Beautiful purple-themed card displaying:
|
|
- Series pass name and pricing
|
|
- What's included (all events listed)
|
|
- Member savings comparison
|
|
- Event schedule preview (first 3 events)
|
|
- Availability status
|
|
- Member benefit callouts
|
|
|
|
#### SeriesPassPurchase.vue
|
|
Complete purchase flow component:
|
|
- Loads series pass information
|
|
- Registration form (name/email)
|
|
- Helcim payment integration for paid passes
|
|
- Success/error handling with toast notifications
|
|
- Automatic registration for all events
|
|
- Email confirmation
|
|
|
|
#### Updated EventTicketPurchase.vue
|
|
Enhanced to detect series pass requirements:
|
|
- Checks if event requires series pass
|
|
- Shows "Series Pass Required" message with link to series
|
|
- Displays "Registered via Series Pass" for pass holders
|
|
- Graceful fallback to regular ticketing
|
|
|
|
### 5. Email Templates
|
|
|
|
#### Series Pass Confirmation Email
|
|
Professional HTML email featuring:
|
|
- Purple gradient header
|
|
- Full series pass details
|
|
- Complete event schedule with dates/times
|
|
- Member benefit callout (if applicable)
|
|
- Transaction ID (if paid)
|
|
- "What's Next" guidance
|
|
- Dashboard link
|
|
|
|
### 6. UI Integration
|
|
|
|
#### Updated Series Detail Page (`/series/[id].vue`)
|
|
- New "Get Your Series Pass" section
|
|
- Displays SeriesPassPurchase component
|
|
- Shows only if `series.tickets.enabled` is true
|
|
- Refreshes data after successful purchase
|
|
- User session integration
|
|
|
|
## Configuration Examples
|
|
|
|
### Example 1: Free Member Series, Paid Public
|
|
|
|
```javascript
|
|
const series = {
|
|
id: "coop-game-dev-2025",
|
|
title: "Cooperative Game Development Workshop Series",
|
|
type: "workshop_series",
|
|
tickets: {
|
|
enabled: true,
|
|
requiresSeriesTicket: true, // Must buy series pass
|
|
allowIndividualEventTickets: false,
|
|
currency: "CAD",
|
|
member: {
|
|
available: true,
|
|
isFree: true,
|
|
name: "Member Series Pass",
|
|
description: "Free for Ghost Guild members"
|
|
},
|
|
public: {
|
|
available: true,
|
|
price: 100.00,
|
|
earlyBirdPrice: 80.00,
|
|
earlyBirdDeadline: "2025-11-01T00:00:00Z",
|
|
name: "Public Series Pass",
|
|
description: "Access to all 4 workshops"
|
|
},
|
|
capacity: {
|
|
total: 30 // Total capacity across all ticket types
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
### Example 2: Circle-Specific Pricing
|
|
|
|
```javascript
|
|
const series = {
|
|
tickets: {
|
|
enabled: true,
|
|
member: {
|
|
isFree: false,
|
|
price: 50.00, // Default member price
|
|
circleOverrides: {
|
|
community: {
|
|
isFree: true // Free for community circle
|
|
},
|
|
founder: {
|
|
price: 25.00 // Discounted for founders
|
|
},
|
|
practitioner: {
|
|
price: 10.00 // Heavily discounted
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
### Example 3: Drop-in Series (Individual Event Tickets)
|
|
|
|
```javascript
|
|
const series = {
|
|
id: "monthly-meetup-2025",
|
|
title: "Monthly Community Meetup",
|
|
type: "recurring_meetup",
|
|
tickets: {
|
|
enabled: false, // No series-level tickets
|
|
requiresSeriesTicket: false,
|
|
allowIndividualEventTickets: true
|
|
}
|
|
}
|
|
|
|
// Each event in series has its own tickets config
|
|
const event = {
|
|
series: {
|
|
id: "monthly-meetup-2025",
|
|
isSeriesEvent: true
|
|
},
|
|
tickets: {
|
|
enabled: true,
|
|
member: {
|
|
isFree: true
|
|
},
|
|
public: {
|
|
available: true,
|
|
price: 10.00
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
## User Flows
|
|
|
|
### Flow 1: Member Purchases Free Series Pass
|
|
|
|
1. Member visits series page (`/series/coop-game-dev-2025`)
|
|
2. Sees "Free for Members" series pass card
|
|
3. Fills in name/email (pre-filled from session)
|
|
4. Clicks "Complete Registration"
|
|
5. System creates series registration
|
|
6. System registers user for all 4 events automatically
|
|
7. User receives series pass confirmation email with full schedule
|
|
8. Page refreshes showing "You're Registered!" status
|
|
|
|
### Flow 2: Public User Purchases Paid Series Pass
|
|
|
|
1. Public user visits series page
|
|
2. Sees "$100 Series Pass" (or $80 early bird)
|
|
3. Fills in name/email
|
|
4. Clicks "Pay $100.00"
|
|
5. Helcim payment modal opens
|
|
6. User completes payment
|
|
7. System processes payment and creates registrations
|
|
8. User receives confirmation email with transaction ID
|
|
9. Success toast shows "You're registered for all 4 events"
|
|
|
|
### Flow 3: User with Series Pass Views Individual Event
|
|
|
|
1. User has series pass for "Coop Game Dev Series"
|
|
2. Visits individual event page (`/events/workshop-1`)
|
|
3. EventTicketPurchase component checks series access
|
|
4. Sees "You're Registered!" with message:
|
|
"You have access to this event via your series pass for Cooperative Game Development Workshop Series"
|
|
5. No payment or registration required
|
|
|
|
### Flow 4: User Without Series Pass Views Required Event
|
|
|
|
1. User visits event that requires series pass
|
|
2. EventTicketPurchase component detects requirement
|
|
3. Sees purple banner: "Series Pass Required"
|
|
4. Message explains event is part of series
|
|
5. "View Series & Purchase Pass" button
|
|
6. Clicking redirects to series page to buy pass
|
|
|
|
## Database Schema
|
|
|
|
### Series Document Structure
|
|
|
|
```javascript
|
|
{
|
|
_id: ObjectId,
|
|
id: "coop-game-dev-2025", // String identifier
|
|
slug: "cooperative-game-development-workshop-series",
|
|
title: "Cooperative Game Development Workshop Series",
|
|
description: "Learn to build co-op games...",
|
|
type: "workshop_series",
|
|
isVisible: true,
|
|
isActive: true,
|
|
startDate: ISODate("2025-11-15"),
|
|
endDate: ISODate("2025-12-06"),
|
|
totalEvents: 4,
|
|
tickets: {
|
|
enabled: true,
|
|
requiresSeriesTicket: true,
|
|
currency: "CAD",
|
|
member: { /* ... */ },
|
|
public: { /* ... */ },
|
|
capacity: { total: 30, reserved: 2 }
|
|
},
|
|
registrations: [
|
|
{
|
|
memberId: ObjectId,
|
|
name: "Jane Doe",
|
|
email: "jane@example.com",
|
|
ticketType: "member",
|
|
ticketPrice: 0,
|
|
paymentStatus: "not_required",
|
|
registeredAt: ISODate,
|
|
eventRegistrations: [
|
|
{ eventId: ObjectId, registrationId: ObjectId },
|
|
{ eventId: ObjectId, registrationId: ObjectId }
|
|
]
|
|
}
|
|
],
|
|
targetCircles: ["founder", "practitioner"],
|
|
createdBy: "admin",
|
|
createdAt: ISODate,
|
|
updatedAt: ISODate
|
|
}
|
|
```
|
|
|
|
## Backward Compatibility
|
|
|
|
✅ **Fully backward compatible**
|
|
- Events without series tickets work as before
|
|
- Legacy pricing model still supported
|
|
- Existing registrations unaffected
|
|
- Series can exist without ticket system enabled
|
|
|
|
## Testing Checklist
|
|
|
|
### Series Pass Purchase
|
|
- [ ] Member can see free series pass
|
|
- [ ] Public user sees correct pricing
|
|
- [ ] Early bird pricing displays correctly
|
|
- [ ] Helcim payment modal opens for paid passes
|
|
- [ ] Payment processes successfully
|
|
- [ ] All events get registrations created
|
|
- [ ] Confirmation email sends with all events listed
|
|
- [ ] Capacity decrements correctly
|
|
|
|
### Event Access Control
|
|
- [ ] User with series pass sees "Registered" on event pages
|
|
- [ ] User without pass sees "Series Pass Required" message
|
|
- [ ] Link to series page works correctly
|
|
- [ ] Individual event tickets disabled when series pass required
|
|
|
|
### Edge Cases
|
|
- [ ] Already registered users see correct message
|
|
- [ ] Sold out series shows waitlist option
|
|
- [ ] Cancelled events don't allow registration
|
|
- [ ] Payment failures don't create registrations
|
|
- [ ] Concurrent purchases don't oversell
|
|
|
|
## Future Enhancements
|
|
|
|
### Phase 2 (Planned)
|
|
- Series pass transfers between users
|
|
- Partial series passes ("Any 3 of 5 workshops")
|
|
- Upgrade from individual ticket to series pass
|
|
- Series pass refunds and cancellations
|
|
- Admin dashboard for series pass holders
|
|
- Export attendee lists by series
|
|
|
|
### Phase 3 (Nice to Have)
|
|
- Gift series passes
|
|
- Group/team series passes
|
|
- Installment payment plans
|
|
- Series completion certificates
|
|
- Recurring series subscriptions
|
|
|
|
## Files Created/Modified
|
|
|
|
### New Files (9)
|
|
- `server/models/series.js` - Series data model
|
|
- `server/api/series/[id]/tickets/available.get.js` - Check availability
|
|
- `server/api/series/[id]/tickets/purchase.post.js` - Purchase series pass
|
|
- `server/api/series/[id]/tickets/check-eligibility.post.js` - Member check
|
|
- `server/api/events/[id]/check-series-access.get.js` - Verify pass ownership
|
|
- `app/components/EventSeriesTicketCard.vue` - Series pass display card
|
|
- `app/components/SeriesPassPurchase.vue` - Purchase flow component
|
|
- `SERIES_TICKETING_IMPLEMENTATION.md` - This documentation
|
|
|
|
### Modified Files (4)
|
|
- `server/models/event.js` - Added series ticket fields
|
|
- `server/utils/tickets.js` - Added 8 series ticket functions (~360 lines)
|
|
- `server/utils/resend.js` - Added series pass confirmation email
|
|
- `app/components/EventTicketPurchase.vue` - Series pass detection
|
|
- `app/pages/series/[id].vue` - Integrated purchase UI
|
|
|
|
### Total Implementation
|
|
- **~1,200 lines of new code**
|
|
- **9 new files**
|
|
- **4 modified files**
|
|
- **4 new API endpoints**
|
|
- **2 new Vue components**
|
|
- **8 new utility functions**
|
|
- **1 new data model**
|
|
|
|
## Support
|
|
|
|
For questions or issues:
|
|
- Review this documentation
|
|
- Check existing ticketing docs: `HELCIM_TICKET_INTEGRATION.md`
|
|
- Reference project context: `CLAUDE.md`
|
|
|
|
---
|
|
|
|
**Status**: ✅ Implementation Complete
|
|
**Last Updated**: 2025-10-14
|
|
**Developer**: Claude (Anthropic)
|