payment-kit 1.19.11 → 1.19.13

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 (48) hide show
  1. package/api/src/libs/notification/template/one-time-payment-refund-succeeded.ts +274 -0
  2. package/api/src/libs/notification/template/one-time-payment-succeeded.ts +10 -21
  3. package/api/src/libs/notification/template/subscription-refund-succeeded.ts +23 -3
  4. package/api/src/libs/notification/template/subscription-will-renew.ts +15 -2
  5. package/api/src/libs/session.ts +70 -1
  6. package/api/src/locales/en.ts +10 -0
  7. package/api/src/locales/zh.ts +10 -0
  8. package/api/src/queues/credit-consume.ts +4 -1
  9. package/api/src/queues/notification.ts +16 -3
  10. package/api/src/routes/checkout-sessions.ts +9 -0
  11. package/blocklet.yml +6 -6
  12. package/package.json +16 -16
  13. package/screenshots/0ffe164ebe4aa2eb43f8d87f87683f7f.png +0 -0
  14. package/screenshots/1ef9e15ac36d4af5bef34941000ba3af.png +0 -0
  15. package/screenshots/3a4cab81c52c29662db8794b05ccc7c7.png +0 -0
  16. package/screenshots/77ac49b79ae920f0f253ce8c694ffd65.png +0 -0
  17. package/screenshots/7ea8ef758865ecf6edb712d3534d2974.png +0 -0
  18. package/src/components/chart.tsx +1 -1
  19. package/src/components/conditional-section.tsx +11 -1
  20. package/src/components/customer/credit-overview.tsx +51 -48
  21. package/src/components/drawer-form.tsx +1 -1
  22. package/src/components/filter-toolbar.tsx +1 -1
  23. package/src/components/info-card.tsx +1 -1
  24. package/src/components/invoice-pdf/pdf.tsx +1 -1
  25. package/src/components/layout/admin.tsx +0 -4
  26. package/src/components/metadata/form.tsx +1 -1
  27. package/src/components/meter/actions.tsx +1 -1
  28. package/src/components/meter/events-list.tsx +6 -0
  29. package/src/components/meter/usage-guide.tsx +1 -1
  30. package/src/components/payment-link/item.tsx +1 -1
  31. package/src/components/price/form.tsx +0 -19
  32. package/src/components/pricing-table/product-item.tsx +1 -1
  33. package/src/components/product/features.tsx +1 -1
  34. package/src/components/uploader.tsx +1 -1
  35. package/src/components/webhook/attempts.tsx +1 -1
  36. package/src/locales/en.tsx +7 -4
  37. package/src/locales/zh.tsx +6 -2
  38. package/src/pages/admin/developers/webhooks/detail.tsx +1 -1
  39. package/src/pages/admin/products/pricing-tables/create.tsx +2 -2
  40. package/src/pages/checkout/pricing-table.tsx +1 -1
  41. package/src/pages/customer/index.tsx +30 -14
  42. package/src/pages/customer/invoice/detail.tsx +1 -1
  43. package/src/pages/integrations/donations/preview.tsx +4 -4
  44. package/screenshots/checkout.png +0 -0
  45. package/screenshots/customer.png +0 -0
  46. package/screenshots/payment.png +0 -0
  47. package/screenshots/setting.png +0 -0
  48. package/screenshots/subscription_detail.png +0 -0
@@ -21,6 +21,10 @@ import {
21
21
  SubscriptionRefundSucceededEmailTemplate,
22
22
  SubscriptionRefundSucceededEmailTemplateOptions,
23
23
  } from '../libs/notification/template/subscription-refund-succeeded';
24
+ import {
25
+ OneTimePaymentRefundSucceededEmailTemplate,
26
+ OneTimePaymentRefundSucceededEmailTemplateOptions,
27
+ } from '../libs/notification/template/one-time-payment-refund-succeeded';
24
28
  import {
25
29
  SubscriptionRenewFailedEmailTemplate,
26
30
  SubscriptionRenewFailedEmailTemplateOptions,
@@ -187,7 +191,7 @@ function calculateNextNotificationTime(preference: NotificationPreference): numb
187
191
  return Math.floor(nextTime.valueOf() / 1000);
188
192
  }
