payment-kit 1.20.8 → 1.20.10

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 (36) hide show
  1. package/api/src/crons/index.ts +1 -1
  2. package/api/src/index.ts +2 -2
  3. package/api/src/integrations/stripe/handlers/index.ts +10 -2
  4. package/api/src/libs/{vendor → vendor-util}/adapters/factory.ts +9 -5
  5. package/api/src/libs/{vendor → vendor-util}/adapters/launcher-adapter.ts +20 -18
  6. package/api/src/libs/{vendor → vendor-util}/adapters/types.ts +1 -4
  7. package/api/src/libs/{vendor → vendor-util}/fulfillment.ts +24 -127
  8. package/api/src/queues/payment.ts +1 -5
  9. package/api/src/queues/{vendor → vendors}/commission.ts +19 -18
  10. package/api/src/queues/{vendor → vendors}/fulfillment-coordinator.ts +35 -6
  11. package/api/src/queues/{vendor → vendors}/fulfillment.ts +2 -2
  12. package/api/src/queues/{vendor → vendors}/status-check.ts +13 -8
  13. package/api/src/routes/payment-links.ts +2 -1
  14. package/api/src/routes/products.ts +1 -0
  15. package/api/src/routes/vendor.ts +157 -216
  16. package/api/src/store/migrations/20250911-add-vendor-type.ts +26 -0
  17. package/api/src/store/migrations/20250916-add-vendor-did.ts +20 -0
  18. package/api/src/store/models/payout.ts +2 -2
  19. package/api/src/store/models/product-vendor.ts +11 -24
  20. package/api/src/store/models/product.ts +2 -0
  21. package/blocklet.yml +1 -1
  22. package/doc/vendor_fulfillment_system.md +1 -1
  23. package/package.json +5 -5
  24. package/src/components/metadata/form.tsx +12 -19
  25. package/src/components/payment-link/before-pay.tsx +40 -0
  26. package/src/components/product/vendor-config.tsx +4 -11
  27. package/src/components/subscription/description.tsx +1 -6
  28. package/src/components/subscription/portal/list.tsx +82 -6
  29. package/src/components/subscription/vendor-service-list.tsx +128 -0
  30. package/src/components/vendor/actions.tsx +1 -33
  31. package/src/locales/en.tsx +16 -3
  32. package/src/locales/zh.tsx +18 -5
  33. package/src/pages/admin/products/links/create.tsx +2 -0
  34. package/src/pages/admin/products/vendors/create.tsx +140 -190
  35. package/src/pages/admin/products/vendors/index.tsx +14 -22
  36. package/src/pages/customer/subscription/detail.tsx +26 -11
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.20.8
17
+ version: 1.20.10
18
18
  logo: logo.png
19
19
  files:
20
20
  - dist
