recur-tw 0.0.4-beta.2 → 0.0.4-beta.4
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/recur.umd.js +4 -4
- package/dist/recur.umd.js.map +1 -1
- package/package.json +1 -1
package/dist/recur.umd.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"use strict";var RecurCheckout=(()=>{var y=Object.defineProperty;var Y=Object.getOwnPropertyDescriptor;var q=Object.getOwnPropertyNames;var X=Object.prototype.hasOwnProperty;var W=(a,e,t)=>e in a?y(a,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):a[e]=t;var p=(a,e)=>()=>(a&&(e=a(a=0)),e);var m=(a,e)=>{for(var t in e)y(a,t,{get:e[t],enumerable:!0})},J=(a,e,t,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of q(e))!X.call(a,i)&&i!==t&&y(a,i,{get:()=>e[i],enumerable:!(r=Y(e,i))||r.enumerable});return a};var Z=a=>J(y({},"__esModule",{value:!0}),a);var o=(a,e,t)=>W(a,typeof e!="symbol"?e+"":e,t);var
|
|
1
|
+
"use strict";var RecurCheckout=(()=>{var y=Object.defineProperty;var Y=Object.getOwnPropertyDescriptor;var q=Object.getOwnPropertyNames;var X=Object.prototype.hasOwnProperty;var W=(a,e,t)=>e in a?y(a,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):a[e]=t;var p=(a,e)=>()=>(a&&(e=a(a=0)),e);var m=(a,e)=>{for(var t in e)y(a,t,{get:e[t],enumerable:!0})},J=(a,e,t,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of q(e))!X.call(a,i)&&i!==t&&y(a,i,{get:()=>e[i],enumerable:!(r=Y(e,i))||r.enumerable});return a};var Z=a=>J(y({},"__esModule",{value:!0}),a);var o=(a,e,t)=>W(a,typeof e!="symbol"?e+"":e,t);var P={};m(P,{RecurLoadingSpinner:()=>k});var k,H=p(()=>{"use strict";k=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;
|
|
@@ -1138,10 +1138,10 @@
|
|
|
1138
1138
|
>
|
|
1139
1139
|
<span id="${this.containerId}-submit-text">\u78BA\u8A8D\u4ED8\u6B3E</span>
|
|
1140
1140
|
</button>
|
|
1141
|
-
`,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`),l=document.getElementById(`${this.containerId}-card-cvc`);if(!i||!s||!l)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 n=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 n.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):(n.onUpdate?.(d=>{if(this._initializationAborted){console.log("[PaymentForm] Ignoring onUpdate event - component disconnected");return}console.log("[PaymentForm] PAYUNi update:",d);let c=d.status&&d.status.CardNo===!0&&d.status.CardExp===!0&&d.status.CardCvc===!0,
|
|
1141
|
+
`,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`),l=document.getElementById(`${this.containerId}-card-cvc`);if(!i||!s||!l)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 n=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 n.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):(n.onUpdate?.(d=>{if(this._initializationAborted){console.log("[PaymentForm] Ignoring onUpdate event - component disconnected");return}console.log("[PaymentForm] PAYUNi update:",d);let c=d.status&&d.status.CardNo===!0&&d.status.CardExp===!0&&d.status.CardCvc===!0,_=document.getElementById(`${this.containerId}-submit-btn`);_&&(_.disabled=!c,console.log("[PaymentForm] Submit button disabled:",!c))}),this._initializationAborted?(console.log("[PaymentForm] Initialization aborted before saving session"),this._isInitializing=!1,null):(this._paymentSession=n,this.setupFormSubmission(),this._isInitializing=!1,n))}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,l,n=document.getElementById(`${this.containerId}-email`),d=document.getElementById(`${this.containerId}-name`),c=document.getElementById(`${this.containerId}-phone`);if(n&&d){if(i=n.value,s=d.value,l=c?.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,customerPhone:l,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=`
|
|
1142
1142
|
<span class="recur-loading-spinner"></span>
|
|
1143
1143
|
<span>\u8655\u7406\u4E2D...</span>
|
|
1144
|
-
`):(r.classList.remove("loading"),r.disabled=!1,r.innerHTML='<span id="'+this.containerId+'-submit-text">\u78BA\u8A8D\u4ED8\u6B3E</span>'))}resetButton(){this.setButtonLoading(!1)}showError(t){let r=this.querySelector(".recur-sdk__error-container");r||(r=document.createElement("div"),r.className="recur-sdk__error-container",r.style.cssText="margin-bottom: 16px;",this.insertBefore(r,this.firstChild));let i=document.createElement("recur-error-display");i.setAttribute("error",t),i.setAttribute("dismissible","true"),r.innerHTML="",r.appendChild(i),i.scrollIntoView({behavior:"smooth",block:"center"})}clearError(){let t=this.querySelector(".recur-sdk__error-container");t&&(t.innerHTML="")}updateComplete(){return new Promise(t=>{if(this.isConnected)setTimeout(t,0);else{let r=new MutationObserver(()=>{this.isConnected&&(r.disconnect(),setTimeout(t,0))});r.observe(document.body,{childList:!0,subtree:!0})}})}getFormData(){return{email:document.getElementById(`${this.containerId}-email`)?.value,name:document.getElementById(`${this.containerId}-name`)?.value,phone:document.getElementById(`${this.containerId}-phone`)?.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)}if(t.phone){let r=document.getElementById(`${this.containerId}-phone`);r&&(r.value=t.phone)}}};customElements.get("recur-payment-form")||customElements.define("recur-payment-form",M)});var ee={};m(ee,{RecurCheckout:()=>v,RecurElements:()=>h,createElements:()=>R,default:()=>Q,init:()=>V});async function G(){if(typeof window>"u"){console.warn("[Recur SDK] Cannot register Web Components: window is not defined");return}await Promise.all([Promise.resolve().then(()=>(H(),
|
|
1144
|
+
`):(r.classList.remove("loading"),r.disabled=!1,r.innerHTML='<span id="'+this.containerId+'-submit-text">\u78BA\u8A8D\u4ED8\u6B3E</span>'))}resetButton(){this.setButtonLoading(!1)}showError(t){let r=this.querySelector(".recur-sdk__error-container");r||(r=document.createElement("div"),r.className="recur-sdk__error-container",r.style.cssText="margin-bottom: 16px;",this.insertBefore(r,this.firstChild));let i=document.createElement("recur-error-display");i.setAttribute("error",t),i.setAttribute("dismissible","true"),r.innerHTML="",r.appendChild(i),i.scrollIntoView({behavior:"smooth",block:"center"})}clearError(){let t=this.querySelector(".recur-sdk__error-container");t&&(t.innerHTML="")}updateComplete(){return new Promise(t=>{if(this.isConnected)setTimeout(t,0);else{let r=new MutationObserver(()=>{this.isConnected&&(r.disconnect(),setTimeout(t,0))});r.observe(document.body,{childList:!0,subtree:!0})}})}getFormData(){return{email:document.getElementById(`${this.containerId}-email`)?.value,name:document.getElementById(`${this.containerId}-name`)?.value,phone:document.getElementById(`${this.containerId}-phone`)?.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)}if(t.phone){let r=document.getElementById(`${this.containerId}-phone`);r&&(r.value=t.phone)}}};customElements.get("recur-payment-form")||customElements.define("recur-payment-form",M)});var ee={};m(ee,{RecurCheckout:()=>v,RecurElements:()=>h,createElements:()=>R,default:()=>Q,init:()=>V});async function G(){if(typeof window>"u"){console.warn("[Recur SDK] Cannot register Web Components: window is not defined");return}await Promise.all([Promise.resolve().then(()=>(H(),P)),Promise.resolve().then(()=>(U(),$)),Promise.resolve().then(()=>(D(),A)),Promise.resolve().then(()=>(N(),B)),Promise.resolve().then(()=>(O(),F)),Promise.resolve().then(()=>(b(),g)),Promise.resolve().then(()=>(K(),j))]);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"].filter(t=>!customElements.get(t));e.length>0&&console.warn("[Recur SDK] The following components are not registered:",e)}typeof window<"u"&&G();var I=class{constructor(e){o(this,"config");if(!e.publishableKey)throw new Error("publishableKey is required");this.config={baseUrl:"https://api.recur.tw",...e}}async createSubscription(e){let{planId:t,customerName:r,customerEmail:i,customerPhone:s}=e;if(!t)throw new Error("planId is required");let l=await fetch(`${this.config.baseUrl}/v1/subscriptions`,{method:"POST",headers:{"Content-Type":"application/json","X-Recur-Publishable-Key":this.config.publishableKey},body:JSON.stringify({planId:t,customerName:r,customerEmail:i,customerPhone:s})});if(!l.ok){let n=await l.json().catch(()=>({}));throw{code:n.error||"CHECKOUT_FAILED",message:n.message||"Failed to initiate checkout",details:n}}return await l.json()}async fetchPlans(){let e=await fetch(`${this.config.baseUrl}/v1/plans`,{method:"GET",headers:{"Content-Type":"application/json","X-Recur-Publishable-Key":this.config.publishableKey}});if(!e.ok){let t=await e.json().catch(()=>({}));throw{code:t.error||"FETCH_PLANS_FAILED",message:t.message||"Failed to fetch plans",details:t}}return await e.json()}getConfig(){return{...this.config}}};var S=class{constructor(e){o(this,"overlay",null);o(this,"modal",null);o(this,"iframe",null);o(this,"onClose");o(this,"handleMessage",e=>{e.origin===window.location.origin&&(e.data?.type==="RECUR_PAYMENT_COMPLETE"?this.close():e.data?.type==="RECUR_PAYMENT_CANCEL"&&this.close())});this.onClose=e}open(e){this.overlay=document.createElement("div"),this.overlay.style.cssText=`
|
|
1145
1145
|
position: fixed;
|
|
1146
1146
|
top: 0;
|
|
1147
1147
|
left: 0;
|
|
@@ -1269,7 +1269,7 @@
|
|
|
1269
1269
|
</button>
|
|
1270
1270
|
|
|
1271
1271
|
<p style="margin: 12px 0 0 0; text-align: center; font-size: 12px; color: #9ca3af;">
|
|
1272
|
-
Secure payment powered by
|
|
1272
|
+
Secure payment powered by Recur
|
|
1273
1273
|
</p>
|
|
1274
1274
|
</form>
|
|
1275
1275
|
</div>
|
package/dist/recur.umd.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/components/base/loading-spinner.ts","../src/components/base/success-message.ts","../src/components/base/error-display.ts","../src/components/base/skeleton-loader.ts","../src/components/base/payment-form-skeleton.ts","../src/components/base/toast.ts","../src/components/payment-form.ts","../src/vanilla/index.ts","../src/components/index.ts","../src/vanilla/core.ts","../src/vanilla/modal.ts","../src/vanilla/iframe.ts","../src/vanilla/embedded-checkout.ts","../src/vanilla/elements.ts"],"sourcesContent":["/**\n * Recur Loading Spinner Web Component\n *\n * A reusable loading spinner with customizable message and size.\n * Eliminates code duplication across the SDK.\n */\n\nexport class RecurLoadingSpinner extends HTMLElement {\n static get observedAttributes() {\n return ['message', 'size'];\n }\n\n constructor() {\n super();\n this.attachShadow({ mode: 'open' });\n }\n\n connectedCallback() {\n this.render();\n }\n\n attributeChangedCallback(name: string, oldValue: string, newValue: string) {\n if (oldValue !== newValue) {\n this.render();\n }\n }\n\n private get message(): string {\n return this.getAttribute('message') || '正在處理訂閱...';\n }\n\n private get size(): 'small' | 'medium' | 'large' {\n const sizeAttr = this.getAttribute('size');\n if (sizeAttr === 'small' || sizeAttr === 'large') return sizeAttr;\n return 'medium';\n }\n\n private getSizeValue(): number {\n const sizes = {\n small: 24,\n medium: 40,\n large: 56,\n };\n return sizes[this.size];\n }\n\n private render() {\n const size = this.getSizeValue();\n\n this.shadowRoot!.innerHTML = `\n <style>\n :host {\n display: block;\n text-align: center;\n }\n\n @keyframes recur-sdk-spin {\n 0% { transform: rotate(0deg); }\n 100% { transform: rotate(360deg); }\n }\n\n .recur-sdk__spinner {\n border: 3px solid #e5e7eb;\n border-top: 3px solid var(--recur-primary-color, #3b82f6);\n border-radius: 50%;\n width: ${size}px;\n height: ${size}px;\n animation: recur-sdk-spin 1s linear infinite;\n margin: 0 auto 16px;\n }\n\n .recur-sdk__loading-text {\n color: var(--recur-text-secondary, #6b7280);\n font-size: ${this.size === 'small' ? '12px' : '14px'};\n margin: 0;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n }\n\n /* 支持主題化 */\n :host([theme=\"dark\"]) .recur-sdk__spinner {\n border-color: #374151;\n border-top-color: #60a5fa;\n }\n\n :host([theme=\"dark\"]) .recur-sdk__loading-text {\n color: #9ca3af;\n }\n </style>\n\n <div class=\"recur-sdk__spinner\"></div>\n ${this.message ? `<p class=\"recur-sdk__loading-text\">${this.message}</p>` : ''}\n `;\n }\n}\n\n// 自動註冊組件(如果還未註冊)\nif (typeof window !== 'undefined' && !customElements.get('recur-loading-spinner')) {\n customElements.define('recur-loading-spinner', RecurLoadingSpinner);\n}\n\n// TypeScript 類型聲明\ndeclare global {\n interface HTMLElementTagNameMap {\n 'recur-loading-spinner': RecurLoadingSpinner;\n }\n}\n","/**\n * Recur Success Message Web Component\n *\n * A reusable success message display with icon and customizable text.\n */\n\nexport class RecurSuccessMessage extends HTMLElement {\n static get observedAttributes() {\n return ['title', 'message', 'icon'];\n }\n\n constructor() {\n super();\n this.attachShadow({ mode: 'open' });\n }\n\n connectedCallback() {\n this.render();\n }\n\n attributeChangedCallback(name: string, oldValue: string, newValue: string) {\n if (oldValue !== newValue) {\n this.render();\n }\n }\n\n private get successTitle(): string {\n return this.getAttribute('title') || 'Subscription Complete!';\n }\n\n private get successMessage(): string {\n return this.getAttribute('message') || 'Thank you for subscribing. Your payment has been processed successfully.';\n }\n\n private get showIcon(): boolean {\n return this.getAttribute('icon') !== 'false';\n }\n\n private render() {\n this.shadowRoot!.innerHTML = `\n <style>\n :host {\n display: block;\n text-align: center;\n padding: var(--recur-spacing-lg, 48px) var(--recur-spacing-md, 24px);\n }\n\n .recur-sdk__success-container {\n max-width: 400px;\n margin: 0 auto;\n }\n\n .recur-sdk__success-icon {\n width: 64px;\n height: 64px;\n margin: 0 auto 24px;\n background: var(--recur-success-bg, #d1fae5);\n border-radius: 50%;\n display: flex;\n align-items: center;\n justify-content: center;\n }\n\n .recur-sdk__success-icon svg {\n width: 32px;\n height: 32px;\n color: var(--recur-success-color, #10b981);\n }\n\n .recur-sdk__success-title {\n font-size: 24px;\n font-weight: 600;\n color: var(--recur-text-primary, #1f2937);\n margin: 0 0 12px 0;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n }\n\n .recur-sdk__success-message {\n font-size: 16px;\n color: var(--recur-text-secondary, #6b7280);\n margin: 0;\n line-height: 1.5;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n }\n\n /* Slot for custom actions */\n ::slotted(button), ::slotted(a) {\n margin-top: 24px;\n }\n\n /* Dark theme support */\n :host([theme=\"dark\"]) .recur-sdk__success-icon {\n background: #065f46;\n }\n\n :host([theme=\"dark\"]) .recur-sdk__success-icon svg {\n color: #34d399;\n }\n\n :host([theme=\"dark\"]) .recur-sdk__success-title {\n color: #f3f4f6;\n }\n\n :host([theme=\"dark\"]) .recur-sdk__success-message {\n color: #d1d5db;\n }\n </style>\n\n <div class=\"recur-sdk__success-container\">\n ${this.showIcon ? `\n <div class=\"recur-sdk__success-icon\">\n <svg fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M5 13l4 4L19 7\" />\n </svg>\n </div>\n ` : ''}\n <h2 class=\"recur-sdk__success-title\">${this.successTitle}</h2>\n <p class=\"recur-sdk__success-message\">${this.successMessage}</p>\n <slot></slot>\n </div>\n `;\n }\n}\n\n// 自動註冊組件\nif (typeof window !== 'undefined' && !customElements.get('recur-success-message')) {\n customElements.define('recur-success-message', RecurSuccessMessage);\n}\n\n// TypeScript 類型聲明\ndeclare global {\n interface HTMLElementTagNameMap {\n 'recur-success-message': RecurSuccessMessage;\n }\n}\n","/**\n * Recur Error Display Web Component\n *\n * A reusable error message display with customizable styling and dismissible option.\n */\n\nexport class RecurErrorDisplay extends HTMLElement {\n static get observedAttributes() {\n return ['error', 'dismissible'];\n }\n\n constructor() {\n super();\n this.attachShadow({ mode: 'open' });\n }\n\n connectedCallback() {\n this.render();\n }\n\n attributeChangedCallback(name: string, oldValue: string, newValue: string) {\n if (oldValue !== newValue) {\n this.render();\n }\n }\n\n private get error(): string {\n return this.getAttribute('error') || '';\n }\n\n private get isDismissible(): boolean {\n return this.getAttribute('dismissible') === 'true';\n }\n\n private handleDismiss() {\n this.dispatchEvent(new CustomEvent('dismiss', {\n bubbles: true,\n composed: true,\n }));\n this.remove();\n }\n\n private render() {\n if (!this.error) {\n this.shadowRoot!.innerHTML = '';\n return;\n }\n\n this.shadowRoot!.innerHTML = `\n <style>\n :host {\n display: block;\n margin: var(--recur-spacing-sm, 16px) 0;\n }\n\n .recur-sdk__error {\n display: flex;\n align-items: flex-start;\n gap: 12px;\n padding: 12px 16px;\n background: var(--recur-error-bg, #fee2e2);\n border: 1px solid var(--recur-error-border, #fecaca);\n border-radius: var(--recur-border-radius, 6px);\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n }\n\n .recur-sdk__error-icon {\n flex-shrink: 0;\n width: 20px;\n height: 20px;\n color: var(--recur-error-color, #dc2626);\n }\n\n .recur-sdk__error-content {\n flex: 1;\n }\n\n .recur-sdk__error-message {\n margin: 0;\n font-size: 14px;\n color: var(--recur-error-text, #991b1b);\n line-height: 1.5;\n }\n\n .recur-sdk__error-dismiss {\n flex-shrink: 0;\n background: none;\n border: none;\n padding: 0;\n width: 20px;\n height: 20px;\n cursor: pointer;\n color: var(--recur-error-color, #dc2626);\n opacity: 0.7;\n transition: opacity 0.2s;\n }\n\n .recur-sdk__error-dismiss:hover {\n opacity: 1;\n }\n\n .recur-sdk__error-dismiss:focus {\n outline: 2px solid var(--recur-error-color, #dc2626);\n outline-offset: 2px;\n border-radius: 2px;\n }\n\n /* Dark theme support */\n :host([theme=\"dark\"]) .recur-sdk__error {\n background: #7f1d1d;\n border-color: #991b1b;\n }\n\n :host([theme=\"dark\"]) .recur-sdk__error-icon,\n :host([theme=\"dark\"]) .recur-sdk__error-dismiss {\n color: #fca5a5;\n }\n\n :host([theme=\"dark\"]) .recur-sdk__error-message {\n color: #fecaca;\n }\n </style>\n\n <div class=\"recur-sdk__error\" role=\"alert\">\n <svg class=\"recur-sdk__error-icon\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z\" />\n </svg>\n <div class=\"recur-sdk__error-content\">\n <p class=\"recur-sdk__error-message\">${this.error}</p>\n </div>\n ${this.isDismissible ? `\n <button class=\"recur-sdk__error-dismiss\" aria-label=\"Dismiss error\">\n <svg fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M6 18L18 6M6 6l12 12\" />\n </svg>\n </button>\n ` : ''}\n </div>\n `;\n\n if (this.isDismissible) {\n const dismissBtn = this.shadowRoot!.querySelector('.recur-sdk__error-dismiss');\n dismissBtn?.addEventListener('click', () => this.handleDismiss());\n }\n }\n}\n\n// 自動註冊組件\nif (typeof window !== 'undefined' && !customElements.get('recur-error-display')) {\n customElements.define('recur-error-display', RecurErrorDisplay);\n}\n\n// TypeScript 類型聲明\ndeclare global {\n interface HTMLElementTagNameMap {\n 'recur-error-display': RecurErrorDisplay;\n }\n}\n","/**\n * Recur Skeleton Loader Web Component\n *\n * A reusable skeleton loading animation for payment forms.\n * Provides visual feedback while content is being loaded.\n */\n\nexport class RecurSkeletonLoader extends HTMLElement {\n static get observedAttributes() {\n return ['type'];\n }\n\n constructor() {\n super();\n this.attachShadow({ mode: 'open' });\n }\n\n connectedCallback() {\n this.render();\n }\n\n attributeChangedCallback(name: string, oldValue: string, newValue: string) {\n if (oldValue !== newValue) {\n this.render();\n }\n }\n\n private get type(): 'payment-form' | 'list' | 'card' {\n const typeAttr = this.getAttribute('type');\n if (typeAttr === 'list' || typeAttr === 'card') return typeAttr;\n return 'payment-form';\n }\n\n private renderPaymentFormSkeleton(): string {\n return `\n <div class=\"skeleton-field\">\n <div class=\"skeleton-label\"></div>\n <div class=\"skeleton-input\"></div>\n </div>\n <div class=\"skeleton-field\">\n <div class=\"skeleton-label\"></div>\n <div class=\"skeleton-input\"></div>\n </div>\n <div class=\"skeleton-field\">\n <div class=\"skeleton-label\"></div>\n <div class=\"skeleton-input\"></div>\n </div>\n <div class=\"skeleton-divider\"></div>\n <div class=\"skeleton-field\">\n <div class=\"skeleton-label\"></div>\n <div class=\"skeleton-input\"></div>\n </div>\n <div class=\"skeleton-field\">\n <div class=\"skeleton-label\"></div>\n <div class=\"skeleton-row\">\n <div class=\"skeleton-input-half\"></div>\n <div class=\"skeleton-input-half\"></div>\n </div>\n </div>\n <div class=\"skeleton-button\"></div>\n `;\n }\n\n private renderListSkeleton(): string {\n return `\n <div class=\"skeleton-list-item\"></div>\n <div class=\"skeleton-list-item\"></div>\n <div class=\"skeleton-list-item\"></div>\n `;\n }\n\n private renderCardSkeleton(): string {\n return `\n <div class=\"skeleton-card-header\"></div>\n <div class=\"skeleton-card-body\">\n <div class=\"skeleton-text\"></div>\n <div class=\"skeleton-text\"></div>\n <div class=\"skeleton-text short\"></div>\n </div>\n `;\n }\n\n private render() {\n let content = '';\n switch (this.type) {\n case 'list':\n content = this.renderListSkeleton();\n break;\n case 'card':\n content = this.renderCardSkeleton();\n break;\n default:\n content = this.renderPaymentFormSkeleton();\n }\n\n this.shadowRoot!.innerHTML = `\n <style>\n :host {\n display: block;\n }\n\n @keyframes recur-sdk-pulse {\n 0%, 100% {\n opacity: 1;\n }\n 50% {\n opacity: 0.4;\n }\n }\n\n .skeleton-field {\n margin-bottom: 20px;\n }\n\n .skeleton-label {\n width: 80px;\n height: 14px;\n background: var(--recur-skeleton-bg, #e5e7eb);\n border-radius: 4px;\n margin-bottom: 8px;\n animation: recur-sdk-pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;\n }\n\n .skeleton-input {\n width: 100%;\n height: 42px;\n background: var(--recur-skeleton-bg, #e5e7eb);\n border-radius: var(--recur-border-radius, 6px);\n animation: recur-sdk-pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;\n animation-delay: 0.1s;\n }\n\n .skeleton-row {\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 12px;\n }\n\n .skeleton-input-half {\n height: 42px;\n background: var(--recur-skeleton-bg, #e5e7eb);\n border-radius: var(--recur-border-radius, 6px);\n animation: recur-sdk-pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;\n animation-delay: 0.15s;\n }\n\n .skeleton-divider {\n height: 1px;\n background: var(--recur-skeleton-bg, #e5e7eb);\n margin: 24px 0;\n animation: recur-sdk-pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;\n animation-delay: 0.2s;\n }\n\n .skeleton-button {\n width: 100%;\n height: 48px;\n background: var(--recur-skeleton-bg, #e5e7eb);\n border-radius: 8px;\n margin-top: 8px;\n animation: recur-sdk-pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;\n animation-delay: 0.25s;\n }\n\n /* List skeleton */\n .skeleton-list-item {\n width: 100%;\n height: 60px;\n background: var(--recur-skeleton-bg, #e5e7eb);\n border-radius: var(--recur-border-radius, 6px);\n margin-bottom: 12px;\n animation: recur-sdk-pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;\n }\n\n .skeleton-list-item:nth-child(2) {\n animation-delay: 0.1s;\n }\n\n .skeleton-list-item:nth-child(3) {\n animation-delay: 0.2s;\n }\n\n /* Card skeleton */\n .skeleton-card-header {\n width: 100%;\n height: 120px;\n background: var(--recur-skeleton-bg, #e5e7eb);\n border-radius: var(--recur-border-radius, 6px) var(--recur-border-radius, 6px) 0 0;\n animation: recur-sdk-pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;\n }\n\n .skeleton-card-body {\n padding: 16px;\n }\n\n .skeleton-text {\n width: 100%;\n height: 16px;\n background: var(--recur-skeleton-bg, #e5e7eb);\n border-radius: 4px;\n margin-bottom: 12px;\n animation: recur-sdk-pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;\n animation-delay: 0.1s;\n }\n\n .skeleton-text.short {\n width: 60%;\n animation-delay: 0.2s;\n }\n\n /* Dark theme support */\n :host([theme=\"dark\"]) .skeleton-label,\n :host([theme=\"dark\"]) .skeleton-input,\n :host([theme=\"dark\"]) .skeleton-input-half,\n :host([theme=\"dark\"]) .skeleton-divider,\n :host([theme=\"dark\"]) .skeleton-button,\n :host([theme=\"dark\"]) .skeleton-list-item,\n :host([theme=\"dark\"]) .skeleton-card-header,\n :host([theme=\"dark\"]) .skeleton-text {\n background: #374151;\n }\n </style>\n\n ${content}\n `;\n }\n}\n\n// 自動註冊組件\nif (typeof window !== 'undefined' && !customElements.get('recur-skeleton-loader')) {\n customElements.define('recur-skeleton-loader', RecurSkeletonLoader);\n}\n\n// TypeScript 類型聲明\ndeclare global {\n interface HTMLElementTagNameMap {\n 'recur-skeleton-loader': RecurSkeletonLoader;\n }\n}\n","/**\n * Payment Form Skeleton Web Component\n *\n * Displays a loading skeleton that matches the structure of RecurPaymentForm\n * This provides better UX by showing the expected form layout during initial loading\n */\n\nexport class RecurPaymentFormSkeleton extends HTMLElement {\n constructor() {\n super();\n this.attachShadow({ mode: 'open' });\n }\n\n connectedCallback() {\n this.render();\n }\n\n private render() {\n this.shadowRoot!.innerHTML = `\n <style>\n :host {\n display: block;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n }\n\n .payment-form-wrapper {\n max-width: 500px;\n margin: 0 auto;\n padding: 24px;\n background: white;\n border-radius: 12px;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);\n }\n\n .form-header {\n margin-bottom: 24px;\n }\n\n .form-section {\n margin-bottom: 24px;\n }\n\n .section-title {\n margin: 0 0 12px 0;\n font-size: 16px;\n font-weight: 600;\n color: #2d3748;\n }\n\n .label {\n font-size: 14px;\n color: #4a5568;\n margin-bottom: 4px;\n }\n\n /* Skeleton base styles */\n .skeleton {\n background: linear-gradient(\n 90deg,\n #f0f0f0 0%,\n #f8f8f8 50%,\n #f0f0f0 100%\n );\n background-size: 200% 100%;\n animation: skeleton-loading 1.5s ease-in-out infinite;\n border-radius: 4px;\n }\n\n @keyframes skeleton-loading {\n 0% {\n background-position: 200% 0;\n }\n 100% {\n background-position: -200% 0;\n }\n }\n\n .skeleton-title {\n height: 28px;\n width: 200px;\n margin-bottom: 8px;\n }\n\n .skeleton-subtitle {\n height: 18px;\n width: 320px;\n }\n\n .skeleton-order-summary {\n padding: 16px;\n background: #f7fafc;\n border-radius: 6px;\n margin-bottom: 24px;\n }\n\n .skeleton-order-row {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 12px;\n }\n\n .skeleton-order-row:last-child {\n margin-bottom: 0;\n padding-top: 12px;\n border-top: 1px solid #e2e8f0;\n font-weight: 600;\n }\n\n .skeleton-order-value {\n height: 20px;\n width: 120px;\n }\n\n .input-group {\n margin-bottom: 16px;\n }\n\n .input-group:last-child {\n margin-bottom: 0;\n }\n\n .skeleton-input {\n height: 44px;\n width: 100%;\n }\n\n .card-fields-grid {\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 12px;\n }\n\n .skeleton-button {\n height: 44px;\n width: 100%;\n border-radius: 6px;\n }\n\n .security-note {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 12px;\n background: #f7fafc;\n border-radius: 6px;\n margin-top: 16px;\n }\n\n .security-icon {\n color: #48bb78;\n }\n\n .security-text {\n font-size: 13px;\n color: #4a5568;\n margin: 0;\n }\n </style>\n\n <div class=\"payment-form-wrapper\">\n <!-- Header Skeleton -->\n <div class=\"form-header\">\n <div class=\"skeleton skeleton-title\"></div>\n <div class=\"skeleton skeleton-subtitle\"></div>\n </div>\n\n <!-- Order Summary - Static labels, skeleton values -->\n <div class=\"skeleton-order-summary\">\n <h3 class=\"section-title\">訂單摘要</h3>\n <div class=\"skeleton-order-row\">\n <span class=\"label\">方案</span>\n <div class=\"skeleton skeleton-order-value\"></div>\n </div>\n <div class=\"skeleton-order-row\">\n <span class=\"label\">金額</span>\n <div class=\"skeleton skeleton-order-value\"></div>\n </div>\n <div class=\"skeleton-order-row\">\n <span class=\"label\">週期</span>\n <div class=\"skeleton skeleton-order-value\"></div>\n </div>\n <div class=\"skeleton-order-row\">\n <span class=\"label\">總計</span>\n <div class=\"skeleton skeleton-order-value\"></div>\n </div>\n </div>\n\n <!-- Customer Info Section - Static labels, skeleton inputs -->\n <div class=\"form-section\">\n <h3 class=\"section-title\">客戶資訊</h3>\n <div class=\"input-group\">\n <div class=\"label\">姓名</div>\n <div class=\"skeleton skeleton-input\"></div>\n </div>\n <div class=\"input-group\">\n <div class=\"label\">Email</div>\n <div class=\"skeleton skeleton-input\"></div>\n </div>\n <div class=\"input-group\">\n <div class=\"label\">電話(選填)</div>\n <div class=\"skeleton skeleton-input\"></div>\n </div>\n </div>\n\n <!-- Card Fields Section - Static labels, skeleton inputs -->\n <div class=\"form-section\">\n <h3 class=\"section-title\">信用卡資訊</h3>\n <div class=\"input-group\">\n <div class=\"label\">信用卡號</div>\n <div class=\"skeleton skeleton-input\"></div>\n </div>\n <div class=\"card-fields-grid\">\n <div class=\"input-group\">\n <div class=\"label\">有效期限</div>\n <div class=\"skeleton skeleton-input\"></div>\n </div>\n <div class=\"input-group\">\n <div class=\"label\">安全碼</div>\n <div class=\"skeleton skeleton-input\"></div>\n </div>\n </div>\n </div>\n\n <!-- Submit Button Skeleton -->\n <div class=\"skeleton skeleton-button\"></div>\n\n <!-- Security Note -->\n <div class=\"security-note\">\n <svg class=\"security-icon\" width=\"16\" height=\"16\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <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\" />\n </svg>\n <p class=\"security-text\">您的付款資訊經過加密保護</p>\n </div>\n </div>\n `;\n }\n}\n\n// Register component if not already registered\nif (typeof window !== 'undefined' && !customElements.get('recur-payment-form-skeleton')) {\n customElements.define('recur-payment-form-skeleton', RecurPaymentFormSkeleton);\n}\n\n// TypeScript type declaration\ndeclare global {\n interface HTMLElementTagNameMap {\n 'recur-payment-form-skeleton': RecurPaymentFormSkeleton;\n }\n}\n","/**\n * Recur Toast Notification Component\n *\n * A lightweight, non-intrusive toast notification for success, error, and info messages.\n * Auto-dismisses after a configurable duration.\n */\n\nexport class RecurToast extends HTMLElement {\n static get observedAttributes() {\n return ['message', 'type', 'duration'];\n }\n\n constructor() {\n super();\n this.attachShadow({ mode: 'open' });\n }\n\n connectedCallback() {\n this.render();\n this.setupAutoDismiss();\n }\n\n private get message(): string {\n return this.getAttribute('message') || 'Notification';\n }\n\n private get type(): 'success' | 'error' | 'info' {\n const typeAttr = this.getAttribute('type');\n if (typeAttr === 'success' || typeAttr === 'error') return typeAttr;\n return 'info';\n }\n\n private get duration(): number {\n const durationAttr = this.getAttribute('duration');\n return durationAttr ? parseInt(durationAttr, 10) : 5000;\n }\n\n private setupAutoDismiss() {\n const duration = this.duration;\n if (duration > 0) {\n setTimeout(() => this.dismiss(), duration);\n }\n }\n\n private dismiss() {\n // Add exit animation\n this.style.animation = 'recur-toast-slide-out 0.3s ease-in-out';\n setTimeout(() => this.remove(), 300);\n }\n\n private getTypeIcon(): string {\n switch (this.type) {\n case 'success':\n return `<svg width=\"20\" height=\"20\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M5 13l4 4L19 7\" />\n </svg>`;\n case 'error':\n return `<svg width=\"20\" height=\"20\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M6 18L18 6M6 6l12 12\" />\n </svg>`;\n case 'info':\n default:\n return `<svg width=\"20\" height=\"20\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <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\" />\n </svg>`;\n }\n }\n\n private getTypeColor(): string {\n switch (this.type) {\n case 'success':\n return '#10b981'; // green-500\n case 'error':\n return '#ef4444'; // red-500\n case 'info':\n default:\n return '#3b82f6'; // blue-500\n }\n }\n\n private render() {\n const color = this.getTypeColor();\n\n this.shadowRoot!.innerHTML = `\n <style>\n :host {\n display: block;\n margin-bottom: 12px;\n animation: recur-toast-slide-in 0.3s ease-out;\n }\n\n @keyframes recur-toast-slide-in {\n from {\n transform: translateX(100%);\n opacity: 0;\n }\n to {\n transform: translateX(0);\n opacity: 1;\n }\n }\n\n @keyframes recur-toast-slide-out {\n from {\n transform: translateX(0);\n opacity: 1;\n }\n to {\n transform: translateX(100%);\n opacity: 0;\n }\n }\n\n .toast {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 12px 16px;\n background: white;\n border-left: 4px solid ${color};\n border-radius: 8px;\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);\n min-width: 280px;\n max-width: 420px;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n cursor: pointer;\n transition: transform 0.2s ease;\n }\n\n .toast:hover {\n transform: translateY(-2px);\n box-shadow: 0 6px 16px rgba(0, 0, 0, 0.2);\n }\n\n .toast-icon {\n flex-shrink: 0;\n color: ${color};\n display: flex;\n align-items: center;\n justify-content: center;\n }\n\n .toast-message {\n flex: 1;\n color: #374151;\n font-size: 14px;\n line-height: 1.5;\n margin: 0;\n }\n\n .toast-close {\n flex-shrink: 0;\n color: #9ca3af;\n cursor: pointer;\n padding: 4px;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 4px;\n transition: all 0.2s;\n }\n\n .toast-close:hover {\n color: #6b7280;\n background: #f3f4f6;\n }\n\n /* Dark mode support */\n :host([theme=\"dark\"]) .toast {\n background: #1f2937;\n }\n\n :host([theme=\"dark\"]) .toast-message {\n color: #e5e7eb;\n }\n\n :host([theme=\"dark\"]) .toast-close {\n color: #6b7280;\n }\n\n :host([theme=\"dark\"]) .toast-close:hover {\n color: #9ca3af;\n background: #374151;\n }\n </style>\n\n <div class=\"toast\" role=\"alert\">\n <div class=\"toast-icon\">\n ${this.getTypeIcon()}\n </div>\n <p class=\"toast-message\">${this.message}</p>\n <button class=\"toast-close\" aria-label=\"Close\">\n <svg width=\"16\" height=\"16\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M6 18L18 6M6 6l12 12\" />\n </svg>\n </button>\n </div>\n `;\n\n // Add click handler for close button\n const closeBtn = this.shadowRoot!.querySelector('.toast-close');\n closeBtn?.addEventListener('click', (e) => {\n e.stopPropagation();\n this.dismiss();\n });\n\n // Click anywhere on toast to dismiss\n const toast = this.shadowRoot!.querySelector('.toast');\n toast?.addEventListener('click', () => this.dismiss());\n }\n\n /**\n * Static helper method to show toast\n */\n static show(\n message: string,\n type: 'success' | 'error' | 'info' = 'info',\n duration: number = 5000\n ): RecurToast {\n const container = RecurToastContainer.getInstance();\n\n const toast = document.createElement('recur-toast');\n toast.setAttribute('message', message);\n toast.setAttribute('type', type);\n toast.setAttribute('duration', duration.toString());\n\n container.appendChild(toast);\n\n return toast;\n }\n}\n\n/**\n * Toast Container - Manages toast stack positioning\n */\nexport class RecurToastContainer extends HTMLElement {\n private static instance: RecurToastContainer | null = null;\n\n constructor() {\n super();\n this.attachShadow({ mode: 'open' });\n }\n\n connectedCallback() {\n this.render();\n }\n\n private render() {\n this.shadowRoot!.innerHTML = `\n <style>\n :host {\n position: fixed;\n top: 24px;\n right: 24px;\n z-index: 9999;\n display: flex;\n flex-direction: column;\n pointer-events: none;\n }\n\n ::slotted(*) {\n pointer-events: auto;\n }\n\n /* Mobile responsive */\n @media (max-width: 640px) {\n :host {\n top: 16px;\n right: 16px;\n left: 16px;\n }\n }\n </style>\n\n <slot></slot>\n `;\n }\n\n /**\n * Get or create singleton instance\n */\n static getInstance(): RecurToastContainer {\n if (!RecurToastContainer.instance) {\n RecurToastContainer.instance = document.querySelector('recur-toast-container');\n\n if (!RecurToastContainer.instance) {\n RecurToastContainer.instance = document.createElement('recur-toast-container');\n document.body.appendChild(RecurToastContainer.instance);\n }\n }\n\n return RecurToastContainer.instance;\n }\n}\n\n// Auto-register components\nif (typeof window !== 'undefined' && !customElements.get('recur-toast')) {\n customElements.define('recur-toast', RecurToast);\n}\n\nif (typeof window !== 'undefined' && !customElements.get('recur-toast-container')) {\n customElements.define('recur-toast-container', RecurToastContainer);\n}\n\n// TypeScript type declarations\ndeclare global {\n interface HTMLElementTagNameMap {\n 'recur-toast': RecurToast;\n 'recur-toast-container': RecurToastContainer;\n }\n}\n","/**\n * Recur Payment Form Web Component (混合模式)\n *\n * 使用 Shadow DOM 包裝外層 UI,PAYUNi 容器保持在 Light DOM\n * 這樣既能享受樣式隔離,又能讓 PAYUNi SDK 正常工作\n */\n\nexport class RecurPaymentForm extends HTMLElement {\n private containerId: string;\n private customStyles: string;\n private _isInitializing = false;\n private _initializationAborted = false;\n\n constructor() {\n super();\n\n // 生成唯一的 container ID\n this.containerId = this.getAttribute('container-id') || `recur-${Date.now()}`;\n\n // 獲取自定義樣式(如果有提供)\n this.customStyles = this.getAttribute('custom-styles') || '';\n\n // 創建 Shadow DOM(用於外層 UI 和樣式隔離)\n this.attachShadow({ mode: 'open' });\n }\n\n connectedCallback() {\n this.render();\n }\n\n disconnectedCallback() {\n console.log('[PaymentForm] Component disconnected, cleaning up...');\n\n // Mark initialization as aborted to stop any ongoing operations\n this._initializationAborted = true;\n\n // Clear payment session reference\n (this as any)._paymentSession = null;\n\n // Remove event listeners from submit button by replacing with clone\n const submitBtn = document.getElementById(`${this.containerId}-submit-btn`);\n if (submitBtn) {\n const newBtn = submitBtn.cloneNode(true);\n submitBtn.parentNode?.replaceChild(newBtn, submitBtn);\n }\n }\n\n // 監聽 attribute 變化\n static get observedAttributes() {\n return ['custom-styles', 'customer-name', 'customer-email', 'plan-name', 'amount', 'billing-period'];\n }\n\n attributeChangedCallback(name: string, oldValue: string, newValue: string) {\n if (name === 'custom-styles' && oldValue !== newValue) {\n this.customStyles = newValue || '';\n this.updateCustomStyles();\n } else if (oldValue !== newValue) {\n // Re-render customer info section when customer/order data changes\n this.updateCustomerInfoSection();\n }\n }\n\n private render() {\n // Shadow DOM 內容:外層 UI 結構和樣式\n this.shadowRoot!.innerHTML = `\n <style>\n /* 這些樣式被隔離在 Shadow DOM 內,不會影響外部 */\n :host {\n display: block;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n }\n\n .payment-form-wrapper {\n max-width: 500px;\n margin: 0 auto;\n padding: 24px;\n background: white;\n border-radius: 12px;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);\n }\n\n .form-header {\n margin-bottom: 24px;\n }\n\n .form-title {\n font-size: 24px;\n font-weight: 600;\n color: #1a202c;\n margin: 0 0 8px 0;\n }\n\n .form-subtitle {\n font-size: 14px;\n color: #718096;\n margin: 0;\n }\n\n .form-section {\n margin-bottom: 24px;\n }\n\n .section-title {\n font-size: 14px;\n font-weight: 600;\n color: #2d3748;\n margin: 0 0 12px 0;\n }\n\n /* Slot 樣式 - 這會應用到 slotted 內容 */\n ::slotted(*) {\n display: block;\n }\n\n .security-note {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 12px;\n background: #f7fafc;\n border-radius: 6px;\n margin-top: 16px;\n }\n\n .security-icon {\n color: #48bb78;\n }\n\n .security-text {\n font-size: 13px;\n color: #4a5568;\n margin: 0;\n }\n </style>\n\n <div class=\"payment-form-wrapper\">\n <div class=\"form-header\">\n <h2 class=\"form-title\">付款資訊</h2>\n <p class=\"form-subtitle\">請填寫您的信用卡資訊以完成訂閱</p>\n </div>\n\n <!-- Slot for order summary (Light DOM) -->\n <slot name=\"order-summary\"></slot>\n\n <div class=\"form-section\">\n <h3 class=\"section-title\">客戶資訊</h3>\n <!-- Slot for customer info fields (Light DOM) -->\n <slot name=\"customer-info\"></slot>\n </div>\n\n <div class=\"form-section\">\n <h3 class=\"section-title\">信用卡資訊</h3>\n <!-- Slot for PAYUNi card fields (Light DOM) -->\n <slot name=\"card-fields\"></slot>\n </div>\n\n <!-- Slot for submit button (Light DOM) -->\n <slot name=\"actions\"></slot>\n\n <div class=\"security-note\">\n <svg class=\"security-icon\" width=\"16\" height=\"16\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <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\" />\n </svg>\n <p class=\"security-text\">您的付款資訊經過加密保護</p>\n </div>\n </div>\n `;\n\n // Light DOM 內容:PAYUNi 需要訪問的元素\n this.createLightDOMContent();\n }\n\n private createLightDOMContent() {\n // 清空現有 Light DOM 內容\n this.innerHTML = '';\n\n // 創建自定義樣式容器(Light DOM - 這樣可以影響 PAYUNi iframes)\n const styleElement = document.createElement('style');\n styleElement.id = `${this.containerId}-custom-styles`;\n\n // 組合自定義樣式和 ID 選擇器樣式\n const idSpecificStyles = `\n /* PAYUNi iframe 容器高度(使用實際的 container ID) */\n #${this.containerId}-card-no,\n #${this.containerId}-card-exp,\n #${this.containerId}-card-cvc {\n height: 36px !important;\n }\n `;\n\n styleElement.textContent = this.customStyles + '\\n' + idSpecificStyles;\n this.appendChild(styleElement);\n\n // 創建訂單摘要區塊(Light DOM)\n const orderSummary = this.createOrderSummarySection();\n orderSummary.slot = 'order-summary';\n this.appendChild(orderSummary);\n\n // 創建客戶資訊區塊(Light DOM)\n const customerInfo = this.createCustomerInfoSection();\n customerInfo.slot = 'customer-info';\n this.appendChild(customerInfo);\n\n // 創建信用卡欄位區塊(Light DOM - PAYUNi 會使用這些 ID)\n const cardFields = this.createCardFieldsSection();\n cardFields.slot = 'card-fields';\n this.appendChild(cardFields);\n\n // 創建操作按鈕區塊(Light DOM)\n const actions = this.createActionsSection();\n actions.slot = 'actions';\n this.appendChild(actions);\n }\n\n private updateCustomerInfoSection() {\n // Update customer info section when attributes change\n const existingSection = this.querySelector('[slot=\"customer-info\"]');\n if (existingSection) {\n const newSection = this.createCustomerInfoSection();\n newSection.slot = 'customer-info';\n existingSection.replaceWith(newSection);\n }\n }\n\n private updateCustomStyles() {\n const styleElement = this.querySelector(`#${this.containerId}-custom-styles`) as HTMLStyleElement;\n if (styleElement) {\n // 組合自定義樣式和 ID 選擇器樣式\n const idSpecificStyles = `\n /* PAYUNi iframe 容器高度(使用實際的 container ID) */\n #${this.containerId}-card-no,\n #${this.containerId}-card-exp,\n #${this.containerId}-card-cvc {\n height: 36px !important;\n }\n `;\n styleElement.textContent = this.customStyles + '\\n' + idSpecificStyles;\n }\n }\n\n private createOrderSummarySection(): HTMLElement {\n const section = document.createElement('div');\n section.className = 'order-summary-section';\n\n // Get order data from attributes\n const planName = this.getAttribute('plan-name');\n const amount = this.getAttribute('amount');\n const billingPeriod = this.getAttribute('billing-period');\n\n // Only show order summary if we have the data\n if (!planName || !amount) {\n return section;\n }\n\n // Format billing period in Chinese\n const billingPeriodMap: { [key: string]: string } = {\n 'MONTHLY': '月',\n 'QUARTERLY': '季',\n 'YEARLY': '年',\n 'WEEKLY': '週',\n };\n const periodText = billingPeriod ? billingPeriodMap[billingPeriod] || billingPeriod : '';\n\n section.innerHTML = `\n <style>\n .order-summary-section {\n background: #f7fafc;\n border: 1px solid #e2e8f0;\n border-radius: 8px;\n padding: 16px;\n margin-bottom: 24px;\n }\n\n .order-summary-title {\n font-size: 14px;\n font-weight: 600;\n color: #2d3748;\n margin: 0 0 12px 0;\n }\n\n .order-summary-item {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 8px;\n }\n\n .order-summary-item:last-child {\n margin-bottom: 0;\n padding-top: 12px;\n border-top: 1px solid #e2e8f0;\n }\n\n .order-summary-label {\n font-size: 14px;\n color: #4a5568;\n }\n\n .order-summary-value {\n font-size: 14px;\n font-weight: 500;\n color: #2d3748;\n }\n\n .order-summary-total {\n font-size: 18px;\n font-weight: 700;\n color: #1a202c;\n }\n </style>\n\n <h3 class=\"order-summary-title\">訂單摘要</h3>\n <div class=\"order-summary-item\">\n <span class=\"order-summary-label\">訂閱方案</span>\n <span class=\"order-summary-value\">${planName}</span>\n </div>\n ${periodText ? `\n <div class=\"order-summary-item\">\n <span class=\"order-summary-label\">計費週期</span>\n <span class=\"order-summary-value\">每${periodText}</span>\n </div>\n ` : ''}\n <div class=\"order-summary-item\">\n <span class=\"order-summary-label\">總計</span>\n <span class=\"order-summary-total\">NT$ ${Number(amount).toLocaleString()}</span>\n </div>\n `;\n\n return section;\n }\n\n private createCustomerInfoSection(): HTMLElement {\n const section = document.createElement('div');\n section.className = 'customer-info-section';\n\n // Check if customer data is provided via attributes\n const customerName = this.getAttribute('customer-name');\n const customerEmail = this.getAttribute('customer-email');\n\n // If customer data is provided, show as read-only display\n if (customerName && customerEmail) {\n section.innerHTML = `\n <style>\n .recur-info-display {\n background: #f7fafc;\n border: 1px solid #e2e8f0;\n border-radius: 6px;\n padding: 12px;\n margin-bottom: 8px;\n }\n\n .recur-info-row {\n display: flex;\n align-items: center;\n margin-bottom: 6px;\n }\n\n .recur-info-row:last-child {\n margin-bottom: 0;\n }\n\n .recur-info-label {\n font-size: 13px;\n color: #718096;\n min-width: 80px;\n }\n\n .recur-info-value {\n font-size: 14px;\n color: #2d3748;\n font-weight: 500;\n }\n </style>\n\n <div class=\"recur-info-display\">\n <div class=\"recur-info-row\">\n <span class=\"recur-info-label\">客戶姓名:</span>\n <span class=\"recur-info-value\">${customerName}</span>\n </div>\n <div class=\"recur-info-row\">\n <span class=\"recur-info-label\">電子郵件:</span>\n <span class=\"recur-info-value\">${customerEmail}</span>\n </div>\n </div>\n `;\n } else {\n // Show input fields if customer data is not provided\n section.innerHTML = `\n <style>\n /* 這些樣式在 Light DOM 中,會影響全局 */\n /* 使用帶 prefix 的 class name 避免衝突 */\n .recur-form-group {\n margin-bottom: 16px;\n }\n\n .recur-form-label {\n display: block;\n font-size: 14px;\n font-weight: 500;\n color: #2d3748;\n margin-bottom: 6px;\n }\n\n .recur-form-input {\n width: 100%;\n padding: 10px 12px;\n font-size: 14px;\n border: 1px solid #d1d5db;\n border-radius: 6px;\n transition: border-color 0.2s;\n box-sizing: border-box;\n }\n\n .recur-form-input:focus {\n outline: none;\n border-color: #3b82f6;\n box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);\n }\n\n .recur-form-input:hover:not(:focus) {\n border-color: #9ca3af;\n }\n </style>\n\n <div class=\"recur-form-group\">\n <label class=\"recur-form-label\" for=\"${this.containerId}-email\">Email</label>\n <input\n type=\"email\"\n id=\"${this.containerId}-email\"\n class=\"recur-form-input\"\n placeholder=\"your@email.com\"\n required\n />\n </div>\n\n <div class=\"recur-form-group\">\n <label class=\"recur-form-label\" for=\"${this.containerId}-name\">姓名</label>\n <input\n type=\"text\"\n id=\"${this.containerId}-name\"\n class=\"recur-form-input\"\n placeholder=\"王小明\"\n required\n />\n </div>\n\n <div class=\"recur-form-group\">\n <label class=\"recur-form-label\" for=\"${this.containerId}-phone\">電話</label>\n <input\n type=\"tel\"\n id=\"${this.containerId}-phone\"\n class=\"recur-form-input\"\n placeholder=\"+886 912345678\"\n required\n />\n </div>\n `;\n }\n\n return section;\n }\n\n private createCardFieldsSection(): HTMLElement {\n const section = document.createElement('div');\n section.className = 'card-fields-section';\n\n section.innerHTML = `\n <style>\n .recur-card-field {\n margin-bottom: 16px;\n }\n\n .recur-card-field-label {\n display: block;\n font-size: 14px;\n font-weight: 500;\n color: #2d3748;\n margin-bottom: 6px;\n }\n\n /* PAYUNi iframe 容器樣式 */\n .recur-payuni-iframe {\n height: 36px;\n width: 100%;\n }\n\n .recur-card-row {\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 12px;\n }\n </style>\n\n <div class=\"recur-card-field\">\n <label class=\"recur-card-field-label\">卡號</label>\n <div class=\"recur-card-field-container\" id=\"${this.containerId}-card-no-wrapper\">\n <!-- PAYUNi 會在這裡注入 iframe -->\n <div id=\"${this.containerId}-card-no\" class=\"recur-payuni-iframe\"></div>\n </div>\n </div>\n\n <div class=\"recur-card-row\">\n <div class=\"recur-card-field\">\n <label class=\"recur-card-field-label\">有效期限</label>\n <div class=\"recur-card-field-container\" id=\"${this.containerId}-card-exp-wrapper\">\n <div id=\"${this.containerId}-card-exp\" class=\"recur-payuni-iframe\"></div>\n </div>\n </div>\n\n <div class=\"recur-card-field\">\n <label class=\"recur-card-field-label\">安全碼</label>\n <div class=\"recur-card-field-container\" id=\"${this.containerId}-card-cvc-wrapper\">\n <div id=\"${this.containerId}-card-cvc\" class=\"recur-payuni-iframe\"></div>\n </div>\n </div>\n </div>\n `;\n\n return section;\n }\n\n private createActionsSection(): HTMLElement {\n const section = document.createElement('div');\n section.className = 'actions-section';\n\n section.innerHTML = `\n <style>\n .recur-submit-button {\n width: 100%;\n padding: 14px 20px;\n font-size: 16px;\n font-weight: 500;\n color: #ffffff;\n background: #18181b;\n border: none;\n border-radius: 6px;\n cursor: pointer;\n transition: background-color 0.2s;\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\n }\n\n .recur-submit-button:hover:not(:disabled) {\n background: #27272a;\n }\n\n .recur-submit-button:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n }\n\n .recur-submit-button.loading {\n cursor: wait;\n }\n\n .recur-loading-spinner {\n width: 16px;\n height: 16px;\n border: 2px solid currentColor;\n border-top-color: transparent;\n border-radius: 50%;\n animation: recur-spin 0.6s linear infinite;\n }\n\n @keyframes recur-spin {\n to { transform: rotate(360deg); }\n }\n </style>\n\n <button\n type=\"submit\"\n id=\"${this.containerId}-submit-btn\"\n class=\"recur-submit-button\"\n disabled\n >\n <span id=\"${this.containerId}-submit-text\">確認付款</span>\n </button>\n `;\n\n return section;\n }\n\n /**\n * 初始化 PAYUNi SDK\n * 這個方法會在外部調用,傳入 SDK token\n */\n async initializePayment(sdkToken: string, environment: 'SANDBOX' | 'PRODUCTION') {\n // Checkpoint 1: Check if component was already disconnected before starting\n if (this._initializationAborted) {\n console.log('[PaymentForm] Initialization aborted before start');\n return null;\n }\n\n // Mark that initialization is in progress\n this._isInitializing = true;\n\n // 等待 DOM 完全準備好\n await this.updateComplete();\n\n // Checkpoint 2: Check if disconnected while waiting for DOM\n if (this._initializationAborted) {\n console.log('[PaymentForm] Initialization aborted after updateComplete');\n this._isInitializing = false;\n return null;\n }\n\n // 確認 PAYUNi SDK 已加載\n if (!window.UniPayment) {\n this._isInitializing = false;\n throw new Error('PAYUNi SDK not loaded');\n }\n\n try {\n // Checkpoint 3: Verify DOM elements exist before creating session\n const cardNoEl = document.getElementById(`${this.containerId}-card-no`);\n const cardExpEl = document.getElementById(`${this.containerId}-card-exp`);\n const cardCvcEl = document.getElementById(`${this.containerId}-card-cvc`);\n\n if (!cardNoEl || !cardExpEl || !cardCvcEl) {\n console.log('[PaymentForm] DOM elements not found, likely disconnected');\n this._isInitializing = false;\n return null;\n }\n\n // Checkpoint 4: Check again before creating session\n if (this._initializationAborted) {\n console.log('[PaymentForm] Initialization aborted before createSession');\n this._isInitializing = false;\n return null;\n }\n\n // 初始化 PAYUNi\n const paymentSession = window.UniPayment.createSession(sdkToken, {\n env: environment === 'SANDBOX' ? 'S' : 'P',\n elements: {\n // 這些 ID 在 Light DOM 中,document.getElementById 可以找到\n CardNo: `${this.containerId}-card-no`,\n CardExp: `${this.containerId}-card-exp`,\n CardCvc: `${this.containerId}-card-cvc`,\n },\n });\n\n // Checkpoint 5: Check before starting payment session (the critical async operation)\n if (this._initializationAborted) {\n console.log('[PaymentForm] Initialization aborted before paymentSession.start()');\n this._isInitializing = false;\n return null;\n }\n\n // Start payment session with error handling for 1008 timeout\n try {\n await paymentSession.start();\n } catch (startError: any) {\n // Check if this is the 1008 timeout error and component was disconnected\n const is1008Error = startError?.message?.includes('1008') ||\n startError?.message?.includes('timeout') ||\n startError?.code === 1008;\n\n if (is1008Error && this._initializationAborted) {\n // Component was disconnected during initialization, silently ignore the error\n console.log('[PaymentForm] Caught 1008 timeout error after component disconnect - ignoring');\n this._isInitializing = false;\n return null;\n }\n\n // For other errors or if component is still mounted, re-throw\n throw startError;\n }\n\n // Checkpoint 6: Check after start completes (this is where 1008 error would occur)\n if (this._initializationAborted) {\n console.log('[PaymentForm] Initialization aborted after paymentSession.start()');\n this._isInitializing = false;\n return null;\n }\n\n // 監聽 PAYUNi 事件\n paymentSession.onUpdate?.((data: any) => {\n // Check if component is still mounted when event fires\n if (this._initializationAborted) {\n console.log('[PaymentForm] Ignoring onUpdate event - component disconnected');\n return;\n }\n\n console.log('[PaymentForm] PAYUNi update:', data);\n\n // 檢查所有卡片欄位是否都已填寫完成\n const allFieldsComplete = data.status &&\n data.status.CardNo === true &&\n data.status.CardExp === true &&\n data.status.CardCvc === true;\n\n // 啟用/禁用提交按鈕\n const submitBtn = document.getElementById(`${this.containerId}-submit-btn`) as HTMLButtonElement;\n if (submitBtn) {\n submitBtn.disabled = !allFieldsComplete;\n console.log('[PaymentForm] Submit button disabled:', !allFieldsComplete);\n }\n });\n\n // Checkpoint 7: Final check before saving session\n if (this._initializationAborted) {\n console.log('[PaymentForm] Initialization aborted before saving session');\n this._isInitializing = false;\n return null;\n }\n\n // 保存 session 供後續使用\n (this as any)._paymentSession = paymentSession;\n\n // 設置表單提交處理\n this.setupFormSubmission();\n\n this._isInitializing = false;\n return paymentSession;\n } catch (error) {\n this._isInitializing = false;\n console.error('[PaymentForm] Failed to initialize payment:', error);\n throw error;\n }\n }\n\n private setupFormSubmission() {\n const submitBtn = document.getElementById(`${this.containerId}-submit-btn`) as HTMLButtonElement;\n if (!submitBtn) return;\n\n submitBtn.addEventListener('click', async (e) => {\n e.preventDefault();\n\n // Prevent double submission\n if (submitBtn.classList.contains('loading')) {\n return;\n }\n\n // 獲取客戶資訊 - 從輸入框或屬性\n let email: string | undefined;\n let name: string | undefined;\n let phone: string | undefined;\n\n // Try to get from input fields first\n const emailInput = document.getElementById(`${this.containerId}-email`) as HTMLInputElement;\n const nameInput = document.getElementById(`${this.containerId}-name`) as HTMLInputElement;\n const phoneInput = document.getElementById(`${this.containerId}-phone`) as HTMLInputElement;\n\n if (emailInput && nameInput) {\n // Customer info is in input fields (not pre-filled)\n email = emailInput.value;\n name = nameInput.value;\n phone = phoneInput?.value;\n\n // 驗證必填欄位\n if (!email || !name) {\n this.showError('請填寫姓名和電子郵件');\n return;\n }\n } else {\n // Customer info is pre-filled (use attributes)\n email = this.getAttribute('customer-email') || undefined;\n name = this.getAttribute('customer-name') || undefined;\n\n // 驗證必填欄位\n if (!email || !name) {\n this.showError('客戶資訊不完整');\n return;\n }\n }\n\n // Clear any previous errors\n this.clearError();\n\n // Show loading state\n this.setButtonLoading(true);\n\n // 觸發自定義事件,讓外部處理提交\n this.dispatchEvent(new CustomEvent('submit', {\n detail: {\n customerEmail: email,\n customerName: name,\n customerPhone: phone,\n paymentSession: (this as any)._paymentSession,\n },\n bubbles: true,\n composed: true, // 讓事件可以穿透 Shadow DOM\n }));\n });\n }\n\n /**\n * Set button loading state\n */\n private setButtonLoading(loading: boolean) {\n const submitBtn = document.getElementById(`${this.containerId}-submit-btn`) as HTMLButtonElement;\n\n if (!submitBtn) return;\n\n if (loading) {\n submitBtn.classList.add('loading');\n submitBtn.disabled = true;\n submitBtn.innerHTML = `\n <span class=\"recur-loading-spinner\"></span>\n <span>處理中...</span>\n `;\n } else {\n submitBtn.classList.remove('loading');\n submitBtn.disabled = false;\n submitBtn.innerHTML = '<span id=\"' + this.containerId + '-submit-text\">確認付款</span>';\n }\n }\n\n /**\n * Reset button to normal state (call this when payment completes or fails)\n */\n resetButton() {\n this.setButtonLoading(false);\n }\n\n /**\n * Show error message using RecurErrorDisplay component\n */\n private showError(message: string) {\n // Find or create error container in Light DOM\n let errorContainer = this.querySelector('.recur-sdk__error-container') as HTMLElement;\n\n if (!errorContainer) {\n errorContainer = document.createElement('div');\n errorContainer.className = 'recur-sdk__error-container';\n errorContainer.style.cssText = 'margin-bottom: 16px;';\n\n // Insert at the beginning of the component\n this.insertBefore(errorContainer, this.firstChild);\n }\n\n // Create error display component\n const errorDisplay = document.createElement('recur-error-display');\n errorDisplay.setAttribute('error', message);\n errorDisplay.setAttribute('dismissible', 'true');\n\n // Replace old error message\n errorContainer.innerHTML = '';\n errorContainer.appendChild(errorDisplay);\n\n // Scroll to error\n errorDisplay.scrollIntoView({ behavior: 'smooth', block: 'center' });\n }\n\n /**\n * Clear any displayed errors\n */\n private clearError() {\n const errorContainer = this.querySelector('.recur-sdk__error-container');\n if (errorContainer) {\n errorContainer.innerHTML = '';\n }\n }\n\n /**\n * 等待組件完全連接到 DOM\n */\n private updateComplete(): Promise<void> {\n return new Promise((resolve) => {\n if (this.isConnected) {\n // 使用 setTimeout 確保 Light DOM 也已渲染\n setTimeout(resolve, 0);\n } else {\n const observer = new MutationObserver(() => {\n if (this.isConnected) {\n observer.disconnect();\n setTimeout(resolve, 0);\n }\n });\n observer.observe(document.body, { childList: true, subtree: true });\n }\n });\n }\n\n /**\n * 獲取表單數據\n */\n getFormData() {\n return {\n email: (document.getElementById(`${this.containerId}-email`) as HTMLInputElement)?.value,\n name: (document.getElementById(`${this.containerId}-name`) as HTMLInputElement)?.value,\n phone: (document.getElementById(`${this.containerId}-phone`) as HTMLInputElement)?.value,\n };\n }\n\n /**\n * 設置表單數據\n */\n setFormData(data: { email?: string; name?: string; phone?: string }) {\n if (data.email) {\n const emailInput = document.getElementById(`${this.containerId}-email`) as HTMLInputElement;\n if (emailInput) emailInput.value = data.email;\n }\n if (data.name) {\n const nameInput = document.getElementById(`${this.containerId}-name`) as HTMLInputElement;\n if (nameInput) nameInput.value = data.name;\n }\n if (data.phone) {\n const phoneInput = document.getElementById(`${this.containerId}-phone`) as HTMLInputElement;\n if (phoneInput) phoneInput.value = data.phone;\n }\n }\n}\n\n// 註冊 Web Component\nif (!customElements.get('recur-payment-form')) {\n customElements.define('recur-payment-form', RecurPaymentForm);\n}\n\n// TypeScript 類型聲明\ndeclare global {\n interface HTMLElementTagNameMap {\n 'recur-payment-form': RecurPaymentForm;\n }\n}\n","/**\n * Recur Vanilla SDK\n *\n * Standalone JavaScript SDK for embedding subscription checkout\n * No dependencies, works with plain HTML/JavaScript\n */\n\n// Auto-register Web Components\nimport '../components/index.js';\n\nimport { RecurCore } from './core.js';\nimport { RecurModal } from './modal.js';\nimport { RecurIframe } from './iframe.js';\nimport { EmbeddedCheckout } from './embedded-checkout.js';\nimport { RecurElements, createElements } from './elements.js';\nimport type { VanillaRecurConfig, VanillaCheckoutOptions } from './types.js';\nimport type { EmbeddedCheckoutOptions } from './embedded-checkout.js';\nimport type { RecurElementsConfig, ConfirmPaymentOptions, PaymentResult } from './elements.js';\n\nexport class RecurCheckout {\n private core: RecurCore;\n private currentModal: RecurModal | null = null;\n private currentIframe: RecurIframe | null = null;\n\n constructor(config: VanillaRecurConfig) {\n this.core = new RecurCore(config);\n }\n\n /**\n * Fetch available subscription plans\n */\n async fetchPlans() {\n return await this.core.fetchPlans();\n }\n\n /**\n * Create embedded checkout form with credit card fields\n * Renders a complete checkout UI in the specified container\n */\n async createEmbeddedCheckout(options: EmbeddedCheckoutOptions): Promise<void> {\n const config = this.core.getConfig();\n const embeddedCheckout = new EmbeddedCheckout(config, options);\n await embeddedCheckout.render();\n }\n\n /**\n * Initiate checkout flow\n */\n async checkout(options: VanillaCheckoutOptions): Promise<void> {\n try {\n // Create subscription\n const result = await this.core.createSubscription(options);\n\n // Call success callback\n if (options.onSuccess) {\n options.onSuccess(result);\n }\n\n // Handle payment based on mode\n const config = this.core.getConfig();\n const mode = options.mode || 'redirect';\n\n if (config.mockMode) {\n // Mock mode: show mock payment UI instead of loading real URL\n console.log('[Recur SDK] 🎭 Mock mode: Showing mock payment UI');\n await this.showMockPaymentUI(mode, result, options.container, options.onClose);\n } else {\n // Real mode: generate payment URL and load it\n const baseUrl = config.baseUrl || window.location.origin;\n const paymentUrl = `${baseUrl}/checkout/${result.subscription.id}`;\n\n switch (mode) {\n case 'modal':\n this.openModal(paymentUrl, options.onClose);\n break;\n\n case 'iframe':\n this.embedIframe(paymentUrl, options.container, options.onClose);\n break;\n\n case 'redirect':\n default:\n window.location.href = paymentUrl;\n break;\n }\n }\n } catch (error) {\n // Call error callback\n if (options.onError) {\n options.onError(error as any);\n } else {\n // Default error handling\n console.error('Recur checkout error:', error);\n throw error;\n }\n }\n }\n\n /**\n * Open payment in modal\n */\n private openModal(paymentUrl: string, onClose?: () => void): void {\n // Close existing modal if any\n this.closeModal();\n\n this.currentModal = new RecurModal(() => {\n this.currentModal = null;\n if (onClose) {\n onClose();\n }\n });\n\n this.currentModal.open(paymentUrl);\n }\n\n /**\n * Embed payment in iframe\n */\n private embedIframe(\n paymentUrl: string,\n container?: string | HTMLElement,\n onClose?: () => void\n ): void {\n if (!container) {\n throw new Error('Container is required for iframe mode');\n }\n\n // Remove existing iframe if any\n this.removeIframe();\n\n this.currentIframe = new RecurIframe(container, () => {\n this.currentIframe = null;\n if (onClose) {\n onClose();\n }\n });\n\n this.currentIframe.embed(paymentUrl);\n }\n\n /**\n * Close modal manually\n */\n closeModal(): void {\n if (this.currentModal) {\n this.currentModal.close();\n this.currentModal = null;\n }\n }\n\n /**\n * Remove iframe manually\n */\n removeIframe(): void {\n if (this.currentIframe) {\n this.currentIframe.remove();\n this.currentIframe = null;\n }\n }\n\n /**\n * Close any active checkout UI (modal or iframe)\n */\n close(): void {\n this.closeModal();\n this.removeIframe();\n }\n\n /**\n * Show mock payment UI for testing\n */\n private async showMockPaymentUI(\n mode: string,\n result: any,\n container?: string | HTMLElement,\n onClose?: () => void\n ): Promise<void> {\n const mockHTML = this.generateMockPaymentHTML(result);\n\n switch (mode) {\n case 'modal':\n this.showMockModal(mockHTML, onClose);\n break;\n\n case 'iframe':\n this.showMockIframe(mockHTML, container, onClose);\n break;\n\n case 'redirect':\n default:\n const baseUrl = window.location.origin;\n const paymentUrl = `${baseUrl}/checkout/${result.subscription.id}`;\n console.log('[Recur SDK] 🎭 Mock mode: Would redirect to:', paymentUrl);\n // Show toast notification instead of alert\n const { RecurToast } = await import('../components/base/toast.js');\n RecurToast.show(\n `🎭 Mock 模式:在真實環境中,用戶會被導向付款頁面`,\n 'info',\n 8000\n );\n console.log('Payment URL:', paymentUrl);\n break;\n }\n }\n\n /**\n * Generate mock payment form HTML\n */\n private generateMockPaymentHTML(result: any): string {\n return `\n <!DOCTYPE html>\n <html>\n <head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <style>\n body {\n margin: 0;\n padding: 20px;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);\n min-height: 100vh;\n display: flex;\n align-items: center;\n justify-content: center;\n }\n .container {\n background: white;\n border-radius: 12px;\n padding: 40px;\n max-width: 500px;\n width: 100%;\n box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);\n }\n .badge {\n display: inline-block;\n padding: 4px 12px;\n background: #fed7d7;\n color: #c53030;\n border-radius: 12px;\n font-size: 12px;\n font-weight: 600;\n margin-bottom: 20px;\n border: 2px solid #fc8181;\n }\n h1 {\n color: #1a202c;\n margin-bottom: 24px;\n font-size: 24px;\n }\n .info {\n background: #f7fafc;\n border-radius: 8px;\n padding: 16px;\n margin-bottom: 24px;\n }\n .info-row {\n display: flex;\n justify-content: space-between;\n padding: 8px 0;\n border-bottom: 1px solid #e2e8f0;\n }\n .info-row:last-child {\n border-bottom: none;\n }\n .label {\n color: #718096;\n font-size: 14px;\n }\n .value {\n color: #2d3748;\n font-weight: 500;\n font-size: 14px;\n }\n .card-form {\n margin-bottom: 24px;\n }\n .form-group {\n margin-bottom: 16px;\n }\n label {\n display: block;\n margin-bottom: 8px;\n color: #2d3748;\n font-weight: 500;\n font-size: 14px;\n }\n .mock-input {\n width: 100%;\n padding: 12px 16px;\n border: 2px solid #e2e8f0;\n border-radius: 8px;\n background: #f7fafc;\n color: #6b7280;\n font-size: 14px;\n }\n button {\n width: 100%;\n padding: 14px;\n background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);\n color: white;\n border: none;\n border-radius: 8px;\n font-size: 16px;\n font-weight: 600;\n cursor: pointer;\n }\n button:hover {\n opacity: 0.9;\n }\n .note {\n margin-top: 16px;\n padding: 12px;\n background: #fef5e7;\n border-left: 4px solid #f59e0b;\n border-radius: 4px;\n font-size: 13px;\n color: #78350f;\n }\n </style>\n </head>\n <body>\n <div class=\"container\">\n <span class=\"badge\">🎭 MOCK MODE</span>\n <h1>訂閱付款(模擬)</h1>\n\n <div class=\"info\">\n <div class=\"info-row\">\n <span class=\"label\">方案</span>\n <span class=\"value\">${result.subscription.planName}</span>\n </div>\n <div class=\"info-row\">\n <span class=\"label\">金額</span>\n <span class=\"value\">NT$ ${result.subscription.amount}</span>\n </div>\n <div class=\"info-row\">\n <span class=\"label\">週期</span>\n <span class=\"value\">${result.subscription.billingPeriod}</span>\n </div>\n <div class=\"info-row\">\n <span class=\"label\">試用期</span>\n <span class=\"value\">${result.subscription.trialDays} 天</span>\n </div>\n </div>\n\n <div class=\"card-form\">\n <div class=\"form-group\">\n <label>信用卡號</label>\n <input type=\"text\" class=\"mock-input\" value=\"4242 4242 4242 4242 (Mock)\" readonly>\n </div>\n <div class=\"form-group\">\n <label>有效期限</label>\n <input type=\"text\" class=\"mock-input\" value=\"12 / 25 (Mock)\" readonly>\n </div>\n <div class=\"form-group\">\n <label>安全碼</label>\n <input type=\"text\" class=\"mock-input\" value=\"123 (Mock)\" readonly>\n </div>\n </div>\n\n <button onclick=\"handleMockPayment()\">確認付款(模擬)</button>\n\n <div class=\"note\">\n <strong>🎭 Mock 模式說明</strong><br>\n 這是模擬的付款表單。點擊「確認付款」會模擬成功的支付流程。\n </div>\n </div>\n\n <script>\n function handleMockPayment() {\n console.log('🎭 Mock 模式:付款成功!');\n\n // Notify parent to show toast\n if (window.parent !== window) {\n window.parent.postMessage({\n type: 'recur-mock-payment-success',\n showToast: {\n message: '🎭 Mock 模式:付款成功!在真實環境中會觸發實際的支付處理',\n type: 'success'\n }\n }, '*');\n } else {\n // If not in iframe, show toast directly\n console.log('Mock payment successful');\n }\n }\n </script>\n </body>\n </html>\n `;\n }\n\n /**\n * Show mock payment in modal\n */\n private showMockModal(html: string, onClose?: () => void): void {\n this.closeModal();\n\n // Create modal overlay\n const overlay = document.createElement('div');\n overlay.id = 'recur-mock-modal';\n overlay.style.cssText = `\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(0, 0, 0, 0.5);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 999999;\n `;\n\n // Create iframe container\n const container = document.createElement('div');\n container.style.cssText = `\n width: 90%;\n max-width: 600px;\n height: 80%;\n max-height: 700px;\n background: white;\n border-radius: 12px;\n overflow: hidden;\n position: relative;\n `;\n\n // Create close button\n const closeBtn = document.createElement('button');\n closeBtn.textContent = '✕';\n closeBtn.style.cssText = `\n position: absolute;\n top: 16px;\n right: 16px;\n background: white;\n border: none;\n width: 32px;\n height: 32px;\n border-radius: 50%;\n cursor: pointer;\n font-size: 18px;\n z-index: 1;\n box-shadow: 0 2px 8px rgba(0,0,0,0.15);\n `;\n closeBtn.onclick = () => {\n overlay.remove();\n if (onClose) onClose();\n };\n\n // Create iframe\n const iframe = document.createElement('iframe');\n iframe.style.cssText = 'width: 100%; height: 100%; border: none;';\n iframe.srcdoc = html;\n\n container.appendChild(closeBtn);\n container.appendChild(iframe);\n overlay.appendChild(container);\n document.body.appendChild(overlay);\n\n // Listen for mock payment success\n window.addEventListener('message', async (event) => {\n if (event.data.type === 'recur-mock-payment-success') {\n // Show toast if requested\n if (event.data.showToast) {\n const { RecurToast } = await import('../components/base/toast.js');\n RecurToast.show(\n event.data.showToast.message,\n event.data.showToast.type || 'success',\n 5000\n );\n }\n\n overlay.remove();\n if (onClose) onClose();\n }\n });\n }\n\n /**\n * Show mock payment in iframe\n */\n private showMockIframe(\n html: string,\n container?: string | HTMLElement,\n onClose?: () => void\n ): void {\n if (!container) {\n throw new Error('Container is required for iframe mode');\n }\n\n const containerEl = typeof container === 'string'\n ? document.getElementById(container) || document.querySelector(container)\n : container;\n\n if (!containerEl) {\n throw new Error('Container element not found');\n }\n\n // Clear container\n containerEl.innerHTML = '';\n\n // Create iframe\n const iframe = document.createElement('iframe');\n iframe.style.cssText = 'width: 100%; height: 100%; border: none; min-height: 600px;';\n iframe.srcdoc = html;\n\n containerEl.appendChild(iframe);\n\n // Listen for mock payment success\n window.addEventListener('message', async (event) => {\n if (event.data.type === 'recur-mock-payment-success') {\n // Show toast if requested\n if (event.data.showToast) {\n const { RecurToast } = await import('../components/base/toast.js');\n RecurToast.show(\n event.data.showToast.message,\n event.data.showToast.type || 'success',\n 5000\n );\n }\n\n if (onClose) onClose();\n }\n });\n }\n}\n\n// Global API for browser usage\nexport function init(config: VanillaRecurConfig): RecurCheckout {\n return new RecurCheckout(config);\n}\n\n// Default export\nexport default {\n init,\n RecurCheckout,\n RecurElements,\n createElements,\n};\n\n// Export Elements class separately\nexport { RecurElements, createElements };\nexport type { RecurElementsConfig, ConfirmPaymentOptions, PaymentResult };\n\n// Expose types for TypeScript users\nexport type * from './types.js';\n","/**\n * Recur SDK Web Components\n *\n * This module exports all Web Components used in the Recur SDK.\n * Components are automatically registered when this module is imported.\n *\n * NOTE: Safe to import in SSR environments (Next.js, etc.)\n * Components are dynamically loaded only in browser environments.\n */\n\n/**\n * Register all Web Components\n *\n * This function loads and registers components dynamically to avoid\n * SSR issues with HTMLElement and other browser-only APIs.\n */\nexport async function registerComponents(): Promise<void> {\n if (typeof window === 'undefined') {\n console.warn('[Recur SDK] Cannot register Web Components: window is not defined');\n return;\n }\n\n // Dynamically import components (browser-only)\n await Promise.all([\n import('./base/loading-spinner.js'),\n import('./base/success-message.js'),\n import('./base/error-display.js'),\n import('./base/skeleton-loader.js'),\n import('./base/payment-form-skeleton.js'),\n import('./base/toast.js'),\n import('./payment-form.js'),\n ]);\n\n // Verify all components are registered\n const components = [\n 'recur-loading-spinner',\n 'recur-success-message',\n 'recur-error-display',\n 'recur-skeleton-loader',\n 'recur-payment-form-skeleton',\n 'recur-toast',\n 'recur-toast-container',\n 'recur-payment-form',\n ];\n\n const unregistered = components.filter((name) => !customElements.get(name));\n\n if (unregistered.length > 0) {\n console.warn(\n `[Recur SDK] The following components are not registered:`,\n unregistered\n );\n }\n}\n\n// Auto-register on import (browser only)\nif (typeof window !== 'undefined') {\n registerComponents();\n}\n\n// Re-export types for TypeScript users (these don't cause SSR issues)\nexport type { RecurLoadingSpinner } from './base/loading-spinner.js';\nexport type { RecurSuccessMessage } from './base/success-message.js';\nexport type { RecurErrorDisplay } from './base/error-display.js';\nexport type { RecurSkeletonLoader } from './base/skeleton-loader.js';\nexport type { RecurPaymentFormSkeleton } from './base/payment-form-skeleton.js';\nexport type { RecurToast, RecurToastContainer } from './base/toast.js';\nexport type { RecurPaymentForm } from './payment-form.js';\n","/**\n * Vanilla SDK Core\n *\n * Core API logic for subscription checkout\n */\n\nimport type { VanillaRecurConfig, VanillaCheckoutOptions, CheckoutResult, CheckoutError, PlansResult } from './types.js';\n\nexport class RecurCore {\n private config: VanillaRecurConfig;\n\n constructor(config: VanillaRecurConfig) {\n if (!config.publishableKey) {\n throw new Error('publishableKey is required');\n }\n\n // Use configured baseUrl or default to production API\n this.config = {\n baseUrl: 'https://api.recur.tw',\n ...config,\n };\n }\n\n /**\n * Call the subscription API\n */\n async createSubscription(options: VanillaCheckoutOptions): Promise<CheckoutResult> {\n const { planId, customerName, customerEmail, customerPhone } = options;\n\n if (!planId) {\n throw new Error('planId is required');\n }\n\n const response = await fetch(`${this.config.baseUrl}/v1/subscriptions`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'X-Recur-Publishable-Key': this.config.publishableKey,\n },\n body: JSON.stringify({\n planId,\n customerName,\n customerEmail,\n customerPhone,\n }),\n });\n\n if (!response.ok) {\n const errorData = await response.json().catch(() => ({}));\n const error: CheckoutError = {\n code: errorData.error || 'CHECKOUT_FAILED',\n message: errorData.message || 'Failed to initiate checkout',\n details: errorData,\n };\n throw error;\n }\n\n return await response.json();\n }\n\n /**\n * Fetch available subscription plans\n */\n async fetchPlans(): Promise<PlansResult> {\n const response = await fetch(`${this.config.baseUrl}/v1/plans`, {\n method: 'GET',\n headers: {\n 'Content-Type': 'application/json',\n 'X-Recur-Publishable-Key': this.config.publishableKey,\n },\n });\n\n if (!response.ok) {\n const errorData = await response.json().catch(() => ({}));\n const error: CheckoutError = {\n code: errorData.error || 'FETCH_PLANS_FAILED',\n message: errorData.message || 'Failed to fetch plans',\n details: errorData,\n };\n throw error;\n }\n\n return await response.json();\n }\n\n getConfig(): VanillaRecurConfig {\n return { ...this.config };\n }\n}\n","/**\n * Vanilla SDK Modal\n *\n * Modal window for payment flow\n */\n\nexport class RecurModal {\n private overlay: HTMLDivElement | null = null;\n private modal: HTMLDivElement | null = null;\n private iframe: HTMLIFrameElement | null = null;\n private onClose?: () => void;\n\n constructor(onClose?: () => void) {\n this.onClose = onClose;\n }\n\n /**\n * Open modal with payment URL\n */\n open(paymentUrl: string): void {\n // Create overlay\n this.overlay = document.createElement('div');\n this.overlay.style.cssText = `\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(0, 0, 0, 0.5);\n z-index: 9998;\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 20px;\n `;\n\n // Create modal container\n this.modal = document.createElement('div');\n this.modal.style.cssText = `\n position: relative;\n background: white;\n border-radius: 8px;\n width: 100%;\n max-width: 600px;\n height: 90vh;\n max-height: 700px;\n box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);\n display: flex;\n flex-direction: column;\n overflow: hidden;\n z-index: 9999;\n `;\n\n // Create close button\n const closeButton = document.createElement('button');\n closeButton.innerHTML = '✕';\n closeButton.style.cssText = `\n position: absolute;\n top: 12px;\n right: 12px;\n width: 32px;\n height: 32px;\n border: none;\n background: rgba(0, 0, 0, 0.1);\n border-radius: 50%;\n cursor: pointer;\n font-size: 18px;\n line-height: 1;\n z-index: 10000;\n transition: background 0.2s;\n color: #333;\n `;\n closeButton.onmouseover = () => {\n closeButton.style.background = 'rgba(0, 0, 0, 0.2)';\n };\n closeButton.onmouseout = () => {\n closeButton.style.background = 'rgba(0, 0, 0, 0.1)';\n };\n closeButton.onclick = () => this.close();\n\n // Create iframe\n this.iframe = document.createElement('iframe');\n this.iframe.src = paymentUrl;\n this.iframe.style.cssText = `\n width: 100%;\n height: 100%;\n border: none;\n border-radius: 8px;\n `;\n\n // Allow payment provider features\n this.iframe.setAttribute('allow', 'payment');\n this.iframe.setAttribute('sandbox', 'allow-same-origin allow-scripts allow-forms allow-popups allow-top-navigation');\n\n // Assemble\n this.modal.appendChild(closeButton);\n this.modal.appendChild(this.iframe);\n this.overlay.appendChild(this.modal);\n document.body.appendChild(this.overlay);\n\n // Prevent body scroll\n document.body.style.overflow = 'hidden';\n\n // Close on overlay click\n this.overlay.onclick = (e) => {\n if (e.target === this.overlay) {\n this.close();\n }\n };\n\n // Listen for payment completion (via postMessage)\n window.addEventListener('message', this.handleMessage);\n }\n\n /**\n * Close modal\n */\n close(): void {\n if (this.overlay && this.overlay.parentNode) {\n this.overlay.parentNode.removeChild(this.overlay);\n }\n\n // Restore body scroll\n document.body.style.overflow = '';\n\n // Clean up\n this.overlay = null;\n this.modal = null;\n this.iframe = null;\n\n // Remove message listener\n window.removeEventListener('message', this.handleMessage);\n\n // Call onClose callback\n if (this.onClose) {\n this.onClose();\n }\n }\n\n /**\n * Handle postMessage from iframe\n */\n private handleMessage = (event: MessageEvent): void => {\n // Only accept messages from same origin\n if (event.origin !== window.location.origin) {\n return;\n }\n\n // Check for payment completion signals\n if (event.data?.type === 'RECUR_PAYMENT_COMPLETE') {\n this.close();\n } else if (event.data?.type === 'RECUR_PAYMENT_CANCEL') {\n this.close();\n }\n };\n}\n","/**\n * Vanilla SDK iframe Embedding\n *\n * Embed payment flow in an iframe\n */\n\nexport class RecurIframe {\n private container: HTMLElement;\n private iframe: HTMLIFrameElement | null = null;\n private onClose?: () => void;\n\n constructor(container: string | HTMLElement, onClose?: () => void) {\n if (typeof container === 'string') {\n const element = document.querySelector<HTMLElement>(container);\n if (!element) {\n throw new Error(`Container element not found: ${container}`);\n }\n this.container = element;\n } else {\n this.container = container;\n }\n\n this.onClose = onClose;\n }\n\n /**\n * Embed iframe with payment URL\n */\n embed(paymentUrl: string): void {\n // Clear existing content\n this.container.innerHTML = '';\n\n // Create iframe\n this.iframe = document.createElement('iframe');\n this.iframe.src = paymentUrl;\n this.iframe.style.cssText = `\n width: 100%;\n height: 600px;\n border: 1px solid #e5e7eb;\n border-radius: 8px;\n `;\n\n // Allow payment provider features\n this.iframe.setAttribute('allow', 'payment');\n this.iframe.setAttribute('sandbox', 'allow-same-origin allow-scripts allow-forms allow-popups allow-top-navigation');\n\n // Append to container\n this.container.appendChild(this.iframe);\n\n // Listen for payment completion\n window.addEventListener('message', this.handleMessage);\n }\n\n /**\n * Remove iframe\n */\n remove(): void {\n if (this.iframe && this.iframe.parentNode) {\n this.iframe.parentNode.removeChild(this.iframe);\n }\n\n this.iframe = null;\n\n // Remove message listener\n window.removeEventListener('message', this.handleMessage);\n\n // Call onClose callback\n if (this.onClose) {\n this.onClose();\n }\n }\n\n /**\n * Handle postMessage from iframe\n */\n private handleMessage = (event: MessageEvent): void => {\n // Only accept messages from same origin\n if (event.origin !== window.location.origin) {\n return;\n }\n\n // Check for payment completion signals\n if (event.data?.type === 'RECUR_PAYMENT_COMPLETE') {\n this.remove();\n } else if (event.data?.type === 'RECUR_PAYMENT_CANCEL') {\n this.remove();\n }\n };\n}\n","/**\n * Embedded Checkout\n *\n * Renders a complete checkout form with embedded credit card fields\n * Powered by PAYUNi SDK for secure card input\n */\n\nimport type { VanillaRecurConfig, CheckoutError, CheckoutResult } from './types.js';\n\nexport interface EmbeddedCheckoutOptions {\n planId: string;\n container: string | HTMLElement;\n customerEmail?: string;\n customerName?: string;\n customerPhone?: string;\n onSuccess?: (result: CheckoutResult) => void;\n onError?: (error: CheckoutError) => void;\n}\n\ninterface InitCheckoutResponse {\n sdkToken: string;\n env: 'S' | 'P';\n timestamp: number;\n plan?: {\n id: string;\n name: string;\n price: number;\n billingPeriod: string;\n description?: string;\n trialDays?: number;\n };\n}\n\nexport class EmbeddedCheckout {\n private config: VanillaRecurConfig;\n private options: EmbeddedCheckoutOptions;\n private container: HTMLElement;\n private sdkToken: string | null = null;\n private sdkEnv: 'S' | 'P' = 'S';\n private payuniSDK: any = null;\n private isFormValid: boolean = false;\n\n constructor(config: VanillaRecurConfig, options: EmbeddedCheckoutOptions) {\n this.config = config;\n this.options = options;\n\n // Resolve container\n if (typeof options.container === 'string') {\n const element = document.querySelector(options.container);\n if (!element) {\n throw new Error(`Container not found: ${options.container}`);\n }\n this.container = element as HTMLElement;\n } else {\n this.container = options.container;\n }\n }\n\n /**\n * Initialize and render the checkout form\n */\n async render(): Promise<void> {\n try {\n // 1. Fetch SDK token and plan info\n await this.initCheckout();\n\n // 2. Render HTML structure\n this.renderHTML();\n\n // 3. Load PAYUNi SDK script\n await this.loadPayUniSDK();\n\n // 4. Initialize PAYUNi SDK with card fields\n await this.initPayUniSDK();\n\n // 5. Setup form submission\n this.setupFormSubmit();\n } catch (error: any) {\n this.handleError(error);\n }\n }\n\n /**\n * Initialize checkout session\n */\n private async initCheckout(): Promise<void> {\n // Auto-detect base URL based on environment\n const getDefaultBaseUrl = () => {\n // Development environment: use relative path /api\n if (typeof window !== 'undefined') {\n const hostname = window.location.hostname;\n if (hostname === 'localhost' ||\n hostname.includes('.test') ||\n hostname.includes('.local') ||\n hostname === '127.0.0.1') {\n return `${window.location.protocol}//${window.location.host}/api`;\n }\n }\n\n // Production environment: use API subdomain\n return 'https://api.recur.tw';\n };\n\n const baseUrl = this.config.baseUrl || getDefaultBaseUrl();\n\n const response = await fetch(`${baseUrl}/v1/checkout/init`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'X-Recur-Publishable-Key': this.config.publishableKey,\n },\n body: JSON.stringify({\n planId: this.options.planId,\n iframeDomain: window.location.origin,\n }),\n });\n\n if (!response.ok) {\n const error = await response.json().catch(() => ({}));\n throw new Error(error.error || 'Failed to initialize checkout');\n }\n\n const data: InitCheckoutResponse = await response.json();\n this.sdkToken = data.sdkToken;\n this.sdkEnv = data.env;\n }\n\n /**\n * Render checkout form HTML\n */\n private renderHTML(): void {\n this.container.innerHTML = `\n <div class=\"recur-embedded-checkout\" style=\"max-width: 400px; margin: 0 auto; padding: 20px; font-family: system-ui, -apple-system, sans-serif;\">\n <div class=\"recur-checkout-header\" style=\"margin-bottom: 24px;\">\n <h2 style=\"margin: 0 0 8px 0; font-size: 20px; font-weight: 600; color: #111;\">Subscribe</h2>\n <p style=\"margin: 0; font-size: 14px; color: #666;\">Complete your subscription</p>\n </div>\n\n <form id=\"recur-checkout-form\" style=\"display: flex; flex-direction: column; gap: 16px;\">\n <div class=\"recur-field\">\n <label style=\"display: block; margin-bottom: 6px; font-size: 14px; font-weight: 500; color: #374151;\">Email</label>\n <input\n type=\"email\"\n id=\"recur-email\"\n required\n value=\"${this.options.customerEmail || ''}\"\n style=\"width: 100%; padding: 8px 12px; border: 1px solid #d1d5db; border-radius: 6px; font-size: 14px; outline: none; transition: border-color 0.2s;\"\n onfocus=\"this.style.borderColor='#3b82f6'\"\n onblur=\"this.style.borderColor='#d1d5db'\"\n />\n </div>\n\n <div class=\"recur-field\">\n <label style=\"display: block; margin-bottom: 6px; font-size: 14px; font-weight: 500; color: #374151;\">Name</label>\n <input\n type=\"text\"\n id=\"recur-name\"\n required\n value=\"${this.options.customerName || ''}\"\n style=\"width: 100%; padding: 8px 12px; border: 1px solid #d1d5db; border-radius: 6px; font-size: 14px; outline: none; transition: border-color 0.2s;\"\n onfocus=\"this.style.borderColor='#3b82f6'\"\n onblur=\"this.style.borderColor='#d1d5db'\"\n />\n </div>\n\n <div class=\"recur-field\">\n <label style=\"display: block; margin-bottom: 6px; font-size: 14px; font-weight: 500; color: #374151;\">Phone (Optional)</label>\n <input\n type=\"tel\"\n id=\"recur-phone\"\n value=\"${this.options.customerPhone || ''}\"\n style=\"width: 100%; padding: 8px 12px; border: 1px solid #d1d5db; border-radius: 6px; font-size: 14px; outline: none; transition: border-color 0.2s;\"\n onfocus=\"this.style.borderColor='#3b82f6'\"\n onblur=\"this.style.borderColor='#d1d5db'\"\n />\n </div>\n\n <div class=\"recur-divider\" style=\"height: 1px; background: #e5e7eb; margin: 8px 0;\"></div>\n\n <div class=\"recur-field\">\n <label style=\"display: block; margin-bottom: 6px; font-size: 14px; font-weight: 500; color: #374151;\">Card Number</label>\n <div id=\"recur-card-number\" style=\"padding: 8px 12px; border: 1px solid #d1d5db; border-radius: 6px; min-height: 38px;\"></div>\n </div>\n\n <div style=\"display: grid; grid-template-columns: 1fr 1fr; gap: 12px;\">\n <div class=\"recur-field\">\n <label style=\"display: block; margin-bottom: 6px; font-size: 14px; font-weight: 500; color: #374151;\">Expiry</label>\n <div id=\"recur-card-exp\" style=\"padding: 8px 12px; border: 1px solid #d1d5db; border-radius: 6px; min-height: 38px;\"></div>\n </div>\n <div class=\"recur-field\">\n <label style=\"display: block; margin-bottom: 6px; font-size: 14px; font-weight: 500; color: #374151;\">CVC</label>\n <div id=\"recur-card-cvc\" style=\"padding: 8px 12px; border: 1px solid #d1d5db; border-radius: 6px; min-height: 38px;\"></div>\n </div>\n </div>\n\n <div id=\"recur-error\" style=\"display: none; padding: 12px; background: #fef2f2; border: 1px solid #fecaca; border-radius: 6px; color: #dc2626; font-size: 14px;\"></div>\n\n <button\n type=\"submit\"\n id=\"recur-submit-btn\"\n style=\"width: 100%; padding: 12px; background: #3b82f6; color: white; border: none; border-radius: 6px; font-size: 16px; font-weight: 500; cursor: pointer; transition: background 0.2s;\"\n onmouseover=\"this.style.background='#2563eb'\"\n onmouseout=\"this.style.background='#3b82f6'\"\n disabled\n >\n Subscribe Now\n </button>\n\n <p style=\"margin: 12px 0 0 0; text-align: center; font-size: 12px; color: #9ca3af;\">\n Secure payment powered by PAYUNi\n </p>\n </form>\n </div>\n `;\n }\n\n /**\n * Load PAYUNi SDK script dynamically\n */\n private async loadPayUniSDK(): Promise<void> {\n return new Promise((resolve, reject) => {\n // Check if already loaded\n if ((window as any).UniPayment) {\n resolve();\n return;\n }\n\n const script = document.createElement('script');\n script.src = this.sdkEnv === 'P'\n ? 'https://vendor.payuni.com.tw/sdk/uni-payment.js'\n : 'https://sandbox-vendor.payuni.com.tw/sdk/uni-payment.js';\n script.async = true;\n script.onload = () => resolve();\n script.onerror = () => reject(new Error('Failed to load PAYUNi SDK'));\n document.head.appendChild(script);\n });\n }\n\n /**\n * Initialize PAYUNi SDK with credit card fields\n */\n private async initPayUniSDK(): Promise<void> {\n if (!this.sdkToken) {\n throw new Error('SDK token not initialized');\n }\n\n const UniPayment = (window as any).UniPayment;\n if (!UniPayment) {\n throw new Error('PAYUNi SDK not loaded');\n }\n\n const options = {\n env: this.sdkEnv,\n useInst: false,\n elements: {\n CardNo: 'recur-card-number',\n CardExp: 'recur-card-exp',\n CardCvc: 'recur-card-cvc',\n },\n style: {\n color: '#111827',\n errorColor: '#dc2626',\n fontSize: '14px',\n fontWeight: '400',\n lineHeight: '24px',\n },\n };\n\n this.payuniSDK = UniPayment.createSession(this.sdkToken, options);\n await this.payuniSDK.start();\n\n // Listen for card validation updates\n this.payuniSDK.onUpdate((update: any) => {\n const status = update?.status;\n if (status) {\n this.isFormValid = status.CardNo === true && status.CardExp === true && status.CardCvc === true;\n this.updateSubmitButton();\n }\n });\n }\n\n /**\n * Update submit button state\n */\n private updateSubmitButton(): void {\n const submitBtn = document.getElementById('recur-submit-btn') as HTMLButtonElement;\n if (submitBtn) {\n submitBtn.disabled = !this.isFormValid;\n submitBtn.style.opacity = this.isFormValid ? '1' : '0.5';\n submitBtn.style.cursor = this.isFormValid ? 'pointer' : 'not-allowed';\n }\n }\n\n /**\n * Setup form submission handler\n */\n private setupFormSubmit(): void {\n const form = document.getElementById('recur-checkout-form') as HTMLFormElement;\n if (!form) return;\n\n form.addEventListener('submit', async (e) => {\n e.preventDefault();\n await this.handleSubmit();\n });\n }\n\n /**\n * Handle form submission\n */\n private async handleSubmit(): Promise<void> {\n try {\n this.showLoading(true);\n this.hideError();\n\n // Get form data\n const email = (document.getElementById('recur-email') as HTMLInputElement).value;\n const name = (document.getElementById('recur-name') as HTMLInputElement).value;\n const phone = (document.getElementById('recur-phone') as HTMLInputElement).value;\n\n // Validate\n if (!email || !name) {\n throw new Error('Please fill in all required fields');\n }\n\n // Bind card data to SDK token\n if (!this.payuniSDK) {\n throw new Error('Payment system not initialized');\n }\n\n const cardResult = await this.payuniSDK.getTradeResult();\n if (cardResult.Status !== 'SUCCESS') {\n throw new Error(cardResult.Message || 'Card validation failed');\n }\n\n // Create subscription\n // Auto-detect base URL based on environment\n const getDefaultBaseUrl = () => {\n // Development environment: use relative path /api\n if (typeof window !== 'undefined') {\n const hostname = window.location.hostname;\n if (hostname === 'localhost' ||\n hostname.includes('.test') ||\n hostname.includes('.local') ||\n hostname === '127.0.0.1') {\n return `${window.location.protocol}//${window.location.host}/api`;\n }\n }\n\n // Production environment: use API subdomain\n return 'https://api.recur.tw';\n };\n\n const baseUrl = this.config.baseUrl || getDefaultBaseUrl();\n const response = await fetch(`${baseUrl}/v1/subscriptions`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'X-Recur-Publishable-Key': this.config.publishableKey,\n },\n body: JSON.stringify({\n planId: this.options.planId,\n customerEmail: email,\n customerName: name,\n customerPhone: phone || undefined,\n }),\n });\n\n if (!response.ok) {\n const error = await response.json().catch(() => ({}));\n throw new Error(error.error || 'Failed to create subscription');\n }\n\n const result: CheckoutResult = await response.json();\n\n // Call success callback\n if (this.options.onSuccess) {\n this.options.onSuccess(result);\n }\n\n // Show success message\n this.showSuccess();\n } catch (error: any) {\n this.handleError(error);\n } finally {\n this.showLoading(false);\n }\n }\n\n /**\n * Show loading state\n */\n private showLoading(loading: boolean): void {\n const submitBtn = document.getElementById('recur-submit-btn') as HTMLButtonElement;\n if (submitBtn) {\n submitBtn.disabled = loading;\n submitBtn.textContent = loading ? 'Processing...' : 'Subscribe Now';\n }\n }\n\n /**\n * Show error message\n */\n private showError(message: string): void {\n const errorDiv = document.getElementById('recur-error');\n if (errorDiv) {\n errorDiv.textContent = message;\n errorDiv.style.display = 'block';\n }\n }\n\n /**\n * Hide error message\n */\n private hideError(): void {\n const errorDiv = document.getElementById('recur-error');\n if (errorDiv) {\n errorDiv.style.display = 'none';\n }\n }\n\n /**\n * Show success message\n */\n private showSuccess(): void {\n // Clear container\n this.container.innerHTML = '';\n\n // Use Web Component instead of innerHTML\n const successMessage = document.createElement('recur-success-message');\n successMessage.setAttribute('title', 'Subscription Complete!');\n successMessage.setAttribute('message', 'Thank you for subscribing. You will receive a confirmation email shortly.');\n\n this.container.appendChild(successMessage);\n }\n\n /**\n * Handle errors\n */\n private handleError(error: any): void {\n const message = error?.message || 'An error occurred';\n this.showError(message);\n\n const checkoutError: CheckoutError = {\n code: 'CHECKOUT_ERROR',\n message,\n };\n\n if (this.options.onError) {\n this.options.onError(checkoutError);\n }\n }\n}\n","/**\n * Recur Elements SDK\n *\n * A Stripe Elements-like SDK for embedding credit card forms\n * Allows rendering card form immediately with just publishable_key\n * without needing a checkout session first\n *\n * Usage:\n * ```typescript\n * // 1. Initialize with publishable key\n * const elements = new RecurElements('pk_test_xxx');\n *\n * // 2. Mount card form to container\n * await elements.mount('#card-container');\n *\n * // 3. Listen for events\n * elements.on('ready', (data) => console.log('Ready:', data));\n * elements.on('change', (data) => console.log('Valid:', data.valid));\n *\n * // 4. On form submit, tokenize and confirm payment\n * const token = await elements.tokenize();\n * const result = await elements.confirmPayment({\n * productId: 'prod_xxx',\n * email: 'user@example.com',\n * });\n * ```\n */\n\nexport interface RecurElementsConfig {\n publishableKey: string\n baseUrl?: string\n embedUrl?: string\n}\n\nexport interface ElementsReadyData {\n sessionId: string\n timestamp: number\n creditToken: string\n}\n\nexport interface CardChangeData {\n valid: boolean\n}\n\nexport interface CardTokenizedData {\n cardToken: string\n timestamp: number\n sessionId: string\n}\n\nexport interface ConfirmPaymentOptions {\n productId: string\n email: string\n name?: string\n phone?: string\n metadata?: Record<string, any>\n}\n\nexport interface PaymentResult {\n status: 'success' | 'failed'\n checkoutId?: string\n subscriptionId?: string\n orderId?: string\n message?: string\n canRetry?: boolean\n}\n\ntype EventType = 'ready' | 'change' | 'tokenized' | 'error'\ntype EventHandler = (data: any) => void\n\nexport class RecurElements {\n private publishableKey: string\n private baseUrl: string\n private embedUrl: string\n private iframe: HTMLIFrameElement | null = null\n private container: HTMLElement | null = null\n\n // Session data (received from iframe after ready)\n private sessionId: string | null = null\n private timestamp: number | null = null\n private creditToken: string | null = null\n\n // Card token (received after tokenize)\n private cardToken: string | null = null\n private cardTimestamp: number | null = null\n\n // Event handlers\n private eventHandlers: Map<EventType, Set<EventHandler>> = new Map()\n\n constructor(config: string | RecurElementsConfig) {\n if (typeof config === 'string') {\n this.publishableKey = config\n this.baseUrl = this.getDefaultBaseUrl()\n this.embedUrl = this.getDefaultEmbedUrl()\n } else {\n this.publishableKey = config.publishableKey\n this.baseUrl = config.baseUrl || this.getDefaultBaseUrl()\n this.embedUrl = config.embedUrl || this.getDefaultEmbedUrl()\n }\n\n // Setup message listener\n window.addEventListener('message', this.handleMessage.bind(this))\n }\n\n /**\n * Mount card form to container\n */\n async mount(container: string | HTMLElement): Promise<void> {\n const el = typeof container === 'string'\n ? document.querySelector(container)\n : container\n\n if (!el) {\n throw new Error(`Container not found: ${container}`)\n }\n\n this.container = el as HTMLElement\n\n // Create iframe\n this.iframe = document.createElement('iframe')\n this.iframe.src = `${this.embedUrl}/elements?key=${encodeURIComponent(this.publishableKey)}`\n this.iframe.style.cssText = 'width:100%;border:none;min-height:200px;'\n this.iframe.allow = 'payment'\n\n // Clear container and append iframe\n this.container.innerHTML = ''\n this.container.appendChild(this.iframe)\n\n // Wait for ready event\n return new Promise((resolve, reject) => {\n const timeout = setTimeout(() => {\n reject(new Error('Elements initialization timeout'))\n }, 30000) // 30 second timeout\n\n const readyHandler = () => {\n clearTimeout(timeout)\n this.off('ready', readyHandler)\n resolve()\n }\n\n const errorHandler = (error: any) => {\n clearTimeout(timeout)\n this.off('error', errorHandler)\n reject(new Error(error.message || 'Elements initialization failed'))\n }\n\n this.on('ready', readyHandler)\n this.on('error', errorHandler)\n })\n }\n\n /**\n * Register event handler\n */\n on(event: EventType, handler: EventHandler): void {\n if (!this.eventHandlers.has(event)) {\n this.eventHandlers.set(event, new Set())\n }\n this.eventHandlers.get(event)!.add(handler)\n }\n\n /**\n * Remove event handler\n */\n off(event: EventType, handler: EventHandler): void {\n const handlers = this.eventHandlers.get(event)\n if (handlers) {\n handlers.delete(handler)\n }\n }\n\n /**\n * Emit event to handlers\n */\n private emit(event: EventType, data?: any): void {\n const handlers = this.eventHandlers.get(event)\n if (handlers) {\n handlers.forEach(handler => handler(data))\n }\n }\n\n /**\n * Handle messages from iframe\n */\n private handleMessage(event: MessageEvent): void {\n // Only accept messages from our embed domain\n if (!this.isValidOrigin(event.origin)) {\n return\n }\n\n const { type, data } = event.data || {}\n\n switch (type) {\n case 'RECUR_ELEMENTS_READY':\n this.sessionId = data.sessionId\n this.timestamp = data.timestamp\n this.creditToken = data.creditToken\n this.emit('ready', data)\n break\n\n case 'RECUR_CARD_VALID':\n this.emit('change', { valid: data.valid })\n break\n\n case 'RECUR_CARD_TOKENIZED':\n this.cardToken = data.cardToken\n this.cardTimestamp = data.timestamp\n this.emit('tokenized', data)\n break\n\n case 'RECUR_PAYMENT_ERROR':\n this.emit('error', data)\n break\n\n case 'RECUR_RESIZE':\n // Auto-resize iframe to match content height\n if (this.iframe && data?.height) {\n this.iframe.style.height = `${data.height}px`\n }\n break\n }\n }\n\n /**\n * Check if message origin is valid\n */\n private isValidOrigin(origin: string): boolean {\n try {\n const embedOrigin = new URL(this.embedUrl).origin\n return origin === embedOrigin\n } catch {\n return false\n }\n }\n\n /**\n * Trigger tokenization\n */\n async tokenize(): Promise<CardTokenizedData> {\n if (!this.iframe || !this.iframe.contentWindow) {\n throw new Error('Elements not mounted')\n }\n\n // Send tokenize message to iframe\n const embedOrigin = new URL(this.embedUrl).origin\n this.iframe.contentWindow.postMessage({ type: 'RECUR_TOKENIZE' }, embedOrigin)\n\n // Wait for tokenized event\n return new Promise((resolve, reject) => {\n const timeout = setTimeout(() => {\n reject(new Error('Tokenization timeout'))\n }, 60000) // 60 second timeout\n\n const handler = (data: CardTokenizedData) => {\n clearTimeout(timeout)\n this.off('tokenized', handler)\n resolve(data)\n }\n\n const errorHandler = (error: any) => {\n clearTimeout(timeout)\n this.off('error', errorHandler)\n reject(new Error(error.message || 'Tokenization failed'))\n }\n\n this.on('tokenized', handler)\n this.on('error', errorHandler)\n })\n }\n\n /**\n * Confirm payment\n */\n async confirmPayment(options: ConfirmPaymentOptions): Promise<PaymentResult> {\n if (!this.sessionId || !this.cardToken) {\n throw new Error('Card not tokenized. Call tokenize() first.')\n }\n\n const response = await fetch(`${this.baseUrl}/api/v1/elements/confirm`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'X-Recur-Publishable-Key': this.publishableKey,\n },\n body: JSON.stringify({\n sessionId: this.sessionId,\n sdkToken: this.cardToken, // CreditHash from PAYUNi\n timestamp: this.cardTimestamp,\n creditToken: this.creditToken,\n productId: options.productId,\n email: options.email,\n name: options.name,\n phone: options.phone,\n metadata: options.metadata,\n }),\n })\n\n const result = await response.json()\n\n if (!response.ok) {\n return {\n status: 'failed',\n message: result.error || result.message || 'Payment failed',\n canRetry: result.canRetry ?? true,\n }\n }\n\n return result as PaymentResult\n }\n\n /**\n * Get default base URL based on environment\n */\n private getDefaultBaseUrl(): string {\n if (typeof window === 'undefined') {\n return 'https://app.recur.tw'\n }\n\n const hostname = window.location.hostname\n if (hostname === 'localhost' ||\n hostname.includes('.test') ||\n hostname.includes('.local') ||\n hostname === '127.0.0.1') {\n return `${window.location.protocol}//${hostname}:3000`\n }\n\n return 'https://app.recur.tw'\n }\n\n /**\n * Get default embed URL based on environment\n */\n private getDefaultEmbedUrl(): string {\n if (typeof window === 'undefined') {\n return 'https://embed.recur.tw'\n }\n\n const hostname = window.location.hostname\n if (hostname === 'localhost' ||\n hostname.includes('.test') ||\n hostname.includes('.local') ||\n hostname === '127.0.0.1') {\n return `${window.location.protocol}//${hostname}:3002`\n }\n\n return 'https://embed.recur.tw'\n }\n\n /**\n * Destroy elements and cleanup\n */\n destroy(): void {\n if (this.iframe && this.iframe.parentNode) {\n this.iframe.parentNode.removeChild(this.iframe)\n }\n this.iframe = null\n this.container = null\n this.sessionId = null\n this.timestamp = null\n this.creditToken = null\n this.cardToken = null\n this.cardTimestamp = null\n this.eventHandlers.clear()\n window.removeEventListener('message', this.handleMessage.bind(this))\n }\n}\n\n/**\n * Create RecurElements instance\n */\nexport function createElements(config: string | RecurElementsConfig): RecurElements {\n return new RecurElements(config)\n}\n\nexport default RecurElements\n"],"mappings":"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","names":["loading_spinner_exports","__export","RecurLoadingSpinner","init_loading_spinner","__esmMin","name","oldValue","newValue","sizeAttr","size","success_message_exports","__export","RecurSuccessMessage","init_success_message","__esmMin","name","oldValue","newValue","error_display_exports","__export","RecurErrorDisplay","init_error_display","__esmMin","name","oldValue","newValue","skeleton_loader_exports","__export","RecurSkeletonLoader","init_skeleton_loader","__esmMin","name","oldValue","newValue","typeAttr","content","payment_form_skeleton_exports","__export","RecurPaymentFormSkeleton","init_payment_form_skeleton","__esmMin","toast_exports","__export","RecurToast","RecurToastContainer","_RecurToastContainer","init_toast","__esmMin","typeAttr","durationAttr","duration","color","e","message","type","container","toast","__publicField","payment_form_exports","__export","RecurPaymentForm","init_payment_form","__esmMin","__publicField","submitBtn","newBtn","name","oldValue","newValue","styleElement","idSpecificStyles","orderSummary","customerInfo","cardFields","actions","existingSection","newSection","section","planName","amount","billingPeriod","periodText","customerName","customerEmail","sdkToken","environment","cardNoEl","cardExpEl","cardCvcEl","paymentSession","startError","data","allFieldsComplete","error","e","email","phone","emailInput","nameInput","phoneInput","loading","message","errorContainer","errorDisplay","resolve","observer","vanilla_exports","__export","RecurCheckout","RecurElements","createElements","vanilla_default","init","registerComponents","unregistered","name","RecurCore","config","__publicField","options","planId","customerName","customerEmail","customerPhone","response","errorData","RecurModal","onClose","__publicField","event","paymentUrl","closeButton","e","RecurIframe","container","onClose","__publicField","event","element","paymentUrl","EmbeddedCheckout","config","options","__publicField","element","error","getDefaultBaseUrl","hostname","baseUrl","response","data","resolve","reject","script","UniPayment","update","status","submitBtn","form","e","email","name","phone","cardResult","result","loading","message","errorDiv","successMessage","checkoutError","RecurElements","config","__publicField","container","el","resolve","reject","timeout","readyHandler","errorHandler","error","event","handler","handlers","data","type","origin","embedOrigin","options","response","result","hostname","createElements","RecurCheckout","config","__publicField","RecurCore","options","EmbeddedCheckout","result","mode","paymentUrl","error","onClose","RecurModal","container","RecurIframe","mockHTML","RecurToast","html","overlay","closeBtn","iframe","event","containerEl","init","vanilla_default","RecurElements","createElements"]}
|
|
1
|
+
{"version":3,"sources":["../src/components/base/loading-spinner.ts","../src/components/base/success-message.ts","../src/components/base/error-display.ts","../src/components/base/skeleton-loader.ts","../src/components/base/payment-form-skeleton.ts","../src/components/base/toast.ts","../src/components/payment-form.ts","../src/vanilla/index.ts","../src/components/index.ts","../src/vanilla/core.ts","../src/vanilla/modal.ts","../src/vanilla/iframe.ts","../src/vanilla/embedded-checkout.ts","../src/vanilla/elements.ts"],"sourcesContent":["/**\n * Recur Loading Spinner Web Component\n *\n * A reusable loading spinner with customizable message and size.\n * Eliminates code duplication across the SDK.\n */\n\nexport class RecurLoadingSpinner extends HTMLElement {\n static get observedAttributes() {\n return ['message', 'size'];\n }\n\n constructor() {\n super();\n this.attachShadow({ mode: 'open' });\n }\n\n connectedCallback() {\n this.render();\n }\n\n attributeChangedCallback(name: string, oldValue: string, newValue: string) {\n if (oldValue !== newValue) {\n this.render();\n }\n }\n\n private get message(): string {\n return this.getAttribute('message') || '正在處理訂閱...';\n }\n\n private get size(): 'small' | 'medium' | 'large' {\n const sizeAttr = this.getAttribute('size');\n if (sizeAttr === 'small' || sizeAttr === 'large') return sizeAttr;\n return 'medium';\n }\n\n private getSizeValue(): number {\n const sizes = {\n small: 24,\n medium: 40,\n large: 56,\n };\n return sizes[this.size];\n }\n\n private render() {\n const size = this.getSizeValue();\n\n this.shadowRoot!.innerHTML = `\n <style>\n :host {\n display: block;\n text-align: center;\n }\n\n @keyframes recur-sdk-spin {\n 0% { transform: rotate(0deg); }\n 100% { transform: rotate(360deg); }\n }\n\n .recur-sdk__spinner {\n border: 3px solid #e5e7eb;\n border-top: 3px solid var(--recur-primary-color, #3b82f6);\n border-radius: 50%;\n width: ${size}px;\n height: ${size}px;\n animation: recur-sdk-spin 1s linear infinite;\n margin: 0 auto 16px;\n }\n\n .recur-sdk__loading-text {\n color: var(--recur-text-secondary, #6b7280);\n font-size: ${this.size === 'small' ? '12px' : '14px'};\n margin: 0;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n }\n\n /* 支持主題化 */\n :host([theme=\"dark\"]) .recur-sdk__spinner {\n border-color: #374151;\n border-top-color: #60a5fa;\n }\n\n :host([theme=\"dark\"]) .recur-sdk__loading-text {\n color: #9ca3af;\n }\n </style>\n\n <div class=\"recur-sdk__spinner\"></div>\n ${this.message ? `<p class=\"recur-sdk__loading-text\">${this.message}</p>` : ''}\n `;\n }\n}\n\n// 自動註冊組件(如果還未註冊)\nif (typeof window !== 'undefined' && !customElements.get('recur-loading-spinner')) {\n customElements.define('recur-loading-spinner', RecurLoadingSpinner);\n}\n\n// TypeScript 類型聲明\ndeclare global {\n interface HTMLElementTagNameMap {\n 'recur-loading-spinner': RecurLoadingSpinner;\n }\n}\n","/**\n * Recur Success Message Web Component\n *\n * A reusable success message display with icon and customizable text.\n */\n\nexport class RecurSuccessMessage extends HTMLElement {\n static get observedAttributes() {\n return ['title', 'message', 'icon'];\n }\n\n constructor() {\n super();\n this.attachShadow({ mode: 'open' });\n }\n\n connectedCallback() {\n this.render();\n }\n\n attributeChangedCallback(name: string, oldValue: string, newValue: string) {\n if (oldValue !== newValue) {\n this.render();\n }\n }\n\n private get successTitle(): string {\n return this.getAttribute('title') || 'Subscription Complete!';\n }\n\n private get successMessage(): string {\n return this.getAttribute('message') || 'Thank you for subscribing. Your payment has been processed successfully.';\n }\n\n private get showIcon(): boolean {\n return this.getAttribute('icon') !== 'false';\n }\n\n private render() {\n this.shadowRoot!.innerHTML = `\n <style>\n :host {\n display: block;\n text-align: center;\n padding: var(--recur-spacing-lg, 48px) var(--recur-spacing-md, 24px);\n }\n\n .recur-sdk__success-container {\n max-width: 400px;\n margin: 0 auto;\n }\n\n .recur-sdk__success-icon {\n width: 64px;\n height: 64px;\n margin: 0 auto 24px;\n background: var(--recur-success-bg, #d1fae5);\n border-radius: 50%;\n display: flex;\n align-items: center;\n justify-content: center;\n }\n\n .recur-sdk__success-icon svg {\n width: 32px;\n height: 32px;\n color: var(--recur-success-color, #10b981);\n }\n\n .recur-sdk__success-title {\n font-size: 24px;\n font-weight: 600;\n color: var(--recur-text-primary, #1f2937);\n margin: 0 0 12px 0;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n }\n\n .recur-sdk__success-message {\n font-size: 16px;\n color: var(--recur-text-secondary, #6b7280);\n margin: 0;\n line-height: 1.5;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n }\n\n /* Slot for custom actions */\n ::slotted(button), ::slotted(a) {\n margin-top: 24px;\n }\n\n /* Dark theme support */\n :host([theme=\"dark\"]) .recur-sdk__success-icon {\n background: #065f46;\n }\n\n :host([theme=\"dark\"]) .recur-sdk__success-icon svg {\n color: #34d399;\n }\n\n :host([theme=\"dark\"]) .recur-sdk__success-title {\n color: #f3f4f6;\n }\n\n :host([theme=\"dark\"]) .recur-sdk__success-message {\n color: #d1d5db;\n }\n </style>\n\n <div class=\"recur-sdk__success-container\">\n ${this.showIcon ? `\n <div class=\"recur-sdk__success-icon\">\n <svg fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M5 13l4 4L19 7\" />\n </svg>\n </div>\n ` : ''}\n <h2 class=\"recur-sdk__success-title\">${this.successTitle}</h2>\n <p class=\"recur-sdk__success-message\">${this.successMessage}</p>\n <slot></slot>\n </div>\n `;\n }\n}\n\n// 自動註冊組件\nif (typeof window !== 'undefined' && !customElements.get('recur-success-message')) {\n customElements.define('recur-success-message', RecurSuccessMessage);\n}\n\n// TypeScript 類型聲明\ndeclare global {\n interface HTMLElementTagNameMap {\n 'recur-success-message': RecurSuccessMessage;\n }\n}\n","/**\n * Recur Error Display Web Component\n *\n * A reusable error message display with customizable styling and dismissible option.\n */\n\nexport class RecurErrorDisplay extends HTMLElement {\n static get observedAttributes() {\n return ['error', 'dismissible'];\n }\n\n constructor() {\n super();\n this.attachShadow({ mode: 'open' });\n }\n\n connectedCallback() {\n this.render();\n }\n\n attributeChangedCallback(name: string, oldValue: string, newValue: string) {\n if (oldValue !== newValue) {\n this.render();\n }\n }\n\n private get error(): string {\n return this.getAttribute('error') || '';\n }\n\n private get isDismissible(): boolean {\n return this.getAttribute('dismissible') === 'true';\n }\n\n private handleDismiss() {\n this.dispatchEvent(new CustomEvent('dismiss', {\n bubbles: true,\n composed: true,\n }));\n this.remove();\n }\n\n private render() {\n if (!this.error) {\n this.shadowRoot!.innerHTML = '';\n return;\n }\n\n this.shadowRoot!.innerHTML = `\n <style>\n :host {\n display: block;\n margin: var(--recur-spacing-sm, 16px) 0;\n }\n\n .recur-sdk__error {\n display: flex;\n align-items: flex-start;\n gap: 12px;\n padding: 12px 16px;\n background: var(--recur-error-bg, #fee2e2);\n border: 1px solid var(--recur-error-border, #fecaca);\n border-radius: var(--recur-border-radius, 6px);\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n }\n\n .recur-sdk__error-icon {\n flex-shrink: 0;\n width: 20px;\n height: 20px;\n color: var(--recur-error-color, #dc2626);\n }\n\n .recur-sdk__error-content {\n flex: 1;\n }\n\n .recur-sdk__error-message {\n margin: 0;\n font-size: 14px;\n color: var(--recur-error-text, #991b1b);\n line-height: 1.5;\n }\n\n .recur-sdk__error-dismiss {\n flex-shrink: 0;\n background: none;\n border: none;\n padding: 0;\n width: 20px;\n height: 20px;\n cursor: pointer;\n color: var(--recur-error-color, #dc2626);\n opacity: 0.7;\n transition: opacity 0.2s;\n }\n\n .recur-sdk__error-dismiss:hover {\n opacity: 1;\n }\n\n .recur-sdk__error-dismiss:focus {\n outline: 2px solid var(--recur-error-color, #dc2626);\n outline-offset: 2px;\n border-radius: 2px;\n }\n\n /* Dark theme support */\n :host([theme=\"dark\"]) .recur-sdk__error {\n background: #7f1d1d;\n border-color: #991b1b;\n }\n\n :host([theme=\"dark\"]) .recur-sdk__error-icon,\n :host([theme=\"dark\"]) .recur-sdk__error-dismiss {\n color: #fca5a5;\n }\n\n :host([theme=\"dark\"]) .recur-sdk__error-message {\n color: #fecaca;\n }\n </style>\n\n <div class=\"recur-sdk__error\" role=\"alert\">\n <svg class=\"recur-sdk__error-icon\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z\" />\n </svg>\n <div class=\"recur-sdk__error-content\">\n <p class=\"recur-sdk__error-message\">${this.error}</p>\n </div>\n ${this.isDismissible ? `\n <button class=\"recur-sdk__error-dismiss\" aria-label=\"Dismiss error\">\n <svg fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M6 18L18 6M6 6l12 12\" />\n </svg>\n </button>\n ` : ''}\n </div>\n `;\n\n if (this.isDismissible) {\n const dismissBtn = this.shadowRoot!.querySelector('.recur-sdk__error-dismiss');\n dismissBtn?.addEventListener('click', () => this.handleDismiss());\n }\n }\n}\n\n// 自動註冊組件\nif (typeof window !== 'undefined' && !customElements.get('recur-error-display')) {\n customElements.define('recur-error-display', RecurErrorDisplay);\n}\n\n// TypeScript 類型聲明\ndeclare global {\n interface HTMLElementTagNameMap {\n 'recur-error-display': RecurErrorDisplay;\n }\n}\n","/**\n * Recur Skeleton Loader Web Component\n *\n * A reusable skeleton loading animation for payment forms.\n * Provides visual feedback while content is being loaded.\n */\n\nexport class RecurSkeletonLoader extends HTMLElement {\n static get observedAttributes() {\n return ['type'];\n }\n\n constructor() {\n super();\n this.attachShadow({ mode: 'open' });\n }\n\n connectedCallback() {\n this.render();\n }\n\n attributeChangedCallback(name: string, oldValue: string, newValue: string) {\n if (oldValue !== newValue) {\n this.render();\n }\n }\n\n private get type(): 'payment-form' | 'list' | 'card' {\n const typeAttr = this.getAttribute('type');\n if (typeAttr === 'list' || typeAttr === 'card') return typeAttr;\n return 'payment-form';\n }\n\n private renderPaymentFormSkeleton(): string {\n return `\n <div class=\"skeleton-field\">\n <div class=\"skeleton-label\"></div>\n <div class=\"skeleton-input\"></div>\n </div>\n <div class=\"skeleton-field\">\n <div class=\"skeleton-label\"></div>\n <div class=\"skeleton-input\"></div>\n </div>\n <div class=\"skeleton-field\">\n <div class=\"skeleton-label\"></div>\n <div class=\"skeleton-input\"></div>\n </div>\n <div class=\"skeleton-divider\"></div>\n <div class=\"skeleton-field\">\n <div class=\"skeleton-label\"></div>\n <div class=\"skeleton-input\"></div>\n </div>\n <div class=\"skeleton-field\">\n <div class=\"skeleton-label\"></div>\n <div class=\"skeleton-row\">\n <div class=\"skeleton-input-half\"></div>\n <div class=\"skeleton-input-half\"></div>\n </div>\n </div>\n <div class=\"skeleton-button\"></div>\n `;\n }\n\n private renderListSkeleton(): string {\n return `\n <div class=\"skeleton-list-item\"></div>\n <div class=\"skeleton-list-item\"></div>\n <div class=\"skeleton-list-item\"></div>\n `;\n }\n\n private renderCardSkeleton(): string {\n return `\n <div class=\"skeleton-card-header\"></div>\n <div class=\"skeleton-card-body\">\n <div class=\"skeleton-text\"></div>\n <div class=\"skeleton-text\"></div>\n <div class=\"skeleton-text short\"></div>\n </div>\n `;\n }\n\n private render() {\n let content = '';\n switch (this.type) {\n case 'list':\n content = this.renderListSkeleton();\n break;\n case 'card':\n content = this.renderCardSkeleton();\n break;\n default:\n content = this.renderPaymentFormSkeleton();\n }\n\n this.shadowRoot!.innerHTML = `\n <style>\n :host {\n display: block;\n }\n\n @keyframes recur-sdk-pulse {\n 0%, 100% {\n opacity: 1;\n }\n 50% {\n opacity: 0.4;\n }\n }\n\n .skeleton-field {\n margin-bottom: 20px;\n }\n\n .skeleton-label {\n width: 80px;\n height: 14px;\n background: var(--recur-skeleton-bg, #e5e7eb);\n border-radius: 4px;\n margin-bottom: 8px;\n animation: recur-sdk-pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;\n }\n\n .skeleton-input {\n width: 100%;\n height: 42px;\n background: var(--recur-skeleton-bg, #e5e7eb);\n border-radius: var(--recur-border-radius, 6px);\n animation: recur-sdk-pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;\n animation-delay: 0.1s;\n }\n\n .skeleton-row {\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 12px;\n }\n\n .skeleton-input-half {\n height: 42px;\n background: var(--recur-skeleton-bg, #e5e7eb);\n border-radius: var(--recur-border-radius, 6px);\n animation: recur-sdk-pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;\n animation-delay: 0.15s;\n }\n\n .skeleton-divider {\n height: 1px;\n background: var(--recur-skeleton-bg, #e5e7eb);\n margin: 24px 0;\n animation: recur-sdk-pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;\n animation-delay: 0.2s;\n }\n\n .skeleton-button {\n width: 100%;\n height: 48px;\n background: var(--recur-skeleton-bg, #e5e7eb);\n border-radius: 8px;\n margin-top: 8px;\n animation: recur-sdk-pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;\n animation-delay: 0.25s;\n }\n\n /* List skeleton */\n .skeleton-list-item {\n width: 100%;\n height: 60px;\n background: var(--recur-skeleton-bg, #e5e7eb);\n border-radius: var(--recur-border-radius, 6px);\n margin-bottom: 12px;\n animation: recur-sdk-pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;\n }\n\n .skeleton-list-item:nth-child(2) {\n animation-delay: 0.1s;\n }\n\n .skeleton-list-item:nth-child(3) {\n animation-delay: 0.2s;\n }\n\n /* Card skeleton */\n .skeleton-card-header {\n width: 100%;\n height: 120px;\n background: var(--recur-skeleton-bg, #e5e7eb);\n border-radius: var(--recur-border-radius, 6px) var(--recur-border-radius, 6px) 0 0;\n animation: recur-sdk-pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;\n }\n\n .skeleton-card-body {\n padding: 16px;\n }\n\n .skeleton-text {\n width: 100%;\n height: 16px;\n background: var(--recur-skeleton-bg, #e5e7eb);\n border-radius: 4px;\n margin-bottom: 12px;\n animation: recur-sdk-pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;\n animation-delay: 0.1s;\n }\n\n .skeleton-text.short {\n width: 60%;\n animation-delay: 0.2s;\n }\n\n /* Dark theme support */\n :host([theme=\"dark\"]) .skeleton-label,\n :host([theme=\"dark\"]) .skeleton-input,\n :host([theme=\"dark\"]) .skeleton-input-half,\n :host([theme=\"dark\"]) .skeleton-divider,\n :host([theme=\"dark\"]) .skeleton-button,\n :host([theme=\"dark\"]) .skeleton-list-item,\n :host([theme=\"dark\"]) .skeleton-card-header,\n :host([theme=\"dark\"]) .skeleton-text {\n background: #374151;\n }\n </style>\n\n ${content}\n `;\n }\n}\n\n// 自動註冊組件\nif (typeof window !== 'undefined' && !customElements.get('recur-skeleton-loader')) {\n customElements.define('recur-skeleton-loader', RecurSkeletonLoader);\n}\n\n// TypeScript 類型聲明\ndeclare global {\n interface HTMLElementTagNameMap {\n 'recur-skeleton-loader': RecurSkeletonLoader;\n }\n}\n","/**\n * Payment Form Skeleton Web Component\n *\n * Displays a loading skeleton that matches the structure of RecurPaymentForm\n * This provides better UX by showing the expected form layout during initial loading\n */\n\nexport class RecurPaymentFormSkeleton extends HTMLElement {\n constructor() {\n super();\n this.attachShadow({ mode: 'open' });\n }\n\n connectedCallback() {\n this.render();\n }\n\n private render() {\n this.shadowRoot!.innerHTML = `\n <style>\n :host {\n display: block;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n }\n\n .payment-form-wrapper {\n max-width: 500px;\n margin: 0 auto;\n padding: 24px;\n background: white;\n border-radius: 12px;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);\n }\n\n .form-header {\n margin-bottom: 24px;\n }\n\n .form-section {\n margin-bottom: 24px;\n }\n\n .section-title {\n margin: 0 0 12px 0;\n font-size: 16px;\n font-weight: 600;\n color: #2d3748;\n }\n\n .label {\n font-size: 14px;\n color: #4a5568;\n margin-bottom: 4px;\n }\n\n /* Skeleton base styles */\n .skeleton {\n background: linear-gradient(\n 90deg,\n #f0f0f0 0%,\n #f8f8f8 50%,\n #f0f0f0 100%\n );\n background-size: 200% 100%;\n animation: skeleton-loading 1.5s ease-in-out infinite;\n border-radius: 4px;\n }\n\n @keyframes skeleton-loading {\n 0% {\n background-position: 200% 0;\n }\n 100% {\n background-position: -200% 0;\n }\n }\n\n .skeleton-title {\n height: 28px;\n width: 200px;\n margin-bottom: 8px;\n }\n\n .skeleton-subtitle {\n height: 18px;\n width: 320px;\n }\n\n .skeleton-order-summary {\n padding: 16px;\n background: #f7fafc;\n border-radius: 6px;\n margin-bottom: 24px;\n }\n\n .skeleton-order-row {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 12px;\n }\n\n .skeleton-order-row:last-child {\n margin-bottom: 0;\n padding-top: 12px;\n border-top: 1px solid #e2e8f0;\n font-weight: 600;\n }\n\n .skeleton-order-value {\n height: 20px;\n width: 120px;\n }\n\n .input-group {\n margin-bottom: 16px;\n }\n\n .input-group:last-child {\n margin-bottom: 0;\n }\n\n .skeleton-input {\n height: 44px;\n width: 100%;\n }\n\n .card-fields-grid {\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 12px;\n }\n\n .skeleton-button {\n height: 44px;\n width: 100%;\n border-radius: 6px;\n }\n\n .security-note {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 12px;\n background: #f7fafc;\n border-radius: 6px;\n margin-top: 16px;\n }\n\n .security-icon {\n color: #48bb78;\n }\n\n .security-text {\n font-size: 13px;\n color: #4a5568;\n margin: 0;\n }\n </style>\n\n <div class=\"payment-form-wrapper\">\n <!-- Header Skeleton -->\n <div class=\"form-header\">\n <div class=\"skeleton skeleton-title\"></div>\n <div class=\"skeleton skeleton-subtitle\"></div>\n </div>\n\n <!-- Order Summary - Static labels, skeleton values -->\n <div class=\"skeleton-order-summary\">\n <h3 class=\"section-title\">訂單摘要</h3>\n <div class=\"skeleton-order-row\">\n <span class=\"label\">方案</span>\n <div class=\"skeleton skeleton-order-value\"></div>\n </div>\n <div class=\"skeleton-order-row\">\n <span class=\"label\">金額</span>\n <div class=\"skeleton skeleton-order-value\"></div>\n </div>\n <div class=\"skeleton-order-row\">\n <span class=\"label\">週期</span>\n <div class=\"skeleton skeleton-order-value\"></div>\n </div>\n <div class=\"skeleton-order-row\">\n <span class=\"label\">總計</span>\n <div class=\"skeleton skeleton-order-value\"></div>\n </div>\n </div>\n\n <!-- Customer Info Section - Static labels, skeleton inputs -->\n <div class=\"form-section\">\n <h3 class=\"section-title\">客戶資訊</h3>\n <div class=\"input-group\">\n <div class=\"label\">姓名</div>\n <div class=\"skeleton skeleton-input\"></div>\n </div>\n <div class=\"input-group\">\n <div class=\"label\">Email</div>\n <div class=\"skeleton skeleton-input\"></div>\n </div>\n <div class=\"input-group\">\n <div class=\"label\">電話(選填)</div>\n <div class=\"skeleton skeleton-input\"></div>\n </div>\n </div>\n\n <!-- Card Fields Section - Static labels, skeleton inputs -->\n <div class=\"form-section\">\n <h3 class=\"section-title\">信用卡資訊</h3>\n <div class=\"input-group\">\n <div class=\"label\">信用卡號</div>\n <div class=\"skeleton skeleton-input\"></div>\n </div>\n <div class=\"card-fields-grid\">\n <div class=\"input-group\">\n <div class=\"label\">有效期限</div>\n <div class=\"skeleton skeleton-input\"></div>\n </div>\n <div class=\"input-group\">\n <div class=\"label\">安全碼</div>\n <div class=\"skeleton skeleton-input\"></div>\n </div>\n </div>\n </div>\n\n <!-- Submit Button Skeleton -->\n <div class=\"skeleton skeleton-button\"></div>\n\n <!-- Security Note -->\n <div class=\"security-note\">\n <svg class=\"security-icon\" width=\"16\" height=\"16\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <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\" />\n </svg>\n <p class=\"security-text\">您的付款資訊經過加密保護</p>\n </div>\n </div>\n `;\n }\n}\n\n// Register component if not already registered\nif (typeof window !== 'undefined' && !customElements.get('recur-payment-form-skeleton')) {\n customElements.define('recur-payment-form-skeleton', RecurPaymentFormSkeleton);\n}\n\n// TypeScript type declaration\ndeclare global {\n interface HTMLElementTagNameMap {\n 'recur-payment-form-skeleton': RecurPaymentFormSkeleton;\n }\n}\n","/**\n * Recur Toast Notification Component\n *\n * A lightweight, non-intrusive toast notification for success, error, and info messages.\n * Auto-dismisses after a configurable duration.\n */\n\nexport class RecurToast extends HTMLElement {\n static get observedAttributes() {\n return ['message', 'type', 'duration'];\n }\n\n constructor() {\n super();\n this.attachShadow({ mode: 'open' });\n }\n\n connectedCallback() {\n this.render();\n this.setupAutoDismiss();\n }\n\n private get message(): string {\n return this.getAttribute('message') || 'Notification';\n }\n\n private get type(): 'success' | 'error' | 'info' {\n const typeAttr = this.getAttribute('type');\n if (typeAttr === 'success' || typeAttr === 'error') return typeAttr;\n return 'info';\n }\n\n private get duration(): number {\n const durationAttr = this.getAttribute('duration');\n return durationAttr ? parseInt(durationAttr, 10) : 5000;\n }\n\n private setupAutoDismiss() {\n const duration = this.duration;\n if (duration > 0) {\n setTimeout(() => this.dismiss(), duration);\n }\n }\n\n private dismiss() {\n // Add exit animation\n this.style.animation = 'recur-toast-slide-out 0.3s ease-in-out';\n setTimeout(() => this.remove(), 300);\n }\n\n private getTypeIcon(): string {\n switch (this.type) {\n case 'success':\n return `<svg width=\"20\" height=\"20\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M5 13l4 4L19 7\" />\n </svg>`;\n case 'error':\n return `<svg width=\"20\" height=\"20\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M6 18L18 6M6 6l12 12\" />\n </svg>`;\n case 'info':\n default:\n return `<svg width=\"20\" height=\"20\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <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\" />\n </svg>`;\n }\n }\n\n private getTypeColor(): string {\n switch (this.type) {\n case 'success':\n return '#10b981'; // green-500\n case 'error':\n return '#ef4444'; // red-500\n case 'info':\n default:\n return '#3b82f6'; // blue-500\n }\n }\n\n private render() {\n const color = this.getTypeColor();\n\n this.shadowRoot!.innerHTML = `\n <style>\n :host {\n display: block;\n margin-bottom: 12px;\n animation: recur-toast-slide-in 0.3s ease-out;\n }\n\n @keyframes recur-toast-slide-in {\n from {\n transform: translateX(100%);\n opacity: 0;\n }\n to {\n transform: translateX(0);\n opacity: 1;\n }\n }\n\n @keyframes recur-toast-slide-out {\n from {\n transform: translateX(0);\n opacity: 1;\n }\n to {\n transform: translateX(100%);\n opacity: 0;\n }\n }\n\n .toast {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 12px 16px;\n background: white;\n border-left: 4px solid ${color};\n border-radius: 8px;\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);\n min-width: 280px;\n max-width: 420px;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n cursor: pointer;\n transition: transform 0.2s ease;\n }\n\n .toast:hover {\n transform: translateY(-2px);\n box-shadow: 0 6px 16px rgba(0, 0, 0, 0.2);\n }\n\n .toast-icon {\n flex-shrink: 0;\n color: ${color};\n display: flex;\n align-items: center;\n justify-content: center;\n }\n\n .toast-message {\n flex: 1;\n color: #374151;\n font-size: 14px;\n line-height: 1.5;\n margin: 0;\n }\n\n .toast-close {\n flex-shrink: 0;\n color: #9ca3af;\n cursor: pointer;\n padding: 4px;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 4px;\n transition: all 0.2s;\n }\n\n .toast-close:hover {\n color: #6b7280;\n background: #f3f4f6;\n }\n\n /* Dark mode support */\n :host([theme=\"dark\"]) .toast {\n background: #1f2937;\n }\n\n :host([theme=\"dark\"]) .toast-message {\n color: #e5e7eb;\n }\n\n :host([theme=\"dark\"]) .toast-close {\n color: #6b7280;\n }\n\n :host([theme=\"dark\"]) .toast-close:hover {\n color: #9ca3af;\n background: #374151;\n }\n </style>\n\n <div class=\"toast\" role=\"alert\">\n <div class=\"toast-icon\">\n ${this.getTypeIcon()}\n </div>\n <p class=\"toast-message\">${this.message}</p>\n <button class=\"toast-close\" aria-label=\"Close\">\n <svg width=\"16\" height=\"16\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M6 18L18 6M6 6l12 12\" />\n </svg>\n </button>\n </div>\n `;\n\n // Add click handler for close button\n const closeBtn = this.shadowRoot!.querySelector('.toast-close');\n closeBtn?.addEventListener('click', (e) => {\n e.stopPropagation();\n this.dismiss();\n });\n\n // Click anywhere on toast to dismiss\n const toast = this.shadowRoot!.querySelector('.toast');\n toast?.addEventListener('click', () => this.dismiss());\n }\n\n /**\n * Static helper method to show toast\n */\n static show(\n message: string,\n type: 'success' | 'error' | 'info' = 'info',\n duration: number = 5000\n ): RecurToast {\n const container = RecurToastContainer.getInstance();\n\n const toast = document.createElement('recur-toast');\n toast.setAttribute('message', message);\n toast.setAttribute('type', type);\n toast.setAttribute('duration', duration.toString());\n\n container.appendChild(toast);\n\n return toast;\n }\n}\n\n/**\n * Toast Container - Manages toast stack positioning\n */\nexport class RecurToastContainer extends HTMLElement {\n private static instance: RecurToastContainer | null = null;\n\n constructor() {\n super();\n this.attachShadow({ mode: 'open' });\n }\n\n connectedCallback() {\n this.render();\n }\n\n private render() {\n this.shadowRoot!.innerHTML = `\n <style>\n :host {\n position: fixed;\n top: 24px;\n right: 24px;\n z-index: 9999;\n display: flex;\n flex-direction: column;\n pointer-events: none;\n }\n\n ::slotted(*) {\n pointer-events: auto;\n }\n\n /* Mobile responsive */\n @media (max-width: 640px) {\n :host {\n top: 16px;\n right: 16px;\n left: 16px;\n }\n }\n </style>\n\n <slot></slot>\n `;\n }\n\n /**\n * Get or create singleton instance\n */\n static getInstance(): RecurToastContainer {\n if (!RecurToastContainer.instance) {\n RecurToastContainer.instance = document.querySelector('recur-toast-container');\n\n if (!RecurToastContainer.instance) {\n RecurToastContainer.instance = document.createElement('recur-toast-container');\n document.body.appendChild(RecurToastContainer.instance);\n }\n }\n\n return RecurToastContainer.instance;\n }\n}\n\n// Auto-register components\nif (typeof window !== 'undefined' && !customElements.get('recur-toast')) {\n customElements.define('recur-toast', RecurToast);\n}\n\nif (typeof window !== 'undefined' && !customElements.get('recur-toast-container')) {\n customElements.define('recur-toast-container', RecurToastContainer);\n}\n\n// TypeScript type declarations\ndeclare global {\n interface HTMLElementTagNameMap {\n 'recur-toast': RecurToast;\n 'recur-toast-container': RecurToastContainer;\n }\n}\n","/**\n * Recur Payment Form Web Component (混合模式)\n *\n * 使用 Shadow DOM 包裝外層 UI,PAYUNi 容器保持在 Light DOM\n * 這樣既能享受樣式隔離,又能讓 PAYUNi SDK 正常工作\n */\n\nexport class RecurPaymentForm extends HTMLElement {\n private containerId: string;\n private customStyles: string;\n private _isInitializing = false;\n private _initializationAborted = false;\n\n constructor() {\n super();\n\n // 生成唯一的 container ID\n this.containerId = this.getAttribute('container-id') || `recur-${Date.now()}`;\n\n // 獲取自定義樣式(如果有提供)\n this.customStyles = this.getAttribute('custom-styles') || '';\n\n // 創建 Shadow DOM(用於外層 UI 和樣式隔離)\n this.attachShadow({ mode: 'open' });\n }\n\n connectedCallback() {\n this.render();\n }\n\n disconnectedCallback() {\n console.log('[PaymentForm] Component disconnected, cleaning up...');\n\n // Mark initialization as aborted to stop any ongoing operations\n this._initializationAborted = true;\n\n // Clear payment session reference\n (this as any)._paymentSession = null;\n\n // Remove event listeners from submit button by replacing with clone\n const submitBtn = document.getElementById(`${this.containerId}-submit-btn`);\n if (submitBtn) {\n const newBtn = submitBtn.cloneNode(true);\n submitBtn.parentNode?.replaceChild(newBtn, submitBtn);\n }\n }\n\n // 監聽 attribute 變化\n static get observedAttributes() {\n return ['custom-styles', 'customer-name', 'customer-email', 'plan-name', 'amount', 'billing-period'];\n }\n\n attributeChangedCallback(name: string, oldValue: string, newValue: string) {\n if (name === 'custom-styles' && oldValue !== newValue) {\n this.customStyles = newValue || '';\n this.updateCustomStyles();\n } else if (oldValue !== newValue) {\n // Re-render customer info section when customer/order data changes\n this.updateCustomerInfoSection();\n }\n }\n\n private render() {\n // Shadow DOM 內容:外層 UI 結構和樣式\n this.shadowRoot!.innerHTML = `\n <style>\n /* 這些樣式被隔離在 Shadow DOM 內,不會影響外部 */\n :host {\n display: block;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n }\n\n .payment-form-wrapper {\n max-width: 500px;\n margin: 0 auto;\n padding: 24px;\n background: white;\n border-radius: 12px;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);\n }\n\n .form-header {\n margin-bottom: 24px;\n }\n\n .form-title {\n font-size: 24px;\n font-weight: 600;\n color: #1a202c;\n margin: 0 0 8px 0;\n }\n\n .form-subtitle {\n font-size: 14px;\n color: #718096;\n margin: 0;\n }\n\n .form-section {\n margin-bottom: 24px;\n }\n\n .section-title {\n font-size: 14px;\n font-weight: 600;\n color: #2d3748;\n margin: 0 0 12px 0;\n }\n\n /* Slot 樣式 - 這會應用到 slotted 內容 */\n ::slotted(*) {\n display: block;\n }\n\n .security-note {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 12px;\n background: #f7fafc;\n border-radius: 6px;\n margin-top: 16px;\n }\n\n .security-icon {\n color: #48bb78;\n }\n\n .security-text {\n font-size: 13px;\n color: #4a5568;\n margin: 0;\n }\n </style>\n\n <div class=\"payment-form-wrapper\">\n <div class=\"form-header\">\n <h2 class=\"form-title\">付款資訊</h2>\n <p class=\"form-subtitle\">請填寫您的信用卡資訊以完成訂閱</p>\n </div>\n\n <!-- Slot for order summary (Light DOM) -->\n <slot name=\"order-summary\"></slot>\n\n <div class=\"form-section\">\n <h3 class=\"section-title\">客戶資訊</h3>\n <!-- Slot for customer info fields (Light DOM) -->\n <slot name=\"customer-info\"></slot>\n </div>\n\n <div class=\"form-section\">\n <h3 class=\"section-title\">信用卡資訊</h3>\n <!-- Slot for PAYUNi card fields (Light DOM) -->\n <slot name=\"card-fields\"></slot>\n </div>\n\n <!-- Slot for submit button (Light DOM) -->\n <slot name=\"actions\"></slot>\n\n <div class=\"security-note\">\n <svg class=\"security-icon\" width=\"16\" height=\"16\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <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\" />\n </svg>\n <p class=\"security-text\">您的付款資訊經過加密保護</p>\n </div>\n </div>\n `;\n\n // Light DOM 內容:PAYUNi 需要訪問的元素\n this.createLightDOMContent();\n }\n\n private createLightDOMContent() {\n // 清空現有 Light DOM 內容\n this.innerHTML = '';\n\n // 創建自定義樣式容器(Light DOM - 這樣可以影響 PAYUNi iframes)\n const styleElement = document.createElement('style');\n styleElement.id = `${this.containerId}-custom-styles`;\n\n // 組合自定義樣式和 ID 選擇器樣式\n const idSpecificStyles = `\n /* PAYUNi iframe 容器高度(使用實際的 container ID) */\n #${this.containerId}-card-no,\n #${this.containerId}-card-exp,\n #${this.containerId}-card-cvc {\n height: 36px !important;\n }\n `;\n\n styleElement.textContent = this.customStyles + '\\n' + idSpecificStyles;\n this.appendChild(styleElement);\n\n // 創建訂單摘要區塊(Light DOM)\n const orderSummary = this.createOrderSummarySection();\n orderSummary.slot = 'order-summary';\n this.appendChild(orderSummary);\n\n // 創建客戶資訊區塊(Light DOM)\n const customerInfo = this.createCustomerInfoSection();\n customerInfo.slot = 'customer-info';\n this.appendChild(customerInfo);\n\n // 創建信用卡欄位區塊(Light DOM - PAYUNi 會使用這些 ID)\n const cardFields = this.createCardFieldsSection();\n cardFields.slot = 'card-fields';\n this.appendChild(cardFields);\n\n // 創建操作按鈕區塊(Light DOM)\n const actions = this.createActionsSection();\n actions.slot = 'actions';\n this.appendChild(actions);\n }\n\n private updateCustomerInfoSection() {\n // Update customer info section when attributes change\n const existingSection = this.querySelector('[slot=\"customer-info\"]');\n if (existingSection) {\n const newSection = this.createCustomerInfoSection();\n newSection.slot = 'customer-info';\n existingSection.replaceWith(newSection);\n }\n }\n\n private updateCustomStyles() {\n const styleElement = this.querySelector(`#${this.containerId}-custom-styles`) as HTMLStyleElement;\n if (styleElement) {\n // 組合自定義樣式和 ID 選擇器樣式\n const idSpecificStyles = `\n /* PAYUNi iframe 容器高度(使用實際的 container ID) */\n #${this.containerId}-card-no,\n #${this.containerId}-card-exp,\n #${this.containerId}-card-cvc {\n height: 36px !important;\n }\n `;\n styleElement.textContent = this.customStyles + '\\n' + idSpecificStyles;\n }\n }\n\n private createOrderSummarySection(): HTMLElement {\n const section = document.createElement('div');\n section.className = 'order-summary-section';\n\n // Get order data from attributes\n const planName = this.getAttribute('plan-name');\n const amount = this.getAttribute('amount');\n const billingPeriod = this.getAttribute('billing-period');\n\n // Only show order summary if we have the data\n if (!planName || !amount) {\n return section;\n }\n\n // Format billing period in Chinese\n const billingPeriodMap: { [key: string]: string } = {\n 'MONTHLY': '月',\n 'QUARTERLY': '季',\n 'YEARLY': '年',\n 'WEEKLY': '週',\n };\n const periodText = billingPeriod ? billingPeriodMap[billingPeriod] || billingPeriod : '';\n\n section.innerHTML = `\n <style>\n .order-summary-section {\n background: #f7fafc;\n border: 1px solid #e2e8f0;\n border-radius: 8px;\n padding: 16px;\n margin-bottom: 24px;\n }\n\n .order-summary-title {\n font-size: 14px;\n font-weight: 600;\n color: #2d3748;\n margin: 0 0 12px 0;\n }\n\n .order-summary-item {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 8px;\n }\n\n .order-summary-item:last-child {\n margin-bottom: 0;\n padding-top: 12px;\n border-top: 1px solid #e2e8f0;\n }\n\n .order-summary-label {\n font-size: 14px;\n color: #4a5568;\n }\n\n .order-summary-value {\n font-size: 14px;\n font-weight: 500;\n color: #2d3748;\n }\n\n .order-summary-total {\n font-size: 18px;\n font-weight: 700;\n color: #1a202c;\n }\n </style>\n\n <h3 class=\"order-summary-title\">訂單摘要</h3>\n <div class=\"order-summary-item\">\n <span class=\"order-summary-label\">訂閱方案</span>\n <span class=\"order-summary-value\">${planName}</span>\n </div>\n ${periodText ? `\n <div class=\"order-summary-item\">\n <span class=\"order-summary-label\">計費週期</span>\n <span class=\"order-summary-value\">每${periodText}</span>\n </div>\n ` : ''}\n <div class=\"order-summary-item\">\n <span class=\"order-summary-label\">總計</span>\n <span class=\"order-summary-total\">NT$ ${Number(amount).toLocaleString()}</span>\n </div>\n `;\n\n return section;\n }\n\n private createCustomerInfoSection(): HTMLElement {\n const section = document.createElement('div');\n section.className = 'customer-info-section';\n\n // Check if customer data is provided via attributes\n const customerName = this.getAttribute('customer-name');\n const customerEmail = this.getAttribute('customer-email');\n\n // If customer data is provided, show as read-only display\n if (customerName && customerEmail) {\n section.innerHTML = `\n <style>\n .recur-info-display {\n background: #f7fafc;\n border: 1px solid #e2e8f0;\n border-radius: 6px;\n padding: 12px;\n margin-bottom: 8px;\n }\n\n .recur-info-row {\n display: flex;\n align-items: center;\n margin-bottom: 6px;\n }\n\n .recur-info-row:last-child {\n margin-bottom: 0;\n }\n\n .recur-info-label {\n font-size: 13px;\n color: #718096;\n min-width: 80px;\n }\n\n .recur-info-value {\n font-size: 14px;\n color: #2d3748;\n font-weight: 500;\n }\n </style>\n\n <div class=\"recur-info-display\">\n <div class=\"recur-info-row\">\n <span class=\"recur-info-label\">客戶姓名:</span>\n <span class=\"recur-info-value\">${customerName}</span>\n </div>\n <div class=\"recur-info-row\">\n <span class=\"recur-info-label\">電子郵件:</span>\n <span class=\"recur-info-value\">${customerEmail}</span>\n </div>\n </div>\n `;\n } else {\n // Show input fields if customer data is not provided\n section.innerHTML = `\n <style>\n /* 這些樣式在 Light DOM 中,會影響全局 */\n /* 使用帶 prefix 的 class name 避免衝突 */\n .recur-form-group {\n margin-bottom: 16px;\n }\n\n .recur-form-label {\n display: block;\n font-size: 14px;\n font-weight: 500;\n color: #2d3748;\n margin-bottom: 6px;\n }\n\n .recur-form-input {\n width: 100%;\n padding: 10px 12px;\n font-size: 14px;\n border: 1px solid #d1d5db;\n border-radius: 6px;\n transition: border-color 0.2s;\n box-sizing: border-box;\n }\n\n .recur-form-input:focus {\n outline: none;\n border-color: #3b82f6;\n box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);\n }\n\n .recur-form-input:hover:not(:focus) {\n border-color: #9ca3af;\n }\n </style>\n\n <div class=\"recur-form-group\">\n <label class=\"recur-form-label\" for=\"${this.containerId}-email\">Email</label>\n <input\n type=\"email\"\n id=\"${this.containerId}-email\"\n class=\"recur-form-input\"\n placeholder=\"your@email.com\"\n required\n />\n </div>\n\n <div class=\"recur-form-group\">\n <label class=\"recur-form-label\" for=\"${this.containerId}-name\">姓名</label>\n <input\n type=\"text\"\n id=\"${this.containerId}-name\"\n class=\"recur-form-input\"\n placeholder=\"王小明\"\n required\n />\n </div>\n\n <div class=\"recur-form-group\">\n <label class=\"recur-form-label\" for=\"${this.containerId}-phone\">電話</label>\n <input\n type=\"tel\"\n id=\"${this.containerId}-phone\"\n class=\"recur-form-input\"\n placeholder=\"+886 912345678\"\n required\n />\n </div>\n `;\n }\n\n return section;\n }\n\n private createCardFieldsSection(): HTMLElement {\n const section = document.createElement('div');\n section.className = 'card-fields-section';\n\n section.innerHTML = `\n <style>\n .recur-card-field {\n margin-bottom: 16px;\n }\n\n .recur-card-field-label {\n display: block;\n font-size: 14px;\n font-weight: 500;\n color: #2d3748;\n margin-bottom: 6px;\n }\n\n /* PAYUNi iframe 容器樣式 */\n .recur-payuni-iframe {\n height: 36px;\n width: 100%;\n }\n\n .recur-card-row {\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 12px;\n }\n </style>\n\n <div class=\"recur-card-field\">\n <label class=\"recur-card-field-label\">卡號</label>\n <div class=\"recur-card-field-container\" id=\"${this.containerId}-card-no-wrapper\">\n <!-- PAYUNi 會在這裡注入 iframe -->\n <div id=\"${this.containerId}-card-no\" class=\"recur-payuni-iframe\"></div>\n </div>\n </div>\n\n <div class=\"recur-card-row\">\n <div class=\"recur-card-field\">\n <label class=\"recur-card-field-label\">有效期限</label>\n <div class=\"recur-card-field-container\" id=\"${this.containerId}-card-exp-wrapper\">\n <div id=\"${this.containerId}-card-exp\" class=\"recur-payuni-iframe\"></div>\n </div>\n </div>\n\n <div class=\"recur-card-field\">\n <label class=\"recur-card-field-label\">安全碼</label>\n <div class=\"recur-card-field-container\" id=\"${this.containerId}-card-cvc-wrapper\">\n <div id=\"${this.containerId}-card-cvc\" class=\"recur-payuni-iframe\"></div>\n </div>\n </div>\n </div>\n `;\n\n return section;\n }\n\n private createActionsSection(): HTMLElement {\n const section = document.createElement('div');\n section.className = 'actions-section';\n\n section.innerHTML = `\n <style>\n .recur-submit-button {\n width: 100%;\n padding: 14px 20px;\n font-size: 16px;\n font-weight: 500;\n color: #ffffff;\n background: #18181b;\n border: none;\n border-radius: 6px;\n cursor: pointer;\n transition: background-color 0.2s;\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\n }\n\n .recur-submit-button:hover:not(:disabled) {\n background: #27272a;\n }\n\n .recur-submit-button:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n }\n\n .recur-submit-button.loading {\n cursor: wait;\n }\n\n .recur-loading-spinner {\n width: 16px;\n height: 16px;\n border: 2px solid currentColor;\n border-top-color: transparent;\n border-radius: 50%;\n animation: recur-spin 0.6s linear infinite;\n }\n\n @keyframes recur-spin {\n to { transform: rotate(360deg); }\n }\n </style>\n\n <button\n type=\"submit\"\n id=\"${this.containerId}-submit-btn\"\n class=\"recur-submit-button\"\n disabled\n >\n <span id=\"${this.containerId}-submit-text\">確認付款</span>\n </button>\n `;\n\n return section;\n }\n\n /**\n * 初始化 PAYUNi SDK\n * 這個方法會在外部調用,傳入 SDK token\n */\n async initializePayment(sdkToken: string, environment: 'SANDBOX' | 'PRODUCTION') {\n // Checkpoint 1: Check if component was already disconnected before starting\n if (this._initializationAborted) {\n console.log('[PaymentForm] Initialization aborted before start');\n return null;\n }\n\n // Mark that initialization is in progress\n this._isInitializing = true;\n\n // 等待 DOM 完全準備好\n await this.updateComplete();\n\n // Checkpoint 2: Check if disconnected while waiting for DOM\n if (this._initializationAborted) {\n console.log('[PaymentForm] Initialization aborted after updateComplete');\n this._isInitializing = false;\n return null;\n }\n\n // 確認 PAYUNi SDK 已加載\n if (!window.UniPayment) {\n this._isInitializing = false;\n throw new Error('PAYUNi SDK not loaded');\n }\n\n try {\n // Checkpoint 3: Verify DOM elements exist before creating session\n const cardNoEl = document.getElementById(`${this.containerId}-card-no`);\n const cardExpEl = document.getElementById(`${this.containerId}-card-exp`);\n const cardCvcEl = document.getElementById(`${this.containerId}-card-cvc`);\n\n if (!cardNoEl || !cardExpEl || !cardCvcEl) {\n console.log('[PaymentForm] DOM elements not found, likely disconnected');\n this._isInitializing = false;\n return null;\n }\n\n // Checkpoint 4: Check again before creating session\n if (this._initializationAborted) {\n console.log('[PaymentForm] Initialization aborted before createSession');\n this._isInitializing = false;\n return null;\n }\n\n // 初始化 PAYUNi\n const paymentSession = window.UniPayment.createSession(sdkToken, {\n env: environment === 'SANDBOX' ? 'S' : 'P',\n elements: {\n // 這些 ID 在 Light DOM 中,document.getElementById 可以找到\n CardNo: `${this.containerId}-card-no`,\n CardExp: `${this.containerId}-card-exp`,\n CardCvc: `${this.containerId}-card-cvc`,\n },\n });\n\n // Checkpoint 5: Check before starting payment session (the critical async operation)\n if (this._initializationAborted) {\n console.log('[PaymentForm] Initialization aborted before paymentSession.start()');\n this._isInitializing = false;\n return null;\n }\n\n // Start payment session with error handling for 1008 timeout\n try {\n await paymentSession.start();\n } catch (startError: any) {\n // Check if this is the 1008 timeout error and component was disconnected\n const is1008Error = startError?.message?.includes('1008') ||\n startError?.message?.includes('timeout') ||\n startError?.code === 1008;\n\n if (is1008Error && this._initializationAborted) {\n // Component was disconnected during initialization, silently ignore the error\n console.log('[PaymentForm] Caught 1008 timeout error after component disconnect - ignoring');\n this._isInitializing = false;\n return null;\n }\n\n // For other errors or if component is still mounted, re-throw\n throw startError;\n }\n\n // Checkpoint 6: Check after start completes (this is where 1008 error would occur)\n if (this._initializationAborted) {\n console.log('[PaymentForm] Initialization aborted after paymentSession.start()');\n this._isInitializing = false;\n return null;\n }\n\n // 監聽 PAYUNi 事件\n paymentSession.onUpdate?.((data: any) => {\n // Check if component is still mounted when event fires\n if (this._initializationAborted) {\n console.log('[PaymentForm] Ignoring onUpdate event - component disconnected');\n return;\n }\n\n console.log('[PaymentForm] PAYUNi update:', data);\n\n // 檢查所有卡片欄位是否都已填寫完成\n const allFieldsComplete = data.status &&\n data.status.CardNo === true &&\n data.status.CardExp === true &&\n data.status.CardCvc === true;\n\n // 啟用/禁用提交按鈕\n const submitBtn = document.getElementById(`${this.containerId}-submit-btn`) as HTMLButtonElement;\n if (submitBtn) {\n submitBtn.disabled = !allFieldsComplete;\n console.log('[PaymentForm] Submit button disabled:', !allFieldsComplete);\n }\n });\n\n // Checkpoint 7: Final check before saving session\n if (this._initializationAborted) {\n console.log('[PaymentForm] Initialization aborted before saving session');\n this._isInitializing = false;\n return null;\n }\n\n // 保存 session 供後續使用\n (this as any)._paymentSession = paymentSession;\n\n // 設置表單提交處理\n this.setupFormSubmission();\n\n this._isInitializing = false;\n return paymentSession;\n } catch (error) {\n this._isInitializing = false;\n console.error('[PaymentForm] Failed to initialize payment:', error);\n throw error;\n }\n }\n\n private setupFormSubmission() {\n const submitBtn = document.getElementById(`${this.containerId}-submit-btn`) as HTMLButtonElement;\n if (!submitBtn) return;\n\n submitBtn.addEventListener('click', async (e) => {\n e.preventDefault();\n\n // Prevent double submission\n if (submitBtn.classList.contains('loading')) {\n return;\n }\n\n // 獲取客戶資訊 - 從輸入框或屬性\n let email: string | undefined;\n let name: string | undefined;\n let phone: string | undefined;\n\n // Try to get from input fields first\n const emailInput = document.getElementById(`${this.containerId}-email`) as HTMLInputElement;\n const nameInput = document.getElementById(`${this.containerId}-name`) as HTMLInputElement;\n const phoneInput = document.getElementById(`${this.containerId}-phone`) as HTMLInputElement;\n\n if (emailInput && nameInput) {\n // Customer info is in input fields (not pre-filled)\n email = emailInput.value;\n name = nameInput.value;\n phone = phoneInput?.value;\n\n // 驗證必填欄位\n if (!email || !name) {\n this.showError('請填寫姓名和電子郵件');\n return;\n }\n } else {\n // Customer info is pre-filled (use attributes)\n email = this.getAttribute('customer-email') || undefined;\n name = this.getAttribute('customer-name') || undefined;\n\n // 驗證必填欄位\n if (!email || !name) {\n this.showError('客戶資訊不完整');\n return;\n }\n }\n\n // Clear any previous errors\n this.clearError();\n\n // Show loading state\n this.setButtonLoading(true);\n\n // 觸發自定義事件,讓外部處理提交\n this.dispatchEvent(new CustomEvent('submit', {\n detail: {\n customerEmail: email,\n customerName: name,\n customerPhone: phone,\n paymentSession: (this as any)._paymentSession,\n },\n bubbles: true,\n composed: true, // 讓事件可以穿透 Shadow DOM\n }));\n });\n }\n\n /**\n * Set button loading state\n */\n private setButtonLoading(loading: boolean) {\n const submitBtn = document.getElementById(`${this.containerId}-submit-btn`) as HTMLButtonElement;\n\n if (!submitBtn) return;\n\n if (loading) {\n submitBtn.classList.add('loading');\n submitBtn.disabled = true;\n submitBtn.innerHTML = `\n <span class=\"recur-loading-spinner\"></span>\n <span>處理中...</span>\n `;\n } else {\n submitBtn.classList.remove('loading');\n submitBtn.disabled = false;\n submitBtn.innerHTML = '<span id=\"' + this.containerId + '-submit-text\">確認付款</span>';\n }\n }\n\n /**\n * Reset button to normal state (call this when payment completes or fails)\n */\n resetButton() {\n this.setButtonLoading(false);\n }\n\n /**\n * Show error message using RecurErrorDisplay component\n */\n private showError(message: string) {\n // Find or create error container in Light DOM\n let errorContainer = this.querySelector('.recur-sdk__error-container') as HTMLElement;\n\n if (!errorContainer) {\n errorContainer = document.createElement('div');\n errorContainer.className = 'recur-sdk__error-container';\n errorContainer.style.cssText = 'margin-bottom: 16px;';\n\n // Insert at the beginning of the component\n this.insertBefore(errorContainer, this.firstChild);\n }\n\n // Create error display component\n const errorDisplay = document.createElement('recur-error-display');\n errorDisplay.setAttribute('error', message);\n errorDisplay.setAttribute('dismissible', 'true');\n\n // Replace old error message\n errorContainer.innerHTML = '';\n errorContainer.appendChild(errorDisplay);\n\n // Scroll to error\n errorDisplay.scrollIntoView({ behavior: 'smooth', block: 'center' });\n }\n\n /**\n * Clear any displayed errors\n */\n private clearError() {\n const errorContainer = this.querySelector('.recur-sdk__error-container');\n if (errorContainer) {\n errorContainer.innerHTML = '';\n }\n }\n\n /**\n * 等待組件完全連接到 DOM\n */\n private updateComplete(): Promise<void> {\n return new Promise((resolve) => {\n if (this.isConnected) {\n // 使用 setTimeout 確保 Light DOM 也已渲染\n setTimeout(resolve, 0);\n } else {\n const observer = new MutationObserver(() => {\n if (this.isConnected) {\n observer.disconnect();\n setTimeout(resolve, 0);\n }\n });\n observer.observe(document.body, { childList: true, subtree: true });\n }\n });\n }\n\n /**\n * 獲取表單數據\n */\n getFormData() {\n return {\n email: (document.getElementById(`${this.containerId}-email`) as HTMLInputElement)?.value,\n name: (document.getElementById(`${this.containerId}-name`) as HTMLInputElement)?.value,\n phone: (document.getElementById(`${this.containerId}-phone`) as HTMLInputElement)?.value,\n };\n }\n\n /**\n * 設置表單數據\n */\n setFormData(data: { email?: string; name?: string; phone?: string }) {\n if (data.email) {\n const emailInput = document.getElementById(`${this.containerId}-email`) as HTMLInputElement;\n if (emailInput) emailInput.value = data.email;\n }\n if (data.name) {\n const nameInput = document.getElementById(`${this.containerId}-name`) as HTMLInputElement;\n if (nameInput) nameInput.value = data.name;\n }\n if (data.phone) {\n const phoneInput = document.getElementById(`${this.containerId}-phone`) as HTMLInputElement;\n if (phoneInput) phoneInput.value = data.phone;\n }\n }\n}\n\n// 註冊 Web Component\nif (!customElements.get('recur-payment-form')) {\n customElements.define('recur-payment-form', RecurPaymentForm);\n}\n\n// TypeScript 類型聲明\ndeclare global {\n interface HTMLElementTagNameMap {\n 'recur-payment-form': RecurPaymentForm;\n }\n}\n","/**\n * Recur Vanilla SDK\n *\n * Standalone JavaScript SDK for embedding subscription checkout\n * No dependencies, works with plain HTML/JavaScript\n */\n\n// Auto-register Web Components\nimport '../components/index.js';\n\nimport { RecurCore } from './core.js';\nimport { RecurModal } from './modal.js';\nimport { RecurIframe } from './iframe.js';\nimport { EmbeddedCheckout } from './embedded-checkout.js';\nimport { RecurElements, createElements } from './elements.js';\nimport type { VanillaRecurConfig, VanillaCheckoutOptions } from './types.js';\nimport type { EmbeddedCheckoutOptions } from './embedded-checkout.js';\nimport type { RecurElementsConfig, ConfirmPaymentOptions, PaymentResult } from './elements.js';\n\nexport class RecurCheckout {\n private core: RecurCore;\n private currentModal: RecurModal | null = null;\n private currentIframe: RecurIframe | null = null;\n\n constructor(config: VanillaRecurConfig) {\n this.core = new RecurCore(config);\n }\n\n /**\n * Fetch available subscription plans\n */\n async fetchPlans() {\n return await this.core.fetchPlans();\n }\n\n /**\n * Create embedded checkout form with credit card fields\n * Renders a complete checkout UI in the specified container\n */\n async createEmbeddedCheckout(options: EmbeddedCheckoutOptions): Promise<void> {\n const config = this.core.getConfig();\n const embeddedCheckout = new EmbeddedCheckout(config, options);\n await embeddedCheckout.render();\n }\n\n /**\n * Initiate checkout flow\n */\n async checkout(options: VanillaCheckoutOptions): Promise<void> {\n try {\n // Create subscription\n const result = await this.core.createSubscription(options);\n\n // Call success callback\n if (options.onSuccess) {\n options.onSuccess(result);\n }\n\n // Handle payment based on mode\n const config = this.core.getConfig();\n const mode = options.mode || 'redirect';\n\n if (config.mockMode) {\n // Mock mode: show mock payment UI instead of loading real URL\n console.log('[Recur SDK] 🎭 Mock mode: Showing mock payment UI');\n await this.showMockPaymentUI(mode, result, options.container, options.onClose);\n } else {\n // Real mode: generate payment URL and load it\n const baseUrl = config.baseUrl || window.location.origin;\n const paymentUrl = `${baseUrl}/checkout/${result.subscription.id}`;\n\n switch (mode) {\n case 'modal':\n this.openModal(paymentUrl, options.onClose);\n break;\n\n case 'iframe':\n this.embedIframe(paymentUrl, options.container, options.onClose);\n break;\n\n case 'redirect':\n default:\n window.location.href = paymentUrl;\n break;\n }\n }\n } catch (error) {\n // Call error callback\n if (options.onError) {\n options.onError(error as any);\n } else {\n // Default error handling\n console.error('Recur checkout error:', error);\n throw error;\n }\n }\n }\n\n /**\n * Open payment in modal\n */\n private openModal(paymentUrl: string, onClose?: () => void): void {\n // Close existing modal if any\n this.closeModal();\n\n this.currentModal = new RecurModal(() => {\n this.currentModal = null;\n if (onClose) {\n onClose();\n }\n });\n\n this.currentModal.open(paymentUrl);\n }\n\n /**\n * Embed payment in iframe\n */\n private embedIframe(\n paymentUrl: string,\n container?: string | HTMLElement,\n onClose?: () => void\n ): void {\n if (!container) {\n throw new Error('Container is required for iframe mode');\n }\n\n // Remove existing iframe if any\n this.removeIframe();\n\n this.currentIframe = new RecurIframe(container, () => {\n this.currentIframe = null;\n if (onClose) {\n onClose();\n }\n });\n\n this.currentIframe.embed(paymentUrl);\n }\n\n /**\n * Close modal manually\n */\n closeModal(): void {\n if (this.currentModal) {\n this.currentModal.close();\n this.currentModal = null;\n }\n }\n\n /**\n * Remove iframe manually\n */\n removeIframe(): void {\n if (this.currentIframe) {\n this.currentIframe.remove();\n this.currentIframe = null;\n }\n }\n\n /**\n * Close any active checkout UI (modal or iframe)\n */\n close(): void {\n this.closeModal();\n this.removeIframe();\n }\n\n /**\n * Show mock payment UI for testing\n */\n private async showMockPaymentUI(\n mode: string,\n result: any,\n container?: string | HTMLElement,\n onClose?: () => void\n ): Promise<void> {\n const mockHTML = this.generateMockPaymentHTML(result);\n\n switch (mode) {\n case 'modal':\n this.showMockModal(mockHTML, onClose);\n break;\n\n case 'iframe':\n this.showMockIframe(mockHTML, container, onClose);\n break;\n\n case 'redirect':\n default:\n const baseUrl = window.location.origin;\n const paymentUrl = `${baseUrl}/checkout/${result.subscription.id}`;\n console.log('[Recur SDK] 🎭 Mock mode: Would redirect to:', paymentUrl);\n // Show toast notification instead of alert\n const { RecurToast } = await import('../components/base/toast.js');\n RecurToast.show(\n `🎭 Mock 模式:在真實環境中,用戶會被導向付款頁面`,\n 'info',\n 8000\n );\n console.log('Payment URL:', paymentUrl);\n break;\n }\n }\n\n /**\n * Generate mock payment form HTML\n */\n private generateMockPaymentHTML(result: any): string {\n return `\n <!DOCTYPE html>\n <html>\n <head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <style>\n body {\n margin: 0;\n padding: 20px;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);\n min-height: 100vh;\n display: flex;\n align-items: center;\n justify-content: center;\n }\n .container {\n background: white;\n border-radius: 12px;\n padding: 40px;\n max-width: 500px;\n width: 100%;\n box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);\n }\n .badge {\n display: inline-block;\n padding: 4px 12px;\n background: #fed7d7;\n color: #c53030;\n border-radius: 12px;\n font-size: 12px;\n font-weight: 600;\n margin-bottom: 20px;\n border: 2px solid #fc8181;\n }\n h1 {\n color: #1a202c;\n margin-bottom: 24px;\n font-size: 24px;\n }\n .info {\n background: #f7fafc;\n border-radius: 8px;\n padding: 16px;\n margin-bottom: 24px;\n }\n .info-row {\n display: flex;\n justify-content: space-between;\n padding: 8px 0;\n border-bottom: 1px solid #e2e8f0;\n }\n .info-row:last-child {\n border-bottom: none;\n }\n .label {\n color: #718096;\n font-size: 14px;\n }\n .value {\n color: #2d3748;\n font-weight: 500;\n font-size: 14px;\n }\n .card-form {\n margin-bottom: 24px;\n }\n .form-group {\n margin-bottom: 16px;\n }\n label {\n display: block;\n margin-bottom: 8px;\n color: #2d3748;\n font-weight: 500;\n font-size: 14px;\n }\n .mock-input {\n width: 100%;\n padding: 12px 16px;\n border: 2px solid #e2e8f0;\n border-radius: 8px;\n background: #f7fafc;\n color: #6b7280;\n font-size: 14px;\n }\n button {\n width: 100%;\n padding: 14px;\n background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);\n color: white;\n border: none;\n border-radius: 8px;\n font-size: 16px;\n font-weight: 600;\n cursor: pointer;\n }\n button:hover {\n opacity: 0.9;\n }\n .note {\n margin-top: 16px;\n padding: 12px;\n background: #fef5e7;\n border-left: 4px solid #f59e0b;\n border-radius: 4px;\n font-size: 13px;\n color: #78350f;\n }\n </style>\n </head>\n <body>\n <div class=\"container\">\n <span class=\"badge\">🎭 MOCK MODE</span>\n <h1>訂閱付款(模擬)</h1>\n\n <div class=\"info\">\n <div class=\"info-row\">\n <span class=\"label\">方案</span>\n <span class=\"value\">${result.subscription.planName}</span>\n </div>\n <div class=\"info-row\">\n <span class=\"label\">金額</span>\n <span class=\"value\">NT$ ${result.subscription.amount}</span>\n </div>\n <div class=\"info-row\">\n <span class=\"label\">週期</span>\n <span class=\"value\">${result.subscription.billingPeriod}</span>\n </div>\n <div class=\"info-row\">\n <span class=\"label\">試用期</span>\n <span class=\"value\">${result.subscription.trialDays} 天</span>\n </div>\n </div>\n\n <div class=\"card-form\">\n <div class=\"form-group\">\n <label>信用卡號</label>\n <input type=\"text\" class=\"mock-input\" value=\"4242 4242 4242 4242 (Mock)\" readonly>\n </div>\n <div class=\"form-group\">\n <label>有效期限</label>\n <input type=\"text\" class=\"mock-input\" value=\"12 / 25 (Mock)\" readonly>\n </div>\n <div class=\"form-group\">\n <label>安全碼</label>\n <input type=\"text\" class=\"mock-input\" value=\"123 (Mock)\" readonly>\n </div>\n </div>\n\n <button onclick=\"handleMockPayment()\">確認付款(模擬)</button>\n\n <div class=\"note\">\n <strong>🎭 Mock 模式說明</strong><br>\n 這是模擬的付款表單。點擊「確認付款」會模擬成功的支付流程。\n </div>\n </div>\n\n <script>\n function handleMockPayment() {\n console.log('🎭 Mock 模式:付款成功!');\n\n // Notify parent to show toast\n if (window.parent !== window) {\n window.parent.postMessage({\n type: 'recur-mock-payment-success',\n showToast: {\n message: '🎭 Mock 模式:付款成功!在真實環境中會觸發實際的支付處理',\n type: 'success'\n }\n }, '*');\n } else {\n // If not in iframe, show toast directly\n console.log('Mock payment successful');\n }\n }\n </script>\n </body>\n </html>\n `;\n }\n\n /**\n * Show mock payment in modal\n */\n private showMockModal(html: string, onClose?: () => void): void {\n this.closeModal();\n\n // Create modal overlay\n const overlay = document.createElement('div');\n overlay.id = 'recur-mock-modal';\n overlay.style.cssText = `\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(0, 0, 0, 0.5);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 999999;\n `;\n\n // Create iframe container\n const container = document.createElement('div');\n container.style.cssText = `\n width: 90%;\n max-width: 600px;\n height: 80%;\n max-height: 700px;\n background: white;\n border-radius: 12px;\n overflow: hidden;\n position: relative;\n `;\n\n // Create close button\n const closeBtn = document.createElement('button');\n closeBtn.textContent = '✕';\n closeBtn.style.cssText = `\n position: absolute;\n top: 16px;\n right: 16px;\n background: white;\n border: none;\n width: 32px;\n height: 32px;\n border-radius: 50%;\n cursor: pointer;\n font-size: 18px;\n z-index: 1;\n box-shadow: 0 2px 8px rgba(0,0,0,0.15);\n `;\n closeBtn.onclick = () => {\n overlay.remove();\n if (onClose) onClose();\n };\n\n // Create iframe\n const iframe = document.createElement('iframe');\n iframe.style.cssText = 'width: 100%; height: 100%; border: none;';\n iframe.srcdoc = html;\n\n container.appendChild(closeBtn);\n container.appendChild(iframe);\n overlay.appendChild(container);\n document.body.appendChild(overlay);\n\n // Listen for mock payment success\n window.addEventListener('message', async (event) => {\n if (event.data.type === 'recur-mock-payment-success') {\n // Show toast if requested\n if (event.data.showToast) {\n const { RecurToast } = await import('../components/base/toast.js');\n RecurToast.show(\n event.data.showToast.message,\n event.data.showToast.type || 'success',\n 5000\n );\n }\n\n overlay.remove();\n if (onClose) onClose();\n }\n });\n }\n\n /**\n * Show mock payment in iframe\n */\n private showMockIframe(\n html: string,\n container?: string | HTMLElement,\n onClose?: () => void\n ): void {\n if (!container) {\n throw new Error('Container is required for iframe mode');\n }\n\n const containerEl = typeof container === 'string'\n ? document.getElementById(container) || document.querySelector(container)\n : container;\n\n if (!containerEl) {\n throw new Error('Container element not found');\n }\n\n // Clear container\n containerEl.innerHTML = '';\n\n // Create iframe\n const iframe = document.createElement('iframe');\n iframe.style.cssText = 'width: 100%; height: 100%; border: none; min-height: 600px;';\n iframe.srcdoc = html;\n\n containerEl.appendChild(iframe);\n\n // Listen for mock payment success\n window.addEventListener('message', async (event) => {\n if (event.data.type === 'recur-mock-payment-success') {\n // Show toast if requested\n if (event.data.showToast) {\n const { RecurToast } = await import('../components/base/toast.js');\n RecurToast.show(\n event.data.showToast.message,\n event.data.showToast.type || 'success',\n 5000\n );\n }\n\n if (onClose) onClose();\n }\n });\n }\n}\n\n// Global API for browser usage\nexport function init(config: VanillaRecurConfig): RecurCheckout {\n return new RecurCheckout(config);\n}\n\n// Default export\nexport default {\n init,\n RecurCheckout,\n RecurElements,\n createElements,\n};\n\n// Export Elements class separately\nexport { RecurElements, createElements };\nexport type { RecurElementsConfig, ConfirmPaymentOptions, PaymentResult };\n\n// Expose types for TypeScript users\nexport type * from './types.js';\n","/**\n * Recur SDK Web Components\n *\n * This module exports all Web Components used in the Recur SDK.\n * Components are automatically registered when this module is imported.\n *\n * NOTE: Safe to import in SSR environments (Next.js, etc.)\n * Components are dynamically loaded only in browser environments.\n */\n\n/**\n * Register all Web Components\n *\n * This function loads and registers components dynamically to avoid\n * SSR issues with HTMLElement and other browser-only APIs.\n */\nexport async function registerComponents(): Promise<void> {\n if (typeof window === 'undefined') {\n console.warn('[Recur SDK] Cannot register Web Components: window is not defined');\n return;\n }\n\n // Dynamically import components (browser-only)\n await Promise.all([\n import('./base/loading-spinner.js'),\n import('./base/success-message.js'),\n import('./base/error-display.js'),\n import('./base/skeleton-loader.js'),\n import('./base/payment-form-skeleton.js'),\n import('./base/toast.js'),\n import('./payment-form.js'),\n ]);\n\n // Verify all components are registered\n const components = [\n 'recur-loading-spinner',\n 'recur-success-message',\n 'recur-error-display',\n 'recur-skeleton-loader',\n 'recur-payment-form-skeleton',\n 'recur-toast',\n 'recur-toast-container',\n 'recur-payment-form',\n ];\n\n const unregistered = components.filter((name) => !customElements.get(name));\n\n if (unregistered.length > 0) {\n console.warn(\n `[Recur SDK] The following components are not registered:`,\n unregistered\n );\n }\n}\n\n// Auto-register on import (browser only)\nif (typeof window !== 'undefined') {\n registerComponents();\n}\n\n// Re-export types for TypeScript users (these don't cause SSR issues)\nexport type { RecurLoadingSpinner } from './base/loading-spinner.js';\nexport type { RecurSuccessMessage } from './base/success-message.js';\nexport type { RecurErrorDisplay } from './base/error-display.js';\nexport type { RecurSkeletonLoader } from './base/skeleton-loader.js';\nexport type { RecurPaymentFormSkeleton } from './base/payment-form-skeleton.js';\nexport type { RecurToast, RecurToastContainer } from './base/toast.js';\nexport type { RecurPaymentForm } from './payment-form.js';\n","/**\n * Vanilla SDK Core\n *\n * Core API logic for subscription checkout\n */\n\nimport type { VanillaRecurConfig, VanillaCheckoutOptions, CheckoutResult, CheckoutError, PlansResult } from './types.js';\n\nexport class RecurCore {\n private config: VanillaRecurConfig;\n\n constructor(config: VanillaRecurConfig) {\n if (!config.publishableKey) {\n throw new Error('publishableKey is required');\n }\n\n // Use configured baseUrl or default to production API\n this.config = {\n baseUrl: 'https://api.recur.tw',\n ...config,\n };\n }\n\n /**\n * Call the subscription API\n */\n async createSubscription(options: VanillaCheckoutOptions): Promise<CheckoutResult> {\n const { planId, customerName, customerEmail, customerPhone } = options;\n\n if (!planId) {\n throw new Error('planId is required');\n }\n\n const response = await fetch(`${this.config.baseUrl}/v1/subscriptions`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'X-Recur-Publishable-Key': this.config.publishableKey,\n },\n body: JSON.stringify({\n planId,\n customerName,\n customerEmail,\n customerPhone,\n }),\n });\n\n if (!response.ok) {\n const errorData = await response.json().catch(() => ({}));\n const error: CheckoutError = {\n code: errorData.error || 'CHECKOUT_FAILED',\n message: errorData.message || 'Failed to initiate checkout',\n details: errorData,\n };\n throw error;\n }\n\n return await response.json();\n }\n\n /**\n * Fetch available subscription plans\n */\n async fetchPlans(): Promise<PlansResult> {\n const response = await fetch(`${this.config.baseUrl}/v1/plans`, {\n method: 'GET',\n headers: {\n 'Content-Type': 'application/json',\n 'X-Recur-Publishable-Key': this.config.publishableKey,\n },\n });\n\n if (!response.ok) {\n const errorData = await response.json().catch(() => ({}));\n const error: CheckoutError = {\n code: errorData.error || 'FETCH_PLANS_FAILED',\n message: errorData.message || 'Failed to fetch plans',\n details: errorData,\n };\n throw error;\n }\n\n return await response.json();\n }\n\n getConfig(): VanillaRecurConfig {\n return { ...this.config };\n }\n}\n","/**\n * Vanilla SDK Modal\n *\n * Modal window for payment flow\n */\n\nexport class RecurModal {\n private overlay: HTMLDivElement | null = null;\n private modal: HTMLDivElement | null = null;\n private iframe: HTMLIFrameElement | null = null;\n private onClose?: () => void;\n\n constructor(onClose?: () => void) {\n this.onClose = onClose;\n }\n\n /**\n * Open modal with payment URL\n */\n open(paymentUrl: string): void {\n // Create overlay\n this.overlay = document.createElement('div');\n this.overlay.style.cssText = `\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(0, 0, 0, 0.5);\n z-index: 9998;\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 20px;\n `;\n\n // Create modal container\n this.modal = document.createElement('div');\n this.modal.style.cssText = `\n position: relative;\n background: white;\n border-radius: 8px;\n width: 100%;\n max-width: 600px;\n height: 90vh;\n max-height: 700px;\n box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);\n display: flex;\n flex-direction: column;\n overflow: hidden;\n z-index: 9999;\n `;\n\n // Create close button\n const closeButton = document.createElement('button');\n closeButton.innerHTML = '✕';\n closeButton.style.cssText = `\n position: absolute;\n top: 12px;\n right: 12px;\n width: 32px;\n height: 32px;\n border: none;\n background: rgba(0, 0, 0, 0.1);\n border-radius: 50%;\n cursor: pointer;\n font-size: 18px;\n line-height: 1;\n z-index: 10000;\n transition: background 0.2s;\n color: #333;\n `;\n closeButton.onmouseover = () => {\n closeButton.style.background = 'rgba(0, 0, 0, 0.2)';\n };\n closeButton.onmouseout = () => {\n closeButton.style.background = 'rgba(0, 0, 0, 0.1)';\n };\n closeButton.onclick = () => this.close();\n\n // Create iframe\n this.iframe = document.createElement('iframe');\n this.iframe.src = paymentUrl;\n this.iframe.style.cssText = `\n width: 100%;\n height: 100%;\n border: none;\n border-radius: 8px;\n `;\n\n // Allow payment provider features\n this.iframe.setAttribute('allow', 'payment');\n this.iframe.setAttribute('sandbox', 'allow-same-origin allow-scripts allow-forms allow-popups allow-top-navigation');\n\n // Assemble\n this.modal.appendChild(closeButton);\n this.modal.appendChild(this.iframe);\n this.overlay.appendChild(this.modal);\n document.body.appendChild(this.overlay);\n\n // Prevent body scroll\n document.body.style.overflow = 'hidden';\n\n // Close on overlay click\n this.overlay.onclick = (e) => {\n if (e.target === this.overlay) {\n this.close();\n }\n };\n\n // Listen for payment completion (via postMessage)\n window.addEventListener('message', this.handleMessage);\n }\n\n /**\n * Close modal\n */\n close(): void {\n if (this.overlay && this.overlay.parentNode) {\n this.overlay.parentNode.removeChild(this.overlay);\n }\n\n // Restore body scroll\n document.body.style.overflow = '';\n\n // Clean up\n this.overlay = null;\n this.modal = null;\n this.iframe = null;\n\n // Remove message listener\n window.removeEventListener('message', this.handleMessage);\n\n // Call onClose callback\n if (this.onClose) {\n this.onClose();\n }\n }\n\n /**\n * Handle postMessage from iframe\n */\n private handleMessage = (event: MessageEvent): void => {\n // Only accept messages from same origin\n if (event.origin !== window.location.origin) {\n return;\n }\n\n // Check for payment completion signals\n if (event.data?.type === 'RECUR_PAYMENT_COMPLETE') {\n this.close();\n } else if (event.data?.type === 'RECUR_PAYMENT_CANCEL') {\n this.close();\n }\n };\n}\n","/**\n * Vanilla SDK iframe Embedding\n *\n * Embed payment flow in an iframe\n */\n\nexport class RecurIframe {\n private container: HTMLElement;\n private iframe: HTMLIFrameElement | null = null;\n private onClose?: () => void;\n\n constructor(container: string | HTMLElement, onClose?: () => void) {\n if (typeof container === 'string') {\n const element = document.querySelector<HTMLElement>(container);\n if (!element) {\n throw new Error(`Container element not found: ${container}`);\n }\n this.container = element;\n } else {\n this.container = container;\n }\n\n this.onClose = onClose;\n }\n\n /**\n * Embed iframe with payment URL\n */\n embed(paymentUrl: string): void {\n // Clear existing content\n this.container.innerHTML = '';\n\n // Create iframe\n this.iframe = document.createElement('iframe');\n this.iframe.src = paymentUrl;\n this.iframe.style.cssText = `\n width: 100%;\n height: 600px;\n border: 1px solid #e5e7eb;\n border-radius: 8px;\n `;\n\n // Allow payment provider features\n this.iframe.setAttribute('allow', 'payment');\n this.iframe.setAttribute('sandbox', 'allow-same-origin allow-scripts allow-forms allow-popups allow-top-navigation');\n\n // Append to container\n this.container.appendChild(this.iframe);\n\n // Listen for payment completion\n window.addEventListener('message', this.handleMessage);\n }\n\n /**\n * Remove iframe\n */\n remove(): void {\n if (this.iframe && this.iframe.parentNode) {\n this.iframe.parentNode.removeChild(this.iframe);\n }\n\n this.iframe = null;\n\n // Remove message listener\n window.removeEventListener('message', this.handleMessage);\n\n // Call onClose callback\n if (this.onClose) {\n this.onClose();\n }\n }\n\n /**\n * Handle postMessage from iframe\n */\n private handleMessage = (event: MessageEvent): void => {\n // Only accept messages from same origin\n if (event.origin !== window.location.origin) {\n return;\n }\n\n // Check for payment completion signals\n if (event.data?.type === 'RECUR_PAYMENT_COMPLETE') {\n this.remove();\n } else if (event.data?.type === 'RECUR_PAYMENT_CANCEL') {\n this.remove();\n }\n };\n}\n","/**\n * Embedded Checkout\n *\n * Renders a complete checkout form with embedded credit card fields\n * Powered by PAYUNi SDK for secure card input\n */\n\nimport type { VanillaRecurConfig, CheckoutError, CheckoutResult } from './types.js';\n\nexport interface EmbeddedCheckoutOptions {\n planId: string;\n container: string | HTMLElement;\n customerEmail?: string;\n customerName?: string;\n customerPhone?: string;\n onSuccess?: (result: CheckoutResult) => void;\n onError?: (error: CheckoutError) => void;\n}\n\ninterface InitCheckoutResponse {\n sdkToken: string;\n env: 'S' | 'P';\n timestamp: number;\n plan?: {\n id: string;\n name: string;\n price: number;\n billingPeriod: string;\n description?: string;\n trialDays?: number;\n };\n}\n\nexport class EmbeddedCheckout {\n private config: VanillaRecurConfig;\n private options: EmbeddedCheckoutOptions;\n private container: HTMLElement;\n private sdkToken: string | null = null;\n private sdkEnv: 'S' | 'P' = 'S';\n private payuniSDK: any = null;\n private isFormValid: boolean = false;\n\n constructor(config: VanillaRecurConfig, options: EmbeddedCheckoutOptions) {\n this.config = config;\n this.options = options;\n\n // Resolve container\n if (typeof options.container === 'string') {\n const element = document.querySelector(options.container);\n if (!element) {\n throw new Error(`Container not found: ${options.container}`);\n }\n this.container = element as HTMLElement;\n } else {\n this.container = options.container;\n }\n }\n\n /**\n * Initialize and render the checkout form\n */\n async render(): Promise<void> {\n try {\n // 1. Fetch SDK token and plan info\n await this.initCheckout();\n\n // 2. Render HTML structure\n this.renderHTML();\n\n // 3. Load PAYUNi SDK script\n await this.loadPayUniSDK();\n\n // 4. Initialize PAYUNi SDK with card fields\n await this.initPayUniSDK();\n\n // 5. Setup form submission\n this.setupFormSubmit();\n } catch (error: any) {\n this.handleError(error);\n }\n }\n\n /**\n * Initialize checkout session\n */\n private async initCheckout(): Promise<void> {\n // Auto-detect base URL based on environment\n const getDefaultBaseUrl = () => {\n // Development environment: use relative path /api\n if (typeof window !== 'undefined') {\n const hostname = window.location.hostname;\n if (hostname === 'localhost' ||\n hostname.includes('.test') ||\n hostname.includes('.local') ||\n hostname === '127.0.0.1') {\n return `${window.location.protocol}//${window.location.host}/api`;\n }\n }\n\n // Production environment: use API subdomain\n return 'https://api.recur.tw';\n };\n\n const baseUrl = this.config.baseUrl || getDefaultBaseUrl();\n\n const response = await fetch(`${baseUrl}/v1/checkout/init`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'X-Recur-Publishable-Key': this.config.publishableKey,\n },\n body: JSON.stringify({\n planId: this.options.planId,\n iframeDomain: window.location.origin,\n }),\n });\n\n if (!response.ok) {\n const error = await response.json().catch(() => ({}));\n throw new Error(error.error || 'Failed to initialize checkout');\n }\n\n const data: InitCheckoutResponse = await response.json();\n this.sdkToken = data.sdkToken;\n this.sdkEnv = data.env;\n }\n\n /**\n * Render checkout form HTML\n */\n private renderHTML(): void {\n this.container.innerHTML = `\n <div class=\"recur-embedded-checkout\" style=\"max-width: 400px; margin: 0 auto; padding: 20px; font-family: system-ui, -apple-system, sans-serif;\">\n <div class=\"recur-checkout-header\" style=\"margin-bottom: 24px;\">\n <h2 style=\"margin: 0 0 8px 0; font-size: 20px; font-weight: 600; color: #111;\">Subscribe</h2>\n <p style=\"margin: 0; font-size: 14px; color: #666;\">Complete your subscription</p>\n </div>\n\n <form id=\"recur-checkout-form\" style=\"display: flex; flex-direction: column; gap: 16px;\">\n <div class=\"recur-field\">\n <label style=\"display: block; margin-bottom: 6px; font-size: 14px; font-weight: 500; color: #374151;\">Email</label>\n <input\n type=\"email\"\n id=\"recur-email\"\n required\n value=\"${this.options.customerEmail || ''}\"\n style=\"width: 100%; padding: 8px 12px; border: 1px solid #d1d5db; border-radius: 6px; font-size: 14px; outline: none; transition: border-color 0.2s;\"\n onfocus=\"this.style.borderColor='#3b82f6'\"\n onblur=\"this.style.borderColor='#d1d5db'\"\n />\n </div>\n\n <div class=\"recur-field\">\n <label style=\"display: block; margin-bottom: 6px; font-size: 14px; font-weight: 500; color: #374151;\">Name</label>\n <input\n type=\"text\"\n id=\"recur-name\"\n required\n value=\"${this.options.customerName || ''}\"\n style=\"width: 100%; padding: 8px 12px; border: 1px solid #d1d5db; border-radius: 6px; font-size: 14px; outline: none; transition: border-color 0.2s;\"\n onfocus=\"this.style.borderColor='#3b82f6'\"\n onblur=\"this.style.borderColor='#d1d5db'\"\n />\n </div>\n\n <div class=\"recur-field\">\n <label style=\"display: block; margin-bottom: 6px; font-size: 14px; font-weight: 500; color: #374151;\">Phone (Optional)</label>\n <input\n type=\"tel\"\n id=\"recur-phone\"\n value=\"${this.options.customerPhone || ''}\"\n style=\"width: 100%; padding: 8px 12px; border: 1px solid #d1d5db; border-radius: 6px; font-size: 14px; outline: none; transition: border-color 0.2s;\"\n onfocus=\"this.style.borderColor='#3b82f6'\"\n onblur=\"this.style.borderColor='#d1d5db'\"\n />\n </div>\n\n <div class=\"recur-divider\" style=\"height: 1px; background: #e5e7eb; margin: 8px 0;\"></div>\n\n <div class=\"recur-field\">\n <label style=\"display: block; margin-bottom: 6px; font-size: 14px; font-weight: 500; color: #374151;\">Card Number</label>\n <div id=\"recur-card-number\" style=\"padding: 8px 12px; border: 1px solid #d1d5db; border-radius: 6px; min-height: 38px;\"></div>\n </div>\n\n <div style=\"display: grid; grid-template-columns: 1fr 1fr; gap: 12px;\">\n <div class=\"recur-field\">\n <label style=\"display: block; margin-bottom: 6px; font-size: 14px; font-weight: 500; color: #374151;\">Expiry</label>\n <div id=\"recur-card-exp\" style=\"padding: 8px 12px; border: 1px solid #d1d5db; border-radius: 6px; min-height: 38px;\"></div>\n </div>\n <div class=\"recur-field\">\n <label style=\"display: block; margin-bottom: 6px; font-size: 14px; font-weight: 500; color: #374151;\">CVC</label>\n <div id=\"recur-card-cvc\" style=\"padding: 8px 12px; border: 1px solid #d1d5db; border-radius: 6px; min-height: 38px;\"></div>\n </div>\n </div>\n\n <div id=\"recur-error\" style=\"display: none; padding: 12px; background: #fef2f2; border: 1px solid #fecaca; border-radius: 6px; color: #dc2626; font-size: 14px;\"></div>\n\n <button\n type=\"submit\"\n id=\"recur-submit-btn\"\n style=\"width: 100%; padding: 12px; background: #3b82f6; color: white; border: none; border-radius: 6px; font-size: 16px; font-weight: 500; cursor: pointer; transition: background 0.2s;\"\n onmouseover=\"this.style.background='#2563eb'\"\n onmouseout=\"this.style.background='#3b82f6'\"\n disabled\n >\n Subscribe Now\n </button>\n\n <p style=\"margin: 12px 0 0 0; text-align: center; font-size: 12px; color: #9ca3af;\">\n Secure payment powered by Recur\n </p>\n </form>\n </div>\n `;\n }\n\n /**\n * Load PAYUNi SDK script dynamically\n */\n private async loadPayUniSDK(): Promise<void> {\n return new Promise((resolve, reject) => {\n // Check if already loaded\n if ((window as any).UniPayment) {\n resolve();\n return;\n }\n\n const script = document.createElement('script');\n script.src = this.sdkEnv === 'P'\n ? 'https://vendor.payuni.com.tw/sdk/uni-payment.js'\n : 'https://sandbox-vendor.payuni.com.tw/sdk/uni-payment.js';\n script.async = true;\n script.onload = () => resolve();\n script.onerror = () => reject(new Error('Failed to load PAYUNi SDK'));\n document.head.appendChild(script);\n });\n }\n\n /**\n * Initialize PAYUNi SDK with credit card fields\n */\n private async initPayUniSDK(): Promise<void> {\n if (!this.sdkToken) {\n throw new Error('SDK token not initialized');\n }\n\n const UniPayment = (window as any).UniPayment;\n if (!UniPayment) {\n throw new Error('PAYUNi SDK not loaded');\n }\n\n const options = {\n env: this.sdkEnv,\n useInst: false,\n elements: {\n CardNo: 'recur-card-number',\n CardExp: 'recur-card-exp',\n CardCvc: 'recur-card-cvc',\n },\n style: {\n color: '#111827',\n errorColor: '#dc2626',\n fontSize: '14px',\n fontWeight: '400',\n lineHeight: '24px',\n },\n };\n\n this.payuniSDK = UniPayment.createSession(this.sdkToken, options);\n await this.payuniSDK.start();\n\n // Listen for card validation updates\n this.payuniSDK.onUpdate((update: any) => {\n const status = update?.status;\n if (status) {\n this.isFormValid = status.CardNo === true && status.CardExp === true && status.CardCvc === true;\n this.updateSubmitButton();\n }\n });\n }\n\n /**\n * Update submit button state\n */\n private updateSubmitButton(): void {\n const submitBtn = document.getElementById('recur-submit-btn') as HTMLButtonElement;\n if (submitBtn) {\n submitBtn.disabled = !this.isFormValid;\n submitBtn.style.opacity = this.isFormValid ? '1' : '0.5';\n submitBtn.style.cursor = this.isFormValid ? 'pointer' : 'not-allowed';\n }\n }\n\n /**\n * Setup form submission handler\n */\n private setupFormSubmit(): void {\n const form = document.getElementById('recur-checkout-form') as HTMLFormElement;\n if (!form) return;\n\n form.addEventListener('submit', async (e) => {\n e.preventDefault();\n await this.handleSubmit();\n });\n }\n\n /**\n * Handle form submission\n */\n private async handleSubmit(): Promise<void> {\n try {\n this.showLoading(true);\n this.hideError();\n\n // Get form data\n const email = (document.getElementById('recur-email') as HTMLInputElement).value;\n const name = (document.getElementById('recur-name') as HTMLInputElement).value;\n const phone = (document.getElementById('recur-phone') as HTMLInputElement).value;\n\n // Validate\n if (!email || !name) {\n throw new Error('Please fill in all required fields');\n }\n\n // Bind card data to SDK token\n if (!this.payuniSDK) {\n throw new Error('Payment system not initialized');\n }\n\n const cardResult = await this.payuniSDK.getTradeResult();\n if (cardResult.Status !== 'SUCCESS') {\n throw new Error(cardResult.Message || 'Card validation failed');\n }\n\n // Create subscription\n // Auto-detect base URL based on environment\n const getDefaultBaseUrl = () => {\n // Development environment: use relative path /api\n if (typeof window !== 'undefined') {\n const hostname = window.location.hostname;\n if (hostname === 'localhost' ||\n hostname.includes('.test') ||\n hostname.includes('.local') ||\n hostname === '127.0.0.1') {\n return `${window.location.protocol}//${window.location.host}/api`;\n }\n }\n\n // Production environment: use API subdomain\n return 'https://api.recur.tw';\n };\n\n const baseUrl = this.config.baseUrl || getDefaultBaseUrl();\n const response = await fetch(`${baseUrl}/v1/subscriptions`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'X-Recur-Publishable-Key': this.config.publishableKey,\n },\n body: JSON.stringify({\n planId: this.options.planId,\n customerEmail: email,\n customerName: name,\n customerPhone: phone || undefined,\n }),\n });\n\n if (!response.ok) {\n const error = await response.json().catch(() => ({}));\n throw new Error(error.error || 'Failed to create subscription');\n }\n\n const result: CheckoutResult = await response.json();\n\n // Call success callback\n if (this.options.onSuccess) {\n this.options.onSuccess(result);\n }\n\n // Show success message\n this.showSuccess();\n } catch (error: any) {\n this.handleError(error);\n } finally {\n this.showLoading(false);\n }\n }\n\n /**\n * Show loading state\n */\n private showLoading(loading: boolean): void {\n const submitBtn = document.getElementById('recur-submit-btn') as HTMLButtonElement;\n if (submitBtn) {\n submitBtn.disabled = loading;\n submitBtn.textContent = loading ? 'Processing...' : 'Subscribe Now';\n }\n }\n\n /**\n * Show error message\n */\n private showError(message: string): void {\n const errorDiv = document.getElementById('recur-error');\n if (errorDiv) {\n errorDiv.textContent = message;\n errorDiv.style.display = 'block';\n }\n }\n\n /**\n * Hide error message\n */\n private hideError(): void {\n const errorDiv = document.getElementById('recur-error');\n if (errorDiv) {\n errorDiv.style.display = 'none';\n }\n }\n\n /**\n * Show success message\n */\n private showSuccess(): void {\n // Clear container\n this.container.innerHTML = '';\n\n // Use Web Component instead of innerHTML\n const successMessage = document.createElement('recur-success-message');\n successMessage.setAttribute('title', 'Subscription Complete!');\n successMessage.setAttribute('message', 'Thank you for subscribing. You will receive a confirmation email shortly.');\n\n this.container.appendChild(successMessage);\n }\n\n /**\n * Handle errors\n */\n private handleError(error: any): void {\n const message = error?.message || 'An error occurred';\n this.showError(message);\n\n const checkoutError: CheckoutError = {\n code: 'CHECKOUT_ERROR',\n message,\n };\n\n if (this.options.onError) {\n this.options.onError(checkoutError);\n }\n }\n}\n","/**\n * Recur Elements SDK\n *\n * A Stripe Elements-like SDK for embedding credit card forms\n * Allows rendering card form immediately with just publishable_key\n * without needing a checkout session first\n *\n * Usage:\n * ```typescript\n * // 1. Initialize with publishable key\n * const elements = new RecurElements('pk_test_xxx');\n *\n * // 2. Mount card form to container\n * await elements.mount('#card-container');\n *\n * // 3. Listen for events\n * elements.on('ready', (data) => console.log('Ready:', data));\n * elements.on('change', (data) => console.log('Valid:', data.valid));\n *\n * // 4. On form submit, tokenize and confirm payment\n * const token = await elements.tokenize();\n * const result = await elements.confirmPayment({\n * productId: 'prod_xxx',\n * email: 'user@example.com',\n * });\n * ```\n */\n\nexport interface RecurElementsConfig {\n publishableKey: string\n baseUrl?: string\n embedUrl?: string\n}\n\nexport interface ElementsReadyData {\n sessionId: string\n timestamp: number\n creditToken: string\n}\n\nexport interface CardChangeData {\n valid: boolean\n}\n\nexport interface CardTokenizedData {\n cardToken: string\n timestamp: number\n sessionId: string\n}\n\nexport interface ConfirmPaymentOptions {\n productId: string\n email: string\n name?: string\n phone?: string\n metadata?: Record<string, any>\n}\n\nexport interface PaymentResult {\n status: 'success' | 'failed'\n checkoutId?: string\n subscriptionId?: string\n orderId?: string\n message?: string\n canRetry?: boolean\n}\n\ntype EventType = 'ready' | 'change' | 'tokenized' | 'error'\ntype EventHandler = (data: any) => void\n\nexport class RecurElements {\n private publishableKey: string\n private baseUrl: string\n private embedUrl: string\n private iframe: HTMLIFrameElement | null = null\n private container: HTMLElement | null = null\n\n // Session data (received from iframe after ready)\n private sessionId: string | null = null\n private timestamp: number | null = null\n private creditToken: string | null = null\n\n // Card token (received after tokenize)\n private cardToken: string | null = null\n private cardTimestamp: number | null = null\n\n // Event handlers\n private eventHandlers: Map<EventType, Set<EventHandler>> = new Map()\n\n constructor(config: string | RecurElementsConfig) {\n if (typeof config === 'string') {\n this.publishableKey = config\n this.baseUrl = this.getDefaultBaseUrl()\n this.embedUrl = this.getDefaultEmbedUrl()\n } else {\n this.publishableKey = config.publishableKey\n this.baseUrl = config.baseUrl || this.getDefaultBaseUrl()\n this.embedUrl = config.embedUrl || this.getDefaultEmbedUrl()\n }\n\n // Setup message listener\n window.addEventListener('message', this.handleMessage.bind(this))\n }\n\n /**\n * Mount card form to container\n */\n async mount(container: string | HTMLElement): Promise<void> {\n const el = typeof container === 'string'\n ? document.querySelector(container)\n : container\n\n if (!el) {\n throw new Error(`Container not found: ${container}`)\n }\n\n this.container = el as HTMLElement\n\n // Create iframe\n this.iframe = document.createElement('iframe')\n this.iframe.src = `${this.embedUrl}/elements?key=${encodeURIComponent(this.publishableKey)}`\n this.iframe.style.cssText = 'width:100%;border:none;min-height:200px;'\n this.iframe.allow = 'payment'\n\n // Clear container and append iframe\n this.container.innerHTML = ''\n this.container.appendChild(this.iframe)\n\n // Wait for ready event\n return new Promise((resolve, reject) => {\n const timeout = setTimeout(() => {\n reject(new Error('Elements initialization timeout'))\n }, 30000) // 30 second timeout\n\n const readyHandler = () => {\n clearTimeout(timeout)\n this.off('ready', readyHandler)\n resolve()\n }\n\n const errorHandler = (error: any) => {\n clearTimeout(timeout)\n this.off('error', errorHandler)\n reject(new Error(error.message || 'Elements initialization failed'))\n }\n\n this.on('ready', readyHandler)\n this.on('error', errorHandler)\n })\n }\n\n /**\n * Register event handler\n */\n on(event: EventType, handler: EventHandler): void {\n if (!this.eventHandlers.has(event)) {\n this.eventHandlers.set(event, new Set())\n }\n this.eventHandlers.get(event)!.add(handler)\n }\n\n /**\n * Remove event handler\n */\n off(event: EventType, handler: EventHandler): void {\n const handlers = this.eventHandlers.get(event)\n if (handlers) {\n handlers.delete(handler)\n }\n }\n\n /**\n * Emit event to handlers\n */\n private emit(event: EventType, data?: any): void {\n const handlers = this.eventHandlers.get(event)\n if (handlers) {\n handlers.forEach(handler => handler(data))\n }\n }\n\n /**\n * Handle messages from iframe\n */\n private handleMessage(event: MessageEvent): void {\n // Only accept messages from our embed domain\n if (!this.isValidOrigin(event.origin)) {\n return\n }\n\n const { type, data } = event.data || {}\n\n switch (type) {\n case 'RECUR_ELEMENTS_READY':\n this.sessionId = data.sessionId\n this.timestamp = data.timestamp\n this.creditToken = data.creditToken\n this.emit('ready', data)\n break\n\n case 'RECUR_CARD_VALID':\n this.emit('change', { valid: data.valid })\n break\n\n case 'RECUR_CARD_TOKENIZED':\n this.cardToken = data.cardToken\n this.cardTimestamp = data.timestamp\n this.emit('tokenized', data)\n break\n\n case 'RECUR_PAYMENT_ERROR':\n this.emit('error', data)\n break\n\n case 'RECUR_RESIZE':\n // Auto-resize iframe to match content height\n if (this.iframe && data?.height) {\n this.iframe.style.height = `${data.height}px`\n }\n break\n }\n }\n\n /**\n * Check if message origin is valid\n */\n private isValidOrigin(origin: string): boolean {\n try {\n const embedOrigin = new URL(this.embedUrl).origin\n return origin === embedOrigin\n } catch {\n return false\n }\n }\n\n /**\n * Trigger tokenization\n */\n async tokenize(): Promise<CardTokenizedData> {\n if (!this.iframe || !this.iframe.contentWindow) {\n throw new Error('Elements not mounted')\n }\n\n // Send tokenize message to iframe\n const embedOrigin = new URL(this.embedUrl).origin\n this.iframe.contentWindow.postMessage({ type: 'RECUR_TOKENIZE' }, embedOrigin)\n\n // Wait for tokenized event\n return new Promise((resolve, reject) => {\n const timeout = setTimeout(() => {\n reject(new Error('Tokenization timeout'))\n }, 60000) // 60 second timeout\n\n const handler = (data: CardTokenizedData) => {\n clearTimeout(timeout)\n this.off('tokenized', handler)\n resolve(data)\n }\n\n const errorHandler = (error: any) => {\n clearTimeout(timeout)\n this.off('error', errorHandler)\n reject(new Error(error.message || 'Tokenization failed'))\n }\n\n this.on('tokenized', handler)\n this.on('error', errorHandler)\n })\n }\n\n /**\n * Confirm payment\n */\n async confirmPayment(options: ConfirmPaymentOptions): Promise<PaymentResult> {\n if (!this.sessionId || !this.cardToken) {\n throw new Error('Card not tokenized. Call tokenize() first.')\n }\n\n const response = await fetch(`${this.baseUrl}/api/v1/elements/confirm`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'X-Recur-Publishable-Key': this.publishableKey,\n },\n body: JSON.stringify({\n sessionId: this.sessionId,\n sdkToken: this.cardToken, // CreditHash from PAYUNi\n timestamp: this.cardTimestamp,\n creditToken: this.creditToken,\n productId: options.productId,\n email: options.email,\n name: options.name,\n phone: options.phone,\n metadata: options.metadata,\n }),\n })\n\n const result = await response.json()\n\n if (!response.ok) {\n return {\n status: 'failed',\n message: result.error || result.message || 'Payment failed',\n canRetry: result.canRetry ?? true,\n }\n }\n\n return result as PaymentResult\n }\n\n /**\n * Get default base URL based on environment\n */\n private getDefaultBaseUrl(): string {\n if (typeof window === 'undefined') {\n return 'https://app.recur.tw'\n }\n\n const hostname = window.location.hostname\n if (hostname === 'localhost' ||\n hostname.includes('.test') ||\n hostname.includes('.local') ||\n hostname === '127.0.0.1') {\n return `${window.location.protocol}//${hostname}:3000`\n }\n\n return 'https://app.recur.tw'\n }\n\n /**\n * Get default embed URL based on environment\n */\n private getDefaultEmbedUrl(): string {\n if (typeof window === 'undefined') {\n return 'https://embed.recur.tw'\n }\n\n const hostname = window.location.hostname\n if (hostname === 'localhost' ||\n hostname.includes('.test') ||\n hostname.includes('.local') ||\n hostname === '127.0.0.1') {\n return `${window.location.protocol}//${hostname}:3002`\n }\n\n return 'https://embed.recur.tw'\n }\n\n /**\n * Destroy elements and cleanup\n */\n destroy(): void {\n if (this.iframe && this.iframe.parentNode) {\n this.iframe.parentNode.removeChild(this.iframe)\n }\n this.iframe = null\n this.container = null\n this.sessionId = null\n this.timestamp = null\n this.creditToken = null\n this.cardToken = null\n this.cardTimestamp = null\n this.eventHandlers.clear()\n window.removeEventListener('message', this.handleMessage.bind(this))\n }\n}\n\n/**\n * Create RecurElements instance\n */\nexport function createElements(config: string | RecurElementsConfig): RecurElements {\n return new RecurElements(config)\n}\n\nexport default RecurElements\n"],"mappings":"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","names":["loading_spinner_exports","__export","RecurLoadingSpinner","init_loading_spinner","__esmMin","name","oldValue","newValue","sizeAttr","size","success_message_exports","__export","RecurSuccessMessage","init_success_message","__esmMin","name","oldValue","newValue","error_display_exports","__export","RecurErrorDisplay","init_error_display","__esmMin","name","oldValue","newValue","skeleton_loader_exports","__export","RecurSkeletonLoader","init_skeleton_loader","__esmMin","name","oldValue","newValue","typeAttr","content","payment_form_skeleton_exports","__export","RecurPaymentFormSkeleton","init_payment_form_skeleton","__esmMin","toast_exports","__export","RecurToast","RecurToastContainer","_RecurToastContainer","init_toast","__esmMin","typeAttr","durationAttr","duration","color","e","message","type","container","toast","__publicField","payment_form_exports","__export","RecurPaymentForm","init_payment_form","__esmMin","__publicField","submitBtn","newBtn","name","oldValue","newValue","styleElement","idSpecificStyles","orderSummary","customerInfo","cardFields","actions","existingSection","newSection","section","planName","amount","billingPeriod","periodText","customerName","customerEmail","sdkToken","environment","cardNoEl","cardExpEl","cardCvcEl","paymentSession","startError","data","allFieldsComplete","error","e","email","phone","emailInput","nameInput","phoneInput","loading","message","errorContainer","errorDisplay","resolve","observer","vanilla_exports","__export","RecurCheckout","RecurElements","createElements","vanilla_default","init","registerComponents","unregistered","name","RecurCore","config","__publicField","options","planId","customerName","customerEmail","customerPhone","response","errorData","RecurModal","onClose","__publicField","event","paymentUrl","closeButton","e","RecurIframe","container","onClose","__publicField","event","element","paymentUrl","EmbeddedCheckout","config","options","__publicField","element","error","getDefaultBaseUrl","hostname","baseUrl","response","data","resolve","reject","script","UniPayment","update","status","submitBtn","form","e","email","name","phone","cardResult","result","loading","message","errorDiv","successMessage","checkoutError","RecurElements","config","__publicField","container","el","resolve","reject","timeout","readyHandler","errorHandler","error","event","handler","handlers","data","type","origin","embedOrigin","options","response","result","hostname","createElements","RecurCheckout","config","__publicField","RecurCore","options","EmbeddedCheckout","result","mode","paymentUrl","error","onClose","RecurModal","container","RecurIframe","mockHTML","RecurToast","html","overlay","closeBtn","iframe","event","containerEl","init","vanilla_default","RecurElements","createElements"]}
|