Skip to main content

๐Ÿ’ฐ OneBalance Module

๐Ÿ“– Overviewโ€‹

The OneBalance module is DashClicks' unified credit and usage tracking system, managing consumption-based billing for platform services like InstaSites, InstaReports, SMS, phone numbers, and API calls. It provides real-time balance tracking, automatic reloading, tiered pricing, and sub-account rebilling with custom markup.

File Path: internal/api/v1/onebalance/

Key Capabilitiesโ€‹

  • Unified Balance: Single credit balance for all platform services
  • Auto-Reload: Automatic balance replenishment when threshold reached
  • Usage Tracking: Real-time consumption logging per service
  • Tiered Pricing: Dynamic pricing based on account tier and volume
  • Sub-Account Rebilling: Parent accounts can markup services for sub-accounts
  • Credit Limits: Per-service credit allocations (e.g., 1000 InstaSite credits/month)
  • Billing Cycle Tracking: Monthly credit resets tied to subscription cycles
  • Currency Conversion: Multi-currency support with real-time exchange rates

๏ฟฝ Directory Structureโ€‹

onebalance/
โ”œโ”€โ”€ ๐Ÿ“„ index.js - Module router
โ”œโ”€โ”€ ๐Ÿ“„ README.md - Basic API documentation
โ”œโ”€โ”€ ๐Ÿ“‚ controllers/
โ”‚ โ””โ”€โ”€ onebalance.js - Request handlers
โ”œโ”€โ”€ ๐Ÿ“‚ services/
โ”‚ โ”œโ”€โ”€ onebalance.js - Core business logic
โ”‚ โ”œโ”€โ”€ analytics.js - Usage analytics
โ”‚ โ””โ”€โ”€ index.js - Service exports
โ”œโ”€โ”€ ๐Ÿ“‚ routes/
โ”‚ โ””โ”€โ”€ index.js - Route definitions
โ””โ”€โ”€ ๐Ÿ“‚ validations/
โ””โ”€โ”€ (validation schemas)

๏ฟฝ๐Ÿ—„๏ธ Collections Usedโ€‹

onebalanceโ€‹

  • Purpose: Store account credit balance and configuration
  • Model: shared/models/onebalance.js
  • Key Fields:
    • account_id (ObjectId) - Owner account
    • balance (Number) - Current credit balance (in cents)
    • reload.enabled (Boolean) - Auto-reload feature toggle
    • reload.threshold (Number) - Balance level to trigger reload (cents)
    • reload.amount (Number) - Amount to add on reload (cents)
    • reload.payment_method (String) - Stripe payment method ID
    • rebill (Object) - Sub-account pricing configuration
    • created_at (Date)
    • updated_at (Date)

Rebill Structure (Main accounts only):

{
rebill: {
sms: { multiplier: 1.5 }, // 1.5x markup on SMS
listing: { value: 5000 }, // $50.00 per listing
lighting_domain: { value: 1000 }, // $10.00 per domain
instasite: { credits: 1000 }, // 1000 credits per month
instareport: { multiplier: 1.2 },
phone: { multiplier: 1.3 },
inbound_call: { multiplier: 1.1 },
outbound_call: { multiplier: 1.1 }
}
}

onebalance.usage_logsโ€‹

  • Purpose: Track service consumption events
  • Model: shared/models/onebalance-usage_logs.js
  • Key Fields:
    • account_id (ObjectId) - Consumer account
    • event (String) - Service type ('instasite', 'instareport', 'sms', 'phone', etc.)
    • amount (Number) - Cost deducted (cents)
    • status ('success' | 'failed') - Transaction status
    • metadata (Object) - Event-specific details
    • balance_before (Number) - Balance before deduction
    • balance_after (Number) - Balance after deduction
    • created_at (Date) - Usage timestamp

configsโ€‹

  • Operations: Read (pricing configuration)
  • Usage: Retrieve base service prices by account tier
  • Key Document: type: 'onebalance-base-prices'

Price Configuration Structure:

{
type: 'onebalance-base-prices',
account_pricing_type: 'pro' | 'plus' | 'platinum',
prices: {
sms: { type: 'dynamic', base_us_price: 150 }, // $0.015 per SMS
phone: { type: 'dynamic', base_us_price: 100 }, // $1.00 per month
listing: { type: 'fixed', amount: 2500 }, // $25.00
lighting_domain: { type: 'fixed', amount: 1000 }, // $10.00
instasite: { type: 'credit', credits: 1000 }, // 1000/month
instareport: { type: 'fixed', amount: 500 } // $5.00
}
}

