@solvapay/react 1.0.9-preview.1 → 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.
package/dist/index.d.ts CHANGED
@@ -1,524 +1,11 @@
1
- import React$1 from 'react';
2
- import * as _stripe_stripe_js from '@stripe/stripe-js';
3
- import { PaymentIntent, Stripe } from '@stripe/stripe-js';
4
- import { ProcessPaymentResult, ActivatePlanResult } from '@solvapay/server';
5
- export { ActivatePlanResult } from '@solvapay/server';
6
- import { AuthAdapter } from './adapters/auth.js';
7
- export { defaultAuthAdapter } from './adapters/auth.js';
8
- import * as react_jsx_runtime from 'react/jsx-runtime';
9
-
10
- interface PurchaseInfo {
11
- reference: string;
12
- productName: string;
13
- productRef?: string;
14
- status: string;
15
- startDate: string;
16
- endDate?: string;
17
- cancelledAt?: string;
18
- cancellationReason?: string;
19
- amount?: number;
20
- currency?: string;
21
- planType?: string;
22
- isRecurring?: boolean;
23
- nextBillingDate?: string;
24
- billingCycle?: string;
25
- planRef?: string;
26
- planSnapshot?: {
27
- reference?: string;
28
- price?: number;
29
- meterRef?: string;
30
- limit?: number;
31
- freeUnits?: number;
32
- creditsPerUnit?: number;
33
- planType?: string;
34
- billingCycle?: string | null;
35
- features?: Record<string, unknown> | null;
36
- };
37
- usage?: {
38
- used: number;
39
- overageUnits?: number;
40
- overageCost?: number;
41
- periodStart?: string;
42
- periodEnd?: string;
43
- };
44
- }
45
- interface CustomerPurchaseData {
46
- customerRef?: string;
47
- email?: string;
48
- name?: string;
49
- purchases: PurchaseInfo[];
50
- }
51
- interface PaymentIntentResult {
52
- clientSecret: string;
53
- publishableKey: string;
54
- accountId?: string;
55
- customerRef?: string;
56
- }
57
- interface TopupPaymentResult {
58
- clientSecret: string;
59
- publishableKey: string;
60
- accountId?: string;
61
- customerRef?: string;
62
- }
63
- interface UseTopupOptions {
64
- amount: number;
65
- currency?: string;
66
- }
67
- interface UseTopupReturn {
68
- loading: boolean;
69
- error: Error | null;
70
- stripePromise: Promise<_stripe_stripe_js.Stripe | null> | null;
71
- clientSecret: string | null;
72
- startTopup: () => Promise<void>;
73
- reset: () => void;
74
- }
75
- interface TopupFormProps {
76
- amount: number;
77
- currency?: string;
78
- onSuccess?: (paymentIntent: PaymentIntent) => void;
79
- onError?: (error: Error) => void;
80
- returnUrl?: string;
81
- submitButtonText?: string;
82
- className?: string;
83
- buttonClassName?: string;
84
- }
85
- interface PurchaseStatus {
86
- loading: boolean;
87
- /** True when data already exists but a background refetch is in progress */
88
- isRefetching: boolean;
89
- /** Last fetch error, or null if the most recent fetch succeeded */
90
- error: Error | null;
91
- customerRef?: string;
92
- email?: string;
93
- name?: string;
94
- purchases: PurchaseInfo[];
95
- hasProduct: (productName: string) => boolean;
96
- /** @deprecated Use hasProduct instead */
97
- hasPlan: (productName: string) => boolean;
98
- /**
99
- * Primary active purchase (paid or free) - most recent purchase with status === 'active'
100
- * Backend keeps purchases as 'active' until expiration, even when cancelled.
101
- * null if no active purchase exists
102
- */
103
- activePurchase: PurchaseInfo | null;
104
- /**
105
- * Check if user has any active paid purchase (amount > 0)
106
- * Checks purchases with status === 'active'.
107
- * Backend keeps purchases as 'active' until expiration, even when cancelled.
108
- */
109
- hasPaidPurchase: boolean;
110
- /**
111
- * Most recent active paid purchase (sorted by startDate)
112
- * Returns purchase with status === 'active' and amount > 0.
113
- * null if no active paid purchase exists
114
- */
115
- activePaidPurchase: PurchaseInfo | null;
116
- }
117
- /**
118
- * SolvaPay Provider Configuration
119
- * Sensible defaults for minimal code, but fully customizable
120
- */
121
- interface BalanceStatus {
122
- loading: boolean;
123
- credits: number | null;
124
- displayCurrency: string | null;
125
- creditsPerMinorUnit: number | null;
126
- displayExchangeRate: number | null;
127
- refetch: () => Promise<void>;
128
- adjustBalance: (credits: number) => void;
129
- }
130
- interface SolvaPayConfig {
131
- /**
132
- * API route configuration
133
- * Defaults to standard Next.js API routes
134
- */
135
- api?: {
136
- checkPurchase?: string;
137
- createPayment?: string;
138
- processPayment?: string;
139
- createTopupPayment?: string;
140
- customerBalance?: string;
141
- cancelRenewal?: string;
142
- reactivateRenewal?: string;
143
- activatePlan?: string;
144
- listPlans?: string;
145
- };
146
- /**
147
- * Authentication configuration
148
- * Uses adapter pattern for flexible auth provider support
149
- */
150
- auth?: {
151
- /**
152
- * Auth adapter instance
153
- * Default: checks localStorage for 'auth_token' key
154
- *
155
- * @example
156
- * ```tsx
157
- * import { createSupabaseAuthAdapter } from '@solvapay/react-supabase';
158
- *
159
- * <SolvaPayProvider
160
- * config={{
161
- * auth: {
162
- * adapter: createSupabaseAuthAdapter({
163
- * supabaseUrl: process.env.NEXT_PUBLIC_SUPABASE_URL!,
164
- * supabaseAnonKey: process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
165
- * })
166
- * }
167
- * }}
168
- * >
169
- * ```
170
- */
171
- adapter?: AuthAdapter;
172
- /**
173
- * @deprecated Use `adapter` instead. Will be removed in a future version.
174
- * Function to get auth token
175
- */
176
- getToken?: () => Promise<string | null>;
177
- /**
178
- * @deprecated Use `adapter` instead. Will be removed in a future version.
179
- * Function to get user ID (for cache key)
180
- */
181
- getUserId?: () => Promise<string | null>;
182
- };
183
- /**
184
- * Custom fetch implementation
185
- * Default: uses global fetch
186
- */
187
- fetch?: typeof fetch;
188
- /**
189
- * Request headers to include in all API calls
190
- * Default: empty
191
- */
192
- headers?: HeadersInit | (() => Promise<HeadersInit>);
193
- /**
194
- * Custom error handler
195
- * Default: logs to console
196
- */
197
- onError?: (error: Error, context: string) => void;
198
- }
199
- interface CancelResult {
200
- reference?: string;
201
- status?: string;
202
- cancelledAt?: string;
203
- [key: string]: unknown;
204
- }
205
- interface ReactivateResult {
206
- reference?: string;
207
- status?: string;
208
- [key: string]: unknown;
209
- }
210
-
211
- interface SolvaPayContextValue {
212
- purchase: PurchaseStatus;
213
- refetchPurchase: () => Promise<void>;
214
- createPayment: (params: {
215
- planRef?: string;
216
- productRef?: string;
217
- }) => Promise<PaymentIntentResult>;
218
- processPayment?: (params: {
219
- paymentIntentId: string;
220
- productRef: string;
221
- planRef?: string;
222
- }) => Promise<ProcessPaymentResult>;
223
- createTopupPayment: (params: {
224
- amount: number;
225
- currency?: string;
226
- }) => Promise<TopupPaymentResult>;
227
- cancelRenewal: (params: {
228
- purchaseRef: string;
229
- reason?: string;
230
- }) => Promise<CancelResult>;
231
- reactivateRenewal: (params: {
232
- purchaseRef: string;
233
- }) => Promise<ReactivateResult>;
234
- activatePlan: (params: {
235
- productRef: string;
236
- planRef: string;
237
- }) => Promise<ActivatePlanResult>;
238
- customerRef?: string;
239
- updateCustomerRef?: (newCustomerRef: string) => void;
240
- balance: BalanceStatus;
241
- /** @internal Provider config — used by SDK hooks, not part of public API */
242
- _config?: SolvaPayConfig;
243
- }
244
- interface SolvaPayProviderProps {
245
- /**
246
- * Configuration object with sensible defaults
247
- * If not provided, uses standard Next.js API routes
248
- */
249
- config?: SolvaPayConfig;
250
- /**
251
- * Custom API functions (override config defaults)
252
- * Use only if you need custom logic beyond standard API routes
253
- */
254
- createPayment?: (params: {
255
- planRef?: string;
256
- productRef?: string;
257
- }) => Promise<PaymentIntentResult>;
258
- checkPurchase?: () => Promise<CustomerPurchaseData>;
259
- processPayment?: (params: {
260
- paymentIntentId: string;
261
- productRef: string;
262
- planRef?: string;
263
- }) => Promise<ProcessPaymentResult>;
264
- createTopupPayment?: (params: {
265
- amount: number;
266
- currency?: string;
267
- }) => Promise<TopupPaymentResult>;
268
- children: React.ReactNode;
269
- }
270
- interface ProductBadgeProps {
271
- children?: (props: {
272
- purchases: PurchaseInfo[];
273
- loading: boolean;
274
- displayPlan: string | null;
275
- shouldShow: boolean;
276
- }) => React.ReactNode;
277
- as?: React.ElementType;
278
- className?: string | ((props: {
279
- purchases: PurchaseInfo[];
280
- }) => string);
281
- }
282
- /** @deprecated Use ProductBadgeProps instead */
283
- type PlanBadgeProps = ProductBadgeProps;
284
- interface PurchaseGateProps {
285
- /** @deprecated Use requireProduct instead */
286
- requirePlan?: string;
287
- requireProduct?: string;
288
- children: (props: {
289
- hasAccess: boolean;
290
- purchases: PurchaseInfo[];
291
- loading: boolean;
292
- }) => React.ReactNode;
293
- }
294
- /**
295
- * Error type for payment operations
296
- */
297
- interface PaymentError extends Error {
298
- code?: string;
299
- type?: string;
300
- }
301
- /**
302
- * Plan returned by the SolvaPay API.
303
- *
304
- * All fields are optional except `reference` so the type stays compatible
305
- * with partial JSON responses from custom fetcher functions.
306
- */
307
- interface Plan {
308
- type?: 'recurring' | 'one-time' | 'usage-based';
309
- reference: string;
310
- name?: string;
311
- description?: string;
312
- price?: number;
313
- currency?: string;
314
- currencySymbol?: string;
315
- freeUnits?: number;
316
- setupFee?: number;
317
- trialDays?: number;
318
- billingCycle?: string;
319
- billingModel?: 'pre-paid' | 'post-paid';
320
- creditsPerUnit?: number;
321
- measures?: string;
322
- limit?: number;
323
- rolloverUnusedUnits?: boolean;
324
- limits?: Record<string, unknown>;
325
- features?: Record<string, unknown> | string[];
326
- requiresPayment?: boolean;
327
- default?: boolean;
328
- isActive?: boolean;
329
- maxActiveUsers?: number;
330
- accessExpiryDays?: number;
331
- status?: string;
332
- createdAt?: string;
333
- updatedAt?: string;
334
- interval?: string;
335
- metadata?: Record<string, unknown>;
336
- }
337
- /**
338
- * Options for usePlans hook
339
- */
340
- interface UsePlansOptions {
341
- /**
342
- * Fetcher function to retrieve plans
343
- */
344
- fetcher: (productRef: string) => Promise<Plan[]>;
345
- /**
346
- * Product reference to fetch plans for
347
- */
348
- productRef?: string;
349
- /**
350
- * Optional filter function to filter plans.
351
- * Receives plan and its index (after sorting, if sortBy is provided).
352
- */
353
- filter?: (plan: Plan, index: number) => boolean;
354
- /**
355
- * Optional sort function to sort plans
356
- */
357
- sortBy?: (a: Plan, b: Plan) => number;
358
- /**
359
- * Auto-select first paid plan on load
360
- */
361
- autoSelectFirstPaid?: boolean;
362
- /**
363
- * Plan reference to select initially when plans load.
364
- * Applied at most once when selectionReady is true.
365
- * Takes priority over autoSelectFirstPaid.
366
- */
367
- initialPlanRef?: string;
368
- /**
369
- * When false, plans still fetch but auto-selection is deferred.
370
- * When it transitions to true, one-shot initial selection fires.
371
- * Defaults to true.
372
- */
373
- selectionReady?: boolean;
374
- }
375
- /**
376
- * Return type for usePlans hook
377
- */
378
- interface UsePlansReturn {
379
- plans: Plan[];
380
- loading: boolean;
381
- error: Error | null;
382
- selectedPlanIndex: number;
383
- selectedPlan: Plan | null;
384
- setSelectedPlanIndex: (index: number) => void;
385
- selectPlan: (planRef: string) => void;
386
- refetch: () => Promise<void>;
387
- /** True after the one-shot initial selection has been applied */
388
- isSelectionReady: boolean;
389
- }
390
- /**
391
- * Props for headless PricingSelector component
392
- */
393
- interface PricingSelectorProps {
394
- /**
395
- * Product reference to fetch plans for
396
- */
397
- productRef?: string;
398
- /**
399
- * Fetcher function to retrieve plans
400
- */
401
- fetcher: (productRef: string) => Promise<Plan[]>;
402
- /**
403
- * Optional filter function
404
- */
405
- filter?: (plan: Plan, index: number) => boolean;
406
- /**
407
- * Optional sort function
408
- */
409
- sortBy?: (a: Plan, b: Plan) => number;
410
- /**
411
- * Auto-select first paid plan on load
412
- */
413
- autoSelectFirstPaid?: boolean;
414
- /**
415
- * Render prop function
416
- */
417
- children: (props: UsePlansReturn & {
418
- purchases: PurchaseInfo[];
419
- isPaidPlan: (planRef: string) => boolean;
420
- isCurrentPlan: (planRef: string) => boolean;
421
- }) => React.ReactNode;
422
- }
423
- /** @deprecated Use PricingSelectorProps instead */
424
- type PlanSelectorProps = PricingSelectorProps;
425
- /**
426
- * Return type for usePurchaseStatus hook
427
- *
428
- * Provides advanced purchase status helpers and utilities.
429
- * Focuses on cancelled purchase logic and date formatting.
430
- * For basic purchase data and paid status, use usePurchase() instead.
431
- */
432
- interface PurchaseStatusReturn {
433
- /**
434
- * Most recent cancelled paid purchase (sorted by startDate)
435
- * null if no cancelled paid purchase exists
436
- */
437
- cancelledPurchase: PurchaseInfo | null;
438
- /**
439
- * Whether to show cancelled purchase notice
440
- * true if cancelledPurchase exists
441
- */
442
- shouldShowCancelledNotice: boolean;
443
- /**
444
- * Format a date string to locale format (e.g., "January 15, 2024")
445
- * Returns null if dateString is not provided
446
- */
447
- formatDate: (dateString?: string) => string | null;
448
- /**
449
- * Calculate days until expiration date
450
- * Returns null if endDate is not provided, otherwise returns days (0 or positive)
451
- */
452
- getDaysUntilExpiration: (endDate?: string) => number | null;
453
- }
454
- /**
455
- * Payment form props - simplified and minimal
456
- */
457
- interface PaymentFormProps {
458
- /**
459
- * Plan reference to checkout. When omitted, the SDK auto-resolves the plan from
460
- * productRef (requires exactly one active plan or a default plan). Pass explicitly
461
- * when the product has multiple plans without a default.
462
- */
463
- planRef?: string;
464
- /**
465
- * Product reference. Required when planRef is omitted (for plan resolution)
466
- * and for processing payment after confirmation.
467
- */
468
- productRef?: string;
469
- /**
470
- * Callback when payment succeeds
471
- */
472
- onSuccess?: (paymentIntent: PaymentIntent) => void;
473
- /**
474
- * Callback when payment fails
475
- */
476
- onError?: (error: Error) => void;
477
- /**
478
- * Return URL after payment completion. Defaults to current page URL if not provided.
479
- */
480
- returnUrl?: string;
481
- /**
482
- * Text for the submit button. Defaults to "Pay Now"
483
- */
484
- submitButtonText?: string;
485
- /**
486
- * Optional className for the form container
487
- */
488
- className?: string;
489
- /**
490
- * Optional className for the submit button
491
- */
492
- buttonClassName?: string;
493
- }
494
- interface BalanceBadgeProps {
495
- className?: string;
496
- numberOnly?: boolean;
497
- children?: (props: {
498
- credits: number | null;
499
- loading: boolean;
500
- displayCurrency: string | null;
501
- creditsPerMinorUnit: number | null;
502
- }) => React.ReactNode;
503
- }
504
- interface UseTopupAmountSelectorOptions {
505
- currency: string;
506
- minAmount?: number;
507
- maxAmount?: number;
508
- }
509
- interface UseTopupAmountSelectorReturn {
510
- quickAmounts: number[];
511
- selectedAmount: number | null;
512
- customAmount: string;
513
- resolvedAmount: number | null;
514
- selectQuickAmount: (amount: number) => void;
515
- setCustomAmount: (value: string) => void;
516
- error: string | null;
517
- validate: () => boolean;
518
- reset: () => void;
519
- currencySymbol: string;
520
- }
521
- type PurchaseStatusValue = 'pending' | 'active' | 'trialing' | 'past_due' | 'cancelled' | 'expired' | 'suspended' | 'refunded';
1
+ import React from 'react';
2
+ import { S as SolvaPayProviderProps, P as PaymentFormProps, a as PrefillCustomer, T as TopupFormProps, C as CheckoutResult, b as Plan, A as ActivationResult, c as PurchaseStatus, d as SolvaPayContextValue, U as UsePlansOptions, e as UsePlansReturn, f as UsePlanOptions, g as UsePlanReturn, h as UseProductReturn, i as UseMerchantReturn, j as SolvaPayCopy, k as PurchaseStatusReturn, l as CancelResult, R as ReactivateResult, m as ActivatePlanResult, n as UseTopupOptions, o as UseTopupReturn, B as BalanceStatus, p as UseTopupAmountSelectorOptions, q as UseTopupAmountSelectorReturn, r as UsePaymentMethodReturn, s as SolvaPayTransport, t as PaywallStructuredContent, u as PartialSolvaPayCopy, v as PurchaseInfo, w as Product, x as SolvaPayConfig } from './index-onWNU7iT.js';
3
+ export { y as CustomerPurchaseData, M as MandateContext, z as MandateTemplate, D as Merchant, E as PaymentError, F as PaymentIntentResult, G as PaymentMethodInfo, H as PaymentResult, I as PurchaseStatusValue, J as TopupPaymentResult, K as TransportBalanceResult, L as TransportCheckoutSessionResult, N as TransportCustomerSessionResult, O as UnsupportedTransportMethodError } from './index-onWNU7iT.js';
4
+ import { P as PaymentFormSummary, a as PaymentFormCustomerFields, b as PaymentFormPaymentElement, c as PaymentFormCardElement, d as PaymentFormMandateText, e as PaymentFormTermsCheckbox, f as PaymentFormSubmitButton, g as PaymentFormLoading, h as PaymentFormError, C as CheckoutVariant } from './useUsage-nD7zwSbG.js';
5
+ export { A as ActivationFlowStep, B as BalanceBadge, i as CancelPlanButton, j as CheckoutSummary, k as CheckoutSummaryProps, M as MandateText, l as MandateTextProps, m as PlanBadge, n as ProductBadge, o as PurchaseGate, U as UsageSnapshot, p as UseUsageReturn, q as deriveVariant, u as useUsage } from './useUsage-nD7zwSbG.js';
6
+ import { PaymentIntent, Stripe, StripeElements } from '@stripe/stripe-js';
7
+ export { AuthAdapter, defaultAuthAdapter } from './adapters/auth.js';
8
+ import '@stripe/react-stripe-js';
522
9
 
