@payware/mcp-server 1.0.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.
Files changed (106) hide show
  1. package/.env.example +92 -0
  2. package/MCP-SETUP.md +190 -0
  3. package/README.md +483 -0
  4. package/check-keys.js +52 -0
  5. package/keys/README.md +112 -0
  6. package/mcp-config.json +9 -0
  7. package/package.json +66 -0
  8. package/payware-mcp-config.json +12 -0
  9. package/src/auth/rsa-keys.js +82 -0
  10. package/src/auth/sandbox-setup.js +187 -0
  11. package/src/config/env.js +229 -0
  12. package/src/config/partner-types.js +143 -0
  13. package/src/core/auth/jwt-factory.js +182 -0
  14. package/src/core/auth/jwt-token.js +735 -0
  15. package/src/core/utils/json-serializer.js +100 -0
  16. package/src/core/utils/key-utils.js +133 -0
  17. package/src/index.js +237 -0
  18. package/src/isv/data/index.js +291 -0
  19. package/src/isv/deep-links/delete-product-link.js +154 -0
  20. package/src/isv/deep-links/delete-transaction-link.js +154 -0
  21. package/src/isv/deep-links/get-product-link.js +167 -0
  22. package/src/isv/deep-links/get-transaction-link.js +167 -0
  23. package/src/isv/deep-links/index.js +23 -0
  24. package/src/isv/index.js +103 -0
  25. package/src/isv/oauth2/create-token-simple.js +46 -0
  26. package/src/isv/oauth2/get-token-info.js +157 -0
  27. package/src/isv/oauth2/obtain-token.js +189 -0
  28. package/src/isv/oauth2/token-helper.js +151 -0
  29. package/src/isv/products/audios.js +470 -0
  30. package/src/isv/products/create-product.js +314 -0
  31. package/src/isv/products/delete-product.js +119 -0
  32. package/src/isv/products/get-product-image.js +247 -0
  33. package/src/isv/products/get-product.js +168 -0
  34. package/src/isv/products/index.js +65 -0
  35. package/src/isv/products/list-products.js +136 -0
  36. package/src/isv/products/schedules.js +522 -0
  37. package/src/isv/products/update-product.js +265 -0
  38. package/src/isv/transactions/cancel-transaction.js +129 -0
  39. package/src/isv/transactions/create-transaction.js +361 -0
  40. package/src/isv/transactions/get-status.js +217 -0
  41. package/src/isv/transactions/process-transaction.js +145 -0
  42. package/src/isv/transactions/simulate-callback.js +146 -0
  43. package/src/isv/transactions/transaction-history.js +186 -0
  44. package/src/isv/utils/auth.js +159 -0
  45. package/src/mcp-proxy.js +150 -0
  46. package/src/merchant/data/export-report.js +108 -0
  47. package/src/merchant/data/generate-report.js +151 -0
  48. package/src/merchant/data/get-report-status.js +120 -0
  49. package/src/merchant/data/index.js +351 -0
  50. package/src/merchant/deep-links/delete-product-link.js +121 -0
  51. package/src/merchant/deep-links/delete-transaction-link.js +121 -0
  52. package/src/merchant/deep-links/get-product-link.js +134 -0
  53. package/src/merchant/deep-links/get-transaction-link.js +134 -0
  54. package/src/merchant/deep-links/index.js +23 -0
  55. package/src/merchant/index.js +92 -0
  56. package/src/merchant/products/audios.js +606 -0
  57. package/src/merchant/products/create-product.js +288 -0
  58. package/src/merchant/products/delete-product.js +111 -0
  59. package/src/merchant/products/get-product-image.js +252 -0
  60. package/src/merchant/products/get-product.js +144 -0
  61. package/src/merchant/products/index.js +65 -0
  62. package/src/merchant/products/list-products.js +112 -0
  63. package/src/merchant/products/schedules.js +646 -0
  64. package/src/merchant/products/update-product.js +282 -0
  65. package/src/merchant/transactions/cancel-transaction.js +241 -0
  66. package/src/merchant/transactions/create-transaction.js +482 -0
  67. package/src/merchant/transactions/get-status.js +196 -0
  68. package/src/merchant/transactions/process-transaction.js +349 -0
  69. package/src/merchant/transactions/simulate-callback.js +246 -0
  70. package/src/merchant/transactions/transaction-history.js +258 -0
  71. package/src/payment-institution/data/index.js +280 -0
  72. package/src/payment-institution/deep-links/delete-transaction-link.js +128 -0
  73. package/src/payment-institution/deep-links/get-transaction-link.js +141 -0
  74. package/src/payment-institution/deep-links/index.js +17 -0
  75. package/src/payment-institution/index.js +92 -0
  76. package/src/payment-institution/transactions/create-pi-transaction.js +484 -0
  77. package/src/payment-institution/transactions/finalize-transaction.js +340 -0
  78. package/src/payment-institution/transactions/get-transaction-history.js +269 -0
  79. package/src/payment-institution/transactions/get-transaction-status.js +234 -0
  80. package/src/payment-institution/transactions/process-pi-transaction.js +421 -0
  81. package/src/payment-institution/transactions/simulate-pi-callback.js +326 -0
  82. package/src/payment-institution/transactions/soundbite-transaction.js +269 -0
  83. package/src/proxy-server.js +205 -0
  84. package/src/shared/data/data-api.js +372 -0
  85. package/src/shared/deep-links/api.js +214 -0
  86. package/src/shared/index.js +44 -0
  87. package/src/shared/products/products-api.js +872 -0
  88. package/src/tools/advanced-code-generator.js +2247 -0
  89. package/src/utils/code-examples/common/auth-examples.js +1482 -0
  90. package/src/utils/code-examples/common/helpers.js +416 -0
  91. package/src/utils/code-examples/common/utils-examples.js +591 -0
  92. package/src/utils/code-examples/frameworks/examples.js +859 -0
  93. package/src/utils/code-examples/index.js +441 -0
  94. package/src/utils/code-examples/isv/oauth2.js +600 -0
  95. package/src/utils/code-examples/isv/products.js +666 -0
  96. package/src/utils/code-examples/isv/transactions.js +813 -0
  97. package/src/utils/code-examples/merchant/products.js +497 -0
  98. package/src/utils/code-examples/merchant/transactions.js +623 -0
  99. package/src/utils/code-examples/payment-institution/transactions.js +388 -0
  100. package/src/utils/code-examples/shared/data.js +607 -0
  101. package/src/utils/code-examples/shared/deep-links.js +662 -0
  102. package/src/utils/code-examples/shared/soundbites.js +808 -0
  103. package/src/utils/code-examples.js +646 -0
  104. package/src/utils/format-request.js +363 -0
  105. package/src/utils/json-formatter.js +249 -0
  106. package/src/utils/server-info.js +132 -0
