@nozle-js/react 0.8.0 → 0.10.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 +109 -7
- package/dist/index.cjs +2812 -374
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +309 -35
- package/dist/index.d.ts +309 -35
- package/dist/index.js +2809 -375
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
# @nozle-js/react
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
React components for Nozle's public catalog, checkout, and customer billing portal.
|
|
4
4
|
|
|
5
5
|
## Credential boundary
|
|
6
6
|
|
|
7
7
|
- `pk_` is used only for `GET /api/v1/plans`.
|
|
8
8
|
- Secret keys stay on the merchant backend.
|
|
9
|
-
-
|
|
10
|
-
-
|
|
9
|
+
- Presentational components consume caller-supplied customer state.
|
|
10
|
+
- `BillingPortal` uses a customer-scoped session from your backend for Core GraphQL reads.
|
|
11
|
+
- Your backend chooses the customer and authorizes the selected external subscription ID.
|
|
11
12
|
|
|
12
13
|
## Install
|
|
13
14
|
|
|
14
15
|
```bash
|
|
15
|
-
npm install @nozle-js/react react react-dom @stripe/stripe-js @stripe/react-stripe-js
|
|
16
|
+
npm install @nozle-js/react@0.10.0 react react-dom @stripe/stripe-js @stripe/react-stripe-js
|
|
16
17
|
```
|
|
17
18
|
|
|
18
19
|
## Merchant-backed checkout
|
|
@@ -47,13 +48,114 @@ export function BillingPage({ csrfToken }: { csrfToken: string }) {
|
|
|
47
48
|
|
|
48
49
|
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.
|
|
49
50
|
|
|
50
|
-
`createCheckout`
|
|
51
|
+
`createCheckout` returns a `stripe`, `razorpay`, `hosted`, `processing`, `completed`, or `scheduled` result. Nozle payment fulfillment determines activation; Razorpay requires capture. A browser redirect or callback alone does not prove activation.
|
|
51
52
|
|
|
52
53
|
## Components
|
|
53
54
|
|
|
54
55
|
- `PricingTable` and `usePlans` read the public catalog with the publishable key.
|
|
55
56
|
- `CheckoutButton`, `UpgradeButton`, `UpgradeModal`, and the default `PricingTable` CTA call the merchant's `createCheckout` callback.
|
|
56
|
-
- `Checkout` renders
|
|
57
|
+
- `Checkout` renders provider checkout results and accepts merchant verification/status callbacks directly.
|
|
58
|
+
- `BillingPortal`, `CancellationControl`, and `PlanChangeControl` provide customer subscription management through authenticated merchant adapters.
|
|
57
59
|
- Gates, usage displays, `PlanBadge`, and `PaymentMethodDisplay` are presentational and consume caller-supplied data.
|
|
58
60
|
|
|
59
|
-
|
|
61
|
+
Authenticate portal sessions and management actions on your merchant backend. Return customer-scoped portal tokens at runtime; never embed an `sk_`, master key, or portal token in a static browser bundle.
|
|
62
|
+
|
|
63
|
+
## Native billing portal and subscription management
|
|
64
|
+
|
|
65
|
+
`BillingPortal` renders subscriptions, current usage, invoices/PDFs, billing details, and
|
|
66
|
+
wallets directly in React. It does not require `BillingProvider` or Tailwind. Reads use a
|
|
67
|
+
customer-scoped Core session; optional `cancellationActions` enable Cancel + Keep through
|
|
68
|
+
your authenticated merchant backend and the Node.js or Python SDK. Optional `planChangeActions`
|
|
69
|
+
add quoted upgrades, scheduled downgrades, exact pending-change withdrawal, and payment recovery.
|
|
70
|
+
|
|
71
|
+
```tsx
|
|
72
|
+
import {
|
|
73
|
+
BillingPortal, BillingPortalError,
|
|
74
|
+
type CancellationActions, type CreateBillingPortalSession,
|
|
75
|
+
} from "@nozle-js/react";
|
|
76
|
+
|
|
77
|
+
async function post(path: string, body: object, signal: AbortSignal) {
|
|
78
|
+
const response = await fetch(path, {
|
|
79
|
+
method: "POST", credentials: "same-origin",
|
|
80
|
+
headers: { "Content-Type": "application/json" },
|
|
81
|
+
body: JSON.stringify(body), signal,
|
|
82
|
+
});
|
|
83
|
+
if (!response.ok) throw new BillingPortalError(
|
|
84
|
+
[401, 403].includes(response.status) ? "unauthorized" :
|
|
85
|
+
response.status === 409 ? "changed" : "request",
|
|
86
|
+
);
|
|
87
|
+
return response.json();
|
|
88
|
+
}
|
|
89
|
+
const createSession: CreateBillingPortalSession = ({ signal }) =>
|
|
90
|
+
post("/api/billing/session", {}, signal);
|
|
91
|
+
const cancellationActions: CancellationActions = {
|
|
92
|
+
preview: ({ signal, ...input }) => post("/api/billing/cancellation/preview", input, signal),
|
|
93
|
+
apply: ({ signal, ...input }) => post("/api/billing/cancellation", input, signal),
|
|
94
|
+
};
|
|
95
|
+
export function BillingPage({ accountId }: { accountId: string }) {
|
|
96
|
+
return <BillingPortal key={accountId} createSession={createSession}
|
|
97
|
+
cancellationActions={cancellationActions} locale="en-US" />;
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
The matching runnable Node merchant example and React app are in
|
|
102
|
+
[`examples/billing-portal`](../../examples/billing-portal). The Python SDK repository has the
|
|
103
|
+
equivalent `examples/billing_portal` backend. They authenticate a server-selected demo customer
|
|
104
|
+
and require same-origin JSON requests. For your app, derive the customer from the authenticated
|
|
105
|
+
user/team, retain your CSRF protection, reject browser-supplied customer identities, and check
|
|
106
|
+
subscription ownership on the server.
|
|
107
|
+
|
|
108
|
+
### Session and action contracts
|
|
109
|
+
|
|
110
|
+
- `createSession({ signal })` returns `{ token, apiUrl? }`. On your server, call Core's
|
|
111
|
+
`GET /api/v1/customers/{external_id}/portal_url` and extract the token from `/customer-portal/{token}`.
|
|
112
|
+
Return it with the Core base URL (default `https://api.nozle.app/core`). The component sends
|
|
113
|
+
`customer-portal-token` on GraphQL requests. Self-hosted Core must allow your app origin/header.
|
|
114
|
+
- `preview({ subscriptionId, operation, signal })` returns `{ operation, effectiveAt, renewalAt }`.
|
|
115
|
+
The identifier is the **external subscription ID**. Operations are `cancel` and `uncancel`.
|
|
116
|
+
Dates are ISO strings; `renewalAt` may be null. Preserve the exact `effectiveAt` string.
|
|
117
|
+
- `apply({ subscriptionId, operation, idempotencyKey, expectedEffectiveAt?, signal })` uses the
|
|
118
|
+
existing server SDK transition method. On the server, choose `timing: "end_of_period"` for
|
|
119
|
+
cancel and forward `expectedEffectiveAt` as the `expected_effective_at` guard. Core rejects a
|
|
120
|
+
changed date before mutation. Uncancel accepts no settlement overrides. Retain merchant-side
|
|
121
|
+
idempotency records across restarts so uncertain retries use the same upstream key.
|
|
122
|
+
- Map HTTP409 state/date conflicts to `BillingPortalError("changed")`, requiring a new preview.
|
|
123
|
+
Map expired authentication to `"unauthorized"`. Never display raw upstream error bodies.
|
|
124
|
+
|
|
125
|
+
Without `cancellationActions`, the overview remains usable and cancel/keep controls are absent.
|
|
126
|
+
After each attempt the portal reads the selected subscription through customer-scoped GraphQL.
|
|
127
|
+
Cancellation stays separate from active status; Keep is available before the ending boundary.
|
|
128
|
+
Cancellation removes any pending plan change. Keep restores renewal without restoring the
|
|
129
|
+
removed change. Outstanding invoices and usage charges remain subject to merchant policy.
|
|
130
|
+
|
|
131
|
+
Use the exported `CancellationControl`, `CancellationSubscription`, and `CancellationActions`
|
|
132
|
+
on a custom billing page. Supply `refresh(signal)` to read persisted subscription state from
|
|
133
|
+
your authenticated source. It must not return an optimistic local update. The component
|
|
134
|
+
supports `onChanged`, redacted `onError`, `locale`, `timezone`, and stylesheet `nonce`.
|
|
135
|
+
|
|
136
|
+
Keep `createSession` stable and change its identity or the portal's `key` when the authenticated
|
|
137
|
+
customer changes. Remount a standalone cancellation control on customer changes too. Old
|
|
138
|
+
requests are aborted and previous customer content is hidden; portal tokens stay in memory.
|
|
139
|
+
|
|
140
|
+
Customize with `className`, `style`, `nonce`, and CSS variables `--nozle-portal-accent`,
|
|
141
|
+
`--nozle-portal-background`, `--nozle-portal-text`, `--nozle-portal-muted`,
|
|
142
|
+
`--nozle-portal-muted-background`, and `--nozle-portal-border`. UI copy is English; `locale`
|
|
143
|
+
formats amounts/dates. Errors use `code: "session" | "unauthorized" | "request" | "changed"`.
|
|
144
|
+
|
|
145
|
+
### Plan changes and payment recovery
|
|
146
|
+
|
|
147
|
+
Pass `planChangeActions` to `BillingPortal`, or use the standalone `PlanChangeControl`.
|
|
148
|
+
Its adapter provides `load`, `status`, `preview`, `apply`, and `withdraw`; use
|
|
149
|
+
`verifyCheckout` and `getCheckoutStatus` for scoped payment verification/recovery.
|
|
150
|
+
The Node.js `0.9.0` and Python `0.9.0` SDKs provide the matching backend methods.
|
|
151
|
+
|
|
152
|
+
Load eligible plans for the selected external subscription, confirm the signed quote,
|
|
153
|
+
and retain the same quote and idempotency key for uncertain retries. Upgrades activate
|
|
154
|
+
after required payment is fulfilled; downgrades use end-of-period timing with `keep_anchor`.
|
|
155
|
+
Withdrawal targets the exact internal pending UUID. Reload authoritative subscription
|
|
156
|
+
and checkout state after submission or a page reload.
|
|
157
|
+
|
|
158
|
+
See the [billing portal guide](https://docs.nozle.dev/sdks/react/billing-portal) for
|
|
159
|
+
complete typed adapters, standalone controls, and the runnable Node/Python integrations.
|
|
160
|
+
The hosted Nozle backend supports these flows; self-hosted deployments need the matching
|
|
161
|
+
subscription-management endpoints and cancellation-date guard.
|