recur-tw 0.8.2 → 0.8.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +0 -3
- package/dist/index.d.cts +10 -4
- package/dist/index.d.ts +10 -4
- package/dist/index.js +0 -3
- package/dist/recur.umd.js +1 -1
- package/package.json +12 -12
package/dist/index.cjs
CHANGED
|
@@ -2637,9 +2637,6 @@ function RecurProvider({ children, config: initialConfig = {} }) {
|
|
|
2637
2637
|
if (!config.publishableKey) {
|
|
2638
2638
|
throw new Error("publishableKey is required");
|
|
2639
2639
|
}
|
|
2640
|
-
if (!options.customerEmail) {
|
|
2641
|
-
throw new Error("customerEmail is required");
|
|
2642
|
-
}
|
|
2643
2640
|
const baseUrl = config.baseUrl || "https://api.recur.tw";
|
|
2644
2641
|
console.log("[Recur SDK] Base URL:", baseUrl);
|
|
2645
2642
|
const headers = {
|
package/dist/index.d.cts
CHANGED
|
@@ -222,14 +222,20 @@ interface CheckoutOptions {
|
|
|
222
222
|
*/
|
|
223
223
|
planId?: string;
|
|
224
224
|
/**
|
|
225
|
-
* Customer name
|
|
225
|
+
* Customer name (optional)
|
|
226
|
+
* Pre-fills the name field on the checkout page.
|
|
227
|
+
* If not provided, customer will enter it during checkout.
|
|
226
228
|
*/
|
|
227
229
|
customerName?: string;
|
|
228
230
|
/**
|
|
229
|
-
* Customer email address (
|
|
230
|
-
*
|
|
231
|
+
* Customer email address (optional)
|
|
232
|
+
* Pre-fills the email field on the checkout page.
|
|
233
|
+
* If not provided, customer will enter it during checkout.
|
|
234
|
+
*
|
|
235
|
+
* Note: Email is required at checkout time (for receipt),
|
|
236
|
+
* but optional when creating the session.
|
|
231
237
|
*/
|
|
232
|
-
customerEmail
|
|
238
|
+
customerEmail?: string;
|
|
233
239
|
/**
|
|
234
240
|
* External customer ID from your system (optional)
|
|
235
241
|
* Use this to link Recur subscriptions to your existing users.
|
package/dist/index.d.ts
CHANGED
|
@@ -222,14 +222,20 @@ interface CheckoutOptions {
|
|
|
222
222
|
*/
|
|
223
223
|
planId?: string;
|
|
224
224
|
/**
|
|
225
|
-
* Customer name
|
|
225
|
+
* Customer name (optional)
|
|
226
|
+
* Pre-fills the name field on the checkout page.
|
|
227
|
+
* If not provided, customer will enter it during checkout.
|
|
226
228
|
*/
|
|
227
229
|
customerName?: string;
|
|
228
230
|
/**
|
|
229
|
-
* Customer email address (
|
|
230
|
-
*
|
|
231
|
+
* Customer email address (optional)
|
|
232
|
+
* Pre-fills the email field on the checkout page.
|
|
233
|
+
* If not provided, customer will enter it during checkout.
|
|
234
|
+
*
|
|
235
|
+
* Note: Email is required at checkout time (for receipt),
|
|
236
|
+
* but optional when creating the session.
|
|
231
237
|
*/
|
|
232
|
-
customerEmail
|
|
238
|
+
customerEmail?: string;
|
|
233
239
|
/**
|
|
234
240
|
* External customer ID from your system (optional)
|
|
235
241
|
* Use this to link Recur subscriptions to your existing users.
|
package/dist/index.js
CHANGED
|
@@ -2631,9 +2631,6 @@ function RecurProvider({ children, config: initialConfig = {} }) {
|
|
|
2631
2631
|
if (!config.publishableKey) {
|
|
2632
2632
|
throw new Error("publishableKey is required");
|
|
2633
2633
|
}
|
|
2634
|
-
if (!options.customerEmail) {
|
|
2635
|
-
throw new Error("customerEmail is required");
|
|
2636
|
-
}
|
|
2637
2634
|
const baseUrl = config.baseUrl || "https://api.recur.tw";
|
|
2638
2635
|
console.log("[Recur SDK] Base URL:", baseUrl);
|
|
2639
2636
|
const headers = {
|
package/dist/recur.umd.js
CHANGED
|
@@ -1526,7 +1526,7 @@
|
|
|
1526
1526
|
display: block;
|
|
1527
1527
|
user-select: none;
|
|
1528
1528
|
transition: height 0.35s ease, opacity 0.4s ease 0.1s;
|
|
1529
|
-
`.replace(/\s+/g," ").trim(),this.iframe.allow="payment *",this.iframe.setAttribute("title","Secure payment input frame"),this.iframe.setAttribute("role","presentation"),this.iframe.setAttribute("scrolling","no"),this.iframe.setAttribute("frameBorder","0"),this.container.innerHTML="",this.container.appendChild(this.iframe),new Promise((r,i)=>{let s=setTimeout(()=>{i(new Error("Elements initialization timeout"))},3e4),a=()=>{clearTimeout(s),this.off("ready",a),r()},n=l=>{clearTimeout(s),this.off("error",n),i(new Error(l.message||"Elements initialization failed"))};this.on("ready",a),this.on("error",n)})}on(e,t){this.eventHandlers.has(e)||this.eventHandlers.set(e,new Set),this.eventHandlers.get(e).add(t)}off(e,t){let r=this.eventHandlers.get(e);r&&r.delete(t)}emit(e,t){let r=this.eventHandlers.get(e);r&&r.forEach(i=>i(t))}handleMessage(e){if(!this.isValidOrigin(e.origin))return;let{type:t,data:r}=e.data||{};switch(t){case"RECUR_ELEMENTS_READY":this.sessionId=r.sessionId,this.timestamp=r.timestamp,this.creditToken=r.creditToken,this.sdkToken=r.sdkToken,this.emit("ready",r);break;case"RECUR_CARD_VALID":this.emit("change",{valid:r.valid});break;case"RECUR_CARD_TOKENIZED":this.cardToken=r.cardToken,this.cardTimestamp=r.timestamp,this.emit("tokenized",r);break;case"RECUR_PAYMENT_ERROR":this.emit("error",r);break;case"RECUR_RESIZE":this.iframe&&r?.height&&(this.iframe.style.height=`${r.height}px`);break}}isValidOrigin(e){try{let t=new URL(this.embedUrl).origin;return e===t}catch{return!1}}async tokenize(){if(!this.iframe||!this.iframe.contentWindow)throw new Error("Elements not mounted");let e=new URL(this.embedUrl).origin;return this.iframe.contentWindow.postMessage({type:"RECUR_TOKENIZE"},e),new Promise((t,r)=>{let i=setTimeout(()=>{r(new Error("Tokenization timeout"))},6e4),s=n=>{clearTimeout(i),this.off("tokenized",s),t(n)},a=n=>{clearTimeout(i),this.off("error",a),r(new Error(n.message||"Tokenization failed"))};this.on("tokenized",s),this.on("error",a)})}async confirmPayment(e){if(!this.sessionId||!this.cardToken)throw new Error("Card not tokenized. Call tokenize() first.");let t=await fetch(`${this.baseUrl}/api/v1/elements/confirm`,{method:"POST",headers:{"Content-Type":"application/json","X-Recur-Publishable-Key":this.publishableKey},body:JSON.stringify({sessionId:this.sessionId,productId:e.productId,email:e.email,name:e.name,phone:e.phone,externalCustomerId:e.externalCustomerId,metadata:e.metadata,successUrl:e.successUrl,cancelUrl:e.cancelUrl})}),r=await t.json();return t.ok?r:{status:"failed",message:r.error||r.message||"Payment failed",canRetry:r.canRetry??!0}}getDefaultBaseUrl(){if(typeof window>"u")return"https://app.recur.tw";let e=window.location.hostname;return e==="localhost"||e.includes(".test")||e.includes(".local")||e==="127.0.0.1"?`${window.location.protocol}//${e}:3000`:"https://app.recur.tw"}getDefaultEmbedUrl(){if(typeof window>"u")return"https://embed.recur.tw";let e=window.location.hostname;return e==="localhost"||e.includes(".test")||e.includes(".local")||e==="127.0.0.1"?`${window.location.protocol}//${e}:3002`:"https://embed.recur.tw"}destroy(){this.iframe&&this.iframe.parentNode&&this.iframe.parentNode.removeChild(this.iframe),this.iframe=null,this.container=null,this.sessionId=null,this.timestamp=null,this.creditToken=null,this.sdkToken=null,this.cardToken=null,this.cardTimestamp=null,this.eventHandlers.clear(),window.removeEventListener("message",this.handleMessage.bind(this))}};function j(o){return new w(o)}var Te="https://vendor.payuni.com.tw/sdk/uni-payment.js",Ie="https://sandbox-vendor.payuni.com.tw/sdk/uni-payment.js",ue=!1,N=!1,S=null;async function me(o=!1){return ue&&window.UniPayment?Promise.resolve():(N&&S||(N=!0,S=new Promise((e,t)=>{let r=document.createElement("script");r.src=o?Ie:Te,r.async=!0,r.onload=()=>{ue=!0,N=!1,e()},r.onerror=()=>{N=!1,S=null,t(new Error("Failed to load PAYUNi SDK"))},document.head.appendChild(r)})),S)}var C=class{constructor(e){c(this,"core");c(this,"currentModal",null);c(this,"currentIframe",null);c(this,"currentModalOverlay",null);this.core=new H(e)}async fetchProducts(e){return await this.core.fetchProducts(e)}async fetchPlans(){return await this.core.fetchPlans()}async createEmbeddedCheckout(e){let t=this.core.getConfig();await new $(t,e).render()}async redirectToCheckout(e){let t=await this.createCheckoutSession(e);window.location.href=t.url}async createCheckoutSession(e){let t=this.getBaseUrl();if(!e.productId&&!e.productSlug)throw new Error("Either productId or productSlug is required");let r={};e.productId&&(r.productId=e.productId),e.productSlug&&(r.productSlug=e.productSlug),e.customerEmail&&(r.customerEmail=e.customerEmail),e.customerName&&(r.customerName=e.customerName),e.externalCustomerId&&(r.externalCustomerId=e.externalCustomerId),e.successUrl&&(r.successUrl=e.successUrl),e.cancelUrl&&(r.cancelUrl=e.cancelUrl);let i=this.core.getConfig(),s=await fetch(`${t}/v1/checkouts`,{method:"POST",headers:{"Content-Type":"application/json","X-Recur-Publishable-Key":i.publishableKey},body:JSON.stringify(r)});if(!s.ok){let l=await s.json().catch(()=>({}));throw new Error(l.error?.message||l.error||"Failed to create checkout session")}let a=await s.json(),n=g(a);return{id:n.checkout.id,url:n.url,expiresAt:n.checkout.expiresAt}}async checkout(e){let t=this.core.getConfig(),r=null,i=e.productId||e.planId,s=e.productSlug;try{if(console.log("[Recur SDK] Starting checkout flow...",{productId:i,productSlug:s,mode:e.mode}),!i&&!s)throw new Error("Either productId or productSlug is required");if(!t.publishableKey)throw new Error("publishableKey is required");
|
|
1529
|
+
`.replace(/\s+/g," ").trim(),this.iframe.allow="payment *",this.iframe.setAttribute("title","Secure payment input frame"),this.iframe.setAttribute("role","presentation"),this.iframe.setAttribute("scrolling","no"),this.iframe.setAttribute("frameBorder","0"),this.container.innerHTML="",this.container.appendChild(this.iframe),new Promise((r,i)=>{let s=setTimeout(()=>{i(new Error("Elements initialization timeout"))},3e4),a=()=>{clearTimeout(s),this.off("ready",a),r()},n=l=>{clearTimeout(s),this.off("error",n),i(new Error(l.message||"Elements initialization failed"))};this.on("ready",a),this.on("error",n)})}on(e,t){this.eventHandlers.has(e)||this.eventHandlers.set(e,new Set),this.eventHandlers.get(e).add(t)}off(e,t){let r=this.eventHandlers.get(e);r&&r.delete(t)}emit(e,t){let r=this.eventHandlers.get(e);r&&r.forEach(i=>i(t))}handleMessage(e){if(!this.isValidOrigin(e.origin))return;let{type:t,data:r}=e.data||{};switch(t){case"RECUR_ELEMENTS_READY":this.sessionId=r.sessionId,this.timestamp=r.timestamp,this.creditToken=r.creditToken,this.sdkToken=r.sdkToken,this.emit("ready",r);break;case"RECUR_CARD_VALID":this.emit("change",{valid:r.valid});break;case"RECUR_CARD_TOKENIZED":this.cardToken=r.cardToken,this.cardTimestamp=r.timestamp,this.emit("tokenized",r);break;case"RECUR_PAYMENT_ERROR":this.emit("error",r);break;case"RECUR_RESIZE":this.iframe&&r?.height&&(this.iframe.style.height=`${r.height}px`);break}}isValidOrigin(e){try{let t=new URL(this.embedUrl).origin;return e===t}catch{return!1}}async tokenize(){if(!this.iframe||!this.iframe.contentWindow)throw new Error("Elements not mounted");let e=new URL(this.embedUrl).origin;return this.iframe.contentWindow.postMessage({type:"RECUR_TOKENIZE"},e),new Promise((t,r)=>{let i=setTimeout(()=>{r(new Error("Tokenization timeout"))},6e4),s=n=>{clearTimeout(i),this.off("tokenized",s),t(n)},a=n=>{clearTimeout(i),this.off("error",a),r(new Error(n.message||"Tokenization failed"))};this.on("tokenized",s),this.on("error",a)})}async confirmPayment(e){if(!this.sessionId||!this.cardToken)throw new Error("Card not tokenized. Call tokenize() first.");let t=await fetch(`${this.baseUrl}/api/v1/elements/confirm`,{method:"POST",headers:{"Content-Type":"application/json","X-Recur-Publishable-Key":this.publishableKey},body:JSON.stringify({sessionId:this.sessionId,productId:e.productId,email:e.email,name:e.name,phone:e.phone,externalCustomerId:e.externalCustomerId,metadata:e.metadata,successUrl:e.successUrl,cancelUrl:e.cancelUrl})}),r=await t.json();return t.ok?r:{status:"failed",message:r.error||r.message||"Payment failed",canRetry:r.canRetry??!0}}getDefaultBaseUrl(){if(typeof window>"u")return"https://app.recur.tw";let e=window.location.hostname;return e==="localhost"||e.includes(".test")||e.includes(".local")||e==="127.0.0.1"?`${window.location.protocol}//${e}:3000`:"https://app.recur.tw"}getDefaultEmbedUrl(){if(typeof window>"u")return"https://embed.recur.tw";let e=window.location.hostname;return e==="localhost"||e.includes(".test")||e.includes(".local")||e==="127.0.0.1"?`${window.location.protocol}//${e}:3002`:"https://embed.recur.tw"}destroy(){this.iframe&&this.iframe.parentNode&&this.iframe.parentNode.removeChild(this.iframe),this.iframe=null,this.container=null,this.sessionId=null,this.timestamp=null,this.creditToken=null,this.sdkToken=null,this.cardToken=null,this.cardTimestamp=null,this.eventHandlers.clear(),window.removeEventListener("message",this.handleMessage.bind(this))}};function j(o){return new w(o)}var Te="https://vendor.payuni.com.tw/sdk/uni-payment.js",Ie="https://sandbox-vendor.payuni.com.tw/sdk/uni-payment.js",ue=!1,N=!1,S=null;async function me(o=!1){return ue&&window.UniPayment?Promise.resolve():(N&&S||(N=!0,S=new Promise((e,t)=>{let r=document.createElement("script");r.src=o?Ie:Te,r.async=!0,r.onload=()=>{ue=!0,N=!1,e()},r.onerror=()=>{N=!1,S=null,t(new Error("Failed to load PAYUNi SDK"))},document.head.appendChild(r)})),S)}var C=class{constructor(e){c(this,"core");c(this,"currentModal",null);c(this,"currentIframe",null);c(this,"currentModalOverlay",null);this.core=new H(e)}async fetchProducts(e){return await this.core.fetchProducts(e)}async fetchPlans(){return await this.core.fetchPlans()}async createEmbeddedCheckout(e){let t=this.core.getConfig();await new $(t,e).render()}async redirectToCheckout(e){let t=await this.createCheckoutSession(e);window.location.href=t.url}async createCheckoutSession(e){let t=this.getBaseUrl();if(!e.productId&&!e.productSlug)throw new Error("Either productId or productSlug is required");let r={};e.productId&&(r.productId=e.productId),e.productSlug&&(r.productSlug=e.productSlug),e.customerEmail&&(r.customerEmail=e.customerEmail),e.customerName&&(r.customerName=e.customerName),e.externalCustomerId&&(r.externalCustomerId=e.externalCustomerId),e.successUrl&&(r.successUrl=e.successUrl),e.cancelUrl&&(r.cancelUrl=e.cancelUrl);let i=this.core.getConfig(),s=await fetch(`${t}/v1/checkouts`,{method:"POST",headers:{"Content-Type":"application/json","X-Recur-Publishable-Key":i.publishableKey},body:JSON.stringify(r)});if(!s.ok){let l=await s.json().catch(()=>({}));throw new Error(l.error?.message||l.error||"Failed to create checkout session")}let a=await s.json(),n=g(a);return{id:n.checkout.id,url:n.url,expiresAt:n.checkout.expiresAt}}async checkout(e){let t=this.core.getConfig(),r=null,i=e.productId||e.planId,s=e.productSlug;try{if(console.log("[Recur SDK] Starting checkout flow...",{productId:i,productSlug:s,mode:e.mode}),!i&&!s)throw new Error("Either productId or productSlug is required");if(!t.publishableKey)throw new Error("publishableKey is required");let a=this.getBaseUrl();console.log("[Recur SDK] Base URL:",a);let n={"Content-Type":"application/json","X-Recur-Publishable-Key":t.publishableKey},l=e.mode||"modal",d=null;if(l==="modal"){let m=this.createModalWithSkeleton(e.onClose);r=m.overlay,d=m.container}else if(l==="iframe"){if(d=this.getEmbeddedContainer(e.container),!d)throw new Error("Container is required for iframe mode");d.innerHTML="";let m=document.createElement("recur-payment-form-skeleton");d.appendChild(m)}console.log("[Recur SDK] Step 1: Creating checkout session...");let v={customerName:e.customerName,customerEmail:e.customerEmail};i&&(v.productId=i),s&&(v.productSlug=s),e.externalCustomerId&&(v.externalCustomerId=e.externalCustomerId);let B=await fetch(`${a}/v1/checkouts`,{method:"POST",headers:n,body:JSON.stringify(v)});if(!B.ok){let m=await B.json().catch(()=>({}));console.error("[Recur SDK] Failed to create checkout:",m);let O=m.details||m.error||"Failed to create checkout";throw new Error(O)}let he=await B.json(),u=g(he);if(console.log("[Recur SDK] Checkout created successfully:",u),e.onSuccess?.(u),l==="redirect"){let m=`https://checkout.recur.tw/${u.checkout.id}`;console.log("[Recur SDK] Redirecting to hosted checkout:",m),window.location.href=m;return}if(console.log("[Recur SDK] Step 2: Extracting SDK token..."),!u.sdkToken)throw new Error("SDK token missing from checkout response");console.log("[Recur SDK] Step 3: Loading PAYUNi SDK...");let q=!0;if(await me(q),console.log("[Recur SDK] PAYUNi SDK loaded successfully"),!window.UniPayment)throw new Error("PAYUNi SDK failed to load");if(console.log("[Recur SDK] Step 4: Rendering payment form..."),!d)throw new Error("Payment container not available");d.innerHTML="";let h=document.createElement("recur-payment-form");if(h.setAttribute("container-id",d.id||"recur-payment-container"),e.customerName&&h.setAttribute("customer-name",e.customerName),e.customerEmail&&h.setAttribute("customer-email",e.customerEmail),u.plan?.name&&h.setAttribute("plan-name",u.plan.name),u.checkout?.amount&&h.setAttribute("amount",u.checkout.amount.toString()),u.plan?.billingPeriod&&h.setAttribute("billing-period",u.plan.billingPeriod),h.setAttribute("custom-styles",`
|
|
1530
1530
|
.form-input-focus {
|
|
1531
1531
|
border-color: var(--ring, hsl(215 16% 47%)) !important;
|
|
1532
1532
|
outline: 0 !important;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "recur-tw",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.3",
|
|
4
4
|
"description": "React & Vanilla JS SDK for embedding subscription checkout flows (Taiwan / PAYUNi)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"private": false,
|
|
@@ -28,14 +28,6 @@
|
|
|
28
28
|
"url": "https://github.com/kaikhq/recur.tw/issues"
|
|
29
29
|
},
|
|
30
30
|
"homepage": "https://github.com/kaikhq/recur.tw/tree/main/packages/recur-sdk#readme",
|
|
31
|
-
"scripts": {
|
|
32
|
-
"build": "tsup",
|
|
33
|
-
"dev": "tsup --watch",
|
|
34
|
-
"lint": "eslint .",
|
|
35
|
-
"lint:fix": "eslint . --fix",
|
|
36
|
-
"type-check": "tsc --noEmit",
|
|
37
|
-
"examples": "npx http-server examples -p 8080 -o"
|
|
38
|
-
},
|
|
39
31
|
"main": "./dist/index.js",
|
|
40
32
|
"module": "./dist/index.js",
|
|
41
33
|
"types": "./dist/index.d.ts",
|
|
@@ -82,7 +74,6 @@
|
|
|
82
74
|
"@types/node": "^20.19.9",
|
|
83
75
|
"@types/react": "^19.1.9",
|
|
84
76
|
"@types/react-dom": "^19.1.7",
|
|
85
|
-
"@workspace/typescript-config": "workspace:*",
|
|
86
77
|
"autoprefixer": "^10.4.22",
|
|
87
78
|
"eslint": "^9",
|
|
88
79
|
"eslint-plugin-react": "^7.37.5",
|
|
@@ -93,6 +84,15 @@
|
|
|
93
84
|
"tailwindcss": "^4.1.11",
|
|
94
85
|
"tsup": "^8.3.5",
|
|
95
86
|
"typescript": "^5.9.2",
|
|
96
|
-
"typescript-eslint": "^8.47.0"
|
|
87
|
+
"typescript-eslint": "^8.47.0",
|
|
88
|
+
"@workspace/typescript-config": "0.0.0"
|
|
89
|
+
},
|
|
90
|
+
"scripts": {
|
|
91
|
+
"build": "tsup",
|
|
92
|
+
"dev": "tsup --watch",
|
|
93
|
+
"lint": "eslint .",
|
|
94
|
+
"lint:fix": "eslint . --fix",
|
|
95
|
+
"type-check": "tsc --noEmit",
|
|
96
|
+
"examples": "npx http-server examples -p 8080 -o"
|
|
97
97
|
}
|
|
98
|
-
}
|
|
98
|
+
}
|