payment-kit 1.13.201 → 1.13.202

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/.eslintrc.js CHANGED
@@ -11,7 +11,7 @@ module.exports = {
11
11
  '@typescript-eslint/no-use-before-define': 'off',
12
12
  '@typescript-eslint/lines-between-class-members': 'off',
13
13
  'import/prefer-default-export': 'off',
14
- "react-hooks/exhaustive-deps": "off",
15
- 'jsx-a11y/no-noninteractive-element-interactions': "off"
14
+ 'react-hooks/exhaustive-deps': 'off',
15
+ 'jsx-a11y/no-noninteractive-element-interactions': 'off',
16
16
  },
17
17
  };
@@ -9,7 +9,7 @@ import { fromUnitToToken, toBN } from '@ocap/util';
9
9
  import { wallet } from '../../libs/auth';
10
10
  import { events } from '../../libs/event';
11
11
  import logger from '../../libs/logger';
12
- import { Customer, PaymentCurrency, PaymentMethod, Subscription } from '../../store/models';
12
+ import { Customer, GroupedBN, PaymentCurrency, PaymentMethod, Subscription } from '../../store/models';
13
13
 
14
14
  export async function ensureStakedForGas() {
15
15
  const currencies = await PaymentCurrency.findAll({ where: { active: true, is_base_currency: true } });
@@ -172,3 +172,30 @@ export async function checkStakeRevokeTx() {
172
172
  );
173
173
  }
174
174
  }
175
+
176
+ export async function getStakeSummaryByDid(did: string): Promise<GroupedBN> {
177
+ const methods = await PaymentMethod.findAll({
178
+ where: { type: 'arcblock' },
179
+ include: [{ model: PaymentCurrency, as: 'payment_currencies' }],
180
+ });
181
+ if (methods.length === 0) {
182
+ return {};
183
+ }
184
+
185
+ const address = toStakeAddress(did, wallet.address);
186
+ const results: GroupedBN = {};
187
+ await Promise.all(
188
+ methods.map(async (method: any) => {
189
+ const client = method.getOcapClient();
190
+ const { state } = await client.getStakeState({ address });
191
+ (state?.tokens || []).forEach((t: any) => {
192
+ const currency = method.payment_currencies.find((c: any) => t.address === c.contract);
193
+ if (currency) {
194
+ results[currency.id] = t.value;
195
+ }
196
+ });
197
+ })
198
+ );
199
+
200
+ return results;
201
+ }
@@ -58,7 +58,7 @@ export function getPriceCurrencyOptions(price: TPrice): PriceCurrency[] {
58
58
  }
59
59
 
60
60
  // FIXME: apply coupon for discounts
