@munchi_oy/payments 1.4.8 → 1.5.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.
@@ -3,7 +3,9 @@ import type { AxiosInstance } from "axios";
3
3
  import { version } from "../package.json";
4
4
  import { PaymentErrorCode, PaymentSDKError } from "./error";
5
5
  import type { IPaymentStrategy } from "./strategies/IPaymentStrategy";
6
+ import { StrategyExecutionMode } from "./strategies/IPaymentStrategy";
6
7
  import { NetsStrategy } from "./strategies/NetsStrategy";
8
+ import { VivaAppToAppStrategy } from "./strategies/VivaAppToAppStrategy";
7
9
  import { VivaStrategy } from "./strategies/VivaStrategy";
8
10
  import { WorldlineStrategy } from "./strategies/WorldlineStrategy";
9
11
  import {
@@ -17,7 +19,7 @@ import {
17
19
  SdkPaymentStatus,
18
20
  type TransactionOptions,
19
21
  } from "./types/payment";
20
- import type { ILogger, SDKOptions } from "./types/sdk";
22
+ import type { AppToAppConfig, ILogger, SDKOptions } from "./types/sdk";
21
23
 
22
24
  type StateListener = (state: PaymentInteractionState) => void;
23
25
 
@@ -33,6 +35,7 @@ export class MunchiPaymentSDK implements IMunchiPaymentSDK {
33
35
  private _currentSessionId: string | undefined;
34
36
  private _autoResetTimer: ReturnType<typeof setTimeout> | undefined;
35
37
  private autoResetOptions: SDKOptions["autoResetOnPaymentComplete"];
38
+ private appToAppConfig: AppToAppConfig | undefined;
36
39
 
37
40
  private static readonly TERMINAL_STATES = [
38
41
  PaymentInteractionState.SUCCESS,
@@ -57,6 +60,7 @@ export class MunchiPaymentSDK implements IMunchiPaymentSDK {
57
60
  this.logger = options.logger;
58
61
  this.timeoutMs = options.timeoutMs || 30000;
59
62
  this.autoResetOptions = options.autoResetOnPaymentComplete;
63
+ this.appToAppConfig = options.appToApp;
60
64
  this.strategy = strategy ?? this.resolveStrategy(config);
61
65
  }
62
66
 
@@ -187,6 +191,16 @@ export class MunchiPaymentSDK implements IMunchiPaymentSDK {
187
191
  return new NetsStrategy(this.axios, this.messaging, config);
188
192
  case PaymentProvider.Worldline:
189
193
  return new WorldlineStrategy(this.axios, this.messaging, config);
194
+ case PaymentProvider.Viva:
195
+ if (this.appToAppConfig?.enabled) {
196
+ return new VivaAppToAppStrategy(
197
+ this.axios,
198
+ this.messaging,
199
+ config,
200
+ this.appToAppConfig,
201
+ );
202
+ }
203
+ return new VivaStrategy(this.axios, this.messaging, config);
190
204
 
191
205
  default:
192
206
  return new VivaStrategy(this.axios, this.messaging, config);
@@ -245,19 +259,25 @@ export class MunchiPaymentSDK implements IMunchiPaymentSDK {
245
259
  params,
246
260
  internalStateCallback,
247
261
  );
248
-
249
- const timeoutPromise = new Promise<never>((_, reject) => {
250
- setTimeout(() => {
251
- reject(
252
- new PaymentSDKError(
253
- PaymentErrorCode.TIMEOUT,
254
- "Transaction timed out",
255
- ),
256
- );
257
- }, this.timeoutMs);
258
- });
259
-
260
- const result = await Promise.race([transactionPromise, timeoutPromise]);
262
+ const executionMode =
263
+ this.strategy.getExecutionMode?.() ?? StrategyExecutionMode.Managed;
264
+
265
+ const result =
266
+ executionMode === StrategyExecutionMode.CallbackDriven
267
+ ? await transactionPromise
268
+ : await Promise.race([
269
+ transactionPromise,
270
+ new Promise<never>((_, reject) => {
271
+ setTimeout(() => {
272
+ reject(
273
+ new PaymentSDKError(
274
+ PaymentErrorCode.TIMEOUT,
275
+ "Transaction timed out",
276
+ ),
277
+ );
278
+ }, this.timeoutMs);
279
+ }),
280
+ ]);
261
281
 
262
282
  if (result.success) {
263
283
  this.transitionTo(PaymentInteractionState.SUCCESS);
@@ -551,6 +571,7 @@ export class MunchiPaymentSDK implements IMunchiPaymentSDK {
551
571
  this.logger?.info("Initiating refund", { orderRef: params.orderRef });
552
572
  // Clear any stale session before starting a new refund flow.
553
573
  this._currentSessionId = undefined;
574
+ this._cancellationIntent = false;
554
575
 
555
576
  // Ensure we are in a valid state to start a refund
556
577
  const isRestingState = MunchiPaymentSDK.RESTING_STATES.includes(
@@ -589,6 +610,17 @@ export class MunchiPaymentSDK implements IMunchiPaymentSDK {
589
610
  if (result.success) {
590
611
  this.transitionTo(PaymentInteractionState.SUCCESS);
591
612
  this.safeFireCallback(() => callbacks.onSuccess?.(result));
613
+ } else if (
614
+ this._cancellationIntent ||
615
+ result.status === SdkPaymentStatus.CANCELLED
616
+ ) {
617
+ this.transitionTo(PaymentInteractionState.FAILED);
618
+ this.safeFireCallback(() =>
619
+ callbacks.onCancelled?.({
620
+ orderRef: params.orderRef,
621
+ refPaymentId: this._currentSessionId,
622
+ }),
623
+ );
592
624
  } else {
593
625
  this.transitionTo(PaymentInteractionState.FAILED);
594
626
  this.safeFireCallback(() => callbacks.onError?.(result));
@@ -602,6 +634,61 @@ export class MunchiPaymentSDK implements IMunchiPaymentSDK {
602
634
  } catch (error) {
603
635
  this.logger?.error("Refund failed", error);
604
636
 
637
+ if (this._cancellationIntent) {
638
+ try {
639
+ if (this._currentSessionId) {
640
+ const finalStatus = await this.verifyWithRetry(
641
+ params,
642
+ this._currentSessionId,
643
+ );
644
+
645
+ if (finalStatus.success) {
646
+ this.transitionTo(PaymentInteractionState.SUCCESS);
647
+ this.safeFireCallback(() => callbacks.onSuccess?.(finalStatus));
648
+ return finalStatus;
649
+ }
650
+
651
+ if (finalStatus.status === SdkPaymentStatus.CANCELLED) {
652
+ this.transitionTo(PaymentInteractionState.FAILED);
653
+ this.safeFireCallback(() =>
654
+ callbacks.onCancelled?.({
655
+ orderRef: params.orderRef,
656
+ refPaymentId: this._currentSessionId,
657
+ }),
658
+ );
659
+ return finalStatus;
660
+ }
661
+
662
+ this.transitionTo(PaymentInteractionState.FAILED);
663
+ this.safeFireCallback(() => callbacks.onError?.(finalStatus));
664
+ return finalStatus;
665
+ }
666
+ } catch (verifyError) {
667
+ this.logger?.warn("Refund final status verification failed", {
668
+ verifyError,
669
+ });
670
+ }
671
+
672
+ this.transitionTo(PaymentInteractionState.FAILED);
673
+
674
+ this.safeFireCallback(() =>
675
+ callbacks.onCancelled?.({
676
+ orderRef: params.orderRef,
677
+ refPaymentId: this._currentSessionId,
678
+ }),
679
+ );
680
+
681
+ return {
682
+ success: false,
683
+ status: SdkPaymentStatus.CANCELLED,
684
+ errorCode: this.normalizeErrorCode(PaymentErrorCode.CANCELLED),
685
+ orderId: params.orderRef,
686
+ ...(this._currentSessionId
687
+ ? { transactionId: this._currentSessionId }
688
+ : {}),
689
+ };
690
+ }
691
+
605
692
  this.transitionTo(PaymentInteractionState.FAILED);
606
693
 
607
694
  const errorResult = this.generateErrorResult(
@@ -5,7 +5,13 @@ import type {
5
5
  RefundRequest,
6
6
  } from "../types/payment";
7
7
 
8
+ export enum StrategyExecutionMode {
9
+ Managed = "managed",
10
+ CallbackDriven = "callback_driven",
11
+ }
12
+
8
13
  export interface IPaymentStrategy {
14
+ getExecutionMode?(): StrategyExecutionMode;
9
15
 
10
16
  processPayment(
11
17
  request: PaymentRequest,