payment-kit 1.13.15

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.
Files changed (222) hide show
  1. package/.eslintrc.js +15 -0
  2. package/README.md +3 -0
  3. package/api/dev.ts +6 -0
  4. package/api/hooks/pre-start.js +12 -0
  5. package/api/src/hooks/pre-start.ts +21 -0
  6. package/api/src/index.ts +92 -0
  7. package/api/src/jobs/event.ts +72 -0
  8. package/api/src/jobs/invoice.ts +148 -0
  9. package/api/src/jobs/payment.ts +208 -0
  10. package/api/src/jobs/subscription.ts +301 -0
  11. package/api/src/jobs/webhook.ts +113 -0
  12. package/api/src/libs/audit.ts +73 -0
  13. package/api/src/libs/auth.ts +40 -0
  14. package/api/src/libs/chain/arcblock.ts +13 -0
  15. package/api/src/libs/dayjs.ts +17 -0
  16. package/api/src/libs/env.ts +5 -0
  17. package/api/src/libs/hooks.ts +42 -0
  18. package/api/src/libs/logger.ts +27 -0
  19. package/api/src/libs/middleware.ts +12 -0
  20. package/api/src/libs/payment.ts +53 -0
  21. package/api/src/libs/queue/index.ts +263 -0
  22. package/api/src/libs/queue/store.ts +47 -0
  23. package/api/src/libs/security.ts +95 -0
  24. package/api/src/libs/session.ts +164 -0
  25. package/api/src/libs/util.ts +93 -0
  26. package/api/src/locales/en.ts +3 -0
  27. package/api/src/locales/index.ts +37 -0
  28. package/api/src/locales/zh.ts +3 -0
  29. package/api/src/routes/checkout-sessions.ts +536 -0
  30. package/api/src/routes/connect/collect.ts +109 -0
  31. package/api/src/routes/connect/pay.ts +116 -0
  32. package/api/src/routes/connect/setup.ts +121 -0
  33. package/api/src/routes/connect/shared.ts +410 -0
  34. package/api/src/routes/connect/subscribe.ts +128 -0
  35. package/api/src/routes/customers.ts +70 -0
  36. package/api/src/routes/events.ts +76 -0
  37. package/api/src/routes/index.ts +59 -0
  38. package/api/src/routes/invoices.ts +126 -0
  39. package/api/src/routes/payment-currencies.ts +38 -0
  40. package/api/src/routes/payment-intents.ts +122 -0
  41. package/api/src/routes/payment-links.ts +221 -0
  42. package/api/src/routes/payment-methods.ts +39 -0
  43. package/api/src/routes/prices.ts +134 -0
  44. package/api/src/routes/products.ts +191 -0
  45. package/api/src/routes/settings.ts +33 -0
  46. package/api/src/routes/subscription-items.ts +148 -0
  47. package/api/src/routes/subscriptions.ts +254 -0
  48. package/api/src/routes/usage-records.ts +120 -0
  49. package/api/src/routes/webhook-attempts.ts +57 -0
  50. package/api/src/routes/webhook-endpoints.ts +105 -0
  51. package/api/src/store/migrate.ts +16 -0
  52. package/api/src/store/migrations/20230905-genesis.ts +52 -0
  53. package/api/src/store/migrations/20230911-seeding.ts +145 -0
  54. package/api/src/store/models/checkout-session.ts +395 -0
  55. package/api/src/store/models/coupon.ts +137 -0
  56. package/api/src/store/models/customer.ts +199 -0
  57. package/api/src/store/models/discount.ts +116 -0
  58. package/api/src/store/models/event.ts +111 -0
  59. package/api/src/store/models/index.ts +165 -0
  60. package/api/src/store/models/invoice-item.ts +185 -0
  61. package/api/src/store/models/invoice.ts +492 -0
  62. package/api/src/store/models/job.ts +75 -0
  63. package/api/src/store/models/payment-currency.ts +139 -0
  64. package/api/src/store/models/payment-intent.ts +282 -0
  65. package/api/src/store/models/payment-link.ts +219 -0
  66. package/api/src/store/models/payment-method.ts +169 -0
  67. package/api/src/store/models/price.ts +266 -0
  68. package/api/src/store/models/product.ts +162 -0
  69. package/api/src/store/models/promotion-code.ts +112 -0
  70. package/api/src/store/models/setup-intent.ts +206 -0
  71. package/api/src/store/models/subscription-item.ts +103 -0
  72. package/api/src/store/models/subscription-schedule.ts +157 -0
  73. package/api/src/store/models/subscription.ts +307 -0
  74. package/api/src/store/models/types.ts +406 -0
  75. package/api/src/store/models/usage-record.ts +132 -0
  76. package/api/src/store/models/webhook-attempt.ts +96 -0
  77. package/api/src/store/models/webhook-endpoint.ts +96 -0
  78. package/api/src/store/sequelize.ts +15 -0
  79. package/api/third.d.ts +28 -0
  80. package/blocklet.md +3 -0
  81. package/blocklet.yml +89 -0
  82. package/index.html +14 -0
  83. package/logo.png +0 -0
  84. package/package.json +133 -0
  85. package/public/.gitkeep +0 -0
  86. package/screenshots/.gitkeep +0 -0
  87. package/screenshots/1-subscription.png +0 -0
  88. package/screenshots/2-customer-1.png +0 -0
  89. package/screenshots/3-customer-2.png +0 -0
  90. package/screenshots/4-admin-3.png +0 -0
  91. package/screenshots/5-admin-4.png +0 -0
  92. package/scripts/build-clean.js +6 -0
  93. package/scripts/bump-version.mjs +35 -0
  94. package/src/app.tsx +68 -0
  95. package/src/components/actions.tsx +85 -0
  96. package/src/components/blockchain/tx.tsx +29 -0
  97. package/src/components/checkout/amount.tsx +24 -0
  98. package/src/components/checkout/error.tsx +30 -0
  99. package/src/components/checkout/footer.tsx +12 -0
  100. package/src/components/checkout/form/address.tsx +38 -0
  101. package/src/components/checkout/form/index.tsx +295 -0
  102. package/src/components/checkout/header.tsx +23 -0
  103. package/src/components/checkout/pay.tsx +222 -0
  104. package/src/components/checkout/product-card.tsx +56 -0
  105. package/src/components/checkout/product-item.tsx +37 -0
  106. package/src/components/checkout/skeleton/overview.tsx +21 -0
  107. package/src/components/checkout/skeleton/payment.tsx +35 -0
  108. package/src/components/checkout/success.tsx +183 -0
  109. package/src/components/checkout/summary.tsx +34 -0
  110. package/src/components/collapse.tsx +50 -0
  111. package/src/components/confirm.tsx +55 -0
  112. package/src/components/copyable.tsx +38 -0
  113. package/src/components/currency.tsx +15 -0
  114. package/src/components/customer/actions.tsx +73 -0
  115. package/src/components/data.tsx +20 -0
  116. package/src/components/drawer-form.tsx +77 -0
  117. package/src/components/error-fallback.tsx +7 -0
  118. package/src/components/error.tsx +39 -0
  119. package/src/components/event/list.tsx +217 -0
  120. package/src/components/info-card.tsx +40 -0
  121. package/src/components/info-metric.tsx +35 -0
  122. package/src/components/info-row.tsx +28 -0
  123. package/src/components/input.tsx +40 -0
  124. package/src/components/invoice/action.tsx +94 -0
  125. package/src/components/invoice/list.tsx +225 -0
  126. package/src/components/invoice/table.tsx +110 -0
  127. package/src/components/layout.tsx +70 -0
  128. package/src/components/livemode.tsx +23 -0
  129. package/src/components/metadata/editor.tsx +57 -0
  130. package/src/components/metadata/form.tsx +45 -0
  131. package/src/components/payment-intent/actions.tsx +81 -0
  132. package/src/components/payment-intent/list.tsx +204 -0
  133. package/src/components/payment-link/actions.tsx +114 -0
  134. package/src/components/payment-link/after-pay.tsx +87 -0
  135. package/src/components/payment-link/before-pay.tsx +175 -0
  136. package/src/components/payment-link/item.tsx +135 -0
  137. package/src/components/payment-link/product-select.tsx +66 -0
  138. package/src/components/payment-link/rename.tsx +64 -0
  139. package/src/components/portal/invoice/list.tsx +110 -0
  140. package/src/components/portal/subscription/cancel.tsx +83 -0
  141. package/src/components/portal/subscription/list.tsx +232 -0
  142. package/src/components/price/actions.tsx +21 -0
  143. package/src/components/price/form.tsx +292 -0
  144. package/src/components/product/actions.tsx +125 -0
  145. package/src/components/product/add-price.tsx +59 -0
  146. package/src/components/product/create.tsx +97 -0
  147. package/src/components/product/edit-price.tsx +75 -0
  148. package/src/components/product/edit.tsx +67 -0
  149. package/src/components/product/features.tsx +32 -0
  150. package/src/components/product/form.tsx +76 -0
  151. package/src/components/relative-time.tsx +41 -0
  152. package/src/components/section/header.tsx +29 -0
  153. package/src/components/status.tsx +12 -0
  154. package/src/components/subscription/actions/cancel.tsx +66 -0
  155. package/src/components/subscription/actions/index.tsx +172 -0
  156. package/src/components/subscription/actions/pause.tsx +83 -0
  157. package/src/components/subscription/items/actions.tsx +31 -0
  158. package/src/components/subscription/items/index.tsx +107 -0
  159. package/src/components/subscription/list.tsx +200 -0
  160. package/src/components/switch.tsx +48 -0
  161. package/src/components/table.tsx +66 -0
  162. package/src/components/uploader.tsx +81 -0
  163. package/src/components/webhook/attempts.tsx +149 -0
  164. package/src/contexts/products.tsx +42 -0
  165. package/src/contexts/session.ts +10 -0
  166. package/src/contexts/settings.tsx +54 -0
  167. package/src/env.d.ts +17 -0
  168. package/src/global.css +97 -0
  169. package/src/hooks/mobile.ts +15 -0
  170. package/src/index.tsx +6 -0
  171. package/src/libs/api.ts +19 -0
  172. package/src/libs/dayjs.ts +17 -0
  173. package/src/libs/util.ts +474 -0
  174. package/src/locales/en.tsx +395 -0
  175. package/src/locales/index.tsx +8 -0
  176. package/src/locales/zh.tsx +389 -0
  177. package/src/pages/admin/billing/index.tsx +56 -0
  178. package/src/pages/admin/billing/invoices/detail.tsx +215 -0
  179. package/src/pages/admin/billing/invoices/index.tsx +5 -0
  180. package/src/pages/admin/billing/subscriptions/detail.tsx +237 -0
  181. package/src/pages/admin/billing/subscriptions/index.tsx +5 -0
  182. package/src/pages/admin/customers/customers/detail.tsx +209 -0
  183. package/src/pages/admin/customers/customers/index.tsx +109 -0
  184. package/src/pages/admin/customers/index.tsx +47 -0
  185. package/src/pages/admin/developers/events/detail.tsx +77 -0
  186. package/src/pages/admin/developers/events/index.tsx +5 -0
  187. package/src/pages/admin/developers/index.tsx +60 -0
  188. package/src/pages/admin/developers/logs.tsx +3 -0
  189. package/src/pages/admin/developers/overview.tsx +3 -0
  190. package/src/pages/admin/developers/webhooks/detail.tsx +109 -0
  191. package/src/pages/admin/developers/webhooks/index.tsx +102 -0
  192. package/src/pages/admin/index.tsx +120 -0
  193. package/src/pages/admin/overview.tsx +3 -0
  194. package/src/pages/admin/payments/index.tsx +65 -0
  195. package/src/pages/admin/payments/intents/detail.tsx +205 -0
  196. package/src/pages/admin/payments/intents/index.tsx +5 -0
  197. package/src/pages/admin/payments/links/create.tsx +141 -0
  198. package/src/pages/admin/payments/links/detail.tsx +318 -0
  199. package/src/pages/admin/payments/links/index.tsx +167 -0
  200. package/src/pages/admin/products/coupons/index.tsx +3 -0
  201. package/src/pages/admin/products/index.tsx +81 -0
  202. package/src/pages/admin/products/prices/actions.tsx +151 -0
  203. package/src/pages/admin/products/prices/detail.tsx +203 -0
  204. package/src/pages/admin/products/prices/list.tsx +95 -0
  205. package/src/pages/admin/products/pricing-tables.tsx +3 -0
  206. package/src/pages/admin/products/products/create.tsx +105 -0
  207. package/src/pages/admin/products/products/detail.tsx +246 -0
  208. package/src/pages/admin/products/products/index.tsx +154 -0
  209. package/src/pages/admin/settings/branding.tsx +3 -0
  210. package/src/pages/admin/settings/business.tsx +3 -0
  211. package/src/pages/admin/settings/index.tsx +47 -0
  212. package/src/pages/admin/settings/payment-methods.tsx +80 -0
  213. package/src/pages/checkout/index.tsx +38 -0
  214. package/src/pages/checkout/pay.tsx +89 -0
  215. package/src/pages/customer/index.tsx +93 -0
  216. package/src/pages/customer/invoice.tsx +147 -0
  217. package/src/pages/home.tsx +9 -0
  218. package/tsconfig.api.json +9 -0
  219. package/tsconfig.eslint.json +7 -0
  220. package/tsconfig.json +99 -0
  221. package/tsconfig.types.json +11 -0
  222. package/vite.config.ts +19 -0
