feat/free-ticket #2

Merged
kbe merged 9 commits from feat/free-ticket into develop 2025-09-16 14:31:44 +00:00
9 changed files with 205 additions and 108 deletions
Showing only changes of commit 049e5505ef - Show all commits

View File

@@ -293,18 +293,7 @@ class OrdersController < ApplicationController
} }
end end
# Add service fee as a separate line item # No service fee added to customer; deducted from promoter payout
line_items << {
price_data: {
currency: "eur",
product_data: {
name: "Frais de service",
description: "Frais de traitement de la commande"
},
unit_amount: 100 # 1€ in cents
},
quantity: 1
}
Stripe::Checkout::Session.create( Stripe::Checkout::Session.create(
payment_method_types: [ "card" ], payment_method_types: [ "card" ],

View File

@@ -88,11 +88,32 @@ class Order < ApplicationRecord
end end
end end
# Calculate total from tickets plus 1€ service fee # Calculate total from ticket prices only (platform fee deducted from promoter payout)
def calculate_total! def calculate_total!
ticket_total = tickets.sum(:price_cents) ticket_total = tickets.sum(:price_cents)
fee_cents = 100 # 1€ in cents update!(total_amount_cents: ticket_total)
update!(total_amount_cents: ticket_total + fee_cents) end
# Platform fee: €0.50 fixed + 1.5% of ticket price, per ticket
def platform_fee_cents
tickets.sum do |ticket|
fixed_fee = 50 # €0.50 in cents
percentage_fee = (ticket.price_cents * 0.015).to_i
fixed_fee + percentage_fee
end
end
# Promoter payout amount after platform fee deduction
def promoter_payout_cents
total_amount_cents - platform_fee_cents
end
def platform_fee_euros
platform_fee_cents / 100.0
end
def promoter_payout_euros
promoter_payout_cents / 100.0
end end
# Create Stripe invoice for accounting records # Create Stripe invoice for accounting records

View File

@@ -166,19 +166,7 @@ class StripeInvoiceService
}) })
end end
# Add service fee line item # No service fee on customer invoice; platform fee deducted from promoter payout
service_fee_cents = 100 # 1€ service fee
Stripe::InvoiceItem.create({
customer: customer.id,
invoice: invoice.id,
amount: service_fee_cents,
currency: "eur",
description: "Frais de service - Frais de traitement de la commande",
metadata: {
item_type: "service_fee",
amount_cents: service_fee_cents
}
})
end end
def build_line_item_description(ticket_type, tickets) def build_line_item_description(ticket_type, tickets)

View File

@@ -100,16 +100,8 @@
</div> </div>
<!-- Order Total --> <!-- Order Total -->
<div class="border-t border-gray-200 pt-6"> <div class=" pt-12">
<div class="space-y-2"> <div class="space-y-2">
<div class="flex items-center justify-between">
<span class="text-gray-600">Sous-total</span>
<span class="text-gray-900"><%= @order.total_amount_euros - 1.0 %>€</span>
</div>
<div class="flex items-center justify-between">
<span class="text-gray-600">Frais de service</span>
<span class="text-gray-900">1.00€</span>
</div>
<div class="flex items-center justify-between text-lg pt-2 border-t border-gray-200"> <div class="flex items-center justify-between text-lg pt-2 border-t border-gray-200">
<span class="font-medium text-gray-900">Total</span> <span class="font-medium text-gray-900">Total</span>
<span class="font-bold text-2xl text-purple-600"><%= @order.total_amount_euros %>€</span> <span class="font-bold text-2xl text-purple-600"><%= @order.total_amount_euros %>€</span>

View File

@@ -119,12 +119,6 @@
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900 text-right"><%= "%.2f" % (tickets.count * ticket_type.price_cents / 100.0) %>€</td> <td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900 text-right"><%= "%.2f" % (tickets.count * ticket_type.price_cents / 100.0) %>€</td>
</tr> </tr>
<% end %> <% end %>
<tr>
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">Frais de service</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 text-right">1</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 text-right">1.00€</td>
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900 text-right">1.00€</td>
</tr>
</tbody> </tbody>
<tfoot class="bg-gray-50"> <tfoot class="bg-gray-50">
<tr> <tr>

View File

