From dff6e615a7d8e2674c3ffceff485546044734aef Mon Sep 17 00:00:00 2001 From: Federico Mastrini Date: Fri, 12 Jan 2024 14:36:28 +0100 Subject: [PATCH] feat: [IOBP-437] Add new wallet payment outcome error handling (#5390) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ⚠️ Depends on https://github.com/pagopa/io-app/pull/5378 ## Short description This PR adds the handling of the payment error outcomes. ## List of changes proposed in this pull request - Added components mapping based on the outcome in `WalletPaymentOutcomeScreen` - Added required locales keys - Refactored `usePaymentFailureSupportModal` to remove the requirement of passing `RptId` as its argument. ## How to test Within the **Profile > New Wallet > Payment** playground, using the `io-dev-api-server`, try to simulate different payment outcomes and check that the screen and its components are displayed correctly. ## Preview https://github.com/pagopa/io-app/assets/6160324/cb7c33f1-9c0d-471c-accf-a362efc42264 --------- Co-authored-by: Alessandro Izzo Co-authored-by: Mario Perrotta --- locales/de/index.yml | 8 +- locales/en/index.yml | 46 +++-- locales/it/index.yml | 46 +++-- .../components/WalletPaymentFailureDetail.tsx | 6 +- .../WalletPaymentFeedbackBanner.tsx | 37 ++++ .../hooks/usePaymentFailureSupportModal.tsx | 26 ++- .../screens/WalletPaymentDetailScreen.tsx | 2 +- .../screens/WalletPaymentOutcomeScreen.tsx | 178 ++++++++++++------ .../walletV3/payment/store/reducers/index.ts | 3 + .../walletV3/payment/store/selectors/index.ts | 7 + .../payment/types/PaymentOutcomeEnum.ts | 13 +- 11 files changed, 276 insertions(+), 96 deletions(-) create mode 100644 ts/features/walletV3/payment/components/WalletPaymentFeedbackBanner.tsx diff --git a/locales/de/index.yml b/locales/de/index.yml index 62fa7defa25..bcc9c96b8ae 100644 --- a/locales/de/index.yml +++ b/locales/de/index.yml @@ -720,7 +720,7 @@ email: subtitle: "Um die App weiter nutzen zu können, musst du deine E-Mail-Adresse bestätigen" editButton: "E-Mail-Adresse ändern" validateButton: "E-Mail-Adresse bestätigen" - header: + header: title: "IO konfigurieren" help: body: "Um die App weiter nutzen zu können, musst du deine E-Mail-Adresse bestätigen" @@ -729,7 +729,7 @@ email: subtitleStart: "Deine E-Mail-Adresse" subtitleEnd: "wird bereits auf IO verwendet; du musst eine andere eingeben, um die App weiter benutzen zu können." editButton: "E-Mail-Adresse ändern" - header: + header: title: "IO konfigurieren" help: body: "Deine E-Mail-Adresse wird bereits auf IO verwendet; du musst eine andere eingeben, um die App weiter benutzen zu können." @@ -936,7 +936,7 @@ payment: description: "IO hat keine Zahlungsversuche gespeichert. Hier findest du deine letzten Zahlungsversuche" title: "Zahlungsversuche" iuv: "Einheitlicher Zahlungskodex (IUV) {{iuv}}" - confirm: + confirm: totalAmount: "Zu zahlender Gesamtbetrag" pay: "Zahle " payWith: "Zahle mit " @@ -1617,7 +1617,7 @@ wallet: amount: "Nach Betrag" name: "Nach Name" outcome: - success: + SUCCESS: title: "Du hast {{amount}} bezahlt" button: "OK, schließen" banner: diff --git a/locales/en/index.yml b/locales/en/index.yml index 5c51410b00b..7a8b0bc4e84 100644 --- a/locales/en/index.yml +++ b/locales/en/index.yml @@ -1711,6 +1711,41 @@ wallet: PAA_PAGAMENTO_SCONOSCIUTO: title: "Non riusciamo a trovare l’avviso" subtitle: "L’avviso potrebbe essere stato già pagato. Per ricevere assistenza, contatta l’Ente Creditore che lo ha emesso." + outcome: + SUCCESS: + title: Hai pagato {{amount}} + button: Ok, chiudi + banner: + title: Puoi dirci com’è andata? + content: Raccontaci la tua esperienza con il pagamento e aiutaci a migliorare. + action: Vai al sondaggio + GENERIC_ERROR: + title: Si è verificato un errore imprevisto + subtitle: Non è stato addebitato alcun importo. + AUTH_ERROR: + title: Autorizzazione negata + subtitle: "Non è stato addebitato alcun importo.\nControlla di aver seguito correttamente le istruzioni della tua banca." + INVALID_DATA: + title: I dati del metodo di pagamento non sono corretti + subtitle: Se hai pagato con una carta di debito o credito, assicurati di inserire i dati come riportato sul fronte. + TIMEOUT: + title: La sessione è scaduta + subtitle: Non è stato addebitato alcun importo.\nPer la tua sicurezza, hai a disposizione un tempo limitato per completare l’operazione. + CIRCUIT_ERROR: + title: Il circuito della carta non è supportato + MISSING_FIELDS: + title: Mancano alcuni dati per procedere con il pagamento + INVALID_CARD: + title: Il metodo di pagamento è scaduto o non più valido + subtitle: Per maggiori informazioni, contatta la tua banca. + CANCELED_BY_USER: + title: L’operazione è stata annullata + subtitle: Non è stato addebitato alcun importo. + EXCESSIVE_AMOUNT: + title: Autorizzazione negata + subtitle: Non è stato addebitato alcun importo.\nProbabilmente hai sforato i limiti di spesa previsti dalla tua banca. + INVALID_METHOD: + title: Il metodo di pagamento non è supportato support: button: "Contatta l'assistenza" supportTitle: Contatta l'assistenza @@ -1721,17 +1756,6 @@ wallet: errorCode: Codice errore noticeNumber: Codice avviso entityCode: Codice Fiscale Ente - outcome: - cancelled: - title: L’operazione è stata annullata - subtitle: Non è stato addebitato alcun importo. - success: - title: Hai pagato {{amount}} - button: Ok, chiudi - banner: - title: Puoi dirci com’è andata? - content: Raccontaci la tua esperienza con il pagamento e aiutaci a migliorare. - action: Vai al sondaggio saveCard: saveCard: Save the card header: Do you want to save this card? diff --git a/locales/it/index.yml b/locales/it/index.yml index 82c9481954d..379a4c9e83a 100644 --- a/locales/it/index.yml +++ b/locales/it/index.yml @@ -1711,6 +1711,41 @@ wallet: PAA_PAGAMENTO_SCONOSCIUTO: title: "Non riusciamo a trovare l’avviso" subtitle: "L’avviso potrebbe essere stato già pagato. Per ricevere assistenza, contatta l’Ente Creditore che lo ha emesso." + outcome: + SUCCESS: + title: Hai pagato {{amount}} + button: Ok, chiudi + banner: + title: Puoi dirci com’è andata? + content: Raccontaci la tua esperienza con il pagamento e aiutaci a migliorare. + action: Vai al sondaggio + GENERIC_ERROR: + title: Si è verificato un errore imprevisto + subtitle: Non è stato addebitato alcun importo. + AUTH_ERROR: + title: Autorizzazione negata + subtitle: "Non è stato addebitato alcun importo.\nControlla di aver seguito correttamente le istruzioni della tua banca." + INVALID_DATA: + title: I dati del metodo di pagamento non sono corretti + subtitle: Se hai pagato con una carta di debito o credito, assicurati di inserire i dati come riportato sul fronte. + TIMEOUT: + title: La sessione è scaduta + subtitle: Non è stato addebitato alcun importo.\nPer la tua sicurezza, hai a disposizione un tempo limitato per completare l’operazione. + CIRCUIT_ERROR: + title: Il circuito della carta non è supportato + MISSING_FIELDS: + title: Mancano alcuni dati per procedere con il pagamento + INVALID_CARD: + title: Il metodo di pagamento è scaduto o non più valido + subtitle: Per maggiori informazioni, contatta la tua banca. + CANCELED_BY_USER: + title: L’operazione è stata annullata + subtitle: Non è stato addebitato alcun importo. + EXCESSIVE_AMOUNT: + title: Autorizzazione negata + subtitle: Non è stato addebitato alcun importo.\nProbabilmente hai sforato i limiti di spesa previsti dalla tua banca. + INVALID_METHOD: + title: Il metodo di pagamento non è supportato support: button: "Contatta l'assistenza" supportTitle: Contatta l'assistenza @@ -1721,17 +1756,6 @@ wallet: errorCode: Codice errore noticeNumber: Codice avviso entityCode: Codice Fiscale Ente - outcome: - cancelled: - title: L’operazione è stata annullata - subtitle: Non è stato addebitato alcun importo. - success: - title: Hai pagato {{amount}} - button: Ok, chiudi - banner: - title: Puoi dirci com’è andata? - content: Raccontaci la tua esperienza con il pagamento e aiutaci a migliorare. - action: Vai al sondaggio saveCard: saveCard: Salva la carta header: Vuoi salvare questa carta? diff --git a/ts/features/walletV3/payment/components/WalletPaymentFailureDetail.tsx b/ts/features/walletV3/payment/components/WalletPaymentFailureDetail.tsx index 9874d828f9d..288590467c6 100644 --- a/ts/features/walletV3/payment/components/WalletPaymentFailureDetail.tsx +++ b/ts/features/walletV3/payment/components/WalletPaymentFailureDetail.tsx @@ -1,7 +1,6 @@ import { useNavigation } from "@react-navigation/native"; import React from "react"; import { FaultCategoryEnum } from "../../../../../definitions/pagopa/ecommerce/FaultCategory"; -import { RptId } from "../../../../../definitions/pagopa/ecommerce/RptId"; import { ValidationFaultEnum } from "../../../../../definitions/pagopa/ecommerce/ValidationFault"; import { OperationResultScreenContent, @@ -16,13 +15,12 @@ import { usePaymentFailureSupportModal } from "../hooks/usePaymentFailureSupport import { WalletPaymentFailure } from "../types/failure"; type Props = { - rptId?: RptId; failure: WalletPaymentFailure; }; -const WalletPaymentFailureDetail = ({ rptId, failure }: Props) => { +const WalletPaymentFailureDetail = ({ failure }: Props) => { const navigation = useNavigation>(); - const supportModal = usePaymentFailureSupportModal({ rptId, failure }); + const supportModal = usePaymentFailureSupportModal({ failure }); const handleClose = () => { navigation.pop(); diff --git a/ts/features/walletV3/payment/components/WalletPaymentFeedbackBanner.tsx b/ts/features/walletV3/payment/components/WalletPaymentFeedbackBanner.tsx new file mode 100644 index 00000000000..c0ab6aa774a --- /dev/null +++ b/ts/features/walletV3/payment/components/WalletPaymentFeedbackBanner.tsx @@ -0,0 +1,37 @@ +import { Banner, VSpacer } from "@pagopa/io-app-design-system"; +import { openAuthenticationSession } from "@pagopa/io-react-native-login-utils"; +import { default as React } from "react"; +import { View } from "react-native"; +import I18n from "../../../../i18n"; +import { mixpanelTrack } from "../../../../mixpanel"; +import { WALLET_PAYMENT_FEEDBACK_URL } from "../utils"; + +const WalletPaymentFeebackBanner = () => { + const bannerViewRef = React.useRef(null); + + const handleBannerPress = () => { + void mixpanelTrack("VOC_USER_EXIT", { + screen_name: "PAYMENT_OUTCOMECODE_MESSAGE" + }); + + return openAuthenticationSession(WALLET_PAYMENT_FEEDBACK_URL, ""); + }; + + return ( + <> + + + + ); +}; + +export { WalletPaymentFeebackBanner }; diff --git a/ts/features/walletV3/payment/hooks/usePaymentFailureSupportModal.tsx b/ts/features/walletV3/payment/hooks/usePaymentFailureSupportModal.tsx index 95bf2b80f2e..2dd47feab59 100644 --- a/ts/features/walletV3/payment/hooks/usePaymentFailureSupportModal.tsx +++ b/ts/features/walletV3/payment/hooks/usePaymentFailureSupportModal.tsx @@ -14,7 +14,6 @@ import { pipe } from "fp-ts/lib/function"; import React from "react"; import { Linking } from "react-native"; import { ToolEnum } from "../../../../../definitions/content/AssistanceToolConfig"; -import { RptId } from "../../../../../definitions/pagopa/ecommerce/RptId"; import I18n from "../../../../i18n"; import { useIODispatch, useIOSelector } from "../../../../store/hooks"; import { assistanceToolConfigSelector } from "../../../../store/reducers/backendStatus"; @@ -35,12 +34,17 @@ import { zendeskSelectedCategory, zendeskSupportStart } from "../../../zendesk/store/actions"; +import { walletPaymentRptIdSelector } from "../store/selectors"; +import { + WalletPaymentOutcome, + getWalletPaymentOutcomeEnumByValue +} from "../types/PaymentOutcomeEnum"; import { WalletPaymentFailure } from "../types/failure"; type PaymentFailureSupportModalParams = { - rptId?: RptId; - failure: WalletPaymentFailure; - withPhoneSupport?: boolean; + failure?: WalletPaymentFailure; + outcome?: WalletPaymentOutcome; + withPhoneAssistance?: boolean; }; type PaymentFailureSupportModal = { @@ -49,16 +53,20 @@ type PaymentFailureSupportModal = { }; const usePaymentFailureSupportModal = ({ - rptId, failure, - withPhoneSupport = false + outcome, + withPhoneAssistance = false }: PaymentFailureSupportModalParams): PaymentFailureSupportModal => { - const { faultCodeDetail } = failure; - const assistanceToolConfig = useIOSelector(assistanceToolConfigSelector); const choosenTool = assistanceToolRemoteConfig(assistanceToolConfig); + const rptId = useIOSelector(walletPaymentRptIdSelector); const dispatch = useIODispatch(); + const faultCodeDetail = + failure?.faultCodeDetail || + (outcome && getWalletPaymentOutcomeEnumByValue(outcome)) || + ""; + const zendeskAssistanceLogAndStart = () => { resetCustomFields(); addTicketCustomField(zendeskCategoryId, zendeskPaymentCategory.value); @@ -126,7 +134,7 @@ const usePaymentFailureSupportModal = ({ component: ( <> - {withPhoneSupport && ( + {withPhoneAssistance && ( { faultCodeDetail: GatewayFaultEnum.GENERIC_ERROR })) ); - return ; + return ; } if (pot.isSome(paymentDetailsPot)) { diff --git a/ts/features/walletV3/payment/screens/WalletPaymentOutcomeScreen.tsx b/ts/features/walletV3/payment/screens/WalletPaymentOutcomeScreen.tsx index 4720045adfb..ae321c1e63e 100644 --- a/ts/features/walletV3/payment/screens/WalletPaymentOutcomeScreen.tsx +++ b/ts/features/walletV3/payment/screens/WalletPaymentOutcomeScreen.tsx @@ -1,27 +1,27 @@ -import { Banner, VSpacer } from "@pagopa/io-app-design-system"; -import { openAuthenticationSession } from "@pagopa/io-react-native-login-utils"; import * as pot from "@pagopa/ts-commons/lib/pot"; import { RouteProp, useNavigation, useRoute } from "@react-navigation/native"; import * as O from "fp-ts/lib/Option"; import { pipe } from "fp-ts/lib/function"; import React from "react"; -import { View } from "react-native"; -import { OperationResultScreenContent } from "../../../../components/screens/OperationResultScreenContent"; +import { + OperationResultScreenContent, + OperationResultScreenContentProps +} from "../../../../components/screens/OperationResultScreenContent"; import I18n from "../../../../i18n"; -import { mixpanelTrack } from "../../../../mixpanel"; import { AppParamsList, IOStackNavigationProp } from "../../../../navigation/params/AppParamsList"; import { useIOSelector } from "../../../../store/hooks"; import { formatNumberCentsToAmount } from "../../../../utils/stringBuilder"; +import { WalletPaymentFeebackBanner } from "../components/WalletPaymentFeedbackBanner"; +import { usePaymentFailureSupportModal } from "../hooks/usePaymentFailureSupportModal"; import { WalletPaymentParamsList } from "../navigation/params"; import { walletPaymentDetailsSelector } from "../store/selectors"; import { WalletPaymentOutcome, WalletPaymentOutcomeEnum } from "../types/PaymentOutcomeEnum"; -import { WALLET_PAYMENT_FEEDBACK_URL } from "../utils"; type WalletPaymentOutcomeScreenNavigationParams = { outcome: WalletPaymentOutcome; @@ -34,74 +34,142 @@ type WalletPaymentOutcomeRouteProps = RouteProp< const WalletPaymentOutcomeScreen = () => { const { params } = useRoute(); + const { outcome } = params; + const navigation = useNavigation>(); const paymentDetailsPot = useIOSelector(walletPaymentDetailsSelector); + const supportModal = usePaymentFailureSupportModal({ + outcome + }); + const paymentAmount = pipe( paymentDetailsPot, pot.toOption, O.map(({ amount }) => formatNumberCentsToAmount(amount, true, "right")), - O.getOrElse(() => "--") + O.getOrElse(() => "-") ); - const handleContinue = () => { + const handleContactSupport = () => { + supportModal.present(); + }; + + const handleClose = () => { navigation.popToTop(); navigation.pop(); }; - const handleBannerPress = () => { - void mixpanelTrack("VOC_USER_EXIT", { - screen_name: "PAYMENT_OUTCOMECODE_MESSAGE" - }); + const closeSuccessAction: OperationResultScreenContentProps["action"] = { + label: I18n.t("wallet.payment.outcome.SUCCESS.button"), + accessibilityLabel: I18n.t("wallet.payment.outcome.SUCCESS.button"), + onPress: handleClose + }; + + const closeFailureAction: OperationResultScreenContentProps["action"] = { + label: I18n.t("global.buttons.close"), + accessibilityLabel: I18n.t("global.buttons.close"), + onPress: handleClose + }; - return openAuthenticationSession(WALLET_PAYMENT_FEEDBACK_URL, ""); + const contactSupportAction: OperationResultScreenContentProps["action"] = { + label: I18n.t("wallet.payment.support.button"), + accessibilityLabel: I18n.t("wallet.payment.support.button"), + onPress: handleContactSupport }; - const bannerViewRef = React.useRef(null); + const getPropsForOutcome = (): OperationResultScreenContentProps => { + switch (outcome) { + case WalletPaymentOutcomeEnum.SUCCESS: + return { + pictogram: "success", + title: I18n.t("wallet.payment.outcome.SUCCESS.title", { + amount: paymentAmount + }), + action: closeSuccessAction + }; + case WalletPaymentOutcomeEnum.GENERIC_ERROR: + default: + return { + pictogram: "umbrellaNew", + title: I18n.t("wallet.payment.outcome.GENERIC_ERROR.title"), + subtitle: I18n.t("wallet.payment.outcome.GENERIC_ERROR.subtitle"), + action: closeFailureAction + }; + case WalletPaymentOutcomeEnum.AUTH_ERROR: + return { + pictogram: "accessDenied", + title: I18n.t("wallet.payment.outcome.AUTH_ERROR.title"), + subtitle: I18n.t("wallet.payment.outcome.AUTH_ERROR.subtitle"), + action: closeFailureAction + }; + case WalletPaymentOutcomeEnum.INVALID_DATA: + return { + pictogram: "cardIssue", + title: I18n.t("wallet.payment.outcome.INVALID_DATA.title"), + subtitle: I18n.t("wallet.payment.outcome.INVALID_DATA.subtitle"), + action: closeFailureAction + }; + case WalletPaymentOutcomeEnum.TIMEOUT: + return { + pictogram: "time", + title: I18n.t("wallet.payment.outcome.TIMEOUT.title"), + subtitle: I18n.t("wallet.payment.outcome.TIMEOUT.subtitle"), + action: closeFailureAction + }; + case WalletPaymentOutcomeEnum.CIRCUIT_ERROR: + return { + pictogram: "cardIssue", + title: I18n.t("wallet.payment.outcome.CIRCUIT_ERROR.title"), + action: contactSupportAction, + secondaryAction: closeFailureAction + }; + case WalletPaymentOutcomeEnum.MISSING_FIELDS: + return { + pictogram: "attention", + title: I18n.t("wallet.payment.outcome.MISSING_FIELDS.title"), + action: contactSupportAction, + secondaryAction: closeFailureAction + }; + case WalletPaymentOutcomeEnum.INVALID_CARD: + return { + pictogram: "cardIssue", + title: I18n.t("wallet.payment.outcome.INVALID_CARD.title"), + subtitle: I18n.t("wallet.payment.outcome.INVALID_CARD.subtitle"), + action: closeFailureAction + }; + case WalletPaymentOutcomeEnum.CANCELED_BY_USER: + return { + pictogram: "trash", + title: I18n.t("wallet.payment.outcome.CANCELED_BY_USER.title"), + subtitle: I18n.t("wallet.payment.outcome.CANCELED_BY_USER.subtitle"), + action: closeFailureAction + }; + case WalletPaymentOutcomeEnum.EXCESSIVE_AMOUNT: + return { + pictogram: "accessDenied", + title: I18n.t("wallet.payment.outcome.EXCESSIVE_AMOUNT.title"), + subtitle: I18n.t("wallet.payment.outcome.EXCESSIVE_AMOUNT.subtitle"), + action: closeFailureAction + }; + case WalletPaymentOutcomeEnum.INVALID_METHOD: + return { + pictogram: "cardIssue", + title: I18n.t("wallet.payment.outcome.INVALID_METHOD.title"), + action: contactSupportAction, + secondaryAction: closeFailureAction + }; + } + }; - if (params.outcome === WalletPaymentOutcomeEnum.CANCELED_BY_USER) { - return ( - navigation.pop(2) - }} - /> - ); - } + const requiresFeedback = outcome === WalletPaymentOutcomeEnum.SUCCESS; return ( - - <> - - - - + <> + + {requiresFeedback && } + + {supportModal.bottomSheet} + ); }; diff --git a/ts/features/walletV3/payment/store/reducers/index.ts b/ts/features/walletV3/payment/store/reducers/index.ts index 4919f5e1e26..d96dd69e5c9 100644 --- a/ts/features/walletV3/payment/store/reducers/index.ts +++ b/ts/features/walletV3/payment/store/reducers/index.ts @@ -26,8 +26,10 @@ import { } from "../actions/orchestration"; import { WalletInfo } from "../../../../../../definitions/pagopa/walletv3/WalletInfo"; import { WalletPaymentFailure } from "../../types/failure"; +import { RptId } from "../../../../../../definitions/pagopa/ecommerce/RptId"; export type WalletPaymentState = { + rptId?: RptId; paymentDetails: pot.Pot< PaymentRequestsGetResponse, NetworkError | WalletPaymentFailure @@ -68,6 +70,7 @@ const reducer = ( case getType(walletPaymentGetDetails.request): return { ...state, + rptId: action.payload, paymentDetails: pot.toLoading(state.paymentDetails) }; case getType(walletPaymentGetDetails.success): diff --git a/ts/features/walletV3/payment/store/selectors/index.ts b/ts/features/walletV3/payment/store/selectors/index.ts index 8bcbdc4f1cc..20afcaaf1b8 100644 --- a/ts/features/walletV3/payment/store/selectors/index.ts +++ b/ts/features/walletV3/payment/store/selectors/index.ts @@ -7,10 +7,16 @@ import { GlobalState } from "../../../../../store/reducers/types"; const selectWalletPayment = (state: GlobalState) => state.features.wallet.payment; +export const walletPaymentRptIdSelector = createSelector( + selectWalletPayment, + state => state.rptId +); + export const walletPaymentDetailsSelector = createSelector( selectWalletPayment, state => state.paymentDetails ); + export const walletPaymentAmountSelector = createSelector( walletPaymentDetailsSelector, state => pot.map(state, payment => payment.amount) @@ -20,6 +26,7 @@ export const walletPaymentAllMethodsSelector = createSelector( selectWalletPayment, state => pot.map(state.allPaymentMethods, _ => _.paymentMethods ?? []) ); + export const walletPaymentGenericMethodByIdSelector = createSelector( walletPaymentAllMethodsSelector, methodsPot => (id: string) => diff --git a/ts/features/walletV3/payment/types/PaymentOutcomeEnum.ts b/ts/features/walletV3/payment/types/PaymentOutcomeEnum.ts index 1b9d670f07a..dd1ff939ff8 100644 --- a/ts/features/walletV3/payment/types/PaymentOutcomeEnum.ts +++ b/ts/features/walletV3/payment/types/PaymentOutcomeEnum.ts @@ -15,7 +15,8 @@ export enum WalletPaymentOutcomeEnum { EXCESSIVE_AMOUNT = "10", // Excess of availability ORDER_NOT_PRESENT = "11", // (should never happen) INVALID_METHOD = "12", // (should never happen) - KO_RETRIABLE = "13" // transaction failed + KO_RETRIABLE = "13", // transaction failed + INVALID_SESSION = "14" // transaction failed } export type WalletPaymentOutcome = t.TypeOf; @@ -23,3 +24,13 @@ export const WalletPaymentOutcome = enumType( WalletPaymentOutcomeEnum, "WalletPaymentOutcome" ); + +export function getWalletPaymentOutcomeEnumByValue( + value: string +): string | undefined { + return ( + Object.keys(WalletPaymentOutcomeEnum) as Array< + keyof typeof WalletPaymentOutcomeEnum + > + ).find(key => WalletPaymentOutcomeEnum[key] === value); +}