F&I Products Pricing Documentation
#Complete Guide to Finance & Insurance Product Pricing Flow
#Table of Contents
- Overview
- Product Categories & Pricing Strategies
- Payment Selection & Product Availability
- Architecture Overview
- Frontend Flow (driveway-ui)
- Backend Flow (products-api)
- The FI-Rate Cache Collection
- Quote Submission Flow
- External Services & Integrations
- Pricing Configuration
- API Reference
- Troubleshooting Guide
#1. Overview
The F&I (Finance & Insurance) Products system provides vehicle protection plans and services to Driveway customers. The pricing architecture uses a hybrid approach:
- Static Pricing: Some products have fixed "Starting at" prices displayed immediately
- Dynamic Pricing: Other products require a quote request based on vehicle-specific factors
- Cached Pricing: PEN (ProvenNet) products are cached with pre-calculated markups
#Key Components
+------------------+ +------------------+ +------------------+| | | | | || driveway-ui | -----> | products-api | -----> | PEN API || (Frontend) | | (Backend) | | (Rate Provider) || | | | | |+------------------+ +------------------+ +------------------+| || v| +------------------+| | |+----------------> | dealer-lead-api |(Quote Submit) | (FNI Leads) || |+------------------+
#2. Product Categories & Pricing Strategies
#Products Overview
| Product | Display Price | Pricing Type | Source |
|---|---|---|---|
| Lifetime Oil & Filter | "Starting at $799" | Static | MongoDB (fi-product-v2) |
| Valet Service | "Starting at $599" | Static | MongoDB (fi-product-v2) |
| Vehicle Service Contract | "Price on request" | Dynamic | PEN API + Markup |
| Appearance Protection | "Price on request" | Dynamic | Quote Required |
| Pre-Paid Maintenance | "Price on request" | Dynamic | Quote Required |
#Pricing Strategy Breakdown
Static Pricing (Lithia Products)
Products with known, fixed pricing stored directly in MongoDB:
Lifetime Oil & Filterโโโ Collection: fi-product-v2โโโ Price: Stored as totalInCentsโโโ Display: "Starting at $799"โโโ Filtering: State, vehicle make, fuel type restrictionsValet Serviceโโโ Collection: fi-product-v2โโโ Price: Stored as totalInCentsโโโ Display: "Starting at $599"โโโ Filtering: Zip code availability check
Dynamic Pricing (PEN Products)
Products requiring real-time or cached rate lookups:
Vehicle Service Contract (WARRANTY)โโโ Source: PEN APIโโโ Calculation: dealerCost + $1,500 markup (non-FL)โโโ Florida: Uses PEN retail price (no markup)โโโ Cache: fi-rate collectionTire & Wheelโโโ Source: PEN APIโโโ Calculation: dealerCost ร 2.2 multiplier (non-FL)โโโ Florida: Uses PEN retail priceโโโ Cache: fi-rate collectionVehicle Loss Protectionโโโ Source: PEN API (availability check only)โโโ Calculation: Fixed $1,095 (config-based)โโโ Availability: FINANCE payment type onlyโโโ Cache: fi-rate collection
#3. Payment Selection & Product Availability
#What is paymentSelection?
paymentSelection refers to HOW THE CUSTOMER IS PAYING FOR THE CAR, not how they're paying for the F&I products.
It determines which F&I protection products should be offered based on the vehicle purchase/payment method.
#The Three Payment Types
| Payment Type | Definition | Business Context |
|---|---|---|
| CASH | Customer pays full price upfront for the car | No loan - customer owns vehicle outright immediately |
| FINANCE | Customer takes out a loan to buy the car | Has monthly payments, owes money on the vehicle |
| LEASE | Customer rents the vehicle from the leasing company | Doesn't own the car, returns it at end of lease term |
#Product Availability Matrix
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ PRODUCT AVAILABILITY BY PAYMENT SELECTION โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโฌโโโโโโโโโโฌโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโคโ Product โ CASH โ FINANCE โ LEASE โ Not Selected โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโผโโโโโโโโโโผโโโโโโโโผโโโโโโโโโโโโโโโโโโโโโคโ Lifetime Oil & Filter โ โ โ โ โ โ โ โ โโ Driveway Valet Service โ โ โ โ โ โ โ โ โโ Vehicle Service Contract โ โ โ โ โ โ โ โ โโ Tire & Wheel Coverage โ โ โ โ โ โ โ โ โโ Vehicle Loss Protection โ โ โ โ โ โ โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโดโโโโโโโโโดโโโโโโโโโโดโโโโโโโโดโโโโโโโโโโโโโโโโโโโโโ
#Backend Filtering Logic
File: Products-Api/library/src/main/kotlin/com/detroitlabs/products/services/fi/FIService.kt
val productTypes = when (paymentSelection) {CartPaymentSelection.CASH -> listOf(FIProduct.Type.LIFETIME_OIL,FIProduct.Type.DRIVEWAY_VALET_SERVICE,FIProduct.Type.WARRANTY,FIProduct.Type.TIRE_WHEEL// Note: NO Vehicle Loss Protection for CASH)CartPaymentSelection.FINANCE, null -> listOf(FIProduct.Type.LIFETIME_OIL,FIProduct.Type.DRIVEWAY_VALET_SERVICE,FIProduct.Type.WARRANTY,FIProduct.Type.TIRE_WHEEL,FIProduct.Type.VEHICLE_LOSS_PROTECTION // ONLY available for FINANCE)CartPaymentSelection.LEASE -> listOf(FIProduct.Type.TIRE_WHEEL // ONLY Tire & Wheel for LEASE)}
#Business Logic Behind Each Restriction
Vehicle Loss Protection (VLP) - FINANCE ONLY
VLP is exclusively available for FINANCE because:
- Purpose: Covers the "gap" between insurance payout and loan balance if the car is totaled
- CASH buyers: Own the car outright - no loan balance to protect, insurance covers full value
- LEASE customers: The leasing company carries gap protection through their own policies
- FINANCE customers: Have an active loan - if car is totaled, insurance may pay less than what's owed
Example Scenario:โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ Car Value: $30,000 โโ Loan Balance: $28,000 โโ Car is totaled in accident โโ Insurance pays: $25,000 (depreciated value) โโ โโ WITHOUT VLP: Customer owes $3,000 on a car they can't drive โโ WITH VLP: Gap coverage pays the $3,000 difference โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
LEASE - Only Tire & Wheel Available
Lease customers only see Tire & Wheel because:
- Maintenance Coverage: Leases often include maintenance packages from the lessor
- Ownership: Customer doesn't own the vehicle - limited modifications/additions
- Return Condition: Lease agreements specify return condition requirements
- Tire & Wheel Universal: Protects against road hazards regardless of ownership type
CASH - All Products Except VLP
Cash buyers can purchase all products except VLP because:
- Full Ownership: They own the car and can add any protection they want
- No Gap to Protect: Without a loan, there's no "gap" between value and owed amount
- Lifetime Products: LOF and other protection products work regardless of payment method
#Enum Definition
File: Products-Api/library/src/main/kotlin/com/detroitlabs/products/models/CartPaymentSelection.kt
enum class CartPaymentSelection {CASH,FINANCE,LEASE}
#API Parameter
The paymentSelection is passed as a query parameter to the estimator endpoint:
GET /estimator/fi-products?vehicleId=123&userZip=97201&paymentSelection=FINANCE
When paymentSelection is:
- Not provided (null): Defaults to FINANCE behavior (all products returned)
- CASH: Returns all products except VLP
- FINANCE: Returns all products including VLP
- LEASE: Returns only Tire & Wheel
#4. Architecture Overview
#System Architecture Diagram
FRONTEND (driveway-ui)โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โโ Care & Protection Page โโ โโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโ โโ โ Product Cards โ โ Get Quote Modal โ โ Quote Form โ โโ โ - Static prices โ โ - Product info โ โ - Mileage โ โโ โ - "Get a Quote" โ โ - Benefits โ โ - Contact info โ โโ โโโโโโโโโโฌโโโโโโโโโ โโโโโโโโโโฌโโโโโโโโโ โ - Address โ โโ โ โ โโโโโโโโโโฌโโโโโโโโโ โโ โ โ โ โโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโโโโโโ โ โv v vโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ API CALLS โโ โโ GET /estimator/fi-products POST /fni-leads โโ โโโ vehicleId โโโ address โโ โโโ userZip โโโ dealershipId โโ โโโ userState โโโ contact info โโ โโโ vehicle info โโ โโโ productType[] โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โv vโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ PRODUCTS-API โ โ DEALER-LEAD-API โโ โ โ โโ โโโโโโโโโโโโโโโโโโโโโโโโโ โ โ Receives quote requests and โโ โ FI Service โ โ โ routes them to dealerships โโ โ - Cache lookup โ โ โ โโ โ - Price calculation โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ - Markup application โ โโ โโโโโโโโโโโโโฌโโโโโโโโโโโโ โโ โ โโ โโโโโโโโโโโโvโโโโโโโโโโโโ โโ โ MongoDB โ โโ โ - fi-rate (cache) โ โโ โ - fi-product-v2 โ โโ โ - pen-dealers โ โโ โโโโโโโโโโโโโฌโโโโโโโโโโโโ โโ โ โโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโv (cache miss only)โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ PEN API โโ (ProvenNet SOAP Service) โโ โโ - getASBRates() โโ - getRateBookCodes() โโ - Dealer cost + retail โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
#5. Frontend Flow (driveway-ui)
#5.1 Care and Protection Page
Location: UI/src/pages/mydriveway/care-and-protection.tsx
The page displays all F&I products using QuickActionCard components.
// Product cards displayed[{ name: "Lifetime Oil & Filter", price: "Starting at $799", type: "LOF" },{ name: "Valet Service", price: "Starting at $599", type: "VALET" },{ name: "Vehicle Service Contract", price: "Price on request", type: "VSC" },{ name: "Appearance Protection", price: "Price on request", type: "APPEARANCE" },{ name: "Pre-Paid Maintenance", price: "Price on request", type: "PPM" }]
#5.2 Get Quote Modal Flow
Component: UI/src/account/components/MyVehicles/GetQuoteModal/GetQuoteWrapper.tsx
Step 1: PRODUCT_INFOโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ VEHICLE SERVICE CONTRACT โโ โโ We'll cover repairs big and small... โโ โโ What's included? โโ โ Covers qualifying repairs โโ โ Emergency roadside assistance โโ โ Rental car reimbursement โโ โโ Terms: โโ โ Coverage up to 96 months โโ โ A small deductible may apply โโ โโ [Get a Quote] โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโvStep 2: PICK_VEHICLE (if multiple vehicles)โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ Select a Vehicle โโ โโ โ 2022 Toyota Camry โโ โ 2021 Honda Accord โโ โโ [Continue] โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโvStep 3: QUOTE_FORMโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ What's your current mileage? โโ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โโ โ 12,000 โ โโ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โโ โโ How can we reach you? โโ First Name: [Cristian ] โโ Last Name: [Torres ] โโ Email: [email@example.com] โโ Phone: [(323) 799-2985 ] โโ โโ What's your current address? โโ Address: [222 East 41st Street] โโ City: [New York ] โโ State: [NY] Zip: [10017] โโ โโ [Continue] โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโvStep 4: SERVICE_CENTERโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ Select a Service Center โโ โโ โ Driveway Portland โโ โ Driveway Seattle โโ โโ [Continue] โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโvStep 5: SUBMIT_QUOTEโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ Review Your Request โโ โโ Product: Vehicle Service Contract โโ Vehicle: 2022 Toyota Camry โโ Mileage: 12,000 โโ Service Center: Driveway Portland โโ โโ [Submit Request] โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
#5.3 Form Validation Rules
File: UI/src/account/components/MyVehicles/GetQuoteModal/sub-components/QuoteForm/useGetQuoteForm.tsx
| Field | Validation Rules |
|---|---|
| Current Mileage | Positive number, required |
| First Name | Alphanumeric, max 15 chars, required |
| Last Name | Alphanumeric, max 25 chars, required |
| Valid email format, required | |
| Phone | Exactly 10 digits, required |
| Address Line 1 | Alphanumeric, max 100 chars, required |
| Address Line 2 | Alphanumeric, optional |
| City | Alphabetic only, max 20 chars, required |
| State | Exactly 2 letters, required |
| Zip Code | Exactly 5 numeric digits, required |
#5.4 API Endpoints Used
File: UI/src/apis/endpoints.ts
// Products API - Get estimated pricesProductsEndpoints.ESTIMATED_PRODUCTS = "/estimator/fi-products"// Dealer Lead API - Submit quote requestDealerLeadEndpoints.FNI_LEADS = "/fni-leads"
#6. Backend Flow (products-api)
#6.1 Estimator Endpoint
Route: GET /estimator/fi-products
File: Products-Api/routes/src/main/kotlin/com/detroitlabs/products/routes/FIRoutes.kt
Request Flow:โโโโโโโโโโโโโโโโโโโ GET /estimator โโ /fi-products โโ โโ ?vehicleId=X โโ &userZip=Y โโ &userState=Z โโโโโโโโโโฌโโโโโโโโโโvโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ FIHandler โโ โโ 1. Parse request parameters โโ 2. If no userState, call Geocoding API with zip โโ 3. Call FIService.getEstimatedFIProducts() โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโvโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ FIService โโ โโ For Lithia Products (LOF, Valet): โโ โโโ Query fi-product-v2 collection โโ โโโ Filter by state, make, fuel type, zip โโ โโโ Return totalInCents directly โโ โโ For PEN Products (VSC, T&W, VLP): โโ โโโ Check fi-rate cache by VIN โโ โโโ If cache hit & not expired (< 60 days): โโ โ โโโ Return cached product with markup applied โโ โโโ If cache miss or expired: โโ โ โโโ Estimator mode: Return empty (no PEN call) โโ โ โโโ Non-estimate: Call PEN API โโ โโโ Apply markup configuration to dealer cost โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโvโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ Response โโ โโ [ โโ { โโ "type": "LIFETIME_OIL", โโ "totalInCents": 79900, โโ "termMonths": "lifetime", โโ "deductible": null โโ }, โโ { โโ "type": "VEHICLE_SERVICE_CONTRACT", โโ "totalInCents": 259500, โโ "termMonths": "96", โโ "termMiles": "120000", โโ "deductible": 100 โโ } โโ ] โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
#6.2 Price Calculation Logic
File: Products-Api/library/src/main/kotlin/com/detroitlabs/products/services/fi/FIService.kt
Vehicle Service Contract (WARRANTY)
fun getVehicleServiceContractPenProduct(vehicle: Vehicle): PenFiProduct {val cachedProduct = getCachedProduct(vehicle.vin)if (vehicle.dealershipState in retailPriceOverrideForStates) { // Floridareturn PenFiProduct(totalInCents = cachedProduct.retailPriceInCents // Use PEN retail)} else {val markupAmount = config.vehicleServiceContract.markupAmount // $1,500return PenFiProduct(totalInCents = cachedProduct.dealerCostInCents + markupAmount)}}
Tire & Wheel
fun getTireAndWheelsPenProduct(vehicle: Vehicle): PenFiProduct {val cachedProduct = getCachedProduct(vehicle.vin)if (vehicle.dealershipState in retailPriceOverrideForStates) { // Floridareturn PenFiProduct(totalInCents = cachedProduct.retailPriceInCents // Use PEN retail)} else {val multiplier = config.tireAndWheel.markupMultiplier // 2.2xreturn PenFiProduct(totalInCents = (cachedProduct.dealerCostInCents * multiplier).toInt())}}
Vehicle Loss Protection
fun getVehicleLossProtectionPenProduct(vehicle: Vehicle): PenFiProduct {val cachedProduct = getCachedProduct(vehicle.vin)if (cachedProduct.rateExist) {val fixedAmount = config.vehicleLossProtection.fixedAmount // $1,095return PenFiProduct(totalInCents = fixedAmount // Ignores PEN price, uses config)} else {return PenFiProduct(rateExist = false)}}
#7. The FI-Rate Cache Collection
#7.1 Collection Details
Database: products
Collection: fi-rate
Document Type: CachedFIProducts
File: Products-Api/library/src/main/kotlin/com/detroitlabs/products/models/database/CachedFIProducts.kt
#7.2 Document Schema
@Document("fi-rate")data class CachedFIProducts(@Idval id: String,@Indexed(unique = true)val vin: String,val status: Status, // ACTIVE, DELETED, UPDATE_ERRORval vehicleId: String?,val priceInCents: Int?,val mileage: Int,val year: Int?,val make: String,val model: String?,val dealerState: String,// Cache validityval effectiveDate: Instant, // Cache expiry check (>= 60 days = expired)val nextRefreshDate: Instant?, // Cron scheduler reference// Cached PEN product rates (with markup already applied)val vehicleServiceContractRate: PenFiProduct?,val tireAndWheelRate: PenFiProduct?,val vehicleLossProtectionRate: PenFiProduct?,// Error trackingval updateFailureCount: Int,// Audit timestampsval createdOn: Instant,val updatedOn: Instant?)
#7.3 Cache Behavior Matrix
| Scenario | Cache Action | PEN Call? | Result |
|---|---|---|---|
| Cache hit, not expired (< 60 days) | Read | No | Return cached product |
| Cache hit, expired (>= 60 days) | Skip | Estimator: No | Return empty |
| Cache miss | Skip | Estimator: No | Return empty |
| Inventory ADD event | Create | Yes | New cache doc |
| Inventory UPDATE event | Update | Yes | Refresh cache |
| Cron refresh (due) | Update | Yes | Update rates only |
#7.4 Cache Population Sources
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ CACHE POPULATION โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ โv v vโโโโโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโโโ Inventory Delta โ โ FI Rate Cron โ โ Manual Refresh โโ Receiver โ โ โ โ โโ โ โ Every 5 minutes โ โ Admin endpoints โโ Azure Service Bus โ โ Batch: 100 vehiclesโ โ /test/fi-Rate/* โโ - ADD: Create doc โ โ Refresh oldest due โ โ - expire-products โโ - UPDATE: Refresh โ โ first โ โ - update-markup โโ - DELETE: Soft del โ โ โ โ - refresh โโโโโโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโโ
#8. Quote Submission Flow
#8.1 Complete Quote Journey
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ USER JOURNEY โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโStep 1: Browse Productsโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ User visits Care & Protection page โโ โ โโ System displays product cards with: โโ - Static prices for LOF ($799) and Valet ($599) โโ - "Price on request" for VSC, Appearance, PPM โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโvStep 2: Select Productโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ User clicks "Get a Quote" on Vehicle Service Contract โโ โ โโ ProductInfoModal opens showing: โโ - Product description โโ - What's included (coverage details) โโ - Terms and conditions โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโvStep 3: Vehicle Selectionโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ System checks user's garage โโ โ โโ IF multiple vehicles: Show PickVehicle modal โโ IF no vehicles: Show AddVehicle modal โโ IF single vehicle: Auto-select and proceed โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโvStep 4: Quote Formโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ QuoteForm modal collects: โโ โโ โโโโโโโโโโโโโโโโโโโโโโโ โโ โ Current Mileage โ โ Vehicle-specific pricing factor โโ โโโโโโโโโโโโโโโโโโโโโโโ โโ โโ โโโโโโโโโโโโโโโโโโโโโโโ โโ โ Contact Information โ โ For quote follow-up โโ โ - First/Last Name โ โโ โ - Email โ โโ โ - Phone โ โโ โโโโโโโโโโโโโโโโโโโโโโโ โโ โโ โโโโโโโโโโโโโโโโโโโโโโโ โโ โ Address โ โ For state-based pricing/availability โโ โ - Street โ โโ โ - City/State/Zip โ โโ โโโโโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโvStep 5: Service Center Selectionโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ ServiceCenterStep shows nearby dealerships โโ โ โโ User selects preferred service location โโ This determines dealershipId for the lead โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโvStep 6: Submit Quote Requestโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ SubmitQuote modal shows summary โโ โ โโ User clicks "Submit Request" โโ โ โโ POST /fni-leads with payload: โโ { โโ address: { line1, line2, city, state, zip }, โโ dealershipId: 12345, โโ email: "user@email.com", โโ firstName: "Cristian", โโ lastName: "Torres", โโ phone: "3237992985", โโ vehicle: { make, model, modelYear, vin, mileage }, โโ productType: ["VSC"] โโ } โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโvStep 7: Lead Processingโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ dealer-lead-api receives the request โโ โ โโ Lead is created and routed to the selected dealership โโ โ โโ Dealership F&I team receives notification โโ โ โโ Team contacts customer with personalized quote โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
#8.2 Quote Submission Payload
Endpoint: POST /fni-leads
Service: dealer-lead-api
interface LeadsData {address: {addressLine1: string;addressLine2?: string;city: string;state: string;zipCode: string;};dealershipId: number;email: string;firstName: string;lastName: string;lithiaId?: string;phone: string;vehicle: {make: string;mileage: number;model: string;modelYear: number;vin: string;};productType: ProductType[]; // ["LOF", "VSC", "VALET", "APPEARANCE", "PPM"]}
#9. External Services & Integrations
#9.1 PEN API (ProvenNet)
Purpose: Source of truth for F&I product dealer costs and retail prices
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ PEN API Integration โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโคโ โโ Protocol: SOAP/XML โโ Auth: Basic Authentication (PEN_USER / PEN_PASSWORD) โโ Timeout: 60 seconds โโ Rate Limit: 1 call per 50ms (Resilience4j) โโ โโ Key Operations: โโ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โโ โ getRateBookCodes() โ โโ โ - Fetches daily rate codes for each product type โ โโ โ - Called during cache refresh โ โโ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โโ โโ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โโ โ getASBRates(vehicle, rateBookCode) โ โโ โ - Returns dealer cost and retail price โ โโ โ - Used for VSC, T&W, VLP products โ โโ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โโ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
#9.2 Google Geocoding API
Purpose: Resolve state from zip code when not provided
// Used in FIHandler when userState is missingval address = geocodingService.getEstimatedAddressFromPostalCode(userZip)val state = address.state
#9.3 Azure Service Bus
Purpose: Receive vehicle inventory delta messages
Azure Service Bus Topicโvโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ VehicleInventoryTopicReceiver โโ โโ Message Actions: โโ - ADD: Create new fi-rate cache doc โโ - UPDATE: Refresh rates if changed โโ - DELETE: Soft delete cache doc โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
#9.4 Odyssey GraphQL
Purpose: Vehicle inventory queries
query GetVehicleById($id: ID!) {vehicle(id: $id) {vinmakemodelyearmileagedealershipState}}
#10. Pricing Configuration
#10.1 Configuration File
Location: Products-Api/library/src/main/resources/application.yml
products:cost:vehicle-service-contract:markup-amount: 150000 # $1,500 in centsvehicle-loss-protection:fixed-amount: 109500 # $1,095 in centstire-and-wheel:markup-multiplier: 2.2 # 2.2x dealer costretail-price-override-states:- FL # Florida uses PEN retail prices
#10.2 Updating Prices
Option A: Configuration Update (Recommended)
- Modify
application.ymlwith new values - Restart Products Cron service
- Call
GET /test/fi-Rate/update-cached-markup - All cached prices immediately reflect new markup
Option B: Wait for Natural Refresh
- Modify
application.yml - Restart services
- Wait for cron to refresh (~100 vehicles every 5 minutes)
- New vehicles get new prices on ADD event
#10.3 Florida-Specific Pricing
Florida uses PEN retail prices instead of markup-based calculations:
| Product | Non-Florida | Florida |
|---|---|---|
| VSC | dealerCost + $1,500 | PEN retail price |
| Tire & Wheel | dealerCost ร 2.2 | PEN retail price |
| VLP | Fixed $1,095 | Fixed $1,095 |
#11. API Reference
#11.1 Products API Endpoints
Get Estimated F&I Products
GET /estimator/fi-products
Query Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
| vehicleId | string | Yes | Odyssey vehicle ID |
| userZip | string | Yes | User's zip code |
| userState | string | No | User's state (derived from zip if missing) |
| paymentSelection | enum | No | CASH, FINANCE, LEASE, or null |
Response:
[{"type": "LIFETIME_OIL","totalInCents": 79900,"termMonths": "lifetime","termMiles": null,"deductible": null},{"type": "VEHICLE_SERVICE_CONTRACT","totalInCents": 259500,"termMonths": "96","termMiles": "120000","deductible": 100}]
#11.2 Admin Endpoints (Cron App)
Trigger FI Rate Refresh
GET /test/fi-Rate/refresh
Manually triggers one batch of the FI Rate Refresh cron.
Update Cached Markup
GET /test/fi-Rate/update-cached-markup
Reapplies current configuration markup to ALL cached documents.
Expire Products by State
POST /test/fi-Rate/expire-productsContent-Type: application/json{"dealerState": "FL"}
Sets effectiveDate = 90 days ago for all documents in the specified state.
#11.3 Dealer Lead API Endpoints
Submit F&I Quote Lead
POST /fni-leadsContent-Type: application/jsonAuthorization: Bearer {token}{"address": {"addressLine1": "123 Main St","city": "Portland","state": "OR","zipCode": "97201"},"dealershipId": 12345,"email": "customer@email.com","firstName": "John","lastName": "Doe","phone": "5035551234","vehicle": {"make": "Toyota","model": "Camry","modelYear": 2022,"vin": "1HGBH41JXMN109186","mileage": 12000},"productType": ["VSC"]}
Response:
{"message": "Lead created successfully"}
#12. Troubleshooting Guide
#Common Issues
Issue: Vehicle shows no PEN products
Cause: Cache miss - vehicle not yet processed by inventory-delta or cron
Solution:
- Check if vehicle exists in
fi-ratecollection - If missing, trigger manual refresh:
GET /test/fi-Rate/refresh - Or wait for next inventory-delta message
Issue: Price increase not reflecting
Cause: Config changes only apply on next cache write
Solution:
- Verify config was updated in
application.yml - Confirm cron app was restarted
- Call
GET /test/fi-Rate/update-cached-markupfor immediate application
Issue: Florida prices different than expected
Cause: FL uses PEN retail price override, not markup config
Solution:
- VRC/T&W prices cannot be changed via config for FL
- Would require PEN to update their prices OR code logic change
Issue: Cache showing expired data
Cause: effectiveDate > 60 days old
Solution:
- Check document's
effectiveDatein MongoDB - If expired, estimator returns empty (by design)
- Use
POST /test/fi-Rate/expire-productsto force expiration - Run refresh to repopulate
#Monitoring Queries
// Find all expired cache documentsdb.getCollection('fi-rate').find({status: 'ACTIVE',effectiveDate: { $lt: new Date(Date.now() - 60 * 24 * 60 * 60 * 1000) }})// Find documents with errorsdb.getCollection('fi-rate').find({status: 'UPDATE_ERROR'})// Count documents by statedb.getCollection('fi-rate').aggregate([{ $group: { _id: '$dealerState', count: { $sum: 1 } } },{ $sort: { count: -1 } }])
#Summary
The F&I Products pricing system uses a sophisticated multi-tier approach:
- Static Products (LOF, Valet): Fixed prices stored in MongoDB, displayed immediately
- Dynamic Products (VSC, T&W, VLP): Cached PEN rates with configurable markup
- Quote-Required Products (Appearance, PPM): Require manual quote through dealer-lead-api
The fi-rate cache collection serves as the performance optimization layer, storing pre-calculated prices with markup already applied. This allows the frontend to display accurate pricing without making expensive PEN API calls on every request.
For products requiring personalized quotes, the system collects vehicle details, mileage, and contact information, then routes the lead to the appropriate dealership's F&I team for follow-up.
Last Updated: February 2026 Maintained by: Driveway Engineering Team