- Add comprehensive payout styling with custom CSS classes for status indicators - Implement payout index and show views with French translations - Add payout migration with proper indexes and defaults - Update database schema with payout-related tables and fields - Add comprehensive seed data for testing payout functionality - Include payout CSS in application stylesheet - Document payout system implementation in AGENT.md - Add payout feature to BACKLOG.md This completes the full promoter payout system allowing event organizers to request and track revenue payouts for completed events. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
15 KiB
Executable File
15 KiB
Executable File
Aperonight - Technical Documentation for AI Agents
🤖 Agent Implementation Guide
This document provides technical details for AI agents working on the Aperonight ticket selling system.
🏗️ System Architecture
Core Components
1. User Management (app/models/user.rb)
- Devise Integration: Complete authentication system with registration, login, password reset
- Relationships: Users can create events and purchase tickets
- Validations: Email format, password strength, optional name fields
- Promoter System: Professional accounts can create and manage events with Stripe integration
2. Event System (app/models/event.rb)
- States:
draft,published,canceled,sold_outwith enum management - Geographic Data: Latitude/longitude for venue mapping
- Relationships: Belongs to user, has many ticket types and tickets through ticket types
- Scopes: Featured events, published events, upcoming events with proper ordering
- Payout Management: Event-level payout tracking and status management
3. Ticket Management
- TicketType (
app/models/ticket_type.rb): Defines ticket categories with pricing, quantity, sale periods - Ticket (
app/models/ticket.rb): Individual tickets with unique QR codes, status tracking, price storage - Order System (
app/models/order.rb): Groups tickets into orders with payment status tracking
4. Payment Processing (app/controllers/events_controller.rb)
- Stripe Integration: Complete checkout session creation and payment confirmation
- Session Management: Proper handling of payment success/failure with ticket generation
- Security: Authentication required, cart validation, availability checking
5. Financial System
- Earnings (
app/models/earning.rb): Tracks revenue from paid orders, excluding refunded tickets - Payouts (
app/models/payout.rb): Manages promoter payout requests and processing - Platform Fees: €0.50 fixed fee + 1.5% of ticket price, per ticket
Database Schema Key Points
-- Users table (managed by Devise)
CREATE TABLE users (
id bigint PRIMARY KEY,
email varchar(255) UNIQUE NOT NULL,
encrypted_password varchar(255) NOT NULL,
first_name varchar(255),
last_name varchar(255),
is_professionnal boolean DEFAULT false, -- Professional account flag
stripe_connected_account_id varchar(255), -- Stripe Connect account for payouts
-- Devise fields: confirmation, reset tokens, etc.
);
-- Events table
CREATE TABLE events (
id bigint PRIMARY KEY,
user_id bigint REFERENCES users(id),
name varchar(100) NOT NULL,
slug varchar(100) NOT NULL,
description text(1000) NOT NULL,
venue_name varchar(100) NOT NULL,
venue_address varchar(200) NOT NULL,
latitude decimal(10,8) NOT NULL,
longitude decimal(11,8) NOT NULL,
start_time datetime NOT NULL,
end_time datetime,
state integer DEFAULT 0, -- enum: draft=0, published=1, canceled=2, sold_out=3
payout_status integer, -- enum: not_requested=0, requested=1, processing=2, completed=3, failed=4
payout_requested_at datetime,
featured boolean DEFAULT false,
image varchar(500)
);
-- Ticket types define pricing and availability
CREATE TABLE ticket_types (
id bigint PRIMARY KEY,
event_id bigint REFERENCES events(id),
name varchar(255) NOT NULL,
description text,
price_cents integer NOT NULL,
quantity integer NOT NULL,
sale_start_at datetime,
sale_end_at datetime,
requires_id boolean DEFAULT false,
minimum_age integer
);
-- Orders group tickets and track payment status
CREATE TABLE orders (
id bigint PRIMARY KEY,
user_id bigint REFERENCES users(id),
event_id bigint REFERENCES events(id),
status varchar(255) DEFAULT 'draft', -- draft, pending_payment, paid, completed, cancelled, expired
total_amount_cents integer DEFAULT 0,
payment_attempts integer DEFAULT 0,
expires_at datetime,
last_payment_attempt_at datetime
);
-- Individual tickets with QR codes
CREATE TABLE tickets (
id bigint PRIMARY KEY,
order_id bigint REFERENCES orders(id),
ticket_type_id bigint REFERENCES ticket_types(id),
qr_code varchar(255) UNIQUE NOT NULL,
price_cents integer NOT NULL,
status varchar(255) DEFAULT 'active', -- draft, active, used, expired, refunded
first_name varchar(255),
last_name varchar(255)
);
-- Earnings track revenue from paid orders
CREATE TABLE earnings (
id bigint PRIMARY KEY,
event_id bigint REFERENCES events(id),
user_id bigint REFERENCES users(id),
order_id bigint REFERENCES orders(id),
amount_cents integer, -- Promoter payout amount (after fees)
fee_cents integer, -- Platform fees
status integer DEFAULT 0, -- enum: pending=0, paid=1
stripe_payout_id varchar(255)
);
-- Payouts track promoter payout requests
CREATE TABLE payouts (
id bigint PRIMARY KEY,
user_id bigint REFERENCES users(id),
event_id bigint REFERENCES events(id),
amount_cents integer NOT NULL, -- Gross amount
fee_cents integer NOT NULL DEFAULT 0, -- Platform fees
status integer DEFAULT 0, -- enum: pending=0, processing=1, completed=2, failed=3
stripe_payout_id varchar(255),
total_orders_count integer DEFAULT 0,
refunded_orders_count integer DEFAULT 0
);
🎯 Key Implementation Details
1. Dashboard Metrics (app/controllers/pages_controller.rb)
# User-specific metrics with optimized queries
@booked_events = current_user.tickets
.joins(:ticket_type, :event)
.where(events: { state: :published })
.count
# Event counts for different timeframes
@events_today = Event.published
.where("DATE(start_time) = ?", Date.current)
.count
# User's actual booked events (not just count)
@user_booked_events = Event.joins(ticket_types: :tickets)
.where(tickets: { user: current_user, status: 'active' })
.distinct
.limit(5)
2. Stripe Payment Flow
Checkout Initiation (events#checkout)
- Cart Validation: Parse JSON cart data, validate ticket types and quantities
- Availability Check: Ensure sufficient tickets available before payment
- Stripe Session: Create checkout session with line items, success/cancel URLs
- Metadata Storage: Store order details in Stripe session metadata for later retrieval
# Key Stripe configuration
session = Stripe::Checkout::Session.create({
payment_method_types: ['card'],
line_items: line_items,
mode: 'payment',
success_url: payment_success_url(event_id: @event.id, session_id: '{CHECKOUT_SESSION_ID}'),
cancel_url: event_url(@event.slug, @event),
customer_email: current_user.email,
metadata: {
event_id: @event.id,
user_id: current_user.id,
order_items: order_items.to_json
}
})
Payment Confirmation (events#payment_success)
- Session Retrieval: Get Stripe session with payment status
- Ticket Creation: Generate tickets based on order items from metadata
- QR Code Generation: Automatic unique QR code creation via model callbacks
- Success Page: Display tickets with download links
- Earnings Creation: Automatically creates earnings records for promoter payout tracking
3. PDF Ticket Generation (app/services/ticket_pdf_generator.rb)
class TicketPdfGenerator
def generate
Prawn::Document.new(page_size: [350, 600], margin: 20) do |pdf|
# Header with branding
pdf.fill_color "2D1B69"
pdf.font "Helvetica", style: :bold, size: 24
pdf.text "ApéroNight", align: :center
# Event details
pdf.text ticket.event.name, align: :center
# QR Code generation
qr_code_data = {
ticket_id: ticket.id,
qr_code: ticket.qr_code,
event_id: ticket.event.id,
user_id: ticket.user.id
}.to_json
qrcode = RQRCode::QRCode.new(qr_code_data)
pdf.print_qr_code(qrcode, extent: 120, align: :center)
end.render
end
end
4. Frontend Cart Management (app/javascript/controllers/ticket_cart_controller.js)
- Stimulus Controller: Manages cart state and interactions
- Authentication Check: Validates user login before checkout
- Session Storage: Preserves cart when redirecting to login
- Dynamic Updates: Real-time cart total and ticket count updates
🔄 Application Workflows
1. User Registration & Onboarding
- User registers with email/password
- Completes onboarding process to set up profile
- Can browse and purchase tickets as a customer
2. Promoter Account Setup
- User requests professional account status
- Connects Stripe account for payment processing
- Can create and manage events
3. Event Creation & Management
- Promoter creates event in draft state
- Adds ticket types with pricing and quantities
- Publishes event to make it publicly available
- Manages event status (publish/unpublish/cancel)
4. Ticket Purchase Flow
- User adds tickets to cart
- Proceeds to checkout with Stripe
- Payment processing through Stripe
- Order and ticket creation upon successful payment
- Email confirmation sent to user
- Automatic earnings record creation for promoter
5. Financial Workflows
Platform Fee Structure
- Fixed Fee: €0.50 per ticket
- Percentage Fee: 1.5% of ticket price per ticket
- Calculation Example:
- 1 ticket at €20.00: €0.50 + (€20.00 × 1.5%) = €0.50 + €0.30 = €0.80 total fees
- 3 tickets at €25.00 each: (3 × €0.50) + (3 × €25.00 × 1.5%) = €1.50 + €1.13 = €2.63 total fees
Earnings Tracking
- When order is marked as paid, earnings record is automatically created
- Earnings amount = Total ticket sales - Platform fees
- Only non-refunded tickets are counted in earnings
- Earnings remain in "pending" status until payout is requested
Payout Request Process
- Event ends (current time >= event end_time)
- Promoter requests payout through event management interface
- System calculates total earnings for the event (excluding refunded tickets)
- Creates payout record with gross amount, fees, and net amount
- Updates event payout status to "requested"
- Admin processes payout through Stripe
- Payout status updated to "processing" then "completed" or "failed"
6. Refund Management
- Tickets can be marked as refunded
- Refunded tickets are excluded from earnings calculations
- Promoters do not receive payouts for refunded tickets
🔧 Development Patterns
Model Validations
# Event validations
validates :name, presence: true, length: { minimum: 3, maximum: 100 }
validates :latitude, numericality: {
greater_than_or_equal_to: -90,
less_than_or_equal_to: 90
}
# Ticket QR code generation
before_validation :generate_qr_code, on: :create
def generate_qr_code
loop do
self.qr_code = SecureRandom.uuid
break unless Ticket.exists?(qr_code: qr_code)
end
end
Controller Patterns
# Authentication for sensitive actions
before_action :authenticate_user!, only: [:checkout, :payment_success, :download_ticket]
# Strong parameters
private
def event_params
params.require(:event).permit(:name, :description, :venue_name, :venue_address,
:latitude, :longitude, :start_time, :image)
end
View Helpers and Partials
- Metric Cards: Reusable component for dashboard statistics
- Event Items: Consistent event display across pages
- Flash Messages: Centralized notification system
🚀 Deployment Considerations
Environment Variables
# Required for production
STRIPE_PUBLISHABLE_KEY=pk_live_...
STRIPE_SECRET_KEY=sk_live_...
STRIPE_WEBHOOK_SECRET=whsec_...
DATABASE_URL=mysql2://user:pass@host/db
RAILS_MASTER_KEY=...
Database Indexes
-- Performance indexes for common queries
CREATE INDEX idx_events_published_start_time ON events (state, start_time);
CREATE INDEX idx_tickets_user_status ON tickets (user_id, status);
CREATE INDEX idx_ticket_types_event ON ticket_types (event_id);
CREATE INDEX idx_orders_event_status ON orders (event_id, status);
CREATE INDEX idx_earnings_event_status ON earnings (event_id, status);
Security Considerations
- CSRF Protection: Rails default protection enabled
- Strong Parameters: All user inputs filtered
- Authentication: Devise handles session security
- Payment Security: Stripe handles sensitive payment data
- Authorization: Proper access controls for promoter vs customer actions
🧪 Testing Strategy
Key Test Cases
- User Authentication: Registration, login, logout flows
- Event Creation: Validation, state management, relationships
- Booking Process: Cart validation, payment processing, ticket generation
- PDF Generation: QR code uniqueness, ticket format
- Dashboard Metrics: Query accuracy, performance
- Financial Workflows: Fee calculations, payout processing, refund handling
Seed Data Structure
# Creates test users, events, and ticket types
users = User.create!([...])
events = Event.create!([...])
ticket_types = TicketType.create!([...])
🛠️ Available Development Tools
AST-Grep for Mass Code Replacement
The system has ast-grep installed for structural code search and replacement. This tool is particularly useful for:
- Mass refactoring: Rename methods, classes, or variables across the codebase
- Pattern-based replacements: Update code patterns using AST matching
- Language-aware transformations: Safer than regex for code modifications
Usage Examples:
# Find all method calls to a specific method
ast-grep --pattern 'find_by_$FIELD($VALUE)' --lang ruby
# Replace method calls with new syntax
ast-grep --pattern 'find_by_$FIELD($VALUE)' --rewrite 'find_by($FIELD: $VALUE)' --lang ruby
# Search for specific Rails patterns
ast-grep --pattern 'validates :$FIELD, presence: true' --lang ruby
# Mass rename across multiple files
ast-grep --pattern 'old_method_name($$ARGS)' --rewrite 'new_method_name($$ARGS)' --lang ruby --update-all
Best Practices:
- Always run with
--dry-runfirst to preview changes - Use
--lang rubyfor Ruby files to ensure proper AST parsing - Test changes in a branch before applying to main codebase
- Particularly useful for Rails conventions and ActiveRecord pattern updates
📝 Code Style & Conventions
- Ruby Style: Follow Rails conventions and Rubocop rules
- Database: Use Rails migrations for all schema changes
- JavaScript: Stimulus controllers for interactive behavior
- CSS: Tailwind utility classes with custom components
- Documentation: Inline comments for complex business logic
- Mass Changes: Use
ast-grepfor structural code replacements instead of simple find/replace
This architecture provides a solid foundation for a scalable ticket selling platform with proper separation of concerns, security, and user experience.