payment-kit 1.20.7 → 1.20.9

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 (32) hide show
  1. package/api/src/crons/index.ts +1 -1
  2. package/api/src/index.ts +2 -2
  3. package/api/src/libs/{vendor → vendor-util}/adapters/factory.ts +9 -5
  4. package/api/src/libs/{vendor → vendor-util}/adapters/launcher-adapter.ts +3 -8
  5. package/api/src/libs/{vendor → vendor-util}/adapters/types.ts +1 -4
  6. package/api/src/libs/{vendor → vendor-util}/fulfillment.ts +11 -8
  7. package/api/src/queues/{vendor → vendors}/commission.ts +4 -5
  8. package/api/src/queues/{vendor → vendors}/fulfillment-coordinator.ts +33 -4
  9. package/api/src/queues/{vendor → vendors}/fulfillment.ts +2 -2
  10. package/api/src/queues/{vendor → vendors}/status-check.ts +2 -2
  11. package/api/src/routes/payment-links.ts +2 -1
  12. package/api/src/routes/products.ts +1 -0
  13. package/api/src/routes/vendor.ts +135 -213
  14. package/api/src/store/migrations/20250911-add-vendor-type.ts +26 -0
  15. package/api/src/store/models/product-vendor.ts +6 -24
  16. package/api/src/store/models/product.ts +1 -0
  17. package/blocklet.yml +1 -1
  18. package/doc/vendor_fulfillment_system.md +1 -1
  19. package/package.json +23 -22
  20. package/src/components/metadata/form.tsx +12 -19
  21. package/src/components/payment-link/before-pay.tsx +40 -0
  22. package/src/components/product/vendor-config.tsx +4 -11
  23. package/src/components/subscription/description.tsx +1 -6
  24. package/src/components/subscription/portal/list.tsx +82 -6
  25. package/src/components/subscription/vendor-service-list.tsx +128 -0
  26. package/src/components/vendor/actions.tsx +1 -33
  27. package/src/locales/en.tsx +13 -3
  28. package/src/locales/zh.tsx +13 -3
  29. package/src/pages/admin/products/links/create.tsx +2 -0
  30. package/src/pages/admin/products/vendors/create.tsx +108 -194
  31. package/src/pages/admin/products/vendors/index.tsx +14 -22
  32. package/src/pages/customer/subscription/detail.tsx +26 -11
@@ -1,30 +1,25 @@
1
1
  import { Router } from 'express';
2
2
  import Joi from 'joi';
3
3
 
4
- import { joinURL } from 'ufo';
5
4
  import { VendorAuth } from '@blocklet/payment-vendor';
6
- import { VendorFulfillmentService } from '../libs/vendor/fulfillment';
7
- import logger from '../libs/logger';
8
- import { ProductVendor } from '../store/models/product-vendor';
5
+ import { joinURL } from 'ufo';
6
+ import { MetadataSchema } from '../libs/api';
9
7
  import { wallet } from '../libs/auth';
10
- import { CheckoutSession } from '../store/models';
8
+ import dayjs from '../libs/dayjs';
9
+ import logger from '../libs/logger';
11
10
  import { authenticate } from '../libs/security';
12
11
  import { formatToShortUrl } from '../libs/url';
13
- import dayjs from '../libs/dayjs';
14
- import { MetadataSchema } from '../libs/api';
12
+ import { CheckoutSession } from '../store/models';
13
+ import { ProductVendor } from '../store/models/product-vendor';
15
14
 
16
15
  const authAdmin = authenticate<CheckoutSession>({ component: true, roles: ['owner', 'admin'] });
17
16
 
