@payware/mcp-server 1.2.0 → 1.3.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 (58) hide show
  1. package/README.md +5 -0
  2. package/package.json +69 -66
  3. package/src/auth/sandbox-setup.js +186 -186
  4. package/src/isv/index.js +29 -1
  5. package/src/isv/invitations/index.js +471 -0
  6. package/src/isv/oauth2/obtain-token.js +19 -8
  7. package/src/isv/oauth2/token-helper.js +5 -2
  8. package/src/isv/poi/cancel-price.js +2 -10
  9. package/src/isv/poi/create.js +19 -0
  10. package/src/isv/poi/get-poi.js +2 -10
  11. package/src/isv/poi/get-qrcode.js +2 -10
  12. package/src/isv/poi/get-status.js +2 -10
  13. package/src/isv/poi/index.js +5 -0
  14. package/src/isv/poi/list-pois.js +2 -10
  15. package/src/isv/poi/set-price.js +217 -225
  16. package/src/isv/products/create-product.js +313 -313
  17. package/src/isv/products/update-product.js +264 -264
  18. package/src/isv/reference/index.js +249 -0
  19. package/src/isv/shops/index.js +22 -0
  20. package/src/isv/transactions/cancel-transaction.js +41 -10
  21. package/src/isv/transactions/create-transaction.js +20 -4
  22. package/src/isv/transactions/get-status.js +18 -11
  23. package/src/isv/transactions/process-transaction.js +160 -42
  24. package/src/isv/transactions/simulate-callback.js +128 -109
  25. package/src/isv/transactions/transaction-history.js +86 -90
  26. package/src/merchant/data/generate-report.js +19 -1
  27. package/src/merchant/index.js +21 -1
  28. package/src/merchant/logs/index.js +366 -0
  29. package/src/merchant/products/create-product.js +287 -287
  30. package/src/merchant/products/update-product.js +281 -281
  31. package/src/merchant/shops/index.js +18 -0
  32. package/src/merchant/transactions/cancel-transaction.js +10 -13
  33. package/src/merchant/transactions/create-transaction.js +88 -8
  34. package/src/merchant/transactions/get-status.js +40 -11
  35. package/src/merchant/transactions/process-transaction.js +2 -10
  36. package/src/merchant/transactions/simulate-callback.js +68 -9
  37. package/src/merchant/transactions/transaction-history.js +275 -257
  38. package/src/payment-institution/transactions/create-pi-transaction.js +2 -10
  39. package/src/payment-institution/transactions/finalize-transaction.js +333 -340
  40. package/src/payment-institution/transactions/get-transaction-history.js +2 -10
  41. package/src/payment-institution/transactions/get-transaction-status.js +38 -11
  42. package/src/payment-institution/transactions/process-pi-transaction.js +2 -10
  43. package/src/payment-institution/transactions/simulate-pi-callback.js +49 -9
  44. package/src/payment-institution/transactions/soundbite-transaction.js +2 -10
  45. package/src/shared/api-errors.js +188 -0
  46. package/src/shared/auth-headers.js +62 -0
  47. package/src/shared/data/data-api.js +11 -10
  48. package/src/shared/deep-links/api.js +5 -4
  49. package/src/shared/logs/logs-api.js +242 -0
  50. package/src/shared/poi/poi-create-api.js +419 -0
  51. package/src/shared/products/products-api.js +17 -16
  52. package/src/shared/shops/shops-api.js +155 -0
  53. package/src/tools/advanced-code-generator.js +2255 -2246
  54. package/src/utils/code-examples/common/auth-examples.js +1484 -1481
  55. package/src/utils/code-examples/common/helpers.js +7 -4
  56. package/src/utils/code-examples/isv/oauth2.js +458 -600
  57. package/src/utils/code-examples/isv/transactions.js +815 -812
  58. package/MCP-SETUP.md +0 -190
