recur-tw 0.3.5 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -403,6 +403,9 @@ interface CheckoutOptions {
403
403
  customerName?: string;
404
404
  customerPhone?: string;
405
405
 
406
+ // External customer ID - link to your existing users
407
+ externalCustomerId?: string;
408
+
406
409
  // Checkout mode
407
410
  mode?: 'embedded' | 'redirect'; // Default: 'embedded' if containerElementId is set
408
411
 
@@ -418,6 +421,68 @@ interface CheckoutOptions {
418
421
 
419
422
  ---
420
423
 
424
+ ### Customer Identification
425
+
426
+ The SDK supports multiple ways to identify customers across your system and Recur.
427
+
428
+ #### Using External Customer ID
429
+
430
+ The `externalCustomerId` parameter allows you to link Recur subscriptions to your existing user database:
431
+
432
+ ```tsx
433
+ // React
434
+ await checkout({
435
+ planId: 'plan_xxx',
436
+ customerEmail: 'user@example.com',
437
+ customerName: 'John Doe',
438
+ externalCustomerId: 'user_12345', // Your system's user ID
439
+ });
440
+
441
+ // Vanilla JS
442
+ await recur.checkout({
443
+ planId: 'plan_xxx',
444
+ customerEmail: 'user@example.com',
445
+ externalCustomerId: 'cus_abc456',
446
+ });
447
+
448
+ // Hosted Checkout
449
+ await recur.redirectToCheckout({
450
+ productId: 'prod_xxx',
451
+ successUrl: '/success',
452
+ cancelUrl: '/cancel',
453
+ customerEmail: 'user@example.com',
454
+ externalCustomerId: 'user_12345',
455
+ });
456
+ ```
457
+
458
+ #### Customer Resolution Priority
459
+
460
+ When processing a checkout, the SDK uses the following priority to identify customers:
461
+
462
+ 1. **externalCustomerId** - If provided, looks up customer by external ID first
463
+ 2. **customerEmail** - Falls back to email-based lookup
464
+ 3. **Create new** - Creates a new customer if no match found
465
+
466
+ If an `externalCustomerId` is provided but the customer already exists by email, the external ID will be bound to that existing customer.
467
+
468
+ #### Retrieving Customer by External ID
469
+
470
+ Use the API to retrieve customers by their external ID:
471
+
472
+ ```bash
473
+ GET /api/v1/subscribers/external/{externalCustomerId}
474
+ ```
475
+
476
+ This allows you to query subscription status and details using your own user identifiers.
477
+
478
+ #### Best Practices
479
+
480
+ - **Use consistent IDs**: Use your database primary key or UUID as the external ID
481
+ - **Set early**: Include `externalCustomerId` in the initial checkout request
482
+ - **Combine with email**: Always provide both email and external ID for reliability
483
+
484
+ ---
485
+
421
486
  ### Custom Styling
422
487
 
423
488
  The SDK uses Web Components with customizable styles. You can override PAYUNi input styles to match your design system.
package/dist/index.cjs CHANGED
@@ -2480,7 +2480,8 @@ function RecurProvider({ children, config: initialConfig = {} }) {
2480
2480
  productId: options.planId,
2481
2481
  customerName: options.customerName,
2482
2482
  customerEmail: options.customerEmail,
2483
- customerPhone: options.customerPhone
2483
+ customerPhone: options.customerPhone,
2484
+ externalCustomerId: options.externalCustomerId
2484
2485
  })
2485
2486
  });
2486
2487
  if (!checkoutResponse.ok) {
package/dist/index.d.cts CHANGED
@@ -214,6 +214,13 @@ interface CheckoutOptions {
214
214
  customerName?: string;
215
215
  customerEmail?: string;
216
216
  customerPhone?: string;
217
+ /**
218
+ * External customer ID from your system
219
+ * Use this to link Recur subscriptions to your existing users
220
+ *
221
+ * @example 'user_123', 'cus_abc456'
222
+ */
223
+ externalCustomerId?: string;
217
224
  /**
218
225
  * Override organization ID
219
226
  */
package/dist/index.d.ts CHANGED
@@ -214,6 +214,13 @@ interface CheckoutOptions {
214
214
  customerName?: string;
215
215
  customerEmail?: string;
216
216
  customerPhone?: string;
217
+ /**
218
+ * External customer ID from your system
219
+ * Use this to link Recur subscriptions to your existing users
220
+ *
221
+ * @example 'user_123', 'cus_abc456'
222
+ */
223
+ externalCustomerId?: string;
217
224
  /**
218
225
  * Override organization ID
219
226
  */
package/dist/index.js CHANGED
@@ -2474,7 +2474,8 @@ function RecurProvider({ children, config: initialConfig = {} }) {
2474
2474
  productId: options.planId,
2475
2475
  customerName: options.customerName,
2476
2476
  customerEmail: options.customerEmail,
2477
- customerPhone: options.customerPhone
2477
+ customerPhone: options.customerPhone,
2478
+ externalCustomerId: options.externalCustomerId
2478
2479
  })
2479
2480
  });
