payment-kit 1.13.246 → 1.13.247
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.
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/* eslint-disable no-await-in-loop */
|
|
1
2
|
import component from '@blocklet/sdk/lib/component';
|
|
2
3
|
import { BN } from '@ocap/util';
|
|
3
4
|
import type { LiteralUnion } from 'type-fest';
|
|
@@ -7,11 +8,13 @@ import {
|
|
|
7
8
|
Customer,
|
|
8
9
|
Invoice,
|
|
9
10
|
InvoiceItem,
|
|
11
|
+
PaymentCurrency,
|
|
10
12
|
Price,
|
|
11
13
|
PriceRecurring,
|
|
12
14
|
Subscription,
|
|
13
15
|
SubscriptionItem,
|
|
14
16
|
TLineItemExpanded,
|
|
17
|
+
UsageRecord,
|
|
15
18
|
} from '../store/models';
|
|
16
19
|
import dayjs from './dayjs';
|
|
17
20
|
import logger from './logger';
|
|
@@ -377,3 +380,50 @@ export async function expandSubscriptionItems(subscriptionId: string) {
|
|
|
377
380
|
const items = await SubscriptionItem.findAll({ where: { subscription_id: subscriptionId } });
|
|
378
381
|
return Price.expand(items.map((x) => x.toJSON()));
|
|
379
382
|
}
|
|
383
|
+
|
|
384
|
+
export async function getUpcomingInvoiceAmount(subscriptionId: string) {
|
|
385
|
+
const subscription = await Subscription.findByPk(subscriptionId);
|
|
386
|
+
if (!subscription) {
|
|
387
|
+
throw new Error('Subscription not found');
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
if (subscription.isActive() === false) {
|
|
391
|
+
throw new Error('Subscription not active, so usage check is skipped');
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
const currency = await PaymentCurrency.findByPk(subscription.currency_id);
|
|
395
|
+
|
|
396
|
+
const items = await SubscriptionItem.findAll({ where: { subscription_id: subscriptionId } });
|
|
397
|
+
const expanded = await Price.expand(items.map((x) => x.toJSON()));
|
|
398
|
+
|
|
399
|
+
let amount = new BN(0);
|
|
400
|
+
for (const item of expanded) {
|
|
401
|
+
const price = getSubscriptionItemPrice(item);
|
|
402
|
+
if (price.type === 'recurring') {
|
|
403
|
+
const unit = getPriceUintAmountByCurrency(price, subscription.currency_id);
|
|
404
|
+
if (price.recurring?.usage_type === 'licensed') {
|
|
405
|
+
amount = amount.add(new BN(unit).mul(new BN(item.quantity)));
|
|
406
|
+
}
|
|
407
|
+
if (price.recurring?.usage_type === 'metered') {
|
|
408
|
+
const rawQuantity = await UsageRecord.getSummary({
|
|
409
|
+
// @ts-ignore
|
|
410
|
+
id: item?.id as string,
|
|
411
|
+
start: subscription.current_period_start,
|
|
412
|
+
end: subscription.current_period_end,
|
|
413
|
+
method: price.recurring?.aggregate_usage as any,
|
|
414
|
+
dryRun: true,
|
|
415
|
+
});
|
|
416
|
+
// @ts-ignore
|
|
417
|
+
const quantity = price.transformQuantity(rawQuantity);
|
|
418
|
+
amount = amount.add(new BN(unit).mul(new BN(quantity)));
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
return {
|
|
424
|
+
amount: amount.toString(),
|
|
425
|
+
start: subscription.current_period_start,
|
|
426
|
+
end: subscription.current_period_end,
|
|
427
|
+
currency,
|
|
428
|
+
};
|
|
429
|
+
}
|
|
@@ -12,7 +12,12 @@ import logger from '../libs/logger';
|
|
|
12
12
|
import { isDelegationSufficientForPayment } from '../libs/payment';
|
|
13
13
|
import { authenticate } from '../libs/security';
|
|
14
14
|
import { expandLineItems, getFastCheckoutAmount, isLineItemAligned } from '../libs/session';
|
|
15
|
-
import {
|
|
15
|
+
import {
|
|
16
|
+
createProration,
|
|
17
|
+
getSubscriptionCreateSetup,
|
|
18
|
+
getSubscriptionRefundSetup,
|
|
19
|
+
getUpcomingInvoiceAmount,
|
|
20
|
+
} from '../libs/subscription';
|
|
16
21
|
import { MAX_SUBSCRIPTION_ITEM_COUNT, formatMetadata } from '../libs/util';
|
|
17
22
|
import { invoiceQueue } from '../queues/invoice';
|
|
18
23
|
import { addSubscriptionJob, subscriptionQueue } from '../queues/subscription';
|
|
@@ -1469,4 +1474,15 @@ router.get('/:id/summary', authPortal, async (req, res) => {
|
|
|
1469
1474
|
}
|
|
1470
1475
|
});
|
|
1471
1476
|
|
|
1477
|
+
// Get upcoming invoice amount
|
|
1478
|
+
router.get('/:id/upcoming', authPortal, async (req, res) => {
|
|
1479
|
+
try {
|
|
1480
|
+
const result = await getUpcomingInvoiceAmount(req.params.id as string);
|
|
1481
|
+
return res.json(result);
|
|
1482
|
+
} catch (err) {
|
|
1483
|
+
console.error(err);
|
|
1484
|
+
return res.json({ error: err.message });
|
|
1485
|
+
}
|
|
1486
|
+
});
|
|
1487
|
+
|
|
1472
1488
|
export default router;
|
package/blocklet.yml
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "payment-kit",
|
|
3
|
-
"version": "1.13.
|
|
3
|
+
"version": "1.13.247",
|
|
4
4
|
"scripts": {
|
|
5
5
|
"dev": "cross-env COMPONENT_STORE_URL=https://test.store.blocklet.dev blocklet dev --open",
|
|
6
6
|
"eject": "vite eject",
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
"@arcblock/ux": "^2.9.77",
|
|
52
52
|
"@arcblock/validator": "^1.18.116",
|
|
53
53
|
"@blocklet/logger": "1.16.26",
|
|
54
|
-
"@blocklet/payment-react": "1.13.
|
|
54
|
+
"@blocklet/payment-react": "1.13.247",
|
|
55
55
|
"@blocklet/sdk": "1.16.26",
|
|
56
56
|
"@blocklet/ui-react": "^2.9.77",
|
|
57
57
|
"@blocklet/uploader": "^0.1.6",
|
|
@@ -116,7 +116,7 @@
|
|
|
116
116
|
"devDependencies": {
|
|
117
117
|
"@abtnode/types": "1.16.26",
|
|
118
118
|
"@arcblock/eslint-config-ts": "^0.3.0",
|
|
119
|
-
"@blocklet/payment-types": "1.13.
|
|
119
|
+
"@blocklet/payment-types": "1.13.247",
|
|
120
120
|
"@types/cookie-parser": "^1.4.7",
|
|
121
121
|
"@types/cors": "^2.8.17",
|
|
122
122
|
"@types/dotenv-flow": "^3.3.3",
|
|
@@ -155,5 +155,5 @@
|
|
|
155
155
|
"parser": "typescript"
|
|
156
156
|
}
|
|
157
157
|
},
|
|
158
|
-
"gitHead": "
|
|
158
|
+
"gitHead": "517ee5ea4b2af99ce581dc74904ef30b08cbd32e"
|
|
159
159
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
|
|
2
|
-
import { formatTime } from '@blocklet/payment-react';
|
|
2
|
+
import { api, formatBNStr, formatTime } from '@blocklet/payment-react';
|
|
3
3
|
import type { TSubscriptionExpanded } from '@blocklet/payment-types';
|
|
4
|
+
import { useRequest } from 'ahooks';
|
|
4
5
|
|
|
5
6
|
import InfoMetric from '../info-metric';
|
|
6
7
|
|
|
@@ -8,8 +9,14 @@ type Props = {
|
|
|
8
9
|
subscription: TSubscriptionExpanded;
|
|
9
10
|
};
|
|
10
11
|
|
|
12
|
+
const fetchUpcoming = (id: string): Promise<{ amount: string }> => {
|
|
13
|
+
return api.get(`/api/subscriptions/${id}/upcoming`).then((res) => res.data);
|
|
14
|
+
};
|
|
15
|
+
|
|
11
16
|
export default function SubscriptionMetrics({ subscription }: Props) {
|
|
12
17
|
const { t } = useLocaleContext();
|
|
18
|
+
const { data: upcoming } = useRequest(() => fetchUpcoming(subscription.id));
|
|
19
|
+
|
|
13
20
|
let scheduleToCancelTime = 0;
|
|
14
21
|
if (['active', 'trialing', 'past_due'].includes(subscription.status) && subscription.cancel_at) {
|
|
15
22
|
scheduleToCancelTime = subscription.cancel_at * 1000;
|
|
@@ -31,6 +38,15 @@ export default function SubscriptionMetrics({ subscription }: Props) {
|
|
|
31
38
|
divider
|
|
32
39
|
/>
|
|
33
40
|
)}
|
|
41
|
+
{upcoming && upcoming.amount !== '0' && (
|
|
42
|
+
<InfoMetric
|
|
43
|
+
label={t('admin.subscription.nextInvoiceAmount')}
|
|
44
|
+
value={`${formatBNStr(upcoming.amount, subscription.paymentCurrency.decimal)} ${
|
|
45
|
+
subscription.paymentCurrency.symbol
|
|
46
|
+
} (${t('common.estimated')})`}
|
|
47
|
+
divider
|
|
48
|
+
/>
|
|
49
|
+
)}
|
|
34
50
|
{scheduleToCancelTime > 0 && (
|
|
35
51
|
<InfoMetric label={t('admin.subscription.cancel.schedule')} value={formatTime(scheduleToCancelTime)} divider />
|
|
36
52
|
)}
|
package/src/locales/en.tsx
CHANGED
|
@@ -4,6 +4,7 @@ export default flat({
|
|
|
4
4
|
common: {
|
|
5
5
|
redirecting: 'Redirecting...',
|
|
6
6
|
title: 'Name',
|
|
7
|
+
estimated: 'Estimated',
|
|
7
8
|
metadata: {
|
|
8
9
|
label: 'Metadata',
|
|
9
10
|
add: 'Add more metadata',
|
|
@@ -384,6 +385,7 @@ export default flat({
|
|
|
384
385
|
discount: 'Discount',
|
|
385
386
|
startedAt: 'Started',
|
|
386
387
|
nextInvoice: 'Next Invoice',
|
|
388
|
+
nextInvoiceAmount: 'Next Invoice Amount',
|
|
387
389
|
itemId: 'Subscription Item ID',
|
|
388
390
|
update: 'Update subscription',
|
|
389
391
|
resume: 'Resume payment collection',
|
package/src/locales/zh.tsx
CHANGED
|
@@ -4,6 +4,7 @@ export default flat({
|
|
|
4
4
|
common: {
|
|
5
5
|
redirecting: '跳转中...',
|
|
6
6
|
title: '名称',
|
|
7
|
+
estimated: '预估',
|
|
7
8
|
metadata: {
|
|
8
9
|
label: '元数据',
|
|
9
10
|
add: '添加更多元数据',
|
|
@@ -375,7 +376,8 @@ export default flat({
|
|
|
375
376
|
trialEnd: '试用期结束于{date}',
|
|
376
377
|
discount: '折扣',
|
|
377
378
|
startedAt: '开始于',
|
|
378
|
-
nextInvoice: '
|
|
379
|
+
nextInvoice: '下次账单时间',
|
|
380
|
+
nextInvoiceAmount: '下次账单金额',
|
|
379
381
|
itemId: '订阅项目ID',
|
|
380
382
|
update: '更新订阅',
|
|
381
383
|
resume: '恢复付款',
|