Skip to main content

Overview

Viax implements a sophisticated dual-screen pricing system that allows administrators to configure tariffs by vehicle type, time periods, and apply dynamic pricing based on demand.
Pricing is configured per vehicle type with support for peak hours, nighttime, and holiday surcharges.

Pricing Database Schema

The pricing system uses dedicated database tables:

Configuration Table

migrations/007_create_configuracion_precios.sql
CREATE TABLE configuracion_precios (
  id INT AUTO_INCREMENT PRIMARY KEY,
  tipo_vehiculo ENUM('moto', 'carro', 'moto_carga', 'carro_carga') NOT NULL UNIQUE,
  tarifa_base DECIMAL(10, 2) NOT NULL DEFAULT 0,
  costo_por_km DECIMAL(10, 2) NOT NULL DEFAULT 0,
  costo_por_minuto DECIMAL(10, 2) NOT NULL DEFAULT 0,
  tarifa_minima DECIMAL(10, 2) NOT NULL DEFAULT 0,
  recargo_hora_pico DECIMAL(5, 2) DEFAULT 0,
  recargo_nocturno DECIMAL(5, 2) DEFAULT 0,
  recargo_festivo DECIMAL(5, 2) DEFAULT 0,
  descuento_distancia_mayor_15km DECIMAL(5, 2) DEFAULT 0,
  comision_plataforma DECIMAL(5, 2) DEFAULT 15.00,
  hora_pico_inicio_manana TIME DEFAULT '07:00:00',
  hora_pico_fin_manana TIME DEFAULT '09:00:00',
  hora_pico_inicio_tarde TIME DEFAULT '17:00:00',
  hora_pico_fin_tarde TIME DEFAULT '19:00:00',
  hora_nocturna_inicio TIME DEFAULT '22:00:00',
  hora_nocturna_fin TIME DEFAULT '06:00:00',
  activo BOOLEAN DEFAULT TRUE,
  notas TEXT,
  creado_en TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  actualizado_en TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);

Price History Table

CREATE TABLE historial_precios (
  id INT AUTO_INCREMENT PRIMARY KEY,
  configuracion_id INT NOT NULL,
  campo_modificado VARCHAR(100) NOT NULL,
  valor_anterior TEXT,
  valor_nuevo TEXT,
  modificado_por INT,
  fecha_modificacion TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  FOREIGN KEY (configuracion_id) REFERENCES configuracion_precios(id),
  FOREIGN KEY (modificado_por) REFERENCES usuarios(id)
);

Default Pricing Configuration

Vehicle Type: moto
ComponentValue
Base Fare$4,000
Per Kilometer$2,000
Per Minute$250
Minimum Fare$6,000
Peak Hour Surcharge15%
Night Surcharge20%
Holiday Surcharge25%
Platform Commission15%

Price Calculation Formula

The system uses a comprehensive formula to calculate trip costs:
Subtotal = Base Fare + (Distance × Cost/km) + (Duration × Cost/min)

Discount = If distance ≥ 15km → Subtotal × 10%

Surcharge = Subtotal × (Percentage based on time period)
  - Peak hours (7-9am, 5-7pm): +15-20%
  - Night (10pm-6am): +20-25%
  - Holidays: +25-30%

Total = Subtotal - Discount + Surcharge
Total = MAX(Total, Minimum Fare)

Platform Commission = Total × (Commission %)
Driver Earnings = Total - Platform Commission

Implementation Example

