- 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>
411 lines
15 KiB
Markdown
Executable File
411 lines
15 KiB
Markdown
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_out` with 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
|
||
|
||
```sql
|
||
-- 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`)
|
||
|
||
```ruby
|
||
# 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`)
|
||
1. **Cart Validation**: Parse JSON cart data, validate ticket types and quantities
|
||
2. **Availability Check**: Ensure sufficient tickets available before payment
|
||
3. **Stripe Session**: Create checkout session with line items, success/cancel URLs
|
||
4. **Metadata Storage**: Store order details in Stripe session metadata for later retrieval
|
||
|
||
```ruby
|
||
# 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`)
|
||
1. **Session Retrieval**: Get Stripe session with payment status
|
||
2. **Ticket Creation**: Generate tickets based on order items from metadata
|
||
3. **QR Code Generation**: Automatic unique QR code creation via model callbacks
|
||
4. **Success Page**: Display tickets with download links
|
||
5. **Earnings Creation**: Automatically creates earnings records for promoter payout tracking
|
||
|
||
### 3. PDF Ticket Generation (`app/services/ticket_pdf_generator.rb`)
|
||
|
||
```ruby
|
||
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
|
||
1. User registers with email/password
|
||
2. Completes onboarding process to set up profile
|
||
3. Can browse and purchase tickets as a customer
|
||
|
||
### 2. Promoter Account Setup
|
||
1. User requests professional account status
|
||
2. Connects Stripe account for payment processing
|
||
3. Can create and manage events
|
||
|
||
### 3. Event Creation & Management
|
||
1. Promoter creates event in draft state
|
||
2. Adds ticket types with pricing and quantities
|
||
3. Publishes event to make it publicly available
|
||
4. Manages event status (publish/unpublish/cancel)
|
||
|
||
### 4. Ticket Purchase Flow
|
||
1. User adds tickets to cart
|
||
2. Proceeds to checkout with Stripe
|
||
3. Payment processing through Stripe
|
||
4. Order and ticket creation upon successful payment
|
||
5. Email confirmation sent to user
|
||
6. 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
|
||
1. When order is marked as paid, earnings record is automatically created
|
||
2. Earnings amount = Total ticket sales - Platform fees
|
||
3. Only non-refunded tickets are counted in earnings
|
||
4. Earnings remain in "pending" status until payout is requested
|
||
|
||
#### Payout Request Process
|
||
1. Event ends (current time >= event end_time)
|
||
2. Promoter requests payout through event management interface
|
||
3. System calculates total earnings for the event (excluding refunded tickets)
|
||
4. Creates payout record with gross amount, fees, and net amount
|
||
5. Updates event payout status to "requested"
|
||
6. Admin processes payout through Stripe
|
||
7. Payout status updated to "processing" then "completed" or "failed"
|
||
|
||
### 6. Refund Management
|
||
1. Tickets can be marked as refunded
|
||
2. Refunded tickets are excluded from earnings calculations
|
||
3. Promoters do not receive payouts for refunded tickets
|
||
|
||
## 🔧 Development Patterns
|
||
|
||
### Model Validations
|
||
```ruby
|
||
# 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
|
||
```ruby
|
||
# 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
|
||
```bash
|
||
# 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
|
||
```sql
|
||
-- 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
|
||
1. **User Authentication**: Registration, login, logout flows
|
||
2. **Event Creation**: Validation, state management, relationships
|
||
3. **Booking Process**: Cart validation, payment processing, ticket generation
|
||
4. **PDF Generation**: QR code uniqueness, ticket format
|
||
5. **Dashboard Metrics**: Query accuracy, performance
|
||
6. **Financial Workflows**: Fee calculations, payout processing, refund handling
|
||
|
||
### Seed Data Structure
|
||
```ruby
|
||
# 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:
|
||
|
||
```bash
|
||
# 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-run` first to preview changes
|
||
- Use `--lang ruby` for 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-grep` for 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. |