Bill Queue Pro per active branch like Enterprise.
Deploy Ladill Queue / deploy (push) Successful in 33s

Show /branch/mo pricing with live branch totals on the plans page,
charge subscribe/prepaid/renewals by branch count, and store pro_billed_branches.
This commit is contained in:
isaacclad
2026-07-16 09:33:03 +00:00
parent 4e45ef4b65
commit 8892316664
6 changed files with 163 additions and 37 deletions
+52 -19
View File
@@ -27,7 +27,11 @@ class ProController extends Controller
: null;
$planKey = $plans->planKey($organization);
$branchCount = $plans->activeBranchCount($organization);
$proPrice = $plans->proPriceMinor($organization);
$enterprisePrice = $plans->enterprisePriceMinor($organization);
$billedBranches = (int) ($settings['pro_billed_branches']
?? $settings['enterprise_billed_branches']
?? $branchCount);
return view('qms.pro.index', [
'organization' => $organization,
@@ -36,7 +40,8 @@ class ProController extends Controller
'isEnterprise' => $planKey === 'enterprise',
'hasPaidPlan' => $plans->hasPaidPlan($organization),
'canManage' => $canManage,
'proPriceMinor' => $plans->proPriceMinor(),
'proPricePerBranchMinor' => $plans->proPricePerBranchMinor(),
'proPriceMinor' => $proPrice,
'enterprisePricePerBranchMinor' => $plans->enterprisePricePerBranchMinor(),
'enterprisePriceMinor' => $enterprisePrice,
'branchCount' => $branchCount,
@@ -45,7 +50,9 @@ class ProController extends Controller
'prepaidMonths' => (array) config('qms.prepaid_months', [6, 12, 24]),
'currency' => (string) config('billing.currency', 'GHS'),
'planExpiresAt' => $expiresAt,
'proBilledBranches' => (int) ($settings['pro_billed_branches'] ?? $branchCount),
'enterpriseBilledBranches' => (int) ($settings['enterprise_billed_branches'] ?? $branchCount),
'billedBranches' => $billedBranches,
'salesUrl' => ladill_platform_url('contact-sales'),
]);
}
@@ -63,12 +70,13 @@ class ProController extends Controller
return $this->chargeMonthlyWallet(
$organization,
$billing,
$plans->proPriceMinor(),
$plans->proPriceMinor($organization),
'pro',
'queue_pro',
'queue-pro-'.$organization->id.'-'.now()->format('Y-m-d-His'),
'Ladill Queue Pro — monthly subscription',
'Welcome to Queue Pro — active for one month.',
$plans->activeBranchCount($organization),
);
}
@@ -128,9 +136,19 @@ class ProController extends Controller
$monthlyMinor = $plan === 'enterprise'
? $plans->enterprisePriceMinor($organization)
: $plans->proPriceMinor();
: $plans->proPriceMinor($organization);
$amountMinor = $monthlyMinor * $months;
$branches = $plan === 'enterprise' ? $plans->activeBranchCount($organization) : null;
$branches = $plans->activeBranchCount($organization);
$metadata = [
'organization_id' => $organization->id,
'billed_branches' => $branches,
];
if ($plan === 'enterprise') {
$metadata['enterprise_billed_branches'] = $branches;
} else {
$metadata['pro_billed_branches'] = $branches;
}
try {
$checkout = $billing->initiatePlanCheckout(
@@ -139,10 +157,7 @@ class ProController extends Controller
$months,
$amountMinor,
route('qms.pro.paystack.callback'),
array_filter([
'organization_id' => $organization->id,
'enterprise_billed_branches' => $branches,
], static fn ($v) => $v !== null),
$metadata,
);
} catch (\Throwable) {
return redirect()->route('qms.pro.index')
@@ -191,9 +206,13 @@ class ProController extends Controller
$plan = (string) ($result['plan'] ?? 'pro');
$months = (int) ($result['months'] ?? 0);
$branches = isset($metadata['enterprise_billed_branches'])
? (int) $metadata['enterprise_billed_branches']
: null;
$branches = isset($metadata['billed_branches'])
? (int) $metadata['billed_branches']
: (isset($metadata['pro_billed_branches'])
? (int) $metadata['pro_billed_branches']
: (isset($metadata['enterprise_billed_branches'])
? (int) $metadata['enterprise_billed_branches']
: null));
$this->activatePrepaidPlan($organization, $plan, $months, $branches);
@@ -212,7 +231,7 @@ class ProController extends Controller
string $reference,
string $description,
string $successMessage,
?int $enterpriseBranches = null,
?int $billedBranches = null,
): RedirectResponse {
try {
if (! $billing->canAfford($organization->owner_ref, $priceMinor)) {
@@ -244,9 +263,7 @@ class ProController extends Controller
$settings['plan_expires_at'] = now()
->addDays((int) config('qms.pro.period_days', 30))
->toIso8601String();
if ($enterpriseBranches !== null) {
$settings['enterprise_billed_branches'] = $enterpriseBranches;
}
$this->applyBilledBranches($settings, $plan, $billedBranches);
unset($settings['plan_renewal_error']);
$organization->update(['settings' => $settings]);
@@ -257,17 +274,33 @@ class ProController extends Controller
Organization $organization,
string $plan,
int $months,
?int $enterpriseBranches = null,
?int $billedBranches = null,
): void {
$settings = $organization->settings ?? [];
$settings['plan'] = $plan;
$settings['auto_renew'] = false;
$settings['billing_method'] = 'paystack_prepaid';
$settings['plan_expires_at'] = now()->addMonths($months)->toIso8601String();
if ($enterpriseBranches !== null) {
$settings['enterprise_billed_branches'] = $enterpriseBranches;
}
$this->applyBilledBranches($settings, $plan, $billedBranches);
unset($settings['plan_renewal_error']);
$organization->update(['settings' => $settings]);
}
/**
* @param array<string, mixed> $settings
*/
private function applyBilledBranches(array &$settings, string $plan, ?int $billedBranches): void
{
if ($billedBranches === null) {
return;
}
if ($plan === 'enterprise') {
$settings['enterprise_billed_branches'] = $billedBranches;
unset($settings['pro_billed_branches']);
} else {
$settings['pro_billed_branches'] = $billedBranches;
unset($settings['enterprise_billed_branches']);
}
}
}
+12 -2
View File
@@ -74,9 +74,19 @@ class PlanService
return $max === null || $currentCount < $max;
}
public function proPriceMinor(): int
public function proPricePerBranchMinor(): int
{
return (int) config('qms.plans.pro.price_minor', 249000);
return (int) config(
'qms.plans.pro.price_minor_per_branch',
config('qms.plans.pro.price_minor', 249000)
);
}
public function proPriceMinor(Organization $organization): int
{
$branches = max(1, $this->activeBranchCount($organization));
return $branches * $this->proPricePerBranchMinor();
}
public function enterprisePricePerBranchMinor(): int
+12 -3
View File
@@ -52,9 +52,10 @@ class ProRenewalService
$isEnterprise = $plan === 'enterprise';
$price = $isEnterprise
? $this->plans->enterprisePriceMinor($organization)
: $this->plans->proPriceMinor();
: $this->plans->proPriceMinor($organization);
$reference = ($isEnterprise ? 'queue-enterprise-' : 'queue-pro-')
.$organization->id.'-'.now()->format('YmdHis');
$branchCount = $this->plans->activeBranchCount($organization);
try {
$charged = $this->billing->debit(
@@ -77,7 +78,11 @@ class ProRenewalService
->addDays((int) config('qms.pro.period_days', 30))
->toIso8601String();
if ($isEnterprise) {
$settings['enterprise_billed_branches'] = $this->plans->activeBranchCount($organization);
$settings['enterprise_billed_branches'] = $branchCount;
unset($settings['pro_billed_branches']);
} else {
$settings['pro_billed_branches'] = $branchCount;
unset($settings['enterprise_billed_branches']);
}
unset($settings['plan_renewal_error']);
$organization->update(['settings' => $settings]);
@@ -89,7 +94,11 @@ class ProRenewalService
$graceEnd = $expires->copy()->addDays((int) config('qms.pro.grace_days', 3));
if ($graceEnd->isPast()) {
$settings['plan'] = 'free';
unset($settings['plan_expires_at'], $settings['enterprise_billed_branches']);
unset(
$settings['plan_expires_at'],
$settings['enterprise_billed_branches'],
$settings['pro_billed_branches'],
);
$settings['plan_renewal_error'] = 'Suspended after failed renewal.';
} else {
$settings['plan_renewal_error'] = 'Renewal failed — top up your wallet.';