double calculateTripPrice({
  required double distanceKm,
  required int durationMinutes,
  required String vehicleType,
  required DateTime tripTime,
}) {
  final config = getPricingConfig(vehicleType);
  
  // Base calculation
  double subtotal = config.tarifaBase +
      (distanceKm * config.costoPorKm) +
      (durationMinutes * config.costoPorMinuto);
  
  // Distance discount
  double discount = 0;
  if (distanceKm >= 15) {
    discount = subtotal * (config.descuentoDistancia / 100);
  }
  
  // Time-based surcharge
  double surcharge = 0;
  final period = getCurrentPeriod(tripTime, config);
  
  if (period == 'hora_pico') {
    surcharge = subtotal * (config.recargoHoraPico / 100);
  } else if (period == 'nocturno') {
    surcharge = subtotal * (config.recargoNocturno / 100);
  } else if (isHoliday(tripTime)) {
    surcharge = subtotal * (config.recargoFestivo / 100);
  }
  
  // Final total
  double total = subtotal - discount + surcharge;
  total = max(total, config.tarifaMinima);
  
  return total;
}

Time Periods

Peak Hours (Hora Pico)

Morning Peak

Default: 7:00 AM - 9:00 AMSurcharge: 15%

Evening Peak

Default: 5:00 PM - 7:00 PMSurcharge: 15%

Night Hours (Nocturno)

Time Range: 10:00 PM - 6:00 AM Surcharge: 20%

Normal Hours

Time Range: All other hours Surcharge: 0%

Active Prices View

A database view provides quick access to current pricing with period detection:
CREATE VIEW vista_precios_activos AS
SELECT 
  cp.*,
  CASE
    WHEN CURRENT_TIME BETWEEN cp.hora_pico_inicio_manana AND cp.hora_pico_fin_manana THEN 'hora_pico'
    WHEN CURRENT_TIME BETWEEN cp.hora_pico_inicio_tarde AND cp.hora_pico_fin_tarde THEN 'hora_pico'
    WHEN CURRENT_TIME >= cp.hora_nocturna_inicio OR CURRENT_TIME <= cp.hora_nocturna_fin THEN 'nocturno'
    ELSE 'normal'
  END AS periodo_actual,
  CASE
    WHEN CURRENT_TIME BETWEEN cp.hora_pico_inicio_manana AND cp.hora_pico_fin_manana THEN cp.recargo_hora_pico
    WHEN CURRENT_TIME BETWEEN cp.hora_pico_inicio_tarde AND cp.hora_pico_fin_tarde THEN cp.recargo_hora_pico
    WHEN CURRENT_TIME >= cp.hora_nocturna_inicio OR CURRENT_TIME <= cp.hora_nocturna_fin THEN cp.recargo_nocturno
    ELSE 0
  END AS recargo_actual
FROM configuracion_precios cp
WHERE cp.activo = TRUE;

Backend API Endpoints

Get Pricing Configuration

backend/pricing/get_config.php
// GET /pricing/get_config.php?tipo_vehiculo=moto

{
  "success": true,
  "data": {
    "tipo_vehiculo": "moto",
    "tarifa_base": 4000.00,
    "costo_por_km": 2000.00,
    "costo_por_minuto": 250.00,
    "tarifa_minima": 6000.00,
    "recargo_hora_pico": 15.00,
    "recargo_nocturno": 20.00,
    "recargo_festivo": 25.00,
    "periodo_actual": "hora_pico",
    "recargo_actual": 15.00
  }
}

Calculate Trip Quote

backend/pricing/calculate_quote.php
// POST /pricing/calculate_quote.php

{
  "distancia_km": 8.5,
  "duracion_minutos": 25,
  "tipo_vehiculo": "moto"
}

// Response:
{
  "success": true,
  "data": {
    "distancia_km": 8.5,
    "duracion_minutos": 25,
    "tipo_vehiculo": "moto",
    "tarifa_base": 4000,
    "precio_distancia": 17000,
    "precio_tiempo": 6250,
    "subtotal": 27250,
    "descuento_distancia": 0,
    "recargo_porcentaje": 15,
    "recargo_precio": 4087.5,
    "total": 31337.5,
    "total_formateado": "$31.338",
    "periodo_actual": "hora_pico",
    "comision_plataforma": 4700.63,
    "ganancia_conductor": 26636.87
  }
}

Updating Pricing Configuration

1

Access Database

Connect to the MySQL database as admin
2

Update Configuration

