payment-kit 1.20.17 → 1.20.19
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.
- package/.env +1 -0
- package/api/src/index.ts +2 -0
- package/api/src/libs/vendor-util/adapters/didnames-adapter.ts +412 -0
- package/api/src/libs/vendor-util/adapters/factory.ts +26 -1
- package/api/src/libs/vendor-util/adapters/launcher-adapter.ts +110 -22
- package/api/src/libs/vendor-util/adapters/types.ts +4 -0
- package/api/src/libs/vendor-util/adapters/util.ts +7 -0
- package/api/src/libs/vendor-util/fulfillment.ts +7 -2
- package/api/src/queues/vendors/fulfillment-coordinator.ts +193 -7
- package/api/src/routes/vendor.ts +87 -86
- package/blocklet.yml +1 -1
- package/package.json +7 -6
- package/src/components/vendor/actions.tsx +1 -1
- package/src/locales/en.tsx +1 -0
- package/src/locales/zh.tsx +2 -1
- package/src/pages/admin/products/vendors/create.tsx +1 -36
- package/src/pages/admin/products/vendors/index.tsx +1 -1
|
@@ -47,7 +47,8 @@ export class VendorFulfillmentService {
|
|
|
47
47
|
currency_id: string;
|
|
48
48
|
customer_did: string;
|
|
49
49
|
},
|
|
50
|
-
vendorConfig: any
|
|
50
|
+
vendorConfig: any,
|
|
51
|
+
sharedContext?: any // Pass bindDomainCap and other shared data between vendors
|
|
51
52
|
): Promise<VendorFulfillmentResult> {
|
|
52
53
|
try {
|
|
53
54
|
const vendor = await ProductVendor.findByPk(vendorConfig.vendor_id);
|
|
@@ -87,7 +88,11 @@ export class VendorFulfillmentService {
|
|
|
87
88
|
},
|
|
88
89
|
deliveryParams: {
|
|
89
90
|
blockletMetaUrl: vendor.metadata?.blockletMetaUrl,
|
|
90
|
-
customParams:
|
|
91
|
+
customParams: {
|
|
92
|
+
...vendorConfig.custom_params,
|
|
93
|
+
// Merge shared context (bindDomainCap from didnames-adapter)
|
|
94
|
+
...(sharedContext || {}),
|
|
95
|
+
},
|
|
91
96
|
},
|
|
92
97
|
});
|
|
93
98
|
|
|
@@ -24,11 +24,172 @@ interface CoordinatorJob {
|
|
|
24
24
|
|
|
25
25
|
const MAX_FULFILLMENT_TIMEOUT = 300000;
|
|
26
26
|
|
|
27
|
+
// Helper function to get order info for coordinated fulfillment
|
|
28
|
+
async function getCoordinatedOrderInfo(checkoutSessionId: string) {
|
|
29
|
+
const checkoutSession = await CheckoutSession.findByPk(checkoutSessionId);
|
|
30
|
+
|
|
31
|
+
if (!checkoutSession) {
|
|
32
|
+
throw new Error('CheckoutSession or Invoice not found');
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return {
|
|
36
|
+
checkoutSessionId,
|
|
37
|
+
amount_total: checkoutSession.amount_total,
|
|
38
|
+
customer_id: checkoutSession.customer_id || '',
|
|
39
|
+
invoiceId: checkoutSession.invoice_id || '',
|
|
40
|
+
currency_id: checkoutSession.currency_id,
|
|
41
|
+
customer_did: checkoutSession.customer_did || '',
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async function handleCoordinatedFulfillment(job: any): Promise<void> {
|
|
46
|
+
const { checkoutSessionId, invoiceId, didnamesVendorConfig, launcherVendorConfig, context } = job;
|
|
47
|
+
|
|
48
|
+
try {
|
|
49
|
+
logger.info('Starting coordinated fulfillment', {
|
|
50
|
+
checkoutSessionId,
|
|
51
|
+
didnamesVendorId: didnamesVendorConfig.vendor_id,
|
|
52
|
+
launcherVendorId: launcherVendorConfig.vendor_id,
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
const orderInfo = await getCoordinatedOrderInfo(checkoutSessionId);
|
|
56
|
+
if (!orderInfo) {
|
|
57
|
+
throw new Error('Order info not found');
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Step 1: Execute didnames fulfillment to generate bindDomainCap
|
|
61
|
+
logger.info('Step 1: Executing didnames fulfillment', { checkoutSessionId });
|
|
62
|
+
const didnamesResult = await VendorFulfillmentService.fulfillSingleVendorOrder(
|
|
63
|
+
orderInfo,
|
|
64
|
+
didnamesVendorConfig,
|
|
65
|
+
context
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
if (didnamesResult.status === 'failed') {
|
|
69
|
+
throw new Error(`Didnames fulfillment failed: ${didnamesResult.errorMessage}`);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Extract bindDomainCap from didnames result
|
|
73
|
+
const bindCapData = didnamesResult.orderDetails?.customParams || {};
|
|
74
|
+
const { domain, sessionId, bindDomainCap, nftDid } = bindCapData;
|
|
75
|
+
|
|
76
|
+
if (!bindDomainCap || !domain || !sessionId) {
|
|
77
|
+
logger.warn('Missing bindDomainCap data from didnames adapter', {
|
|
78
|
+
checkoutSessionId,
|
|
79
|
+
hasBindCap: !!bindDomainCap,
|
|
80
|
+
hasDomain: !!domain,
|
|
81
|
+
hasSessionId: !!sessionId,
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Update didnames vendor status
|
|
86
|
+
await updateVendorFulfillmentStatus(
|
|
87
|
+
checkoutSessionId,
|
|
88
|
+
invoiceId,
|
|
89
|
+
didnamesVendorConfig.vendor_id,
|
|
90
|
+
didnamesResult.status === 'completed' ? 'completed' : 'sent',
|
|
91
|
+
{
|
|
92
|
+
orderId: didnamesResult.orderId,
|
|
93
|
+
commissionAmount: didnamesResult.commissionAmount,
|
|
94
|
+
serviceUrl: didnamesResult.serviceUrl,
|
|
95
|
+
}
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
// Step 2: Execute launcher fulfillment with bindDomainCap
|
|
99
|
+
logger.info('Step 2: Executing launcher fulfillment with bindDomainCap', {
|
|
100
|
+
checkoutSessionId,
|
|
101
|
+
hasBindCapData: !!bindDomainCap,
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
const launcherResult = await VendorFulfillmentService.fulfillSingleVendorOrder(orderInfo, launcherVendorConfig, {
|
|
105
|
+
...context,
|
|
106
|
+
domain,
|
|
107
|
+
sessionId,
|
|
108
|
+
bindDomainCap,
|
|
109
|
+
domainNftDid: nftDid,
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
// Update launcher vendor status
|
|
113
|
+
await updateVendorFulfillmentStatus(
|
|
114
|
+
checkoutSessionId,
|
|
115
|
+
invoiceId,
|
|
116
|
+
launcherVendorConfig.vendor_id,
|
|
117
|
+
launcherResult.status === 'completed' ? 'completed' : 'sent',
|
|
118
|
+
{
|
|
119
|
+
orderId: launcherResult.orderId,
|
|
120
|
+
commissionAmount: launcherResult.commissionAmount,
|
|
121
|
+
serviceUrl: launcherResult.serviceUrl,
|
|
122
|
+
}
|
|
123
|
+
);
|
|
124
|
+
|
|
125
|
+
logger.info('Coordinated fulfillment completed successfully', {
|
|
126
|
+
checkoutSessionId,
|
|
127
|
+
didnamesStatus: didnamesResult.status,
|
|
128
|
+
launcherStatus: launcherResult.status,
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
// Check if all vendors are completed to trigger commission
|
|
132
|
+
await triggerCommissionProcess(checkoutSessionId, invoiceId);
|
|
133
|
+
} catch (error: any) {
|
|
134
|
+
logger.error('Coordinated fulfillment failed', {
|
|
135
|
+
checkoutSessionId,
|
|
136
|
+
error: error.message,
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
// Mark both vendors as failed
|
|
140
|
+
await updateVendorFulfillmentStatus(checkoutSessionId, invoiceId, didnamesVendorConfig.vendor_id, 'failed', {
|
|
141
|
+
lastError: error.message,
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
await updateVendorFulfillmentStatus(checkoutSessionId, invoiceId, launcherVendorConfig.vendor_id, 'failed', {
|
|
145
|
+
lastError: error.message,
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
throw error;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
27
152
|
export const fulfillmentCoordinatorQueue = createQueue({
|
|
28
153
|
name: 'fulfillment-coordinator',
|
|
29
154
|
onJob: handleFulfillmentCoordination,
|
|
30
155
|
});
|
|
31
156
|
|
|
157
|
+
export const coordinatedFulfillmentQueue = createQueue({
|
|
158
|
+
name: 'coordinated-fulfillment',
|
|
159
|
+
onJob: handleCoordinatedFulfillment,
|
|
160
|
+
options: {
|
|
161
|
+
concurrency: 1,
|
|
162
|
+
maxRetries: 3,
|
|
163
|
+
enableScheduledJob: true,
|
|
164
|
+
},
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
export const startCoordinatedFulfillmentQueue = () => {
|
|
168
|
+
logger.debug('startCoordinatedFulfillmentQueue');
|
|
169
|
+
return Promise.resolve();
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
// Add event listener for coordinated fulfillment
|
|
173
|
+
events.on('vendor.fulfillment.coordinated', async (id, job) => {
|
|
174
|
+
try {
|
|
175
|
+
const exist = await coordinatedFulfillmentQueue.get(id);
|
|
176
|
+
if (!exist) {
|
|
177
|
+
logger.info('Adding coordinated fulfillment job to queue', { id, checkoutSessionId: job.checkoutSessionId });
|
|
178
|
+
coordinatedFulfillmentQueue.push({
|
|
179
|
+
id,
|
|
180
|
+
job,
|
|
181
|
+
});
|
|
182
|
+
} else {
|
|
183
|
+
logger.info('Coordinated fulfillment job already exists, skipping', { id });
|
|
184
|
+
}
|
|
185
|
+
} catch (error: any) {
|
|
186
|
+
logger.error('Failed to handle coordinated fulfillment queue event', {
|
|
187
|
+
id,
|
|
188
|
+
error: error.message,
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
});
|
|
192
|
+
|
|
32
193
|
export async function startVendorFulfillment(checkoutSessionId: string, invoiceId: string): Promise<void> {
|
|
33
194
|
try {
|
|
34
195
|
logger.info('Starting vendor fulfillment process', {
|
|
@@ -60,17 +221,42 @@ export async function startVendorFulfillment(checkoutSessionId: string, invoiceI
|
|
|
60
221
|
|
|
61
222
|
await updateVendorInfo(checkoutSessionId, initialVendorInfo);
|
|
62
223
|
|
|
63
|
-
//
|
|
64
|
-
|
|
65
|
-
|
|
224
|
+
// Check if we need coordinated fulfillment (didnames + launcher)
|
|
225
|
+
const didnamesVendor = vendorConfigs.find((v) => v.vendor_type === 'didnames');
|
|
226
|
+
const launcherVendor = vendorConfigs.find((v) => v.vendor_type === 'launcher');
|
|
227
|
+
|
|
228
|
+
if (didnamesVendor && launcherVendor) {
|
|
229
|
+
// Coordinated fulfillment: didnames first, then launcher with bindDomainCap
|
|
230
|
+
logger.info('Starting coordinated domain binding fulfillment', {
|
|
231
|
+
checkoutSessionId,
|
|
232
|
+
didnamesVendorId: didnamesVendor.vendor_id,
|
|
233
|
+
launcherVendorId: launcherVendor.vendor_id,
|
|
234
|
+
});
|
|
66
235
|
|
|
67
|
-
|
|
236
|
+
const coordinatedJobId = `coordinated-fulfillment-${checkoutSessionId}`;
|
|
237
|
+
events.emit('vendor.fulfillment.coordinated', coordinatedJobId, {
|
|
68
238
|
checkoutSessionId,
|
|
69
239
|
invoiceId,
|
|
70
|
-
|
|
71
|
-
|
|
240
|
+
didnamesVendorConfig: didnamesVendor,
|
|
241
|
+
launcherVendorConfig: launcherVendor,
|
|
72
242
|
retryOnError: true,
|
|
73
243
|
});
|
|
244
|
+
} else {
|
|
245
|
+
// Regular parallel fulfillment for other vendors
|
|
246
|
+
for (const vendorConfig of vendorConfigs) {
|
|
247
|
+
const vendorFulfillmentJobId = `vendor-fulfillment-${checkoutSessionId}-${vendorConfig.vendor_id}`;
|
|
248
|
+
|
|
249
|
+
events.emit('vendor.fulfillment.queued', vendorFulfillmentJobId, {
|
|
250
|
+
checkoutSessionId,
|
|
251
|
+
invoiceId,
|
|
252
|
+
vendorId: vendorConfig.vendor_id,
|
|
253
|
+
vendorConfig,
|
|
254
|
+
retryOnError: true,
|
|
255
|
+
context: {
|
|
256
|
+
subdomain: `docsmith-${Date.now()}`,
|
|
257
|
+
},
|
|
258
|
+
});
|
|
259
|
+
}
|
|
74
260
|
}
|
|
75
261
|
|
|
76
262
|
logger.info('Vendor fulfillment process has been triggered', {
|
|
@@ -294,7 +480,7 @@ async function updateSingleVendorInfo(
|
|
|
294
480
|
logger.error('updateVendorInfo - Update failed', {
|
|
295
481
|
checkoutSessionId,
|
|
296
482
|
vendorId,
|
|
297
|
-
error
|
|
483
|
+
error,
|
|
298
484
|
});
|
|
299
485
|
throw error;
|
|
300
486
|
} finally {
|
package/api/src/routes/vendor.ts
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { getUrl } from '@blocklet/sdk/lib/component';
|
|
2
2
|
import { Router } from 'express';
|
|
3
3
|
import Joi from 'joi';
|
|
4
|
+
import { middleware } from '@blocklet/payment-vendor';
|
|
4
5
|
|
|
5
|
-
import { Auth as VendorAuth, middleware } from '@blocklet/payment-vendor';
|
|
6
|
-
import { joinURL } from 'ufo';
|
|
7
6
|
import { MetadataSchema } from '../libs/api';
|
|
8
7
|
import { wallet } from '../libs/auth';
|
|
9
8
|
import dayjs from '../libs/dayjs';
|
|
@@ -11,37 +10,37 @@ import logger from '../libs/logger';
|
|
|
11
10
|
import { authenticate } from '../libs/security';
|
|
12
11
|
import { formatToShortUrl } from '../libs/url';
|
|
13
12
|
import { getBlockletJson } from '../libs/util';
|
|
13
|
+
import { VendorFulfillmentService } from '../libs/vendor-util/fulfillment';
|
|
14
14
|
import { CheckoutSession, Invoice, Subscription } from '../store/models';
|
|
15
15
|
import { ProductVendor } from '../store/models/product-vendor';
|
|
16
16
|
|
|
17
|
+
const VENDOR_DID = {
|
|
18
|
+
launcher: 'z8iZkFBbrVQxZHvcWWB3Sa2TrfGmSeFz9MSU7',
|
|
19
|
+
didnames: 'z2qaGosS3rZ7m5ttP3Nd4V4qczR9TryTcRV4p',
|
|
20
|
+
};
|
|
21
|
+
|
|
17
22
|
const authAdmin = authenticate<CheckoutSession>({ component: true, roles: ['owner', 'admin'] });
|
|
18
23
|
const loginAuth = authenticate<CheckoutSession>({ component: true, ensureLogin: true, mine: true });
|
|
19
24
|
|
|
20
25
|
const createVendorSchema = Joi.object({
|
|
21
26
|
vendor_key: Joi.string().max(50).required(),
|
|
22
|
-
vendor_type: Joi.string().valid('launcher').default('launcher'),
|
|
27
|
+
vendor_type: Joi.string().valid('launcher', 'didnames').default('launcher'),
|
|
23
28
|
name: Joi.string().max(255).required(),
|
|
24
29
|
description: Joi.string().max(1000).allow('').optional(),
|
|
25
30
|
app_url: Joi.string().uri().max(512).required(),
|
|
26
31
|
app_pid: Joi.string().max(255).allow('').optional(),
|
|
27
32
|
app_logo: Joi.string().max(512).allow('').optional(),
|
|
28
|
-
vendor_did: Joi.string()
|
|
29
|
-
.pattern(/^(did:abt:)?[1-9A-HJ-NP-Za-km-z]{37}$/)
|
|
30
|
-
.required(),
|
|
31
33
|
status: Joi.string().valid('active', 'inactive').default('active'),
|
|
32
34
|
metadata: MetadataSchema,
|
|
33
35
|
}).unknown(false);
|
|
34
36
|
|
|
35
37
|
const updateVendorSchema = Joi.object({
|
|
36
|
-
vendor_type: Joi.string().valid('launcher').optional(),
|
|
38
|
+
vendor_type: Joi.string().valid('launcher', 'didnames').optional(),
|
|
37
39
|
name: Joi.string().max(255).optional(),
|
|
38
40
|
description: Joi.string().max(1000).allow('').optional(),
|
|
39
41
|
app_url: Joi.string().uri().max(512).optional(),
|
|
40
42
|
app_pid: Joi.string().max(255).allow('').optional(),
|
|
41
43
|
app_logo: Joi.string().max(512).allow('').optional(),
|
|
42
|
-
vendor_did: Joi.string()
|
|
43
|
-
.pattern(/^(did:abt:)?[1-9A-HJ-NP-Za-km-z]{37}$/)
|
|
44
|
-
.required(),
|
|
45
44
|
status: Joi.string().valid('active', 'inactive').optional(),
|
|
46
45
|
metadata: MetadataSchema,
|
|
47
46
|
}).unknown(true);
|
|
@@ -115,7 +114,7 @@ async function getAllVendors(_req: any, res: any) {
|
|
|
115
114
|
}
|
|
116
115
|
}
|
|
117
116
|
|
|
118
|
-
async function
|
|
117
|
+
async function getVendorInfo(req: any, res: any) {
|
|
119
118
|
try {
|
|
120
119
|
const vendor = await ProductVendor.findByPk(req.params.id);
|
|
121
120
|
if (!vendor) {
|
|
@@ -144,17 +143,19 @@ async function createVendor(req: any, res: any) {
|
|
|
144
143
|
|
|
145
144
|
const {
|
|
146
145
|
vendor_key: vendorKey,
|
|
147
|
-
vendor_type:
|
|
146
|
+
vendor_type: type,
|
|
148
147
|
name,
|
|
149
148
|
description,
|
|
150
149
|
app_url: appUrl,
|
|
151
|
-
vendor_did: vendorDid,
|
|
152
150
|
metadata,
|
|
153
151
|
app_pid: appPid,
|
|
154
152
|
app_logo: appLogo,
|
|
155
153
|
status,
|
|
156
154
|
} = value;
|
|
157
155
|
|
|
156
|
+
const vendorType = (type || 'launcher') as 'launcher' | 'didnames';
|
|
157
|
+
const vendorDid = VENDOR_DID[vendorType];
|
|
158
|
+
|
|
158
159
|
const existingVendor = await ProductVendor.findOne({
|
|
159
160
|
where: { vendor_key: vendorKey },
|
|
160
161
|
});
|
|
@@ -164,18 +165,25 @@ async function createVendor(req: any, res: any) {
|
|
|
164
165
|
|
|
165
166
|
const blockletJson = await getBlockletJson(appUrl);
|
|
166
167
|
|
|
168
|
+
const mountPoint =
|
|
169
|
+
blockletJson?.componentMountPoints?.find((item: any) => item.did === vendorDid)?.mountPoint || '/';
|
|
170
|
+
|
|
167
171
|
const vendor = await ProductVendor.create({
|
|
168
172
|
vendor_key: vendorKey,
|
|
169
|
-
vendor_type: vendorType
|
|
173
|
+
vendor_type: vendorType,
|
|
170
174
|
name,
|
|
171
175
|
description,
|
|
172
176
|
app_url: appUrl,
|
|
173
|
-
vendor_did: vendorDid
|
|
177
|
+
vendor_did: vendorDid,
|
|
174
178
|
status: status || 'active',
|
|
175
179
|
app_pid: appPid,
|
|
176
180
|
app_logo: appLogo,
|
|
177
|
-
metadata:
|
|
181
|
+
metadata: {
|
|
182
|
+
...metadata,
|
|
183
|
+
mountPoint,
|
|
184
|
+
},
|
|
178
185
|
extends: {
|
|
186
|
+
mountPoint,
|
|
179
187
|
appId: blockletJson?.appId,
|
|
180
188
|
appPk: blockletJson?.appPk,
|
|
181
189
|
},
|
|
@@ -208,19 +216,24 @@ async function updateVendor(req: any, res: any) {
|
|
|
208
216
|
}
|
|
209
217
|
|
|
210
218
|
const {
|
|
211
|
-
vendor_type:
|
|
219
|
+
vendor_type: type,
|
|
212
220
|
name,
|
|
213
221
|
description,
|
|
214
222
|
app_url: appUrl,
|
|
215
|
-
vendor_did: vendorDid,
|
|
216
223
|
status,
|
|
217
224
|
metadata,
|
|
218
225
|
app_pid: appPid,
|
|
219
226
|
app_logo: appLogo,
|
|
220
227
|
} = value;
|
|
221
228
|
|
|
229
|
+
const vendorType = (type || 'launcher') as 'launcher' | 'didnames';
|
|
230
|
+
const vendorDid = VENDOR_DID[vendorType];
|
|
231
|
+
|
|
222
232
|
const blockletJson = await getBlockletJson(appUrl);
|
|
223
233
|
|
|
234
|
+
const mountPoint =
|
|
235
|
+
blockletJson?.componentMountPoints?.find((item: any) => item.did === vendorDid)?.mountPoint || '/';
|
|
236
|
+
|
|
224
237
|
if (req.body.vendorKey && req.body.vendorKey !== vendor.vendor_key) {
|
|
225
238
|
const existingVendor = await ProductVendor.findOne({
|
|
226
239
|
where: { vendor_key: req.body.vendorKey },
|
|
@@ -236,11 +249,15 @@ async function updateVendor(req: any, res: any) {
|
|
|
236
249
|
app_url: appUrl,
|
|
237
250
|
vendor_did: vendorDid,
|
|
238
251
|
status,
|
|
239
|
-
metadata
|
|
252
|
+
metadata: {
|
|
253
|
+
...metadata,
|
|
254
|
+
mountPoint,
|
|
255
|
+
},
|
|
240
256
|
app_pid: appPid,
|
|
241
257
|
app_logo: appLogo,
|
|
242
258
|
vendor_key: req.body.vendor_key,
|
|
243
259
|
extends: {
|
|
260
|
+
mountPoint,
|
|
244
261
|
appId: blockletJson?.appId,
|
|
245
262
|
appPk: blockletJson?.appPk,
|
|
246
263
|
},
|
|
@@ -295,57 +312,40 @@ async function testVendorConnection(req: any, res: any) {
|
|
|
295
312
|
}
|
|
296
313
|
}
|
|
297
314
|
|
|
298
|
-
async
|
|
315
|
+
const getVendorById = async (vendorId: string, orderId: string) => {
|
|
316
|
+
if (!vendorId || !orderId) {
|
|
317
|
+
throw new Error(`vendorId or orderId is required, vendorId: ${vendorId}, orderId: ${orderId}`);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
const vendor = await ProductVendor.findByPk(vendorId);
|
|
321
|
+
if (!vendor) {
|
|
322
|
+
throw new Error(`vendor not found, vendorId: ${vendorId}`);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
const vendorAdapter = await VendorFulfillmentService.getVendorAdapter(vendor.vendor_key);
|
|
326
|
+
|
|
327
|
+
const data = await vendorAdapter.getOrder(vendor, orderId);
|
|
328
|
+
|
|
329
|
+
return data;
|
|
330
|
+
};
|
|
331
|
+
|
|
332
|
+
async function getVendorStatusById(vendorId: string, orderId: string) {
|
|
299
333
|
if (!vendorId || !orderId) {
|
|
300
|
-
|
|
334
|
+
throw new Error(`vendorId or orderId is required, vendorId: ${vendorId}, orderId: ${orderId}`);
|
|
301
335
|
}
|
|
302
336
|
|
|
303
337
|
const vendor = await ProductVendor.findByPk(vendorId);
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
: null;
|
|
313
|
-
|
|
314
|
-
const { headers } = VendorAuth.signRequestWithHeaders({});
|
|
315
|
-
const name = vendor?.name;
|
|
316
|
-
const key = vendor?.vendor_key;
|
|
317
|
-
|
|
318
|
-
return url
|
|
319
|
-
? fetch(url, { headers })
|
|
320
|
-
.then(async (r) => {
|
|
321
|
-
const data = await r.json();
|
|
322
|
-
|
|
323
|
-
if (!data.dashboardUrl) {
|
|
324
|
-
return {
|
|
325
|
-
...data,
|
|
326
|
-
name,
|
|
327
|
-
key,
|
|
328
|
-
};
|
|
329
|
-
}
|
|
330
|
-
const validUntil = dayjs().add(20, 'minutes').format('YYYY-MM-DDTHH:mm:ss+00:00');
|
|
331
|
-
const maxVisits = 5;
|
|
332
|
-
|
|
333
|
-
const homeUrl = await formatToShortUrl({ url: data.homeUrl, maxVisits, validUntil });
|
|
334
|
-
const dashboardUrl = await formatToShortUrl({ url: data.dashboardUrl, maxVisits, validUntil });
|
|
335
|
-
|
|
336
|
-
return {
|
|
337
|
-
...data,
|
|
338
|
-
name,
|
|
339
|
-
key,
|
|
340
|
-
homeUrl,
|
|
341
|
-
dashboardUrl,
|
|
342
|
-
};
|
|
343
|
-
})
|
|
344
|
-
.catch((e) => ({ error: e.message }))
|
|
345
|
-
: null;
|
|
338
|
+
|
|
339
|
+
if (!vendor) {
|
|
340
|
+
throw new Error(`vendor not found, vendorId: ${vendorId}`);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
const vendorAdapter = await VendorFulfillmentService.getVendorAdapter(vendor.vendor_key);
|
|
344
|
+
|
|
345
|
+
return vendorAdapter.getOrderStatus(vendor, orderId);
|
|
346
346
|
}
|
|
347
347
|
|
|
348
|
-
async function
|
|
348
|
+
async function doRequestVendor(sessionId: string, func: (vendorId: string, orderId: string) => Promise<any>) {
|
|
349
349
|
const doc = await CheckoutSession.findByPk(sessionId);
|
|
350
350
|
|
|
351
351
|
if (!doc) {
|
|
@@ -377,35 +377,36 @@ async function getVendorStatus(sessionId: string, isDetail = false) {
|
|
|
377
377
|
}
|
|
378
378
|
|
|
379
379
|
const vendors = doc.vendor_info.map((item) => {
|
|
380
|
-
return
|
|
381
|
-
return {
|
|
382
|
-
error_message: item.error_message,
|
|
383
|
-
status: item.status,
|
|
384
|
-
...status,
|
|
385
|
-
};
|
|
386
|
-
});
|
|
380
|
+
return func(item.vendor_id, item.order_id);
|
|
387
381
|
});
|
|
388
382
|
|
|
389
|
-
|
|
390
|
-
|
|
383
|
+
return {
|
|
384
|
+
payment_status: doc.payment_status,
|
|
385
|
+
session_status: doc.status,
|
|
386
|
+
vendors: await Promise.all(vendors),
|
|
387
|
+
subscriptionId: doc.subscription_id,
|
|
388
|
+
error: null,
|
|
389
|
+
};
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
async function getVendorStatus(sessionId: string) {
|
|
393
|
+
const result: any = await doRequestVendor(sessionId, getVendorStatusById);
|
|
391
394
|
|
|
392
|
-
if (
|
|
393
|
-
const subscriptionUrl = getUrl(`/customer/subscription/${subscriptionId}`);
|
|
395
|
+
if (result.subscriptionId) {
|
|
396
|
+
const subscriptionUrl = getUrl(`/customer/subscription/${result.subscriptionId}`);
|
|
394
397
|
|
|
395
|
-
|
|
398
|
+
result.subscriptionUrl = await formatToShortUrl({
|
|
396
399
|
url: subscriptionUrl,
|
|
397
400
|
maxVisits: 5,
|
|
398
401
|
validUntil: dayjs().add(20, 'minutes').format('YYYY-MM-DDTHH:mm:ss+00:00'),
|
|
399
402
|
});
|
|
400
403
|
}
|
|
401
404
|
|
|
402
|
-
return
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
error: null,
|
|
408
|
-
};
|
|
405
|
+
return result;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
function getVendor(sessionId: string) {
|
|
409
|
+
return doRequestVendor(sessionId, getVendorById);
|
|
409
410
|
}
|
|
410
411
|
|
|
411
412
|
async function getVendorFulfillmentStatus(req: any, res: any) {
|
|
@@ -427,7 +428,7 @@ async function getVendorFulfillmentDetail(req: any, res: any) {
|
|
|
427
428
|
const { sessionId } = req.params;
|
|
428
429
|
|
|
429
430
|
try {
|
|
430
|
-
const detail = await
|
|
431
|
+
const detail = await getVendor(sessionId);
|
|
431
432
|
if (detail.code) {
|
|
432
433
|
return res.status(detail.code).json({ error: detail.error });
|
|
433
434
|
}
|
|
@@ -458,7 +459,7 @@ async function redirectToVendor(req: any, res: any) {
|
|
|
458
459
|
return res.redirect('/404');
|
|
459
460
|
}
|
|
460
461
|
|
|
461
|
-
const detail = await
|
|
462
|
+
const detail = await getVendorById(vendorId, order.order_id || '');
|
|
462
463
|
if (!detail) {
|
|
463
464
|
logger.warn('Vendor status detail not found', {
|
|
464
465
|
subscriptionId,
|
|
@@ -555,7 +556,7 @@ router.get(
|
|
|
555
556
|
);
|
|
556
557
|
|
|
557
558
|
router.get('/', getAllVendors);
|
|
558
|
-
router.get('/:id', authAdmin, validateParams(vendorIdParamSchema),
|
|
559
|
+
router.get('/:id', authAdmin, validateParams(vendorIdParamSchema), getVendorInfo);
|
|
559
560
|
router.post('/', authAdmin, createVendor);
|
|
560
561
|
router.put('/:id', authAdmin, validateParams(vendorIdParamSchema), updateVendor);
|
|
561
562
|
router.delete('/:id', authAdmin, validateParams(vendorIdParamSchema), deleteVendor);
|
package/blocklet.yml
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "payment-kit",
|
|
3
|
-
"version": "1.20.
|
|
3
|
+
"version": "1.20.19",
|
|
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,9 +56,9 @@
|
|
|
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-broker-client": "1.20.
|
|
60
|
-
"@blocklet/payment-react": "1.20.
|
|
61
|
-
"@blocklet/payment-vendor": "1.20.
|
|
59
|
+
"@blocklet/payment-broker-client": "1.20.19",
|
|
60
|
+
"@blocklet/payment-react": "1.20.19",
|
|
61
|
+
"@blocklet/payment-vendor": "1.20.19",
|
|
62
62
|
"@blocklet/sdk": "^1.16.52-beta-20250912-112002-e3499e9c",
|
|
63
63
|
"@blocklet/ui-react": "^3.1.41",
|
|
64
64
|
"@blocklet/uploader": "^0.2.12",
|
|
@@ -121,13 +121,14 @@
|
|
|
121
121
|
"ufo": "^1.6.1",
|
|
122
122
|
"umzug": "^3.8.2",
|
|
123
123
|
"use-bus": "^2.5.2",
|
|
124
|
+
"uuid": "^13.0.0",
|
|
124
125
|
"validator": "^13.15.15",
|
|
125
126
|
"web3": "^4.16.0"
|
|
126
127
|
},
|
|
127
128
|
"devDependencies": {
|
|
128
129
|
"@abtnode/types": "^1.16.52-beta-20250912-112002-e3499e9c",
|
|
129
130
|
"@arcblock/eslint-config-ts": "^0.3.3",
|
|
130
|
-
"@blocklet/payment-types": "1.20.
|
|
131
|
+
"@blocklet/payment-types": "1.20.19",
|
|
131
132
|
"@types/cookie-parser": "^1.4.9",
|
|
132
133
|
"@types/cors": "^2.8.19",
|
|
133
134
|
"@types/debug": "^4.1.12",
|
|
@@ -174,5 +175,5 @@
|
|
|
174
175
|
"parser": "typescript"
|
|
175
176
|
}
|
|
176
177
|
},
|
|
177
|
-
"gitHead": "
|
|
178
|
+
"gitHead": "13703fca3eb2151623b303605df104c4a1199741"
|
|
178
179
|
}
|
|
@@ -37,7 +37,7 @@ export default function VendorActions({ data, variant = 'compact', onChange }: V
|
|
|
37
37
|
try {
|
|
38
38
|
setState({ loading: true });
|
|
39
39
|
const newStatus = data.status === 'active' ? 'inactive' : 'active';
|
|
40
|
-
await api.put(`/api/vendors/${data.id}`, { status: newStatus }).then((res: any) => res.data);
|
|
40
|
+
await api.put(`/api/vendors/${data.id}`, { ...data, status: newStatus }).then((res: any) => res.data);
|
|
41
41
|
Toast.success(t('common.saved'));
|
|
42
42
|
onChange(state.action);
|
|
43
43
|
} catch (err) {
|
package/src/locales/en.tsx
CHANGED
|
@@ -1126,6 +1126,7 @@ export default flat({
|
|
|
1126
1126
|
nameRequired: 'Vendor name is required',
|
|
1127
1127
|
vendorType: 'Vendor Type',
|
|
1128
1128
|
vendorTypeRequired: 'Vendor type is required',
|
|
1129
|
+
didnames: 'DID Names',
|
|
1129
1130
|
launcher: 'Launcher',
|
|
1130
1131
|
vendorKey: 'Vendor Key',
|
|
1131
1132
|
vendorKeyRequired: 'Vendor key is required',
|
package/src/locales/zh.tsx
CHANGED
|
@@ -1098,7 +1098,8 @@ export default flat({
|
|
|
1098
1098
|
nameRequired: '供应商名称是必填项',
|
|
1099
1099
|
vendorType: '供应商类型',
|
|
1100
1100
|
vendorTypeRequired: '供应商类型是必填项',
|
|
1101
|
-
|
|
1101
|
+
didnames: 'DID Names',
|
|
1102
|
+
launcher: 'Launcher',
|
|
1102
1103
|
vendorKey: '供应商标识',
|
|
1103
1104
|
vendorKeyRequired: '供应商标识是必填项',
|
|
1104
1105
|
vendorKeyHelp: '供应商的唯一标识符',
|