_store.subscriptionsโ€‹

  • Operations: Read (billing cycle info for credit resets)
  • Usage: Determine current billing period for monthly credit allocations

_accountsโ€‹

  • Operations: Read (account tier, parent account, pricing type)
  • Usage: Determine pricing tier and rebilling relationships

๐Ÿ”„ Data Flowโ€‹

Auto-Reload Flowโ€‹

sequenceDiagram
participant Service as Platform Service
participant Verify as verifyBalance()
participant OB as OneBalance
participant Stripe as Stripe API
participant Log as Usage Logs

Service->>Verify: Request service (e.g., send SMS)
Verify->>OB: Check current balance
OB-->>Verify: balance: $5.00

alt Balance > Service Cost
Verify->>Log: Deduct cost from balance
Log->>OB: Update balance
Verify-->>Service: Approved
Service->>Service: Execute service
else Balance < Service Cost
Verify->>OB: Check auto-reload enabled
OB-->>Verify: reload.enabled: true
Verify->>Stripe: Charge payment method
Stripe-->>Verify: Payment success
Verify->>OB: Add reload.amount to balance
Verify->>Log: Log reload transaction
Verify->>Log: Deduct service cost
Verify-->>Service: Approved (after reload)
Service->>Service: Execute service
else Insufficient Balance & No Auto-Reload
Verify-->>Service: Rejected (insufficient credits)
Service-->>User: Error: Insufficient balance
end

Sub-Account Rebilling Flowโ€‹

flowchart TD
A[Sub-Account Requests Service] --> B[Get Base Price]
B --> C[Check Parent Account rebill Config]
C --> D{Rebill Type?}