523
10
  /**
524
11
  * SolvaPay Provider - Headless Context Provider for React.
@@ -527,243 +14,415 @@ type PurchaseStatusValue = 'pending' | 'active' | 'trialing' | 'past_due' | 'can
527
14
  * via React Context. This is the root component that must wrap your app to use
528
15
  * SolvaPay React hooks and components.
529
16
  *
530
- * Features:
531
- * - Automatic purchase status checking
532
- * - Customer reference caching in localStorage
533
- * - Payment intent creation and processing
534
- * - Authentication adapter support (Supabase, custom, etc.)
535
- * - Zero-config with sensible defaults, or full customization
536
- *
537
- * @param props - Provider configuration
538
- * @param props.config - Configuration object for API routes and authentication
539
- * @param props.config.api - API route configuration (optional, uses defaults if not provided)
540
- * @param props.config.api.checkPurchase - Endpoint for checking purchase status (default: '/api/check-purchase')
541
- * @param props.config.api.createPayment - Endpoint for creating payment intents (default: '/api/create-payment-intent')
542
- * @param props.config.api.processPayment - Endpoint for processing payments (default: '/api/process-payment')
543
- * @param props.config.auth - Authentication configuration (optional)
544
- * @param props.config.auth.adapter - Auth adapter for extracting user ID and token
545
- * @param props.children - React children components
17
+ * All data access flows through `config.transport`. When omitted, a default
18
+ * HTTP transport is built from `config.api` + `config.fetch`. Integrators who
19
+ * need to route calls somewhere else (e.g. MCP hosts) pass a custom transport
20
+ * see `@solvapay/react/mcp` and `createHttpTransport` for building blocks.
546
21
  *
547
22
  * @example
548
23
  * ```tsx
549
- * import { SolvaPayProvider } from '@solvapay/react';
24
+ * import { SolvaPayProvider } from '@solvapay/react'
550
25
  *
551
- * // Zero config (uses defaults)
552
26
  * function App() {
553
27
  * return (
554
28
  * <SolvaPayProvider>
555
29
  * <YourApp />
556
30
  * </SolvaPayProvider>
557
- * );
31
+ * )
558
32
  * }
33
+ * ```
559
34
  *
560
- * // Custom API routes
561
- * function App() {
562
- * return (
563
- * <SolvaPayProvider
564
- * config={{
565
- * api: {
566
- * checkPurchase: '/custom/api/purchase',
567
- * createPayment: '/custom/api/payment'
568
- * }
569
- * }}
570
- * >
571
- * <YourApp />
572
- * </SolvaPayProvider>
573
- * );
574
- * }
35
+ * @example MCP App
36
+ * ```tsx
37
+ * import { SolvaPayProvider } from '@solvapay/react'
38
+ * import { createMcpAppAdapter } from '@solvapay/react/mcp'
575
39
  *
576
- * // With Supabase auth adapter
577
- * import { createSupabaseAuthAdapter } from '@solvapay/react-supabase';
40
+ * const transport = createMcpAppAdapter(app)
578
41
  *
579
42
  * function App() {
580
- * const adapter = createSupabaseAuthAdapter({
581
- * supabaseUrl: process.env.NEXT_PUBLIC_SUPABASE_URL!,
582
- * supabaseAnonKey: process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
583
- * });
584
- *
585
43
  * return (
586
- * <SolvaPayProvider
587
- * config={{
588
- * auth: { adapter }
589
- * }}
590
- * >
44
+ * <SolvaPayProvider config={{ transport }}>
591
45
  * <YourApp />
592
46
  * </SolvaPayProvider>
593
- * );
47
+ * )
594
48
  * }
595
49
  * ```
596
- *
597
- * @see {@link usePurchase} for accessing purchase data
598
- * @see {@link useCheckout} for payment checkout flow
599
- * @see {@link useSolvaPay} for accessing provider methods
600
- * @since 1.0.0
601
50
  */