61
- export function getCheckoutAmount(items: TLineItemExpanded[], currencyId: string, includeFreeTrial = false) {
61
+ export function getCheckoutAmount(items: TLineItemExpanded[], currencyId: string, trialing = false) {
62
62
  let renew = new BN(0);
63
63
 
64
64
  const total = items
@@ -67,7 +67,7 @@ export function getCheckoutAmount(items: TLineItemExpanded[], currencyId: string
67
67
  if (price?.type === 'recurring') {
68
68
  renew = renew.add(new BN(getPriceUintAmountByCurrency(price, currencyId)).mul(new BN(x.quantity)));
69
69
 
70
- if (includeFreeTrial) {
70
+ if (trialing) {
71
71
  return acc;
72
72
  }
73
73
  if (price?.recurring?.usage_type === 'metered') {
@@ -227,7 +227,7 @@ export function getFastCheckoutAmount(
227
227
  items: TLineItemExpanded[],
228
228
  mode: string,
229
229
  currencyId: string,
230
- includeFreeTrial = false,
230
+ trialing = false,
231
231
  minimumCycle = 1
232
232
  ) {
233
233
  if (minimumCycle < 1) {
@@ -235,7 +235,7 @@ export function getFastCheckoutAmount(
235
235
  minimumCycle = 1;
236
236
  }
237
237
 
238
- const { total, renew } = getCheckoutAmount(items, currencyId, includeFreeTrial);
238
+ const { total, renew } = getCheckoutAmount(items, currencyId, trialing);
239
239
 
240
240
  if (mode === 'payment') {
241
241
  return total;
@@ -246,7 +246,7 @@ export function getFastCheckoutAmount(
246
246
  }
247
247
 
248
248
  if (mode === 'subscription') {
249
- return new BN(total).add(new BN(renew).mul(new BN(includeFreeTrial ? minimumCycle : minimumCycle - 1))).toString();
249
+ return new BN(total).add(new BN(renew).mul(new BN(trialing ? minimumCycle : minimumCycle - 1))).toString();
250
250
  }
251
251
 
252
252
  return '0';
@@ -192,9 +192,11 @@ export const formatCheckoutSession = async (payload: any, throwOnEmptyItems = tr
192
192
  };
193
193
 
194
194
  export async function getCheckoutSessionAmounts(checkoutSession: CheckoutSession) {
195
+ const now = dayjs().unix();
195
196
  const items = await Price.expand(checkoutSession.line_items);
196
- const includeTrial = !!checkoutSession.subscription_data?.trial_period_days;
197
- const amount = getCheckoutAmount(items, checkoutSession.currency_id, includeTrial);
197
+ const trialInDays = Number(checkoutSession.subscription_data?.trial_period_days || 0);
198
+ const trialEnds = Number(checkoutSession.subscription_data?.trial_end || 0);
199
+ const amount = getCheckoutAmount(items, checkoutSession.currency_id, trialInDays > 0 || trialEnds > now);
198
200
  return {
199
201
  amount_subtotal: amount.subtotal,
200
202
  amount_total: amount.total,
@@ -460,11 +462,12 @@ router.put('/:id/submit', user, ensureCheckoutSessionOpen, async (req, res) => {
460
462
  await checkoutSession.update({ currency_id: paymentCurrency.id });
461
463
 
462
464
  // always update payment amount in case currency has changed
465
+ const now = dayjs().unix();
463
466
  const lineItems = await Price.expand(checkoutSession.line_items, { product: true, upsell: true });
464
467
  const trialInDays = Number(checkoutSession.subscription_data?.trial_period_days || 0);
465
468
  const trialEnds = Number(checkoutSession.subscription_data?.trial_end || 0);
466
469
  const billingThreshold = Number(checkoutSession.subscription_data?.billing_threshold_amount || 0);
467
- const amount = getCheckoutAmount(lineItems, paymentCurrency.id, !!trialInDays);
470
+ const amount = getCheckoutAmount(lineItems, paymentCurrency.id, trialInDays > 0 || trialEnds > now);
468
471
  await checkoutSession.update({
469
472
  amount_subtotal: amount.subtotal,
470
473
  amount_total: amount.total,
@@ -726,7 +729,7 @@ router.put('/:id/submit', user, ensureCheckoutSessionOpen, async (req, res) => {
726
729
  lineItems,
727
730
  checkoutSession.mode,
728
731
  paymentCurrency.id,
729
- !!trialInDays
732
+ trialInDays > 0 || trialEnds > now
730
733
  );
731
734
  const paymentSettings = {
732
735
  payment_method_types: checkoutSession.payment_method_types,
@@ -29,9 +29,9 @@ export default {
29
29
 
30
30
  // @ts-ignore
31
31
  const items = subscription!.items as TLineItemExpanded[];
32
- const trialInDays = 0;
32
+ const trialing = false;
33
33
  const billingThreshold = Number(subscription.billing_thresholds?.amount_gte || 0);
34
- const fastCheckoutAmount = getFastCheckoutAmount(items, 'setup', paymentCurrency.id, !!trialInDays);
34
+ const fastCheckoutAmount = getFastCheckoutAmount(items, 'setup', paymentCurrency.id, trialing);
35
35
  const delegation = await isDelegationSufficientForPayment({
36
36
  paymentMethod,
37
37
  paymentCurrency,
@@ -51,7 +51,7 @@ export default {
51
51
  data: getTxMetadata({ subscriptionId: subscription.id }),
52
52
  paymentCurrency,
53
53
  paymentMethod,
54
- trialInDays,
54
+ trialing,
55
55
  billingThreshold,
56
56
  items,
57
57
  }),
@@ -31,9 +31,9 @@ export default {
31
31
 
32
32
  // @ts-ignore
33
33
  const items = subscription!.items as TLineItemExpanded[];
34
- const trialInDays = 0;
34
+ const trialing = false;
35
35
  const billingThreshold = Number(subscription!.billing_thresholds?.amount_gte || 0);
36
- const fastCheckoutAmount = getFastCheckoutAmount(items, 'subscription', paymentCurrency.id, !!trialInDays);
36
+ const fastCheckoutAmount = getFastCheckoutAmount(items, 'subscription', paymentCurrency.id, false);
37
37
  const delegation = await isDelegationSufficientForPayment({
38
38
  paymentMethod,
39
39
  paymentCurrency,
@@ -53,7 +53,7 @@ export default {
53
53
  data: getTxMetadata({ subscriptionId: subscription!.id }),
54
54
  paymentCurrency,
55
55
  paymentMethod,
56
- trialInDays,
56
+ trialing,
57
57
  billingThreshold,
58
58
  items,
59
59
  }),
@@ -58,7 +58,7 @@ export default {
58
58
  userPk,
59
59
  nonce: invoice.id,
60
60
  data: getTxMetadata({ subscriptionId: subscription!.id, invoiceId: invoice.id }),
61
- trialInDays: 0,
61
+ trialing: false,
62
62
  paymentCurrency,
63
63
  paymentMethod,
64
64
  items,
@@ -1,4 +1,5 @@
1
1
  import type { CallbackArgs } from '../../libs/auth';
2
+ import dayjs from '../../libs/dayjs';
2
3
  import logger from '../../libs/logger';
3
4
  import { isDelegationSufficientForPayment } from '../../libs/payment';
4
5
  import { getFastCheckoutAmount } from '../../libs/session';
@@ -37,10 +38,13 @@ export default {
37
38
  const claims: { [type: string]: [string, object] } = {};
38
39
 
39
40
  // if we can complete purchase without any wallet interaction
41
+ const now = dayjs().unix();
40
42
  const items = checkoutSession.line_items as TLineItemExpanded[];
41
43
  const trialInDays = Number(checkoutSession.subscription_data?.trial_period_days || 0);
44
+ const trialEnds = Number(checkoutSession.subscription_data?.trial_end || 0);
45
+ const trialing = trialInDays > 0 || trialEnds > now;
42
46
  const billingThreshold = Number(checkoutSession.subscription_data?.billing_threshold_amount || 0);
43
- const fastCheckoutAmount = getFastCheckoutAmount(items, checkoutSession.mode, paymentCurrency.id, !!trialInDays);
47
+ const fastCheckoutAmount = getFastCheckoutAmount(items, checkoutSession.mode, paymentCurrency.id, trialing);
44
48
  const delegation = await isDelegationSufficientForPayment({
45
49
  paymentMethod,
46
50
  paymentCurrency,
@@ -58,7 +62,7 @@ export default {
58
62
  data: getTxMetadata({ subscriptionId: subscription.id, checkoutSessionId }),
59
63
  paymentCurrency,
60
64
  paymentMethod,
61
- trialInDays,
65
+ trialing,
62
66
  billingThreshold,
63
67
  items,
64
68
  }),
@@ -573,7 +573,7 @@ export async function getDelegationTxClaim({
573
573
  items,
574
574
  paymentCurrency,
575
575
  paymentMethod,
576
- trialInDays = 0,
576
+ trialing = false,
577
577
  billingThreshold = 0,
578
578
  }: {
579
579
  userDid: string;
@@ -584,7 +584,7 @@ export async function getDelegationTxClaim({
584
584
  items: TLineItemExpanded[];
585
585
  paymentCurrency: PaymentCurrency;
586
586
  paymentMethod: PaymentMethod;
587
- trialInDays: number;
587
+ trialing: boolean;
588
588
  billingThreshold?: number;
589
589
  }) {
590
590
  const amount = getFastCheckoutAmount(items, mode, paymentCurrency.id);
@@ -593,7 +593,7 @@ export async function getDelegationTxClaim({
593
593
  const tokenRequirements = await getTokenRequirements({
594
594
  items,
595
595
  mode,
596
- trialInDays,
596
+ trialing,
597
597
  billingThreshold,
598
598
  paymentMethod,
599
599
  paymentCurrency,
@@ -702,7 +702,7 @@ export type TokenRequirementArgs = {
702
702
  mode: string;
703
703
  paymentMethod: PaymentMethod;
704
704
  paymentCurrency: PaymentCurrency;
705
- trialInDays: number;
705
+ trialing: boolean;
706
706
  billingThreshold: number;
707
707
  };
708
708
 
@@ -711,11 +711,11 @@ export async function getTokenRequirements({
711
711
  mode,
712
712
  paymentMethod,
713
713
  paymentCurrency,
714
- trialInDays = 0,
714
+ trialing = false,
715
715
  billingThreshold = 0,
716
716
  }: TokenRequirementArgs) {
717
717
  const tokenRequirements = [];
718
- let amount = getFastCheckoutAmount(items, mode, paymentCurrency.id, !!trialInDays);
718
+ let amount = getFastCheckoutAmount(items, mode, paymentCurrency.id, !!trialing);
719
719
 
720
720
  // If the app has not staked, we need to add the gas fee to the amount
721
721
  if ((await hasStakedForGas(paymentMethod)) === false) {
@@ -1,4 +1,5 @@
1
1
  import type { CallbackArgs } from '../../libs/auth';
2
+ import dayjs from '../../libs/dayjs';
2
3
  import logger from '../../libs/logger';
3
4
  import { isDelegationSufficientForPayment } from '../../libs/payment';
4
5
  import { getFastCheckoutAmount } from '../../libs/session';
@@ -37,10 +38,13 @@ export default {
37
38
  if (paymentMethod.type === 'arcblock') {
38
39
  const claims: { [type: string]: [string, object] } = {};
39
40
 
41
+ const now = dayjs().unix();
40
42
  const items = checkoutSession.line_items as TLineItemExpanded[];
41
43
  const trialInDays = Number(checkoutSession.subscription_data?.trial_period_days || 0);
44
+ const trialEnds = Number(checkoutSession.subscription_data?.trial_end || 0);
45
+ const trialing = trialInDays > 0 || trialEnds > now;
42
46
  const billingThreshold = Number(checkoutSession.subscription_data?.billing_threshold_amount || 0);
43
- const fastCheckoutAmount = getFastCheckoutAmount(items, checkoutSession.mode, paymentCurrency.id, !!trialInDays);
47
+ const fastCheckoutAmount = getFastCheckoutAmount(items, checkoutSession.mode, paymentCurrency.id, trialing);
44
48
  const delegation = await isDelegationSufficientForPayment({
45
49
  paymentMethod,
46
50
  paymentCurrency,
@@ -60,7 +64,7 @@ export default {
60
64
  data: getTxMetadata({ subscriptionId: subscription.id, checkoutSessionId }),
61
65
  paymentCurrency,
62
66
  paymentMethod,
63
- trialInDays,
67
+ trialing,
64
68
  billingThreshold,
65
69
  items,
66
70
  }),
@@ -3,6 +3,7 @@ import { Router } from 'express';
3
3
  import Joi from 'joi';
4
4
  import pick from 'lodash/pick';
5
5
 
6
+ import { getStakeSummaryByDid } from '../integrations/blockchain/stake';
6
7
  import { getWhereFromKvQuery, getWhereFromQuery } from '../libs/api';
7
8
  import { authenticate } from '../libs/security';
8
9
  import { formatMetadata } from '../libs/util';
@@ -110,8 +111,8 @@ router.get('/me', user(), async (req, res) => {
110
111
  if (!doc) {
111
112
  res.json(null);
112
113
  } else {
113
- const summary = await doc.getSummary();
114
- res.json({ ...doc.toJSON(), summary });
114
+ const [summary, stake] = await Promise.all([doc.getSummary(), getStakeSummaryByDid(doc.did)]);
115
+ res.json({ ...doc.toJSON(), summary: { ...summary, stake } });
115
116
  }
116
117
  } catch (err) {
117
118
  console.error(err);
@@ -141,8 +142,8 @@ router.get('/:id/summary', auth, async (req, res) => {
141
142
  return;
142
143
  }
143
144
 
144
- const result = await doc.getSummary();
145
- res.json(result);
145
+ const [summary, stake] = await Promise.all([doc.getSummary(), getStakeSummaryByDid(doc.did)]);
146
+ res.json({ ...summary, stake });
146
147
  } catch (err) {
147
148
  console.error(err);
148
149
  res.json(null);
package/blocklet.yml CHANGED
@@ -14,7 +14,7 @@ repository:
14
14
  type: git
15
15
  url: git+https://github.com/blocklet/payment-kit.git
16
16
  specVersion: 1.2.8
17
- version: 1.13.201
17
+ version: 1.13.202
18
18
  logo: logo.png
19
19
  files:
20
20
  - dist
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "payment-kit",
3
- "version": "1.13.201",
3
+ "version": "1.13.202",
4
4
  "scripts": {
5
5
  "dev": "cross-env COMPONENT_STORE_URL=https://test.store.blocklet.dev blocklet dev --open",
6
6
  "eject": "vite eject",
@@ -45,15 +45,15 @@
45
45
  "@abtnode/cron": "1.16.24",
46
46
  "@arcblock/did": "^1.18.113",
47
47
  "@arcblock/did-auth-storage-nedb": "^1.7.1",
48
- "@arcblock/did-connect": "^2.9.57",
48
+ "@arcblock/did-connect": "^2.9.63",
49
49
  "@arcblock/did-util": "^1.18.113",
50
50
  "@arcblock/jwt": "^1.18.113",
51
- "@arcblock/ux": "^2.9.57",
51
+ "@arcblock/ux": "^2.9.63",
52
52
  "@blocklet/logger": "1.16.24",
53
- "@blocklet/payment-react": "1.13.201",
53
+ "@blocklet/payment-react": "1.13.202",
54
54
  "@blocklet/sdk": "1.16.24",
55
- "@blocklet/ui-react": "^2.9.57",
56
- "@blocklet/uploader": "^0.0.74",
55
+ "@blocklet/ui-react": "^2.9.63",
56
+ "@blocklet/uploader": "^0.0.75",
57
57
  "@mui/icons-material": "^5.15.14",
58
58
  "@mui/lab": "^5.0.0-alpha.169",
59
59
  "@mui/material": "^5.15.14",
@@ -109,8 +109,8 @@
109
109
  },
110
110
  "devDependencies": {
111
111
  "@abtnode/types": "1.16.24",
112
- "@arcblock/eslint-config-ts": "^0.2.4",
113
- "@blocklet/payment-types": "1.13.201",
112
+ "@arcblock/eslint-config-ts": "^0.3.0",
113
+ "@blocklet/payment-types": "1.13.202",
114
114
  "@types/cookie-parser": "^1.4.6",
115
115
  "@types/cors": "^2.8.17",
116
116
  "@types/dotenv-flow": "^3.3.3",
@@ -134,7 +134,7 @@
134
134
  "type-fest": "^4.10.2",
135
135
  "typescript": "^4.9.5",
136
136
  "vite": "^4.5.2",
137
- "vite-plugin-blocklet": "^0.7.4",
137
+ "vite-plugin-blocklet": "^0.7.5",
138
138
  "vite-plugin-node-polyfills": "^0.7.0",
139
139
  "vite-plugin-svgr": "^2.4.0",
140
140
  "zx": "^7.2.3"
@@ -149,5 +149,5 @@
149
149
  "parser": "typescript"
150
150
  }
151
151
  },
152
- "gitHead": "0f62c0c55a77845cece0656cc6801847b9dc9e24"
152
+ "gitHead": "e9c7a403fee7506f92118581d659c91f210714a7"
153
153
  }
@@ -398,6 +398,7 @@ export default flat({
398
398
  refund: 'Refund Amount',
399
399
  dispute: 'Dispute Amount',
400
400
  due: 'Due Amount',
401
+ stake: 'Stake Amount',
401
402
  name: 'Name',
402
403
  email: 'Email',
403
404
  phone: 'Phone',
@@ -387,6 +387,7 @@ export default flat({
387
387
  edit: '编辑信息',
388
388
  spent: '花费金额',
389
389
  refund: '退款金额',
390
+ stake: '质押金额',
390
391
  dispute: '争议金额',
391
392
  due: '欠款金额',
392
393
  name: '名称',
@@ -166,12 +166,13 @@ export default function CustomerDetail(props: { id: string }) {
166
166
  <InfoMetric label={t('common.createdAt')} value={formatTime(data.customer.created_at)} divider />
167
167
  <InfoMetric label={t('common.updatedAt')} value={formatTime(data.customer.updated_at)} divider />
168
168
  <InfoMetric label={t('admin.customer.spent')} value={<BalanceList data={data.summary.paid} />} divider />
169
+ <InfoMetric label={t('admin.customer.stake')} value={<BalanceList data={data.summary.stake} />} divider />
170
+ <InfoMetric label={t('admin.customer.due')} value={<BalanceList data={data.summary.due} />} divider />
169
171
  <InfoMetric
170
172
  label={t('admin.customer.refund')}
171
173
  value={<BalanceList data={data.summary.refunded} />}
172
174
  divider
173
175
  />
174
- <InfoMetric label={t('admin.customer.due')} value={<BalanceList data={data.summary.due} />} divider />
175
176
  {tokenBalances.map((x) => (
176
177
  <InfoMetric
177
178
  key={x.currency}
@@ -197,7 +198,7 @@ export default function CustomerDetail(props: { id: string }) {
197
198
  </Button>
198
199
  </SectionHeader>
199
200
  <Stack>
200
- <InfoRow label={t('common.did')} value={<DidAddress did={data.customer.did} />} />
201
+ <InfoRow label={t('common.did')} value={<DidAddress did={data.customer.did} showQrcode />} />
201
202
  <InfoRow label={t('admin.customer.name')} value={data.customer.name} />
202
203
  <InfoRow label={t('admin.customer.phone')} value={data.customer.phone} />
203
204
  <InfoRow label={t('admin.customer.email')} value={data.customer.email} />
@@ -6,14 +6,11 @@ import { useNavigate, useParams } from 'react-router-dom';
6
6
 
7
7
  import { useTransitionContext } from '../../../components/progress-bar';
8
8
 
9
- const PaymentLinkCreate = React.lazy(() => import('./links/create'));
10
- const PaymentLinkDetail = React.lazy(() => import('./links/detail'));
11
9
  const PaymentIntentDetail = React.lazy(() => import('./intents/detail'));
12
10
  const RefundDetail = React.lazy(() => import('./refunds/detail'));
13
11
 
14
12
  const pages = {
15
13
  intents: React.lazy(() => import('./intents')),
16
- links: React.lazy(() => import('./links')),
17
14
  refunds: React.lazy(() => import('./refunds')),
18
15
  };
19
16
 
@@ -31,10 +28,6 @@ export default function PaymentIndex() {
31
28
  return <RefundDetail id={page} />;
32
29
  }
33
30
 
34
- if (page.startsWith('plink_')) {
35
- return <PaymentLinkDetail id={page} />;
36
- }
37
-
38
31
  const onTabChange = (newTab: string) => {
39
32
  startTransition(() => {
40
33
  navigate(`/admin/payments/${newTab}`);
@@ -46,21 +39,14 @@ export default function PaymentIndex() {
46
39
  const tabs = [
47
40
  { label: t('admin.paymentIntent.list'), value: 'intents' },
48
41
  { label: t('admin.refunds'), value: 'refunds' },
49
- { label: t('admin.paymentLinks'), value: 'links' },
50
42
  ];
51
43
 
52
- let extra = null;
53
- if (page === 'links') {
54
- extra = <PaymentLinkCreate />;
55
- }
56
-
57
44
  return (
58
45
  <div>
59
46
  <Stack direction="row" alignItems="center" justifyContent="space-between">
60
47
  <Typography variant="h5" sx={{ mb: 1, fontWeight: 600 }}>
61
48
  {t('admin.payments')}
62
49
  </Typography>
63
- {extra}
64
50
  </Stack>
65
51
  <Tabs tabs={tabs} current={page} onChange={onTabChange} scrollButtons="auto" />
66
52
  {isValidElement(TabComponent) ? TabComponent : <TabComponent />}
@@ -1,3 +1,5 @@
1
+ import { Alert } from '@mui/material';
2
+
1
3
  export default function Coupons() {
2
- return <div>Coupons List</div>;
4
+ return <Alert severity="info">This feature is planned, but not implemented yet.</Alert>;
3
5
  }
@@ -9,14 +9,17 @@ import { useTransitionContext } from '../../../components/progress-bar';
9
9
  const ProductCreate = React.lazy(() => import('./products/create'));
10
10
  const ProductDetail = React.lazy(() => import('./products/detail'));
11
11
  const PriceDetail = React.lazy(() => import('./prices/detail'));
12
+ const PaymentLinkCreate = React.lazy(() => import('./links/create'));
13
+ const PaymentLinkDetail = React.lazy(() => import('./links/detail'));
12
14
  const PricingTableCreate = React.lazy(() => import('./pricing-tables/create'));
13
15
  const PricingTableDetail = React.lazy(() => import('./pricing-tables/detail'));
14
16
 
15
17
  const pages = {
16
18
  products: React.lazy(() => import('./products')),
17
- coupons: React.lazy(() => import('./coupons')),
19
+ links: React.lazy(() => import('./links')),
18
20
  'pricing-tables': React.lazy(() => import('./pricing-tables')),
19
21
  passports: React.lazy(() => import('./passports')),
22
+ // coupons: React.lazy(() => import('./coupons')),
20
23
  };
21
24
 
22
25
  export default function Products() {
@@ -37,18 +40,25 @@ export default function Products() {
37
40
  return <PricingTableDetail id={page} />;
38
41
  }
39
42
 
43
+ if (page.startsWith('plink_')) {
44
+ return <PaymentLinkDetail id={page} />;
45
+ }
46
+
40
47
  // @ts-ignore
41
48
  const TabComponent = pages[page] || pages.products;
42
49
  const tabs = [
43
50
  { label: t('admin.products'), value: 'products' },
44
- { label: t('admin.coupons'), value: 'coupons' },
51
+ { label: t('admin.paymentLinks'), value: 'links' },
45
52
  { label: t('admin.pricingTables'), value: 'pricing-tables' },
46
53
  { label: t('admin.passports'), value: 'passports' },
54
+ // { label: t('admin.coupons'), value: 'coupons' },
47
55
  ];
48
56
 
49
57
  let extra = null;
50
58
  if (page === 'products') {
51
59
  extra = <ProductCreate />;
60
+ } else if (page === 'links') {
61
+ extra = <PaymentLinkCreate />;
52
62
  } else if (page === 'pricing-tables') {
53
63
  extra = <PricingTableCreate />;
54
64
  }
@@ -87,7 +87,7 @@ export default function PaymentLinkDetail(props: { id: string }) {
87
87
  const onUpdateMetadata = createUpdater('metadata');
88
88
  const onChange = (action: string) => {
89
89
  if (action === 'remove') {
90
- navigate('/admin/payments/links');
90
+ navigate('/admin/products/links');
91
91
  } else {
92
92
  runAsync();
93
93
  }
@@ -99,7 +99,7 @@ export default function PaymentLinkDetail(props: { id: string }) {
99
99
  <Grid container spacing={4} sx={{ mb: 4 }}>
100
100
  <Grid item md={12}>
101
101
  <Stack className="page-header" direction="row" justifyContent="space-between" alignItems="center">
102
- <Link to="/admin/payments/links">
102
+ <Link to="/admin/products/links">
103
103
  <Stack direction="row" alignItems="center" sx={{ fontWeight: 'normal' }}>
104
104
  <ArrowBackOutlined fontSize="small" sx={{ mr: 0.5, color: 'text.secondary' }} />
105
105
  <Typography variant="h6" sx={{ color: 'text.secondary', fontWeight: 'normal' }}>
@@ -151,7 +151,7 @@ function PaymentLinks() {
151
151
  onRowClick: (_: any, { dataIndex }: any) => {
152
152
  const item = data.list[dataIndex] as TPaymentLinkExpanded;
153
153
  startTransition(() => {
154
- navigate(`/admin/payments/${item.id}`);
154
+ navigate(`/admin/products/${item.id}`);
155
155
  });
156
156
  },
157
157
  }}
@@ -88,7 +88,7 @@ export default function PriceActions({ data, onChange, variant, setAsDefault }:
88
88
  };
89
89
 
90
90
  const onCreatePaymentLink = () => {
91
- navigate(`/admin/payments/links?price_id=${data.id}`);
91
+ navigate(`/admin/products/links?price_id=${data.id}`);
92
92
  };
93
93
 
94
94
  const onCreatePricingTable = () => {
@@ -1,4 +1,4 @@
1
- import DID from '@arcblock/ux/lib/Address';
1
+ import DID from '@arcblock/ux/lib/DID';
2
2
  import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
3
3
  import Toast from '@arcblock/ux/lib/Toast';
4
4
  import { CustomerInvoiceList, PaymentProvider, formatError, getPrefix } from '@blocklet/payment-react';
@@ -134,7 +134,7 @@ export default function CustomerHome() {
134
134
  </Button>
135
135
  </SectionHeader>
136
136
  <Stack>
137
- <InfoRow sizes={[1, 2]} label={t('common.did')} value={<DID copyable>{data.did}</DID>} />
137
+ <InfoRow sizes={[1, 2]} label={t('common.did')} value={<DID did={data.did} copyable showQrcode />} />
138
138
  <InfoRow sizes={[1, 2]} label={t('admin.customer.name')} value={data.name} />
139
139
  <InfoRow sizes={[1, 2]} label={t('admin.customer.phone')} value={data.phone} />
140
140
  <InfoRow sizes={[1, 2]} label={t('admin.customer.email')} value={data.email} />
@@ -178,6 +178,7 @@ export default function CustomerHome() {
178
178
  justifyContent="flex-start"
179
179
  sx={{ width: '100%' }}>
180
180
  <InfoMetric label={t('admin.customer.spent')} value={<BalanceList data={data.summary.paid} />} />
181
+ <InfoMetric label={t('admin.customer.stake')} value={<BalanceList data={data.summary.stake} />} />
181
182
  <InfoMetric label={t('admin.customer.due')} value={<BalanceList data={data.summary.due} />} />
182
183
  <InfoMetric label={t('admin.customer.refund')} value={<BalanceList data={data.summary.refunded} />} />
183
184
  </Stack>