Skip to content

Commit

Permalink
feat(payment): add tvod to checkout
Browse files Browse the repository at this point in the history
  • Loading branch information
royschut committed May 20, 2022
1 parent 14893ae commit c92bb15
Show file tree
Hide file tree
Showing 5 changed files with 53 additions and 29 deletions.
1 change: 1 addition & 0 deletions src/components/CheckoutForm/CheckoutForm.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ describe('<CheckoutForm>', () => {
couponFormSubmitting={false}
order={order as Order}
offer={offer as Offer}
offerType={'svod'}
submitting={false}
/>,
);
Expand Down
34 changes: 20 additions & 14 deletions src/components/CheckoutForm/CheckoutForm.tsx
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -30,6 +32,7 @@ type Props = {
couponInputValue: string;
order: Order;
offer: Offer;
offerType: OfferType;
renderPaymentMethod?: () => JSX.Element | null;
submitting: boolean;
};
Expand All @@ -39,6 +42,7 @@ const CheckoutForm: React.FC<Props> = ({
paymentMethods,
order,
offer,
offerType,
onBackButtonClick,
onPaymentMethodChange,
couponFormOpen,
Expand All @@ -64,7 +68,7 @@ const CheckoutForm: React.FC<Props> = ({
};

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')
Expand All @@ -82,18 +86,20 @@ const CheckoutForm: React.FC<Props> = ({
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 (
<div>
<DialogBackButton onClick={onBackButtonClick} />
<h2 className={styles.title}>{t('checkout.payment_method')}</h2>
<div className={styles.order}>
<div className={styles.orderInfo}>
<p className={styles.orderTitle}>{offer.period === 'month' ? t('checkout.monthly') : t('checkout.yearly')}</p>
<p className={classNames(styles.orderTitle, { [styles.orderTitleMargin]: offerType === 'svod' })}>{orderTitle}</p>
{order.discount.type === 'trial' ? <p className={styles.orderBillingDate}>{getFreeTrialText(offer)}</p> : null}
</div>
<div className={styles.orderPrice}>
<span>{formatPrice(offer.customerPriceInclTax, order.currency, offer.customerCountry)}</span>
<small>/{getOfferPeriod()}</small>
{offerType === 'svod' && <small>/{getOfferPeriod()}</small>}
</div>
</div>
<div className={styles.couponForm}>
Expand Down
26 changes: 17 additions & 9 deletions src/containers/AccountModal/forms/Checkout.tsx
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -26,7 +26,15 @@ const Checkout = () => {
const [couponCodeApplied, setCouponCodeApplied] = useState(false);
const [paymentMethodId, setPaymentMethodId] = useState<number | undefined>(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);
Expand Down Expand Up @@ -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);
Expand All @@ -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;
Expand All @@ -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);
Expand All @@ -149,7 +156,7 @@ const Checkout = () => {

setUpdatingOrder(false);
},
[history],
[history, paymentSuccessUrl],
);

const renderPaymentMethod = () => {
Expand All @@ -171,7 +178,7 @@ const Checkout = () => {
};

// loading state
if (!offer || !order || !paymentMethods) {
if (!offer || !order || !paymentMethods || !offerType) {
return (
<div style={{ height: 300 }}>
<LoadingOverlay inline />
Expand All @@ -183,6 +190,7 @@ const Checkout = () => {
<CheckoutForm
order={order}
offer={offer}
offerType={offerType}
onBackButtonClick={backButtonClickHandler}
paymentMethods={paymentMethods}
paymentMethodId={paymentMethodId}
Expand Down
16 changes: 10 additions & 6 deletions src/containers/AccountModal/forms/ChooseOffer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ const ChooseOffer = () => {
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;
Expand Down Expand Up @@ -51,19 +51,23 @@ const ChooseOffer = () => {
if (!hasOffer) history.replace(removeQueryParam(history, 'u'));
}, [hasOffer, history]);

const chooseOfferSubmitHandler: UseFormOnSubmitHandler<ChooseOfferFormData> = async (formData, { setSubmitting, setErrors }) => {
const chooseOfferSubmitHandler: UseFormOnSubmitHandler<ChooseOfferFormData> = 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'));

Expand All @@ -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);
Expand Down
5 changes: 5 additions & 0 deletions src/stores/CheckoutStore.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,29 @@
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;
};

export const useCheckoutStore = createStore<CheckoutStore>('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 }),
Expand Down

0 comments on commit c92bb15

Please sign in to comment.