@thorprovider/adapters 3.0.2

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 (95) hide show
  1. package/.turbo/turbo-build.log +18 -0
  2. package/.turbo/turbo-test.log +1 -0
  3. package/.turbo/turbo-type-check.log +0 -0
  4. package/AGENTS.md +1 -0
  5. package/CHANGELOG.md +271 -0
  6. package/README.md +634 -0
  7. package/dist/.tsbuildinfo +1 -0
  8. package/dist/index.d.mts +2524 -0
  9. package/dist/index.d.ts +2524 -0
  10. package/dist/index.js +8136 -0
  11. package/dist/index.js.map +1 -0
  12. package/dist/index.mjs +8045 -0
  13. package/dist/index.mjs.map +1 -0
  14. package/docs/RETRY_SOLUTION_SUMMARY.md +55 -0
  15. package/docs/authentication.md +165 -0
  16. package/docs/capabilities-system.md +143 -0
  17. package/docs/category-customization.md +309 -0
  18. package/docs/fetch-reliability-solution.md +399 -0
  19. package/docs/getting-started.md +154 -0
  20. package/docs/multi-tenant-channel-scoping.md +225 -0
  21. package/docs/profile-management.md +147 -0
  22. package/docs/providers-overview.md +85 -0
  23. package/docs/services-overview.md +135 -0
  24. package/docs/update-documentation.md +113 -0
  25. package/package.json +43 -0
  26. package/publish.log +93 -0
  27. package/quick-reference.md +169 -0
  28. package/src/admin/AdminAuthService.ts +88 -0
  29. package/src/admin/AdminSessionService.ts +91 -0
  30. package/src/admin/MetricsService.ts +145 -0
  31. package/src/admin/index.ts +13 -0
  32. package/src/backend/BackendMetadataAdapter.ts +88 -0
  33. package/src/backend/factory.ts +107 -0
  34. package/src/backend/index.ts +12 -0
  35. package/src/backend/medusa/MedusaMetadataAdapter.ts +139 -0
  36. package/src/core/errors.js +161 -0
  37. package/src/core/errors.ts +132 -0
  38. package/src/core/fetch-utils.js +240 -0
  39. package/src/core/fetch-utils.ts +227 -0
  40. package/src/core/index.ts +8 -0
  41. package/src/core/logger.js +63 -0
  42. package/src/core/logger.ts +37 -0
  43. package/src/factory/index.ts +102 -0
  44. package/src/index.ts +205 -0
  45. package/src/lib/money-transform.ts +80 -0
  46. package/src/metadata/channel-scope-validation.js +136 -0
  47. package/src/metadata/channel-scope-validation.ts +151 -0
  48. package/src/metadata/channel-scope.js +174 -0
  49. package/src/metadata/channel-scope.ts +215 -0
  50. package/src/metadata/index.js +31 -0
  51. package/src/metadata/index.ts +37 -0
  52. package/src/providers/bigcommerce/bigcommerce.ts +6 -0
  53. package/src/providers/bigcommerce/index.ts +315 -0
  54. package/src/providers/bigcommerce/transforms.ts +227 -0
  55. package/src/providers/index.ts +22 -0
  56. package/src/providers/magento/index.ts +387 -0
  57. package/src/providers/magento/magento.ts +5 -0
  58. package/src/providers/magento/queries.ts +267 -0
  59. package/src/providers/magento/transforms.ts +249 -0
  60. package/src/providers/medusa/auth.js +601 -0
  61. package/src/providers/medusa/auth.ts +496 -0
  62. package/src/providers/medusa/fulfillment-planner.ts +225 -0
  63. package/src/providers/medusa/index.js +2485 -0
  64. package/src/providers/medusa/index.ts +4239 -0
  65. package/src/providers/medusa/medusa.ts +8 -0
  66. package/src/providers/medusa/payment.js +265 -0
  67. package/src/providers/medusa/payment.ts +269 -0
  68. package/src/providers/medusa/transforms.js +505 -0
  69. package/src/providers/medusa/transforms.ts +625 -0
  70. package/src/providers/shopify/index.ts +444 -0
  71. package/src/providers/shopify/mutations.ts +102 -0
  72. package/src/providers/shopify/queries.ts +324 -0
  73. package/src/providers/shopify/shopify.ts +7 -0
  74. package/src/providers/shopify/transforms.ts +270 -0
  75. package/src/providers/spree/index.ts +344 -0
  76. package/src/providers/spree/spree.ts +5 -0
  77. package/src/providers/spree/transforms.ts +254 -0
  78. package/src/providers/woocommerce/index.ts +310 -0
  79. package/src/providers/woocommerce/transforms.ts +254 -0
  80. package/src/providers/woocommerce/woocommerce.ts +5 -0
  81. package/src/resilience/README.md +299 -0
  82. package/src/resilience/circuit-breaker.ts +222 -0
  83. package/src/resilience/index.ts +10 -0
  84. package/src/types/capabilities.types.js +141 -0
  85. package/src/types/capabilities.types.ts +366 -0
  86. package/src/types/index.ts +24 -0
  87. package/src/types/provider.js +9 -0
  88. package/src/types/provider.ts +9 -0
  89. package/src/types/ui-responses.ts +261 -0
  90. package/src/utils/city-coordinates.ts +40 -0
  91. package/src/utils/geolocation.ts +52 -0
  92. package/src/utils/payment-utils.js +152 -0
  93. package/src/utils/payment-utils.ts +203 -0
  94. package/tsconfig.json +15 -0
  95. package/tsup.config.ts +14 -0
