recur-tw 0.0.1 → 0.0.2
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/README.md +162 -7
- package/dist/index.cjs +593 -48
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +103 -9
- package/dist/index.d.ts +103 -9
- package/dist/index.js +582 -41
- package/dist/index.js.map +1 -1
- package/dist/recur.umd.js +145 -0
- package/dist/recur.umd.js.map +1 -0
- package/package.json +20 -3
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/context.tsx","../src/use-recur.ts"],"names":["createContext","useState","useCallback","useMemo","jsx","useContext"],"mappings":"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","file":"index.cjs","sourcesContent":["'use client';\n\nimport React, { createContext, useCallback, useState, useMemo } from 'react';\nimport type {\n RecurConfig,\n RecurContextValue,\n CheckoutOptions,\n CheckoutResult,\n CheckoutError,\n} from './types.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={{ organizationId: 'org_xxx' }}>\n * <App />\n * </RecurProvider>\n * ```\n */\nexport function RecurProvider({ children, config: initialConfig = {} }: RecurProviderProps) {\n const [config, setConfig] = useState<RecurConfig>({\n redirectMode: 'redirect',\n ...initialConfig,\n });\n\n const [isCheckingOut, setIsCheckingOut] = useState(false);\n\n const updateConfig = useCallback((newConfig: Partial<RecurConfig>) => {\n setConfig((prev) => ({ ...prev, ...newConfig }));\n }, []);\n\n const checkout = useCallback(\n async (options: CheckoutOptions) => {\n try {\n setIsCheckingOut(true);\n\n // Validate required fields\n if (!options.planId) {\n throw new Error('planId is required');\n }\n\n // Determine organization ID\n const organizationId = options.organizationId || config.organizationId;\n if (!organizationId) {\n throw new Error('organizationId is required. Provide it in RecurProvider config or checkout options.');\n }\n\n // Determine base URL\n const baseUrl = config.baseUrl || (typeof window !== 'undefined' ? window.location.origin : '');\n\n // Call the subscription API\n const response = await fetch(`${baseUrl}/api/subscriptions`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n organizationId,\n planId: options.planId,\n customerName: options.customerName,\n customerEmail: options.customerEmail,\n customerPhone: options.customerPhone,\n }),\n });\n\n if (!response.ok) {\n const errorData = await response.json().catch(() => ({}));\n const error: CheckoutError = {\n code: errorData.error || 'CHECKOUT_FAILED',\n message: errorData.message || 'Failed to initiate checkout',\n details: errorData,\n };\n\n options.onError?.(error);\n throw new Error(error.message);\n }\n\n const result: CheckoutResult = await response.json();\n\n // Call success callback\n options.onSuccess?.(result);\n\n // Handle redirect based on mode\n if (config.redirectMode === 'popup') {\n // Open payment in popup window\n const popup = window.open(\n result.paymentUrl,\n 'recurCheckout',\n 'width=600,height=700,scrollbars=yes,resizable=yes'\n );\n\n if (!popup) {\n throw new Error('Failed to open popup. Please allow popups for this site.');\n }\n\n // Poll for popup close\n const pollInterval = setInterval(() => {\n if (popup.closed) {\n clearInterval(pollInterval);\n setIsCheckingOut(false);\n\n // Check if payment was completed\n // You might want to implement a message listener here\n // or poll your API to check subscription status\n if (options.onPaymentComplete) {\n // Note: result.subscription may not have all fields for SubscriptionResult\n // You may need to fetch full subscription details from API\n options.onPaymentComplete(result.subscription as any);\n }\n }\n }, 500);\n } else {\n // Full page redirect\n window.location.href = result.paymentUrl;\n }\n } catch (error) {\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 value = useMemo(\n () => ({\n config,\n checkout,\n isCheckingOut,\n updateConfig,\n }),\n [config, checkout, 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"],"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"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -8,6 +8,15 @@ import React from 'react';
|
|
|
8
8
|
*/
|
|
9
9
|
interface RecurConfig {
|
|
10
10
|
/**
|
|
11
|
+
* Publishable API key for authentication
|
|
12
|
+
* Get this from your organization settings
|
|
13
|
+
*
|
|
14
|
+
* @example 'pk_test_abc123...' or 'pk_live_xyz789...'
|
|
15
|
+
*/
|
|
16
|
+
publishableKey?: string;
|
|
17
|
+
/**
|
|
18
|
+
* @deprecated Use publishableKey instead for better security
|
|
19
|
+
*
|
|
11
20
|
* Organization ID for the checkout
|
|
12
21
|
* If not provided, will be inferred from the current domain
|
|
13
22
|
*/
|
|
@@ -18,11 +27,17 @@ interface RecurConfig {
|
|
|
18
27
|
*/
|
|
19
28
|
baseUrl?: string;
|
|
20
29
|
/**
|
|
21
|
-
*
|
|
22
|
-
* - '
|
|
23
|
-
* - '
|
|
30
|
+
* Checkout mode
|
|
31
|
+
* - 'embedded': Render credit card form inline (default)
|
|
32
|
+
* - 'modal': Show credit card form in modal
|
|
33
|
+
* - 'redirect': Full page redirect (legacy, deprecated)
|
|
24
34
|
*/
|
|
25
|
-
|
|
35
|
+
checkoutMode?: 'embedded' | 'modal' | 'redirect';
|
|
36
|
+
/**
|
|
37
|
+
* Container element ID for embedded checkout
|
|
38
|
+
* Required when checkoutMode is 'embedded'
|
|
39
|
+
*/
|
|
40
|
+
containerElementId?: string;
|
|
26
41
|
/**
|
|
27
42
|
* Success callback URL
|
|
28
43
|
* Where to redirect after successful payment
|
|
@@ -33,6 +48,21 @@ interface RecurConfig {
|
|
|
33
48
|
* Where to redirect if user cancels payment
|
|
34
49
|
*/
|
|
35
50
|
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;
|
|
36
66
|
}
|
|
37
67
|
interface CheckoutOptions {
|
|
38
68
|
/**
|
|
@@ -76,15 +106,26 @@ interface CheckoutResult {
|
|
|
76
106
|
id: string;
|
|
77
107
|
status: string;
|
|
78
108
|
planId: string;
|
|
109
|
+
planName: string;
|
|
79
110
|
amount: number;
|
|
80
111
|
billingPeriod: string;
|
|
81
|
-
|
|
82
|
-
|
|
112
|
+
trialDays: number;
|
|
113
|
+
};
|
|
114
|
+
/**
|
|
115
|
+
* Subscriber information
|
|
116
|
+
*/
|
|
117
|
+
subscriber: {
|
|
118
|
+
id: string;
|
|
119
|
+
email: string;
|
|
120
|
+
name: string;
|
|
83
121
|
};
|
|
84
122
|
/**
|
|
85
|
-
*
|
|
123
|
+
* Next steps in the checkout flow
|
|
86
124
|
*/
|
|
87
|
-
|
|
125
|
+
nextSteps: {
|
|
126
|
+
getSdkToken: string;
|
|
127
|
+
completeSubscription: string;
|
|
128
|
+
};
|
|
88
129
|
}
|
|
89
130
|
interface CheckoutError {
|
|
90
131
|
/**
|
|
@@ -111,6 +152,20 @@ interface SubscriptionResult {
|
|
|
111
152
|
currentPeriodEnd: string;
|
|
112
153
|
nextBillingDate?: string;
|
|
113
154
|
}
|
|
155
|
+
interface Plan {
|
|
156
|
+
id: string;
|
|
157
|
+
name: string;
|
|
158
|
+
slug: string;
|
|
159
|
+
description: string | null;
|
|
160
|
+
billingPeriod: 'WEEKLY' | 'MONTHLY' | 'YEARLY' | 'CUSTOM';
|
|
161
|
+
price: number;
|
|
162
|
+
trialDays: number;
|
|
163
|
+
features: Record<string, unknown> | null;
|
|
164
|
+
displayOrder: number;
|
|
165
|
+
}
|
|
166
|
+
interface PlansResult {
|
|
167
|
+
plans: Plan[];
|
|
168
|
+
}
|
|
114
169
|
interface RecurContextValue {
|
|
115
170
|
/**
|
|
116
171
|
* Current configuration
|
|
@@ -120,6 +175,10 @@ interface RecurContextValue {
|
|
|
120
175
|
* Initiate checkout flow
|
|
121
176
|
*/
|
|
122
177
|
checkout: (options: CheckoutOptions) => Promise<void>;
|
|
178
|
+
/**
|
|
179
|
+
* Fetch available subscription plans
|
|
180
|
+
*/
|
|
181
|
+
fetchPlans: () => Promise<PlansResult>;
|
|
123
182
|
/**
|
|
124
183
|
* Check if checkout is in progress
|
|
125
184
|
*/
|
|
@@ -129,6 +188,41 @@ interface RecurContextValue {
|
|
|
129
188
|
*/
|
|
130
189
|
updateConfig: (config: Partial<RecurConfig>) => void;
|
|
131
190
|
}
|
|
191
|
+
/**
|
|
192
|
+
* PAYUNi SDK types
|
|
193
|
+
* https://www.payuni.com.tw/
|
|
194
|
+
*/
|
|
195
|
+
declare global {
|
|
196
|
+
interface Window {
|
|
197
|
+
UniPayment?: {
|
|
198
|
+
createSession: (token: string, options: {
|
|
199
|
+
env?: 'P' | 'S';
|
|
200
|
+
useInst?: boolean;
|
|
201
|
+
elements: {
|
|
202
|
+
CardNo: string;
|
|
203
|
+
CardExp: string;
|
|
204
|
+
CardCvc: string;
|
|
205
|
+
CardTokenType?: string;
|
|
206
|
+
};
|
|
207
|
+
style?: {
|
|
208
|
+
color?: string;
|
|
209
|
+
errorColor?: string;
|
|
210
|
+
fontSize?: string;
|
|
211
|
+
fontWeight?: string;
|
|
212
|
+
lineHeight?: string;
|
|
213
|
+
};
|
|
214
|
+
}) => UniPaymentInstance;
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
interface UniPaymentInstance {
|
|
219
|
+
start: () => Promise<void>;
|
|
220
|
+
getTradeResult: (options?: {
|
|
221
|
+
useDefault?: boolean;
|
|
222
|
+
cardInst?: number;
|
|
223
|
+
}) => Promise<any>;
|
|
224
|
+
onUpdate?: (callback: (data: any) => void) => void;
|
|
225
|
+
}
|
|
132
226
|
|
|
133
227
|
interface RecurProviderProps {
|
|
134
228
|
children: React.ReactNode;
|
|
@@ -141,7 +235,7 @@ interface RecurProviderProps {
|
|
|
141
235
|
*
|
|
142
236
|
* @example
|
|
143
237
|
* ```tsx
|
|
144
|
-
* <RecurProvider config={{
|
|
238
|
+
* <RecurProvider config={{ publishableKey: 'pk_test_xxx' }}>
|
|
145
239
|
* <App />
|
|
146
240
|
* </RecurProvider>
|
|
147
241
|
* ```
|
package/dist/index.d.ts
CHANGED
|
@@ -8,6 +8,15 @@ import React from 'react';
|
|
|
8
8
|
*/
|
|
9
9
|
interface RecurConfig {
|
|
10
10
|
/**
|
|
11
|
+
* Publishable API key for authentication
|
|
12
|
+
* Get this from your organization settings
|
|
13
|
+
*
|
|
14
|
+
* @example 'pk_test_abc123...' or 'pk_live_xyz789...'
|
|
15
|
+
*/
|
|
16
|
+
publishableKey?: string;
|
|
17
|
+
/**
|
|
18
|
+
* @deprecated Use publishableKey instead for better security
|
|
19
|
+
*
|
|
11
20
|
* Organization ID for the checkout
|
|
12
21
|
* If not provided, will be inferred from the current domain
|
|
13
22
|
*/
|
|
@@ -18,11 +27,17 @@ interface RecurConfig {
|
|
|
18
27
|
*/
|
|
19
28
|
baseUrl?: string;
|
|
20
29
|
/**
|
|
21
|
-
*
|
|
22
|
-
* - '
|
|
23
|
-
* - '
|
|
30
|
+
* Checkout mode
|
|
31
|
+
* - 'embedded': Render credit card form inline (default)
|
|
32
|
+
* - 'modal': Show credit card form in modal
|
|
33
|
+
* - 'redirect': Full page redirect (legacy, deprecated)
|
|
24
34
|
*/
|
|
25
|
-
|
|
35
|
+
checkoutMode?: 'embedded' | 'modal' | 'redirect';
|
|
36
|
+
/**
|
|
37
|
+
* Container element ID for embedded checkout
|
|
38
|
+
* Required when checkoutMode is 'embedded'
|
|
39
|
+
*/
|
|
40
|
+
containerElementId?: string;
|
|
26
41
|
/**
|
|
27
42
|
* Success callback URL
|
|
28
43
|
* Where to redirect after successful payment
|
|
@@ -33,6 +48,21 @@ interface RecurConfig {
|
|
|
33
48
|
* Where to redirect if user cancels payment
|
|
34
49
|
*/
|
|
35
50
|
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;
|
|
36
66
|
}
|
|
37
67
|
interface CheckoutOptions {
|
|
38
68
|
/**
|
|
@@ -76,15 +106,26 @@ interface CheckoutResult {
|
|
|
76
106
|
id: string;
|
|
77
107
|
status: string;
|
|
78
108
|
planId: string;
|
|
109
|
+
planName: string;
|
|
79
110
|
amount: number;
|
|
80
111
|
billingPeriod: string;
|
|
81
|
-
|
|
82
|
-
|
|
112
|
+
trialDays: number;
|
|
113
|
+
};
|
|
114
|
+
/**
|
|
115
|
+
* Subscriber information
|
|
116
|
+
*/
|
|
117
|
+
subscriber: {
|
|
118
|
+
id: string;
|
|
119
|
+
email: string;
|
|
120
|
+
name: string;
|
|
83
121
|
};
|
|
84
122
|
/**
|
|
85
|
-
*
|
|
123
|
+
* Next steps in the checkout flow
|
|
86
124
|
*/
|
|
87
|
-
|
|
125
|
+
nextSteps: {
|
|
126
|
+
getSdkToken: string;
|
|
127
|
+
completeSubscription: string;
|
|
128
|
+
};
|
|
88
129
|
}
|
|
89
130
|
interface CheckoutError {
|
|
90
131
|
/**
|
|
@@ -111,6 +152,20 @@ interface SubscriptionResult {
|
|
|
111
152
|
currentPeriodEnd: string;
|
|
112
153
|
nextBillingDate?: string;
|
|
113
154
|
}
|
|
155
|
+
interface Plan {
|
|
156
|
+
id: string;
|
|
157
|
+
name: string;
|
|
158
|
+
slug: string;
|
|
159
|
+
description: string | null;
|
|
160
|
+
billingPeriod: 'WEEKLY' | 'MONTHLY' | 'YEARLY' | 'CUSTOM';
|
|
161
|
+
price: number;
|
|
162
|
+
trialDays: number;
|
|
163
|
+
features: Record<string, unknown> | null;
|
|
164
|
+
displayOrder: number;
|
|
165
|
+
}
|
|
166
|
+
interface PlansResult {
|
|
167
|
+
plans: Plan[];
|
|
168
|
+
}
|
|
114
169
|
interface RecurContextValue {
|
|
115
170
|
/**
|
|
116
171
|
* Current configuration
|
|
@@ -120,6 +175,10 @@ interface RecurContextValue {
|
|
|
120
175
|
* Initiate checkout flow
|
|
121
176
|
*/
|
|
122
177
|
checkout: (options: CheckoutOptions) => Promise<void>;
|
|
178
|
+
/**
|
|
179
|
+
* Fetch available subscription plans
|
|
180
|
+
*/
|
|
181
|
+
fetchPlans: () => Promise<PlansResult>;
|
|
123
182
|
/**
|
|
124
183
|
* Check if checkout is in progress
|
|
125
184
|
*/
|
|
@@ -129,6 +188,41 @@ interface RecurContextValue {
|
|
|
129
188
|
*/
|
|
130
189
|
updateConfig: (config: Partial<RecurConfig>) => void;
|
|
131
190
|
}
|
|
191
|
+
/**
|
|
192
|
+
* PAYUNi SDK types
|
|
193
|
+
* https://www.payuni.com.tw/
|
|
194
|
+
*/
|
|
195
|
+
declare global {
|
|
196
|
+
interface Window {
|
|
197
|
+
UniPayment?: {
|
|
198
|
+
createSession: (token: string, options: {
|
|
199
|
+
env?: 'P' | 'S';
|
|
200
|
+
useInst?: boolean;
|
|
201
|
+
elements: {
|
|
202
|
+
CardNo: string;
|
|
203
|
+
CardExp: string;
|
|
204
|
+
CardCvc: string;
|
|
205
|
+
CardTokenType?: string;
|
|
206
|
+
};
|
|
207
|
+
style?: {
|
|
208
|
+
color?: string;
|
|
209
|
+
errorColor?: string;
|
|
210
|
+
fontSize?: string;
|
|
211
|
+
fontWeight?: string;
|
|
212
|
+
lineHeight?: string;
|
|
213
|
+
};
|
|
214
|
+
}) => UniPaymentInstance;
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
interface UniPaymentInstance {
|
|
219
|
+
start: () => Promise<void>;
|
|
220
|
+
getTradeResult: (options?: {
|
|
221
|
+
useDefault?: boolean;
|
|
222
|
+
cardInst?: number;
|
|
223
|
+
}) => Promise<any>;
|
|
224
|
+
onUpdate?: (callback: (data: any) => void) => void;
|
|
225
|
+
}
|
|
132
226
|
|
|
133
227
|
interface RecurProviderProps {
|
|
134
228
|
children: React.ReactNode;
|
|
@@ -141,7 +235,7 @@ interface RecurProviderProps {
|
|
|
141
235
|
*
|
|
142
236
|
* @example
|
|
143
237
|
* ```tsx
|
|
144
|
-
* <RecurProvider config={{
|
|
238
|
+
* <RecurProvider config={{ publishableKey: 'pk_test_xxx' }}>
|
|
145
239
|
* <App />
|
|
146
240
|
* </RecurProvider>
|
|
147
241
|
* ```
|