payment-kit 1.20.5 → 1.20.6

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 (39) hide show
  1. package/api/src/crons/index.ts +11 -3
  2. package/api/src/index.ts +18 -14
  3. package/api/src/libs/adapters/launcher-adapter.ts +177 -0
  4. package/api/src/libs/env.ts +7 -0
  5. package/api/src/libs/url.ts +77 -0
  6. package/api/src/libs/vendor-adapter-factory.ts +22 -0
  7. package/api/src/libs/vendor-adapter.ts +109 -0
  8. package/api/src/libs/vendor-fulfillment.ts +321 -0
  9. package/api/src/queues/payment.ts +14 -10
  10. package/api/src/queues/payout.ts +1 -0
  11. package/api/src/queues/vendor/vendor-commission.ts +192 -0
  12. package/api/src/queues/vendor/vendor-fulfillment-coordinator.ts +627 -0
  13. package/api/src/queues/vendor/vendor-fulfillment.ts +97 -0
  14. package/api/src/queues/vendor/vendor-status-check.ts +179 -0
  15. package/api/src/routes/checkout-sessions.ts +3 -0
  16. package/api/src/routes/index.ts +2 -0
  17. package/api/src/routes/products.ts +72 -1
  18. package/api/src/routes/vendor.ts +526 -0
  19. package/api/src/store/migrations/20250820-add-product-vendor.ts +102 -0
  20. package/api/src/store/migrations/20250822-add-vendor-config-to-products.ts +56 -0
  21. package/api/src/store/models/checkout-session.ts +84 -18
  22. package/api/src/store/models/index.ts +3 -0
  23. package/api/src/store/models/payout.ts +11 -0
  24. package/api/src/store/models/product-vendor.ts +118 -0
  25. package/api/src/store/models/product.ts +15 -0
  26. package/blocklet.yml +8 -2
  27. package/doc/vendor_fulfillment_system.md +931 -0
  28. package/package.json +5 -4
  29. package/src/components/collapse.tsx +1 -0
  30. package/src/components/product/edit.tsx +9 -0
  31. package/src/components/product/form.tsx +11 -0
  32. package/src/components/product/vendor-config.tsx +249 -0
  33. package/src/components/vendor/actions.tsx +145 -0
  34. package/src/locales/en.tsx +89 -0
  35. package/src/locales/zh.tsx +89 -0
  36. package/src/pages/admin/products/index.tsx +11 -1
  37. package/src/pages/admin/products/products/detail.tsx +79 -2
  38. package/src/pages/admin/products/vendors/create.tsx +418 -0
  39. package/src/pages/admin/products/vendors/index.tsx +313 -0
@@ -29,6 +29,7 @@ import type {
29
29
  } from './types';
30
30
  import { Invoice } from './invoice';
31
31
  import { Subscription } from './subscription';
32
+ import { PaymentIntent } from './payment-intent';
32
33
 
33
34
  export const nextCheckoutSessionId = createIdGenerator('cs', 58);
34
35
 
@@ -195,24 +196,47 @@ export class CheckoutSession extends Model<InferAttributes<CheckoutSession>, Inf
195
196
 
196
197
  declare cross_sell_behavior?: LiteralUnion<'auto' | 'required', string>;
197
198
 
198
- // FIXME: Only exist on creation
199
- // declare discounts?: {
200
- // coupon?: string;
201
- // promotion_code?: string;
202
- // };
203
-
204
- // TODO: following fields not supported yet
205
- // automatic_tax
206
- // locale --> auto selected
207
- // payment_method_collection // FIXME:
208
- // payment_method_options
209
- // setup_intent_data
210
- // shipping_address_collection
211
- // shipping_options
212
- // tax_id_collection
213
- // shipping_cost
214
- // shipping_details
215
- // shipping_options
199
+ declare fulfillment_status?: LiteralUnion<
200
+ | 'pending'
201
+ | 'processing'
202
+ | 'completed'
203
+ | 'failed'
204
+ | 'cancelled'
205
+ | 'max_retries_exceeded'
206
+ | 'return_requested'
207
+ | 'sent',
208
+ string
209
+ >;
210
+ declare vendor_info?: Array<{
211
+ vendor_id: string;
212
+ vendor_key: string;
213
+ order_id: string;
214
+ status:
215
+ | 'pending'
216
+ | 'processing'
217
+ | 'completed'
218
+ | 'failed'
219
+ | 'cancelled'
220
+ | 'max_retries_exceeded'
221
+ | 'return_requested'
222
+ | 'sent';
223
+ service_url?: string;
224
+ app_url?: string;
225
+ error_message?: string;
226
+ amount: string;
227
+
228
+ attempts?: number;
229
+ lastAttemptAt?: string;
230
+ completedAt?: string;
231
+ commissionAmount?: string;
232
+
233
+ returnRequest?: {
234
+ reason: string;
235
+ requestedAt: string;
236
+ status: 'pending' | 'accepted' | 'rejected';
237
+ returnDetails?: string;
238
+ };
239
+ }>;
216
240
 
