advanced
pricing
billing
calculations

How Pricing Works

Comprehensive guide explaining how the entire pricing system calculates ride charges, from start to finish with the complete billing order of operations.

Levy Fleets TeamDecember 25, 202515 min read

How Pricing Works

This comprehensive guide explains how the entire pricing system calculates ride charges, from start to finish. Understanding this flow is essential for operators who want to configure pricing effectively and troubleshoot billing issues.


Configuration Checklist

Before your pricing system works correctly, ensure you've completed these steps:

  1. Verify each vehicle model exists for the active subaccount (location)
  2. Set a base pricing rule per model with unlock, pause, minimum, and daily cap values
  3. Create subscription plans riders can purchase from the mobile app or dashboard
  4. Publish ride packages that grant prepaid unlocks, minutes, or distance
  5. Layer dynamic pricing rules for time, weather, demand, or model-specific adjustments
  6. Issue promo codes and verify their scope (global, subaccount, vehicle type) and limits
  7. Test your configuration using the pricing API tests to validate everything end-to-end

Billing Order of Operations

When a ride completes, the system runs through these stages before charging the customer:

┌─────────────────────────────────────────────────────────────────┐
│                     RIDE COMPLETES                               │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│ STEP 1: Calculate Base Charges                                   │
│   • Apply unlock fee, time rate, pause rate, distance rate       │
│   • Enforce vehicle's daily cap                                  │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│ STEP 2: Apply Tier Benefits                                      │
│   • Check customer loyalty tier                                  │
│   • Apply unlock discount percentage                             │
│   • Apply per-minute discount percentage                         │
│   • Use free unlock if eligible and requested                    │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│ STEP 3: Consume Subscription Allowances                          │
│   • Find active subscription purchases                           │
│   • Deduct unlocks, minutes, distance from subscription          │
│   • Track usage for subscription limits                          │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│ STEP 4: Consume Ride Packages                                    │
│   • Find active package purchases (oldest first)                 │
│   • Deduct remaining unlocks, minutes, distance                  │
│   • Log detailed usage events                                    │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│ STEP 5: Apply Dynamic Pricing                                    │
│   • Evaluate time/weather/demand rules                           │
│   • Apply multipliers and fixed adjustments                      │
│   • Process rules by priority order                              │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│ STEP 6: Apply Promo Code                                         │
│   • Validate time windows, scopes, usage limits                  │
│   • Check minimum spend requirements                             │
│   • Apply percentage or fixed discount                           │
│   • Enforce max discount cap                                     │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│ STEP 7: Final Adjustments                                        │
│   • Re-check daily cap against final total                       │
│   • Apply minimum price (if no subscription/package used)        │
│   • Subtract any partial charges already collected               │
│   • Calculate final amount due                                   │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│                     CHARGE CUSTOMER                              │
└─────────────────────────────────────────────────────────────────┘

Detailed Stage Breakdown

Stage 1: Base Pricing Calculation

The first stage calculates raw charges based on the vehicle's pricing rule:

Inputs:

  • Ride duration (active minutes)
  • Pause duration (paused minutes)
  • Distance traveled (kilometers)
  • Vehicle pricing configuration

Calculation:

Base Subtotal = Unlock Fee
              + (Active Minutes × Per Minute Rate)
              + (Pause Minutes × Pause Rate)
              + (Distance KM × Per KM Rate)

Daily Cap Enforcement: If the subtotal exceeds the daily cap, charges are reduced in order:

  1. Time fees reduced first
  2. Pause fees reduced second
  3. Distance fees reduced third
  4. Unlock fee reduced last (only if needed)

Stage 2: Loyalty Tier Benefits

For customers with loyalty tier status, significant discounts may apply:

Tier Benefit Types:

BenefitDescriptionExample
unlock_discount_pctPercentage off unlock fee20% off $1.50 = $0.30 saved
per_minute_discount_pctPercentage off time charges15% off $5.00 = $0.75 saved
free_unlocks_per_monthWaive unlock fee entirely5 free unlocks per month

Application Order:

  1. Check free unlock - If customer requests and has remaining free unlocks
  2. Apply unlock discount - Percentage off (only if free unlock not used)
  3. Apply per-minute discount - Percentage off all time charges

Calculation:

If Free Unlock Used:
  Unlock Discount = Full Unlock Fee (100% covered)
Else:
  Unlock Discount = Unlock Fee × (unlock_discount_pct / 100)

Time Discount = Time Fee × (per_minute_discount_pct / 100)

Tier Discount Total = Unlock Discount + Time Discount
After Tier = Base Subtotal - Tier Discount Total

Example (Premium Tier):

Base Charges:
  Unlock:     $1.50
  Time:       $5.85
  Subtotal:   $7.35

Premium Tier Benefits (20% unlock, 15% time):
  Unlock Discount: $1.50 × 0.20 = $0.30
  Time Discount:   $5.85 × 0.15 = $0.88
  Total Discount:  $1.18

