recur-tw 0.11.0 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/checkout.js CHANGED
@@ -1,4 +1,4 @@
1
- "use strict";var RecurCheckoutScript=(()=>{var m=Object.defineProperty;var v=Object.getOwnPropertyDescriptor;var E=Object.getOwnPropertyNames;var C=Object.prototype.hasOwnProperty;var R=(e,t)=>{for(var r in t)m(e,r,{get:t[r],enumerable:!0})},_=(e,t,r,o)=>{if(t&&typeof t=="object"||typeof t=="function")for(let n of E(t))!C.call(e,n)&&n!==r&&m(e,n,{get:()=>t[n],enumerable:!(o=v(t,n))||o.enumerable});return e};var U=e=>_(m({},"__esModule",{value:!0}),e);var T={};R(T,{default:()=>I});var c={embedUrl:"https://embed.recur.tw",apiUrl:"https://api.recur.tw"},l=null,g=null;function f(){if(document.getElementById("recur-checkout-styles"))return;let e=document.createElement("style");e.id="recur-checkout-styles",e.textContent=`
1
+ "use strict";var RecurCheckoutScript=(()=>{var f=Object.defineProperty;var v=Object.getOwnPropertyDescriptor;var E=Object.getOwnPropertyNames;var C=Object.prototype.hasOwnProperty;var R=(e,t)=>{for(var r in t)f(e,r,{get:t[r],enumerable:!0})},_=(e,t,r,o)=>{if(t&&typeof t=="object"||typeof t=="function")for(let n of E(t))!C.call(e,n)&&n!==r&&f(e,n,{get:()=>t[n],enumerable:!(o=v(t,n))||o.enumerable});return e};var U=e=>_(f({},"__esModule",{value:!0}),e);var T={};R(T,{default:()=>I});var a={embedUrl:"https://embed.recur.tw",apiUrl:"https://api.recur.tw"},h=null,p=null;function m(){if(document.getElementById("recur-checkout-styles"))return;let e=document.createElement("style");e.id="recur-checkout-styles",e.textContent=`
2
2
  .recur-checkout-dialog {
3
3
  border: none;
4
4
  border-radius: 12px;
@@ -122,5 +122,5 @@
122
122
  opacity: 0.7;
123
123
  pointer-events: none;
124
124
  }
125
- `,document.head.appendChild(e)}function S(e){try{let r=new URL(e,window.location.origin).pathname.split("/"),o=r.indexOf("buy");if(o===-1||o>=r.length-1)return null;let n=r[o+1];return n?n.startsWith("prod_")?{productId:n}:{productSlug:n}:null}catch{return null}}function P(e){let t=e.getAttribute("data-key");if(t)return t;try{let o=e.getAttribute("href");if(o){let i=new URL(o,window.location.origin).searchParams.get("key");if(i)return i}}catch{}let r=document.querySelector("script[data-recur-key]");return r?r.getAttribute("data-recur-key"):window.RecurConfig?.publishableKey?window.RecurConfig.publishableKey:null}async function b(e,t){let r={};e.productId&&(r.productId=e.productId),e.email&&(r.customerEmail=e.email),e.name&&(r.customerName=e.name);let o=await fetch(`${c.apiUrl}/v1/checkouts`,{method:"POST",headers:{"Content-Type":"application/json","X-Recur-Publishable-Key":t,"X-Recur-Embed-Domain":c.embedUrl,"X-Recur-SDK-Type":"checkout.js","X-Recur-Source":window.location.origin},body:JSON.stringify(r)});if(!o.ok){let i=await o.json().catch(()=>({}));throw new Error(i.error?.message||i.error||"Failed to create checkout")}let n=await o.json();return{id:n.checkout.id,publishableKey:t,clientSecret:n.checkout.client_secret}}function y(e,t){s(),f();let r=document.createElement("dialog");r.className="recur-checkout-dialog";let o=document.createElement("div");o.className="recur-checkout-dialog__header";let n=document.createElement("button");n.className="recur-checkout-dialog__close",n.innerHTML="×",n.type="button",n.setAttribute("aria-label","Close"),n.onclick=()=>{s(),t?.(),u("recur:close",{})},o.appendChild(n);let i=document.createElement("iframe");i.className="recur-checkout-dialog__iframe",i.src=e,i.setAttribute("allow","payment"),i.setAttribute("title","Recur Checkout"),r.appendChild(o),r.appendChild(i),document.body.appendChild(r),l=r,g=i,r.showModal(),document.body.style.overflow="hidden",r.addEventListener("cancel",p=>{p.preventDefault(),s(),t?.(),u("recur:close",{})}),window.addEventListener("message",k)}function s(){l&&(l.close(),l.remove(),l=null),g=null,document.body.style.overflow="",window.removeEventListener("message",k)}function k(e){if(!e.origin.includes("recur.tw")&&!e.origin.includes("localhost"))return;let{type:t,data:r}=e.data||{};switch(t){case"RECUR_PAYMENT_SUCCESS":s(),u("recur:success",r);break;case"RECUR_PAYMENT_ERROR":u("recur:error",r);break;case"RECUR_PAYMENT_CANCEL":s(),u("recur:close",{});break;case"RECUR_RESIZE":g&&r?.height&&(g.style.height=`${Math.min(r.height+56,window.innerHeight*.9)}px`);break}}function u(e,t){window.dispatchEvent(new CustomEvent(e,{detail:t}))}async function A(e,t){e.preventDefault();let r=t.getAttribute("href");if(!r){console.error("[Recur] Missing href on recur-button");return}let o=S(r);if(!o){console.error("[Recur] Invalid product URL:",r);return}let n=P(t);if(!n){console.error("[Recur] Missing publishable key. Add data-key attribute or set RecurConfig.publishableKey");return}let i={productId:o.productId||"",email:t.getAttribute("data-email")||void 0,name:t.getAttribute("data-name")||void 0,successUrl:t.getAttribute("data-success-url")||void 0,theme:t.getAttribute("data-theme")||void 0};t.setAttribute("data-loading","true");let p=t.textContent;t.textContent="...";try{let a=await b(i,n),d=new URL(`${c.embedUrl}/checkout`);d.searchParams.set("id",a.id),d.searchParams.set("key",a.publishableKey),d.searchParams.set("client_secret",a.clientSecret),i.theme&&d.searchParams.set("theme",i.theme),y(d.toString(),()=>{let x=i.successUrl})}catch(a){console.error("[Recur] Checkout error:",a),u("recur:error",{message:a.message})}finally{t.setAttribute("data-loading","false"),t.textContent=p}}function h(){document.querySelectorAll(".recur-button").forEach(t=>{t.hasAttribute("data-recur-initialized")||(t.setAttribute("data-recur-initialized","true"),t.addEventListener("click",r=>A(r,t)))})}function L(){let e=window.location.hostname;(e==="localhost"||e==="127.0.0.1"||e.includes(".local"))&&(c.embedUrl=`${window.location.protocol}//${e}:3002`,c.apiUrl=`${window.location.protocol}//${e}:3000/api`)}var w={config(e){Object.assign(c,e)},async popup(e){let t=await b(e,e.key),r=new URL(`${c.embedUrl}/checkout`);r.searchParams.set("id",t.id),r.searchParams.set("key",t.publishableKey),r.searchParams.set("client_secret",t.clientSecret),e.theme&&r.searchParams.set("theme",e.theme),y(r.toString())},close(){s()},init(){h()}};function M(){L(),f(),document.readyState==="loading"?document.addEventListener("DOMContentLoaded",h):h(),new MutationObserver(t=>{for(let r of t)r.addedNodes.length>0&&h()}).observe(document.body,{childList:!0,subtree:!0})}window.Recur=w;M();var I=w;return U(T);})();
125
+ `,document.head.appendChild(e)}function S(e){try{let r=new URL(e,window.location.origin).pathname.split("/"),o=r.indexOf("buy");if(o===-1||o>=r.length-1)return null;let n=r[o+1];return n?n.startsWith("prod_")?{productId:n}:{productSlug:n}:null}catch{return null}}function P(e){let t=e.getAttribute("data-key");if(t)return t;try{let n=e.getAttribute("href");if(n){let s=new URL(n,window.location.origin).searchParams.get("key");if(s)return s}}catch{}let r=document.querySelector("script[data-recur-key]");if(r)return r.getAttribute("data-recur-key");let o=window.RecurConfig;return o?.publishableKey?o.publishableKey:null}async function b(e,t){let r={};e.productId&&(r.productId=e.productId),e.email&&(r.customerEmail=e.email),e.name&&(r.customerName=e.name);let o=await fetch(`${a.apiUrl}/v1/checkouts`,{method:"POST",headers:{"Content-Type":"application/json","X-Recur-Publishable-Key":t,"X-Recur-Embed-Domain":a.embedUrl,"X-Recur-SDK-Type":"checkout.js","X-Recur-Source":window.location.origin},body:JSON.stringify(r)});if(!o.ok){let i=await o.json().catch(()=>({}));throw new Error(i.error?.message||i.error||"Failed to create checkout")}let n=await o.json();return{id:n.checkout.id,publishableKey:t,clientSecret:n.checkout.client_secret}}function y(e,t){u(),m();let r=document.createElement("dialog");r.className="recur-checkout-dialog";let o=document.createElement("div");o.className="recur-checkout-dialog__header";let n=document.createElement("button");n.className="recur-checkout-dialog__close",n.innerHTML="×",n.type="button",n.setAttribute("aria-label","Close"),n.onclick=()=>{u(),t?.(),d("recur:close",{})},o.appendChild(n);let i=document.createElement("iframe");i.className="recur-checkout-dialog__iframe",i.src=e,i.setAttribute("allow","payment"),i.setAttribute("title","Recur Checkout"),r.appendChild(o),r.appendChild(i),document.body.appendChild(r),h=r,p=i,r.showModal(),document.body.style.overflow="hidden",r.addEventListener("cancel",s=>{s.preventDefault(),u(),t?.(),d("recur:close",{})}),window.addEventListener("message",k)}function u(){h&&(h.close(),h.remove(),h=null),p=null,document.body.style.overflow="",window.removeEventListener("message",k)}function k(e){if(!e.origin.includes("recur.tw")&&!e.origin.includes("localhost"))return;let{type:t,data:r}=e.data||{};switch(t){case"RECUR_PAYMENT_SUCCESS":u(),d("recur:success",r);break;case"RECUR_PAYMENT_ERROR":d("recur:error",r);break;case"RECUR_PAYMENT_CANCEL":u(),d("recur:close",{});break;case"RECUR_RESIZE":p&&r?.height&&(p.style.height=`${Math.min(r.height+56,window.innerHeight*.9)}px`);break}}function d(e,t){window.dispatchEvent(new CustomEvent(e,{detail:t}))}async function A(e,t){e.preventDefault();let r=t.getAttribute("href");if(!r){console.error("[Recur] Missing href on recur-button");return}let o=S(r);if(!o){console.error("[Recur] Invalid product URL:",r);return}let n=P(t);if(!n){console.error("[Recur] Missing publishable key. Add data-key attribute or set RecurConfig.publishableKey");return}let i={productId:o.productId||"",email:t.getAttribute("data-email")||void 0,name:t.getAttribute("data-name")||void 0,successUrl:t.getAttribute("data-success-url")||void 0,theme:t.getAttribute("data-theme")||void 0};t.setAttribute("data-loading","true");let s=t.textContent;t.textContent="...";try{let c=await b(i,n),l=new URL(`${a.embedUrl}/checkout`);l.searchParams.set("id",c.id),l.searchParams.set("key",c.publishableKey),l.searchParams.set("client_secret",c.clientSecret),i.theme&&l.searchParams.set("theme",i.theme),y(l.toString(),()=>{let x=i.successUrl})}catch(c){console.error("[Recur] Checkout error:",c),d("recur:error",{message:c instanceof Error?c.message:"Unknown error"})}finally{t.setAttribute("data-loading","false"),t.textContent=s}}function g(){document.querySelectorAll(".recur-button").forEach(t=>{t.hasAttribute("data-recur-initialized")||(t.setAttribute("data-recur-initialized","true"),t.addEventListener("click",r=>A(r,t)))})}function L(){let e=window.location.hostname;(e==="localhost"||e==="127.0.0.1"||e.includes(".local"))&&(a.embedUrl=`${window.location.protocol}//${e}:3002`,a.apiUrl=`${window.location.protocol}//${e}:3000/api`)}var w={config(e){Object.assign(a,e)},async popup(e){let t=await b(e,e.key),r=new URL(`${a.embedUrl}/checkout`);r.searchParams.set("id",t.id),r.searchParams.set("key",t.publishableKey),r.searchParams.set("client_secret",t.clientSecret),e.theme&&r.searchParams.set("theme",e.theme),y(r.toString())},close(){u()},init(){g()}};function M(){L(),m(),document.readyState==="loading"?document.addEventListener("DOMContentLoaded",g):g(),new MutationObserver(t=>{for(let r of t)r.addedNodes.length>0&&g()}).observe(document.body,{childList:!0,subtree:!0})}window.Recur=w;M();var I=w;return U(T);})();
126
126
  /* Recur Checkout - https://recur.tw */
package/dist/index.cjs CHANGED
@@ -1048,7 +1048,7 @@ var init_payment_form_shadow = __esm({
1048
1048
  var payment_form_light_default;
1049
1049
  var init_payment_form_light = __esm({
1050
1050
  "src/components/styles/payment-form-light.css"() {
1051
- payment_form_light_default = "/**\n * Payment Form - Light DOM Styles\n * These styles are applied to slotted content in the Light DOM\n * Using BEM naming convention with 'recur-' prefix for isolation\n */\n\n/* ============================================\n * Order Summary Styles\n * ============================================ */\n.recur-order-summary {\n background: #f7fafc;\n border: 1px solid #e2e8f0;\n border-radius: 8px;\n padding: 16px;\n}\n\n.recur-order-summary__row {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 8px;\n}\n\n.recur-order-summary__row--total {\n margin-bottom: 0;\n padding-top: 12px;\n border-top: 1px solid #e2e8f0;\n}\n\n.recur-order-summary__label {\n font-size: 14px;\n color: #4a5568;\n}\n\n.recur-order-summary__value {\n font-size: 14px;\n line-height: 20px;\n font-weight: 500;\n color: #2d3748;\n text-align: right;\n font-variant-numeric: tabular-nums;\n}\n\n.recur-order-summary__total {\n font-size: 18px;\n line-height: 26px;\n font-weight: 700;\n color: #1a202c;\n text-align: right;\n font-variant-numeric: tabular-nums;\n}\n\n/* ============================================\n * Coupon Section Styles\n * ============================================ */\n.recur-coupon {\n padding: 12px 0;\n border-top: 1px solid #e2e8f0;\n margin-top: 12px;\n min-height: 36px;\n display: flex;\n align-items: center;\n}\n\n.recur-coupon__trigger {\n display: flex;\n align-items: center;\n gap: 6px;\n width: 100%;\n height: 36px;\n background: none;\n border: none;\n padding: 0;\n font-size: 14px;\n line-height: 1;\n color: #718096;\n cursor: pointer;\n transition: color 0.2s;\n}\n\n.recur-coupon__trigger:hover {\n color: #4a5568;\n}\n\n.recur-coupon__trigger-icon {\n width: 16px;\n height: 16px;\n}\n\n.recur-coupon__input-row {\n display: flex;\n align-items: center;\n gap: 8px;\n width: 100%;\n height: 36px;\n}\n\n.recur-coupon__input-wrapper {\n position: relative;\n flex: 1;\n min-width: 0;\n}\n\n.recur-coupon__input {\n width: 100%;\n height: 36px;\n padding: 0 32px 0 12px;\n font-size: 14px;\n text-transform: uppercase;\n background-color: #ffffff;\n border: 1px solid #d1d5db;\n border-radius: 6px;\n outline: none;\n transition: border-color 0.2s, box-shadow 0.2s;\n}\n\n.recur-coupon__input::placeholder {\n text-transform: none;\n}\n\n.recur-coupon__input:hover:not(:focus) {\n border-color: #9ca3af;\n}\n\n.recur-coupon__input:focus {\n border-color: var(--ring, hsl(215 16% 47%));\n outline: 0;\n box-shadow: 0 0 0 3px color-mix(in oklch, var(--ring, hsl(215 16% 47%)) 50%, transparent);\n transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n}\n\n.recur-coupon__clear {\n position: absolute;\n right: 8px;\n top: 50%;\n transform: translateY(-50%);\n background: none;\n border: none;\n padding: 4px;\n cursor: pointer;\n color: #9ca3af;\n border-radius: 4px;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: color 0.2s;\n}\n\n.recur-coupon__clear:hover {\n color: #6b7280;\n}\n\n.recur-coupon__clear-icon {\n width: 16px;\n height: 16px;\n}\n\n.recur-coupon__btn {\n height: 36px;\n padding: 0 12px;\n font-size: 14px;\n font-weight: 500;\n border-radius: 6px;\n cursor: pointer;\n transition: background-color 0.2s, opacity 0.2s;\n white-space: nowrap;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n}\n\n.recur-coupon__btn--apply {\n background: #18181b;\n color: white;\n border: none;\n min-width: 52px;\n}\n\n.recur-coupon__btn--apply .recur-coupon__spinner {\n width: 14px;\n height: 14px;\n}\n\n.recur-coupon__btn--apply:hover:not(:disabled) {\n background: #27272a;\n}\n\n.recur-coupon__btn--apply:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n.recur-coupon__error {\n width: 100%;\n margin-top: 8px;\n font-size: 13px;\n color: #dc2626;\n}\n\n.recur-coupon__applied-wrapper {\n width: 100%;\n}\n\n.recur-coupon__description {\n font-size: 12px;\n color: #6b7280;\n margin: 4px 0 0 22px;\n}\n\n.recur-coupon__bonus {\n display: flex;\n align-items: center;\n gap: 6px;\n background: #dcfce7;\n border-radius: 6px;\n padding: 8px 12px;\n margin-top: 8px;\n font-size: 12px;\n color: #15803d;\n}\n\n.recur-coupon__applied {\n display: flex;\n align-items: center;\n gap: 8px;\n width: 100%;\n height: 36px;\n}\n\n.recur-coupon__applied-info {\n display: flex;\n align-items: center;\n gap: 6px;\n min-width: 0;\n flex: 1;\n}\n\n.recur-coupon__applied-icon {\n width: 16px;\n height: 16px;\n color: #dc2626;\n flex-shrink: 0;\n}\n\n.recur-coupon__applied-name {\n font-size: 14px;\n font-weight: 500;\n color: #dc2626;\n white-space: nowrap;\n}\n\n.recur-coupon__applied-code {\n font-size: 14px;\n color: #9ca3af;\n white-space: nowrap;\n margin-right: 2px;\n}\n\n.recur-coupon__applied-amount {\n font-size: 14px;\n font-weight: 500;\n color: #dc2626;\n white-space: nowrap;\n font-variant-numeric: tabular-nums;\n flex-shrink: 0;\n margin-left: auto;\n}\n\n.recur-coupon__remove {\n background: none;\n border: none;\n padding: 4px;\n cursor: pointer;\n color: #9ca3af;\n border-radius: 4px;\n transition: color 0.2s, background-color 0.2s;\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n}\n\n.recur-coupon__remove:hover {\n color: #6b7280;\n background: rgba(0, 0, 0, 0.05);\n}\n\n.recur-coupon__remove-icon {\n width: 14px;\n height: 14px;\n}\n\n.recur-coupon__spinner {\n width: 14px;\n height: 14px;\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/* Discount Row */\n.recur-order-summary__row--discount {\n color: #16a34a;\n}\n\n.recur-order-summary__row--discount .recur-order-summary__value {\n color: #16a34a;\n font-weight: 500;\n}\n\n/* ============================================\n * Customer Info Styles\n * ============================================ */\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 margin-bottom: 12px;\n}\n\n.recur-info-row:last-child {\n margin-bottom: 0;\n}\n\n.recur-info-label {\n display: block;\n font-size: 12px;\n line-height: 16px;\n color: #718096;\n margin-bottom: 2px;\n}\n\n.recur-info-value {\n display: block;\n font-size: 14px;\n line-height: 20px;\n min-height: 20px;\n color: #2d3748;\n font-weight: 500;\n word-break: break-all;\n}\n\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 height: 36px;\n padding: 0 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 border-color: var(--ring, hsl(215 16% 47%));\n outline: 0;\n box-shadow: 0 0 0 3px color-mix(in oklch, var(--ring, hsl(215 16% 47%)) 50%, transparent);\n transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n}\n\n.recur-form-input:hover:not(:focus) {\n border-color: #9ca3af;\n}\n\n/* ============================================\n * Card Fields Styles\n * ============================================ */\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.recur-payuni-iframe {\n height: 36px;\n width: 100%;\n max-width: 100%;\n box-sizing: border-box;\n touch-action: manipulation;\n}\n\n.recur-card-row {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));\n column-gap: 16px;\n max-width: 100%;\n box-sizing: border-box;\n}\n\n.recur-card-field-container {\n position: relative;\n touch-action: manipulation;\n}\n\n.recur-card-skeleton {\n position: absolute;\n top: 0;\n left: 0;\n height: 36px;\n width: 100%;\n background: linear-gradient(90deg, #f0f0f0 0%, #e8e8e8 50%, #f0f0f0 100%);\n background-size: 200% 100%;\n animation: recur-skeleton-loading 1.5s ease-in-out infinite;\n border-radius: 4px;\n z-index: 1;\n pointer-events: none;\n}\n\n.recur-card-skeleton.hidden {\n display: none;\n}\n\n@keyframes recur-skeleton-loading {\n 0% { background-position: 200% 0; }\n 100% { background-position: -200% 0; }\n}\n\n/* ============================================\n * Submit Button Styles\n * ============================================ */\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 box-sizing: border-box;\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\n/* ============================================\n * Skeleton Loading Styles\n * ============================================ */\n.recur-skeleton {\n display: inline-block;\n background: linear-gradient(90deg, #f0f0f0 0%, #e8e8e8 50%, #f0f0f0 100%);\n background-size: 200% 100%;\n animation: recur-skeleton-loading 1.5s ease-in-out infinite;\n border-radius: 4px;\n}\n";
1051
+ payment_form_light_default = "/**\n * Payment Form - Light DOM Styles\n * These styles are applied to slotted content in the Light DOM\n * Using BEM naming convention with 'recur-' prefix for isolation\n */\n\n/* ============================================\n * Order Summary Styles\n * ============================================ */\n.recur-order-summary {\n background: #f7fafc;\n border: 1px solid #e2e8f0;\n border-radius: 8px;\n padding: 16px;\n}\n\n.recur-order-summary__row {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 8px;\n}\n\n.recur-order-summary__row--total {\n margin-bottom: 0;\n padding-top: 12px;\n border-top: 1px solid #e2e8f0;\n}\n\n.recur-order-summary__label {\n font-size: 14px;\n color: #4a5568;\n}\n\n.recur-order-summary__value {\n font-size: 14px;\n line-height: 20px;\n font-weight: 500;\n color: #2d3748;\n text-align: right;\n font-variant-numeric: tabular-nums;\n}\n\n.recur-order-summary__total {\n font-size: 18px;\n line-height: 26px;\n font-weight: 700;\n color: #1a202c;\n text-align: right;\n font-variant-numeric: tabular-nums;\n}\n\n/* ============================================\n * Coupon Section Styles\n * ============================================ */\n.recur-coupon {\n padding: 12px 0;\n border-top: 1px solid #e2e8f0;\n margin-top: 12px;\n min-height: 36px;\n display: flex;\n align-items: center;\n}\n\n.recur-coupon__trigger {\n display: flex;\n align-items: center;\n gap: 6px;\n width: 100%;\n height: 36px;\n background: none;\n border: none;\n padding: 0;\n font-size: 14px;\n line-height: 1;\n color: #718096;\n cursor: pointer;\n transition: color 0.2s;\n}\n\n.recur-coupon__trigger:hover {\n color: #4a5568;\n}\n\n.recur-coupon__trigger-icon {\n width: 16px;\n height: 16px;\n}\n\n.recur-coupon__input-row {\n display: flex;\n align-items: center;\n gap: 8px;\n width: 100%;\n height: 36px;\n}\n\n.recur-coupon__input-wrapper {\n position: relative;\n flex: 1;\n min-width: 0;\n}\n\n.recur-coupon__input {\n width: 100%;\n height: 36px;\n padding: 0 32px 0 12px;\n font-size: 14px;\n text-transform: uppercase;\n background-color: #ffffff;\n border: 1px solid #d1d5db;\n border-radius: 6px;\n outline: none;\n transition: border-color 0.2s, box-shadow 0.2s;\n}\n\n.recur-coupon__input::placeholder {\n text-transform: none;\n}\n\n.recur-coupon__input:hover:not(:focus) {\n border-color: #9ca3af;\n}\n\n.recur-coupon__input:focus {\n border-color: var(--ring, hsl(215 16% 47%));\n outline: 0;\n box-shadow: 0 0 0 3px color-mix(in oklch, var(--ring, hsl(215 16% 47%)) 50%, transparent);\n transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n}\n\n.recur-coupon__clear {\n position: absolute;\n right: 8px;\n top: 50%;\n transform: translateY(-50%);\n background: none;\n border: none;\n padding: 4px;\n cursor: pointer;\n color: #9ca3af;\n border-radius: 4px;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: color 0.2s;\n}\n\n.recur-coupon__clear:hover {\n color: #6b7280;\n}\n\n.recur-coupon__clear-icon {\n width: 16px;\n height: 16px;\n}\n\n.recur-coupon__btn {\n height: 36px;\n padding: 0 12px;\n font-size: 14px;\n font-weight: 500;\n border-radius: 6px;\n cursor: pointer;\n transition: background-color 0.2s, opacity 0.2s;\n white-space: nowrap;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n}\n\n.recur-coupon__btn--apply {\n background: #18181b;\n color: white;\n border: none;\n min-width: 52px;\n}\n\n.recur-coupon__btn--apply .recur-coupon__spinner {\n width: 14px;\n height: 14px;\n}\n\n.recur-coupon__btn--apply:hover:not(:disabled) {\n background: #27272a;\n}\n\n.recur-coupon__btn--apply:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n.recur-coupon__error {\n width: 100%;\n margin-top: 8px;\n font-size: 13px;\n color: #dc2626;\n}\n\n.recur-coupon__applied-wrapper {\n width: 100%;\n}\n\n.recur-coupon__description {\n font-size: 12px;\n color: #6b7280;\n margin: 4px 0 0 22px;\n}\n\n.recur-coupon__bonus {\n display: flex;\n align-items: center;\n gap: 6px;\n background: #dcfce7;\n border-radius: 6px;\n padding: 8px 12px;\n margin-top: 8px;\n font-size: 12px;\n color: #15803d;\n}\n\n.recur-coupon__applied {\n display: flex;\n align-items: center;\n gap: 8px;\n width: 100%;\n height: 36px;\n}\n\n.recur-coupon__applied-info {\n display: flex;\n align-items: center;\n gap: 6px;\n min-width: 0;\n flex: 1;\n}\n\n.recur-coupon__applied-icon {\n width: 16px;\n height: 16px;\n color: #dc2626;\n flex-shrink: 0;\n}\n\n.recur-coupon__applied-name {\n font-size: 14px;\n font-weight: 500;\n color: #dc2626;\n white-space: nowrap;\n}\n\n.recur-coupon__applied-code {\n font-size: 14px;\n color: #9ca3af;\n white-space: nowrap;\n margin-right: 2px;\n}\n\n.recur-coupon__applied-amount {\n font-size: 14px;\n font-weight: 500;\n color: #dc2626;\n white-space: nowrap;\n font-variant-numeric: tabular-nums;\n flex-shrink: 0;\n margin-left: auto;\n}\n\n.recur-coupon__remove {\n background: none;\n border: none;\n padding: 4px;\n cursor: pointer;\n color: #9ca3af;\n border-radius: 4px;\n transition: color 0.2s, background-color 0.2s;\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n}\n\n.recur-coupon__remove:hover {\n color: #6b7280;\n background: rgba(0, 0, 0, 0.05);\n}\n\n.recur-coupon__remove-icon {\n width: 14px;\n height: 14px;\n}\n\n.recur-coupon__spinner {\n width: 14px;\n height: 14px;\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/* Discount Row */\n.recur-order-summary__row--discount {\n color: #16a34a;\n}\n\n.recur-order-summary__row--discount .recur-order-summary__value {\n color: #16a34a;\n font-weight: 500;\n}\n\n/* ============================================\n * Customer Info Styles\n * ============================================ */\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 margin-bottom: 12px;\n}\n\n.recur-info-row:last-child {\n margin-bottom: 0;\n}\n\n.recur-info-label {\n display: block;\n font-size: 12px;\n line-height: 16px;\n color: #718096;\n margin-bottom: 2px;\n}\n\n.recur-info-value {\n display: block;\n font-size: 14px;\n line-height: 20px;\n min-height: 20px;\n color: #2d3748;\n font-weight: 500;\n word-break: break-all;\n}\n\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 height: 36px;\n padding: 0 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 border-color: var(--ring, hsl(215 16% 47%));\n outline: 0;\n box-shadow: 0 0 0 3px color-mix(in oklch, var(--ring, hsl(215 16% 47%)) 50%, transparent);\n transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n}\n\n.recur-form-input:hover:not(:focus) {\n border-color: #9ca3af;\n}\n\n/* ============================================\n * Card Fields Styles\n * ============================================ */\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.recur-payuni-iframe {\n height: 36px;\n width: 100%;\n max-width: 100%;\n box-sizing: border-box;\n touch-action: manipulation;\n}\n\n.recur-card-row {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));\n column-gap: 16px;\n max-width: 100%;\n box-sizing: border-box;\n}\n\n.recur-card-field-container {\n position: relative;\n touch-action: manipulation;\n}\n\n.recur-card-skeleton {\n position: absolute;\n top: 0;\n left: 0;\n height: 36px;\n width: 100%;\n background: linear-gradient(90deg, #f0f0f0 0%, #e8e8e8 50%, #f0f0f0 100%);\n background-size: 200% 100%;\n animation: recur-skeleton-loading 1.5s ease-in-out infinite;\n border-radius: 4px;\n z-index: 1;\n pointer-events: none;\n}\n\n.recur-card-skeleton.hidden {\n display: none;\n}\n\n@keyframes recur-skeleton-loading {\n 0% { background-position: 200% 0; }\n 100% { background-position: -200% 0; }\n}\n\n/* ============================================\n * Submit Button Styles\n * ============================================ */\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 box-sizing: border-box;\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\n/* ============================================\n * Skeleton Loading Styles\n * ============================================ */\n.recur-skeleton {\n display: inline-block;\n background: linear-gradient(90deg, #f0f0f0 0%, #e8e8e8 50%, #f0f0f0 100%);\n background-size: 200% 100%;\n animation: recur-skeleton-loading 1.5s ease-in-out infinite;\n border-radius: 4px;\n}\n\n/* ============================================\n * Test Mode Banner Styles\n * ============================================ */\n.recur-test-mode-banner {\n background: linear-gradient(135deg, #fef3c7 0%, #fde68a 100%);\n border: 1px solid #f59e0b;\n border-radius: 8px;\n margin-bottom: 16px;\n overflow: hidden;\n}\n\n.recur-test-mode-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 10px 14px;\n cursor: pointer;\n user-select: none;\n transition: background 0.2s;\n}\n\n.recur-test-mode-header:hover {\n background: rgba(245, 158, 11, 0.1);\n}\n\n.recur-test-mode-badge {\n font-size: 13px;\n font-weight: 600;\n color: #92400e;\n}\n\n.recur-test-mode-toggle {\n font-size: 12px;\n color: #b45309;\n}\n\n.recur-test-cards {\n background: rgba(255, 255, 255, 0.7);\n padding: 12px 14px;\n border-top: 1px solid rgba(245, 158, 11, 0.3);\n}\n\n.recur-test-cards-info {\n font-size: 12px;\n color: #78350f;\n margin: 0 0 10px 0;\n}\n\n.recur-test-card {\n background: white;\n border: 1px solid #e5e7eb;\n border-radius: 6px;\n padding: 10px 12px;\n margin-bottom: 8px;\n}\n\n.recur-test-card:last-child {\n margin-bottom: 0;\n}\n\n.recur-test-card-row {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 4px;\n}\n\n.recur-test-card-number {\n font-family: 'SF Mono', 'Monaco', 'Consolas', monospace;\n font-size: 14px;\n font-weight: 600;\n color: #1f2937;\n letter-spacing: 0.5px;\n}\n\n.recur-test-card-copy {\n font-size: 11px;\n font-weight: 500;\n color: #3b82f6;\n background: #eff6ff;\n border: 1px solid #bfdbfe;\n border-radius: 4px;\n padding: 3px 8px;\n cursor: pointer;\n transition: all 0.2s;\n}\n\n.recur-test-card-copy:hover {\n background: #dbeafe;\n border-color: #93c5fd;\n}\n\n.recur-test-card-copy.copied {\n background: #dcfce7;\n border-color: #86efac;\n color: #16a34a;\n}\n\n.recur-test-card-details {\n display: flex;\n flex-wrap: wrap;\n gap: 8px;\n font-size: 11px;\n color: #6b7280;\n}\n\n.recur-test-card-desc {\n color: #374151;\n font-weight: 500;\n}\n\n.recur-test-card-meta {\n color: #9ca3af;\n}\n";
1052
1052
  }
1053
1053
  });
1054
1054
 
@@ -1097,6 +1097,9 @@ var init_payment_form = __esm({
1097
1097
  __publicField(this, "customStyles");
1098
1098
  __publicField(this, "_isInitializing", false);
1099
1099
  __publicField(this, "_initializationAborted", false);
1100
+ __publicField(this, "_paymentSession", null);
1101
+ // Test mode state
1102
+ __publicField(this, "_showTestCardHints", false);
1100
1103
  // Coupon state
1101
1104
  __publicField(this, "_showCouponInput", false);
1102
1105
  __publicField(this, "_couponCode", "");
@@ -1163,6 +1166,8 @@ var init_payment_form = __esm({
1163
1166
  <style>${this.getShadowDOMStyles()}</style>
1164
1167
 
1165
1168
  <div class="payment-form-wrapper">
1169
+ <slot name="test-mode-banner"></slot>
1170
+
1166
1171
  <div class="form-header">
1167
1172
  <h2 class="form-title">訂閱付款</h2>
1168
1173
  </div>
@@ -1199,6 +1204,10 @@ var init_payment_form = __esm({
1199
1204
  ${this.getCustomStyles()}
1200
1205
  </style>
1201
1206
 
1207
+ <div slot="test-mode-banner">
1208
+ ${this.renderTestCardHints()}
1209
+ </div>
1210
+
1202
1211
  <div slot="order-summary">
1203
1212
  ${this.renderOrderSummary()}
1204
1213
  </div>
@@ -1235,6 +1244,63 @@ var init_payment_form = __esm({
1235
1244
  }
1236
1245
  }
1237
1246
  // ============================================
1247
+ // Test Mode Helpers
1248
+ // ============================================
1249
+ isTestMode() {
1250
+ const publishableKey = this.getAttribute("publishable-key") || "";
1251
+ return publishableKey.startsWith("pk_test_");
1252
+ }
1253
+ renderTestCardHints() {
1254
+ if (!this.isTestMode()) {
1255
+ return litHtml.html``;
1256
+ }
1257
+ const testCards = [
1258
+ { number: "4147 6310 0000 0001", desc: "VISA \u6E2C\u8A66\u5361\uFF08\u6388\u6B0A\u6210\u529F\uFF09", expiry: "\u4EFB\u610F\u672A\u4F86\u65E5\u671F", cvc: "\u4EFB\u610F3\u78BC" },
1259
+ { number: "3560 5110 0000 0001", desc: "JCB \u6E2C\u8A66\u5361\uFF08\u6388\u6B0A\u6210\u529F\uFF09", expiry: "\u4EFB\u610F\u672A\u4F86\u65E5\u671F", cvc: "\u4EFB\u610F3\u78BC" },
1260
+ { number: "4147 6310 0000 0002", desc: "VISA \u6E2C\u8A66\u5361\uFF083D\u9A57\u8B49\u5931\u6557\uFF09", expiry: "\u4EFB\u610F\u672A\u4F86\u65E5\u671F", cvc: "\u4EFB\u610F3\u78BC" }
1261
+ ];
1262
+ const copyToClipboard = (text, event) => {
1263
+ const btn = event.currentTarget;
1264
+ navigator.clipboard.writeText(text.replace(/\s/g, "")).then(() => {
1265
+ const originalText = btn.textContent;
1266
+ btn.textContent = "\u5DF2\u8907\u88FD!";
1267
+ btn.classList.add("copied");
1268
+ setTimeout(() => {
1269
+ btn.textContent = originalText;
1270
+ btn.classList.remove("copied");
1271
+ }, 1500);
1272
+ });
1273
+ };
1274
+ return litHtml.html`
1275
+ <div class="recur-test-mode-banner">
1276
+ <div class="recur-test-mode-header" @click=${() => {
1277
+ this._showTestCardHints = !this._showTestCardHints;
1278
+ this.renderLightDOM();
1279
+ }}>
1280
+ <span class="recur-test-mode-badge">🧪 測試模式</span>
1281
+ <span class="recur-test-mode-toggle">${this._showTestCardHints ? "\u6536\u5408 \u25B2" : "\u5C55\u958B\u6E2C\u8A66\u5361\u865F \u25BC"}</span>
1282
+ </div>
1283
+ ${this._showTestCardHints ? litHtml.html`
1284
+ <div class="recur-test-cards">
1285
+ <p class="recur-test-cards-info">使用以下測試卡號進行測試付款:</p>
1286
+ ${testCards.map((card) => litHtml.html`
1287
+ <div class="recur-test-card">
1288
+ <div class="recur-test-card-row">
1289
+ <span class="recur-test-card-number">${card.number}</span>
1290
+ <button type="button" class="recur-test-card-copy" @click=${(e) => copyToClipboard(card.number, e)}>複製</button>
1291
+ </div>
1292
+ <div class="recur-test-card-details">
1293
+ <span class="recur-test-card-desc">${card.desc}</span>
1294
+ <span class="recur-test-card-meta">到期日: ${card.expiry} / CVC: ${card.cvc}</span>
1295
+ </div>
1296
+ </div>
1297
+ `)}
1298
+ </div>
1299
+ ` : litHtml.nothing}
1300
+ </div>
1301
+ `;
1302
+ }
1303
+ // ============================================
1238
1304
  // Section Renderers (lit-html)
1239
1305
  // ============================================
1240
1306
  renderOrderSummary() {
@@ -1648,16 +1714,16 @@ var init_payment_form = __esm({
1648
1714
  }
1649
1715
  this._appliedCoupon = {
1650
1716
  code: data.discount.code,
1651
- couponName: data.discount.couponName,
1652
- discountType: data.discount.discountType,
1653
- discountAmount: data.discount.discountAmount,
1717
+ couponName: data.discount.coupon_name,
1718
+ discountType: data.discount.discount_type,
1719
+ discountAmount: data.discount.discount_amount,
1654
1720
  duration: data.discount.duration,
1655
- durationMonths: data.discount.durationMonths,
1656
- bonusTrialDays: data.discount.bonusTrialDays,
1657
- bonusMonths: data.discount.bonusMonths,
1658
- productInterval: data.discount.productInterval,
1659
- productIntervalCount: data.discount.productIntervalCount,
1660
- productPrice: data.discount.productPrice
1721
+ durationMonths: data.discount.duration_months,
1722
+ bonusTrialDays: data.discount.bonus_trial_days,
1723
+ bonusMonths: data.discount.bonus_months,
1724
+ productInterval: data.discount.product_interval,
1725
+ productIntervalCount: data.discount.product_interval_count,
1726
+ productPrice: data.discount.product_price
1661
1727
  };
1662
1728
  this._showCouponInput = false;
1663
1729
  this._couponCode = "";
@@ -1668,7 +1734,7 @@ var init_payment_form = __esm({
1668
1734
  bubbles: true,
1669
1735
  composed: true
1670
1736
  }));
1671
- } catch (err) {
1737
+ } catch {
1672
1738
  this._couponError = "\u7DB2\u8DEF\u932F\u8AA4\uFF0C\u8ACB\u7A0D\u5F8C\u518D\u8A66";
1673
1739
  this._couponLoading = false;
1674
1740
  this.updateCouponSectionDOM();
@@ -1702,7 +1768,7 @@ var init_payment_form = __esm({
1702
1768
  bubbles: true,
1703
1769
  composed: true
1704
1770
  }));
1705
- } catch (err) {
1771
+ } catch {
1706
1772
  this._couponError = "\u7DB2\u8DEF\u932F\u8AA4\uFF0C\u8ACB\u7A0D\u5F8C\u518D\u8A66";
1707
1773
  this._couponLoading = false;
1708
1774
  this.updateCouponSectionDOM();
@@ -1861,7 +1927,8 @@ var init_payment_form = __esm({
1861
1927
  try {
1862
1928
  await paymentSession.start();
1863
1929
  } catch (startError) {
1864
- const is1008Error = startError?.message?.includes("1008") || startError?.message?.includes("timeout") || startError?.code === 1008;
1930
+ const errorObj = startError;
1931
+ const is1008Error = errorObj?.message?.includes("1008") || errorObj?.message?.includes("timeout") || errorObj?.code === 1008;
1865
1932
  if (is1008Error && this._initializationAborted) {
1866
1933
  console.log("[PaymentForm] Caught 1008 timeout error after component disconnect - ignoring");
1867
1934
  this._isInitializing = false;
@@ -1881,7 +1948,8 @@ var init_payment_form = __esm({
1881
1948
  return;
1882
1949
  }
1883
1950
  console.log("[PaymentForm] PAYUNi update:", data);
1884
- const allFieldsComplete = data.status && data.status.CardNo === true && data.status.CardExp === true && data.status.CardCvc === true;
1951
+ const updateData = data;
1952
+ const allFieldsComplete = updateData.status && updateData.status.CardNo === true && updateData.status.CardExp === true && updateData.status.CardCvc === true;
1885
1953
  const submitBtn = document.getElementById(`${this.containerId}-submit-btn`);
1886
1954
  if (submitBtn) {
1887
1955
  submitBtn.disabled = !allFieldsComplete;
@@ -2107,7 +2175,7 @@ var init_checkout_button = __esm({
2107
2175
  this._isLoading = false;
2108
2176
  this.render();
2109
2177
  this.setupEventListeners();
2110
- this.dispatchError(error.message || "Failed to create checkout session");
2178
+ this.dispatchError(error instanceof Error ? error.message : "Failed to create checkout session");
2111
2179
  }
2112
2180
  });
2113
2181
  this.attachShadow({ mode: "open" });
@@ -2555,7 +2623,7 @@ var init_portal_button = __esm({
2555
2623
  this._isLoading = false;
2556
2624
  this.render();
2557
2625
  this.setupEventListeners();
2558
- this.dispatchError(error.message || "Failed to create portal session");
2626
+ this.dispatchError(error instanceof Error ? error.message : "Failed to create portal session");
2559
2627
  }
2560
2628
  }
2561
2629
  dispatchError(message) {
@@ -2674,7 +2742,7 @@ function toCamelCase(obj) {
2674
2742
 
2675
2743
  // package.json
2676
2744
  var package_default = {
2677
- version: "0.11.0"};
2745
+ version: "0.13.0"};
2678
2746
  var SDK_VERSION = package_default.version;
2679
2747
  var SDK_TYPE = "react";
2680
2748
  var RecurContext = React.createContext(null);
@@ -2700,7 +2768,7 @@ function RecurProvider({ children, config: initialConfig = {}, customer: custome
2700
2768
  checkoutMode: "embedded",
2701
2769
  ...initialConfig
2702
2770
  });
2703
- }, [initialConfig.checkoutMode, initialConfig.publishableKey, initialConfig.containerElementId]);
2771
+ }, [initialConfig]);
2704
2772
  React__default.default.useEffect(() => {
2705
2773
  setIsCheckingOut(false);
2706
2774
  }, [config.checkoutMode]);
@@ -2988,14 +3056,14 @@ function RecurProvider({ children, config: initialConfig = {}, customer: custome
2988
3056
  }
2989
3057
  console.log("[Recur SDK] Step 1: Creating checkout session...");
2990
3058
  const checkoutRequestBody = {
2991
- customerName: options.customerName,
2992
- customerEmail: options.customerEmail
3059
+ customer_name: options.customerName,
3060
+ customer_email: options.customerEmail
2993
3061
  };
2994
- if (productId) checkoutRequestBody.productId = productId;
2995
- if (productSlug) checkoutRequestBody.productSlug = productSlug;
2996
- if (options.externalCustomerId) checkoutRequestBody.externalCustomerId = options.externalCustomerId;
2997
- if (options.successUrl) checkoutRequestBody.successUrl = options.successUrl;
2998
- if (options.cancelUrl) checkoutRequestBody.cancelUrl = options.cancelUrl;
3062
+ if (productId) checkoutRequestBody.product_id = productId;
3063
+ if (productSlug) checkoutRequestBody.product_slug = productSlug;
3064
+ if (options.externalCustomerId) checkoutRequestBody.external_customer_id = options.externalCustomerId;
3065
+ if (options.successUrl) checkoutRequestBody.success_url = options.successUrl;
3066
+ if (options.cancelUrl) checkoutRequestBody.cancel_url = options.cancelUrl;
2999
3067
  const checkoutResponse = await fetch(`${baseUrl}/v1/checkouts`, {
3000
3068
  method: "POST",
3001
3069
  headers,
@@ -3198,9 +3266,10 @@ function RecurProvider({ children, config: initialConfig = {}, customer: custome
3198
3266
  paymentForm.resetButton?.();
3199
3267
  return;
3200
3268
  } else {
3269
+ const failureMessage = failureError.details && !Array.isArray(failureError.details) ? failureError.details.failure_message : void 0;
3201
3270
  paymentForm.showError?.({
3202
3271
  title: "\u4ED8\u6B3E\u5931\u6557",
3203
- message: failureError.details?.failure_message || failureError.message
3272
+ message: failureMessage || failureError.message
3204
3273
  });
3205
3274
  paymentForm.resetButton?.();
3206
3275
  return;
@@ -3327,7 +3396,8 @@ function RecurProvider({ children, config: initialConfig = {}, customer: custome
3327
3396
  }
3328
3397
  }
3329
3398
  } catch (pollError) {
3330
- if (pollError.message === "3D \u9A57\u8B49\u5DF2\u53D6\u6D88" || pollError.message === "\u4ED8\u6B3E\u5931\u6557\u6216\u5DF2\u53D6\u6D88") {
3399
+ const errorMessage = pollError instanceof Error ? pollError.message : String(pollError);
3400
+ if (errorMessage === "3D \u9A57\u8B49\u5DF2\u53D6\u6D88" || errorMessage === "\u4ED8\u6B3E\u5931\u6557\u6216\u5DF2\u53D6\u6D88") {
3331
3401
  throw pollError;
3332
3402
  }
3333
3403
  console.error("[Recur SDK] Poll error:", pollError);
@@ -3389,7 +3459,7 @@ function RecurProvider({ children, config: initialConfig = {}, customer: custome
3389
3459
  currentPeriodEnd: paymentResult.subscription.currentPeriodEnd
3390
3460
  });
3391
3461
  } else {
3392
- const chargeId = paymentResult.charge?.id || paymentResult.paymentIntent?.id;
3462
+ const chargeId = paymentResult.charge?.id || paymentResult.paymentIntent?.id || checkoutResult.checkout.id;
3393
3463
  const chargeStatus = paymentResult.charge?.status || "SUCCEEDED";
3394
3464
  options.onPaymentComplete({
3395
3465
  id: chargeId,
@@ -3610,28 +3680,65 @@ function useCustomer() {
3610
3680
  throw new Error("useCustomer must be used within a RecurProvider");
3611
3681
  }
3612
3682
  const { customer, subscription, entitlements, isLoading: isLoading2, error, refetch } = context;
3613
- const check = React.useCallback(
3614
- (options) => {
3683
+ const resolveProduct = (target) => {
3684
+ if (typeof target === "string") {
3685
+ return target;
3686
+ }
3687
+ if ("product" in target) {
3688
+ return target.product;
3689
+ }
3690
+ if ("feature" in target) {
3691
+ throw new Error("Feature-based checks are not yet supported. Coming in Phase 2.");
3692
+ }
3693
+ if ("benefit" in target) {
3694
+ throw new Error("Benefit-based checks are not yet supported. Coming in Phase 2.");
3695
+ }
3696
+ throw new Error("Invalid check target");
3697
+ };
3698
+ const checkSync = React.useCallback(
3699
+ (target) => {
3700
+ const product = resolveProduct(target);
3615
3701
  if (!customer) {
3616
3702
  return { allowed: false, reason: "no_customer" };
3617
3703
  }
3618
3704
  if (entitlements.length === 0) {
3619
- return { allowed: false, reason: "no_subscription" };
3705
+ return { allowed: false, reason: "no_entitlement" };
3620
3706
  }
3621
- const entitlement = entitlements.find(
3622
- (e) => e.product === options.product || e.productId === options.product
3707
+ const matchedEntitlement = entitlements.find(
3708
+ (e) => e.product === product || e.productId === product
3623
3709
  );
3624
- if (!entitlement) {
3625
- return { allowed: false, reason: "wrong_product" };
3710
+ if (!matchedEntitlement) {
3711
+ return { allowed: false, reason: "not_found" };
3626
3712
  }
3627
3713
  let matchingSubscription;
3628
- if (subscription && (subscription.product.slug === options.product || subscription.product.id === options.product)) {
3714
+ if (subscription && (subscription.product.slug === product || subscription.product.id === product)) {
3629
3715
  matchingSubscription = subscription;
3630
3716
  }
3631
- return { allowed: true, subscription: matchingSubscription };
3717
+ return {
3718
+ allowed: true,
3719
+ entitlement: matchedEntitlement,
3720
+ // Backward compatibility
3721
+ subscription: matchingSubscription
3722
+ };
3632
3723
  },
3633
3724
  [customer, entitlements, subscription]
3634
3725
  );
3726
+ const checkLive = React.useCallback(
3727
+ async (target) => {
3728
+ await refetch();
3729
+ return checkSync(target);
3730
+ },
3731
+ [refetch, checkSync]
3732
+ );
3733
+ const check = React.useCallback(
3734
+ (target, options) => {
3735
+ if (options?.live) {
3736
+ return checkLive(target);
3737
+ }
3738
+ return checkSync(target);
3739
+ },
3740
+ [checkSync, checkLive]
3741
+ );
3635
3742
  return {
3636
3743
  customer,
3637
3744
  subscription,