✅ Complete
Phase 7
Phase 7 — Billing System
Hybrid subscription + wallet billing — tenants choose a plan with a monthly call quota, top up their wallet via Stripe or Razorpay, and pay per minute after quota is exhausted.
What It Does
KaiVox bills tenants using a two-layer model: a monthly subscription plan that includes free call minutes, and a prepaid wallet that is charged per minute when the quota runs out. The wallet is topped up by the tenant at any time. The Super Admin manages plans, adjusts wallets, and monitors low-balance alerts.
Key Routes
| URL | Description |
GET /billing | Billing overview (current plan, wallet balance, usage) |
GET /billing/plans | Available plans — upgrade/downgrade |
POST /billing/subscribe/{planId} | Change plan |
GET /billing/topup | Wallet top-up form |
POST /billing/topup/stripe | Initiate Stripe Checkout Session |
GET /billing/topup/stripe/success | Stripe success callback — verifies and credits wallet |
POST /billing/topup/razorpay | Create Razorpay order |
POST /billing/topup/razorpay/verify | Verify Razorpay signature and credit wallet |
GET /billing/history | Wallet transaction history + invoices |
GET /super-admin/plans | Super Admin: manage subscription plans |
GET /super-admin/billing | Super Admin: platform billing overview |
Key Files
| Type | Path |
| Service | app/Services/BillingService.php — chargeForCall(), wallet deduction |
| Controller | app/Http/Controllers/BillingController.php |
| Model | app/Models/SubscriptionPlan.php |
| Model | app/Models/Subscription.php |
| Table | subscription_plans — name, monthly_price, call_quota_minutes, per_minute_rate, features |
| Table | subscriptions — tenant_id, plan_id, status, trial_ends_at, current_period_start |
| Table | wallet_transactions — tenant_id, type, amount, balance_before, balance_after, reference, notes |
| Column | tenants.wallet_balance — current prepaid balance |
| Column | tenants.wallet_low_threshold — triggers low_wallet automation |
Plans
| Plan | Monthly Price | Free Minutes | Overage Rate |
| Starter | Free / $XX | 50 min/month | $0.01/min |
| Pro | $XX/month | 200 min/month | $0.008/min |
| Enterprise | Custom | Unlimited | Custom |
Exact pricing is managed by Super Admin — values in the table above are examples.
How Call Billing Works
Call ends → Vobiz/Twilio sends hangup webhook
↓
BillingService::chargeForCall($tenant, $durationSeconds)
↓
1. Convert seconds to minutes (round up to nearest minute)
2. Check subscriptions.quota_used vs subscription_plans.call_quota_minutes
3. If quota remaining → deduct from quota (free, no wallet charge)
4. If quota exhausted → calculate cost at plan per_minute_rate
5. Check tenants.wallet_balance ≥ cost
6. If sufficient → deduct from wallet, create wallet_transaction record
7. If insufficient → log failure, flag account for low wallet
8. Check if wallet_balance < wallet_low_threshold → fire low_wallet automation trigger
What's Complete
- Subscription plan CRUD (Super Admin) — create, edit, toggle active
- Tenant plan selection and upgrade/downgrade flows
- 14-day free trial (trial_ends_at set on subscription creation)
- Wallet top-up via Stripe Checkout Session flow
- Wallet top-up via Razorpay order + signature verification
- BillingService::chargeForCall() — quota then wallet deduction
- Wallet transaction history (all credits and debits with reasons)
- Invoice / billing history page
- Super Admin billing overview (all tenant wallets, low-balance alerts)
- Super Admin manual wallet adjustment (add/deduct with reason note)
- BillingService wired into VobizService and TwilioService hangup handlers
Dynamic Pricing Control (2026-05-21)
Super Admin can increase or decrease any pricing metric at GET /super-admin/pricing. Changes are audited and can be scheduled or applied immediately.
| URL | Description |
GET /super-admin/pricing | Pricing control dashboard |
POST /super-admin/pricing/plans/{plan}/update-now | Apply price change immediately |
POST /super-admin/pricing/plans/{plan}/schedule | Schedule change for 1st of next month |
POST /super-admin/pricing/plans/{plan}/cancel-change | Cancel a pending scheduled change |
POST /super-admin/pricing/platform/update-now | Update platform base rates immediately |
POST /super-admin/pricing/platform/schedule | Schedule platform rate change |
POST /super-admin/pricing/feature-plan-map | Update which plan each feature requires |
POST /super-admin/pricing/apply-due | Manually trigger due scheduled changes |
- Scheduled changes — set
price_effective_date → applies on 1st of that month via daily 00:05 scheduler
- Tenant notice — yellow banner on tenant dashboard + billing page showing upcoming price change
- Yearly plan protection — banner tells yearly subscribers their current cycle is unaffected
- Audit trail — all changes logged to
pricing_changes table with old/new values, who made the change, and when it applied
- Metrics supported: monthly price, yearly price, per-minute rate, per-SMS rate, per-WhatsApp rate, discount percentage
| Type | Path |
| Service | app/Services/Billing/PricingService.php |
| Controller | app/Http/Controllers/SuperAdmin/PricingController.php |
| View | resources/views/super-admin/pricing.blade.php |
| Partial | resources/views/partials/pricing-change-notice.blade.php |
| Table | pricing_changes — full audit history |
| Columns | plans.next_price_monthly/yearly, next_per_minute_rate, price_effective_date |
What's Deferred
- Automatic recurring Stripe subscription (currently manual plan tracking)
- Prorated billing on plan change mid-cycle
- Email receipts for top-ups and invoices (blocked locally; works on Hostinger)
- Tax handling (GST for India, VAT for EU)