After Tier: $7.35 - $1.18 = $6.17

Free Unlock Example:

Base Charges:
  Unlock:     $1.50
  Time:       $5.85
  Subtotal:   $7.35

Premium Tier (Free Unlock Used):
  Unlock Discount: $1.50 (full amount)
  Time Discount:   $5.85 × 0.15 = $0.88
  Total Discount:  $2.38

After Tier: $7.35 - $2.38 = $4.97

Tier discounts are tracked separately for reporting and appear on customer receipts.

Stage 3: Subscription Consumption

Active subscriptions are consumed before ride packages:

Subscription Allowances:

  • Included unlocks (per day or total)
  • Ride minutes (per day or total)
  • Pause minutes (per day or total)
  • Ride distance (per day or total)

Limit Types:

TypeBehavior
daily_limitAllowances reset at midnight each day
whole_durationAllowances valid for entire subscription period

Matching:

  • Subaccount-specific subscriptions match first
  • Global subscriptions used as fallback
  • Oldest subscription consumed first

Stage 4: Ride Package Consumption

Remaining charges after subscriptions are covered by ride packages:

Package Contents:

  • Prepaid unlocks
  • Prepaid minutes
  • Prepaid pause minutes
  • Prepaid distance

Consumption Logic:

  1. Find all active package purchases for the customer
  2. Filter by subaccount (if package is location-specific)
  3. Sort by purchase date (oldest first)
  4. Consume allowances until exhausted or charges covered

Usage Tracking: Each consumption is logged with:

  • Ride UUID
  • Minutes used
  • Distance used
  • Unlock used (boolean)
  • Discount applied (cents)
  • Timestamp

Stage 5: Dynamic Pricing Adjustments

After packages, dynamic pricing rules are evaluated:

Rule Evaluation:

  1. Filter active rules for the vehicle model
  2. Check time windows (for time-based rules)
  3. Check weather conditions (for weather-based rules)
  4. Check demand thresholds (for demand-based rules)
  5. Sort by priority (highest first)
  6. Apply each matching rule sequentially

Adjustment Application:

For each applicable rule:
  If percentage: Subtotal = Subtotal × (1 + percent/100)
  If multiplier: Subtotal = Subtotal × multiplier
  Then: Subtotal = Subtotal + fixed_adjustment

Stage 6: Promo Code Application

If a promo code was provided:

Validation Checks:

  1. Code exists and is active
  2. Current time within valid_from and valid_until
  3. Global uses haven't exceeded max_uses
  4. Customer uses haven't exceeded max_uses_per_customer
  5. Subaccount matches (if scoped)
  6. Vehicle type matches (if scoped)
  7. Subtotal meets minimum ride amount

Discount Calculation:

If percentage:
  Discount = Subtotal × (percent / 100)
If fixed:
  Discount = Fixed Amount

If max_discount_cents set:
  Discount = MIN(Discount, max_discount_cents)

Final Discount = MIN(Discount, Subtotal)  // Can't exceed subtotal

Stage 7: Final Adjustments

The final stage applies protective limits:

Daily Cap Re-check: After all discounts and surcharges, the daily cap is verified:

If Final > Daily Cap:
  Final = Daily Cap

Minimum Price: Only applied when no subscription or package discounts were used:

If Final < Minimum AND no_benefits_used:
  Final = Minimum

Already Charged Subtraction: For rides with partial charges (e.g., hold amounts):

Amount Due = Final - Already Charged

The Complete Calculation Flow

Here's a step-by-step example with real numbers:

Scenario: Premium E-Bike, 25-minute ride during surge

Given:

  • Vehicle: Premium E-Bike
  • Base Pricing: $1.50 unlock, $0.49/min, $30 daily cap
  • Subscription: None
  • Package: "10-minute bundle" with 3 unlocks and 20 minutes remaining
  • Dynamic Rule: "Weekend Surge" +25% with $1.00 fixed
  • Promo Code: "RIDENOW" 20% off, max $2.00

Step 1: Base Charges

Unlock:     $1.50
Time:       25 × $0.49 = $12.25
Subtotal:   $13.75

Step 2: Tier Benefits

No tier benefits
Subtotal:   $13.75

Step 3: Subscription

No active subscription
Subtotal:   $13.75

Step 4: Package Consumption

Package covers: 1 unlock ($1.50) + 20 minutes ($9.80)
Package discount: $11.30
Remaining: 5 minutes × $0.49 = $2.45
Subtotal:   $2.45

Step 5: Dynamic Pricing

Weekend Surge (+25%): $2.45 × 1.25 = $3.06
Fixed surcharge: $3.06 + $1.00 = $4.06
Subtotal:   $4.06

Step 6: Promo Code

