# 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)