From c92bb15d51c580fb6b76089ede200236724edf4e Mon Sep 17 00:00:00 2001 From: Roy Schut Date: Fri, 20 May 2022 14:06:43 +0200 Subject: [PATCH] feat(payment): add tvod to checkout --- .../CheckoutForm/CheckoutForm.test.tsx | 1 + src/components/CheckoutForm/CheckoutForm.tsx | 34 +++++++++++-------- .../AccountModal/forms/Checkout.tsx | 26 +++++++++----- .../AccountModal/forms/ChooseOffer.tsx | 16 +++++---- src/stores/CheckoutStore.ts | 5 +++ 5 files changed, 53 insertions(+), 29 deletions(-) diff --git a/src/components/CheckoutForm/CheckoutForm.test.tsx b/src/components/CheckoutForm/CheckoutForm.test.tsx index 11796e36d..d6c6409d7 100644 --- a/src/components/CheckoutForm/CheckoutForm.test.tsx +++ b/src/components/CheckoutForm/CheckoutForm.test.tsx @@ -24,6 +24,7 @@ describe('', () => { couponFormSubmitting={false} order={order as Order} offer={offer as Offer} + offerType={'svod'} submitting={false} />, ); diff --git a/src/components/CheckoutForm/CheckoutForm.tsx b/src/components/CheckoutForm/CheckoutForm.tsx index b6e521580..44b234e15 100644 --- a/src/components/CheckoutForm/CheckoutForm.tsx +++ b/src/components/CheckoutForm/CheckoutForm.tsx @@ -1,19 +1,21 @@ import React from 'react'; import { useTranslation } from 'react-i18next'; - -import Button from '../Button/Button'; -import type { Offer, Order, PaymentMethod } from '../../../types/checkout'; -import IconButton from '../IconButton/IconButton'; -import FormFeedback from '../FormFeedback/FormFeedback'; -import { formatPrice } from '../../utils/formatting'; -import Close from '../../icons/Close'; -import DialogBackButton from '../DialogBackButton/DialogBackButton'; -import PayPal from '../../icons/PayPal'; -import CreditCard from '../../icons/CreditCard'; -import LoadingOverlay from '../LoadingOverlay/LoadingOverlay'; +import classNames from 'classnames'; import styles from './CheckoutForm.module.scss'; +import Button from '#src/components/Button/Button'; +import type { Offer, Order, PaymentMethod } from '#types/checkout'; +import IconButton from '#src/components/IconButton/IconButton'; +import FormFeedback from '#src/components/FormFeedback/FormFeedback'; +import { formatPrice } from '#src/utils/formatting'; +import Close from '#src/icons/Close'; +import DialogBackButton from '#src/components/DialogBackButton/DialogBackButton'; +import PayPal from '#src/icons/PayPal'; +import CreditCard from '#src/icons/CreditCard'; +import LoadingOverlay from '#src/components/LoadingOverlay/LoadingOverlay'; +import type { OfferType } from '#types/account'; + type Props = { paymentMethodId?: number; onBackButtonClick: () => void; @@ -30,6 +32,7 @@ type Props = { couponInputValue: string; order: Order; offer: Offer; + offerType: OfferType; renderPaymentMethod?: () => JSX.Element | null; submitting: boolean; }; @@ -39,6 +42,7 @@ const CheckoutForm: React.FC = ({ paymentMethods, order, offer, + offerType, onBackButtonClick, onPaymentMethodChange, couponFormOpen, @@ -64,7 +68,7 @@ const CheckoutForm: React.FC = ({ }; const getFreeTrialText = (offer: Offer) => { - if (offer.freeDays > 0) { + if (offer.freeDays && offer.freeDays > 0) { return t('checkout.days_trial', { count: offer.freeDays }); } else if (offer.freePeriods) { // t('periods.day') @@ -82,18 +86,20 @@ const CheckoutForm: React.FC = ({ const cardPaymentMethod = paymentMethods?.find((method) => method.methodName === 'card'); const paypalPaymentMethod = paymentMethods?.find((method) => method.methodName === 'paypal'); + const orderTitle = offerType === 'svod' ? (offer.period === 'month' ? t('checkout.monthly') : t('checkout.yearly')) : offer.offerTitle; + return (

{t('checkout.payment_method')}

-

{offer.period === 'month' ? t('checkout.monthly') : t('checkout.yearly')}

+

{orderTitle}

{order.discount.type === 'trial' ?

{getFreeTrialText(offer)}

: null}
{formatPrice(offer.customerPriceInclTax, order.currency, offer.customerCountry)} - /{getOfferPeriod()} + {offerType === 'svod' && /{getOfferPeriod()}}
diff --git a/src/containers/AccountModal/forms/Checkout.tsx b/src/containers/AccountModal/forms/Checkout.tsx index 4dff7328e..88e7df513 100644 --- a/src/containers/AccountModal/forms/Checkout.tsx +++ b/src/containers/AccountModal/forms/Checkout.tsx @@ -1,10 +1,10 @@ -import React, { useCallback, useEffect, useState } from 'react'; +import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { useHistory } from 'react-router'; import { useTranslation } from 'react-i18next'; import shallow from 'zustand/shallow'; import CheckoutForm from '#src/components/CheckoutForm/CheckoutForm'; -import { addQueryParam } from '#src/utils/history'; +import { addQueryParam, removeQueryParam } from '#src/utils/history'; import useForm from '#src/hooks/useForm'; import LoadingOverlay from '#src/components/LoadingOverlay/LoadingOverlay'; import Adyen from '#src/components/Adyen/Adyen'; @@ -26,7 +26,15 @@ const Checkout = () => { const [couponCodeApplied, setCouponCodeApplied] = useState(false); const [paymentMethodId, setPaymentMethodId] = useState(undefined); - const { order, offer, paymentMethods } = useCheckoutStore(({ order, offer, paymentMethods }) => ({ order, offer, paymentMethods }), shallow); + const { order, offer, offerType, paymentMethods } = useCheckoutStore( + ({ order, offer, offerType, paymentMethods }) => ({ order, offer, offerType, paymentMethods }), + shallow, + ); + + const paymentSuccessUrl = useMemo( + () => (offerType === 'svod' ? addQueryParams(window.location.href, { u: 'welcome' }) : removeQueryParam(history, 'u')), + [history, offerType], + ); const couponCodeForm = useForm({ couponCode: '' }, async (values, { setSubmitting, setErrors }) => { setUpdatingOrder(true); @@ -101,7 +109,7 @@ const Checkout = () => { setPaymentError(undefined); await paymentWithoutDetails(); await reloadActiveSubscription({ delay: 1000 }); - history.replace(addQueryParam(history, 'u', 'welcome')); + history.replace(paymentSuccessUrl); } catch (error: unknown) { if (error instanceof Error) { setPaymentError(error.message); @@ -115,10 +123,9 @@ const Checkout = () => { try { setPaymentError(undefined); setUpdatingOrder(true); - const successUrl = addQueryParams(window.location.href, { u: 'welcome' }); const cancelUrl = addQueryParams(window.location.href, { u: 'paypal-cancelled' }); const errorUrl = addQueryParams(window.location.href, { u: 'paypal-error' }); - const response = await paypalPayment(successUrl, cancelUrl, errorUrl); + const response = await paypalPayment(paymentSuccessUrl, cancelUrl, errorUrl); if (response.redirectUrl) { window.location.href = response.redirectUrl; @@ -140,7 +147,7 @@ const Checkout = () => { setPaymentError(undefined); await adyenPayment(data.data.paymentMethod); await reloadActiveSubscription({ delay: 1000 }); - history.replace(addQueryParam(history, 'u', 'welcome')); + history.replace(paymentSuccessUrl); } catch (error: unknown) { if (error instanceof Error) { setPaymentError(error.message); @@ -149,7 +156,7 @@ const Checkout = () => { setUpdatingOrder(false); }, - [history], + [history, paymentSuccessUrl], ); const renderPaymentMethod = () => { @@ -171,7 +178,7 @@ const Checkout = () => { }; // loading state - if (!offer || !order || !paymentMethods) { + if (!offer || !order || !paymentMethods || !offerType) { return (
@@ -183,6 +190,7 @@ const Checkout = () => { { const { t } = useTranslation('account'); const { config, accessModel } = useConfigStore(({ config, accessModel }) => ({ config, accessModel }), shallow); const { cleengSandbox, json } = config; - const { offer, setOffer } = useCheckoutStore(({ offer, setOffer }) => ({ offer, setOffer }), shallow); + const { setOffer, setOfferType } = useCheckoutStore(({ setOffer, setOfferType }) => ({ setOffer, setOfferType }), shallow); const cleengMonthlyOffer = json?.cleengMonthlyOffer as string; const cleengYearlyOffer = json?.cleengYearlyOffer as string; @@ -51,19 +51,23 @@ const ChooseOffer = () => { if (!hasOffer) history.replace(removeQueryParam(history, 'u')); }, [hasOffer, history]); - const chooseOfferSubmitHandler: UseFormOnSubmitHandler = async (formData, { setSubmitting, setErrors }) => { + const chooseOfferSubmitHandler: UseFormOnSubmitHandler = async ( + { offerType, periodicity, tvodOfferId }, + { setSubmitting, setErrors }, + ) => { const offer = - formData.offerType === 'svod' - ? formData.periodicity === 'monthly' + offerType === 'svod' + ? periodicity === 'monthly' ? monthlyOfferData?.responseData : yearlyOfferData?.responseData - : tvodOffers.find(({ offerId }) => offerId === formData.tvodOfferId); + : tvodOffers.find(({ offerId }) => offerId === tvodOfferId); if (!offer) { return setErrors({ form: t('choose_offer.offer_not_found') }); } setOffer(offer); + setOfferType(offerType); history.push(addQueryParam(history, 'u', 'checkout')); @@ -77,7 +81,7 @@ const ChooseOffer = () => { }); const initialValues: ChooseOfferFormData = { offerType: 'svod', - periodicity: offer?.period === 'month' ? 'monthly' : 'yearly', + periodicity: 'yearly', tvodOfferId: requestedMediaOffers[0]?.offerId, }; const { handleSubmit, handleChange, values, errors, submitting } = useForm(initialValues, chooseOfferSubmitHandler, validationSchema); diff --git a/src/stores/CheckoutStore.ts b/src/stores/CheckoutStore.ts index 2a05da712..a10488792 100644 --- a/src/stores/CheckoutStore.ts +++ b/src/stores/CheckoutStore.ts @@ -1,13 +1,16 @@ import type { Offer, Order, PaymentMethod } from '#types/checkout'; import type { MediaOffer } from '#types/media'; import { createStore } from '#src/stores/utils'; +import type { OfferType } from '#types/account'; type CheckoutStore = { offer: Offer | null; + offerType: OfferType | null; order: Order | null; paymentMethods: PaymentMethod[] | null; requestedMediaOffers: MediaOffer[] | null; setOffer: (offer: Offer | null) => void; + setOfferType: (offerType: OfferType | null) => void; setOrder: (order: Order | null) => void; setPaymentMethods: (paymentMethods: PaymentMethod[] | null) => void; setRequestedMediaOffers: (requestedMediaOffers: MediaOffer[] | null) => void; @@ -15,10 +18,12 @@ type CheckoutStore = { export const useCheckoutStore = createStore('CheckoutStore', (set) => ({ offer: null, + offerType: null, order: null, paymentMethods: null, requestedMediaOffers: null, setOffer: (offer) => set({ offer }), + setOfferType: (offerType) => set({ offerType }), setOrder: (order) => set({ order }), setPaymentMethods: (paymentMethods) => set({ paymentMethods }), setRequestedMediaOffers: (requestedMediaOffers) => set({ requestedMediaOffers }),