fix(billing): hide billing portal for lifetime licenses (#427)

Lifetime licenses have no recurring subscription, so the Lemon Squeezy
customer portal cannot generate a URL. The Manage Subscription button in
Settings already had the isLifetime guard, but the Billing button in the
profile dropdown did not, causing a confusing "No billing portal
available" error.

- Add !license.isLifetime guard to UserProfileDropdown (matches
  LicenseSection pattern)
- Move lifetime detection into getBillingPortalUrl() so the service owns
  all billing eligibility logic
- Change return type to { url } | { error } discriminated union for
  clear error propagation
This commit is contained in:
Anso
2026-04-08 09:51:27 -04:00
committed by GitHub
parent f6d2199978
commit be7eda85f1
5 changed files with 20 additions and 13 deletions
+4 -4
View File
@@ -1073,12 +1073,12 @@ app.post('/api/license/validate', async (_req: Request, res: Response): Promise<
app.get('/api/license/billing-portal', async (_req: Request, res: Response): Promise<void> => {
try {
const url = await LicenseService.getInstance().getBillingPortalUrl();
if (!url) {
res.status(404).json({ error: 'No billing portal available. Ensure you have an active license.' });
const result = await LicenseService.getInstance().getBillingPortalUrl();
if ('error' in result) {
res.status(404).json({ error: result.error });
return;
}
res.json({ url });
res.json({ url: result.url });
} catch (error) {
console.error('[License] Billing portal error:', error);
res.status(500).json({ error: 'Failed to retrieve billing portal URL' });