217
241
  declare created_at: CreationOptional<Date>;
218
242
  declare created_via: LiteralUnion<'api' | 'dashboard' | 'portal', string>;
@@ -438,6 +462,14 @@ export class CheckoutSession extends Model<InferAttributes<CheckoutSession>, Inf
438
462
  type: DataTypes.INTEGER,
439
463
  defaultValue: 0,
440
464
  },
465
+ fulfillment_status: {
466
+ type: DataTypes.STRING,
467
+ allowNull: true,
468
+ },
469
+ vendor_info: {
470
+ type: DataTypes.JSON,
471
+ allowNull: true,
472
+ },
441
473
  },
442
474
  {
443
475
  sequelize,
@@ -542,6 +574,40 @@ export class CheckoutSession extends Model<InferAttributes<CheckoutSession>, Inf
542
574
  }
543
575
  return checkoutSession;
544
576
  }
577
+
578
+ public static async findByPaymentIntentId(paymentIntentId: string): Promise<CheckoutSession | null> {
579
+ try {
580
+ // Method 1: Direct lookup by payment_intent_id
581
+ let checkoutSession = await CheckoutSession.findOne({
582
+ where: { payment_intent_id: paymentIntentId },
583
+ });
584
+
585
+ if (checkoutSession) {
586
+ return checkoutSession;
587
+ }
588
+
589
+ // Method 2: Lookup via Invoice -> Subscription for original CheckoutSession
590
+ // This typically occurs during subscription renewals
591
+ const paymentIntent = await PaymentIntent.findByPk(paymentIntentId);
592
+ if (!paymentIntent || !paymentIntent.invoice_id) {
593
+ return null;
594
+ }
595
+
596
+ const invoice = await Invoice.findByPk(paymentIntent.invoice_id);
597
+ if (!invoice || !invoice.subscription_id) {
598
+ return null;
599
+ }
600
+
601
+ // Find original CheckoutSession through subscription
602
+ checkoutSession = await CheckoutSession.findBySubscriptionId(invoice.subscription_id);
603
+ if (checkoutSession) {
604
+ return checkoutSession;
605
+ }
606
+ return null;
607
+ } catch (error: any) {
608
+ return null;
609
+ }
610
+ }
545
611
  }
546
612
 
547
613
  export type TCheckoutSession = InferAttributes<CheckoutSession>;
@@ -32,6 +32,7 @@ import { CreditTransaction, TCreditTransaction } from './credit-transaction';
32
32
  import { Meter, TMeter } from './meter';
33
33
  import { MeterEvent, TMeterEvent } from './meter-event';
34
34
  import { AutoRechargeConfig } from './auto-recharge-config';
35
+ import { ProductVendor } from './product-vendor';
35
36
 
