@stigg/react-sdk 5.3.0 → 5.4.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.
- package/dist/components/utils/fixtures/price.fixtures.d.ts +7 -0
- package/dist/components/utils/priceTierUtils.d.ts +8 -2
- package/dist/components/utils/priceTierUtils.spec.d.ts +1 -0
- package/dist/react-sdk.cjs.development.js +57 -14
- package/dist/react-sdk.cjs.development.js.map +1 -1
- package/dist/react-sdk.cjs.production.min.js +1 -1
- package/dist/react-sdk.cjs.production.min.js.map +1 -1
- package/dist/react-sdk.esm.js +59 -16
- package/dist/react-sdk.esm.js.map +1 -1
- package/package.json +2 -2
- package/src/components/checkout/summary/components/LineItems.tsx +5 -8
- package/src/components/utils/calculateDiscountRate.ts +2 -2
- package/src/components/utils/fixtures/price.fixtures.ts +42 -0
- package/src/components/utils/getPaidPriceText.ts +1 -1
- package/src/components/utils/priceTierUtils.spec.ts +84 -0
- package/src/components/utils/priceTierUtils.ts +53 -4
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "5.
|
|
2
|
+
"version": "5.4.0",
|
|
3
3
|
"license": "MIT",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"typings": "dist/index.d.ts",
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"scripts": {
|
|
14
14
|
"start": "tsdx watch",
|
|
15
15
|
"build": "tsdx build",
|
|
16
|
-
"test": "tsdx test
|
|
16
|
+
"test": "tsdx test",
|
|
17
17
|
"lint": "eslint src --ext .ts,.tsx --max-warnings=0",
|
|
18
18
|
"lint-fix": "yarn run lint --fix",
|
|
19
19
|
"size": "size-limit",
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
} from '@stigg/js-client-sdk';
|
|
11
11
|
import { Typography } from '../../../common/Typography';
|
|
12
12
|
import { currencyPriceFormatter } from '../../../utils/currencyUtils';
|
|
13
|
-
import {
|
|
13
|
+
import { calculateTierPrice } from '../../../utils/priceTierUtils';
|
|
14
14
|
import { WithSkeleton } from './WithSkeleton';
|
|
15
15
|
import { Skeleton } from '../../components/Skeletons.style';
|
|
16
16
|
import { CheckoutLocalization } from '../../configurations/textOverrides';
|
|
@@ -74,14 +74,11 @@ export const BilledPriceLineItem = ({
|
|
|
74
74
|
|
|
75
75
|
let amount;
|
|
76
76
|
if (price.isTieredPrice) {
|
|
77
|
-
|
|
78
|
-
amount = calculateTierPriceVolume(tier, quantity);
|
|
77
|
+
amount = calculateTierPrice(price, quantity);
|
|
79
78
|
} else {
|
|
80
|
-
amount = price.amount
|
|
79
|
+
amount = price.amount! * quantity;
|
|
81
80
|
}
|
|
82
81
|
|
|
83
|
-
const totalLineAmount = price.isTieredPrice ? amount : amount * quantity;
|
|
84
|
-
|
|
85
82
|
return (
|
|
86
83
|
<LineItemContainer>
|
|
87
84
|
<LineItemRow style={{ alignItems: 'flex-start' }}>
|
|
@@ -91,7 +88,7 @@ export const BilledPriceLineItem = ({
|
|
|
91
88
|
</Typography>
|
|
92
89
|
{(quantity > 1 || billingPeriod === BillingPeriod.Annually) && (
|
|
93
90
|
<Typography variant="body1" color="secondary">
|
|
94
|
-
{getPriceString({ amount
|
|
91
|
+
{getPriceString({ amount, price, quantity })}
|
|
95
92
|
</Typography>
|
|
96
93
|
)}
|
|
97
94
|
</Grid>
|
|
@@ -99,7 +96,7 @@ export const BilledPriceLineItem = ({
|
|
|
99
96
|
{isPayAsYouGo && <PayAsYouGoPriceTooltip checkoutLocalization={checkoutLocalization} />}
|
|
100
97
|
<Typography variant="body1" color="secondary" style={{ wordBreak: 'break-word' }}>
|
|
101
98
|
{currencyPriceFormatter({
|
|
102
|
-
amount
|
|
99
|
+
amount,
|
|
103
100
|
currency: price.currency,
|
|
104
101
|
minimumFractionDigits: 2,
|
|
105
102
|
})}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { BillingPeriod, PaywallCalculatedPricePoint, Price } from '@stigg/js-client-sdk';
|
|
2
2
|
import isNil from 'lodash/isNil';
|
|
3
3
|
import { PaywallPlan } from '../paywall';
|
|
4
|
-
import {
|
|
4
|
+
import { calculateTierPrice } from './priceTierUtils';
|
|
5
5
|
|
|
6
6
|
export function calculateDiscountRate(monthlyPrice?: number | null, annuallyPrice?: number | null) {
|
|
7
7
|
if (!isNil(monthlyPrice) && !isNil(annuallyPrice)) {
|
|
@@ -27,7 +27,7 @@ function getPlanBillingPeriodAmount(plan: PaywallPlan, billingPeriod: BillingPer
|
|
|
27
27
|
});
|
|
28
28
|
|
|
29
29
|
if (tieredPrice) {
|
|
30
|
-
return
|
|
30
|
+
return calculateTierPrice(tieredPrice, 1);
|
|
31
31
|
}
|
|
32
32
|
}
|
|
33
33
|
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { Currency, Price, TiersMode } from '@stigg/js-client-sdk';
|
|
2
|
+
|
|
3
|
+
export const money = (amount: number, currency: Currency) => ({ amount, currency });
|
|
4
|
+
|
|
5
|
+
export const priceTiersFlat = (tiersMode: TiersMode, currency: Currency): Pick<Price, 'tiers' | 'tiersMode'> => {
|
|
6
|
+
return {
|
|
7
|
+
tiersMode,
|
|
8
|
+
tiers: [
|
|
9
|
+
{
|
|
10
|
+
upTo: 10,
|
|
11
|
+
flatPrice: money(100, currency),
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
upTo: 20,
|
|
15
|
+
flatPrice: money(180, currency),
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
upTo: 30,
|
|
19
|
+
flatPrice: money(240, currency),
|
|
20
|
+
},
|
|
21
|
+
],
|
|
22
|
+
};
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export const priceTiersUnit = (tiersMode: TiersMode, currency: Currency): Pick<Price, 'tiers' | 'tiersMode'> => {
|
|
26
|
+
return {
|
|
27
|
+
tiersMode,
|
|
28
|
+
tiers: [
|
|
29
|
+
{
|
|
30
|
+
upTo: 10,
|
|
31
|
+
unitPrice: money(10, currency),
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
upTo: 20,
|
|
35
|
+
unitPrice: money(9, currency),
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
unitPrice: money(8, currency),
|
|
39
|
+
},
|
|
40
|
+
],
|
|
41
|
+
};
|
|
42
|
+
};
|
|
@@ -43,7 +43,7 @@ export function getPaidPriceText({
|
|
|
43
43
|
tiers = price.tiers;
|
|
44
44
|
tierUnits = getPriceFeatureUnit(price);
|
|
45
45
|
|
|
46
|
-
priceNumber += calculateTierPrice(price, selectedBillingPeriod, shouldShowMonthlyPriceAmount
|
|
46
|
+
priceNumber += calculateTierPrice(price, quantity, selectedBillingPeriod, shouldShowMonthlyPriceAmount);
|
|
47
47
|
}
|
|
48
48
|
}
|
|
49
49
|
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { Currency, TiersMode } from '@stigg/js-client-sdk';
|
|
2
|
+
import { priceTiersFlat, priceTiersUnit } from './fixtures/price.fixtures';
|
|
3
|
+
import { calculateTierPrice, calculateTierPriceGraduated } from './priceTierUtils';
|
|
4
|
+
|
|
5
|
+
describe('getPrice tests', () => {
|
|
6
|
+
describe('volume flat price', () => {
|
|
7
|
+
test('should get total price', () => {
|
|
8
|
+
const price = priceTiersFlat(TiersMode.Volume, Currency.Usd);
|
|
9
|
+
expect(calculateTierPrice(price, 1)).toEqual(100);
|
|
10
|
+
expect(calculateTierPrice(price, 3)).toEqual(100);
|
|
11
|
+
expect(calculateTierPrice(price, 7)).toEqual(100);
|
|
12
|
+
expect(calculateTierPrice(price, 10)).toEqual(100);
|
|
13
|
+
expect(calculateTierPrice(price, 11)).toEqual(180);
|
|
14
|
+
expect(calculateTierPrice(price, 14)).toEqual(180);
|
|
15
|
+
expect(calculateTierPrice(price, 20)).toEqual(180);
|
|
16
|
+
expect(calculateTierPrice(price, 21)).toEqual(240);
|
|
17
|
+
expect(calculateTierPrice(price, 25)).toEqual(240);
|
|
18
|
+
expect(calculateTierPrice(price, 30)).toEqual(240);
|
|
19
|
+
|
|
20
|
+
// questionable behaviour, but that's the current implementation :)
|
|
21
|
+
expect(calculateTierPrice(price, 35)).toEqual(240);
|
|
22
|
+
expect(calculateTierPrice(price, 250)).toEqual(240);
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
describe('volume per unit', () => {
|
|
27
|
+
test('should get total price', () => {
|
|
28
|
+
const price = priceTiersUnit(TiersMode.Volume, Currency.Usd);
|
|
29
|
+
expect(calculateTierPrice(price, 1)).toEqual(10);
|
|
30
|
+
expect(calculateTierPrice(price, 3)).toEqual(30);
|
|
31
|
+
expect(calculateTierPrice(price, 7)).toEqual(70);
|
|
32
|
+
expect(calculateTierPrice(price, 10)).toEqual(100);
|
|
33
|
+
expect(calculateTierPrice(price, 11)).toEqual(99);
|
|
34
|
+
expect(calculateTierPrice(price, 14)).toEqual(126);
|
|
35
|
+
expect(calculateTierPrice(price, 20)).toEqual(180);
|
|
36
|
+
expect(calculateTierPrice(price, 21)).toEqual(168);
|
|
37
|
+
expect(calculateTierPrice(price, 25)).toEqual(200);
|
|
38
|
+
expect(calculateTierPrice(price, 30)).toEqual(240);
|
|
39
|
+
expect(calculateTierPrice(price, 35)).toEqual(280);
|
|
40
|
+
expect(calculateTierPrice(price, 240)).toEqual(1_920);
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
describe('graduated', () => {
|
|
45
|
+
test('should get total price', () => {
|
|
46
|
+
const price = priceTiersUnit(TiersMode.Graduated, Currency.Usd);
|
|
47
|
+
expect(calculateTierPrice(price, 1)).toEqual(10);
|
|
48
|
+
expect(calculateTierPrice(price, 3)).toEqual(30);
|
|
49
|
+
expect(calculateTierPrice(price, 7)).toEqual(70);
|
|
50
|
+
expect(calculateTierPrice(price, 10)).toEqual(100);
|
|
51
|
+
expect(calculateTierPrice(price, 11)).toEqual(109);
|
|
52
|
+
expect(calculateTierPrice(price, 14)).toEqual(136);
|
|
53
|
+
expect(calculateTierPrice(price, 20)).toEqual(190);
|
|
54
|
+
expect(calculateTierPrice(price, 21)).toEqual(198);
|
|
55
|
+
expect(calculateTierPrice(price, 25)).toEqual(230);
|
|
56
|
+
expect(calculateTierPrice(price, 30)).toEqual(270);
|
|
57
|
+
expect(calculateTierPrice(price, 35)).toEqual(310);
|
|
58
|
+
expect(calculateTierPrice(price, 240)).toEqual(1_950);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
test('should return nested breakdown', () => {
|
|
62
|
+
const price = priceTiersUnit(TiersMode.Graduated, Currency.Usd);
|
|
63
|
+
|
|
64
|
+
const result1 = calculateTierPriceGraduated(price.tiers!, 10);
|
|
65
|
+
expect(result1.total).toEqual(100);
|
|
66
|
+
expect(result1.breakdown).toEqual([{ unitQuantity: 10, amount: 100 }]);
|
|
67
|
+
|
|
68
|
+
const result2 = calculateTierPriceGraduated(price.tiers!, 13);
|
|
69
|
+
expect(result2.total).toEqual(127);
|
|
70
|
+
expect(result2.breakdown).toEqual([
|
|
71
|
+
{ unitQuantity: 10, amount: 100 },
|
|
72
|
+
{ unitQuantity: 3, amount: 27 },
|
|
73
|
+
]);
|
|
74
|
+
|
|
75
|
+
const result3 = calculateTierPriceGraduated(price.tiers!, 35);
|
|
76
|
+
expect(result3.total).toEqual(310);
|
|
77
|
+
expect(result3.breakdown).toEqual([
|
|
78
|
+
{ unitQuantity: 10, amount: 100 },
|
|
79
|
+
{ unitQuantity: 10, amount: 90 },
|
|
80
|
+
{ unitQuantity: 15, amount: 120 },
|
|
81
|
+
]);
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
});
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { BillingModel, TiersMode, BillingPeriod, Price, PriceTierFragment, Subscription } from '@stigg/js-client-sdk';
|
|
2
2
|
import isNil from 'lodash/isNil';
|
|
3
|
+
import { sum } from 'lodash';
|
|
3
4
|
import { PaywallPlan } from '../paywall';
|
|
4
5
|
import { SelectDefaultTierIndexFn } from '../paywall/types';
|
|
5
6
|
|
|
@@ -30,7 +31,7 @@ function getAmount(amount: number, selectedBillingPeriod?: BillingPeriod, should
|
|
|
30
31
|
return selectedBillingPeriod === BillingPeriod.Annually && shouldShowMonthlyPriceAmount ? amount / 12 : amount;
|
|
31
32
|
}
|
|
32
33
|
|
|
33
|
-
|
|
34
|
+
function calculateTierPriceVolume(
|
|
34
35
|
currentTier: PriceTierFragment,
|
|
35
36
|
perUnitQuantity: number,
|
|
36
37
|
selectedBillingPeriod?: BillingPeriod,
|
|
@@ -50,11 +51,55 @@ export function calculateTierPriceVolume(
|
|
|
50
51
|
return amount;
|
|
51
52
|
}
|
|
52
53
|
|
|
54
|
+
export function calculateTierPriceGraduated(
|
|
55
|
+
tiers: PriceTierFragment[],
|
|
56
|
+
unitQuantity: number,
|
|
57
|
+
selectedBillingPeriod?: BillingPeriod,
|
|
58
|
+
shouldShowMonthlyPriceAmount?: boolean,
|
|
59
|
+
): { total: number; breakdown: Array<{ unitQuantity: number; amount: number }> } {
|
|
60
|
+
let remainingQuantity = unitQuantity;
|
|
61
|
+
let prevUpTo = 0;
|
|
62
|
+
let currentTierIndex = 0;
|
|
63
|
+
|
|
64
|
+
const breakdown: Array<{ unitQuantity: number; amount: number }> = [];
|
|
65
|
+
|
|
66
|
+
while (remainingQuantity > 0 && currentTierIndex < tiers.length) {
|
|
67
|
+
const tier = tiers[currentTierIndex];
|
|
68
|
+
const { upTo } = tier;
|
|
69
|
+
|
|
70
|
+
if (isNil(upTo)) {
|
|
71
|
+
breakdown.push({
|
|
72
|
+
unitQuantity: remainingQuantity,
|
|
73
|
+
amount: calculateTierPriceVolume(tier, remainingQuantity, selectedBillingPeriod, shouldShowMonthlyPriceAmount),
|
|
74
|
+
});
|
|
75
|
+
remainingQuantity = 0;
|
|
76
|
+
} else {
|
|
77
|
+
const unitsInTier = upTo - prevUpTo;
|
|
78
|
+
const consumed = Math.min(remainingQuantity, unitsInTier);
|
|
79
|
+
breakdown.push({
|
|
80
|
+
unitQuantity: consumed,
|
|
81
|
+
amount: calculateTierPriceVolume(tier, consumed, selectedBillingPeriod, shouldShowMonthlyPriceAmount),
|
|
82
|
+
});
|
|
83
|
+
remainingQuantity -= consumed;
|
|
84
|
+
prevUpTo = upTo;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
currentTierIndex += 1;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const total = sum(breakdown.map(({ amount }) => amount));
|
|
91
|
+
|
|
92
|
+
return {
|
|
93
|
+
breakdown,
|
|
94
|
+
total,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
|
|
53
98
|
export function calculateTierPrice(
|
|
54
|
-
price: Price,
|
|
55
|
-
selectedBillingPeriod: BillingPeriod,
|
|
56
|
-
shouldShowMonthlyPriceAmount: boolean,
|
|
99
|
+
price: Pick<Price, 'tiers' | 'tiersMode'>,
|
|
57
100
|
unitQuantity: number,
|
|
101
|
+
selectedBillingPeriod?: BillingPeriod,
|
|
102
|
+
shouldShowMonthlyPriceAmount?: boolean,
|
|
58
103
|
): number {
|
|
59
104
|
if (!price.tiers) {
|
|
60
105
|
return 0;
|
|
@@ -65,6 +110,10 @@ export function calculateTierPrice(
|
|
|
65
110
|
const currentTier = getTierByQuantity(price.tiers, unitQuantity);
|
|
66
111
|
return calculateTierPriceVolume(currentTier, unitQuantity, selectedBillingPeriod, shouldShowMonthlyPriceAmount);
|
|
67
112
|
}
|
|
113
|
+
case TiersMode.Graduated: {
|
|
114
|
+
return calculateTierPriceGraduated(price.tiers, unitQuantity, selectedBillingPeriod, shouldShowMonthlyPriceAmount)
|
|
115
|
+
.total;
|
|
116
|
+
}
|
|
68
117
|
default:
|
|
69
118
|
return 0;
|
|
70
119
|
}
|