From fb0d284f799214af3b3f2437c6eb8e1be4f92ed1 Mon Sep 17 00:00:00 2001 From: Christiaan Scheermeijer Date: Mon, 1 Jul 2024 12:37:26 +0200 Subject: [PATCH] refactor(payment): align integration flows --- .../src/controllers/AccountController.ts | 6 +- .../src/controllers/CheckoutController.ts | 15 +- .../src/services/WatchHistoryService.ts | 25 ++- .../cleeng/CleengCheckoutService.ts | 23 ++- .../integrations/jwp/JWPAccountService.ts | 2 +- .../integrations/jwp/JWPCheckoutService.ts | 54 +++++- .../jwp/JWPSubscriptionService.ts | 12 +- packages/common/types/checkout.ts | 11 +- .../hooks-react/src/useSubscriptionChange.ts | 38 ----- .../OfferSwitch/OfferSwitch.module.scss | 62 ------- .../components/OfferSwitch/OfferSwitch.tsx | 55 ------ .../src/components/Payment/Payment.test.tsx | 11 -- .../src/components/Payment/Payment.tsx | 158 +++--------------- .../PaymentContainer/PaymentContainer.tsx | 25 +-- .../User/__snapshots__/User.test.tsx.snap | 10 -- 15 files changed, 133 insertions(+), 374 deletions(-) delete mode 100644 packages/hooks-react/src/useSubscriptionChange.ts delete mode 100644 packages/ui-react/src/components/OfferSwitch/OfferSwitch.module.scss delete mode 100644 packages/ui-react/src/components/OfferSwitch/OfferSwitch.tsx diff --git a/packages/common/src/controllers/AccountController.ts b/packages/common/src/controllers/AccountController.ts index 5c876c941..6f48e3b68 100644 --- a/packages/common/src/controllers/AccountController.ts +++ b/packages/common/src/controllers/AccountController.ts @@ -427,13 +427,11 @@ export default class AccountController { // resolve and fetch the pending offer after upgrade/downgrade try { if (activeSubscription?.pendingSwitchId) { - assertModuleMethod(this.checkoutService.getOffer, 'getOffer is not available in checkout service'); assertModuleMethod(this.checkoutService.getSubscriptionSwitch, 'getSubscriptionSwitch is not available in checkout service'); - const switchOffer = await this.checkoutService.getSubscriptionSwitch({ switchId: activeSubscription.pendingSwitchId }); - const offerResponse = await this.checkoutService.getOffer({ offerId: switchOffer.responseData.toOfferId }); + const switchOffer = await this.checkoutService.getSubscriptionSwitch({ subscription: activeSubscription }); - pendingOffer = offerResponse.responseData; + pendingOffer = switchOffer.responseData; } } catch (error: unknown) { logDev('Failed to fetch the pending offer', error); diff --git a/packages/common/src/controllers/CheckoutController.ts b/packages/common/src/controllers/CheckoutController.ts index 3695e4af9..c09c30932 100644 --- a/packages/common/src/controllers/CheckoutController.ts +++ b/packages/common/src/controllers/CheckoutController.ts @@ -53,8 +53,7 @@ export default class CheckoutController { } if (!useCheckoutStore.getState().switchSubscriptionOffers.length) { - const subscriptionSwitches = await this.getSubscriptionSwitches(); - const switchSubscriptionOffers = subscriptionSwitches ? await this.getOffers({ offerIds: subscriptionSwitches }) : []; + const switchSubscriptionOffers = await this.getSubscriptionSwitches(); useCheckoutStore.setState({ switchSubscriptionOffers }); } }; @@ -238,24 +237,20 @@ export default class CheckoutController { }; }; - getSubscriptionSwitches = async (): Promise => { + getSubscriptionSwitches = async (): Promise => { const { getAccountInfo } = useAccountStore.getState(); const { customerId } = getAccountInfo(); const { subscription } = useAccountStore.getState(); - if (!subscription || !this.checkoutService.getSubscriptionSwitches) return null; - - assertModuleMethod(this.checkoutService.getOffer, 'getOffer is not available in checkout service'); + if (!subscription || !this.checkoutService.getSubscriptionSwitches) return []; const response = await this.checkoutService.getSubscriptionSwitches({ customerId: customerId, offerId: subscription.offerId, }); - if (!response.responseData.available.length) return null; - - return response.responseData.available.map(({ toOfferId }) => toOfferId); + return response.responseData; }; switchSubscription = async () => { @@ -271,9 +266,9 @@ export default class CheckoutController { const switchDirection: 'upgrade' | 'downgrade' = determineSwitchDirection(subscription); const switchSubscriptionPayload = { + subscription, toOfferId: selectedOffer.offerId, customerId: customerId, - offerId: subscription.offerId, switchDirection: switchDirection, }; diff --git a/packages/common/src/services/WatchHistoryService.ts b/packages/common/src/services/WatchHistoryService.ts index 62e4408ac..e33dc1269 100644 --- a/packages/common/src/services/WatchHistoryService.ts +++ b/packages/common/src/services/WatchHistoryService.ts @@ -1,5 +1,5 @@ import { inject, injectable } from 'inversify'; -import { array, number, object, string } from 'yup'; +import { number, object, string } from 'yup'; import type { PlaylistItem } from '../../types/playlist'; import type { SerializedWatchHistoryItem, WatchHistoryItem } from '../../types/watchHistory'; @@ -13,12 +13,10 @@ import ApiService from './ApiService'; import StorageService from './StorageService'; import AccountService from './integrations/AccountService'; -const schema = array( - object().shape({ - mediaid: string(), - progress: number(), - }), -); +const schema = object().shape({ + mediaid: string(), + progress: number(), +}); @injectable() export default class WatchHistoryService { @@ -63,8 +61,17 @@ export default class WatchHistoryService { }; private validateWatchHistory(history: unknown) { - if (history && schema.validateSync(history)) { - return history as SerializedWatchHistoryItem[]; + if (Array.isArray(history)) { + const validatedHistory = history.filter((item) => { + try { + return schema.validateSync(item); + } catch (error: unknown) { + logDev('Failed to validated watch history item', error); + return false; + } + }); + + return validatedHistory as SerializedWatchHistoryItem[]; } return []; diff --git a/packages/common/src/services/integrations/cleeng/CleengCheckoutService.ts b/packages/common/src/services/integrations/cleeng/CleengCheckoutService.ts index 6aa89be0e..0a87af9c5 100644 --- a/packages/common/src/services/integrations/cleeng/CleengCheckoutService.ts +++ b/packages/common/src/services/integrations/cleeng/CleengCheckoutService.ts @@ -16,6 +16,8 @@ import type { GetPaymentMethods, GetSubscriptionSwitch, GetSubscriptionSwitches, + GetSubscriptionSwitchesResponse, + GetSubscriptionSwitchResponse, PaymentWithoutDetails, PaymentWithPayPal, SwitchSubscription, @@ -128,16 +130,31 @@ export default class CleengCheckoutService extends CheckoutService { }; getSubscriptionSwitches: GetSubscriptionSwitches = async (payload) => { - return this.cleengService.get(`/customers/${payload.customerId}/subscription_switches/${payload.offerId}/availability`, { authenticate: true }); + const response = await this.cleengService.get>( + `/customers/${payload.customerId}/subscription_switches/${payload.offerId}/availability`, + { authenticate: true }, + ); + + const subscriptionSwitches = await this.getOffers({ offerIds: response.responseData.available.map((switchOffer) => switchOffer.toOfferId) }); + + return { + responseData: subscriptionSwitches, + errors: [], + }; }; getSubscriptionSwitch: GetSubscriptionSwitch = async (payload) => { - return this.cleengService.get(`/subscription_switches/${payload.switchId}`, { authenticate: true }); + const response = await this.cleengService.get>( + `/subscription_switches/${payload.subscription.pendingSwitchId}`, + { authenticate: true }, + ); + + return this.getOffer({ offerId: response.responseData.toOfferId }); }; switchSubscription: SwitchSubscription = async (payload) => { return this.cleengService.post( - `/customers/${payload.customerId}/subscription_switches/${payload.offerId}`, + `/customers/${payload.customerId}/subscription_switches/${payload.subscription.offerId}`, JSON.stringify({ toOfferId: payload.toOfferId, switchDirection: payload.switchDirection }), { authenticate: true }, ); diff --git a/packages/common/src/services/integrations/jwp/JWPAccountService.ts b/packages/common/src/services/integrations/jwp/JWPAccountService.ts index 5daba8d5e..c3e949e41 100644 --- a/packages/common/src/services/integrations/jwp/JWPAccountService.ts +++ b/packages/common/src/services/integrations/jwp/JWPAccountService.ts @@ -61,7 +61,7 @@ export default class JWPAccountService extends AccountService { canUpdateEmail: false, canSupportEmptyFullName: false, canChangePasswordWithOldPassword: true, - canRenewSubscription: false, + canRenewSubscription: true, canExportAccountData: true, canUpdatePaymentMethod: false, canShowReceipts: true, diff --git a/packages/common/src/services/integrations/jwp/JWPCheckoutService.ts b/packages/common/src/services/integrations/jwp/JWPCheckoutService.ts index c22c678ed..d8d02c1a2 100644 --- a/packages/common/src/services/integrations/jwp/JWPCheckoutService.ts +++ b/packages/common/src/services/integrations/jwp/JWPCheckoutService.ts @@ -1,5 +1,5 @@ import InPlayer, { type AccessFee, type MerchantPaymentMethod } from '@inplayer-org/inplayer.js'; -import { injectable } from 'inversify'; +import { inject, injectable, named } from 'inversify'; import { isSVODOffer } from '../../../utils/offers'; import type { @@ -10,6 +10,8 @@ import type { GetEntitlementsResponse, GetOffers, GetPaymentMethods, + GetSubscriptionSwitch, + GetSubscriptionSwitches, Offer, Order, Payment, @@ -17,11 +19,13 @@ import type { PaymentWithAdyen, PaymentWithoutDetails, PaymentWithPayPal, + SwitchSubscription, UpdateOrder, } from '../../../../types/checkout'; import CheckoutService from '../CheckoutService'; import type { ServiceResponse } from '../../../../types/service'; import { isCommonError } from '../../../utils/api'; +import AccountService from '../AccountService'; @injectable() export default class JWPCheckoutService extends CheckoutService { @@ -85,7 +89,6 @@ export default class JWPCheckoutService extends CheckoutService { active: true, period: offer.access_type.period === 'month' && offer.access_type.quantity === 12 ? 'year' : offer.access_type.period, freePeriods: offer.trial_period ? 1 : 0, - planSwitchEnabled: offer.item.plan_switch_enabled ?? false, } as Offer; }; @@ -109,6 +112,10 @@ export default class JWPCheckoutService extends CheckoutService { } as Order; }; + constructor(@inject(AccountService) @named('JWP') private readonly accountService: AccountService) { + super(); + } + createOrder: CreateOrder = async (payload) => { return { errors: [], @@ -276,13 +283,50 @@ export default class JWPCheckoutService extends CheckoutService { } }; - getSubscriptionSwitches = undefined; + getSubscriptionSwitches: GetSubscriptionSwitches = async (payload) => { + const { data } = await InPlayer.Asset.getAssetAccessFees(this.parseOfferId(payload.offerId)); + + const subscriptionSwitches = data?.filter((accessFee) => accessFee.item.plan_switch_enabled).map((accessFee) => this.formatOffer(accessFee)) || []; + + return { responseData: subscriptionSwitches, errors: [] }; + }; getOrder = undefined; - switchSubscription = undefined; + switchSubscription: SwitchSubscription = async (payload) => { + const { subscription, toOfferId } = payload; + const accessFeeId = parseInt(toOfferId.split('_')[1]); + + try { + const response = await InPlayer.Subscription.changeSubscriptionPlan({ + access_fee_id: accessFeeId, + inplayer_token: String(subscription.subscriptionId), + }); + + await this.accountService.updateCustomer({ + metadata: { + [`${subscription.subscriptionId}_pending_downgrade`]: toOfferId, + }, + }); + + return { + errors: [], + responseData: response.data.message, + }; + } catch { + throw new Error('Failed to change subscription'); + } + }; + + getSubscriptionSwitch: GetSubscriptionSwitch = async ({ subscription }) => { + if (subscription.pendingSwitchId) { + const offers = await this.getOffers({ offerIds: [subscription.offerId] }); - getSubscriptionSwitch = undefined; + return { responseData: offers.find((offer) => offer.offerId === subscription.pendingSwitchId) || null, errors: [] }; + } + + return { responseData: null, errors: [] }; + }; createAdyenPaymentSession = undefined; diff --git a/packages/common/src/services/integrations/jwp/JWPSubscriptionService.ts b/packages/common/src/services/integrations/jwp/JWPSubscriptionService.ts index 6d42b457e..41dff124c 100644 --- a/packages/common/src/services/integrations/jwp/JWPSubscriptionService.ts +++ b/packages/common/src/services/integrations/jwp/JWPSubscriptionService.ts @@ -100,7 +100,7 @@ export default class JWPSubscriptionService extends SubscriptionService { }; }; - private formatActiveSubscription = (subscription: SubscriptionDetails, expiresAt: number) => { + private formatActiveSubscription = (subscription: SubscriptionDetails, expiresAt: number, pendingSwitchId: string | null) => { let status = ''; switch (subscription.action_type) { case 'free-trial': @@ -134,7 +134,7 @@ export default class JWPSubscriptionService extends SubscriptionService { period: subscription.access_type?.period, totalPrice: subscription.charged_amount, unsubscribeUrl: subscription.unsubscribe_url, - pendingSwitchId: null, + pendingSwitchId: pendingSwitchId, } as Subscription; }; @@ -159,6 +159,7 @@ export default class JWPSubscriptionService extends SubscriptionService { getActiveSubscription: GetActiveSubscription = async () => { const assetId = this.accountService.assetId; + const { data: customer } = await InPlayer.Account.getAccountInfo(); if (assetId === null) throw new Error("Couldn't fetch active subscription, there is no assetId configured"); @@ -167,10 +168,13 @@ export default class JWPSubscriptionService extends SubscriptionService { if (hasAccess) { const { data } = await InPlayer.Subscription.getSubscriptions(); - const activeSubscription = data.collection.find((subscription: SubscriptionDetails) => subscription.item_id === assetId); + const activeSubscription = data.collection.find((subscription: SubscriptionDetails) => subscription.item_id === assetId) as + | SubscriptionDetails + | undefined; + const pendingSwitchId = (customer.metadata?.[`${activeSubscription?.subscription_id}_pending_downgrade`] as string) || null; if (activeSubscription) { - return this.formatActiveSubscription(activeSubscription, hasAccess?.data?.expires_at); + return this.formatActiveSubscription(activeSubscription, hasAccess?.data?.expires_at, pendingSwitchId); } return this.formatGrantedSubscription(hasAccess.data); diff --git a/packages/common/types/checkout.ts b/packages/common/types/checkout.ts index 89210f117..8d1983b0d 100644 --- a/packages/common/types/checkout.ts +++ b/packages/common/types/checkout.ts @@ -1,5 +1,5 @@ import type { PayloadWithIPOverride } from './account'; -import type { PaymentDetail } from './subscription'; +import type { PaymentDetail, Subscription } from './subscription'; import type { EmptyEnvironmentServiceRequest, EnvironmentServiceRequest, PromiseRequest } from './service'; export type Offer = { @@ -41,7 +41,6 @@ export type Offer = { contentExternalId: number | null; contentExternalData: string | null; contentAgeRestriction: string | null; - planSwitchEnabled?: boolean; }; export type OfferType = 'svod' | 'tvod'; @@ -188,7 +187,7 @@ export type GetSubscriptionSwitchesResponse = { }; export type GetSubscriptionSwitchPayload = { - switchId: string; + subscription: Subscription; }; export type GetSubscriptionSwitchResponse = { @@ -206,7 +205,7 @@ export type GetSubscriptionSwitchResponse = { export type SwitchSubscriptionPayload = { customerId: string; - offerId: string; + subscription: Subscription; toOfferId: string; switchDirection: string; }; @@ -372,8 +371,8 @@ export type GetPaymentMethods = EmptyEnvironmentServiceRequest; export type PaymentWithAdyen = EnvironmentServiceRequest; export type PaymentWithPayPal = EnvironmentServiceRequest; -export type GetSubscriptionSwitches = EnvironmentServiceRequest; -export type GetSubscriptionSwitch = EnvironmentServiceRequest; +export type GetSubscriptionSwitches = EnvironmentServiceRequest; +export type GetSubscriptionSwitch = EnvironmentServiceRequest; export type SwitchSubscription = EnvironmentServiceRequest; export type GetEntitlements = EnvironmentServiceRequest; export type GetAdyenPaymentSession = EnvironmentServiceRequest; diff --git a/packages/hooks-react/src/useSubscriptionChange.ts b/packages/hooks-react/src/useSubscriptionChange.ts deleted file mode 100644 index 772397ef4..000000000 --- a/packages/hooks-react/src/useSubscriptionChange.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { useMutation } from 'react-query'; -import type { Customer } from '@jwp/ott-common/types/account'; -import { getModule } from '@jwp/ott-common/src/modules/container'; -import { useAccountStore } from '@jwp/ott-common/src/stores/AccountStore'; -import AccountController from '@jwp/ott-common/src/controllers/AccountController'; -import CheckoutController from '@jwp/ott-common/src/controllers/CheckoutController'; - -export const useSubscriptionChange = ( - isUpgradeOffer: boolean, - selectedOfferId: string | null, - customer: Customer | null, - activeSubscriptionId: string | number | undefined, -) => { - const accountController = getModule(AccountController); - const checkoutController = getModule(CheckoutController); - - const updateSubscriptionMetadata = useMutation(accountController.updateUser, { - onSuccess: () => { - useAccountStore.setState({ - loading: false, - }); - }, - }); - - return useMutation(checkoutController.changeSubscription, { - onSuccess: () => { - if (!isUpgradeOffer && selectedOfferId) { - updateSubscriptionMetadata.mutate({ - firstName: customer?.firstName || '', - lastName: customer?.lastName || '', - metadata: { - [`${activeSubscriptionId}_pending_downgrade`]: selectedOfferId, - }, - }); - } - }, - }); -}; diff --git a/packages/ui-react/src/components/OfferSwitch/OfferSwitch.module.scss b/packages/ui-react/src/components/OfferSwitch/OfferSwitch.module.scss deleted file mode 100644 index 43326e202..000000000 --- a/packages/ui-react/src/components/OfferSwitch/OfferSwitch.module.scss +++ /dev/null @@ -1,62 +0,0 @@ -@use '@jwp/ott-ui-react/src/styles/variables'; -@use '@jwp/ott-ui-react/src/styles/theme'; - -.offerSwitchContainer { - display: flex; - align-items: center; - width: 100%; - height: auto; - padding: 16px; - gap: 25px; - color: variables.$gray-white; - background-color: theme.$panel-bg; - border-radius: 4px; -} - -.activeOfferSwitchContainer { - color: variables.$gray-darker; - background-color: variables.$white; -} - -.offerSwitchInfoContainer { - display: flex; - flex-direction: column; - justify-content: center; - align-items: flex-start; - height: 100%; - gap: 4px; - font-weight: 600; -} - -.offerSwitchPlanContainer { - display: flex; - flex-direction: column; - justify-content: center; - align-items: flex-start; - gap: 2px; -} - -.currentPlanHeading { - color: variables.$gray; - font-size: 10px; -} - -.activeCurrentPlanHeading { - color: variables.$gray; -} - -.nextBillingDate { - color: variables.$gray; - font-weight: 400; - font-size: 12px; -} - -.price { - margin-left: auto; - font-size: 20px; - line-height: variables.$base-line-height; -} - -.paymentFrequency { - font-size: 12px; -} diff --git a/packages/ui-react/src/components/OfferSwitch/OfferSwitch.tsx b/packages/ui-react/src/components/OfferSwitch/OfferSwitch.tsx deleted file mode 100644 index 3ced92a6e..000000000 --- a/packages/ui-react/src/components/OfferSwitch/OfferSwitch.tsx +++ /dev/null @@ -1,55 +0,0 @@ -import classNames from 'classnames'; -import { useTranslation } from 'react-i18next'; -import type { Offer } from '@jwp/ott-common/types/checkout'; -import { formatLocalizedDate, formatPrice } from '@jwp/ott-common/src/utils/formatting'; - -import Checkbox from '../form-fields/Checkbox/Checkbox'; - -import styles from './OfferSwitch.module.scss'; - -type OfferSwitchProps = { - isCurrentOffer: boolean; - pendingDowngradeOfferId: string; - offer: Offer; - selected: boolean; - onChange: (offerId: string) => void; - expiresAt: number | undefined; -}; - -const OfferSwitch = ({ isCurrentOffer, pendingDowngradeOfferId, offer, selected, onChange, expiresAt }: OfferSwitchProps) => { - const { t, i18n } = useTranslation(['user', 'account']); - const { customerPriceInclTax, customerCurrency, period } = offer; - - const isPendingDowngrade = pendingDowngradeOfferId === offer.offerId; - - return ( -
- onChange(offer.offerId)} /> -
- {(isCurrentOffer || isPendingDowngrade) && ( -
- {isCurrentOffer && t('user:payment.current_plan').toUpperCase()} - {isPendingDowngrade && t('user:payment.pending_downgrade').toUpperCase()} -
- )} -
-
{t(`user:payment.${period === 'month' ? 'monthly' : 'annual'}_subscription`)}
- {(isCurrentOffer || isPendingDowngrade) && expiresAt && ( -
- {isCurrentOffer && - !pendingDowngradeOfferId && - t('user:payment.next_billing_date_on', { date: formatLocalizedDate(new Date(expiresAt * 1000), i18n.language) })} - {isPendingDowngrade && t('user:payment.downgrade_on', { date: formatLocalizedDate(new Date(expiresAt * 1000), i18n.language) })} -
- )} -
-
-
- {formatPrice(customerPriceInclTax, customerCurrency, undefined)} - /{t(`account:periods.${period}_one`)} -
-
- ); -}; - -export default OfferSwitch; diff --git a/packages/ui-react/src/components/Payment/Payment.test.tsx b/packages/ui-react/src/components/Payment/Payment.test.tsx index a4847043d..c5ad4f0f3 100644 --- a/packages/ui-react/src/components/Payment/Payment.test.tsx +++ b/packages/ui-react/src/components/Payment/Payment.test.tsx @@ -27,17 +27,6 @@ describe('', () => { onShowReceiptClick={vi.fn()} onUpgradeSubscriptionClick={vi.fn()} onShowAllTransactionsClick={vi.fn()} - changeSubscriptionPlan={{ - isLoading: false, - isSuccess: false, - isError: false, - reset: vi.fn(), - }} - onChangePlanClick={vi.fn()} - selectedOfferId={null} - setSelectedOfferId={vi.fn()} - isUpgradeOffer={undefined} - setIsUpgradeOffer={vi.fn()} isExternalPaymentProvider={false} />, ); diff --git a/packages/ui-react/src/components/Payment/Payment.tsx b/packages/ui-react/src/components/Payment/Payment.tsx index 538e78509..dd8b9bc7c 100644 --- a/packages/ui-react/src/components/Payment/Payment.tsx +++ b/packages/ui-react/src/components/Payment/Payment.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from 'react'; +import React from 'react'; import { useTranslation } from 'react-i18next'; import { useLocation, useNavigate } from 'react-router-dom'; import type { AccessModel } from '@jwp/ott-common/types/config'; @@ -14,10 +14,8 @@ import useOpaqueId from '@jwp/ott-hooks-react/src/useOpaqueId'; import classNames from 'classnames'; import IconButton from '../IconButton/IconButton'; -import Alert from '../Alert/Alert'; import Button from '../Button/Button'; import LoadingOverlay from '../LoadingOverlay/LoadingOverlay'; -import OfferSwitch from '../OfferSwitch/OfferSwitch'; import TextField from '../form-fields/TextField/TextField'; import Icon from '../Icon/Icon'; import Link from '../Link/Link'; @@ -45,19 +43,6 @@ type Props = { canUpdatePaymentMethod: boolean; canRenewSubscription?: boolean; canShowReceipts?: boolean; - offers?: Offer[]; - pendingDowngradeOfferId?: string; - changeSubscriptionPlan: { - isLoading: boolean; - isError: boolean; - isSuccess: boolean; - reset: () => void; - }; - onChangePlanClick: () => void; - selectedOfferId: string | null; - setSelectedOfferId: (offerId: string | null) => void; - isUpgradeOffer: boolean | undefined; - setIsUpgradeOffer: (isUpgradeOffer: boolean | undefined) => void; isExternalPaymentProvider: boolean; paymentProvider?: string; paymentProviderLink?: string; @@ -81,14 +66,6 @@ const Payment = ({ canUpdatePaymentMethod, onUpgradeSubscriptionClick, offerSwitchesAvailable, - offers = [], - pendingDowngradeOfferId = '', - changeSubscriptionPlan, - onChangePlanClick, - selectedOfferId, - setSelectedOfferId, - isUpgradeOffer, - setIsUpgradeOffer, isExternalPaymentProvider, paymentProvider, paymentProviderLink, @@ -105,27 +82,6 @@ const Payment = ({ const breakpoint = useBreakpoint(); const isMobile = breakpoint === Breakpoint.xs; - const [isChangingOffer, setIsChangingOffer] = useState(false); - - useEffect(() => { - if (!isChangingOffer) { - setSelectedOfferId(activeSubscription?.accessFeeId ?? null); - } - }, [activeSubscription, isChangingOffer, setSelectedOfferId]); - - useEffect(() => { - setIsChangingOffer(false); - }, [activeSubscription?.status, activeSubscription?.pendingSwitchId]); - - useEffect(() => { - if (selectedOfferId && offers) { - setIsUpgradeOffer( - (offers.find((offer) => offer.offerId === selectedOfferId)?.customerPriceInclTax ?? 0) > - (offers.find((offer) => offer.offerId === activeSubscription?.accessFeeId)?.customerPriceInclTax ?? 0), - ); - } - }, [selectedOfferId, offers, activeSubscription, setIsUpgradeOffer]); - function onCompleteSubscriptionClick() { navigate(modalURLFromLocation(location, 'choose-offer')); } @@ -159,58 +115,39 @@ const Payment = ({ } } - const showChangeSubscriptionButton = (!isExternalPaymentProvider && offerSwitchesAvailable) || (!isChangingOffer && !canRenewSubscription); + const showChangeSubscriptionButton = !isExternalPaymentProvider && offerSwitchesAvailable; return ( <> - { - changeSubscriptionPlan.reset(); - setIsChangingOffer(false); - }} - />

{t('nav.payments')}

{accessModel === ACCESS_MODEL.SVOD && (
-

{isChangingOffer ? t('user:payment.change_plan') : t('user:payment.subscription_details')}

+

{t('user:payment.subscription_details')}

{activeSubscription ? ( - {!isChangingOffer && ( -
-

- {getTitle(activeSubscription.period)}
- {activeSubscription.status === 'active' && !isGrantedSubscription && !pendingDowngradeOfferId - ? t('user:payment.next_billing_date_on', { date: formatLocalizedDate(new Date(activeSubscription.expiresAt * 1000), i18n.language) }) - : t('user:payment.subscription_expires_on', { date: formatLocalizedDate(new Date(activeSubscription.expiresAt * 1000), i18n.language) })} - {(pendingOffer || pendingDowngradeOfferId) && activeSubscription.status !== 'cancelled' && ( - - {t('user:payment.pending_offer_switch', { - title: getTitle(pendingOffer?.period || offers.find((offer) => offer.offerId === pendingDowngradeOfferId)?.period || 'month'), - })} - - )} -

- {!isGrantedSubscription && ( -

- {formatPrice(activeSubscription.nextPaymentPrice, activeSubscription.nextPaymentCurrency, customer.country)} - /{t(`account:periods.${activeSubscription.period}`)} -

+
+

+ {getTitle(activeSubscription.period)}
+ {activeSubscription.status === 'active' && !isGrantedSubscription + ? t('user:payment.next_billing_date_on', { date: formatLocalizedDate(new Date(activeSubscription.expiresAt * 1000), i18n.language) }) + : t('user:payment.subscription_expires_on', { date: formatLocalizedDate(new Date(activeSubscription.expiresAt * 1000), i18n.language) })} + {pendingOffer && activeSubscription.status !== 'cancelled' && ( + + {t('user:payment.pending_offer_switch', { + title: getTitle(pendingOffer?.period), + })} + )} -

- )} +

+ {!isGrantedSubscription && ( +

+ {formatPrice(activeSubscription.nextPaymentPrice, activeSubscription.nextPaymentCurrency, customer.country)} + /{t(`account:periods.${activeSubscription.period}`)} +

+ )} +
{isExternalPaymentProvider ? (

{t('account:external_payment.explanation', { paymentProvider })}{' '} @@ -223,24 +160,15 @@ const Payment = ({

)}
diff --git a/packages/ui-react/src/containers/PaymentContainer/PaymentContainer.tsx b/packages/ui-react/src/containers/PaymentContainer/PaymentContainer.tsx index 2aaeb2b87..fe7f794f3 100644 --- a/packages/ui-react/src/containers/PaymentContainer/PaymentContainer.tsx +++ b/packages/ui-react/src/containers/PaymentContainer/PaymentContainer.tsx @@ -6,7 +6,6 @@ import { useAccountStore } from '@jwp/ott-common/src/stores/AccountStore'; import { useConfigStore } from '@jwp/ott-common/src/stores/ConfigStore'; import AccountController from '@jwp/ott-common/src/controllers/AccountController'; import useOffers from '@jwp/ott-hooks-react/src/useOffers'; -import { useSubscriptionChange } from '@jwp/ott-hooks-react/src/useSubscriptionChange'; import styles from '../../pages/User/User.module.scss'; import LoadingOverlay from '../../components/LoadingOverlay/LoadingOverlay'; @@ -59,12 +58,10 @@ const PaymentContainer = () => { const { accessModel } = useConfigStore(({ accessModel }) => ({ accessModel }), shallow); const { user: customer, subscription: activeSubscription, transactions, activePayment, pendingOffer, loading } = useAccountStore(); const { canUpdatePaymentMethod, canShowReceipts, canRenewSubscription } = accountController.getFeatures(); - const { subscriptionOffers, switchSubscriptionOffers } = useOffers(); + const { switchSubscriptionOffers } = useOffers(); const [showAllTransactions, setShowAllTransactions] = useState(false); const [isLoadingReceipt, setIsLoadingReceipt] = useState(false); - const [selectedOfferId, setSelectedOfferId] = useState(activeSubscription?.accessFeeId ?? null); - const [isUpgradeOffer, setIsUpgradeOffer] = useState(undefined); const location = useLocation(); @@ -85,22 +82,10 @@ const PaymentContainer = () => { setIsLoadingReceipt(false); }; - const changeSubscriptionPlan = useSubscriptionChange(isUpgradeOffer ?? false, selectedOfferId, customer, activeSubscription?.subscriptionId); - - const onChangePlanClick = async () => { - if (selectedOfferId && activeSubscription?.subscriptionId) { - changeSubscriptionPlan.mutate({ - accessFeeId: selectedOfferId.slice(1), - subscriptionId: `${activeSubscription.subscriptionId}`, - }); - } - }; - if (!customer) { return ; } - const pendingDowngradeOfferId = (customer.metadata?.[`${activeSubscription?.subscriptionId}_pending_downgrade`] as string) || ''; const isExternalPaymentProvider = !!activeSubscription && EXTERNAL_PAYMENT_METHODS.includes(activeSubscription.paymentMethod); const paymentProvider = activeSubscription?.paymentMethod.split(' ')[0] || 'unknown'; const paymentProviderLink = STORE_LINKS[paymentProvider.toLowerCase()]; @@ -124,14 +109,6 @@ const PaymentContainer = () => { offerSwitchesAvailable={switchSubscriptionOffers.length > 0} canShowReceipts={canShowReceipts} onShowReceiptClick={handleShowReceiptClick} - offers={subscriptionOffers} - pendingDowngradeOfferId={pendingDowngradeOfferId} - changeSubscriptionPlan={changeSubscriptionPlan} - onChangePlanClick={onChangePlanClick} - selectedOfferId={selectedOfferId} - setSelectedOfferId={(offerId: string | null) => setSelectedOfferId(offerId)} - isUpgradeOffer={isUpgradeOffer} - setIsUpgradeOffer={(isUpgradeOffer: boolean | undefined) => setIsUpgradeOffer(isUpgradeOffer)} isExternalPaymentProvider={isExternalPaymentProvider} paymentProvider={paymentProvider} paymentProviderLink={paymentProviderLink} diff --git a/packages/ui-react/src/pages/User/__snapshots__/User.test.tsx.snap b/packages/ui-react/src/pages/User/__snapshots__/User.test.tsx.snap index c6c0ec92b..fb0b843c0 100644 --- a/packages/ui-react/src/pages/User/__snapshots__/User.test.tsx.snap +++ b/packages/ui-react/src/pages/User/__snapshots__/User.test.tsx.snap @@ -689,16 +689,6 @@ exports[`User Component tests > Payments Page 1`] = `

-