diff --git a/package.json b/package.json index b96559cb614..4836709eaa4 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,7 @@ "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/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", + "pagopa_api_ecommerce": "https://raw.githubusercontent.com/pagopa/pagopa-infra/a5299db39de86a951d353fdc2bfed48188c0c125/src/domains/ecommerce-app/api/ecommerce-io/v1/_openapi.json.tpl", "private": true, "scripts": { "start": "react-native start", diff --git a/ts/features/walletV3/payment/saga/networking/__tests__/handleWalletPaymentCalculateFees.test.ts b/ts/features/walletV3/payment/saga/networking/__tests__/handleWalletPaymentCalculateFees.test.ts index 7295c8816bb..85e26423685 100644 --- a/ts/features/walletV3/payment/saga/networking/__tests__/handleWalletPaymentCalculateFees.test.ts +++ b/ts/features/walletV3/payment/saga/networking/__tests__/handleWalletPaymentCalculateFees.test.ts @@ -6,16 +6,16 @@ import { PaymentMethodStatusEnum } from "../../../../../../../definitions/pagopa import { getGenericError } from "../../../../../../utils/errors"; import { readablePrivacyReport } from "../../../../../../utils/reporters"; import { withRefreshApiCall } from "../../../../../fastLogin/saga/utils"; -import { - WalletPaymentCalculateFeesPayload, - walletPaymentCalculateFees -} from "../../../store/actions/networking"; +import { walletPaymentCalculateFees } from "../../../store/actions/networking"; import { handleWalletPaymentCalculateFees } from "../handleWalletPaymentCalculateFees"; +import { CalculateFeeRequest } from "../../../../../../../definitions/pagopa/ecommerce/CalculateFeeRequest"; describe("Test handleWalletPaymentCalculateFees saga", () => { - const calculateFeesPayload: WalletPaymentCalculateFeesPayload = { - walletId: "1234", - paymentAmountInCents: 1234 + const calculateFeesPayload: CalculateFeeRequest & { + paymentMethodId: string; + } = { + paymentMethodId: "1234", + paymentAmount: 1234 }; it(`should put ${getType( diff --git a/ts/features/walletV3/payment/saga/networking/handleWalletPaymentCalculateFees.ts b/ts/features/walletV3/payment/saga/networking/handleWalletPaymentCalculateFees.ts index b49fa030ad4..aeda5074d1b 100644 --- a/ts/features/walletV3/payment/saga/networking/handleWalletPaymentCalculateFees.ts +++ b/ts/features/walletV3/payment/saga/networking/handleWalletPaymentCalculateFees.ts @@ -3,7 +3,6 @@ import * as O from "fp-ts/lib/Option"; import { call, put, select } from "typed-redux-saga/macro"; import { ActionType } from "typesafe-actions"; -import { CalculateFeeRequest } from "../../../../../../definitions/pagopa/ecommerce/CalculateFeeRequest"; import { SagaCallReturnType } from "../../../../../types/utils"; import { getGenericError, getNetworkError } from "../../../../../utils/errors"; import { readablePrivacyReport } from "../../../../../utils/reporters"; @@ -19,14 +18,10 @@ export function* handleWalletPaymentCalculateFees( calculateFees: PaymentClient["calculateFees"], action: ActionType<(typeof walletPaymentCalculateFees)["request"]> ) { - const requestBody: CalculateFeeRequest = { - paymentAmount: action.payload.paymentAmountInCents, - walletId: action.payload.walletId - }; - + const { paymentMethodId, ...body } = action.payload; const calculateFeesRequest = calculateFees({ - id: action.payload.walletId, - body: requestBody + id: paymentMethodId, + body }); try { diff --git a/ts/features/walletV3/payment/saga/networking/handleWalletPaymentCreateTransaction.ts b/ts/features/walletV3/payment/saga/networking/handleWalletPaymentCreateTransaction.ts index 609df9291e4..97d6ffd9802 100644 --- a/ts/features/walletV3/payment/saga/networking/handleWalletPaymentCreateTransaction.ts +++ b/ts/features/walletV3/payment/saga/networking/handleWalletPaymentCreateTransaction.ts @@ -34,6 +34,7 @@ export function* handleWalletPaymentCreateTransaction( }), ({ status, value }) => { if (status === 200) { + action.payload.onSucces?.(); return walletPaymentCreateTransaction.success(value); } else if (status === 400) { return walletPaymentCreateTransaction.failure({ diff --git a/ts/features/walletV3/payment/screens/WalletPaymentConfirmScreen.tsx b/ts/features/walletV3/payment/screens/WalletPaymentConfirmScreen.tsx index 11c6b47f9b0..b8328f6f70c 100644 --- a/ts/features/walletV3/payment/screens/WalletPaymentConfirmScreen.tsx +++ b/ts/features/walletV3/payment/screens/WalletPaymentConfirmScreen.tsx @@ -5,7 +5,7 @@ import { VSpacer } from "@pagopa/io-app-design-system"; import * as pot from "@pagopa/ts-commons/lib/pot"; -import { useFocusEffect, useNavigation } from "@react-navigation/native"; +import { useNavigation } from "@react-navigation/native"; import { sequenceS } from "fp-ts/lib/Apply"; import * as O from "fp-ts/lib/Option"; import { pipe } from "fp-ts/lib/function"; @@ -18,11 +18,11 @@ import { AppParamsList, IOStackNavigationProp } from "../../../../navigation/params/AppParamsList"; -import { useIODispatch, useIOSelector } from "../../../../store/hooks"; +import { useIOSelector } from "../../../../store/hooks"; import { emptyContextualHelp } from "../../../../utils/emptyContextualHelp"; import { WalletPaymentConfirmContent } from "../components/WalletPaymentConfirmContent"; +import { useWalletPaymentAuthorizationModal } from "../hooks/useWalletPaymentAuthorizationModal"; import { WalletPaymentRoutes } from "../navigation/routes"; -import { walletPaymentCreateTransaction } from "../store/actions/networking"; import { walletPaymentDetailsSelector, walletPaymentPickedPaymentMethodSelector, @@ -30,10 +30,8 @@ import { walletPaymentTransactionSelector } from "../store/selectors"; import { WalletPaymentOutcome } from "../types/PaymentOutcomeEnum"; -import { useWalletPaymentAuthorizationModal } from "../hooks/useWalletPaymentAuthorizationModal"; const WalletPaymentConfirmScreen = () => { - const dispatch = useIODispatch(); const navigation = useNavigation>(); const paymentDetailsPot = useIOSelector(walletPaymentDetailsSelector); @@ -43,9 +41,6 @@ const WalletPaymentConfirmScreen = () => { ); const selectedPspOption = useIOSelector(walletPaymentPickedPspSelector); - const isTransactionLoading = pot.isLoading(transactionPot); - const isTransactionError = pot.isError(transactionPot); - useHeaderSecondLevel({ title: "", contextualHelp: emptyContextualHelp, @@ -53,12 +48,6 @@ const WalletPaymentConfirmScreen = () => { supportRequest: true }); - useFocusEffect( - React.useCallback(() => { - dispatch(walletPaymentCreateTransaction.request({ paymentNotices: [] })); - }, [dispatch]) - ); - const handleStartPaymentAuthorization = () => pipe( sequenceS(O.Monad)({ @@ -96,9 +85,8 @@ const WalletPaymentConfirmScreen = () => { onAuthorizationOutcome: handleAuthorizationOutcome }); - const isLoading = - isTransactionLoading || isAuthUrlLoading || isPendingAuthorization; - const isError = isTransactionError || isAuthUrlError; + const isLoading = isAuthUrlLoading || isPendingAuthorization; + const isError = isAuthUrlError; if (isError) { // TODO: Failure handling (https://pagopa.atlassian.net/browse/IOBP-471) diff --git a/ts/features/walletV3/payment/screens/WalletPaymentPickMethodScreen.tsx b/ts/features/walletV3/payment/screens/WalletPaymentPickMethodScreen.tsx index bed3a5835dc..da133cd3e8a 100644 --- a/ts/features/walletV3/payment/screens/WalletPaymentPickMethodScreen.tsx +++ b/ts/features/walletV3/payment/screens/WalletPaymentPickMethodScreen.tsx @@ -11,7 +11,7 @@ import { } from "@pagopa/io-app-design-system"; import * as pot from "@pagopa/ts-commons/lib/pot"; import { useFocusEffect, useNavigation } from "@react-navigation/native"; -import { sequenceS } from "fp-ts/lib/Apply"; +import { sequenceS, sequenceT } from "fp-ts/lib/Apply"; import * as A from "fp-ts/lib/Array"; import * as O from "fp-ts/lib/Option"; import { pipe } from "fp-ts/lib/function"; @@ -30,19 +30,24 @@ import { useIODispatch, useIOSelector } from "../../../../store/hooks"; import { ComponentProps } from "../../../../types/react"; import { emptyContextualHelp } from "../../../../utils/emptyContextualHelp"; import { findFirstCaseInsensitive } from "../../../../utils/object"; +import { UIWalletInfoDetails } from "../../details/types/UIWalletInfoDetails"; +import { WalletPaymentMissingMethodsError } from "../components/WalletPaymentMissingMethodsError"; +import { useWalletPaymentGoBackHandler } from "../hooks/useWalletPaymentGoBackHandler"; import { WalletPaymentRoutes } from "../navigation/routes"; -import { walletPaymentGetUserWallets } from "../store/actions/networking"; +import { + walletPaymentCreateTransaction, + walletPaymentGetUserWallets +} from "../store/actions/networking"; import { walletPaymentPickPaymentMethod } from "../store/actions/orchestration"; import { walletPaymentAllMethodsSelector, walletPaymentAmountSelector, + walletPaymentDetailsSelector, walletPaymentSavedMethodByIdSelector, walletPaymentTransactionSelector, walletPaymentUserWalletsSelector } from "../store/selectors"; -import { WalletPaymentMissingMethodsError } from "../components/WalletPaymentMissingMethodsError"; -import { useWalletPaymentGoBackHandler } from "../hooks/useWalletPaymentGoBackHandler"; -import { UIWalletInfoDetails } from "../../details/types/UIWalletInfoDetails"; +import { WalletPaymentOutcomeEnum } from "../types/PaymentOutcomeEnum"; type SavedMethodState = { kind: "saved"; @@ -72,6 +77,7 @@ const WalletPaymentPickMethodScreen = () => { supportRequest: true }); + const paymentDetailsPot = useIOSelector(walletPaymentDetailsSelector); const transactionPot = useIOSelector(walletPaymentTransactionSelector); const getSavedtMethodById = useIOSelector( walletPaymentSavedMethodByIdSelector @@ -84,22 +90,19 @@ const WalletPaymentPickMethodScreen = () => { const alertRef = React.useRef(null); const isLoading = - pot.isLoading(paymentMethodsPot) || - pot.isLoading(userWalletsPots) || - pot.isLoading(transactionPot); + pot.isLoading(paymentMethodsPot) || pot.isLoading(userWalletsPots); + const isLoadingTransaction = pot.isLoading(transactionPot); + + const isError = + pot.isError(transactionPot) || + pot.isError(paymentMethodsPot) || + pot.isError(userWalletsPots); const [shouldShowWarningBanner, setShouldShowWarningBanner] = React.useState(false); const [selectedMethod, setSelectedMethod] = React.useState(undefined); - useHeaderSecondLevel({ - title: "", - contextualHelp: emptyContextualHelp, - faqCategories: ["payment"], - supportRequest: true - }); - useFocusEffect( React.useCallback(() => { // dispatch(walletPaymentGetAllMethods.request()); // currently we do not allow onboarding new methods in payment flow @@ -107,6 +110,17 @@ const WalletPaymentPickMethodScreen = () => { }, [dispatch]) ); + React.useEffect(() => { + if (isError) { + navigation.navigate(WalletPaymentRoutes.WALLET_PAYMENT_MAIN, { + screen: WalletPaymentRoutes.WALLET_PAYMENT_OUTCOME, + params: { + outcome: WalletPaymentOutcomeEnum.GENERIC_ERROR + } + }); + } + }, [isError, navigation]); + const paymentAmount = pot.getOrElse(paymentAmountPot, undefined); const canContinue = selectedMethod !== undefined; @@ -152,19 +166,32 @@ const WalletPaymentPickMethodScreen = () => { }; */ + const navigateToPspSelectionScreen = () => { + navigation.navigate(WalletPaymentRoutes.WALLET_PAYMENT_MAIN, { + screen: WalletPaymentRoutes.WALLET_PAYMENT_PICK_PSP + }); + }; + const handleContinue = () => { // todo:: should handle the case where the user // selects a non saved method if (selectedMethod?.kind === "saved") { pipe( - getSavedtMethodById(selectedMethod.walletId), - O.map(walletPaymentPickPaymentMethod), - O.map(dispatch), - O.map(() => - navigation.navigate(WalletPaymentRoutes.WALLET_PAYMENT_MAIN, { - screen: WalletPaymentRoutes.WALLET_PAYMENT_PICK_PSP - }) - ) + sequenceT(O.Monad)( + getSavedtMethodById(selectedMethod.walletId), + pot.toOption(paymentDetailsPot) + ), + O.map(([method, paymentDetails]) => { + dispatch(walletPaymentPickPaymentMethod(method)); + dispatch( + walletPaymentCreateTransaction.request({ + paymentNotices: [ + { rptId: paymentDetails.rptId, amount: paymentDetails.amount } + ], + onSucces: navigateToPspSelectionScreen + }) + ); + }) ); } }; @@ -185,13 +212,17 @@ const WalletPaymentPickMethodScreen = () => { return (

{I18n.t("wallet.payment.methodSelection.header")}

diff --git a/ts/features/walletV3/payment/screens/WalletPaymentPickPspScreen.tsx b/ts/features/walletV3/payment/screens/WalletPaymentPickPspScreen.tsx index b331f4c1ddc..f2ae6b02aad 100644 --- a/ts/features/walletV3/payment/screens/WalletPaymentPickPspScreen.tsx +++ b/ts/features/walletV3/payment/screens/WalletPaymentPickPspScreen.tsx @@ -36,9 +36,11 @@ import { walletPaymentAmountSelector, walletPaymentPickedPaymentMethodSelector, walletPaymentPickedPspSelector, - walletPaymentPspListSelector + walletPaymentPspListSelector, + walletPaymentTransactionTransferListSelector } from "../store/selectors"; import { WalletPaymentPspSortType } from "../types"; +import { WalletPaymentOutcomeEnum } from "../types/PaymentOutcomeEnum"; const WalletPaymentPickPspScreen = () => { const paymentAmountPot = useIOSelector(walletPaymentAmountSelector); @@ -51,8 +53,16 @@ const WalletPaymentPickPspScreen = () => { const [sortType, setSortType] = React.useState("default"); + const transactionTransferListPot = useIOSelector( + walletPaymentTransactionTransferListSelector + ); const pspListPot = useIOSelector(walletPaymentPspListSelector); + const selectedPspOption = useIOSelector(walletPaymentPickedPspSelector); + const isLoading = pot.isLoading(pspListPot); + const isError = pot.isError(pspListPot); + + const canContinue = O.isSome(selectedPspOption); const sortedPspList = pipe( pot.toOption(pspListPot), @@ -60,9 +70,16 @@ const WalletPaymentPickPspScreen = () => { O.toUndefined ); - const selectedPspOption = useIOSelector(walletPaymentPickedPspSelector); - - const canContinue = O.isSome(selectedPspOption); + React.useEffect(() => { + if (isError) { + navigation.navigate(WalletPaymentRoutes.WALLET_PAYMENT_MAIN, { + screen: WalletPaymentRoutes.WALLET_PAYMENT_OUTCOME, + params: { + outcome: WalletPaymentOutcomeEnum.GENERIC_ERROR + } + }); + } + }, [isError, navigation]); useHeaderSecondLevel({ title: "", @@ -76,18 +93,25 @@ const WalletPaymentPickPspScreen = () => { pipe( sequenceT(O.Monad)( pot.toOption(paymentAmountPot), + pot.toOption(transactionTransferListPot), selectedWalletOption ), - O.map(([paymentAmountInCents, selectedWallet]) => { + O.map(([paymentAmountInCents, transferList, selectedWallet]) => { dispatch( walletPaymentCalculateFees.request({ - walletId: selectedWallet.walletId, - paymentAmountInCents + paymentMethodId: selectedWallet.paymentMethodId, + paymentAmount: paymentAmountInCents, + transferList }) ); }) ); - }, [dispatch, paymentAmountPot, selectedWalletOption]) + }, [ + dispatch, + paymentAmountPot, + selectedWalletOption, + transactionTransferListPot + ]) ); React.useEffect( @@ -172,13 +196,17 @@ const WalletPaymentPickPspScreen = () => { return ( {!isLoading && ( diff --git a/ts/features/walletV3/payment/store/actions/networking.ts b/ts/features/walletV3/payment/store/actions/networking.ts index 4e457460086..92b4594490b 100644 --- a/ts/features/walletV3/payment/store/actions/networking.ts +++ b/ts/features/walletV3/payment/store/actions/networking.ts @@ -10,6 +10,7 @@ import { PaymentMethodsResponse } from "../../../../../../definitions/pagopa/wal import { Wallets } from "../../../../../../definitions/pagopa/walletv3/Wallets"; import { NetworkError } from "../../../../../utils/errors"; import { WalletPaymentFailure } from "../../types/failure"; +import { CalculateFeeRequest } from "../../../../../../definitions/pagopa/ecommerce/CalculateFeeRequest"; export const walletPaymentGetDetails = createAsyncAction( "WALLET_PAYMENT_GET_DETAILS_REQUEST", @@ -29,23 +30,22 @@ export const walletPaymentGetUserWallets = createAsyncAction( "WALLET_PAYMENT_GET_USER_WALLETS_FAILURE" )(); -export type WalletPaymentCalculateFeesPayload = { - walletId: string; - paymentAmountInCents: number; -}; - export const walletPaymentCalculateFees = createAsyncAction( "WALLET_PAYMET_CALCULATE_FEES_REQUEST", "WALLET_PAYMET_CALCULATE_FEES_SUCCESS", "WALLET_PAYMET_CALCULATE_FEES_FAILURE" -)(); +)< + CalculateFeeRequest & { paymentMethodId: string }, + CalculateFeeResponse, + NetworkError +>(); export const walletPaymentCreateTransaction = createAsyncAction( "WALLET_PAYMENT_CREATE_TRANSACTION_REQUEST", "WALLET_PAYMENT_CREATE_TRANSACTION_SUCCESS", "WALLET_PAYMENT_CREATE_TRANSACTION_FAILURE" )< - NewTransactionRequest, + NewTransactionRequest & { onSucces?: () => void }, NewTransactionResponse, NetworkError | WalletPaymentFailure >(); diff --git a/ts/features/walletV3/payment/store/selectors/index.ts b/ts/features/walletV3/payment/store/selectors/index.ts index e122cb38c2f..e46cf27e914 100644 --- a/ts/features/walletV3/payment/store/selectors/index.ts +++ b/ts/features/walletV3/payment/store/selectors/index.ts @@ -3,6 +3,7 @@ import * as O from "fp-ts/lib/Option"; import { pipe } from "fp-ts/lib/function"; import { createSelector } from "reselect"; import { GlobalState } from "../../../../../store/reducers/types"; +import { Transfer } from "../../../../../../definitions/pagopa/ecommerce/Transfer"; const selectWalletPayment = (state: GlobalState) => state.features.wallet.payment; @@ -72,6 +73,17 @@ export const walletPaymentTransactionSelector = createSelector( state => state.transaction ); +export const walletPaymentTransactionTransferListSelector = createSelector( + walletPaymentTransactionSelector, + transaction => + pot.map(transaction, t => + t.payments.reduce( + (a, p) => [...a, ...(p.transferList ?? [])], + [] as ReadonlyArray + ) + ) +); + export const walletPaymentAuthorizationUrlSelector = createSelector( selectWalletPayment, state => state.authorizationUrl