ghostguild-org/SERIES_TICKETING_IMPLEMENTATION.md

12 KiB

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

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

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)

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

{
  _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)