@nozle-js/react 0.5.2 → 0.7.0
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 +34 -216
- package/dist/index.cjs +400 -1909
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +122 -295
- package/dist/index.d.ts +122 -295
- package/dist/index.js +400 -1899
- package/dist/index.js.map +1 -1
- package/package.json +11 -7
package/README.md
CHANGED
|
@@ -1,241 +1,59 @@
|
|
|
1
1
|
# @nozle-js/react
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Browser-safe React components for Nozle's public plan catalog and merchant-controlled checkout.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Credential boundary
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
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
|
-
|
|
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
|
-
##
|
|
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
|
|
23
|
+
export function BillingPage({ csrfToken }: { csrfToken: string }) {
|
|
25
24
|
return (
|
|
26
25
|
<BillingProvider
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
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
|
-
<
|
|
42
|
+
<PricingTable returnUrl={window.location.href} highlightPlan="pro" />
|
|
33
43
|
</BillingProvider>
|
|
34
44
|
);
|
|
35
45
|
}
|
|
36
46
|
```
|
|
37
47
|
|
|
38
|
-
|
|
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()`
|
|
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.
|
|
124
49
|
|
|
125
|
-
|
|
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();
|
|
155
|
-
|
|
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
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
Displays usage with bar, ring, or minimal variants.
|
|
175
|
-
|
|
176
|
-
```tsx
|
|
177
|
-
import { UsageMeter } from "@nozle-js/react";
|
|
178
|
-
|
|
179
|
-
<UsageMeter metric="tokens_used" variant="bar" />
|
|
180
|
-
<UsageMeter metric="tokens_used" variant="ring" />
|
|
181
|
-
<UsageMeter metric="tokens_used" variant="minimal" />
|
|
182
|
-
```
|
|
183
|
-
|
|
184
|
-
### `PlanBadge`
|
|
185
|
-
|
|
186
|
-
Shows the current plan with status indicator.
|
|
187
|
-
|
|
188
|
-
```tsx
|
|
189
|
-
import { PlanBadge } from "@nozle-js/react";
|
|
190
|
-
|
|
191
|
-
<PlanBadge variant="pill" />
|
|
192
|
-
<PlanBadge variant="text" />
|
|
193
|
-
<PlanBadge variant="icon" />
|
|
194
|
-
```
|
|
195
|
-
|
|
196
|
-
### `UpgradePrompt`
|
|
197
|
-
|
|
198
|
-
Conditionally renders an upgrade prompt when a feature is gated.
|
|
199
|
-
|
|
200
|
-
```tsx
|
|
201
|
-
import { UpgradePrompt } from "@nozle-js/react";
|
|
202
|
-
|
|
203
|
-
<UpgradePrompt feature="advanced_analytics" variant="card" upgradeUrl="/upgrade" />
|
|
204
|
-
<UpgradePrompt feature="advanced_analytics" variant="banner" />
|
|
205
|
-
<UpgradePrompt feature="advanced_analytics" variant="inline" />
|
|
206
|
-
```
|
|
207
|
-
|
|
208
|
-
## Tailwind CSS Preset
|
|
209
|
-
|
|
210
|
-
Optional Tailwind preset for styling the `data-nozle` attributes on components.
|
|
211
|
-
|
|
212
|
-
```ts
|
|
213
|
-
// tailwind.config.ts
|
|
214
|
-
import { nozlePreset } from "@nozle-js/react/styles/preset";
|
|
215
|
-
|
|
216
|
-
export default {
|
|
217
|
-
presets: [nozlePreset],
|
|
218
|
-
};
|
|
219
|
-
```
|
|
220
|
-
|
|
221
|
-
## TypeScript
|
|
222
|
-
|
|
223
|
-
All hooks and components are fully typed. Exported types:
|
|
224
|
-
|
|
225
|
-
```ts
|
|
226
|
-
import type {
|
|
227
|
-
BillingState,
|
|
228
|
-
UseCanResult,
|
|
229
|
-
UseUsageResult,
|
|
230
|
-
UsePlanResult,
|
|
231
|
-
UseCheckoutResult,
|
|
232
|
-
Plan,
|
|
233
|
-
UsageMeterProps,
|
|
234
|
-
PlanBadgeProps,
|
|
235
|
-
UpgradePromptProps,
|
|
236
|
-
} from "@nozle-js/react";
|
|
237
|
-
```
|
|
238
|
-
|
|
239
|
-
## 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.
|
|
240
58
|
|
|
241
|
-
|
|
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.
|