602
- declare const SolvaPayProvider: React$1.FC<SolvaPayProviderProps>;
51
+ declare const SolvaPayProvider: React.FC<SolvaPayProviderProps>;
603
52
 
604
53
  /**
605
- * Payment form component for handling Stripe checkout.
606
- *
607
- * This component provides a complete payment form with Stripe integration,
608
- * including card input, plan selection, and payment processing. It handles
609
- * the entire checkout flow including payment intent creation and confirmation.
610
- *
611
- * When `planRef` is omitted but `productRef` is provided, the component
612
- * auto-resolves the plan by fetching the product's plans and selecting the
613
- * single active plan or the one marked as default.
614
- *
615
- * @param props - Payment form configuration
616
- * @param props.planRef - Plan reference (optional if product has single/default plan)
617
- * @param props.productRef - Product reference (required when planRef is omitted)
618
- * @param props.onSuccess - Callback when payment succeeds
619
- * @param props.onError - Callback when payment fails
620
- * @param props.returnUrl - Optional return URL after payment (for redirects)
621
- * @param props.submitButtonText - Custom text for submit button (default: 'Pay Now')
622
- * @param props.className - Custom CSS class for the form container
623
- * @param props.buttonClassName - Custom CSS class for the submit button
624
- *
625
- * @example
626
- * ```tsx
627
- * // Explicit plan
628
- * <PaymentForm planRef="pln_premium" productRef="prd_myapi" onSuccess={...} />
629
- *
630
- * // Auto-resolve plan (product must have exactly one plan or a default)
631
- * <PaymentForm productRef="prd_myapi" onSuccess={...} />
632
- * ```
54
+ * Default-tree shim over the `PaymentForm` primitive.
55
+ *
56
+ * Consumers who just want a drop-in payment form use this component. It
57
+ * renders the primitive's Root with a golden-path default tree composed of
58
+ * `PaymentForm.Summary`, `CustomerFields`, `PaymentElement`, `MandateText`,
59
+ * an optional `TermsCheckbox`, and `SubmitButton`. Free-plan activation
60
+ * flows through the same composition `FreeInner` in the primitive swaps
61
+ * the submit handler so the default tree works identically for paid and
62
+ * free plans.
63
+ *
64
+ * Full control (swap PaymentElement for CardElement, reorder, compose with
65
+ * shadcn/Tailwind) is available via the primitives at
66
+ * `@solvapay/react/primitives`.
633
67
  */
