@munchi_oy/payments 1.2.6 → 1.2.8
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.js +1 -1
- package/dist/index.mjs +1 -1
- package/dist/src/MunchiPaymentSDK.d.ts +3 -0
- package/dist/src/MunchiPaymentSDK.d.ts.map +1 -1
- package/dist/src/strategies/IPaymentStrategy.d.ts +1 -0
- package/dist/src/strategies/IPaymentStrategy.d.ts.map +1 -1
- package/dist/src/strategies/MockStrategy.d.ts +1 -0
- package/dist/src/strategies/MockStrategy.d.ts.map +1 -1
- package/dist/src/strategies/NetsStrategy.d.ts +1 -0
- package/dist/src/strategies/NetsStrategy.d.ts.map +1 -1
- package/dist/src/strategies/VivaStrategy.d.ts +1 -0
- package/dist/src/strategies/VivaStrategy.d.ts.map +1 -1
- package/dist/src/version.d.ts +1 -1
- package/package.json +1 -1
- package/src/MunchiPaymentSDK.ts +58 -6
- package/src/strategies/IPaymentStrategy.ts +1 -0
- package/src/strategies/MockStrategy.ts +4 -0
- package/src/strategies/NetsStrategy.ts +20 -5
- package/src/strategies/VivaStrategy.ts +21 -2
- package/src/version.ts +1 -1
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var N=Object.defineProperty;var A=Object.getOwnPropertyDescriptor;var w=Object.getOwnPropertyNames;var O=Object.prototype.hasOwnProperty;var D=(u,e)=>{for(var r in e)N(u,r,{get:e[r],enumerable:!0})},_=(u,e,r,t)=>{if(e&&typeof e=="object"||typeof e=="function")for(let n of w(e))!O.call(u,n)&&n!==r&&N(u,n,{get:()=>e[n],enumerable:!(t=A(e,n))||t.enumerable});return u};var L=u=>_(N({},"__esModule",{value:!0}),u);var x={};D(x,{AppReaderStatus:()=>b,MunchiPaymentSDK:()=>P,PaymentInteractionState:()=>S,SdkPaymentStatus:()=>E});module.exports=L(x);var d=require("@munchi_oy/core");var C="1.2.6";var c=class u extends Error{code;rawError;constructor(e,r,t){super(r),this.name="PaymentSDKError",this.code=e,this.rawError=t,Object.setPrototypeOf(this,u.prototype)}};var y=require("@munchi_oy/core");var E=(i=>(i.PENDING="PENDING",i.SUCCESS="SUCCESS",i.APPROVED="APPROVED",i.FAILED="FAILED",i.CANCELLED="CANCELLED",i.ERROR="ERROR",i))(E||{}),S=(a=>(a.IDLE="IDLE",a.CONNECTING="CONNECTING",a.REQUIRES_INPUT="REQUIRES_INPUT",a.PROCESSING="PROCESSING",a.SUCCESS="SUCCESS",a.FAILED="FAILED",a.INTERNAL_ERROR="INTERNAL_ERROR",a.VERIFYING="VERIFYING",a))(S||{});var g=class{constructor(e,r,t){this.messaging=r;this.config=t;this.api=new y.PaymentApi(void 0,"",e)}api;abortController=null;currentRequestId=null;paymentProvider=y.PaymentProviderEnum.Nets;async processPayment(e,r){this.abortController=new AbortController,r("CONNECTING");let t={amount:e.amountCents,businessId:Number(this.config.storeId),referenceId:e.orderRef,currency:e.currency,displayId:e.displayId,options:{allowPinBypass:!0,transactionType:y.TransactionType.Purchase}};try{let{data:n}=await this.api.initiateNetsTerminalTransaction(t),s=n.connectCloudRequestId;if(!s)throw new Error("connectCloudRequestId is missing from response.");if(this.currentRequestId=s,this.abortController.signal.aborted)throw new Error("Aborted");r("REQUIRES_INPUT",{sessionId:s});let i=await this.waitForPaymentCompletion(s,e.orderRef,this.abortController.signal);return this.currentRequestId=null,i}catch(n){throw this.currentRequestId=null,n instanceof c?n:new c("TERMINAL_BUSY","Failed to create Nets Intent",n)}}async waitForPaymentCompletion(e,r,t){let n=`nets.requests.${e}`,s=y.PaymentEventType.StatusChanged;return new Promise((i,l)=>{let a=!1,o=()=>{a=!0,typeof f=="function"&&f(),clearTimeout(I)},m=()=>{o(),l(new c("CANCELLED","Transaction cancelled"))};t.addEventListener("abort",m);let f=this.messaging.subscribe(n,s,h=>{a||(o(),t.removeEventListener("abort",m),i(this.handleSuccess(h)))}),I=setTimeout(async()=>{if(!(a||t.aborted))try{let h=await this.pollOrderStatus(e,r,this.config.storeId,t);i(this.handleSuccess(h))}catch(h){l(new c("TIMEOUT","Payment timed out and polling failed",h))}finally{t.removeEventListener("abort",m),o()}},1e4)})}async pollOrderStatus(e,r,t,n){let a=Date.now()+12e4;for(;Date.now()<a;){if(n.aborted)throw new Error("Aborted");try{let{data:o}=await this.api.getPaymentStatus({businessId:Number(t),orderId:r,provider:this.paymentProvider,referenceId:e});if(o.status!==y.SimplePaymentStatus.Pending)return o}catch(o){throw new Error(`Payment verification failed: ${o instanceof Error?o.message:String(o)}`)}await new Promise(o=>setTimeout(o,2e3))}throw new Error("Payment verification timed out.")}async cancelTransaction(e){if(!this.currentRequestId)return!1;let r={requestId:this.currentRequestId,businessId:Number(this.config.storeId)};try{return await this.api.cancelNetsTerminalTransaction(r),this.abortController?.abort(),this.currentRequestId=null,!0}catch{return!1}}async refundTransaction(e,r){try{this.abortController=new AbortController;let t={amount:e.amountCents,businessId:Number(this.config.storeId),currency:e.currency,displayId:this.config.kioskId,referenceId:e.orderRef,options:{allowPinBypass:!0,transactionType:y.TransactionType.ReturnOfGoods}},{data:n}=await this.api.initiateNetsTerminalTransaction(t),s=n.connectCloudRequestId;if(!s)throw new Error("connectCloudRequestId is missing from response.");if(this.currentRequestId=s,this.abortController.signal.aborted)throw new Error("Aborted");r("REQUIRES_INPUT",{sessionId:s});let i=await this.waitForPaymentCompletion(s,e.orderRef,this.abortController.signal);return this.currentRequestId=null,i}catch(t){throw this.currentRequestId=null,t instanceof c||t instanceof c?t:new c("NETWORK_ERROR","Failed to refund Nets transaction",t)}}async verifyFinalStatus(e,r){try{let{data:t}=await this.api.getPaymentStatus({businessId:Number(this.config.storeId),orderId:e.orderRef,provider:this.paymentProvider,referenceId:r});return this.handleSuccess(t)}catch(t){throw new c("NETWORK_ERROR","Failed to verify final Nets status",t)}}handleSuccess(e){let r=e.status===y.SimplePaymentStatus.Success,t={success:r,status:r?"SUCCESS":"FAILED",orderId:e.orderId,transaction:e.transaction};return e.transactionId&&(t.transactionId=e.transactionId),e.error?.code&&(t.errorCode=e.error.code),e.error?.message&&(t.errorMessage=e.error.message),t}};var p=require("@munchi_oy/core");var T=class{constructor(e,r,t){this.messaging=r;this.config=t;this.api=new p.PaymentApi(void 0,"",e)}api;abortController=null;currentSessionId=null;paymentProvider=p.PaymentProviderEnum.Viva;async processPayment(e,r){this.abortController=new AbortController,r("CONNECTING");let t={amount:e.amountCents,referenceId:e.orderRef,businessId:parseInt(this.config.storeId),currency:e.currency,displayId:e.displayId,showReceipt:!0,showTransactionResult:!0};try{let{data:n}=await this.api.initiateTerminalTransaction(t);if(this.currentSessionId=n.sessionId,this.abortController.signal.aborted)throw new Error("Aborted");r("REQUIRES_INPUT",{sessionId:n.sessionId});let s=await this.waitForPaymentCompletion(n.sessionId,e.orderRef,this.abortController.signal);return this.currentSessionId=null,s}catch(n){throw this.currentSessionId=null,n instanceof c?n:new c("NETWORK_ERROR","Failed to create Viva Intent",n)}}async waitForPaymentCompletion(e,r,t){let s=`viva.${this.config.channel.toLowerCase()}.requests.${e}`,i="payment:status-changed";return new Promise((l,a)=>{let o=!1,m=()=>{o=!0,typeof I=="function"&&I(),clearTimeout(h)},f=()=>{m(),a(new Error("Aborted"))};t.addEventListener("abort",f);let I=this.messaging.subscribe(s,i,R=>{o||(m(),t.removeEventListener("abort",f),l(this.handleSuccess(R)))}),h=setTimeout(async()=>{if(!(o||t.aborted))try{let R=await this.pollOrderStatus(e,r,this.config.storeId,t);l(this.handleSuccess(R))}catch(R){a(new c("TIMEOUT","Payment timed out and polling failed",R))}finally{t.removeEventListener("abort",f),m()}},1e4)})}async pollOrderStatus(e,r,t,n){let a=Date.now()+12e4;for(;Date.now()<a;){if(n.aborted)throw new Error("Aborted");try{let{data:o}=await this.api.getPaymentStatus({businessId:Number(t),orderId:r,provider:this.paymentProvider,referenceId:e});if(o.status!==p.SimplePaymentStatus.Pending)return o}catch(o){throw new Error(`Payment verification failed: ${o instanceof Error?o.message:String(o)}`)}await new Promise(o=>setTimeout(o,2e3))}throw new Error("Payment verification timed out.")}handleSuccess(e){let r=e.status===p.SimplePaymentStatus.Success,t={success:r,status:r?"SUCCESS":"FAILED",orderId:e.orderId,errorCode:e.error?.code??"",errorMessage:e.error?.message??""};return e.transactionId&&(t.transactionId=e.transactionId),e.error?.referenceError&&(t.errorReference=e.error.referenceError),e.transaction&&(t.transaction=e.transaction),t}async cancelTransaction(e){if(!this.currentSessionId)return!1;try{let r=this.currentSessionId;return this.currentSessionId=null,await this.api.cancelVivaTransactionV2({cashRegisterId:this.config.storeId,sessionId:r}),this.abortController?.abort(),!0}catch(r){throw this.currentSessionId=null,new c("NETWORK_ERROR","Failed to cancel Viva transaction",r)}}async verifyFinalStatus(e,r){try{let{data:t}=await this.api.getPaymentStatus({businessId:Number(this.config.storeId),orderId:e.orderRef,provider:this.paymentProvider,referenceId:r}),n=t.status===p.SimplePaymentStatus.Success,s={success:n,status:n?"SUCCESS":"FAILED",orderId:t.orderId,errorCode:t.error?.code??"",errorMessage:t.error?.message??""};return t.transactionId&&(s.transactionId=t.transactionId),t.error?.referenceError&&(s.errorReference=t.error.referenceError),t.transaction&&(s.transaction=t.transaction),s}catch(t){throw new c("NETWORK_ERROR","Failed to verify final transaction status",t)}}async refundTransaction(e,r){try{let t={amount:e.amountCents,businessId:Number(this.config.storeId),displayId:this.config.kioskId,currency:e.currency,orderReferenceId:e.orderRef,referenceId:e.originalTransactionId},{data:n}=await this.api.refundSingleVivaTransaction(t),s=n.success;return{success:s,status:s?"SUCCESS":"FAILED",orderId:e.orderRef,transactionId:n.sessionId}}catch(t){throw new c("NETWORK_ERROR","Failed to refund Viva transaction",t)}}};var P=class u{strategy;axios;messaging;timeoutMs;logger;_currentState="IDLE";_listeners=[];_cancellationIntent=!1;_currentSessionId;_autoResetTimer;autoResetOptions;static TERMINAL_STATES=["SUCCESS","FAILED","INTERNAL_ERROR"];static RESTING_STATES=["IDLE",...u.TERMINAL_STATES];constructor(e,r,t,n={},s){this.axios=e,this.messaging=r,this.logger=n.logger,this.timeoutMs=n.timeoutMs||6e4,this.autoResetOptions=n.autoResetOnPaymentComplete,this.strategy=s??this.resolveStrategy(t)}get version(){return C}get currentState(){return this._currentState}generateErrorResult(e,r,t){return{success:!1,status:"ERROR",errorCode:this.normalizeErrorCode(r),errorMessage:t,orderId:e}}normalizeErrorCode(e){return e?e.includes(".")?e:{CANCELLED:d.PaymentFailureCode.PaymentCancelledByUser,DECLINED:d.PaymentFailureCode.PaymentDeclined,TERMINAL_BUSY:d.PaymentFailureCode.TerminalBusy,TERMINAL_OFFLINE:d.PaymentFailureCode.TerminalOffline,TIMEOUT:d.PaymentFailureCode.TerminalTimeout,NETWORK_ERROR:d.PaymentFailureCode.SystemProviderError,STRATEGY_ERROR:d.PaymentFailureCode.SystemProviderError,MISSING_CONFIG:d.PaymentFailureCode.SystemUnknown,INVALID_AMOUNT:d.PaymentFailureCode.SystemUnknown,UNKNOWN:d.PaymentFailureCode.SystemUnknown}[e]??d.PaymentFailureCode.SystemUnknown:d.PaymentFailureCode.SystemUnknown}subscribe=e=>(this._listeners.push(e),e(this._currentState),()=>{this._listeners=this._listeners.filter(r=>r!==e)});transitionTo(e){if(this._currentState===e)return;if(e==="IDLE"){this.cancelAutoReset(),this._currentState=e,this._listeners.forEach(t=>t(e));return}if(u.TERMINAL_STATES.includes(this._currentState)){let t=`Invalid State Transition: Attempted to move from terminal state ${this._currentState} to ${e}`;throw this.logger?.error(t),this._currentState!=="INTERNAL_ERROR"&&(this._currentState="INTERNAL_ERROR",this._listeners.forEach(n=>n(this._currentState))),new c("UNKNOWN",t)}this._currentState=e,u.TERMINAL_STATES.includes(e)&&this.scheduleAutoReset(e),this._listeners.forEach(t=>t(e))}_resetScheduledAt;get nextAutoResetAt(){return this._resetScheduledAt}cancelAutoReset(){this._autoResetTimer&&(clearTimeout(this._autoResetTimer),this._autoResetTimer=void 0),this._resetScheduledAt=void 0}scheduleAutoReset(e){if(!this.autoResetOptions)return;let t=e==="SUCCESS"?this.autoResetOptions.successDelayMs??5e3:this.autoResetOptions.failureDelayMs??5e3;this.logger?.info(`Scheduling auto-reset to IDLE in ${t}ms`),this._resetScheduledAt=Date.now()+t,this._autoResetTimer=setTimeout(()=>{this.logger?.info("Auto-reset triggered"),this.reset()},t)}resolveStrategy(e){return e.provider===d.PaymentProvider.Nets?new g(this.axios,this.messaging,e):new T(this.axios,this.messaging,e)}initiateTransaction=async(e,r)=>{let t=r??{},n=e.orderRef;if(!u.RESTING_STATES.includes(this._currentState))return this.generateErrorResult(e.orderRef,"UNKNOWN","A transaction is already in progress");let i=Date.now();if(this._cancellationIntent=!1,this._currentSessionId=void 0,this.transitionTo("IDLE"),e.amountCents<=0)return this.generateErrorResult(e.orderRef,"INVALID_AMOUNT","Amount must be greater than 0");try{let l=(f,I)=>{I?.sessionId&&(this._currentSessionId=I.sessionId),f!=="FAILED"&&(this.transitionTo(f),this.fireStateCallback(f,t,n))},a=this.strategy.processPayment(e,l),o=new Promise((f,I)=>{setTimeout(()=>{I(new c("TIMEOUT","Transaction timed out"))},this.timeoutMs)}),m=await Promise.race([a,o]);if(m.success)this.transitionTo("SUCCESS"),this.safeFireCallback(()=>t.onSuccess?.(m));else{if(this._cancellationIntent)return await this.handleTransactionError(e,new Error("Aborted after resolution"),t);this.transitionTo("FAILED"),this.safeFireCallback(()=>t.onError?.(m))}return this.logger?.info("Transaction completed successfully",{orderId:e.orderRef,durationMs:Date.now()-i}),m}catch(l){return this.logger?.warn("Transaction interrupted. Handling final status...",{error:l}),await this.handleTransactionError(e,l,t)}};async handleTransactionError(e,r,t={}){if(this._cancellationIntent||(this.transitionTo("VERIFYING"),this.safeFireCallback(()=>t.onVerifying?.({orderRef:e.orderRef,refPaymentId:this._currentSessionId}))),this._cancellationIntent){try{if(this._currentSessionId){let s=await this.strategy.verifyFinalStatus(e,this._currentSessionId);if(s.success)return this.transitionTo("SUCCESS"),this.safeFireCallback(()=>t.onSuccess?.(s)),s}}catch(s){this.logger?.warn("Final status verification failed during cancellation",{err:s})}return this.transitionTo("FAILED"),this.safeFireCallback(()=>t.onCancelled?.({orderRef:e.orderRef,refPaymentId:this._currentSessionId})),{success:!1,status:"CANCELLED",errorCode:this.normalizeErrorCode("CANCELLED"),orderId:e.orderRef,...this._currentSessionId?{transactionId:this._currentSessionId}:{}}}this.transitionTo("FAILED");let n;if(this._currentSessionId)try{n=await this.strategy.verifyFinalStatus(e,this._currentSessionId)}catch(s){this.logger?.warn("Failed to get detailed error from verifyFinalStatus",{verifyErr:s}),n=this.buildErrorResultFromException(e.orderRef,r)}else n=this.buildErrorResultFromException(e.orderRef,r);return this.safeFireCallback(()=>t.onError?.(n)),n}buildErrorResultFromException(e,r){return r instanceof c?this.generateErrorResult(e,r.code,r.message):this.generateErrorResult(e,"UNKNOWN",r instanceof Error?r.message:"Unknown fatal error")}fireStateCallback(e,r,t){let n={orderRef:t,refPaymentId:this._currentSessionId};switch(e){case"CONNECTING":this.safeFireCallback(()=>r.onConnecting?.(n));break;case"REQUIRES_INPUT":this.safeFireCallback(()=>r.onRequiresInput?.(n));break;case"PROCESSING":this.safeFireCallback(()=>r.onProcessing?.(n));break}}safeFireCallback(e){try{e()}catch(r){this.logger?.warn("Callback execution failed",{error:r})}}cancel=async()=>{if(this.logger?.info("Attempting cancellation"),u.TERMINAL_STATES.includes(this._currentState))return this.logger?.warn("Cannot cancel: Transaction already in terminal state",{state:this._currentState}),!1;this._cancellationIntent=!0,this.transitionTo("VERIFYING");try{let e=await this.strategy.cancelTransaction(r=>this.transitionTo(r));return!e&&this._currentState==="VERIFYING"&&this.transitionTo("IDLE"),e}catch(e){return this.logger?.error("Cancellation command failed",e),!1}};reset=()=>{u.TERMINAL_STATES.includes(this._currentState)&&this.transitionTo("IDLE")};refund=async(e,r)=>{let t=r??{};if(this.logger?.info("Initiating refund",{orderRef:e.orderRef}),this._currentSessionId=void 0,!u.RESTING_STATES.includes(this._currentState))return this.generateErrorResult(e.orderRef,"UNKNOWN","A transaction is already in progress");this.transitionTo("IDLE");try{let s=(l,a)=>{a?.sessionId&&(this._currentSessionId=a.sessionId),l!=="FAILED"&&(this.transitionTo(l),this.fireStateCallback(l,t,e.orderRef))},i=await this.strategy.refundTransaction(e,s);return i.success?(this.transitionTo("SUCCESS"),this.safeFireCallback(()=>t.onSuccess?.(i))):(this.transitionTo("FAILED"),this.safeFireCallback(()=>t.onError?.(i))),this.logger?.info("Refund completed",{success:i.success,orderRef:e.orderRef}),i}catch(s){this.logger?.error("Refund failed",s),this.transitionTo("FAILED");let i=this.generateErrorResult(e.orderRef,"UNKNOWN",s instanceof Error?s.message:"Refund failed");return this.safeFireCallback(()=>t.onError?.(i)),i}}};var b=(n=>(n.CONNECTING="CONNECTING",n.CONNECTED="CONNECTED",n.OFFLINE="OFFLINE",n.DISCONNECTED="DISCONNECTED",n))(b||{});0&&(module.exports={AppReaderStatus,MunchiPaymentSDK,PaymentInteractionState,SdkPaymentStatus});
|
|
1
|
+
"use strict";var N=Object.defineProperty;var A=Object.getOwnPropertyDescriptor;var w=Object.getOwnPropertyNames;var O=Object.prototype.hasOwnProperty;var _=(u,e)=>{for(var r in e)N(u,r,{get:e[r],enumerable:!0})},D=(u,e,r,t)=>{if(e&&typeof e=="object"||typeof e=="function")for(let n of w(e))!O.call(u,n)&&n!==r&&N(u,n,{get:()=>e[n],enumerable:!(t=A(e,n))||t.enumerable});return u};var L=u=>D(N({},"__esModule",{value:!0}),u);var M={};_(M,{AppReaderStatus:()=>v,MunchiPaymentSDK:()=>P,PaymentInteractionState:()=>S,SdkPaymentStatus:()=>E});module.exports=L(M);var m=require("@munchi_oy/core");var C="1.2.8";var c=class u extends Error{code;rawError;constructor(e,r,t){super(r),this.name="PaymentSDKError",this.code=e,this.rawError=t,Object.setPrototypeOf(this,u.prototype)}};var f=require("@munchi_oy/core");var E=(i=>(i.PENDING="PENDING",i.SUCCESS="SUCCESS",i.APPROVED="APPROVED",i.FAILED="FAILED",i.CANCELLED="CANCELLED",i.ERROR="ERROR",i))(E||{}),S=(a=>(a.IDLE="IDLE",a.CONNECTING="CONNECTING",a.REQUIRES_INPUT="REQUIRES_INPUT",a.PROCESSING="PROCESSING",a.SUCCESS="SUCCESS",a.FAILED="FAILED",a.INTERNAL_ERROR="INTERNAL_ERROR",a.VERIFYING="VERIFYING",a))(S||{});var g=class{constructor(e,r,t){this.messaging=r;this.config=t;this.api=new f.PaymentApi(void 0,"",e)}api;abortController=null;currentRequestId=null;paymentProvider=f.PaymentProviderEnum.Nets;async processPayment(e,r){this.abortController=new AbortController,r("CONNECTING");let t={amount:e.amountCents,businessId:Number(this.config.storeId),referenceId:e.orderRef,currency:e.currency,displayId:e.displayId,options:{allowPinBypass:!0,transactionType:f.TransactionType.Purchase}};try{let{data:n}=await this.api.initiateNetsTerminalTransaction(t),s=n.connectCloudRequestId;if(!s)throw new Error("connectCloudRequestId is missing from response.");if(this.currentRequestId=s,this.abortController.signal.aborted)throw new Error("Aborted");r("REQUIRES_INPUT",{sessionId:s});let i=await this.waitForPaymentCompletion(s,e.orderRef,this.abortController.signal);return this.currentRequestId=null,i}catch(n){throw this.currentRequestId=null,n instanceof c?n:new c("TERMINAL_BUSY","Failed to create Nets Intent",n)}}async waitForPaymentCompletion(e,r,t){let n=`nets.requests.${e}`,s=f.PaymentEventType.StatusChanged;return new Promise((i,d)=>{let a=!1,o=()=>{a=!0,typeof y=="function"&&y(),clearTimeout(I)},l=()=>{o(),d(new c("CANCELLED","Transaction cancelled"))};t.addEventListener("abort",l);let y=this.messaging.subscribe(n,s,p=>{a||(o(),t.removeEventListener("abort",l),i(this.handleSuccess(p)))}),I=setTimeout(async()=>{if(!(a||t.aborted))try{let p=await this.pollOrderStatus(e,r,this.config.storeId,t);i(this.handleSuccess(p))}catch(p){d(new c("TIMEOUT","Payment timed out and polling failed",p))}finally{t.removeEventListener("abort",l),o()}},1e4)})}async pollOrderStatus(e,r,t,n){let a=Date.now()+12e4;for(;Date.now()<a;){if(n.aborted)throw new Error("Aborted");try{let{data:o}=await this.api.getPaymentStatus({businessId:Number(t),orderId:r,provider:this.paymentProvider,referenceId:e});if(n.aborted)throw new Error("Aborted");if(o.status!==f.SimplePaymentStatus.Pending)return o}catch(o){if(o instanceof Error&&o.message==="Aborted")throw o}await new Promise(o=>{let l=()=>{clearTimeout(y),o(void 0)};n.addEventListener("abort",l,{once:!0});let y=setTimeout(()=>{n.removeEventListener("abort",l),o(void 0)},2e3)})}throw new Error("Payment verification timed out.")}async cancelTransaction(e){if(!this.currentRequestId)return!1;let r={requestId:this.currentRequestId,businessId:Number(this.config.storeId)};try{return await this.api.cancelNetsTerminalTransaction(r),this.abortController?.abort(),this.currentRequestId=null,!0}catch{return!1}}async refundTransaction(e,r){try{this.abortController=new AbortController;let t={amount:e.amountCents,businessId:Number(this.config.storeId),currency:e.currency,displayId:this.config.kioskId,referenceId:e.orderRef,options:{allowPinBypass:!0,transactionType:f.TransactionType.ReturnOfGoods}},{data:n}=await this.api.initiateNetsTerminalTransaction(t),s=n.connectCloudRequestId;if(!s)throw new Error("connectCloudRequestId is missing from response.");if(this.currentRequestId=s,this.abortController.signal.aborted)throw new Error("Aborted");r("REQUIRES_INPUT",{sessionId:s});let i=await this.waitForPaymentCompletion(s,e.orderRef,this.abortController.signal);return this.currentRequestId=null,i}catch(t){throw this.currentRequestId=null,t instanceof c||t instanceof c?t:new c("NETWORK_ERROR","Failed to refund Nets transaction",t)}}async verifyFinalStatus(e,r){try{let{data:t}=await this.api.getPaymentStatus({businessId:Number(this.config.storeId),orderId:e.orderRef,provider:this.paymentProvider,referenceId:r});return this.handleSuccess(t)}catch(t){throw new c("NETWORK_ERROR","Failed to verify final Nets status",t)}}handleSuccess(e){let r=e.status===f.SimplePaymentStatus.Success,t={success:r,status:r?"SUCCESS":"FAILED",orderId:e.orderId,transaction:e.transaction};return e.transactionId&&(t.transactionId=e.transactionId),e.error?.code&&(t.errorCode=e.error.code),e.error?.message&&(t.errorMessage=e.error.message),t}abort(){this.abortController?.abort()}};var h=require("@munchi_oy/core");var T=class{constructor(e,r,t){this.messaging=r;this.config=t;this.api=new h.PaymentApi(void 0,"",e)}api;abortController=null;currentSessionId=null;paymentProvider=h.PaymentProviderEnum.Viva;async processPayment(e,r){this.abortController=new AbortController,r("CONNECTING");let t={amount:e.amountCents,referenceId:e.orderRef,businessId:parseInt(this.config.storeId),currency:e.currency,displayId:e.displayId,showReceipt:!0,showTransactionResult:!0};try{let{data:n}=await this.api.initiateTerminalTransaction(t);if(this.currentSessionId=n.sessionId,this.abortController.signal.aborted)throw new Error("Aborted");r("REQUIRES_INPUT",{sessionId:n.sessionId});let s=await this.waitForPaymentCompletion(n.sessionId,e.orderRef,this.abortController.signal);return this.currentSessionId=null,s}catch(n){throw this.currentSessionId=null,n instanceof c?n:new c("NETWORK_ERROR","Failed to create Viva Intent",n)}}async waitForPaymentCompletion(e,r,t){let s=`viva.${this.config.channel.toLowerCase()}.requests.${e}`,i="payment:status-changed";return new Promise((d,a)=>{let o=!1,l=()=>{o=!0,typeof I=="function"&&I(),clearTimeout(p)},y=()=>{l(),a(new Error("Aborted"))};t.addEventListener("abort",y);let I=this.messaging.subscribe(s,i,R=>{o||(l(),t.removeEventListener("abort",y),d(this.handleSuccess(R)))}),p=setTimeout(async()=>{if(!(o||t.aborted))try{let R=await this.pollOrderStatus(e,r,this.config.storeId,t);d(this.handleSuccess(R))}catch(R){a(new c("TIMEOUT","Payment timed out and polling failed",R))}finally{t.removeEventListener("abort",y),l()}},1e4)})}async pollOrderStatus(e,r,t,n){let a=Date.now()+12e4;for(;Date.now()<a;){if(n.aborted)throw new Error("Aborted");try{let{data:o}=await this.api.getPaymentStatus({businessId:Number(t),orderId:r,provider:this.paymentProvider,referenceId:e});if(n.aborted)throw new Error("Aborted");if(o.status!==h.SimplePaymentStatus.Pending)return o}catch(o){if(o instanceof Error&&o.message==="Aborted")throw o}await new Promise(o=>{let l=()=>{clearTimeout(y),o(void 0)};n.addEventListener("abort",l,{once:!0});let y=setTimeout(()=>{n.removeEventListener("abort",l),o(void 0)},2e3)})}throw new Error("Payment verification timed out.")}handleSuccess(e){let r=e.status===h.SimplePaymentStatus.Success,t={success:r,status:r?"SUCCESS":"FAILED",orderId:e.orderId,errorCode:e.error?.code??"",errorMessage:e.error?.message??""};return e.transactionId&&(t.transactionId=e.transactionId),e.error?.referenceError&&(t.errorReference=e.error.referenceError),e.transaction&&(t.transaction=e.transaction),t}async cancelTransaction(e){if(!this.currentSessionId)return!1;try{let r=this.currentSessionId;return this.currentSessionId=null,await this.api.cancelVivaTransactionV2({cashRegisterId:this.config.storeId,sessionId:r}),this.abortController?.abort(),!0}catch(r){throw this.currentSessionId=null,new c("NETWORK_ERROR","Failed to cancel Viva transaction",r)}}async verifyFinalStatus(e,r){try{let{data:t}=await this.api.getPaymentStatus({businessId:Number(this.config.storeId),orderId:e.orderRef,provider:this.paymentProvider,referenceId:r}),n=t.status===h.SimplePaymentStatus.Success,s={success:n,status:n?"SUCCESS":"FAILED",orderId:t.orderId,errorCode:t.error?.code??"",errorMessage:t.error?.message??""};return t.transactionId&&(s.transactionId=t.transactionId),t.error?.referenceError&&(s.errorReference=t.error.referenceError),t.transaction&&(s.transaction=t.transaction),s}catch(t){throw new c("NETWORK_ERROR","Failed to verify final transaction status",t)}}async refundTransaction(e,r){try{let t={amount:e.amountCents,businessId:Number(this.config.storeId),displayId:this.config.kioskId,currency:e.currency,orderReferenceId:e.orderRef,referenceId:e.originalTransactionId},{data:n}=await this.api.refundSingleVivaTransaction(t),s=n.success;return{success:s,status:s?"SUCCESS":"FAILED",orderId:e.orderRef,transactionId:n.sessionId}}catch(t){throw new c("NETWORK_ERROR","Failed to refund Viva transaction",t)}}abort(){this.abortController?.abort()}};var P=class u{strategy;axios;messaging;timeoutMs;logger;_currentState="IDLE";_listeners=[];_cancellationIntent=!1;_currentSessionId;_autoResetTimer;autoResetOptions;static TERMINAL_STATES=["SUCCESS","FAILED","INTERNAL_ERROR"];static RESTING_STATES=["IDLE",...u.TERMINAL_STATES];constructor(e,r,t,n={},s){this.axios=e,this.messaging=r,this.logger=n.logger,this.timeoutMs=n.timeoutMs||3e4,this.autoResetOptions=n.autoResetOnPaymentComplete,this.strategy=s??this.resolveStrategy(t)}get version(){return C}get currentState(){return this._currentState}generateErrorResult(e,r,t){return{success:!1,status:"ERROR",errorCode:this.normalizeErrorCode(r),errorMessage:t,orderId:e}}normalizeErrorCode(e){return e?e.includes(".")?e:{CANCELLED:m.PaymentFailureCode.PaymentCancelledByUser,DECLINED:m.PaymentFailureCode.PaymentDeclined,TERMINAL_BUSY:m.PaymentFailureCode.TerminalBusy,TERMINAL_OFFLINE:m.PaymentFailureCode.TerminalOffline,TIMEOUT:m.PaymentFailureCode.TerminalTimeout,NETWORK_ERROR:m.PaymentFailureCode.SystemProviderError,STRATEGY_ERROR:m.PaymentFailureCode.SystemProviderError,MISSING_CONFIG:m.PaymentFailureCode.SystemUnknown,INVALID_AMOUNT:m.PaymentFailureCode.SystemUnknown,UNKNOWN:m.PaymentFailureCode.SystemUnknown}[e]??m.PaymentFailureCode.SystemUnknown:m.PaymentFailureCode.SystemUnknown}subscribe=e=>(this._listeners.push(e),e(this._currentState),()=>{this._listeners=this._listeners.filter(r=>r!==e)});transitionTo(e){if(this._currentState===e)return;if(e==="IDLE"){this.cancelAutoReset(),this._currentState=e,this._listeners.forEach(t=>t(e));return}if(u.TERMINAL_STATES.includes(this._currentState)){let t=`Invalid State Transition: Attempted to move from terminal state ${this._currentState} to ${e}`;throw this.logger?.error(t),this._currentState!=="INTERNAL_ERROR"&&(this._currentState="INTERNAL_ERROR",this._listeners.forEach(n=>n(this._currentState))),new c("UNKNOWN",t)}this._currentState=e,u.TERMINAL_STATES.includes(e)&&this.scheduleAutoReset(e),this._listeners.forEach(t=>t(e))}_resetScheduledAt;get nextAutoResetAt(){return this._resetScheduledAt}cancelAutoReset(){this._autoResetTimer&&(clearTimeout(this._autoResetTimer),this._autoResetTimer=void 0),this._resetScheduledAt=void 0}scheduleAutoReset(e){if(!this.autoResetOptions)return;let t=e==="SUCCESS"?this.autoResetOptions.successDelayMs??5e3:this.autoResetOptions.failureDelayMs??5e3;this.logger?.info(`Scheduling auto-reset to IDLE in ${t}ms`),this._resetScheduledAt=Date.now()+t,this._autoResetTimer=setTimeout(()=>{this.logger?.info("Auto-reset triggered"),this.reset()},t)}resolveStrategy(e){return e.provider===m.PaymentProvider.Nets?new g(this.axios,this.messaging,e):new T(this.axios,this.messaging,e)}initiateTransaction=async(e,r)=>{let t=r??{},n=e.orderRef;if(!u.RESTING_STATES.includes(this._currentState))return this.generateErrorResult(e.orderRef,"UNKNOWN","A transaction is already in progress");let i=Date.now();if(this._cancellationIntent=!1,this._currentSessionId=void 0,this.transitionTo("IDLE"),e.amountCents<=0)return this.generateErrorResult(e.orderRef,"INVALID_AMOUNT","Amount must be greater than 0");try{let d=(y,I)=>{I?.sessionId&&(this._currentSessionId=I.sessionId),y!=="FAILED"&&(this.transitionTo(y),this.fireStateCallback(y,t,n))},a=this.strategy.processPayment(e,d),o=new Promise((y,I)=>{setTimeout(()=>{I(new c("TIMEOUT","Transaction timed out"))},this.timeoutMs)}),l=await Promise.race([a,o]);if(l.success)this.transitionTo("SUCCESS"),this.safeFireCallback(()=>t.onSuccess?.(l));else{if(this._cancellationIntent)return await this.handleTransactionError(e,new Error("Aborted after resolution"),t);this.transitionTo("FAILED"),this.safeFireCallback(()=>t.onError?.(l))}return this.logger?.info("Transaction completed successfully",{orderId:e.orderRef,durationMs:Date.now()-i}),l}catch(d){return this.logger?.warn("Transaction interrupted. Handling final status...",{error:d}),await this.handleTransactionError(e,d,t)}};async handleTransactionError(e,r,t={}){if(this._cancellationIntent||(this.transitionTo("VERIFYING"),this.safeFireCallback(()=>t.onVerifying?.({orderRef:e.orderRef,refPaymentId:this._currentSessionId}))),this._cancellationIntent){try{if(this._currentSessionId){let s=await this.verifyWithRetry(e,this._currentSessionId);if(s.success)return this.transitionTo("SUCCESS"),this.safeFireCallback(()=>t.onSuccess?.(s)),s}}catch(s){this.logger?.warn("Final status verification failed during cancellation",{err:s})}return this.transitionTo("FAILED"),this.safeFireCallback(()=>t.onCancelled?.({orderRef:e.orderRef,refPaymentId:this._currentSessionId})),{success:!1,status:"CANCELLED",errorCode:this.normalizeErrorCode("CANCELLED"),orderId:e.orderRef,...this._currentSessionId?{transactionId:this._currentSessionId}:{}}}let n;if(this.strategy.abort(),this._currentSessionId)try{let s=await this.verifyWithRetry(e,this._currentSessionId);if(s.success)return this.transitionTo("SUCCESS"),this.safeFireCallback(()=>t.onSuccess?.(s)),s;n=s}catch(s){this.logger?.warn("Failed to get detailed error from verifyFinalStatus",{verifyErr:s}),n=this.buildErrorResultFromException(e.orderRef,s)}else n=this.buildErrorResultFromException(e.orderRef,r);return this.transitionTo("FAILED"),this.safeFireCallback(()=>t.onError?.(n)),n}static VERIFY_TIMEOUT_MS=1e4;static VERIFY_MAX_RETRIES=3;async verifyWithRetry(e,r){let t,n=!0;for(let d=1;d<=u.VERIFY_MAX_RETRIES;d++)try{return await Promise.race([this.strategy.verifyFinalStatus(e,r),new Promise((o,l)=>setTimeout(()=>l(new Error("Verify timed out")),u.VERIFY_TIMEOUT_MS))])}catch(a){t=a,a instanceof Error&&a.message==="Verify timed out"||(n=!1),this.logger?.warn(`verifyFinalStatus attempt ${d}/${u.VERIFY_MAX_RETRIES} failed`,{err:a})}let s=t instanceof Error?t.message:"Verify retries exhausted",i=n?m.PaymentFailureCode.PaymentTimeout:m.PaymentFailureCode.PaymentUnknown;throw new c(i,s)}buildErrorResultFromException(e,r){return r instanceof c?this.generateErrorResult(e,r.code,r.message):this.generateErrorResult(e,"UNKNOWN",r instanceof Error?r.message:"Unknown fatal error")}fireStateCallback(e,r,t){let n={orderRef:t,refPaymentId:this._currentSessionId};switch(e){case"CONNECTING":this.safeFireCallback(()=>r.onConnecting?.(n));break;case"REQUIRES_INPUT":this.safeFireCallback(()=>r.onRequiresInput?.(n));break;case"PROCESSING":this.safeFireCallback(()=>r.onProcessing?.(n));break}}safeFireCallback(e){try{e()}catch(r){this.logger?.warn("Callback execution failed",{error:r})}}cancel=async()=>{if(this.logger?.info("Attempting cancellation"),u.TERMINAL_STATES.includes(this._currentState))return this.logger?.warn("Cannot cancel: Transaction already in terminal state",{state:this._currentState}),!1;this._cancellationIntent=!0,this.transitionTo("VERIFYING");try{let e=await this.strategy.cancelTransaction(r=>this.transitionTo(r));return!e&&this._currentState==="VERIFYING"&&this.transitionTo("IDLE"),e}catch(e){return this.logger?.error("Cancellation command failed",e),!1}};reset=()=>{u.TERMINAL_STATES.includes(this._currentState)&&this.transitionTo("IDLE")};refund=async(e,r)=>{let t=r??{};if(this.logger?.info("Initiating refund",{orderRef:e.orderRef}),this._currentSessionId=void 0,!u.RESTING_STATES.includes(this._currentState))return this.generateErrorResult(e.orderRef,"UNKNOWN","A transaction is already in progress");this.transitionTo("IDLE");try{let s=(d,a)=>{a?.sessionId&&(this._currentSessionId=a.sessionId),d!=="FAILED"&&(this.transitionTo(d),this.fireStateCallback(d,t,e.orderRef))},i=await this.strategy.refundTransaction(e,s);return i.success?(this.transitionTo("SUCCESS"),this.safeFireCallback(()=>t.onSuccess?.(i))):(this.transitionTo("FAILED"),this.safeFireCallback(()=>t.onError?.(i))),this.logger?.info("Refund completed",{success:i.success,orderRef:e.orderRef}),i}catch(s){this.logger?.error("Refund failed",s),this.transitionTo("FAILED");let i=this.generateErrorResult(e.orderRef,"UNKNOWN",s instanceof Error?s.message:"Refund failed");return this.safeFireCallback(()=>t.onError?.(i)),i}}};var v=(n=>(n.CONNECTING="CONNECTING",n.CONNECTED="CONNECTED",n.OFFLINE="OFFLINE",n.DISCONNECTED="DISCONNECTED",n))(v||{});0&&(module.exports={AppReaderStatus,MunchiPaymentSDK,PaymentInteractionState,SdkPaymentStatus});
|
package/dist/index.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{PaymentFailureCode as d,PaymentProvider as L}from"@munchi_oy/core";var P="1.2.6";var c=class m extends Error{code;rawError;constructor(e,r,t){super(r),this.name="PaymentSDKError",this.code=e,this.rawError=t,Object.setPrototypeOf(this,m.prototype)}};import{PaymentApi as A,PaymentEventType as w,PaymentProviderEnum as O,SimplePaymentStatus as N,TransactionType as C}from"@munchi_oy/core";var h=(i=>(i.PENDING="PENDING",i.SUCCESS="SUCCESS",i.APPROVED="APPROVED",i.FAILED="FAILED",i.CANCELLED="CANCELLED",i.ERROR="ERROR",i))(h||{}),R=(a=>(a.IDLE="IDLE",a.CONNECTING="CONNECTING",a.REQUIRES_INPUT="REQUIRES_INPUT",a.PROCESSING="PROCESSING",a.SUCCESS="SUCCESS",a.FAILED="FAILED",a.INTERNAL_ERROR="INTERNAL_ERROR",a.VERIFYING="VERIFYING",a))(R||{});var E=class{constructor(e,r,t){this.messaging=r;this.config=t;this.api=new A(void 0,"",e)}api;abortController=null;currentRequestId=null;paymentProvider=O.Nets;async processPayment(e,r){this.abortController=new AbortController,r("CONNECTING");let t={amount:e.amountCents,businessId:Number(this.config.storeId),referenceId:e.orderRef,currency:e.currency,displayId:e.displayId,options:{allowPinBypass:!0,transactionType:C.Purchase}};try{let{data:n}=await this.api.initiateNetsTerminalTransaction(t),s=n.connectCloudRequestId;if(!s)throw new Error("connectCloudRequestId is missing from response.");if(this.currentRequestId=s,this.abortController.signal.aborted)throw new Error("Aborted");r("REQUIRES_INPUT",{sessionId:s});let i=await this.waitForPaymentCompletion(s,e.orderRef,this.abortController.signal);return this.currentRequestId=null,i}catch(n){throw this.currentRequestId=null,n instanceof c?n:new c("TERMINAL_BUSY","Failed to create Nets Intent",n)}}async waitForPaymentCompletion(e,r,t){let n=`nets.requests.${e}`,s=w.StatusChanged;return new Promise((i,u)=>{let a=!1,o=()=>{a=!0,typeof y=="function"&&y(),clearTimeout(f)},l=()=>{o(),u(new c("CANCELLED","Transaction cancelled"))};t.addEventListener("abort",l);let y=this.messaging.subscribe(n,s,I=>{a||(o(),t.removeEventListener("abort",l),i(this.handleSuccess(I)))}),f=setTimeout(async()=>{if(!(a||t.aborted))try{let I=await this.pollOrderStatus(e,r,this.config.storeId,t);i(this.handleSuccess(I))}catch(I){u(new c("TIMEOUT","Payment timed out and polling failed",I))}finally{t.removeEventListener("abort",l),o()}},1e4)})}async pollOrderStatus(e,r,t,n){let a=Date.now()+12e4;for(;Date.now()<a;){if(n.aborted)throw new Error("Aborted");try{let{data:o}=await this.api.getPaymentStatus({businessId:Number(t),orderId:r,provider:this.paymentProvider,referenceId:e});if(o.status!==N.Pending)return o}catch(o){throw new Error(`Payment verification failed: ${o instanceof Error?o.message:String(o)}`)}await new Promise(o=>setTimeout(o,2e3))}throw new Error("Payment verification timed out.")}async cancelTransaction(e){if(!this.currentRequestId)return!1;let r={requestId:this.currentRequestId,businessId:Number(this.config.storeId)};try{return await this.api.cancelNetsTerminalTransaction(r),this.abortController?.abort(),this.currentRequestId=null,!0}catch{return!1}}async refundTransaction(e,r){try{this.abortController=new AbortController;let t={amount:e.amountCents,businessId:Number(this.config.storeId),currency:e.currency,displayId:this.config.kioskId,referenceId:e.orderRef,options:{allowPinBypass:!0,transactionType:C.ReturnOfGoods}},{data:n}=await this.api.initiateNetsTerminalTransaction(t),s=n.connectCloudRequestId;if(!s)throw new Error("connectCloudRequestId is missing from response.");if(this.currentRequestId=s,this.abortController.signal.aborted)throw new Error("Aborted");r("REQUIRES_INPUT",{sessionId:s});let i=await this.waitForPaymentCompletion(s,e.orderRef,this.abortController.signal);return this.currentRequestId=null,i}catch(t){throw this.currentRequestId=null,t instanceof c||t instanceof c?t:new c("NETWORK_ERROR","Failed to refund Nets transaction",t)}}async verifyFinalStatus(e,r){try{let{data:t}=await this.api.getPaymentStatus({businessId:Number(this.config.storeId),orderId:e.orderRef,provider:this.paymentProvider,referenceId:r});return this.handleSuccess(t)}catch(t){throw new c("NETWORK_ERROR","Failed to verify final Nets status",t)}}handleSuccess(e){let r=e.status===N.Success,t={success:r,status:r?"SUCCESS":"FAILED",orderId:e.orderId,transaction:e.transaction};return e.transactionId&&(t.transactionId=e.transactionId),e.error?.code&&(t.errorCode=e.error.code),e.error?.message&&(t.errorMessage=e.error.message),t}};import{PaymentApi as D,PaymentProviderEnum as _,SimplePaymentStatus as g}from"@munchi_oy/core";var S=class{constructor(e,r,t){this.messaging=r;this.config=t;this.api=new D(void 0,"",e)}api;abortController=null;currentSessionId=null;paymentProvider=_.Viva;async processPayment(e,r){this.abortController=new AbortController,r("CONNECTING");let t={amount:e.amountCents,referenceId:e.orderRef,businessId:parseInt(this.config.storeId),currency:e.currency,displayId:e.displayId,showReceipt:!0,showTransactionResult:!0};try{let{data:n}=await this.api.initiateTerminalTransaction(t);if(this.currentSessionId=n.sessionId,this.abortController.signal.aborted)throw new Error("Aborted");r("REQUIRES_INPUT",{sessionId:n.sessionId});let s=await this.waitForPaymentCompletion(n.sessionId,e.orderRef,this.abortController.signal);return this.currentSessionId=null,s}catch(n){throw this.currentSessionId=null,n instanceof c?n:new c("NETWORK_ERROR","Failed to create Viva Intent",n)}}async waitForPaymentCompletion(e,r,t){let s=`viva.${this.config.channel.toLowerCase()}.requests.${e}`,i="payment:status-changed";return new Promise((u,a)=>{let o=!1,l=()=>{o=!0,typeof f=="function"&&f(),clearTimeout(I)},y=()=>{l(),a(new Error("Aborted"))};t.addEventListener("abort",y);let f=this.messaging.subscribe(s,i,p=>{o||(l(),t.removeEventListener("abort",y),u(this.handleSuccess(p)))}),I=setTimeout(async()=>{if(!(o||t.aborted))try{let p=await this.pollOrderStatus(e,r,this.config.storeId,t);u(this.handleSuccess(p))}catch(p){a(new c("TIMEOUT","Payment timed out and polling failed",p))}finally{t.removeEventListener("abort",y),l()}},1e4)})}async pollOrderStatus(e,r,t,n){let a=Date.now()+12e4;for(;Date.now()<a;){if(n.aborted)throw new Error("Aborted");try{let{data:o}=await this.api.getPaymentStatus({businessId:Number(t),orderId:r,provider:this.paymentProvider,referenceId:e});if(o.status!==g.Pending)return o}catch(o){throw new Error(`Payment verification failed: ${o instanceof Error?o.message:String(o)}`)}await new Promise(o=>setTimeout(o,2e3))}throw new Error("Payment verification timed out.")}handleSuccess(e){let r=e.status===g.Success,t={success:r,status:r?"SUCCESS":"FAILED",orderId:e.orderId,errorCode:e.error?.code??"",errorMessage:e.error?.message??""};return e.transactionId&&(t.transactionId=e.transactionId),e.error?.referenceError&&(t.errorReference=e.error.referenceError),e.transaction&&(t.transaction=e.transaction),t}async cancelTransaction(e){if(!this.currentSessionId)return!1;try{let r=this.currentSessionId;return this.currentSessionId=null,await this.api.cancelVivaTransactionV2({cashRegisterId:this.config.storeId,sessionId:r}),this.abortController?.abort(),!0}catch(r){throw this.currentSessionId=null,new c("NETWORK_ERROR","Failed to cancel Viva transaction",r)}}async verifyFinalStatus(e,r){try{let{data:t}=await this.api.getPaymentStatus({businessId:Number(this.config.storeId),orderId:e.orderRef,provider:this.paymentProvider,referenceId:r}),n=t.status===g.Success,s={success:n,status:n?"SUCCESS":"FAILED",orderId:t.orderId,errorCode:t.error?.code??"",errorMessage:t.error?.message??""};return t.transactionId&&(s.transactionId=t.transactionId),t.error?.referenceError&&(s.errorReference=t.error.referenceError),t.transaction&&(s.transaction=t.transaction),s}catch(t){throw new c("NETWORK_ERROR","Failed to verify final transaction status",t)}}async refundTransaction(e,r){try{let t={amount:e.amountCents,businessId:Number(this.config.storeId),displayId:this.config.kioskId,currency:e.currency,orderReferenceId:e.orderRef,referenceId:e.originalTransactionId},{data:n}=await this.api.refundSingleVivaTransaction(t),s=n.success;return{success:s,status:s?"SUCCESS":"FAILED",orderId:e.orderRef,transactionId:n.sessionId}}catch(t){throw new c("NETWORK_ERROR","Failed to refund Viva transaction",t)}}};var T=class m{strategy;axios;messaging;timeoutMs;logger;_currentState="IDLE";_listeners=[];_cancellationIntent=!1;_currentSessionId;_autoResetTimer;autoResetOptions;static TERMINAL_STATES=["SUCCESS","FAILED","INTERNAL_ERROR"];static RESTING_STATES=["IDLE",...m.TERMINAL_STATES];constructor(e,r,t,n={},s){this.axios=e,this.messaging=r,this.logger=n.logger,this.timeoutMs=n.timeoutMs||6e4,this.autoResetOptions=n.autoResetOnPaymentComplete,this.strategy=s??this.resolveStrategy(t)}get version(){return P}get currentState(){return this._currentState}generateErrorResult(e,r,t){return{success:!1,status:"ERROR",errorCode:this.normalizeErrorCode(r),errorMessage:t,orderId:e}}normalizeErrorCode(e){return e?e.includes(".")?e:{CANCELLED:d.PaymentCancelledByUser,DECLINED:d.PaymentDeclined,TERMINAL_BUSY:d.TerminalBusy,TERMINAL_OFFLINE:d.TerminalOffline,TIMEOUT:d.TerminalTimeout,NETWORK_ERROR:d.SystemProviderError,STRATEGY_ERROR:d.SystemProviderError,MISSING_CONFIG:d.SystemUnknown,INVALID_AMOUNT:d.SystemUnknown,UNKNOWN:d.SystemUnknown}[e]??d.SystemUnknown:d.SystemUnknown}subscribe=e=>(this._listeners.push(e),e(this._currentState),()=>{this._listeners=this._listeners.filter(r=>r!==e)});transitionTo(e){if(this._currentState===e)return;if(e==="IDLE"){this.cancelAutoReset(),this._currentState=e,this._listeners.forEach(t=>t(e));return}if(m.TERMINAL_STATES.includes(this._currentState)){let t=`Invalid State Transition: Attempted to move from terminal state ${this._currentState} to ${e}`;throw this.logger?.error(t),this._currentState!=="INTERNAL_ERROR"&&(this._currentState="INTERNAL_ERROR",this._listeners.forEach(n=>n(this._currentState))),new c("UNKNOWN",t)}this._currentState=e,m.TERMINAL_STATES.includes(e)&&this.scheduleAutoReset(e),this._listeners.forEach(t=>t(e))}_resetScheduledAt;get nextAutoResetAt(){return this._resetScheduledAt}cancelAutoReset(){this._autoResetTimer&&(clearTimeout(this._autoResetTimer),this._autoResetTimer=void 0),this._resetScheduledAt=void 0}scheduleAutoReset(e){if(!this.autoResetOptions)return;let t=e==="SUCCESS"?this.autoResetOptions.successDelayMs??5e3:this.autoResetOptions.failureDelayMs??5e3;this.logger?.info(`Scheduling auto-reset to IDLE in ${t}ms`),this._resetScheduledAt=Date.now()+t,this._autoResetTimer=setTimeout(()=>{this.logger?.info("Auto-reset triggered"),this.reset()},t)}resolveStrategy(e){return e.provider===L.Nets?new E(this.axios,this.messaging,e):new S(this.axios,this.messaging,e)}initiateTransaction=async(e,r)=>{let t=r??{},n=e.orderRef;if(!m.RESTING_STATES.includes(this._currentState))return this.generateErrorResult(e.orderRef,"UNKNOWN","A transaction is already in progress");let i=Date.now();if(this._cancellationIntent=!1,this._currentSessionId=void 0,this.transitionTo("IDLE"),e.amountCents<=0)return this.generateErrorResult(e.orderRef,"INVALID_AMOUNT","Amount must be greater than 0");try{let u=(y,f)=>{f?.sessionId&&(this._currentSessionId=f.sessionId),y!=="FAILED"&&(this.transitionTo(y),this.fireStateCallback(y,t,n))},a=this.strategy.processPayment(e,u),o=new Promise((y,f)=>{setTimeout(()=>{f(new c("TIMEOUT","Transaction timed out"))},this.timeoutMs)}),l=await Promise.race([a,o]);if(l.success)this.transitionTo("SUCCESS"),this.safeFireCallback(()=>t.onSuccess?.(l));else{if(this._cancellationIntent)return await this.handleTransactionError(e,new Error("Aborted after resolution"),t);this.transitionTo("FAILED"),this.safeFireCallback(()=>t.onError?.(l))}return this.logger?.info("Transaction completed successfully",{orderId:e.orderRef,durationMs:Date.now()-i}),l}catch(u){return this.logger?.warn("Transaction interrupted. Handling final status...",{error:u}),await this.handleTransactionError(e,u,t)}};async handleTransactionError(e,r,t={}){if(this._cancellationIntent||(this.transitionTo("VERIFYING"),this.safeFireCallback(()=>t.onVerifying?.({orderRef:e.orderRef,refPaymentId:this._currentSessionId}))),this._cancellationIntent){try{if(this._currentSessionId){let s=await this.strategy.verifyFinalStatus(e,this._currentSessionId);if(s.success)return this.transitionTo("SUCCESS"),this.safeFireCallback(()=>t.onSuccess?.(s)),s}}catch(s){this.logger?.warn("Final status verification failed during cancellation",{err:s})}return this.transitionTo("FAILED"),this.safeFireCallback(()=>t.onCancelled?.({orderRef:e.orderRef,refPaymentId:this._currentSessionId})),{success:!1,status:"CANCELLED",errorCode:this.normalizeErrorCode("CANCELLED"),orderId:e.orderRef,...this._currentSessionId?{transactionId:this._currentSessionId}:{}}}this.transitionTo("FAILED");let n;if(this._currentSessionId)try{n=await this.strategy.verifyFinalStatus(e,this._currentSessionId)}catch(s){this.logger?.warn("Failed to get detailed error from verifyFinalStatus",{verifyErr:s}),n=this.buildErrorResultFromException(e.orderRef,r)}else n=this.buildErrorResultFromException(e.orderRef,r);return this.safeFireCallback(()=>t.onError?.(n)),n}buildErrorResultFromException(e,r){return r instanceof c?this.generateErrorResult(e,r.code,r.message):this.generateErrorResult(e,"UNKNOWN",r instanceof Error?r.message:"Unknown fatal error")}fireStateCallback(e,r,t){let n={orderRef:t,refPaymentId:this._currentSessionId};switch(e){case"CONNECTING":this.safeFireCallback(()=>r.onConnecting?.(n));break;case"REQUIRES_INPUT":this.safeFireCallback(()=>r.onRequiresInput?.(n));break;case"PROCESSING":this.safeFireCallback(()=>r.onProcessing?.(n));break}}safeFireCallback(e){try{e()}catch(r){this.logger?.warn("Callback execution failed",{error:r})}}cancel=async()=>{if(this.logger?.info("Attempting cancellation"),m.TERMINAL_STATES.includes(this._currentState))return this.logger?.warn("Cannot cancel: Transaction already in terminal state",{state:this._currentState}),!1;this._cancellationIntent=!0,this.transitionTo("VERIFYING");try{let e=await this.strategy.cancelTransaction(r=>this.transitionTo(r));return!e&&this._currentState==="VERIFYING"&&this.transitionTo("IDLE"),e}catch(e){return this.logger?.error("Cancellation command failed",e),!1}};reset=()=>{m.TERMINAL_STATES.includes(this._currentState)&&this.transitionTo("IDLE")};refund=async(e,r)=>{let t=r??{};if(this.logger?.info("Initiating refund",{orderRef:e.orderRef}),this._currentSessionId=void 0,!m.RESTING_STATES.includes(this._currentState))return this.generateErrorResult(e.orderRef,"UNKNOWN","A transaction is already in progress");this.transitionTo("IDLE");try{let s=(u,a)=>{a?.sessionId&&(this._currentSessionId=a.sessionId),u!=="FAILED"&&(this.transitionTo(u),this.fireStateCallback(u,t,e.orderRef))},i=await this.strategy.refundTransaction(e,s);return i.success?(this.transitionTo("SUCCESS"),this.safeFireCallback(()=>t.onSuccess?.(i))):(this.transitionTo("FAILED"),this.safeFireCallback(()=>t.onError?.(i))),this.logger?.info("Refund completed",{success:i.success,orderRef:e.orderRef}),i}catch(s){this.logger?.error("Refund failed",s),this.transitionTo("FAILED");let i=this.generateErrorResult(e.orderRef,"UNKNOWN",s instanceof Error?s.message:"Refund failed");return this.safeFireCallback(()=>t.onError?.(i)),i}}};var F=(n=>(n.CONNECTING="CONNECTING",n.CONNECTED="CONNECTED",n.OFFLINE="OFFLINE",n.DISCONNECTED="DISCONNECTED",n))(F||{});export{F as AppReaderStatus,T as MunchiPaymentSDK,R as PaymentInteractionState,h as SdkPaymentStatus};
|
|
1
|
+
import{PaymentFailureCode as y,PaymentProvider as L}from"@munchi_oy/core";var P="1.2.8";var c=class m extends Error{code;rawError;constructor(e,r,t){super(r),this.name="PaymentSDKError",this.code=e,this.rawError=t,Object.setPrototypeOf(this,m.prototype)}};import{PaymentApi as A,PaymentEventType as w,PaymentProviderEnum as O,SimplePaymentStatus as N,TransactionType as C}from"@munchi_oy/core";var p=(i=>(i.PENDING="PENDING",i.SUCCESS="SUCCESS",i.APPROVED="APPROVED",i.FAILED="FAILED",i.CANCELLED="CANCELLED",i.ERROR="ERROR",i))(p||{}),R=(a=>(a.IDLE="IDLE",a.CONNECTING="CONNECTING",a.REQUIRES_INPUT="REQUIRES_INPUT",a.PROCESSING="PROCESSING",a.SUCCESS="SUCCESS",a.FAILED="FAILED",a.INTERNAL_ERROR="INTERNAL_ERROR",a.VERIFYING="VERIFYING",a))(R||{});var E=class{constructor(e,r,t){this.messaging=r;this.config=t;this.api=new A(void 0,"",e)}api;abortController=null;currentRequestId=null;paymentProvider=O.Nets;async processPayment(e,r){this.abortController=new AbortController,r("CONNECTING");let t={amount:e.amountCents,businessId:Number(this.config.storeId),referenceId:e.orderRef,currency:e.currency,displayId:e.displayId,options:{allowPinBypass:!0,transactionType:C.Purchase}};try{let{data:n}=await this.api.initiateNetsTerminalTransaction(t),s=n.connectCloudRequestId;if(!s)throw new Error("connectCloudRequestId is missing from response.");if(this.currentRequestId=s,this.abortController.signal.aborted)throw new Error("Aborted");r("REQUIRES_INPUT",{sessionId:s});let i=await this.waitForPaymentCompletion(s,e.orderRef,this.abortController.signal);return this.currentRequestId=null,i}catch(n){throw this.currentRequestId=null,n instanceof c?n:new c("TERMINAL_BUSY","Failed to create Nets Intent",n)}}async waitForPaymentCompletion(e,r,t){let n=`nets.requests.${e}`,s=w.StatusChanged;return new Promise((i,u)=>{let a=!1,o=()=>{a=!0,typeof l=="function"&&l(),clearTimeout(f)},d=()=>{o(),u(new c("CANCELLED","Transaction cancelled"))};t.addEventListener("abort",d);let l=this.messaging.subscribe(n,s,I=>{a||(o(),t.removeEventListener("abort",d),i(this.handleSuccess(I)))}),f=setTimeout(async()=>{if(!(a||t.aborted))try{let I=await this.pollOrderStatus(e,r,this.config.storeId,t);i(this.handleSuccess(I))}catch(I){u(new c("TIMEOUT","Payment timed out and polling failed",I))}finally{t.removeEventListener("abort",d),o()}},1e4)})}async pollOrderStatus(e,r,t,n){let a=Date.now()+12e4;for(;Date.now()<a;){if(n.aborted)throw new Error("Aborted");try{let{data:o}=await this.api.getPaymentStatus({businessId:Number(t),orderId:r,provider:this.paymentProvider,referenceId:e});if(n.aborted)throw new Error("Aborted");if(o.status!==N.Pending)return o}catch(o){if(o instanceof Error&&o.message==="Aborted")throw o}await new Promise(o=>{let d=()=>{clearTimeout(l),o(void 0)};n.addEventListener("abort",d,{once:!0});let l=setTimeout(()=>{n.removeEventListener("abort",d),o(void 0)},2e3)})}throw new Error("Payment verification timed out.")}async cancelTransaction(e){if(!this.currentRequestId)return!1;let r={requestId:this.currentRequestId,businessId:Number(this.config.storeId)};try{return await this.api.cancelNetsTerminalTransaction(r),this.abortController?.abort(),this.currentRequestId=null,!0}catch{return!1}}async refundTransaction(e,r){try{this.abortController=new AbortController;let t={amount:e.amountCents,businessId:Number(this.config.storeId),currency:e.currency,displayId:this.config.kioskId,referenceId:e.orderRef,options:{allowPinBypass:!0,transactionType:C.ReturnOfGoods}},{data:n}=await this.api.initiateNetsTerminalTransaction(t),s=n.connectCloudRequestId;if(!s)throw new Error("connectCloudRequestId is missing from response.");if(this.currentRequestId=s,this.abortController.signal.aborted)throw new Error("Aborted");r("REQUIRES_INPUT",{sessionId:s});let i=await this.waitForPaymentCompletion(s,e.orderRef,this.abortController.signal);return this.currentRequestId=null,i}catch(t){throw this.currentRequestId=null,t instanceof c||t instanceof c?t:new c("NETWORK_ERROR","Failed to refund Nets transaction",t)}}async verifyFinalStatus(e,r){try{let{data:t}=await this.api.getPaymentStatus({businessId:Number(this.config.storeId),orderId:e.orderRef,provider:this.paymentProvider,referenceId:r});return this.handleSuccess(t)}catch(t){throw new c("NETWORK_ERROR","Failed to verify final Nets status",t)}}handleSuccess(e){let r=e.status===N.Success,t={success:r,status:r?"SUCCESS":"FAILED",orderId:e.orderId,transaction:e.transaction};return e.transactionId&&(t.transactionId=e.transactionId),e.error?.code&&(t.errorCode=e.error.code),e.error?.message&&(t.errorMessage=e.error.message),t}abort(){this.abortController?.abort()}};import{PaymentApi as _,PaymentProviderEnum as D,SimplePaymentStatus as g}from"@munchi_oy/core";var S=class{constructor(e,r,t){this.messaging=r;this.config=t;this.api=new _(void 0,"",e)}api;abortController=null;currentSessionId=null;paymentProvider=D.Viva;async processPayment(e,r){this.abortController=new AbortController,r("CONNECTING");let t={amount:e.amountCents,referenceId:e.orderRef,businessId:parseInt(this.config.storeId),currency:e.currency,displayId:e.displayId,showReceipt:!0,showTransactionResult:!0};try{let{data:n}=await this.api.initiateTerminalTransaction(t);if(this.currentSessionId=n.sessionId,this.abortController.signal.aborted)throw new Error("Aborted");r("REQUIRES_INPUT",{sessionId:n.sessionId});let s=await this.waitForPaymentCompletion(n.sessionId,e.orderRef,this.abortController.signal);return this.currentSessionId=null,s}catch(n){throw this.currentSessionId=null,n instanceof c?n:new c("NETWORK_ERROR","Failed to create Viva Intent",n)}}async waitForPaymentCompletion(e,r,t){let s=`viva.${this.config.channel.toLowerCase()}.requests.${e}`,i="payment:status-changed";return new Promise((u,a)=>{let o=!1,d=()=>{o=!0,typeof f=="function"&&f(),clearTimeout(I)},l=()=>{d(),a(new Error("Aborted"))};t.addEventListener("abort",l);let f=this.messaging.subscribe(s,i,h=>{o||(d(),t.removeEventListener("abort",l),u(this.handleSuccess(h)))}),I=setTimeout(async()=>{if(!(o||t.aborted))try{let h=await this.pollOrderStatus(e,r,this.config.storeId,t);u(this.handleSuccess(h))}catch(h){a(new c("TIMEOUT","Payment timed out and polling failed",h))}finally{t.removeEventListener("abort",l),d()}},1e4)})}async pollOrderStatus(e,r,t,n){let a=Date.now()+12e4;for(;Date.now()<a;){if(n.aborted)throw new Error("Aborted");try{let{data:o}=await this.api.getPaymentStatus({businessId:Number(t),orderId:r,provider:this.paymentProvider,referenceId:e});if(n.aborted)throw new Error("Aborted");if(o.status!==g.Pending)return o}catch(o){if(o instanceof Error&&o.message==="Aborted")throw o}await new Promise(o=>{let d=()=>{clearTimeout(l),o(void 0)};n.addEventListener("abort",d,{once:!0});let l=setTimeout(()=>{n.removeEventListener("abort",d),o(void 0)},2e3)})}throw new Error("Payment verification timed out.")}handleSuccess(e){let r=e.status===g.Success,t={success:r,status:r?"SUCCESS":"FAILED",orderId:e.orderId,errorCode:e.error?.code??"",errorMessage:e.error?.message??""};return e.transactionId&&(t.transactionId=e.transactionId),e.error?.referenceError&&(t.errorReference=e.error.referenceError),e.transaction&&(t.transaction=e.transaction),t}async cancelTransaction(e){if(!this.currentSessionId)return!1;try{let r=this.currentSessionId;return this.currentSessionId=null,await this.api.cancelVivaTransactionV2({cashRegisterId:this.config.storeId,sessionId:r}),this.abortController?.abort(),!0}catch(r){throw this.currentSessionId=null,new c("NETWORK_ERROR","Failed to cancel Viva transaction",r)}}async verifyFinalStatus(e,r){try{let{data:t}=await this.api.getPaymentStatus({businessId:Number(this.config.storeId),orderId:e.orderRef,provider:this.paymentProvider,referenceId:r}),n=t.status===g.Success,s={success:n,status:n?"SUCCESS":"FAILED",orderId:t.orderId,errorCode:t.error?.code??"",errorMessage:t.error?.message??""};return t.transactionId&&(s.transactionId=t.transactionId),t.error?.referenceError&&(s.errorReference=t.error.referenceError),t.transaction&&(s.transaction=t.transaction),s}catch(t){throw new c("NETWORK_ERROR","Failed to verify final transaction status",t)}}async refundTransaction(e,r){try{let t={amount:e.amountCents,businessId:Number(this.config.storeId),displayId:this.config.kioskId,currency:e.currency,orderReferenceId:e.orderRef,referenceId:e.originalTransactionId},{data:n}=await this.api.refundSingleVivaTransaction(t),s=n.success;return{success:s,status:s?"SUCCESS":"FAILED",orderId:e.orderRef,transactionId:n.sessionId}}catch(t){throw new c("NETWORK_ERROR","Failed to refund Viva transaction",t)}}abort(){this.abortController?.abort()}};var T=class m{strategy;axios;messaging;timeoutMs;logger;_currentState="IDLE";_listeners=[];_cancellationIntent=!1;_currentSessionId;_autoResetTimer;autoResetOptions;static TERMINAL_STATES=["SUCCESS","FAILED","INTERNAL_ERROR"];static RESTING_STATES=["IDLE",...m.TERMINAL_STATES];constructor(e,r,t,n={},s){this.axios=e,this.messaging=r,this.logger=n.logger,this.timeoutMs=n.timeoutMs||3e4,this.autoResetOptions=n.autoResetOnPaymentComplete,this.strategy=s??this.resolveStrategy(t)}get version(){return P}get currentState(){return this._currentState}generateErrorResult(e,r,t){return{success:!1,status:"ERROR",errorCode:this.normalizeErrorCode(r),errorMessage:t,orderId:e}}normalizeErrorCode(e){return e?e.includes(".")?e:{CANCELLED:y.PaymentCancelledByUser,DECLINED:y.PaymentDeclined,TERMINAL_BUSY:y.TerminalBusy,TERMINAL_OFFLINE:y.TerminalOffline,TIMEOUT:y.TerminalTimeout,NETWORK_ERROR:y.SystemProviderError,STRATEGY_ERROR:y.SystemProviderError,MISSING_CONFIG:y.SystemUnknown,INVALID_AMOUNT:y.SystemUnknown,UNKNOWN:y.SystemUnknown}[e]??y.SystemUnknown:y.SystemUnknown}subscribe=e=>(this._listeners.push(e),e(this._currentState),()=>{this._listeners=this._listeners.filter(r=>r!==e)});transitionTo(e){if(this._currentState===e)return;if(e==="IDLE"){this.cancelAutoReset(),this._currentState=e,this._listeners.forEach(t=>t(e));return}if(m.TERMINAL_STATES.includes(this._currentState)){let t=`Invalid State Transition: Attempted to move from terminal state ${this._currentState} to ${e}`;throw this.logger?.error(t),this._currentState!=="INTERNAL_ERROR"&&(this._currentState="INTERNAL_ERROR",this._listeners.forEach(n=>n(this._currentState))),new c("UNKNOWN",t)}this._currentState=e,m.TERMINAL_STATES.includes(e)&&this.scheduleAutoReset(e),this._listeners.forEach(t=>t(e))}_resetScheduledAt;get nextAutoResetAt(){return this._resetScheduledAt}cancelAutoReset(){this._autoResetTimer&&(clearTimeout(this._autoResetTimer),this._autoResetTimer=void 0),this._resetScheduledAt=void 0}scheduleAutoReset(e){if(!this.autoResetOptions)return;let t=e==="SUCCESS"?this.autoResetOptions.successDelayMs??5e3:this.autoResetOptions.failureDelayMs??5e3;this.logger?.info(`Scheduling auto-reset to IDLE in ${t}ms`),this._resetScheduledAt=Date.now()+t,this._autoResetTimer=setTimeout(()=>{this.logger?.info("Auto-reset triggered"),this.reset()},t)}resolveStrategy(e){return e.provider===L.Nets?new E(this.axios,this.messaging,e):new S(this.axios,this.messaging,e)}initiateTransaction=async(e,r)=>{let t=r??{},n=e.orderRef;if(!m.RESTING_STATES.includes(this._currentState))return this.generateErrorResult(e.orderRef,"UNKNOWN","A transaction is already in progress");let i=Date.now();if(this._cancellationIntent=!1,this._currentSessionId=void 0,this.transitionTo("IDLE"),e.amountCents<=0)return this.generateErrorResult(e.orderRef,"INVALID_AMOUNT","Amount must be greater than 0");try{let u=(l,f)=>{f?.sessionId&&(this._currentSessionId=f.sessionId),l!=="FAILED"&&(this.transitionTo(l),this.fireStateCallback(l,t,n))},a=this.strategy.processPayment(e,u),o=new Promise((l,f)=>{setTimeout(()=>{f(new c("TIMEOUT","Transaction timed out"))},this.timeoutMs)}),d=await Promise.race([a,o]);if(d.success)this.transitionTo("SUCCESS"),this.safeFireCallback(()=>t.onSuccess?.(d));else{if(this._cancellationIntent)return await this.handleTransactionError(e,new Error("Aborted after resolution"),t);this.transitionTo("FAILED"),this.safeFireCallback(()=>t.onError?.(d))}return this.logger?.info("Transaction completed successfully",{orderId:e.orderRef,durationMs:Date.now()-i}),d}catch(u){return this.logger?.warn("Transaction interrupted. Handling final status...",{error:u}),await this.handleTransactionError(e,u,t)}};async handleTransactionError(e,r,t={}){if(this._cancellationIntent||(this.transitionTo("VERIFYING"),this.safeFireCallback(()=>t.onVerifying?.({orderRef:e.orderRef,refPaymentId:this._currentSessionId}))),this._cancellationIntent){try{if(this._currentSessionId){let s=await this.verifyWithRetry(e,this._currentSessionId);if(s.success)return this.transitionTo("SUCCESS"),this.safeFireCallback(()=>t.onSuccess?.(s)),s}}catch(s){this.logger?.warn("Final status verification failed during cancellation",{err:s})}return this.transitionTo("FAILED"),this.safeFireCallback(()=>t.onCancelled?.({orderRef:e.orderRef,refPaymentId:this._currentSessionId})),{success:!1,status:"CANCELLED",errorCode:this.normalizeErrorCode("CANCELLED"),orderId:e.orderRef,...this._currentSessionId?{transactionId:this._currentSessionId}:{}}}let n;if(this.strategy.abort(),this._currentSessionId)try{let s=await this.verifyWithRetry(e,this._currentSessionId);if(s.success)return this.transitionTo("SUCCESS"),this.safeFireCallback(()=>t.onSuccess?.(s)),s;n=s}catch(s){this.logger?.warn("Failed to get detailed error from verifyFinalStatus",{verifyErr:s}),n=this.buildErrorResultFromException(e.orderRef,s)}else n=this.buildErrorResultFromException(e.orderRef,r);return this.transitionTo("FAILED"),this.safeFireCallback(()=>t.onError?.(n)),n}static VERIFY_TIMEOUT_MS=1e4;static VERIFY_MAX_RETRIES=3;async verifyWithRetry(e,r){let t,n=!0;for(let u=1;u<=m.VERIFY_MAX_RETRIES;u++)try{return await Promise.race([this.strategy.verifyFinalStatus(e,r),new Promise((o,d)=>setTimeout(()=>d(new Error("Verify timed out")),m.VERIFY_TIMEOUT_MS))])}catch(a){t=a,a instanceof Error&&a.message==="Verify timed out"||(n=!1),this.logger?.warn(`verifyFinalStatus attempt ${u}/${m.VERIFY_MAX_RETRIES} failed`,{err:a})}let s=t instanceof Error?t.message:"Verify retries exhausted",i=n?y.PaymentTimeout:y.PaymentUnknown;throw new c(i,s)}buildErrorResultFromException(e,r){return r instanceof c?this.generateErrorResult(e,r.code,r.message):this.generateErrorResult(e,"UNKNOWN",r instanceof Error?r.message:"Unknown fatal error")}fireStateCallback(e,r,t){let n={orderRef:t,refPaymentId:this._currentSessionId};switch(e){case"CONNECTING":this.safeFireCallback(()=>r.onConnecting?.(n));break;case"REQUIRES_INPUT":this.safeFireCallback(()=>r.onRequiresInput?.(n));break;case"PROCESSING":this.safeFireCallback(()=>r.onProcessing?.(n));break}}safeFireCallback(e){try{e()}catch(r){this.logger?.warn("Callback execution failed",{error:r})}}cancel=async()=>{if(this.logger?.info("Attempting cancellation"),m.TERMINAL_STATES.includes(this._currentState))return this.logger?.warn("Cannot cancel: Transaction already in terminal state",{state:this._currentState}),!1;this._cancellationIntent=!0,this.transitionTo("VERIFYING");try{let e=await this.strategy.cancelTransaction(r=>this.transitionTo(r));return!e&&this._currentState==="VERIFYING"&&this.transitionTo("IDLE"),e}catch(e){return this.logger?.error("Cancellation command failed",e),!1}};reset=()=>{m.TERMINAL_STATES.includes(this._currentState)&&this.transitionTo("IDLE")};refund=async(e,r)=>{let t=r??{};if(this.logger?.info("Initiating refund",{orderRef:e.orderRef}),this._currentSessionId=void 0,!m.RESTING_STATES.includes(this._currentState))return this.generateErrorResult(e.orderRef,"UNKNOWN","A transaction is already in progress");this.transitionTo("IDLE");try{let s=(u,a)=>{a?.sessionId&&(this._currentSessionId=a.sessionId),u!=="FAILED"&&(this.transitionTo(u),this.fireStateCallback(u,t,e.orderRef))},i=await this.strategy.refundTransaction(e,s);return i.success?(this.transitionTo("SUCCESS"),this.safeFireCallback(()=>t.onSuccess?.(i))):(this.transitionTo("FAILED"),this.safeFireCallback(()=>t.onError?.(i))),this.logger?.info("Refund completed",{success:i.success,orderRef:e.orderRef}),i}catch(s){this.logger?.error("Refund failed",s),this.transitionTo("FAILED");let i=this.generateErrorResult(e.orderRef,"UNKNOWN",s instanceof Error?s.message:"Refund failed");return this.safeFireCallback(()=>t.onError?.(i)),i}}};var F=(n=>(n.CONNECTING="CONNECTING",n.CONNECTED="CONNECTED",n.OFFLINE="OFFLINE",n.DISCONNECTED="DISCONNECTED",n))(F||{});export{F as AppReaderStatus,T as MunchiPaymentSDK,R as PaymentInteractionState,p as SdkPaymentStatus};
|
|
@@ -31,6 +31,9 @@ export declare class MunchiPaymentSDK implements IMunchiPaymentSDK {
|
|
|
31
31
|
private resolveStrategy;
|
|
32
32
|
initiateTransaction: (params: SdkPaymentRequest, options?: TransactionOptions) => Promise<PaymentResult>;
|
|
33
33
|
private handleTransactionError;
|
|
34
|
+
private static readonly VERIFY_TIMEOUT_MS;
|
|
35
|
+
private static readonly VERIFY_MAX_RETRIES;
|
|
36
|
+
private verifyWithRetry;
|
|
34
37
|
private buildErrorResultFromException;
|
|
35
38
|
private fireStateCallback;
|
|
36
39
|
private safeFireCallback;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MunchiPaymentSDK.d.ts","sourceRoot":"","sources":["../../src/MunchiPaymentSDK.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AAG3C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AAGtE,OAAO,EACL,KAAK,iBAAiB,EACtB,KAAK,iBAAiB,EACtB,uBAAuB,EACvB,KAAK,aAAa,EAClB,KAAK,qBAAqB,EAC1B,KAAK,aAAa,EAClB,KAAK,cAAc,IAAI,iBAAiB,EAExC,KAAK,kBAAkB,EACxB,MAAM,iBAAiB,CAAC;AACzB,OAAO,KAAK,EAAW,UAAU,EAAE,MAAM,aAAa,CAAC;AAEvD,KAAK,aAAa,GAAG,CAAC,KAAK,EAAE,uBAAuB,KAAK,IAAI,CAAC;AAE9D,qBAAa,gBAAiB,YAAW,iBAAiB;IACxD,OAAO,CAAC,QAAQ,CAAmB;IACnC,OAAO,CAAC,KAAK,CAAgB;IAC7B,OAAO,CAAC,SAAS,CAAoB;IACrC,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,MAAM,CAAsB;IACpC,OAAO,CAAC,aAAa,CAAyD;IAC9E,OAAO,CAAC,UAAU,CAAuB;IACzC,OAAO,CAAC,mBAAmB,CAAS;IACpC,OAAO,CAAC,iBAAiB,CAAqB;IAC9C,OAAO,CAAC,eAAe,CAA4C;IACnE,OAAO,CAAC,gBAAgB,CAA2C;IAEnE,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,eAAe,CAIrC;IAEF,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,cAAc,CAGpC;gBAGA,KAAK,EAAE,aAAa,EACpB,SAAS,EAAE,iBAAiB,EAC5B,MAAM,EAAE,qBAAqB,EAC7B,OAAO,GAAE,UAAe,EACxB,QAAQ,CAAC,EAAE,gBAAgB;IAU7B,IAAW,OAAO,WAEjB;IAED,IAAW,YAAY,4BAEtB;IAED,OAAO,CAAC,mBAAmB;IAc3B,OAAO,CAAC,kBAAkB;IAoBnB,SAAS,GAAI,UAAU,aAAa,KAAG,CAAC,MAAM,IAAI,CAAC,CAOxD;IAEF,OAAO,CAAC,YAAY;IAmCpB,OAAO,CAAC,iBAAiB,CAAqB;IAE9C,IAAW,eAAe,IAAI,MAAM,GAAG,SAAS,CAE/C;IAED,OAAO,CAAC,eAAe;IAQvB,OAAO,CAAC,iBAAiB;IAqBzB,OAAO,CAAC,eAAe;IAUhB,mBAAmB,GACxB,QAAQ,iBAAiB,EACzB,UAAU,kBAAkB,KAC3B,OAAO,CAAC,aAAa,CAAC,CA0FvB;YAEY,sBAAsB;
|
|
1
|
+
{"version":3,"file":"MunchiPaymentSDK.d.ts","sourceRoot":"","sources":["../../src/MunchiPaymentSDK.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AAG3C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AAGtE,OAAO,EACL,KAAK,iBAAiB,EACtB,KAAK,iBAAiB,EACtB,uBAAuB,EACvB,KAAK,aAAa,EAClB,KAAK,qBAAqB,EAC1B,KAAK,aAAa,EAClB,KAAK,cAAc,IAAI,iBAAiB,EAExC,KAAK,kBAAkB,EACxB,MAAM,iBAAiB,CAAC;AACzB,OAAO,KAAK,EAAW,UAAU,EAAE,MAAM,aAAa,CAAC;AAEvD,KAAK,aAAa,GAAG,CAAC,KAAK,EAAE,uBAAuB,KAAK,IAAI,CAAC;AAE9D,qBAAa,gBAAiB,YAAW,iBAAiB;IACxD,OAAO,CAAC,QAAQ,CAAmB;IACnC,OAAO,CAAC,KAAK,CAAgB;IAC7B,OAAO,CAAC,SAAS,CAAoB;IACrC,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,MAAM,CAAsB;IACpC,OAAO,CAAC,aAAa,CAAyD;IAC9E,OAAO,CAAC,UAAU,CAAuB;IACzC,OAAO,CAAC,mBAAmB,CAAS;IACpC,OAAO,CAAC,iBAAiB,CAAqB;IAC9C,OAAO,CAAC,eAAe,CAA4C;IACnE,OAAO,CAAC,gBAAgB,CAA2C;IAEnE,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,eAAe,CAIrC;IAEF,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,cAAc,CAGpC;gBAGA,KAAK,EAAE,aAAa,EACpB,SAAS,EAAE,iBAAiB,EAC5B,MAAM,EAAE,qBAAqB,EAC7B,OAAO,GAAE,UAAe,EACxB,QAAQ,CAAC,EAAE,gBAAgB;IAU7B,IAAW,OAAO,WAEjB;IAED,IAAW,YAAY,4BAEtB;IAED,OAAO,CAAC,mBAAmB;IAc3B,OAAO,CAAC,kBAAkB;IAoBnB,SAAS,GAAI,UAAU,aAAa,KAAG,CAAC,MAAM,IAAI,CAAC,CAOxD;IAEF,OAAO,CAAC,YAAY;IAmCpB,OAAO,CAAC,iBAAiB,CAAqB;IAE9C,IAAW,eAAe,IAAI,MAAM,GAAG,SAAS,CAE/C;IAED,OAAO,CAAC,eAAe;IAQvB,OAAO,CAAC,iBAAiB;IAqBzB,OAAO,CAAC,eAAe;IAUhB,mBAAmB,GACxB,QAAQ,iBAAiB,EACzB,UAAU,kBAAkB,KAC3B,OAAO,CAAC,aAAa,CAAC,CA0FvB;YAEY,sBAAsB;IAiGpC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,iBAAiB,CAAS;IAClD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,kBAAkB,CAAK;YAEjC,eAAe;IAuC7B,OAAO,CAAC,6BAA6B;IAarC,OAAO,CAAC,iBAAiB;IAsBzB,OAAO,CAAC,gBAAgB;IAQjB,MAAM,QAAa,OAAO,CAAC,OAAO,CAAC,CAoCxC;IAEK,KAAK,QAAO,IAAI,CAIrB;IAEK,MAAM,GACX,QAAQ,aAAa,EACrB,UAAU,kBAAkB,KAC3B,OAAO,CAAC,aAAa,CAAC,CAkEvB;CACH"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"IPaymentStrategy.d.ts","sourceRoot":"","sources":["../../../src/strategies/IPaymentStrategy.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,uBAAuB,EACvB,cAAc,EACd,aAAa,EACb,aAAa,EACd,MAAM,kBAAkB,CAAC;AAE1B,MAAM,WAAW,gBAAgB;IAE/B,cAAc,CACZ,OAAO,EAAE,cAAc,EACvB,aAAa,EAAE,CAAC,KAAK,EAAE,uBAAuB,EAAE,MAAM,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,GACvF,OAAO,CAAC,aAAa,CAAC,CAAC;IAC1B,iBAAiB,CACf,aAAa,EAAE,CAAC,KAAK,EAAE,uBAAuB,KAAK,IAAI,GACtD,OAAO,CAAC,OAAO,CAAC,CAAC;IACpB,iBAAiB,CACf,OAAO,EAAE,aAAa,EACtB,aAAa,EAAE,CAAC,KAAK,EAAE,uBAAuB,EAAE,MAAM,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,GACvF,OAAO,CAAC,aAAa,CAAC,CAAC;IAC1B,iBAAiB,CAAC,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"IPaymentStrategy.d.ts","sourceRoot":"","sources":["../../../src/strategies/IPaymentStrategy.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,uBAAuB,EACvB,cAAc,EACd,aAAa,EACb,aAAa,EACd,MAAM,kBAAkB,CAAC;AAE1B,MAAM,WAAW,gBAAgB;IAE/B,cAAc,CACZ,OAAO,EAAE,cAAc,EACvB,aAAa,EAAE,CAAC,KAAK,EAAE,uBAAuB,EAAE,MAAM,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,GACvF,OAAO,CAAC,aAAa,CAAC,CAAC;IAC1B,iBAAiB,CACf,aAAa,EAAE,CAAC,KAAK,EAAE,uBAAuB,KAAK,IAAI,GACtD,OAAO,CAAC,OAAO,CAAC,CAAC;IACpB,iBAAiB,CACf,OAAO,EAAE,aAAa,EACtB,aAAa,EAAE,CAAC,KAAK,EAAE,uBAAuB,EAAE,MAAM,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,GACvF,OAAO,CAAC,aAAa,CAAC,CAAC;IAC1B,iBAAiB,CAAC,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;IACtF,KAAK,IAAI,IAAI,CAAC;CACf"}
|
|
@@ -9,5 +9,6 @@ export declare class MockStrategy implements IPaymentStrategy {
|
|
|
9
9
|
refundTransaction(request: RefundRequest, _onStateChange: (state: PaymentInteractionState, detail?: {
|
|
10
10
|
sessionId?: string;
|
|
11
11
|
}) => void): Promise<PaymentResult>;
|
|
12
|
+
abort(): void;
|
|
12
13
|
}
|
|
13
14
|
//# sourceMappingURL=MockStrategy.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MockStrategy.d.ts","sourceRoot":"","sources":["../../../src/strategies/MockStrategy.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,uBAAuB,EAC5B,KAAK,cAAc,EACnB,KAAK,aAAa,EAClB,KAAK,aAAa,EAEnB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAE3D,qBAAa,YAAa,YAAW,gBAAgB;IAG7C,cAAc,CAClB,OAAO,EAAE,cAAc,EACvB,cAAc,EAAE,CAAC,KAAK,EAAE,uBAAuB,EAAE,MAAM,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,GACxF,OAAO,CAAC,aAAa,CAAC;IAYnB,iBAAiB,CACrB,cAAc,EAAE,CAAC,KAAK,EAAE,uBAAuB,KAAK,IAAI,GACvD,OAAO,CAAC,OAAO,CAAC;IAKb,iBAAiB,CAAC,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC;IAQrF,iBAAiB,CACrB,OAAO,EAAE,aAAa,EACtB,cAAc,EAAE,CAAC,KAAK,EAAE,uBAAuB,EAAE,MAAM,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,GACxF,OAAO,CAAC,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"MockStrategy.d.ts","sourceRoot":"","sources":["../../../src/strategies/MockStrategy.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,uBAAuB,EAC5B,KAAK,cAAc,EACnB,KAAK,aAAa,EAClB,KAAK,aAAa,EAEnB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAE3D,qBAAa,YAAa,YAAW,gBAAgB;IAG7C,cAAc,CAClB,OAAO,EAAE,cAAc,EACvB,cAAc,EAAE,CAAC,KAAK,EAAE,uBAAuB,EAAE,MAAM,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,GACxF,OAAO,CAAC,aAAa,CAAC;IAYnB,iBAAiB,CACrB,cAAc,EAAE,CAAC,KAAK,EAAE,uBAAuB,KAAK,IAAI,GACvD,OAAO,CAAC,OAAO,CAAC;IAKb,iBAAiB,CAAC,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC;IAQrF,iBAAiB,CACrB,OAAO,EAAE,aAAa,EACtB,cAAc,EAAE,CAAC,KAAK,EAAE,uBAAuB,EAAE,MAAM,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,GACxF,OAAO,CAAC,aAAa,CAAC;IAUzB,KAAK,IAAI,IAAI;CAGd"}
|
|
@@ -20,5 +20,6 @@ export declare class NetsStrategy implements IPaymentStrategy {
|
|
|
20
20
|
}) => void): Promise<PaymentResult>;
|
|
21
21
|
verifyFinalStatus(request: PaymentRequest, sessionId: string): Promise<PaymentResult>;
|
|
22
22
|
private handleSuccess;
|
|
23
|
+
abort(): void;
|
|
23
24
|
}
|
|
24
25
|
//# sourceMappingURL=NetsStrategy.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NetsStrategy.d.ts","sourceRoot":"","sources":["../../../src/strategies/NetsStrategy.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"NetsStrategy.d.ts","sourceRoot":"","sources":["../../../src/strategies/NetsStrategy.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AAG3C,OAAO,EACL,KAAK,iBAAiB,EACtB,uBAAuB,EACvB,KAAK,cAAc,EACnB,KAAK,aAAa,EAClB,KAAK,qBAAqB,EAC1B,KAAK,aAAa,EAEnB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAE3D,qBAAa,YAAa,YAAW,gBAAgB;IAQjD,OAAO,CAAC,SAAS;IACjB,OAAO,CAAC,MAAM;IARhB,OAAO,CAAC,GAAG,CAAa;IACxB,OAAO,CAAC,eAAe,CAAgC;IACvD,OAAO,CAAC,gBAAgB,CAAuB;IAC/C,OAAO,CAAC,eAAe,CAA4B;gBAGjD,KAAK,EAAE,aAAa,EACZ,SAAS,EAAE,iBAAiB,EAC5B,MAAM,EAAE,qBAAqB;IAKjC,cAAc,CAClB,OAAO,EAAE,cAAc,EACvB,aAAa,EAAE,CACb,KAAK,EAAE,uBAAuB,EAC9B,MAAM,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,KAC5B,IAAI,GACR,OAAO,CAAC,aAAa,CAAC;YAwDX,wBAAwB;YAsExB,eAAe;IA+CvB,iBAAiB,CACrB,cAAc,EAAE,CAAC,KAAK,EAAE,uBAAuB,KAAK,IAAI,GACvD,OAAO,CAAC,OAAO,CAAC;IAkBb,iBAAiB,CACrB,OAAO,EAAE,aAAa,EACtB,aAAa,EAAE,CACb,KAAK,EAAE,uBAAuB,EAC9B,MAAM,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,KAC5B,IAAI,GACR,OAAO,CAAC,aAAa,CAAC;IAyDnB,iBAAiB,CACrB,OAAO,EAAE,cAAc,EACvB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,aAAa,CAAC;IAkBzB,OAAO,CAAC,aAAa;IAyBrB,KAAK,IAAI,IAAI;CAGd"}
|
|
@@ -20,5 +20,6 @@ export declare class VivaStrategy implements IPaymentStrategy {
|
|
|
20
20
|
refundTransaction(request: RefundRequest, _onStateChange: (state: PaymentInteractionState, detail?: {
|
|
21
21
|
sessionId?: string;
|
|
22
22
|
}) => void): Promise<PaymentResult>;
|
|
23
|
+
abort(): void;
|
|
23
24
|
}
|
|
24
25
|
//# sourceMappingURL=VivaStrategy.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VivaStrategy.d.ts","sourceRoot":"","sources":["../../../src/strategies/VivaStrategy.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AAG3C,OAAO,EACL,KAAK,iBAAiB,EACtB,uBAAuB,EACvB,KAAK,cAAc,EACnB,KAAK,aAAa,EAClB,KAAK,qBAAqB,EAC1B,KAAK,aAAa,EAEnB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAE3D,qBAAa,YAAa,YAAW,gBAAgB;IAOjD,OAAO,CAAC,SAAS;IACjB,OAAO,CAAC,MAAM;IAPhB,OAAO,CAAC,GAAG,CAAa;IACxB,OAAO,CAAC,eAAe,CAAgC;IACvD,OAAO,CAAC,gBAAgB,CAAuB;IAC/C,OAAO,CAAC,eAAe,CAA4B;gBAEjD,KAAK,EAAE,aAAa,EACZ,SAAS,EAAE,iBAAiB,EAC5B,MAAM,EAAE,qBAAqB;IAOjC,cAAc,CAClB,OAAO,EAAE,cAAc,EACvB,aAAa,EAAE,CAAC,KAAK,EAAE,uBAAuB,EAAE,MAAM,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,GACvF,OAAO,CAAC,aAAa,CAAC;YA2CX,wBAAwB;YAmExB,eAAe;
|
|
1
|
+
{"version":3,"file":"VivaStrategy.d.ts","sourceRoot":"","sources":["../../../src/strategies/VivaStrategy.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AAG3C,OAAO,EACL,KAAK,iBAAiB,EACtB,uBAAuB,EACvB,KAAK,cAAc,EACnB,KAAK,aAAa,EAClB,KAAK,qBAAqB,EAC1B,KAAK,aAAa,EAEnB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAE3D,qBAAa,YAAa,YAAW,gBAAgB;IAOjD,OAAO,CAAC,SAAS;IACjB,OAAO,CAAC,MAAM;IAPhB,OAAO,CAAC,GAAG,CAAa;IACxB,OAAO,CAAC,eAAe,CAAgC;IACvD,OAAO,CAAC,gBAAgB,CAAuB;IAC/C,OAAO,CAAC,eAAe,CAA4B;gBAEjD,KAAK,EAAE,aAAa,EACZ,SAAS,EAAE,iBAAiB,EAC5B,MAAM,EAAE,qBAAqB;IAOjC,cAAc,CAClB,OAAO,EAAE,cAAc,EACvB,aAAa,EAAE,CAAC,KAAK,EAAE,uBAAuB,EAAE,MAAM,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,GACvF,OAAO,CAAC,aAAa,CAAC;YA2CX,wBAAwB;YAmExB,eAAe;IAiD7B,OAAO,CAAC,aAAa;IA4Bf,iBAAiB,CACrB,cAAc,EAAE,CAAC,KAAK,EAAE,uBAAuB,KAAK,IAAI,GACvD,OAAO,CAAC,OAAO,CAAC;IA0Bb,iBAAiB,CAAC,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC;IA2CrF,iBAAiB,CACrB,OAAO,EAAE,aAAa,EACtB,cAAc,EAAE,CAAC,KAAK,EAAE,uBAAuB,EAAE,MAAM,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,GACxF,OAAO,CAAC,aAAa,CAAC;IAgCzB,KAAK,IAAI,IAAI;CAGd"}
|
package/dist/src/version.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const VERSION = "1.2.
|
|
1
|
+
export declare const VERSION = "1.2.8";
|
|
2
2
|
//# sourceMappingURL=version.d.ts.map
|
package/package.json
CHANGED
package/src/MunchiPaymentSDK.ts
CHANGED
|
@@ -54,7 +54,7 @@ export class MunchiPaymentSDK implements IMunchiPaymentSDK {
|
|
|
54
54
|
this.axios = axios;
|
|
55
55
|
this.messaging = messaging;
|
|
56
56
|
this.logger = options.logger;
|
|
57
|
-
this.timeoutMs = options.timeoutMs ||
|
|
57
|
+
this.timeoutMs = options.timeoutMs || 30000;
|
|
58
58
|
this.autoResetOptions = options.autoResetOnPaymentComplete;
|
|
59
59
|
this.strategy = strategy ?? this.resolveStrategy(config);
|
|
60
60
|
}
|
|
@@ -303,7 +303,7 @@ export class MunchiPaymentSDK implements IMunchiPaymentSDK {
|
|
|
303
303
|
if (this._cancellationIntent) {
|
|
304
304
|
try {
|
|
305
305
|
if (this._currentSessionId) {
|
|
306
|
-
const finalStatus = await this.
|
|
306
|
+
const finalStatus = await this.verifyWithRetry(
|
|
307
307
|
params,
|
|
308
308
|
this._currentSessionId,
|
|
309
309
|
);
|
|
@@ -340,16 +340,25 @@ export class MunchiPaymentSDK implements IMunchiPaymentSDK {
|
|
|
340
340
|
};
|
|
341
341
|
}
|
|
342
342
|
|
|
343
|
-
this.transitionTo(PaymentInteractionState.FAILED);
|
|
344
|
-
|
|
345
343
|
let errorResult: PaymentResult;
|
|
346
344
|
|
|
345
|
+
// If we're here, something went wrong (timeout, user cancel, error).
|
|
346
|
+
// Ensure we stop any ongoing strategy work (polling, etc.) to prevent leaks.
|
|
347
|
+
this.strategy.abort();
|
|
348
|
+
|
|
347
349
|
if (this._currentSessionId) {
|
|
348
350
|
try {
|
|
349
|
-
const finalStatus = await this.
|
|
351
|
+
const finalStatus = await this.verifyWithRetry(
|
|
350
352
|
params,
|
|
351
353
|
this._currentSessionId,
|
|
352
354
|
);
|
|
355
|
+
|
|
356
|
+
if (finalStatus.success) {
|
|
357
|
+
this.transitionTo(PaymentInteractionState.SUCCESS);
|
|
358
|
+
this.safeFireCallback(() => callbacks.onSuccess?.(finalStatus));
|
|
359
|
+
return finalStatus;
|
|
360
|
+
}
|
|
361
|
+
|
|
353
362
|
errorResult = finalStatus;
|
|
354
363
|
} catch (verifyErr) {
|
|
355
364
|
this.logger?.warn(
|
|
@@ -358,7 +367,7 @@ export class MunchiPaymentSDK implements IMunchiPaymentSDK {
|
|
|
358
367
|
);
|
|
359
368
|
errorResult = this.buildErrorResultFromException(
|
|
360
369
|
params.orderRef,
|
|
361
|
-
|
|
370
|
+
verifyErr,
|
|
362
371
|
);
|
|
363
372
|
}
|
|
364
373
|
} else {
|
|
@@ -368,10 +377,53 @@ export class MunchiPaymentSDK implements IMunchiPaymentSDK {
|
|
|
368
377
|
);
|
|
369
378
|
}
|
|
370
379
|
|
|
380
|
+
this.transitionTo(PaymentInteractionState.FAILED);
|
|
371
381
|
this.safeFireCallback(() => callbacks.onError?.(errorResult));
|
|
372
382
|
return errorResult;
|
|
373
383
|
}
|
|
374
384
|
|
|
385
|
+
private static readonly VERIFY_TIMEOUT_MS = 10000;
|
|
386
|
+
private static readonly VERIFY_MAX_RETRIES = 3;
|
|
387
|
+
|
|
388
|
+
private async verifyWithRetry(
|
|
389
|
+
params: SdkPaymentRequest,
|
|
390
|
+
sessionId: string,
|
|
391
|
+
): Promise<PaymentResult> {
|
|
392
|
+
let lastError: unknown;
|
|
393
|
+
let allTimeouts = true;
|
|
394
|
+
|
|
395
|
+
for (let attempt = 1; attempt <= MunchiPaymentSDK.VERIFY_MAX_RETRIES; attempt++) {
|
|
396
|
+
try {
|
|
397
|
+
const result = await Promise.race([
|
|
398
|
+
this.strategy.verifyFinalStatus(params, sessionId),
|
|
399
|
+
new Promise<never>((_, reject) =>
|
|
400
|
+
setTimeout(
|
|
401
|
+
() => reject(new Error("Verify timed out")),
|
|
402
|
+
MunchiPaymentSDK.VERIFY_TIMEOUT_MS,
|
|
403
|
+
),
|
|
404
|
+
),
|
|
405
|
+
]);
|
|
406
|
+
return result;
|
|
407
|
+
} catch (err) {
|
|
408
|
+
lastError = err;
|
|
409
|
+
const isTimeout = err instanceof Error && err.message === "Verify timed out";
|
|
410
|
+
if (!isTimeout) allTimeouts = false;
|
|
411
|
+
|
|
412
|
+
this.logger?.warn(
|
|
413
|
+
`verifyFinalStatus attempt ${attempt}/${MunchiPaymentSDK.VERIFY_MAX_RETRIES} failed`,
|
|
414
|
+
{ err },
|
|
415
|
+
);
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
const message = lastError instanceof Error ? lastError.message : "Verify retries exhausted";
|
|
420
|
+
const code = allTimeouts
|
|
421
|
+
? PaymentFailureCode.PaymentTimeout
|
|
422
|
+
: PaymentFailureCode.PaymentUnknown;
|
|
423
|
+
|
|
424
|
+
throw new PaymentSDKError(code as unknown as PaymentErrorCode, message);
|
|
425
|
+
}
|
|
426
|
+
|
|
375
427
|
private buildErrorResultFromException(
|
|
376
428
|
orderRef: string,
|
|
377
429
|
error: unknown,
|
|
@@ -19,4 +19,5 @@ export interface IPaymentStrategy {
|
|
|
19
19
|
onStateChange: (state: PaymentInteractionState, detail?: { sessionId?: string }) => void,
|
|
20
20
|
): Promise<PaymentResult>;
|
|
21
21
|
verifyFinalStatus(request: PaymentRequest, sessionId: string): Promise<PaymentResult>;
|
|
22
|
+
abort(): void;
|
|
22
23
|
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
2
|
type CreateNetsTerminalPaymentDto,
|
|
3
|
-
type NetsCancelPayloadDto,
|
|
4
3
|
type NetsCancelTransactionDto,
|
|
5
4
|
PaymentApi,
|
|
6
5
|
PaymentEventType,
|
|
@@ -194,13 +193,25 @@ export class NetsStrategy implements IPaymentStrategy {
|
|
|
194
193
|
referenceId: requestId,
|
|
195
194
|
});
|
|
196
195
|
|
|
196
|
+
if (signal.aborted) throw new Error("Aborted");
|
|
197
197
|
if (data.status !== SimplePaymentStatus.Pending) return data;
|
|
198
198
|
} catch (error) {
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
199
|
+
if (error instanceof Error && error.message === "Aborted") throw error;
|
|
200
|
+
// We ignore network errors during polling and keep trying until the deadline or abort signal.
|
|
201
|
+
// This prevents the SDK from prematurely transitioning to the VERIFYING state.
|
|
202
202
|
}
|
|
203
|
-
|
|
203
|
+
|
|
204
|
+
await new Promise((resolve) => {
|
|
205
|
+
const onAbort = () => {
|
|
206
|
+
clearTimeout(timeout);
|
|
207
|
+
resolve(undefined);
|
|
208
|
+
};
|
|
209
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
210
|
+
const timeout = setTimeout(() => {
|
|
211
|
+
signal.removeEventListener("abort", onAbort);
|
|
212
|
+
resolve(undefined);
|
|
213
|
+
}, INTERVAL_MS);
|
|
214
|
+
});
|
|
204
215
|
}
|
|
205
216
|
throw new Error("Payment verification timed out.");
|
|
206
217
|
}
|
|
@@ -333,4 +344,8 @@ export class NetsStrategy implements IPaymentStrategy {
|
|
|
333
344
|
|
|
334
345
|
return result;
|
|
335
346
|
}
|
|
347
|
+
|
|
348
|
+
abort(): void {
|
|
349
|
+
this.abortController?.abort();
|
|
350
|
+
}
|
|
336
351
|
}
|
|
@@ -174,11 +174,26 @@ export class VivaStrategy implements IPaymentStrategy {
|
|
|
174
174
|
referenceId: sessionId,
|
|
175
175
|
}
|
|
176
176
|
);
|
|
177
|
+
|
|
178
|
+
if (signal.aborted) throw new Error("Aborted");
|
|
177
179
|
if (data.status !== SimplePaymentStatus.Pending) return data;
|
|
178
180
|
} catch (error) {
|
|
179
|
-
|
|
181
|
+
if (error instanceof Error && error.message === "Aborted") throw error;
|
|
182
|
+
// We ignore network errors during polling and keep trying until the deadline or abort signal.
|
|
183
|
+
// This prevents the SDK from prematurely transitioning to the VERIFYING state.
|
|
180
184
|
}
|
|
181
|
-
|
|
185
|
+
|
|
186
|
+
await new Promise((resolve) => {
|
|
187
|
+
const onAbort = () => {
|
|
188
|
+
clearTimeout(timeout);
|
|
189
|
+
resolve(undefined);
|
|
190
|
+
};
|
|
191
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
192
|
+
const timeout = setTimeout(() => {
|
|
193
|
+
signal.removeEventListener("abort", onAbort);
|
|
194
|
+
resolve(undefined);
|
|
195
|
+
}, INTERVAL_MS);
|
|
196
|
+
});
|
|
182
197
|
}
|
|
183
198
|
throw new Error("Payment verification timed out.");
|
|
184
199
|
}
|
|
@@ -316,4 +331,8 @@ export class VivaStrategy implements IPaymentStrategy {
|
|
|
316
331
|
);
|
|
317
332
|
}
|
|
318
333
|
}
|
|
334
|
+
|
|
335
|
+
abort(): void {
|
|
336
|
+
this.abortController?.abort();
|
|
337
|
+
}
|
|
319
338
|
}
|
package/src/version.ts
CHANGED