lightswitch-js
v0.0.9
Published
A minimal SDK for subscription management, entitlements, and paywalls
Maintainers
Readme
lightswitch-js
A minimal SDK for subscription management, entitlements, and paywalls.
📦 Installation
npm install lightswitch-jsyarn add lightswitch-jspnpm add lightswitch-js📁 Project Structure
lightswitch-sdk-js/
├── index.js # Main entry point
├── README.md # This file
│
├── components/ # React Components
│ ├── LightswitchProvider.jsx # Main provider component
│ └── ProtectedRoute.jsx # Route protection component
│
├── hooks/ # React Hooks
│ ├── useLightswitch.js # Main SDK hook
│ ├── useAuth.js # Authentication hook
│ └── useEntitlement.js # Entitlement checking hook
│
├── core/ # Core Vanilla JS
│ ├── index.js # Core SDK entry
│ ├── api.js # API communication
│ ├── auth.js # Authentication logic
│ ├── config.js # Configuration
│ ├── entitlement.js # Entitlement checking
│ ├── paywall.js # Paywall rendering
│ └── tracking.js # Analytics
│
└── examples/ # Usage Examples
├── basic-setup.js # Minimal setup
├── advanced-usage.js # Advanced features
└── test-without-supabase.js # Core testing🚀 Quick Start
1. Basic Setup (Recommended)
import React from 'react';
import { LightswitchProvider, ProtectedRoute } from 'lightswitch-js';
import { supabase } from './your-supabase-client';
const App = () => (
<LightswitchProvider
publishableKey="pk_your-app_your-key"
appSlug="your-app"
supabaseClient={supabase}
autoShowPaywall={true}
>
<BrowserRouter>
<Routes>
<Route path="/" element={<Landing />} />
<Route path="/app" element={
<ProtectedRoute>
<Dashboard />
</ProtectedRoute>
} />
</Routes>
</BrowserRouter>
</LightswitchProvider>
);2. Advanced Usage
import { useLightswitch, useEntitlement } from 'lightswitch-js';
const MyComponent = () => {
const { showPaywall, redirectToLogin } = useLightswitch();
const { entitled, loading } = useEntitlement('has_access');
if (!entitled) {
return <button onClick={showPaywall}>Upgrade</button>;
}
return <div>Premium content!</div>;
};📚 API Reference
Components
LightswitchProvider
Main provider that wraps your app and handles SDK initialization.
Props:
publishableKey(string) - Your app's publishable keyappSlug(string) - Your app slugsupabaseClient(optional) - Supabase client for auth integrationautoShowPaywall(boolean) - Auto-show paywall when not entitled
ProtectedRoute
Component that protects routes based on entitlements.
Props:
children(ReactNode) - Content to show when entitledfeature(string) - Feature to check (defaults to "has_access")
Hooks
useLightswitch()
Main hook for SDK functionality.
Returns:
showPaywall()- Show paywall modalredirectToLogin()- Redirect to loginredirectToSignup()- Redirect to signupisInitialized- SDK initialization status
useEntitlement(feature, options)
Hook for checking entitlements.
Parameters:
feature(string) - Feature to checkoptions.checkOnMount(boolean) - Check on component mount
Returns:
entitled(boolean) - Whether user is entitledloading(boolean) - Loading stateerror(string) - Error message if any
useLightswitchAuth()
Hook for authentication state.
Returns:
isAuthenticated(boolean) - Auth statususer(object) - User infologout()- Logout functionsetSupabaseSession(token)- Set Supabase session from external token
🔧 Core Features
✅ 7 Key Functionalities:
- Redirecting for sign up -
useLightswitch().redirectToSignup() - Redirecting to Login -
useLightswitch().redirectToLogin() - Auth token sets supabase session - Automatic via
useAuth.js - Initialization - Via
LightswitchProvider - Check for entitlement - Via
useEntitlementhook - Rendering Paywall - Via
showPaywall()function - Analytics - Via
trackEvent()function
📖 Examples
See the /examples folder for complete usage examples:
basic-setup.js- Minimal integrationadvanced-usage.js- Using hooks for controlexternal-auth-flow.js- Handle external auth flows (funnels)test-without-supabase.js- Core functionality testing
🔗 External Auth Integration
✨ Automatic (Recommended)
The SDK automatically handles external auth tokens (like from funnels) with zero additional code:
// That's it! The SDK handles everything automatically
<LightswitchProvider
publishableKey="pk_your-key"
appSlug="your-app"
supabaseClient={supabaseClient}
autoExternalAuth={true} // 👈 Default: true
>
<YourApp />
</LightswitchProvider>What happens automatically:
- User returns from funnel with
?token=SUPABASE_TOKEN - SDK detects token during initialization
- Sets Supabase session and updates auth state
- Cleans up URL parameters
- User sees authenticated app immediately
🛠️ Manual (If Needed)
If you need custom control:
// Method 1: Using the hook
const { setSupabaseSession } = useLightswitch();
await setSupabaseSession(token);
// Method 2: Direct import
import { setSupabaseSession } from 'lightswitch-js';
await setSupabaseSession(token);
// Method 3: Complete control
import { handleExternalAuthReturn } from 'lightswitch-js';
await handleExternalAuthReturn({ redirectPath: '/dashboard' });💳 Customer Portal (Billing Management)
Allow users to manage their subscriptions, update payment methods, and view billing history through Stripe's customer portal.
Automatic DOM Binding (Easiest)
The SDK automatically finds and binds to elements with the data-lightswitch-customer-portal attribute:
<!-- Simple portal button -->
<button data-lightswitch-customer-portal>Manage Billing</button>
<!-- With custom return URL -->
<button data-lightswitch-customer-portal="/dashboard">Account Settings</button>
<!-- Works on any clickable element -->
<a href="#" data-lightswitch-customer-portal="/account">Billing Portal</a>React Hook
import { useCustomerPortal } from 'lightswitch-js';
function AccountSettings() {
const { handleCustomerPortal, canAccessPortal } = useCustomerPortal();
if (!canAccessPortal) {
return <div>Please log in to manage billing.</div>;
}
return (
<button onClick={() => handleCustomerPortal('/dashboard')}>
Manage Billing
</button>
);
}Vanilla JavaScript
import Lightswitch from 'lightswitch-js';
// Redirect to customer portal
await Lightswitch.redirectToCustomerPortal('/dashboard');
// Or get portal URL for custom handling
const portalUrl = await Lightswitch.createCustomerPortal('/account');
window.open(portalUrl, '_blank');Error Handling
The customer portal requires:
- Authenticated user (401 → redirect to login)
- User with payment history (400 → redirect to pricing)
try {
await handleCustomerPortal('/dashboard');
} catch (error) {
if (error.message.includes('401')) {
// Redirect to login
} else if (error.message.includes('400')) {
// User needs to make a purchase first
}
}🏗️ Architecture
The SDK is organized into three layers:
- Components - React components for easy integration
- Hooks - React hooks for advanced control
- Core - Vanilla JS functionality that works anywhere
This structure makes it easy to understand, maintain, and extend the SDK while keeping the integration footprint minimal.