UPDATE configuracion_precios 
SET costo_por_km = 2200.00,
    notas = 'Ajuste por inflación - Octubre 2025'
WHERE tipo_vehiculo = 'moto';
3

Verify Changes

SELECT * FROM vista_precios_activos 
WHERE tipo_vehiculo = 'moto';
4

Check History

Changes are automatically logged in historial_precios
Pricing changes take effect immediately for new trip requests. Active trips use the pricing from when they were created.

Commission Distribution

Platform commission is calculated per trip:
class TripEarnings {
  final double tripTotal;
  final double platformCommissionPercentage;
  
  double get platformEarnings {
    return tripTotal * (platformCommissionPercentage / 100);
  }
  
  double get driverEarnings {
    return tripTotal - platformEarnings;
  }
  
  double get companyCommission {
    // If driver belongs to a company, company may take additional %
    final companyRate = driver.company?.commissionRate ?? 0;
    return driverEarnings * (companyRate / 100);
  }
  
  double get driverFinalEarnings {
    return driverEarnings - companyCommission;
  }
}

Example Breakdown

Trip Total: $50,000 COP Platform Commission (15%): $7,500 COP Driver Base Earnings: $42,500 COP Company Commission (10%): $4,250 COP Driver Final Earnings: $38,250 COP

Distance Discount

Long-distance trips receive automatic discounts:
if (distanceKm >= 15.0) {
  final discountPercentage = 10.0;
  discount = subtotal * (discountPercentage / 100);
}
Trips over 15km automatically receive a 10% discount to encourage long-distance rides.

Holiday Pricing

Implement holiday detection for special rates:
bool isHoliday(DateTime date) {
  final holidays = [
    DateTime(date.year, 1, 1),   // New Year
    DateTime(date.year, 5, 1),   // Labor Day
    DateTime(date.year, 7, 20),  // Independence Day
    DateTime(date.year, 8, 7),   // Battle of Boyacá
    DateTime(date.year, 12, 25), // Christmas
    // Add more holidays
  ];
  
  return holidays.any((holiday) => 
    date.year == holiday.year &&
    date.month == holiday.month &&
    date.day == holiday.day
  );
}
Holiday surcharges can be configured per vehicle type and override other time-based surcharges.

Testing Pricing Configuration

Verify Period Detection

SELECT tipo_vehiculo, periodo_actual, recargo_actual 
FROM vista_precios_activos;

Simulate Price Calculation

-- Test pricing for 10km, 20min motorcycle trip
SELECT 
  tarifa_base + (10 * costo_por_km) + (20 * costo_por_minuto) AS subtotal,
  periodo_actual,
  recargo_actual
FROM vista_precios_activos
WHERE tipo_vehiculo = 'moto';

Change Time Periods for Testing

UPDATE configuracion_precios 
SET hora_pico_inicio_manana = '14:00:00',
    hora_pico_fin_manana = '16:00:00'
WHERE tipo_vehiculo = 'moto';

Price Display to Users

Users see estimated pricing before requesting trips:
1

Enter Route

User selects origin and destination
2

Select Vehicle Type

User chooses moto, carro, etc.
3

View Quote

System calculates and displays:
  • Base fare
  • Distance cost
  • Time cost
  • Active surcharges
  • Total estimated price
4

Confirm Request

User accepts price and requests trip

Future Enhancements

Dynamic Pricing

Adjust prices based on real-time demand

Zone-Based Pricing

Different rates for different geographic areas

Promotional Codes

Discount codes and promotional campaigns

Membership Tiers

Discounted rates for premium members

Best Practices

Always test pricing changes with the calculate_quote endpoint before deploying to production.
Use the ‘notas’ field to document why prices were changed and when.
Regularly compare your rates with competitors to stay competitive.
Ensure drivers earn fair wages after platform and company commissions.
Notify drivers and users before implementing significant price changes.

Vehicle Types

Manage vehicle categories

Company Management

Configure company commissions

Service Areas

Define service boundaries

Build docs developers (and LLMs) love