36
37
  const models = {
37
38
  CheckoutSession,
@@ -67,6 +68,7 @@ const models = {
67
68
  Meter,
68
69
  MeterEvent,
69
70
  AutoRechargeConfig,
71
+ ProductVendor,
70
72
  };
71
73
 
72
74
  export function initialize(sequelize: any) {
@@ -116,6 +118,7 @@ export * from './credit-transaction';
116
118
  export * from './meter';
117
119
  export * from './meter-event';
118
120
  export * from './auto-recharge-config';
121
+ export * from './product-vendor';
119
122
 
120
123
  export type TPriceExpanded = TPrice & {
121
124
  object: 'price';
@@ -31,6 +31,13 @@ export class Payout extends Model<InferAttributes<Payout>, InferCreationAttribut
31
31
 
32
32
  declare metadata?: Record<string, any>;
33
33
 
34
+ declare vendor_info?: {
35
+ vendor_id: string;
36
+ app_pid?: string;
37
+ order_id: string;
38
+ commission_amount: string;
39
+ };
40
+
34
41
  declare status: LiteralUnion<'pending' | 'paid' | 'failed' | 'canceled' | 'in_transit', string>;
35
42
 
36
43
  // retry logic
@@ -168,6 +175,10 @@ export class Payout extends Model<InferAttributes<Payout>, InferCreationAttribut
168
175
  this.init(
169
176
  {
170
177
  ...Payout.GENESIS_ATTRIBUTES,
178
+ vendor_info: {
179
+ type: DataTypes.JSON,
180
+ allowNull: true,
181
+ },
171
182
  },
172
183
  {
173
184
  sequelize,
@@ -0,0 +1,118 @@
1
+ /* eslint-disable @typescript-eslint/lines-between-class-members */
2
+ import { CreationOptional, DataTypes, InferAttributes, InferCreationAttributes, Model } from 'sequelize';
3
+
4
+ import { createIdGenerator } from '../../libs/util';
5
+
6
+ export const nextProductVendorId = createIdGenerator('pv', 24);
7
+
8
+ export class ProductVendor extends Model<InferAttributes<ProductVendor>, InferCreationAttributes<ProductVendor>> {
9
+ declare id: CreationOptional<string>;
10
+ declare vendor_key: string;
11
+ declare name: string;
12
+ declare description: string;
13
+
14
+ 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
+ declare app_pid?: string;
23
+ declare app_logo?: string;
24
+
25
+ declare status: 'active' | 'inactive';
26
+ declare metadata: Record<string, any>;
27
+
28
+ declare created_by: string;
29
+ declare created_at: CreationOptional<Date>;
30
+ declare updated_at: CreationOptional<Date>;
31
+
32
+ public static readonly GENESIS_ATTRIBUTES = {
33
+ id: {
34
+ type: DataTypes.STRING(30),
35
+ primaryKey: true,
36
+ allowNull: false,
37
+ defaultValue: nextProductVendorId,
38
+ },
39
+ vendor_key: {
40
+ type: DataTypes.STRING(50),
41
+ allowNull: false,
42
+ unique: true,
43
+ },
44
+ name: {
45
+ type: DataTypes.STRING(255),
46
+ allowNull: false,
47
+ },
48
+ description: {
49
+ type: DataTypes.TEXT,
50
+ allowNull: true,
51
+ },
52
+ app_url: {
53
+ type: DataTypes.STRING(512),
54
+ allowNull: false,
55
+ },
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
+ app_pid: {
74
+ type: DataTypes.STRING(255),
75
+ allowNull: true,
76
+ },
77
+ app_logo: {
78
+ type: DataTypes.STRING(512),
79
+ allowNull: true,
80
+ },
81
+ status: {
82
+ type: DataTypes.ENUM('active', 'inactive'),
83
+ allowNull: false,
84
+ defaultValue: 'active',
85
+ },
86
+ metadata: {
87
+ type: DataTypes.JSON,
88
+ allowNull: true,
89
+ defaultValue: {},
90
+ },
91
+ created_by: {
92
+ type: DataTypes.STRING(30),
93
+ allowNull: true,
94
+ },
95
+ created_at: {
96
+ type: DataTypes.DATE,
97
+ defaultValue: DataTypes.NOW,
98
+ allowNull: false,
99
+ },
100
+ updated_at: {
101
+ type: DataTypes.DATE,
102
+ defaultValue: DataTypes.NOW,
103
+ allowNull: false,
104
+ },
105
+ };
106
+
107
+ public static initialize(sequelize: any) {
108
+ this.init(ProductVendor.GENESIS_ATTRIBUTES, {
109
+ sequelize,
110
+ modelName: 'ProductVendor',
111
+ tableName: 'product_vendors',
112
+ createdAt: 'created_at',
113
+ updatedAt: 'updated_at',
114
+ });
115
+ }
116
+ }
117
+
118
+ export type TProductVendor = InferAttributes<ProductVendor>;
@@ -51,6 +51,17 @@ export class Product extends Model<InferAttributes<Product>, InferCreationAttrib
51
51
  cross_sells_to_id: string;
52
52
  };
53
53
 
54
+ declare vendor_config?: Array<{
55
+ vendor_id: string;
56
+ vendor_key: string;
57
+ name?: string;
58
+ description?: string;
59
+ commission_rate?: number;
60
+ commission_type?: 'percentage' | 'fixed_amount';
61
+ amount?: string;
62
+ custom_params?: Record<string, any>;
63
+ }>;
64
+
54
65
  // TODO: goods related props are not supported
55
66
  // declare package_dimensions?: any;
56
67
  // declare shippable?: boolean;
@@ -138,6 +149,10 @@ export class Product extends Model<InferAttributes<Product>, InferCreationAttrib
138
149
  type: DataTypes.JSON,
139
150
  allowNull: true,
140
151
  },
152
+ vendor_config: {
153
+ type: DataTypes.JSON,
154
+ allowNull: true,
155
+ },
141
156
  },
142
157
  {
143
158
  sequelize,
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.5
17
+ version: 1.20.6
18
18
  logo: logo.png
19
19
  files:
20
20
  - dist
@@ -66,7 +66,13 @@ scripts:
66
66
  preStart: node api/hooks/pre-start.js
67
67
  preFlight: node api/hooks/pre-flight.js
68
68
  dev: pnpm run start
69
- environments: []
69
+ environments:
70
+ - name: SHORT_URL_API_KEY
71
+ description: Short URL service API Key
72
+ required: false
73
+ default: ''
74
+ secure: true
75
+ shared: false
70
76
  capabilities:
71
77
  navigation: true
72
78
  clusterMode: false