@@ -1,1482 +1,1485 @@
1
- /**
2
- * Authentication examples for all partner types and languages
3
- */
4
-
5
- import { ExampleGenerator, CommonTemplates } from './helpers.js';
6
-
7
- /**
8
- * Python authentication examples
9
- */
10
- export class PythonAuthGenerator extends ExampleGenerator {
11
- constructor() {
12
- super('python');
13
- }
14
-
15
- getEnvironmentTemplate(partnerType, options = {}) {
16
- const envVars = CommonTemplates.getEnvironmentVariables(partnerType);
17
- const envString = Object.entries(envVars)
18
- .map(([key, value]) => `${key}=${value}`)
19
- .join('\n');
20
-
21
- return `${options.includeComments ? '# .env file configuration\n' : ''}${envString}
22
-
23
- ${options.includeComments ? '# Load environment variables\n' : ''}import os
24
- from dotenv import load_dotenv
25
- load_dotenv()`;
26
- }
27
-
28
- getDependenciesTemplate(options = {}) {
29
- return `${options.includeComments ? '# Install required dependencies:\n# pip install requests pyjwt cryptography python-dotenv\n\n' : ''}import requests
30
- import jwt
31
- import json
32
- import hashlib
33
- import base64
34
- import os
35
- from datetime import datetime, timedelta
36
- from cryptography.hazmat.primitives import serialization`;
37
- }
38
-
39
- getAuthTemplate(partnerType, options = {}) {
40
- const isISV = partnerType === 'isv';
41
-
42
- return `def create_jwt_token(request_body=None, use_sandbox=True):
43
- ${options.includeComments ? '"""Create JWT token for payware API authentication"""\n\n ' : ''}${options.includeComments ? '# Get configuration from environment\n ' : ''}partner_id = os.getenv('PAYWARE_PARTNER_ID')
44
- if use_sandbox:
45
- private_key_path = os.getenv('PAYWARE_SANDBOX_PRIVATE_KEY_PATH')
46
- else:
47
- private_key_path = os.getenv('PAYWARE_PRODUCTION_PRIVATE_KEY_PATH')
48
-
49
- if not partner_id or not private_key_path:
50
- raise ValueError("Missing required environment variables")
51
-
52
- ${options.includeComments ? '# Load private key\n ' : ''}with open(private_key_path, 'r') as f:
53
- private_key = f.read()
54
-
55
- ${options.includeComments ? '# Create JWT header\n ' : ''}header = {
56
- 'alg': 'RS256',
57
- 'typ': 'JWT'
58
- }
59
-
60
- ${options.includeComments ? '# Add content SHA-256 for requests with body\n ' : ''}body_string = None
61
- if request_body:
62
- body_string = create_minimized_json(request_body)
63
- content_sha256 = base64.b64encode(
64
- hashlib.sha256(body_string.encode('utf-8')).digest()
65
- ).decode('utf-8')
66
- header['contentSha256'] = content_sha256
67
-
68
- ${options.includeComments ? '# Create JWT payload\n ' : ''}payload = {
69
- 'iss': partner_id,
70
- 'aud': 'https://payware.eu',
71
- 'iat': int(datetime.utcnow().timestamp())
72
- }
73
-
74
- ${options.includeComments ? '# Create and return token\n ' : ''}token = jwt.encode(payload, private_key, algorithm='RS256', headers=header)
75
- return token, body_string
76
-
77
- def create_minimized_json(data):
78
- ${options.includeComments ? '"""Create deterministic minimized JSON for SHA-256 calculation"""\n ' : ''}def sort_dict(obj):
79
- if isinstance(obj, dict):
80
- return {k: sort_dict(v) for k, v in sorted(obj.items())}
81
- elif isinstance(obj, list):
82
- return [sort_dict(item) for item in obj]
83
- else:
84
- return obj
85
-
86
- sorted_data = sort_dict(data)
87
- return json.dumps(sorted_data, separators=(',', ':'))
88
-
89
- def get_api_headers(token):
90
- ${options.includeComments ? '"""Get standard API headers with authentication"""\n ' : ''}return {
91
- 'Authorization': f'Bearer {token}',
92
- 'Content-Type': 'application/json',
93
- 'Api-Version': '1'
94
- }
95
-
96
- def get_api_base_url(use_sandbox=True):
97
- ${options.includeComments ? '"""Get API base URL based on environment"""\n ' : ''}if use_sandbox:
98
- return os.getenv('PAYWARE_SANDBOX_URL', 'https://sandbox.payware.eu/api')
99
- else:
100
- return os.getenv('PAYWARE_PRODUCTION_URL', 'https://api.payware.eu/api')${isISV ? `
101
-
102
- ${options.includeComments ? '# ISV-specific OAuth2 authentication\n' : ''}def get_oauth2_token(merchant_partner_id):
103
- ${options.includeComments ? '"""Get OAuth2 token for ISV operations"""\n ' : ''}client_id = os.getenv('PAYWARE_OAUTH_CLIENT_ID')
104
- client_secret = os.getenv('PAYWARE_OAUTH_CLIENT_SECRET')
105
-
106
- if not client_id or not client_secret:
107
- raise ValueError("Missing OAuth2 credentials")
108
-
109
- ${options.includeComments ? '# OAuth2 token request\n ' : ''}token_data = {
110
- 'grant_type': 'client_credentials',
111
- 'client_id': client_id,
112
- 'client_secret': client_secret,
113
- 'scope': f'merchant:{merchant_partner_id}'
114
- }
115
-
116
- response = requests.post(
117
- f"{get_api_base_url()}/oauth2/token",
118
- data=token_data
119
- )
120
-
121
- if response.status_code == 200:
122
- return response.json()['access_token']
123
- else:
124
- raise Exception(f"OAuth2 token request failed: {response.text}")` : ''}`;
125
- }
126
- }
127
-
128
- /**
129
- * Node.js authentication examples
130
- */
131
- export class NodeJSAuthGenerator extends ExampleGenerator {
132
- constructor() {
133
- super('nodejs');
134
- }
135
-
136
- getEnvironmentTemplate(partnerType, options = {}) {
137
- const envVars = CommonTemplates.getEnvironmentVariables(partnerType);
138
- const envString = Object.entries(envVars)
139
- .map(([key, value]) => `${key}=${value}`)
140
- .join('\n');
141
-
142
- return `${options.includeComments ? '// .env file configuration\n' : ''}${envString}
143
-
144
- ${options.includeComments ? '// Load environment variables\n' : ''}require('dotenv').config();`;
145
- }
146
-
147
- getDependenciesTemplate(options = {}) {
148
- return `${options.includeComments ? '// Install required dependencies:\n// npm install axios jsonwebtoken crypto fs dotenv\n\n' : ''}const axios = require('axios');
149
- const jwt = require('jsonwebtoken');
150
- const crypto = require('crypto');
151
- const fs = require('fs');
152
- require('dotenv').config();`;
153
- }
154
-
155
- getAuthTemplate(partnerType, options = {}) {
156
- const isISV = partnerType === 'isv';
157
-
158
- return `function createJWTToken(requestBody = null, useSandbox = true) {
159
- ${options.includeComments ? '// Get configuration from environment\n ' : ''}const partnerId = process.env.PAYWARE_PARTNER_ID;
160
- const privateKeyPath = useSandbox ?
161
- process.env.PAYWARE_SANDBOX_PRIVATE_KEY_PATH :
162
- process.env.PAYWARE_PRODUCTION_PRIVATE_KEY_PATH;
163
-
164
- if (!partnerId || !privateKeyPath) {
165
- throw new Error('Missing required environment variables');
166
- }
167
-
168
- ${options.includeComments ? '// Load private key\n ' : ''}const privateKey = fs.readFileSync(privateKeyPath, 'utf8');
169
-
170
- ${options.includeComments ? '// Create JWT header\n ' : ''}const header = {
171
- alg: 'RS256',
172
- typ: 'JWT'
173
- };
174
-
175
- ${options.includeComments ? '// Add content SHA-256 for requests with body\n ' : ''}let bodyString = null;
176
- if (requestBody) {
177
- bodyString = createMinimizedJSON(requestBody);
178
- const contentSha256 = crypto
179
- .createHash('sha256')
180
- .update(bodyString, 'utf8')
181
- .digest('base64');
182
- header.contentSha256 = contentSha256;
183
- }
184
-
185
- ${options.includeComments ? '// Create JWT payload\n ' : ''}const payload = {
186
- iss: partnerId,
187
- aud: 'https://payware.eu',
188
- iat: Math.floor(Date.now() / 1000)
189
- };
190
-
191
- ${options.includeComments ? '// Create and return token\n ' : ''}const token = jwt.sign(payload, privateKey, {
192
- algorithm: 'RS256',
193
- header
194
- });
195
-
196
- return { token, bodyString };
197
- }
198
-
199
- function createMinimizedJSON(data) {
200
- ${options.includeComments ? '// Sort object keys recursively for deterministic output\n ' : ''}function sortObject(obj) {
201
- if (Array.isArray(obj)) {
202
- return obj.map(item => sortObject(item));
203
- } else if (obj !== null && typeof obj === 'object') {
204
- const sorted = {};
205
- Object.keys(obj).sort().forEach(key => {
206
- sorted[key] = sortObject(obj[key]);
207
- });
208
- return sorted;
209
- }
210
- return obj;
211
- }
212
-
213
- const sortedData = sortObject(data);
214
- return JSON.stringify(sortedData);
215
- }
216
-
217
- function getAPIHeaders(token) {
218
- return {
219
- 'Authorization': \`Bearer \${token}\`,
220
- 'Content-Type': 'application/json',
221
- 'Api-Version': '1'
222
- };
223
- }
224
-
225
- function getAPIBaseURL(useSandbox = true) {
226
- if (useSandbox) {
227
- return process.env.PAYWARE_SANDBOX_URL || 'https://sandbox.payware.eu/api';
228
- } else {
229
- return process.env.PAYWARE_PRODUCTION_URL || 'https://api.payware.eu/api';
230
- }
231
- }${isISV ? `
232
-
233
- ${options.includeComments ? '// ISV-specific OAuth2 authentication\n' : ''}async function getOAuth2Token(merchantPartnerId) {
234
- const clientId = process.env.PAYWARE_OAUTH_CLIENT_ID;
235
- const clientSecret = process.env.PAYWARE_OAUTH_CLIENT_SECRET;
236
-
237
- if (!clientId || !clientSecret) {
238
- throw new Error('Missing OAuth2 credentials');
239
- }
240
-
241
- const tokenData = {
242
- grant_type: 'client_credentials',
243
- client_id: clientId,
244
- client_secret: clientSecret,
245
- scope: \`merchant:\${merchantPartnerId}\`
246
- };
247
-
248
- try {
249
- const response = await axios.post(
250
- \`\${getAPIBaseURL()}/oauth2/token\`,
251
- tokenData
252
- );
253
-
254
- return response.data.access_token;
255
- } catch (error) {
256
- throw new Error(\`OAuth2 token request failed: \${error.response?.data || error.message}\`);
257
- }
258
- }` : ''}`;
259
- }
260
- }
261
-
262
- /**
263
- * PHP authentication examples
264
- */
265
- export class PHPAuthGenerator extends ExampleGenerator {
266
- constructor() {
267
- super('php');
268
- }
269
-
270
- getEnvironmentTemplate(partnerType, options = {}) {
271
- const envVars = CommonTemplates.getEnvironmentVariables(partnerType);
272
- const envString = Object.entries(envVars)
273
- .map(([key, value]) => `${key}=${value}`)
274
- .join('\n');
275
-
276
- return `<?php
277
- ${options.includeComments ? '// .env file configuration\n' : ''}${envString}
278
-
279
- ${options.includeComments ? '// Load environment variables\n' : ''}$dotenv = Dotenv\\Dotenv::createImmutable(__DIR__);
280
- $dotenv->load();`;
281
- }
282
-
283
- getDependenciesTemplate(options = {}) {
284
- return `<?php
285
- ${options.includeComments ? '// Install required dependencies:\n// composer require firebase/php-jwt guzzlehttp/guzzle vlucas/phpdotenv\n\n' : ''}require_once 'vendor/autoload.php';
286
-
287
- use Firebase\\JWT\\JWT;
288
- use Firebase\\JWT\\Key;
289
- use GuzzleHttp\\Client;
290
- use GuzzleHttp\\Exception\\RequestException;`;
291
- }
292
-
293
- getAuthTemplate(partnerType, options = {}) {
294
- return `<?php
295
- class PaywareAuth {
296
-
297
- public static function createJWTToken($requestBody = null, $useSandbox = true) {
298
- ${options.includeComments ? '// Get configuration from environment\n ' : ''}$partnerId = $_ENV['PAYWARE_PARTNER_ID'];
299
- $privateKeyPath = $useSandbox ?
300
- $_ENV['PAYWARE_SANDBOX_PRIVATE_KEY_PATH'] :
301
- $_ENV['PAYWARE_PRODUCTION_PRIVATE_KEY_PATH'];
302
-
303
- if (!$partnerId || !$privateKeyPath) {
304
- throw new Exception('Missing required environment variables');
305
- }
306
-
307
- ${options.includeComments ? '// Load private key\n ' : ''}$privateKey = file_get_contents($privateKeyPath);
308
- if (!$privateKey) {
309
- throw new Exception('Unable to load private key');
310
- }
311
-
312
- ${options.includeComments ? '// Create JWT header\n ' : ''}$header = [
313
- 'alg' => 'RS256',
314
- 'typ' => 'JWT'
315
- ];
316
-
317
- ${options.includeComments ? '// Add content SHA-256 for requests with body\n ' : ''}$bodyString = null;
318
- if ($requestBody) {
319
- $bodyString = self::createMinimizedJSON($requestBody);
320
- $contentSha256 = base64_encode(hash('sha256', $bodyString, true));
321
- $header['contentSha256'] = $contentSha256;
322
- }
323
-
324
- ${options.includeComments ? '// Create JWT payload\n ' : ''}$payload = [
325
- 'iss' => $partnerId,
326
- 'aud' => 'https://payware.eu',
327
- 'iat' => time()
328
- ];
329
-
330
- ${options.includeComments ? '// Create and return token\n ' : ''}$token = JWT::encode($payload, $privateKey, 'RS256', null, $header);
331
-
332
- return [
333
- 'token' => $token,
334
- 'bodyString' => $bodyString
335
- ];
336
- }
337
-
338
- private static function createMinimizedJSON($data) {
339
- ${options.includeComments ? '// Sort array recursively for deterministic output\n ' : ''}if (is_array($data)) {
340
- ksort($data);
341
- foreach ($data as $key => $value) {
342
- $data[$key] = self::createMinimizedJSON($value);
343
- }
344
- }
345
-
346
- return json_encode($data, JSON_UNESCAPED_SLASHES);
347
- }
348
-
349
- public static function getAPIHeaders($token) {
350
- return [
351
- 'Authorization' => 'Bearer ' . $token,
352
- 'Content-Type' => 'application/json',
353
- 'Api-Version' => '1'
354
- ];
355
- }
356
-
357
- public static function getAPIBaseURL($useSandbox = true) {
358
- if ($useSandbox) {
359
- return $_ENV['PAYWARE_SANDBOX_URL'] ?? 'https://sandbox.payware.eu/api';
360
- } else {
361
- return $_ENV['PAYWARE_PRODUCTION_URL'] ?? 'https://api.payware.eu/api';
362
- }
363
- }
364
- }
365
- ?>`;
366
- }
367
- }
368
-
369
- /**
370
- * Java authentication examples
371
- */
372
- export class JavaAuthGenerator extends ExampleGenerator {
373
- constructor() {
374
- super('java');
375
- }
376
-
377
- getEnvironmentTemplate(partnerType, options = {}) {
378
- const envVars = CommonTemplates.getEnvironmentVariables(partnerType);
379
- const envString = Object.entries(envVars)
380
- .map(([key, value]) => `${key}=${value}`)
381
- .join('\n');
382
-
383
- return `${options.includeComments ? '// application.properties configuration\n' : ''}${envString}
384
-
385
- ${options.includeComments ? '// Load configuration in your application\n' : ''}@Configuration
386
- @PropertySource("classpath:application.properties")
387
- public class PaywareConfig {
388
- @Value("\${payware.partner.id}")
389
- private String partnerId;
390
-
391
- @Value("\${payware.sandbox.private.key.path}")
392
- private String sandboxPrivateKeyPath;
393
-
394
- ${options.includeComments ? '// ... other properties\n' : ''}
395
- }`;
396
- }
397
-
398
- getDependenciesTemplate(options = {}) {
399
- return `${options.includeComments ? '// Add dependencies to pom.xml:\n/*\n<dependencies>\n <dependency>\n <groupId>io.jsonwebtoken</groupId>\n <artifactId>jjwt-api</artifactId>\n <version>0.11.5</version>\n </dependency>\n <dependency>\n <groupId>io.jsonwebtoken</groupId>\n <artifactId>jjwt-impl</artifactId>\n <version>0.11.5</version>\n </dependency>\n <dependency>\n <groupId>io.jsonwebtoken</groupId>\n <artifactId>jjwt-jackson</artifactId>\n <version>0.11.5</version>\n </dependency>\n <dependency>\n <groupId>com.squareup.okhttp3</groupId>\n <artifactId>okhttp</artifactId>\n <version>4.10.0</version>\n </dependency>\n <dependency>\n <groupId>com.fasterxml.jackson.core</groupId>\n <artifactId>jackson-databind</artifactId>\n <version>2.15.2</version>\n </dependency>\n</dependencies>\n*/\n\n' : ''}import io.jsonwebtoken.Jwts;
400
- import io.jsonwebtoken.SignatureAlgorithm;
401
- import io.jsonwebtoken.security.Keys;
402
- import okhttp3.*;
403
- import com.fasterxml.jackson.databind.ObjectMapper;
404
- import com.fasterxml.jackson.databind.JsonNode;
405
-
406
- import java.security.KeyFactory;
407
- import java.security.PrivateKey;
408
- import java.security.spec.PKCS8EncodedKeySpec;
409
- import java.nio.file.Files;
410
- import java.nio.file.Paths;
411
- import java.util.*;
412
- import java.time.Instant;
413
- import java.security.MessageDigest;`;
414
- }
415
-
416
- getAuthTemplate(partnerType, options = {}) {
417
- const isISV = partnerType === 'isv';
418
-
419
- return `public class PaywareAuthenticator {
420
-
421
- private static final String PAYWARE_AUDIENCE = "https://payware.eu";
422
- private String partnerId;
423
- private String privateKeyPath;
424
- private ObjectMapper objectMapper;
425
- private OkHttpClient httpClient;
426
-
427
- public PaywareAuthenticator(String partnerId, String privateKeyPath) {
428
- this.partnerId = partnerId;
429
- this.privateKeyPath = privateKeyPath;
430
- this.objectMapper = new ObjectMapper();
431
- this.httpClient = new OkHttpClient();
432
- }
433
-
434
- public TokenResult createJWTToken(Object requestBody, boolean useSandbox) throws Exception {
435
- ${options.includeComments ? '// Load private key\n ' : ''}PrivateKey privateKey = loadPrivateKey();
436
-
437
- ${options.includeComments ? '// Create JWT header\n ' : ''}Map<String, Object> header = new HashMap<>();
438
- header.put("alg", "RS256");
439
- header.put("typ", "JWT");
440
-
441
- ${options.includeComments ? '// Handle request body and SHA-256\n ' : ''}String bodyString = null;
442
- if (requestBody != null) {
443
- bodyString = createMinimizedJSON(requestBody);
444
- String contentSha256 = calculateSHA256(bodyString);
445
- header.put("contentSha256", contentSha256);
446
- }
447
-
448
- ${options.includeComments ? '// Create JWT payload\n ' : ''}Map<String, Object> payload = new HashMap<>();
449
- payload.put("iss", partnerId);
450
- payload.put("aud", PAYWARE_AUDIENCE);
451
- payload.put("iat", Instant.now().getEpochSecond());
452
-
453
- ${options.includeComments ? '// Create JWT token\n ' : ''}String token = Jwts.builder()
454
- .setHeader(header)
455
- .setClaims(payload)
456
- .signWith(privateKey, SignatureAlgorithm.RS256)
457
- .compact();
458
-
459
- return new TokenResult(token, bodyString);
460
- }
461
-
462
- private PrivateKey loadPrivateKey() throws Exception {
463
- String keyContent = new String(Files.readAllBytes(Paths.get(privateKeyPath)));
464
-
465
- ${options.includeComments ? '// Remove PEM headers and whitespace\n ' : ''}keyContent = keyContent
466
- .replace("-----BEGIN PRIVATE KEY-----", "")
467
- .replace("-----END PRIVATE KEY-----", "")
468
- .replaceAll("\\\\s", "");
469
-
470
- byte[] keyBytes = Base64.getDecoder().decode(keyContent);
471
- PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes);
472
- KeyFactory keyFactory = KeyFactory.getInstance("RSA");
473
-
474
- return keyFactory.generatePrivate(spec);
475
- }
476
-
477
- private String createMinimizedJSON(Object data) throws Exception {
478
- ${options.includeComments ? '// Sort keys recursively for deterministic output\n ' : ''}JsonNode sortedNode = objectMapper.valueToTree(data);
479
- sortedNode = sortObjectKeys(sortedNode);
480
-
481
- return objectMapper.writeValueAsString(sortedNode);
482
- }
483
-
484
- private JsonNode sortObjectKeys(JsonNode node) {
485
- if (node.isObject()) {
486
- Map<String, JsonNode> sorted = new TreeMap<>();
487
- node.fields().forEachRemaining(entry -> {
488
- sorted.put(entry.getKey(), sortObjectKeys(entry.getValue()));
489
- });
490
- return objectMapper.valueToTree(sorted);
491
- } else if (node.isArray()) {
492
- List<JsonNode> sortedArray = new ArrayList<>();
493
- node.elements().forEachRemaining(element -> {
494
- sortedArray.add(sortObjectKeys(element));
495
- });
496
- return objectMapper.valueToTree(sortedArray);
497
- }
498
- return node;
499
- }
500
-
501
- private String calculateSHA256(String input) throws Exception {
502
- MessageDigest md = MessageDigest.getInstance("SHA-256");
503
- byte[] digest = md.digest(input.getBytes("UTF-8"));
504
- return Base64.getEncoder().encodeToString(digest);
505
- }
506
-
507
- public Map<String, String> getAPIHeaders(String token) {
508
- Map<String, String> headers = new HashMap<>();
509
- headers.put("Authorization", "Bearer " + token);
510
- headers.put("Content-Type", "application/json");
511
- headers.put("Api-Version", "1");
512
- return headers;
513
- }
514
-
515
- public String getAPIBaseURL(boolean useSandbox) {
516
- if (useSandbox) {
517
- return System.getProperty("payware.sandbox.url", "https://sandbox.payware.eu/api");
518
- } else {
519
- return System.getProperty("payware.production.url", "https://api.payware.eu/api");
520
- }
521
- }${isISV ? `
522
-
523
- ${options.includeComments ? '// ISV-specific OAuth2 authentication\n ' : ''}public String getOAuth2Token(String merchantPartnerId) throws Exception {
524
- String clientId = System.getProperty("payware.oauth.client.id");
525
- String clientSecret = System.getProperty("payware.oauth.client.secret");
526
-
527
- if (clientId == null || clientSecret == null) {
528
- throw new IllegalArgumentException("Missing OAuth2 credentials");
529
- }
530
-
531
- ${options.includeComments ? '// Create form data\n ' : ''}FormBody formBody = new FormBody.Builder()
532
- .add("grant_type", "client_credentials")
533
- .add("client_id", clientId)
534
- .add("client_secret", clientSecret)
535
- .add("scope", "merchant:" + merchantPartnerId)
536
- .build();
537
-
538
- Request request = new Request.Builder()
539
- .url(getAPIBaseURL(true) + "/oauth2/token")
540
- .post(formBody)
541
- .build();
542
-
543
- try (Response response = httpClient.newCall(request).execute()) {
544
- if (response.isSuccessful()) {
545
- String responseBody = response.body().string();
546
- JsonNode jsonResponse = objectMapper.readTree(responseBody);
547
- return jsonResponse.get("access_token").asText();
548
- } else {
549
- throw new RuntimeException("OAuth2 token request failed: " + response.code());
550
- }
551
- }
552
- }` : ''}
553
-
554
- public static class TokenResult {
555
- private final String token;
556
- private final String bodyString;
557
-
558
- public TokenResult(String token, String bodyString) {
559
- this.token = token;
560
- this.bodyString = bodyString;
561
- }
562
-
563
- public String getToken() { return token; }
564
- public String getBodyString() { return bodyString; }
565
- }
566
- }`;
567
- }
568
- }
569
-
570
- /**
571
- * C# authentication examples
572
- */
573
- export class CSharpAuthGenerator extends ExampleGenerator {
574
- constructor() {
575
- super('csharp');
576
- }
577
-
578
- getEnvironmentTemplate(partnerType) {
579
- const envVars = CommonTemplates.getEnvironmentVariables(partnerType);
580
- const envString = Object.entries(envVars)
581
- .map(([key, value]) => `${key}=${value}`)
582
- .join('\n');
583
-
584
- return `// appsettings.json configuration
585
- {
586
- "Payware": {
587
- "PartnerId": "YOUR_PARTNER_ID",
588
- "SandboxPrivateKeyPath": "keys/sandbox-${partnerType}-private-key.pem",
589
- "ProductionPrivateKeyPath": "keys/production-${partnerType}-private-key.pem",
590
- "SandboxUrl": "https://sandbox.payware.eu/api",
591
- "ProductionUrl": "https://api.payware.eu/api"${partnerType === 'isv' ? `,
592
- "OAuth": {
593
- "ClientId": "YOUR_OAUTH_CLIENT_ID",
594
- "ClientSecret": "YOUR_OAUTH_CLIENT_SECRET"
595
- }` : ''}
596
- }
597
- }
598
-
599
- // Or use environment variables (.env file)
600
- // ${envString}`;
601
- }
602
-
603
- getDependenciesTemplate() {
604
- return `// Install NuGet packages:
605
- // Install-Package System.IdentityModel.Tokens.Jwt
606
- // Install-Package Microsoft.Extensions.Configuration
607
- // Install-Package Newtonsoft.Json
608
-
609
- using System;
610
- using System.Collections.Generic;
611
- using System.IO;
612
- using System.Net.Http;
613
- using System.Security.Cryptography;
614
- using System.Text;
615
- using System.Threading.Tasks;
616
- using Microsoft.IdentityModel.Tokens;
617
- using System.IdentityModel.Tokens.Jwt;
618
- using Newtonsoft.Json;
619
- using Newtonsoft.Json.Linq;
620
- using Microsoft.Extensions.Configuration;`;
621
- }
622
-
623
- getAuthTemplate(partnerType) {
624
- const isISV = partnerType === 'isv';
625
-
626
- return `public class PaywareAuthenticator
627
- {
628
- private const string PaywareAudience = "https://payware.eu";
629
- private readonly string _partnerId;
630
- private readonly string _privateKeyPath;
631
- private readonly HttpClient _httpClient;
632
- private readonly IConfiguration _configuration;
633
-
634
- public PaywareAuthenticator(IConfiguration configuration)
635
- {
636
- _configuration = configuration;
637
- _partnerId = configuration["Payware:PartnerId"];
638
- _httpClient = new HttpClient();
639
- }
640
-
641
- public async Task<TokenResult> CreateJWTTokenAsync(object requestBody = null, bool useSandbox = true)
642
- {
643
- // Get private key path
644
- var keyPath = useSandbox
645
- ? _configuration["Payware:SandboxPrivateKeyPath"]
646
- : _configuration["Payware:ProductionPrivateKeyPath"];
647
-
648
- // Load private key
649
- var privateKey = LoadPrivateKey(keyPath);
650
-
651
- // Create JWT header
652
- var header = new Dictionary<string, object>
653
- {
654
- ["alg"] = "RS256",
655
- ["typ"] = "JWT"
656
- };
657
-
658
- // Handle request body and SHA-256
659
- string bodyString = null;
660
- if (requestBody != null)
661
- {
662
- bodyString = CreateMinimizedJSON(requestBody);
663
- var contentSha256 = CalculateSHA256(bodyString);
664
- header["contentSha256"] = contentSha256;
665
- }
666
-
667
- // Create JWT payload
668
- var payload = new Dictionary<string, object>
669
- {
670
- ["iss"] = _partnerId,
671
- ["aud"] = PaywareAudience,
672
- ["iat"] = DateTimeOffset.UtcNow.ToUnixTimeSeconds()
673
- };
674
-
675
- // Create JWT token
676
- var tokenHandler = new JwtSecurityTokenHandler();
677
- var tokenDescriptor = new SecurityTokenDescriptor
678
- {
679
- Claims = payload,
680
- SigningCredentials = new SigningCredentials(privateKey, SecurityAlgorithms.RsaSha256),
681
- AdditionalHeaderClaims = header
682
- };
683
-
684
- var token = tokenHandler.CreateToken(tokenDescriptor);
685
- var tokenString = tokenHandler.WriteToken(token);
686
-
687
- return new TokenResult(tokenString, bodyString);
688
- }
689
-
690
- private RsaSecurityKey LoadPrivateKey(string keyPath)
691
- {
692
- var keyContent = File.ReadAllText(keyPath);
693
-
694
- // Remove PEM headers and whitespace
695
- keyContent = keyContent
696
- .Replace("-----BEGIN PRIVATE KEY-----", "")
697
- .Replace("-----END PRIVATE KEY-----", "")
698
- .Replace("\\n", "")
699
- .Replace("\\r", "")
700
- .Replace(" ", "");
701
-
702
- var keyBytes = Convert.FromBase64String(keyContent);
703
-
704
- var rsa = RSA.Create();
705
- rsa.ImportPkcs8PrivateKey(keyBytes, out _);
706
-
707
- return new RsaSecurityKey(rsa);
708
- }
709
-
710
- private string CreateMinimizedJSON(object data)
711
- {
712
- // Sort keys recursively for deterministic output
713
- var sortedData = SortObjectKeys(JToken.FromObject(data));
714
- return JsonConvert.SerializeObject(sortedData, Formatting.None);
715
- }
716
-
717
- private JToken SortObjectKeys(JToken token)
718
- {
719
- if (token is JObject obj)
720
- {
721
- var sorted = new JObject();
722
- foreach (var property in obj.Properties().OrderBy(p => p.Name))
723
- {
724
- sorted[property.Name] = SortObjectKeys(property.Value);
725
- }
726
- return sorted;
727
- }
728
- else if (token is JArray array)
729
- {
730
- var sortedArray = new JArray();
731
- foreach (var item in array)
732
- {
733
- sortedArray.Add(SortObjectKeys(item));
734
- }
735
- return sortedArray;
736
- }
737
- return token;
738
- }
739
-
740
- private string CalculateSHA256(string input)
741
- {
742
- using var sha256 = SHA256.Create();
743
- var inputBytes = Encoding.UTF8.GetBytes(input);
744
- var hashBytes = sha256.ComputeHash(inputBytes);
745
- return Convert.ToBase64String(hashBytes);
746
- }
747
-
748
- public Dictionary<string, string> GetAPIHeaders(string token)
749
- {
750
- return new Dictionary<string, string>
751
- {
752
- ["Authorization"] = $"Bearer {token}",
753
- ["Content-Type"] = "application/json",
754
- ["Api-Version"] = "1"
755
- };
756
- }
757
-
758
- public string GetAPIBaseURL(bool useSandbox = true)
759
- {
760
- if (useSandbox)
761
- {
762
- return _configuration["Payware:SandboxUrl"] ?? "https://sandbox.payware.eu/api";
763
- }
764
- else
765
- {
766
- return _configuration["Payware:ProductionUrl"] ?? "https://api.payware.eu/api";
767
- }
768
- }${isISV ? `
769
-
770
- // ISV-specific OAuth2 authentication
771
- public async Task<string> GetOAuth2TokenAsync(string merchantPartnerId)
772
- {
773
- var clientId = _configuration["Payware:OAuth:ClientId"];
774
- var clientSecret = _configuration["Payware:OAuth:ClientSecret"];
775
-
776
- if (string.IsNullOrEmpty(clientId) || string.IsNullOrEmpty(clientSecret))
777
- {
778
- throw new ArgumentException("Missing OAuth2 credentials");
779
- }
780
-
781
- var formData = new FormUrlEncodedContent(new[]
782
- {
783
- new KeyValuePair<string, string>("grant_type", "client_credentials"),
784
- new KeyValuePair<string, string>("client_id", clientId),
785
- new KeyValuePair<string, string>("client_secret", clientSecret),
786
- new KeyValuePair<string, string>("scope", $"merchant:{merchantPartnerId}")
787
- });
788
-
789
- var response = await _httpClient.PostAsync($"{GetAPIBaseURL()}/oauth2/token", formData);
790
-
791
- if (response.IsSuccessStatusCode)
792
- {
793
- var responseContent = await response.Content.ReadAsStringAsync();
794
- var jsonResponse = JObject.Parse(responseContent);
795
- return jsonResponse["access_token"]?.ToString();
796
- }
797
- else
798
- {
799
- throw new HttpRequestException($"OAuth2 token request failed: {response.StatusCode}");
800
- }
801
- }` : ''}
802
-
803
- public class TokenResult
804
- {
805
- public string Token { get; }
806
- public string BodyString { get; }
807
-
808
- public TokenResult(string token, string bodyString)
809
- {
810
- Token = token;
811
- BodyString = bodyString;
812
- }
813
- }
814
- }`;
815
- }
816
- }
817
-
818
- /**
819
- * Go authentication examples
820
- */
821
- export class GoAuthGenerator extends ExampleGenerator {
822
- constructor() {
823
- super('go');
824
- }
825
-
826
- getEnvironmentTemplate(partnerType) {
827
- const envVars = CommonTemplates.getEnvironmentVariables(partnerType);
828
- const envString = Object.entries(envVars)
829
- .map(([key, value]) => `${key}=${value}`)
830
- .join('\n');
831
-
832
- return `// .env file configuration
833
- ${envString}
834
-
835
- // Load environment variables in Go
836
- package main
837
-
838
- import (
839
- "os"
840
- "github.com/joho/godotenv"
841
- )
842
-
843
- func init() {
844
- err := godotenv.Load()
845
- if err != nil {
846
- log.Fatal("Error loading .env file")
847
- }
848
- }`;
849
- }
850
-
851
- getDependenciesTemplate() {
852
- return `// go.mod dependencies
853
- // go mod init payware-client
854
- // go get github.com/golang-jwt/jwt/v5
855
- // go get github.com/joho/godotenv
856
-
857
- package main
858
-
859
- import (
860
- "crypto/sha256"
861
- "crypto/rsa"
862
- "crypto/x509"
863
- "encoding/base64"
864
- "encoding/json"
865
- "encoding/pem"
866
- "fmt"
867
- "io/ioutil"
868
- "net/http"
869
- "net/url"
870
- "os"
871
- "sort"
872
- "strings"
873
- "time"
874
-
875
- "github.com/golang-jwt/jwt/v5"
876
- )`;
877
- }
878
-
879
- getAuthTemplate(partnerType) {
880
- const isISV = partnerType === 'isv';
881
-
882
- return `type PaywareConfig struct {
883
- PartnerID string
884
- PrivateKeyPath string
885
- SandboxURL string
886
- ProductionURL string${isISV ? `
887
- OAuthClientID string
888
- OAuthClientSecret string` : ''}
889
- }
890
-
891
- type TokenResult struct {
892
- Token string
893
- BodyString string
894
- }
895
-
896
- type PaywareAuthenticator struct {
897
- config PaywareConfig
898
- client *http.Client
899
- }
900
-
901
- func NewPaywareAuthenticator() *PaywareAuthenticator {
902
- config := PaywareConfig{
903
- PartnerID: os.Getenv("PAYWARE_PARTNER_ID"),
904
- PrivateKeyPath: getPrivateKeyPath(),
905
- SandboxURL: getEnvOrDefault("PAYWARE_SANDBOX_URL", "https://sandbox.payware.eu/api"),
906
- ProductionURL: getEnvOrDefault("PAYWARE_PRODUCTION_URL", "https://api.payware.eu/api"),${isISV ? `
907
- OAuthClientID: os.Getenv("PAYWARE_OAUTH_CLIENT_ID"),
908
- OAuthClientSecret: os.Getenv("PAYWARE_OAUTH_CLIENT_SECRET"),` : ''}
909
- }
910
-
911
- return &PaywareAuthenticator{
912
- config: config,
913
- client: &http.Client{Timeout: 30 * time.Second},
914
- }
915
- }
916
-
917
- func (p *PaywareAuthenticator) CreateJWTToken(requestBody interface{}, useSandbox bool) (*TokenResult, error) {
918
- // Load private key
919
- privateKey, err := p.loadPrivateKey()
920
- if err != nil {
921
- return nil, fmt.Errorf("failed to load private key: %w", err)
922
- }
923
-
924
- // Create JWT header
925
- header := map[string]interface{}{
926
- "alg": "RS256",
927
- "typ": "JWT",
928
- }
929
-
930
- // Handle request body and SHA-256
931
- var bodyString string
932
- if requestBody != nil {
933
- bodyString, err = p.createMinimizedJSON(requestBody)
934
- if err != nil {
935
- return nil, fmt.Errorf("failed to create minimized JSON: %w", err)
936
- }
937
-
938
- contentSha256, err := p.calculateSHA256(bodyString)
939
- if err != nil {
940
- return nil, fmt.Errorf("failed to calculate SHA-256: %w", err)
941
- }
942
- header["contentSha256"] = contentSha256
943
- }
944
-
945
- // Create JWT claims
946
- claims := jwt.MapClaims{
947
- "iss": p.config.PartnerID,
948
- "aud": "https://payware.eu",
949
- "iat": time.Now().Unix(),
950
- }
951
-
952
- // Create JWT token
953
- token := jwt.NewWithClaims(jwt.SigningMethodRS256, claims)
954
- token.Header = header
955
-
956
- tokenString, err := token.SignedString(privateKey)
957
- if err != nil {
958
- return nil, fmt.Errorf("failed to sign JWT token: %w", err)
959
- }
960
-
961
- return &TokenResult{
962
- Token: tokenString,
963
- BodyString: bodyString,
964
- }, nil
965
- }
966
-
967
- func (p *PaywareAuthenticator) loadPrivateKey() (*rsa.PrivateKey, error) {
968
- keyData, err := ioutil.ReadFile(p.config.PrivateKeyPath)
969
- if err != nil {
970
- return nil, err
971
- }
972
-
973
- block, _ := pem.Decode(keyData)
974
- if block == nil {
975
- return nil, fmt.Errorf("failed to decode PEM block")
976
- }
977
-
978
- key, err := x509.ParsePKCS8PrivateKey(block.Bytes)
979
- if err != nil {
980
- return nil, err
981
- }
982
-
983
- rsaKey, ok := key.(*rsa.PrivateKey)
984
- if !ok {
985
- return nil, fmt.Errorf("key is not RSA private key")
986
- }
987
-
988
- return rsaKey, nil
989
- }
990
-
991
- func (p *PaywareAuthenticator) createMinimizedJSON(data interface{}) (string, error) {
992
- // Sort keys recursively for deterministic output
993
- sortedData := p.sortObjectKeys(data)
994
-
995
- jsonBytes, err := json.Marshal(sortedData)
996
- if err != nil {
997
- return "", err
998
- }
999
-
1000
- return string(jsonBytes), nil
1001
- }
1002
-
1003
- func (p *PaywareAuthenticator) sortObjectKeys(data interface{}) interface{} {
1004
- switch v := data.(type) {
1005
- case map[string]interface{}:
1006
- keys := make([]string, 0, len(v))
1007
- for k := range v {
1008
- keys = append(keys, k)
1009
- }
1010
- sort.Strings(keys)
1011
-
1012
- sorted := make(map[string]interface{})
1013
- for _, k := range keys {
1014
- sorted[k] = p.sortObjectKeys(v[k])
1015
- }
1016
- return sorted
1017
-
1018
- case []interface{}:
1019
- sorted := make([]interface{}, len(v))
1020
- for i, item := range v {
1021
- sorted[i] = p.sortObjectKeys(item)
1022
- }
1023
- return sorted
1024
-
1025
- default:
1026
- return v
1027
- }
1028
- }
1029
-
1030
- func (p *PaywareAuthenticator) calculateSHA256(input string) (string, error) {
1031
- hash := sha256.Sum256([]byte(input))
1032
- return base64.StdEncoding.EncodeToString(hash[:]), nil
1033
- }
1034
-
1035
- func (p *PaywareAuthenticator) GetAPIHeaders(token string) map[string]string {
1036
- return map[string]string{
1037
- "Authorization": "Bearer " + token,
1038
- "Content-Type": "application/json",
1039
- "Api-Version": "1",
1040
- }
1041
- }
1042
-
1043
- func (p *PaywareAuthenticator) GetAPIBaseURL(useSandbox bool) string {
1044
- if useSandbox {
1045
- return p.config.SandboxURL
1046
- }
1047
- return p.config.ProductionURL
1048
- }${isISV ? `
1049
-
1050
- // ISV-specific OAuth2 authentication
1051
- func (p *PaywareAuthenticator) GetOAuth2Token(merchantPartnerID string) (string, error) {
1052
- if p.config.OAuthClientID == "" || p.config.OAuthClientSecret == "" {
1053
- return "", fmt.Errorf("missing OAuth2 credentials")
1054
- }
1055
-
1056
- data := url.Values{}
1057
- data.Set("grant_type", "client_credentials")
1058
- data.Set("client_id", p.config.OAuthClientID)
1059
- data.Set("client_secret", p.config.OAuthClientSecret)
1060
- data.Set("scope", "merchant:"+merchantPartnerID)
1061
-
1062
- req, err := http.NewRequest("POST", p.GetAPIBaseURL(true)+"/oauth2/token", strings.NewReader(data.Encode()))
1063
- if err != nil {
1064
- return "", err
1065
- }
1066
-
1067
- req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
1068
- req.Header.Set("Api-Version", "1")
1069
-
1070
- resp, err := p.client.Do(req)
1071
- if err != nil {
1072
- return "", err
1073
- }
1074
- defer resp.Body.Close()
1075
-
1076
- if resp.StatusCode != http.StatusOK {
1077
- return "", fmt.Errorf("OAuth2 token request failed: %d", resp.StatusCode)
1078
- }
1079
-
1080
- var tokenResponse struct {
1081
- AccessToken string \`json:"access_token"\`
1082
- }
1083
-
1084
- if err := json.NewDecoder(resp.Body).Decode(&tokenResponse); err != nil {
1085
- return "", err
1086
- }
1087
-
1088
- return tokenResponse.AccessToken, nil
1089
- }` : ''}
1090
-
1091
- func getPrivateKeyPath() string {
1092
- if sandbox := os.Getenv("PAYWARE_SANDBOX_PRIVATE_KEY_PATH"); sandbox != "" {
1093
- return sandbox
1094
- }
1095
- return os.Getenv("PAYWARE_PRODUCTION_PRIVATE_KEY_PATH")
1096
- }
1097
-
1098
- func getEnvOrDefault(key, defaultValue string) string {
1099
- if value := os.Getenv(key); value != "" {
1100
- return value
1101
- }
1102
- return defaultValue
1103
- }`;
1104
- }
1105
- }
1106
-
1107
- /**
1108
- * Ruby authentication examples
1109
- */
1110
- export class RubyAuthGenerator extends ExampleGenerator {
1111
- constructor() {
1112
- super('ruby');
1113
- }
1114
-
1115
- getEnvironmentTemplate(partnerType) {
1116
- const envVars = CommonTemplates.getEnvironmentVariables(partnerType);
1117
- const envString = Object.entries(envVars)
1118
- .map(([key, value]) => `${key}=${value}`)
1119
- .join('\n');
1120
-
1121
- return `# .env file configuration
1122
- ${envString}
1123
-
1124
- # Load environment variables in Ruby
1125
- require 'dotenv/load'`;
1126
- }
1127
-
1128
- getDependenciesTemplate() {
1129
- return `# Gemfile dependencies
1130
- # gem 'jwt'
1131
- # gem 'httparty'
1132
- # gem 'dotenv'
1133
-
1134
- require 'jwt'
1135
- require 'httparty'
1136
- require 'openssl'
1137
- require 'json'
1138
- require 'digest'
1139
- require 'base64'
1140
- require 'dotenv/load'`;
1141
- }
1142
-
1143
- getAuthTemplate(partnerType) {
1144
- const isISV = partnerType === 'isv';
1145
-
1146
- return `class PaywareAuthenticator
1147
- PAYWARE_AUDIENCE = 'https://payware.eu'.freeze
1148
-
1149
- def initialize
1150
- @partner_id = ENV['PAYWARE_PARTNER_ID']
1151
- @private_key_path = private_key_path
1152
- @sandbox_url = ENV['PAYWARE_SANDBOX_URL'] || 'https://sandbox.payware.eu/api'
1153
- @production_url = ENV['PAYWARE_PRODUCTION_URL'] || 'https://api.payware.eu/api'${isISV ? `
1154
- @oauth_client_id = ENV['PAYWARE_OAUTH_CLIENT_ID']
1155
- @oauth_client_secret = ENV['PAYWARE_OAUTH_CLIENT_SECRET']` : ''}
1156
- end
1157
-
1158
- def create_jwt_token(request_body = nil, use_sandbox = true)
1159
- # Load private key
1160
- private_key = load_private_key
1161
-
1162
- # Create JWT header
1163
- header = {
1164
- alg: 'RS256',
1165
- typ: 'JWT'
1166
- }
1167
-
1168
- # Handle request body and SHA-256
1169
- body_string = nil
1170
- if request_body
1171
- body_string = create_minimized_json(request_body)
1172
- content_sha256 = calculate_sha256(body_string)
1173
- header[:contentSha256] = content_sha256
1174
- end
1175
-
1176
- # Create JWT payload
1177
- payload = {
1178
- iss: @partner_id,
1179
- aud: PAYWARE_AUDIENCE,
1180
- iat: Time.now.to_i
1181
- }
1182
-
1183
- # Create JWT token
1184
- token = JWT.encode(payload, private_key, 'RS256', header)
1185
-
1186
- {
1187
- token: token,
1188
- body_string: body_string
1189
- }
1190
- rescue => e
1191
- raise "Failed to create JWT token: #{e.message}"
1192
- end
1193
-
1194
- def load_private_key
1195
- key_content = File.read(@private_key_path)
1196
- OpenSSL::PKey::RSA.new(key_content)
1197
- rescue => e
1198
- raise "Failed to load private key: #{e.message}"
1199
- end
1200
-
1201
- def create_minimized_json(data)
1202
- # Sort keys recursively for deterministic output
1203
- sorted_data = sort_object_keys(data)
1204
- JSON.generate(sorted_data, space: '', object_nl: '', array_nl: '')
1205
- end
1206
-
1207
- def sort_object_keys(obj)
1208
- case obj
1209
- when Hash
1210
- sorted_hash = {}
1211
- obj.keys.sort.each do |key|
1212
- sorted_hash[key] = sort_object_keys(obj[key])
1213
- end
1214
- sorted_hash
1215
- when Array
1216
- obj.map { |item| sort_object_keys(item) }
1217
- else
1218
- obj
1219
- end
1220
- end
1221
-
1222
- def calculate_sha256(input)
1223
- digest = Digest::SHA256.digest(input)
1224
- Base64.encode64(digest).strip
1225
- end
1226
-
1227
- def api_headers(token)
1228
- {
1229
- 'Authorization' => "Bearer #{token}",
1230
- 'Content-Type' => 'application/json',
1231
- 'Api-Version' => '1'
1232
- }
1233
- end
1234
-
1235
- def api_base_url(use_sandbox = true)
1236
- use_sandbox ? @sandbox_url : @production_url
1237
- end${isISV ? `
1238
-
1239
- # ISV-specific OAuth2 authentication
1240
- def get_oauth2_token(merchant_partner_id)
1241
- raise 'Missing OAuth2 credentials' if @oauth_client_id.nil? || @oauth_client_secret.nil?
1242
-
1243
- response = HTTParty.post(
1244
- "#{api_base_url}/oauth2/token",
1245
- body: {
1246
- grant_type: 'client_credentials',
1247
- client_id: @oauth_client_id,
1248
- client_secret: @oauth_client_secret,
1249
- scope: "merchant:#{merchant_partner_id}"
1250
- },
1251
- headers: {
1252
- 'Content-Type' => 'application/x-www-form-urlencoded',
1253
- 'Api-Version' => '1'
1254
- }
1255
- )
1256
-
1257
- if response.success?
1258
- response.parsed_response['access_token']
1259
- else
1260
- raise "OAuth2 token request failed: #{response.code}"
1261
- end
1262
- end` : ''}
1263
-
1264
- private
1265
-
1266
- def private_key_path
1267
- ENV['PAYWARE_SANDBOX_PRIVATE_KEY_PATH'] || ENV['PAYWARE_PRODUCTION_PRIVATE_KEY_PATH']
1268
- end
1269
- end`;
1270
- }
1271
- }
1272
-
1273
- /**
1274
- * cURL authentication examples
1275
- */
1276
- export class CurlAuthGenerator extends ExampleGenerator {
1277
- constructor() {
1278
- super('curl');
1279
- }
1280
-
1281
- getEnvironmentTemplate(partnerType) {
1282
- const envVars = CommonTemplates.getEnvironmentVariables(partnerType);
1283
- const envString = Object.entries(envVars)
1284
- .map(([key, value]) => `export ${key}="${value}"`)
1285
- .join('\n');
1286
-
1287
- return `#!/bin/bash
1288
- # Environment variables setup
1289
- ${envString}
1290
-
1291
- # Load environment variables
1292
- source .env`;
1293
- }
1294
-
1295
- getDependenciesTemplate() {
1296
- return `#!/bin/bash
1297
- # Required tools: curl, jq, openssl, base64
1298
- # Install on macOS: brew install jq
1299
- # Install on Ubuntu: apt-get install jq
1300
-
1301
- # Check if required tools are available
1302
- command -v curl >/dev/null 2>&1 || { echo "curl is required but not installed"; exit 1; }
1303
- command -v jq >/dev/null 2>&1 || { echo "jq is required but not installed"; exit 1; }
1304
- command -v openssl >/dev/null 2>&1 || { echo "openssl is required but not installed"; exit 1; }`;
1305
- }
1306
-
1307
- getAuthTemplate(partnerType) {
1308
- const isISV = partnerType === 'isv';
1309
-
1310
- return `#!/bin/bash
1311
-
1312
- # payware API Configuration
1313
- PAYWARE_AUDIENCE="https://payware.eu"
1314
- SANDBOX_URL="\${PAYWARE_SANDBOX_URL:-https://sandbox.payware.eu/api}"
1315
- PRODUCTION_URL="\${PAYWARE_PRODUCTION_URL:-https://api.payware.eu/api}"
1316
-
1317
- # Function to create JWT token
1318
- create_jwt_token() {
1319
- local request_body="$1"
1320
- local use_sandbox="\${2:-true}"
1321
-
1322
- # Get private key path
1323
- local private_key_path
1324
- if [ "$use_sandbox" = "true" ]; then
1325
- private_key_path="$PAYWARE_SANDBOX_PRIVATE_KEY_PATH"
1326
- else
1327
- private_key_path="$PAYWARE_PRODUCTION_PRIVATE_KEY_PATH"
1328
- fi
1329
-
1330
- # Create JWT header
1331
- local header='{"alg":"RS256","typ":"JWT"}'
1332
-
1333
- # Handle request body and SHA-256
1334
- local body_string=""
1335
- if [ -n "$request_body" ]; then
1336
- body_string=$(echo "$request_body" | jq -S -c .)
1337
- local content_sha256=$(echo -n "$body_string" | openssl dgst -sha256 -binary | base64)
1338
- header=$(echo "$header" | jq --arg sha256 "$content_sha256" '. + {contentSha256: $sha256}')
1339
- fi
1340
-
1341
- # Create JWT payload
1342
- local iat=$(date +%s)
1343
- local payload=$(jq -n \\
1344
- --arg iss "$PAYWARE_PARTNER_ID" \\
1345
- --arg aud "$PAYWARE_AUDIENCE" \\
1346
- --argjson iat "$iat" \\
1347
- '{iss: $iss, aud: $aud, iat: $iat}')
1348
-
1349
- # Encode header and payload
1350
- local header_b64=$(echo -n "$header" | base64 | tr -d '=' | tr '/+' '_-' | tr -d '\\n')
1351
- local payload_b64=$(echo -n "$payload" | base64 | tr -d '=' | tr '/+' '_-' | tr -d '\\n')
1352
- local unsigned_token="\${header_b64}.\${payload_b64}"
1353
-
1354
- # Sign token
1355
- local signature=$(echo -n "$unsigned_token" | \\
1356
- openssl dgst -sha256 -sign "$private_key_path" | \\
1357
- base64 | tr -d '=' | tr '/+' '_-' | tr -d '\\n')
1358
-
1359
- local token="\${unsigned_token}.\${signature}"
1360
-
1361
- echo "{\\"token\\": \\"$token\\", \\"body_string\\": \\"$body_string\\"}"
1362
- }
1363
-
1364
- # Function to get API headers
1365
- get_api_headers() {
1366
- local token="$1"
1367
- echo "Authorization: Bearer $token"
1368
- echo "Content-Type: application/json"
1369
- echo "Api-Version: 1"
1370
- }
1371
-
1372
- # Function to get API base URL
1373
- get_api_base_url() {
1374
- local use_sandbox="\${1:-true}"
1375
- if [ "$use_sandbox" = "true" ]; then
1376
- echo "$SANDBOX_URL"
1377
- else
1378
- echo "$PRODUCTION_URL"
1379
- fi
1380
- }${isISV ? `
1381
-
1382
- # ISV-specific OAuth2 authentication
1383
- get_oauth2_token() {
1384
- local merchant_partner_id="$1"
1385
-
1386
- if [ -z "$PAYWARE_OAUTH_CLIENT_ID" ] || [ -z "$PAYWARE_OAUTH_CLIENT_SECRET" ]; then
1387
- echo "Error: Missing OAuth2 credentials" >&2
1388
- return 1
1389
- fi
1390
-
1391
- local response=$(curl -s -X POST \\
1392
- "$(get_api_base_url)/oauth2/token" \\
1393
- -H "Content-Type: application/x-www-form-urlencoded" \\
1394
- -H "Api-Version: 1" \\
1395
- -d "grant_type=client_credentials" \\
1396
- -d "client_id=$PAYWARE_OAUTH_CLIENT_ID" \\
1397
- -d "client_secret=$PAYWARE_OAUTH_CLIENT_SECRET" \\
1398
- -d "scope=merchant:$merchant_partner_id")
1399
-
1400
- echo "$response" | jq -r '.access_token'
1401
- }` : ''}
1402
-
1403
- # Example usage function
1404
- make_api_request() {
1405
- local endpoint="$1"
1406
- local method="\${2:-GET}"
1407
- local request_body="$3"
1408
- local use_sandbox="\${4:-true}"
1409
-
1410
- # Create JWT token
1411
- local token_result=$(create_jwt_token "$request_body" "$use_sandbox")
1412
- local token=$(echo "$token_result" | jq -r '.token')
1413
- local body_string=$(echo "$token_result" | jq -r '.body_string')
1414
-
1415
- # Get API URL
1416
- local base_url=$(get_api_base_url "$use_sandbox")
1417
- local url="\${base_url}\${endpoint}"
1418
-
1419
- # Prepare curl command
1420
- local curl_cmd="curl -s -X $method"
1421
-
1422
- # Add headers
1423
- while IFS= read -r header; do
1424
- curl_cmd="$curl_cmd -H \\"$header\\""
1425
- done <<< "$(get_api_headers "$token")"
1426
-
1427
- # Add request body if present
1428
- if [ -n "$body_string" ] && [ "$body_string" != "null" ]; then
1429
- curl_cmd="$curl_cmd -d '$body_string'"
1430
- fi
1431
-
1432
- # Add URL
1433
- curl_cmd="$curl_cmd \\"$url\\""
1434
-
1435
- # Execute request
1436
- eval "$curl_cmd"
1437
- }
1438
-
1439
- # Example: Create transaction
1440
- create_transaction_example() {
1441
- local request_body='{
1442
- "trData": {
1443
- "amount": "10.00",
1444
- "currency": "EUR",
1445
- "reasonL1": "Test transaction"
1446
- },
1447
- "trOptions": {
1448
- "type": "PLAIN",
1449
- "timeToLive": 120
1450
- }
1451
- }'
1452
-
1453
- echo "Creating transaction..."
1454
- local response=$(make_api_request "/transactions" "POST" "$request_body")
1455
- echo "$response" | jq .
1456
- }
1457
-
1458
- # Example: Get transaction status
1459
- get_transaction_status_example() {
1460
- local transaction_id="$1"
1461
-
1462
- echo "Getting transaction status for: $transaction_id"
1463
- local response=$(make_api_request "/transactions/$transaction_id" "GET")
1464
- echo "$response" | jq .
1465
- }`;
1466
- }
1467
- }
1468
-
1469
- /**
1470
- * Export all authentication generators
1471
- */
1472
- export const AuthGenerators = {
1473
- python: PythonAuthGenerator,
1474
- nodejs: NodeJSAuthGenerator,
1475
- javascript: NodeJSAuthGenerator,
1476
- php: PHPAuthGenerator,
1477
- java: JavaAuthGenerator,
1478
- csharp: CSharpAuthGenerator,
1479
- go: GoAuthGenerator,
1480
- ruby: RubyAuthGenerator,
1481
- curl: CurlAuthGenerator
1
+ /**
2
+ * Authentication examples for all partner types and languages
3
+ */
4
+
5
+ import { ExampleGenerator, CommonTemplates } from './helpers.js';
6
+
7
+ /**
8
+ * Python authentication examples
9
+ */
10
+ export class PythonAuthGenerator extends ExampleGenerator {
11
+ constructor() {
12
+ super('python');
13
+ }
14
+
15
+ getEnvironmentTemplate(partnerType, options = {}) {
16
+ const envVars = CommonTemplates.getEnvironmentVariables(partnerType);
17
+ const envString = Object.entries(envVars)
18
+ .map(([key, value]) => `${key}=${value}`)
19
+ .join('\n');
20
+
21
+ return `${options.includeComments ? '# .env file configuration\n' : ''}${envString}
22
+
23
+ ${options.includeComments ? '# Load environment variables\n' : ''}import os
24
+ from dotenv import load_dotenv
25
+ load_dotenv()`;
26
+ }
27
+
28
+ getDependenciesTemplate(options = {}) {
29
+ return `${options.includeComments ? '# Install required dependencies:\n# pip install requests pyjwt cryptography python-dotenv\n\n' : ''}import requests
30
+ import jwt
31
+ import json
32
+ import hashlib
33
+ import base64
34
+ import os
35
+ from datetime import datetime, timedelta
36
+ from cryptography.hazmat.primitives import serialization`;
37
+ }
38
+
39
+ getAuthTemplate(partnerType, options = {}) {
40
+ const isISV = partnerType === 'isv';
41
+
42
+ return `def create_jwt_token(request_body=None, use_sandbox=True):
43
+ ${options.includeComments ? '"""Create JWT token for payware API authentication"""\n\n ' : ''}${options.includeComments ? '# Get configuration from environment\n ' : ''}partner_id = os.getenv('PAYWARE_PARTNER_ID')
44
+ if use_sandbox:
45
+ private_key_path = os.getenv('PAYWARE_SANDBOX_PRIVATE_KEY_PATH')
46
+ else:
47
+ private_key_path = os.getenv('PAYWARE_PRODUCTION_PRIVATE_KEY_PATH')
48
+
49
+ if not partner_id or not private_key_path:
50
+ raise ValueError("Missing required environment variables")
51
+
52
+ ${options.includeComments ? '# Load private key\n ' : ''}with open(private_key_path, 'r') as f:
53
+ private_key = f.read()
54
+
55
+ ${options.includeComments ? '# Create JWT header\n ' : ''}header = {
56
+ 'alg': 'RS256',
57
+ 'typ': 'JWT'
58
+ }
59
+
60
+ ${options.includeComments ? '# Add content SHA-256 for requests with body\n ' : ''}body_string = None
61
+ if request_body:
62
+ body_string = create_minimized_json(request_body)
63
+ content_sha256 = base64.b64encode(
64
+ hashlib.sha256(body_string.encode('utf-8')).digest()
65
+ ).decode('utf-8')
66
+ header['contentSha256'] = content_sha256
67
+
68
+ ${options.includeComments ? '# Create JWT payload\n ' : ''}payload = {
69
+ 'iss': partner_id,
70
+ 'aud': 'https://payware.eu',
71
+ 'iat': int(datetime.utcnow().timestamp())
72
+ }
73
+
74
+ ${options.includeComments ? '# Create and return token\n ' : ''}token = jwt.encode(payload, private_key, algorithm='RS256', headers=header)
75
+ return token, body_string
76
+
77
+ def create_minimized_json(data):
78
+ ${options.includeComments ? '"""Create deterministic minimized JSON for SHA-256 calculation"""\n ' : ''}def sort_dict(obj):
79
+ if isinstance(obj, dict):
80
+ return {k: sort_dict(v) for k, v in sorted(obj.items())}
81
+ elif isinstance(obj, list):
82
+ return [sort_dict(item) for item in obj]
83
+ else:
84
+ return obj
85
+
86
+ sorted_data = sort_dict(data)
87
+ return json.dumps(sorted_data, separators=(',', ':'))
88
+
89
+ def get_api_headers(token):
90
+ ${options.includeComments ? '"""Get standard API headers with authentication"""\n ' : ''}return {
91
+ 'Authorization': f'Bearer {token}',
92
+ 'Content-Type': 'application/json',
93
+ 'Api-Version': '1'
94
+ }
95
+
96
+ def get_api_base_url(use_sandbox=True):
97
+ ${options.includeComments ? '"""Get API base URL based on environment"""\n ' : ''}if use_sandbox:
98
+ return os.getenv('PAYWARE_SANDBOX_URL', 'https://sandbox.payware.eu/api')
99
+ else:
100
+ return os.getenv('PAYWARE_PRODUCTION_URL', 'https://api.payware.eu/api')${isISV ? `
101
+
102
+ ${options.includeComments ? '# ISV-specific OAuth2 authentication\n' : ''}def get_oauth2_token(merchant_partner_id):
103
+ ${options.includeComments ? '"""Get OAuth2 token for ISV operations"""\n ' : ''}client_id = os.getenv('PAYWARE_OAUTH_CLIENT_ID')
104
+ client_secret = os.getenv('PAYWARE_OAUTH_CLIENT_SECRET')
105
+
106
+ if not client_id or not client_secret:
107
+ raise ValueError("Missing OAuth2 credentials")
108
+
109
+ ${options.includeComments ? '# OAuth2 token request: JSON, camelCase, no scope - and /oauth2/tokens is\n # served from the host root, not under /api\n ' : ''}token_data = {
110
+ 'grantType': 'client_credentials',
111
+ 'clientId': merchant_partner_id,
112
+ 'clientSecret': base64.b64encode(merchant_secret.encode()).decode()
113
+ }
114
+
115
+ response = requests.post(
116
+ f"{get_api_base_url().replace('/api', '')}/oauth2/tokens",
117
+ json=token_data,
118
+ headers={'Content-Type': 'application/json'}
119
+ )
120
+
121
+ if response.status_code == 200:
122
+ return response.json()['accessToken']
123
+ else:
124
+ raise Exception(f"OAuth2 token request failed: {response.text}")` : ''}`;
125
+ }
126
+ }
127
+
128
+ /**
129
+ * Node.js authentication examples
130
+ */
131
+ export class NodeJSAuthGenerator extends ExampleGenerator {
132
+ constructor() {
133
+ super('nodejs');
134
+ }
135
+
136
+ getEnvironmentTemplate(partnerType, options = {}) {
137
+ const envVars = CommonTemplates.getEnvironmentVariables(partnerType);
138
+ const envString = Object.entries(envVars)
139
+ .map(([key, value]) => `${key}=${value}`)
140
+ .join('\n');
141
+
142
+ return `${options.includeComments ? '// .env file configuration\n' : ''}${envString}
143
+
144
+ ${options.includeComments ? '// Load environment variables\n' : ''}require('dotenv').config();`;
145
+ }
146
+
147
+ getDependenciesTemplate(options = {}) {
148
+ return `${options.includeComments ? '// Install required dependencies:\n// npm install axios jsonwebtoken crypto fs dotenv\n\n' : ''}const axios = require('axios');
149
+ const jwt = require('jsonwebtoken');
150
+ const crypto = require('crypto');
151
+ const fs = require('fs');
152
+ require('dotenv').config();`;
153
+ }
154
+
155
+ getAuthTemplate(partnerType, options = {}) {
156
+ const isISV = partnerType === 'isv';
157
+
158
+ return `function createJWTToken(requestBody = null, useSandbox = true) {
159
+ ${options.includeComments ? '// Get configuration from environment\n ' : ''}const partnerId = process.env.PAYWARE_PARTNER_ID;
160
+ const privateKeyPath = useSandbox ?
161
+ process.env.PAYWARE_SANDBOX_PRIVATE_KEY_PATH :
162
+ process.env.PAYWARE_PRODUCTION_PRIVATE_KEY_PATH;
163
+
164
+ if (!partnerId || !privateKeyPath) {
165
+ throw new Error('Missing required environment variables');
166
+ }
167
+
168
+ ${options.includeComments ? '// Load private key\n ' : ''}const privateKey = fs.readFileSync(privateKeyPath, 'utf8');
169
+
170
+ ${options.includeComments ? '// Create JWT header\n ' : ''}const header = {
171
+ alg: 'RS256',
172
+ typ: 'JWT'
173
+ };
174
+
175
+ ${options.includeComments ? '// Add content SHA-256 for requests with body\n ' : ''}let bodyString = null;
176
+ if (requestBody) {
177
+ bodyString = createMinimizedJSON(requestBody);
178
+ const contentSha256 = crypto
179
+ .createHash('sha256')
180
+ .update(bodyString, 'utf8')
181
+ .digest('base64');
182
+ header.contentSha256 = contentSha256;
183
+ }
184
+
185
+ ${options.includeComments ? '// Create JWT payload\n ' : ''}const payload = {
186
+ iss: partnerId,
187
+ aud: 'https://payware.eu',
188
+ iat: Math.floor(Date.now() / 1000)
189
+ };
190
+
191
+ ${options.includeComments ? '// Create and return token\n ' : ''}const token = jwt.sign(payload, privateKey, {
192
+ algorithm: 'RS256',
193
+ header
194
+ });
195
+
196
+ return { token, bodyString };
197
+ }
198
+
199
+ function createMinimizedJSON(data) {
200
+ ${options.includeComments ? '// Sort object keys recursively for deterministic output\n ' : ''}function sortObject(obj) {
201
+ if (Array.isArray(obj)) {
202
+ return obj.map(item => sortObject(item));
203
+ } else if (obj !== null && typeof obj === 'object') {
204
+ const sorted = {};
205
+ Object.keys(obj).sort().forEach(key => {
206
+ sorted[key] = sortObject(obj[key]);
207
+ });
208
+ return sorted;
209
+ }
210
+ return obj;
211
+ }
212
+
213
+ const sortedData = sortObject(data);
214
+ return JSON.stringify(sortedData);
215
+ }
216
+
217
+ function getAPIHeaders(token) {
218
+ return {
219
+ 'Authorization': \`Bearer \${token}\`,
220
+ 'Content-Type': 'application/json',
221
+ 'Api-Version': '1'
222
+ };
223
+ }
224
+
225
+ function getAPIBaseURL(useSandbox = true) {
226
+ if (useSandbox) {
227
+ return process.env.PAYWARE_SANDBOX_URL || 'https://sandbox.payware.eu/api';
228
+ } else {
229
+ return process.env.PAYWARE_PRODUCTION_URL || 'https://api.payware.eu/api';
230
+ }
231
+ }${isISV ? `
232
+
233
+ ${options.includeComments ? '// ISV-specific OAuth2 authentication\n' : ''}async function getOAuth2Token(merchantPartnerId) {
234
+ const clientId = process.env.PAYWARE_OAUTH_CLIENT_ID;
235
+ const clientSecret = process.env.PAYWARE_OAUTH_CLIENT_SECRET;
236
+
237
+ if (!clientId || !clientSecret) {
238
+ throw new Error('Missing OAuth2 credentials');
239
+ }
240
+
241
+ // JSON, camelCase, no scope - and /oauth2/tokens is served from the host root, not under /api
242
+ const tokenData = {
243
+ grantType: 'client_credentials',
244
+ clientId: merchantPartnerId,
245
+ clientSecret: Buffer.from(merchantSecret).toString('base64')
246
+ };
247
+
248
+ try {
249
+ const response = await axios.post(
250
+ \`\${getAPIBaseURL().replace('/api', '')}/oauth2/tokens\`,
251
+ tokenData,
252
+ { headers: { 'Content-Type': 'application/json' } }
253
+ );
254
+
255
+ return response.data.accessToken;
256
+ } catch (error) {
257
+ throw new Error(\`OAuth2 token request failed: \${error.response?.data || error.message}\`);
258
+ }
259
+ }` : ''}`;
260
+ }
261
+ }
262
+
263
+ /**
264
+ * PHP authentication examples
265
+ */
266
+ export class PHPAuthGenerator extends ExampleGenerator {
267
+ constructor() {
268
+ super('php');
269
+ }
270
+
271
+ getEnvironmentTemplate(partnerType, options = {}) {
272
+ const envVars = CommonTemplates.getEnvironmentVariables(partnerType);
273
+ const envString = Object.entries(envVars)
274
+ .map(([key, value]) => `${key}=${value}`)
275
+ .join('\n');
276
+
277
+ return `<?php
278
+ ${options.includeComments ? '// .env file configuration\n' : ''}${envString}
279
+
280
+ ${options.includeComments ? '// Load environment variables\n' : ''}$dotenv = Dotenv\\Dotenv::createImmutable(__DIR__);
281
+ $dotenv->load();`;
282
+ }
283
+
284
+ getDependenciesTemplate(options = {}) {
285
+ return `<?php
286
+ ${options.includeComments ? '// Install required dependencies:\n// composer require firebase/php-jwt guzzlehttp/guzzle vlucas/phpdotenv\n\n' : ''}require_once 'vendor/autoload.php';
287
+
288
+ use Firebase\\JWT\\JWT;
289
+ use Firebase\\JWT\\Key;
290
+ use GuzzleHttp\\Client;
291
+ use GuzzleHttp\\Exception\\RequestException;`;
292
+ }
293
+
294
+ getAuthTemplate(partnerType, options = {}) {
295
+ return `<?php
296
+ class PaywareAuth {
297
+
298
+ public static function createJWTToken($requestBody = null, $useSandbox = true) {
299
+ ${options.includeComments ? '// Get configuration from environment\n ' : ''}$partnerId = $_ENV['PAYWARE_PARTNER_ID'];
300
+ $privateKeyPath = $useSandbox ?
301
+ $_ENV['PAYWARE_SANDBOX_PRIVATE_KEY_PATH'] :
302
+ $_ENV['PAYWARE_PRODUCTION_PRIVATE_KEY_PATH'];
303
+
304
+ if (!$partnerId || !$privateKeyPath) {
305
+ throw new Exception('Missing required environment variables');
306
+ }
307
+
308
+ ${options.includeComments ? '// Load private key\n ' : ''}$privateKey = file_get_contents($privateKeyPath);
309
+ if (!$privateKey) {
310
+ throw new Exception('Unable to load private key');
311
+ }
312
+
313
+ ${options.includeComments ? '// Create JWT header\n ' : ''}$header = [
314
+ 'alg' => 'RS256',
315
+ 'typ' => 'JWT'
316
+ ];
317
+
318
+ ${options.includeComments ? '// Add content SHA-256 for requests with body\n ' : ''}$bodyString = null;
319
+ if ($requestBody) {
320
+ $bodyString = self::createMinimizedJSON($requestBody);
321
+ $contentSha256 = base64_encode(hash('sha256', $bodyString, true));
322
+ $header['contentSha256'] = $contentSha256;
323
+ }
324
+
325
+ ${options.includeComments ? '// Create JWT payload\n ' : ''}$payload = [
326
+ 'iss' => $partnerId,
327
+ 'aud' => 'https://payware.eu',
328
+ 'iat' => time()
329
+ ];
330
+
331
+ ${options.includeComments ? '// Create and return token\n ' : ''}$token = JWT::encode($payload, $privateKey, 'RS256', null, $header);
332
+
333
+ return [
334
+ 'token' => $token,
335
+ 'bodyString' => $bodyString
336
+ ];
337
+ }
338
+
339
+ private static function createMinimizedJSON($data) {
340
+ ${options.includeComments ? '// Sort array recursively for deterministic output\n ' : ''}if (is_array($data)) {
341
+ ksort($data);
342
+ foreach ($data as $key => $value) {
343
+ $data[$key] = self::createMinimizedJSON($value);
344
+ }
345
+ }
346
+
347
+ return json_encode($data, JSON_UNESCAPED_SLASHES);
348
+ }
349
+
350
+ public static function getAPIHeaders($token) {
351
+ return [
352
+ 'Authorization' => 'Bearer ' . $token,
353
+ 'Content-Type' => 'application/json',
354
+ 'Api-Version' => '1'
355
+ ];
356
+ }
357
+
358
+ public static function getAPIBaseURL($useSandbox = true) {
359
+ if ($useSandbox) {
360
+ return $_ENV['PAYWARE_SANDBOX_URL'] ?? 'https://sandbox.payware.eu/api';
361
+ } else {
362
+ return $_ENV['PAYWARE_PRODUCTION_URL'] ?? 'https://api.payware.eu/api';
363
+ }
364
+ }
365
+ }
366
+ ?>`;
367
+ }
368
+ }
369
+
370
+ /**
371
+ * Java authentication examples
372
+ */
373
+ export class JavaAuthGenerator extends ExampleGenerator {
374
+ constructor() {
375
+ super('java');
376
+ }
377
+
378
+ getEnvironmentTemplate(partnerType, options = {}) {
379
+ const envVars = CommonTemplates.getEnvironmentVariables(partnerType);
380
+ const envString = Object.entries(envVars)
381
+ .map(([key, value]) => `${key}=${value}`)
382
+ .join('\n');
383
+
384
+ return `${options.includeComments ? '// application.properties configuration\n' : ''}${envString}
385
+
386
+ ${options.includeComments ? '// Load configuration in your application\n' : ''}@Configuration
387
+ @PropertySource("classpath:application.properties")
388
+ public class PaywareConfig {
389
+ @Value("\${payware.partner.id}")
390
+ private String partnerId;
391
+
392
+ @Value("\${payware.sandbox.private.key.path}")
393
+ private String sandboxPrivateKeyPath;
394
+
395
+ ${options.includeComments ? '// ... other properties\n' : ''}
396
+ }`;
397
+ }
398
+
399
+ getDependenciesTemplate(options = {}) {
400
+ return `${options.includeComments ? '// Add dependencies to pom.xml:\n/*\n<dependencies>\n <dependency>\n <groupId>io.jsonwebtoken</groupId>\n <artifactId>jjwt-api</artifactId>\n <version>0.11.5</version>\n </dependency>\n <dependency>\n <groupId>io.jsonwebtoken</groupId>\n <artifactId>jjwt-impl</artifactId>\n <version>0.11.5</version>\n </dependency>\n <dependency>\n <groupId>io.jsonwebtoken</groupId>\n <artifactId>jjwt-jackson</artifactId>\n <version>0.11.5</version>\n </dependency>\n <dependency>\n <groupId>com.squareup.okhttp3</groupId>\n <artifactId>okhttp</artifactId>\n <version>4.10.0</version>\n </dependency>\n <dependency>\n <groupId>com.fasterxml.jackson.core</groupId>\n <artifactId>jackson-databind</artifactId>\n <version>2.15.2</version>\n </dependency>\n</dependencies>\n*/\n\n' : ''}import io.jsonwebtoken.Jwts;
401
+ import io.jsonwebtoken.SignatureAlgorithm;
402
+ import io.jsonwebtoken.security.Keys;
403
+ import okhttp3.*;
404
+ import com.fasterxml.jackson.databind.ObjectMapper;
405
+ import com.fasterxml.jackson.databind.JsonNode;
406
+
407
+ import java.security.KeyFactory;
408
+ import java.security.PrivateKey;
409
+ import java.security.spec.PKCS8EncodedKeySpec;
410
+ import java.nio.file.Files;
411
+ import java.nio.file.Paths;
412
+ import java.util.*;
413
+ import java.time.Instant;
414
+ import java.security.MessageDigest;`;
415
+ }
416
+
417
+ getAuthTemplate(partnerType, options = {}) {
418
+ const isISV = partnerType === 'isv';
419
+
420
+ return `public class PaywareAuthenticator {
421
+
422
+ private static final String PAYWARE_AUDIENCE = "https://payware.eu";
423
+ private String partnerId;
424
+ private String privateKeyPath;
425
+ private ObjectMapper objectMapper;
426
+ private OkHttpClient httpClient;
427
+
428
+ public PaywareAuthenticator(String partnerId, String privateKeyPath) {
429
+ this.partnerId = partnerId;
430
+ this.privateKeyPath = privateKeyPath;
431
+ this.objectMapper = new ObjectMapper();
432
+ this.httpClient = new OkHttpClient();
433
+ }
434
+
435
+ public TokenResult createJWTToken(Object requestBody, boolean useSandbox) throws Exception {
436
+ ${options.includeComments ? '// Load private key\n ' : ''}PrivateKey privateKey = loadPrivateKey();
437
+
438
+ ${options.includeComments ? '// Create JWT header\n ' : ''}Map<String, Object> header = new HashMap<>();
439
+ header.put("alg", "RS256");
440
+ header.put("typ", "JWT");
441
+
442
+ ${options.includeComments ? '// Handle request body and SHA-256\n ' : ''}String bodyString = null;
443
+ if (requestBody != null) {
444
+ bodyString = createMinimizedJSON(requestBody);
445
+ String contentSha256 = calculateSHA256(bodyString);
446
+ header.put("contentSha256", contentSha256);
447
+ }
448
+
449
+ ${options.includeComments ? '// Create JWT payload\n ' : ''}Map<String, Object> payload = new HashMap<>();
450
+ payload.put("iss", partnerId);
451
+ payload.put("aud", PAYWARE_AUDIENCE);
452
+ payload.put("iat", Instant.now().getEpochSecond());
453
+
454
+ ${options.includeComments ? '// Create JWT token\n ' : ''}String token = Jwts.builder()
455
+ .setHeader(header)
456
+ .setClaims(payload)
457
+ .signWith(privateKey, SignatureAlgorithm.RS256)
458
+ .compact();
459
+
460
+ return new TokenResult(token, bodyString);
461
+ }
462
+
463
+ private PrivateKey loadPrivateKey() throws Exception {
464
+ String keyContent = new String(Files.readAllBytes(Paths.get(privateKeyPath)));
465
+
466
+ ${options.includeComments ? '// Remove PEM headers and whitespace\n ' : ''}keyContent = keyContent
467
+ .replace("-----BEGIN PRIVATE KEY-----", "")
468
+ .replace("-----END PRIVATE KEY-----", "")
469
+ .replaceAll("\\\\s", "");
470
+
471
+ byte[] keyBytes = Base64.getDecoder().decode(keyContent);
472
+ PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes);
473
+ KeyFactory keyFactory = KeyFactory.getInstance("RSA");
474
+
475
+ return keyFactory.generatePrivate(spec);
476
+ }
477
+
478
+ private String createMinimizedJSON(Object data) throws Exception {
479
+ ${options.includeComments ? '// Sort keys recursively for deterministic output\n ' : ''}JsonNode sortedNode = objectMapper.valueToTree(data);
480
+ sortedNode = sortObjectKeys(sortedNode);
481
+
482
+ return objectMapper.writeValueAsString(sortedNode);
483
+ }
484
+
485
+ private JsonNode sortObjectKeys(JsonNode node) {
486
+ if (node.isObject()) {
487
+ Map<String, JsonNode> sorted = new TreeMap<>();
488
+ node.fields().forEachRemaining(entry -> {
489
+ sorted.put(entry.getKey(), sortObjectKeys(entry.getValue()));
490
+ });
491
+ return objectMapper.valueToTree(sorted);
492
+ } else if (node.isArray()) {
493
+ List<JsonNode> sortedArray = new ArrayList<>();
494
+ node.elements().forEachRemaining(element -> {
495
+ sortedArray.add(sortObjectKeys(element));
496
+ });
497
+ return objectMapper.valueToTree(sortedArray);
498
+ }
499
+ return node;
500
+ }
501
+
502
+ private String calculateSHA256(String input) throws Exception {
503
+ MessageDigest md = MessageDigest.getInstance("SHA-256");
504
+ byte[] digest = md.digest(input.getBytes("UTF-8"));
505
+ return Base64.getEncoder().encodeToString(digest);
506
+ }
507
+
508
+ public Map<String, String> getAPIHeaders(String token) {
509
+ Map<String, String> headers = new HashMap<>();
510
+ headers.put("Authorization", "Bearer " + token);
511
+ headers.put("Content-Type", "application/json");
512
+ headers.put("Api-Version", "1");
513
+ return headers;
514
+ }
515
+
516
+ public String getAPIBaseURL(boolean useSandbox) {
517
+ if (useSandbox) {
518
+ return System.getProperty("payware.sandbox.url", "https://sandbox.payware.eu/api");
519
+ } else {
520
+ return System.getProperty("payware.production.url", "https://api.payware.eu/api");
521
+ }
522
+ }${isISV ? `
523
+
524
+ ${options.includeComments ? '// ISV-specific OAuth2 authentication\n ' : ''}public String getOAuth2Token(String merchantPartnerId) throws Exception {
525
+ String clientId = System.getProperty("payware.oauth.client.id");
526
+ String clientSecret = System.getProperty("payware.oauth.client.secret");
527
+
528
+ if (clientId == null || clientSecret == null) {
529
+ throw new IllegalArgumentException("Missing OAuth2 credentials");
530
+ }
531
+
532
+ ${options.includeComments ? '// JSON body, camelCase, no scope\n ' : ''}ObjectNode body = objectMapper.createObjectNode();
533
+ body.put("grantType", "client_credentials");
534
+ body.put("clientId", merchantPartnerId);
535
+ body.put("clientSecret", Base64.getEncoder().encodeToString(merchantSecret.getBytes()));
536
+
537
+ // /oauth2/tokens is served from the host root, not under /api
538
+ Request request = new Request.Builder()
539
+ .url(getAPIBaseURL(true).replace("/api", "") + "/oauth2/tokens")
540
+ .post(RequestBody.create(objectMapper.writeValueAsString(body),
541
+ MediaType.parse("application/json")))
542
+ .build();
543
+
544
+ try (Response response = httpClient.newCall(request).execute()) {
545
+ if (response.isSuccessful()) {
546
+ String responseBody = response.body().string();
547
+ JsonNode jsonResponse = objectMapper.readTree(responseBody);
548
+ return jsonResponse.get("accessToken").asText();
549
+ } else {
550
+ throw new RuntimeException("OAuth2 token request failed: " + response.code());
551
+ }
552
+ }
553
+ }` : ''}
554
+
555
+ public static class TokenResult {
556
+ private final String token;
557
+ private final String bodyString;
558
+
559
+ public TokenResult(String token, String bodyString) {
560
+ this.token = token;
561
+ this.bodyString = bodyString;
562
+ }
563
+
564
+ public String getToken() { return token; }
565
+ public String getBodyString() { return bodyString; }
566
+ }
567
+ }`;
568
+ }
569
+ }
570
+
571
+ /**
572
+ * C# authentication examples
573
+ */
574
+ export class CSharpAuthGenerator extends ExampleGenerator {
575
+ constructor() {
576
+ super('csharp');
577
+ }
578
+
579
+ getEnvironmentTemplate(partnerType) {
580
+ const envVars = CommonTemplates.getEnvironmentVariables(partnerType);
581
+ const envString = Object.entries(envVars)
582
+ .map(([key, value]) => `${key}=${value}`)
583
+ .join('\n');
584
+
585
+ return `// appsettings.json configuration
586
+ {
587
+ "Payware": {
588
+ "PartnerId": "YOUR_PARTNER_ID",
589
+ "SandboxPrivateKeyPath": "keys/sandbox-${partnerType}-private-key.pem",
590
+ "ProductionPrivateKeyPath": "keys/production-${partnerType}-private-key.pem",
591
+ "SandboxUrl": "https://sandbox.payware.eu/api",
592
+ "ProductionUrl": "https://api.payware.eu/api"${partnerType === 'isv' ? `,
593
+ "OAuth": {
594
+ "ClientId": "YOUR_OAUTH_CLIENT_ID",
595
+ "ClientSecret": "YOUR_OAUTH_CLIENT_SECRET"
596
+ }` : ''}
597
+ }
598
+ }
599
+
600
+ // Or use environment variables (.env file)
601
+ // ${envString}`;
602
+ }
603
+
604
+ getDependenciesTemplate() {
605
+ return `// Install NuGet packages:
606
+ // Install-Package System.IdentityModel.Tokens.Jwt
607
+ // Install-Package Microsoft.Extensions.Configuration
608
+ // Install-Package Newtonsoft.Json
609
+
610
+ using System;
611
+ using System.Collections.Generic;
612
+ using System.IO;
613
+ using System.Net.Http;
614
+ using System.Security.Cryptography;
615
+ using System.Text;
616
+ using System.Threading.Tasks;
617
+ using Microsoft.IdentityModel.Tokens;
618
+ using System.IdentityModel.Tokens.Jwt;
619
+ using Newtonsoft.Json;
620
+ using Newtonsoft.Json.Linq;
621
+ using Microsoft.Extensions.Configuration;`;
622
+ }
623
+
624
+ getAuthTemplate(partnerType) {
625
+ const isISV = partnerType === 'isv';
626
+
627
+ return `public class PaywareAuthenticator
628
+ {
629
+ private const string PaywareAudience = "https://payware.eu";
630
+ private readonly string _partnerId;
631
+ private readonly string _privateKeyPath;
632
+ private readonly HttpClient _httpClient;
633
+ private readonly IConfiguration _configuration;
634
+
635
+ public PaywareAuthenticator(IConfiguration configuration)
636
+ {
637
+ _configuration = configuration;
638
+ _partnerId = configuration["Payware:PartnerId"];
639
+ _httpClient = new HttpClient();
640
+ }
641
+
642
+ public async Task<TokenResult> CreateJWTTokenAsync(object requestBody = null, bool useSandbox = true)
643
+ {
644
+ // Get private key path
645
+ var keyPath = useSandbox
646
+ ? _configuration["Payware:SandboxPrivateKeyPath"]
647
+ : _configuration["Payware:ProductionPrivateKeyPath"];
648
+
649
+ // Load private key
650
+ var privateKey = LoadPrivateKey(keyPath);
651
+
652
+ // Create JWT header
653
+ var header = new Dictionary<string, object>
654
+ {
655
+ ["alg"] = "RS256",
656
+ ["typ"] = "JWT"
657
+ };
658
+
659
+ // Handle request body and SHA-256
660
+ string bodyString = null;
661
+ if (requestBody != null)
662
+ {
663
+ bodyString = CreateMinimizedJSON(requestBody);
664
+ var contentSha256 = CalculateSHA256(bodyString);
665
+ header["contentSha256"] = contentSha256;
666
+ }
667
+
668
+ // Create JWT payload
669
+ var payload = new Dictionary<string, object>
670
+ {
671
+ ["iss"] = _partnerId,
672
+ ["aud"] = PaywareAudience,
673
+ ["iat"] = DateTimeOffset.UtcNow.ToUnixTimeSeconds()
674
+ };
675
+
676
+ // Create JWT token
677
+ var tokenHandler = new JwtSecurityTokenHandler();
678
+ var tokenDescriptor = new SecurityTokenDescriptor
679
+ {
680
+ Claims = payload,
681
+ SigningCredentials = new SigningCredentials(privateKey, SecurityAlgorithms.RsaSha256),
682
+ AdditionalHeaderClaims = header
683
+ };
684
+
685
+ var token = tokenHandler.CreateToken(tokenDescriptor);
686
+ var tokenString = tokenHandler.WriteToken(token);
687
+
688
+ return new TokenResult(tokenString, bodyString);
689
+ }
690
+
691
+ private RsaSecurityKey LoadPrivateKey(string keyPath)
692
+ {
693
+ var keyContent = File.ReadAllText(keyPath);
694
+
695
+ // Remove PEM headers and whitespace
696
+ keyContent = keyContent
697
+ .Replace("-----BEGIN PRIVATE KEY-----", "")
698
+ .Replace("-----END PRIVATE KEY-----", "")
699
+ .Replace("\\n", "")
700
+ .Replace("\\r", "")
701
+ .Replace(" ", "");
702
+
703
+ var keyBytes = Convert.FromBase64String(keyContent);
704
+
705
+ var rsa = RSA.Create();
706
+ rsa.ImportPkcs8PrivateKey(keyBytes, out _);
707
+
708
+ return new RsaSecurityKey(rsa);
709
+ }
710
+
711
+ private string CreateMinimizedJSON(object data)
712
+ {
713
+ // Sort keys recursively for deterministic output
714
+ var sortedData = SortObjectKeys(JToken.FromObject(data));
715
+ return JsonConvert.SerializeObject(sortedData, Formatting.None);
716
+ }
717
+
718
+ private JToken SortObjectKeys(JToken token)
719
+ {
720
+ if (token is JObject obj)
721
+ {
722
+ var sorted = new JObject();
723
+ foreach (var property in obj.Properties().OrderBy(p => p.Name))
724
+ {
725
+ sorted[property.Name] = SortObjectKeys(property.Value);
726
+ }
727
+ return sorted;
728
+ }
729
+ else if (token is JArray array)
730
+ {
731
+ var sortedArray = new JArray();
732
+ foreach (var item in array)
733
+ {
734
+ sortedArray.Add(SortObjectKeys(item));
735
+ }
736
+ return sortedArray;
737
+ }
738
+ return token;
739
+ }
740
+
741
+ private string CalculateSHA256(string input)
742
+ {
743
+ using var sha256 = SHA256.Create();
744
+ var inputBytes = Encoding.UTF8.GetBytes(input);
745
+ var hashBytes = sha256.ComputeHash(inputBytes);
746
+ return Convert.ToBase64String(hashBytes);
747
+ }
748
+
749
+ public Dictionary<string, string> GetAPIHeaders(string token)
750
+ {
751
+ return new Dictionary<string, string>
752
+ {
753
+ ["Authorization"] = $"Bearer {token}",
754
+ ["Content-Type"] = "application/json",
755
+ ["Api-Version"] = "1"
756
+ };
757
+ }
758
+
759
+ public string GetAPIBaseURL(bool useSandbox = true)
760
+ {
761
+ if (useSandbox)
762
+ {
763
+ return _configuration["Payware:SandboxUrl"] ?? "https://sandbox.payware.eu/api";
764
+ }
765
+ else
766
+ {
767
+ return _configuration["Payware:ProductionUrl"] ?? "https://api.payware.eu/api";
768
+ }
769
+ }${isISV ? `
770
+
771
+ // ISV-specific OAuth2 authentication
772
+ public async Task<string> GetOAuth2TokenAsync(string merchantPartnerId)
773
+ {
774
+ var clientId = _configuration["Payware:OAuth:ClientId"];
775
+ var clientSecret = _configuration["Payware:OAuth:ClientSecret"];
776
+
777
+ if (string.IsNullOrEmpty(clientId) || string.IsNullOrEmpty(clientSecret))
778
+ {
779
+ throw new ArgumentException("Missing OAuth2 credentials");
780
+ }
781
+
782
+ // JSON body, camelCase, no scope
783
+ var body = new StringContent(JsonConvert.SerializeObject(new
784
+ {
785
+ grantType = "client_credentials",
786
+ clientId = merchantPartnerId,
787
+ clientSecret = Convert.ToBase64String(Encoding.UTF8.GetBytes(merchantSecret))
788
+ }), Encoding.UTF8, "application/json");
789
+
790
+ // /oauth2/tokens is served from the host root, not under /api
791
+ var response = await _httpClient.PostAsync($"{GetAPIBaseURL().Replace("/api", "")}/oauth2/tokens", body);
792
+
793
+ if (response.IsSuccessStatusCode)
794
+ {
795
+ var responseContent = await response.Content.ReadAsStringAsync();
796
+ var jsonResponse = JObject.Parse(responseContent);
797
+ return jsonResponse["accessToken"]?.ToString();
798
+ }
799
+ else
800
+ {
801
+ throw new HttpRequestException($"OAuth2 token request failed: {response.StatusCode}");
802
+ }
803
+ }` : ''}
804
+
805
+ public class TokenResult
806
+ {
807
+ public string Token { get; }
808
+ public string BodyString { get; }
809
+
810
+ public TokenResult(string token, string bodyString)
811
+ {
812
+ Token = token;
813
+ BodyString = bodyString;
814
+ }
815
+ }
816
+ }`;
817
+ }
818
+ }
819
+
820
+ /**
821
+ * Go authentication examples
822
+ */
823
+ export class GoAuthGenerator extends ExampleGenerator {
824
+ constructor() {
825
+ super('go');
826
+ }
827
+
828
+ getEnvironmentTemplate(partnerType) {
829
+ const envVars = CommonTemplates.getEnvironmentVariables(partnerType);
830
+ const envString = Object.entries(envVars)
831
+ .map(([key, value]) => `${key}=${value}`)
832
+ .join('\n');
833
+
834
+ return `// .env file configuration
835
+ ${envString}
836
+
837
+ // Load environment variables in Go
838
+ package main
839
+
840
+ import (
841
+ "os"
842
+ "github.com/joho/godotenv"
843
+ )
844
+
845
+ func init() {
846
+ err := godotenv.Load()
847
+ if err != nil {
848
+ log.Fatal("Error loading .env file")
849
+ }
850
+ }`;
851
+ }
852
+
853
+ getDependenciesTemplate() {
854
+ return `// go.mod dependencies
855
+ // go mod init payware-client
856
+ // go get github.com/golang-jwt/jwt/v5
857
+ // go get github.com/joho/godotenv
858
+
859
+ package main
860
+
861
+ import (
862
+ "crypto/sha256"
863
+ "crypto/rsa"
864
+ "crypto/x509"
865
+ "encoding/base64"
866
+ "encoding/json"
867
+ "encoding/pem"
868
+ "fmt"
869
+ "io/ioutil"
870
+ "net/http"
871
+ "net/url"
872
+ "os"
873
+ "sort"
874
+ "strings"
875
+ "time"
876
+
877
+ "github.com/golang-jwt/jwt/v5"
878
+ )`;
879
+ }
880
+
881
+ getAuthTemplate(partnerType) {
882
+ const isISV = partnerType === 'isv';
883
+
884
+ return `type PaywareConfig struct {
885
+ PartnerID string
886
+ PrivateKeyPath string
887
+ SandboxURL string
888
+ ProductionURL string${isISV ? `
889
+ OAuthClientID string
890
+ OAuthClientSecret string` : ''}
891
+ }
892
+
893
+ type TokenResult struct {
894
+ Token string
895
+ BodyString string
896
+ }
897
+
898
+ type PaywareAuthenticator struct {
899
+ config PaywareConfig
900
+ client *http.Client
901
+ }
902
+
903
+ func NewPaywareAuthenticator() *PaywareAuthenticator {
904
+ config := PaywareConfig{
905
+ PartnerID: os.Getenv("PAYWARE_PARTNER_ID"),
906
+ PrivateKeyPath: getPrivateKeyPath(),
907
+ SandboxURL: getEnvOrDefault("PAYWARE_SANDBOX_URL", "https://sandbox.payware.eu/api"),
908
+ ProductionURL: getEnvOrDefault("PAYWARE_PRODUCTION_URL", "https://api.payware.eu/api"),${isISV ? `
909
+ OAuthClientID: os.Getenv("PAYWARE_OAUTH_CLIENT_ID"),
910
+ OAuthClientSecret: os.Getenv("PAYWARE_OAUTH_CLIENT_SECRET"),` : ''}
911
+ }
912
+
913
+ return &PaywareAuthenticator{
914
+ config: config,
915
+ client: &http.Client{Timeout: 30 * time.Second},
916
+ }
917
+ }
918
+
919
+ func (p *PaywareAuthenticator) CreateJWTToken(requestBody interface{}, useSandbox bool) (*TokenResult, error) {
920
+ // Load private key
921
+ privateKey, err := p.loadPrivateKey()
922
+ if err != nil {
923
+ return nil, fmt.Errorf("failed to load private key: %w", err)
924
+ }
925
+
926
+ // Create JWT header
927
+ header := map[string]interface{}{
928
+ "alg": "RS256",
929
+ "typ": "JWT",
930
+ }
931
+
932
+ // Handle request body and SHA-256
933
+ var bodyString string
934
+ if requestBody != nil {
935
+ bodyString, err = p.createMinimizedJSON(requestBody)
936
+ if err != nil {
937
+ return nil, fmt.Errorf("failed to create minimized JSON: %w", err)
938
+ }
939
+
940
+ contentSha256, err := p.calculateSHA256(bodyString)
941
+ if err != nil {
942
+ return nil, fmt.Errorf("failed to calculate SHA-256: %w", err)
943
+ }
944
+ header["contentSha256"] = contentSha256
945
+ }
946
+
947
+ // Create JWT claims
948
+ claims := jwt.MapClaims{
949
+ "iss": p.config.PartnerID,
950
+ "aud": "https://payware.eu",
951
+ "iat": time.Now().Unix(),
952
+ }
953
+
954
+ // Create JWT token
955
+ token := jwt.NewWithClaims(jwt.SigningMethodRS256, claims)
956
+ token.Header = header
957
+
958
+ tokenString, err := token.SignedString(privateKey)
959
+ if err != nil {
960
+ return nil, fmt.Errorf("failed to sign JWT token: %w", err)
961
+ }
962
+
963
+ return &TokenResult{
964
+ Token: tokenString,
965
+ BodyString: bodyString,
966
+ }, nil
967
+ }
968
+
969
+ func (p *PaywareAuthenticator) loadPrivateKey() (*rsa.PrivateKey, error) {
970
+ keyData, err := ioutil.ReadFile(p.config.PrivateKeyPath)
971
+ if err != nil {
972
+ return nil, err
973
+ }
974
+
975
+ block, _ := pem.Decode(keyData)
976
+ if block == nil {
977
+ return nil, fmt.Errorf("failed to decode PEM block")
978
+ }
979
+
980
+ key, err := x509.ParsePKCS8PrivateKey(block.Bytes)
981
+ if err != nil {
982
+ return nil, err
983
+ }
984
+
985
+ rsaKey, ok := key.(*rsa.PrivateKey)
986
+ if !ok {
987
+ return nil, fmt.Errorf("key is not RSA private key")
988
+ }
989
+
990
+ return rsaKey, nil
991
+ }
992
+
993
+ func (p *PaywareAuthenticator) createMinimizedJSON(data interface{}) (string, error) {
994
+ // Sort keys recursively for deterministic output
995
+ sortedData := p.sortObjectKeys(data)
996
+
997
+ jsonBytes, err := json.Marshal(sortedData)
998
+ if err != nil {
999
+ return "", err
1000
+ }
1001
+
1002
+ return string(jsonBytes), nil
1003
+ }
1004
+
1005
+ func (p *PaywareAuthenticator) sortObjectKeys(data interface{}) interface{} {
1006
+ switch v := data.(type) {
1007
+ case map[string]interface{}:
1008
+ keys := make([]string, 0, len(v))
1009
+ for k := range v {
1010
+ keys = append(keys, k)
1011
+ }
1012
+ sort.Strings(keys)
1013
+
1014
+ sorted := make(map[string]interface{})
1015
+ for _, k := range keys {
1016
+ sorted[k] = p.sortObjectKeys(v[k])
1017
+ }
1018
+ return sorted
1019
+
1020
+ case []interface{}:
1021
+ sorted := make([]interface{}, len(v))
1022
+ for i, item := range v {
1023
+ sorted[i] = p.sortObjectKeys(item)
1024
+ }
1025
+ return sorted
1026
+
1027
+ default:
1028
+ return v
1029
+ }
1030
+ }
1031
+
1032
+ func (p *PaywareAuthenticator) calculateSHA256(input string) (string, error) {
1033
+ hash := sha256.Sum256([]byte(input))
1034
+ return base64.StdEncoding.EncodeToString(hash[:]), nil
1035
+ }
1036
+
1037
+ func (p *PaywareAuthenticator) GetAPIHeaders(token string) map[string]string {
1038
+ return map[string]string{
1039
+ "Authorization": "Bearer " + token,
1040
+ "Content-Type": "application/json",
1041
+ "Api-Version": "1",
1042
+ }
1043
+ }
1044
+
1045
+ func (p *PaywareAuthenticator) GetAPIBaseURL(useSandbox bool) string {
1046
+ if useSandbox {
1047
+ return p.config.SandboxURL
1048
+ }
1049
+ return p.config.ProductionURL
1050
+ }${isISV ? `
1051
+
1052
+ // ISV-specific OAuth2 authentication
1053
+ func (p *PaywareAuthenticator) GetOAuth2Token(merchantPartnerID string) (string, error) {
1054
+ if p.config.OAuthClientID == "" || p.config.OAuthClientSecret == "" {
1055
+ return "", fmt.Errorf("missing OAuth2 credentials")
1056
+ }
1057
+
1058
+ // JSON body, camelCase, no scope
1059
+ body, err := json.Marshal(map[string]string{
1060
+ "grantType": "client_credentials",
1061
+ "clientId": merchantPartnerID,
1062
+ "clientSecret": base64.StdEncoding.EncodeToString([]byte(p.config.MerchantSecret)),
1063
+ })
1064
+ if err != nil {
1065
+ return "", err
1066
+ }
1067
+
1068
+ // /oauth2/tokens is served from the host root, not under /api
1069
+ baseURL := strings.Replace(p.GetAPIBaseURL(true), "/api", "", 1)
1070
+ req, err := http.NewRequest("POST", baseURL+"/oauth2/tokens", bytes.NewReader(body))
1071
+ if err != nil {
1072
+ return "", err
1073
+ }
1074
+
1075
+ req.Header.Set("Content-Type", "application/json")
1076
+
1077
+ resp, err := p.client.Do(req)
1078
+ if err != nil {
1079
+ return "", err
1080
+ }
1081
+ defer resp.Body.Close()
1082
+
1083
+ if resp.StatusCode != http.StatusOK {
1084
+ return "", fmt.Errorf("OAuth2 token request failed: %d", resp.StatusCode)
1085
+ }
1086
+
1087
+ var tokenResponse struct {
1088
+ AccessToken string \`json:"access_token"\`
1089
+ }
1090
+
1091
+ if err := json.NewDecoder(resp.Body).Decode(&tokenResponse); err != nil {
1092
+ return "", err
1093
+ }
1094
+
1095
+ return tokenResponse.AccessToken, nil
1096
+ }` : ''}
1097
+
1098
+ func getPrivateKeyPath() string {
1099
+ if sandbox := os.Getenv("PAYWARE_SANDBOX_PRIVATE_KEY_PATH"); sandbox != "" {
1100
+ return sandbox
1101
+ }
1102
+ return os.Getenv("PAYWARE_PRODUCTION_PRIVATE_KEY_PATH")
1103
+ }
1104
+
1105
+ func getEnvOrDefault(key, defaultValue string) string {
1106
+ if value := os.Getenv(key); value != "" {
1107
+ return value
1108
+ }
1109
+ return defaultValue
1110
+ }`;
1111
+ }
1112
+ }
1113
+
1114
+ /**
1115
+ * Ruby authentication examples
1116
+ */
1117
+ export class RubyAuthGenerator extends ExampleGenerator {
1118
+ constructor() {
1119
+ super('ruby');
1120
+ }
1121
+
1122
+ getEnvironmentTemplate(partnerType) {
1123
+ const envVars = CommonTemplates.getEnvironmentVariables(partnerType);
1124
+ const envString = Object.entries(envVars)
1125
+ .map(([key, value]) => `${key}=${value}`)
1126
+ .join('\n');
1127
+
1128
+ return `# .env file configuration
1129
+ ${envString}
1130
+
1131
+ # Load environment variables in Ruby
1132
+ require 'dotenv/load'`;
1133
+ }
1134
+
1135
+ getDependenciesTemplate() {
1136
+ return `# Gemfile dependencies
1137
+ # gem 'jwt'
1138
+ # gem 'httparty'
1139
+ # gem 'dotenv'
1140
+
1141
+ require 'jwt'
1142
+ require 'httparty'
1143
+ require 'openssl'
1144
+ require 'json'
1145
+ require 'digest'
1146
+ require 'base64'
1147
+ require 'dotenv/load'`;
1148
+ }
1149
+
1150
+ getAuthTemplate(partnerType) {
1151
+ const isISV = partnerType === 'isv';
1152
+
1153
+ return `class PaywareAuthenticator
1154
+ PAYWARE_AUDIENCE = 'https://payware.eu'.freeze
1155
+
1156
+ def initialize
1157
+ @partner_id = ENV['PAYWARE_PARTNER_ID']
1158
+ @private_key_path = private_key_path
1159
+ @sandbox_url = ENV['PAYWARE_SANDBOX_URL'] || 'https://sandbox.payware.eu/api'
1160
+ @production_url = ENV['PAYWARE_PRODUCTION_URL'] || 'https://api.payware.eu/api'${isISV ? `
1161
+ @oauth_client_id = ENV['PAYWARE_OAUTH_CLIENT_ID']
1162
+ @oauth_client_secret = ENV['PAYWARE_OAUTH_CLIENT_SECRET']` : ''}
1163
+ end
1164
+
1165
+ def create_jwt_token(request_body = nil, use_sandbox = true)
1166
+ # Load private key
1167
+ private_key = load_private_key
1168
+
1169
+ # Create JWT header
1170
+ header = {
1171
+ alg: 'RS256',
1172
+ typ: 'JWT'
1173
+ }
1174
+
1175
+ # Handle request body and SHA-256
1176
+ body_string = nil
1177
+ if request_body
1178
+ body_string = create_minimized_json(request_body)
1179
+ content_sha256 = calculate_sha256(body_string)
1180
+ header[:contentSha256] = content_sha256
1181
+ end
1182
+
1183
+ # Create JWT payload
1184
+ payload = {
1185
+ iss: @partner_id,
1186
+ aud: PAYWARE_AUDIENCE,
1187
+ iat: Time.now.to_i
1188
+ }
1189
+
1190
+ # Create JWT token
1191
+ token = JWT.encode(payload, private_key, 'RS256', header)
1192
+
1193
+ {
1194
+ token: token,
1195
+ body_string: body_string
1196
+ }
1197
+ rescue => e
1198
+ raise "Failed to create JWT token: #{e.message}"
1199
+ end
1200
+
1201
+ def load_private_key
1202
+ key_content = File.read(@private_key_path)
1203
+ OpenSSL::PKey::RSA.new(key_content)
1204
+ rescue => e
1205
+ raise "Failed to load private key: #{e.message}"
1206
+ end
1207
+
1208
+ def create_minimized_json(data)
1209
+ # Sort keys recursively for deterministic output
1210
+ sorted_data = sort_object_keys(data)
1211
+ JSON.generate(sorted_data, space: '', object_nl: '', array_nl: '')
1212
+ end
1213
+
1214
+ def sort_object_keys(obj)
1215
+ case obj
1216
+ when Hash
1217
+ sorted_hash = {}
1218
+ obj.keys.sort.each do |key|
1219
+ sorted_hash[key] = sort_object_keys(obj[key])
1220
+ end
1221
+ sorted_hash
1222
+ when Array
1223
+ obj.map { |item| sort_object_keys(item) }
1224
+ else
1225
+ obj
1226
+ end
1227
+ end
1228
+
1229
+ def calculate_sha256(input)
1230
+ digest = Digest::SHA256.digest(input)
1231
+ Base64.encode64(digest).strip
1232
+ end
1233
+
1234
+ def api_headers(token)
1235
+ {
1236
+ 'Authorization' => "Bearer #{token}",
1237
+ 'Content-Type' => 'application/json',
1238
+ 'Api-Version' => '1'
1239
+ }
1240
+ end
1241
+
1242
+ def api_base_url(use_sandbox = true)
1243
+ use_sandbox ? @sandbox_url : @production_url
1244
+ end${isISV ? `
1245
+
1246
+ # ISV-specific OAuth2 authentication
1247
+ def get_oauth2_token(merchant_partner_id)
1248
+ raise 'Missing OAuth2 credentials' if @oauth_client_id.nil? || @oauth_client_secret.nil?
1249
+
1250
+ # JSON, camelCase, no scope - and /oauth2/tokens is served from the host root, not under /api
1251
+ response = HTTParty.post(
1252
+ "#{api_base_url.sub('/api', '')}/oauth2/tokens",
1253
+ body: {
1254
+ grantType: 'client_credentials',
1255
+ clientId: merchant_partner_id,
1256
+ clientSecret: Base64.strict_encode64(@merchant_secret)
1257
+ }.to_json,
1258
+ headers: {
1259
+ 'Content-Type' => 'application/json'
1260
+ }
1261
+ )
1262
+
1263
+ if response.success?
1264
+ response.parsed_response['access_token']
1265
+ else
1266
+ raise "OAuth2 token request failed: #{response.code}"
1267
+ end
1268
+ end` : ''}
1269
+
1270
+ private
1271
+
1272
+ def private_key_path
1273
+ ENV['PAYWARE_SANDBOX_PRIVATE_KEY_PATH'] || ENV['PAYWARE_PRODUCTION_PRIVATE_KEY_PATH']
1274
+ end
1275
+ end`;
1276
+ }
1277
+ }
1278
+
1279
+ /**
1280
+ * cURL authentication examples
1281
+ */
1282
+ export class CurlAuthGenerator extends ExampleGenerator {
1283
+ constructor() {
1284
+ super('curl');
1285
+ }
1286
+
1287
+ getEnvironmentTemplate(partnerType) {
1288
+ const envVars = CommonTemplates.getEnvironmentVariables(partnerType);
1289
+ const envString = Object.entries(envVars)
1290
+ .map(([key, value]) => `export ${key}="${value}"`)
1291
+ .join('\n');
1292
+
1293
+ return `#!/bin/bash
1294
+ # Environment variables setup
1295
+ ${envString}
1296
+
1297
+ # Load environment variables
1298
+ source .env`;
1299
+ }
1300
+
1301
+ getDependenciesTemplate() {
1302
+ return `#!/bin/bash
1303
+ # Required tools: curl, jq, openssl, base64
1304
+ # Install on macOS: brew install jq
1305
+ # Install on Ubuntu: apt-get install jq
1306
+
1307
+ # Check if required tools are available
1308
+ command -v curl >/dev/null 2>&1 || { echo "curl is required but not installed"; exit 1; }
1309
+ command -v jq >/dev/null 2>&1 || { echo "jq is required but not installed"; exit 1; }
1310
+ command -v openssl >/dev/null 2>&1 || { echo "openssl is required but not installed"; exit 1; }`;
1311
+ }
1312
+
1313
+ getAuthTemplate(partnerType) {
1314
+ const isISV = partnerType === 'isv';
1315
+
1316
+ return `#!/bin/bash
1317
+
1318
+ # payware API Configuration
1319
+ PAYWARE_AUDIENCE="https://payware.eu"
1320
+ SANDBOX_URL="\${PAYWARE_SANDBOX_URL:-https://sandbox.payware.eu/api}"
1321
+ PRODUCTION_URL="\${PAYWARE_PRODUCTION_URL:-https://api.payware.eu/api}"
1322
+
1323
+ # Function to create JWT token
1324
+ create_jwt_token() {
1325
+ local request_body="$1"
1326
+ local use_sandbox="\${2:-true}"
1327
+
1328
+ # Get private key path
1329
+ local private_key_path
1330
+ if [ "$use_sandbox" = "true" ]; then
1331
+ private_key_path="$PAYWARE_SANDBOX_PRIVATE_KEY_PATH"
1332
+ else
1333
+ private_key_path="$PAYWARE_PRODUCTION_PRIVATE_KEY_PATH"
1334
+ fi
1335
+
1336
+ # Create JWT header
1337
+ local header='{"alg":"RS256","typ":"JWT"}'
1338
+
1339
+ # Handle request body and SHA-256
1340
+ local body_string=""
1341
+ if [ -n "$request_body" ]; then
1342
+ body_string=$(echo "$request_body" | jq -S -c .)
1343
+ local content_sha256=$(echo -n "$body_string" | openssl dgst -sha256 -binary | base64)
1344
+ header=$(echo "$header" | jq --arg sha256 "$content_sha256" '. + {contentSha256: $sha256}')
1345
+ fi
1346
+
1347
+ # Create JWT payload
1348
+ local iat=$(date +%s)
1349
+ local payload=$(jq -n \\
1350
+ --arg iss "$PAYWARE_PARTNER_ID" \\
1351
+ --arg aud "$PAYWARE_AUDIENCE" \\
1352
+ --argjson iat "$iat" \\
1353
+ '{iss: $iss, aud: $aud, iat: $iat}')
1354
+
1355
+ # Encode header and payload
1356
+ local header_b64=$(echo -n "$header" | base64 | tr -d '=' | tr '/+' '_-' | tr -d '\\n')
1357
+ local payload_b64=$(echo -n "$payload" | base64 | tr -d '=' | tr '/+' '_-' | tr -d '\\n')
1358
+ local unsigned_token="\${header_b64}.\${payload_b64}"
1359
+
1360
+ # Sign token
1361
+ local signature=$(echo -n "$unsigned_token" | \\
1362
+ openssl dgst -sha256 -sign "$private_key_path" | \\
1363
+ base64 | tr -d '=' | tr '/+' '_-' | tr -d '\\n')
1364
+
1365
+ local token="\${unsigned_token}.\${signature}"
1366
+
1367
+ echo "{\\"token\\": \\"$token\\", \\"body_string\\": \\"$body_string\\"}"
1368
+ }
1369
+
1370
+ # Function to get API headers
1371
+ get_api_headers() {
1372
+ local token="$1"
1373
+ echo "Authorization: Bearer $token"
1374
+ echo "Content-Type: application/json"
1375
+ echo "Api-Version: 1"
1376
+ }
1377
+
1378
+ # Function to get API base URL
1379
+ get_api_base_url() {
1380
+ local use_sandbox="\${1:-true}"
1381
+ if [ "$use_sandbox" = "true" ]; then
1382
+ echo "$SANDBOX_URL"
1383
+ else
1384
+ echo "$PRODUCTION_URL"
1385
+ fi
1386
+ }${isISV ? `
1387
+
1388
+ # ISV-specific OAuth2 authentication
1389
+ get_oauth2_token() {
1390
+ local merchant_partner_id="$1"
1391
+
1392
+ if [ -z "$PAYWARE_OAUTH_CLIENT_ID" ] || [ -z "$PAYWARE_OAUTH_CLIENT_SECRET" ]; then
1393
+ echo "Error: Missing OAuth2 credentials" >&2
1394
+ return 1
1395
+ fi
1396
+
1397
+ # JSON, camelCase, no scope - and /oauth2/tokens is served from the host root, not under /api
1398
+ local response=$(curl -s -X POST \\
1399
+ "$(get_api_base_url | sed 's|/api$||')/oauth2/tokens" \\
1400
+ -H "Content-Type: application/json" \\
1401
+ -d "{\\"grantType\\":\\"client_credentials\\",\\"clientId\\":\\"$merchant_partner_id\\",\\"clientSecret\\":\\"$(printf '%s' "$PAYWARE_MERCHANT_SECRET" | base64)\\"}")
1402
+
1403
+ echo "$response" | jq -r '.accessToken'
1404
+ }` : ''}
1405
+
1406
+ # Example usage function
1407
+ make_api_request() {
1408
+ local endpoint="$1"
1409
+ local method="\${2:-GET}"
1410
+ local request_body="$3"
1411
+ local use_sandbox="\${4:-true}"
1412
+
1413
+ # Create JWT token
1414
+ local token_result=$(create_jwt_token "$request_body" "$use_sandbox")
1415
+ local token=$(echo "$token_result" | jq -r '.token')
1416
+ local body_string=$(echo "$token_result" | jq -r '.body_string')
1417
+
1418
+ # Get API URL
1419
+ local base_url=$(get_api_base_url "$use_sandbox")
1420
+ local url="\${base_url}\${endpoint}"
1421
+
1422
+ # Prepare curl command
1423
+ local curl_cmd="curl -s -X $method"
1424
+
1425
+ # Add headers
1426
+ while IFS= read -r header; do
1427
+ curl_cmd="$curl_cmd -H \\"$header\\""
1428
+ done <<< "$(get_api_headers "$token")"
1429
+
1430
+ # Add request body if present
1431
+ if [ -n "$body_string" ] && [ "$body_string" != "null" ]; then
1432
+ curl_cmd="$curl_cmd -d '$body_string'"
1433
+ fi
1434
+
1435
+ # Add URL
1436
+ curl_cmd="$curl_cmd \\"$url\\""
1437
+
1438
+ # Execute request
1439
+ eval "$curl_cmd"
1440
+ }
1441
+
1442
+ # Example: Create transaction
1443
+ create_transaction_example() {
1444
+ local request_body='{
1445
+ "trData": {
1446
+ "amount": "10.00",
1447
+ "currency": "EUR",
1448
+ "reasonL1": "Test transaction"
1449
+ },
1450
+ "trOptions": {
1451
+ "type": "PLAIN",
1452
+ "timeToLive": 120
1453
+ }
1454
+ }'
1455
+
1456
+ echo "Creating transaction..."
1457
+ local response=$(make_api_request "/transactions" "POST" "$request_body")
1458
+ echo "$response" | jq .
1459
+ }
1460
+
1461
+ # Example: Get transaction status
1462
+ get_transaction_status_example() {
1463
+ local transaction_id="$1"
1464
+
1465
+ echo "Getting transaction status for: $transaction_id"
1466
+ local response=$(make_api_request "/transactions/$transaction_id" "GET")
1467
+ echo "$response" | jq .
1468
+ }`;
1469
+ }
1470
+ }
1471
+
1472
+ /**
1473
+ * Export all authentication generators
1474
+ */
1475
+ export const AuthGenerators = {
1476
+ python: PythonAuthGenerator,
1477
+ nodejs: NodeJSAuthGenerator,
1478
+ javascript: NodeJSAuthGenerator,
1479
+ php: PHPAuthGenerator,
1480
+ java: JavaAuthGenerator,
1481
+ csharp: CSharpAuthGenerator,
1482
+ go: GoAuthGenerator,
1483
+ ruby: RubyAuthGenerator,
1484
+ curl: CurlAuthGenerator
1482
1485
  };