@aforoai/storefront-widgets 1.0.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/CHANGELOG.md ADDED
@@ -0,0 +1,327 @@
1
+ # Changelog
2
+
3
+ All notable changes to `@aforoai/storefront-widgets` will be documented in
4
+ this file.
5
+
6
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/)
7
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
8
+
9
+ ## [Unreleased]
10
+
11
+ ### Fixed
12
+
13
+ - **Script-tag loader now parses the PricingCard presentation attributes
14
+ `data-cta-text`, `data-cta-url`, `data-max-plans`, and
15
+ `data-show-features`.** `pricingCardConfigToProps` reads all four
16
+ (`ctaText`/`ctaUrl`/`maxPlans`/`showFeatures`), and the operator Embed
17
+ Studio snippet (`EmbeddableWidgetTab`) emits `data-cta-text`,
18
+ `data-max-plans`, and `data-show-features` — but `readDataAttrs` never
19
+ parsed them, so those per-mount overrides silently did nothing on a
20
+ customer's page. Added a `readBooleanAttr` coercion helper for
21
+ `data-show-features` (data attributes are strings; the mapper guards on
22
+ `typeof === 'boolean'`). This brings PricingCard's script-tag tier to
23
+ parity with its React/JSX props. Regression coverage in
24
+ `tests/loader-config-attrs.test.ts`. Purely additive on the parse side.
25
+
26
+ - **Script-tag loader now parses `data-theme`, `data-cart-type`, and
27
+ `data-target-id`.** The shared config→props mappers
28
+ (`src/core/widgetConfigMaps.ts`) already read `config.config.theme`
29
+ (every widget), `config.config.cartType`, and `config.config.targetId`
30
+ (CheckoutFlow), but `readDataAttrs` in `loader/loader.ts` never parsed
31
+ those three `data-*` attributes — so a script-tag placeholder's theme
32
+ selection was ignored (always `auto`) and an embedded checkout always
33
+ fell back to `SUBSCRIBE` with an empty target. This completes the
34
+ parse↔consume contract for the documented attribute surface. Regression
35
+ coverage added in `tests/loader-config-attrs.test.ts`. No public API
36
+ change; purely additive on the loader's parse side.
37
+
38
+ - **`subscription-manager` `data-default-status` pre-filter now works.**
39
+ `subscriptionManagerConfigToProps` narrowed the value against a **lowercase**
40
+ allow-list (`active`, `trialing`, …) while `SubscriptionStatusFilter`, the
41
+ widget's `STATUS_CHIPS`, its `sub.status` comparisons, and the list-request
42
+ `status` param are all **UPPERCASE**. Every non-`all` selection (e.g.
43
+ `data-default-status="ACTIVE"`) was therefore rejected and silently fell
44
+ back to `all`, so the initial status filter never applied. The allow-list is
45
+ now the exact `SubscriptionStatusFilter` members (adds the previously-missing
46
+ `EXPIRING_SOON`). Regression test locks the accept-uppercase / reject-
47
+ lowercase-fallback contract.
48
+
49
+ ## [1.0.1] — 2026-05-29
50
+
51
+ ### Fixed
52
+
53
+ - **`BridgeClient` PaymentMethod methods now honour the demo-mode
54
+ provider.** All other `BridgeClient` methods (`exchange`,
55
+ `fetchHeadlessConfig`, `fetchSubscriptions`, `fetchUsage`,
56
+ `previewSubscriptionChange`, etc.) short-circuit via
57
+ `if (BridgeClient.demoModeProvider) return ...` at the top of the
58
+ method, so `installDemoModeProvider()` routes every call to the
59
+ registered mock. The four PaymentMethod methods shipped in v1.0.0
60
+ (`fetchPaymentMethods`, `createPaymentMethodSetupIntent`,
61
+ `setDefaultPaymentMethod`, `deletePaymentMethod`) were missing this
62
+ delegate — they always hit the real network path.
63
+
64
+ Symptom: any consumer that installed a demo-mode provider for testing
65
+ saw "No payment method on file" (empty list) from `AforoPaymentMethod`
66
+ even though the mock provider's `fetchPaymentMethods` returned cards.
67
+ The four methods now match the rest of the interface.
68
+
69
+ Discovered during the `@aforoai/storefront-widgets-sandbox` build-out
70
+ pass (sandbox v0.0.1 → live mounts). All other widgets render their
71
+ synthetic fixtures correctly; only PaymentMethod was returning empty.
72
+
73
+ No public API change — bug fix only. Customers on v1.0.0 should
74
+ upgrade if they rely on demo mode for PaymentMethod widget testing
75
+ (Embed Studio's operator preview pane is unaffected because it
76
+ bypasses BridgeClient via the `_mockPaymentMethods` internal prop).
77
+
78
+ ## [1.0.0] — 2026-05-29
79
+
80
+ ### General availability — all 8 widgets shipped, public API surface frozen.
81
+
82
+ This is the first generally-available release of `@aforoai/storefront-widgets`.
83
+ The public API is now stable per semver — breaking changes will require a major
84
+ bump and follow a 90-day deprecation period (see
85
+ [Versioning docs](https://docs.aforo.ai/docs/embed-versioning)).
86
+
87
+ ### Widgets (8 — all with full implementations, no placeholders)
88
+
89
+ | Widget | Purpose | Gzipped bundle |
90
+ |---|---|---|
91
+ | **`AforoPricingCard`** | Anonymous pricing display (3 layouts: horizontal cards, vertical list, comparison table) | 59.15 KB |
92
+ | **`AforoSubscribeButton`** | Authenticated subscribe CTA with anti-double-subscribe lookup + 3 modes (redirect / event-only / embedded-flow) | 64.23 KB |
93
+ | **`AforoInvoiceList`** | Paginated customer invoice browser with status filter, PDF download, Pay Now | 69.93 KB |
94
+ | **`AforoCheckoutFlow`** | Multi-step embedded checkout (4-phase state machine) — Stripe Elements / Razorpay Drop-in / PayPal Smart Buttons | 60.86 KB |
95
+ | **`AforoSubscriptionManager`** | Customer-portal subscription detail view (read-only state machine surface, upgrade/cancel events delegated to parent) | 60.23 KB |
96
+ | **`AforoUsageMeter`** | Per-subscription real-time usage display with quota progress + threshold-reached alerts | 58.85 KB |
97
+ | **`AforoPaymentMethod`** | Add / update / remove payment method via provider iframes (Stripe SetupIntent, Razorpay tokenize, PayPal Vault) | 57.89 KB |
98
+ | **`AforoUpgradeCancel`** | Multi-step upgrade + cancellation deflection wizard (V50 CancellationFeedback reuse) | 61.51 KB |
99
+
100
+ All bundles within FR-TIER-7 (loader ≤ 15 KB gz) and FR-TIER-8 (per-widget ≤ 80 KB gz) budgets defined in the spec.
101
+
102
+ ### Foundation locked
103
+
104
+ - **AforoSession singleton** — N widgets on the same page perform exactly 1 bridge exchange (FR-§17.16). Verified by `tests/integration/all-widgets-singleton.test.tsx` (deferred to follow-up).
105
+ - **Three distribution tiers** — Script-tag loader (2.42 KB gz), npm ESM / CJS (peer-dep React 18+), Vue 3 adapter at `@aforoai/storefront-widgets/vue`.
106
+ - **Real-time push via SSE** — Backend `SseEmitter` + Redis pub/sub fan-out, exponential-backoff reconnect (capped at 60s), 100ms event dedup.
107
+ - **Magic-link auth** — Anti-enumeration 200-always pattern + 15-minute single-use tokens + 10/min per-IP + 5/hour per-email rate limits.
108
+ - **SRI hash publication** — Manifest at `embed.aforo.ai/v1/sri.json` for per-bundle integrity pinning.
109
+ - **Theme inheritance** — 4-layer cascade (overrides → CSS custom properties → brand kit → neutral default).
110
+
111
+ ### Security
112
+
113
+ - Embed-key auth + bridge-token JWT exchange + per-widget signature verification (FR-SEC-9).
114
+ - Origin allowlist enforcement on all postMessage events (FR-EVT-6) — `targetOrigin` is never `*`.
115
+ - HTTPS-only enforcement at loader (rejects `http://` pages with clear console error, FR-TIER-12).
116
+ - CSP `nonce` propagation from loader script to dynamically-injected widget scripts (FR-SEC-16).
117
+ - Webhook-authoritative rule (FR-SEC-23) — backend webhooks are canonical; postMessage events are informational.
118
+ - In-memory tokens only — session JWTs never written to `localStorage`, `sessionStorage`, or cookies. Cleared on tab close.
119
+ - No `eval()`, no `Function()`, no dynamic `import()` of untrusted URLs.
120
+
121
+ ### Operator surface
122
+
123
+ - Embed Studio tab in aforo-product UI with 4 sub-tabs: Widgets / Keys / Auth Bridge / Telemetry.
124
+ - Inline preview pane for all 8 widgets with mock-mode short-circuit via underscore-prefixed props.
125
+ - One-time plaintext reveal on key mint with 10-minute auto-clear.
126
+ - Telemetry CSV / JSON export with formula-injection guard.
127
+
128
+ ### Public docs
129
+
130
+ - 12 docs pages at `docs.aforo.ai/docs/embed-*` — Quick Start, all 8 widget references, Authentication, Theming, Events, Integration Patterns, Security, Versioning.
131
+
132
+ ### Bundle sizes (gzipped)
133
+
134
+ | Bundle | Size | Budget |
135
+ |---|---|---|
136
+ | `dist/loader.umd.js` | 2.42 KB | 15 KB |
137
+ | `dist/widgets/pricing-card.js` | 59.15 KB | 80 KB |
138
+ | `dist/widgets/subscribe-button.js` | 64.23 KB | 80 KB |
139
+ | `dist/widgets/invoice-list.js` | 69.93 KB | 80 KB |
140
+ | `dist/widgets/checkout-flow.js` | 60.86 KB | 80 KB |
141
+ | `dist/widgets/subscription-manager.js` | 60.23 KB | 80 KB |
142
+ | `dist/widgets/usage-meter.js` | 58.85 KB | 80 KB |
143
+ | `dist/widgets/payment-method.js` | 57.89 KB | 80 KB |
144
+ | `dist/widgets/upgrade-cancel.js` | 61.51 KB | 80 KB |
145
+
146
+ ### Quality gates (status at 1.0.0 ship)
147
+
148
+ - **393 unit tests pass** across 41 test files. Public surface contract locked
149
+ by `sdk-contract.test.ts`; BridgeClient fail-soft on read paths + propagates
150
+ on mutation paths; AforoSession multi-tenant isolation + magic-link verify;
151
+ EventBus origin allowlist + dedup + non-`*` targetOrigin; ThemeReader
152
+ cascade; TelemetryReporter consent gating; per-widget unit + a11y + events +
153
+ session + loader tests.
154
+ - **Axe-core WCAG 2.1 AA** verified zero violations across all 8 widgets in
155
+ default + loading + error + has-data + empty states.
156
+
157
+ ### Deferred to Phase 1 (explicitly out of scope for v1.0.0)
158
+
159
+ - iframe tier rendering — current ship is script-tag + npm + Vue 3 only
160
+ - OAuth / OIDC tenant-managed auth
161
+ - Multi-subject sessions
162
+ - Last-Event-ID SSE replay
163
+ - DNS ownership challenge for custom domains
164
+ - Enterprise self-hosted bundle assets
165
+ - Web Components tier
166
+ - Subscription pause flow within `AforoUpgradeCancel`
167
+ - Per-tenant custom deflection offers in cancel flow
168
+ - Sortable columns on `AforoInvoiceList`
169
+ - Inline 3DS modal (currently falls back to provider-hosted challenge page in new tab)
170
+ - Full Storybook config + Chromatic CI integration (`.stories.tsx` files
171
+ shipped; mounting via Storybook is a separate ship)
172
+ - Sandbox playground at `embed.aforo.ai/sandbox`
173
+ - Cross-browser matrix execution + Lighthouse audit runbooks (Prompt 15
174
+ formal gauntlet documents — `v1.0.0` ships with the artifact verified to
175
+ build + test green and bundle sizes within budget; the formal matrix
176
+ runbook is a follow-up)
177
+ - i18n — UI strings are English-only at v1.0.0; locale-aware currency + date
178
+ formatting via `Intl` works today
179
+
180
+ ### Known limitations
181
+
182
+ Same as the Phase 1 deferral list above. None of these block v1.0.0 customer
183
+ rollout for tenants on Chrome / Edge / Firefox / Safari modern channels and
184
+ standard payment providers.
185
+
186
+ ## [0.1.0] — 2026-05-29
187
+
188
+ ### Initial public release.
189
+
190
+ This is the first release published to public npm. The SDK was previously
191
+ private (workspace-internal) while widget bodies were built out. The public
192
+ surface is locked by [`sdk-contract.test.ts`](https://github.com/aforoai/aforo-nextgen-UI/blob/main/packages/storefront-widgets/tests/sdk-contract.test.ts) —
193
+ adding or removing an export is a deliberate test update.
194
+
195
+ ### Widgets shipped (4 of 8)
196
+
197
+ - **`AforoPricingCard`** — anonymous pricing display with three layouts
198
+ (horizontal cards, vertical list, comparison table). Auto-fetches the
199
+ tenant's published offerings from the headless config endpoint and
200
+ inherits brand kit colors via the theme cascade.
201
+ - **`AforoSubscribeButton`** — authenticated subscribe CTA. Exchanges a
202
+ bridge token for a session JWT, checks for an existing subscription to
203
+ avoid double-subscribing, and routes to checkout in redirect, event-only,
204
+ or embedded-flow mode.
205
+ - **`AforoInvoiceList`** — paginated customer invoice table with status
206
+ filtering (UNPAID/PAID/OVERDUE/VOID), search, PDF download, and Pay Now
207
+ action. 30s in-widget client cache for responsive filter/page scrubbing.
208
+ - **`AforoCheckoutFlow`** — multi-step embedded checkout. 4-phase state
209
+ machine (customer details → payment → confirming → completed), with
210
+ Stripe Payment Element, Razorpay Drop-in, and PayPal Smart Buttons via
211
+ postMessage-bridged iframes. Supports both SUBSCRIBE and INVOICE_PAYMENT
212
+ cart types.
213
+
214
+ ### Widgets stubbed (placeholder bodies; full implementations in subsequent releases)
215
+
216
+ - `AforoSubscriptionManager`, `AforoUsageMeter`, `AforoPaymentMethod`,
217
+ `AforoUpgradeCancel` — these render an accessible loading placeholder
218
+ with their final prop interface locked. Drop-in replacement once their
219
+ bodies ship; the public API will not change.
220
+
221
+ ### Infrastructure
222
+
223
+ #### Three embed tiers
224
+
225
+ - **Script tag.** Drop one `<script>` plus `<div data-aforo-widget="…">`
226
+ placeholders. The loader (~2.5 KB gzipped) discovers placeholders,
227
+ lazy-fetches per-widget bundles, and mounts. Works in Webflow, WordPress,
228
+ plain HTML — no build toolchain required.
229
+ - **npm / React.** `npm install @aforoai/storefront-widgets` then
230
+ `import { AforoPricingCard } from '@aforoai/storefront-widgets'`.
231
+ React 18+ peer dependency.
232
+ - **Vue 3 + vanilla JS.** Wrappers shipped at
233
+ `@aforoai/storefront-widgets/vue` and
234
+ `@aforoai/storefront-widgets/vanilla`.
235
+
236
+ #### Authentication
237
+
238
+ - **Anonymous** — pricing display only; embed key + domain allowlist.
239
+ - **Bridge token** — customer backend mints a short-lived signed token;
240
+ SDK exchanges it for an in-memory session JWT.
241
+ - **Magic link** — for portals without their own session: SDK posts the
242
+ customer's email to Aforo, customer clicks the emailed link, returns to
243
+ the host page with a session JWT. Anti-enumeration: identical 202
244
+ response regardless of whether the email is registered. Rate-limited per
245
+ IP (10/min) and per email (5/hour). 15-minute one-time-use tokens.
246
+
247
+ #### Real-time push (SSE)
248
+
249
+ - Server-Sent-Events stream pushes lifecycle events
250
+ (`invoice.paid`, `subscription.status_changed`, `payment.failed`,
251
+ `usage.threshold_breached`, `invoice.created`) to mounted widgets in
252
+ real time. Automatic exponential-backoff reconnect (capped at 60s), per-
253
+ tab connection cap, 30s grace-close after last subscriber unsubscribes.
254
+
255
+ #### Theming
256
+
257
+ - Three-layer cascade: tenant brand-kit fallback → CSS variables on the
258
+ customer's page → per-widget `themeOverrides` prop. Dark mode via
259
+ `theme="dark"`, RTL via 100% logical CSS properties, namespaced classes
260
+ (`aforo-w-pc-*`, `aforo-w-sb-*`, etc.).
261
+
262
+ #### Events
263
+
264
+ - Outbound: every widget emits a typed
265
+ `aforo.<widget>.<event>` postMessage. Origin is always the resolved
266
+ parent origin — never `*`. 100ms dedup window per `(type, payload-hash)`.
267
+ - Inbound: 5-type allowlist (`aforo:checkout-completed`,
268
+ `aforo:checkout-cancelled`, `aforo:invoice-paid`,
269
+ `aforo:refresh-requested`, `aforo:payment-challenge-completed`).
270
+ Origin allowlist enforced against the embed key's registered domains.
271
+
272
+ ### Security
273
+
274
+ - **Subresource Integrity (SRI).** Per-version SHA-384 hashes published at
275
+ `https://embed.aforo.ai/v1/sri.json`. Customers pin via `integrity=`
276
+ attribute on the loader `<script>`.
277
+ - **Dedicated origin.** All bundles served from `embed.aforo.ai` so CORS,
278
+ CSP, and SRI policies isolate the embed surface from the rest of Aforo.
279
+ - **No dynamic code.** No `eval()`, no `Function()`, no dynamic
280
+ `import()` of untrusted URLs.
281
+ - **HTTPS-only loader.** Bootstrap refuses to run on `http://` pages.
282
+ - **In-memory tokens.** Session JWTs are never written to `localStorage`,
283
+ `sessionStorage`, or cookies. Cleared on tab close.
284
+
285
+ ### Test coverage
286
+
287
+ - 313 unit tests covering: public surface contract, BridgeClient (with
288
+ fail-soft semantics on all read paths), AforoSession multi-tenant
289
+ isolation + magic-link verify flow, EventBus origin allowlist + dedup
290
+ + outbound non-`*` targetOrigin, ThemeReader cascade, TelemetryReporter
291
+ consent gating, all 4 shipped widgets (unit + a11y + events + SSE).
292
+ - Axe-core WCAG 2.1 AA verified zero violations across all shipped
293
+ widgets in default + loading + error + has-data states.
294
+
295
+ ### Bundle sizes (gzipped)
296
+
297
+ | Bundle | Size | Budget |
298
+ |---------------------------------|-----------|-----------|
299
+ | `dist/loader.umd.js` | 2.42 KB | 15 KB |
300
+ | `dist/widgets/pricing-card.js` | 56.28 KB | 80 KB |
301
+ | `dist/widgets/subscribe-button.js` | 62.80 KB | 80 KB |
302
+ | `dist/widgets/invoice-list.js` | 68.49 KB | 80 KB |
303
+ | `dist/widgets/checkout-flow.js` | 59.43 KB | 80 KB |
304
+
305
+ All within FR-TIER-7 (loader 15 KB) and FR-TIER-8 (per-widget 80 KB)
306
+ budgets defined in the spec.
307
+
308
+ ### Known limitations
309
+
310
+ - **Iframe tier rendering deferred to Phase 1.** v0.1.x ships script-tag
311
+ and npm tiers only. The iframe wrapper (for tenants requiring strict
312
+ origin isolation) lands in a future release.
313
+ - **3DS challenge falls back to redirect.** Inline 3DS modal coming in
314
+ Phase 1; v0.1.x customers requiring 3DS see the gateway's hosted
315
+ challenge page in a new tab.
316
+ - **4 widget shells still render placeholder.** SubscriptionManager,
317
+ UsageMeter, PaymentMethod, UpgradeCancel will receive full bodies in
318
+ subsequent minor releases. Their public API surface (props + events)
319
+ is locked so dropping in the new bundle does not require code changes.
320
+ - **Sandbox playground at embed.aforo.ai/sandbox coming.** Lets developers
321
+ try widgets against synthetic data without setting up an account.
322
+ - **No i18n yet.** All copy is English. Locale-aware currency + date
323
+ formatting via `Intl` works today, but UI strings are not externalised.
324
+
325
+ [1.0.1]: https://github.com/aforoai/aforo-nextgen-UI/releases/tag/storefront-widgets-v1.0.1
326
+ [1.0.0]: https://github.com/aforoai/aforo-nextgen-UI/releases/tag/storefront-widgets-v1.0.0
327
+ [0.1.0]: https://github.com/aforoai/aforo-nextgen-UI/releases/tag/storefront-widgets-v0.1.0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Aforo AI Inc.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,249 @@
1
+ # @aforoai/storefront-widgets
2
+
3
+ > Drop-in embeddable widgets for Aforo's storefront — pricing cards, subscribe
4
+ > buttons, invoice lists, and embedded checkout. Brand them yours, ship in
5
+ > minutes.
6
+
7
+ [![npm version](https://img.shields.io/npm/v/@aforoai/storefront-widgets.svg)](https://www.npmjs.com/package/@aforoai/storefront-widgets)
8
+ [![license](https://img.shields.io/npm/l/@aforoai/storefront-widgets.svg)](./LICENSE)
9
+
10
+ ```bash
11
+ npm install @aforoai/storefront-widgets
12
+ ```
13
+
14
+ **Full developer docs:** <https://docs.aforo.ai/docs/embed-quickstart>
15
+
16
+ ---
17
+
18
+ ## What you get
19
+
20
+ Eight widgets covering the customer billing surface. v0.1.0 ships four real
21
+ widget bodies — the other four render an accessible loading placeholder while
22
+ their bodies are being built. Their public API is locked, so dropping in the
23
+ new bundle later requires no code changes on your side.
24
+
25
+ | Widget | Status | What it does |
26
+ |-----------------------------|-----------------|---------------------------------------------------|
27
+ | `AforoPricingCard` | ✅ Shipped | Anonymous pricing display (horizontal / vertical / table) |
28
+ | `AforoSubscribeButton` | ✅ Shipped | Authenticated subscribe CTA with anti-double-subscribe check |
29
+ | `AforoInvoiceList` | ✅ Shipped | Paginated invoice list with status filter, search, PDF, Pay Now |
30
+ | `AforoCheckoutFlow` | ✅ Shipped | Multi-step embedded checkout (Stripe / Razorpay / PayPal) |
31
+ | `AforoSubscriptionManager` | 🔜 Coming soon | Customer-facing subscription summary + lifecycle actions |
32
+ | `AforoUsageMeter` | 🔜 Coming soon | Real-time per-metric usage gauges with quota visualization |
33
+ | `AforoPaymentMethod` | 🔜 Coming soon | Saved payment methods table + add/remove flow |
34
+ | `AforoUpgradeCancel` | 🔜 Coming soon | Plan upgrade / downgrade / cancel flow with deflection |
35
+
36
+ ---
37
+
38
+ ## Three ways to embed
39
+
40
+ ### 1. Script tag (no build needed)
41
+
42
+ For Webflow, WordPress, Squarespace, and plain HTML pages. The loader is
43
+ ~2.5 KB gzipped and lazy-fetches per-widget bundles only when their
44
+ placeholders are on the page.
45
+
46
+ ```html
47
+ <script
48
+ src="https://embed.aforo.ai/v1/loader.js"
49
+ integrity="sha384-…"
50
+ crossorigin="anonymous"
51
+ async
52
+ ></script>
53
+
54
+ <div
55
+ data-aforo-widget="pricing-card"
56
+ data-tenant-slug="acme"
57
+ data-embed-key="embk_live_…"
58
+ data-layout="horizontal"
59
+ ></div>
60
+ ```
61
+
62
+ The current SRI hash for `loader.js` is at
63
+ <https://embed.aforo.ai/v1/sri.json>. Copy it into the `integrity=`
64
+ attribute on the `<script>` tag so the browser refuses to execute a
65
+ tampered bundle.
66
+
67
+ ### 2. React / Next.js
68
+
69
+ ```tsx
70
+ import { AforoPricingCard } from '@aforoai/storefront-widgets';
71
+
72
+ export function PricingSection() {
73
+ return (
74
+ <AforoPricingCard
75
+ tenantSlug="acme"
76
+ embedKey="embk_live_…"
77
+ layout="horizontal"
78
+ ctaUrlByOffering={{ off_pro: '/checkout/pro' }}
79
+ />
80
+ );
81
+ }
82
+ ```
83
+
84
+ ### 3. Vue 3
85
+
86
+ ```vue
87
+ <script setup>
88
+ import { AforoPricingCard } from '@aforoai/storefront-widgets/vue';
89
+ </script>
90
+
91
+ <template>
92
+ <AforoPricingCard
93
+ tenant-slug="acme"
94
+ embed-key="embk_live_…"
95
+ layout="horizontal"
96
+ />
97
+ </template>
98
+ ```
99
+
100
+ Vanilla JS consumers (Svelte, Solid, Lit, etc.) import from
101
+ `@aforoai/storefront-widgets/vanilla` — the same mount/unmount API the
102
+ loader script uses internally.
103
+
104
+ ---
105
+
106
+ ## Authentication modes
107
+
108
+ The plugin tier supports three auth modes depending on what your portal
109
+ already has:
110
+
111
+ 1. **Anonymous** — pricing display only, no customer context. Use this for
112
+ marketing pages where you want to show your live offerings without
113
+ asking visitors to sign in.
114
+
115
+ 2. **Bridge token** — your backend mints a short-lived signed token that
116
+ identifies your customer to Aforo. The widget exchanges it for an
117
+ in-memory session JWT — your signing key never reaches the browser.
118
+ Use this when your portal already has its own authenticated session.
119
+
120
+ 3. **Magic link** — for portals that don't have their own session yet.
121
+ The customer types their email, gets an Aforo-branded email, clicks
122
+ the link, and lands back on your page with a session JWT. Rate-limited
123
+ and anti-enumeration-defended out of the box.
124
+
125
+ Full guide with sequence diagrams + code samples for Node.js / Python / Go
126
+ bridge-token signing: <https://docs.aforo.ai/docs/embed-authentication>.
127
+
128
+ ---
129
+
130
+ ## Theming
131
+
132
+ Three-layer cascade — each layer overrides the previous:
133
+
134
+ 1. **Tenant brand kit** (logo + colors) configured in Aforo's Storefront →
135
+ Customize, fetched once when the widget mounts.
136
+ 2. **CSS variables** on your page (e.g. `--aforo-color-primary: #1A73E8;`)
137
+ override the brand kit for fine-grained theming.
138
+ 3. **Per-widget `themeOverrides` prop** for in-React/Vue customization at
139
+ the component level.
140
+
141
+ Dark mode is opt-in via `theme="dark"`. The widgets are RTL-safe (100%
142
+ logical CSS properties) and namespace every class
143
+ (`aforo-w-pc-*`, `aforo-w-sb-*`, etc.) to avoid colliding with your CSS.
144
+
145
+ Full theming docs: <https://docs.aforo.ai/docs/embed-theming>.
146
+
147
+ ---
148
+
149
+ ## Events
150
+
151
+ Every widget emits typed `postMessage` events you can listen to from the
152
+ parent page. Origin is always the resolved parent origin — never `*`.
153
+
154
+ ```js
155
+ window.aforoEmbed?.on('aforo.subscribe.checkout_requested', (payload) => {
156
+ console.log('Customer clicked subscribe', payload);
157
+ // payload = { offeringId, planName, priceCents, currency, billingCycle, ratePlanId }
158
+ });
159
+ ```
160
+
161
+ Common events:
162
+
163
+ | Event | When |
164
+ |--------------------------------------|-----------------------------------------------|
165
+ | `aforo.<widget>.ready` | Widget mounted and first paint complete |
166
+ | `aforo.subscribe.checkout_requested` | Customer clicked Subscribe |
167
+ | `aforo.subscription.created` | Subscribe checkout completed (advisory only) |
168
+ | `aforo.invoice-list.invoice.paid` | Invoice marked paid via SSE push |
169
+ | `aforo.checkout.confirmed` | Embedded checkout completed |
170
+ | `aforo.<widget>.error` | Any error path (typed `code`, no PII) |
171
+
172
+ > **⚠ Webhook is authoritative.** postMessage events fired into your page
173
+ > are **advisory only**. Always validate state changes via Aforo's signed
174
+ > webhooks before mutating your own database. We surface this prominently
175
+ > because it's the most common source of integration bugs.
176
+
177
+ Full event catalog: <https://docs.aforo.ai/docs/embed-events>.
178
+
179
+ ---
180
+
181
+ ## Browser support
182
+
183
+ We test against the latest two stable versions of every major browser. The
184
+ SDK runs anywhere modern JavaScript runs.
185
+
186
+ | Browser | Minimum version | Notes |
187
+ |--------------------|-----------------|-------|
188
+ | Chrome | N-2 (latest 2) | Desktop and Android |
189
+ | Firefox | N-2 | Desktop |
190
+ | Safari | 16+ | macOS |
191
+ | Mobile Safari | iOS 16+ | iPhone + iPad |
192
+ | Mobile Chrome | Android 12+ | Most devices |
193
+ | Edge | Latest | Chromium-based |
194
+
195
+ The loader refuses to bootstrap on `http://` pages — HTTPS is mandatory.
196
+ We do not support IE11 or pre-Chromium Edge.
197
+
198
+ ---
199
+
200
+ ## Security
201
+
202
+ - **SRI hashes published per release** at
203
+ <https://embed.aforo.ai/v1/sri.json>. Pin them via `integrity=` on the
204
+ `<script>` tag.
205
+ - **Customer CSP guidance.** Add `script-src https://embed.aforo.ai` and
206
+ `connect-src https://api.aforo.ai` to your Content-Security-Policy. No
207
+ `unsafe-inline` or `unsafe-eval` required.
208
+ - **In-memory session JWTs.** Never written to `localStorage`,
209
+ `sessionStorage`, or cookies. Cleared on tab close.
210
+ - **Anti-enumeration on magic-link.** Identical 202 response regardless of
211
+ whether the email is registered. Rate-limited per IP and per email.
212
+ - **No dynamic code.** No `eval()`, no `Function()`, no dynamic `import()`
213
+ of untrusted URLs.
214
+
215
+ Full security model + how to report vulnerabilities: see
216
+ [`SECURITY.md`](./SECURITY.md).
217
+
218
+ ---
219
+
220
+ ## Versioning
221
+
222
+ We follow [Semantic Versioning](https://semver.org). Pre-1.0:
223
+
224
+ - `0.x.y` — frequent additions, occasional breaking changes documented in
225
+ [CHANGELOG.md](./CHANGELOG.md). Pin to a specific version in production.
226
+
227
+ Post-1.0:
228
+
229
+ - `1.x.y` — strict semver. Additions and bug fixes only.
230
+ - `2.x.y` — breaking changes with a 90-day deprecation period announced
231
+ via the changelog. The loader URL is pinned at `/v1/` and stays
232
+ backward-compatible within v1.
233
+
234
+ Full versioning policy: <https://docs.aforo.ai/docs/embed-versioning>.
235
+
236
+ ---
237
+
238
+ ## License
239
+
240
+ MIT — see [LICENSE](./LICENSE).
241
+
242
+ ---
243
+
244
+ ## Support
245
+
246
+ - 📖 Docs: <https://docs.aforo.ai/docs/embed-quickstart>
247
+ - 🐛 Issues: <https://github.com/aforoai/aforo-nextgen-UI/issues>
248
+ - 🔒 Security: `security@aforo.ai`
249
+ - 💬 Email: `support@aforo.ai`