teamdigitale/italia-app

View on GitHub
ts/screens/wallet/AddPaymentMethodScreen.tsx

Summary

Maintainability
B
4 hrs
Test Coverage
import { FooterWithButtons, VSpacer } from "@pagopa/io-app-design-system";
import { AmountInEuroCents, RptId } from "@pagopa/io-pagopa-commons/lib/pagopa";
import * as pot from "@pagopa/ts-commons/lib/pot";
import { Route, useRoute } from "@react-navigation/native";
import * as O from "fp-ts/lib/Option";
import * as React from "react";
import { ScrollView, View } from "react-native";
import { connect } from "react-redux";
import { PaymentRequestsGetResponse } from "../../../definitions/backend/PaymentRequestsGetResponse";
import BpayLogo from "../../../img/wallet/payment-methods/bancomat_pay.svg";
import CreditCard from "../../../img/wallet/payment-methods/creditcard.svg";
import PaypalLogo from "../../../img/wallet/payment-methods/paypal/paypal_logo.svg";
import { H1 } from "../../components/core/typography/H1";
import { IOStyles } from "../../components/core/variables/IOStyles";
import BaseScreenComponent, {
  ContextualHelpPropsMarkdown
} from "../../components/screens/BaseScreenComponent";
import PaymentBannerComponent from "../../components/wallet/PaymentBannerComponent";
import PaymentMethodsList, {
  IPaymentMethod
} from "../../components/wallet/PaymentMethodsList";
import { walletAddBancomatStart } from "../../features/wallet/onboarding/bancomat/store/actions";
import { walletAddBPayStart } from "../../features/wallet/onboarding/bancomatPay/store/actions";
import {
  OnOnboardingCompleted,
  walletAddPaypalStart
} from "../../features/wallet/onboarding/paypal/store/actions";
import I18n from "../../i18n";
import {
  navigateBack,
  navigateToWalletAddCreditCard
} from "../../store/actions/navigation";
import { Dispatch } from "../../store/actions/types";
import {
  bancomatPayConfigSelector,
  isPaypalEnabledSelector
} from "../../store/reducers/backendStatus";
import { GlobalState } from "../../store/reducers/types";
import { paypalSelector } from "../../store/reducers/wallet/wallets";
import { AsyncAlert } from "../../utils/asyncAlert";
import { isTestEnv } from "../../utils/environment";

export type AddPaymentMethodScreenNavigationParams = Readonly<{
  inPayment: O.Option<{
    rptId: RptId;
    initialAmount: AmountInEuroCents;
    verifica: PaymentRequestsGetResponse;
    idPayment: string;
  }>;
  // if set, only those methods that can pay with pagoPA will be shown
  showOnlyPayablePaymentMethods?: true;
  keyFrom?: string;
}>;

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

const contextualHelpMarkdown: ContextualHelpPropsMarkdown = {
  title: "wallet.newPaymentMethod.contextualHelpTitle",
  body: "wallet.newPaymentMethod.contextualHelpContent"
};

const getPaymentMethods = (
  props: Props,
  options: {
    onlyPaymentMethodCanPay: boolean;
    isPaymentOnGoing: boolean;
    isPaypalEnabled: boolean;
    canOnboardBPay: boolean;
  },
  navigateToAddCreditCard: () => void
): ReadonlyArray<IPaymentMethod> => [
  {
    name: I18n.t("wallet.methods.card.name"),
    description: I18n.t("wallet.methods.card.description"),
    icon: CreditCard,
    onPress: navigateToAddCreditCard,
    status: "implemented",
    section: "credit_card"
  },
  {
    name: I18n.t("wallet.methods.paypal.name"),
    description: I18n.t("wallet.methods.paypal.description"),
    icon: PaypalLogo,
    onPress: options.isPaypalEnabled
      ? async () => {
          const startPaypalOnboarding = () =>
            props.startPaypalOnboarding(
              options.isPaymentOnGoing ? "back" : "payment_method_details"
            );
          if (props.isPaypalAlreadyAdded) {
            await AsyncAlert(
              I18n.t("wallet.onboarding.paypal.paypalAlreadyAdded.alert.title"),
              I18n.t(
                "wallet.onboarding.paypal.paypalAlreadyAdded.alert.message"
              ),
              [
                {
                  text: I18n.t("global.buttons.continue"),
                  style: "default",
                  onPress: startPaypalOnboarding
                },
                {
                  text: I18n.t("global.buttons.cancel"),
                  style: "cancel"
                }
              ]
            );
            return;
          }
          startPaypalOnboarding();
        }
      : undefined,
    status: options.isPaypalEnabled ? "implemented" : "notImplemented",
    section: "paypal"
  },
  {
    name: I18n.t("wallet.methods.bancomatPay.name"),
    description: I18n.t("wallet.methods.bancomatPay.description"),
    icon: BpayLogo,
    status: options.canOnboardBPay ? "implemented" : "notImplemented",
    onPress: props.startBPayOnboarding,
    section: "digital_payments"
  }
];

