@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 +327 -0
- package/LICENSE +21 -0
- package/README.md +249 -0
- package/SECURITY.md +138 -0
- package/dist/AforoEmbed-BqQOFsl3.d.cts +67 -0
- package/dist/AforoEmbed-CmqjlYlt.d.ts +67 -0
- package/dist/index.cjs +12320 -0
- package/dist/index.d.cts +1228 -0
- package/dist/index.d.ts +1228 -0
- package/dist/index.mjs +12286 -0
- package/dist/loader.js +3 -0
- package/dist/loader.mjs +3 -0
- package/dist/sri.json +15 -0
- package/dist/types-Dm6cp_Cc.d.cts +1185 -0
- package/dist/types-Dm6cp_Cc.d.ts +1185 -0
- package/dist/vanilla/index.cjs +12567 -0
- package/dist/vanilla/index.d.cts +22 -0
- package/dist/vanilla/index.d.ts +22 -0
- package/dist/vanilla/index.mjs +12543 -0
- package/dist/vue/index.cjs +12634 -0
- package/dist/vue/index.d.cts +157 -0
- package/dist/vue/index.d.ts +157 -0
- package/dist/vue/index.mjs +12605 -0
- package/dist/widgets/checkout-flow.js +55 -0
- package/dist/widgets/invoice-list.js +60 -0
- package/dist/widgets/payment-method.js +55 -0
- package/dist/widgets/pricing-card.js +60 -0
- package/dist/widgets/subscribe-button.js +61 -0
- package/dist/widgets/subscription-manager.js +55 -0
- package/dist/widgets/upgrade-cancel.js +55 -0
- package/dist/widgets/usage-meter.js +55 -0
- package/loader.sri.txt +1 -0
- package/package.json +118 -0
package/dist/index.d.ts
ADDED
|
@@ -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.js';
|
|
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.js';
|
|
4
|
+
export { A as AforoEmbed } from './AforoEmbed-CmqjlYlt.js';
|
|
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 };
|