mirror of
https://github.com/ever-co/ever-gauzy.git
synced 2026-10-02 01:54:50 +08:00
fix(billing): scope Stripe linking, paywall and billing pages to this deployment's product (#10331)
Product-scoped (hosted plan only) Stripe webhook linking, signup paywall, lazy tenant link and /billing routes; checkout-session proof at register/onboarding; BILLING_PRODUCT, BILLING_SIGNUP_PAYWALL and BILLING_WEBHOOK_LINKING (default off); 402 payment_method_required on a paid upgrade without a card. See the PR description for details. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
367a01a66d
commit
ba62a02001
@@ -121,12 +121,29 @@ export class BillingComponent extends TranslationBaseComponent implements OnInit
|
||||
if (this.working || this.isCurrentPlan(plan)) return;
|
||||
this.working = true;
|
||||
try {
|
||||
this.subscription = await firstValueFrom(this.billingService.changePlan(plan.lookupKey));
|
||||
this.subscription = await firstValueFrom(
|
||||
this.billingService.changePlan(plan.lookupKey, window.location.href)
|
||||
);
|
||||
this.toastrService.success('SETTINGS_MENU.BILLING_PLAN_CHANGED', { name: plan.productName });
|
||||
// Switching plans issues an invoice, so the list below is now stale.
|
||||
this.invoices = await this.safe(() => firstValueFrom(this.billingService.getInvoices()), this.invoices);
|
||||
} catch (error) {
|
||||
this.errorHandlingService.handleError(error);
|
||||
// An upgrade to a paid plan from one with no card on file: the API refuses it rather than
|
||||
// leave an invoice nobody can pay, and hands back a Stripe portal link to add a card. Send the
|
||||
// admin there; they return to this page and switch again.
|
||||
const body = (error as { status?: number; error?: { code?: string; portalUrl?: string } })?.error;
|
||||
if ((error as { status?: number })?.status === 402 && body?.code === 'payment_method_required') {
|
||||
// The API still answers 402 when Stripe could not open a portal session, just without the
|
||||
// link — then say so, and point at the page's own "manage billing" action instead.
|
||||
if (body.portalUrl) {
|
||||
this.toastrService.info('SETTINGS_MENU.BILLING_PAYMENT_METHOD_REQUIRED', 'TOASTR.TITLE.INFO');
|
||||
window.location.href = body.portalUrl;
|
||||
return;
|
||||
}
|
||||
this.toastrService.warning('SETTINGS_MENU.BILLING_PAYMENT_METHOD_REQUIRED_NO_PORTAL');
|
||||
} else {
|
||||
this.errorHandlingService.handleError(error);
|
||||
}
|
||||
} finally {
|
||||
this.working = false;
|
||||
}
|
||||
|
||||
@@ -74,8 +74,12 @@ export class BillingService {
|
||||
return this.http.get<IBillingPlan[]>(`${this.endpoint}/plans`);
|
||||
}
|
||||
|
||||
changePlan(lookupKey: string): Observable<IBillingSubscription> {
|
||||
return this.http.post<IBillingSubscription>(`${this.endpoint}/subscription/change`, { lookupKey });
|
||||
/**
|
||||
* Switch plans. `returnUrl` is where Stripe's portal sends the admin back to if the API answers 402
|
||||
* `payment_method_required` — an upgrade to a paid plan from one with no card on file.
|
||||
*/
|
||||
changePlan(lookupKey: string, returnUrl?: string): Observable<IBillingSubscription> {
|
||||
return this.http.post<IBillingSubscription>(`${this.endpoint}/subscription/change`, { lookupKey, returnUrl });
|
||||
}
|
||||
|
||||
cancel(): Observable<IBillingSubscription> {
|
||||
|
||||
Reference in New Issue
Block a user