recur-tw 0.8.8 → 0.9.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/dist/index.cjs +188 -5
- package/dist/index.d.cts +5 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +188 -5
- package/dist/recur.umd.js +20 -17
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -1901,6 +1901,24 @@ var init_payment_form = __esm({
|
|
|
1901
1901
|
resetButton() {
|
|
1902
1902
|
this.setButtonLoading(false);
|
|
1903
1903
|
}
|
|
1904
|
+
/**
|
|
1905
|
+
* Set verifying state (for 3D Secure verification)
|
|
1906
|
+
* Shows a "verifying" message while polling for 3D result
|
|
1907
|
+
*/
|
|
1908
|
+
setVerifying(verifying) {
|
|
1909
|
+
const submitBtn = document.getElementById(`${this.containerId}-submit-btn`);
|
|
1910
|
+
if (!submitBtn) return;
|
|
1911
|
+
if (verifying) {
|
|
1912
|
+
submitBtn.classList.add("loading");
|
|
1913
|
+
submitBtn.disabled = true;
|
|
1914
|
+
submitBtn.innerHTML = `
|
|
1915
|
+
<span class="recur-loading-spinner"></span>
|
|
1916
|
+
<span>3D \u9A57\u8B49\u4E2D...</span>
|
|
1917
|
+
`;
|
|
1918
|
+
} else {
|
|
1919
|
+
this.setButtonLoading(false);
|
|
1920
|
+
}
|
|
1921
|
+
}
|
|
1904
1922
|
/**
|
|
1905
1923
|
* Show error message using RecurErrorDisplay component
|
|
1906
1924
|
*/
|
|
@@ -2598,7 +2616,11 @@ function toCamelCase(obj) {
|
|
|
2598
2616
|
}
|
|
2599
2617
|
return obj;
|
|
2600
2618
|
}
|
|
2601
|
-
|
|
2619
|
+
|
|
2620
|
+
// package.json
|
|
2621
|
+
var package_default = {
|
|
2622
|
+
version: "0.9.2"};
|
|
2623
|
+
var SDK_VERSION = package_default.version;
|
|
2602
2624
|
var SDK_TYPE = "react";
|
|
2603
2625
|
var RecurContext = React.createContext(null);
|
|
2604
2626
|
function RecurProvider({ children, config: initialConfig = {} }) {
|
|
@@ -2925,12 +2947,173 @@ function RecurProvider({ children, config: initialConfig = {} }) {
|
|
|
2925
2947
|
console.error("[Recur SDK] Failed to execute payment:", errorData);
|
|
2926
2948
|
throw new Error(errorData.error || "Failed to execute payment");
|
|
2927
2949
|
}
|
|
2928
|
-
const
|
|
2950
|
+
const rawPaymentResult = await paymentResponse.json();
|
|
2951
|
+
const paymentResult = toCamelCase(rawPaymentResult);
|
|
2929
2952
|
console.log("[Recur SDK] Payment executed:", paymentResult);
|
|
2930
2953
|
if (paymentResult.requires3D && paymentResult.redirectUrl) {
|
|
2931
|
-
console.log("[Recur SDK] 3D verification required
|
|
2932
|
-
|
|
2933
|
-
|
|
2954
|
+
console.log("[Recur SDK] 3D verification required");
|
|
2955
|
+
console.log("[Recur SDK] Using popup for 3D verification");
|
|
2956
|
+
{
|
|
2957
|
+
const popup = window.open(
|
|
2958
|
+
paymentResult.redirectUrl,
|
|
2959
|
+
"recur_3d_verification",
|
|
2960
|
+
"width=500,height=700,scrollbars=yes,resizable=yes"
|
|
2961
|
+
);
|
|
2962
|
+
if (!popup) {
|
|
2963
|
+
console.log("[Recur SDK] Popup was blocked (null), falling back to redirect");
|
|
2964
|
+
window.location.href = paymentResult.redirectUrl;
|
|
2965
|
+
return;
|
|
2966
|
+
}
|
|
2967
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
2968
|
+
if (popup.closed) {
|
|
2969
|
+
console.log("[Recur SDK] Popup was closed immediately, falling back to redirect");
|
|
2970
|
+
window.location.href = paymentResult.redirectUrl;
|
|
2971
|
+
return;
|
|
2972
|
+
}
|
|
2973
|
+
paymentForm.setVerifying?.(true);
|
|
2974
|
+
const checkoutId = checkoutResult.checkout.id;
|
|
2975
|
+
const clientSecret = checkoutResult.checkout.clientSecret;
|
|
2976
|
+
const maxAttempts = 90;
|
|
2977
|
+
const pollInterval = 2e3;
|
|
2978
|
+
let polling = true;
|
|
2979
|
+
let succeededViaPopupClose = false;
|
|
2980
|
+
let finalOrderId;
|
|
2981
|
+
console.log("[Recur SDK] Starting 3D verification polling...");
|
|
2982
|
+
for (let attempt = 0; attempt < maxAttempts && polling; attempt++) {
|
|
2983
|
+
if (popup.closed) {
|
|
2984
|
+
console.log("[Recur SDK] Popup was closed");
|
|
2985
|
+
try {
|
|
2986
|
+
const statusResponse = await fetch(
|
|
2987
|
+
`${baseUrl}/v1/checkouts/${checkoutId}/status?client_secret=${encodeURIComponent(clientSecret)}`,
|
|
2988
|
+
{ headers }
|
|
2989
|
+
);
|
|
2990
|
+
if (statusResponse.ok) {
|
|
2991
|
+
const statusData = await statusResponse.json();
|
|
2992
|
+
if (statusData.checkout?.status === "SUCCEEDED") {
|
|
2993
|
+
console.log("[Recur SDK] Payment succeeded after popup close");
|
|
2994
|
+
polling = false;
|
|
2995
|
+
succeededViaPopupClose = true;
|
|
2996
|
+
finalOrderId = statusData.checkout?.orderId;
|
|
2997
|
+
break;
|
|
2998
|
+
}
|
|
2999
|
+
}
|
|
3000
|
+
} catch (e) {
|
|
3001
|
+
console.error("[Recur SDK] Final status check failed:", e);
|
|
3002
|
+
}
|
|
3003
|
+
console.log("[Recur SDK] Payment not confirmed, user closed popup");
|
|
3004
|
+
paymentForm.setVerifying?.(false);
|
|
3005
|
+
paymentForm.resetButton?.();
|
|
3006
|
+
throw new Error("3D \u9A57\u8B49\u5DF2\u53D6\u6D88");
|
|
3007
|
+
}
|
|
3008
|
+
try {
|
|
3009
|
+
const statusResponse = await fetch(
|
|
3010
|
+
`${baseUrl}/v1/checkouts/${checkoutId}/status?client_secret=${encodeURIComponent(clientSecret)}`,
|
|
3011
|
+
{ headers }
|
|
3012
|
+
);
|
|
3013
|
+
if (statusResponse.ok) {
|
|
3014
|
+
const statusData = await statusResponse.json();
|
|
3015
|
+
const status = statusData.checkout?.status;
|
|
3016
|
+
console.log(`[Recur SDK] Poll ${attempt + 1}: status = ${status}`);
|
|
3017
|
+
if (status === "SUCCEEDED") {
|
|
3018
|
+
console.log("[Recur SDK] Payment succeeded");
|
|
3019
|
+
polling = false;
|
|
3020
|
+
try {
|
|
3021
|
+
popup.close();
|
|
3022
|
+
} catch {
|
|
3023
|
+
}
|
|
3024
|
+
paymentForm.setVerifying?.(false);
|
|
3025
|
+
if (options.onPaymentComplete) {
|
|
3026
|
+
if (paymentResult.subscription) {
|
|
3027
|
+
options.onPaymentComplete({
|
|
3028
|
+
id: paymentResult.subscription.id,
|
|
3029
|
+
status: "ACTIVE",
|
|
3030
|
+
planId: checkoutResult.checkout.productId,
|
|
3031
|
+
amount: checkoutResult.checkout.amount,
|
|
3032
|
+
billingPeriod: paymentResult.subscription.billingPeriod,
|
|
3033
|
+
currentPeriodStart: paymentResult.subscription.currentPeriodStart,
|
|
3034
|
+
currentPeriodEnd: paymentResult.subscription.currentPeriodEnd
|
|
3035
|
+
});
|
|
3036
|
+
} else {
|
|
3037
|
+
options.onPaymentComplete({
|
|
3038
|
+
id: statusData.checkout?.orderId || checkoutId,
|
|
3039
|
+
status: "SUCCEEDED",
|
|
3040
|
+
planId: checkoutResult.checkout.productId,
|
|
3041
|
+
amount: checkoutResult.checkout.amount,
|
|
3042
|
+
billingPeriod: checkoutResult.checkout.productType,
|
|
3043
|
+
currentPeriodStart: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3044
|
+
currentPeriodEnd: (/* @__PURE__ */ new Date()).toISOString()
|
|
3045
|
+
});
|
|
3046
|
+
}
|
|
3047
|
+
}
|
|
3048
|
+
if (modalOverlay) {
|
|
3049
|
+
modalOverlay.remove();
|
|
3050
|
+
}
|
|
3051
|
+
setIsCheckingOut(false);
|
|
3052
|
+
return;
|
|
3053
|
+
}
|
|
3054
|
+
if (status === "CANCELED" || status === "FAILED") {
|
|
3055
|
+
console.log("[Recur SDK] Payment failed or canceled");
|
|
3056
|
+
polling = false;
|
|
3057
|
+
try {
|
|
3058
|
+
popup.close();
|
|
3059
|
+
} catch {
|
|
3060
|
+
}
|
|
3061
|
+
paymentForm.setVerifying?.(false);
|
|
3062
|
+
paymentForm.resetButton?.();
|
|
3063
|
+
throw new Error("\u4ED8\u6B3E\u5931\u6557\u6216\u5DF2\u53D6\u6D88");
|
|
3064
|
+
}
|
|
3065
|
+
}
|
|
3066
|
+
} catch (pollError) {
|
|
3067
|
+
if (pollError.message === "3D \u9A57\u8B49\u5DF2\u53D6\u6D88" || pollError.message === "\u4ED8\u6B3E\u5931\u6557\u6216\u5DF2\u53D6\u6D88") {
|
|
3068
|
+
throw pollError;
|
|
3069
|
+
}
|
|
3070
|
+
console.error("[Recur SDK] Poll error:", pollError);
|
|
3071
|
+
}
|
|
3072
|
+
await new Promise((resolve) => setTimeout(resolve, pollInterval));
|
|
3073
|
+
}
|
|
3074
|
+
if (polling) {
|
|
3075
|
+
console.log("[Recur SDK] 3D verification polling timeout");
|
|
3076
|
+
try {
|
|
3077
|
+
popup.close();
|
|
3078
|
+
} catch {
|
|
3079
|
+
}
|
|
3080
|
+
paymentForm.setVerifying?.(false);
|
|
3081
|
+
paymentForm.resetButton?.();
|
|
3082
|
+
throw new Error("3D \u9A57\u8B49\u903E\u6642\uFF0C\u8ACB\u91CD\u8A66");
|
|
3083
|
+
}
|
|
3084
|
+
if (succeededViaPopupClose) {
|
|
3085
|
+
console.log("[Recur SDK] Handling success after popup close");
|
|
3086
|
+
paymentForm.setVerifying?.(false);
|
|
3087
|
+
if (options.onPaymentComplete) {
|
|
3088
|
+
if (paymentResult.subscription) {
|
|
3089
|
+
options.onPaymentComplete({
|
|
3090
|
+
id: paymentResult.subscription.id,
|
|
3091
|
+
status: "ACTIVE",
|
|
3092
|
+
planId: checkoutResult.checkout.productId,
|
|
3093
|
+
amount: checkoutResult.checkout.amount,
|
|
3094
|
+
billingPeriod: paymentResult.subscription.billingPeriod,
|
|
3095
|
+
currentPeriodStart: paymentResult.subscription.currentPeriodStart,
|
|
3096
|
+
currentPeriodEnd: paymentResult.subscription.currentPeriodEnd
|
|
3097
|
+
});
|
|
3098
|
+
} else {
|
|
3099
|
+
options.onPaymentComplete({
|
|
3100
|
+
id: finalOrderId || checkoutId,
|
|
3101
|
+
status: "SUCCEEDED",
|
|
3102
|
+
planId: checkoutResult.checkout.productId,
|
|
3103
|
+
amount: checkoutResult.checkout.amount,
|
|
3104
|
+
billingPeriod: checkoutResult.checkout.productType,
|
|
3105
|
+
currentPeriodStart: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3106
|
+
currentPeriodEnd: (/* @__PURE__ */ new Date()).toISOString()
|
|
3107
|
+
});
|
|
3108
|
+
}
|
|
3109
|
+
}
|
|
3110
|
+
if (modalOverlay) {
|
|
3111
|
+
modalOverlay.remove();
|
|
3112
|
+
}
|
|
3113
|
+
setIsCheckingOut(false);
|
|
3114
|
+
}
|
|
3115
|
+
return;
|
|
3116
|
+
}
|
|
2934
3117
|
}
|
|
2935
3118
|
console.log("[Recur SDK] Calling onPaymentComplete callback...");
|
|
2936
3119
|
if (options.onPaymentComplete) {
|
package/dist/index.d.cts
CHANGED
|
@@ -505,6 +505,11 @@ declare class RecurPaymentForm extends HTMLElement {
|
|
|
505
505
|
* Reset button to normal state (call this when payment completes or fails)
|
|
506
506
|
*/
|
|
507
507
|
resetButton(): void;
|
|
508
|
+
/**
|
|
509
|
+
* Set verifying state (for 3D Secure verification)
|
|
510
|
+
* Shows a "verifying" message while polling for 3D result
|
|
511
|
+
*/
|
|
512
|
+
setVerifying(verifying: boolean): void;
|
|
508
513
|
/**
|
|
509
514
|
* Show error message using RecurErrorDisplay component
|
|
510
515
|
*/
|
package/dist/index.d.ts
CHANGED
|
@@ -505,6 +505,11 @@ declare class RecurPaymentForm extends HTMLElement {
|
|
|
505
505
|
* Reset button to normal state (call this when payment completes or fails)
|
|
506
506
|
*/
|
|
507
507
|
resetButton(): void;
|
|
508
|
+
/**
|
|
509
|
+
* Set verifying state (for 3D Secure verification)
|
|
510
|
+
* Shows a "verifying" message while polling for 3D result
|
|
511
|
+
*/
|
|
512
|
+
setVerifying(verifying: boolean): void;
|
|
508
513
|
/**
|
|
509
514
|
* Show error message using RecurErrorDisplay component
|
|
510
515
|
*/
|
package/dist/index.js
CHANGED
|
@@ -1895,6 +1895,24 @@ var init_payment_form = __esm({
|
|
|
1895
1895
|
resetButton() {
|
|
1896
1896
|
this.setButtonLoading(false);
|
|
1897
1897
|
}
|
|
1898
|
+
/**
|
|
1899
|
+
* Set verifying state (for 3D Secure verification)
|
|
1900
|
+
* Shows a "verifying" message while polling for 3D result
|
|
1901
|
+
*/
|
|
1902
|
+
setVerifying(verifying) {
|
|
1903
|
+
const submitBtn = document.getElementById(`${this.containerId}-submit-btn`);
|
|
1904
|
+
if (!submitBtn) return;
|
|
1905
|
+
if (verifying) {
|
|
1906
|
+
submitBtn.classList.add("loading");
|
|
1907
|
+
submitBtn.disabled = true;
|
|
1908
|
+
submitBtn.innerHTML = `
|
|
1909
|
+
<span class="recur-loading-spinner"></span>
|
|
1910
|
+
<span>3D \u9A57\u8B49\u4E2D...</span>
|
|
1911
|
+
`;
|
|
1912
|
+
} else {
|
|
1913
|
+
this.setButtonLoading(false);
|
|
1914
|
+
}
|
|
1915
|
+
}
|
|
1898
1916
|
/**
|
|
1899
1917
|
* Show error message using RecurErrorDisplay component
|
|
1900
1918
|
*/
|
|
@@ -2592,7 +2610,11 @@ function toCamelCase(obj) {
|
|
|
2592
2610
|
}
|
|
2593
2611
|
return obj;
|
|
2594
2612
|
}
|
|
2595
|
-
|
|
2613
|
+
|
|
2614
|
+
// package.json
|
|
2615
|
+
var package_default = {
|
|
2616
|
+
version: "0.9.2"};
|
|
2617
|
+
var SDK_VERSION = package_default.version;
|
|
2596
2618
|
var SDK_TYPE = "react";
|
|
2597
2619
|
var RecurContext = createContext(null);
|
|
2598
2620
|
function RecurProvider({ children, config: initialConfig = {} }) {
|
|
@@ -2919,12 +2941,173 @@ function RecurProvider({ children, config: initialConfig = {} }) {
|
|
|
2919
2941
|
console.error("[Recur SDK] Failed to execute payment:", errorData);
|
|
2920
2942
|
throw new Error(errorData.error || "Failed to execute payment");
|
|
2921
2943
|
}
|
|
2922
|
-
const
|
|
2944
|
+
const rawPaymentResult = await paymentResponse.json();
|
|
2945
|
+
const paymentResult = toCamelCase(rawPaymentResult);
|
|
2923
2946
|
console.log("[Recur SDK] Payment executed:", paymentResult);
|
|
2924
2947
|
if (paymentResult.requires3D && paymentResult.redirectUrl) {
|
|
2925
|
-
console.log("[Recur SDK] 3D verification required
|
|
2926
|
-
|
|
2927
|
-
|
|
2948
|
+
console.log("[Recur SDK] 3D verification required");
|
|
2949
|
+
console.log("[Recur SDK] Using popup for 3D verification");
|
|
2950
|
+
{
|
|
2951
|
+
const popup = window.open(
|
|
2952
|
+
paymentResult.redirectUrl,
|
|
2953
|
+
"recur_3d_verification",
|
|
2954
|
+
"width=500,height=700,scrollbars=yes,resizable=yes"
|
|
2955
|
+
);
|
|
2956
|
+
if (!popup) {
|
|
2957
|
+
console.log("[Recur SDK] Popup was blocked (null), falling back to redirect");
|
|
2958
|
+
window.location.href = paymentResult.redirectUrl;
|
|
2959
|
+
return;
|
|
2960
|
+
}
|
|
2961
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
2962
|
+
if (popup.closed) {
|
|
2963
|
+
console.log("[Recur SDK] Popup was closed immediately, falling back to redirect");
|
|
2964
|
+
window.location.href = paymentResult.redirectUrl;
|
|
2965
|
+
return;
|
|
2966
|
+
}
|
|
2967
|
+
paymentForm.setVerifying?.(true);
|
|
2968
|
+
const checkoutId = checkoutResult.checkout.id;
|
|
2969
|
+
const clientSecret = checkoutResult.checkout.clientSecret;
|
|
2970
|
+
const maxAttempts = 90;
|
|
2971
|
+
const pollInterval = 2e3;
|
|
2972
|
+
let polling = true;
|
|
2973
|
+
let succeededViaPopupClose = false;
|
|
2974
|
+
let finalOrderId;
|
|
2975
|
+
console.log("[Recur SDK] Starting 3D verification polling...");
|
|
2976
|
+
for (let attempt = 0; attempt < maxAttempts && polling; attempt++) {
|
|
2977
|
+
if (popup.closed) {
|
|
2978
|
+
console.log("[Recur SDK] Popup was closed");
|
|
2979
|
+
try {
|
|
2980
|
+
const statusResponse = await fetch(
|
|
2981
|
+
`${baseUrl}/v1/checkouts/${checkoutId}/status?client_secret=${encodeURIComponent(clientSecret)}`,
|
|
2982
|
+
{ headers }
|
|
2983
|
+
);
|
|
2984
|
+
if (statusResponse.ok) {
|
|
2985
|
+
const statusData = await statusResponse.json();
|
|
2986
|
+
if (statusData.checkout?.status === "SUCCEEDED") {
|
|
2987
|
+
console.log("[Recur SDK] Payment succeeded after popup close");
|
|
2988
|
+
polling = false;
|
|
2989
|
+
succeededViaPopupClose = true;
|
|
2990
|
+
finalOrderId = statusData.checkout?.orderId;
|
|
2991
|
+
break;
|
|
2992
|
+
}
|
|
2993
|
+
}
|
|
2994
|
+
} catch (e) {
|
|
2995
|
+
console.error("[Recur SDK] Final status check failed:", e);
|
|
2996
|
+
}
|
|
2997
|
+
console.log("[Recur SDK] Payment not confirmed, user closed popup");
|
|
2998
|
+
paymentForm.setVerifying?.(false);
|
|
2999
|
+
paymentForm.resetButton?.();
|
|
3000
|
+
throw new Error("3D \u9A57\u8B49\u5DF2\u53D6\u6D88");
|
|
3001
|
+
}
|
|
3002
|
+
try {
|
|
3003
|
+
const statusResponse = await fetch(
|
|
3004
|
+
`${baseUrl}/v1/checkouts/${checkoutId}/status?client_secret=${encodeURIComponent(clientSecret)}`,
|
|
3005
|
+
{ headers }
|
|
3006
|
+
);
|
|
3007
|
+
if (statusResponse.ok) {
|
|
3008
|
+
const statusData = await statusResponse.json();
|
|
3009
|
+
const status = statusData.checkout?.status;
|
|
3010
|
+
console.log(`[Recur SDK] Poll ${attempt + 1}: status = ${status}`);
|
|
3011
|
+
if (status === "SUCCEEDED") {
|
|
3012
|
+
console.log("[Recur SDK] Payment succeeded");
|
|
3013
|
+
polling = false;
|
|
3014
|
+
try {
|
|
3015
|
+
popup.close();
|
|
3016
|
+
} catch {
|
|
3017
|
+
}
|
|
3018
|
+
paymentForm.setVerifying?.(false);
|
|
3019
|
+
if (options.onPaymentComplete) {
|
|
3020
|
+
if (paymentResult.subscription) {
|
|
3021
|
+
options.onPaymentComplete({
|
|
3022
|
+
id: paymentResult.subscription.id,
|
|
3023
|
+
status: "ACTIVE",
|
|
3024
|
+
planId: checkoutResult.checkout.productId,
|
|
3025
|
+
amount: checkoutResult.checkout.amount,
|
|
3026
|
+
billingPeriod: paymentResult.subscription.billingPeriod,
|
|
3027
|
+
currentPeriodStart: paymentResult.subscription.currentPeriodStart,
|
|
3028
|
+
currentPeriodEnd: paymentResult.subscription.currentPeriodEnd
|
|
3029
|
+
});
|
|
3030
|
+
} else {
|
|
3031
|
+
options.onPaymentComplete({
|
|
3032
|
+
id: statusData.checkout?.orderId || checkoutId,
|
|
3033
|
+
status: "SUCCEEDED",
|
|
3034
|
+
planId: checkoutResult.checkout.productId,
|
|
3035
|
+
amount: checkoutResult.checkout.amount,
|
|
3036
|
+
billingPeriod: checkoutResult.checkout.productType,
|
|
3037
|
+
currentPeriodStart: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3038
|
+
currentPeriodEnd: (/* @__PURE__ */ new Date()).toISOString()
|
|
3039
|
+
});
|
|
3040
|
+
}
|
|
3041
|
+
}
|
|
3042
|
+
if (modalOverlay) {
|
|
3043
|
+
modalOverlay.remove();
|
|
3044
|
+
}
|
|
3045
|
+
setIsCheckingOut(false);
|
|
3046
|
+
return;
|
|
3047
|
+
}
|
|
3048
|
+
if (status === "CANCELED" || status === "FAILED") {
|
|
3049
|
+
console.log("[Recur SDK] Payment failed or canceled");
|
|
3050
|
+
polling = false;
|
|
3051
|
+
try {
|
|
3052
|
+
popup.close();
|
|
3053
|
+
} catch {
|
|
3054
|
+
}
|
|
3055
|
+
paymentForm.setVerifying?.(false);
|
|
3056
|
+
paymentForm.resetButton?.();
|
|
3057
|
+
throw new Error("\u4ED8\u6B3E\u5931\u6557\u6216\u5DF2\u53D6\u6D88");
|
|
3058
|
+
}
|
|
3059
|
+
}
|
|
3060
|
+
} catch (pollError) {
|
|
3061
|
+
if (pollError.message === "3D \u9A57\u8B49\u5DF2\u53D6\u6D88" || pollError.message === "\u4ED8\u6B3E\u5931\u6557\u6216\u5DF2\u53D6\u6D88") {
|
|
3062
|
+
throw pollError;
|
|
3063
|
+
}
|
|
3064
|
+
console.error("[Recur SDK] Poll error:", pollError);
|
|
3065
|
+
}
|
|
3066
|
+
await new Promise((resolve) => setTimeout(resolve, pollInterval));
|
|
3067
|
+
}
|
|
3068
|
+
if (polling) {
|
|
3069
|
+
console.log("[Recur SDK] 3D verification polling timeout");
|
|
3070
|
+
try {
|
|
3071
|
+
popup.close();
|
|
3072
|
+
} catch {
|
|
3073
|
+
}
|
|
3074
|
+
paymentForm.setVerifying?.(false);
|
|
3075
|
+
paymentForm.resetButton?.();
|
|
3076
|
+
throw new Error("3D \u9A57\u8B49\u903E\u6642\uFF0C\u8ACB\u91CD\u8A66");
|
|
3077
|
+
}
|
|
3078
|
+
if (succeededViaPopupClose) {
|
|
3079
|
+
console.log("[Recur SDK] Handling success after popup close");
|
|
3080
|
+
paymentForm.setVerifying?.(false);
|
|
3081
|
+
if (options.onPaymentComplete) {
|
|
3082
|
+
if (paymentResult.subscription) {
|
|
3083
|
+
options.onPaymentComplete({
|
|
3084
|
+
id: paymentResult.subscription.id,
|
|
3085
|
+
status: "ACTIVE",
|
|
3086
|
+
planId: checkoutResult.checkout.productId,
|
|
3087
|
+
amount: checkoutResult.checkout.amount,
|
|
3088
|
+
billingPeriod: paymentResult.subscription.billingPeriod,
|
|
3089
|
+
currentPeriodStart: paymentResult.subscription.currentPeriodStart,
|
|
3090
|
+
currentPeriodEnd: paymentResult.subscription.currentPeriodEnd
|
|
3091
|
+
});
|
|
3092
|
+
} else {
|
|
3093
|
+
options.onPaymentComplete({
|
|
3094
|
+
id: finalOrderId || checkoutId,
|
|
3095
|
+
status: "SUCCEEDED",
|
|
3096
|
+
planId: checkoutResult.checkout.productId,
|
|
3097
|
+
amount: checkoutResult.checkout.amount,
|
|
3098
|
+
billingPeriod: checkoutResult.checkout.productType,
|
|
3099
|
+
currentPeriodStart: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3100
|
+
currentPeriodEnd: (/* @__PURE__ */ new Date()).toISOString()
|
|
3101
|
+
});
|
|
3102
|
+
}
|
|
3103
|
+
}
|
|
3104
|
+
if (modalOverlay) {
|
|
3105
|
+
modalOverlay.remove();
|
|
3106
|
+
}
|
|
3107
|
+
setIsCheckingOut(false);
|
|
3108
|
+
}
|
|
3109
|
+
return;
|
|
3110
|
+
}
|
|
2928
3111
|
}
|
|
2929
3112
|
console.log("[Recur SDK] Calling onPaymentComplete callback...");
|
|
2930
3113
|
if (options.onPaymentComplete) {
|
package/dist/recur.umd.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"use strict";var RecurCheckout=(()=>{var
|
|
1
|
+
"use strict";var RecurCheckout=(()=>{var A=Object.defineProperty;var Re=Object.getOwnPropertyDescriptor;var Pe=Object.getOwnPropertyNames;var De=Object.prototype.hasOwnProperty;var Le=(a,e,t)=>e in a?A(a,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):a[e]=t;var w=(a,e)=>()=>(a&&(e=a(a=0)),e);var k=(a,e)=>{for(var t in e)A(a,t,{get:e[t],enumerable:!0})},Ue=(a,e,t,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let s of Pe(e))!De.call(a,s)&&s!==t&&A(a,s,{get:()=>e[s],enumerable:!(r=Re(e,s))||r.enumerable});return a};var Me=a=>Ue(A({},"__esModule",{value:!0}),a);var c=(a,e,t)=>Le(a,typeof e!="symbol"?e+"":e,t);var re={};k(re,{RecurLoadingSpinner:()=>_});var _,se=w(()=>{"use strict";_=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",
|
|
43
|
+
`}};typeof window<"u"&&!customElements.get("recur-loading-spinner")&&customElements.define("recur-loading-spinner",_)});var ie={};k(ie,{RecurSuccessMessage:()=>z});var z,oe=w(()=>{"use strict";z=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",
|
|
124
|
+
`}};typeof window<"u"&&!customElements.get("recur-success-message")&&customElements.define("recur-success-message",z)});var ne={};k(ne,{RecurErrorDisplay:()=>$});var $,ae=w(()=>{"use strict";$=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"
|
|
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",$)});var ce={};k(ce,{RecurSkeletonLoader:()=>H});var H,le=w(()=>{"use strict";H=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",
|
|
380
|
+
`}};typeof window<"u"&&!customElements.get("recur-skeleton-loader")&&customElements.define("recur-skeleton-loader",H)});var de={};k(de,{RecurPaymentFormSkeleton:()=>K});var K,ue=w(()=>{"use strict";K=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",K)});var me={};k(me,{RecurToast:()=>N,RecurToastContainer:()=>D});var N,x,D,pe=w(()=>{"use strict";N=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",s=>{s.stopPropagation(),this.dismiss()}),this.shadowRoot.querySelector(".toast")?.addEventListener("click",()=>this.dismiss())}static show(e,t="info",r=5e3){let s=
|
|
767
|
+
`,this.shadowRoot.querySelector(".toast-close")?.addEventListener("click",s=>{s.stopPropagation(),this.dismiss()}),this.shadowRoot.querySelector(".toast")?.addEventListener("click",()=>this.dismiss())}static show(e,t="info",r=5e3){let s=D.getInstance(),i=document.createElement("recur-toast");return i.setAttribute("message",e),i.setAttribute("type",t),i.setAttribute("duration",r.toString()),s.appendChild(i),i}},x=class x 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
|
|
794
|
+
`}static getInstance(){return x.instance||(x.instance=document.querySelector("recur-toast-container"),x.instance||(x.instance=document.createElement("recur-toast-container"),document.body.appendChild(x.instance))),x.instance}};c(x,"instance",null);D=x;typeof window<"u"&&!customElements.get("recur-toast")&&customElements.define("recur-toast",N);typeof window<"u"&&!customElements.get("recur-toast-container")&&customElements.define("recur-toast-container",D)});var he={};k(he,{RecurPaymentForm:()=>B});var B,fe=w(()=>{"use strict";B=class extends HTMLElement{constructor(){super();c(this,"containerId");c(this,"customStyles");c(this,"_isInitializing",!1);c(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,s){t==="custom-styles"&&r!==s?(this.customStyles=s||"",this.updateCustomStyles()):r!==s&&this.updateCustomerInfoSection()}render(){this.shadowRoot.innerHTML=`
|
|
795
795
|
<style>
|
|
796
796
|
:host {
|
|
797
797
|
display: block;
|
|
@@ -899,7 +899,7 @@
|
|
|
899
899
|
height: 36px !important;
|
|
900
900
|
}
|
|
901
901
|
`;t.textContent=this.customStyles+`
|
|
902
|
-
`+r,this.appendChild(t);let s=this.createOrderSummarySection();s.slot="order-summary",this.appendChild(s);let i=this.createCustomerInfoSection();i.slot="customer-info",this.appendChild(i);let
|
|
902
|
+
`+r,this.appendChild(t);let s=this.createOrderSummarySection();s.slot="order-summary",this.appendChild(s);let i=this.createCustomerInfoSection();i.slot="customer-info",this.appendChild(i);let n=this.createCardFieldsSection();n.slot="card-fields",this.appendChild(n);let o=this.createActionsSection();o.slot="actions",this.appendChild(o)}updateCustomerInfoSection(){let t=this.querySelector('[slot="customer-info"]');if(t){let r=this.createCustomerInfoSection();r.slot="customer-info",t.replaceWith(r)}}updateCustomStyles(){let t=this.querySelector(`#${this.containerId}-custom-styles`);if(t){let r=`
|
|
903
903
|
/* PAYUNi iframe \u5BB9\u5668\u9AD8\u5EA6\uFF08\u4F7F\u7528\u5BE6\u969B\u7684 container ID\uFF09 */
|
|
904
904
|
#${this.containerId}-card-no,
|
|
905
905
|
#${this.containerId}-card-exp,
|
|
@@ -1220,10 +1220,13 @@
|
|
|
1220
1220
|
>
|
|
1221
1221
|
<span id="${this.containerId}-submit-text">\u78BA\u8A8D\u4ED8\u6B3E</span>
|
|
1222
1222
|
</button>
|
|
1223
|
-
`,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 s=document.getElementById(`${this.containerId}-card-no`),i=document.getElementById(`${this.containerId}-card-exp`),
|
|
1223
|
+
`,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 s=document.getElementById(`${this.containerId}-card-no`),i=document.getElementById(`${this.containerId}-card-exp`),n=document.getElementById(`${this.containerId}-card-cvc`);if(!s||!i||!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 m=d.status&&d.status.CardNo===!0&&d.status.CardExp===!0&&d.status.CardCvc===!0,E=document.getElementById(`${this.containerId}-submit-btn`);E&&(E.disabled=!m,console.log("[PaymentForm] Submit button disabled:",!m))}),this._initializationAborted?(console.log("[PaymentForm] Initialization aborted before saving session"),this._isInitializing=!1,null):(this._paymentSession=o,this.setupFormSubmission(),this._isInitializing=!1,o))}catch(s){throw this._isInitializing=!1,console.error("[PaymentForm] Failed to initialize payment:",s),s}}setupFormSubmission(){let t=document.getElementById(`${this.containerId}-submit-btn`);t&&t.addEventListener("click",async r=>{if(r.preventDefault(),t.classList.contains("loading"))return;let s,i,n=document.getElementById(`${this.containerId}-email`),o=document.getElementById(`${this.containerId}-name`);if(n&&o){if(s=n.value,i=o.value,!s||!i){this.showError("\u8ACB\u586B\u5BEB\u59D3\u540D\u548C\u96FB\u5B50\u90F5\u4EF6");return}}else if(s=this.getAttribute("customer-email")||void 0,i=this.getAttribute("customer-name")||void 0,!s||!i){this.showError("\u5BA2\u6236\u8CC7\u8A0A\u4E0D\u5B8C\u6574");return}this.clearError(),this.setButtonLoading(!0),this.dispatchEvent(new CustomEvent("submit",{detail:{customerEmail:s,customerName:i,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=`
|
|
1224
1224
|
<span class="recur-loading-spinner"></span>
|
|
1225
1225
|
<span>\u8655\u7406\u4E2D...</span>
|
|
1226
|
-
`):(r.classList.remove("loading"),r.disabled=!1,r.innerHTML='<span id="'+this.containerId+'-submit-text">\u78BA\u8A8D\u4ED8\u6B3E</span>'))}resetButton(){this.setButtonLoading(!1)}
|
|
1226
|
+
`):(r.classList.remove("loading"),r.disabled=!1,r.innerHTML='<span id="'+this.containerId+'-submit-text">\u78BA\u8A8D\u4ED8\u6B3E</span>'))}resetButton(){this.setButtonLoading(!1)}setVerifying(t){let r=document.getElementById(`${this.containerId}-submit-btn`);r&&(t?(r.classList.add("loading"),r.disabled=!0,r.innerHTML=`
|
|
1227
|
+
<span class="recur-loading-spinner"></span>
|
|
1228
|
+
<span>3D \u9A57\u8B49\u4E2D...</span>
|
|
1229
|
+
`):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 s=document.createElement("recur-error-display");s.setAttribute("error",t),s.setAttribute("dismissible","true"),r.innerHTML="",r.appendChild(s),s.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",B)});var ge={};k(ge,{RecurCheckoutButton:()=>O});var O,be=w(()=>{"use strict";O=class extends HTMLElement{constructor(){super();c(this,"_isLoading",!1);c(this,"handleClick",async t=>{if(t.preventDefault(),this._isLoading||this.hasAttribute("disabled"))return;let r=this.getAttribute("publishable-key"),s=this.getAttribute("product-id"),i=this.getAttribute("success-url"),n=this.getAttribute("cancel-url");if(!r){this.dispatchError("Missing required attribute: publishable-key");return}if(!s){this.dispatchError("Missing required attribute: product-id");return}if(!i){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:s,successUrl:this.resolveUrl(i),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,s){r!==s&&this.render()}render(){let t=this.getAttribute("button-text")||this.textContent?.trim()||"\u8A02\u95B1",r=this.getAttribute("button-style")||"primary",s=this.hasAttribute("disabled")||this._isLoading;this.shadowRoot.innerHTML=`
|
|
1227
1230
|
<style>
|
|
1228
1231
|
:host {
|
|
1229
1232
|
display: inline-block;
|
|
@@ -1322,7 +1325,7 @@
|
|
|
1322
1325
|
${this._isLoading?'<span class="spinner"></span>':""}
|
|
1323
1326
|
<span>${this._isLoading?"\u8655\u7406\u4E2D...":t}</span>
|
|
1324
1327
|
</button>
|
|
1325
|
-
`}setupEventListeners(){let t=this.shadowRoot?.querySelector("button");t&&t.addEventListener("click",this.handleClick)}async createCheckoutSession(t){let r=this.getApiBaseUrl(),s={productId:t.productId,successUrl:t.successUrl,cancelUrl:t.cancelUrl};t.mode&&(s.mode=t.mode),t.customerEmail&&(s.customerEmail=t.customerEmail);let i=await fetch(`${r}/v1/checkout/sessions`,{method:"POST",headers:{"Content-Type":"application/json","X-Recur-Publishable-Key":t.publishableKey},body:JSON.stringify(s)});if(!i.ok){let
|
|
1328
|
+
`}setupEventListeners(){let t=this.shadowRoot?.querySelector("button");t&&t.addEventListener("click",this.handleClick)}async createCheckoutSession(t){let r=this.getApiBaseUrl(),s={productId:t.productId,successUrl:t.successUrl,cancelUrl:t.cancelUrl};t.mode&&(s.mode=t.mode),t.customerEmail&&(s.customerEmail=t.customerEmail);let i=await fetch(`${r}/v1/checkout/sessions`,{method:"POST",headers:{"Content-Type":"application/json","X-Recur-Publishable-Key":t.publishableKey},body:JSON.stringify(s)});if(!i.ok){let n=await i.json().catch(()=>({}));throw new Error(n.error?.message||`HTTP ${i.status}: Failed to create checkout session`)}return i.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",O)});var ye={};k(ye,{RecurPortalButton:()=>j});var j,ke=w(()=>{"use strict";j=class extends HTMLElement{constructor(){super();c(this,"_isLoading",!1);c(this,"handleClick",async t=>{if(t.preventDefault(),this._isLoading||this.hasAttribute("disabled"))return;let r=this.getAttribute("portal-url"),s=this.getAttribute("api-endpoint");if(r){this.redirectToPortal(r);return}if(s){await this.fetchAndRedirect(s);return}this.dispatchError("Missing required attribute: either portal-url or api-endpoint must be provided")});this.attachShadow({mode:"open"})}static get observedAttributes(){return["portal-url","api-endpoint","customer-id","return-url","button-text","button-style","disabled","target"]}connectedCallback(){this.render(),this.setupEventListeners()}disconnectedCallback(){let t=this.shadowRoot?.querySelector("button");t&&t.removeEventListener("click",this.handleClick)}attributeChangedCallback(t,r,s){r!==s&&this.render()}render(){let t=this.getAttribute("button-text")||this.textContent?.trim()||"\u7BA1\u7406\u8A02\u95B1",r=this.getAttribute("button-style")||"primary",s=this.hasAttribute("disabled")||this._isLoading;this.shadowRoot.innerHTML=`
|
|
1326
1329
|
<style>
|
|
1327
1330
|
:host {
|
|
1328
1331
|
display: inline-block;
|
|
@@ -1449,7 +1452,7 @@
|
|
|
1449
1452
|
<path d="M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2"/>
|
|
1450
1453
|
<circle cx="12" cy="7" r="4"/>
|
|
1451
1454
|
</svg>
|
|
1452
|
-
`}setupEventListeners(){let t=this.shadowRoot?.querySelector("button");t&&t.addEventListener("click",this.handleClick)}redirectToPortal(t){let r=this.getAttribute("target");this.dispatchEvent(new CustomEvent("portal-redirect",{detail:{url:t},bubbles:!0,composed:!0})),r==="_blank"?window.open(t,"_blank","noopener,noreferrer"):window.location.href=t}async fetchAndRedirect(t){let r=this.getAttribute("customer-id"),s=this.getAttribute("return-url");this._isLoading=!0,this.render(),this.setupEventListeners();try{let i={};r&&(i.customerId=r),s&&(i.returnUrl=s);let
|
|
1455
|
+
`}setupEventListeners(){let t=this.shadowRoot?.querySelector("button");t&&t.addEventListener("click",this.handleClick)}redirectToPortal(t){let r=this.getAttribute("target");this.dispatchEvent(new CustomEvent("portal-redirect",{detail:{url:t},bubbles:!0,composed:!0})),r==="_blank"?window.open(t,"_blank","noopener,noreferrer"):window.location.href=t}async fetchAndRedirect(t){let r=this.getAttribute("customer-id"),s=this.getAttribute("return-url");this._isLoading=!0,this.render(),this.setupEventListeners();try{let i={};r&&(i.customerId=r),s&&(i.returnUrl=s);let n=await fetch(t,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(i)});if(!n.ok){let m=await n.json().catch(()=>({}));throw new Error(m.error?.message||m.message||`HTTP ${n.status}: Failed to create portal session`)}let o=await n.json(),d=o.url||o.portalUrl;if(!d)throw new Error("Portal URL not found in response");this._isLoading=!1,this.render(),this.setupEventListeners(),this.redirectToPortal(d)}catch(i){this._isLoading=!1,this.render(),this.setupEventListeners(),this.dispatchError(i.message||"Failed to create portal session")}}dispatchError(t){console.error("[recur-portal]",t),this.dispatchEvent(new CustomEvent("portal-error",{detail:{message:t},bubbles:!0,composed:!0}))}};typeof window<"u"&&!customElements.get("recur-portal")&&customElements.define("recur-portal",j)});var Oe={};k(Oe,{RecurCheckout:()=>U,RecurElements:()=>T,createElements:()=>G,default:()=>Be,init:()=>Ee});async function Ae(){if(typeof window>"u"){console.warn("[Recur SDK] Cannot register Web Components: window is not defined");return}await Promise.all([Promise.resolve().then(()=>(se(),re)),Promise.resolve().then(()=>(oe(),ie)),Promise.resolve().then(()=>(ae(),ne)),Promise.resolve().then(()=>(le(),ce)),Promise.resolve().then(()=>(ue(),de)),Promise.resolve().then(()=>(pe(),me)),Promise.resolve().then(()=>(fe(),he)),Promise.resolve().then(()=>(be(),ge)),Promise.resolve().then(()=>(ke(),ye))]);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","recur-portal"].filter(t=>!customElements.get(t));e.length>0&&console.warn("[Recur SDK] The following components are not registered:",e)}typeof window<"u"&&Ae();function _e(a){return a.replace(/_([a-z])/g,(e,t)=>t.toUpperCase())}function b(a){if(a==null)return a;if(Array.isArray(a))return a.map(e=>b(e));if(a instanceof Date)return a;if(typeof a=="object"){let e={};for(let[t,r]of Object.entries(a)){let s=_e(t);e[s]=b(r)}return e}return a}var ve={name:"recur-tw",version:"0.9.2",description:"React & Vanilla JS SDK for embedding subscription checkout flows (Taiwan / PAYUNi)",type:"module",private:!1,author:"Recur",license:"Elastic-2.0",keywords:["react","vanilla-js","subscription","checkout","payment","payuni","recurring-billing","taiwan","\u7E41\u9AD4\u4E2D\u6587","sdk","embedded-checkout"],homepage:"https://recur.tw/",bugs:{email:"support@recur.tw"},scripts:{build:"tsup",dev:"tsup --watch",lint:"eslint .","lint:fix":"eslint . --fix","type-check":"tsc --noEmit",examples:"npx http-server examples -p 8080 -o"},main:"./dist/index.js",module:"./dist/index.js",types:"./dist/index.d.ts",exports:{".":{types:"./dist/index.d.ts",import:"./dist/index.js",require:"./dist/index.cjs"},"./server":{types:"./dist/server.d.ts",import:"./dist/server.js",require:"./dist/server.cjs"},"./vanilla":"./dist/recur.umd.js","./package.json":"./package.json"},unpkg:"./dist/recur.umd.js",jsdelivr:"./dist/recur.umd.js",files:["dist","README.md","AGENTS.md","LICENSE"],sideEffects:["src/components/**/*.ts","src/components/**/*.js"],peerDependencies:{react:">=18.0.0","react-dom":">=18.0.0"},peerDependenciesMeta:{react:{optional:!0},"react-dom":{optional:!0}},devDependencies:{"@eslint/js":"^9.39.1","@types/node":"^20.19.9","@types/react":"^19.1.9","@types/react-dom":"^19.1.7","@workspace/typescript-config":"workspace:*",autoprefixer:"^10.4.22",eslint:"^9","eslint-plugin-react":"^7.37.5","eslint-plugin-react-hooks":"^7.0.1",postcss:"^8.5.6",react:"^19.2.1","react-dom":"^19.2.1",tailwindcss:"^4.1.11",tsup:"^8.3.5",typescript:"^5.9.2","typescript-eslint":"^8.47.0"}};var $e=ve.version,He="vanilla",F=class{constructor(e){c(this,"config");if(!e.publishableKey)throw new Error("publishableKey is required");this.config={baseUrl:"https://api.recur.tw",...e}}getHeaders(){return{"Content-Type":"application/json","X-Recur-Publishable-Key":this.config.publishableKey,"X-Recur-SDK-Type":He,"X-Recur-SDK-Version":$e,"X-Recur-Source":typeof window<"u"?window.location.origin:"server"}}async createSubscription(e){let{customerName:t,customerEmail:r}=e,s=e.productId||e.planId,i=e.productSlug;if(!s&&!i)throw new Error("Either productId or productSlug is required");let n={customerName:t,customerEmail:r};s&&(n.productId=s),i&&(n.productSlug=i);let o=await fetch(`${this.config.baseUrl}/v1/subscriptions`,{method:"POST",headers:this.getHeaders(),body:JSON.stringify(n)});if(!o.ok){let m=await o.json().catch(()=>({}));throw{code:m.error||"CHECKOUT_FAILED",message:m.message||"Failed to initiate checkout",details:m}}let d=await o.json();return b(d)}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:this.getHeaders()});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}}let s=await r.json();return b(s)}async fetchPlans(){return{plans:(await this.fetchProducts({type:"SUBSCRIPTION"})).products}}getConfig(){return{...this.config}}};var q=class{constructor(e,t){c(this,"config");c(this,"options");c(this,"container");c(this,"checkoutId",null);c(this,"sdkToken",null);c(this,"sdkTimestamp",null);c(this,"creditToken",null);c(this,"sdkEnv","S");c(this,"payuniSDK",null);c(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(),s=b(r);this.checkoutId=s.checkout.id,this.sdkToken=s.sdkToken,this.sdkTimestamp=s.sdkTimestamp||null,this.creditToken=s.creditToken||null,this.sdkEnv=this.config.publishableKey.startsWith("pk_test_")?"S":"P"}renderHTML(){this.container.innerHTML=`
|
|
1453
1456
|
<div class="recur-embedded-checkout" style="max-width: 400px; margin: 0 auto; padding: 20px; font-family: system-ui, -apple-system, sans-serif;">
|
|
1454
1457
|
<div class="recur-checkout-header" style="margin-bottom: 24px;">
|
|
1455
1458
|
<h2 style="margin: 0 0 8px 0; font-size: 20px; font-weight: 600; color: #111;">Subscribe</h2>
|
|
@@ -1519,21 +1522,21 @@
|
|
|
1519
1522
|
</p>
|
|
1520
1523
|
</form>
|
|
1521
1524
|
</div>
|
|
1522
|
-
`}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 s=r?.status;s&&(this.isFormValid=s.CardNo===!0&&s.CardExp===!0&&s.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 s=r.EncryptInfo||r.creditToken;if(!s)throw console.error("[Recur SDK] Missing payment token. Available fields:",Object.keys(r)),new Error("Missing payment token");let i=this.getBaseUrl(),
|
|
1525
|
+
`}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 s=r?.status;s&&(this.isFormValid=s.CardNo===!0&&s.CardExp===!0&&s.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 s=r.EncryptInfo||r.creditToken;if(!s)throw console.error("[Recur SDK] Missing payment token. Available fields:",Object.keys(r)),new Error("Missing payment token");let i=this.getBaseUrl(),n=await fetch(`${i}/v1/checkouts/${this.checkoutId}/pay`,{method:"POST",headers:{"Content-Type":"application/json","X-Recur-Publishable-Key":this.config.publishableKey},body:JSON.stringify({creditToken:s,timestamp:this.sdkTimestamp||r.HashTimestamp||r.timestamp})});if(!n.ok){let m=await n.json().catch(()=>({}));throw new Error(m.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 T=class{constructor(e){c(this,"publishableKey");c(this,"baseUrl");c(this,"embedUrl");c(this,"iframe",null);c(this,"container",null);c(this,"sessionId",null);c(this,"timestamp",null);c(this,"creditToken",null);c(this,"sdkToken",null);c(this,"cardToken",null);c(this,"cardTimestamp",null);c(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=`
|
|
1523
1526
|
width: 100%;
|
|
1524
1527
|
border: none;
|
|
1525
1528
|
min-height: 200px;
|
|
1526
1529
|
display: block;
|
|
1527
1530
|
user-select: none;
|
|
1528
1531
|
transition: height 0.35s ease, opacity 0.4s ease 0.1s;
|
|
1529
|
-
`.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,s)=>{let i=setTimeout(()=>{s(new Error("Elements initialization timeout"))},3e4),
|
|
1532
|
+
`.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,s)=>{let i=setTimeout(()=>{s(new Error("Elements initialization timeout"))},3e4),n=()=>{clearTimeout(i),this.off("ready",n),r()},o=d=>{clearTimeout(i),this.off("error",o),s(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(s=>s(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.sdkToken=r.sdkToken,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 s=setTimeout(()=>{r(new Error("Tokenization timeout"))},6e4),i=o=>{clearTimeout(s),this.off("tokenized",i),t(o)},n=o=>{clearTimeout(s),this.off("error",n),r(new Error(o.message||"Tokenization failed"))};this.on("tokenized",i),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,productId:e.productId,email:e.email,name:e.name,phone:e.phone,externalCustomerId:e.externalCustomerId,metadata:e.metadata,successUrl:e.successUrl,cancelUrl:e.cancelUrl})}),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.sdkToken=null,this.cardToken=null,this.cardTimestamp=null,this.eventHandlers.clear(),window.removeEventListener("message",this.handleMessage.bind(this))}};function G(a){return new T(a)}var Ke="https://vendor.payuni.com.tw/sdk/uni-payment.js",Ne="https://sandbox-vendor.payuni.com.tw/sdk/uni-payment.js",we=!1,V=!1,L=null;async function xe(a=!1){return we&&window.UniPayment?Promise.resolve():(V&&L||(V=!0,L=new Promise((e,t)=>{let r=document.createElement("script");r.src=a?Ne:Ke,r.async=!0,r.onload=()=>{we=!0,V=!1,e()},r.onerror=()=>{V=!1,L=null,t(new Error("Failed to load PAYUNi SDK"))},document.head.appendChild(r)})),L)}var U=class{constructor(e){c(this,"core");c(this,"currentModal",null);c(this,"currentIframe",null);c(this,"currentModalOverlay",null);this.core=new F(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 q(t,e).render()}async redirectToCheckout(e){let t=await this.createCheckoutSession(e);window.location.href=t.url}async createCheckoutSession(e){let t=this.getBaseUrl();if(!e.productId&&!e.productSlug)throw new Error("Either productId or productSlug is required");if(!e.successUrl)throw new Error("successUrl is required for hosted checkout");if(!e.cancelUrl)throw new Error("cancelUrl is required for hosted checkout");let r={successUrl:e.successUrl,cancelUrl:e.cancelUrl};e.productId&&(r.productId=e.productId),e.productSlug&&(r.productSlug=e.productSlug),e.customerEmail&&(r.customerEmail=e.customerEmail),e.customerName&&(r.customerName=e.customerName),e.externalCustomerId&&(r.externalCustomerId=e.externalCustomerId);let s=this.core.getConfig(),i=await fetch(`${t}/v1/checkout/sessions`,{method:"POST",headers:{"Content-Type":"application/json","X-Recur-Publishable-Key":s.publishableKey},body:JSON.stringify(r)});if(!i.ok){let d=await i.json().catch(()=>({}));throw new Error(d.error?.message||d.error||"Failed to create checkout session")}let n=await i.json(),o=b(n);return{id:o.id,url:o.url,expiresAt:o.expiresAt,clientSecret:o.clientSecret}}async getCheckoutStatus(e,t){let r=this.getBaseUrl(),s=this.core.getConfig(),i=await fetch(`${r}/v1/checkouts/${e}?client_secret=${encodeURIComponent(t)}`,{method:"GET",headers:{"X-Recur-Publishable-Key":s.publishableKey}});if(!i.ok){let d=await i.json().catch(()=>({}));throw new Error(d.error||"Failed to get checkout status")}let n=await i.json(),o=b(n);return{id:o.checkout.id,status:o.checkout.status,amount:o.checkout.amount,currency:o.checkout.currency,lastCharge:o.lastCharge}}async checkout(e){let t=this.core.getConfig(),r=null,s=e.productId||e.planId,i=e.productSlug;try{if(console.log("[Recur SDK] Starting checkout flow...",{productId:s,productSlug:i,mode:e.mode}),!s&&!i)throw new Error("Either productId or productSlug is required");if(!t.publishableKey)throw new Error("publishableKey is required");let n=this.getBaseUrl();console.log("[Recur SDK] Base URL:",n);let o={"Content-Type":"application/json","X-Recur-Publishable-Key":t.publishableKey},d=e.mode||"modal";if(d==="redirect"){if(!e.successUrl)throw new Error("successUrl is required for redirect mode");if(!e.cancelUrl)throw new Error("cancelUrl is required for redirect mode");console.log("[Recur SDK] Creating hosted checkout session...");let h={successUrl:e.successUrl,cancelUrl:e.cancelUrl};s&&(h.productId=s),i&&(h.productSlug=i),e.customerEmail&&(h.customerEmail=e.customerEmail),e.customerName&&(h.customerName=e.customerName),e.externalCustomerId&&(h.externalCustomerId=e.externalCustomerId);let S=await fetch(`${n}/v1/checkout/sessions`,{method:"POST",headers:o,body:JSON.stringify(h)});if(!S.ok){let C=await S.json().catch(()=>({}));throw console.error("[Recur SDK] Failed to create checkout session:",C),new Error(C.error?.message||C.error||"Failed to create checkout session")}let J=await S.json(),y=b(J);console.log("[Recur SDK] Checkout session created:",y),console.log("[Recur SDK] Redirecting to hosted checkout:",y.url),window.location.href=y.url;return}let m=null;if(d==="modal"){let h=this.createModalWithSkeleton(e.onClose);r=h.overlay,m=h.container}else if(d==="iframe"){if(m=this.getEmbeddedContainer(e.container),!m)throw new Error("Container is required for iframe mode");m.innerHTML="";let h=document.createElement("recur-payment-form-skeleton");m.appendChild(h)}console.log("[Recur SDK] Step 1: Creating embedded checkout...");let E={customerName:e.customerName,customerEmail:e.customerEmail};s&&(E.productId=s),i&&(E.productSlug=i),e.externalCustomerId&&(E.externalCustomerId=e.externalCustomerId);let Y=await fetch(`${n}/v1/checkouts`,{method:"POST",headers:o,body:JSON.stringify(E)});if(!Y.ok){let h=await Y.json().catch(()=>({}));console.error("[Recur SDK] Failed to create checkout:",h);let S=h.details||h.error||"Failed to create checkout";throw new Error(S)}let Se=await Y.json(),l=b(Se);if(console.log("[Recur SDK] Checkout created successfully:",l),e.onSuccess?.(l),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 X=!l.livemode;if(console.log("[Recur SDK] Environment:",X?"SANDBOX":"PRODUCTION"),await xe(X),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..."),!m)throw new Error("Payment container not available");m.innerHTML="";let p=document.createElement("recur-payment-form");if(p.setAttribute("container-id",m.id||"recur-payment-container"),e.customerName&&p.setAttribute("customer-name",e.customerName),e.customerEmail&&p.setAttribute("customer-email",e.customerEmail),l.plan?.name&&p.setAttribute("plan-name",l.plan.name),l.checkout?.amount&&p.setAttribute("amount",l.checkout.amount.toString()),l.plan?.billingPeriod&&p.setAttribute("billing-period",l.plan.billingPeriod),p.setAttribute("custom-styles",`
|
|
1530
1533
|
.form-input-focus {
|
|
1531
1534
|
border-color: var(--ring, hsl(215 16% 47%)) !important;
|
|
1532
1535
|
outline: 0 !important;
|
|
1533
1536
|
box-shadow: 0 0 0 3px color-mix(in oklch, var(--ring, hsl(215 16% 47%)) 50%, transparent) !important;
|
|
1534
1537
|
transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out !important;
|
|
1535
1538
|
}
|
|
1536
|
-
`),
|
|
1539
|
+
`),m.appendChild(p),await new Promise(h=>setTimeout(h,100)),console.log("[Recur SDK] Step 5: Initializing PAYUNi SDK..."),await p.initializePayment(l.sdkToken,X?"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..."),p.addEventListener("submit",(async h=>{console.log("[Recur SDK] Form submitted");let S=h,{paymentSession:J}=S.detail;try{let y=await J.getTradeResult();console.log("[Recur SDK] Trade result received"),console.log("[Recur SDK] Executing payment...");let C=y.HashTimestamp||y.timestamp,Z={};if(l.checkout.productType==="SUBSCRIPTION"){let f=l.creditToken,v=l.sdkTimestamp;if(!f)throw console.error("[Recur SDK] Missing creditToken from checkout for subscription"),new Error("Missing creditToken for subscription payment");Z={creditToken:f,timestamp:v||C},console.log("[Recur SDK] Using creditToken from checkout:",f.substring(0,30)+"..."),console.log("[Recur SDK] Using timestamp:",v?"from checkout (sdkTimestamp)":"from tradeResult")}let W=await fetch(`${n}/v1/checkouts/${l.checkout.id}/pay`,{method:"POST",headers:o,body:JSON.stringify(Z)});if(!W.ok){let f=await W.json().catch(()=>({}));throw new Error(f.error||"Failed to execute payment")}let Ce=await W.json(),u=b(Ce);if(console.log("[Recur SDK] Payment executed:",u),u.requires3D&&u.redirectUrl){console.log("[Recur SDK] 3D verification required"),console.log("[Recur SDK] Using popup for 3D verification");{let f=window.open(u.redirectUrl,"recur_3d_verification","width=500,height=700,scrollbars=yes,resizable=yes");if(!f){console.log("[Recur SDK] Popup was blocked (null), falling back to redirect"),window.location.href=u.redirectUrl;return}if(await new Promise(R=>setTimeout(R,100)),f.closed){console.log("[Recur SDK] Popup was closed immediately, falling back to redirect"),window.location.href=u.redirectUrl;return}p.setVerifying?.(!0);let v=l.checkout.id,Q=l.checkout.clientSecret,Te=90,Ie=2e3,I=!0,ee=!1,te;console.log("[Recur SDK] Starting 3D verification polling...");for(let R=0;R<Te&&I;R++){if(f.closed){console.log("[Recur SDK] Popup was closed");try{let g=await fetch(`${n}/v1/checkouts/${v}/status?client_secret=${encodeURIComponent(Q)}`,{headers:o});if(g.ok){let P=await g.json();if(P.checkout?.status==="SUCCEEDED"){console.log("[Recur SDK] Payment succeeded after popup close"),I=!1,ee=!0,te=P.checkout?.orderId;break}}}catch(g){console.error("[Recur SDK] Final status check failed:",g)}throw console.log("[Recur SDK] Payment not confirmed, user closed popup"),p.setVerifying?.(!1),p.resetButton?.(),new Error("3D \u9A57\u8B49\u5DF2\u53D6\u6D88")}try{let g=await fetch(`${n}/v1/checkouts/${v}/status?client_secret=${encodeURIComponent(Q)}`,{headers:o});if(g.ok){let P=await g.json(),M=P.checkout?.status;if(console.log(`[Recur SDK] Poll ${R+1}: status = ${M}`),M==="SUCCEEDED"){console.log("[Recur SDK] Payment succeeded"),I=!1;try{f.close()}catch{}p.setVerifying?.(!1),e.onPaymentComplete&&(u.subscription?e.onPaymentComplete({id:u.subscription.id,status:"ACTIVE",planId:l.checkout.productId,amount:l.checkout.amount,billingPeriod:u.subscription.billingPeriod,currentPeriodStart:u.subscription.currentPeriodStart,currentPeriodEnd:u.subscription.currentPeriodEnd}):e.onPaymentComplete({id:P.checkout?.orderId||v,status:"SUCCEEDED",planId:l.checkout.productId,amount:l.checkout.amount,billingPeriod:l.checkout.productType})),r&&r.remove();return}if(M==="CANCELED"||M==="FAILED"){console.log("[Recur SDK] Payment failed or canceled"),I=!1;try{f.close()}catch{}throw p.setVerifying?.(!1),p.resetButton?.(),new Error("\u4ED8\u6B3E\u5931\u6557\u6216\u5DF2\u53D6\u6D88")}}}catch(g){if(g.message==="3D \u9A57\u8B49\u5DF2\u53D6\u6D88"||g.message==="\u4ED8\u6B3E\u5931\u6557\u6216\u5DF2\u53D6\u6D88")throw g;console.error("[Recur SDK] Poll error:",g)}await new Promise(g=>setTimeout(g,Ie))}if(I){console.log("[Recur SDK] 3D verification polling timeout");try{f.close()}catch{}throw p.setVerifying?.(!1),p.resetButton?.(),new Error("3D \u9A57\u8B49\u903E\u6642\uFF0C\u8ACB\u91CD\u8A66")}ee&&(console.log("[Recur SDK] Handling success after popup close"),p.setVerifying?.(!1),e.onPaymentComplete&&(u.subscription?e.onPaymentComplete({id:u.subscription.id,status:"ACTIVE",planId:l.checkout.productId,amount:l.checkout.amount,billingPeriod:u.subscription.billingPeriod,currentPeriodStart:u.subscription.currentPeriodStart,currentPeriodEnd:u.subscription.currentPeriodEnd}):e.onPaymentComplete({id:te||v,status:"SUCCEEDED",planId:l.checkout.productId,amount:l.checkout.amount,billingPeriod:l.checkout.productType})),r&&r.remove());return}}if(e.onPaymentComplete)if(u.subscription)e.onPaymentComplete({id:u.subscription.id,status:u.subscription.status,planId:l.checkout.productId,amount:l.checkout.amount,billingPeriod:u.subscription.billingPeriod,currentPeriodStart:u.subscription.currentPeriodStart,currentPeriodEnd:u.subscription.currentPeriodEnd});else{let f=u.charge?.id||u.paymentIntent?.id,v=u.charge?.status||"SUCCEEDED";e.onPaymentComplete({id:f,status:v,planId:l.checkout.productId,amount:l.checkout.amount,billingPeriod:l.checkout.productType})}console.log("[Recur SDK] Checkout flow completed successfully!"),p.resetButton?.(),r&&r.remove()}catch(y){console.error("[Recur SDK] Payment error:",y);let C={code:"PAYMENT_FAILED",message:y instanceof Error?y.message:"Payment failed"};e.onError?.(C),p.resetButton?.()}})),console.log("[Recur SDK] Checkout flow initialized, waiting for user input...")}catch(n){console.error("[Recur SDK] Checkout error:",n),r&&r.remove();let o={code:"CHECKOUT_ERROR",message:n instanceof Error?n.message:"An unknown error occurred"};throw e.onError?.(o),n}}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=`
|
|
1537
1540
|
position: fixed;
|
|
1538
1541
|
top: 0;
|
|
1539
1542
|
left: 0;
|
|
@@ -1572,7 +1575,7 @@
|
|
|
1572
1575
|
border-radius: 50%;
|
|
1573
1576
|
z-index: 10;
|
|
1574
1577
|
transition: background 0.2s;
|
|
1575
|
-
`,s.onmouseover=()=>{s.style.background="rgba(0, 0, 0, 0.1)"},s.onmouseout=()=>{s.style.background="rgba(0, 0, 0, 0.05)"},s.onclick=()=>{t.remove(),e?.()};let i=document.createElement("div");i.id="recur-modal-payment-container";let
|
|
1578
|
+
`,s.onmouseover=()=>{s.style.background="rgba(0, 0, 0, 0.1)"},s.onmouseout=()=>{s.style.background="rgba(0, 0, 0, 0.05)"},s.onclick=()=>{t.remove(),e?.()};let i=document.createElement("div");i.id="recur-modal-payment-container";let n=document.createElement("recur-payment-form-skeleton");return i.appendChild(n),r.appendChild(s),r.appendChild(i),t.appendChild(r),document.body.appendChild(t),this.currentModalOverlay=t,{overlay:t,container:i}}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()}async createPortalSession(e,t){let r=await fetch(e,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({customerId:t.customerId,returnUrl:t.returnUrl})});if(!r.ok){let i=await r.json().catch(()=>({}));throw new Error(i.error?.message||i.message||"Failed to create portal session")}let s=await r.json();return{id:s.id,url:s.url||s.portalUrl,expiresAt:s.expiresAt}}async redirectToPortal(e,t){let r=await this.createPortalSession(e,t);window.location.href=r.url}};function Ee(a){return new U(a)}var Be={init:Ee,RecurCheckout:U,RecurElements:T,createElements:G};return Me(Oe);})();
|
|
1576
1579
|
if (typeof window !== "undefined") {
|
|
1577
1580
|
window.RecurCheckout = RecurCheckout.default;
|
|
1578
1581
|
window.RecurElements = RecurCheckout.RecurElements;
|