D -->|Multiplier| E[Base Price ร— Multiplier]
D -->|Fixed Value| F[Use Parent's Fixed Price]

E --> G[Calculate Final Cost]
F --> G

G --> H[Deduct from Sub-Account Balance]
H --> I{Balance Sufficient?}

I -->|Yes| J[Execute Service]
I -->|No| K[Trigger Auto-Reload or Reject]

J --> L[Log Usage]
K --> L

InstaSite Credit Tracking Flowโ€‹

flowchart TD
A[Request InstaSite Generation] --> B[Get Current Billing Cycle]
B --> C[Count InstaSites Used This Cycle]
C --> D[Calculate Remaining Credits]
D --> E{Credits > 0?}

E -->|Yes| F[Decrement Credit Count]
E -->|No| G[Charge Per-Use Fee]

F --> H[Generate InstaSite]
G --> I{Balance Sufficient?}

I -->|Yes| H
I -->|No| J[Reject: Insufficient Credits]

H --> K[Log Usage Event]
K --> L[Return Success]

๐Ÿ”ง Core Functionsโ€‹

get({ account_id, account, currency, main })โ€‹

Retrieve OneBalance configuration and current balance for an account.

Parameters:

  • account_id (ObjectId) - Account ID
  • account (Object) - Full account object
  • currency (String) - Desired currency for display (e.g., 'USD', 'EUR')
  • main (Boolean) - Is main account (includes rebill config)

Returns:

{
account_id: ObjectId,
balance: 125.50, // Converted from cents to dollars
reload: {
enabled: true,
threshold: 50.00,
amount: 100.00,
payment_method: 'pm_xxx'
},
rebill: { // Main accounts only
listing: { value: 50.00 },
lighting_domain: { value: 10.00 },
instasite: { credits: 850 } // Remaining credits this cycle
}
}

Business Logic:

  1. Auto-Initialization: If no OneBalance record exists, creates one via initOnebalance()
  2. Currency Conversion: Converts cent-based storage to dollar display (รท 100)
  3. Main Account Features: Only main accounts receive rebill configuration
  4. InstaSite Credits: Calculates remaining credits for current billing cycle

update(account_id, data, currency, main)โ€‹

Update OneBalance configuration (reload settings, rebill config).

Parameters:

  • account_id (ObjectId)
  • data (Object) - Fields to update
  • currency (String)
  • main (Boolean)

Updatable Fields:

{
reload: {
enabled: Boolean,
threshold: Number,
amount: Number,
payment_method: String
},
rebill: { // Main accounts only
sms: { multiplier: Number },
listing: { value: Number },
lighting_domain: { value: Number },
instasite: { credits: Number }
}
}

Process:

  1. Convert dollar amounts to cents (ร— 100)
  2. Flatten nested object structure
  3. Update document in MongoDB
  4. Convert response back to dollars

costPerItem({ account_id, account, currency, user_id })โ€‹

Calculate per-service pricing for an account, applying tier pricing and rebilling.

Returns:

{
sms: {
type: 'dynamic',
base_us_price: 0.015, // Per SMS
a2p_registration_pending: false
},
phone: {
type: 'dynamic',
base_us_price: 1.00 // Per month
},
listing: {
type: 'fixed',
amount: 50.00 // Per listing
},
lighting_domain: {
type: 'fixed',
amount: 10.00 // Per domain
},
instasite: {
type: 'credit',
credits: 850, // Remaining
credit_details: {
total: 1000,
used: 150,
remaining: 850
},
billing_cycle: {
start: Date,
end: Date
}
},
instareport: {
type: 'fixed',
amount: 5.00
}
}

Business Logic:

  1. Base Pricing: Retrieve from configs collection by account pricing_type (Pro/Plus/Platinum)
  2. Dynamic Pricing: For SMS/phone, fetch real-time pricing from Twilio API
  3. Currency Conversion: Convert USD base prices to requested currency
  4. Sub-Account Markup: Apply parent account's rebill multipliers/values
  5. InstaSite Credits: Calculate remaining credits for current billing cycle
  6. A2P Registration: Flag SMS pricing if Twilio A2P registration pending

Sub-Account Pricing Calculation:

// If parent has rebill.sms.multiplier = 1.5 and base price is $0.01:
final_price = base_price * multiplier; // $0.01 * 1.5 = $0.015

// If parent has rebill.listing.value = 5000 (cents):
final_price = value / 100; // 5000 / 100 = $50.00

getInstasiteCreditInfo({ account_id, account })โ€‹

Calculate remaining InstaSite credits for current billing cycle.

Returns:

{
type: 'credit',
credits: 850, // Remaining credits
credit_details: {
total: 1000,
used: 150,
remaining: 850
},
billing_cycle: {
start: Date('2025-10-01'),
end: Date('2025-11-01')
}
}

Process:

  1. Find active subscription for account (or parent account)
  2. Extract current billing period (start/end dates)
  3. Get configured credit limit from onebalance.rebill.instasite.credits (default: 1000)
  4. Count InstaSite usage logs within billing cycle
  5. Calculate remaining credits: total - used

Edge Cases:

  • No Subscription: Returns null (no credit allocation)
  • Tier Override: Checks for manual tier overrides via tierOverride() function
  • Parent Account Lookup: Sub-accounts use parent's subscription and credit config

๐Ÿ”€ Integration Pointsโ€‹

Verification Utilityโ€‹

File: shared/utilities/onebalance.js

Function: verifyBalance({ event, account, user_id, quantity })

Used by all platform services before execution:

// Example: Before sending SMS
await verifyBalance({
event: 'sms',
account: accountObject,
user_id: userId,
quantity: 0, // 0 = check only, 1+ = deduct
});

// Example: Before creating InstaSite
await verifyBalance({
event: 'instasite',
account: accountObject,
user_id: userId,
quantity: 0,
});

Services Using OneBalance:

  • SMS Service (utilities/sms.js) - Per-message charges
  • Phone Numbers (utilities/twilio.js) - Monthly number rental
  • InstaSites (instasites/) - Credit-based or per-use
  • InstaReports (instareports/) - Per-report charges
  • Listings (reviews/) - Business listing management
  • Lightning Domains (funnels/) - Custom domain provisioning

Stripe Integrationโ€‹

Auto-Reload Payments:

  • Charges configured reload.payment_method when balance < reload.threshold
  • Uses Stripe Payment Intents API
  • Logs successful/failed charges to onebalance.usage_logs

Currency Conversionโ€‹

Utility: CurrencyUtil from utilities/currency.js

const currUtil = new CurrencyUtil();
const converted = await currUtil.convert(
account_id,
amount, // USD amount
'USD', // From currency
'EUR', // To currency
);
// Returns: { amount: 0.92, rate: 0.92, currency: 'EUR' }

๐Ÿ“Š Analytics Serviceโ€‹

File: services/analytics.js

Provides usage reporting and cost breakdown:

  • Usage by Service: SMS, InstaSites, InstaReports, etc.
  • Cost by Date Range: Daily/weekly/monthly aggregations
  • Top Consuming Sub-Accounts: For parent accounts
  • Credit Utilization: Percentage of monthly credits used
  • Reload History: Frequency and amounts

โš™๏ธ Configurationโ€‹

Environment Variablesโ€‹

BASE_CURRENCY=USD
API_BASE_URL=http://localhost:5001
API_VERSION=v1
APP_SECRET=your_jwt_secret

Account Pricing Typesโ€‹

const PRICING_TYPES = {
PRO: 'pro', // Base tier
PLUS: 'plus', // Mid tier
PLATINUM: 'platinum', // Premium tier
};

Each tier has different base pricing in configs collection.

โš ๏ธ Important Notesโ€‹

  • ๐Ÿ’ต Cent-Based Storage: All balances stored in cents (100 = $1.00) to avoid floating-point errors
  • ๐Ÿ”„ Credit Resets: InstaSite credits reset monthly based on subscription billing cycle
  • ๐Ÿ”’ Parent Control: Only parent accounts can configure rebilling for sub-accounts
  • โšก Real-Time Deduction: Balance deducted immediately before service execution
  • ๐Ÿ“Š Usage Logs: All transactions logged for audit and analytics
  • ๐ŸŒ Multi-Currency: Display in any currency, but stored in USD cents
  • ๐Ÿšจ A2P SMS: SMS pricing flagged if Twilio A2P registration incomplete

๐Ÿงช Edge Cases & Special Handlingโ€‹

Case: Insufficient Balance During Service Executionโ€‹

Condition: Balance becomes insufficient between verification and execution

Handling:

// Service uses atomic transaction
await session.withTransaction(async () => {
await verifyBalance(); // Re-verify within transaction
await executeService();
await logUsage();
});

Case: Credit Overage for InstaSitesโ€‹

Condition: User exceeds monthly credit allocation

Handling:

  • After credits exhausted, charge per-use fee from OneBalance
  • Per-use fee typically higher than credit-based pricing
  • User can purchase additional credit packs or use pay-per-use

Case: Sub-Account Without Parent Rebill Configโ€‹

Condition: Parent account has no rebill configuration

Handling:

  • Throws error: "Onebalance billing record not found"
  • Sub-account cannot use services until parent configures rebilling
  • Parent must set at least base multipliers (1.0x)

Case: Reload Payment Failureโ€‹

Condition: Auto-reload payment method declined

Handling:

  1. Log failed reload attempt
  2. Send notification to account owner
  3. Retry payment 3 times over 24 hours
  4. After 3 failures, disable services requiring credits
  5. Account owner must manually add credits or update payment method

๐Ÿ“ˆ Performance Considerationsโ€‹

Indexing Requirementsโ€‹

// onebalance collection
{ account_id: 1 }

// onebalance.usage_logs collection
{ account_id: 1, created_at: -1 }
{ account_id: 1, event: 1, created_at: -1 }
{ account_id: 1, event: 1, status: 1, created_at: -1 } // For credit calculations

Caching Strategyโ€‹

  • Balance Checks: Cache balance for 30 seconds (TTL cache)
  • Pricing Config: Cache service prices for 5 minutes
  • Credit Calculations: Cache InstaSite credits for current billing cycle (1 hour)

Optimization Patternsโ€‹

  • Batch Deductions: Group multiple SMS/email charges into single transaction
  • Lazy Loading: Only fetch rebill config when needed (main accounts)
  • Projection: Exclude unused fields in queries

Last Updated: 2025-10-08
Module Path: internal/api/v1/onebalance/
Primary Service: services/onebalance.js (300+ lines)

๐Ÿ’ฌ

Documentation Assistant

Ask me anything about the docs

Hi! I'm your documentation assistant. Ask me anything about the docs!

I can help you with:
- Code examples
- Configuration details
- Troubleshooting
- Best practices

Try asking: How do I configure the API?
09:31 AM