634
- declare const PaymentForm: React$1.FC<PaymentFormProps>;
68
+
69
+ type PaymentFormRootProps = PaymentFormProps & {
70
+ prefillCustomer?: PrefillCustomer;
71
+ requireTermsAcceptance?: boolean;
72
+ children?: React.ReactNode;
73
+ };
74
+ declare const PaymentForm: React.FC<PaymentFormRootProps> & {
75
+ Summary: typeof PaymentFormSummary;
76
+ CustomerFields: typeof PaymentFormCustomerFields;
77
+ PaymentElement: typeof PaymentFormPaymentElement;
78
+ CardElement: typeof PaymentFormCardElement;
79
+ MandateText: typeof PaymentFormMandateText;
80
+ TermsCheckbox: typeof PaymentFormTermsCheckbox;
81
+ SubmitButton: typeof PaymentFormSubmitButton;
82
+ Loading: typeof PaymentFormLoading;
83
+ Error: typeof PaymentFormError;
84
+ };
635
85
 
636
86
  /**
637
- * Credit top-up form with Stripe Elements.
87
+ * Default-tree shim over the `TopupForm` primitive.
638
88
  *
639
- * Unlike `PaymentForm`, this component does **not** call `processPayment`
640
- * after Stripe confirmation. Credits are recorded via the backend webhook
641
- * handler (`CreditService.recordCredit`), so `onSuccess` is called
642
- * immediately after `stripe.confirmCardPayment` succeeds.
89
+ * Drops in a golden-path credit top-up form: Stripe `PaymentElement` +
90
+ * submit button + loading/error states. Full control is available by
91
+ * composing the primitive at `@solvapay/react/primitives`.
643
92
  */
644
- declare const TopupForm: React$1.FC<TopupFormProps>;
93
+
94
+ declare const TopupForm: React.FC<TopupFormProps>;
645
95
 
646
96
  /**
647
- * Headless Product Badge Component
648
- *
649
- * Displays purchase status with complete styling control.
650
- * Supports render props, custom components, or className patterns.
651
- *
652
- * Hidden before initial data load or when no active purchase exists.
653
- * Remains visible during background refetches to avoid flickering.
654
- *
655
- * @example
656
- * ```tsx
657
- * // Render prop pattern
658
- * <ProductBadge>
659
- * {({ purchases, loading, displayPlan, shouldShow }) => (
660
- * shouldShow ? (
661
- * <div>{displayPlan}</div>
662
- * ) : null
663
- * )}
664
- * </ProductBadge>
665
- *
666
- * // ClassName pattern
667
- * <ProductBadge className="badge badge-primary" />
668
- * ```
97
+ * SDK-scoped SVG spinner. Self-contained: SVG `width`/`height` and inline
98
+ * stroke/fill opacity guarantee the visual regardless of consumer CSS.
99
+ * Rotation is driven by the `[data-solvapay-spinner]` rule in `styles.css`,
100
+ * so it only animates when consumers import `@solvapay/react/styles.css`
101
+ * (or target the attribute themselves).
102
+ */
103
+ declare const SIZE_PX: {
104
+ readonly sm: 16;
105
+ readonly md: 20;
106
+ readonly lg: 24;
107
+ };
108
+ declare const Spinner: React.FC<{
109
+ className?: string;
110
+ size?: keyof typeof SIZE_PX;
111
+ }>;
112
+
113
+ interface StripePaymentFormWrapperProps {
114
+ onSuccess?: (paymentIntent: unknown) => void | Promise<void>;
115
+ onError?: (error: Error) => void;
116
+ returnUrl?: string;
117
+ submitButtonText?: string;
118
+ buttonClassName?: string;
119
+ clientSecret: string;
120
+ }
121
+ /**
122
+ * Stripe Payment Form Wrapper Component
123
+ * Renders inside Stripe Elements context and handles the payment flow
124
+ * All hooks are called unconditionally to comply with React Rules of Hooks
669
125
  */
670
- declare const ProductBadge: React$1.FC<ProductBadgeProps>;
671
- /** @deprecated Use ProductBadge instead */
672
- declare const PlanBadge: React$1.FC<ProductBadgeProps>;
126
+ declare const StripePaymentFormWrapper: React.FC<StripePaymentFormWrapperProps>;
673
127
 
