@stigg/react-sdk 5.28.3 → 5.29.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. package/dist/components/checkout/CheckoutProvider.d.ts +1 -0
  2. package/dist/components/checkout/hooks/useLoadCheckout.d.ts +1 -0
  3. package/dist/components/checkout/summary/CheckoutSummary.d.ts +2 -1
  4. package/dist/components/checkout/summary/CheckoutSummarySkeleton.d.ts +3 -1
  5. package/dist/components/paywall/Paywall.d.ts +3 -2
  6. package/dist/components/paywall/PaywallContainer.d.ts +3 -2
  7. package/dist/components/paywall/PlanOffering.d.ts +3 -2
  8. package/dist/components/paywall/PlanOfferingButton.d.ts +3 -2
  9. package/dist/components/paywall/hooks/useLoadPaywallData.d.ts +3 -2
  10. package/dist/components/paywall/types.d.ts +8 -0
  11. package/dist/components/paywall/utils/mapPaywallData.d.ts +3 -2
  12. package/dist/components/utils/priceTierUtils.d.ts +14 -3
  13. package/dist/react-sdk.cjs.development.js +130 -49
  14. package/dist/react-sdk.cjs.development.js.map +1 -1
  15. package/dist/react-sdk.cjs.production.min.js +1 -1
  16. package/dist/react-sdk.cjs.production.min.js.map +1 -1
  17. package/dist/react-sdk.esm.js +130 -49
  18. package/dist/react-sdk.esm.js.map +1 -1
  19. package/package.json +1 -1
  20. package/src/components/checkout/CheckoutContainer.tsx +3 -2
  21. package/src/components/checkout/CheckoutProvider.tsx +8 -1
  22. package/src/components/checkout/hooks/useLoadCheckout.ts +1 -0
  23. package/src/components/checkout/summary/CheckoutSummary.tsx +3 -2
  24. package/src/components/checkout/summary/CheckoutSummarySkeleton.tsx +2 -2
  25. package/src/components/paywall/Paywall.tsx +11 -2
  26. package/src/components/paywall/PaywallContainer.tsx +11 -1
  27. package/src/components/paywall/PlanOffering.tsx +11 -2
  28. package/src/components/paywall/PlanOfferingButton.tsx +9 -2
  29. package/src/components/paywall/hooks/useLoadPaywallData.ts +4 -2
  30. package/src/components/paywall/types.ts +12 -0
  31. package/src/components/paywall/utils/mapPaywallData.ts +13 -3
  32. package/src/components/utils/priceTierUtils.ts +54 -13
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "5.28.3",
2
+ "version": "5.29.0",
3
3
  "license": "MIT",
4
4
  "main": "dist/index.js",
5
5
  "typings": "dist/index.d.ts",