189
193
 
190
- function getNotificationTemplate(job: NotificationQueueJob): BaseEmailTemplate {
194
+ async function getNotificationTemplate(job: NotificationQueueJob): Promise<BaseEmailTemplate> {
191
195
  if (job.type === 'usage.report.empty') {
192
196
  return new UsageReportEmptyEmailTemplate(job.options as UsageReportEmptyEmailTemplateOptions);
193
197
  }
@@ -225,7 +229,16 @@ function getNotificationTemplate(job: NotificationQueueJob): BaseEmailTemplate {
225
229
  return new SubscriptionCanceledEmailTemplate(job.options as SubscriptionCanceledEmailTemplateOptions);
226
230
  }
227
231
  if (job.type === 'refund.succeeded') {
228
- return new SubscriptionRefundSucceededEmailTemplate(job.options as SubscriptionRefundSucceededEmailTemplateOptions);
232
+ const { refundId } = job.options as { refundId: string };
233
+ const refund = await Refund.findByPk(refundId);
234
+ if (refund?.subscription_id) {
235
+ return new SubscriptionRefundSucceededEmailTemplate(
236
+ job.options as SubscriptionRefundSucceededEmailTemplateOptions
237
+ );
238
+ }
239
+ return new OneTimePaymentRefundSucceededEmailTemplate(
240
+ job.options as OneTimePaymentRefundSucceededEmailTemplateOptions
241
+ );
229
242
  }
230
243
  if (job.type === 'customer.reward.succeeded') {
231
244
  return new CustomerRewardSucceededEmailTemplate(job.options as CustomerRewardSucceededEmailTemplateOptions);
@@ -264,7 +277,7 @@ function getNotificationTemplate(job: NotificationQueueJob): BaseEmailTemplate {
264
277
 
265
278
  async function handleNotificationJob(job: NotificationQueueJob): Promise<void> {
266
279
  try {
267
- const template = getNotificationTemplate(job);
280
+ const template = await getNotificationTemplate(job);
268
281
 
269
282
  await new Notification(template, job.type).send();
270
283
  logger.info('handleImmediateNotificationJob.success', { job });
@@ -41,6 +41,7 @@ import {
41
41
  getCheckoutSessionSubscriptionIds,
42
42
  getSubscriptionLineItems,
43
43
  isCreditMeteredLineItems,
44
+ validatePaymentAmounts,
44
45
  } from '../libs/session';
45
46
  import { getDaysUntilCancel, getDaysUntilDue, getSubscriptionTrialSetup } from '../libs/subscription';
46
47
  import {
@@ -1074,6 +1075,14 @@ router.put('/:id/submit', user, ensureCheckoutSessionOpen, async (req, res) => {
1074
1075
  true
1075
1076
  );
1076
1077
 
1078
+ // Validate payment amounts meet minimum requirements
1079
+ if (paymentMethod.type === 'stripe') {
1080
+ const result = validatePaymentAmounts(lineItems, paymentCurrency, checkoutSession);
1081
+ if (!result.valid) {
1082
+ return res.status(400).json({ error: result.error });
1083
+ }
1084
+ }
1085
+
1077
1086
  let customer = await Customer.findOne({ where: { did: req.user.did } });
1078
1087
  if (!customer) {
1079
1088
  const { user: userInfo } = await blocklet.getUser(req.user.did);
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.19.11
17
+ version: 1.19.13
18
18
  logo: logo.png
19
19
  files:
20
20
  - dist
@@ -72,11 +72,11 @@ capabilities:
72
72
  clusterMode: false
73
73
  component: true
74
74
  screenshots:
75
- - setting.png
76
- - payment.png
77
- - checkout.png
78
- - subscription_detail.png
79
- - customer.png
75
+ - 3a4cab81c52c29662db8794b05ccc7c7.png
76
+ - 77ac49b79ae920f0f253ce8c694ffd65.png
77
+ - 1ef9e15ac36d4af5bef34941000ba3af.png
78
+ - 7ea8ef758865ecf6edb712d3534d2974.png
79
+ - 0ffe164ebe4aa2eb43f8d87f87683f7f.png
80
80
  components:
81
81
  - name: image-bin
82
82
  source:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "payment-kit",
3
- "version": "1.19.11",
3
+ "version": "1.19.13",
4
4
  "scripts": {
5
5
  "dev": "blocklet dev --open",
6
6
  "lint": "tsc --noEmit && eslint src api/src --ext .mjs,.js,.jsx,.ts,.tsx",
@@ -44,31 +44,31 @@
44
44
  },
45
45
  "dependencies": {
46
46
  "@abtnode/cron": "^1.16.46",
47
- "@arcblock/did": "^1.21.0",
47
+ "@arcblock/did": "^1.21.1",
48
48
  "@arcblock/did-auth-storage-nedb": "^1.7.1",
49
- "@arcblock/did-connect": "^3.0.37",
50
- "@arcblock/did-util": "^1.21.0",
51
- "@arcblock/jwt": "^1.21.0",
52
- "@arcblock/ux": "^3.0.37",
53
- "@arcblock/validator": "^1.21.0",
49
+ "@arcblock/did-connect": "^3.0.41",
50
+ "@arcblock/did-util": "^1.21.1",
51
+ "@arcblock/jwt": "^1.21.1",
52
+ "@arcblock/ux": "^3.0.41",
53
+ "@arcblock/validator": "^1.21.1",
54
54
  "@blocklet/did-space-js": "^1.1.13",
55
55
  "@blocklet/error": "^0.2.5",
56
56
  "@blocklet/js-sdk": "^1.16.46",
57
57
  "@blocklet/logger": "^1.16.46",
58
- "@blocklet/payment-react": "1.19.11",
58
+ "@blocklet/payment-react": "1.19.13",
59
59
  "@blocklet/sdk": "^1.16.46",
60
- "@blocklet/ui-react": "^3.0.37",
60
+ "@blocklet/ui-react": "^3.0.41",
61
61
  "@blocklet/uploader": "^0.2.5",
62
62
  "@blocklet/xss": "^0.2.3",
63
63
  "@mui/icons-material": "^7.1.2",
64
64
  "@mui/lab": "7.0.0-beta.14",
65
65
  "@mui/material": "^7.1.2",
66
66
  "@mui/system": "^7.1.1",
67
- "@ocap/asset": "^1.21.0",
68
- "@ocap/client": "^1.21.0",
69
- "@ocap/mcrypto": "^1.21.0",
70
- "@ocap/util": "^1.21.0",
71
- "@ocap/wallet": "^1.21.0",
67
+ "@ocap/asset": "^1.21.1",
68
+ "@ocap/client": "^1.21.1",
69
+ "@ocap/mcrypto": "^1.21.1",
70
+ "@ocap/util": "^1.21.1",
71
+ "@ocap/wallet": "^1.21.1",
72
72
  "@stripe/react-stripe-js": "^2.9.0",
73
73
  "@stripe/stripe-js": "^2.4.0",
74
74
  "ahooks": "^3.8.5",
@@ -124,7 +124,7 @@
124
124
  "devDependencies": {
125
125
  "@abtnode/types": "^1.16.46",
126
126
  "@arcblock/eslint-config-ts": "^0.3.3",
127
- "@blocklet/payment-types": "1.19.11",
127
+ "@blocklet/payment-types": "1.19.13",
128
128
  "@types/cookie-parser": "^1.4.9",
129
129
  "@types/cors": "^2.8.19",
130
130
  "@types/debug": "^4.1.12",
@@ -170,5 +170,5 @@
170
170
  "parser": "typescript"
171
171
  }
172
172
  },
173
- "gitHead": "8742626f35fe37db1691744217cfa13547e91591"
173
+ "gitHead": "07c6878eb9d455f3898adfda7d07ca9e321e866c"
174
174
  }
@@ -63,7 +63,7 @@ export default function Chart({
63
63
  }}>
64
64
  <CartesianGrid strokeDasharray="3 3" stroke={theme.palette.grey[200]} />
65
65
  <Tooltip
66
- contentStyle={{ backgroundColor: theme.palette.background.paper, borderColor: theme.palette.grey[200] }}
66
+ contentStyle={{ backgroundColor: theme.palette.background.paper, borderColor: theme.palette.divider }}
67
67
  />
68
68
  {Object.keys(currencies).map((x: string, i) => {
69
69
  const dataKey = currencies[x]?.symbol as string;
@@ -15,6 +15,7 @@ interface ConditionalSectionProps {
15
15
  skeleton: boolean;
16
16
  children: ReactNode;
17
17
  skeletonComponent?: ReactNode;
18
+ onStateChange?: (state: 'hidden' | 'visible' | 'none') => void;
18
19
  }
19
20
 
20
21
  /**
@@ -29,10 +30,19 @@ interface ConditionalSectionProps {
29
30
  * 使用方式:
30
31
  * - 在任意深度的子组件中调用 useConditionalSection()?.hideRender()
31
32
  */
32
- export default function ConditionalSection({ skeleton, children, skeletonComponent = null }: ConditionalSectionProps) {
33
+ export default function ConditionalSection({
34
+ skeleton,
35
+ children,
36
+ skeletonComponent = null,
37
+ onStateChange = () => {},
38
+ }: ConditionalSectionProps) {
33
39
  const [renderState, setRenderState] = useState<'hidden' | 'visible' | 'none'>('hidden');
34
40
  const timerRef = useRef<NodeJS.Timeout | null>(null);
35
41
 
42
+ useEffect(() => {
43
+ onStateChange(renderState);
44
+ }, [renderState]);
45
+
36
46
  const handleHideRender = useCallback((hide: boolean = true) => {
37
47
  if (timerRef.current) {
38
48
  clearTimeout(timerRef.current);
@@ -99,6 +99,7 @@ export default function CreditOverview({ customerId, settings, mode = 'portal' }
99
99
  borderColor: 'divider',
100
100
  boxShadow: 1,
101
101
  borderRadius: 1,
102
+ backgroundColor: 'background.default',
102
103
  }}>
103
104
  <CardContent sx={{ flexGrow: 1 }}>
104
105
  <Stack spacing={2}>
@@ -190,60 +191,62 @@ export default function CreditOverview({ customerId, settings, mode = 'portal' }
190
191
  },
191
192
  }}>
192
193
  <Tab label={t('admin.creditGrants.overview')} value={CreditTab.OVERVIEW} />
193
- <Tab label={t('admin.creditGrants.title')} value={CreditTab.GRANTS} />
194
- <Tab label={t('admin.creditTransactions.title')} value={CreditTab.TRANSACTIONS} />
194
+ <Tab label={t('admin.creditGrants.tab')} value={CreditTab.GRANTS} />
195
+ <Tab label={t('admin.creditTransactions.tab')} value={CreditTab.TRANSACTIONS} />
195
196
  </Tabs>
196
- {/* 概览标签页 */}
197
- {creditTab === CreditTab.OVERVIEW && (
198
- <Box sx={{ width: '100%', mt: 1 }} key={creditTab}>
199
- <Box
200
- sx={{
201
- display: 'grid',
202
- gridTemplateColumns: {
203
- xs: 'repeat(1, 1fr)',
204
- md: 'repeat(2, 1fr)',
205
- },
206
- gap: 2,
207
- '@container (max-width: 600px)': {
208
- gridTemplateColumns: 'repeat(1, 1fr)',
209
- },
210
- }}>
211
- {filteredCreditCurrencies.map(renderCreditOverviewCard)}
212
- </Box>
213
-
214
- {filteredCreditCurrencies.length === 0 && (
197
+ <Box className="section-body">
198
+ {/* 概览标签页 */}
199
+ {creditTab === CreditTab.OVERVIEW && (
200
+ <Box sx={{ width: '100%', mt: 1 }} key={creditTab}>
215
201
  <Box
216
202
  sx={{
217
- display: 'flex',
218
- flexDirection: 'column',
219
- alignItems: 'center',
220
- justifyContent: 'center',
221
- py: 8,
222
- textAlign: 'center',
203
+ display: 'grid',
204
+ gridTemplateColumns: {
205
+ xs: 'repeat(1, 1fr)',
206
+ md: 'repeat(2, 1fr)',
207
+ },
208
+ gap: 2,
209
+ '@container (max-width: 600px)': {
210
+ gridTemplateColumns: 'repeat(1, 1fr)',
211
+ },
223
212
  }}>
224
- <Typography
225
- variant="h6"
226
- gutterBottom
227
- sx={{
228
- color: 'text.secondary',
229
- }}>
230
- {t('admin.customer.creditGrants.noGrants')}
231
- </Typography>
232
- <Typography
233
- variant="body2"
213
+ {filteredCreditCurrencies.map(renderCreditOverviewCard)}
214
+ </Box>
215
+
216
+ {filteredCreditCurrencies.length === 0 && (
217
+ <Box
234
218
  sx={{
235
- color: 'text.secondary',
219
+ display: 'flex',
220
+ flexDirection: 'column',
221
+ alignItems: 'center',
222
+ justifyContent: 'center',
223
+ py: 8,
224
+ textAlign: 'center',
236
225
  }}>
237
- {t('admin.customer.creditGrants.noGrantsDescription')}
238
- </Typography>
239
- </Box>
240
- )}
241
- </Box>
242
- )}
243
- {creditTab === CreditTab.GRANTS && <CreditGrantsList customer_id={customerId} mode={mode} key={creditTab} />}
244
- {creditTab === CreditTab.TRANSACTIONS && (
245
- <CreditTransactionsList customer_id={customerId} mode={mode} key={creditTab} />
246
- )}
226
+ <Typography
227
+ variant="h6"
228
+ gutterBottom
229
+ sx={{
230
+ color: 'text.secondary',
231
+ }}>
232
+ {t('admin.customer.creditGrants.noGrants')}
233
+ </Typography>
234
+ <Typography
235
+ variant="body2"
236
+ sx={{
237
+ color: 'text.secondary',
238
+ }}>
239
+ {t('admin.customer.creditGrants.noGrantsDescription')}
240
+ </Typography>
241
+ </Box>
242
+ )}
243
+ </Box>
244
+ )}
245
+ {creditTab === CreditTab.GRANTS && <CreditGrantsList customer_id={customerId} mode={mode} key={creditTab} />}
246
+ {creditTab === CreditTab.TRANSACTIONS && (
247
+ <CreditTransactionsList customer_id={customerId} mode={mode} key={creditTab} />
248
+ )}
249
+ </Box>
247
250
  </Stack>
248
251
  );
249
252
  }
@@ -72,7 +72,7 @@ export default function DrawerForm(rawProps: Props) {
72
72
  pt: 2,
73
73
  gap: 1,
74
74
  borderBottom: '1px solid',
75
- borderColor: 'grey.200',
75
+ borderColor: 'divider',
76
76
 
77
77
  '@media (max-width: 600px)': {
78
78
  '& > .addons-wrapper': {
@@ -522,7 +522,7 @@ const Root = styled(Box)`
522
522
  padding: 0;
523
523
  background: ${({ theme }) => theme.palette.background.paper};
524
524
  border: 1px solid;
525
- border-color: ${({ theme }) => theme.palette.grey[100]};
525
+ border-color: ${({ theme }) => theme.palette.divider};
526
526
  z-index: 999999;
527
527
  top: 15px;
528
528
  display: block;
@@ -100,7 +100,7 @@ export default function InfoCard(rawProps: Props) {
100
100
  color: 'background.paper',
101
101
  '&::before': {
102
102
  border: '1px solid',
103
- borderColor: 'grey.200',
103
+ borderColor: 'divider',
104
104
  backgroundColor: 'background.paper',
105
105
  },
106
106
  },
@@ -100,7 +100,7 @@ export function Download({ data }: { data: TInvoiceExpanded }) {
100
100
  size="small"
101
101
  onClick={generatePDF}
102
102
  disabled={!isReady || isGenerating}
103
- sx={{ borderColor: 'grey.100' }}>
103
+ sx={{ borderColor: 'divider' }}>
104
104
  {isGenerating ? (
105
105
  <Box sx={{ display: 'flex', alignItems: 'center', gap: '8px' }}>
106
106
  <CircularProgress size={16} sx={{ color: 'inherit' }} />
@@ -34,10 +34,6 @@ const Root = styled(Dashboard)<{ padding: string }>`
34
34
  margin-bottom: 8px;
35
35
  }
36
36
 
37
- .MuiTabs-scroller {
38
- border-bottom: 1px solid ${({ theme }) => theme.palette.divider};
39
- }
40
-
41
37
  .page-content {
42
38
  margin-top: 2px;
43
39
  }
@@ -241,7 +241,7 @@ export default function MetadataForm({
241
241
  onClick={() => metadata.remove(index)}
242
242
  sx={{
243
243
  border: '1px solid',
244
- borderColor: 'grey.100',
244
+ borderColor: 'divider',
245
245
  borderRadius: 1,
246
246
  padding: '8px',
247
247
  }}>
@@ -66,7 +66,7 @@ export default function MeterActions({ data, variant = 'compact', onChange }: Me
66
66
  if (variant === 'compact') {
67
67
  actions.push({
68
68
  label: t('admin.meter.view'),
69
- handler: () => navigate(`/admin/billing/meters/${data.id}`),
69
+ handler: () => navigate(`/admin/billing/${data.id}`),
70
70
  color: 'primary',
71
71
  });
72
72
  }
@@ -418,10 +418,16 @@ export default function MeterEventsList({ meterId, paymentCurrency }: MeterEvent
418
418
  variant="outlined"
419
419
  startIcon={<CalendarTodayOutlined />}
420
420
  onClick={onTogglePicker}
421
+ size="large"
421
422
  sx={{
422
423
  minWidth: isMobile ? '100%' : 200,
423
424
  justifyContent: isMobile ? 'flex-start' : 'center',
424
425
  color: 'text.secondary',
426
+ borderColor: 'grey.300',
427
+ '&:hover': {
428
+ borderColor: 'primary.main',
429
+ },
430
+ backgroundColor: 'grey.50',
425
431
  }}>
426
432
  {dayjs(state.startDate).format('YYYY-MM-DD')} - {dayjs(state.endDate).format('YYYY-MM-DD')}
427
433
  </Button>
@@ -160,7 +160,7 @@ print("Event recorded:", response.json())`;
160
160
  const CodeBlock = styled(Paper)(({ theme }) => ({
161
161
  padding: theme.spacing(2),
162
162
  backgroundColor: theme.palette.grey[50],
163
- border: `1px solid ${theme.palette.grey[200]}`,
163
+ border: `1px solid ${theme.palette.divider}`,
164
164
  borderRadius: theme.shape.borderRadius,
165
165
  overflow: 'auto',
166
166
  '& pre': {
@@ -54,7 +54,7 @@ export default function LineItem({ prefix, product, valid, onUpdate, onRemove }:
54
54
  p: 2,
55
55
  borderWidth: valid ? 1 : 2,
56
56
  borderStyle: 'solid',
57
- borderColor: valid ? 'grey.100' : 'error.main',
57
+ borderColor: valid ? 'divider' : 'error.main',
58
58
  borderRadius: 2,
59
59
  position: 'relative',
60
60
  }}>
@@ -15,7 +15,6 @@ import type {
15
15
  TMeter,
16
16
  TMeterExpanded,
17
17
  TPaymentCurrency,
18
- TPaymentCurrencyExpanded,
19
18
  TPaymentMethodExpanded,
20
19
  TPrice,
21
20
  TPriceExpanded,
@@ -102,16 +101,6 @@ const hasMoreCurrency = (methods: TPaymentMethodExpanded[] = []) => {
102
101
  return methods.every((method) => method.payment_currencies.length > 1) || methods.length > 1;
103
102
  };
104
103
 
105
- function stripeCurrencyValidate(v: number, currency: TPaymentCurrencyExpanded | null) {
106
- if (!currency) {
107
- return true;
108
- }
109
- if (currency.paymentMethod?.type === 'stripe') {
110
- return v >= 0.5;
111
- }
112
- return true;
113
- }
114
-
115
104
  const fetchMeters = (): Promise<{ list: TMeterExpanded[]; count: number }> => {
116
105
  return api.get('/api/meters?status=active&limit=100').then((res: any) => res.data);
117
106
  };
@@ -397,10 +386,6 @@ export default function PriceForm({ prefix = '', simple = false, productType = u
397
386
  required: t('admin.price.unit_amount.required'),
398
387
  validate: (v) => {
399
388
  const currency = findCurrency(settings.paymentMethods, defaultCurrencyId);
400
- const hasStripError = !stripeCurrencyValidate(v, currency);
401
- if (hasStripError) {
402
- return t('admin.price.unit_amount.stripeTip');
403
- }
404
389
  return validateAmount(v, currency ?? {});
405
390
  },
406
391
  }}
@@ -482,10 +467,6 @@ export default function PriceForm({ prefix = '', simple = false, productType = u
482
467
  rules={{
483
468
  required: t('admin.price.unit_amount.required'),
484
469
  validate: (v) => {
485
- const hasStripError = !stripeCurrencyValidate(v, currency);
486
- if (hasStripError) {
487
- return t('admin.price.unit_amount.stripeTip');
488
- }
489
470
  return validateAmount(v, currency ?? {});
490
471
  },
491
472
  }}
@@ -47,7 +47,7 @@ export default function ProductItem({ product, prices, valid, onUpdate, onRemove
47
47
  p: 2,
48
48
  borderWidth: valid ? 1 : 2,
49
49
  borderStyle: 'solid',
50
- borderColor: valid ? 'grey.100' : 'error.main',
50
+ borderColor: valid ? 'divider' : 'error.main',
51
51
  borderRadius: 2,
52
52
  position: 'relative',
53
53
  }}>
@@ -40,7 +40,7 @@ export default function MetadataForm() {
40
40
  onClick={() => features.remove(index)}
41
41
  sx={{
42
42
  border: '1px solid',
43
- borderColor: 'grey.100',
43
+ borderColor: 'divider',
44
44
  borderRadius: 1,
45
45
  padding: '8px',
46
46
  }}>
@@ -150,7 +150,7 @@ export default function Uploader({
150
150
 
151
151
  const Div = styled(Box)`
152
152
  border: 1px solid;
153
- border-color: ${({ theme }) => theme.palette.grey[100]};
153
+ border-color: ${({ theme }) => theme.palette.grey[300]};
154
154
  border-radius: 4px;
155
155
  cursor: pointer;
156
156
  width: 120px;
@@ -160,7 +160,7 @@ export default function WebhookAttempts({ event_id = '', webhook_endpoint_id = '
160
160
  md: 8,
161
161
  }}>
162
162
  {selected && (
163
- <Stack direction="column" spacing={2} sx={{ pt: 3, pl: 3, borderLeft: '1px solid', borderColor: 'grey.100' }}>
163
+ <Stack direction="column" spacing={2} sx={{ pt: 3, pl: 3, borderLeft: '1px solid', borderColor: 'divider' }}>
164
164
  <Typography variant="h6">{event_id ? selected.endpoint.url : selected.event.type}</Typography>
165
165
  <Box>
166
166
  <Typography variant="h6">Response ({selected.response_status})</Typography>
@@ -63,7 +63,8 @@ export default flat({
63
63
  scope: 'Scope',
64
64
  effectiveDate: 'Effective Date',
65
65
  expirationDate: 'Expiration Date',
66
- creditGrant: 'Credit Grant',
66
+ creditGrant: 'Grant',
67
+ date: 'Date',
67
68
  subscription: 'Subscription',
68
69
  meterEvent: 'Meter Event',
69
70
  creditAmount: 'Credit',
@@ -1144,11 +1145,12 @@ export default flat({
1144
1145
  appBalance: 'App Balance',
1145
1146
  },
1146
1147
  creditGrants: {
1147
- title: 'Credit Grants',
1148
+ tab: 'Grants',
1149
+ title: 'Credits',
1148
1150
  summary: 'Credit Summary',
1149
1151
  noGrants: 'No credit grants found',
1150
1152
  grantDetail: 'Credit Grant Details',
1151
- overview: 'Credit Overview',
1153
+ overview: 'Overview',
1152
1154
  overviewDescription: 'Monitor credit balances, usage, and outstanding debt across all currencies.',
1153
1155
  availableBalance: 'Available Balance',
1154
1156
  usage: 'Usage',
@@ -1180,7 +1182,8 @@ export default flat({
1180
1182
  viewDetails: 'View Details',
1181
1183
  },
1182
1184
  creditTransactions: {
1183
- title: 'Credit Transactions',
1185
+ tab: 'Transactions',
1186
+ title: 'Transactions',
1184
1187
  summary: 'Transaction Summary',
1185
1188
  noTransactions: 'No credit transactions found',
1186
1189
  totalTransactions: 'Total Transactions',
@@ -63,6 +63,7 @@ export default flat({
63
63
  effectiveDate: '生效时间',
64
64
  expirationDate: '过期时间',
65
65
  creditGrant: '信用额度',
66
+ date: '日期',
66
67
  subscription: '订阅',
67
68
  meterEvent: '计量事件',
68
69
  creditAmount: '额度',
@@ -927,11 +928,12 @@ export default flat({
927
928
  postal_code: '邮政编码',
928
929
  },
929
930
  creditGrants: {
931
+ tab: '信用额度',
930
932
  title: '信用额度',
931
933
  summary: '额度汇总',
932
934
  noGrants: '未找到信用额度',
933
935
  grantDetail: '信用额度详情',
934
- overview: '额度概览',
936
+ overview: '概览',
935
937
  overviewDescription: '监控所有货币的信用余额、使用情况和未偿债务。',
936
938
  availableBalance: '可用余额',
937
939
  usage: '使用情况',
@@ -1103,7 +1105,8 @@ export default flat({
1103
1105
  appBalance: '热钱包余额',
1104
1106
  },
1105
1107
  creditGrants: {
1106
- title: '信用额度',
1108
+ tab: '信用额度',
1109
+ title: '额度',
1107
1110
  summary: '额度汇总',
1108
1111
  noGrants: '未找到信用额度',
1109
1112
  grantDetail: '信用额度详情',
@@ -1139,6 +1142,7 @@ export default flat({
1139
1142
  viewDetails: '查看详情',
1140
1143
  },
1141
1144
  creditTransactions: {
1145
+ tab: '账单',
1142
1146
  title: '额度账单',
1143
1147
  summary: '账单汇总',
1144
1148
  noTransactions: '未找到额度账单',
@@ -90,7 +90,7 @@ export default function WebhookDetail(props: { id: string }) {
90
90
  pt: 2,
91
91
  mt: 2,
92
92
  borderTop: '1px solid',
93
- borderColor: 'grey.100',
93
+ borderColor: 'divider',
94
94
  }}>
95
95
  <InfoMetric
96
96
  label={t('common.status')}
@@ -132,7 +132,7 @@ export default function CreatePricingTable() {
132
132
  flex: 2,
133
133
  position: 'relative',
134
134
  borderRight: '1px solid',
135
- borderColor: 'grey.100',
135
+ borderColor: 'divider',
136
136
  }}>
137
137
  <Stack
138
138
  spacing={2}
@@ -159,7 +159,7 @@ export default function CreatePricingTable() {
159
159
  justifyContent: 'flex-end',
160
160
  position: 'absolute',
161
161
  borderTop: '1px solid',
162
- borderColor: 'grey.100',
162
+ borderColor: 'divider',
163
163
  left: 0,
164
164
  bottom: 0,
165
165
  }}>
@@ -26,7 +26,7 @@ export default function PricingTablePage({ id }: Props) {
26
26
  theme={undefined}
27
27
  hideNavMenu={undefined}
28
28
  maxWidth={false}
29
- sx={{ borderBottom: '1px solid', borderColor: 'grey.100' }}
29
+ sx={{ borderBottom: '1px solid', borderColor: 'divider' }}
30
30
  />
31
31
 
32
32
  <Stack