@thorprovider/medusa-extended 1.3.0 → 1.5.0
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/dist/index.d.mts +19 -1
- package/dist/index.d.ts +19 -1
- package/dist/index.js +33 -0
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +33 -0
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
- package/src/dropshipper.live-smoke.test.ts +685 -0
- package/src/dropshipper.test.ts +272 -0
- package/src/dropshipper.ts +41 -0
package/src/dropshipper.test.ts
CHANGED
|
@@ -13,6 +13,7 @@ import { describe, expect, it } from 'vitest'
|
|
|
13
13
|
import { http, HttpResponse } from 'msw'
|
|
14
14
|
import { server } from '../stelorder/client.test-helpers'
|
|
15
15
|
import { DropshipperClient } from './dropshipper'
|
|
16
|
+
import { ProviderAPIError } from '@thorprovider/adapters'
|
|
16
17
|
|
|
17
18
|
const BASE = 'https://medusa.example.com'
|
|
18
19
|
const TOKEN = 'test-jwt-token'
|
|
@@ -372,6 +373,241 @@ describe('DropshipperClient.createOrder', () => {
|
|
|
372
373
|
})
|
|
373
374
|
expect(result.order.id).toBe('ord_new')
|
|
374
375
|
})
|
|
376
|
+
|
|
377
|
+
it('POST /admin/thor/dropshipper/orders with province and city (geozone match)', async () => {
|
|
378
|
+
server.use(
|
|
379
|
+
http.post(`${BASE}/admin/thor/dropshipper/orders`, async ({ request }) => {
|
|
380
|
+
requireBearerToken(request)
|
|
381
|
+
const body = await request.json() as any
|
|
382
|
+
expect(body.shipping_address.province).toBe('La Habana')
|
|
383
|
+
expect(body.shipping_address.city).toBe('Centro Habana')
|
|
384
|
+
return HttpResponse.json({
|
|
385
|
+
order: {
|
|
386
|
+
id: 'ord_new',
|
|
387
|
+
display_id: 43,
|
|
388
|
+
status: 'pending',
|
|
389
|
+
subtotal: 4000,
|
|
390
|
+
tax_total: 0,
|
|
391
|
+
shipping_total: 1000,
|
|
392
|
+
discount_total: 0,
|
|
393
|
+
total: 5000,
|
|
394
|
+
profit: 2000,
|
|
395
|
+
created_at: '2026-01-01T00:00:00Z',
|
|
396
|
+
shipping_method: null,
|
|
397
|
+
},
|
|
398
|
+
})
|
|
399
|
+
}),
|
|
400
|
+
)
|
|
401
|
+
|
|
402
|
+
const result = await client().createOrder({
|
|
403
|
+
customer_id: 'cus_1',
|
|
404
|
+
currency_code: 'usd',
|
|
405
|
+
items: [{ variant_id: 'var_1', quantity: 1 }],
|
|
406
|
+
shipping_address: { full_name: 'John', address_1: '123 Main', city: 'Centro Habana', province: 'La Habana', postal_code: '10001', country_code: 'cu' },
|
|
407
|
+
payment_method_id: 'pm_1',
|
|
408
|
+
})
|
|
409
|
+
expect(result.order.id).toBe('ord_new')
|
|
410
|
+
})
|
|
411
|
+
|
|
412
|
+
it('POST /admin/thor/dropshipper/orders without province/city (geozone fallback)', async () => {
|
|
413
|
+
server.use(
|
|
414
|
+
http.post(`${BASE}/admin/thor/dropshipper/orders`, async ({ request }) => {
|
|
415
|
+
requireBearerToken(request)
|
|
416
|
+
const body = await request.json() as any
|
|
417
|
+
expect(body.shipping_address.province).toBeUndefined()
|
|
418
|
+
expect(body.shipping_address.city).toBe('Miami')
|
|
419
|
+
return HttpResponse.json({
|
|
420
|
+
order: {
|
|
421
|
+
id: 'ord_new',
|
|
422
|
+
display_id: 44,
|
|
423
|
+
status: 'pending',
|
|
424
|
+
subtotal: 3000,
|
|
425
|
+
tax_total: 0,
|
|
426
|
+
shipping_total: 500,
|
|
427
|
+
discount_total: 0,
|
|
428
|
+
total: 3500,
|
|
429
|
+
profit: 1500,
|
|
430
|
+
created_at: '2026-01-01T00:00:00Z',
|
|
431
|
+
shipping_method: null,
|
|
432
|
+
},
|
|
433
|
+
})
|
|
434
|
+
}),
|
|
435
|
+
)
|
|
436
|
+
|
|
437
|
+
const result = await client().createOrder({
|
|
438
|
+
customer_id: 'cus_2',
|
|
439
|
+
currency_code: 'usd',
|
|
440
|
+
items: [{ variant_id: 'var_2', quantity: 2 }],
|
|
441
|
+
shipping_address: { full_name: 'Jane', address_1: '456 Oak', city: 'Miami', postal_code: '33101', country_code: 'us' },
|
|
442
|
+
payment_method_id: 'pm_2',
|
|
443
|
+
})
|
|
444
|
+
expect(result.order.id).toBe('ord_new')
|
|
445
|
+
})
|
|
446
|
+
|
|
447
|
+
it('POST /admin/thor/dropshipper/orders with separate billing_address', async () => {
|
|
448
|
+
server.use(
|
|
449
|
+
http.post(`${BASE}/admin/thor/dropshipper/orders`, async ({ request }) => {
|
|
450
|
+
requireBearerToken(request)
|
|
451
|
+
const body = await request.json() as any
|
|
452
|
+
expect(body.billing_address).toBeDefined()
|
|
453
|
+
expect(body.billing_address.full_name).toBe('Jane Doe')
|
|
454
|
+
expect(body.billing_address.address_1).toBe('456 Main')
|
|
455
|
+
expect(body.billing_address.city).toBe('NY')
|
|
456
|
+
expect(body.billing_address.postal_code).toBe('10001')
|
|
457
|
+
expect(body.billing_address.country_code).toBe('us')
|
|
458
|
+
return HttpResponse.json({
|
|
459
|
+
order: {
|
|
460
|
+
id: 'ord_new',
|
|
461
|
+
display_id: 45,
|
|
462
|
+
status: 'pending',
|
|
463
|
+
subtotal: 5000,
|
|
464
|
+
tax_total: 0,
|
|
465
|
+
shipping_total: 1000,
|
|
466
|
+
discount_total: 0,
|
|
467
|
+
total: 6000,
|
|
468
|
+
profit: 2500,
|
|
469
|
+
created_at: '2026-01-01T00:00:00Z',
|
|
470
|
+
shipping_method: null,
|
|
471
|
+
},
|
|
472
|
+
})
|
|
473
|
+
}),
|
|
474
|
+
)
|
|
475
|
+
|
|
476
|
+
const result = await client().createOrder({
|
|
477
|
+
customer_id: 'cus_1',
|
|
478
|
+
currency_code: 'usd',
|
|
479
|
+
items: [{ variant_id: 'var_1', quantity: 1 }],
|
|
480
|
+
shipping_address: { full_name: 'John', address_1: '123 Main', city: 'NY', province: null, postal_code: '10001', country_code: 'us' },
|
|
481
|
+
billing_address: { full_name: 'Jane Doe', address_1: '456 Main', city: 'NY', province: null, postal_code: '10001', country_code: 'us' },
|
|
482
|
+
payment_method_id: 'pm_1',
|
|
483
|
+
})
|
|
484
|
+
expect(result.order.id).toBe('ord_new')
|
|
485
|
+
})
|
|
486
|
+
|
|
487
|
+
it('createOrder with dropshipper-collected payment method', async () => {
|
|
488
|
+
server.use(
|
|
489
|
+
http.post(`${BASE}/admin/thor/dropshipper/orders`, async ({ request }) => {
|
|
490
|
+
requireBearerToken(request)
|
|
491
|
+
const body = await request.json() as any
|
|
492
|
+
expect(body.payment_method_id).toBe('pm_dropshipper')
|
|
493
|
+
return HttpResponse.json({
|
|
494
|
+
order: {
|
|
495
|
+
id: 'ord_new',
|
|
496
|
+
display_id: 44,
|
|
497
|
+
status: 'pending',
|
|
498
|
+
subtotal: 4000,
|
|
499
|
+
tax_total: 0,
|
|
500
|
+
shipping_total: 1000,
|
|
501
|
+
discount_total: 0,
|
|
502
|
+
total: 5000,
|
|
503
|
+
profit: 2000,
|
|
504
|
+
created_at: '2026-01-01T00:00:00Z',
|
|
505
|
+
shipping_method: null,
|
|
506
|
+
},
|
|
507
|
+
})
|
|
508
|
+
}),
|
|
509
|
+
)
|
|
510
|
+
|
|
511
|
+
const result = await client().createOrder({
|
|
512
|
+
customer_id: 'cus_1',
|
|
513
|
+
currency_code: 'usd',
|
|
514
|
+
items: [{ variant_id: 'var_1', quantity: 1 }],
|
|
515
|
+
shipping_address: { full_name: 'John', address_1: '123 Main', city: 'NY', province: null, postal_code: '10001', country_code: 'us' },
|
|
516
|
+
payment_method_id: 'pm_dropshipper',
|
|
517
|
+
})
|
|
518
|
+
expect(result.order.id).toBe('ord_new')
|
|
519
|
+
})
|
|
520
|
+
|
|
521
|
+
it('createOrder with provider-collected payment method', async () => {
|
|
522
|
+
server.use(
|
|
523
|
+
http.post(`${BASE}/admin/thor/dropshipper/orders`, async ({ request }) => {
|
|
524
|
+
requireBearerToken(request)
|
|
525
|
+
const body = await request.json() as any
|
|
526
|
+
expect(body.payment_method_id).toBe('pm_provider')
|
|
527
|
+
return HttpResponse.json({
|
|
528
|
+
order: {
|
|
529
|
+
id: 'ord_new',
|
|
530
|
+
display_id: 45,
|
|
531
|
+
status: 'pending',
|
|
532
|
+
subtotal: 4000,
|
|
533
|
+
tax_total: 0,
|
|
534
|
+
shipping_total: 1000,
|
|
535
|
+
discount_total: 0,
|
|
536
|
+
total: 5000,
|
|
537
|
+
profit: 2000,
|
|
538
|
+
created_at: '2026-01-01T00:00:00Z',
|
|
539
|
+
shipping_method: null,
|
|
540
|
+
},
|
|
541
|
+
})
|
|
542
|
+
}),
|
|
543
|
+
)
|
|
544
|
+
|
|
545
|
+
const result = await client().createOrder({
|
|
546
|
+
customer_id: 'cus_1',
|
|
547
|
+
currency_code: 'usd',
|
|
548
|
+
items: [{ variant_id: 'var_1', quantity: 1 }],
|
|
549
|
+
shipping_address: { full_name: 'John', address_1: '123 Main', city: 'NY', province: null, postal_code: '10001', country_code: 'us' },
|
|
550
|
+
payment_method_id: 'pm_provider',
|
|
551
|
+
})
|
|
552
|
+
expect(result.order.id).toBe('ord_new')
|
|
553
|
+
})
|
|
554
|
+
|
|
555
|
+
it('throws ProviderAPIError on 422 for invalid variants', async () => {
|
|
556
|
+
server.use(
|
|
557
|
+
http.post(`${BASE}/admin/thor/dropshipper/orders`, async () => {
|
|
558
|
+
return HttpResponse.json(
|
|
559
|
+
{ message: 'Invalid variants: var_invalid_1, var_invalid_2' },
|
|
560
|
+
{ status: 422 },
|
|
561
|
+
)
|
|
562
|
+
}),
|
|
563
|
+
)
|
|
564
|
+
|
|
565
|
+
await expect(client().createOrder({
|
|
566
|
+
customer_id: 'cus_1',
|
|
567
|
+
currency_code: 'usd',
|
|
568
|
+
items: [{ variant_id: 'var_invalid_1', quantity: 1 }],
|
|
569
|
+
shipping_address: { full_name: 'John', address_1: '123 Main', city: 'NY', province: null, postal_code: '10001', country_code: 'us' },
|
|
570
|
+
payment_method_id: 'pm_1',
|
|
571
|
+
})).rejects.toThrow(ProviderAPIError)
|
|
572
|
+
})
|
|
573
|
+
|
|
574
|
+
it('throws ProviderAPIError on 404 for customer not found', async () => {
|
|
575
|
+
server.use(
|
|
576
|
+
http.post(`${BASE}/admin/thor/dropshipper/orders`, async () => {
|
|
577
|
+
return HttpResponse.json(
|
|
578
|
+
{ message: 'Customer not found' },
|
|
579
|
+
{ status: 404 },
|
|
580
|
+
)
|
|
581
|
+
}),
|
|
582
|
+
)
|
|
583
|
+
|
|
584
|
+
await expect(client().createOrder({
|
|
585
|
+
customer_id: 'cus_missing',
|
|
586
|
+
currency_code: 'usd',
|
|
587
|
+
items: [{ variant_id: 'var_1', quantity: 1 }],
|
|
588
|
+
shipping_address: { full_name: 'John', address_1: '123 Main', city: 'NY', province: null, postal_code: '10001', country_code: 'us' },
|
|
589
|
+
payment_method_id: 'pm_1',
|
|
590
|
+
})).rejects.toThrow(ProviderAPIError)
|
|
591
|
+
})
|
|
592
|
+
|
|
593
|
+
it('throws ProviderAPIError on 500 for server error', async () => {
|
|
594
|
+
server.use(
|
|
595
|
+
http.post(`${BASE}/admin/thor/dropshipper/orders`, async () => {
|
|
596
|
+
return HttpResponse.json(
|
|
597
|
+
{ message: 'Internal server error' },
|
|
598
|
+
{ status: 500 },
|
|
599
|
+
)
|
|
600
|
+
}),
|
|
601
|
+
)
|
|
602
|
+
|
|
603
|
+
await expect(client().createOrder({
|
|
604
|
+
customer_id: 'cus_1',
|
|
605
|
+
currency_code: 'usd',
|
|
606
|
+
items: [{ variant_id: 'var_1', quantity: 1 }],
|
|
607
|
+
shipping_address: { full_name: 'John', address_1: '123 Main', city: 'NY', province: null, postal_code: '10001', country_code: 'us' },
|
|
608
|
+
payment_method_id: 'pm_1',
|
|
609
|
+
})).rejects.toThrow(ProviderAPIError)
|
|
610
|
+
})
|
|
375
611
|
})
|
|
376
612
|
|
|
377
613
|
describe('DropshipperClient.getOrderShippingOptions', () => {
|
|
@@ -403,6 +639,42 @@ describe('DropshipperClient.getOrderShippingOptions', () => {
|
|
|
403
639
|
expect(result.shipping_options).toHaveLength(1)
|
|
404
640
|
expect(result.shipping_options[0].id).toBe('ship_1')
|
|
405
641
|
})
|
|
642
|
+
|
|
643
|
+
it('forwards province and city as query params', async () => {
|
|
644
|
+
server.use(
|
|
645
|
+
http.get(`${BASE}/admin/thor/dropshipper/orders/shipping-options`, ({ request }) => {
|
|
646
|
+
requireBearerToken(request)
|
|
647
|
+
const url = new URL(request.url)
|
|
648
|
+
expect(url.searchParams.get('currency_code')).toBe('usd')
|
|
649
|
+
expect(url.searchParams.get('country_code')).toBe('cu')
|
|
650
|
+
expect(url.searchParams.get('province')).toBe('La Habana')
|
|
651
|
+
expect(url.searchParams.get('city')).toBe('Centro Habana')
|
|
652
|
+
return HttpResponse.json({
|
|
653
|
+
shipping_options: [
|
|
654
|
+
{
|
|
655
|
+
id: 'ship_1',
|
|
656
|
+
name: 'La Habana Express',
|
|
657
|
+
amount: 500,
|
|
658
|
+
currency_code: 'usd',
|
|
659
|
+
price_type: 'flat',
|
|
660
|
+
is_default: true,
|
|
661
|
+
type: null,
|
|
662
|
+
},
|
|
663
|
+
],
|
|
664
|
+
count: 1,
|
|
665
|
+
})
|
|
666
|
+
}),
|
|
667
|
+
)
|
|
668
|
+
|
|
669
|
+
const result = await client().getOrderShippingOptions({
|
|
670
|
+
currency_code: 'usd',
|
|
671
|
+
country_code: 'cu',
|
|
672
|
+
province: 'La Habana',
|
|
673
|
+
city: 'Centro Habana',
|
|
674
|
+
})
|
|
675
|
+
expect(result.shipping_options).toHaveLength(1)
|
|
676
|
+
expect(result.shipping_options[0].id).toBe('ship_1')
|
|
677
|
+
})
|
|
406
678
|
})
|
|
407
679
|
|
|
408
680
|
describe('DropshipperClient.setPaymentCollector', () => {
|
package/src/dropshipper.ts
CHANGED
|
@@ -133,6 +133,10 @@ import type {
|
|
|
133
133
|
GetFinancialAnalysisResponse,
|
|
134
134
|
RegisterPaymentBody,
|
|
135
135
|
RegisterPaymentResponse,
|
|
136
|
+
// Geo-reference
|
|
137
|
+
GetGeoCountriesResponse,
|
|
138
|
+
GetGeoProvincesResponse,
|
|
139
|
+
GetGeoCitiesResponse,
|
|
136
140
|
// Dashboard Capabilities
|
|
137
141
|
DropshipperDashboardCapabilities,
|
|
138
142
|
// Inline return types
|
|
@@ -1451,6 +1455,43 @@ export class DropshipperClient {
|
|
|
1451
1455
|
'/store/thor/dropshipper-categories',
|
|
1452
1456
|
);
|
|
1453
1457
|
}
|
|
1458
|
+
|
|
1459
|
+
// -------------------------------------------------------------------------
|
|
1460
|
+
// Geo-reference
|
|
1461
|
+
// -------------------------------------------------------------------------
|
|
1462
|
+
|
|
1463
|
+
/**
|
|
1464
|
+
* List all geo-reference countries.
|
|
1465
|
+
*
|
|
1466
|
+
* `GET /admin/geo-reference`
|
|
1467
|
+
*/
|
|
1468
|
+
async getGeoCountries(): Promise<GetGeoCountriesResponse> {
|
|
1469
|
+
return this.request<GetGeoCountriesResponse>('GET', '/admin/geo-reference');
|
|
1470
|
+
}
|
|
1471
|
+
|
|
1472
|
+
/**
|
|
1473
|
+
* List provinces/states for a country.
|
|
1474
|
+
*
|
|
1475
|
+
* `GET /admin/geo-reference/countries/:iso_2/provinces`
|
|
1476
|
+
*/
|
|
1477
|
+
async getGeoProvinces(iso2: string): Promise<GetGeoProvincesResponse> {
|
|
1478
|
+
return this.request<GetGeoProvincesResponse>(
|
|
1479
|
+
'GET',
|
|
1480
|
+
`/admin/geo-reference/countries/${iso2}/provinces`,
|
|
1481
|
+
);
|
|
1482
|
+
}
|
|
1483
|
+
|
|
1484
|
+
/**
|
|
1485
|
+
* List cities for a province/state.
|
|
1486
|
+
*
|
|
1487
|
+
* `GET /admin/geo-reference/provinces/:code/cities`
|
|
1488
|
+
*/
|
|
1489
|
+
async getGeoCities(code: string): Promise<GetGeoCitiesResponse> {
|
|
1490
|
+
return this.request<GetGeoCitiesResponse>(
|
|
1491
|
+
'GET',
|
|
1492
|
+
`/admin/geo-reference/provinces/${code}/cities`,
|
|
1493
|
+
);
|
|
1494
|
+
}
|
|
1454
1495
|
}
|
|
1455
1496
|
|
|
1456
1497
|
// Re-export types used by callers so they don't need to import from @thorprovider/types directly.
|