recur-tw 0.9.7 → 0.9.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -2634,7 +2634,7 @@ function toCamelCase(obj) {
2634
2634
 
2635
2635
  // package.json
2636
2636
  var package_default = {
2637
- version: "0.9.7"};
2637
+ version: "0.9.8"};
2638
2638
  var SDK_VERSION = package_default.version;
2639
2639
  var SDK_TYPE = "react";
2640
2640
  var RecurContext = React.createContext(null);
@@ -2814,15 +2814,6 @@ function RecurProvider({ children, config: initialConfig = {} }) {
2814
2814
  document.body.appendChild(dialog);
2815
2815
  dialog.showModal();
2816
2816
  lockBodyScroll();
2817
- dialog.addEventListener("click", (e) => {
2818
- const rect = dialog.getBoundingClientRect();
2819
- const isInDialog = e.clientX >= rect.left && e.clientX <= rect.right && e.clientY >= rect.top && e.clientY <= rect.bottom;
2820
- if (!isInDialog) {
2821
- closeDialog();
2822
- setIsCheckingOut(false);
2823
- options.onPaymentCancel?.();
2824
- }
2825
- });
2826
2817
  dialog.addEventListener("cancel", (e) => {
2827
2818
  e.preventDefault();
2828
2819
  closeDialog();
package/dist/index.js CHANGED
@@ -2628,7 +2628,7 @@ function toCamelCase(obj) {
2628
2628
 
2629
2629
  // package.json
2630
2630
  var package_default = {
2631
- version: "0.9.7"};
2631
+ version: "0.9.8"};
2632
2632
  var SDK_VERSION = package_default.version;
2633
2633
  var SDK_TYPE = "react";
2634
2634
  var RecurContext = createContext(null);
@@ -2808,15 +2808,6 @@ function RecurProvider({ children, config: initialConfig = {} }) {
2808
2808
  document.body.appendChild(dialog);
2809
2809
  dialog.showModal();
2810
2810
  lockBodyScroll();
2811
- dialog.addEventListener("click", (e) => {
2812
- const rect = dialog.getBoundingClientRect();
2813
- const isInDialog = e.clientX >= rect.left && e.clientX <= rect.right && e.clientY >= rect.top && e.clientY <= rect.bottom;
2814
- if (!isInDialog) {
2815
- closeDialog();
2816
- setIsCheckingOut(false);
2817
- options.onPaymentCancel?.();
2818
- }
2819
- });
2820
2811
  dialog.addEventListener("cancel", (e) => {
2821
2812
  e.preventDefault();
2822
2813
  closeDialog();
package/dist/recur.umd.js CHANGED
@@ -211,7 +211,7 @@
211
211
  </button>
212
212
  `:""}
213
213
  </div>
214
- `,this.isDismissible&&this.shadowRoot.querySelector(".recur-sdk__error-dismiss")?.addEventListener("click",()=>this.handleDismiss())}};typeof window<"u"&&!customElements.get("recur-error-display")&&customElements.define("recur-error-display",$)});var le={};v(le,{RecurSkeletonLoader:()=>B});var B,de=w(()=>{"use strict";B=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`
214
+ `,this.isDismissible&&this.shadowRoot.querySelector(".recur-sdk__error-dismiss")?.addEventListener("click",()=>this.handleDismiss())}};typeof window<"u"&&!customElements.get("recur-error-display")&&customElements.define("recur-error-display",$)});var le={};v(le,{RecurSkeletonLoader:()=>K});var K,de=w(()=>{"use strict";K=class extends HTMLElement{static get observedAttributes(){return["type"]}constructor(){super(),this.attachShadow({mode:"open"})}connectedCallback(){this.render()}attributeChangedCallback(e,t,r){t!==r&&this.render()}get type(){let e=this.getAttribute("type");return e==="list"||e==="card"?e:"payment-form"}renderPaymentFormSkeleton(){return`
215
215
  <div class="skeleton-field">
216
216
  <div class="skeleton-label"></div>
217
217
  <div class="skeleton-input"></div>
@@ -381,7 +381,7 @@
381
381
  </style>
382
382
 
383
383
  ${e}
384
- `}};typeof window<"u"&&!customElements.get("recur-skeleton-loader")&&customElements.define("recur-skeleton-loader",B)});var ue={};v(ue,{RecurPaymentFormSkeleton:()=>K});var K,me=w(()=>{"use strict";K=class extends HTMLElement{constructor(){super(),this.attachShadow({mode:"open"})}connectedCallback(){this.render()}render(){this.shadowRoot.innerHTML=`
384
+ `}};typeof window<"u"&&!customElements.get("recur-skeleton-loader")&&customElements.define("recur-skeleton-loader",K)});var ue={};v(ue,{RecurPaymentFormSkeleton:()=>B});var B,me=w(()=>{"use strict";B=class extends HTMLElement{constructor(){super(),this.attachShadow({mode:"open"})}connectedCallback(){this.render()}render(){this.shadowRoot.innerHTML=`
385
385
  <style>
386
386
  :host {
387
387
  display: block;
@@ -648,7 +648,7 @@
648
648
  <p class="security-text">\u60A8\u7684\u4ED8\u6B3E\u8CC7\u8A0A\u7D93\u904E\u52A0\u5BC6\u4FDD\u8B77</p>
649
649
  </div>
650
650
  </div>
651
- `}};typeof window<"u"&&!customElements.get("recur-payment-form-skeleton")&&customElements.define("recur-payment-form-skeleton",K)});var pe={};v(pe,{RecurToast:()=>N,RecurToastContainer:()=>D});var N,x,D,he=w(()=>{"use strict";N=class extends HTMLElement{static get observedAttributes(){return["message","type","duration"]}constructor(){super(),this.attachShadow({mode:"open"})}connectedCallback(){this.render(),this.setupAutoDismiss()}get message(){return this.getAttribute("message")||"Notification"}get type(){let e=this.getAttribute("type");return e==="success"||e==="error"?e:"info"}get duration(){let e=this.getAttribute("duration");return e?parseInt(e,10):5e3}setupAutoDismiss(){let e=this.duration;e>0&&setTimeout(()=>this.dismiss(),e)}dismiss(){this.style.animation="recur-toast-slide-out 0.3s ease-in-out",setTimeout(()=>this.remove(),300)}getTypeIcon(){switch(this.type){case"success":return`<svg width="20" height="20" fill="none" stroke="currentColor" viewBox="0 0 24 24">
651
+ `}};typeof window<"u"&&!customElements.get("recur-payment-form-skeleton")&&customElements.define("recur-payment-form-skeleton",B)});var pe={};v(pe,{RecurToast:()=>N,RecurToastContainer:()=>D});var N,x,D,he=w(()=>{"use strict";N=class extends HTMLElement{static get observedAttributes(){return["message","type","duration"]}constructor(){super(),this.attachShadow({mode:"open"})}connectedCallback(){this.render(),this.setupAutoDismiss()}get message(){return this.getAttribute("message")||"Notification"}get type(){let e=this.getAttribute("type");return e==="success"||e==="error"?e:"info"}get duration(){let e=this.getAttribute("duration");return e?parseInt(e,10):5e3}setupAutoDismiss(){let e=this.duration;e>0&&setTimeout(()=>this.dismiss(),e)}dismiss(){this.style.animation="recur-toast-slide-out 0.3s ease-in-out",setTimeout(()=>this.remove(),300)}getTypeIcon(){switch(this.type){case"success":return`<svg width="20" height="20" fill="none" stroke="currentColor" viewBox="0 0 24 24">
652
652
  <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7" />
653
653
  </svg>`;case"error":return`<svg width="20" height="20" fill="none" stroke="currentColor" viewBox="0 0 24 24">
654
654
  <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
@@ -1235,7 +1235,7 @@
1235
1235
  >
1236
1236
  <span id="${this.containerId}-submit-text">\u78BA\u8A8D\u4ED8\u6B3E</span>
1237
1237
  </button>
1238
- `,t}async initializePayment(t,r){if(this._initializationAborted)return console.log("[PaymentForm] Initialization aborted before start"),null;if(this._isInitializing=!0,await this.updateComplete(),this._initializationAborted)return console.log("[PaymentForm] Initialization aborted after updateComplete"),this._isInitializing=!1,null;if(!window.UniPayment)throw this._isInitializing=!1,new Error("PAYUNi SDK not loaded");try{let i=document.getElementById(`${this.containerId}-card-no`),s=document.getElementById(`${this.containerId}-card-exp`),n=document.getElementById(`${this.containerId}-card-cvc`);if(!i||!s||!n)return console.log("[PaymentForm] DOM elements not found, likely disconnected"),this._isInitializing=!1,null;if(this._initializationAborted)return console.log("[PaymentForm] Initialization aborted before createSession"),this._isInitializing=!1,null;let o=window.UniPayment.createSession(t,{env:r==="SANDBOX"?"S":"P",elements:{CardNo:`${this.containerId}-card-no`,CardExp:`${this.containerId}-card-exp`,CardCvc:`${this.containerId}-card-cvc`}});if(this._initializationAborted)return console.log("[PaymentForm] Initialization aborted before paymentSession.start()"),this._isInitializing=!1,null;try{await o.start()}catch(l){if((l?.message?.includes("1008")||l?.message?.includes("timeout")||l?.code===1008)&&this._initializationAborted)return console.log("[PaymentForm] Caught 1008 timeout error after component disconnect - ignoring"),this._isInitializing=!1,null;throw l}return this._initializationAborted?(console.log("[PaymentForm] Initialization aborted after paymentSession.start()"),this._isInitializing=!1,null):(this.hideCardSkeletons(),o.onUpdate?.(l=>{if(this._initializationAborted){console.log("[PaymentForm] Ignoring onUpdate event - component disconnected");return}console.log("[PaymentForm] PAYUNi update:",l);let m=l.status&&l.status.CardNo===!0&&l.status.CardExp===!0&&l.status.CardCvc===!0,b=document.getElementById(`${this.containerId}-submit-btn`);b&&(b.disabled=!m,console.log("[PaymentForm] Submit button disabled:",!m))}),this._initializationAborted?(console.log("[PaymentForm] Initialization aborted before saving session"),this._isInitializing=!1,null):(this._paymentSession=o,this.setupFormSubmission(),this._isInitializing=!1,o))}catch(i){throw this._isInitializing=!1,console.error("[PaymentForm] Failed to initialize payment:",i),i}}setupFormSubmission(){let t=document.getElementById(`${this.containerId}-submit-btn`);t&&t.addEventListener("click",async r=>{if(r.preventDefault(),t.classList.contains("loading"))return;let i,s,n=document.getElementById(`${this.containerId}-email`),o=document.getElementById(`${this.containerId}-name`);if(n&&o){if(i=n.value,s=o.value,!i||!s){this.showError("\u8ACB\u586B\u5BEB\u59D3\u540D\u548C\u96FB\u5B50\u90F5\u4EF6");return}}else if(i=this.getAttribute("customer-email")||void 0,s=this.getAttribute("customer-name")||void 0,!i||!s){this.showError("\u5BA2\u6236\u8CC7\u8A0A\u4E0D\u5B8C\u6574");return}this.clearError(),this.setButtonLoading(!0),this.dispatchEvent(new CustomEvent("submit",{detail:{customerEmail:i,customerName:s,paymentSession:this._paymentSession},bubbles:!0,composed:!0}))})}setButtonLoading(t){let r=document.getElementById(`${this.containerId}-submit-btn`);r&&(t?(r.classList.add("loading"),r.disabled=!0,r.innerHTML=`
1238
+ `,t}async initializePayment(t,r){if(this._initializationAborted)return console.log("[PaymentForm] Initialization aborted before start"),null;if(this._isInitializing=!0,await this.updateComplete(),this._initializationAborted)return console.log("[PaymentForm] Initialization aborted after updateComplete"),this._isInitializing=!1,null;if(!window.UniPayment)throw this._isInitializing=!1,new Error("PAYUNi SDK not loaded");try{let i=document.getElementById(`${this.containerId}-card-no`),s=document.getElementById(`${this.containerId}-card-exp`),n=document.getElementById(`${this.containerId}-card-cvc`);if(!i||!s||!n)return console.log("[PaymentForm] DOM elements not found, likely disconnected"),this._isInitializing=!1,null;if(this._initializationAborted)return console.log("[PaymentForm] Initialization aborted before createSession"),this._isInitializing=!1,null;let o=window.UniPayment.createSession(t,{env:r==="SANDBOX"?"S":"P",elements:{CardNo:`${this.containerId}-card-no`,CardExp:`${this.containerId}-card-exp`,CardCvc:`${this.containerId}-card-cvc`}});if(this._initializationAborted)return console.log("[PaymentForm] Initialization aborted before paymentSession.start()"),this._isInitializing=!1,null;try{await o.start()}catch(d){if((d?.message?.includes("1008")||d?.message?.includes("timeout")||d?.code===1008)&&this._initializationAborted)return console.log("[PaymentForm] Caught 1008 timeout error after component disconnect - ignoring"),this._isInitializing=!1,null;throw d}return this._initializationAborted?(console.log("[PaymentForm] Initialization aborted after paymentSession.start()"),this._isInitializing=!1,null):(this.hideCardSkeletons(),o.onUpdate?.(d=>{if(this._initializationAborted){console.log("[PaymentForm] Ignoring onUpdate event - component disconnected");return}console.log("[PaymentForm] PAYUNi update:",d);let m=d.status&&d.status.CardNo===!0&&d.status.CardExp===!0&&d.status.CardCvc===!0,b=document.getElementById(`${this.containerId}-submit-btn`);b&&(b.disabled=!m,console.log("[PaymentForm] Submit button disabled:",!m))}),this._initializationAborted?(console.log("[PaymentForm] Initialization aborted before saving session"),this._isInitializing=!1,null):(this._paymentSession=o,this.setupFormSubmission(),this._isInitializing=!1,o))}catch(i){throw this._isInitializing=!1,console.error("[PaymentForm] Failed to initialize payment:",i),i}}setupFormSubmission(){let t=document.getElementById(`${this.containerId}-submit-btn`);t&&t.addEventListener("click",async r=>{if(r.preventDefault(),t.classList.contains("loading"))return;let i,s,n=document.getElementById(`${this.containerId}-email`),o=document.getElementById(`${this.containerId}-name`);if(n&&o){if(i=n.value,s=o.value,!i||!s){this.showError("\u8ACB\u586B\u5BEB\u59D3\u540D\u548C\u96FB\u5B50\u90F5\u4EF6");return}}else if(i=this.getAttribute("customer-email")||void 0,s=this.getAttribute("customer-name")||void 0,!i||!s){this.showError("\u5BA2\u6236\u8CC7\u8A0A\u4E0D\u5B8C\u6574");return}this.clearError(),this.setButtonLoading(!0),this.dispatchEvent(new CustomEvent("submit",{detail:{customerEmail:i,customerName:s,paymentSession:this._paymentSession},bubbles:!0,composed:!0}))})}setButtonLoading(t){let r=document.getElementById(`${this.containerId}-submit-btn`);r&&(t?(r.classList.add("loading"),r.disabled=!0,r.innerHTML=`
1239
1239
  <span class="recur-loading-spinner"></span>
1240
1240
  <span>\u8655\u7406\u4E2D...</span>
1241
1241
  `):(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=`
@@ -1467,7 +1467,7 @@
1467
1467
  <path d="M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2"/>
1468
1468
  <circle cx="12" cy="7" r="4"/>
1469
1469
  </svg>
1470
- `}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"),i=this.getAttribute("return-url");this._isLoading=!0,this.render(),this.setupEventListeners();try{let s={};r&&(s.customerId=r),i&&(s.returnUrl=i);let n=await fetch(t,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(s)});if(!n.ok){let m=await n.json().catch(()=>({}));throw new Error(m.error?.message||m.message||`HTTP ${n.status}: Failed to create portal session`)}let o=await n.json(),l=o.url||o.portalUrl;if(!l)throw new Error("Portal URL not found in response");this._isLoading=!1,this.render(),this.setupEventListeners(),this.redirectToPortal(l)}catch(s){this._isLoading=!1,this.render(),this.setupEventListeners(),this.dispatchError(s.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",F)});var je={};v(je,{RecurCheckout:()=>L,RecurElements:()=>I,createElements:()=>Z,default:()=>Oe,init:()=>Se});async function Ae(){if(typeof window>"u"){console.warn("[Recur SDK] Cannot register Web Components: window is not defined");return}await Promise.all([Promise.resolve().then(()=>(se(),ie)),Promise.resolve().then(()=>(ne(),oe)),Promise.resolve().then(()=>(ce(),ae)),Promise.resolve().then(()=>(de(),le)),Promise.resolve().then(()=>(me(),ue)),Promise.resolve().then(()=>(he(),pe)),Promise.resolve().then(()=>(ge(),fe)),Promise.resolve().then(()=>(ye(),be)),Promise.resolve().then(()=>(ve(),ke))]);let e=["recur-loading-spinner","recur-success-message","recur-error-display","recur-skeleton-loader","recur-payment-form-skeleton","recur-toast","recur-toast-container","recur-payment-form","recur-checkout","recur-portal"].filter(t=>!customElements.get(t));e.length>0&&console.warn("[Recur SDK] The following components are not registered:",e)}typeof window<"u"&&Ae();function ze(a){return a.replace(/_([a-z])/g,(e,t)=>t.toUpperCase())}function g(a){if(a==null)return a;if(Array.isArray(a))return a.map(e=>g(e));if(a instanceof Date)return a;if(typeof a=="object"){let e={};for(let[t,r]of Object.entries(a)){let i=ze(t);e[i]=g(r)}return e}return a}var we={name:"recur-tw",version:"0.9.7",description:"React & Vanilla JS SDK for embedding subscription checkout flows (Taiwan / PAYUNi)",type:"module",private:!1,author:"Recur",license:"Elastic-2.0",keywords:["react","vanilla-js","subscription","checkout","payment","payuni","recurring-billing","taiwan","\u7E41\u9AD4\u4E2D\u6587","sdk","embedded-checkout"],homepage:"https://recur.tw/",bugs:{email:"support@recur.tw"},scripts:{build:"tsup",dev:"tsup --watch",lint:"eslint .","lint:fix":"eslint . --fix","type-check":"tsc --noEmit",examples:"npx http-server examples -p 8080 -o"},main:"./dist/index.js",module:"./dist/index.js",types:"./dist/index.d.ts",exports:{".":{types:"./dist/index.d.ts",import:"./dist/index.js",require:"./dist/index.cjs"},"./server":{types:"./dist/server.d.ts",import:"./dist/server.js",require:"./dist/server.cjs"},"./vanilla":"./dist/recur.umd.js","./package.json":"./package.json"},unpkg:"./dist/recur.umd.js",jsdelivr:"./dist/recur.umd.js",files:["dist","README.md","AGENTS.md","LICENSE"],sideEffects:["src/components/**/*.ts","src/components/**/*.js"],peerDependencies:{react:">=18.0.0","react-dom":">=18.0.0"},peerDependenciesMeta:{react:{optional:!0},"react-dom":{optional:!0}},devDependencies:{"@eslint/js":"^9.39.1","@types/node":"^20.19.9","@types/react":"^19.1.9","@types/react-dom":"^19.1.7","@workspace/typescript-config":"workspace:*",autoprefixer:"^10.4.22",eslint:"^9","eslint-plugin-react":"^7.37.5","eslint-plugin-react-hooks":"^7.0.1",postcss:"^8.5.6",react:"^19.2.1","react-dom":"^19.2.1",tailwindcss:"^4.1.11",tsup:"^8.3.5",typescript:"^5.9.2","typescript-eslint":"^8.47.0"}};var $e=we.version,Be="vanilla",q=class{constructor(e){c(this,"config");if(!e.publishableKey)throw new Error("publishableKey is required");this.config={baseUrl:"https://api.recur.tw",...e}}getHeaders(){return{"Content-Type":"application/json","X-Recur-Publishable-Key":this.config.publishableKey,"X-Recur-SDK-Type":Be,"X-Recur-SDK-Version":$e,"X-Recur-Source":typeof window<"u"?window.location.origin:"server"}}async createSubscription(e){let{customerName:t,customerEmail:r}=e,i=e.productId||e.planId,s=e.productSlug;if(!i&&!s)throw new Error("Either productId or productSlug is required");let n={customerName:t,customerEmail:r};i&&(n.productId=i),s&&(n.productSlug=s);let o=await fetch(`${this.config.baseUrl}/v1/subscriptions`,{method:"POST",headers:this.getHeaders(),body:JSON.stringify(n)});if(!o.ok){let m=await o.json().catch(()=>({}));throw{code:m.error||"CHECKOUT_FAILED",message:m.message||"Failed to initiate checkout",details:m}}let l=await o.json();return g(l)}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 s=await r.json().catch(()=>({}));throw{code:s.error||"FETCH_PRODUCTS_FAILED",message:s.message||"Failed to fetch products",details:s}}let i=await r.json();return g(i)}async fetchPlans(){return{plans:(await this.fetchProducts({type:"SUBSCRIPTION"})).products}}getConfig(){return{...this.config}}};var V=class{constructor(e,t){c(this,"config");c(this,"options");c(this,"container");c(this,"checkoutId",null);c(this,"sdkToken",null);c(this,"sdkTimestamp",null);c(this,"creditToken",null);c(this,"sdkEnv","S");c(this,"payuniSDK",null);c(this,"isFormValid",!1);if(this.config=e,this.options=t,typeof t.container=="string"){let r=document.querySelector(t.container);if(!r)throw new Error(`Container not found: ${t.container}`);this.container=r}else this.container=t.container}async render(){try{await this.initCheckout(),this.renderHTML(),await this.loadPayUniSDK(),await this.initPayUniSDK(),this.setupFormSubmit()}catch(e){this.handleError(e)}}getBaseUrl(){if(this.config.baseUrl)return this.config.baseUrl;if(typeof window<"u"){let e=window.location.hostname;if(e==="localhost"||e.includes(".test")||e.includes(".local")||e==="127.0.0.1")return`${window.location.protocol}//${window.location.host}/api`}return"https://api.recur.tw"}async initCheckout(){let e=this.getBaseUrl(),t=await fetch(`${e}/v1/checkouts`,{method:"POST",headers:{"Content-Type":"application/json","X-Recur-Publishable-Key":this.config.publishableKey},body:JSON.stringify({productId:this.options.productId||this.options.planId,customerEmail:this.options.customerEmail,customerName:this.options.customerName})});if(!t.ok){let s=await t.json().catch(()=>({}));throw new Error(s.error||"Failed to initialize checkout")}let r=await t.json(),i=g(r);this.checkoutId=i.checkout.id,this.sdkToken=i.sdkToken,this.sdkTimestamp=i.sdkTimestamp||null,this.creditToken=i.creditToken||null,this.sdkEnv=this.config.publishableKey.startsWith("pk_test_")?"S":"P"}renderHTML(){this.container.innerHTML=`
1470
+ `}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"),i=this.getAttribute("return-url");this._isLoading=!0,this.render(),this.setupEventListeners();try{let s={};r&&(s.customerId=r),i&&(s.returnUrl=i);let n=await fetch(t,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(s)});if(!n.ok){let m=await n.json().catch(()=>({}));throw new Error(m.error?.message||m.message||`HTTP ${n.status}: Failed to create portal session`)}let o=await n.json(),d=o.url||o.portalUrl;if(!d)throw new Error("Portal URL not found in response");this._isLoading=!1,this.render(),this.setupEventListeners(),this.redirectToPortal(d)}catch(s){this._isLoading=!1,this.render(),this.setupEventListeners(),this.dispatchError(s.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",F)});var je={};v(je,{RecurCheckout:()=>L,RecurElements:()=>I,createElements:()=>Z,default:()=>Oe,init:()=>Se});async function Ae(){if(typeof window>"u"){console.warn("[Recur SDK] Cannot register Web Components: window is not defined");return}await Promise.all([Promise.resolve().then(()=>(se(),ie)),Promise.resolve().then(()=>(ne(),oe)),Promise.resolve().then(()=>(ce(),ae)),Promise.resolve().then(()=>(de(),le)),Promise.resolve().then(()=>(me(),ue)),Promise.resolve().then(()=>(he(),pe)),Promise.resolve().then(()=>(ge(),fe)),Promise.resolve().then(()=>(ye(),be)),Promise.resolve().then(()=>(ve(),ke))]);let e=["recur-loading-spinner","recur-success-message","recur-error-display","recur-skeleton-loader","recur-payment-form-skeleton","recur-toast","recur-toast-container","recur-payment-form","recur-checkout","recur-portal"].filter(t=>!customElements.get(t));e.length>0&&console.warn("[Recur SDK] The following components are not registered:",e)}typeof window<"u"&&Ae();function ze(a){return a.replace(/_([a-z])/g,(e,t)=>t.toUpperCase())}function g(a){if(a==null)return a;if(Array.isArray(a))return a.map(e=>g(e));if(a instanceof Date)return a;if(typeof a=="object"){let e={};for(let[t,r]of Object.entries(a)){let i=ze(t);e[i]=g(r)}return e}return a}var we={name:"recur-tw",version:"0.9.8",description:"React & Vanilla JS SDK for embedding subscription checkout flows (Taiwan / PAYUNi)",type:"module",private:!1,author:"Recur",license:"Elastic-2.0",keywords:["react","vanilla-js","subscription","checkout","payment","payuni","recurring-billing","taiwan","\u7E41\u9AD4\u4E2D\u6587","sdk","embedded-checkout"],homepage:"https://recur.tw/",bugs:{email:"support@recur.tw"},scripts:{build:"tsup",dev:"tsup --watch",lint:"eslint .","lint:fix":"eslint . --fix","type-check":"tsc --noEmit",examples:"npx http-server examples -p 8080 -o"},main:"./dist/index.js",module:"./dist/index.js",types:"./dist/index.d.ts",exports:{".":{types:"./dist/index.d.ts",import:"./dist/index.js",require:"./dist/index.cjs"},"./server":{types:"./dist/server.d.ts",import:"./dist/server.js",require:"./dist/server.cjs"},"./vanilla":"./dist/recur.umd.js","./package.json":"./package.json"},unpkg:"./dist/recur.umd.js",jsdelivr:"./dist/recur.umd.js",files:["dist","README.md","AGENTS.md","LICENSE"],sideEffects:["src/components/**/*.ts","src/components/**/*.js"],peerDependencies:{react:">=18.0.0","react-dom":">=18.0.0"},peerDependenciesMeta:{react:{optional:!0},"react-dom":{optional:!0}},devDependencies:{"@eslint/js":"^9.39.1","@types/node":"^20.19.9","@types/react":"^19.1.9","@types/react-dom":"^19.1.7","@workspace/typescript-config":"workspace:*",autoprefixer:"^10.4.22",eslint:"^9","eslint-plugin-react":"^7.37.5","eslint-plugin-react-hooks":"^7.0.1",postcss:"^8.5.6",react:"^19.2.1","react-dom":"^19.2.1",tailwindcss:"^4.1.11",tsup:"^8.3.5",typescript:"^5.9.2","typescript-eslint":"^8.47.0"}};var $e=we.version,Ke="vanilla",q=class{constructor(e){c(this,"config");if(!e.publishableKey)throw new Error("publishableKey is required");this.config={baseUrl:"https://api.recur.tw",...e}}getHeaders(){return{"Content-Type":"application/json","X-Recur-Publishable-Key":this.config.publishableKey,"X-Recur-SDK-Type":Ke,"X-Recur-SDK-Version":$e,"X-Recur-Source":typeof window<"u"?window.location.origin:"server"}}async createSubscription(e){let{customerName:t,customerEmail:r}=e,i=e.productId||e.planId,s=e.productSlug;if(!i&&!s)throw new Error("Either productId or productSlug is required");let n={customerName:t,customerEmail:r};i&&(n.productId=i),s&&(n.productSlug=s);let o=await fetch(`${this.config.baseUrl}/v1/subscriptions`,{method:"POST",headers:this.getHeaders(),body:JSON.stringify(n)});if(!o.ok){let m=await o.json().catch(()=>({}));throw{code:m.error||"CHECKOUT_FAILED",message:m.message||"Failed to initiate checkout",details:m}}let d=await o.json();return g(d)}async fetchProducts(e){let t=new URL(`${this.config.baseUrl}/v1/products`);e?.type&&t.searchParams.set("type",e.type);let r=await fetch(t.toString(),{method:"GET",headers:this.getHeaders()});if(!r.ok){let s=await r.json().catch(()=>({}));throw{code:s.error||"FETCH_PRODUCTS_FAILED",message:s.message||"Failed to fetch products",details:s}}let i=await r.json();return g(i)}async fetchPlans(){return{plans:(await this.fetchProducts({type:"SUBSCRIPTION"})).products}}getConfig(){return{...this.config}}};var V=class{constructor(e,t){c(this,"config");c(this,"options");c(this,"container");c(this,"checkoutId",null);c(this,"sdkToken",null);c(this,"sdkTimestamp",null);c(this,"creditToken",null);c(this,"sdkEnv","S");c(this,"payuniSDK",null);c(this,"isFormValid",!1);if(this.config=e,this.options=t,typeof t.container=="string"){let r=document.querySelector(t.container);if(!r)throw new Error(`Container not found: ${t.container}`);this.container=r}else this.container=t.container}async render(){try{await this.initCheckout(),this.renderHTML(),await this.loadPayUniSDK(),await this.initPayUniSDK(),this.setupFormSubmit()}catch(e){this.handleError(e)}}getBaseUrl(){if(this.config.baseUrl)return this.config.baseUrl;if(typeof window<"u"){let e=window.location.hostname;if(e==="localhost"||e.includes(".test")||e.includes(".local")||e==="127.0.0.1")return`${window.location.protocol}//${window.location.host}/api`}return"https://api.recur.tw"}async initCheckout(){let e=this.getBaseUrl(),t=await fetch(`${e}/v1/checkouts`,{method:"POST",headers:{"Content-Type":"application/json","X-Recur-Publishable-Key":this.config.publishableKey},body:JSON.stringify({productId:this.options.productId||this.options.planId,customerEmail:this.options.customerEmail,customerName:this.options.customerName})});if(!t.ok){let s=await t.json().catch(()=>({}));throw new Error(s.error||"Failed to initialize checkout")}let r=await t.json(),i=g(r);this.checkoutId=i.checkout.id,this.sdkToken=i.sdkToken,this.sdkTimestamp=i.sdkTimestamp||null,this.creditToken=i.creditToken||null,this.sdkEnv=this.config.publishableKey.startsWith("pk_test_")?"S":"P"}renderHTML(){this.container.innerHTML=`
1471
1471
  <div class="recur-embedded-checkout" style="max-width: 400px; margin: 0 auto; padding: 20px; font-family: system-ui, -apple-system, sans-serif;">
1472
1472
  <div class="recur-checkout-header" style="margin-bottom: 24px;">
1473
1473
  <h2 style="margin: 0 0 8px 0; font-size: 20px; font-weight: 600; color: #111;">Subscribe</h2>
@@ -1537,21 +1537,21 @@
1537
1537
  </p>
1538
1538
  </form>
1539
1539
  </div>
1540
- `}async loadPayUniSDK(){return new Promise((e,t)=>{if(window.UniPayment){e();return}let r=document.createElement("script");r.src=this.sdkEnv==="P"?"https://vendor.payuni.com.tw/sdk/uni-payment.js":"https://sandbox-vendor.payuni.com.tw/sdk/uni-payment.js",r.async=!0,r.onload=()=>e(),r.onerror=()=>t(new Error("Failed to load PAYUNi SDK")),document.head.appendChild(r)})}async initPayUniSDK(){if(!this.sdkToken)throw new Error("SDK token not initialized");let e=window.UniPayment;if(!e)throw new Error("PAYUNi SDK not loaded");let t={env:this.sdkEnv,useInst:!1,elements:{CardNo:"recur-card-number",CardExp:"recur-card-exp",CardCvc:"recur-card-cvc"},style:{color:"#111827",errorColor:"#dc2626",fontSize:"14px",fontWeight:"400",lineHeight:"24px"}};this.payuniSDK=e.createSession(this.sdkToken,t),await this.payuniSDK.start(),this.payuniSDK.onUpdate(r=>{let i=r?.status;i&&(this.isFormValid=i.CardNo===!0&&i.CardExp===!0&&i.CardCvc===!0,this.updateSubmitButton())})}updateSubmitButton(){let e=document.getElementById("recur-submit-btn");e&&(e.disabled=!this.isFormValid,e.style.opacity=this.isFormValid?"1":"0.5",e.style.cursor=this.isFormValid?"pointer":"not-allowed")}setupFormSubmit(){let e=document.getElementById("recur-checkout-form");e&&e.addEventListener("submit",async t=>{t.preventDefault(),await this.handleSubmit()})}async handleSubmit(){try{this.showLoading(!0),this.hideError();let e=document.getElementById("recur-email").value,t=document.getElementById("recur-name").value;if(!e||!t)throw new Error("Please fill in all required fields");if(!this.checkoutId)throw new Error("Checkout session not initialized");if(!this.payuniSDK)throw new Error("Payment system not initialized");let r=await this.payuniSDK.getTradeResult();if(r.Status!=="SUCCESS")throw new Error(r.Message||"Card validation failed");let i=r.EncryptInfo||r.creditToken;if(!i)throw console.error("[Recur SDK] Missing payment token. Available fields:",Object.keys(r)),new Error("Missing payment token");let s=this.getBaseUrl(),n=await fetch(`${s}/v1/checkouts/${this.checkoutId}/pay`,{method:"POST",headers:{"Content-Type":"application/json","X-Recur-Publishable-Key":this.config.publishableKey},body:JSON.stringify({creditToken:i,timestamp:this.sdkTimestamp||r.HashTimestamp||r.timestamp})});if(!n.ok){let b=await n.json().catch(()=>({}));throw new Error(b.error||"Failed to process payment")}let o=await n.json(),l=this.options.productId||this.options.planId||"",m={subscription:{id:o.subscription?.id||o.charge?.id||"",status:o.success?"active":"failed",productId:l,productName:"",amount:o.charge?.amount||0,billingPeriod:o.subscription?.billingPeriod||"MONTHLY",trialDays:null},subscriber:{id:"",email:document.getElementById("recur-email")?.value||"",name:document.getElementById("recur-name")?.value||""},nextSteps:{getSdkToken:"",completeSubscription:""}};this.options.onSuccess&&this.options.onSuccess(m),this.showSuccess()}catch(e){this.handleError(e)}finally{this.showLoading(!1)}}showLoading(e){let t=document.getElementById("recur-submit-btn");t&&(t.disabled=e,t.textContent=e?"Processing...":"Subscribe Now")}showError(e){let t=document.getElementById("recur-error");t&&(t.textContent=e,t.style.display="block")}hideError(){let e=document.getElementById("recur-error");e&&(e.style.display="none")}showSuccess(){this.container.innerHTML="";let e=document.createElement("recur-success-message");e.setAttribute("title","Subscription Complete!"),e.setAttribute("message","Thank you for subscribing. You will receive a confirmation email shortly."),this.container.appendChild(e)}handleError(e){let t=e?.message||"An error occurred";this.showError(t);let r={code:"CHECKOUT_ERROR",message:t};this.options.onError&&this.options.onError(r)}};var I=class{constructor(e){c(this,"publishableKey");c(this,"baseUrl");c(this,"embedUrl");c(this,"iframe",null);c(this,"container",null);c(this,"sessionId",null);c(this,"timestamp",null);c(this,"creditToken",null);c(this,"sdkToken",null);c(this,"cardToken",null);c(this,"cardTimestamp",null);c(this,"eventHandlers",new Map);typeof e=="string"?(this.publishableKey=e,this.baseUrl=this.getDefaultBaseUrl(),this.embedUrl=this.getDefaultEmbedUrl()):(this.publishableKey=e.publishableKey,this.baseUrl=e.baseUrl||this.getDefaultBaseUrl(),this.embedUrl=e.embedUrl||this.getDefaultEmbedUrl()),window.addEventListener("message",this.handleMessage.bind(this))}async mount(e){let t=typeof e=="string"?document.querySelector(e):e;if(!t)throw new Error(`Container not found: ${e}`);return this.container=t,this.iframe=document.createElement("iframe"),this.iframe.src=`${this.embedUrl}/elements?key=${encodeURIComponent(this.publishableKey)}`,this.iframe.style.cssText=`
1540
+ `}async loadPayUniSDK(){return new Promise((e,t)=>{if(window.UniPayment){e();return}let r=document.createElement("script");r.src=this.sdkEnv==="P"?"https://vendor.payuni.com.tw/sdk/uni-payment.js":"https://sandbox-vendor.payuni.com.tw/sdk/uni-payment.js",r.async=!0,r.onload=()=>e(),r.onerror=()=>t(new Error("Failed to load PAYUNi SDK")),document.head.appendChild(r)})}async initPayUniSDK(){if(!this.sdkToken)throw new Error("SDK token not initialized");let e=window.UniPayment;if(!e)throw new Error("PAYUNi SDK not loaded");let t={env:this.sdkEnv,useInst:!1,elements:{CardNo:"recur-card-number",CardExp:"recur-card-exp",CardCvc:"recur-card-cvc"},style:{color:"#111827",errorColor:"#dc2626",fontSize:"14px",fontWeight:"400",lineHeight:"24px"}};this.payuniSDK=e.createSession(this.sdkToken,t),await this.payuniSDK.start(),this.payuniSDK.onUpdate(r=>{let i=r?.status;i&&(this.isFormValid=i.CardNo===!0&&i.CardExp===!0&&i.CardCvc===!0,this.updateSubmitButton())})}updateSubmitButton(){let e=document.getElementById("recur-submit-btn");e&&(e.disabled=!this.isFormValid,e.style.opacity=this.isFormValid?"1":"0.5",e.style.cursor=this.isFormValid?"pointer":"not-allowed")}setupFormSubmit(){let e=document.getElementById("recur-checkout-form");e&&e.addEventListener("submit",async t=>{t.preventDefault(),await this.handleSubmit()})}async handleSubmit(){try{this.showLoading(!0),this.hideError();let e=document.getElementById("recur-email").value,t=document.getElementById("recur-name").value;if(!e||!t)throw new Error("Please fill in all required fields");if(!this.checkoutId)throw new Error("Checkout session not initialized");if(!this.payuniSDK)throw new Error("Payment system not initialized");let r=await this.payuniSDK.getTradeResult();if(r.Status!=="SUCCESS")throw new Error(r.Message||"Card validation failed");let i=r.EncryptInfo||r.creditToken;if(!i)throw console.error("[Recur SDK] Missing payment token. Available fields:",Object.keys(r)),new Error("Missing payment token");let s=this.getBaseUrl(),n=await fetch(`${s}/v1/checkouts/${this.checkoutId}/pay`,{method:"POST",headers:{"Content-Type":"application/json","X-Recur-Publishable-Key":this.config.publishableKey},body:JSON.stringify({creditToken:i,timestamp:this.sdkTimestamp||r.HashTimestamp||r.timestamp})});if(!n.ok){let b=await n.json().catch(()=>({}));throw new Error(b.error||"Failed to process payment")}let o=await n.json(),d=this.options.productId||this.options.planId||"",m={subscription:{id:o.subscription?.id||o.charge?.id||"",status:o.success?"active":"failed",productId:d,productName:"",amount:o.charge?.amount||0,billingPeriod:o.subscription?.billingPeriod||"MONTHLY",trialDays:null},subscriber:{id:"",email:document.getElementById("recur-email")?.value||"",name:document.getElementById("recur-name")?.value||""},nextSteps:{getSdkToken:"",completeSubscription:""}};this.options.onSuccess&&this.options.onSuccess(m),this.showSuccess()}catch(e){this.handleError(e)}finally{this.showLoading(!1)}}showLoading(e){let t=document.getElementById("recur-submit-btn");t&&(t.disabled=e,t.textContent=e?"Processing...":"Subscribe Now")}showError(e){let t=document.getElementById("recur-error");t&&(t.textContent=e,t.style.display="block")}hideError(){let e=document.getElementById("recur-error");e&&(e.style.display="none")}showSuccess(){this.container.innerHTML="";let e=document.createElement("recur-success-message");e.setAttribute("title","Subscription Complete!"),e.setAttribute("message","Thank you for subscribing. You will receive a confirmation email shortly."),this.container.appendChild(e)}handleError(e){let t=e?.message||"An error occurred";this.showError(t);let r={code:"CHECKOUT_ERROR",message:t};this.options.onError&&this.options.onError(r)}};var I=class{constructor(e){c(this,"publishableKey");c(this,"baseUrl");c(this,"embedUrl");c(this,"iframe",null);c(this,"container",null);c(this,"sessionId",null);c(this,"timestamp",null);c(this,"creditToken",null);c(this,"sdkToken",null);c(this,"cardToken",null);c(this,"cardTimestamp",null);c(this,"eventHandlers",new Map);typeof e=="string"?(this.publishableKey=e,this.baseUrl=this.getDefaultBaseUrl(),this.embedUrl=this.getDefaultEmbedUrl()):(this.publishableKey=e.publishableKey,this.baseUrl=e.baseUrl||this.getDefaultBaseUrl(),this.embedUrl=e.embedUrl||this.getDefaultEmbedUrl()),window.addEventListener("message",this.handleMessage.bind(this))}async mount(e){let t=typeof e=="string"?document.querySelector(e):e;if(!t)throw new Error(`Container not found: ${e}`);return this.container=t,this.iframe=document.createElement("iframe"),this.iframe.src=`${this.embedUrl}/elements?key=${encodeURIComponent(this.publishableKey)}`,this.iframe.style.cssText=`
1541
1541
  width: 100%;
1542
1542
  border: none;
1543
1543
  min-height: 200px;
1544
1544
  display: block;
1545
1545
  user-select: none;
1546
1546
  transition: height 0.35s ease, opacity 0.4s ease 0.1s;
1547
- `.replace(/\s+/g," ").trim(),this.iframe.allow="payment *",this.iframe.setAttribute("title","Secure payment input frame"),this.iframe.setAttribute("role","presentation"),this.iframe.setAttribute("scrolling","no"),this.iframe.setAttribute("frameBorder","0"),this.container.innerHTML="",this.container.appendChild(this.iframe),new Promise((r,i)=>{let s=setTimeout(()=>{i(new Error("Elements initialization timeout"))},3e4),n=()=>{clearTimeout(s),this.off("ready",n),r()},o=l=>{clearTimeout(s),this.off("error",o),i(new Error(l.message||"Elements initialization failed"))};this.on("ready",n),this.on("error",o)})}on(e,t){this.eventHandlers.has(e)||this.eventHandlers.set(e,new Set),this.eventHandlers.get(e).add(t)}off(e,t){let r=this.eventHandlers.get(e);r&&r.delete(t)}emit(e,t){let r=this.eventHandlers.get(e);r&&r.forEach(i=>i(t))}handleMessage(e){if(!this.isValidOrigin(e.origin))return;let{type:t,data:r}=e.data||{};switch(t){case"RECUR_ELEMENTS_READY":this.sessionId=r.sessionId,this.timestamp=r.timestamp,this.creditToken=r.creditToken,this.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 i=setTimeout(()=>{r(new Error("Tokenization timeout"))},6e4),s=o=>{clearTimeout(i),this.off("tokenized",s),t(o)},n=o=>{clearTimeout(i),this.off("error",n),r(new Error(o.message||"Tokenization failed"))};this.on("tokenized",s),this.on("error",n)})}async confirmPayment(e){if(!this.sessionId||!this.cardToken)throw new Error("Card not tokenized. Call tokenize() first.");let t=await fetch(`${this.baseUrl}/api/v1/elements/confirm`,{method:"POST",headers:{"Content-Type":"application/json","X-Recur-Publishable-Key":this.publishableKey},body:JSON.stringify({sessionId:this.sessionId,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 Z(a){return new I(a)}var Ke="https://vendor.payuni.com.tw/sdk/uni-payment.js",Ne="https://sandbox-vendor.payuni.com.tw/sdk/uni-payment.js",xe=!1,Y=!1,U=null;async function Ee(a=!1){return xe&&window.UniPayment?Promise.resolve():(Y&&U||(Y=!0,U=new Promise((e,t)=>{let r=document.createElement("script");r.src=a?Ne:Ke,r.async=!0,r.onload=()=>{xe=!0,Y=!1,e()},r.onerror=()=>{Y=!1,U=null,t(new Error("Failed to load PAYUNi SDK"))},document.head.appendChild(r)})),U)}var L=class{constructor(e){c(this,"core");c(this,"currentDialog",null);c(this,"currentIframe",null);c(this,"currentDialogElement",null);this.core=new q(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 V(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 i=this.core.getConfig(),s=await fetch(`${t}/v1/checkout/sessions`,{method:"POST",headers:{"Content-Type":"application/json","X-Recur-Publishable-Key":i.publishableKey},body:JSON.stringify(r)});if(!s.ok){let l=await s.json().catch(()=>({}));throw new Error(l.error?.message||l.error||"Failed to create checkout session")}let n=await s.json(),o=g(n);return{id:o.id,url:o.url,expiresAt:o.expiresAt,clientSecret:o.clientSecret}}async getCheckoutStatus(e,t){let r=this.getBaseUrl(),i=this.core.getConfig(),s=await fetch(`${r}/v1/checkouts/${e}?client_secret=${encodeURIComponent(t)}`,{method:"GET",headers:{"X-Recur-Publishable-Key":i.publishableKey}});if(!s.ok){let l=await s.json().catch(()=>({}));throw new Error(l.error||"Failed to get checkout status")}let n=await s.json(),o=g(n);return{id:o.checkout.id,status:o.checkout.status,amount:o.checkout.amount,currency:o.checkout.currency,lastCharge:o.lastCharge}}async checkout(e){let t=this.core.getConfig(),r=null,i=e.productId||e.planId,s=e.productSlug;try{if(console.log("[Recur SDK] Starting checkout flow...",{productId:i,productSlug:s,mode:e.mode}),!i&&!s)throw new Error("Either productId or productSlug is required");if(!t.publishableKey)throw new Error("publishableKey is required");let n=this.getBaseUrl();console.log("[Recur SDK] Base URL:",n);let o={"Content-Type":"application/json","X-Recur-Publishable-Key":t.publishableKey},l=e.mode||"modal";if(l==="redirect"){if(!e.successUrl)throw new Error("successUrl is required for redirect mode");if(!e.cancelUrl)throw new Error("cancelUrl is required for redirect mode");console.log("[Recur SDK] Creating hosted checkout session...");let h={successUrl:e.successUrl,cancelUrl:e.cancelUrl};i&&(h.productId=i),s&&(h.productSlug=s),e.customerEmail&&(h.customerEmail=e.customerEmail),e.customerName&&(h.customerName=e.customerName),e.externalCustomerId&&(h.externalCustomerId=e.externalCustomerId);let S=await fetch(`${n}/v1/checkout/sessions`,{method:"POST",headers:o,body:JSON.stringify(h)});if(!S.ok){let C=await S.json().catch(()=>({}));throw console.error("[Recur SDK] Failed to create checkout session:",C),new Error(C.error?.message||C.error||"Failed to create checkout session")}let J=await S.json(),k=g(J);console.log("[Recur SDK] Checkout session created:",k),console.log("[Recur SDK] Redirecting to hosted checkout:",k.url),window.location.href=k.url;return}let m=null;if(l==="modal"){let h=this.createDialogWithSkeleton(e.onClose);r=h.dialog,m=h.container}else if(l==="iframe"){if(m=this.getEmbeddedContainer(e.container),!m)throw new Error("Container is required for iframe mode");m.innerHTML="";let h=document.createElement("recur-payment-form-skeleton");m.appendChild(h)}console.log("[Recur SDK] Step 1: Creating embedded checkout...");let b={customerName:e.customerName,customerEmail:e.customerEmail};i&&(b.productId=i),s&&(b.productSlug=s),e.externalCustomerId&&(b.externalCustomerId=e.externalCustomerId),e.successUrl&&(b.successUrl=e.successUrl),e.cancelUrl&&(b.cancelUrl=e.cancelUrl);let X=await fetch(`${n}/v1/checkouts`,{method:"POST",headers:o,body:JSON.stringify(b)});if(!X.ok){let h=await X.json().catch(()=>({}));console.error("[Recur SDK] Failed to create checkout:",h);let S=h.details||h.error||"Failed to create checkout";throw new Error(S)}let Ce=await X.json(),d=g(Ce);if(console.log("[Recur SDK] Checkout created successfully:",d),e.onSuccess?.(d),console.log("[Recur SDK] Step 2: Extracting SDK token..."),!d.sdkToken)throw new Error("SDK token missing from checkout response");console.log("[Recur SDK] Step 3: Loading PAYUNi SDK...");let W=!d.livemode;if(console.log("[Recur SDK] Environment:",W?"SANDBOX":"PRODUCTION"),await Ee(W),console.log("[Recur SDK] PAYUNi SDK loaded successfully"),!window.UniPayment)throw new Error("PAYUNi SDK failed to load");if(console.log("[Recur SDK] Step 4: Rendering payment form..."),!m)throw new Error("Payment container not available");m.innerHTML="";let p=document.createElement("recur-payment-form");if(p.setAttribute("container-id",m.id||"recur-payment-container"),e.customerName&&p.setAttribute("customer-name",e.customerName),e.customerEmail&&p.setAttribute("customer-email",e.customerEmail),d.product?.name&&p.setAttribute("product-name",d.product.name),d.checkout?.amount&&p.setAttribute("amount",d.checkout.amount.toString()),d.product?.interval&&p.setAttribute("interval",d.product.interval),p.setAttribute("custom-styles",`
1547
+ `.replace(/\s+/g," ").trim(),this.iframe.allow="payment *",this.iframe.setAttribute("title","Secure payment input frame"),this.iframe.setAttribute("role","presentation"),this.iframe.setAttribute("scrolling","no"),this.iframe.setAttribute("frameBorder","0"),this.container.innerHTML="",this.container.appendChild(this.iframe),new Promise((r,i)=>{let s=setTimeout(()=>{i(new Error("Elements initialization timeout"))},3e4),n=()=>{clearTimeout(s),this.off("ready",n),r()},o=d=>{clearTimeout(s),this.off("error",o),i(new Error(d.message||"Elements initialization failed"))};this.on("ready",n),this.on("error",o)})}on(e,t){this.eventHandlers.has(e)||this.eventHandlers.set(e,new Set),this.eventHandlers.get(e).add(t)}off(e,t){let r=this.eventHandlers.get(e);r&&r.delete(t)}emit(e,t){let r=this.eventHandlers.get(e);r&&r.forEach(i=>i(t))}handleMessage(e){if(!this.isValidOrigin(e.origin))return;let{type:t,data:r}=e.data||{};switch(t){case"RECUR_ELEMENTS_READY":this.sessionId=r.sessionId,this.timestamp=r.timestamp,this.creditToken=r.creditToken,this.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 i=setTimeout(()=>{r(new Error("Tokenization timeout"))},6e4),s=o=>{clearTimeout(i),this.off("tokenized",s),t(o)},n=o=>{clearTimeout(i),this.off("error",n),r(new Error(o.message||"Tokenization failed"))};this.on("tokenized",s),this.on("error",n)})}async confirmPayment(e){if(!this.sessionId||!this.cardToken)throw new Error("Card not tokenized. Call tokenize() first.");let t=await fetch(`${this.baseUrl}/api/v1/elements/confirm`,{method:"POST",headers:{"Content-Type":"application/json","X-Recur-Publishable-Key":this.publishableKey},body:JSON.stringify({sessionId:this.sessionId,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 Z(a){return new I(a)}var Be="https://vendor.payuni.com.tw/sdk/uni-payment.js",Ne="https://sandbox-vendor.payuni.com.tw/sdk/uni-payment.js",xe=!1,Y=!1,U=null;async function Ee(a=!1){return xe&&window.UniPayment?Promise.resolve():(Y&&U||(Y=!0,U=new Promise((e,t)=>{let r=document.createElement("script");r.src=a?Ne:Be,r.async=!0,r.onload=()=>{xe=!0,Y=!1,e()},r.onerror=()=>{Y=!1,U=null,t(new Error("Failed to load PAYUNi SDK"))},document.head.appendChild(r)})),U)}var L=class{constructor(e){c(this,"core");c(this,"currentDialog",null);c(this,"currentIframe",null);c(this,"currentDialogElement",null);this.core=new q(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 V(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 i=this.core.getConfig(),s=await fetch(`${t}/v1/checkout/sessions`,{method:"POST",headers:{"Content-Type":"application/json","X-Recur-Publishable-Key":i.publishableKey},body:JSON.stringify(r)});if(!s.ok){let d=await s.json().catch(()=>({}));throw new Error(d.error?.message||d.error||"Failed to create checkout session")}let n=await s.json(),o=g(n);return{id:o.id,url:o.url,expiresAt:o.expiresAt,clientSecret:o.clientSecret}}async getCheckoutStatus(e,t){let r=this.getBaseUrl(),i=this.core.getConfig(),s=await fetch(`${r}/v1/checkouts/${e}?client_secret=${encodeURIComponent(t)}`,{method:"GET",headers:{"X-Recur-Publishable-Key":i.publishableKey}});if(!s.ok){let d=await s.json().catch(()=>({}));throw new Error(d.error||"Failed to get checkout status")}let n=await s.json(),o=g(n);return{id:o.checkout.id,status:o.checkout.status,amount:o.checkout.amount,currency:o.checkout.currency,lastCharge:o.lastCharge}}async checkout(e){let t=this.core.getConfig(),r=null,i=e.productId||e.planId,s=e.productSlug;try{if(console.log("[Recur SDK] Starting checkout flow...",{productId:i,productSlug:s,mode:e.mode}),!i&&!s)throw new Error("Either productId or productSlug is required");if(!t.publishableKey)throw new Error("publishableKey is required");let n=this.getBaseUrl();console.log("[Recur SDK] Base URL:",n);let o={"Content-Type":"application/json","X-Recur-Publishable-Key":t.publishableKey},d=e.mode||"modal";if(d==="redirect"){if(!e.successUrl)throw new Error("successUrl is required for redirect mode");if(!e.cancelUrl)throw new Error("cancelUrl is required for redirect mode");console.log("[Recur SDK] Creating hosted checkout session...");let h={successUrl:e.successUrl,cancelUrl:e.cancelUrl};i&&(h.productId=i),s&&(h.productSlug=s),e.customerEmail&&(h.customerEmail=e.customerEmail),e.customerName&&(h.customerName=e.customerName),e.externalCustomerId&&(h.externalCustomerId=e.externalCustomerId);let S=await fetch(`${n}/v1/checkout/sessions`,{method:"POST",headers:o,body:JSON.stringify(h)});if(!S.ok){let C=await S.json().catch(()=>({}));throw console.error("[Recur SDK] Failed to create checkout session:",C),new Error(C.error?.message||C.error||"Failed to create checkout session")}let J=await S.json(),k=g(J);console.log("[Recur SDK] Checkout session created:",k),console.log("[Recur SDK] Redirecting to hosted checkout:",k.url),window.location.href=k.url;return}let m=null;if(d==="modal"){let h=this.createDialogWithSkeleton(e.onClose);r=h.dialog,m=h.container}else if(d==="iframe"){if(m=this.getEmbeddedContainer(e.container),!m)throw new Error("Container is required for iframe mode");m.innerHTML="";let h=document.createElement("recur-payment-form-skeleton");m.appendChild(h)}console.log("[Recur SDK] Step 1: Creating embedded checkout...");let b={customerName:e.customerName,customerEmail:e.customerEmail};i&&(b.productId=i),s&&(b.productSlug=s),e.externalCustomerId&&(b.externalCustomerId=e.externalCustomerId),e.successUrl&&(b.successUrl=e.successUrl),e.cancelUrl&&(b.cancelUrl=e.cancelUrl);let X=await fetch(`${n}/v1/checkouts`,{method:"POST",headers:o,body:JSON.stringify(b)});if(!X.ok){let h=await X.json().catch(()=>({}));console.error("[Recur SDK] Failed to create checkout:",h);let S=h.details||h.error||"Failed to create checkout";throw new Error(S)}let Ce=await X.json(),l=g(Ce);if(console.log("[Recur SDK] Checkout created successfully:",l),e.onSuccess?.(l),console.log("[Recur SDK] Step 2: Extracting SDK token..."),!l.sdkToken)throw new Error("SDK token missing from checkout response");console.log("[Recur SDK] Step 3: Loading PAYUNi SDK...");let W=!l.livemode;if(console.log("[Recur SDK] Environment:",W?"SANDBOX":"PRODUCTION"),await Ee(W),console.log("[Recur SDK] PAYUNi SDK loaded successfully"),!window.UniPayment)throw new Error("PAYUNi SDK failed to load");if(console.log("[Recur SDK] Step 4: Rendering payment form..."),!m)throw new Error("Payment container not available");m.innerHTML="";let p=document.createElement("recur-payment-form");if(p.setAttribute("container-id",m.id||"recur-payment-container"),e.customerName&&p.setAttribute("customer-name",e.customerName),e.customerEmail&&p.setAttribute("customer-email",e.customerEmail),l.product?.name&&p.setAttribute("product-name",l.product.name),l.checkout?.amount&&p.setAttribute("amount",l.checkout.amount.toString()),l.product?.interval&&p.setAttribute("interval",l.product.interval),p.setAttribute("custom-styles",`
1548
1548
  .form-input-focus {
1549
1549
  border-color: var(--ring, hsl(215 16% 47%)) !important;
1550
1550
  outline: 0 !important;
1551
1551
  box-shadow: 0 0 0 3px color-mix(in oklch, var(--ring, hsl(215 16% 47%)) 50%, transparent) !important;
1552
1552
  transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out !important;
1553
1553
  }
1554
- `),m.appendChild(p),await new Promise(h=>setTimeout(h,100)),console.log("[Recur SDK] Step 5: Initializing PAYUNi SDK..."),await p.initializePayment(d.sdkToken,W?"SANDBOX":"PRODUCTION")===null){console.log("[Recur SDK] Initialization aborted");return}console.log("[Recur SDK] PAYUNi SDK initialized successfully"),console.log("[Recur SDK] Step 6: Setting up submit handler..."),p.addEventListener("submit",(async h=>{console.log("[Recur SDK] Form submitted");let S=h,{paymentSession:J}=S.detail;try{let k=await J.getTradeResult();console.log("[Recur SDK] Trade result received"),console.log("[Recur SDK] Executing payment...");let C=k.HashTimestamp||k.timestamp,Q={};if(d.checkout.productType==="SUBSCRIPTION"){let E=d.creditToken,y=d.sdkTimestamp;if(!E)throw console.error("[Recur SDK] Missing creditToken from checkout for subscription"),new Error("Missing creditToken for subscription payment");Q={creditToken:E,timestamp:y||C},console.log("[Recur SDK] Using creditToken from checkout:",E.substring(0,30)+"..."),console.log("[Recur SDK] Using timestamp:",y?"from checkout (sdkTimestamp)":"from tradeResult")}let G=await fetch(`${n}/v1/checkouts/${d.checkout.id}/pay`,{method:"POST",headers:o,body:JSON.stringify(Q)});if(!G.ok){let E=await G.json().catch(()=>({}));throw new Error(E.error||"Failed to execute payment")}let Ie=await G.json(),u=g(Ie);if(console.log("[Recur SDK] Payment executed:",u),u.requires3D&&u.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=u.redirectUrl;return}console.log("[Recur SDK] Using popup for 3D verification");{let y=window.open(u.redirectUrl,"recur_3d_verification","width=500,height=700,scrollbars=yes,resizable=yes");if(!y){console.log("[Recur SDK] Popup was blocked (null), falling back to redirect"),window.location.href=u.redirectUrl;return}if(await new Promise(R=>setTimeout(R,100)),y.closed){console.log("[Recur SDK] Popup was closed immediately, falling back to redirect"),window.location.href=u.redirectUrl;return}p.setVerifying?.(!0);let M=d.checkout.id,ee=d.checkout.clientSecret,Te=90,Re=2e3,T=!0,te=!1,re;console.log("[Recur SDK] Starting 3D verification polling...");for(let R=0;R<Te&&T;R++){if(y.closed){console.log("[Recur SDK] Popup was closed");try{let f=await fetch(`${n}/v1/checkouts/${M}/status?client_secret=${encodeURIComponent(ee)}`,{headers:o});if(f.ok){let P=await f.json();if(P.checkout?.status==="SUCCEEDED"){console.log("[Recur SDK] Payment succeeded after popup close"),T=!1,te=!0,re=P.checkout?.orderId;break}}}catch(f){console.error("[Recur SDK] Final status check failed:",f)}throw console.log("[Recur SDK] Payment not confirmed, user closed popup"),p.setVerifying?.(!1),p.resetButton?.(),new Error("3D \u9A57\u8B49\u5DF2\u53D6\u6D88")}try{let f=await fetch(`${n}/v1/checkouts/${M}/status?client_secret=${encodeURIComponent(ee)}`,{headers:o});if(f.ok){let P=await f.json(),_=P.checkout?.status;if(console.log(`[Recur SDK] Poll ${R+1}: status = ${_}`),_==="SUCCEEDED"){console.log("[Recur SDK] Payment succeeded"),T=!1;try{y.close()}catch{}p.setVerifying?.(!1),e.onPaymentComplete&&(u.subscription?e.onPaymentComplete({id:u.subscription.id,status:"ACTIVE",productId:d.checkout.productId,amount:d.checkout.amount,billingPeriod:u.subscription.billingPeriod,currentPeriodStart:u.subscription.currentPeriodStart,currentPeriodEnd:u.subscription.currentPeriodEnd}):e.onPaymentComplete({id:P.checkout?.orderId||M,status:"SUCCEEDED",productId:d.checkout.productId,amount:d.checkout.amount,billingPeriod:d.checkout.productType})),r&&(r.close(),r.remove());return}if(_==="CANCELED"||_==="FAILED"){console.log("[Recur SDK] Payment failed or canceled"),T=!1;try{y.close()}catch{}throw p.setVerifying?.(!1),p.resetButton?.(),new Error("\u4ED8\u6B3E\u5931\u6557\u6216\u5DF2\u53D6\u6D88")}}}catch(f){if(f.message==="3D \u9A57\u8B49\u5DF2\u53D6\u6D88"||f.message==="\u4ED8\u6B3E\u5931\u6557\u6216\u5DF2\u53D6\u6D88")throw f;console.error("[Recur SDK] Poll error:",f)}await new Promise(f=>setTimeout(f,Re))}if(T){console.log("[Recur SDK] 3D verification polling timeout");try{y.close()}catch{}throw p.setVerifying?.(!1),p.resetButton?.(),new Error("3D \u9A57\u8B49\u903E\u6642\uFF0C\u8ACB\u91CD\u8A66")}te&&(console.log("[Recur SDK] Handling success after popup close"),p.setVerifying?.(!1),e.onPaymentComplete&&(u.subscription?e.onPaymentComplete({id:u.subscription.id,status:"ACTIVE",productId:d.checkout.productId,amount:d.checkout.amount,billingPeriod:u.subscription.billingPeriod,currentPeriodStart:u.subscription.currentPeriodStart,currentPeriodEnd:u.subscription.currentPeriodEnd}):e.onPaymentComplete({id:re||M,status:"SUCCEEDED",productId:d.checkout.productId,amount:d.checkout.amount,billingPeriod:d.checkout.productType})),r&&(r.close(),r.remove()));return}}if(e.onPaymentComplete)if(u.subscription)e.onPaymentComplete({id:u.subscription.id,status:u.subscription.status,productId:d.checkout.productId,amount:d.checkout.amount,billingPeriod:u.subscription.billingPeriod,currentPeriodStart:u.subscription.currentPeriodStart,currentPeriodEnd:u.subscription.currentPeriodEnd});else{let E=u.charge?.id||u.paymentIntent?.id,y=u.charge?.status||"SUCCEEDED";e.onPaymentComplete({id:E,status:y,productId:d.checkout.productId,amount:d.checkout.amount,billingPeriod:d.checkout.productType})}console.log("[Recur SDK] Checkout flow completed successfully!"),p.resetButton?.(),r&&(r.close(),r.remove())}catch(k){console.error("[Recur SDK] Payment error:",k);let C={code:"PAYMENT_FAILED",message:k instanceof Error?k.message:"Payment failed"};e.onError?.(C),p.resetButton?.()}})),console.log("[Recur SDK] Checkout flow initialized, waiting for user input...")}catch(n){console.error("[Recur SDK] Checkout error:",n),r&&(r.close(),r.remove());let o={code:"CHECKOUT_ERROR",message:n instanceof Error?n.message:"An unknown error occurred"};throw e.onError?.(o),n}}getBaseUrl(){let e=this.core.getConfig();if(e.baseUrl)return e.baseUrl;if(typeof window<"u"){let t=window.location.hostname;if(t==="localhost"||t.includes(".test")||t.includes(".local")||t==="127.0.0.1")return`${window.location.protocol}//${window.location.host}/api`}return"https://api.recur.tw"}injectDialogStyles(){if(document.getElementById("recur-dialog-styles"))return;let e=document.createElement("style");e.id="recur-dialog-styles",e.textContent=`
1554
+ `),m.appendChild(p),await new Promise(h=>setTimeout(h,100)),console.log("[Recur SDK] Step 5: Initializing PAYUNi SDK..."),await p.initializePayment(l.sdkToken,W?"SANDBOX":"PRODUCTION")===null){console.log("[Recur SDK] Initialization aborted");return}console.log("[Recur SDK] PAYUNi SDK initialized successfully"),console.log("[Recur SDK] Step 6: Setting up submit handler..."),p.addEventListener("submit",(async h=>{console.log("[Recur SDK] Form submitted");let S=h,{paymentSession:J}=S.detail;try{let k=await J.getTradeResult();console.log("[Recur SDK] Trade result received"),console.log("[Recur SDK] Executing payment...");let C=k.HashTimestamp||k.timestamp,Q={};if(l.checkout.productType==="SUBSCRIPTION"){let E=l.creditToken,y=l.sdkTimestamp;if(!E)throw console.error("[Recur SDK] Missing creditToken from checkout for subscription"),new Error("Missing creditToken for subscription payment");Q={creditToken:E,timestamp:y||C},console.log("[Recur SDK] Using creditToken from checkout:",E.substring(0,30)+"..."),console.log("[Recur SDK] Using timestamp:",y?"from checkout (sdkTimestamp)":"from tradeResult")}let G=await fetch(`${n}/v1/checkouts/${l.checkout.id}/pay`,{method:"POST",headers:o,body:JSON.stringify(Q)});if(!G.ok){let E=await G.json().catch(()=>({}));throw new Error(E.error||"Failed to execute payment")}let Ie=await G.json(),u=g(Ie);if(console.log("[Recur SDK] Payment executed:",u),u.requires3D&&u.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=u.redirectUrl;return}console.log("[Recur SDK] Using popup for 3D verification");{let y=window.open(u.redirectUrl,"recur_3d_verification","width=500,height=700,scrollbars=yes,resizable=yes");if(!y){console.log("[Recur SDK] Popup was blocked (null), falling back to redirect"),window.location.href=u.redirectUrl;return}if(await new Promise(R=>setTimeout(R,100)),y.closed){console.log("[Recur SDK] Popup was closed immediately, falling back to redirect"),window.location.href=u.redirectUrl;return}p.setVerifying?.(!0);let M=l.checkout.id,ee=l.checkout.clientSecret,Te=90,Re=2e3,T=!0,te=!1,re;console.log("[Recur SDK] Starting 3D verification polling...");for(let R=0;R<Te&&T;R++){if(y.closed){console.log("[Recur SDK] Popup was closed");try{let f=await fetch(`${n}/v1/checkouts/${M}/status?client_secret=${encodeURIComponent(ee)}`,{headers:o});if(f.ok){let P=await f.json();if(P.checkout?.status==="SUCCEEDED"){console.log("[Recur SDK] Payment succeeded after popup close"),T=!1,te=!0,re=P.checkout?.orderId;break}}}catch(f){console.error("[Recur SDK] Final status check failed:",f)}throw console.log("[Recur SDK] Payment not confirmed, user closed popup"),p.setVerifying?.(!1),p.resetButton?.(),new Error("3D \u9A57\u8B49\u5DF2\u53D6\u6D88")}try{let f=await fetch(`${n}/v1/checkouts/${M}/status?client_secret=${encodeURIComponent(ee)}`,{headers:o});if(f.ok){let P=await f.json(),_=P.checkout?.status;if(console.log(`[Recur SDK] Poll ${R+1}: status = ${_}`),_==="SUCCEEDED"){console.log("[Recur SDK] Payment succeeded"),T=!1;try{y.close()}catch{}p.setVerifying?.(!1),e.onPaymentComplete&&(u.subscription?e.onPaymentComplete({id:u.subscription.id,status:"ACTIVE",productId:l.checkout.productId,amount:l.checkout.amount,billingPeriod:u.subscription.billingPeriod,currentPeriodStart:u.subscription.currentPeriodStart,currentPeriodEnd:u.subscription.currentPeriodEnd}):e.onPaymentComplete({id:P.checkout?.orderId||M,status:"SUCCEEDED",productId:l.checkout.productId,amount:l.checkout.amount,billingPeriod:l.checkout.productType})),r&&(r.close(),r.remove());return}if(_==="CANCELED"||_==="FAILED"){console.log("[Recur SDK] Payment failed or canceled"),T=!1;try{y.close()}catch{}throw p.setVerifying?.(!1),p.resetButton?.(),new Error("\u4ED8\u6B3E\u5931\u6557\u6216\u5DF2\u53D6\u6D88")}}}catch(f){if(f.message==="3D \u9A57\u8B49\u5DF2\u53D6\u6D88"||f.message==="\u4ED8\u6B3E\u5931\u6557\u6216\u5DF2\u53D6\u6D88")throw f;console.error("[Recur SDK] Poll error:",f)}await new Promise(f=>setTimeout(f,Re))}if(T){console.log("[Recur SDK] 3D verification polling timeout");try{y.close()}catch{}throw p.setVerifying?.(!1),p.resetButton?.(),new Error("3D \u9A57\u8B49\u903E\u6642\uFF0C\u8ACB\u91CD\u8A66")}te&&(console.log("[Recur SDK] Handling success after popup close"),p.setVerifying?.(!1),e.onPaymentComplete&&(u.subscription?e.onPaymentComplete({id:u.subscription.id,status:"ACTIVE",productId:l.checkout.productId,amount:l.checkout.amount,billingPeriod:u.subscription.billingPeriod,currentPeriodStart:u.subscription.currentPeriodStart,currentPeriodEnd:u.subscription.currentPeriodEnd}):e.onPaymentComplete({id:re||M,status:"SUCCEEDED",productId:l.checkout.productId,amount:l.checkout.amount,billingPeriod:l.checkout.productType})),r&&(r.close(),r.remove()));return}}if(e.onPaymentComplete)if(u.subscription)e.onPaymentComplete({id:u.subscription.id,status:u.subscription.status,productId:l.checkout.productId,amount:l.checkout.amount,billingPeriod:u.subscription.billingPeriod,currentPeriodStart:u.subscription.currentPeriodStart,currentPeriodEnd:u.subscription.currentPeriodEnd});else{let E=u.charge?.id||u.paymentIntent?.id,y=u.charge?.status||"SUCCEEDED";e.onPaymentComplete({id:E,status:y,productId:l.checkout.productId,amount:l.checkout.amount,billingPeriod:l.checkout.productType})}console.log("[Recur SDK] Checkout flow completed successfully!"),p.resetButton?.(),r&&(r.close(),r.remove())}catch(k){console.error("[Recur SDK] Payment error:",k);let C={code:"PAYMENT_FAILED",message:k instanceof Error?k.message:"Payment failed"};e.onError?.(C),p.resetButton?.()}})),console.log("[Recur SDK] Checkout flow initialized, waiting for user input...")}catch(n){console.error("[Recur SDK] Checkout error:",n),r&&(r.close(),r.remove());let o={code:"CHECKOUT_ERROR",message:n instanceof Error?n.message:"An unknown error occurred"};throw e.onError?.(o),n}}getBaseUrl(){let e=this.core.getConfig();if(e.baseUrl)return e.baseUrl;if(typeof window<"u"){let t=window.location.hostname;if(t==="localhost"||t.includes(".test")||t.includes(".local")||t==="127.0.0.1")return`${window.location.protocol}//${window.location.host}/api`}return"https://api.recur.tw"}injectDialogStyles(){if(document.getElementById("recur-dialog-styles"))return;let e=document.createElement("style");e.id="recur-dialog-styles",e.textContent=`
1555
1555
  .recur-dialog {
1556
1556
  border: none;
1557
1557
  border-radius: 12px;
@@ -1607,7 +1607,7 @@
1607
1607
  .recur-dialog__close:hover {
1608
1608
  background: rgba(0, 0, 0, 0.1);
1609
1609
  }
1610
- `,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 i=document.createElement("button");i.className="recur-dialog__close",i.innerHTML="\u2715",i.type="button",i.onclick=()=>{this.closeDialog(),e?.()};let s=document.createElement("div");s.id="recur-dialog-payment-container";let n=document.createElement("recur-payment-form-skeleton");return s.appendChild(n),r.appendChild(i),r.appendChild(s),t.appendChild(r),document.body.appendChild(t),t.showModal(),t.addEventListener("click",o=>{let l=t.getBoundingClientRect();o.clientX>=l.left&&o.clientX<=l.right&&o.clientY>=l.top&&o.clientY<=l.bottom||(this.closeDialog(),e?.())}),t.addEventListener("cancel",o=>{o.preventDefault(),this.closeDialog(),e?.()}),this.currentDialogElement=t,{dialog:t,container:s}}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 s=await r.json().catch(()=>({}));throw new Error(s.error?.message||s.message||"Failed to create portal session")}let i=await r.json();return{id:i.id,url:i.url||i.portalUrl,expiresAt:i.expiresAt}}async redirectToPortal(e,t){let r=await this.createPortalSession(e,t);window.location.href=r.url}};function Se(a){return new L(a)}var Oe={init:Se,RecurCheckout:L,RecurElements:I,createElements:Z};return _e(je);})();
1610
+ `,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 i=document.createElement("button");i.className="recur-dialog__close",i.innerHTML="\u2715",i.type="button",i.onclick=()=>{this.closeDialog(),e?.()};let s=document.createElement("div");s.id="recur-dialog-payment-container";let n=document.createElement("recur-payment-form-skeleton");return s.appendChild(n),r.appendChild(i),r.appendChild(s),t.appendChild(r),document.body.appendChild(t),t.showModal(),t.addEventListener("cancel",o=>{o.preventDefault(),this.closeDialog(),e?.()}),this.currentDialogElement=t,{dialog:t,container:s}}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 s=await r.json().catch(()=>({}));throw new Error(s.error?.message||s.message||"Failed to create portal session")}let i=await r.json();return{id:i.id,url:i.url||i.portalUrl,expiresAt:i.expiresAt}}async redirectToPortal(e,t){let r=await this.createPortalSession(e,t);window.location.href=r.url}};function Se(a){return new L(a)}var Oe={init:Se,RecurCheckout:L,RecurElements:I,createElements:Z};return _e(je);})();
1611
1611
  if (typeof window !== "undefined") {
1612
1612
  window.RecurCheckout = RecurCheckout.default;
1613
1613
  window.RecurElements = RecurCheckout.RecurElements;
package/dist/server.cjs CHANGED
@@ -20,7 +20,7 @@ var RecurAPIError = class extends Error {
20
20
 
21
21
  // package.json
22
22
  var package_default = {
23
- version: "0.9.7"};
23
+ version: "0.9.8"};
24
24
 
25
25
  // src/server/resources/portal.ts
26
26
  var SDK_VERSION = package_default.version;
@@ -0,0 +1,194 @@
1
+ /**
2
+ * Recur Server SDK Types
3
+ */
4
+ interface RecurConfig {
5
+ /**
6
+ * Secret API key for authentication
7
+ * Get this from your organization settings
8
+ *
9
+ * @example 'sk_test_abc123...' or 'sk_live_xyz789...'
10
+ */
11
+ secretKey: string;
12
+ /**
13
+ * Base URL for API calls
14
+ * Defaults to 'https://api.recur.tw'
15
+ */
16
+ baseUrl?: string;
17
+ }
18
+ /**
19
+ * Parameters for creating a portal session
20
+ *
21
+ * Customer Identification (at least one required):
22
+ * - customer: Internal customer ID in Recur (highest priority)
23
+ * - externalId: Customer's external ID from your system
24
+ * - email: Customer's email address (lowest priority)
25
+ */
26
+ interface PortalSessionCreateParams {
27
+ /**
28
+ * The internal ID of the customer to create a portal session for
29
+ * Takes highest priority when multiple identifiers are provided
30
+ */
31
+ customer?: string;
32
+ /**
33
+ * Customer's email address
34
+ * Used to identify the customer if `customer` and `externalId` are not provided
35
+ */
36
+ email?: string;
37
+ /**
38
+ * Customer's external ID from your system
39
+ * Used to identify the customer if `customer` is not provided
40
+ */
41
+ externalId?: string;
42
+ /**
43
+ * The URL to redirect the customer to when they exit the portal
44
+ * If not provided, uses the default return URL from portal configuration
45
+ */
46
+ returnUrl?: string;
47
+ /**
48
+ * The ID of a specific portal configuration to use
49
+ * If not provided, uses the default configuration
50
+ */
51
+ configuration?: string;
52
+ /**
53
+ * Preferred locale for the portal
54
+ */
55
+ locale?: 'zh-TW' | 'en';
56
+ }
57
+ interface PortalSession {
58
+ /**
59
+ * Unique identifier for the portal session
60
+ */
61
+ id: string;
62
+ /**
63
+ * String representing the object's type
64
+ */
65
+ object: 'portal.session';
66
+ /**
67
+ * The URL to redirect the customer to access the portal
68
+ */
69
+ url: string;
70
+ /**
71
+ * The ID of the customer for this session
72
+ */
73
+ customer: string;
74
+ /**
75
+ * The URL to redirect the customer when they exit the portal
76
+ */
77
+ returnUrl: string;
78
+ /**
79
+ * The status of the session
80
+ */
81
+ status: 'active' | 'expired';
82
+ /**
83
+ * Time at which the session expires (ISO 8601)
84
+ */
85
+ expiresAt: string;
86
+ /**
87
+ * Time at which the session was last accessed (ISO 8601), or null
88
+ */
89
+ accessedAt: string | null;
90
+ /**
91
+ * Time at which the session was created (ISO 8601)
92
+ */
93
+ createdAt: string;
94
+ }
95
+ interface RecurError {
96
+ type: 'authentication_error' | 'invalid_request_error' | 'api_error';
97
+ code: string;
98
+ message: string;
99
+ details?: unknown;
100
+ }
101
+ declare class RecurAPIError extends Error {
102
+ readonly type: string;
103
+ readonly code: string;
104
+ readonly statusCode: number;
105
+ constructor(error: RecurError, statusCode: number);
106
+ }
107
+
108
+ /**
109
+ * Portal Sessions Resource
110
+ *
111
+ * Create portal sessions to allow customers to manage their subscriptions
112
+ */
113
+
114
+ declare class PortalSessions {
115
+ private config;
116
+ constructor(config: RecurConfig);
117
+ /**
118
+ * Create a portal session for a customer
119
+ *
120
+ * Customer can be identified using one of:
121
+ * - `customer`: Internal customer ID (highest priority)
122
+ * - `externalId`: External customer ID from your system
123
+ * - `email`: Customer's email address (lowest priority)
124
+ *
125
+ * @param params - Portal session creation parameters
126
+ * @returns The created portal session with URL
127
+ *
128
+ * @example
129
+ * ```typescript
130
+ * // By customer ID
131
+ * const session = await recur.portal.sessions.create({
132
+ * customer: 'cus_xxx',
133
+ * returnUrl: 'https://myapp.com/account',
134
+ * });
135
+ *
136
+ * // By email
137
+ * const session = await recur.portal.sessions.create({
138
+ * email: 'customer@example.com',
139
+ * returnUrl: 'https://myapp.com/account',
140
+ * });
141
+ *
142
+ * // By external ID
143
+ * const session = await recur.portal.sessions.create({
144
+ * externalId: 'user_123',
145
+ * returnUrl: 'https://myapp.com/account',
146
+ * });
147
+ *
148
+ * // Redirect the customer to the portal
149
+ * redirect(session.url);
150
+ * ```
151
+ */
152
+ create(params: PortalSessionCreateParams): Promise<PortalSession>;
153
+ }
154
+ declare class Portal {
155
+ readonly sessions: PortalSessions;
156
+ constructor(config: RecurConfig);
157
+ }
158
+
159
+ /**
160
+ * Recur Server SDK
161
+ *
162
+ * Server-side SDK for interacting with the Recur API
163
+ * Use this in your backend (API routes, server actions, etc.)
164
+ *
165
+ * @example
166
+ * ```typescript
167
+ * import { Recur } from 'recur-tw/server';
168
+ *
169
+ * const recur = new Recur(process.env.RECUR_SECRET_KEY!);
170
+ *
171
+ * // Create a portal session
172
+ * const session = await recur.portal.sessions.create({
173
+ * customer: 'cus_xxx',
174
+ * returnUrl: 'https://myapp.com/account',
175
+ * });
176
+ * ```
177
+ */
178
+
179
+ declare class Recur {
180
+ private config;
181
+ /**
182
+ * Portal resource for managing customer portal sessions
183
+ */
184
+ readonly portal: Portal;
185
+ /**
186
+ * Create a new Recur client
187
+ *
188
+ * @param secretKey - Your Recur secret API key (sk_test_xxx or sk_live_xxx)
189
+ * @param options - Additional configuration options
190
+ */
191
+ constructor(secretKey: string, options?: Omit<RecurConfig, 'secretKey'>);
192
+ }
193
+
194
+ export { type PortalSession, type PortalSessionCreateParams, Recur, RecurAPIError, type RecurConfig, type RecurError };
@@ -0,0 +1,194 @@
1
+ /**
2
+ * Recur Server SDK Types
3
+ */
4
+ interface RecurConfig {
5
+ /**
6
+ * Secret API key for authentication
7
+ * Get this from your organization settings
8
+ *
9
+ * @example 'sk_test_abc123...' or 'sk_live_xyz789...'
10
+ */
11
+ secretKey: string;
12
+ /**
13
+ * Base URL for API calls
14
+ * Defaults to 'https://api.recur.tw'
15
+ */
16
+ baseUrl?: string;
17
+ }
18
+ /**
19
+ * Parameters for creating a portal session
20
+ *
21
+ * Customer Identification (at least one required):
22
+ * - customer: Internal customer ID in Recur (highest priority)
23
+ * - externalId: Customer's external ID from your system
24
+ * - email: Customer's email address (lowest priority)
25
+ */
26
+ interface PortalSessionCreateParams {
27
+ /**
28
+ * The internal ID of the customer to create a portal session for
29
+ * Takes highest priority when multiple identifiers are provided
30
+ */
31
+ customer?: string;
32
+ /**
33
+ * Customer's email address
34
+ * Used to identify the customer if `customer` and `externalId` are not provided
35
+ */
36
+ email?: string;
37
+ /**
38
+ * Customer's external ID from your system
39
+ * Used to identify the customer if `customer` is not provided
40
+ */
41
+ externalId?: string;
42
+ /**
43
+ * The URL to redirect the customer to when they exit the portal
44
+ * If not provided, uses the default return URL from portal configuration
45
+ */
46
+ returnUrl?: string;
47
+ /**
48
+ * The ID of a specific portal configuration to use
49
+ * If not provided, uses the default configuration
50
+ */
51
+ configuration?: string;
52
+ /**
53
+ * Preferred locale for the portal
54
+ */
55
+ locale?: 'zh-TW' | 'en';
56
+ }
57
+ interface PortalSession {
58
+ /**
59
+ * Unique identifier for the portal session
60
+ */
61
+ id: string;
62
+ /**
63
+ * String representing the object's type
64
+ */
65
+ object: 'portal.session';
66
+ /**
67
+ * The URL to redirect the customer to access the portal
68
+ */
69
+ url: string;
70
+ /**
71
+ * The ID of the customer for this session
72
+ */
73
+ customer: string;
74
+ /**
75
+ * The URL to redirect the customer when they exit the portal
76
+ */
77
+ returnUrl: string;
78
+ /**
79
+ * The status of the session
80
+ */
81
+ status: 'active' | 'expired';
82
+ /**
83
+ * Time at which the session expires (ISO 8601)
84
+ */
85
+ expiresAt: string;
86
+ /**
87
+ * Time at which the session was last accessed (ISO 8601), or null
88
+ */
89
+ accessedAt: string | null;
90
+ /**
91
+ * Time at which the session was created (ISO 8601)
92
+ */
93
+ createdAt: string;
94
+ }
95
+ interface RecurError {
96
+ type: 'authentication_error' | 'invalid_request_error' | 'api_error';
97
+ code: string;
98
+ message: string;
99
+ details?: unknown;
100
+ }
101
+ declare class RecurAPIError extends Error {
102
+ readonly type: string;
103
+ readonly code: string;
104
+ readonly statusCode: number;
105
+ constructor(error: RecurError, statusCode: number);
106
+ }
107
+
108
+ /**
109
+ * Portal Sessions Resource
110
+ *
111
+ * Create portal sessions to allow customers to manage their subscriptions
112
+ */
113
+
114
+ declare class PortalSessions {
115
+ private config;
116
+ constructor(config: RecurConfig);
117
+ /**
118
+ * Create a portal session for a customer
119
+ *
120
+ * Customer can be identified using one of:
121
+ * - `customer`: Internal customer ID (highest priority)
122
+ * - `externalId`: External customer ID from your system
123
+ * - `email`: Customer's email address (lowest priority)
124
+ *
125
+ * @param params - Portal session creation parameters
126
+ * @returns The created portal session with URL
127
+ *
128
+ * @example
129
+ * ```typescript
130
+ * // By customer ID
131
+ * const session = await recur.portal.sessions.create({
132
+ * customer: 'cus_xxx',
133
+ * returnUrl: 'https://myapp.com/account',
134
+ * });
135
+ *
136
+ * // By email
137
+ * const session = await recur.portal.sessions.create({
138
+ * email: 'customer@example.com',
139
+ * returnUrl: 'https://myapp.com/account',
140
+ * });
141
+ *
142
+ * // By external ID
143
+ * const session = await recur.portal.sessions.create({
144
+ * externalId: 'user_123',
145
+ * returnUrl: 'https://myapp.com/account',
146
+ * });
147
+ *
148
+ * // Redirect the customer to the portal
149
+ * redirect(session.url);
150
+ * ```
151
+ */
152
+ create(params: PortalSessionCreateParams): Promise<PortalSession>;
153
+ }
154
+ declare class Portal {
155
+ readonly sessions: PortalSessions;
156
+ constructor(config: RecurConfig);
157
+ }
158
+
159
+ /**
160
+ * Recur Server SDK
161
+ *
162
+ * Server-side SDK for interacting with the Recur API
163
+ * Use this in your backend (API routes, server actions, etc.)
164
+ *
165
+ * @example
166
+ * ```typescript
167
+ * import { Recur } from 'recur-tw/server';
168
+ *
169
+ * const recur = new Recur(process.env.RECUR_SECRET_KEY!);
170
+ *
171
+ * // Create a portal session
172
+ * const session = await recur.portal.sessions.create({
173
+ * customer: 'cus_xxx',
174
+ * returnUrl: 'https://myapp.com/account',
175
+ * });
176
+ * ```
177
+ */
178
+
179
+ declare class Recur {
180
+ private config;
181
+ /**
182
+ * Portal resource for managing customer portal sessions
183
+ */
184
+ readonly portal: Portal;
185
+ /**
186
+ * Create a new Recur client
187
+ *
188
+ * @param secretKey - Your Recur secret API key (sk_test_xxx or sk_live_xxx)
189
+ * @param options - Additional configuration options
190
+ */
191
+ constructor(secretKey: string, options?: Omit<RecurConfig, 'secretKey'>);
192
+ }
193
+
194
+ export { type PortalSession, type PortalSessionCreateParams, Recur, RecurAPIError, type RecurConfig, type RecurError };
package/dist/server.js CHANGED
@@ -18,7 +18,7 @@ var RecurAPIError = class extends Error {
18
18
 
19
19
  // package.json
20
20
  var package_default = {
21
- version: "0.9.7"};
21
+ version: "0.9.8"};
22
22
 
23
23
  // src/server/resources/portal.ts
24
24
  var SDK_VERSION = package_default.version;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "recur-tw",
3
- "version": "0.9.7",
3
+ "version": "0.9.8",
4
4
  "description": "React & Vanilla JS SDK for embedding subscription checkout flows (Taiwan / PAYUNi)",
5
5
  "type": "module",
6
6
  "private": false,