@payware/mcp-server 1.1.1 → 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.
- package/.env.example +0 -0
- package/README.md +22 -16
- package/check-keys.js +0 -0
- package/keys/README.md +0 -0
- package/mcp-config.json +0 -0
- package/package.json +69 -66
- package/payware-mcp-config.json +0 -0
- package/src/auth/rsa-keys.js +0 -0
- package/src/auth/sandbox-setup.js +186 -186
- package/src/config/env.js +0 -0
- package/src/config/partner-types.js +0 -0
- package/src/core/auth/jwt-factory.js +4 -4
- package/src/core/auth/jwt-token.js +107 -66
- package/src/core/utils/json-serializer.js +5 -5
- package/src/core/utils/key-utils.js +0 -0
- package/src/index.js +0 -0
- package/src/isv/data/index.js +0 -0
- package/src/isv/deep-links/delete-product-link.js +0 -0
- package/src/isv/deep-links/delete-transaction-link.js +0 -0
- package/src/isv/deep-links/get-product-link.js +0 -0
- package/src/isv/deep-links/get-transaction-link.js +0 -0
- package/src/isv/deep-links/index.js +0 -0
- package/src/isv/index.js +29 -1
- package/src/isv/invitations/index.js +471 -0
- package/src/isv/oauth2/create-token-simple.js +0 -0
- package/src/isv/oauth2/get-token-info.js +0 -0
- package/src/isv/oauth2/obtain-token.js +19 -8
- package/src/isv/oauth2/token-helper.js +5 -2
- package/src/isv/poi/cancel-price.js +2 -10
- package/src/isv/poi/create.js +19 -0
- package/src/isv/poi/get-poi.js +2 -10
- package/src/isv/poi/get-qrcode.js +2 -10
- package/src/isv/poi/get-status.js +2 -10
- package/src/isv/poi/index.js +5 -0
- package/src/isv/poi/list-pois.js +2 -10
- package/src/isv/poi/set-price.js +217 -225
- package/src/isv/products/audios.js +0 -0
- package/src/isv/products/create-product.js +313 -313
- package/src/isv/products/delete-product.js +0 -0
- package/src/isv/products/get-product-image.js +0 -0
- package/src/isv/products/get-product.js +0 -0
- package/src/isv/products/index.js +0 -0
- package/src/isv/products/list-products.js +0 -0
- package/src/isv/products/schedules.js +0 -0
- package/src/isv/products/update-product.js +264 -264
- package/src/isv/reference/index.js +249 -0
- package/src/isv/shops/index.js +22 -0
- package/src/isv/transactions/cancel-transaction.js +43 -12
- package/src/isv/transactions/create-transaction.js +22 -6
- package/src/isv/transactions/get-status.js +18 -11
- package/src/isv/transactions/process-transaction.js +160 -42
- package/src/isv/transactions/simulate-callback.js +128 -109
- package/src/isv/transactions/transaction-history.js +86 -90
- package/src/isv/utils/auth.js +2 -2
- package/src/mcp-proxy.js +0 -0
- package/src/merchant/data/export-report.js +0 -0
- package/src/merchant/data/generate-report.js +19 -1
- package/src/merchant/data/get-export-list.js +0 -0
- package/src/merchant/data/get-report-requests.js +0 -0
- package/src/merchant/data/get-report-status.js +0 -0
- package/src/merchant/data/index.js +0 -0
- package/src/merchant/data/list-reports.js +0 -0
- package/src/merchant/deep-links/delete-product-link.js +0 -0
- package/src/merchant/deep-links/delete-transaction-link.js +0 -0
- package/src/merchant/deep-links/get-product-link.js +0 -0
- package/src/merchant/deep-links/get-transaction-link.js +0 -0
- package/src/merchant/deep-links/index.js +0 -0
- package/src/merchant/index.js +21 -1
- package/src/merchant/logs/index.js +366 -0
- package/src/merchant/products/audios.js +0 -0
- package/src/merchant/products/create-product.js +287 -287
- package/src/merchant/products/delete-product.js +0 -0
- package/src/merchant/products/get-product-image.js +0 -0
- package/src/merchant/products/get-product.js +0 -0
- package/src/merchant/products/index.js +0 -0
- package/src/merchant/products/list-products.js +0 -0
- package/src/merchant/products/schedules.js +0 -0
- package/src/merchant/products/update-product.js +281 -281
- package/src/merchant/shops/index.js +18 -0
- package/src/merchant/transactions/cancel-transaction.js +16 -19
- package/src/merchant/transactions/create-transaction.js +99 -19
- package/src/merchant/transactions/get-status.js +41 -12
- package/src/merchant/transactions/process-transaction.js +8 -16
- package/src/merchant/transactions/simulate-callback.js +68 -9
- package/src/merchant/transactions/transaction-history.js +275 -257
- package/src/payment-institution/data/index.js +0 -0
- package/src/payment-institution/deep-links/delete-transaction-link.js +0 -0
- package/src/payment-institution/deep-links/get-transaction-link.js +0 -0
- package/src/payment-institution/deep-links/index.js +0 -0
- package/src/payment-institution/index.js +0 -0
- package/src/payment-institution/transactions/create-pi-transaction.js +6 -14
- package/src/payment-institution/transactions/finalize-transaction.js +333 -340
- package/src/payment-institution/transactions/get-transaction-history.js +3 -11
- package/src/payment-institution/transactions/get-transaction-status.js +39 -12
- package/src/payment-institution/transactions/process-pi-transaction.js +5 -13
- package/src/payment-institution/transactions/simulate-pi-callback.js +49 -9
- package/src/payment-institution/transactions/soundbite-transaction.js +5 -13
- package/src/proxy-server.js +0 -0
- package/src/shared/api-errors.js +188 -0
- package/src/shared/auth-headers.js +62 -0
- package/src/shared/data/data-api.js +11 -10
- package/src/shared/deep-links/api.js +5 -4
- package/src/shared/index.js +0 -0
- package/src/shared/logs/logs-api.js +242 -0
- package/src/shared/poi/poi-create-api.js +419 -0
- package/src/shared/products/products-api.js +17 -16
- package/src/shared/shops/shops-api.js +155 -0
- package/src/tools/advanced-code-generator.js +2255 -2246
- package/src/utils/code-examples/common/auth-examples.js +1484 -1481
- package/src/utils/code-examples/common/helpers.js +7 -4
- package/src/utils/code-examples/common/utils-examples.js +0 -0
- package/src/utils/code-examples/frameworks/examples.js +4 -4
- package/src/utils/code-examples/index.js +0 -0
- package/src/utils/code-examples/isv/oauth2.js +458 -600
- package/src/utils/code-examples/isv/products.js +8 -8
- package/src/utils/code-examples/isv/transactions.js +815 -812
- package/src/utils/code-examples/merchant/products.js +0 -0
- package/src/utils/code-examples/merchant/transactions.js +9 -9
- package/src/utils/code-examples/payment-institution/transactions.js +0 -0
- package/src/utils/code-examples/shared/data.js +0 -0
- package/src/utils/code-examples/shared/deep-links.js +0 -0
- package/src/utils/code-examples/shared/soundbites.js +0 -0
- package/src/utils/code-examples.js +24 -24
- package/src/utils/format-request.js +3 -3
- package/src/utils/json-formatter.js +20 -20
- package/src/utils/server-info.js +0 -0
- 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
|
|
61
|
-
if request_body:
|
|
62
|
-
body_string = create_minimized_json(request_body)
|
|
63
|
-
|
|
64
|
-
hashlib.
|
|
65
|
-
).decode('utf-8')
|
|
66
|
-
header['
|
|
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
|
|
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
|
-
'
|
|
111
|
-
'
|
|
112
|
-
'
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
)
|
|
120
|
-
|
|
121
|
-
if response.status_code == 200:
|
|
122
|
-
return response.json()['
|
|
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
|
|
176
|
-
if (requestBody) {
|
|
177
|
-
bodyString = createMinimizedJSON(requestBody);
|
|
178
|
-
const
|
|
179
|
-
.createHash('
|
|
180
|
-
.update(bodyString, 'utf8')
|
|
181
|
-
.digest('base64');
|
|
182
|
-
header.
|
|
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
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
};
|
|
247
|
-
|
|
248
|
-
try {
|
|
249
|
-
const response = await axios.post(
|
|
250
|
-
\`\${getAPIBaseURL()}/oauth2/
|
|
251
|
-
tokenData
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
const
|
|
273
|
-
|
|
274
|
-
.
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
$dotenv
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
use Firebase\\JWT\\
|
|
289
|
-
use
|
|
290
|
-
use GuzzleHttp\\
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
$
|
|
300
|
-
|
|
301
|
-
$_ENV['
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
'
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
$
|
|
321
|
-
$
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
'
|
|
327
|
-
'
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
'
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
'
|
|
353
|
-
'
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
const
|
|
380
|
-
|
|
381
|
-
.
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
@
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
private
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
import io.jsonwebtoken.
|
|
401
|
-
import io.jsonwebtoken.
|
|
402
|
-
import
|
|
403
|
-
import
|
|
404
|
-
import com.fasterxml.jackson.databind.
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
import java.security.
|
|
408
|
-
import java.security.
|
|
409
|
-
import java.
|
|
410
|
-
import java.nio.file.
|
|
411
|
-
import java.
|
|
412
|
-
import java.
|
|
413
|
-
import java.
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
private String
|
|
423
|
-
private String
|
|
424
|
-
private
|
|
425
|
-
private
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
this.
|
|
430
|
-
this.
|
|
431
|
-
this.
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
header
|
|
439
|
-
header.put("
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
payload
|
|
450
|
-
payload.put("
|
|
451
|
-
payload.put("
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
.
|
|
456
|
-
.
|
|
457
|
-
.
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
.replace("-----
|
|
468
|
-
.
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
sortedNode =
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
headers.put("
|
|
511
|
-
headers.put("
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
String
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
Request request = new Request.Builder()
|
|
539
|
-
.url(getAPIBaseURL(true) + "/oauth2/
|
|
540
|
-
.post(
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
private final String
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
this.
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
public String
|
|
565
|
-
|
|
566
|
-
}
|
|
567
|
-
|
|
568
|
-
}
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
const
|
|
581
|
-
|
|
582
|
-
.
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
"
|
|
589
|
-
"
|
|
590
|
-
"
|
|
591
|
-
"
|
|
592
|
-
"
|
|
593
|
-
|
|
594
|
-
"
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
}
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
//
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
// Install-Package
|
|
607
|
-
// Install-Package
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
using System
|
|
611
|
-
using System.
|
|
612
|
-
using System.
|
|
613
|
-
using System.
|
|
614
|
-
using System.
|
|
615
|
-
using System.
|
|
616
|
-
using
|
|
617
|
-
using
|
|
618
|
-
using
|
|
619
|
-
using Newtonsoft.Json
|
|
620
|
-
using
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
private
|
|
630
|
-
private readonly string
|
|
631
|
-
private readonly
|
|
632
|
-
private readonly
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
["
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
["
|
|
672
|
-
["
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
var
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
var
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
.Replace("-----
|
|
698
|
-
.Replace("
|
|
699
|
-
.Replace("\\
|
|
700
|
-
.Replace("
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
rsa.
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
var
|
|
744
|
-
var
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
["
|
|
754
|
-
["
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
var
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
public
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
}
|
|
815
|
-
|
|
816
|
-
}
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
// go
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
"crypto/
|
|
863
|
-
"
|
|
864
|
-
"
|
|
865
|
-
"encoding/
|
|
866
|
-
"
|
|
867
|
-
"
|
|
868
|
-
"
|
|
869
|
-
"
|
|
870
|
-
"
|
|
871
|
-
"
|
|
872
|
-
"
|
|
873
|
-
"
|
|
874
|
-
|
|
875
|
-
"
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
}
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
"
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
"
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
}
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
if err != nil {
|
|
1072
|
-
return "", err
|
|
1073
|
-
}
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
}
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
return
|
|
1096
|
-
}
|
|
1097
|
-
|
|
1098
|
-
func
|
|
1099
|
-
if
|
|
1100
|
-
return
|
|
1101
|
-
}
|
|
1102
|
-
return
|
|
1103
|
-
}
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
#
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
# Create JWT
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
rescue => e
|
|
1198
|
-
raise "Failed to
|
|
1199
|
-
end
|
|
1200
|
-
|
|
1201
|
-
def
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
obj
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
end
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
#
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
--arg
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
echo
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
echo "
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
}
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
local
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
local
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
local
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
local
|
|
1421
|
-
|
|
1422
|
-
#
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
#
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
"
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
local
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
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
|
};
|