@@ -96,22 +96,12 @@
<!-- Total --> <!-- Total -->
<div class="border-t border-gray-200 pt-6 mt-6"> <div class="border-t border-gray-200 pt-6 mt-6">
<div class="space-y-2">
<div class="flex items-center justify-between">
<span class="text-gray-600">Sous-total</span>
<span class="text-gray-900"><%= @order.total_amount_euros - 1.0 %>€</span>
</div>
<div class="flex items-center justify-between">
<span class="text-gray-600">Frais de service</span>
<span class="text-gray-900">1.00€</span>
</div>
<div class="flex items-center justify-between text-lg pt-2 border-t border-gray-200"> <div class="flex items-center justify-between text-lg pt-2 border-t border-gray-200">
<span class="font-medium text-gray-900">Total <%= @order.status == 'paid' || @order.status == 'completed' ? 'payé' : 'à payer' %></span> <span class="font-medium text-gray-900">Total <%= @order.status == 'paid' || @order.status == 'completed' ? 'payé' : 'à payer' %></span>
<span class="font-bold text-2xl <%= @order.status == 'paid' || @order.status == 'completed' ? 'text-green-600' : 'text-purple-600' %>"> <span class="font-bold text-2xl <%= @order.status == 'paid' || @order.status == 'completed' ? 'text-green-600' : 'text-purple-600' %>">
<%= @order.total_amount_euros %>€ <%= @order.total_amount_euros %>€
</span> </span>
</div> </div>
</div>
</div> </div>
<!-- View Invoice --> <!-- View Invoice -->

View File