RIDENOW 20%: $4.06 × 0.20 = $0.81
Capped at $2.00: $0.81 (under cap)
Subtotal:   $4.06 - $0.81 = $3.25

Step 7: Final

Daily cap check: $3.25 < $30 ✓
Minimum check: Skipped (package used)
Final Amount Due: $3.25

Configuration Scenarios

Scenario 1: Base Pricing Only

Setup:

  • Vehicle: Standard Scooter
  • Unlock: $1.00
  • Per minute: $0.39
  • No packages, subscriptions, or promos

15-minute ride:

Unlock:     $1.00
Time:       15 × $0.39 = $5.85
Total:      $6.85

Scenario 2: Full Package Coverage

Setup:

  • Package: "15 Minute Boost" - 1 unlock, 20 minutes

18-minute ride:

Base:       $1.00 + (18 × $0.39) = $8.02
Package:    Covers unlock ($1.00) + 18 min ($7.02)
Discount:   $8.02
Amount Due: $0.00

All usage logged, 2 minutes remain in package.

Scenario 3: Surge + Promo Stack

Setup:

  • Premium Scooter: $1.50 unlock, $0.49/min
  • Dynamic: "Weekend Surge" +25% + $1.00 fixed
  • Promo: "RIDENOW" 20% off, capped at $2.00

25-minute ride:

Base:       $1.50 + (25 × $0.49) = $13.75
Surge 25%:  $13.75 × 1.25 = $17.19
Fixed:      $17.19 + $1.00 = $18.19
Promo 20%:  $18.19 × 0.20 = $3.64 → Capped at $2.00
Final:      $18.19 - $2.00 = $16.19

Frequently Asked Questions

Can packages and promo codes stack?

Yes. Packages are consumed first, reducing the subtotal. Then promo codes apply to the remaining balance. This means customers get the best possible discount.

What happens if I have multiple packages?

The system uses the oldest package first (FIFO - First In, First Out). Once a package is depleted, the next one is used.

Can I use both per-minute and per-distance pricing?

No, you must choose one or the other per vehicle model. The system uses whichever field has a value greater than zero.

How do I test pricing safely?

  1. Create a test subaccount with test pricing rules
  2. Use the pricing API tests: npm run test:pricing
  3. Review the detailed breakdown in test output
  4. Never test on production data

Why was minimum price not applied?

Minimum price is skipped when subscription or package discounts are used. Customers with paid benefits shouldn't be penalized with minimum charges.

How does the daily cap interact with packages?

Daily cap applies to the base charges before package discounts. If a rider has a package, the cap calculation still considers the full base charges, but the rider only pays the post-package amount.


Technical Reference

Core Functions

FunctionLocationPurpose
buildRidePricingsrc/lib/billing/ride-pricing.tsMaster orchestrator
calculateRideChargessrc/lib/billing/calculate-ride-charges.tsBase charge calculation
applySubscriptionsrc/lib/billing/ride-pricing.tsSubscription consumption
applyRidePackagesrc/lib/billing/ride-pricing.tsPackage consumption
applyDynamicPricingsrc/lib/billing/ride-pricing.tsDynamic rule evaluation
applyPromoCodesrc/lib/billing/ride-pricing.tsPromo validation & discount

Database Tables

TablePurpose
vehicle_pricingBase pricing rules per model
dynamic_pricing_rulesDynamic adjustment rules
dynamic_pricing_time_windowsTime windows for rules
subscription_packagesSubscription plan definitions
subscription_package_purchasesCustomer subscription instances
ride_pricing_packagesRide package definitions
ride_pricing_package_purchasesCustomer package instances
promo_codesPromo code definitions
customer_promo_usesUsage tracking per customer

Result Structure

The buildRidePricing function returns:

{
  base: {
    unlockFeeCents: number
    timeFeeCents: number
    pauseFeeCents: number
    distanceFeeCents: number
    subtotalCents: number
    dailyCapApplied: boolean
  }
  tier: {
    tierName: string | null
    unlockDiscountCents: number
    timeDiscountCents: number
    freeUnlockUsed: boolean
    totalDiscountCents: number
  } | null
  subscription: {
    discountCents: number
    purchaseId: string
    usageEvent: object
  } | null
  package: {
    discountCents: number
    purchaseId: string
    usageEvent: object
  } | null
  dynamic: {
    subtotalBefore: number
    finalSubtotal: number
    multiplier: number
    adjustmentCents: number
    appliedRules: array
  }
  promo: {
    discountCents: number
    code: string
    promoId: string
  } | null
  totals: {
    baseSubtotalCents: number
    tierDiscountCents: number
    subscriptionDiscountCents: number
    packageDiscountCents: number
    dynamicAdjustmentCents: number
    promoDiscountCents: number
    finalCents: number
    amountDueCents: number
  }
}

Need Help?

For pricing configuration assistance, contact support@levyelectric.com.