@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.
- package/.env.example +92 -0
- package/MCP-SETUP.md +190 -0
- package/README.md +483 -0
- package/check-keys.js +52 -0
- package/keys/README.md +112 -0
- package/mcp-config.json +9 -0
- package/package.json +66 -0
- package/payware-mcp-config.json +12 -0
- package/src/auth/rsa-keys.js +82 -0
- package/src/auth/sandbox-setup.js +187 -0
- package/src/config/env.js +229 -0
- package/src/config/partner-types.js +143 -0
- package/src/core/auth/jwt-factory.js +182 -0
- package/src/core/auth/jwt-token.js +735 -0
- package/src/core/utils/json-serializer.js +100 -0
- package/src/core/utils/key-utils.js +133 -0
- package/src/index.js +237 -0
- package/src/isv/data/index.js +291 -0
- package/src/isv/deep-links/delete-product-link.js +154 -0
- package/src/isv/deep-links/delete-transaction-link.js +154 -0
- package/src/isv/deep-links/get-product-link.js +167 -0
- package/src/isv/deep-links/get-transaction-link.js +167 -0
- package/src/isv/deep-links/index.js +23 -0
- package/src/isv/index.js +103 -0
- package/src/isv/oauth2/create-token-simple.js +46 -0
- package/src/isv/oauth2/get-token-info.js +157 -0
- package/src/isv/oauth2/obtain-token.js +189 -0
- package/src/isv/oauth2/token-helper.js +151 -0
- package/src/isv/products/audios.js +470 -0
- package/src/isv/products/create-product.js +314 -0
- package/src/isv/products/delete-product.js +119 -0
- package/src/isv/products/get-product-image.js +247 -0
- package/src/isv/products/get-product.js +168 -0
- package/src/isv/products/index.js +65 -0
- package/src/isv/products/list-products.js +136 -0
- package/src/isv/products/schedules.js +522 -0
- package/src/isv/products/update-product.js +265 -0
- package/src/isv/transactions/cancel-transaction.js +129 -0
- package/src/isv/transactions/create-transaction.js +361 -0
- package/src/isv/transactions/get-status.js +217 -0
- package/src/isv/transactions/process-transaction.js +145 -0
- package/src/isv/transactions/simulate-callback.js +146 -0
- package/src/isv/transactions/transaction-history.js +186 -0
- package/src/isv/utils/auth.js +159 -0
- package/src/mcp-proxy.js +150 -0
- package/src/merchant/data/export-report.js +108 -0
- package/src/merchant/data/generate-report.js +151 -0
- package/src/merchant/data/get-report-status.js +120 -0
- package/src/merchant/data/index.js +351 -0
- package/src/merchant/deep-links/delete-product-link.js +121 -0
- package/src/merchant/deep-links/delete-transaction-link.js +121 -0
- package/src/merchant/deep-links/get-product-link.js +134 -0
- package/src/merchant/deep-links/get-transaction-link.js +134 -0
- package/src/merchant/deep-links/index.js +23 -0
- package/src/merchant/index.js +92 -0
- package/src/merchant/products/audios.js +606 -0
- package/src/merchant/products/create-product.js +288 -0
- package/src/merchant/products/delete-product.js +111 -0
- package/src/merchant/products/get-product-image.js +252 -0
- package/src/merchant/products/get-product.js +144 -0
- package/src/merchant/products/index.js +65 -0
- package/src/merchant/products/list-products.js +112 -0
- package/src/merchant/products/schedules.js +646 -0
- package/src/merchant/products/update-product.js +282 -0
- package/src/merchant/transactions/cancel-transaction.js +241 -0
- package/src/merchant/transactions/create-transaction.js +482 -0
- package/src/merchant/transactions/get-status.js +196 -0
- package/src/merchant/transactions/process-transaction.js +349 -0
- package/src/merchant/transactions/simulate-callback.js +246 -0
- package/src/merchant/transactions/transaction-history.js +258 -0
- package/src/payment-institution/data/index.js +280 -0
- package/src/payment-institution/deep-links/delete-transaction-link.js +128 -0
- package/src/payment-institution/deep-links/get-transaction-link.js +141 -0
- package/src/payment-institution/deep-links/index.js +17 -0
- package/src/payment-institution/index.js +92 -0
- package/src/payment-institution/transactions/create-pi-transaction.js +484 -0
- package/src/payment-institution/transactions/finalize-transaction.js +340 -0
- package/src/payment-institution/transactions/get-transaction-history.js +269 -0
- package/src/payment-institution/transactions/get-transaction-status.js +234 -0
- package/src/payment-institution/transactions/process-pi-transaction.js +421 -0
- package/src/payment-institution/transactions/simulate-pi-callback.js +326 -0
- package/src/payment-institution/transactions/soundbite-transaction.js +269 -0
- package/src/proxy-server.js +205 -0
- package/src/shared/data/data-api.js +372 -0
- package/src/shared/deep-links/api.js +214 -0
- package/src/shared/index.js +44 -0
- package/src/shared/products/products-api.js +872 -0
- package/src/tools/advanced-code-generator.js +2247 -0
- package/src/utils/code-examples/common/auth-examples.js +1482 -0
- package/src/utils/code-examples/common/helpers.js +416 -0
- package/src/utils/code-examples/common/utils-examples.js +591 -0
- package/src/utils/code-examples/frameworks/examples.js +859 -0
- package/src/utils/code-examples/index.js +441 -0
- package/src/utils/code-examples/isv/oauth2.js +600 -0
- package/src/utils/code-examples/isv/products.js +666 -0
- package/src/utils/code-examples/isv/transactions.js +813 -0
- package/src/utils/code-examples/merchant/products.js +497 -0
- package/src/utils/code-examples/merchant/transactions.js +623 -0
- package/src/utils/code-examples/payment-institution/transactions.js +388 -0
- package/src/utils/code-examples/shared/data.js +607 -0
- package/src/utils/code-examples/shared/deep-links.js +662 -0
- package/src/utils/code-examples/shared/soundbites.js +808 -0
- package/src/utils/code-examples.js +646 -0
- package/src/utils/format-request.js +363 -0
- package/src/utils/json-formatter.js +249 -0
- 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
|
+
}
|