@@ -3,28 +3,32 @@
## Current Problem Analysis ## Current Problem Analysis
### Current Model: €1 Fixed Fee Per Order ### Current Model: €1 Fixed Fee Per Order
- **Revenue**: €1.00 per order (regardless of ticket price) - **Revenue**: €1.00 per order (regardless of ticket price)
- **Stripe Costs**: 1.15% + €0.25 per transaction - **Stripe Costs**: 1.15% + €0.25 per transaction
- **Result**: Losing money on higher-priced tickets - **Result**: Losing money on higher-priced tickets
### Financial Impact Examples ### Financial Impact Examples
| Ticket Price | Current Revenue | Stripe Fees | Net Profit/Loss | | Ticket Price | Current Revenue | Stripe Fees | Net Profit/Loss |
|--------------|----------------|-------------|-----------------| | ------------ | --------------- | ----------- | --------------- |
| €20 | €1.00 | €0.48 | **+€0.52** ✅ | | €20 | €1.00 | €0.48 | **+€0.52** ✅ |
| €50 | €1.00 | €0.83 | **+€0.17** ⚠️ | | €50 | €1.00 | €0.83 | **+€0.17** ⚠️ |
| €100 | €1.00 | €1.40 | **-€0.40** ❌ | | €100 | €1.00 | €1.40 | **-€0.40** ❌ |
| €200 | €1.00 | €2.55 | **-€1.55** ❌ | | €200 | €1.00 | €2.55 | **-€1.55** ❌ |
**Critical Issue**: We lose money on any ticket above €65 (€1.00 - (€65 × 1.15% + €0.25) = -€0.00) **Critical Issue**: We lose money on any ticket above €65 (€1.00 - (€65 × 1.15% + €0.25) = -€0.00)
## Recommended Pricing Models ## Recommended Pricing Models
### Model 1: Percentage-Based (Recommended) ### Model 1: Percentage-Based (Recommended)
``` ```
Platform Fee = 3-5% of ticket price Platform Fee = 3-5% of ticket price
``` ```
**Advantages:** **Advantages:**
- Always profitable regardless of ticket price - Always profitable regardless of ticket price
- Scales naturally with event value - Scales naturally with event value
- Simple for promoters to understand - Simple for promoters to understand
@@ -33,17 +37,19 @@ Platform Fee = 3-5% of ticket price
**Financial Impact:** **Financial Impact:**
| Ticket Price | 3.5% Fee | Stripe Costs | Net Profit | | Ticket Price | 3.5% Fee | Stripe Costs | Net Profit |
|--------------|----------|--------------|------------| |--------------|----------|--------------|------------|
| €20 | €0.70 | €0.48 | €0.22 | | €20 | €0.70 | €0.48 | €0.22 |
| €50 | €1.75 | €0.83 | €0.92 | | €50 | €1.75 | €0.83 | €0.92 |
| €100 | €3.50 | €1.40 | €2.10 | | €100 | €3.50 | €1.40 | €2.10 |
| €200 | €7.00 | €2.55 | €4.45 | | €200 | €7.00 | €2.55 | €4.45 |
### Model 2: Hybrid Model (Best) ### Model 2: Hybrid Model (Best)
``` ```
Platform Fee = €1.50 + 2% of ticket price Platform Fee = €1.50 + 2% of ticket price
``` ```
**Advantages:** **Advantages:**
- Higher base fee covers Stripe fixed costs - Higher base fee covers Stripe fixed costs
- Percentage component scales with value - Percentage component scales with value
- Better margins on low-priced tickets - Better margins on low-priced tickets
@@ -52,12 +58,13 @@ Platform Fee = €1.50 + 2% of ticket price
**Financial Impact:** **Financial Impact:**
| Ticket Price | Hybrid Fee | Stripe Costs | Net Profit | | Ticket Price | Hybrid Fee | Stripe Costs | Net Profit |
|--------------|------------|--------------|------------| |--------------|------------|--------------|------------|
| €20 | €1.90 | €0.48 | €1.42 | | €20 | €1.90 | €0.48 | €1.42 |
| €50 | €2.50 | €0.83 | €1.67 | | €50 | €2.50 | €0.83 | €1.67 |
| €100 | €3.50 | €1.40 | €2.10 | | €100 | €3.50 | €1.40 | €2.10 |
| €200 | €5.50 | €2.55 | €2.95 | | €200 | €5.50 | €2.55 | €2.95 |
### Model 3: Tiered Flat Fees ### Model 3: Tiered Flat Fees
``` ```
€0-25: €1.50 fee €0-25: €1.50 fee
€26-75: €2.50 fee €26-75: €2.50 fee
@@ -66,32 +73,36 @@ Platform Fee = €1.50 + 2% of ticket price
``` ```
**Advantages:** **Advantages:**
- Predictable fees for promoters - Predictable fees for promoters
- Simple pricing tiers - Simple pricing tiers
- Better than current model - Better than current model
**Disadvantages:** **Disadvantages:**
- Less scalable than percentage - Less scalable than percentage
- Requires tier management - Requires tier management
## Industry Benchmarks ## Industry Benchmarks
| Platform | Fee Structure | Effective Rate (€50 ticket) | | Platform | Fee Structure | Effective Rate (€50 ticket) |
|-------------|----------------------------|------------------------------| | --------------- | ------------- | --------------------------- |
| Eventbrite | 3.7% + €0.59 | €2.44 | | Eventbrite | 3.7% + €0.59 | €2.44 |
| Universe | 2.5% + €0.49 | €1.74 | | Universe | 2.5% + €0.49 | €1.74 |
| Ticketfly | 4% + €0.99 | €2.99 | | Ticketfly | 4% + €0.99 | €2.99 |
| **Recommended** | **3.5%** | **€1.75** | | **Recommended** | **3.5%** | **€1.75** |
## Implementation Strategy ## Implementation Strategy
### Phase 1: Immediate Implementation (Week 1-2) ### Phase 1: Immediate Implementation (Week 1-2)
1. **Switch to 3.5% percentage model** for new events 1. **Switch to 3.5% percentage model** for new events
2. **Grandfathering**: Existing published events keep €1 pricing 2. **Grandfathering**: Existing published events keep €1 pricing
3. **Communication**: Notify promoters 1 week before change 3. **Communication**: Notify promoters 1 week before change
4. **Update pricing display** on event creation and checkout pages 4. **Update pricing display** on event creation and checkout pages
### Phase 2: Optimization (Month 2-3) ### Phase 2: Optimization (Month 2-3)
1. **A/B Test different rates**: 3%, 3.5%, 4% 1. **A/B Test different rates**: 3%, 3.5%, 4%
2. **Monitor metrics**: 2. **Monitor metrics**:
- Promoter signup rate - Promoter signup rate
@@ -101,6 +112,7 @@ Platform Fee = €1.50 + 2% of ticket price
3. **Adjust based on data** 3. **Adjust based on data**
### Phase 3: Premium Tiers (Month 4-6) ### Phase 3: Premium Tiers (Month 4-6)
1. **Volume discounts** for high-performing promoters: 1. **Volume discounts** for high-performing promoters:
- Standard: 3.5% - Standard: 3.5%
- Bronze (€5K+ monthly): 3% - Bronze (€5K+ monthly): 3%
@@ -112,6 +124,7 @@ Platform Fee = €1.50 + 2% of ticket price
## Revenue Projections ## Revenue Projections
### Current State (€1 fixed) ### Current State (€1 fixed)
- Average ticket price: €35 - Average ticket price: €35
- Monthly orders: 1,000 - Monthly orders: 1,000
- Monthly revenue: €1,000 - Monthly revenue: €1,000
@@ -119,11 +132,13 @@ Platform Fee = €1.50 + 2% of ticket price
- **Net monthly profit: €372** - **Net monthly profit: €372**
### With 3.5% Model ### With 3.5% Model
- Monthly revenue: €1,225 (3.5% × €35 × 1,000) - Monthly revenue: €1,225 (3.5% × €35 × 1,000)
- Monthly Stripe costs: €628 - Monthly Stripe costs: €628
- **Net monthly profit: €597** (+60% increase) - **Net monthly profit: €597** (+60% increase)
### With Growth (3.5% model + 20% more events) ### With Growth (3.5% model + 20% more events)
- Monthly orders: 1,200 (20% growth from competitive pricing) - Monthly orders: 1,200 (20% growth from competitive pricing)
- Monthly revenue: €1,470 - Monthly revenue: €1,470
- **Net monthly profit: €842** (+126% increase) - **Net monthly profit: €842** (+126% increase)
@@ -131,6 +146,7 @@ Platform Fee = €1.50 + 2% of ticket price
## Technical Implementation ## Technical Implementation
### Database Changes ### Database Changes
```ruby ```ruby
# Add to events table # Add to events table
add_column :events, :commission_rate, :decimal, precision: 5, scale: 4, default: 0.035 add_column :events, :commission_rate, :decimal, precision: 5, scale: 4, default: 0.035
@@ -140,6 +156,7 @@ add_column :users, :commission_tier, :string, default: 'standard'
``` ```
### Fee Calculation Logic ### Fee Calculation Logic
```ruby ```ruby
class Order < ApplicationRecord class Order < ApplicationRecord
def calculate_platform_fee def calculate_platform_fee
@@ -156,6 +173,7 @@ end
``` ```
### Promoter Dashboard Updates ### Promoter Dashboard Updates
- Show fee breakdown on event creation - Show fee breakdown on event creation
- Display projected fees during ticket setup - Display projected fees during ticket setup
- Add revenue vs. fees analytics - Add revenue vs. fees analytics
@@ -164,9 +182,11 @@ end
## Communication Plan ## Communication Plan
### Email to Existing Promoters ### Email to Existing Promoters
**Subject**: "Important Pricing Update - New Fair Fee Structure" **Subject**: "Important Pricing Update - New Fair Fee Structure"
**Key Points**: **Key Points**:
- Current model loses money on higher-priced tickets - Current model loses money on higher-priced tickets
- New model ensures platform sustainability - New model ensures platform sustainability
- Better features and support with improved revenue - Better features and support with improved revenue
@@ -174,6 +194,7 @@ end
- Competitive with industry standards - Competitive with industry standards
### Website Updates ### Website Updates
- Update pricing page with clear fee calculator - Update pricing page with clear fee calculator
- Add FAQ about fee structure - Add FAQ about fee structure
- Transparency about what fees cover (development, support, payment processing) - Transparency about what fees cover (development, support, payment processing)
@@ -181,11 +202,13 @@ end
## Risk Mitigation ## Risk Mitigation
### Potential Issues ### Potential Issues
1. **Promoter backlash**: Higher fees on expensive tickets 1. **Promoter backlash**: Higher fees on expensive tickets
2. **Competitor advantage**: Other platforms with lower fees 2. **Competitor advantage**: Other platforms with lower fees
3. **Reduced event creation**: Promoters may create fewer events 3. **Reduced event creation**: Promoters may create fewer events
### Mitigation Strategies ### Mitigation Strategies
1. **Value communication**: Emphasize platform improvements and reliability 1. **Value communication**: Emphasize platform improvements and reliability
2. **Competitive analysis**: Position as "fair and sustainable" vs. competitors 2. **Competitive analysis**: Position as "fair and sustainable" vs. competitors
3. **Volume incentives**: Quick path to reduced rates for active promoters 3. **Volume incentives**: Quick path to reduced rates for active promoters
@@ -194,12 +217,14 @@ end
## Success Metrics ## Success Metrics
### Financial KPIs ### Financial KPIs
- Monthly recurring revenue growth - Monthly recurring revenue growth
- Average revenue per transaction - Average revenue per transaction
- Profit margin improvement - Profit margin improvement
- Customer acquisition cost vs. lifetime value - Customer acquisition cost vs. lifetime value
### Product KPIs ### Product KPIs
- Promoter retention rate - Promoter retention rate
- New event creation volume - New event creation volume
- Average ticket prices - Average ticket prices
@@ -221,6 +246,7 @@ The current €1 fixed fee model is financially unsustainable and actually costs
### Current Approach vs Industry Standard ### Current Approach vs Industry Standard
**Current Model (Not Recommended)**: **Current Model (Not Recommended)**:
``` ```
Customer pays: €50 + €1 fee = €51 Customer pays: €50 + €1 fee = €51
Promoter receives: €50 Promoter receives: €50
@@ -228,6 +254,7 @@ Platform keeps: €1
``` ```
**Industry Standard (Recommended)**: **Industry Standard (Recommended)**:
``` ```
Customer pays: €50 (clean price) Customer pays: €50 (clean price)
Platform keeps: €1.75 (3.5% of €50) Platform keeps: €1.75 (3.5% of €50)
@@ -237,18 +264,21 @@ Promoter receives: €48.25
### How Major Platforms Handle Fees ### How Major Platforms Handle Fees
**Eventbrite:** **Eventbrite:**
- Promoter sets: €50 ticket - Promoter sets: €50 ticket
- Customer pays: €50 - Customer pays: €50
- Eventbrite keeps: €1.85 (3.7%) - Eventbrite keeps: €1.85 (3.7%)
- Promoter receives: €48.15 - Promoter receives: €48.15
**Ticketmaster:** **Ticketmaster:**
- Promoter sets: €50 ticket - Promoter sets: €50 ticket
- Customer pays: €50 - Customer pays: €50
- Ticketmaster keeps: €5-7.50 (10-15%) - Ticketmaster keeps: €5-7.50 (10-15%)
- Promoter receives: €42.50-45 - Promoter receives: €42.50-45
**Universe (by Ticketmaster):** **Universe (by Ticketmaster):**
- Promoter sets: €50 ticket - Promoter sets: €50 ticket
- Customer pays: €50 - Customer pays: €50
- Universe keeps: €1.74 (2.5% + €0.49) - Universe keeps: €1.74 (2.5% + €0.49)
@@ -257,16 +287,19 @@ Promoter receives: €48.25
### Why Deducting from Payout is Better ### Why Deducting from Payout is Better
#### 1. Customer Experience #### 1. Customer Experience
- **Price transparency**: Customer sees exactly what they expect to pay - **Price transparency**: Customer sees exactly what they expect to pay
- **No surprise fees**: Reduces cart abandonment - **No surprise fees**: Reduces cart abandonment
- **Competitive pricing**: Easier to compare with other events - **Competitive pricing**: Easier to compare with other events
#### 2. Promoter Benefits #### 2. Promoter Benefits
- **Marketing simplicity**: Can advertise clean prices - **Marketing simplicity**: Can advertise clean prices
- **Psychological pricing**: €50 sounds better than €51.75 - **Psychological pricing**: €50 sounds better than €51.75
- **Competitive advantage**: Not adding extra fees to customer - **Competitive advantage**: Not adding extra fees to customer
#### 3. Platform Benefits #### 3. Platform Benefits
- **Higher conversion rates**: No fee-shock at checkout - **Higher conversion rates**: No fee-shock at checkout
- **Better promoter adoption**: Easier to sell to event organizers - **Better promoter adoption**: Easier to sell to event organizers
- **Industry standard**: Follows established practices - **Industry standard**: Follows established practices
@@ -274,14 +307,17 @@ Promoter receives: €48.25
### Psychological Impact ### Psychological Impact
**Adding Fees to Customer (Current)**: **Adding Fees to Customer (Current)**:
- Customer thinks: "€50 ticket... oh wait, €51.75 total" 😤 - Customer thinks: "€50 ticket... oh wait, €51.75 total" 😤
- Cart abandonment risk - Cart abandonment risk
**Deducting from Payout (Recommended)**: **Deducting from Payout (Recommended)**:
- Customer thinks: "€50 ticket, €50 total" 😊 - Customer thinks: "€50 ticket, €50 total" 😊
- Smooth purchase experience - Smooth purchase experience
### Promoter Dashboard Display ### Promoter Dashboard Display
``` ```
Ticket Price: €50.00 Ticket Price: €50.00
Platform Fee (3.5%): -€1.75 Platform Fee (3.5%): -€1.75
@@ -289,10 +325,12 @@ Your Earnings per Ticket: €48.25
``` ```
### Communication to Promoters ### Communication to Promoters
**Before:** "Platform charges €1 per order to customers" **Before:** "Platform charges €1 per order to customers"
**After:** "Set your desired revenue per ticket, we handle the rest" **After:** "Set your desired revenue per ticket, we handle the rest"
**Example:** **Example:**
- Promoter wants €48.25 net per ticket - Promoter wants €48.25 net per ticket
- They should set ticket price at €50 - They should set ticket price at €50
- Customer pays €50, promoter gets €48.25 - Customer pays €50, promoter gets €48.25
@@ -310,45 +348,49 @@ Our main competitor charges a simple €1 flat fee per order. Here's how our mod
**Competitor**: €1.00 flat fee **Competitor**: €1.00 flat fee
| Ticket Price | Competitor Fee | Hybrid Fee | Difference | Competitive Position | | Ticket Price | Competitor Fee | Hybrid Fee | Difference | Competitive Position |
|--------------|----------------|------------|------------|---------------------| | ------------ | -------------- | ---------- | ---------- | -------------------- |
| €10 | €1.00 | €1.70 | +€0.70 | More expensive | | €10 | €1.00 | €1.70 | +€0.70 | More expensive |
| €25 | €1.00 | €2.00 | +€1.00 | More expensive | | €25 | €1.00 | €2.00 | +€1.00 | More expensive |
| €50 | €1.00 | €2.50 | +€1.50 | More expensive | | €50 | €1.00 | €2.50 | +€1.50 | More expensive |
| **€75** | **€1.00** | **€3.00** | **+€2.00** | **Break-even point** | | **€75** | **€1.00** | **€3.00** | **+€2.00** | **Break-even point** |
| €100 | €1.00 | €3.50 | +€2.50 | Much more expensive | | €100 | €1.00 | €3.50 | +€2.50 | Much more expensive |
### Alternative Competitive Models ### Alternative Competitive Models
#### Option 1: Low-End Competitive Model #### Option 1: Low-End Competitive Model
``` ```
Platform Fee = €0.50 + 1.5% of ticket price Platform Fee = €0.50 + 1.5% of ticket price
``` ```
| Ticket Price | Competitor Fee | Our Fee | Difference | Position | | Ticket Price | Competitor Fee | Our Fee | Difference | Position |
|--------------|----------------|---------|------------|----------| | ------------ | -------------- | ------- | ---------- | ------------------ |
| €10 | €1.00 | €0.65 | **-€0.35** | ✅ **Cheaper** | | €10 | €1.00 | €0.65 | **-€0.35** | ✅ **Cheaper** |
| €25 | €1.00 | €0.88 | **-€0.12** | ✅ **Cheaper** | | €25 | €1.00 | €0.88 | **-€0.12** | ✅ **Cheaper** |
| €50 | €1.00 | €1.25 | +€0.25 | ⚠️ Slightly higher | | €50 | €1.00 | €1.25 | +€0.25 | ⚠️ Slightly higher |
| €100 | €1.00 | €2.00 | +€1.00 | More expensive | | €100 | €1.00 | €2.00 | +€1.00 | More expensive |
#### Option 2: Modified Hybrid Model #### Option 2: Modified Hybrid Model
``` ```
Platform Fee = €0.75 + 2.5% of ticket price Platform Fee = €0.75 + 2.5% of ticket price
``` ```
| Ticket Price | Competitor Fee | Our Fee | Difference | Position | | Ticket Price | Competitor Fee | Our Fee | Difference | Position |
|--------------|----------------|---------|------------|----------| | ------------ | -------------- | ------- | ---------- | ------------------- |
| €10 | €1.00 | €1.00 | **Equal** | ✅ Competitive | | €10 | €1.00 | €1.00 | **Equal** | ✅ Competitive |
| €25 | €1.00 | €1.38 | +€0.38 | ⚠️ Slightly higher | | €25 | €1.00 | €1.38 | +€0.38 | ⚠️ Slightly higher |
| €40 | €1.00 | €1.75 | +€0.75 | **Break-even** | | €40 | €1.00 | €1.75 | +€0.75 | **Break-even** |
| €75 | €1.00 | €2.63 | +€1.63 | Much more expensive | | €75 | €1.00 | €2.63 | +€1.63 | Much more expensive |
### Competitive Strategy Recommendations ### Competitive Strategy Recommendations
#### 1. Value Differentiation Approach #### 1. Value Differentiation Approach
Since we'll be more expensive on higher-priced tickets, focus on premium positioning: Since we'll be more expensive on higher-priced tickets, focus on premium positioning:
**Value Proposition:** **Value Proposition:**
- "We're not the cheapest, we're the most complete" - "We're not the cheapest, we're the most complete"
- Advanced analytics dashboard - Advanced analytics dashboard
- Real-time sales tracking - Real-time sales tracking
@@ -359,12 +401,14 @@ Since we'll be more expensive on higher-priced tickets, focus on premium positio
#### 2. Market Segmentation Strategy #### 2. Market Segmentation Strategy
**Target Market Positioning:** **Target Market Positioning:**
- **Competitor**: Best for small, simple events (€10-30 tickets) - **Competitor**: Best for small, simple events (€10-30 tickets)
- **Us**: Best for professional events (€40+ tickets) with serious promoters - **Us**: Best for professional events (€40+ tickets) with serious promoters
#### 3. Hybrid Competitive Approach #### 3. Hybrid Competitive Approach
**Tiered Offering:** **Tiered Offering:**
- **Basic Plan**: Match competitor at €1 flat fee (limited features) - **Basic Plan**: Match competitor at €1 flat fee (limited features)
- **Professional Plan**: Hybrid model with premium features - **Professional Plan**: Hybrid model with premium features
- **Enterprise Plan**: Custom pricing with full feature set - **Enterprise Plan**: Custom pricing with full feature set
@@ -372,6 +416,7 @@ Since we'll be more expensive on higher-priced tickets, focus on premium positio
#### 4. Volume-Based Competitive Response #### 4. Volume-Based Competitive Response
**Free Tier Strategy:** **Free Tier Strategy:**
- First 3 events per month at competitor's €1 rate - First 3 events per month at competitor's €1 rate
- Volume discounts for high-activity promoters - Volume discounts for high-activity promoters
- Loyalty rewards for long-term customers - Loyalty rewards for long-term customers
@@ -381,15 +426,18 @@ Since we'll be more expensive on higher-priced tickets, focus on premium positio
#### "Choose Your Business Model" Campaign #### "Choose Your Business Model" Campaign
**For Simple Events (Under €40):** **For Simple Events (Under €40):**
- "Need basic ticketing? Our competitor works fine" - "Need basic ticketing? Our competitor works fine"
- "Pay €1 flat fee for simple events" - "Pay €1 flat fee for simple events"
**For Professional Events (€40+):** **For Professional Events (€40+):**
- "Serious about your business? You need serious tools" - "Serious about your business? You need serious tools"
- "Fair percentage-based pricing" - "Fair percentage-based pricing"
- "Advanced analytics, marketing tools, priority support" - "Advanced analytics, marketing tools, priority support"
#### Brand Positioning Statement #### Brand Positioning Statement
**"We're the Shopify of Events - Built for Growth"** **"We're the Shopify of Events - Built for Growth"**
This positions us as the premium option for serious promoters while acknowledging the competitor's advantage on small events. This positions us as the premium option for serious promoters while acknowledging the competitor's advantage on small events.
@@ -399,12 +447,14 @@ This positions us as the premium option for serious promoters while acknowledgin
Given the competitive landscape, we recommend **Option 1** (€0.50 + 1.5%): Given the competitive landscape, we recommend **Option 1** (€0.50 + 1.5%):
**Advantages:** **Advantages:**
- Competitive on low-priced tickets - Competitive on low-priced tickets
- Still profitable at all price points - Still profitable at all price points
- Better positioning against main competitor - Better positioning against main competitor
- Appeals to both small and large event organizers - Appeals to both small and large event organizers
**Financial Impact:** **Financial Impact:**
- Lower fees on tickets under €33 - Lower fees on tickets under €33
- Competitive fees on tickets €33-66 - Competitive fees on tickets €33-66
- Premium pricing on high-value tickets justified by features - Premium pricing on high-value tickets justified by features

