@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.
@@ -0,0 +1,1228 @@
1
+ import * as React from 'react';
2
+ import { T as ThemeTokenOverrides, O as OfferingPayload, H as HeadlessConfigResponse, S as SubscriptionPayload, W as WidgetErrorPayload, a as SubscriptionForOfferingResponse, C as CheckoutCartType, b as CheckoutConfirmedPayload, c as CheckoutCancelledPayload, d as CheckoutCartResponse, e as SubscriptionStatusFilter, f as SubscriptionPayloadV2, g as SubscriptionPageResponse, I as InvoiceStatusFilter, h as InvoicePayload, i as InvoicePageResponse, U as UsageMetricPayload, j as UsageThreshold, k as UsageResponse, P as PaymentMethodListResponse, A as ApplyAt, l as UpgradeCancelResultPayload, D as DemoModeBridgeProvider, m as TenantBrandKit, n as InitiateCheckoutRequest, o as InitiateCheckoutResponse, p as InvoiceListFilter, q as InvoicePdfUrlResponse, r as InitiateInvoicePaymentRequest, s as InitiateInvoicePaymentResponse, t as CreateCartRequest, u as UpdateCustomerDetailsRequest, v as InitiateCartPaymentRequest, w as ConfirmCartResponse, R as RequestMagicLinkRequest, x as RequestMagicLinkResponse, V as VerifyMagicLinkResponse, y as SubscriptionListFilter, z as PreviewChangeRequest, B as PreviewChangeResponse, E as UpgradeRequest, F as UpgradeResponse, G as CancelWithFeedbackRequest, J as CancelResponse, K as SetupIntentResponse, L as SessionConfig, M as SessionState } from './types-Dm6cp_Cc.cjs';
3
+ export { N as AforoEventPayload, Q as AforoEventType, X as CancelReason, Y as CheckoutBillingAddress, Z as CheckoutCartErrorResponse, _ as CheckoutCartStatus, $ as CheckoutCustomerDetailsSubmittedPayload, a0 as CheckoutExpiredPayload, a1 as CheckoutFlowPhase, a2 as CheckoutPaymentCompletedPayload, a3 as CheckoutPaymentInitiatedPayload, a4 as CheckoutPaymentProvider, a5 as CheckoutStepChangedPayload, a6 as ConsentState, a7 as FeaturePayload, a8 as HeadlessBrandingPayload, a9 as InvoiceDownloadedPayload, aa as InvoiceExpandedPayload, ab as InvoiceFilterChangedPayload, ac as InvoicePaidPayload, ad as InvoicePayRequestedPayload, ae as InvoiceSearchChangedPayload, af as InvoiceStatus, ag as InvoiceTypeKind, ah as LiveSubscriptionStatus, ai as MagicLinkFailureCode, aj as OfferingSummary, ak as RatePlanPayload, al as RetentionOfferShown, am as RetentionOutcome, an as SubscriptionCancelRequestedPayload, ao as SubscriptionChangeType, ap as SubscriptionClickedPayload, aq as SubscriptionFilterChangedPayload, ar as SubscriptionStatus, as as SubscriptionUpgradeRequestedPayload, at as ThemeTokens, au as UpgradeCancelAbandonedPayload, av as UpgradeCancelCompletedPayload, aw as UpgradeCancelDeflectionOfferAcceptedPayload, ax as UpgradeCancelDeflectionOfferShownPayload, ay as UpgradeCancelPreviewFetchedPayload, az as UpgradeCancelStepChangedPayload, aA as UsageMetricClickedPayload, aB as UsageThresholdReachedPayload, aC as WidgetId, aD as WidgetMountConfig } from './types-Dm6cp_Cc.cjs';
4
+ export { A as AforoEmbed } from './AforoEmbed-BqQOFsl3.cjs';
5
+ export { EmbedEnvironment, EmbedKey } from '@aforoai/types';
6
+
7
+ /**
8
+ * AforoPricingCard — renders all PUBLISHED offerings for the tenant.
9
+ *
10
+ * Spec: FR-WIDGET-PC-1..9 (Section 3.2 of 103-embeddable-plugins-requirements).
11
+ *
12
+ * v0.2.0 (Prompt 4 — 2026-05-26): real rendering shipped. Replaces the
13
+ * placeholder body from Prompt 3 with three layouts (horizontal /
14
+ * vertical / table), full theme inheritance, locale-aware currency
15
+ * formatting, the 4 distinct UI states required by FR-WIDGET-X-1, and
16
+ * postMessage emission of `aforo.pricing-card.cta_clicked` on every CTA
17
+ * click.
18
+ *
19
+ * The component itself is a thin orchestrator over `<WidgetShell>` —
20
+ * the shell handles session init + theme resolve + event bus +
21
+ * ErrorBoundary, and exposes everything via the `useWidgetShell()`
22
+ * context. The actual rendering work lives in `<PricingCardBody>`.
23
+ */
24
+
25
+ interface AforoPricingCardProps {
26
+ /** Tenant slug — required. */
27
+ tenantSlug: string;
28
+ /** Embed key (Bearer for all backend calls) — required. */
29
+ embedKey: string;
30
+ /** Pre-issued customer bridge token. Optional — pricing card is anonymous-mode safe (FR-AUTH-1). */
31
+ bridgeToken?: string;
32
+ /** Layout key. Default `horizontal`. */
33
+ layout?: 'horizontal' | 'vertical' | 'table';
34
+ /** Per-offering CTA URL override (Prompt 3 surface). Wins over `ctaUrl`. */
35
+ ctaUrlByOffering?: Record<string, string>;
36
+ /** Force-feature a specific offering. Overrides backend `offering.featured`. */
37
+ featuredOfferingId?: string;
38
+ /** Theme cascade overrides (applied via the shell's ThemeReader). */
39
+ themeOverrides?: ThemeTokenOverrides;
40
+ /** Dark-mode mode. `auto` reads `prefers-color-scheme`. Default `auto`. */
41
+ theme?: 'light' | 'dark' | 'auto';
42
+ /** BCP-47 locale (e.g. `en-US`, `de-DE`). Default `navigator.language`. */
43
+ locale?: string;
44
+ /** Max plans to render — operator-set per FR-EMB-02. Default 4. */
45
+ maxPlans?: number;
46
+ /** Whether to show the per-plan features list. Default `true`. */
47
+ showFeatures?: boolean;
48
+ /** Default CTA label when not overridden per-offering. Default `Get Started` (FR-EMB-03). */
49
+ ctaText?: string;
50
+ /** Default CTA URL. If absent, CTA click only fires postMessage. */
51
+ ctaUrl?: string;
52
+ /** Optional React-side callback fired before the URL navigation + postMessage. */
53
+ onCtaClick?: (offering: OfferingPayload) => void;
54
+ /** Optional `Intl.NumberFormat` factory override — used by tests to inject a deterministic formatter. */
55
+ intlOverride?: (locale: string, currency: string) => Intl.NumberFormat;
56
+ /**
57
+ * INTERNAL — operator Embed Studio preview short-circuit. When provided,
58
+ * the widget bypasses the BridgeClient fetch entirely and renders this
59
+ * payload directly. Underscore prefix signals "not for production app
60
+ * code"; this prop has no impact on customer integrations and is
61
+ * deliberately undocumented in the public README.
62
+ */
63
+ _mockHeadlessConfig?: HeadlessConfigResponse | null;
64
+ }
65
+ declare function AforoPricingCard(props: AforoPricingCardProps): React.ReactElement;
66
+
67
+ /**
68
+ * AforoSubscribeButton — single CTA that initiates a hosted-checkout
69
+ * subscription flow for a specific offering.
70
+ *
71
+ * Spec: FR-WIDGET-SB-1..7 (Section 3.3) + FR-WIDGET-X-1..11 (Section 3.10).
72
+ *
73
+ * v0.3.0 (Prompt 5 — 2026-05-26): real rendering shipped. Replaces the
74
+ * Prompt 3 placeholder body. After this commit, AforoSubscribeButton is
75
+ * the first AUTHENTICATED widget in the embed plane — it exercises the
76
+ * full {@code AforoSession} bridge-token → session-JWT flow end-to-end,
77
+ * the anti-double-subscribe check against pricing-service, and the
78
+ * idempotent checkout-initiate path through storefront-service.
79
+ *
80
+ * Public prop interface evolves from Prompt 3:
81
+ *
82
+ * <ul>
83
+ * <li>{@code bridgeToken} is now REQUIRED (was optional)</li>
84
+ * <li>{@code buttonText} → {@code ctaText} (renamed to match the Prompt 4
85
+ * AforoPricingCard convention)</li>
86
+ * <li>New: {@code mode}, {@code returnUrl}, {@code onSubscribed},
87
+ * {@code onError}</li>
88
+ * </ul>
89
+ *
90
+ * The {@code sdk-contract.test.ts} surface lock checks only the export name
91
+ * — prop interface evolution is permitted in v0.x per CLAUDE.md pre-launch
92
+ * posture. Locked once v1.0 ships.
93
+ *
94
+ * The component itself is a thin orchestrator over {@code <WidgetShell>} —
95
+ * the shell handles session init + theme resolve + event bus +
96
+ * ErrorBoundary, and exposes everything via the {@code useWidgetShell()}
97
+ * context. The actual rendering work lives in {@code <SubscribeButtonBody>}.
98
+ */
99
+
100
+ interface AforoSubscribeButtonProps {
101
+ /** Tenant slug — required. */
102
+ tenantSlug: string;
103
+ /** Embed key (Bearer for all backend calls) — required. */
104
+ embedKey: string;
105
+ /**
106
+ * Customer bridge token (JWT issued at signup / login). REQUIRED for the
107
+ * subscribe-button widget — anti-double-subscribe + checkout-initiate both
108
+ * need a customer-session JWT (different from the embed key) per FR-AUTH-3.
109
+ */
110
+ bridgeToken: string;
111
+ /** Offering to subscribe to — required (FR-WIDGET-SB-1). */
112
+ offeringId: string;
113
+ /**
114
+ * Mode advisory — controls what happens on CTA click:
115
+ * <ul>
116
+ * <li>{@code redirect} (default): navigates to hosted checkout</li>
117
+ * <li>{@code event-only}: emits {@code aforo.subscribe.checkout_requested}
118
+ * postMessage and lets the parent page handle the redirect</li>
119
+ * <li>{@code embedded-flow}: reserved for Prompt 7 (CheckoutFlow widget)
120
+ * — currently renders a "Coming soon" stub and falls back to
121
+ * redirect mode if the operator wants a working flow today</li>
122
+ * </ul>
123
+ */
124
+ mode?: 'redirect' | 'event-only' | 'embedded-flow';
125
+ /**
126
+ * CTA label. Defaults to {@code 'Subscribe'} or {@code 'Start free trial'}
127
+ * (when {@code trialMode=true}).
128
+ */
129
+ ctaText?: string;
130
+ /** Button size variant (FR-WIDGET-SB-6). Default {@code 'medium'}. */
131
+ size?: 'small' | 'medium' | 'large';
132
+ /** Start-free-trial mode — no payment method collected at checkout (FR-WIDGET-SB-5). */
133
+ trialMode?: boolean;
134
+ /**
135
+ * Where hosted checkout returns the customer to on success. Default is
136
+ * {@code window.location.href}. Validated server-side against the embed
137
+ * key's {@code allowed_domains} list.
138
+ */
139
+ returnUrl?: string;
140
+ /** Theme cascade overrides (applied via the shell's ThemeReader). */
141
+ themeOverrides?: ThemeTokenOverrides;
142
+ /** Dark-mode mode. Default {@code 'auto'}. */
143
+ theme?: 'light' | 'dark' | 'auto';
144
+ /** Optional React-side callback fired when {@code aforo.subscription.created} resolves. */
145
+ onSubscribed?: (subscription: SubscriptionPayload) => void;
146
+ /** Optional React-side callback fired on any widget error. */
147
+ onError?: (error: WidgetErrorPayload) => void;
148
+ /**
149
+ * INTERNAL — operator Embed Studio preview short-circuit. When provided,
150
+ * the widget bypasses the AforoSession bridge exchange + the
151
+ * BridgeClient.fetchSubscriptionForOffering call entirely and renders
152
+ * directly against this synthetic response. Underscore prefix signals
153
+ * "not for production app code"; this prop has no impact on customer
154
+ * integrations and is deliberately undocumented in the public README.
155
+ * Mirrors AforoPricingCard's `_mockHeadlessConfig` operator-preview
156
+ * pattern.
157
+ */
158
+ _mockSubscriptionForOffering?: SubscriptionForOfferingResponse | null;
159
+ }
160
+ declare function AforoSubscribeButton(props: AforoSubscribeButtonProps): React.ReactElement;
161
+
162
+ /**
163
+ * AforoCheckoutFlow — multi-step embedded checkout flow.
164
+ *
165
+ * Spec: FR-WIDGET-CK-1..7 (Section 3.4) + FR-WIDGET-X-1..11 (Section 3.10).
166
+ *
167
+ * v0.4.0 (Prompt 7B — 2026-05-28): real rendering shipped. Replaces the
168
+ * Prompt 3 placeholder. After this commit, AforoCheckoutFlow is the
169
+ * second AUTHENTICATED widget in the embed plane (after SubscribeButton
170
+ * from Prompt 5) — it exercises the full Prompt 7A backend BFF surface
171
+ * (createCart / getCart / updateCustomerDetails / initiatePayment /
172
+ * confirmCart / cancelCart) end-to-end.
173
+ *
174
+ * Cart types supported:
175
+ * - SUBSCRIBE — direct-to-checkout for an offering (from SubscribeButton
176
+ * embedded-flow mode OR standalone widget mount)
177
+ * - INVOICE_PAYMENT — pay an existing invoice (from InvoiceList Pay Now)
178
+ *
179
+ * Multi-step state machine (4 phases + 3 terminal states):
180
+ * bootstrapping → customer-details → payment → confirming → completed
181
+ * ↘ cancelled / expired / error
182
+ *
183
+ * Payment provider integration is iframe-based per FR-SEC-15 — the widget
184
+ * never touches raw PAN/CVV; the gateway-hosted form owns that. v1 widget
185
+ * renders the appropriate surface per provider (Stripe clientSecret →
186
+ * embedded iframe; Razorpay orderId → embedded checkout; PayPal approvalUrl
187
+ * → popup window). Cross-origin postMessage on a strict allowlist drives
188
+ * the tokenization → /initiate-payment → /confirm flow.
189
+ *
190
+ * FR-SEC-23 reminder: widget events (subscription.created, invoice.paid)
191
+ * are advisory only. Backend webhooks are the authoritative source for
192
+ * customer-side state.
193
+ */
194
+
195
+ interface AforoCheckoutFlowProps {
196
+ /** Tenant slug — required. */
197
+ tenantSlug: string;
198
+ /** Embed key (Bearer for all backend calls) — required. */
199
+ embedKey: string;
200
+ /**
201
+ * Customer bridge token (JWT issued at signup / login). REQUIRED — the
202
+ * checkout-flow widget exercises every mutation under the BFF; needs a
203
+ * customer-session JWT for every call.
204
+ */
205
+ bridgeToken: string;
206
+ /**
207
+ * Cart type — drives which downstream-target the cart is bound to. Default
208
+ * is `SUBSCRIBE`. INVOICE_PAYMENT carts skip the offering anti-double-
209
+ * subscribe check.
210
+ */
211
+ cartType?: CheckoutCartType;
212
+ /**
213
+ * Target id for the cart:
214
+ * - cartType=SUBSCRIBE → offering id (required)
215
+ * - cartType=INVOICE_PAYMENT → invoice id (required)
216
+ */
217
+ targetId: string;
218
+ /**
219
+ * Where to return the customer if the payment flow leaves the iframe
220
+ * (PayPal approval URL, 3DS challenges in a new tab). Defaults to the
221
+ * current page URL.
222
+ */
223
+ returnUrl?: string;
224
+ /** Theme cascade overrides. */
225
+ themeOverrides?: ThemeTokenOverrides;
226
+ /** Dark-mode mode. Default `auto`. */
227
+ theme?: 'light' | 'dark' | 'auto';
228
+ /** Optional React callback fired on `aforo.checkout-flow.confirmed`. */
229
+ onConfirmed?: (payload: CheckoutConfirmedPayload) => void;
230
+ /** Optional React callback fired on any widget error. */
231
+ onError?: (error: WidgetErrorPayload) => void;
232
+ /** Optional React callback fired when customer dismisses the flow. */
233
+ onCancelled?: (payload: CheckoutCancelledPayload) => void;
234
+ /**
235
+ * INTERNAL — operator Embed Studio preview short-circuit. When provided,
236
+ * the widget bypasses the AforoSession bridge exchange + the BFF
237
+ * createCart call and uses the supplied cart instead. The Embed Studio
238
+ * preview pane uses this to render the widget against a synthetic
239
+ * fixture without depending on a live backend.
240
+ */
241
+ _mockCartResponse?: CheckoutCartResponse;
242
+ /**
243
+ * INTERNAL — short-circuit for the bridge-token exchange. Pairs with
244
+ * `_mockCartResponse` for operator preview. Production widget mounts
245
+ * NEVER pass this.
246
+ */
247
+ _mockSessionJwt?: string;
248
+ /**
249
+ * INTERNAL — for tests + operator preview. When set, the widget skips the
250
+ * iframe render entirely and uses the supplied paymentMethodId on the
251
+ * /initiate-payment call. Production widget mounts NEVER pass this.
252
+ */
253
+ _mockPaymentMethodId?: string;
254
+ }
255
+ declare function AforoCheckoutFlow(props: AforoCheckoutFlowProps): React.ReactElement;
256
+
257
+ /**
258
+ * AforoSubscriptionManager — paginated list of the signed-in customer's
259
+ * subscriptions with status filters + per-row actions (Manage / Upgrade /
260
+ * Cancel / Update payment / Resume / Re-subscribe).
261
+ *
262
+ * Spec: FR-WIDGET-SM-1..6 (Section 3.5) + FR-WIDGET-X-1..11 (Section 3.10).
263
+ *
264
+ * v0.5.0 (Prompt 12 — 2026-05-29): real rendering shipped. Replaces the
265
+ * Prompt 3 placeholder body. Consumes the {@code GET /api/v1/portal/embed/subscriptions}
266
+ * endpoint added in Prompt 12 (storefront-service Embed BFF).
267
+ *
268
+ * <p>SSE-driven row refresh ({@code subscription.status_changed} from
269
+ * Prompt 8) is deliberately deferred — Prompt 8 SSE wiring on
270
+ * {@code AforoSession.subscribe} is still the Prompt 3 stub. The widget
271
+ * works correctly via polling-or-manual-refresh in the meantime; SSE
272
+ * wiring lands in a follow-up commit once Prompt 8 ships for real. The
273
+ * inbound {@code aforo:subscription-changed} parent-postMessage path works
274
+ * today and provides the same UX in practice.</p>
275
+ *
276
+ * <p>Public prop interface evolves from Prompt 3 (which had a thin 5-prop
277
+ * shape with {@code statusFilter} as a single-value pre-filter). Following
278
+ * the Prompt 5 / 6B precedent ({@code buttonText} → {@code ctaText},
279
+ * {@code statusFilter} → {@code defaultStatus}), the prop interface
280
+ * evolves freely in v0.x; {@code sdk-contract.test.ts} only locks runtime
281
+ * exports.</p>
282
+ */
283
+
284
+ interface AforoSubscriptionManagerProps {
285
+ /** Tenant slug — required. */
286
+ tenantSlug: string;
287
+ /** Embed key (Bearer for all backend calls) — required. */
288
+ embedKey: string;
289
+ /** Customer bridge token (JWT issued at signup / login). */
290
+ bridgeToken: string;
291
+ /** Page size for pagination. Default 10. Server clamps to [1, 50]. */
292
+ pageSize?: number;
293
+ /**
294
+ * Status filter on first render. Default {@code 'all'}. The widget
295
+ * normalises to uppercase before forwarding to the BFF.
296
+ */
297
+ defaultStatus?: SubscriptionStatusFilter;
298
+ /** Whether to render the status filter chips. Default {@code true}. */
299
+ showFilters?: boolean;
300
+ /** BCP-47 locale (e.g. {@code 'en-US'}, {@code 'de-DE'}). Default {@code navigator.language}. */
301
+ locale?: string;
302
+ /** Theme cascade overrides (applied via the shell's ThemeReader). */
303
+ themeOverrides?: ThemeTokenOverrides;
304
+ /** Dark-mode posture. Default {@code 'auto'}. */
305
+ theme?: 'light' | 'dark' | 'auto';
306
+ /** Fires when the "Manage" button is clicked (or row click in some layouts). */
307
+ onSubscriptionClick?: (sub: SubscriptionPayloadV2) => void;
308
+ /**
309
+ * Fires when "Upgrade" / "Update payment" / "Re-subscribe" is clicked.
310
+ * For PAST_DUE rows, the callback receives a payload with
311
+ * {@code paymentUpdate: true} signalling the parent should drive the
312
+ * customer to a payment-update flow (Phase 1 — Prompt 13 wires this to
313
+ * the AforoPaymentMethod widget).
314
+ */
315
+ onUpgradeRequested?: (sub: SubscriptionPayloadV2, paymentUpdate: boolean) => void;
316
+ /** Fires when "Cancel" is clicked. */
317
+ onCancelRequested?: (sub: SubscriptionPayloadV2) => void;
318
+ /** Optional React-side callback fired on any widget error. */
319
+ onError?: (error: WidgetErrorPayload) => void;
320
+ /**
321
+ * INTERNAL — operator Embed Studio preview short-circuit. When provided,
322
+ * the widget bypasses the AforoSession bridge exchange + the
323
+ * {@code BridgeClient.fetchSubscriptions} call entirely and renders
324
+ * directly against this synthetic page. Mirrors the
325
+ * {@code _mockInvoicePage} / {@code _mockHeadlessConfig} patterns.
326
+ */
327
+ _mockSubscriptionPage?: SubscriptionPageResponse | null;
328
+ /** INTERNAL — short-circuits the in-widget refetch on filter changes for static-preview stories. */
329
+ _mockStaticPreview?: boolean;
330
+ }
331
+ declare function AforoSubscriptionManager(props: AforoSubscriptionManagerProps): React.ReactElement;
332
+
333
+ /**
334
+ * AforoInvoiceList — paginated list of the signed-in customer's invoices.
335
+ *
336
+ * Spec: FR-WIDGET-IL-1..7 (Section 3.6) + FR-WIDGET-X-1..11 (Section 3.10).
337
+ *
338
+ * v0.4.0 (Prompt 6B — 2026-05-27): real rendering shipped. Replaces the
339
+ * Prompt 3 placeholder body. Consumes the 3 BFF endpoints shipped in Prompt
340
+ * 6A (storefront-service commit 41c8177):
341
+ *
342
+ * <ul>
343
+ * <li>{@code GET /api/v1/portal/embed/invoices} — paginated list with
344
+ * page/size/status/search/sort query params</li>
345
+ * <li>{@code GET /api/v1/portal/embed/invoices/{id}/pdf-url} — short-
346
+ * lived download link</li>
347
+ * <li>{@code POST /api/v1/portal/embed/invoices/{id}/pay/initiate} —
348
+ * Phase 0 stub; replaced in-place by Prompt 7 (CheckoutFlow widget)</li>
349
+ * </ul>
350
+ *
351
+ * <p>Public prop interface evolves from Prompt 3 (which had {@code statusFilter}
352
+ * uppercase). Following the Prompt 5 precedent ({@code buttonText} →
353
+ * {@code ctaText}), the prop interface evolves freely in v0.x; the
354
+ * {@code sdk-contract.test.ts} surface lock only covers runtime exports.
355
+ * Locked once v1.0 ships.</p>
356
+ *
357
+ * <p>The component itself is a thin orchestrator over {@code <WidgetShell>}.
358
+ * The shell handles session init + theme resolve + event bus + ErrorBoundary,
359
+ * and exposes everything via {@code useWidgetShell()}. Actual rendering
360
+ * work lives in {@code <InvoiceListBody>}.</p>
361
+ */
362
+
363
+ interface AforoInvoiceListProps {
364
+ /** Tenant slug — required. */
365
+ tenantSlug: string;
366
+ /** Embed key (Bearer for all backend calls) — required. */
367
+ embedKey: string;
368
+ /**
369
+ * Customer bridge token (JWT issued at signup / login).
370
+ *
371
+ * Either {@link bridgeToken} OR {@link magicLinkMode} MUST be set — the
372
+ * invoice list is customer-scoped (anonymous mode unsupported per FR-AUTH-3).
373
+ * Prompt 9 / FR-AUTH-6 made this prop optional so widgets can fall back
374
+ * to email-based magic-link auth when the host portal doesn't have a
375
+ * pre-issued bridge token (marketing site, support portal, pre-integration
376
+ * self-serve).
377
+ */
378
+ bridgeToken?: string;
379
+ /**
380
+ * Magic-link auth mode (Prompt 9 / FR-AUTH-6). When {@code true}:
381
+ * <ul>
382
+ * <li>If the URL contains an {@code aforo_magic_token} query param, the
383
+ * SDK consumes it on mount, exchanges for a session JWT via the
384
+ * backend's verify endpoint, then clears the token from the URL
385
+ * (via {@code history.replaceState}).</li>
386
+ * <li>Otherwise the widget renders a {@link MagicLinkFallback} form —
387
+ * the customer enters their email and Aforo sends them a sign-in
388
+ * link.</li>
389
+ * </ul>
390
+ *
391
+ * Mutually exclusive with {@link bridgeToken}; when both are set,
392
+ * {@link bridgeToken} wins (a host portal that has a real session
393
+ * shouldn't accidentally fall through to the email prompt).
394
+ */
395
+ magicLinkMode?: boolean;
396
+ /** Page size for pagination. Default 10. Server clamps to [1, 50]. */
397
+ pageSize?: number;
398
+ /**
399
+ * Status filter on first render. Default {@code 'all'}. Lowercase per
400
+ * the prompt's API; the BFF uppercases on receive.
401
+ */
402
+ defaultStatus?: InvoiceStatusFilter;
403
+ /** Whether to render the search input. Default {@code true}. */
404
+ showSearch?: boolean;
405
+ /** Whether to render the status filter chips. Default {@code true}. */
406
+ showFilters?: boolean;
407
+ /**
408
+ * Whether to render the Pay Now button on UNPAID / OVERDUE rows.
409
+ * Default {@code true}. Phase 0: fires {@code aforo.invoice-list.pay_requested}
410
+ * postMessage only — customer's parent handles payment via their own flow.
411
+ * Phase 1 (Prompt 7): wires the embedded CheckoutFlow widget in-place.
412
+ */
413
+ showPayNow?: boolean;
414
+ /** BCP-47 locale (e.g. {@code 'en-US'}, {@code 'de-DE'}). Default {@code navigator.language}. */
415
+ locale?: string;
416
+ /** Theme cascade overrides (applied via the shell's ThemeReader). */
417
+ themeOverrides?: ThemeTokenOverrides;
418
+ /** Dark-mode posture. Default {@code 'auto'}. */
419
+ theme?: 'light' | 'dark' | 'auto';
420
+ /** Fires when a row is clicked (in addition to inline expansion). */
421
+ onInvoiceClick?: (invoice: InvoicePayload) => void;
422
+ /** Fires when Pay Now is clicked (Phase 0: customer parent handles payment). */
423
+ onPayRequested?: (invoice: InvoicePayload) => void;
424
+ /** Fires when a PDF download is initiated. */
425
+ onDownload?: (invoice: InvoicePayload, downloadUrl: string) => void;
426
+ /** Optional React-side callback fired on any widget error. */
427
+ onError?: (error: WidgetErrorPayload) => void;
428
+ /**
429
+ * INTERNAL — operator Embed Studio preview short-circuit. When provided,
430
+ * the widget bypasses the AforoSession bridge exchange + the
431
+ * {@code BridgeClient.fetchInvoices} call entirely and renders directly
432
+ * against this synthetic page. Underscore prefix signals "not for
433
+ * production app code"; this prop has no impact on customer integrations.
434
+ * Mirrors AforoPricingCard's {@code _mockHeadlessConfig} +
435
+ * AforoSubscribeButton's {@code _mockSubscriptionForOffering} patterns.
436
+ */
437
+ _mockInvoicePage?: InvoicePageResponse | null;
438
+ /** INTERNAL — short-circuits the in-widget refetch on filter changes for static-preview stories. */
439
+ _mockStaticPreview?: boolean;
440
+ }
441
+ declare function AforoInvoiceList(props: AforoInvoiceListProps): React.ReactElement;
442
+
443
+ /**
444
+ * AforoUsageMeter — current-period usage vs quota for one or more metrics.
445
+ *
446
+ * Spec: FR-WIDGET-UM-1..7 (Section 3.7) + FR-WIDGET-X-1..11 (Section 3.10).
447
+ *
448
+ * v0.5.0 (Prompt 12 — 2026-05-29): real rendering shipped. Replaces the
449
+ * Prompt 3 placeholder body. Consumes the {@code GET /api/v1/portal/embed/subscriptions/{id}/usage}
450
+ * endpoint added in Prompt 12 (storefront-service Embed BFF).
451
+ *
452
+ * <p>SSE-driven refresh ({@code usage.threshold_breached} from Prompt 8) is
453
+ * deferred — Prompt 8 SSE wiring on {@code AforoSession.subscribe} is still
454
+ * the Prompt 3 stub. The widget refreshes via configurable polling
455
+ * (default 60s) and the inbound {@code aforo:usage-refresh-requested}
456
+ * parent-postMessage signal in the meantime.</p>
457
+ *
458
+ * <p>Threshold detection ({@code aforo.usage-meter.threshold_reached}
459
+ * events at 70/90/100%) is fully client-side — the widget compares the
460
+ * current vs prior {@code quotaUsedPct} across refreshes and fires the
461
+ * event the FIRST time each threshold is crossed within the current
462
+ * mount lifetime. Resets when the metric drops below 70% (next period or
463
+ * a quota reset).</p>
464
+ */
465
+
466
+ interface AforoUsageMeterProps {
467
+ /** Tenant slug — required. */
468
+ tenantSlug: string;
469
+ /** Embed key (Bearer for all backend calls) — required. */
470
+ embedKey: string;
471
+ /** Customer bridge token. */
472
+ bridgeToken: string;
473
+ /** Subscription id whose usage to display — required. */
474
+ subscriptionId: string;
475
+ /**
476
+ * If provided, the widget filters to just this metric (compact mode).
477
+ * If omitted, shows all metrics in expanded mode by default.
478
+ */
479
+ metricName?: string;
480
+ /**
481
+ * Polling interval in ms. Default 60_000. Server-side cache returns
482
+ * stale values within a 30s bucket so the actual refresh tempo is
483
+ * closer to 60s no matter what this is set to.
484
+ */
485
+ pollIntervalMs?: number;
486
+ /**
487
+ * Layout mode:
488
+ * - 'compact' — single metric or first-of-many, hero card
489
+ * - 'expanded' — grid of metrics
490
+ * Default: 'compact' if {@code metricName} provided, else 'expanded'.
491
+ */
492
+ mode?: 'compact' | 'expanded';
493
+ /** BCP-47 locale. Default {@code navigator.language}. */
494
+ locale?: string;
495
+ /** Theme cascade overrides. */
496
+ themeOverrides?: ThemeTokenOverrides;
497
+ /** Dark-mode posture. Default {@code 'auto'}. */
498
+ theme?: 'light' | 'dark' | 'auto';
499
+ /** Fires when a metric is clicked (compact mode click area). */
500
+ onMetricClick?: (metric: UsageMetricPayload) => void;
501
+ /** Fires the first time {@code quotaUsedPct} crosses 70%, 90%, or 100%. */
502
+ onThresholdReached?: (metric: UsageMetricPayload, threshold: UsageThreshold) => void;
503
+ /** Optional callback fired on any widget error. */
504
+ onError?: (error: WidgetErrorPayload) => void;
505
+ /**
506
+ * INTERNAL — operator Embed Studio preview short-circuit. When provided,
507
+ * the widget bypasses the AforoSession bridge exchange + the
508
+ * {@code BridgeClient.fetchUsage} call entirely and renders directly
509
+ * against this synthetic payload. Mirrors the
510
+ * {@code _mockSubscriptionPage} pattern.
511
+ */
512
+ _mockUsage?: UsageResponse | null;
513
+ }
514
+ declare function AforoUsageMeter(props: AforoUsageMeterProps): React.ReactElement;
515
+
516
+ /**
517
+ * AforoPaymentMethod — display + manage the customer's stored payment
518
+ * methods.
519
+ *
520
+ * Spec: FR-WIDGET-PM-1..6 (Section 3.8) + FR-WIDGET-X-1..11 (Section 3.10).
521
+ *
522
+ * v1.0.0 (Prompt 15 closure — 2026-05-29): real body shipped. Closes the
523
+ * gap surfaced during the v1.0.0 production-readiness gauntlet where the
524
+ * prior tracker entry claimed Prompt 13 shipped this widget but no SDK /
525
+ * loader / preview code was ever committed.
526
+ *
527
+ * <p>Two modes:</p>
528
+ * <ul>
529
+ * <li><b>compact</b> — single card showing the default method (brand +
530
+ * last4 + expiry) + an "Update" CTA. Sized for embedding in a sidebar
531
+ * or modal.</li>
532
+ * <li><b>expanded</b> — full list of stored methods with per-row "Set
533
+ * default" and "Remove" actions + an "Add method" CTA at the top.</li>
534
+ * </ul>
535
+ *
536
+ * <p>Provider iframe mount (Stripe Elements / Razorpay Drop-in / PayPal
537
+ * Smart Buttons) is intentionally deferred to Phase 1. v1.0.0 ships the
538
+ * event-based flow: clicking "Add" or "Update" fires an
539
+ * {@code aforo.payment-method.update_requested} event carrying the
540
+ * SetupIntent client secret so the parent page wires their existing
541
+ * provider SDK to confirm. This is the honest contract — the SDK doesn't
542
+ * pretend to mount providers it hasn't shipped support for.</p>
543
+ *
544
+ * <p>FR-SEC-23 reminder: the {@code aforo.payment-method.updated} event
545
+ * fired after the parent's provider SDK confirms is ADVISORY only. The
546
+ * backend webhook on the provider callback is authoritative. Don't trigger
547
+ * downstream business actions from this client event.</p>
548
+ */
549
+
550
+ interface AforoPaymentMethodProps {
551
+ /** Tenant slug — required. */
552
+ tenantSlug: string;
553
+ /** Embed key (Bearer for all backend calls) — required. */
554
+ embedKey: string;
555
+ /** Customer bridge token. */
556
+ bridgeToken: string;
557
+ /**
558
+ * Layout mode. Default 'compact' (shows default method only).
559
+ * 'expanded' shows the full list with per-row actions.
560
+ */
561
+ mode?: 'compact' | 'expanded';
562
+ /** Theme cascade overrides. */
563
+ themeOverrides?: ThemeTokenOverrides;
564
+ /** Dark-mode posture. Default {@code 'auto'}. */
565
+ theme?: 'light' | 'dark' | 'auto';
566
+ /** Fires when the customer clicks "Add" or "Update" — carries
567
+ * {@code clientSecret} for parent-side provider SDK confirmation. */
568
+ onUpdateRequested?: (clientSecret: string, provider: string) => void;
569
+ /** Optional callback fired on any widget error. */
570
+ onError?: (error: WidgetErrorPayload) => void;
571
+ /**
572
+ * INTERNAL — operator Embed Studio preview short-circuit. When
573
+ * provided, the widget bypasses the bridge exchange + BFF call entirely
574
+ * and renders directly against this synthetic payload. Mirrors the
575
+ * {@code _mockUsage} / {@code _mockSubscriptionPage} pattern.
576
+ */
577
+ _mockPaymentMethods?: PaymentMethodListResponse | null;
578
+ }
579
+ declare function AforoPaymentMethod(props: AforoPaymentMethodProps): React.ReactElement;
580
+
581
+ /**
582
+ * AforoUpgradeCancel — upgrade preview + cancel deflection wizard.
583
+ *
584
+ * Spec: FR-WIDGET-UC-1..6 (Section 3.9). Builds on the V50 cancellation
585
+ * feedback infrastructure shipped 2026-05-09 + the storefront-service
586
+ * `EmbedSubscriptionLifecycleController` shipped 2026-05-29 (3 BFF
587
+ * endpoints: preview-change / upgrade / cancel).
588
+ *
589
+ * Two modes, gated by the required `mode` prop:
590
+ *
591
+ * mode='upgrade' → 4 steps: plan picker → preview impact → confirm → result
592
+ * mode='cancel' → 3 steps: reason → tailored deflection → confirm → result
593
+ *
594
+ * Pattern #18: `BridgeClient.previewSubscriptionChange` returns null on
595
+ * failure (read path). Mutations (`upgradeSubscription`, `cancelSubscription`)
596
+ * throw BridgeClientError so the widget can surface inline error banners.
597
+ *
598
+ * FR-SEC-23 reminder: postMessage events are advisory only — the customer
599
+ * backend MUST receive the `subscription.status_changed` webhook as the
600
+ * authoritative signal. Stamped at the `completed` emission site.
601
+ */
602
+
603
+ interface AforoUpgradeCancelProps {
604
+ tenantSlug: string;
605
+ embedKey: string;
606
+ bridgeToken: string;
607
+ /** Subscription this widget targets. */
608
+ subscriptionId: string;
609
+ /** REQUIRED. The Prompt 3 placeholder `'both'` value is removed. */
610
+ mode: 'upgrade' | 'cancel';
611
+ /** 'inline' renders the wizard inline; 'modal' (default) renders in a dialog overlay. */
612
+ renderMode?: 'inline' | 'modal';
613
+ /** Initial ApplyAt selection for upgrade/cancel confirm steps. */
614
+ defaultApplyAt?: ApplyAt;
615
+ /** BCP-47 locale for `Intl.NumberFormat` / `Intl.DateTimeFormat`. */
616
+ locale?: string;
617
+ /** Per-widget theme overrides (highest priority in the cascade). */
618
+ themeOverrides?: ThemeTokenOverrides;
619
+ /** Fires when the customer completes the flow (upgrade applied or cancel scheduled). */
620
+ onCompleted?: (result: UpgradeCancelResultPayload) => void;
621
+ /** Fires when the customer dismisses (Esc / backdrop / accepted deflection). */
622
+ onCancelled?: () => void;
623
+ /** Fires on any error (preview / mutation). Mirrors `aforo.upgrade-cancel.error`. */
624
+ onError?: (error: WidgetErrorPayload) => void;
625
+ /** Test seam — short-circuits offerings fetch in operator preview. */
626
+ _mockOfferings?: HeadlessConfigResponse | null;
627
+ }
628
+ declare function AforoUpgradeCancel(props: AforoUpgradeCancelProps): React.ReactElement;
629
+
630
+ /**
631
+ * BridgeClient — internal fetch wrapper for the embed plane.
632
+ *
633
+ * This is the customer-browser equivalent of `@aforoai/api-client`. It is
634
+ * NEVER exposed publicly from `src/index.ts` — widgets get a configured
635
+ * instance via the AforoSession singleton. Customers don't construct one
636
+ * directly.
637
+ *
638
+ * Endpoints touched (storefront-service, shipped in Prompt 1):
639
+ * POST /api/v1/portal/embed/bridge/exchange — bridge JWT → session JWT
640
+ * POST /api/v1/portal/embed/bridge/refresh — session JWT refresh (Prompt 8)
641
+ * POST /api/v1/portal/embed/telemetry — telemetry batch sink
642
+ * GET /api/v1/portal/embed/tenant-config/{slug} — brand kit (this Prompt)
643
+ * GET /api/v1/portal/embed/health — public liveness
644
+ *
645
+ * Headers:
646
+ * Authorization: Bearer <embed_key> (on every embed-key-gated call)
647
+ * X-Embed-Domain: <window.location.origin> (for the backend Origin guard
648
+ * cross-check; this is on top of the browser-sent `Origin` header)
649
+ * Idempotency-Key: <UUID v4> (FR-SEC-19 on mutations)
650
+ *
651
+ * Error mapping is structured: callers get typed errors with `status` +
652
+ * `code` fields so widgets can choose their retry / display behavior. The
653
+ * Pattern #18 fail-soft posture (return null / safe fallback) applies to
654
+ * READ paths only; WRITE paths throw so widgets can show "couldn't save…"
655
+ * banners instead of silently no-op'ing.
656
+ */
657
+
658
+ type FetchLike = (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
659
+ interface BridgeClientOptions {
660
+ /** Default `https://embed.aforo.ai`. Override for tests / dev. */
661
+ embedBaseUrl?: string;
662
+ /** Embed key (Bearer token for all embed-key-gated endpoints). */
663
+ embedKey: string;
664
+ /** Tenant slug — included as `X-Embed-Tenant` header for backend audit. */
665
+ tenantSlug: string;
666
+ /** Pluggable fetch — defaults to `globalThis.fetch`. Test seam. */
667
+ fetch?: FetchLike;
668
+ /** Override timeout. Default 15s per outbound call. */
669
+ timeoutMs?: number;
670
+ }
671
+ interface BridgeExchangeResponse {
672
+ sessionJwt: string;
673
+ expiresAt: number;
674
+ customerId: string | null;
675
+ }
676
+ declare class BridgeClient {
677
+ private readonly baseUrl;
678
+ private readonly embedKey;
679
+ private readonly tenantSlug;
680
+ private readonly fetchImpl;
681
+ private readonly timeoutMs;
682
+ static demoModeProvider: DemoModeBridgeProvider | null;
683
+ /**
684
+ * Install a demo-mode provider that intercepts every BridgeClient call.
685
+ * Called once by the sandbox playground at startup. Real consumers
686
+ * never invoke this — wrapping the static setter in a top-level
687
+ * function ({@code installDemoModeProvider} from src/index.ts) avoids
688
+ * exposing BridgeClient itself in the public API.
689
+ *
690
+ * Pass {@code null} to clear (used by tests in `afterEach`).
691
+ */
692
+ static setDemoModeProvider(provider: DemoModeBridgeProvider | null): void;
693
+ /** True when a demo-mode provider is currently installed. */
694
+ static isDemoMode(): boolean;
695
+ constructor(opts: BridgeClientOptions);
696
+ private withTimeout;
697
+ private buildHeaders;
698
+ private send;
699
+ private parseError;
700
+ /** Bridge-token → Aforo session JWT. Always a mutation (idempotency-key). */
701
+ exchange(bridgeToken: string): Promise<BridgeExchangeResponse>;
702
+ /**
703
+ * Tenant brand kit — Pattern #18 fail-soft. Returns `null` on any error so
704
+ * the ThemeReader can fall through to the neutral default cascade.
705
+ */
706
+ getTenantBrandKit(slug?: string): Promise<TenantBrandKit | null>;
707
+ /**
708
+ * Headless config — the PricingCard widget's primary read path.
709
+ *
710
+ * Mirrors `GET /api/v1/portal/headless/config` which returns
711
+ * `{branding, offerings, ratePlans, themeTokens, customPages, ...}`
712
+ * denormalized JSON. The endpoint is 60s Caffeine-cached server-side; we
713
+ * cache once per widget instance on the client.
714
+ *
715
+ * Pattern #18 fail-soft — returns `null` on transport failure, 404, or
716
+ * malformed JSON. Widgets fall through to their empty / error state and
717
+ * present a retry button instead of crashing.
718
+ *
719
+ * NOTE: this surface is anonymous-mode-safe — the embed key in
720
+ * Authorization is sufficient; no bridge token required.
721
+ */
722
+ fetchHeadlessConfig(slug?: string): Promise<HeadlessConfigResponse | null>;
723
+ /**
724
+ * Telemetry sink — never throws; respects FR-OBS-1 fail-soft contract.
725
+ * Consent gating + sampling is the TelemetryReporter's job, not here.
726
+ */
727
+ sendTelemetry(batch: ReadonlyArray<Record<string, unknown>>): Promise<boolean>;
728
+ /**
729
+ * Anti-double-subscribe lookup for the AforoSubscribeButton widget.
730
+ *
731
+ * <p>Mirrors {@code GET /api/v1/portal/embed/subscriptions/by-offering/{offeringId}}.
732
+ * Customer is resolved server-side from the session JWT (Pattern #7) — never
733
+ * from the URL or body. Cross-customer probes return
734
+ * {@code hasActiveSubscription: false} with the same shape as a legitimate
735
+ * "not subscribed" response (Pattern #9 — no existence-leak).</p>
736
+ *
737
+ * <p>Pattern #18 fail-soft: returns {@code null} on any network /
738
+ * non-2xx failure so the widget can degrade to a working CTA (better
739
+ * UX than permanently disabling on a transient outage).</p>
740
+ *
741
+ * @param offeringId offering to check
742
+ * @param sessionJwt customer-session JWT (from {@code AforoSession.getSessionJwt()})
743
+ * @returns subscription state or {@code null} on failure
744
+ */
745
+ fetchSubscriptionForOffering(offeringId: string, sessionJwt: string): Promise<SubscriptionForOfferingResponse | null>;
746
+ /**
747
+ * Hosted-checkout initiation for the AforoSubscribeButton widget in
748
+ * {@code redirect} mode.
749
+ *
750
+ * <p>Mirrors {@code POST /api/v1/portal/embed/checkout/initiate}. The
751
+ * underlying CheckoutSessionService delegates to the existing
752
+ * V43 hosted-checkout infrastructure — this method is a thin embed-tier
753
+ * adapter on top.</p>
754
+ *
755
+ * <p>Idempotency: the caller-supplied {@code request.idempotencyKey}
756
+ * becomes the {@code Idempotency-Key} request header per FR-SEC-19.
757
+ * Same key + same payload → cached response replay (200 OK with prior
758
+ * body). Same key + different payload → 409 Conflict (the
759
+ * {@link BridgeClientError} carries the status + 'IDEMPOTENCY_CONFLICT'
760
+ * code).</p>
761
+ *
762
+ * <p>Write path — throws {@link BridgeClientError} on any failure so the
763
+ * widget can surface an actionable error banner instead of silently
764
+ * no-op'ing. Pattern #18 fail-soft does NOT apply to mutation paths.</p>
765
+ *
766
+ * @param request offering id + return URL + idempotency key
767
+ * @param sessionJwt customer-session JWT
768
+ * @returns checkout URL + session id + expiry the widget redirects to
769
+ * @throws BridgeClientError on transport / non-2xx / malformed response
770
+ */
771
+ initiateCheckout(request: InitiateCheckoutRequest, sessionJwt: string): Promise<InitiateCheckoutResponse>;
772
+ /**
773
+ * Paginated invoice list — primary read path for the AforoInvoiceList
774
+ * widget.
775
+ *
776
+ * <p>Mirrors {@code GET /api/v1/portal/embed/invoices} with query
777
+ * parameters {@code page}, {@code size}, {@code status} (lowercase
778
+ * filter chip value), {@code search}, {@code sort}. Server-side sort
779
+ * allowlist is {@code issuedAt,desc|asc} or {@code total,desc|asc};
780
+ * the widget defaults to {@code issuedAt,desc}.</p>
781
+ *
782
+ * <p><b>Pattern #18 fail-soft:</b> returns a safe empty page on any
783
+ * transport / non-2xx / malformed JSON. The widget treats the empty
784
+ * page identically to a real "no invoices" response and renders the
785
+ * empty state with a retry affordance.</p>
786
+ *
787
+ * @param filter page + size + status + search + sort
788
+ * @param sessionJwt customer-session JWT (from {@code AforoSession.getSessionJwt()})
789
+ * @returns paginated invoice page (always non-null)
790
+ */
791
+ fetchInvoices(filter: InvoiceListFilter, sessionJwt: string): Promise<InvoicePageResponse>;
792
+ /**
793
+ * Per-invoice PDF download URL.
794
+ *
795
+ * <p>Mirrors {@code GET /api/v1/portal/embed/invoices/{invoiceId}/pdf-url}.
796
+ * v1 returns a session-authenticated companion-endpoint URL the widget
797
+ * opens in a new tab. v2 will return a standalone JWS-signed URL —
798
+ * {@code downloadUrl} consumers (the widget's {@code window.open} call)
799
+ * don't change.</p>
800
+ *
801
+ * <p><b>Pattern #18 fail-soft:</b> returns {@code null} on any failure
802
+ * so the widget can surface an inline tooltip near the Download button
803
+ * without exploding. Cross-customer access returns 404 same as
804
+ * not-found (Pattern #9 existence-leak prevention).</p>
805
+ */
806
+ fetchInvoicePdfUrl(invoiceId: string, sessionJwt: string): Promise<InvoicePdfUrlResponse | null>;
807
+ /**
808
+ * Pay Now mutation — Phase 0 stub.
809
+ *
810
+ * <p>Mirrors {@code POST /api/v1/portal/embed/invoices/{invoiceId}/pay/initiate}.
811
+ * Caller MUST supply an {@code idempotencyKey} (UUID v4 generated client-
812
+ * side per CTA click) which becomes the {@code Idempotency-Key} request
813
+ * header per FR-SEC-19. Same key + same payload → cached replay; same key
814
+ * + different payload → 409 Conflict (the {@link BridgeClientError}
815
+ * carries the status + {@code 'IDEMPOTENCY_CONFLICT'} code).</p>
816
+ *
817
+ * <p><b>Phase 0 contract:</b> the BFF always returns {@code {supported:
818
+ * false, reason: 'EMBEDDED_CHECKOUT_NOT_YET_WIRED'}} with HTTP 200. The
819
+ * widget consumes the response shape (not the {@code supported} flag) so
820
+ * Prompt 7's drop-in replacement that flips {@code supported} to true and
821
+ * populates {@code checkoutUrl} doesn't change the call site.</p>
822
+ *
823
+ * <p>Write path — throws {@link BridgeClientError} on any failure. Pattern
824
+ * #18 fail-soft does NOT apply to mutations.</p>
825
+ *
826
+ * @param invoiceId invoice id (path param)
827
+ * @param request returnUrl payload
828
+ * @param idempotencyKey UUID v4 — generated client-side per CTA click
829
+ * @param sessionJwt customer-session JWT
830
+ * @returns Phase 0 stub today ({@code supported: false}); v2 returns checkoutUrl
831
+ * @throws BridgeClientError on transport / non-2xx / malformed response
832
+ */
833
+ initiateInvoicePayment(invoiceId: string, request: InitiateInvoicePaymentRequest, idempotencyKey: string, sessionJwt: string): Promise<InitiateInvoicePaymentResponse>;
834
+ /**
835
+ * Create a new checkout cart — primary entry point for the CheckoutFlow
836
+ * widget. Returns gateway secrets ONCE in the response per FR-SEC-9; the
837
+ * widget MUST sessionStorage them (subsequent {@link getCart} reads
838
+ * return null for those fields).
839
+ *
840
+ * <p>Cart-type-specific behavior:</p>
841
+ * <ul>
842
+ * <li>{@code SUBSCRIBE} — anti-double-subscribe check fires server-side;
843
+ * 409 Conflict with {@code code: 'ALREADY_SUBSCRIBED'} when an
844
+ * active subscription already exists for this (customer, offering)</li>
845
+ * <li>{@code INVOICE_PAYMENT} — already-paid check fires server-side;
846
+ * 409 Conflict with {@code code: 'INVOICE_ALREADY_PAID'} when the
847
+ * invoice is already in PAID status</li>
848
+ * </ul>
849
+ *
850
+ * @param request cartType + targetId + returnUrl
851
+ * @param idempotencyKey UUID v4 — generated client-side per CTA click
852
+ * @param sessionJwt customer-session JWT
853
+ * @returns cart with gateway secrets populated ONCE
854
+ * @throws BridgeClientError on transport / non-2xx / malformed response
855
+ */
856
+ createCart(request: CreateCartRequest, idempotencyKey: string, sessionJwt: string): Promise<CheckoutCartResponse>;
857
+ /**
858
+ * Read cart state — used by the widget to refresh after async events
859
+ * (3DS challenge return, parent-side inbound messages). Pattern #18
860
+ * fail-soft: returns null on any transport / non-2xx failure.
861
+ *
862
+ * <p>Gateway secrets are NOT returned on this path (per FR-SEC-9 — only
863
+ * the cart-create response includes them). Widget reads them from
864
+ * sessionStorage instead.</p>
865
+ */
866
+ getCart(cartId: string, sessionJwt: string): Promise<CheckoutCartResponse | null>;
867
+ /**
868
+ * Update billing address + display name (Step 1 → Step 2 transition).
869
+ * All fields optional — pre-filled / returning customers may skip this
870
+ * step entirely (widget transitions straight to {@code payment} phase).
871
+ *
872
+ * @throws BridgeClientError on transport / non-2xx / malformed response
873
+ */
874
+ updateCustomerDetails(cartId: string, request: UpdateCustomerDetailsRequest, idempotencyKey: string, sessionJwt: string): Promise<CheckoutCartResponse>;
875
+ /**
876
+ * Record the payment method id captured by the provider iframe
877
+ * (Step 2 — after tokenization succeeds client-side via Stripe Elements /
878
+ * Razorpay Drop-in / PayPal Smart Buttons).
879
+ *
880
+ * <p><b>Intent-swap defense:</b> the {@code gatewayPaymentIntentId} MUST
881
+ * match the cart's stored intent id. Backend rejects with 400 if they
882
+ * differ — defense against an attacker swapping in a different cart's
883
+ * intent. See Prompt 7A round-2 audit.</p>
884
+ *
885
+ * @throws BridgeClientError on transport / non-2xx / 400 intent-swap
886
+ */
887
+ initiatePayment(cartId: string, request: InitiateCartPaymentRequest, idempotencyKey: string, sessionJwt: string): Promise<CheckoutCartResponse>;
888
+ /**
889
+ * Confirm the cart — atomic provisioning step (creates subscription via
890
+ * pricing-service for SUBSCRIBE carts; marks invoice paid via billing-service
891
+ * for INVOICE_PAYMENT carts). Either succeeds end-to-end or rolls back.
892
+ *
893
+ * <p>Returns a {@link ConfirmCartResponse} with cart-type-specific fields
894
+ * populated (subscriptionId for SUBSCRIBE, invoiceId for INVOICE_PAYMENT).
895
+ * The widget's success screen reads these to render the "Welcome to ..."
896
+ * affordance.</p>
897
+ *
898
+ * <p><b>402 Payment Required</b> indicates the gateway declined the card
899
+ * or returned a 3DS challenge — the {@link BridgeClientError} carries
900
+ * status=402 and (parsed by the widget) code in {@code PAYMENT_FAILED}
901
+ * or {@code REQUIRES_ACTION}.</p>
902
+ *
903
+ * @throws BridgeClientError on any failure (mutations never silent-fail)
904
+ */
905
+ confirmCart(cartId: string, idempotencyKey: string, sessionJwt: string): Promise<ConfirmCartResponse>;
906
+ /**
907
+ * Cancel an in-progress cart. Best-effort gateway cancellation runs
908
+ * server-side; widget surfaces the cancelled state regardless of upstream
909
+ * success. 204 No Content on success.
910
+ *
911
+ * <p>Cart-already-COMPLETED returns 409 — the widget shouldn't allow
912
+ * cancel after success, but defense-in-depth.</p>
913
+ *
914
+ * @throws BridgeClientError on transport / non-2xx
915
+ */
916
+ cancelCart(cartId: string, sessionJwt: string): Promise<void>;
917
+ /**
918
+ * Shared parser for the 4 mutation methods that return CartResponse.
919
+ * Throws BridgeClientError on missing required fields so the widget gets
920
+ * a clear "upstream malformed" signal rather than crashing on a downstream
921
+ * undefined access.
922
+ */
923
+ private parseCartResponse;
924
+ /** Public liveness probe. Returns null on failure (not an error). */
925
+ health(): Promise<{
926
+ status: string;
927
+ } | null>;
928
+ /**
929
+ * Request a magic-link email. Always resolves with {@code {accepted: true}}
930
+ * on the wire — anti-enumeration per FR-SEC-21. Pattern #18 fail-soft:
931
+ * transport failure returns a synthesized {@code {accepted: true}} envelope
932
+ * so the form UX never surfaces "we couldn't send the email" when the
933
+ * underlying cause is "you're offline" (which the user already knows).
934
+ *
935
+ * Idempotency-Key is auto-generated per call when none is supplied —
936
+ * matches the rest of the BridgeClient mutation pattern.
937
+ *
938
+ * @param request body shape mirroring backend {@code RequestMagicLinkRequest}
939
+ * @param idempotencyKey caller-supplied UUID v4; auto-generated when absent
940
+ */
941
+ requestMagicLink(request: RequestMagicLinkRequest, idempotencyKey?: string): Promise<RequestMagicLinkResponse>;
942
+ /**
943
+ * Verify a plaintext magic-link token + mint an Aforo session JWT.
944
+ *
945
+ * Mutations propagate errors — this path runs ON the URL-param-consumption
946
+ * branch of {@code AforoSession.init({magicLinkMode: true})}; widgets need
947
+ * to know if the verify failed so they can render the sign-in prompt UI.
948
+ *
949
+ * @throws BridgeClientError on any non-2xx — typed code surfaces in the
950
+ * {@code aforo.session.magic_link_failed} event payload
951
+ */
952
+ verifyMagicLink(token: string, externalId?: string): Promise<VerifyMagicLinkResponse>;
953
+ /**
954
+ * Paginated subscription list — primary read path for the
955
+ * AforoSubscriptionManager widget.
956
+ *
957
+ * <p>Mirrors {@code GET /api/v1/portal/embed/subscriptions} with query
958
+ * parameters {@code page}, {@code size}, {@code status} (uppercase filter
959
+ * chip value), {@code sort}. Server-side sort allowlist defaults to
960
+ * {@code startedAt,desc}; the widget surfaces the same sort vocabulary.</p>
961
+ *
962
+ * <p><b>Pattern #18 fail-soft:</b> returns a safe empty page on any
963
+ * transport / non-2xx / malformed JSON. The widget treats the empty page
964
+ * identically to a real "no subscriptions" response and renders the empty
965
+ * state with a retry affordance.</p>
966
+ *
967
+ * @param filter page + size + status + sort
968
+ * @param sessionJwt customer-session JWT (from {@code AforoSession.getSessionJwt()})
969
+ * @returns paginated subscription page (always non-null)
970
+ */
971
+ fetchSubscriptions(filter: SubscriptionListFilter, sessionJwt: string): Promise<SubscriptionPageResponse>;
972
+ /**
973
+ * Current-period usage projection — primary read path for the
974
+ * AforoUsageMeter widget.
975
+ *
976
+ * <p>Mirrors {@code GET /api/v1/portal/embed/subscriptions/{id}/usage}.
977
+ * The BFF performs a Pattern #9 ownership check before forwarding to
978
+ * billing-service; cross-customer probes return 404 same as not-found.</p>
979
+ *
980
+ * <p><b>Pattern #18 fail-soft:</b> returns {@code null} on any failure so
981
+ * the widget can render an inline error banner with retry affordance
982
+ * without crashing the consumer's React tree. The 404 case (cross-customer
983
+ * or genuinely not found) also returns null — the widget surfaces this
984
+ * as "couldn't load usage" since the customer has no actionable distinction
985
+ * between the two.</p>
986
+ *
987
+ * @param subscriptionId subscription id (path param)
988
+ * @param sessionJwt customer-session JWT
989
+ * @returns usage payload or null on failure
990
+ */
991
+ fetchUsage(subscriptionId: string, sessionJwt: string): Promise<UsageResponse | null>;
992
+ /**
993
+ * Preview the prorated impact of switching to {@code targetOfferingId}.
994
+ *
995
+ * <p>Pattern #18 fail-soft — returns {@code null} on transport / non-2xx
996
+ * (including 400 NO_CHANGE) / malformed JSON. The widget renders an
997
+ * inline error with a Retry button when null and never blocks Back
998
+ * navigation. NO Idempotency-Key on the wire (this is a read).</p>
999
+ */
1000
+ previewSubscriptionChange(subscriptionId: string, request: PreviewChangeRequest, sessionJwt: string): Promise<PreviewChangeResponse | null>;
1001
+ /**
1002
+ * Apply an upgrade or downgrade. Mutation path — throws
1003
+ * {@link BridgeClientError} on transport / non-2xx / malformed JSON.
1004
+ *
1005
+ * <p>Idempotency: caller passes UUID v4 {@code idempotencyKey}. Same
1006
+ * key + same body → cached 200 replay; same key + different body →
1007
+ * 409 IDEMPOTENCY_CONFLICT (the error carries the typed code).</p>
1008
+ */
1009
+ upgradeSubscription(subscriptionId: string, request: UpgradeRequest, idempotencyKey: string, sessionJwt: string): Promise<UpgradeResponse>;
1010
+ /**
1011
+ * Cancel a subscription with V50 feedback payload. Mutation path —
1012
+ * throws {@link BridgeClientError} on transport / non-2xx / malformed
1013
+ * JSON.
1014
+ *
1015
+ * <p>The backend maps the widget's {@code retentionOutcome} values onto
1016
+ * V50 audit categories; the widget sends the values defined in
1017
+ * {@link RetentionOutcome} verbatim.</p>
1018
+ */
1019
+ cancelSubscription(subscriptionId: string, request: CancelWithFeedbackRequest, idempotencyKey: string, sessionJwt: string): Promise<CancelResponse>;
1020
+ fetchPaymentMethods(sessionJwt: string): Promise<PaymentMethodListResponse>;
1021
+ createPaymentMethodSetupIntent(sessionJwt: string, idempotencyKey: string): Promise<SetupIntentResponse | null>;
1022
+ setDefaultPaymentMethod(methodId: string, sessionJwt: string): Promise<void>;
1023
+ deletePaymentMethod(methodId: string, sessionJwt: string): Promise<void>;
1024
+ }
1025
+
1026
+ /**
1027
+ * AforoSession — singleton holding the Aforo session JWT in memory.
1028
+ *
1029
+ * Per §17.16 (Concurrent Widgets — Shared Auth State):
1030
+ * - First widget to mount triggers bridge-exchange
1031
+ * - Subsequent widgets reuse the cached session JWT
1032
+ * - Refresh happens once (~5 min before exp), broadcast to all subscribers
1033
+ * - Logout clears + fires `aforo.session.cleared`
1034
+ *
1035
+ * Storage: in-memory ONLY (FR-AUTH-11). Never localStorage / cookies. Page
1036
+ * refresh deliberately drops the session — customer's frontend re-runs the
1037
+ * bridge-token flow.
1038
+ *
1039
+ * Multi-tenant: keyed by `tenantSlug` so an agency portal embedding multiple
1040
+ * customers' billing dashboards in the same browser doesn't conflate auth.
1041
+ *
1042
+ * The shared SSE connection (FR-PUSH-3) is stubbed here: `subscribe()`
1043
+ * records the handler in an internal map and returns an unsubscribe fn.
1044
+ * The real EventSource wiring lands in Prompt 8 — this prompt focuses on
1045
+ * the connection-management API contract so widgets can be coded against it
1046
+ * today without rewriting once SSE arrives.
1047
+ */
1048
+
1049
+ type RefreshCallback = (state: SessionState) => void;
1050
+ type SseHandler = (event: {
1051
+ type: string;
1052
+ data: unknown;
1053
+ }) => void;
1054
+ /** Public singleton API. */
1055
+ declare const AforoSession: {
1056
+ /**
1057
+ * Initialise (or reuse) the session for a tenant. Idempotent — calling
1058
+ * twice with the same `tenantSlug` returns the existing entry instead of
1059
+ * re-running bridge exchange (React Strict Mode safety).
1060
+ *
1061
+ * Does NOT block on bridge exchange — kicks it off in the background.
1062
+ * Callers wanting the JWT in hand should `await getSessionJwt()`.
1063
+ */
1064
+ init(config: SessionConfig): SessionState;
1065
+ /**
1066
+ * Returns the cached JWT — fetching one via bridge exchange if missing
1067
+ * or expired. Concurrent callers share the single in-flight promise.
1068
+ * Returns `null` for anonymous mode (no bridgeToken configured).
1069
+ */
1070
+ getSessionJwt(tenantSlug: string): Promise<string | null>;
1071
+ /** Subscribe to refresh / clear events. Returns an unsubscribe fn. */
1072
+ onSessionRefreshed(tenantSlug: string, cb: RefreshCallback): () => void;
1073
+ /** Snapshot the current state. Useful for tests + debugging. */
1074
+ getState(tenantSlug: string): SessionState | null;
1075
+ /**
1076
+ * Public accessor for the per-tenant BridgeClient. Widgets that need to
1077
+ * hit embed-key-gated endpoints (e.g. PricingCard fetching headless
1078
+ * config) consume this. Returns `null` if `init()` hasn't been called
1079
+ * for the tenant yet — callers should treat that the same as "not
1080
+ * available; surface error state".
1081
+ *
1082
+ * The BridgeClient instance is shared across all widgets on the same
1083
+ * tenant (per §17.16 concurrent-widget contract).
1084
+ */
1085
+ getBridgeClient(tenantSlug: string): BridgeClient | null;
1086
+ /**
1087
+ * Subscribe to a push-event type (FR-PUSH-2). v1: stub — records the
1088
+ * handler; the real SSE connection lands in Prompt 8 and will fan
1089
+ * events out via this same handler registry.
1090
+ */
1091
+ subscribe(tenantSlug: string, eventType: string, handler: SseHandler): () => void;
1092
+ /** Logout: clear in-memory state and fire `aforo.session.cleared`. */
1093
+ logout(tenantSlug: string): void;
1094
+ /**
1095
+ * Full teardown — clears timers, handlers, removes the entry. Useful for
1096
+ * tests + multi-tenant agency portals tearing down per-tenant scopes.
1097
+ */
1098
+ destroy(tenantSlug: string): void;
1099
+ /**
1100
+ * Internal: dispatch an SSE event to subscribers. Called by Prompt 8's
1101
+ * SSE wiring; exposed here so tests can drive it without spinning up
1102
+ * EventSource. Not part of the public surface; not exported from
1103
+ * `src/index.ts`.
1104
+ */
1105
+ _dispatch(tenantSlug: string, event: {
1106
+ type: string;
1107
+ data: unknown;
1108
+ }): number;
1109
+ /**
1110
+ * Programmatic magic-link request. Resolves with {@code {accepted: true}}
1111
+ * on success, transport failure, AND server rate-limit (anti-enumeration).
1112
+ * Only 400 / 409 errors throw — those are developer-side mistakes
1113
+ * (invalid returnUrl, Idempotency-Key collision) that the form must
1114
+ * surface so the developer can fix them.
1115
+ *
1116
+ * Emits {@code aforo.session.magic_link_requested} before the fetch so
1117
+ * parent pages can update progress UI; emits no failure event on the
1118
+ * silent-200 path (per FR-SEC-21 — silent at the wire is silent in the
1119
+ * event stream too).
1120
+ *
1121
+ * @param tenantSlug per-tenant session lookup
1122
+ * @param email recipient email (validated server-side)
1123
+ * @param options.externalId optional binding for the resolved customer id
1124
+ * @param options.returnUrl defaults to {@code window.location.href}
1125
+ */
1126
+ requestMagicLink(tenantSlug: string, email: string, options?: {
1127
+ externalId?: string;
1128
+ returnUrl?: string;
1129
+ }): Promise<{
1130
+ accepted: boolean;
1131
+ }>;
1132
+ /**
1133
+ * Direct magic-link verify. Most consumers use the
1134
+ * {@code init({magicLinkMode: true})} URL-param branch instead — this
1135
+ * method is exposed for (a) tests, (b) future widgets that want to
1136
+ * accept a token from a parent-supplied source instead of the URL.
1137
+ *
1138
+ * Emits {@code aforo.session.magic_link_verified} on success +
1139
+ * {@code aforo.session.magic_link_failed} on any failure.
1140
+ *
1141
+ * @throws BridgeClientError on verify failure (mapped to typed code)
1142
+ */
1143
+ verifyMagicLink(tenantSlug: string, token: string, externalId?: string): Promise<SessionState>;
1144
+ /** Test seam: clear ALL session state. */
1145
+ _resetForTesting(): void;
1146
+ /** Test seam: list active tenant slugs. */
1147
+ _listTenantsForTesting(): string[];
1148
+ };
1149
+
1150
+ declare function installDemoModeProvider(provider: DemoModeBridgeProvider | null): void;
1151
+
1152
+ /**
1153
+ * MagicLinkFallback — shared form rendered by widgets when the customer
1154
+ * needs to authenticate via email magic link instead of a host-portal
1155
+ * bridge token.
1156
+ *
1157
+ * Prompt 9 / FR-AUTH-6 (2026-05-29).
1158
+ *
1159
+ * <p>First non-widget component in the {@code @aforoai/storefront-widgets}
1160
+ * public surface. Widgets that support magic-link mode (Prompt 9: InvoiceList;
1161
+ * later prompts: SubscribeButton, CheckoutFlow) mount this when they detect
1162
+ * "no session JWT + magicLinkMode=true + no URL-param token to consume".</p>
1163
+ *
1164
+ * <h2>UX contract</h2>
1165
+ * <ul>
1166
+ * <li>Single email input + submit button.</li>
1167
+ * <li>Submit → fires {@link AforoSession.requestMagicLink} → success
1168
+ * message ("check your inbox") regardless of whether the email
1169
+ * actually exists (anti-enumeration per FR-SEC-21).</li>
1170
+ * <li>Only developer-side errors (returnUrl rejection 400 / Idempotency
1171
+ * collision 409) surface as inline error banners.</li>
1172
+ * <li>Theme cascade applied via {@code data-aforo-magic-link-fallback}
1173
+ * CSS vars so customer-side theming can recolor without prop drilling.</li>
1174
+ * <li>{@code prefers-reduced-motion} honoured on the loading spinner.</li>
1175
+ * <li>Aria roles: form has {@code aria-label}, status message
1176
+ * {@code role="status"} aria-live polite, error message
1177
+ * {@code role="alert"}.</li>
1178
+ * </ul>
1179
+ *
1180
+ * <h2>Component contract</h2>
1181
+ * <p>Stateless from the parent's perspective — internal state machine
1182
+ * tracks idle / submitting / success / error transitions. Parent supplies:</p>
1183
+ * <ul>
1184
+ * <li>{@link AforoSession} already initialised for the tenant (the widget
1185
+ * wrapper takes care of this before mounting the fallback).</li>
1186
+ * <li>{@code widgetDisplayName} for the header copy
1187
+ * (e.g. "your invoices" → "Sign in to view your invoices").</li>
1188
+ * <li>Optional {@code externalId} — forwarded to the request body when the
1189
+ * host portal already knows the customer's external id.</li>
1190
+ * <li>Optional {@code onLinkSent} callback — fires after a successful
1191
+ * request (parent can update its own UI / log analytics).</li>
1192
+ * </ul>
1193
+ */
1194
+
1195
+ interface MagicLinkFallbackProps {
1196
+ /** Tenant slug (same as the widget's). */
1197
+ tenantSlug: string;
1198
+ /**
1199
+ * Human-readable phrase for the header copy. Examples:
1200
+ * "your invoices" → "Sign in to view your invoices"
1201
+ * "your subscription" → "Sign in to manage your subscription"
1202
+ */
1203
+ widgetDisplayName: string;
1204
+ /**
1205
+ * Optional binding for the resolved customer id. When the host portal
1206
+ * already knows the external id (e.g. it's in the URL or session
1207
+ * storage), pass it here so the verify path can resolve the Aforo
1208
+ * customer immediately.
1209
+ */
1210
+ externalId?: string;
1211
+ /**
1212
+ * Optional {@code returnUrl} override. Defaults to
1213
+ * {@code window.location.href}.
1214
+ */
1215
+ returnUrl?: string;
1216
+ /** Fires after a successful submit. Parent can log / update UI. */
1217
+ onLinkSent?: (maskedEmail: string) => void;
1218
+ /**
1219
+ * Override the submit button label. Default
1220
+ * {@code "Send sign-in link"}.
1221
+ */
1222
+ submitLabel?: string;
1223
+ }
1224
+ declare function MagicLinkFallback(props: MagicLinkFallbackProps): React.ReactElement;
1225
+
1226
+ declare const VERSION: string;
1227
+
1228
+ export { AforoCheckoutFlow, type AforoCheckoutFlowProps, AforoInvoiceList, type AforoInvoiceListProps, AforoPaymentMethod, type AforoPaymentMethodProps, AforoPricingCard, type AforoPricingCardProps, AforoSession, AforoSubscribeButton, type AforoSubscribeButtonProps, AforoSubscriptionManager, type AforoSubscriptionManagerProps, AforoUpgradeCancel, type AforoUpgradeCancelProps, AforoUsageMeter, type AforoUsageMeterProps, ApplyAt, CancelResponse, CancelWithFeedbackRequest, CheckoutCancelledPayload, CheckoutCartResponse, CheckoutCartType, CheckoutConfirmedPayload, ConfirmCartResponse, CreateCartRequest, DemoModeBridgeProvider, HeadlessConfigResponse, InitiateCartPaymentRequest, InitiateCheckoutRequest, InitiateCheckoutResponse, InitiateInvoicePaymentRequest, InitiateInvoicePaymentResponse, InvoiceListFilter, InvoicePageResponse, InvoicePayload, InvoicePdfUrlResponse, InvoiceStatusFilter, MagicLinkFallback, type MagicLinkFallbackProps, OfferingPayload, PreviewChangeRequest, PreviewChangeResponse, RequestMagicLinkRequest, RequestMagicLinkResponse, SessionConfig, SessionState, SubscriptionForOfferingResponse, SubscriptionListFilter, SubscriptionPageResponse, SubscriptionPayload, SubscriptionPayloadV2, SubscriptionStatusFilter, TenantBrandKit, ThemeTokenOverrides, UpdateCustomerDetailsRequest, UpgradeCancelResultPayload, UpgradeRequest, UpgradeResponse, UsageMetricPayload, UsageResponse, UsageThreshold, VERSION, VerifyMagicLinkResponse, WidgetErrorPayload, installDemoModeProvider };