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.cjs
CHANGED
|
@@ -15,52 +15,7 @@ var PAYUNI_SANDBOX_SDK_URL = "https://sandbox-vendor.payuni.com.tw/sdk/uni-payme
|
|
|
15
15
|
var isLoaded = false;
|
|
16
16
|
var isLoading = false;
|
|
17
17
|
var loadPromise = null;
|
|
18
|
-
function
|
|
19
|
-
return {
|
|
20
|
-
createSession: (token, options) => {
|
|
21
|
-
console.log("[Mock PAYUNi SDK] Creating session with token:", token.substring(0, 20) + "...");
|
|
22
|
-
console.log("[Mock PAYUNi SDK] Options:", options);
|
|
23
|
-
const { elements } = options;
|
|
24
|
-
const cardNoEl = document.getElementById(elements.CardNo);
|
|
25
|
-
const cardExpEl = document.getElementById(elements.CardExp);
|
|
26
|
-
const cardCvcEl = document.getElementById(elements.CardCvc);
|
|
27
|
-
if (cardNoEl) {
|
|
28
|
-
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>';
|
|
29
|
-
}
|
|
30
|
-
if (cardExpEl) {
|
|
31
|
-
cardExpEl.innerHTML = '<div style="border: 1px solid #e5e7eb; padding: 8px; border-radius: 4px; background: white; font-size: 14px; color: #6b7280;">12 / 25 (Mock)</div>';
|
|
32
|
-
}
|
|
33
|
-
if (cardCvcEl) {
|
|
34
|
-
cardCvcEl.innerHTML = '<div style="border: 1px solid #e5e7eb; padding: 8px; border-radius: 4px; background: white; font-size: 14px; color: #6b7280;">123 (Mock)</div>';
|
|
35
|
-
}
|
|
36
|
-
return {
|
|
37
|
-
start: async () => {
|
|
38
|
-
console.log("[Mock PAYUNi SDK] \u2713 Started successfully");
|
|
39
|
-
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
40
|
-
},
|
|
41
|
-
getTradeResult: async () => {
|
|
42
|
-
console.log("[Mock PAYUNi SDK] \u2713 Getting trade result");
|
|
43
|
-
await new Promise((resolve) => setTimeout(resolve, 800));
|
|
44
|
-
return {
|
|
45
|
-
Status: "SUCCESS",
|
|
46
|
-
Message: "Mock payment successful",
|
|
47
|
-
TradeNo: `MOCK${Date.now()}`,
|
|
48
|
-
AuthCode: "123456"
|
|
49
|
-
};
|
|
50
|
-
}
|
|
51
|
-
};
|
|
52
|
-
}
|
|
53
|
-
};
|
|
54
|
-
}
|
|
55
|
-
async function loadPayUniSDK(isSandbox = false, mockMode = false) {
|
|
56
|
-
if (mockMode) {
|
|
57
|
-
console.log("[Recur SDK] \u{1F3AD} Mock mode enabled - using mock PAYUNi SDK");
|
|
58
|
-
if (typeof window !== "undefined") {
|
|
59
|
-
window.UniPayment = createMockUniPayment();
|
|
60
|
-
}
|
|
61
|
-
isLoaded = true;
|
|
62
|
-
return Promise.resolve();
|
|
63
|
-
}
|
|
18
|
+
async function loadPayUniSDK(isSandbox = false) {
|
|
64
19
|
if (isLoaded && window.UniPayment) {
|
|
65
20
|
return Promise.resolve();
|
|
66
21
|
}
|
|
@@ -124,7 +79,7 @@ function RecurProvider({ children, config: initialConfig = {} }) {
|
|
|
124
79
|
if (!options.customerEmail || !options.customerName) {
|
|
125
80
|
throw new Error("customerEmail and customerName are required");
|
|
126
81
|
}
|
|
127
|
-
const baseUrl = config.baseUrl ||
|
|
82
|
+
const baseUrl = config.baseUrl || "https://api.recur.tw";
|
|
128
83
|
console.log("[Recur SDK] Base URL:", baseUrl);
|
|
129
84
|
const headers = {
|
|
130
85
|
"Content-Type": "application/json",
|
|
@@ -278,7 +233,7 @@ function RecurProvider({ children, config: initialConfig = {} }) {
|
|
|
278
233
|
}
|
|
279
234
|
}
|
|
280
235
|
console.log("[Recur SDK] Step 1: Creating subscription...");
|
|
281
|
-
const subscriptionResponse = await fetch(`${baseUrl}/
|
|
236
|
+
const subscriptionResponse = await fetch(`${baseUrl}/v1/subscriptions`, {
|
|
282
237
|
method: "POST",
|
|
283
238
|
headers,
|
|
284
239
|
body: JSON.stringify({
|
|
@@ -298,7 +253,7 @@ function RecurProvider({ children, config: initialConfig = {} }) {
|
|
|
298
253
|
console.log("[Recur SDK] Subscription created successfully:", subscriptionResult);
|
|
299
254
|
options.onSuccess?.(subscriptionResult);
|
|
300
255
|
console.log("[Recur SDK] Step 2: Getting SDK token...");
|
|
301
|
-
const tokenResponse = await fetch(`${baseUrl}/
|
|
256
|
+
const tokenResponse = await fetch(`${baseUrl}/v1/subscriptions/sdk-token`, {
|
|
302
257
|
method: "POST",
|
|
303
258
|
headers,
|
|
304
259
|
body: JSON.stringify({
|
|
@@ -319,7 +274,7 @@ function RecurProvider({ children, config: initialConfig = {} }) {
|
|
|
319
274
|
});
|
|
320
275
|
console.log("[Recur SDK] Step 3: Loading PAYUNi SDK...");
|
|
321
276
|
const isSandbox = tokenResult.environment === "SANDBOX";
|
|
322
|
-
await loadPayUniSDK(isSandbox
|
|
277
|
+
await loadPayUniSDK(isSandbox);
|
|
323
278
|
console.log("[Recur SDK] PAYUNi SDK loaded successfully");
|
|
324
279
|
if (!window.UniPayment) {
|
|
325
280
|
console.error("[Recur SDK] window.UniPayment is not available after loading");
|
|
@@ -543,7 +498,7 @@ function RecurProvider({ children, config: initialConfig = {} }) {
|
|
|
543
498
|
console.log("[Recur SDK] Trade result received:", tradeResult);
|
|
544
499
|
console.log("[Recur SDK] Step 8: Completing subscription...");
|
|
545
500
|
const completeResponse = await fetch(
|
|
546
|
-
`${baseUrl}/
|
|
501
|
+
`${baseUrl}/v1/subscriptions/${subscriptionResult.subscription.id}/complete`,
|
|
547
502
|
{
|
|
548
503
|
method: "POST",
|
|
549
504
|
headers,
|
|
@@ -612,8 +567,8 @@ function RecurProvider({ children, config: initialConfig = {} }) {
|
|
|
612
567
|
if (!config.publishableKey) {
|
|
613
568
|
throw new Error("publishableKey is required");
|
|
614
569
|
}
|
|
615
|
-
const baseUrl = config.baseUrl ||
|
|
616
|
-
const response = await fetch(`${baseUrl}/
|
|
570
|
+
const baseUrl = config.baseUrl || "https://api.recur.tw";
|
|
571
|
+
const response = await fetch(`${baseUrl}/v1/plans`, {
|
|
617
572
|
method: "GET",
|
|
618
573
|
headers: {
|
|
619
574
|
"Content-Type": "application/json",
|
|
@@ -647,8 +602,88 @@ function useRecur() {
|
|
|
647
602
|
}
|
|
648
603
|
return context;
|
|
649
604
|
}
|
|
605
|
+
function usePlans(options = {}) {
|
|
606
|
+
const { enabled = true, onSuccess, onError } = options;
|
|
607
|
+
const { fetchPlans } = useRecur();
|
|
608
|
+
const [data, setData] = React.useState(void 0);
|
|
609
|
+
const [isLoading2, setIsLoading] = React.useState(false);
|
|
610
|
+
const [error, setError] = React.useState(null);
|
|
611
|
+
const fetchData = React.useCallback(async () => {
|
|
612
|
+
if (!enabled) return;
|
|
613
|
+
setIsLoading(true);
|
|
614
|
+
setError(null);
|
|
615
|
+
try {
|
|
616
|
+
const result = await fetchPlans();
|
|
617
|
+
setData(result.plans);
|
|
618
|
+
onSuccess?.(result.plans);
|
|
619
|
+
} catch (err) {
|
|
620
|
+
const error2 = err instanceof Error ? err : new Error("Failed to fetch plans");
|
|
621
|
+
setError(error2);
|
|
622
|
+
onError?.(error2);
|
|
623
|
+
} finally {
|
|
624
|
+
setIsLoading(false);
|
|
625
|
+
}
|
|
626
|
+
}, [enabled, fetchPlans, onSuccess, onError]);
|
|
627
|
+
React.useEffect(() => {
|
|
628
|
+
fetchData();
|
|
629
|
+
}, [fetchData]);
|
|
630
|
+
return {
|
|
631
|
+
data,
|
|
632
|
+
isLoading: isLoading2,
|
|
633
|
+
error,
|
|
634
|
+
refetch: fetchData
|
|
635
|
+
};
|
|
636
|
+
}
|
|
637
|
+
function useSubscribe(options = {}) {
|
|
638
|
+
const { onSuccess, onError, onPaymentComplete, onPaymentCancel } = options;
|
|
639
|
+
const { checkout, isCheckingOut } = useRecur();
|
|
640
|
+
const [error, setError] = React.useState(null);
|
|
641
|
+
const mutate = React.useCallback(
|
|
642
|
+
async (checkoutOptions) => {
|
|
643
|
+
setError(null);
|
|
644
|
+
try {
|
|
645
|
+
await checkout({
|
|
646
|
+
...checkoutOptions,
|
|
647
|
+
onSuccess: (result) => {
|
|
648
|
+
onSuccess?.(result);
|
|
649
|
+
},
|
|
650
|
+
onPaymentComplete: (subscription) => {
|
|
651
|
+
onPaymentComplete?.(subscription);
|
|
652
|
+
},
|
|
653
|
+
onError: (err) => {
|
|
654
|
+
setError(err);
|
|
655
|
+
onError?.(err);
|
|
656
|
+
},
|
|
657
|
+
onPaymentCancel: () => {
|
|
658
|
+
onPaymentCancel?.();
|
|
659
|
+
}
|
|
660
|
+
});
|
|
661
|
+
} catch (err) {
|
|
662
|
+
const checkoutError = {
|
|
663
|
+
code: "CHECKOUT_FAILED",
|
|
664
|
+
message: err instanceof Error ? err.message : "Unknown error occurred"
|
|
665
|
+
};
|
|
666
|
+
setError(checkoutError);
|
|
667
|
+
onError?.(checkoutError);
|
|
668
|
+
}
|
|
669
|
+
},
|
|
670
|
+
[checkout, onSuccess, onError, onPaymentComplete, onPaymentCancel]
|
|
671
|
+
);
|
|
672
|
+
const reset = React.useCallback(() => {
|
|
673
|
+
setError(null);
|
|
674
|
+
}, []);
|
|
675
|
+
return {
|
|
676
|
+
mutate,
|
|
677
|
+
subscribe: mutate,
|
|
678
|
+
isLoading: isCheckingOut,
|
|
679
|
+
error,
|
|
680
|
+
reset
|
|
681
|
+
};
|
|
682
|
+
}
|
|
650
683
|
|
|
651
684
|
exports.RecurProvider = RecurProvider;
|
|
685
|
+
exports.usePlans = usePlans;
|
|
652
686
|
exports.useRecur = useRecur;
|
|
687
|
+
exports.useSubscribe = useSubscribe;
|
|
653
688
|
//# sourceMappingURL=index.cjs.map
|
|
654
689
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/payuni-loader.ts","../src/context.tsx","../src/use-recur.ts"],"names":["createContext","useState","React","useCallback","useMemo","jsx","useContext"],"mappings":"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","file":"index.cjs","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":["createContext","useState","React","useCallback","useMemo","jsx","useContext","isLoading","error","useEffect"],"mappings":"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","file":"index.cjs","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"]}
|
package/dist/index.d.cts
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 };
|