@nozle-js/react 0.5.3 → 0.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,329 +1,59 @@
1
1
  # @nozle-js/react
2
2
 
3
- React SDK for usage-based billing. Hooks and components for entitlements, real-time usage tracking, plan management, and Stripe checkout.
3
+ Browser-safe React components for Nozle's public plan catalog and merchant-controlled checkout.
4
4
 
5
- ## Install
5
+ ## Credential boundary
6
6
 
7
- ```bash
8
- npm install @nozle-js/react
9
- ```
7
+ - `pk_` is used only for `GET /api/v1/plans`.
8
+ - Secret keys stay on the merchant backend.
9
+ - The React package does not fetch customer billing, invoice, subscription, entitlement, or credit data.
10
+ - The React package never sends a customer ID.
10
11
 
11
- Peer dependencies:
12
+ ## Install
12
13
 
13
14
  ```bash
14
- npm install react react-dom @stripe/stripe-js @stripe/react-stripe-js
15
+ npm install @nozle-js/react react react-dom @stripe/stripe-js @stripe/react-stripe-js
15
16
  ```
16
17
 
17
- ## Quick Start
18
-
19
- Wrap your app with `BillingProvider`:
18
+ ## Merchant-backed checkout
20
19
 
21
20
  ```tsx
22
- import { BillingProvider } from "@nozle-js/react";
21
+ import { BillingProvider, PricingTable } from "@nozle-js/react";
23
22
 
24
- function App() {
23
+ export function BillingPage({ csrfToken }: { csrfToken: string }) {
25
24
  return (
26
25
  <BillingProvider
27
- apiKey="pk_live_..."
28
- baseUrl="https://api.nozle.app" // Default: https://api.nozle.app
29
- workspaceId="ws_123" // Optional: workspace scoping
30
- centrifugoUrl="wss://ws.nozle.app/connection/websocket" // Optional: real-time updates
26
+ publishableKey={import.meta.env.VITE_NOZLE_PUBLISHABLE_KEY}
27
+ createCheckout={async ({ planCode, returnUrl }) => {
28
+ const response = await fetch("/api/billing/checkout", {
29
+ method: "POST",
30
+ credentials: "include",
31
+ headers: {
32
+ "Content-Type": "application/json",
33
+ "X-CSRF-Token": csrfToken,
34
+ },
35
+ body: JSON.stringify({ planCode, returnUrl }),
36
+ });
37
+
38
+ if (!response.ok) throw new Error("Checkout failed");
39
+ return response.json();
40
+ }}
31
41
  >
32
- <YourApp />
42
+ <PricingTable returnUrl={window.location.href} highlightPlan="pro" />
33
43
  </BillingProvider>
34
44
  );
35
45
  }
36
46
  ```
37
47
 
