recur-tw 0.0.2 → 0.0.3-beta.3
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 +88 -53
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +148 -18
- package/dist/index.d.ts +148 -18
- package/dist/index.js +88 -55
- package/dist/index.js.map +1 -1
- package/dist/recur.umd.js +209 -5
- package/dist/recur.umd.js.map +1 -1
- package/package.json +12 -11
package/dist/index.d.ts
CHANGED
|
@@ -23,7 +23,14 @@ interface RecurConfig {
|
|
|
23
23
|
organizationId?: string;
|
|
24
24
|
/**
|
|
25
25
|
* Base URL for API calls
|
|
26
|
-
* Defaults to
|
|
26
|
+
* Defaults to 'https://api.recur.tw'
|
|
27
|
+
*
|
|
28
|
+
* Only override this if:
|
|
29
|
+
* - You're using a local development API
|
|
30
|
+
* - You're using a custom domain for your API
|
|
31
|
+
*
|
|
32
|
+
* @example 'http://localhost:3000/api' (development)
|
|
33
|
+
* @example 'https://api.yourcompany.com' (custom domain)
|
|
27
34
|
*/
|
|
28
35
|
baseUrl?: string;
|
|
29
36
|
/**
|
|
@@ -48,21 +55,6 @@ interface RecurConfig {
|
|
|
48
55
|
* Where to redirect if user cancels payment
|
|
49
56
|
*/
|
|
50
57
|
cancelUrl?: string;
|
|
51
|
-
/**
|
|
52
|
-
* Enable mock mode for testing without backend API
|
|
53
|
-
* When enabled, all API calls return mock data
|
|
54
|
-
* Perfect for demos, examples, and local development
|
|
55
|
-
*
|
|
56
|
-
* @default false
|
|
57
|
-
* @example
|
|
58
|
-
* ```typescript
|
|
59
|
-
* const recur = RecurCheckout.init({
|
|
60
|
-
* publishableKey: 'mock',
|
|
61
|
-
* mockMode: true
|
|
62
|
-
* });
|
|
63
|
-
* ```
|
|
64
|
-
*/
|
|
65
|
-
mockMode?: boolean;
|
|
66
58
|
}
|
|
67
59
|
interface CheckoutOptions {
|
|
68
60
|
/**
|
|
@@ -109,7 +101,7 @@ interface CheckoutResult {
|
|
|
109
101
|
planName: string;
|
|
110
102
|
amount: number;
|
|
111
103
|
billingPeriod: string;
|
|
112
|
-
trialDays: number;
|
|
104
|
+
trialDays: number | null;
|
|
113
105
|
};
|
|
114
106
|
/**
|
|
115
107
|
* Subscriber information
|
|
@@ -267,4 +259,142 @@ declare function RecurProvider({ children, config: initialConfig }: RecurProvide
|
|
|
267
259
|
*/
|
|
268
260
|
declare function useRecur(): RecurContextValue;
|
|
269
261
|
|
|
270
|
-
|
|
262
|
+
interface UsePlansOptions {
|
|
263
|
+
/**
|
|
264
|
+
* Whether to fetch plans immediately on mount
|
|
265
|
+
* @default true
|
|
266
|
+
*/
|
|
267
|
+
enabled?: boolean;
|
|
268
|
+
/**
|
|
269
|
+
* Callback when plans are fetched successfully
|
|
270
|
+
*/
|
|
271
|
+
onSuccess?: (plans: Plan[]) => void;
|
|
272
|
+
/**
|
|
273
|
+
* Callback when an error occurs
|
|
274
|
+
*/
|
|
275
|
+
onError?: (error: Error) => void;
|
|
276
|
+
}
|
|
277
|
+
interface UsePlansResult {
|
|
278
|
+
/**
|
|
279
|
+
* The fetched plans data
|
|
280
|
+
*/
|
|
281
|
+
data: Plan[] | undefined;
|
|
282
|
+
/**
|
|
283
|
+
* Whether the plans are currently being fetched
|
|
284
|
+
*/
|
|
285
|
+
isLoading: boolean;
|
|
286
|
+
/**
|
|
287
|
+
* Error object if fetch failed
|
|
288
|
+
*/
|
|
289
|
+
error: Error | null;
|
|
290
|
+
/**
|
|
291
|
+
* Manually trigger a refetch
|
|
292
|
+
*/
|
|
293
|
+
refetch: () => Promise<void>;
|
|
294
|
+
}
|
|
295
|
+
/**
|
|
296
|
+
* usePlans Hook
|
|
297
|
+
*
|
|
298
|
+
* Fetch available subscription plans with automatic state management
|
|
299
|
+
*
|
|
300
|
+
* @example
|
|
301
|
+
* ```tsx
|
|
302
|
+
* function PlansPage() {
|
|
303
|
+
* const { data: plans, isLoading, error } = usePlans();
|
|
304
|
+
*
|
|
305
|
+
* if (isLoading) return <PlansSkeletonLoader />;
|
|
306
|
+
* if (error) return <ErrorMessage error={error} />;
|
|
307
|
+
*
|
|
308
|
+
* return (
|
|
309
|
+
* <div>
|
|
310
|
+
* {plans?.map(plan => (
|
|
311
|
+
* <PlanCard key={plan.id} plan={plan} />
|
|
312
|
+
* ))}
|
|
313
|
+
* </div>
|
|
314
|
+
* );
|
|
315
|
+
* }
|
|
316
|
+
* ```
|
|
317
|
+
*/
|
|
318
|
+
declare function usePlans(options?: UsePlansOptions): UsePlansResult;
|
|
319
|
+
|
|
320
|
+
interface UseSubscribeOptions {
|
|
321
|
+
/**
|
|
322
|
+
* Callback when subscription is created successfully
|
|
323
|
+
*/
|
|
324
|
+
onSuccess?: (result: CheckoutResult) => void;
|
|
325
|
+
/**
|
|
326
|
+
* Callback when payment is completed
|
|
327
|
+
*/
|
|
328
|
+
onPaymentComplete?: (subscription: any) => void;
|
|
329
|
+
/**
|
|
330
|
+
* Callback when an error occurs
|
|
331
|
+
*/
|
|
332
|
+
onError?: (error: CheckoutError) => void;
|
|
333
|
+
/**
|
|
334
|
+
* Callback when payment is cancelled
|
|
335
|
+
*/
|
|
336
|
+
onPaymentCancel?: () => void;
|
|
337
|
+
}
|
|
338
|
+
interface UseSubscribeResult {
|
|
339
|
+
/**
|
|
340
|
+
* Function to initiate subscription checkout
|
|
341
|
+
*/
|
|
342
|
+
mutate: (options: Omit<CheckoutOptions, 'onSuccess' | 'onError' | 'onPaymentComplete' | 'onPaymentCancel'>) => Promise<void>;
|
|
343
|
+
/**
|
|
344
|
+
* Alias for mutate (for better DX)
|
|
345
|
+
*/
|
|
346
|
+
subscribe: (options: Omit<CheckoutOptions, 'onSuccess' | 'onError' | 'onPaymentComplete' | 'onPaymentCancel'>) => Promise<void>;
|
|
347
|
+
/**
|
|
348
|
+
* Whether subscription is in progress
|
|
349
|
+
*/
|
|
350
|
+
isLoading: boolean;
|
|
351
|
+
/**
|
|
352
|
+
* Error object if subscription failed
|
|
353
|
+
*/
|
|
354
|
+
error: CheckoutError | null;
|
|
355
|
+
/**
|
|
356
|
+
* Reset error state
|
|
357
|
+
*/
|
|
358
|
+
reset: () => void;
|
|
359
|
+
}
|
|
360
|
+
/**
|
|
361
|
+
* useSubscribe Hook
|
|
362
|
+
*
|
|
363
|
+
* Create a subscription with automatic state management
|
|
364
|
+
*
|
|
365
|
+
* @example
|
|
366
|
+
* ```tsx
|
|
367
|
+
* function SubscribePage() {
|
|
368
|
+
* const { data: plans } = usePlans();
|
|
369
|
+
* const { subscribe, isLoading, error } = useSubscribe({
|
|
370
|
+
* onSuccess: () => {
|
|
371
|
+
* console.log('Subscription created!');
|
|
372
|
+
* },
|
|
373
|
+
* onPaymentComplete: () => {
|
|
374
|
+
* router.push('/account');
|
|
375
|
+
* }
|
|
376
|
+
* });
|
|
377
|
+
*
|
|
378
|
+
* return (
|
|
379
|
+
* <div>
|
|
380
|
+
* {plans?.map(plan => (
|
|
381
|
+
* <PlanCard
|
|
382
|
+
* key={plan.id}
|
|
383
|
+
* plan={plan}
|
|
384
|
+
* onSubscribe={() => subscribe({
|
|
385
|
+
* planId: plan.id,
|
|
386
|
+
* customerEmail: 'user@example.com',
|
|
387
|
+
* customerName: 'John Doe'
|
|
388
|
+
* })}
|
|
389
|
+
* isLoading={isLoading}
|
|
390
|
+
* />
|
|
391
|
+
* ))}
|
|
392
|
+
* {error && <ErrorMessage error={error} />}
|
|
393
|
+
* </div>
|
|
394
|
+
* );
|
|
395
|
+
* }
|
|
396
|
+
* ```
|
|
397
|
+
*/
|
|
398
|
+
declare function useSubscribe(options?: UseSubscribeOptions): UseSubscribeResult;
|
|
399
|
+
|
|
400
|
+
export { type CheckoutError, type CheckoutOptions, type CheckoutResult, type Plan, type PlansResult, type RecurConfig, type RecurContextValue, RecurProvider, type RecurProviderProps, type SubscriptionResult, type UsePlansOptions, type UsePlansResult, type UseSubscribeOptions, type UseSubscribeResult, usePlans, useRecur, useSubscribe };
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { createContext, useState, useCallback, useMemo, useContext } from 'react';
|
|
1
|
+
import React, { createContext, useState, useCallback, useMemo, useContext, useEffect } from 'react';
|
|
2
2
|
import { jsx } from 'react/jsx-runtime';
|
|
3
3
|
|
|
4
4
|
// src/context.tsx
|
|
@@ -9,52 +9,7 @@ var PAYUNI_SANDBOX_SDK_URL = "https://sandbox-vendor.payuni.com.tw/sdk/uni-payme
|
|
|
9
9
|
var isLoaded = false;
|
|
10
10
|
var isLoading = false;
|
|
11
11
|
var loadPromise = null;
|
|
12
|
-
function
|
|
13
|
-
return {
|
|
14
|
-
createSession: (token, options) => {
|
|
15
|
-
console.log("[Mock PAYUNi SDK] Creating session with token:", token.substring(0, 20) + "...");
|
|
16
|
-
console.log("[Mock PAYUNi SDK] Options:", options);
|
|
17
|
-
const { elements } = options;
|
|
18
|
-
const cardNoEl = document.getElementById(elements.CardNo);
|
|
19
|
-
const cardExpEl = document.getElementById(elements.CardExp);
|
|
20
|
-
const cardCvcEl = document.getElementById(elements.CardCvc);
|
|
21
|
-
if (cardNoEl) {
|
|
22
|
-
cardNoEl.innerHTML = '<div style="border: 1px solid #e5e7eb; padding: 8px; border-radius: 4px; background: white; font-size: 14px; color: #6b7280;">4242 4242 4242 4242 (Mock)</div>';
|
|
23
|
-
}
|
|
24
|
-
if (cardExpEl) {
|
|
25
|
-
cardExpEl.innerHTML = '<div style="border: 1px solid #e5e7eb; padding: 8px; border-radius: 4px; background: white; font-size: 14px; color: #6b7280;">12 / 25 (Mock)</div>';
|
|
26
|
-
}
|
|
27
|
-
if (cardCvcEl) {
|
|
28
|
-
cardCvcEl.innerHTML = '<div style="border: 1px solid #e5e7eb; padding: 8px; border-radius: 4px; background: white; font-size: 14px; color: #6b7280;">123 (Mock)</div>';
|
|
29
|
-
}
|
|
30
|
-
return {
|
|
31
|
-
start: async () => {
|
|
32
|
-
console.log("[Mock PAYUNi SDK] \u2713 Started successfully");
|
|
33
|
-
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
34
|
-
},
|
|
35
|
-
getTradeResult: async () => {
|
|
36
|
-
console.log("[Mock PAYUNi SDK] \u2713 Getting trade result");
|
|
37
|
-
await new Promise((resolve) => setTimeout(resolve, 800));
|
|
38
|
-
return {
|
|
39
|
-
Status: "SUCCESS",
|
|
40
|
-
Message: "Mock payment successful",
|
|
41
|
-
TradeNo: `MOCK${Date.now()}`,
|
|
42
|
-
AuthCode: "123456"
|
|
43
|
-
};
|
|
44
|
-
}
|
|
45
|
-
};
|
|
46
|
-
}
|
|
47
|
-
};
|
|
48
|
-
}
|
|
49
|
-
async function loadPayUniSDK(isSandbox = false, mockMode = false) {
|
|
50
|
-
if (mockMode) {
|
|
51
|
-
console.log("[Recur SDK] \u{1F3AD} Mock mode enabled - using mock PAYUNi SDK");
|
|
52
|
-
if (typeof window !== "undefined") {
|
|
53
|
-
window.UniPayment = createMockUniPayment();
|
|
54
|
-
}
|
|
55
|
-
isLoaded = true;
|
|
56
|
-
return Promise.resolve();
|
|
57
|
-
}
|
|
12
|
+
async function loadPayUniSDK(isSandbox = false) {
|
|
58
13
|
if (isLoaded && window.UniPayment) {
|
|
59
14
|
return Promise.resolve();
|
|
60
15
|
}
|
|
@@ -118,7 +73,7 @@ function RecurProvider({ children, config: initialConfig = {} }) {
|
|
|
118
73
|
if (!options.customerEmail || !options.customerName) {
|
|
119
74
|
throw new Error("customerEmail and customerName are required");
|
|
120
75
|
}
|
|
121
|
-
const baseUrl = config.baseUrl ||
|
|
76
|
+
const baseUrl = config.baseUrl || "https://api.recur.tw";
|
|
122
77
|
console.log("[Recur SDK] Base URL:", baseUrl);
|
|
123
78
|
const headers = {
|
|
124
79
|
"Content-Type": "application/json",
|
|
@@ -272,7 +227,7 @@ function RecurProvider({ children, config: initialConfig = {} }) {
|
|
|
272
227
|
}
|
|
273
228
|
}
|
|
274
229
|
console.log("[Recur SDK] Step 1: Creating subscription...");
|
|
275
|
-
const subscriptionResponse = await fetch(`${baseUrl}/
|
|
230
|
+
const subscriptionResponse = await fetch(`${baseUrl}/v1/subscriptions`, {
|
|
276
231
|
method: "POST",
|
|
277
232
|
headers,
|
|
278
233
|
body: JSON.stringify({
|
|
@@ -292,7 +247,7 @@ function RecurProvider({ children, config: initialConfig = {} }) {
|
|
|
292
247
|
console.log("[Recur SDK] Subscription created successfully:", subscriptionResult);
|
|
293
248
|
options.onSuccess?.(subscriptionResult);
|
|
294
249
|
console.log("[Recur SDK] Step 2: Getting SDK token...");
|
|
295
|
-
const tokenResponse = await fetch(`${baseUrl}/
|
|
250
|
+
const tokenResponse = await fetch(`${baseUrl}/v1/subscriptions/sdk-token`, {
|
|
296
251
|
method: "POST",
|
|
297
252
|
headers,
|
|
298
253
|
body: JSON.stringify({
|
|
@@ -313,7 +268,7 @@ function RecurProvider({ children, config: initialConfig = {} }) {
|
|
|
313
268
|
});
|
|
314
269
|
console.log("[Recur SDK] Step 3: Loading PAYUNi SDK...");
|
|
315
270
|
const isSandbox = tokenResult.environment === "SANDBOX";
|
|
316
|
-
await loadPayUniSDK(isSandbox
|
|
271
|
+
await loadPayUniSDK(isSandbox);
|
|
317
272
|
console.log("[Recur SDK] PAYUNi SDK loaded successfully");
|
|
318
273
|
if (!window.UniPayment) {
|
|
319
274
|
console.error("[Recur SDK] window.UniPayment is not available after loading");
|
|
@@ -537,7 +492,7 @@ function RecurProvider({ children, config: initialConfig = {} }) {
|
|
|
537
492
|
console.log("[Recur SDK] Trade result received:", tradeResult);
|
|
538
493
|
console.log("[Recur SDK] Step 8: Completing subscription...");
|
|
539
494
|
const completeResponse = await fetch(
|
|
540
|
-
`${baseUrl}/
|
|
495
|
+
`${baseUrl}/v1/subscriptions/${subscriptionResult.subscription.id}/complete`,
|
|
541
496
|
{
|
|
542
497
|
method: "POST",
|
|
543
498
|
headers,
|
|
@@ -606,8 +561,8 @@ function RecurProvider({ children, config: initialConfig = {} }) {
|
|
|
606
561
|
if (!config.publishableKey) {
|
|
607
562
|
throw new Error("publishableKey is required");
|
|
608
563
|
}
|
|
609
|
-
const baseUrl = config.baseUrl ||
|
|
610
|
-
const response = await fetch(`${baseUrl}/
|
|
564
|
+
const baseUrl = config.baseUrl || "https://api.recur.tw";
|
|
565
|
+
const response = await fetch(`${baseUrl}/v1/plans`, {
|
|
611
566
|
method: "GET",
|
|
612
567
|
headers: {
|
|
613
568
|
"Content-Type": "application/json",
|
|
@@ -641,7 +596,85 @@ function useRecur() {
|
|
|
641
596
|
}
|
|
642
597
|
return context;
|
|
643
598
|
}
|
|
599
|
+
function usePlans(options = {}) {
|
|
600
|
+
const { enabled = true, onSuccess, onError } = options;
|
|
601
|
+
const { fetchPlans } = useRecur();
|
|
602
|
+
const [data, setData] = useState(void 0);
|
|
603
|
+
const [isLoading2, setIsLoading] = useState(false);
|
|
604
|
+
const [error, setError] = useState(null);
|
|
605
|
+
const fetchData = useCallback(async () => {
|
|
606
|
+
if (!enabled) return;
|
|
607
|
+
setIsLoading(true);
|
|
608
|
+
setError(null);
|
|
609
|
+
try {
|
|
610
|
+
const result = await fetchPlans();
|
|
611
|
+
setData(result.plans);
|
|
612
|
+
onSuccess?.(result.plans);
|
|
613
|
+
} catch (err) {
|
|
614
|
+
const error2 = err instanceof Error ? err : new Error("Failed to fetch plans");
|
|
615
|
+
setError(error2);
|
|
616
|
+
onError?.(error2);
|
|
617
|
+
} finally {
|
|
618
|
+
setIsLoading(false);
|
|
619
|
+
}
|
|
620
|
+
}, [enabled, fetchPlans, onSuccess, onError]);
|
|
621
|
+
useEffect(() => {
|
|
622
|
+
fetchData();
|
|
623
|
+
}, [fetchData]);
|
|
624
|
+
return {
|
|
625
|
+
data,
|
|
626
|
+
isLoading: isLoading2,
|
|
627
|
+
error,
|
|
628
|
+
refetch: fetchData
|
|
629
|
+
};
|
|
630
|
+
}
|
|
631
|
+
function useSubscribe(options = {}) {
|
|
632
|
+
const { onSuccess, onError, onPaymentComplete, onPaymentCancel } = options;
|
|
633
|
+
const { checkout, isCheckingOut } = useRecur();
|
|
634
|
+
const [error, setError] = useState(null);
|
|
635
|
+
const mutate = useCallback(
|
|
636
|
+
async (checkoutOptions) => {
|
|
637
|
+
setError(null);
|
|
638
|
+
try {
|
|
639
|
+
await checkout({
|
|
640
|
+
...checkoutOptions,
|
|
641
|
+
onSuccess: (result) => {
|
|
642
|
+
onSuccess?.(result);
|
|
643
|
+
},
|
|
644
|
+
onPaymentComplete: (subscription) => {
|
|
645
|
+
onPaymentComplete?.(subscription);
|
|
646
|
+
},
|
|
647
|
+
onError: (err) => {
|
|
648
|
+
setError(err);
|
|
649
|
+
onError?.(err);
|
|
650
|
+
},
|
|
651
|
+
onPaymentCancel: () => {
|
|
652
|
+
onPaymentCancel?.();
|
|
653
|
+
}
|
|
654
|
+
});
|
|
655
|
+
} catch (err) {
|
|
656
|
+
const checkoutError = {
|
|
657
|
+
code: "CHECKOUT_FAILED",
|
|
658
|
+
message: err instanceof Error ? err.message : "Unknown error occurred"
|
|
659
|
+
};
|
|
660
|
+
setError(checkoutError);
|
|
661
|
+
onError?.(checkoutError);
|
|
662
|
+
}
|
|
663
|
+
},
|
|
664
|
+
[checkout, onSuccess, onError, onPaymentComplete, onPaymentCancel]
|
|
665
|
+
);
|
|
666
|
+
const reset = useCallback(() => {
|
|
667
|
+
setError(null);
|
|
668
|
+
}, []);
|
|
669
|
+
return {
|
|
670
|
+
mutate,
|
|
671
|
+
subscribe: mutate,
|
|
672
|
+
isLoading: isCheckingOut,
|
|
673
|
+
error,
|
|
674
|
+
reset
|
|
675
|
+
};
|
|
676
|
+
}
|
|
644
677
|
|
|
645
|
-
export { RecurProvider, useRecur };
|
|
678
|
+
export { RecurProvider, usePlans, useRecur, useSubscribe };
|
|
646
679
|
//# sourceMappingURL=index.js.map
|
|
647
680
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/payuni-loader.ts","../src/context.tsx","../src/use-recur.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * PAYUNi SDK Loader\n * Dynamically loads and initializes the PAYUNi UNi Embed SDK\n */\n\nimport type { UniPaymentInstance } from './types.js';\n\nconst PAYUNI_SDK_URL = 'https://vendor.payuni.com.tw/sdk/uni-payment.js';\nconst PAYUNI_SANDBOX_SDK_URL = 'https://sandbox-vendor.payuni.com.tw/sdk/uni-payment.js';\n\nlet isLoaded = false;\nlet isLoading = false;\nlet loadPromise: Promise<void> | null = null;\n\n/**\n * Create a mock PAYUNi SDK for testing without backend\n */\nfunction createMockUniPayment() {\n return {\n createSession: (token: string, options: any): UniPaymentInstance => {\n console.log('[Mock PAYUNi SDK] Creating session with token:', token.substring(0, 20) + '...');\n console.log('[Mock PAYUNi SDK] Options:', options);\n\n // Simulate rendering credit card iframes\n const { elements } = options;\n const cardNoEl = document.getElementById(elements.CardNo);\n const cardExpEl = document.getElementById(elements.CardExp);\n const cardCvcEl = document.getElementById(elements.CardCvc);\n\n // Inject mock iframe placeholders\n if (cardNoEl) {\n cardNoEl.innerHTML = '<div style=\"border: 1px solid #e5e7eb; padding: 8px; border-radius: 4px; background: white; font-size: 14px; color: #6b7280;\">4242 4242 4242 4242 (Mock)</div>';\n }\n if (cardExpEl) {\n cardExpEl.innerHTML = '<div style=\"border: 1px solid #e5e7eb; padding: 8px; border-radius: 4px; background: white; font-size: 14px; color: #6b7280;\">12 / 25 (Mock)</div>';\n }\n if (cardCvcEl) {\n cardCvcEl.innerHTML = '<div style=\"border: 1px solid #e5e7eb; padding: 8px; border-radius: 4px; background: white; font-size: 14px; color: #6b7280;\">123 (Mock)</div>';\n }\n\n return {\n start: async () => {\n console.log('[Mock PAYUNi SDK] ✓ Started successfully');\n // Simulate async loading\n await new Promise(resolve => setTimeout(resolve, 500));\n },\n getTradeResult: async () => {\n console.log('[Mock PAYUNi SDK] ✓ Getting trade result');\n // Simulate payment processing\n await new Promise(resolve => setTimeout(resolve, 800));\n return {\n Status: 'SUCCESS',\n Message: 'Mock payment successful',\n TradeNo: `MOCK${Date.now()}`,\n AuthCode: '123456',\n };\n },\n };\n },\n };\n}\n\n/**\n * Load PAYUNi SDK script\n * @param isSandbox - Use sandbox environment\n * @param mockMode - Use mock mode for testing\n */\nexport async function loadPayUniSDK(isSandbox: boolean = false, mockMode: boolean = false): Promise<void> {\n // Mock mode: inject mock SDK immediately\n if (mockMode) {\n console.log('[Recur SDK] 🎭 Mock mode enabled - using mock PAYUNi SDK');\n if (typeof window !== 'undefined') {\n (window as any).UniPayment = createMockUniPayment();\n }\n isLoaded = true;\n return Promise.resolve();\n }\n // Already loaded\n if (isLoaded && window.UniPayment) {\n return Promise.resolve();\n }\n\n // Currently loading\n if (isLoading && loadPromise) {\n return loadPromise;\n }\n\n // Start loading\n isLoading = true;\n loadPromise = new Promise((resolve, reject) => {\n const script = document.createElement('script');\n script.src = isSandbox ? PAYUNI_SANDBOX_SDK_URL : PAYUNI_SDK_URL;\n script.async = true;\n\n script.onload = () => {\n isLoaded = true;\n isLoading = false;\n resolve();\n };\n\n script.onerror = () => {\n isLoading = false;\n loadPromise = null;\n reject(new Error('Failed to load PAYUNi SDK'));\n };\n\n document.head.appendChild(script);\n });\n\n return loadPromise;\n}\n\n/**\n * Check if PAYUNi SDK is loaded\n */\nexport function isPayUniSDKLoaded(): boolean {\n return isLoaded && typeof window !== 'undefined' && !!window.UniPayment;\n}\n\n/**\n * Wait for PAYUNi SDK to be ready\n */\nexport async function waitForPayUniSDK(timeout: number = 10000): Promise<void> {\n const startTime = Date.now();\n\n while (!isPayUniSDKLoaded()) {\n if (Date.now() - startTime > timeout) {\n throw new Error('PAYUNi SDK load timeout');\n }\n await new Promise(resolve => setTimeout(resolve, 100));\n }\n}\n","'use client';\n\nimport React, { createContext, useCallback, useState, useMemo } from 'react';\nimport type {\n RecurConfig,\n RecurContextValue,\n CheckoutOptions,\n CheckoutResult,\n CheckoutError,\n PlansResult,\n SdkTokenResult,\n SubscriptionCompleteResult,\n UniPaymentInstance,\n} from './types.js';\nimport { loadPayUniSDK } from './payuni-loader.js';\n\nconst RecurContext = createContext<RecurContextValue | null>(null);\n\nexport interface RecurProviderProps {\n children: React.ReactNode;\n config?: RecurConfig;\n}\n\n/**\n * RecurProvider\n *\n * Provides Recur checkout functionality to your React application\n *\n * @example\n * ```tsx\n * <RecurProvider config={{ publishableKey: 'pk_test_xxx' }}>\n * <App />\n * </RecurProvider>\n * ```\n */\nexport function RecurProvider({ children, config: initialConfig = {} }: RecurProviderProps) {\n const [config, setConfig] = useState<RecurConfig>({\n checkoutMode: 'embedded',\n ...initialConfig,\n });\n\n const [isCheckingOut, setIsCheckingOut] = useState(false);\n\n // Update config when initialConfig changes (e.g., when user switches modes)\n React.useEffect(() => {\n setConfig({\n checkoutMode: 'embedded',\n ...initialConfig,\n });\n }, [initialConfig.checkoutMode, initialConfig.publishableKey, initialConfig.containerElementId]);\n\n // Reset isCheckingOut when checkout mode changes to allow re-testing\n React.useEffect(() => {\n setIsCheckingOut(false);\n }, [config.checkoutMode]);\n\n const updateConfig = useCallback((newConfig: Partial<RecurConfig>) => {\n setConfig((prev) => ({ ...prev, ...newConfig }));\n }, []);\n\n const checkout = useCallback(\n async (options: CheckoutOptions) => {\n let modalOverlay: HTMLElement | null = null;\n\n try {\n console.log('[Recur SDK] Starting checkout flow...', {\n planId: options.planId,\n checkoutMode: config.checkoutMode,\n containerElementId: config.containerElementId,\n });\n\n setIsCheckingOut(true);\n\n // Validate required fields\n if (!options.planId) {\n throw new Error('planId is required');\n }\n\n if (!config.publishableKey) {\n throw new Error('publishableKey is required');\n }\n\n if (!options.customerEmail || !options.customerName) {\n throw new Error('customerEmail and customerName are required');\n }\n\n const baseUrl = config.baseUrl || (typeof window !== 'undefined' ? window.location.origin : '');\n console.log('[Recur SDK] Base URL:', baseUrl);\n\n const headers: HeadersInit = {\n 'Content-Type': 'application/json',\n 'X-Recur-Publishable-Key': config.publishableKey,\n };\n\n // Show loading state early for both modal and embedded modes\n let modalContent: HTMLElement | null = null;\n let loadingContainer: HTMLElement | null = null;\n let embeddedContainer: HTMLElement | null = null;\n\n if (config.checkoutMode === 'modal') {\n console.log('[Recur SDK] Mode is modal, creating modal with loading state...');\n\n // Create modal overlay\n modalOverlay = document.createElement('div');\n modalOverlay.id = 'recur-modal-overlay';\n modalOverlay.className = 'recur-sdk__modal-overlay';\n modalOverlay.style.cssText = `\n position: fixed;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n background: rgba(0, 0, 0, 0.5);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 9999;\n `;\n\n // Create modal content\n modalContent = document.createElement('div');\n modalContent.className = 'recur-sdk__modal-content';\n modalContent.style.cssText = `\n background: white;\n padding: 32px;\n border-radius: 12px;\n max-width: 500px;\n width: 90%;\n max-height: 90vh;\n overflow-y: auto;\n position: relative;\n `;\n\n // Create close button\n const closeButton = document.createElement('button');\n closeButton.className = 'recur-sdk__close-button';\n closeButton.innerHTML = '✕';\n closeButton.style.cssText = `\n position: absolute;\n top: 16px;\n right: 16px;\n background: none;\n border: none;\n font-size: 24px;\n cursor: pointer;\n color: #666;\n padding: 0;\n width: 32px;\n height: 32px;\n display: flex;\n align-items: center;\n justify-content: center;\n `;\n closeButton.onclick = () => {\n modalOverlay?.remove();\n setIsCheckingOut(false);\n options.onPaymentCancel?.();\n };\n\n // Create title\n const title = document.createElement('h2');\n title.className = 'recur-sdk__title';\n title.textContent = '訂閱付款';\n title.style.cssText = 'margin: 0 0 24px 0; font-size: 24px; font-weight: 600;';\n\n // Create loading container with spinner\n loadingContainer = document.createElement('div');\n loadingContainer.id = 'recur-modal-loading';\n loadingContainer.className = 'recur-sdk__loading-container';\n loadingContainer.style.cssText = 'text-align: center; padding: 40px 0;';\n loadingContainer.innerHTML = `\n <style>\n @keyframes recur-sdk-spin {\n 0% { transform: rotate(0deg); }\n 100% { transform: rotate(360deg); }\n }\n .recur-sdk__spinner {\n border: 3px solid #e5e7eb;\n border-top: 3px solid #3b82f6;\n border-radius: 50%;\n width: 40px;\n height: 40px;\n animation: recur-sdk-spin 1s linear infinite;\n margin: 0 auto 16px;\n }\n .recur-sdk__loading-text {\n color: #6b7280;\n font-size: 14px;\n margin: 0;\n }\n </style>\n <div class=\"recur-sdk__spinner\"></div>\n <p class=\"recur-sdk__loading-text\">正在處理訂閱...</p>\n `;\n\n modalContent.appendChild(closeButton);\n modalContent.appendChild(title);\n modalContent.appendChild(loadingContainer);\n modalOverlay.appendChild(modalContent);\n document.body.appendChild(modalOverlay);\n\n console.log('[Recur SDK] Modal created with loading state');\n } else if (config.checkoutMode === 'embedded') {\n // For embedded mode, try to show loading in the container if it exists\n console.log('[Recur SDK] Mode is embedded, checking for container...');\n\n if (!config.containerElementId) {\n console.warn('[Recur SDK] No containerElementId provided for embedded mode');\n } else {\n // Poll for the container to appear (wait up to 500ms)\n embeddedContainer = document.getElementById(config.containerElementId);\n\n if (!embeddedContainer) {\n console.log('[Recur SDK] Container not found, polling for container...');\n for (let i = 0; i < 10; i++) {\n await new Promise(resolve => setTimeout(resolve, 50));\n embeddedContainer = document.getElementById(config.containerElementId);\n if (embeddedContainer) {\n console.log('[Recur SDK] Container found after polling');\n break;\n }\n }\n }\n\n if (embeddedContainer) {\n console.log('[Recur SDK] Container found, showing loading state...');\n\n // Show loading state in the embedded container\n loadingContainer = document.createElement('div');\n loadingContainer.id = 'recur-embedded-loading';\n loadingContainer.className = 'recur-sdk__loading-container';\n loadingContainer.style.cssText = 'text-align: center; padding: 40px 0;';\n loadingContainer.innerHTML = `\n <style>\n @keyframes recur-sdk-spin {\n 0% { transform: rotate(0deg); }\n 100% { transform: rotate(360deg); }\n }\n .recur-sdk__spinner {\n border: 3px solid #e5e7eb;\n border-top: 3px solid #3b82f6;\n border-radius: 50%;\n width: 40px;\n height: 40px;\n animation: recur-sdk-spin 1s linear infinite;\n margin: 0 auto 16px;\n }\n .recur-sdk__loading-text {\n color: #6b7280;\n font-size: 14px;\n margin: 0;\n }\n </style>\n <div class=\"recur-sdk__spinner\"></div>\n <p class=\"recur-sdk__loading-text\">正在處理訂閱...</p>\n `;\n\n embeddedContainer.appendChild(loadingContainer);\n } else {\n console.log('[Recur SDK] Container not found after polling, will continue without early loading state');\n }\n }\n }\n\n // Step 1: Create subscription\n console.log('[Recur SDK] Step 1: Creating subscription...');\n const subscriptionResponse = await fetch(`${baseUrl}/api/v1/subscriptions`, {\n method: 'POST',\n headers,\n body: JSON.stringify({\n planId: options.planId,\n customerName: options.customerName,\n customerEmail: options.customerEmail,\n customerPhone: options.customerPhone,\n }),\n });\n\n if (!subscriptionResponse.ok) {\n const errorData = await subscriptionResponse.json().catch(() => ({}));\n console.error('[Recur SDK] Failed to create subscription:', errorData);\n const errorMessage = errorData.details || errorData.error || 'Failed to create subscription';\n throw new Error(errorMessage);\n }\n\n const subscriptionResult: CheckoutResult = await subscriptionResponse.json();\n console.log('[Recur SDK] Subscription created successfully:', subscriptionResult);\n\n // Call onSuccess callback\n options.onSuccess?.(subscriptionResult);\n\n // Step 2: Get SDK token\n console.log('[Recur SDK] Step 2: Getting SDK token...');\n const tokenResponse = await fetch(`${baseUrl}/api/v1/subscriptions/sdk-token`, {\n method: 'POST',\n headers,\n body: JSON.stringify({\n subscriberId: subscriptionResult.subscriber.id,\n iframeDomain: window.location.origin,\n }),\n });\n\n if (!tokenResponse.ok) {\n const errorData = await tokenResponse.json().catch(() => ({}));\n console.error('[Recur SDK] Failed to get SDK token:', errorData);\n throw new Error('Failed to get SDK token');\n }\n\n const tokenResult: SdkTokenResult = await tokenResponse.json();\n console.log('[Recur SDK] SDK token received:', {\n environment: tokenResult.environment,\n creditToken: tokenResult.creditToken,\n tokenExpired: tokenResult.tokenExpired,\n });\n\n // Step 3: Load PAYUNi SDK\n console.log('[Recur SDK] Step 3: Loading PAYUNi SDK...');\n const isSandbox = tokenResult.environment === 'SANDBOX';\n await loadPayUniSDK(isSandbox, config.mockMode);\n console.log('[Recur SDK] PAYUNi SDK loaded successfully');\n\n if (!window.UniPayment) {\n console.error('[Recur SDK] window.UniPayment is not available after loading');\n throw new Error('PAYUNi SDK failed to load');\n }\n\n // Step 4: Determine container element based on checkout mode\n console.log('[Recur SDK] Step 4: Preparing container for checkout mode:', config.checkoutMode);\n console.log('[Recur SDK] Initial containerElementId from config:', config.containerElementId);\n\n let containerElementId = config.containerElementId;\n\n // For modal mode, update the existing modal with payment form container\n if (config.checkoutMode === 'modal') {\n console.log('[Recur SDK] Mode is modal, updating modal with payment form...');\n\n if (!loadingContainer || !modalContent) {\n throw new Error('Modal components not initialized');\n }\n\n // Remove loading spinner\n loadingContainer.remove();\n\n // Create container for payment form\n const formContainer = document.createElement('div');\n containerElementId = 'recur-modal-payment-container';\n formContainer.id = containerElementId;\n\n modalContent.appendChild(formContainer);\n\n console.log('[Recur SDK] Modal updated with container ID:', containerElementId);\n } else if (config.checkoutMode === 'embedded') {\n // For embedded mode, remove loading spinner if it exists\n console.log('[Recur SDK] Mode is embedded, preparing container...');\n\n if (!containerElementId) {\n throw new Error('containerElementId is required for embedded mode');\n }\n\n // Remove loading spinner if we showed it earlier\n if (loadingContainer && embeddedContainer) {\n console.log('[Recur SDK] Removing loading spinner from embedded container');\n loadingContainer.remove();\n }\n }\n\n // Step 5: Initialize PAYUNi form\n console.log('[Recur SDK] Step 5: Initializing PAYUNi form...');\n console.log('[Recur SDK] Available UniPayment methods:', Object.keys(window.UniPayment || {}));\n\n if (!containerElementId) {\n throw new Error('containerElementId is required');\n }\n\n // PAYUNi SDK uses createSession(token, options)\n const uniPayment = window.UniPayment.createSession(tokenResult.sdkToken, {\n env: tokenResult.environment === 'SANDBOX' ? 'S' : 'P',\n elements: {\n CardNo: `${containerElementId}-card-no`,\n CardExp: `${containerElementId}-card-exp`,\n CardCvc: `${containerElementId}-card-cvc`,\n },\n });\n console.log('[Recur SDK] PAYUNi form initialized');\n\n // Step 6: Wait for container and create form elements\n console.log('[Recur SDK] Step 6: Waiting for container element...');\n\n // For embedded mode, wait for the container to appear in DOM\n // (React needs time to render after onSuccess callback)\n let container = document.getElementById(containerElementId);\n if (!container && config.checkoutMode === 'embedded') {\n console.log('[Recur SDK] Container not found, waiting for React to render...');\n // Wait up to 2 seconds for container to appear\n for (let i = 0; i < 20; i++) {\n await new Promise(resolve => setTimeout(resolve, 100));\n container = document.getElementById(containerElementId);\n if (container) {\n console.log('[Recur SDK] Container found after waiting');\n break;\n }\n }\n }\n\n if (!container) {\n throw new Error(`Container element '${containerElementId}' not found`);\n }\n\n console.log('[Recur SDK] Creating loading skeleton...');\n\n // Step 1: Show loading skeleton first\n container.innerHTML = `\n <style>\n /* Recur SDK Styles - CSS Namespace with BEM */\n\n /* Animations */\n @keyframes recur-sdk-pulse {\n 0%, 100% { opacity: 1; }\n 50% { opacity: 0.5; }\n }\n\n /* Skeleton Loading State */\n .recur-sdk__skeleton {\n animation: recur-sdk-pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;\n background-color: #e5e7eb;\n border-radius: 6px;\n }\n\n /* Form States */\n .recur-sdk__form--hidden {\n display: none;\n }\n\n /* Form Layout */\n .recur-sdk__form-field {\n margin-bottom: 16px;\n }\n\n .recur-sdk__form-row {\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 16px;\n margin-bottom: 16px;\n }\n\n /* Labels and Text */\n .recur-sdk__label {\n display: block;\n margin-bottom: 8px;\n font-weight: 500;\n color: #374151;\n }\n\n .recur-sdk__hint-text {\n margin-top: 4px;\n font-size: 12px;\n color: #6b7280;\n }\n\n /* Input Container (for PAYUNi iframes) */\n .recur-sdk__input-container {\n /* Container styles applied via ID selectors below for PAYUNi compatibility */\n }\n\n /* Button */\n .recur-sdk__button {\n width: 100%;\n padding: 12px;\n background: #3b82f6;\n color: white;\n border: none;\n border-radius: 6px;\n font-weight: 600;\n cursor: pointer;\n transition: background-color 0.15s ease-in-out;\n }\n\n .recur-sdk__button:hover {\n background: #2563eb;\n }\n\n .recur-sdk__button:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n }\n\n /* PAYUNi SDK 自訂樣式 - 與 shadcn input focus 樣式一致 */\n /* Note: .form-input-focus is applied by PAYUNi SDK, not by our code */\n .form-input-focus {\n border-color: hsl(215 16% 47%) !important;\n outline: 0 !important;\n box-shadow: 0 0 0 3px hsl(215 16% 47% / 0.5) !important;\n transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out !important;\n }\n\n /* 確保 PAYUNi iframe 容器高度一致 */\n /* Note: These IDs are required by PAYUNi SDK and cannot be changed */\n #${containerElementId}-card-no,\n #${containerElementId}-card-exp,\n #${containerElementId}-card-cvc {\n height: 36px !important;\n }\n </style>\n\n <!-- Skeleton Loading State -->\n <div id=\"${containerElementId}-skeleton\" class=\"recur-sdk__skeleton-container\">\n <div class=\"recur-sdk__form-field\">\n <label class=\"recur-sdk__label\">卡號</label>\n <div class=\"recur-sdk__skeleton\" style=\"height: 36px;\"></div>\n <p class=\"recur-sdk__hint-text\">請輸入您的 16 位數卡號</p>\n </div>\n <div class=\"recur-sdk__form-row\">\n <div class=\"recur-sdk__form-field\">\n <label class=\"recur-sdk__label\">有效期限</label>\n <div class=\"recur-sdk__skeleton\" style=\"height: 36px;\"></div>\n </div>\n <div class=\"recur-sdk__form-field\">\n <label class=\"recur-sdk__label\">安全碼</label>\n <div class=\"recur-sdk__skeleton\" style=\"height: 36px;\"></div>\n </div>\n </div>\n <!-- Show button during loading to prevent layout shift -->\n <button class=\"recur-sdk__button\" disabled>\n 載入中...\n </button>\n </div>\n\n <!-- Actual Payment Form (hidden initially) -->\n <div id=\"${containerElementId}-form\" class=\"recur-sdk__form recur-sdk__form--hidden\">\n <div class=\"recur-sdk__form-field\">\n <label class=\"recur-sdk__label\">卡號</label>\n <div id=\"${containerElementId}-card-no\" class=\"recur-sdk__input-container\"></div>\n <p class=\"recur-sdk__hint-text\">請輸入您的 16 位數卡號</p>\n </div>\n <div class=\"recur-sdk__form-row\">\n <div class=\"recur-sdk__form-field\">\n <label class=\"recur-sdk__label\">有效期限</label>\n <div id=\"${containerElementId}-card-exp\" class=\"recur-sdk__input-container\"></div>\n </div>\n <div class=\"recur-sdk__form-field\">\n <label class=\"recur-sdk__label\">安全碼</label>\n <div id=\"${containerElementId}-card-cvc\" class=\"recur-sdk__input-container\"></div>\n </div>\n </div>\n <button id=\"${containerElementId}-submit-btn\" class=\"recur-sdk__button\">\n 確認付款\n </button>\n </div>\n `;\n\n console.log('[Recur SDK] Starting PAYUNi SDK...');\n await uniPayment.start();\n console.log('[Recur SDK] PAYUNi SDK started successfully');\n\n // Step 2: Hide skeleton and show actual form\n console.log('[Recur SDK] Showing actual form...');\n const skeleton = document.getElementById(`${containerElementId}-skeleton`);\n const form = document.getElementById(`${containerElementId}-form`);\n if (skeleton && form) {\n skeleton.style.display = 'none';\n form.classList.remove('recur-sdk__form--hidden');\n }\n\n // Step 7: Handle form submission\n console.log('[Recur SDK] Step 7: Setting up submit button handler...');\n const submitBtn = document.getElementById(`${containerElementId}-submit-btn`);\n if (!submitBtn) {\n throw new Error('Submit button not found');\n }\n\n submitBtn.addEventListener('click', async () => {\n console.log('[Recur SDK] Submit button clicked');\n submitBtn.setAttribute('disabled', 'true');\n submitBtn.textContent = '處理中...';\n\n try {\n // Get card data from PAYUNi SDK\n console.log('[Recur SDK] Getting trade result from PAYUNi...');\n const tradeResult = await uniPayment.getTradeResult();\n console.log('[Recur SDK] Trade result received:', tradeResult);\n\n // Step 8: Complete subscription\n console.log('[Recur SDK] Step 8: Completing subscription...');\n const completeResponse = await fetch(\n `${baseUrl}/api/v1/subscriptions/${subscriptionResult.subscription.id}/complete`,\n {\n method: 'POST',\n headers,\n body: JSON.stringify({\n sdkToken: tokenResult.sdkToken,\n timestamp: tokenResult.timestamp,\n creditToken: tokenResult.creditToken,\n }),\n }\n );\n\n if (!completeResponse.ok) {\n const errorData = await completeResponse.json().catch(() => ({}));\n console.error('[Recur SDK] Failed to complete subscription:', errorData);\n throw new Error('Failed to complete subscription');\n }\n\n const completeResult: SubscriptionCompleteResult = await completeResponse.json();\n console.log('[Recur SDK] Subscription completed successfully:', completeResult);\n\n // Call payment complete callback\n console.log('[Recur SDK] Calling onPaymentComplete callback...');\n if (options.onPaymentComplete) {\n options.onPaymentComplete({\n id: completeResult.subscription.id,\n status: completeResult.subscription.status,\n planId: subscriptionResult.subscription.planId,\n amount: subscriptionResult.subscription.amount,\n billingPeriod: subscriptionResult.subscription.billingPeriod,\n currentPeriodStart: new Date().toISOString(),\n currentPeriodEnd: new Date().toISOString(),\n });\n }\n\n console.log('[Recur SDK] Checkout flow completed successfully!');\n\n // Close modal if in modal mode\n if (modalOverlay) {\n modalOverlay.remove();\n }\n\n setIsCheckingOut(false);\n } catch (error) {\n console.error('[Recur SDK] Error in payment flow:', error);\n const checkoutError: CheckoutError = {\n code: 'PAYMENT_FAILED',\n message: error instanceof Error ? error.message : 'Payment failed',\n };\n options.onError?.(checkoutError);\n\n // Re-enable submit button\n submitBtn.removeAttribute('disabled');\n submitBtn.textContent = '確認付款';\n setIsCheckingOut(false);\n }\n });\n\n console.log('[Recur SDK] Checkout flow initialized, waiting for user input...');\n } catch (error) {\n console.error('[Recur SDK] Checkout error:', error);\n\n // Clean up modal if it exists\n if (modalOverlay) {\n modalOverlay.remove();\n }\n\n const checkoutError: CheckoutError = {\n code: 'CHECKOUT_ERROR',\n message: error instanceof Error ? error.message : 'An unknown error occurred',\n };\n\n options.onError?.(checkoutError);\n setIsCheckingOut(false);\n throw error;\n }\n },\n [config]\n );\n\n const fetchPlans = useCallback(\n async (): Promise<PlansResult> => {\n // Check publishable key (required)\n if (!config.publishableKey) {\n throw new Error('publishableKey is required');\n }\n\n // Determine base URL\n const baseUrl = config.baseUrl || (typeof window !== 'undefined' ? window.location.origin : '');\n\n // Call the plans API (v1)\n const response = await fetch(`${baseUrl}/api/v1/plans`, {\n method: 'GET',\n headers: {\n 'Content-Type': 'application/json',\n 'X-Recur-Publishable-Key': config.publishableKey,\n },\n });\n\n if (!response.ok) {\n const errorData = await response.json().catch(() => ({}));\n throw new Error(errorData.error || 'Failed to fetch plans');\n }\n\n return await response.json();\n },\n [config]\n );\n\n const value = useMemo(\n () => ({\n config,\n checkout,\n fetchPlans,\n isCheckingOut,\n updateConfig,\n }),\n [config, checkout, fetchPlans, isCheckingOut, updateConfig]\n );\n\n return <RecurContext.Provider value={value}>{children}</RecurContext.Provider>;\n}\n\nexport { RecurContext };\n","'use client';\n\nimport { useContext } from 'react';\nimport { RecurContext } from './context.js';\nimport type { RecurContextValue } from './types.js';\n\n/**\n * useRecur Hook\n *\n * Access Recur checkout functionality from within your components\n *\n * @example\n * ```tsx\n * function MyComponent() {\n * const { checkout, isCheckingOut } = useRecur();\n *\n * return (\n * <button\n * onClick={() => checkout({ planId: 'pro' })}\n * disabled={isCheckingOut}\n * >\n * {isCheckingOut ? 'Processing...' : 'Subscribe'}\n * </button>\n * );\n * }\n * ```\n *\n * @throws {Error} If used outside of RecurProvider\n */\nexport function useRecur(): RecurContextValue {\n const context = useContext(RecurContext);\n\n if (!context) {\n throw new Error('useRecur must be used within a RecurProvider');\n }\n\n return context;\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/payuni-loader.ts","../src/context.tsx","../src/use-recur.ts","../src/hooks/use-plans.ts","../src/hooks/use-subscribe.ts"],"names":["useState","isLoading","useCallback","error"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * PAYUNi SDK Loader\n * Dynamically loads and initializes the PAYUNi UNi Embed SDK\n */\n\nimport type { UniPaymentInstance } from './types.js';\n\nconst PAYUNI_SDK_URL = 'https://vendor.payuni.com.tw/sdk/uni-payment.js';\nconst PAYUNI_SANDBOX_SDK_URL = 'https://sandbox-vendor.payuni.com.tw/sdk/uni-payment.js';\n\nlet isLoaded = false;\nlet isLoading = false;\nlet loadPromise: Promise<void> | null = null;\n\n/**\n * Load PAYUNi SDK script\n * @param isSandbox - Use sandbox environment\n */\nexport async function loadPayUniSDK(isSandbox: boolean = false): Promise<void> {\n // Already loaded\n if (isLoaded && window.UniPayment) {\n return Promise.resolve();\n }\n\n // Currently loading\n if (isLoading && loadPromise) {\n return loadPromise;\n }\n\n // Start loading\n isLoading = true;\n loadPromise = new Promise((resolve, reject) => {\n const script = document.createElement('script');\n script.src = isSandbox ? PAYUNI_SANDBOX_SDK_URL : PAYUNI_SDK_URL;\n script.async = true;\n\n script.onload = () => {\n isLoaded = true;\n isLoading = false;\n resolve();\n };\n\n script.onerror = () => {\n isLoading = false;\n loadPromise = null;\n reject(new Error('Failed to load PAYUNi SDK'));\n };\n\n document.head.appendChild(script);\n });\n\n return loadPromise;\n}\n\n/**\n * Check if PAYUNi SDK is loaded\n */\nexport function isPayUniSDKLoaded(): boolean {\n return isLoaded && typeof window !== 'undefined' && !!window.UniPayment;\n}\n\n/**\n * Wait for PAYUNi SDK to be ready\n */\nexport async function waitForPayUniSDK(timeout: number = 10000): Promise<void> {\n const startTime = Date.now();\n\n while (!isPayUniSDKLoaded()) {\n if (Date.now() - startTime > timeout) {\n throw new Error('PAYUNi SDK load timeout');\n }\n await new Promise(resolve => setTimeout(resolve, 100));\n }\n}\n","'use client';\n\nimport React, { createContext, useCallback, useState, useMemo } from 'react';\nimport type {\n RecurConfig,\n RecurContextValue,\n CheckoutOptions,\n CheckoutResult,\n CheckoutError,\n PlansResult,\n SdkTokenResult,\n SubscriptionCompleteResult,\n UniPaymentInstance,\n} from './types.js';\nimport { loadPayUniSDK } from './payuni-loader.js';\n\nconst RecurContext = createContext<RecurContextValue | null>(null);\n\nexport interface RecurProviderProps {\n children: React.ReactNode;\n config?: RecurConfig;\n}\n\n/**\n * RecurProvider\n *\n * Provides Recur checkout functionality to your React application\n *\n * @example\n * ```tsx\n * <RecurProvider config={{ publishableKey: 'pk_test_xxx' }}>\n * <App />\n * </RecurProvider>\n * ```\n */\nexport function RecurProvider({ children, config: initialConfig = {} }: RecurProviderProps) {\n const [config, setConfig] = useState<RecurConfig>({\n checkoutMode: 'embedded',\n ...initialConfig,\n });\n\n const [isCheckingOut, setIsCheckingOut] = useState(false);\n\n // Update config when initialConfig changes (e.g., when user switches modes)\n React.useEffect(() => {\n setConfig({\n checkoutMode: 'embedded',\n ...initialConfig,\n });\n }, [initialConfig.checkoutMode, initialConfig.publishableKey, initialConfig.containerElementId]);\n\n // Reset isCheckingOut when checkout mode changes to allow re-testing\n React.useEffect(() => {\n setIsCheckingOut(false);\n }, [config.checkoutMode]);\n\n const updateConfig = useCallback((newConfig: Partial<RecurConfig>) => {\n setConfig((prev) => ({ ...prev, ...newConfig }));\n }, []);\n\n const checkout = useCallback(\n async (options: CheckoutOptions) => {\n let modalOverlay: HTMLElement | null = null;\n\n try {\n console.log('[Recur SDK] Starting checkout flow...', {\n planId: options.planId,\n checkoutMode: config.checkoutMode,\n containerElementId: config.containerElementId,\n });\n\n setIsCheckingOut(true);\n\n // Validate required fields\n if (!options.planId) {\n throw new Error('planId is required');\n }\n\n if (!config.publishableKey) {\n throw new Error('publishableKey is required');\n }\n\n if (!options.customerEmail || !options.customerName) {\n throw new Error('customerEmail and customerName are required');\n }\n\n // Use configured baseUrl or default to production API\n const baseUrl = config.baseUrl || 'https://api.recur.tw';\n console.log('[Recur SDK] Base URL:', baseUrl);\n\n const headers: HeadersInit = {\n 'Content-Type': 'application/json',\n 'X-Recur-Publishable-Key': config.publishableKey,\n };\n\n // Show loading state early for both modal and embedded modes\n let modalContent: HTMLElement | null = null;\n let loadingContainer: HTMLElement | null = null;\n let embeddedContainer: HTMLElement | null = null;\n\n if (config.checkoutMode === 'modal') {\n console.log('[Recur SDK] Mode is modal, creating modal with loading state...');\n\n // Create modal overlay\n modalOverlay = document.createElement('div');\n modalOverlay.id = 'recur-modal-overlay';\n modalOverlay.className = 'recur-sdk__modal-overlay';\n modalOverlay.style.cssText = `\n position: fixed;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n background: rgba(0, 0, 0, 0.5);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 9999;\n `;\n\n // Create modal content\n modalContent = document.createElement('div');\n modalContent.className = 'recur-sdk__modal-content';\n modalContent.style.cssText = `\n background: white;\n padding: 32px;\n border-radius: 12px;\n max-width: 500px;\n width: 90%;\n max-height: 90vh;\n overflow-y: auto;\n position: relative;\n `;\n\n // Create close button\n const closeButton = document.createElement('button');\n closeButton.className = 'recur-sdk__close-button';\n closeButton.innerHTML = '✕';\n closeButton.style.cssText = `\n position: absolute;\n top: 16px;\n right: 16px;\n background: none;\n border: none;\n font-size: 24px;\n cursor: pointer;\n color: #666;\n padding: 0;\n width: 32px;\n height: 32px;\n display: flex;\n align-items: center;\n justify-content: center;\n `;\n closeButton.onclick = () => {\n modalOverlay?.remove();\n setIsCheckingOut(false);\n options.onPaymentCancel?.();\n };\n\n // Create title\n const title = document.createElement('h2');\n title.className = 'recur-sdk__title';\n title.textContent = '訂閱付款';\n title.style.cssText = 'margin: 0 0 24px 0; font-size: 24px; font-weight: 600;';\n\n // Create loading container with spinner\n loadingContainer = document.createElement('div');\n loadingContainer.id = 'recur-modal-loading';\n loadingContainer.className = 'recur-sdk__loading-container';\n loadingContainer.style.cssText = 'text-align: center; padding: 40px 0;';\n loadingContainer.innerHTML = `\n <style>\n @keyframes recur-sdk-spin {\n 0% { transform: rotate(0deg); }\n 100% { transform: rotate(360deg); }\n }\n .recur-sdk__spinner {\n border: 3px solid #e5e7eb;\n border-top: 3px solid #3b82f6;\n border-radius: 50%;\n width: 40px;\n height: 40px;\n animation: recur-sdk-spin 1s linear infinite;\n margin: 0 auto 16px;\n }\n .recur-sdk__loading-text {\n color: #6b7280;\n font-size: 14px;\n margin: 0;\n }\n </style>\n <div class=\"recur-sdk__spinner\"></div>\n <p class=\"recur-sdk__loading-text\">正在處理訂閱...</p>\n `;\n\n modalContent.appendChild(closeButton);\n modalContent.appendChild(title);\n modalContent.appendChild(loadingContainer);\n modalOverlay.appendChild(modalContent);\n document.body.appendChild(modalOverlay);\n\n console.log('[Recur SDK] Modal created with loading state');\n } else if (config.checkoutMode === 'embedded') {\n // For embedded mode, try to show loading in the container if it exists\n console.log('[Recur SDK] Mode is embedded, checking for container...');\n\n if (!config.containerElementId) {\n console.warn('[Recur SDK] No containerElementId provided for embedded mode');\n } else {\n // Poll for the container to appear (wait up to 500ms)\n embeddedContainer = document.getElementById(config.containerElementId);\n\n if (!embeddedContainer) {\n console.log('[Recur SDK] Container not found, polling for container...');\n for (let i = 0; i < 10; i++) {\n await new Promise(resolve => setTimeout(resolve, 50));\n embeddedContainer = document.getElementById(config.containerElementId);\n if (embeddedContainer) {\n console.log('[Recur SDK] Container found after polling');\n break;\n }\n }\n }\n\n if (embeddedContainer) {\n console.log('[Recur SDK] Container found, showing loading state...');\n\n // Show loading state in the embedded container\n loadingContainer = document.createElement('div');\n loadingContainer.id = 'recur-embedded-loading';\n loadingContainer.className = 'recur-sdk__loading-container';\n loadingContainer.style.cssText = 'text-align: center; padding: 40px 0;';\n loadingContainer.innerHTML = `\n <style>\n @keyframes recur-sdk-spin {\n 0% { transform: rotate(0deg); }\n 100% { transform: rotate(360deg); }\n }\n .recur-sdk__spinner {\n border: 3px solid #e5e7eb;\n border-top: 3px solid #3b82f6;\n border-radius: 50%;\n width: 40px;\n height: 40px;\n animation: recur-sdk-spin 1s linear infinite;\n margin: 0 auto 16px;\n }\n .recur-sdk__loading-text {\n color: #6b7280;\n font-size: 14px;\n margin: 0;\n }\n </style>\n <div class=\"recur-sdk__spinner\"></div>\n <p class=\"recur-sdk__loading-text\">正在處理訂閱...</p>\n `;\n\n embeddedContainer.appendChild(loadingContainer);\n } else {\n console.log('[Recur SDK] Container not found after polling, will continue without early loading state');\n }\n }\n }\n\n // Step 1: Create subscription\n console.log('[Recur SDK] Step 1: Creating subscription...');\n\n const subscriptionResponse = await fetch(`${baseUrl}/v1/subscriptions`, {\n method: 'POST',\n headers,\n body: JSON.stringify({\n planId: options.planId,\n customerName: options.customerName,\n customerEmail: options.customerEmail,\n customerPhone: options.customerPhone,\n }),\n });\n\n if (!subscriptionResponse.ok) {\n const errorData = await subscriptionResponse.json().catch(() => ({}));\n console.error('[Recur SDK] Failed to create subscription:', errorData);\n const errorMessage = errorData.details || errorData.error || 'Failed to create subscription';\n throw new Error(errorMessage);\n }\n\n const subscriptionResult: CheckoutResult = await subscriptionResponse.json();\n console.log('[Recur SDK] Subscription created successfully:', subscriptionResult);\n\n // Call onSuccess callback\n options.onSuccess?.(subscriptionResult);\n\n // Step 2: Get SDK token\n console.log('[Recur SDK] Step 2: Getting SDK token...');\n\n const tokenResponse = await fetch(`${baseUrl}/v1/subscriptions/sdk-token`, {\n method: 'POST',\n headers,\n body: JSON.stringify({\n subscriberId: subscriptionResult.subscriber.id,\n iframeDomain: window.location.origin,\n }),\n });\n\n if (!tokenResponse.ok) {\n const errorData = await tokenResponse.json().catch(() => ({}));\n console.error('[Recur SDK] Failed to get SDK token:', errorData);\n throw new Error('Failed to get SDK token');\n }\n\n const tokenResult: SdkTokenResult = await tokenResponse.json();\n console.log('[Recur SDK] SDK token received:', {\n environment: tokenResult.environment,\n creditToken: tokenResult.creditToken,\n tokenExpired: tokenResult.tokenExpired,\n });\n\n // Step 3: Load PAYUNi SDK\n console.log('[Recur SDK] Step 3: Loading PAYUNi SDK...');\n const isSandbox = tokenResult.environment === 'SANDBOX';\n await loadPayUniSDK(isSandbox);\n console.log('[Recur SDK] PAYUNi SDK loaded successfully');\n\n if (!window.UniPayment) {\n console.error('[Recur SDK] window.UniPayment is not available after loading');\n throw new Error('PAYUNi SDK failed to load');\n }\n\n // Step 4: Determine container element based on checkout mode\n console.log('[Recur SDK] Step 4: Preparing container for checkout mode:', config.checkoutMode);\n console.log('[Recur SDK] Initial containerElementId from config:', config.containerElementId);\n\n let containerElementId = config.containerElementId;\n\n // For modal mode, update the existing modal with payment form container\n if (config.checkoutMode === 'modal') {\n console.log('[Recur SDK] Mode is modal, updating modal with payment form...');\n\n if (!loadingContainer || !modalContent) {\n throw new Error('Modal components not initialized');\n }\n\n // Remove loading spinner\n loadingContainer.remove();\n\n // Create container for payment form\n const formContainer = document.createElement('div');\n containerElementId = 'recur-modal-payment-container';\n formContainer.id = containerElementId;\n\n modalContent.appendChild(formContainer);\n\n console.log('[Recur SDK] Modal updated with container ID:', containerElementId);\n } else if (config.checkoutMode === 'embedded') {\n // For embedded mode, remove loading spinner if it exists\n console.log('[Recur SDK] Mode is embedded, preparing container...');\n\n if (!containerElementId) {\n throw new Error('containerElementId is required for embedded mode');\n }\n\n // Remove loading spinner if we showed it earlier\n if (loadingContainer && embeddedContainer) {\n console.log('[Recur SDK] Removing loading spinner from embedded container');\n loadingContainer.remove();\n }\n }\n\n // Step 5: Initialize PAYUNi form\n console.log('[Recur SDK] Step 5: Initializing PAYUNi form...');\n console.log('[Recur SDK] Available UniPayment methods:', Object.keys(window.UniPayment || {}));\n\n if (!containerElementId) {\n throw new Error('containerElementId is required');\n }\n\n // PAYUNi SDK uses createSession(token, options)\n const uniPayment = window.UniPayment.createSession(tokenResult.sdkToken, {\n env: tokenResult.environment === 'SANDBOX' ? 'S' : 'P',\n elements: {\n CardNo: `${containerElementId}-card-no`,\n CardExp: `${containerElementId}-card-exp`,\n CardCvc: `${containerElementId}-card-cvc`,\n },\n });\n console.log('[Recur SDK] PAYUNi form initialized');\n\n // Step 6: Wait for container and create form elements\n console.log('[Recur SDK] Step 6: Waiting for container element...');\n\n // For embedded mode, wait for the container to appear in DOM\n // (React needs time to render after onSuccess callback)\n let container = document.getElementById(containerElementId);\n if (!container && config.checkoutMode === 'embedded') {\n console.log('[Recur SDK] Container not found, waiting for React to render...');\n // Wait up to 2 seconds for container to appear\n for (let i = 0; i < 20; i++) {\n await new Promise(resolve => setTimeout(resolve, 100));\n container = document.getElementById(containerElementId);\n if (container) {\n console.log('[Recur SDK] Container found after waiting');\n break;\n }\n }\n }\n\n if (!container) {\n throw new Error(`Container element '${containerElementId}' not found`);\n }\n\n console.log('[Recur SDK] Creating loading skeleton...');\n\n // Step 1: Show loading skeleton first\n container.innerHTML = `\n <style>\n /* Recur SDK Styles - CSS Namespace with BEM */\n\n /* Animations */\n @keyframes recur-sdk-pulse {\n 0%, 100% { opacity: 1; }\n 50% { opacity: 0.5; }\n }\n\n /* Skeleton Loading State */\n .recur-sdk__skeleton {\n animation: recur-sdk-pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;\n background-color: #e5e7eb;\n border-radius: 6px;\n }\n\n /* Form States */\n .recur-sdk__form--hidden {\n display: none;\n }\n\n /* Form Layout */\n .recur-sdk__form-field {\n margin-bottom: 16px;\n }\n\n .recur-sdk__form-row {\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 16px;\n margin-bottom: 16px;\n }\n\n /* Labels and Text */\n .recur-sdk__label {\n display: block;\n margin-bottom: 8px;\n font-weight: 500;\n color: #374151;\n }\n\n .recur-sdk__hint-text {\n margin-top: 4px;\n font-size: 12px;\n color: #6b7280;\n }\n\n /* Input Container (for PAYUNi iframes) */\n .recur-sdk__input-container {\n /* Container styles applied via ID selectors below for PAYUNi compatibility */\n }\n\n /* Button */\n .recur-sdk__button {\n width: 100%;\n padding: 12px;\n background: #3b82f6;\n color: white;\n border: none;\n border-radius: 6px;\n font-weight: 600;\n cursor: pointer;\n transition: background-color 0.15s ease-in-out;\n }\n\n .recur-sdk__button:hover {\n background: #2563eb;\n }\n\n .recur-sdk__button:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n }\n\n /* PAYUNi SDK 自訂樣式 - 與 shadcn input focus 樣式一致 */\n /* Note: .form-input-focus is applied by PAYUNi SDK, not by our code */\n .form-input-focus {\n border-color: hsl(215 16% 47%) !important;\n outline: 0 !important;\n box-shadow: 0 0 0 3px hsl(215 16% 47% / 0.5) !important;\n transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out !important;\n }\n\n /* 確保 PAYUNi iframe 容器高度一致 */\n /* Note: These IDs are required by PAYUNi SDK and cannot be changed */\n #${containerElementId}-card-no,\n #${containerElementId}-card-exp,\n #${containerElementId}-card-cvc {\n height: 36px !important;\n }\n </style>\n\n <!-- Skeleton Loading State -->\n <div id=\"${containerElementId}-skeleton\" class=\"recur-sdk__skeleton-container\">\n <div class=\"recur-sdk__form-field\">\n <label class=\"recur-sdk__label\">卡號</label>\n <div class=\"recur-sdk__skeleton\" style=\"height: 36px;\"></div>\n <p class=\"recur-sdk__hint-text\">請輸入您的 16 位數卡號</p>\n </div>\n <div class=\"recur-sdk__form-row\">\n <div class=\"recur-sdk__form-field\">\n <label class=\"recur-sdk__label\">有效期限</label>\n <div class=\"recur-sdk__skeleton\" style=\"height: 36px;\"></div>\n </div>\n <div class=\"recur-sdk__form-field\">\n <label class=\"recur-sdk__label\">安全碼</label>\n <div class=\"recur-sdk__skeleton\" style=\"height: 36px;\"></div>\n </div>\n </div>\n <!-- Show button during loading to prevent layout shift -->\n <button class=\"recur-sdk__button\" disabled>\n 載入中...\n </button>\n </div>\n\n <!-- Actual Payment Form (hidden initially) -->\n <div id=\"${containerElementId}-form\" class=\"recur-sdk__form recur-sdk__form--hidden\">\n <div class=\"recur-sdk__form-field\">\n <label class=\"recur-sdk__label\">卡號</label>\n <div id=\"${containerElementId}-card-no\" class=\"recur-sdk__input-container\"></div>\n <p class=\"recur-sdk__hint-text\">請輸入您的 16 位數卡號</p>\n </div>\n <div class=\"recur-sdk__form-row\">\n <div class=\"recur-sdk__form-field\">\n <label class=\"recur-sdk__label\">有效期限</label>\n <div id=\"${containerElementId}-card-exp\" class=\"recur-sdk__input-container\"></div>\n </div>\n <div class=\"recur-sdk__form-field\">\n <label class=\"recur-sdk__label\">安全碼</label>\n <div id=\"${containerElementId}-card-cvc\" class=\"recur-sdk__input-container\"></div>\n </div>\n </div>\n <button id=\"${containerElementId}-submit-btn\" class=\"recur-sdk__button\">\n 確認付款\n </button>\n </div>\n `;\n\n console.log('[Recur SDK] Starting PAYUNi SDK...');\n await uniPayment.start();\n console.log('[Recur SDK] PAYUNi SDK started successfully');\n\n // Step 2: Hide skeleton and show actual form\n console.log('[Recur SDK] Showing actual form...');\n const skeleton = document.getElementById(`${containerElementId}-skeleton`);\n const form = document.getElementById(`${containerElementId}-form`);\n if (skeleton && form) {\n skeleton.style.display = 'none';\n form.classList.remove('recur-sdk__form--hidden');\n }\n\n // Step 7: Handle form submission\n console.log('[Recur SDK] Step 7: Setting up submit button handler...');\n const submitBtn = document.getElementById(`${containerElementId}-submit-btn`);\n if (!submitBtn) {\n throw new Error('Submit button not found');\n }\n\n submitBtn.addEventListener('click', async () => {\n console.log('[Recur SDK] Submit button clicked');\n submitBtn.setAttribute('disabled', 'true');\n submitBtn.textContent = '處理中...';\n\n try {\n // Get card data from PAYUNi SDK\n console.log('[Recur SDK] Getting trade result from PAYUNi...');\n const tradeResult = await uniPayment.getTradeResult();\n console.log('[Recur SDK] Trade result received:', tradeResult);\n\n // Step 8: Complete subscription\n console.log('[Recur SDK] Step 8: Completing subscription...');\n const completeResponse = await fetch(\n `${baseUrl}/v1/subscriptions/${subscriptionResult.subscription.id}/complete`,\n {\n method: 'POST',\n headers,\n body: JSON.stringify({\n sdkToken: tokenResult.sdkToken,\n timestamp: tokenResult.timestamp,\n creditToken: tokenResult.creditToken,\n }),\n }\n );\n\n if (!completeResponse.ok) {\n const errorData = await completeResponse.json().catch(() => ({}));\n console.error('[Recur SDK] Failed to complete subscription:', errorData);\n throw new Error('Failed to complete subscription');\n }\n\n const completeResult: SubscriptionCompleteResult = await completeResponse.json();\n console.log('[Recur SDK] Subscription completed successfully:', completeResult);\n\n // Call payment complete callback\n console.log('[Recur SDK] Calling onPaymentComplete callback...');\n if (options.onPaymentComplete) {\n options.onPaymentComplete({\n id: completeResult.subscription.id,\n status: completeResult.subscription.status,\n planId: subscriptionResult.subscription.planId,\n amount: subscriptionResult.subscription.amount,\n billingPeriod: subscriptionResult.subscription.billingPeriod,\n currentPeriodStart: new Date().toISOString(),\n currentPeriodEnd: new Date().toISOString(),\n });\n }\n\n console.log('[Recur SDK] Checkout flow completed successfully!');\n\n // Close modal if in modal mode\n if (modalOverlay) {\n modalOverlay.remove();\n }\n\n setIsCheckingOut(false);\n } catch (error) {\n console.error('[Recur SDK] Error in payment flow:', error);\n const checkoutError: CheckoutError = {\n code: 'PAYMENT_FAILED',\n message: error instanceof Error ? error.message : 'Payment failed',\n };\n options.onError?.(checkoutError);\n\n // Re-enable submit button\n submitBtn.removeAttribute('disabled');\n submitBtn.textContent = '確認付款';\n setIsCheckingOut(false);\n }\n });\n\n console.log('[Recur SDK] Checkout flow initialized, waiting for user input...');\n } catch (error) {\n console.error('[Recur SDK] Checkout error:', error);\n\n // Clean up modal if it exists\n if (modalOverlay) {\n modalOverlay.remove();\n }\n\n const checkoutError: CheckoutError = {\n code: 'CHECKOUT_ERROR',\n message: error instanceof Error ? error.message : 'An unknown error occurred',\n };\n\n options.onError?.(checkoutError);\n setIsCheckingOut(false);\n throw error;\n }\n },\n [config]\n );\n\n const fetchPlans = useCallback(\n async (): Promise<PlansResult> => {\n // Check publishable key (required)\n if (!config.publishableKey) {\n throw new Error('publishableKey is required');\n }\n\n // Use configured baseUrl or default to production API\n const baseUrl = config.baseUrl || 'https://api.recur.tw';\n\n // Call the plans API (v1)\n const response = await fetch(`${baseUrl}/v1/plans`, {\n method: 'GET',\n headers: {\n 'Content-Type': 'application/json',\n 'X-Recur-Publishable-Key': config.publishableKey,\n },\n });\n\n if (!response.ok) {\n const errorData = await response.json().catch(() => ({}));\n throw new Error(errorData.error || 'Failed to fetch plans');\n }\n\n return await response.json();\n },\n [config]\n );\n\n const value = useMemo(\n () => ({\n config,\n checkout,\n fetchPlans,\n isCheckingOut,\n updateConfig,\n }),\n [config, checkout, fetchPlans, isCheckingOut, updateConfig]\n );\n\n return <RecurContext.Provider value={value}>{children}</RecurContext.Provider>;\n}\n\nexport { RecurContext };\n","'use client';\n\nimport { useContext } from 'react';\nimport { RecurContext } from './context.js';\nimport type { RecurContextValue } from './types.js';\n\n/**\n * useRecur Hook\n *\n * Access Recur checkout functionality from within your components\n *\n * @example\n * ```tsx\n * function MyComponent() {\n * const { checkout, isCheckingOut } = useRecur();\n *\n * return (\n * <button\n * onClick={() => checkout({ planId: 'pro' })}\n * disabled={isCheckingOut}\n * >\n * {isCheckingOut ? 'Processing...' : 'Subscribe'}\n * </button>\n * );\n * }\n * ```\n *\n * @throws {Error} If used outside of RecurProvider\n */\nexport function useRecur(): RecurContextValue {\n const context = useContext(RecurContext);\n\n if (!context) {\n throw new Error('useRecur must be used within a RecurProvider');\n }\n\n return context;\n}\n","'use client';\n\nimport { useState, useEffect, useCallback } from 'react';\nimport { useRecur } from '../use-recur.js';\nimport type { Plan } from '../types.js';\n\nexport interface UsePlansOptions {\n /**\n * Whether to fetch plans immediately on mount\n * @default true\n */\n enabled?: boolean;\n\n /**\n * Callback when plans are fetched successfully\n */\n onSuccess?: (plans: Plan[]) => void;\n\n /**\n * Callback when an error occurs\n */\n onError?: (error: Error) => void;\n}\n\nexport interface UsePlansResult {\n /**\n * The fetched plans data\n */\n data: Plan[] | undefined;\n\n /**\n * Whether the plans are currently being fetched\n */\n isLoading: boolean;\n\n /**\n * Error object if fetch failed\n */\n error: Error | null;\n\n /**\n * Manually trigger a refetch\n */\n refetch: () => Promise<void>;\n}\n\n/**\n * usePlans Hook\n *\n * Fetch available subscription plans with automatic state management\n *\n * @example\n * ```tsx\n * function PlansPage() {\n * const { data: plans, isLoading, error } = usePlans();\n *\n * if (isLoading) return <PlansSkeletonLoader />;\n * if (error) return <ErrorMessage error={error} />;\n *\n * return (\n * <div>\n * {plans?.map(plan => (\n * <PlanCard key={plan.id} plan={plan} />\n * ))}\n * </div>\n * );\n * }\n * ```\n */\nexport function usePlans(options: UsePlansOptions = {}): UsePlansResult {\n const { enabled = true, onSuccess, onError } = options;\n const { fetchPlans } = useRecur();\n\n const [data, setData] = useState<Plan[] | undefined>(undefined);\n const [isLoading, setIsLoading] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n\n const fetchData = useCallback(async () => {\n if (!enabled) return;\n\n setIsLoading(true);\n setError(null);\n\n try {\n const result = await fetchPlans();\n setData(result.plans);\n onSuccess?.(result.plans);\n } catch (err) {\n const error = err instanceof Error ? err : new Error('Failed to fetch plans');\n setError(error);\n onError?.(error);\n } finally {\n setIsLoading(false);\n }\n }, [enabled, fetchPlans, onSuccess, onError]);\n\n useEffect(() => {\n fetchData();\n }, [fetchData]);\n\n return {\n data,\n isLoading,\n error,\n refetch: fetchData,\n };\n}\n","'use client';\n\nimport { useState, useCallback } from 'react';\nimport { useRecur } from '../use-recur.js';\nimport type { CheckoutOptions, CheckoutResult, CheckoutError } from '../types.js';\n\nexport interface UseSubscribeOptions {\n /**\n * Callback when subscription is created successfully\n */\n onSuccess?: (result: CheckoutResult) => void;\n\n /**\n * Callback when payment is completed\n */\n onPaymentComplete?: (subscription: any) => void;\n\n /**\n * Callback when an error occurs\n */\n onError?: (error: CheckoutError) => void;\n\n /**\n * Callback when payment is cancelled\n */\n onPaymentCancel?: () => void;\n}\n\nexport interface UseSubscribeResult {\n /**\n * Function to initiate subscription checkout\n */\n mutate: (options: Omit<CheckoutOptions, 'onSuccess' | 'onError' | 'onPaymentComplete' | 'onPaymentCancel'>) => Promise<void>;\n\n /**\n * Alias for mutate (for better DX)\n */\n subscribe: (options: Omit<CheckoutOptions, 'onSuccess' | 'onError' | 'onPaymentComplete' | 'onPaymentCancel'>) => Promise<void>;\n\n /**\n * Whether subscription is in progress\n */\n isLoading: boolean;\n\n /**\n * Error object if subscription failed\n */\n error: CheckoutError | null;\n\n /**\n * Reset error state\n */\n reset: () => void;\n}\n\n/**\n * useSubscribe Hook\n *\n * Create a subscription with automatic state management\n *\n * @example\n * ```tsx\n * function SubscribePage() {\n * const { data: plans } = usePlans();\n * const { subscribe, isLoading, error } = useSubscribe({\n * onSuccess: () => {\n * console.log('Subscription created!');\n * },\n * onPaymentComplete: () => {\n * router.push('/account');\n * }\n * });\n *\n * return (\n * <div>\n * {plans?.map(plan => (\n * <PlanCard\n * key={plan.id}\n * plan={plan}\n * onSubscribe={() => subscribe({\n * planId: plan.id,\n * customerEmail: 'user@example.com',\n * customerName: 'John Doe'\n * })}\n * isLoading={isLoading}\n * />\n * ))}\n * {error && <ErrorMessage error={error} />}\n * </div>\n * );\n * }\n * ```\n */\nexport function useSubscribe(options: UseSubscribeOptions = {}): UseSubscribeResult {\n const { onSuccess, onError, onPaymentComplete, onPaymentCancel } = options;\n const { checkout, isCheckingOut } = useRecur();\n\n const [error, setError] = useState<CheckoutError | null>(null);\n\n const mutate = useCallback(\n async (checkoutOptions: Omit<CheckoutOptions, 'onSuccess' | 'onError' | 'onPaymentComplete' | 'onPaymentCancel'>) => {\n setError(null);\n\n try {\n await checkout({\n ...checkoutOptions,\n onSuccess: (result) => {\n onSuccess?.(result);\n },\n onPaymentComplete: (subscription) => {\n onPaymentComplete?.(subscription);\n },\n onError: (err) => {\n setError(err);\n onError?.(err);\n },\n onPaymentCancel: () => {\n onPaymentCancel?.();\n },\n });\n } catch (err) {\n const checkoutError: CheckoutError = {\n code: 'CHECKOUT_FAILED',\n message: err instanceof Error ? err.message : 'Unknown error occurred',\n };\n setError(checkoutError);\n onError?.(checkoutError);\n }\n },\n [checkout, onSuccess, onError, onPaymentComplete, onPaymentCancel]\n );\n\n const reset = useCallback(() => {\n setError(null);\n }, []);\n\n return {\n mutate,\n subscribe: mutate,\n isLoading: isCheckingOut,\n error,\n reset,\n };\n}\n"]}
|