@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,623 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Merchant 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
|
+
* Merchant transaction operations
|
|
10
|
+
*/
|
|
11
|
+
export const MerchantTransactionOperations = {
|
|
12
|
+
create_transaction: {
|
|
13
|
+
description: 'Create a new payment transaction',
|
|
14
|
+
endpoint: '/transactions',
|
|
15
|
+
method: 'POST',
|
|
16
|
+
sampleBody: {
|
|
17
|
+
trData: {
|
|
18
|
+
amount: '25.50',
|
|
19
|
+
currency: 'EUR',
|
|
20
|
+
reasonL1: 'Product purchase',
|
|
21
|
+
reasonL2: 'Online store order #12345'
|
|
22
|
+
},
|
|
23
|
+
trOptions: {
|
|
24
|
+
type: 'QR',
|
|
25
|
+
timeToLive: 300
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
|
|
30
|
+
get_transaction_status: {
|
|
31
|
+
description: 'Get the current status of a transaction',
|
|
32
|
+
endpoint: '/transactions/{id}',
|
|
33
|
+
method: 'GET',
|
|
34
|
+
sampleBody: {}
|
|
35
|
+
},
|
|
36
|
+
|
|
37
|
+
cancel_transaction: {
|
|
38
|
+
description: 'Cancel an active transaction',
|
|
39
|
+
endpoint: '/transactions/{id}',
|
|
40
|
+
method: 'PATCH',
|
|
41
|
+
sampleBody: {
|
|
42
|
+
status: 'CANCELLED',
|
|
43
|
+
statusMessage: 'Customer requested cancellation'
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
|
|
47
|
+
process_transaction: {
|
|
48
|
+
description: 'Process a transaction as merchant',
|
|
49
|
+
endpoint: '/transactions/{id}',
|
|
50
|
+
method: 'POST',
|
|
51
|
+
sampleBody: {
|
|
52
|
+
account: 'MERCHANT_ACCOUNT_123',
|
|
53
|
+
friendlyName: 'My Store'
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
|
|
57
|
+
get_transaction_history: {
|
|
58
|
+
description: 'Get transaction history',
|
|
59
|
+
endpoint: '/transactions/{id}/history',
|
|
60
|
+
method: 'GET',
|
|
61
|
+
sampleBody: {}
|
|
62
|
+
},
|
|
63
|
+
|
|
64
|
+
simulate_callback: {
|
|
65
|
+
description: 'Simulate a transaction callback for testing',
|
|
66
|
+
endpoint: '/transactions/{id}/callback',
|
|
67
|
+
method: 'POST',
|
|
68
|
+
sampleBody: {
|
|
69
|
+
status: 'PROCESSED',
|
|
70
|
+
timestamp: new Date().toISOString()
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Generate Python examples for merchant transactions
|
|
77
|
+
*/
|
|
78
|
+
export function generateMerchantTransactionPython(operation, params = {}) {
|
|
79
|
+
const generator = new PythonGenerator(params.framework);
|
|
80
|
+
const operationConfig = MerchantTransactionOperations[operation];
|
|
81
|
+
|
|
82
|
+
if (!operationConfig) {
|
|
83
|
+
throw new Error(`Unknown operation: ${operation}`);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Customize request body with parameters
|
|
87
|
+
const customBody = { ...operationConfig.sampleBody };
|
|
88
|
+
if (params.amount) customBody.trData = { ...customBody.trData, amount: params.amount };
|
|
89
|
+
if (params.currency) customBody.trData = { ...customBody.trData, currency: params.currency };
|
|
90
|
+
if (params.reasonL1) customBody.trData = { ...customBody.trData, reasonL1: params.reasonL1 };
|
|
91
|
+
|
|
92
|
+
const sections = [];
|
|
93
|
+
|
|
94
|
+
// Header and description
|
|
95
|
+
sections.push(`"""
|
|
96
|
+
${operationConfig.description}
|
|
97
|
+
|
|
98
|
+
This example demonstrates how to ${operationConfig.description.toLowerCase()} using the payware API.
|
|
99
|
+
Partner Type: Merchant
|
|
100
|
+
Operation: ${operation}
|
|
101
|
+
Method: ${operationConfig.method}
|
|
102
|
+
Endpoint: ${operationConfig.endpoint}
|
|
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(`# Environment Configuration
|
|
122
|
+
def load_merchant_config():
|
|
123
|
+
"""Load merchant configuration from environment"""
|
|
124
|
+
config = {
|
|
125
|
+
'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
|
+
'sandbox_url': os.getenv('PAYWARE_SANDBOX_URL', 'https://sandbox.payware.eu/api'),
|
|
129
|
+
'production_url': os.getenv('PAYWARE_PRODUCTION_URL', 'https://api.payware.eu/api')
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
# Validate required configuration
|
|
133
|
+
if not config['partner_id']:
|
|
134
|
+
raise ValueError("PAYWARE_PARTNER_ID environment variable is required")
|
|
135
|
+
|
|
136
|
+
return config`);
|
|
137
|
+
|
|
138
|
+
// Authentication functions
|
|
139
|
+
sections.push(`# Authentication Functions
|
|
140
|
+
def create_jwt_token(request_body=None, use_sandbox=True):
|
|
141
|
+
"""Create JWT token for payware API authentication"""
|
|
142
|
+
config = load_merchant_config()
|
|
143
|
+
|
|
144
|
+
# Get private key path based on environment
|
|
145
|
+
private_key_path = config['sandbox_key_path'] if use_sandbox else config['production_key_path']
|
|
146
|
+
if not private_key_path:
|
|
147
|
+
env_var = 'PAYWARE_SANDBOX_PRIVATE_KEY_PATH' if use_sandbox else 'PAYWARE_PRODUCTION_PRIVATE_KEY_PATH'
|
|
148
|
+
raise ValueError(f"{env_var} environment variable is required")
|
|
149
|
+
|
|
150
|
+
# Load private key
|
|
151
|
+
with open(private_key_path, 'r') as f:
|
|
152
|
+
private_key = f.read()
|
|
153
|
+
|
|
154
|
+
# Create JWT header
|
|
155
|
+
header = {
|
|
156
|
+
'alg': 'RS256',
|
|
157
|
+
'typ': 'JWT'
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
# Add content MD5 for requests with body
|
|
161
|
+
body_string = None
|
|
162
|
+
if request_body:
|
|
163
|
+
body_string = create_minimized_json(request_body)
|
|
164
|
+
content_md5 = base64.b64encode(
|
|
165
|
+
hashlib.md5(body_string.encode('utf-8')).digest()
|
|
166
|
+
).decode('utf-8')
|
|
167
|
+
header['contentMd5'] = content_md5
|
|
168
|
+
|
|
169
|
+
# Create JWT payload
|
|
170
|
+
payload = {
|
|
171
|
+
'iss': config['partner_id'],
|
|
172
|
+
'aud': 'https://payware.eu',
|
|
173
|
+
'iat': int(datetime.utcnow().timestamp())
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
# Create token
|
|
177
|
+
token = jwt.encode(payload, private_key, algorithm='RS256', headers=header)
|
|
178
|
+
return token, body_string
|
|
179
|
+
|
|
180
|
+
def create_minimized_json(data):
|
|
181
|
+
"""Create deterministic minimized JSON for MD5 calculation"""
|
|
182
|
+
def sort_dict(obj):
|
|
183
|
+
if isinstance(obj, dict):
|
|
184
|
+
return {k: sort_dict(v) for k, v in sorted(obj.items())}
|
|
185
|
+
elif isinstance(obj, list):
|
|
186
|
+
return [sort_dict(item) for item in obj]
|
|
187
|
+
else:
|
|
188
|
+
return obj
|
|
189
|
+
|
|
190
|
+
sorted_data = sort_dict(data)
|
|
191
|
+
return json.dumps(sorted_data, separators=(',', ':'))
|
|
192
|
+
|
|
193
|
+
def get_api_headers(token):
|
|
194
|
+
"""Get standard API headers"""
|
|
195
|
+
return {
|
|
196
|
+
'Authorization': f'Bearer {token}',
|
|
197
|
+
'Content-Type': 'application/json',
|
|
198
|
+
'Api-Version': '1'
|
|
199
|
+
}`);
|
|
200
|
+
|
|
201
|
+
// Main operation function
|
|
202
|
+
const needsId = operationConfig.endpoint.includes('{id}');
|
|
203
|
+
const hasBody = operationConfig.method !== 'GET' && Object.keys(operationConfig.sampleBody).length > 0;
|
|
204
|
+
|
|
205
|
+
const paramList = ['use_sandbox=True'];
|
|
206
|
+
if (needsId) {
|
|
207
|
+
paramList.unshift('transaction_id');
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// Add custom parameters for specific operations
|
|
211
|
+
if (operation === 'create_transaction') {
|
|
212
|
+
paramList.splice(-1, 0, 'amount="25.50"', 'currency="EUR"', 'reason_l1="Product purchase"', 'reason_l2=None');
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const functionName = operation;
|
|
216
|
+
const endpoint = operationConfig.endpoint.replace('{id}', '{transaction_id}');
|
|
217
|
+
|
|
218
|
+
sections.push(`# Main Operation
|
|
219
|
+
def ${functionName}(${paramList.join(', ')}):
|
|
220
|
+
"""${operationConfig.description}"""
|
|
221
|
+
|
|
222
|
+
try:
|
|
223
|
+
config = load_merchant_config()
|
|
224
|
+
base_url = config['sandbox_url'] if use_sandbox else config['production_url']
|
|
225
|
+
|
|
226
|
+
${hasBody ? `# Prepare request body
|
|
227
|
+
request_body = ${JSON.stringify(customBody, null, 8).replace(/"/g, "'")}
|
|
228
|
+
|
|
229
|
+
${operation === 'create_transaction' ? `# Customize with parameters
|
|
230
|
+
if amount:
|
|
231
|
+
request_body['trData']['amount'] = amount
|
|
232
|
+
if currency:
|
|
233
|
+
request_body['trData']['currency'] = currency
|
|
234
|
+
if reason_l1:
|
|
235
|
+
request_body['trData']['reasonL1'] = reason_l1
|
|
236
|
+
if reason_l2:
|
|
237
|
+
request_body['trData']['reasonL2'] = reason_l2` : ''}
|
|
238
|
+
|
|
239
|
+
# Create JWT token with body hash
|
|
240
|
+
token, body_string = create_jwt_token(request_body, use_sandbox)
|
|
241
|
+
headers = get_api_headers(token)
|
|
242
|
+
|
|
243
|
+
# Make API request
|
|
244
|
+
url = f"{base_url}${endpoint}"${needsId ? `.format(transaction_id=transaction_id)` : ''}
|
|
245
|
+
response = requests.${operationConfig.method.toLowerCase()}(
|
|
246
|
+
url,
|
|
247
|
+
data=body_string,
|
|
248
|
+
headers=headers
|
|
249
|
+
)` : `# Create JWT token for GET request
|
|
250
|
+
token, _ = create_jwt_token(None, use_sandbox)
|
|
251
|
+
headers = get_api_headers(token)
|
|
252
|
+
|
|
253
|
+
# Make API request
|
|
254
|
+
url = f"{base_url}${endpoint}"${needsId ? `.format(transaction_id=transaction_id)` : ''}
|
|
255
|
+
response = requests.${operationConfig.method.toLowerCase()}(
|
|
256
|
+
url,
|
|
257
|
+
headers=headers
|
|
258
|
+
)`}
|
|
259
|
+
|
|
260
|
+
# Handle response
|
|
261
|
+
if response.status_code in [200, 201]:
|
|
262
|
+
result = response.json()
|
|
263
|
+
return {
|
|
264
|
+
'success': True,
|
|
265
|
+
'data': result,
|
|
266
|
+
'request_id': response.headers.get('x-request-id'),
|
|
267
|
+
'timestamp': datetime.utcnow().isoformat()
|
|
268
|
+
}
|
|
269
|
+
else:
|
|
270
|
+
return {
|
|
271
|
+
'success': False,
|
|
272
|
+
'error': {
|
|
273
|
+
'status': response.status_code,
|
|
274
|
+
'message': response.text,
|
|
275
|
+
'details': response.json() if response.headers.get('content-type', '').startswith('application/json') else None
|
|
276
|
+
},
|
|
277
|
+
'timestamp': datetime.utcnow().isoformat()
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
except Exception as e:
|
|
281
|
+
return {
|
|
282
|
+
'success': False,
|
|
283
|
+
'error': {
|
|
284
|
+
'message': str(e),
|
|
285
|
+
'type': type(e).__name__
|
|
286
|
+
},
|
|
287
|
+
'timestamp': datetime.utcnow().isoformat()
|
|
288
|
+
}`);
|
|
289
|
+
|
|
290
|
+
// Error handling
|
|
291
|
+
sections.push(`# Error Handling
|
|
292
|
+
def handle_payware_error(error_response):
|
|
293
|
+
"""Handle common payware API errors"""
|
|
294
|
+
status = error_response.get('status', 0)
|
|
295
|
+
message = error_response.get('message', 'Unknown error')
|
|
296
|
+
|
|
297
|
+
error_messages = {
|
|
298
|
+
400: f"Bad Request: {message}",
|
|
299
|
+
401: "Authentication failed - check your partner ID and private key",
|
|
300
|
+
403: "Forbidden - insufficient permissions",
|
|
301
|
+
404: "Resource not found",
|
|
302
|
+
409: "Conflict - resource already exists or invalid state",
|
|
303
|
+
429: "Rate limit exceeded - please retry later",
|
|
304
|
+
500: "Internal server error - please try again",
|
|
305
|
+
502: "Bad gateway - service temporarily unavailable",
|
|
306
|
+
503: "Service unavailable - please retry later"
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
return error_messages.get(status, f"API Error {status}: {message}")`);
|
|
310
|
+
|
|
311
|
+
// Example usage
|
|
312
|
+
const exampleParams = needsId ? "'pw12345678'" : '';
|
|
313
|
+
sections.push(`# Example Usage
|
|
314
|
+
if __name__ == "__main__":
|
|
315
|
+
print("=== Merchant ${operation.replace('_', ' ').title()} Example ===\\n")
|
|
316
|
+
|
|
317
|
+
try:
|
|
318
|
+
# Execute the operation
|
|
319
|
+
result = ${functionName}(${exampleParams})
|
|
320
|
+
|
|
321
|
+
if result['success']:
|
|
322
|
+
print("✅ Operation successful!")
|
|
323
|
+
print(f"Request ID: {result.get('request_id', 'N/A')}")
|
|
324
|
+
print("Response Data:")
|
|
325
|
+
print(json.dumps(result['data'], indent=2))
|
|
326
|
+
else:
|
|
327
|
+
print("❌ Operation failed!")
|
|
328
|
+
error_message = handle_payware_error(result['error'])
|
|
329
|
+
print(f"Error: {error_message}")
|
|
330
|
+
|
|
331
|
+
if result['error'].get('details'):
|
|
332
|
+
print("Error Details:")
|
|
333
|
+
print(json.dumps(result['error']['details'], indent=2))
|
|
334
|
+
|
|
335
|
+
except Exception as e:
|
|
336
|
+
print(f"❌ Unexpected error: {e}")
|
|
337
|
+
|
|
338
|
+
print(f"\\nTimestamp: {datetime.utcnow().isoformat()}")
|
|
339
|
+
print("\\n" + "="*50)`);
|
|
340
|
+
|
|
341
|
+
return sections.join('\n\n');
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* Generate Node.js examples for merchant transactions
|
|
346
|
+
*/
|
|
347
|
+
export function generateMerchantTransactionNodeJS(operation, params = {}) {
|
|
348
|
+
const generator = new NodeJSGenerator(params.framework);
|
|
349
|
+
const operationConfig = MerchantTransactionOperations[operation];
|
|
350
|
+
|
|
351
|
+
if (!operationConfig) {
|
|
352
|
+
throw new Error(`Unknown operation: ${operation}`);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
const sections = [];
|
|
356
|
+
|
|
357
|
+
// Header and description
|
|
358
|
+
sections.push(`/**
|
|
359
|
+
* ${operationConfig.description}
|
|
360
|
+
*
|
|
361
|
+
* This example demonstrates how to ${operationConfig.description.toLowerCase()} using the payware API.
|
|
362
|
+
* Partner Type: Merchant
|
|
363
|
+
* Operation: ${operation}
|
|
364
|
+
* Method: ${operationConfig.method}
|
|
365
|
+
* Endpoint: ${operationConfig.endpoint}
|
|
366
|
+
*/
|
|
367
|
+
|
|
368
|
+
// Required dependencies:
|
|
369
|
+
// npm install axios jsonwebtoken crypto fs dotenv
|
|
370
|
+
|
|
371
|
+
const axios = require('axios');
|
|
372
|
+
const jwt = require('jsonwebtoken');
|
|
373
|
+
const crypto = require('crypto');
|
|
374
|
+
const fs = require('fs');
|
|
375
|
+
require('dotenv').config();`);
|
|
376
|
+
|
|
377
|
+
// Configuration
|
|
378
|
+
sections.push(`// Configuration
|
|
379
|
+
function loadMerchantConfig() {
|
|
380
|
+
const config = {
|
|
381
|
+
partnerId: process.env.PAYWARE_PARTNER_ID,
|
|
382
|
+
sandboxKeyPath: process.env.PAYWARE_SANDBOX_PRIVATE_KEY_PATH,
|
|
383
|
+
productionKeyPath: process.env.PAYWARE_PRODUCTION_PRIVATE_KEY_PATH,
|
|
384
|
+
sandboxUrl: process.env.PAYWARE_SANDBOX_URL || 'https://sandbox.payware.eu/api',
|
|
385
|
+
productionUrl: process.env.PAYWARE_PRODUCTION_URL || 'https://api.payware.eu/api'
|
|
386
|
+
};
|
|
387
|
+
|
|
388
|
+
// Validate required configuration
|
|
389
|
+
if (!config.partnerId) {
|
|
390
|
+
throw new Error('PAYWARE_PARTNER_ID environment variable is required');
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
return config;
|
|
394
|
+
}`);
|
|
395
|
+
|
|
396
|
+
// Authentication functions
|
|
397
|
+
sections.push(`// Authentication Functions
|
|
398
|
+
function createJWTToken(requestBody = null, useSandbox = true) {
|
|
399
|
+
const config = loadMerchantConfig();
|
|
400
|
+
|
|
401
|
+
// Get private key path based on environment
|
|
402
|
+
const privateKeyPath = useSandbox ? config.sandboxKeyPath : config.productionKeyPath;
|
|
403
|
+
if (!privateKeyPath) {
|
|
404
|
+
const envVar = useSandbox ? 'PAYWARE_SANDBOX_PRIVATE_KEY_PATH' : 'PAYWARE_PRODUCTION_PRIVATE_KEY_PATH';
|
|
405
|
+
throw new Error(\`\${envVar} environment variable is required\`);
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
// Load private key
|
|
409
|
+
const privateKey = fs.readFileSync(privateKeyPath, 'utf8');
|
|
410
|
+
|
|
411
|
+
// Create JWT header
|
|
412
|
+
const header = {
|
|
413
|
+
alg: 'RS256',
|
|
414
|
+
typ: 'JWT'
|
|
415
|
+
};
|
|
416
|
+
|
|
417
|
+
// Add content MD5 for requests with body
|
|
418
|
+
let bodyString = null;
|
|
419
|
+
if (requestBody) {
|
|
420
|
+
bodyString = createMinimizedJSON(requestBody);
|
|
421
|
+
const contentMd5 = crypto
|
|
422
|
+
.createHash('md5')
|
|
423
|
+
.update(bodyString, 'utf8')
|
|
424
|
+
.digest('base64');
|
|
425
|
+
header.contentMd5 = contentMd5;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
// Create JWT payload
|
|
429
|
+
const payload = {
|
|
430
|
+
iss: config.partnerId,
|
|
431
|
+
aud: 'https://payware.eu',
|
|
432
|
+
iat: Math.floor(Date.now() / 1000)
|
|
433
|
+
};
|
|
434
|
+
|
|
435
|
+
// Create token
|
|
436
|
+
const token = jwt.sign(payload, privateKey, {
|
|
437
|
+
algorithm: 'RS256',
|
|
438
|
+
header
|
|
439
|
+
});
|
|
440
|
+
|
|
441
|
+
return { token, bodyString };
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
function createMinimizedJSON(data) {
|
|
445
|
+
// Sort object keys recursively for deterministic output
|
|
446
|
+
function sortObject(obj) {
|
|
447
|
+
if (Array.isArray(obj)) {
|
|
448
|
+
return obj.map(item => sortObject(item));
|
|
449
|
+
} else if (obj !== null && typeof obj === 'object') {
|
|
450
|
+
const sorted = {};
|
|
451
|
+
Object.keys(obj).sort().forEach(key => {
|
|
452
|
+
sorted[key] = sortObject(obj[key]);
|
|
453
|
+
});
|
|
454
|
+
return sorted;
|
|
455
|
+
}
|
|
456
|
+
return obj;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
const sortedData = sortObject(data);
|
|
460
|
+
return JSON.stringify(sortedData);
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
function getAPIHeaders(token) {
|
|
464
|
+
return {
|
|
465
|
+
'Authorization': \`Bearer \${token}\`,
|
|
466
|
+
'Content-Type': 'application/json',
|
|
467
|
+
'Api-Version': '1'
|
|
468
|
+
};
|
|
469
|
+
}`);
|
|
470
|
+
|
|
471
|
+
// Main operation function
|
|
472
|
+
const needsId = operationConfig.endpoint.includes('{id}');
|
|
473
|
+
const hasBody = operationConfig.method !== 'GET' && Object.keys(operationConfig.sampleBody).length > 0;
|
|
474
|
+
|
|
475
|
+
const paramList = ['useSandbox = true'];
|
|
476
|
+
if (needsId) {
|
|
477
|
+
paramList.unshift('transactionId');
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
// Add custom parameters for specific operations
|
|
481
|
+
if (operation === 'create_transaction') {
|
|
482
|
+
paramList.splice(-1, 0, 'amount = "25.50"', 'currency = "EUR"', 'reasonL1 = "Product purchase"', 'reasonL2 = null');
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
const endpoint = operationConfig.endpoint.replace('{id}', '${transactionId}');
|
|
486
|
+
|
|
487
|
+
sections.push(`// Main Operation
|
|
488
|
+
async function ${operation}(${paramList.join(', ')}) {
|
|
489
|
+
/**
|
|
490
|
+
* ${operationConfig.description}
|
|
491
|
+
*/
|
|
492
|
+
|
|
493
|
+
try {
|
|
494
|
+
const config = loadMerchantConfig();
|
|
495
|
+
const baseUrl = useSandbox ? config.sandboxUrl : config.productionUrl;
|
|
496
|
+
|
|
497
|
+
${hasBody ? `// Prepare request body
|
|
498
|
+
const requestBody = ${JSON.stringify(operationConfig.sampleBody, null, 4)};
|
|
499
|
+
|
|
500
|
+
${operation === 'create_transaction' ? `// Customize with parameters
|
|
501
|
+
if (amount) requestBody.trData.amount = amount;
|
|
502
|
+
if (currency) requestBody.trData.currency = currency;
|
|
503
|
+
if (reasonL1) requestBody.trData.reasonL1 = reasonL1;
|
|
504
|
+
if (reasonL2) requestBody.trData.reasonL2 = reasonL2;` : ''}
|
|
505
|
+
|
|
506
|
+
// Create JWT token with body hash
|
|
507
|
+
const { token, bodyString } = createJWTToken(requestBody, useSandbox);
|
|
508
|
+
const headers = getAPIHeaders(token);
|
|
509
|
+
|
|
510
|
+
// Make API request
|
|
511
|
+
const url = \`\${baseUrl}${endpoint}\`;
|
|
512
|
+
const response = await axios.${operationConfig.method.toLowerCase()}(
|
|
513
|
+
url,
|
|
514
|
+
bodyString,
|
|
515
|
+
{
|
|
516
|
+
headers,
|
|
517
|
+
transformRequest: [(data) => data]
|
|
518
|
+
}
|
|
519
|
+
);` : `// Create JWT token for GET request
|
|
520
|
+
const { token } = createJWTToken(null, useSandbox);
|
|
521
|
+
const headers = getAPIHeaders(token);
|
|
522
|
+
|
|
523
|
+
// Make API request
|
|
524
|
+
const url = \`\${baseUrl}${endpoint}\`;
|
|
525
|
+
const response = await axios.${operationConfig.method.toLowerCase()}(
|
|
526
|
+
url,
|
|
527
|
+
{ headers }
|
|
528
|
+
);`}
|
|
529
|
+
|
|
530
|
+
// Handle successful response
|
|
531
|
+
return {
|
|
532
|
+
success: true,
|
|
533
|
+
data: response.data,
|
|
534
|
+
requestId: response.headers['x-request-id'],
|
|
535
|
+
timestamp: new Date().toISOString()
|
|
536
|
+
};
|
|
537
|
+
|
|
538
|
+
} catch (error) {
|
|
539
|
+
// Handle errors
|
|
540
|
+
return {
|
|
541
|
+
success: false,
|
|
542
|
+
error: {
|
|
543
|
+
message: error.response?.data?.message || error.message,
|
|
544
|
+
status: error.response?.status,
|
|
545
|
+
details: error.response?.data
|
|
546
|
+
},
|
|
547
|
+
timestamp: new Date().toISOString()
|
|
548
|
+
};
|
|
549
|
+
}
|
|
550
|
+
}`);
|
|
551
|
+
|
|
552
|
+
// Error handling
|
|
553
|
+
sections.push(`// Error Handling
|
|
554
|
+
function handlePaywareError(errorResponse) {
|
|
555
|
+
const status = errorResponse.status || 0;
|
|
556
|
+
const message = errorResponse.message || 'Unknown error';
|
|
557
|
+
|
|
558
|
+
const errorMessages = {
|
|
559
|
+
400: \`Bad Request: \${message}\`,
|
|
560
|
+
401: 'Authentication failed - check your partner ID and private key',
|
|
561
|
+
403: 'Forbidden - insufficient permissions',
|
|
562
|
+
404: 'Resource not found',
|
|
563
|
+
409: 'Conflict - resource already exists or invalid state',
|
|
564
|
+
429: 'Rate limit exceeded - please retry later',
|
|
565
|
+
500: 'Internal server error - please try again',
|
|
566
|
+
502: 'Bad gateway - service temporarily unavailable',
|
|
567
|
+
503: 'Service unavailable - please retry later'
|
|
568
|
+
};
|
|
569
|
+
|
|
570
|
+
return errorMessages[status] || \`API Error \${status}: \${message}\`;
|
|
571
|
+
}`);
|
|
572
|
+
|
|
573
|
+
// Example usage
|
|
574
|
+
const exampleParams = needsId ? "'pw12345678'" : '';
|
|
575
|
+
sections.push(`// Example Usage
|
|
576
|
+
async function main() {
|
|
577
|
+
console.log('=== Merchant ${operation.replace(/_/g, ' ').replace(/\\b\\w/g, l => l.toUpperCase())} Example ===\\n');
|
|
578
|
+
|
|
579
|
+
try {
|
|
580
|
+
// Execute the operation
|
|
581
|
+
const result = await ${operation}(${exampleParams});
|
|
582
|
+
|
|
583
|
+
if (result.success) {
|
|
584
|
+
console.log('✅ Operation successful!');
|
|
585
|
+
console.log(\`Request ID: \${result.requestId || 'N/A'}\`);
|
|
586
|
+
console.log('Response Data:');
|
|
587
|
+
console.log(JSON.stringify(result.data, null, 2));
|
|
588
|
+
} else {
|
|
589
|
+
console.log('❌ Operation failed!');
|
|
590
|
+
const errorMessage = handlePaywareError(result.error);
|
|
591
|
+
console.log(\`Error: \${errorMessage}\`);
|
|
592
|
+
|
|
593
|
+
if (result.error.details) {
|
|
594
|
+
console.log('Error Details:');
|
|
595
|
+
console.log(JSON.stringify(result.error.details, null, 2));
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
} catch (error) {
|
|
600
|
+
console.error(\`❌ Unexpected error: \${error.message}\`);
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
console.log(\`\\nTimestamp: \${new Date().toISOString()}\`);
|
|
604
|
+
console.log('\\n' + '='.repeat(50));
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
// Run the example
|
|
608
|
+
main();`);
|
|
609
|
+
|
|
610
|
+
return sections.join('\n\n');
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
/**
|
|
614
|
+
* Export all merchant transaction examples
|
|
615
|
+
*/
|
|
616
|
+
export const MerchantTransactionExamples = {
|
|
617
|
+
operations: MerchantTransactionOperations,
|
|
618
|
+
generators: {
|
|
619
|
+
python: generateMerchantTransactionPython,
|
|
620
|
+
nodejs: generateMerchantTransactionNodeJS,
|
|
621
|
+
javascript: generateMerchantTransactionNodeJS
|
|
622
|
+
}
|
|
623
|
+
};
|