2480
2481
  if (!checkoutResponse.ok) {
package/dist/recur.umd.js CHANGED
@@ -1,4 +1,4 @@
1
- "use strict";var RecurCheckout=(()=>{var k=Object.defineProperty;var W=Object.getOwnPropertyDescriptor;var J=Object.getOwnPropertyNames;var Z=Object.prototype.hasOwnProperty;var G=(l,e,t)=>e in l?k(l,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):l[e]=t;var m=(l,e)=>()=>(l&&(e=l(l=0)),e);var d=(l,e)=>{for(var t in e)k(l,t,{get:e[t],enumerable:!0})},Q=(l,e,t,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of J(e))!Z.call(l,i)&&i!==t&&k(l,i,{get:()=>e[i],enumerable:!(r=W(e,i))||r.enumerable});return l};var ee=l=>Q(k({},"__esModule",{value:!0}),l);var n=(l,e,t)=>G(l,typeof e!="symbol"?e+"":e,t);var U={};d(U,{RecurLoadingSpinner:()=>x});var x,H=m(()=>{"use strict";x=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=`
1
+ "use strict";var RecurCheckout=(()=>{var C=Object.defineProperty;var ce=Object.getOwnPropertyDescriptor;var le=Object.getOwnPropertyNames;var de=Object.prototype.hasOwnProperty;var ue=(c,e,t)=>e in c?C(c,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):c[e]=t;var g=(c,e)=>()=>(c&&(e=c(c=0)),e);var h=(c,e)=>{for(var t in e)C(c,t,{get:e[t],enumerable:!0})},me=(c,e,t,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of le(e))!de.call(c,i)&&i!==t&&C(c,i,{get:()=>e[i],enumerable:!(r=ce(e,i))||r.enumerable});return c};var pe=c=>me(C({},"__esModule",{value:!0}),c);var a=(c,e,t)=>ue(c,typeof e!="symbol"?e+"":e,t);var B={};h(B,{RecurLoadingSpinner:()=>S});var S,N=g(()=>{"use strict";S=class extends HTMLElement{static get observedAttributes(){return["message","size"]}constructor(){super(),this.attachShadow({mode:"open"})}connectedCallback(){this.render()}attributeChangedCallback(e,t,r){t!==r&&this.render()}get message(){return this.getAttribute("message")||"\u6B63\u5728\u8655\u7406\u8A02\u95B1..."}get size(){let e=this.getAttribute("size");return e==="small"||e==="large"?e:"medium"}getSizeValue(){return{small:24,medium:40,large:56}[this.size]}render(){let e=this.getSizeValue();this.shadowRoot.innerHTML=`
2
2
  <style>
3
3
  :host {
4
4
  display: block;
@@ -40,7 +40,7 @@
40
40
 
41
41
  <div class="recur-sdk__spinner"></div>
42
42
  ${this.message?`<p class="recur-sdk__loading-text">${this.message}</p>`:""}
43
- `}};typeof window<"u"&&!customElements.get("recur-loading-spinner")&&customElements.define("recur-loading-spinner",x)});var $={};d($,{RecurSuccessMessage:()=>w});var w,A=m(()=>{"use strict";w=class extends HTMLElement{static get observedAttributes(){return["title","message","icon"]}constructor(){super(),this.attachShadow({mode:"open"})}connectedCallback(){this.render()}attributeChangedCallback(e,t,r){t!==r&&this.render()}get successTitle(){return this.getAttribute("title")||"Subscription Complete!"}get successMessage(){return this.getAttribute("message")||"Thank you for subscribing. Your payment has been processed successfully."}get showIcon(){return this.getAttribute("icon")!=="false"}render(){this.shadowRoot.innerHTML=`
43
+ `}};typeof window<"u"&&!customElements.get("recur-loading-spinner")&&customElements.define("recur-loading-spinner",S)});var K={};h(K,{RecurSuccessMessage:()=>I});var I,O=g(()=>{"use strict";I=class extends HTMLElement{static get observedAttributes(){return["title","message","icon"]}constructor(){super(),this.attachShadow({mode:"open"})}connectedCallback(){this.render()}attributeChangedCallback(e,t,r){t!==r&&this.render()}get successTitle(){return this.getAttribute("title")||"Subscription Complete!"}get successMessage(){return this.getAttribute("message")||"Thank you for subscribing. Your payment has been processed successfully."}get showIcon(){return this.getAttribute("icon")!=="false"}render(){this.shadowRoot.innerHTML=`
44
44
  <style>
45
45
  :host {
46
46
  display: block;
@@ -121,7 +121,7 @@
121
121
  <p class="recur-sdk__success-message">${this.successMessage}</p>
122
122
  <slot></slot>
123
123
  </div>
124
- `}};typeof window<"u"&&!customElements.get("recur-success-message")&&customElements.define("recur-success-message",w)});var B={};d(B,{RecurErrorDisplay:()=>E});var E,N=m(()=>{"use strict";E=class extends HTMLElement{static get observedAttributes(){return["error","dismissible"]}constructor(){super(),this.attachShadow({mode:"open"})}connectedCallback(){this.render()}attributeChangedCallback(e,t,r){t!==r&&this.render()}get error(){return this.getAttribute("error")||""}get isDismissible(){return this.getAttribute("dismissible")==="true"}handleDismiss(){this.dispatchEvent(new CustomEvent("dismiss",{bubbles:!0,composed:!0})),this.remove()}render(){if(!this.error){this.shadowRoot.innerHTML="";return}this.shadowRoot.innerHTML=`
124
+ `}};typeof window<"u"&&!customElements.get("recur-success-message")&&customElements.define("recur-success-message",I)});var F={};h(F,{RecurErrorDisplay:()=>T});var T,j=g(()=>{"use strict";T=class extends HTMLElement{static get observedAttributes(){return["error","dismissible"]}constructor(){super(),this.attachShadow({mode:"open"})}connectedCallback(){this.render()}attributeChangedCallback(e,t,r){t!==r&&this.render()}get error(){return this.getAttribute("error")||""}get isDismissible(){return this.getAttribute("dismissible")==="true"}handleDismiss(){this.dispatchEvent(new CustomEvent("dismiss",{bubbles:!0,composed:!0})),this.remove()}render(){if(!this.error){this.shadowRoot.innerHTML="";return}this.shadowRoot.innerHTML=`
125
125
  <style>
126
126
  :host {
127
127
  display: block;
@@ -211,7 +211,7 @@
211
211
  </button>
212
212
  `:""}
213
213
  </div>
214
- `,this.isDismissible&&this.shadowRoot.querySelector(".recur-sdk__error-dismiss")?.addEventListener("click",()=>this.handleDismiss())}};typeof window<"u"&&!customElements.get("recur-error-display")&&customElements.define("recur-error-display",E)});var D={};d(D,{RecurSkeletonLoader:()=>C});var C,O=m(()=>{"use strict";C=class extends HTMLElement{static get observedAttributes(){return["type"]}constructor(){super(),this.attachShadow({mode:"open"})}connectedCallback(){this.render()}attributeChangedCallback(e,t,r){t!==r&&this.render()}get type(){let e=this.getAttribute("type");return e==="list"||e==="card"?e:"payment-form"}renderPaymentFormSkeleton(){return`
214
+ `,this.isDismissible&&this.shadowRoot.querySelector(".recur-sdk__error-dismiss")?.addEventListener("click",()=>this.handleDismiss())}};typeof window<"u"&&!customElements.get("recur-error-display")&&customElements.define("recur-error-display",T)});var Y={};h(Y,{RecurSkeletonLoader:()=>R});var R,q=g(()=>{"use strict";R=class extends HTMLElement{static get observedAttributes(){return["type"]}constructor(){super(),this.attachShadow({mode:"open"})}connectedCallback(){this.render()}attributeChangedCallback(e,t,r){t!==r&&this.render()}get type(){let e=this.getAttribute("type");return e==="list"||e==="card"?e:"payment-form"}renderPaymentFormSkeleton(){return`
215
215
  <div class="skeleton-field">
216
216
  <div class="skeleton-label"></div>
217
217
  <div class="skeleton-input"></div>
@@ -377,7 +377,7 @@
377
377
  </style>
378
378
 
379
379
  ${e}
380
- `}};typeof window<"u"&&!customElements.get("recur-skeleton-loader")&&customElements.define("recur-skeleton-loader",C)});var F={};d(F,{RecurPaymentFormSkeleton:()=>T});var T,j=m(()=>{"use strict";T=class extends HTMLElement{constructor(){super(),this.attachShadow({mode:"open"})}connectedCallback(){this.render()}render(){this.shadowRoot.innerHTML=`
380
+ `}};typeof window<"u"&&!customElements.get("recur-skeleton-loader")&&customElements.define("recur-skeleton-loader",R)});var V={};h(V,{RecurPaymentFormSkeleton:()=>P});var P,X=g(()=>{"use strict";P=class extends HTMLElement{constructor(){super(),this.attachShadow({mode:"open"})}connectedCallback(){this.render()}render(){this.shadowRoot.innerHTML=`
381
381
  <style>
382
382
  :host {
383
383
  display: block;
@@ -644,7 +644,7 @@
644
644
  <p class="security-text">\u60A8\u7684\u4ED8\u6B3E\u8CC7\u8A0A\u7D93\u904E\u52A0\u5BC6\u4FDD\u8B77</p>
645
645
  </div>
646
646
  </div>
647
- `}};typeof window<"u"&&!customElements.get("recur-payment-form-skeleton")&&customElements.define("recur-payment-form-skeleton",T)});var b={};d(b,{RecurToast:()=>I,RecurToastContainer:()=>f});var I,u,f,g=m(()=>{"use strict";I=class extends HTMLElement{static get observedAttributes(){return["message","type","duration"]}constructor(){super(),this.attachShadow({mode:"open"})}connectedCallback(){this.render(),this.setupAutoDismiss()}get message(){return this.getAttribute("message")||"Notification"}get type(){let e=this.getAttribute("type");return e==="success"||e==="error"?e:"info"}get duration(){let e=this.getAttribute("duration");return e?parseInt(e,10):5e3}setupAutoDismiss(){let e=this.duration;e>0&&setTimeout(()=>this.dismiss(),e)}dismiss(){this.style.animation="recur-toast-slide-out 0.3s ease-in-out",setTimeout(()=>this.remove(),300)}getTypeIcon(){switch(this.type){case"success":return`<svg width="20" height="20" fill="none" stroke="currentColor" viewBox="0 0 24 24">
647
+ `}};typeof window<"u"&&!customElements.get("recur-payment-form-skeleton")&&customElements.define("recur-payment-form-skeleton",P)});var W={};h(W,{RecurToast:()=>M,RecurToastContainer:()=>k});var M,f,k,J=g(()=>{"use strict";M=class extends HTMLElement{static get observedAttributes(){return["message","type","duration"]}constructor(){super(),this.attachShadow({mode:"open"})}connectedCallback(){this.render(),this.setupAutoDismiss()}get message(){return this.getAttribute("message")||"Notification"}get type(){let e=this.getAttribute("type");return e==="success"||e==="error"?e:"info"}get duration(){let e=this.getAttribute("duration");return e?parseInt(e,10):5e3}setupAutoDismiss(){let e=this.duration;e>0&&setTimeout(()=>this.dismiss(),e)}dismiss(){this.style.animation="recur-toast-slide-out 0.3s ease-in-out",setTimeout(()=>this.remove(),300)}getTypeIcon(){switch(this.type){case"success":return`<svg width="20" height="20" fill="none" stroke="currentColor" viewBox="0 0 24 24">
648
648
  <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7" />
649
649
  </svg>`;case"error":return`<svg width="20" height="20" fill="none" stroke="currentColor" viewBox="0 0 24 24">
650
650
  <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
@@ -764,7 +764,7 @@
764
764
  </svg>
765
765
  </button>
766
766
  </div>
767
- `,this.shadowRoot.querySelector(".toast-close")?.addEventListener("click",i=>{i.stopPropagation(),this.dismiss()}),this.shadowRoot.querySelector(".toast")?.addEventListener("click",()=>this.dismiss())}static show(e,t="info",r=5e3){let i=f.getInstance(),s=document.createElement("recur-toast");return s.setAttribute("message",e),s.setAttribute("type",t),s.setAttribute("duration",r.toString()),i.appendChild(s),s}},u=class u extends HTMLElement{constructor(){super(),this.attachShadow({mode:"open"})}connectedCallback(){this.render()}render(){this.shadowRoot.innerHTML=`
767
+ `,this.shadowRoot.querySelector(".toast-close")?.addEventListener("click",i=>{i.stopPropagation(),this.dismiss()}),this.shadowRoot.querySelector(".toast")?.addEventListener("click",()=>this.dismiss())}static show(e,t="info",r=5e3){let i=k.getInstance(),s=document.createElement("recur-toast");return s.setAttribute("message",e),s.setAttribute("type",t),s.setAttribute("duration",r.toString()),i.appendChild(s),s}},f=class f extends HTMLElement{constructor(){super(),this.attachShadow({mode:"open"})}connectedCallback(){this.render()}render(){this.shadowRoot.innerHTML=`
768
768
  <style>
769
769
  :host {
770
770
  position: fixed;
@@ -791,7 +791,7 @@
791
791
  </style>
792
792
 
793
793
  <slot></slot>
794
- `}static getInstance(){return u.instance||(u.instance=document.querySelector("recur-toast-container"),u.instance||(u.instance=document.createElement("recur-toast-container"),document.body.appendChild(u.instance))),u.instance}};n(u,"instance",null);f=u;typeof window<"u"&&!customElements.get("recur-toast")&&customElements.define("recur-toast",I);typeof window<"u"&&!customElements.get("recur-toast-container")&&customElements.define("recur-toast-container",f)});var K={};d(K,{RecurPaymentForm:()=>M});var M,q=m(()=>{"use strict";M=class extends HTMLElement{constructor(){super();n(this,"containerId");n(this,"customStyles");n(this,"_isInitializing",!1);n(this,"_initializationAborted",!1);this.containerId=this.getAttribute("container-id")||`recur-${Date.now()}`,this.customStyles=this.getAttribute("custom-styles")||"",this.attachShadow({mode:"open"})}connectedCallback(){this.render()}disconnectedCallback(){console.log("[PaymentForm] Component disconnected, cleaning up..."),this._initializationAborted=!0,this._paymentSession=null;let t=document.getElementById(`${this.containerId}-submit-btn`);if(t){let r=t.cloneNode(!0);t.parentNode?.replaceChild(r,t)}}static get observedAttributes(){return["custom-styles","customer-name","customer-email","plan-name","amount","billing-period"]}attributeChangedCallback(t,r,i){t==="custom-styles"&&r!==i?(this.customStyles=i||"",this.updateCustomStyles()):r!==i&&this.updateCustomerInfoSection()}render(){this.shadowRoot.innerHTML=`
794
+ `}static getInstance(){return f.instance||(f.instance=document.querySelector("recur-toast-container"),f.instance||(f.instance=document.createElement("recur-toast-container"),document.body.appendChild(f.instance))),f.instance}};a(f,"instance",null);k=f;typeof window<"u"&&!customElements.get("recur-toast")&&customElements.define("recur-toast",M);typeof window<"u"&&!customElements.get("recur-toast-container")&&customElements.define("recur-toast-container",k)});var Z={};h(Z,{RecurPaymentForm:()=>L});var L,G=g(()=>{"use strict";L=class extends HTMLElement{constructor(){super();a(this,"containerId");a(this,"customStyles");a(this,"_isInitializing",!1);a(this,"_initializationAborted",!1);this.containerId=this.getAttribute("container-id")||`recur-${Date.now()}`,this.customStyles=this.getAttribute("custom-styles")||"",this.attachShadow({mode:"open"})}connectedCallback(){this.render()}disconnectedCallback(){console.log("[PaymentForm] Component disconnected, cleaning up..."),this._initializationAborted=!0,this._paymentSession=null;let t=document.getElementById(`${this.containerId}-submit-btn`);if(t){let r=t.cloneNode(!0);t.parentNode?.replaceChild(r,t)}}static get observedAttributes(){return["custom-styles","customer-name","customer-email","plan-name","amount","billing-period"]}attributeChangedCallback(t,r,i){t==="custom-styles"&&r!==i?(this.customStyles=i||"",this.updateCustomStyles()):r!==i&&this.updateCustomerInfoSection()}render(){this.shadowRoot.innerHTML=`
795
795
  <style>
796
796
  /* \u9019\u4E9B\u6A23\u5F0F\u88AB\u9694\u96E2\u5728 Shadow DOM \u5167\uFF0C\u4E0D\u6703\u5F71\u97FF\u5916\u90E8 */
797
797
  :host {
@@ -901,7 +901,7 @@
901
901
  height: 36px !important;
902
902
  }
903
903
  `;t.textContent=this.customStyles+`
904
- `+r,this.appendChild(t);let i=this.createOrderSummarySection();i.slot="order-summary",this.appendChild(i);let s=this.createCustomerInfoSection();s.slot="customer-info",this.appendChild(s);let a=this.createCardFieldsSection();a.slot="card-fields",this.appendChild(a);let o=this.createActionsSection();o.slot="actions",this.appendChild(o)}updateCustomerInfoSection(){let t=this.querySelector('[slot="customer-info"]');if(t){let r=this.createCustomerInfoSection();r.slot="customer-info",t.replaceWith(r)}}updateCustomStyles(){let t=this.querySelector(`#${this.containerId}-custom-styles`);if(t){let r=`
904
+ `+r,this.appendChild(t);let i=this.createOrderSummarySection();i.slot="order-summary",this.appendChild(i);let s=this.createCustomerInfoSection();s.slot="customer-info",this.appendChild(s);let n=this.createCardFieldsSection();n.slot="card-fields",this.appendChild(n);let o=this.createActionsSection();o.slot="actions",this.appendChild(o)}updateCustomerInfoSection(){let t=this.querySelector('[slot="customer-info"]');if(t){let r=this.createCustomerInfoSection();r.slot="customer-info",t.replaceWith(r)}}updateCustomStyles(){let t=this.querySelector(`#${this.containerId}-custom-styles`);if(t){let r=`
905
905
  /* PAYUNi iframe \u5BB9\u5668\u9AD8\u5EA6\uFF08\u4F7F\u7528\u5BE6\u969B\u7684 container ID\uFF09 */
906
906
  #${this.containerId}-card-no,
907
907
  #${this.containerId}-card-exp,
@@ -1225,10 +1225,10 @@
1225
1225
  >
1226
1226
  <span id="${this.containerId}-submit-text">\u78BA\u8A8D\u4ED8\u6B3E</span>
1227
1227
  </button>
1228
- `,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`),a=document.getElementById(`${this.containerId}-card-cvc`);if(!i||!s||!a)return console.log("[PaymentForm] DOM elements not found, likely disconnected"),this._isInitializing=!1,null;if(this._initializationAborted)return console.log("[PaymentForm] Initialization aborted before createSession"),this._isInitializing=!1,null;let o=window.UniPayment.createSession(t,{env:r==="SANDBOX"?"S":"P",elements:{CardNo:`${this.containerId}-card-no`,CardExp:`${this.containerId}-card-exp`,CardCvc:`${this.containerId}-card-cvc`}});if(this._initializationAborted)return console.log("[PaymentForm] Initialization aborted before paymentSession.start()"),this._isInitializing=!1,null;try{await o.start()}catch(c){if((c?.message?.includes("1008")||c?.message?.includes("timeout")||c?.code===1008)&&this._initializationAborted)return console.log("[PaymentForm] Caught 1008 timeout error after component disconnect - ignoring"),this._isInitializing=!1,null;throw c}return this._initializationAborted?(console.log("[PaymentForm] Initialization aborted after paymentSession.start()"),this._isInitializing=!1,null):(this.hideCardSkeletons(),o.onUpdate?.(c=>{if(this._initializationAborted){console.log("[PaymentForm] Ignoring onUpdate event - component disconnected");return}console.log("[PaymentForm] PAYUNi update:",c);let p=c.status&&c.status.CardNo===!0&&c.status.CardExp===!0&&c.status.CardCvc===!0,v=document.getElementById(`${this.containerId}-submit-btn`);v&&(v.disabled=!p,console.log("[PaymentForm] Submit button disabled:",!p))}),this._initializationAborted?(console.log("[PaymentForm] Initialization aborted before saving session"),this._isInitializing=!1,null):(this._paymentSession=o,this.setupFormSubmission(),this._isInitializing=!1,o))}catch(i){throw this._isInitializing=!1,console.error("[PaymentForm] Failed to initialize payment:",i),i}}setupFormSubmission(){let t=document.getElementById(`${this.containerId}-submit-btn`);t&&t.addEventListener("click",async r=>{if(r.preventDefault(),t.classList.contains("loading"))return;let i,s,a,o=document.getElementById(`${this.containerId}-email`),c=document.getElementById(`${this.containerId}-name`),p=document.getElementById(`${this.containerId}-phone`);if(o&&c){if(i=o.value,s=c.value,a=p?.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:a,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=`
1228
+ `,t}async initializePayment(t,r){if(this._initializationAborted)return console.log("[PaymentForm] Initialization aborted before start"),null;if(this._isInitializing=!0,await this.updateComplete(),this._initializationAborted)return console.log("[PaymentForm] Initialization aborted after updateComplete"),this._isInitializing=!1,null;if(!window.UniPayment)throw this._isInitializing=!1,new Error("PAYUNi SDK not loaded");try{let i=document.getElementById(`${this.containerId}-card-no`),s=document.getElementById(`${this.containerId}-card-exp`),n=document.getElementById(`${this.containerId}-card-cvc`);if(!i||!s||!n)return console.log("[PaymentForm] DOM elements not found, likely disconnected"),this._isInitializing=!1,null;if(this._initializationAborted)return console.log("[PaymentForm] Initialization aborted before createSession"),this._isInitializing=!1,null;let o=window.UniPayment.createSession(t,{env:r==="SANDBOX"?"S":"P",elements:{CardNo:`${this.containerId}-card-no`,CardExp:`${this.containerId}-card-exp`,CardCvc:`${this.containerId}-card-cvc`}});if(this._initializationAborted)return console.log("[PaymentForm] Initialization aborted before paymentSession.start()"),this._isInitializing=!1,null;try{await o.start()}catch(l){if((l?.message?.includes("1008")||l?.message?.includes("timeout")||l?.code===1008)&&this._initializationAborted)return console.log("[PaymentForm] Caught 1008 timeout error after component disconnect - ignoring"),this._isInitializing=!1,null;throw l}return this._initializationAborted?(console.log("[PaymentForm] Initialization aborted after paymentSession.start()"),this._isInitializing=!1,null):(this.hideCardSkeletons(),o.onUpdate?.(l=>{if(this._initializationAborted){console.log("[PaymentForm] Ignoring onUpdate event - component disconnected");return}console.log("[PaymentForm] PAYUNi update:",l);let d=l.status&&l.status.CardNo===!0&&l.status.CardExp===!0&&l.status.CardCvc===!0,y=document.getElementById(`${this.containerId}-submit-btn`);y&&(y.disabled=!d,console.log("[PaymentForm] Submit button disabled:",!d))}),this._initializationAborted?(console.log("[PaymentForm] Initialization aborted before saving session"),this._isInitializing=!1,null):(this._paymentSession=o,this.setupFormSubmission(),this._isInitializing=!1,o))}catch(i){throw this._isInitializing=!1,console.error("[PaymentForm] Failed to initialize payment:",i),i}}setupFormSubmission(){let t=document.getElementById(`${this.containerId}-submit-btn`);t&&t.addEventListener("click",async r=>{if(r.preventDefault(),t.classList.contains("loading"))return;let i,s,n,o=document.getElementById(`${this.containerId}-email`),l=document.getElementById(`${this.containerId}-name`),d=document.getElementById(`${this.containerId}-phone`);if(o&&l){if(i=o.value,s=l.value,n=d?.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:n,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=`
1229
1229
  <span class="recur-loading-spinner"></span>
1230
1230
  <span>\u8655\u7406\u4E2D...</span>
1231
- `):(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 Y={};d(Y,{RecurCheckoutButton:()=>S});var S,V=m(()=>{"use strict";S=class extends HTMLElement{constructor(){super();n(this,"_isLoading",!1);n(this,"handleClick",async t=>{if(t.preventDefault(),this._isLoading||this.hasAttribute("disabled"))return;let r=this.getAttribute("publishable-key"),i=this.getAttribute("product-id"),s=this.getAttribute("success-url"),a=this.getAttribute("cancel-url");if(!r){this.dispatchError("Missing required attribute: publishable-key");return}if(!i){this.dispatchError("Missing required attribute: product-id");return}if(!s){this.dispatchError("Missing required attribute: success-url");return}if(!a){this.dispatchError("Missing required attribute: cancel-url");return}this._isLoading=!0,this.render(),this.setupEventListeners();try{let o=await this.createCheckoutSession({publishableKey:r,productId:i,successUrl:this.resolveUrl(s),cancelUrl:this.resolveUrl(a),customerEmail:this.getAttribute("customer-email")||void 0,mode:this.getAttribute("mode")});this.dispatchEvent(new CustomEvent("checkout-started",{detail:{sessionId:o.id,url:o.url},bubbles:!0,composed:!0})),window.location.href=o.url}catch(o){this._isLoading=!1,this.render(),this.setupEventListeners(),this.dispatchError(o.message||"Failed to create checkout session")}});this.attachShadow({mode:"open"})}static get observedAttributes(){return["publishable-key","product-id","success-url","cancel-url","customer-email","mode","button-text","button-style","disabled"]}connectedCallback(){this.render(),this.setupEventListeners()}disconnectedCallback(){let t=this.shadowRoot?.querySelector("button");t&&t.removeEventListener("click",this.handleClick)}attributeChangedCallback(t,r,i){r!==i&&this.render()}render(){let t=this.getAttribute("button-text")||this.textContent?.trim()||"\u8A02\u95B1",r=this.getAttribute("button-style")||"primary",i=this.hasAttribute("disabled")||this._isLoading;this.shadowRoot.innerHTML=`
1231
+ `):(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",L)});var Q={};h(Q,{RecurCheckoutButton:()=>U});var U,ee=g(()=>{"use strict";U=class extends HTMLElement{constructor(){super();a(this,"_isLoading",!1);a(this,"handleClick",async t=>{if(t.preventDefault(),this._isLoading||this.hasAttribute("disabled"))return;let r=this.getAttribute("publishable-key"),i=this.getAttribute("product-id"),s=this.getAttribute("success-url"),n=this.getAttribute("cancel-url");if(!r){this.dispatchError("Missing required attribute: publishable-key");return}if(!i){this.dispatchError("Missing required attribute: product-id");return}if(!s){this.dispatchError("Missing required attribute: success-url");return}if(!n){this.dispatchError("Missing required attribute: cancel-url");return}this._isLoading=!0,this.render(),this.setupEventListeners();try{let o=await this.createCheckoutSession({publishableKey:r,productId:i,successUrl:this.resolveUrl(s),cancelUrl:this.resolveUrl(n),customerEmail:this.getAttribute("customer-email")||void 0,mode:this.getAttribute("mode")});this.dispatchEvent(new CustomEvent("checkout-started",{detail:{sessionId:o.id,url:o.url},bubbles:!0,composed:!0})),window.location.href=o.url}catch(o){this._isLoading=!1,this.render(),this.setupEventListeners(),this.dispatchError(o.message||"Failed to create checkout session")}});this.attachShadow({mode:"open"})}static get observedAttributes(){return["publishable-key","product-id","success-url","cancel-url","customer-email","mode","button-text","button-style","disabled"]}connectedCallback(){this.render(),this.setupEventListeners()}disconnectedCallback(){let t=this.shadowRoot?.querySelector("button");t&&t.removeEventListener("click",this.handleClick)}attributeChangedCallback(t,r,i){r!==i&&this.render()}render(){let t=this.getAttribute("button-text")||this.textContent?.trim()||"\u8A02\u95B1",r=this.getAttribute("button-style")||"primary",i=this.hasAttribute("disabled")||this._isLoading;this.shadowRoot.innerHTML=`
1232
1232
  <style>
1233
1233
  :host {
1234
1234
  display: inline-block;
@@ -1327,57 +1327,7 @@
1327
1327
  ${this._isLoading?'<span class="spinner"></span>':""}
1328
1328
  <span>${this._isLoading?"\u8655\u7406\u4E2D...":t}</span>
1329
1329
  </button>
1330
- `}setupEventListeners(){let t=this.shadowRoot?.querySelector("button");t&&t.addEventListener("click",this.handleClick)}async createCheckoutSession(t){let r=this.getApiBaseUrl(),i={productId:t.productId,successUrl:t.successUrl,cancelUrl:t.cancelUrl};t.mode&&(i.mode=t.mode),t.customerEmail&&(i.customerEmail=t.customerEmail);let s=await fetch(`${r}/v1/checkout/sessions`,{method:"POST",headers:{"Content-Type":"application/json","X-Recur-Publishable-Key":t.publishableKey},body:JSON.stringify(i)});if(!s.ok){let a=await s.json().catch(()=>({}));throw new Error(a.error?.message||`HTTP ${s.status}: Failed to create checkout session`)}return s.json()}getApiBaseUrl(){let t=this.getAttribute("api-base-url");if(t)return t;if(typeof window<"u"){let r=window.location.hostname;if(r==="localhost"||r.includes(".test")||r.includes(".local")||r==="127.0.0.1")return`${window.location.protocol}//${window.location.host}/api`}return"https://api.recur.tw"}resolveUrl(t){return t.startsWith("/")?`${window.location.origin}${t}`:t}dispatchError(t){console.error("[recur-checkout]",t),this.dispatchEvent(new CustomEvent("checkout-error",{detail:{message:t},bubbles:!0,composed:!0}))}};typeof window<"u"&&!customElements.get("recur-checkout")&&customElements.define("recur-checkout",S)});var ie={};d(ie,{RecurCheckout:()=>y,RecurElements:()=>h,createElements:()=>_,default:()=>re,init:()=>X});async function te(){if(typeof window>"u"){console.warn("[Recur SDK] Cannot register Web Components: window is not defined");return}await Promise.all([Promise.resolve().then(()=>(H(),U)),Promise.resolve().then(()=>(A(),$)),Promise.resolve().then(()=>(N(),B)),Promise.resolve().then(()=>(O(),D)),Promise.resolve().then(()=>(j(),F)),Promise.resolve().then(()=>(g(),b)),Promise.resolve().then(()=>(q(),K)),Promise.resolve().then(()=>(V(),Y))]);let e=["recur-loading-spinner","recur-success-message","recur-error-display","recur-skeleton-loader","recur-payment-form-skeleton","recur-toast","recur-toast-container","recur-payment-form","recur-checkout"].filter(t=>!customElements.get(t));e.length>0&&console.warn("[Recur SDK] The following components are not registered:",e)}typeof window<"u"&&te();var L=class{constructor(e){n(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 a=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(!a.ok){let o=await a.json().catch(()=>({}));throw{code:o.error||"CHECKOUT_FAILED",message:o.message||"Failed to initiate checkout",details:o}}return await a.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 R=class{constructor(e){n(this,"overlay",null);n(this,"modal",null);n(this,"iframe",null);n(this,"onClose");n(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=`
1331
- position: fixed;
1332
- top: 0;
1333
- left: 0;
1334
- right: 0;
1335
- bottom: 0;
1336
- background: rgba(0, 0, 0, 0.5);
1337
- z-index: 9998;
1338
- display: flex;
1339
- align-items: center;
1340
- justify-content: center;
1341
- padding: 20px;
1342
- `,this.modal=document.createElement("div"),this.modal.style.cssText=`
1343
- position: relative;
1344
- background: white;
1345
- border-radius: 8px;
1346
- width: 100%;
1347
- max-width: 600px;
1348
- height: 90vh;
1349
- max-height: 700px;
1350
- box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
1351
- display: flex;
1352
- flex-direction: column;
1353
- overflow: hidden;
1354
- z-index: 9999;
1355
- `;let t=document.createElement("button");t.innerHTML="\u2715",t.style.cssText=`
1356
- position: absolute;
1357
- top: 12px;
1358
- right: 12px;
1359
- width: 32px;
1360
- height: 32px;
1361
- border: none;
1362
- background: rgba(0, 0, 0, 0.1);
1363
- border-radius: 50%;
1364
- cursor: pointer;
1365
- font-size: 18px;
1366
- line-height: 1;
1367
- z-index: 10000;
1368
- transition: background 0.2s;
1369
- color: #333;
1370
- `,t.onmouseover=()=>{t.style.background="rgba(0, 0, 0, 0.2)"},t.onmouseout=()=>{t.style.background="rgba(0, 0, 0, 0.1)"},t.onclick=()=>this.close(),this.iframe=document.createElement("iframe"),this.iframe.src=e,this.iframe.style.cssText=`
1371
- width: 100%;
1372
- height: 100%;
1373
- border: none;
1374
- border-radius: 8px;
1375
- `,this.iframe.setAttribute("allow","payment"),this.iframe.setAttribute("sandbox","allow-same-origin allow-scripts allow-forms allow-popups allow-top-navigation"),this.modal.appendChild(t),this.modal.appendChild(this.iframe),this.overlay.appendChild(this.modal),document.body.appendChild(this.overlay),document.body.style.overflow="hidden",this.overlay.onclick=r=>{r.target===this.overlay&&this.close()},window.addEventListener("message",this.handleMessage)}close(){this.overlay&&this.overlay.parentNode&&this.overlay.parentNode.removeChild(this.overlay),document.body.style.overflow="",this.overlay=null,this.modal=null,this.iframe=null,window.removeEventListener("message",this.handleMessage),this.onClose&&this.onClose()}};var P=class{constructor(e,t){n(this,"container");n(this,"iframe",null);n(this,"onClose");n(this,"handleMessage",e=>{e.origin===window.location.origin&&(e.data?.type==="RECUR_PAYMENT_COMPLETE"?this.remove():e.data?.type==="RECUR_PAYMENT_CANCEL"&&this.remove())});if(typeof e=="string"){let r=document.querySelector(e);if(!r)throw new Error(`Container element not found: ${e}`);this.container=r}else this.container=e;this.onClose=t}embed(e){this.container.innerHTML="",this.iframe=document.createElement("iframe"),this.iframe.src=e,this.iframe.style.cssText=`
1376
- width: 100%;
1377
- height: 600px;
1378
- border: 1px solid #e5e7eb;
1379
- border-radius: 8px;
1380
- `,this.iframe.setAttribute("allow","payment"),this.iframe.setAttribute("sandbox","allow-same-origin allow-scripts allow-forms allow-popups allow-top-navigation"),this.container.appendChild(this.iframe),window.addEventListener("message",this.handleMessage)}remove(){this.iframe&&this.iframe.parentNode&&this.iframe.parentNode.removeChild(this.iframe),this.iframe=null,window.removeEventListener("message",this.handleMessage),this.onClose&&this.onClose()}};var z=class{constructor(e,t){n(this,"config");n(this,"options");n(this,"container");n(this,"checkoutId",null);n(this,"sdkToken",null);n(this,"sdkEnv","S");n(this,"payuniSDK",null);n(this,"isFormValid",!1);if(this.config=e,this.options=t,typeof t.container=="string"){let r=document.querySelector(t.container);if(!r)throw new Error(`Container not found: ${t.container}`);this.container=r}else this.container=t.container}async render(){try{await this.initCheckout(),this.renderHTML(),await this.loadPayUniSDK(),await this.initPayUniSDK(),this.setupFormSubmit()}catch(e){this.handleError(e)}}getBaseUrl(){if(this.config.baseUrl)return this.config.baseUrl;if(typeof window<"u"){let e=window.location.hostname;if(e==="localhost"||e.includes(".test")||e.includes(".local")||e==="127.0.0.1")return`${window.location.protocol}//${window.location.host}/api`}return"https://api.recur.tw"}async initCheckout(){let e=this.getBaseUrl(),t=await fetch(`${e}/v1/checkouts`,{method:"POST",headers:{"Content-Type":"application/json","X-Recur-Publishable-Key":this.config.publishableKey},body:JSON.stringify({productId:this.options.planId,customerEmail:this.options.customerEmail,customerName:this.options.customerName,customerPhone:this.options.customerPhone})});if(!t.ok){let i=await t.json().catch(()=>({}));throw new Error(i.error||"Failed to initialize checkout")}let r=await t.json();this.checkoutId=r.checkout.id,this.sdkToken=r.sdkToken,this.sdkEnv=this.config.publishableKey.startsWith("pk_test_")?"S":"P"}renderHTML(){this.container.innerHTML=`
1330
+ `}setupEventListeners(){let t=this.shadowRoot?.querySelector("button");t&&t.addEventListener("click",this.handleClick)}async createCheckoutSession(t){let r=this.getApiBaseUrl(),i={productId:t.productId,successUrl:t.successUrl,cancelUrl:t.cancelUrl};t.mode&&(i.mode=t.mode),t.customerEmail&&(i.customerEmail=t.customerEmail);let s=await fetch(`${r}/v1/checkout/sessions`,{method:"POST",headers:{"Content-Type":"application/json","X-Recur-Publishable-Key":t.publishableKey},body:JSON.stringify(i)});if(!s.ok){let n=await s.json().catch(()=>({}));throw new Error(n.error?.message||`HTTP ${s.status}: Failed to create checkout session`)}return s.json()}getApiBaseUrl(){let t=this.getAttribute("api-base-url");if(t)return t;if(typeof window<"u"){let r=window.location.hostname;if(r==="localhost"||r.includes(".test")||r.includes(".local")||r==="127.0.0.1")return`${window.location.protocol}//${window.location.host}/api`}return"https://api.recur.tw"}resolveUrl(t){return t.startsWith("/")?`${window.location.origin}${t}`:t}dispatchError(t){console.error("[recur-checkout]",t),this.dispatchEvent(new CustomEvent("checkout-error",{detail:{message:t},bubbles:!0,composed:!0}))}};typeof window<"u"&&!customElements.get("recur-checkout")&&customElements.define("recur-checkout",U)});var ye={};h(ye,{RecurCheckout:()=>w,RecurElements:()=>v,createElements:()=>$,default:()=>ge,init:()=>ie});async function he(){if(typeof window>"u"){console.warn("[Recur SDK] Cannot register Web Components: window is not defined");return}await Promise.all([Promise.resolve().then(()=>(N(),B)),Promise.resolve().then(()=>(O(),K)),Promise.resolve().then(()=>(j(),F)),Promise.resolve().then(()=>(q(),Y)),Promise.resolve().then(()=>(X(),V)),Promise.resolve().then(()=>(J(),W)),Promise.resolve().then(()=>(G(),Z)),Promise.resolve().then(()=>(ee(),Q))]);let e=["recur-loading-spinner","recur-success-message","recur-error-display","recur-skeleton-loader","recur-payment-form-skeleton","recur-toast","recur-toast-container","recur-payment-form","recur-checkout"].filter(t=>!customElements.get(t));e.length>0&&console.warn("[Recur SDK] The following components are not registered:",e)}typeof window<"u"&&he();var z=class{constructor(e){a(this,"config");if(!e.publishableKey)throw new Error("publishableKey is required");this.config={baseUrl:"https://api.recur.tw",...e}}async createSubscription(e){let{planId:t,customerName:r,customerEmail:i,customerPhone:s}=e;if(!t)throw new Error("planId is required");let n=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(!n.ok){let o=await n.json().catch(()=>({}));throw{code:o.error||"CHECKOUT_FAILED",message:o.message||"Failed to initiate checkout",details:o}}return await n.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 _=class{constructor(e,t){a(this,"config");a(this,"options");a(this,"container");a(this,"checkoutId",null);a(this,"sdkToken",null);a(this,"sdkEnv","S");a(this,"payuniSDK",null);a(this,"isFormValid",!1);if(this.config=e,this.options=t,typeof t.container=="string"){let r=document.querySelector(t.container);if(!r)throw new Error(`Container not found: ${t.container}`);this.container=r}else this.container=t.container}async render(){try{await this.initCheckout(),this.renderHTML(),await this.loadPayUniSDK(),await this.initPayUniSDK(),this.setupFormSubmit()}catch(e){this.handleError(e)}}getBaseUrl(){if(this.config.baseUrl)return this.config.baseUrl;if(typeof window<"u"){let e=window.location.hostname;if(e==="localhost"||e.includes(".test")||e.includes(".local")||e==="127.0.0.1")return`${window.location.protocol}//${window.location.host}/api`}return"https://api.recur.tw"}async initCheckout(){let e=this.getBaseUrl(),t=await fetch(`${e}/v1/checkouts`,{method:"POST",headers:{"Content-Type":"application/json","X-Recur-Publishable-Key":this.config.publishableKey},body:JSON.stringify({productId:this.options.planId,customerEmail:this.options.customerEmail,customerName:this.options.customerName,customerPhone:this.options.customerPhone})});if(!t.ok){let i=await t.json().catch(()=>({}));throw new Error(i.error||"Failed to initialize checkout")}let r=await t.json();this.checkoutId=r.checkout.id,this.sdkToken=r.sdkToken,this.sdkEnv=this.config.publishableKey.startsWith("pk_test_")?"S":"P"}renderHTML(){this.container.innerHTML=`
1381
1331
  <div class="recur-embedded-checkout" style="max-width: 400px; margin: 0 auto; padding: 20px; font-family: system-ui, -apple-system, sans-serif;">
1382
1332
  <div class="recur-checkout-header" style="margin-bottom: 24px;">
1383
1333
  <h2 style="margin: 0 0 8px 0; font-size: 20px; font-weight: 600; color: #111;">Subscribe</h2>
@@ -1459,227 +1409,60 @@
1459
1409
  </p>
1460
1410
  </form>
1461
1411
  </div>
1462
- `}async loadPayUniSDK(){return new Promise((e,t)=>{if(window.UniPayment){e();return}let r=document.createElement("script");r.src=this.sdkEnv==="P"?"https://vendor.payuni.com.tw/sdk/uni-payment.js":"https://sandbox-vendor.payuni.com.tw/sdk/uni-payment.js",r.async=!0,r.onload=()=>e(),r.onerror=()=>t(new Error("Failed to load PAYUNi SDK")),document.head.appendChild(r)})}async initPayUniSDK(){if(!this.sdkToken)throw new Error("SDK token not initialized");let e=window.UniPayment;if(!e)throw new Error("PAYUNi SDK not loaded");let t={env:this.sdkEnv,useInst:!1,elements:{CardNo:"recur-card-number",CardExp:"recur-card-exp",CardCvc:"recur-card-cvc"},style:{color:"#111827",errorColor:"#dc2626",fontSize:"14px",fontWeight:"400",lineHeight:"24px"}};this.payuniSDK=e.createSession(this.sdkToken,t),await this.payuniSDK.start(),this.payuniSDK.onUpdate(r=>{let i=r?.status;i&&(this.isFormValid=i.CardNo===!0&&i.CardExp===!0&&i.CardCvc===!0,this.updateSubmitButton())})}updateSubmitButton(){let e=document.getElementById("recur-submit-btn");e&&(e.disabled=!this.isFormValid,e.style.opacity=this.isFormValid?"1":"0.5",e.style.cursor=this.isFormValid?"pointer":"not-allowed")}setupFormSubmit(){let e=document.getElementById("recur-checkout-form");e&&e.addEventListener("submit",async t=>{t.preventDefault(),await this.handleSubmit()})}async handleSubmit(){try{this.showLoading(!0),this.hideError();let e=document.getElementById("recur-email").value,t=document.getElementById("recur-name").value,r=document.getElementById("recur-phone").value;if(!e||!t)throw new Error("Please fill in all required fields");if(!this.checkoutId)throw new Error("Checkout session not initialized");if(!this.payuniSDK)throw new Error("Payment system not initialized");let i=await this.payuniSDK.getTradeResult();if(i.Status!=="SUCCESS")throw new Error(i.Message||"Card validation failed");let s=i.EncryptInfo||i.creditToken;if(!s)throw console.error("[Recur SDK] Missing payment token. Available fields:",Object.keys(i)),new Error("Missing payment token");let a=this.getBaseUrl(),o=await fetch(`${a}/v1/checkouts/${this.checkoutId}/pay`,{method:"POST",headers:{"Content-Type":"application/json","X-Recur-Publishable-Key":this.config.publishableKey},body:JSON.stringify({creditToken:s,timestamp:i.HashTimestamp||i.timestamp})});if(!o.ok){let v=await o.json().catch(()=>({}));throw new Error(v.error||"Failed to process payment")}let c=await o.json(),p={subscription:{id:c.subscription?.id||c.charge?.id||"",status:c.success?"active":"failed",planId:this.options.planId,planName:"",amount:c.charge?.amount||0,billingPeriod:c.subscription?.billingPeriod||"MONTHLY",trialDays:null},subscriber:{id:"",email:document.getElementById("recur-email")?.value||"",name:document.getElementById("recur-name")?.value||""},nextSteps:{getSdkToken:"",completeSubscription:""}};this.options.onSuccess&&this.options.onSuccess(p),this.showSuccess()}catch(e){this.handleError(e)}finally{this.showLoading(!1)}}showLoading(e){let t=document.getElementById("recur-submit-btn");t&&(t.disabled=e,t.textContent=e?"Processing...":"Subscribe Now")}showError(e){let t=document.getElementById("recur-error");t&&(t.textContent=e,t.style.display="block")}hideError(){let e=document.getElementById("recur-error");e&&(e.style.display="none")}showSuccess(){this.container.innerHTML="";let e=document.createElement("recur-success-message");e.setAttribute("title","Subscription Complete!"),e.setAttribute("message","Thank you for subscribing. You will receive a confirmation email shortly."),this.container.appendChild(e)}handleError(e){let t=e?.message||"An error occurred";this.showError(t);let r={code:"CHECKOUT_ERROR",message:t};this.options.onError&&this.options.onError(r)}};var h=class{constructor(e){n(this,"publishableKey");n(this,"baseUrl");n(this,"embedUrl");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",new Map);typeof e=="string"?(this.publishableKey=e,this.baseUrl=this.getDefaultBaseUrl(),this.embedUrl=this.getDefaultEmbedUrl()):(this.publishableKey=e.publishableKey,this.baseUrl=e.baseUrl||this.getDefaultBaseUrl(),this.embedUrl=e.embedUrl||this.getDefaultEmbedUrl()),window.addEventListener("message",this.handleMessage.bind(this))}async mount(e){let t=typeof e=="string"?document.querySelector(e):e;if(!t)throw new Error(`Container not found: ${e}`);return this.container=t,this.iframe=document.createElement("iframe"),this.iframe.src=`${this.embedUrl}/elements?key=${encodeURIComponent(this.publishableKey)}`,this.iframe.style.cssText=`
1412
+ `}async loadPayUniSDK(){return new Promise((e,t)=>{if(window.UniPayment){e();return}let r=document.createElement("script");r.src=this.sdkEnv==="P"?"https://vendor.payuni.com.tw/sdk/uni-payment.js":"https://sandbox-vendor.payuni.com.tw/sdk/uni-payment.js",r.async=!0,r.onload=()=>e(),r.onerror=()=>t(new Error("Failed to load PAYUNi SDK")),document.head.appendChild(r)})}async initPayUniSDK(){if(!this.sdkToken)throw new Error("SDK token not initialized");let e=window.UniPayment;if(!e)throw new Error("PAYUNi SDK not loaded");let t={env:this.sdkEnv,useInst:!1,elements:{CardNo:"recur-card-number",CardExp:"recur-card-exp",CardCvc:"recur-card-cvc"},style:{color:"#111827",errorColor:"#dc2626",fontSize:"14px",fontWeight:"400",lineHeight:"24px"}};this.payuniSDK=e.createSession(this.sdkToken,t),await this.payuniSDK.start(),this.payuniSDK.onUpdate(r=>{let i=r?.status;i&&(this.isFormValid=i.CardNo===!0&&i.CardExp===!0&&i.CardCvc===!0,this.updateSubmitButton())})}updateSubmitButton(){let e=document.getElementById("recur-submit-btn");e&&(e.disabled=!this.isFormValid,e.style.opacity=this.isFormValid?"1":"0.5",e.style.cursor=this.isFormValid?"pointer":"not-allowed")}setupFormSubmit(){let e=document.getElementById("recur-checkout-form");e&&e.addEventListener("submit",async t=>{t.preventDefault(),await this.handleSubmit()})}async handleSubmit(){try{this.showLoading(!0),this.hideError();let e=document.getElementById("recur-email").value,t=document.getElementById("recur-name").value,r=document.getElementById("recur-phone").value;if(!e||!t)throw new Error("Please fill in all required fields");if(!this.checkoutId)throw new Error("Checkout session not initialized");if(!this.payuniSDK)throw new Error("Payment system not initialized");let i=await this.payuniSDK.getTradeResult();if(i.Status!=="SUCCESS")throw new Error(i.Message||"Card validation failed");let s=i.EncryptInfo||i.creditToken;if(!s)throw console.error("[Recur SDK] Missing payment token. Available fields:",Object.keys(i)),new Error("Missing payment token");let n=this.getBaseUrl(),o=await fetch(`${n}/v1/checkouts/${this.checkoutId}/pay`,{method:"POST",headers:{"Content-Type":"application/json","X-Recur-Publishable-Key":this.config.publishableKey},body:JSON.stringify({creditToken:s,timestamp:i.HashTimestamp||i.timestamp})});if(!o.ok){let y=await o.json().catch(()=>({}));throw new Error(y.error||"Failed to process payment")}let l=await o.json(),d={subscription:{id:l.subscription?.id||l.charge?.id||"",status:l.success?"active":"failed",planId:this.options.planId,planName:"",amount:l.charge?.amount||0,billingPeriod:l.subscription?.billingPeriod||"MONTHLY",trialDays:null},subscriber:{id:"",email:document.getElementById("recur-email")?.value||"",name:document.getElementById("recur-name")?.value||""},nextSteps:{getSdkToken:"",completeSubscription:""}};this.options.onSuccess&&this.options.onSuccess(d),this.showSuccess()}catch(e){this.handleError(e)}finally{this.showLoading(!1)}}showLoading(e){let t=document.getElementById("recur-submit-btn");t&&(t.disabled=e,t.textContent=e?"Processing...":"Subscribe Now")}showError(e){let t=document.getElementById("recur-error");t&&(t.textContent=e,t.style.display="block")}hideError(){let e=document.getElementById("recur-error");e&&(e.style.display="none")}showSuccess(){this.container.innerHTML="";let e=document.createElement("recur-success-message");e.setAttribute("title","Subscription Complete!"),e.setAttribute("message","Thank you for subscribing. You will receive a confirmation email shortly."),this.container.appendChild(e)}handleError(e){let t=e?.message||"An error occurred";this.showError(t);let r={code:"CHECKOUT_ERROR",message:t};this.options.onError&&this.options.onError(r)}};var v=class{constructor(e){a(this,"publishableKey");a(this,"baseUrl");a(this,"embedUrl");a(this,"iframe",null);a(this,"container",null);a(this,"sessionId",null);a(this,"timestamp",null);a(this,"creditToken",null);a(this,"cardToken",null);a(this,"cardTimestamp",null);a(this,"eventHandlers",new Map);typeof e=="string"?(this.publishableKey=e,this.baseUrl=this.getDefaultBaseUrl(),this.embedUrl=this.getDefaultEmbedUrl()):(this.publishableKey=e.publishableKey,this.baseUrl=e.baseUrl||this.getDefaultBaseUrl(),this.embedUrl=e.embedUrl||this.getDefaultEmbedUrl()),window.addEventListener("message",this.handleMessage.bind(this))}async mount(e){let t=typeof e=="string"?document.querySelector(e):e;if(!t)throw new Error(`Container not found: ${e}`);return this.container=t,this.iframe=document.createElement("iframe"),this.iframe.src=`${this.embedUrl}/elements?key=${encodeURIComponent(this.publishableKey)}`,this.iframe.style.cssText=`
1463
1413
  width: 100%;
1464
1414
  border: none;
1465
1415
  min-height: 200px;
1466
1416
  display: block;
1467
1417
  user-select: none;
1468
1418
  transition: height 0.35s ease, opacity 0.4s ease 0.1s;
1469
- `.replace(/\s+/g," ").trim(),this.iframe.allow="payment *",this.iframe.setAttribute("title","Secure payment input frame"),this.iframe.setAttribute("role","presentation"),this.iframe.setAttribute("scrolling","no"),this.iframe.setAttribute("frameBorder","0"),this.container.innerHTML="",this.container.appendChild(this.iframe),new Promise((r,i)=>{let s=setTimeout(()=>{i(new Error("Elements initialization timeout"))},3e4),a=()=>{clearTimeout(s),this.off("ready",a),r()},o=c=>{clearTimeout(s),this.off("error",o),i(new Error(c.message||"Elements initialization failed"))};this.on("ready",a),this.on("error",o)})}on(e,t){this.eventHandlers.has(e)||this.eventHandlers.set(e,new Set),this.eventHandlers.get(e).add(t)}off(e,t){let r=this.eventHandlers.get(e);r&&r.delete(t)}emit(e,t){let r=this.eventHandlers.get(e);r&&r.forEach(i=>i(t))}handleMessage(e){if(!this.isValidOrigin(e.origin))return;let{type:t,data:r}=e.data||{};switch(t){case"RECUR_ELEMENTS_READY":this.sessionId=r.sessionId,this.timestamp=r.timestamp,this.creditToken=r.creditToken,this.emit("ready",r);break;case"RECUR_CARD_VALID":this.emit("change",{valid:r.valid});break;case"RECUR_CARD_TOKENIZED":this.cardToken=r.cardToken,this.cardTimestamp=r.timestamp,this.emit("tokenized",r);break;case"RECUR_PAYMENT_ERROR":this.emit("error",r);break;case"RECUR_RESIZE":this.iframe&&r?.height&&(this.iframe.style.height=`${r.height}px`);break}}isValidOrigin(e){try{let t=new URL(this.embedUrl).origin;return e===t}catch{return!1}}async tokenize(){if(!this.iframe||!this.iframe.contentWindow)throw new Error("Elements not mounted");let e=new URL(this.embedUrl).origin;return this.iframe.contentWindow.postMessage({type:"RECUR_TOKENIZE"},e),new Promise((t,r)=>{let i=setTimeout(()=>{r(new Error("Tokenization timeout"))},6e4),s=o=>{clearTimeout(i),this.off("tokenized",s),t(o)},a=o=>{clearTimeout(i),this.off("error",a),r(new Error(o.message||"Tokenization failed"))};this.on("tokenized",s),this.on("error",a)})}async confirmPayment(e){if(!this.sessionId||!this.cardToken)throw new Error("Card not tokenized. Call tokenize() first.");let t=await fetch(`${this.baseUrl}/api/v1/elements/confirm`,{method:"POST",headers:{"Content-Type":"application/json","X-Recur-Publishable-Key":this.publishableKey},body:JSON.stringify({sessionId:this.sessionId,sdkToken:this.cardToken,timestamp:this.cardTimestamp,creditToken:this.creditToken,productId:e.productId,email:e.email,name:e.name,phone:e.phone,metadata:e.metadata})}),r=await t.json();return t.ok?r:{status:"failed",message:r.error||r.message||"Payment failed",canRetry:r.canRetry??!0}}getDefaultBaseUrl(){if(typeof window>"u")return"https://app.recur.tw";let e=window.location.hostname;return e==="localhost"||e.includes(".test")||e.includes(".local")||e==="127.0.0.1"?`${window.location.protocol}//${e}:3000`:"https://app.recur.tw"}getDefaultEmbedUrl(){if(typeof window>"u")return"https://embed.recur.tw";let e=window.location.hostname;return e==="localhost"||e.includes(".test")||e.includes(".local")||e==="127.0.0.1"?`${window.location.protocol}//${e}:3002`:"https://embed.recur.tw"}destroy(){this.iframe&&this.iframe.parentNode&&this.iframe.parentNode.removeChild(this.iframe),this.iframe=null,this.container=null,this.sessionId=null,this.timestamp=null,this.creditToken=null,this.cardToken=null,this.cardTimestamp=null,this.eventHandlers.clear(),window.removeEventListener("message",this.handleMessage.bind(this))}};function _(l){return new h(l)}var y=class{constructor(e){n(this,"core");n(this,"currentModal",null);n(this,"currentIframe",null);this.core=new L(e)}async fetchPlans(){return await this.core.fetchPlans()}async createEmbeddedCheckout(e){let t=this.core.getConfig();await new z(t,e).render()}async redirectToCheckout(e){let t=this.core.getConfig(),r=()=>{if(typeof window<"u"){let c=window.location.hostname;if(c==="localhost"||c.includes(".test")||c.includes(".local")||c==="127.0.0.1")return`${window.location.protocol}//${window.location.host}/api`}return"https://api.recur.tw"},i=t.baseUrl||r(),s={productId:e.productId,successUrl:e.successUrl,cancelUrl:e.cancelUrl};e.mode&&(s.mode=e.mode),e.customerEmail&&(s.customerEmail=e.customerEmail);let a=await fetch(`${i}/v1/checkout/sessions`,{method:"POST",headers:{"Content-Type":"application/json","X-Recur-Publishable-Key":t.publishableKey},body:JSON.stringify(s)});if(!a.ok){let c=await a.json().catch(()=>({}));throw new Error(c.error?.message||"Failed to create checkout session")}let o=await a.json();window.location.href=o.url}async createCheckoutSession(e){let t=this.core.getConfig(),r=()=>{if(typeof window<"u"){let o=window.location.hostname;if(o==="localhost"||o.includes(".test")||o.includes(".local")||o==="127.0.0.1")return`${window.location.protocol}//${window.location.host}/api`}return"https://api.recur.tw"},i=t.baseUrl||r(),s={productId:e.productId,successUrl:e.successUrl,cancelUrl:e.cancelUrl};e.mode&&(s.mode=e.mode),e.customerEmail&&(s.customerEmail=e.customerEmail);let a=await fetch(`${i}/v1/checkout/sessions`,{method:"POST",headers:{"Content-Type":"application/json","X-Recur-Publishable-Key":t.publishableKey},body:JSON.stringify(s)});if(!a.ok){let o=await a.json().catch(()=>({}));throw new Error(o.error?.message||"Failed to create checkout session")}return a.json()}async checkout(e){try{let t=await this.core.createSubscription(e);e.onSuccess&&e.onSuccess(t);let r=this.core.getConfig(),i=e.mode||"redirect";if(r.mockMode)console.log("[Recur SDK] \u{1F3AD} Mock mode: Showing mock payment UI"),await this.showMockPaymentUI(i,t,e.container,e.onClose);else{let a=`${r.baseUrl||window.location.origin}/checkout/${t.subscription.id}`;switch(i){case"modal":this.openModal(a,e.onClose);break;case"iframe":this.embedIframe(a,e.container,e.onClose);break;case"redirect":default:window.location.href=a;break}}}catch(t){if(e.onError)e.onError(t);else throw console.error("Recur checkout error:",t),t}}openModal(e,t){this.closeModal(),this.currentModal=new R(()=>{this.currentModal=null,t&&t()}),this.currentModal.open(e)}embedIframe(e,t,r){if(!t)throw new Error("Container is required for iframe mode");this.removeIframe(),this.currentIframe=new P(t,()=>{this.currentIframe=null,r&&r()}),this.currentIframe.embed(e)}closeModal(){this.currentModal&&(this.currentModal.close(),this.currentModal=null)}removeIframe(){this.currentIframe&&(this.currentIframe.remove(),this.currentIframe=null)}close(){this.closeModal(),this.removeIframe()}async showMockPaymentUI(e,t,r,i){let s=this.generateMockPaymentHTML(t);switch(e){case"modal":this.showMockModal(s,i);break;case"iframe":this.showMockIframe(s,r,i);break;case"redirect":default:let o=`${window.location.origin}/checkout/${t.subscription.id}`;console.log("[Recur SDK] \u{1F3AD} Mock mode: Would redirect to:",o);let{RecurToast:c}=await Promise.resolve().then(()=>(g(),b));c.show("\u{1F3AD} Mock \u6A21\u5F0F\uFF1A\u5728\u771F\u5BE6\u74B0\u5883\u4E2D\uFF0C\u7528\u6236\u6703\u88AB\u5C0E\u5411\u4ED8\u6B3E\u9801\u9762","info",8e3),console.log("Payment URL:",o);break}}generateMockPaymentHTML(e){return`
1470
- <!DOCTYPE html>
1471
- <html>
1472
- <head>
1473
- <meta charset="UTF-8">
1474
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
1475
- <style>
1476
- body {
1477
- margin: 0;
1478
- padding: 20px;
1479
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
1480
- background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
1481
- min-height: 100vh;
1482
- display: flex;
1483
- align-items: center;
1484
- justify-content: center;
1485
- }
1486
- .container {
1487
- background: white;
1488
- border-radius: 12px;
1489
- padding: 40px;
1490
- max-width: 500px;
1491
- width: 100%;
1492
- box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
1493
- }
1494
- .badge {
1495
- display: inline-block;
1496
- padding: 4px 12px;
1497
- background: #fed7d7;
1498
- color: #c53030;
1499
- border-radius: 12px;
1500
- font-size: 12px;
1501
- font-weight: 600;
1502
- margin-bottom: 20px;
1503
- border: 2px solid #fc8181;
1504
- }
1505
- h1 {
1506
- color: #1a202c;
1507
- margin-bottom: 24px;
1508
- font-size: 24px;
1509
- }
1510
- .info {
1511
- background: #f7fafc;
1512
- border-radius: 8px;
1513
- padding: 16px;
1514
- margin-bottom: 24px;
1515
- }
1516
- .info-row {
1517
- display: flex;
1518
- justify-content: space-between;
1519
- padding: 8px 0;
1520
- border-bottom: 1px solid #e2e8f0;
1521
- }
1522
- .info-row:last-child {
1523
- border-bottom: none;
1524
- }
1525
- .label {
1526
- color: #718096;
1527
- font-size: 14px;
1528
- }
1529
- .value {
1530
- color: #2d3748;
1531
- font-weight: 500;
1532
- font-size: 14px;
1533
- }
1534
- .card-form {
1535
- margin-bottom: 24px;
1536
- }
1537
- .form-group {
1538
- margin-bottom: 16px;
1539
- }
1540
- label {
1541
- display: block;
1542
- margin-bottom: 8px;
1543
- color: #2d3748;
1544
- font-weight: 500;
1545
- font-size: 14px;
1546
- }
1547
- .mock-input {
1548
- width: 100%;
1549
- padding: 12px 16px;
1550
- border: 2px solid #e2e8f0;
1551
- border-radius: 8px;
1552
- background: #f7fafc;
1553
- color: #6b7280;
1554
- font-size: 14px;
1555
- }
1556
- button {
1557
- width: 100%;
1558
- padding: 14px;
1559
- background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
1560
- color: white;
1561
- border: none;
1562
- border-radius: 8px;
1563
- font-size: 16px;
1564
- font-weight: 600;
1565
- cursor: pointer;
1566
- }
1567
- button:hover {
1568
- opacity: 0.9;
1569
- }
1570
- .note {
1571
- margin-top: 16px;
1572
- padding: 12px;
1573
- background: #fef5e7;
1574
- border-left: 4px solid #f59e0b;
1575
- border-radius: 4px;
1576
- font-size: 13px;
1577
- color: #78350f;
1578
- }
1579
- </style>
1580
- </head>
1581
- <body>
1582
- <div class="container">
1583
- <span class="badge">\u{1F3AD} MOCK MODE</span>
1584
- <h1>\u8A02\u95B1\u4ED8\u6B3E\uFF08\u6A21\u64EC\uFF09</h1>
1585
-
1586
- <div class="info">
1587
- <div class="info-row">
1588
- <span class="label">\u65B9\u6848</span>
1589
- <span class="value">${e.subscription.planName}</span>
1590
- </div>
1591
- <div class="info-row">
1592
- <span class="label">\u91D1\u984D</span>
1593
- <span class="value">NT$ ${e.subscription.amount}</span>
1594
- </div>
1595
- <div class="info-row">
1596
- <span class="label">\u9031\u671F</span>
1597
- <span class="value">${e.subscription.billingPeriod}</span>
1598
- </div>
1599
- <div class="info-row">
1600
- <span class="label">\u8A66\u7528\u671F</span>
1601
- <span class="value">${e.subscription.trialDays} \u5929</span>
1602
- </div>
1603
- </div>
1604
-
1605
- <div class="card-form">
1606
- <div class="form-group">
1607
- <label>\u4FE1\u7528\u5361\u865F</label>
1608
- <input type="text" class="mock-input" value="4242 4242 4242 4242 (Mock)" readonly>
1609
- </div>
1610
- <div class="form-group">
1611
- <label>\u6709\u6548\u671F\u9650</label>
1612
- <input type="text" class="mock-input" value="12 / 25 (Mock)" readonly>
1613
- </div>
1614
- <div class="form-group">
1615
- <label>\u5B89\u5168\u78BC</label>
1616
- <input type="text" class="mock-input" value="123 (Mock)" readonly>
1617
- </div>
1618
- </div>
1619
-
1620
- <button onclick="handleMockPayment()">\u78BA\u8A8D\u4ED8\u6B3E\uFF08\u6A21\u64EC\uFF09</button>
1621
-
1622
- <div class="note">
1623
- <strong>\u{1F3AD} Mock \u6A21\u5F0F\u8AAA\u660E</strong><br>
1624
- \u9019\u662F\u6A21\u64EC\u7684\u4ED8\u6B3E\u8868\u55AE\u3002\u9EDE\u64CA\u300C\u78BA\u8A8D\u4ED8\u6B3E\u300D\u6703\u6A21\u64EC\u6210\u529F\u7684\u652F\u4ED8\u6D41\u7A0B\u3002
1625
- </div>
1626
- </div>
1627
-
1628
- <script>
1629
- function handleMockPayment() {
1630
- console.log('\u{1F3AD} Mock \u6A21\u5F0F\uFF1A\u4ED8\u6B3E\u6210\u529F\uFF01');
1631
-
1632
- // Notify parent to show toast
1633
- if (window.parent !== window) {
1634
- window.parent.postMessage({
1635
- type: 'recur-mock-payment-success',
1636
- showToast: {
1637
- message: '\u{1F3AD} Mock \u6A21\u5F0F\uFF1A\u4ED8\u6B3E\u6210\u529F\uFF01\u5728\u771F\u5BE6\u74B0\u5883\u4E2D\u6703\u89F8\u767C\u5BE6\u969B\u7684\u652F\u4ED8\u8655\u7406',
1638
- type: 'success'
1639
- }
1640
- }, '*');
1641
- } else {
1642
- // If not in iframe, show toast directly
1643
- console.log('Mock payment successful');
1644
- }
1645
- }
1646
- </script>
1647
- </body>
1648
- </html>
1649
- `}showMockModal(e,t){this.closeModal();let r=document.createElement("div");r.id="recur-mock-modal",r.style.cssText=`
1419
+ `.replace(/\s+/g," ").trim(),this.iframe.allow="payment *",this.iframe.setAttribute("title","Secure payment input frame"),this.iframe.setAttribute("role","presentation"),this.iframe.setAttribute("scrolling","no"),this.iframe.setAttribute("frameBorder","0"),this.container.innerHTML="",this.container.appendChild(this.iframe),new Promise((r,i)=>{let s=setTimeout(()=>{i(new Error("Elements initialization timeout"))},3e4),n=()=>{clearTimeout(s),this.off("ready",n),r()},o=l=>{clearTimeout(s),this.off("error",o),i(new Error(l.message||"Elements initialization failed"))};this.on("ready",n),this.on("error",o)})}on(e,t){this.eventHandlers.has(e)||this.eventHandlers.set(e,new Set),this.eventHandlers.get(e).add(t)}off(e,t){let r=this.eventHandlers.get(e);r&&r.delete(t)}emit(e,t){let r=this.eventHandlers.get(e);r&&r.forEach(i=>i(t))}handleMessage(e){if(!this.isValidOrigin(e.origin))return;let{type:t,data:r}=e.data||{};switch(t){case"RECUR_ELEMENTS_READY":this.sessionId=r.sessionId,this.timestamp=r.timestamp,this.creditToken=r.creditToken,this.emit("ready",r);break;case"RECUR_CARD_VALID":this.emit("change",{valid:r.valid});break;case"RECUR_CARD_TOKENIZED":this.cardToken=r.cardToken,this.cardTimestamp=r.timestamp,this.emit("tokenized",r);break;case"RECUR_PAYMENT_ERROR":this.emit("error",r);break;case"RECUR_RESIZE":this.iframe&&r?.height&&(this.iframe.style.height=`${r.height}px`);break}}isValidOrigin(e){try{let t=new URL(this.embedUrl).origin;return e===t}catch{return!1}}async tokenize(){if(!this.iframe||!this.iframe.contentWindow)throw new Error("Elements not mounted");let e=new URL(this.embedUrl).origin;return this.iframe.contentWindow.postMessage({type:"RECUR_TOKENIZE"},e),new Promise((t,r)=>{let i=setTimeout(()=>{r(new Error("Tokenization timeout"))},6e4),s=o=>{clearTimeout(i),this.off("tokenized",s),t(o)},n=o=>{clearTimeout(i),this.off("error",n),r(new Error(o.message||"Tokenization failed"))};this.on("tokenized",s),this.on("error",n)})}async confirmPayment(e){if(!this.sessionId||!this.cardToken)throw new Error("Card not tokenized. Call tokenize() first.");let t=await fetch(`${this.baseUrl}/api/v1/elements/confirm`,{method:"POST",headers:{"Content-Type":"application/json","X-Recur-Publishable-Key":this.publishableKey},body:JSON.stringify({sessionId:this.sessionId,sdkToken:this.cardToken,timestamp:this.cardTimestamp,creditToken:this.creditToken,productId:e.productId,email:e.email,name:e.name,phone:e.phone,metadata:e.metadata})}),r=await t.json();return t.ok?r:{status:"failed",message:r.error||r.message||"Payment failed",canRetry:r.canRetry??!0}}getDefaultBaseUrl(){if(typeof window>"u")return"https://app.recur.tw";let e=window.location.hostname;return e==="localhost"||e.includes(".test")||e.includes(".local")||e==="127.0.0.1"?`${window.location.protocol}//${e}:3000`:"https://app.recur.tw"}getDefaultEmbedUrl(){if(typeof window>"u")return"https://embed.recur.tw";let e=window.location.hostname;return e==="localhost"||e.includes(".test")||e.includes(".local")||e==="127.0.0.1"?`${window.location.protocol}//${e}:3002`:"https://embed.recur.tw"}destroy(){this.iframe&&this.iframe.parentNode&&this.iframe.parentNode.removeChild(this.iframe),this.iframe=null,this.container=null,this.sessionId=null,this.timestamp=null,this.creditToken=null,this.cardToken=null,this.cardTimestamp=null,this.eventHandlers.clear(),window.removeEventListener("message",this.handleMessage.bind(this))}};function $(c){return new v(c)}var fe="https://vendor.payuni.com.tw/sdk/uni-payment.js",be="https://sandbox-vendor.payuni.com.tw/sdk/uni-payment.js",te=!1,D=!1,x=null;async function re(c=!1){return te&&window.UniPayment?Promise.resolve():(D&&x||(D=!0,x=new Promise((e,t)=>{let r=document.createElement("script");r.src=c?be:fe,r.async=!0,r.onload=()=>{te=!0,D=!1,e()},r.onerror=()=>{D=!1,x=null,t(new Error("Failed to load PAYUNi SDK"))},document.head.appendChild(r)})),x)}var w=class{constructor(e){a(this,"core");a(this,"currentModal",null);a(this,"currentIframe",null);a(this,"currentModalOverlay",null);this.core=new z(e)}async fetchPlans(){return await this.core.fetchPlans()}async createEmbeddedCheckout(e){let t=this.core.getConfig();await new _(t,e).render()}async redirectToCheckout(e){let t=this.core.getConfig(),r=()=>{if(typeof window<"u"){let l=window.location.hostname;if(l==="localhost"||l.includes(".test")||l.includes(".local")||l==="127.0.0.1")return`${window.location.protocol}//${window.location.host}/api`}return"https://api.recur.tw"},i=t.baseUrl||r(),s={productId:e.productId,successUrl:e.successUrl,cancelUrl:e.cancelUrl};e.mode&&(s.mode=e.mode),e.customerEmail&&(s.customerEmail=e.customerEmail),e.externalCustomerId&&(s.externalCustomerId=e.externalCustomerId);let n=await fetch(`${i}/v1/checkout/sessions`,{method:"POST",headers:{"Content-Type":"application/json","X-Recur-Publishable-Key":t.publishableKey},body:JSON.stringify(s)});if(!n.ok){let l=await n.json().catch(()=>({}));throw new Error(l.error?.message||"Failed to create checkout session")}let o=await n.json();window.location.href=o.url}async createCheckoutSession(e){let t=this.core.getConfig(),r=()=>{if(typeof window<"u"){let o=window.location.hostname;if(o==="localhost"||o.includes(".test")||o.includes(".local")||o==="127.0.0.1")return`${window.location.protocol}//${window.location.host}/api`}return"https://api.recur.tw"},i=t.baseUrl||r(),s={productId:e.productId,successUrl:e.successUrl,cancelUrl:e.cancelUrl};e.mode&&(s.mode=e.mode),e.customerEmail&&(s.customerEmail=e.customerEmail),e.externalCustomerId&&(s.externalCustomerId=e.externalCustomerId);let n=await fetch(`${i}/v1/checkout/sessions`,{method:"POST",headers:{"Content-Type":"application/json","X-Recur-Publishable-Key":t.publishableKey},body:JSON.stringify(s)});if(!n.ok){let o=await n.json().catch(()=>({}));throw new Error(o.error?.message||"Failed to create checkout session")}return n.json()}async checkout(e){let t=this.core.getConfig(),r=null;try{if(console.log("[Recur SDK] Starting checkout flow...",{planId:e.planId,mode:e.mode}),!e.planId)throw new Error("planId is required");if(!t.publishableKey)throw new Error("publishableKey is required");if(!e.customerEmail||!e.customerName)throw new Error("customerEmail and customerName are required");let i=this.getBaseUrl();console.log("[Recur SDK] Base URL:",i);let s={"Content-Type":"application/json","X-Recur-Publishable-Key":t.publishableKey},n=e.mode||"modal",o=null;if(n==="modal"){let u=this.createModalWithSkeleton(e.onClose);r=u.overlay,o=u.container}else if(n==="iframe"){if(o=this.getEmbeddedContainer(e.container),!o)throw new Error("Container is required for iframe mode");o.innerHTML="";let u=document.createElement("recur-payment-form-skeleton");o.appendChild(u)}console.log("[Recur SDK] Step 1: Creating checkout session...");let l=await fetch(`${i}/v1/checkouts`,{method:"POST",headers:s,body:JSON.stringify({productId:e.planId,customerName:e.customerName,customerEmail:e.customerEmail,customerPhone:e.customerPhone,externalCustomerId:e.externalCustomerId})});if(!l.ok){let u=await l.json().catch(()=>({}));console.error("[Recur SDK] Failed to create checkout:",u);let A=u.details||u.error||"Failed to create checkout";throw new Error(A)}let d=await l.json();if(console.log("[Recur SDK] Checkout created successfully:",d),e.onSuccess?.(d),n==="redirect"){let u=`https://checkout.recur.tw/${d.checkout.id}`;console.log("[Recur SDK] Redirecting to hosted checkout:",u),window.location.href=u;return}if(console.log("[Recur SDK] Step 2: Extracting SDK token..."),!d.sdkToken)throw new Error("SDK token missing from checkout response");console.log("[Recur SDK] Step 3: Loading PAYUNi SDK...");let y=!0;if(await re(y),console.log("[Recur SDK] PAYUNi SDK loaded successfully"),!window.UniPayment)throw new Error("PAYUNi SDK failed to load");if(console.log("[Recur SDK] Step 4: Rendering payment form..."),!o)throw new Error("Payment container not available");o.innerHTML="";let m=document.createElement("recur-payment-form");if(m.setAttribute("container-id",o.id||"recur-payment-container"),e.customerName&&m.setAttribute("customer-name",e.customerName),e.customerEmail&&m.setAttribute("customer-email",e.customerEmail),d.plan?.name&&m.setAttribute("plan-name",d.plan.name),d.checkout?.amount&&m.setAttribute("amount",d.checkout.amount.toString()),d.plan?.billingPeriod&&m.setAttribute("billing-period",d.plan.billingPeriod),m.setAttribute("custom-styles",`
1420
+ .form-input-focus {
1421
+ border-color: var(--ring, hsl(215 16% 47%)) !important;
1422
+ outline: 0 !important;
1423
+ box-shadow: 0 0 0 3px color-mix(in oklch, var(--ring, hsl(215 16% 47%)) 50%, transparent) !important;
1424
+ transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out !important;
1425
+ }
1426
+ `),o.appendChild(m),await new Promise(u=>setTimeout(u,100)),console.log("[Recur SDK] Step 5: Initializing PAYUNi SDK..."),await m.initializePayment(d.sdkToken,y?"SANDBOX":"PRODUCTION")===null){console.log("[Recur SDK] Initialization aborted");return}console.log("[Recur SDK] PAYUNi SDK initialized successfully"),console.log("[Recur SDK] Step 6: Setting up submit handler..."),m.addEventListener("submit",(async u=>{console.log("[Recur SDK] Form submitted");let A=u,{paymentSession:se}=A.detail;try{let b=await se.getTradeResult();console.log("[Recur SDK] Trade result received"),console.log("[Recur SDK] Executing payment...");let E=b.EncryptInfo||b.creditToken,oe=b.HashTimestamp||b.timestamp;if(!E)throw new Error("Missing payment token from PAYUNi SDK");let ne=d.checkout.productType==="SUBSCRIPTION"?{creditToken:E,timestamp:oe}:{},H=await fetch(`${i}/v1/checkouts/${d.checkout.id}/pay`,{method:"POST",headers:s,body:JSON.stringify(ne)});if(!H.ok){let ae=await H.json().catch(()=>({}));throw new Error(ae.error||"Failed to execute payment")}let p=await H.json();if(console.log("[Recur SDK] Payment executed:",p),p.requires3D&&p.redirectUrl){console.log("[Recur SDK] 3D verification required"),window.location.href=p.redirectUrl;return}e.onPaymentComplete&&(p.subscription?e.onPaymentComplete({id:p.subscription.id,status:p.subscription.status,planId:d.checkout.productId,amount:d.checkout.amount,billingPeriod:p.subscription.billingPeriod,currentPeriodStart:p.subscription.currentPeriodStart,currentPeriodEnd:p.subscription.currentPeriodEnd}):e.onPaymentComplete({id:p.checkout.id,status:p.checkout.status,planId:d.checkout.productId,amount:d.checkout.amount,billingPeriod:d.checkout.productType})),console.log("[Recur SDK] Checkout flow completed successfully!"),m.resetButton?.(),r&&r.remove()}catch(b){console.error("[Recur SDK] Payment error:",b);let E={code:"PAYMENT_FAILED",message:b instanceof Error?b.message:"Payment failed"};e.onError?.(E),m.resetButton?.()}})),console.log("[Recur SDK] Checkout flow initialized, waiting for user input...")}catch(i){console.error("[Recur SDK] Checkout error:",i),r&&r.remove();let s={code:"CHECKOUT_ERROR",message:i instanceof Error?i.message:"An unknown error occurred"};throw e.onError?.(s),i}}getBaseUrl(){let e=this.core.getConfig();if(e.baseUrl)return e.baseUrl;if(typeof window<"u"){let t=window.location.hostname;if(t==="localhost"||t.includes(".test")||t.includes(".local")||t==="127.0.0.1")return`${window.location.protocol}//${window.location.host}/api`}return"https://api.recur.tw"}createModalWithSkeleton(e){this.closeModal();let t=document.createElement("div");t.id="recur-modal-overlay",t.style.cssText=`
1650
1427
  position: fixed;
1651
1428
  top: 0;
1652
1429
  left: 0;
1653
- right: 0;
1654
- bottom: 0;
1430
+ width: 100%;
1431
+ height: 100%;
1655
1432
  background: rgba(0, 0, 0, 0.5);
1656
1433
  display: flex;
1657
1434
  align-items: center;
1658
1435
  justify-content: center;
1659
- z-index: 999999;
1660
- `;let i=document.createElement("div");i.style.cssText=`
1436
+ z-index: 9999;
1437
+ `;let r=document.createElement("div");r.style.cssText=`
1438
+ max-width: 500px;
1661
1439
  width: 90%;
1662
- max-width: 600px;
1663
- height: 80%;
1664
- max-height: 700px;
1665
- background: white;
1666
- border-radius: 12px;
1667
- overflow: hidden;
1440
+ max-height: 90vh;
1441
+ overflow-y: auto;
1668
1442
  position: relative;
1669
- `;let s=document.createElement("button");s.textContent="\u2715",s.style.cssText=`
1443
+ `;let i=document.createElement("button");i.innerHTML="\u2715",i.style.cssText=`
1670
1444
  position: absolute;
1671
- top: 16px;
1672
- right: 16px;
1673
- background: white;
1445
+ top: 12px;
1446
+ right: 12px;
1447
+ background: rgba(0, 0, 0, 0.05);
1674
1448
  border: none;
1449
+ font-size: 18px;
1450
+ cursor: pointer;
1451
+ color: #666;
1452
+ padding: 0;
1675
1453
  width: 32px;
1676
1454
  height: 32px;
1455
+ display: flex;
1456
+ align-items: center;
1457
+ justify-content: center;
1677
1458
  border-radius: 50%;
1678
- cursor: pointer;
1679
- font-size: 18px;
1680
- z-index: 1;
1681
- box-shadow: 0 2px 8px rgba(0,0,0,0.15);
1682
- `,s.onclick=()=>{r.remove(),t&&t()};let a=document.createElement("iframe");a.style.cssText="width: 100%; height: 100%; border: none;",a.srcdoc=e,i.appendChild(s),i.appendChild(a),r.appendChild(i),document.body.appendChild(r),window.addEventListener("message",async o=>{if(o.data.type==="recur-mock-payment-success"){if(o.data.showToast){let{RecurToast:c}=await Promise.resolve().then(()=>(g(),b));c.show(o.data.showToast.message,o.data.showToast.type||"success",5e3)}r.remove(),t&&t()}})}showMockIframe(e,t,r){if(!t)throw new Error("Container is required for iframe mode");let i=typeof t=="string"?document.getElementById(t)||document.querySelector(t):t;if(!i)throw new Error("Container element not found");i.innerHTML="";let s=document.createElement("iframe");s.style.cssText="width: 100%; height: 100%; border: none; min-height: 600px;",s.srcdoc=e,i.appendChild(s),window.addEventListener("message",async a=>{if(a.data.type==="recur-mock-payment-success"){if(a.data.showToast){let{RecurToast:o}=await Promise.resolve().then(()=>(g(),b));o.show(a.data.showToast.message,a.data.showToast.type||"success",5e3)}r&&r()}})}};function X(l){return new y(l)}var re={init:X,RecurCheckout:y,RecurElements:h,createElements:_};return ee(ie);})();
1459
+ z-index: 10;
1460
+ transition: background 0.2s;
1461
+ `,i.onmouseover=()=>{i.style.background="rgba(0, 0, 0, 0.1)"},i.onmouseout=()=>{i.style.background="rgba(0, 0, 0, 0.05)"},i.onclick=()=>{t.remove(),e?.()};let s=document.createElement("div");s.id="recur-modal-payment-container",s.style.cssText=`
1462
+ background: white;
1463
+ border-radius: 12px;
1464
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
1465
+ `;let n=document.createElement("recur-payment-form-skeleton");return s.appendChild(n),r.appendChild(i),r.appendChild(s),t.appendChild(r),document.body.appendChild(t),this.currentModalOverlay=t,{overlay:t,container:s}}getEmbeddedContainer(e){return e?typeof e=="string"?document.getElementById(e)||document.querySelector(e):e:null}closeModal(){this.currentModal&&(this.currentModal.close(),this.currentModal=null),this.currentModalOverlay&&(this.currentModalOverlay.remove(),this.currentModalOverlay=null)}removeIframe(){this.currentIframe&&(this.currentIframe.remove(),this.currentIframe=null)}close(){this.closeModal(),this.removeIframe()}};function ie(c){return new w(c)}var ge={init:ie,RecurCheckout:w,RecurElements:v,createElements:$};return pe(ye);})();
1683
1466
  if (typeof window !== "undefined") {
1684
1467
  window.RecurCheckout = RecurCheckout.default;
1685
1468
  window.RecurElements = RecurCheckout.RecurElements;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "recur-tw",
3
- "version": "0.3.5",
3
+ "version": "0.4.0",
4
4
  "description": "React & Vanilla JS SDK for embedding subscription checkout flows (Taiwan / PAYUNi)",
5
5
  "type": "module",
6
6
  "private": false,