diff --git a/locales/de/index.yml b/locales/de/index.yml
index 04323fd863e..42f1b34816b 100644
--- a/locales/de/index.yml
+++ b/locales/de/index.yml
@@ -704,7 +704,7 @@ authentication:
description1_2: "Alternativ kannst du die App auch über ein Login mit der"
description1_3: "höchsten Sicherheitsstufe öffnen."
description1_4: "Diese Art der Authentifizierung erfordert einen Benutzernamen, ein Passwort und ein physisches Medium, wie z.B. eine CIE."
- title2: "Wie man den Zugang zu IO entsperrst"
+ title2: "Möchtest du den Zugang zu IO entsperren?"
listitem1: "Stell sicher, dass du deine digitale Identität abgesichert hast, indem du deine Anmeldedaten bei deinem Identitätsanbieter änderst."
listitem2_1: "Greif über das Internet auf IO zu, indem du deine SPID-Anmeldedaten oder deine CIE verwendest."
listitem2_2: "Geh zur Website"
@@ -1598,6 +1598,22 @@ wallet:
title: "Gib die Steuernummer der Körperschaft ein"
subtitle: "Sie hat 11 Ziffern und ist neben dem QR-Code zu finden."
placeholder: "Steuernummer der Körperschaft"
+ abortDialog:
+ title: "Möchtest du den Vorgang abbrechen?"
+ confirm: "Ja, abbrechen"
+ cancel: "Nein, zurück"
+ methodSelection:
+ header: "Zahlungsmethode auswählen"
+ yourMethods: "Deine Zahlungsmethoden"
+ otherMethods: "Andere Zahlungsmethoden"
+ alert:
+ body: "Aufgrund des größeren Betrags sind einige Zahlungsmethoden nicht verfügbar."
+ cta: "OK, habe verstanden!"
+ missingMethodsError:
+ title: "Füge eine Zahlungsmethode für In-App-Zahlungen hinzu"
+ subtitle: "Die Zahlungsmethode wird in deinem Konto gespeichert, so dass du beim nächsten Mal einfacher bezahlen kannst."
+ addMethod: "Zahlungsmethode hinzufügen"
+ notNow: "Jetzt nicht"
psp:
title: "Wähle, von wem die Zahlung abgewickelt werden soll"
description: "Jeder Zahlungsdienstleister (PSP) schlägt eine Provision vor."
@@ -1612,6 +1628,31 @@ wallet:
default: "Standard"
amount: "Nach Betrag"
name: "Nach Name"
+ failure:
+ PAYMENT_UNAVAILABLE:
+ title: "Es gibt ein technisches Problem mit dieser Zahlungsmitteilung."
+ PAYMENT_UNKNOWN:
+ title: "Die Daten der Zahlungsmitteilung sind falsch"
+ DOMAIN_UNKNOWN:
+ title: "Die ausstellende Körperschaft hat Probleme bei der Bearbeitung"
+ subtitle: "Bitte versuche es später noch einmal. Wenn das Problem weiterhin besteht, wende dich bitte an den Support."
+ PAYMENT_ONGOING:
+ title: "Es ist bereits eine Zahlung im Gange, bitte versuche es später noch einmal"
+ subtitle: "Wenn das Problem weiterhin besteht, kannst du ein Ticket erstellen."
+ PAYMENT_EXPIRED:
+ title: "Die Zahlungsmitteilung ist abgelaufen und kann nicht mehr bezahlt werden"
+ subtitle: "Wende dich für weitere Informationen an die Körperschaft."
+ PAYMENT_CANCELED:
+ title: "Die ausstellende Körperschaft hat diese Zahlungsmitteilung zurückgezogen"
+ subtitle: "Wende dich für weitere Informationen an die Körperschaft."
+ PAYMENT_DUPLICATED:
+ title: "Diese Zahlungsmitteilung wurde bereits bezahlt!"
+ GENERIC_ERROR:
+ title: "Ein unerwarteter Fehler ist aufgetreten"
+ subtitle: "Versuche es erneut oder wende dich an den Support."
+ PAA_PAGAMENTO_SCONOSCIUTO:
+ title: "Wir können die Zahlungsmitteilung nicht finden"
+ subtitle: "Die Zahlungsmitteilung kann bereits bezahlt worden sein. Wende dich bitte an den ausstellende Körperschaft."
outcome:
SUCCESS:
title: "Du hast {{amount}} bezahlt"
@@ -1619,7 +1660,44 @@ wallet:
banner:
title: "Kannst du uns sagen, wie es lief?"
content: "Erzähl uns von deinen Erfahrungen mit der Zahlung und hilf uns, uns zu verbessern."
- action: "Weiter zur Umfrage"
+ action: "Zur Umfrage"
+ GENERIC_ERROR:
+ title: "Es ist ein unerwarteter Fehler aufgetreten"
+ subtitle: "Es wurde kein Betrag abgebucht."
+ AUTH_ERROR:
+ title: "Autorisierung verweigert"
+ subtitle: "Es wurde kein Betrag abgebucht.\nVergewissere dich, dass du die Anweisungen deiner Bank oder Zahlungsapp korrekt befolgt hast."
+ INVALID_DATA:
+ title: "Die Daten der Zahlungsmethode sind falsch"
+ subtitle: "Wenn du mit einer Debit- oder Kreditkarte bezahlt hast, musst du die Daten wie auf der Karte angegeben eingeben."
+ TIMEOUT:
+ title: "Die Sitzung ist abgelaufen"
+ subtitle: "Es wurde kein Betrag abgebucht.\nZu deiner Sicherheit hast du nur eine begrenzte Zeit, um den Vorgang abzuschließen."
+ CIRCUIT_ERROR:
+ title: "Das Kartennetzwerk deiner Karte wird nicht unterstützt"
+ MISSING_FIELDS:
+ title: "Es fehlen einige Daten, um mit der Zahlung fortzufahren"
+ INVALID_CARD:
+ title: "Die Zahlungsmethode ist abgelaufen oder nicht mehr gültig"
+ subtitle: "Weitere Informationen erhältst du bei deiner Bank."
+ CANCELED_BY_USER:
+ title: "Der Vorgang wurde abgebrochen"
+ subtitle: "Es wurde kein Betrag abgebucht."
+ EXCESSIVE_AMOUNT:
+ title: "Autorisierung verweigert"
+ subtitle: "Es wurde kein Betrag abgebucht.\nDu hast wahrscheinlich die Obergrenze deiner Zahlungsmethode überschritten."
+ INVALID_METHOD:
+ title: "Die Zahlungsmethode wird nicht unterstützt"
+ support:
+ button: "Support kontaktieren"
+ supportTitle: "Support kontaktieren"
+ phone: "{{phoneNumber}} anrufen"
+ chat: "Im Chat um Hilfe fragen"
+ additionalDataTitle: "Zusätzliche Daten"
+ copyAll: "Alles kopieren"
+ errorCode: "Fehlercode"
+ noticeNumber: "Zahlungskodex"
+ entityCode: "Steuernummer Körperschaft"
saveCard:
saveCard: "Karte speichern"
header: "Möchtest du diese Karte speichern?"
@@ -2063,13 +2141,13 @@ bonus:
goToDetail: "Die Karte anzeigen"
detail:
cta:
- buyers: "CGN Rabatte und Ermäßigungen anzeigen"
+ buyers: "CGN Ermäßigungen anzeigen"
otp: "Code generieren"
eyca:
copy: "EYCA Kartennummer kopieren"
- pending: "EYCA Rabatte und Ermäßigungen anzeigen"
+ pending: "EYCA Ermäßigungen anzeigen"
bottomSheet: "Besuche die EYCA Website"
- showEycaDiscounts: "EYCA Rabatte und Ermäßigungen anzeigen"
+ showEycaDiscounts: "EYCA Ermäßigungen anzeigen"
information:
active: "Die Karte ist aktiv und kann bis zum {{date}} verwendet werden."
warning: "Achtung! "
@@ -2373,6 +2451,7 @@ whatsNew:
bonusCard:
validUntil: "Gültig bis {{endDate}}"
expiring: "Verfällt am {{endDate}}"
+ expired: "Abgelaufen am {{endDate}}"
paused: "In Pause"
removed: "Entfernt"
transaction:
@@ -2381,6 +2460,7 @@ transaction:
totalAmount: "Insgesamt"
totalFee: "Der Gesamtbetrag umfasst "
totalFeePsp: "Provision, berechnet von {{pspName}}"
+ totalFeeNoPsp: "Provision, die vom Transaktionsdienstleister (PSP) erhoben wird."
info:
title: "Informationen zur Transaktion"
pspName: "Zahlungsdienstleister (PSP)"
@@ -2394,7 +2474,7 @@ transaction:
subject: "Zahlungsgrund"
permissionRequest:
gallery:
- title: "Erlaube IO den Zugriff auf deine Fotos"
+ title: "Erlaube IO den Zugriff auf deine Fotos"
subtitle: "Dazu musst du die Rechte in den Einstellungen deines Geräts ändern."
caption: "So geht's:"
step: "Schritt {{step}}"
@@ -2405,4 +2485,8 @@ permissionRequest:
3: "Wähle “IO”"
4: "Wähle den Punkt App-Berechtigungen"
5: "Lass den Zugriff auf Dateien und Medien zu"
+ ios:
+ 1: "Geh zu “Einstellungen”"
+ 2: "Wähle “IO”"
+ 3: "Wähle “Fotos” und erlaube den Zugriff"
cta: "Öffne Einstellungen"
diff --git a/package.json b/package.json
index fd4d0b1467a..8c95c272193 100644
--- a/package.json
+++ b/package.json
@@ -27,8 +27,8 @@
"idpay_api": "https://raw.githubusercontent.com/pagopa/cstar-infrastructure/v6.9.1/src/domains/idpay-app/api/idpay_appio_full/openapi.appio.full.yml",
"lollipop_api": "https://raw.githubusercontent.com/pagopa/io-backend/v13.25.1-RELEASE/api_lollipop_first_consumer.yaml",
"fast_login_api": "https://raw.githubusercontent.com/pagopa/io-backend/v13.25.1-RELEASE/openapi/generated/api_fast_login.yaml",
- "pagopa_api_walletv3": "https://raw.githubusercontent.com/pagopa/pagopa-infra/4cd111e94432ff62580adc391de78a5462a7128e/src/domains/wallet-app/api/payment-wallet/v1/_openapi.json.tpl",
- "pagopa_api_ecommerce": "https://raw.githubusercontent.com/pagopa/pagopa-infra/65878f9913fcc0eaff499ba8a1a20427a412c010/src/domains/ecommerce-app/api/ecommerce-io/v1/_openapi.json.tpl",
+ "pagopa_api_walletv3": "https://raw.githubusercontent.com/pagopa/pagopa-infra/740e7dcc5ea2ea19639316fea6797bbd504dd0ae/src/domains/wallet-app/api/payment-wallet/v1/_openapi.json.tpl",
+ "pagopa_api_ecommerce": "https://raw.githubusercontent.com/pagopa/pagopa-infra/5190135ac34791cf66c1986735d4134bcaf4096f/src/domains/ecommerce-app/api/ecommerce-io/v1/_openapi.json.tpl",
"private": true,
"scripts": {
"start": "react-native start",
diff --git a/ts/config.ts b/ts/config.ts
index a5496e6722c..92ede32ee6c 100644
--- a/ts/config.ts
+++ b/ts/config.ts
@@ -122,7 +122,7 @@ export const remindersOptInEnabled = Config.REMINDERS_OPT_IN_ENABLED === "YES";
export const isNewCduFlow = Config.CDU_NEW_FLOW === "YES";
// version of ToS
-export const tosVersion: NonNegativeNumber = 4.7 as NonNegativeNumber;
+export const tosVersion: NonNegativeNumber = 4.8 as NonNegativeNumber;
export const fetchTimeout = pipe(
parseInt(Config.FETCH_TIMEOUT_MS, 10),
diff --git a/ts/features/walletV3/common/components/WalletDetailsPaymentMethodFeatures.tsx b/ts/features/walletV3/common/components/WalletDetailsPaymentMethodFeatures.tsx
index 959c7e90a41..b3799b6f200 100644
--- a/ts/features/walletV3/common/components/WalletDetailsPaymentMethodFeatures.tsx
+++ b/ts/features/walletV3/common/components/WalletDetailsPaymentMethodFeatures.tsx
@@ -19,7 +19,7 @@ type Props = { paymentMethod: WalletInfo };
* - global settings (payment capability, favourite, etc.)
*/
const WalletDetailsPaymentMethodFeatures = ({ paymentMethod }: Props) => {
- const isMethodExpired = isPaymentMethodExpired(paymentMethod);
+ const isMethodExpired = isPaymentMethodExpired(paymentMethod.details);
const isIdpayEnabled = useIOSelector(isIdPayEnabledSelector);
if (isMethodExpired) {
diff --git a/ts/features/walletV3/common/utils/index.ts b/ts/features/walletV3/common/utils/index.ts
index 67866723415..812c3a3b7b6 100644
--- a/ts/features/walletV3/common/utils/index.ts
+++ b/ts/features/walletV3/common/utils/index.ts
@@ -1,25 +1,20 @@
-import _ from "lodash";
-import * as O from "fp-ts/lib/Option";
import {
IOLogoPaymentType,
IOPaymentLogos,
ListItemTransactionStatusWithBadge
} from "@pagopa/io-app-design-system";
-import I18n from "i18n-js";
+import * as O from "fp-ts/lib/Option";
import { pipe } from "fp-ts/lib/function";
-
-import { isExpiredDate } from "../../../../utils/dates";
+import I18n from "i18n-js";
+import _ from "lodash";
+import { Bundle } from "../../../../../definitions/pagopa/ecommerce/Bundle";
import { ServiceNameEnum } from "../../../../../definitions/pagopa/walletv3/ServiceName";
-import { PaymentSupportStatus } from "../../../../types/paymentMethodCapabilities";
-import {
- TypeEnum,
- WalletInfoDetails,
- WalletInfoDetails1
-} from "../../../../../definitions/pagopa/walletv3/WalletInfoDetails";
import { ServiceStatusEnum } from "../../../../../definitions/pagopa/walletv3/ServiceStatus";
import { WalletInfo } from "../../../../../definitions/pagopa/walletv3/WalletInfo";
+import { PaymentSupportStatus } from "../../../../types/paymentMethodCapabilities";
+import { isExpiredDate } from "../../../../utils/dates";
import { findFirstCaseInsensitive } from "../../../../utils/object";
-import { Bundle } from "../../../../../definitions/pagopa/ecommerce/Bundle";
+import { UIWalletInfoDetails } from "../../details/types/UIWalletInfoDetails";
import { WalletPaymentPspSortType } from "../../payment/types";
/**
@@ -50,16 +45,15 @@ export const getBadgeTextByTransactionStatus = (
* left if expiring date can't be evaluated
* @param paymentMethod
*/
-export const isPaymentMethodExpired = (paymentMethod: WalletInfo): boolean => {
- switch (paymentMethod.details?.type) {
- case TypeEnum.PAYPAL:
- return false;
- case TypeEnum.CARDS:
- const cardDetails = paymentMethod.details as WalletInfoDetails1;
- return isExpiredDate(cardDetails.expiryDate);
- }
- return false;
-};
+export const isPaymentMethodExpired = (
+ details?: UIWalletInfoDetails
+): boolean =>
+ pipe(
+ details?.expiryDate,
+ O.fromNullable,
+ O.map(isExpiredDate),
+ O.getOrElse(() => false)
+ );
/**
* true if the given paymentMethod supports the given walletFunction
@@ -111,25 +105,25 @@ export const isPaymentSupported = (
};
export const getPaymentLogo = (
- selectedMethod: WalletInfoDetails
+ details: UIWalletInfoDetails
): IOLogoPaymentType | undefined => {
- switch (selectedMethod.type) {
- case TypeEnum.CARDS:
- const cardsType = selectedMethod as WalletInfoDetails1;
- const { brand } = cardsType;
- return pipe(
- brand,
- findFirstCaseInsensitive(IOPaymentLogos),
- O.fold(
- () => undefined,
- ([logoName, _]) => logoName
- )
- ) as IOLogoPaymentType;
- case TypeEnum.PAYPAL:
- return "payPal";
- default:
- return undefined;
+ if (details.maskedEmail !== undefined) {
+ return "payPal";
+ } else if (details.maskedNumber !== undefined) {
+ return "bancomatPay";
+ } else if (details.maskedPan !== undefined) {
+ return pipe(
+ details.brand,
+ O.fromNullable,
+ O.chain(findFirstCaseInsensitive(IOPaymentLogos)),
+ O.fold(
+ () => undefined,
+ ([logoName, _]) => logoName
+ )
+ ) as IOLogoPaymentType;
}
+
+ return undefined;
};
export const WALLET_PAYMENT_TERMS_AND_CONDITIONS_URL =
diff --git a/ts/features/walletV3/details/screens/WalletDetailsScreen.tsx b/ts/features/walletV3/details/screens/WalletDetailsScreen.tsx
index ea637521529..2eec49f09b4 100644
--- a/ts/features/walletV3/details/screens/WalletDetailsScreen.tsx
+++ b/ts/features/walletV3/details/screens/WalletDetailsScreen.tsx
@@ -20,12 +20,8 @@ import {
walletDetailsInstrumentSelector
} from "../store";
import { walletDetailsGetInstrument } from "../store/actions";
-import {
- TypeEnum,
- WalletInfoDetails,
- WalletInfoDetails1,
- WalletInfoDetails2
-} from "../../../../../definitions/pagopa/walletv3/WalletInfoDetails";
+import { UIWalletInfoDetails } from "../types/UIWalletInfoDetails";
+import { getDateFromExpiryDate } from "../../../../utils/dates";
export type WalletDetailsScreenNavigationParams = Readonly<{
walletId: string;
@@ -36,44 +32,38 @@ export type WalletDetailsScreenRouteProps = RouteProp<
"WALLET_DETAILS_SCREEN"
>;
-const generateCardComponent = (walletDetails: WalletInfoDetails) => {
- switch (walletDetails.type) {
- case TypeEnum.PAYPAL:
- const paypalDetails = walletDetails as WalletInfoDetails2;
- return (
-
- );
- case TypeEnum.CARDS:
- default:
- const cardDetails = walletDetails as WalletInfoDetails1;
- return (
-
- );
+const generateCardComponent = (details: UIWalletInfoDetails) => {
+ if (details.maskedEmail !== undefined) {
+ return (
+
+ );
}
+
+ return (
+
+ );
};
-const generateCardHeaderTitle = (walletDetails?: WalletInfoDetails) => {
- switch (walletDetails?.type) {
- case TypeEnum.CARDS:
- const cardDetails = walletDetails as WalletInfoDetails1;
- const capitalizedCardCircuit = capitalize(
- cardDetails.brand.toLowerCase() ?? ""
- );
- return `${capitalizedCardCircuit} ••${cardDetails.maskedPan}`;
- default:
- return "";
+const generateCardHeaderTitle = (details?: UIWalletInfoDetails) => {
+ if (details?.maskedPan !== undefined) {
+ const capitalizedCardCircuit = capitalize(
+ details.brand?.toLowerCase() ?? ""
+ );
+ return `${capitalizedCardCircuit} ••${details.maskedPan}`;
}
+
+ return "";
};
/**
diff --git a/ts/features/walletV3/details/types/UIWalletInfoDetails.ts b/ts/features/walletV3/details/types/UIWalletInfoDetails.ts
new file mode 100644
index 00000000000..56f40ccbd47
--- /dev/null
+++ b/ts/features/walletV3/details/types/UIWalletInfoDetails.ts
@@ -0,0 +1,40 @@
+import * as t from "io-ts";
+import {
+ WalletInfoDetails1,
+ WalletInfoDetails2,
+ WalletInfoDetails3
+} from "../../../../../definitions/pagopa/walletv3/WalletInfoDetails";
+
+/**
+ * Transforms all required props from WalletInfoDetails1 to partial
+ */
+export const UIWalletInfoDetails1 = t.partial({
+ ...WalletInfoDetails1.types[0].props,
+ ...WalletInfoDetails1.types[0].props
+});
+
+/**
+ * Transforms all required props from WalletInfoDetails2 to partial
+ */
+export const UIWalletInfoDetails2 = t.partial({
+ ...WalletInfoDetails2.types[0].props,
+ ...WalletInfoDetails2.types[1].props
+});
+
+/**
+ * Transforms all required props from WalletInfoDetails3 to partial
+ */
+export const UIWalletInfoDetails3 = t.partial({
+ ...WalletInfoDetails3.types[0].props,
+ ...WalletInfoDetails3.types[1].props
+});
+
+/**
+ * This type is used to bypass the `type` props of {@see WalletInfoDetails}
+ */
+export const UIWalletInfoDetails = t.intersection(
+ [UIWalletInfoDetails1, UIWalletInfoDetails2, UIWalletInfoDetails3],
+ "UIWalletInfoDetails"
+);
+
+export type UIWalletInfoDetails = t.TypeOf;
diff --git a/ts/features/walletV3/payment/components/WalletPaymentConfirmContent.tsx b/ts/features/walletV3/payment/components/WalletPaymentConfirmContent.tsx
index d7c8a22c6d2..456914c71e8 100644
--- a/ts/features/walletV3/payment/components/WalletPaymentConfirmContent.tsx
+++ b/ts/features/walletV3/payment/components/WalletPaymentConfirmContent.tsx
@@ -1,4 +1,3 @@
-import React from "react";
import {
Body,
GradientScrollView,
@@ -7,35 +6,29 @@ import {
ModuleCheckout,
VSpacer
} from "@pagopa/io-app-design-system";
-import { useNavigation } from "@react-navigation/native";
import { openAuthenticationSession } from "@pagopa/io-react-native-login-utils";
+import { useNavigation } from "@react-navigation/native";
+import React from "react";
+import { Bundle } from "../../../../../definitions/pagopa/ecommerce/Bundle";
+import { PaymentRequestsGetResponse } from "../../../../../definitions/pagopa/ecommerce/PaymentRequestsGetResponse";
+import I18n from "../../../../i18n";
import {
AppParamsList,
IOStackNavigationProp
} from "../../../../navigation/params/AppParamsList";
-import { WalletPaymentRoutes } from "../navigation/routes";
+import { format } from "../../../../utils/dates";
+import { formatNumberCentsToAmount } from "../../../../utils/stringBuilder";
+import { capitalize } from "../../../../utils/strings";
import {
WALLET_PAYMENT_TERMS_AND_CONDITIONS_URL,
getPaymentLogo
} from "../../common/utils";
-import { format } from "../../../../utils/dates";
-import { capitalize } from "../../../../utils/strings";
-import { PaymentRequestsGetResponse } from "../../../../../definitions/pagopa/ecommerce/PaymentRequestsGetResponse";
-import { Bundle } from "../../../../../definitions/pagopa/ecommerce/Bundle";
-import {
- TypeEnum,
- WalletInfoDetails,
- WalletInfoDetails1,
- WalletInfoDetails2,
- WalletInfoDetails3
-} from "../../../../../definitions/pagopa/walletv3/WalletInfoDetails";
-import I18n from "../../../../i18n";
-
-import { formatNumberCentsToAmount } from "../../../../utils/stringBuilder";
+import { UIWalletInfoDetails } from "../../details/types/UIWalletInfoDetails";
+import { WalletPaymentRoutes } from "../navigation/routes";
import { WalletPaymentTotalAmount } from "./WalletPaymentTotalAmount";
export type WalletPaymentConfirmContentProps = {
- paymentMethodDetails: WalletInfoDetails;
+ paymentMethodDetails: UIWalletInfoDetails;
selectedPsp: Bundle;
paymentDetails: PaymentRequestsGetResponse;
isLoading?: boolean;
@@ -127,33 +120,26 @@ export const WalletPaymentConfirmContent = ({
);
};
-const getPaymentSubtitle = (cardDetails: WalletInfoDetails) => {
- switch (cardDetails.type) {
- case TypeEnum.CARDS:
- const cardsDetail = cardDetails as WalletInfoDetails1;
- return `${format(cardsDetail.expiryDate, "MM/YY")}`;
- case TypeEnum.PAYPAL:
- return I18n.t("wallet.onboarding.paypal.name");
- case TypeEnum.BANCOMATPAY:
- const bancomatpayDetail = cardDetails as WalletInfoDetails3;
- return `${bancomatpayDetail.bankName}`;
- default:
- return "";
+const getPaymentSubtitle = (details: UIWalletInfoDetails): string => {
+ if (details.maskedPan !== undefined) {
+ return `${format(details.expiryDate, "MM/YY")}`;
+ } else if (details.maskedEmail !== undefined) {
+ return I18n.t("wallet.onboarding.paypal.name");
+ } else if (details.maskedNumber !== undefined) {
+ return `${details.bankName}`;
}
+
+ return "";
};
-const getPaymentTitle = (cardDetails: WalletInfoDetails) => {
- switch (cardDetails.type) {
- case TypeEnum.CARDS:
- const cardsDetail = cardDetails as WalletInfoDetails1;
- return `${capitalize(cardsDetail.brand)} ••${cardsDetail.maskedPan}`;
- case TypeEnum.PAYPAL:
- const paypalDetail = cardDetails as WalletInfoDetails2;
- return `${paypalDetail.maskedEmail}`;
- case TypeEnum.BANCOMATPAY:
- const bancomatpayDetail = cardDetails as WalletInfoDetails3;
- return `${bancomatpayDetail.maskedNumber}`;
- default:
- return "";
+const getPaymentTitle = (details: UIWalletInfoDetails): string => {
+ if (details.maskedPan !== undefined) {
+ return `${capitalize(details.brand || "")} ••${details.maskedPan}`;
+ } else if (details.maskedEmail !== undefined) {
+ return `${details.maskedEmail}`;
+ } else if (details.maskedNumber !== undefined) {
+ return `${details.maskedNumber}`;
}
+
+ return "";
};
diff --git a/ts/features/walletV3/payment/saga/networking/handleWalletPaymentAuthorization.ts b/ts/features/walletV3/payment/saga/networking/handleWalletPaymentAuthorization.ts
index d634e782503..2a1a9614566 100644
--- a/ts/features/walletV3/payment/saga/networking/handleWalletPaymentAuthorization.ts
+++ b/ts/features/walletV3/payment/saga/networking/handleWalletPaymentAuthorization.ts
@@ -2,16 +2,17 @@ import * as E from "fp-ts/lib/Either";
import { pipe } from "fp-ts/lib/function";
import { call, put } from "typed-redux-saga/macro";
import { ActionType } from "typesafe-actions";
+import {
+ LanguageEnum,
+ RequestAuthorizationRequest
+} from "../../../../../../definitions/pagopa/ecommerce/RequestAuthorizationRequest";
+import { WalletDetailTypeEnum } from "../../../../../../definitions/pagopa/ecommerce/WalletDetailType";
import { SagaCallReturnType } from "../../../../../types/utils";
import { getGenericError, getNetworkError } from "../../../../../utils/errors";
import { readablePrivacyReport } from "../../../../../utils/reporters";
import { withRefreshApiCall } from "../../../../fastLogin/saga/utils";
import { PaymentClient } from "../../api/client";
import { walletPaymentAuthorization } from "../../store/actions/networking";
-import {
- LanguageEnum,
- RequestAuthorizationRequest
-} from "../../../../../../definitions/pagopa/ecommerce/RequestAuthorizationRequest";
export function* handleWalletPaymentAuthorization(
requestTransactionAuthorization: PaymentClient["requestTransactionAuthorization"],
@@ -23,7 +24,10 @@ export function* handleWalletPaymentAuthorization(
isAllCCP: true,
language: LanguageEnum.IT,
pspId: action.payload.pspId,
- walletId: action.payload.walletId
+ details: {
+ detailType: WalletDetailTypeEnum.wallet,
+ walletId: action.payload.walletId
+ }
};
const requestTransactionAuthorizationRequest =
requestTransactionAuthorization({
diff --git a/ts/features/walletV3/payment/screens/WalletPaymentPickMethodScreen.tsx b/ts/features/walletV3/payment/screens/WalletPaymentPickMethodScreen.tsx
index 23c53a5caa0..bed3a5835dc 100644
--- a/ts/features/walletV3/payment/screens/WalletPaymentPickMethodScreen.tsx
+++ b/ts/features/walletV3/payment/screens/WalletPaymentPickMethodScreen.tsx
@@ -20,7 +20,6 @@ import React, { useEffect, useMemo } from "react";
import { View } from "react-native";
import { PaymentMethodResponse } from "../../../../../definitions/pagopa/walletv3/PaymentMethodResponse";
import { WalletInfo } from "../../../../../definitions/pagopa/walletv3/WalletInfo";
-import { WalletInfoDetails1 } from "../../../../../definitions/pagopa/walletv3/WalletInfoDetails";
import { useHeaderSecondLevel } from "../../../../hooks/useHeaderSecondLevel";
import I18n from "../../../../i18n";
import {
@@ -43,6 +42,7 @@ import {
} from "../store/selectors";
import { WalletPaymentMissingMethodsError } from "../components/WalletPaymentMissingMethodsError";
import { useWalletPaymentGoBackHandler } from "../hooks/useWalletPaymentGoBackHandler";
+import { UIWalletInfoDetails } from "../../details/types/UIWalletInfoDetails";
type SavedMethodState = {
kind: "saved";
@@ -265,29 +265,29 @@ const mapGenericToRadioItem = (
const mapSavedToRadioItem = (
method: WalletInfo
): RadioItem | undefined => {
- switch (method.details?.type) {
- case "CARDS":
- const cardDetails = method.details as WalletInfoDetails1;
- return {
- id: method.walletId,
- value: `${capitalize(cardDetails.brand)} ••${cardDetails.maskedPan}`,
- startImage: getIconWithFallback(cardDetails.brand)
- };
- case "PAYPAL":
- return {
- id: method.walletId,
- value: "PayPal",
- startImage: getIconWithFallback("paypal")
- };
- case "BANCOMATPAY":
- return {
- id: method.walletId,
- value: "BANCOMAT Pay",
- startImage: getIconWithFallback("bancomatpay")
- };
- default:
- return undefined;
+ const details = method.details as UIWalletInfoDetails;
+
+ if (details.maskedPan !== undefined) {
+ return {
+ id: method.walletId,
+ value: `${capitalize(details.brand)} ••${details.maskedPan}`,
+ startImage: getIconWithFallback(details.brand)
+ };
+ } else if (details.maskedEmail !== undefined) {
+ return {
+ id: method.walletId,
+ value: "PayPal",
+ startImage: getIconWithFallback("paypal")
+ };
+ } else if (details.maskedNumber !== undefined) {
+ return {
+ id: method.walletId,
+ value: "BANCOMAT Pay",
+ startImage: getIconWithFallback("bancomatpay")
+ };
}
+
+ return undefined;
};
const isMethodDisabledForAmount = (
diff --git a/ts/navigation/AuthenticationNavigator.tsx b/ts/navigation/AuthenticationNavigator.tsx
index c7a392f824e..af41d43c234 100644
--- a/ts/navigation/AuthenticationNavigator.tsx
+++ b/ts/navigation/AuthenticationNavigator.tsx
@@ -10,7 +10,7 @@ import CiePinScreen from "../screens/authentication/cie/CiePinScreen";
import CieWrongCiePinScreen from "../screens/authentication/cie/CieWrongCiePinScreen";
import IdpLoginScreen from "../screens/authentication/IdpLoginScreen";
import IdpSelectionScreen from "../screens/authentication/IdpSelectionScreen";
-import LandingScreen from "../screens/authentication/LandingScreen";
+import { LandingScreen } from "../screens/authentication/LandingScreen";
import TestAuthenticationScreen from "../screens/authentication/TestAuthenticationScreen";
import MarkdownScreen from "../screens/development/MarkdownScreen";
import { AuthSessionPage } from "../screens/authentication/idpAuthSessionHandler";
diff --git a/ts/screens/authentication/LandingScreen.tsx b/ts/screens/authentication/LandingScreen.tsx
index 4f138840e7f..3053331215e 100644
--- a/ts/screens/authentication/LandingScreen.tsx
+++ b/ts/screens/authentication/LandingScreen.tsx
@@ -10,8 +10,9 @@ import { Content, Text as NBButtonText } from "native-base";
import * as React from "react";
import { View, Alert, StyleSheet } from "react-native";
import DeviceInfo from "react-native-device-info";
-import { connect } from "react-redux";
+import { useDispatch, useStore } from "react-redux";
import { IOColors, Icon, HSpacer, VSpacer } from "@pagopa/io-app-design-system";
+import { useNavigation } from "@react-navigation/native";
import sessionExpiredImg from "../../../img/landing/session_expired.png";
import ButtonDefaultOpacity from "../../components/ButtonDefaultOpacity";
import CieNotSupported from "../../components/cie/CieNotSupported";
@@ -19,7 +20,6 @@ import ContextualInfo from "../../components/ContextualInfo";
import { Link } from "../../components/core/typography/Link";
import { IOStyles } from "../../components/core/variables/IOStyles";
import { DevScreenButton } from "../../components/DevScreenButton";
-import { withLightModalContext } from "../../components/helpers/withLightModalContext";
import { HorizontalScroll } from "../../components/HorizontalScroll";
import { renderInfoRasterImage } from "../../components/infoScreen/imageRendering";
import { InfoScreenComponent } from "../../components/infoScreen/InfoScreenComponent";
@@ -29,28 +29,19 @@ import BaseScreenComponent, {
ContextualHelpPropsMarkdown
} from "../../components/screens/BaseScreenComponent";
import SectionStatusComponent from "../../components/SectionStatus";
-import { LightModalContextInterface } from "../../components/ui/LightModal";
import I18n from "../../i18n";
import { mixpanelTrack } from "../../mixpanel";
-import {
- AppParamsList,
- IOStackNavigationRouteProps
-} from "../../navigation/params/AppParamsList";
import ROUTES from "../../navigation/routes";
import {
idpSelected,
resetAuthenticationState
} from "../../store/actions/authentication";
-import { continueWithRootOrJailbreak } from "../../store/actions/persistedPreferences";
-import { Dispatch } from "../../store/actions/types";
-import { isSessionExpiredSelector } from "../../store/reducers/authentication";
import {
hasApiLevelSupportSelector,
hasNFCFeatureSelector,
isCieSupportedSelector
} from "../../store/reducers/cie";
import { continueWithRootOrJailbreakSelector } from "../../store/reducers/persistedPreferences";
-import { GlobalState } from "../../store/reducers/types";
import variables from "../../theme/variables";
import { ComponentProps } from "../../types/react";
import { isDevEnv } from "../../utils/environment";
@@ -64,24 +55,17 @@ import {
} from "../../features/fastLogin/store/selectors";
import { isCieLoginUatEnabledSelector } from "../../features/cieLogin/store/selectors";
import { cieFlowForDevServerEnabled } from "../../features/cieLogin/utils";
+import { useOnFirstRender } from "../../utils/hooks/useOnFirstRender";
+import { useIOSelector } from "../../store/hooks";
+import { isSessionExpiredSelector } from "../../store/reducers/authentication";
+import { LightModalContext } from "../../components/ui/LightModal";
+import { continueWithRootOrJailbreak } from "../../store/actions/persistedPreferences";
import {
trackCieLoginSelected,
trackMethodInfo,
trackSpidLoginSelected
} from "./analytics";
-type NavigationProps = IOStackNavigationRouteProps;
-
-type Props = NavigationProps &
- LightModalContextInterface &
- ReturnType &
- ReturnType;
-
-type State = {
- isRootedOrJailbroken: O.Option;
- isSessionExpired: boolean;
-};
-
const getCards = (
isCIEAvailable: boolean
): ReadonlyArray> => [
@@ -179,124 +163,166 @@ export const IdpCIE: SpidIdp = {
profileUrl: ""
};
-class LandingScreen extends React.PureComponent {
- constructor(props: Props) {
- super(props);
- this.state = { isRootedOrJailbroken: O.none, isSessionExpired: false };
- }
+export const LandingScreen = () => {
+ const [isRootedOrJailbroken, setIsRootedOrJailbroken] = React.useState<
+ O.Option
+ >(O.none);
+ const [
+ hasTabletCompatibilityAlertAlreadyShown,
+ setHasTabletCompatibilityAlertAlreadyShown
+ ] = React.useState(false);
+
+ const store = useStore();
+
+ const dispatch = useDispatch();
+ const navigation = useNavigation();
- private isCieSupported = () =>
- cieFlowForDevServerEnabled || this.props.isCieSupported;
- private isCieUatEnabled = () => this.props.isCieUatEnabled;
+ const isSessionExpired = useIOSelector(isSessionExpiredSelector);
- public async componentDidMount() {
+ const isContinueWithRootOrJailbreak = useIOSelector(
+ continueWithRootOrJailbreakSelector
+ );
+
+ const isFastLoginEnabled = useIOSelector(isFastLoginEnabledSelector);
+ const isFastLoginOptInFFEnabled = useIOSelector(fastLoginOptInFFEnabled);
+
+ const isCIEAuthenticationSupported = useIOSelector(isCieSupportedSelector);
+ const hasApiLevelSupport = useIOSelector(hasApiLevelSupportSelector);
+ const hasCieApiLevelSupport = pot.getOrElse(hasApiLevelSupport, false);
+ const hasNFCFeature = useIOSelector(hasNFCFeatureSelector);
+ const hasCieNFCFeature = pot.getOrElse(hasNFCFeature, false);
+
+ const isCieSupported = React.useCallback(
+ () =>
+ cieFlowForDevServerEnabled ||
+ pot.getOrElse(isCIEAuthenticationSupported, false),
+ [isCIEAuthenticationSupported]
+ );
+ const isCieUatEnabled = useIOSelector(isCieLoginUatEnabledSelector);
+
+ useOnFirstRender(async () => {
const isRootedOrJailbroken = await JailMonkey.isJailBroken();
- this.setState({ isRootedOrJailbroken: O.some(isRootedOrJailbroken) });
- if (this.props.isSessionExpired) {
- this.setState({ isSessionExpired: true });
- this.props.resetState();
+ setIsRootedOrJailbroken(O.some(isRootedOrJailbroken));
+ if (isSessionExpired) {
+ dispatch(resetAuthenticationState());
}
- }
+ });
- private displayTabletAlert() {
- Alert.alert(
- "",
- I18n.t("tablet.message"),
- [
- {
- text: I18n.t("global.buttons.continue"),
- style: "cancel"
- }
- ],
- { cancelable: true }
- );
- }
+ const { hideModal, showAnimatedModal } = React.useContext(LightModalContext);
- private openUnsupportedCIEModal = () => {
- this.props.showAnimatedModal(
- (
-
- )}
- />
- );
+ const displayTabletAlert = () => {
+ if (!hasTabletCompatibilityAlertAlreadyShown) {
+ setHasTabletCompatibilityAlertAlreadyShown(true);
+ Alert.alert(
+ "",
+ I18n.t("tablet.message"),
+ [
+ {
+ text: I18n.t("global.buttons.continue"),
+ style: "cancel"
+ }
+ ],
+ { cancelable: true }
+ );
+ }
};
- private navigateToMarkdown = () =>
- this.props.navigation.navigate(ROUTES.AUTHENTICATION, {
- screen: ROUTES.MARKDOWN
- });
+ const navigateToMarkdown = React.useCallback(
+ () =>
+ navigation.navigate(ROUTES.AUTHENTICATION, {
+ screen: ROUTES.MARKDOWN
+ }),
+ [navigation]
+ );
- private navigateToIdpSelection = () => {
+ const navigateToIdpSelection = React.useCallback(() => {
trackSpidLoginSelected();
- if (this.props.isFastLoginOptInFFEnabled) {
- this.props.navigation.navigate(ROUTES.AUTHENTICATION, {
+ if (isFastLoginOptInFFEnabled) {
+ navigation.navigate(ROUTES.AUTHENTICATION, {
screen: ROUTES.AUTHENTICATION_OPT_IN,
params: { identifier: "SPID" }
});
} else {
- this.props.navigation.navigate(ROUTES.AUTHENTICATION, {
+ navigation.navigate(ROUTES.AUTHENTICATION, {
screen: ROUTES.AUTHENTICATION_IDP_SELECTION
});
}
- };
+ }, [isFastLoginOptInFFEnabled, navigation]);
+
+ const navigateToCiePinScreen = React.useCallback(() => {
+ const openUnsupportedCIEModal = () => {
+ showAnimatedModal(
+ (
+
+ )}
+ />
+ );
+ };
- private navigateToCiePinScreen = () => {
- if (this.isCieSupported()) {
- void trackCieLoginSelected(this.props.state);
- this.props.dispatchIdpCieSelected();
- if (this.props.isFastLoginOptInFFEnabled) {
- this.props.navigation.navigate(ROUTES.AUTHENTICATION, {
+ if (isCieSupported()) {
+ void trackCieLoginSelected(store.getState());
+ dispatch(idpSelected(IdpCIE));
+ if (isFastLoginOptInFFEnabled) {
+ navigation.navigate(ROUTES.AUTHENTICATION, {
screen: ROUTES.AUTHENTICATION_OPT_IN,
params: { identifier: "CIE" }
});
} else {
- this.props.navigation.navigate(ROUTES.AUTHENTICATION, {
+ navigation.navigate(ROUTES.AUTHENTICATION, {
screen: ROUTES.CIE_PIN_SCREEN
});
}
} else {
- this.openUnsupportedCIEModal();
+ openUnsupportedCIEModal();
}
- };
+ }, [
+ dispatch,
+ hasCieApiLevelSupport,
+ hasCieNFCFeature,
+ hideModal,
+ isCieSupported,
+ isFastLoginOptInFFEnabled,
+ navigation,
+ showAnimatedModal,
+ store
+ ]);
- private navigateToSpidCieInformationRequest = () => {
+ const navigateToSpidCieInformationRequest = () => {
trackMethodInfo();
openWebUrl(cieSpidMoreInfoUrl);
};
- private navigateToCieUatSelectionScreen = () => {
- if (this.isCieSupported()) {
- this.props.navigation.navigate(ROUTES.AUTHENTICATION, {
+ const navigateToCieUatSelectionScreen = React.useCallback(() => {
+ if (isCieSupported()) {
+ navigation.navigate(ROUTES.AUTHENTICATION, {
screen: ROUTES.CIE_LOGIN_CONFIG_SCREEN
});
}
- };
+ }, [isCieSupported, navigation]);
- private renderCardComponents = () => {
- const cardProps = getCards(this.isCieSupported());
+ const renderCardComponents = () => {
+ const cardProps = getCards(isCieSupported());
return cardProps.map(p => (
));
};
- private handleContinueWithRootOrJailbreak = (continueWith: boolean) => {
- this.props.dispatchContinueWithRootOrJailbreak(continueWith);
+ const handleContinueWithRootOrJailbreak = (continueWith: boolean) => {
+ dispatch(continueWithRootOrJailbreak(continueWith));
};
// eslint-disable-next-line sonarjs/cognitive-complexity
- private renderLandingScreen = () => {
- const isCieSupported = this.isCieSupported();
- const isCieUatEnabled = this.isCieUatEnabled();
+ const renderLandingScreen = () => {
const firstButtonStyle = isCieUatEnabled
? styles.uatCie
: styles.fullOpacity;
- const secondButtonStyle = isCieSupported
+ const secondButtonStyle = isCieSupported()
? styles.fullOpacity
: styles.noCie;
return (
@@ -304,22 +330,22 @@ class LandingScreen extends React.PureComponent {
appLogo
contextualHelpMarkdown={contextualHelpMarkdown}
faqCategories={
- isCieSupported ? ["landing_SPID", "landing_CIE"] : ["landing_SPID"]
+ isCieSupported() ? ["landing_SPID", "landing_CIE"] : ["landing_SPID"]
}
>
- {isDevEnv && }
+ {isDevEnv && }
- {this.state.isSessionExpired ? (
+ {isSessionExpired ? (
) : (
-
+
)}
@@ -330,31 +356,32 @@ class LandingScreen extends React.PureComponent {
primary={true}
iconLeft={true}
onPress={
- isCieSupported
- ? this.navigateToCiePinScreen
- : this.navigateToIdpSelection
+ isCieSupported() ? navigateToCiePinScreen : navigateToIdpSelection
}
onLongPress={() =>
- isCieSupported ? this.navigateToCieUatSelectionScreen() : ""
+ isCieSupported() ? navigateToCieUatSelectionScreen() : ""
}
accessibilityRole="button"
accessible={true}
style={firstButtonStyle}
accessibilityLabel={
- isCieSupported
+ isCieSupported()
? I18n.t("authentication.landing.loginCie")
: I18n.t("authentication.landing.loginSpid")
}
testID={
- isCieSupported
+ isCieSupported()
? "landing-button-login-cie"
: "landing-button-login-spid"
}
>
-
+
- {isCieSupported
+ {isCieSupported()
? I18n.t("authentication.landing.loginCie")
: I18n.t("authentication.landing.loginSpid")}
@@ -362,7 +389,7 @@ class LandingScreen extends React.PureComponent {
{
primary={true}
iconLeft={true}
onPress={
- this.isCieSupported()
- ? this.navigateToIdpSelection
- : this.navigateToCiePinScreen
+ isCieSupported() ? navigateToIdpSelection : navigateToCiePinScreen
}
testID={
- this.isCieSupported()
+ isCieSupported()
? "landing-button-login-spid"
: "landing-button-login-cie"
}
>
- {this.isCieSupported()
+ {isCieSupported()
? I18n.t("authentication.landing.loginSpid")
: I18n.t("authentication.landing.loginCie")}
@@ -397,9 +422,9 @@ class LandingScreen extends React.PureComponent {
- {this.isCieSupported()
+ {isCieSupported()
? I18n.t("authentication.landing.nospid-nocie")
: I18n.t("authentication.landing.nospid")}
@@ -409,70 +434,39 @@ class LandingScreen extends React.PureComponent {
};
// Screen displayed during the async loading of the JailMonkey.isJailBroken()
- private renderLoadingScreen = () => (
+ const renderLoadingScreen = () => (
);
- private chooseScreenToRender = (isRootedOrJailbroken: boolean) => {
+ const chooseScreenToRender = (isRootedOrJailbroken: boolean) => {
// if the device is compromised and the user didn't allow to continue
// show a blocking modal
- if (isRootedOrJailbroken && !this.props.continueWithRootOrJailbreak) {
+ if (isRootedOrJailbroken && !isContinueWithRootOrJailbreak) {
void mixpanelTrack("SHOW_ROOTED_OR_JAILBROKEN_MODAL");
return (
this.handleContinueWithRootOrJailbreak(true)}
- onCancel={() => this.handleContinueWithRootOrJailbreak(false)}
+ onContinue={() => handleContinueWithRootOrJailbreak(true)}
+ onCancel={() => handleContinueWithRootOrJailbreak(false)}
/>
);
}
// In case of Tablet, display an alert to inform the user
if (DeviceInfo.isTablet()) {
- this.displayTabletAlert();
+ displayTabletAlert();
}
// standard rendering of the landing screen
- return this.renderLandingScreen();
+ return renderLandingScreen();
};
- public render() {
- // If the async loading of the isRootedOrJailbroken is not ready, display a loading
- return pipe(
- this.state.isRootedOrJailbroken,
- O.fold(
- () => this.renderLoadingScreen(),
- // when the value isRootedOrJailbroken is ready, display the right screen based on a set of rule
- rootedOrJailbroken => this.chooseScreenToRender(rootedOrJailbroken)
- )
- );
- }
-}
-
-const mapStateToProps = (state: GlobalState) => {
- const isCIEAuthenticationSupported = isCieSupportedSelector(state);
- const hasApiLevelSupport = hasApiLevelSupportSelector(state);
- const hasNFCFeature = hasNFCFeatureSelector(state);
- return {
- isFastLoginEnabled: isFastLoginEnabledSelector(state),
- isSessionExpired: isSessionExpiredSelector(state),
- isFastLoginOptInFFEnabled: fastLoginOptInFFEnabled(state),
- continueWithRootOrJailbreak: continueWithRootOrJailbreakSelector(state),
- isCieSupported: pot.getOrElse(isCIEAuthenticationSupported, false),
- hasCieApiLevelSupport: pot.getOrElse(hasApiLevelSupport, false),
- hasCieNFCFeature: pot.getOrElse(hasNFCFeature, false),
- isCieUatEnabled: isCieLoginUatEnabledSelector(state),
- state
- };
+ // If the async loading of the isRootedOrJailbroken is not ready, display a loading
+ return pipe(
+ isRootedOrJailbroken,
+ O.fold(
+ () => renderLoadingScreen(),
+ // when the value isRootedOrJailbroken is ready, display the right screen based on a set of rule
+ rootedOrJailbroken => chooseScreenToRender(rootedOrJailbroken)
+ )
+ );
};
-
-const mapDispatchToProps = (dispatch: Dispatch) => ({
- resetState: () => dispatch(resetAuthenticationState()),
- dispatchIdpCieSelected: () => dispatch(idpSelected(IdpCIE)),
- dispatchContinueWithRootOrJailbreak: (continueWith: boolean) =>
- dispatch(continueWithRootOrJailbreak(continueWith))
-});
-
-export default connect(
- mapStateToProps,
- mapDispatchToProps
-)(withLightModalContext(LandingScreen));
diff --git a/ts/utils/dates.ts b/ts/utils/dates.ts
index 009d0fb6805..41b5b99c83a 100644
--- a/ts/utils/dates.ts
+++ b/ts/utils/dates.ts
@@ -231,8 +231,8 @@ export const isExpired = (
* @param expiryDate
*/
export const isExpiredDate = (expiryDate: string): boolean => {
- const year = +expiryDate.slice(3, 5);
- const month = +expiryDate.slice(0, 3);
+ const year = +expiryDate.slice(0, 4);
+ const month = +expiryDate.slice(4, 6);
const now = new Date();
const nowYearMonth = new Date(now.getFullYear(), now.getMonth() + 1);
const cardExpirationDate = new Date(year, month);
@@ -320,3 +320,13 @@ export const toAndroidCacheTimestamp = () =>
new Date(),
I18n.t("global.dateFormats.shortFormat").replace(/\//g, "")
);
+
+/**
+ * This function returns a Date object from a string in format "YYYYMM"
+ * @param expiryDate
+ */
+export const getDateFromExpiryDate = (expiryDate: string): Date => {
+ const year = +expiryDate.slice(0, 4);
+ const month = +expiryDate.slice(4, 6);
+ return new Date(year, month - 1);
+};