@blocklet/payment-react 1.14.24 → 1.14.25

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/es/libs/util.d.ts CHANGED
@@ -19,7 +19,10 @@ export declare function getStatementDescriptor(items: any[]): any;
19
19
  export declare function formatRecurring(recurring: PriceRecurring, translate?: boolean, separator?: string, locale?: string): string;
20
20
  export declare function getPriceUintAmountByCurrency(price: TPrice, currency: TPaymentCurrency): string;
21
21
  export declare function getPriceCurrencyOptions(price: TPrice): PriceCurrency[];
22
- export declare function formatLineItemPricing(item: TLineItemExpanded, currency: TPaymentCurrency, trial: number, locale?: string): {
22
+ export declare function formatLineItemPricing(item: TLineItemExpanded, currency: TPaymentCurrency, { trialEnd, trialInDays }: {
23
+ trialEnd: number;
24
+ trialInDays: number;
25
+ }, locale?: string): {
23
26
  primary: string;
24
27
  secondary?: string;
25
28
  quantity: string;
@@ -47,7 +50,17 @@ export declare function formatPriceDisplay({ amount, then, actualAmount, showThe
47
50
  actualAmount: string;
48
51
  showThen?: boolean;
49
52
  }, recurring: string, hasMetered: boolean, locale?: string): string;
50
- export declare function formatCheckoutHeadlines(items: TLineItemExpanded[], currency: TPaymentCurrency, trialInDays: number, locale?: string): {
53
+ export declare function getFreeTrialTime({ trialInDays, trialEnd }: {
54
+ trialInDays: number;
55
+ trialEnd: number;
56
+ }, locale?: string): {
57
+ count: number;
58
+ interval: string;
59
+ };
60
+ export declare function formatCheckoutHeadlines(items: TLineItemExpanded[], currency: TPaymentCurrency, { trialInDays, trialEnd }: {
61
+ trialInDays: number;
62
+ trialEnd: number;
63
+ }, locale?: string): {
51
64
  action: string;
52
65
  amount: string;
53
66
  then?: string;
package/es/libs/util.js CHANGED
@@ -202,7 +202,7 @@ export function getPriceCurrencyOptions(price) {
202
202
  }
203
203
  ];
204
204
  }
205
- export function formatLineItemPricing(item, currency, trial, locale = "en") {
205
+ export function formatLineItemPricing(item, currency, { trialEnd, trialInDays }, locale = "en") {
206
206
  const price = item.upsell_price || item.price;
207
207
  let quantity = t("common.qty", locale, { count: item.quantity });
208
208
  if (price.recurring?.usage_type === "metered" || +item.quantity === 1) {
@@ -211,6 +211,7 @@ export function formatLineItemPricing(item, currency, trial, locale = "en") {
211
211
  const unitValue = new BN(getPriceUintAmountByCurrency(price, currency));
212
212
  const total = `${fromUnitToToken(unitValue.mul(new BN(item.quantity)), currency.decimal)} ${currency.symbol}`;
213
213
  const unit = `${fromUnitToToken(unitValue, currency.decimal)} ${currency.symbol}`;
214
+ const trialResult = getFreeTrialTime({ trialInDays, trialEnd }, locale);
214
215
  const appendUnit = (v, alt) => {
215
216
  if (price.product.unit_label) {
216
217
  return `${v}/${price.product.unit_label}`;
@@ -221,9 +222,9 @@ export function formatLineItemPricing(item, currency, trial, locale = "en") {
221
222
  return quantity ? t("common.each", locale, { unit }) : "";
222
223
  };
223
224
  if (price.type === "recurring" && price.recurring) {
224
- if (trial > 0) {
225
+ if (trialResult.count > 0) {
225
226
  return {
226
- primary: t("common.trial", locale, { count: trial }),
227
+ primary: t("common.trial", locale, { count: trialResult.count, interval: trialResult.interval }),
227
228
  secondary: `${appendUnit(total, total)} ${formatRecurring(price.recurring, false, "slash", locale)}`,
228
229
  quantity
229
230
  };
@@ -415,11 +416,43 @@ export function formatPriceDisplay({ amount, then, actualAmount, showThen }, rec
415
416
  }
416
417
  return [amount, then].filter(Boolean).join(" ");
417
418
  }
418
- export function formatCheckoutHeadlines(items, currency, trialInDays, locale = "en") {
419
+ export function getFreeTrialTime({ trialInDays, trialEnd }, locale = "en") {
420
+ const now = dayjs().unix();
421
+ if (trialEnd > 0 && trialEnd > now) {
422
+ if (trialEnd - now < 3600) {
423
+ return {
424
+ count: Math.ceil((trialEnd - now) / 60),
425
+ interval: t("common.minute", locale)
426
+ };
427
+ }
428
+ if (trialEnd - now < 86400) {
429
+ return {
430
+ count: Math.ceil((trialEnd - now) / 3600),
431
+ interval: t("common.hour", locale)
432
+ };
433
+ }
434
+ return {
435
+ count: Math.ceil((trialEnd - now) / 86400),
436
+ interval: t("common.day", locale)
437
+ };
438
+ }
439
+ if (trialInDays > 0) {
440
+ return {
441
+ count: trialInDays,
442
+ interval: t("common.day", locale)
443
+ };
444
+ }
445
+ return {
446
+ count: 0,
447
+ interval: t("common.day", locale)
448
+ };
449
+ }
450
+ export function formatCheckoutHeadlines(items, currency, { trialInDays, trialEnd }, locale = "en") {
419
451
  const brand = getStatementDescriptor(items);
420
452
  const { total } = getCheckoutAmount(items, currency, trialInDays > 0);
421
453
  const actualAmount = fromUnitToToken(total, currency.decimal);
422
454
  const amount = `${fromUnitToToken(total, currency.decimal)} ${currency.symbol}`;
455
+ const trialResult = getFreeTrialTime({ trialInDays, trialEnd }, locale);
423
456
  if (items.length === 0) {
424
457
  return {
425
458
  action: t("payment.checkout.empty", locale),
@@ -453,17 +486,19 @@ export function formatCheckoutHeadlines(items, currency, trialInDays, locale = "
453
486
  if (x.price.recurring?.usage_type === "metered") {
454
487
  return acc;
455
488
  }
456
- return acc.add(new BN(getPriceUintAmountByCurrency(x.price, currency)).mul(new BN(x.quantity)));
489
+ return acc.add(
490
+ new BN(getPriceUintAmountByCurrency(x.upsell_price || x.price, currency)).mul(new BN(x.quantity))
491
+ );
457
492
  }, new BN(0)),
458
493
  currency.decimal
459
494
  ),
460
495
  currency.symbol
461
496
  ].filter(Boolean).join(" ");
462
497
  if (items.length > 1) {
463
- if (trialInDays > 0) {
498
+ if (trialResult.count > 0) {
464
499
  const result4 = {
465
500
  action: t("payment.checkout.try2", locale, { name, count: items.length - 1 }),
466
- amount: t("payment.checkout.free", locale, { count: trialInDays }),
501
+ amount: t("payment.checkout.free", locale, { count: trialResult.count, interval: trialResult.interval }),
467
502
  then: formatMeteredThen(subscription2, recurring, hasMetered && Number(subscription2) === 0, locale),
468
503
  showThen: true,
469
504
  actualAmount: "0"
@@ -485,10 +520,10 @@ export function formatCheckoutHeadlines(items, currency, trialInDays, locale = "
485
520
  priceDisplay: formatPriceDisplay(result3, recurring, hasMetered, locale)
486
521
  };
487
522
  }
488
- if (trialInDays > 0) {
523
+ if (trialResult.count > 0) {
489
524
  const result3 = {
490
525
  action: t("payment.checkout.try1", locale, { name }),
491
- amount: t("payment.checkout.free", locale, { count: trialInDays }),
526
+ amount: t("payment.checkout.free", locale, { count: trialResult.count, interval: trialResult.interval }),
492
527
  then: formatMeteredThen(subscription2, recurring, hasMetered && Number(subscription2) === 0, locale),
493
528
  showThen: true,
494
529
  actualAmount: "0"
package/es/locales/en.js CHANGED
@@ -60,9 +60,10 @@ export default flat({
60
60
  continue: "Continue",
61
61
  qty: "Qty {count}",
62
62
  each: "{unit} each",
63
- trial: "Free for {count} day{count > 1 ? 's' : ''}",
63
+ trial: "Free for {count} {interval}{count > 1 ? 's' : ''}",
64
64
  billed: "billed {rule}",
65
65
  metered: "based on usage",
66
+ minute: "minute",
66
67
  hour: "hour",
67
68
  day: "day",
68
69
  week: "week",
@@ -133,7 +134,7 @@ export default flat({
133
134
  then: "Then {subscription} {recurring}",
134
135
  meteredThen: "Then {recurring} based on usage",
135
136
  metered: "{recurring} based on usage",
136
- free: "{count} day{count > 1 ? 's' : ''} free",
137
+ free: "{count} {interval}{count > 1 ? 's' : ''} free",
137
138
  least: "continue with at least",
138
139
  completed: {
139
140
  payment: "Thanks for your purchase",
package/es/locales/zh.js CHANGED
@@ -60,9 +60,10 @@ export default flat({
60
60
  continue: "\u7EE7\u7EED",
61
61
  qty: "{count} \u4EF6",
62
62
  each: "\u6BCF\u4EF6 {unit}",
63
- trial: "\u514D\u8D39\u8BD5\u7528 {count} \u5929",
63
+ trial: "\u514D\u8D39\u8BD5\u7528 {count} {interval}",
64
64
  billed: "{rule}\u6536\u8D39",
65
65
  metered: "\u6309\u7528\u91CF",
66
+ minute: "\u5206\u949F",
66
67
  hour: "\u5C0F\u65F6",
67
68
  day: "\u5929",
68
69
  week: "\u5468",
@@ -133,7 +134,7 @@ export default flat({
133
134
  then: "\u7136\u540E {subscription} {recurring}",
134
135
  meteredThen: "\u7136\u540E{recurring}\u6309\u7528\u91CF\u8BA1\u8D39",
135
136
  metered: "{recurring}\u6309\u7528\u91CF\u8BA1\u8D39",
136
- free: "\u514D\u8D39\u8BD5\u7528 {count} \u5929",
137
+ free: "\u514D\u8D39\u8BD5\u7528 {count} {interval}",
137
138
  least: "\u81F3\u5C11",
138
139
  completed: {
139
140
  payment: "\u611F\u8C22\u60A8\u7684\u8D2D\u4E70",
@@ -61,16 +61,6 @@ const Root = styled("section")`
61
61
  background: var(--backgrounds-bg-field, #f9fafb);
62
62
  }
63
63
 
64
- // .cko-payment-card::before {
65
- // content: '';
66
- // position: absolute;
67
- // right: 0;
68
- // bottom: 0;
69
- // border: 12px solid ${(props) => props.theme.palette.primary.main};
70
- // border-top-color: transparent;
71
- // border-left-color: transparent;
72
- // }
73
-
74
64
  .cko-payment-card-unselect {
75
65
  border: 1px solid #ddd;
76
66
  padding: 4px 8px;
@@ -133,6 +133,7 @@ function PaymentInner({
133
133
  {
134
134
  items: state.checkoutSession.line_items,
135
135
  trialInDays: state.checkoutSession.subscription_data?.trial_period_days || 0,
136
+ trialEnd: state.checkoutSession.subscription_data?.trial_end || 0,
136
137
  billingThreshold: Math.max(
137
138
  state.checkoutSession.subscription_data?.billing_threshold_amount || 0,
138
139
  // @ts-ignore
@@ -321,23 +322,18 @@ export const Root = styled(Box)`
321
322
  box-sizing: border-box;
322
323
  display: flex;
323
324
  flex-direction: column;
324
- // justify-content: center;
325
325
  align-items: center;
326
326
  overflow: hidden;
327
- // min-height: ${(props) => props.mode === "standalone" ? "100vh" : "auto"};
328
327
  position: relative;
329
328
  .cko-container {
330
329
  overflow: hidden;
331
330
  width: 100%;
332
- // max-width: ${(props) => props.mode.endsWith("-minimal") ? "400px" : "1000px"};
333
331
  display: flex;
334
332
  flex-direction: row;
335
333
  justify-content: center;
336
- // gap: 4px;
337
334
  position: relative;
338
335
  flex: 1;
339
336
  padding: 1px;
340
- // padding: ${(props) => props.mode === "standalone" ? "0 16px" : "0"};
341
337
  }
342
338
 
343
339
  .base-card {
@@ -350,14 +346,11 @@ export const Root = styled(Box)`
350
346
  }
351
347
 
352
348
  .cko-overview {
353
- // width: ${(props) => props.mode.endsWith("-minimal") ? "100%" : "400px"};
354
- // min-height: ${(props) => props.mode === "standalone" ? "540px" : "auto"};
355
349
  position: relative;
356
350
  flex-direction: column;
357
351
  display: ${(props) => props.mode.endsWith("-minimal") ? "none" : "flex"};
358
352
  background: var(--backgrounds-bg-base);
359
353
  min-height: 'auto';
360
- // width: 502px;
361
354
  }
362
355
  .cko-header {
363
356
  left: 0;
@@ -385,8 +378,6 @@ export const Root = styled(Box)`
385
378
 
386
379
  .cko-payment {
387
380
  width: 502px;
388
- // width: ${(props) => props.mode.endsWith("-minimal") ? "100%" : "400px"};
389
- // box-shadow: -4px 0px 8px 0px rgba(2, 7, 19, 0.04);
390
381
  padding-left: ${(props) => props.mode === "standalone" ? "40px" : "20px"};
391
382
  position: relative;
392
383
  &:before {
@@ -411,10 +402,6 @@ export const Root = styled(Box)`
411
402
  }
412
403
 
413
404
  .cko-payment-form {
414
- // .MuiInputAdornment-positionStart {
415
- // width: 50px;
416
- // }
417
-
418
405
  .MuiFormLabel-root {
419
406
  color: var(--foregrounds-fg-base, #010714);
420
407
  font-weight: 500;
@@ -4,17 +4,19 @@ type Props = {
4
4
  item: TLineItemExpanded;
5
5
  items: TLineItemExpanded[];
6
6
  trialInDays: number;
7
+ trialEnd?: number;
7
8
  currency: TPaymentCurrency;
8
9
  onUpsell: Function;
9
10
  onDownsell: Function;
10
11
  mode?: 'normal' | 'cross-sell';
11
12
  children?: React.ReactNode;
12
13
  };
13
- declare function ProductItem({ item, items, trialInDays, currency, mode, children, onUpsell, onDownsell, }: Props): JSX.Element;
14
+ declare function ProductItem({ item, items, trialInDays, trialEnd, currency, mode, children, onUpsell, onDownsell, }: Props): JSX.Element;
14
15
  declare namespace ProductItem {
15
16
  var defaultProps: {
16
17
  mode: string;
17
18
  children: null;
19
+ trialEnd: number;
18
20
  };
19
21
  }
20
22
  export default ProductItem;
@@ -12,14 +12,17 @@ import {
12
12
  formatUpsellSaving
13
13
  } from "../libs/util.js";
14
14
  import ProductCard from "./product-card.js";
15
+ import dayjs from "../libs/dayjs.js";
15
16
  ProductItem.defaultProps = {
16
17
  mode: "normal",
17
- children: null
18
+ children: null,
19
+ trialEnd: 0
18
20
  };
19
21
  export default function ProductItem({
20
22
  item,
21
23
  items,
22
24
  trialInDays,
25
+ trialEnd = 0,
23
26
  currency,
24
27
  mode,
25
28
  children,
@@ -27,18 +30,19 @@ export default function ProductItem({
27
30
  onDownsell
28
31
  }) {
29
32
  const { t, locale } = useLocaleContext();
30
- const pricing = formatLineItemPricing(item, currency, trialInDays, locale);
33
+ const pricing = formatLineItemPricing(item, currency, { trialEnd, trialInDays }, locale);
31
34
  const saving = formatUpsellSaving(items, currency);
32
35
  const metered = item.price?.recurring?.usage_type === "metered" ? t("common.metered") : "";
33
36
  const canUpsell = mode === "normal" && items.length === 1;
34
37
  const primaryText = useMemo(() => {
35
38
  const price = item.upsell_price || item.price || {};
36
39
  const isRecurring = price?.type === "recurring" && price?.recurring;
37
- if (isRecurring && trialInDays <= 0 && price?.recurring?.usage_type !== "metered") {
40
+ const trial = trialInDays > 0 || trialEnd > dayjs().unix();
41
+ if (isRecurring && !trial && price?.recurring?.usage_type !== "metered") {
38
42
  return `${pricing.primary} ${price.recurring ? formatRecurring(price.recurring, false, "slash", locale) : ""}`;
39
43
  }
40
44
  return pricing.primary;
41
- }, [trialInDays, pricing, item, locale]);
45
+ }, [trialInDays, trialEnd, pricing, item, locale]);
42
46
  return /* @__PURE__ */ jsxs(Stack, { direction: "column", alignItems: "flex-start", spacing: 1, sx: { width: "100%" }, className: "product-item", children: [
43
47
  /* @__PURE__ */ jsxs(Stack, { direction: "column", alignItems: "flex-start", sx: { width: "100%" }, className: "product-item-content", children: [
44
48
  /* @__PURE__ */ jsxs(
@@ -4,6 +4,7 @@ type Props = {
4
4
  items: TLineItemExpanded[];
5
5
  currency: TPaymentCurrency;
6
6
  trialInDays: number;
7
+ trialEnd?: number;
7
8
  billingThreshold: number;
8
9
  showStaking?: boolean;
9
10
  onUpsell?: Function;
@@ -16,7 +17,7 @@ type Props = {
16
17
  donationSettings?: DonationSettings;
17
18
  action?: string;
18
19
  };
19
- declare function PaymentSummary({ items, currency, trialInDays, billingThreshold, onUpsell, onDownsell, onApplyCrossSell, onCancelCrossSell, onChangeAmount, checkoutSessionId, crossSellBehavior, showStaking, donationSettings, action, ...rest }: Props): import("react").JSX.Element;
20
+ declare function PaymentSummary({ items, currency, trialInDays, billingThreshold, onUpsell, onDownsell, onApplyCrossSell, onCancelCrossSell, onChangeAmount, checkoutSessionId, crossSellBehavior, showStaking, donationSettings, action, trialEnd, ...rest }: Props): import("react").JSX.Element;
20
21
  declare namespace PaymentSummary {
21
22
  var defaultProps: {
22
23
  onUpsell: any;
@@ -29,6 +30,7 @@ declare namespace PaymentSummary {
29
30
  showStaking: boolean;
30
31
  donationSettings: null;
31
32
  action: string;
33
+ trialEnd: number;
32
34
  };
33
35
  }
34
36
  export default PaymentSummary;
@@ -83,7 +83,8 @@ PaymentSummary.defaultProps = {
83
83
  crossSellBehavior: "",
84
84
  showStaking: false,
85
85
  donationSettings: null,
86
- action: ""
86
+ action: "",
87
+ trialEnd: 0
87
88
  };
88
89
  export default function PaymentSummary({
89
90
  items,
@@ -100,6 +101,7 @@ export default function PaymentSummary({
100
101
  showStaking,
101
102
  donationSettings,
102
103
  action,
104
+ trialEnd = 0,
103
105
  ...rest
104
106
  }) {
105
107
  const { t, locale } = useLocaleContext();
@@ -109,7 +111,7 @@ export default function PaymentSummary({
109
111
  const { data, runAsync } = useRequest(
110
112
  () => checkoutSessionId ? fetchCrossSell(checkoutSessionId) : Promise.resolve(null)
111
113
  );
112
- const headlines = formatCheckoutHeadlines(items, currency, trialInDays, locale);
114
+ const headlines = formatCheckoutHeadlines(items, currency, { trialEnd, trialInDays }, locale);
113
115
  const staking = showStaking ? getStakingSetup(items, currency, billingThreshold) : "0";
114
116
  const totalAmount = fromUnitToToken(
115
117
  new BN(fromTokenToUnit(headlines.actualAmount, currency?.decimal)).add(new BN(staking)).toString(),
@@ -180,6 +182,7 @@ export default function PaymentSummary({
180
182
  item: x,
181
183
  items,
182
184
  trialInDays,
185
+ trialEnd,
183
186
  currency,
184
187
  onUpsell: handleUpsell,
185
188
  onDownsell: handleDownsell,
@@ -209,6 +212,7 @@ export default function PaymentSummary({
209
212
  items,
210
213
  trialInDays,
211
214
  currency,
215
+ trialEnd,
212
216
  onUpsell: noop,
213
217
  onDownsell: noop,
214
218
  children: /* @__PURE__ */ jsxs(Stack, { direction: "row", alignItems: "center", justifyContent: "space-between", sx: { width: 1 }, children: [
@@ -19,7 +19,10 @@ export declare function getStatementDescriptor(items: any[]): any;
19
19
  export declare function formatRecurring(recurring: PriceRecurring, translate?: boolean, separator?: string, locale?: string): string;
20
20
  export declare function getPriceUintAmountByCurrency(price: TPrice, currency: TPaymentCurrency): string;
21
21
  export declare function getPriceCurrencyOptions(price: TPrice): PriceCurrency[];
22
- export declare function formatLineItemPricing(item: TLineItemExpanded, currency: TPaymentCurrency, trial: number, locale?: string): {
22
+ export declare function formatLineItemPricing(item: TLineItemExpanded, currency: TPaymentCurrency, { trialEnd, trialInDays }: {
23
+ trialEnd: number;
24
+ trialInDays: number;
25
+ }, locale?: string): {
23
26
  primary: string;
24
27
  secondary?: string;
25
28
  quantity: string;
@@ -47,7 +50,17 @@ export declare function formatPriceDisplay({ amount, then, actualAmount, showThe
47
50
  actualAmount: string;
48
51
  showThen?: boolean;
49
52
  }, recurring: string, hasMetered: boolean, locale?: string): string;
50
- export declare function formatCheckoutHeadlines(items: TLineItemExpanded[], currency: TPaymentCurrency, trialInDays: number, locale?: string): {
53
+ export declare function getFreeTrialTime({ trialInDays, trialEnd }: {
54
+ trialInDays: number;
55
+ trialEnd: number;
56
+ }, locale?: string): {
57
+ count: number;
58
+ interval: string;
59
+ };
60
+ export declare function formatCheckoutHeadlines(items: TLineItemExpanded[], currency: TPaymentCurrency, { trialInDays, trialEnd }: {
61
+ trialInDays: number;
62
+ trialEnd: number;
63
+ }, locale?: string): {
51
64
  action: string;
52
65
  amount: string;
53
66
  then?: string;
package/lib/libs/util.js CHANGED
@@ -28,6 +28,7 @@ exports.formatToDatetime = formatToDatetime;
28
28
  exports.formatTotalPrice = formatTotalPrice;
29
29
  exports.formatUpsellSaving = formatUpsellSaving;
30
30
  exports.getCheckoutAmount = getCheckoutAmount;
31
+ exports.getFreeTrialTime = getFreeTrialTime;
31
32
  exports.getInvoiceStatusColor = getInvoiceStatusColor;
32
33
  exports.getPaymentIntentStatusColor = getPaymentIntentStatusColor;
33
34
  exports.getPayoutStatusColor = getPayoutStatusColor;
@@ -266,7 +267,10 @@ function getPriceCurrencyOptions(price) {
266
267
  tiers: null
267
268
  }];
268
269
  }
269
- function formatLineItemPricing(item, currency, trial, locale = "en") {
270
+ function formatLineItemPricing(item, currency, {
271
+ trialEnd,
272
+ trialInDays
273
+ }, locale = "en") {
270
274
  const price = item.upsell_price || item.price;
271
275
  let quantity = (0, _locales.t)("common.qty", locale, {
272
276
  count: item.quantity
@@ -277,6 +281,10 @@ function formatLineItemPricing(item, currency, trial, locale = "en") {
277
281
  const unitValue = new _util.BN(getPriceUintAmountByCurrency(price, currency));
278
282
  const total = `${(0, _util.fromUnitToToken)(unitValue.mul(new _util.BN(item.quantity)), currency.decimal)} ${currency.symbol}`;
279
283
  const unit = `${(0, _util.fromUnitToToken)(unitValue, currency.decimal)} ${currency.symbol}`;
284
+ const trialResult = getFreeTrialTime({
285
+ trialInDays,
286
+ trialEnd
287
+ }, locale);
280
288
  const appendUnit = (v, alt) => {
281
289
  if (price.product.unit_label) {
282
290
  return `${v}/${price.product.unit_label}`;
@@ -289,10 +297,11 @@ function formatLineItemPricing(item, currency, trial, locale = "en") {
289
297
  }) : "";
290
298
  };
291
299
  if (price.type === "recurring" && price.recurring) {
292
- if (trial > 0) {
300
+ if (trialResult.count > 0) {
293
301
  return {
294
302
  primary: (0, _locales.t)("common.trial", locale, {
295
- count: trial
303
+ count: trialResult.count,
304
+ interval: trialResult.interval
296
305
  }),
297
306
  secondary: `${appendUnit(total, total)} ${formatRecurring(price.recurring, false, "slash", locale)}`,
298
307
  quantity
@@ -502,13 +511,54 @@ function formatPriceDisplay({
502
511
  }
503
512
  return [amount, then].filter(Boolean).join(" ");
504
513
  }
505
- function formatCheckoutHeadlines(items, currency, trialInDays, locale = "en") {
514
+ function getFreeTrialTime({
515
+ trialInDays,
516
+ trialEnd
517
+ }, locale = "en") {
518
+ const now = (0, _dayjs.default)().unix();
519
+ if (trialEnd > 0 && trialEnd > now) {
520
+ if (trialEnd - now < 3600) {
521
+ return {
522
+ count: Math.ceil((trialEnd - now) / 60),
523
+ interval: (0, _locales.t)("common.minute", locale)
524
+ };
525
+ }
526
+ if (trialEnd - now < 86400) {
527
+ return {
528
+ count: Math.ceil((trialEnd - now) / 3600),
529
+ interval: (0, _locales.t)("common.hour", locale)
530
+ };
531
+ }
532
+ return {
533
+ count: Math.ceil((trialEnd - now) / 86400),
534
+ interval: (0, _locales.t)("common.day", locale)
535
+ };
536
+ }
537
+ if (trialInDays > 0) {
538
+ return {
539
+ count: trialInDays,
540
+ interval: (0, _locales.t)("common.day", locale)
541
+ };
542
+ }
543
+ return {
544
+ count: 0,
545
+ interval: (0, _locales.t)("common.day", locale)
546
+ };
547
+ }
548
+ function formatCheckoutHeadlines(items, currency, {
549
+ trialInDays,
550
+ trialEnd
551
+ }, locale = "en") {
506
552
  const brand = getStatementDescriptor(items);
507
553
  const {
508
554
  total
509
555
  } = getCheckoutAmount(items, currency, trialInDays > 0);
510
556
  const actualAmount = (0, _util.fromUnitToToken)(total, currency.decimal);
511
557
  const amount = `${(0, _util.fromUnitToToken)(total, currency.decimal)} ${currency.symbol}`;
558
+ const trialResult = getFreeTrialTime({
559
+ trialInDays,
560
+ trialEnd
561
+ }, locale);
512
562
  if (items.length === 0) {
513
563
  return {
514
564
  action: (0, _locales.t)("payment.checkout.empty", locale),
@@ -551,17 +601,18 @@ function formatCheckoutHeadlines(items, currency, trialInDays, locale = "en") {
551
601
  if (x.price.recurring?.usage_type === "metered") {
552
602
  return acc;
553
603
  }
554
- return acc.add(new _util.BN(getPriceUintAmountByCurrency(x.price, currency)).mul(new _util.BN(x.quantity)));
604
+ return acc.add(new _util.BN(getPriceUintAmountByCurrency(x.upsell_price || x.price, currency)).mul(new _util.BN(x.quantity)));
555
605
  }, new _util.BN(0)), currency.decimal), currency.symbol].filter(Boolean).join(" ");
556
606
  if (items.length > 1) {
557
- if (trialInDays > 0) {
607
+ if (trialResult.count > 0) {
558
608
  const result4 = {
559
609
  action: (0, _locales.t)("payment.checkout.try2", locale, {
560
610
  name,
561
611
  count: items.length - 1
562
612
  }),
563
613
  amount: (0, _locales.t)("payment.checkout.free", locale, {
564
- count: trialInDays
614
+ count: trialResult.count,
615
+ interval: trialResult.interval
565
616
  }),
566
617
  then: formatMeteredThen(subscription2, recurring, hasMetered && Number(subscription2) === 0, locale),
567
618
  showThen: true,
@@ -589,13 +640,14 @@ function formatCheckoutHeadlines(items, currency, trialInDays, locale = "en") {
589
640
  priceDisplay: formatPriceDisplay(result3, recurring, hasMetered, locale)
590
641
  };
591
642
  }
592
- if (trialInDays > 0) {
643
+ if (trialResult.count > 0) {
593
644
  const result3 = {
594
645
  action: (0, _locales.t)("payment.checkout.try1", locale, {
595
646
  name
596
647
  }),
597
648
  amount: (0, _locales.t)("payment.checkout.free", locale, {
598
- count: trialInDays
649
+ count: trialResult.count,
650
+ interval: trialResult.interval
599
651
  }),
600
652
  then: formatMeteredThen(subscription2, recurring, hasMetered && Number(subscription2) === 0, locale),
601
653
  showThen: true,
package/lib/locales/en.js CHANGED
@@ -67,9 +67,10 @@ module.exports = (0, _flat.default)({
67
67
  continue: "Continue",
68
68
  qty: "Qty {count}",
69
69
  each: "{unit} each",
70
- trial: "Free for {count} day{count > 1 ? 's' : ''}",
70
+ trial: "Free for {count} {interval}{count > 1 ? 's' : ''}",
71
71
  billed: "billed {rule}",
72
72
  metered: "based on usage",
73
+ minute: "minute",
73
74
  hour: "hour",
74
75
  day: "day",
75
76
  week: "week",
@@ -140,7 +141,7 @@ module.exports = (0, _flat.default)({
140
141
  then: "Then {subscription} {recurring}",
141
142
  meteredThen: "Then {recurring} based on usage",
142
143
  metered: "{recurring} based on usage",
143
- free: "{count} day{count > 1 ? 's' : ''} free",
144
+ free: "{count} {interval}{count > 1 ? 's' : ''} free",
144
145
  least: "continue with at least",
145
146
  completed: {
146
147
  payment: "Thanks for your purchase",
package/lib/locales/zh.js CHANGED
@@ -67,9 +67,10 @@ module.exports = (0, _flat.default)({
67
67
  continue: "\u7EE7\u7EED",
68
68
  qty: "{count} \u4EF6",
69
69
  each: "\u6BCF\u4EF6 {unit}",
70
- trial: "\u514D\u8D39\u8BD5\u7528 {count} \u5929",
70
+ trial: "\u514D\u8D39\u8BD5\u7528 {count} {interval}",
71
71
  billed: "{rule}\u6536\u8D39",
72
72
  metered: "\u6309\u7528\u91CF",
73
+ minute: "\u5206\u949F",
73
74
  hour: "\u5C0F\u65F6",
74
75
  day: "\u5929",
75
76
  week: "\u5468",
@@ -140,7 +141,7 @@ module.exports = (0, _flat.default)({
140
141
  then: "\u7136\u540E {subscription} {recurring}",
141
142
  meteredThen: "\u7136\u540E{recurring}\u6309\u7528\u91CF\u8BA1\u8D39",
142
143
  metered: "{recurring}\u6309\u7528\u91CF\u8BA1\u8D39",
143
- free: "\u514D\u8D39\u8BD5\u7528 {count} \u5929",
144
+ free: "\u514D\u8D39\u8BD5\u7528 {count} {interval}",
144
145
  least: "\u81F3\u5C11",
145
146
  completed: {
146
147
  payment: "\u611F\u8C22\u60A8\u7684\u8D2D\u4E70",
@@ -79,16 +79,6 @@ const Root = (0, _system.styled)("section")`
79
79
  background: var(--backgrounds-bg-field, #f9fafb);
80
80
  }
81
81
 
82
- // .cko-payment-card::before {
83
- // content: '';
84
- // position: absolute;
85
- // right: 0;
86
- // bottom: 0;
87
- // border: 12px solid ${props => props.theme.palette.primary.main};
88
- // border-top-color: transparent;
89
- // border-left-color: transparent;
90
- // }
91
-
92
82
  .cko-payment-card-unselect {
93
83
  border: 1px solid #ddd;
94
84
  padding: 4px 8px;
@@ -194,6 +194,7 @@ function PaymentInner({
194
194
  children: [/* @__PURE__ */(0, _jsxRuntime.jsx)(_summary.default, {
195
195
  items: state.checkoutSession.line_items,
196
196
  trialInDays: state.checkoutSession.subscription_data?.trial_period_days || 0,
197
+ trialEnd: state.checkoutSession.subscription_data?.trial_end || 0,
197
198
  billingThreshold: Math.max(state.checkoutSession.subscription_data?.billing_threshold_amount || 0,
198
199
  // @ts-ignore
199
200
  state.checkoutSession.subscription_data?.min_stake_amount || 0),
@@ -395,23 +396,18 @@ const Root = exports.Root = (0, _system.styled)(_material.Box)`
395
396
  box-sizing: border-box;
396
397
  display: flex;
397
398
  flex-direction: column;
398
- // justify-content: center;
399
399
  align-items: center;
400
400
  overflow: hidden;
401
- // min-height: ${props => props.mode === "standalone" ? "100vh" : "auto"};
402
401
  position: relative;
403
402
  .cko-container {
404
403
  overflow: hidden;
405
404
  width: 100%;
406
- // max-width: ${props => props.mode.endsWith("-minimal") ? "400px" : "1000px"};
407
405
  display: flex;
408
406
  flex-direction: row;
409
407
  justify-content: center;
410
- // gap: 4px;
411
408
  position: relative;
412
409
  flex: 1;
413
410
  padding: 1px;
414
- // padding: ${props => props.mode === "standalone" ? "0 16px" : "0"};
415
411
  }
416
412
 
417
413
  .base-card {
@@ -424,14 +420,11 @@ const Root = exports.Root = (0, _system.styled)(_material.Box)`
424
420
  }
425
421
 
426
422
  .cko-overview {
427
- // width: ${props => props.mode.endsWith("-minimal") ? "100%" : "400px"};
428
- // min-height: ${props => props.mode === "standalone" ? "540px" : "auto"};
429
423
  position: relative;
430
424
  flex-direction: column;
431
425
  display: ${props => props.mode.endsWith("-minimal") ? "none" : "flex"};
432
426
  background: var(--backgrounds-bg-base);
433
427
  min-height: 'auto';
434
- // width: 502px;
435
428
  }
436
429
  .cko-header {
437
430
  left: 0;
@@ -459,8 +452,6 @@ const Root = exports.Root = (0, _system.styled)(_material.Box)`
459
452
 
460
453
  .cko-payment {
461
454
  width: 502px;
462
- // width: ${props => props.mode.endsWith("-minimal") ? "100%" : "400px"};
463
- // box-shadow: -4px 0px 8px 0px rgba(2, 7, 19, 0.04);
464
455
  padding-left: ${props => props.mode === "standalone" ? "40px" : "20px"};
465
456
  position: relative;
466
457
  &:before {
@@ -485,10 +476,6 @@ const Root = exports.Root = (0, _system.styled)(_material.Box)`
485
476
  }
486
477
 
487
478
  .cko-payment-form {
488
- // .MuiInputAdornment-positionStart {
489
- // width: 50px;
490
- // }
491
-
492
479
  .MuiFormLabel-root {
493
480
  color: var(--foregrounds-fg-base, #010714);
494
481
  font-weight: 500;
@@ -4,17 +4,19 @@ type Props = {
4
4
  item: TLineItemExpanded;
5
5
  items: TLineItemExpanded[];
6
6
  trialInDays: number;
7
+ trialEnd?: number;
7
8
  currency: TPaymentCurrency;
8
9
  onUpsell: Function;
9
10
  onDownsell: Function;
10
11
  mode?: 'normal' | 'cross-sell';
11
12
  children?: React.ReactNode;
12
13
  };
13
- declare function ProductItem({ item, items, trialInDays, currency, mode, children, onUpsell, onDownsell, }: Props): JSX.Element;
14
+ declare function ProductItem({ item, items, trialInDays, trialEnd, currency, mode, children, onUpsell, onDownsell, }: Props): JSX.Element;
14
15
  declare namespace ProductItem {
15
16
  var defaultProps: {
16
17
  mode: string;
17
18
  children: null;
19
+ trialEnd: number;
18
20
  };
19
21
  }
20
22
  export default ProductItem;
@@ -12,15 +12,18 @@ var _status = _interopRequireDefault(require("../components/status"));
12
12
  var _switchButton = _interopRequireDefault(require("../components/switch-button"));
13
13
  var _util = require("../libs/util");
14
14
  var _productCard = _interopRequireDefault(require("./product-card"));
15
+ var _dayjs = _interopRequireDefault(require("../libs/dayjs"));
15
16
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
16
17
  ProductItem.defaultProps = {
17
18
  mode: "normal",
18
- children: null
19
+ children: null,
20
+ trialEnd: 0
19
21
  };
20
22
  function ProductItem({
21
23
  item,
22
24
  items,
23
25
  trialInDays,
26
+ trialEnd = 0,
24
27
  currency,
25
28
  mode,
26
29
  children,
@@ -31,18 +34,22 @@ function ProductItem({
31
34
  t,
32
35
  locale
33
36
  } = (0, _context.useLocaleContext)();
34
- const pricing = (0, _util.formatLineItemPricing)(item, currency, trialInDays, locale);
37
+ const pricing = (0, _util.formatLineItemPricing)(item, currency, {
38
+ trialEnd,
39
+ trialInDays
40
+ }, locale);
35
41
  const saving = (0, _util.formatUpsellSaving)(items, currency);
36
42
  const metered = item.price?.recurring?.usage_type === "metered" ? t("common.metered") : "";
37
43
  const canUpsell = mode === "normal" && items.length === 1;
38
44
  const primaryText = (0, _react.useMemo)(() => {
39
45
  const price = item.upsell_price || item.price || {};
40
46
  const isRecurring = price?.type === "recurring" && price?.recurring;
41
- if (isRecurring && trialInDays <= 0 && price?.recurring?.usage_type !== "metered") {
47
+ const trial = trialInDays > 0 || trialEnd > (0, _dayjs.default)().unix();
48
+ if (isRecurring && !trial && price?.recurring?.usage_type !== "metered") {
42
49
  return `${pricing.primary} ${price.recurring ? (0, _util.formatRecurring)(price.recurring, false, "slash", locale) : ""}`;
43
50
  }
44
51
  return pricing.primary;
45
- }, [trialInDays, pricing, item, locale]);
52
+ }, [trialInDays, trialEnd, pricing, item, locale]);
46
53
  return /* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Stack, {
47
54
  direction: "column",
48
55
  alignItems: "flex-start",
@@ -4,6 +4,7 @@ type Props = {
4
4
  items: TLineItemExpanded[];
5
5
  currency: TPaymentCurrency;
6
6
  trialInDays: number;
7
+ trialEnd?: number;
7
8
  billingThreshold: number;
8
9
  showStaking?: boolean;
9
10
  onUpsell?: Function;
@@ -16,7 +17,7 @@ type Props = {
16
17
  donationSettings?: DonationSettings;
17
18
  action?: string;
18
19
  };
19
- declare function PaymentSummary({ items, currency, trialInDays, billingThreshold, onUpsell, onDownsell, onApplyCrossSell, onCancelCrossSell, onChangeAmount, checkoutSessionId, crossSellBehavior, showStaking, donationSettings, action, ...rest }: Props): import("react").JSX.Element;
20
+ declare function PaymentSummary({ items, currency, trialInDays, billingThreshold, onUpsell, onDownsell, onApplyCrossSell, onCancelCrossSell, onChangeAmount, checkoutSessionId, crossSellBehavior, showStaking, donationSettings, action, trialEnd, ...rest }: Props): import("react").JSX.Element;
20
21
  declare namespace PaymentSummary {
21
22
  var defaultProps: {
22
23
  onUpsell: any;
@@ -29,6 +30,7 @@ declare namespace PaymentSummary {
29
30
  showStaking: boolean;
30
31
  donationSettings: null;
31
32
  action: string;
33
+ trialEnd: number;
32
34
  };
33
35
  }
34
36
  export default PaymentSummary;
@@ -90,7 +90,8 @@ PaymentSummary.defaultProps = {
90
90
  crossSellBehavior: "",
91
91
  showStaking: false,
92
92
  donationSettings: null,
93
- action: ""
93
+ action: "",
94
+ trialEnd: 0
94
95
  };
95
96
  function PaymentSummary({
96
97
  items,
@@ -107,6 +108,7 @@ function PaymentSummary({
107
108
  showStaking,
108
109
  donationSettings,
109
110
  action,
111
+ trialEnd = 0,
110
112
  ...rest
111
113
  }) {
112
114
  const {
@@ -126,7 +128,10 @@ function PaymentSummary({
126
128
  data,
127
129
  runAsync
128
130
  } = (0, _ahooks.useRequest)(() => checkoutSessionId ? fetchCrossSell(checkoutSessionId) : Promise.resolve(null));
129
- const headlines = (0, _util2.formatCheckoutHeadlines)(items, currency, trialInDays, locale);
131
+ const headlines = (0, _util2.formatCheckoutHeadlines)(items, currency, {
132
+ trialEnd,
133
+ trialInDays
134
+ }, locale);
130
135
  const staking = showStaking ? getStakingSetup(items, currency, billingThreshold) : "0";
131
136
  const totalAmount = (0, _util.fromUnitToToken)(new _util.BN((0, _util.fromTokenToUnit)(headlines.actualAmount, currency?.decimal)).add(new _util.BN(staking)).toString(), currency?.decimal);
132
137
  (0, _useBus.default)("error.REQUIRE_CROSS_SELL", () => {
@@ -197,6 +202,7 @@ function PaymentSummary({
197
202
  item: x,
198
203
  items,
199
204
  trialInDays,
205
+ trialEnd,
200
206
  currency,
201
207
  onUpsell: handleUpsell,
202
208
  onDownsell: handleDownsell,
@@ -232,6 +238,7 @@ function PaymentSummary({
232
238
  items,
233
239
  trialInDays,
234
240
  currency,
241
+ trialEnd,
235
242
  onUpsell: _noop.default,
236
243
  onDownsell: _noop.default,
237
244
  children: /* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Stack, {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blocklet/payment-react",
3
- "version": "1.14.24",
3
+ "version": "1.14.25",
4
4
  "description": "Reusable react components for payment kit v2",
5
5
  "keywords": [
6
6
  "react",
@@ -93,7 +93,7 @@
93
93
  "@babel/core": "^7.25.2",
94
94
  "@babel/preset-env": "^7.25.2",
95
95
  "@babel/preset-react": "^7.24.7",
96
- "@blocklet/payment-types": "1.14.24",
96
+ "@blocklet/payment-types": "1.14.25",
97
97
  "@storybook/addon-essentials": "^7.6.20",
98
98
  "@storybook/addon-interactions": "^7.6.20",
99
99
  "@storybook/addon-links": "^7.6.20",
@@ -123,5 +123,5 @@
123
123
  "vite-plugin-babel": "^1.2.0",
124
124
  "vite-plugin-node-polyfills": "^0.21.0"
125
125
  },
126
- "gitHead": "34ddc3ea5d4e166304a4966a926bfc35796193b1"
126
+ "gitHead": "d326f7f50aeb5a0b727b1eb1f5b042f82e2e4059"
127
127
  }
package/src/libs/util.ts CHANGED
@@ -294,7 +294,7 @@ export function getPriceCurrencyOptions(price: TPrice): PriceCurrency[] {
294
294
  export function formatLineItemPricing(
295
295
  item: TLineItemExpanded,
296
296
  currency: TPaymentCurrency,
297
- trial: number,
297
+ { trialEnd, trialInDays }: { trialEnd: number; trialInDays: number },
298
298
  locale: string = 'en'
299
299
  ): { primary: string; secondary?: string; quantity: string } {
300
300
  const price = item.upsell_price || item.price;
@@ -303,13 +303,14 @@ export function formatLineItemPricing(
303
303
  if (price.recurring?.usage_type === 'metered' || +item.quantity === 1) {
304
304
  quantity = '';
305
305
  }
306
-
307
306
  const unitValue = new BN(getPriceUintAmountByCurrency(price, currency));
308
307
 
309
308
  const total = `${fromUnitToToken(unitValue.mul(new BN(item.quantity)), currency.decimal)} ${currency.symbol}`;
310
309
 
311
310
  const unit = `${fromUnitToToken(unitValue, currency.decimal)} ${currency.symbol}`;
312
311
 
312
+ const trialResult = getFreeTrialTime({ trialInDays, trialEnd }, locale);
313
+
313
314
  const appendUnit = (v: string, alt: string) => {
314
315
  if (price.product.unit_label) {
315
316
  return `${v}/${price.product.unit_label}`;
@@ -322,9 +323,9 @@ export function formatLineItemPricing(
322
323
  };
323
324
 
324
325
  if (price.type === 'recurring' && price.recurring) {
325
- if (trial > 0) {
326
+ if (trialResult.count > 0) {
326
327
  return {
327
- primary: t('common.trial', locale, { count: trial }),
328
+ primary: t('common.trial', locale, { count: trialResult.count, interval: trialResult.interval }),
328
329
  secondary: `${appendUnit(total, total)} ${formatRecurring(price.recurring, false, 'slash', locale)}`,
329
330
  quantity,
330
331
  };
@@ -559,10 +560,48 @@ export function formatPriceDisplay(
559
560
  }
560
561
  return [amount, then].filter(Boolean).join(' ');
561
562
  }
563
+
564
+ export function getFreeTrialTime(
565
+ { trialInDays, trialEnd }: { trialInDays: number; trialEnd: number },
566
+ locale: string = 'en'
567
+ ): {
568
+ count: number;
569
+ interval: string;
570
+ } {
571
+ const now = dayjs().unix();
572
+ if (trialEnd > 0 && trialEnd > now) {
573
+ if (trialEnd - now < 3600) {
574
+ return {
575
+ count: Math.ceil((trialEnd - now) / 60),
576
+ interval: t('common.minute', locale),
577
+ };
578
+ }
579
+ if (trialEnd - now < 86400) {
580
+ return {
581
+ count: Math.ceil((trialEnd - now) / 3600),
582
+ interval: t('common.hour', locale),
583
+ };
584
+ }
585
+ return {
586
+ count: Math.ceil((trialEnd - now) / 86400),
587
+ interval: t('common.day', locale),
588
+ };
589
+ }
590
+ if (trialInDays > 0) {
591
+ return {
592
+ count: trialInDays,
593
+ interval: t('common.day', locale),
594
+ };
595
+ }
596
+ return {
597
+ count: 0,
598
+ interval: t('common.day', locale),
599
+ };
600
+ }
562
601
  export function formatCheckoutHeadlines(
563
602
  items: TLineItemExpanded[],
564
603
  currency: TPaymentCurrency,
565
- trialInDays: number,
604
+ { trialInDays, trialEnd }: { trialInDays: number; trialEnd: number },
566
605
  locale: string = 'en'
567
606
  ): {
568
607
  action: string;
@@ -577,6 +616,7 @@ export function formatCheckoutHeadlines(
577
616
  const { total } = getCheckoutAmount(items, currency, trialInDays > 0);
578
617
  const actualAmount = fromUnitToToken(total, currency.decimal);
579
618
  const amount = `${fromUnitToToken(total, currency.decimal)} ${currency.symbol}`;
619
+ const trialResult = getFreeTrialTime({ trialInDays, trialEnd }, locale);
580
620
 
581
621
  // empty
582
622
  if (items.length === 0) {
@@ -618,7 +658,10 @@ export function formatCheckoutHeadlines(
618
658
  if (x.price.recurring?.usage_type === 'metered') {
619
659
  return acc;
620
660
  }
621
- return acc.add(new BN(getPriceUintAmountByCurrency(x.price, currency)).mul(new BN(x.quantity)));
661
+
662
+ return acc.add(
663
+ new BN(getPriceUintAmountByCurrency(x.upsell_price || x.price, currency)).mul(new BN(x.quantity))
664
+ );
622
665
  }, new BN(0)),
623
666
  currency.decimal
624
667
  ),
@@ -627,10 +670,10 @@ export function formatCheckoutHeadlines(
627
670
  .filter(Boolean)
628
671
  .join(' ');
629
672
  if (items.length > 1) {
630
- if (trialInDays > 0) {
673
+ if (trialResult.count > 0) {
631
674
  const result = {
632
675
  action: t('payment.checkout.try2', locale, { name, count: items.length - 1 }),
633
- amount: t('payment.checkout.free', locale, { count: trialInDays }),
676
+ amount: t('payment.checkout.free', locale, { count: trialResult.count, interval: trialResult.interval }),
634
677
  then: formatMeteredThen(subscription, recurring, hasMetered && Number(subscription) === 0, locale),
635
678
  showThen: true,
636
679
  actualAmount: '0',
@@ -653,10 +696,10 @@ export function formatCheckoutHeadlines(
653
696
  };
654
697
  }
655
698
 
656
- if (trialInDays > 0) {
699
+ if (trialResult.count > 0) {
657
700
  const result = {
658
701
  action: t('payment.checkout.try1', locale, { name }),
659
- amount: t('payment.checkout.free', locale, { count: trialInDays }),
702
+ amount: t('payment.checkout.free', locale, { count: trialResult.count, interval: trialResult.interval }),
660
703
  then: formatMeteredThen(subscription, recurring, hasMetered && Number(subscription) === 0, locale),
661
704
  showThen: true,
662
705
  actualAmount: '0',
@@ -62,9 +62,10 @@ export default flat({
62
62
  continue: 'Continue',
63
63
  qty: 'Qty {count}',
64
64
  each: '{unit} each',
65
- trial: "Free for {count} day{count > 1 ? 's' : ''}",
65
+ trial: "Free for {count} {interval}{count > 1 ? 's' : ''}",
66
66
  billed: 'billed {rule}',
67
67
  metered: 'based on usage',
68
+ minute: 'minute',
68
69
  hour: 'hour',
69
70
  day: 'day',
70
71
  week: 'week',
@@ -136,7 +137,7 @@ export default flat({
136
137
  then: 'Then {subscription} {recurring}',
137
138
  meteredThen: 'Then {recurring} based on usage',
138
139
  metered: '{recurring} based on usage',
139
- free: "{count} day{count > 1 ? 's' : ''} free",
140
+ free: "{count} {interval}{count > 1 ? 's' : ''} free",
140
141
  least: 'continue with at least',
141
142
  completed: {
142
143
  payment: 'Thanks for your purchase',
@@ -62,9 +62,10 @@ export default flat({
62
62
  continue: '继续',
63
63
  qty: '{count} 件',
64
64
  each: '每件 {unit}',
65
- trial: '免费试用 {count} ',
65
+ trial: '免费试用 {count} {interval}',
66
66
  billed: '{rule}收费',
67
67
  metered: '按用量',
68
+ minute: '分钟',
68
69
  hour: '小时',
69
70
  day: '天',
70
71
  week: '周',
@@ -135,7 +136,7 @@ export default flat({
135
136
  then: '然后 {subscription} {recurring}',
136
137
  meteredThen: '然后{recurring}按用量计费',
137
138
  metered: '{recurring}按用量计费',
138
- free: '免费试用 {count} ',
139
+ free: '免费试用 {count} {interval}',
139
140
  least: '至少',
140
141
  completed: {
141
142
  payment: '感谢您的购买',
@@ -63,16 +63,6 @@ const Root = styled<any>('section')`
63
63
  background: var(--backgrounds-bg-field, #f9fafb);
64
64
  }
65
65
 
66
- // .cko-payment-card::before {
67
- // content: '';
68
- // position: absolute;
69
- // right: 0;
70
- // bottom: 0;
71
- // border: 12px solid ${(props) => props.theme.palette.primary.main};
72
- // border-top-color: transparent;
73
- // border-left-color: transparent;
74
- // }
75
-
76
66
  .cko-payment-card-unselect {
77
67
  border: 1px solid #ddd;
78
68
  padding: 4px 8px;
@@ -155,6 +155,7 @@ function PaymentInner({
155
155
  setState({ checkoutSession: result.checkoutSession });
156
156
  onPaid(result);
157
157
  };
158
+
158
159
  return (
159
160
  <FormProvider {...methods}>
160
161
  <Stack className="cko-container" sx={{ gap: { sm: mode === 'standalone' ? 0 : mode === 'inline' ? 4 : 8 } }}>
@@ -164,6 +165,7 @@ function PaymentInner({
164
165
  <PaymentSummary
165
166
  items={state.checkoutSession.line_items}
166
167
  trialInDays={state.checkoutSession.subscription_data?.trial_period_days || 0}
168
+ trialEnd={state.checkoutSession.subscription_data?.trial_end || 0}
167
169
  billingThreshold={Math.max(
168
170
  state.checkoutSession.subscription_data?.billing_threshold_amount || 0,
169
171
  // @ts-ignore
@@ -376,23 +378,18 @@ export const Root = styled(Box)<{ mode: LiteralUnion<'standalone' | 'inline' | '
376
378
  box-sizing: border-box;
377
379
  display: flex;
378
380
  flex-direction: column;
379
- // justify-content: center;
380
381
  align-items: center;
381
382
  overflow: hidden;
382
- // min-height: ${(props) => (props.mode === 'standalone' ? '100vh' : 'auto')};
383
383
  position: relative;
384
384
  .cko-container {
385
385
  overflow: hidden;
386
386
  width: 100%;
387
- // max-width: ${(props) => (props.mode.endsWith('-minimal') ? '400px' : '1000px')};
388
387
  display: flex;
389
388
  flex-direction: row;
390
389
  justify-content: center;
391
- // gap: 4px;
392
390
  position: relative;
393
391
  flex: 1;
394
392
  padding: 1px;
395
- // padding: ${(props) => (props.mode === 'standalone' ? '0 16px' : '0')};
396
393
  }
397
394
 
398
395
  .base-card {
@@ -405,14 +402,11 @@ export const Root = styled(Box)<{ mode: LiteralUnion<'standalone' | 'inline' | '
405
402
  }
406
403
 
407
404
  .cko-overview {
408
- // width: ${(props) => (props.mode.endsWith('-minimal') ? '100%' : '400px')};
409
- // min-height: ${(props) => (props.mode === 'standalone' ? '540px' : 'auto')};
410
405
  position: relative;
411
406
  flex-direction: column;
412
407
  display: ${(props) => (props.mode.endsWith('-minimal') ? 'none' : 'flex')};
413
408
  background: var(--backgrounds-bg-base);
414
409
  min-height: 'auto';
415
- // width: 502px;
416
410
  }
417
411
  .cko-header {
418
412
  left: 0;
@@ -440,8 +434,6 @@ export const Root = styled(Box)<{ mode: LiteralUnion<'standalone' | 'inline' | '
440
434
 
441
435
  .cko-payment {
442
436
  width: 502px;
443
- // width: ${(props) => (props.mode.endsWith('-minimal') ? '100%' : '400px')};
444
- // box-shadow: -4px 0px 8px 0px rgba(2, 7, 19, 0.04);
445
437
  padding-left: ${(props) => (props.mode === 'standalone' ? '40px' : '20px')};
446
438
  position: relative;
447
439
  &:before {
@@ -466,10 +458,6 @@ export const Root = styled(Box)<{ mode: LiteralUnion<'standalone' | 'inline' | '
466
458
  }
467
459
 
468
460
  .cko-payment-form {
469
- // .MuiInputAdornment-positionStart {
470
- // width: 50px;
471
- // }
472
-
473
461
  .MuiFormLabel-root {
474
462
  color: var(--foregrounds-fg-base, #010714);
475
463
  font-weight: 500;
@@ -13,11 +13,13 @@ import {
13
13
  formatUpsellSaving,
14
14
  } from '../libs/util';
15
15
  import ProductCard from './product-card';
16
+ import dayjs from '../libs/dayjs';
16
17
 
17
18
  type Props = {
18
19
  item: TLineItemExpanded;
19
20
  items: TLineItemExpanded[];
20
21
  trialInDays: number;
22
+ trialEnd?: number;
21
23
  currency: TPaymentCurrency;
22
24
  onUpsell: Function;
23
25
  onDownsell: Function;
@@ -28,12 +30,14 @@ type Props = {
28
30
  ProductItem.defaultProps = {
29
31
  mode: 'normal',
30
32
  children: null,
33
+ trialEnd: 0,
31
34
  };
32
35
 
33
36
  export default function ProductItem({
34
37
  item,
35
38
  items,
36
39
  trialInDays,
40
+ trialEnd = 0,
37
41
  currency,
38
42
  mode,
39
43
  children,
@@ -41,18 +45,19 @@ export default function ProductItem({
41
45
  onDownsell,
42
46
  }: Props) {
43
47
  const { t, locale } = useLocaleContext();
44
- const pricing = formatLineItemPricing(item, currency, trialInDays, locale);
48
+ const pricing = formatLineItemPricing(item, currency, { trialEnd, trialInDays }, locale);
45
49
  const saving = formatUpsellSaving(items, currency);
46
50
  const metered = item.price?.recurring?.usage_type === 'metered' ? t('common.metered') : '';
47
51
  const canUpsell = mode === 'normal' && items.length === 1;
48
52
  const primaryText = useMemo(() => {
49
53
  const price = item.upsell_price || item.price || {};
50
54
  const isRecurring = price?.type === 'recurring' && price?.recurring;
51
- if (isRecurring && trialInDays <= 0 && price?.recurring?.usage_type !== 'metered') {
55
+ const trial = trialInDays > 0 || trialEnd > dayjs().unix();
56
+ if (isRecurring && !trial && price?.recurring?.usage_type !== 'metered') {
52
57
  return `${pricing.primary} ${price.recurring ? formatRecurring(price.recurring, false, 'slash', locale) : ''}`;
53
58
  }
54
59
  return pricing.primary;
55
- }, [trialInDays, pricing, item, locale]);
60
+ }, [trialInDays, trialEnd, pricing, item, locale]);
56
61
 
57
62
  return (
58
63
  <Stack direction="column" alignItems="flex-start" spacing={1} sx={{ width: '100%' }} className="product-item">
@@ -67,6 +67,7 @@ type Props = {
67
67
  items: TLineItemExpanded[];
68
68
  currency: TPaymentCurrency;
69
69
  trialInDays: number;
70
+ trialEnd?: number;
70
71
  billingThreshold: number;
71
72
  showStaking?: boolean;
72
73
  onUpsell?: Function;
@@ -131,6 +132,7 @@ PaymentSummary.defaultProps = {
131
132
  showStaking: false,
132
133
  donationSettings: null,
133
134
  action: '',
135
+ trialEnd: 0,
134
136
  };
135
137
 
136
138
  export default function PaymentSummary({
@@ -148,6 +150,7 @@ export default function PaymentSummary({
148
150
  showStaking,
149
151
  donationSettings,
150
152
  action,
153
+ trialEnd = 0,
151
154
  ...rest
152
155
  }: Props) {
153
156
  const { t, locale } = useLocaleContext();
@@ -157,7 +160,7 @@ export default function PaymentSummary({
157
160
  const { data, runAsync } = useRequest(() =>
158
161
  checkoutSessionId ? fetchCrossSell(checkoutSessionId) : Promise.resolve(null)
159
162
  );
160
- const headlines = formatCheckoutHeadlines(items, currency, trialInDays, locale);
163
+ const headlines = formatCheckoutHeadlines(items, currency, { trialEnd, trialInDays }, locale);
161
164
  const staking = showStaking ? getStakingSetup(items, currency, billingThreshold) : '0';
162
165
  const totalAmount = fromUnitToToken(
163
166
  new BN(fromTokenToUnit(headlines.actualAmount, currency?.decimal)).add(new BN(staking)).toString(),
@@ -231,6 +234,7 @@ export default function PaymentSummary({
231
234
  item={x}
232
235
  items={items}
233
236
  trialInDays={trialInDays}
237
+ trialEnd={trialEnd}
234
238
  currency={currency}
235
239
  onUpsell={handleUpsell}
236
240
  onDownsell={handleDownsell}>
@@ -260,6 +264,7 @@ export default function PaymentSummary({
260
264
  items={items}
261
265
  trialInDays={trialInDays}
262
266
  currency={currency}
267
+ trialEnd={trialEnd}
263
268
  onUpsell={noop}
264
269
  onDownsell={noop}>
265
270
  <Stack direction="row" alignItems="center" justifyContent="space-between" sx={{ width: 1 }}>