@solvapay/react 1.0.0-preview.6 → 1.0.0-preview.7
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.cjs +507 -173
- package/dist/index.d.cts +333 -19
- package/dist/index.d.ts +333 -19
- package/dist/index.js +497 -174
- package/package.json +2 -1
package/dist/index.d.cts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import React$1 from 'react';
|
|
2
2
|
import { PaymentIntent, Stripe } from '@stripe/stripe-js';
|
|
3
|
+
import { ProcessPaymentResult } from '@solvapay/server';
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* TypeScript type definitions for @solvapay/react
|
|
@@ -11,6 +12,9 @@ interface SubscriptionInfo {
|
|
|
11
12
|
agentName: string;
|
|
12
13
|
status: string;
|
|
13
14
|
startDate: string;
|
|
15
|
+
endDate?: string;
|
|
16
|
+
cancelledAt?: string;
|
|
17
|
+
cancellationReason?: string;
|
|
14
18
|
}
|
|
15
19
|
interface CustomerSubscriptionData {
|
|
16
20
|
customerRef?: string;
|
|
@@ -40,6 +44,12 @@ interface SolvaPayContextValue {
|
|
|
40
44
|
planRef: string;
|
|
41
45
|
customerRef: string;
|
|
42
46
|
}) => Promise<PaymentIntentResult>;
|
|
47
|
+
processPayment?: (params: {
|
|
48
|
+
paymentIntentId: string;
|
|
49
|
+
agentRef: string;
|
|
50
|
+
customerRef: string;
|
|
51
|
+
planRef?: string;
|
|
52
|
+
}) => Promise<ProcessPaymentResult>;
|
|
43
53
|
customerRef?: string;
|
|
44
54
|
updateCustomerRef?: (newCustomerRef: string) => void;
|
|
45
55
|
}
|
|
@@ -49,6 +59,12 @@ interface SolvaPayProviderProps {
|
|
|
49
59
|
customerRef: string;
|
|
50
60
|
}) => Promise<PaymentIntentResult>;
|
|
51
61
|
checkSubscription: (customerRef: string) => Promise<CustomerSubscriptionData>;
|
|
62
|
+
processPayment?: (params: {
|
|
63
|
+
paymentIntentId: string;
|
|
64
|
+
agentRef: string;
|
|
65
|
+
customerRef: string;
|
|
66
|
+
planRef?: string;
|
|
67
|
+
}) => Promise<ProcessPaymentResult>;
|
|
52
68
|
customerRef?: string;
|
|
53
69
|
onCustomerRefUpdate?: (newCustomerRef: string) => void;
|
|
54
70
|
children: React.ReactNode;
|
|
@@ -57,6 +73,8 @@ interface PlanBadgeProps {
|
|
|
57
73
|
children?: (props: {
|
|
58
74
|
subscriptions: SubscriptionInfo[];
|
|
59
75
|
loading: boolean;
|
|
76
|
+
displayPlan: string | null;
|
|
77
|
+
shouldShow: boolean;
|
|
60
78
|
}) => React.ReactNode;
|
|
61
79
|
as?: React.ElementType;
|
|
62
80
|
className?: string | ((props: {
|
|
@@ -79,7 +97,129 @@ interface PaymentError extends Error {
|
|
|
79
97
|
type?: string;
|
|
80
98
|
}
|
|
81
99
|
/**
|
|
82
|
-
*
|
|
100
|
+
* Plan interface for subscription plans
|
|
101
|
+
*/
|
|
102
|
+
interface Plan {
|
|
103
|
+
reference: string;
|
|
104
|
+
name: string;
|
|
105
|
+
description?: string;
|
|
106
|
+
price?: number;
|
|
107
|
+
currency?: string;
|
|
108
|
+
interval?: string;
|
|
109
|
+
features?: string[];
|
|
110
|
+
isFreeTier?: boolean;
|
|
111
|
+
metadata?: Record<string, any>;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Options for usePlans hook
|
|
115
|
+
*/
|
|
116
|
+
interface UsePlansOptions {
|
|
117
|
+
/**
|
|
118
|
+
* Fetcher function to retrieve plans
|
|
119
|
+
*/
|
|
120
|
+
fetcher: (agentRef: string) => Promise<Plan[]>;
|
|
121
|
+
/**
|
|
122
|
+
* Agent reference to fetch plans for
|
|
123
|
+
*/
|
|
124
|
+
agentRef?: string;
|
|
125
|
+
/**
|
|
126
|
+
* Optional filter function to filter plans
|
|
127
|
+
*/
|
|
128
|
+
filter?: (plan: Plan) => boolean;
|
|
129
|
+
/**
|
|
130
|
+
* Optional sort function to sort plans
|
|
131
|
+
*/
|
|
132
|
+
sortBy?: (a: Plan, b: Plan) => number;
|
|
133
|
+
/**
|
|
134
|
+
* Auto-select first paid plan on load
|
|
135
|
+
*/
|
|
136
|
+
autoSelectFirstPaid?: boolean;
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Return type for usePlans hook
|
|
140
|
+
*/
|
|
141
|
+
interface UsePlansReturn {
|
|
142
|
+
plans: Plan[];
|
|
143
|
+
loading: boolean;
|
|
144
|
+
error: Error | null;
|
|
145
|
+
selectedPlanIndex: number;
|
|
146
|
+
selectedPlan: Plan | null;
|
|
147
|
+
setSelectedPlanIndex: (index: number) => void;
|
|
148
|
+
selectPlan: (planRef: string) => void;
|
|
149
|
+
refetch: () => Promise<void>;
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Props for headless PlanSelector component
|
|
153
|
+
*/
|
|
154
|
+
interface PlanSelectorProps {
|
|
155
|
+
/**
|
|
156
|
+
* Agent reference to fetch plans for
|
|
157
|
+
*/
|
|
158
|
+
agentRef?: string;
|
|
159
|
+
/**
|
|
160
|
+
* Fetcher function to retrieve plans
|
|
161
|
+
*/
|
|
162
|
+
fetcher: (agentRef: string) => Promise<Plan[]>;
|
|
163
|
+
/**
|
|
164
|
+
* Optional filter function
|
|
165
|
+
*/
|
|
166
|
+
filter?: (plan: Plan) => boolean;
|
|
167
|
+
/**
|
|
168
|
+
* Optional sort function
|
|
169
|
+
*/
|
|
170
|
+
sortBy?: (a: Plan, b: Plan) => number;
|
|
171
|
+
/**
|
|
172
|
+
* Auto-select first paid plan on load
|
|
173
|
+
*/
|
|
174
|
+
autoSelectFirstPaid?: boolean;
|
|
175
|
+
/**
|
|
176
|
+
* Render prop function
|
|
177
|
+
*/
|
|
178
|
+
children: (props: UsePlansReturn & {
|
|
179
|
+
subscriptions: SubscriptionInfo[];
|
|
180
|
+
isPaidPlan: (planName: string) => boolean;
|
|
181
|
+
isCurrentPlan: (planName: string) => boolean;
|
|
182
|
+
}) => React.ReactNode;
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Return type for useSubscriptionHelpers hook
|
|
186
|
+
*/
|
|
187
|
+
interface SubscriptionHelpersReturn {
|
|
188
|
+
/**
|
|
189
|
+
* Check if a plan name is a paid plan
|
|
190
|
+
*/
|
|
191
|
+
isPaidPlan: (planName: string) => boolean;
|
|
192
|
+
/**
|
|
193
|
+
* Get active paid subscription
|
|
194
|
+
*/
|
|
195
|
+
activePaidSubscription: SubscriptionInfo | null;
|
|
196
|
+
/**
|
|
197
|
+
* Get cancelled subscription
|
|
198
|
+
*/
|
|
199
|
+
cancelledSubscription: SubscriptionInfo | null;
|
|
200
|
+
/**
|
|
201
|
+
* Check if user has any paid subscription
|
|
202
|
+
*/
|
|
203
|
+
hasPaidSubscription: boolean;
|
|
204
|
+
/**
|
|
205
|
+
* Check if should show cancelled notice
|
|
206
|
+
*/
|
|
207
|
+
shouldShowCancelledNotice: boolean;
|
|
208
|
+
/**
|
|
209
|
+
* Get active plan name
|
|
210
|
+
*/
|
|
211
|
+
activePlanName: string | null;
|
|
212
|
+
/**
|
|
213
|
+
* Format a date string
|
|
214
|
+
*/
|
|
215
|
+
formatDate: (dateString?: string) => string | null;
|
|
216
|
+
/**
|
|
217
|
+
* Get days until a date
|
|
218
|
+
*/
|
|
219
|
+
getDaysUntilExpiration: (endDate?: string) => number | null;
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Payment form props - simplified and minimal
|
|
83
223
|
*/
|
|
84
224
|
interface PaymentFormProps {
|
|
85
225
|
/**
|
|
@@ -87,30 +227,34 @@ interface PaymentFormProps {
|
|
|
87
227
|
* including Stripe initialization and payment intent creation.
|
|
88
228
|
*/
|
|
89
229
|
planRef: string;
|
|
230
|
+
/**
|
|
231
|
+
* Agent reference. Required for processing payment after confirmation.
|
|
232
|
+
*/
|
|
233
|
+
agentRef?: string;
|
|
234
|
+
/**
|
|
235
|
+
* Callback when payment succeeds
|
|
236
|
+
*/
|
|
90
237
|
onSuccess?: (paymentIntent: PaymentIntent) => void;
|
|
238
|
+
/**
|
|
239
|
+
* Callback when payment fails
|
|
240
|
+
*/
|
|
91
241
|
onError?: (error: Error) => void;
|
|
92
242
|
/**
|
|
93
243
|
* Return URL after payment completion. Defaults to current page URL if not provided.
|
|
94
244
|
*/
|
|
95
245
|
returnUrl?: string;
|
|
246
|
+
/**
|
|
247
|
+
* Text for the submit button. Defaults to "Pay Now"
|
|
248
|
+
*/
|
|
96
249
|
submitButtonText?: string;
|
|
97
250
|
/**
|
|
98
|
-
* Optional className for the form container
|
|
251
|
+
* Optional className for the form container
|
|
99
252
|
*/
|
|
100
253
|
className?: string;
|
|
101
254
|
/**
|
|
102
255
|
* Optional className for the submit button
|
|
103
256
|
*/
|
|
104
257
|
buttonClassName?: string;
|
|
105
|
-
/**
|
|
106
|
-
* Text for the initial checkout button. If provided, shows a button first instead of
|
|
107
|
-
* auto-starting checkout.
|
|
108
|
-
*/
|
|
109
|
-
initialButtonText?: string;
|
|
110
|
-
/**
|
|
111
|
-
* Text for the cancel button. Set to empty string to hide cancel button.
|
|
112
|
-
*/
|
|
113
|
-
cancelButtonText?: string;
|
|
114
258
|
}
|
|
115
259
|
|
|
116
260
|
/**
|
|
@@ -144,9 +288,8 @@ declare const SolvaPayProvider: React$1.FC<SolvaPayProviderProps>;
|
|
|
144
288
|
/**
|
|
145
289
|
* SolvaPay Payment Form Component
|
|
146
290
|
*
|
|
147
|
-
*
|
|
148
|
-
*
|
|
149
|
-
* handle success/error callbacks.
|
|
291
|
+
* A simplified, minimal payment form that handles the checkout flow.
|
|
292
|
+
* Automatically initializes Stripe and displays the payment form.
|
|
150
293
|
*
|
|
151
294
|
* @example
|
|
152
295
|
* ```tsx
|
|
@@ -154,6 +297,7 @@ declare const SolvaPayProvider: React$1.FC<SolvaPayProviderProps>;
|
|
|
154
297
|
*
|
|
155
298
|
* <PaymentForm
|
|
156
299
|
* planRef="pro_plan"
|
|
300
|
+
* agentRef="agent_123"
|
|
157
301
|
* onSuccess={(paymentIntent) => {
|
|
158
302
|
* console.log('Payment successful!');
|
|
159
303
|
* }}
|
|
@@ -171,14 +315,18 @@ declare const PaymentForm: React$1.FC<PaymentFormProps>;
|
|
|
171
315
|
* Displays subscription status with complete styling control.
|
|
172
316
|
* Supports render props, custom components, or className patterns.
|
|
173
317
|
*
|
|
318
|
+
* Prevents flickering by hiding the badge during initial load and when no subscription exists.
|
|
319
|
+
* Only shows the badge once loading completes AND a subscription value is available.
|
|
320
|
+
* Badge only updates when the plan name actually changes (prevents unnecessary re-renders).
|
|
321
|
+
*
|
|
174
322
|
* @example
|
|
175
323
|
* ```tsx
|
|
176
324
|
* // Render prop pattern
|
|
177
325
|
* <PlanBadge>
|
|
178
|
-
* {({ subscriptions, loading }) => (
|
|
179
|
-
*
|
|
180
|
-
*
|
|
181
|
-
*
|
|
326
|
+
* {({ subscriptions, loading, displayPlan, shouldShow }) => (
|
|
327
|
+
* shouldShow ? (
|
|
328
|
+
* <div>{displayPlan}</div>
|
|
329
|
+
* ) : null
|
|
182
330
|
* )}
|
|
183
331
|
* </PlanBadge>
|
|
184
332
|
*
|
|
@@ -207,6 +355,75 @@ declare const PlanBadge: React$1.FC<PlanBadgeProps>;
|
|
|
207
355
|
*/
|
|
208
356
|
declare const SubscriptionGate: React$1.FC<SubscriptionGateProps>;
|
|
209
357
|
|
|
358
|
+
/**
|
|
359
|
+
* Headless Plan Selector Component
|
|
360
|
+
*
|
|
361
|
+
* Provides plan selection logic with complete styling control via render props.
|
|
362
|
+
* Integrates plan fetching, subscription status, and selection state management.
|
|
363
|
+
*
|
|
364
|
+
* Features:
|
|
365
|
+
* - Fetches and manages plans
|
|
366
|
+
* - Tracks selected plan
|
|
367
|
+
* - Provides helpers for checking if plan is current/paid
|
|
368
|
+
* - Integrates with subscription context
|
|
369
|
+
*
|
|
370
|
+
* @example
|
|
371
|
+
* ```tsx
|
|
372
|
+
* <PlanSelector
|
|
373
|
+
* agentRef="agent_123"
|
|
374
|
+
* fetcher={async (agentRef) => {
|
|
375
|
+
* const res = await fetch(`/api/list-plans?agentRef=${agentRef}`);
|
|
376
|
+
* const data = await res.json();
|
|
377
|
+
* return data.plans;
|
|
378
|
+
* }}
|
|
379
|
+
* sortBy={(a, b) => (a.price || 0) - (b.price || 0)}
|
|
380
|
+
* autoSelectFirstPaid
|
|
381
|
+
* >
|
|
382
|
+
* {({ plans, selectedPlan, setSelectedPlanIndex, loading, isPaidPlan, isCurrentPlan }) => (
|
|
383
|
+
* <div>
|
|
384
|
+
* {loading ? (
|
|
385
|
+
* <div>Loading plans...</div>
|
|
386
|
+
* ) : (
|
|
387
|
+
* plans.map((plan, index) => (
|
|
388
|
+
* <button
|
|
389
|
+
* key={plan.reference}
|
|
390
|
+
* onClick={() => setSelectedPlanIndex(index)}
|
|
391
|
+
* disabled={!isPaidPlan(plan.name)}
|
|
392
|
+
* >
|
|
393
|
+
* {plan.name} - ${plan.price}
|
|
394
|
+
* {isCurrentPlan(plan.name) && ' (Current)'}
|
|
395
|
+
* </button>
|
|
396
|
+
* ))
|
|
397
|
+
* )}
|
|
398
|
+
* </div>
|
|
399
|
+
* )}
|
|
400
|
+
* </PlanSelector>
|
|
401
|
+
* ```
|
|
402
|
+
*/
|
|
403
|
+
declare const PlanSelector: React$1.FC<PlanSelectorProps>;
|
|
404
|
+
|
|
405
|
+
/**
|
|
406
|
+
* Simple spinner component using CSS animations
|
|
407
|
+
*/
|
|
408
|
+
declare const Spinner: React$1.FC<{
|
|
409
|
+
className?: string;
|
|
410
|
+
size?: 'sm' | 'md' | 'lg';
|
|
411
|
+
}>;
|
|
412
|
+
|
|
413
|
+
interface StripePaymentFormWrapperProps {
|
|
414
|
+
onSuccess?: (paymentIntent: any) => void;
|
|
415
|
+
onError?: (error: Error) => void;
|
|
416
|
+
returnUrl?: string;
|
|
417
|
+
submitButtonText?: string;
|
|
418
|
+
buttonClassName?: string;
|
|
419
|
+
}
|
|
420
|
+
/**
|
|
421
|
+
* Stripe Payment Form Wrapper Component
|
|
422
|
+
* Renders inside Stripe Elements context and handles the payment flow
|
|
423
|
+
* All hooks are called unconditionally to comply with React Rules of Hooks
|
|
424
|
+
*/
|
|
425
|
+
declare const StripePaymentFormWrapper: React$1.FC<StripePaymentFormWrapperProps>;
|
|
426
|
+
|
|
210
427
|
/**
|
|
211
428
|
* Hook to access subscription status
|
|
212
429
|
* Returns the current subscription state and a refetch function
|
|
@@ -237,4 +454,101 @@ declare function useCheckout(planRef: string): UseCheckoutReturn;
|
|
|
237
454
|
*/
|
|
238
455
|
declare function useSolvaPay(): SolvaPayContextValue;
|
|
239
456
|
|
|
240
|
-
|
|
457
|
+
/**
|
|
458
|
+
* Hook to manage plan fetching and selection
|
|
459
|
+
*
|
|
460
|
+
* Provides a reusable way to fetch, filter, sort and select subscription plans.
|
|
461
|
+
* Handles loading and error states automatically.
|
|
462
|
+
*
|
|
463
|
+
* @example
|
|
464
|
+
* ```tsx
|
|
465
|
+
* const plans = usePlans({
|
|
466
|
+
* agentRef: 'agent_123',
|
|
467
|
+
* fetcher: async (agentRef) => {
|
|
468
|
+
* const res = await fetch(`/api/list-plans?agentRef=${agentRef}`);
|
|
469
|
+
* const data = await res.json();
|
|
470
|
+
* return data.plans;
|
|
471
|
+
* },
|
|
472
|
+
* sortBy: (a, b) => (a.price || 0) - (b.price || 0),
|
|
473
|
+
* autoSelectFirstPaid: true,
|
|
474
|
+
* });
|
|
475
|
+
*
|
|
476
|
+
* // Use in component
|
|
477
|
+
* if (plans.loading) return <div>Loading...</div>;
|
|
478
|
+
* if (plans.error) return <div>Error: {plans.error.message}</div>;
|
|
479
|
+
* ```
|
|
480
|
+
*/
|
|
481
|
+
declare function usePlans(options: UsePlansOptions): UsePlansReturn;
|
|
482
|
+
|
|
483
|
+
/**
|
|
484
|
+
* Hook providing helper functions for subscription management
|
|
485
|
+
*
|
|
486
|
+
* Provides utilities for checking subscription status, formatting dates,
|
|
487
|
+
* and working with paid vs free plans.
|
|
488
|
+
*
|
|
489
|
+
* @param plans - Array of available plans to determine if a subscription is paid
|
|
490
|
+
*
|
|
491
|
+
* @example
|
|
492
|
+
* ```tsx
|
|
493
|
+
* const helpers = useSubscriptionHelpers(plans);
|
|
494
|
+
*
|
|
495
|
+
* if (helpers.isPaidPlan('Pro Plan')) {
|
|
496
|
+
* // Handle paid plan
|
|
497
|
+
* }
|
|
498
|
+
*
|
|
499
|
+
* const daysLeft = helpers.getDaysUntilExpiration(subscription.endDate);
|
|
500
|
+
* ```
|
|
501
|
+
*/
|
|
502
|
+
declare function useSubscriptionHelpers(plans: Plan[]): SubscriptionHelpersReturn;
|
|
503
|
+
|
|
504
|
+
/**
|
|
505
|
+
* Subscription utility functions
|
|
506
|
+
*
|
|
507
|
+
* Provides shared logic for filtering and prioritizing subscriptions
|
|
508
|
+
*/
|
|
509
|
+
|
|
510
|
+
/**
|
|
511
|
+
* Filter subscriptions based on cancellation status and endDate
|
|
512
|
+
*
|
|
513
|
+
* Rules:
|
|
514
|
+
* - Keep all non-cancelled subscriptions
|
|
515
|
+
* - A subscription is considered cancelled if:
|
|
516
|
+
* 1. status === 'cancelled', OR
|
|
517
|
+
* 2. cancelledAt is set (even if status is still 'active' - this is expected behavior)
|
|
518
|
+
* - Keep cancelled subscriptions only if they have an endDate in the future
|
|
519
|
+
* (meaning the subscription is cancelled but still active until the endDate)
|
|
520
|
+
* - Filter out cancelled subscriptions without endDate or with past endDate
|
|
521
|
+
*/
|
|
522
|
+
declare function filterSubscriptions(subscriptions: SubscriptionInfo[]): SubscriptionInfo[];
|
|
523
|
+
/**
|
|
524
|
+
* Get active subscriptions (excluding cancelled ones)
|
|
525
|
+
* Also excludes subscriptions with cancelledAt set, even if status is still 'active'
|
|
526
|
+
*/
|
|
527
|
+
declare function getActiveSubscriptions(subscriptions: SubscriptionInfo[]): SubscriptionInfo[];
|
|
528
|
+
/**
|
|
529
|
+
* Get cancelled subscriptions with valid endDate (not expired)
|
|
530
|
+
* Includes subscriptions with cancelledAt set, even if status is still 'active'
|
|
531
|
+
*/
|
|
532
|
+
declare function getCancelledSubscriptionsWithEndDate(subscriptions: SubscriptionInfo[]): SubscriptionInfo[];
|
|
533
|
+
/**
|
|
534
|
+
* Get the most recent subscription by startDate
|
|
535
|
+
*/
|
|
536
|
+
declare function getMostRecentSubscription(subscriptions: SubscriptionInfo[]): SubscriptionInfo | null;
|
|
537
|
+
/**
|
|
538
|
+
* Get the primary subscription to display
|
|
539
|
+
*
|
|
540
|
+
* Prioritization:
|
|
541
|
+
* 1. Active subscriptions (most recent by startDate)
|
|
542
|
+
* 2. Cancelled subscriptions with endDate in the future (most recent by startDate)
|
|
543
|
+
* 3. null if no valid subscriptions
|
|
544
|
+
*/
|
|
545
|
+
declare function getPrimarySubscription(subscriptions: SubscriptionInfo[]): SubscriptionInfo | null;
|
|
546
|
+
/**
|
|
547
|
+
* Check if user has an active paid subscription
|
|
548
|
+
*
|
|
549
|
+
* @param subscriptions - Array of subscriptions
|
|
550
|
+
* @param isPaidPlan - Function to check if a plan name represents a paid plan
|
|
551
|
+
*/
|
|
552
|
+
declare function hasActivePaidSubscription(subscriptions: SubscriptionInfo[], isPaidPlan: (planName: string) => boolean): boolean;
|
|
553
|
+
|
|
554
|
+
export { type CustomerSubscriptionData, type PaymentError, PaymentForm, type PaymentFormProps, type PaymentIntentResult, type Plan, PlanBadge, type PlanBadgeProps, PlanSelector, type PlanSelectorProps, type SolvaPayContextValue, SolvaPayProvider, type SolvaPayProviderProps, Spinner, StripePaymentFormWrapper, SubscriptionGate, type SubscriptionGateProps, type SubscriptionHelpersReturn, type SubscriptionInfo, type SubscriptionStatus, type UsePlansOptions, type UsePlansReturn, filterSubscriptions, getActiveSubscriptions, getCancelledSubscriptionsWithEndDate, getMostRecentSubscription, getPrimarySubscription, hasActivePaidSubscription, useCheckout, usePlans, useSolvaPay, useSubscription, useSubscriptionHelpers };
|