Skip to content

Instantly share code, notes, and snippets.

@aungkyawminn
Created December 30, 2025 16:05
Show Gist options
  • Select an option

  • Save aungkyawminn/3072847e40edce21be2d0912f3698b21 to your computer and use it in GitHub Desktop.

Select an option

Save aungkyawminn/3072847e40edce21be2d0912f3698b21 to your computer and use it in GitHub Desktop.

Platform Fee Rules – Design & Implementation Guide

This document defines how platform fee rules work in the Eventickat system. It is intended for backend developers, frontend developers, and admins who manage pricing rules.


1. Purpose of Platform Fee Rules

Platform fee rules define how much commission the platform earns from ticket sales.

Key goals:

  • Support default, organizer-specific, and event-specific fees
  • Allow time-based changes without breaking old tickets
  • Ensure pricing immutability after purchase
  • Be safe for finance & audit

Platform fee rules are applied at ticket purchase time, not at settlement time.


2. Core Design Principles

  1. Fees are calculated once at checkout
  2. Calculated values are stored per ticket (pricing snapshot)
  3. Settlement only aggregates stored values
  4. Historical rules are never edited or deleted
  5. New pricing is introduced using effective dates

3. Database Schema

3.1 platform_fee_rules table

CREATE TABLE platform_fee_rules (
    id              UUID PRIMARY KEY,

    organizer_id    UUID NULL,   -- NULL = applies to all organizers
    event_id        UUID NULL,   -- NULL = applies to all events

    fee_type        VARCHAR(20) NOT NULL,
    -- PERCENTAGE | FIXED

    fee_value       DECIMAL(8,4) NOT NULL,
    -- For PERCENTAGE: stored as 0-100 (e.g., 5.25 means 5.25%)
    -- For FIXED: stored as absolute amount in currency

    currency        CHAR(3),     -- required if FIXED

    effective_from  TIMESTAMP NOT NULL,
    effective_to    TIMESTAMP NULL,

    is_active       BOOLEAN NOT NULL DEFAULT TRUE,

    created_at      TIMESTAMP NOT NULL DEFAULT now(),
    created_by      UUID
);

4. Fee Value Storage Format

4.1 Design Decision: Business-Friendly Percentage

Platform fee rules use percentage notation (0-100) rather than decimal rate notation (0-1).

Rationale:

  1. Admin-facing context: Business admins think in "5%" not "0.05"
  2. Industry standard: Matches Stripe Connect, Shopify, PayPal APIs
  3. UI simplicity: Input shows "5.25" directly, no conversion needed
  4. Clear validation: Range 0-100 is intuitive

4.2 Storage Details

Fee Type Storage Format Example Meaning
PERCENTAGE 0-100 decimal 5.25 5.25% commission
FIXED Absolute amount 1000 1,000 MMK flat fee

Precision:

  • DECIMAL(10,2) in database
  • Examples: 5.25, 10.00, 100.00, 1000.00

4.3 Comparison with Other Settings

Setting Storage Format Reason
platform_fee_rules Percentage (0-100) Business/admin facing
payment_fee_rules Decimal rate (0-1) System-internal calculation
tax_rules Decimal rate (0-1) System-internal calculation

Note: Different notation is acceptable when contexts differ:

  • Platform fees: Business policy configuration
  • Payment/tax fees: Internal calculation rules

4.4 Validation & Calculation Code

// For PERCENTAGE type
if ($feeType === 'PERCENTAGE') {
    assert($feeValue >= 0 && $feeValue <= 100);
    $platformFee = $payoutAmount * ($feeValue / 100);
}

// For FIXED type
if ($feeType === 'FIXED') {
    assert($feeValue >= 0);
    assert(!empty($currency));
    $platformFee = $feeValue;
}

4.5 Frontend Implementation Examples

For Percentage Type:

<input 
  type="number" 
  min="0" 
  max="100" 
  step="0.01"
  placeholder="e.g., 5.25"
  label="Commission Rate (%)"
/>

For Fixed Type:

<input 
  type="number" 
  min="0" 
  step="0.01"
  placeholder="e.g., 1000"
  label="Fixed Fee Amount"
/>

5. Meaning of effective_from and effective_to

A rule is considered valid when:

effective_from <= pricing_time
AND
(effective_to IS NULL OR pricing_time < effective_to)

