@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,813 @@
1
+ /**
2
+ * ISV transaction examples for all operations
3
+ */
4
+
5
+ import { PythonGenerator, NodeJSGenerator } from '../common/utils-examples.js';
6
+ import { CommonTemplates } from '../common/helpers.js';
7
+
8
+ /**
9
+ * ISV transaction operations
10
+ */
11
+ export const ISVTransactionOperations = {
12
+ create_transaction: {
13
+ description: 'Create a transaction on behalf of a merchant (ISV operation)',
14
+ endpoint: '/transactions',
15
+ method: 'POST',
16
+ requiresOAuth: true,
17
+ sampleBody: {
18
+ trData: {
19
+ amount: '50.00',
20
+ currency: 'EUR',
21
+ reasonL1: 'ISV managed transaction',
22
+ reasonL2: 'Created on behalf of merchant'
23
+ },
24
+ trOptions: {
25
+ type: 'QR',
26
+ timeToLive: 600
27
+ }
28
+ }
29
+ },
30
+
31
+ get_transaction_status: {
32
+ description: 'Get transaction status on behalf of merchant',
33
+ endpoint: '/transactions/{id}',
34
+ method: 'GET',
35
+ requiresOAuth: true,
36
+ sampleBody: {}
37
+ },
38
+
39
+ cancel_transaction: {
40
+ description: 'Cancel transaction on behalf of merchant',
41
+ endpoint: '/transactions/{id}',
42
+ method: 'PATCH',
43
+ requiresOAuth: true,
44
+ sampleBody: {
45
+ status: 'CANCELLED',
46
+ statusMessage: 'ISV initiated cancellation'
47
+ }
48
+ },
49
+
50
+ process_transaction: {
51
+ description: 'Process transaction on behalf of merchant',
52
+ endpoint: '/transactions/{id}',
53
+ method: 'POST',
54
+ requiresOAuth: true,
55
+ sampleBody: {
56
+ account: 'MERCHANT_ACCOUNT_123',
57
+ friendlyName: 'Merchant Name via ISV'
58
+ }
59
+ },
60
+
61
+ get_transaction_history: {
62
+ description: 'Get transaction history for merchant',
63
+ endpoint: '/transactions/{id}/history',
64
+ method: 'GET',
65
+ requiresOAuth: true,
66
+ sampleBody: {}
67
+ },
68
+
69
+ simulate_callback: {
70
+ description: 'Simulate callback for merchant transaction',
71
+ endpoint: '/transactions/{id}/callback',
72
+ method: 'POST',
73
+ requiresOAuth: true,
74
+ sampleBody: {
75
+ status: 'PROCESSED',
76
+ timestamp: new Date().toISOString()
77
+ }
78
+ }
79
+ };
80
+
81
+ /**
82
+ * Generate Python examples for ISV transactions
83
+ */
84
+ export function generateISVTransactionPython(operation, params = {}) {
85
+ const operationConfig = ISVTransactionOperations[operation];
86
+
87
+ if (!operationConfig) {
88
+ throw new Error(`Unknown operation: ${operation}`);
89
+ }
90
+
91
+ const sections = [];
92
+
93
+ // Header and description
94
+ sections.push(`"""
95
+ ${operationConfig.description}
96
+
97
+ This example demonstrates ISV operations for managing transactions on behalf of merchants.
98
+ Partner Type: ISV (Independent Software Vendor)
99
+ Operation: ${operation}
100
+ Method: ${operationConfig.method}
101
+ Endpoint: ${operationConfig.endpoint}
102
+ Authentication: ISV JWT + OAuth2 Token
103
+ """
104
+
105
+ # Required dependencies:
106
+ # pip install requests pyjwt cryptography python-dotenv
107
+
108
+ import requests
109
+ import jwt
110
+ import json
111
+ import hashlib
112
+ import base64
113
+ import os
114
+ from datetime import datetime
115
+ from dotenv import load_dotenv
116
+
117
+ # Load environment variables
118
+ load_dotenv()`);
119
+
120
+ // Environment setup
121
+ sections.push(`# ISV Environment Configuration
122
+ def load_isv_config():
123
+ """Load ISV configuration from environment"""
124
+ config = {
125
+ 'isv_partner_id': os.getenv('PAYWARE_PARTNER_ID'),
126
+ 'sandbox_key_path': os.getenv('PAYWARE_SANDBOX_PRIVATE_KEY_PATH'),
127
+ 'production_key_path': os.getenv('PAYWARE_PRODUCTION_PRIVATE_KEY_PATH'),
128
+ 'oauth_client_id': os.getenv('PAYWARE_OAUTH_CLIENT_ID'),
129
+ 'oauth_client_secret': os.getenv('PAYWARE_OAUTH_CLIENT_SECRET'),
130
+ 'default_merchant_id': os.getenv('PAYWARE_DEFAULT_MERCHANT_ID'),
131
+ 'sandbox_url': os.getenv('PAYWARE_SANDBOX_URL', 'https://sandbox.payware.eu/api'),
132
+ 'production_url': os.getenv('PAYWARE_PRODUCTION_URL', 'https://api.payware.eu/api')
133
+ }
134
+
135
+ # Validate required configuration
136
+ required_vars = ['isv_partner_id', 'oauth_client_id', 'oauth_client_secret']
137
+ for var in required_vars:
138
+ if not config[var]:
139
+ env_var = {
140
+ 'isv_partner_id': 'PAYWARE_PARTNER_ID',
141
+ 'oauth_client_id': 'PAYWARE_OAUTH_CLIENT_ID',
142
+ 'oauth_client_secret': 'PAYWARE_OAUTH_CLIENT_SECRET'
143
+ }[var]
144
+ raise ValueError(f"{env_var} environment variable is required for ISV operations")
145
+
146
+ return config`);
147
+
148
+ // OAuth2 functions
149
+ sections.push(`# OAuth2 Authentication for ISV
150
+ def get_oauth2_token(merchant_partner_id, use_sandbox=True):
151
+ """Get OAuth2 token for accessing merchant resources"""
152
+ config = load_isv_config()
153
+ base_url = config['sandbox_url'] if use_sandbox else config['production_url']
154
+
155
+ # Prepare OAuth2 token request
156
+ token_data = {
157
+ 'grant_type': 'client_credentials',
158
+ 'client_id': config['oauth_client_id'],
159
+ 'client_secret': config['oauth_client_secret'],
160
+ 'scope': f'merchant:{merchant_partner_id}'
161
+ }
162
+
163
+ try:
164
+ response = requests.post(
165
+ f"{base_url}/oauth2/token",
166
+ data=token_data,
167
+ headers={'Content-Type': 'application/x-www-form-urlencoded'}
168
+ )
169
+
170
+ if response.status_code == 200:
171
+ token_response = response.json()
172
+ return {
173
+ 'access_token': token_response['access_token'],
174
+ 'token_type': token_response['token_type'],
175
+ 'expires_in': token_response.get('expires_in'),
176
+ 'scope': token_response.get('scope')
177
+ }
178
+ else:
179
+ raise Exception(f"OAuth2 token request failed: {response.status_code} - {response.text}")
180
+
181
+ except Exception as e:
182
+ raise Exception(f"OAuth2 authentication error: {str(e)}")
183
+
184
+ def create_isv_jwt_token(merchant_partner_id, oauth2_token, request_body=None, use_sandbox=True):
185
+ """Create ISV JWT token with OAuth2 token for merchant access"""
186
+ config = load_isv_config()
187
+
188
+ # Get private key path based on environment
189
+ private_key_path = config['sandbox_key_path'] if use_sandbox else config['production_key_path']
190
+ if not private_key_path:
191
+ env_var = 'PAYWARE_SANDBOX_PRIVATE_KEY_PATH' if use_sandbox else 'PAYWARE_PRODUCTION_PRIVATE_KEY_PATH'
192
+ raise ValueError(f"{env_var} environment variable is required")
193
+
194
+ # Load ISV private key
195
+ with open(private_key_path, 'r') as f:
196
+ private_key = f.read()
197
+
198
+ # Create JWT header
199
+ header = {
200
+ 'alg': 'RS256',
201
+ 'typ': 'JWT'
202
+ }
203
+
204
+ # Add content MD5 for requests with body
205
+ body_string = None
206
+ if request_body:
207
+ body_string = create_minimized_json(request_body)
208
+ content_md5 = base64.b64encode(
209
+ hashlib.md5(body_string.encode('utf-8')).digest()
210
+ ).decode('utf-8')
211
+ header['contentMd5'] = content_md5
212
+
213
+ # Create ISV JWT payload with OAuth2 token
214
+ payload = {
215
+ 'iss': config['isv_partner_id'], # ISV Partner ID
216
+ 'aud': merchant_partner_id, # Target Merchant ID
217
+ 'sub': oauth2_token, # OAuth2 access token
218
+ 'iat': int(datetime.utcnow().timestamp())
219
+ }
220
+
221
+ # Create token
222
+ token = jwt.encode(payload, private_key, algorithm='RS256', headers=header)
223
+ return token, body_string
224
+
225
+ def create_minimized_json(data):
226
+ """Create deterministic minimized JSON for MD5 calculation"""
227
+ def sort_dict(obj):
228
+ if isinstance(obj, dict):
229
+ return {k: sort_dict(v) for k, v in sorted(obj.items())}
230
+ elif isinstance(obj, list):
231
+ return [sort_dict(item) for item in obj]
232
+ else:
233
+ return obj
234
+
235
+ sorted_data = sort_dict(data)
236
+ return json.dumps(sorted_data, separators=(',', ':'))
237
+
238
+ def get_api_headers(token):
239
+ """Get standard API headers"""
240
+ return {
241
+ 'Authorization': f'Bearer {token}',
242
+ 'Content-Type': 'application/json',
243
+ 'Api-Version': '1'
244
+ }`);
245
+
246
+ // Main operation function
247
+ const needsId = operationConfig.endpoint.includes('{id}');
248
+ const hasBody = operationConfig.method !== 'GET' && Object.keys(operationConfig.sampleBody).length > 0;
249
+
250
+ const paramList = ['merchant_partner_id', 'use_sandbox=True'];
251
+ if (needsId) {
252
+ paramList.splice(1, 0, 'transaction_id');
253
+ }
254
+
255
+ // Add custom parameters for specific operations
256
+ if (operation === 'create_transaction') {
257
+ paramList.splice(-1, 0, 'amount="50.00"', 'currency="EUR"', 'reason_l1="ISV transaction"', 'reason_l2=None');
258
+ }
259
+
260
+ const endpoint = operationConfig.endpoint.replace('{id}', '{transaction_id}');
261
+
262
+ sections.push(`# Main ISV Operation
263
+ def ${operation}(${paramList.join(', ')}):
264
+ """${operationConfig.description}"""
265
+
266
+ try:
267
+ config = load_isv_config()
268
+ base_url = config['sandbox_url'] if use_sandbox else config['production_url']
269
+
270
+ # Step 1: Get OAuth2 token for merchant access
271
+ print(f"šŸ” Getting OAuth2 token for merchant: {merchant_partner_id}")
272
+ oauth2_response = get_oauth2_token(merchant_partner_id, use_sandbox)
273
+ oauth2_token = oauth2_response['access_token']
274
+ print(f"āœ… OAuth2 token acquired (expires in: {oauth2_response.get('expires_in', 'unknown')} seconds)")
275
+
276
+ ${hasBody ? `# Step 2: Prepare request body
277
+ request_body = ${JSON.stringify(operationConfig.sampleBody, null, 8).replace(/"/g, "'")}
278
+
279
+ ${operation === 'create_transaction' ? `# Customize with parameters
280
+ if amount:
281
+ request_body['trData']['amount'] = amount
282
+ if currency:
283
+ request_body['trData']['currency'] = currency
284
+ if reason_l1:
285
+ request_body['trData']['reasonL1'] = reason_l1
286
+ if reason_l2:
287
+ request_body['trData']['reasonL2'] = reason_l2` : ''}
288
+
289
+ # Step 3: Create ISV JWT token with OAuth2 token
290
+ print("šŸ” Creating ISV JWT token with OAuth2 authentication")
291
+ token, body_string = create_isv_jwt_token(
292
+ merchant_partner_id,
293
+ oauth2_token,
294
+ request_body,
295
+ use_sandbox
296
+ )
297
+ headers = get_api_headers(token)
298
+
299
+ # Step 4: Make API request
300
+ url = f"{base_url}${endpoint}"${needsId ? `.format(transaction_id=transaction_id)` : ''}
301
+ print(f"šŸ“” Making ${operationConfig.method} request to: {url}")
302
+ response = requests.${operationConfig.method.toLowerCase()}(
303
+ url,
304
+ data=body_string,
305
+ headers=headers
306
+ )` : `# Step 2: Create ISV JWT token with OAuth2 token
307
+ print("šŸ” Creating ISV JWT token with OAuth2 authentication")
308
+ token, _ = create_isv_jwt_token(
309
+ merchant_partner_id,
310
+ oauth2_token,
311
+ None,
312
+ use_sandbox
313
+ )
314
+ headers = get_api_headers(token)
315
+
316
+ # Step 3: Make API request
317
+ url = f"{base_url}${endpoint}"${needsId ? `.format(transaction_id=transaction_id)` : ''}
318
+ print(f"šŸ“” Making ${operationConfig.method} request to: {url}")
319
+ response = requests.${operationConfig.method.toLowerCase()}(
320
+ url,
321
+ headers=headers
322
+ )`}
323
+
324
+ # Step ${hasBody ? '5' : '4'}: Handle response
325
+ if response.status_code in [200, 201]:
326
+ result = response.json()
327
+ return {
328
+ 'success': True,
329
+ 'data': result,
330
+ 'merchant_partner_id': merchant_partner_id,
331
+ 'oauth2_info': {
332
+ 'token_type': oauth2_response['token_type'],
333
+ 'scope': oauth2_response.get('scope'),
334
+ 'expires_in': oauth2_response.get('expires_in')
335
+ },
336
+ 'request_id': response.headers.get('x-request-id'),
337
+ 'timestamp': datetime.utcnow().isoformat()
338
+ }
339
+ else:
340
+ return {
341
+ 'success': False,
342
+ 'error': {
343
+ 'status': response.status_code,
344
+ 'message': response.text,
345
+ 'details': response.json() if response.headers.get('content-type', '').startswith('application/json') else None
346
+ },
347
+ 'merchant_partner_id': merchant_partner_id,
348
+ 'timestamp': datetime.utcnow().isoformat()
349
+ }
350
+
351
+ except Exception as e:
352
+ return {
353
+ 'success': False,
354
+ 'error': {
355
+ 'message': str(e),
356
+ 'type': type(e).__name__
357
+ },
358
+ 'merchant_partner_id': merchant_partner_id,
359
+ 'timestamp': datetime.utcnow().isoformat()
360
+ }`);
361
+
362
+ // Error handling
363
+ sections.push(`# ISV Error Handling
364
+ def handle_isv_error(error_response):
365
+ """Handle ISV-specific API errors"""
366
+ status = error_response.get('status', 0)
367
+ message = error_response.get('message', 'Unknown error')
368
+
369
+ isv_error_messages = {
370
+ 400: f"Bad Request: {message}",
371
+ 401: "Authentication failed - check ISV credentials and OAuth2 setup",
372
+ 403: "Forbidden - ISV may not have permission for this merchant",
373
+ 404: "Resource not found",
374
+ 409: "Conflict - resource already exists or invalid state",
375
+ 429: "Rate limit exceeded - please retry later",
376
+ 500: "Internal server error - please try again"
377
+ }
378
+
379
+ base_message = isv_error_messages.get(status, f"API Error {status}: {message}")
380
+
381
+ # Add ISV-specific guidance
382
+ if status == 401:
383
+ base_message += "\\nšŸ’” ISV Troubleshooting:"
384
+ base_message += "\\n - Verify PAYWARE_PARTNER_ID is your ISV partner ID"
385
+ base_message += "\\n - Check OAuth2 client credentials"
386
+ base_message += "\\n - Ensure merchant has granted access to your ISV"
387
+ elif status == 403:
388
+ base_message += "\\nšŸ’” ISV Authorization:"
389
+ base_message += "\\n - Merchant may need to authorize your ISV application"
390
+ base_message += "\\n - Check OAuth2 scope permissions"
391
+ base_message += "\\n - Verify merchant partner ID is correct"
392
+
393
+ return base_message`);
394
+
395
+ // Example usage
396
+ const exampleParams = needsId ? '"MERCHANT123", "pw12345678"' : '"MERCHANT123"';
397
+ sections.push(`# Example Usage
398
+ if __name__ == "__main__":
399
+ print("=== ISV ${operation.replace('_', ' ').title()} Example ===\\n")
400
+
401
+ # Example merchant partner ID (replace with actual merchant ID)
402
+ merchant_id = "MERCHANT123"
403
+
404
+ try:
405
+ # Execute the ISV operation
406
+ result = ${operation}(${exampleParams})
407
+
408
+ if result['success']:
409
+ print("āœ… ISV operation successful!")
410
+ print(f"šŸ¢ Merchant: {result['merchant_partner_id']}")
411
+ print(f"šŸ” OAuth2 Scope: {result['oauth2_info']['scope']}")
412
+ print(f"šŸ“‹ Request ID: {result.get('request_id', 'N/A')}")
413
+ print("\\nšŸ“Š Response Data:")
414
+ print(json.dumps(result['data'], indent=2))
415
+ else:
416
+ print("āŒ ISV operation failed!")
417
+ print(f"šŸ¢ Merchant: {result['merchant_partner_id']}")
418
+ error_message = handle_isv_error(result['error'])
419
+ print(f"āŒ Error: {error_message}")
420
+
421
+ if result['error'].get('details'):
422
+ print("\\nšŸ“‹ Error Details:")
423
+ print(json.dumps(result['error']['details'], indent=2))
424
+
425
+ except Exception as e:
426
+ print(f"āŒ Unexpected error: {e}")
427
+ print("\\nšŸ’” Common Issues:")
428
+ print(" - Check all ISV environment variables are set")
429
+ print(" - Verify merchant has authorized your ISV application")
430
+ print(" - Ensure OAuth2 credentials are correct")
431
+
432
+ print(f"\\nTimestamp: {datetime.utcnow().isoformat()}")
433
+ print("\\n" + "="*60)`);
434
+
435
+ return sections.join('\n\n');
436
+ }
437
+
438
+ /**
439
+ * Generate Node.js examples for ISV transactions
440
+ */
441
+ export function generateISVTransactionNodeJS(operation, params = {}) {
442
+ const operationConfig = ISVTransactionOperations[operation];
443
+
444
+ if (!operationConfig) {
445
+ throw new Error(`Unknown operation: ${operation}`);
446
+ }
447
+
448
+ const sections = [];
449
+
450
+ // Header and description
451
+ sections.push(`/**
452
+ * ${operationConfig.description}
453
+ *
454
+ * This example demonstrates ISV operations for managing transactions on behalf of merchants.
455
+ * Partner Type: ISV (Independent Software Vendor)
456
+ * Operation: ${operation}
457
+ * Method: ${operationConfig.method}
458
+ * Endpoint: ${operationConfig.endpoint}
459
+ * Authentication: ISV JWT + OAuth2 Token
460
+ */
461
+
462
+ // Required dependencies:
463
+ // npm install axios jsonwebtoken crypto fs dotenv
464
+
465
+ const axios = require('axios');
466
+ const jwt = require('jsonwebtoken');
467
+ const crypto = require('crypto');
468
+ const fs = require('fs');
469
+ require('dotenv').config();`);
470
+
471
+ // Configuration
472
+ sections.push(`// ISV Configuration
473
+ function loadISVConfig() {
474
+ const config = {
475
+ isvPartnerId: process.env.PAYWARE_PARTNER_ID,
476
+ sandboxKeyPath: process.env.PAYWARE_SANDBOX_PRIVATE_KEY_PATH,
477
+ productionKeyPath: process.env.PAYWARE_PRODUCTION_PRIVATE_KEY_PATH,
478
+ oauthClientId: process.env.PAYWARE_OAUTH_CLIENT_ID,
479
+ oauthClientSecret: process.env.PAYWARE_OAUTH_CLIENT_SECRET,
480
+ defaultMerchantId: process.env.PAYWARE_DEFAULT_MERCHANT_ID,
481
+ sandboxUrl: process.env.PAYWARE_SANDBOX_URL || 'https://sandbox.payware.eu/api',
482
+ productionUrl: process.env.PAYWARE_PRODUCTION_URL || 'https://api.payware.eu/api'
483
+ };
484
+
485
+ // Validate required configuration
486
+ const requiredVars = ['isvPartnerId', 'oauthClientId', 'oauthClientSecret'];
487
+ for (const varName of requiredVars) {
488
+ if (!config[varName]) {
489
+ const envVar = {
490
+ isvPartnerId: 'PAYWARE_PARTNER_ID',
491
+ oauthClientId: 'PAYWARE_OAUTH_CLIENT_ID',
492
+ oauthClientSecret: 'PAYWARE_OAUTH_CLIENT_SECRET'
493
+ }[varName];
494
+ throw new Error(\`\${envVar} environment variable is required for ISV operations\`);
495
+ }
496
+ }
497
+
498
+ return config;
499
+ }`);
500
+
501
+ // OAuth2 functions
502
+ sections.push(`// OAuth2 Authentication for ISV
503
+ async function getOAuth2Token(merchantPartnerId, useSandbox = true) {
504
+ const config = loadISVConfig();
505
+ const baseUrl = useSandbox ? config.sandboxUrl : config.productionUrl;
506
+
507
+ const tokenData = new URLSearchParams({
508
+ grant_type: 'client_credentials',
509
+ client_id: config.oauthClientId,
510
+ client_secret: config.oauthClientSecret,
511
+ scope: \`merchant:\${merchantPartnerId}\`
512
+ });
513
+
514
+ try {
515
+ const response = await axios.post(
516
+ \`\${baseUrl}/oauth2/token\`,
517
+ tokenData,
518
+ {
519
+ headers: {
520
+ 'Content-Type': 'application/x-www-form-urlencoded'
521
+ }
522
+ }
523
+ );
524
+
525
+ return {
526
+ accessToken: response.data.access_token,
527
+ tokenType: response.data.token_type,
528
+ expiresIn: response.data.expires_in,
529
+ scope: response.data.scope
530
+ };
531
+
532
+ } catch (error) {
533
+ throw new Error(\`OAuth2 token request failed: \${error.response?.data?.error || error.message}\`);
534
+ }
535
+ }
536
+
537
+ function createISVJWTToken(merchantPartnerId, oauth2Token, requestBody = null, useSandbox = true) {
538
+ const config = loadISVConfig();
539
+
540
+ // Get private key path based on environment
541
+ const privateKeyPath = useSandbox ? config.sandboxKeyPath : config.productionKeyPath;
542
+ if (!privateKeyPath) {
543
+ const envVar = useSandbox ? 'PAYWARE_SANDBOX_PRIVATE_KEY_PATH' : 'PAYWARE_PRODUCTION_PRIVATE_KEY_PATH';
544
+ throw new Error(\`\${envVar} environment variable is required\`);
545
+ }
546
+
547
+ // Load ISV private key
548
+ const privateKey = fs.readFileSync(privateKeyPath, 'utf8');
549
+
550
+ // Create JWT header
551
+ const header = {
552
+ alg: 'RS256',
553
+ typ: 'JWT'
554
+ };
555
+
556
+ // Add content MD5 for requests with body
557
+ let bodyString = null;
558
+ if (requestBody) {
559
+ bodyString = createMinimizedJSON(requestBody);
560
+ const contentMd5 = crypto
561
+ .createHash('md5')
562
+ .update(bodyString, 'utf8')
563
+ .digest('base64');
564
+ header.contentMd5 = contentMd5;
565
+ }
566
+
567
+ // Create ISV JWT payload with OAuth2 token
568
+ const payload = {
569
+ iss: config.isvPartnerId, // ISV Partner ID
570
+ aud: merchantPartnerId, // Target Merchant ID
571
+ sub: oauth2Token, // OAuth2 access token
572
+ iat: Math.floor(Date.now() / 1000)
573
+ };
574
+
575
+ // Create token
576
+ const token = jwt.sign(payload, privateKey, {
577
+ algorithm: 'RS256',
578
+ header
579
+ });
580
+
581
+ return { token, bodyString };
582
+ }
583
+
584
+ function createMinimizedJSON(data) {
585
+ // Sort object keys recursively for deterministic output
586
+ function sortObject(obj) {
587
+ if (Array.isArray(obj)) {
588
+ return obj.map(item => sortObject(item));
589
+ } else if (obj !== null && typeof obj === 'object') {
590
+ const sorted = {};
591
+ Object.keys(obj).sort().forEach(key => {
592
+ sorted[key] = sortObject(obj[key]);
593
+ });
594
+ return sorted;
595
+ }
596
+ return obj;
597
+ }
598
+
599
+ const sortedData = sortObject(data);
600
+ return JSON.stringify(sortedData);
601
+ }
602
+
603
+ function getAPIHeaders(token) {
604
+ return {
605
+ 'Authorization': \`Bearer \${token}\`,
606
+ 'Content-Type': 'application/json',
607
+ 'Api-Version': '1'
608
+ };
609
+ }`);
610
+
611
+ // Main operation function
612
+ const needsId = operationConfig.endpoint.includes('{id}');
613
+ const hasBody = operationConfig.method !== 'GET' && Object.keys(operationConfig.sampleBody).length > 0;
614
+
615
+ const paramList = ['merchantPartnerId', 'useSandbox = true'];
616
+ if (needsId) {
617
+ paramList.splice(1, 0, 'transactionId');
618
+ }
619
+
620
+ // Add custom parameters for specific operations
621
+ if (operation === 'create_transaction') {
622
+ paramList.splice(-1, 0, 'amount = "50.00"', 'currency = "EUR"', 'reasonL1 = "ISV transaction"', 'reasonL2 = null');
623
+ }
624
+
625
+ const endpoint = operationConfig.endpoint.replace('{id}', '${transactionId}');
626
+
627
+ sections.push(`// Main ISV Operation
628
+ async function ${operation}(${paramList.join(', ')}) {
629
+ /**
630
+ * ${operationConfig.description}
631
+ */
632
+
633
+ try {
634
+ const config = loadISVConfig();
635
+ const baseUrl = useSandbox ? config.sandboxUrl : config.productionUrl;
636
+
637
+ // Step 1: Get OAuth2 token for merchant access
638
+ console.log(\`šŸ” Getting OAuth2 token for merchant: \${merchantPartnerId}\`);
639
+ const oauth2Response = await getOAuth2Token(merchantPartnerId, useSandbox);
640
+ const oauth2Token = oauth2Response.accessToken;
641
+ console.log(\`āœ… OAuth2 token acquired (expires in: \${oauth2Response.expiresIn || 'unknown'} seconds)\`);
642
+
643
+ ${hasBody ? `// Step 2: Prepare request body
644
+ const requestBody = ${JSON.stringify(operationConfig.sampleBody, null, 4)};
645
+
646
+ ${operation === 'create_transaction' ? `// Customize with parameters
647
+ if (amount) requestBody.trData.amount = amount;
648
+ if (currency) requestBody.trData.currency = currency;
649
+ if (reasonL1) requestBody.trData.reasonL1 = reasonL1;
650
+ if (reasonL2) requestBody.trData.reasonL2 = reasonL2;` : ''}
651
+
652
+ // Step 3: Create ISV JWT token with OAuth2 token
653
+ console.log('šŸ” Creating ISV JWT token with OAuth2 authentication');
654
+ const { token, bodyString } = createISVJWTToken(
655
+ merchantPartnerId,
656
+ oauth2Token,
657
+ requestBody,
658
+ useSandbox
659
+ );
660
+ const headers = getAPIHeaders(token);
661
+
662
+ // Step 4: Make API request
663
+ const url = \`\${baseUrl}${endpoint}\`;
664
+ console.log(\`šŸ“” Making ${operationConfig.method} request to: \${url}\`);
665
+ const response = await axios.${operationConfig.method.toLowerCase()}(
666
+ url,
667
+ bodyString,
668
+ {
669
+ headers,
670
+ transformRequest: [(data) => data]
671
+ }
672
+ );` : `// Step 2: Create ISV JWT token with OAuth2 token
673
+ console.log('šŸ” Creating ISV JWT token with OAuth2 authentication');
674
+ const { token } = createISVJWTToken(
675
+ merchantPartnerId,
676
+ oauth2Token,
677
+ null,
678
+ useSandbox
679
+ );
680
+ const headers = getAPIHeaders(token);
681
+
682
+ // Step 3: Make API request
683
+ const url = \`\${baseUrl}${endpoint}\`;
684
+ console.log(\`šŸ“” Making ${operationConfig.method} request to: \${url}\`);
685
+ const response = await axios.${operationConfig.method.toLowerCase()}(
686
+ url,
687
+ { headers }
688
+ );`}
689
+
690
+ // Step ${hasBody ? '5' : '4'}: Handle successful response
691
+ return {
692
+ success: true,
693
+ data: response.data,
694
+ merchantPartnerId,
695
+ oauth2Info: {
696
+ tokenType: oauth2Response.tokenType,
697
+ scope: oauth2Response.scope,
698
+ expiresIn: oauth2Response.expiresIn
699
+ },
700
+ requestId: response.headers['x-request-id'],
701
+ timestamp: new Date().toISOString()
702
+ };
703
+
704
+ } catch (error) {
705
+ // Handle errors
706
+ return {
707
+ success: false,
708
+ error: {
709
+ message: error.response?.data?.message || error.message,
710
+ status: error.response?.status,
711
+ details: error.response?.data
712
+ },
713
+ merchantPartnerId,
714
+ timestamp: new Date().toISOString()
715
+ };
716
+ }
717
+ }`);
718
+
719
+ // Error handling
720
+ sections.push(`// ISV Error Handling
721
+ function handleISVError(errorResponse) {
722
+ const status = errorResponse.status || 0;
723
+ const message = errorResponse.message || 'Unknown error';
724
+
725
+ const isvErrorMessages = {
726
+ 400: \`Bad Request: \${message}\`,
727
+ 401: 'Authentication failed - check ISV credentials and OAuth2 setup',
728
+ 403: 'Forbidden - ISV may not have permission for this merchant',
729
+ 404: 'Resource not found',
730
+ 409: 'Conflict - resource already exists or invalid state',
731
+ 429: 'Rate limit exceeded - please retry later',
732
+ 500: 'Internal server error - please try again'
733
+ };
734
+
735
+ let baseMessage = isvErrorMessages[status] || \`API Error \${status}: \${message}\`;
736
+
737
+ // Add ISV-specific guidance
738
+ if (status === 401) {
739
+ baseMessage += '\\nšŸ’” ISV Troubleshooting:';
740
+ baseMessage += '\\n - Verify PAYWARE_PARTNER_ID is your ISV partner ID';
741
+ baseMessage += '\\n - Check OAuth2 client credentials';
742
+ baseMessage += '\\n - Ensure merchant has granted access to your ISV';
743
+ } else if (status === 403) {
744
+ baseMessage += '\\nšŸ’” ISV Authorization:';
745
+ baseMessage += '\\n - Merchant may need to authorize your ISV application';
746
+ baseMessage += '\\n - Check OAuth2 scope permissions';
747
+ baseMessage += '\\n - Verify merchant partner ID is correct';
748
+ }
749
+
750
+ return baseMessage;
751
+ }`);
752
+
753
+ // Example usage
754
+ const exampleParams = needsId ? "'MERCHANT123', 'pw12345678'" : "'MERCHANT123'";
755
+ sections.push(`// Example Usage
756
+ async function main() {
757
+ console.log('=== ISV ${operation.replace(/_/g, ' ').replace(/\\b\\w/g, l => l.toUpperCase())} Example ===\\n');
758
+
759
+ // Example merchant partner ID (replace with actual merchant ID)
760
+ const merchantId = 'MERCHANT123';
761
+
762
+ try {
763
+ // Execute the ISV operation
764
+ const result = await ${operation}(${exampleParams});
765
+
766
+ if (result.success) {
767
+ console.log('āœ… ISV operation successful!');
768
+ console.log(\`šŸ¢ Merchant: \${result.merchantPartnerId}\`);
769
+ console.log(\`šŸ” OAuth2 Scope: \${result.oauth2Info.scope}\`);
770
+ console.log(\`šŸ“‹ Request ID: \${result.requestId || 'N/A'}\`);
771
+ console.log('\\nšŸ“Š Response Data:');
772
+ console.log(JSON.stringify(result.data, null, 2));
773
+ } else {
774
+ console.log('āŒ ISV operation failed!');
775
+ console.log(\`šŸ¢ Merchant: \${result.merchantPartnerId}\`);
776
+ const errorMessage = handleISVError(result.error);
777
+ console.log(\`āŒ Error: \${errorMessage}\`);
778
+
779
+ if (result.error.details) {
780
+ console.log('\\nšŸ“‹ Error Details:');
781
+ console.log(JSON.stringify(result.error.details, null, 2));
782
+ }
783
+ }
784
+
785
+ } catch (error) {
786
+ console.error(\`āŒ Unexpected error: \${error.message}\`);
787
+ console.log('\\nšŸ’” Common Issues:');
788
+ console.log(' - Check all ISV environment variables are set');
789
+ console.log(' - Verify merchant has authorized your ISV application');
790
+ console.log(' - Ensure OAuth2 credentials are correct');
791
+ }
792
+
793
+ console.log(\`\\nTimestamp: \${new Date().toISOString()}\`);
794
+ console.log('\\n' + '='.repeat(60));
795
+ }
796
+
797
+ // Run the example
798
+ main();`);
799
+
800
+ return sections.join('\n\n');
801
+ }
802
+
803
+ /**
804
+ * Export all ISV transaction examples
805
+ */
806
+ export const ISVTransactionExamples = {
807
+ operations: ISVTransactionOperations,
808
+ generators: {
809
+ python: generateISVTransactionPython,
810
+ nodejs: generateISVTransactionNodeJS,
811
+ javascript: generateISVTransactionNodeJS
812
+ }
813
+ };