@munchi_oy/payments 1.10.6 → 1.11.0

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.
@@ -1,5 +1,12 @@
1
- import { type PaymentSessionViewDto } from "@munchi_oy/core";
1
+ import { PaymentSessionStatus, type PaymentSessionViewDto } from "@munchi_oy/core";
2
2
  import { type PaymentResult } from "../types/payment";
3
3
  export declare function pendingResult(orderId: string): PaymentResult;
4
4
  export declare function mapSessionResult(data: PaymentSessionViewDto, orderId: string): PaymentResult;
5
+ export interface ReversalStatusView {
6
+ status: PaymentSessionStatus;
7
+ providerTransactionId?: string;
8
+ errorCode?: string;
9
+ errorMessage?: string;
10
+ }
11
+ export declare function mapReversalResult(data: ReversalStatusView, orderId: string): PaymentResult;
5
12
  //# sourceMappingURL=sessionResult.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"sessionResult.d.ts","sourceRoot":"","sources":["../../../src/strategies/sessionResult.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,qBAAqB,EAC3B,MAAM,iBAAiB,CAAC;AAGzB,OAAO,EAAE,KAAK,aAAa,EAAoB,MAAM,kBAAkB,CAAC;AASxE,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,aAAa,CAQ5D;AAED,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,qBAAqB,EAC3B,OAAO,EAAE,MAAM,GACd,aAAa,CA+Df"}
1
+ {"version":3,"file":"sessionResult.d.ts","sourceRoot":"","sources":["../../../src/strategies/sessionResult.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,oBAAoB,EACpB,KAAK,qBAAqB,EAC3B,MAAM,iBAAiB,CAAC;AAGzB,OAAO,EAAE,KAAK,aAAa,EAAoB,MAAM,kBAAkB,CAAC;AASxE,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,aAAa,CAQ5D;AAED,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,qBAAqB,EAC3B,OAAO,EAAE,MAAM,GACd,aAAa,CAgEf;AAED,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,oBAAoB,CAAC;IAC7B,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,kBAAkB,EACxB,OAAO,EAAE,MAAM,GACd,aAAa,CAgEf"}
@@ -1,2 +1,2 @@
1
- export declare const VERSION = "1.10.6";
1
+ export declare const VERSION = "1.11.0";
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.10.6",
3
+ "version": "1.11.0",
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.67"
35
+ "@munchi_oy/core": "1.9.74"
36
36
  },