674
128
  /**
675
- * Headless Purchase Gate Component
129
+ * Opinionated one-line drop-in checkout built on top of `PlanSelector`,
130
+ * `PaymentForm`, and `ActivationFlow`. Renders the select → pay | activate
131
+ * machine internally, and auto-skips the select step when a product has a
132
+ * single selectable plan.
133
+ *
134
+ * Styling is done entirely via the `solvapay-*` class names documented in
135
+ * PR 5's `styles.css`. No inline styles. Full control (custom layouts,
136
+ * alternate CTAs, multi-step wizards) is available by composing the
137
+ * primitives at `@solvapay/react/primitives` directly.
138
+ */
139
+
140
+ type CheckoutLayoutSize = 'chat' | 'mobile' | 'desktop' | 'auto';
141
+ type CheckoutLayoutPlanSelectorOptions = {
142
+ filter?: (plan: Plan, index: number) => boolean;
143
+ sortBy?: (a: Plan, b: Plan) => number;
144
+ popularPlanRef?: string;
145
+ };
146
+ type CheckoutLayoutProps = {
147
+ /**
148
+ * Plan reference. When passed, skips plan selection and goes directly to
149
+ * payment or activation (routed based on plan type). Backwards compatible
150
+ * with today's payment-only behavior for non-usage-based plans.
151
+ */
152
+ planRef?: string;
153
+ productRef?: string;
154
+ prefillCustomer?: PrefillCustomer;
155
+ size?: CheckoutLayoutSize;
156
+ requireTermsAcceptance?: boolean;
157
+ /**
158
+ * Fires on paid completions only — preserved for backwards compatibility.
159
+ * For a unified callback across paid + activated flows, use `onResult`.
160
+ */
161
+ onSuccess?: (paymentIntent: PaymentIntent) => void;
162
+ /** Unified completion callback (paid + activated). */
163
+ onResult?: (result: CheckoutResult) => void;
164
+ /** Override the default free-plan activation step. Forwarded to PaymentForm. */
165
+ onFreePlan?: (plan: Plan) => Promise<unknown> | void;
166
+ onError?: (error: Error) => void;
167
+ /** Initial selection when rendering the selector step. */
168
+ initialPlanRef?: string;
169
+ /** Callback when the user picks a plan from the selector. */
170
+ onPlanSelect?: (planRef: string, plan: Plan) => void;
171
+ /** Controls for the internal `<PlanSelector>`. */
172
+ planSelector?: CheckoutLayoutPlanSelectorOptions;
173
+ /** Hide the "← Back to plans" affordance on the pay step. Defaults to true. */
174
+ showBackButton?: boolean;
175
+ submitButtonText?: string;
176
+ returnUrl?: string;
177
+ };
178
+ declare const CheckoutLayout: React.FC<CheckoutLayoutProps>;
179
+
180
+ /**
181
+ * Default-tree shim over the `PlanSelector` primitive.
676
182
  *
677
- * Controls access to content based on purchase status.
678
- * Uses render props to give developers full control over locked/unlocked states.
183
+ * Consumers who want a drop-in grid of plan cards use this component.
184
+ * Consumers who want full control compose `@solvapay/react/primitives`
185
+ * directly.
186
+ */
187
+
188
+ interface PlanSelectorProps {
189
+ productRef: string;
190
+ fetcher?: (productRef: string) => Promise<Plan[]>;
191
+ filter?: (plan: Plan, index: number) => boolean;
192
+ sortBy?: (a: Plan, b: Plan) => number;
193
+ autoSelectFirstPaid?: boolean;
194
+ initialPlanRef?: string;
195
+ currentPlanRef?: string | null;
196
+ popularPlanRef?: string;
197
+ onSelect?: (planRef: string, plan: Plan) => void;
198
+ className?: string;
199
+ children?: React.ReactNode;
200
+ }
201
+ declare const PlanSelector: React.FC<PlanSelectorProps>;
202
+
203
+ /**
204
+ * Default-tree shim over the `AmountPicker` primitive.
679
205
  *
680
- * @example
681
- * ```tsx
682
- * <PurchaseGate requireProduct="Pro Plan">
683
- * {({ hasAccess, loading }) => {
684
- * if (loading) return <Skeleton />;
685
- * if (!hasAccess) return <Paywall />;
686
- * return <PremiumContent />;
687
- * }}
688
- * </PurchaseGate>
689
- * ```
206
+ * Renders the golden-path pills + custom input combo consumers expect in
207
+ * drop-in usage. For full control (alternate layouts, custom confirm
208
+ * affordance, Tailwind variants), compose
209
+ * `@solvapay/react/primitives` directly.
690
210
  */
691
- declare const PurchaseGate: React$1.FC<PurchaseGateProps>;
211
+
212
+ interface AmountPickerProps {
213
+ currency: string;
214
+ minAmount?: number;
215
+ maxAmount?: number;
216
+ showCreditEstimate?: boolean;
217
+ onChange?: (amount: number | null) => void;
218
+ className?: string;
219
+ }
220
+ declare const AmountPicker: React.FC<AmountPickerProps>;
692
221
 
693
222
  /**
694
- * Headless Pricing Selector Component
223
+ * Default-tree shim over the `ActivationFlow` primitive.
695
224
  *
696
- * Provides pricing selection logic with complete styling control via render props.
697
- * Integrates plan fetching, purchase status, and selection state management.
225
+ * Renders the full usage-based activation state machine (summary
226
+ * activating selectAmount topupPayment retrying activated | error)
227
+ * with the golden-path copy, the embedded `<CheckoutSummary>` + `<TopupForm>`,
228
+ * and an optional back button. Full control is available by composing the
229
+ * primitive at `@solvapay/react/primitives`.
230
+ */
231
+
232
+ interface ActivationFlowProps {
233
+ productRef: string;
234
+ planRef?: string;
235
+ onSuccess?: (result: ActivationResult) => void;
236
+ onError?: (error: Error) => void;
237
+ onBack?: () => void;
238
+ retryDelayMs?: number;
239
+ retryBackoffMs?: number;
240
+ className?: string;
241
+ }
242
+ declare const ActivationFlow: React.FC<ActivationFlowProps>;
243
+
244
+ /**
245
+ * Default-tree shim over the `CancelledPlanNotice` primitive.
698
246
  *
699
- * Features:
700
- * - Fetches and manages pricing options
701
- * - Tracks selected option
702
- * - Provides helpers for checking if option is current/paid
703
- * - Integrates with purchase context
247
+ * Renders the full cancellation banner (heading, expiry, days remaining,
248
+ * access-until, cancellation date, reason, reactivate CTA) when the
249
+ * customer has a cancelled-but-still-active purchase. Renders `null`
250
+ * otherwise.
251
+ */
252
+
253
+ interface CancelledPlanNoticeProps {
254
+ onReactivated?: () => void;
255
+ onError?: (error: Error) => void;
256
+ className?: string;
257
+ }
258
+ declare const CancelledPlanNotice: React.FC<CancelledPlanNoticeProps>;
259
+
260
+ /**
261
+ * Default-tree shim over the `CreditGate` primitive.
704
262
  *
705
- * @example
706
- * ```tsx
707
- * <PricingSelector
708
- * productRef="prd_123"
709
- * fetcher={async (productRef) => {
710
- * const res = await fetch(`/api/list-plans?productRef=${productRef}`);
711
- * const data = await res.json();
712
- * return data.plans;
713
- * }}
714
- * sortBy={(a, b) => (a.price || 0) - (b.price || 0)}
715
- * autoSelectFirstPaid
716
- * >
717
- * {({ plans, selectedPlan, setSelectedPlanIndex, loading, isPaidPlan, isCurrentPlan }) => (
718
- * <div>
719
- * {loading ? (
720
- * <div>Loading...</div>
721
- * ) : (
722
- * plans.map((plan, index) => (
723
- * <button
724
- * key={plan.reference}
725
- * onClick={() => setSelectedPlanIndex(index)}
726
- * disabled={!isPaidPlan(plan.reference)}
727
- * >
728
- * ${plan.price}/{plan.interval}
729
- * {isCurrentPlan(plan.reference) && ' (Current)'}
730
- * </button>
731
- * ))
732
- * )}
733
- * </div>
734
- * )}
735
- * </PricingSelector>
736
- * ```
263
+ * Renders `children` when the customer has enough credits, otherwise renders
264
+ * a default top-up prompt (Heading + Subheading + Topup) or a user-provided
265
+ * `fallback`. Full control is available via
266
+ * `@solvapay/react/primitives` — compose `CreditGate.Root` with your own
267
+ * subcomponent arrangement.
737
268
  */
738
- declare const PricingSelector: React$1.FC<PricingSelectorProps>;
739
- /** @deprecated Use PricingSelector instead */
740
- declare const PlanSelector: React$1.FC<PricingSelectorProps>;
269
+
270
+ interface CreditGateProps {
271
+ minCredits?: number;
272
+ productRef?: string;
273
+ topupAmount?: number;
274
+ topupCurrency?: string;
275
+ fallback?: React.ReactNode;
276
+ className?: string;
277
+ children?: React.ReactNode;
278
+ }
279
+ declare const CreditGate: React.FC<CreditGateProps>;
741
280
 
742
281
  /**
743
- * SVG-based spinner component using CSS animations
744
- * Uses the same spinner design as the rest of the site
282
+ * `<CurrentPlanCard>` summary card for the customer's active purchase.
283
+ *
284
+ * Pure projection of existing provider state (`usePurchase`,
285
+ * `usePurchaseStatus`, `useBalance`, `usePaymentMethod`) plus Phase 1
286
+ * action components (`<CancelPlanButton>`, Phase 2's
287
+ * `<UpdatePaymentMethodButton>`). No Stripe Elements dependency, so the
288
+ * default tree renders identically inside an MCP host sandbox and a
289
+ * standalone HTTP app.
290
+ *
291
+ * Returns `null` when `usePurchase()` reports no active purchase, so
292
+ * integrators can drop it into account pages without wrapping in
293
+ * `{hasPaidPurchase && ...}`.
294
+ *
295
+ * Plan-type-aware lines:
296
+ * - `recurring` — "Next billing: {date}"
297
+ * - `one-time` — "Expires {date}" or "Valid indefinitely"
298
+ * - `usage-based` — `<BalanceBadge>` line; no date
745
299
  */