18
17
  const createVendorSchema = Joi.object({
19
18
  vendor_key: Joi.string().max(50).required(),
19
+ vendor_type: Joi.string().valid('launcher').default('launcher'),
20
20
  name: Joi.string().max(255).required(),
21
21
  description: Joi.string().max(1000).allow('').optional(),
22
22
  app_url: Joi.string().uri().max(512).required(),
23
- webhook_path: Joi.string().max(255).allow('').optional(),
24
- blocklet_meta_url: Joi.string().uri().max(512).allow('').optional(),
25
- default_commission_rate: Joi.number().min(0).max(100).required(),
26
- default_commission_type: Joi.string().valid('percentage', 'fixed_amount').required(),
27
- order_create_params: Joi.object().default({}),
28
23
  app_pid: Joi.string().max(255).allow('').optional(),
29
24
  app_logo: Joi.string().max(512).allow('').optional(),
30
25
  status: Joi.string().valid('active', 'inactive').default('active'),
@@ -32,26 +27,16 @@ const createVendorSchema = Joi.object({
32
27
  }).unknown(false);
33
28
 
34
29
  const updateVendorSchema = Joi.object({
30
+ vendor_type: Joi.string().valid('launcher').optional(),
35
31
  name: Joi.string().max(255).optional(),
36
32
  description: Joi.string().max(1000).allow('').optional(),
37
33
  app_url: Joi.string().uri().max(512).optional(),
38
- webhook_path: Joi.string().max(255).allow('').optional(),
39
- blocklet_meta_url: Joi.string().uri().max(512).allow('').optional(),
40
- default_commission_rate: Joi.number().min(0).max(100).optional(),
41
- default_commission_type: Joi.string().valid('percentage', 'fixed_amount').optional(),
42
- order_create_params: Joi.object().optional(),
43
34
  app_pid: Joi.string().max(255).allow('').optional(),
44
35
  app_logo: Joi.string().max(512).allow('').optional(),
45
36
  status: Joi.string().valid('active', 'inactive').optional(),
46
37
  metadata: MetadataSchema,
47
38
  }).unknown(true);
48
39
 
49
- const testFulfillmentSchema = Joi.object({
50
- productCode: Joi.string().max(100).required(),
51
- quantity: Joi.number().integer().min(1).default(1),
52
- customParams: Joi.object().optional(),
53
- }).unknown(true);
54
-
55
40
  const vendorIdParamSchema = Joi.object({
56
41
  id: Joi.string().max(100).required(),
57
42
  });
@@ -60,12 +45,21 @@ const sessionIdParamSchema = Joi.object({
60
45
  sessionId: Joi.string().max(100).required(),
61
46
  });
62
47
 
48
+ const subscriptionIdParamSchema = Joi.object({
49
+ subscriptionId: Joi.string().max(100).required(),
50
+ });
51
+
52
+ const vendorRedirectQuerySchema = Joi.object({
53
+ vendorId: Joi.string().max(100).required(),
54
+ target: Joi.string().valid('home', 'dashboard').default('home').allow(''),
55
+ });
56
+
63
57
  function validateParams(schema: Joi.ObjectSchema) {
64
58
  return (req: any, res: any, next: any) => {
65
59
  const { error } = schema.validate(req.params);
66
60
  if (error) {
67
61
  logger.error('Invalid parameters', {
68
- error: error.message,
62
+ error,
69
63
  params: req.params,
70
64
  });
71
65
  return res.status(400).json({
@@ -77,6 +71,21 @@ function validateParams(schema: Joi.ObjectSchema) {
77
71
  };
78
72
  }
79
73
 
74
+ function validateQuery(schema: Joi.ObjectSchema) {
75
+ return (req: any, res: any, next: any) => {
76
+ const { error, value } = schema.validate(req.query);
77
+ if (error) {
78
+ logger.error('Invalid query parameters', {
79
+ error,
80
+ query: req.query,
81
+ });
82
+ return res.redirect('/404');
83
+ }
84
+ req.query = value;
85
+ return next();
86
+ };
87
+ }
88
+
80
89
  async function getAllVendors(_req: any, res: any) {
81
90
  try {
82
91
  const vendors = await ProductVendor.findAll({
@@ -90,7 +99,7 @@ async function getAllVendors(_req: any, res: any) {
90
99
  });
91
100
  } catch (error: any) {
92
101
  logger.error('Failed to get vendors', {
93
- error: error.message || error.toString() || 'Unknown error',
102
+ error,
94
103
  stack: error.stack,
95
104
  });
96
105
  return res.status(500).json({ error: 'Internal server error. Failed to get vendors' });
@@ -107,7 +116,7 @@ async function getVendorById(req: any, res: any) {
107
116
  return res.json({ data: vendor });
108
117
  } catch (error: any) {
109
118
  logger.error('Failed to get vendor', {
110
- error: error.message || error.toString() || 'Unknown error',
119
+ error,
111
120
  id: req.params.id,
112
121
  });
113
122
  return res.status(500).json({ error: 'Internal server error. Failed to get vendor' });
@@ -126,27 +135,16 @@ async function createVendor(req: any, res: any) {
126
135
 
127
136
  const {
128
137
  vendor_key: vendorKey,
138
+ vendor_type: vendorType,
129
139
  name,
130
140
  description,
131
141
  app_url: appUrl,
132
- webhook_path: webhookPath,
133
- blocklet_meta_url: blockletMetaUrl,
134
- default_commission_rate: defaultCommissionRate,
135
- default_commission_type: defaultCommissionType,
136
- order_create_params: orderCreateParams,
137
142
  metadata,
138
143
  app_pid: appPid,
139
144
  app_logo: appLogo,
140
145
  status,
141
146
  } = value;
142
147
 
143
- const blockletMetaUrlFromMetadata = metadata?.blockletMetaUrl || blockletMetaUrl;
144
- if (!blockletMetaUrlFromMetadata) {
145
- return res.status(400).json({
146
- error: 'blockletMetaUrl is required in metadata',
147
- });
148
- }
149
-
150
148
  const existingVendor = await ProductVendor.findOne({
151
149
  where: { vendor_key: vendorKey },
152
150
  });
@@ -156,20 +154,14 @@ async function createVendor(req: any, res: any) {
156
154
 
157
155
  const vendor = await ProductVendor.create({
158
156
  vendor_key: vendorKey,
157
+ vendor_type: vendorType || 'launcher',
159
158
  name,
160
159
  description,
161
160
  app_url: appUrl,
162
- webhook_path: webhookPath,
163
- default_commission_rate: defaultCommissionRate,
164
- default_commission_type: defaultCommissionType,
165
- order_create_params: orderCreateParams || {},
166
161
  status: status || 'active',
167
162
  app_pid: appPid,
168
163
  app_logo: appLogo,
169
- metadata: {
170
- ...(metadata || {}),
171
- ...(blockletMetaUrl && { blockletMetaUrl }),
172
- },
164
+ metadata: metadata || {},
173
165
  created_by: req.user?.did || 'admin',
174
166
  });
175
167
 
@@ -177,7 +169,7 @@ async function createVendor(req: any, res: any) {
177
169
  return res.status(201).json({ data: vendor });
178
170
  } catch (error: any) {
179
171
  logger.error('Failed to create vendor', {
180
- error: error.message || error.toString() || 'Unknown error',
172
+ error,
181
173
  });
182
174
  return res.status(500).json({ error: 'Internal server error' });
183
175
  }
@@ -199,14 +191,10 @@ async function updateVendor(req: any, res: any) {
199
191
  }
200
192
 
201
193
  const {
194
+ vendor_type: vendorType,
202
195
  name,
203
196
  description,
204
197
  app_url: appUrl,
205
- webhook_path: webhookPath,
206
- blocklet_meta_url: blockletMetaUrl,
207
- default_commission_rate: defaultCommissionRate,
208
- default_commission_type: defaultCommissionType,
209
- order_create_params: orderCreateParams,
210
198
  status,
211
199
  metadata,
212
200
  app_pid: appPid,
@@ -222,21 +210,12 @@ async function updateVendor(req: any, res: any) {
222
210
  }
223
211
  }
224
212
  const updates = {
213
+ vendor_type: vendorType,
225
214
  name,
226
215
  description,
227
216
  app_url: appUrl,
228
- webhook_path: webhookPath,
229
- default_commission_rate: defaultCommissionRate,
230
- default_commission_type: defaultCommissionType,
231
- order_create_params: orderCreateParams,
232
217
  status,
233
- ...((metadata || blockletMetaUrl !== undefined) && {
234
- metadata: {
235
- ...(vendor.metadata || {}),
236
- ...(metadata || {}),
237
- ...(blockletMetaUrl !== undefined && { blockletMetaUrl }),
238
- },
239
- }),
218
+ metadata,
240
219
  app_pid: appPid,
241
220
  app_logo: appLogo,
242
221
  vendor_key: req.body.vendor_key,
@@ -247,7 +226,7 @@ async function updateVendor(req: any, res: any) {
247
226
  logger.info('Vendor updated', { vendorId: vendor.id });
248
227
  return res.json({ data: vendor });
249
228
  } catch (error: any) {
250
- logger.error('Failed to update vendor', { error: error.message, id: req.params.id });
229
+ logger.error('Failed to update vendor', { error, id: req.params.id });
251
230
  return res.status(500).json({ error: 'Internal server error' });
252
231
  }
253
232
  }
@@ -264,7 +243,7 @@ async function deleteVendor(req: any, res: any) {
264
243
  logger.info('Vendor deleted', { vendorId: vendor.id });
265
244
  return res.json({ success: true });
266
245
  } catch (error: any) {
267
- logger.error('Failed to delete vendor', { error: error.message, id: req.params.id });
246
+ logger.error('Failed to delete vendor', { error, id: req.params.id });
268
247
  return res.status(500).json({ error: 'Internal server error' });
269
248
  }
270
249
  }
@@ -286,131 +265,51 @@ async function testVendorConnection(req: any, res: any) {
286
265
  },
287
266
  });
288
267
  } catch (error: any) {
289
- logger.error('Failed to test vendor connection', { error: error.message, id: req.params.id });
268
+ logger.error('Failed to test vendor connection', { error, id: req.params.id });
290
269
  return res.status(500).json({ error: 'Internal server error' });
291
270
  }
292
271
  }
293
272
 
294
- async function testVendorFulfillment(req: any, res: any) {
295
- try {
296
- const vendor = await ProductVendor.findByPk(req.params.id);
297
- if (!vendor) {
298
- return res.status(404).json({ error: 'Vendor not found' });
299
- }
300
-
301
- const { error, value } = testFulfillmentSchema.validate(req.body);
302
- if (error) {
303
- return res.status(400).json({
304
- error: 'Validation failed',
305
- message: error.message,
306
- });
307
- }
308
-
309
- const { productCode, quantity, customParams } = value;
310
-
311
- const testCheckoutSession = {
312
- id: `test_checkout_${Date.now()}`,
313
- payment_intent_id: `test_payment_intent_${Date.now()}`,
314
- customer_id: 'test_customer',
315
- customer_did: 'z1XGnwJiV3Hnz36kCTJaDe6iiv2kF6DJokt',
316
- amount: 1000,
317
- currency: 'USD',
318
- amount_total: 1000,
319
- currency_id: 'USD',
320
- status: 'paid',
321
- line_items: [
322
- {
323
- vendor_id: productCode,
324
- quantity,
325
- amount: 1000,
326
- custom_params: {
327
- email: customParams?.email || 'test@example.com',
328
- testMode: true,
329
- ...customParams,
330
- },
331
- },
332
- ],
333
- vendor_info: [
334
- {
335
- vendor_key: vendor.vendor_key,
336
- status: 'pending',
337
- attempts: 0,
338
- },
339
- ],
340
- };
341
-
342
- const testVendorConfig = {
343
- vendor_id: vendor.id,
344
- vendor_key: vendor.vendor_key,
345
- app_url: vendor.app_url,
346
- commission_rate: vendor.default_commission_rate,
347
- commission_type: vendor.default_commission_type,
348
- order_create_params: vendor.order_create_params || {},
349
- custom_params: {
350
- email: customParams?.email || 'test@example.com',
351
- userDid: 'z1XGnwJiV3Hnz36kCTJaDe6iiv2kF6DJokt',
352
- },
353
- };
273
+ async function getVendorStatusByVendorId(vendorId: string, orderId: string, isDetail = false) {
274
+ if (!vendorId || !orderId) {
275
+ return {};
276
+ }
354
277
 
355
- logger.info('Starting test fulfillment', {
356
- vendorId: vendor.id,
357
- vendorKey: vendor.vendor_key,
358
- testData: {
359
- checkoutSessionId: testCheckoutSession.id,
360
- productCode: req.body.productCode || 'test_product',
361
- amount: testCheckoutSession.amount,
362
- currency: testCheckoutSession.currency,
363
- },
364
- });
278
+ const vendor = await ProductVendor.findByPk(vendorId);
279
+ const url = vendor?.app_url ? joinURL(vendor.app_url, '/api/vendor/', isDetail ? 'orders' : 'status', orderId) : null;
365
280
 
366
- const orderInfo = {
367
- checkoutSessionId: testCheckoutSession.id,
368
- amount_total: testCheckoutSession.amount_total.toString(),
369
- customer_id: testCheckoutSession.customer_id,
370
- payment_intent_id: testCheckoutSession.payment_intent_id,
371
- currency_id: testCheckoutSession.currency_id,
372
- customer_did: testCheckoutSession.customer_did,
373
- };
374
- const fulfillmentResult = await VendorFulfillmentService.fulfillSingleVendorOrder(orderInfo, testVendorConfig);
281
+ const { headers } = VendorAuth.signRequestWithHeaders({});
282
+ const name = vendor?.name;
283
+ const key = vendor?.vendor_key;
375
284
 
376
- logger.info('Test fulfillment completed', {
377
- vendorId: vendor.id,
378
- orderId: fulfillmentResult.orderId,
379
- status: fulfillmentResult.status,
380
- serviceUrl: fulfillmentResult.serviceUrl,
381
- });
285
+ return url
286
+ ? fetch(url, { headers })
287
+ .then(async (r) => {
288
+ const data = await r.json();
382
289
 
383
- return res.json({
384
- success: true,
385
- message: 'Test fulfillment completed successfully!',
386
- vendor: {
387
- id: vendor.id,
388
- name: vendor.name,
389
- vendor_key: vendor.vendor_key,
390
- app_url: vendor.app_url,
391
- },
392
- testData: {
393
- checkoutSessionId: testCheckoutSession.id,
394
- paymentIntentId: testCheckoutSession.payment_intent_id,
395
- productCode: req.body.productCode || 'test_product',
396
- amount: testCheckoutSession.amount,
397
- currency: testCheckoutSession.currency,
398
- },
399
- fulfillmentResult,
400
- });
401
- } catch (error: any) {
402
- logger.error('Test fulfillment failed', {
403
- error: error.message,
404
- stack: error.stack,
405
- vendorId: req.params.id,
406
- requestBody: req.body,
407
- });
408
- return res.status(500).json({
409
- error: 'Test fulfillment failed',
410
- details: error.message,
411
- vendor: req.params.id,
412
- });
413
- }
290
+ if (!data.dashboardUrl) {
291
+ return {
292
+ ...data,
293
+ name,
294
+ key,
295
+ };
296
+ }
297
+ const validUntil = dayjs().add(20, 'minutes').format('YYYY-MM-DDTHH:mm:ss+00:00');
298
+ const maxVisits = 5;
299
+
300
+ const homeUrl = await formatToShortUrl({ url: data.homeUrl, maxVisits, validUntil });
301
+ const dashboardUrl = await formatToShortUrl({ url: data.dashboardUrl, maxVisits, validUntil });
302
+
303
+ return {
304
+ ...data,
305
+ name,
306
+ key,
307
+ homeUrl,
308
+ dashboardUrl,
309
+ };
310
+ })
311
+ .catch((e) => ({ error: e.message }))
312
+ : null;
414
313
  }
415
314
 
416
315
  async function getVendorStatus(sessionId: string, isDetail = false) {
@@ -441,36 +340,8 @@ async function getVendorStatus(sessionId: string, isDetail = false) {
441
340
  };
442
341
  }
443
342
 
444
- const vendors = doc.vendor_info.map(async (item) => {
445
- const vendor = await ProductVendor.findByPk(item.vendor_id);
446
- const url = vendor?.app_url
447
- ? joinURL(vendor.app_url, '/api/vendor/', isDetail ? 'orders' : 'status', item.order_id)
448
- : null;
449
-
450
- const { headers } = VendorAuth.signRequestWithHeaders({});
451
-
452
- return url
453
- ? fetch(url, { headers })
454
- .then(async (r) => {
455
- const data = await r.json();
456
-
457
- if (!data.dashboardUrl) {
458
- return data;
459
- }
460
- const validUntil = dayjs().add(20, 'minutes').format('YYYY-MM-DDTHH:mm:ss+00:00');
461
- const maxVisits = 5;
462
-
463
- const homeUrl = await formatToShortUrl({ url: data.homeUrl, maxVisits, validUntil });
464
- const dashboardUrl = await formatToShortUrl({ url: data.dashboardUrl, maxVisits, validUntil });
465
-
466
- return {
467
- ...data,
468
- homeUrl,
469
- dashboardUrl,
470
- };
471
- })
472
- .catch((e) => ({ error: e.message }))
473
- : null;
343
+ const vendors = doc.vendor_info.map((item) => {
344
+ return getVendorStatusByVendorId(item.vendor_id, item.order_id, isDetail);
474
345
  });
475
346
 
476
347
  return {
@@ -510,11 +381,63 @@ async function getVendorFulfillmentDetail(req: any, res: any) {
510
381
  }
511
382
  }
512
383
 
384
+ async function redirectToVendor(req: any, res: any) {
385
+ const { subscriptionId } = req.params;
386
+ const { vendorId, target } = req.query;
387
+
388
+ try {
389
+ const checkoutSession = await CheckoutSession.findBySubscriptionId(subscriptionId);
390
+ if (!checkoutSession) {
391
+ logger.warn('CheckoutSession not found for subscription[redirect to vendor]', { subscriptionId });
392
+ return res.redirect('/404');
393
+ }
394
+
395
+ const order = checkoutSession?.vendor_info?.find((item) => item.vendor_id === vendorId);
396
+ if (!order) {
397
+ logger.warn('Vendor order not found in checkout session[redirect to vendor]', {
398
+ subscriptionId,
399
+ vendorId,
400
+ availableVendors: checkoutSession.vendor_info?.map((v) => v.vendor_key) || [],
401
+ });
402
+ return res.redirect('/404');
403
+ }
404
+
405
+ const detail = await getVendorStatusByVendorId(vendorId, order.order_id || '', true);
406
+ if (!detail) {
407
+ logger.warn('Vendor status detail not found', {
408
+ subscriptionId,
409
+ vendorId,
410
+ orderId: order.order_id,
411
+ });
412
+ return res.redirect('/404');
413
+ }
414
+
415
+ const redirectUrl = target === 'dashboard' ? detail.dashboardUrl : detail.homeUrl;
416
+ return res.redirect(redirectUrl);
417
+ } catch (error: any) {
418
+ logger.error('Failed to redirect to vendor service', {
419
+ error,
420
+ subscriptionId,
421
+ vendorId,
422
+ target,
423
+ });
424
+ return res.redirect('/404');
425
+ }
426
+ }
427
+
513
428
  const router = Router();
514
429
 
515
430
  router.get('/order/:sessionId/status', validateParams(sessionIdParamSchema), getVendorFulfillmentStatus);
516
431
  router.get('/order/:sessionId/detail', validateParams(sessionIdParamSchema), getVendorFulfillmentDetail);
517
432
 
433
+ router.get(
434
+ '/open/:subscriptionId',
435
+ authAdmin,
436
+ validateParams(subscriptionIdParamSchema),
437
+ validateQuery(vendorRedirectQuerySchema),
438
+ redirectToVendor
439
+ );
440
+
518
441
  router.get('/', getAllVendors);
519
442
  router.get('/:id', authAdmin, validateParams(vendorIdParamSchema), getVendorById);
520
443
  router.post('/', authAdmin, createVendor);
@@ -522,6 +445,5 @@ router.put('/:id', authAdmin, validateParams(vendorIdParamSchema), updateVendor)
522
445
  router.delete('/:id', authAdmin, validateParams(vendorIdParamSchema), deleteVendor);
523
446
 
524
447
  router.post('/:id/test-connection', authAdmin, validateParams(vendorIdParamSchema), testVendorConnection);
525
- router.post('/:id/test-fulfillment', authAdmin, validateParams(vendorIdParamSchema), testVendorFulfillment);
526
448
 
527
449
  export default router;
@@ -0,0 +1,26 @@
1
+ import { safeApplyColumnChanges, type Migration } from '../migrate';
2
+
3
+ export const up: Migration = async ({ context }) => {
4
+ // Add vendor_type column to product_vendors table
5
+ await safeApplyColumnChanges(context, {
6
+ product_vendors: [
7
+ {
8
+ name: 'vendor_type',
9
+ field: {
10
+ type: 'STRING',
11
+ allowNull: false,
12
+ defaultValue: 'launcher',
13
+ },
14
+ },
15
+ ],
16
+ });
17
+ await context.removeColumn('product_vendors', 'default_commission_rate');
18
+ await context.removeColumn('product_vendors', 'default_commission_type');
19
+ await context.removeColumn('product_vendors', 'order_create_params');
20
+ await context.removeColumn('product_vendors', 'webhook_path');
21
+ };
22
+
23
+ export const down: Migration = async ({ context }) => {
24
+ // Remove vendor_type column if we need to rollback
25
+ await context.removeColumn('product_vendors', 'vendor_type');
26
+ };
@@ -8,17 +8,11 @@ export const nextProductVendorId = createIdGenerator('pv', 24);
8
8
  export class ProductVendor extends Model<InferAttributes<ProductVendor>, InferCreationAttributes<ProductVendor>> {
9
9
  declare id: CreationOptional<string>;
10
10
  declare vendor_key: string;
11
+ declare vendor_type: string;
11
12
  declare name: string;
12
13
  declare description: string;
13
14
 
14
15
  declare app_url: string;
15
- declare webhook_path?: string;
16
-
17
- declare default_commission_rate: number;
18
- declare default_commission_type: 'percentage' | 'fixed_amount';
19
-
20
- declare order_create_params: Record<string, any>;
21
-
22
16
  declare app_pid?: string;
23
17
  declare app_logo?: string;
24
18
 
@@ -41,6 +35,11 @@ export class ProductVendor extends Model<InferAttributes<ProductVendor>, InferCr
41
35
  allowNull: false,
42
36
  unique: true,
43
37
  },
38
+ vendor_type: {
39
+ type: DataTypes.STRING(50),
40
+ allowNull: false,
41
+ defaultValue: 'launcher',
42
+ },
44
43
  name: {
45
44
  type: DataTypes.STRING(255),
46
45
  allowNull: false,
@@ -53,23 +52,6 @@ export class ProductVendor extends Model<InferAttributes<ProductVendor>, InferCr
53
52
  type: DataTypes.STRING(512),
54
53
  allowNull: false,
55
54
  },
56
- webhook_path: {
57
- type: DataTypes.STRING(255),
58
- allowNull: true,
59
- },
60
- default_commission_rate: {
61
- type: DataTypes.DECIMAL(7, 4),
62
- allowNull: false,
63
- },
64
- default_commission_type: {
65
- type: DataTypes.ENUM('percentage', 'fixed_amount'),
66
- allowNull: false,
67
- },
68
- order_create_params: {
69
- type: DataTypes.JSON,
70
- allowNull: true,
71
- defaultValue: {},
72
- },
73
55
  app_pid: {
74
56
  type: DataTypes.STRING(255),
75
57
  allowNull: true,
@@ -54,6 +54,7 @@ export class Product extends Model<InferAttributes<Product>, InferCreationAttrib
54
54
  declare vendor_config?: Array<{
55
55
  vendor_id: string;
56
56
  vendor_key: string;
57
+ vendor_type: string;
57
58
  name?: string;
58
59
  description?: string;
59
60
  commission_rate?: number;
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.7
17
+ version: 1.20.9
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({