recur-tw 0.12.0 → 0.13.0
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/checkout.js +2 -2
- package/dist/index.cjs +16 -11
- package/dist/index.d.cts +68 -7
- package/dist/index.d.ts +68 -7
- package/dist/index.js +16 -11
- package/dist/recur.umd.js +26 -26
- package/dist/server.cjs +5 -1
- package/dist/server.d.cts +36 -3
- package/dist/server.d.ts +36 -3
- package/dist/server.js +5 -1
- package/dist/widget.js +3 -3
- package/package.json +1 -1
package/dist/checkout.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"use strict";var RecurCheckoutScript=(()=>{var
|
|
1
|
+
"use strict";var RecurCheckoutScript=(()=>{var f=Object.defineProperty;var v=Object.getOwnPropertyDescriptor;var E=Object.getOwnPropertyNames;var C=Object.prototype.hasOwnProperty;var R=(e,t)=>{for(var r in t)f(e,r,{get:t[r],enumerable:!0})},_=(e,t,r,o)=>{if(t&&typeof t=="object"||typeof t=="function")for(let n of E(t))!C.call(e,n)&&n!==r&&f(e,n,{get:()=>t[n],enumerable:!(o=v(t,n))||o.enumerable});return e};var U=e=>_(f({},"__esModule",{value:!0}),e);var T={};R(T,{default:()=>I});var a={embedUrl:"https://embed.recur.tw",apiUrl:"https://api.recur.tw"},h=null,p=null;function m(){if(document.getElementById("recur-checkout-styles"))return;let e=document.createElement("style");e.id="recur-checkout-styles",e.textContent=`
|
|
2
2
|
.recur-checkout-dialog {
|
|
3
3
|
border: none;
|
|
4
4
|
border-radius: 12px;
|
|
@@ -122,5 +122,5 @@
|
|
|
122
122
|
opacity: 0.7;
|
|
123
123
|
pointer-events: none;
|
|
124
124
|
}
|
|
125
|
-
`,document.head.appendChild(e)}function S(e){try{let r=new URL(e,window.location.origin).pathname.split("/"),o=r.indexOf("buy");if(o===-1||o>=r.length-1)return null;let n=r[o+1];return n?n.startsWith("prod_")?{productId:n}:{productSlug:n}:null}catch{return null}}function P(e){let t=e.getAttribute("data-key");if(t)return t;try{let
|
|
125
|
+
`,document.head.appendChild(e)}function S(e){try{let r=new URL(e,window.location.origin).pathname.split("/"),o=r.indexOf("buy");if(o===-1||o>=r.length-1)return null;let n=r[o+1];return n?n.startsWith("prod_")?{productId:n}:{productSlug:n}:null}catch{return null}}function P(e){let t=e.getAttribute("data-key");if(t)return t;try{let n=e.getAttribute("href");if(n){let s=new URL(n,window.location.origin).searchParams.get("key");if(s)return s}}catch{}let r=document.querySelector("script[data-recur-key]");if(r)return r.getAttribute("data-recur-key");let o=window.RecurConfig;return o?.publishableKey?o.publishableKey:null}async function b(e,t){let r={};e.productId&&(r.productId=e.productId),e.email&&(r.customerEmail=e.email),e.name&&(r.customerName=e.name);let o=await fetch(`${a.apiUrl}/v1/checkouts`,{method:"POST",headers:{"Content-Type":"application/json","X-Recur-Publishable-Key":t,"X-Recur-Embed-Domain":a.embedUrl,"X-Recur-SDK-Type":"checkout.js","X-Recur-Source":window.location.origin},body:JSON.stringify(r)});if(!o.ok){let i=await o.json().catch(()=>({}));throw new Error(i.error?.message||i.error||"Failed to create checkout")}let n=await o.json();return{id:n.checkout.id,publishableKey:t,clientSecret:n.checkout.client_secret}}function y(e,t){u(),m();let r=document.createElement("dialog");r.className="recur-checkout-dialog";let o=document.createElement("div");o.className="recur-checkout-dialog__header";let n=document.createElement("button");n.className="recur-checkout-dialog__close",n.innerHTML="×",n.type="button",n.setAttribute("aria-label","Close"),n.onclick=()=>{u(),t?.(),d("recur:close",{})},o.appendChild(n);let i=document.createElement("iframe");i.className="recur-checkout-dialog__iframe",i.src=e,i.setAttribute("allow","payment"),i.setAttribute("title","Recur Checkout"),r.appendChild(o),r.appendChild(i),document.body.appendChild(r),h=r,p=i,r.showModal(),document.body.style.overflow="hidden",r.addEventListener("cancel",s=>{s.preventDefault(),u(),t?.(),d("recur:close",{})}),window.addEventListener("message",k)}function u(){h&&(h.close(),h.remove(),h=null),p=null,document.body.style.overflow="",window.removeEventListener("message",k)}function k(e){if(!e.origin.includes("recur.tw")&&!e.origin.includes("localhost"))return;let{type:t,data:r}=e.data||{};switch(t){case"RECUR_PAYMENT_SUCCESS":u(),d("recur:success",r);break;case"RECUR_PAYMENT_ERROR":d("recur:error",r);break;case"RECUR_PAYMENT_CANCEL":u(),d("recur:close",{});break;case"RECUR_RESIZE":p&&r?.height&&(p.style.height=`${Math.min(r.height+56,window.innerHeight*.9)}px`);break}}function d(e,t){window.dispatchEvent(new CustomEvent(e,{detail:t}))}async function A(e,t){e.preventDefault();let r=t.getAttribute("href");if(!r){console.error("[Recur] Missing href on recur-button");return}let o=S(r);if(!o){console.error("[Recur] Invalid product URL:",r);return}let n=P(t);if(!n){console.error("[Recur] Missing publishable key. Add data-key attribute or set RecurConfig.publishableKey");return}let i={productId:o.productId||"",email:t.getAttribute("data-email")||void 0,name:t.getAttribute("data-name")||void 0,successUrl:t.getAttribute("data-success-url")||void 0,theme:t.getAttribute("data-theme")||void 0};t.setAttribute("data-loading","true");let s=t.textContent;t.textContent="...";try{let c=await b(i,n),l=new URL(`${a.embedUrl}/checkout`);l.searchParams.set("id",c.id),l.searchParams.set("key",c.publishableKey),l.searchParams.set("client_secret",c.clientSecret),i.theme&&l.searchParams.set("theme",i.theme),y(l.toString(),()=>{let x=i.successUrl})}catch(c){console.error("[Recur] Checkout error:",c),d("recur:error",{message:c instanceof Error?c.message:"Unknown error"})}finally{t.setAttribute("data-loading","false"),t.textContent=s}}function g(){document.querySelectorAll(".recur-button").forEach(t=>{t.hasAttribute("data-recur-initialized")||(t.setAttribute("data-recur-initialized","true"),t.addEventListener("click",r=>A(r,t)))})}function L(){let e=window.location.hostname;(e==="localhost"||e==="127.0.0.1"||e.includes(".local"))&&(a.embedUrl=`${window.location.protocol}//${e}:3002`,a.apiUrl=`${window.location.protocol}//${e}:3000/api`)}var w={config(e){Object.assign(a,e)},async popup(e){let t=await b(e,e.key),r=new URL(`${a.embedUrl}/checkout`);r.searchParams.set("id",t.id),r.searchParams.set("key",t.publishableKey),r.searchParams.set("client_secret",t.clientSecret),e.theme&&r.searchParams.set("theme",e.theme),y(r.toString())},close(){u()},init(){g()}};function M(){L(),m(),document.readyState==="loading"?document.addEventListener("DOMContentLoaded",g):g(),new MutationObserver(t=>{for(let r of t)r.addedNodes.length>0&&g()}).observe(document.body,{childList:!0,subtree:!0})}window.Recur=w;M();var I=w;return U(T);})();
|
|
126
126
|
/* Recur Checkout - https://recur.tw */
|
package/dist/index.cjs
CHANGED
|
@@ -1097,6 +1097,7 @@ var init_payment_form = __esm({
|
|
|
1097
1097
|
__publicField(this, "customStyles");
|
|
1098
1098
|
__publicField(this, "_isInitializing", false);
|
|
1099
1099
|
__publicField(this, "_initializationAborted", false);
|
|
1100
|
+
__publicField(this, "_paymentSession", null);
|
|
1100
1101
|
// Test mode state
|
|
1101
1102
|
__publicField(this, "_showTestCardHints", false);
|
|
1102
1103
|
// Coupon state
|
|
@@ -1733,7 +1734,7 @@ var init_payment_form = __esm({
|
|
|
1733
1734
|
bubbles: true,
|
|
1734
1735
|
composed: true
|
|
1735
1736
|
}));
|
|
1736
|
-
} catch
|
|
1737
|
+
} catch {
|
|
1737
1738
|
this._couponError = "\u7DB2\u8DEF\u932F\u8AA4\uFF0C\u8ACB\u7A0D\u5F8C\u518D\u8A66";
|
|
1738
1739
|
this._couponLoading = false;
|
|
1739
1740
|
this.updateCouponSectionDOM();
|
|
@@ -1767,7 +1768,7 @@ var init_payment_form = __esm({
|
|
|
1767
1768
|
bubbles: true,
|
|
1768
1769
|
composed: true
|
|
1769
1770
|
}));
|
|
1770
|
-
} catch
|
|
1771
|
+
} catch {
|
|
1771
1772
|
this._couponError = "\u7DB2\u8DEF\u932F\u8AA4\uFF0C\u8ACB\u7A0D\u5F8C\u518D\u8A66";
|
|
1772
1773
|
this._couponLoading = false;
|
|
1773
1774
|
this.updateCouponSectionDOM();
|
|
@@ -1926,7 +1927,8 @@ var init_payment_form = __esm({
|
|
|
1926
1927
|
try {
|
|
1927
1928
|
await paymentSession.start();
|
|
1928
1929
|
} catch (startError) {
|
|
1929
|
-
const
|
|
1930
|
+
const errorObj = startError;
|
|
1931
|
+
const is1008Error = errorObj?.message?.includes("1008") || errorObj?.message?.includes("timeout") || errorObj?.code === 1008;
|
|
1930
1932
|
if (is1008Error && this._initializationAborted) {
|
|
1931
1933
|
console.log("[PaymentForm] Caught 1008 timeout error after component disconnect - ignoring");
|
|
1932
1934
|
this._isInitializing = false;
|
|
@@ -1946,7 +1948,8 @@ var init_payment_form = __esm({
|
|
|
1946
1948
|
return;
|
|
1947
1949
|
}
|
|
1948
1950
|
console.log("[PaymentForm] PAYUNi update:", data);
|
|
1949
|
-
const
|
|
1951
|
+
const updateData = data;
|
|
1952
|
+
const allFieldsComplete = updateData.status && updateData.status.CardNo === true && updateData.status.CardExp === true && updateData.status.CardCvc === true;
|
|
1950
1953
|
const submitBtn = document.getElementById(`${this.containerId}-submit-btn`);
|
|
1951
1954
|
if (submitBtn) {
|
|
1952
1955
|
submitBtn.disabled = !allFieldsComplete;
|
|
@@ -2172,7 +2175,7 @@ var init_checkout_button = __esm({
|
|
|
2172
2175
|
this._isLoading = false;
|
|
2173
2176
|
this.render();
|
|
2174
2177
|
this.setupEventListeners();
|
|
2175
|
-
this.dispatchError(error.message
|
|
2178
|
+
this.dispatchError(error instanceof Error ? error.message : "Failed to create checkout session");
|
|
2176
2179
|
}
|
|
2177
2180
|
});
|
|
2178
2181
|
this.attachShadow({ mode: "open" });
|
|
@@ -2620,7 +2623,7 @@ var init_portal_button = __esm({
|
|
|
2620
2623
|
this._isLoading = false;
|
|
2621
2624
|
this.render();
|
|
2622
2625
|
this.setupEventListeners();
|
|
2623
|
-
this.dispatchError(error.message
|
|
2626
|
+
this.dispatchError(error instanceof Error ? error.message : "Failed to create portal session");
|
|
2624
2627
|
}
|
|
2625
2628
|
}
|
|
2626
2629
|
dispatchError(message) {
|
|
@@ -2739,7 +2742,7 @@ function toCamelCase(obj) {
|
|
|
2739
2742
|
|
|
2740
2743
|
// package.json
|
|
2741
2744
|
var package_default = {
|
|
2742
|
-
version: "0.
|
|
2745
|
+
version: "0.13.0"};
|
|
2743
2746
|
var SDK_VERSION = package_default.version;
|
|
2744
2747
|
var SDK_TYPE = "react";
|
|
2745
2748
|
var RecurContext = React.createContext(null);
|
|
@@ -2765,7 +2768,7 @@ function RecurProvider({ children, config: initialConfig = {}, customer: custome
|
|
|
2765
2768
|
checkoutMode: "embedded",
|
|
2766
2769
|
...initialConfig
|
|
2767
2770
|
});
|
|
2768
|
-
}, [initialConfig
|
|
2771
|
+
}, [initialConfig]);
|
|
2769
2772
|
React__default.default.useEffect(() => {
|
|
2770
2773
|
setIsCheckingOut(false);
|
|
2771
2774
|
}, [config.checkoutMode]);
|
|
@@ -3263,9 +3266,10 @@ function RecurProvider({ children, config: initialConfig = {}, customer: custome
|
|
|
3263
3266
|
paymentForm.resetButton?.();
|
|
3264
3267
|
return;
|
|
3265
3268
|
} else {
|
|
3269
|
+
const failureMessage = failureError.details && !Array.isArray(failureError.details) ? failureError.details.failure_message : void 0;
|
|
3266
3270
|
paymentForm.showError?.({
|
|
3267
3271
|
title: "\u4ED8\u6B3E\u5931\u6557",
|
|
3268
|
-
message:
|
|
3272
|
+
message: failureMessage || failureError.message
|
|
3269
3273
|
});
|
|
3270
3274
|
paymentForm.resetButton?.();
|
|
3271
3275
|
return;
|
|
@@ -3392,7 +3396,8 @@ function RecurProvider({ children, config: initialConfig = {}, customer: custome
|
|
|
3392
3396
|
}
|
|
3393
3397
|
}
|
|
3394
3398
|
} catch (pollError) {
|
|
3395
|
-
|
|
3399
|
+
const errorMessage = pollError instanceof Error ? pollError.message : String(pollError);
|
|
3400
|
+
if (errorMessage === "3D \u9A57\u8B49\u5DF2\u53D6\u6D88" || errorMessage === "\u4ED8\u6B3E\u5931\u6557\u6216\u5DF2\u53D6\u6D88") {
|
|
3396
3401
|
throw pollError;
|
|
3397
3402
|
}
|
|
3398
3403
|
console.error("[Recur SDK] Poll error:", pollError);
|
|
@@ -3454,7 +3459,7 @@ function RecurProvider({ children, config: initialConfig = {}, customer: custome
|
|
|
3454
3459
|
currentPeriodEnd: paymentResult.subscription.currentPeriodEnd
|
|
3455
3460
|
});
|
|
3456
3461
|
} else {
|
|
3457
|
-
const chargeId = paymentResult.charge?.id || paymentResult.paymentIntent?.id;
|
|
3462
|
+
const chargeId = paymentResult.charge?.id || paymentResult.paymentIntent?.id || checkoutResult.checkout.id;
|
|
3458
3463
|
const chargeStatus = paymentResult.charge?.status || "SUCCEEDED";
|
|
3459
3464
|
options.onPaymentComplete({
|
|
3460
3465
|
id: chargeId,
|
package/dist/index.d.cts
CHANGED
|
@@ -347,6 +347,16 @@ interface CheckoutResult {
|
|
|
347
347
|
* SDK token expiration time
|
|
348
348
|
*/
|
|
349
349
|
sdkTokenExpiresAt: string;
|
|
350
|
+
/**
|
|
351
|
+
* Credit token for subscription payments
|
|
352
|
+
* Used internally for credit binding with payment gateway
|
|
353
|
+
*/
|
|
354
|
+
creditToken?: string;
|
|
355
|
+
/**
|
|
356
|
+
* SDK timestamp from getIframeToken response
|
|
357
|
+
* Used internally for subscription credit binding
|
|
358
|
+
*/
|
|
359
|
+
sdkTimestamp?: number;
|
|
350
360
|
}
|
|
351
361
|
/**
|
|
352
362
|
* Payment failure codes from payment provider
|
|
@@ -373,19 +383,69 @@ interface PaymentFailureDetails {
|
|
|
373
383
|
*/
|
|
374
384
|
can_retry?: boolean;
|
|
375
385
|
}
|
|
386
|
+
/**
|
|
387
|
+
* Standard API error codes from Recur API
|
|
388
|
+
*
|
|
389
|
+
* These codes match the backend error codes for consistent error handling.
|
|
390
|
+
* Use these to detect specific error conditions and provide appropriate UX.
|
|
391
|
+
*/
|
|
392
|
+
type ApiErrorCode = 'bad_request' | 'unauthorized' | 'payment_required' | 'forbidden' | 'not_found' | 'conflict' | 'validation_error' | 'rate_limit_exceeded' | 'internal_server_error';
|
|
393
|
+
/**
|
|
394
|
+
* Details for duplicate subscription errors (conflict)
|
|
395
|
+
* Present when code is 'conflict' and attempting to create a subscription
|
|
396
|
+
* for a product the customer already subscribes to
|
|
397
|
+
*/
|
|
398
|
+
interface DuplicateSubscriptionDetails {
|
|
399
|
+
/**
|
|
400
|
+
* ID of the existing subscription
|
|
401
|
+
*/
|
|
402
|
+
existing_subscription_id: string;
|
|
403
|
+
/**
|
|
404
|
+
* Status of the existing subscription
|
|
405
|
+
* Typically 'ACTIVE', 'TRIAL', or 'PAST_DUE'
|
|
406
|
+
*/
|
|
407
|
+
status: string;
|
|
408
|
+
}
|
|
409
|
+
/**
|
|
410
|
+
* Error details that can be either:
|
|
411
|
+
* - An array of objects (for API errors like 'conflict')
|
|
412
|
+
* - An object with payment failure fields (for onPaymentFailed callback)
|
|
413
|
+
*/
|
|
414
|
+
type CheckoutErrorDetails = (DuplicateSubscriptionDetails | Record<string, unknown>)[] | (Record<string, unknown> & PaymentFailureDetails);
|
|
376
415
|
interface CheckoutError {
|
|
377
416
|
/**
|
|
378
|
-
* Error code
|
|
417
|
+
* Error code - use ApiErrorCode for standard errors
|
|
379
418
|
*/
|
|
380
|
-
code: string;
|
|
419
|
+
code: ApiErrorCode | string;
|
|
381
420
|
/**
|
|
382
421
|
* Human-readable error message
|
|
383
422
|
*/
|
|
384
423
|
message: string;
|
|
424
|
+
/**
|
|
425
|
+
* URL to relevant documentation
|
|
426
|
+
*/
|
|
427
|
+
docUrl?: string;
|
|
385
428
|
/**
|
|
386
429
|
* Additional error details
|
|
430
|
+
*
|
|
431
|
+
* For 'conflict' errors when creating subscriptions (onError):
|
|
432
|
+
* details is an array where details[0] contains DuplicateSubscriptionDetails
|
|
433
|
+
*
|
|
434
|
+
* For payment failures (onPaymentFailed):
|
|
435
|
+
* details is an object with PaymentFailureDetails fields
|
|
436
|
+
*
|
|
437
|
+
* @example
|
|
438
|
+
* // Conflict error (array)
|
|
439
|
+
* if (error.code === 'conflict' && Array.isArray(error.details)) {
|
|
440
|
+
* const subId = error.details[0]?.existing_subscription_id;
|
|
441
|
+
* }
|
|
442
|
+
*
|
|
443
|
+
* // Payment failure (object)
|
|
444
|
+
* if (error.details && !Array.isArray(error.details)) {
|
|
445
|
+
* const canRetry = error.details.can_retry;
|
|
446
|
+
* }
|
|
387
447
|
*/
|
|
388
|
-
details?:
|
|
448
|
+
details?: CheckoutErrorDetails;
|
|
389
449
|
}
|
|
390
450
|
/**
|
|
391
451
|
* Action to take after payment failure
|
|
@@ -548,8 +608,8 @@ interface UniPaymentInstance {
|
|
|
548
608
|
getTradeResult: (options?: {
|
|
549
609
|
useDefault?: boolean;
|
|
550
610
|
cardInst?: number;
|
|
551
|
-
}) => Promise<
|
|
552
|
-
onUpdate?: (callback: (data:
|
|
611
|
+
}) => Promise<unknown>;
|
|
612
|
+
onUpdate?: (callback: (data: unknown) => void) => void;
|
|
553
613
|
}
|
|
554
614
|
/**
|
|
555
615
|
* Customer identifier for entitlements lookup
|
|
@@ -847,6 +907,7 @@ declare class RecurPaymentForm extends HTMLElement {
|
|
|
847
907
|
private customStyles;
|
|
848
908
|
private _isInitializing;
|
|
849
909
|
private _initializationAborted;
|
|
910
|
+
private _paymentSession;
|
|
850
911
|
private _showTestCardHints;
|
|
851
912
|
private _showCouponInput;
|
|
852
913
|
private _couponCode;
|
|
@@ -1168,7 +1229,7 @@ interface UseSubscribeOptions {
|
|
|
1168
1229
|
/**
|
|
1169
1230
|
* Callback when payment is completed
|
|
1170
1231
|
*/
|
|
1171
|
-
onPaymentComplete?: (subscription:
|
|
1232
|
+
onPaymentComplete?: (subscription: SubscriptionResult) => void;
|
|
1172
1233
|
/**
|
|
1173
1234
|
* Callback when an error occurs
|
|
1174
1235
|
*/
|
|
@@ -1395,4 +1456,4 @@ declare function useSubscribe(options?: UseSubscribeOptions): UseSubscribeResult
|
|
|
1395
1456
|
*/
|
|
1396
1457
|
declare function useCustomer(): UseCustomerResult;
|
|
1397
1458
|
|
|
1398
|
-
export { type CheckDeniedReason, type CheckOptions, type CheckResult, type CheckoutError, type CheckoutOptions, type CheckoutResult, type CustomerIdentifier, type Entitlement, type EntitlementCustomer, type EntitlementStatus, type EntitlementSubscription, type Plan, type PlansResult, type Product, type ProductMetadata, type ProductsResult, type RecurConfig, type RecurContextValue, RecurProvider, type RecurProviderProps, type SubscriptionResult, type UseCustomerResult, type UseProductsOptions as UsePlansOptions, type UseProductsResult as UsePlansResult, type UseProductsOptions, type UseProductsResult, type UseSubscribeOptions, type UseSubscribeResult, useCustomer, useProducts as usePlans, useProducts, useRecur, useSubscribe };
|
|
1459
|
+
export { type ApiErrorCode, type CheckDeniedReason, type CheckOptions, type CheckResult, type CheckoutError, type CheckoutErrorDetails, type CheckoutOptions, type CheckoutResult, type CustomerIdentifier, type DuplicateSubscriptionDetails, type Entitlement, type EntitlementCustomer, type EntitlementStatus, type EntitlementSubscription, type PaymentFailedAction, type PaymentFailureCode, type PaymentFailureDetails, type Plan, type PlansResult, type Product, type ProductMetadata, type ProductsResult, type RecurConfig, type RecurContextValue, RecurProvider, type RecurProviderProps, type SubscriptionResult, type UseCustomerResult, type UseProductsOptions as UsePlansOptions, type UseProductsResult as UsePlansResult, type UseProductsOptions, type UseProductsResult, type UseSubscribeOptions, type UseSubscribeResult, useCustomer, useProducts as usePlans, useProducts, useRecur, useSubscribe };
|
package/dist/index.d.ts
CHANGED
|
@@ -347,6 +347,16 @@ interface CheckoutResult {
|
|
|
347
347
|
* SDK token expiration time
|
|
348
348
|
*/
|
|
349
349
|
sdkTokenExpiresAt: string;
|
|
350
|
+
/**
|
|
351
|
+
* Credit token for subscription payments
|
|
352
|
+
* Used internally for credit binding with payment gateway
|
|
353
|
+
*/
|
|
354
|
+
creditToken?: string;
|
|
355
|
+
/**
|
|
356
|
+
* SDK timestamp from getIframeToken response
|
|
357
|
+
* Used internally for subscription credit binding
|
|
358
|
+
*/
|
|
359
|
+
sdkTimestamp?: number;
|
|
350
360
|
}
|
|
351
361
|
/**
|
|
352
362
|
* Payment failure codes from payment provider
|
|
@@ -373,19 +383,69 @@ interface PaymentFailureDetails {
|
|
|
373
383
|
*/
|
|
374
384
|
can_retry?: boolean;
|
|
375
385
|
}
|
|
386
|
+
/**
|
|
387
|
+
* Standard API error codes from Recur API
|
|
388
|
+
*
|
|
389
|
+
* These codes match the backend error codes for consistent error handling.
|
|
390
|
+
* Use these to detect specific error conditions and provide appropriate UX.
|
|
391
|
+
*/
|
|
392
|
+
type ApiErrorCode = 'bad_request' | 'unauthorized' | 'payment_required' | 'forbidden' | 'not_found' | 'conflict' | 'validation_error' | 'rate_limit_exceeded' | 'internal_server_error';
|
|
393
|
+
/**
|
|
394
|
+
* Details for duplicate subscription errors (conflict)
|
|
395
|
+
* Present when code is 'conflict' and attempting to create a subscription
|
|
396
|
+
* for a product the customer already subscribes to
|
|
397
|
+
*/
|
|
398
|
+
interface DuplicateSubscriptionDetails {
|
|
399
|
+
/**
|
|
400
|
+
* ID of the existing subscription
|
|
401
|
+
*/
|
|
402
|
+
existing_subscription_id: string;
|
|
403
|
+
/**
|
|
404
|
+
* Status of the existing subscription
|
|
405
|
+
* Typically 'ACTIVE', 'TRIAL', or 'PAST_DUE'
|
|
406
|
+
*/
|
|
407
|
+
status: string;
|
|
408
|
+
}
|
|
409
|
+
/**
|
|
410
|
+
* Error details that can be either:
|
|
411
|
+
* - An array of objects (for API errors like 'conflict')
|
|
412
|
+
* - An object with payment failure fields (for onPaymentFailed callback)
|
|
413
|
+
*/
|
|
414
|
+
type CheckoutErrorDetails = (DuplicateSubscriptionDetails | Record<string, unknown>)[] | (Record<string, unknown> & PaymentFailureDetails);
|
|
376
415
|
interface CheckoutError {
|
|
377
416
|
/**
|
|
378
|
-
* Error code
|
|
417
|
+
* Error code - use ApiErrorCode for standard errors
|
|
379
418
|
*/
|
|
380
|
-
code: string;
|
|
419
|
+
code: ApiErrorCode | string;
|
|
381
420
|
/**
|
|
382
421
|
* Human-readable error message
|
|
383
422
|
*/
|
|
384
423
|
message: string;
|
|
424
|
+
/**
|
|
425
|
+
* URL to relevant documentation
|
|
426
|
+
*/
|
|
427
|
+
docUrl?: string;
|
|
385
428
|
/**
|
|
386
429
|
* Additional error details
|
|
430
|
+
*
|
|
431
|
+
* For 'conflict' errors when creating subscriptions (onError):
|
|
432
|
+
* details is an array where details[0] contains DuplicateSubscriptionDetails
|
|
433
|
+
*
|
|
434
|
+
* For payment failures (onPaymentFailed):
|
|
435
|
+
* details is an object with PaymentFailureDetails fields
|
|
436
|
+
*
|
|
437
|
+
* @example
|
|
438
|
+
* // Conflict error (array)
|
|
439
|
+
* if (error.code === 'conflict' && Array.isArray(error.details)) {
|
|
440
|
+
* const subId = error.details[0]?.existing_subscription_id;
|
|
441
|
+
* }
|
|
442
|
+
*
|
|
443
|
+
* // Payment failure (object)
|
|
444
|
+
* if (error.details && !Array.isArray(error.details)) {
|
|
445
|
+
* const canRetry = error.details.can_retry;
|
|
446
|
+
* }
|
|
387
447
|
*/
|
|
388
|
-
details?:
|
|
448
|
+
details?: CheckoutErrorDetails;
|
|
389
449
|
}
|
|
390
450
|
/**
|
|
391
451
|
* Action to take after payment failure
|
|
@@ -548,8 +608,8 @@ interface UniPaymentInstance {
|
|
|
548
608
|
getTradeResult: (options?: {
|
|
549
609
|
useDefault?: boolean;
|
|
550
610
|
cardInst?: number;
|
|
551
|
-
}) => Promise<
|
|
552
|
-
onUpdate?: (callback: (data:
|
|
611
|
+
}) => Promise<unknown>;
|
|
612
|
+
onUpdate?: (callback: (data: unknown) => void) => void;
|
|
553
613
|
}
|
|
554
614
|
/**
|
|
555
615
|
* Customer identifier for entitlements lookup
|
|
@@ -847,6 +907,7 @@ declare class RecurPaymentForm extends HTMLElement {
|
|
|
847
907
|
private customStyles;
|
|
848
908
|
private _isInitializing;
|
|
849
909
|
private _initializationAborted;
|
|
910
|
+
private _paymentSession;
|
|
850
911
|
private _showTestCardHints;
|
|
851
912
|
private _showCouponInput;
|
|
852
913
|
private _couponCode;
|
|
@@ -1168,7 +1229,7 @@ interface UseSubscribeOptions {
|
|
|
1168
1229
|
/**
|
|
1169
1230
|
* Callback when payment is completed
|
|
1170
1231
|
*/
|
|
1171
|
-
onPaymentComplete?: (subscription:
|
|
1232
|
+
onPaymentComplete?: (subscription: SubscriptionResult) => void;
|
|
1172
1233
|
/**
|
|
1173
1234
|
* Callback when an error occurs
|
|
1174
1235
|
*/
|
|
@@ -1395,4 +1456,4 @@ declare function useSubscribe(options?: UseSubscribeOptions): UseSubscribeResult
|
|
|
1395
1456
|
*/
|
|
1396
1457
|
declare function useCustomer(): UseCustomerResult;
|
|
1397
1458
|
|
|
1398
|
-
export { type CheckDeniedReason, type CheckOptions, type CheckResult, type CheckoutError, type CheckoutOptions, type CheckoutResult, type CustomerIdentifier, type Entitlement, type EntitlementCustomer, type EntitlementStatus, type EntitlementSubscription, type Plan, type PlansResult, type Product, type ProductMetadata, type ProductsResult, type RecurConfig, type RecurContextValue, RecurProvider, type RecurProviderProps, type SubscriptionResult, type UseCustomerResult, type UseProductsOptions as UsePlansOptions, type UseProductsResult as UsePlansResult, type UseProductsOptions, type UseProductsResult, type UseSubscribeOptions, type UseSubscribeResult, useCustomer, useProducts as usePlans, useProducts, useRecur, useSubscribe };
|
|
1459
|
+
export { type ApiErrorCode, type CheckDeniedReason, type CheckOptions, type CheckResult, type CheckoutError, type CheckoutErrorDetails, type CheckoutOptions, type CheckoutResult, type CustomerIdentifier, type DuplicateSubscriptionDetails, type Entitlement, type EntitlementCustomer, type EntitlementStatus, type EntitlementSubscription, type PaymentFailedAction, type PaymentFailureCode, type PaymentFailureDetails, type Plan, type PlansResult, type Product, type ProductMetadata, type ProductsResult, type RecurConfig, type RecurContextValue, RecurProvider, type RecurProviderProps, type SubscriptionResult, type UseCustomerResult, type UseProductsOptions as UsePlansOptions, type UseProductsResult as UsePlansResult, type UseProductsOptions, type UseProductsResult, type UseSubscribeOptions, type UseSubscribeResult, useCustomer, useProducts as usePlans, useProducts, useRecur, useSubscribe };
|
package/dist/index.js
CHANGED
|
@@ -1091,6 +1091,7 @@ var init_payment_form = __esm({
|
|
|
1091
1091
|
__publicField(this, "customStyles");
|
|
1092
1092
|
__publicField(this, "_isInitializing", false);
|
|
1093
1093
|
__publicField(this, "_initializationAborted", false);
|
|
1094
|
+
__publicField(this, "_paymentSession", null);
|
|
1094
1095
|
// Test mode state
|
|
1095
1096
|
__publicField(this, "_showTestCardHints", false);
|
|
1096
1097
|
// Coupon state
|
|
@@ -1727,7 +1728,7 @@ var init_payment_form = __esm({
|
|
|
1727
1728
|
bubbles: true,
|
|
1728
1729
|
composed: true
|
|
1729
1730
|
}));
|
|
1730
|
-
} catch
|
|
1731
|
+
} catch {
|
|
1731
1732
|
this._couponError = "\u7DB2\u8DEF\u932F\u8AA4\uFF0C\u8ACB\u7A0D\u5F8C\u518D\u8A66";
|
|
1732
1733
|
this._couponLoading = false;
|
|
1733
1734
|
this.updateCouponSectionDOM();
|
|
@@ -1761,7 +1762,7 @@ var init_payment_form = __esm({
|
|
|
1761
1762
|
bubbles: true,
|
|
1762
1763
|
composed: true
|
|
1763
1764
|
}));
|
|
1764
|
-
} catch
|
|
1765
|
+
} catch {
|
|
1765
1766
|
this._couponError = "\u7DB2\u8DEF\u932F\u8AA4\uFF0C\u8ACB\u7A0D\u5F8C\u518D\u8A66";
|
|
1766
1767
|
this._couponLoading = false;
|
|
1767
1768
|
this.updateCouponSectionDOM();
|
|
@@ -1920,7 +1921,8 @@ var init_payment_form = __esm({
|
|
|
1920
1921
|
try {
|
|
1921
1922
|
await paymentSession.start();
|
|
1922
1923
|
} catch (startError) {
|
|
1923
|
-
const
|
|
1924
|
+
const errorObj = startError;
|
|
1925
|
+
const is1008Error = errorObj?.message?.includes("1008") || errorObj?.message?.includes("timeout") || errorObj?.code === 1008;
|
|
1924
1926
|
if (is1008Error && this._initializationAborted) {
|
|
1925
1927
|
console.log("[PaymentForm] Caught 1008 timeout error after component disconnect - ignoring");
|
|
1926
1928
|
this._isInitializing = false;
|
|
@@ -1940,7 +1942,8 @@ var init_payment_form = __esm({
|
|
|
1940
1942
|
return;
|
|
1941
1943
|
}
|
|
1942
1944
|
console.log("[PaymentForm] PAYUNi update:", data);
|
|
1943
|
-
const
|
|
1945
|
+
const updateData = data;
|
|
1946
|
+
const allFieldsComplete = updateData.status && updateData.status.CardNo === true && updateData.status.CardExp === true && updateData.status.CardCvc === true;
|
|
1944
1947
|
const submitBtn = document.getElementById(`${this.containerId}-submit-btn`);
|
|
1945
1948
|
if (submitBtn) {
|
|
1946
1949
|
submitBtn.disabled = !allFieldsComplete;
|
|
@@ -2166,7 +2169,7 @@ var init_checkout_button = __esm({
|
|
|
2166
2169
|
this._isLoading = false;
|
|
2167
2170
|
this.render();
|
|
2168
2171
|
this.setupEventListeners();
|
|
2169
|
-
this.dispatchError(error.message
|
|
2172
|
+
this.dispatchError(error instanceof Error ? error.message : "Failed to create checkout session");
|
|
2170
2173
|
}
|
|
2171
2174
|
});
|
|
2172
2175
|
this.attachShadow({ mode: "open" });
|
|
@@ -2614,7 +2617,7 @@ var init_portal_button = __esm({
|
|
|
2614
2617
|
this._isLoading = false;
|
|
2615
2618
|
this.render();
|
|
2616
2619
|
this.setupEventListeners();
|
|
2617
|
-
this.dispatchError(error.message
|
|
2620
|
+
this.dispatchError(error instanceof Error ? error.message : "Failed to create portal session");
|
|
2618
2621
|
}
|
|
2619
2622
|
}
|
|
2620
2623
|
dispatchError(message) {
|
|
@@ -2733,7 +2736,7 @@ function toCamelCase(obj) {
|
|
|
2733
2736
|
|
|
2734
2737
|
// package.json
|
|
2735
2738
|
var package_default = {
|
|
2736
|
-
version: "0.
|
|
2739
|
+
version: "0.13.0"};
|
|
2737
2740
|
var SDK_VERSION = package_default.version;
|
|
2738
2741
|
var SDK_TYPE = "react";
|
|
2739
2742
|
var RecurContext = createContext(null);
|
|
@@ -2759,7 +2762,7 @@ function RecurProvider({ children, config: initialConfig = {}, customer: custome
|
|
|
2759
2762
|
checkoutMode: "embedded",
|
|
2760
2763
|
...initialConfig
|
|
2761
2764
|
});
|
|
2762
|
-
}, [initialConfig
|
|
2765
|
+
}, [initialConfig]);
|
|
2763
2766
|
React.useEffect(() => {
|
|
2764
2767
|
setIsCheckingOut(false);
|
|
2765
2768
|
}, [config.checkoutMode]);
|
|
@@ -3257,9 +3260,10 @@ function RecurProvider({ children, config: initialConfig = {}, customer: custome
|
|
|
3257
3260
|
paymentForm.resetButton?.();
|
|
3258
3261
|
return;
|
|
3259
3262
|
} else {
|
|
3263
|
+
const failureMessage = failureError.details && !Array.isArray(failureError.details) ? failureError.details.failure_message : void 0;
|
|
3260
3264
|
paymentForm.showError?.({
|
|
3261
3265
|
title: "\u4ED8\u6B3E\u5931\u6557",
|
|
3262
|
-
message:
|
|
3266
|
+
message: failureMessage || failureError.message
|
|
3263
3267
|
});
|
|
3264
3268
|
paymentForm.resetButton?.();
|
|
3265
3269
|
return;
|
|
@@ -3386,7 +3390,8 @@ function RecurProvider({ children, config: initialConfig = {}, customer: custome
|
|
|
3386
3390
|
}
|
|
3387
3391
|
}
|
|
3388
3392
|
} catch (pollError) {
|
|
3389
|
-
|
|
3393
|
+
const errorMessage = pollError instanceof Error ? pollError.message : String(pollError);
|
|
3394
|
+
if (errorMessage === "3D \u9A57\u8B49\u5DF2\u53D6\u6D88" || errorMessage === "\u4ED8\u6B3E\u5931\u6557\u6216\u5DF2\u53D6\u6D88") {
|
|
3390
3395
|
throw pollError;
|
|
3391
3396
|
}
|
|
3392
3397
|
console.error("[Recur SDK] Poll error:", pollError);
|
|
@@ -3448,7 +3453,7 @@ function RecurProvider({ children, config: initialConfig = {}, customer: custome
|
|
|
3448
3453
|
currentPeriodEnd: paymentResult.subscription.currentPeriodEnd
|
|
3449
3454
|
});
|
|
3450
3455
|
} else {
|
|
3451
|
-
const chargeId = paymentResult.charge?.id || paymentResult.paymentIntent?.id;
|
|
3456
|
+
const chargeId = paymentResult.charge?.id || paymentResult.paymentIntent?.id || checkoutResult.checkout.id;
|
|
3452
3457
|
const chargeStatus = paymentResult.charge?.status || "SUCCEEDED";
|
|
3453
3458
|
options.onPaymentComplete({
|
|
3454
3459
|
id: chargeId,
|
package/dist/recur.umd.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"use strict";var RecurCheckout=(()=>{var te=Object.defineProperty;var vt=Object.getOwnPropertyDescriptor;var kt=Object.getOwnPropertyNames;var wt=Object.prototype.hasOwnProperty;var xt=(n,e,t)=>e in n?te(n,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):n[e]=t;var w=(n,e)=>()=>(n&&(e=n(n=0)),e);var
|
|
1
|
+
"use strict";var RecurCheckout=(()=>{var te=Object.defineProperty;var vt=Object.getOwnPropertyDescriptor;var kt=Object.getOwnPropertyNames;var wt=Object.prototype.hasOwnProperty;var xt=(n,e,t)=>e in n?te(n,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):n[e]=t;var w=(n,e)=>()=>(n&&(e=n(n=0)),e);var C=(n,e)=>{for(var t in e)te(n,t,{get:e[t],enumerable:!0})},_t=(n,e,t,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let o of kt(e))!wt.call(n,o)&&o!==t&&te(n,o,{get:()=>e[o],enumerable:!(r=vt(e,o))||r.enumerable});return n};var Et=n=>_t(te({},"__esModule",{value:!0}),n);var l=(n,e,t)=>xt(n,typeof e!="symbol"?e+"":e,t);var Pe={};C(Pe,{RecurLoadingSpinner:()=>re});var re,De=w(()=>{"use strict";re=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",re)});var
|
|
43
|
+
`}};typeof window<"u"&&!customElements.get("recur-loading-spinner")&&customElements.define("recur-loading-spinner",re)});var Me={};C(Me,{RecurSuccessMessage:()=>oe});var oe,Le=w(()=>{"use strict";oe=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",oe)});var Ue={};
|
|
124
|
+
`}};typeof window<"u"&&!customElements.get("recur-success-message")&&customElements.define("recur-success-message",oe)});var Ue={};C(Ue,{RecurErrorDisplay:()=>ie});var ie,He=w(()=>{"use strict";ie=class extends HTMLElement{static get observedAttributes(){return["error","error-title","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 errorTitle(){return this.getAttribute("error-title")||""}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;
|
|
@@ -220,7 +220,7 @@
|
|
|
220
220
|
</button>
|
|
221
221
|
`:""}
|
|
222
222
|
</div>
|
|
223
|
-
`,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",ie)});var Be={};
|
|
223
|
+
`,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",ie)});var Be={};C(Be,{RecurSkeletonLoader:()=>se});var se,Ne=w(()=>{"use strict";se=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`
|
|
224
224
|
<div class="skeleton-field">
|
|
225
225
|
<div class="skeleton-label"></div>
|
|
226
226
|
<div class="skeleton-input"></div>
|
|
@@ -391,9 +391,9 @@
|
|
|
391
391
|
</style>
|
|
392
392
|
|
|
393
393
|
${e}
|
|
394
|
-
`}};typeof window<"u"&&!customElements.get("recur-skeleton-loader")&&customElements.define("recur-skeleton-loader",se)});function Xe(n,e){if(!
|
|
394
|
+
`}};typeof window<"u"&&!customElements.get("recur-skeleton-loader")&&customElements.define("recur-skeleton-loader",se)});function Xe(n,e){if(!Ee(n)||!n.hasOwnProperty("raw"))throw Error("invalid template strings array");return ze!==void 0?ze.createHTML(e):e}function B(n,e,t=n,r){if(e===Y)return e;let o=r!==void 0?t._$Co?.[r]:t._$Cl,i=q(e)?void 0:e._$litDirective$;return o?.constructor!==i&&(o?._$AO?.(!1),i===void 0?o=void 0:(o=new i(n),o._$AT(n,t,r)),r!==void 0?(t._$Co??(t._$Co=[]))[r]=o:t._$Cl=o),o!==void 0&&(e=B(n,o._$AS(n,e.values),o,r)),e}var H,ne,ze,qe,A,Ye,Ct,L,V,q,Ee,St,ye,F,Oe,Ke,D,je,Fe,We,Ce,p,Yt,Wt,Y,f,Ve,M,Tt,W,ve,X,N,ke,we,xe,_e,$t,J,Se=w(()=>{"use strict";H=globalThis,ne=H.trustedTypes,ze=ne?ne.createPolicy("lit-html",{createHTML:n=>n}):void 0,qe="$lit$",A=`lit$${Math.random().toFixed(9).slice(2)}$`,Ye="?"+A,Ct=`<${Ye}>`,L=H.document===void 0?{createTreeWalker:()=>({})}:document,V=()=>L.createComment(""),q=n=>n===null||typeof n!="object"&&typeof n!="function",Ee=Array.isArray,St=n=>Ee(n)||typeof n?.[Symbol.iterator]=="function",ye=`[
|
|
395
395
|
\f\r]`,F=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,Oe=/-->/g,Ke=/>/g,D=RegExp(`>|${ye}(?:([^\\s"'>=/]+)(${ye}*=${ye}*(?:[^
|
|
396
|
-
\f\r"'\`<>=]|("|')|))|$)`,"g"),je=/'/g,Fe=/"/g,We=/^(?:script|style|textarea|title)$/i,
|
|
396
|
+
\f\r"'\`<>=]|("|')|))|$)`,"g"),je=/'/g,Fe=/"/g,We=/^(?:script|style|textarea|title)$/i,Ce=n=>(e,...t)=>({_$litType$:n,strings:e,values:t}),p=Ce(1),Yt=Ce(2),Wt=Ce(3),Y=Symbol.for("lit-noChange"),f=Symbol.for("lit-nothing"),Ve=new WeakMap,M=L.createTreeWalker(L,129);Tt=(n,e)=>{let t=n.length-1,r=[],o,i=e===2?"<svg>":e===3?"<math>":"",s=F;for(let a=0;a<t;a++){let c=n[a],d,h,m=-1,x=0;for(;x<c.length&&(s.lastIndex=x,h=s.exec(c),h!==null);)x=s.lastIndex,s===F?h[1]==="!--"?s=Oe:h[1]!==void 0?s=Ke:h[2]!==void 0?(We.test(h[2])&&(o=RegExp("</"+h[2],"g")),s=D):h[3]!==void 0&&(s=D):s===D?h[0]===">"?(s=o??F,m=-1):h[1]===void 0?m=-2:(m=s.lastIndex-h[2].length,d=h[1],s=h[3]===void 0?D:h[3]==='"'?Fe:je):s===Fe||s===je?s=D:s===Oe||s===Ke?s=F:(s=D,o=void 0);let u=s===D&&n[a+1].startsWith("/>")?" ":"";i+=s===F?c+Ct:m>=0?(r.push(d),c.slice(0,m)+qe+c.slice(m)+A+u):c+A+(m===-2?a:u)}return[Xe(n,i+(n[t]||"<?>")+(e===2?"</svg>":e===3?"</math>":"")),r]},W=class n{constructor({strings:e,_$litType$:t},r){let o;this.parts=[];let i=0,s=0,a=e.length-1,c=this.parts,[d,h]=Tt(e,t);if(this.el=n.createElement(d,r),M.currentNode=this.el.content,t===2||t===3){let m=this.el.content.firstChild;m.replaceWith(...m.childNodes)}for(;(o=M.nextNode())!==null&&c.length<a;){if(o.nodeType===1){if(o.hasAttributes())for(let m of o.getAttributeNames())if(m.endsWith(qe)){let x=h[s++],u=o.getAttribute(m).split(A),T=/([.?@])?(.*)/.exec(x);c.push({type:1,index:i,name:T[2],strings:u,ctor:T[1]==="."?ke:T[1]==="?"?we:T[1]==="@"?xe:N}),o.removeAttribute(m)}else m.startsWith(A)&&(c.push({type:6,index:i}),o.removeAttribute(m));if(We.test(o.tagName)){let m=o.textContent.split(A),x=m.length-1;if(x>0){o.textContent=ne?ne.emptyScript:"";for(let u=0;u<x;u++)o.append(m[u],V()),M.nextNode(),c.push({type:2,index:++i});o.append(m[x],V())}}}else if(o.nodeType===8)if(o.data===Ye)c.push({type:2,index:i});else{let m=-1;for(;(m=o.data.indexOf(A,m+1))!==-1;)c.push({type:7,index:i}),m+=A.length-1}i++}}static createElement(e,t){let r=L.createElement("template");return r.innerHTML=e,r}};ve=class{constructor(e,t){this._$AV=[],this._$AN=void 0,this._$AD=e,this._$AM=t}get parentNode(){return this._$AM.parentNode}get _$AU(){return this._$AM._$AU}u(e){let{el:{content:t},parts:r}=this._$AD,o=(e?.creationScope??L).importNode(t,!0);M.currentNode=o;let i=M.nextNode(),s=0,a=0,c=r[0];for(;c!==void 0;){if(s===c.index){let d;c.type===2?d=new X(i,i.nextSibling,this,e):c.type===1?d=new c.ctor(i,c.name,c.strings,this,e):c.type===6&&(d=new _e(i,this,e)),this._$AV.push(d),c=r[++a]}s!==c?.index&&(i=M.nextNode(),s++)}return M.currentNode=L,o}p(e){let t=0;for(let r of this._$AV)r!==void 0&&(r.strings!==void 0?(r._$AI(e,r,t),t+=r.strings.length-2):r._$AI(e[t])),t++}},X=class n{get _$AU(){return this._$AM?._$AU??this._$Cv}constructor(e,t,r,o){this.type=2,this._$AH=f,this._$AN=void 0,this._$AA=e,this._$AB=t,this._$AM=r,this.options=o,this._$Cv=o?.isConnected??!0}get parentNode(){let e=this._$AA.parentNode,t=this._$AM;return t!==void 0&&e?.nodeType===11&&(e=t.parentNode),e}get startNode(){return this._$AA}get endNode(){return this._$AB}_$AI(e,t=this){e=B(this,e,t),q(e)?e===f||e==null||e===""?(this._$AH!==f&&this._$AR(),this._$AH=f):e!==this._$AH&&e!==Y&&this._(e):e._$litType$!==void 0?this.$(e):e.nodeType!==void 0?this.T(e):St(e)?this.k(e):this._(e)}O(e){return this._$AA.parentNode.insertBefore(e,this._$AB)}T(e){this._$AH!==e&&(this._$AR(),this._$AH=this.O(e))}_(e){this._$AH!==f&&q(this._$AH)?this._$AA.nextSibling.data=e:this.T(L.createTextNode(e)),this._$AH=e}$(e){let{values:t,_$litType$:r}=e,o=typeof r=="number"?this._$AC(e):(r.el===void 0&&(r.el=W.createElement(Xe(r.h,r.h[0]),this.options)),r);if(this._$AH?._$AD===o)this._$AH.p(t);else{let i=new ve(o,this),s=i.u(this.options);i.p(t),this.T(s),this._$AH=i}}_$AC(e){let t=Ve.get(e.strings);return t===void 0&&Ve.set(e.strings,t=new W(e)),t}k(e){Ee(this._$AH)||(this._$AH=[],this._$AR());let t=this._$AH,r,o=0;for(let i of e)o===t.length?t.push(r=new n(this.O(V()),this.O(V()),this,this.options)):r=t[o],r._$AI(i),o++;o<t.length&&(this._$AR(r&&r._$AB.nextSibling,o),t.length=o)}_$AR(e=this._$AA.nextSibling,t){for(this._$AP?.(!1,!0,t);e!==this._$AB;){let r=e.nextSibling;e.remove(),e=r}}setConnected(e){this._$AM===void 0&&(this._$Cv=e,this._$AP?.(e))}},N=class{get tagName(){return this.element.tagName}get _$AU(){return this._$AM._$AU}constructor(e,t,r,o,i){this.type=1,this._$AH=f,this._$AN=void 0,this.element=e,this.name=t,this._$AM=o,this.options=i,r.length>2||r[0]!==""||r[1]!==""?(this._$AH=Array(r.length-1).fill(new String),this.strings=r):this._$AH=f}_$AI(e,t=this,r,o){let i=this.strings,s=!1;if(i===void 0)e=B(this,e,t,0),s=!q(e)||e!==this._$AH&&e!==Y,s&&(this._$AH=e);else{let a=e,c,d;for(e=i[0],c=0;c<i.length-1;c++)d=B(this,a[r+c],t,c),d===Y&&(d=this._$AH[c]),s||(s=!q(d)||d!==this._$AH[c]),d===f?e=f:e!==f&&(e+=(d??"")+i[c+1]),this._$AH[c]=d}s&&!o&&this.j(e)}j(e){e===f?this.element.removeAttribute(this.name):this.element.setAttribute(this.name,e??"")}},ke=class extends N{constructor(){super(...arguments),this.type=3}j(e){this.element[this.name]=e===f?void 0:e}},we=class extends N{constructor(){super(...arguments),this.type=4}j(e){this.element.toggleAttribute(this.name,!!e&&e!==f)}},xe=class extends N{constructor(e,t,r,o,i){super(e,t,r,o,i),this.type=5}_$AI(e,t=this){if((e=B(this,e,t,0)??f)===Y)return;let r=this._$AH,o=e===f&&r!==f||e.capture!==r.capture||e.once!==r.once||e.passive!==r.passive,i=e!==f&&(r===f||o);o&&this.element.removeEventListener(this.name,this,r),i&&this.element.addEventListener(this.name,this,e),this._$AH=e}handleEvent(e){typeof this._$AH=="function"?this._$AH.call(this.options?.host??this.element,e):this._$AH.handleEvent(e)}},_e=class{constructor(e,t,r){this.element=e,this.type=6,this._$AN=void 0,this._$AM=t,this.options=r}get _$AU(){return this._$AM._$AU}_$AI(e){B(this,e)}},$t=H.litHtmlPolyfillSupport;$t?.(W,X),(H.litHtmlVersions??(H.litHtmlVersions=[])).push("3.3.1");J=(n,e,t)=>{let r=t?.renderBefore??e,o=r._$litPart$;if(o===void 0){let i=t?.renderBefore??null;r._$litPart$=o=new X(e.insertBefore(V(),i),i,void 0,t??{})}return o._$AI(n),o}});var Ge,Je=w(()=>{Ge=`/**
|
|
397
397
|
* Payment Form Skeleton - Shadow DOM Styles
|
|
398
398
|
* Loading skeleton that matches the structure of RecurPaymentForm
|
|
399
399
|
*/
|
|
@@ -605,7 +605,7 @@
|
|
|
605
605
|
color: #4a5568;
|
|
606
606
|
margin: 0;
|
|
607
607
|
}
|
|
608
|
-
`});var Ze={};
|
|
608
|
+
`});var Ze={};C(Ze,{RecurPaymentFormSkeleton:()=>ae});var At,ae,Qe=w(()=>{"use strict";Se();Je();At=p`
|
|
609
609
|
<svg class="security-icon" width="16" height="16" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
610
610
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z" />
|
|
611
611
|
</svg>
|
|
@@ -682,13 +682,13 @@
|
|
|
682
682
|
${this.renderSubmitButton()}
|
|
683
683
|
${this.renderSecurityNote()}
|
|
684
684
|
</div>
|
|
685
|
-
`;J(e,this.shadowRoot)}};typeof window<"u"&&!customElements.get("recur-payment-form-skeleton")&&customElements.define("recur-payment-form-skeleton",ae)});var et={};
|
|
685
|
+
`;J(e,this.shadowRoot)}};typeof window<"u"&&!customElements.get("recur-payment-form-skeleton")&&customElements.define("recur-payment-form-skeleton",ae)});var et={};C(et,{RecurToast:()=>ce,RecurToastContainer:()=>G});var ce,S,G,tt=w(()=>{"use strict";ce=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">
|
|
686
686
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7" />
|
|
687
687
|
</svg>`;case"error":return`<svg width="20" height="20" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
688
688
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
|
|
689
|
-
</svg>`;
|
|
689
|
+
</svg>`;default:return`<svg width="20" height="20" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
690
690
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
|
|
691
|
-
</svg>`}}getTypeColor(){switch(this.type){case"success":return"#10b981";case"error":return"#ef4444";
|
|
691
|
+
</svg>`}}getTypeColor(){switch(this.type){case"success":return"#10b981";case"error":return"#ef4444";default:return"#3b82f6"}}render(){let e=this.getTypeColor();this.shadowRoot.innerHTML=`
|
|
692
692
|
<style>
|
|
693
693
|
:host {
|
|
694
694
|
display: block;
|
|
@@ -1515,7 +1515,7 @@
|
|
|
1515
1515
|
.recur-test-card-meta {
|
|
1516
1516
|
color: #9ca3af;
|
|
1517
1517
|
}
|
|
1518
|
-
`});var nt={};
|
|
1518
|
+
`});var nt={};C(nt,{RecurPaymentForm:()=>le});var U,le,at=w(()=>{"use strict";Se();rt();it();U={lock:p`
|
|
1519
1519
|
<svg class="security-icon" width="16" height="16" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
1520
1520
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z" />
|
|
1521
1521
|
</svg>
|
|
@@ -1535,7 +1535,7 @@
|
|
|
1535
1535
|
<svg class="recur-coupon__clear-icon" width="16" height="16" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
1536
1536
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
|
|
1537
1537
|
</svg>
|
|
1538
|
-
`,spinner:p`<span class="recur-coupon__spinner"></span>`},le=class extends HTMLElement{constructor(){super();l(this,"containerId");l(this,"customStyles");l(this,"_isInitializing",!1);l(this,"_initializationAborted",!1);l(this,"_showTestCardHints",!1);l(this,"_showCouponInput",!1);l(this,"_couponCode","");l(this,"_couponLoading",!1);l(this,"_couponError",null);l(this,"_appliedCoupon",null);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","product-name","amount","interval","checkout-id","publishable-key","api-base-url"]}attributeChangedCallback(t,r,o){r!==o&&(t==="custom-styles"?(this.customStyles=o||"",this.updateCustomStyles()):this.updateOrderSummaryDOM())}getShadowDOMStyles(){return ot}getLightDOMStyles(){return st}render(){this.renderShadowDOM(),this.renderLightDOM()}renderShadowDOM(){let t=p`
|
|
1538
|
+
`,spinner:p`<span class="recur-coupon__spinner"></span>`},le=class extends HTMLElement{constructor(){super();l(this,"containerId");l(this,"customStyles");l(this,"_isInitializing",!1);l(this,"_initializationAborted",!1);l(this,"_paymentSession",null);l(this,"_showTestCardHints",!1);l(this,"_showCouponInput",!1);l(this,"_couponCode","");l(this,"_couponLoading",!1);l(this,"_couponError",null);l(this,"_appliedCoupon",null);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","product-name","amount","interval","checkout-id","publishable-key","api-base-url"]}attributeChangedCallback(t,r,o){r!==o&&(t==="custom-styles"?(this.customStyles=o||"",this.updateCustomStyles()):this.updateOrderSummaryDOM())}getShadowDOMStyles(){return ot}getLightDOMStyles(){return st}render(){this.renderShadowDOM(),this.renderLightDOM()}renderShadowDOM(){let t=p`
|
|
1539
1539
|
<style>${this.getShadowDOMStyles()}</style>
|
|
1540
1540
|
|
|
1541
1541
|
<div class="payment-form-wrapper">
|
|
@@ -1563,7 +1563,7 @@
|
|
|
1563
1563
|
<slot name="actions"></slot>
|
|
1564
1564
|
|
|
1565
1565
|
<div class="security-note">
|
|
1566
|
-
${
|
|
1566
|
+
${U.lock}
|
|
1567
1567
|
<p class="security-text">您的付款資訊經過加密保護</p>
|
|
1568
1568
|
</div>
|
|
1569
1569
|
</div>
|
|
@@ -1670,7 +1670,7 @@
|
|
|
1670
1670
|
<div class="recur-coupon__applied-wrapper">
|
|
1671
1671
|
<div class="recur-coupon__applied">
|
|
1672
1672
|
<div class="recur-coupon__applied-info">
|
|
1673
|
-
${
|
|
1673
|
+
${U.tagApplied}
|
|
1674
1674
|
${this._appliedCoupon.couponName?p`
|
|
1675
1675
|
<span class="recur-coupon__applied-name">${this._appliedCoupon.couponName}</span>
|
|
1676
1676
|
<span class="recur-coupon__applied-code">(${this._appliedCoupon.code})</span>
|
|
@@ -1683,7 +1683,7 @@
|
|
|
1683
1683
|
?disabled=${this._couponLoading}
|
|
1684
1684
|
@click=${()=>this.removeCoupon()}
|
|
1685
1685
|
>
|
|
1686
|
-
${this._couponLoading?
|
|
1686
|
+
${this._couponLoading?U.spinner:U.close}
|
|
1687
1687
|
</button>
|
|
1688
1688
|
</div>
|
|
1689
1689
|
<span class="recur-coupon__applied-amount">
|
|
@@ -1721,7 +1721,7 @@
|
|
|
1721
1721
|
@click=${()=>this.clearCouponInput()}
|
|
1722
1722
|
aria-label="清除"
|
|
1723
1723
|
>
|
|
1724
|
-
${
|
|
1724
|
+
${U.clearInput}
|
|
1725
1725
|
</button>
|
|
1726
1726
|
`:f}
|
|
1727
1727
|
</div>
|
|
@@ -1731,7 +1731,7 @@
|
|
|
1731
1731
|
?disabled=${this._couponLoading||!this._couponCode.trim()}
|
|
1732
1732
|
@click=${()=>this.applyCoupon()}
|
|
1733
1733
|
>
|
|
1734
|
-
${this._couponLoading?
|
|
1734
|
+
${this._couponLoading?U.spinner:"\u5957\u7528"}
|
|
1735
1735
|
</button>
|
|
1736
1736
|
</div>
|
|
1737
1737
|
${this._couponError?p`
|
|
@@ -1744,7 +1744,7 @@
|
|
|
1744
1744
|
class="recur-coupon__trigger"
|
|
1745
1745
|
@click=${()=>this.showCouponInput()}
|
|
1746
1746
|
>
|
|
1747
|
-
${
|
|
1747
|
+
${U.tag}
|
|
1748
1748
|
有優惠碼?
|
|
1749
1749
|
</button>
|
|
1750
1750
|
`}showCouponInput(){this._showCouponInput=!0,this._couponError=null,this.updateCouponSectionDOM(),requestAnimationFrame(()=>{document.getElementById(`${this.containerId}-coupon-input`)?.focus()})}cancelCouponInput(){this._showCouponInput=!1,this._couponCode="",this._couponError=null,this.updateCouponSectionDOM()}clearCouponInput(){this._couponCode="",this._couponError=null,this.updateCouponSectionDOM(),requestAnimationFrame(()=>{document.getElementById(`${this.containerId}-coupon-input`)?.focus()})}handleCouponInputChange(t){this._couponCode=t.target.value.toUpperCase(),this._couponError=null,this.updateApplyButtonState()}handleCouponKeydown(t){t.key==="Enter"?(t.preventDefault(),this.applyCoupon()):t.key==="Escape"&&this.cancelCouponInput()}updateApplyButtonState(){let t=this.querySelector(".recur-coupon__btn--apply");t&&(t.disabled=this._couponLoading||!this._couponCode.trim())}updateCouponSectionDOM(){let t=`${this.containerId}-coupon`,r=document.getElementById(t);r&&(this._appliedCoupon?(r.innerHTML=this.getCouponAppliedHTML(),this.bindCouponAppliedEvents(r)):this._showCouponInput?(r.innerHTML=this.getCouponInputHTML(),this.bindCouponInputEvents(r)):(r.innerHTML=this.getCouponTriggerHTML(),this.bindCouponTriggerEvents(r)))}getCouponTriggerHTML(){return`
|
|
@@ -1870,13 +1870,13 @@
|
|
|
1870
1870
|
>
|
|
1871
1871
|
<span id="${this.containerId}-submit-text">確認付款</span>
|
|
1872
1872
|
</button>
|
|
1873
|
-
`}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 o=document.getElementById(`${this.containerId}-card-no`),i=document.getElementById(`${this.containerId}-card-exp`),s=document.getElementById(`${this.containerId}-card-cvc`);if(!o||!i||!s)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 a=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 a.start()}catch(c){if((
|
|
1873
|
+
`}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 o=document.getElementById(`${this.containerId}-card-no`),i=document.getElementById(`${this.containerId}-card-exp`),s=document.getElementById(`${this.containerId}-card-cvc`);if(!o||!i||!s)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 a=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 a.start()}catch(c){let d=c;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 c}return this._initializationAborted?(console.log("[PaymentForm] Initialization aborted after paymentSession.start()"),this._isInitializing=!1,null):(this.hideCardSkeletons(),a.onUpdate?.(c=>{if(this._initializationAborted){console.log("[PaymentForm] Ignoring onUpdate event - component disconnected");return}console.log("[PaymentForm] PAYUNi update:",c);let d=c,h=d.status&&d.status.CardNo===!0&&d.status.CardExp===!0&&d.status.CardCvc===!0,m=document.getElementById(`${this.containerId}-submit-btn`);m&&(m.disabled=!h,console.log("[PaymentForm] Submit button disabled:",!h))}),this._initializationAborted?(console.log("[PaymentForm] Initialization aborted before saving session"),this._isInitializing=!1,null):(this._paymentSession=a,this.setupFormSubmission(),this._isInitializing=!1,a))}catch(o){throw this._isInitializing=!1,console.error("[PaymentForm] Failed to initialize payment:",o),o}}setupFormSubmission(){let t=document.getElementById(`${this.containerId}-submit-btn`);t&&t.addEventListener("click",async r=>{if(r.preventDefault(),t.classList.contains("loading"))return;let o,i,s=document.getElementById(`${this.containerId}-email`),a=document.getElementById(`${this.containerId}-name`);if(s&&a){if(o=s.value,i=a.value,!o||!i){this.showError("\u8ACB\u586B\u5BEB\u59D3\u540D\u548C\u96FB\u5B50\u90F5\u4EF6");return}}else if(o=this.getAttribute("customer-email")||void 0,i=this.getAttribute("customer-name")||void 0,!o||!i){this.showError("\u5BA2\u6236\u8CC7\u8A0A\u4E0D\u5B8C\u6574");return}this.clearError(),this.setButtonLoading(!0),this.dispatchEvent(new CustomEvent("submit",{detail:{customerEmail:o,customerName:i,paymentSession:this._paymentSession,appliedCoupon:this._appliedCoupon},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=`
|
|
1874
1874
|
<span class="recur-loading-spinner"></span>
|
|
1875
1875
|
<span>\u8655\u7406\u4E2D...</span>
|
|
1876
1876
|
`):(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=`
|
|
1877
1877
|
<span class="recur-loading-spinner"></span>
|
|
1878
1878
|
<span>3D \u9A57\u8B49\u4E2D...</span>
|
|
1879
|
-
`):this.setButtonLoading(!1))}showError(t){let r=typeof t=="string"?t:t.message,o=typeof t=="object"?t.title:void 0,i=this.querySelector(".recur-sdk__error-container");i||(i=document.createElement("div"),i.className="recur-sdk__error-container",i.style.cssText="margin-bottom: 16px;",this.insertBefore(i,this.firstChild));let s=document.createElement("recur-error-display");s.setAttribute("error",r),o&&s.setAttribute("error-title",o),s.setAttribute("dismissible","true"),i.innerHTML="",i.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",le)});var ct={};
|
|
1879
|
+
`):this.setButtonLoading(!1))}showError(t){let r=typeof t=="string"?t:t.message,o=typeof t=="object"?t.title:void 0,i=this.querySelector(".recur-sdk__error-container");i||(i=document.createElement("div"),i.className="recur-sdk__error-container",i.style.cssText="margin-bottom: 16px;",this.insertBefore(i,this.firstChild));let s=document.createElement("recur-error-display");s.setAttribute("error",r),o&&s.setAttribute("error-title",o),s.setAttribute("dismissible","true"),i.innerHTML="",i.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",le)});var ct={};C(ct,{RecurCheckoutButton:()=>de});var de,lt=w(()=>{"use strict";de=class extends HTMLElement{constructor(){super();l(this,"_isLoading",!1);l(this,"handleClick",async t=>{if(t.preventDefault(),this._isLoading||this.hasAttribute("disabled"))return;let r=this.getAttribute("publishable-key"),o=this.getAttribute("product-id"),i=this.getAttribute("success-url"),s=this.getAttribute("cancel-url");if(!r){this.dispatchError("Missing required attribute: publishable-key");return}if(!o){this.dispatchError("Missing required attribute: product-id");return}if(!i){this.dispatchError("Missing required attribute: success-url");return}if(!s){this.dispatchError("Missing required attribute: cancel-url");return}this._isLoading=!0,this.render(),this.setupEventListeners();try{let a=await this.createCheckoutSession({publishableKey:r,productId:o,successUrl:this.resolveUrl(i),cancelUrl:this.resolveUrl(s),customerEmail:this.getAttribute("customer-email")||void 0,mode:this.getAttribute("mode")});this.dispatchEvent(new CustomEvent("checkout-started",{detail:{sessionId:a.id,url:a.url},bubbles:!0,composed:!0})),window.location.href=a.url}catch(a){this._isLoading=!1,this.render(),this.setupEventListeners(),this.dispatchError(a instanceof Error?a.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,o){r!==o&&this.render()}render(){let t=this.getAttribute("button-text")||this.textContent?.trim()||"\u8A02\u95B1",r=this.getAttribute("button-style")||"primary",o=this.hasAttribute("disabled")||this._isLoading;this.shadowRoot.innerHTML=`
|
|
1880
1880
|
<style>
|
|
1881
1881
|
:host {
|
|
1882
1882
|
display: inline-block;
|
|
@@ -1975,7 +1975,7 @@
|
|
|
1975
1975
|
${this._isLoading?'<span class="spinner"></span>':""}
|
|
1976
1976
|
<span>${this._isLoading?"\u8655\u7406\u4E2D...":t}</span>
|
|
1977
1977
|
</button>
|
|
1978
|
-
`}setupEventListeners(){let t=this.shadowRoot?.querySelector("button");t&&t.addEventListener("click",this.handleClick)}async createCheckoutSession(t){let r=this.getApiBaseUrl(),o={productId:t.productId,successUrl:t.successUrl,cancelUrl:t.cancelUrl};t.mode&&(o.mode=t.mode),t.customerEmail&&(o.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(o)});if(!i.ok){let s=await i.json().catch(()=>({}));throw new Error(s.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",de)});var dt={};
|
|
1978
|
+
`}setupEventListeners(){let t=this.shadowRoot?.querySelector("button");t&&t.addEventListener("click",this.handleClick)}async createCheckoutSession(t){let r=this.getApiBaseUrl(),o={productId:t.productId,successUrl:t.successUrl,cancelUrl:t.cancelUrl};t.mode&&(o.mode=t.mode),t.customerEmail&&(o.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(o)});if(!i.ok){let s=await i.json().catch(()=>({}));throw new Error(s.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",de)});var dt={};C(dt,{RecurPortalButton:()=>ue});var ue,ut=w(()=>{"use strict";ue=class extends HTMLElement{constructor(){super();l(this,"_isLoading",!1);l(this,"handleClick",async t=>{if(t.preventDefault(),this._isLoading||this.hasAttribute("disabled"))return;let r=this.getAttribute("portal-url"),o=this.getAttribute("api-endpoint");if(r){this.redirectToPortal(r);return}if(o){await this.fetchAndRedirect(o);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,o){r!==o&&this.render()}render(){let t=this.getAttribute("button-text")||this.textContent?.trim()||"\u7BA1\u7406\u8A02\u95B1",r=this.getAttribute("button-style")||"primary",o=this.hasAttribute("disabled")||this._isLoading;this.shadowRoot.innerHTML=`
|
|
1979
1979
|
<style>
|
|
1980
1980
|
:host {
|
|
1981
1981
|
display: inline-block;
|
|
@@ -2102,7 +2102,7 @@
|
|
|
2102
2102
|
<path d="M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2"/>
|
|
2103
2103
|
<circle cx="12" cy="7" r="4"/>
|
|
2104
2104
|
</svg>
|
|
2105
|
-
`}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"),o=this.getAttribute("return-url");this._isLoading=!0,this.render(),this.setupEventListeners();try{let i={};r&&(i.customerId=r),o&&(i.returnUrl=o);let s=await fetch(t,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(i)});if(!s.ok){let d=await s.json().catch(()=>({}));throw new Error(d.error?.message||d.message||`HTTP ${s.status}: Failed to create portal session`)}let a=await s.json(),c=a.url||a.portalUrl;if(!c)throw new Error("Portal URL not found in response");this._isLoading=!1,this.render(),this.setupEventListeners(),this.redirectToPortal(c)}catch(i){this._isLoading=!1,this.render(),this.setupEventListeners(),this.dispatchError(i.message
|
|
2105
|
+
`}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"),o=this.getAttribute("return-url");this._isLoading=!0,this.render(),this.setupEventListeners();try{let i={};r&&(i.customerId=r),o&&(i.returnUrl=o);let s=await fetch(t,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(i)});if(!s.ok){let d=await s.json().catch(()=>({}));throw new Error(d.error?.message||d.message||`HTTP ${s.status}: Failed to create portal session`)}let a=await s.json(),c=a.url||a.portalUrl;if(!c)throw new Error("Portal URL not found in response");this._isLoading=!1,this.render(),this.setupEventListeners(),this.redirectToPortal(c)}catch(i){this._isLoading=!1,this.render(),this.setupEventListeners(),this.dispatchError(i instanceof Error?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",ue)});var jt={};C(jt,{RecurCheckout:()=>O,RecurElements:()=>z,create:()=>gt,createElements:()=>Te,default:()=>Kt,init:()=>ht});async function Dt(){if(typeof window>"u"){console.warn("[Recur SDK] Cannot register Web Components: window is not defined");return}await Promise.all([Promise.resolve().then(()=>(De(),Pe)),Promise.resolve().then(()=>(Le(),Me)),Promise.resolve().then(()=>(He(),Ue)),Promise.resolve().then(()=>(Ne(),Be)),Promise.resolve().then(()=>(Qe(),Ze)),Promise.resolve().then(()=>(tt(),et)),Promise.resolve().then(()=>(at(),nt)),Promise.resolve().then(()=>(lt(),ct)),Promise.resolve().then(()=>(ut(),dt))]);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"&&Dt();function Mt(n){return n.replace(/_([a-z])/g,(e,t)=>t.toUpperCase())}function k(n){if(n==null)return n;if(Array.isArray(n))return n.map(e=>k(e));if(n instanceof Date)return n;if(typeof n=="object"){let e={};for(let[t,r]of Object.entries(n)){let o=Mt(t);e[o]=k(r)}return e}return n}var pe={name:"recur-tw",version:"0.13.0",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 . -p 8080 -o /examples/"},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","./checkout":"./dist/checkout.js","./widget":"./dist/widget.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"},dependencies:{"lit-html":"^3.3.1"}};var Ut=pe.version,Ht="vanilla",me=class{constructor(e){l(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":Ht,"X-Recur-SDK-Version":Ut,"X-Recur-Source":typeof window<"u"?window.location.origin:"server"}}async createSubscription(e){let{customerName:t,customerEmail:r}=e,o=e.productId||e.planId,i=e.productSlug;if(!o&&!i)throw new Error("Either productId or productSlug is required");let s={customerName:t,customerEmail:r};o&&(s.productId=o),i&&(s.productSlug=i);let a=await fetch(`${this.config.baseUrl}/v1/subscriptions`,{method:"POST",headers:this.getHeaders(),body:JSON.stringify(s)});if(!a.ok){let d=await a.json().catch(()=>({}));throw{code:d.error||"CHECKOUT_FAILED",message:d.message||"Failed to initiate checkout",details:d}}let c=await a.json();return k(c)}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 o=await r.json();return k(o)}async fetchPlans(){return{plans:(await this.fetchProducts({type:"SUBSCRIPTION"})).products}}getConfig(){return{...this.config}}};var he=class{constructor(e,t){l(this,"config");l(this,"options");l(this,"container");l(this,"checkoutId",null);l(this,"sdkToken",null);l(this,"sdkTimestamp",null);l(this,"creditToken",null);l(this,"sdkEnv","S");l(this,"payuniSDK",null);l(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.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(),o=k(r);this.checkoutId=o.checkout.id,this.sdkToken=o.sdkToken,this.sdkTimestamp=o.sdkTimestamp||null,this.creditToken=o.creditToken||null,this.sdkEnv=this.config.publishableKey.startsWith("pk_test_")?"S":"P"}renderHTML(){this.container.innerHTML=`
|
|
2106
2106
|
<div class="recur-embedded-checkout" style="max-width: 400px; margin: 0 auto; padding: 20px; font-family: system-ui, -apple-system, sans-serif;">
|
|
2107
2107
|
<div class="recur-checkout-header" style="margin-bottom: 24px;">
|
|
2108
2108
|
<h2 style="margin: 0 0 8px 0; font-size: 20px; font-weight: 600; color: #111;">Subscribe</h2>
|
|
@@ -2172,21 +2172,21 @@
|
|
|
2172
2172
|
</p>
|
|
2173
2173
|
</form>
|
|
2174
2174
|
</div>
|
|
2175
|
-
`}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");
|
|
2175
|
+
`}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");if(!window.UniPayment)throw new Error("PAYUNi SDK not loaded");let e={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=window.UniPayment.createSession(this.sdkToken,e),await this.payuniSDK.start(),this.payuniSDK.onUpdate?.(t=>{let r=t?.status;r&&(this.isFormValid=r.CardNo===!0&&r.CardExp===!0&&r.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 o=r.EncryptInfo||r.creditToken;if(!o)throw console.error("[Recur SDK] Missing payment token. Available fields:",Object.keys(r)),new Error("Missing payment token");let i=this.getBaseUrl(),s=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:o,timestamp:this.sdkTimestamp||r.HashTimestamp||r.timestamp})});if(!s.ok){let h=await s.json().catch(()=>({}));throw new Error(h.error||"Failed to process payment")}let a=await s.json(),c=this.options.productId||this.options.planId||"",d={subscription:{id:a.subscription?.id||a.charge?.id||"",status:a.success?"active":"failed",productId:c,productName:"",amount:a.charge?.amount||0,billingPeriod:a.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 instanceof Error?e.message:"An error occurred";this.showError(t);let r={code:"CHECKOUT_ERROR",message:t};this.options.onError&&this.options.onError(r)}};var z=class{constructor(e){l(this,"publishableKey");l(this,"baseUrl");l(this,"embedUrl");l(this,"iframe",null);l(this,"container",null);l(this,"sessionId",null);l(this,"timestamp",null);l(this,"creditToken",null);l(this,"sdkToken",null);l(this,"cardToken",null);l(this,"cardTimestamp",null);l(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=`
|
|
2176
2176
|
width: 100%;
|
|
2177
2177
|
border: none;
|
|
2178
2178
|
min-height: 200px;
|
|
2179
2179
|
display: block;
|
|
2180
2180
|
user-select: none;
|
|
2181
2181
|
transition: height 0.35s ease, opacity 0.4s ease 0.1s;
|
|
2182
|
-
`.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,o)=>{let i=setTimeout(()=>{o(new Error("Elements initialization timeout"))},3e4),s=()=>{clearTimeout(i),this.off("ready",s),r()},a=c=>{clearTimeout(i),this.off("error",a),o(new Error(c.message||"Elements initialization failed"))};this.on("ready",s),this.on("error",a)})}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(o=>o(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 o=setTimeout(()=>{r(new Error("Tokenization timeout"))},6e4),i=a=>{clearTimeout(o),this.off("tokenized",i),t(a)},s=a=>{clearTimeout(o),this.off("error",s),r(new Error(a.message||"Tokenization failed"))};this.on("tokenized",i),this.on("error",s)})}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 Te(n){return new z(n)}var Bt="https://vendor.payuni.com.tw/sdk/uni-payment.js",zt="https://sandbox-vendor.payuni.com.tw/sdk/uni-payment.js",pt=!1,ge=!1,Z=null;async function ht(n=!1){return pt&&window.UniPayment?Promise.resolve():(ge&&Z||(ge=!0,Z=new Promise((e,t)=>{let r=document.createElement("script");r.src=n?zt:Bt,r.async=!0,r.onload=()=>{pt=!0,ge=!1,e()},r.onerror=()=>{ge=!1,Z=null,t(new Error("Failed to load PAYUNi SDK"))},document.head.appendChild(r)})),Z)}var Nt=pe.version,Ot="vanilla";var N=class n{constructor(e){l(this,"core");l(this,"currentDialog",null);l(this,"currentIframe",null);l(this,"_customer",null);l(this,"_subscription",null);l(this,"_entitlements",[]);l(this,"_customerIdentifier",null);l(this,"_isLoading",!1);l(this,"_error",null);l(this,"currentDialogElement",null);this.core=new he(e)}static async create(e){let t=new n(e);return e.customer&&(t._customerIdentifier=e.customer,await t.fetchEntitlements()),t}get customer(){return this._customer}get subscription(){return this._subscription}get entitlements(){return this._entitlements}get isLoading(){return this._isLoading}get error(){return this._error}async fetchEntitlements(){if(!this._customerIdentifier){console.warn("[Recur SDK] Cannot fetch entitlements: customer identifier not set");return}let e=this.core.getConfig();if(!e.publishableKey){console.warn("[Recur SDK] Cannot fetch entitlements: publishableKey is required");return}this._isLoading=!0,this._error=null;try{let t=e.baseUrl||"https://api.recur.tw",r=new URLSearchParams;this._customerIdentifier.email&&r.set("email",this._customerIdentifier.email),this._customerIdentifier.externalId&&r.set("external_id",this._customerIdentifier.externalId),this._customerIdentifier.id&&r.set("customer_id",this._customerIdentifier.id);let o=await fetch(`${t}/v1/customers/entitlements?${r.toString()}`,{method:"GET",headers:{"X-Recur-Publishable-Key":e.publishableKey,"X-Recur-SDK-Type":Ot,"X-Recur-SDK-Version":Nt}});if(!o.ok){let a=await o.json().catch(()=>({}));throw new Error(a.error?.message||"Failed to fetch entitlements")}let i=await o.json(),s=k(i);this._customer=s.customer||null,this._subscription=s.subscription||null,this._entitlements=s.entitlements||[]}catch(t){console.error("[Recur SDK] Failed to fetch entitlements:",t),this._error=t instanceof Error?t:new Error("Failed to fetch entitlements")}finally{this._isLoading=!1}}async refetch(){await this.fetchEntitlements()}resolveProduct(e){if(typeof e=="string")return e;if("product"in e)return e.product;throw"feature"in e?new Error("Feature-based checks are not yet supported. Coming in Phase 2."):"benefit"in e?new Error("Benefit-based checks are not yet supported. Coming in Phase 2."):new Error("Invalid check target")}check(e,t){return t?.live?this.checkLive(e):this.checkSync(e)}checkSync(e){let t=this.resolveProduct(e);if(!this._customer)return{allowed:!1,reason:"no_customer"};if(this._entitlements.length===0)return{allowed:!1,reason:"no_entitlement"};let r=this._entitlements.find(o=>o.product===t||o.productId===t);return r?{allowed:!0,entitlement:r}:{allowed:!1,reason:"not_found"}}async checkLive(e){return await this.refetch(),this.checkSync(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 me(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 o=this.core.getConfig(),i=await fetch(`${t}/v1/checkout/sessions`,{method:"POST",headers:{"Content-Type":"application/json","X-Recur-Publishable-Key":o.publishableKey},body:JSON.stringify(r)});if(!i.ok){let c=await i.json().catch(()=>({}));throw new Error(c.error?.message||c.error||"Failed to create checkout session")}let s=await i.json(),a=k(s);return{id:a.id,url:a.url,expiresAt:a.expiresAt,clientSecret:a.clientSecret}}async getCheckoutStatus(e,t){let r=this.getBaseUrl(),o=this.core.getConfig(),i=await fetch(`${r}/v1/checkouts/${e}?client_secret=${encodeURIComponent(t)}`,{method:"GET",headers:{"X-Recur-Publishable-Key":o.publishableKey}});if(!i.ok){let c=await i.json().catch(()=>({}));throw new Error(c.error||"Failed to get checkout status")}let s=await i.json(),a=k(s);return{id:a.checkout.id,status:a.checkout.status,amount:a.checkout.amount,currency:a.checkout.currency,lastCharge:a.lastCharge}}async checkout(e){let t=this.core.getConfig(),r=null,o=e.productId||e.planId,i=e.productSlug;try{if(console.log("[Recur SDK] Starting checkout flow...",{productId:o,productSlug:i,mode:e.mode}),!o&&!i)throw new Error("Either productId or productSlug is required");if(!t.publishableKey)throw new Error("publishableKey is required");let s=this.getBaseUrl();console.log("[Recur SDK] Base URL:",s);let a={"Content-Type":"application/json","X-Recur-Publishable-Key":t.publishableKey},c=e.mode||"modal";if(c==="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 y={successUrl:e.successUrl,cancelUrl:e.cancelUrl};o&&(y.productId=o),i&&(y.productSlug=i),e.customerEmail&&(y.customerEmail=e.customerEmail),e.customerName&&(y.customerName=e.customerName),e.externalCustomerId&&(y.externalCustomerId=e.externalCustomerId);let A=await fetch(`${s}/v1/checkout/sessions`,{method:"POST",headers:a,body:JSON.stringify(y)});if(!A.ok){let R=await A.json().catch(()=>({}));throw console.error("[Recur SDK] Failed to create checkout session:",R),new Error(R.error?.message||R.error||"Failed to create checkout session")}let fe=await A.json(),C=k(fe);console.log("[Recur SDK] Checkout session created:",C),console.log("[Recur SDK] Redirecting to hosted checkout:",C.url),window.location.href=C.url;return}let d=null;if(c==="modal"){let y=this.createDialogWithSkeleton(e.onClose);r=y.dialog,d=y.container}else if(c==="iframe"){if(d=this.getEmbeddedContainer(e.container),!d)throw new Error("Container is required for iframe mode");d.innerHTML="";let y=document.createElement("recur-payment-form-skeleton");d.appendChild(y)}console.log("[Recur SDK] Step 1: Creating embedded checkout...");let h={customerName:e.customerName,customerEmail:e.customerEmail};o&&(h.productId=o),i&&(h.productSlug=i),e.externalCustomerId&&(h.externalCustomerId=e.externalCustomerId),e.successUrl&&(h.successUrl=e.successUrl),e.cancelUrl&&(h.cancelUrl=e.cancelUrl);let m=await fetch(`${s}/v1/checkouts`,{method:"POST",headers:a,body:JSON.stringify(h)});if(!m.ok){let y=await m.json().catch(()=>({}));console.error("[Recur SDK] Failed to create checkout:",y);let A=y.details||y.error||"Failed to create checkout";throw new Error(A)}let x=await m.json(),u=k(x);if(console.log("[Recur SDK] Checkout created successfully:",u),e.onSuccess?.(u),console.log("[Recur SDK] Step 2: Extracting SDK token..."),!u.sdkToken)throw new Error("SDK token missing from checkout response");console.log("[Recur SDK] Step 3: Loading PAYUNi SDK...");let T=!u.livemode;if(console.log("[Recur SDK] Environment:",T?"SANDBOX":"PRODUCTION"),await ht(T),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..."),!d)throw new Error("Payment container not available");d.innerHTML="";let b=document.createElement("recur-payment-form");if(b.setAttribute("container-id",d.id||"recur-payment-container"),u.checkout?.id&&b.setAttribute("checkout-id",u.checkout.id),b.setAttribute("api-base-url",s),e.customerName&&b.setAttribute("customer-name",e.customerName),e.customerEmail&&b.setAttribute("customer-email",e.customerEmail),u.product?.name&&b.setAttribute("product-name",u.product.name),u.checkout?.amount&&b.setAttribute("amount",u.checkout.amount.toString()),u.product?.interval&&b.setAttribute("interval",u.product.interval),b.setAttribute("custom-styles",`
|
|
2182
|
+
`.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,o)=>{let i=setTimeout(()=>{o(new Error("Elements initialization timeout"))},3e4),s=()=>{clearTimeout(i),this.off("ready",s),r()},a=c=>{clearTimeout(i),this.off("error",a);let d=c instanceof Error?c.message:c?.message;o(new Error(d||"Elements initialization failed"))};this.on("ready",s),this.on("error",a)})}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(o=>o(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 o=setTimeout(()=>{r(new Error("Tokenization timeout"))},6e4),i=a=>{clearTimeout(o),this.off("tokenized",i),t(a)},s=a=>{clearTimeout(o),this.off("error",s);let c=a instanceof Error?a.message:a?.message;r(new Error(c||"Tokenization failed"))};this.on("tokenized",i),this.on("error",s)})}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 Te(n){return new z(n)}var Bt="https://vendor.payuni.com.tw/sdk/uni-payment.js",Nt="https://sandbox-vendor.payuni.com.tw/sdk/uni-payment.js",pt=!1,ge=!1,Z=null;async function mt(n=!1){return pt&&window.UniPayment?Promise.resolve():(ge&&Z||(ge=!0,Z=new Promise((e,t)=>{let r=document.createElement("script");r.src=n?Nt:Bt,r.async=!0,r.onload=()=>{pt=!0,ge=!1,e()},r.onerror=()=>{ge=!1,Z=null,t(new Error("Failed to load PAYUNi SDK"))},document.head.appendChild(r)})),Z)}var zt=pe.version,Ot="vanilla",O=class n{constructor(e){l(this,"core");l(this,"currentDialog",null);l(this,"currentIframe",null);l(this,"_customer",null);l(this,"_subscription",null);l(this,"_entitlements",[]);l(this,"_customerIdentifier",null);l(this,"_isLoading",!1);l(this,"_error",null);l(this,"currentDialogElement",null);this.core=new me(e)}static async create(e){let t=new n(e);return e.customer&&(t._customerIdentifier=e.customer,await t.fetchEntitlements()),t}get customer(){return this._customer}get subscription(){return this._subscription}get entitlements(){return this._entitlements}get isLoading(){return this._isLoading}get error(){return this._error}async fetchEntitlements(){if(!this._customerIdentifier){console.warn("[Recur SDK] Cannot fetch entitlements: customer identifier not set");return}let e=this.core.getConfig();if(!e.publishableKey){console.warn("[Recur SDK] Cannot fetch entitlements: publishableKey is required");return}this._isLoading=!0,this._error=null;try{let t=e.baseUrl||"https://api.recur.tw",r=new URLSearchParams;this._customerIdentifier.email&&r.set("email",this._customerIdentifier.email),this._customerIdentifier.externalId&&r.set("external_id",this._customerIdentifier.externalId),this._customerIdentifier.id&&r.set("customer_id",this._customerIdentifier.id);let o=await fetch(`${t}/v1/customers/entitlements?${r.toString()}`,{method:"GET",headers:{"X-Recur-Publishable-Key":e.publishableKey,"X-Recur-SDK-Type":Ot,"X-Recur-SDK-Version":zt}});if(!o.ok){let a=await o.json().catch(()=>({}));throw new Error(a.error?.message||"Failed to fetch entitlements")}let i=await o.json(),s=k(i);this._customer=s.customer||null,this._subscription=s.subscription||null,this._entitlements=s.entitlements||[]}catch(t){console.error("[Recur SDK] Failed to fetch entitlements:",t),this._error=t instanceof Error?t:new Error("Failed to fetch entitlements")}finally{this._isLoading=!1}}async refetch(){await this.fetchEntitlements()}resolveProduct(e){if(typeof e=="string")return e;if("product"in e)return e.product;throw"feature"in e?new Error("Feature-based checks are not yet supported. Coming in Phase 2."):"benefit"in e?new Error("Benefit-based checks are not yet supported. Coming in Phase 2."):new Error("Invalid check target")}check(e,t){return t?.live?this.checkLive(e):this.checkSync(e)}checkSync(e){let t=this.resolveProduct(e);if(!this._customer)return{allowed:!1,reason:"no_customer"};if(this._entitlements.length===0)return{allowed:!1,reason:"no_entitlement"};let r=this._entitlements.find(o=>o.product===t||o.productId===t);return r?{allowed:!0,entitlement:r}:{allowed:!1,reason:"not_found"}}async checkLive(e){return await this.refetch(),this.checkSync(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 he(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 o=this.core.getConfig(),i=await fetch(`${t}/v1/checkout/sessions`,{method:"POST",headers:{"Content-Type":"application/json","X-Recur-Publishable-Key":o.publishableKey},body:JSON.stringify(r)});if(!i.ok){let c=await i.json().catch(()=>({}));throw new Error(c.error?.message||c.error||"Failed to create checkout session")}let s=await i.json(),a=k(s);return{id:a.id,url:a.url,expiresAt:a.expiresAt,clientSecret:a.clientSecret}}async getCheckoutStatus(e,t){let r=this.getBaseUrl(),o=this.core.getConfig(),i=await fetch(`${r}/v1/checkouts/${e}?client_secret=${encodeURIComponent(t)}`,{method:"GET",headers:{"X-Recur-Publishable-Key":o.publishableKey}});if(!i.ok){let c=await i.json().catch(()=>({}));throw new Error(c.error||"Failed to get checkout status")}let s=await i.json(),a=k(s);return{id:a.checkout.id,status:a.checkout.status,amount:a.checkout.amount,currency:a.checkout.currency,lastCharge:a.lastCharge}}async checkout(e){let t=this.core.getConfig(),r=null,o=e.productId||e.planId,i=e.productSlug;try{if(console.log("[Recur SDK] Starting checkout flow...",{productId:o,productSlug:i,mode:e.mode}),!o&&!i)throw new Error("Either productId or productSlug is required");if(!t.publishableKey)throw new Error("publishableKey is required");let s=this.getBaseUrl();console.log("[Recur SDK] Base URL:",s);let a={"Content-Type":"application/json","X-Recur-Publishable-Key":t.publishableKey},c=e.mode||"modal";if(c==="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 y={successUrl:e.successUrl,cancelUrl:e.cancelUrl};o&&(y.productId=o),i&&(y.productSlug=i),e.customerEmail&&(y.customerEmail=e.customerEmail),e.customerName&&(y.customerName=e.customerName),e.externalCustomerId&&(y.externalCustomerId=e.externalCustomerId);let R=await fetch(`${s}/v1/checkout/sessions`,{method:"POST",headers:a,body:JSON.stringify(y)});if(!R.ok){let P=await R.json().catch(()=>({}));throw console.error("[Recur SDK] Failed to create checkout session:",P),new Error(P.error?.message||P.error||"Failed to create checkout session")}let fe=await R.json(),E=k(fe);console.log("[Recur SDK] Checkout session created:",E),console.log("[Recur SDK] Redirecting to hosted checkout:",E.url),window.location.href=E.url;return}let d=null;if(c==="modal"){let y=this.createDialogWithSkeleton(e.onClose);r=y.dialog,d=y.container}else if(c==="iframe"){if(d=this.getEmbeddedContainer(e.container),!d)throw new Error("Container is required for iframe mode");d.innerHTML="";let y=document.createElement("recur-payment-form-skeleton");d.appendChild(y)}console.log("[Recur SDK] Step 1: Creating embedded checkout...");let h={customerName:e.customerName,customerEmail:e.customerEmail};o&&(h.productId=o),i&&(h.productSlug=i),e.externalCustomerId&&(h.externalCustomerId=e.externalCustomerId),e.successUrl&&(h.successUrl=e.successUrl),e.cancelUrl&&(h.cancelUrl=e.cancelUrl);let m=await fetch(`${s}/v1/checkouts`,{method:"POST",headers:a,body:JSON.stringify(h)});if(!m.ok){let y=await m.json().catch(()=>({}));console.error("[Recur SDK] Failed to create checkout:",y);let R=y.details||y.error||"Failed to create checkout";throw new Error(R)}let x=await m.json(),u=k(x);if(console.log("[Recur SDK] Checkout created successfully:",u),e.onSuccess?.(u),console.log("[Recur SDK] Step 2: Extracting SDK token..."),!u.sdkToken)throw new Error("SDK token missing from checkout response");console.log("[Recur SDK] Step 3: Loading PAYUNi SDK...");let T=!u.livemode;if(console.log("[Recur SDK] Environment:",T?"SANDBOX":"PRODUCTION"),await mt(T),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..."),!d)throw new Error("Payment container not available");d.innerHTML="";let b=document.createElement("recur-payment-form");if(b.setAttribute("container-id",d.id||"recur-payment-container"),u.checkout?.id&&b.setAttribute("checkout-id",u.checkout.id),b.setAttribute("api-base-url",s),e.customerName&&b.setAttribute("customer-name",e.customerName),e.customerEmail&&b.setAttribute("customer-email",e.customerEmail),u.product?.name&&b.setAttribute("product-name",u.product.name),u.checkout?.amount&&b.setAttribute("amount",u.checkout.amount.toString()),u.product?.interval&&b.setAttribute("interval",u.product.interval),b.setAttribute("custom-styles",`
|
|
2183
2183
|
.form-input-focus {
|
|
2184
2184
|
border-color: var(--ring, hsl(215 16% 47%)) !important;
|
|
2185
2185
|
outline: 0 !important;
|
|
2186
2186
|
box-shadow: 0 0 0 3px color-mix(in oklch, var(--ring, hsl(215 16% 47%)) 50%, transparent) !important;
|
|
2187
2187
|
transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out !important;
|
|
2188
2188
|
}
|
|
2189
|
-
`),d.appendChild(b),await new Promise(y=>setTimeout(y,100)),console.log("[Recur SDK] Step 5: Initializing PAYUNi SDK..."),await b.initializePayment(u.sdkToken,T?"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..."),b.addEventListener("submit",(async y=>{console.log("[Recur SDK] Form submitted");let
|
|
2189
|
+
`),d.appendChild(b),await new Promise(y=>setTimeout(y,100)),console.log("[Recur SDK] Step 5: Initializing PAYUNi SDK..."),await b.initializePayment(u.sdkToken,T?"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..."),b.addEventListener("submit",(async y=>{console.log("[Recur SDK] Form submitted");let R=y,{paymentSession:fe}=R.detail;try{let E=await fe.getTradeResult();console.log("[Recur SDK] Trade result received"),console.log("[Recur SDK] Executing payment...");let P=E.HashTimestamp||E.timestamp,$e={};if(u.checkout.productType==="SUBSCRIPTION"){let $=u.creditToken,_=u.sdkTimestamp;if(!$)throw console.error("[Recur SDK] Missing creditToken from checkout for subscription"),new Error("Missing creditToken for subscription payment");$e={creditToken:$,timestamp:_||P},console.log("[Recur SDK] Using creditToken from checkout:",$.substring(0,30)+"..."),console.log("[Recur SDK] Using timestamp:",_?"from checkout (sdkTimestamp)":"from tradeResult")}let be=await fetch(`${s}/v1/checkouts/${u.checkout.id}/pay`,{method:"POST",headers:a,body:JSON.stringify($e)});if(!be.ok){let $=await be.json().catch(()=>({}));throw new Error($.error||"Failed to execute payment")}let ft=await be.json(),g=k(ft);if(console.log("[Recur SDK] Payment executed:",g),g.requires3D&&g.redirectUrl){if(console.log("[Recur SDK] 3D verification required"),/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)||/wv|WebView|FBAN|FBAV|Instagram|Line|MicroMessenger|QQ/i.test(navigator.userAgent)||/(iPhone|iPod|iPad).*AppleWebKit(?!.*Safari)/i.test(navigator.userAgent)||"standalone"in navigator&&navigator.standalone){console.log("[Recur SDK] Mobile/WebView detected, using redirect for 3D verification"),window.location.href=g.redirectUrl;return}console.log("[Recur SDK] Using popup for 3D verification");{let _=window.open(g.redirectUrl,"recur_3d_verification","width=500,height=700,scrollbars=yes,resizable=yes");if(!_){console.log("[Recur SDK] Popup was blocked (null), falling back to redirect"),window.location.href=g.redirectUrl;return}if(await new Promise(j=>setTimeout(j,100)),_.closed){console.log("[Recur SDK] Popup was closed immediately, falling back to redirect"),window.location.href=g.redirectUrl;return}b.setVerifying?.(!0);let Q=u.checkout.id,Ie=u.checkout.clientSecret,bt=90,yt=2e3,K=!0,Ae=!1,Re;console.log("[Recur SDK] Starting 3D verification polling...");for(let j=0;j<bt&&K;j++){if(_.closed){console.log("[Recur SDK] Popup was closed");try{let v=await fetch(`${s}/v1/checkouts/${Q}/status?client_secret=${encodeURIComponent(Ie)}`,{headers:a});if(v.ok){let I=await v.json();if(I.checkout?.status==="SUCCEEDED"){console.log("[Recur SDK] Payment succeeded after popup close"),K=!1,Ae=!0,Re=I.checkout?.orderId;break}}}catch(v){console.error("[Recur SDK] Final status check failed:",v)}throw console.log("[Recur SDK] Payment not confirmed, user closed popup"),b.setVerifying?.(!1),b.resetButton?.(),new Error("3D \u9A57\u8B49\u5DF2\u53D6\u6D88")}try{let v=await fetch(`${s}/v1/checkouts/${Q}/status?client_secret=${encodeURIComponent(Ie)}`,{headers:a});if(v.ok){let I=await v.json(),ee=I.checkout?.status;if(console.log(`[Recur SDK] Poll ${j+1}: status = ${ee}`),ee==="SUCCEEDED"){console.log("[Recur SDK] Payment succeeded"),K=!1;try{_.close()}catch{}b.setVerifying?.(!1),e.onPaymentComplete&&(g.subscription?e.onPaymentComplete({id:g.subscription.id,status:"ACTIVE",productId:u.checkout.productId,amount:u.checkout.amount,billingPeriod:g.subscription.billingPeriod,currentPeriodStart:g.subscription.currentPeriodStart,currentPeriodEnd:g.subscription.currentPeriodEnd}):e.onPaymentComplete({id:I.checkout?.orderId||Q,status:"SUCCEEDED",productId:u.checkout.productId,amount:u.checkout.amount,billingPeriod:u.checkout.productType})),r&&(r.close(),r.remove());return}if(ee==="CANCELED"||ee==="FAILED"){console.log("[Recur SDK] Payment failed or canceled"),K=!1;try{_.close()}catch{}throw b.setVerifying?.(!1),b.resetButton?.(),new Error("\u4ED8\u6B3E\u5931\u6557\u6216\u5DF2\u53D6\u6D88")}}}catch(v){let I=v instanceof Error?v.message:String(v);if(I==="3D \u9A57\u8B49\u5DF2\u53D6\u6D88"||I==="\u4ED8\u6B3E\u5931\u6557\u6216\u5DF2\u53D6\u6D88")throw v;console.error("[Recur SDK] Poll error:",v)}await new Promise(v=>setTimeout(v,yt))}if(K){console.log("[Recur SDK] 3D verification polling timeout");try{_.close()}catch{}throw b.setVerifying?.(!1),b.resetButton?.(),new Error("3D \u9A57\u8B49\u903E\u6642\uFF0C\u8ACB\u91CD\u8A66")}Ae&&(console.log("[Recur SDK] Handling success after popup close"),b.setVerifying?.(!1),e.onPaymentComplete&&(g.subscription?e.onPaymentComplete({id:g.subscription.id,status:"ACTIVE",productId:u.checkout.productId,amount:u.checkout.amount,billingPeriod:g.subscription.billingPeriod,currentPeriodStart:g.subscription.currentPeriodStart,currentPeriodEnd:g.subscription.currentPeriodEnd}):e.onPaymentComplete({id:Re||Q,status:"SUCCEEDED",productId:u.checkout.productId,amount:u.checkout.amount,billingPeriod:u.checkout.productType})),r&&(r.close(),r.remove()));return}}if(e.onPaymentComplete)if(g.subscription)e.onPaymentComplete({id:g.subscription.id,status:g.subscription.status,productId:u.checkout.productId,amount:u.checkout.amount,billingPeriod:g.subscription.billingPeriod,currentPeriodStart:g.subscription.currentPeriodStart,currentPeriodEnd:g.subscription.currentPeriodEnd});else{let $=g.charge?.id||g.paymentIntent?.id,_=g.charge?.status||"SUCCEEDED";e.onPaymentComplete({id:$,status:_,productId:u.checkout.productId,amount:u.checkout.amount,billingPeriod:u.checkout.productType})}console.log("[Recur SDK] Checkout flow completed successfully!"),b.resetButton?.(),r&&(r.close(),r.remove())}catch(E){console.error("[Recur SDK] Payment error:",E);let P={code:"PAYMENT_FAILED",message:E instanceof Error?E.message:"Payment failed"};e.onError?.(P),b.resetButton?.()}})),console.log("[Recur SDK] Checkout flow initialized, waiting for user input...")}catch(s){console.error("[Recur SDK] Checkout error:",s),r&&(r.close(),r.remove());let a={code:"CHECKOUT_ERROR",message:s instanceof Error?s.message:"An unknown error occurred"};throw e.onError?.(a),s}}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"}injectDialogStyles(){if(document.getElementById("recur-dialog-styles"))return;let e=document.createElement("style");e.id="recur-dialog-styles",e.textContent=`
|
|
2190
2190
|
.recur-dialog {
|
|
2191
2191
|
border: none;
|
|
2192
2192
|
border-radius: 12px;
|
|
@@ -2242,7 +2242,7 @@
|
|
|
2242
2242
|
.recur-dialog__close:hover {
|
|
2243
2243
|
background: rgba(0, 0, 0, 0.1);
|
|
2244
2244
|
}
|
|
2245
|
-
`,document.head.appendChild(e)}createDialogWithSkeleton(e){this.closeDialog(),this.injectDialogStyles();let t=document.createElement("dialog");t.id="recur-dialog",t.className="recur-dialog";let r=document.createElement("div");r.className="recur-dialog__content";let o=document.createElement("button");o.className="recur-dialog__close",o.innerHTML="\u2715",o.type="button",o.onclick=()=>{this.closeDialog(),e?.()};let i=document.createElement("div");i.id="recur-dialog-payment-container";let s=document.createElement("recur-payment-form-skeleton");return i.appendChild(s),r.appendChild(o),r.appendChild(i),t.appendChild(r),document.body.appendChild(t),t.showModal(),t.addEventListener("cancel",a=>{a.preventDefault(),this.closeDialog(),e?.()}),this.currentDialogElement=t,{dialog:t,container:i}}getEmbeddedContainer(e){return e?typeof e=="string"?document.getElementById(e)||document.querySelector(e):e:null}closeDialog(){this.currentDialog&&(this.currentDialog.close(),this.currentDialog=null),this.currentDialogElement&&(this.currentDialogElement.close(),this.currentDialogElement.remove(),this.currentDialogElement=null)}closeModal(){this.closeDialog()}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 o=await r.json();return{id:o.id,url:o.url||o.portalUrl,expiresAt:o.expiresAt}}async redirectToPortal(e,t){let r=await this.createPortalSession(e,t);window.location.href=r.url}};function
|
|
2245
|
+
`,document.head.appendChild(e)}createDialogWithSkeleton(e){this.closeDialog(),this.injectDialogStyles();let t=document.createElement("dialog");t.id="recur-dialog",t.className="recur-dialog";let r=document.createElement("div");r.className="recur-dialog__content";let o=document.createElement("button");o.className="recur-dialog__close",o.innerHTML="\u2715",o.type="button",o.onclick=()=>{this.closeDialog(),e?.()};let i=document.createElement("div");i.id="recur-dialog-payment-container";let s=document.createElement("recur-payment-form-skeleton");return i.appendChild(s),r.appendChild(o),r.appendChild(i),t.appendChild(r),document.body.appendChild(t),t.showModal(),t.addEventListener("cancel",a=>{a.preventDefault(),this.closeDialog(),e?.()}),this.currentDialogElement=t,{dialog:t,container:i}}getEmbeddedContainer(e){return e?typeof e=="string"?document.getElementById(e)||document.querySelector(e):e:null}closeDialog(){this.currentDialog&&(this.currentDialog.close(),this.currentDialog=null),this.currentDialogElement&&(this.currentDialogElement.close(),this.currentDialogElement.remove(),this.currentDialogElement=null)}closeModal(){this.closeDialog()}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 o=await r.json();return{id:o.id,url:o.url||o.portalUrl,expiresAt:o.expiresAt}}async redirectToPortal(e,t){let r=await this.createPortalSession(e,t);window.location.href=r.url}};function ht(n){return new O(n)}function gt(n){return O.create(n)}var Kt={init:ht,create:gt,RecurCheckout:O,RecurElements:z,createElements:Te};return Et(jt);})();
|
|
2246
2246
|
/*! Bundled license information:
|
|
2247
2247
|
|
|
2248
2248
|
lit-html/node/lit-html.js:
|
package/dist/server.cjs
CHANGED
|
@@ -11,16 +11,20 @@ var RecurAPIError = class extends Error {
|
|
|
11
11
|
__publicField(this, "type");
|
|
12
12
|
__publicField(this, "code");
|
|
13
13
|
__publicField(this, "statusCode");
|
|
14
|
+
__publicField(this, "docUrl");
|
|
15
|
+
__publicField(this, "details");
|
|
14
16
|
this.name = "RecurAPIError";
|
|
15
17
|
this.type = error.type;
|
|
16
18
|
this.code = error.code;
|
|
17
19
|
this.statusCode = statusCode;
|
|
20
|
+
this.docUrl = error.doc_url;
|
|
21
|
+
this.details = error.details;
|
|
18
22
|
}
|
|
19
23
|
};
|
|
20
24
|
|
|
21
25
|
// package.json
|
|
22
26
|
var package_default = {
|
|
23
|
-
version: "0.
|
|
27
|
+
version: "0.13.0"};
|
|
24
28
|
|
|
25
29
|
// src/server/resources/portal.ts
|
|
26
30
|
var SDK_VERSION = package_default.version;
|
package/dist/server.d.cts
CHANGED
|
@@ -92,16 +92,49 @@ interface PortalSession {
|
|
|
92
92
|
*/
|
|
93
93
|
createdAt: string;
|
|
94
94
|
}
|
|
95
|
+
/**
|
|
96
|
+
* Standard API error codes from Recur API
|
|
97
|
+
*
|
|
98
|
+
* These codes match the backend error codes for consistent error handling.
|
|
99
|
+
*/
|
|
100
|
+
type ApiErrorCode = 'bad_request' | 'unauthorized' | 'payment_required' | 'forbidden' | 'not_found' | 'conflict' | 'validation_error' | 'rate_limit_exceeded' | 'internal_server_error';
|
|
101
|
+
/**
|
|
102
|
+
* Details for duplicate subscription errors (conflict)
|
|
103
|
+
*/
|
|
104
|
+
interface DuplicateSubscriptionDetails {
|
|
105
|
+
/**
|
|
106
|
+
* ID of the existing subscription
|
|
107
|
+
*/
|
|
108
|
+
existing_subscription_id: string;
|
|
109
|
+
/**
|
|
110
|
+
* Status of the existing subscription
|
|
111
|
+
*/
|
|
112
|
+
status: string;
|
|
113
|
+
}
|
|
95
114
|
interface RecurError {
|
|
96
115
|
type: 'authentication_error' | 'invalid_request_error' | 'api_error';
|
|
97
|
-
|
|
116
|
+
/**
|
|
117
|
+
* Error code - see ApiErrorCode for standard codes
|
|
118
|
+
*/
|
|
119
|
+
code: ApiErrorCode | string;
|
|
98
120
|
message: string;
|
|
99
|
-
|
|
121
|
+
/**
|
|
122
|
+
* URL to relevant documentation
|
|
123
|
+
*/
|
|
124
|
+
doc_url?: string;
|
|
125
|
+
/**
|
|
126
|
+
* Additional error details
|
|
127
|
+
*
|
|
128
|
+
* For 'conflict' errors: contains DuplicateSubscriptionDetails
|
|
129
|
+
*/
|
|
130
|
+
details?: (DuplicateSubscriptionDetails | Record<string, unknown>)[];
|
|
100
131
|
}
|
|
101
132
|
declare class RecurAPIError extends Error {
|
|
102
133
|
readonly type: string;
|
|
103
134
|
readonly code: string;
|
|
104
135
|
readonly statusCode: number;
|
|
136
|
+
readonly docUrl?: string;
|
|
137
|
+
readonly details?: unknown[];
|
|
105
138
|
constructor(error: RecurError, statusCode: number);
|
|
106
139
|
}
|
|
107
140
|
/**
|
|
@@ -435,4 +468,4 @@ declare class Recur {
|
|
|
435
468
|
constructor(secretKey: string, options?: Omit<RecurConfig, 'secretKey'>);
|
|
436
469
|
}
|
|
437
470
|
|
|
438
|
-
export { type CustomerIdentifier, type Entitlement, type EntitlementCheckOptions, type EntitlementCheckResult, type EntitlementListOptions, type EntitlementListResult, type EntitlementStatus, type EntitlementSubscription, type PortalSession, type PortalSessionCreateParams, Recur, RecurAPIError, type RecurConfig, type RecurError };
|
|
471
|
+
export { type ApiErrorCode, type CustomerIdentifier, type DuplicateSubscriptionDetails, type Entitlement, type EntitlementCheckOptions, type EntitlementCheckResult, type EntitlementListOptions, type EntitlementListResult, type EntitlementStatus, type EntitlementSubscription, type PortalSession, type PortalSessionCreateParams, Recur, RecurAPIError, type RecurConfig, type RecurError };
|
package/dist/server.d.ts
CHANGED
|
@@ -92,16 +92,49 @@ interface PortalSession {
|
|
|
92
92
|
*/
|
|
93
93
|
createdAt: string;
|
|
94
94
|
}
|
|
95
|
+
/**
|
|
96
|
+
* Standard API error codes from Recur API
|
|
97
|
+
*
|
|
98
|
+
* These codes match the backend error codes for consistent error handling.
|
|
99
|
+
*/
|
|
100
|
+
type ApiErrorCode = 'bad_request' | 'unauthorized' | 'payment_required' | 'forbidden' | 'not_found' | 'conflict' | 'validation_error' | 'rate_limit_exceeded' | 'internal_server_error';
|
|
101
|
+
/**
|
|
102
|
+
* Details for duplicate subscription errors (conflict)
|
|
103
|
+
*/
|
|
104
|
+
interface DuplicateSubscriptionDetails {
|
|
105
|
+
/**
|
|
106
|
+
* ID of the existing subscription
|
|
107
|
+
*/
|
|
108
|
+
existing_subscription_id: string;
|
|
109
|
+
/**
|
|
110
|
+
* Status of the existing subscription
|
|
111
|
+
*/
|
|
112
|
+
status: string;
|
|
113
|
+
}
|
|
95
114
|
interface RecurError {
|
|
96
115
|
type: 'authentication_error' | 'invalid_request_error' | 'api_error';
|
|
97
|
-
|
|
116
|
+
/**
|
|
117
|
+
* Error code - see ApiErrorCode for standard codes
|
|
118
|
+
*/
|
|
119
|
+
code: ApiErrorCode | string;
|
|
98
120
|
message: string;
|
|
99
|
-
|
|
121
|
+
/**
|
|
122
|
+
* URL to relevant documentation
|
|
123
|
+
*/
|
|
124
|
+
doc_url?: string;
|
|
125
|
+
/**
|
|
126
|
+
* Additional error details
|
|
127
|
+
*
|
|
128
|
+
* For 'conflict' errors: contains DuplicateSubscriptionDetails
|
|
129
|
+
*/
|
|
130
|
+
details?: (DuplicateSubscriptionDetails | Record<string, unknown>)[];
|
|
100
131
|
}
|
|
101
132
|
declare class RecurAPIError extends Error {
|
|
102
133
|
readonly type: string;
|
|
103
134
|
readonly code: string;
|
|
104
135
|
readonly statusCode: number;
|
|
136
|
+
readonly docUrl?: string;
|
|
137
|
+
readonly details?: unknown[];
|
|
105
138
|
constructor(error: RecurError, statusCode: number);
|
|
106
139
|
}
|
|
107
140
|
/**
|
|
@@ -435,4 +468,4 @@ declare class Recur {
|
|
|
435
468
|
constructor(secretKey: string, options?: Omit<RecurConfig, 'secretKey'>);
|
|
436
469
|
}
|
|
437
470
|
|
|
438
|
-
export { type CustomerIdentifier, type Entitlement, type EntitlementCheckOptions, type EntitlementCheckResult, type EntitlementListOptions, type EntitlementListResult, type EntitlementStatus, type EntitlementSubscription, type PortalSession, type PortalSessionCreateParams, Recur, RecurAPIError, type RecurConfig, type RecurError };
|
|
471
|
+
export { type ApiErrorCode, type CustomerIdentifier, type DuplicateSubscriptionDetails, type Entitlement, type EntitlementCheckOptions, type EntitlementCheckResult, type EntitlementListOptions, type EntitlementListResult, type EntitlementStatus, type EntitlementSubscription, type PortalSession, type PortalSessionCreateParams, Recur, RecurAPIError, type RecurConfig, type RecurError };
|
package/dist/server.js
CHANGED
|
@@ -9,16 +9,20 @@ var RecurAPIError = class extends Error {
|
|
|
9
9
|
__publicField(this, "type");
|
|
10
10
|
__publicField(this, "code");
|
|
11
11
|
__publicField(this, "statusCode");
|
|
12
|
+
__publicField(this, "docUrl");
|
|
13
|
+
__publicField(this, "details");
|
|
12
14
|
this.name = "RecurAPIError";
|
|
13
15
|
this.type = error.type;
|
|
14
16
|
this.code = error.code;
|
|
15
17
|
this.statusCode = statusCode;
|
|
18
|
+
this.docUrl = error.doc_url;
|
|
19
|
+
this.details = error.details;
|
|
16
20
|
}
|
|
17
21
|
};
|
|
18
22
|
|
|
19
23
|
// package.json
|
|
20
24
|
var package_default = {
|
|
21
|
-
version: "0.
|
|
25
|
+
version: "0.13.0"};
|
|
22
26
|
|
|
23
27
|
// src/server/resources/portal.ts
|
|
24
28
|
var SDK_VERSION = package_default.version;
|
package/dist/widget.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"use strict";var RecurWidgetScript=(()=>{var g=Object.defineProperty;var w=Object.getOwnPropertyDescriptor;var x=Object.getOwnPropertyNames;var y=Object.prototype.hasOwnProperty;var v=(e,t)=>{for(var
|
|
1
|
+
"use strict";var RecurWidgetScript=(()=>{var g=Object.defineProperty;var w=Object.getOwnPropertyDescriptor;var x=Object.getOwnPropertyNames;var y=Object.prototype.hasOwnProperty;var v=(e,t)=>{for(var o in t)g(e,o,{get:t[o],enumerable:!0})},k=(e,t,o,i)=>{if(t&&typeof t=="object"||typeof t=="function")for(let n of x(t))!y.call(e,n)&&n!==o&&g(e,n,{get:()=>t[n],enumerable:!(i=w(t,n))||i.enumerable});return e};var E=e=>k(g({},"__esModule",{value:!0}),e);var W={};v(W,{default:()=>S});var d={text:"\u8A02\u95B1\u652F\u6301",color:"#667eea",textColor:"#ffffff",position:"right",xMargin:18,yMargin:18,embedUrl:"https://embed.recur.tw",apiUrl:"https://api.recur.tw"},r=null,a=null,l=null,u=null;function C(){let e=document.querySelectorAll("script[data-product]"),t=e[e.length-1];if(!t)return console.error("[Recur Widget] Missing data-product attribute on script tag"),null;let o=t.getAttribute("data-key"),i=t.getAttribute("data-product");return o?i?{key:o,product:i,text:t.getAttribute("data-text")||d.text,color:t.getAttribute("data-color")||d.color,textColor:t.getAttribute("data-text-color")||d.textColor,position:t.getAttribute("data-position")||d.position,xMargin:parseInt(t.getAttribute("data-x-margin")||String(d.xMargin),10),yMargin:parseInt(t.getAttribute("data-y-margin")||String(d.yMargin),10),embedUrl:t.getAttribute("data-embed-url")||d.embedUrl,apiUrl:t.getAttribute("data-api-url")||d.apiUrl}:(console.error("[Recur Widget] Missing data-product attribute on script tag"),null):(console.error("[Recur Widget] Missing data-key attribute on script tag"),null)}function M(){if(!r)return;let e=window.location.hostname;(e==="localhost"||e==="127.0.0.1"||e.includes(".local"))&&(r.embedUrl=`${window.location.protocol}//${e}:3002`,r.apiUrl=`${window.location.protocol}//${e}:3000/api`)}function R(){if(!r||document.getElementById("recur-widget-styles"))return;let e=document.createElement("style");e.id="recur-widget-styles",e.textContent=`
|
|
2
2
|
.recur-widget-button {
|
|
3
3
|
position: fixed;
|
|
4
4
|
bottom: ${r.yMargin}px;
|
|
@@ -123,10 +123,10 @@
|
|
|
123
123
|
max-height: 95vh;
|
|
124
124
|
}
|
|
125
125
|
}
|
|
126
|
-
`,document.head.appendChild(e)}function
|
|
126
|
+
`,document.head.appendChild(e)}function _(){if(!r||a)return;let e=document.createElement("button");e.className="recur-widget-button",e.setAttribute("aria-label",r.text),e.innerHTML=`
|
|
127
127
|
<svg class="recur-widget-button__icon" viewBox="0 0 24 24" fill="currentColor">
|
|
128
128
|
<path d="M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z"/>
|
|
129
129
|
</svg>
|
|
130
130
|
<span>${r.text}</span>
|
|
131
|
-
`,e.addEventListener("click",f),document.body.appendChild(e),a=e}async function
|
|
131
|
+
`,e.addEventListener("click",f),document.body.appendChild(e),a=e}async function U(){if(!r)throw new Error("Widget not configured");let e=await fetch(`${r.apiUrl}/v1/checkouts`,{method:"POST",headers:{"Content-Type":"application/json","X-Recur-Publishable-Key":r.key,"X-Recur-Embed-Domain":r.embedUrl,"X-Recur-SDK-Type":"widget.js","X-Recur-Source":window.location.origin},body:JSON.stringify({productId:r.product})});if(!e.ok){let o=await e.json().catch(()=>({}));throw new Error(o.error?.message||o.error||"Failed to create checkout")}let t=await e.json();return{id:t.checkout.id,clientSecret:t.checkout.client_secret}}function A(e){if(!r)return;c();let t=document.createElement("dialog");t.className="recur-widget-dialog";let o=document.createElement("div");o.className="recur-widget-dialog__header";let i=document.createElement("button");i.className="recur-widget-dialog__close",i.innerHTML="×",i.type="button",i.setAttribute("aria-label","Close"),i.onclick=()=>{c(),s("recur:close",{})},o.appendChild(i);let n=document.createElement("iframe");n.className="recur-widget-dialog__iframe",n.src=e,n.setAttribute("allow","payment"),n.setAttribute("title","Recur Checkout"),t.appendChild(o),t.appendChild(n),document.body.appendChild(t),l=t,u=n,t.showModal(),document.body.style.overflow="hidden",t.addEventListener("cancel",b=>{b.preventDefault(),c(),s("recur:close",{})}),window.addEventListener("message",m)}function c(){l&&(l.close(),l.remove(),l=null),u=null,document.body.style.overflow="",window.removeEventListener("message",m)}function m(e){if(!e.origin.includes("recur.tw")&&!e.origin.includes("localhost"))return;let{type:t,data:o}=e.data||{};switch(t){case"RECUR_PAYMENT_SUCCESS":c(),s("recur:success",o);break;case"RECUR_PAYMENT_ERROR":s("recur:error",o);break;case"RECUR_PAYMENT_CANCEL":c(),s("recur:close",{});break;case"RECUR_RESIZE":u&&o?.height&&(u.style.height=`${Math.min(o.height+56,window.innerHeight*.9)}px`);break}}function s(e,t){window.dispatchEvent(new CustomEvent(e,{detail:t}))}async function f(){if(!(!r||!a)){a.setAttribute("data-loading","true");try{let e=await U(),t=new URL(`${r.embedUrl}/checkout`);t.searchParams.set("id",e.id),t.searchParams.set("key",r.key),t.searchParams.set("client_secret",e.clientSecret),A(t.toString())}catch(e){console.error("[Recur Widget] Error:",e),s("recur:error",{message:e instanceof Error?e.message:"Unknown error"})}finally{a.setAttribute("data-loading","false")}}}var h={open(){f()},close(){c()},show(){a&&(a.style.display="flex")},hide(){a&&(a.style.display="none")},destroy(){c(),a&&(a.remove(),a=null);let e=document.getElementById("recur-widget-styles");e&&e.remove()}};function p(){r=C(),r&&(M(),R(),_())}window.RecurWidget=h;document.readyState==="loading"?document.addEventListener("DOMContentLoaded",p):p();var S=h;return E(W);})();
|
|
132
132
|
/* Recur Widget - https://recur.tw */
|