recur-tw 0.4.4 β 0.6.1
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 +84 -49
- package/dist/index.cjs +41 -19
- package/dist/index.d.cts +52 -24
- package/dist/index.d.ts +52 -24
- package/dist/index.js +40 -19
- package/dist/recur.umd.js +15 -15
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@ Taiwan-specific subscription checkout SDK with embedded payment forms powered by
|
|
|
9
9
|
## π Features
|
|
10
10
|
|
|
11
11
|
- β
**Hosted Checkout** - Zero-backend integration with `<recur-checkout>` Web Component
|
|
12
|
-
- β
**React SDK** - Full React integration with hooks (`
|
|
12
|
+
- β
**React SDK** - Full React integration with hooks (`useProducts`, `useRecur`)
|
|
13
13
|
- β
**Vanilla JS** - Use with plain HTML/JavaScript (no framework required)
|
|
14
14
|
- β
**Embedded Checkout** - Native payment forms with PAYUNi credit card fields
|
|
15
15
|
- β
**Web Components** - Modern, encapsulated UI components
|
|
@@ -53,7 +53,7 @@ Full framework integration with React hooks and embedded checkout:
|
|
|
53
53
|
```tsx
|
|
54
54
|
'use client';
|
|
55
55
|
|
|
56
|
-
import { RecurProvider,
|
|
56
|
+
import { RecurProvider, useProducts, useRecur } from 'recur-tw';
|
|
57
57
|
|
|
58
58
|
// 1. Wrap your app with RecurProvider
|
|
59
59
|
export default function App() {
|
|
@@ -64,31 +64,33 @@ export default function App() {
|
|
|
64
64
|
containerElementId: 'recur-payment-container' // For embedded checkout
|
|
65
65
|
}}
|
|
66
66
|
>
|
|
67
|
-
<
|
|
67
|
+
<ProductsPage />
|
|
68
68
|
</RecurProvider>
|
|
69
69
|
);
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
-
// 2. Fetch
|
|
73
|
-
function
|
|
74
|
-
|
|
72
|
+
// 2. Fetch products and checkout
|
|
73
|
+
function ProductsPage() {
|
|
74
|
+
// Fetch all products (or filter by type: 'SUBSCRIPTION', 'ONE_TIME', etc.)
|
|
75
|
+
const { data: products, isLoading } = useProducts();
|
|
75
76
|
const { checkout, isCheckingOut } = useRecur();
|
|
76
77
|
|
|
77
|
-
if (isLoading) return <div>Loading
|
|
78
|
+
if (isLoading) return <div>Loading products...</div>;
|
|
78
79
|
|
|
79
80
|
return (
|
|
80
81
|
<div>
|
|
81
|
-
{
|
|
82
|
+
{products?.map((product) => (
|
|
82
83
|
<button
|
|
83
|
-
key={
|
|
84
|
+
key={product.id}
|
|
84
85
|
onClick={() => checkout({
|
|
85
|
-
planId:
|
|
86
|
+
planId: product.id,
|
|
86
87
|
customerEmail: 'user@example.com',
|
|
87
88
|
customerName: 'John Doe',
|
|
88
89
|
})}
|
|
89
90
|
disabled={isCheckingOut}
|
|
90
91
|
>
|
|
91
|
-
|
|
92
|
+
{/* product.type: 'SUBSCRIPTION' | 'ONE_TIME' | 'CREDITS' | 'DONATION' */}
|
|
93
|
+
{product.type === 'SUBSCRIPTION' ? 'Subscribe to' : 'Buy'} {product.name} - NT${product.price}
|
|
92
94
|
</button>
|
|
93
95
|
))}
|
|
94
96
|
|
|
@@ -301,26 +303,34 @@ document.querySelector('recur-checkout').addEventListener('checkout-error', (e)
|
|
|
301
303
|
|
|
302
304
|
### React Hooks
|
|
303
305
|
|
|
304
|
-
#### `
|
|
306
|
+
#### `useProducts()`
|
|
305
307
|
|
|
306
|
-
Fetch available
|
|
308
|
+
Fetch available products with optional type filtering:
|
|
307
309
|
|
|
308
310
|
```tsx
|
|
309
|
-
import {
|
|
311
|
+
import { useProducts } from 'recur-tw';
|
|
310
312
|
|
|
311
|
-
function
|
|
312
|
-
|
|
313
|
+
function ProductsPage() {
|
|
314
|
+
// Fetch all products
|
|
315
|
+
const { data: products, isLoading, error } = useProducts();
|
|
316
|
+
|
|
317
|
+
// Or filter by type
|
|
318
|
+
// const { data: subscriptions } = useProducts({ type: 'SUBSCRIPTION' });
|
|
319
|
+
// const { data: oneTimeProducts } = useProducts({ type: 'ONE_TIME' });
|
|
313
320
|
|
|
314
321
|
if (isLoading) return <div>Loading...</div>;
|
|
315
322
|
if (error) return <div>Error: {error.message}</div>;
|
|
316
323
|
|
|
317
324
|
return (
|
|
318
325
|
<div>
|
|
319
|
-
{
|
|
320
|
-
<div key={
|
|
321
|
-
<
|
|
322
|
-
|
|
323
|
-
|
|
326
|
+
{products?.map((product) => (
|
|
327
|
+
<div key={product.id}>
|
|
328
|
+
<span className="badge">
|
|
329
|
+
{product.type === 'SUBSCRIPTION' ? 'θ¨ι±' : 'δΈζ¬‘ζ§'}
|
|
330
|
+
</span>
|
|
331
|
+
<h3>{product.name}</h3>
|
|
332
|
+
<p>NT${product.price} {product.billingPeriod && `/ ${product.billingPeriod}`}</p>
|
|
333
|
+
{product.trialDays && product.trialDays > 0 && <p>π {product.trialDays} days free trial</p>}
|
|
324
334
|
</div>
|
|
325
335
|
))}
|
|
326
336
|
</div>
|
|
@@ -328,6 +338,12 @@ function PlansPage() {
|
|
|
328
338
|
}
|
|
329
339
|
```
|
|
330
340
|
|
|
341
|
+
**Product Types:**
|
|
342
|
+
- `SUBSCRIPTION` - Recurring subscription products
|
|
343
|
+
- `ONE_TIME` - One-time purchase products
|
|
344
|
+
- `CREDITS` - Credit/token packages
|
|
345
|
+
- `DONATION` - Donation products
|
|
346
|
+
|
|
331
347
|
#### `useRecur()`
|
|
332
348
|
|
|
333
349
|
Access checkout functionality:
|
|
@@ -354,7 +370,8 @@ function CheckoutButton({ planId }: { planId: string }) {
|
|
|
354
370
|
- `isCheckingOut` - Boolean indicating if checkout is in progress
|
|
355
371
|
- `config` - Current SDK configuration
|
|
356
372
|
- `updateConfig(newConfig)` - Update configuration dynamically
|
|
357
|
-
- `
|
|
373
|
+
- `fetchProducts(options)` - Manually fetch products (optionally filter by type)
|
|
374
|
+
- `fetchPlans()` - Fetch subscription products only (backward compat)
|
|
358
375
|
|
|
359
376
|
---
|
|
360
377
|
|
|
@@ -581,24 +598,25 @@ export function CheckoutForm({ planId }: { planId: string }) {
|
|
|
581
598
|
}
|
|
582
599
|
```
|
|
583
600
|
|
|
584
|
-
#### Dynamic
|
|
601
|
+
#### Dynamic Product Selection
|
|
585
602
|
|
|
586
603
|
```tsx
|
|
587
604
|
'use client';
|
|
588
605
|
|
|
589
606
|
import { useState } from 'react';
|
|
590
|
-
import {
|
|
607
|
+
import { useProducts, useRecur } from 'recur-tw';
|
|
591
608
|
|
|
592
609
|
export function PricingTable() {
|
|
593
|
-
|
|
610
|
+
// Filter to show only subscription products
|
|
611
|
+
const { data: products } = useProducts({ type: 'SUBSCRIPTION' });
|
|
594
612
|
const { checkout, isCheckingOut } = useRecur();
|
|
595
|
-
const [
|
|
613
|
+
const [selectedProduct, setSelectedProduct] = useState<string | null>(null);
|
|
596
614
|
|
|
597
|
-
const
|
|
598
|
-
|
|
615
|
+
const handleSelectProduct = async (productId: string) => {
|
|
616
|
+
setSelectedProduct(productId);
|
|
599
617
|
|
|
600
618
|
await checkout({
|
|
601
|
-
planId,
|
|
619
|
+
planId: productId,
|
|
602
620
|
customerEmail: 'user@example.com',
|
|
603
621
|
customerName: 'John Doe',
|
|
604
622
|
});
|
|
@@ -606,18 +624,18 @@ export function PricingTable() {
|
|
|
606
624
|
|
|
607
625
|
return (
|
|
608
626
|
<div>
|
|
609
|
-
{!
|
|
610
|
-
//
|
|
611
|
-
<div className="
|
|
612
|
-
{
|
|
613
|
-
<div key={
|
|
614
|
-
<h3>{
|
|
615
|
-
<p className="price">NT${
|
|
616
|
-
{
|
|
617
|
-
<p className="trial">π {
|
|
627
|
+
{!selectedProduct ? (
|
|
628
|
+
// Product selection
|
|
629
|
+
<div className="products-grid">
|
|
630
|
+
{products?.map((product) => (
|
|
631
|
+
<div key={product.id} className="product-card">
|
|
632
|
+
<h3>{product.name}</h3>
|
|
633
|
+
<p className="price">NT${product.price} / {product.billingPeriod}</p>
|
|
634
|
+
{product.trialDays && product.trialDays > 0 && (
|
|
635
|
+
<p className="trial">π {product.trialDays} day free trial</p>
|
|
618
636
|
)}
|
|
619
|
-
<button onClick={() =>
|
|
620
|
-
Select
|
|
637
|
+
<button onClick={() => handleSelectProduct(product.id)}>
|
|
638
|
+
Select Product
|
|
621
639
|
</button>
|
|
622
640
|
</div>
|
|
623
641
|
))}
|
|
@@ -625,7 +643,7 @@ export function PricingTable() {
|
|
|
625
643
|
) : (
|
|
626
644
|
// Payment form
|
|
627
645
|
<div>
|
|
628
|
-
<button onClick={() =>
|
|
646
|
+
<button onClick={() => setSelectedProduct(null)}>β Back to products</button>
|
|
629
647
|
<div id="recur-payment-container"></div>
|
|
630
648
|
</div>
|
|
631
649
|
)}
|
|
@@ -700,26 +718,43 @@ import type {
|
|
|
700
718
|
CheckoutOptions,
|
|
701
719
|
CheckoutResult,
|
|
702
720
|
CheckoutError,
|
|
703
|
-
|
|
704
|
-
|
|
721
|
+
Product,
|
|
722
|
+
ProductsResult,
|
|
723
|
+
FetchProductsOptions,
|
|
705
724
|
Subscription,
|
|
706
725
|
SubscriptionResult,
|
|
726
|
+
// Backward compatibility aliases
|
|
727
|
+
Plan, // = Product
|
|
728
|
+
PlansResult, // = ProductsResult
|
|
707
729
|
} from 'recur-tw';
|
|
708
730
|
```
|
|
709
731
|
|
|
710
732
|
### Type Definitions
|
|
711
733
|
|
|
712
734
|
```tsx
|
|
713
|
-
interface
|
|
735
|
+
interface Product {
|
|
714
736
|
id: string;
|
|
715
737
|
name: string;
|
|
738
|
+
slug: string;
|
|
739
|
+
description: string | null;
|
|
740
|
+
// Product type distinguishes between recurring and one-time purchases
|
|
741
|
+
type: 'SUBSCRIPTION' | 'ONE_TIME' | 'CREDITS' | 'DONATION';
|
|
742
|
+
billingPeriod: 'WEEKLY' | 'MONTHLY' | 'YEARLY' | 'ONE_TIME' | 'CUSTOM' | null;
|
|
716
743
|
price: number;
|
|
717
|
-
|
|
718
|
-
trialDays: number;
|
|
719
|
-
|
|
720
|
-
|
|
744
|
+
currency: string;
|
|
745
|
+
trialDays: number | null;
|
|
746
|
+
metadata: ProductMetadata | null;
|
|
747
|
+
productFamily?: string | null;
|
|
748
|
+
displayOrder: number;
|
|
721
749
|
}
|
|
722
750
|
|
|
751
|
+
interface FetchProductsOptions {
|
|
752
|
+
type?: 'SUBSCRIPTION' | 'ONE_TIME' | 'CREDITS' | 'DONATION';
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
// Backward compatibility
|
|
756
|
+
type Plan = Product;
|
|
757
|
+
|
|
723
758
|
interface Subscription {
|
|
724
759
|
id: string;
|
|
725
760
|
status: string;
|
|
@@ -819,7 +854,7 @@ POST /v1/checkouts/:id/pay β ε·θ‘δ»ζ¬Ύ
|
|
|
819
854
|
**Breaking Changes:**
|
|
820
855
|
- `organizationId` β `publishableKey` in config
|
|
821
856
|
- Checkout now uses Web Components instead of innerHTML
|
|
822
|
-
- New `
|
|
857
|
+
- New `useProducts()` hook for fetching products (replaces `usePlans()`)
|
|
823
858
|
- Embedded mode is now the default when `containerElementId` is set
|
|
824
859
|
|
|
825
860
|
**Migration:**
|
package/dist/index.cjs
CHANGED
|
@@ -2598,13 +2598,22 @@ function RecurProvider({ children, config: initialConfig = {} }) {
|
|
|
2598
2598
|
const tradeResult = await paymentSession.getTradeResult();
|
|
2599
2599
|
console.log("[Recur SDK] Trade result received:", tradeResult);
|
|
2600
2600
|
console.log("[Recur SDK] Step 8: Executing payment...");
|
|
2601
|
-
const creditToken = tradeResult.EncryptInfo || tradeResult.creditToken;
|
|
2602
2601
|
const timestamp = tradeResult.HashTimestamp || tradeResult.timestamp;
|
|
2603
|
-
|
|
2604
|
-
|
|
2605
|
-
|
|
2602
|
+
let paymentBody = {};
|
|
2603
|
+
if (checkoutResult.checkout.productType === "SUBSCRIPTION") {
|
|
2604
|
+
const subscriptionCreditToken = checkoutResult.creditToken;
|
|
2605
|
+
const subscriptionTimestamp = checkoutResult.sdkTimestamp;
|
|
2606
|
+
if (!subscriptionCreditToken) {
|
|
2607
|
+
console.error("[Recur SDK] Missing creditToken from checkout for subscription");
|
|
2608
|
+
throw new Error("Missing creditToken for subscription payment");
|
|
2609
|
+
}
|
|
2610
|
+
paymentBody = {
|
|
2611
|
+
creditToken: subscriptionCreditToken,
|
|
2612
|
+
timestamp: subscriptionTimestamp || timestamp
|
|
2613
|
+
};
|
|
2614
|
+
console.log("[Recur SDK] Using creditToken from checkout:", subscriptionCreditToken.substring(0, 30) + "...");
|
|
2615
|
+
console.log("[Recur SDK] Using timestamp:", subscriptionTimestamp ? "from checkout (sdkTimestamp)" : "from tradeResult");
|
|
2606
2616
|
}
|
|
2607
|
-
const paymentBody = checkoutResult.checkout.productType === "SUBSCRIPTION" ? { creditToken, timestamp } : {};
|
|
2608
2617
|
const paymentResponse = await fetch(
|
|
2609
2618
|
`${baseUrl}/v1/checkouts/${checkoutResult.checkout.id}/pay`,
|
|
2610
2619
|
{
|
|
@@ -2686,13 +2695,17 @@ function RecurProvider({ children, config: initialConfig = {} }) {
|
|
|
2686
2695
|
},
|
|
2687
2696
|
[config]
|
|
2688
2697
|
);
|
|
2689
|
-
const
|
|
2690
|
-
async () => {
|
|
2698
|
+
const fetchProducts = React.useCallback(
|
|
2699
|
+
async (options) => {
|
|
2691
2700
|
if (!config.publishableKey) {
|
|
2692
2701
|
throw new Error("publishableKey is required");
|
|
2693
2702
|
}
|
|
2694
2703
|
const baseUrl = config.baseUrl || "https://api.recur.tw";
|
|
2695
|
-
const
|
|
2704
|
+
const url = new URL(`${baseUrl}/v1/products`);
|
|
2705
|
+
if (options?.type) {
|
|
2706
|
+
url.searchParams.set("type", options.type);
|
|
2707
|
+
}
|
|
2708
|
+
const response = await fetch(url.toString(), {
|
|
2696
2709
|
method: "GET",
|
|
2697
2710
|
headers: {
|
|
2698
2711
|
"Content-Type": "application/json",
|
|
@@ -2701,21 +2714,29 @@ function RecurProvider({ children, config: initialConfig = {} }) {
|
|
|
2701
2714
|
});
|
|
2702
2715
|
if (!response.ok) {
|
|
2703
2716
|
const errorData = await response.json().catch(() => ({}));
|
|
2704
|
-
throw new Error(errorData.error || "Failed to fetch
|
|
2717
|
+
throw new Error(errorData.error || "Failed to fetch products");
|
|
2705
2718
|
}
|
|
2706
2719
|
return await response.json();
|
|
2707
2720
|
},
|
|
2708
2721
|
[config]
|
|
2709
2722
|
);
|
|
2723
|
+
const fetchPlans = React.useCallback(
|
|
2724
|
+
async () => {
|
|
2725
|
+
const result = await fetchProducts({ type: "SUBSCRIPTION" });
|
|
2726
|
+
return { plans: result.products };
|
|
2727
|
+
},
|
|
2728
|
+
[fetchProducts]
|
|
2729
|
+
);
|
|
2710
2730
|
const value = React.useMemo(
|
|
2711
2731
|
() => ({
|
|
2712
2732
|
config,
|
|
2713
2733
|
checkout,
|
|
2734
|
+
fetchProducts,
|
|
2714
2735
|
fetchPlans,
|
|
2715
2736
|
isCheckingOut,
|
|
2716
2737
|
updateConfig
|
|
2717
2738
|
}),
|
|
2718
|
-
[config, checkout, fetchPlans, isCheckingOut, updateConfig]
|
|
2739
|
+
[config, checkout, fetchProducts, fetchPlans, isCheckingOut, updateConfig]
|
|
2719
2740
|
);
|
|
2720
2741
|
return /* @__PURE__ */ jsxRuntime.jsx(RecurContext.Provider, { value, children });
|
|
2721
2742
|
}
|
|
@@ -2726,9 +2747,9 @@ function useRecur() {
|
|
|
2726
2747
|
}
|
|
2727
2748
|
return context;
|
|
2728
2749
|
}
|
|
2729
|
-
function
|
|
2730
|
-
const { enabled = true, onSuccess, onError } = options;
|
|
2731
|
-
const {
|
|
2750
|
+
function useProducts(options = {}) {
|
|
2751
|
+
const { enabled = true, type, onSuccess, onError } = options;
|
|
2752
|
+
const { fetchProducts } = useRecur();
|
|
2732
2753
|
const [data, setData] = React.useState(void 0);
|
|
2733
2754
|
const [isLoading2, setIsLoading] = React.useState(false);
|
|
2734
2755
|
const [error, setError] = React.useState(null);
|
|
@@ -2737,17 +2758,17 @@ function usePlans(options = {}) {
|
|
|
2737
2758
|
setIsLoading(true);
|
|
2738
2759
|
setError(null);
|
|
2739
2760
|
try {
|
|
2740
|
-
const result = await
|
|
2741
|
-
setData(result.
|
|
2742
|
-
onSuccess?.(result.
|
|
2761
|
+
const result = await fetchProducts({ type });
|
|
2762
|
+
setData(result.products);
|
|
2763
|
+
onSuccess?.(result.products);
|
|
2743
2764
|
} catch (err) {
|
|
2744
|
-
const error2 = err instanceof Error ? err : new Error("Failed to fetch
|
|
2765
|
+
const error2 = err instanceof Error ? err : new Error("Failed to fetch products");
|
|
2745
2766
|
setError(error2);
|
|
2746
2767
|
onError?.(error2);
|
|
2747
2768
|
} finally {
|
|
2748
2769
|
setIsLoading(false);
|
|
2749
2770
|
}
|
|
2750
|
-
}, [enabled,
|
|
2771
|
+
}, [enabled, type, fetchProducts, onSuccess, onError]);
|
|
2751
2772
|
React.useEffect(() => {
|
|
2752
2773
|
fetchData();
|
|
2753
2774
|
}, [fetchData]);
|
|
@@ -2806,6 +2827,7 @@ function useSubscribe(options = {}) {
|
|
|
2806
2827
|
}
|
|
2807
2828
|
|
|
2808
2829
|
exports.RecurProvider = RecurProvider;
|
|
2809
|
-
exports.usePlans =
|
|
2830
|
+
exports.usePlans = useProducts;
|
|
2831
|
+
exports.useProducts = useProducts;
|
|
2810
2832
|
exports.useRecur = useRecur;
|
|
2811
2833
|
exports.useSubscribe = useSubscribe;
|
package/dist/index.d.cts
CHANGED
|
@@ -325,24 +325,34 @@ interface ProductMetadata {
|
|
|
325
325
|
/** Whether this is the primary/default variant */
|
|
326
326
|
isMainVariant?: boolean;
|
|
327
327
|
}
|
|
328
|
-
interface
|
|
328
|
+
interface Product {
|
|
329
329
|
id: string;
|
|
330
330
|
name: string;
|
|
331
331
|
slug: string;
|
|
332
332
|
description: string | null;
|
|
333
|
-
|
|
333
|
+
type: 'SUBSCRIPTION' | 'ONE_TIME' | 'CREDITS' | 'DONATION';
|
|
334
|
+
billingPeriod: 'WEEKLY' | 'MONTHLY' | 'YEARLY' | 'ONE_TIME' | 'CUSTOM' | null;
|
|
334
335
|
price: number;
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
features: Record<string, unknown> | null;
|
|
336
|
+
currency: string;
|
|
337
|
+
trialDays: number | null;
|
|
338
338
|
/** System metadata for grouping and organization */
|
|
339
339
|
metadata: ProductMetadata | null;
|
|
340
340
|
/** Convenience field: extracted from metadata.productFamily for easier access */
|
|
341
341
|
productFamily?: string | null;
|
|
342
342
|
displayOrder: number;
|
|
343
343
|
}
|
|
344
|
+
interface ProductsResult {
|
|
345
|
+
products: Product[];
|
|
346
|
+
}
|
|
347
|
+
type Plan = Product;
|
|
344
348
|
interface PlansResult {
|
|
345
|
-
plans:
|
|
349
|
+
plans: Product[];
|
|
350
|
+
}
|
|
351
|
+
interface FetchProductsOptions {
|
|
352
|
+
/**
|
|
353
|
+
* Filter by product type
|
|
354
|
+
*/
|
|
355
|
+
type?: 'SUBSCRIPTION' | 'ONE_TIME' | 'CREDITS' | 'DONATION';
|
|
346
356
|
}
|
|
347
357
|
interface RecurContextValue {
|
|
348
358
|
/**
|
|
@@ -354,7 +364,12 @@ interface RecurContextValue {
|
|
|
354
364
|
*/
|
|
355
365
|
checkout: (options: CheckoutOptions) => Promise<void>;
|
|
356
366
|
/**
|
|
357
|
-
* Fetch available
|
|
367
|
+
* Fetch available products
|
|
368
|
+
*/
|
|
369
|
+
fetchProducts: (options?: FetchProductsOptions) => Promise<ProductsResult>;
|
|
370
|
+
/**
|
|
371
|
+
* @deprecated Use fetchProducts instead
|
|
372
|
+
* Fetch available subscription plans (backward compatibility)
|
|
358
373
|
*/
|
|
359
374
|
fetchPlans: () => Promise<PlansResult>;
|
|
360
375
|
/**
|
|
@@ -558,28 +573,32 @@ declare function RecurProvider({ children, config: initialConfig }: RecurProvide
|
|
|
558
573
|
*/
|
|
559
574
|
declare function useRecur(): RecurContextValue;
|
|
560
575
|
|
|
561
|
-
interface
|
|
576
|
+
interface UseProductsOptions {
|
|
562
577
|
/**
|
|
563
|
-
* Whether to fetch
|
|
578
|
+
* Whether to fetch products immediately on mount
|
|
564
579
|
* @default true
|
|
565
580
|
*/
|
|
566
581
|
enabled?: boolean;
|
|
567
582
|
/**
|
|
568
|
-
*
|
|
583
|
+
* Filter by product type
|
|
569
584
|
*/
|
|
570
|
-
|
|
585
|
+
type?: 'SUBSCRIPTION' | 'ONE_TIME' | 'CREDITS' | 'DONATION';
|
|
586
|
+
/**
|
|
587
|
+
* Callback when products are fetched successfully
|
|
588
|
+
*/
|
|
589
|
+
onSuccess?: (products: Product[]) => void;
|
|
571
590
|
/**
|
|
572
591
|
* Callback when an error occurs
|
|
573
592
|
*/
|
|
574
593
|
onError?: (error: Error) => void;
|
|
575
594
|
}
|
|
576
|
-
interface
|
|
595
|
+
interface UseProductsResult {
|
|
577
596
|
/**
|
|
578
|
-
* The fetched
|
|
597
|
+
* The fetched products data
|
|
579
598
|
*/
|
|
580
|
-
data:
|
|
599
|
+
data: Product[] | undefined;
|
|
581
600
|
/**
|
|
582
|
-
* Whether the
|
|
601
|
+
* Whether the products are currently being fetched
|
|
583
602
|
*/
|
|
584
603
|
isLoading: boolean;
|
|
585
604
|
/**
|
|
@@ -592,29 +611,38 @@ interface UsePlansResult {
|
|
|
592
611
|
refetch: () => Promise<void>;
|
|
593
612
|
}
|
|
594
613
|
/**
|
|
595
|
-
*
|
|
614
|
+
* useProducts Hook
|
|
596
615
|
*
|
|
597
|
-
* Fetch available
|
|
616
|
+
* Fetch available products with automatic state management
|
|
598
617
|
*
|
|
599
618
|
* @example
|
|
600
619
|
* ```tsx
|
|
601
|
-
* function
|
|
602
|
-
* const { data:
|
|
620
|
+
* function ProductsPage() {
|
|
621
|
+
* const { data: products, isLoading, error } = useProducts();
|
|
603
622
|
*
|
|
604
|
-
* if (isLoading) return <
|
|
623
|
+
* if (isLoading) return <ProductsSkeletonLoader />;
|
|
605
624
|
* if (error) return <ErrorMessage error={error} />;
|
|
606
625
|
*
|
|
607
626
|
* return (
|
|
608
627
|
* <div>
|
|
609
|
-
* {
|
|
610
|
-
* <
|
|
628
|
+
* {products?.map(product => (
|
|
629
|
+
* <ProductCard key={product.id} product={product} />
|
|
611
630
|
* ))}
|
|
612
631
|
* </div>
|
|
613
632
|
* );
|
|
614
633
|
* }
|
|
615
634
|
* ```
|
|
635
|
+
*
|
|
636
|
+
* @example Filter by type
|
|
637
|
+
* ```tsx
|
|
638
|
+
* // Only fetch subscription products
|
|
639
|
+
* const { data: subscriptions } = useProducts({ type: 'SUBSCRIPTION' });
|
|
640
|
+
*
|
|
641
|
+
* // Only fetch one-time products
|
|
642
|
+
* const { data: oneTimeProducts } = useProducts({ type: 'ONE_TIME' });
|
|
643
|
+
* ```
|
|
616
644
|
*/
|
|
617
|
-
declare function
|
|
645
|
+
declare function useProducts(options?: UseProductsOptions): UseProductsResult;
|
|
618
646
|
|
|
619
647
|
interface UseSubscribeOptions {
|
|
620
648
|
/**
|
|
@@ -696,4 +724,4 @@ interface UseSubscribeResult {
|
|
|
696
724
|
*/
|
|
697
725
|
declare function useSubscribe(options?: UseSubscribeOptions): UseSubscribeResult;
|
|
698
726
|
|
|
699
|
-
export { type CheckoutError, type CheckoutOptions, type CheckoutResult, type Plan, type PlansResult, type ProductMetadata, type RecurConfig, type RecurContextValue, RecurProvider, type RecurProviderProps, type SubscriptionResult, type UsePlansOptions, type UsePlansResult, type UseSubscribeOptions, type UseSubscribeResult, usePlans, useRecur, useSubscribe };
|
|
727
|
+
export { type CheckoutError, type CheckoutOptions, type CheckoutResult, type Plan, type PlansResult, type Product, type ProductMetadata, type ProductsResult, type RecurConfig, type RecurContextValue, RecurProvider, type RecurProviderProps, type SubscriptionResult, type UseProductsOptions as UsePlansOptions, type UseProductsResult as UsePlansResult, type UseProductsOptions, type UseProductsResult, type UseSubscribeOptions, type UseSubscribeResult, useProducts as usePlans, useProducts, useRecur, useSubscribe };
|
package/dist/index.d.ts
CHANGED
|
@@ -325,24 +325,34 @@ interface ProductMetadata {
|
|
|
325
325
|
/** Whether this is the primary/default variant */
|
|
326
326
|
isMainVariant?: boolean;
|
|
327
327
|
}
|
|
328
|
-
interface
|
|
328
|
+
interface Product {
|
|
329
329
|
id: string;
|
|
330
330
|
name: string;
|
|
331
331
|
slug: string;
|
|
332
332
|
description: string | null;
|
|
333
|
-
|
|
333
|
+
type: 'SUBSCRIPTION' | 'ONE_TIME' | 'CREDITS' | 'DONATION';
|
|
334
|
+
billingPeriod: 'WEEKLY' | 'MONTHLY' | 'YEARLY' | 'ONE_TIME' | 'CUSTOM' | null;
|
|
334
335
|
price: number;
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
features: Record<string, unknown> | null;
|
|
336
|
+
currency: string;
|
|
337
|
+
trialDays: number | null;
|
|
338
338
|
/** System metadata for grouping and organization */
|
|
339
339
|
metadata: ProductMetadata | null;
|
|
340
340
|
/** Convenience field: extracted from metadata.productFamily for easier access */
|
|
341
341
|
productFamily?: string | null;
|
|
342
342
|
displayOrder: number;
|
|
343
343
|
}
|
|
344
|
+
interface ProductsResult {
|
|
345
|
+
products: Product[];
|
|
346
|
+
}
|
|
347
|
+
type Plan = Product;
|
|
344
348
|
interface PlansResult {
|
|
345
|
-
plans:
|
|
349
|
+
plans: Product[];
|
|
350
|
+
}
|
|
351
|
+
interface FetchProductsOptions {
|
|
352
|
+
/**
|
|
353
|
+
* Filter by product type
|
|
354
|
+
*/
|
|
355
|
+
type?: 'SUBSCRIPTION' | 'ONE_TIME' | 'CREDITS' | 'DONATION';
|
|
346
356
|
}
|
|
347
357
|
interface RecurContextValue {
|
|
348
358
|
/**
|
|
@@ -354,7 +364,12 @@ interface RecurContextValue {
|
|
|
354
364
|
*/
|
|
355
365
|
checkout: (options: CheckoutOptions) => Promise<void>;
|
|
356
366
|
/**
|
|
357
|
-
* Fetch available
|
|
367
|
+
* Fetch available products
|
|
368
|
+
*/
|
|
369
|
+
fetchProducts: (options?: FetchProductsOptions) => Promise<ProductsResult>;
|
|
370
|
+
/**
|
|
371
|
+
* @deprecated Use fetchProducts instead
|
|
372
|
+
* Fetch available subscription plans (backward compatibility)
|
|
358
373
|
*/
|
|
359
374
|
fetchPlans: () => Promise<PlansResult>;
|
|
360
375
|
/**
|
|
@@ -558,28 +573,32 @@ declare function RecurProvider({ children, config: initialConfig }: RecurProvide
|
|
|
558
573
|
*/
|
|
559
574
|
declare function useRecur(): RecurContextValue;
|
|
560
575
|
|
|
561
|
-
interface
|
|
576
|
+
interface UseProductsOptions {
|
|
562
577
|
/**
|
|
563
|
-
* Whether to fetch
|
|
578
|
+
* Whether to fetch products immediately on mount
|
|
564
579
|
* @default true
|
|
565
580
|
*/
|
|
566
581
|
enabled?: boolean;
|
|
567
582
|
/**
|
|
568
|
-
*
|
|
583
|
+
* Filter by product type
|
|
569
584
|
*/
|
|
570
|
-
|
|
585
|
+
type?: 'SUBSCRIPTION' | 'ONE_TIME' | 'CREDITS' | 'DONATION';
|
|
586
|
+
/**
|
|
587
|
+
* Callback when products are fetched successfully
|
|
588
|
+
*/
|
|
589
|
+
onSuccess?: (products: Product[]) => void;
|
|
571
590
|
/**
|
|
572
591
|
* Callback when an error occurs
|
|
573
592
|
*/
|
|
574
593
|
onError?: (error: Error) => void;
|
|
575
594
|
}
|
|
576
|
-
interface
|
|
595
|
+
interface UseProductsResult {
|
|
577
596
|
/**
|
|
578
|
-
* The fetched
|
|
597
|
+
* The fetched products data
|
|
579
598
|
*/
|
|
580
|
-
data:
|
|
599
|
+
data: Product[] | undefined;
|
|
581
600
|
/**
|
|
582
|
-
* Whether the
|
|
601
|
+
* Whether the products are currently being fetched
|
|
583
602
|
*/
|
|
584
603
|
isLoading: boolean;
|
|
585
604
|
/**
|
|
@@ -592,29 +611,38 @@ interface UsePlansResult {
|
|
|
592
611
|
refetch: () => Promise<void>;
|
|
593
612
|
}
|
|
594
613
|
/**
|
|
595
|
-
*
|
|
614
|
+
* useProducts Hook
|
|
596
615
|
*
|
|
597
|
-
* Fetch available
|
|
616
|
+
* Fetch available products with automatic state management
|
|
598
617
|
*
|
|
599
618
|
* @example
|
|
600
619
|
* ```tsx
|
|
601
|
-
* function
|
|
602
|
-
* const { data:
|
|
620
|
+
* function ProductsPage() {
|
|
621
|
+
* const { data: products, isLoading, error } = useProducts();
|
|
603
622
|
*
|
|
604
|
-
* if (isLoading) return <
|
|
623
|
+
* if (isLoading) return <ProductsSkeletonLoader />;
|
|
605
624
|
* if (error) return <ErrorMessage error={error} />;
|
|
606
625
|
*
|
|
607
626
|
* return (
|
|
608
627
|
* <div>
|
|
609
|
-
* {
|
|
610
|
-
* <
|
|
628
|
+
* {products?.map(product => (
|
|
629
|
+
* <ProductCard key={product.id} product={product} />
|
|
611
630
|
* ))}
|
|
612
631
|
* </div>
|
|
613
632
|
* );
|
|
614
633
|
* }
|
|
615
634
|
* ```
|
|
635
|
+
*
|
|
636
|
+
* @example Filter by type
|
|
637
|
+
* ```tsx
|
|
638
|
+
* // Only fetch subscription products
|
|
639
|
+
* const { data: subscriptions } = useProducts({ type: 'SUBSCRIPTION' });
|
|
640
|
+
*
|
|
641
|
+
* // Only fetch one-time products
|
|
642
|
+
* const { data: oneTimeProducts } = useProducts({ type: 'ONE_TIME' });
|
|
643
|
+
* ```
|
|
616
644
|
*/
|
|
617
|
-
declare function
|
|
645
|
+
declare function useProducts(options?: UseProductsOptions): UseProductsResult;
|
|
618
646
|
|
|
619
647
|
interface UseSubscribeOptions {
|
|
620
648
|
/**
|
|
@@ -696,4 +724,4 @@ interface UseSubscribeResult {
|
|
|
696
724
|
*/
|
|
697
725
|
declare function useSubscribe(options?: UseSubscribeOptions): UseSubscribeResult;
|
|
698
726
|
|
|
699
|
-
export { type CheckoutError, type CheckoutOptions, type CheckoutResult, type Plan, type PlansResult, type ProductMetadata, type RecurConfig, type RecurContextValue, RecurProvider, type RecurProviderProps, type SubscriptionResult, type UsePlansOptions, type UsePlansResult, type UseSubscribeOptions, type UseSubscribeResult, usePlans, useRecur, useSubscribe };
|
|
727
|
+
export { type CheckoutError, type CheckoutOptions, type CheckoutResult, type Plan, type PlansResult, type Product, type ProductMetadata, type ProductsResult, type RecurConfig, type RecurContextValue, RecurProvider, type RecurProviderProps, type SubscriptionResult, type UseProductsOptions as UsePlansOptions, type UseProductsResult as UsePlansResult, type UseProductsOptions, type UseProductsResult, type UseSubscribeOptions, type UseSubscribeResult, useProducts as usePlans, useProducts, useRecur, useSubscribe };
|
package/dist/index.js
CHANGED
|
@@ -2592,13 +2592,22 @@ function RecurProvider({ children, config: initialConfig = {} }) {
|
|
|
2592
2592
|
const tradeResult = await paymentSession.getTradeResult();
|
|
2593
2593
|
console.log("[Recur SDK] Trade result received:", tradeResult);
|
|
2594
2594
|
console.log("[Recur SDK] Step 8: Executing payment...");
|
|
2595
|
-
const creditToken = tradeResult.EncryptInfo || tradeResult.creditToken;
|
|
2596
2595
|
const timestamp = tradeResult.HashTimestamp || tradeResult.timestamp;
|
|
2597
|
-
|
|
2598
|
-
|
|
2599
|
-
|
|
2596
|
+
let paymentBody = {};
|
|
2597
|
+
if (checkoutResult.checkout.productType === "SUBSCRIPTION") {
|
|
2598
|
+
const subscriptionCreditToken = checkoutResult.creditToken;
|
|
2599
|
+
const subscriptionTimestamp = checkoutResult.sdkTimestamp;
|
|
2600
|
+
if (!subscriptionCreditToken) {
|
|
2601
|
+
console.error("[Recur SDK] Missing creditToken from checkout for subscription");
|
|
2602
|
+
throw new Error("Missing creditToken for subscription payment");
|
|
2603
|
+
}
|
|
2604
|
+
paymentBody = {
|
|
2605
|
+
creditToken: subscriptionCreditToken,
|
|
2606
|
+
timestamp: subscriptionTimestamp || timestamp
|
|
2607
|
+
};
|
|
2608
|
+
console.log("[Recur SDK] Using creditToken from checkout:", subscriptionCreditToken.substring(0, 30) + "...");
|
|
2609
|
+
console.log("[Recur SDK] Using timestamp:", subscriptionTimestamp ? "from checkout (sdkTimestamp)" : "from tradeResult");
|
|
2600
2610
|
}
|
|
2601
|
-
const paymentBody = checkoutResult.checkout.productType === "SUBSCRIPTION" ? { creditToken, timestamp } : {};
|
|
2602
2611
|
const paymentResponse = await fetch(
|
|
2603
2612
|
`${baseUrl}/v1/checkouts/${checkoutResult.checkout.id}/pay`,
|
|
2604
2613
|
{
|
|
@@ -2680,13 +2689,17 @@ function RecurProvider({ children, config: initialConfig = {} }) {
|
|
|
2680
2689
|
},
|
|
2681
2690
|
[config]
|
|
2682
2691
|
);
|
|
2683
|
-
const
|
|
2684
|
-
async () => {
|
|
2692
|
+
const fetchProducts = useCallback(
|
|
2693
|
+
async (options) => {
|
|
2685
2694
|
if (!config.publishableKey) {
|
|
2686
2695
|
throw new Error("publishableKey is required");
|
|
2687
2696
|
}
|
|
2688
2697
|
const baseUrl = config.baseUrl || "https://api.recur.tw";
|
|
2689
|
-
const
|
|
2698
|
+
const url = new URL(`${baseUrl}/v1/products`);
|
|
2699
|
+
if (options?.type) {
|
|
2700
|
+
url.searchParams.set("type", options.type);
|
|
2701
|
+
}
|
|
2702
|
+
const response = await fetch(url.toString(), {
|
|
2690
2703
|
method: "GET",
|
|
2691
2704
|
headers: {
|
|
2692
2705
|
"Content-Type": "application/json",
|
|
@@ -2695,21 +2708,29 @@ function RecurProvider({ children, config: initialConfig = {} }) {
|
|
|
2695
2708
|
});
|
|
2696
2709
|
if (!response.ok) {
|
|
2697
2710
|
const errorData = await response.json().catch(() => ({}));
|
|
2698
|
-
throw new Error(errorData.error || "Failed to fetch
|
|
2711
|
+
throw new Error(errorData.error || "Failed to fetch products");
|
|
2699
2712
|
}
|
|
2700
2713
|
return await response.json();
|
|
2701
2714
|
},
|
|
2702
2715
|
[config]
|
|
2703
2716
|
);
|
|
2717
|
+
const fetchPlans = useCallback(
|
|
2718
|
+
async () => {
|
|
2719
|
+
const result = await fetchProducts({ type: "SUBSCRIPTION" });
|
|
2720
|
+
return { plans: result.products };
|
|
2721
|
+
},
|
|
2722
|
+
[fetchProducts]
|
|
2723
|
+
);
|
|
2704
2724
|
const value = useMemo(
|
|
2705
2725
|
() => ({
|
|
2706
2726
|
config,
|
|
2707
2727
|
checkout,
|
|
2728
|
+
fetchProducts,
|
|
2708
2729
|
fetchPlans,
|
|
2709
2730
|
isCheckingOut,
|
|
2710
2731
|
updateConfig
|
|
2711
2732
|
}),
|
|
2712
|
-
[config, checkout, fetchPlans, isCheckingOut, updateConfig]
|
|
2733
|
+
[config, checkout, fetchProducts, fetchPlans, isCheckingOut, updateConfig]
|
|
2713
2734
|
);
|
|
2714
2735
|
return /* @__PURE__ */ jsx(RecurContext.Provider, { value, children });
|
|
2715
2736
|
}
|
|
@@ -2720,9 +2741,9 @@ function useRecur() {
|
|
|
2720
2741
|
}
|
|
2721
2742
|
return context;
|
|
2722
2743
|
}
|
|
2723
|
-
function
|
|
2724
|
-
const { enabled = true, onSuccess, onError } = options;
|
|
2725
|
-
const {
|
|
2744
|
+
function useProducts(options = {}) {
|
|
2745
|
+
const { enabled = true, type, onSuccess, onError } = options;
|
|
2746
|
+
const { fetchProducts } = useRecur();
|
|
2726
2747
|
const [data, setData] = useState(void 0);
|
|
2727
2748
|
const [isLoading2, setIsLoading] = useState(false);
|
|
2728
2749
|
const [error, setError] = useState(null);
|
|
@@ -2731,17 +2752,17 @@ function usePlans(options = {}) {
|
|
|
2731
2752
|
setIsLoading(true);
|
|
2732
2753
|
setError(null);
|
|
2733
2754
|
try {
|
|
2734
|
-
const result = await
|
|
2735
|
-
setData(result.
|
|
2736
|
-
onSuccess?.(result.
|
|
2755
|
+
const result = await fetchProducts({ type });
|
|
2756
|
+
setData(result.products);
|
|
2757
|
+
onSuccess?.(result.products);
|
|
2737
2758
|
} catch (err) {
|
|
2738
|
-
const error2 = err instanceof Error ? err : new Error("Failed to fetch
|
|
2759
|
+
const error2 = err instanceof Error ? err : new Error("Failed to fetch products");
|
|
2739
2760
|
setError(error2);
|
|
2740
2761
|
onError?.(error2);
|
|
2741
2762
|
} finally {
|
|
2742
2763
|
setIsLoading(false);
|
|
2743
2764
|
}
|
|
2744
|
-
}, [enabled,
|
|
2765
|
+
}, [enabled, type, fetchProducts, onSuccess, onError]);
|
|
2745
2766
|
useEffect(() => {
|
|
2746
2767
|
fetchData();
|
|
2747
2768
|
}, [fetchData]);
|
|
@@ -2799,4 +2820,4 @@ function useSubscribe(options = {}) {
|
|
|
2799
2820
|
};
|
|
2800
2821
|
}
|
|
2801
2822
|
|
|
2802
|
-
export { RecurProvider, usePlans, useRecur, useSubscribe };
|
|
2823
|
+
export { RecurProvider, useProducts as usePlans, useProducts, useRecur, useSubscribe };
|
package/dist/recur.umd.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"use strict";var RecurCheckout=(()=>{var C=Object.defineProperty;var ce=Object.getOwnPropertyDescriptor;var le=Object.getOwnPropertyNames;var de=Object.prototype.hasOwnProperty;var ue=(c,e,t)=>e in c?C(c,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):c[e]=t;var
|
|
1
|
+
"use strict";var RecurCheckout=(()=>{var C=Object.defineProperty;var ce=Object.getOwnPropertyDescriptor;var le=Object.getOwnPropertyNames;var de=Object.prototype.hasOwnProperty;var ue=(c,e,t)=>e in c?C(c,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):c[e]=t;var b=(c,e)=>()=>(c&&(e=c(c=0)),e);var h=(c,e)=>{for(var t in e)C(c,t,{get:e[t],enumerable:!0})},me=(c,e,t,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of le(e))!de.call(c,i)&&i!==t&&C(c,i,{get:()=>e[i],enumerable:!(r=ce(e,i))||r.enumerable});return c};var pe=c=>me(C({},"__esModule",{value:!0}),c);var a=(c,e,t)=>ue(c,typeof e!="symbol"?e+"":e,t);var O={};h(O,{RecurLoadingSpinner:()=>S});var S,F=b(()=>{"use strict";S=class extends HTMLElement{static get observedAttributes(){return["message","size"]}constructor(){super(),this.attachShadow({mode:"open"})}connectedCallback(){this.render()}attributeChangedCallback(e,t,r){t!==r&&this.render()}get message(){return this.getAttribute("message")||"\u6B63\u5728\u8655\u7406\u8A02\u95B1..."}get size(){let e=this.getAttribute("size");return e==="small"||e==="large"?e:"medium"}getSizeValue(){return{small:24,medium:40,large:56}[this.size]}render(){let e=this.getSizeValue();this.shadowRoot.innerHTML=`
|
|
2
2
|
<style>
|
|
3
3
|
:host {
|
|
4
4
|
display: block;
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
|
|
41
41
|
<div class="recur-sdk__spinner"></div>
|
|
42
42
|
${this.message?`<p class="recur-sdk__loading-text">${this.message}</p>`:""}
|
|
43
|
-
`}};typeof window<"u"&&!customElements.get("recur-loading-spinner")&&customElements.define("recur-loading-spinner",S)});var
|
|
43
|
+
`}};typeof window<"u"&&!customElements.get("recur-loading-spinner")&&customElements.define("recur-loading-spinner",S)});var j={};h(j,{RecurSuccessMessage:()=>T});var T,Y=b(()=>{"use strict";T=class extends HTMLElement{static get observedAttributes(){return["title","message","icon"]}constructor(){super(),this.attachShadow({mode:"open"})}connectedCallback(){this.render()}attributeChangedCallback(e,t,r){t!==r&&this.render()}get successTitle(){return this.getAttribute("title")||"Subscription Complete!"}get successMessage(){return this.getAttribute("message")||"Thank you for subscribing. Your payment has been processed successfully."}get showIcon(){return this.getAttribute("icon")!=="false"}render(){this.shadowRoot.innerHTML=`
|
|
44
44
|
<style>
|
|
45
45
|
:host {
|
|
46
46
|
display: block;
|
|
@@ -121,7 +121,7 @@
|
|
|
121
121
|
<p class="recur-sdk__success-message">${this.successMessage}</p>
|
|
122
122
|
<slot></slot>
|
|
123
123
|
</div>
|
|
124
|
-
`}};typeof window<"u"&&!customElements.get("recur-success-message")&&customElements.define("recur-success-message",T)});var
|
|
124
|
+
`}};typeof window<"u"&&!customElements.get("recur-success-message")&&customElements.define("recur-success-message",T)});var q={};h(q,{RecurErrorDisplay:()=>I});var I,V=b(()=>{"use strict";I=class extends HTMLElement{static get observedAttributes(){return["error","dismissible"]}constructor(){super(),this.attachShadow({mode:"open"})}connectedCallback(){this.render()}attributeChangedCallback(e,t,r){t!==r&&this.render()}get error(){return this.getAttribute("error")||""}get isDismissible(){return this.getAttribute("dismissible")==="true"}handleDismiss(){this.dispatchEvent(new CustomEvent("dismiss",{bubbles:!0,composed:!0})),this.remove()}render(){if(!this.error){this.shadowRoot.innerHTML="";return}this.shadowRoot.innerHTML=`
|
|
125
125
|
<style>
|
|
126
126
|
:host {
|
|
127
127
|
display: block;
|
|
@@ -211,7 +211,7 @@
|
|
|
211
211
|
</button>
|
|
212
212
|
`:""}
|
|
213
213
|
</div>
|
|
214
|
-
`,this.isDismissible&&this.shadowRoot.querySelector(".recur-sdk__error-dismiss")?.addEventListener("click",()=>this.handleDismiss())}};typeof window<"u"&&!customElements.get("recur-error-display")&&customElements.define("recur-error-display",I)});var
|
|
214
|
+
`,this.isDismissible&&this.shadowRoot.querySelector(".recur-sdk__error-dismiss")?.addEventListener("click",()=>this.handleDismiss())}};typeof window<"u"&&!customElements.get("recur-error-display")&&customElements.define("recur-error-display",I)});var X={};h(X,{RecurSkeletonLoader:()=>R});var R,W=b(()=>{"use strict";R=class extends HTMLElement{static get observedAttributes(){return["type"]}constructor(){super(),this.attachShadow({mode:"open"})}connectedCallback(){this.render()}attributeChangedCallback(e,t,r){t!==r&&this.render()}get type(){let e=this.getAttribute("type");return e==="list"||e==="card"?e:"payment-form"}renderPaymentFormSkeleton(){return`
|
|
215
215
|
<div class="skeleton-field">
|
|
216
216
|
<div class="skeleton-label"></div>
|
|
217
217
|
<div class="skeleton-input"></div>
|
|
@@ -377,7 +377,7 @@
|
|
|
377
377
|
</style>
|
|
378
378
|
|
|
379
379
|
${e}
|
|
380
|
-
`}};typeof window<"u"&&!customElements.get("recur-skeleton-loader")&&customElements.define("recur-skeleton-loader",R)});var
|
|
380
|
+
`}};typeof window<"u"&&!customElements.get("recur-skeleton-loader")&&customElements.define("recur-skeleton-loader",R)});var J={};h(J,{RecurPaymentFormSkeleton:()=>P});var P,Z=b(()=>{"use strict";P=class extends HTMLElement{constructor(){super(),this.attachShadow({mode:"open"})}connectedCallback(){this.render()}render(){this.shadowRoot.innerHTML=`
|
|
381
381
|
<style>
|
|
382
382
|
:host {
|
|
383
383
|
display: block;
|
|
@@ -644,7 +644,7 @@
|
|
|
644
644
|
<p class="security-text">\u60A8\u7684\u4ED8\u6B3E\u8CC7\u8A0A\u7D93\u904E\u52A0\u5BC6\u4FDD\u8B77</p>
|
|
645
645
|
</div>
|
|
646
646
|
</div>
|
|
647
|
-
`}};typeof window<"u"&&!customElements.get("recur-payment-form-skeleton")&&customElements.define("recur-payment-form-skeleton",
|
|
647
|
+
`}};typeof window<"u"&&!customElements.get("recur-payment-form-skeleton")&&customElements.define("recur-payment-form-skeleton",P)});var G={};h(G,{RecurToast:()=>M,RecurToastContainer:()=>x});var M,f,x,Q=b(()=>{"use strict";M=class extends HTMLElement{static get observedAttributes(){return["message","type","duration"]}constructor(){super(),this.attachShadow({mode:"open"})}connectedCallback(){this.render(),this.setupAutoDismiss()}get message(){return this.getAttribute("message")||"Notification"}get type(){let e=this.getAttribute("type");return e==="success"||e==="error"?e:"info"}get duration(){let e=this.getAttribute("duration");return e?parseInt(e,10):5e3}setupAutoDismiss(){let e=this.duration;e>0&&setTimeout(()=>this.dismiss(),e)}dismiss(){this.style.animation="recur-toast-slide-out 0.3s ease-in-out",setTimeout(()=>this.remove(),300)}getTypeIcon(){switch(this.type){case"success":return`<svg width="20" height="20" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
648
648
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7" />
|
|
649
649
|
</svg>`;case"error":return`<svg width="20" height="20" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
650
650
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
|
|
@@ -764,7 +764,7 @@
|
|
|
764
764
|
</svg>
|
|
765
765
|
</button>
|
|
766
766
|
</div>
|
|
767
|
-
`,this.shadowRoot.querySelector(".toast-close")?.addEventListener("click",i=>{i.stopPropagation(),this.dismiss()}),this.shadowRoot.querySelector(".toast")?.addEventListener("click",()=>this.dismiss())}static show(e,t="info",r=5e3){let i=
|
|
767
|
+
`,this.shadowRoot.querySelector(".toast-close")?.addEventListener("click",i=>{i.stopPropagation(),this.dismiss()}),this.shadowRoot.querySelector(".toast")?.addEventListener("click",()=>this.dismiss())}static show(e,t="info",r=5e3){let i=x.getInstance(),s=document.createElement("recur-toast");return s.setAttribute("message",e),s.setAttribute("type",t),s.setAttribute("duration",r.toString()),i.appendChild(s),s}},f=class f extends HTMLElement{constructor(){super(),this.attachShadow({mode:"open"})}connectedCallback(){this.render()}render(){this.shadowRoot.innerHTML=`
|
|
768
768
|
<style>
|
|
769
769
|
:host {
|
|
770
770
|
position: fixed;
|
|
@@ -791,7 +791,7 @@
|
|
|
791
791
|
</style>
|
|
792
792
|
|
|
793
793
|
<slot></slot>
|
|
794
|
-
`}static getInstance(){return f.instance||(f.instance=document.querySelector("recur-toast-container"),f.instance||(f.instance=document.createElement("recur-toast-container"),document.body.appendChild(f.instance))),f.instance}};a(f,"instance",null);
|
|
794
|
+
`}static getInstance(){return f.instance||(f.instance=document.querySelector("recur-toast-container"),f.instance||(f.instance=document.createElement("recur-toast-container"),document.body.appendChild(f.instance))),f.instance}};a(f,"instance",null);x=f;typeof window<"u"&&!customElements.get("recur-toast")&&customElements.define("recur-toast",M);typeof window<"u"&&!customElements.get("recur-toast-container")&&customElements.define("recur-toast-container",x)});var ee={};h(ee,{RecurPaymentForm:()=>L});var L,te=b(()=>{"use strict";L=class extends HTMLElement{constructor(){super();a(this,"containerId");a(this,"customStyles");a(this,"_isInitializing",!1);a(this,"_initializationAborted",!1);this.containerId=this.getAttribute("container-id")||`recur-${Date.now()}`,this.customStyles=this.getAttribute("custom-styles")||"",this.attachShadow({mode:"open"})}connectedCallback(){this.render()}disconnectedCallback(){console.log("[PaymentForm] Component disconnected, cleaning up..."),this._initializationAborted=!0,this._paymentSession=null;let t=document.getElementById(`${this.containerId}-submit-btn`);if(t){let r=t.cloneNode(!0);t.parentNode?.replaceChild(r,t)}}static get observedAttributes(){return["custom-styles","customer-name","customer-email","plan-name","amount","billing-period"]}attributeChangedCallback(t,r,i){t==="custom-styles"&&r!==i?(this.customStyles=i||"",this.updateCustomStyles()):r!==i&&this.updateCustomerInfoSection()}render(){this.shadowRoot.innerHTML=`
|
|
795
795
|
<style>
|
|
796
796
|
/* \u9019\u4E9B\u6A23\u5F0F\u88AB\u9694\u96E2\u5728 Shadow DOM \u5167\uFF0C\u4E0D\u6703\u5F71\u97FF\u5916\u90E8 */
|
|
797
797
|
:host {
|
|
@@ -1217,10 +1217,10 @@
|
|
|
1217
1217
|
>
|
|
1218
1218
|
<span id="${this.containerId}-submit-text">\u78BA\u8A8D\u4ED8\u6B3E</span>
|
|
1219
1219
|
</button>
|
|
1220
|
-
`,t}async initializePayment(t,r){if(this._initializationAborted)return console.log("[PaymentForm] Initialization aborted before start"),null;if(this._isInitializing=!0,await this.updateComplete(),this._initializationAborted)return console.log("[PaymentForm] Initialization aborted after updateComplete"),this._isInitializing=!1,null;if(!window.UniPayment)throw this._isInitializing=!1,new Error("PAYUNi SDK not loaded");try{let i=document.getElementById(`${this.containerId}-card-no`),s=document.getElementById(`${this.containerId}-card-exp`),n=document.getElementById(`${this.containerId}-card-cvc`);if(!i||!s||!n)return console.log("[PaymentForm] DOM elements not found, likely disconnected"),this._isInitializing=!1,null;if(this._initializationAborted)return console.log("[PaymentForm] Initialization aborted before createSession"),this._isInitializing=!1,null;let o=window.UniPayment.createSession(t,{env:r==="SANDBOX"?"S":"P",elements:{CardNo:`${this.containerId}-card-no`,CardExp:`${this.containerId}-card-exp`,CardCvc:`${this.containerId}-card-cvc`}});if(this._initializationAborted)return console.log("[PaymentForm] Initialization aborted before paymentSession.start()"),this._isInitializing=!1,null;try{await o.start()}catch(
|
|
1220
|
+
`,t}async initializePayment(t,r){if(this._initializationAborted)return console.log("[PaymentForm] Initialization aborted before start"),null;if(this._isInitializing=!0,await this.updateComplete(),this._initializationAborted)return console.log("[PaymentForm] Initialization aborted after updateComplete"),this._isInitializing=!1,null;if(!window.UniPayment)throw this._isInitializing=!1,new Error("PAYUNi SDK not loaded");try{let i=document.getElementById(`${this.containerId}-card-no`),s=document.getElementById(`${this.containerId}-card-exp`),n=document.getElementById(`${this.containerId}-card-cvc`);if(!i||!s||!n)return console.log("[PaymentForm] DOM elements not found, likely disconnected"),this._isInitializing=!1,null;if(this._initializationAborted)return console.log("[PaymentForm] Initialization aborted before createSession"),this._isInitializing=!1,null;let o=window.UniPayment.createSession(t,{env:r==="SANDBOX"?"S":"P",elements:{CardNo:`${this.containerId}-card-no`,CardExp:`${this.containerId}-card-exp`,CardCvc:`${this.containerId}-card-cvc`}});if(this._initializationAborted)return console.log("[PaymentForm] Initialization aborted before paymentSession.start()"),this._isInitializing=!1,null;try{await o.start()}catch(d){if((d?.message?.includes("1008")||d?.message?.includes("timeout")||d?.code===1008)&&this._initializationAborted)return console.log("[PaymentForm] Caught 1008 timeout error after component disconnect - ignoring"),this._isInitializing=!1,null;throw d}return this._initializationAborted?(console.log("[PaymentForm] Initialization aborted after paymentSession.start()"),this._isInitializing=!1,null):(this.hideCardSkeletons(),o.onUpdate?.(d=>{if(this._initializationAborted){console.log("[PaymentForm] Ignoring onUpdate event - component disconnected");return}console.log("[PaymentForm] PAYUNi update:",d);let l=d.status&&d.status.CardNo===!0&&d.status.CardExp===!0&&d.status.CardCvc===!0,v=document.getElementById(`${this.containerId}-submit-btn`);v&&(v.disabled=!l,console.log("[PaymentForm] Submit button disabled:",!l))}),this._initializationAborted?(console.log("[PaymentForm] Initialization aborted before saving session"),this._isInitializing=!1,null):(this._paymentSession=o,this.setupFormSubmission(),this._isInitializing=!1,o))}catch(i){throw this._isInitializing=!1,console.error("[PaymentForm] Failed to initialize payment:",i),i}}setupFormSubmission(){let t=document.getElementById(`${this.containerId}-submit-btn`);t&&t.addEventListener("click",async r=>{if(r.preventDefault(),t.classList.contains("loading"))return;let i,s,n=document.getElementById(`${this.containerId}-email`),o=document.getElementById(`${this.containerId}-name`);if(n&&o){if(i=n.value,s=o.value,!i||!s){this.showError("\u8ACB\u586B\u5BEB\u59D3\u540D\u548C\u96FB\u5B50\u90F5\u4EF6");return}}else if(i=this.getAttribute("customer-email")||void 0,s=this.getAttribute("customer-name")||void 0,!i||!s){this.showError("\u5BA2\u6236\u8CC7\u8A0A\u4E0D\u5B8C\u6574");return}this.clearError(),this.setButtonLoading(!0),this.dispatchEvent(new CustomEvent("submit",{detail:{customerEmail:i,customerName:s,paymentSession:this._paymentSession},bubbles:!0,composed:!0}))})}setButtonLoading(t){let r=document.getElementById(`${this.containerId}-submit-btn`);r&&(t?(r.classList.add("loading"),r.disabled=!0,r.innerHTML=`
|
|
1221
1221
|
<span class="recur-loading-spinner"></span>
|
|
1222
1222
|
<span>\u8655\u7406\u4E2D...</span>
|
|
1223
|
-
`):(r.classList.remove("loading"),r.disabled=!1,r.innerHTML='<span id="'+this.containerId+'-submit-text">\u78BA\u8A8D\u4ED8\u6B3E</span>'))}resetButton(){this.setButtonLoading(!1)}showError(t){let r=this.querySelector(".recur-sdk__error-container");r||(r=document.createElement("div"),r.className="recur-sdk__error-container",r.style.cssText="margin-bottom: 16px;",this.insertBefore(r,this.firstChild));let i=document.createElement("recur-error-display");i.setAttribute("error",t),i.setAttribute("dismissible","true"),r.innerHTML="",r.appendChild(i),i.scrollIntoView({behavior:"smooth",block:"center"})}clearError(){let t=this.querySelector(".recur-sdk__error-container");t&&(t.innerHTML="")}updateComplete(){return new Promise(t=>{if(this.isConnected)setTimeout(t,0);else{let r=new MutationObserver(()=>{this.isConnected&&(r.disconnect(),setTimeout(t,0))});r.observe(document.body,{childList:!0,subtree:!0})}})}getFormData(){return{email:document.getElementById(`${this.containerId}-email`)?.value,name:document.getElementById(`${this.containerId}-name`)?.value}}setFormData(t){if(t.email){let r=document.getElementById(`${this.containerId}-email`);r&&(r.value=t.email)}if(t.name){let r=document.getElementById(`${this.containerId}-name`);r&&(r.value=t.name)}}};customElements.get("recur-payment-form")||customElements.define("recur-payment-form",L)});var
|
|
1223
|
+
`):(r.classList.remove("loading"),r.disabled=!1,r.innerHTML='<span id="'+this.containerId+'-submit-text">\u78BA\u8A8D\u4ED8\u6B3E</span>'))}resetButton(){this.setButtonLoading(!1)}showError(t){let r=this.querySelector(".recur-sdk__error-container");r||(r=document.createElement("div"),r.className="recur-sdk__error-container",r.style.cssText="margin-bottom: 16px;",this.insertBefore(r,this.firstChild));let i=document.createElement("recur-error-display");i.setAttribute("error",t),i.setAttribute("dismissible","true"),r.innerHTML="",r.appendChild(i),i.scrollIntoView({behavior:"smooth",block:"center"})}clearError(){let t=this.querySelector(".recur-sdk__error-container");t&&(t.innerHTML="")}updateComplete(){return new Promise(t=>{if(this.isConnected)setTimeout(t,0);else{let r=new MutationObserver(()=>{this.isConnected&&(r.disconnect(),setTimeout(t,0))});r.observe(document.body,{childList:!0,subtree:!0})}})}getFormData(){return{email:document.getElementById(`${this.containerId}-email`)?.value,name:document.getElementById(`${this.containerId}-name`)?.value}}setFormData(t){if(t.email){let r=document.getElementById(`${this.containerId}-email`);r&&(r.value=t.email)}if(t.name){let r=document.getElementById(`${this.containerId}-name`);r&&(r.value=t.name)}}};customElements.get("recur-payment-form")||customElements.define("recur-payment-form",L)});var re={};h(re,{RecurCheckoutButton:()=>U});var U,ie=b(()=>{"use strict";U=class extends HTMLElement{constructor(){super();a(this,"_isLoading",!1);a(this,"handleClick",async t=>{if(t.preventDefault(),this._isLoading||this.hasAttribute("disabled"))return;let r=this.getAttribute("publishable-key"),i=this.getAttribute("product-id"),s=this.getAttribute("success-url"),n=this.getAttribute("cancel-url");if(!r){this.dispatchError("Missing required attribute: publishable-key");return}if(!i){this.dispatchError("Missing required attribute: product-id");return}if(!s){this.dispatchError("Missing required attribute: success-url");return}if(!n){this.dispatchError("Missing required attribute: cancel-url");return}this._isLoading=!0,this.render(),this.setupEventListeners();try{let o=await this.createCheckoutSession({publishableKey:r,productId:i,successUrl:this.resolveUrl(s),cancelUrl:this.resolveUrl(n),customerEmail:this.getAttribute("customer-email")||void 0,mode:this.getAttribute("mode")});this.dispatchEvent(new CustomEvent("checkout-started",{detail:{sessionId:o.id,url:o.url},bubbles:!0,composed:!0})),window.location.href=o.url}catch(o){this._isLoading=!1,this.render(),this.setupEventListeners(),this.dispatchError(o.message||"Failed to create checkout session")}});this.attachShadow({mode:"open"})}static get observedAttributes(){return["publishable-key","product-id","success-url","cancel-url","customer-email","mode","button-text","button-style","disabled"]}connectedCallback(){this.render(),this.setupEventListeners()}disconnectedCallback(){let t=this.shadowRoot?.querySelector("button");t&&t.removeEventListener("click",this.handleClick)}attributeChangedCallback(t,r,i){r!==i&&this.render()}render(){let t=this.getAttribute("button-text")||this.textContent?.trim()||"\u8A02\u95B1",r=this.getAttribute("button-style")||"primary",i=this.hasAttribute("disabled")||this._isLoading;this.shadowRoot.innerHTML=`
|
|
1224
1224
|
<style>
|
|
1225
1225
|
:host {
|
|
1226
1226
|
display: inline-block;
|
|
@@ -1319,7 +1319,7 @@
|
|
|
1319
1319
|
${this._isLoading?'<span class="spinner"></span>':""}
|
|
1320
1320
|
<span>${this._isLoading?"\u8655\u7406\u4E2D...":t}</span>
|
|
1321
1321
|
</button>
|
|
1322
|
-
`}setupEventListeners(){let t=this.shadowRoot?.querySelector("button");t&&t.addEventListener("click",this.handleClick)}async createCheckoutSession(t){let r=this.getApiBaseUrl(),i={productId:t.productId,successUrl:t.successUrl,cancelUrl:t.cancelUrl};t.mode&&(i.mode=t.mode),t.customerEmail&&(i.customerEmail=t.customerEmail);let s=await fetch(`${r}/v1/checkout/sessions`,{method:"POST",headers:{"Content-Type":"application/json","X-Recur-Publishable-Key":t.publishableKey},body:JSON.stringify(i)});if(!s.ok){let n=await s.json().catch(()=>({}));throw new Error(n.error?.message||`HTTP ${s.status}: Failed to create checkout session`)}return s.json()}getApiBaseUrl(){let t=this.getAttribute("api-base-url");if(t)return t;if(typeof window<"u"){let r=window.location.hostname;if(r==="localhost"||r.includes(".test")||r.includes(".local")||r==="127.0.0.1")return`${window.location.protocol}//${window.location.host}/api`}return"https://api.recur.tw"}resolveUrl(t){return t.startsWith("/")?`${window.location.origin}${t}`:t}dispatchError(t){console.error("[recur-checkout]",t),this.dispatchEvent(new CustomEvent("checkout-error",{detail:{message:t},bubbles:!0,composed:!0}))}};typeof window<"u"&&!customElements.get("recur-checkout")&&customElements.define("recur-checkout",U)});var ye={};h(ye,{RecurCheckout:()=>
|
|
1322
|
+
`}setupEventListeners(){let t=this.shadowRoot?.querySelector("button");t&&t.addEventListener("click",this.handleClick)}async createCheckoutSession(t){let r=this.getApiBaseUrl(),i={productId:t.productId,successUrl:t.successUrl,cancelUrl:t.cancelUrl};t.mode&&(i.mode=t.mode),t.customerEmail&&(i.customerEmail=t.customerEmail);let s=await fetch(`${r}/v1/checkout/sessions`,{method:"POST",headers:{"Content-Type":"application/json","X-Recur-Publishable-Key":t.publishableKey},body:JSON.stringify(i)});if(!s.ok){let n=await s.json().catch(()=>({}));throw new Error(n.error?.message||`HTTP ${s.status}: Failed to create checkout session`)}return s.json()}getApiBaseUrl(){let t=this.getAttribute("api-base-url");if(t)return t;if(typeof window<"u"){let r=window.location.hostname;if(r==="localhost"||r.includes(".test")||r.includes(".local")||r==="127.0.0.1")return`${window.location.protocol}//${window.location.host}/api`}return"https://api.recur.tw"}resolveUrl(t){return t.startsWith("/")?`${window.location.origin}${t}`:t}dispatchError(t){console.error("[recur-checkout]",t),this.dispatchEvent(new CustomEvent("checkout-error",{detail:{message:t},bubbles:!0,composed:!0}))}};typeof window<"u"&&!customElements.get("recur-checkout")&&customElements.define("recur-checkout",U)});var ye={};h(ye,{RecurCheckout:()=>E,RecurElements:()=>y,createElements:()=>N,default:()=>ge,init:()=>ne});async function he(){if(typeof window>"u"){console.warn("[Recur SDK] Cannot register Web Components: window is not defined");return}await Promise.all([Promise.resolve().then(()=>(F(),O)),Promise.resolve().then(()=>(Y(),j)),Promise.resolve().then(()=>(V(),q)),Promise.resolve().then(()=>(W(),X)),Promise.resolve().then(()=>(Z(),J)),Promise.resolve().then(()=>(Q(),G)),Promise.resolve().then(()=>(te(),ee)),Promise.resolve().then(()=>(ie(),re))]);let e=["recur-loading-spinner","recur-success-message","recur-error-display","recur-skeleton-loader","recur-payment-form-skeleton","recur-toast","recur-toast-container","recur-payment-form","recur-checkout"].filter(t=>!customElements.get(t));e.length>0&&console.warn("[Recur SDK] The following components are not registered:",e)}typeof window<"u"&&he();var D=class{constructor(e){a(this,"config");if(!e.publishableKey)throw new Error("publishableKey is required");this.config={baseUrl:"https://api.recur.tw",...e}}async createSubscription(e){let{planId:t,customerName:r,customerEmail:i}=e;if(!t)throw new Error("planId is required");let s=await fetch(`${this.config.baseUrl}/v1/subscriptions`,{method:"POST",headers:{"Content-Type":"application/json","X-Recur-Publishable-Key":this.config.publishableKey},body:JSON.stringify({planId:t,customerName:r,customerEmail:i})});if(!s.ok){let n=await s.json().catch(()=>({}));throw{code:n.error||"CHECKOUT_FAILED",message:n.message||"Failed to initiate checkout",details:n}}return await s.json()}async fetchProducts(e){let t=new URL(`${this.config.baseUrl}/v1/products`);e?.type&&t.searchParams.set("type",e.type);let r=await fetch(t.toString(),{method:"GET",headers:{"Content-Type":"application/json","X-Recur-Publishable-Key":this.config.publishableKey}});if(!r.ok){let i=await r.json().catch(()=>({}));throw{code:i.error||"FETCH_PRODUCTS_FAILED",message:i.message||"Failed to fetch products",details:i}}return await r.json()}async fetchPlans(){return{plans:(await this.fetchProducts({type:"SUBSCRIPTION"})).products}}getConfig(){return{...this.config}}};var _=class{constructor(e,t){a(this,"config");a(this,"options");a(this,"container");a(this,"checkoutId",null);a(this,"sdkToken",null);a(this,"sdkEnv","S");a(this,"payuniSDK",null);a(this,"isFormValid",!1);if(this.config=e,this.options=t,typeof t.container=="string"){let r=document.querySelector(t.container);if(!r)throw new Error(`Container not found: ${t.container}`);this.container=r}else this.container=t.container}async render(){try{await this.initCheckout(),this.renderHTML(),await this.loadPayUniSDK(),await this.initPayUniSDK(),this.setupFormSubmit()}catch(e){this.handleError(e)}}getBaseUrl(){if(this.config.baseUrl)return this.config.baseUrl;if(typeof window<"u"){let e=window.location.hostname;if(e==="localhost"||e.includes(".test")||e.includes(".local")||e==="127.0.0.1")return`${window.location.protocol}//${window.location.host}/api`}return"https://api.recur.tw"}async initCheckout(){let e=this.getBaseUrl(),t=await fetch(`${e}/v1/checkouts`,{method:"POST",headers:{"Content-Type":"application/json","X-Recur-Publishable-Key":this.config.publishableKey},body:JSON.stringify({productId:this.options.planId,customerEmail:this.options.customerEmail,customerName:this.options.customerName})});if(!t.ok){let i=await t.json().catch(()=>({}));throw new Error(i.error||"Failed to initialize checkout")}let r=await t.json();this.checkoutId=r.checkout.id,this.sdkToken=r.sdkToken,this.sdkEnv=this.config.publishableKey.startsWith("pk_test_")?"S":"P"}renderHTML(){this.container.innerHTML=`
|
|
1323
1323
|
<div class="recur-embedded-checkout" style="max-width: 400px; margin: 0 auto; padding: 20px; font-family: system-ui, -apple-system, sans-serif;">
|
|
1324
1324
|
<div class="recur-checkout-header" style="margin-bottom: 24px;">
|
|
1325
1325
|
<h2 style="margin: 0 0 8px 0; font-size: 20px; font-weight: 600; color: #111;">Subscribe</h2>
|
|
@@ -1389,21 +1389,21 @@
|
|
|
1389
1389
|
</p>
|
|
1390
1390
|
</form>
|
|
1391
1391
|
</div>
|
|
1392
|
-
`}async loadPayUniSDK(){return new Promise((e,t)=>{if(window.UniPayment){e();return}let r=document.createElement("script");r.src=this.sdkEnv==="P"?"https://vendor.payuni.com.tw/sdk/uni-payment.js":"https://sandbox-vendor.payuni.com.tw/sdk/uni-payment.js",r.async=!0,r.onload=()=>e(),r.onerror=()=>t(new Error("Failed to load PAYUNi SDK")),document.head.appendChild(r)})}async initPayUniSDK(){if(!this.sdkToken)throw new Error("SDK token not initialized");let e=window.UniPayment;if(!e)throw new Error("PAYUNi SDK not loaded");let t={env:this.sdkEnv,useInst:!1,elements:{CardNo:"recur-card-number",CardExp:"recur-card-exp",CardCvc:"recur-card-cvc"},style:{color:"#111827",errorColor:"#dc2626",fontSize:"14px",fontWeight:"400",lineHeight:"24px"}};this.payuniSDK=e.createSession(this.sdkToken,t),await this.payuniSDK.start(),this.payuniSDK.onUpdate(r=>{let i=r?.status;i&&(this.isFormValid=i.CardNo===!0&&i.CardExp===!0&&i.CardCvc===!0,this.updateSubmitButton())})}updateSubmitButton(){let e=document.getElementById("recur-submit-btn");e&&(e.disabled=!this.isFormValid,e.style.opacity=this.isFormValid?"1":"0.5",e.style.cursor=this.isFormValid?"pointer":"not-allowed")}setupFormSubmit(){let e=document.getElementById("recur-checkout-form");e&&e.addEventListener("submit",async t=>{t.preventDefault(),await this.handleSubmit()})}async handleSubmit(){try{this.showLoading(!0),this.hideError();let e=document.getElementById("recur-email").value,t=document.getElementById("recur-name").value;if(!e||!t)throw new Error("Please fill in all required fields");if(!this.checkoutId)throw new Error("Checkout session not initialized");if(!this.payuniSDK)throw new Error("Payment system not initialized");let r=await this.payuniSDK.getTradeResult();if(r.Status!=="SUCCESS")throw new Error(r.Message||"Card validation failed");let i=r.EncryptInfo||r.creditToken;if(!i)throw console.error("[Recur SDK] Missing payment token. Available fields:",Object.keys(r)),new Error("Missing payment token");let s=this.getBaseUrl(),n=await fetch(`${s}/v1/checkouts/${this.checkoutId}/pay`,{method:"POST",headers:{"Content-Type":"application/json","X-Recur-Publishable-Key":this.config.publishableKey},body:JSON.stringify({creditToken:i,timestamp:r.HashTimestamp||r.timestamp})});if(!n.ok){let
|
|
1392
|
+
`}async loadPayUniSDK(){return new Promise((e,t)=>{if(window.UniPayment){e();return}let r=document.createElement("script");r.src=this.sdkEnv==="P"?"https://vendor.payuni.com.tw/sdk/uni-payment.js":"https://sandbox-vendor.payuni.com.tw/sdk/uni-payment.js",r.async=!0,r.onload=()=>e(),r.onerror=()=>t(new Error("Failed to load PAYUNi SDK")),document.head.appendChild(r)})}async initPayUniSDK(){if(!this.sdkToken)throw new Error("SDK token not initialized");let e=window.UniPayment;if(!e)throw new Error("PAYUNi SDK not loaded");let t={env:this.sdkEnv,useInst:!1,elements:{CardNo:"recur-card-number",CardExp:"recur-card-exp",CardCvc:"recur-card-cvc"},style:{color:"#111827",errorColor:"#dc2626",fontSize:"14px",fontWeight:"400",lineHeight:"24px"}};this.payuniSDK=e.createSession(this.sdkToken,t),await this.payuniSDK.start(),this.payuniSDK.onUpdate(r=>{let i=r?.status;i&&(this.isFormValid=i.CardNo===!0&&i.CardExp===!0&&i.CardCvc===!0,this.updateSubmitButton())})}updateSubmitButton(){let e=document.getElementById("recur-submit-btn");e&&(e.disabled=!this.isFormValid,e.style.opacity=this.isFormValid?"1":"0.5",e.style.cursor=this.isFormValid?"pointer":"not-allowed")}setupFormSubmit(){let e=document.getElementById("recur-checkout-form");e&&e.addEventListener("submit",async t=>{t.preventDefault(),await this.handleSubmit()})}async handleSubmit(){try{this.showLoading(!0),this.hideError();let e=document.getElementById("recur-email").value,t=document.getElementById("recur-name").value;if(!e||!t)throw new Error("Please fill in all required fields");if(!this.checkoutId)throw new Error("Checkout session not initialized");if(!this.payuniSDK)throw new Error("Payment system not initialized");let r=await this.payuniSDK.getTradeResult();if(r.Status!=="SUCCESS")throw new Error(r.Message||"Card validation failed");let i=r.EncryptInfo||r.creditToken;if(!i)throw console.error("[Recur SDK] Missing payment token. Available fields:",Object.keys(r)),new Error("Missing payment token");let s=this.getBaseUrl(),n=await fetch(`${s}/v1/checkouts/${this.checkoutId}/pay`,{method:"POST",headers:{"Content-Type":"application/json","X-Recur-Publishable-Key":this.config.publishableKey},body:JSON.stringify({creditToken:i,timestamp:r.HashTimestamp||r.timestamp})});if(!n.ok){let l=await n.json().catch(()=>({}));throw new Error(l.error||"Failed to process payment")}let o=await n.json(),d={subscription:{id:o.subscription?.id||o.charge?.id||"",status:o.success?"active":"failed",planId:this.options.planId,planName:"",amount:o.charge?.amount||0,billingPeriod:o.subscription?.billingPeriod||"MONTHLY",trialDays:null},subscriber:{id:"",email:document.getElementById("recur-email")?.value||"",name:document.getElementById("recur-name")?.value||""},nextSteps:{getSdkToken:"",completeSubscription:""}};this.options.onSuccess&&this.options.onSuccess(d),this.showSuccess()}catch(e){this.handleError(e)}finally{this.showLoading(!1)}}showLoading(e){let t=document.getElementById("recur-submit-btn");t&&(t.disabled=e,t.textContent=e?"Processing...":"Subscribe Now")}showError(e){let t=document.getElementById("recur-error");t&&(t.textContent=e,t.style.display="block")}hideError(){let e=document.getElementById("recur-error");e&&(e.style.display="none")}showSuccess(){this.container.innerHTML="";let e=document.createElement("recur-success-message");e.setAttribute("title","Subscription Complete!"),e.setAttribute("message","Thank you for subscribing. You will receive a confirmation email shortly."),this.container.appendChild(e)}handleError(e){let t=e?.message||"An error occurred";this.showError(t);let r={code:"CHECKOUT_ERROR",message:t};this.options.onError&&this.options.onError(r)}};var y=class{constructor(e){a(this,"publishableKey");a(this,"baseUrl");a(this,"embedUrl");a(this,"iframe",null);a(this,"container",null);a(this,"sessionId",null);a(this,"timestamp",null);a(this,"creditToken",null);a(this,"cardToken",null);a(this,"cardTimestamp",null);a(this,"eventHandlers",new Map);typeof e=="string"?(this.publishableKey=e,this.baseUrl=this.getDefaultBaseUrl(),this.embedUrl=this.getDefaultEmbedUrl()):(this.publishableKey=e.publishableKey,this.baseUrl=e.baseUrl||this.getDefaultBaseUrl(),this.embedUrl=e.embedUrl||this.getDefaultEmbedUrl()),window.addEventListener("message",this.handleMessage.bind(this))}async mount(e){let t=typeof e=="string"?document.querySelector(e):e;if(!t)throw new Error(`Container not found: ${e}`);return this.container=t,this.iframe=document.createElement("iframe"),this.iframe.src=`${this.embedUrl}/elements?key=${encodeURIComponent(this.publishableKey)}`,this.iframe.style.cssText=`
|
|
1393
1393
|
width: 100%;
|
|
1394
1394
|
border: none;
|
|
1395
1395
|
min-height: 200px;
|
|
1396
1396
|
display: block;
|
|
1397
1397
|
user-select: none;
|
|
1398
1398
|
transition: height 0.35s ease, opacity 0.4s ease 0.1s;
|
|
1399
|
-
`.replace(/\s+/g," ").trim(),this.iframe.allow="payment *",this.iframe.setAttribute("title","Secure payment input frame"),this.iframe.setAttribute("role","presentation"),this.iframe.setAttribute("scrolling","no"),this.iframe.setAttribute("frameBorder","0"),this.container.innerHTML="",this.container.appendChild(this.iframe),new Promise((r,i)=>{let s=setTimeout(()=>{i(new Error("Elements initialization timeout"))},3e4),n=()=>{clearTimeout(s),this.off("ready",n),r()},o=
|
|
1399
|
+
`.replace(/\s+/g," ").trim(),this.iframe.allow="payment *",this.iframe.setAttribute("title","Secure payment input frame"),this.iframe.setAttribute("role","presentation"),this.iframe.setAttribute("scrolling","no"),this.iframe.setAttribute("frameBorder","0"),this.container.innerHTML="",this.container.appendChild(this.iframe),new Promise((r,i)=>{let s=setTimeout(()=>{i(new Error("Elements initialization timeout"))},3e4),n=()=>{clearTimeout(s),this.off("ready",n),r()},o=d=>{clearTimeout(s),this.off("error",o),i(new Error(d.message||"Elements initialization failed"))};this.on("ready",n),this.on("error",o)})}on(e,t){this.eventHandlers.has(e)||this.eventHandlers.set(e,new Set),this.eventHandlers.get(e).add(t)}off(e,t){let r=this.eventHandlers.get(e);r&&r.delete(t)}emit(e,t){let r=this.eventHandlers.get(e);r&&r.forEach(i=>i(t))}handleMessage(e){if(!this.isValidOrigin(e.origin))return;let{type:t,data:r}=e.data||{};switch(t){case"RECUR_ELEMENTS_READY":this.sessionId=r.sessionId,this.timestamp=r.timestamp,this.creditToken=r.creditToken,this.emit("ready",r);break;case"RECUR_CARD_VALID":this.emit("change",{valid:r.valid});break;case"RECUR_CARD_TOKENIZED":this.cardToken=r.cardToken,this.cardTimestamp=r.timestamp,this.emit("tokenized",r);break;case"RECUR_PAYMENT_ERROR":this.emit("error",r);break;case"RECUR_RESIZE":this.iframe&&r?.height&&(this.iframe.style.height=`${r.height}px`);break}}isValidOrigin(e){try{let t=new URL(this.embedUrl).origin;return e===t}catch{return!1}}async tokenize(){if(!this.iframe||!this.iframe.contentWindow)throw new Error("Elements not mounted");let e=new URL(this.embedUrl).origin;return this.iframe.contentWindow.postMessage({type:"RECUR_TOKENIZE"},e),new Promise((t,r)=>{let i=setTimeout(()=>{r(new Error("Tokenization timeout"))},6e4),s=o=>{clearTimeout(i),this.off("tokenized",s),t(o)},n=o=>{clearTimeout(i),this.off("error",n),r(new Error(o.message||"Tokenization failed"))};this.on("tokenized",s),this.on("error",n)})}async confirmPayment(e){if(!this.sessionId||!this.cardToken)throw new Error("Card not tokenized. Call tokenize() first.");let t=await fetch(`${this.baseUrl}/api/v1/elements/confirm`,{method:"POST",headers:{"Content-Type":"application/json","X-Recur-Publishable-Key":this.publishableKey},body:JSON.stringify({sessionId:this.sessionId,sdkToken:this.cardToken,timestamp:this.cardTimestamp,creditToken:this.creditToken,productId:e.productId,email:e.email,name:e.name,metadata:e.metadata})}),r=await t.json();return t.ok?r:{status:"failed",message:r.error||r.message||"Payment failed",canRetry:r.canRetry??!0}}getDefaultBaseUrl(){if(typeof window>"u")return"https://app.recur.tw";let e=window.location.hostname;return e==="localhost"||e.includes(".test")||e.includes(".local")||e==="127.0.0.1"?`${window.location.protocol}//${e}:3000`:"https://app.recur.tw"}getDefaultEmbedUrl(){if(typeof window>"u")return"https://embed.recur.tw";let e=window.location.hostname;return e==="localhost"||e.includes(".test")||e.includes(".local")||e==="127.0.0.1"?`${window.location.protocol}//${e}:3002`:"https://embed.recur.tw"}destroy(){this.iframe&&this.iframe.parentNode&&this.iframe.parentNode.removeChild(this.iframe),this.iframe=null,this.container=null,this.sessionId=null,this.timestamp=null,this.creditToken=null,this.cardToken=null,this.cardTimestamp=null,this.eventHandlers.clear(),window.removeEventListener("message",this.handleMessage.bind(this))}};function N(c){return new y(c)}var fe="https://vendor.payuni.com.tw/sdk/uni-payment.js",be="https://sandbox-vendor.payuni.com.tw/sdk/uni-payment.js",se=!1,z=!1,w=null;async function oe(c=!1){return se&&window.UniPayment?Promise.resolve():(z&&w||(z=!0,w=new Promise((e,t)=>{let r=document.createElement("script");r.src=c?be:fe,r.async=!0,r.onload=()=>{se=!0,z=!1,e()},r.onerror=()=>{z=!1,w=null,t(new Error("Failed to load PAYUNi SDK"))},document.head.appendChild(r)})),w)}var E=class{constructor(e){a(this,"core");a(this,"currentModal",null);a(this,"currentIframe",null);a(this,"currentModalOverlay",null);this.core=new D(e)}async fetchProducts(e){return await this.core.fetchProducts(e)}async fetchPlans(){return await this.core.fetchPlans()}async createEmbeddedCheckout(e){let t=this.core.getConfig();await new _(t,e).render()}async redirectToCheckout(e){let t=this.core.getConfig(),r=()=>{if(typeof window<"u"){let d=window.location.hostname;if(d==="localhost"||d.includes(".test")||d.includes(".local")||d==="127.0.0.1")return`${window.location.protocol}//${window.location.host}/api`}return"https://api.recur.tw"},i=t.baseUrl||r(),s={productId:e.productId,successUrl:e.successUrl,cancelUrl:e.cancelUrl};e.mode&&(s.mode=e.mode),e.customerEmail&&(s.customerEmail=e.customerEmail),e.externalCustomerId&&(s.externalCustomerId=e.externalCustomerId);let n=await fetch(`${i}/v1/checkout/sessions`,{method:"POST",headers:{"Content-Type":"application/json","X-Recur-Publishable-Key":t.publishableKey},body:JSON.stringify(s)});if(!n.ok){let d=await n.json().catch(()=>({}));throw new Error(d.error?.message||"Failed to create checkout session")}let o=await n.json();window.location.href=o.url}async createCheckoutSession(e){let t=this.core.getConfig(),r=()=>{if(typeof window<"u"){let o=window.location.hostname;if(o==="localhost"||o.includes(".test")||o.includes(".local")||o==="127.0.0.1")return`${window.location.protocol}//${window.location.host}/api`}return"https://api.recur.tw"},i=t.baseUrl||r(),s={productId:e.productId,successUrl:e.successUrl,cancelUrl:e.cancelUrl};e.mode&&(s.mode=e.mode),e.customerEmail&&(s.customerEmail=e.customerEmail),e.externalCustomerId&&(s.externalCustomerId=e.externalCustomerId);let n=await fetch(`${i}/v1/checkout/sessions`,{method:"POST",headers:{"Content-Type":"application/json","X-Recur-Publishable-Key":t.publishableKey},body:JSON.stringify(s)});if(!n.ok){let o=await n.json().catch(()=>({}));throw new Error(o.error?.message||"Failed to create checkout session")}return n.json()}async checkout(e){let t=this.core.getConfig(),r=null;try{if(console.log("[Recur SDK] Starting checkout flow...",{planId:e.planId,mode:e.mode}),!e.planId)throw new Error("planId is required");if(!t.publishableKey)throw new Error("publishableKey is required");if(!e.customerName)throw new Error("customerName is required");if(!e.customerEmail)throw new Error("customerEmail is required");let i=this.getBaseUrl();console.log("[Recur SDK] Base URL:",i);let s={"Content-Type":"application/json","X-Recur-Publishable-Key":t.publishableKey},n=e.mode||"modal",o=null;if(n==="modal"){let u=this.createModalWithSkeleton(e.onClose);r=u.overlay,o=u.container}else if(n==="iframe"){if(o=this.getEmbeddedContainer(e.container),!o)throw new Error("Container is required for iframe mode");o.innerHTML="";let u=document.createElement("recur-payment-form-skeleton");o.appendChild(u)}console.log("[Recur SDK] Step 1: Creating checkout session...");let d=await fetch(`${i}/v1/checkouts`,{method:"POST",headers:s,body:JSON.stringify({productId:e.planId,customerName:e.customerName,customerEmail:e.customerEmail,externalCustomerId:e.externalCustomerId})});if(!d.ok){let u=await d.json().catch(()=>({}));console.error("[Recur SDK] Failed to create checkout:",u);let A=u.details||u.error||"Failed to create checkout";throw new Error(A)}let l=await d.json();if(console.log("[Recur SDK] Checkout created successfully:",l),e.onSuccess?.(l),n==="redirect"){let u=`https://checkout.recur.tw/${l.checkout.id}`;console.log("[Recur SDK] Redirecting to hosted checkout:",u),window.location.href=u;return}if(console.log("[Recur SDK] Step 2: Extracting SDK token..."),!l.sdkToken)throw new Error("SDK token missing from checkout response");console.log("[Recur SDK] Step 3: Loading PAYUNi SDK...");let v=!0;if(await oe(v),console.log("[Recur SDK] PAYUNi SDK loaded successfully"),!window.UniPayment)throw new Error("PAYUNi SDK failed to load");if(console.log("[Recur SDK] Step 4: Rendering payment form..."),!o)throw new Error("Payment container not available");o.innerHTML="";let m=document.createElement("recur-payment-form");if(m.setAttribute("container-id",o.id||"recur-payment-container"),e.customerName&&m.setAttribute("customer-name",e.customerName),e.customerEmail&&m.setAttribute("customer-email",e.customerEmail),l.plan?.name&&m.setAttribute("plan-name",l.plan.name),l.checkout?.amount&&m.setAttribute("amount",l.checkout.amount.toString()),l.plan?.billingPeriod&&m.setAttribute("billing-period",l.plan.billingPeriod),m.setAttribute("custom-styles",`
|
|
1400
1400
|
.form-input-focus {
|
|
1401
1401
|
border-color: var(--ring, hsl(215 16% 47%)) !important;
|
|
1402
1402
|
outline: 0 !important;
|
|
1403
1403
|
box-shadow: 0 0 0 3px color-mix(in oklch, var(--ring, hsl(215 16% 47%)) 50%, transparent) !important;
|
|
1404
1404
|
transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out !important;
|
|
1405
1405
|
}
|
|
1406
|
-
`),o.appendChild(m),await new Promise(u=>setTimeout(u,100)),console.log("[Recur SDK] Step 5: Initializing PAYUNi SDK..."),await m.initializePayment(
|
|
1406
|
+
`),o.appendChild(m),await new Promise(u=>setTimeout(u,100)),console.log("[Recur SDK] Step 5: Initializing PAYUNi SDK..."),await m.initializePayment(l.sdkToken,v?"SANDBOX":"PRODUCTION")===null){console.log("[Recur SDK] Initialization aborted");return}console.log("[Recur SDK] PAYUNi SDK initialized successfully"),console.log("[Recur SDK] Step 6: Setting up submit handler..."),m.addEventListener("submit",(async u=>{console.log("[Recur SDK] Form submitted");let A=u,{paymentSession:ae}=A.detail;try{let g=await ae.getTradeResult();console.log("[Recur SDK] Trade result received"),console.log("[Recur SDK] Executing payment...");let H=g.HashTimestamp||g.timestamp,B={};if(l.checkout.productType==="SUBSCRIPTION"){let k=l.creditToken,K=l.sdkTimestamp;if(!k)throw console.error("[Recur SDK] Missing creditToken from checkout for subscription"),new Error("Missing creditToken for subscription payment");B={creditToken:k,timestamp:K||H},console.log("[Recur SDK] Using creditToken from checkout:",k.substring(0,30)+"..."),console.log("[Recur SDK] Using timestamp:",K?"from checkout (sdkTimestamp)":"from tradeResult")}let $=await fetch(`${i}/v1/checkouts/${l.checkout.id}/pay`,{method:"POST",headers:s,body:JSON.stringify(B)});if(!$.ok){let k=await $.json().catch(()=>({}));throw new Error(k.error||"Failed to execute payment")}let p=await $.json();if(console.log("[Recur SDK] Payment executed:",p),p.requires3D&&p.redirectUrl){console.log("[Recur SDK] 3D verification required"),window.location.href=p.redirectUrl;return}e.onPaymentComplete&&(p.subscription?e.onPaymentComplete({id:p.subscription.id,status:p.subscription.status,planId:l.checkout.productId,amount:l.checkout.amount,billingPeriod:p.subscription.billingPeriod,currentPeriodStart:p.subscription.currentPeriodStart,currentPeriodEnd:p.subscription.currentPeriodEnd}):e.onPaymentComplete({id:p.checkout.id,status:p.checkout.status,planId:l.checkout.productId,amount:l.checkout.amount,billingPeriod:l.checkout.productType})),console.log("[Recur SDK] Checkout flow completed successfully!"),m.resetButton?.(),r&&r.remove()}catch(g){console.error("[Recur SDK] Payment error:",g);let H={code:"PAYMENT_FAILED",message:g instanceof Error?g.message:"Payment failed"};e.onError?.(H),m.resetButton?.()}})),console.log("[Recur SDK] Checkout flow initialized, waiting for user input...")}catch(i){console.error("[Recur SDK] Checkout error:",i),r&&r.remove();let s={code:"CHECKOUT_ERROR",message:i instanceof Error?i.message:"An unknown error occurred"};throw e.onError?.(s),i}}getBaseUrl(){let e=this.core.getConfig();if(e.baseUrl)return e.baseUrl;if(typeof window<"u"){let t=window.location.hostname;if(t==="localhost"||t.includes(".test")||t.includes(".local")||t==="127.0.0.1")return`${window.location.protocol}//${window.location.host}/api`}return"https://api.recur.tw"}createModalWithSkeleton(e){this.closeModal();let t=document.createElement("div");t.id="recur-modal-overlay",t.style.cssText=`
|
|
1407
1407
|
position: fixed;
|
|
1408
1408
|
top: 0;
|
|
1409
1409
|
left: 0;
|
|
@@ -1442,7 +1442,7 @@
|
|
|
1442
1442
|
background: white;
|
|
1443
1443
|
border-radius: 12px;
|
|
1444
1444
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
|
1445
|
-
`;let n=document.createElement("recur-payment-form-skeleton");return s.appendChild(n),r.appendChild(i),r.appendChild(s),t.appendChild(r),document.body.appendChild(t),this.currentModalOverlay=t,{overlay:t,container:s}}getEmbeddedContainer(e){return e?typeof e=="string"?document.getElementById(e)||document.querySelector(e):e:null}closeModal(){this.currentModal&&(this.currentModal.close(),this.currentModal=null),this.currentModalOverlay&&(this.currentModalOverlay.remove(),this.currentModalOverlay=null)}removeIframe(){this.currentIframe&&(this.currentIframe.remove(),this.currentIframe=null)}close(){this.closeModal(),this.removeIframe()}};function
|
|
1445
|
+
`;let n=document.createElement("recur-payment-form-skeleton");return s.appendChild(n),r.appendChild(i),r.appendChild(s),t.appendChild(r),document.body.appendChild(t),this.currentModalOverlay=t,{overlay:t,container:s}}getEmbeddedContainer(e){return e?typeof e=="string"?document.getElementById(e)||document.querySelector(e):e:null}closeModal(){this.currentModal&&(this.currentModal.close(),this.currentModal=null),this.currentModalOverlay&&(this.currentModalOverlay.remove(),this.currentModalOverlay=null)}removeIframe(){this.currentIframe&&(this.currentIframe.remove(),this.currentIframe=null)}close(){this.closeModal(),this.removeIframe()}};function ne(c){return new E(c)}var ge={init:ne,RecurCheckout:E,RecurElements:y,createElements:N};return pe(ye);})();
|
|
1446
1446
|
if (typeof window !== "undefined") {
|
|
1447
1447
|
window.RecurCheckout = RecurCheckout.default;
|
|
1448
1448
|
window.RecurElements = RecurCheckout.RecurElements;
|