{"record":{"id":"f95cf9ad4d9bb0c8","repo":"Stirling-Tools/Stirling-PDF","slug":"failed-to-create-billing-portal-session-error-m","errorCode":null,"errorMessage":"Failed to create billing portal session: ${error.message}","messagePattern":"Failed to create billing portal session: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"frontend/editor/src/proprietary/services/licenseService.ts","lineNumber":401,"sourceCode":"    licenseKey: string,\n  ): Promise<BillingPortalResponse> {\n    // Check if Supabase is configured\n    if (!isSupabaseConfigured || !supabase) {\n      throw new Error(\n        \"Supabase is not configured. Billing portal is not available.\",\n      );\n    }\n\n    const { data, error } = await supabase.functions.invoke(\"manage-billing\", {\n      body: {\n        return_url: returnUrl,\n        license_key: licenseKey,\n        self_hosted: true, // Explicitly indicate self-hosted mode\n      },\n    });\n\n    if (error) {\n      throw new Error(\n        `Failed to create billing portal session: ${error.message}`,\n      );\n    }\n\n    return data as BillingPortalResponse;\n  },\n\n  /**\n   * Get the installation ID from the backend (MAC-based fingerprint)\n   */\n  async getInstallationId(): Promise<string> {\n    try {\n      const response = await apiClient.get(\"/api/v1/admin/installation-id\");\n\n      const data: InstallationIdResponse = await response.data;\n      return data.installationId;\n    } catch (error) {\n      console.error(\"Error fetching installation ID:\", error);","sourceCodeStart":383,"sourceCodeEnd":419,"githubUrl":"https://github.com/Stirling-Tools/Stirling-PDF/blob/9ef20dcab80b85041912f045e17a6aea1d08f969/frontend/editor/src/proprietary/services/licenseService.ts#L383-L419","documentation":"Thrown by createBillingPortalSession when supabase.functions.invoke('manage-billing', ...) returns a non-null error. The manage-billing edge function creates a Stripe billing-portal session using the supplied license_key for self-hosted auth; a returned error means the function failed — invalid/expired license key, no matching Stripe customer, missing STRIPE_SECRET_KEY, or a function exception. error.message is wrapped in the thrown Error.","triggerScenarios":"license_key doesn't map to a Stripe customer; license was revoked/expired so the function rejects; manage-billing function missing STRIPE_SECRET_KEY secret; return_url malformed; Stripe-side error creating the portal session.","commonSituations":"User's license was cancelled/refunded so the Stripe customer no longer has a subscription; license key typos; edge function not deployed or missing secrets; Stripe account misconfigured for billing portal (must be enabled in Stripe dashboard).","solutions":["Read error.message to distinguish auth (license key) failures from Stripe errors.","Confirm the manage-billing function is deployed with STRIPE_SECRET_KEY and that the billing portal is enabled in Stripe.","Re-verify the license key via checkLicenseKey before opening the portal.","Prompt the user to re-enter their license key if it's invalid."],"exampleFix":"// before\nconst { url } = await licenseService.createBillingPortalSession(returnUrl, key);\n\n// after\ntry {\n  const { url } = await licenseService.createBillingPortalSession(returnUrl, key);\n  window.location.href = url;\n} catch (e) {\n  showToast(e instanceof Error ? e.message : 'Billing portal unavailable');\n}","handlingStrategy":"try-catch","validationCode":"if (!licenseKey) {\n  setError('License key required');\n  return;\n}\n// optionally re-verify: await licenseService.checkLicenseKey(installationId);","typeGuard":"export function isPlausibleLicenseKey(k: string): boolean {\n  return typeof k === 'string' && k.trim().length > 0;\n}","tryCatchPattern":"try {\n  const { url } = await licenseService.createBillingPortalSession(returnUrl, key);\n  window.location.href = url;\n} catch (e) {\n  showToast(e instanceof Error ? e.message : 'Billing portal unavailable');\n  if (/license|auth|invalid/i.test(e instanceof Error ? e.message : \"\")) promptRelicense();\n}","preventionTips":["Enable the Stripe customer portal in the Stripe dashboard before offering this flow.","Confirm manage-billing is deployed with STRIPE_SECRET_KEY.","Re-verify the license key is still active before opening the portal."],"tags":["supabase","edge-function","stripe","billing-portal","license","proprietary","typescript"],"backgroundTag":null,"analyzedSha":"9ef20dcab80b85041912f045e17a6aea1d08f969","analyzedAt":"2026-08-13T22:11:39.827Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}