@@ -0,0 +1,205 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * HTTP Proxy Server for payware MCP
5
+ * Provides HTTP/REST interface to the MCP server for external access
6
+ */
7
+
8
+ import express from 'express';
9
+ import cors from 'cors';
10
+ import { spawn } from 'child_process';
11
+ import './config/env.js'; // Load environment variables
12
+ import { fileURLToPath } from 'url';
13
+ import { dirname, join } from 'path';
14
+
15
+ const __filename = fileURLToPath(import.meta.url);
16
+ const __dirname = dirname(__filename);
17
+
18
+ class PaywareMCPProxy {
19
+ constructor(port = 3001) {
20
+ this.app = express();
21
+ this.port = port;
22
+ this.mcpProcess = null;
23
+ this.setupMiddleware();
24
+ this.setupRoutes();
25
+ }
26
+
27
+ setupMiddleware() {
28
+ // Enable CORS for all routes
29
+ this.app.use(cors({
30
+ origin: true,
31
+ credentials: true
32
+ }));
33
+
34
+ // Parse JSON requests
35
+ this.app.use(express.json());
36
+
37
+ // Add request logging
38
+ this.app.use((req, res, next) => {
39
+ console.log(`[${new Date().toISOString()}] ${req.method} ${req.path}`);
40
+ next();
41
+ });
42
+ }
43
+
44
+ setupRoutes() {
45
+ // Health check endpoint
46
+ this.app.get('/health', (req, res) => {
47
+ res.json({
48
+ status: 'healthy',
49
+ timestamp: new Date().toISOString(),
50
+ service: 'payware-mcp-proxy',
51
+ version: '1.0.0'
52
+ });
53
+ });
54
+
55
+ // List available tools
56
+ this.app.get('/tools', async (req, res) => {
57
+ try {
58
+ const result = await this.callMCPTool('tools/list', {});
59
+ res.json(result);
60
+ } catch (error) {
61
+ res.status(500).json({
62
+ error: 'Failed to list tools',
63
+ message: error.message
64
+ });
65
+ }
66
+ });
67
+
68
+ // Call a specific tool
69
+ this.app.post('/tools/:toolName', async (req, res) => {
70
+ try {
71
+ const { toolName } = req.params;
72
+ const args = req.body || {};
73
+
74
+ const result = await this.callMCPTool('tools/call', {
75
+ name: toolName,
76
+ arguments: args
77
+ });
78
+
79
+ res.json(result);
80
+ } catch (error) {
81
+ res.status(500).json({
82
+ error: `Failed to call tool ${req.params.toolName}`,
83
+ message: error.message
84
+ });
85
+ }
86
+ });
87
+
88
+ // Generic MCP endpoint
89
+ this.app.post('/mcp', async (req, res) => {
90
+ try {
91
+ const { method, params } = req.body;
92
+ const result = await this.callMCPTool(method, params);
93
+ res.json(result);
94
+ } catch (error) {
95
+ res.status(500).json({
96
+ error: 'MCP call failed',
97
+ message: error.message
98
+ });
99
+ }
100
+ });
101
+
102
+ // Serve API documentation
103
+ this.app.get('/', (req, res) => {
104
+ res.json({
105
+ name: 'payware MCP Proxy',
106
+ version: '1.0.0',
107
+ endpoints: {
108
+ 'GET /health': 'Health check',
109
+ 'GET /tools': 'List available MCP tools',
110
+ 'POST /tools/:toolName': 'Call a specific tool',
111
+ 'POST /mcp': 'Generic MCP method call'
112
+ },
113
+ examples: {
114
+ listTools: 'GET /tools',
115
+ generateKeys: 'POST /tools/generate_rsa_keys',
116
+ createJWT: 'POST /tools/create_jwt_token'
117
+ }
118
+ });
119
+ });
120
+ }
121
+
122
+ async callMCPTool(method, params = {}) {
123
+ return new Promise((resolve, reject) => {
124
+ const mcpServerPath = join(__dirname, 'index.js');
125
+ const mcpProcess = spawn('node', [mcpServerPath], {
126
+ stdio: ['pipe', 'pipe', 'pipe'],
127
+ env: process.env // Pass environment variables to spawned process
128
+ });
129
+
130
+ let responseData = '';
131
+ let errorData = '';
132
+
133
+ const request = {
134
+ jsonrpc: '2.0',
135
+ id: Date.now(),
136
+ method,
137
+ params
138
+ };
139
+
140
+ mcpProcess.stdout.on('data', (data) => {
141
+ responseData += data.toString();
142
+ });
143
+
144
+ mcpProcess.stderr.on('data', (data) => {
145
+ errorData += data.toString();
146
+ });
147
+
148
+ mcpProcess.on('close', (code) => {
149
+ if (code === 0) {
150
+ try {
151
+ // Parse the last valid JSON response
152
+ const lines = responseData.trim().split('\n');
153
+ const lastLine = lines[lines.length - 1];
154
+ const result = JSON.parse(lastLine);
155
+ resolve(result);
156
+ } catch (parseError) {
157
+ reject(new Error(`Failed to parse MCP response: ${parseError.message}`));
158
+ }
159
+ } else {
160
+ reject(new Error(`MCP process failed with code ${code}: ${errorData}`));
161
+ }
162
+ });
163
+
164
+ // Send the request to the MCP server
165
+ mcpProcess.stdin.write(JSON.stringify(request) + '\n');
166
+ mcpProcess.stdin.end();
167
+ });
168
+ }
169
+
170
+ async start() {
171
+ this.app.listen(this.port, '0.0.0.0', () => {
172
+ console.log(`🚀 payware MCP Proxy Server running on http://0.0.0.0:${this.port}`);
173
+ console.log(`📚 API Documentation: http://localhost:${this.port}`);
174
+ console.log(`🔧 Health Check: http://localhost:${this.port}/health`);
175
+ console.log(`🛠️ Tools List: http://localhost:${this.port}/tools`);
176
+ });
177
+ }
178
+
179
+ async stop() {
180
+ if (this.mcpProcess) {
181
+ this.mcpProcess.kill();
182
+ }
183
+ }
184
+ }
185
+
186
+ // Handle graceful shutdown
187
+ process.on('SIGINT', async () => {
188
+ console.log('\n🛑 Shutting down proxy server...');
189
+ process.exit(0);
190
+ });
191
+
192
+ process.on('SIGTERM', async () => {
193
+ console.log('\n🛑 Shutting down proxy server...');
194
+ process.exit(0);
195
+ });
196
+
197
+ // Start the proxy server
198
+ const port = process.env.PROXY_PORT || 3001;
199
+ const proxy = new PaywareMCPProxy(port);
200
+ proxy.start().catch(error => {
201
+ console.error('❌ Failed to start proxy server:', error);
202
+ process.exit(1);
203
+ });
204
+
205
+ export default PaywareMCPProxy;
@@ -0,0 +1,372 @@
1
+ import axios from 'axios';
2
+ import { createJWTToken } from '../../core/auth/jwt-token.js';
3
+ import { createJWTForPartner } from '../../core/auth/jwt-factory.js';
4
+ import { createMinimizedJSON } from '../../core/utils/json-serializer.js';
5
+ import { getSandboxUrl, getProductionUrl, getPartnerIdSafe, getPrivateKeySafe } from '../../config/env.js';
6
+
7
+ /**
8
+ * Available report types from the Data API documentation
9
+ */
10
+ export const REPORT_TYPES = {
11
+ // Standard reports
12
+ CURRENT_WEEK_TRANSACTIONS: '/processedTransactionsWTD',
13
+ CURRENT_MONTH_TRANSACTIONS: '/processedTransactionsMTD',
14
+ CURRENT_YEAR_TRANSACTIONS: '/processedTransactionsYTD',
15
+ PRODUCTS_LIST: '/productsList',
16
+ SHOPS_LIST: '/shopsList',
17
+ SALES_PER_PRODUCT: '/salesPerProduct',
18
+ PAYMENT_PROFILES: '/paymentProfiles',
19
+ TRANSACTIONS_PER_WEBPOS: '/transactionsPerWebPOS',
20
+ PARTNER_PROFILE: '/partnerProfile',
21
+
22
+ // Premium reports
23
+ PROCESSED_TRANSACTIONS: '/processedTransactions',
24
+ SCHEDULES_LIST: '/schedulesList',
25
+ TRANSACTIONS_PER_PRODUCT: '/processedTransactionsPerProduct'
26
+ };
27
+
28
+ /**
29
+ * Supported export formats
30
+ */
31
+ export const EXPORT_FORMATS = {
32
+ PDF: 'pdf',
33
+ CSV: 'csv',
34
+ EXCEL: 'xlsx',
35
+ JSON: 'json'
36
+ };
37
+
38
+ /**
39
+ * Supported locales
40
+ */
41
+ export const LOCALES = {
42
+ ENGLISH_US: 'en_US',
43
+ SPANISH: 'es_ES',
44
+ BULGARIAN: 'bg_BG'
45
+ };
46
+
47
+ /**
48
+ * Create authentication headers based on partner type
49
+ * @param {string} partnerType - merchant, isv, or payment_institution
50
+ * @param {Object} authParams - Authentication parameters
51
+ * @returns {Object} Headers for API request
52
+ */
53
+ async function createAuthHeaders(partnerType, authParams) {
54
+ const baseHeaders = {
55
+ 'Content-Type': 'application/json',
56
+ 'Api-Version': '1'
57
+ };
58
+
59
+ let tokenData;
60
+
61
+ switch (partnerType) {
62
+ case 'merchant':
63
+ case 'payment_institution':
64
+ // Standard JWT authentication
65
+ tokenData = createJWTToken(
66
+ authParams.partnerId,
67
+ authParams.privateKey,
68
+ authParams.requestBody
69
+ );
70
+ break;
71
+
72
+ case 'isv':
73
+ // ISV JWT with OAuth2 - note the parameter mapping
74
+ tokenData = await createJWTForPartner({
75
+ partnerId: authParams.isvPartnerId,
76
+ privateKey: authParams.privateKey,
77
+ merchantId: authParams.merchantPartnerId, // Map merchantPartnerId to merchantId
78
+ oauth2Token: authParams.oauth2Token,
79
+ requestBody: authParams.requestBody
80
+ });
81
+ break;
82
+
83
+ default:
84
+ throw new Error(`Unknown partner type: ${partnerType}`);
85
+ }
86
+
87
+ return {
88
+ ...baseHeaders,
89
+ 'Authorization': `Bearer ${tokenData.token}`
90
+ };
91
+ }
92
+
93
+ /**
94
+ * Generate a report asynchronously
95
+ */
96
+ export async function generateReport({
97
+ reportUnitId,
98
+ ignorePagination = false,
99
+ locale = LOCALES.ENGLISH_US,
100
+ dateFrom,
101
+ dateTo,
102
+ timeZone = 'GMT',
103
+ partnerType,
104
+ ...authParams
105
+ }) {
106
+ if (!reportUnitId) {
107
+ throw new Error('reportUnitId is required (e.g., "/processedTransactions")');
108
+ }
109
+
110
+ if (!Object.values(REPORT_TYPES).includes(reportUnitId)) {
111
+ console.warn(`Warning: reportUnitId "${reportUnitId}" is not in the known report types list`);
112
+ }
113
+
114
+ if (!Object.values(LOCALES).includes(locale)) {
115
+ throw new Error(`Invalid locale. Supported: ${Object.values(LOCALES).join(', ')}`);
116
+ }
117
+
118
+ const requestBody = {
119
+ reportUnitId,
120
+ ignorePagination,
121
+ locale,
122
+ timeZone
123
+ };
124
+
125
+ // Add date range if provided
126
+ if (dateFrom) requestBody.dateFrom = dateFrom;
127
+ if (dateTo) requestBody.dateTo = dateTo;
128
+
129
+ const minimizedBody = createMinimizedJSON(requestBody);
130
+ const headers = await createAuthHeaders(partnerType, { ...authParams, requestBody: minimizedBody });
131
+
132
+ try {
133
+ const response = await axios.post(
134
+ `${getProductionUrl()}/data-retrieval`,
135
+ minimizedBody,
136
+ { headers }
137
+ );
138
+
139
+ return {
140
+ success: true,
141
+ data: response.data,
142
+ requestInfo: {
143
+ url: `${getProductionUrl()}/data-retrieval`,
144
+ method: 'POST',
145
+ requestBody: minimizedBody,
146
+ statusCode: response.status
147
+ }
148
+ };
149
+ } catch (error) {
150
+ throw new Error(`Failed to generate report: ${error.response?.data?.message || error.message}`);
151
+ }
152
+ }
153
+
154
+ /**
155
+ * Get report generation status
156
+ */
157
+ export async function getReportStatus({
158
+ requestId,
159
+ partnerType,
160
+ ...authParams
161
+ }) {
162
+ if (!requestId) {
163
+ throw new Error('requestId is required (UUID from report generation)');
164
+ }
165
+
166
+ const headers = await createAuthHeaders(partnerType, authParams);
167
+
168
+ try {
169
+ const response = await axios.get(
170
+ `${getProductionUrl()}/data-retrieval/request/${requestId}/status`,
171
+ { headers }
172
+ );
173
+
174
+ return {
175
+ success: true,
176
+ data: response.data,
177
+ requestInfo: {
178
+ url: `${getProductionUrl()}/data-retrieval/request/${requestId}/status`,
179
+ method: 'GET',
180
+ statusCode: response.status
181
+ }
182
+ };
183
+ } catch (error) {
184
+ throw new Error(`Failed to get report status: ${error.response?.data?.message || error.message}`);
185
+ }
186
+ }
187
+
188
+ /**
189
+ * Export report in specific format
190
+ */
191
+ export async function exportReport({
192
+ requestId,
193
+ outputFormat = EXPORT_FORMATS.PDF,
194
+ partnerType,
195
+ ...authParams
196
+ }) {
197
+ if (!requestId) {
198
+ throw new Error('requestId is required (UUID from report generation)');
199
+ }
200
+
201
+ if (!Object.values(EXPORT_FORMATS).includes(outputFormat)) {
202
+ throw new Error(`Invalid output format. Supported: ${Object.values(EXPORT_FORMATS).join(', ')}`);
203
+ }
204
+
205
+ const requestBody = {};
206
+ const headers = await createAuthHeaders(partnerType, { ...authParams, requestBody });
207
+
208
+ try {
209
+ const response = await axios.post(
210
+ `${getProductionUrl()}/data-retrieval/request/${requestId}/export/${outputFormat}`,
211
+ requestBody,
212
+ { headers }
213
+ );
214
+
215
+ return {
216
+ success: true,
217
+ data: response.data,
218
+ requestInfo: {
219
+ url: `${getProductionUrl()}/data-retrieval/request/${requestId}/export/${outputFormat}`,
220
+ method: 'POST',
221
+ statusCode: response.status
222
+ }
223
+ };
224
+ } catch (error) {
225
+ throw new Error(`Failed to export report: ${error.response?.data?.message || error.message}`);
226
+ }
227
+ }
228
+
229
+ /**
230
+ * Get export status
231
+ */
232
+ export async function getExportStatus({
233
+ exportId,
234
+ partnerType,
235
+ ...authParams
236
+ }) {
237
+ if (!exportId) {
238
+ throw new Error('exportId is required (UUID from export request)');
239
+ }
240
+
241
+ const headers = await createAuthHeaders(partnerType, authParams);
242
+
243
+ try {
244
+ const response = await axios.get(
245
+ `${getProductionUrl()}/data-retrieval/export/${exportId}/status`,
246
+ { headers }
247
+ );
248
+
249
+ return {
250
+ success: true,
251
+ data: response.data,
252
+ requestInfo: {
253
+ url: `${getProductionUrl()}/data-retrieval/export/${exportId}/status`,
254
+ method: 'GET',
255
+ statusCode: response.status
256
+ }
257
+ };
258
+ } catch (error) {
259
+ throw new Error(`Failed to get export status: ${error.response?.data?.message || error.message}`);
260
+ }
261
+ }
262
+
263
+ /**
264
+ * Download exported report
265
+ */
266
+ export async function downloadExport({
267
+ exportId,
268
+ partnerType,
269
+ ...authParams
270
+ }) {
271
+ if (!exportId) {
272
+ throw new Error('exportId is required (UUID from export request)');
273
+ }
274
+
275
+ const headers = await createAuthHeaders(partnerType, authParams);
276
+
277
+ try {
278
+ const response = await axios.get(
279
+ `${getProductionUrl()}/data-retrieval/export/${exportId}/download`,
280
+ {
281
+ headers,
282
+ responseType: 'arraybuffer' // For binary data (PDF, Excel, etc.)
283
+ }
284
+ );
285
+
286
+ return {
287
+ success: true,
288
+ data: response.data,
289
+ contentType: response.headers['content-type'],
290
+ contentLength: response.headers['content-length'],
291
+ requestInfo: {
292
+ url: `${getProductionUrl()}/data-retrieval/export/${exportId}/download`,
293
+ method: 'GET',
294
+ statusCode: response.status
295
+ }
296
+ };
297
+ } catch (error) {
298
+ throw new Error(`Failed to download export: ${error.response?.data?.message || error.message}`);
299
+ }
300
+ }
301
+
302
+ /**
303
+ * Cancel report generation
304
+ */
305
+ export async function cancelReport({
306
+ requestId,
307
+ partnerType,
308
+ ...authParams
309
+ }) {
310
+ if (!requestId) {
311
+ throw new Error('requestId is required (UUID from report generation)');
312
+ }
313
+
314
+ const requestBody = {};
315
+ const headers = await createAuthHeaders(partnerType, { ...authParams, requestBody });
316
+
317
+ try {
318
+ const response = await axios.put(
319
+ `${getProductionUrl()}/data-retrieval/${requestId}/cancel`,
320
+ requestBody,
321
+ { headers }
322
+ );
323
+
324
+ return {
325
+ success: true,
326
+ data: response.data,
327
+ requestInfo: {
328
+ url: `${getProductionUrl()}/data-retrieval/${requestId}/cancel`,
329
+ method: 'PUT',
330
+ statusCode: response.status
331
+ }
332
+ };
333
+ } catch (error) {
334
+ throw new Error(`Failed to cancel report: ${error.response?.data?.message || error.message}`);
335
+ }
336
+ }
337
+
338
+ /**
339
+ * Cancel export
340
+ */
341
+ export async function cancelExport({
342
+ exportId,
343
+ partnerType,
344
+ ...authParams
345
+ }) {
346
+ if (!exportId) {
347
+ throw new Error('exportId is required (UUID from export request)');
348
+ }
349
+
350
+ const requestBody = {};
351
+ const headers = await createAuthHeaders(partnerType, { ...authParams, requestBody });
352
+
353
+ try {
354
+ const response = await axios.put(
355
+ `${getProductionUrl()}/data-retrieval/${exportId}/cancel`,
356
+ requestBody,
357
+ { headers }
358
+ );
359
+
360
+ return {
361
+ success: true,
362
+ data: response.data,
363
+ requestInfo: {
364
+ url: `${getProductionUrl()}/data-retrieval/${exportId}/cancel`,
365
+ method: 'PUT',
366
+ statusCode: response.status
367
+ }
368
+ };
369
+ } catch (error) {
370
+ throw new Error(`Failed to cancel export: ${error.response?.data?.message || error.message}`);
371
+ }
372
+ }