/**
 * This is the screen presented to the user
 * when they request adding a new payment method.
 * From here, they can select their payment method
 * of choice (although only credit cards will be allowed
 * initially).
 *
 * This screen allows also to add a new payment method after a transaction is identified.
 *
 * The header banner provides a summary on the transaction to perform.
 *
 * Keep in mind that the rest of the "add credit card" process
 * is handled @https://www.pivotaltracker.com/story/show/157838293
 */
const AddPaymentMethodScreen: React.FunctionComponent<Props> = (
  props: Props
) => {
  const { inPayment, showOnlyPayablePaymentMethods, keyFrom } =
    useRoute<
      Route<"WALLET_ADD_PAYMENT_METHOD", AddPaymentMethodScreenNavigationParams>
    >().params;

  const navigateToAddCreditCard = () =>
    navigateToWalletAddCreditCard({
      inPayment,
      keyFrom
    });

  const buttonLabel = O.isSome(inPayment)
    ? I18n.t("global.buttons.back")
    : I18n.t("global.buttons.cancel");

  return (
    <BaseScreenComponent
      goBack={true}
      contextualHelpMarkdown={contextualHelpMarkdown}
      faqCategories={["wallet", "wallet_methods"]}
      headerTitle={
        O.isSome(inPayment)
          ? I18n.t("wallet.payWith.header")
          : I18n.t("wallet.addPaymentMethodTitle")
      }
    >
      <ScrollView contentContainerStyle={{ flexGrow: 1 }}>
        {O.isSome(inPayment) ? (
          <>
            <PaymentBannerComponent
              paymentReason={inPayment.value.verifica.causaleVersamento}
              currentAmount={inPayment.value.verifica.importoSingoloVersamento}
            />
            <View style={IOStyles.horizontalContentPadding}>
              <VSpacer size={24} />
              <H1>{I18n.t("wallet.payWith.title")}</H1>
              <VSpacer size={16} />
              {/* since we're paying show only those method can pay with pagoPA */}
              <PaymentMethodsList
                paymentMethods={getPaymentMethods(
                  props,
                  {
                    onlyPaymentMethodCanPay: true,
                    isPaymentOnGoing: O.isSome(inPayment),
                    isPaypalEnabled: props.isPaypalEnabled,
                    // can onboard bpay only when both FF are enabled
                    canOnboardBPay: props.canOnboardBPay && props.canPayWithBPay
                  },
                  navigateToAddCreditCard
                )}
              />
            </View>
          </>
        ) : (
          <PaymentMethodsList
            paymentMethods={getPaymentMethods(
              props,
              {
                onlyPaymentMethodCanPay: showOnlyPayablePaymentMethods === true,
                isPaymentOnGoing: O.isSome(inPayment),
                isPaypalEnabled: props.isPaypalEnabled,
                canOnboardBPay: props.canOnboardBPay
              },
              navigateToAddCreditCard
            )}
          />
        )}
      </ScrollView>
      <FooterWithButtons
        type="SingleButton"
        primary={{
          type: "Outline",
          buttonProps: {
            onPress: props.navigateBack,
            accessibilityLabel: buttonLabel,
            label: buttonLabel
          }
        }}
      />
    </BaseScreenComponent>
  );
};

const mapDispatchToProps = (dispatch: Dispatch) => ({
  navigateBack: () => navigateBack(),
  startBPayOnboarding: () => dispatch(walletAddBPayStart()),
  startPaypalOnboarding: (onOboardingCompleted: OnOnboardingCompleted) =>
    dispatch(walletAddPaypalStart(onOboardingCompleted)),
  startAddBancomat: () => dispatch(walletAddBancomatStart())
});

const mapStateToProps = (state: GlobalState) => {
  const bpayConfig = bancomatPayConfigSelector(state);
  return {
    isPaypalAlreadyAdded: pot.isSome(paypalSelector(state)),
    isPaypalEnabled: isPaypalEnabledSelector(state),
    canOnboardBPay: bpayConfig.onboarding,
    canPayWithBPay: bpayConfig.payment
  };
};

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

// to keep solid code encapsulation
export const testableFunctions = {
  getPaymentMethods: isTestEnv ? getPaymentMethods : undefined
};