@payware/mcp-server 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (106) hide show
  1. package/.env.example +92 -0
  2. package/MCP-SETUP.md +190 -0
  3. package/README.md +483 -0
  4. package/check-keys.js +52 -0
  5. package/keys/README.md +112 -0
  6. package/mcp-config.json +9 -0
  7. package/package.json +66 -0
  8. package/payware-mcp-config.json +12 -0
  9. package/src/auth/rsa-keys.js +82 -0
  10. package/src/auth/sandbox-setup.js +187 -0
  11. package/src/config/env.js +229 -0
  12. package/src/config/partner-types.js +143 -0
  13. package/src/core/auth/jwt-factory.js +182 -0
  14. package/src/core/auth/jwt-token.js +735 -0
  15. package/src/core/utils/json-serializer.js +100 -0
  16. package/src/core/utils/key-utils.js +133 -0
  17. package/src/index.js +237 -0
  18. package/src/isv/data/index.js +291 -0
  19. package/src/isv/deep-links/delete-product-link.js +154 -0
  20. package/src/isv/deep-links/delete-transaction-link.js +154 -0
  21. package/src/isv/deep-links/get-product-link.js +167 -0
  22. package/src/isv/deep-links/get-transaction-link.js +167 -0
  23. package/src/isv/deep-links/index.js +23 -0
  24. package/src/isv/index.js +103 -0
  25. package/src/isv/oauth2/create-token-simple.js +46 -0
  26. package/src/isv/oauth2/get-token-info.js +157 -0
  27. package/src/isv/oauth2/obtain-token.js +189 -0
  28. package/src/isv/oauth2/token-helper.js +151 -0
  29. package/src/isv/products/audios.js +470 -0
  30. package/src/isv/products/create-product.js +314 -0
  31. package/src/isv/products/delete-product.js +119 -0
  32. package/src/isv/products/get-product-image.js +247 -0
  33. package/src/isv/products/get-product.js +168 -0
  34. package/src/isv/products/index.js +65 -0
  35. package/src/isv/products/list-products.js +136 -0
  36. package/src/isv/products/schedules.js +522 -0
  37. package/src/isv/products/update-product.js +265 -0
  38. package/src/isv/transactions/cancel-transaction.js +129 -0
  39. package/src/isv/transactions/create-transaction.js +361 -0
  40. package/src/isv/transactions/get-status.js +217 -0
  41. package/src/isv/transactions/process-transaction.js +145 -0
  42. package/src/isv/transactions/simulate-callback.js +146 -0
  43. package/src/isv/transactions/transaction-history.js +186 -0
  44. package/src/isv/utils/auth.js +159 -0
  45. package/src/mcp-proxy.js +150 -0
  46. package/src/merchant/data/export-report.js +108 -0
  47. package/src/merchant/data/generate-report.js +151 -0
  48. package/src/merchant/data/get-report-status.js +120 -0
  49. package/src/merchant/data/index.js +351 -0
  50. package/src/merchant/deep-links/delete-product-link.js +121 -0
  51. package/src/merchant/deep-links/delete-transaction-link.js +121 -0
  52. package/src/merchant/deep-links/get-product-link.js +134 -0
  53. package/src/merchant/deep-links/get-transaction-link.js +134 -0
  54. package/src/merchant/deep-links/index.js +23 -0
  55. package/src/merchant/index.js +92 -0
  56. package/src/merchant/products/audios.js +606 -0
  57. package/src/merchant/products/create-product.js +288 -0
  58. package/src/merchant/products/delete-product.js +111 -0
  59. package/src/merchant/products/get-product-image.js +252 -0
  60. package/src/merchant/products/get-product.js +144 -0
  61. package/src/merchant/products/index.js +65 -0
  62. package/src/merchant/products/list-products.js +112 -0
  63. package/src/merchant/products/schedules.js +646 -0
  64. package/src/merchant/products/update-product.js +282 -0
  65. package/src/merchant/transactions/cancel-transaction.js +241 -0
  66. package/src/merchant/transactions/create-transaction.js +482 -0
  67. package/src/merchant/transactions/get-status.js +196 -0
  68. package/src/merchant/transactions/process-transaction.js +349 -0
  69. package/src/merchant/transactions/simulate-callback.js +246 -0
  70. package/src/merchant/transactions/transaction-history.js +258 -0
  71. package/src/payment-institution/data/index.js +280 -0
  72. package/src/payment-institution/deep-links/delete-transaction-link.js +128 -0
  73. package/src/payment-institution/deep-links/get-transaction-link.js +141 -0
  74. package/src/payment-institution/deep-links/index.js +17 -0
  75. package/src/payment-institution/index.js +92 -0
  76. package/src/payment-institution/transactions/create-pi-transaction.js +484 -0
  77. package/src/payment-institution/transactions/finalize-transaction.js +340 -0
  78. package/src/payment-institution/transactions/get-transaction-history.js +269 -0
  79. package/src/payment-institution/transactions/get-transaction-status.js +234 -0
  80. package/src/payment-institution/transactions/process-pi-transaction.js +421 -0
  81. package/src/payment-institution/transactions/simulate-pi-callback.js +326 -0
  82. package/src/payment-institution/transactions/soundbite-transaction.js +269 -0
  83. package/src/proxy-server.js +205 -0
  84. package/src/shared/data/data-api.js +372 -0
  85. package/src/shared/deep-links/api.js +214 -0
  86. package/src/shared/index.js +44 -0
  87. package/src/shared/products/products-api.js +872 -0
  88. package/src/tools/advanced-code-generator.js +2247 -0
  89. package/src/utils/code-examples/common/auth-examples.js +1482 -0
  90. package/src/utils/code-examples/common/helpers.js +416 -0
  91. package/src/utils/code-examples/common/utils-examples.js +591 -0
  92. package/src/utils/code-examples/frameworks/examples.js +859 -0
  93. package/src/utils/code-examples/index.js +441 -0
  94. package/src/utils/code-examples/isv/oauth2.js +600 -0
  95. package/src/utils/code-examples/isv/products.js +666 -0
  96. package/src/utils/code-examples/isv/transactions.js +813 -0
  97. package/src/utils/code-examples/merchant/products.js +497 -0
  98. package/src/utils/code-examples/merchant/transactions.js +623 -0
  99. package/src/utils/code-examples/payment-institution/transactions.js +388 -0
  100. package/src/utils/code-examples/shared/data.js +607 -0
  101. package/src/utils/code-examples/shared/deep-links.js +662 -0
  102. package/src/utils/code-examples/shared/soundbites.js +808 -0
  103. package/src/utils/code-examples.js +646 -0
  104. package/src/utils/format-request.js +363 -0
  105. package/src/utils/json-formatter.js +249 -0
  106. package/src/utils/server-info.js +132 -0
