Stripe does not support negative invoices, so to allow correct invoice generation, we apply dismiss negative invoices.
566 lines
19 KiB
Markdown
Executable File
566 lines
19 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
|
|
- **Professional Users**: `is_professionnal` field for event promoters with enhanced permissions
|
|
- **Onboarding System**: Multi-step onboarding process with `onboarding_completed` tracking
|
|
- **Stripe Integration**: `stripe_customer_id` for accounting and invoice management
|
|
- **Relationships**: Users can create events, purchase tickets, and manage promotion codes
|
|
- **Validations**: Email format, password strength, optional name fields, company information
|
|
|
|
#### 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, tickets through ticket types, and orders
|
|
- **Scopes**: Featured events, published events, upcoming events with proper ordering
|
|
- **Duplication**: Event duplication functionality for similar events
|
|
|
|
#### 3. Order Management (`app/models/order.rb`)
|
|
- **Order States**: `draft`, `pending_payment`, `paid`, `completed`, `cancelled`, `expired`
|
|
- **Payment Processing**: Stripe integration with payment attempt tracking
|
|
- **Platform Fees**: €0.50 fixed + 1.5% per ticket automatic calculation
|
|
- **Expiration**: 15-minute draft order expiration with automatic cleanup
|
|
- **Promotion Integration**: Support for discount code application
|
|
- **Invoice Generation**: Automatic Stripe invoice creation for accounting
|
|
|
|
#### 4. Promotion Code System (`app/models/promotion_code.rb`)
|
|
- **Discount Management**: Fixed amount discounts (stored in cents, displayed in euros)
|
|
- **Usage Controls**: Per-event and per-user association with usage limits
|
|
- **Expiration**: Date-based expiration with active/inactive status management
|
|
- **Validation**: Real-time validation during checkout process
|
|
- **Tracking**: Complete usage tracking and analytics
|
|
|
|
#### 5. 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 Association**: Tickets now belong to orders for better transaction management
|
|
|
|
#### 6. Payment Processing (`app/controllers/orders_controller.rb`)
|
|
- **Order-Based Workflow**: Complete shift from direct ticket purchase to order-based system
|
|
- **Stripe Integration**: Complete checkout session creation and payment confirmation
|
|
- **Session Management**: Proper handling of payment success/failure with order and ticket generation
|
|
- **Security**: Authentication required, cart validation, availability checking
|
|
- **Invoice Service**: Post-payment invoice generation with StripeInvoiceService
|
|
|
|
### Database Schema Key Points
|
|
|
|
```sql
|
|
-- Users table (enhanced with professional features)
|
|
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,
|
|
onboarding_completed boolean DEFAULT false,
|
|
stripe_customer_id varchar(255),
|
|
company_name varchar(255),
|
|
-- Devise fields: confirmation, reset tokens, etc.
|
|
);
|
|
|
|
-- Events table (enhanced with order management)
|
|
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
|
|
featured boolean DEFAULT false,
|
|
image varchar(500)
|
|
);
|
|
|
|
-- Order management system (new core table)
|
|
CREATE TABLE orders (
|
|
id bigint PRIMARY KEY,
|
|
user_id bigint REFERENCES users(id),
|
|
event_id bigint REFERENCES events(id),
|
|
status varchar(255) DEFAULT 'draft',
|
|
total_amount_cents integer DEFAULT 0,
|
|
platform_fee_cents integer DEFAULT 0,
|
|
payment_attempts integer DEFAULT 0,
|
|
expires_at timestamp,
|
|
last_payment_attempt_at timestamp,
|
|
stripe_checkout_session_id varchar(255),
|
|
stripe_invoice_id varchar(255)
|
|
);
|
|
|
|
-- Promotion codes table (new discount system)
|
|
CREATE TABLE promotion_codes (
|
|
id bigint PRIMARY KEY,
|
|
code varchar(255) UNIQUE NOT NULL,
|
|
discount_amount_cents integer DEFAULT 0,
|
|
expires_at datetime,
|
|
active boolean DEFAULT true,
|
|
usage_limit integer,
|
|
uses_count integer DEFAULT 0,
|
|
user_id bigint REFERENCES users(id),
|
|
event_id bigint REFERENCES events(id)
|
|
);
|
|
|
|
-- Order-promotion code join table
|
|
CREATE TABLE order_promotion_codes (
|
|
order_id bigint REFERENCES orders(id),
|
|
promotion_code_id bigint REFERENCES promotion_codes(id)
|
|
);
|
|
|
|
-- 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
|
|
);
|
|
|
|
-- Individual tickets with QR codes (enhanced with order association)
|
|
CREATE TABLE tickets (
|
|
id bigint PRIMARY KEY,
|
|
user_id bigint REFERENCES users(id),
|
|
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' -- active, used, expired, refunded
|
|
);
|
|
```
|
|
|
|
## 🎯 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. Order Management Flow (`app/controllers/orders_controller.rb`)
|
|
|
|
#### Order Creation and Payment
|
|
1. **Cart-to-Order Conversion**: Convert shopping cart to draft order with 15-minute expiration
|
|
2. **Platform Fee Calculation**: Automatic calculation of €0.50 fixed + 1.5% per ticket
|
|
3. **Promotion Code Application**: Real-time discount validation and application
|
|
4. **Stripe Checkout Session**: Create payment session with order metadata
|
|
5. **Payment Retry**: Support for multiple payment attempts with proper tracking
|
|
|
|
```ruby
|
|
# Order creation with platform fees
|
|
def create
|
|
@order = Order.new(order_params)
|
|
@order.user = current_user
|
|
@order.calculate_platform_fee
|
|
@order.set_expiration
|
|
|
|
if @order.save
|
|
session = create_stripe_checkout_session(@order)
|
|
redirect_to session.url, allow_other_host: true
|
|
else
|
|
render :new, status: :unprocessable_entity
|
|
end
|
|
end
|
|
|
|
# Platform fee calculation
|
|
def calculate_platform_fee
|
|
ticket_count = order_items.sum(:quantity)
|
|
self.platform_fee_cents = 50 + (total_amount_cents * 0.015).to_i
|
|
end
|
|
```
|
|
|
|
#### Payment Confirmation and Invoice Generation
|
|
1. **Order Status Update**: Transition from pending_payment to paid
|
|
2. **Ticket Generation**: Create tickets associated with the order
|
|
3. **Stripe Invoice Creation**: Async invoice generation for accounting
|
|
4. **Promotion Code Usage**: Increment usage counters for applied codes
|
|
|
|
### 3. Enhanced Stripe Integration
|
|
|
|
#### StripeInvoiceService (`app/services/stripe_invoice_service.rb`)
|
|
- Post-payment invoice creation with customer management
|
|
- Line item processing with promotion discounts
|
|
- PDF invoice URL generation for download
|
|
- Accounting record synchronization
|
|
|
|
```ruby
|
|
class StripeInvoiceService
|
|
def initialize(order)
|
|
@order = order
|
|
end
|
|
|
|
def create_invoice
|
|
customer = find_or_create_stripe_customer
|
|
invoice_items = create_invoice_items(customer)
|
|
|
|
invoice = Stripe::Invoice.create({
|
|
customer: customer.id,
|
|
auto_advance: true,
|
|
collection_method: 'charge_automatically'
|
|
})
|
|
|
|
@order.update(stripe_invoice_id: invoice.id)
|
|
invoice.finalize_invoice
|
|
end
|
|
end
|
|
```
|
|
|
|
### 4. Promotion Code System (`app/models/promotion_code.rb`)
|
|
|
|
#### Code Validation and Application
|
|
- **Real-time Validation**: Check code validity, expiration, and usage limits
|
|
- **Discount Calculation**: Apply fixed amount discounts to order totals
|
|
- **Usage Tracking**: Increment usage counters and prevent overuse
|
|
- **Event-Specific Codes**: Support for both global and event-specific codes
|
|
|
|
```ruby
|
|
def valid_for_use?(user = nil, event = nil)
|
|
return false unless active?
|
|
return false if expired?
|
|
return false if usage_limit_reached?
|
|
return false if user.present? && !valid_for_user?(user)
|
|
return false if event.present? && !valid_for_event?(event)
|
|
true
|
|
end
|
|
|
|
def apply_discount(total_amount)
|
|
[total_amount - discount_amount_cents, 0].max
|
|
end
|
|
```
|
|
|
|
### 5. Background Job Architecture
|
|
|
|
#### StripeInvoiceGenerationJob
|
|
- Async invoice creation after successful payment
|
|
- Retry logic with exponential backoff
|
|
- Error handling and logging
|
|
|
|
#### ExpiredOrdersCleanupJob
|
|
- Automatic cleanup of expired draft orders
|
|
- Database maintenance and hygiene
|
|
|
|
#### EventReminderJob & EventReminderSchedulerJob
|
|
- Automated event reminder emails
|
|
- Scheduled notifications for upcoming events
|
|
|
|
### 6. 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
|
|
```
|
|
|
|
### 7. Frontend Architecture
|
|
|
|
#### Enhanced Stimulus Controllers
|
|
- **ticket_selection_controller.js**: Advanced cart management with real-time updates
|
|
- **event_form_controller.js**: Dynamic event creation with location services
|
|
- **countdown_controller.js**: Order expiration countdown timers
|
|
- **event_duplication_controller.js**: Event copying functionality
|
|
- **qr_code_controller.js**: QR code display and scanning
|
|
|
|
#### Order-Based Cart Management
|
|
- **Session Storage**: Preserves cart state during authentication flows
|
|
- **Real-time Updates**: Dynamic total calculation with promotion codes
|
|
- **Validation**: Client-side validation with server-side verification
|
|
- **Payment Flow**: Seamless integration with Stripe checkout
|
|
|
|
## 🔧 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
|
|
}
|
|
|
|
# Order validations with state management
|
|
validates :status, presence: true, inclusion: { in: %w[draft pending_payment paid completed cancelled expired] }
|
|
validate :order_not_expired, on: :create
|
|
before_validation :set_expiration, on: :create
|
|
|
|
# Promotion code validations
|
|
validates :code, presence: true, uniqueness: true
|
|
validates :discount_amount_cents, numericality: { greater_than_or_equal_to: 0 }
|
|
validate :expiration_date_cannot_be_in_the_past
|
|
|
|
# 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]
|
|
|
|
# Professional user authorization
|
|
before_action :authenticate_professional!, only: [:create_promotion_code]
|
|
|
|
# Strong parameters with nested attributes
|
|
private
|
|
def order_params
|
|
params.require(:order).permit(:promotion_code, order_items_attributes: [:ticket_type_id, :quantity])
|
|
end
|
|
|
|
# Platform fee calculation
|
|
def calculate_platform_fee
|
|
ticket_count = order_items.sum(:quantity)
|
|
self.platform_fee_cents = 50 + (total_amount_cents * 0.015).to_i
|
|
end
|
|
```
|
|
|
|
### Service Layer Patterns
|
|
```ruby
|
|
# Service for complex business logic
|
|
class StripeInvoiceService
|
|
def initialize(order)
|
|
@order = order
|
|
end
|
|
|
|
def call
|
|
customer = find_or_create_stripe_customer
|
|
create_invoice_items(customer)
|
|
generate_invoice
|
|
end
|
|
|
|
private
|
|
|
|
def find_or_create_stripe_customer
|
|
if @order.user.stripe_customer_id.present?
|
|
Stripe::Customer.retrieve(@order.user.stripe_customer_id)
|
|
else
|
|
customer = Stripe::Customer.create(email: @order.user.email)
|
|
@order.user.update(stripe_customer_id: customer.id)
|
|
customer
|
|
end
|
|
end
|
|
end
|
|
```
|
|
|
|
### View Helpers and Partials
|
|
- **Metric Cards**: Reusable component for dashboard statistics
|
|
- **Event Items**: Consistent event display across pages
|
|
- **Flash Messages**: Centralized notification system
|
|
- **Order Components**: Reusable order display and management components
|
|
|
|
## 🚀 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=...
|
|
|
|
# Rails 8 Solid Stack
|
|
SOLID_QUEUE_IN_PUMA=true
|
|
SOLID_CACHE_URL=redis://localhost:6379/0
|
|
SOLID_CABLE_URL=redis://localhost:6379/1
|
|
|
|
# Application Configuration
|
|
PLATFORM_FEE_FIXED_CENTS=50
|
|
PLATFORM_FEE_PERCENTAGE=1.5
|
|
ORDER_EXPIRATION_MINUTES=15
|
|
```
|
|
|
|
### Database Indexes
|
|
```sql
|
|
-- Performance indexes for common queries
|
|
CREATE INDEX idx_events_published_start_time ON events (state, start_time);
|
|
CREATE INDEX idx_orders_user_status ON orders (user_id, status);
|
|
CREATE INDEX idx_orders_expires_at ON orders (expires_at) WHERE status = 'draft';
|
|
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_promotion_codes_code ON promotion_codes (code);
|
|
CREATE INDEX idx_promotion_codes_active_expires ON promotion_codes (active, expires_at);
|
|
```
|
|
|
|
### 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
|
|
- **Professional User Authorization**: Role-based access control for event promoters
|
|
- **Order Expiration**: Automatic cleanup of abandoned orders
|
|
- **Promotion Code Validation**: Server-side validation with usage limits
|
|
|
|
### Background Jobs
|
|
```ruby
|
|
# Async invoice generation
|
|
StripeInvoiceGenerationJob.perform_later(order_id)
|
|
|
|
# Cleanup expired orders
|
|
ExpiredOrdersCleanupJob.perform_later
|
|
|
|
# Event reminders
|
|
EventReminderSchedulerJob.set(wait_until: event.start_time - 2.hours).perform_later(event_id)
|
|
```
|
|
|
|
## 🌐 API Layer
|
|
|
|
### RESTful Endpoints
|
|
```ruby
|
|
# API Namespacing for external integrations
|
|
namespace :api do
|
|
namespace :v1 do
|
|
resources :events, only: [:index, :show] do
|
|
resources :ticket_types, only: [:index]
|
|
end
|
|
|
|
resources :carts, only: [:create, :show, :update]
|
|
resources :orders, only: [:create, :show, :update]
|
|
|
|
post '/promotion_codes/validate', to: 'promotion_codes#validate'
|
|
end
|
|
end
|
|
```
|
|
|
|
### API Authentication
|
|
- **Token-based authentication**: API tokens for external integrations
|
|
- **Rate limiting**: Request throttling for API endpoints
|
|
- **Versioning**: Versioned API namespace for backward compatibility
|
|
|
|
## 🧪 Testing Strategy
|
|
|
|
### Key Test Cases
|
|
1. **User Authentication**: Registration, login, logout flows
|
|
2. **Professional User Onboarding**: Multi-step onboarding process
|
|
3. **Event Creation**: Validation, state management, relationships
|
|
4. **Order Management**: Cart-to-order conversion, payment processing, expiration
|
|
5. **Promotion Code System**: Code validation, discount application, usage tracking
|
|
6. **PDF Generation**: QR code uniqueness, ticket format
|
|
7. **Stripe Integration**: Payment processing, invoice generation
|
|
8. **Background Jobs**: Async processing, error handling, retry logic
|
|
9. **API Endpoints**: RESTful API functionality and authentication
|
|
10. **Dashboard Metrics**: Query accuracy, performance
|
|
|
|
### Seed Data Structure
|
|
```ruby
|
|
# Creates comprehensive test data
|
|
users = User.create!([...])
|
|
events = Event.create!([...])
|
|
ticket_types = TicketType.create!([...])
|
|
promotion_codes = PromotionCode.create!([...])
|
|
orders = Order.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
|
|
|
|
# Find all order-related validations
|
|
ast-grep --pattern 'validates :status, inclusion: { in: \%w[...] }' --lang ruby
|
|
```
|
|
|
|
#### 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
|
|
|
|
### Modern Rails 8 Stack
|
|
- **Solid Queue**: Background job processing
|
|
- **Solid Cache**: Fast caching layer
|
|
- **Solid Cable**: Action Cable over Redis
|
|
- **Propshaft**: Asset pipeline
|
|
- **Kamal**: Deployment tooling
|
|
- **Thruster**: Performance optimization
|
|
|
|
## 📝 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
|
|
- **Service Layer**: Complex business logic in service objects
|
|
- **Background Jobs**: Async processing for long-running tasks
|
|
- **API Design**: RESTful principles with versioning
|
|
- **Documentation**: Inline comments for complex business logic
|
|
- **Mass Changes**: Use `ast-grep` for structural code replacements instead of simple find/replace
|
|
- **Testing**: Comprehensive test coverage for all business logic
|
|
|
|
This architecture provides a solid foundation for a scalable ticket selling platform with proper separation of concerns, security, and user experience, featuring modern Rails 8 capabilities and a comprehensive order management system. |