From 5fdaa06f85ba916cefed1e01505c52a27c4703ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=94=D0=B5=D0=BD=D0=B8=D1=81=20=D0=9A=D0=B0=D1=82=D0=B0?= =?UTF-8?q?=D0=B5=D0=B2?= Date: Tue, 11 Jun 2024 19:23:06 +0000 Subject: [PATCH] AW-104-stripe-button --- public/google-pay-mark.png | Bin 0 -> 16605 bytes public/link-pay-mark.png | Bin 0 -> 1092 bytes .../methods/ApplePayButton/index.tsx | 121 ------------------ .../CheckAvailableStripeButton/index.tsx | 33 +++++ .../methods/StripeButton/index.tsx | 27 ++++ .../styles.module.css | 0 .../SubPlanInformation/TotalToday/index.tsx | 17 --- .../TotalToday/styles.module.css | 17 --- src/components/SubPlanInformation/index.tsx | 37 ------ .../SubPlanInformation/styles.module.css | 31 ----- .../components/PaymentModal/index.tsx | 28 ++-- .../components/PaymentModal/index.tsx | 26 ++-- .../PaymentButtons/index.tsx | 30 ++++- .../PaymentButtons/styles.module.css | 2 +- src/data/paymentMethods.tsx | 43 +++++-- src/hooks/payment/useCanUseStripeButton.ts | 98 ++++++++++++++ src/store/index.ts | 2 + src/store/payment.ts | 19 +++ 18 files changed, 272 insertions(+), 259 deletions(-) create mode 100644 public/google-pay-mark.png create mode 100644 public/link-pay-mark.png delete mode 100644 src/components/PaymentPage/methods/ApplePayButton/index.tsx create mode 100644 src/components/PaymentPage/methods/StripeButton/CheckAvailableStripeButton/index.tsx create mode 100644 src/components/PaymentPage/methods/StripeButton/index.tsx rename src/components/PaymentPage/methods/{ApplePayButton => StripeButton}/styles.module.css (100%) delete mode 100644 src/components/SubPlanInformation/TotalToday/index.tsx delete mode 100644 src/components/SubPlanInformation/TotalToday/styles.module.css delete mode 100644 src/components/SubPlanInformation/index.tsx delete mode 100644 src/components/SubPlanInformation/styles.module.css create mode 100644 src/hooks/payment/useCanUseStripeButton.ts diff --git a/public/google-pay-mark.png b/public/google-pay-mark.png new file mode 100644 index 0000000000000000000000000000000000000000..07cd76a672e32757d65f34dccf73281970b92140 GIT binary patch literal 16605 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 KMNUMnLSTaDl>h1g literal 0 HcmV?d00001 diff --git a/src/components/PaymentPage/methods/ApplePayButton/index.tsx b/src/components/PaymentPage/methods/ApplePayButton/index.tsx deleted file mode 100644 index 6e88090..0000000 --- a/src/components/PaymentPage/methods/ApplePayButton/index.tsx +++ /dev/null @@ -1,121 +0,0 @@ -import { useEffect, useState } from "react"; -import { - PaymentRequestButtonElement, - useStripe, - useElements, -} from "@stripe/react-stripe-js"; -import { PaymentRequest } from "@stripe/stripe-js"; -import styles from "./styles.module.css"; -import { useDispatch } from "react-redux"; -import { useNavigate } from "react-router-dom"; -import routes from "@/routes"; -import { IPaywallProduct } from "@/api/resources/Paywall"; - -interface ApplePayButtonProps { - activeProduct: IPaywallProduct | null; - client_secret: string; - subscriptionReceiptId?: string; - returnUrl?: string; - setCanMakePayment?: (isCanMakePayment: boolean) => void; -} - -function ApplePayButton({ - activeProduct, - client_secret, - subscriptionReceiptId, - returnUrl, - setCanMakePayment, -}: ApplePayButtonProps) { - const stripe = useStripe(); - const elements = useElements(); - const dispatch = useDispatch(); - const navigate = useNavigate(); - const [paymentRequest, setPaymentRequest] = useState( - null - ); - - const getAmountFromProduct = (subPlan: IPaywallProduct) => { - if (subPlan.isTrial) { - return subPlan.trialPrice; - } - return subPlan.price; - }; - - useEffect(() => { - if (!stripe || !elements || !activeProduct) { - return; - } - - const pr = stripe.paymentRequest({ - country: "US", - currency: "usd", - total: { - label: activeProduct.name || "Subscription", - amount: getAmountFromProduct(activeProduct), - }, - requestPayerName: true, - requestPayerEmail: true, - }); - - pr.canMakePayment().then((result) => { - if (result) { - setPaymentRequest(pr); - setCanMakePayment?.(true); - } - }); - - pr.on("paymentmethod", async (e) => { - const { error: stripeError, paymentIntent } = - await stripe.confirmCardPayment( - client_secret, - { - payment_method: e.paymentMethod.id, - }, - { handleActions: false } - ); - paymentIntent; - - if (stripeError) { - // Show error to your customer (e.g., insufficient funds) - navigate( - `${routes.client.paymentResult()}/${subscriptionReceiptId}/?redirect_status=failed` - ); - return e.complete("fail"); - } - navigate( - returnUrl || - `${routes.client.paymentResult()}/${subscriptionReceiptId}/?redirect_status=succeeded` - ); - e.complete("success"); - // Show a success message to your customer - // There's a risk of the customer closing the window before callback - // execution. Set up a webhook or plugin to listen for the - // payment_intent.succeeded event that handles any business critical - // post-payment actions. - }); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [ - client_secret, - dispatch, - elements, - navigate, - stripe, - subscriptionReceiptId, - ]); - - return ( - <> - {paymentRequest && ( - - )} - - ); -} - -export default ApplePayButton; diff --git a/src/components/PaymentPage/methods/StripeButton/CheckAvailableStripeButton/index.tsx b/src/components/PaymentPage/methods/StripeButton/CheckAvailableStripeButton/index.tsx new file mode 100644 index 0000000..3b686d5 --- /dev/null +++ b/src/components/PaymentPage/methods/StripeButton/CheckAvailableStripeButton/index.tsx @@ -0,0 +1,33 @@ +import { IPaywallProduct } from "@/api/resources/Paywall"; +import { useCanUseStripeButton } from "@/hooks/payment/useCanUseStripeButton"; +import { actions } from "@/store"; +import { useEffect } from "react"; +import { useDispatch } from "react-redux"; + +interface ICheckAvailableStripeButtonProps { + activeProduct: IPaywallProduct | null; + clientSecret: string; +} + +function CheckAvailableStripeButton({ + activeProduct, + clientSecret, +}: ICheckAvailableStripeButtonProps) { + const dispatch = useDispatch(); + const { paymentRequest, availableMethods } = useCanUseStripeButton({ + activeProduct, + client_secret: clientSecret, + }); + + useEffect(() => { + if (paymentRequest && availableMethods) { + dispatch( + actions.payment.updateStripeButton({ paymentRequest, availableMethods }) + ); + } + }, [availableMethods, dispatch, paymentRequest]); + + return <>; +} + +export default CheckAvailableStripeButton; diff --git a/src/components/PaymentPage/methods/StripeButton/index.tsx b/src/components/PaymentPage/methods/StripeButton/index.tsx new file mode 100644 index 0000000..9b47aa1 --- /dev/null +++ b/src/components/PaymentPage/methods/StripeButton/index.tsx @@ -0,0 +1,27 @@ +import { PaymentRequestButtonElement } from "@stripe/react-stripe-js"; +import { CanMakePaymentResult, PaymentRequest } from "@stripe/stripe-js"; +import styles from "./styles.module.css"; + +export type TCanMakePaymentResult = CanMakePaymentResult | null; + +interface ApplePayButtonProps { + paymentRequest: PaymentRequest; +} + +function StripeButton({ paymentRequest }: ApplePayButtonProps) { + return ( + <> + {paymentRequest && ( + + )} + + ); +} + +export default StripeButton; diff --git a/src/components/PaymentPage/methods/ApplePayButton/styles.module.css b/src/components/PaymentPage/methods/StripeButton/styles.module.css similarity index 100% rename from src/components/PaymentPage/methods/ApplePayButton/styles.module.css rename to src/components/PaymentPage/methods/StripeButton/styles.module.css diff --git a/src/components/SubPlanInformation/TotalToday/index.tsx b/src/components/SubPlanInformation/TotalToday/index.tsx deleted file mode 100644 index d36b3c4..0000000 --- a/src/components/SubPlanInformation/TotalToday/index.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import styles from "./styles.module.css"; -import Title from "@/components/Title"; - -interface ITotalTodayProps { - total: string; -} - -function TotalToday({ total }: ITotalTodayProps): JSX.Element { - return ( -
- {"Total today:"} - {total} -
- ); -} - -export default TotalToday; diff --git a/src/components/SubPlanInformation/TotalToday/styles.module.css b/src/components/SubPlanInformation/TotalToday/styles.module.css deleted file mode 100644 index fe619d9..0000000 --- a/src/components/SubPlanInformation/TotalToday/styles.module.css +++ /dev/null @@ -1,17 +0,0 @@ -.container { - width: 100%; - padding: 16px; - display: flex; - flex-direction: row; - justify-content: space-between; - align-items: center; - background-color: #e7f5ee; - border-radius: 7px; -} - -.text { - font-size: 16px; - font-weight: 700; - color: #000; - margin: 0; -} diff --git a/src/components/SubPlanInformation/index.tsx b/src/components/SubPlanInformation/index.tsx deleted file mode 100644 index c872f5c..0000000 --- a/src/components/SubPlanInformation/index.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import { useTranslation } from "react-i18next"; -import styles from "./styles.module.css"; -import TotalToday from "./TotalToday"; -import ApplePayButton from "../PaymentPage/methods/ApplePayButton"; -import { IPaywallProduct } from "@/api/resources/Paywall"; - -interface ISubPlanInformationProps { - product: IPaywallProduct; - client_secret?: string; -} - -const getPrice = (product: IPaywallProduct): string => { - return `$${ - (product.trialPrice === 100 ? 99 : product.trialPrice || 0) / 100 - }`; -}; - -function SubPlanInformation({ - product, - client_secret, -}: ISubPlanInformationProps): JSX.Element { - const { t } = useTranslation(); - - return ( -
- - {client_secret && ( - - )} -

- {t("auweb.pay.information").replaceAll("%@", getPrice(product))}. -

-
- ); -} - -export default SubPlanInformation; diff --git a/src/components/SubPlanInformation/styles.module.css b/src/components/SubPlanInformation/styles.module.css deleted file mode 100644 index 7c6cd7d..0000000 --- a/src/components/SubPlanInformation/styles.module.css +++ /dev/null @@ -1,31 +0,0 @@ -.container { - width: 100%; - max-width: 300px; - display: flex; - flex-direction: column; - gap: 20px; -} - -.description { - font-size: 13px; - color: #666666; - text-align: left; - font-weight: 400; - line-height: 16px; - padding-bottom: 16px; -} - -.pay-pal-button { - width: 100%; - height: 60px; - display: flex; - align-items: center; - justify-content: center; - background-color: #ffc43a; - border-radius: 7px; -} - -.errors { - color: red; - text-align: center; -} diff --git a/src/components/pages/ABDesign/v1/pages/TrialPayment/components/PaymentModal/index.tsx b/src/components/pages/ABDesign/v1/pages/TrialPayment/components/PaymentModal/index.tsx index 5f14274..4b4412d 100644 --- a/src/components/pages/ABDesign/v1/pages/TrialPayment/components/PaymentModal/index.tsx +++ b/src/components/pages/ABDesign/v1/pages/TrialPayment/components/PaymentModal/index.tsx @@ -4,7 +4,6 @@ import PaymentMethodsChoice from "../PaymentMethodsChoice"; import { useEffect, useMemo, useState } from "react"; import { EPaymentMethod, paymentMethods } from "@/data/paymentMethods"; import { Elements } from "@stripe/react-stripe-js"; -import ApplePayButton from "@/components/PaymentPage/methods/ApplePayButton"; import CheckoutForm from "@/components/PaymentPage/methods/CheckoutForm"; import { Stripe, loadStripe } from "@stripe/stripe-js"; import { useSelector } from "react-redux"; @@ -16,6 +15,8 @@ import SecurityPayments from "../SecurityPayments"; import { EPlacementKeys, IPaywallProduct } from "@/api/resources/Paywall"; import { usePaywall } from "@/hooks/paywall/usePaywall"; import { useMakePayment } from "@/hooks/payment/useMakePayment"; +import StripeButton from "@/components/PaymentPage/methods/StripeButton"; +import CheckAvailableStripeButton from "@/components/PaymentPage/methods/StripeButton/CheckAvailableStripeButton"; interface IPaymentModalProps { activeProduct?: IPaywallProduct; @@ -41,7 +42,7 @@ function PaymentModal({ const activeProductFromStore = useSelector(selectors.selectActiveProduct); const _activeProduct = activeProduct ? activeProduct : activeProductFromStore; const { products, paywallId, placementId } = usePaywall({ placementKey }); - + const { paymentIntentId, clientSecret, @@ -57,16 +58,21 @@ function PaymentModal({ returnPaidUrl: returnUrl, }); + const stripeButton = useSelector(selectors.selectStripeButton); + + const paymentRequest = stripeButton?.paymentRequest; + const availableMethods = stripeButton?.availableMethods; + if (checkoutUrl?.length) { window.location.href = checkoutUrl; } const paymentMethodsButtons = useMemo(() => { - return paymentMethods; - }, []); + return paymentMethods(availableMethods); + }, [availableMethods]); const [selectedPaymentMethod, setSelectedPaymentMethod] = useState( - EPaymentMethod.PAYMENT_BUTTONS + paymentMethodsButtons[0].id ); const onSelectPaymentMethod = (method: EPaymentMethod) => { @@ -138,13 +144,15 @@ function PaymentModal({
{stripePromise && clientSecret && ( + {selectedPaymentMethod === EPaymentMethod.PAYMENT_BUTTONS && (
- + {paymentRequest && ( + + )}
)} diff --git a/src/components/pages/TrialPayment/components/PaymentModal/index.tsx b/src/components/pages/TrialPayment/components/PaymentModal/index.tsx index a6f9908..8f11bdd 100644 --- a/src/components/pages/TrialPayment/components/PaymentModal/index.tsx +++ b/src/components/pages/TrialPayment/components/PaymentModal/index.tsx @@ -4,7 +4,6 @@ import PaymentMethodsChoice from "../PaymentMethodsChoice"; import { useEffect, useMemo, useState } from "react"; import { EPaymentMethod, paymentMethods } from "@/data/paymentMethods"; import { Elements } from "@stripe/react-stripe-js"; -import ApplePayButton from "@/components/PaymentPage/methods/ApplePayButton"; import CheckoutForm from "@/components/PaymentPage/methods/CheckoutForm"; import { Stripe, loadStripe } from "@stripe/stripe-js"; import { useSelector } from "react-redux"; @@ -14,6 +13,8 @@ import SecurityPayments from "../SecurityPayments"; import { EPlacementKeys, IPaywallProduct } from "@/api/resources/Paywall"; import { usePaywall } from "@/hooks/paywall/usePaywall"; import { useMakePayment } from "@/hooks/payment/useMakePayment"; +import StripeButton from "@/components/PaymentPage/methods/StripeButton"; +import CheckAvailableStripeButton from "@/components/PaymentPage/methods/StripeButton/CheckAvailableStripeButton"; interface IPaymentModalProps { activeProduct?: IPaywallProduct; @@ -59,16 +60,17 @@ function PaymentModal({ returnPaidUrl: returnUrl, }); + const { paymentRequest, availableMethods } = useSelector( + selectors.selectStripeButton + ); + if (checkoutUrl?.length) { window.location.href = checkoutUrl; } const paymentMethodsButtons = useMemo(() => { - // return paymentMethods.filter( - // (method) => method.id !== EPaymentMethod.PAYMENT_BUTTONS - // ); - return paymentMethods; - }, []); + return paymentMethods(availableMethods); + }, [availableMethods]); const [selectedPaymentMethod, setSelectedPaymentMethod] = useState( EPaymentMethod.PAYMENT_BUTTONS @@ -137,13 +139,15 @@ function PaymentModal({
{stripePromise && clientSecret && ( + {selectedPaymentMethod === EPaymentMethod.PAYMENT_BUTTONS && (
- + {paymentRequest && ( + + )}
)} diff --git a/src/components/ui/PaymentMethodsButtons/PaymentButtons/index.tsx b/src/components/ui/PaymentMethodsButtons/PaymentButtons/index.tsx index dd1721a..1faf69c 100644 --- a/src/components/ui/PaymentMethodsButtons/PaymentButtons/index.tsx +++ b/src/components/ui/PaymentMethodsButtons/PaymentButtons/index.tsx @@ -1,9 +1,31 @@ import styles from "./styles.module.css"; +import { TCanMakePaymentResult } from "@/components/PaymentPage/methods/StripeButton"; -function PaymentButtons() { - return
- ApplePay -
; +interface IPaymentButtonsProps { + availableMethods: TCanMakePaymentResult; +} + +function PaymentButtons({ availableMethods }: IPaymentButtonsProps) { + if (!availableMethods) return <>; + return ( +
+ {availableMethods["applePay"] && ( + ApplePay + )} + {availableMethods["googlePay"] && ( + google + )} + {availableMethods["link"] && ( + LinkPay + )} +
+ ); } export default PaymentButtons; diff --git a/src/components/ui/PaymentMethodsButtons/PaymentButtons/styles.module.css b/src/components/ui/PaymentMethodsButtons/PaymentButtons/styles.module.css index 7d1360f..0bad093 100644 --- a/src/components/ui/PaymentMethodsButtons/PaymentButtons/styles.module.css +++ b/src/components/ui/PaymentMethodsButtons/PaymentButtons/styles.module.css @@ -8,5 +8,5 @@ } .container > img { - height: 16px; + height: 22px; } \ No newline at end of file diff --git a/src/data/paymentMethods.tsx b/src/data/paymentMethods.tsx index 5428dc2..f827a26 100644 --- a/src/data/paymentMethods.tsx +++ b/src/data/paymentMethods.tsx @@ -1,3 +1,4 @@ +import { TCanMakePaymentResult } from "@/components/PaymentPage/methods/StripeButton"; import CreditCard from "@/components/ui/PaymentMethodsButtons/CreditCard"; import PaymentButtons from "@/components/ui/PaymentMethodsButtons/PaymentButtons"; @@ -11,13 +12,35 @@ export interface IPaymentMethod { component: JSX.Element; } -export const paymentMethods: IPaymentMethod[] = [ - { - id: EPaymentMethod.PAYMENT_BUTTONS, - component: , - }, - { - id: EPaymentMethod.CREDIT_CARD, - component: , - }, -]; +// export const paymentMethods: IPaymentMethod[] = [ +// { +// id: EPaymentMethod.PAYMENT_BUTTONS, +// component: , +// }, +// { +// id: EPaymentMethod.CREDIT_CARD, +// component: , +// }, +// ]; + +export function paymentMethods( + availableMethods: TCanMakePaymentResult +): IPaymentMethod[] { + let methods = [ + { + id: EPaymentMethod.PAYMENT_BUTTONS, + component: , + }, + { + id: EPaymentMethod.CREDIT_CARD, + component: , + }, + ]; + + if (!availableMethods) { + methods = methods.filter( + (method) => method.id !== EPaymentMethod.PAYMENT_BUTTONS + ); + } + return methods; +} diff --git a/src/hooks/payment/useCanUseStripeButton.ts b/src/hooks/payment/useCanUseStripeButton.ts new file mode 100644 index 0000000..f387d22 --- /dev/null +++ b/src/hooks/payment/useCanUseStripeButton.ts @@ -0,0 +1,98 @@ +import { IPaywallProduct } from "@/api/resources/Paywall"; +import routes from "@/routes"; +import { useElements, useStripe } from "@stripe/react-stripe-js"; +import { CanMakePaymentResult, PaymentRequest } from "@stripe/stripe-js"; +import { useEffect, useMemo, useState } from "react" +import { useNavigate } from "react-router-dom"; + +export type TCanMakePaymentResult = CanMakePaymentResult | null; + + +const getAmountFromProduct = (subPlan: IPaywallProduct) => { + if (subPlan.isTrial) { + return subPlan.trialPrice; + } + return subPlan.price; +}; + +interface IUseCanUseStripeButton { + activeProduct: IPaywallProduct | null; + client_secret: string; + subscriptionReceiptId?: string; + returnUrl?: string; + setCanMakePayment?: (canMakePayment: TCanMakePaymentResult) => void; + setCanMakePaymentLoading?: (loading: boolean) => void; +} + +export const useCanUseStripeButton = ({ + activeProduct, + client_secret, + subscriptionReceiptId, + returnUrl, +}: IUseCanUseStripeButton) => { + const stripe = useStripe(); + const elements = useElements(); + const [paymentRequest, setPaymentRequest] = useState( + null + ); + const [availableMethods, setAvailableMethods] = useState(null); + const navigate = useNavigate(); + + useEffect(() => { + if (!stripe || !elements || !activeProduct) { + return; + } + + const pr = stripe.paymentRequest({ + country: "US", + currency: "usd", + total: { + label: activeProduct.name || "Subscription", + amount: getAmountFromProduct(activeProduct), + }, + requestPayerName: true, + requestPayerEmail: true, + }); + + pr.canMakePayment() + .then((result) => { + if (result) { + setPaymentRequest(pr) + setAvailableMethods(result) + } + }) + + pr.on("paymentmethod", async (e) => { + const { error: stripeError, paymentIntent } = + await stripe.confirmCardPayment( + client_secret, + { + payment_method: e.paymentMethod.id, + }, + { handleActions: false } + ); + paymentIntent; + + if (stripeError) { + // Show error to your customer (e.g., insufficient funds) + navigate( + `${routes.client.paymentResult()}/${subscriptionReceiptId}/?redirect_status=failed` + ); + return e.complete("fail"); + } + navigate( + returnUrl || + `${routes.client.paymentResult()}/${subscriptionReceiptId}/?redirect_status=succeeded` + ); + e.complete("success"); + }); + }, [activeProduct, client_secret, elements, navigate, returnUrl, stripe, subscriptionReceiptId]); + + return useMemo(() => ({ + paymentRequest, + availableMethods + }), [ + paymentRequest, + availableMethods + ]) +} \ No newline at end of file diff --git a/src/store/index.ts b/src/store/index.ts index c1357a4..33550ea 100644 --- a/src/store/index.ts +++ b/src/store/index.ts @@ -32,6 +32,7 @@ import payment, { actions as paymentActions, selectActiveProduct, selectIsDiscount, + selectStripeButton, selectSubscriptionReceipt, } from "./payment"; import subscriptionPlans, { @@ -123,6 +124,7 @@ export const selectors = { selectPaywalls, selectPaywallsIsMustUpdate, selectPrivacyPolicy, + selectStripeButton, ...formSelectors, }; diff --git a/src/store/payment.ts b/src/store/payment.ts index 23dfc9d..50d4d06 100644 --- a/src/store/payment.ts +++ b/src/store/payment.ts @@ -1,13 +1,21 @@ import { IPaywallProduct } from "@/api/resources/Paywall"; import { SubscriptionReceipt } from "@/api/resources/UserSubscriptionReceipts"; +import { TCanMakePaymentResult } from "@/hooks/payment/useCanUseStripeButton"; import { createSlice, createSelector } from "@reduxjs/toolkit"; import type { PayloadAction } from "@reduxjs/toolkit"; +import { PaymentRequest } from "@stripe/stripe-js"; + +interface IStripeButton { + paymentRequest: PaymentRequest | null; + availableMethods: TCanMakePaymentResult; +} interface IPayment { selectedPrice: number | null; isDiscount: boolean; subscriptionReceipt: SubscriptionReceipt | null; activeProduct: IPaywallProduct | null; + stripeButton: IStripeButton; } const initialState: IPayment = { @@ -15,6 +23,10 @@ const initialState: IPayment = { isDiscount: false, subscriptionReceipt: null, activeProduct: null, + stripeButton: { + paymentRequest: null, + availableMethods: null, + } }; const paymentSlice = createSlice({ @@ -24,6 +36,9 @@ const paymentSlice = createSlice({ update(state, action: PayloadAction>) { return { ...state, ...action.payload }; }, + updateStripeButton(state, action: PayloadAction) { + return { ...state, stripeButton: action.payload }; + }, }, extraReducers: (builder) => builder.addCase("reset", () => initialState), }); @@ -45,4 +60,8 @@ export const selectSubscriptionReceipt = createSelector( (state: { payment: IPayment }) => state.payment.subscriptionReceipt, (payment) => payment ); +export const selectStripeButton = createSelector( + (state: { payment: IPayment }) => state.payment.stripeButton, + (payment) => payment +); export default paymentSlice.reducer;