38
- > **Note:** LLM wrappers (`wrapOpenAI`, `wrapAnthropic`) and server-side methods (`checkAndDeduct`, `customers.upsert`) are only available in `@nozle-js/node`. The React SDK is for client-side billing UI only.
39
-
40
- ## Hooks
41
-
42
- ### `useCan(feature)`
43
-
44
- Check if a customer has access to a feature.
45
-
46
- ```tsx
47
- import { useCan } from "@nozle-js/react";
48
-
49
- function MyFeature() {
50
- const { allowed, isLoading, error } = useCan("advanced_analytics");
51
-
52
- if (isLoading) return <p>Loading...</p>;
53
- if (!allowed) return <p>Upgrade to access this feature.</p>;
54
-
55
- return <div>Feature content here</div>;
56
- }
57
- ```
58
-
59
- ### `useUsage(metric)`
60
-
61
- Get real-time usage data for a metric. Updates automatically via WebSocket.
62
-
63
- ```tsx
64
- import { useUsage } from "@nozle-js/react";
65
-
66
- function TokenCounter() {
67
- const { data, isLoading } = useUsage("tokens_used");
68
-
69
- if (isLoading || !data) return null;
70
-
71
- return (
72
- <p>
73
- {data.used.toLocaleString()} / {data.limit.toLocaleString()} tokens used
74
- </p>
75
- );
76
- }
77
- ```
78
-
79
- ### `usePlan()`
80
-
81
- Get the customer's current plan and subscription status.
82
-
83
- ```tsx
84
- import { usePlan } from "@nozle-js/react";
85
-
86
- function CurrentPlan() {
87
- const { data } = usePlan();
88
-
89
- if (!data) return null;
90
-
91
- return (
92
- <p>
93
- Plan: {data.plan_slug} ({data.subscription_status})
94
- </p>
95
- );
96
- }
97
- ```
98
-
99
- ### `usePlans()`
100
-
101
- Fetch all available plans.
102
-
103
- ```tsx
104
- import { usePlans } from "@nozle-js/react";
105
-
106
- function PlanList() {
107
- const { plans, isLoading } = usePlans();
108
-
109
- if (isLoading) return <p>Loading plans...</p>;
110
-
111
- return (
112
- <ul>
113
- {plans.map((plan) => (
114
- <li key={plan.code}>
115
- {plan.name} — ${(plan.amount_cents / 100).toFixed(2)}/{plan.interval}
116
- </li>
117
- ))}
118
- </ul>
119
- );
120
- }
121
- ```
122
-
123
- ### `useCheckout()`
124
-
125
- Create a Stripe checkout session and render embedded checkout.
126
-
127
- ```tsx
128
- import { useCheckout } from "@nozle-js/react";
129
-
130
- function UpgradeButton({ planCode }: { planCode: string }) {
131
- const { fetchClientSecret, isLoading } = useCheckout();
132
-
133
- const handleUpgrade = async () => {
134
- const clientSecret = await fetchClientSecret(planCode);
135
- // Use clientSecret with Stripe EmbeddedCheckout
136
- };
137
-
138
- return (
139
- <button onClick={handleUpgrade} disabled={isLoading}>
140
- Upgrade
141
- </button>
142
- );
143
- }
144
- ```
145
-
146
- ### `useSubscribe()`
147
-
148
- Create a subscription after successful payment.
149
-
150
- ```tsx
151
- import { useSubscribe } from "@nozle-js/react";
152
-
153
- function AfterPayment({ planCode }: { planCode: string }) {
154
- const { subscribe, isLoading } = useSubscribe();
48
+ The merchant endpoint authenticates the user, derives the Nozle customer from the authenticated user or team, validates the plan and HTTPS return URL, and calls Nozle with a restricted server-side `sk_`. Any browser-supplied customer identifier must be ignored or rejected.
155
49
 
156
- const handleComplete = async () => {
157
- await subscribe(planCode);
158
- };
159
-
160
- return (
161
- <button onClick={handleComplete} disabled={isLoading}>
162
- Activate Subscription
163
- </button>
164
- );
165
- }
166
- ```
50
+ `createCheckout` may return a hosted URL, an embedded Stripe client secret, `type: "completed"`, or `type: "scheduled"`. Paid plans activate only after Nozle processes verified Stripe success; a browser redirect is not proof of payment.
167
51
 
168
52
  ## Components
169
53
 
