@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,808 @@
1
+ /**
2
+ * Soundbite operations examples (shared across all partner types)
3
+ */
4
+
5
+ import { ExampleGenerator, CommonTemplates } from '../common/helpers.js';
6
+
7
+ /**
8
+ * Soundbite operations for audio content and payment integration
9
+ */
10
+ export const SoundbiteOperations = {
11
+ register_audio: {
12
+ description: 'Register audio content for soundbite transactions',
13
+ endpoint: '/products/{id}/audios/upload',
14
+ method: 'POST',
15
+ contentType: 'multipart/form-data',
16
+ sampleBody: {
17
+ file: '@/path/to/audio.mp3'
18
+ }
19
+ },
20
+
21
+ get_audio: {
22
+ description: 'Get audio content details',
23
+ endpoint: '/products/{id}/audios/{audioId}',
24
+ method: 'GET',
25
+ sampleBody: null
26
+ },
27
+
28
+ update_audio: {
29
+ description: 'Update audio content metadata',
30
+ endpoint: '/products/{id}/audios/{audioId}',
31
+ method: 'PATCH',
32
+ sampleBody: {
33
+ title: 'Updated Audio Title',
34
+ productId: 'pr_new_product_123'
35
+ }
36
+ },
37
+
38
+ delete_audio: {
39
+ description: 'Delete audio content',
40
+ endpoint: '/products/{id}/audios/{audioId}',
41
+ method: 'DELETE',
42
+ sampleBody: null
43
+ },
44
+
45
+ list_audios: {
46
+ description: 'List audio content for product',
47
+ endpoint: '/products/{id}/audios',
48
+ method: 'GET',
49
+ sampleBody: null,
50
+ queryParams: {
51
+ format: 'MP3',
52
+ quality: 'HIGH',
53
+ limit: 20,
54
+ offset: 0
55
+ }
56
+ },
57
+
58
+ create_soundbite_transaction: {
59
+ description: 'Create transaction for soundbite purchase',
60
+ endpoint: '/transactions/soundbite',
61
+ method: 'POST',
62
+ sampleBody: {
63
+ trData: {
64
+ amount: '2.99',
65
+ currency: 'EUR',
66
+ reasonL1: 'Soundbite Purchase',
67
+ reasonL2: 'Premium audio content'
68
+ },
69
+ soundbiteData: {
70
+ productId: 'pr_audio_123',
71
+ audioId: 'aud_content_456',
72
+ accessType: 'STREAM_ONLY',
73
+ duration: 180,
74
+ quality: 'HIGH'
75
+ },
76
+ trOptions: {
77
+ type: 'SOUNDBITE',
78
+ timeToLive: 300,
79
+ autoComplete: true,
80
+ deliveryMethod: 'INSTANT_STREAM'
81
+ }
82
+ }
83
+ },
84
+
85
+ stream_audio: {
86
+ description: 'Stream audio content after purchase',
87
+ endpoint: '/soundbites/{id}/stream',
88
+ method: 'GET',
89
+ sampleBody: null,
90
+ queryParams: {
91
+ quality: 'HIGH',
92
+ format: 'MP3',
93
+ startTime: 0,
94
+ duration: 180
95
+ },
96
+ responseType: 'audio'
97
+ },
98
+
99
+ download_audio: {
100
+ description: 'Download purchased audio content',
101
+ endpoint: '/soundbites/{id}/download',
102
+ method: 'GET',
103
+ sampleBody: null,
104
+ queryParams: {
105
+ quality: 'HIGHEST',
106
+ format: 'FLAC'
107
+ },
108
+ responseType: 'file'
109
+ },
110
+
111
+ get_soundbite_analytics: {
112
+ description: 'Get analytics for soundbite transactions',
113
+ endpoint: '/soundbites/analytics',
114
+ method: 'GET',
115
+ sampleBody: null,
116
+ queryParams: {
117
+ period: '30d',
118
+ metrics: 'purchases,streams,revenue',
119
+ productId: 'pr_audio_123',
120
+ groupBy: 'day'
121
+ }
122
+ },
123
+
124
+ create_audio_playlist: {
125
+ description: 'Create playlist from multiple audio contents',
126
+ endpoint: '/soundbites/playlists',
127
+ method: 'POST',
128
+ sampleBody: {
129
+ playlistData: {
130
+ name: 'Premium Collection',
131
+ description: 'Curated premium audio content',
132
+ isPublic: false,
133
+ category: 'PREMIUM'
134
+ },
135
+ audioItems: [
136
+ {
137
+ audioId: 'aud_content_001',
138
+ position: 1,
139
+ startTime: 0,
140
+ endTime: 180
141
+ },
142
+ {
143
+ audioId: 'aud_content_002',
144
+ position: 2,
145
+ startTime: 30,
146
+ endTime: 210
147
+ }
148
+ ],
149
+ playlistOptions: {
150
+ shuffle: false,
151
+ repeat: false,
152
+ autoPlay: true,
153
+ crossfade: 2
154
+ }
155
+ }
156
+ },
157
+
158
+ get_audio_preview: {
159
+ description: 'Get preview of audio content before purchase',
160
+ endpoint: '/products/{id}/audios/{audioId}/preview',
161
+ method: 'GET',
162
+ sampleBody: null,
163
+ queryParams: {
164
+ duration: 30,
165
+ startTime: 60,
166
+ quality: 'MEDIUM'
167
+ },
168
+ responseType: 'audio'
169
+ }
170
+ };
171
+
172
+ /**
173
+ * Python Soundbites Generator
174
+ */
175
+ export class PythonSoundbitesGenerator extends ExampleGenerator {
176
+ constructor() {
177
+ super('python');
178
+ }
179
+
180
+ getOperationTemplate(operation, partnerType, params = {}) {
181
+ const opConfig = SoundbiteOperations[operation];
182
+ if (!opConfig) {
183
+ throw new Error(`Unknown operation: ${operation}`);
184
+ }
185
+
186
+ const {
187
+ productId = 'pr_audio_123',
188
+ audioId = 'aud_content_456',
189
+ soundbiteId = 'sb_transaction_789',
190
+ playlistId = 'pl_collection_012',
191
+ ...otherParams
192
+ } = params;
193
+
194
+ let endpoint = opConfig.endpoint;
195
+ endpoint = endpoint.replace('{id}', productId);
196
+ endpoint = endpoint.replace('{audioId}', audioId);
197
+ endpoint = endpoint.replace('{soundbiteId}', soundbiteId);
198
+ endpoint = endpoint.replace('{playlistId}', playlistId);
199
+
200
+ const functionName = `${operation}_example`;
201
+
202
+ return `def ${functionName}(${this.getFunctionParams(operation, productId, audioId, soundbiteId)}, use_sandbox=True):
203
+ """${opConfig.description}"""
204
+
205
+ try:
206
+ # Get API configuration
207
+ base_url = get_api_base_url(use_sandbox)
208
+ endpoint = '${endpoint}'
209
+ url = f"{base_url}{endpoint}"
210
+
211
+ ${this.getQueryParamsSection(operation, opConfig)}
212
+ ${this.getRequestBodySection(operation, opConfig)}
213
+
214
+ # Create JWT token
215
+ token, body_string = create_jwt_token(request_body, use_sandbox)
216
+ headers = get_api_headers(token)
217
+
218
+ ${this.getSpecialHandling(operation, opConfig)}
219
+
220
+ ${this.getRequestSection(opConfig.method, opConfig.responseType)}
221
+
222
+ ${this.getResponseHandling(operation, opConfig)}
223
+
224
+ except requests.exceptions.RequestException as e:
225
+ print(f"Request error: {str(e)}")
226
+ return None
227
+ except Exception as e:
228
+ print(f"Unexpected error: {str(e)}")
229
+ return None
230
+
231
+ def validate_audio_format(audio_data):
232
+ """Validate audio format and specifications"""
233
+
234
+ supported_formats = ['MP3', 'FLAC', 'WAV', 'AAC']
235
+ format_type = audio_data.get('format', '').upper()
236
+
237
+ if format_type not in supported_formats:
238
+ print(f"Warning: Unsupported audio format: {format_type}")
239
+ return False
240
+
241
+ duration = audio_data.get('duration', 0)
242
+ if duration <= 0 or duration > 3600: # Max 1 hour
243
+ print(f"Warning: Invalid audio duration: {duration} seconds")
244
+ return False
245
+
246
+ bitrate = audio_data.get('bitrate', 0)
247
+ if bitrate < 64 or bitrate > 1411: # 64kbps to 1411kbps (FLAC)
248
+ print(f"Warning: Invalid bitrate: {bitrate} kbps")
249
+ return False
250
+
251
+ return True
252
+
253
+ def save_audio_file(audio_data, filename=None):
254
+ """Save streamed audio data to file"""
255
+
256
+ if not filename:
257
+ timestamp = int(time.time())
258
+ filename = f"soundbite_{timestamp}.mp3"
259
+
260
+ try:
261
+ with open(filename, 'wb') as f:
262
+ for chunk in audio_data.iter_content(chunk_size=8192):
263
+ f.write(chunk)
264
+
265
+ print(f"Audio saved as: {filename}")
266
+ return filename
267
+ except Exception as e:
268
+ print(f"Error saving audio: {str(e)}")
269
+ return None
270
+
271
+ def calculate_audio_price(duration, quality, format_type):
272
+ """Calculate price for audio content based on specifications"""
273
+
274
+ base_price = 0.99 # Base price in EUR
275
+ duration_multiplier = max(1, duration / 60) # Per minute
276
+ quality_multiplier = {'LOW': 0.8, 'MEDIUM': 1.0, 'HIGH': 1.3, 'HIGHEST': 1.6}
277
+ format_multiplier = {'MP3': 1.0, 'FLAC': 1.5, 'WAV': 1.3, 'AAC': 1.1}
278
+
279
+ total_price = (base_price *
280
+ duration_multiplier *
281
+ quality_multiplier.get(quality, 1.0) *
282
+ format_multiplier.get(format_type, 1.0))
283
+
284
+ return round(total_price, 2)
285
+
286
+ # Example usage
287
+ if __name__ == "__main__":
288
+ print("Soundbites Example")
289
+
290
+ # ${opConfig.description}
291
+ result = ${functionName}()
292
+
293
+ if result:
294
+ print("Soundbite operation completed successfully")
295
+ ${this.getUsageExample(operation)}
296
+ else:
297
+ print("Soundbite operation failed")`;
298
+ }
299
+
300
+ getFunctionParams(operation, productId, audioId, soundbiteId) {
301
+ const params = [];
302
+
303
+ if (operation.includes('audio') || operation.includes('register') || operation.includes('list')) {
304
+ params.push(`product_id='${productId}'`);
305
+ if (!operation.includes('list') && !operation.includes('register')) {
306
+ params.push(`audio_id='${audioId}'`);
307
+ }
308
+ } else if (operation.includes('soundbite') || operation.includes('stream') || operation.includes('download')) {
309
+ params.push(`soundbite_id='${soundbiteId}'`);
310
+ }
311
+
312
+ return params.join(', ');
313
+ }
314
+
315
+ getQueryParamsSection(operation, opConfig) {
316
+ if (!opConfig.queryParams) {
317
+ return '';
318
+ }
319
+
320
+ const paramsStr = Object.entries(opConfig.queryParams)
321
+ .map(([key, value]) => ` '${key}': ${typeof value === 'string' ? `'${value}'` : value}`)
322
+ .join(',\n');
323
+
324
+ return `# Add query parameters
325
+ params = {
326
+ ${paramsStr}
327
+ }
328
+ if params:
329
+ url += '?' + '&'.join([f"{k}={v}" for k, v in params.items()])`;
330
+ }
331
+
332
+ getRequestBodySection(operation, opConfig) {
333
+ if (!opConfig.sampleBody) {
334
+ return '# No request body needed for this operation\n request_body = None';
335
+ }
336
+
337
+ const bodyStr = JSON.stringify(opConfig.sampleBody, null, 8).replace(/^/gm, ' ');
338
+
339
+ let validation = '';
340
+ if (operation === 'register_audio') {
341
+ validation = `
342
+ # Validate audio specifications
343
+ if 'audioData' in request_body:
344
+ if not validate_audio_format(request_body['audioData']):
345
+ print("Warning: Audio validation failed")`;
346
+ }
347
+
348
+ return `# Prepare request body
349
+ request_body = ${bodyStr}${validation}`;
350
+ }
351
+
352
+ getSpecialHandling(operation, opConfig) {
353
+ if (opConfig.responseType === 'audio' || opConfig.responseType === 'file') {
354
+ return `# Set headers for audio/file response
355
+ headers['Accept'] = 'audio/*' if '${opConfig.responseType}' == 'audio' else 'application/octet-stream'`;
356
+ }
357
+ return '';
358
+ }
359
+
360
+ getRequestSection(method, responseType) {
361
+ if (method === 'GET') {
362
+ if (responseType === 'audio' || responseType === 'file') {
363
+ return 'response = requests.get(url, headers=headers, stream=True)';
364
+ }
365
+ return 'response = requests.get(url, headers=headers)';
366
+ } else if (method === 'DELETE') {
367
+ return 'response = requests.delete(url, headers=headers, data=body_string if request_body else None)';
368
+ } else {
369
+ return `response = requests.${method.toLowerCase()}(
370
+ url,
371
+ headers=headers,
372
+ data=body_string if request_body else None
373
+ )`;
374
+ }
375
+ }
376
+
377
+ getResponseHandling(operation, opConfig) {
378
+ if (opConfig.responseType === 'audio' || opConfig.responseType === 'file') {
379
+ return `if response.status_code == 200:
380
+ # Handle audio/file download
381
+ content_type = response.headers.get('content-type', '')
382
+ file_extension = 'mp3' if 'audio' in content_type else 'dat'
383
+ filename = f"{operation}_{soundbite_id if 'soundbite' in locals() else audio_id}.{file_extension}"
384
+
385
+ saved_file = save_audio_file(response, filename)
386
+ if saved_file:
387
+ print(f"${opConfig.description} successful - file saved: {saved_file}")
388
+ return {'filename': saved_file, 'content_type': content_type}
389
+ else:
390
+ print("Failed to save audio file")
391
+ return None
392
+ else:
393
+ error_msg = f"Request failed with status {response.status_code}: {response.text}"
394
+ print(error_msg)
395
+ return None`;
396
+ } else {
397
+ return `if response.status_code == 200:
398
+ result = response.json()
399
+ print(f"${opConfig.description} successful:")
400
+ print(json.dumps(result, indent=2))
401
+ return result
402
+ else:
403
+ error_msg = f"Request failed with status {response.status_code}: {response.text}"
404
+ print(error_msg)
405
+ return None`;
406
+ }
407
+ }
408
+
409
+ getUsageExample(operation) {
410
+ const examples = {
411
+ register_audio: `# Store audio ID for transactions
412
+ if 'audioId' in result:
413
+ audio_id = result['audioId']
414
+ print(f"Audio registered: {audio_id}")
415
+ print("Use create_soundbite_transaction_example() to enable purchases")`,
416
+
417
+ create_soundbite_transaction: `# Monitor transaction and enable access
418
+ if 'transactionId' in result:
419
+ transaction_id = result['transactionId']
420
+ soundbite_id = result.get('soundbiteId')
421
+ print(f"Soundbite transaction: {transaction_id}")
422
+ if soundbite_id:
423
+ print(f"Access with: stream_audio_example('{soundbite_id}')")`,
424
+
425
+ stream_audio: `# Audio streaming started
426
+ if 'filename' in result:
427
+ print(f"Audio streamed and saved: {result['filename']}")
428
+ print("Playback can begin immediately")`,
429
+
430
+ get_soundbite_analytics: `# Display analytics insights
431
+ analytics = result.get('analytics', {})
432
+ period = result.get('period')
433
+
434
+ print(f"Soundbite Analytics for {period}:")
435
+ for metric, data in analytics.items():
436
+ if isinstance(data, dict):
437
+ print(f"- {metric}:")
438
+ for key, value in data.items():
439
+ print(f" - {key}: {value}")
440
+ else:
441
+ print(f"- {metric}: {data}")
442
+
443
+ # Calculate revenue per stream
444
+ if 'purchases' in analytics and 'revenue' in analytics:
445
+ purchases = analytics['purchases']
446
+ revenue = analytics['revenue']
447
+ if purchases > 0:
448
+ avg_revenue = revenue / purchases
449
+ print(f"\\nAverage revenue per purchase: €{avg_revenue:.2f}")`
450
+ };
451
+
452
+ return examples[operation] || '# Process soundbite result as needed';
453
+ }
454
+ }
455
+
456
+ /**
457
+ * Node.js Soundbites Generator
458
+ */
459
+ export class NodeJSSoundbitesGenerator extends ExampleGenerator {
460
+ constructor() {
461
+ super('nodejs');
462
+ }
463
+
464
+ getOperationTemplate(operation, partnerType, params = {}) {
465
+ const opConfig = SoundbiteOperations[operation];
466
+ if (!opConfig) {
467
+ throw new Error(`Unknown operation: ${operation}`);
468
+ }
469
+
470
+ const {
471
+ productId = 'pr_audio_123',
472
+ audioId = 'aud_content_456',
473
+ soundbiteId = 'sb_transaction_789',
474
+ playlistId = 'pl_collection_012',
475
+ ...otherParams
476
+ } = params;
477
+
478
+ let endpoint = opConfig.endpoint;
479
+ endpoint = endpoint.replace('{id}', productId);
480
+ endpoint = endpoint.replace('{audioId}', audioId);
481
+ endpoint = endpoint.replace('{soundbiteId}', soundbiteId);
482
+ endpoint = endpoint.replace('{playlistId}', playlistId);
483
+
484
+ const functionName = `${operation}Example`;
485
+
486
+ return `async function ${functionName}(${this.getFunctionParams(operation, productId, audioId, soundbiteId)}, useSandbox = true) {
487
+ /**
488
+ * ${opConfig.description}
489
+ */
490
+
491
+ try {
492
+ // Get API configuration
493
+ const baseUrl = getAPIBaseURL(useSandbox);
494
+ const endpoint = '${endpoint}';
495
+ let url = \`\${baseUrl}\${endpoint}\`;
496
+
497
+ ${this.getQueryParamsSection(operation, opConfig)}
498
+ ${this.getRequestBodySection(operation, opConfig)}
499
+
500
+ // Create JWT token
501
+ const { token, bodyString } = createJWTToken(requestBody, useSandbox);
502
+ const headers = getAPIHeaders(token);
503
+
504
+ ${this.getSpecialHandling(operation, opConfig)}
505
+
506
+ ${this.getRequestSection(opConfig.method, opConfig.responseType)}
507
+
508
+ ${this.getResponseHandling(operation, opConfig)}
509
+
510
+ } catch (error) {
511
+ if (error.response) {
512
+ console.error(\`API Error: \${error.response.status} - \${error.response.data}\`);
513
+ } else {
514
+ console.error('Request Error:', error.message);
515
+ }
516
+ return null;
517
+ }
518
+ }
519
+
520
+ function validateAudioFormat(audioData) {
521
+ /**
522
+ * Validate audio format and specifications
523
+ */
524
+
525
+ const supportedFormats = ['MP3', 'FLAC', 'WAV', 'AAC'];
526
+ const formatType = (audioData.format || '').toUpperCase();
527
+
528
+ if (!supportedFormats.includes(formatType)) {
529
+ console.warn(\`Warning: Unsupported audio format: \${formatType}\`);
530
+ return false;
531
+ }
532
+
533
+ const duration = audioData.duration || 0;
534
+ if (duration <= 0 || duration > 3600) { // Max 1 hour
535
+ console.warn(\`Warning: Invalid audio duration: \${duration} seconds\`);
536
+ return false;
537
+ }
538
+
539
+ const bitrate = audioData.bitrate || 0;
540
+ if (bitrate < 64 || bitrate > 1411) { // 64kbps to 1411kbps (FLAC)
541
+ console.warn(\`Warning: Invalid bitrate: \${bitrate} kbps\`);
542
+ return false;
543
+ }
544
+
545
+ return true;
546
+ }
547
+
548
+ function saveAudioFile(audioData, filename) {
549
+ /**
550
+ * Save streamed audio data to file
551
+ */
552
+ const fs = require('fs');
553
+
554
+ if (!filename) {
555
+ const timestamp = Date.now();
556
+ filename = \`soundbite_\${timestamp}.mp3\`;
557
+ }
558
+
559
+ return new Promise((resolve, reject) => {
560
+ const writer = fs.createWriteStream(filename);
561
+ audioData.pipe(writer);
562
+
563
+ writer.on('finish', () => {
564
+ console.log(\`Audio saved as: \${filename}\`);
565
+ resolve(filename);
566
+ });
567
+
568
+ writer.on('error', (error) => {
569
+ console.error(\`Error saving audio: \${error.message}\`);
570
+ reject(error);
571
+ });
572
+ });
573
+ }
574
+
575
+ function calculateAudioPrice(duration, quality, formatType) {
576
+ /**
577
+ * Calculate price for audio content based on specifications
578
+ */
579
+
580
+ const basePrice = 0.99; // Base price in EUR
581
+ const durationMultiplier = Math.max(1, duration / 60); // Per minute
582
+ const qualityMultiplier = { 'LOW': 0.8, 'MEDIUM': 1.0, 'HIGH': 1.3, 'HIGHEST': 1.6 };
583
+ const formatMultiplier = { 'MP3': 1.0, 'FLAC': 1.5, 'WAV': 1.3, 'AAC': 1.1 };
584
+
585
+ const totalPrice = basePrice *
586
+ durationMultiplier *
587
+ (qualityMultiplier[quality] || 1.0) *
588
+ (formatMultiplier[formatType] || 1.0);
589
+
590
+ return Math.round(totalPrice * 100) / 100; // Round to 2 decimal places
591
+ }
592
+
593
+ // Example usage
594
+ async function main() {
595
+ console.log('Soundbites Example');
596
+
597
+ try {
598
+ const result = await ${functionName}();
599
+
600
+ if (result) {
601
+ console.log('Soundbite operation completed successfully');
602
+ ${this.getUsageExample(operation)}
603
+ } else {
604
+ console.log('Soundbite operation failed');
605
+ }
606
+
607
+ } catch (error) {
608
+ console.error('Soundbite operation error:', error.message);
609
+ }
610
+ }
611
+
612
+ // Run example if this file is executed directly
613
+ if (require.main === module) {
614
+ main().catch(console.error);
615
+ }`;
616
+ }
617
+
618
+ getFunctionParams(operation, productId, audioId, soundbiteId) {
619
+ const params = [];
620
+
621
+ if (operation.includes('audio') || operation.includes('register') || operation.includes('list')) {
622
+ params.push(`productId = '${productId}'`);
623
+ if (!operation.includes('list') && !operation.includes('register')) {
624
+ params.push(`audioId = '${audioId}'`);
625
+ }
626
+ } else if (operation.includes('soundbite') || operation.includes('stream') || operation.includes('download')) {
627
+ params.push(`soundbiteId = '${soundbiteId}'`);
628
+ }
629
+
630
+ return params.join(', ');
631
+ }
632
+
633
+ getQueryParamsSection(operation, opConfig) {
634
+ if (!opConfig.queryParams) {
635
+ return '';
636
+ }
637
+
638
+ const paramsStr = JSON.stringify(opConfig.queryParams, null, 4).replace(/^/gm, ' ');
639
+ return `// Add query parameters
640
+ const queryParams = ${paramsStr};
641
+ const searchParams = new URLSearchParams(queryParams);
642
+ url += \`?\${searchParams.toString()}\`;`;
643
+ }
644
+
645
+ getRequestBodySection(operation, opConfig) {
646
+ if (!opConfig.sampleBody) {
647
+ return '// No request body needed for this operation\n const requestBody = null;';
648
+ }
649
+
650
+ const bodyStr = JSON.stringify(opConfig.sampleBody, null, 4).replace(/^/gm, ' ');
651
+
652
+ let validation = '';
653
+ if (operation === 'register_audio') {
654
+ validation = `
655
+
656
+ // Validate audio specifications
657
+ if (requestBody.audioData) {
658
+ if (!validateAudioFormat(requestBody.audioData)) {
659
+ console.warn('Warning: Audio validation failed');
660
+ }
661
+ }`;
662
+ }
663
+
664
+ return `// Prepare request body
665
+ const requestBody = ${bodyStr};${validation}`;
666
+ }
667
+
668
+ getSpecialHandling(operation, opConfig) {
669
+ if (opConfig.responseType === 'audio' || opConfig.responseType === 'file') {
670
+ return `// Set headers for audio/file response
671
+ headers['Accept'] = '${opConfig.responseType}' === 'audio' ? 'audio/*' : 'application/octet-stream';`;
672
+ }
673
+ return '';
674
+ }
675
+
676
+ getRequestSection(method, responseType) {
677
+ if (method === 'GET') {
678
+ if (responseType === 'audio' || responseType === 'file') {
679
+ return `const response = await axios.get(url, {
680
+ headers,
681
+ responseType: 'stream'
682
+ });`;
683
+ }
684
+ return 'const response = await axios.get(url, { headers });';
685
+ } else if (method === 'DELETE') {
686
+ return `const response = await axios.delete(url, {
687
+ headers,
688
+ data: bodyString || undefined
689
+ });`;
690
+ } else {
691
+ return `const response = await axios.${method.toLowerCase()}(
692
+ url,
693
+ bodyString || undefined,
694
+ { headers }
695
+ );`;
696
+ }
697
+ }
698
+
699
+ getResponseHandling(operation, opConfig) {
700
+ if (opConfig.responseType === 'audio' || opConfig.responseType === 'file') {
701
+ return `if (response.status === 200) {
702
+ // Handle audio/file download
703
+ const contentType = response.headers['content-type'] || '';
704
+ const fileExtension = contentType.includes('audio') ? 'mp3' : 'dat';
705
+ const filename = \`\${operation}_\${soundbiteId || audioId}.\${fileExtension}\`;
706
+
707
+ try {
708
+ const savedFile = await saveAudioFile(response.data, filename);
709
+ console.log(\`${opConfig.description} successful - file saved: \${savedFile}\`);
710
+ return { filename: savedFile, contentType };
711
+ } catch (error) {
712
+ console.error('Failed to save audio file:', error.message);
713
+ return null;
714
+ }
715
+ } else {
716
+ console.error(\`Request failed with status \${response.status}: \${response.statusText}\`);
717
+ return null;
718
+ }`;
719
+ } else {
720
+ return `if (response.status === 200) {
721
+ const result = response.data;
722
+ console.log('${opConfig.description} successful:');
723
+ console.log(JSON.stringify(result, null, 2));
724
+ return result;
725
+ } else {
726
+ console.error(\`Request failed with status \${response.status}: \${response.statusText}\`);
727
+ return null;
728
+ }`;
729
+ }
730
+ }
731
+
732
+ getUsageExample(operation) {
733
+ const examples = {
734
+ register_audio: `// Store audio ID for transactions
735
+ if (result.audioId) {
736
+ const audioId = result.audioId;
737
+ console.log(\`Audio registered: \${audioId}\`);
738
+ console.log('Use createSoundbiteTransactionExample() to enable purchases');
739
+ }`,
740
+
741
+ create_soundbite_transaction: `// Monitor transaction and enable access
742
+ if (result.transactionId) {
743
+ const transactionId = result.transactionId;
744
+ const soundbiteId = result.soundbiteId;
745
+ console.log(\`Soundbite transaction: \${transactionId}\`);
746
+ if (soundbiteId) {
747
+ console.log(\`Access with: streamAudioExample('\${soundbiteId}')\`);
748
+ }
749
+ }`,
750
+
751
+ stream_audio: `// Audio streaming started
752
+ if (result.filename) {
753
+ console.log(\`Audio streamed and saved: \${result.filename}\`);
754
+ console.log('Playback can begin immediately');
755
+ }`,
756
+
757
+ get_soundbite_analytics: `// Display analytics insights
758
+ const analytics = result.analytics || {};
759
+ const period = result.period;
760
+
761
+ console.log(\`Soundbite Analytics for \${period}:\`);
762
+ Object.entries(analytics).forEach(([metric, data]) => {
763
+ if (typeof data === 'object' && data !== null) {
764
+ console.log(\`- \${metric}:\`);
765
+ Object.entries(data).forEach(([key, value]) => {
766
+ console.log(\` - \${key}: \${value}\`);
767
+ });
768
+ } else {
769
+ console.log(\`- \${metric}: \${data}\`);
770
+ }
771
+ });
772
+
773
+ // Calculate revenue per stream
774
+ if (analytics.purchases && analytics.revenue) {
775
+ const purchases = analytics.purchases;
776
+ const revenue = analytics.revenue;
777
+ if (purchases > 0) {
778
+ const avgRevenue = revenue / purchases;
779
+ console.log(\`\\nAverage revenue per purchase: €\${avgRevenue.toFixed(2)}\`);
780
+ }
781
+ }`
782
+ };
783
+
784
+ return examples[operation] || '// Process soundbite result as needed';
785
+ }
786
+ }
787
+
788
+ /**
789
+ * Export all generators
790
+ */
791
+ export const SoundbitesGenerators = {
792
+ python: PythonSoundbitesGenerator,
793
+ nodejs: NodeJSSoundbitesGenerator,
794
+ javascript: NodeJSSoundbitesGenerator
795
+ };
796
+
797
+ /**
798
+ * Generate soundbites example
799
+ */
800
+ export function generateSoundbitesExample(operation, language = 'python', partnerType = 'merchant', options = {}) {
801
+ const GeneratorClass = SoundbitesGenerators[language];
802
+ if (!GeneratorClass) {
803
+ throw new Error(`Unsupported language: ${language}`);
804
+ }
805
+
806
+ const generator = new GeneratorClass();
807
+ return generator.generateExample(operation, partnerType, options);
808
+ }