@munchi_oy/payments 1.2.6 → 1.2.7
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/NetsStrategy.d.ts.map +1 -1
- 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 +54 -6
- package/src/strategies/NetsStrategy.ts +16 -5
- package/src/strategies/VivaStrategy.ts +17 -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.7";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}};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)}}};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._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.7";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}};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)}}};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._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;IA6FpC,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":"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;CAwBtB"}
|
|
@@ -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;CA+B1B"}
|
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.7";
|
|
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,21 @@ export class MunchiPaymentSDK implements IMunchiPaymentSDK {
|
|
|
340
340
|
};
|
|
341
341
|
}
|
|
342
342
|
|
|
343
|
-
this.transitionTo(PaymentInteractionState.FAILED);
|
|
344
|
-
|
|
345
343
|
let errorResult: PaymentResult;
|
|
346
344
|
|
|
347
345
|
if (this._currentSessionId) {
|
|
348
346
|
try {
|
|
349
|
-
const finalStatus = await this.
|
|
347
|
+
const finalStatus = await this.verifyWithRetry(
|
|
350
348
|
params,
|
|
351
349
|
this._currentSessionId,
|
|
352
350
|
);
|
|
351
|
+
|
|
352
|
+
if (finalStatus.success) {
|
|
353
|
+
this.transitionTo(PaymentInteractionState.SUCCESS);
|
|
354
|
+
this.safeFireCallback(() => callbacks.onSuccess?.(finalStatus));
|
|
355
|
+
return finalStatus;
|
|
356
|
+
}
|
|
357
|
+
|
|
353
358
|
errorResult = finalStatus;
|
|
354
359
|
} catch (verifyErr) {
|
|
355
360
|
this.logger?.warn(
|
|
@@ -358,7 +363,7 @@ export class MunchiPaymentSDK implements IMunchiPaymentSDK {
|
|
|
358
363
|
);
|
|
359
364
|
errorResult = this.buildErrorResultFromException(
|
|
360
365
|
params.orderRef,
|
|
361
|
-
|
|
366
|
+
verifyErr,
|
|
362
367
|
);
|
|
363
368
|
}
|
|
364
369
|
} else {
|
|
@@ -368,10 +373,53 @@ export class MunchiPaymentSDK implements IMunchiPaymentSDK {
|
|
|
368
373
|
);
|
|
369
374
|
}
|
|
370
375
|
|
|
376
|
+
this.transitionTo(PaymentInteractionState.FAILED);
|
|
371
377
|
this.safeFireCallback(() => callbacks.onError?.(errorResult));
|
|
372
378
|
return errorResult;
|
|
373
379
|
}
|
|
374
380
|
|
|
381
|
+
private static readonly VERIFY_TIMEOUT_MS = 10000;
|
|
382
|
+
private static readonly VERIFY_MAX_RETRIES = 3;
|
|
383
|
+
|
|
384
|
+
private async verifyWithRetry(
|
|
385
|
+
params: SdkPaymentRequest,
|
|
386
|
+
sessionId: string,
|
|
387
|
+
): Promise<PaymentResult> {
|
|
388
|
+
let lastError: unknown;
|
|
389
|
+
let allTimeouts = true;
|
|
390
|
+
|
|
391
|
+
for (let attempt = 1; attempt <= MunchiPaymentSDK.VERIFY_MAX_RETRIES; attempt++) {
|
|
392
|
+
try {
|
|
393
|
+
const result = await Promise.race([
|
|
394
|
+
this.strategy.verifyFinalStatus(params, sessionId),
|
|
395
|
+
new Promise<never>((_, reject) =>
|
|
396
|
+
setTimeout(
|
|
397
|
+
() => reject(new Error("Verify timed out")),
|
|
398
|
+
MunchiPaymentSDK.VERIFY_TIMEOUT_MS,
|
|
399
|
+
),
|
|
400
|
+
),
|
|
401
|
+
]);
|
|
402
|
+
return result;
|
|
403
|
+
} catch (err) {
|
|
404
|
+
lastError = err;
|
|
405
|
+
const isTimeout = err instanceof Error && err.message === "Verify timed out";
|
|
406
|
+
if (!isTimeout) allTimeouts = false;
|
|
407
|
+
|
|
408
|
+
this.logger?.warn(
|
|
409
|
+
`verifyFinalStatus attempt ${attempt}/${MunchiPaymentSDK.VERIFY_MAX_RETRIES} failed`,
|
|
410
|
+
{ err },
|
|
411
|
+
);
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
const message = lastError instanceof Error ? lastError.message : "Verify retries exhausted";
|
|
416
|
+
const code = allTimeouts
|
|
417
|
+
? PaymentFailureCode.PaymentTimeout
|
|
418
|
+
: PaymentFailureCode.PaymentUnknown;
|
|
419
|
+
|
|
420
|
+
throw new PaymentSDKError(code as unknown as PaymentErrorCode, message);
|
|
421
|
+
}
|
|
422
|
+
|
|
375
423
|
private buildErrorResultFromException(
|
|
376
424
|
orderRef: string,
|
|
377
425
|
error: unknown,
|
|
@@ -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
|
}
|
|
@@ -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
|
}
|
package/src/version.ts
CHANGED