@@ -0,0 +1,77 @@
1
+ /* eslint-disable react/no-unstable-nested-components */
2
+ import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
3
+ import type { TEventExpanded } from '@did-pay/types';
4
+ import { ArrowBackOutlined } from '@mui/icons-material';
5
+ import { Alert, Box, CircularProgress, Stack, Typography } from '@mui/material';
6
+ import { styled } from '@mui/system';
7
+ import { useRequest } from 'ahooks';
8
+ import { Link } from 'react-router-dom';
9
+
10
+ import Copyable from '../../../../components/copyable';
11
+ import InfoMetric from '../../../../components/info-metric';
12
+ import SectionHeader from '../../../../components/section/header';
13
+ import WebhookAttempts from '../../../../components/webhook/attempts';
14
+ import api from '../../../../libs/api';
15
+ import { formatTime } from '../../../../libs/util';
16
+
17
+ const fetchData = (id: string): Promise<TEventExpanded> => {
18
+ return api.get(`/api/events/${id}`).then((res) => res.data);
19
+ };
20
+
21
+ export default function EventDetail(props: { id: string }) {
22
+ const { t } = useLocaleContext();
23
+
24
+ const { loading, error, data } = useRequest(() => fetchData(props.id));
25
+
26
+ if (error) {
27
+ return <Alert severity="error">{error.message}</Alert>;
28
+ }
29
+
30
+ if (loading || !data) {
31
+ return <CircularProgress />;
32
+ }
33
+
34
+ return (
35
+ <Root direction="column" spacing={4} sx={{ mb: 4 }}>
36
+ <Box>
37
+ <Stack className="page-header" direction="row" justifyContent="space-between" alignItems="center">
38
+ <Link to="/admin/developers/events">
39
+ <Stack direction="row" alignItems="center" sx={{ fontWeight: 'normal' }}>
40
+ <ArrowBackOutlined fontSize="small" sx={{ mr: 0.5, color: 'text.secondary' }} />
41
+ <Typography variant="h6" sx={{ color: 'text.secondary', fontWeight: 'normal' }}>
42
+ {t('admin.events')}
43
+ </Typography>
44
+ </Stack>
45
+ </Link>
46
+ <Copyable text={props.id} style={{ marginLeft: 4 }} />
47
+ </Stack>
48
+ <Box mt={2}>
49
+ <Stack direction="row" justifyContent="space-between" alignItems="center">
50
+ <Typography variant="h5" fontWeight={600}>
51
+ {data.type}
52
+ </Typography>
53
+ </Stack>
54
+ <Stack
55
+ className="section-body"
56
+ direction="row"
57
+ spacing={3}
58
+ justifyContent="flex-start"
59
+ flexWrap="wrap"
60
+ sx={{ pt: 2, mt: 2, borderTop: '1px solid #eee' }}>
61
+ <InfoMetric label={t('admin.event.pendingWebhooks')} value={data.pending_webhooks} divider />
62
+ <InfoMetric label={t('common.createdAt')} value={formatTime(data.created_at)} divider />
63
+ <InfoMetric label={t('common.updatedAt')} value={formatTime(data.updated_at)} />
64
+ </Stack>
65
+ </Box>
66
+ </Box>
67
+ <Box className="section">
68
+ <SectionHeader title={t('admin.event.webhooks')} />
69
+ <Box className="section-body">
70
+ <WebhookAttempts event_id={data.id} event={data} />
71
+ </Box>
72
+ </Box>
73
+ </Root>
74
+ );
75
+ }
76
+
77
+ const Root = styled(Stack)``;
@@ -0,0 +1,5 @@
1
+ import EventList from '../../../../components/event/list';
2
+
3
+ export default function EventsList() {
4
+ return <EventList features={{ toolbar: false }} />;
5
+ }
@@ -0,0 +1,60 @@
1
+ import Center from '@arcblock/ux/lib/Center';
2
+ import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
3
+ import Tabs from '@arcblock/ux/lib/Tabs';
4
+ import { CircularProgress, Typography } from '@mui/material';
5
+ import React, { Suspense, isValidElement } from 'react';
6
+ import { useNavigate, useParams } from 'react-router-dom';
7
+
8
+ const EventDetail = React.lazy(() => import('./events/detail'));
9
+ const WebhookDetail = React.lazy(() => import('./webhooks/detail'));
10
+
11
+ const pages = {
12
+ overview: React.lazy(() => import('./overview')),
13
+ webhooks: React.lazy(() => import('./webhooks')),
14
+ events: React.lazy(() => import('./events')),
15
+ logs: React.lazy(() => import('./logs')),
16
+ };
17
+
18
+ export default function DevelopersIndex() {
19
+ const navigate = useNavigate();
20
+ const { t } = useLocaleContext();
21
+ const { page = 'overview' } = useParams();
22
+
23
+ if (page.startsWith('evt_')) {
24
+ return <EventDetail id={page} />;
25
+ }
26
+
27
+ if (page.startsWith('we_')) {
28
+ return <WebhookDetail id={page} />;
29
+ }
30
+
31
+ const onTabChange = (newTab: string) => {
32
+ navigate(`/admin/developers/${newTab}`);
33
+ };
34
+
35
+ // @ts-ignore
36
+ const TabComponent = pages[page] || pages.overview;
37
+ const tabs = [
38
+ { label: t('admin.overview'), value: 'overview' },
39
+ { label: t('admin.webhooks'), value: 'webhooks' },
40
+ { label: t('admin.events'), value: 'events' },
41
+ { label: t('admin.logs'), value: 'logs' },
42
+ ];
43
+
44
+ return (
45
+ <div>
46
+ <Typography variant="h5" sx={{ mb: 1, fontWeight: 600 }}>
47
+ {t('admin.developers')}
48
+ </Typography>
49
+ <Tabs tabs={tabs} current={page} onChange={onTabChange} scrollButtons="auto" />
50
+ <Suspense
51
+ fallback={
52
+ <Center relative="parent">
53
+ <CircularProgress />
54
+ </Center>
55
+ }>
56
+ {isValidElement(TabComponent) ? TabComponent : <TabComponent />}
57
+ </Suspense>
58
+ </div>
59
+ );
60
+ }
@@ -0,0 +1,3 @@
1
+ export default function Logs() {
2
+ return <div>Logs</div>;
3
+ }
@@ -0,0 +1,3 @@
1
+ export default function Developers() {
2
+ return <div>Developers</div>;
3
+ }
@@ -0,0 +1,109 @@
1
+ /* eslint-disable react/no-unstable-nested-components */
2
+ import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
3
+ import type { TWebhookEndpointExpanded } from '@did-pay/types';
4
+ import { ArrowBackOutlined } from '@mui/icons-material';
5
+ import { Alert, Box, CircularProgress, Stack, Tooltip, Typography } from '@mui/material';
6
+ import { styled } from '@mui/system';
7
+ import { useRequest } from 'ahooks';
8
+ import { Link } from 'react-router-dom';
9
+
10
+ import Copyable from '../../../../components/copyable';
11
+ import InfoMetric from '../../../../components/info-metric';
12
+ import SectionHeader from '../../../../components/section/header';
13
+ import Status from '../../../../components/status';
14
+ import WebhookAttempts from '../../../../components/webhook/attempts';
15
+ import api from '../../../../libs/api';
16
+ import { formatTime, getWebhookStatusColor } from '../../../../libs/util';
17
+
18
+ const fetchData = (id: string): Promise<TWebhookEndpointExpanded> => {
19
+ return api.get(`/api/webhook-endpoints/${id}`).then((res) => res.data);
20
+ };
21
+
22
+ export default function WebhookDetail(props: { id: string }) {
23
+ const { t } = useLocaleContext();
24
+
25
+ const { loading, error, data } = useRequest(() => fetchData(props.id));
26
+
27
+ if (error) {
28
+ return <Alert severity="error">{error.message}</Alert>;
29
+ }
30
+
31
+ if (loading || !data) {
32
+ return <CircularProgress />;
33
+ }
34
+
35
+ return (
36
+ <Root direction="column" spacing={4} sx={{ mb: 4 }}>
37
+ <Box>
38
+ <Stack className="page-header" direction="row" justifyContent="space-between" alignItems="center">
39
+ <Link to="/admin/developers/webhooks">
40
+ <Stack direction="row" alignItems="center" sx={{ fontWeight: 'normal' }}>
41
+ <ArrowBackOutlined fontSize="small" sx={{ mr: 0.5, color: 'text.secondary' }} />
42
+ <Typography variant="h6" sx={{ color: 'text.secondary', fontWeight: 'normal' }}>
43
+ {t('admin.webhooks')}
44
+ </Typography>
45
+ </Stack>
46
+ </Link>
47
+ <Copyable text={props.id} style={{ marginLeft: 4 }} />
48
+ </Stack>
49
+ <Box mt={2}>
50
+ <Stack direction="row" justifyContent="space-between" alignItems="center">
51
+ <Stack direction="column" alignItems="flex-start">
52
+ <Typography variant="h5" fontWeight={600}>
53
+ {data.url}
54
+ </Typography>
55
+ <Typography variant="body1" color="text.secondary">
56
+ {data.description}
57
+ </Typography>
58
+ </Stack>
59
+ </Stack>
60
+ <Stack
61
+ className="section-body"
62
+ direction="row"
63
+ spacing={3}
64
+ justifyContent="flex-start"
65
+ flexWrap="wrap"
66
+ sx={{ pt: 2, mt: 2, borderTop: '1px solid #eee' }}>
67
+ <InfoMetric
68
+ label={t('common.status')}
69
+ value={<Status label={data.status} color={getWebhookStatusColor(data.status)} />}
70
+ divider
71
+ />
72
+ <InfoMetric
73
+ label={t('admin.webhookEndpoint.listen')}
74
+ value={
75
+ <Tooltip
76
+ arrow
77
+ title={
78
+ <ul style={{ padding: 0, margin: 0 }}>
79
+ {data.enabled_events.map((x) => (
80
+ <li key={x} style={{ fontSize: '0.9rem' }}>
81
+ {x}
82
+ </li>
83
+ ))}
84
+ </ul>
85
+ }>
86
+ <Typography sx={{ backgroundColor: '#eee', paddingX: 0.5, borderRadius: 0.5 }}>
87
+ {data.enabled_events.length} events
88
+ </Typography>
89
+ </Tooltip>
90
+ }
91
+ divider
92
+ />
93
+ <InfoMetric label={t('admin.webhookEndpoint.version')} value={data.api_version} divider />
94
+ <InfoMetric label={t('common.createdAt')} value={formatTime(data.created_at)} divider />
95
+ <InfoMetric label={t('common.updatedAt')} value={formatTime(data.updated_at)} />
96
+ </Stack>
97
+ </Box>
98
+ </Box>
99
+ <Box className="section">
100
+ <SectionHeader title={t('admin.webhookEndpoint.attempts')} mb={0} />
101
+ <Box className="section-body">
102
+ <WebhookAttempts webhook_endpoint_id={data.id} />
103
+ </Box>
104
+ </Box>
105
+ </Root>
106
+ );
107
+ }
108
+
109
+ const Root = styled(Stack)``;
@@ -0,0 +1,102 @@
1
+ /* eslint-disable react/no-unstable-nested-components */
2
+ import { getDurableData } from '@arcblock/ux/lib/Datatable';
3
+ import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
4
+ import type { TWebhookEndpoint } from '@did-pay/types';
5
+ import { Alert, CircularProgress, Typography } from '@mui/material';
6
+ import { useRequest } from 'ahooks';
7
+ import { useEffect, useState } from 'react';
8
+ import { useNavigate } from 'react-router-dom';
9
+
10
+ import Status from '../../../../components/status';
11
+ import Table from '../../../../components/table';
12
+ import api from '../../../../libs/api';
13
+ import { formatTime, getWebhookStatusColor } from '../../../../libs/util';
14
+
15
+ const fetchData = (params: Record<string, any> = {}): Promise<{ list: TWebhookEndpoint[]; count: number }> => {
16
+ const search = new URLSearchParams();
17
+ Object.keys(params).forEach((key) => {
18
+ search.set(key, String(params[key]));
19
+ });
20
+ return api.get(`/api/webhook-endpoints?${search.toString()}`).then((res) => res.data);
21
+ };
22
+
23
+ export default function WebhooksList() {
24
+ const listKey = 'webhooks';
25
+ const persisted = getDurableData(listKey);
26
+
27
+ const { t } = useLocaleContext();
28
+ const navigate = useNavigate();
29
+ const [search, setSearch] = useState<{ active: string; pageSize: number; page: number }>({
30
+ active: '',
31
+ pageSize: persisted.rowsPerPage || 20,
32
+ page: persisted.page ? persisted.page + 1 : 1,
33
+ });
34
+
35
+ const { loading, error, data, refresh } = useRequest(() => fetchData(search));
36
+ useEffect(() => {
37
+ refresh();
38
+ }, [search, refresh]);
39
+
40
+ if (error) {
41
+ return <Alert severity="error">{error.message}</Alert>;
42
+ }
43
+
44
+ if (loading || !data) {
45
+ return <CircularProgress />;
46
+ }
47
+
48
+ const columns = [
49
+ {
50
+ label: t('admin.webhookEndpoint.url.label'),
51
+ name: 'url',
52
+ },
53
+ {
54
+ label: t('common.status'),
55
+ name: 'status',
56
+ options: {
57
+ customBodyRenderLite: (_: string, index: number) => {
58
+ const item = data.list[index] as TWebhookEndpoint;
59
+ return <Status label={item?.status} color={getWebhookStatusColor(item?.status)} />;
60
+ },
61
+ },
62
+ },
63
+ {
64
+ label: t('common.updatedAt'),
65
+ name: 'updated_at',
66
+ options: {
67
+ customBodyRender: (e: string) => {
68
+ return formatTime(e);
69
+ },
70
+ },
71
+ },
72
+ ];
73
+
74
+ const onTableChange = ({ page, rowsPerPage }: any) => {
75
+ if (search.pageSize !== rowsPerPage) {
76
+ setSearch((x) => ({ ...x, pageSize: rowsPerPage, page: 1 }));
77
+ } else if (search.page !== page + 1) {
78
+ setSearch((x) => ({ ...x, page: page + 1 }));
79
+ }
80
+ };
81
+
82
+ return (
83
+ <Table
84
+ durable={listKey}
85
+ durableKeys={['page', 'rowsPerPage']}
86
+ data={data.list}
87
+ columns={columns}
88
+ options={{
89
+ count: data.count,
90
+ page: search.page - 1,
91
+ rowsPerPage: search.pageSize,
92
+ onRowClick: (_: any, { dataIndex }: any) => {
93
+ const item = data.list[dataIndex] as TWebhookEndpoint;
94
+ navigate(`/admin/developers/${item.id}`);
95
+ },
96
+ }}
97
+ loading={loading}
98
+ onChange={onTableChange}
99
+ title={<Typography variant="h6">{t('admin.webhookEndpoint.hosted')}</Typography>}
100
+ />
101
+ );
102
+ }
@@ -0,0 +1,120 @@
1
+ import Center from '@arcblock/ux/lib/Center';
2
+ import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
3
+ import { Box, Chip, CircularProgress, Stack } from '@mui/material';
4
+ import React, { Suspense, isValidElement } from 'react';
5
+ import { useNavigate, useParams } from 'react-router-dom';
6
+
7
+ import Layout from '../../components/layout';
8
+ import Switch from '../../components/switch';
9
+ import { SettingsProvider, useSettingsContext } from '../../contexts/settings';
10
+
11
+ const groups = {
12
+ overview: React.lazy(() => import('./overview')),
13
+ payments: React.lazy(() => import('./payments/index')),
14
+ customers: React.lazy(() => import('./customers/index')),
15
+ products: React.lazy(() => import('./products/index')),
16
+ billing: React.lazy(() => import('./billing/index')),
17
+ developers: React.lazy(() => import('./developers/index')),
18
+ settings: React.lazy(() => import('./settings/index')),
19
+ };
20
+
21
+ // eslint-disable-next-line @typescript-eslint/no-shadow
22
+ const renderNav = (group: string, onChange: Function, groups: { label: string; value: string }[]) =>
23
+ groups.map((x) => (
24
+ <Chip
25
+ key={x.value}
26
+ variant={group === x.value ? 'filled' : 'outlined'}
27
+ color={group === x.value ? 'primary' : 'default'}
28
+ size="small"
29
+ label={x.label}
30
+ onClick={() => onChange(x.value)}
31
+ sx={{
32
+ mr: 2,
33
+ fontSize: 14,
34
+ fontWeight: 600,
35
+ border: 'none',
36
+ cursor: 'pointer',
37
+ '&:hover': group === x.value ? {} : { backgroundColor: '#eee' },
38
+ }}
39
+ />
40
+ ));
41
+
42
+ function Admin() {
43
+ const navigate = useNavigate();
44
+ const { t } = useLocaleContext();
45
+ const settings = useSettingsContext();
46
+ const { group = 'overview' } = useParams();
47
+
48
+ const onLivemodeChange = (e: any) => {
49
+ settings.setLivemode(!e.target.checked);
50
+ settings.refresh();
51
+ };
52
+
53
+ const onTabChange = (newTab: string) => {
54
+ navigate(`/admin/${newTab}`);
55
+ };
56
+
57
+ // @ts-ignore
58
+ const TabComponent = groups[group] || groups.overview;
59
+
60
+ const group1 = [
61
+ { label: t('admin.overview'), value: 'overview' },
62
+ { label: t('admin.payments'), value: 'payments' },
63
+ { label: t('admin.customers'), value: 'customers' },
64
+ { label: t('admin.products'), value: 'products' },
65
+ { label: t('admin.billing'), value: 'billing' },
66
+ { label: t('admin.settings'), value: 'settings' },
67
+ ];
68
+
69
+ const group2 = [{ label: t('admin.developers'), value: 'developers' }];
70
+
71
+ return (
72
+ <Layout>
73
+ <Stack
74
+ direction="row"
75
+ alignItems="center"
76
+ justifyContent="space-between"
77
+ spacing={1}
78
+ sx={{ mt: 1, pb: 1, borderBottom: '1px solid #eee' }}>
79
+ <Box>{renderNav(group, onTabChange, group1)}</Box>
80
+ <Stack direction="row" alignItems="center">
81
+ {renderNav(group, onTabChange, group2)}{' '}
82
+ <label
83
+ htmlFor="livemode-switch"
84
+ style={{
85
+ fontSize: 14,
86
+ fontWeight: 600,
87
+ color: settings.livemode ? 'text.primary' : 'warning.light',
88
+ }}>
89
+ {t('common.livemode')}
90
+ <Switch
91
+ id="livemode-switch"
92
+ sx={{ ml: 1 }}
93
+ variant="warning"
94
+ checked={!settings.livemode}
95
+ onChange={onLivemodeChange}
96
+ />
97
+ </label>
98
+ </Stack>
99
+ </Stack>
100
+ <div className="page-content">
101
+ <Suspense
102
+ fallback={
103
+ <Center relative="parent">
104
+ <CircularProgress />
105
+ </Center>
106
+ }>
107
+ {isValidElement(TabComponent) ? TabComponent : <TabComponent />}
108
+ </Suspense>
109
+ </div>
110
+ </Layout>
111
+ );
112
+ }
113
+
114
+ export default function WrappedAdmin() {
115
+ return (
116
+ <SettingsProvider>
117
+ <Admin />
118
+ </SettingsProvider>
119
+ );
120
+ }
@@ -0,0 +1,3 @@
1
+ export default function Overview() {
2
+ return <div>Overview</div>;
3
+ }
@@ -0,0 +1,65 @@
1
+ import Center from '@arcblock/ux/lib/Center';
2
+ import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
3
+ import Tabs from '@arcblock/ux/lib/Tabs';
4
+ import { CircularProgress, Stack, Typography } from '@mui/material';
5
+ import React, { Suspense, isValidElement } from 'react';
6
+ import { useNavigate, useParams } from 'react-router-dom';
7
+
8
+ const PaymentLinkCreate = React.lazy(() => import('./links/create'));
9
+ const PaymentLinkDetail = React.lazy(() => import('./links/detail'));
10
+ const PaymentIntentDetail = React.lazy(() => import('./intents/detail'));
11
+
12
+ const pages = {
13
+ intents: React.lazy(() => import('./intents')),
14
+ links: React.lazy(() => import('./links')),
15
+ };
16
+
17
+ export default function PaymentIndex() {
18
+ const navigate = useNavigate();
19
+ const { t } = useLocaleContext();
20
+ const { page = 'intents' } = useParams();
21
+
22
+ if (page.startsWith('pi_')) {
23
+ return <PaymentIntentDetail id={page} />;
24
+ }
25
+
26
+ if (page.startsWith('plink_')) {
27
+ return <PaymentLinkDetail id={page} />;
28
+ }
29
+
30
+ const onTabChange = (newTab: string) => {
31
+ navigate(`/admin/payments/${newTab}`);
32
+ };
33
+
34
+ // @ts-ignore
35
+ const TabComponent = pages[page] || pages.intents;
36
+ const tabs = [
37
+ { label: t('admin.payments'), value: 'intents' },
38
+ { label: t('admin.paymentLinks'), value: 'links' },
39
+ ];
40
+
41
+ let extra = null;
42
+ if (page === 'links') {
43
+ extra = <PaymentLinkCreate />;
44
+ }
45
+
46
+ return (
47
+ <div>
48
+ <Stack direction="row" alignItems="center" justifyContent="space-between">
49
+ <Typography variant="h5" sx={{ mb: 1, fontWeight: 600 }}>
50
+ {t('admin.payments')}
51
+ </Typography>
52
+ {extra}
53
+ </Stack>
54
+ <Tabs tabs={tabs} current={page} onChange={onTabChange} scrollButtons="auto" />
55
+ <Suspense
56
+ fallback={
57
+ <Center relative="parent">
58
+ <CircularProgress />
59
+ </Center>
60
+ }>
61
+ {isValidElement(TabComponent) ? TabComponent : <TabComponent />}
62
+ </Suspense>
63
+ </div>
64
+ );
65
+ }