teamdigitale/italia-app

View on GitHub
ts/utils/paymentMethod.ts

Summary

Maintainability
D
1 day
Test Coverage
import * as E from "fp-ts/lib/Either";
import * as O from "fp-ts/lib/Option";
import { pipe } from "fp-ts/lib/function";
import { Alert, ImageSourcePropType } from "react-native";
import { Abi } from "../../definitions/pagopa/walletv2/Abi";
import {
  Card,
  ValidityStateEnum
} from "../../definitions/pagopa/walletv2/Card";
import {
  PaymentInstrument,
  ValidityStatusEnum
} from "../../definitions/pagopa/walletv2/PaymentInstrument";
import bPayImage from "../../img/wallet/cards-icons/bPay.png";
import pagoBancomatImage from "../../img/wallet/cards-icons/pagobancomat.png";
import paypalImage from "../../img/wallet/cards-icons/paypal.png";
import {
  cardIcons,
  getCardIconFromBrandLogo
} from "../components/wallet/card/Logo";
import { contentRepoUrl } from "../config";
import I18n from "../i18n";
import { mixpanelTrack } from "../mixpanel";
import { IndexedById } from "../store/helpers/indexer";
import {
  BPayPaymentMethod,
  BancomatPaymentMethod,
  CreditCardPaymentMethod,
  PaymentMethod,
  RawBPayPaymentMethod,
  RawBancomatPaymentMethod,
  RawCreditCardPaymentMethod,
  RawPayPalPaymentMethod,
  RawPaymentMethod,
  isRawBPay,
  isRawBancomat,
  isRawCreditCard,
  isRawPayPal
} from "../types/pagopa";
import { isExpired } from "./dates";
import { getPaypalAccountEmail } from "./paypal";
import { FOUR_UNICODE_CIRCLES } from "./wallet";

export const getPaymentMethodHash = (
  pm: RawPaymentMethod
): string | undefined => {
  switch (pm.kind) {
    case "PayPal":
      return getPaypalAccountEmail(pm.info);
    case "BPay":
      return pm.info.uidHash;
    case "Bancomat":
    case "CreditCard":
      return pm.info.hashPan;
  }
};
export const getTitleFromPaymentInstrument = (
  paymentInstrument: PaymentInstrument
) => `${FOUR_UNICODE_CIRCLES} ${paymentInstrument.panPartialNumber ?? ""}`;

export const getTitleFromCard = (creditCard: RawCreditCardPaymentMethod) =>
  `${FOUR_UNICODE_CIRCLES} ${creditCard.info.blurredNumber ?? ""}`;

export const getBancomatAbiIconUrl = (abi: string) =>
  `${contentRepoUrl}/logos/abi/${abi}.png`;

export const getPspIconUrlFromAbi = (abi: string) =>
  `${contentRepoUrl}/logos/abi/${abi}.png`;

/**
 * Choose an image to represent a {@link RawPaymentMethod}
 * @param paymentMethod
 */
export const getImageFromPaymentMethod = (
  paymentMethod: RawPaymentMethod
): ImageSourcePropType => {
  if (isRawCreditCard(paymentMethod)) {
    return getCardIconFromBrandLogo(paymentMethod.info);
  }
  if (isRawBancomat(paymentMethod)) {
    return pagoBancomatImage;
  }
  if (isRawPayPal(paymentMethod)) {
    return paypalImage;
  }
  if (isRawBPay(paymentMethod)) {
    return bPayImage;
  }
  return cardIcons.UNKNOWN;
};

export const getTitleFromBancomat = (
  bancomatInfo: RawBancomatPaymentMethod,
  abiList: IndexedById<Abi>
) =>
  pipe(
    bancomatInfo.info.issuerAbiCode,
    O.fromNullable,
    O.chain(abiCode => O.fromNullable(abiList[abiCode])),
    O.chain(abi => O.fromNullable(abi.name)),
    O.getOrElse(() => I18n.t("wallet.methods.bancomat.name"))
  );

const getTitleForPaypal = (paypal: RawPayPalPaymentMethod) =>
  getPaypalAccountEmail(paypal.info);
/**
 * Choose a textual representation for a {@link PatchedWalletV2}
 * @param paymentMethod
 * @param abiList
 */
export const getTitleFromPaymentMethod = (
  paymentMethod: RawPaymentMethod,
  abiList: IndexedById<Abi>
) => {
  if (isRawCreditCard(paymentMethod)) {
    return getTitleFromCard(paymentMethod);
  }
  if (isRawBancomat(paymentMethod)) {
    return getTitleFromBancomat(paymentMethod, abiList);
  }
  if (isRawBPay(paymentMethod)) {
    return (
      pipe(
        paymentMethod.info.instituteCode,
        O.fromNullable,
        O.chain(abiCode => O.fromNullable(abiList[abiCode])),
        O.chain(abi => O.fromNullable(abi.name)),
        O.toUndefined
      ) ??
      paymentMethod.info.bankName ??
      I18n.t("wallet.methods.bancomatPay.name")
    );
  }
  if (isRawPayPal(paymentMethod)) {
    return getTitleForPaypal(paymentMethod);
  }
  return FOUR_UNICODE_CIRCLES;
};

