@solvapay/react 1.0.8-preview.9 → 1.0.9

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,2724 @@
1
+ import {
2
+ CurrentPlanCard,
3
+ ExternalLinkGlyph,
4
+ LaunchCustomerPortalButton,
5
+ paymentMethodCache
6
+ } from "../chunk-HWVJL5X6.js";
7
+ import {
8
+ PaywallNotice,
9
+ isPaygPlan
10
+ } from "../chunk-ZAV7CQ4G.js";
11
+ import {
12
+ AmountPicker,
13
+ BalanceBadge,
14
+ CancelledPlanNotice,
15
+ PaymentForm,
16
+ PlanSelector,
17
+ SolvaPayProvider,
18
+ TopupForm,
19
+ UsageMeter,
20
+ createTransportCacheKey,
21
+ formatPrice,
22
+ getMinorUnitsPerMajor,
23
+ merchantCache,
24
+ plansCache,
25
+ productCache,
26
+ useAmountPicker,
27
+ useBalance,
28
+ useCopy,
29
+ useCustomer,
30
+ useMerchant,
31
+ usePlanSelector,
32
+ usePurchase,
33
+ usePurchaseStatus,
34
+ useTransport,
35
+ useUsage
36
+ } from "../chunk-37R5NZGF.js";
37
+ import "../chunk-OUSEQRCT.js";
38
+ import "../chunk-MLKGABMK.js";
39
+
40
+ // src/mcp/adapter.ts
41
+ import { MCP_TOOL_NAMES } from "@solvapay/mcp-core";
42
+ function unwrap(result) {
43
+ if (result.isError) {
44
+ const first2 = result.content?.[0];
45
+ const message = first2 && "text" in first2 && typeof first2.text === "string" ? first2.text : "MCP tool failed";
46
+ throw new Error(message);
47
+ }
48
+ if (result.structuredContent !== void 0) {
49
+ return result.structuredContent;
50
+ }
51
+ const first = result.content?.[0];
52
+ if (first && "text" in first && typeof first.text === "string") {
53
+ try {
54
+ return JSON.parse(first.text);
55
+ } catch {
56
+ throw new Error("MCP tool returned no parseable content");
57
+ }
58
+ }
59
+ throw new Error("MCP tool returned no parseable content");
60
+ }
61
+ function pickDefined(input) {
62
+ const out = {};
63
+ for (const [k, v] of Object.entries(input)) {
64
+ if (v !== void 0) out[k] = v;
65
+ }
66
+ return out;
67
+ }
68
+ function createMcpAppAdapter(app) {
69
+ const callTool = async (name, args = {}) => unwrap(await app.callServerTool({ name, arguments: args }));
70
+ return {
71
+ createPayment: (params) => callTool(MCP_TOOL_NAMES.createPayment, pickDefined({ ...params })),
72
+ processPayment: (params) => callTool(MCP_TOOL_NAMES.processPayment, pickDefined({ ...params })),
73
+ createTopupPayment: (params) => callTool(MCP_TOOL_NAMES.createTopupPayment, pickDefined({ ...params })),
74
+ cancelRenewal: (params) => callTool(MCP_TOOL_NAMES.cancelRenewal, pickDefined({ ...params })),
75
+ reactivateRenewal: (params) => callTool(MCP_TOOL_NAMES.reactivateRenewal, pickDefined({ ...params })),
76
+ activatePlan: (params) => callTool(MCP_TOOL_NAMES.activatePlan, pickDefined({ ...params })),
77
+ createCheckoutSession: (params) => callTool(MCP_TOOL_NAMES.createCheckoutSession, pickDefined({ ...params ?? {} })),
78
+ createCustomerSession: () => callTool(MCP_TOOL_NAMES.createCustomerSession)
79
+ };
80
+ }
81
+
82
+ // src/mcp/index.ts
83
+ import { MCP_TOOL_NAMES as MCP_TOOL_NAMES3 } from "@solvapay/mcp-core";
84
+
85
+ // src/mcp/useStripeProbe.ts
86
+ import { useEffect, useState } from "react";
87
+ import { loadStripe } from "@stripe/stripe-js";
88
+ var STRIPE_PROBE_TIMEOUT_MS = 3e3;
89
+ function useStripeProbe(publishableKey) {
90
+ const [state, setState] = useState(publishableKey ? "loading" : "blocked");
91
+ useEffect(() => {
92
+ if (!publishableKey) {
93
+ setState("blocked");
94
+ return;
95
+ }
96
+ let cancelled = false;
97
+ setState("loading");
98
+ const timeout = new Promise((resolve) => {
99
+ window.setTimeout(() => resolve("blocked"), STRIPE_PROBE_TIMEOUT_MS);
100
+ });
101
+ const load = loadStripe(publishableKey).then((stripe) => stripe ? "ready" : "blocked").catch(() => "blocked");
102
+ Promise.race([load, timeout]).then((result) => {
103
+ if (!cancelled) setState(result);
104
+ });
105
+ return () => {
106
+ cancelled = true;
107
+ };
108
+ }, [publishableKey]);
109
+ return state;
110
+ }
111
+
112
+ // src/mcp/useHostLocale.ts
113
+ import { useEffect as useEffect2, useState as useState2 } from "react";
114
+ var FALLBACK_LOCALE = "en-US";
115
+ function resolveLocale() {
116
+ if (typeof document !== "undefined") {
117
+ const lang = document.documentElement?.lang;
118
+ if (lang) return lang;
119
+ }
120
+ if (typeof navigator !== "undefined" && navigator.language) {
121
+ return navigator.language;
122
+ }
123
+ return FALLBACK_LOCALE;
124
+ }
125
+ function useHostLocale() {
126
+ const [locale, setLocale] = useState2(() => resolveLocale());
127
+ useEffect2(() => {
128
+ if (typeof document === "undefined" || typeof MutationObserver === "undefined") {
129
+ return;
130
+ }
131
+ const root = document.documentElement;
132
+ const observer = new MutationObserver(() => {
133
+ setLocale((prev) => {
134
+ const next = resolveLocale();
135
+ return next === prev ? prev : next;
136
+ });
137
+ });
138
+ observer.observe(root, { attributes: true, attributeFilter: ["lang"] });
139
+ setLocale((prev) => {
140
+ const next = resolveLocale();
141
+ return next === prev ? prev : next;
142
+ });
143
+ return () => {
144
+ observer.disconnect();
145
+ };
146
+ }, []);
147
+ return locale;
148
+ }
149
+
150
+ // src/mcp/hooks/useMcpToolResult.ts
151
+ import { useCallback, useEffect as useEffect3, useRef, useSyncExternalStore } from "react";
152
+ var EMPTY_CONTENT = [];
153
+ function useMcpToolResult(app) {
154
+ const snapshotRef = useRef({
155
+ structuredContent: null,
156
+ content: EMPTY_CONTENT,
157
+ toolName: app.getHostContext?.()?.toolInfo?.tool?.name ?? null
158
+ });
159
+ const subscribe = useCallback(
160
+ (onStoreChange) => {
161
+ const handler = (params) => {
162
+ if (params?.isError) return;
163
+ const toolName = app.getHostContext?.()?.toolInfo?.tool?.name ?? null;
164
+ const content = Array.isArray(params?.content) ? params.content : EMPTY_CONTENT;
165
+ const structuredContent = params?.structuredContent ?? null;
166
+ snapshotRef.current = { structuredContent, content, toolName };
167
+ onStoreChange();
168
+ };
169
+ if (typeof app.addEventListener === "function") {
170
+ app.addEventListener("toolresult", handler);
171
+ return () => app.removeEventListener?.("toolresult", handler);
172
+ }
173
+ const prior = app.ontoolresult;
174
+ app.ontoolresult = handler;
175
+ return () => {
176
+ if (app.ontoolresult === handler) app.ontoolresult = prior;
177
+ };
178
+ },
179
+ [app]
180
+ );
181
+ useEffect3(() => {
182
+ const current = app.getHostContext?.()?.toolInfo?.tool?.name ?? null;
183
+ if (current !== snapshotRef.current.toolName) {
184
+ snapshotRef.current = { ...snapshotRef.current, toolName: current };
185
+ }
186
+ });
187
+ return useSyncExternalStore(
188
+ subscribe,
189
+ () => snapshotRef.current,
190
+ () => snapshotRef.current
191
+ );
192
+ }
193
+
194
+ // src/mcp/bridge.tsx
195
+ import { createContext, useContext, useMemo } from "react";
196
+ import { jsx } from "react/jsx-runtime";
197
+ var NOOP_BRIDGE = {
198
+ notifyModelContext: async () => {
199
+ },
200
+ sendMessage: async () => {
201
+ },
202
+ notifySuccess: async () => {
203
+ }
204
+ };
205
+ var McpBridgeContext = createContext(NOOP_BRIDGE);
206
+ function McpBridgeProvider({ app, messageOnSuccess, children }) {
207
+ const locale = useHostLocale();
208
+ const value = useMemo(() => {
209
+ return {
210
+ notifyModelContext: async (params) => {
211
+ if (typeof app.updateModelContext !== "function") return;
212
+ const payload = params.structuredContent !== void 0 ? { structuredContent: params.structuredContent } : params.text !== void 0 ? { content: [{ type: "text", text: params.text }] } : null;
213
+ if (!payload) return;
214
+ try {
215
+ await app.updateModelContext(payload);
216
+ } catch {
217
+ }
218
+ },
219
+ sendMessage: async (params) => {
220
+ if (typeof app.sendMessage !== "function") return;
221
+ try {
222
+ await app.sendMessage({
223
+ role: "user",
224
+ content: [{ type: "text", text: params.text }]
225
+ });
226
+ } catch {
227
+ }
228
+ },
229
+ notifySuccess: async (evt) => {
230
+ const resolved = messageOnSuccess ? messageOnSuccess(evt) : defaultSuccessCopy(evt, locale);
231
+ if (resolved === null || resolved === void 0) return;
232
+ if (typeof app.sendMessage !== "function") return;
233
+ try {
234
+ await app.sendMessage({
235
+ role: "user",
236
+ content: [{ type: "text", text: resolved }]
237
+ });
238
+ } catch {
239
+ }
240
+ }
241
+ };
242
+ }, [app, locale, messageOnSuccess]);
243
+ return /* @__PURE__ */ jsx(McpBridgeContext.Provider, { value, children });
244
+ }
245
+ function useMcpBridge() {
246
+ return useContext(McpBridgeContext);
247
+ }
248
+ function defaultSuccessCopy(evt, locale) {
249
+ switch (evt.kind) {
250
+ case "topup": {
251
+ const amount = formatPrice(evt.amountMinor, evt.currency, { locale });
252
+ return `Topped up ${amount}. Ready to keep working.`;
253
+ }
254
+ case "plan-activated":
255
+ return `Activated ${evt.planName ?? "plan"}.`;
256
+ default: {
257
+ const _never = evt;
258
+ void _never;
259
+ return "";
260
+ }
261
+ }
262
+ }
263
+
264
+ // src/mcp/bootstrap.ts
265
+ import { MCP_TOOL_NAMES as MCP_TOOL_NAMES2, TOOL_FOR_VIEW, VIEW_FOR_TOOL } from "@solvapay/mcp-core";
266
+
267
+ // ../server/src/index.ts
268
+ import crypto from "crypto";
269
+
270
+ // ../server/src/types/paywall.ts
271
+ function isPaywallStructuredContent(value) {
272
+ if (typeof value !== "object" || value === null || !("kind" in value)) {
273
+ return false;
274
+ }
275
+ const kind = value.kind;
276
+ return kind === "payment_required" || kind === "activation_required";
277
+ }
278
+
279
+ // src/mcp/bootstrap.ts
280
+ var SOLVAPAY_TRANSPORT_TOOL_NAMES = new Set(
281
+ Object.values(MCP_TOOL_NAMES2).filter(
282
+ (name) => !(name in VIEW_FOR_TOOL)
283
+ )
284
+ );
285
+ function isTransportToolName(name) {
286
+ return SOLVAPAY_TRANSPORT_TOOL_NAMES.has(name);
287
+ }
288
+ function classifyHostEntry(app) {
289
+ const toolName = app.getHostContext()?.toolInfo?.tool?.name;
290
+ if (!toolName) return { kind: "other" };
291
+ const view = VIEW_FOR_TOOL[toolName];
292
+ if (view && view in TOOL_FOR_VIEW) {
293
+ return {
294
+ kind: "intent",
295
+ toolName,
296
+ view
297
+ };
298
+ }
299
+ if (isTransportToolName(toolName)) return { kind: "other", toolName };
300
+ return { kind: "data", toolName };
301
+ }
302
+ function inferViewFromHost(app) {
303
+ const classification = classifyHostEntry(app);
304
+ if (classification.kind === "intent") return classification.view;
305
+ return "checkout";
306
+ }
307
+ async function fetchMcpBootstrap(app) {
308
+ const view = inferViewFromHost(app);
309
+ const toolName = TOOL_FOR_VIEW[view];
310
+ const result = await app.callServerTool({
311
+ name: toolName,
312
+ arguments: {}
313
+ });
314
+ return parseBootstrapFromToolResult(result, toolName, view);
315
+ }
316
+ function parseBootstrapFromToolResult(result, toolName, fallbackView) {
317
+ const structured = result.structuredContent;
318
+ const hasBootstrapShape = structured !== void 0 && typeof structured === "object" && typeof structured.productRef === "string";
319
+ if (result.isError && !hasBootstrapShape) {
320
+ const first = result.content?.[0];
321
+ const message = first && "text" in first && typeof first.text === "string" ? first.text : `${toolName} failed`;
322
+ throw new Error(message);
323
+ }
324
+ const ref = structured?.productRef;
325
+ if (!ref) throw new Error(`${toolName} did not return a productRef`);
326
+ const raw = structured?.returnUrl;
327
+ if (typeof raw !== "string" || !/^https?:\/\//i.test(raw)) {
328
+ throw new Error(
329
+ `${toolName} did not return a valid http(s) returnUrl. Set MCP_PUBLIC_BASE_URL on the MCP server.`
330
+ );
331
+ }
332
+ const key = structured?.stripePublishableKey ?? null;
333
+ const requestedView = structured?.view ?? fallbackView;
334
+ const paywall = requestedView === "paywall" && isPaywallStructuredContent(structured?.paywall) ? structured.paywall : void 0;
335
+ const resolvedView = requestedView;
336
+ const nudgeSpec = structured?.nudge;
337
+ const nudge = requestedView === "nudge" && isNudgeSpec(nudgeSpec) ? nudgeSpec : void 0;
338
+ const data = requestedView === "nudge" ? structured?.data : void 0;
339
+ return {
340
+ view: resolvedView,
341
+ productRef: ref,
342
+ stripePublishableKey: typeof key === "string" && key ? key : null,
343
+ returnUrl: raw,
344
+ merchant: structured?.merchant ?? {},
345
+ product: structured?.product ?? { reference: ref },
346
+ plans: Array.isArray(structured?.plans) ? structured.plans : [],
347
+ customer: structured?.customer ?? null,
348
+ ...paywall ? { paywall } : {},
349
+ ...nudge ? { nudge } : {},
350
+ ...data !== void 0 ? { data } : {}
351
+ };
352
+ }
353
+ function isNudgeSpec(value) {
354
+ if (typeof value !== "object" || value === null) return false;
355
+ const kind = value.kind;
356
+ return typeof kind === "string" && (kind === "low-balance" || kind === "cycle-ending" || kind === "approaching-limit");
357
+ }
358
+ function waitForInitialToolResult(app, options = {}) {
359
+ const { timeoutMs = 2e3, signal, fallbackView = "paywall" } = options;
360
+ return new Promise((resolve, reject) => {
361
+ let settled = false;
362
+ let cleanup;
363
+ let timer = null;
364
+ const finish = (outcome) => {
365
+ if (settled) return;
366
+ settled = true;
367
+ if (timer !== null) clearTimeout(timer);
368
+ cleanup?.();
369
+ resolve(outcome);
370
+ };
371
+ const handler = (params) => {
372
+ if (settled) return;
373
+ const toolName = app.getHostContext()?.toolInfo?.tool?.name ?? null;
374
+ if (toolName && isTransportToolName(toolName)) return;
375
+ try {
376
+ const bootstrap = parseBootstrapFromToolResult(
377
+ params,
378
+ toolName ?? "(unknown)",
379
+ fallbackView
380
+ );
381
+ finish({ timedOut: false, bootstrap, toolName, params });
382
+ } catch (err) {
383
+ if (settled) return;
384
+ settled = true;
385
+ if (timer !== null) clearTimeout(timer);
386
+ cleanup?.();
387
+ reject(err instanceof Error ? err : new Error(String(err)));
388
+ }
389
+ };
390
+ if (typeof app.addEventListener === "function") {
391
+ app.addEventListener("toolresult", handler);
392
+ cleanup = () => app.removeEventListener?.("toolresult", handler);
393
+ } else {
394
+ const prior = app.ontoolresult;
395
+ app.ontoolresult = handler;
396
+ cleanup = () => {
397
+ if (app.ontoolresult === handler) app.ontoolresult = prior;
398
+ };
399
+ }
400
+ timer = setTimeout(() => {
401
+ finish({ timedOut: true, bootstrap: null, toolName: null, params: null });
402
+ }, timeoutMs);
403
+ if (signal) {
404
+ if (signal.aborted) {
405
+ finish({ timedOut: true, bootstrap: null, toolName: null, params: null });
406
+ return;
407
+ }
408
+ signal.addEventListener(
409
+ "abort",
410
+ () => {
411
+ finish({ timedOut: true, bootstrap: null, toolName: null, params: null });
412
+ },
413
+ { once: true }
414
+ );
415
+ }
416
+ });
417
+ }
418
+
419
+ // src/mcp/cache-seed.ts
420
+ function seedMcpCaches(initial, config) {
421
+ const now = Date.now();
422
+ const merchantKey = createTransportCacheKey(
423
+ config,
424
+ config.api?.getMerchant || "/api/merchant"
425
+ );
426
+ merchantCache.set(merchantKey, {
427
+ merchant: initial.merchant,
428
+ promise: null,
429
+ timestamp: now
430
+ });
431
+ const productKey = createTransportCacheKey(
432
+ config,
433
+ initial.product.reference,
434
+ initial.product.reference
435
+ );
436
+ productCache.set(productKey, {
437
+ product: initial.product,
438
+ promise: null,
439
+ timestamp: now
440
+ });
441
+ plansCache.set(initial.product.reference, {
442
+ plans: initial.plans,
443
+ promise: null,
444
+ timestamp: now
445
+ });
446
+ if (initial.paymentMethod && initial.customerRef) {
447
+ const paymentMethodKey = createTransportCacheKey(
448
+ config,
449
+ config.api?.getPaymentMethod || "/api/payment-method"
450
+ );
451
+ paymentMethodCache.set(paymentMethodKey, {
452
+ paymentMethod: initial.paymentMethod,
453
+ promise: null,
454
+ timestamp: now
455
+ });
456
+ }
457
+ }
458
+
459
+ // src/mcp/McpApp.tsx
460
+ import { useEffect as useEffect6, useMemo as useMemo4, useRef as useRef4, useState as useState9 } from "react";
461
+ import { VIEW_FOR_TOOL as VIEW_FOR_TOOL2 } from "@solvapay/mcp-core";
462
+
463
+ // src/mcp/McpAppShell.tsx
464
+ import { useEffect as useEffect5, useRef as useRef3, useState as useState8 } from "react";
465
+
466
+ // src/mcp/views/types.ts
467
+ var DEFAULT_MCP_CLASS_NAMES = {
468
+ card: "solvapay-mcp-card",
469
+ stack: "solvapay-mcp-stack",
470
+ heading: "solvapay-mcp-heading",
471
+ muted: "solvapay-mcp-muted",
472
+ button: "solvapay-mcp-button",
473
+ linkButton: "solvapay-mcp-link-button",
474
+ notice: "solvapay-mcp-notice",
475
+ error: "solvapay-mcp-error",
476
+ awaitingHeader: "solvapay-mcp-awaiting-header",
477
+ balanceRow: "solvapay-mcp-balance-row",
478
+ amountPicker: "solvapay-mcp-amount-picker",
479
+ amountOptions: "solvapay-mcp-amount-options",
480
+ amountOption: "solvapay-mcp-amount-option",
481
+ amountCustom: "solvapay-mcp-amount-custom",
482
+ topupForm: "solvapay-mcp-topup-form",
483
+ activationFlow: "solvapay-mcp-activation-flow"
484
+ };
485
+ function resolveMcpClassNames(overrides) {
486
+ if (!overrides) return DEFAULT_MCP_CLASS_NAMES;
487
+ const out = { ...DEFAULT_MCP_CLASS_NAMES };
488
+ for (const key of Object.keys(overrides)) {
489
+ const value = overrides[key];
490
+ if (value !== void 0) out[key] = value;
491
+ }
492
+ return out;
493
+ }
494
+
495
+ // src/mcp/views/detail-cards.tsx
496
+ import { jsx as jsx2, jsxs } from "react/jsx-runtime";
497
+ function DetailRow({
498
+ label,
499
+ value,
500
+ mono,
501
+ muted,
502
+ labelMuted
503
+ }) {
504
+ return /* @__PURE__ */ jsxs("div", { className: "solvapay-mcp-detail-row", children: [
505
+ label ? /* @__PURE__ */ jsx2("span", { className: `solvapay-mcp-detail-label ${labelMuted ?? ""}`.trim(), children: label }) : null,
506
+ /* @__PURE__ */ jsx2(
507
+ "span",
508
+ {
509
+ className: `solvapay-mcp-detail-value${mono ? " solvapay-mcp-detail-value-mono" : ""} ${muted ?? ""}`.trim(),
510
+ children: value
511
+ }
512
+ )
513
+ ] });
514
+ }
515
+ function McpCustomerDetailsCard({
516
+ classNames,
517
+ onTopup,
518
+ hideBalance
519
+ }) {
520
+ const cx = resolveMcpClassNames(classNames);
521
+ const { name, email, customerRef, loading } = useCustomer();
522
+ const { credits, displayCurrency, creditsPerMinorUnit, displayExchangeRate } = useBalance();
523
+ const locale = useHostLocale();
524
+ if (loading && !customerRef) {
525
+ return /* @__PURE__ */ jsxs("div", { className: cx.card, "aria-busy": "true", children: [
526
+ /* @__PURE__ */ jsx2("h2", { className: cx.heading, children: "Your details" }),
527
+ /* @__PURE__ */ jsx2("p", { className: cx.muted, children: "Loading\u2026" })
528
+ ] });
529
+ }
530
+ const displayName = name?.trim() || "Customer";
531
+ const showBalance = !hideBalance && typeof credits === "number" && credits > 0;
532
+ const approxValue = showBalance && typeof creditsPerMinorUnit === "number" && creditsPerMinorUnit > 0 && displayCurrency ? credits / creditsPerMinorUnit * (displayExchangeRate ?? 1) : null;
533
+ return /* @__PURE__ */ jsxs("section", { className: cx.card, "aria-label": "Your details", children: [
534
+ /* @__PURE__ */ jsx2("h2", { className: cx.heading, children: "Your details" }),
535
+ /* @__PURE__ */ jsxs("div", { className: "solvapay-mcp-detail-grid", children: [
536
+ /* @__PURE__ */ jsx2(DetailRow, { value: displayName, muted: cx.muted }),
537
+ email ? /* @__PURE__ */ jsx2(DetailRow, { label: "Email", value: email, labelMuted: cx.muted }) : null,
538
+ customerRef ? /* @__PURE__ */ jsx2(
539
+ DetailRow,
540
+ {
541
+ label: "Customer reference",
542
+ value: customerRef,
543
+ mono: true,
544
+ labelMuted: cx.muted,
545
+ muted: cx.muted
546
+ }
547
+ ) : null,
548
+ showBalance ? /* @__PURE__ */ jsxs("div", { className: "solvapay-mcp-detail-row", children: [
549
+ /* @__PURE__ */ jsxs("div", { className: "solvapay-mcp-detail-balance-head", children: [
550
+ /* @__PURE__ */ jsx2("span", { className: `solvapay-mcp-detail-label ${cx.muted}`.trim(), children: "Credit balance" }),
551
+ onTopup ? /* @__PURE__ */ jsx2(
552
+ "button",
553
+ {
554
+ type: "button",
555
+ className: cx.linkButton,
556
+ onClick: onTopup,
557
+ "aria-label": "Top up credits",
558
+ children: "Top up"
559
+ }
560
+ ) : null
561
+ ] }),
562
+ /* @__PURE__ */ jsxs("span", { className: "solvapay-mcp-detail-value", children: [
563
+ Intl.NumberFormat(locale).format(credits ?? 0),
564
+ " credits"
565
+ ] }),
566
+ approxValue !== null ? /* @__PURE__ */ jsxs("span", { className: `solvapay-mcp-detail-value-mono ${cx.muted}`.trim(), children: [
567
+ "~",
568
+ formatPrice(
569
+ Math.round(approxValue * getMinorUnitsPerMajor(displayCurrency ?? "USD")),
570
+ displayCurrency ?? "USD",
571
+ { locale, free: "" }
572
+ )
573
+ ] }) : null
574
+ ] }) : null
575
+ ] })
576
+ ] });
577
+ }
578
+ function McpSellerDetailsCard({
579
+ classNames,
580
+ showVerifiedBadge = true
581
+ }) {
582
+ const cx = resolveMcpClassNames(classNames);
583
+ const { merchant, loading } = useMerchant();
584
+ if (loading && !merchant) {
585
+ return /* @__PURE__ */ jsxs("div", { className: cx.card, "aria-busy": "true", children: [
586
+ /* @__PURE__ */ jsx2("h2", { className: cx.heading, children: "Seller details" }),
587
+ /* @__PURE__ */ jsx2("p", { className: cx.muted, children: "Loading\u2026" })
588
+ ] });
589
+ }
590
+ if (!merchant) {
591
+ return null;
592
+ }
593
+ const supportEmail = merchant.supportEmail;
594
+ const supportUrl = merchant.supportUrl;
595
+ return /* @__PURE__ */ jsxs("section", { className: cx.card, "aria-label": "Seller details", children: [
596
+ /* @__PURE__ */ jsxs("div", { className: "solvapay-mcp-detail-heading-row", children: [
597
+ /* @__PURE__ */ jsx2("h2", { className: cx.heading, children: "Seller details" }),
598
+ showVerifiedBadge ? /* @__PURE__ */ jsxs("span", { className: "solvapay-mcp-verified-badge", "aria-label": "Verified seller", children: [
599
+ /* @__PURE__ */ jsx2("span", { "aria-hidden": "true", children: "\u2713" }),
600
+ " Verified seller"
601
+ ] }) : null
602
+ ] }),
603
+ /* @__PURE__ */ jsxs("div", { className: "solvapay-mcp-detail-grid", children: [
604
+ /* @__PURE__ */ jsx2(DetailRow, { value: merchant.displayName, muted: cx.muted }),
605
+ merchant.legalName && merchant.legalName !== merchant.displayName ? /* @__PURE__ */ jsx2(DetailRow, { value: merchant.legalName, muted: cx.muted }) : null,
606
+ supportEmail ? /* @__PURE__ */ jsxs("div", { className: "solvapay-mcp-detail-row", children: [
607
+ /* @__PURE__ */ jsx2("span", { className: `solvapay-mcp-detail-label ${cx.muted}`.trim(), children: "Support email" }),
608
+ /* @__PURE__ */ jsx2(
609
+ "a",
610
+ {
611
+ className: "solvapay-mcp-detail-link",
612
+ href: `mailto:${supportEmail}`,
613
+ children: supportEmail
614
+ }
615
+ )
616
+ ] }) : null,
617
+ supportUrl ? /* @__PURE__ */ jsxs("div", { className: "solvapay-mcp-detail-row", children: [
618
+ /* @__PURE__ */ jsx2("span", { className: `solvapay-mcp-detail-label ${cx.muted}`.trim(), children: "Support" }),
619
+ /* @__PURE__ */ jsx2(
620
+ "a",
621
+ {
622
+ className: "solvapay-mcp-detail-link",
623
+ href: supportUrl,
624
+ target: "_blank",
625
+ rel: "noopener noreferrer",
626
+ children: "Visit support centre"
627
+ }
628
+ )
629
+ ] }) : null,
630
+ merchant.country ? /* @__PURE__ */ jsx2(DetailRow, { label: "Country", value: merchant.country, labelMuted: cx.muted }) : null
631
+ ] })
632
+ ] });
633
+ }
634
+
635
+ // src/mcp/views/McpAccountView.tsx
636
+ import { Fragment, jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
637
+ function McpAccountView({
638
+ classNames,
639
+ onTopup,
640
+ onChangePlan,
641
+ hideDetailCards
642
+ }) {
643
+ const cx = resolveMcpClassNames(classNames);
644
+ const { loading, isRefetching, hasPaidPurchase, activePurchase } = usePurchase();
645
+ const { shouldShowCancelledNotice } = usePurchaseStatus();
646
+ const { credits } = useBalance();
647
+ const { usage } = useUsage();
648
+ if (loading) {
649
+ return /* @__PURE__ */ jsx3("div", { className: cx.card, children: /* @__PURE__ */ jsx3("p", { children: "Loading account\u2026" }) });
650
+ }
651
+ const hasAnyPlan = hasPaidPurchase || shouldShowCancelledNotice;
652
+ const hasCredits = (credits ?? 0) > 0;
653
+ const planSnapshot = activePurchase?.planSnapshot;
654
+ const planType = planSnapshot?.planType;
655
+ const isUsageBased = planType === "usage-based";
656
+ const hasMeter = usage != null || Boolean(planSnapshot?.meterRef);
657
+ return /* @__PURE__ */ jsxs2("div", { className: "solvapay-mcp-account", "data-refreshing": isRefetching ? "true" : void 0, children: [
658
+ hasCredits || isUsageBased ? /* @__PURE__ */ jsx3("section", { className: cx.card, "aria-label": "Credit balance", children: /* @__PURE__ */ jsxs2("div", { className: cx.balanceRow, children: [
659
+ /* @__PURE__ */ jsxs2("div", { children: [
660
+ /* @__PURE__ */ jsx3("h2", { className: cx.heading, children: "Credit balance" }),
661
+ /* @__PURE__ */ jsx3(BalanceBadge, {})
662
+ ] }),
663
+ onTopup ? /* @__PURE__ */ jsx3("button", { type: "button", className: cx.button, onClick: onTopup, children: "Top up" }) : null
664
+ ] }) }) : null,
665
+ hasMeter ? /* @__PURE__ */ jsx3("section", { className: cx.card, "aria-label": "Usage", children: /* @__PURE__ */ jsxs2(UsageMeter.Root, { children: [
666
+ /* @__PURE__ */ jsx3(UsageMeter.Label, {}),
667
+ /* @__PURE__ */ jsx3(UsageMeter.Bar, {}),
668
+ /* @__PURE__ */ jsx3(UsageMeter.Percentage, {}),
669
+ /* @__PURE__ */ jsx3(UsageMeter.ResetsIn, {}),
670
+ /* @__PURE__ */ jsx3(UsageMeter.Loading, {}),
671
+ /* @__PURE__ */ jsx3(UsageMeter.Empty, {})
672
+ ] }) }) : null,
673
+ /* @__PURE__ */ jsxs2("div", { className: cx.card, children: [
674
+ hasPaidPurchase ? /* @__PURE__ */ jsx3(CurrentPlanCard, {}) : null,
675
+ /* @__PURE__ */ jsxs2(CancelledPlanNotice.Root, { className: cx.notice, children: [
676
+ /* @__PURE__ */ jsx3(CancelledPlanNotice.Heading, {}),
677
+ /* @__PURE__ */ jsx3(CancelledPlanNotice.Expires, {}),
678
+ /* @__PURE__ */ jsx3(CancelledPlanNotice.DaysRemaining, { className: cx.muted }),
679
+ /* @__PURE__ */ jsx3(CancelledPlanNotice.ReactivateButton, { className: cx.button })
680
+ ] }),
681
+ !hasAnyPlan && hasCredits && /* @__PURE__ */ jsxs2("div", { className: cx.stack, children: [
682
+ /* @__PURE__ */ jsx3("h2", { className: cx.heading, children: "You're on pay-as-you-go credits" }),
683
+ /* @__PURE__ */ jsx3("p", { className: cx.muted, children: "Top up to keep going, or choose a plan from the Plan tab for predictable monthly billing." }),
684
+ onChangePlan ? /* @__PURE__ */ jsx3("button", { type: "button", className: cx.linkButton, onClick: onChangePlan, children: "See plans" }) : null
685
+ ] }),
686
+ !hasAnyPlan && !hasCredits && /* @__PURE__ */ jsxs2("div", { className: cx.stack, children: [
687
+ /* @__PURE__ */ jsx3("h2", { className: cx.heading, children: "You don't have an active plan" }),
688
+ /* @__PURE__ */ jsx3("p", { className: cx.muted, children: "Pick a plan \u2014 free, pay-as-you-go, or paid \u2014 from the Plan tab." }),
689
+ onChangePlan ? /* @__PURE__ */ jsx3("button", { type: "button", className: cx.button, onClick: onChangePlan, children: "Pick a plan" }) : null
690
+ ] }),
691
+ hasPaidPurchase && activePurchase && activePurchase.amount && activePurchase.amount > 0 ? /* @__PURE__ */ jsx3(
692
+ LaunchCustomerPortalButton,
693
+ {
694
+ className: cx.button,
695
+ loadingClassName: cx.button,
696
+ errorClassName: cx.button,
697
+ children: "Manage billing"
698
+ }
699
+ ) : null
700
+ ] }),
701
+ !hideDetailCards ? /* @__PURE__ */ jsxs2(Fragment, { children: [
702
+ /* @__PURE__ */ jsx3(McpCustomerDetailsCard, { classNames, onTopup }),
703
+ /* @__PURE__ */ jsx3(McpSellerDetailsCard, { classNames })
704
+ ] }) : null
705
+ ] });
706
+ }
707
+
708
+ // src/mcp/views/McpCheckoutView.tsx
709
+ import React9, { useCallback as useCallback3, useEffect as useEffect4, useMemo as useMemo3, useRef as useRef2, useState as useState5 } from "react";
710
+
711
+ // src/mcp/views/checkout/EmbeddedCheckout.tsx
712
+ import { useMemo as useMemo2, useState as useState4 } from "react";
713
+
714
+ // src/mcp/views/checkout/CheckoutStateMachine.tsx
715
+ import { useCallback as useCallback2 } from "react";
716
+
717
+ // src/mcp/views/checkout/steps/PlanStep.tsx
718
+ import { memo } from "react";
719
+
720
+ // src/mcp/views/checkout/shared.ts
721
+ function isPayg(plan) {
722
+ return isPaygPlan(plan ?? null);
723
+ }
724
+ function planSortByPaygFirstThenAsc(a, b) {
725
+ const aPayg = isPayg(a);
726
+ const bPayg = isPayg(b);
727
+ if (aPayg && !bPayg) return -1;
728
+ if (!aPayg && bPayg) return 1;
729
+ return (a.price ?? 0) - (b.price ?? 0);
730
+ }
731
+ function formatContinueLabel(plan, locale) {
732
+ if (!plan) return "Continue";
733
+ if (isPayg(plan)) {
734
+ return `Continue with ${plan.name ?? "Pay as you go"}`;
735
+ }
736
+ const currency = (plan.currency ?? "USD").toUpperCase();
737
+ const priceLabel = formatPrice(plan.price ?? 0, currency, { locale });
738
+ const cycle = plan.billingCycle ? `/${shortCycle(plan.billingCycle)}` : "";
739
+ return `Continue with ${plan.name ?? "Plan"} \u2014 ${priceLabel}${cycle}`;
740
+ }
741
+ function formatPaygRate(plan, locale) {
742
+ const currency = (plan.currency ?? "USD").toUpperCase();
743
+ const creditsPerUnit = plan.creditsPerUnit ?? 1;
744
+ const perCreditMinor = Math.max(1, Math.round(1 / creditsPerUnit));
745
+ return `${formatPrice(perCreditMinor, currency, { locale })} / call`;
746
+ }
747
+ function inferIncludedCredits(plan) {
748
+ const price = plan.price ?? 0;
749
+ const creditsPerUnit = plan.creditsPerUnit ?? 0;
750
+ if (price > 0 && creditsPerUnit > 0) {
751
+ return Math.round(price * creditsPerUnit);
752
+ }
753
+ return 0;
754
+ }
755
+ function shortCycle(cycle) {
756
+ if (!cycle) return "mo";
757
+ const lc = cycle.toLowerCase();
758
+ if (lc.startsWith("year") || lc === "annually" || lc === "annual") return "yr";
759
+ if (lc.startsWith("week")) return "wk";
760
+ if (lc.startsWith("day")) return "d";
761
+ return "mo";
762
+ }
763
+
764
+ // src/mcp/views/checkout/steps/PlanStep.tsx
765
+ import { Fragment as Fragment2, jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
766
+ var PlanStep = memo(function PlanStep2({
767
+ fromPaywall,
768
+ paywallKind,
769
+ hideUpgradeBanner,
770
+ onContinue,
771
+ onStayOnFree,
772
+ isActivating,
773
+ activationError,
774
+ cx
775
+ }) {
776
+ const { selectedPlan, selectedPlanRef } = usePlanSelector();
777
+ const locale = useHostLocale();
778
+ const selectedPlanShape = selectedPlan;
779
+ const ctaLabel = formatContinueLabel(selectedPlanShape, locale);
780
+ const showBanner = fromPaywall && !hideUpgradeBanner;
781
+ return /* @__PURE__ */ jsxs3(Fragment2, { children: [
782
+ showBanner ? /* @__PURE__ */ jsx4(UpgradeBanner, { kind: paywallKind, cx }) : null,
783
+ /* @__PURE__ */ jsx4("h2", { className: cx.heading, children: "Choose a plan" }),
784
+ /* @__PURE__ */ jsx4(PlanSelector.Grid, { className: "solvapay-plan-selector-grid", children: /* @__PURE__ */ jsxs3(PlanSelector.Card, { className: "solvapay-plan-selector-card", children: [
785
+ /* @__PURE__ */ jsx4(PlanSelector.CardBadge, { className: "solvapay-plan-selector-card-badge" }),
786
+ /* @__PURE__ */ jsx4(PlanSelector.CardName, { className: "solvapay-plan-selector-card-name" }),
787
+ /* @__PURE__ */ jsx4(PlanSelector.CardPrice, { className: "solvapay-plan-selector-card-price" }),
788
+ /* @__PURE__ */ jsx4(PlanSelector.CardInterval, { className: "solvapay-plan-selector-card-interval" })
789
+ ] }) }),
790
+ /* @__PURE__ */ jsx4(PlanSelector.Loading, { className: "solvapay-plan-selector-loading" }),
791
+ /* @__PURE__ */ jsx4(PlanSelector.Error, { className: "solvapay-plan-selector-error" }),
792
+ activationError ? /* @__PURE__ */ jsx4("p", { className: cx.error, role: "alert", children: activationError }) : null,
793
+ /* @__PURE__ */ jsx4(
794
+ "button",
795
+ {
796
+ type: "button",
797
+ className: cx.button,
798
+ disabled: !selectedPlanRef || isActivating,
799
+ "aria-disabled": !selectedPlanRef || isActivating,
800
+ onClick: onContinue,
801
+ children: ctaLabel
802
+ }
803
+ ),
804
+ onStayOnFree ? /* @__PURE__ */ jsx4(
805
+ "button",
806
+ {
807
+ type: "button",
808
+ className: `${cx.linkButton ?? ""} solvapay-mcp-checkout-dismiss`.trim(),
809
+ onClick: onStayOnFree,
810
+ "data-solvapay-mcp-checkout-stay-on-free": "",
811
+ children: "Stay on Free"
812
+ }
813
+ ) : null
814
+ ] });
815
+ });
816
+ function UpgradeBanner({
817
+ kind,
818
+ cx
819
+ }) {
820
+ const copy = kind === "payment_required" ? "You've used your free quota. Pick a plan to keep going." : "This tool needs a paid plan. Pick one to get started.";
821
+ return /* @__PURE__ */ jsxs3(
822
+ "div",
823
+ {
824
+ className: "solvapay-mcp-checkout-banner",
825
+ role: "status",
826
+ "data-solvapay-mcp-checkout-banner": "",
827
+ children: [
828
+ /* @__PURE__ */ jsx4("strong", { className: "solvapay-mcp-checkout-banner-title", children: "Upgrade to continue" }),
829
+ /* @__PURE__ */ jsx4("span", { className: `${cx.muted} solvapay-mcp-checkout-banner-message`.trim(), children: copy })
830
+ ]
831
+ }
832
+ );
833
+ }
834
+
835
+ // src/mcp/views/checkout/steps/AmountStep.tsx
836
+ import { memo as memo2, useState as useState3 } from "react";
837
+
838
+ // src/mcp/views/BackLink.tsx
839
+ import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
840
+ function BackLink({ label, glyph = "\u2190", className, onClick, ...rest }) {
841
+ return /* @__PURE__ */ jsxs4(
842
+ "button",
843
+ {
844
+ type: "button",
845
+ className: ["solvapay-mcp-back-link", className].filter(Boolean).join(" "),
846
+ onClick,
847
+ ...rest,
848
+ children: [
849
+ /* @__PURE__ */ jsx5("span", { className: "solvapay-mcp-back-link-glyph", "aria-hidden": "true", children: glyph }),
850
+ label
851
+ ]
852
+ }
853
+ );
854
+ }
855
+
856
+ // src/mcp/views/checkout/steps/AmountStep.tsx
857
+ import { Fragment as Fragment3, jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
858
+ var AmountStep = memo2(function AmountStep2({
859
+ plan,
860
+ onBack,
861
+ onContinue,
862
+ isActivating,
863
+ activationError,
864
+ cx
865
+ }) {
866
+ const currency = (plan.currency ?? "USD").toUpperCase();
867
+ const locale = useHostLocale();
868
+ const [stagedAmountMinor, setStagedAmountMinor] = useState3(null);
869
+ return /* @__PURE__ */ jsxs5(Fragment3, { children: [
870
+ /* @__PURE__ */ jsx6(BackLink, { label: "Back", onClick: onBack }),
871
+ /* @__PURE__ */ jsx6("h2", { className: cx.heading, children: "How many credits?" }),
872
+ /* @__PURE__ */ jsx6("p", { className: cx.muted, children: "Top up to start using the tool." }),
873
+ /* @__PURE__ */ jsxs5(
874
+ AmountPicker.Root,
875
+ {
876
+ currency,
877
+ emit: "minor",
878
+ className: cx.amountPicker,
879
+ onChange: (value) => setStagedAmountMinor(value),
880
+ children: [
881
+ /* @__PURE__ */ jsx6(PresetAmountRow, { cx }),
882
+ /* @__PURE__ */ jsx6(AmountPicker.Custom, { className: cx.amountCustom, placeholder: "or custom amount" }),
883
+ /* @__PURE__ */ jsx6(
884
+ AmountPicker.Confirm,
885
+ {
886
+ className: cx.button,
887
+ onConfirm: (amountMinor) => {
888
+ void onContinue(amountMinor);
889
+ },
890
+ children: isActivating ? "Activating\u2026" : stagedAmountMinor ? `Continue \u2014 ${formatPrice(stagedAmountMinor, currency, { locale })}` : "Continue"
891
+ }
892
+ )
893
+ ]
894
+ }
895
+ ),
896
+ activationError ? /* @__PURE__ */ jsx6("p", { className: cx.error, role: "alert", children: activationError }) : null
897
+ ] });
898
+ });
899
+ function PresetAmountRow({ cx }) {
900
+ const { quickAmounts, currency } = useAmountPicker();
901
+ const locale = useHostLocale();
902
+ const popularIndex = Math.min(1, quickAmounts.length - 1);
903
+ return /* @__PURE__ */ jsx6("div", { className: cx.amountOptions, children: quickAmounts.map((amount, i) => {
904
+ const label = formatPrice(amount * getMinorUnitsPerMajor(currency), currency, {
905
+ locale,
906
+ free: ""
907
+ });
908
+ return /* @__PURE__ */ jsx6(
909
+ AmountPicker.Option,
910
+ {
911
+ amount,
912
+ className: cx.amountOption,
913
+ "data-popular": i === popularIndex ? "" : void 0,
914
+ "aria-label": `${label}${i === popularIndex ? " (popular)" : ""}`
915
+ },
916
+ amount
917
+ );
918
+ }) });
919
+ }
920
+
921
+ // src/mcp/views/checkout/steps/PaygPaymentStep.tsx
922
+ import { memo as memo3 } from "react";
923
+ import { Fragment as Fragment4, jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
924
+ var PaygPaymentStep = memo3(function PaygPaymentStep2({
925
+ plan,
926
+ amountMinor,
927
+ returnUrl,
928
+ onBack,
929
+ onSuccess,
930
+ cx
931
+ }) {
932
+ const currency = (plan.currency ?? "USD").toUpperCase();
933
+ const locale = useHostLocale();
934
+ const creditsPerUnit = plan.creditsPerUnit ?? 1;
935
+ const creditsAdded = Math.round(amountMinor * creditsPerUnit);
936
+ return /* @__PURE__ */ jsxs6(Fragment4, { children: [
937
+ /* @__PURE__ */ jsx7(BackLink, { label: "Change amount", onClick: onBack }),
938
+ /* @__PURE__ */ jsx7("h2", { className: cx.heading, children: "Payment" }),
939
+ /* @__PURE__ */ jsxs6("div", { className: "solvapay-mcp-checkout-order-summary", "data-variant": "payg", children: [
940
+ /* @__PURE__ */ jsxs6("div", { className: "solvapay-mcp-checkout-order-summary-row", children: [
941
+ /* @__PURE__ */ jsxs6("span", { className: cx.muted, children: [
942
+ creditsAdded.toLocaleString(locale),
943
+ " credits"
944
+ ] }),
945
+ /* @__PURE__ */ jsx7("span", { children: formatPrice(amountMinor, currency, { locale }) })
946
+ ] }),
947
+ /* @__PURE__ */ jsx7("div", { className: "solvapay-mcp-checkout-order-summary-row", children: /* @__PURE__ */ jsx7("span", { className: cx.muted, children: "One-time" }) })
948
+ ] }),
949
+ /* @__PURE__ */ jsxs6(
950
+ TopupForm.Root,
951
+ {
952
+ amount: amountMinor,
953
+ currency,
954
+ returnUrl,
955
+ className: cx.topupForm,
956
+ onSuccess: () => onSuccess(),
957
+ children: [
958
+ /* @__PURE__ */ jsx7(TopupForm.Loading, {}),
959
+ /* @__PURE__ */ jsx7(TopupForm.PaymentElement, {}),
960
+ /* @__PURE__ */ jsx7(TopupForm.Error, { className: cx.error }),
961
+ /* @__PURE__ */ jsxs6("label", { className: "solvapay-mcp-checkout-save-card", children: [
962
+ /* @__PURE__ */ jsx7("input", { type: "checkbox", defaultChecked: true }),
963
+ /* @__PURE__ */ jsx7("span", { className: cx.muted, children: "Save card for future top-ups" })
964
+ ] }),
965
+ /* @__PURE__ */ jsxs6(TopupForm.SubmitButton, { className: cx.button, children: [
966
+ "Pay ",
967
+ formatPrice(amountMinor, currency, { locale })
968
+ ] })
969
+ ]
970
+ }
971
+ )
972
+ ] });
973
+ });
974
+
975
+ // src/mcp/views/checkout/steps/RecurringPaymentStep.tsx
976
+ import { memo as memo4 } from "react";
977
+ import { Fragment as Fragment5, jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
978
+ var RecurringPaymentStep = memo4(function RecurringPaymentStep2({
979
+ plan,
980
+ planRef,
981
+ productRef,
982
+ returnUrl,
983
+ onBack,
984
+ onSuccess,
985
+ cx
986
+ }) {
987
+ const currency = (plan.currency ?? "USD").toUpperCase();
988
+ const locale = useHostLocale();
989
+ const amountMinor = plan.price ?? 0;
990
+ const cycle = plan.billingCycle ?? "monthly";
991
+ const credits = inferIncludedCredits(plan);
992
+ const planName = plan.name ?? "Plan";
993
+ return /* @__PURE__ */ jsxs7(Fragment5, { children: [
994
+ /* @__PURE__ */ jsx8(BackLink, { label: "Change plan", onClick: onBack }),
995
+ /* @__PURE__ */ jsx8("h2", { className: cx.heading, children: "Payment" }),
996
+ /* @__PURE__ */ jsxs7("div", { className: "solvapay-mcp-checkout-order-summary", "data-variant": "recurring", children: [
997
+ /* @__PURE__ */ jsxs7("div", { className: "solvapay-mcp-checkout-order-summary-row", children: [
998
+ /* @__PURE__ */ jsx8("span", { className: cx.muted, children: planName }),
999
+ /* @__PURE__ */ jsxs7("span", { children: [
1000
+ formatPrice(amountMinor, currency, { locale }),
1001
+ "/",
1002
+ shortCycle(cycle)
1003
+ ] })
1004
+ ] }),
1005
+ credits > 0 ? /* @__PURE__ */ jsx8("div", { className: "solvapay-mcp-checkout-order-summary-row", children: /* @__PURE__ */ jsxs7("span", { className: cx.muted, children: [
1006
+ credits.toLocaleString(locale),
1007
+ " credits included"
1008
+ ] }) }) : null
1009
+ ] }),
1010
+ /* @__PURE__ */ jsxs7(
1011
+ PaymentForm.Root,
1012
+ {
1013
+ planRef,
1014
+ productRef,
1015
+ returnUrl,
1016
+ requireTermsAcceptance: false,
1017
+ onSuccess,
1018
+ children: [
1019
+ /* @__PURE__ */ jsx8(PaymentForm.Loading, {}),
1020
+ /* @__PURE__ */ jsx8(PaymentForm.PaymentElement, {}),
1021
+ /* @__PURE__ */ jsx8(PaymentForm.Error, { className: cx.error }),
1022
+ /* @__PURE__ */ jsxs7("p", { className: `${cx.muted} solvapay-mcp-checkout-terms`.trim(), children: [
1023
+ "By subscribing, you agree ",
1024
+ planName,
1025
+ " renews ",
1026
+ cycle,
1027
+ " at",
1028
+ " ",
1029
+ formatPrice(amountMinor, currency, { locale }),
1030
+ " until you cancel."
1031
+ ] }),
1032
+ /* @__PURE__ */ jsxs7(PaymentForm.SubmitButton, { className: cx.button, children: [
1033
+ "Subscribe \u2014 ",
1034
+ formatPrice(amountMinor, currency, { locale }),
1035
+ "/",
1036
+ shortCycle(cycle)
1037
+ ] })
1038
+ ]
1039
+ }
1040
+ )
1041
+ ] });
1042
+ });
1043
+
1044
+ // src/mcp/views/checkout/steps/SuccessStep.tsx
1045
+ import { memo as memo5 } from "react";
1046
+ import { Fragment as Fragment6, jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
1047
+ var SuccessStep = memo5(function SuccessStep2({
1048
+ meta,
1049
+ onBackToChat,
1050
+ cx
1051
+ }) {
1052
+ const locale = useHostLocale();
1053
+ if (meta.branch === "payg") {
1054
+ return /* @__PURE__ */ jsxs8(Fragment6, { children: [
1055
+ /* @__PURE__ */ jsx9("div", { className: "solvapay-mcp-checkout-success-check", "aria-hidden": "true", children: "\u2713" }),
1056
+ /* @__PURE__ */ jsx9("h2", { className: cx.heading, children: "Credits added" }),
1057
+ /* @__PURE__ */ jsx9("p", { className: cx.muted, children: "Pay as you go plan is active." }),
1058
+ /* @__PURE__ */ jsxs8("dl", { className: "solvapay-mcp-checkout-receipt", "data-variant": "payg", children: [
1059
+ /* @__PURE__ */ jsxs8("div", { className: "solvapay-mcp-checkout-receipt-row", children: [
1060
+ /* @__PURE__ */ jsx9("dt", { children: "Amount" }),
1061
+ /* @__PURE__ */ jsx9("dd", { children: formatPrice(meta.amountMinor, meta.currency, { locale }) })
1062
+ ] }),
1063
+ /* @__PURE__ */ jsxs8("div", { className: "solvapay-mcp-checkout-receipt-row", children: [
1064
+ /* @__PURE__ */ jsx9("dt", { children: "Credits" }),
1065
+ /* @__PURE__ */ jsxs8("dd", { children: [
1066
+ "+",
1067
+ meta.creditsAdded.toLocaleString(locale)
1068
+ ] })
1069
+ ] }),
1070
+ /* @__PURE__ */ jsxs8("div", { className: "solvapay-mcp-checkout-receipt-row", children: [
1071
+ /* @__PURE__ */ jsx9("dt", { children: "Plan" }),
1072
+ /* @__PURE__ */ jsx9("dd", { children: meta.plan.name ?? "Pay as you go" })
1073
+ ] }),
1074
+ /* @__PURE__ */ jsxs8("div", { className: "solvapay-mcp-checkout-receipt-row", children: [
1075
+ /* @__PURE__ */ jsx9("dt", { children: "Rate" }),
1076
+ /* @__PURE__ */ jsx9("dd", { children: meta.rateLabel })
1077
+ ] })
1078
+ ] }),
1079
+ /* @__PURE__ */ jsx9(
1080
+ "button",
1081
+ {
1082
+ type: "button",
1083
+ className: cx.button,
1084
+ "data-variant": "success",
1085
+ onClick: () => {
1086
+ void onBackToChat();
1087
+ },
1088
+ children: "Back to chat"
1089
+ }
1090
+ )
1091
+ ] });
1092
+ }
1093
+ return /* @__PURE__ */ jsxs8(Fragment6, { children: [
1094
+ /* @__PURE__ */ jsx9("div", { className: "solvapay-mcp-checkout-success-check", "aria-hidden": "true", children: "\u2713" }),
1095
+ /* @__PURE__ */ jsxs8("h2", { className: cx.heading, children: [
1096
+ meta.plan.name ?? "Plan",
1097
+ " active"
1098
+ ] }),
1099
+ /* @__PURE__ */ jsx9("p", { className: cx.muted, children: "Subscription is live and credits are ready." }),
1100
+ /* @__PURE__ */ jsxs8("dl", { className: "solvapay-mcp-checkout-receipt", "data-variant": "recurring", children: [
1101
+ /* @__PURE__ */ jsxs8("div", { className: "solvapay-mcp-checkout-receipt-row", children: [
1102
+ /* @__PURE__ */ jsx9("dt", { children: "Plan" }),
1103
+ /* @__PURE__ */ jsx9("dd", { children: meta.plan.name ?? "Plan" })
1104
+ ] }),
1105
+ meta.creditsIncluded > 0 ? /* @__PURE__ */ jsxs8("div", { className: "solvapay-mcp-checkout-receipt-row", children: [
1106
+ /* @__PURE__ */ jsx9("dt", { children: "Credits" }),
1107
+ /* @__PURE__ */ jsxs8("dd", { children: [
1108
+ "+",
1109
+ meta.creditsIncluded.toLocaleString(locale)
1110
+ ] })
1111
+ ] }) : null,
1112
+ /* @__PURE__ */ jsxs8("div", { className: "solvapay-mcp-checkout-receipt-row", children: [
1113
+ /* @__PURE__ */ jsx9("dt", { children: "Charged today" }),
1114
+ /* @__PURE__ */ jsx9("dd", { children: formatPrice(meta.chargedTodayMinor, meta.currency, { locale }) })
1115
+ ] }),
1116
+ meta.nextRenewalLabel ? /* @__PURE__ */ jsxs8("div", { className: "solvapay-mcp-checkout-receipt-row", children: [
1117
+ /* @__PURE__ */ jsx9("dt", { children: "Next renewal" }),
1118
+ /* @__PURE__ */ jsx9("dd", { children: meta.nextRenewalLabel })
1119
+ ] }) : null
1120
+ ] }),
1121
+ /* @__PURE__ */ jsxs8("p", { className: `${cx.muted} solvapay-mcp-checkout-manage-pointer`.trim(), children: [
1122
+ "Manage from ",
1123
+ /* @__PURE__ */ jsx9("code", { children: "/manage_account" })
1124
+ ] }),
1125
+ /* @__PURE__ */ jsx9(
1126
+ "button",
1127
+ {
1128
+ type: "button",
1129
+ className: cx.button,
1130
+ "data-variant": "success",
1131
+ onClick: () => {
1132
+ void onBackToChat();
1133
+ },
1134
+ children: "Back to chat"
1135
+ }
1136
+ )
1137
+ ] });
1138
+ });
1139
+
1140
+ // src/mcp/views/checkout/CheckoutStateMachine.tsx
1141
+ import { Fragment as Fragment7, jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
1142
+ function CheckoutStateMachine(props) {
1143
+ const {
1144
+ step,
1145
+ setStep,
1146
+ selectedAmountMinor,
1147
+ setSelectedAmountMinor,
1148
+ successMeta,
1149
+ setSuccessMeta,
1150
+ activationError,
1151
+ setActivationError,
1152
+ isActivating,
1153
+ setIsActivating,
1154
+ fromPaywall,
1155
+ paywallKind,
1156
+ hideUpgradeBanner,
1157
+ productRef,
1158
+ returnUrl,
1159
+ onPurchaseSuccess,
1160
+ onRefreshBootstrap,
1161
+ onClose,
1162
+ cx
1163
+ } = props;
1164
+ const { selectedPlan, selectedPlanRef, plans } = usePlanSelector();
1165
+ const transport = useTransport();
1166
+ const locale = useHostLocale();
1167
+ const { notifyModelContext, notifySuccess } = useMcpBridge();
1168
+ const selectedPlanShape = selectedPlan;
1169
+ const branch = selectedPlanShape ? isPayg(selectedPlanShape) ? "payg" : "recurring" : null;
1170
+ const onPlanContinue = useCallback2(() => {
1171
+ if (!selectedPlanShape || !selectedPlanRef) return;
1172
+ setActivationError(null);
1173
+ void notifyModelContext({
1174
+ text: `User selected ${selectedPlanShape.name ?? "a plan"}.`
1175
+ });
1176
+ if (branch === "payg") {
1177
+ setStep("amount");
1178
+ return;
1179
+ }
1180
+ setStep("payment");
1181
+ }, [
1182
+ branch,
1183
+ notifyModelContext,
1184
+ selectedPlanRef,
1185
+ selectedPlanShape,
1186
+ setActivationError,
1187
+ setStep
1188
+ ]);
1189
+ const onAmountContinue = useCallback2(
1190
+ async (amountMinor) => {
1191
+ if (!selectedPlanRef) return;
1192
+ setSelectedAmountMinor(amountMinor);
1193
+ setActivationError(null);
1194
+ setIsActivating(true);
1195
+ try {
1196
+ await transport.activatePlan({ productRef, planRef: selectedPlanRef });
1197
+ setStep("payment");
1198
+ } catch (err) {
1199
+ const msg = err instanceof Error ? err.message : "Activation failed";
1200
+ setActivationError(msg);
1201
+ } finally {
1202
+ setIsActivating(false);
1203
+ }
1204
+ },
1205
+ [
1206
+ productRef,
1207
+ selectedPlanRef,
1208
+ setActivationError,
1209
+ setIsActivating,
1210
+ setSelectedAmountMinor,
1211
+ setStep,
1212
+ transport
1213
+ ]
1214
+ );
1215
+ const onPaygPaymentSuccess = useCallback2(() => {
1216
+ if (!selectedPlanShape || selectedAmountMinor == null) return;
1217
+ const currency = (selectedPlanShape.currency ?? "USD").toUpperCase();
1218
+ const creditsPerUnit = selectedPlanShape.creditsPerUnit ?? 1;
1219
+ const creditsAdded = Math.round(selectedAmountMinor * creditsPerUnit);
1220
+ setSuccessMeta({
1221
+ branch: "payg",
1222
+ amountMinor: selectedAmountMinor,
1223
+ currency,
1224
+ creditsAdded,
1225
+ plan: selectedPlanShape,
1226
+ rateLabel: formatPaygRate(selectedPlanShape, locale)
1227
+ });
1228
+ setStep("success");
1229
+ void notifyModelContext({
1230
+ text: `Activated ${selectedPlanShape.name ?? "plan"} with ${formatPrice(
1231
+ selectedAmountMinor,
1232
+ currency,
1233
+ { locale }
1234
+ )} in credits.`
1235
+ });
1236
+ void notifySuccess({ kind: "topup", amountMinor: selectedAmountMinor, currency });
1237
+ onPurchaseSuccess?.();
1238
+ }, [
1239
+ locale,
1240
+ notifyModelContext,
1241
+ notifySuccess,
1242
+ onPurchaseSuccess,
1243
+ selectedAmountMinor,
1244
+ selectedPlanShape,
1245
+ setStep,
1246
+ setSuccessMeta
1247
+ ]);
1248
+ const onRecurringPaymentSuccess = useCallback2(
1249
+ (_intent) => {
1250
+ if (!selectedPlanShape) return;
1251
+ const currency = (selectedPlanShape.currency ?? "USD").toUpperCase();
1252
+ setSuccessMeta({
1253
+ branch: "recurring",
1254
+ plan: selectedPlanShape,
1255
+ creditsIncluded: inferIncludedCredits(selectedPlanShape),
1256
+ chargedTodayMinor: selectedPlanShape.price ?? 0,
1257
+ currency,
1258
+ nextRenewalLabel: null
1259
+ // server-provided — surfaced as null until wired.
1260
+ });
1261
+ setStep("success");
1262
+ void notifyModelContext({
1263
+ text: `Activated ${selectedPlanShape.name ?? "plan"}.`
1264
+ });
1265
+ void notifySuccess({
1266
+ kind: "plan-activated",
1267
+ planName: selectedPlanShape.name ?? null
1268
+ });
1269
+ onPurchaseSuccess?.();
1270
+ },
1271
+ [
1272
+ notifyModelContext,
1273
+ notifySuccess,
1274
+ onPurchaseSuccess,
1275
+ selectedPlanShape,
1276
+ setStep,
1277
+ setSuccessMeta
1278
+ ]
1279
+ );
1280
+ const onBackFromAmount = useCallback2(() => {
1281
+ setActivationError(null);
1282
+ setStep("plan");
1283
+ }, [setActivationError, setStep]);
1284
+ const onBackFromRecurringPayment = useCallback2(() => {
1285
+ setStep("plan");
1286
+ }, [setStep]);
1287
+ const onBackFromPaygPayment = useCallback2(() => {
1288
+ setStep("amount");
1289
+ }, [setStep]);
1290
+ const onBackToChat = useCallback2(async () => {
1291
+ try {
1292
+ await Promise.resolve(onRefreshBootstrap?.());
1293
+ } catch {
1294
+ }
1295
+ onClose?.();
1296
+ }, [onClose, onRefreshBootstrap]);
1297
+ const onStayOnFree = useCallback2(() => {
1298
+ onClose?.();
1299
+ }, [onClose]);
1300
+ if (plans.length === 0) {
1301
+ return /* @__PURE__ */ jsxs9(Fragment7, { children: [
1302
+ /* @__PURE__ */ jsx10("h2", { className: cx.heading, children: "Checkout unavailable" }),
1303
+ /* @__PURE__ */ jsx10("p", { className: cx.muted, children: "No paid plans are configured on this product." })
1304
+ ] });
1305
+ }
1306
+ switch (step) {
1307
+ case "plan":
1308
+ return /* @__PURE__ */ jsx10(
1309
+ PlanStep,
1310
+ {
1311
+ fromPaywall,
1312
+ paywallKind,
1313
+ hideUpgradeBanner,
1314
+ onContinue: onPlanContinue,
1315
+ onStayOnFree: fromPaywall && onClose ? onStayOnFree : void 0,
1316
+ isActivating,
1317
+ activationError,
1318
+ cx
1319
+ }
1320
+ );
1321
+ case "amount":
1322
+ return /* @__PURE__ */ jsx10(
1323
+ AmountStep,
1324
+ {
1325
+ plan: selectedPlanShape,
1326
+ onBack: onBackFromAmount,
1327
+ onContinue: onAmountContinue,
1328
+ isActivating,
1329
+ activationError,
1330
+ cx
1331
+ }
1332
+ );
1333
+ case "payment":
1334
+ if (branch === "payg" && selectedPlanShape && selectedAmountMinor != null) {
1335
+ return /* @__PURE__ */ jsx10(
1336
+ PaygPaymentStep,
1337
+ {
1338
+ plan: selectedPlanShape,
1339
+ amountMinor: selectedAmountMinor,
1340
+ returnUrl,
1341
+ onBack: onBackFromPaygPayment,
1342
+ onSuccess: onPaygPaymentSuccess,
1343
+ cx
1344
+ }
1345
+ );
1346
+ }
1347
+ if (branch === "recurring" && selectedPlanShape && selectedPlanRef) {
1348
+ return /* @__PURE__ */ jsx10(
1349
+ RecurringPaymentStep,
1350
+ {
1351
+ plan: selectedPlanShape,
1352
+ planRef: selectedPlanRef,
1353
+ productRef,
1354
+ returnUrl,
1355
+ onBack: onBackFromRecurringPayment,
1356
+ onSuccess: onRecurringPaymentSuccess,
1357
+ cx
1358
+ }
1359
+ );
1360
+ }
1361
+ return /* @__PURE__ */ jsx10(
1362
+ PlanStep,
1363
+ {
1364
+ fromPaywall,
1365
+ paywallKind,
1366
+ hideUpgradeBanner,
1367
+ onContinue: onPlanContinue,
1368
+ onStayOnFree: fromPaywall && onClose ? onStayOnFree : void 0,
1369
+ isActivating,
1370
+ activationError,
1371
+ cx
1372
+ }
1373
+ );
1374
+ case "success":
1375
+ if (!successMeta) return null;
1376
+ return /* @__PURE__ */ jsx10(SuccessStep, { meta: successMeta, onBackToChat, cx });
1377
+ }
1378
+ }
1379
+
1380
+ // src/mcp/views/checkout/EmbeddedCheckout.tsx
1381
+ import { jsx as jsx11, jsxs as jsxs10 } from "react/jsx-runtime";
1382
+ function EmbeddedCheckout({
1383
+ productRef,
1384
+ returnUrl,
1385
+ onPurchaseSuccess,
1386
+ fromPaywall,
1387
+ paywallKind,
1388
+ hideUpgradeBanner,
1389
+ plans,
1390
+ onRefreshBootstrap,
1391
+ onClose,
1392
+ cx,
1393
+ children
1394
+ }) {
1395
+ const { loading, isRefetching, activePurchase } = usePurchase();
1396
+ const currentPlanRef = activePurchase?.planSnapshot?.reference ?? null;
1397
+ const paidPlans = useMemo2(() => {
1398
+ const list = plans ?? [];
1399
+ return list.filter((p) => p.requiresPayment !== false);
1400
+ }, [plans]);
1401
+ const paygPlanRef = useMemo2(() => {
1402
+ const payg = paidPlans.find((p) => isPayg(p));
1403
+ return payg?.reference;
1404
+ }, [paidPlans]);
1405
+ const planFilter = useMemo2(
1406
+ () => (plan) => plan.requiresPayment !== false || plan.reference === currentPlanRef,
1407
+ [currentPlanRef]
1408
+ );
1409
+ const [step, setStep] = useState4("plan");
1410
+ const [selectedAmountMinor, setSelectedAmountMinor] = useState4(null);
1411
+ const [successMeta, setSuccessMeta] = useState4(null);
1412
+ const [activationError, setActivationError] = useState4(null);
1413
+ const [isActivating, setIsActivating] = useState4(false);
1414
+ if (loading) {
1415
+ return /* @__PURE__ */ jsx11("div", { className: cx.card, children: /* @__PURE__ */ jsx11("p", { children: "Loading checkout\u2026" }) });
1416
+ }
1417
+ return /* @__PURE__ */ jsxs10("div", { className: cx.card, "data-refreshing": isRefetching ? "true" : void 0, children: [
1418
+ /* @__PURE__ */ jsx11(
1419
+ PlanSelector.Root,
1420
+ {
1421
+ productRef,
1422
+ filter: planFilter,
1423
+ sortBy: planSortByPaygFirstThenAsc,
1424
+ popularPlanRef: paygPlanRef,
1425
+ currentPlanRef,
1426
+ autoSelectFirstPaid: Boolean(paygPlanRef),
1427
+ className: "solvapay-plan-selector",
1428
+ children: /* @__PURE__ */ jsx11(
1429
+ CheckoutStateMachine,
1430
+ {
1431
+ step,
1432
+ setStep,
1433
+ selectedAmountMinor,
1434
+ setSelectedAmountMinor,
1435
+ successMeta,
1436
+ setSuccessMeta,
1437
+ activationError,
1438
+ setActivationError,
1439
+ isActivating,
1440
+ setIsActivating,
1441
+ fromPaywall,
1442
+ paywallKind,
1443
+ hideUpgradeBanner,
1444
+ productRef,
1445
+ returnUrl,
1446
+ onPurchaseSuccess,
1447
+ onRefreshBootstrap,
1448
+ onClose,
1449
+ cx
1450
+ }
1451
+ )
1452
+ }
1453
+ ),
1454
+ children
1455
+ ] });
1456
+ }
1457
+
1458
+ // src/mcp/views/McpCheckoutView.tsx
1459
+ import { Fragment as Fragment8, jsx as jsx12, jsxs as jsxs11 } from "react/jsx-runtime";
1460
+ var POLL_INTERVAL_MS = 3e3;
1461
+ var AWAITING_TIMEOUT_MS = 10 * 60 * 1e3;
1462
+ function McpCheckoutView({
1463
+ productRef,
1464
+ publishableKey = null,
1465
+ returnUrl,
1466
+ onPurchaseSuccess,
1467
+ onRequestTopup: _onRequestTopup,
1468
+ fromPaywall = false,
1469
+ paywallKind,
1470
+ plans,
1471
+ onRefreshBootstrap,
1472
+ onClose,
1473
+ classNames,
1474
+ children
1475
+ }) {
1476
+ const cx = resolveMcpClassNames(classNames);
1477
+ const probe = useStripeProbe(publishableKey);
1478
+ if (probe === "loading") {
1479
+ return /* @__PURE__ */ jsxs11("div", { className: cx.card, children: [
1480
+ /* @__PURE__ */ jsx12("p", { children: "Loading checkout\u2026" }),
1481
+ children
1482
+ ] });
1483
+ }
1484
+ if (probe === "ready") {
1485
+ return /* @__PURE__ */ jsx12(
1486
+ EmbeddedCheckout,
1487
+ {
1488
+ productRef,
1489
+ returnUrl,
1490
+ onPurchaseSuccess,
1491
+ fromPaywall,
1492
+ paywallKind,
1493
+ plans,
1494
+ onRefreshBootstrap,
1495
+ onClose,
1496
+ cx,
1497
+ children
1498
+ }
1499
+ );
1500
+ }
1501
+ return /* @__PURE__ */ jsx12(
1502
+ HostedCheckout,
1503
+ {
1504
+ productRef,
1505
+ onPurchaseSuccess,
1506
+ cx,
1507
+ children
1508
+ }
1509
+ );
1510
+ }
1511
+ function useHostedUrl(enabled, fetcher, label) {
1512
+ const [state, setState] = useState5({ status: "idle" });
1513
+ useEffect4(() => {
1514
+ if (!enabled) {
1515
+ setState({ status: "idle" });
1516
+ return;
1517
+ }
1518
+ let cancelled = false;
1519
+ setState({ status: "loading" });
1520
+ fetcher().then(({ href }) => {
1521
+ if (cancelled) return;
1522
+ setState({ status: "ready", href });
1523
+ }).catch((err) => {
1524
+ if (cancelled) return;
1525
+ const message = err instanceof Error ? err.message : `Failed to load ${label}`;
1526
+ setState({ status: "error", message });
1527
+ });
1528
+ return () => {
1529
+ cancelled = true;
1530
+ };
1531
+ }, [enabled, fetcher, label]);
1532
+ return state;
1533
+ }
1534
+ var HostedLinkButton = React9.memo(function HostedLinkButton2({
1535
+ state,
1536
+ loadingLabel,
1537
+ readyLabel,
1538
+ onLaunch,
1539
+ cx
1540
+ }) {
1541
+ if (state.status === "ready") {
1542
+ return /* @__PURE__ */ jsx12(
1543
+ "a",
1544
+ {
1545
+ className: "solvapay-mcp-hosted-link",
1546
+ href: state.href,
1547
+ target: "_blank",
1548
+ rel: "noopener noreferrer",
1549
+ "aria-label": `${readyLabel} (opens in a new tab)`,
1550
+ onClick: () => onLaunch?.(state.href),
1551
+ children: /* @__PURE__ */ jsxs11("button", { type: "button", className: cx.button, children: [
1552
+ readyLabel,
1553
+ /* @__PURE__ */ jsx12(ExternalLinkGlyph, {})
1554
+ ] })
1555
+ }
1556
+ );
1557
+ }
1558
+ return /* @__PURE__ */ jsx12("button", { type: "button", className: cx.button, disabled: true, children: state.status === "error" ? "Unavailable" : loadingLabel });
1559
+ });
1560
+ function Spinner() {
1561
+ return /* @__PURE__ */ jsx12("span", { className: "solvapay-mcp-spinner", "aria-hidden": "true" });
1562
+ }
1563
+ var AwaitingBody = React9.memo(function AwaitingBody2({
1564
+ href,
1565
+ timedOut,
1566
+ onReopen,
1567
+ onCancel,
1568
+ cx
1569
+ }) {
1570
+ return /* @__PURE__ */ jsxs11(Fragment8, { children: [
1571
+ /* @__PURE__ */ jsxs11("div", { className: cx.awaitingHeader, children: [
1572
+ /* @__PURE__ */ jsx12(Spinner, {}),
1573
+ /* @__PURE__ */ jsx12("h2", { className: cx.heading, children: timedOut ? "Still waiting for payment" : "Waiting for payment\u2026" })
1574
+ ] }),
1575
+ /* @__PURE__ */ jsx12("p", { className: cx.muted, children: timedOut ? "We haven't seen your purchase yet. If you completed payment, give it another moment \u2014 otherwise reopen checkout or cancel." : "Complete payment in the other tab. Your purchase will show up here automatically." }),
1576
+ /* @__PURE__ */ jsx12(
1577
+ "a",
1578
+ {
1579
+ className: "solvapay-mcp-hosted-link",
1580
+ href,
1581
+ target: "_blank",
1582
+ rel: "noopener noreferrer",
1583
+ "aria-label": "Reopen checkout (opens in a new tab)",
1584
+ onClick: () => onReopen(),
1585
+ children: /* @__PURE__ */ jsxs11("button", { type: "button", className: cx.button, children: [
1586
+ "Reopen checkout",
1587
+ /* @__PURE__ */ jsx12(ExternalLinkGlyph, {})
1588
+ ] })
1589
+ }
1590
+ ),
1591
+ /* @__PURE__ */ jsx12("button", { type: "button", className: cx.linkButton, onClick: onCancel, children: "Didn't complete? Cancel" })
1592
+ ] });
1593
+ });
1594
+ var CancelledBody = React9.memo(function CancelledBody2({
1595
+ productName,
1596
+ endDate,
1597
+ daysLeft,
1598
+ formattedEndDate,
1599
+ checkout,
1600
+ onLaunch,
1601
+ cx
1602
+ }) {
1603
+ return /* @__PURE__ */ jsxs11(Fragment8, { children: [
1604
+ /* @__PURE__ */ jsxs11("h2", { className: cx.heading, children: [
1605
+ "Your ",
1606
+ productName,
1607
+ " purchase is cancelled"
1608
+ ] }),
1609
+ endDate && formattedEndDate ? /* @__PURE__ */ jsxs11("div", { className: cx.notice, children: [
1610
+ /* @__PURE__ */ jsx12("p", { children: /* @__PURE__ */ jsxs11("strong", { children: [
1611
+ "Access expires ",
1612
+ formattedEndDate
1613
+ ] }) }),
1614
+ daysLeft !== null && daysLeft > 0 && /* @__PURE__ */ jsxs11("p", { className: cx.muted, children: [
1615
+ daysLeft,
1616
+ " ",
1617
+ daysLeft === 1 ? "day" : "days",
1618
+ " remaining"
1619
+ ] })
1620
+ ] }) : /* @__PURE__ */ jsx12("p", { className: cx.muted, children: "Your purchase access has ended." }),
1621
+ /* @__PURE__ */ jsx12(
1622
+ HostedLinkButton,
1623
+ {
1624
+ state: checkout,
1625
+ loadingLabel: "Loading checkout\u2026",
1626
+ readyLabel: "Purchase again",
1627
+ onLaunch,
1628
+ cx
1629
+ }
1630
+ ),
1631
+ checkout.status === "error" && /* @__PURE__ */ jsx12("p", { className: cx.error, role: "alert", children: checkout.message })
1632
+ ] });
1633
+ });
1634
+ var UpgradeBody = React9.memo(function UpgradeBody2({ checkout, onLaunch, cx }) {
1635
+ return /* @__PURE__ */ jsxs11(Fragment8, { children: [
1636
+ /* @__PURE__ */ jsx12("h2", { className: cx.heading, children: "Upgrade your plan" }),
1637
+ /* @__PURE__ */ jsx12("p", { className: cx.muted, children: "The SolvaPay checkout opens in a new tab. Return here after payment and your purchase will show up automatically." }),
1638
+ /* @__PURE__ */ jsx12(
1639
+ HostedLinkButton,
1640
+ {
1641
+ state: checkout,
1642
+ loadingLabel: "Loading checkout\u2026",
1643
+ readyLabel: "Upgrade",
1644
+ onLaunch,
1645
+ cx
1646
+ }
1647
+ ),
1648
+ checkout.status === "error" && /* @__PURE__ */ jsx12("p", { className: cx.error, role: "alert", children: checkout.message })
1649
+ ] });
1650
+ });
1651
+ function HostedCheckout({
1652
+ productRef,
1653
+ onPurchaseSuccess,
1654
+ cx,
1655
+ children
1656
+ }) {
1657
+ const { loading, isRefetching, refetch, hasPaidPurchase, activePurchase } = usePurchase();
1658
+ const { cancelledPurchase, shouldShowCancelledNotice, formatDate, getDaysUntilExpiration } = usePurchaseStatus();
1659
+ const transport = useTransport();
1660
+ const [awaiting, setAwaiting] = useState5(null);
1661
+ const [awaitingTimedOut, setAwaitingTimedOut] = useState5(false);
1662
+ const [hasLoadedOnce, setHasLoadedOnce] = useState5(false);
1663
+ const onPurchaseSuccessRef = useRef2(onPurchaseSuccess);
1664
+ useEffect4(() => {
1665
+ onPurchaseSuccessRef.current = onPurchaseSuccess;
1666
+ }, [onPurchaseSuccess]);
1667
+ useEffect4(() => {
1668
+ if (!loading && !hasLoadedOnce) setHasLoadedOnce(true);
1669
+ }, [loading, hasLoadedOnce]);
1670
+ const fetchCheckoutUrl = useCallback3(async () => {
1671
+ const { checkoutUrl } = await transport.createCheckoutSession({ productRef });
1672
+ return { href: checkoutUrl };
1673
+ }, [productRef, transport]);
1674
+ const checkout = useHostedUrl(hasLoadedOnce, fetchCheckoutUrl, "checkout session");
1675
+ const safeRefetch = useCallback3(() => {
1676
+ refetch().catch((err) => {
1677
+ console.warn("[solvapay-mcp] refetch failed", err);
1678
+ });
1679
+ }, [refetch]);
1680
+ useEffect4(() => {
1681
+ const onFocus = () => safeRefetch();
1682
+ const onVisibility = () => {
1683
+ if (document.visibilityState === "visible") safeRefetch();
1684
+ };
1685
+ window.addEventListener("focus", onFocus);
1686
+ document.addEventListener("visibilitychange", onVisibility);
1687
+ return () => {
1688
+ window.removeEventListener("focus", onFocus);
1689
+ document.removeEventListener("visibilitychange", onVisibility);
1690
+ };
1691
+ }, [safeRefetch]);
1692
+ useEffect4(() => {
1693
+ if (!awaiting) return;
1694
+ const interval = window.setInterval(() => {
1695
+ if (document.visibilityState === "hidden") return;
1696
+ safeRefetch();
1697
+ }, POLL_INTERVAL_MS);
1698
+ const timeout = window.setTimeout(() => {
1699
+ setAwaitingTimedOut(true);
1700
+ }, AWAITING_TIMEOUT_MS);
1701
+ return () => {
1702
+ window.clearInterval(interval);
1703
+ window.clearTimeout(timeout);
1704
+ };
1705
+ }, [awaiting, safeRefetch]);
1706
+ useEffect4(() => {
1707
+ if (!awaiting) return;
1708
+ if (!hasPaidPurchase) return;
1709
+ const newRef = activePurchase?.reference ?? null;
1710
+ const isNewPurchase = !awaiting.baselineHadPaidPurchase || newRef !== awaiting.baselineActiveRef;
1711
+ if (isNewPurchase) {
1712
+ setAwaiting(null);
1713
+ setAwaitingTimedOut(false);
1714
+ onPurchaseSuccessRef.current?.();
1715
+ }
1716
+ }, [awaiting, hasPaidPurchase, activePurchase?.reference]);
1717
+ const beginAwaiting = useCallback3(
1718
+ (href) => {
1719
+ setAwaiting({
1720
+ baselineActiveRef: activePurchase?.reference ?? null,
1721
+ baselineHadPaidPurchase: hasPaidPurchase,
1722
+ startedAt: Date.now(),
1723
+ href
1724
+ });
1725
+ setAwaitingTimedOut(false);
1726
+ },
1727
+ [activePurchase?.reference, hasPaidPurchase]
1728
+ );
1729
+ const cancelAwaiting = useCallback3(() => {
1730
+ setAwaiting(null);
1731
+ setAwaitingTimedOut(false);
1732
+ }, []);
1733
+ const dismissTimeout = useCallback3(() => {
1734
+ setAwaitingTimedOut(false);
1735
+ }, []);
1736
+ const cancelledProductName = cancelledPurchase?.productName ?? null;
1737
+ const cancelledEndDate = cancelledPurchase?.endDate;
1738
+ const cancelledDaysLeft = useMemo3(
1739
+ () => cancelledEndDate ? getDaysUntilExpiration(cancelledEndDate) : null,
1740
+ [cancelledEndDate, getDaysUntilExpiration]
1741
+ );
1742
+ const cancelledFormattedEndDate = useMemo3(
1743
+ () => cancelledEndDate ? formatDate(cancelledEndDate) : null,
1744
+ [cancelledEndDate, formatDate]
1745
+ );
1746
+ const awaitingHref = awaiting?.href ?? null;
1747
+ const inner = useMemo3(() => {
1748
+ if (awaiting && awaitingHref) {
1749
+ return /* @__PURE__ */ jsx12(
1750
+ AwaitingBody,
1751
+ {
1752
+ href: awaitingHref,
1753
+ timedOut: awaitingTimedOut,
1754
+ onReopen: dismissTimeout,
1755
+ onCancel: cancelAwaiting,
1756
+ cx
1757
+ }
1758
+ );
1759
+ }
1760
+ if (shouldShowCancelledNotice && cancelledProductName) {
1761
+ return /* @__PURE__ */ jsx12(
1762
+ CancelledBody,
1763
+ {
1764
+ productName: cancelledProductName,
1765
+ endDate: cancelledEndDate,
1766
+ daysLeft: cancelledDaysLeft,
1767
+ formattedEndDate: cancelledFormattedEndDate,
1768
+ checkout,
1769
+ onLaunch: beginAwaiting,
1770
+ cx
1771
+ }
1772
+ );
1773
+ }
1774
+ return /* @__PURE__ */ jsx12(UpgradeBody, { checkout, onLaunch: beginAwaiting, cx });
1775
+ }, [
1776
+ awaiting,
1777
+ awaitingHref,
1778
+ awaitingTimedOut,
1779
+ dismissTimeout,
1780
+ cancelAwaiting,
1781
+ shouldShowCancelledNotice,
1782
+ cancelledProductName,
1783
+ cancelledEndDate,
1784
+ cancelledDaysLeft,
1785
+ cancelledFormattedEndDate,
1786
+ checkout,
1787
+ beginAwaiting,
1788
+ cx
1789
+ ]);
1790
+ if (loading) {
1791
+ return /* @__PURE__ */ jsx12("div", { className: cx.card, children: /* @__PURE__ */ jsx12("p", { children: "Loading purchase\u2026" }) });
1792
+ }
1793
+ return /* @__PURE__ */ jsxs11("div", { className: cx.card, "data-refreshing": isRefetching ? "true" : void 0, children: [
1794
+ inner,
1795
+ children
1796
+ ] });
1797
+ }
1798
+
1799
+ // src/mcp/views/McpPaywallView.tsx
1800
+ import { jsx as jsx13, jsxs as jsxs12 } from "react/jsx-runtime";
1801
+ function McpPaywallView({
1802
+ content,
1803
+ publishableKey = null,
1804
+ returnUrl,
1805
+ classNames,
1806
+ onResolved,
1807
+ plans,
1808
+ upgradeCta
1809
+ }) {
1810
+ const cx = resolveMcpClassNames(classNames);
1811
+ const copy = useCopy();
1812
+ const probe = useStripeProbe(publishableKey);
1813
+ const productRef = content.product;
1814
+ const embeddedReady = probe === "ready" && Boolean(productRef);
1815
+ const isLoading = probe === "loading";
1816
+ return /* @__PURE__ */ jsxs12(
1817
+ PaywallNotice.Root,
1818
+ {
1819
+ content,
1820
+ onResolved,
1821
+ classNames: {
1822
+ // Flat flex-column stack — no outer card. `EmbeddedCheckout`
1823
+ // provides the single card below.
1824
+ root: cx.stack,
1825
+ heading: cx.heading,
1826
+ message: cx.muted,
1827
+ balance: cx.notice,
1828
+ hostedLink: cx.button
1829
+ },
1830
+ children: [
1831
+ /* @__PURE__ */ jsx13(PaywallNotice.Heading, {}),
1832
+ /* @__PURE__ */ jsx13(PaywallNotice.Message, {}),
1833
+ /* @__PURE__ */ jsx13(PaywallNotice.Balance, {}),
1834
+ isLoading ? /* @__PURE__ */ jsx13("div", { className: cx.muted, children: copy.paywall.hostedCheckoutLoading }) : embeddedReady ? /* @__PURE__ */ jsx13(
1835
+ EmbeddedCheckout,
1836
+ {
1837
+ productRef,
1838
+ returnUrl,
1839
+ fromPaywall: true,
1840
+ paywallKind: content.kind,
1841
+ hideUpgradeBanner: true,
1842
+ plans,
1843
+ onRefreshBootstrap: onResolved,
1844
+ onClose: onResolved,
1845
+ cx
1846
+ }
1847
+ ) : /* @__PURE__ */ jsx13(PaywallNotice.HostedCheckoutLink, {}),
1848
+ upgradeCta ? /* @__PURE__ */ jsx13(
1849
+ "button",
1850
+ {
1851
+ type: "button",
1852
+ className: `${cx.button} solvapay-mcp-paywall-upgrade-cta`.trim(),
1853
+ "data-variant": "secondary",
1854
+ onClick: upgradeCta.onClick,
1855
+ children: upgradeCta.label
1856
+ }
1857
+ ) : null
1858
+ ]
1859
+ }
1860
+ );
1861
+ }
1862
+
1863
+ // src/mcp/views/McpTopupView.tsx
1864
+ import { useState as useState6 } from "react";
1865
+ import { jsx as jsx14, jsxs as jsxs13 } from "react/jsx-runtime";
1866
+ var FALLBACK_TOPUP_CURRENCY = "USD";
1867
+ function McpTopupView({
1868
+ publishableKey = null,
1869
+ returnUrl,
1870
+ onTopupSuccess,
1871
+ onBack,
1872
+ classNames
1873
+ }) {
1874
+ const cx = resolveMcpClassNames(classNames);
1875
+ const probe = useStripeProbe(publishableKey);
1876
+ const { merchant, loading: merchantLoading } = useMerchant();
1877
+ if (probe === "loading" || merchantLoading) {
1878
+ return /* @__PURE__ */ jsx14("div", { className: cx.card, children: /* @__PURE__ */ jsx14("p", { children: "Loading top-up\u2026" }) });
1879
+ }
1880
+ if (probe === "blocked") return /* @__PURE__ */ jsx14(HostedTopupFallback, { cx, onBack });
1881
+ const currency = merchant?.defaultCurrency?.toUpperCase() ?? FALLBACK_TOPUP_CURRENCY;
1882
+ return /* @__PURE__ */ jsx14(
1883
+ EmbeddedTopup,
1884
+ {
1885
+ returnUrl,
1886
+ currency,
1887
+ onTopupSuccess,
1888
+ onBack,
1889
+ cx
1890
+ }
1891
+ );
1892
+ }
1893
+ function EmbeddedTopup({
1894
+ returnUrl,
1895
+ currency,
1896
+ onTopupSuccess,
1897
+ onBack,
1898
+ cx
1899
+ }) {
1900
+ const [committedAmountMinor, setCommittedAmountMinor] = useState6(null);
1901
+ const [justPaidMinor, setJustPaidMinor] = useState6(null);
1902
+ const { adjustBalance, creditsPerMinorUnit } = useBalance();
1903
+ const locale = useHostLocale();
1904
+ const { notifyModelContext, notifySuccess } = useMcpBridge();
1905
+ if (justPaidMinor != null) {
1906
+ const displayAmount = formatPrice(justPaidMinor, currency, { locale, free: "" });
1907
+ return /* @__PURE__ */ jsxs13("div", { className: cx.card, children: [
1908
+ onBack ? /* @__PURE__ */ jsx14(BackLink, { label: "Back to my account", onClick: onBack }) : null,
1909
+ /* @__PURE__ */ jsxs13("div", { className: cx.balanceRow, children: [
1910
+ /* @__PURE__ */ jsx14("h2", { className: cx.heading, children: "Credits added" }),
1911
+ /* @__PURE__ */ jsx14(BalanceBadge, {})
1912
+ ] }),
1913
+ /* @__PURE__ */ jsxs13("p", { className: cx.muted, children: [
1914
+ displayAmount,
1915
+ " landed in your balance."
1916
+ ] }),
1917
+ /* @__PURE__ */ jsx14(
1918
+ "button",
1919
+ {
1920
+ type: "button",
1921
+ className: cx.button,
1922
+ onClick: () => setJustPaidMinor(null),
1923
+ children: "Add more credits"
1924
+ }
1925
+ ),
1926
+ /* @__PURE__ */ jsx14(
1927
+ LaunchCustomerPortalButton,
1928
+ {
1929
+ className: cx.button,
1930
+ loadingClassName: cx.button,
1931
+ errorClassName: cx.button,
1932
+ children: "Manage billing"
1933
+ }
1934
+ )
1935
+ ] });
1936
+ }
1937
+ if (committedAmountMinor != null && committedAmountMinor > 0) {
1938
+ const displayAmount = formatPrice(committedAmountMinor, currency, { locale, free: "" });
1939
+ return /* @__PURE__ */ jsxs13("div", { className: cx.card, children: [
1940
+ onBack ? /* @__PURE__ */ jsx14(BackLink, { label: "Back to my account", onClick: onBack }) : null,
1941
+ /* @__PURE__ */ jsxs13("div", { className: cx.balanceRow, children: [
1942
+ /* @__PURE__ */ jsx14("h2", { className: cx.heading, children: "Pay with card" }),
1943
+ /* @__PURE__ */ jsx14(BalanceBadge, {})
1944
+ ] }),
1945
+ /* @__PURE__ */ jsxs13("p", { className: cx.muted, children: [
1946
+ "Adding ",
1947
+ displayAmount,
1948
+ " in credits."
1949
+ ] }),
1950
+ /* @__PURE__ */ jsxs13(
1951
+ TopupForm.Root,
1952
+ {
1953
+ amount: committedAmountMinor,
1954
+ currency,
1955
+ returnUrl,
1956
+ className: cx.topupForm,
1957
+ onSuccess: () => {
1958
+ adjustBalance(committedAmountMinor * (creditsPerMinorUnit ?? 100));
1959
+ setJustPaidMinor(committedAmountMinor);
1960
+ void notifyModelContext({
1961
+ text: `Topup of ${formatPrice(committedAmountMinor, currency, {
1962
+ locale,
1963
+ free: ""
1964
+ })} succeeded.`
1965
+ });
1966
+ void notifySuccess({
1967
+ kind: "topup",
1968
+ amountMinor: committedAmountMinor,
1969
+ currency
1970
+ });
1971
+ onTopupSuccess?.(committedAmountMinor);
1972
+ setCommittedAmountMinor(null);
1973
+ },
1974
+ children: [
1975
+ /* @__PURE__ */ jsx14(TopupForm.Loading, {}),
1976
+ /* @__PURE__ */ jsx14(TopupForm.PaymentElement, {}),
1977
+ /* @__PURE__ */ jsx14(TopupForm.Error, { className: cx.error }),
1978
+ /* @__PURE__ */ jsx14(TopupForm.SubmitButton, { className: cx.button })
1979
+ ]
1980
+ }
1981
+ ),
1982
+ /* @__PURE__ */ jsx14(
1983
+ BackLink,
1984
+ {
1985
+ label: "Change amount",
1986
+ onClick: () => setCommittedAmountMinor(null)
1987
+ }
1988
+ )
1989
+ ] });
1990
+ }
1991
+ return /* @__PURE__ */ jsxs13("div", { className: cx.card, children: [
1992
+ onBack ? /* @__PURE__ */ jsx14(BackLink, { label: "Back to my account", onClick: onBack }) : null,
1993
+ /* @__PURE__ */ jsxs13("div", { className: cx.balanceRow, children: [
1994
+ /* @__PURE__ */ jsx14("h2", { className: cx.heading, children: "Add credits" }),
1995
+ /* @__PURE__ */ jsx14(BalanceBadge, {})
1996
+ ] }),
1997
+ /* @__PURE__ */ jsxs13(AmountPicker.Root, { currency, emit: "minor", className: cx.amountPicker, children: [
1998
+ /* @__PURE__ */ jsx14(QuickAmountOptions, { className: cx.amountOptions, optionClassName: cx.amountOption }),
1999
+ /* @__PURE__ */ jsx14(AmountPicker.Custom, { className: cx.amountCustom }),
2000
+ /* @__PURE__ */ jsx14(
2001
+ AmountPicker.Confirm,
2002
+ {
2003
+ className: cx.button,
2004
+ onConfirm: (amountMinor) => {
2005
+ setCommittedAmountMinor(amountMinor);
2006
+ void notifyModelContext({
2007
+ text: `User confirmed topup of ${formatPrice(amountMinor, currency, {
2008
+ locale,
2009
+ free: ""
2010
+ })}.`
2011
+ });
2012
+ },
2013
+ children: "Continue"
2014
+ }
2015
+ )
2016
+ ] })
2017
+ ] });
2018
+ }
2019
+ function QuickAmountOptions({
2020
+ className,
2021
+ optionClassName
2022
+ }) {
2023
+ const { quickAmounts } = useAmountPicker();
2024
+ return /* @__PURE__ */ jsx14("div", { className, children: quickAmounts.map((amount) => /* @__PURE__ */ jsx14(AmountPicker.Option, { amount, className: optionClassName }, amount)) });
2025
+ }
2026
+ function HostedTopupFallback({ cx, onBack }) {
2027
+ return /* @__PURE__ */ jsxs13("div", { className: cx.card, children: [
2028
+ onBack ? /* @__PURE__ */ jsx14(BackLink, { label: "Back to my account", onClick: onBack }) : null,
2029
+ /* @__PURE__ */ jsx14("h2", { className: cx.heading, children: "Add credits" }),
2030
+ /* @__PURE__ */ jsx14("p", { className: cx.muted, children: "This host doesn't allow embedded payments. Open the SolvaPay portal in a new tab to complete your top-up there." }),
2031
+ /* @__PURE__ */ jsx14(
2032
+ LaunchCustomerPortalButton,
2033
+ {
2034
+ className: cx.button,
2035
+ loadingClassName: cx.button,
2036
+ errorClassName: cx.button,
2037
+ children: "Open SolvaPay portal"
2038
+ }
2039
+ )
2040
+ ] });
2041
+ }
2042
+
2043
+ // src/mcp/components/McpUpsellStrip.tsx
2044
+ import { useState as useState7 } from "react";
2045
+ import { jsx as jsx15, jsxs as jsxs14 } from "react/jsx-runtime";
2046
+ var NUDGE_DEFAULTS = {
2047
+ "low-balance": { label: "Upgrade" },
2048
+ "cycle-ending": { label: "Renew" },
2049
+ "approaching-limit": { label: "Upgrade" }
2050
+ };
2051
+ function McpUpsellStrip({
2052
+ nudge,
2053
+ onCta,
2054
+ onDismiss,
2055
+ hideDismiss = false,
2056
+ ctaLabel,
2057
+ className
2058
+ }) {
2059
+ const [dismissed, setDismissed] = useState7(false);
2060
+ if (dismissed) return null;
2061
+ const label = ctaLabel ?? NUDGE_DEFAULTS[nudge.kind].label;
2062
+ const handleDismiss = () => {
2063
+ setDismissed(true);
2064
+ onDismiss?.();
2065
+ };
2066
+ const rootClass = [
2067
+ "solvapay-mcp-upsell-strip",
2068
+ `solvapay-mcp-upsell-strip--${nudge.kind}`,
2069
+ className
2070
+ ].filter(Boolean).join(" ");
2071
+ return /* @__PURE__ */ jsxs14("aside", { className: rootClass, role: "status", "aria-live": "polite", "data-testid": "mcp-upsell-strip", children: [
2072
+ /* @__PURE__ */ jsx15("span", { className: "solvapay-mcp-upsell-strip-message", children: nudge.message }),
2073
+ /* @__PURE__ */ jsxs14("div", { className: "solvapay-mcp-upsell-strip-actions", children: [
2074
+ onCta ? /* @__PURE__ */ jsx15(
2075
+ "button",
2076
+ {
2077
+ type: "button",
2078
+ onClick: onCta,
2079
+ className: "solvapay-mcp-upsell-strip-cta",
2080
+ children: label
2081
+ }
2082
+ ) : null,
2083
+ hideDismiss ? null : /* @__PURE__ */ jsx15(
2084
+ "button",
2085
+ {
2086
+ type: "button",
2087
+ onClick: handleDismiss,
2088
+ className: "solvapay-mcp-upsell-strip-dismiss",
2089
+ "aria-label": "Dismiss",
2090
+ children: "\xD7"
2091
+ }
2092
+ )
2093
+ ] })
2094
+ ] });
2095
+ }
2096
+
2097
+ // src/mcp/views/McpNudgeView.tsx
2098
+ import { jsx as jsx16, jsxs as jsxs15 } from "react/jsx-runtime";
2099
+ function McpNudgeView({ bootstrap, onCta, onDismiss, classNames }) {
2100
+ const cx = resolveMcpClassNames(classNames);
2101
+ const { nudge, data } = bootstrap;
2102
+ return /* @__PURE__ */ jsxs15("section", { className: "solvapay-mcp-nudge-view", children: [
2103
+ nudge ? /* @__PURE__ */ jsx16(McpUpsellStrip, { nudge, onCta, onDismiss }) : null,
2104
+ data !== void 0 ? /* @__PURE__ */ jsxs15("div", { className: cx.card, children: [
2105
+ /* @__PURE__ */ jsx16("h2", { className: cx.heading, children: "Tool result" }),
2106
+ /* @__PURE__ */ jsx16("pre", { className: "solvapay-mcp-nudge-data", children: JSON.stringify(data, null, 2) })
2107
+ ] }) : null
2108
+ ] });
2109
+ }
2110
+
2111
+ // src/mcp/McpAppShell.tsx
2112
+ import { jsx as jsx17, jsxs as jsxs16 } from "react/jsx-runtime";
2113
+ function resolveSurface(bootstrapView) {
2114
+ switch (bootstrapView) {
2115
+ case "checkout":
2116
+ case "about":
2117
+ // About folds into checkout's picker.
2118
+ case "activate":
2119
+ return "checkout";
2120
+ case "topup":
2121
+ return "topup";
2122
+ case "paywall":
2123
+ return "paywall";
2124
+ case "nudge":
2125
+ return "nudge";
2126
+ case "usage":
2127
+ // Usage folds into the account surface.
2128
+ case "account":
2129
+ default:
2130
+ return "account";
2131
+ }
2132
+ }
2133
+ function McpAppShell({
2134
+ bootstrap,
2135
+ views,
2136
+ classNames,
2137
+ footer,
2138
+ onRefreshBootstrap,
2139
+ onClose
2140
+ }) {
2141
+ const { merchant } = useMerchant();
2142
+ const [paywallDismissed, setPaywallDismissed] = useState8(false);
2143
+ const [overrideView, setOverrideView] = useState8(null);
2144
+ const [cameFromPaywall, setCameFromPaywall] = useState8(false);
2145
+ const initialFromPaywall = bootstrap.view === "paywall";
2146
+ const resolvedView = resolveSurface(bootstrap.view);
2147
+ const isPaywall = resolvedView === "paywall" && !paywallDismissed;
2148
+ const effectiveView = overrideView ?? (isPaywall ? "paywall" : resolvedView);
2149
+ const showShellHeader = effectiveView !== "nudge";
2150
+ const isChrome = showShellHeader && effectiveView !== "paywall";
2151
+ const fromPaywall = cameFromPaywall || initialFromPaywall;
2152
+ const refreshedRef = useRef3(false);
2153
+ useEffect5(() => {
2154
+ if (!onRefreshBootstrap) return;
2155
+ if (refreshedRef.current) return;
2156
+ if (bootstrap.view === "paywall" || bootstrap.view === "nudge") return;
2157
+ refreshedRef.current = true;
2158
+ void Promise.resolve(onRefreshBootstrap()).catch(() => {
2159
+ });
2160
+ }, [onRefreshBootstrap, bootstrap.view]);
2161
+ const showFooter = footer ?? Boolean(merchant?.termsUrl || merchant?.privacyUrl);
2162
+ const isShellSidebarEligible = isChrome && bootstrap.customer !== null;
2163
+ const productName = bootstrap.product?.name ?? null;
2164
+ const productDescription = bootstrap.product?.description ?? null;
2165
+ const handlePaywallUpgrade = () => {
2166
+ setPaywallDismissed(true);
2167
+ setCameFromPaywall(true);
2168
+ setOverrideView("checkout");
2169
+ };
2170
+ const handleNudgeCta = () => {
2171
+ setOverrideView("checkout");
2172
+ };
2173
+ const handleChangePlan = () => {
2174
+ setCameFromPaywall(false);
2175
+ setOverrideView("checkout");
2176
+ };
2177
+ return /* @__PURE__ */ jsxs16("div", { className: "solvapay-mcp-shell", "data-paywall": isPaywall ? "true" : void 0, children: [
2178
+ showShellHeader ? /* @__PURE__ */ jsx17(
2179
+ ShellHeader,
2180
+ {
2181
+ merchant,
2182
+ productName,
2183
+ productDescription,
2184
+ classNames
2185
+ }
2186
+ ) : null,
2187
+ /* @__PURE__ */ jsxs16("div", { className: "solvapay-mcp-shell-layout", children: [
2188
+ /* @__PURE__ */ jsx17("div", { className: "solvapay-mcp-shell-body", children: /* @__PURE__ */ jsx17(
2189
+ McpViewRouter,
2190
+ {
2191
+ view: effectiveView,
2192
+ bootstrap,
2193
+ views,
2194
+ classNames,
2195
+ suppressDetailCards: isShellSidebarEligible,
2196
+ fromPaywall,
2197
+ onSurfaceChange: setOverrideView,
2198
+ onPaywallUpgrade: handlePaywallUpgrade,
2199
+ onNudgeCta: handleNudgeCta,
2200
+ onChangePlan: handleChangePlan,
2201
+ onRefreshBootstrap,
2202
+ onClose
2203
+ }
2204
+ ) }),
2205
+ isShellSidebarEligible ? /* @__PURE__ */ jsxs16("aside", { className: "solvapay-mcp-shell-sidebar", "aria-label": "Your account context", children: [
2206
+ /* @__PURE__ */ jsx17(McpSellerDetailsCard, { classNames }),
2207
+ /* @__PURE__ */ jsx17(
2208
+ McpCustomerDetailsCard,
2209
+ {
2210
+ classNames,
2211
+ onTopup: () => setOverrideView("topup")
2212
+ }
2213
+ )
2214
+ ] }) : null
2215
+ ] }),
2216
+ isChrome && showFooter ? /* @__PURE__ */ jsx17(ShellFooter, { classNames, merchant }) : null
2217
+ ] });
2218
+ }
2219
+ function ShellHeader({
2220
+ merchant,
2221
+ productName,
2222
+ productDescription,
2223
+ classNames
2224
+ }) {
2225
+ const cx = resolveMcpClassNames(classNames);
2226
+ const displayName = merchant?.displayName ?? null;
2227
+ const logoUrl = merchant?.logoUrl ?? null;
2228
+ const initials = displayName ? displayName.split(/\s+/).filter(Boolean).slice(0, 2).map((part) => part[0].toUpperCase()).join("") : "SP";
2229
+ const headingText = productName ?? displayName ?? "My account";
2230
+ const tagline = productName && productDescription ? productDescription.trim() : null;
2231
+ return /* @__PURE__ */ jsxs16("header", { className: "solvapay-mcp-shell-header", children: [
2232
+ /* @__PURE__ */ jsxs16("div", { className: "solvapay-mcp-shell-brand", children: [
2233
+ logoUrl ? /* @__PURE__ */ jsx17(
2234
+ "img",
2235
+ {
2236
+ className: "solvapay-mcp-shell-logo",
2237
+ src: logoUrl,
2238
+ alt: displayName ?? "Merchant logo"
2239
+ }
2240
+ ) : /* @__PURE__ */ jsx17("span", { className: "solvapay-mcp-shell-logo-initials", "aria-hidden": "true", children: initials }),
2241
+ displayName ? /* @__PURE__ */ jsx17("span", { className: "solvapay-mcp-shell-brand-name", children: displayName }) : null
2242
+ ] }),
2243
+ /* @__PURE__ */ jsx17("h1", { className: `${cx.heading} solvapay-mcp-shell-title`.trim(), children: headingText }),
2244
+ tagline ? /* @__PURE__ */ jsx17("p", { className: "solvapay-mcp-shell-tagline", children: tagline }) : null
2245
+ ] });
2246
+ }
2247
+ function ShellFooter({
2248
+ classNames,
2249
+ merchant
2250
+ }) {
2251
+ const cx = resolveMcpClassNames(classNames);
2252
+ const termsUrl = merchant?.termsUrl;
2253
+ const privacyUrl = merchant?.privacyUrl;
2254
+ if (!termsUrl && !privacyUrl) {
2255
+ return /* @__PURE__ */ jsx17("footer", { className: `solvapay-mcp-shell-footer ${cx.muted}`.trim(), children: "Provided by SolvaPay" });
2256
+ }
2257
+ return /* @__PURE__ */ jsxs16("footer", { className: `solvapay-mcp-shell-footer ${cx.muted}`.trim(), children: [
2258
+ termsUrl ? /* @__PURE__ */ jsxs16(
2259
+ "a",
2260
+ {
2261
+ className: "solvapay-mcp-shell-footer-link",
2262
+ href: termsUrl,
2263
+ target: "_blank",
2264
+ rel: "noopener noreferrer",
2265
+ "aria-label": "Terms (opens in a new tab)",
2266
+ children: [
2267
+ "Terms",
2268
+ /* @__PURE__ */ jsx17(ExternalLinkGlyph, {})
2269
+ ]
2270
+ }
2271
+ ) : null,
2272
+ termsUrl && privacyUrl ? /* @__PURE__ */ jsx17("span", { "aria-hidden": "true", children: " \xB7 " }) : null,
2273
+ privacyUrl ? /* @__PURE__ */ jsxs16(
2274
+ "a",
2275
+ {
2276
+ className: "solvapay-mcp-shell-footer-link",
2277
+ href: privacyUrl,
2278
+ target: "_blank",
2279
+ rel: "noopener noreferrer",
2280
+ "aria-label": "Privacy (opens in a new tab)",
2281
+ children: [
2282
+ "Privacy",
2283
+ /* @__PURE__ */ jsx17(ExternalLinkGlyph, {})
2284
+ ]
2285
+ }
2286
+ ) : null,
2287
+ /* @__PURE__ */ jsx17("span", { "aria-hidden": "true", children: " \xB7 " }),
2288
+ /* @__PURE__ */ jsx17("span", { children: "Provided by SolvaPay" })
2289
+ ] });
2290
+ }
2291
+ function McpViewRouter({
2292
+ view,
2293
+ bootstrap,
2294
+ views,
2295
+ classNames,
2296
+ suppressDetailCards,
2297
+ fromPaywall,
2298
+ onSurfaceChange,
2299
+ onPaywallUpgrade,
2300
+ onNudgeCta,
2301
+ onChangePlan,
2302
+ onRefreshBootstrap,
2303
+ onClose
2304
+ }) {
2305
+ const { productRef, stripePublishableKey, returnUrl, paywall } = bootstrap;
2306
+ const locale = useHostLocale();
2307
+ const CheckoutView = views?.checkout ?? McpCheckoutView;
2308
+ const AccountView = views?.account ?? McpAccountView;
2309
+ const TopupView = views?.topup ?? McpTopupView;
2310
+ const PaywallView = views?.paywall ?? McpPaywallView;
2311
+ const NudgeView = views?.nudge ?? McpNudgeView;
2312
+ const goCheckout = onSurfaceChange ? () => onSurfaceChange("checkout") : void 0;
2313
+ const goTopup = onSurfaceChange ? () => onSurfaceChange("topup") : void 0;
2314
+ const goAccount = onSurfaceChange ? () => onSurfaceChange("account") : void 0;
2315
+ const changePlan = onChangePlan ?? goCheckout;
2316
+ const nudgeCta = onNudgeCta ?? goCheckout;
2317
+ const paywallUpgrade = onPaywallUpgrade ?? goCheckout;
2318
+ switch (view) {
2319
+ case "checkout":
2320
+ return /* @__PURE__ */ jsx17(
2321
+ CheckoutView,
2322
+ {
2323
+ productRef,
2324
+ publishableKey: stripePublishableKey,
2325
+ returnUrl,
2326
+ classNames,
2327
+ fromPaywall,
2328
+ paywallKind: paywall?.kind,
2329
+ plans: bootstrap.plans,
2330
+ onRequestTopup: goTopup,
2331
+ onRefreshBootstrap,
2332
+ onClose
2333
+ }
2334
+ );
2335
+ case "account":
2336
+ return /* @__PURE__ */ jsx17(
2337
+ AccountView,
2338
+ {
2339
+ classNames,
2340
+ onTopup: goTopup,
2341
+ onChangePlan: changePlan,
2342
+ hideDetailCards: suppressDetailCards
2343
+ }
2344
+ );
2345
+ case "topup":
2346
+ return /* @__PURE__ */ jsx17(
2347
+ TopupView,
2348
+ {
2349
+ publishableKey: stripePublishableKey,
2350
+ returnUrl,
2351
+ classNames,
2352
+ onBack: goAccount
2353
+ }
2354
+ );
2355
+ case "paywall": {
2356
+ if (!paywall) return null;
2357
+ const upgradeCandidate = findRecurringPlan(bootstrap.plans);
2358
+ const upgradeCta = upgradeCandidate && paywallUpgrade ? { label: formatUpgradeLabel(upgradeCandidate, locale), onClick: paywallUpgrade } : void 0;
2359
+ return /* @__PURE__ */ jsx17(
2360
+ PaywallView,
2361
+ {
2362
+ content: paywall,
2363
+ publishableKey: stripePublishableKey,
2364
+ returnUrl,
2365
+ classNames,
2366
+ plans: bootstrap.plans,
2367
+ upgradeCta
2368
+ }
2369
+ );
2370
+ }
2371
+ case "nudge":
2372
+ return /* @__PURE__ */ jsx17(
2373
+ NudgeView,
2374
+ {
2375
+ bootstrap,
2376
+ onCta: nudgeCta,
2377
+ classNames
2378
+ }
2379
+ );
2380
+ default:
2381
+ return null;
2382
+ }
2383
+ }
2384
+ function findRecurringPlan(plans) {
2385
+ const list = plans;
2386
+ if (!list || list.length === 0) return null;
2387
+ const match = list.find((p) => {
2388
+ if (p.planType !== "recurring") return false;
2389
+ return !p.meterRef;
2390
+ });
2391
+ return match ?? null;
2392
+ }
2393
+ var BILLING_CYCLE_SUFFIX = {
2394
+ daily: "d",
2395
+ weekly: "wk",
2396
+ monthly: "mo",
2397
+ quarterly: "qtr",
2398
+ yearly: "yr",
2399
+ annual: "yr",
2400
+ annually: "yr"
2401
+ };
2402
+ function abbreviateBillingCycle(cycle) {
2403
+ const key = cycle.toLowerCase();
2404
+ if (BILLING_CYCLE_SUFFIX[key]) return BILLING_CYCLE_SUFFIX[key];
2405
+ return key.slice(0, 2);
2406
+ }
2407
+ function formatUpgradeLabel(plan, locale) {
2408
+ const name = plan.name ?? "Unlimited";
2409
+ if (typeof plan.price === "number" && plan.price > 0 && plan.currency) {
2410
+ const amount = formatPrice(plan.price, plan.currency, { locale });
2411
+ const cycle = plan.billingCycle ? `/${abbreviateBillingCycle(plan.billingCycle)}` : "";
2412
+ return `Upgrade to ${name} \u2014 ${amount}${cycle}`;
2413
+ }
2414
+ return `Upgrade to ${name}`;
2415
+ }
2416
+
2417
+ // src/mcp/McpApp.tsx
2418
+ import { jsx as jsx18, jsxs as jsxs17 } from "react/jsx-runtime";
2419
+ function bootstrapToInitial(bs) {
2420
+ return {
2421
+ customerRef: bs.customer?.ref ?? null,
2422
+ purchase: bs.customer?.purchase ?? null,
2423
+ paymentMethod: bs.customer?.paymentMethod ?? null,
2424
+ balance: bs.customer?.balance ?? null,
2425
+ usage: bs.customer?.usage ?? null,
2426
+ merchant: bs.merchant,
2427
+ product: bs.product,
2428
+ plans: bs.plans
2429
+ };
2430
+ }
2431
+ function McpApp({
2432
+ app,
2433
+ productRef: productRefOverride,
2434
+ views,
2435
+ classNames,
2436
+ footer,
2437
+ onInitError,
2438
+ applyContext,
2439
+ onClose,
2440
+ messageOnSuccess
2441
+ }) {
2442
+ const cx = resolveMcpClassNames(classNames);
2443
+ const [bootstrap, setBootstrap] = useState9(null);
2444
+ const [initError, setInitError] = useState9(null);
2445
+ const pendingBootstrapFetchRef = useRef4(0);
2446
+ const applyContextRef = useRef4(applyContext);
2447
+ const onInitErrorRef = useRef4(onInitError);
2448
+ useEffect6(() => {
2449
+ applyContextRef.current = applyContext;
2450
+ }, [applyContext]);
2451
+ useEffect6(() => {
2452
+ onInitErrorRef.current = onInitError;
2453
+ }, [onInitError]);
2454
+ useEffect6(() => {
2455
+ let cancelled = false;
2456
+ setInitError(null);
2457
+ setBootstrap(null);
2458
+ app.onhostcontextchanged = (ctx) => {
2459
+ applyContextRef.current?.(ctx);
2460
+ };
2461
+ app.onteardown = async () => ({});
2462
+ let resolveFirstNotification;
2463
+ const firstNotificationApplied = new Promise((r) => {
2464
+ resolveFirstNotification = r;
2465
+ });
2466
+ const onToolResult = (params) => {
2467
+ if (cancelled) return;
2468
+ const toolName = app.getHostContext?.()?.toolInfo?.tool?.name ?? null;
2469
+ if (toolName && isTransportToolName(toolName)) return;
2470
+ if (pendingBootstrapFetchRef.current) {
2471
+ resolveFirstNotification?.();
2472
+ return;
2473
+ }
2474
+ try {
2475
+ const intentView = toolName ? VIEW_FOR_TOOL2[toolName] : void 0;
2476
+ const fallbackView = intentView ?? "paywall";
2477
+ const fresh = parseBootstrapFromToolResult(
2478
+ params,
2479
+ toolName ?? "(unknown tool)",
2480
+ fallbackView
2481
+ );
2482
+ setBootstrap(fresh);
2483
+ resolveFirstNotification?.();
2484
+ } catch (err) {
2485
+ if (typeof console !== "undefined") {
2486
+ console.warn("[solvapay] ignoring malformed tool-result notification:", err);
2487
+ }
2488
+ }
2489
+ };
2490
+ let unsubscribe;
2491
+ if (typeof app.addEventListener === "function") {
2492
+ app.addEventListener("toolresult", onToolResult);
2493
+ unsubscribe = () => {
2494
+ app.removeEventListener?.("toolresult", onToolResult);
2495
+ };
2496
+ } else {
2497
+ const prior = app.ontoolresult;
2498
+ app.ontoolresult = onToolResult;
2499
+ unsubscribe = () => {
2500
+ if (app.ontoolresult === onToolResult) app.ontoolresult = prior;
2501
+ };
2502
+ }
2503
+ ;
2504
+ (async () => {
2505
+ try {
2506
+ await app.connect();
2507
+ if (cancelled) return;
2508
+ applyContextRef.current?.(app.getHostContext());
2509
+ const classification = classifyHostEntry(app);
2510
+ if (classification.kind === "data") {
2511
+ const timeoutMs = 2e3;
2512
+ const timedOut = await Promise.race([
2513
+ firstNotificationApplied.then(() => false),
2514
+ new Promise((r) => setTimeout(() => r(true), timeoutMs))
2515
+ ]);
2516
+ if (cancelled) return;
2517
+ if (timedOut) {
2518
+ void Promise.resolve(app.requestTeardown?.()).catch(() => {
2519
+ });
2520
+ return;
2521
+ }
2522
+ return;
2523
+ }
2524
+ pendingBootstrapFetchRef.current += 1;
2525
+ try {
2526
+ const result = await fetchMcpBootstrap(app);
2527
+ if (!cancelled) setBootstrap(result);
2528
+ } finally {
2529
+ pendingBootstrapFetchRef.current = Math.max(
2530
+ 0,
2531
+ pendingBootstrapFetchRef.current - 1
2532
+ );
2533
+ }
2534
+ } catch (err) {
2535
+ if (cancelled) return;
2536
+ const message = err instanceof Error ? err.message : "Failed to initialize SolvaPay";
2537
+ setInitError(message);
2538
+ onInitErrorRef.current?.(err instanceof Error ? err : new Error(message));
2539
+ }
2540
+ })();
2541
+ return () => {
2542
+ cancelled = true;
2543
+ unsubscribe?.();
2544
+ };
2545
+ }, [app]);
2546
+ const transport = useMemo4(() => createMcpAppAdapter(app), [app]);
2547
+ const initial = useMemo4(
2548
+ () => bootstrap ? bootstrapToInitial(bootstrap) : void 0,
2549
+ [bootstrap]
2550
+ );
2551
+ const iconUrl = bootstrap?.merchant ? bootstrap.merchant.iconUrl ?? bootstrap.merchant.logoUrl ?? null : null;
2552
+ useEffect6(() => {
2553
+ if (typeof document === "undefined") return;
2554
+ if (!iconUrl) return;
2555
+ const MANAGED_ATTR = "data-solvapay-favicon";
2556
+ const existing = document.head.querySelector(
2557
+ `link[${MANAGED_ATTR}]`
2558
+ );
2559
+ const link = existing ?? document.createElement("link");
2560
+ link.setAttribute(MANAGED_ATTR, "");
2561
+ link.rel = "icon";
2562
+ link.href = iconUrl;
2563
+ if (!existing) document.head.appendChild(link);
2564
+ return () => {
2565
+ };
2566
+ }, [iconUrl]);
2567
+ const providerConfig = useMemo4(
2568
+ () => {
2569
+ const resolved = {
2570
+ // `SolvaPayProvider` short-circuits its fetch pipeline when there's
2571
+ // no auth token, which means our `checkPurchase` override would
2572
+ // never run. In the MCP App the real identity lives server-side on
2573
+ // the OAuth bridge's `customer_ref`, so we just need to tell the
2574
+ // provider "yes, you're authenticated". Returning a sentinel token
2575
+ // is enough to flip `isAuthenticated` true and unlock the refetch
2576
+ // path.
2577
+ auth: {
2578
+ adapter: {
2579
+ getToken: async () => "mcp-session",
2580
+ getUserId: async () => initial?.customerRef ?? null
2581
+ }
2582
+ },
2583
+ transport,
2584
+ initial,
2585
+ refreshInitial: async () => {
2586
+ const fresh = await fetchMcpBootstrap(app);
2587
+ const next = bootstrapToInitial(fresh);
2588
+ seedMcpCaches(next, resolved);
2589
+ return next;
2590
+ }
2591
+ };
2592
+ return resolved;
2593
+ },
2594
+ [transport, initial, app]
2595
+ );
2596
+ const seededInitialRef = useRef4(null);
2597
+ if (initial && seededInitialRef.current !== initial) {
2598
+ seedMcpCaches(initial, providerConfig);
2599
+ seededInitialRef.current = initial;
2600
+ }
2601
+ const refreshBootstrap = useMemo4(
2602
+ () => async () => {
2603
+ pendingBootstrapFetchRef.current += 1;
2604
+ try {
2605
+ const fresh = await fetchMcpBootstrap(app);
2606
+ const next = bootstrapToInitial(fresh);
2607
+ seedMcpCaches(next, providerConfig);
2608
+ setBootstrap(fresh);
2609
+ } finally {
2610
+ pendingBootstrapFetchRef.current = Math.max(0, pendingBootstrapFetchRef.current - 1);
2611
+ }
2612
+ },
2613
+ [app, providerConfig]
2614
+ );
2615
+ const defaultOnClose = useMemo4(
2616
+ () => () => {
2617
+ void Promise.resolve(app.requestTeardown?.()).catch(() => {
2618
+ });
2619
+ },
2620
+ [app]
2621
+ );
2622
+ const effectiveOnClose = onClose ?? defaultOnClose;
2623
+ if (initError) {
2624
+ return /* @__PURE__ */ jsx18("main", { className: "solvapay-mcp-main", children: /* @__PURE__ */ jsxs17("div", { className: `${cx.card} ${cx.error}`.trim(), children: [
2625
+ /* @__PURE__ */ jsx18("h2", { className: cx.heading, children: "Unable to load SolvaPay" }),
2626
+ /* @__PURE__ */ jsx18("p", { children: initError })
2627
+ ] }) });
2628
+ }
2629
+ if (!bootstrap) {
2630
+ return /* @__PURE__ */ jsx18("main", { className: "solvapay-mcp-main", children: /* @__PURE__ */ jsx18("div", { className: cx.card, children: /* @__PURE__ */ jsx18("p", { children: "Loading\u2026" }) }) });
2631
+ }
2632
+ const effectiveBootstrap = productRefOverride ? { ...bootstrap, productRef: productRefOverride } : bootstrap;
2633
+ return /* @__PURE__ */ jsx18(SolvaPayProvider, { config: providerConfig, children: /* @__PURE__ */ jsx18(McpBridgeProvider, { app, messageOnSuccess, children: /* @__PURE__ */ jsx18("main", { className: "solvapay-mcp-main", children: /* @__PURE__ */ jsx18(
2634
+ McpAppShell,
2635
+ {
2636
+ bootstrap: effectiveBootstrap,
2637
+ views,
2638
+ classNames,
2639
+ ...footer !== void 0 ? { footer } : {},
2640
+ onRefreshBootstrap: refreshBootstrap,
2641
+ onClose: effectiveOnClose
2642
+ }
2643
+ ) }) }) });
2644
+ }
2645
+
2646
+ // src/mcp/plan-actions.ts
2647
+ function resolvePlanShape(plan) {
2648
+ if (!plan) return null;
2649
+ const type = plan.planType;
2650
+ if (type === "free") return "free";
2651
+ if (type === "trial") return "trial";
2652
+ if (type === "usage-based") return "usage-based";
2653
+ if (type === "recurring") {
2654
+ const hasMeter = Boolean(plan.meterRef || plan.meterId);
2655
+ const hasLimit = plan.limit != null && plan.limit > 0;
2656
+ return hasMeter || hasLimit ? "recurring-metered" : "recurring-unlimited";
2657
+ }
2658
+ if ((plan.price ?? 0) === 0) return "free";
2659
+ return "recurring-unlimited";
2660
+ }
2661
+ function resolveActivationStrategy(plan) {
2662
+ const shape = resolvePlanShape(plan);
2663
+ if (shape === "usage-based") return "topup-first";
2664
+ if (shape === "free" || shape === "trial") return "activate";
2665
+ if ((plan?.price ?? 0) === 0) return "activate";
2666
+ return "paid-checkout";
2667
+ }
2668
+ function resolvePlanActions({ purchase, planCount, paidPlanCount }) {
2669
+ const shape = resolvePlanShape(purchase?.planSnapshot);
2670
+ const hasPaymentMethod = Boolean(purchase?.hasPaymentMethod);
2671
+ const canOfferChange = planCount > 1;
2672
+ const isFree = shape === "free";
2673
+ const canUpgrade = isFree && paidPlanCount > 0;
2674
+ return {
2675
+ topUp: shape === "usage-based",
2676
+ cancel: shape === "recurring-unlimited" || shape === "recurring-metered" || shape === "free" || shape === "trial",
2677
+ changePlan: canOfferChange && !canUpgrade,
2678
+ managePortal: hasPaymentMethod,
2679
+ upgrade: canUpgrade
2680
+ };
2681
+ }
2682
+ function resolveActivityStrip(purchase) {
2683
+ const shape = resolvePlanShape(purchase?.planSnapshot);
2684
+ if (!shape) return "none";
2685
+ if (shape === "usage-based") return "payg-balance";
2686
+ if (shape === "recurring-unlimited") return "recurring-unlimited-renew";
2687
+ if (shape === "recurring-metered") return "recurring-metered-usage";
2688
+ if (shape === "free") return "free-usage";
2689
+ if (shape === "trial") return "free-usage";
2690
+ return "none";
2691
+ }
2692
+ export {
2693
+ BackLink,
2694
+ MCP_TOOL_NAMES3 as MCP_TOOL_NAMES,
2695
+ McpAccountView,
2696
+ McpApp,
2697
+ McpAppShell,
2698
+ McpBridgeProvider,
2699
+ McpCheckoutView,
2700
+ McpCustomerDetailsCard,
2701
+ McpNudgeView,
2702
+ McpPaywallView,
2703
+ McpSellerDetailsCard,
2704
+ McpTopupView,
2705
+ McpUpsellStrip,
2706
+ McpViewRouter,
2707
+ SOLVAPAY_TRANSPORT_TOOL_NAMES,
2708
+ classifyHostEntry,
2709
+ createMcpAppAdapter,
2710
+ fetchMcpBootstrap,
2711
+ isTransportToolName,
2712
+ parseBootstrapFromToolResult,
2713
+ resolveActivationStrategy,
2714
+ resolveActivityStrip,
2715
+ resolveMcpClassNames,
2716
+ resolvePlanActions,
2717
+ resolvePlanShape,
2718
+ seedMcpCaches,
2719
+ useHostLocale,
2720
+ useMcpBridge,
2721
+ useMcpToolResult,
2722
+ useStripeProbe,
2723
+ waitForInitialToolResult
2724
+ };