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 CHANGED
@@ -1 +1,2 @@
1
1
  COMPONENT_STORE_URL="https://test.store.blocklet.dev"
2
+ LOG_LEVEL=debug
package/api/src/index.ts CHANGED
@@ -37,6 +37,7 @@ import { startUploadBillingInfoListener } from './queues/space';
37
37
  import { startSubscriptionQueue } from './queues/subscription';
38
38
  import { startVendorCommissionQueue } from './queues/vendors/commission';
39
39
  import { startVendorFulfillmentQueue } from './queues/vendors/fulfillment';
40
+ import { startCoordinatedFulfillmentQueue } from './queues/vendors/fulfillment-coordinator';
40
41
  import routes from './routes';
41
42
  import autoRechargeAuthorizationHandlers from './routes/connect/auto-recharge-auth';
42
43
  import changePaymentHandlers from './routes/connect/change-payment';
@@ -131,6 +132,7 @@ export const server = app.listen(port, (err?: any) => {
131
132
  startPayoutQueue().then(() => logger.info('payout queue started'));
132
133
  startVendorCommissionQueue().then(() => logger.info('vendor commission queue started'));
133
134
  startVendorFulfillmentQueue().then(() => logger.info('vendor fulfillment queue started'));
135
+ startCoordinatedFulfillmentQueue().then(() => logger.info('coordinated fulfillment queue started'));
134
136
  startCheckoutSessionQueue().then(() => logger.info('checkoutSession queue started'));
135
137
  startNotificationQueue().then(() => logger.info('notification queue started'));
136
138
  startRefundQueue().then(() => logger.info('refund queue started'));
@@ -0,0 +1,412 @@
1
+ import { VendorAuth } from '@blocklet/payment-vendor';
2
+ import { toBase58 } from '@ocap/util';
3
+ import stableStringify from 'json-stable-stringify';
4
+ import { v4 as uuidV4 } from 'uuid';
5
+
6
+ import { ProductVendor } from '../../../store/models';
7
+ import { wallet } from '../../auth';
8
+ import logger from '../../logger';
9
+ import {
10
+ CheckOrderStatusParams,
11
+ CheckOrderStatusResult,
12
+ FulfillOrderParams,
13
+ FulfillOrderResult,
14
+ ReturnRequestParams,
15
+ ReturnRequestResult,
16
+ VendorAdapter,
17
+ VendorConfig,
18
+ } from './types';
19
+ import { formatVendorUrl } from './util';
20
+
21
+ export class DidnamesAdapter implements VendorAdapter {
22
+ private vendorConfig: VendorConfig | null = null;
23
+ private vendorKey: string;
24
+
25
+ constructor(vendorInfo: VendorConfig | string) {
26
+ if (typeof vendorInfo === 'string') {
27
+ this.vendorKey = vendorInfo;
28
+ this.vendorConfig = null;
29
+ } else {
30
+ this.vendorKey = vendorInfo.id;
31
+ this.vendorConfig = vendorInfo;
32
+ }
33
+ }
34
+
35
+ /**
36
+ * Generate random subdomain for documentation site
37
+ * Format: [prefix][separator][timestamp-suffix] or pure timestamp
38
+ * Configurable length, prefix, separator with timestamp-based uniqueness
39
+ */
40
+ private generateRandomSubdomain(
41
+ options: {
42
+ totalLength?: number; // Total subdomain length (default: 9)
43
+ prefix?: string; // Prefix (default: 'doc')
44
+ usePrefix?: boolean; // Whether to use prefix (default: true)
45
+ separator?: string; // Separator between prefix and suffix (default: '-')
46
+ } = {}
47
+ ): string {
48
+ const { totalLength = 8, prefix = 'doc', usePrefix = true, separator = '-' } = options;
49
+
50
+ if (usePrefix) {
51
+ // With prefix: 'doc' + separator + timestamp suffix
52
+ const prefixWithSeparatorLength = prefix.length + separator.length;
53
+ const suffixLength = totalLength - prefixWithSeparatorLength;
54
+ if (suffixLength <= 0) {
55
+ throw new Error(
56
+ `Total length (${totalLength}) must be greater than prefix + separator length (${prefixWithSeparatorLength})`
57
+ );
58
+ }
59
+
60
+ const timestamp = Date.now();
61
+ const timeStr = timestamp.toString(36);
62
+ let suffix = timeStr.slice(-suffixLength);
63
+
64
+ // Pad with random chars if timestamp is shorter than needed
65
+ while (suffix.length < suffixLength) {
66
+ suffix = Math.floor(Math.random() * 36).toString(36) + suffix;
67
+ }
68
+
69
+ return `${prefix}${separator}${suffix}`;
70
+ }
71
+
72
+ // Pure timestamp-based without prefix
73
+ const timestamp = Date.now();
74
+ const timeStr = timestamp.toString(36);
75
+ let result = timeStr.slice(-totalLength);
76
+
77
+ // Pad with random chars if needed
78
+ while (result.length < totalLength) {
79
+ result = Math.floor(Math.random() * 36).toString(36) + result;
80
+ }
81
+
82
+ return result;
83
+ }
84
+
85
+ /**
86
+ * Generate bindDomainCap (binding capability) for domain authorization
87
+ */
88
+ private generateBindCap({ domain, checkoutSessionId }: { domain: string; checkoutSessionId: string }): any {
89
+ const now = Math.floor(Date.now() / 1000);
90
+ const expireInMinutes = 30;
91
+
92
+ const cap = {
93
+ domain,
94
+ checkoutSessionId,
95
+ nbf: now, // not before
96
+ exp: now + expireInMinutes * 60,
97
+ nonce: uuidV4(),
98
+ };
99
+
100
+ const signature = toBase58(wallet.sign(stableStringify(cap) || ''));
101
+
102
+ return {
103
+ cap,
104
+ sig: signature,
105
+ };
106
+ }
107
+
108
+ async getVendorConfig(): Promise<VendorConfig> {
109
+ if (this.vendorConfig === null) {
110
+ this.vendorConfig = await ProductVendor.findOne({ where: { vendor_key: this.vendorKey } });
111
+ if (!this.vendorConfig) {
112
+ throw new Error(`Vendor not found: ${this.vendorKey}`);
113
+ }
114
+ }
115
+ return this.vendorConfig;
116
+ }
117
+
118
+ async fulfillOrder(params: FulfillOrderParams): Promise<FulfillOrderResult> {
119
+ logger.info('Creating didnames order with domain binding', {
120
+ checkoutSessionId: params.checkoutSessionId,
121
+ productCode: params.productCode,
122
+ customerId: params.customerId,
123
+ });
124
+
125
+ const vendorConfig = await this.getVendorConfig();
126
+
127
+ try {
128
+ const rootDomain = vendorConfig.metadata?.rootDomain;
129
+ if (!rootDomain) {
130
+ throw new Error('missing required metadata in didnames vendor: rootDomain');
131
+ }
132
+
133
+ logger.info('didnames vendor rootDomain', { rootDomain });
134
+
135
+ const subdomain = this.generateRandomSubdomain({ totalLength: 8 });
136
+ const domain = `${subdomain}.${rootDomain}`;
137
+
138
+ const { checkoutSessionId } = params;
139
+ const bindDomainCap = this.generateBindCap({
140
+ domain,
141
+ checkoutSessionId,
142
+ });
143
+
144
+ params.deliveryParams.customParams = {
145
+ ...params.deliveryParams.customParams,
146
+ years: 1,
147
+ whoisPrivacy: true,
148
+ subdomain,
149
+ rootDomain,
150
+ domain,
151
+ checkoutSessionId,
152
+ bindDomainCap,
153
+ };
154
+
155
+ const orderData = {
156
+ checkoutSessionId: params.checkoutSessionId,
157
+ description: params.description,
158
+ userInfo: params.userInfo,
159
+ deliveryParams: params.deliveryParams,
160
+ };
161
+
162
+ const url = formatVendorUrl(vendorConfig, '/api/vendor/deliveries');
163
+ logger.info('submitting domain delivery to DID Names', {
164
+ subdomain,
165
+ url,
166
+ });
167
+
168
+ const { headers, body } = VendorAuth.signRequestWithHeaders(orderData);
169
+
170
+ const response = await fetch(url, {
171
+ method: 'POST',
172
+ headers,
173
+ body,
174
+ });
175
+
176
+ if (!response.ok) {
177
+ const errorBody = await response.text();
178
+ logger.error('DID Names API error', {
179
+ url,
180
+ status: response.status,
181
+ statusText: response.statusText,
182
+ body: errorBody,
183
+ });
184
+ throw new Error(`DID Names API error: ${response.status} ${response.statusText}`);
185
+ }
186
+
187
+ const didNamesResult = await response.json();
188
+
189
+ const result: FulfillOrderResult = {
190
+ orderId: didNamesResult.orderId,
191
+ status: didNamesResult.status || 'pending',
192
+ serviceUrl: `https://${subdomain}.${rootDomain}`, // User expects to access via custom domain
193
+ estimatedTime: didNamesResult.estimatedTime || 300,
194
+ message: didNamesResult.message || 'Domain binding order created successfully',
195
+ vendorOrderId: didNamesResult.orderId,
196
+ progress: didNamesResult.progress || 0,
197
+ orderDetails: {
198
+ productCode: params.productCode,
199
+ customerId: params.customerId,
200
+ amount: params.amount,
201
+ currency: params.currency,
202
+ quantity: params.quantity,
203
+ invoiceId: params.invoiceId,
204
+ customParams: {
205
+ ...params.customParams,
206
+ subdomain,
207
+ rootDomain,
208
+ domain: didNamesResult.domain || domain,
209
+ nftDid: didNamesResult.nftDid,
210
+ sessionId: params.customParams?.checkoutSessionId,
211
+ bindDomainCap,
212
+ },
213
+ },
214
+ installationInfo: {
215
+ appId: didNamesResult.appId || `app_${subdomain.replace(/\./g, '_')}`,
216
+ appUrl: `https://${subdomain}.${rootDomain}`, // Custom domain URL
217
+ adminUrl: didNamesResult.adminUrl || `https://${subdomain}.${rootDomain}/admin`,
218
+ status: didNamesResult.installationStatus || didNamesResult.status || 'installing',
219
+ estimatedCompletionTime:
220
+ didNamesResult.estimatedCompletionTime || new Date(Date.now() + 300000).toISOString(),
221
+ },
222
+ };
223
+
224
+ logger.info('Didnames order created successfully', {
225
+ orderId: result.orderId,
226
+ status: result.status,
227
+ subdomain,
228
+ serviceUrl: result.serviceUrl,
229
+ });
230
+
231
+ return result;
232
+ } catch (error: any) {
233
+ logger.error('Failed to create didnames order', {
234
+ error,
235
+ productCode: params.productCode,
236
+ customerId: params.customerId,
237
+ });
238
+
239
+ throw error;
240
+ }
241
+ }
242
+
243
+ async requestReturn(params: ReturnRequestParams): Promise<ReturnRequestResult> {
244
+ logger.info('Requesting return for Didnames order', {
245
+ orderId: params.orderId,
246
+ reason: params.reason,
247
+ });
248
+
249
+ const vendorConfig = await this.getVendorConfig();
250
+
251
+ try {
252
+ const returnRequest = {
253
+ orderId: params.orderId,
254
+ vendorOrderId: params.vendorOrderId,
255
+ reason: params.reason,
256
+ customParams: params.customParams,
257
+ };
258
+
259
+ const { headers, body } = VendorAuth.signRequestWithHeaders(returnRequest);
260
+ const url = formatVendorUrl(vendorConfig, '/api/vendor/return');
261
+ logger.info('submitting domain return to DID Names', {
262
+ url,
263
+ });
264
+ const response = await fetch(url, {
265
+ method: 'POST',
266
+ headers,
267
+ body,
268
+ });
269
+
270
+ if (!response.ok) {
271
+ const errorBody = await response.text();
272
+ logger.error('domain return to DID Names API error', {
273
+ url,
274
+ status: response.status,
275
+ statusText: response.statusText,
276
+ body: errorBody,
277
+ });
278
+ throw new Error(`DID Names API error: ${response.status} ${response.statusText}`);
279
+ }
280
+
281
+ const didNamesResult = await response.json();
282
+
283
+ logger.info('domain return to DID Names processed', {
284
+ url,
285
+ orderId: params.orderId,
286
+ status: didNamesResult.status,
287
+ });
288
+
289
+ return {
290
+ status: didNamesResult.status || 'requested',
291
+ message: didNamesResult.message || 'Domain unbinding requested',
292
+ };
293
+ } catch (error: any) {
294
+ logger.error('Failed to process return request', {
295
+ error: error.message,
296
+ orderId: params.orderId,
297
+ });
298
+
299
+ throw error;
300
+ }
301
+ }
302
+
303
+ async checkOrderStatus(params: CheckOrderStatusParams): Promise<CheckOrderStatusResult> {
304
+ logger.info('Checking Didnames order status', {
305
+ orderId: params.orderId,
306
+ });
307
+
308
+ try {
309
+ const vendorConfig = await this.getVendorConfig();
310
+ const url = formatVendorUrl(vendorConfig, `/api/vendor/orders/${params.orderId}/status`);
311
+
312
+ const response = await fetch(url, {
313
+ method: 'GET',
314
+ headers: {
315
+ 'Content-Type': 'application/json',
316
+ },
317
+ });
318
+
319
+ if (!response.ok) {
320
+ logger.warn('DID Names order status check failed', {
321
+ orderId: params.orderId,
322
+ status: response.status,
323
+ });
324
+ return { status: 'processing' };
325
+ }
326
+
327
+ const orderStatus = await response.json();
328
+ let status: 'processing' | 'completed' | 'failed' = 'processing';
329
+
330
+ switch (orderStatus.status) {
331
+ case 'completed':
332
+ case 'active':
333
+ case 'failed':
334
+ case 'rejected':
335
+ case 'error':
336
+ status = 'failed';
337
+ break;
338
+ default:
339
+ status = 'processing';
340
+ }
341
+
342
+ logger.info('Didnames order status checked', {
343
+ orderId: params.orderId,
344
+ status,
345
+ didNamesStatus: orderStatus.status,
346
+ domain: params.domain,
347
+ });
348
+
349
+ return { status };
350
+ } catch (error: any) {
351
+ logger.error('Failed to check didnames order status', {
352
+ error: error.message,
353
+ orderId: params.orderId,
354
+ });
355
+
356
+ throw error;
357
+ }
358
+ }
359
+
360
+ async getOrder(vendor: ProductVendor, orderId: string): Promise<any> {
361
+ const url = formatVendorUrl(vendor, `/api/vendor/orders/${orderId}`);
362
+
363
+ const { headers } = VendorAuth.signRequestWithHeaders({});
364
+ const response = await fetch(url, { method: 'GET', headers });
365
+
366
+ if (!response.ok) {
367
+ logger.error('Failed to get didnames order', {
368
+ url,
369
+ orderId,
370
+ status: response.status,
371
+ statusText: response.statusText,
372
+ });
373
+ throw new Error(`Failed to get order: ${response.status} ${response.statusText}`);
374
+ }
375
+
376
+ const data = await response.json();
377
+ return data;
378
+ }
379
+
380
+ async getOrderStatus(vendor: ProductVendor, orderId: string): Promise<any> {
381
+ const url = formatVendorUrl(vendor, `/api/vendor/status/${orderId}`);
382
+
383
+ logger.info('getting didnames order status', {
384
+ url,
385
+ orderId,
386
+ });
387
+
388
+ const { headers } = VendorAuth.signRequestWithHeaders({});
389
+ const response = await fetch(url, { method: 'GET', headers });
390
+
391
+ logger.info('didnames order status response', {
392
+ url,
393
+ orderId,
394
+ status: response.status,
395
+ statusText: response.statusText,
396
+ });
397
+
398
+ if (!response.ok) {
399
+ logger.error('Failed to get didnames order status', {
400
+ url,
401
+ orderId,
402
+ status: response.status,
403
+ statusText: response.statusText,
404
+ });
405
+ throw new Error(`Failed to get order status: ${response.status} ${response.statusText}`);
406
+ }
407
+
408
+ const data = await response.json();
409
+
410
+ return data;
411
+ }
412
+ }
@@ -1,4 +1,6 @@
1
1
  import { BN } from '@ocap/util';
2
+
3
+ import { DidnamesAdapter } from './didnames-adapter';
2
4
  import { LauncherAdapter } from './launcher-adapter';
3
5
  import { VendorAdapter } from './types';
4
6
  import { ProductVendor } from '../../../store/models';
@@ -26,16 +28,39 @@ export class VendorAdapterFactory {
26
28
  if (!vendorConfig) {
27
29
  throw new Error(`Vendor not found: ${vendorKey}`);
28
30
  }
31
+
29
32
  switch (vendorConfig.vendor_type) {
30
33
  case 'launcher':
31
34
  return new LauncherAdapter(vendorConfig);
35
+ case 'didnames':
36
+ return new DidnamesAdapter(vendorConfig);
32
37
  default:
33
38
  throw new Error(`Unsupported vendor: ${vendorConfig.vendor_type}`);
34
39
  }
35
40
  }
36
41
 
42
+ /**
43
+ * Create coordinated adapters for domain binding workflow
44
+ * @param vendorKeys Array of vendor keys in execution order
45
+ */
46
+ static async createCoordinated(vendorKeys: string[]): Promise<{
47
+ didnamesAdapter?: DidnamesAdapter;
48
+ launcherAdapter?: LauncherAdapter;
49
+ }> {
50
+ const adapters: { [key: string]: VendorAdapter } = {};
51
+
52
+ for (const vendorKey of vendorKeys) {
53
+ adapters[vendorKey] = await this.create(vendorKey); // eslint-disable-line no-await-in-loop
54
+ }
55
+
56
+ return {
57
+ didnamesAdapter: adapters.didnames as DidnamesAdapter,
58
+ launcherAdapter: adapters.launcher as LauncherAdapter,
59
+ };
60
+ }
61
+
37
62
  static getSupportedVendors(): string[] {
38
- return ['launcher'];
63
+ return ['launcher', 'didnames'];
39
64
  }
40
65
 
41
66
  static isSupported(vendorKey: string): boolean {
@@ -1,8 +1,9 @@
1
1
  import { Auth as VendorAuth } from '@blocklet/payment-vendor';
2
2
 
3
- import { joinURL } from 'ufo';
4
3
  import { ProductVendor } from '../../../store/models';
4
+ import dayjs from '../../dayjs';
5
5
  import logger from '../../logger';
6
+ import { formatToShortUrl } from '../../url';
6
7
  import { api } from '../../util';
7
8
  import {
8
9
  CheckOrderStatusParams,
@@ -14,6 +15,51 @@ import {
14
15
  VendorAdapter,
15
16
  VendorConfig,
16
17
  } from './types';
18
+ import { formatVendorUrl } from './util';
19
+
20
+ const doRequestVendorData = (vendor: ProductVendor, orderId: string, url: string) => {
21
+ const { headers } = VendorAuth.signRequestWithHeaders({});
22
+ const name = vendor?.name;
23
+ const key = vendor?.vendor_key;
24
+
25
+ return fetch(url, { headers })
26
+ .then(async (r) => {
27
+ const data = await r.json();
28
+ if (r.status !== 200) {
29
+ logger.error('vendor status fetch failed', {
30
+ vendorId: vendor.id,
31
+ vendorKey: vendor.vendor_key,
32
+ orderId,
33
+ status: r.status,
34
+ url,
35
+ data,
36
+ });
37
+ throw new Error(
38
+ `vendor status fetch failed, vendor: ${vendor.vendor_key}, orderId: ${orderId}, status: ${r.status}, url: ${url}`
39
+ );
40
+ }
41
+ if (!data.dashboardUrl) {
42
+ return {
43
+ ...data,
44
+ name,
45
+ key,
46
+ };
47
+ }
48
+
49
+ const validUntil = dayjs().add(20, 'minutes').format('YYYY-MM-DDTHH:mm:ss+00:00');
50
+ const maxVisits = 5;
51
+ const homeUrl = await formatToShortUrl({ url: data.homeUrl, maxVisits, validUntil });
52
+ const dashboardUrl = await formatToShortUrl({ url: data.dashboardUrl, maxVisits, validUntil });
53
+ return {
54
+ ...data,
55
+ name,
56
+ key,
57
+ homeUrl,
58
+ dashboardUrl,
59
+ };
60
+ })
61
+ .catch((e) => ({ error: e.message }));
62
+ };
17
63
 
18
64
  export class LauncherAdapter implements VendorAdapter {
19
65
  private vendorConfig: VendorConfig | null = null;
@@ -40,12 +86,12 @@ export class LauncherAdapter implements VendorAdapter {
40
86
  productCode: params.productCode,
41
87
  customerId: params.customerId,
42
88
  description: params.description,
89
+ bindCapInfo: params.deliveryParams?.customParams?.bindDomainCap ? 'present' : 'not present',
43
90
  });
44
91
 
45
92
  const vendorConfig = await this.getVendorConfig();
46
93
 
47
94
  try {
48
- const launcherApiUrl = vendorConfig.app_url;
49
95
  const orderData = {
50
96
  checkoutSessionId: params.checkoutSessionId,
51
97
  description: params.description,
@@ -53,19 +99,42 @@ export class LauncherAdapter implements VendorAdapter {
53
99
  deliveryParams: params.deliveryParams,
54
100
  };
55
101
 
102
+ // Extract bindDomainCap related params if present
103
+ const customParams = params.deliveryParams?.customParams || {};
104
+ const { domain, sessionId, bindDomainCap, domainNftDid } = customParams;
105
+
106
+ if (bindDomainCap && domain && sessionId) {
107
+ logger.info('passing domain binding authorization to Blocklet Server', {
108
+ domain,
109
+ sessionId: `${sessionId.substring(0, 8)}...`, // Log partial sessionId for security
110
+ hasBindCap: true,
111
+ });
112
+
113
+ // Ensure bindDomainCap data is passed through to Blocklet Server
114
+ orderData.deliveryParams = {
115
+ ...orderData.deliveryParams,
116
+ customParams: {
117
+ ...orderData.deliveryParams.customParams,
118
+ domain,
119
+ sessionId,
120
+ bindDomainCap,
121
+ domainNftDid,
122
+ },
123
+ };
124
+ }
125
+
56
126
  const { headers, body } = VendorAuth.signRequestWithHeaders(orderData);
57
- const response = await fetch(
58
- joinURL(launcherApiUrl, vendorConfig.metadata?.mountPoint || '', '/api/vendor/deliveries'),
59
- {
60
- method: 'POST',
61
- headers,
62
- body,
63
- }
64
- );
127
+ const url = formatVendorUrl(vendorConfig, '/api/vendor/deliveries');
128
+ const response = await fetch(url, {
129
+ method: 'POST',
130
+ headers,
131
+ body,
132
+ });
65
133
 
66
134
  if (!response.ok) {
67
135
  const errorBody = await response.text();
68
- logger.error('Launcher API error', {
136
+ logger.error('call launcher API error', {
137
+ url,
69
138
  status: response.status,
70
139
  statusText: response.statusText,
71
140
  body: errorBody,
@@ -90,7 +159,18 @@ export class LauncherAdapter implements VendorAdapter {
90
159
  currency: params.currency,
91
160
  quantity: params.quantity,
92
161
  invoiceId: params.invoiceId,
93
- customParams: params.customParams,
162
+ customParams: {
163
+ ...params.customParams,
164
+ // Include domain binding info if available
165
+ ...(customParams.bindDomainCap
166
+ ? {
167
+ domain: customParams.domain,
168
+ sessionId: customParams.sessionId,
169
+ bindDomainCap: customParams.bindDomainCap,
170
+ domainNftDid: customParams.domainNftDid,
171
+ }
172
+ : {}),
173
+ },
94
174
  },
95
175
  installationInfo: {
96
176
  appId: launcherResult.appId,
@@ -110,7 +190,7 @@ export class LauncherAdapter implements VendorAdapter {
110
190
  return result;
111
191
  } catch (error: any) {
112
192
  logger.error('Failed to create launcher order', {
113
- error: error.message,
193
+ error,
114
194
  productCode: params.productCode,
115
195
  customerId: params.customerId,
116
196
  });
@@ -126,17 +206,13 @@ export class LauncherAdapter implements VendorAdapter {
126
206
  });
127
207
 
128
208
  const vendorConfig = await this.getVendorConfig();
129
- const launcherApiUrl = vendorConfig.app_url;
130
209
  const { headers, body } = VendorAuth.signRequestWithHeaders(params);
131
210
 
132
- const response = await fetch(
133
- joinURL(launcherApiUrl, vendorConfig.metadata?.mountPoint || '', '/api/vendor/return'),
134
- {
135
- method: 'POST',
136
- headers,
137
- body,
138
- }
139
- );
211
+ const response = await fetch(formatVendorUrl(vendorConfig, '/api/vendor/return'), {
212
+ method: 'POST',
213
+ headers,
214
+ body,
215
+ });
140
216
 
141
217
  if (!response.ok) {
142
218
  const errorBody = await response.text();
@@ -178,4 +254,16 @@ export class LauncherAdapter implements VendorAdapter {
178
254
  throw error;
179
255
  }
180
256
  }
257
+
258
+ getOrderStatus(vendor: ProductVendor, orderId: string): Promise<any> {
259
+ const url = formatVendorUrl(vendor, `/api/vendor/status/${orderId}`);
260
+
261
+ return doRequestVendorData(vendor, orderId, url);
262
+ }
263
+
264
+ getOrder(vendor: ProductVendor, orderId: string): Promise<any> {
265
+ const url = formatVendorUrl(vendor, `/api/vendor/orders/${orderId}`);
266
+
267
+ return doRequestVendorData(vendor, orderId, url);
268
+ }
181
269
  }
@@ -1,3 +1,5 @@
1
+ import type { ProductVendor } from '../../../store/models';
2
+
1
3
  export interface VendorConfig {
2
4
  id: string;
3
5
  vendor_key: string;
@@ -85,4 +87,6 @@ export interface VendorAdapter {
85
87
  fulfillOrder(params: FulfillOrderParams): Promise<FulfillOrderResult>;
86
88
  requestReturn(params: ReturnRequestParams): Promise<ReturnRequestResult>;
87
89
  checkOrderStatus(params: CheckOrderStatusParams): Promise<CheckOrderStatusResult>;
90
+ getOrder(vendor: ProductVendor, orderId: string): Promise<any>;
91
+ getOrderStatus(vendor: ProductVendor, orderId: string): Promise<any>;
88
92
  }
@@ -0,0 +1,7 @@
1
+ import { joinURL } from 'ufo';
2
+
3
+ import type { VendorConfig } from './types';
4
+
5
+ export const formatVendorUrl = (vendorConfig: VendorConfig, p: string) => {
6
+ return joinURL(vendorConfig.app_url, vendorConfig.metadata?.mountPoint || '', p);
7
+ };