170
- Pre-built UI components with built-in styling. No CSS imports needed.
171
-
172
- ### `PricingTable`
173
-
174
- Renders plan cards in a responsive grid with monthly/annual toggle, current plan detection, and CSS variable theming. Auto-fetches plans from the API if none are provided.
175
-
176
- ```tsx
177
- import { PricingTable } from "@nozle-js/react";
178
-
179
- // Auto-fetch plans from API
180
- <PricingTable
181
- customerId="cust_123"
182
- highlightPlan="pro"
183
- onSelect={(plan) => handleUpgrade(plan)}
184
- />
185
-
186
- // Or pass plans explicitly
187
- <PricingTable
188
- plans={[
189
- { code: "starter", name: "Starter", amount_cents: 2900, amount_currency: "USD", interval: "monthly" },
190
- { code: "pro", name: "Pro", amount_cents: 9900, amount_currency: "USD", interval: "monthly" },
191
- ]}
192
- features={[
193
- ["10K API calls", "Email support"],
194
- ["100K API calls", "Priority support", "Analytics"],
195
- ]}
196
- currentPlanCode="starter"
197
- highlightPlan="pro"
198
- onSelect={(plan) => console.log("Selected:", plan.code)}
199
- showToggle={true}
200
- enterpriseEmail="sales@example.com"
201
- className="my-pricing"
202
- />
203
- ```
204
-
205
- | Prop | Type | Default | Description |
206
- |------|------|---------|-------------|
207
- | `customerId` | `string` | -- | Customer ID for auto-detecting current plan |
208
- | `currentPlanCode` | `string` | -- | Explicitly set current plan (overrides auto-detect) |
209
- | `plans` | `PricingPlan[]` | -- | Plans to display (auto-fetched from API if omitted) |
210
- | `features` | `string[][]` | -- | Feature lists per plan (index-matched to plans array) |
211
- | `onSelect` | `(plan: PricingPlan) => void` | -- | Called when a plan's CTA is clicked |
212
- | `highlightPlan` | `string` | -- | Plan code to highlight as "Most Popular" |
213
- | `enterpriseEmail` | `string` | -- | Email for enterprise plan "Contact Sales" button |
214
- | `showToggle` | `boolean` | `true` | Show monthly/annual toggle (only when annual plans exist) |
215
- | `className` | `string` | -- | CSS class for the outer wrapper |
216
-
217
- #### PricingPlan type
218
-
219
- ```ts
220
- interface PricingPlan {
221
- code: string;
222
- name: string;
223
- amount_cents: number;
224
- amount_currency: string;
225
- interval: string;
226
- description?: string;
227
- }
228
- ```
229
-
230
- #### CSS Variable Theming
231
-
232
- PricingTable uses CSS custom properties for full theming control. Set these on a parent element or `:root`:
233
-
234
- ```css
235
- :root {
236
- /* Component-specific overrides */
237
- --nozle-pricing-bg: #ffffff;
238
- --nozle-pricing-card-bg: #ffffff;
239
- --nozle-pricing-highlight: #6366f1;
240
- --nozle-pricing-border: #e5e7eb;
241
- --nozle-pricing-radius: 12px;
242
-
243
- /* Or use global Nozle variables (PricingTable falls back to these) */
244
- --nozle-background: #ffffff;
245
- --nozle-card: #ffffff;
246
- --nozle-primary: #6366f1;
247
- --nozle-border: #e5e7eb;
248
- --nozle-radius: 12px;
249
- --nozle-foreground: #111827;
250
- --nozle-muted-foreground: #6b7280;
251
- --nozle-muted: #f3f4f6;
252
- --nozle-primary-foreground: #ffffff;
253
- }
254
- ```
255
-
256
- The component-specific variables (`--nozle-pricing-*`) take precedence over the global ones (`--nozle-*`), which fall back to sensible defaults.
257
-
258
- ### `UsageMeter`
259
-
260
- Displays usage with bar, ring, or minimal variants.
261
-
262
- ```tsx
263
- import { UsageMeter } from "@nozle-js/react";
264
-
265
- <UsageMeter metric="tokens_used" variant="bar" />
266
- <UsageMeter metric="tokens_used" variant="ring" />
267
- <UsageMeter metric="tokens_used" variant="minimal" />
268
- ```
269
-
270
- ### `PlanBadge`
271
-
272
- Shows the current plan with status indicator.
273
-
274
- ```tsx
275
- import { PlanBadge } from "@nozle-js/react";
276
-
277
- <PlanBadge variant="pill" />
278
- <PlanBadge variant="text" />
279
- <PlanBadge variant="icon" />
280
- ```
281
-
282
- ### `UpgradePrompt`
283
-
284
- Conditionally renders an upgrade prompt when a feature is gated.
285
-
286
- ```tsx
287
- import { UpgradePrompt } from "@nozle-js/react";
288
-
289
- <UpgradePrompt feature="advanced_analytics" variant="card" upgradeUrl="/upgrade" />
290
- <UpgradePrompt feature="advanced_analytics" variant="banner" />
291
- <UpgradePrompt feature="advanced_analytics" variant="inline" />
292
- ```
293
-
294
- ## Tailwind CSS Preset
295
-
296
- Optional Tailwind preset for styling the `data-nozle` attributes on components.
297
-
298
- ```ts
299
- // tailwind.config.ts
300
- import { nozlePreset } from "@nozle-js/react/styles/preset";
301
-
302
- export default {
303
- presets: [nozlePreset],
304
- };
305
- ```
306
-
307
- ## TypeScript
308
-
309
- All hooks and components are fully typed. Exported types:
310
-
311
- ```ts
312
- import type {
313
- BillingState,
314
- UseCanResult,
315
- UseUsageResult,
316
- UsePlanResult,
317
- UseCheckoutResult,
318
- Plan,
319
- PricingPlan,
320
- PricingTableProps,
321
- UsageMeterProps,
322
- PlanBadgeProps,
323
- UpgradePromptProps,
324
- } from "@nozle-js/react";
325
- ```
326
-
327
- ## License
54
+ - `PricingTable` and `usePlans` read the public catalog with the publishable key.
55
+ - `CheckoutButton`, `UpgradeButton`, `UpgradeModal`, and the default `PricingTable` CTA call the merchant's `createCheckout` callback.
56
+ - `Checkout` renders Stripe hosted/embedded checkout results supplied by the merchant.
57
+ - Gates, usage displays, `PlanBadge`, and `PaymentMethodDisplay` are presentational and consume caller-supplied data.
328
58
 
329
- Proprietary
59
+ Customer billing, invoices, cancellation, top-ups, subscriptions, entitlements, and credits belong behind authenticated merchant endpoints. Never put an `sk_`, master key, or internal credential in browser code.