teamdigitale/italia-app

View on GitHub
ts/features/payments/checkout/saga/networking/handleWalletPaymentCreateTransaction.ts

Summary

Maintainability
A
2 hrs
Test Coverage
import * as E from "fp-ts/lib/Either";
import { put, select } from "typed-redux-saga/macro";
import { ActionType } from "typesafe-actions";
import { IOToast } from "@pagopa/io-app-design-system";
import { getGenericError, getNetworkError } from "../../../../../utils/errors";
import { PaymentClient } from "../../../common/api/client";
import { paymentsCreateTransactionAction } from "../../store/actions/networking";
import { withPaymentsSessionToken } from "../../../common/utils/withPaymentsSessionToken";
import { paymentAnalyticsDataSelector } from "../../../history/store/selectors";
import I18n from "../../../../../i18n";
import * as analytics from "../../analytics";
import { readablePrivacyReport } from "../../../../../utils/reporters";

const handleError = (
  paymentAnalyticsData: ReturnType<typeof paymentAnalyticsDataSelector>,
  onErrorCallback?: () => void
) => {
  IOToast.error(I18n.t("features.payments.errors.transactionCreationError"));
  analytics.trackPaymentMethodVerificaFatalError({
    organization_name: paymentAnalyticsData?.verifiedData?.paName,
    organization_fiscal_code: paymentAnalyticsData?.verifiedData?.paFiscalCode,
    service_name: paymentAnalyticsData?.serviceName,
    attempt: paymentAnalyticsData?.attempt,
    expiration_date: paymentAnalyticsData?.verifiedData?.dueDate
  });
  if (onErrorCallback) {
    onErrorCallback();
  }
};

export function* handleWalletPaymentCreateTransaction(
  newTransaction: PaymentClient["newTransactionForIO"],
  action: ActionType<(typeof paymentsCreateTransactionAction)["request"]>
) {
  const paymentAnalyticsData = yield* select(paymentAnalyticsDataSelector);
  try {
    const newTransactionResult = yield* withPaymentsSessionToken(
      newTransaction,
      action,
      {
        body: action.payload.data
      },
      "pagoPAPlatformSessionToken"
    );

    if (E.isLeft(newTransactionResult)) {
      handleError(paymentAnalyticsData, action.payload.onError);
      yield* put(
        paymentsCreateTransactionAction.failure({
          ...getGenericError(
            new Error(readablePrivacyReport(newTransactionResult.left))
          )
        })
      );
      return;
    }
    const status = newTransactionResult.right.status;
    if (status === 200) {
      yield* put(
        paymentsCreateTransactionAction.success(
          newTransactionResult.right.value
        )
      );
    } else if (status === 400) {
      // Handling unhandled error from third-party services (GEC) during payment verification.
      // This is not an internal backend error from pagoPA, but rather a third-party service error and should be handled differently.
      handleError(paymentAnalyticsData, action.payload.onError);
      yield* put(
        paymentsCreateTransactionAction.failure({
          ...getGenericError(
            new Error(`Error: ${newTransactionResult.right.status}`)
          )
        })
      );
    } else if (status !== 401) {
      // The 401 status is handled by the withPaymentsSessionToken
      handleError(paymentAnalyticsData, action.payload.onError);
      yield* put(
        paymentsCreateTransactionAction.failure(
          newTransactionResult.right.value
        )
      );
    }
  } catch (e) {
    handleError(paymentAnalyticsData, action.payload.onError);
    yield* put(
      paymentsCreateTransactionAction.failure({ ...getNetworkError(e) })
    );
  }
}