@munchi_oy/payments 1.6.2 → 1.6.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 +0 -7
- package/dist/src/MunchiPaymentSDK.d.ts.map +1 -1
- package/dist/src/strategies/VivaAppToAppStrategy.d.ts.map +1 -1
- package/dist/src/strategies/VivaStrategy.d.ts +11 -0
- package/dist/src/strategies/VivaStrategy.d.ts.map +1 -1
- package/dist/src/types/payment.d.ts +1 -0
- package/dist/src/types/payment.d.ts.map +1 -1
- package/dist/src/version.d.ts +1 -1
- package/package.json +2 -2
- package/src/MunchiPaymentSDK.ts +17 -124
- package/src/strategies/VivaAppToAppStrategy.ts +7 -2
- package/src/strategies/VivaStrategy.ts +129 -18
- package/src/types/payment.ts +1 -0
- package/src/version.ts +1 -1
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var w=Object.defineProperty;var x=Object.getOwnPropertyDescriptor;var U=Object.getOwnPropertyNames;var k=Object.prototype.hasOwnProperty;var V=(u,e)=>{for(var r in e)w(u,r,{get:e[r],enumerable:!0})},W=(u,e,r,t)=>{if(e&&typeof e=="object"||typeof e=="function")for(let n of U(e))!k.call(u,n)&&n!==r&&w(u,n,{get:()=>e[n],enumerable:!(t=x(e,n))||t.enumerable});return u};var G=u=>W(w({},"__esModule",{value:!0}),u);var te={};V(te,{AppReaderStatus:()=>M,MunchiPaymentSDK:()=>N,PaymentInteractionState:()=>E,SdkPaymentStatus:()=>T});module.exports=G(te);var f=require("@munchi_oy/core");var O="1.6.2";var l=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 R=require("@munchi_oy/core");var T=(o=>(o.PENDING="PENDING",o.SUCCESS="SUCCESS",o.APPROVED="APPROVED",o.FAILED="FAILED",o.CANCELLED="CANCELLED",o.ERROR="ERROR",o))(T||{}),E=(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))(E||{});var S=class{constructor(e,r,t){this.messaging=r;this.config=t;this.api=new R.PaymentApi(void 0,"",e)}api;abortController=null;currentRequestId=null;paymentProvider=R.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:R.TransactionType.Purchase}};try{let{data:n}=await this.api.initiateNetsTerminalTransaction(t),i=n.connectCloudRequestId;if(!i)throw new Error("connectCloudRequestId is missing from response.");if(this.currentRequestId=i,this.abortController.signal.aborted)throw new Error("Aborted");r("REQUIRES_INPUT",{sessionId:i});let o=await this.waitForPaymentCompletion(i,e.orderRef,this.abortController.signal);return this.currentRequestId=null,o}catch(n){throw this.currentRequestId=null,n instanceof l?n:new l("TERMINAL_BUSY","Failed to create Nets Intent",n)}}async waitForPaymentCompletion(e,r,t){let n=`nets.requests.${e}`,i=R.PaymentEventType.StatusChanged;return new Promise((o,s)=>{let a=!1,c=()=>{a=!0,typeof m=="function"&&m(),clearTimeout(y)},d=()=>{c(),s(new l("CANCELLED","Transaction cancelled"))};t.addEventListener("abort",d);let m=this.messaging.subscribe(n,i,I=>{a||I.status===R.SimplePaymentStatus.Pending||(c(),t.removeEventListener("abort",d),o(this.handleSuccess(I)))}),y=setTimeout(async()=>{if(!(a||t.aborted))try{let I=await this.pollOrderStatus(e,r,this.config.storeId,t);o(this.handleSuccess(I))}catch(I){s(new l("TIMEOUT","Payment timed out and polling failed",I))}finally{t.removeEventListener("abort",d),c()}},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:c}=await this.api.getPaymentStatus({businessId:Number(t),orderId:r,provider:this.paymentProvider,referenceId:e});if(n.aborted)throw new Error("Aborted");if(c.status!==R.SimplePaymentStatus.Pending)return c}catch(c){if(c instanceof Error&&c.message==="Aborted")throw c}await new Promise(c=>{let d=()=>{clearTimeout(m),c(void 0)};n.addEventListener("abort",d,{once:!0});let m=setTimeout(()=>{n.removeEventListener("abort",d),c(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:R.TransactionType.ReturnOfGoods}},{data:n}=await this.api.initiateNetsTerminalTransaction(t),i=n.connectCloudRequestId;if(!i)throw new Error("connectCloudRequestId is missing from response.");if(this.currentRequestId=i,this.abortController.signal.aborted)throw new Error("Aborted");r("REQUIRES_INPUT",{sessionId:i});let o=await this.waitForPaymentCompletion(i,e.orderRef,this.abortController.signal);return this.currentRequestId=null,o}catch(t){throw this.currentRequestId=null,t instanceof l||t instanceof l?t:new l("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 l("NETWORK_ERROR","Failed to verify final Nets status",t)}}handleSuccess(e){let r=e.status===R.SimplePaymentStatus.Success,t=e.status===R.SimplePaymentStatus.Pending,n={success:r,status:r?"SUCCESS":t?"PENDING":"FAILED",orderId:e.orderId,transaction:e.transaction};return e.transactionId&&(n.transactionId=e.transactionId),e.error?.code&&(n.errorCode=e.error.code),e.error?.message&&(n.errorMessage=e.error.message),n}abort(){this.abortController?.abort()}};var p=require("@munchi_oy/core");var _=u=>{if(!u)return 0;let e=Number.parseInt(u,10);return Number.isFinite(e)?e:0},D=u=>{if(!u)return new Date().toISOString();let e=new Date(u);return Number.isNaN(e.getTime())?new Date().toISOString():e.toISOString()};var Y="vivapayclient://pay/v1",B="abort",$="cancel",j="sale",L="transactionDetails",Q=4e3,X="534287",z="38",H="493591",J="497439",Z=[H,J],ee=["CREDIT","PREPAID","CORPORATE"],v=class{constructor(e,r,t,n){this.config=t;this.appToAppConfig=n;this.api=new p.VivaApi(void 0,"",e),this.paymentApi=new p.PaymentApi(void 0,"",e),this.paymentApiWithReference=this.paymentApi}api;paymentApi;abortController=null;currentClientTransactionId=null;currentIsvClientTransactionId=null;currentCurrency=null;currentOrderRef=null;currentSourceTerminalId=null;latestResult=null;paymentApiWithReference;wasCancelled=!1;getExecutionMode(){return"callback_driven"}async processPayment(e,r){if(!this.appToAppConfig.enabled)throw new l("STRATEGY_ERROR","Viva app-to-app is not enabled");r("CONNECTING");try{let{data:t}=await this.api.createVivaAppToAppPayment(this.buildCreatePaymentPayload(e));return await this.runAction(j,this.buildLaunchParams(t),e,r)}catch(t){throw new l("NETWORK_ERROR","Failed to create Viva app-to-app payment",t)}}async cancelTransaction(e){if(!this.currentClientTransactionId)return!1;let r=await this.lookupTransactionDetails();if(r)return this.latestResult=r,this.wasCancelled=r.status==="CANCELLED",this.abortController?.abort(),!0;try{await this.appToAppConfig.adapter.openUrl(this.buildActionUrl(B,{}))}catch(t){throw this.abortController?.abort(),new l("NETWORK_ERROR","Failed to launch Viva abort flow",t)}return this.wasCancelled=!0,this.abortController?.abort(),!0}async refundTransaction(e,r){r("CONNECTING");let t=await this.fetchPaymentReference(e),n=this.buildRefundLaunchParams(e,t);return this.runAction($,n,e,r)}async verifyFinalStatus(e,r){return this.latestResult?this.latestResult:this.wasCancelled?{success:!1,status:"CANCELLED",orderId:this.currentOrderRef??r,errorCode:p.PaymentFailureCode.PaymentCancelledByUser,errorMessage:"Transaction was cancelled",transactionId:r}:{success:!1,status:"PENDING",orderId:this.currentOrderRef??r,transactionId:r}}abort(){this.abortController?.abort()}async runAction(e,r,t,n){if(!this.appToAppConfig.enabled)throw new l("STRATEGY_ERROR","Viva app-to-app is not enabled");this.abortController=new AbortController,this.latestResult=null,this.wasCancelled=!1,this.currentCurrency=t.currency,this.currentOrderRef=t.orderRef,this.currentSourceTerminalId=r.sourceTerminalId??r.tid??null,this.currentClientTransactionId=r.clientTransactionId??null,this.currentIsvClientTransactionId=r.ISV_clientTransactionId??null;let i=this.currentClientTransactionId?{sessionId:this.currentClientTransactionId}:void 0;return new Promise((o,s)=>{let a=(m,y)=>{m?.(),this.abortController?.signal.removeEventListener("abort",y)},c=()=>{a(d,c),s(new Error("Aborted"))},d=this.appToAppConfig.adapter.subscribe(m=>{if(this.abortController?.signal.aborted)return;let y=this.parseCallbackUrl(m);y&&this.matchesKnownTransactionId(y.rawParams)&&(this.latestResult=y.result,this.wasCancelled=y.result.status==="CANCELLED",this.currentClientTransactionId=null,this.currentIsvClientTransactionId=null,this.currentCurrency=null,this.currentOrderRef=null,this.currentSourceTerminalId=null,a(d,c),o(y.result))});this.abortController?.signal.addEventListener("abort",c),n("REQUIRES_INPUT",i),this.appToAppConfig.adapter.openUrl(this.buildActionUrl(e,r)).then(()=>{this.abortController?.signal.aborted}).catch(m=>{a(d,c),this.currentClientTransactionId=null,this.currentIsvClientTransactionId=null,this.currentCurrency=null,this.currentOrderRef=null,this.currentSourceTerminalId=null,s(new l("NETWORK_ERROR","Failed to launch Viva app",m))})})}buildCreatePaymentPayload(e){let r=e.options??{},t={amount:e.amountCents,referenceId:e.orderRef,businessId:Number(this.config.storeId),currency:e.currency,displayId:e.displayId,showReceipt:!1,showTransactionResult:!1};return r.tipAmount!==void 0&&(t.tipAmount=r.tipAmount),t}buildLaunchParams(e){let r={};return Object.entries(e).forEach(([t,n])=>{n!==void 0&&(r[t]=String(n))}),r}buildRefundLaunchParams(e,r){let t=e.options??{},n=r?.originalTransactionId?.trim()||e.originalTransactionId.trim(),i={amount:String(e.amountCents),clientTransactionId:n,show_receipt:"false",show_transaction_result:"false"};if(t.sourceCode&&(i.sourceCode=t.sourceCode),r?.referenceCode&&(i.orderCode=r.referenceCode),r?.shortReferenceCode&&(i.shortOrderCode=r.shortReferenceCode),r?.receiptNumber&&(i.referenceNumber=r.receiptNumber),r?.terminalId&&(i.tid=r.terminalId),r?.referenceCode||r?.shortReferenceCode||r?.receiptNumber)return i;let o=n;return/^\d+$/.test(o)&&(o.length===16?i.orderCode=o:o.length===10?i.shortOrderCode=o:i.referenceNumber=o),i}async fetchPaymentReference(e){try{let{data:r}=await this.paymentApiWithReference.getPaymentReference({orderId:e.orderRef,provider:p.PaymentProvider.Viva,transactionId:e.originalTransactionId});return this.extractPaymentReferenceContext(r)}catch{return}}extractPaymentReferenceContext(e){if(!e)return;let r=e.references;if(!r||typeof r!="object"||Array.isArray(r))return{originalTransactionId:e.originalTransactionId};let t=r,n={originalTransactionId:this.readReferenceValue(t.originalTransactionId)??this.readReferenceValue(t.clientTransactionId)??e.originalTransactionId},i=this.readReferenceValue(t.receiptNumber)??this.readReferenceValue(t.referenceNumber),o=this.readReferenceValue(t.referenceCode)??this.readReferenceValue(t.orderCode),s=this.readReferenceValue(t.shortReferenceCode)??this.readReferenceValue(t.shortOrderCode),a=this.readReferenceValue(t.terminalId)??this.readReferenceValue(t.tid);return i&&(n.receiptNumber=i),o&&(n.referenceCode=o),s&&(n.shortReferenceCode=s),a&&(n.terminalId=a),n}readReferenceValue(e){if(typeof e!="string")return;let r=e.trim();return r.length>0?r:void 0}buildActionUrl(e,r){let t=new URLSearchParams({action:e,appId:this.appToAppConfig.appId,callback:this.appToAppConfig.callbackUrl});return Object.entries(r).forEach(([n,i])=>{t.set(n,i)}),`${Y}?${t.toString()}`}parseCallbackUrl(e){if(!this.matchesCallbackBase(e))return null;let r=new URL(e),t=Object.fromEntries(r.searchParams.entries()),n=this.isSuccessfulCallback(t),i=this.isCancelledCallback(t),o=this.resolveFailureCode(t,i),s={success:n,status:n?"SUCCESS":i?"CANCELLED":"FAILED",orderId:this.currentOrderRef??t.clientTransactionId??"unknown",errorCode:n?"":o,errorMessage:n?"":t.message??"Transaction failed without error details"};n&&(s.transaction=this.mapCallbackToTransaction(t));let a=t.transactionId??t.orderCode??t.clientTransactionId,c=t.errorCode??this.extractErrorReferenceFromMessage(t.message)??t.referenceNumber??t.tid??t.rrn;return a&&(s.transactionId=a),c&&(s.errorReference=c),{rawParams:t,result:s}}matchesCallbackBase(e){let r=new URL(e),t=new URL(this.appToAppConfig.callbackUrl);return r.protocol===t.protocol&&r.hostname===t.hostname&&r.pathname===t.pathname}matchesKnownTransactionId(e){let r=[this.currentClientTransactionId,this.currentIsvClientTransactionId].filter(n=>!!n);if(r.length===0)return!0;let t=[e.clientTransactionId,e.ISV_clientTransactionId].filter(n=>!!n);return t.length===0?!0:t.some(n=>r.includes(n))}resolveFailureCode(e,r){if(r)return p.PaymentFailureCode.PaymentCancelledByUser;let t=e.errorCode;if(t?.includes("."))return t;if(this.isTransactionDetailsAction(e)&&(e.transactionType??"").toLowerCase().includes("abort"))return p.PaymentFailureCode.SystemUnknown;let n=(e.message??"").toLowerCase(),i=(e.status??"").toLowerCase();return n.includes("declined")||i==="declined"||i==="fail"||i==="failed"?p.PaymentFailureCode.PaymentDeclined:p.PaymentFailureCode.SystemUnknown}isSuccessfulCallback(e){let r=(e.status??"").toLowerCase();return this.isTransactionDetailsAction(e)?r!=="success"?!1:!(e.transactionType??"").toLowerCase().includes("abort"):r==="success"}isTransactionDetailsAction(e){return(e.action??"").toLowerCase()===L.toLowerCase()}isCancelledCallback(e){let r=(e.status??"").toLowerCase(),t=(e.message??"").toLowerCase(),n=(e.errorCode??"").toLowerCase();return r==="cancelled"||r==="canceled"||t.includes("user_cancel")||t.includes("user cancel")||n.includes("user_cancel")||n.includes("user cancel")}extractErrorReferenceFromMessage(e){return e?e.match(/\((-?\d+)\)/)?.[1]:void 0}mapCallbackToTransaction(e){let r=e.transactionId??e.orderCode??e.clientTransactionId??"unknown",t=D(e.transactionDate),n=_(e.amount),i=_(e.tipAmount);return{amount:n,cardDetail:{aid:e.aid??null,applicationLabel:e.applicationLabel??null,cardNumber:e.accountNumber??"N/A",cardType:e.cardType??"N/A",issuer:e.bankId??null,orderCode:e.orderCode??null,timestamp:t,transactionId:r},createdAt:t,fees:this.buildFeesFromTip(i),id:r,label:null,provider:p.PaymentProvider.Viva,rawData:e,referenceId:e.clientTransactionId??null,roundingDifference:0,type:this.getVivaPaymentMethodType(e)}}getVivaPaymentMethodType(e){let r=(e.applicationLabel??"").toUpperCase().trim(),n=(e.accountNumber??"").substring(0,6),i=(e.bankId??"").trim();return n===X||i===z?p.PaymentMethod.Edenred:Z.includes(n)?p.PaymentMethod.WoltBenefit:ee.some(o=>r.includes(o))?p.PaymentMethod.Credit:p.PaymentMethod.Debit}buildFeesFromTip(e){return!e||!this.currentCurrency?null:{parts:[{taxAmount:0,total:{amount:e,currency:this.currentCurrency},type:p.FeeType.TipAmount,vatPercentage:0}]}}async lookupTransactionDetails(){if(!this.currentClientTransactionId||!this.currentSourceTerminalId)return null;let e={clientTransactionId:this.currentClientTransactionId,sourceTerminalId:this.currentSourceTerminalId};return new Promise(r=>{let t=!1,n=setTimeout(()=>{t||(t=!0,i(),r(null))},Q),i=this.appToAppConfig.adapter.subscribe(o=>{if(t)return;let s=this.parseCallbackUrl(o);s&&this.matchesKnownTransactionId(s.rawParams)&&(t=!0,clearTimeout(n),i(),r(s.result))});this.appToAppConfig.adapter.openUrl(this.buildActionUrl(L,e)).catch(()=>{t||(t=!0,clearTimeout(n),i(),r(null))})})}};var g=require("@munchi_oy/core");var P=class{constructor(e,r,t){this.messaging=r;this.config=t;this.api=new g.PaymentApi(void 0,"",e)}api;abortController=null;currentSessionId=null;paymentProvider=g.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 i=await this.waitForPaymentCompletion(n.sessionId,e.orderRef,this.abortController.signal);return this.currentSessionId=null,i}catch(n){throw this.currentSessionId=null,n instanceof l?n:new l("NETWORK_ERROR","Failed to create Viva Intent",n)}}async waitForPaymentCompletion(e,r,t){let i=`viva.${this.config.channel.toLowerCase()}.requests.${e}`,o="payment:status-changed";return new Promise((s,a)=>{let c=!1,d=()=>{c=!0,typeof y=="function"&&y(),clearTimeout(I)},m=()=>{d(),a(new Error("Aborted"))};t.addEventListener("abort",m);let y=this.messaging.subscribe(i,o,C=>{c||C.status!==g.SimplePaymentStatus.Pending&&(d(),t.removeEventListener("abort",m),s(this.handleSuccess(C)))}),I=setTimeout(async()=>{if(!(c||t.aborted))try{let C=await this.pollOrderStatus(e,r,this.config.storeId,t);s(this.handleSuccess(C))}catch(C){a(new l("TIMEOUT","Payment timed out and polling failed",C))}finally{t.removeEventListener("abort",m),d()}},1e4)})}async pollOrderStatus(e,r,t,n){let a=Date.now()+18e4;for(;Date.now()<a;){if(n.aborted)throw new Error("Aborted");try{let{data:c}=await this.api.getPaymentStatus({businessId:Number(t),orderId:r,provider:this.paymentProvider,referenceId:e});if(n.aborted)throw new Error("Aborted");if(c.status!==g.SimplePaymentStatus.Pending)return c}catch(c){if(c instanceof Error&&c.message==="Aborted")throw c}await new Promise(c=>{let d=()=>{clearTimeout(m),c(void 0)};n.addEventListener("abort",d,{once:!0});let m=setTimeout(()=>{n.removeEventListener("abort",d),c(void 0)},2e3)})}throw new Error("Payment verification timed out.")}handleSuccess(e){let r=e.status===g.SimplePaymentStatus.Success,t={success:r,status:r?"SUCCESS":"FAILED",orderId:e.orderId,errorCode:e.error?.code||(r?"":g.PaymentFailureCode.SystemUnknown),errorMessage:e.error?.message||(r?"":"Transaction failed without error details")};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 l("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.SimplePaymentStatus.Success,i=t.status===g.SimplePaymentStatus.Pending,o={success:n,status:n?"SUCCESS":i?"PENDING":"FAILED",orderId:t.orderId,errorCode:t.error?.code||(n||i?"":g.PaymentFailureCode.SystemUnknown),errorMessage:t.error?.message||(n||i?"":"Transaction failed without error details")};return t.transactionId&&(o.transactionId=t.transactionId),t.error?.referenceError&&(o.errorReference=t.error.referenceError),t.transaction&&(o.transaction=t.transaction),o}catch(t){throw new l("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),i=n.success;return{success:i,status:i?"SUCCESS":"FAILED",orderId:e.orderRef,transactionId:n.sessionId}}catch(t){throw new l("NETWORK_ERROR","Failed to refund Viva transaction",t)}}abort(){this.abortController?.abort()}};var h=require("@munchi_oy/core"),F=require("axios");var A=class{constructor(e,r,t){this.messaging=r;this.config=t;this.paymentApi=new h.PaymentApi(void 0,"",e),this.worldlineApi=new h.WorldlineApi(void 0,"",e)}paymentApi;worldlineApi;abortController=null;currentOperationId=null;paymentProvider=h.PaymentProviderEnum.Worldline;async processPayment(e,r){this.abortController=new AbortController,r("CONNECTING");let t={amount:e.amountCents,businessId:Number(this.config.storeId),currency:e.currency,displayId:e.displayId,referenceId:e.orderRef,showReceipt:!0,showTransactionResult:!0,...e.options&&"tipAmount"in e.options&&typeof e.options.tipAmount=="number"?{tipAmount:e.options.tipAmount}:{}};try{let n=await this.worldlineApi.createPayment(t),i=this.extractOperationId(n.data);if(this.currentOperationId=i,this.abortController.signal.aborted)throw new Error("Aborted");r("REQUIRES_INPUT",{sessionId:i});let o=await this.waitForPaymentCompletion(i,e.orderRef,h.PaymentEventType.StatusChanged,this.abortController.signal);return this.currentOperationId=null,o}catch(n){throw this.currentOperationId=null,n instanceof l?n:this.mapWorldlineRequestError(n,"Failed to create Worldline payment")}}async cancelTransaction(e){if(!this.currentOperationId)return!1;let r={businessId:Number(this.config.storeId),targetOperationId:this.currentOperationId};try{return await this.worldlineApi.cancelPayment(r),!0}catch(t){throw this.mapWorldlineRequestError(t,"Failed to cancel Worldline transaction")}}async refundTransaction(e,r){this.abortController=new AbortController,r("CONNECTING");let t={amount:e.amountCents,businessId:Number(this.config.storeId),currency:e.currency,displayId:e.displayId,orderReferenceId:e.orderRef,referenceId:e.originalTransactionId};try{let n=await this.worldlineApi.createRefund(t),i=this.extractOperationId(n.data);if(this.currentOperationId=i,this.abortController.signal.aborted)throw new Error("Aborted");r("REQUIRES_INPUT",{sessionId:i});let o=await this.waitForPaymentCompletion(i,e.orderRef,h.PaymentEventType.RefundStatusChanged,this.abortController.signal);return this.currentOperationId=null,o}catch(n){throw this.currentOperationId=null,n instanceof l?n:this.mapWorldlineRequestError(n,"Failed to refund Worldline transaction")}}async verifyFinalStatus(e,r){try{let{data:t}=await this.paymentApi.getPaymentStatus({businessId:Number(this.config.storeId),orderId:e.orderRef,provider:this.paymentProvider,referenceId:r});return this.handlePaymentStatus(t)}catch(t){throw this.mapWorldlineRequestError(t,"Failed to verify final Worldline status")}}abort(){this.abortController?.abort()}async waitForPaymentCompletion(e,r,t,n){let i=`worldline.requests.${e}`;return new Promise((o,s)=>{let a=!1,c=()=>{a=!0,typeof m=="function"&&m(),clearTimeout(y)},d=()=>{c(),s(new l("CANCELLED","Transaction cancelled"))};n.addEventListener("abort",d);let m=this.messaging.subscribe(i,t,I=>{a||I.status===h.SimplePaymentStatus.Pending||(c(),n.removeEventListener("abort",d),o(this.handlePaymentStatus(I)))}),y=setTimeout(async()=>{if(!(a||n.aborted))try{let I=await this.pollOrderStatus(e,r,this.config.storeId,n);o(this.handlePaymentStatus(I))}catch(I){s(new l("TIMEOUT","Payment timed out and polling failed",I))}finally{n.removeEventListener("abort",d),c()}},1e4)})}async pollOrderStatus(e,r,t,n){let s=Date.now()+12e4;for(;Date.now()<s;){if(n.aborted)throw new Error("Aborted");try{let{data:a}=await this.paymentApi.getPaymentStatus({businessId:Number(t),orderId:r,provider:this.paymentProvider,referenceId:e});if(n.aborted)throw new Error("Aborted");if(a.status!==h.SimplePaymentStatus.Pending)return a}catch(a){if(a instanceof Error&&a.message==="Aborted")throw a}await new Promise(a=>{let c=()=>{clearTimeout(d),a(void 0)};n.addEventListener("abort",c,{once:!0});let d=setTimeout(()=>{n.removeEventListener("abort",c),a(void 0)},2e3)})}throw new Error("Payment verification timed out.")}handlePaymentStatus(e){let r=e.status===h.SimplePaymentStatus.Success,t=e.status===h.SimplePaymentStatus.Pending,n={success:r,status:r?"SUCCESS":t?"PENDING":"FAILED",orderId:e.orderId,errorCode:e.error?.code||(r||t?"":h.PaymentFailureCode.SystemUnknown),errorMessage:e.error?.message||(r||t?"":"Transaction failed without error details")};return e.transactionId&&(n.transactionId=e.transactionId),e.error?.referenceError&&(n.errorReference=e.error.referenceError),e.transaction&&(n.transaction=e.transaction),n}extractOperationId(e){let r=typeof e=="object"&&e!==null&&"operationId"in e&&typeof e.operationId=="string"?e.operationId:null;if(!r)throw new Error("operationId is missing from response.");return r}mapWorldlineRequestError(e,r){if((0,F.isAxiosError)(e)){if(e.response?.status===400||e.response?.status===409)return new l("TERMINAL_BUSY",r,e);if(e.response?.status===404)return new l("TERMINAL_OFFLINE",r,e)}return new l("NETWORK_ERROR",r,e)}};var N=class u{static VIVA_TIMEOUT_CANCEL_TIMEOUT_MS=5e3;strategy;axios;messaging;provider;timeoutMs;logger;_currentState="IDLE";_listeners=[];_cancellationIntent=!1;_currentSessionId;_autoResetTimer;autoResetOptions;appToAppConfig;static TERMINAL_STATES=["SUCCESS","FAILED","INTERNAL_ERROR"];static RESTING_STATES=["IDLE",...u.TERMINAL_STATES];constructor(e,r,t,n={},i){this.axios=e,this.messaging=r,this.provider=t.provider,this.logger=n.logger,this.timeoutMs=n.timeoutMs||3e4,this.autoResetOptions=n.autoResetOnPaymentComplete,this.appToAppConfig=n.appToApp,this.strategy=i??this.resolveStrategy(t)}get version(){return O}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:f.PaymentFailureCode.PaymentCancelledByUser,DECLINED:f.PaymentFailureCode.PaymentDeclined,TERMINAL_BUSY:f.PaymentFailureCode.TerminalBusy,TERMINAL_OFFLINE:f.PaymentFailureCode.TerminalOffline,TIMEOUT:f.PaymentFailureCode.TerminalTimeout,NETWORK_ERROR:f.PaymentFailureCode.SystemProviderError,STRATEGY_ERROR:f.PaymentFailureCode.SystemProviderError,MISSING_CONFIG:f.PaymentFailureCode.SystemUnknown,INVALID_AMOUNT:f.PaymentFailureCode.SystemUnknown,UNKNOWN:f.PaymentFailureCode.SystemUnknown}[e]??f.PaymentFailureCode.SystemUnknown:f.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 l("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){switch(e.provider){case f.PaymentProvider.Nets:return new S(this.axios,this.messaging,e);case f.PaymentProvider.Worldline:return new A(this.axios,this.messaging,e);case f.PaymentProvider.Viva:return this.appToAppConfig?.enabled?new v(this.axios,this.messaging,e,this.appToAppConfig):new P(this.axios,this.messaging,e);default:return new P(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 o=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 s=(m,y)=>{y?.sessionId&&(this._currentSessionId=y.sessionId),m!=="FAILED"&&(this.transitionTo(m),this.fireStateCallback(m,t,n))},a=this.strategy.processPayment(e,s),d=(this.strategy.getExecutionMode?.()??"managed")==="callback_driven"?await a:await Promise.race([a,new Promise((m,y)=>{setTimeout(()=>{y(new l("TIMEOUT","Transaction timed out"))},this.timeoutMs)})]);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()-o}),d}catch(s){return this.logger?.warn("Transaction interrupted. Handling final status...",{error:s}),await this.handleTransactionError(e,s,t)}};async handleTransactionError(e,r,t={}){let n=r instanceof l&&r.code==="TIMEOUT";if(!this._cancellationIntent&&!n&&(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 i,o=this._currentSessionId;if(n&&o&&this.provider===f.PaymentProvider.Viva&&!this.appToAppConfig?.enabled)try{let s=await this.recoverVivaTimeout(e,o);if(s.success)return this.transitionTo("SUCCESS"),this.safeFireCallback(()=>t.onSuccess?.(s)),s;i=s}catch(s){this.logger?.warn("Failed to get detailed error from verifyFinalStatus",{verifyErr:s}),i=this.buildErrorResultFromException(e.orderRef,s)}else if(o){this.strategy.abort();try{let s=await this.verifyWithRetry(e,o);if(s.success)return this.transitionTo("SUCCESS"),this.safeFireCallback(()=>t.onSuccess?.(s)),s;i=s}catch(s){this.logger?.warn("Failed to get detailed error from verifyFinalStatus",{verifyErr:s}),i=this.buildErrorResultFromException(e.orderRef,s)}}else this.strategy.abort(),i=this.buildErrorResultFromException(e.orderRef,r);return this.transitionTo("FAILED"),this.safeFireCallback(()=>t.onError?.(i)),i}async recoverVivaTimeout(e,r){let t;try{await Promise.race([this.strategy.cancelTransaction(()=>{}),new Promise((n,i)=>{t=setTimeout(()=>{i(new Error("Viva timeout cancellation timed out"))},u.VIVA_TIMEOUT_CANCEL_TIMEOUT_MS)})])}catch(n){this.logger?.warn("Viva timeout cancellation attempt failed",{error:n})}finally{t&&clearTimeout(t),this.strategy.abort()}return await this.verifyWithRetry(e,r)}static VERIFY_TIMEOUT_MS=1e4;static VERIFY_MAX_RETRIES=3;static VERIFY_RETRY_DELAY_MS=1500;async verifyWithRetry(e,r){let t,n=!0;for(let s=1;s<=u.VERIFY_MAX_RETRIES;s++)try{let a=await Promise.race([this.strategy.verifyFinalStatus(e,r),new Promise((c,d)=>setTimeout(()=>d(new Error("Verify timed out")),u.VERIFY_TIMEOUT_MS))]);if(a.status==="PENDING"){t=new Error("Verify returned pending status"),this.logger?.warn(`verifyFinalStatus attempt ${s}/${u.VERIFY_MAX_RETRIES} returned pending status`),s<u.VERIFY_MAX_RETRIES&&await new Promise(c=>setTimeout(c,u.VERIFY_RETRY_DELAY_MS));continue}return a}catch(a){t=a,a instanceof Error&&a.message==="Verify timed out"||(n=!1),this.logger?.warn(`verifyFinalStatus attempt ${s}/${u.VERIFY_MAX_RETRIES} failed`,{err:a}),s<u.VERIFY_MAX_RETRIES&&await new Promise(d=>setTimeout(d,u.VERIFY_RETRY_DELAY_MS))}let i=t instanceof Error?t.message:"Verify retries exhausted",o=n?f.PaymentFailureCode.PaymentTimeout:f.PaymentFailureCode.PaymentUnknown;throw new l(o,i)}buildErrorResultFromException(e,r){return r instanceof l?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;if(!this._currentSessionId&&this._currentState==="IDLE")return this.logger?.warn("Cannot cancel: No active session to cancel",{state:this._currentState}),!1;this._cancellationIntent=!0,this.transitionTo("VERIFYING");try{return await this.strategy.cancelTransaction(r=>this.transitionTo(r))}catch(e){return this.logger?.error("Cancellation command failed",e),!1}};reset=()=>{u.TERMINAL_STATES.includes(this._currentState)&&(this._currentSessionId=void 0,this._cancellationIntent=!1,this.transitionTo("IDLE"))};refund=async(e,r)=>{let t=r??{};if(this.logger?.info("Initiating refund",{orderRef:e.orderRef}),this._currentSessionId=void 0,this._cancellationIntent=!1,!u.RESTING_STATES.includes(this._currentState))return this.generateErrorResult(e.orderRef,"UNKNOWN","A transaction is already in progress");this.transitionTo("IDLE");try{let i=(s,a)=>{a?.sessionId&&(this._currentSessionId=a.sessionId),s!=="FAILED"&&(this.transitionTo(s),this.fireStateCallback(s,t,e.orderRef))},o=await this.strategy.refundTransaction(e,i);return o.success?(this.transitionTo("SUCCESS"),this.safeFireCallback(()=>t.onSuccess?.(o))):this._cancellationIntent||o.status==="CANCELLED"?(this.transitionTo("FAILED"),this.safeFireCallback(()=>t.onCancelled?.({orderRef:e.orderRef,refPaymentId:this._currentSessionId}))):(this.transitionTo("FAILED"),this.safeFireCallback(()=>t.onError?.(o))),this.logger?.info("Refund completed",{success:o.success,orderRef:e.orderRef}),o}catch(i){if(this.logger?.error("Refund failed",i),this._cancellationIntent){try{if(this._currentSessionId){let s=await this.verifyWithRetry(e,this._currentSessionId);return s.success?(this.transitionTo("SUCCESS"),this.safeFireCallback(()=>t.onSuccess?.(s)),s):s.status==="CANCELLED"?(this.transitionTo("FAILED"),this.safeFireCallback(()=>t.onCancelled?.({orderRef:e.orderRef,refPaymentId:this._currentSessionId})),s):(this.transitionTo("FAILED"),this.safeFireCallback(()=>t.onError?.(s)),s)}}catch(s){this.logger?.warn("Refund final status verification failed",{verifyError: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 o=this.generateErrorResult(e.orderRef,"UNKNOWN",i instanceof Error?i.message:"Refund failed");return this.safeFireCallback(()=>t.onError?.(o)),o}}};var M=(n=>(n.CONNECTING="CONNECTING",n.CONNECTED="CONNECTED",n.OFFLINE="OFFLINE",n.DISCONNECTED="DISCONNECTED",n))(M||{});0&&(module.exports={AppReaderStatus,MunchiPaymentSDK,PaymentInteractionState,SdkPaymentStatus});
|
|
1
|
+
"use strict";var _=Object.defineProperty;var U=Object.getOwnPropertyDescriptor;var k=Object.getOwnPropertyNames;var V=Object.prototype.hasOwnProperty;var W=(l,e)=>{for(var n in e)_(l,n,{get:e[n],enumerable:!0})},G=(l,e,n,t)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of k(e))!V.call(l,r)&&r!==n&&_(l,r,{get:()=>e[r],enumerable:!(t=U(e,r))||t.enumerable});return l};var q=l=>G(_({},"__esModule",{value:!0}),l);var ne={};W(ne,{AppReaderStatus:()=>x,MunchiPaymentSDK:()=>w,PaymentInteractionState:()=>P,SdkPaymentStatus:()=>E});module.exports=q(ne);var y=require("@munchi_oy/core");var D="1.6.8";var c=class l extends Error{code;rawError;constructor(e,n,t){super(n),this.name="PaymentSDKError",this.code=e,this.rawError=t,Object.setPrototypeOf(this,l.prototype)}};var R=require("@munchi_oy/core");var E=(s=>(s.PENDING="PENDING",s.SUCCESS="SUCCESS",s.APPROVED="APPROVED",s.FAILED="FAILED",s.CANCELLED="CANCELLED",s.ERROR="ERROR",s))(E||{}),P=(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))(P||{});var b=class{constructor(e,n,t){this.messaging=n;this.config=t;this.api=new R.PaymentApi(void 0,"",e)}api;abortController=null;currentRequestId=null;paymentProvider=R.PaymentProviderEnum.Nets;async processPayment(e,n){this.abortController=new AbortController,n("CONNECTING");let t={amount:e.amountCents,businessId:Number(this.config.storeId),referenceId:e.orderRef,currency:e.currency,displayId:e.displayId,options:{allowPinBypass:!0,transactionType:R.TransactionType.Purchase}};try{let{data:r}=await this.api.initiateNetsTerminalTransaction(t),i=r.connectCloudRequestId;if(!i)throw new Error("connectCloudRequestId is missing from response.");if(this.currentRequestId=i,this.abortController.signal.aborted)throw new Error("Aborted");n("REQUIRES_INPUT",{sessionId:i});let s=await this.waitForPaymentCompletion(i,e.orderRef,this.abortController.signal);return this.currentRequestId=null,s}catch(r){throw this.currentRequestId=null,r instanceof c?r:new c("TERMINAL_BUSY","Failed to create Nets Intent",r)}}async waitForPaymentCompletion(e,n,t){let r=`nets.requests.${e}`,i=R.PaymentEventType.StatusChanged;return new Promise((s,o)=>{let a=!1,u=()=>{a=!0,typeof m=="function"&&m(),clearTimeout(f)},d=()=>{u(),o(new c("CANCELLED","Transaction cancelled"))};t.addEventListener("abort",d);let m=this.messaging.subscribe(r,i,h=>{a||h.status===R.SimplePaymentStatus.Pending||(u(),t.removeEventListener("abort",d),s(this.handleSuccess(h)))}),f=setTimeout(async()=>{if(!(a||t.aborted))try{let h=await this.pollOrderStatus(e,n,this.config.storeId,t);s(this.handleSuccess(h))}catch(h){o(new c("TIMEOUT","Payment timed out and polling failed",h))}finally{t.removeEventListener("abort",d),u()}},1e4)})}async pollOrderStatus(e,n,t,r){let a=Date.now()+12e4;for(;Date.now()<a;){if(r.aborted)throw new Error("Aborted");try{let{data:u}=await this.api.getPaymentStatus({businessId:Number(t),orderId:n,provider:this.paymentProvider,referenceId:e});if(r.aborted)throw new Error("Aborted");if(u.status!==R.SimplePaymentStatus.Pending)return u}catch(u){if(u instanceof Error&&u.message==="Aborted")throw u}await new Promise(u=>{let d=()=>{clearTimeout(m),u(void 0)};r.addEventListener("abort",d,{once:!0});let m=setTimeout(()=>{r.removeEventListener("abort",d),u(void 0)},2e3)})}throw new Error("Payment verification timed out.")}async cancelTransaction(e){if(!this.currentRequestId)return!1;let n={requestId:this.currentRequestId,businessId:Number(this.config.storeId)};try{return await this.api.cancelNetsTerminalTransaction(n),this.abortController?.abort(),this.currentRequestId=null,!0}catch{return!1}}async refundTransaction(e,n){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:R.TransactionType.ReturnOfGoods}},{data:r}=await this.api.initiateNetsTerminalTransaction(t),i=r.connectCloudRequestId;if(!i)throw new Error("connectCloudRequestId is missing from response.");if(this.currentRequestId=i,this.abortController.signal.aborted)throw new Error("Aborted");n("REQUIRES_INPUT",{sessionId:i});let s=await this.waitForPaymentCompletion(i,e.orderRef,this.abortController.signal);return this.currentRequestId=null,s}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,n){try{let{data:t}=await this.api.getPaymentStatus({businessId:Number(this.config.storeId),orderId:e.orderRef,provider:this.paymentProvider,referenceId:n});return this.handleSuccess(t)}catch(t){throw new c("NETWORK_ERROR","Failed to verify final Nets status",t)}}handleSuccess(e){let n=e.status===R.SimplePaymentStatus.Success,t=e.status===R.SimplePaymentStatus.Pending,r={success:n,status:n?"SUCCESS":t?"PENDING":"FAILED",orderId:e.orderId,transaction:e.transaction};return e.transactionId&&(r.transactionId=e.transactionId),e.error?.code&&(r.errorCode=e.error.code),e.error?.message&&(r.errorMessage=e.error.message),r}abort(){this.abortController?.abort()}};var p=require("@munchi_oy/core");var O=l=>{if(!l)return 0;let e=Number.parseInt(l,10);return Number.isFinite(e)?e:0},L=l=>{if(!l)return new Date().toISOString();let e=new Date(l);return Number.isNaN(e.getTime())?new Date().toISOString():e.toISOString()};var B="vivapayclient://pay/v1",j="abort",Q="cancel",$="sale",F="transactionDetails",X=4e3,z="534287",H="38",J="493591",Z="497439",ee=[J,Z],te=["CREDIT","PREPAID","CORPORATE"],A=class{constructor(e,n,t,r){this.config=t;this.appToAppConfig=r;this.api=new p.VivaApi(void 0,"",e),this.paymentApi=new p.PaymentApi(void 0,"",e),this.paymentApiWithReference=this.paymentApi}api;paymentApi;abortController=null;currentClientTransactionId=null;currentIsvClientTransactionId=null;currentCurrency=null;currentOrderRef=null;currentSourceTerminalId=null;latestResult=null;paymentApiWithReference;wasCancelled=!1;getExecutionMode(){return"callback_driven"}async processPayment(e,n){if(!this.appToAppConfig.enabled)throw new c("STRATEGY_ERROR","Viva app-to-app is not enabled");n("CONNECTING");try{let{data:t}=await this.api.createVivaAppToAppPayment(this.buildCreatePaymentPayload(e));return await this.runAction($,this.buildLaunchParams(t),e,n)}catch(t){throw new c("NETWORK_ERROR","Failed to create Viva app-to-app payment",t)}}async cancelTransaction(e){if(!this.currentClientTransactionId)return!1;let n=await this.lookupTransactionDetails();if(n)return this.latestResult=n,this.wasCancelled=n.status==="CANCELLED",this.abortController?.abort(),!0;try{await this.appToAppConfig.adapter.openUrl(this.buildActionUrl(j,{}))}catch(t){throw this.abortController?.abort(),new c("NETWORK_ERROR","Failed to launch Viva abort flow",t)}return this.wasCancelled=!0,this.abortController?.abort(),!0}async refundTransaction(e,n){n("CONNECTING");let t=await this.fetchPaymentReference(e),r=this.buildRefundLaunchParams(e,t);return this.runAction(Q,r,e,n)}async verifyFinalStatus(e,n){return this.latestResult?this.latestResult:this.wasCancelled?{success:!1,status:"CANCELLED",orderId:this.currentOrderRef??n,errorCode:p.PaymentFailureCode.PaymentCancelledByUser,errorMessage:"Transaction was cancelled",transactionId:n}:{success:!1,status:"PENDING",orderId:this.currentOrderRef??n,transactionId:n}}abort(){this.abortController?.abort()}async runAction(e,n,t,r){if(!this.appToAppConfig.enabled)throw new c("STRATEGY_ERROR","Viva app-to-app is not enabled");this.abortController=new AbortController,this.latestResult=null,this.wasCancelled=!1,this.currentCurrency=t.currency,this.currentOrderRef=t.orderRef,this.currentSourceTerminalId=n.sourceTerminalId??n.tid??null,this.currentClientTransactionId=n.clientTransactionId??null,this.currentIsvClientTransactionId=n.ISV_clientTransactionId??null;let i=this.currentClientTransactionId?{sessionId:this.currentClientTransactionId}:void 0;return new Promise((s,o)=>{let a=(m,f)=>{m?.(),this.abortController?.signal.removeEventListener("abort",f)},u=()=>{a(d,u),o(new Error("Aborted"))},d=this.appToAppConfig.adapter.subscribe(m=>{if(this.abortController?.signal.aborted)return;let f=this.parseCallbackUrl(m);f&&this.matchesKnownTransactionId(f.rawParams)&&(this.latestResult=f.result,this.wasCancelled=f.result.status==="CANCELLED",this.currentClientTransactionId=null,this.currentIsvClientTransactionId=null,this.currentCurrency=null,this.currentOrderRef=null,this.currentSourceTerminalId=null,a(d,u),s(f.result))});this.abortController?.signal.addEventListener("abort",u),r("REQUIRES_INPUT",i),this.appToAppConfig.adapter.openUrl(this.buildActionUrl(e,n)).then(()=>{this.abortController?.signal.aborted}).catch(m=>{a(d,u),this.currentClientTransactionId=null,this.currentIsvClientTransactionId=null,this.currentCurrency=null,this.currentOrderRef=null,this.currentSourceTerminalId=null,o(new c("NETWORK_ERROR","Failed to launch Viva app",m))})})}buildCreatePaymentPayload(e){let n=e.options??{},t={amount:e.amountCents,referenceId:e.orderRef,businessId:Number(this.config.storeId),currency:e.currency,displayId:e.displayId,showReceipt:!1,showTransactionResult:!1};return n.tipAmount!==void 0&&(t.tipAmount=n.tipAmount),t}buildLaunchParams(e){let n={};return Object.entries(e).forEach(([t,r])=>{r!==void 0&&(n[t]=String(r))}),n}buildRefundLaunchParams(e,n){let t=e.options??{},r=n?.originalTransactionId?.trim()||e.originalTransactionId.trim(),i={amount:String(e.amountCents),clientTransactionId:r,show_receipt:"false",show_transaction_result:"false"};if(t.sourceCode&&(i.sourceCode=t.sourceCode),n?.referenceCode&&(i.orderCode=n.referenceCode),n?.shortReferenceCode&&(i.shortOrderCode=n.shortReferenceCode),n?.receiptNumber&&(i.referenceNumber=n.receiptNumber),n?.terminalId&&(i.tid=n.terminalId),n?.referenceCode||n?.shortReferenceCode||n?.receiptNumber)return i;let s=r;return/^\d+$/.test(s)&&(s.length===16?i.orderCode=s:s.length===10?i.shortOrderCode=s:i.referenceNumber=s),i}async fetchPaymentReference(e){try{let n={orderId:e.orderRef,provider:p.PaymentProvider.Viva,transactionId:e.originalTransactionId,businessId:e.businessId},{data:t}=await this.paymentApiWithReference.getPaymentReference(n);return this.extractPaymentReferenceContext(t)}catch{return}}extractPaymentReferenceContext(e){if(!e)return;let n=e.references;if(!n||typeof n!="object"||Array.isArray(n))return{originalTransactionId:e.originalTransactionId};let t=n,r={originalTransactionId:this.readReferenceValue(t.originalTransactionId)??this.readReferenceValue(t.clientTransactionId)??e.originalTransactionId},i=this.readReferenceValue(t.receiptNumber)??this.readReferenceValue(t.referenceNumber),s=this.readReferenceValue(t.referenceCode)??this.readReferenceValue(t.orderCode),o=this.readReferenceValue(t.shortReferenceCode)??this.readReferenceValue(t.shortOrderCode),a=this.readReferenceValue(t.terminalId)??this.readReferenceValue(t.tid);return i&&(r.receiptNumber=i),s&&(r.referenceCode=s),o&&(r.shortReferenceCode=o),a&&(r.terminalId=a),r}readReferenceValue(e){if(typeof e!="string")return;let n=e.trim();return n.length>0?n:void 0}buildActionUrl(e,n){let t=new URLSearchParams({action:e,appId:this.appToAppConfig.appId,callback:this.appToAppConfig.callbackUrl});return Object.entries(n).forEach(([r,i])=>{t.set(r,i)}),`${B}?${t.toString()}`}parseCallbackUrl(e){if(!this.matchesCallbackBase(e))return null;let n=new URL(e),t=Object.fromEntries(n.searchParams.entries()),r=this.isSuccessfulCallback(t),i=this.isCancelledCallback(t),s=this.resolveFailureCode(t,i),o={success:r,status:r?"SUCCESS":i?"CANCELLED":"FAILED",orderId:this.currentOrderRef??t.clientTransactionId??"unknown",errorCode:r?"":s,errorMessage:r?"":t.message??"Transaction failed without error details"};r&&(o.transaction=this.mapCallbackToTransaction(t));let a=t.transactionId??t.orderCode??t.clientTransactionId,u=t.errorCode??this.extractErrorReferenceFromMessage(t.message)??t.referenceNumber??t.tid??t.rrn;return a&&(o.transactionId=a),u&&(o.errorReference=u),{rawParams:t,result:o}}matchesCallbackBase(e){let n=new URL(e),t=new URL(this.appToAppConfig.callbackUrl);return n.protocol===t.protocol&&n.hostname===t.hostname&&n.pathname===t.pathname}matchesKnownTransactionId(e){let n=[this.currentClientTransactionId,this.currentIsvClientTransactionId].filter(r=>!!r);if(n.length===0)return!0;let t=[e.clientTransactionId,e.ISV_clientTransactionId].filter(r=>!!r);return t.length===0?!0:t.some(r=>n.includes(r))}resolveFailureCode(e,n){if(n)return p.PaymentFailureCode.PaymentCancelledByUser;let t=e.errorCode;if(t?.includes("."))return t;if(this.isTransactionDetailsAction(e)&&(e.transactionType??"").toLowerCase().includes("abort"))return p.PaymentFailureCode.SystemUnknown;let r=(e.message??"").toLowerCase(),i=(e.status??"").toLowerCase();return r.includes("declined")||i==="declined"||i==="fail"||i==="failed"?p.PaymentFailureCode.PaymentDeclined:p.PaymentFailureCode.SystemUnknown}isSuccessfulCallback(e){let n=(e.status??"").toLowerCase();return this.isTransactionDetailsAction(e)?n!=="success"?!1:!(e.transactionType??"").toLowerCase().includes("abort"):n==="success"}isTransactionDetailsAction(e){return(e.action??"").toLowerCase()===F.toLowerCase()}isCancelledCallback(e){let n=(e.status??"").toLowerCase(),t=(e.message??"").toLowerCase(),r=(e.errorCode??"").toLowerCase();return n==="cancelled"||n==="canceled"||t.includes("user_cancel")||t.includes("user cancel")||r.includes("user_cancel")||r.includes("user cancel")}extractErrorReferenceFromMessage(e){return e?e.match(/\((-?\d+)\)/)?.[1]:void 0}mapCallbackToTransaction(e){let n=e.transactionId??e.orderCode??e.clientTransactionId??"unknown",t=L(e.transactionDate),r=O(e.amount),i=O(e.tipAmount);return{amount:r,cardDetail:{aid:e.aid??null,applicationLabel:e.applicationLabel??null,cardNumber:e.accountNumber??"N/A",cardType:e.cardType??"N/A",issuer:e.bankId??null,orderCode:e.orderCode??null,timestamp:t,transactionId:n},createdAt:t,fees:this.buildFeesFromTip(i),id:n,label:null,provider:p.PaymentProvider.Viva,rawData:e,referenceId:e.clientTransactionId??null,roundingDifference:0,type:this.getVivaPaymentMethodType(e)}}getVivaPaymentMethodType(e){let n=(e.applicationLabel??"").toUpperCase().trim(),r=(e.accountNumber??"").substring(0,6),i=(e.bankId??"").trim();return r===z||i===H?p.PaymentMethod.Edenred:ee.includes(r)?p.PaymentMethod.WoltBenefit:te.some(s=>n.includes(s))?p.PaymentMethod.Credit:p.PaymentMethod.Debit}buildFeesFromTip(e){return!e||!this.currentCurrency?null:{parts:[{taxAmount:0,total:{amount:e,currency:this.currentCurrency},type:p.FeeType.TipAmount,vatPercentage:0}]}}async lookupTransactionDetails(){if(!this.currentClientTransactionId||!this.currentSourceTerminalId)return null;let e={clientTransactionId:this.currentClientTransactionId,sourceTerminalId:this.currentSourceTerminalId};return new Promise(n=>{let t=!1,r=setTimeout(()=>{t||(t=!0,i(),n(null))},X),i=this.appToAppConfig.adapter.subscribe(s=>{if(t)return;let o=this.parseCallbackUrl(s);o&&this.matchesKnownTransactionId(o.rawParams)&&(t=!0,clearTimeout(r),i(),n(o.result))});this.appToAppConfig.adapter.openUrl(this.buildActionUrl(F,e)).catch(()=>{t||(t=!0,clearTimeout(r),i(),n(null))})})}};var g=require("@munchi_oy/core");var S=class l{constructor(e,n,t){this.messaging=n;this.config=t;this.api=new g.PaymentApi(void 0,"",e)}static INITIAL_POLL_DELAY_MS=1e4;static POLLING_DURATION_MS=18e4;static POLLING_INTERVAL_MS=2e3;static TIMEOUT_CANCEL_TIMEOUT_MS=5e3;static VERIFY_TIMEOUT_MS=1e4;static VERIFY_MAX_RETRIES=3;static VERIFY_RETRY_DELAY_MS=1500;api;abortController=null;currentSessionId=null;paymentProvider=g.PaymentProviderEnum.Viva;async processPayment(e,n){this.abortController=new AbortController,n("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:r}=await this.api.initiateTerminalTransaction(t);if(this.currentSessionId=r.sessionId,this.abortController.signal.aborted)throw new Error("Aborted");n("REQUIRES_INPUT",{sessionId:r.sessionId});let i=await this.waitForPaymentCompletion(r.sessionId,e,this.abortController.signal);return this.currentSessionId=null,i}catch(r){throw this.currentSessionId=null,r instanceof c?r:new c("NETWORK_ERROR","Failed to create Viva Intent",r)}}async waitForPaymentCompletion(e,n,t){let i=`viva.${this.config.channel.toLowerCase()}.requests.${e}`,s="payment:status-changed";return new Promise((o,a)=>{let u=!1,d=()=>{u=!0,typeof f=="function"&&f(),clearTimeout(h)},m=()=>{d(),a(new Error("Aborted"))};t.addEventListener("abort",m);let f=this.messaging.subscribe(i,s,T=>{u||T.status!==g.SimplePaymentStatus.Pending&&(d(),t.removeEventListener("abort",m),o(this.handleSuccess(T)))}),h=setTimeout(async()=>{if(!(u||t.aborted))try{let T=await this.pollOrderStatus(e,n.orderRef,this.config.storeId,t);if(u)return;o(this.handleSuccess(T))}catch(T){if(u)return;if(T instanceof Error&&T.message==="Aborted"){a(T);return}try{let C=await this.recoverAfterPollingTimeout(n,e);o(C)}catch(C){a(C instanceof c?C:new c("TIMEOUT","Payment timed out and polling failed",C))}}finally{t.removeEventListener("abort",m),d()}},l.INITIAL_POLL_DELAY_MS)})}async pollOrderStatus(e,n,t,r){let s=Date.now()+l.POLLING_DURATION_MS;for(;Date.now()<s;){if(r.aborted)throw new Error("Aborted");try{let{data:o}=await this.api.getPaymentStatus({businessId:Number(t),orderId:n,provider:this.paymentProvider,referenceId:e});if(r.aborted)throw new Error("Aborted");if(o.status!==g.SimplePaymentStatus.Pending)return o}catch(o){if(o instanceof Error&&o.message==="Aborted")throw o}await new Promise(o=>{let a=()=>{clearTimeout(u),o(void 0)};r.addEventListener("abort",a,{once:!0});let u=setTimeout(()=>{r.removeEventListener("abort",a),o(void 0)},l.POLLING_INTERVAL_MS)})}throw new Error("Payment verification timed out.")}async recoverAfterPollingTimeout(e,n){return await this.cancelSessionWithTimeout(n),await this.verifyFinalStatusWithRetry(e,n)}async cancelSessionWithTimeout(e){let n;try{await Promise.race([this.cancelSession(e),new Promise((t,r)=>{n=setTimeout(()=>{r(new Error("Viva timeout cancellation timed out"))},l.TIMEOUT_CANCEL_TIMEOUT_MS)})])}catch{}finally{n&&clearTimeout(n)}}async cancelSession(e){return await this.api.cancelVivaTransactionV2({cashRegisterId:this.config.storeId,sessionId:e}),this.currentSessionId===e&&(this.currentSessionId=null),!0}async verifyFinalStatusWithRetry(e,n){let t;for(let i=1;i<=l.VERIFY_MAX_RETRIES;i++)try{let s=await Promise.race([this.verifyFinalStatus(e,n),new Promise((o,a)=>setTimeout(()=>a(new Error("Verify timed out")),l.VERIFY_TIMEOUT_MS))]);if(s.status==="PENDING"){t=new Error("Verify returned pending status"),i<l.VERIFY_MAX_RETRIES&&await new Promise(o=>setTimeout(o,l.VERIFY_RETRY_DELAY_MS));continue}return s}catch(s){t=s,i<l.VERIFY_MAX_RETRIES&&await new Promise(o=>setTimeout(o,l.VERIFY_RETRY_DELAY_MS))}let r=t instanceof Error?t.message:"Verify retries exhausted";throw new c(g.PaymentFailureCode.PaymentTimeout,r)}handleSuccess(e){let n=e.status===g.SimplePaymentStatus.Success,t={success:n,status:n?"SUCCESS":"FAILED",orderId:e.orderId,errorCode:e.error?.code||(n?"":g.PaymentFailureCode.SystemUnknown),errorMessage:e.error?.message||(n?"":"Transaction failed without error details")};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 n=this.currentSessionId;return this.currentSessionId=null,await this.api.cancelVivaTransactionV2({cashRegisterId:this.config.storeId,sessionId:n}),this.abortController?.abort(),!0}catch(n){throw this.currentSessionId=null,new c("NETWORK_ERROR","Failed to cancel Viva transaction",n)}}async verifyFinalStatus(e,n){try{let{data:t}=await this.api.getPaymentStatus({businessId:Number(this.config.storeId),orderId:e.orderRef,provider:this.paymentProvider,referenceId:n}),r=t.status===g.SimplePaymentStatus.Success,i=t.status===g.SimplePaymentStatus.Pending,s={success:r,status:r?"SUCCESS":i?"PENDING":"FAILED",orderId:t.orderId,errorCode:t.error?.code||(r||i?"":g.PaymentFailureCode.SystemUnknown),errorMessage:t.error?.message||(r||i?"":"Transaction failed without error details")};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,n){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:r}=await this.api.refundSingleVivaTransaction(t),i=r.success;return{success:i,status:i?"SUCCESS":"FAILED",orderId:e.orderRef,transactionId:r.sessionId}}catch(t){throw new c("NETWORK_ERROR","Failed to refund Viva transaction",t)}}abort(){this.abortController?.abort()}};var I=require("@munchi_oy/core"),M=require("axios");var N=class{constructor(e,n,t){this.messaging=n;this.config=t;this.paymentApi=new I.PaymentApi(void 0,"",e),this.worldlineApi=new I.WorldlineApi(void 0,"",e)}paymentApi;worldlineApi;abortController=null;currentOperationId=null;paymentProvider=I.PaymentProviderEnum.Worldline;async processPayment(e,n){this.abortController=new AbortController,n("CONNECTING");let t={amount:e.amountCents,businessId:Number(this.config.storeId),currency:e.currency,displayId:e.displayId,referenceId:e.orderRef,showReceipt:!0,showTransactionResult:!0,...e.options&&"tipAmount"in e.options&&typeof e.options.tipAmount=="number"?{tipAmount:e.options.tipAmount}:{}};try{let r=await this.worldlineApi.createPayment(t),i=this.extractOperationId(r.data);if(this.currentOperationId=i,this.abortController.signal.aborted)throw new Error("Aborted");n("REQUIRES_INPUT",{sessionId:i});let s=await this.waitForPaymentCompletion(i,e.orderRef,I.PaymentEventType.StatusChanged,this.abortController.signal);return this.currentOperationId=null,s}catch(r){throw this.currentOperationId=null,r instanceof c?r:this.mapWorldlineRequestError(r,"Failed to create Worldline payment")}}async cancelTransaction(e){if(!this.currentOperationId)return!1;let n={businessId:Number(this.config.storeId),targetOperationId:this.currentOperationId};try{return await this.worldlineApi.cancelPayment(n),!0}catch(t){throw this.mapWorldlineRequestError(t,"Failed to cancel Worldline transaction")}}async refundTransaction(e,n){this.abortController=new AbortController,n("CONNECTING");let t={amount:e.amountCents,businessId:Number(this.config.storeId),currency:e.currency,displayId:e.displayId,orderReferenceId:e.orderRef,referenceId:e.originalTransactionId};try{let r=await this.worldlineApi.createRefund(t),i=this.extractOperationId(r.data);if(this.currentOperationId=i,this.abortController.signal.aborted)throw new Error("Aborted");n("REQUIRES_INPUT",{sessionId:i});let s=await this.waitForPaymentCompletion(i,e.orderRef,I.PaymentEventType.RefundStatusChanged,this.abortController.signal);return this.currentOperationId=null,s}catch(r){throw this.currentOperationId=null,r instanceof c?r:this.mapWorldlineRequestError(r,"Failed to refund Worldline transaction")}}async verifyFinalStatus(e,n){try{let{data:t}=await this.paymentApi.getPaymentStatus({businessId:Number(this.config.storeId),orderId:e.orderRef,provider:this.paymentProvider,referenceId:n});return this.handlePaymentStatus(t)}catch(t){throw this.mapWorldlineRequestError(t,"Failed to verify final Worldline status")}}abort(){this.abortController?.abort()}async waitForPaymentCompletion(e,n,t,r){let i=`worldline.requests.${e}`;return new Promise((s,o)=>{let a=!1,u=()=>{a=!0,typeof m=="function"&&m(),clearTimeout(f)},d=()=>{u(),o(new c("CANCELLED","Transaction cancelled"))};r.addEventListener("abort",d);let m=this.messaging.subscribe(i,t,h=>{a||h.status===I.SimplePaymentStatus.Pending||(u(),r.removeEventListener("abort",d),s(this.handlePaymentStatus(h)))}),f=setTimeout(async()=>{if(!(a||r.aborted))try{let h=await this.pollOrderStatus(e,n,this.config.storeId,r);s(this.handlePaymentStatus(h))}catch(h){o(new c("TIMEOUT","Payment timed out and polling failed",h))}finally{r.removeEventListener("abort",d),u()}},1e4)})}async pollOrderStatus(e,n,t,r){let o=Date.now()+12e4;for(;Date.now()<o;){if(r.aborted)throw new Error("Aborted");try{let{data:a}=await this.paymentApi.getPaymentStatus({businessId:Number(t),orderId:n,provider:this.paymentProvider,referenceId:e});if(r.aborted)throw new Error("Aborted");if(a.status!==I.SimplePaymentStatus.Pending)return a}catch(a){if(a instanceof Error&&a.message==="Aborted")throw a}await new Promise(a=>{let u=()=>{clearTimeout(d),a(void 0)};r.addEventListener("abort",u,{once:!0});let d=setTimeout(()=>{r.removeEventListener("abort",u),a(void 0)},2e3)})}throw new Error("Payment verification timed out.")}handlePaymentStatus(e){let n=e.status===I.SimplePaymentStatus.Success,t=e.status===I.SimplePaymentStatus.Pending,r={success:n,status:n?"SUCCESS":t?"PENDING":"FAILED",orderId:e.orderId,errorCode:e.error?.code||(n||t?"":I.PaymentFailureCode.SystemUnknown),errorMessage:e.error?.message||(n||t?"":"Transaction failed without error details")};return e.transactionId&&(r.transactionId=e.transactionId),e.error?.referenceError&&(r.errorReference=e.error.referenceError),e.transaction&&(r.transaction=e.transaction),r}extractOperationId(e){let n=typeof e=="object"&&e!==null&&"operationId"in e&&typeof e.operationId=="string"?e.operationId:null;if(!n)throw new Error("operationId is missing from response.");return n}mapWorldlineRequestError(e,n){if((0,M.isAxiosError)(e)){if(e.response?.status===400||e.response?.status===409)return new c("TERMINAL_BUSY",n,e);if(e.response?.status===404)return new c("TERMINAL_OFFLINE",n,e)}return new c("NETWORK_ERROR",n,e)}};var w=class l{strategy;axios;messaging;timeoutMs;logger;_currentState="IDLE";_listeners=[];_cancellationIntent=!1;_currentSessionId;_autoResetTimer;autoResetOptions;appToAppConfig;static TERMINAL_STATES=["SUCCESS","FAILED","INTERNAL_ERROR"];static RESTING_STATES=["IDLE",...l.TERMINAL_STATES];constructor(e,n,t,r={},i){this.axios=e,this.messaging=n,this.logger=r.logger,this.timeoutMs=r.timeoutMs||3e4,this.autoResetOptions=r.autoResetOnPaymentComplete,this.appToAppConfig=r.appToApp,this.strategy=i??this.resolveStrategy(t)}get version(){return D}get currentState(){return this._currentState}generateErrorResult(e,n,t){return{success:!1,status:"ERROR",errorCode:this.normalizeErrorCode(n),errorMessage:t,orderId:e}}normalizeErrorCode(e){return e?e.includes(".")?e:{CANCELLED:y.PaymentFailureCode.PaymentCancelledByUser,DECLINED:y.PaymentFailureCode.PaymentDeclined,TERMINAL_BUSY:y.PaymentFailureCode.TerminalBusy,TERMINAL_OFFLINE:y.PaymentFailureCode.TerminalOffline,TIMEOUT:y.PaymentFailureCode.TerminalTimeout,NETWORK_ERROR:y.PaymentFailureCode.SystemProviderError,STRATEGY_ERROR:y.PaymentFailureCode.SystemProviderError,MISSING_CONFIG:y.PaymentFailureCode.SystemUnknown,INVALID_AMOUNT:y.PaymentFailureCode.SystemUnknown,UNKNOWN:y.PaymentFailureCode.SystemUnknown}[e]??y.PaymentFailureCode.SystemUnknown:y.PaymentFailureCode.SystemUnknown}subscribe=e=>(this._listeners.push(e),e(this._currentState),()=>{this._listeners=this._listeners.filter(n=>n!==e)});transitionTo(e){if(this._currentState===e)return;if(e==="IDLE"){this.cancelAutoReset(),this._currentState=e,this._listeners.forEach(t=>t(e));return}if(l.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(r=>r(this._currentState))),new c("UNKNOWN",t)}this._currentState=e,l.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){switch(e.provider){case y.PaymentProvider.Nets:return new b(this.axios,this.messaging,e);case y.PaymentProvider.Worldline:return new N(this.axios,this.messaging,e);case y.PaymentProvider.Viva:return this.appToAppConfig?.enabled?new A(this.axios,this.messaging,e,this.appToAppConfig):new S(this.axios,this.messaging,e);default:return new S(this.axios,this.messaging,e)}}initiateTransaction=async(e,n)=>{let t=n??{},r=e.orderRef;if(!l.RESTING_STATES.includes(this._currentState))return this.generateErrorResult(e.orderRef,"UNKNOWN","A transaction is already in progress");let s=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 o=(m,f)=>{f?.sessionId&&(this._currentSessionId=f.sessionId),m!=="FAILED"&&(this.transitionTo(m),this.fireStateCallback(m,t,r))},a=this.strategy.processPayment(e,o),d=(this.strategy.getExecutionMode?.()??"managed")==="callback_driven"?await a:await Promise.race([a,new Promise((m,f)=>{setTimeout(()=>{f(new c("TIMEOUT","Transaction timed out"))},this.timeoutMs)})]);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()-s}),d}catch(o){return this.logger?.warn("Transaction interrupted. Handling final status...",{error:o}),await this.handleTransactionError(e,o,t)}};async handleTransactionError(e,n,t={}){let r=n instanceof c&&(n.code==="TIMEOUT"||n.code===y.PaymentFailureCode.PaymentTimeout);if(!this._cancellationIntent&&!r&&(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}:{}}}let i;if(this.strategy.abort(),this._currentSessionId)try{let s=await this.strategy.verifyFinalStatus(e,this._currentSessionId);if(s.success)return this.transitionTo("SUCCESS"),this.safeFireCallback(()=>t.onSuccess?.(s)),s;i=r&&s.status==="PENDING"?this.buildErrorResultFromException(e.orderRef,n):s}catch(s){this.logger?.warn("Failed to get detailed error from verifyFinalStatus",{verifyErr:s}),i=this.buildErrorResultFromException(e.orderRef,s)}else i=this.buildErrorResultFromException(e.orderRef,n);return this.transitionTo("FAILED"),this.safeFireCallback(()=>t.onError?.(i)),i}buildErrorResultFromException(e,n){return n instanceof c?this.generateErrorResult(e,n.code,n.message):this.generateErrorResult(e,"UNKNOWN",n instanceof Error?n.message:"Unknown fatal error")}fireStateCallback(e,n,t){let r={orderRef:t,refPaymentId:this._currentSessionId};switch(e){case"CONNECTING":this.safeFireCallback(()=>n.onConnecting?.(r));break;case"REQUIRES_INPUT":this.safeFireCallback(()=>n.onRequiresInput?.(r));break;case"PROCESSING":this.safeFireCallback(()=>n.onProcessing?.(r));break}}safeFireCallback(e){try{e()}catch(n){this.logger?.warn("Callback execution failed",{error:n})}}cancel=async()=>{if(this.logger?.info("Attempting cancellation"),l.TERMINAL_STATES.includes(this._currentState))return this.logger?.warn("Cannot cancel: Transaction already in terminal state",{state:this._currentState}),!1;if(!this._currentSessionId&&this._currentState==="IDLE")return this.logger?.warn("Cannot cancel: No active session to cancel",{state:this._currentState}),!1;this._cancellationIntent=!0,this.transitionTo("VERIFYING");try{return await this.strategy.cancelTransaction(n=>this.transitionTo(n))}catch(e){return this.logger?.error("Cancellation command failed",e),!1}};reset=()=>{l.TERMINAL_STATES.includes(this._currentState)&&(this._currentSessionId=void 0,this._cancellationIntent=!1,this.transitionTo("IDLE"))};refund=async(e,n)=>{let t=n??{};if(this.logger?.info("Initiating refund",{orderRef:e.orderRef}),this._currentSessionId=void 0,this._cancellationIntent=!1,!l.RESTING_STATES.includes(this._currentState))return this.generateErrorResult(e.orderRef,"UNKNOWN","A transaction is already in progress");this.transitionTo("IDLE");try{let i=(o,a)=>{a?.sessionId&&(this._currentSessionId=a.sessionId),o!=="FAILED"&&(this.transitionTo(o),this.fireStateCallback(o,t,e.orderRef))},s=await this.strategy.refundTransaction(e,i);return s.success?(this.transitionTo("SUCCESS"),this.safeFireCallback(()=>t.onSuccess?.(s))):this._cancellationIntent||s.status==="CANCELLED"?(this.transitionTo("FAILED"),this.safeFireCallback(()=>t.onCancelled?.({orderRef:e.orderRef,refPaymentId:this._currentSessionId}))):(this.transitionTo("FAILED"),this.safeFireCallback(()=>t.onError?.(s))),this.logger?.info("Refund completed",{success:s.success,orderRef:e.orderRef}),s}catch(i){if(this.logger?.error("Refund failed",i),this._cancellationIntent){try{if(this._currentSessionId){let o=await this.strategy.verifyFinalStatus(e,this._currentSessionId);return o.success?(this.transitionTo("SUCCESS"),this.safeFireCallback(()=>t.onSuccess?.(o)),o):o.status==="CANCELLED"?(this.transitionTo("FAILED"),this.safeFireCallback(()=>t.onCancelled?.({orderRef:e.orderRef,refPaymentId:this._currentSessionId})),o):(this.transitionTo("FAILED"),this.safeFireCallback(()=>t.onError?.(o)),o)}}catch(o){this.logger?.warn("Refund final status verification failed",{verifyError:o})}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 s=this.generateErrorResult(e.orderRef,"UNKNOWN",i instanceof Error?i.message:"Refund failed");return this.safeFireCallback(()=>t.onError?.(s)),s}}};var x=(r=>(r.CONNECTING="CONNECTING",r.CONNECTED="CONNECTED",r.OFFLINE="OFFLINE",r.DISCONNECTED="DISCONNECTED",r))(x||{});0&&(module.exports={AppReaderStatus,MunchiPaymentSDK,PaymentInteractionState,SdkPaymentStatus});
|
package/dist/index.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{PaymentFailureCode as y,PaymentProvider as w}from"@munchi_oy/core";var D="1.6.2";var l=class d extends Error{code;rawError;constructor(e,r,t){super(r),this.name="PaymentSDKError",this.code=e,this.rawError=t,Object.setPrototypeOf(this,d.prototype)}};import{PaymentApi as W,PaymentEventType as G,PaymentProviderEnum as q,SimplePaymentStatus as C,TransactionType as L}from"@munchi_oy/core";var I=(o=>(o.PENDING="PENDING",o.SUCCESS="SUCCESS",o.APPROVED="APPROVED",o.FAILED="FAILED",o.CANCELLED="CANCELLED",o.ERROR="ERROR",o))(I||{}),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 P=class{constructor(e,r,t){this.messaging=r;this.config=t;this.api=new W(void 0,"",e)}api;abortController=null;currentRequestId=null;paymentProvider=q.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:L.Purchase}};try{let{data:n}=await this.api.initiateNetsTerminalTransaction(t),i=n.connectCloudRequestId;if(!i)throw new Error("connectCloudRequestId is missing from response.");if(this.currentRequestId=i,this.abortController.signal.aborted)throw new Error("Aborted");r("REQUIRES_INPUT",{sessionId:i});let o=await this.waitForPaymentCompletion(i,e.orderRef,this.abortController.signal);return this.currentRequestId=null,o}catch(n){throw this.currentRequestId=null,n instanceof l?n:new l("TERMINAL_BUSY","Failed to create Nets Intent",n)}}async waitForPaymentCompletion(e,r,t){let n=`nets.requests.${e}`,i=G.StatusChanged;return new Promise((o,s)=>{let a=!1,c=()=>{a=!0,typeof m=="function"&&m(),clearTimeout(p)},u=()=>{c(),s(new l("CANCELLED","Transaction cancelled"))};t.addEventListener("abort",u);let m=this.messaging.subscribe(n,i,f=>{a||f.status===C.Pending||(c(),t.removeEventListener("abort",u),o(this.handleSuccess(f)))}),p=setTimeout(async()=>{if(!(a||t.aborted))try{let f=await this.pollOrderStatus(e,r,this.config.storeId,t);o(this.handleSuccess(f))}catch(f){s(new l("TIMEOUT","Payment timed out and polling failed",f))}finally{t.removeEventListener("abort",u),c()}},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:c}=await this.api.getPaymentStatus({businessId:Number(t),orderId:r,provider:this.paymentProvider,referenceId:e});if(n.aborted)throw new Error("Aborted");if(c.status!==C.Pending)return c}catch(c){if(c instanceof Error&&c.message==="Aborted")throw c}await new Promise(c=>{let u=()=>{clearTimeout(m),c(void 0)};n.addEventListener("abort",u,{once:!0});let m=setTimeout(()=>{n.removeEventListener("abort",u),c(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:L.ReturnOfGoods}},{data:n}=await this.api.initiateNetsTerminalTransaction(t),i=n.connectCloudRequestId;if(!i)throw new Error("connectCloudRequestId is missing from response.");if(this.currentRequestId=i,this.abortController.signal.aborted)throw new Error("Aborted");r("REQUIRES_INPUT",{sessionId:i});let o=await this.waitForPaymentCompletion(i,e.orderRef,this.abortController.signal);return this.currentRequestId=null,o}catch(t){throw this.currentRequestId=null,t instanceof l||t instanceof l?t:new l("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 l("NETWORK_ERROR","Failed to verify final Nets status",t)}}handleSuccess(e){let r=e.status===C.Success,t=e.status===C.Pending,n={success:r,status:r?"SUCCESS":t?"PENDING":"FAILED",orderId:e.orderId,transaction:e.transaction};return e.transactionId&&(n.transactionId=e.transactionId),e.error?.code&&(n.errorCode=e.error.code),e.error?.message&&(n.errorMessage=e.error.message),n}abort(){this.abortController?.abort()}};import{FeeType as Y,PaymentApi as B,PaymentFailureCode as g,PaymentMethod as b,PaymentProvider as M,VivaApi as $}from"@munchi_oy/core";var _=d=>{if(!d)return 0;let e=Number.parseInt(d,10);return Number.isFinite(e)?e:0},F=d=>{if(!d)return new Date().toISOString();let e=new Date(d);return Number.isNaN(e.getTime())?new Date().toISOString():e.toISOString()};var j="vivapayclient://pay/v1",Q="abort",X="cancel",z="sale",x="transactionDetails",H=4e3,J="534287",Z="38",ee="493591",te="497439",re=[ee,te],ne=["CREDIT","PREPAID","CORPORATE"],v=class{constructor(e,r,t,n){this.config=t;this.appToAppConfig=n;this.api=new $(void 0,"",e),this.paymentApi=new B(void 0,"",e),this.paymentApiWithReference=this.paymentApi}api;paymentApi;abortController=null;currentClientTransactionId=null;currentIsvClientTransactionId=null;currentCurrency=null;currentOrderRef=null;currentSourceTerminalId=null;latestResult=null;paymentApiWithReference;wasCancelled=!1;getExecutionMode(){return"callback_driven"}async processPayment(e,r){if(!this.appToAppConfig.enabled)throw new l("STRATEGY_ERROR","Viva app-to-app is not enabled");r("CONNECTING");try{let{data:t}=await this.api.createVivaAppToAppPayment(this.buildCreatePaymentPayload(e));return await this.runAction(z,this.buildLaunchParams(t),e,r)}catch(t){throw new l("NETWORK_ERROR","Failed to create Viva app-to-app payment",t)}}async cancelTransaction(e){if(!this.currentClientTransactionId)return!1;let r=await this.lookupTransactionDetails();if(r)return this.latestResult=r,this.wasCancelled=r.status==="CANCELLED",this.abortController?.abort(),!0;try{await this.appToAppConfig.adapter.openUrl(this.buildActionUrl(Q,{}))}catch(t){throw this.abortController?.abort(),new l("NETWORK_ERROR","Failed to launch Viva abort flow",t)}return this.wasCancelled=!0,this.abortController?.abort(),!0}async refundTransaction(e,r){r("CONNECTING");let t=await this.fetchPaymentReference(e),n=this.buildRefundLaunchParams(e,t);return this.runAction(X,n,e,r)}async verifyFinalStatus(e,r){return this.latestResult?this.latestResult:this.wasCancelled?{success:!1,status:"CANCELLED",orderId:this.currentOrderRef??r,errorCode:g.PaymentCancelledByUser,errorMessage:"Transaction was cancelled",transactionId:r}:{success:!1,status:"PENDING",orderId:this.currentOrderRef??r,transactionId:r}}abort(){this.abortController?.abort()}async runAction(e,r,t,n){if(!this.appToAppConfig.enabled)throw new l("STRATEGY_ERROR","Viva app-to-app is not enabled");this.abortController=new AbortController,this.latestResult=null,this.wasCancelled=!1,this.currentCurrency=t.currency,this.currentOrderRef=t.orderRef,this.currentSourceTerminalId=r.sourceTerminalId??r.tid??null,this.currentClientTransactionId=r.clientTransactionId??null,this.currentIsvClientTransactionId=r.ISV_clientTransactionId??null;let i=this.currentClientTransactionId?{sessionId:this.currentClientTransactionId}:void 0;return new Promise((o,s)=>{let a=(m,p)=>{m?.(),this.abortController?.signal.removeEventListener("abort",p)},c=()=>{a(u,c),s(new Error("Aborted"))},u=this.appToAppConfig.adapter.subscribe(m=>{if(this.abortController?.signal.aborted)return;let p=this.parseCallbackUrl(m);p&&this.matchesKnownTransactionId(p.rawParams)&&(this.latestResult=p.result,this.wasCancelled=p.result.status==="CANCELLED",this.currentClientTransactionId=null,this.currentIsvClientTransactionId=null,this.currentCurrency=null,this.currentOrderRef=null,this.currentSourceTerminalId=null,a(u,c),o(p.result))});this.abortController?.signal.addEventListener("abort",c),n("REQUIRES_INPUT",i),this.appToAppConfig.adapter.openUrl(this.buildActionUrl(e,r)).then(()=>{this.abortController?.signal.aborted}).catch(m=>{a(u,c),this.currentClientTransactionId=null,this.currentIsvClientTransactionId=null,this.currentCurrency=null,this.currentOrderRef=null,this.currentSourceTerminalId=null,s(new l("NETWORK_ERROR","Failed to launch Viva app",m))})})}buildCreatePaymentPayload(e){let r=e.options??{},t={amount:e.amountCents,referenceId:e.orderRef,businessId:Number(this.config.storeId),currency:e.currency,displayId:e.displayId,showReceipt:!1,showTransactionResult:!1};return r.tipAmount!==void 0&&(t.tipAmount=r.tipAmount),t}buildLaunchParams(e){let r={};return Object.entries(e).forEach(([t,n])=>{n!==void 0&&(r[t]=String(n))}),r}buildRefundLaunchParams(e,r){let t=e.options??{},n=r?.originalTransactionId?.trim()||e.originalTransactionId.trim(),i={amount:String(e.amountCents),clientTransactionId:n,show_receipt:"false",show_transaction_result:"false"};if(t.sourceCode&&(i.sourceCode=t.sourceCode),r?.referenceCode&&(i.orderCode=r.referenceCode),r?.shortReferenceCode&&(i.shortOrderCode=r.shortReferenceCode),r?.receiptNumber&&(i.referenceNumber=r.receiptNumber),r?.terminalId&&(i.tid=r.terminalId),r?.referenceCode||r?.shortReferenceCode||r?.receiptNumber)return i;let o=n;return/^\d+$/.test(o)&&(o.length===16?i.orderCode=o:o.length===10?i.shortOrderCode=o:i.referenceNumber=o),i}async fetchPaymentReference(e){try{let{data:r}=await this.paymentApiWithReference.getPaymentReference({orderId:e.orderRef,provider:M.Viva,transactionId:e.originalTransactionId});return this.extractPaymentReferenceContext(r)}catch{return}}extractPaymentReferenceContext(e){if(!e)return;let r=e.references;if(!r||typeof r!="object"||Array.isArray(r))return{originalTransactionId:e.originalTransactionId};let t=r,n={originalTransactionId:this.readReferenceValue(t.originalTransactionId)??this.readReferenceValue(t.clientTransactionId)??e.originalTransactionId},i=this.readReferenceValue(t.receiptNumber)??this.readReferenceValue(t.referenceNumber),o=this.readReferenceValue(t.referenceCode)??this.readReferenceValue(t.orderCode),s=this.readReferenceValue(t.shortReferenceCode)??this.readReferenceValue(t.shortOrderCode),a=this.readReferenceValue(t.terminalId)??this.readReferenceValue(t.tid);return i&&(n.receiptNumber=i),o&&(n.referenceCode=o),s&&(n.shortReferenceCode=s),a&&(n.terminalId=a),n}readReferenceValue(e){if(typeof e!="string")return;let r=e.trim();return r.length>0?r:void 0}buildActionUrl(e,r){let t=new URLSearchParams({action:e,appId:this.appToAppConfig.appId,callback:this.appToAppConfig.callbackUrl});return Object.entries(r).forEach(([n,i])=>{t.set(n,i)}),`${j}?${t.toString()}`}parseCallbackUrl(e){if(!this.matchesCallbackBase(e))return null;let r=new URL(e),t=Object.fromEntries(r.searchParams.entries()),n=this.isSuccessfulCallback(t),i=this.isCancelledCallback(t),o=this.resolveFailureCode(t,i),s={success:n,status:n?"SUCCESS":i?"CANCELLED":"FAILED",orderId:this.currentOrderRef??t.clientTransactionId??"unknown",errorCode:n?"":o,errorMessage:n?"":t.message??"Transaction failed without error details"};n&&(s.transaction=this.mapCallbackToTransaction(t));let a=t.transactionId??t.orderCode??t.clientTransactionId,c=t.errorCode??this.extractErrorReferenceFromMessage(t.message)??t.referenceNumber??t.tid??t.rrn;return a&&(s.transactionId=a),c&&(s.errorReference=c),{rawParams:t,result:s}}matchesCallbackBase(e){let r=new URL(e),t=new URL(this.appToAppConfig.callbackUrl);return r.protocol===t.protocol&&r.hostname===t.hostname&&r.pathname===t.pathname}matchesKnownTransactionId(e){let r=[this.currentClientTransactionId,this.currentIsvClientTransactionId].filter(n=>!!n);if(r.length===0)return!0;let t=[e.clientTransactionId,e.ISV_clientTransactionId].filter(n=>!!n);return t.length===0?!0:t.some(n=>r.includes(n))}resolveFailureCode(e,r){if(r)return g.PaymentCancelledByUser;let t=e.errorCode;if(t?.includes("."))return t;if(this.isTransactionDetailsAction(e)&&(e.transactionType??"").toLowerCase().includes("abort"))return g.SystemUnknown;let n=(e.message??"").toLowerCase(),i=(e.status??"").toLowerCase();return n.includes("declined")||i==="declined"||i==="fail"||i==="failed"?g.PaymentDeclined:g.SystemUnknown}isSuccessfulCallback(e){let r=(e.status??"").toLowerCase();return this.isTransactionDetailsAction(e)?r!=="success"?!1:!(e.transactionType??"").toLowerCase().includes("abort"):r==="success"}isTransactionDetailsAction(e){return(e.action??"").toLowerCase()===x.toLowerCase()}isCancelledCallback(e){let r=(e.status??"").toLowerCase(),t=(e.message??"").toLowerCase(),n=(e.errorCode??"").toLowerCase();return r==="cancelled"||r==="canceled"||t.includes("user_cancel")||t.includes("user cancel")||n.includes("user_cancel")||n.includes("user cancel")}extractErrorReferenceFromMessage(e){return e?e.match(/\((-?\d+)\)/)?.[1]:void 0}mapCallbackToTransaction(e){let r=e.transactionId??e.orderCode??e.clientTransactionId??"unknown",t=F(e.transactionDate),n=_(e.amount),i=_(e.tipAmount);return{amount:n,cardDetail:{aid:e.aid??null,applicationLabel:e.applicationLabel??null,cardNumber:e.accountNumber??"N/A",cardType:e.cardType??"N/A",issuer:e.bankId??null,orderCode:e.orderCode??null,timestamp:t,transactionId:r},createdAt:t,fees:this.buildFeesFromTip(i),id:r,label:null,provider:M.Viva,rawData:e,referenceId:e.clientTransactionId??null,roundingDifference:0,type:this.getVivaPaymentMethodType(e)}}getVivaPaymentMethodType(e){let r=(e.applicationLabel??"").toUpperCase().trim(),n=(e.accountNumber??"").substring(0,6),i=(e.bankId??"").trim();return n===J||i===Z?b.Edenred:re.includes(n)?b.WoltBenefit:ne.some(o=>r.includes(o))?b.Credit:b.Debit}buildFeesFromTip(e){return!e||!this.currentCurrency?null:{parts:[{taxAmount:0,total:{amount:e,currency:this.currentCurrency},type:Y.TipAmount,vatPercentage:0}]}}async lookupTransactionDetails(){if(!this.currentClientTransactionId||!this.currentSourceTerminalId)return null;let e={clientTransactionId:this.currentClientTransactionId,sourceTerminalId:this.currentSourceTerminalId};return new Promise(r=>{let t=!1,n=setTimeout(()=>{t||(t=!0,i(),r(null))},H),i=this.appToAppConfig.adapter.subscribe(o=>{if(t)return;let s=this.parseCallbackUrl(o);s&&this.matchesKnownTransactionId(s.rawParams)&&(t=!0,clearTimeout(n),i(),r(s.result))});this.appToAppConfig.adapter.openUrl(this.buildActionUrl(x,e)).catch(()=>{t||(t=!0,clearTimeout(n),i(),r(null))})})}};import{PaymentApi as ie,PaymentFailureCode as U,PaymentProviderEnum as se,SimplePaymentStatus as T}from"@munchi_oy/core";var E=class{constructor(e,r,t){this.messaging=r;this.config=t;this.api=new ie(void 0,"",e)}api;abortController=null;currentSessionId=null;paymentProvider=se.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 i=await this.waitForPaymentCompletion(n.sessionId,e.orderRef,this.abortController.signal);return this.currentSessionId=null,i}catch(n){throw this.currentSessionId=null,n instanceof l?n:new l("NETWORK_ERROR","Failed to create Viva Intent",n)}}async waitForPaymentCompletion(e,r,t){let i=`viva.${this.config.channel.toLowerCase()}.requests.${e}`,o="payment:status-changed";return new Promise((s,a)=>{let c=!1,u=()=>{c=!0,typeof p=="function"&&p(),clearTimeout(f)},m=()=>{u(),a(new Error("Aborted"))};t.addEventListener("abort",m);let p=this.messaging.subscribe(i,o,h=>{c||h.status!==T.Pending&&(u(),t.removeEventListener("abort",m),s(this.handleSuccess(h)))}),f=setTimeout(async()=>{if(!(c||t.aborted))try{let h=await this.pollOrderStatus(e,r,this.config.storeId,t);s(this.handleSuccess(h))}catch(h){a(new l("TIMEOUT","Payment timed out and polling failed",h))}finally{t.removeEventListener("abort",m),u()}},1e4)})}async pollOrderStatus(e,r,t,n){let a=Date.now()+18e4;for(;Date.now()<a;){if(n.aborted)throw new Error("Aborted");try{let{data:c}=await this.api.getPaymentStatus({businessId:Number(t),orderId:r,provider:this.paymentProvider,referenceId:e});if(n.aborted)throw new Error("Aborted");if(c.status!==T.Pending)return c}catch(c){if(c instanceof Error&&c.message==="Aborted")throw c}await new Promise(c=>{let u=()=>{clearTimeout(m),c(void 0)};n.addEventListener("abort",u,{once:!0});let m=setTimeout(()=>{n.removeEventListener("abort",u),c(void 0)},2e3)})}throw new Error("Payment verification timed out.")}handleSuccess(e){let r=e.status===T.Success,t={success:r,status:r?"SUCCESS":"FAILED",orderId:e.orderId,errorCode:e.error?.code||(r?"":U.SystemUnknown),errorMessage:e.error?.message||(r?"":"Transaction failed without error details")};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 l("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===T.Success,i=t.status===T.Pending,o={success:n,status:n?"SUCCESS":i?"PENDING":"FAILED",orderId:t.orderId,errorCode:t.error?.code||(n||i?"":U.SystemUnknown),errorMessage:t.error?.message||(n||i?"":"Transaction failed without error details")};return t.transactionId&&(o.transactionId=t.transactionId),t.error?.referenceError&&(o.errorReference=t.error.referenceError),t.transaction&&(o.transaction=t.transaction),o}catch(t){throw new l("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),i=n.success;return{success:i,status:i?"SUCCESS":"FAILED",orderId:e.orderRef,transactionId:n.sessionId}}catch(t){throw new l("NETWORK_ERROR","Failed to refund Viva transaction",t)}}abort(){this.abortController?.abort()}};import{PaymentApi as oe,PaymentEventType as k,PaymentFailureCode as ae,PaymentProviderEnum as ce,SimplePaymentStatus as A,WorldlineApi as le}from"@munchi_oy/core";import{isAxiosError as ue}from"axios";var N=class{constructor(e,r,t){this.messaging=r;this.config=t;this.paymentApi=new oe(void 0,"",e),this.worldlineApi=new le(void 0,"",e)}paymentApi;worldlineApi;abortController=null;currentOperationId=null;paymentProvider=ce.Worldline;async processPayment(e,r){this.abortController=new AbortController,r("CONNECTING");let t={amount:e.amountCents,businessId:Number(this.config.storeId),currency:e.currency,displayId:e.displayId,referenceId:e.orderRef,showReceipt:!0,showTransactionResult:!0,...e.options&&"tipAmount"in e.options&&typeof e.options.tipAmount=="number"?{tipAmount:e.options.tipAmount}:{}};try{let n=await this.worldlineApi.createPayment(t),i=this.extractOperationId(n.data);if(this.currentOperationId=i,this.abortController.signal.aborted)throw new Error("Aborted");r("REQUIRES_INPUT",{sessionId:i});let o=await this.waitForPaymentCompletion(i,e.orderRef,k.StatusChanged,this.abortController.signal);return this.currentOperationId=null,o}catch(n){throw this.currentOperationId=null,n instanceof l?n:this.mapWorldlineRequestError(n,"Failed to create Worldline payment")}}async cancelTransaction(e){if(!this.currentOperationId)return!1;let r={businessId:Number(this.config.storeId),targetOperationId:this.currentOperationId};try{return await this.worldlineApi.cancelPayment(r),!0}catch(t){throw this.mapWorldlineRequestError(t,"Failed to cancel Worldline transaction")}}async refundTransaction(e,r){this.abortController=new AbortController,r("CONNECTING");let t={amount:e.amountCents,businessId:Number(this.config.storeId),currency:e.currency,displayId:e.displayId,orderReferenceId:e.orderRef,referenceId:e.originalTransactionId};try{let n=await this.worldlineApi.createRefund(t),i=this.extractOperationId(n.data);if(this.currentOperationId=i,this.abortController.signal.aborted)throw new Error("Aborted");r("REQUIRES_INPUT",{sessionId:i});let o=await this.waitForPaymentCompletion(i,e.orderRef,k.RefundStatusChanged,this.abortController.signal);return this.currentOperationId=null,o}catch(n){throw this.currentOperationId=null,n instanceof l?n:this.mapWorldlineRequestError(n,"Failed to refund Worldline transaction")}}async verifyFinalStatus(e,r){try{let{data:t}=await this.paymentApi.getPaymentStatus({businessId:Number(this.config.storeId),orderId:e.orderRef,provider:this.paymentProvider,referenceId:r});return this.handlePaymentStatus(t)}catch(t){throw this.mapWorldlineRequestError(t,"Failed to verify final Worldline status")}}abort(){this.abortController?.abort()}async waitForPaymentCompletion(e,r,t,n){let i=`worldline.requests.${e}`;return new Promise((o,s)=>{let a=!1,c=()=>{a=!0,typeof m=="function"&&m(),clearTimeout(p)},u=()=>{c(),s(new l("CANCELLED","Transaction cancelled"))};n.addEventListener("abort",u);let m=this.messaging.subscribe(i,t,f=>{a||f.status===A.Pending||(c(),n.removeEventListener("abort",u),o(this.handlePaymentStatus(f)))}),p=setTimeout(async()=>{if(!(a||n.aborted))try{let f=await this.pollOrderStatus(e,r,this.config.storeId,n);o(this.handlePaymentStatus(f))}catch(f){s(new l("TIMEOUT","Payment timed out and polling failed",f))}finally{n.removeEventListener("abort",u),c()}},1e4)})}async pollOrderStatus(e,r,t,n){let s=Date.now()+12e4;for(;Date.now()<s;){if(n.aborted)throw new Error("Aborted");try{let{data:a}=await this.paymentApi.getPaymentStatus({businessId:Number(t),orderId:r,provider:this.paymentProvider,referenceId:e});if(n.aborted)throw new Error("Aborted");if(a.status!==A.Pending)return a}catch(a){if(a instanceof Error&&a.message==="Aborted")throw a}await new Promise(a=>{let c=()=>{clearTimeout(u),a(void 0)};n.addEventListener("abort",c,{once:!0});let u=setTimeout(()=>{n.removeEventListener("abort",c),a(void 0)},2e3)})}throw new Error("Payment verification timed out.")}handlePaymentStatus(e){let r=e.status===A.Success,t=e.status===A.Pending,n={success:r,status:r?"SUCCESS":t?"PENDING":"FAILED",orderId:e.orderId,errorCode:e.error?.code||(r||t?"":ae.SystemUnknown),errorMessage:e.error?.message||(r||t?"":"Transaction failed without error details")};return e.transactionId&&(n.transactionId=e.transactionId),e.error?.referenceError&&(n.errorReference=e.error.referenceError),e.transaction&&(n.transaction=e.transaction),n}extractOperationId(e){let r=typeof e=="object"&&e!==null&&"operationId"in e&&typeof e.operationId=="string"?e.operationId:null;if(!r)throw new Error("operationId is missing from response.");return r}mapWorldlineRequestError(e,r){if(ue(e)){if(e.response?.status===400||e.response?.status===409)return new l("TERMINAL_BUSY",r,e);if(e.response?.status===404)return new l("TERMINAL_OFFLINE",r,e)}return new l("NETWORK_ERROR",r,e)}};var O=class d{static VIVA_TIMEOUT_CANCEL_TIMEOUT_MS=5e3;strategy;axios;messaging;provider;timeoutMs;logger;_currentState="IDLE";_listeners=[];_cancellationIntent=!1;_currentSessionId;_autoResetTimer;autoResetOptions;appToAppConfig;static TERMINAL_STATES=["SUCCESS","FAILED","INTERNAL_ERROR"];static RESTING_STATES=["IDLE",...d.TERMINAL_STATES];constructor(e,r,t,n={},i){this.axios=e,this.messaging=r,this.provider=t.provider,this.logger=n.logger,this.timeoutMs=n.timeoutMs||3e4,this.autoResetOptions=n.autoResetOnPaymentComplete,this.appToAppConfig=n.appToApp,this.strategy=i??this.resolveStrategy(t)}get version(){return D}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(d.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 l("UNKNOWN",t)}this._currentState=e,d.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){switch(e.provider){case w.Nets:return new P(this.axios,this.messaging,e);case w.Worldline:return new N(this.axios,this.messaging,e);case w.Viva:return this.appToAppConfig?.enabled?new v(this.axios,this.messaging,e,this.appToAppConfig):new E(this.axios,this.messaging,e);default:return new E(this.axios,this.messaging,e)}}initiateTransaction=async(e,r)=>{let t=r??{},n=e.orderRef;if(!d.RESTING_STATES.includes(this._currentState))return this.generateErrorResult(e.orderRef,"UNKNOWN","A transaction is already in progress");let o=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 s=(m,p)=>{p?.sessionId&&(this._currentSessionId=p.sessionId),m!=="FAILED"&&(this.transitionTo(m),this.fireStateCallback(m,t,n))},a=this.strategy.processPayment(e,s),u=(this.strategy.getExecutionMode?.()??"managed")==="callback_driven"?await a:await Promise.race([a,new Promise((m,p)=>{setTimeout(()=>{p(new l("TIMEOUT","Transaction timed out"))},this.timeoutMs)})]);if(u.success)this.transitionTo("SUCCESS"),this.safeFireCallback(()=>t.onSuccess?.(u));else{if(this._cancellationIntent)return await this.handleTransactionError(e,new Error("Aborted after resolution"),t);this.transitionTo("FAILED"),this.safeFireCallback(()=>t.onError?.(u))}return this.logger?.info("Transaction completed successfully",{orderId:e.orderRef,durationMs:Date.now()-o}),u}catch(s){return this.logger?.warn("Transaction interrupted. Handling final status...",{error:s}),await this.handleTransactionError(e,s,t)}};async handleTransactionError(e,r,t={}){let n=r instanceof l&&r.code==="TIMEOUT";if(!this._cancellationIntent&&!n&&(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 i,o=this._currentSessionId;if(n&&o&&this.provider===w.Viva&&!this.appToAppConfig?.enabled)try{let s=await this.recoverVivaTimeout(e,o);if(s.success)return this.transitionTo("SUCCESS"),this.safeFireCallback(()=>t.onSuccess?.(s)),s;i=s}catch(s){this.logger?.warn("Failed to get detailed error from verifyFinalStatus",{verifyErr:s}),i=this.buildErrorResultFromException(e.orderRef,s)}else if(o){this.strategy.abort();try{let s=await this.verifyWithRetry(e,o);if(s.success)return this.transitionTo("SUCCESS"),this.safeFireCallback(()=>t.onSuccess?.(s)),s;i=s}catch(s){this.logger?.warn("Failed to get detailed error from verifyFinalStatus",{verifyErr:s}),i=this.buildErrorResultFromException(e.orderRef,s)}}else this.strategy.abort(),i=this.buildErrorResultFromException(e.orderRef,r);return this.transitionTo("FAILED"),this.safeFireCallback(()=>t.onError?.(i)),i}async recoverVivaTimeout(e,r){let t;try{await Promise.race([this.strategy.cancelTransaction(()=>{}),new Promise((n,i)=>{t=setTimeout(()=>{i(new Error("Viva timeout cancellation timed out"))},d.VIVA_TIMEOUT_CANCEL_TIMEOUT_MS)})])}catch(n){this.logger?.warn("Viva timeout cancellation attempt failed",{error:n})}finally{t&&clearTimeout(t),this.strategy.abort()}return await this.verifyWithRetry(e,r)}static VERIFY_TIMEOUT_MS=1e4;static VERIFY_MAX_RETRIES=3;static VERIFY_RETRY_DELAY_MS=1500;async verifyWithRetry(e,r){let t,n=!0;for(let s=1;s<=d.VERIFY_MAX_RETRIES;s++)try{let a=await Promise.race([this.strategy.verifyFinalStatus(e,r),new Promise((c,u)=>setTimeout(()=>u(new Error("Verify timed out")),d.VERIFY_TIMEOUT_MS))]);if(a.status==="PENDING"){t=new Error("Verify returned pending status"),this.logger?.warn(`verifyFinalStatus attempt ${s}/${d.VERIFY_MAX_RETRIES} returned pending status`),s<d.VERIFY_MAX_RETRIES&&await new Promise(c=>setTimeout(c,d.VERIFY_RETRY_DELAY_MS));continue}return a}catch(a){t=a,a instanceof Error&&a.message==="Verify timed out"||(n=!1),this.logger?.warn(`verifyFinalStatus attempt ${s}/${d.VERIFY_MAX_RETRIES} failed`,{err:a}),s<d.VERIFY_MAX_RETRIES&&await new Promise(u=>setTimeout(u,d.VERIFY_RETRY_DELAY_MS))}let i=t instanceof Error?t.message:"Verify retries exhausted",o=n?y.PaymentTimeout:y.PaymentUnknown;throw new l(o,i)}buildErrorResultFromException(e,r){return r instanceof l?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"),d.TERMINAL_STATES.includes(this._currentState))return this.logger?.warn("Cannot cancel: Transaction already in terminal state",{state:this._currentState}),!1;if(!this._currentSessionId&&this._currentState==="IDLE")return this.logger?.warn("Cannot cancel: No active session to cancel",{state:this._currentState}),!1;this._cancellationIntent=!0,this.transitionTo("VERIFYING");try{return await this.strategy.cancelTransaction(r=>this.transitionTo(r))}catch(e){return this.logger?.error("Cancellation command failed",e),!1}};reset=()=>{d.TERMINAL_STATES.includes(this._currentState)&&(this._currentSessionId=void 0,this._cancellationIntent=!1,this.transitionTo("IDLE"))};refund=async(e,r)=>{let t=r??{};if(this.logger?.info("Initiating refund",{orderRef:e.orderRef}),this._currentSessionId=void 0,this._cancellationIntent=!1,!d.RESTING_STATES.includes(this._currentState))return this.generateErrorResult(e.orderRef,"UNKNOWN","A transaction is already in progress");this.transitionTo("IDLE");try{let i=(s,a)=>{a?.sessionId&&(this._currentSessionId=a.sessionId),s!=="FAILED"&&(this.transitionTo(s),this.fireStateCallback(s,t,e.orderRef))},o=await this.strategy.refundTransaction(e,i);return o.success?(this.transitionTo("SUCCESS"),this.safeFireCallback(()=>t.onSuccess?.(o))):this._cancellationIntent||o.status==="CANCELLED"?(this.transitionTo("FAILED"),this.safeFireCallback(()=>t.onCancelled?.({orderRef:e.orderRef,refPaymentId:this._currentSessionId}))):(this.transitionTo("FAILED"),this.safeFireCallback(()=>t.onError?.(o))),this.logger?.info("Refund completed",{success:o.success,orderRef:e.orderRef}),o}catch(i){if(this.logger?.error("Refund failed",i),this._cancellationIntent){try{if(this._currentSessionId){let s=await this.verifyWithRetry(e,this._currentSessionId);return s.success?(this.transitionTo("SUCCESS"),this.safeFireCallback(()=>t.onSuccess?.(s)),s):s.status==="CANCELLED"?(this.transitionTo("FAILED"),this.safeFireCallback(()=>t.onCancelled?.({orderRef:e.orderRef,refPaymentId:this._currentSessionId})),s):(this.transitionTo("FAILED"),this.safeFireCallback(()=>t.onError?.(s)),s)}}catch(s){this.logger?.warn("Refund final status verification failed",{verifyError: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 o=this.generateErrorResult(e.orderRef,"UNKNOWN",i instanceof Error?i.message:"Refund failed");return this.safeFireCallback(()=>t.onError?.(o)),o}}};var de=(n=>(n.CONNECTING="CONNECTING",n.CONNECTED="CONNECTED",n.OFFLINE="OFFLINE",n.DISCONNECTED="DISCONNECTED",n))(de||{});export{de as AppReaderStatus,O as MunchiPaymentSDK,R as PaymentInteractionState,I as SdkPaymentStatus};
|
|
1
|
+
import{PaymentFailureCode as y,PaymentProvider as D}from"@munchi_oy/core";var F="1.6.8";var c=class u extends Error{code;rawError;constructor(e,n,t){super(n),this.name="PaymentSDKError",this.code=e,this.rawError=t,Object.setPrototypeOf(this,u.prototype)}};import{PaymentApi as G,PaymentEventType as q,PaymentProviderEnum as K,SimplePaymentStatus as C,TransactionType as M}from"@munchi_oy/core";var h=(s=>(s.PENDING="PENDING",s.SUCCESS="SUCCESS",s.APPROVED="APPROVED",s.FAILED="FAILED",s.CANCELLED="CANCELLED",s.ERROR="ERROR",s))(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 S=class{constructor(e,n,t){this.messaging=n;this.config=t;this.api=new G(void 0,"",e)}api;abortController=null;currentRequestId=null;paymentProvider=K.Nets;async processPayment(e,n){this.abortController=new AbortController,n("CONNECTING");let t={amount:e.amountCents,businessId:Number(this.config.storeId),referenceId:e.orderRef,currency:e.currency,displayId:e.displayId,options:{allowPinBypass:!0,transactionType:M.Purchase}};try{let{data:r}=await this.api.initiateNetsTerminalTransaction(t),i=r.connectCloudRequestId;if(!i)throw new Error("connectCloudRequestId is missing from response.");if(this.currentRequestId=i,this.abortController.signal.aborted)throw new Error("Aborted");n("REQUIRES_INPUT",{sessionId:i});let s=await this.waitForPaymentCompletion(i,e.orderRef,this.abortController.signal);return this.currentRequestId=null,s}catch(r){throw this.currentRequestId=null,r instanceof c?r:new c("TERMINAL_BUSY","Failed to create Nets Intent",r)}}async waitForPaymentCompletion(e,n,t){let r=`nets.requests.${e}`,i=q.StatusChanged;return new Promise((s,o)=>{let a=!1,l=()=>{a=!0,typeof m=="function"&&m(),clearTimeout(p)},d=()=>{l(),o(new c("CANCELLED","Transaction cancelled"))};t.addEventListener("abort",d);let m=this.messaging.subscribe(r,i,f=>{a||f.status===C.Pending||(l(),t.removeEventListener("abort",d),s(this.handleSuccess(f)))}),p=setTimeout(async()=>{if(!(a||t.aborted))try{let f=await this.pollOrderStatus(e,n,this.config.storeId,t);s(this.handleSuccess(f))}catch(f){o(new c("TIMEOUT","Payment timed out and polling failed",f))}finally{t.removeEventListener("abort",d),l()}},1e4)})}async pollOrderStatus(e,n,t,r){let a=Date.now()+12e4;for(;Date.now()<a;){if(r.aborted)throw new Error("Aborted");try{let{data:l}=await this.api.getPaymentStatus({businessId:Number(t),orderId:n,provider:this.paymentProvider,referenceId:e});if(r.aborted)throw new Error("Aborted");if(l.status!==C.Pending)return l}catch(l){if(l instanceof Error&&l.message==="Aborted")throw l}await new Promise(l=>{let d=()=>{clearTimeout(m),l(void 0)};r.addEventListener("abort",d,{once:!0});let m=setTimeout(()=>{r.removeEventListener("abort",d),l(void 0)},2e3)})}throw new Error("Payment verification timed out.")}async cancelTransaction(e){if(!this.currentRequestId)return!1;let n={requestId:this.currentRequestId,businessId:Number(this.config.storeId)};try{return await this.api.cancelNetsTerminalTransaction(n),this.abortController?.abort(),this.currentRequestId=null,!0}catch{return!1}}async refundTransaction(e,n){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:M.ReturnOfGoods}},{data:r}=await this.api.initiateNetsTerminalTransaction(t),i=r.connectCloudRequestId;if(!i)throw new Error("connectCloudRequestId is missing from response.");if(this.currentRequestId=i,this.abortController.signal.aborted)throw new Error("Aborted");n("REQUIRES_INPUT",{sessionId:i});let s=await this.waitForPaymentCompletion(i,e.orderRef,this.abortController.signal);return this.currentRequestId=null,s}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,n){try{let{data:t}=await this.api.getPaymentStatus({businessId:Number(this.config.storeId),orderId:e.orderRef,provider:this.paymentProvider,referenceId:n});return this.handleSuccess(t)}catch(t){throw new c("NETWORK_ERROR","Failed to verify final Nets status",t)}}handleSuccess(e){let n=e.status===C.Success,t=e.status===C.Pending,r={success:n,status:n?"SUCCESS":t?"PENDING":"FAILED",orderId:e.orderId,transaction:e.transaction};return e.transactionId&&(r.transactionId=e.transactionId),e.error?.code&&(r.errorCode=e.error.code),e.error?.message&&(r.errorMessage=e.error.message),r}abort(){this.abortController?.abort()}};import{FeeType as B,PaymentApi as j,PaymentFailureCode as T,PaymentMethod as v,PaymentProvider as U,VivaApi as Q}from"@munchi_oy/core";var _=u=>{if(!u)return 0;let e=Number.parseInt(u,10);return Number.isFinite(e)?e:0},x=u=>{if(!u)return new Date().toISOString();let e=new Date(u);return Number.isNaN(e.getTime())?new Date().toISOString():e.toISOString()};var $="vivapayclient://pay/v1",X="abort",z="cancel",H="sale",k="transactionDetails",J=4e3,Z="534287",ee="38",te="493591",ne="497439",re=[te,ne],ie=["CREDIT","PREPAID","CORPORATE"],A=class{constructor(e,n,t,r){this.config=t;this.appToAppConfig=r;this.api=new Q(void 0,"",e),this.paymentApi=new j(void 0,"",e),this.paymentApiWithReference=this.paymentApi}api;paymentApi;abortController=null;currentClientTransactionId=null;currentIsvClientTransactionId=null;currentCurrency=null;currentOrderRef=null;currentSourceTerminalId=null;latestResult=null;paymentApiWithReference;wasCancelled=!1;getExecutionMode(){return"callback_driven"}async processPayment(e,n){if(!this.appToAppConfig.enabled)throw new c("STRATEGY_ERROR","Viva app-to-app is not enabled");n("CONNECTING");try{let{data:t}=await this.api.createVivaAppToAppPayment(this.buildCreatePaymentPayload(e));return await this.runAction(H,this.buildLaunchParams(t),e,n)}catch(t){throw new c("NETWORK_ERROR","Failed to create Viva app-to-app payment",t)}}async cancelTransaction(e){if(!this.currentClientTransactionId)return!1;let n=await this.lookupTransactionDetails();if(n)return this.latestResult=n,this.wasCancelled=n.status==="CANCELLED",this.abortController?.abort(),!0;try{await this.appToAppConfig.adapter.openUrl(this.buildActionUrl(X,{}))}catch(t){throw this.abortController?.abort(),new c("NETWORK_ERROR","Failed to launch Viva abort flow",t)}return this.wasCancelled=!0,this.abortController?.abort(),!0}async refundTransaction(e,n){n("CONNECTING");let t=await this.fetchPaymentReference(e),r=this.buildRefundLaunchParams(e,t);return this.runAction(z,r,e,n)}async verifyFinalStatus(e,n){return this.latestResult?this.latestResult:this.wasCancelled?{success:!1,status:"CANCELLED",orderId:this.currentOrderRef??n,errorCode:T.PaymentCancelledByUser,errorMessage:"Transaction was cancelled",transactionId:n}:{success:!1,status:"PENDING",orderId:this.currentOrderRef??n,transactionId:n}}abort(){this.abortController?.abort()}async runAction(e,n,t,r){if(!this.appToAppConfig.enabled)throw new c("STRATEGY_ERROR","Viva app-to-app is not enabled");this.abortController=new AbortController,this.latestResult=null,this.wasCancelled=!1,this.currentCurrency=t.currency,this.currentOrderRef=t.orderRef,this.currentSourceTerminalId=n.sourceTerminalId??n.tid??null,this.currentClientTransactionId=n.clientTransactionId??null,this.currentIsvClientTransactionId=n.ISV_clientTransactionId??null;let i=this.currentClientTransactionId?{sessionId:this.currentClientTransactionId}:void 0;return new Promise((s,o)=>{let a=(m,p)=>{m?.(),this.abortController?.signal.removeEventListener("abort",p)},l=()=>{a(d,l),o(new Error("Aborted"))},d=this.appToAppConfig.adapter.subscribe(m=>{if(this.abortController?.signal.aborted)return;let p=this.parseCallbackUrl(m);p&&this.matchesKnownTransactionId(p.rawParams)&&(this.latestResult=p.result,this.wasCancelled=p.result.status==="CANCELLED",this.currentClientTransactionId=null,this.currentIsvClientTransactionId=null,this.currentCurrency=null,this.currentOrderRef=null,this.currentSourceTerminalId=null,a(d,l),s(p.result))});this.abortController?.signal.addEventListener("abort",l),r("REQUIRES_INPUT",i),this.appToAppConfig.adapter.openUrl(this.buildActionUrl(e,n)).then(()=>{this.abortController?.signal.aborted}).catch(m=>{a(d,l),this.currentClientTransactionId=null,this.currentIsvClientTransactionId=null,this.currentCurrency=null,this.currentOrderRef=null,this.currentSourceTerminalId=null,o(new c("NETWORK_ERROR","Failed to launch Viva app",m))})})}buildCreatePaymentPayload(e){let n=e.options??{},t={amount:e.amountCents,referenceId:e.orderRef,businessId:Number(this.config.storeId),currency:e.currency,displayId:e.displayId,showReceipt:!1,showTransactionResult:!1};return n.tipAmount!==void 0&&(t.tipAmount=n.tipAmount),t}buildLaunchParams(e){let n={};return Object.entries(e).forEach(([t,r])=>{r!==void 0&&(n[t]=String(r))}),n}buildRefundLaunchParams(e,n){let t=e.options??{},r=n?.originalTransactionId?.trim()||e.originalTransactionId.trim(),i={amount:String(e.amountCents),clientTransactionId:r,show_receipt:"false",show_transaction_result:"false"};if(t.sourceCode&&(i.sourceCode=t.sourceCode),n?.referenceCode&&(i.orderCode=n.referenceCode),n?.shortReferenceCode&&(i.shortOrderCode=n.shortReferenceCode),n?.receiptNumber&&(i.referenceNumber=n.receiptNumber),n?.terminalId&&(i.tid=n.terminalId),n?.referenceCode||n?.shortReferenceCode||n?.receiptNumber)return i;let s=r;return/^\d+$/.test(s)&&(s.length===16?i.orderCode=s:s.length===10?i.shortOrderCode=s:i.referenceNumber=s),i}async fetchPaymentReference(e){try{let n={orderId:e.orderRef,provider:U.Viva,transactionId:e.originalTransactionId,businessId:e.businessId},{data:t}=await this.paymentApiWithReference.getPaymentReference(n);return this.extractPaymentReferenceContext(t)}catch{return}}extractPaymentReferenceContext(e){if(!e)return;let n=e.references;if(!n||typeof n!="object"||Array.isArray(n))return{originalTransactionId:e.originalTransactionId};let t=n,r={originalTransactionId:this.readReferenceValue(t.originalTransactionId)??this.readReferenceValue(t.clientTransactionId)??e.originalTransactionId},i=this.readReferenceValue(t.receiptNumber)??this.readReferenceValue(t.referenceNumber),s=this.readReferenceValue(t.referenceCode)??this.readReferenceValue(t.orderCode),o=this.readReferenceValue(t.shortReferenceCode)??this.readReferenceValue(t.shortOrderCode),a=this.readReferenceValue(t.terminalId)??this.readReferenceValue(t.tid);return i&&(r.receiptNumber=i),s&&(r.referenceCode=s),o&&(r.shortReferenceCode=o),a&&(r.terminalId=a),r}readReferenceValue(e){if(typeof e!="string")return;let n=e.trim();return n.length>0?n:void 0}buildActionUrl(e,n){let t=new URLSearchParams({action:e,appId:this.appToAppConfig.appId,callback:this.appToAppConfig.callbackUrl});return Object.entries(n).forEach(([r,i])=>{t.set(r,i)}),`${$}?${t.toString()}`}parseCallbackUrl(e){if(!this.matchesCallbackBase(e))return null;let n=new URL(e),t=Object.fromEntries(n.searchParams.entries()),r=this.isSuccessfulCallback(t),i=this.isCancelledCallback(t),s=this.resolveFailureCode(t,i),o={success:r,status:r?"SUCCESS":i?"CANCELLED":"FAILED",orderId:this.currentOrderRef??t.clientTransactionId??"unknown",errorCode:r?"":s,errorMessage:r?"":t.message??"Transaction failed without error details"};r&&(o.transaction=this.mapCallbackToTransaction(t));let a=t.transactionId??t.orderCode??t.clientTransactionId,l=t.errorCode??this.extractErrorReferenceFromMessage(t.message)??t.referenceNumber??t.tid??t.rrn;return a&&(o.transactionId=a),l&&(o.errorReference=l),{rawParams:t,result:o}}matchesCallbackBase(e){let n=new URL(e),t=new URL(this.appToAppConfig.callbackUrl);return n.protocol===t.protocol&&n.hostname===t.hostname&&n.pathname===t.pathname}matchesKnownTransactionId(e){let n=[this.currentClientTransactionId,this.currentIsvClientTransactionId].filter(r=>!!r);if(n.length===0)return!0;let t=[e.clientTransactionId,e.ISV_clientTransactionId].filter(r=>!!r);return t.length===0?!0:t.some(r=>n.includes(r))}resolveFailureCode(e,n){if(n)return T.PaymentCancelledByUser;let t=e.errorCode;if(t?.includes("."))return t;if(this.isTransactionDetailsAction(e)&&(e.transactionType??"").toLowerCase().includes("abort"))return T.SystemUnknown;let r=(e.message??"").toLowerCase(),i=(e.status??"").toLowerCase();return r.includes("declined")||i==="declined"||i==="fail"||i==="failed"?T.PaymentDeclined:T.SystemUnknown}isSuccessfulCallback(e){let n=(e.status??"").toLowerCase();return this.isTransactionDetailsAction(e)?n!=="success"?!1:!(e.transactionType??"").toLowerCase().includes("abort"):n==="success"}isTransactionDetailsAction(e){return(e.action??"").toLowerCase()===k.toLowerCase()}isCancelledCallback(e){let n=(e.status??"").toLowerCase(),t=(e.message??"").toLowerCase(),r=(e.errorCode??"").toLowerCase();return n==="cancelled"||n==="canceled"||t.includes("user_cancel")||t.includes("user cancel")||r.includes("user_cancel")||r.includes("user cancel")}extractErrorReferenceFromMessage(e){return e?e.match(/\((-?\d+)\)/)?.[1]:void 0}mapCallbackToTransaction(e){let n=e.transactionId??e.orderCode??e.clientTransactionId??"unknown",t=x(e.transactionDate),r=_(e.amount),i=_(e.tipAmount);return{amount:r,cardDetail:{aid:e.aid??null,applicationLabel:e.applicationLabel??null,cardNumber:e.accountNumber??"N/A",cardType:e.cardType??"N/A",issuer:e.bankId??null,orderCode:e.orderCode??null,timestamp:t,transactionId:n},createdAt:t,fees:this.buildFeesFromTip(i),id:n,label:null,provider:U.Viva,rawData:e,referenceId:e.clientTransactionId??null,roundingDifference:0,type:this.getVivaPaymentMethodType(e)}}getVivaPaymentMethodType(e){let n=(e.applicationLabel??"").toUpperCase().trim(),r=(e.accountNumber??"").substring(0,6),i=(e.bankId??"").trim();return r===Z||i===ee?v.Edenred:re.includes(r)?v.WoltBenefit:ie.some(s=>n.includes(s))?v.Credit:v.Debit}buildFeesFromTip(e){return!e||!this.currentCurrency?null:{parts:[{taxAmount:0,total:{amount:e,currency:this.currentCurrency},type:B.TipAmount,vatPercentage:0}]}}async lookupTransactionDetails(){if(!this.currentClientTransactionId||!this.currentSourceTerminalId)return null;let e={clientTransactionId:this.currentClientTransactionId,sourceTerminalId:this.currentSourceTerminalId};return new Promise(n=>{let t=!1,r=setTimeout(()=>{t||(t=!0,i(),n(null))},J),i=this.appToAppConfig.adapter.subscribe(s=>{if(t)return;let o=this.parseCallbackUrl(s);o&&this.matchesKnownTransactionId(o.rawParams)&&(t=!0,clearTimeout(r),i(),n(o.result))});this.appToAppConfig.adapter.openUrl(this.buildActionUrl(k,e)).catch(()=>{t||(t=!0,clearTimeout(r),i(),n(null))})})}};import{PaymentApi as se,PaymentFailureCode as O,PaymentProviderEnum as oe,SimplePaymentStatus as E}from"@munchi_oy/core";var P=class u{constructor(e,n,t){this.messaging=n;this.config=t;this.api=new se(void 0,"",e)}static INITIAL_POLL_DELAY_MS=1e4;static POLLING_DURATION_MS=18e4;static POLLING_INTERVAL_MS=2e3;static TIMEOUT_CANCEL_TIMEOUT_MS=5e3;static VERIFY_TIMEOUT_MS=1e4;static VERIFY_MAX_RETRIES=3;static VERIFY_RETRY_DELAY_MS=1500;api;abortController=null;currentSessionId=null;paymentProvider=oe.Viva;async processPayment(e,n){this.abortController=new AbortController,n("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:r}=await this.api.initiateTerminalTransaction(t);if(this.currentSessionId=r.sessionId,this.abortController.signal.aborted)throw new Error("Aborted");n("REQUIRES_INPUT",{sessionId:r.sessionId});let i=await this.waitForPaymentCompletion(r.sessionId,e,this.abortController.signal);return this.currentSessionId=null,i}catch(r){throw this.currentSessionId=null,r instanceof c?r:new c("NETWORK_ERROR","Failed to create Viva Intent",r)}}async waitForPaymentCompletion(e,n,t){let i=`viva.${this.config.channel.toLowerCase()}.requests.${e}`,s="payment:status-changed";return new Promise((o,a)=>{let l=!1,d=()=>{l=!0,typeof p=="function"&&p(),clearTimeout(f)},m=()=>{d(),a(new Error("Aborted"))};t.addEventListener("abort",m);let p=this.messaging.subscribe(i,s,I=>{l||I.status!==E.Pending&&(d(),t.removeEventListener("abort",m),o(this.handleSuccess(I)))}),f=setTimeout(async()=>{if(!(l||t.aborted))try{let I=await this.pollOrderStatus(e,n.orderRef,this.config.storeId,t);if(l)return;o(this.handleSuccess(I))}catch(I){if(l)return;if(I instanceof Error&&I.message==="Aborted"){a(I);return}try{let g=await this.recoverAfterPollingTimeout(n,e);o(g)}catch(g){a(g instanceof c?g:new c("TIMEOUT","Payment timed out and polling failed",g))}}finally{t.removeEventListener("abort",m),d()}},u.INITIAL_POLL_DELAY_MS)})}async pollOrderStatus(e,n,t,r){let s=Date.now()+u.POLLING_DURATION_MS;for(;Date.now()<s;){if(r.aborted)throw new Error("Aborted");try{let{data:o}=await this.api.getPaymentStatus({businessId:Number(t),orderId:n,provider:this.paymentProvider,referenceId:e});if(r.aborted)throw new Error("Aborted");if(o.status!==E.Pending)return o}catch(o){if(o instanceof Error&&o.message==="Aborted")throw o}await new Promise(o=>{let a=()=>{clearTimeout(l),o(void 0)};r.addEventListener("abort",a,{once:!0});let l=setTimeout(()=>{r.removeEventListener("abort",a),o(void 0)},u.POLLING_INTERVAL_MS)})}throw new Error("Payment verification timed out.")}async recoverAfterPollingTimeout(e,n){return await this.cancelSessionWithTimeout(n),await this.verifyFinalStatusWithRetry(e,n)}async cancelSessionWithTimeout(e){let n;try{await Promise.race([this.cancelSession(e),new Promise((t,r)=>{n=setTimeout(()=>{r(new Error("Viva timeout cancellation timed out"))},u.TIMEOUT_CANCEL_TIMEOUT_MS)})])}catch{}finally{n&&clearTimeout(n)}}async cancelSession(e){return await this.api.cancelVivaTransactionV2({cashRegisterId:this.config.storeId,sessionId:e}),this.currentSessionId===e&&(this.currentSessionId=null),!0}async verifyFinalStatusWithRetry(e,n){let t;for(let i=1;i<=u.VERIFY_MAX_RETRIES;i++)try{let s=await Promise.race([this.verifyFinalStatus(e,n),new Promise((o,a)=>setTimeout(()=>a(new Error("Verify timed out")),u.VERIFY_TIMEOUT_MS))]);if(s.status==="PENDING"){t=new Error("Verify returned pending status"),i<u.VERIFY_MAX_RETRIES&&await new Promise(o=>setTimeout(o,u.VERIFY_RETRY_DELAY_MS));continue}return s}catch(s){t=s,i<u.VERIFY_MAX_RETRIES&&await new Promise(o=>setTimeout(o,u.VERIFY_RETRY_DELAY_MS))}let r=t instanceof Error?t.message:"Verify retries exhausted";throw new c(O.PaymentTimeout,r)}handleSuccess(e){let n=e.status===E.Success,t={success:n,status:n?"SUCCESS":"FAILED",orderId:e.orderId,errorCode:e.error?.code||(n?"":O.SystemUnknown),errorMessage:e.error?.message||(n?"":"Transaction failed without error details")};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 n=this.currentSessionId;return this.currentSessionId=null,await this.api.cancelVivaTransactionV2({cashRegisterId:this.config.storeId,sessionId:n}),this.abortController?.abort(),!0}catch(n){throw this.currentSessionId=null,new c("NETWORK_ERROR","Failed to cancel Viva transaction",n)}}async verifyFinalStatus(e,n){try{let{data:t}=await this.api.getPaymentStatus({businessId:Number(this.config.storeId),orderId:e.orderRef,provider:this.paymentProvider,referenceId:n}),r=t.status===E.Success,i=t.status===E.Pending,s={success:r,status:r?"SUCCESS":i?"PENDING":"FAILED",orderId:t.orderId,errorCode:t.error?.code||(r||i?"":O.SystemUnknown),errorMessage:t.error?.message||(r||i?"":"Transaction failed without error details")};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,n){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:r}=await this.api.refundSingleVivaTransaction(t),i=r.success;return{success:i,status:i?"SUCCESS":"FAILED",orderId:e.orderRef,transactionId:r.sessionId}}catch(t){throw new c("NETWORK_ERROR","Failed to refund Viva transaction",t)}}abort(){this.abortController?.abort()}};import{PaymentApi as ae,PaymentEventType as V,PaymentFailureCode as ce,PaymentProviderEnum as le,SimplePaymentStatus as N,WorldlineApi as ue}from"@munchi_oy/core";import{isAxiosError as de}from"axios";var w=class{constructor(e,n,t){this.messaging=n;this.config=t;this.paymentApi=new ae(void 0,"",e),this.worldlineApi=new ue(void 0,"",e)}paymentApi;worldlineApi;abortController=null;currentOperationId=null;paymentProvider=le.Worldline;async processPayment(e,n){this.abortController=new AbortController,n("CONNECTING");let t={amount:e.amountCents,businessId:Number(this.config.storeId),currency:e.currency,displayId:e.displayId,referenceId:e.orderRef,showReceipt:!0,showTransactionResult:!0,...e.options&&"tipAmount"in e.options&&typeof e.options.tipAmount=="number"?{tipAmount:e.options.tipAmount}:{}};try{let r=await this.worldlineApi.createPayment(t),i=this.extractOperationId(r.data);if(this.currentOperationId=i,this.abortController.signal.aborted)throw new Error("Aborted");n("REQUIRES_INPUT",{sessionId:i});let s=await this.waitForPaymentCompletion(i,e.orderRef,V.StatusChanged,this.abortController.signal);return this.currentOperationId=null,s}catch(r){throw this.currentOperationId=null,r instanceof c?r:this.mapWorldlineRequestError(r,"Failed to create Worldline payment")}}async cancelTransaction(e){if(!this.currentOperationId)return!1;let n={businessId:Number(this.config.storeId),targetOperationId:this.currentOperationId};try{return await this.worldlineApi.cancelPayment(n),!0}catch(t){throw this.mapWorldlineRequestError(t,"Failed to cancel Worldline transaction")}}async refundTransaction(e,n){this.abortController=new AbortController,n("CONNECTING");let t={amount:e.amountCents,businessId:Number(this.config.storeId),currency:e.currency,displayId:e.displayId,orderReferenceId:e.orderRef,referenceId:e.originalTransactionId};try{let r=await this.worldlineApi.createRefund(t),i=this.extractOperationId(r.data);if(this.currentOperationId=i,this.abortController.signal.aborted)throw new Error("Aborted");n("REQUIRES_INPUT",{sessionId:i});let s=await this.waitForPaymentCompletion(i,e.orderRef,V.RefundStatusChanged,this.abortController.signal);return this.currentOperationId=null,s}catch(r){throw this.currentOperationId=null,r instanceof c?r:this.mapWorldlineRequestError(r,"Failed to refund Worldline transaction")}}async verifyFinalStatus(e,n){try{let{data:t}=await this.paymentApi.getPaymentStatus({businessId:Number(this.config.storeId),orderId:e.orderRef,provider:this.paymentProvider,referenceId:n});return this.handlePaymentStatus(t)}catch(t){throw this.mapWorldlineRequestError(t,"Failed to verify final Worldline status")}}abort(){this.abortController?.abort()}async waitForPaymentCompletion(e,n,t,r){let i=`worldline.requests.${e}`;return new Promise((s,o)=>{let a=!1,l=()=>{a=!0,typeof m=="function"&&m(),clearTimeout(p)},d=()=>{l(),o(new c("CANCELLED","Transaction cancelled"))};r.addEventListener("abort",d);let m=this.messaging.subscribe(i,t,f=>{a||f.status===N.Pending||(l(),r.removeEventListener("abort",d),s(this.handlePaymentStatus(f)))}),p=setTimeout(async()=>{if(!(a||r.aborted))try{let f=await this.pollOrderStatus(e,n,this.config.storeId,r);s(this.handlePaymentStatus(f))}catch(f){o(new c("TIMEOUT","Payment timed out and polling failed",f))}finally{r.removeEventListener("abort",d),l()}},1e4)})}async pollOrderStatus(e,n,t,r){let o=Date.now()+12e4;for(;Date.now()<o;){if(r.aborted)throw new Error("Aborted");try{let{data:a}=await this.paymentApi.getPaymentStatus({businessId:Number(t),orderId:n,provider:this.paymentProvider,referenceId:e});if(r.aborted)throw new Error("Aborted");if(a.status!==N.Pending)return a}catch(a){if(a instanceof Error&&a.message==="Aborted")throw a}await new Promise(a=>{let l=()=>{clearTimeout(d),a(void 0)};r.addEventListener("abort",l,{once:!0});let d=setTimeout(()=>{r.removeEventListener("abort",l),a(void 0)},2e3)})}throw new Error("Payment verification timed out.")}handlePaymentStatus(e){let n=e.status===N.Success,t=e.status===N.Pending,r={success:n,status:n?"SUCCESS":t?"PENDING":"FAILED",orderId:e.orderId,errorCode:e.error?.code||(n||t?"":ce.SystemUnknown),errorMessage:e.error?.message||(n||t?"":"Transaction failed without error details")};return e.transactionId&&(r.transactionId=e.transactionId),e.error?.referenceError&&(r.errorReference=e.error.referenceError),e.transaction&&(r.transaction=e.transaction),r}extractOperationId(e){let n=typeof e=="object"&&e!==null&&"operationId"in e&&typeof e.operationId=="string"?e.operationId:null;if(!n)throw new Error("operationId is missing from response.");return n}mapWorldlineRequestError(e,n){if(de(e)){if(e.response?.status===400||e.response?.status===409)return new c("TERMINAL_BUSY",n,e);if(e.response?.status===404)return new c("TERMINAL_OFFLINE",n,e)}return new c("NETWORK_ERROR",n,e)}};var L=class u{strategy;axios;messaging;timeoutMs;logger;_currentState="IDLE";_listeners=[];_cancellationIntent=!1;_currentSessionId;_autoResetTimer;autoResetOptions;appToAppConfig;static TERMINAL_STATES=["SUCCESS","FAILED","INTERNAL_ERROR"];static RESTING_STATES=["IDLE",...u.TERMINAL_STATES];constructor(e,n,t,r={},i){this.axios=e,this.messaging=n,this.logger=r.logger,this.timeoutMs=r.timeoutMs||3e4,this.autoResetOptions=r.autoResetOnPaymentComplete,this.appToAppConfig=r.appToApp,this.strategy=i??this.resolveStrategy(t)}get version(){return F}get currentState(){return this._currentState}generateErrorResult(e,n,t){return{success:!1,status:"ERROR",errorCode:this.normalizeErrorCode(n),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(n=>n!==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(r=>r(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){switch(e.provider){case D.Nets:return new S(this.axios,this.messaging,e);case D.Worldline:return new w(this.axios,this.messaging,e);case D.Viva:return this.appToAppConfig?.enabled?new A(this.axios,this.messaging,e,this.appToAppConfig):new P(this.axios,this.messaging,e);default:return new P(this.axios,this.messaging,e)}}initiateTransaction=async(e,n)=>{let t=n??{},r=e.orderRef;if(!u.RESTING_STATES.includes(this._currentState))return this.generateErrorResult(e.orderRef,"UNKNOWN","A transaction is already in progress");let s=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 o=(m,p)=>{p?.sessionId&&(this._currentSessionId=p.sessionId),m!=="FAILED"&&(this.transitionTo(m),this.fireStateCallback(m,t,r))},a=this.strategy.processPayment(e,o),d=(this.strategy.getExecutionMode?.()??"managed")==="callback_driven"?await a:await Promise.race([a,new Promise((m,p)=>{setTimeout(()=>{p(new c("TIMEOUT","Transaction timed out"))},this.timeoutMs)})]);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()-s}),d}catch(o){return this.logger?.warn("Transaction interrupted. Handling final status...",{error:o}),await this.handleTransactionError(e,o,t)}};async handleTransactionError(e,n,t={}){let r=n instanceof c&&(n.code==="TIMEOUT"||n.code===y.PaymentTimeout);if(!this._cancellationIntent&&!r&&(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}:{}}}let i;if(this.strategy.abort(),this._currentSessionId)try{let s=await this.strategy.verifyFinalStatus(e,this._currentSessionId);if(s.success)return this.transitionTo("SUCCESS"),this.safeFireCallback(()=>t.onSuccess?.(s)),s;i=r&&s.status==="PENDING"?this.buildErrorResultFromException(e.orderRef,n):s}catch(s){this.logger?.warn("Failed to get detailed error from verifyFinalStatus",{verifyErr:s}),i=this.buildErrorResultFromException(e.orderRef,s)}else i=this.buildErrorResultFromException(e.orderRef,n);return this.transitionTo("FAILED"),this.safeFireCallback(()=>t.onError?.(i)),i}buildErrorResultFromException(e,n){return n instanceof c?this.generateErrorResult(e,n.code,n.message):this.generateErrorResult(e,"UNKNOWN",n instanceof Error?n.message:"Unknown fatal error")}fireStateCallback(e,n,t){let r={orderRef:t,refPaymentId:this._currentSessionId};switch(e){case"CONNECTING":this.safeFireCallback(()=>n.onConnecting?.(r));break;case"REQUIRES_INPUT":this.safeFireCallback(()=>n.onRequiresInput?.(r));break;case"PROCESSING":this.safeFireCallback(()=>n.onProcessing?.(r));break}}safeFireCallback(e){try{e()}catch(n){this.logger?.warn("Callback execution failed",{error:n})}}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;if(!this._currentSessionId&&this._currentState==="IDLE")return this.logger?.warn("Cannot cancel: No active session to cancel",{state:this._currentState}),!1;this._cancellationIntent=!0,this.transitionTo("VERIFYING");try{return await this.strategy.cancelTransaction(n=>this.transitionTo(n))}catch(e){return this.logger?.error("Cancellation command failed",e),!1}};reset=()=>{u.TERMINAL_STATES.includes(this._currentState)&&(this._currentSessionId=void 0,this._cancellationIntent=!1,this.transitionTo("IDLE"))};refund=async(e,n)=>{let t=n??{};if(this.logger?.info("Initiating refund",{orderRef:e.orderRef}),this._currentSessionId=void 0,this._cancellationIntent=!1,!u.RESTING_STATES.includes(this._currentState))return this.generateErrorResult(e.orderRef,"UNKNOWN","A transaction is already in progress");this.transitionTo("IDLE");try{let i=(o,a)=>{a?.sessionId&&(this._currentSessionId=a.sessionId),o!=="FAILED"&&(this.transitionTo(o),this.fireStateCallback(o,t,e.orderRef))},s=await this.strategy.refundTransaction(e,i);return s.success?(this.transitionTo("SUCCESS"),this.safeFireCallback(()=>t.onSuccess?.(s))):this._cancellationIntent||s.status==="CANCELLED"?(this.transitionTo("FAILED"),this.safeFireCallback(()=>t.onCancelled?.({orderRef:e.orderRef,refPaymentId:this._currentSessionId}))):(this.transitionTo("FAILED"),this.safeFireCallback(()=>t.onError?.(s))),this.logger?.info("Refund completed",{success:s.success,orderRef:e.orderRef}),s}catch(i){if(this.logger?.error("Refund failed",i),this._cancellationIntent){try{if(this._currentSessionId){let o=await this.strategy.verifyFinalStatus(e,this._currentSessionId);return o.success?(this.transitionTo("SUCCESS"),this.safeFireCallback(()=>t.onSuccess?.(o)),o):o.status==="CANCELLED"?(this.transitionTo("FAILED"),this.safeFireCallback(()=>t.onCancelled?.({orderRef:e.orderRef,refPaymentId:this._currentSessionId})),o):(this.transitionTo("FAILED"),this.safeFireCallback(()=>t.onError?.(o)),o)}}catch(o){this.logger?.warn("Refund final status verification failed",{verifyError:o})}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 s=this.generateErrorResult(e.orderRef,"UNKNOWN",i instanceof Error?i.message:"Refund failed");return this.safeFireCallback(()=>t.onError?.(s)),s}}};var me=(r=>(r.CONNECTING="CONNECTING",r.CONNECTED="CONNECTED",r.OFFLINE="OFFLINE",r.DISCONNECTED="DISCONNECTED",r))(me||{});export{me as AppReaderStatus,L as MunchiPaymentSDK,R as PaymentInteractionState,h as SdkPaymentStatus};
|
|
@@ -4,11 +4,9 @@ import { type IMessagingAdapter, type IMunchiPaymentSDK, PaymentInteractionState
|
|
|
4
4
|
import type { SDKOptions } from "./types/sdk";
|
|
5
5
|
type StateListener = (state: PaymentInteractionState) => void;
|
|
6
6
|
export declare class MunchiPaymentSDK implements IMunchiPaymentSDK {
|
|
7
|
-
private static readonly VIVA_TIMEOUT_CANCEL_TIMEOUT_MS;
|
|
8
7
|
private strategy;
|
|
9
8
|
private axios;
|
|
10
9
|
private messaging;
|
|
11
|
-
private provider;
|
|
12
10
|
private timeoutMs;
|
|
13
11
|
private logger;
|
|
14
12
|
private _currentState;
|
|
@@ -34,11 +32,6 @@ export declare class MunchiPaymentSDK implements IMunchiPaymentSDK {
|
|
|
34
32
|
private resolveStrategy;
|
|
35
33
|
initiateTransaction: (params: SdkPaymentRequest, options?: TransactionOptions) => Promise<PaymentResult>;
|
|
36
34
|
private handleTransactionError;
|
|
37
|
-
private recoverVivaTimeout;
|
|
38
|
-
private static readonly VERIFY_TIMEOUT_MS;
|
|
39
|
-
private static readonly VERIFY_MAX_RETRIES;
|
|
40
|
-
private static readonly VERIFY_RETRY_DELAY_MS;
|
|
41
|
-
private verifyWithRetry;
|
|
42
35
|
private buildErrorResultFromException;
|
|
43
36
|
private fireStateCallback;
|
|
44
37
|
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;AAMtE,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,EAA2B,UAAU,EAAE,MAAM,aAAa,CAAC;AAEvE,KAAK,aAAa,GAAG,CAAC,KAAK,EAAE,uBAAuB,KAAK,IAAI,CAAC;AAE9D,qBAAa,gBAAiB,YAAW,iBAAiB;IACxD,OAAO,CAAC,
|
|
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;AAMtE,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,EAA2B,UAAU,EAAE,MAAM,aAAa,CAAC;AAEvE,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;IACnE,OAAO,CAAC,cAAc,CAA6B;IAEnD,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;IAW7B,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;IAsBhB,mBAAmB,GACxB,QAAQ,iBAAiB,EACzB,UAAU,kBAAkB,KAC3B,OAAO,CAAC,aAAa,CAAC,CAgGvB;YAEY,sBAAsB;IA0GpC,OAAO,CAAC,6BAA6B;IAarC,OAAO,CAAC,iBAAiB;IAsBzB,OAAO,CAAC,gBAAgB;IAQjB,MAAM,QAAa,OAAO,CAAC,OAAO,CAAC,CAyCxC;IAEK,KAAK,QAAO,IAAI,CAMrB;IAEK,MAAM,GACX,QAAQ,aAAa,EACrB,UAAU,kBAAkB,KAC3B,OAAO,CAAC,aAAa,CAAC,CAqIvB;CACH"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VivaAppToAppStrategy.d.ts","sourceRoot":"","sources":["../../../src/strategies/VivaAppToAppStrategy.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"VivaAppToAppStrategy.d.ts","sourceRoot":"","sources":["../../../src/strategies/VivaAppToAppStrategy.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AAG3C,OAAO,EACL,uBAAuB,EACvB,KAAK,cAAc,EACnB,KAAK,aAAa,EAClB,KAAK,qBAAqB,EAC1B,KAAK,aAAa,EAGnB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAEnD,OAAO,EACL,KAAK,gBAAgB,EACrB,qBAAqB,EACtB,MAAM,oBAAoB,CAAC;AAqC5B,qBAAa,oBAAqB,YAAW,gBAAgB;IAoBzD,OAAO,CAAC,MAAM;IACd,OAAO,CAAC,cAAc;IApBxB,OAAO,CAAC,GAAG,CAAU;IACrB,OAAO,CAAC,UAAU,CAAa;IAC/B,OAAO,CAAC,eAAe,CAAgC;IACvD,OAAO,CAAC,0BAA0B,CAAuB;IACzD,OAAO,CAAC,6BAA6B,CAAuB;IAC5D,OAAO,CAAC,eAAe,CAA2C;IAClE,OAAO,CAAC,eAAe,CAAuB;IAC9C,OAAO,CAAC,uBAAuB,CAAuB;IACtD,OAAO,CAAC,YAAY,CAA8B;IAClD,OAAO,CAAC,uBAAuB,CAI7B;IACF,OAAO,CAAC,YAAY,CAAS;gBAG3B,KAAK,EAAE,aAAa,EACpB,UAAU,EAAE,OAAO,EACX,MAAM,EAAE,qBAAqB,EAC7B,cAAc,EAAE,cAAc;IAWxC,gBAAgB;IAIV,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;IA8BnB,iBAAiB,CACrB,cAAc,EAAE,CAAC,KAAK,EAAE,uBAAuB,KAAK,IAAI,GACvD,OAAO,CAAC,OAAO,CAAC;IAgCb,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;IAanB,iBAAiB,CACrB,QAAQ,EAAE,cAAc,EACxB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,aAAa,CAAC;IAwBzB,KAAK,IAAI,IAAI;YAIC,SAAS;IAqGvB,OAAO,CAAC,yBAAyB;IAqBjC,OAAO,CAAC,iBAAiB;IAczB,OAAO,CAAC,uBAAuB;YAyDjB,qBAAqB;IAoBnC,OAAO,CAAC,8BAA8B;IAqDtC,OAAO,CAAC,kBAAkB;IAS1B,OAAO,CAAC,cAAc;IAiBtB,OAAO,CAAC,gBAAgB;IAyDxB,OAAO,CAAC,mBAAmB;IAW3B,OAAO,CAAC,yBAAyB;IAsBjC,OAAO,CAAC,kBAAkB;IAmC1B,OAAO,CAAC,oBAAoB;IAc5B,OAAO,CAAC,0BAA0B;IAKlC,OAAO,CAAC,mBAAmB;IAe3B,OAAO,CAAC,gCAAgC;IAWxC,OAAO,CAAC,wBAAwB;IAkChC,OAAO,CAAC,wBAAwB;IAuBhC,OAAO,CAAC,gBAAgB;YAoBV,wBAAwB;CA2DvC"}
|
|
@@ -4,6 +4,13 @@ import type { IPaymentStrategy } from "./IPaymentStrategy";
|
|
|
4
4
|
export declare class VivaStrategy implements IPaymentStrategy {
|
|
5
5
|
private messaging;
|
|
6
6
|
private config;
|
|
7
|
+
private static readonly INITIAL_POLL_DELAY_MS;
|
|
8
|
+
private static readonly POLLING_DURATION_MS;
|
|
9
|
+
private static readonly POLLING_INTERVAL_MS;
|
|
10
|
+
private static readonly TIMEOUT_CANCEL_TIMEOUT_MS;
|
|
11
|
+
private static readonly VERIFY_TIMEOUT_MS;
|
|
12
|
+
private static readonly VERIFY_MAX_RETRIES;
|
|
13
|
+
private static readonly VERIFY_RETRY_DELAY_MS;
|
|
7
14
|
private api;
|
|
8
15
|
private abortController;
|
|
9
16
|
private currentSessionId;
|
|
@@ -14,6 +21,10 @@ export declare class VivaStrategy implements IPaymentStrategy {
|
|
|
14
21
|
}) => void): Promise<PaymentResult>;
|
|
15
22
|
private waitForPaymentCompletion;
|
|
16
23
|
private pollOrderStatus;
|
|
24
|
+
private recoverAfterPollingTimeout;
|
|
25
|
+
private cancelSessionWithTimeout;
|
|
26
|
+
private cancelSession;
|
|
27
|
+
private verifyFinalStatusWithRetry;
|
|
17
28
|
private handleSuccess;
|
|
18
29
|
cancelTransaction(_onStateChange: (state: PaymentInteractionState) => void): Promise<boolean>;
|
|
19
30
|
verifyFinalStatus(request: PaymentRequest, sessionId: string): Promise<PaymentResult>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VivaStrategy.d.ts","sourceRoot":"","sources":["../../../src/strategies/VivaStrategy.ts"],"names":[],"mappings":"AAUA,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;
|
|
1
|
+
{"version":3,"file":"VivaStrategy.d.ts","sourceRoot":"","sources":["../../../src/strategies/VivaStrategy.ts"],"names":[],"mappings":"AAUA,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;IAgBjD,OAAO,CAAC,SAAS;IACjB,OAAO,CAAC,MAAM;IAhBhB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,qBAAqB,CAAS;IACtD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,mBAAmB,CAAU;IACrD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,mBAAmB,CAAQ;IACnD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,yBAAyB,CAAQ;IACzD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,iBAAiB,CAAS;IAClD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,kBAAkB,CAAK;IAC/C,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,qBAAqB,CAAQ;IAErD,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,CAAC,KAAK,EAAE,uBAAuB,EAAE,MAAM,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,GACvF,OAAO,CAAC,aAAa,CAAC;YA2CX,wBAAwB;YAmFxB,eAAe;YA+Cf,0BAA0B;YAS1B,wBAAwB;YAqBxB,aAAa;YAab,0BAA0B;IA+CxC,OAAO,CAAC,aAAa;IAgCf,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;IAsDrF,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"}
|
|
@@ -58,6 +58,7 @@ export interface RefundRequest {
|
|
|
58
58
|
* - Nets: preAuthorizationInfo (or reference)
|
|
59
59
|
*/
|
|
60
60
|
originalTransactionId: string;
|
|
61
|
+
businessId: number;
|
|
61
62
|
options?: VivaRefundOptions | NetsRefundOptions | WorldlineRefundOptions;
|
|
62
63
|
}
|
|
63
64
|
export interface PaymentTerminalConfig {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"payment.d.ts","sourceRoot":"","sources":["../../../src/types/payment.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACR,YAAY,EACZ,eAAe,EACf,YAAY,EACZ,cAAc,EACjB,MAAM,iBAAiB,CAAC;AAEzB,MAAM,MAAM,kBAAkB,GAAG,IAAI,CACnC,cAAc,EACd,IAAI,GAAG,WAAW,GAAG,UAAU,CAChC,CAAC;AAEF,oBAAY,gBAAgB;IAC1B,OAAO,YAAY;IACnB,OAAO,YAAY;IACnB,QAAQ,aAAa;IACrB,MAAM,WAAW;IACjB,SAAS,cAAc;IACvB,KAAK,UAAU;CAChB;AAED,oBAAY,uBAAuB;IACjC,IAAI,SAAS;IACb,UAAU,eAAe;IACzB,cAAc,mBAAmB;IACjC,UAAU,eAAe;IACzB,OAAO,YAAY;IACnB,MAAM,WAAW;IACjB,cAAc,mBAAmB;IACjC,SAAS,cAAc;CACxB;AAED,MAAM,WAAW,WAAW;IAC1B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,WAAW;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,gBAAgB;IAC/B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,iBAAkB,SAAQ,WAAW;CAErD;AAED,MAAM,WAAW,iBAAkB,SAAQ,WAAW;IACpD,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED,MAAM,WAAW,sBAAuB,SAAQ,gBAAgB;CAE/D;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,YAAY,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,WAAW,GAAG,WAAW,GAAG,gBAAgB,CAAC;CACxD;AAED,MAAM,WAAW,aAAa;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,YAAY,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB;;;;;;;OAOG;IACH,qBAAqB,EAAE,MAAM,CAAC;IAC9B,OAAO,CAAC,EAAE,iBAAiB,GAAG,iBAAiB,GAAG,sBAAsB,CAAC;CAC1E;AAED,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,YAAY,CAAC;IACtB,QAAQ,CAAC,EAAE,eAAe,GAAG,IAAI,CAAC;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,gBAAgB,CAAC;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,cAAc,CAAC;CAC9B;AAED,MAAM,WAAW,kBAAkB;IACjC,YAAY,CAAC,EAAE,CAAC,GAAG,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,YAAY,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;KAAE,KAAK,IAAI,CAAC;IACtF,eAAe,CAAC,EAAE,CAAC,GAAG,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,YAAY,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;KAAE,KAAK,IAAI,CAAC;IACzF,YAAY,CAAC,EAAE,CAAC,GAAG,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,YAAY,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;KAAE,KAAK,IAAI,CAAC;IACtF,WAAW,CAAC,EAAE,CAAC,GAAG,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,YAAY,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;KAAE,KAAK,IAAI,CAAC;IACrF,SAAS,CAAC,EAAE,CAAC,MAAM,EAAE,aAAa,KAAK,IAAI,CAAC;IAC5C,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,aAAa,KAAK,IAAI,CAAC;IAC1C,WAAW,CAAC,EAAE,CAAC,GAAG,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,YAAY,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;KAAE,KAAK,IAAI,CAAC;CACtF;AAED,MAAM,WAAW,iBAAiB;IAChC,SAAS,CAAC,CAAC,EACT,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,IAAI,GAC3B,MAAM,IAAI,CAAC;CACf;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,YAAY,EAAE,uBAAuB,CAAC;IAC/C,QAAQ,CAAC,eAAe,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7C,SAAS,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,uBAAuB,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;IAC1E,mBAAmB,CACjB,MAAM,EAAE,cAAc,EACtB,OAAO,CAAC,EAAE,kBAAkB,GAC3B,OAAO,CAAC,aAAa,CAAC,CAAC;IAC1B,MAAM,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;IAC3B,MAAM,CAAC,MAAM,EAAE,aAAa,EAAE,OAAO,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;IACpF,KAAK,IAAI,IAAI,CAAC;CACf"}
|
|
1
|
+
{"version":3,"file":"payment.d.ts","sourceRoot":"","sources":["../../../src/types/payment.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACR,YAAY,EACZ,eAAe,EACf,YAAY,EACZ,cAAc,EACjB,MAAM,iBAAiB,CAAC;AAEzB,MAAM,MAAM,kBAAkB,GAAG,IAAI,CACnC,cAAc,EACd,IAAI,GAAG,WAAW,GAAG,UAAU,CAChC,CAAC;AAEF,oBAAY,gBAAgB;IAC1B,OAAO,YAAY;IACnB,OAAO,YAAY;IACnB,QAAQ,aAAa;IACrB,MAAM,WAAW;IACjB,SAAS,cAAc;IACvB,KAAK,UAAU;CAChB;AAED,oBAAY,uBAAuB;IACjC,IAAI,SAAS;IACb,UAAU,eAAe;IACzB,cAAc,mBAAmB;IACjC,UAAU,eAAe;IACzB,OAAO,YAAY;IACnB,MAAM,WAAW;IACjB,cAAc,mBAAmB;IACjC,SAAS,cAAc;CACxB;AAED,MAAM,WAAW,WAAW;IAC1B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,WAAW;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,gBAAgB;IAC/B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,iBAAkB,SAAQ,WAAW;CAErD;AAED,MAAM,WAAW,iBAAkB,SAAQ,WAAW;IACpD,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED,MAAM,WAAW,sBAAuB,SAAQ,gBAAgB;CAE/D;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,YAAY,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,WAAW,GAAG,WAAW,GAAG,gBAAgB,CAAC;CACxD;AAED,MAAM,WAAW,aAAa;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,YAAY,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB;;;;;;;OAOG;IACH,qBAAqB,EAAE,MAAM,CAAC;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,iBAAiB,GAAG,iBAAiB,GAAG,sBAAsB,CAAC;CAC1E;AAED,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,YAAY,CAAC;IACtB,QAAQ,CAAC,EAAE,eAAe,GAAG,IAAI,CAAC;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,gBAAgB,CAAC;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,cAAc,CAAC;CAC9B;AAED,MAAM,WAAW,kBAAkB;IACjC,YAAY,CAAC,EAAE,CAAC,GAAG,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,YAAY,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;KAAE,KAAK,IAAI,CAAC;IACtF,eAAe,CAAC,EAAE,CAAC,GAAG,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,YAAY,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;KAAE,KAAK,IAAI,CAAC;IACzF,YAAY,CAAC,EAAE,CAAC,GAAG,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,YAAY,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;KAAE,KAAK,IAAI,CAAC;IACtF,WAAW,CAAC,EAAE,CAAC,GAAG,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,YAAY,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;KAAE,KAAK,IAAI,CAAC;IACrF,SAAS,CAAC,EAAE,CAAC,MAAM,EAAE,aAAa,KAAK,IAAI,CAAC;IAC5C,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,aAAa,KAAK,IAAI,CAAC;IAC1C,WAAW,CAAC,EAAE,CAAC,GAAG,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,YAAY,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;KAAE,KAAK,IAAI,CAAC;CACtF;AAED,MAAM,WAAW,iBAAiB;IAChC,SAAS,CAAC,CAAC,EACT,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,IAAI,GAC3B,MAAM,IAAI,CAAC;CACf;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,YAAY,EAAE,uBAAuB,CAAC;IAC/C,QAAQ,CAAC,eAAe,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7C,SAAS,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,uBAAuB,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;IAC1E,mBAAmB,CACjB,MAAM,EAAE,cAAc,EACtB,OAAO,CAAC,EAAE,kBAAkB,GAC3B,OAAO,CAAC,aAAa,CAAC,CAAC;IAC1B,MAAM,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;IAC3B,MAAM,CAAC,MAAM,EAAE,aAAa,EAAE,OAAO,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;IACpF,KAAK,IAAI,IAAI,CAAC;CACf"}
|
package/dist/src/version.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const VERSION = "1.6.
|
|
1
|
+
export declare const VERSION = "1.6.8";
|
|
2
2
|
//# sourceMappingURL=version.d.ts.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@munchi_oy/payments",
|
|
3
|
-
"version": "1.6.
|
|
3
|
+
"version": "1.6.8",
|
|
4
4
|
"description": "Munchi Payments SDK - Payment processing utilities",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"publishConfig": {
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"@types/jest": "^30.0.0",
|
|
33
33
|
"tsup": "^8.0.0",
|
|
34
34
|
"typescript": "^5.0.0",
|
|
35
|
-
"@munchi_oy/core": "1.9.
|
|
35
|
+
"@munchi_oy/core": "1.9.7"
|
|
36
36
|
},
|
|
37
37
|
"scripts": {
|
|
38
38
|
"test": "jest",
|
package/src/MunchiPaymentSDK.ts
CHANGED
|
@@ -24,11 +24,9 @@ import type { AppToAppConfig, ILogger, SDKOptions } from "./types/sdk";
|
|
|
24
24
|
type StateListener = (state: PaymentInteractionState) => void;
|
|
25
25
|
|
|
26
26
|
export class MunchiPaymentSDK implements IMunchiPaymentSDK {
|
|
27
|
-
private static readonly VIVA_TIMEOUT_CANCEL_TIMEOUT_MS = 5000;
|
|
28
27
|
private strategy: IPaymentStrategy;
|
|
29
28
|
private axios: AxiosInstance;
|
|
30
29
|
private messaging: IMessagingAdapter;
|
|
31
|
-
private provider: PaymentProvider | null | undefined;
|
|
32
30
|
private timeoutMs: number;
|
|
33
31
|
private logger: ILogger | undefined;
|
|
34
32
|
private _currentState: PaymentInteractionState = PaymentInteractionState.IDLE;
|
|
@@ -59,7 +57,6 @@ export class MunchiPaymentSDK implements IMunchiPaymentSDK {
|
|
|
59
57
|
) {
|
|
60
58
|
this.axios = axios;
|
|
61
59
|
this.messaging = messaging;
|
|
62
|
-
this.provider = config.provider;
|
|
63
60
|
this.logger = options.logger;
|
|
64
61
|
this.timeoutMs = options.timeoutMs || 30000;
|
|
65
62
|
this.autoResetOptions = options.autoResetOnPaymentComplete;
|
|
@@ -318,7 +315,9 @@ export class MunchiPaymentSDK implements IMunchiPaymentSDK {
|
|
|
318
315
|
): Promise<PaymentResult> {
|
|
319
316
|
const isTimeout =
|
|
320
317
|
originalError instanceof PaymentSDKError &&
|
|
321
|
-
originalError.code === PaymentErrorCode.TIMEOUT
|
|
318
|
+
(originalError.code === PaymentErrorCode.TIMEOUT ||
|
|
319
|
+
originalError.code ===
|
|
320
|
+
(PaymentFailureCode.PaymentTimeout as unknown as PaymentErrorCode));
|
|
322
321
|
|
|
323
322
|
if (!this._cancellationIntent && !isTimeout) {
|
|
324
323
|
this.transitionTo(PaymentInteractionState.VERIFYING);
|
|
@@ -333,7 +332,7 @@ export class MunchiPaymentSDK implements IMunchiPaymentSDK {
|
|
|
333
332
|
if (this._cancellationIntent) {
|
|
334
333
|
try {
|
|
335
334
|
if (this._currentSessionId) {
|
|
336
|
-
const finalStatus = await this.
|
|
335
|
+
const finalStatus = await this.strategy.verifyFinalStatus(
|
|
337
336
|
params,
|
|
338
337
|
this._currentSessionId,
|
|
339
338
|
);
|
|
@@ -371,38 +370,17 @@ export class MunchiPaymentSDK implements IMunchiPaymentSDK {
|
|
|
371
370
|
}
|
|
372
371
|
|
|
373
372
|
let errorResult: PaymentResult;
|
|
374
|
-
const sessionId = this._currentSessionId;
|
|
375
373
|
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
this.provider === PaymentProvider.Viva &&
|
|
380
|
-
!this.appToAppConfig?.enabled
|
|
381
|
-
) {
|
|
382
|
-
try {
|
|
383
|
-
const finalStatus = await this.recoverVivaTimeout(params, sessionId);
|
|
384
|
-
|
|
385
|
-
if (finalStatus.success) {
|
|
386
|
-
this.transitionTo(PaymentInteractionState.SUCCESS);
|
|
387
|
-
this.safeFireCallback(() => callbacks.onSuccess?.(finalStatus));
|
|
388
|
-
return finalStatus;
|
|
389
|
-
}
|
|
374
|
+
// If we're here, something went wrong (timeout, user cancel, error).
|
|
375
|
+
// Ensure we stop any ongoing strategy work (polling, etc.) to prevent leaks.
|
|
376
|
+
this.strategy.abort();
|
|
390
377
|
|
|
391
|
-
|
|
392
|
-
} catch (verifyErr) {
|
|
393
|
-
this.logger?.warn(
|
|
394
|
-
"Failed to get detailed error from verifyFinalStatus",
|
|
395
|
-
{ verifyErr },
|
|
396
|
-
);
|
|
397
|
-
errorResult = this.buildErrorResultFromException(
|
|
398
|
-
params.orderRef,
|
|
399
|
-
verifyErr,
|
|
400
|
-
);
|
|
401
|
-
}
|
|
402
|
-
} else if (sessionId) {
|
|
403
|
-
this.strategy.abort();
|
|
378
|
+
if (this._currentSessionId) {
|
|
404
379
|
try {
|
|
405
|
-
const finalStatus = await this.
|
|
380
|
+
const finalStatus = await this.strategy.verifyFinalStatus(
|
|
381
|
+
params,
|
|
382
|
+
this._currentSessionId,
|
|
383
|
+
);
|
|
406
384
|
|
|
407
385
|
if (finalStatus.success) {
|
|
408
386
|
this.transitionTo(PaymentInteractionState.SUCCESS);
|
|
@@ -410,7 +388,10 @@ export class MunchiPaymentSDK implements IMunchiPaymentSDK {
|
|
|
410
388
|
return finalStatus;
|
|
411
389
|
}
|
|
412
390
|
|
|
413
|
-
errorResult =
|
|
391
|
+
errorResult =
|
|
392
|
+
isTimeout && finalStatus.status === SdkPaymentStatus.PENDING
|
|
393
|
+
? this.buildErrorResultFromException(params.orderRef, originalError)
|
|
394
|
+
: finalStatus;
|
|
414
395
|
} catch (verifyErr) {
|
|
415
396
|
this.logger?.warn(
|
|
416
397
|
"Failed to get detailed error from verifyFinalStatus",
|
|
@@ -422,7 +403,6 @@ export class MunchiPaymentSDK implements IMunchiPaymentSDK {
|
|
|
422
403
|
);
|
|
423
404
|
}
|
|
424
405
|
} else {
|
|
425
|
-
this.strategy.abort();
|
|
426
406
|
errorResult = this.buildErrorResultFromException(
|
|
427
407
|
params.orderRef,
|
|
428
408
|
originalError,
|
|
@@ -434,93 +414,6 @@ export class MunchiPaymentSDK implements IMunchiPaymentSDK {
|
|
|
434
414
|
return errorResult;
|
|
435
415
|
}
|
|
436
416
|
|
|
437
|
-
private async recoverVivaTimeout(
|
|
438
|
-
params: SdkPaymentRequest,
|
|
439
|
-
sessionId: string,
|
|
440
|
-
): Promise<PaymentResult> {
|
|
441
|
-
let cancelTimeout: ReturnType<typeof setTimeout> | undefined;
|
|
442
|
-
|
|
443
|
-
try {
|
|
444
|
-
await Promise.race([
|
|
445
|
-
this.strategy.cancelTransaction(() => undefined),
|
|
446
|
-
new Promise<never>((_, reject) => {
|
|
447
|
-
cancelTimeout = setTimeout(() => {
|
|
448
|
-
reject(new Error("Viva timeout cancellation timed out"));
|
|
449
|
-
}, MunchiPaymentSDK.VIVA_TIMEOUT_CANCEL_TIMEOUT_MS);
|
|
450
|
-
}),
|
|
451
|
-
]);
|
|
452
|
-
} catch (error) {
|
|
453
|
-
this.logger?.warn("Viva timeout cancellation attempt failed", { error });
|
|
454
|
-
} finally {
|
|
455
|
-
if (cancelTimeout) {
|
|
456
|
-
clearTimeout(cancelTimeout);
|
|
457
|
-
}
|
|
458
|
-
this.strategy.abort();
|
|
459
|
-
}
|
|
460
|
-
|
|
461
|
-
return await this.verifyWithRetry(params, sessionId);
|
|
462
|
-
}
|
|
463
|
-
|
|
464
|
-
private static readonly VERIFY_TIMEOUT_MS = 10000;
|
|
465
|
-
private static readonly VERIFY_MAX_RETRIES = 3;
|
|
466
|
-
private static readonly VERIFY_RETRY_DELAY_MS = 1500;
|
|
467
|
-
|
|
468
|
-
private async verifyWithRetry(
|
|
469
|
-
params: SdkPaymentRequest,
|
|
470
|
-
sessionId: string,
|
|
471
|
-
): Promise<PaymentResult> {
|
|
472
|
-
let lastError: unknown;
|
|
473
|
-
let allTimeouts = true;
|
|
474
|
-
|
|
475
|
-
for (let attempt = 1; attempt <= MunchiPaymentSDK.VERIFY_MAX_RETRIES; attempt++) {
|
|
476
|
-
try {
|
|
477
|
-
const result = await Promise.race([
|
|
478
|
-
this.strategy.verifyFinalStatus(params, sessionId),
|
|
479
|
-
new Promise<never>((_, reject) =>
|
|
480
|
-
setTimeout(
|
|
481
|
-
() => reject(new Error("Verify timed out")),
|
|
482
|
-
MunchiPaymentSDK.VERIFY_TIMEOUT_MS,
|
|
483
|
-
),
|
|
484
|
-
),
|
|
485
|
-
]);
|
|
486
|
-
if (result.status === SdkPaymentStatus.PENDING) {
|
|
487
|
-
lastError = new Error("Verify returned pending status");
|
|
488
|
-
this.logger?.warn(
|
|
489
|
-
`verifyFinalStatus attempt ${attempt}/${MunchiPaymentSDK.VERIFY_MAX_RETRIES} returned pending status`,
|
|
490
|
-
);
|
|
491
|
-
if (attempt < MunchiPaymentSDK.VERIFY_MAX_RETRIES) {
|
|
492
|
-
await new Promise((resolve) =>
|
|
493
|
-
setTimeout(resolve, MunchiPaymentSDK.VERIFY_RETRY_DELAY_MS),
|
|
494
|
-
);
|
|
495
|
-
}
|
|
496
|
-
continue;
|
|
497
|
-
}
|
|
498
|
-
return result;
|
|
499
|
-
} catch (err) {
|
|
500
|
-
lastError = err;
|
|
501
|
-
const isTimeout = err instanceof Error && err.message === "Verify timed out";
|
|
502
|
-
if (!isTimeout) allTimeouts = false;
|
|
503
|
-
|
|
504
|
-
this.logger?.warn(
|
|
505
|
-
`verifyFinalStatus attempt ${attempt}/${MunchiPaymentSDK.VERIFY_MAX_RETRIES} failed`,
|
|
506
|
-
{ err },
|
|
507
|
-
);
|
|
508
|
-
if (attempt < MunchiPaymentSDK.VERIFY_MAX_RETRIES) {
|
|
509
|
-
await new Promise((resolve) =>
|
|
510
|
-
setTimeout(resolve, MunchiPaymentSDK.VERIFY_RETRY_DELAY_MS),
|
|
511
|
-
);
|
|
512
|
-
}
|
|
513
|
-
}
|
|
514
|
-
}
|
|
515
|
-
|
|
516
|
-
const message = lastError instanceof Error ? lastError.message : "Verify retries exhausted";
|
|
517
|
-
const code = allTimeouts
|
|
518
|
-
? PaymentFailureCode.PaymentTimeout
|
|
519
|
-
: PaymentFailureCode.PaymentUnknown;
|
|
520
|
-
|
|
521
|
-
throw new PaymentSDKError(code as unknown as PaymentErrorCode, message);
|
|
522
|
-
}
|
|
523
|
-
|
|
524
417
|
private buildErrorResultFromException(
|
|
525
418
|
orderRef: string,
|
|
526
419
|
error: unknown,
|
|
@@ -689,7 +582,7 @@ export class MunchiPaymentSDK implements IMunchiPaymentSDK {
|
|
|
689
582
|
if (this._cancellationIntent) {
|
|
690
583
|
try {
|
|
691
584
|
if (this._currentSessionId) {
|
|
692
|
-
const finalStatus = await this.
|
|
585
|
+
const finalStatus = await this.strategy.verifyFinalStatus(
|
|
693
586
|
params,
|
|
694
587
|
this._currentSessionId,
|
|
695
588
|
);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
type CreateTerminalPaymentDto,
|
|
3
3
|
FeeType,
|
|
4
|
+
type GetPaymentReferenceDto,
|
|
4
5
|
PaymentApi,
|
|
5
6
|
PaymentFailureCode,
|
|
6
7
|
PaymentMethod,
|
|
@@ -417,11 +418,15 @@ export class VivaAppToAppStrategy implements IPaymentStrategy {
|
|
|
417
418
|
request: RefundRequest,
|
|
418
419
|
): Promise<PaymentReferenceContext | undefined> {
|
|
419
420
|
try {
|
|
420
|
-
|
|
421
|
+
|
|
422
|
+
const payload = {
|
|
421
423
|
orderId: request.orderRef,
|
|
422
424
|
provider: PaymentProvider.Viva,
|
|
423
425
|
transactionId: request.originalTransactionId,
|
|
424
|
-
|
|
426
|
+
businessId: request.businessId,
|
|
427
|
+
} satisfies GetPaymentReferenceDto;
|
|
428
|
+
|
|
429
|
+
const { data } = await this.paymentApiWithReference.getPaymentReference(payload);
|
|
425
430
|
|
|
426
431
|
return this.extractPaymentReferenceContext(data);
|
|
427
432
|
} catch {
|
|
@@ -23,10 +23,19 @@ import {
|
|
|
23
23
|
import type { IPaymentStrategy } from "./IPaymentStrategy";
|
|
24
24
|
|
|
25
25
|
export class VivaStrategy implements IPaymentStrategy {
|
|
26
|
+
private static readonly INITIAL_POLL_DELAY_MS = 10000;
|
|
27
|
+
private static readonly POLLING_DURATION_MS = 180000;
|
|
28
|
+
private static readonly POLLING_INTERVAL_MS = 2000;
|
|
29
|
+
private static readonly TIMEOUT_CANCEL_TIMEOUT_MS = 5000;
|
|
30
|
+
private static readonly VERIFY_TIMEOUT_MS = 10000;
|
|
31
|
+
private static readonly VERIFY_MAX_RETRIES = 3;
|
|
32
|
+
private static readonly VERIFY_RETRY_DELAY_MS = 1500;
|
|
33
|
+
|
|
26
34
|
private api: PaymentApi;
|
|
27
35
|
private abortController: AbortController | null = null;
|
|
28
36
|
private currentSessionId: string | null = null;
|
|
29
37
|
private paymentProvider = PaymentProviderEnum.Viva;
|
|
38
|
+
|
|
30
39
|
constructor(
|
|
31
40
|
axios: AxiosInstance,
|
|
32
41
|
private messaging: IMessagingAdapter,
|
|
@@ -35,8 +44,6 @@ export class VivaStrategy implements IPaymentStrategy {
|
|
|
35
44
|
this.api = new PaymentApi(undefined, "", axios);
|
|
36
45
|
}
|
|
37
46
|
|
|
38
|
-
|
|
39
|
-
|
|
40
47
|
async processPayment(
|
|
41
48
|
request: PaymentRequest,
|
|
42
49
|
onStateChange: (state: PaymentInteractionState, detail?: { sessionId?: string }) => void,
|
|
@@ -66,7 +73,7 @@ export class VivaStrategy implements IPaymentStrategy {
|
|
|
66
73
|
|
|
67
74
|
const result = await this.waitForPaymentCompletion(
|
|
68
75
|
data.sessionId,
|
|
69
|
-
request
|
|
76
|
+
request,
|
|
70
77
|
this.abortController.signal,
|
|
71
78
|
);
|
|
72
79
|
|
|
@@ -85,7 +92,7 @@ export class VivaStrategy implements IPaymentStrategy {
|
|
|
85
92
|
|
|
86
93
|
private async waitForPaymentCompletion(
|
|
87
94
|
sessionId: string,
|
|
88
|
-
|
|
95
|
+
request: PaymentRequest,
|
|
89
96
|
signal: AbortSignal,
|
|
90
97
|
): Promise<PaymentResult> {
|
|
91
98
|
const channel = this.config.channel.toLowerCase()
|
|
@@ -128,25 +135,41 @@ export class VivaStrategy implements IPaymentStrategy {
|
|
|
128
135
|
try {
|
|
129
136
|
const finalResult = await this.pollOrderStatus(
|
|
130
137
|
sessionId,
|
|
131
|
-
orderRef,
|
|
138
|
+
request.orderRef,
|
|
132
139
|
this.config.storeId,
|
|
133
140
|
signal,
|
|
134
141
|
);
|
|
142
|
+
if (isResolved) return;
|
|
135
143
|
resolve(this.handleSuccess(finalResult));
|
|
136
144
|
} catch (pollError) {
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
+
if (isResolved) return;
|
|
146
|
+
if (pollError instanceof Error && pollError.message === "Aborted") {
|
|
147
|
+
reject(pollError);
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
try {
|
|
152
|
+
const finalResult = await this.recoverAfterPollingTimeout(
|
|
153
|
+
request,
|
|
154
|
+
sessionId,
|
|
155
|
+
);
|
|
156
|
+
resolve(finalResult);
|
|
157
|
+
} catch (recoveryError) {
|
|
158
|
+
reject(
|
|
159
|
+
recoveryError instanceof PaymentSDKError
|
|
160
|
+
? recoveryError
|
|
161
|
+
: new PaymentSDKError(
|
|
162
|
+
PaymentErrorCode.TIMEOUT,
|
|
163
|
+
"Payment timed out and polling failed",
|
|
164
|
+
recoveryError,
|
|
165
|
+
),
|
|
166
|
+
);
|
|
167
|
+
}
|
|
145
168
|
} finally {
|
|
146
169
|
signal.removeEventListener("abort", onAbort);
|
|
147
170
|
cleanup();
|
|
148
171
|
}
|
|
149
|
-
},
|
|
172
|
+
}, VivaStrategy.INITIAL_POLL_DELAY_MS);
|
|
150
173
|
});
|
|
151
174
|
}
|
|
152
175
|
|
|
@@ -156,10 +179,8 @@ export class VivaStrategy implements IPaymentStrategy {
|
|
|
156
179
|
businessId: string,
|
|
157
180
|
signal: AbortSignal,
|
|
158
181
|
): Promise<PaymentStatusDto> {
|
|
159
|
-
const POLLING_DURATION_MS = 180000;
|
|
160
|
-
const INTERVAL_MS = 2000;
|
|
161
182
|
const startTime = Date.now();
|
|
162
|
-
const deadline = startTime + POLLING_DURATION_MS;
|
|
183
|
+
const deadline = startTime + VivaStrategy.POLLING_DURATION_MS;
|
|
163
184
|
|
|
164
185
|
while (Date.now() < deadline) {
|
|
165
186
|
if (signal.aborted) {
|
|
@@ -193,12 +214,102 @@ export class VivaStrategy implements IPaymentStrategy {
|
|
|
193
214
|
const timeout = setTimeout(() => {
|
|
194
215
|
signal.removeEventListener("abort", onAbort);
|
|
195
216
|
resolve(undefined);
|
|
196
|
-
},
|
|
217
|
+
}, VivaStrategy.POLLING_INTERVAL_MS);
|
|
197
218
|
});
|
|
198
219
|
}
|
|
199
220
|
throw new Error("Payment verification timed out.");
|
|
200
221
|
}
|
|
201
222
|
|
|
223
|
+
private async recoverAfterPollingTimeout(
|
|
224
|
+
request: PaymentRequest,
|
|
225
|
+
sessionId: string,
|
|
226
|
+
): Promise<PaymentResult> {
|
|
227
|
+
await this.cancelSessionWithTimeout(sessionId);
|
|
228
|
+
|
|
229
|
+
return await this.verifyFinalStatusWithRetry(request, sessionId);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
private async cancelSessionWithTimeout(sessionId: string): Promise<void> {
|
|
233
|
+
let cancelTimeout: ReturnType<typeof setTimeout> | undefined;
|
|
234
|
+
|
|
235
|
+
try {
|
|
236
|
+
await Promise.race([
|
|
237
|
+
this.cancelSession(sessionId),
|
|
238
|
+
new Promise<never>((_, reject) => {
|
|
239
|
+
cancelTimeout = setTimeout(() => {
|
|
240
|
+
reject(new Error("Viva timeout cancellation timed out"));
|
|
241
|
+
}, VivaStrategy.TIMEOUT_CANCEL_TIMEOUT_MS);
|
|
242
|
+
}),
|
|
243
|
+
]);
|
|
244
|
+
} catch (_error) {
|
|
245
|
+
// Verification below is the source of truth if cancellation fails or times out.
|
|
246
|
+
} finally {
|
|
247
|
+
if (cancelTimeout) {
|
|
248
|
+
clearTimeout(cancelTimeout);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
private async cancelSession(sessionId: string): Promise<boolean> {
|
|
254
|
+
await this.api.cancelVivaTransactionV2({
|
|
255
|
+
cashRegisterId: this.config.storeId,
|
|
256
|
+
sessionId,
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
if (this.currentSessionId === sessionId) {
|
|
260
|
+
this.currentSessionId = null;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
return true;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
private async verifyFinalStatusWithRetry(
|
|
267
|
+
request: PaymentRequest,
|
|
268
|
+
sessionId: string,
|
|
269
|
+
): Promise<PaymentResult> {
|
|
270
|
+
let lastError: unknown;
|
|
271
|
+
|
|
272
|
+
for (let attempt = 1; attempt <= VivaStrategy.VERIFY_MAX_RETRIES; attempt++) {
|
|
273
|
+
try {
|
|
274
|
+
const result = await Promise.race([
|
|
275
|
+
this.verifyFinalStatus(request, sessionId),
|
|
276
|
+
new Promise<never>((_, reject) =>
|
|
277
|
+
setTimeout(
|
|
278
|
+
() => reject(new Error("Verify timed out")),
|
|
279
|
+
VivaStrategy.VERIFY_TIMEOUT_MS,
|
|
280
|
+
),
|
|
281
|
+
),
|
|
282
|
+
]);
|
|
283
|
+
|
|
284
|
+
if (result.status === SdkPaymentStatus.PENDING) {
|
|
285
|
+
lastError = new Error("Verify returned pending status");
|
|
286
|
+
if (attempt < VivaStrategy.VERIFY_MAX_RETRIES) {
|
|
287
|
+
await new Promise((resolve) =>
|
|
288
|
+
setTimeout(resolve, VivaStrategy.VERIFY_RETRY_DELAY_MS),
|
|
289
|
+
);
|
|
290
|
+
}
|
|
291
|
+
continue;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
return result;
|
|
295
|
+
} catch (err) {
|
|
296
|
+
lastError = err;
|
|
297
|
+
|
|
298
|
+
if (attempt < VivaStrategy.VERIFY_MAX_RETRIES) {
|
|
299
|
+
await new Promise((resolve) =>
|
|
300
|
+
setTimeout(resolve, VivaStrategy.VERIFY_RETRY_DELAY_MS),
|
|
301
|
+
);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
const message = lastError instanceof Error ? lastError.message : "Verify retries exhausted";
|
|
307
|
+
throw new PaymentSDKError(
|
|
308
|
+
PaymentFailureCode.PaymentTimeout as unknown as PaymentErrorCode,
|
|
309
|
+
message,
|
|
310
|
+
);
|
|
311
|
+
}
|
|
312
|
+
|
|
202
313
|
private handleSuccess(
|
|
203
314
|
data: PaymentStatusDto,
|
|
204
315
|
): PaymentResult {
|
package/src/types/payment.ts
CHANGED
package/src/version.ts
CHANGED