Rules:

  • effective_from is inclusive
  • effective_to is exclusive
  • NULL effective_to means open-ended

This avoids overlapping ambiguity.


6. Rule Scope & Priority

Supported scopes

Scope organizer_id event_id
Event-specific set set
Organizer-specific set NULL
Default NULL NULL

Priority order (highest wins)

  1. Event-specific rule
  2. Organizer-specific rule
  3. Default rule

7. Querying the Active Rule

SQL example

SELECT *
FROM platform_fee_rules
WHERE is_active = true
  AND effective_from <= :pricing_time
  AND (effective_to IS NULL OR :pricing_time < effective_to)
  AND (event_id = :event_id OR event_id IS NULL)
  AND (organizer_id = :organizer_id OR organizer_id IS NULL)
ORDER BY
  event_id DESC,
  organizer_id DESC,
  effective_from DESC
LIMIT 1;

This query guarantees one deterministic rule.


8. Fee Calculation Flow

At ticket purchase time

  1. Determine pricing timestamp (now())
  2. Load platform fee rule using query above
  3. Calculate platform fee
  4. Combine with payment fee & tax
  5. Store all values in pricing snapshot

Formula examples

Percentage fee

platform_fee = payout_amount × (fee_value / 100)

Example:

  • Payout amount: 50,000 MMK
  • fee_value: 5.25 (stored as percentage)
  • Platform fee: 50,000 × (5.25 / 100) = 50,000 × 0.0525 = 2,625 MMK

Fixed fee

platform_fee = fee_value

Example:

  • fee_value: 1000 (stored as absolute amount)
  • currency: MMK
  • Platform fee: 1,000 MMK (regardless of ticket price)

All rounding must happen before storing values.


9. Relationship to Settlement

  • platform_fee_rules are NOT used during settlement
  • Settlement uses stored ticket pricing snapshots
  • settlement_items are created by copying stored values

Settlement must never re-evaluate rules.


10. Admin UI – Rule Editor Design

10.1 Rule List Screen

Columns:

  • Scope (Default / Organizer / Event)
  • Target (Organizer name / Event name)
  • Fee (e.g. 5%)
  • Effective Period
  • Status (Upcoming / Active / Expired)

Actions:

  • View
  • Edit (future rules only)
  • Disable

No delete for historical rules.


9.2 Create / Edit Rule Form

Section A – Scope

  • Default
  • Organizer-specific (select organizer)
  • Event-specific (select event)

Section B – Fee

  • Fee type: Percentage / Fixed
  • Fee value:
    • For Percentage: Enter 0-100 (e.g., 5.25 means 5.25%)
    • For Fixed: Enter absolute amount (e.g., 1000)
  • Currency (required for Fixed type)

Section C – Effective Period

  • Effective From (required)
  • Effective To (optional, open-ended)

Section D – Preview

Display:

  • Who the rule applies to
  • When it applies
  • Which rule it replaces

10.3 Conflict Detection

If overlap is detected, UI must:

  • Show conflicting rule
  • Offer to auto-close old rule
  • Block unsafe save

Admins should never manually manage overlaps.


10.4 Rule History (Read-only)

Show:

  • All past versions
  • Created by / created at
  • Effective periods

No edit, no delete.


11. Backend Guardrails (Mandatory)

Backend must enforce:

  1. No overlapping rules per scope (same scope + overlapping dates = conflict)
  2. No updates to past rules (if effective_from has passed)
  3. One default rule always exists (system-wide fallback)
  4. Effective date validation (effective_to must be after effective_from)
  5. Fee value range validation:
    • PERCENTAGE: 0 ≤ fee_value ≤ 100
    • FIXED: fee_value ≥ 0
  6. Currency validation:
    • FIXED fee type requires currency
    • PERCENTAGE fee type doesn't use currency

UI helps – backend guarantees.


12. MVP vs Future Enhancements

MVP

  • Default + organizer rules
  • Percentage fee only
  • Simple admin UI

Future

  • Approval workflow
  • Bulk rule updates
  • Simulation tools
  • Event templates

13. Summary

  • Platform fee rules are time-bound business policies
  • They are applied once at checkout
  • They must be queryable, auditable, and immutable
  • Admin UI should make it hard to do the wrong thing

Fees are promises. Tickets honor the promise made at purchase time.


End of document.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment