payment-kit 1.19.0 → 1.19.1
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/api/src/crons/index.ts +8 -0
- package/api/src/index.ts +4 -0
- package/api/src/libs/credit-grant.ts +146 -0
- package/api/src/libs/env.ts +1 -0
- package/api/src/libs/invoice.ts +4 -3
- package/api/src/libs/notification/template/base.ts +388 -2
- package/api/src/libs/notification/template/customer-credit-grant-granted.ts +149 -0
- package/api/src/libs/notification/template/customer-credit-grant-low-balance.ts +151 -0
- package/api/src/libs/notification/template/customer-credit-insufficient.ts +254 -0
- package/api/src/libs/notification/template/subscription-canceled.ts +193 -202
- package/api/src/libs/notification/template/subscription-refund-succeeded.ts +215 -237
- package/api/src/libs/notification/template/subscription-renewed.ts +130 -200
- package/api/src/libs/notification/template/subscription-succeeded.ts +100 -202
- package/api/src/libs/notification/template/subscription-trial-start.ts +142 -188
- package/api/src/libs/notification/template/subscription-trial-will-end.ts +146 -174
- package/api/src/libs/notification/template/subscription-upgraded.ts +96 -192
- package/api/src/libs/notification/template/subscription-will-canceled.ts +94 -135
- package/api/src/libs/notification/template/subscription-will-renew.ts +220 -245
- package/api/src/libs/payment.ts +69 -0
- package/api/src/libs/queue/index.ts +3 -2
- package/api/src/libs/session.ts +8 -0
- package/api/src/libs/subscription.ts +74 -3
- package/api/src/libs/ws.ts +23 -1
- package/api/src/locales/en.ts +33 -0
- package/api/src/locales/zh.ts +31 -0
- package/api/src/queues/credit-consume.ts +715 -0
- package/api/src/queues/credit-grant.ts +572 -0
- package/api/src/queues/notification.ts +173 -128
- package/api/src/queues/payment.ts +210 -122
- package/api/src/queues/subscription.ts +179 -0
- package/api/src/routes/checkout-sessions.ts +157 -9
- package/api/src/routes/connect/shared.ts +3 -2
- package/api/src/routes/credit-grants.ts +241 -0
- package/api/src/routes/credit-transactions.ts +208 -0
- package/api/src/routes/index.ts +8 -0
- package/api/src/routes/meter-events.ts +347 -0
- package/api/src/routes/meters.ts +219 -0
- package/api/src/routes/payment-currencies.ts +14 -2
- package/api/src/routes/payment-links.ts +1 -1
- package/api/src/routes/payment-methods.ts +14 -2
- package/api/src/routes/prices.ts +43 -0
- package/api/src/routes/pricing-table.ts +13 -7
- package/api/src/routes/products.ts +63 -4
- package/api/src/routes/settings.ts +1 -1
- package/api/src/routes/subscriptions.ts +4 -0
- package/api/src/store/migrations/20250610-billing-credit.ts +43 -0
- package/api/src/store/models/credit-grant.ts +486 -0
- package/api/src/store/models/credit-transaction.ts +268 -0
- package/api/src/store/models/customer.ts +8 -0
- package/api/src/store/models/index.ts +52 -1
- package/api/src/store/models/meter-event.ts +423 -0
- package/api/src/store/models/meter.ts +176 -0
- package/api/src/store/models/payment-currency.ts +66 -14
- package/api/src/store/models/price.ts +6 -0
- package/api/src/store/models/product.ts +2 -2
- package/api/src/store/models/subscription.ts +24 -0
- package/api/src/store/models/types.ts +28 -2
- package/api/tests/libs/subscription.spec.ts +53 -0
- package/blocklet.yml +9 -1
- package/package.json +4 -4
- package/scripts/sdk.js +233 -1
- package/src/app.tsx +10 -0
- package/src/components/collapse.tsx +11 -1
- package/src/components/customer/credit-grant-item-list.tsx +99 -0
- package/src/components/customer/credit-overview.tsx +233 -0
- package/src/components/customer/form.tsx +5 -2
- package/src/components/invoice/list.tsx +19 -1
- package/src/components/metadata/form.tsx +286 -90
- package/src/components/meter/actions.tsx +101 -0
- package/src/components/meter/add-usage-dialog.tsx +239 -0
- package/src/components/meter/events-list.tsx +657 -0
- package/src/components/meter/form.tsx +245 -0
- package/src/components/meter/products.tsx +264 -0
- package/src/components/meter/usage-guide.tsx +174 -0
- package/src/components/payment-currency/form.tsx +2 -0
- package/src/components/payment-intent/list.tsx +19 -1
- package/src/components/payment-link/preview.tsx +1 -1
- package/src/components/payment-link/product-select.tsx +52 -12
- package/src/components/payment-method/arcblock.tsx +2 -0
- package/src/components/payment-method/base.tsx +2 -0
- package/src/components/payment-method/bitcoin.tsx +2 -0
- package/src/components/payment-method/ethereum.tsx +2 -0
- package/src/components/payment-method/stripe.tsx +2 -0
- package/src/components/payouts/list.tsx +19 -1
- package/src/components/price/currency-select.tsx +51 -31
- package/src/components/price/form.tsx +881 -407
- package/src/components/pricing-table/preview.tsx +1 -1
- package/src/components/product/add-price.tsx +9 -7
- package/src/components/product/create.tsx +7 -4
- package/src/components/product/edit-price.tsx +21 -12
- package/src/components/product/features.tsx +17 -7
- package/src/components/product/form.tsx +104 -89
- package/src/components/refund/list.tsx +19 -1
- package/src/components/section/header.tsx +5 -18
- package/src/components/subscription/items/index.tsx +1 -1
- package/src/components/subscription/metrics.tsx +37 -5
- package/src/components/subscription/portal/actions.tsx +2 -1
- package/src/contexts/products.tsx +26 -9
- package/src/hooks/subscription.ts +34 -0
- package/src/libs/meter-utils.ts +196 -0
- package/src/libs/util.ts +4 -0
- package/src/locales/en.tsx +385 -4
- package/src/locales/zh.tsx +364 -0
- package/src/pages/admin/billing/index.tsx +61 -33
- package/src/pages/admin/billing/invoices/detail.tsx +1 -1
- package/src/pages/admin/billing/meters/create.tsx +60 -0
- package/src/pages/admin/billing/meters/detail.tsx +435 -0
- package/src/pages/admin/billing/meters/index.tsx +210 -0
- package/src/pages/admin/billing/meters/meter-event.tsx +346 -0
- package/src/pages/admin/billing/subscriptions/detail.tsx +47 -14
- package/src/pages/admin/customers/customers/credit-grant/detail.tsx +391 -0
- package/src/pages/admin/customers/customers/detail.tsx +22 -10
- package/src/pages/admin/customers/index.tsx +5 -0
- package/src/pages/admin/developers/events/detail.tsx +1 -1
- package/src/pages/admin/developers/index.tsx +1 -1
- package/src/pages/admin/payments/intents/detail.tsx +1 -1
- package/src/pages/admin/payments/payouts/detail.tsx +1 -1
- package/src/pages/admin/payments/refunds/detail.tsx +1 -1
- package/src/pages/admin/products/index.tsx +3 -2
- package/src/pages/admin/products/links/detail.tsx +1 -1
- package/src/pages/admin/products/prices/actions.tsx +16 -4
- package/src/pages/admin/products/prices/detail.tsx +30 -3
- package/src/pages/admin/products/prices/list.tsx +8 -1
- package/src/pages/admin/products/pricing-tables/detail.tsx +1 -1
- package/src/pages/admin/products/products/create.tsx +233 -57
- package/src/pages/admin/products/products/detail.tsx +2 -1
- package/src/pages/admin/settings/payment-methods/index.tsx +3 -0
- package/src/pages/customer/credit-grant/detail.tsx +308 -0
- package/src/pages/customer/index.tsx +35 -2
- package/src/pages/customer/recharge/account.tsx +5 -5
- package/src/pages/customer/subscription/change-payment.tsx +4 -2
- package/src/pages/customer/subscription/detail.tsx +48 -14
- package/src/pages/customer/subscription/embed.tsx +1 -1
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
usePaymentContext,
|
|
8
8
|
OverdueInvoicePayment,
|
|
9
9
|
} from '@blocklet/payment-react';
|
|
10
|
+
|
|
10
11
|
import type { GroupedBN, TCustomerExpanded } from '@blocklet/payment-types';
|
|
11
12
|
import {
|
|
12
13
|
ExpandMore,
|
|
@@ -38,6 +39,7 @@ import { flatten, isEmpty } from 'lodash';
|
|
|
38
39
|
import { memo, useEffect, useState } from 'react';
|
|
39
40
|
import { useNavigate, useSearchParams } from 'react-router-dom';
|
|
40
41
|
import { joinURL } from 'ufo';
|
|
42
|
+
import CreditOverview from '../../components/customer/credit-overview';
|
|
41
43
|
|
|
42
44
|
import { useTransitionContext } from '../../components/progress-bar';
|
|
43
45
|
import CurrentSubscriptions from '../../components/subscription/portal/list';
|
|
@@ -46,7 +48,15 @@ import { useSessionContext } from '../../contexts/session';
|
|
|
46
48
|
import api from '../../libs/api';
|
|
47
49
|
import CustomerRevenueList from '../../components/payouts/portal/list';
|
|
48
50
|
|
|
49
|
-
type Result = TCustomerExpanded & {
|
|
51
|
+
type Result = TCustomerExpanded & {
|
|
52
|
+
summary: { [key: string]: GroupedBN };
|
|
53
|
+
creditSummary?: {
|
|
54
|
+
grants?: { [key: string]: { totalAmount: string; remainingAmount: string; grantCount: number } };
|
|
55
|
+
transactions?: any;
|
|
56
|
+
pendingAmount?: { [key: string]: string };
|
|
57
|
+
};
|
|
58
|
+
error?: string;
|
|
59
|
+
};
|
|
50
60
|
|
|
51
61
|
const fetchData = (): Promise<Result> => {
|
|
52
62
|
return api.get('/api/customers/me?skipError=true&create=true').then((res) => res.data);
|
|
@@ -273,6 +283,12 @@ export default function CustomerHome() {
|
|
|
273
283
|
}
|
|
274
284
|
};
|
|
275
285
|
|
|
286
|
+
// const handleAddCredit = (currency: any) => {
|
|
287
|
+
// if (currency?.id) {
|
|
288
|
+
// navigate(`/customer/credit-packages?currency=${currency.id}`);
|
|
289
|
+
// }
|
|
290
|
+
// };
|
|
291
|
+
|
|
276
292
|
const onToggleActive = (e: SelectChangeEvent) => {
|
|
277
293
|
setSubscriptionLoading(true);
|
|
278
294
|
setState({ onlyActive: e.target.value === 'active' });
|
|
@@ -281,6 +297,10 @@ export default function CustomerHome() {
|
|
|
281
297
|
}, 300);
|
|
282
298
|
};
|
|
283
299
|
|
|
300
|
+
// const handleViewCreditDetails = (currencyId: string) => {
|
|
301
|
+
// navigate(`/customer/credit-grants?currency=${currencyId}`);
|
|
302
|
+
// };
|
|
303
|
+
|
|
284
304
|
const SubscriptionCard =
|
|
285
305
|
loadingCard || !hasSubscriptions ? null : (
|
|
286
306
|
<Box className="base-card section section-subscription">
|
|
@@ -428,7 +448,6 @@ export default function CustomerHome() {
|
|
|
428
448
|
gap: 1,
|
|
429
449
|
mt: 1.5,
|
|
430
450
|
}}>
|
|
431
|
-
{/* 使用配置渲染卡片 */}
|
|
432
451
|
{Object.entries(CARD_CONFIG).map(([type, config]) => {
|
|
433
452
|
if (!isCardVisible(type, config, data, c, method)) {
|
|
434
453
|
return null;
|
|
@@ -460,6 +479,18 @@ export default function CustomerHome() {
|
|
|
460
479
|
</Box>
|
|
461
480
|
);
|
|
462
481
|
|
|
482
|
+
// 独立的Credit Card组件
|
|
483
|
+
const CreditCard = loadingCard ? (
|
|
484
|
+
<CardSkeleton height={400} />
|
|
485
|
+
) : (
|
|
486
|
+
<Box className="base-card section section-credit">
|
|
487
|
+
<Box className="section-header" sx={{ mb: 2 }}>
|
|
488
|
+
<Typography variant="h3">{t('admin.creditGrants.title')}</Typography>
|
|
489
|
+
</Box>
|
|
490
|
+
<CreditOverview customerId={data?.id} settings={settings} />
|
|
491
|
+
</Box>
|
|
492
|
+
);
|
|
493
|
+
|
|
463
494
|
const InvoiceCard = loadingCard ? (
|
|
464
495
|
<CardSkeleton height={200} />
|
|
465
496
|
) : (
|
|
@@ -551,6 +582,8 @@ export default function CustomerHome() {
|
|
|
551
582
|
<Root>
|
|
552
583
|
{SummaryCard}
|
|
553
584
|
{SummaryCard && <Divider />}
|
|
585
|
+
{CreditCard}
|
|
586
|
+
{CreditCard && <Divider />}
|
|
554
587
|
{SubscriptionCard}
|
|
555
588
|
{SubscriptionCard && <Divider />}
|
|
556
589
|
{InvoiceCard}
|
|
@@ -553,14 +553,14 @@ export default function BalanceRechargePage() {
|
|
|
553
553
|
helperText={amountError}
|
|
554
554
|
onChange={handleAmountChange}
|
|
555
555
|
inputRef={customInputRef}
|
|
556
|
+
autoComplete="off"
|
|
556
557
|
slotProps={{
|
|
557
558
|
input: {
|
|
558
559
|
endAdornment: <Typography>{currency.symbol}</Typography>,
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
},
|
|
560
|
+
},
|
|
561
|
+
htmlInput: {
|
|
562
|
+
min: 0,
|
|
563
|
+
max: MAX_SAFE_AMOUNT,
|
|
564
564
|
},
|
|
565
565
|
}}
|
|
566
566
|
/>
|
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
getPriceCurrencyOptions,
|
|
18
18
|
getStatementDescriptor,
|
|
19
19
|
isValidCountry,
|
|
20
|
+
showStaking,
|
|
20
21
|
usePaymentContext,
|
|
21
22
|
} from '@blocklet/payment-react';
|
|
22
23
|
import type { TCustomer, TPaymentCurrency, TPaymentMethod, TSubscriptionExpanded } from '@blocklet/payment-types';
|
|
@@ -222,6 +223,7 @@ function CustomerSubscriptionChangePayment({ subscription, customer, onComplete
|
|
|
222
223
|
setState({ stripePaying: false });
|
|
223
224
|
};
|
|
224
225
|
|
|
226
|
+
const currency = findCurrency(settings.paymentMethods, selectedCurrencyId) as TPaymentCurrency;
|
|
225
227
|
return (
|
|
226
228
|
<Stack direction="column" spacing={4} sx={{ maxWidth: '540px' }}>
|
|
227
229
|
<Stack
|
|
@@ -248,10 +250,10 @@ function CustomerSubscriptionChangePayment({ subscription, customer, onComplete
|
|
|
248
250
|
<SectionHeader title={t('payment.customer.changePayment.review')} />
|
|
249
251
|
<PaymentSummary
|
|
250
252
|
items={subscription.items as any[]}
|
|
251
|
-
currency={
|
|
253
|
+
currency={currency}
|
|
252
254
|
trialInDays={0}
|
|
253
255
|
billingThreshold={0}
|
|
254
|
-
showStaking={method
|
|
256
|
+
showStaking={showStaking(method, currency, !!subscription.billing_thresholds?.no_stake)}
|
|
255
257
|
/>
|
|
256
258
|
</Stack>
|
|
257
259
|
<Stack direction="column" spacing={2}>
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/* eslint-disable react/no-unstable-nested-components */
|
|
2
2
|
import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
|
|
3
3
|
import {
|
|
4
|
+
CreditTransactionsList,
|
|
4
5
|
CustomerInvoiceList,
|
|
5
6
|
TxLink,
|
|
6
7
|
api,
|
|
@@ -45,7 +46,7 @@ import SubscriptionActions, { ActionMethods } from '../../../components/subscrip
|
|
|
45
46
|
import { canChangePaymentMethod } from '../../../libs/util';
|
|
46
47
|
import { useSessionContext } from '../../../contexts/session';
|
|
47
48
|
import InfoMetric from '../../../components/info-metric';
|
|
48
|
-
import { useUnpaidInvoicesCheckForSubscription } from '../../../hooks/subscription';
|
|
49
|
+
import { useUnpaidInvoicesCheckForSubscription, usePendingAmountForSubscription } from '../../../hooks/subscription';
|
|
49
50
|
import { formatSmartDuration, TimeUnit } from '../../../libs/dayjs';
|
|
50
51
|
import InfoRowGroup from '../../../components/info-row-group';
|
|
51
52
|
|
|
@@ -74,6 +75,7 @@ export default function CustomerSubscriptionDetail() {
|
|
|
74
75
|
const { session } = useSessionContext();
|
|
75
76
|
const { loading, error, data, refresh } = useRequest(() => fetchData(id));
|
|
76
77
|
const { hasUnpaid, checkUnpaidInvoices } = useUnpaidInvoicesCheckForSubscription(id);
|
|
78
|
+
const { hasPendingAmount, pendingAmount } = usePendingAmountForSubscription(id, data?.paymentCurrency);
|
|
77
79
|
const overdraftProtectionReady =
|
|
78
80
|
['active', 'trialing', 'past_due'].includes(data?.status || '') && data?.paymentMethod?.type === 'arcblock';
|
|
79
81
|
const {
|
|
@@ -119,11 +121,13 @@ export default function CustomerSubscriptionDetail() {
|
|
|
119
121
|
return <CircularProgress />;
|
|
120
122
|
}
|
|
121
123
|
|
|
124
|
+
const isCredit = data?.paymentCurrency?.type === 'credit';
|
|
122
125
|
const showOverdraftProtection =
|
|
123
126
|
data?.paymentMethod?.type === 'arcblock' &&
|
|
124
127
|
!overdraftProtectionLoading &&
|
|
125
128
|
!!overdraftProtection &&
|
|
126
|
-
['active', 'trialing', 'past_due'].includes(data.status)
|
|
129
|
+
['active', 'trialing', 'past_due'].includes(data.status) &&
|
|
130
|
+
!isCredit;
|
|
127
131
|
|
|
128
132
|
const renderOverdraftProtectionLabel = () => {
|
|
129
133
|
const enabled = data?.overdraft_protection?.enabled;
|
|
@@ -303,6 +307,14 @@ export default function CustomerSubscriptionDetail() {
|
|
|
303
307
|
{t('customer.unpaidInvoicesWarningTip')}
|
|
304
308
|
</Alert>
|
|
305
309
|
)}
|
|
310
|
+
{hasPendingAmount && pendingAmount && (
|
|
311
|
+
<Alert severity="error" sx={{ mb: 2 }}>
|
|
312
|
+
{t('customer.pendingAmountWarningTip', {
|
|
313
|
+
amount: formatBNStr(pendingAmount, data?.paymentCurrency?.decimal),
|
|
314
|
+
symbol: data?.paymentCurrency?.symbol,
|
|
315
|
+
})}
|
|
316
|
+
</Alert>
|
|
317
|
+
)}
|
|
306
318
|
<Stack
|
|
307
319
|
className="page-header"
|
|
308
320
|
direction="row"
|
|
@@ -491,6 +503,17 @@ export default function CustomerSubscriptionDetail() {
|
|
|
491
503
|
value={renderOverdraftProtectionLabel()}
|
|
492
504
|
/>
|
|
493
505
|
)}
|
|
506
|
+
{isCredit && hasPendingAmount && (
|
|
507
|
+
<InfoMetric
|
|
508
|
+
label={t('admin.customer.creditGrants.pendingAmount')}
|
|
509
|
+
value={
|
|
510
|
+
<Typography
|
|
511
|
+
sx={{
|
|
512
|
+
color: 'error.main',
|
|
513
|
+
}}>{`${formatBNStr(pendingAmount, data?.paymentCurrency?.decimal)} ${data?.paymentCurrency?.symbol}`}</Typography>
|
|
514
|
+
}
|
|
515
|
+
/>
|
|
516
|
+
)}
|
|
494
517
|
</Stack>
|
|
495
518
|
</Box>
|
|
496
519
|
</Box>
|
|
@@ -642,19 +665,30 @@ export default function CustomerSubscriptionDetail() {
|
|
|
642
665
|
</Box>
|
|
643
666
|
</Box>
|
|
644
667
|
<Divider />
|
|
645
|
-
|
|
646
|
-
<
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
<
|
|
651
|
-
subscription_id={data.id}
|
|
652
|
-
|
|
653
|
-
include_staking
|
|
654
|
-
status="open,paid,uncollectible,void"
|
|
655
|
-
/>
|
|
668
|
+
{isCredit ? (
|
|
669
|
+
<Box className="section">
|
|
670
|
+
<Typography variant="h3" className="section-header">
|
|
671
|
+
{t('admin.creditTransactions.title')}
|
|
672
|
+
</Typography>
|
|
673
|
+
<Box className="section-body">
|
|
674
|
+
<CreditTransactionsList customer_id={data.customer_id} subscription_id={data.id} showAdminColumns={false} />
|
|
675
|
+
</Box>
|
|
656
676
|
</Box>
|
|
657
|
-
|
|
677
|
+
) : (
|
|
678
|
+
<Box className="section">
|
|
679
|
+
<Typography variant="h3" className="section-header">
|
|
680
|
+
{t('customer.invoiceHistory')}
|
|
681
|
+
</Typography>
|
|
682
|
+
<Box className="section-body">
|
|
683
|
+
<CustomerInvoiceList
|
|
684
|
+
subscription_id={data.id}
|
|
685
|
+
type="table"
|
|
686
|
+
include_staking
|
|
687
|
+
status="open,paid,uncollectible,void"
|
|
688
|
+
/>
|
|
689
|
+
</Box>
|
|
690
|
+
</Box>
|
|
691
|
+
)}
|
|
658
692
|
</Root>
|
|
659
693
|
);
|
|
660
694
|
}
|
|
@@ -60,7 +60,7 @@ const fetchSubscriptionData = (id: string, authToken: string): Promise<TSubscrip
|
|
|
60
60
|
throw new Error('Subscription ID is missing');
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
-
return api.get(`/api/subscriptions/${id}?authToken=${authToken}`).then((res) => res.data);
|
|
63
|
+
return api.get(`/api/subscriptions/${id}?authToken=${authToken}`).then((res: any) => res.data);
|
|
64
64
|
};
|
|
65
65
|
|
|
66
66
|
const checkHasPastDue = async (subscriptionId: string, authToken: string): Promise<boolean> => {
|