@@ -0,0 +1,8 @@
1
+ /**
2
+ * @thorprovider/adapters v2.0
3
+ * Medusa Provider exports
4
+ */
5
+
6
+ export { MedusaProvider } from './index';
7
+ export type { MedusaProviderConfig } from './index';
8
+ export * from './transforms';
@@ -0,0 +1,265 @@
1
+ "use strict";
2
+ /**
3
+ * @thorprovider/adapters v2.0
4
+ * Medusa Payment Provider Implementation
5
+ *
6
+ * Transforms Medusa payment providers to Thor's unified PaymentMethod format.
7
+ * Handles region-specific payment provider queries.
8
+ */
9
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
10
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
11
+ return new (P || (P = Promise))(function (resolve, reject) {
12
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
13
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
14
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
15
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
16
+ });
17
+ };
18
+ var __generator = (this && this.__generator) || function (thisArg, body) {
19
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
20
+ return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
21
+ function verb(n) { return function (v) { return step([n, v]); }; }
22
+ function step(op) {
23
+ if (f) throw new TypeError("Generator is already executing.");
24
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
25
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
26
+ if (y = 0, t) op = [op[0] & 2, t.value];
27
+ switch (op[0]) {
28
+ case 0: case 1: t = op; break;
29
+ case 4: _.label++; return { value: op[1], done: false };
30
+ case 5: _.label++; y = op[1]; op = [0]; continue;
31
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
32
+ default:
33
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
34
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
35
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
36
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
37
+ if (t[2]) _.ops.pop();
38
+ _.trys.pop(); continue;
39
+ }
40
+ op = body.call(thisArg, _);
41
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
42
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
43
+ }
44
+ };
45
+ Object.defineProperty(exports, "__esModule", { value: true });
46
+ exports.transformPaymentMethod = transformPaymentMethod;
47
+ exports.getPaymentMethods = getPaymentMethods;
48
+ var errors_1 = require("../../core/errors");
49
+ /**
50
+ * Map of Medusa provider names to display names
51
+ */
52
+ var PROVIDER_DISPLAY_NAMES = {
53
+ stripe: 'Stripe',
54
+ paypal: 'PayPal',
55
+ system: 'Cash on Delivery',
56
+ manual: 'Manual Payment',
57
+ bancontact: 'Bancontact',
58
+ blik: 'BLIK',
59
+ giropay: 'giropay',
60
+ ideal: 'iDEAL',
61
+ przelewy24: 'Przelewy24',
62
+ promptpay: 'PromptPay',
63
+ oxxo: 'OXXO',
64
+ };
65
+ /**
66
+ * Infer payment method type from Medusa provider ID
67
+ *
68
+ * Medusa format: pp_{provider}_{method}
69
+ * Examples:
70
+ * - pp_stripe_stripe → 'card'
71
+ * - pp_paypal_paypal → 'wallet'
72
+ * - pp_system_default → 'cash_on_delivery'
73
+ * - pp_stripe_bancontact → 'bank_transfer'
74
+ *
75
+ * @param providerId - Medusa provider ID
76
+ * @returns Normalized payment method type
77
+ */
78
+ function inferPaymentType(providerId) {
79
+ var id = providerId.toLowerCase();
80
+ // Card payments
81
+ if (id.includes('stripe') || id.includes('card')) {
82
+ return 'card';
83
+ }
84
+ // Digital wallets
85
+ if (id.includes('paypal') ||
86
+ id.includes('wallet') ||
87
+ id.includes('apple') ||
88
+ id.includes('google')) {
89
+ return 'wallet';
90
+ }
91
+ // Bank transfers
92
+ if (id.includes('bancontact') ||
93
+ id.includes('ideal') ||
94
+ id.includes('giropay') ||
95
+ id.includes('sepa') ||
96
+ id.includes('ach') ||
97
+ id.includes('bank')) {
98
+ return 'bank_transfer';
99
+ }
100
+ // Buy now pay later
101
+ if (id.includes('klarna') ||
102
+ id.includes('affirm') ||
103
+ id.includes('afterpay') ||
104
+ id.includes('przelewy24')) {
105
+ return 'buy_now_pay_later';
106
+ }
107
+ // Cash on delivery / Manual
108
+ if (id.includes('system') ||
109
+ id.includes('manual') ||
110
+ id.includes('cod') ||
111
+ id.includes('default')) {
112
+ return 'cash_on_delivery';
113
+ }
114
+ // Crypto
115
+ if (id.includes('crypto') || id.includes('bitcoin')) {
116
+ return 'crypto';
117
+ }
118
+ return 'other';
119
+ }
120
+ /**
121
+ * Parse Medusa provider ID to extract provider and method names
122
+ *
123
+ * @param providerId - Medusa provider ID (e.g., "pp_stripe_stripe")
124
+ * @returns Object with provider and method names
125
+ */
126
+ function parseProviderId(providerId) {
127
+ // Medusa format: pp_{provider}_{method}
128
+ var match = providerId.match(/^pp_([^_]+)_(.+)$/);
129
+ if (match) {
130
+ return {
131
+ provider: match[1],
132
+ method: match[2],
133
+ };
134
+ }
135
+ // Fallback for non-standard formats
136
+ return {
137
+ provider: 'unknown',
138
+ method: providerId,
139
+ };
140
+ }
141
+ /**
142
+ * Get display name for a provider
143
+ */
144
+ function getDisplayName(providerName) {
145
+ return PROVIDER_DISPLAY_NAMES[providerName.toLowerCase()] ||
146
+ providerName.charAt(0).toUpperCase() + providerName.slice(1);
147
+ }
148
+ /**
149
+ * Transform Medusa payment provider to Thor payment method
150
+ *
151
+ * @param medusaProvider - Medusa payment provider from API
152
+ * @returns Normalized Thor PaymentMethod
153
+ */
154
+ function transformPaymentMethod(medusaProvider) {
155
+ var providerId = medusaProvider.id;
156
+ var _a = parseProviderId(providerId), provider = _a.provider, method = _a.method;
157
+ var type = inferPaymentType(providerId);
158
+ // Determine features based on provider type
159
+ var isStripe = provider === 'stripe';
160
+ var isSystemDefault = provider === 'system' || method === 'default';
161
+ var isPayPal = provider === 'paypal';
162
+ return {
163
+ id: providerId,
164
+ name: getDisplayName(provider),
165
+ type: type,
166
+ isEnabled: true, // Medusa returns only enabled providers by default
167
+ requiresSetup: !isSystemDefault, // System/manual payment requires no setup
168
+ metadata: {
169
+ providerId: provider,
170
+ methodId: method,
171
+ originalData: medusaProvider,
172
+ _backendMapping: {
173
+ backend: 'medusa',
174
+ originalId: providerId,
175
+ normalizedType: type,
176
+ },
177
+ },
178
+ features: {
179
+ // Stripe supports saving payment methods
180
+ saveForLater: isStripe,
181
+ // Stripe requires 3DS for SCA compliance
182
+ requires3DS: isStripe,
183
+ // Most providers support refunds except COD/manual
184
+ supportsRefunds: !isSystemDefault,
185
+ // Stripe has minimum amounts (varies by currency)
186
+ minAmount: isStripe ? 50 : undefined, // 50 cents minimum for Stripe
187
+ // PayPal has maximum transaction limits
188
+ maxAmount: isPayPal ? 1000000 : undefined, // $10,000 for PayPal
189
+ },
190
+ };
191
+ }
192
+ /**
193
+ * Get payment methods for Medusa
194
+ *
195
+ * Queries Medusa API for available payment providers in a region.
196
+ * Medusa requires either regionId or cartId to determine available providers.
197
+ *
198
+ * @param sdk - Medusa SDK instance
199
+ * @param options - Payment methods query options
200
+ * @returns Array of normalized payment methods
201
+ *
202
+ * @throws {Error} If neither regionId nor cartId is provided
203
+ * @throws {ProviderAPIError} If API request fails
204
+ */
205
+ function getPaymentMethods(sdk, options) {
206
+ return __awaiter(this, void 0, void 0, function () {
207
+ var regionId, cart, error_1, err, response, methods, error_2, err;
208
+ return __generator(this, function (_a) {
209
+ switch (_a.label) {
210
+ case 0:
211
+ regionId = options.regionId;
212
+ if (!(options.cartId && !regionId)) return [3 /*break*/, 4];
213
+ _a.label = 1;
214
+ case 1:
215
+ _a.trys.push([1, 3, , 4]);
216
+ return [4 /*yield*/, sdk.store.cart.retrieve(options.cartId)];
217
+ case 2:
218
+ cart = (_a.sent()).cart;
219
+ regionId = cart.region_id;
220
+ return [3 /*break*/, 4];
221
+ case 3:
222
+ error_1 = _a.sent();
223
+ err = error_1;
224
+ throw new errors_1.ProviderAPIError('Failed to fetch cart to determine region', 'medusa', err === null || err === void 0 ? void 0 : err.status);
225
+ case 4:
226
+ // Validate that we have a region ID
227
+ if (!regionId) {
228
+ throw new Error('Either regionId or cartId is required for Medusa payment methods. ' +
229
+ 'Medusa requires a region to determine available payment providers.');
230
+ }
231
+ _a.label = 5;
232
+ case 5:
233
+ _a.trys.push([5, 7, , 8]);
234
+ return [4 /*yield*/, sdk.store.payment.listPaymentProviders({
235
+ region_id: regionId,
236
+ })];
237
+ case 6:
238
+ response = _a.sent();
239
+ methods = response.payment_providers.map(transformPaymentMethod);
240
+ // Apply filters
241
+ if (options.enabledOnly !== false) { // Default to true
242
+ methods = methods.filter(function (m) { return m.isEnabled; });
243
+ }
244
+ // Filter by currency if provided
245
+ if (options.currencyCode) {
246
+ methods = methods.filter(function (m) {
247
+ var _a;
248
+ // If method specifies supported currencies, check them
249
+ if ((_a = m.features) === null || _a === void 0 ? void 0 : _a.supportedCurrencies) {
250
+ return m.features.supportedCurrencies.includes(options.currencyCode);
251
+ }
252
+ // Otherwise assume all currencies supported
253
+ return true;
254
+ });
255
+ }
256
+ return [2 /*return*/, methods];
257
+ case 7:
258
+ error_2 = _a.sent();
259
+ err = error_2;
260
+ throw new errors_1.ProviderAPIError('Failed to fetch payment providers from Medusa', 'medusa', err === null || err === void 0 ? void 0 : err.status);
261
+ case 8: return [2 /*return*/];
262
+ }
263
+ });
264
+ });
265
+ }
@@ -0,0 +1,269 @@
1
+ /**
2
+ * @thorprovider/adapters v2.0
3
+ * Medusa Payment Provider Implementation
4
+ *
5
+ * Transforms Medusa payment providers to Thor's unified PaymentMethod format.
6
+ * Handles region-specific payment provider queries.
7
+ */
8
+
9
+ import type Medusa from '@medusajs/js-sdk';
10
+ import type { HttpTypes } from '@medusajs/types';
11
+ import type {
12
+ PaymentMethod,
13
+ PaymentMethodsOptions,
14
+ PaymentMethodType
15
+ } from '@thorprovider/types';
16
+ import { ProviderAPIError } from '../../core/errors';
17
+
18
+ /**
19
+ * Map of Medusa provider names to display names
20
+ */
21
+ const PROVIDER_DISPLAY_NAMES: Record<string, string> = {
22
+ stripe: 'Stripe',
23
+ paypal: 'PayPal',
24
+ system: 'Cash on Delivery',
25
+ manual: 'Manual Payment',
26
+ bancontact: 'Bancontact',
27
+ blik: 'BLIK',
28
+ giropay: 'giropay',
29
+ ideal: 'iDEAL',
30
+ przelewy24: 'Przelewy24',
31
+ promptpay: 'PromptPay',
32
+ oxxo: 'OXXO',
33
+ };
34
+
35
+ /**
36
+ * Infer payment method type from Medusa provider ID
37
+ *
38
+ * Medusa format: pp_{provider}_{method}
39
+ * Examples:
40
+ * - pp_stripe_stripe → 'card'
41
+ * - pp_paypal_paypal → 'wallet'
42
+ * - pp_system_default → 'cash_on_delivery'
43
+ * - pp_stripe_bancontact → 'bank_transfer'
44
+ *
45
+ * @param providerId - Medusa provider ID
46
+ * @returns Normalized payment method type
47
+ */
48
+ function inferPaymentType(providerId: string): PaymentMethodType {
49
+ const id = providerId.toLowerCase();
50
+
51
+ // Card payments
52
+ if (id.includes('stripe') || id.includes('card')) {
53
+ return 'card';
54
+ }
55
+
56
+ // Digital wallets
57
+ if (
58
+ id.includes('paypal') ||
59
+ id.includes('wallet') ||
60
+ id.includes('apple') ||
61
+ id.includes('google')
62
+ ) {
63
+ return 'wallet';
64
+ }
65
+
66
+ // Bank transfers
67
+ if (
68
+ id.includes('bancontact') ||
69
+ id.includes('ideal') ||
70
+ id.includes('giropay') ||
71
+ id.includes('sepa') ||
72
+ id.includes('ach') ||
73
+ id.includes('bank')
74
+ ) {
75
+ return 'bank_transfer';
76
+ }
77
+
78
+ // Buy now pay later
79
+ if (
80
+ id.includes('klarna') ||
81
+ id.includes('affirm') ||
82
+ id.includes('afterpay') ||
83
+ id.includes('przelewy24')
84
+ ) {
85
+ return 'buy_now_pay_later';
86
+ }
87
+
88
+ // Cash on delivery / Manual
89
+ if (
90
+ id.includes('system') ||
91
+ id.includes('manual') ||
92
+ id.includes('cod') ||
93
+ id.includes('default')
94
+ ) {
95
+ return 'cash_on_delivery';
96
+ }
97
+
98
+ // Crypto
99
+ if (id.includes('crypto') || id.includes('bitcoin')) {
100
+ return 'crypto';
101
+ }
102
+
103
+ return 'other';
104
+ }
105
+
106
+ /**
107
+ * Parse Medusa provider ID to extract provider and method names
108
+ *
109
+ * @param providerId - Medusa provider ID (e.g., "pp_stripe_stripe")
110
+ * @returns Object with provider and method names
111
+ */
112
+ function parseProviderId(providerId: string): { provider: string; method: string } {
113
+ // Medusa format: pp_{provider}_{method}
114
+ const match = providerId.match(/^pp_([^_]+)_(.+)$/);
115
+
116
+ if (match) {
117
+ return {
118
+ provider: match[1],
119
+ method: match[2],
120
+ };
121
+ }
122
+
123
+ // Fallback for non-standard formats
124
+ return {
125
+ provider: 'unknown',
126
+ method: providerId,
127
+ };
128
+ }
129
+
130
+ /**
131
+ * Get display name for a provider
132
+ */
133
+ function getDisplayName(providerName: string): string {
134
+ return PROVIDER_DISPLAY_NAMES[providerName.toLowerCase()] ||
135
+ providerName.charAt(0).toUpperCase() + providerName.slice(1);
136
+ }
137
+
138
+ /**
139
+ * Transform Medusa payment provider to Thor payment method
140
+ *
141
+ * @param medusaProvider - Medusa payment provider from API
142
+ * @returns Normalized Thor PaymentMethod
143
+ */
144
+ export function transformPaymentMethod(
145
+ medusaProvider: HttpTypes.StorePaymentProvider
146
+ ): PaymentMethod {
147
+ const providerId = medusaProvider.id;
148
+ const { provider, method } = parseProviderId(providerId);
149
+ const type = inferPaymentType(providerId);
150
+
151
+ // Determine features based on provider type
152
+ const isStripe = provider === 'stripe';
153
+ const isSystemDefault = provider === 'system' || method === 'default';
154
+ const isPayPal = provider === 'paypal';
155
+
156
+ return {
157
+ id: providerId,
158
+ name: getDisplayName(provider),
159
+ type,
160
+ isEnabled: true, // Medusa returns only enabled providers by default
161
+ requiresSetup: !isSystemDefault, // System/manual payment requires no setup
162
+ metadata: {
163
+ providerId: provider,
164
+ methodId: method,
165
+ originalData: medusaProvider,
166
+ _backendMapping: {
167
+ backend: 'medusa',
168
+ originalId: providerId,
169
+ normalizedType: type,
170
+ },
171
+ },
172
+ features: {
173
+ // Stripe supports saving payment methods
174
+ saveForLater: isStripe,
175
+
176
+ // Stripe requires 3DS for SCA compliance
177
+ requires3DS: isStripe,
178
+
179
+ // Most providers support refunds except COD/manual
180
+ supportsRefunds: !isSystemDefault,
181
+
182
+ // Stripe has minimum amounts (varies by currency)
183
+ minAmount: isStripe ? 50 : undefined, // 50 cents minimum for Stripe
184
+
185
+ // PayPal has maximum transaction limits
186
+ maxAmount: isPayPal ? 1000000 : undefined, // $10,000 for PayPal
187
+ },
188
+ };
189
+ }
190
+
191
+ /**
192
+ * Get payment methods for Medusa
193
+ *
194
+ * Queries Medusa API for available payment providers in a region.
195
+ * Medusa requires either regionId or cartId to determine available providers.
196
+ *
197
+ * @param sdk - Medusa SDK instance
198
+ * @param options - Payment methods query options
199
+ * @returns Array of normalized payment methods
200
+ *
201
+ * @throws {Error} If neither regionId nor cartId is provided
202
+ * @throws {ProviderAPIError} If API request fails
203
+ */
204
+ export async function getPaymentMethods(
205
+ sdk: Medusa,
206
+ options: PaymentMethodsOptions
207
+ ): Promise<PaymentMethod[]> {
208
+ // Medusa requires region_id to list payment providers
209
+ let regionId = options.regionId;
210
+
211
+ // If cart ID is provided but no region, fetch cart to get region
212
+ if (options.cartId && !regionId) {
213
+ try {
214
+ const { cart } = await sdk.store.cart.retrieve(options.cartId);
215
+ regionId = cart.region_id;
216
+ } catch (error) {
217
+ const err = error as any;
218
+ throw new ProviderAPIError(
219
+ 'Failed to fetch cart to determine region',
220
+ 'medusa',
221
+ err?.status
222
+ );
223
+ }
224
+ }
225
+
226
+ // Validate that we have a region ID
227
+ if (!regionId) {
228
+ throw new Error(
229
+ 'Either regionId or cartId is required for Medusa payment methods. ' +
230
+ 'Medusa requires a region to determine available payment providers.'
231
+ );
232
+ }
233
+
234
+ try {
235
+ // Query Medusa API for payment providers in this region
236
+ const response = await sdk.store.payment.listPaymentProviders({
237
+ region_id: regionId,
238
+ });
239
+
240
+ // Transform to Thor format
241
+ let methods = response.payment_providers.map(transformPaymentMethod);
242
+
243
+ // Apply filters
244
+ if (options.enabledOnly !== false) { // Default to true
245
+ methods = methods.filter(m => m.isEnabled);
246
+ }
247
+
248
+ // Filter by currency if provided
249
+ if (options.currencyCode) {
250
+ methods = methods.filter(m => {
251
+ // If method specifies supported currencies, check them
252
+ if (m.features?.supportedCurrencies) {
253
+ return m.features.supportedCurrencies.includes(options.currencyCode!);
254
+ }
255
+ // Otherwise assume all currencies supported
256
+ return true;
257
+ });
258
+ }
259
+
260
+ return methods;
261
+ } catch (error) {
262
+ const err = error as any;
263
+ throw new ProviderAPIError(
264
+ 'Failed to fetch payment providers from Medusa',
265
+ 'medusa',
266
+ err?.status
267
+ );
268
+ }
269
+ }