746
- declare const Spinner: React$1.FC<{
300
+
301
+ interface CurrentPlanCardClassNames {
302
+ root?: string;
303
+ heading?: string;
304
+ planName?: string;
305
+ productContext?: string;
306
+ price?: string;
307
+ dateLine?: string;
308
+ balanceLine?: string;
309
+ usageMeter?: string;
310
+ paymentMethod?: string;
311
+ actions?: string;
312
+ }
313
+ interface CurrentPlanCardProps {
314
+ /** Hide the payment-method line even when the endpoint returns a card. Default: `false`. */
315
+ hidePaymentMethod?: boolean;
316
+ /** Hide the "Cancel plan" action. Default: `false`. */
317
+ hideCancelButton?: boolean;
318
+ /** Hide the "Update card" action. Default: `false`. */
319
+ hideUpdatePaymentButton?: boolean;
320
+ /**
321
+ * Hide the `<UsageMeter>` that automatically renders for usage-based
322
+ * plans. Default: `false` (meter renders whenever the active plan has
323
+ * a quota).
324
+ */
325
+ hideUsageMeter?: boolean;
326
+ /** Per-element classNames. */
327
+ classNames?: CurrentPlanCardClassNames;
328
+ /**
329
+ * Custom className on the root. Appended after `solvapay-current-plan-card`
330
+ * so integrators can tweak without losing the SDK baseline.
331
+ */
747
332
  className?: string;
748
- size?: 'sm' | 'md' | 'lg';
749
- }>;
333
+ }
334
+ declare const CurrentPlanCard: React.FC<CurrentPlanCardProps>;
750
335
 