export const enhanceBancomat = (
  bancomat: RawBancomatPaymentMethod,
  abiList: IndexedById<Abi>
): BancomatPaymentMethod => ({
  ...bancomat,
  abiInfo: bancomat.info.issuerAbiCode
    ? abiList[bancomat.info.issuerAbiCode]
    : undefined,
  caption: getTitleFromBancomat(bancomat, abiList),
  icon: getImageFromPaymentMethod(bancomat)
});

export const enhanceBPay = (
  rawBPay: RawBPayPaymentMethod,
  abiList: IndexedById<Abi>
): BPayPaymentMethod => ({
  ...rawBPay,
  info: {
    ...rawBPay.info,
    numberObfuscated: `${rawBPay.info.numberObfuscated?.replace(/\*+/g, "●●●")}`
  },
  abiInfo: rawBPay.info.instituteCode
    ? abiList[rawBPay.info.instituteCode]
    : undefined,
  caption: I18n.t("wallet.methods.bancomatPay.name"),
  icon: getImageFromPaymentMethod(rawBPay)
});

export const enhanceCreditCard = (
  rawCreditCard: RawCreditCardPaymentMethod,
  abiList: IndexedById<Abi>
): CreditCardPaymentMethod => ({
  ...rawCreditCard,
  abiInfo: rawCreditCard.info.issuerAbiCode
    ? abiList[rawCreditCard.info.issuerAbiCode]
    : undefined,
  caption: getTitleFromPaymentMethod(rawCreditCard, abiList),
  icon: getImageFromPaymentMethod(rawCreditCard)
});

export const enhancePaymentMethod = (
  pm: RawPaymentMethod,
  abiList: IndexedById<Abi>
): PaymentMethod | null => {
  switch (pm.kind) {
    // bancomat need a special handling, we need to include the abi
    case "Bancomat":
      return enhanceBancomat(pm, abiList);
    case "BPay":
      return enhanceBPay(pm, abiList);
    case "CreditCard":
      return enhanceCreditCard(pm, abiList);
    case "PayPal":
      return {
        ...pm,
        caption: getTitleFromPaymentMethod(pm, abiList),
        icon: getImageFromPaymentMethod(pm)
      };
  }
};

export const isBancomatBlocked = (pan: Card) =>
  pan.validityState === ValidityStateEnum.BR;

export const isCoBadgeBlocked = (pan: PaymentInstrument) =>
  pan.validityStatus === ValidityStatusEnum.BLOCK_REVERSIBLE;

/**
 * Check if the given payment method is expired
 * right(true) if it is expired, right(false) if it is still valid
 * left if expiring date can't be evaluated
 * @param paymentMethod
 */
export const isPaymentMethodExpired = (
  paymentMethod: RawPaymentMethod
): E.Either<Error, boolean> => {
  switch (paymentMethod.kind) {
    case "BPay":
    case "PayPal":
      return E.right(false);
    case "Bancomat":
    case "CreditCard":
      return isExpired(
        paymentMethod.info.expireMonth,
        paymentMethod.info.expireYear
      );
  }
};

// inform the user he/she has no payment methods to pay
export const alertNoPayablePaymentMethods = (
  onContinue: () => void,
  onCancel?: () => void
) => {
  void mixpanelTrack("NO_PAYABLE_METHODS");
  Alert.alert(
    I18n.t("payment.alertNoPaymentMethods.title"),
    I18n.t("payment.alertNoPaymentMethods.message"),
    [
      {
        text: I18n.t("payment.alertNoPaymentMethods.buttons.ko"),
        onPress: onCancel
      },
      {
        text: I18n.t("payment.alertNoPaymentMethods.buttons.ok"),
        onPress: onContinue
      }
    ]
  );
};

// inform the user he/she has some payable payment methods but not one of them is active
export const alertNoActivePayablePaymentMethods = (
  onContinue: () => void,
  onCancel?: () => void
) => {
  void mixpanelTrack("NO_ACTIVE_PAYABLE_METHODS");
  Alert.alert(
    I18n.t("payment.alertNoActivePaymentMethods.title"),
    I18n.t("payment.alertNoActivePaymentMethods.message"),
    [
      {
        text: I18n.t("payment.alertNoActivePaymentMethods.buttons.ko"),
        onPress: onCancel
      },
      {
        text: I18n.t("payment.alertNoActivePaymentMethods.buttons.ok"),
        onPress: onContinue
      }
    ]
  );
};