@munchi_oy/payments 1.6.3 → 1.6.9
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/README.md +194 -13
- package/dist/index.js +1 -1
- package/dist/index.mjs +1 -1
- package/dist/src/strategies/VivaAppToAppStrategy.d.ts +2 -1
- package/dist/src/strategies/VivaAppToAppStrategy.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/types/sdk.d.ts +2 -0
- package/dist/src/types/sdk.d.ts.map +1 -1
- package/dist/src/version.d.ts +1 -1
- package/package.json +2 -2
- package/src/strategies/VivaAppToAppStrategy.ts +77 -20
- package/src/types/payment.ts +1 -0
- package/src/types/sdk.ts +2 -0
- package/src/version.ts +1 -1
package/README.md
CHANGED
|
@@ -1,28 +1,209 @@
|
|
|
1
1
|
# @munchi_oy/payments
|
|
2
2
|
|
|
3
|
-
Payment
|
|
3
|
+
Payment SDK for Munchi POS clients, including Viva app-to-app support.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
-
|
|
9
|
-
# or
|
|
10
|
-
pnpm add @munchi_oy/payments
|
|
11
|
-
# or
|
|
12
|
-
yarn add @munchi_oy/payments
|
|
8
|
+
pnpm add @munchi_oy/payments @munchi_oy/core axios
|
|
13
9
|
```
|
|
14
10
|
|
|
15
|
-
##
|
|
11
|
+
## Core Exports
|
|
16
12
|
|
|
17
|
-
|
|
18
|
-
|
|
13
|
+
```ts
|
|
14
|
+
import {
|
|
15
|
+
MunchiPaymentSDK,
|
|
16
|
+
PaymentInteractionState,
|
|
17
|
+
SdkPaymentStatus,
|
|
18
|
+
type AppToAppAdapter,
|
|
19
|
+
type AppToAppConfig,
|
|
20
|
+
type PaymentResult,
|
|
21
|
+
} from "@munchi_oy/payments";
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## App-to-App Integration (Viva)
|
|
25
|
+
|
|
26
|
+
### 1) Provide a messaging adapter
|
|
27
|
+
|
|
28
|
+
The SDK needs a messaging adapter for non-app-to-app providers and fallback flows.
|
|
29
|
+
|
|
30
|
+
```ts
|
|
31
|
+
import type { IMessagingAdapter } from "@munchi_oy/payments";
|
|
32
|
+
|
|
33
|
+
export class MessagingAdapter implements IMessagingAdapter {
|
|
34
|
+
subscribe<T>(_channel: string, _event: string, _onMessage: (data: T) => void) {
|
|
35
|
+
return () => {};
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### 2) Provide an app-to-app adapter
|
|
41
|
+
|
|
42
|
+
Implement `openUrl` + `subscribe` using your platform deep-link APIs.
|
|
43
|
+
|
|
44
|
+
```ts
|
|
45
|
+
import type { AppToAppAdapter } from "@munchi_oy/payments";
|
|
46
|
+
import { Linking } from "react-native";
|
|
47
|
+
|
|
48
|
+
export class VivaAppToAppAdapter implements AppToAppAdapter {
|
|
49
|
+
async openUrl(url: string) {
|
|
50
|
+
await Linking.openURL(url);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
subscribe(listener: (url: string) => void) {
|
|
54
|
+
const subscription = Linking.addEventListener("url", ({ url }) => {
|
|
55
|
+
listener(url);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
return () => subscription.remove();
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### 3) Configure SDK
|
|
64
|
+
|
|
65
|
+
Use full callback URL in `callbackUrl` and control Viva callback query format using `callbackParamFormat`.
|
|
66
|
+
|
|
67
|
+
```ts
|
|
68
|
+
import { MunchiPaymentSDK, type AppToAppConfig } from "@munchi_oy/payments";
|
|
69
|
+
import { PaymentProvider, ProviderEnum } from "@munchi_oy/core";
|
|
70
|
+
import axios from "axios";
|
|
71
|
+
import { Platform } from "react-native";
|
|
72
|
+
|
|
73
|
+
const appToApp: AppToAppConfig = {
|
|
74
|
+
enabled: true,
|
|
75
|
+
appId: "com.example.pos",
|
|
76
|
+
callbackUrl: "myapp://result",
|
|
77
|
+
callbackParamFormat: Platform.OS === "ios" ? "scheme-only" : "full-url",
|
|
78
|
+
adapter: new VivaAppToAppAdapter(),
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
const sdk = new MunchiPaymentSDK(
|
|
82
|
+
axios,
|
|
83
|
+
new MessagingAdapter(),
|
|
84
|
+
{
|
|
85
|
+
channel: ProviderEnum.MunchiPos,
|
|
86
|
+
provider: PaymentProvider.Viva,
|
|
87
|
+
kioskId: "kiosk-123",
|
|
88
|
+
storeId: "351",
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
timeoutMs: 120000,
|
|
92
|
+
appToApp,
|
|
93
|
+
},
|
|
94
|
+
);
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### 4) Initiate payment
|
|
98
|
+
|
|
99
|
+
```ts
|
|
100
|
+
const result = await sdk.initiateTransaction(
|
|
101
|
+
{
|
|
102
|
+
orderRef: "order-123",
|
|
103
|
+
amountCents: 8000,
|
|
104
|
+
currency: "EUR",
|
|
105
|
+
displayId: "display-1",
|
|
106
|
+
options: {
|
|
107
|
+
tipAmount: 0,
|
|
108
|
+
sourceCode: "6532",
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
onConnecting: () => {},
|
|
113
|
+
onRequiresInput: () => {},
|
|
114
|
+
onSuccess: (successResult) => console.log(successResult),
|
|
115
|
+
onError: (errorResult) => console.log(errorResult),
|
|
116
|
+
},
|
|
117
|
+
);
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## Platform Setup
|
|
121
|
+
|
|
122
|
+
### iOS
|
|
123
|
+
|
|
124
|
+
1. Register your app URL scheme.
|
|
125
|
+
2. Add Viva scheme to query list:
|
|
126
|
+
3. Use `callbackParamFormat: "scheme-only"` in app-to-app config.
|
|
19
127
|
|
|
20
|
-
|
|
128
|
+
Example (`app.config.ts`):
|
|
21
129
|
|
|
22
|
-
```
|
|
23
|
-
|
|
130
|
+
```ts
|
|
131
|
+
export default {
|
|
132
|
+
ios: {
|
|
133
|
+
bundleIdentifier: "com.example.pos",
|
|
134
|
+
infoPlist: {
|
|
135
|
+
LSApplicationQueriesSchemes: ["vivapayclient"],
|
|
136
|
+
},
|
|
137
|
+
},
|
|
138
|
+
scheme: "myapp",
|
|
139
|
+
};
|
|
24
140
|
```
|
|
25
141
|
|
|
142
|
+
### Android
|
|
143
|
+
|
|
144
|
+
1. Register intent filter for callback scheme.
|
|
145
|
+
2. Ensure `vivapayclient` can be resolved on device.
|
|
146
|
+
3. Use `callbackParamFormat: "full-url"` for standard Android app-to-app query behavior.
|
|
147
|
+
|
|
148
|
+
## Callback Semantics
|
|
149
|
+
|
|
150
|
+
### Cancellation vs decline
|
|
151
|
+
|
|
152
|
+
The SDK maps Viva callback payloads to Munchi failure codes:
|
|
153
|
+
|
|
154
|
+
- User-cancel patterns map to `payment.cancelled_by_user`.
|
|
155
|
+
- Decline patterns (`declined`, failed status variants) map to `payment.declined`.
|
|
156
|
+
|
|
157
|
+
Example iOS cancel callback:
|
|
158
|
+
|
|
159
|
+
```text
|
|
160
|
+
myapp://result?action=sale&status=failure&errorCode=1000&message=Transaction%20canceled%20by%20user
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### Amount units
|
|
164
|
+
|
|
165
|
+
All callback monetary fields are treated as minor units (cents):
|
|
166
|
+
|
|
167
|
+
- `amount`
|
|
168
|
+
- `tipAmount`
|
|
169
|
+
- `surchargeAmount`
|
|
170
|
+
|
|
171
|
+
No conversion to major units is done by the SDK.
|
|
172
|
+
|
|
173
|
+
### Fees mapping
|
|
174
|
+
|
|
175
|
+
For successful Viva callbacks, transaction fees are derived as:
|
|
176
|
+
|
|
177
|
+
- `tipAmount > 0` -> `TIP_AMOUNT`
|
|
178
|
+
- `surchargeAmount > 0` -> `SURCHARGE`
|
|
179
|
+
|
|
180
|
+
## Recommended Integration Pattern
|
|
181
|
+
|
|
182
|
+
1. Keep `appId` equal to the native bundle/package id of the installed app.
|
|
183
|
+
2. Keep `callbackUrl` as full callback URL (`<scheme>://result`).
|
|
184
|
+
3. Set `callbackParamFormat` per platform.
|
|
185
|
+
4. Subscribe to SDK state and always handle `FAILED`, `CANCELLED`, and `SUCCESS`.
|
|
186
|
+
5. Persist deep-link debug logs while troubleshooting app-switch flows.
|
|
187
|
+
|
|
188
|
+
## Troubleshooting
|
|
189
|
+
|
|
190
|
+
### `canOpenUrl` true but no callback arrives
|
|
191
|
+
|
|
192
|
+
- Validate `appId` matches the installed app variant.
|
|
193
|
+
- Validate callback scheme registration.
|
|
194
|
+
- Validate iOS `LSApplicationQueriesSchemes` includes `vivapayclient`.
|
|
195
|
+
- Verify the callback URL in logs exactly matches the app scheme.
|
|
196
|
+
|
|
197
|
+
### Callback arrives but result mapping looks wrong
|
|
198
|
+
|
|
199
|
+
- Inspect callback `status`, `message`, `errorCode`.
|
|
200
|
+
- Ensure callback is forwarded to SDK listener.
|
|
201
|
+
- Verify callback belongs to current `clientTransactionId` or `ISV_clientTransactionId`.
|
|
202
|
+
|
|
203
|
+
### Security note
|
|
204
|
+
|
|
205
|
+
Do not print `ISV_clientSecret` or other credentials in production logs.
|
|
206
|
+
|
|
26
207
|
## License
|
|
27
208
|
|
|
28
|
-
UNLICENSED - Private package
|
|
209
|
+
UNLICENSED - Private package.
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
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 r in e)_(l,r,{get:e[r],enumerable:!0})},G=(l,e,r,t)=>{if(e&&typeof e=="object"||typeof e=="function")for(let n of k(e))!V.call(l,n)&&n!==r&&_(l,n,{get:()=>e[n],enumerable:!(t=U(e,n))||t.enumerable});return l};var q=l=>G(_({},"__esModule",{value:!0}),l);var re={};W(re,{AppReaderStatus:()=>x,MunchiPaymentSDK:()=>w,PaymentInteractionState:()=>C,SdkPaymentStatus:()=>E});module.exports=q(re);var y=require("@munchi_oy/core");var D="1.6.3";var c=class l extends Error{code;rawError;constructor(e,r,t){super(r),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||{}),C=(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))(C||{});var b=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 s=await this.waitForPaymentCompletion(i,e.orderRef,this.abortController.signal);return this.currentRequestId=null,s}catch(n){throw this.currentRequestId=null,n instanceof c?n:new c("TERMINAL_BUSY","Failed to create Nets Intent",n)}}async waitForPaymentCompletion(e,r,t){let n=`nets.requests.${e}`,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(n,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,r,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,r,t,n){let a=Date.now()+12e4;for(;Date.now()<a;){if(n.aborted)throw new Error("Aborted");try{let{data:u}=await this.api.getPaymentStatus({businessId:Number(t),orderId:r,provider:this.paymentProvider,referenceId:e});if(n.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)};n.addEventListener("abort",d,{once:!0});let m=setTimeout(()=>{n.removeEventListener("abort",d),u(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 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,r){try{let{data:t}=await this.api.getPaymentStatus({businessId:Number(this.config.storeId),orderId:e.orderRef,provider:this.paymentProvider,referenceId:r});return this.handleSuccess(t)}catch(t){throw new c("NETWORK_ERROR","Failed to verify final Nets status",t)}}handleSuccess(e){let r=e.status===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 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,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 c("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($,this.buildLaunchParams(t),e,r)}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 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(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,r){r("CONNECTING");let t=await this.fetchPaymentReference(e),n=this.buildRefundLaunchParams(e,t);return this.runAction(Q,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 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=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((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),n("REQUIRES_INPUT",i),this.appToAppConfig.adapter.openUrl(this.buildActionUrl(e,r)).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 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 s=n;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{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),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&&(n.receiptNumber=i),s&&(n.referenceCode=s),o&&(n.shortReferenceCode=o),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)}),`${B}?${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),s=this.resolveFailureCode(t,i),o={success:n,status:n?"SUCCESS":i?"CANCELLED":"FAILED",orderId:this.currentOrderRef??t.clientTransactionId??"unknown",errorCode:n?"":s,errorMessage:n?"":t.message??"Transaction failed without error details"};n&&(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 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()===F.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=L(e.transactionDate),n=O(e.amount),i=O(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===z||i===H?p.PaymentMethod.Edenred:ee.includes(n)?p.PaymentMethod.WoltBenefit:te.some(s=>r.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(r=>{let t=!1,n=setTimeout(()=>{t||(t=!0,i(),r(null))},X),i=this.appToAppConfig.adapter.subscribe(s=>{if(t)return;let o=this.parseCallbackUrl(s);o&&this.matchesKnownTransactionId(o.rawParams)&&(t=!0,clearTimeout(n),i(),r(o.result))});this.appToAppConfig.adapter.openUrl(this.buildActionUrl(F,e)).catch(()=>{t||(t=!0,clearTimeout(n),i(),r(null))})})}};var g=require("@munchi_oy/core");var S=class l{constructor(e,r,t){this.messaging=r;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,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,this.abortController.signal);return this.currentSessionId=null,i}catch(n){throw this.currentSessionId=null,n instanceof c?n:new c("NETWORK_ERROR","Failed to create Viva Intent",n)}}async waitForPaymentCompletion(e,r,t){let 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,r.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 P=await this.recoverAfterPollingTimeout(r,e);o(P)}catch(P){a(P instanceof c?P:new c("TIMEOUT","Payment timed out and polling failed",P))}}finally{t.removeEventListener("abort",m),d()}},l.INITIAL_POLL_DELAY_MS)})}async pollOrderStatus(e,r,t,n){let s=Date.now()+l.POLLING_DURATION_MS;for(;Date.now()<s;){if(n.aborted)throw new Error("Aborted");try{let{data:o}=await this.api.getPaymentStatus({businessId:Number(t),orderId:r,provider:this.paymentProvider,referenceId:e});if(n.aborted)throw new Error("Aborted");if(o.status!==g.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)};n.addEventListener("abort",a,{once:!0});let u=setTimeout(()=>{n.removeEventListener("abort",a),o(void 0)},l.POLLING_INTERVAL_MS)})}throw new Error("Payment verification timed out.")}async recoverAfterPollingTimeout(e,r){return await this.cancelSessionWithTimeout(r),await this.verifyFinalStatusWithRetry(e,r)}async cancelSessionWithTimeout(e){let r;try{await Promise.race([this.cancelSession(e),new Promise((t,n)=>{r=setTimeout(()=>{n(new Error("Viva timeout cancellation timed out"))},l.TIMEOUT_CANCEL_TIMEOUT_MS)})])}catch{}finally{r&&clearTimeout(r)}}async cancelSession(e){return await this.api.cancelVivaTransactionV2({cashRegisterId:this.config.storeId,sessionId:e}),this.currentSessionId===e&&(this.currentSessionId=null),!0}async verifyFinalStatusWithRetry(e,r){let t;for(let i=1;i<=l.VERIFY_MAX_RETRIES;i++)try{let s=await Promise.race([this.verifyFinalStatus(e,r),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 n=t instanceof Error?t.message:"Verify retries exhausted";throw new c(g.PaymentFailureCode.PaymentTimeout,n)}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 c("NETWORK_ERROR","Failed to cancel Viva transaction",r)}}async verifyFinalStatus(e,r){try{let{data:t}=await this.api.getPaymentStatus({businessId:Number(this.config.storeId),orderId:e.orderRef,provider:this.paymentProvider,referenceId:r}),n=t.status===g.SimplePaymentStatus.Success,i=t.status===g.SimplePaymentStatus.Pending,s={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&&(s.transactionId=t.transactionId),t.error?.referenceError&&(s.errorReference=t.error.referenceError),t.transaction&&(s.transaction=t.transaction),s}catch(t){throw new c("NETWORK_ERROR","Failed to verify final transaction status",t)}}async refundTransaction(e,r){try{let t={amount:e.amountCents,businessId:Number(this.config.storeId),displayId:this.config.kioskId,currency:e.currency,orderReferenceId:e.orderRef,referenceId:e.originalTransactionId},{data:n}=await this.api.refundSingleVivaTransaction(t),i=n.success;return{success:i,status:i?"SUCCESS":"FAILED",orderId:e.orderRef,transactionId:n.sessionId}}catch(t){throw new c("NETWORK_ERROR","Failed to refund Viva transaction",t)}}abort(){this.abortController?.abort()}};var I=require("@munchi_oy/core"),M=require("axios");var N=class{constructor(e,r,t){this.messaging=r;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,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 s=await this.waitForPaymentCompletion(i,e.orderRef,I.PaymentEventType.StatusChanged,this.abortController.signal);return this.currentOperationId=null,s}catch(n){throw this.currentOperationId=null,n instanceof c?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 s=await this.waitForPaymentCompletion(i,e.orderRef,I.PaymentEventType.RefundStatusChanged,this.abortController.signal);return this.currentOperationId=null,s}catch(n){throw this.currentOperationId=null,n instanceof c?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((s,o)=>{let a=!1,u=()=>{a=!0,typeof m=="function"&&m(),clearTimeout(f)},d=()=>{u(),o(new c("CANCELLED","Transaction cancelled"))};n.addEventListener("abort",d);let m=this.messaging.subscribe(i,t,h=>{a||h.status===I.SimplePaymentStatus.Pending||(u(),n.removeEventListener("abort",d),s(this.handlePaymentStatus(h)))}),f=setTimeout(async()=>{if(!(a||n.aborted))try{let h=await this.pollOrderStatus(e,r,this.config.storeId,n);s(this.handlePaymentStatus(h))}catch(h){o(new c("TIMEOUT","Payment timed out and polling failed",h))}finally{n.removeEventListener("abort",d),u()}},1e4)})}async pollOrderStatus(e,r,t,n){let o=Date.now()+12e4;for(;Date.now()<o;){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!==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)};n.addEventListener("abort",u,{once:!0});let d=setTimeout(()=>{n.removeEventListener("abort",u),a(void 0)},2e3)})}throw new Error("Payment verification timed out.")}handlePaymentStatus(e){let r=e.status===I.SimplePaymentStatus.Success,t=e.status===I.SimplePaymentStatus.Pending,n={success:r,status:r?"SUCCESS":t?"PENDING":"FAILED",orderId:e.orderId,errorCode:e.error?.code||(r||t?"":I.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,M.isAxiosError)(e)){if(e.response?.status===400||e.response?.status===409)return new c("TERMINAL_BUSY",r,e);if(e.response?.status===404)return new c("TERMINAL_OFFLINE",r,e)}return new c("NETWORK_ERROR",r,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,r,t,n={},i){this.axios=e,this.messaging=r,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.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(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(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(n=>n(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,r)=>{let t=r??{},n=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,n))},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,r,t={}){let n=r instanceof c&&(r.code==="TIMEOUT"||r.code===y.PaymentFailureCode.PaymentTimeout);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.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=n&&s.status==="PENDING"?this.buildErrorResultFromException(e.orderRef,r):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,r);return this.transitionTo("FAILED"),this.safeFireCallback(()=>t.onError?.(i)),i}buildErrorResultFromException(e,r){return r instanceof c?this.generateErrorResult(e,r.code,r.message):this.generateErrorResult(e,"UNKNOWN",r instanceof Error?r.message:"Unknown fatal error")}fireStateCallback(e,r,t){let n={orderRef:t,refPaymentId:this._currentSessionId};switch(e){case"CONNECTING":this.safeFireCallback(()=>r.onConnecting?.(n));break;case"REQUIRES_INPUT":this.safeFireCallback(()=>r.onRequiresInput?.(n));break;case"PROCESSING":this.safeFireCallback(()=>r.onProcessing?.(n));break}}safeFireCallback(e){try{e()}catch(r){this.logger?.warn("Callback execution failed",{error:r})}}cancel=async()=>{if(this.logger?.info("Attempting cancellation"),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(r=>this.transitionTo(r))}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,r)=>{let t=r??{};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=(n=>(n.CONNECTING="CONNECTING",n.CONNECTED="CONNECTED",n.OFFLINE="OFFLINE",n.DISCONNECTED="DISCONNECTED",n))(x||{});0&&(module.exports={AppReaderStatus,MunchiPaymentSDK,PaymentInteractionState,SdkPaymentStatus});
|
|
1
|
+
"use strict";var D=Object.defineProperty;var U=Object.getOwnPropertyDescriptor;var k=Object.getOwnPropertyNames;var V=Object.prototype.hasOwnProperty;var W=(l,e)=>{for(var r in e)D(l,r,{get:e[r],enumerable:!0})},G=(l,e,r,t)=>{if(e&&typeof e=="object"||typeof e=="function")for(let n of k(e))!V.call(l,n)&&n!==r&&D(l,n,{get:()=>e[n],enumerable:!(t=U(e,n))||t.enumerable});return l};var q=l=>G(D({},"__esModule",{value:!0}),l);var re={};W(re,{AppReaderStatus:()=>M,MunchiPaymentSDK:()=>_,PaymentInteractionState:()=>C,SdkPaymentStatus:()=>E});module.exports=q(re);var y=require("@munchi_oy/core");var O="1.6.9";var c=class l extends Error{code;rawError;constructor(e,r,t){super(r),this.name="PaymentSDKError",this.code=e,this.rawError=t,Object.setPrototypeOf(this,l.prototype)}};var R=require("@munchi_oy/core");var E=(i=>(i.PENDING="PENDING",i.SUCCESS="SUCCESS",i.APPROVED="APPROVED",i.FAILED="FAILED",i.CANCELLED="CANCELLED",i.ERROR="ERROR",i))(E||{}),C=(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))(C||{});var b=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),s=n.connectCloudRequestId;if(!s)throw new Error("connectCloudRequestId is missing from response.");if(this.currentRequestId=s,this.abortController.signal.aborted)throw new Error("Aborted");r("REQUIRES_INPUT",{sessionId:s});let i=await this.waitForPaymentCompletion(s,e.orderRef,this.abortController.signal);return this.currentRequestId=null,i}catch(n){throw this.currentRequestId=null,n instanceof c?n:new c("TERMINAL_BUSY","Failed to create Nets Intent",n)}}async waitForPaymentCompletion(e,r,t){let n=`nets.requests.${e}`,s=R.PaymentEventType.StatusChanged;return new Promise((i,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(n,s,I=>{a||I.status===R.SimplePaymentStatus.Pending||(u(),t.removeEventListener("abort",d),i(this.handleSuccess(I)))}),f=setTimeout(async()=>{if(!(a||t.aborted))try{let I=await this.pollOrderStatus(e,r,this.config.storeId,t);i(this.handleSuccess(I))}catch(I){o(new c("TIMEOUT","Payment timed out and polling failed",I))}finally{t.removeEventListener("abort",d),u()}},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:u}=await this.api.getPaymentStatus({businessId:Number(t),orderId:r,provider:this.paymentProvider,referenceId:e});if(n.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)};n.addEventListener("abort",d,{once:!0});let m=setTimeout(()=>{n.removeEventListener("abort",d),u(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),s=n.connectCloudRequestId;if(!s)throw new Error("connectCloudRequestId is missing from response.");if(this.currentRequestId=s,this.abortController.signal.aborted)throw new Error("Aborted");r("REQUIRES_INPUT",{sessionId:s});let i=await this.waitForPaymentCompletion(s,e.orderRef,this.abortController.signal);return this.currentRequestId=null,i}catch(t){throw this.currentRequestId=null,t instanceof c||t instanceof c?t:new c("NETWORK_ERROR","Failed to refund Nets transaction",t)}}async verifyFinalStatus(e,r){try{let{data:t}=await this.api.getPaymentStatus({businessId:Number(this.config.storeId),orderId:e.orderRef,provider:this.paymentProvider,referenceId:r});return this.handleSuccess(t)}catch(t){throw new c("NETWORK_ERROR","Failed to verify final Nets status",t)}}handleSuccess(e){let r=e.status===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 A=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",Q="abort",$="cancel",j="sale",F="transactionDetails",X=4e3,z="534287",H="38",J="493591",Z="497439",ee=[J,Z],te=["CREDIT","PREPAID","CORPORATE"],N=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 c("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 c("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 c("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 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=r.sourceTerminalId??r.tid??null,this.currentClientTransactionId=r.clientTransactionId??null,this.currentIsvClientTransactionId=r.ISV_clientTransactionId??null;let s=this.currentClientTransactionId?{sessionId:this.currentClientTransactionId}:void 0;return new Promise((i,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),i(f.result))});this.abortController?.signal.addEventListener("abort",u),n("REQUIRES_INPUT",s),this.appToAppConfig.adapter.openUrl(this.buildActionUrl(e,r)).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 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(),s={amount:String(e.amountCents),clientTransactionId:n,show_receipt:"false",show_transaction_result:"false"};if(t.sourceCode&&(s.sourceCode=t.sourceCode),r?.referenceCode&&(s.orderCode=r.referenceCode),r?.shortReferenceCode&&(s.shortOrderCode=r.shortReferenceCode),r?.receiptNumber&&(s.referenceNumber=r.receiptNumber),r?.terminalId&&(s.tid=r.terminalId),r?.referenceCode||r?.shortReferenceCode||r?.receiptNumber)return s;let i=n;return/^\d+$/.test(i)&&(i.length===16?s.orderCode=i:i.length===10?s.shortOrderCode=i:s.referenceNumber=i),s}async fetchPaymentReference(e){try{let r={orderId:e.orderRef,provider:p.PaymentProvider.Viva,transactionId:e.originalTransactionId,businessId:e.businessId},{data:t}=await this.paymentApiWithReference.getPaymentReference(r);return this.extractPaymentReferenceContext(t)}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},s=this.readReferenceValue(t.receiptNumber)??this.readReferenceValue(t.referenceNumber),i=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 s&&(n.receiptNumber=s),i&&(n.referenceCode=i),o&&(n.shortReferenceCode=o),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.resolveCallbackQueryParam()});return Object.entries(r).forEach(([n,s])=>{t.set(n,s)}),`${B}?${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),s=this.isCancelledCallback(t),i=this.resolveFailureCode(t,s),o={success:n,status:n?"SUCCESS":s?"CANCELLED":"FAILED",orderId:this.currentOrderRef??t.clientTransactionId??"unknown",errorCode:n?"":i,errorMessage:n?"":t.message??"Transaction failed without error details"};n&&(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 r=new URL(e),t=this.appToAppConfig.callbackUrl;if(!t.includes("://")){let s=t.replace(/:$/,"");return r.protocol===`${s}:`}let n=new URL(t);return r.protocol===n.protocol&&r.hostname===n.hostname&&r.pathname===n.pathname}resolveCallbackQueryParam(){if(this.appToAppConfig.callbackParamFormat!=="scheme-only")return this.appToAppConfig.callbackUrl;let e=this.appToAppConfig.callbackUrl;return e.includes("://")?new URL(e).protocol.replace(/:$/,""):e.replace(/:$/,"")}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(),s=(e.status??"").toLowerCase();return n.includes("declined")||s==="declined"||s==="fail"||s==="failed"||s==="failure"?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()===F.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")||t.includes("cancelled by user")||t.includes("canceled by user")||t.includes("transaction cancelled by user")||t.includes("transaction canceled by user")||n.includes("user_cancel")||n.includes("user cancel")||n==="1000"&&(t.includes("cancelled by user")||t.includes("canceled by user"))}extractErrorReferenceFromMessage(e){return e?e.match(/\((-?\d+)\)/)?.[1]:void 0}mapCallbackToTransaction(e){let r=e.transactionId??e.orderCode??e.clientTransactionId??"unknown",t=L(e.transactionDate),n=A(e.amount),s=A(e.tipAmount),i=A(e.surchargeAmount);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.buildFees(s,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),s=(e.bankId??"").trim();return n===z||s===H?p.PaymentMethod.Edenred:ee.includes(n)?p.PaymentMethod.WoltBenefit:te.some(i=>r.includes(i))?p.PaymentMethod.Credit:p.PaymentMethod.Debit}buildFees(e,r){if(!this.currentCurrency)return null;let t=[];return e>0&&t.push({taxAmount:0,total:{amount:e,currency:this.currentCurrency},type:p.FeeType.TipAmount,vatPercentage:0}),r>0&&t.push({taxAmount:0,total:{amount:r,currency:this.currentCurrency},type:p.FeeType.Surcharge,vatPercentage:0}),t.length===0?null:{parts:t}}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,s(),r(null))},X),s=this.appToAppConfig.adapter.subscribe(i=>{if(t)return;let o=this.parseCallbackUrl(i);o&&this.matchesKnownTransactionId(o.rawParams)&&(t=!0,clearTimeout(n),s(),r(o.result))});this.appToAppConfig.adapter.openUrl(this.buildActionUrl(F,e)).catch(()=>{t||(t=!0,clearTimeout(n),s(),r(null))})})}};var g=require("@munchi_oy/core");var S=class l{constructor(e,r,t){this.messaging=r;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,r){this.abortController=new AbortController,r("CONNECTING");let t={amount:e.amountCents,referenceId:e.orderRef,businessId:parseInt(this.config.storeId),currency:e.currency,displayId:e.displayId,showReceipt:!0,showTransactionResult:!0};try{let{data:n}=await this.api.initiateTerminalTransaction(t);if(this.currentSessionId=n.sessionId,this.abortController.signal.aborted)throw new Error("Aborted");r("REQUIRES_INPUT",{sessionId:n.sessionId});let s=await this.waitForPaymentCompletion(n.sessionId,e,this.abortController.signal);return this.currentSessionId=null,s}catch(n){throw this.currentSessionId=null,n instanceof c?n:new c("NETWORK_ERROR","Failed to create Viva Intent",n)}}async waitForPaymentCompletion(e,r,t){let s=`viva.${this.config.channel.toLowerCase()}.requests.${e}`,i="payment:status-changed";return new Promise((o,a)=>{let u=!1,d=()=>{u=!0,typeof f=="function"&&f(),clearTimeout(I)},m=()=>{d(),a(new Error("Aborted"))};t.addEventListener("abort",m);let f=this.messaging.subscribe(s,i,T=>{u||T.status!==g.SimplePaymentStatus.Pending&&(d(),t.removeEventListener("abort",m),o(this.handleSuccess(T)))}),I=setTimeout(async()=>{if(!(u||t.aborted))try{let T=await this.pollOrderStatus(e,r.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 P=await this.recoverAfterPollingTimeout(r,e);o(P)}catch(P){a(P instanceof c?P:new c("TIMEOUT","Payment timed out and polling failed",P))}}finally{t.removeEventListener("abort",m),d()}},l.INITIAL_POLL_DELAY_MS)})}async pollOrderStatus(e,r,t,n){let i=Date.now()+l.POLLING_DURATION_MS;for(;Date.now()<i;){if(n.aborted)throw new Error("Aborted");try{let{data:o}=await this.api.getPaymentStatus({businessId:Number(t),orderId:r,provider:this.paymentProvider,referenceId:e});if(n.aborted)throw new Error("Aborted");if(o.status!==g.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)};n.addEventListener("abort",a,{once:!0});let u=setTimeout(()=>{n.removeEventListener("abort",a),o(void 0)},l.POLLING_INTERVAL_MS)})}throw new Error("Payment verification timed out.")}async recoverAfterPollingTimeout(e,r){return await this.cancelSessionWithTimeout(r),await this.verifyFinalStatusWithRetry(e,r)}async cancelSessionWithTimeout(e){let r;try{await Promise.race([this.cancelSession(e),new Promise((t,n)=>{r=setTimeout(()=>{n(new Error("Viva timeout cancellation timed out"))},l.TIMEOUT_CANCEL_TIMEOUT_MS)})])}catch{}finally{r&&clearTimeout(r)}}async cancelSession(e){return await this.api.cancelVivaTransactionV2({cashRegisterId:this.config.storeId,sessionId:e}),this.currentSessionId===e&&(this.currentSessionId=null),!0}async verifyFinalStatusWithRetry(e,r){let t;for(let s=1;s<=l.VERIFY_MAX_RETRIES;s++)try{let i=await Promise.race([this.verifyFinalStatus(e,r),new Promise((o,a)=>setTimeout(()=>a(new Error("Verify timed out")),l.VERIFY_TIMEOUT_MS))]);if(i.status==="PENDING"){t=new Error("Verify returned pending status"),s<l.VERIFY_MAX_RETRIES&&await new Promise(o=>setTimeout(o,l.VERIFY_RETRY_DELAY_MS));continue}return i}catch(i){t=i,s<l.VERIFY_MAX_RETRIES&&await new Promise(o=>setTimeout(o,l.VERIFY_RETRY_DELAY_MS))}let n=t instanceof Error?t.message:"Verify retries exhausted";throw new c(g.PaymentFailureCode.PaymentTimeout,n)}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 c("NETWORK_ERROR","Failed to cancel Viva transaction",r)}}async verifyFinalStatus(e,r){try{let{data:t}=await this.api.getPaymentStatus({businessId:Number(this.config.storeId),orderId:e.orderRef,provider:this.paymentProvider,referenceId:r}),n=t.status===g.SimplePaymentStatus.Success,s=t.status===g.SimplePaymentStatus.Pending,i={success:n,status:n?"SUCCESS":s?"PENDING":"FAILED",orderId:t.orderId,errorCode:t.error?.code||(n||s?"":g.PaymentFailureCode.SystemUnknown),errorMessage:t.error?.message||(n||s?"":"Transaction failed without error details")};return t.transactionId&&(i.transactionId=t.transactionId),t.error?.referenceError&&(i.errorReference=t.error.referenceError),t.transaction&&(i.transaction=t.transaction),i}catch(t){throw new c("NETWORK_ERROR","Failed to verify final transaction status",t)}}async refundTransaction(e,r){try{let t={amount:e.amountCents,businessId:Number(this.config.storeId),displayId:this.config.kioskId,currency:e.currency,orderReferenceId:e.orderRef,referenceId:e.originalTransactionId},{data:n}=await this.api.refundSingleVivaTransaction(t),s=n.success;return{success:s,status:s?"SUCCESS":"FAILED",orderId:e.orderRef,transactionId:n.sessionId}}catch(t){throw new c("NETWORK_ERROR","Failed to refund Viva transaction",t)}}abort(){this.abortController?.abort()}};var h=require("@munchi_oy/core"),x=require("axios");var w=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),s=this.extractOperationId(n.data);if(this.currentOperationId=s,this.abortController.signal.aborted)throw new Error("Aborted");r("REQUIRES_INPUT",{sessionId:s});let i=await this.waitForPaymentCompletion(s,e.orderRef,h.PaymentEventType.StatusChanged,this.abortController.signal);return this.currentOperationId=null,i}catch(n){throw this.currentOperationId=null,n instanceof c?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),s=this.extractOperationId(n.data);if(this.currentOperationId=s,this.abortController.signal.aborted)throw new Error("Aborted");r("REQUIRES_INPUT",{sessionId:s});let i=await this.waitForPaymentCompletion(s,e.orderRef,h.PaymentEventType.RefundStatusChanged,this.abortController.signal);return this.currentOperationId=null,i}catch(n){throw this.currentOperationId=null,n instanceof c?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 s=`worldline.requests.${e}`;return new Promise((i,o)=>{let a=!1,u=()=>{a=!0,typeof m=="function"&&m(),clearTimeout(f)},d=()=>{u(),o(new c("CANCELLED","Transaction cancelled"))};n.addEventListener("abort",d);let m=this.messaging.subscribe(s,t,I=>{a||I.status===h.SimplePaymentStatus.Pending||(u(),n.removeEventListener("abort",d),i(this.handlePaymentStatus(I)))}),f=setTimeout(async()=>{if(!(a||n.aborted))try{let I=await this.pollOrderStatus(e,r,this.config.storeId,n);i(this.handlePaymentStatus(I))}catch(I){o(new c("TIMEOUT","Payment timed out and polling failed",I))}finally{n.removeEventListener("abort",d),u()}},1e4)})}async pollOrderStatus(e,r,t,n){let o=Date.now()+12e4;for(;Date.now()<o;){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 u=()=>{clearTimeout(d),a(void 0)};n.addEventListener("abort",u,{once:!0});let d=setTimeout(()=>{n.removeEventListener("abort",u),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,x.isAxiosError)(e)){if(e.response?.status===400||e.response?.status===409)return new c("TERMINAL_BUSY",r,e);if(e.response?.status===404)return new c("TERMINAL_OFFLINE",r,e)}return new c("NETWORK_ERROR",r,e)}};var _=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,r,t,n={},s){this.axios=e,this.messaging=r,this.logger=n.logger,this.timeoutMs=n.timeoutMs||3e4,this.autoResetOptions=n.autoResetOnPaymentComplete,this.appToAppConfig=n.appToApp,this.strategy=s??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: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(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(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(n=>n(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 w(this.axios,this.messaging,e);case y.PaymentProvider.Viva:return this.appToAppConfig?.enabled?new N(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,r)=>{let t=r??{},n=e.orderRef;if(!l.RESTING_STATES.includes(this._currentState))return this.generateErrorResult(e.orderRef,"UNKNOWN","A transaction is already in progress");let i=Date.now();if(this._cancellationIntent=!1,this._currentSessionId=void 0,this.transitionTo("IDLE"),e.amountCents<=0)return this.generateErrorResult(e.orderRef,"INVALID_AMOUNT","Amount must be greater than 0");try{let o=(m,f)=>{f?.sessionId&&(this._currentSessionId=f.sessionId),m!=="FAILED"&&(this.transitionTo(m),this.fireStateCallback(m,t,n))},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()-i}),d}catch(o){return this.logger?.warn("Transaction interrupted. Handling final status...",{error:o}),await this.handleTransactionError(e,o,t)}};async handleTransactionError(e,r,t={}){let n=r instanceof c&&(r.code==="TIMEOUT"||r.code===y.PaymentFailureCode.PaymentTimeout);if(!this._cancellationIntent&&!n&&(this.transitionTo("VERIFYING"),this.safeFireCallback(()=>t.onVerifying?.({orderRef:e.orderRef,refPaymentId:this._currentSessionId}))),this._cancellationIntent){try{if(this._currentSessionId){let i=await this.strategy.verifyFinalStatus(e,this._currentSessionId);if(i.success)return this.transitionTo("SUCCESS"),this.safeFireCallback(()=>t.onSuccess?.(i)),i}}catch(i){this.logger?.warn("Final status verification failed during cancellation",{err:i})}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 s;if(this.strategy.abort(),this._currentSessionId)try{let i=await this.strategy.verifyFinalStatus(e,this._currentSessionId);if(i.success)return this.transitionTo("SUCCESS"),this.safeFireCallback(()=>t.onSuccess?.(i)),i;s=n&&i.status==="PENDING"?this.buildErrorResultFromException(e.orderRef,r):i}catch(i){this.logger?.warn("Failed to get detailed error from verifyFinalStatus",{verifyErr:i}),s=this.buildErrorResultFromException(e.orderRef,i)}else s=this.buildErrorResultFromException(e.orderRef,r);return this.transitionTo("FAILED"),this.safeFireCallback(()=>t.onError?.(s)),s}buildErrorResultFromException(e,r){return r instanceof c?this.generateErrorResult(e,r.code,r.message):this.generateErrorResult(e,"UNKNOWN",r instanceof Error?r.message:"Unknown fatal error")}fireStateCallback(e,r,t){let n={orderRef:t,refPaymentId:this._currentSessionId};switch(e){case"CONNECTING":this.safeFireCallback(()=>r.onConnecting?.(n));break;case"REQUIRES_INPUT":this.safeFireCallback(()=>r.onRequiresInput?.(n));break;case"PROCESSING":this.safeFireCallback(()=>r.onProcessing?.(n));break}}safeFireCallback(e){try{e()}catch(r){this.logger?.warn("Callback execution failed",{error:r})}}cancel=async()=>{if(this.logger?.info("Attempting cancellation"),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(r=>this.transitionTo(r))}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,r)=>{let t=r??{};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 s=(o,a)=>{a?.sessionId&&(this._currentSessionId=a.sessionId),o!=="FAILED"&&(this.transitionTo(o),this.fireStateCallback(o,t,e.orderRef))},i=await this.strategy.refundTransaction(e,s);return i.success?(this.transitionTo("SUCCESS"),this.safeFireCallback(()=>t.onSuccess?.(i))):this._cancellationIntent||i.status==="CANCELLED"?(this.transitionTo("FAILED"),this.safeFireCallback(()=>t.onCancelled?.({orderRef:e.orderRef,refPaymentId:this._currentSessionId}))):(this.transitionTo("FAILED"),this.safeFireCallback(()=>t.onError?.(i))),this.logger?.info("Refund completed",{success:i.success,orderRef:e.orderRef}),i}catch(s){if(this.logger?.error("Refund failed",s),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 i=this.generateErrorResult(e.orderRef,"UNKNOWN",s instanceof Error?s.message:"Refund failed");return this.safeFireCallback(()=>t.onError?.(i)),i}}};var M=(n=>(n.CONNECTING="CONNECTING",n.CONNECTED="CONNECTED",n.OFFLINE="OFFLINE",n.DISCONNECTED="DISCONNECTED",n))(M||{});0&&(module.exports={AppReaderStatus,MunchiPaymentSDK,PaymentInteractionState,SdkPaymentStatus});
|
package/dist/index.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{PaymentFailureCode as y,PaymentProvider as D}from"@munchi_oy/core";var F="1.6.3";var c=class u extends Error{code;rawError;constructor(e,r,t){super(r),this.name="PaymentSDKError",this.code=e,this.rawError=t,Object.setPrototypeOf(this,u.prototype)}};import{PaymentApi as G,PaymentEventType as q,PaymentProviderEnum as K,SimplePaymentStatus as P,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,r,t){this.messaging=r;this.config=t;this.api=new G(void 0,"",e)}api;abortController=null;currentRequestId=null;paymentProvider=K.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:M.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 s=await this.waitForPaymentCompletion(i,e.orderRef,this.abortController.signal);return this.currentRequestId=null,s}catch(n){throw this.currentRequestId=null,n instanceof c?n:new c("TERMINAL_BUSY","Failed to create Nets Intent",n)}}async waitForPaymentCompletion(e,r,t){let n=`nets.requests.${e}`,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(n,i,f=>{a||f.status===P.Pending||(l(),t.removeEventListener("abort",d),s(this.handleSuccess(f)))}),p=setTimeout(async()=>{if(!(a||t.aborted))try{let f=await this.pollOrderStatus(e,r,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,r,t,n){let a=Date.now()+12e4;for(;Date.now()<a;){if(n.aborted)throw new Error("Aborted");try{let{data:l}=await this.api.getPaymentStatus({businessId:Number(t),orderId:r,provider:this.paymentProvider,referenceId:e});if(n.aborted)throw new Error("Aborted");if(l.status!==P.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)};n.addEventListener("abort",d,{once:!0});let m=setTimeout(()=>{n.removeEventListener("abort",d),l(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:M.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 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,r){try{let{data:t}=await this.api.getPaymentStatus({businessId:Number(this.config.storeId),orderId:e.orderRef,provider:this.paymentProvider,referenceId:r});return this.handleSuccess(t)}catch(t){throw new c("NETWORK_ERROR","Failed to verify final Nets status",t)}}handleSuccess(e){let r=e.status===P.Success,t=e.status===P.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 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",re="497439",ne=[te,re],ie=["CREDIT","PREPAID","CORPORATE"],A=class{constructor(e,r,t,n){this.config=t;this.appToAppConfig=n;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,r){if(!this.appToAppConfig.enabled)throw new c("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(H,this.buildLaunchParams(t),e,r)}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 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(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,r){r("CONNECTING");let t=await this.fetchPaymentReference(e),n=this.buildRefundLaunchParams(e,t);return this.runAction(z,n,e,r)}async verifyFinalStatus(e,r){return this.latestResult?this.latestResult:this.wasCancelled?{success:!1,status:"CANCELLED",orderId:this.currentOrderRef??r,errorCode:T.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 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=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((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),n("REQUIRES_INPUT",i),this.appToAppConfig.adapter.openUrl(this.buildActionUrl(e,r)).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 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 s=n;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{data:r}=await this.paymentApiWithReference.getPaymentReference({orderId:e.orderRef,provider:U.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),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&&(n.receiptNumber=i),s&&(n.referenceCode=s),o&&(n.shortReferenceCode=o),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)}),`${$}?${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),s=this.resolveFailureCode(t,i),o={success:n,status:n?"SUCCESS":i?"CANCELLED":"FAILED",orderId:this.currentOrderRef??t.clientTransactionId??"unknown",errorCode:n?"":s,errorMessage:n?"":t.message??"Transaction failed without error details"};n&&(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 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 T.PaymentCancelledByUser;let t=e.errorCode;if(t?.includes("."))return t;if(this.isTransactionDetailsAction(e)&&(e.transactionType??"").toLowerCase().includes("abort"))return T.SystemUnknown;let n=(e.message??"").toLowerCase(),i=(e.status??"").toLowerCase();return n.includes("declined")||i==="declined"||i==="fail"||i==="failed"?T.PaymentDeclined:T.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()===k.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=x(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:U.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===Z||i===ee?v.Edenred:ne.includes(n)?v.WoltBenefit:ie.some(s=>r.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(r=>{let t=!1,n=setTimeout(()=>{t||(t=!0,i(),r(null))},J),i=this.appToAppConfig.adapter.subscribe(s=>{if(t)return;let o=this.parseCallbackUrl(s);o&&this.matchesKnownTransactionId(o.rawParams)&&(t=!0,clearTimeout(n),i(),r(o.result))});this.appToAppConfig.adapter.openUrl(this.buildActionUrl(k,e)).catch(()=>{t||(t=!0,clearTimeout(n),i(),r(null))})})}};import{PaymentApi as se,PaymentFailureCode as O,PaymentProviderEnum as oe,SimplePaymentStatus as E}from"@munchi_oy/core";var C=class u{constructor(e,r,t){this.messaging=r;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,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,this.abortController.signal);return this.currentSessionId=null,i}catch(n){throw this.currentSessionId=null,n instanceof c?n:new c("NETWORK_ERROR","Failed to create Viva Intent",n)}}async waitForPaymentCompletion(e,r,t){let 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,r.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(r,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,r,t,n){let s=Date.now()+u.POLLING_DURATION_MS;for(;Date.now()<s;){if(n.aborted)throw new Error("Aborted");try{let{data:o}=await this.api.getPaymentStatus({businessId:Number(t),orderId:r,provider:this.paymentProvider,referenceId:e});if(n.aborted)throw new Error("Aborted");if(o.status!==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)};n.addEventListener("abort",a,{once:!0});let l=setTimeout(()=>{n.removeEventListener("abort",a),o(void 0)},u.POLLING_INTERVAL_MS)})}throw new Error("Payment verification timed out.")}async recoverAfterPollingTimeout(e,r){return await this.cancelSessionWithTimeout(r),await this.verifyFinalStatusWithRetry(e,r)}async cancelSessionWithTimeout(e){let r;try{await Promise.race([this.cancelSession(e),new Promise((t,n)=>{r=setTimeout(()=>{n(new Error("Viva timeout cancellation timed out"))},u.TIMEOUT_CANCEL_TIMEOUT_MS)})])}catch{}finally{r&&clearTimeout(r)}}async cancelSession(e){return await this.api.cancelVivaTransactionV2({cashRegisterId:this.config.storeId,sessionId:e}),this.currentSessionId===e&&(this.currentSessionId=null),!0}async verifyFinalStatusWithRetry(e,r){let t;for(let i=1;i<=u.VERIFY_MAX_RETRIES;i++)try{let s=await Promise.race([this.verifyFinalStatus(e,r),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 n=t instanceof Error?t.message:"Verify retries exhausted";throw new c(O.PaymentTimeout,n)}handleSuccess(e){let r=e.status===E.Success,t={success:r,status:r?"SUCCESS":"FAILED",orderId:e.orderId,errorCode:e.error?.code||(r?"":O.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 c("NETWORK_ERROR","Failed to cancel Viva transaction",r)}}async verifyFinalStatus(e,r){try{let{data:t}=await this.api.getPaymentStatus({businessId:Number(this.config.storeId),orderId:e.orderRef,provider:this.paymentProvider,referenceId:r}),n=t.status===E.Success,i=t.status===E.Pending,s={success:n,status:n?"SUCCESS":i?"PENDING":"FAILED",orderId:t.orderId,errorCode:t.error?.code||(n||i?"":O.SystemUnknown),errorMessage:t.error?.message||(n||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,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 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,r,t){this.messaging=r;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,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 s=await this.waitForPaymentCompletion(i,e.orderRef,V.StatusChanged,this.abortController.signal);return this.currentOperationId=null,s}catch(n){throw this.currentOperationId=null,n instanceof c?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 s=await this.waitForPaymentCompletion(i,e.orderRef,V.RefundStatusChanged,this.abortController.signal);return this.currentOperationId=null,s}catch(n){throw this.currentOperationId=null,n instanceof c?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((s,o)=>{let a=!1,l=()=>{a=!0,typeof m=="function"&&m(),clearTimeout(p)},d=()=>{l(),o(new c("CANCELLED","Transaction cancelled"))};n.addEventListener("abort",d);let m=this.messaging.subscribe(i,t,f=>{a||f.status===N.Pending||(l(),n.removeEventListener("abort",d),s(this.handlePaymentStatus(f)))}),p=setTimeout(async()=>{if(!(a||n.aborted))try{let f=await this.pollOrderStatus(e,r,this.config.storeId,n);s(this.handlePaymentStatus(f))}catch(f){o(new c("TIMEOUT","Payment timed out and polling failed",f))}finally{n.removeEventListener("abort",d),l()}},1e4)})}async pollOrderStatus(e,r,t,n){let o=Date.now()+12e4;for(;Date.now()<o;){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!==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)};n.addEventListener("abort",l,{once:!0});let d=setTimeout(()=>{n.removeEventListener("abort",l),a(void 0)},2e3)})}throw new Error("Payment verification timed out.")}handlePaymentStatus(e){let r=e.status===N.Success,t=e.status===N.Pending,n={success:r,status:r?"SUCCESS":t?"PENDING":"FAILED",orderId:e.orderId,errorCode:e.error?.code||(r||t?"":ce.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(de(e)){if(e.response?.status===400||e.response?.status===409)return new c("TERMINAL_BUSY",r,e);if(e.response?.status===404)return new c("TERMINAL_OFFLINE",r,e)}return new c("NETWORK_ERROR",r,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,r,t,n={},i){this.axios=e,this.messaging=r,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 F}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(u.TERMINAL_STATES.includes(this._currentState)){let t=`Invalid State Transition: Attempted to move from terminal state ${this._currentState} to ${e}`;throw this.logger?.error(t),this._currentState!=="INTERNAL_ERROR"&&(this._currentState="INTERNAL_ERROR",this._listeners.forEach(n=>n(this._currentState))),new c("UNKNOWN",t)}this._currentState=e,u.TERMINAL_STATES.includes(e)&&this.scheduleAutoReset(e),this._listeners.forEach(t=>t(e))}_resetScheduledAt;get nextAutoResetAt(){return this._resetScheduledAt}cancelAutoReset(){this._autoResetTimer&&(clearTimeout(this._autoResetTimer),this._autoResetTimer=void 0),this._resetScheduledAt=void 0}scheduleAutoReset(e){if(!this.autoResetOptions)return;let t=e==="SUCCESS"?this.autoResetOptions.successDelayMs??5e3:this.autoResetOptions.failureDelayMs??5e3;this.logger?.info(`Scheduling auto-reset to IDLE in ${t}ms`),this._resetScheduledAt=Date.now()+t,this._autoResetTimer=setTimeout(()=>{this.logger?.info("Auto-reset triggered"),this.reset()},t)}resolveStrategy(e){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 C(this.axios,this.messaging,e);default:return new C(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 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,n))},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,r,t={}){let n=r instanceof c&&(r.code==="TIMEOUT"||r.code===y.PaymentTimeout);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.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=n&&s.status==="PENDING"?this.buildErrorResultFromException(e.orderRef,r):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,r);return this.transitionTo("FAILED"),this.safeFireCallback(()=>t.onError?.(i)),i}buildErrorResultFromException(e,r){return r instanceof c?this.generateErrorResult(e,r.code,r.message):this.generateErrorResult(e,"UNKNOWN",r instanceof Error?r.message:"Unknown fatal error")}fireStateCallback(e,r,t){let n={orderRef:t,refPaymentId:this._currentSessionId};switch(e){case"CONNECTING":this.safeFireCallback(()=>r.onConnecting?.(n));break;case"REQUIRES_INPUT":this.safeFireCallback(()=>r.onRequiresInput?.(n));break;case"PROCESSING":this.safeFireCallback(()=>r.onProcessing?.(n));break}}safeFireCallback(e){try{e()}catch(r){this.logger?.warn("Callback execution failed",{error:r})}}cancel=async()=>{if(this.logger?.info("Attempting cancellation"),u.TERMINAL_STATES.includes(this._currentState))return this.logger?.warn("Cannot cancel: Transaction already in terminal state",{state:this._currentState}),!1;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=(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=(n=>(n.CONNECTING="CONNECTING",n.CONNECTED="CONNECTED",n.OFFLINE="OFFLINE",n.DISCONNECTED="DISCONNECTED",n))(me||{});export{me as AppReaderStatus,L as MunchiPaymentSDK,R as PaymentInteractionState,h as SdkPaymentStatus};
|
|
1
|
+
import{PaymentFailureCode as y,PaymentProvider as O}from"@munchi_oy/core";var F="1.6.9";var c=class u extends Error{code;rawError;constructor(e,r,t){super(r),this.name="PaymentSDKError",this.code=e,this.rawError=t,Object.setPrototypeOf(this,u.prototype)}};import{PaymentApi as q,PaymentEventType as K,PaymentProviderEnum as Y,SimplePaymentStatus as P,TransactionType as x}from"@munchi_oy/core";var I=(i=>(i.PENDING="PENDING",i.SUCCESS="SUCCESS",i.APPROVED="APPROVED",i.FAILED="FAILED",i.CANCELLED="CANCELLED",i.ERROR="ERROR",i))(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 S=class{constructor(e,r,t){this.messaging=r;this.config=t;this.api=new q(void 0,"",e)}api;abortController=null;currentRequestId=null;paymentProvider=Y.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:x.Purchase}};try{let{data:n}=await this.api.initiateNetsTerminalTransaction(t),s=n.connectCloudRequestId;if(!s)throw new Error("connectCloudRequestId is missing from response.");if(this.currentRequestId=s,this.abortController.signal.aborted)throw new Error("Aborted");r("REQUIRES_INPUT",{sessionId:s});let i=await this.waitForPaymentCompletion(s,e.orderRef,this.abortController.signal);return this.currentRequestId=null,i}catch(n){throw this.currentRequestId=null,n instanceof c?n:new c("TERMINAL_BUSY","Failed to create Nets Intent",n)}}async waitForPaymentCompletion(e,r,t){let n=`nets.requests.${e}`,s=K.StatusChanged;return new Promise((i,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(n,s,f=>{a||f.status===P.Pending||(l(),t.removeEventListener("abort",d),i(this.handleSuccess(f)))}),p=setTimeout(async()=>{if(!(a||t.aborted))try{let f=await this.pollOrderStatus(e,r,this.config.storeId,t);i(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,r,t,n){let a=Date.now()+12e4;for(;Date.now()<a;){if(n.aborted)throw new Error("Aborted");try{let{data:l}=await this.api.getPaymentStatus({businessId:Number(t),orderId:r,provider:this.paymentProvider,referenceId:e});if(n.aborted)throw new Error("Aborted");if(l.status!==P.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)};n.addEventListener("abort",d,{once:!0});let m=setTimeout(()=>{n.removeEventListener("abort",d),l(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:x.ReturnOfGoods}},{data:n}=await this.api.initiateNetsTerminalTransaction(t),s=n.connectCloudRequestId;if(!s)throw new Error("connectCloudRequestId is missing from response.");if(this.currentRequestId=s,this.abortController.signal.aborted)throw new Error("Aborted");r("REQUIRES_INPUT",{sessionId:s});let i=await this.waitForPaymentCompletion(s,e.orderRef,this.abortController.signal);return this.currentRequestId=null,i}catch(t){throw this.currentRequestId=null,t instanceof c||t instanceof c?t:new c("NETWORK_ERROR","Failed to refund Nets transaction",t)}}async verifyFinalStatus(e,r){try{let{data:t}=await this.api.getPaymentStatus({businessId:Number(this.config.storeId),orderId:e.orderRef,provider:this.paymentProvider,referenceId:r});return this.handleSuccess(t)}catch(t){throw new c("NETWORK_ERROR","Failed to verify final Nets status",t)}}handleSuccess(e){let r=e.status===P.Success,t=e.status===P.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 U,PaymentApi as Q,PaymentFailureCode as T,PaymentMethod as A,PaymentProvider as k,VivaApi as $}from"@munchi_oy/core";var v=u=>{if(!u)return 0;let e=Number.parseInt(u,10);return Number.isFinite(e)?e:0},M=u=>{if(!u)return new Date().toISOString();let e=new Date(u);return Number.isNaN(e.getTime())?new Date().toISOString():e.toISOString()};var j="vivapayclient://pay/v1",X="abort",z="cancel",H="sale",V="transactionDetails",J=4e3,Z="534287",ee="38",te="493591",re="497439",ne=[te,re],se=["CREDIT","PREPAID","CORPORATE"],N=class{constructor(e,r,t,n){this.config=t;this.appToAppConfig=n;this.api=new $(void 0,"",e),this.paymentApi=new Q(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 c("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(H,this.buildLaunchParams(t),e,r)}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 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(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,r){r("CONNECTING");let t=await this.fetchPaymentReference(e),n=this.buildRefundLaunchParams(e,t);return this.runAction(z,n,e,r)}async verifyFinalStatus(e,r){return this.latestResult?this.latestResult:this.wasCancelled?{success:!1,status:"CANCELLED",orderId:this.currentOrderRef??r,errorCode:T.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 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=r.sourceTerminalId??r.tid??null,this.currentClientTransactionId=r.clientTransactionId??null,this.currentIsvClientTransactionId=r.ISV_clientTransactionId??null;let s=this.currentClientTransactionId?{sessionId:this.currentClientTransactionId}:void 0;return new Promise((i,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),i(p.result))});this.abortController?.signal.addEventListener("abort",l),n("REQUIRES_INPUT",s),this.appToAppConfig.adapter.openUrl(this.buildActionUrl(e,r)).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 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(),s={amount:String(e.amountCents),clientTransactionId:n,show_receipt:"false",show_transaction_result:"false"};if(t.sourceCode&&(s.sourceCode=t.sourceCode),r?.referenceCode&&(s.orderCode=r.referenceCode),r?.shortReferenceCode&&(s.shortOrderCode=r.shortReferenceCode),r?.receiptNumber&&(s.referenceNumber=r.receiptNumber),r?.terminalId&&(s.tid=r.terminalId),r?.referenceCode||r?.shortReferenceCode||r?.receiptNumber)return s;let i=n;return/^\d+$/.test(i)&&(i.length===16?s.orderCode=i:i.length===10?s.shortOrderCode=i:s.referenceNumber=i),s}async fetchPaymentReference(e){try{let r={orderId:e.orderRef,provider:k.Viva,transactionId:e.originalTransactionId,businessId:e.businessId},{data:t}=await this.paymentApiWithReference.getPaymentReference(r);return this.extractPaymentReferenceContext(t)}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},s=this.readReferenceValue(t.receiptNumber)??this.readReferenceValue(t.referenceNumber),i=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 s&&(n.receiptNumber=s),i&&(n.referenceCode=i),o&&(n.shortReferenceCode=o),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.resolveCallbackQueryParam()});return Object.entries(r).forEach(([n,s])=>{t.set(n,s)}),`${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),s=this.isCancelledCallback(t),i=this.resolveFailureCode(t,s),o={success:n,status:n?"SUCCESS":s?"CANCELLED":"FAILED",orderId:this.currentOrderRef??t.clientTransactionId??"unknown",errorCode:n?"":i,errorMessage:n?"":t.message??"Transaction failed without error details"};n&&(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 r=new URL(e),t=this.appToAppConfig.callbackUrl;if(!t.includes("://")){let s=t.replace(/:$/,"");return r.protocol===`${s}:`}let n=new URL(t);return r.protocol===n.protocol&&r.hostname===n.hostname&&r.pathname===n.pathname}resolveCallbackQueryParam(){if(this.appToAppConfig.callbackParamFormat!=="scheme-only")return this.appToAppConfig.callbackUrl;let e=this.appToAppConfig.callbackUrl;return e.includes("://")?new URL(e).protocol.replace(/:$/,""):e.replace(/:$/,"")}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 T.PaymentCancelledByUser;let t=e.errorCode;if(t?.includes("."))return t;if(this.isTransactionDetailsAction(e)&&(e.transactionType??"").toLowerCase().includes("abort"))return T.SystemUnknown;let n=(e.message??"").toLowerCase(),s=(e.status??"").toLowerCase();return n.includes("declined")||s==="declined"||s==="fail"||s==="failed"||s==="failure"?T.PaymentDeclined:T.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()===V.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")||t.includes("cancelled by user")||t.includes("canceled by user")||t.includes("transaction cancelled by user")||t.includes("transaction canceled by user")||n.includes("user_cancel")||n.includes("user cancel")||n==="1000"&&(t.includes("cancelled by user")||t.includes("canceled by user"))}extractErrorReferenceFromMessage(e){return e?e.match(/\((-?\d+)\)/)?.[1]:void 0}mapCallbackToTransaction(e){let r=e.transactionId??e.orderCode??e.clientTransactionId??"unknown",t=M(e.transactionDate),n=v(e.amount),s=v(e.tipAmount),i=v(e.surchargeAmount);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.buildFees(s,i),id:r,label:null,provider:k.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),s=(e.bankId??"").trim();return n===Z||s===ee?A.Edenred:ne.includes(n)?A.WoltBenefit:se.some(i=>r.includes(i))?A.Credit:A.Debit}buildFees(e,r){if(!this.currentCurrency)return null;let t=[];return e>0&&t.push({taxAmount:0,total:{amount:e,currency:this.currentCurrency},type:U.TipAmount,vatPercentage:0}),r>0&&t.push({taxAmount:0,total:{amount:r,currency:this.currentCurrency},type:U.Surcharge,vatPercentage:0}),t.length===0?null:{parts:t}}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,s(),r(null))},J),s=this.appToAppConfig.adapter.subscribe(i=>{if(t)return;let o=this.parseCallbackUrl(i);o&&this.matchesKnownTransactionId(o.rawParams)&&(t=!0,clearTimeout(n),s(),r(o.result))});this.appToAppConfig.adapter.openUrl(this.buildActionUrl(V,e)).catch(()=>{t||(t=!0,clearTimeout(n),s(),r(null))})})}};import{PaymentApi as ie,PaymentFailureCode as D,PaymentProviderEnum as oe,SimplePaymentStatus as E}from"@munchi_oy/core";var C=class u{constructor(e,r,t){this.messaging=r;this.config=t;this.api=new ie(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,r){this.abortController=new AbortController,r("CONNECTING");let t={amount:e.amountCents,referenceId:e.orderRef,businessId:parseInt(this.config.storeId),currency:e.currency,displayId:e.displayId,showReceipt:!0,showTransactionResult:!0};try{let{data:n}=await this.api.initiateTerminalTransaction(t);if(this.currentSessionId=n.sessionId,this.abortController.signal.aborted)throw new Error("Aborted");r("REQUIRES_INPUT",{sessionId:n.sessionId});let s=await this.waitForPaymentCompletion(n.sessionId,e,this.abortController.signal);return this.currentSessionId=null,s}catch(n){throw this.currentSessionId=null,n instanceof c?n:new c("NETWORK_ERROR","Failed to create Viva Intent",n)}}async waitForPaymentCompletion(e,r,t){let s=`viva.${this.config.channel.toLowerCase()}.requests.${e}`,i="payment:status-changed";return new Promise((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(s,i,h=>{l||h.status!==E.Pending&&(d(),t.removeEventListener("abort",m),o(this.handleSuccess(h)))}),f=setTimeout(async()=>{if(!(l||t.aborted))try{let h=await this.pollOrderStatus(e,r.orderRef,this.config.storeId,t);if(l)return;o(this.handleSuccess(h))}catch(h){if(l)return;if(h instanceof Error&&h.message==="Aborted"){a(h);return}try{let g=await this.recoverAfterPollingTimeout(r,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,r,t,n){let i=Date.now()+u.POLLING_DURATION_MS;for(;Date.now()<i;){if(n.aborted)throw new Error("Aborted");try{let{data:o}=await this.api.getPaymentStatus({businessId:Number(t),orderId:r,provider:this.paymentProvider,referenceId:e});if(n.aborted)throw new Error("Aborted");if(o.status!==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)};n.addEventListener("abort",a,{once:!0});let l=setTimeout(()=>{n.removeEventListener("abort",a),o(void 0)},u.POLLING_INTERVAL_MS)})}throw new Error("Payment verification timed out.")}async recoverAfterPollingTimeout(e,r){return await this.cancelSessionWithTimeout(r),await this.verifyFinalStatusWithRetry(e,r)}async cancelSessionWithTimeout(e){let r;try{await Promise.race([this.cancelSession(e),new Promise((t,n)=>{r=setTimeout(()=>{n(new Error("Viva timeout cancellation timed out"))},u.TIMEOUT_CANCEL_TIMEOUT_MS)})])}catch{}finally{r&&clearTimeout(r)}}async cancelSession(e){return await this.api.cancelVivaTransactionV2({cashRegisterId:this.config.storeId,sessionId:e}),this.currentSessionId===e&&(this.currentSessionId=null),!0}async verifyFinalStatusWithRetry(e,r){let t;for(let s=1;s<=u.VERIFY_MAX_RETRIES;s++)try{let i=await Promise.race([this.verifyFinalStatus(e,r),new Promise((o,a)=>setTimeout(()=>a(new Error("Verify timed out")),u.VERIFY_TIMEOUT_MS))]);if(i.status==="PENDING"){t=new Error("Verify returned pending status"),s<u.VERIFY_MAX_RETRIES&&await new Promise(o=>setTimeout(o,u.VERIFY_RETRY_DELAY_MS));continue}return i}catch(i){t=i,s<u.VERIFY_MAX_RETRIES&&await new Promise(o=>setTimeout(o,u.VERIFY_RETRY_DELAY_MS))}let n=t instanceof Error?t.message:"Verify retries exhausted";throw new c(D.PaymentTimeout,n)}handleSuccess(e){let r=e.status===E.Success,t={success:r,status:r?"SUCCESS":"FAILED",orderId:e.orderId,errorCode:e.error?.code||(r?"":D.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 c("NETWORK_ERROR","Failed to cancel Viva transaction",r)}}async verifyFinalStatus(e,r){try{let{data:t}=await this.api.getPaymentStatus({businessId:Number(this.config.storeId),orderId:e.orderRef,provider:this.paymentProvider,referenceId:r}),n=t.status===E.Success,s=t.status===E.Pending,i={success:n,status:n?"SUCCESS":s?"PENDING":"FAILED",orderId:t.orderId,errorCode:t.error?.code||(n||s?"":D.SystemUnknown),errorMessage:t.error?.message||(n||s?"":"Transaction failed without error details")};return t.transactionId&&(i.transactionId=t.transactionId),t.error?.referenceError&&(i.errorReference=t.error.referenceError),t.transaction&&(i.transaction=t.transaction),i}catch(t){throw new c("NETWORK_ERROR","Failed to verify final transaction status",t)}}async refundTransaction(e,r){try{let t={amount:e.amountCents,businessId:Number(this.config.storeId),displayId:this.config.kioskId,currency:e.currency,orderReferenceId:e.orderRef,referenceId:e.originalTransactionId},{data:n}=await this.api.refundSingleVivaTransaction(t),s=n.success;return{success:s,status:s?"SUCCESS":"FAILED",orderId:e.orderRef,transactionId:n.sessionId}}catch(t){throw new c("NETWORK_ERROR","Failed to refund Viva transaction",t)}}abort(){this.abortController?.abort()}};import{PaymentApi as ae,PaymentEventType as W,PaymentFailureCode as ce,PaymentProviderEnum as le,SimplePaymentStatus as w,WorldlineApi as ue}from"@munchi_oy/core";import{isAxiosError as de}from"axios";var _=class{constructor(e,r,t){this.messaging=r;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,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),s=this.extractOperationId(n.data);if(this.currentOperationId=s,this.abortController.signal.aborted)throw new Error("Aborted");r("REQUIRES_INPUT",{sessionId:s});let i=await this.waitForPaymentCompletion(s,e.orderRef,W.StatusChanged,this.abortController.signal);return this.currentOperationId=null,i}catch(n){throw this.currentOperationId=null,n instanceof c?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),s=this.extractOperationId(n.data);if(this.currentOperationId=s,this.abortController.signal.aborted)throw new Error("Aborted");r("REQUIRES_INPUT",{sessionId:s});let i=await this.waitForPaymentCompletion(s,e.orderRef,W.RefundStatusChanged,this.abortController.signal);return this.currentOperationId=null,i}catch(n){throw this.currentOperationId=null,n instanceof c?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 s=`worldline.requests.${e}`;return new Promise((i,o)=>{let a=!1,l=()=>{a=!0,typeof m=="function"&&m(),clearTimeout(p)},d=()=>{l(),o(new c("CANCELLED","Transaction cancelled"))};n.addEventListener("abort",d);let m=this.messaging.subscribe(s,t,f=>{a||f.status===w.Pending||(l(),n.removeEventListener("abort",d),i(this.handlePaymentStatus(f)))}),p=setTimeout(async()=>{if(!(a||n.aborted))try{let f=await this.pollOrderStatus(e,r,this.config.storeId,n);i(this.handlePaymentStatus(f))}catch(f){o(new c("TIMEOUT","Payment timed out and polling failed",f))}finally{n.removeEventListener("abort",d),l()}},1e4)})}async pollOrderStatus(e,r,t,n){let o=Date.now()+12e4;for(;Date.now()<o;){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!==w.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)};n.addEventListener("abort",l,{once:!0});let d=setTimeout(()=>{n.removeEventListener("abort",l),a(void 0)},2e3)})}throw new Error("Payment verification timed out.")}handlePaymentStatus(e){let r=e.status===w.Success,t=e.status===w.Pending,n={success:r,status:r?"SUCCESS":t?"PENDING":"FAILED",orderId:e.orderId,errorCode:e.error?.code||(r||t?"":ce.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(de(e)){if(e.response?.status===400||e.response?.status===409)return new c("TERMINAL_BUSY",r,e);if(e.response?.status===404)return new c("TERMINAL_OFFLINE",r,e)}return new c("NETWORK_ERROR",r,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,r,t,n={},s){this.axios=e,this.messaging=r,this.logger=n.logger,this.timeoutMs=n.timeoutMs||3e4,this.autoResetOptions=n.autoResetOnPaymentComplete,this.appToAppConfig=n.appToApp,this.strategy=s??this.resolveStrategy(t)}get version(){return F}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(u.TERMINAL_STATES.includes(this._currentState)){let t=`Invalid State Transition: Attempted to move from terminal state ${this._currentState} to ${e}`;throw this.logger?.error(t),this._currentState!=="INTERNAL_ERROR"&&(this._currentState="INTERNAL_ERROR",this._listeners.forEach(n=>n(this._currentState))),new c("UNKNOWN",t)}this._currentState=e,u.TERMINAL_STATES.includes(e)&&this.scheduleAutoReset(e),this._listeners.forEach(t=>t(e))}_resetScheduledAt;get nextAutoResetAt(){return this._resetScheduledAt}cancelAutoReset(){this._autoResetTimer&&(clearTimeout(this._autoResetTimer),this._autoResetTimer=void 0),this._resetScheduledAt=void 0}scheduleAutoReset(e){if(!this.autoResetOptions)return;let t=e==="SUCCESS"?this.autoResetOptions.successDelayMs??5e3:this.autoResetOptions.failureDelayMs??5e3;this.logger?.info(`Scheduling auto-reset to IDLE in ${t}ms`),this._resetScheduledAt=Date.now()+t,this._autoResetTimer=setTimeout(()=>{this.logger?.info("Auto-reset triggered"),this.reset()},t)}resolveStrategy(e){switch(e.provider){case O.Nets:return new S(this.axios,this.messaging,e);case O.Worldline:return new _(this.axios,this.messaging,e);case O.Viva:return this.appToAppConfig?.enabled?new N(this.axios,this.messaging,e,this.appToAppConfig):new C(this.axios,this.messaging,e);default:return new C(this.axios,this.messaging,e)}}initiateTransaction=async(e,r)=>{let t=r??{},n=e.orderRef;if(!u.RESTING_STATES.includes(this._currentState))return this.generateErrorResult(e.orderRef,"UNKNOWN","A transaction is already in progress");let i=Date.now();if(this._cancellationIntent=!1,this._currentSessionId=void 0,this.transitionTo("IDLE"),e.amountCents<=0)return this.generateErrorResult(e.orderRef,"INVALID_AMOUNT","Amount must be greater than 0");try{let o=(m,p)=>{p?.sessionId&&(this._currentSessionId=p.sessionId),m!=="FAILED"&&(this.transitionTo(m),this.fireStateCallback(m,t,n))},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()-i}),d}catch(o){return this.logger?.warn("Transaction interrupted. Handling final status...",{error:o}),await this.handleTransactionError(e,o,t)}};async handleTransactionError(e,r,t={}){let n=r instanceof c&&(r.code==="TIMEOUT"||r.code===y.PaymentTimeout);if(!this._cancellationIntent&&!n&&(this.transitionTo("VERIFYING"),this.safeFireCallback(()=>t.onVerifying?.({orderRef:e.orderRef,refPaymentId:this._currentSessionId}))),this._cancellationIntent){try{if(this._currentSessionId){let i=await this.strategy.verifyFinalStatus(e,this._currentSessionId);if(i.success)return this.transitionTo("SUCCESS"),this.safeFireCallback(()=>t.onSuccess?.(i)),i}}catch(i){this.logger?.warn("Final status verification failed during cancellation",{err:i})}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 s;if(this.strategy.abort(),this._currentSessionId)try{let i=await this.strategy.verifyFinalStatus(e,this._currentSessionId);if(i.success)return this.transitionTo("SUCCESS"),this.safeFireCallback(()=>t.onSuccess?.(i)),i;s=n&&i.status==="PENDING"?this.buildErrorResultFromException(e.orderRef,r):i}catch(i){this.logger?.warn("Failed to get detailed error from verifyFinalStatus",{verifyErr:i}),s=this.buildErrorResultFromException(e.orderRef,i)}else s=this.buildErrorResultFromException(e.orderRef,r);return this.transitionTo("FAILED"),this.safeFireCallback(()=>t.onError?.(s)),s}buildErrorResultFromException(e,r){return r instanceof c?this.generateErrorResult(e,r.code,r.message):this.generateErrorResult(e,"UNKNOWN",r instanceof Error?r.message:"Unknown fatal error")}fireStateCallback(e,r,t){let n={orderRef:t,refPaymentId:this._currentSessionId};switch(e){case"CONNECTING":this.safeFireCallback(()=>r.onConnecting?.(n));break;case"REQUIRES_INPUT":this.safeFireCallback(()=>r.onRequiresInput?.(n));break;case"PROCESSING":this.safeFireCallback(()=>r.onProcessing?.(n));break}}safeFireCallback(e){try{e()}catch(r){this.logger?.warn("Callback execution failed",{error:r})}}cancel=async()=>{if(this.logger?.info("Attempting cancellation"),u.TERMINAL_STATES.includes(this._currentState))return this.logger?.warn("Cannot cancel: Transaction already in terminal state",{state:this._currentState}),!1;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 s=(o,a)=>{a?.sessionId&&(this._currentSessionId=a.sessionId),o!=="FAILED"&&(this.transitionTo(o),this.fireStateCallback(o,t,e.orderRef))},i=await this.strategy.refundTransaction(e,s);return i.success?(this.transitionTo("SUCCESS"),this.safeFireCallback(()=>t.onSuccess?.(i))):this._cancellationIntent||i.status==="CANCELLED"?(this.transitionTo("FAILED"),this.safeFireCallback(()=>t.onCancelled?.({orderRef:e.orderRef,refPaymentId:this._currentSessionId}))):(this.transitionTo("FAILED"),this.safeFireCallback(()=>t.onError?.(i))),this.logger?.info("Refund completed",{success:i.success,orderRef:e.orderRef}),i}catch(s){if(this.logger?.error("Refund failed",s),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 i=this.generateErrorResult(e.orderRef,"UNKNOWN",s instanceof Error?s.message:"Refund failed");return this.safeFireCallback(()=>t.onError?.(i)),i}}};var me=(n=>(n.CONNECTING="CONNECTING",n.CONNECTED="CONNECTED",n.OFFLINE="OFFLINE",n.DISCONNECTED="DISCONNECTED",n))(me||{});export{me as AppReaderStatus,L as MunchiPaymentSDK,R as PaymentInteractionState,I as SdkPaymentStatus};
|
|
@@ -37,6 +37,7 @@ export declare class VivaAppToAppStrategy implements IPaymentStrategy {
|
|
|
37
37
|
private buildActionUrl;
|
|
38
38
|
private parseCallbackUrl;
|
|
39
39
|
private matchesCallbackBase;
|
|
40
|
+
private resolveCallbackQueryParam;
|
|
40
41
|
private matchesKnownTransactionId;
|
|
41
42
|
private resolveFailureCode;
|
|
42
43
|
private isSuccessfulCallback;
|
|
@@ -45,7 +46,7 @@ export declare class VivaAppToAppStrategy implements IPaymentStrategy {
|
|
|
45
46
|
private extractErrorReferenceFromMessage;
|
|
46
47
|
private mapCallbackToTransaction;
|
|
47
48
|
private getVivaPaymentMethodType;
|
|
48
|
-
private
|
|
49
|
+
private buildFees;
|
|
49
50
|
private lookupTransactionDetails;
|
|
50
51
|
}
|
|
51
52
|
//# sourceMappingURL=VivaAppToAppStrategy.d.ts.map
|
|
@@ -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;IAkB3B,OAAO,CAAC,yBAAyB;IAajC,OAAO,CAAC,yBAAyB;IAsBjC,OAAO,CAAC,kBAAkB;IAoC1B,OAAO,CAAC,oBAAoB;IAc5B,OAAO,CAAC,0BAA0B;IAKlC,OAAO,CAAC,mBAAmB;IAsB3B,OAAO,CAAC,gCAAgC;IAWxC,OAAO,CAAC,wBAAwB;IAmChC,OAAO,CAAC,wBAAwB;IAuBhC,OAAO,CAAC,SAAS;YA2CH,wBAAwB;CA2DvC"}
|
|
@@ -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/types/sdk.d.ts
CHANGED
|
@@ -37,6 +37,7 @@ export interface AutoResetOptions {
|
|
|
37
37
|
failureDelayMs?: number;
|
|
38
38
|
}
|
|
39
39
|
export type AppToAppScalar = boolean | number | string;
|
|
40
|
+
export type AppToAppCallbackParamFormat = "full-url" | "scheme-only";
|
|
40
41
|
export interface AppToAppAdapter {
|
|
41
42
|
openUrl(url: string): Promise<void>;
|
|
42
43
|
subscribe(listener: (url: string) => void): () => void;
|
|
@@ -46,6 +47,7 @@ export interface AppToAppConfig {
|
|
|
46
47
|
enabled: boolean;
|
|
47
48
|
appId: string;
|
|
48
49
|
callbackUrl: string;
|
|
50
|
+
callbackParamFormat?: AppToAppCallbackParamFormat;
|
|
49
51
|
adapter: AppToAppAdapter;
|
|
50
52
|
}
|
|
51
53
|
export interface SDKOptions {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sdk.d.ts","sourceRoot":"","sources":["../../../src/types/sdk.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAE/C,MAAM,WAAW,OAAO;IACtB,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IAC7D,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IAC5D,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IAC9E,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;CAC7D;AAED,MAAM,WAAW,gBAAgB;IAC/B,YAAY,CAAC,EAAE,CAAC,GAAG,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IACnD,eAAe,CAAC,EAAE,CAAC,GAAG,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IACtD,YAAY,CAAC,EAAE,CAAC,GAAG,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IACnD,WAAW,CAAC,EAAE,CAAC,GAAG,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IAClD,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,CAAA;KAAE,KAAK,IAAI,CAAC;CACnD;AAED,MAAM,WAAW,gBAAgB;IAC/B;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IAExB;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,MAAM,cAAc,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"sdk.d.ts","sourceRoot":"","sources":["../../../src/types/sdk.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAE/C,MAAM,WAAW,OAAO;IACtB,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IAC7D,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IAC5D,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IAC9E,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;CAC7D;AAED,MAAM,WAAW,gBAAgB;IAC/B,YAAY,CAAC,EAAE,CAAC,GAAG,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IACnD,eAAe,CAAC,EAAE,CAAC,GAAG,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IACtD,YAAY,CAAC,EAAE,CAAC,GAAG,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IACnD,WAAW,CAAC,EAAE,CAAC,GAAG,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IAClD,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,CAAA;KAAE,KAAK,IAAI,CAAC;CACnD;AAED,MAAM,WAAW,gBAAgB;IAC/B;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IAExB;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,MAAM,cAAc,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,CAAC;AACvD,MAAM,MAAM,2BAA2B,GAAG,UAAU,GAAG,aAAa,CAAC;AAErE,MAAM,WAAW,eAAe;IAC9B,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACpC,SAAS,CAAC,QAAQ,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;IACvD,aAAa,CAAC,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;CAC1C;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,mBAAmB,CAAC,EAAE,2BAA2B,CAAC;IAClD,OAAO,EAAE,eAAe,CAAC;CAC1B;AAED,MAAM,WAAW,UAAU;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB;;;OAGG;IACH,0BAA0B,CAAC,EAAE,gBAAgB,CAAC;IAC9C,QAAQ,CAAC,EAAE,cAAc,CAAC;CAC3B"}
|
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.9";
|
|
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.9",
|
|
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",
|
|
@@ -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 {
|
|
@@ -498,7 +503,7 @@ export class VivaAppToAppStrategy implements IPaymentStrategy {
|
|
|
498
503
|
const query = new URLSearchParams({
|
|
499
504
|
action,
|
|
500
505
|
appId: this.appToAppConfig.appId,
|
|
501
|
-
callback: this.
|
|
506
|
+
callback: this.resolveCallbackQueryParam(),
|
|
502
507
|
});
|
|
503
508
|
|
|
504
509
|
Object.entries(params).forEach(([key, value]) => {
|
|
@@ -567,7 +572,14 @@ export class VivaAppToAppStrategy implements IPaymentStrategy {
|
|
|
567
572
|
|
|
568
573
|
private matchesCallbackBase(url: string): boolean {
|
|
569
574
|
const actual = new URL(url);
|
|
570
|
-
const
|
|
575
|
+
const expectedCallbackUrl = this.appToAppConfig.callbackUrl;
|
|
576
|
+
|
|
577
|
+
if (!expectedCallbackUrl.includes("://")) {
|
|
578
|
+
const expectedScheme = expectedCallbackUrl.replace(/:$/, "");
|
|
579
|
+
return actual.protocol === `${expectedScheme}:`;
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
const expected = new URL(expectedCallbackUrl);
|
|
571
583
|
|
|
572
584
|
return (
|
|
573
585
|
actual.protocol === expected.protocol &&
|
|
@@ -576,6 +588,19 @@ export class VivaAppToAppStrategy implements IPaymentStrategy {
|
|
|
576
588
|
);
|
|
577
589
|
}
|
|
578
590
|
|
|
591
|
+
private resolveCallbackQueryParam(): string {
|
|
592
|
+
if (this.appToAppConfig.callbackParamFormat !== "scheme-only") {
|
|
593
|
+
return this.appToAppConfig.callbackUrl;
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
const callbackUrl = this.appToAppConfig.callbackUrl;
|
|
597
|
+
if (!callbackUrl.includes("://")) {
|
|
598
|
+
return callbackUrl.replace(/:$/, "");
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
return new URL(callbackUrl).protocol.replace(/:$/, "");
|
|
602
|
+
}
|
|
603
|
+
|
|
579
604
|
private matchesKnownTransactionId(rawParams: Record<string, string>): boolean {
|
|
580
605
|
const knownIds = [
|
|
581
606
|
this.currentClientTransactionId,
|
|
@@ -625,7 +650,8 @@ export class VivaAppToAppStrategy implements IPaymentStrategy {
|
|
|
625
650
|
rawMessage.includes("declined") ||
|
|
626
651
|
rawStatus === "declined" ||
|
|
627
652
|
rawStatus === "fail" ||
|
|
628
|
-
rawStatus === "failed"
|
|
653
|
+
rawStatus === "failed" ||
|
|
654
|
+
rawStatus === "failure"
|
|
629
655
|
) {
|
|
630
656
|
return PaymentFailureCode.PaymentDeclined;
|
|
631
657
|
}
|
|
@@ -662,8 +688,15 @@ export class VivaAppToAppStrategy implements IPaymentStrategy {
|
|
|
662
688
|
rawStatus === "canceled" ||
|
|
663
689
|
rawMessage.includes("user_cancel") ||
|
|
664
690
|
rawMessage.includes("user cancel") ||
|
|
691
|
+
rawMessage.includes("cancelled by user") ||
|
|
692
|
+
rawMessage.includes("canceled by user") ||
|
|
693
|
+
rawMessage.includes("transaction cancelled by user") ||
|
|
694
|
+
rawMessage.includes("transaction canceled by user") ||
|
|
665
695
|
rawErrorCode.includes("user_cancel") ||
|
|
666
|
-
rawErrorCode.includes("user cancel")
|
|
696
|
+
rawErrorCode.includes("user cancel") ||
|
|
697
|
+
(rawErrorCode === "1000" &&
|
|
698
|
+
(rawMessage.includes("cancelled by user") ||
|
|
699
|
+
rawMessage.includes("canceled by user")))
|
|
667
700
|
);
|
|
668
701
|
}
|
|
669
702
|
|
|
@@ -687,6 +720,7 @@ export class VivaAppToAppStrategy implements IPaymentStrategy {
|
|
|
687
720
|
const timestamp = normalizeTimestamp(rawParams.transactionDate);
|
|
688
721
|
const amount = parseInteger(rawParams.amount);
|
|
689
722
|
const tipAmount = parseInteger(rawParams.tipAmount);
|
|
723
|
+
const surchargeAmount = parseInteger(rawParams.surchargeAmount);
|
|
690
724
|
|
|
691
725
|
return {
|
|
692
726
|
amount,
|
|
@@ -701,7 +735,7 @@ export class VivaAppToAppStrategy implements IPaymentStrategy {
|
|
|
701
735
|
transactionId,
|
|
702
736
|
},
|
|
703
737
|
createdAt: timestamp,
|
|
704
|
-
fees: this.
|
|
738
|
+
fees: this.buildFees(tipAmount, surchargeAmount),
|
|
705
739
|
id: transactionId,
|
|
706
740
|
label: null,
|
|
707
741
|
provider: PaymentProvider.Viva,
|
|
@@ -735,23 +769,46 @@ export class VivaAppToAppStrategy implements IPaymentStrategy {
|
|
|
735
769
|
return PaymentMethod.Debit;
|
|
736
770
|
}
|
|
737
771
|
|
|
738
|
-
private
|
|
739
|
-
|
|
772
|
+
private buildFees(
|
|
773
|
+
tipAmount: number,
|
|
774
|
+
surchargeAmount: number,
|
|
775
|
+
): TransactionDtoFees | null {
|
|
776
|
+
if (!this.currentCurrency) {
|
|
740
777
|
return null;
|
|
741
778
|
}
|
|
742
779
|
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
780
|
+
const parts: TransactionDtoFees["parts"] = [];
|
|
781
|
+
|
|
782
|
+
if (tipAmount > 0) {
|
|
783
|
+
parts.push({
|
|
784
|
+
taxAmount: 0,
|
|
785
|
+
total: {
|
|
786
|
+
amount: tipAmount,
|
|
787
|
+
currency: this.currentCurrency,
|
|
788
|
+
},
|
|
789
|
+
type: FeeType.TipAmount,
|
|
790
|
+
vatPercentage: 0,
|
|
791
|
+
});
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
if (surchargeAmount > 0) {
|
|
795
|
+
parts.push({
|
|
796
|
+
taxAmount: 0,
|
|
797
|
+
total: {
|
|
798
|
+
amount: surchargeAmount,
|
|
799
|
+
currency: this.currentCurrency,
|
|
753
800
|
},
|
|
754
|
-
|
|
801
|
+
type: FeeType.Surcharge,
|
|
802
|
+
vatPercentage: 0,
|
|
803
|
+
});
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
if (parts.length === 0) {
|
|
807
|
+
return null;
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
return {
|
|
811
|
+
parts,
|
|
755
812
|
};
|
|
756
813
|
}
|
|
757
814
|
|
package/src/types/payment.ts
CHANGED
package/src/types/sdk.ts
CHANGED
|
@@ -32,6 +32,7 @@ export interface AutoResetOptions {
|
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
export type AppToAppScalar = boolean | number | string;
|
|
35
|
+
export type AppToAppCallbackParamFormat = "full-url" | "scheme-only";
|
|
35
36
|
|
|
36
37
|
export interface AppToAppAdapter {
|
|
37
38
|
openUrl(url: string): Promise<void>;
|
|
@@ -43,6 +44,7 @@ export interface AppToAppConfig {
|
|
|
43
44
|
enabled: boolean;
|
|
44
45
|
appId: string;
|
|
45
46
|
callbackUrl: string;
|
|
47
|
+
callbackParamFormat?: AppToAppCallbackParamFormat;
|
|
46
48
|
adapter: AppToAppAdapter;
|
|
47
49
|
}
|
|
48
50
|
|
package/src/version.ts
CHANGED