@@ -83,7 +83,7 @@ export function CheckoutContainer({
83
83
  onMockCheckoutPreview,
84
84
  }: CheckoutContainerProps) {
85
85
  const { stripePromise, setupIntentClientSecret } = useStripeIntegration();
86
- const [{ stiggTheme, widgetState, theme }] = useCheckoutContext();
86
+ const [{ stiggTheme, widgetState, theme, isWidgetWatermarkEnabled }] = useCheckoutContext();
87
87
  const { currentStep } = useProgressBarModel();
88
88
 
89
89
  const { isLoadingCheckoutData } = widgetState;
@@ -151,7 +151,7 @@ export function CheckoutContainer({
151
151
  <CheckoutContent>
152
152
  <CheckoutPanel>{isLoadingCheckoutData ? <ContentLoadingSkeleton /> : checkoutContent}</CheckoutPanel>
153
153
  {isLoadingCheckoutData ? (
154
- <CheckoutSummarySkeleton />
154
+ <CheckoutSummarySkeleton isWidgetWatermarkEnabled={isWidgetWatermarkEnabled} />
155
155
  ) : (
156
156
  <CheckoutSummary
157
157
  disablePromotionCode={disablePromotionCode}
@@ -160,6 +160,7 @@ export function CheckoutContainer({
160
160
  onCheckoutCompleted={onCheckoutCompleted}
161
161
  isFreeDowngrade={isFreeDowngrade}
162
162
  onMockCheckoutPreview={onMockCheckoutPreview}
163
+ isWidgetWatermarkEnabled={isWidgetWatermarkEnabled}
163
164
  />
164
165
  )}
165
166
  </CheckoutContent>
@@ -35,6 +35,7 @@ export interface CheckoutContextState {
35
35
  addonsStep: AddonsStepState;
36
36
  paymentStep: PaymentStepState;
37
37
  widgetState: WidgetState;
38
+ isWidgetWatermarkEnabled: boolean;
38
39
  }
39
40
 
40
41
  export const CheckoutContext = React.createContext<
@@ -101,7 +102,12 @@ export function CheckoutProvider({
101
102
  }: {
102
103
  children: React.ReactNode;
103
104
  } & CheckoutProviderProps) {
104
- const { checkout, isLoading } = useLoadCheckout({ resourceId, planId, billingCountryCode, onMockCheckoutState });
105
+ const { checkout, isLoading, isWidgetWatermarkEnabled } = useLoadCheckout({
106
+ resourceId,
107
+ planId,
108
+ billingCountryCode,
109
+ onMockCheckoutState,
110
+ });
105
111
  const configuration: CustomizedTheme = checkout?.configuration
106
112
  ? mapCheckoutConfiguration(checkout.configuration)
107
113
  : { typography: mapTypography(defaultCheckoutTypography) };
@@ -147,6 +153,7 @@ export function CheckoutProvider({
147
153
  paymentStep,
148
154
  resourceId: checkout?.resource?.id,
149
155
  widgetState: { readOnly: false, isValid: true, isLoadingCheckoutData: isLoading },
156
+ isWidgetWatermarkEnabled,
150
157
  };
151
158
 
152
159
  return initialState;
@@ -43,5 +43,6 @@ export function useLoadCheckout({ planId, resourceId, billingCountryCode, onMock
43
43
  return {
44
44
  checkout,
45
45
  isLoading,
46
+ isWidgetWatermarkEnabled: stigg.isWidgetWatermarkEnabled,
46
47
  };
47
48
  }
@@ -110,7 +110,8 @@ export const CheckoutSummary = ({
110
110
  disableSuccessAnimation,
111
111
  isFreeDowngrade,
112
112
  onMockCheckoutPreview,
113
- }: CheckoutContainerProps & { isFreeDowngrade: boolean }) => {
113
+ isWidgetWatermarkEnabled,
114
+ }: CheckoutContainerProps & { isFreeDowngrade: boolean; isWidgetWatermarkEnabled: boolean }) => {
114
115
  const [isCheckoutCompletedSuccessfully, setIsCheckoutCompletedSuccessfully] = useState(false);
115
116
  const { setErrorMessage } = usePaymentStepModel();
116
117
  const progressBar = useProgressBarModel();
@@ -415,7 +416,7 @@ export const CheckoutSummary = ({
415
416
  </SummaryCard>
416
417
  <PoweredByStigg
417
418
  source="checkout"
418
- showWatermark
419
+ showWatermark={isWidgetWatermarkEnabled}
419
420
  style={{ marginTop: 8, display: 'flex', justifyContent: 'center' }}
420
421
  />
421
422
  {!disableSuccessAnimation && isCheckoutCompletedSuccessfully && (
@@ -3,7 +3,7 @@ import { PoweredByStigg } from '../../common/PoweredByStigg';
3
3
  import { SummaryCard, SummaryContainer } from './CheckoutSummary';
4
4
  import { FlexedSkeleton, Skeleton, SkeletonsContainer } from '../components/Skeletons.style';
5
5
 
6
- export const CheckoutSummarySkeleton = () => {
6
+ export const CheckoutSummarySkeleton = ({ isWidgetWatermarkEnabled }: { isWidgetWatermarkEnabled: boolean }) => {
7
7
  return (
8
8
  <SummaryContainer>
9
9
  <SummaryCard>
@@ -31,7 +31,7 @@ export const CheckoutSummarySkeleton = () => {
31
31
  </SummaryCard>
32
32
  <PoweredByStigg
33
33
  source="checkout"
34
- showWatermark
34
+ showWatermark={isWidgetWatermarkEnabled}
35
35
  style={{ marginTop: 8, display: 'flex', justifyContent: 'center' }}
36
36
  />
37
37
  </SummaryContainer>
@@ -10,6 +10,7 @@ import {
10
10
  PaywallPlan,
11
11
  SubscribeIntentionType,
12
12
  SelectDefaultTierIndexFn,
13
+ CurrentSubscriptionOverride,
13
14
  } from './types';
14
15
  import { PaywallLocalization } from './paywallTextOverrides';
15
16
  import { PoweredByStigg } from '../common/PoweredByStigg';
@@ -49,6 +50,7 @@ type PaywallProps = {
49
50
  plans: PaywallPlan[];
50
51
  customer: Customer | null;
51
52
  currentSubscription: Subscription | null;
53
+ currentSubscriptionOverride?: CurrentSubscriptionOverride | null;
52
54
  selectedBillingPeriod: BillingPeriod;
53
55
  highlightedPlanId?: string;
54
56
  onBillingPeriodChanged: (billingPeriod: BillingPeriod) => void;
@@ -75,6 +77,7 @@ export const Paywall = ({
75
77
  locale,
76
78
  shouldHidePlan,
77
79
  selectDefaultTierIndex,
80
+ currentSubscriptionOverride,
78
81
  }: PaywallProps) => {
79
82
  const { stigg } = useStiggContext();
80
83
  const discountRate = calculatePaywallDiscountRate(plans);
@@ -123,11 +126,16 @@ export const Paywall = ({
123
126
  return (
124
127
  !isCustomerInCustomPlan &&
125
128
  plansToShow.some((plan) => {
126
- const tiers = getTiersPerUnitQuantities(plan, selectedBillingPeriod, currentSubscription);
129
+ const tiers = getTiersPerUnitQuantities({
130
+ plan,
131
+ billingPeriod: selectedBillingPeriod,
132
+ currentSubscription,
133
+ currentSubscriptionOverride,
134
+ });
127
135
  return Object.values(tiers).length > 0;
128
136
  })
129
137
  );
130
- }, [selectedBillingPeriod, currentSubscription, isCustomerInCustomPlan, plansToShow]);
138
+ }, [selectedBillingPeriod, currentSubscription, currentSubscriptionOverride, isCustomerInCustomPlan, plansToShow]);
131
139
 
132
140
  const withTrialLeftRow = plansToShow.some((plan) => {
133
141
  return plan.isCurrentCustomerPlan && plan.trialDaysLeft;
@@ -156,6 +164,7 @@ export const Paywall = ({
156
164
  plan={plan}
157
165
  withStartingAtRow={withStartingAtRow}
158
166
  currentSubscription={currentSubscription}
167
+ currentSubscriptionOverride={currentSubscriptionOverride}
159
168
  billingPeriod={selectedBillingPeriod}
160
169
  isHighlighted={plan.id === highlightedPlanId}
161
170
  isCustomerOnTrial={isCustomerOnTrial}
@@ -2,7 +2,12 @@ import React from 'react';
2
2
  import { BillingPeriod } from '@stigg/js-client-sdk';
3
3
  import { Paywall } from './Paywall';
4
4
  import { useLoadPaywallData } from './hooks/useLoadPaywallData';
5
- import { ShouldHidePlanFn, OnPlanSelectedCallbackFn, SelectDefaultTierIndexFn } from './types';
5
+ import {
6
+ ShouldHidePlanFn,
7
+ OnPlanSelectedCallbackFn,
8
+ SelectDefaultTierIndexFn,
9
+ CurrentSubscriptionOverrideFn,
10
+ } from './types';
6
11
  import { getResolvedPaywallLocalize, PaywallLocalization } from './paywallTextOverrides';
7
12
  import { DeepPartial } from '../../types';
8
13
  import { PaywallLoader } from './PaywallLoader';
@@ -25,6 +30,7 @@ export type PaywallContainerProps = {
25
30
  billingCountryCode?: string;
26
31
  shouldHidePlan?: ShouldHidePlanFn;
27
32
  selectDefaultTierIndex?: SelectDefaultTierIndexFn;
33
+ currentSubscriptionOverride?: CurrentSubscriptionOverrideFn;
28
34
  };
29
35
 
30
36
  export const PaywallContainer = ({
@@ -39,6 +45,7 @@ export const PaywallContainer = ({
39
45
  billingCountryCode,
40
46
  shouldHidePlan,
41
47
  selectDefaultTierIndex,
48
+ currentSubscriptionOverride: currentSubscriptionOverrideFn,
42
49
  }: PaywallContainerProps) => {
43
50
  const hasCustomerPortalContext = useCheckContextExists(CustomerPortalContext);
44
51
  let isCustomerPortalLoading = false;
@@ -52,6 +59,7 @@ export const PaywallContainer = ({
52
59
  plans,
53
60
  customer,
54
61
  currentSubscription,
62
+ currentSubscriptionOverride,
55
63
  isCustomerOnTrial,
56
64
  isLoading,
57
65
  selectedBillingPeriod,
@@ -65,6 +73,7 @@ export const PaywallContainer = ({
65
73
  showOnlyEligiblePlans,
66
74
  billingCountryCode,
67
75
  preferredBillingPeriod,
76
+ currentSubscriptionOverrideFn,
68
77
  });
69
78
  const paywallLocale = getResolvedPaywallLocalize(textOverrides);
70
79
  const handlePeriodChange = (billingPeriod: BillingPeriod) => {
@@ -82,6 +91,7 @@ export const PaywallContainer = ({
82
91
  plans={plans}
83
92
  customer={customer}
84
93
  currentSubscription={currentSubscription}
94
+ currentSubscriptionOverride={currentSubscriptionOverride}
85
95
  selectedBillingPeriod={selectedBillingPeriod}
86
96
  onBillingPeriodChanged={handlePeriodChange}
87
97
  availableBillingPeriods={availableBillingPeriods}
@@ -5,7 +5,7 @@ import classNames from 'classnames';
5
5
  import Grid from '@mui/material/Grid';
6
6
  import { PlanEntitlements } from './PlanEntitlements';
7
7
  import { PlanOfferingButton } from './PlanOfferingButton';
8
- import { PaywallPlan, SelectDefaultTierIndexFn, SubscribeIntentionType } from './types';
8
+ import { CurrentSubscriptionOverride, PaywallPlan, SelectDefaultTierIndexFn, SubscribeIntentionType } from './types';
9
9
  import { PaywallLocalization } from './paywallTextOverrides';
10
10
  import { flexLayoutMapper } from '../../theme/getResolvedTheme';
11
11
  import { Typography } from '../common/Typography';
@@ -75,6 +75,7 @@ type PlanOfferingProps = {
75
75
  plan: PaywallPlan;
76
76
  billingPeriod: BillingPeriod;
77
77
  currentSubscription: Subscription | null;
78
+ currentSubscriptionOverride?: CurrentSubscriptionOverride | null;
78
79
  isHighlighted: boolean;
79
80
  shouldShowDescriptionSection: boolean;
80
81
  hasAnnuallyPrice: boolean;
@@ -128,6 +129,7 @@ export function PlanOffering({
128
129
  billingPeriod,
129
130
  isHighlighted,
130
131
  currentSubscription,
132
+ currentSubscriptionOverride,
131
133
  shouldShowDescriptionSection,
132
134
  hasMonthlyPrice,
133
135
  hasAnnuallyPrice,
@@ -161,7 +163,13 @@ export function PlanOffering({
161
163
  }
162
164
 
163
165
  const [perUnitQuantityByFeature, setPerUnitQuantityByFeature] = useState<Record<string, number>>(
164
- getTiersPerUnitQuantities(plan, billingPeriod, currentSubscription, selectDefaultTierIndex),
166
+ getTiersPerUnitQuantities({
167
+ plan,
168
+ billingPeriod,
169
+ currentSubscription,
170
+ currentSubscriptionOverride,
171
+ selectDefaultTierIndex,
172
+ }),
165
173
  );
166
174
 
167
175
  const onPlanButtonClick = (intentionType: SubscribeIntentionType) => {
@@ -214,6 +222,7 @@ export function PlanOffering({
214
222
  customer={customer}
215
223
  plan={plan}
216
224
  currentSubscription={currentSubscription}
225
+ currentSubscriptionOverride={currentSubscriptionOverride}
217
226
  billingPeriod={billingPeriod}
218
227
  isCustomerOnTrial={isCustomerOnTrial}
219
228
  onPlanSelected={onPlanButtonClick}
@@ -4,7 +4,7 @@ import { isFunction } from 'lodash';
4
4
  import ClipLoader from 'react-spinners/ClipLoader';
5
5
  import styled from '@emotion/styled/macro';
6
6
  import { css, useTheme } from '@emotion/react';
7
- import { PaywallPlan, SubscribeIntentionType } from './types';
7
+ import { CurrentSubscriptionOverride, PaywallPlan, SubscribeIntentionType } from './types';
8
8
  import { PaywallLocalization } from './paywallTextOverrides';
9
9
  import { flexLayoutMapper } from '../../theme/getResolvedTheme';
10
10
  import { Typography } from '../common/Typography';
@@ -77,6 +77,7 @@ type PlanOfferingButtonProps = {
77
77
  customer: Customer | null;
78
78
  plan: PaywallPlan;
79
79
  currentSubscription: Subscription | null;
80
+ currentSubscriptionOverride?: CurrentSubscriptionOverride | null;
80
81
  billingPeriod: BillingPeriod;
81
82
  isCustomerOnTrial: boolean;
82
83
  paywallLocale: PaywallLocalization;
@@ -95,6 +96,7 @@ export function PlanOfferingButton({
95
96
  paywallLocale,
96
97
  withTrialLeftRow,
97
98
  currentSubscription,
99
+ currentSubscriptionOverride,
98
100
  perUnitQuantityByFeature,
99
101
  }: PlanOfferingButtonProps) {
100
102
  const theme = useTheme();
@@ -142,7 +144,12 @@ export function PlanOfferingButton({
142
144
  isSameBillingPeriod ||
143
145
  (plan.pricingType && [PricingType.Free, PricingType.Custom].includes(plan.pricingType))
144
146
  ) {
145
- const planComparison = compareSelectedTierToCurrentTier(perUnitQuantityByFeature, currentSubscription);
147
+ const planComparison = compareSelectedTierToCurrentTier({
148
+ perUnitQuantityByFeature,
149
+ plan,
150
+ currentSubscription,
151
+ currentSubscriptionOverride,
152
+ });
146
153
  switch (planComparison) {
147
154
  case PriceTierComparison.Lower:
148
155
  buttonProps.intentionType = SubscribeIntentionType.CHANGE_UNIT_QUANTITY;
@@ -1,7 +1,7 @@
1
1
  import { BillingPeriod, Paywall } from '@stigg/js-client-sdk';
2
2
  import { useEffect, useState } from 'react';
3
3
  import logger from '../../../services/logger';
4
- import { PaywallData } from '../types';
4
+ import { CurrentSubscriptionOverrideFn, PaywallData } from '../types';
5
5
  import { computeBillingPeriods } from '../utils/computeDefaultBillingPeriod';
6
6
  import { mapPaywallData } from '../utils/mapPaywallData';
7
7
  import { useStiggContext } from '../../../hooks/useStiggContext';
@@ -12,6 +12,7 @@ type UseLoadPaywallDataProps = {
12
12
  showOnlyEligiblePlans?: boolean;
13
13
  billingCountryCode?: string;
14
14
  preferredBillingPeriod?: BillingPeriod;
15
+ currentSubscriptionOverrideFn?: CurrentSubscriptionOverrideFn;
15
16
  };
16
17
 
17
18
  export function useLoadPaywallData({
@@ -20,6 +21,7 @@ export function useLoadPaywallData({
20
21
  showOnlyEligiblePlans,
21
22
  billingCountryCode,
22
23
  preferredBillingPeriod,
24
+ currentSubscriptionOverrideFn,
23
25
  }: UseLoadPaywallDataProps): PaywallData {
24
26
  const { stigg, locale } = useStiggContext();
25
27
  const [selectedBillingPeriod, setSelectedBillingPeriod] = useState(BillingPeriod.Annually);
@@ -57,7 +59,7 @@ export function useLoadPaywallData({
57
59
  void loadPaywall();
58
60
  }, [stigg, productId, stigg.isCustomerLoaded, billingCountryCode, resourceId, preferredBillingPeriod]);
59
61
 
60
- const paywallData = mapPaywallData(paywall, showOnlyEligiblePlans);
62
+ const paywallData = mapPaywallData(paywall, showOnlyEligiblePlans, currentSubscriptionOverrideFn);
61
63
 
62
64
  return {
63
65
  customer: paywall?.customer || null,
@@ -14,6 +14,7 @@ export type PaywallData = {
14
14
  plans: PaywallPlan[] | null;
15
15
  customer: Customer | null;
16
16
  currentSubscription: Subscription | null;
17
+ currentSubscriptionOverride?: CurrentSubscriptionOverride | null;
17
18
  isCustomerOnTrial: boolean;
18
19
  isLoading: boolean;
19
20
  selectedBillingPeriod: BillingPeriod;
@@ -23,6 +24,11 @@ export type PaywallData = {
23
24
  configuration?: CustomizedTheme;
24
25
  };
25
26
 
27
+ export type CurrentSubscriptionOverride = {
28
+ planId: string;
29
+ billableFeatures: BillableFeature[];
30
+ };
31
+
26
32
  export enum SubscribeIntentionType {
27
33
  START_TRIAL = 'START_TRIAL',
28
34
  UPGRADE_TRIAL_TO_PAID = 'UPGRADE_TRIAL_TO_PAID',
@@ -64,4 +70,10 @@ export type OnPlanSelectedCallbackFn = ({
64
70
 
65
71
  export type ShouldHidePlanFn = ({ plan }: { plan: PaywallPlan }) => boolean | Promise<boolean>;
66
72
 
73
+ export type CurrentSubscriptionOverrideFn = ({
74
+ currentSubscription,
75
+ }: {
76
+ currentSubscription: Subscription | null;
77
+ }) => CurrentSubscriptionOverride | null | undefined;
78
+
67
79
  export type SelectDefaultTierIndexFn = ({ plan }: { plan: PaywallPlan }) => number;
@@ -8,7 +8,7 @@ import {
8
8
  } from '@stigg/js-client-sdk';
9
9
  import isNil from 'lodash/isNil';
10
10
  import sortBy from 'lodash/sortBy';
11
- import { PaywallPlan } from '../types';
11
+ import { CurrentSubscriptionOverride, CurrentSubscriptionOverrideFn, PaywallPlan } from '../types';
12
12
  import { calculateTrialDaysLeft } from './calculateTrialDaysLeft';
13
13
  import { BillingPeriodChangeVariables, DeepPartial, DowngradeChangeVariables } from '../../../types';
14
14
  import { StiggTheme } from '../../../theme/types';
@@ -39,16 +39,23 @@ function getCustomerSubscriptionDetails(activeSubscriptions?: Subscription[] | n
39
39
  type PaywallData = {
40
40
  currentPlan?: Plan;
41
41
  currentSubscription: Subscription | null;
42
+ currentSubscriptionOverride: CurrentSubscriptionOverride | null | undefined;
42
43
  isCustomerOnTrial: boolean;
43
44
  plans: PaywallPlan[];
44
45
  paywallConfiguration?: DeepPartial<StiggTheme>;
45
46
  };
46
47
 
47
- export function mapPaywallData(paywall: Paywall | null, showOnlyEligiblePlans?: boolean): PaywallData {
48
+ export function mapPaywallData(
49
+ paywall: Paywall | null,
50
+ showOnlyEligiblePlans?: boolean,
51
+ currentSubscriptionOverrideFn?: CurrentSubscriptionOverrideFn,
52
+ ): PaywallData {
48
53
  const { plans, currency, configuration, customer, activeSubscriptions, paywallCalculatedPricePoints } = paywall || {};
49
54
  const { currentSubscription, currentPlan, isCustomerOnTrial, trialDaysLeft } =
50
55
  getCustomerSubscriptionDetails(activeSubscriptions);
51
56
 
57
+ const currentSubscriptionOverride = currentSubscriptionOverrideFn?.({ currentSubscription });
58
+
52
59
  const scheduledUpdates = currentSubscription?.scheduledUpdates || [];
53
60
  const currentCustomerPlanBillingPeriod = currentSubscription?.price?.billingPeriod;
54
61
  const downgradeSchedule = scheduledUpdates.find(
@@ -62,7 +69,9 @@ export function mapPaywallData(paywall: Paywall | null, showOnlyEligiblePlans?:
62
69
  const eligibleForProductTrial = customer?.eligibleForTrial?.find(
63
70
  (productTrial) => productTrial.productId === plan.product.id,
64
71
  );
65
- const isCurrentCustomerPlan = plan.id === currentPlan?.id;
72
+ const isCurrentCustomerPlan = currentSubscriptionOverride?.planId
73
+ ? currentSubscriptionOverride?.planId === plan.id
74
+ : plan.id === currentPlan?.id;
66
75
 
67
76
  const isNextPlan = (currentBillingPeriod: BillingPeriod) => {
68
77
  const downgradeVariables = downgradeSchedule?.scheduleVariables as DowngradeChangeVariables;
@@ -109,6 +118,7 @@ export function mapPaywallData(paywall: Paywall | null, showOnlyEligiblePlans?:
109
118
  return {
110
119
  currentPlan,
111
120
  currentSubscription,
121
+ currentSubscriptionOverride,
112
122
  isCustomerOnTrial,
113
123
  plans: paywallPlans,
114
124
  paywallConfiguration,
@@ -2,7 +2,7 @@ import { BillingModel, TiersMode, BillingPeriod, Price, PriceTierFragment, Subsc
2
2
  import isNil from 'lodash/isNil';
3
3
  import { sum } from 'lodash';
4
4
  import { PaywallPlan } from '../paywall';
5
- import { SelectDefaultTierIndexFn } from '../paywall/types';
5
+ import { CurrentSubscriptionOverride, SelectDefaultTierIndexFn } from '../paywall/types';
6
6
 
7
7
  export function getPriceFeatureUnit(price: Price) {
8
8
  if (!price.feature) {
@@ -131,12 +131,19 @@ export function isQuantityInFirstTier(tiers: PriceTierFragment[] | null | undefi
131
131
  return tiers?.[0].upTo && quantity <= tiers[0].upTo;
132
132
  }
133
133
 
134
- export function getTiersPerUnitQuantities(
135
- plan: PaywallPlan,
136
- billingPeriod: BillingPeriod,
137
- currentSubscription: Subscription | null,
138
- selectDefaultTierIndex?: SelectDefaultTierIndexFn,
139
- ): Record<string, number> {
134
+ export function getTiersPerUnitQuantities({
135
+ plan,
136
+ billingPeriod,
137
+ currentSubscription,
138
+ currentSubscriptionOverride,
139
+ selectDefaultTierIndex,
140
+ }: {
141
+ plan: PaywallPlan;
142
+ billingPeriod: BillingPeriod;
143
+ currentSubscription: Subscription | null;
144
+ currentSubscriptionOverride?: CurrentSubscriptionOverride | null;
145
+ selectDefaultTierIndex?: SelectDefaultTierIndexFn;
146
+ }): Record<string, number> {
140
147
  const planTierPrices = plan.pricePoints.filter(
141
148
  (price) => price.billingPeriod === billingPeriod && price.isTieredPrice,
142
149
  );
@@ -161,6 +168,15 @@ export function getTiersPerUnitQuantities(
161
168
  }
162
169
  }
163
170
 
171
+ if (currentSubscriptionOverride?.planId === plan.id) {
172
+ const billableFeature = currentSubscriptionOverride.billableFeatures.find(
173
+ (billableFeature) => billableFeature.featureId === featureId,
174
+ );
175
+ if (billableFeature) {
176
+ quantity = billableFeature.quantity;
177
+ }
178
+ }
179
+
164
180
  const result: Record<string, number> = {};
165
181
  result[featureId] = quantity;
166
182
 
@@ -176,22 +192,47 @@ export enum PriceTierComparison {
176
192
  Higher = 1,
177
193
  }
178
194
 
179
- export function compareSelectedTierToCurrentTier(
180
- perUnitQuantityByFeature: Record<string, number>,
181
- currentSubscription: Subscription | null,
182
- ): PriceTierComparison {
195
+ export function compareSelectedTierToCurrentTier({
196
+ perUnitQuantityByFeature,
197
+ plan,
198
+ currentSubscription,
199
+ currentSubscriptionOverride,
200
+ }: {
201
+ perUnitQuantityByFeature: Record<string, number>;
202
+ plan: PaywallPlan;
203
+ currentSubscription: Subscription | null;
204
+ currentSubscriptionOverride?: CurrentSubscriptionOverride | null;
205
+ }): PriceTierComparison {
183
206
  if (!currentSubscription) {
184
207
  return PriceTierComparison.Equal;
185
208
  }
186
209
 
187
- const currentTierPrice = currentSubscription.prices.find(
210
+ let currentTierPrice = currentSubscription.prices.find(
188
211
  (price) => price.pricingModel === BillingModel.PerUnit && price.tiersMode,
189
212
  );
213
+
214
+ const isCurrentPlanOverride = plan.id === currentSubscriptionOverride?.planId;
215
+ if (isCurrentPlanOverride) {
216
+ currentTierPrice =
217
+ plan.pricePoints.find((price) => price.pricingModel === BillingModel.PerUnit && price.tiersMode) ??
218
+ currentTierPrice;
219
+ }
220
+
190
221
  if (!currentTierPrice) {
191
222
  return PriceTierComparison.Equal;
192
223
  }
193
224
 
194
- const { featureId, unitQuantity: oldQuantity } = currentTierPrice.feature!;
225
+ const { featureId, unitQuantity } = currentTierPrice.feature!;
226
+ let oldQuantity = unitQuantity;
227
+
228
+ if (isCurrentPlanOverride) {
229
+ const billableFeature = currentSubscriptionOverride?.billableFeatures?.find(
230
+ (billableFeature) => billableFeature.featureId === featureId,
231
+ );
232
+ if (billableFeature) {
233
+ oldQuantity = billableFeature.quantity;
234
+ }
235
+ }
195
236
 
196
237
  if (isNil(oldQuantity)) {
197
238
  return PriceTierComparison.Equal;