751
- interface StripePaymentFormWrapperProps {
752
- onSuccess?: (paymentIntent: unknown) => void | Promise<void>;
336
+ /**
337
+ * `<LaunchCustomerPortalButton>` opens the SolvaPay hosted customer
338
+ * portal in a new browser tab.
339
+ *
340
+ * Works identically in HTTP and MCP contexts because it routes through
341
+ * `transport.createCustomerSession()` (either HTTP `/api/create-customer-session`
342
+ * or MCP `create_customer_session` tool). The portal URL is pre-fetched on
343
+ * mount so the click handler can navigate via a real `<a target="_blank">`,
344
+ * which MCP host sandboxes permit (scripted `window.open` after an async
345
+ * round-trip is blocked — see `mcp-checkout-app` for prior art).
346
+ */
347
+
348
+ interface LaunchCustomerPortalButtonProps extends Omit<React.AnchorHTMLAttributes<HTMLAnchorElement>, 'href' | 'target' | 'rel' | 'onError'> {
349
+ /** Override the default "Manage billing" label. */
350
+ children?: React.ReactNode;
351
+ /** Called immediately before the user navigates to `href`. */
352
+ onLaunch?: (href: string) => void;
353
+ /** Called when the portal session fetch fails. */
753
354
  onError?: (error: Error) => void;
754
- returnUrl?: string;
755
- submitButtonText?: string;
756
- buttonClassName?: string;
757
- clientSecret: string;
355
+ /** Optional className applied to the disabled <button> shown while loading. */
356
+ loadingClassName?: string;
357
+ /** Optional className applied to the disabled <button> shown on error. */
358
+ errorClassName?: string;
359
+ /**
360
+ * Render the ready-state anchor via `Slot` so consumers can substitute
361
+ * their own element (typically a real `<button>`) while preserving the
362
+ * `href`, `target`, `rel`, and click chain. The loading/error fallback
363
+ * buttons are untouched — `asChild` only swaps the ready-state shell.
364
+ */
365
+ asChild?: boolean;
758
366
  }
367
+ declare const LaunchCustomerPortalButton: React.ForwardRefExoticComponent<LaunchCustomerPortalButtonProps & React.RefAttributes<HTMLAnchorElement>>;
368
+
759
369
  /**
760
- * Stripe Payment Form Wrapper Component
761
- * Renders inside Stripe Elements context and handles the payment flow
762
- * All hooks are called unconditionally to comply with React Rules of Hooks
370
+ * `<UpdatePaymentMethodButton>` trigger that opens the SolvaPay hosted
371
+ * customer portal so the customer can update their card on file.
372
+ *
373
+ * This MCP-first slice ships `mode="portal"` only (thin wrapper around
374
+ * `<LaunchCustomerPortalButton>`). A future Lovable-focused PR will add
375
+ * `mode="inline"` — a drawer containing `<PaymentMethodForm>` (Stripe
376
+ * Elements + SetupIntent). The `mode` prop is defined now so the API
377
+ * stays stable across both PRs.
763
378
  */
764
- declare const StripePaymentFormWrapper: React$1.FC<StripePaymentFormWrapperProps>;
765
379
 
766
- declare function BalanceBadge({ className, numberOnly, children }: BalanceBadgeProps): react_jsx_runtime.JSX.Element | null;
380
+ type UpdatePaymentMethodButtonMode = 'portal';
381
+ interface UpdatePaymentMethodButtonProps extends Omit<LaunchCustomerPortalButtonProps, 'children'> {
382
+ /**
383
+ * How card updates are collected. `"portal"` (default, only value shipped
384
+ * today) opens the SolvaPay hosted customer portal in a new tab. A
385
+ * future PR adds `"inline"` for Stripe Elements; keep the prop stable so
386
+ * callers don't need to migrate when that lands.
387
+ */
388
+ mode?: UpdatePaymentMethodButtonMode;
389
+ /** Override the default "Update card" label. */
390
+ children?: React.ReactNode;
391
+ }
392
+ declare const UpdatePaymentMethodButton: React.ForwardRefExoticComponent<UpdatePaymentMethodButtonProps & React.RefAttributes<HTMLAnchorElement>>;
393
+
394
+ type PaymentElementKind = 'payment-element' | 'card-element' | null;
395
+ interface PaymentFormContextValue {
396
+ planRef?: string;
397
+ productRef?: string;
398
+ prefillCustomer?: PrefillCustomer;
399
+ resolvedPlanRef: string | null;
400
+ plan: Plan | null;
401
+ clientSecret: string | null;
402
+ stripe: Stripe | null;
403
+ elements: StripeElements | null;
404
+ isProcessing: boolean;
405
+ isReady: boolean;
406
+ paymentInputComplete: boolean;
407
+ termsAccepted: boolean;
408
+ requireTermsAcceptance: boolean;
409
+ canSubmit: boolean;
410
+ error: string | null;
411
+ elementKind: PaymentElementKind;
412
+ returnUrl: string;
413
+ submitButtonText?: string;
414
+ buttonClassName?: string;
415
+ setElementKind: (k: PaymentElementKind) => void;
416
+ setPaymentInputComplete: (complete: boolean) => void;
417
+ setTermsAccepted: (accepted: boolean) => void;
418
+ submit: () => Promise<void>;
419
+ }
420
+ declare const PaymentFormContext: React.Context<PaymentFormContextValue | null>;
421
+ declare function usePaymentForm(): PaymentFormContextValue;
422
+ declare const PaymentFormProvider: React.FC<{
423
+ value: PaymentFormContextValue;
424
+ children: React.ReactNode;
425
+ }>;
767
426
 
768
427
  /**
769
428
  * Hook to get current purchase status and information.
@@ -886,6 +545,7 @@ interface UseCheckoutReturn {
886
545
  declare function useCheckout(options: {
887
546
  planRef?: string;
888
547
  productRef?: string;
548
+ customer?: PrefillCustomer;
889
549
  }): UseCheckoutReturn;
890
550
 
891
551
  /**
@@ -942,6 +602,44 @@ declare function useSolvaPay(): SolvaPayContextValue;
942
602
  */
943
603
  declare function usePlans(options: UsePlansOptions): UsePlansReturn;
944
604
 
605
+ /**
606
+ * Hook to load a single plan by reference.
607
+ *
608
+ * When `productRef` is known, piggybacks on the `usePlans` cache so there's
609
+ * no extra fetch. Otherwise fetches the full plan list and filters.
610
+ */
611
+ declare function usePlan(options: UsePlanOptions): UsePlanReturn;
612
+
613
+ /**
614
+ * Hook to load a single product by reference. Uses a module-level
615
+ * single-flight cache keyed by `productRef` (and transport identity) so
616
+ * concurrent consumers share the same in-flight request.
617
+ */
618
+ declare function useProduct(productRef: string | undefined): UseProductReturn;
619
+
620
+ /**
621
+ * Hook to load merchant identity (legal name, support email, terms/privacy
622
+ * URLs, ...) for rendering mandate copy and trust signals.
623
+ *
624
+ * Uses a module-level single-flight cache keyed by the configured transport
625
+ * (or HTTP route when no transport is provided) so concurrent consumers
626
+ * share one in-flight request and response.
627
+ */
628
+ declare function useMerchant(): UseMerchantReturn;
629
+
630
+ /**
631
+ * Read the merged copy bundle from context. Used by every SDK component that
632
+ * renders user-visible strings; consumers rarely call it directly but it's
633
+ * exported as an escape hatch for custom UIs.
634
+ */
635
+ declare function useCopy(): SolvaPayCopy;
636
+ /**
637
+ * Current locale string (e.g. `'en'`, `'sv-SE'`). Used to thread the locale
638
+ * through `Intl.NumberFormat`, `Intl.DateTimeFormat`, and Stripe Elements.
639
+ * `undefined` means "runtime default".
640
+ */
641
+ declare function useLocale(): string | undefined;
642
+
945
643
  /**
946
644
  * Hook providing advanced status and helper functions for purchase management
947
645
  *
@@ -1067,6 +765,83 @@ declare function useBalance(): BalanceStatus;
1067
765
  */
1068
766
  declare function useTopupAmountSelector(options: UseTopupAmountSelectorOptions): UseTopupAmountSelectorReturn;
1069
767
 
768
+ /**
769
+ * Hook that loads the customer's default payment method for rendering
770
+ * under `<CurrentPlanCard>`. Mirrors `useMerchant`'s caching semantics:
771
+ * module-level single-flight cache keyed by transport identity (or HTTP
772
+ * route when no custom transport is provided).
773
+ *
774
+ * The hook **does not throw** on transport errors — it sets `error` and
775
+ * keeps `paymentMethod: null` so the consuming component can hide the
776
+ * payment-method line. This lets `<CurrentPlanCard>` degrade gracefully
777
+ * when the backend endpoint isn't deployed yet or the MCP server doesn't
778
+ * expose the `get_payment_method` tool.
779
+ *
780
+ * @example
781
+ * ```tsx
782
+ * const { paymentMethod, loading } = usePaymentMethod()
783
+ *
784
+ * if (loading) return null
785
+ * if (!paymentMethod || paymentMethod.kind === 'none') return null
786
+ *
787
+ * return <span>{paymentMethod.brand} •••• {paymentMethod.last4}</span>
788
+ * ```
789
+ */
790
+ declare function usePaymentMethod(): UsePaymentMethodReturn;
791
+
792
+ /**
793
+ * Returns the effective data-access transport for the current provider.
794
+ *
795
+ * If the consumer passed a custom `transport` on `SolvaPayConfig`, that
796
+ * instance is returned. Otherwise a fresh HTTP transport is built from
797
+ * `config.api` + `config.fetch`. Memoised on the config identity so
798
+ * HTTP-default consumers don't re-create a transport on every render.
799
+ *
800
+ * Use this from components that need a transport method the provider
801
+ * doesn't already expose on its context (e.g. `createCustomerSession`,
802
+ * `getPaymentMethod`, `listPlans`).
803
+ */
804
+ declare function useTransport(): SolvaPayTransport;
805
+
806
+ interface UsePaywallResolverReturn {
807
+ /** `true` once the paywall requirement is met. */
808
+ resolved: boolean;
809
+ /** Refetch the underlying purchase + balance state. */
810
+ refetch: () => Promise<void>;
811
+ }
812
+ declare function usePaywallResolver(content: PaywallStructuredContent): UsePaywallResolverReturn;
813
+
814
+ type CopyContextValue = {
815
+ locale?: string;
816
+ copy: SolvaPayCopy;
817
+ };
818
+ declare const CopyContext: React.Context<CopyContextValue>;
819
+ type CopyProviderProps = {
820
+ locale?: string;
821
+ copy?: PartialSolvaPayCopy;
822
+ children: React.ReactNode;
823
+ };
824
+ declare const CopyProvider: React.FC<CopyProviderProps>;
825
+
826
+ /**
827
+ * Canonical English copy. Preserves the exact copy shipped before the i18n
828
+ * refactor so no visible behavior changes for English consumers.
829
+ */
830
+ declare const enCopy: SolvaPayCopy;
831
+
832
+ /**
833
+ * Tiny templating helper: replaces `{name}` tokens in `template` with values
834
+ * from `vars`. Missing keys are left as-is so integrators can spot typos.
835
+ */
836
+ declare function interpolate(template: string, vars: Record<string, string | number | undefined>): string;
837
+
838
+ /**
839
+ * Shallow-per-section merge over the default English bundle. Each top-level
840
+ * section of `SolvaPayCopy` is itself a flat record, so a two-level spread is
841
+ * sufficient — no recursive walk needed.
842
+ */
843
+ declare function mergeCopy(defaults: SolvaPayCopy, overrides?: PartialSolvaPayCopy): SolvaPayCopy;
844
+
1070
845
  /**
1071
846
  * Purchase utility functions
1072
847
  *
@@ -1123,5 +898,140 @@ declare function getPrimaryPurchase(purchases: PurchaseInfo[]): PurchaseInfo | n
1123
898
  * @returns true if purchase is paid (amount > 0)
1124
899
  */
1125
900
  declare function isPaidPurchase(purchase: PurchaseInfo): boolean;
901
+ /**
902
+ * Classify a purchase as a plan (subscription / one-time / usage-based) vs a
903
+ * balance transaction (credit top-up, and any future non-plan purposes).
904
+ *
905
+ * Primary signal is structural: a purchase with no `planSnapshot` was never a
906
+ * plan. The `metadata.purpose` check is defense in depth — it keeps obvious
907
+ * top-ups out of plan selectors even if a future backend regression
908
+ * accidentally attaches a snapshot to one.
909
+ *
910
+ * @param purchase - Purchase to classify
911
+ * @returns true when the purchase represents a plan
912
+ */
913
+ declare function isPlanPurchase(purchase: PurchaseInfo): boolean;
914
+ /**
915
+ * Inverse of `isPlanPurchase` — balance transactions (credit top-ups today,
916
+ * gift credits / refunds / bonuses tomorrow).
917
+ */
918
+ declare function isTopupPurchase(purchase: PurchaseInfo): boolean;
919
+
920
+ /**
921
+ * Currency + interval price formatting utilities.
922
+ *
923
+ * `formatPrice` renders a minor-unit amount with `Intl.NumberFormat`, handling
924
+ * locale, symbol placement, zero-decimal currencies (JPY, KRW, ...), and an
925
+ * optional trailing interval suffix ("/ month", "/ 3 months").
926
+ */
927
+ type FormatPriceOptions = {
928
+ /** BCP-47 locale tag. Falls back to the runtime default. */
929
+ locale?: string;
930
+ /** Recurring interval unit in English. Localize via the copy bundle if needed. */
931
+ interval?: string;
932
+ /** How many of `interval` per billing cycle. Defaults to 1. */
933
+ intervalCount?: number;
934
+ /**
935
+ * Copy used when `amountMinor` is 0. Defaults to `'Free'`.
936
+ * Pass `''` to disable the zero-check and always render the numeric zero.
937
+ */
938
+ free?: string;
939
+ };
940
+ /**
941
+ * Number of minor units per one major unit of `currency`. 1 for zero-decimal
942
+ * currencies (JPY, KRW, …), 100 for everything else. Use this to convert
943
+ * between the units a user types (major, e.g. dollars) and the units Stripe
944
+ * and the SolvaPay API consume (minor, e.g. cents).
945
+ */
946
+ declare function getMinorUnitsPerMajor(currency: string): number;
947
+ /**
948
+ * Convert a minor-unit amount to its major-unit equivalent. Zero-decimal
949
+ * currencies pass through unchanged (1000 JPY minor = 1000 JPY major);
950
+ * two-decimal currencies divide by 100 (1999 USD minor = 19.99 USD).
951
+ */
952
+ declare function toMajorUnits(amountMinor: number, currency: string): number;
953
+ declare function formatPrice(amountMinor: number, currency: string, opts?: FormatPriceOptions): string;
954
+
955
+ type ResolveCtaInput = {
956
+ variant: CheckoutVariant;
957
+ plan?: Plan | null;
958
+ product?: Product | null;
959
+ amountFormatted: string;
960
+ copy: SolvaPayCopy;
961
+ /** Caller-provided override; short-circuits derivation. */
962
+ override?: string;
963
+ };
964
+ /**
965
+ * Build the submit-button label. Keeps the mapping between plan type and CTA
966
+ * in one place so the button, the aria-label, and `<MandateText>` agree.
967
+ */
968
+ declare function resolveCta(input: ResolveCtaInput): string;
969
+
970
+ type ConfirmPaymentMode = 'payment-element' | 'card-element';
971
+ type ConfirmPaymentInput = {
972
+ stripe: Stripe;
973
+ elements: StripeElements;
974
+ clientSecret: string;
975
+ mode: ConfirmPaymentMode;
976
+ returnUrl: string;
977
+ /** Billing details from `useCustomer()` (echoed from backend). */
978
+ billingDetails?: {
979
+ email?: string;
980
+ name?: string;
981
+ };
982
+ /**
983
+ * When true, skip the browser redirect during PaymentElement confirmation
984
+ * and resolve only if the intent finishes synchronously. This is what
985
+ * SolvaPay's inline forms want so `onSuccess` fires in the same tab.
986
+ */
987
+ redirectIfRequired?: boolean;
988
+ /** Copy bundle for human-readable status messages. */
989
+ copy: SolvaPayCopy;
990
+ };
991
+ type ConfirmPaymentResult = {
992
+ status: 'succeeded';
993
+ paymentIntent: PaymentIntent;
994
+ } | {
995
+ status: 'requires_action';
996
+ message: string;
997
+ } | {
998
+ status: 'other';
999
+ message: string;
1000
+ paymentIntent?: PaymentIntent;
1001
+ } | {
1002
+ status: 'error';
1003
+ message: string;
1004
+ };
1005
+ /**
1006
+ * Wrap Stripe confirmation so callers don't have to branch between
1007
+ * `confirmPayment` (PaymentElement) and `confirmCardPayment` (CardElement).
1008
+ */
1009
+ declare function confirmPayment(input: ConfirmPaymentInput): Promise<ConfirmPaymentResult>;
1010
+
1011
+ /**
1012
+ * Default HTTP transport — wraps `config.api` + `config.fetch` so every SDK
1013
+ * hook and component routes through a single place. Extracted from the
1014
+ * inline `buildDefault*` callbacks that used to live inside
1015
+ * `SolvaPayProvider`.
1016
+ */
1017
+
1018
+ declare const DEFAULT_ROUTES: {
1019
+ readonly checkPurchase: "/api/check-purchase";
1020
+ readonly createPayment: "/api/create-payment-intent";
1021
+ readonly processPayment: "/api/process-payment";
1022
+ readonly createTopupPayment: "/api/create-topup-payment-intent";
1023
+ readonly customerBalance: "/api/customer-balance";
1024
+ readonly cancelRenewal: "/api/cancel-renewal";
1025
+ readonly reactivateRenewal: "/api/reactivate-renewal";
1026
+ readonly activatePlan: "/api/activate-plan";
1027
+ readonly createCheckoutSession: "/api/create-checkout-session";
1028
+ readonly createCustomerSession: "/api/create-customer-session";
1029
+ readonly getMerchant: "/api/merchant";
1030
+ readonly getProduct: "/api/get-product";
1031
+ readonly listPlans: "/api/list-plans";
1032
+ readonly getPaymentMethod: "/api/payment-method";
1033
+ readonly getUsage: "/api/usage";
1034
+ };
1035
+ declare function createHttpTransport(config: SolvaPayConfig | undefined): SolvaPayTransport;
1126
1036
 
1127
- export { type ActivationState, AuthAdapter, BalanceBadge, type BalanceBadgeProps, type BalanceStatus, type CancelResult, type CustomerInfo, type CustomerPurchaseData, type PaymentError, PaymentForm, type PaymentFormProps, type PaymentIntentResult, type Plan, PlanBadge, type PlanBadgeProps, PlanSelector, type PlanSelectorProps, PricingSelector, type PricingSelectorProps, ProductBadge, type ProductBadgeProps, type PurchaseActions, PurchaseGate, type PurchaseGateProps, type PurchaseInfo, type PurchaseStatus, type PurchaseStatusReturn, type PurchaseStatusValue, type ReactivateResult, type SolvaPayConfig, type SolvaPayContextValue, SolvaPayProvider, type SolvaPayProviderProps, Spinner, StripePaymentFormWrapper, TopupForm, type TopupFormProps, type TopupPaymentResult, type UseActivationReturn, type UsePlansOptions, type UsePlansReturn, type UseTopupAmountSelectorOptions, type UseTopupAmountSelectorReturn, type UseTopupOptions, type UseTopupReturn, filterPurchases, getActivePurchases, getCancelledPurchasesWithEndDate, getMostRecentPurchase, getPrimaryPurchase, isPaidPurchase, useActivation, useBalance, useCheckout, useCustomer, usePlans, usePurchase, usePurchaseActions, usePurchaseStatus, useSolvaPay, useTopup, useTopupAmountSelector };
1037
+ export { ActivatePlanResult, ActivationFlow, type ActivationFlowProps, ActivationResult, type ActivationState, AmountPicker, type AmountPickerProps, BalanceStatus, CancelResult, CancelledPlanNotice, type CancelledPlanNoticeProps, CheckoutLayout, type CheckoutLayoutPlanSelectorOptions, type CheckoutLayoutProps, type CheckoutLayoutSize, CheckoutResult, CheckoutVariant, type ConfirmPaymentInput, type ConfirmPaymentMode, type ConfirmPaymentResult, CopyContext, CopyProvider, CreditGate, type CreditGateProps, CurrentPlanCard, type CurrentPlanCardClassNames, type CurrentPlanCardProps, type CustomerInfo, DEFAULT_ROUTES, type FormatPriceOptions, LaunchCustomerPortalButton, type LaunchCustomerPortalButtonProps, PartialSolvaPayCopy, type PaymentElementKind, PaymentForm, PaymentFormContext, type PaymentFormContextValue, PaymentFormProps, PaymentFormProvider, Plan, PlanSelector, type PlanSelectorProps, PrefillCustomer, Product, type PurchaseActions, PurchaseInfo, PurchaseStatus, PurchaseStatusReturn, ReactivateResult, SolvaPayConfig, SolvaPayContextValue, SolvaPayCopy, SolvaPayProvider, SolvaPayProviderProps, SolvaPayTransport, Spinner, StripePaymentFormWrapper, TopupForm, TopupFormProps, UpdatePaymentMethodButton, type UpdatePaymentMethodButtonMode, type UpdatePaymentMethodButtonProps, type UseActivationReturn, UseMerchantReturn, UsePaymentMethodReturn, type UsePaywallResolverReturn, UsePlanOptions, UsePlanReturn, UsePlansOptions, UsePlansReturn, UseProductReturn, UseTopupAmountSelectorOptions, UseTopupAmountSelectorReturn, UseTopupOptions, UseTopupReturn, confirmPayment, createHttpTransport, enCopy, filterPurchases, formatPrice, getActivePurchases, getCancelledPurchasesWithEndDate, getMinorUnitsPerMajor, getMostRecentPurchase, getPrimaryPurchase, interpolate, isPaidPurchase, isPlanPurchase, isTopupPurchase, mergeCopy, resolveCta, toMajorUnits, useActivation, useBalance, useCheckout, useCopy, useCustomer, useLocale, useMerchant, usePaymentForm, usePaymentMethod, usePaywallResolver, usePlan, usePlans, useProduct, usePurchase, usePurchaseActions, usePurchaseStatus, useSolvaPay, useTopup, useTopupAmountSelector, useTransport };