Initial commit - Black Canyon Tickets whitelabel platform
🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
246
supabase/migrations/006_standardize_bct_fees.sql
Normal file
246
supabase/migrations/006_standardize_bct_fees.sql
Normal file
@@ -0,0 +1,246 @@
|
||||
-- Standardize BCT platform fees across all organizations
|
||||
-- Organizations can only customize HOW fees are applied, not the fee amounts
|
||||
|
||||
-- Remove fee amount columns from organizations table since fees are now standard
|
||||
-- Keep only the fee application model (how fees are presented to customers)
|
||||
ALTER TABLE organizations
|
||||
DROP COLUMN IF EXISTS platform_fee_type,
|
||||
DROP COLUMN IF EXISTS platform_fee_percentage,
|
||||
DROP COLUMN IF EXISTS platform_fee_fixed;
|
||||
|
||||
-- Rename fee model column for clarity
|
||||
ALTER TABLE organizations
|
||||
RENAME COLUMN platform_fee_model TO fee_display_model;
|
||||
|
||||
-- Add comment for clarity
|
||||
COMMENT ON COLUMN organizations.fee_display_model IS 'How fees are displayed to customers: customer_pays (separate line) or absorbed_in_price (included)';
|
||||
COMMENT ON COLUMN organizations.absorb_fee_in_price IS 'Whether to include BCT fee in displayed ticket price (true) or show separately (false)';
|
||||
|
||||
-- Create platform_settings table for system-wide configuration
|
||||
CREATE TABLE platform_settings (
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
setting_key TEXT UNIQUE NOT NULL,
|
||||
setting_value JSONB NOT NULL,
|
||||
description TEXT,
|
||||
is_public BOOLEAN DEFAULT false, -- Whether this setting can be viewed by organizers
|
||||
updated_by UUID REFERENCES users(id),
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Insert standard BCT fee structure
|
||||
INSERT INTO platform_settings (setting_key, setting_value, description, is_public) VALUES
|
||||
('bct_platform_fee_percentage', '0.025', 'BCT platform fee percentage (2.5%)', true),
|
||||
('bct_platform_fee_fixed', '150', 'BCT platform fee fixed amount in cents ($1.50)', true),
|
||||
('stripe_fee_percentage', '0.0299', 'Stripe processing fee percentage (2.99%)', true),
|
||||
('stripe_fee_fixed', '30', 'Stripe processing fee fixed amount in cents ($0.30)', true),
|
||||
('platform_name', '"Black Canyon Tickets"', 'Platform display name', true),
|
||||
('platform_email', '"support@blackcanyontickets.com"', 'Platform support email', true),
|
||||
('max_events_per_organization', '100', 'Maximum events per organization', false);
|
||||
|
||||
-- Update all existing organizations to use standard fee display model
|
||||
UPDATE organizations
|
||||
SET
|
||||
fee_display_model = COALESCE(fee_display_model, 'customer_pays'),
|
||||
absorb_fee_in_price = COALESCE(absorb_fee_in_price, false);
|
||||
|
||||
-- Set default fee display model for new organizations
|
||||
ALTER TABLE organizations
|
||||
ALTER COLUMN fee_display_model SET DEFAULT 'customer_pays',
|
||||
ALTER COLUMN absorb_fee_in_price SET DEFAULT false;
|
||||
|
||||
-- Create function to get current BCT platform fees
|
||||
CREATE OR REPLACE FUNCTION get_bct_platform_fees()
|
||||
RETURNS TABLE(
|
||||
fee_percentage DECIMAL,
|
||||
fee_fixed INTEGER
|
||||
) AS $$
|
||||
BEGIN
|
||||
RETURN QUERY
|
||||
SELECT
|
||||
(SELECT (setting_value#>>'{}')::DECIMAL FROM platform_settings WHERE setting_key = 'bct_platform_fee_percentage'),
|
||||
(SELECT (setting_value#>>'{}')::INTEGER FROM platform_settings WHERE setting_key = 'bct_platform_fee_fixed');
|
||||
END;
|
||||
$$ LANGUAGE plpgsql SECURITY DEFINER;
|
||||
|
||||
-- Create function to get current Stripe fees
|
||||
CREATE OR REPLACE FUNCTION get_stripe_fees()
|
||||
RETURNS TABLE(
|
||||
fee_percentage DECIMAL,
|
||||
fee_fixed INTEGER
|
||||
) AS $$
|
||||
BEGIN
|
||||
RETURN QUERY
|
||||
SELECT
|
||||
(SELECT (setting_value#>>'{}')::DECIMAL FROM platform_settings WHERE setting_key = 'stripe_fee_percentage'),
|
||||
(SELECT (setting_value#>>'{}')::INTEGER FROM platform_settings WHERE setting_key = 'stripe_fee_fixed');
|
||||
END;
|
||||
$$ LANGUAGE plpgsql SECURITY DEFINER;
|
||||
|
||||
-- Update the display price calculation function to use standard fees
|
||||
CREATE OR REPLACE FUNCTION calculate_display_price_standard(
|
||||
base_price DECIMAL,
|
||||
fee_display_model VARCHAR DEFAULT 'customer_pays'
|
||||
) RETURNS DECIMAL AS $$
|
||||
DECLARE
|
||||
bct_fee_percentage DECIMAL;
|
||||
bct_fee_fixed INTEGER;
|
||||
platform_fee DECIMAL;
|
||||
display_price DECIMAL;
|
||||
BEGIN
|
||||
-- Get current BCT platform fees
|
||||
SELECT fee_percentage, fee_fixed INTO bct_fee_percentage, bct_fee_fixed
|
||||
FROM get_bct_platform_fees();
|
||||
|
||||
-- Calculate BCT platform fee
|
||||
platform_fee := (base_price * bct_fee_percentage) + (bct_fee_fixed / 100.0);
|
||||
|
||||
-- Calculate display price based on fee model
|
||||
CASE fee_display_model
|
||||
WHEN 'customer_pays' THEN
|
||||
-- Customer pays base price + platform fee separately
|
||||
display_price := base_price;
|
||||
WHEN 'absorbed_in_price' THEN
|
||||
-- Platform fee is absorbed into the display price
|
||||
display_price := base_price + platform_fee;
|
||||
ELSE
|
||||
display_price := base_price;
|
||||
END CASE;
|
||||
|
||||
RETURN ROUND(display_price, 2);
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
-- Update the customer total calculation function to use standard fees
|
||||
CREATE OR REPLACE FUNCTION calculate_customer_total_standard(
|
||||
base_price DECIMAL,
|
||||
fee_display_model VARCHAR DEFAULT 'customer_pays'
|
||||
) RETURNS DECIMAL AS $$
|
||||
DECLARE
|
||||
bct_fee_percentage DECIMAL;
|
||||
bct_fee_fixed INTEGER;
|
||||
platform_fee DECIMAL;
|
||||
customer_total DECIMAL;
|
||||
BEGIN
|
||||
-- Get current BCT platform fees
|
||||
SELECT fee_percentage, fee_fixed INTO bct_fee_percentage, bct_fee_fixed
|
||||
FROM get_bct_platform_fees();
|
||||
|
||||
-- Calculate BCT platform fee
|
||||
platform_fee := (base_price * bct_fee_percentage) + (bct_fee_fixed / 100.0);
|
||||
|
||||
-- Calculate total amount customer pays
|
||||
CASE fee_display_model
|
||||
WHEN 'customer_pays' THEN
|
||||
-- Customer pays base price + platform fee separately
|
||||
customer_total := base_price + platform_fee;
|
||||
WHEN 'absorbed_in_price' THEN
|
||||
-- Customer pays only the display price (fee is included)
|
||||
customer_total := base_price;
|
||||
ELSE
|
||||
customer_total := base_price + platform_fee;
|
||||
END CASE;
|
||||
|
||||
RETURN ROUND(customer_total, 2);
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
-- Update the organizer net calculation function to use standard fees
|
||||
CREATE OR REPLACE FUNCTION calculate_organizer_net_standard(
|
||||
base_price DECIMAL
|
||||
) RETURNS DECIMAL AS $$
|
||||
DECLARE
|
||||
bct_fee_percentage DECIMAL;
|
||||
bct_fee_fixed INTEGER;
|
||||
platform_fee DECIMAL;
|
||||
organizer_net DECIMAL;
|
||||
BEGIN
|
||||
-- Get current BCT platform fees
|
||||
SELECT fee_percentage, fee_fixed INTO bct_fee_percentage, bct_fee_fixed
|
||||
FROM get_bct_platform_fees();
|
||||
|
||||
-- Calculate BCT platform fee
|
||||
platform_fee := (base_price * bct_fee_percentage) + (bct_fee_fixed / 100.0);
|
||||
|
||||
-- Calculate organizer net (what they receive before Stripe fees)
|
||||
organizer_net := base_price - platform_fee;
|
||||
|
||||
-- Ensure organizer net is never negative
|
||||
IF organizer_net < 0 THEN
|
||||
organizer_net := 0;
|
||||
END IF;
|
||||
|
||||
RETURN ROUND(organizer_net, 2);
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
-- Enable RLS on platform_settings
|
||||
ALTER TABLE platform_settings ENABLE ROW LEVEL SECURITY;
|
||||
|
||||
-- Public settings can be viewed by authenticated users
|
||||
CREATE POLICY "Authenticated users can view public platform settings" ON platform_settings
|
||||
FOR SELECT USING (is_public = true AND auth.role() = 'authenticated');
|
||||
|
||||
-- Only admins can manage platform settings
|
||||
CREATE POLICY "Admins can manage platform settings" ON platform_settings
|
||||
FOR ALL USING (auth.uid() IN (SELECT id FROM users WHERE role = 'admin'));
|
||||
|
||||
-- Add indexes for performance
|
||||
CREATE INDEX idx_platform_settings_key ON platform_settings(setting_key);
|
||||
CREATE INDEX idx_platform_settings_public ON platform_settings(is_public);
|
||||
CREATE INDEX idx_organizations_fee_display_model ON organizations(fee_display_model);
|
||||
|
||||
-- Add check constraint to ensure valid fee display models
|
||||
ALTER TABLE organizations
|
||||
DROP CONSTRAINT IF EXISTS check_platform_fee_model,
|
||||
ADD CONSTRAINT check_fee_display_model
|
||||
CHECK (fee_display_model IN ('customer_pays', 'absorbed_in_price'));
|
||||
|
||||
-- Update fee_structures table to remove custom fee amounts (since fees are now standard)
|
||||
-- Keep only for reference and templates
|
||||
ALTER TABLE fee_structures
|
||||
ADD COLUMN is_deprecated BOOLEAN DEFAULT false;
|
||||
|
||||
-- Mark old custom fee structures as deprecated
|
||||
UPDATE fee_structures
|
||||
SET is_deprecated = true
|
||||
WHERE is_template = false;
|
||||
|
||||
-- Clean up old fee structure templates and add new standard ones
|
||||
DELETE FROM fee_structures WHERE is_template = true;
|
||||
|
||||
INSERT INTO fee_structures (name, description, fee_type, fee_percentage, fee_fixed, fee_model, absorb_fee_in_price, is_template) VALUES
|
||||
('BCT Standard - Customer Pays', 'Customer pays BCT fee (2.5% + $1.50) as separate line item', 'percentage_plus_fixed', 0.025, 150, 'customer_pays', false, true),
|
||||
('BCT Standard - All Inclusive', 'BCT fee (2.5% + $1.50) included in ticket price', 'percentage_plus_fixed', 0.025, 150, 'absorbed_in_price', true, true),
|
||||
('Stripe Processing Fee', 'Stripe credit card processing: 2.99% + $0.30', 'percentage_plus_fixed', 0.0299, 30, 'customer_pays', false, true);
|
||||
|
||||
-- Add audit logging for platform settings changes
|
||||
CREATE OR REPLACE FUNCTION log_platform_settings_change()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
IF TG_OP = 'UPDATE' THEN
|
||||
INSERT INTO audit_logs (user_id, action, resource_type, resource_id, old_values, new_values)
|
||||
VALUES (
|
||||
auth.uid(),
|
||||
'update',
|
||||
'platform_settings',
|
||||
NEW.id,
|
||||
row_to_json(OLD),
|
||||
row_to_json(NEW)
|
||||
);
|
||||
ELSIF TG_OP = 'INSERT' THEN
|
||||
INSERT INTO audit_logs (user_id, action, resource_type, resource_id, new_values)
|
||||
VALUES (
|
||||
auth.uid(),
|
||||
'create',
|
||||
'platform_settings',
|
||||
NEW.id,
|
||||
row_to_json(NEW)
|
||||
);
|
||||
END IF;
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql SECURITY DEFINER;
|
||||
|
||||
CREATE TRIGGER platform_settings_audit_trigger
|
||||
AFTER INSERT OR UPDATE ON platform_settings
|
||||
FOR EACH ROW EXECUTE FUNCTION log_platform_settings_change();
|
||||
Reference in New Issue
Block a user