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 +2 -2
- package/dist/index.cjs +144 -37
- package/dist/index.d.cts +243 -22
- package/dist/index.d.ts +243 -22
- package/dist/index.js +144 -37
- package/dist/recur.umd.js +181 -36
- package/dist/server.cjs +5 -1
- package/dist/server.d.cts +36 -3
- package/dist/server.d.ts +36 -3
- package/dist/server.js +5 -1
- package/dist/widget.js +3 -3
- package/package.json +1 -1
package/dist/checkout.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"use strict";var RecurCheckoutScript=(()=>{var
|
|
1
|
+
"use strict";var RecurCheckoutScript=(()=>{var f=Object.defineProperty;var v=Object.getOwnPropertyDescriptor;var E=Object.getOwnPropertyNames;var C=Object.prototype.hasOwnProperty;var R=(e,t)=>{for(var r in t)f(e,r,{get:t[r],enumerable:!0})},_=(e,t,r,o)=>{if(t&&typeof t=="object"||typeof t=="function")for(let n of E(t))!C.call(e,n)&&n!==r&&f(e,n,{get:()=>t[n],enumerable:!(o=v(t,n))||o.enumerable});return e};var U=e=>_(f({},"__esModule",{value:!0}),e);var T={};R(T,{default:()=>I});var a={embedUrl:"https://embed.recur.tw",apiUrl:"https://api.recur.tw"},h=null,p=null;function m(){if(document.getElementById("recur-checkout-styles"))return;let e=document.createElement("style");e.id="recur-checkout-styles",e.textContent=`
|
|
2
2
|
.recur-checkout-dialog {
|
|
3
3
|
border: none;
|
|
4
4
|
border-radius: 12px;
|
|
@@ -122,5 +122,5 @@
|
|
|
122
122
|
opacity: 0.7;
|
|
123
123
|
pointer-events: none;
|
|
124
124
|
}
|
|
125
|
-
`,document.head.appendChild(e)}function S(e){try{let r=new URL(e,window.location.origin).pathname.split("/"),o=r.indexOf("buy");if(o===-1||o>=r.length-1)return null;let n=r[o+1];return n?n.startsWith("prod_")?{productId:n}:{productSlug:n}:null}catch{return null}}function P(e){let t=e.getAttribute("data-key");if(t)return t;try{let
|
|
125
|
+
`,document.head.appendChild(e)}function S(e){try{let r=new URL(e,window.location.origin).pathname.split("/"),o=r.indexOf("buy");if(o===-1||o>=r.length-1)return null;let n=r[o+1];return n?n.startsWith("prod_")?{productId:n}:{productSlug:n}:null}catch{return null}}function P(e){let t=e.getAttribute("data-key");if(t)return t;try{let n=e.getAttribute("href");if(n){let s=new URL(n,window.location.origin).searchParams.get("key");if(s)return s}}catch{}let r=document.querySelector("script[data-recur-key]");if(r)return r.getAttribute("data-recur-key");let o=window.RecurConfig;return o?.publishableKey?o.publishableKey:null}async function b(e,t){let r={};e.productId&&(r.productId=e.productId),e.email&&(r.customerEmail=e.email),e.name&&(r.customerName=e.name);let o=await fetch(`${a.apiUrl}/v1/checkouts`,{method:"POST",headers:{"Content-Type":"application/json","X-Recur-Publishable-Key":t,"X-Recur-Embed-Domain":a.embedUrl,"X-Recur-SDK-Type":"checkout.js","X-Recur-Source":window.location.origin},body:JSON.stringify(r)});if(!o.ok){let i=await o.json().catch(()=>({}));throw new Error(i.error?.message||i.error||"Failed to create checkout")}let n=await o.json();return{id:n.checkout.id,publishableKey:t,clientSecret:n.checkout.client_secret}}function y(e,t){u(),m();let r=document.createElement("dialog");r.className="recur-checkout-dialog";let o=document.createElement("div");o.className="recur-checkout-dialog__header";let n=document.createElement("button");n.className="recur-checkout-dialog__close",n.innerHTML="×",n.type="button",n.setAttribute("aria-label","Close"),n.onclick=()=>{u(),t?.(),d("recur:close",{})},o.appendChild(n);let i=document.createElement("iframe");i.className="recur-checkout-dialog__iframe",i.src=e,i.setAttribute("allow","payment"),i.setAttribute("title","Recur Checkout"),r.appendChild(o),r.appendChild(i),document.body.appendChild(r),h=r,p=i,r.showModal(),document.body.style.overflow="hidden",r.addEventListener("cancel",s=>{s.preventDefault(),u(),t?.(),d("recur:close",{})}),window.addEventListener("message",k)}function u(){h&&(h.close(),h.remove(),h=null),p=null,document.body.style.overflow="",window.removeEventListener("message",k)}function k(e){if(!e.origin.includes("recur.tw")&&!e.origin.includes("localhost"))return;let{type:t,data:r}=e.data||{};switch(t){case"RECUR_PAYMENT_SUCCESS":u(),d("recur:success",r);break;case"RECUR_PAYMENT_ERROR":d("recur:error",r);break;case"RECUR_PAYMENT_CANCEL":u(),d("recur:close",{});break;case"RECUR_RESIZE":p&&r?.height&&(p.style.height=`${Math.min(r.height+56,window.innerHeight*.9)}px`);break}}function d(e,t){window.dispatchEvent(new CustomEvent(e,{detail:t}))}async function A(e,t){e.preventDefault();let r=t.getAttribute("href");if(!r){console.error("[Recur] Missing href on recur-button");return}let o=S(r);if(!o){console.error("[Recur] Invalid product URL:",r);return}let n=P(t);if(!n){console.error("[Recur] Missing publishable key. Add data-key attribute or set RecurConfig.publishableKey");return}let i={productId:o.productId||"",email:t.getAttribute("data-email")||void 0,name:t.getAttribute("data-name")||void 0,successUrl:t.getAttribute("data-success-url")||void 0,theme:t.getAttribute("data-theme")||void 0};t.setAttribute("data-loading","true");let s=t.textContent;t.textContent="...";try{let c=await b(i,n),l=new URL(`${a.embedUrl}/checkout`);l.searchParams.set("id",c.id),l.searchParams.set("key",c.publishableKey),l.searchParams.set("client_secret",c.clientSecret),i.theme&&l.searchParams.set("theme",i.theme),y(l.toString(),()=>{let x=i.successUrl})}catch(c){console.error("[Recur] Checkout error:",c),d("recur:error",{message:c instanceof Error?c.message:"Unknown error"})}finally{t.setAttribute("data-loading","false"),t.textContent=s}}function g(){document.querySelectorAll(".recur-button").forEach(t=>{t.hasAttribute("data-recur-initialized")||(t.setAttribute("data-recur-initialized","true"),t.addEventListener("click",r=>A(r,t)))})}function L(){let e=window.location.hostname;(e==="localhost"||e==="127.0.0.1"||e.includes(".local"))&&(a.embedUrl=`${window.location.protocol}//${e}:3002`,a.apiUrl=`${window.location.protocol}//${e}:3000/api`)}var w={config(e){Object.assign(a,e)},async popup(e){let t=await b(e,e.key),r=new URL(`${a.embedUrl}/checkout`);r.searchParams.set("id",t.id),r.searchParams.set("key",t.publishableKey),r.searchParams.set("client_secret",t.clientSecret),e.theme&&r.searchParams.set("theme",e.theme),y(r.toString())},close(){u()},init(){g()}};function M(){L(),m(),document.readyState==="loading"?document.addEventListener("DOMContentLoaded",g):g(),new MutationObserver(t=>{for(let r of t)r.addedNodes.length>0&&g()}).observe(document.body,{childList:!0,subtree:!0})}window.Recur=w;M();var I=w;return U(T);})();
|
|
126
126
|
/* Recur Checkout - https://recur.tw */
|
package/dist/index.cjs
CHANGED
|
@@ -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.
|
|
1652
|
-
discountType: data.discount.
|
|
1653
|
-
discountAmount: data.discount.
|
|
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.
|
|
1656
|
-
bonusTrialDays: data.discount.
|
|
1657
|
-
bonusMonths: data.discount.
|
|
1658
|
-
productInterval: data.discount.
|
|
1659
|
-
productIntervalCount: data.discount.
|
|
1660
|
-
productPrice: data.discount.
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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.
|
|
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
|
|
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
|
-
|
|
2992
|
-
|
|
3059
|
+
customer_name: options.customerName,
|
|
3060
|
+
customer_email: options.customerEmail
|
|
2993
3061
|
};
|
|
2994
|
-
if (productId) checkoutRequestBody.
|
|
2995
|
-
if (productSlug) checkoutRequestBody.
|
|
2996
|
-
if (options.externalCustomerId) checkoutRequestBody.
|
|
2997
|
-
if (options.successUrl) checkoutRequestBody.
|
|
2998
|
-
if (options.cancelUrl) checkoutRequestBody.
|
|
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:
|
|
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
|
-
|
|
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
|
|
3614
|
-
(
|
|
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: "
|
|
3705
|
+
return { allowed: false, reason: "no_entitlement" };
|
|
3620
3706
|
}
|
|
3621
|
-
const
|
|
3622
|
-
(e) => e.product ===
|
|
3707
|
+
const matchedEntitlement = entitlements.find(
|
|
3708
|
+
(e) => e.product === product || e.productId === product
|
|
3623
3709
|
);
|
|
3624
|
-
if (!
|
|
3625
|
-
return { allowed: false, reason: "
|
|
3710
|
+
if (!matchedEntitlement) {
|
|
3711
|
+
return { allowed: false, reason: "not_found" };
|
|
3626
3712
|
}
|
|
3627
3713
|
let matchingSubscription;
|
|
3628
|
-
if (subscription && (subscription.product.slug ===
|
|
3714
|
+
if (subscription && (subscription.product.slug === product || subscription.product.id === product)) {
|
|
3629
3715
|
matchingSubscription = subscription;
|
|
3630
3716
|
}
|
|
3631
|
-
return {
|
|
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,
|