teamdigitale/italia-app

View on GitHub
ts/features/wallet/onboarding/paypal/screen/PayPalOnboardingCheckoutScreen.tsx

Summary

Maintainability
B
5 hrs
Test Coverage
import * as O from "fp-ts/lib/Option";
import React, { useEffect, useState } from "react";
import { Alert } from "react-native";
import { connect } from "react-redux";
import { Dispatch } from "redux";
import WorkunitGenericFailure from "../../../../../components/error/WorkunitGenericFailure";
import BaseScreenComponent from "../../../../../components/screens/BaseScreenComponent";
import { PayWebViewModal } from "../../../../../components/wallet/PayWebViewModal";
import {
  pagoPaApiUrlPrefix,
  pagoPaApiUrlPrefixTest
} from "../../../../../config";
import I18n from "../../../../../i18n";
import { isPagoPATestEnabledSelector } from "../../../../../store/reducers/persistedPreferences";
import { GlobalState } from "../../../../../store/reducers/types";
import { pmSessionTokenSelector } from "../../../../../store/reducers/wallet/payment";
import { emptyContextualHelp } from "../../../../../utils/emptyContextualHelp";
import { getLocalePrimaryWithFallback } from "../../../../../utils/locale";
import { getLookUpIdPO } from "../../../../../utils/pmLookUpId";
import { LoadingErrorComponent } from "../../../../../components/LoadingErrorComponent";
import { fold } from "../../../../../common/model/RemoteValue";
import {
  walletAddPaypalBack,
  walletAddPaypalOutcome,
  walletAddPaypalRefreshPMToken
} from "../store/actions";
import { paypalOnboardingSelectedPsp } from "../store/reducers/selectedPsp";
import PAYPAL_ROUTES from "../navigation/routes";
import { useIONavigation } from "../../../../../navigation/params/AppParamsList";
import ROUTES from "../../../../../navigation/routes";

type Props = ReturnType<typeof mapDispatchToProps> &
  ReturnType<typeof mapStateToProps>;

const payUrlSuffix = "/v3/webview/paypal/onboarding/psp";
const webViewExitPathName = "/v3/webview/logout/bye";
const webViewOutcomeParamName = "outcome";

const LoadingOrError = (loadingProps: {
  hasError: boolean;
  onRetry: () => void;
}) => (
  <LoadingErrorComponent
    testID={"PayPalOnboardingCheckoutScreenLoadingError"}
    isLoading={!loadingProps.hasError}
    loadingCaption={I18n.t("global.remoteStates.loading")}
    onRetry={loadingProps.onRetry}
  />
);

const CheckoutContent = (
  props: Props & {
    onCheckoutCompleted: (outcode: O.Option<string>) => void;
    onGoBack: () => void;
  }
) => {
  const [isOnboardingCompleted, setIsOnboardingComplete] = useState(false);
  const urlPrefix = props.isPagoPATestEnabled
    ? pagoPaApiUrlPrefixTest
    : pagoPaApiUrlPrefix;
  return fold(
    props.pmToken,
    () => <LoadingOrError hasError={false} onRetry={props.refreshPMtoken} />,
    () => <LoadingOrError hasError={false} onRetry={props.refreshPMtoken} />,
    sessionToken => {
      // it should not never happen since this screen is just after the psp selection
      if (props.pspSelected === null) {
        return <WorkunitGenericFailure />;
      }
      // we have all we need to starts the checkout into the webview
      const formData = {
        idPsp: props.pspSelected.id,
        language: getLocalePrimaryWithFallback(),
        sessionToken,
        ...getLookUpIdPO()
      };
      return (
        <PayWebViewModal
          testID={"PayWebViewModalTestID"}
          showInfoHeader={false}
          postUri={urlPrefix + payUrlSuffix}
          formData={formData}
          isVisible={!isOnboardingCompleted}
          finishPathName={webViewExitPathName}
          onFinish={outcomeCode => {
            setIsOnboardingComplete(true);
            props.onCheckoutCompleted(outcomeCode);
          }}
          outcomeQueryparamName={webViewOutcomeParamName}
          onGoBack={props.onGoBack}
          modalHeaderTitle={I18n.t("wallet.onboarding.paypal.headerTitle")}
        />
      );
    },
    _ => <LoadingOrError hasError={true} onRetry={props.refreshPMtoken} />
  );
};

/**
 * This screen includes a webview where the paypal checkout happens. This flow is external to IO, it happens in the Payment Manager
 * As first step it asks for a fresh token from the Payment Manager, it will be included in the webview
 * 1. request for a fresh PM token
 * 2. when the PM token is obtained, starts the checkout challenge in the webview
 * 3. handle the outcome code coming from the step 2
 * 4. navigate to the checkout completed screen
 */
const PayPalOnboardingCheckoutScreen = (props: Props) => {
  const navigation = useIONavigation();
  const { refreshPMtoken } = props;
  // refresh the PM at the startup
  useEffect(() => {
    refreshPMtoken();
  }, [refreshPMtoken]);

  const handleCheckoutCompleted = (outcomeCode: O.Option<string>) => {
    props.setOutcomeCode(outcomeCode);
    navigation.navigate(ROUTES.WALLET_NAVIGATOR, {
      screen: PAYPAL_ROUTES.ONBOARDING.MAIN,
      params: {
        screen: PAYPAL_ROUTES.ONBOARDING.CHECKOUT_COMPLETED
      }
    });
  };

  // notify the user that the current onboarding operation will be interrupted
  const handleBack = () => {
    Alert.alert(I18n.t("wallet.abortWebView.title"), "", [
      {
        text: I18n.t("wallet.abortWebView.confirm"),
        onPress: props.goBack,
        style: "cancel"
      },
      {
        text: I18n.t("wallet.abortWebView.cancel")
      }
    ]);
  };

  return (
    <BaseScreenComponent
      backButtonTestID={"host-back-button"}
      goBack={handleBack}
      headerTitle={I18n.t("wallet.onboarding.paypal.headerTitle")}
      contextualHelp={emptyContextualHelp}
    >
      <CheckoutContent
        {...props}
        onGoBack={handleBack}
        onCheckoutCompleted={handleCheckoutCompleted}
      />
    </BaseScreenComponent>
  );
};

const mapDispatchToProps = (dispatch: Dispatch) => ({
  goBack: () => dispatch(walletAddPaypalBack()),
  setOutcomeCode: (oc: O.Option<string>) =>
    dispatch(walletAddPaypalOutcome(oc)),
  refreshPMtoken: () => dispatch(walletAddPaypalRefreshPMToken.request())
});
const mapStateToProps = (state: GlobalState) => ({
  pmToken: pmSessionTokenSelector(state),
  isPagoPATestEnabled: isPagoPATestEnabledSelector(state),
  pspSelected: paypalOnboardingSelectedPsp(state)
});

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(PayPalOnboardingCheckoutScreen);