+ {/* Current Plan Section */}
+
+
+ {t('plan.activePlan.title', 'Active Plan')}
+
+
+ {t('plan.activePlan.subtitle', 'Your current subscription details')}
+
+
+
+
+
+
+
+ {currentPlan.name}
+
+
+ {t('subscription.status.active', 'Active')}
+
+
+ {currentLicenseInfo && (
+
+ {t('plan.static.maxUsers', 'Max Users')}: {currentLicenseInfo.maxUsers}
+
+ )}
+
+
+
+ {currentPlan.price === 0 ? t('plan.free.name', 'Free') : `${currentPlan.currency}${currentPlan.price}${currentPlan.period}`}
+
+
+
+
+
+
+ {/* Available Plans */}
+
+
+ {t('plan.availablePlans.title', 'Available Plans')}
+
+
+ {t('plan.static.contactToUpgrade', 'Contact us to upgrade or customize your plan')}
+
+
+
+ {staticPlans.map((plan) => (
+
+ {plan.id === currentPlan.id && (
+
+ {t('plan.current', 'Current Plan')}
+
+ )}
+ {plan.popular && plan.id !== currentPlan.id && (
+
+ {t('plan.popular', 'Popular')}
+
+ )}
+
+
+
+
+ {plan.name}
+
+
+
+ {plan.price === 0 && plan.id !== 'free'
+ ? t('plan.customPricing', 'Custom')
+ : plan.price === 0
+ ? t('plan.free.name', 'Free')
+ : `${plan.currency}${plan.price}`}
+
+ {plan.period && (
+
+ {plan.period}
+
+ )}
+
+
+ {typeof plan.maxUsers === 'string'
+ ? plan.maxUsers
+ : `${t('plan.static.upTo', 'Up to')} ${plan.maxUsers} ${t('workspace.people.license.users', 'users')}`}
+
+
+
+
+ {plan.highlights.map((highlight, index) => (
+
+ • {highlight}
+
+ ))}
+
+
+
+
+
+
+
+ ))}
+
+
+ {/* Feature Comparison Toggle */}
+
+
+
+
+ {/* Feature Comparison Table */}
+
+
+
+
+
+
+
+ {/* License Key Section */}
+
+
}
+ onClick={() => setShowLicenseKey(!showLicenseKey)}
+ >
+ {t('admin.settings.premium.licenseKey.toggle', 'Got a license key or certificate file?')}
+
+
+
+
+ }
+ >
+
+ {t('admin.settings.premium.licenseKey.info', 'If you have a license key or certificate file from a direct purchase, you can enter it here to activate premium or enterprise features.')}
+
+
+
+ {premiumLoading ? (
+
+
+
+ ) : (
+
+
+
+
+ {t('admin.settings.premium.key.label', 'License Key')}
+
+
+ }
+ description={t('admin.settings.premium.key.description', 'Enter your premium or enterprise license key. Premium features will be automatically enabled when a key is provided.')}
+ value={premiumSettings.key || ''}
+ onChange={(e) => setPremiumSettings({ ...premiumSettings, key: e.target.value })}
+ placeholder="00000000-0000-0000-0000-000000000000"
+ />
+
+
+
+
+
+
+
+ )}
+
+
+
+
+ {/* Restart Confirmation Modal */}
+
+
+ );
+};
+
+export default StaticPlanSection;
diff --git a/frontend/src/proprietary/constants/planConstants.ts b/frontend/src/proprietary/constants/planConstants.ts
new file mode 100644
index 000000000..1865238df
--- /dev/null
+++ b/frontend/src/proprietary/constants/planConstants.ts
@@ -0,0 +1,97 @@
+import { PlanFeature } from '@app/services/licenseService';
+
+/**
+ * Shared plan feature definitions for Stirling PDF Self-Hosted
+ * Used by both dynamic (Stripe) and static (fallback) plan displays
+ */
+
+export const PLAN_FEATURES = {
+ FREE: [
+ { name: 'Self-hosted deployment', included: true },
+ { name: 'All PDF operations', included: true },
+ { name: 'Secure Login Support', included: true },
+ { name: 'Community support', included: true },
+ { name: 'Regular updates', included: true },
+ { name: 'up to 5 users', included: true },
+ { name: 'Unlimited users', included: false },
+ { name: 'Google drive integration', included: false },
+ { name: 'External Database', included: false },
+ { name: 'Editing text in pdfs', included: false },
+ { name: 'Users limited to seats', included: false },
+ { name: 'SSO', included: false },
+ { name: 'Auditing', included: false },
+ { name: 'Usage tracking', included: false },
+ { name: 'Prometheus Support', included: false },
+ { name: 'Custom PDF metadata', included: false },
+ ] as PlanFeature[],
+
+ SERVER: [
+ { name: 'Self-hosted deployment', included: true },
+ { name: 'All PDF operations', included: true },
+ { name: 'Secure Login Support', included: true },
+ { name: 'Community support', included: true },
+ { name: 'Regular updates', included: true },
+ { name: 'Up to 5 users', included: false },
+ { name: 'Unlimited users', included: true },
+ { name: 'Google drive integration', included: true },
+ { name: 'External Database', included: true },
+ { name: 'Editing text in pdfs', included: true },
+ { name: 'Users limited to seats', included: false },
+ { name: 'SSO', included: false },
+ { name: 'Auditing', included: false },
+ { name: 'Usage tracking', included: false },
+ { name: 'Prometheus Support', included: false },
+ { name: 'Custom PDF metadata', included: false },
+ ] as PlanFeature[],
+
+ ENTERPRISE: [
+ { name: 'Self-hosted deployment', included: true },
+ { name: 'All PDF operations', included: true },
+ { name: 'Secure Login Support', included: true },
+ { name: 'Community support', included: true },
+ { name: 'Regular updates', included: true },
+ { name: 'up to 5 users', included: false },
+ { name: 'Unlimited users', included: false },
+ { name: 'Google drive integration', included: true },
+ { name: 'External Database', included: true },
+ { name: 'Editing text in pdfs', included: true },
+ { name: 'Users limited to seats', included: true },
+ { name: 'SSO', included: true },
+ { name: 'Auditing', included: true },
+ { name: 'Usage tracking', included: true },
+ { name: 'Prometheus Support', included: true },
+ { name: 'Custom PDF metadata', included: true },
+ ] as PlanFeature[],
+} as const;
+
+export const PLAN_HIGHLIGHTS = {
+ FREE: [
+ 'Up to 5 users',
+ 'Self-hosted',
+ 'All basic features'
+ ],
+ SERVER_MONTHLY: [
+ 'Self-hosted on your infrastructure',
+ 'Unlimited users',
+ 'Advanced integrations',
+ 'Cancel anytime'
+ ],
+ SERVER_YEARLY: [
+ 'Self-hosted on your infrastructure',
+ 'Unlimited users',
+ 'Advanced integrations',
+ 'Save with annual billing'
+ ],
+ ENTERPRISE_MONTHLY: [
+ 'Enterprise features (SSO, Auditing)',
+ 'Usage tracking & Prometheus',
+ 'Custom PDF metadata',
+ 'Per-seat licensing'
+ ],
+ ENTERPRISE_YEARLY: [
+ 'Enterprise features (SSO, Auditing)',
+ 'Usage tracking & Prometheus',
+ 'Custom PDF metadata',
+ 'Save with annual billing'
+ ]
+} as const;
diff --git a/frontend/src/proprietary/contexts/CheckoutContext.tsx b/frontend/src/proprietary/contexts/CheckoutContext.tsx
new file mode 100644
index 000000000..f8558a650
--- /dev/null
+++ b/frontend/src/proprietary/contexts/CheckoutContext.tsx
@@ -0,0 +1,350 @@
+import React, { createContext, useContext, useState, useCallback, useEffect, ReactNode } from 'react';
+import { useTranslation } from 'react-i18next';
+import { usePlans } from '@app/hooks/usePlans';
+import licenseService, { PlanTierGroup, LicenseInfo, mapLicenseToTier } from '@app/services/licenseService';
+import StripeCheckout from '@app/components/shared/StripeCheckout';
+import { userManagementService } from '@app/services/userManagementService';
+import { alert } from '@app/components/toast';
+import { pollLicenseKeyWithBackoff, activateLicenseKey, resyncExistingLicense } from '@app/utils/licenseCheckoutUtils';
+import { useLicense } from '@app/contexts/LicenseContext';
+import { isSupabaseConfigured } from '@app/services/supabaseClient';
+
+export interface CheckoutOptions {
+ minimumSeats?: number; // Override calculated seats for enterprise
+ currency?: string; // Optional currency override (defaults to 'gbp')
+ onSuccess?: (sessionId: string) => void; // Callback after successful payment
+ onError?: (error: string) => void; // Callback on error
+}
+
+interface CheckoutContextValue {
+ openCheckout: (
+ tier: 'server' | 'enterprise',
+ options?: CheckoutOptions
+ ) => Promise