@@ -790,7 +790,7 @@ async function requestReturnFromSingleVendor(
790
790
  ): Promise<void> {
791
791
  try {
792
792
  // 1. 获取供应商适配器
793
- const vendorAdapter = VendorFulfillmentService.getVendorAdapter(vendor.vendor_key);
793
+ const vendorAdapter = await VendorFulfillmentService.getVendorAdapter(vendor.vendor_key);
794
794
 
795
795
  // 2. 调用供应商的退货请求方法
796
796
  const returnResult = await vendorAdapter.requestReturn({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "payment-kit",
3
- "version": "1.20.8",
3
+ "version": "1.20.10",
4
4
  "scripts": {
5
5
  "dev": "blocklet dev --open",
6
6
  "lint": "tsc --noEmit && eslint src api/src --ext .mjs,.js,.jsx,.ts,.tsx",
@@ -56,8 +56,8 @@
56
56
  "@blocklet/error": "^0.2.5",
57
57
  "@blocklet/js-sdk": "^1.16.52-beta-20250912-112002-e3499e9c",
58
58
  "@blocklet/logger": "^1.16.52-beta-20250912-112002-e3499e9c",
59
- "@blocklet/payment-react": "1.20.8",
60
- "@blocklet/payment-vendor": "1.20.8",
59
+ "@blocklet/payment-react": "1.20.10",
60
+ "@blocklet/payment-vendor": "1.20.10",
61
61
  "@blocklet/sdk": "^1.16.52-beta-20250912-112002-e3499e9c",
62
62
  "@blocklet/ui-react": "^3.1.40",
63
63
  "@blocklet/uploader": "^0.2.10",
@@ -126,7 +126,7 @@
126
126
  "devDependencies": {
127
127
  "@abtnode/types": "^1.16.52-beta-20250912-112002-e3499e9c",
128
128
  "@arcblock/eslint-config-ts": "^0.3.3",
129
- "@blocklet/payment-types": "1.20.8",
129
+ "@blocklet/payment-types": "1.20.10",
130
130
  "@types/cookie-parser": "^1.4.9",
131
131
  "@types/cors": "^2.8.19",
132
132
  "@types/debug": "^4.1.12",
@@ -173,5 +173,5 @@
173
173
  "parser": "typescript"
174
174
  }
175
175
  },
176
- "gitHead": "804fbe22b7f12af2fc58134679161be52a464582"
176
+ "gitHead": "1659d63120ced92167ec8681e51db96801b910ec"
177
177
  }
@@ -151,6 +151,7 @@ export default function MetadataForm({
151
151
  color="primary"
152
152
  sx={{
153
153
  color: color === 'inherit' ? 'text.secondary' : 'primary.main',
154
+ minWidth: 'max-content',
154
155
  }}
155
156
  onClick={() => handleModeChange(mode === 'form' ? 'json' : 'form')}
156
157
  startIcon={<Autorenew />}>
@@ -162,7 +163,7 @@ export default function MetadataForm({
162
163
  <Box sx={{ width: '100%', height: '100%', display: 'flex', flexDirection: 'column' }}>
163
164
  {title ? (
164
165
  <Stack sx={{ flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center' }}>
165
- <FormLabel sx={{ color: 'text.primary', mt: 0 }}>{title}</FormLabel>
166
+ <FormLabel sx={{ color: 'text.primary', my: 0, '& label': { mb: 0 } }}>{title}</FormLabel>
166
167
  {toggleBtn}
167
168
  </Stack>
168
169
  ) : (
@@ -189,15 +190,17 @@ export default function MetadataForm({
189
190
  ref={index === metadata.fields.length - 1 ? lastItemRef : null}
190
191
  sx={{
191
192
  mt: 2,
193
+ '&:first-child': { mt: 0 },
192
194
  spacing: 2,
193
-
194
195
  alignItems: 'flex-end',
195
196
  }}
196
197
  spacing={2}
197
198
  direction="row">
198
- <Stack direction="row" spacing={2} sx={{ flex: 1 }}>
199
+ <Stack
200
+ direction="row"
201
+ spacing={2}
202
+ sx={{ flex: 1, '& > div:first-child': { flex: 1 }, '& > div:last-child': { flex: 2 } }}>
199
203
  <FormInput
200
- sx={{ flex: 1 }}
201
204
  errorPosition="right"
202
205
  size="small"
203
206
  name={`metadata.${index}.key`}
@@ -208,19 +211,17 @@ export default function MetadataForm({
208
211
  message: t('common.maxLength', { len: 40 }),
209
212
  },
210
213
  }}
214
+ label="Key *"
211
215
  placeholder="Key"
212
- label="Key"
213
216
  // @ts-ignore
214
217
  ref={errors?.metadata?.[index]?.key ? errorRef : null}
215
- inputProps={{
216
- maxLength: 40,
217
- }}
218
+ inputProps={{ maxLength: 40 }}
218
219
  />
219
220
  <FormInput
220
- sx={{ flex: 2 }}
221
221
  size="small"
222
222
  errorPosition="right"
223
223
  name={`metadata.${index}.value`}
224
+ label="Value *"
224
225
  placeholder="Value"
225
226
  rules={{
226
227
  required: t('payment.checkout.required'),
@@ -229,12 +230,9 @@ export default function MetadataForm({
229
230
  message: t('common.maxLength', { len: 256 }),
230
231
  },
231
232
  }}
232
- label="Value"
233
233
  // @ts-ignore
234
234
  ref={errors?.metadata?.[index]?.value ? errorRef : null}
235
- inputProps={{
236
- maxLength: 256,
237
- }}
235
+ inputProps={{ maxLength: 256 }}
238
236
  />
239
237
  </Stack>
240
238
  <IconButton
@@ -322,12 +320,7 @@ export default function MetadataForm({
322
320
  }}
323
321
  />
324
322
  <Divider />
325
- <Stack
326
- direction="row"
327
- sx={{
328
- mt: 2,
329
- justifyContent: 'flex-end',
330
- }}>
323
+ <Stack direction="row" sx={{ mt: 2, justifyContent: 'flex-end' }}>
331
324
  {actions}
332
325
  </Stack>
333
326
  </>
@@ -239,6 +239,46 @@ export default function BeforePay({
239
239
  />
240
240
  )}
241
241
  />
242
+ <Controller
243
+ name="subscription_data.no_stake"
244
+ control={control}
245
+ render={({ field }) => (
246
+ <FormControlLabel
247
+ control={
248
+ <Checkbox
249
+ checked={getValues().subscription_data?.no_stake || false}
250
+ {...field}
251
+ onChange={(_, checked) => setValue(field.name, checked)}
252
+ />
253
+ }
254
+ label={t('admin.paymentLink.noStakeRequired')}
255
+ />
256
+ )}
257
+ />
258
+ <Controller
259
+ name="metadata"
260
+ control={control}
261
+ render={({ field }) => {
262
+ const metadata = field.value || {};
263
+ const isChecked = metadata.show_product_features === 'true';
264
+
265
+ return (
266
+ <FormControlLabel
267
+ control={
268
+ <Checkbox
269
+ checked={isChecked}
270
+ onChange={(_, checked) => {
271
+ const newMetadata = { ...metadata };
272
+ newMetadata.show_product_features = checked ? 'true' : 'false';
273
+ field.onChange(newMetadata);
274
+ }}
275
+ />
276
+ }
277
+ label={t('admin.paymentLink.showProductFeatures')}
278
+ />
279
+ );
280
+ }}
281
+ />
242
282
  {includeFreeTrial && (
243
283
  <Controller
244
284
  name="subscription_data.trial_period_days"
@@ -10,8 +10,7 @@ interface Vendor {
10
10
  name: string;
11
11
  description: string;
12
12
  vendor_key: string;
13
- default_commission_rate: number;
14
- default_commission_type: 'percentage' | 'fixed_amount';
13
+ vendor_type: string;
15
14
  }
16
15
 
17
16
  export default function VendorConfig() {
@@ -120,15 +119,9 @@ export default function VendorConfig() {
120
119
  const vendorId = e.target.value as string;
121
120
  field.onChange(vendorId);
122
121
 
123
- const selectedVendor = vendors.find((v) => v.id === vendorId);
124
- if (selectedVendor) {
125
- setValue(`vendor_config.${index}.commission_rate`, selectedVendor.default_commission_rate);
126
- const commissionType =
127
- selectedVendor.default_commission_type === 'fixed_amount'
128
- ? 'fixed'
129
- : selectedVendor.default_commission_type;
130
- setValue(`vendor_config.${index}.commission_type`, commissionType);
131
- }
122
+ // Set default values when selecting a vendor
123
+ setValue(`vendor_config.${index}.commission_rate`, 20);
124
+ setValue(`vendor_config.${index}.commission_type`, 'percentage');
132
125
  }}
133
126
  renderValue={(value: string) => {
134
127
  const vendor = vendors.find((v) => v.id === value);
@@ -19,12 +19,7 @@ export default function SubscriptionDescription({
19
19
  const { isMobile } = useMobile();
20
20
  if (subscription.description) {
21
21
  return (
22
- <Stack
23
- direction="row"
24
- spacing={1}
25
- sx={{
26
- alignItems: 'center',
27
- }}>
22
+ <Stack direction="row" spacing={1} sx={{ alignItems: 'center' }}>
28
23
  <Typography variant={variant} className="subscription-description">
29
24
  <TruncatedText text={subscription.description} maxLength={maxLength} useWidth />
30
25
  </Typography>
@@ -38,6 +38,8 @@ const fetchData = (params: Record<string, any> = {}): Promise<SubscriptionListRe
38
38
  return api.get(`/api/subscriptions?${search.toString()}`).then((res) => res.data);
39
39
  };
40
40
 
41
+ const TAG_COUNT = 2;
42
+
41
43
  type Props = {
42
44
  id: string;
43
45
  status: string;
@@ -146,6 +148,7 @@ export default function CurrentSubscriptions({
146
148
  flexDirection: 'column',
147
149
  justifyContent: 'space-between',
148
150
  gap: 2,
151
+ width: '100%',
149
152
 
150
153
  '&:hover': {
151
154
  backgroundColor: 'grey.50',
@@ -160,6 +163,7 @@ export default function CurrentSubscriptions({
160
163
  sx={[
161
164
  {
162
165
  flex: 1,
166
+ width: '100%',
163
167
  },
164
168
  ...(Array.isArray(rest.sx) ? rest.sx : [rest.sx]),
165
169
  ]}>
@@ -170,12 +174,14 @@ export default function CurrentSubscriptions({
170
174
  sx={{
171
175
  alignItems: 'flex-start',
172
176
  justifyContent: 'space-between',
177
+ width: '100%',
173
178
  }}>
174
179
  <Stack
175
180
  direction="row"
176
181
  spacing={1.5}
177
182
  sx={{
178
183
  alignItems: 'center',
184
+ width: '100%',
179
185
  }}>
180
186
  {subscription.metadata?.appLogo ? (
181
187
  <Avatar
@@ -212,18 +218,88 @@ export default function CurrentSubscriptions({
212
218
  </AvatarGroup>
213
219
  )}
214
220
 
215
- <Stack direction="column" spacing={0.25}>
221
+ <Stack direction="column" spacing={0.25} width="100%">
216
222
  <SubscriptionDescription
217
223
  subscription={subscription}
218
224
  hideSubscription
219
225
  maxLength={isMobile ? 30 : 40}
220
226
  variant={isMobile ? 'subtitle2' : 'subtitle1'}
221
227
  />
222
- <SubscriptionStatus
223
- subscription={subscription}
224
- sx={{ height: 18, width: 'fit-content' }}
225
- size="small"
226
- />
228
+ <Box
229
+ sx={{
230
+ display: 'flex',
231
+ alignItems: 'center',
232
+ gap: 1,
233
+ justifyContent: 'space-between',
234
+ }}>
235
+ <SubscriptionStatus
236
+ subscription={subscription}
237
+ sx={{ height: 18, width: 'fit-content' }}
238
+ size="small"
239
+ />
240
+ {/* Vendor 信息显示 */}
241
+ {(() => {
242
+ const vendorConfig = subscription.items?.[0]?.price?.product?.vendor_config;
243
+ if (!vendorConfig || vendorConfig.length === 0) return null;
244
+
245
+ return (
246
+ <Box
247
+ sx={{
248
+ display: 'flex',
249
+ alignItems: 'center',
250
+ gap: 0.5,
251
+ flexWrap: 'wrap',
252
+ }}>
253
+ {vendorConfig.slice(0, TAG_COUNT).map((vendor, index) => (
254
+ <Box
255
+ key={vendor.vendor_id || index}
256
+ sx={{
257
+ display: 'flex',
258
+ alignItems: 'center',
259
+ gap: 0.25,
260
+ px: 0.5,
261
+ py: 0.25,
262
+ bgcolor: 'action.hover',
263
+ borderRadius: 0.5,
264
+ border: '1px solid',
265
+ borderColor: 'divider',
266
+ }}>
267
+ <Box
268
+ sx={{
269
+ width: 4,
270
+ height: 4,
271
+ borderRadius: '50%',
272
+ bgcolor: 'success.main',
273
+ }}
274
+ />
275
+ <Typography
276
+ variant="caption"
277
+ sx={{
278
+ color: 'text.secondary',
279
+ fontSize: '0.7rem',
280
+ fontWeight: 500,
281
+ whiteSpace: 'nowrap',
282
+ }}>
283
+ {vendor.name || vendor.vendor_key}
284
+ </Typography>
285
+ </Box>
286
+ ))}
287
+ {vendorConfig.length > TAG_COUNT && (
288
+ <Typography
289
+ variant="caption"
290
+ sx={{
291
+ color: 'text.secondary',
292
+ fontSize: '0.7rem',
293
+ fontStyle: 'italic',
294
+ px: 0.5,
295
+ }}>
296
+ +{vendorConfig.length - TAG_COUNT} more
297
+ </Typography>
298
+ )}
299
+ </Box>
300
+ );
301
+ })()}
302
+ </Box>
227
303
  </Stack>
228
304
  </Stack>
229
305
  </Stack>
@@ -0,0 +1,128 @@
1
+ import { Box, Stack, Typography, IconButton, Tooltip } from '@mui/material';
2
+ import { Home, Dashboard } from '@mui/icons-material';
3
+ import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
4
+ import { joinURL } from 'ufo';
5
+
6
+ interface VendorConfig {
7
+ vendor_id: string;
8
+ vendor_key: string;
9
+ name?: string;
10
+ vendor_type?: string;
11
+ }
12
+
13
+ interface VendorServiceListProps {
14
+ vendorServices: VendorConfig[];
15
+ subscriptionId: string;
16
+ }
17
+
18
+ export default function VendorServiceList({ vendorServices, subscriptionId }: VendorServiceListProps) {
19
+ const { t } = useLocaleContext();
20
+
21
+ if (!vendorServices || vendorServices.length === 0) {
22
+ return null;
23
+ }
24
+
25
+ const prefix = window.blocklet.prefix || '/';
26
+
27
+ return (
28
+ <>
29
+ <Typography variant="h3" className="section-header" sx={{ mb: 3 }}>
30
+ {t('admin.subscription.includedServices')} ({vendorServices.length})
31
+ </Typography>
32
+ <Box className="section-body">
33
+ <Stack
34
+ spacing={2}
35
+ sx={{
36
+ display: 'grid',
37
+ gridTemplateColumns: { xs: '1fr', md: '1fr 1fr', lg: '1fr 1fr 1fr' },
38
+ gap: 2,
39
+ }}>
40
+ {vendorServices.map((vendor, index) => {
41
+ const isLauncher = vendor.vendor_type === 'launcher';
42
+
43
+ return (
44
+ <Box
45
+ key={vendor.vendor_key || index}
46
+ sx={{
47
+ p: 2,
48
+ border: '1px solid',
49
+ borderColor: 'divider',
50
+ borderRadius: 2,
51
+ backgroundColor: 'background.paper',
52
+ '&:hover': {
53
+ backgroundColor: 'action.hover',
54
+ },
55
+ transition: 'background-color 0.2s ease',
56
+ }}>
57
+ <Stack direction="row" justifyContent="space-between" alignItems="flex-start">
58
+ <Stack direction="row" alignItems="center" spacing={1} flex={1}>
59
+ <Box
60
+ sx={{
61
+ width: 8,
62
+ height: 8,
63
+ borderRadius: '50%',
64
+ bgcolor: 'success.main',
65
+ flexShrink: 0,
66
+ }}
67
+ />
68
+ <Typography
69
+ variant="body1"
70
+ sx={{
71
+ fontWeight: 600,
72
+ fontSize: '1rem',
73
+ color: 'text.primary',
74
+ }}>
75
+ {vendor.name || vendor.vendor_key}
76
+ </Typography>
77
+ </Stack>
78
+ {/* Launcher 类型的链接 */}
79
+ {isLauncher && (
80
+ <Stack direction="row" spacing={0.5}>
81
+ <Tooltip title={t('admin.subscription.serviceHome')} placement="top">
82
+ <IconButton
83
+ size="small"
84
+ component="a"
85
+ href={joinURL(prefix, '/api/vendors/open/', subscriptionId, `?vendorId=${vendor.vendor_id}`)}
86
+ target="_blank"
87
+ rel="noopener noreferrer"
88
+ sx={{
89
+ color: 'primary.main',
90
+ '&:hover': {
91
+ backgroundColor: 'primary.lighter',
92
+ },
93
+ }}>
94
+ <Home fontSize="small" />
95
+ </IconButton>
96
+ </Tooltip>
97
+ <Tooltip title={t('admin.subscription.serviceDashboard')} placement="top">
98
+ <IconButton
99
+ size="small"
100
+ component="a"
101
+ href={joinURL(
102
+ prefix,
103
+ '/api/vendors/open/',
104
+ subscriptionId,
105
+ `?vendorId=${vendor.vendor_id}&target=dashboard`
106
+ )}
107
+ target="_blank"
108
+ rel="noopener noreferrer"
109
+ sx={{
110
+ color: 'primary.main',
111
+ '&:hover': {
112
+ backgroundColor: 'primary.lighter',
113
+ },
114
+ }}>
115
+ <Dashboard fontSize="small" />
116
+ </IconButton>
117
+ </Tooltip>
118
+ </Stack>
119
+ )}
120
+ </Stack>
121
+ </Box>
122
+ );
123
+ })}
124
+ </Stack>
125
+ </Box>
126
+ </>
127
+ );
128
+ }
@@ -10,14 +10,11 @@ import ClickBoundary from '../click-boundary';
10
10
  interface Vendor {
11
11
  id: string;
12
12
  vendor_key: string;
13
+ vendor_type: string;
13
14
  name: string;
14
15
  description: string;
15
16
  app_url: string;
16
- webhook_path: string;
17
- default_commission_rate: number;
18
- default_commission_type: 'percentage' | 'fixed_amount';
19
17
  status: 'active' | 'inactive';
20
- order_create_params: Record<string, any>;
21
18
  metadata: Record<string, any>;
22
19
  created_at: string;
23
20
  updated_at: string;
@@ -65,40 +62,11 @@ export default function VendorActions({ data, variant = 'compact', onChange }: V
65
62
  }
66
63
  };
67
64
 
68
- // 测试发货功能
69
- const onTestFulfillment = async () => {
70
- try {
71
- setState({ loading: true });
72
- const result = await api
73
- .post(`/api/vendors/${data.id}/test-fulfillment`, {
74
- productCode: 'test_product',
75
- })
76
- .then((res: any) => res.data);
77
-
78
- Toast.success(
79
- '发货测试成功!\n' +
80
- `订单号: ${result.fulfillmentResult?.orderId || 'N/A'}\n` +
81
- `状态: ${result.fulfillmentResult?.status || 'N/A'}\n` +
82
- `服务地址: ${result.fulfillmentResult?.serviceUrl || 'N/A'}`
83
- );
84
- onChange(state.action);
85
- } catch (err) {
86
- Toast.error(`发货测试失败: ${formatError(err)}`);
87
- } finally {
88
- setState({ loading: false, action: '' });
89
- }
90
- };
91
-
92
65
  return (
93
66
  <ClickBoundary>
94
67
  <Actions
95
68
  variant={variant}
96
69
  actions={[
97
- {
98
- label: '测试发货',
99
- handler: onTestFulfillment,
100
- color: 'success',
101
- },
102
70
  data.status === 'active'
103
71
  ? {
104
72
  label: t('admin.vendor.deactivate'),
@@ -602,6 +602,8 @@ export default flat({
602
602
  allowPromotionCodes: 'Allow promotion codes',
603
603
  requireCrossSell: 'Require cross sell products selected if eligible',
604
604
  includeFreeTrial: 'Include a free trial',
605
+ noStakeRequired: 'No stake required',
606
+ showProductFeatures: 'Show product features',
605
607
  freeTrialDaysPositive: 'Free trial days must be positive',
606
608
  includeCustomFields: 'Add custom fields',
607
609
  confirmPage: 'Confirmation Page',
@@ -890,14 +892,22 @@ export default flat({
890
892
  deactivateTip: 'Are you sure you want to deactivate vendor "{name}"?',
891
893
  name: 'Vendor Name',
892
894
  nameRequired: 'Vendor name is required',
893
- vendorKey: 'Vendor Type',
894
- vendorKeyRequired: 'Vendor type is required',
895
- vendorKeyHelp: 'Unique identifier for the vendor (e.g., launcher, did_names)',
895
+ vendorType: 'Vendor Type',
896
+ vendorTypeRequired: 'Vendor type is required',
897
+ launcher: 'Launcher',
898
+ vendorKey: 'Vendor Key',
899
+ vendorKeyRequired: 'Vendor key is required',
900
+ vendorKeyHelp: 'Unique identifier for the vendor',
896
901
  description: 'Description',
902
+ displayNameRequired: 'Display name is required',
903
+ displayNameHelp: 'This name will be displayed on the installation interface after successful payment',
897
904
  appUrl: 'App URL',
898
905
  appUrlRequired: 'App URL is required',
899
906
  appUrlInvalid: 'Please enter a valid URL starting with http:// or https://',
900
907
  appUrlHelp: 'The base URL of the vendor application',
908
+ vendorDid: 'Vendor DID',
909
+ vendorDidInvalid: 'Please enter a valid DID',
910
+ vendorDidHelp: 'Optional DID address for the vendor',
901
911
  webhookPath: 'Webhook Path',
902
912
  webhookPathInvalid: 'Please enter a valid path starting with /',
903
913
  webhookPathHelp: 'Optional webhook callback path (e.g., /webhooks/status)',
@@ -930,6 +940,9 @@ export default flat({
930
940
  empty: 'No subscriptions',
931
941
  viewAll: 'View all subscriptions',
932
942
  noActiveEmpty: 'You currently have no active subscriptions. You can choose to view your subscription history.',
943
+ includedServices: 'Included Services',
944
+ serviceHome: 'Visit Service Homepage',
945
+ serviceDashboard: 'Access Service Dashboard',
933
946
  attention: 'Past due subscriptions',
934
947
  product: 'Product',
935
948
  collectionMethod: 'Billing',
@@ -570,6 +570,8 @@ export default flat({
570
570
  requirePhoneNumber: '收集客户的电话号码',
571
571
  allowPromotionCodes: '允许促销代码',
572
572
  includeFreeTrial: '包含免费试用',
573
+ noStakeRequired: '无需质押',
574
+ showProductFeatures: '显示产品特性',
573
575
  freeTrialDaysPositive: '免费试用天数必须是正数',
574
576
  includeCustomFields: '添加自定义字段',
575
577
  requireCrossSell: '用户必须选择交叉销售的商品(如果有的话)',
@@ -868,14 +870,22 @@ export default flat({
868
870
  deactivateTip: '确定要停用供应商 "{name}" 吗?',
869
871
  name: '供应商名称',
870
872
  nameRequired: '供应商名称是必填项',
871
- vendorKey: '供应商类型',
872
- vendorKeyRequired: '供应商类型是必填项',
873
- vendorKeyHelp: '供应商的唯一标识符(如:launcher、did_names)',
873
+ vendorType: '供应商类型',
874
+ vendorTypeRequired: '供应商类型是必填项',
875
+ launcher: '启动器',
876
+ vendorKey: '供应商标识',
877
+ vendorKeyRequired: '供应商标识是必填项',
878
+ vendorKeyHelp: '供应商的唯一标识符',
874
879
  description: '描述',
880
+ displayNameRequired: '前台展示名称是必填项',
881
+ displayNameHelp: '此数据会展示到付款成功后的安装界面',
875
882
  appUrl: '应用地址',
876
883
  appUrlRequired: '应用地址是必填项',
877
- appUrlInvalid: '请输入以http://或https://开头的有效URL',
878
- appUrlHelp: '供应商应用的基础URL',
884
+ appUrlInvalid: '请输入有效的 URL,以 http:// 或 https:// 开头',
885
+ appUrlHelp: '供应商应用的基础 URL 地址',
886
+ vendorDid: '供应商 DID',
887
+ vendorDidInvalid: '请输入有效的 DID',
888
+ vendorDidHelp: '供应商的可选 DID 地址',
879
889
  webhookPath: 'Webhook路径',
880
890
  webhookPathInvalid: '请输入以/开头的有效路径',
881
891
  webhookPathHelp: '可选的webhook回调路径(如:/webhooks/status)',
@@ -908,6 +918,9 @@ export default flat({
908
918
  empty: '没有订阅',
909
919
  viewAll: '查看历史订阅',
910
920
  noActiveEmpty: '您当前没有服务中的订阅,您可以选择查看历史订阅',
921
+ includedServices: '包含的服务',
922
+ serviceHome: '访问服务主页',
923
+ serviceDashboard: '访问服务管理后台',
911
924
  product: '产品',
912
925
  attention: '将过期的订阅',
913
926
  collectionMethod: '计费',
@@ -66,6 +66,7 @@ export default function CreatePaymentLink() {
66
66
  subscription_data: {
67
67
  description: '',
68
68
  trial_period_days: 0,
69
+ no_stake: false,
69
70
  },
70
71
  nft_mint_settings: {
71
72
  enabled: false,
@@ -86,6 +87,7 @@ export default function CreatePaymentLink() {
86
87
  'billing_address_collection',
87
88
  'phone_number_collection',
88
89
  'currency_id',
90
+ 'metadata',
89
91
  ]);
90
92
 
91
93
  useEffect(() => {