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:
- Verify each vehicle model exists for the active subaccount (location)
- Set a base pricing rule per model with unlock, pause, minimum, and daily cap values
- Create subscription plans riders can purchase from the mobile app or dashboard
- Publish ride packages that grant prepaid unlocks, minutes, or distance
- Layer dynamic pricing rules for time, weather, demand, or model-specific adjustments
- Issue promo codes and verify their scope (global, subaccount, vehicle type) and limits
- 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:
- Time fees reduced first
- Pause fees reduced second
- Distance fees reduced third
- 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:
| Benefit | Description | Example |
|---|---|---|
unlock_discount_pct | Percentage off unlock fee | 20% off $1.50 = $0.30 saved |
per_minute_discount_pct | Percentage off time charges | 15% off $5.00 = $0.75 saved |
free_unlocks_per_month | Waive unlock fee entirely | 5 free unlocks per month |
Application Order:
- Check free unlock - If customer requests and has remaining free unlocks
- Apply unlock discount - Percentage off (only if free unlock not used)
- 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:
| Type | Behavior |
|---|---|
daily_limit | Allowances reset at midnight each day |
whole_duration | Allowances 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:
- Find all active package purchases for the customer
- Filter by subaccount (if package is location-specific)
- Sort by purchase date (oldest first)
- 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:
- Filter active rules for the vehicle model
- Check time windows (for time-based rules)
- Check weather conditions (for weather-based rules)
- Check demand thresholds (for demand-based rules)
- Sort by priority (highest first)
- 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:
- Code exists and is active
- Current time within valid_from and valid_until
- Global uses haven't exceeded max_uses
- Customer uses haven't exceeded max_uses_per_customer
- Subaccount matches (if scoped)
- Vehicle type matches (if scoped)
- 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?
- Create a test subaccount with test pricing rules
- Use the pricing API tests:
npm run test:pricing - Review the detailed breakdown in test output
- 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
| Function | Location | Purpose |
|---|---|---|
buildRidePricing | src/lib/billing/ride-pricing.ts | Master orchestrator |
calculateRideCharges | src/lib/billing/calculate-ride-charges.ts | Base charge calculation |
applySubscription | src/lib/billing/ride-pricing.ts | Subscription consumption |
applyRidePackage | src/lib/billing/ride-pricing.ts | Package consumption |
applyDynamicPricing | src/lib/billing/ride-pricing.ts | Dynamic rule evaluation |
applyPromoCode | src/lib/billing/ride-pricing.ts | Promo validation & discount |
Database Tables
| Table | Purpose |
|---|---|
vehicle_pricing | Base pricing rules per model |
dynamic_pricing_rules | Dynamic adjustment rules |
dynamic_pricing_time_windows | Time windows for rules |
subscription_packages | Subscription plan definitions |
subscription_package_purchases | Customer subscription instances |
ride_pricing_packages | Ride package definitions |
ride_pricing_package_purchases | Customer package instances |
promo_codes | Promo code definitions |
customer_promo_uses | Usage 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.