@@ -0,0 +1,363 @@
1
+ /**
2
+ * Format and validate requests for payware API with deterministic JSON serialization
3
+ */
4
+
5
+ import { createMinimizedJSON } from '../core/utils/json-serializer.js';
6
+
7
+ /**
8
+ * Format transaction request data
9
+ * @param {Object} data - Raw transaction data
10
+ * @returns {Object} Formatted and validated transaction data
11
+ */
12
+ export function formatTransactionRequest(data) {
13
+ const {
14
+ type = 'PLAIN',
15
+ amount,
16
+ currency = 'EUR',
17
+ reasonL1,
18
+ reasonL2,
19
+ callbackUrl,
20
+ account,
21
+ friendlyName,
22
+ shop,
23
+ timeToLive = 120,
24
+ passbackParams
25
+ } = data;
26
+
27
+ // Validation
28
+ const errors = [];
29
+
30
+ // reasonL1 is required
31
+ if (!reasonL1 || typeof reasonL1 !== 'string' || reasonL1.trim().length === 0) {
32
+ errors.push('reasonL1 is required (transaction grounds description)');
33
+ }
34
+
35
+ if (reasonL1 && reasonL1.length > 100) {
36
+ errors.push('reasonL1 cannot exceed 100 characters');
37
+ }
38
+
39
+ if (reasonL2 && reasonL2.length > 100) {
40
+ errors.push('reasonL2 cannot exceed 100 characters');
41
+ }
42
+
43
+ // Amount validation (optional for flexible amounts)
44
+ if (amount !== undefined && (typeof amount !== 'number' && typeof amount !== 'string')) {
45
+ errors.push('Amount must be a number or string representing currency value');
46
+ }
47
+
48
+ if (amount !== undefined && parseFloat(amount) < 0) {
49
+ errors.push('Amount must be non-negative currency value');
50
+ }
51
+
52
+ // Amount can be any positive value (limited by Java type implementation)
53
+
54
+ if (!['PLAIN', 'QR', 'BARCODE'].includes(type)) {
55
+ errors.push('Transaction type must be PLAIN, QR, or BARCODE');
56
+ }
57
+
58
+ // Currency validation - accept any 3-character ISO code
59
+ if (!currency || typeof currency !== 'string' || !currency.match(/^[A-Z]{3}$/)) {
60
+ errors.push('Currency must be a valid ISO 3-character code (e.g., EUR, USD, JPY)');
61
+ }
62
+
63
+ if (callbackUrl && !callbackUrl.match(/^https?:\/\/.+/)) {
64
+ errors.push('Callback URL must be a valid HTTP/HTTPS URL');
65
+ }
66
+
67
+ if (account && account.length > 36) {
68
+ errors.push('Account identifier cannot exceed 36 characters');
69
+ }
70
+
71
+ if (friendlyName && friendlyName.length > 100) {
72
+ errors.push('Friendly name cannot exceed 100 characters');
73
+ }
74
+
75
+ if (shop && shop.length > 10) {
76
+ errors.push('Shop code cannot exceed 10 characters');
77
+ }
78
+
79
+ if (timeToLive && (timeToLive < 60 || timeToLive > 600)) {
80
+ errors.push('Time to live must be between 60 and 600 seconds');
81
+ }
82
+
83
+ if (errors.length > 0) {
84
+ throw new Error(`Validation errors: ${errors.join(', ')}`);
85
+ }
86
+
87
+ // Format the request according to documentation structure
88
+ const formattedRequest = {
89
+ ...(account && { account }),
90
+ ...(friendlyName && { friendlyName }),
91
+ ...(shop && { shop }),
92
+ ...(callbackUrl && { callbackUrl }),
93
+ ...(passbackParams && { passbackParams }),
94
+ trData: {
95
+ amount: amount !== undefined ? amount.toString() : '0.00',
96
+ currency,
97
+ reasonL1,
98
+ ...(reasonL2 && { reasonL2 })
99
+ },
100
+ trOptions: {
101
+ type,
102
+ timeToLive
103
+ }
104
+ };
105
+
106
+ return {
107
+ request: formattedRequest,
108
+ validation: {
109
+ valid: true,
110
+ errors: [],
111
+ warnings: []
112
+ },
113
+ formatted: true,
114
+ timestamp: new Date().toISOString()
115
+ };
116
+ }
117
+
118
+ /**
119
+ * Format API headers
120
+ * @param {string} jwtToken - JWT authentication token
121
+ * @param {string} signature - Request signature (optional)
122
+ * @returns {Object} Formatted headers
123
+ */
124
+ export function formatAPIHeaders(jwtToken, signature = null) {
125
+ const headers = {
126
+ 'Authorization': `Bearer ${jwtToken}`,
127
+ 'Content-Type': 'application/json',
128
+ 'Api-Version': '1',
129
+ 'Accept': 'application/json'
130
+ };
131
+
132
+ if (signature) {
133
+ headers['X-Signature'] = signature;
134
+ }
135
+
136
+ return headers;
137
+ }
138
+
139
+ /**
140
+ * Format curl command for API testing
141
+ * @param {string} method - HTTP method
142
+ * @param {string} endpoint - API endpoint
143
+ * @param {Object} headers - Request headers
144
+ * @param {Object} body - Request body (optional)
145
+ * @returns {string} Formatted curl command
146
+ */
147
+ export function formatCurlCommand(method, endpoint, headers, body = null) {
148
+ let curl = `curl -X ${method.toUpperCase()} "${endpoint}"`;
149
+
150
+ // Add headers
151
+ Object.entries(headers).forEach(([key, value]) => {
152
+ curl += ` \\\n -H "${key}: ${value}"`;
153
+ });
154
+
155
+ // Add body if present
156
+ if (body) {
157
+ curl += ` \\\n -d '${JSON.stringify(body, null, 2)}'`;
158
+ }
159
+
160
+ return curl;
161
+ }
162
+
163
+ /**
164
+ * Format request tool implementation
165
+ */
166
+ export const formatRequestTool = {
167
+ name: "payware_utils_format_request",
168
+ description: "Format and validate payware API requests with proper structure and headers",
169
+ inputSchema: {
170
+ type: "object",
171
+ properties: {
172
+ type: {
173
+ type: "string",
174
+ enum: ["transaction", "headers", "curl"],
175
+ description: "Type of formatting to perform"
176
+ },
177
+ data: {
178
+ type: "object",
179
+ description: "Data to format (structure depends on type)"
180
+ },
181
+ jwtToken: {
182
+ type: "string",
183
+ description: "JWT token for header formatting"
184
+ },
185
+ signature: {
186
+ type: "string",
187
+ description: "Request signature (optional)"
188
+ },
189
+ endpoint: {
190
+ type: "string",
191
+ description: "API endpoint URL (for curl formatting)"
192
+ },
193
+ method: {
194
+ type: "string",
195
+ enum: ["GET", "POST", "PUT", "DELETE"],
196
+ description: "HTTP method (for curl formatting)"
197
+ }
198
+ },
199
+ required: ["type"],
200
+ additionalProperties: false
201
+ },
202
+
203
+ async handler(args) {
204
+ const { type, data = {}, jwtToken, signature, endpoint, method = 'POST' } = args;
205
+
206
+ switch (type) {
207
+ case 'transaction': {
208
+ if (!data.amount) {
209
+ throw new Error("Amount is required for transaction formatting");
210
+ }
211
+
212
+ const formatted = formatTransactionRequest(data);
213
+
214
+ const minimizedJSON = createMinimizedJSON(formatted.request);
215
+
216
+ return {
217
+ content: [{
218
+ type: "text",
219
+ text: `๐Ÿ“‹ **Transaction Request Formatted**
220
+
221
+ **Formatted Request (Pretty):**
222
+ \`\`\`json
223
+ ${JSON.stringify(formatted.request, null, 2)}
224
+ \`\`\`
225
+
226
+ **Minimized JSON (API Format):**
227
+ \`\`\`json
228
+ ${minimizedJSON}
229
+ \`\`\`
230
+
231
+ **Validation Result:**
232
+ - โœ… Valid: ${formatted.validation.valid}
233
+ - Errors: ${formatted.validation.errors.length}
234
+ - Warnings: ${formatted.validation.warnings.length}
235
+
236
+ **Request Details:**
237
+ - Type: ${formatted.request.trOptions.type}
238
+ - Amount: ${formatted.request.trData.amount} ${formatted.request.trData.currency}
239
+ - Description: ${formatted.request.trData.reasonL1}
240
+ ${formatted.request.trData.reasonL2 ? `- Description L2: ${formatted.request.trData.reasonL2}` : ''}
241
+ ${formatted.request.callbackUrl ? `- Callback URL: ${formatted.request.callbackUrl}` : ''}
242
+
243
+ **Usage:**
244
+ Use the minimized JSON format for API calls to ensure MD5 consistency:
245
+ \`\`\`javascript
246
+ const response = await fetch('https://sandbox.payware.eu/api/transactions', {
247
+ method: 'POST',
248
+ headers: {
249
+ 'Authorization': 'Bearer YOUR_JWT_TOKEN',
250
+ 'Content-Type': 'application/json'
251
+ },
252
+ body: '${minimizedJSON}' // Use pre-minimized string for MD5 consistency
253
+ });
254
+ \`\`\`
255
+
256
+ **โš ๏ธ Important:** Always use the minimized JSON format to ensure the MD5 hash in your JWT token matches the request body.
257
+
258
+ **Formatted:** ${formatted.formatted}
259
+ **Timestamp:** ${formatted.timestamp}`
260
+ }]
261
+ };
262
+ }
263
+
264
+ case 'headers': {
265
+ if (!jwtToken) {
266
+ throw new Error("JWT token is required for header formatting");
267
+ }
268
+
269
+ const headers = formatAPIHeaders(jwtToken, signature);
270
+
271
+ return {
272
+ content: [{
273
+ type: "text",
274
+ text: `๐Ÿ“‹ **API Headers Formatted**
275
+
276
+ **Headers:**
277
+ \`\`\`json
278
+ ${JSON.stringify(headers, null, 2)}
279
+ \`\`\`
280
+
281
+ **Header Breakdown:**
282
+ - **Authorization**: Bearer token for authentication
283
+ - **Content-Type**: JSON content type
284
+ - **Accept**: Expects JSON response
285
+ ${signature ? '- **X-Signature**: Request signature for additional security' : ''}
286
+
287
+ **Usage in JavaScript:**
288
+ \`\`\`javascript
289
+ const headers = ${JSON.stringify(headers, null, 2)};
290
+
291
+ fetch(url, { headers, ...options });
292
+ \`\`\`
293
+
294
+ **Usage in Python:**
295
+ \`\`\`python
296
+ headers = ${JSON.stringify(headers, null, 2).replace(/"/g, "'")}
297
+
298
+ requests.post(url, headers=headers, json=data)
299
+ \`\`\`
300
+
301
+ **Usage in cURL:**
302
+ \`\`\`bash
303
+ ${Object.entries(headers).map(([key, value]) => `curl -H "${key}: ${value}"`).join(' \\\n')} \\
304
+ https://sandbox.payware.eu/api/transactions
305
+ \`\`\``
306
+ }]
307
+ };
308
+ }
309
+
310
+ case 'curl': {
311
+ if (!endpoint) {
312
+ throw new Error("Endpoint is required for curl formatting");
313
+ }
314
+
315
+ if (!jwtToken) {
316
+ throw new Error("JWT token is required for curl formatting");
317
+ }
318
+
319
+ const headers = formatAPIHeaders(jwtToken, signature);
320
+ const curl = formatCurlCommand(method, endpoint, headers, data);
321
+
322
+ return {
323
+ content: [{
324
+ type: "text",
325
+ text: `๐Ÿ’ป **cURL Command Generated**
326
+
327
+ **Command:**
328
+ \`\`\`bash
329
+ ${curl}
330
+ \`\`\`
331
+
332
+ **Command Breakdown:**
333
+ - Method: ${method.toUpperCase()}
334
+ - Endpoint: ${endpoint}
335
+ - Headers: ${Object.keys(headers).length} headers included
336
+ ${data && Object.keys(data).length > 0 ? `- Body: JSON payload included` : '- Body: No payload'}
337
+
338
+ **Usage:**
339
+ 1. Copy the command above
340
+ 2. Paste it into your terminal
341
+ 3. Execute to test the API call directly
342
+ 4. Modify parameters as needed
343
+
344
+ **Response:**
345
+ The command will return the API response directly in your terminal, including:
346
+ - HTTP status code
347
+ - Response headers
348
+ - JSON response body
349
+ - Any error messages
350
+
351
+ **Tips:**
352
+ - Add \`-v\` flag for verbose output with headers
353
+ - Add \`-i\` flag to include response headers
354
+ - Use \`-o filename\` to save response to file`
355
+ }]
356
+ };
357
+ }
358
+
359
+ default:
360
+ throw new Error(`Unknown formatting type: ${type}`);
361
+ }
362
+ }
363
+ };
@@ -0,0 +1,249 @@
1
+ /**
2
+ * JSON formatter utility for deterministic serialization
3
+ * Helps developers format JSON correctly for payware API requests
4
+ */
5
+
6
+ import { createDeterministicJSON, validateJSONConsistency } from '../core/utils/json-serializer.js';
7
+ import { generateContentMd5 } from '../core/auth/jwt-token.js';
8
+
9
+ /**
10
+ * Format JSON with deterministic serialization tool
11
+ */
12
+ export const formatJSONDeterministicTool = {
13
+ name: "payware_utils_format_json_deterministic",
14
+ description: "Format JSON payload with deterministic key sorting for consistent MD5 calculation and payware API compliance",
15
+ inputSchema: {
16
+ type: "object",
17
+ properties: {
18
+ payload: {
19
+ type: "object",
20
+ description: "JSON payload to format with deterministic serialization"
21
+ },
22
+ showComparison: {
23
+ type: "boolean",
24
+ description: "Show comparison between standard and deterministic JSON",
25
+ default: true
26
+ },
27
+ calculateMD5: {
28
+ type: "boolean",
29
+ description: "Calculate MD5 hash for JWT contentMd5 usage",
30
+ default: true
31
+ },
32
+ validateConsistency: {
33
+ type: "boolean",
34
+ description: "Validate that multiple serializations produce the same result",
35
+ default: false
36
+ }
37
+ },
38
+ required: ["payload"],
39
+ additionalProperties: false
40
+ },
41
+
42
+ async handler(args) {
43
+ const {
44
+ payload,
45
+ showComparison = true,
46
+ calculateMD5 = true,
47
+ validateConsistency = false
48
+ } = args;
49
+
50
+ if (!payload || typeof payload !== 'object') {
51
+ throw new Error("Payload must be a valid JSON object");
52
+ }
53
+
54
+ try {
55
+ // Generate different serializations
56
+ const standardJson = JSON.stringify(payload);
57
+ const prettyJson = JSON.stringify(payload, null, 2);
58
+ const deterministicJson = createDeterministicJSON(payload);
59
+
60
+ // Calculate MD5 hash
61
+ const md5Hash = calculateMD5 ? generateContentMd5(payload) : null;
62
+
63
+ // Get key ordering information
64
+ const originalKeys = Object.keys(payload);
65
+ const sortedKeys = [...originalKeys].sort();
66
+ const isAlreadySorted = JSON.stringify(originalKeys) === JSON.stringify(sortedKeys);
67
+
68
+ // Consistency validation
69
+ let consistencyTest = null;
70
+ if (validateConsistency) {
71
+ // Create multiple instances and test consistency
72
+ const testPayload1 = { ...payload };
73
+ const testPayload2 = JSON.parse(JSON.stringify(payload));
74
+ const testPayload3 = Object.assign({}, payload);
75
+
76
+ consistencyTest = {
77
+ test1: createDeterministicJSON(testPayload1),
78
+ test2: createDeterministicJSON(testPayload2),
79
+ test3: createDeterministicJSON(testPayload3),
80
+ allMatch: validateJSONConsistency(testPayload1, testPayload2) &&
81
+ validateJSONConsistency(testPayload2, testPayload3)
82
+ };
83
+ }
84
+
85
+ return {
86
+ content: [{
87
+ type: "text",
88
+ text: `๐Ÿ“‹ **JSON Deterministic Formatter**
89
+
90
+ ## Input Payload Analysis
91
+
92
+ **Original Key Order**: [${originalKeys.join(', ')}]
93
+ **Sorted Key Order**: [${sortedKeys.join(', ')}]
94
+ **Already Sorted**: ${isAlreadySorted ? 'โœ… Yes' : 'โŒ No (will be reordered)'}
95
+
96
+ ${showComparison ? `## JSON Serialization Comparison
97
+
98
+ **โŒ Standard JSON.stringify()** (property order varies):
99
+ \`\`\`json
100
+ ${standardJson}
101
+ \`\`\`
102
+
103
+ **๐Ÿ“– Pretty JSON** (formatted but inconsistent order):
104
+ \`\`\`json
105
+ ${prettyJson}
106
+ \`\`\`
107
+
108
+ **โœ… Deterministic JSON** (sorted keys, consistent):
109
+ \`\`\`json
110
+ ${deterministicJson}
111
+ \`\`\`
112
+
113
+ ### Key Differences:
114
+ ${isAlreadySorted ?
115
+ '- โœ… Your object keys are already in sorted order' :
116
+ `- โŒ Keys will be reordered: ${originalKeys.join(' โ†’ ')} becomes ${sortedKeys.join(' โ†’ ')}`}
117
+ - โœ… Deterministic JSON ensures consistent serialization across platforms
118
+ - โœ… Required for accurate MD5 hash calculation in JWT tokens
119
+ ` : ''}
120
+
121
+ ## โœ… **payware API Ready JSON**
122
+
123
+ Use this exact JSON string for both JWT MD5 calculation and HTTP request body:
124
+
125
+ \`\`\`json
126
+ ${deterministicJson}
127
+ \`\`\`
128
+
129
+ ${calculateMD5 ? `## ๐Ÿ”’ **MD5 Hash for JWT**
130
+
131
+ **contentMd5**: \`${md5Hash}\`
132
+
133
+ **Usage in JWT Header**:
134
+ \`\`\`json
135
+ {
136
+ "alg": "RS256",
137
+ "typ": "JWT",
138
+ "contentMd5": "${md5Hash}"
139
+ }
140
+ \`\`\`
141
+ ` : ''}
142
+
143
+ ## ๐Ÿ›  **Implementation Guide**
144
+
145
+ ### Python Implementation:
146
+ \`\`\`python
147
+ import json
148
+
149
+ # CORRECT: Use deterministic JSON
150
+ json_body = json.dumps(payload, sort_keys=True, separators=(',', ':'))
151
+ jwt_token = create_jwt_token(json_body)
152
+ response = requests.post(url, data=json_body, headers=headers)
153
+ \`\`\`
154
+
155
+ ### Node.js Implementation:
156
+ \`\`\`javascript
157
+ // Helper function for deterministic JSON
158
+ function createDeterministicJSON(obj) {
159
+ return JSON.stringify(obj, Object.keys(obj).sort());
160
+ }
161
+
162
+ // CORRECT: Use deterministic JSON
163
+ const jsonBody = createDeterministicJSON(payload);
164
+ const jwtToken = createJWTToken(jsonBody);
165
+ const response = await axios.post(url, jsonBody, {headers});
166
+ \`\`\`
167
+
168
+ ### PHP Implementation:
169
+ \`\`\`php
170
+ // CORRECT: Use deterministic JSON
171
+ function createDeterministicJSON($obj) {
172
+ ksort($obj);
173
+ return json_encode($obj, JSON_UNESCAPED_SLASHES);
174
+ }
175
+
176
+ $jsonBody = createDeterministicJSON($payload);
177
+ $jwtToken = createJWTToken($jsonBody);
178
+ // Use $jsonBody for both JWT and cURL request
179
+ \`\`\`
180
+
181
+ ${consistencyTest ? `## ๐Ÿงช **Consistency Validation**
182
+
183
+ **Test Results**:
184
+ - Test 1: \`${consistencyTest.test1}\`
185
+ - Test 2: \`${consistencyTest.test2}\`
186
+ - Test 3: \`${consistencyTest.test3}\`
187
+
188
+ **All Match**: ${consistencyTest.allMatch ? 'โœ… Consistent' : 'โŒ Inconsistent'}
189
+
190
+ ${!consistencyTest.allMatch ? 'โš ๏ธ **Warning**: Inconsistent serialization detected. This could cause MD5 mismatches.' : ''}
191
+ ` : ''}
192
+
193
+ ## โš ๏ธ **PAYWARE Critical Requirements**
194
+
195
+ 1. **Same String Requirement**: The EXACT same compact JSON string must be used for both JWT contentMd5 and HTTP request body
196
+ 2. **Compact Format**: No extra whitespace (spaces, newlines) in JSON
197
+ 3. **Consistent Serialization**: Use deterministic serialization to prevent variations
198
+ 4. **MD5 Consistency**: Any difference will cause ERR_INVALID_MD5 authentication failures
199
+
200
+ Note: Sorted keys are our METHOD to guarantee requirement #1, not a payware requirement itself.
201
+
202
+ ## ๐Ÿ”— **Next Steps**
203
+
204
+ 1. Use the deterministic JSON above in your API request
205
+ 2. Include the MD5 hash in your JWT header${calculateMD5 ? ` (\`${md5Hash}\`)` : ''}
206
+ 3. Send the exact same JSON string in your HTTP request body
207
+ 4. Verify with \`payware_authentication_validate_jwt\` tool
208
+
209
+ ---
210
+ **Execution Info:**
211
+ - Tool: payware_utils_format_json_deterministic
212
+ - Keys Processed: ${originalKeys.length}
213
+ - Sorting Required: ${!isAlreadySorted}
214
+ - MD5 Calculated: ${calculateMD5}
215
+ - Executed: ${new Date().toISOString()}
216
+ - Status: โœ… Complete`
217
+ }]
218
+ };
219
+ } catch (error) {
220
+ return {
221
+ content: [{
222
+ type: "text",
223
+ text: `โŒ **JSON Formatting Failed**
224
+
225
+ **Error**: ${error.message}
226
+
227
+ **Common Issues**:
228
+ - Invalid JSON structure
229
+ - Circular references in payload
230
+ - Non-serializable values (functions, undefined, etc.)
231
+ - Different JSON strings used for JWT MD5 vs HTTP body
232
+
233
+ **Debug Steps for MD5 Errors**:
234
+ 1. Verify the SAME string is used for JWT contentMd5 and HTTP body
235
+ 2. Check for extra whitespace or formatting differences
236
+ 3. Ensure consistent property ordering (use deterministic serialization)
237
+ 4. Validate that objects are properly serializable
238
+ 5. Test with the consistency validation option
239
+
240
+ ---
241
+ **Execution Info:**
242
+ - Tool: payware_utils_format_json_deterministic
243
+ - Executed: ${new Date().toISOString()}
244
+ - Status: โŒ Error`
245
+ }]
246
+ };
247
+ }
248
+ }
249
+ };