@funnelfox/billing 0.1.0 → 0.1.1
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.
|
@@ -139,7 +139,7 @@ class NetworkError extends FunnefoxSDKError {
|
|
|
139
139
|
/**
|
|
140
140
|
* SDK version
|
|
141
141
|
*/
|
|
142
|
-
const SDK_VERSION = '0.1.
|
|
142
|
+
const SDK_VERSION = '0.1.1';
|
|
143
143
|
|
|
144
144
|
/**
|
|
145
145
|
* Default configuration values
|
|
@@ -1169,6 +1169,10 @@ class CheckoutInstance extends EventEmitter {
|
|
|
1169
1169
|
* @private
|
|
1170
1170
|
*/
|
|
1171
1171
|
async _processPaymentResult(result, primerHandler) {
|
|
1172
|
+
// Update order ID if it changed
|
|
1173
|
+
if (result.orderId) {
|
|
1174
|
+
this.orderId = result.orderId;
|
|
1175
|
+
}
|
|
1172
1176
|
switch (result.type) {
|
|
1173
1177
|
case 'success':
|
|
1174
1178
|
this._setState('completed');
|
|
@@ -135,7 +135,7 @@ class NetworkError extends FunnefoxSDKError {
|
|
|
135
135
|
/**
|
|
136
136
|
* SDK version
|
|
137
137
|
*/
|
|
138
|
-
const SDK_VERSION = '0.1.
|
|
138
|
+
const SDK_VERSION = '0.1.1';
|
|
139
139
|
|
|
140
140
|
/**
|
|
141
141
|
* Default configuration values
|
|
@@ -1165,6 +1165,10 @@ class CheckoutInstance extends EventEmitter {
|
|
|
1165
1165
|
* @private
|
|
1166
1166
|
*/
|
|
1167
1167
|
async _processPaymentResult(result, primerHandler) {
|
|
1168
|
+
// Update order ID if it changed
|
|
1169
|
+
if (result.orderId) {
|
|
1170
|
+
this.orderId = result.orderId;
|
|
1171
|
+
}
|
|
1168
1172
|
switch (result.type) {
|
|
1169
1173
|
case 'success':
|
|
1170
1174
|
this._setState('completed');
|
|
@@ -141,7 +141,7 @@
|
|
|
141
141
|
/**
|
|
142
142
|
* SDK version
|
|
143
143
|
*/
|
|
144
|
-
const SDK_VERSION = '0.1.
|
|
144
|
+
const SDK_VERSION = '0.1.1';
|
|
145
145
|
|
|
146
146
|
/**
|
|
147
147
|
* Default configuration values
|
|
@@ -1171,6 +1171,10 @@
|
|
|
1171
1171
|
* @private
|
|
1172
1172
|
*/
|
|
1173
1173
|
async _processPaymentResult(result, primerHandler) {
|
|
1174
|
+
// Update order ID if it changed
|
|
1175
|
+
if (result.orderId) {
|
|
1176
|
+
this.orderId = result.orderId;
|
|
1177
|
+
}
|
|
1174
1178
|
switch (result.type) {
|
|
1175
1179
|
case 'success':
|
|
1176
1180
|
this._setState('completed');
|
|
@@ -1 +1 @@
|
|
|
1
|
-
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).FunnelfoxSDK={})}(this,function(e){"use strict";class t extends Error{constructor(e,r="SDK_ERROR",s=null){super(e),this.name="FunnefoxSDKError",this.code=r,this.details=s,Error.captureStackTrace&&Error.captureStackTrace(this,t)}}class r extends t{constructor(e,t,r=null){super(`Invalid ${e}: ${t}`,"VALIDATION_ERROR"),this.name="ValidationError",this.field=e,this.value=r}}class s extends t{constructor(e,t=null,r={}){super(e,r.errorCode||"API_ERROR"),this.name="APIError",this.statusCode=t,this.errorCode=r.errorCode||null,this.errorType=r.errorType||null,this.requestId=r.requestId||null,this.response=r.response||null}}class n extends t{constructor(e,t=null){super(e,"PRIMER_ERROR"),this.name="PrimerError",this.primerError=t}}class i extends t{constructor(e,t=null){super(e,"CHECKOUT_ERROR"),this.name="CheckoutError",this.phase=t}}class o extends t{constructor(e,t=null){super(e,"NETWORK_ERROR"),this.name="NetworkError",this.originalError=t}}const a={BASE_URL:"https://billing.funnelfox.com",REGION:"default",SANDBOX:!1,REQUEST_TIMEOUT:3e4,RETRY_ATTEMPTS:3,RETRY_BASE_DELAY:1e3};class c{constructor(){this._events=new Map}on(e,t){if("function"!=typeof t)throw new Error("Event handler must be a function");return this._events.has(e)||this._events.set(e,[]),this._events.get(e).push(t),this}once(e,t){if("function"!=typeof t)throw new Error("Event handler must be a function");const r=(...s)=>{this.off(e,r),t.apply(this,s)};return this.on(e,r)}off(e,t=null){if(!this._events.has(e))return this;if(null===t)return this._events.delete(e),this;const r=this._events.get(e),s=r.indexOf(t);return-1!==s&&(r.splice(s,1),0===r.length&&this._events.delete(e)),this}emit(e,...t){if(!this._events.has(e))return!1;const r=this._events.get(e).slice();for(const s of r)try{s.apply(this,t)}catch(t){console.warn(`Error in event handler for "${e}":`,t)}return!0}listenerCount(e){return this._events.has(e)?this._events.get(e).length:0}eventNames(){return Array.from(this._events.keys())}removeAllListeners(){return this._events.clear(),this}listeners(e){return this._events.has(e)?this._events.get(e).slice():[]}}function u(...e){const t={};for(const r of e)if(r&&"object"==typeof r)for(const e in r)Object.prototype.hasOwnProperty.call(r,e)&&("object"!=typeof r[e]||Array.isArray(r[e])||null===r[e]?t[e]=r[e]:t[e]=u(t[e]||{},r[e]));return t}function h(e){return new Promise(t=>setTimeout(t,e))}class l{constructor(){this.currentCheckout=null,this.isInitialized=!1}isPrimerAvailable(){return"undefined"!=typeof window&&window.Primer&&"function"==typeof window.Primer.showUniversalCheckout}ensurePrimerAvailable(){if(!this.isPrimerAvailable())throw new n("Primer SDK not found. Please include the Primer SDK script before initializing FunnefoxSDK.")}async showUniversalCheckout(e,t){this.ensurePrimerAvailable();const{onTokenizeSuccess:r,onResumeSuccess:s,container:i,...o}=t,a=u({clientToken:e,container:i,paymentHandling:"MANUAL",apiVersion:"2.4",paypal:{buttonColor:"blue",paymentFlow:"PREFER_VAULT"}},o);a.onTokenizeSuccess=this._wrapTokenizeHandler(r),a.onResumeSuccess=this._wrapResumeHandler(s);try{const t=await window.Primer.showUniversalCheckout(e,a);return this.currentCheckout=t,this.isInitialized=!0,t}catch(e){throw new n("Failed to initialize Primer checkout",e)}}_wrapTokenizeHandler(e){return async(t,r)=>{try{await e(t,r)}catch(e){console.error("Error in tokenize handler:",e),r.handleFailure("Payment processing failed. Please try again.")}}}_wrapResumeHandler(e){return async(t,r)=>{try{await e(t,r)}catch(e){console.error("Error in resume handler:",e),r.handleFailure("Payment processing failed. Please try again.")}}}async updateClientToken(e){if(!this.currentCheckout)throw new n("No active checkout to update");try{throw new n("Client token updates require checkout recreation")}catch(e){throw new n("Failed to update client token",e)}}async destroy(){if(this.currentCheckout)try{if("function"==typeof this.currentCheckout.destroy)await this.currentCheckout.destroy();else{const e=document.querySelector(this.currentCheckout.container);e&&(e.innerHTML="")}}catch(e){console.warn("Error destroying Primer checkout:",e)}finally{this.currentCheckout=null,this.isInitialized=!1}}createHandlers(e){return{handleSuccess:()=>{e.onSuccess&&e.onSuccess()},handleFailure:t=>{e.onError&&e.onError(new Error(t))},continueWithNewClientToken:t=>{e.onActionRequired&&e.onActionRequired(t)}}}getCurrentCheckout(){return this.currentCheckout}isActive(){return this.isInitialized&&null!==this.currentCheckout}validateContainer(e){const t=document.querySelector(e);if(!t)throw new n(`Checkout container not found: ${e}`);return"none"===window.getComputedStyle(t).display&&console.warn("Checkout container is hidden, this may cause display issues"),t}}var d=Object.freeze({__proto__:null,default:l});function p(e,t){var s;if(0===(null==(s=e)?"":String(s).trim()).length)throw new r(t,"must be a non-empty string",e);return!0}class y{constructor(e){this.baseUrl=e.baseUrl.replace(/\/$/,""),this.orgId=e.orgId,this.timeout=e.timeout||3e4,this.retryAttempts=e.retryAttempts||3}async request(e,t={}){const r=`${this.baseUrl}/${this.orgId}${e}`,s={method:"GET",headers:{"Content-Type":"application/json",...t.headers},...t};try{return await async function(e,t=3,r=1e3){let s;for(let n=1;n<=t;n++)try{return await e()}catch(e){if(s=e,n===t)throw s;const i=r*Math.pow(2,n-1);await h(i)}throw s}(async()=>await function(e,t,r="Operation timed out"){const s=new Promise((e,s)=>{setTimeout(()=>s(new Error(r)),t)});return Promise.race([e,s])}(this._makeRequest(r,s),this.timeout,"Request timed out"),this.retryAttempts)}catch(e){if("APIError"===e.name)throw e;throw new o("Network request failed",e)}}async _makeRequest(e,t){let r,n;try{r=await fetch(e,t)}catch(e){throw new o("Network request failed",e)}try{n=await r.json()}catch(e){throw new s("Invalid JSON response",r.status,{})}if(!r.ok){const e=n.message||n.error||`HTTP ${r.status}`;throw new s(e,r.status,{response:n})}return n}async createClientSession(e){const t={region:e.region||"default",integration_type:"primer",pp_ident:e.priceId,external_id:e.externalId,email_address:e.email,client_metadata:e.clientMetadata||{}};return void 0!==e.countryCode&&(t.country_code=e.countryCode),await this.request("/v1/checkout/create_client_session",{method:"POST",body:JSON.stringify(t)})}async updateClientSession(e){const t={order_id:e.orderId,client_token:e.clientToken,pp_ident:e.priceId};return await this.request("/v1/checkout/update_client_session",{method:"POST",body:JSON.stringify(t)})}async createPayment(e){const t={order_id:e.orderId,payment_method_token:e.paymentMethodToken};return await this.request("/v1/checkout/create_payment",{method:"POST",body:JSON.stringify(t)})}async resumePayment(e){const t={order_id:e.orderId,resume_token:e.resumeToken};return await this.request("/v1/checkout/resume_payment",{method:"POST",body:JSON.stringify(t)})}async oneClickPayment(e){const t={external_id:e.externalId,pp_ident:e.priceId,client_metadata:e.clientMetadata||{}};return await this.request("/v1/checkout/one_click",{method:"POST",body:JSON.stringify(t)})}processSessionResponse(e){if("error"===e.status){const t=e.error?.[0];throw new s(t?.msg||"Session creation failed",null,{errorCode:t?.code,errorType:t?.type,requestId:e.req_id,response:e})}const t=e.data||e;return{type:"session_created",orderId:t.order_id,clientToken:t.client_token}}processPaymentResponse(e){if("error"===e.status){const t=e.error?.[0];throw new s(t?.msg||"Payment request failed",null,{errorCode:t?.code,errorType:t?.type,requestId:e.req_id,response:e})}const t=e.data||e;if(t.action_required_token)return{type:"action_required",orderId:t.order_id,clientToken:t.action_required_token};if(t.checkout_status)switch(t.checkout_status){case"succeeded":return{type:"success",orderId:t.order_id,status:"succeeded",transactionId:t.transaction_id};case"failed":throw new s(t.failed_message_for_user||"Payment failed",null,t);case"cancelled":throw new s("Payment was cancelled by user",null,t);case"processing":return{type:"processing",orderId:t.order_id,status:"processing"};default:throw new s(`Unhandled checkout status: ${t.checkout_status}`,null,t)}throw new s("Invalid payment response format",null,t)}processResponse(e){const t=e.data||e;if(t.client_token&&t.order_id&&!t.checkout_status)return this.processSessionResponse(e);if(t.checkout_status||t.action_required_token)return this.processPaymentResponse(e);throw new s("Unknown response format",null,e)}}class m extends c{constructor(e){super(),this.id=function(e=""){return`${e}${Date.now().toString(36)}_${Math.random().toString(36).substr(2,5)}`}("checkout_"),this.orgId=e.orgId,this.baseUrl=e.baseUrl,this.region=e.region,this.checkoutConfig={...e.checkoutConfig},this.callbacks={onSuccess:this.checkoutConfig.onSuccess,onError:this.checkoutConfig.onError,onStatusChange:this.checkoutConfig.onStatusChange,onDestroy:this.checkoutConfig.onDestroy},delete this.checkoutConfig.onSuccess,delete this.checkoutConfig.onError,delete this.checkoutConfig.onStatusChange,delete this.checkoutConfig.onDestroy,this.state="initializing",this.orderId=null,this.clientToken=null,this.primerWrapper=new l,this.isDestroyed=!1,this._setupCallbackBridges(),this._handleTokenizeSuccess=this._handleTokenizeSuccess.bind(this),this._handleResumeSuccess=this._handleResumeSuccess.bind(this)}_setupCallbackBridges(){this.callbacks.onSuccess&&this.on("success",this.callbacks.onSuccess),this.callbacks.onError&&this.on("error",this.callbacks.onError),this.callbacks.onStatusChange&&this.on("status-change",this.callbacks.onStatusChange),this.callbacks.onDestroy&&this.on("destroy",this.callbacks.onDestroy)}async initialize(){try{this._setState("initializing"),function(e){p(e,"container");const t=document.querySelector(e);if(!t)throw new r("container",`element not found: ${e}`,e)}(this.checkoutConfig.container),this.apiClient=new y({baseUrl:this.baseUrl||a.BASE_URL,orgId:this.orgId,timeout:a.REQUEST_TIMEOUT,retryAttempts:a.RETRY_ATTEMPTS});const e=await this.apiClient.createClientSession({priceId:this.checkoutConfig.priceId,externalId:this.checkoutConfig.customer.externalId,email:this.checkoutConfig.customer.email,region:this.region||a.REGION,clientMetadata:this.checkoutConfig.clientMetadata,countryCode:this.checkoutConfig.customer.countryCode}),t=this.apiClient.processSessionResponse(e);return this.orderId=t.orderId,this.clientToken=t.clientToken,await this._initializePrimerCheckout(),this._setState("ready"),this}catch(e){throw this._setState("error"),this.emit("error",e),e}}async _initializePrimerCheckout(){const e={container:this.checkoutConfig.container,onTokenizeSuccess:this._handleTokenizeSuccess,onResumeSuccess:this._handleResumeSuccess,...this.checkoutConfig.universalCheckoutOptions||{}};await this.primerWrapper.showUniversalCheckout(this.clientToken,e)}async _handleTokenizeSuccess(e,t){try{this._setState("processing");const r=await this.apiClient.createPayment({orderId:this.orderId,paymentMethodToken:e.token}),s=this.apiClient.processPaymentResponse(r);await this._processPaymentResult(s,t)}catch(e){this._setState("error"),this.emit("error",e),t.handleFailure(e.message||"Payment processing failed")}}async _handleResumeSuccess(e,t){try{this._setState("processing");const r=await this.apiClient.resumePayment({orderId:this.orderId,resumeToken:e.resumeToken}),s=this.apiClient.processPaymentResponse(r);await this._processPaymentResult(s,t)}catch(e){this._setState("error"),this.emit("error",e),t.handleFailure(e.message||"Payment processing failed")}}async _processPaymentResult(e,t){switch(e.type){case"success":this._setState("completed"),this.emit("success",{orderId:e.orderId,status:e.status,transactionId:e.transactionId,metadata:e.metadata}),t.handleSuccess();break;case"action_required":this._setState("action_required"),this.clientToken=e.clientToken,t.continueWithNewClientToken(e.clientToken);break;case"processing":this._setState("processing"),setTimeout(()=>{t.handleFailure("Payment is still processing. Please check back later.")},3e4);break;default:throw new i(`Unknown payment result type: ${e.type}`)}}async updatePrice(e){if(this._ensureNotDestroyed(),p(e,"priceId"),"processing"===this.state)throw new i("Cannot update price while payment is processing");try{this._setState("updating"),await this.apiClient.updateClientSession({orderId:this.orderId,clientToken:this.clientToken,priceId:e}),this.checkoutConfig.priceId=e,this._setState("ready"),this.emit("status-change","price-updated")}catch(e){throw this._setState("error"),this.emit("error",e),e}}getStatus(){return{id:this.id,state:this.state,orderId:this.orderId,priceId:this.checkoutConfig.priceId,isDestroyed:this.isDestroyed}}async destroy(){if(!this.isDestroyed)try{await this.primerWrapper.destroy(),this._setState("destroyed"),this.orderId=null,this.clientToken=null,this.isDestroyed=!0,this.emit("destroy"),this.removeAllListeners()}catch(e){console.warn("Error during checkout cleanup:",e)}}_setState(e){if(this.state!==e){const t=this.state;this.state=e,this.emit("status-change",e,t)}}_ensureNotDestroyed(){if(this.isDestroyed)throw new i("Checkout instance has been destroyed")}getContainer(){return document.querySelector(this.checkoutConfig.container)}isInState(e){return this.state===e}isReady(){return"ready"===this.state&&!this.isDestroyed}isProcessing(){return["processing","action_required"].includes(this.state)}}let _=null;function f(e){_=e}function k(e,t){const{orgId:r,apiConfig:s}=e,n=r||_?.orgId;if(!n)throw new Error(`orgId is required. Pass it to ${t}() or call configure() first.`);return{orgId:n,baseUrl:s?.baseUrl||_?.baseUrl||a.BASE_URL,region:s?.region||_?.region||a.REGION}}async function w(e){const{...t}=e;(new l).ensurePrimerAvailable();const r=k(e,"createCheckout"),s=new m({...r,checkoutConfig:t});return await s.initialize(),s}async function R(e,t){const{default:r}=await Promise.resolve().then(function(){return d}),s=new r;return await s.showUniversalCheckout(e,t)}async function g(e){const{priceId:t,externalId:r,email:s,clientMetadata:n,countryCode:i}=e,o=k(e,"createClientSession"),c=new y({baseUrl:o.baseUrl,orgId:o.orgId,timeout:a.REQUEST_TIMEOUT,retryAttempts:a.RETRY_ATTEMPTS}),u=await c.createClientSession({priceId:t,externalId:r,email:s,region:o.region,clientMetadata:n,countryCode:i});return c.processSessionResponse(u)}const C={configure:f,createCheckout:w,showUniversalCheckout:R,createClientSession:g};"undefined"!=typeof window&&(window.Billing=C),e.APIError=s,e.Billing=C,e.CHECKOUT_STATES={INITIALIZING:"initializing",READY:"ready",PROCESSING:"processing",ACTION_REQUIRED:"action_required",UPDATING:"updating",COMPLETED:"completed",ERROR:"error",DESTROYED:"destroyed"},e.CheckoutError=i,e.ConfigurationError=class extends t{constructor(e){super(e,"CONFIGURATION_ERROR"),this.name="ConfigurationError"}},e.DEFAULTS=a,e.ERROR_CODES={SDK_ERROR:"SDK_ERROR",VALIDATION_ERROR:"VALIDATION_ERROR",API_ERROR:"API_ERROR",PRIMER_ERROR:"PRIMER_ERROR",CHECKOUT_ERROR:"CHECKOUT_ERROR",CONFIGURATION_ERROR:"CONFIGURATION_ERROR",NETWORK_ERROR:"NETWORK_ERROR"},e.EVENTS={SUCCESS:"success",ERROR:"error",STATUS_CHANGE:"status-change",DESTROY:"destroy"},e.FunnefoxSDKError=t,e.NetworkError=o,e.PrimerError=n,e.SDK_VERSION="0.1.0",e.ValidationError=r,e.configure=f,e.createCheckout=w,e.createClientSession=g,e.default=C,e.showUniversalCheckout=R,Object.defineProperty(e,"__esModule",{value:!0})});
|
|
1
|
+
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).FunnelfoxSDK={})}(this,function(e){"use strict";class t extends Error{constructor(e,r="SDK_ERROR",s=null){super(e),this.name="FunnefoxSDKError",this.code=r,this.details=s,Error.captureStackTrace&&Error.captureStackTrace(this,t)}}class r extends t{constructor(e,t,r=null){super(`Invalid ${e}: ${t}`,"VALIDATION_ERROR"),this.name="ValidationError",this.field=e,this.value=r}}class s extends t{constructor(e,t=null,r={}){super(e,r.errorCode||"API_ERROR"),this.name="APIError",this.statusCode=t,this.errorCode=r.errorCode||null,this.errorType=r.errorType||null,this.requestId=r.requestId||null,this.response=r.response||null}}class n extends t{constructor(e,t=null){super(e,"PRIMER_ERROR"),this.name="PrimerError",this.primerError=t}}class i extends t{constructor(e,t=null){super(e,"CHECKOUT_ERROR"),this.name="CheckoutError",this.phase=t}}class o extends t{constructor(e,t=null){super(e,"NETWORK_ERROR"),this.name="NetworkError",this.originalError=t}}const a={BASE_URL:"https://billing.funnelfox.com",REGION:"default",SANDBOX:!1,REQUEST_TIMEOUT:3e4,RETRY_ATTEMPTS:3,RETRY_BASE_DELAY:1e3};class c{constructor(){this._events=new Map}on(e,t){if("function"!=typeof t)throw new Error("Event handler must be a function");return this._events.has(e)||this._events.set(e,[]),this._events.get(e).push(t),this}once(e,t){if("function"!=typeof t)throw new Error("Event handler must be a function");const r=(...s)=>{this.off(e,r),t.apply(this,s)};return this.on(e,r)}off(e,t=null){if(!this._events.has(e))return this;if(null===t)return this._events.delete(e),this;const r=this._events.get(e),s=r.indexOf(t);return-1!==s&&(r.splice(s,1),0===r.length&&this._events.delete(e)),this}emit(e,...t){if(!this._events.has(e))return!1;const r=this._events.get(e).slice();for(const s of r)try{s.apply(this,t)}catch(t){console.warn(`Error in event handler for "${e}":`,t)}return!0}listenerCount(e){return this._events.has(e)?this._events.get(e).length:0}eventNames(){return Array.from(this._events.keys())}removeAllListeners(){return this._events.clear(),this}listeners(e){return this._events.has(e)?this._events.get(e).slice():[]}}function u(...e){const t={};for(const r of e)if(r&&"object"==typeof r)for(const e in r)Object.prototype.hasOwnProperty.call(r,e)&&("object"!=typeof r[e]||Array.isArray(r[e])||null===r[e]?t[e]=r[e]:t[e]=u(t[e]||{},r[e]));return t}function h(e){return new Promise(t=>setTimeout(t,e))}class l{constructor(){this.currentCheckout=null,this.isInitialized=!1}isPrimerAvailable(){return"undefined"!=typeof window&&window.Primer&&"function"==typeof window.Primer.showUniversalCheckout}ensurePrimerAvailable(){if(!this.isPrimerAvailable())throw new n("Primer SDK not found. Please include the Primer SDK script before initializing FunnefoxSDK.")}async showUniversalCheckout(e,t){this.ensurePrimerAvailable();const{onTokenizeSuccess:r,onResumeSuccess:s,container:i,...o}=t,a=u({clientToken:e,container:i,paymentHandling:"MANUAL",apiVersion:"2.4",paypal:{buttonColor:"blue",paymentFlow:"PREFER_VAULT"}},o);a.onTokenizeSuccess=this._wrapTokenizeHandler(r),a.onResumeSuccess=this._wrapResumeHandler(s);try{const t=await window.Primer.showUniversalCheckout(e,a);return this.currentCheckout=t,this.isInitialized=!0,t}catch(e){throw new n("Failed to initialize Primer checkout",e)}}_wrapTokenizeHandler(e){return async(t,r)=>{try{await e(t,r)}catch(e){console.error("Error in tokenize handler:",e),r.handleFailure("Payment processing failed. Please try again.")}}}_wrapResumeHandler(e){return async(t,r)=>{try{await e(t,r)}catch(e){console.error("Error in resume handler:",e),r.handleFailure("Payment processing failed. Please try again.")}}}async updateClientToken(e){if(!this.currentCheckout)throw new n("No active checkout to update");try{throw new n("Client token updates require checkout recreation")}catch(e){throw new n("Failed to update client token",e)}}async destroy(){if(this.currentCheckout)try{if("function"==typeof this.currentCheckout.destroy)await this.currentCheckout.destroy();else{const e=document.querySelector(this.currentCheckout.container);e&&(e.innerHTML="")}}catch(e){console.warn("Error destroying Primer checkout:",e)}finally{this.currentCheckout=null,this.isInitialized=!1}}createHandlers(e){return{handleSuccess:()=>{e.onSuccess&&e.onSuccess()},handleFailure:t=>{e.onError&&e.onError(new Error(t))},continueWithNewClientToken:t=>{e.onActionRequired&&e.onActionRequired(t)}}}getCurrentCheckout(){return this.currentCheckout}isActive(){return this.isInitialized&&null!==this.currentCheckout}validateContainer(e){const t=document.querySelector(e);if(!t)throw new n(`Checkout container not found: ${e}`);return"none"===window.getComputedStyle(t).display&&console.warn("Checkout container is hidden, this may cause display issues"),t}}var d=Object.freeze({__proto__:null,default:l});function p(e,t){var s;if(0===(null==(s=e)?"":String(s).trim()).length)throw new r(t,"must be a non-empty string",e);return!0}class y{constructor(e){this.baseUrl=e.baseUrl.replace(/\/$/,""),this.orgId=e.orgId,this.timeout=e.timeout||3e4,this.retryAttempts=e.retryAttempts||3}async request(e,t={}){const r=`${this.baseUrl}/${this.orgId}${e}`,s={method:"GET",headers:{"Content-Type":"application/json",...t.headers},...t};try{return await async function(e,t=3,r=1e3){let s;for(let n=1;n<=t;n++)try{return await e()}catch(e){if(s=e,n===t)throw s;const i=r*Math.pow(2,n-1);await h(i)}throw s}(async()=>await function(e,t,r="Operation timed out"){const s=new Promise((e,s)=>{setTimeout(()=>s(new Error(r)),t)});return Promise.race([e,s])}(this._makeRequest(r,s),this.timeout,"Request timed out"),this.retryAttempts)}catch(e){if("APIError"===e.name)throw e;throw new o("Network request failed",e)}}async _makeRequest(e,t){let r,n;try{r=await fetch(e,t)}catch(e){throw new o("Network request failed",e)}try{n=await r.json()}catch(e){throw new s("Invalid JSON response",r.status,{})}if(!r.ok){const e=n.message||n.error||`HTTP ${r.status}`;throw new s(e,r.status,{response:n})}return n}async createClientSession(e){const t={region:e.region||"default",integration_type:"primer",pp_ident:e.priceId,external_id:e.externalId,email_address:e.email,client_metadata:e.clientMetadata||{}};return void 0!==e.countryCode&&(t.country_code=e.countryCode),await this.request("/v1/checkout/create_client_session",{method:"POST",body:JSON.stringify(t)})}async updateClientSession(e){const t={order_id:e.orderId,client_token:e.clientToken,pp_ident:e.priceId};return await this.request("/v1/checkout/update_client_session",{method:"POST",body:JSON.stringify(t)})}async createPayment(e){const t={order_id:e.orderId,payment_method_token:e.paymentMethodToken};return await this.request("/v1/checkout/create_payment",{method:"POST",body:JSON.stringify(t)})}async resumePayment(e){const t={order_id:e.orderId,resume_token:e.resumeToken};return await this.request("/v1/checkout/resume_payment",{method:"POST",body:JSON.stringify(t)})}async oneClickPayment(e){const t={external_id:e.externalId,pp_ident:e.priceId,client_metadata:e.clientMetadata||{}};return await this.request("/v1/checkout/one_click",{method:"POST",body:JSON.stringify(t)})}processSessionResponse(e){if("error"===e.status){const t=e.error?.[0];throw new s(t?.msg||"Session creation failed",null,{errorCode:t?.code,errorType:t?.type,requestId:e.req_id,response:e})}const t=e.data||e;return{type:"session_created",orderId:t.order_id,clientToken:t.client_token}}processPaymentResponse(e){if("error"===e.status){const t=e.error?.[0];throw new s(t?.msg||"Payment request failed",null,{errorCode:t?.code,errorType:t?.type,requestId:e.req_id,response:e})}const t=e.data||e;if(t.action_required_token)return{type:"action_required",orderId:t.order_id,clientToken:t.action_required_token};if(t.checkout_status)switch(t.checkout_status){case"succeeded":return{type:"success",orderId:t.order_id,status:"succeeded",transactionId:t.transaction_id};case"failed":throw new s(t.failed_message_for_user||"Payment failed",null,t);case"cancelled":throw new s("Payment was cancelled by user",null,t);case"processing":return{type:"processing",orderId:t.order_id,status:"processing"};default:throw new s(`Unhandled checkout status: ${t.checkout_status}`,null,t)}throw new s("Invalid payment response format",null,t)}processResponse(e){const t=e.data||e;if(t.client_token&&t.order_id&&!t.checkout_status)return this.processSessionResponse(e);if(t.checkout_status||t.action_required_token)return this.processPaymentResponse(e);throw new s("Unknown response format",null,e)}}class m extends c{constructor(e){super(),this.id=function(e=""){return`${e}${Date.now().toString(36)}_${Math.random().toString(36).substr(2,5)}`}("checkout_"),this.orgId=e.orgId,this.baseUrl=e.baseUrl,this.region=e.region,this.checkoutConfig={...e.checkoutConfig},this.callbacks={onSuccess:this.checkoutConfig.onSuccess,onError:this.checkoutConfig.onError,onStatusChange:this.checkoutConfig.onStatusChange,onDestroy:this.checkoutConfig.onDestroy},delete this.checkoutConfig.onSuccess,delete this.checkoutConfig.onError,delete this.checkoutConfig.onStatusChange,delete this.checkoutConfig.onDestroy,this.state="initializing",this.orderId=null,this.clientToken=null,this.primerWrapper=new l,this.isDestroyed=!1,this._setupCallbackBridges(),this._handleTokenizeSuccess=this._handleTokenizeSuccess.bind(this),this._handleResumeSuccess=this._handleResumeSuccess.bind(this)}_setupCallbackBridges(){this.callbacks.onSuccess&&this.on("success",this.callbacks.onSuccess),this.callbacks.onError&&this.on("error",this.callbacks.onError),this.callbacks.onStatusChange&&this.on("status-change",this.callbacks.onStatusChange),this.callbacks.onDestroy&&this.on("destroy",this.callbacks.onDestroy)}async initialize(){try{this._setState("initializing"),function(e){p(e,"container");const t=document.querySelector(e);if(!t)throw new r("container",`element not found: ${e}`,e)}(this.checkoutConfig.container),this.apiClient=new y({baseUrl:this.baseUrl||a.BASE_URL,orgId:this.orgId,timeout:a.REQUEST_TIMEOUT,retryAttempts:a.RETRY_ATTEMPTS});const e=await this.apiClient.createClientSession({priceId:this.checkoutConfig.priceId,externalId:this.checkoutConfig.customer.externalId,email:this.checkoutConfig.customer.email,region:this.region||a.REGION,clientMetadata:this.checkoutConfig.clientMetadata,countryCode:this.checkoutConfig.customer.countryCode}),t=this.apiClient.processSessionResponse(e);return this.orderId=t.orderId,this.clientToken=t.clientToken,await this._initializePrimerCheckout(),this._setState("ready"),this}catch(e){throw this._setState("error"),this.emit("error",e),e}}async _initializePrimerCheckout(){const e={container:this.checkoutConfig.container,onTokenizeSuccess:this._handleTokenizeSuccess,onResumeSuccess:this._handleResumeSuccess,...this.checkoutConfig.universalCheckoutOptions||{}};await this.primerWrapper.showUniversalCheckout(this.clientToken,e)}async _handleTokenizeSuccess(e,t){try{this._setState("processing");const r=await this.apiClient.createPayment({orderId:this.orderId,paymentMethodToken:e.token}),s=this.apiClient.processPaymentResponse(r);await this._processPaymentResult(s,t)}catch(e){this._setState("error"),this.emit("error",e),t.handleFailure(e.message||"Payment processing failed")}}async _handleResumeSuccess(e,t){try{this._setState("processing");const r=await this.apiClient.resumePayment({orderId:this.orderId,resumeToken:e.resumeToken}),s=this.apiClient.processPaymentResponse(r);await this._processPaymentResult(s,t)}catch(e){this._setState("error"),this.emit("error",e),t.handleFailure(e.message||"Payment processing failed")}}async _processPaymentResult(e,t){switch(e.orderId&&(this.orderId=e.orderId),e.type){case"success":this._setState("completed"),this.emit("success",{orderId:e.orderId,status:e.status,transactionId:e.transactionId,metadata:e.metadata}),t.handleSuccess();break;case"action_required":this._setState("action_required"),this.clientToken=e.clientToken,t.continueWithNewClientToken(e.clientToken);break;case"processing":this._setState("processing"),setTimeout(()=>{t.handleFailure("Payment is still processing. Please check back later.")},3e4);break;default:throw new i(`Unknown payment result type: ${e.type}`)}}async updatePrice(e){if(this._ensureNotDestroyed(),p(e,"priceId"),"processing"===this.state)throw new i("Cannot update price while payment is processing");try{this._setState("updating"),await this.apiClient.updateClientSession({orderId:this.orderId,clientToken:this.clientToken,priceId:e}),this.checkoutConfig.priceId=e,this._setState("ready"),this.emit("status-change","price-updated")}catch(e){throw this._setState("error"),this.emit("error",e),e}}getStatus(){return{id:this.id,state:this.state,orderId:this.orderId,priceId:this.checkoutConfig.priceId,isDestroyed:this.isDestroyed}}async destroy(){if(!this.isDestroyed)try{await this.primerWrapper.destroy(),this._setState("destroyed"),this.orderId=null,this.clientToken=null,this.isDestroyed=!0,this.emit("destroy"),this.removeAllListeners()}catch(e){console.warn("Error during checkout cleanup:",e)}}_setState(e){if(this.state!==e){const t=this.state;this.state=e,this.emit("status-change",e,t)}}_ensureNotDestroyed(){if(this.isDestroyed)throw new i("Checkout instance has been destroyed")}getContainer(){return document.querySelector(this.checkoutConfig.container)}isInState(e){return this.state===e}isReady(){return"ready"===this.state&&!this.isDestroyed}isProcessing(){return["processing","action_required"].includes(this.state)}}let _=null;function f(e){_=e}function k(e,t){const{orgId:r,apiConfig:s}=e,n=r||_?.orgId;if(!n)throw new Error(`orgId is required. Pass it to ${t}() or call configure() first.`);return{orgId:n,baseUrl:s?.baseUrl||_?.baseUrl||a.BASE_URL,region:s?.region||_?.region||a.REGION}}async function w(e){const{...t}=e;(new l).ensurePrimerAvailable();const r=k(e,"createCheckout"),s=new m({...r,checkoutConfig:t});return await s.initialize(),s}async function R(e,t){const{default:r}=await Promise.resolve().then(function(){return d}),s=new r;return await s.showUniversalCheckout(e,t)}async function g(e){const{priceId:t,externalId:r,email:s,clientMetadata:n,countryCode:i}=e,o=k(e,"createClientSession"),c=new y({baseUrl:o.baseUrl,orgId:o.orgId,timeout:a.REQUEST_TIMEOUT,retryAttempts:a.RETRY_ATTEMPTS}),u=await c.createClientSession({priceId:t,externalId:r,email:s,region:o.region,clientMetadata:n,countryCode:i});return c.processSessionResponse(u)}const C={configure:f,createCheckout:w,showUniversalCheckout:R,createClientSession:g};"undefined"!=typeof window&&(window.Billing=C),e.APIError=s,e.Billing=C,e.CHECKOUT_STATES={INITIALIZING:"initializing",READY:"ready",PROCESSING:"processing",ACTION_REQUIRED:"action_required",UPDATING:"updating",COMPLETED:"completed",ERROR:"error",DESTROYED:"destroyed"},e.CheckoutError=i,e.ConfigurationError=class extends t{constructor(e){super(e,"CONFIGURATION_ERROR"),this.name="ConfigurationError"}},e.DEFAULTS=a,e.ERROR_CODES={SDK_ERROR:"SDK_ERROR",VALIDATION_ERROR:"VALIDATION_ERROR",API_ERROR:"API_ERROR",PRIMER_ERROR:"PRIMER_ERROR",CHECKOUT_ERROR:"CHECKOUT_ERROR",CONFIGURATION_ERROR:"CONFIGURATION_ERROR",NETWORK_ERROR:"NETWORK_ERROR"},e.EVENTS={SUCCESS:"success",ERROR:"error",STATUS_CHANGE:"status-change",DESTROY:"destroy"},e.FunnefoxSDKError=t,e.NetworkError=o,e.PrimerError=n,e.SDK_VERSION="0.1.1",e.ValidationError=r,e.configure=f,e.createCheckout=w,e.createClientSession=g,e.default=C,e.showUniversalCheckout=R,Object.defineProperty(e,"__esModule",{value:!0})});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@funnelfox/billing",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"author": "Funnelfox",
|
|
6
6
|
"description": "JavaScript SDK for Funnelfox billing with Primer integration",
|
|
@@ -71,10 +71,10 @@
|
|
|
71
71
|
},
|
|
72
72
|
"repository": {
|
|
73
73
|
"type": "git",
|
|
74
|
-
"url": "https://github.com/funnelfox
|
|
74
|
+
"url": "https://github.com/adaptyteam/funnelfox-billing-js.git"
|
|
75
75
|
},
|
|
76
76
|
"bugs": {
|
|
77
|
-
"url": "https://github.com/funnelfox
|
|
77
|
+
"url": "https://github.com/adaptyteam/funnelfox-billing-js/issues"
|
|
78
78
|
},
|
|
79
|
-
"homepage": "https://github.com/funnelfox
|
|
79
|
+
"homepage": "https://github.com/adaptyteam/funnelfox-billing-js#readme"
|
|
80
80
|
}
|