View File

@@ -469,7 +469,7 @@ class OrderTest < ActiveSupport::TestCase
assert_equal "active", ticket2.status assert_equal "active", ticket2.status
end end
test "calculate_total! should sum ticket prices plus 1€ service fee" do test "calculate_total! should sum ticket prices only (platform fee deducted from promoter payout)" do
order = Order.create!( order = Order.create!(
user: @user, event: @event, total_amount_cents: 0, user: @user, event: @event, total_amount_cents: 0,
status: "draft", payment_attempts: 0 status: "draft", payment_attempts: 0
@@ -506,7 +506,80 @@ class OrderTest < ActiveSupport::TestCase
order.calculate_total! order.calculate_total!
order.reload order.reload
assert_equal 3100, order.total_amount_cents # 2 tickets * 1500 cents + 100 cents (1€ fee) assert_equal 3000, order.total_amount_cents # 2 tickets * 1500 cents (no service fee added to customer)
end
test "platform_fee_cents should calculate €0.50 + 1.5% per ticket" do
order = Order.create!(
user: @user, event: @event, total_amount_cents: 0,
status: "draft", payment_attempts: 0
)
ticket_type1 = TicketType.create!(
name: "Cheap Ticket",
description: "Cheap ticket type",
price_cents: 1000, # €10
quantity: 10,
sale_start_at: Time.current,
sale_end_at: Time.current + 1.day,
requires_id: false,
event: @event
)
ticket_type2 = TicketType.create!(
name: "Expensive Ticket",
description: "Expensive ticket type",
price_cents: 5000, # €50
quantity: 10,
sale_start_at: Time.current,
sale_end_at: Time.current + 1.day,
requires_id: false,
event: @event
)
ticket1 = Ticket.create!(order: order, ticket_type: ticket_type1, status: "draft", first_name: "John", last_name: "Doe")
ticket2 = Ticket.create!(order: order, ticket_type: ticket_type2, status: "draft", first_name: "Jane", last_name: "Doe")
expected_fee = (50 + (1000 * 0.015).to_i) + (50 + (5000 * 0.015).to_i) # 50+15 + 50+75 = 190
assert_equal 190, order.platform_fee_cents
end
test "promoter_payout_cents should be total minus platform fee" do
order = Order.create!(
user: @user, event: @event, total_amount_cents: 3000,
status: "paid", payment_attempts: 0
)
ticket_type = TicketType.create!(
name: "Test Ticket",
description: "Test ticket",
price_cents: 1500,
quantity: 10,
sale_start_at: Time.current,
sale_end_at: Time.current + 1.day,
requires_id: false,
event: @event
)
Ticket.create!(order: order, ticket_type: ticket_type, status: "active", first_name: "John", last_name: "Doe")
Ticket.create!(order: order, ticket_type: ticket_type, status: "active", first_name: "Jane", last_name: "Doe")
order.calculate_total! # Should still be 3000
expected_payout = 3000 - (50 + (1500 * 0.015).to_i) * 2 # 3000 - (50+22.5≈22)*2 = 3000 - 144 = 2856
assert_equal 2856, order.promoter_payout_cents
end
test "platform_fee_euros should convert cents to euros" do
order = Order.new(total_amount_cents: 0)
# Assuming one €10 ticket: 50 + 150 = 200 cents = €2.00
def order.platform_fee_cents; 200; end
assert_equal 2.0, order.platform_fee_euros
end
test "promoter_payout_euros should convert cents to euros" do
order = Order.new(total_amount_cents: 10000)
def order.platform_fee_cents; 500; end
assert_equal 95.0, order.promoter_payout_euros
end end
# === Stripe Integration Tests (Mock) === # === Stripe Integration Tests (Mock) ===

View File

@@ -151,7 +151,7 @@ class StripeInvoiceServiceTest < ActiveSupport::TestCase
mock_invoice.stubs(:finalize_invoice).returns(mock_invoice) mock_invoice.stubs(:finalize_invoice).returns(mock_invoice)
mock_invoice.expects(:pay) mock_invoice.expects(:pay)
Stripe::Invoice.expects(:create).returns(mock_invoice) Stripe::Invoice.expects(:create).returns(mock_invoice)
Stripe::InvoiceItem.expects(:create).twice # Once for tickets, once for service fee Stripe::InvoiceItem.expects(:create).once # Only for tickets, no service fee
result = @service.create_post_payment_invoice result = @service.create_post_payment_invoice
assert_not_nil result assert_not_nil result
@@ -173,7 +173,7 @@ class StripeInvoiceServiceTest < ActiveSupport::TestCase
mock_invoice.stubs(:finalize_invoice).returns(mock_invoice) mock_invoice.stubs(:finalize_invoice).returns(mock_invoice)
mock_invoice.expects(:pay) mock_invoice.expects(:pay)
Stripe::Invoice.expects(:create).returns(mock_invoice) Stripe::Invoice.expects(:create).returns(mock_invoice)
Stripe::InvoiceItem.expects(:create).twice # Once for tickets, once for service fee Stripe::InvoiceItem.expects(:create).once # Only for tickets, no service fee
result = @service.create_post_payment_invoice result = @service.create_post_payment_invoice
assert_not_nil result assert_not_nil result
@@ -261,7 +261,7 @@ class StripeInvoiceServiceTest < ActiveSupport::TestCase
mock_invoice.expects(:pay) mock_invoice.expects(:pay)
Stripe::Invoice.expects(:create).with(expected_invoice_data).returns(mock_invoice) Stripe::Invoice.expects(:create).with(expected_invoice_data).returns(mock_invoice)
Stripe::InvoiceItem.expects(:create).twice # Once for tickets, once for service fee Stripe::InvoiceItem.expects(:create).once # Only for tickets, no service fee
result = @service.create_post_payment_invoice result = @service.create_post_payment_invoice
assert_not_nil result assert_not_nil result
@@ -300,7 +300,7 @@ class StripeInvoiceServiceTest < ActiveSupport::TestCase
}) })
Stripe::Invoice.expects(:create).returns(mock_invoice) Stripe::Invoice.expects(:create).returns(mock_invoice)
Stripe::InvoiceItem.expects(:create).twice # Once for tickets, once for service fee Stripe::InvoiceItem.expects(:create).once # Only for tickets, no service fee
mock_invoice.expects(:finalize_invoice).returns(mock_finalized_invoice) mock_invoice.expects(:finalize_invoice).returns(mock_finalized_invoice)
result = @service.create_post_payment_invoice result = @service.create_post_payment_invoice