37
37
  "scripts": {
38
38
  "test": "jest",
@@ -4,6 +4,7 @@ import { version } from "../package.json";
4
4
  import { PaymentErrorCode, PaymentSDKError } from "./error";
5
5
  import type { IPaymentStrategy } from "./strategies/IPaymentStrategy";
6
6
  import { StrategyExecutionMode } from "./strategies/IPaymentStrategy";
7
+ import { NetsSessionStrategy } from "./strategies/NetsSessionStrategy";
7
8
  import { NetsStrategy } from "./strategies/NetsStrategy";
8
9
  import { VivaAppToAppStrategy } from "./strategies/VivaAppToAppStrategy";
9
10
  import { VivaSessionStrategy } from "./strategies/VivaSessionStrategy";
@@ -56,6 +57,8 @@ export class MunchiPaymentSDK implements IMunchiPaymentSDK {
56
57
  ...MunchiPaymentSDK.TERMINAL_STATES,
57
58
  ];
58
59
 
60
+ private static readonly CANCEL_VERIFY_TIMEOUT_MS = 5000;
61
+
59
62
  constructor(
60
63
  axios: AxiosInstance,
61
64
  messaging: IMessagingAdapter,
@@ -197,6 +200,9 @@ export class MunchiPaymentSDK implements IMunchiPaymentSDK {
197
200
  private resolveStrategy(config: PaymentTerminalConfig): IPaymentStrategy {
198
201
  switch (config.provider) {
199
202
  case PaymentProvider.Nets:
203
+ if (this.paymentSessionConfig?.enabled) {
204
+ return new NetsSessionStrategy(this.axios, this.messaging, config);
205
+ }
200
206
  return new NetsStrategy(this.axios, this.messaging, config);
201
207
  case PaymentProvider.Worldline:
202
208
  return new WorldlineStrategy(this.axios, this.messaging, config);
@@ -343,10 +349,14 @@ export class MunchiPaymentSDK implements IMunchiPaymentSDK {
343
349
 
344
350
  if (this._cancellationIntent) {
345
351
  try {
346
- if (this._currentSessionId) {
352
+ if (
353
+ this._currentSessionId &&
354
+ (this.strategy.shouldVerifyAfterCancel?.() ?? true)
355
+ ) {
347
356
  const finalStatus = await this.strategy.verifyFinalStatus(
348
357
  params,
349
358
  this._currentSessionId,
359
+ MunchiPaymentSDK.CANCEL_VERIFY_TIMEOUT_MS,
350
360
  );
351
361
  if (finalStatus.success) {
352
362
  this.transitionTo(PaymentInteractionState.SUCCESS);
@@ -689,7 +699,15 @@ export class MunchiPaymentSDK implements IMunchiPaymentSDK {
689
699
  this.transitionTo(PaymentInteractionState.CONNECTING);
690
700
 
691
701
  try {
692
- const result = await revertTransaction(params);
702
+ const result = await revertTransaction(params, (state, detail) => {
703
+ if (detail?.sessionId) {
704
+ this._currentSessionId = detail.sessionId;
705
+ }
706
+ if (state !== PaymentInteractionState.FAILED) {
707
+ this.transitionTo(state);
708
+ this.fireStateCallback(state, callbacks, params.orderRef);
709
+ }
710
+ });
693
711
  this.settleSingleShot(result, params.orderRef, callbacks);
694
712
  return result;
695
713
  } catch (error) {
@@ -13,6 +13,7 @@ export enum StrategyExecutionMode {
13
13
 
14
14
  export interface IPaymentStrategy {
15
15
  getExecutionMode?(): StrategyExecutionMode;
16
+ shouldVerifyAfterCancel?(): boolean;
16
17
 
17
18
  processPayment(
18
19
  request: PaymentRequest,
@@ -25,7 +26,17 @@ export interface IPaymentStrategy {
25
26
  request: RefundRequest,
26
27
  onStateChange: (state: PaymentInteractionState, detail?: { sessionId?: string }) => void,
27
28
  ): Promise<PaymentResult>;
28
- verifyFinalStatus(request: PaymentRequest, sessionId: string): Promise<PaymentResult>;
29
- revertTransaction?(request: RevertRequest): Promise<PaymentResult>;
29
+ verifyFinalStatus(
30
+ request: PaymentRequest,
31
+ sessionId: string,
32
+ timeoutMs?: number,
33
+ ): Promise<PaymentResult>;
34
+ revertTransaction?(
35
+ request: RevertRequest,
36
+ onStateChange?: (
37
+ state: PaymentInteractionState,
38
+ detail?: { sessionId?: string },
39
+ ) => void,
40
+ ): Promise<PaymentResult>;
30
41
  abort(): void;
31
42
  }
@@ -0,0 +1,448 @@
1
+ import {
2
+ type InitiateNetsSessionDto,
3
+ PaymentEventType,
4
+ PaymentSessionApi,
5
+ type PaymentSessionViewDto,
6
+ type PaymentStatusDto,
7
+ type ReverseSessionDto,
8
+ SimplePaymentStatus,
9
+ type TransactionDto,
10
+ } from "@munchi_oy/core";
11
+ import type { AxiosInstance } from "axios";
12
+
13
+ import { PaymentErrorCode, PaymentSDKError } from "../error";
14
+ import {
15
+ type IMessagingAdapter,
16
+ PaymentInteractionState as InteractionState,
17
+ type PaymentInteractionState,
18
+ type PaymentRequest,
19
+ type PaymentResult,
20
+ type PaymentTerminalConfig,
21
+ type RefundRequest,
22
+ type RevertRequest,
23
+ SdkPaymentStatus,
24
+ } from "../types/payment";
25
+ import type { IPaymentStrategy } from "./IPaymentStrategy";
26
+ import {
27
+ mapReversalResult,
28
+ mapSessionResult,
29
+ pendingResult,
30
+ type ReversalStatusView,
31
+ } from "./sessionResult";
32
+
33
+ export class NetsSessionStrategy implements IPaymentStrategy {
34
+ private static readonly POLLING_DURATION_MS = 180000;
35
+ private static readonly POLLING_INTERVAL_MS = 1000;
36
+ private static readonly VERIFY_DURATION_MS = 30000;
37
+ private static readonly INITIAL_POLL_DELAY_MS = 10000;
38
+
39
+ private sessionApi: PaymentSessionApi;
40
+ private abortController: AbortController | null = null;
41
+ private idempotencyKey: string | null = null;
42
+ private idempotencyOrderRef: string | null = null;
43
+ private currentClientToken: string | null = null;
44
+
45
+ constructor(
46
+ axios: AxiosInstance,
47
+ private messaging: IMessagingAdapter,
48
+ private config: PaymentTerminalConfig,
49
+ ) {
50
+ this.sessionApi = new PaymentSessionApi(undefined, "", axios);
51
+ }
52
+
53
+ async processPayment(
54
+ request: PaymentRequest,
55
+ onStateChange: (
56
+ state: PaymentInteractionState,
57
+ detail?: { sessionId?: string },
58
+ ) => void,
59
+ ): Promise<PaymentResult> {
60
+ this.abortController = new AbortController();
61
+ if (
62
+ this.idempotencyKey === null ||
63
+ this.idempotencyOrderRef !== request.orderRef
64
+ ) {
65
+ this.idempotencyKey = this.generateIdempotencyKey();
66
+ this.idempotencyOrderRef = request.orderRef;
67
+ }
68
+ this.currentClientToken = null;
69
+
70
+ onStateChange(InteractionState.CONNECTING);
71
+
72
+ const payload: InitiateNetsSessionDto = {
73
+ amount: request.amountCents,
74
+ businessId: Number(this.config.storeId),
75
+ orderId: request.orderRef,
76
+ };
77
+ if (request.splitContext !== undefined) {
78
+ payload.splitContext = request.splitContext;
79
+ }
80
+
81
+ try {
82
+ const { data } = await this.sessionApi.initiateNetsSession(
83
+ payload,
84
+ this.idempotencyKey,
85
+ );
86
+ this.currentClientToken = data.clientToken;
87
+ const signal = this.abortController.signal;
88
+
89
+ if (signal.aborted) {
90
+ throw new Error("Aborted");
91
+ }
92
+
93
+ onStateChange(InteractionState.REQUIRES_INPUT, {
94
+ sessionId: data.sessionId,
95
+ });
96
+
97
+ const terminal = await this.waitForCompletion(
98
+ data.clientToken,
99
+ data.sessionId,
100
+ request,
101
+ signal,
102
+ );
103
+
104
+ if (terminal) {
105
+ return this.finalize(terminal);
106
+ }
107
+
108
+ await this.armCancel(data.clientToken);
109
+ return pendingResult(request.orderRef);
110
+ } catch (err) {
111
+ if (err instanceof PaymentSDKError) throw err;
112
+ throw new PaymentSDKError(
113
+ PaymentErrorCode.TERMINAL_BUSY,
114
+ "Failed to create Nets payment session",
115
+ err,
116
+ );
117
+ }
118
+ }
119
+
120
+ async verifyFinalStatus(
121
+ request: PaymentRequest,
122
+ _sessionId: string,
123
+ timeoutMs?: number,
124
+ ): Promise<PaymentResult> {
125
+ if (!this.currentClientToken) {
126
+ throw new PaymentSDKError(
127
+ PaymentErrorCode.NETWORK_ERROR,
128
+ "No active payment session to verify",
129
+ );
130
+ }
131
+
132
+ const terminal = await this.pollUntilTerminal(
133
+ this.currentClientToken,
134
+ request.orderRef,
135
+ undefined,
136
+ timeoutMs ?? NetsSessionStrategy.VERIFY_DURATION_MS,
137
+ );
138
+
139
+ return this.finalize(terminal ?? pendingResult(request.orderRef));
140
+ }
141
+
142
+ async cancelTransaction(
143
+ _onStateChange: (state: PaymentInteractionState) => void,
144
+ ): Promise<boolean> {
145
+ if (!this.currentClientToken) {
146
+ return false;
147
+ }
148
+
149
+ try {
150
+ await this.sessionApi.cancelSession(this.currentClientToken);
151
+ } catch (error) {
152
+ throw new PaymentSDKError(
153
+ PaymentErrorCode.NETWORK_ERROR,
154
+ "Failed to cancel Nets payment session",
155
+ error,
156
+ );
157
+ }
158
+
159
+ this.abortController?.abort();
160
+ this.resetIdempotencyKey();
161
+ return true;
162
+ }
163
+
164
+ async refundTransaction(
165
+ request: RefundRequest,
166
+ onStateChange: (
167
+ state: PaymentInteractionState,
168
+ detail?: { sessionId?: string },
169
+ ) => void,
170
+ ): Promise<PaymentResult> {
171
+ this.abortController = new AbortController();
172
+ onStateChange(InteractionState.CONNECTING);
173
+
174
+ const payload: ReverseSessionDto = {
175
+ sessionId: request.originalTransactionId,
176
+ amount: request.amountCents,
177
+ };
178
+
179
+ try {
180
+ const { data } = await this.sessionApi.refundNetsSession(payload);
181
+ return await this.settleReversal(
182
+ data,
183
+ request.orderRef,
184
+ this.abortController.signal,
185
+ true,
186
+ onStateChange,
187
+ );
188
+ } catch (error) {
189
+ if (error instanceof PaymentSDKError) throw error;
190
+ throw new PaymentSDKError(
191
+ PaymentErrorCode.NETWORK_ERROR,
192
+ "Failed to refund Nets transaction",
193
+ error,
194
+ );
195
+ }
196
+ }
197
+
198
+ async revertTransaction(
199
+ request: RevertRequest,
200
+ onStateChange?: (
201
+ state: PaymentInteractionState,
202
+ detail?: { sessionId?: string },
203
+ ) => void,
204
+ ): Promise<PaymentResult> {
205
+ this.abortController = new AbortController();
206
+
207
+ const payload: ReverseSessionDto = {
208
+ sessionId: request.originalTransactionId,
209
+ };
210
+
211
+ try {
212
+ const { data } = await this.sessionApi.revertNetsSession(payload);
213
+ return await this.settleReversal(
214
+ data,
215
+ request.orderRef,
216
+ this.abortController.signal,
217
+ data.requiresCardTap,
218
+ onStateChange,
219
+ );
220
+ } catch (error) {
221
+ if (error instanceof PaymentSDKError) throw error;
222
+ throw new PaymentSDKError(
223
+ PaymentErrorCode.NETWORK_ERROR,
224
+ "Failed to revert Nets transaction",
225
+ error,
226
+ );
227
+ }
228
+ }
229
+
230
+ shouldVerifyAfterCancel(): boolean {
231
+ return false;
232
+ }
233
+
234
+ abort(): void {
235
+ this.abortController?.abort();
236
+ }
237
+
238
+ private async settleReversal(
239
+ view: ReversalStatusView & { clientToken: string; sessionId: string },
240
+ orderRef: string,
241
+ signal: AbortSignal,
242
+ requiresCardTap: boolean,
243
+ onStateChange?: (
244
+ state: PaymentInteractionState,
245
+ detail?: { sessionId?: string },
246
+ ) => void,
247
+ ): Promise<PaymentResult> {
248
+ const immediate = mapReversalResult(view, orderRef);
249
+ if (immediate.status !== SdkPaymentStatus.PENDING) {
250
+ return immediate;
251
+ }
252
+
253
+ if (requiresCardTap) {
254
+ onStateChange?.(InteractionState.REQUIRES_INPUT, {
255
+ sessionId: view.sessionId,
256
+ });
257
+ }
258
+
259
+ const terminal = await this.pollUntilTerminal(
260
+ view.clientToken,
261
+ orderRef,
262
+ signal,
263
+ NetsSessionStrategy.POLLING_DURATION_MS,
264
+ mapReversalResult,
265
+ );
266
+
267
+ return terminal ?? pendingResult(orderRef);
268
+ }
269
+
270
+ private async pollUntilTerminal(
271
+ clientToken: string,
272
+ orderRef: string,
273
+ signal: AbortSignal | undefined,
274
+ durationMs: number,
275
+ map: (
276
+ data: PaymentSessionViewDto,
277
+ orderId: string,
278
+ ) => PaymentResult = mapSessionResult,
279
+ ): Promise<PaymentResult | undefined> {
280
+ const deadline = Date.now() + durationMs;
281
+
282
+ while (Date.now() < deadline) {
283
+ if (signal?.aborted) {
284
+ throw new Error("Aborted");
285
+ }
286
+
287
+ let data: PaymentSessionViewDto | undefined;
288
+ try {
289
+ data = (await this.sessionApi.getPaymentSession(clientToken)).data;
290
+ } catch (error) {
291
+ if (error instanceof Error && error.message === "Aborted") throw error;
292
+ }
293
+
294
+ if (signal?.aborted) {
295
+ throw new Error("Aborted");
296
+ }
297
+
298
+ if (data) {
299
+ const result = map(data, orderRef);
300
+ if (result.status !== SdkPaymentStatus.PENDING) {
301
+ return result;
302
+ }
303
+ }
304
+
305
+ await this.delay(NetsSessionStrategy.POLLING_INTERVAL_MS, signal);
306
+ }
307
+
308
+ return undefined;
309
+ }
310
+
311
+ private waitForCompletion(
312
+ clientToken: string,
313
+ sessionId: string,
314
+ request: PaymentRequest,
315
+ signal: AbortSignal,
316
+ ): Promise<PaymentResult | undefined> {
317
+ const channelName = `nets.requests.${sessionId}`;
318
+
319
+ return new Promise((resolve, reject) => {
320
+ let settled = false;
321
+
322
+ const cleanup = () => {
323
+ settled = true;
324
+ if (typeof unsubscribe === "function") unsubscribe();
325
+ clearTimeout(pollTimer);
326
+ signal.removeEventListener("abort", onAbort);
327
+ };
328
+
329
+ const onAbort = () => {
330
+ cleanup();
331
+ reject(new Error("Aborted"));
332
+ };
333
+ signal.addEventListener("abort", onAbort);
334
+
335
+ const unsubscribe = this.messaging.subscribe<PaymentStatusDto>(
336
+ channelName,
337
+ PaymentEventType.StatusChanged,
338
+ (data: PaymentStatusDto) => {
339
+ if (settled) return;
340
+ if (data.status === SimplePaymentStatus.Pending) return;
341
+ cleanup();
342
+ resolve(this.mapStatusDto(data, request));
343
+ },
344
+ );
345
+
346
+ const pollTimer = setTimeout(() => {
347
+ if (settled) return;
348
+ this.pollUntilTerminal(
349
+ clientToken,
350
+ request.orderRef,
351
+ signal,
352
+ NetsSessionStrategy.POLLING_DURATION_MS,
353
+ ).then(
354
+ (result) => {
355
+ if (settled) return;
356
+ cleanup();
357
+ resolve(result);
358
+ },
359
+ (error) => {
360
+ if (settled) return;
361
+ cleanup();
362
+ reject(error);
363
+ },
364
+ );
365
+ }, NetsSessionStrategy.INITIAL_POLL_DELAY_MS);
366
+ });
367
+ }
368
+
369
+ private mapStatusDto(
370
+ data: PaymentStatusDto,
371
+ request: PaymentRequest,
372
+ ): PaymentResult {
373
+ const isSuccess = data.status === SimplePaymentStatus.Success;
374
+
375
+ const result: PaymentResult = {
376
+ success: isSuccess,
377
+ status: isSuccess ? SdkPaymentStatus.SUCCESS : SdkPaymentStatus.FAILED,
378
+ orderId: request.orderRef,
379
+ };
380
+
381
+ if (data.transactionId) {
382
+ result.transactionId = data.transactionId;
383
+ }
384
+ if (data.transaction) {
385
+ result.transaction = data.transaction as unknown as TransactionDto;
386
+ }
387
+ if (data.error?.code) {
388
+ result.errorCode = data.error.code;
389
+ }
390
+ if (data.error?.message) {
391
+ result.errorMessage = data.error.message;
392
+ }
393
+
394
+ return result;
395
+ }
396
+
397
+ private finalize(result: PaymentResult): PaymentResult {
398
+ if (result.status !== SdkPaymentStatus.PENDING) {
399
+ this.resetIdempotencyKey();
400
+ }
401
+ return result;
402
+ }
403
+
404
+ private resetIdempotencyKey(): void {
405
+ this.idempotencyKey = null;
406
+ this.idempotencyOrderRef = null;
407
+ }
408
+
409
+ private armCancel(clientToken: string): Promise<void> {
410
+ return this.sessionApi.cancelSession(clientToken).then(
411
+ () => undefined,
412
+ () => undefined,
413
+ );
414
+ }
415
+
416
+ private generateIdempotencyKey(): string {
417
+ const cryptoRef = (globalThis as { crypto?: { randomUUID?: () => string } })
418
+ .crypto;
419
+
420
+ if (cryptoRef?.randomUUID) {
421
+ return cryptoRef.randomUUID();
422
+ }
423
+
424
+ return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (char) => {
425
+ const random = (Math.random() * 16) | 0;
426
+ const value = char === "x" ? random : (random & 0x3) | 0x8;
427
+ return value.toString(16);
428
+ });
429
+ }
430
+
431
+ private delay(ms: number, signal?: AbortSignal): Promise<void> {
432
+ return new Promise((resolve) => {
433
+ const onAbort = () => {
434
+ clearTimeout(timeout);
435
+ resolve();
436
+ };
437
+ if (signal) {
438
+ signal.addEventListener("abort", onAbort, { once: true });
439
+ }
440
+ const timeout = setTimeout(() => {
441
+ if (signal) {
442
+ signal.removeEventListener("abort", onAbort);
443
+ }
444
+ resolve();
445
+ }, ms);
446
+ });
447
+ }
448
+ }
@@ -111,6 +111,7 @@ export class VivaSessionStrategy implements IPaymentStrategy {
111
111
  async verifyFinalStatus(
112
112
  request: PaymentRequest,
113
113
  _sessionId: string,
114
+ timeoutMs?: number,
114
115
  ): Promise<PaymentResult> {
115
116
  if (!this.currentClientToken) {
116
117
  throw new PaymentSDKError(
@@ -123,7 +124,7 @@ export class VivaSessionStrategy implements IPaymentStrategy {
123
124
  this.currentClientToken,
124
125
  request,
125
126
  undefined,
126
- VivaSessionStrategy.VERIFY_DURATION_MS,
127
+ timeoutMs ?? VivaSessionStrategy.VERIFY_DURATION_MS,
127
128
  );
128
129
 
129
130
  return this.finalize(terminal ?? pendingResult(request.orderRef));
@@ -137,7 +138,7 @@ export class VivaSessionStrategy implements IPaymentStrategy {
137
138
  }
138
139
 
139
140
  try {
140
- await this.sessionApi.cancelVivaSession(this.currentClientToken);
141
+ await this.sessionApi.cancelSession(this.currentClientToken);
141
142
  } catch (error) {
142
143
  throw new PaymentSDKError(
143
144
  PaymentErrorCode.NETWORK_ERROR,
@@ -147,6 +148,7 @@ export class VivaSessionStrategy implements IPaymentStrategy {
147
148
  }
148
149
 
149
150
  this.abortController?.abort();
151
+ this.resetIdempotencyKey();
150
152
  return true;
151
153
  }
152
154
 
@@ -353,14 +355,18 @@ export class VivaSessionStrategy implements IPaymentStrategy {
353
355
 
354
356
  private finalize(result: PaymentResult): PaymentResult {
355
357
  if (result.status !== SdkPaymentStatus.PENDING) {
356
- this.idempotencyKey = null;
357
- this.idempotencyOrderRef = null;
358
+ this.resetIdempotencyKey();
358
359
  }
359
360
  return result;
360
361
  }
361
362
 
363
+ private resetIdempotencyKey(): void {
364
+ this.idempotencyKey = null;
365
+ this.idempotencyOrderRef = null;
366
+ }
367
+
362
368
  private armCancel(clientToken: string): Promise<void> {
363
- return this.sessionApi.cancelVivaSession(clientToken).then(
369
+ return this.sessionApi.cancelSession(clientToken).then(
364
370
  () => undefined,
365
371
  () => undefined,
366
372
  );