@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.
Files changed (127) hide show
  1. package/.env.example +0 -0
  2. package/README.md +22 -16
  3. package/check-keys.js +0 -0
  4. package/keys/README.md +0 -0
  5. package/mcp-config.json +0 -0
  6. package/package.json +69 -66
  7. package/payware-mcp-config.json +0 -0
  8. package/src/auth/rsa-keys.js +0 -0
  9. package/src/auth/sandbox-setup.js +186 -186
  10. package/src/config/env.js +0 -0
  11. package/src/config/partner-types.js +0 -0
  12. package/src/core/auth/jwt-factory.js +4 -4
  13. package/src/core/auth/jwt-token.js +107 -66
  14. package/src/core/utils/json-serializer.js +5 -5
  15. package/src/core/utils/key-utils.js +0 -0
  16. package/src/index.js +0 -0
  17. package/src/isv/data/index.js +0 -0
  18. package/src/isv/deep-links/delete-product-link.js +0 -0
  19. package/src/isv/deep-links/delete-transaction-link.js +0 -0
  20. package/src/isv/deep-links/get-product-link.js +0 -0
  21. package/src/isv/deep-links/get-transaction-link.js +0 -0
  22. package/src/isv/deep-links/index.js +0 -0
  23. package/src/isv/index.js +29 -1
  24. package/src/isv/invitations/index.js +471 -0
  25. package/src/isv/oauth2/create-token-simple.js +0 -0
  26. package/src/isv/oauth2/get-token-info.js +0 -0
  27. package/src/isv/oauth2/obtain-token.js +19 -8
  28. package/src/isv/oauth2/token-helper.js +5 -2
  29. package/src/isv/poi/cancel-price.js +2 -10
  30. package/src/isv/poi/create.js +19 -0
  31. package/src/isv/poi/get-poi.js +2 -10
  32. package/src/isv/poi/get-qrcode.js +2 -10
  33. package/src/isv/poi/get-status.js +2 -10
  34. package/src/isv/poi/index.js +5 -0
  35. package/src/isv/poi/list-pois.js +2 -10
  36. package/src/isv/poi/set-price.js +217 -225
  37. package/src/isv/products/audios.js +0 -0
  38. package/src/isv/products/create-product.js +313 -313
  39. package/src/isv/products/delete-product.js +0 -0
  40. package/src/isv/products/get-product-image.js +0 -0
  41. package/src/isv/products/get-product.js +0 -0
  42. package/src/isv/products/index.js +0 -0
  43. package/src/isv/products/list-products.js +0 -0
  44. package/src/isv/products/schedules.js +0 -0
  45. package/src/isv/products/update-product.js +264 -264
  46. package/src/isv/reference/index.js +249 -0
  47. package/src/isv/shops/index.js +22 -0
  48. package/src/isv/transactions/cancel-transaction.js +43 -12
  49. package/src/isv/transactions/create-transaction.js +22 -6
  50. package/src/isv/transactions/get-status.js +18 -11
  51. package/src/isv/transactions/process-transaction.js +160 -42
  52. package/src/isv/transactions/simulate-callback.js +128 -109
  53. package/src/isv/transactions/transaction-history.js +86 -90
  54. package/src/isv/utils/auth.js +2 -2
  55. package/src/mcp-proxy.js +0 -0
  56. package/src/merchant/data/export-report.js +0 -0
  57. package/src/merchant/data/generate-report.js +19 -1
  58. package/src/merchant/data/get-export-list.js +0 -0
  59. package/src/merchant/data/get-report-requests.js +0 -0
  60. package/src/merchant/data/get-report-status.js +0 -0
  61. package/src/merchant/data/index.js +0 -0
  62. package/src/merchant/data/list-reports.js +0 -0
  63. package/src/merchant/deep-links/delete-product-link.js +0 -0
  64. package/src/merchant/deep-links/delete-transaction-link.js +0 -0
  65. package/src/merchant/deep-links/get-product-link.js +0 -0
  66. package/src/merchant/deep-links/get-transaction-link.js +0 -0
  67. package/src/merchant/deep-links/index.js +0 -0
  68. package/src/merchant/index.js +21 -1
  69. package/src/merchant/logs/index.js +366 -0
  70. package/src/merchant/products/audios.js +0 -0
  71. package/src/merchant/products/create-product.js +287 -287
  72. package/src/merchant/products/delete-product.js +0 -0
  73. package/src/merchant/products/get-product-image.js +0 -0
  74. package/src/merchant/products/get-product.js +0 -0
  75. package/src/merchant/products/index.js +0 -0
  76. package/src/merchant/products/list-products.js +0 -0
  77. package/src/merchant/products/schedules.js +0 -0
  78. package/src/merchant/products/update-product.js +281 -281
  79. package/src/merchant/shops/index.js +18 -0
  80. package/src/merchant/transactions/cancel-transaction.js +16 -19
  81. package/src/merchant/transactions/create-transaction.js +99 -19
  82. package/src/merchant/transactions/get-status.js +41 -12
  83. package/src/merchant/transactions/process-transaction.js +8 -16
  84. package/src/merchant/transactions/simulate-callback.js +68 -9
  85. package/src/merchant/transactions/transaction-history.js +275 -257
  86. package/src/payment-institution/data/index.js +0 -0
  87. package/src/payment-institution/deep-links/delete-transaction-link.js +0 -0
  88. package/src/payment-institution/deep-links/get-transaction-link.js +0 -0
  89. package/src/payment-institution/deep-links/index.js +0 -0
  90. package/src/payment-institution/index.js +0 -0
  91. package/src/payment-institution/transactions/create-pi-transaction.js +6 -14
  92. package/src/payment-institution/transactions/finalize-transaction.js +333 -340
  93. package/src/payment-institution/transactions/get-transaction-history.js +3 -11
  94. package/src/payment-institution/transactions/get-transaction-status.js +39 -12
  95. package/src/payment-institution/transactions/process-pi-transaction.js +5 -13
  96. package/src/payment-institution/transactions/simulate-pi-callback.js +49 -9
  97. package/src/payment-institution/transactions/soundbite-transaction.js +5 -13
  98. package/src/proxy-server.js +0 -0
  99. package/src/shared/api-errors.js +188 -0
  100. package/src/shared/auth-headers.js +62 -0
  101. package/src/shared/data/data-api.js +11 -10
  102. package/src/shared/deep-links/api.js +5 -4
  103. package/src/shared/index.js +0 -0
  104. package/src/shared/logs/logs-api.js +242 -0
  105. package/src/shared/poi/poi-create-api.js +419 -0
  106. package/src/shared/products/products-api.js +17 -16
  107. package/src/shared/shops/shops-api.js +155 -0
  108. package/src/tools/advanced-code-generator.js +2255 -2246
  109. package/src/utils/code-examples/common/auth-examples.js +1484 -1481
  110. package/src/utils/code-examples/common/helpers.js +7 -4
  111. package/src/utils/code-examples/common/utils-examples.js +0 -0
  112. package/src/utils/code-examples/frameworks/examples.js +4 -4
  113. package/src/utils/code-examples/index.js +0 -0
  114. package/src/utils/code-examples/isv/oauth2.js +458 -600
  115. package/src/utils/code-examples/isv/products.js +8 -8
  116. package/src/utils/code-examples/isv/transactions.js +815 -812
  117. package/src/utils/code-examples/merchant/products.js +0 -0
  118. package/src/utils/code-examples/merchant/transactions.js +9 -9
  119. package/src/utils/code-examples/payment-institution/transactions.js +0 -0
  120. package/src/utils/code-examples/shared/data.js +0 -0
  121. package/src/utils/code-examples/shared/deep-links.js +0 -0
  122. package/src/utils/code-examples/shared/soundbites.js +0 -0
  123. package/src/utils/code-examples.js +24 -24
  124. package/src/utils/format-request.js +3 -3
  125. package/src/utils/json-formatter.js +20 -20
  126. package/src/utils/server-info.js +0 -0
  127. package/MCP-SETUP.md +0 -190
@@ -99,7 +99,7 @@ async function createISVJWT({ isvPartnerId, merchantPartnerId, oauth2Token, priv
99
99
  * Create JWT token with custom payload (for ISV use case)
100
100
  * @param {Object} payload - Custom JWT payload
101
101
  * @param {string} privateKey - Private key for signing
102
- * @param {Object} requestBody - Request body for content MD5
102
+ * @param {Object} requestBody - Request body for content SHA-256
103
103
  * @returns {Object} JWT token data
104
104
  */
105
105
  async function createJWTTokenWithCustomPayload(payload, privateKey, requestBody) {
@@ -116,11 +116,11 @@ async function createJWTTokenWithCustomPayload(payload, privateKey, requestBody)
116
116
  typ: 'JWT'
117
117
  };
118
118
 
119
- // Add contentMd5 to header if request body exists
119
+ // Add contentSha256 to header if request body exists (SHA-256 is preferred over MD5)
120
120
  if (requestBody) {
121
121
  const { createMinimizedJSON } = await import('../utils/json-serializer.js');
122
122
  const bodyString = createMinimizedJSON(requestBody);
123
- header.contentMd5 = crypto.default.createHash('md5').update(bodyString, 'utf8').digest('base64');
123
+ header.contentSha256 = crypto.default.createHash('sha256').update(bodyString, 'utf8').digest('base64');
124
124
  }
125
125
 
126
126
  const token = jwt.default.sign(payload, normalizedKey, {
@@ -134,7 +134,7 @@ async function createJWTTokenWithCustomPayload(payload, privateKey, requestBody)
134
134
  audience: payload.aud,
135
135
  subject: payload.sub,
136
136
  issuedAt: new Date(payload.iat * 1000).toISOString(),
137
- contentMd5: header.contentMd5 || null,
137
+ contentSha256: header.contentSha256 || null,
138
138
  hasBody: !!requestBody,
139
139
  isISV: true
140
140
  };
@@ -5,14 +5,36 @@ import { getPartnerIdSafe, getPrivateKeySafe } from '../../config/env.js';
5
5
  import { createDeterministicJSON } from '../utils/json-serializer.js';
6
6
 
7
7
  /**
8
- * Generate MD5 hash for JWT contentMd5 header
8
+ * Generate SHA-256 hash for JWT contentSha256 header (PREFERRED)
9
9
  *
10
- * PAYWARE REQUIREMENT: The MD5 hash must be calculated from the EXACT same
10
+ * PAYWARE REQUIREMENT: The SHA-256 hash must be calculated from the EXACT same
11
11
  * compact JSON string that will be sent as the HTTP request body.
12
12
  *
13
13
  * CRITICAL: Any difference in whitespace, property order, or formatting
14
- * between the string used for MD5 and the HTTP body will cause authentication
15
- * failures with ERR_INVALID_MD5 errors.
14
+ * between the string used for hash and the HTTP body will cause authentication
15
+ * failures with ERR_INVALID_CONTENT_HASH errors.
16
+ *
17
+ * @param {Object|string} body - Request body (object will be consistently serialized)
18
+ * @returns {string} Base64 encoded SHA-256 hash for JWT contentSha256 header
19
+ */
20
+ export function generateContentSha256(body) {
21
+ if (!body) return null;
22
+
23
+ // CRITICAL: Use deterministic JSON serialization with sorted keys
24
+ // This ensures consistent hashes regardless of property order
25
+ const bodyString = typeof body === 'string' ? body : createDeterministicJSON(body);
26
+
27
+ // Generate SHA-256 hash as raw binary and encode in base64
28
+ const hash = crypto.createHash('sha256');
29
+ hash.update(bodyString, 'utf8');
30
+ return hash.digest('base64');
31
+ }
32
+
33
+ /**
34
+ * Generate MD5 hash for JWT contentMd5 header (DEPRECATED - use generateContentSha256)
35
+ *
36
+ * @deprecated Use generateContentSha256 instead. MD5 is still supported by the server
37
+ * for backwards compatibility but is deprecated.
16
38
  *
17
39
  * @param {Object|string} body - Request body (object will be consistently serialized)
18
40
  * @returns {string} Base64 encoded MD5 hash for JWT contentMd5 header
@@ -20,8 +42,6 @@ import { createDeterministicJSON } from '../utils/json-serializer.js';
20
42
  export function generateContentMd5(body) {
21
43
  if (!body) return null;
22
44
 
23
- // CRITICAL: Use deterministic JSON serialization with sorted keys
24
- // This ensures consistent MD5 hashes regardless of property order
25
45
  const bodyString = typeof body === 'string' ? body : createDeterministicJSON(body);
26
46
 
27
47
  // Generate MD5 hash as raw binary and encode in base64
@@ -31,17 +51,17 @@ export function generateContentMd5(body) {
31
51
  }
32
52
 
33
53
  /**
34
- * Generate the exact JSON string for both JWT MD5 and HTTP request body
54
+ * Generate the exact JSON string for both JWT content hash and HTTP request body
35
55
  *
36
56
  * PAYWARE REQUIREMENT: The same compact JSON string must be used for:
37
- * 1. JWT contentMd5 calculation (passed to generateContentMd5)
57
+ * 1. JWT contentSha256 calculation (passed to generateContentSha256)
38
58
  * 2. HTTP request body (sent to payware API)
39
59
  *
40
60
  * This function guarantees consistency by using deterministic serialization.
41
61
  * Any mismatch between these two uses will result in authentication failure.
42
62
  *
43
63
  * @param {Object} payload - Request payload object
44
- * @returns {string} Compact JSON string for both MD5 and HTTP body
64
+ * @returns {string} Compact JSON string for both hash calculation and HTTP body
45
65
  */
46
66
  export function serializePayload(payload) {
47
67
  if (!payload) return null;
@@ -71,11 +91,11 @@ export function createJWTToken(partnerId, privateKey, requestBody = null) {
71
91
  typ: 'JWT'
72
92
  };
73
93
 
74
- // Add contentMd5 to header if request body exists (for POST/PUT/PATCH)
94
+ // Add contentSha256 to header if request body exists (for POST/PUT/PATCH)
75
95
  // CRITICAL: The requestBody here must be the EXACT same string that will
76
- // be sent as the HTTP request body, otherwise MD5 will not match
96
+ // be sent as the HTTP request body, otherwise hash will not match
77
97
  if (requestBody) {
78
- header.contentMd5 = generateContentMd5(requestBody);
98
+ header.contentSha256 = generateContentSha256(requestBody);
79
99
  }
80
100
 
81
101
  // JWT Payload (as per documentation)
@@ -95,7 +115,7 @@ export function createJWTToken(partnerId, privateKey, requestBody = null) {
95
115
  partnerId,
96
116
  audience: 'https://payware.eu',
97
117
  issuedAt: new Date(now * 1000).toISOString(),
98
- contentMd5: header.contentMd5 || null,
118
+ contentSha256: header.contentSha256 || null,
99
119
  hasBody: !!requestBody
100
120
  };
101
121
  }
@@ -125,7 +145,7 @@ export function createRequestSignature(method, path, body, privateKey) {
125
145
  */
126
146
  export const validateJWTTokenTool = {
127
147
  name: "payware_authentication_validate_jwt",
128
- description: "Validate and debug JWT token structure, decode payload/headers, verify MD5 calculation, and check RS256 signature format for payware API compliance",
148
+ description: "Validate and debug JWT token structure, decode payload/headers, verify content hash (SHA-256/MD5) calculation, and check RS256 signature format for payware API compliance",
129
149
  inputSchema: {
130
150
  type: "object",
131
151
  properties: {
@@ -135,7 +155,7 @@ export const validateJWTTokenTool = {
135
155
  },
136
156
  expectedPayload: {
137
157
  type: "object",
138
- description: "Expected request body (optional - for MD5 validation)"
158
+ description: "Expected request body (optional - for content hash validation)"
139
159
  }
140
160
  },
141
161
  required: ["jwtToken"],
@@ -159,6 +179,11 @@ export const validateJWTTokenTool = {
159
179
 
160
180
  const { header, payload } = decoded;
161
181
 
182
+ // Determine which hash type is used
183
+ const hasContentSha256 = !!header.contentSha256;
184
+ const hasContentMd5 = !!header.contentMd5;
185
+ const hasContentHash = hasContentSha256 || hasContentMd5;
186
+
162
187
  // Validation results
163
188
  const validation = {
164
189
  structure: true,
@@ -167,19 +192,31 @@ export const validateJWTTokenTool = {
167
192
  audience: payload.aud === 'https://payware.eu',
168
193
  issuer: !!payload.iss,
169
194
  issuedAt: !!payload.iat,
170
- contentMd5: header.contentMd5 ? true : false
195
+ contentHash: hasContentHash
171
196
  };
172
197
 
173
- // MD5 validation if expected payload provided
174
- let md5Validation = null;
175
- if (expectedPayload && header.contentMd5) {
176
- const calculatedMd5 = generateContentMd5(expectedPayload);
177
- md5Validation = {
178
- provided: header.contentMd5,
179
- calculated: calculatedMd5,
180
- matches: header.contentMd5 === calculatedMd5,
181
- deterministicJson: createDeterministicJSON(expectedPayload)
182
- };
198
+ // Content hash validation if expected payload provided
199
+ let hashValidation = null;
200
+ if (expectedPayload && hasContentHash) {
201
+ if (hasContentSha256) {
202
+ const calculatedSha256 = generateContentSha256(expectedPayload);
203
+ hashValidation = {
204
+ type: 'SHA-256 (preferred)',
205
+ provided: header.contentSha256,
206
+ calculated: calculatedSha256,
207
+ matches: header.contentSha256 === calculatedSha256,
208
+ deterministicJson: createDeterministicJSON(expectedPayload)
209
+ };
210
+ } else if (hasContentMd5) {
211
+ const calculatedMd5 = generateContentMd5(expectedPayload);
212
+ hashValidation = {
213
+ type: 'MD5 (deprecated)',
214
+ provided: header.contentMd5,
215
+ calculated: calculatedMd5,
216
+ matches: header.contentMd5 === calculatedMd5,
217
+ deterministicJson: createDeterministicJSON(expectedPayload)
218
+ };
219
+ }
183
220
  }
184
221
 
185
222
  // Check for common issues
@@ -189,11 +226,14 @@ export const validateJWTTokenTool = {
189
226
  if (!validation.audience) issues.push("Audience should be 'https://payware.eu'");
190
227
  if (!validation.issuer) issues.push("Missing issuer (iss) claim");
191
228
  if (!validation.issuedAt) issues.push("Missing issued at (iat) claim");
192
- if (md5Validation && !md5Validation.matches) {
193
- issues.push("MD5 hash mismatch - ensure deterministic JSON serialization with sorted keys");
229
+ if (hasContentMd5 && !hasContentSha256) {
230
+ issues.push("Using deprecated MD5 hash - consider upgrading to SHA-256 (contentSha256)");
231
+ }
232
+ if (hashValidation && !hashValidation.matches) {
233
+ issues.push("Content hash mismatch - ensure deterministic JSON serialization with sorted keys");
194
234
  }
195
235
 
196
- const overallValid = Object.values(validation).every(v => v) && (!md5Validation || md5Validation.matches);
236
+ const overallValid = Object.values(validation).every(v => v) && (!hashValidation || hashValidation.matches);
197
237
 
198
238
  return {
199
239
  content: [{
@@ -222,26 +262,26 @@ ${validation.type ? '✅' : '❌'} **Type**: JWT ${validation.type ? '(correct)'
222
262
  ${validation.audience ? '✅' : '❌'} **Audience**: https://payware.eu ${validation.audience ? '(correct)' : `(found: ${payload.aud})`}
223
263
  ${validation.issuer ? '✅' : '❌'} **Issuer**: ${payload.iss || 'Missing'}
224
264
  ${validation.issuedAt ? '✅' : '❌'} **Issued At**: ${payload.iat ? new Date(payload.iat * 1000).toISOString() : 'Missing'}
225
- ${validation.contentMd5 ? '✅' : 'ℹ️'} **Content MD5**: ${header.contentMd5 || 'Not present (OK for GET requests)'}
265
+ ${validation.contentHash ? '✅' : 'ℹ️'} **Content Hash**: ${header.contentSha256 ? `SHA-256: ${header.contentSha256}` : header.contentMd5 ? `MD5 (deprecated): ${header.contentMd5}` : 'Not present (OK for GET requests)'}
226
266
 
227
- ${md5Validation ? `## MD5 Hash Validation
267
+ ${hashValidation ? `## Content Hash Validation
228
268
 
229
- ${md5Validation.matches ? '✅' : '❌'} **MD5 Match**: ${md5Validation.matches ? 'Correct' : 'Mismatch detected'}
230
- **Provided**: \`${md5Validation.provided}\`
231
- **Calculated**: \`${md5Validation.calculated}\`
269
+ ${hashValidation.matches ? '✅' : '❌'} **${hashValidation.type} Match**: ${hashValidation.matches ? 'Correct' : 'Mismatch detected'}
270
+ **Provided**: \`${hashValidation.provided}\`
271
+ **Calculated**: \`${hashValidation.calculated}\`
232
272
 
233
273
  **Deterministic JSON Used**:
234
274
  \`\`\`json
235
- ${md5Validation.deterministicJson}
275
+ ${hashValidation.deterministicJson}
236
276
  \`\`\`
237
277
 
238
- ${!md5Validation.matches ? `
239
- ⚠️ **MD5 Mismatch Debugging**:
278
+ ${!hashValidation.matches ? `
279
+ ⚠️ **Hash Mismatch Debugging**:
240
280
 
241
- PAYWARE REQUIREMENT: The EXACT same compact JSON string must be used for both JWT contentMd5 and HTTP body.
281
+ PAYWARE REQUIREMENT: The EXACT same compact JSON string must be used for both JWT content hash and HTTP body.
242
282
 
243
283
  **Common Causes**:
244
- 1. Different JSON strings used for JWT MD5 vs HTTP body
284
+ 1. Different JSON strings used for JWT hash vs HTTP body
245
285
  2. Extra whitespace or formatting differences
246
286
  3. Property order variations between serializations
247
287
 
@@ -268,7 +308,7 @@ ${issues.includes("Type should be 'JWT'") ? '- Set JWT type to "JWT"\n' : ''}
268
308
  ${issues.includes("Audience should be 'https://payware.eu'") ? '- Set audience to "https://payware.eu" (not just "payware")\n' : ''}
269
309
  ${issues.includes("Missing issuer (iss) claim") ? '- Add your partner ID as the issuer claim\n' : ''}
270
310
  ${issues.includes("Missing issued at (iat) claim") ? '- Add current Unix timestamp as issued at claim\n' : ''}
271
- ${issues.some(i => i.includes('MD5 hash mismatch')) ? '- Use deterministic JSON serialization with sorted keys\n- Ensure same JSON string for MD5 and HTTP body\n' : ''}
311
+ ${issues.some(i => i.includes('hash mismatch')) ? '- Use deterministic JSON serialization with sorted keys\n- Ensure same JSON string for hash calculation and HTTP body\n' : ''}
272
312
  ` : '## ✅ No Issues Found\n\nYour JWT token is properly formatted for payware API usage.'}
273
313
 
274
314
  ## Technical Details
@@ -324,7 +364,7 @@ ${issues.some(i => i.includes('MD5 hash mismatch')) ? '- Use deterministic JSON
324
364
  */
325
365
  export const testJWTTokenTool = {
326
366
  name: "payware_authentication_test_jwt",
327
- description: "Test JWT token creation process with detailed step-by-step breakdown, showing deterministic JSON serialization, MD5 calculation, and final JWT structure",
367
+ description: "Test JWT token creation process with detailed step-by-step breakdown, showing deterministic JSON serialization, SHA-256 hash calculation, and final JWT structure",
328
368
  inputSchema: {
329
369
  type: "object",
330
370
  properties: {
@@ -334,7 +374,7 @@ export const testJWTTokenTool = {
334
374
  },
335
375
  requestPayload: {
336
376
  type: "object",
337
- description: "Sample request payload to demonstrate MD5 calculation"
377
+ description: "Sample request payload to demonstrate SHA-256 hash calculation"
338
378
  },
339
379
  showPrivateKey: {
340
380
  type: "boolean",
@@ -387,27 +427,27 @@ ${deterministicJson}
387
427
 
388
428
  **Key Sorting**: [${sortedKeys.join(', ')}]`);
389
429
 
390
- // Step 2: MD5 Calculation
391
- const md5Hash = generateContentMd5(requestPayload);
430
+ // Step 2: SHA-256 Calculation
431
+ const sha256Hash = generateContentSha256(requestPayload);
392
432
 
393
- steps.push(`**Step 2: MD5 Hash Calculation**
433
+ steps.push(`**Step 2: SHA-256 Hash Calculation**
394
434
  Using deterministic JSON string:
395
435
  \`\`\`
396
436
  ${deterministicJson}
397
437
  \`\`\`
398
438
 
399
- MD5 hash (Base64): \`${md5Hash}\`
439
+ SHA-256 hash (Base64): \`${sha256Hash}\`
400
440
 
401
441
  **Process**:
402
- 1. Create MD5 hash from UTF-8 bytes of deterministic JSON
442
+ 1. Create SHA-256 hash from UTF-8 bytes of deterministic JSON
403
443
  2. Encode hash as Base64
404
- 3. Include in JWT header as \`contentMd5\``);
444
+ 3. Include in JWT header as \`contentSha256\``);
405
445
 
406
446
  // Step 3: JWT Header Construction
407
447
  const jwtHeader = {
408
448
  alg: 'RS256',
409
449
  typ: 'JWT',
410
- contentMd5: md5Hash
450
+ contentSha256: sha256Hash
411
451
  };
412
452
 
413
453
  steps.push(`**Step 3: JWT Header Construction**
@@ -418,7 +458,7 @@ ${JSON.stringify(jwtHeader, null, 2)}
418
458
  **Requirements**:
419
459
  - Algorithm: RS256 (RSA with SHA-256)
420
460
  - Type: JWT
421
- - Content MD5: ${md5Hash}`);
461
+ - Content SHA-256: ${sha256Hash}`);
422
462
 
423
463
  // Step 4: JWT Payload Construction
424
464
  const now = Math.floor(Date.now() / 1000);
@@ -498,18 +538,19 @@ ${deterministicJson}
498
538
  ## 🔑 **Key Insights**
499
539
 
500
540
  1. **Deterministic JSON is Critical**: Property order MUST be sorted
501
- 2. **MD5 Consistency**: Same JSON string for both MD5 and HTTP body
541
+ 2. **Hash Consistency**: Same JSON string for both SHA-256 hash and HTTP body
502
542
  3. **Audience Requirement**: Must be "https://payware.eu" (include https://)
503
543
  4. **RS256 Algorithm**: RSA with SHA-256 signature
504
- 5. **Content MD5 in Header**: Not in payload, but in JWT header
544
+ 5. **Content Hash in Header**: Not in payload, but in JWT header (use contentSha256)
505
545
 
506
546
  ## 🐛 **Common Mistakes to Avoid**
507
547
 
508
548
  ❌ Using \`JSON.stringify()\` without sorted keys
509
- ❌ Different JSON for MD5 vs HTTP request
549
+ ❌ Different JSON for hash calculation vs HTTP request
510
550
  ❌ Setting audience to "payware" instead of "https://payware.eu"
511
- ❌ Putting contentMd5 in payload instead of header
551
+ ❌ Putting contentSha256 in payload instead of header
512
552
  ❌ Using wrong algorithm (HS256 instead of RS256)
553
+ ❌ Using deprecated contentMd5 instead of contentSha256
513
554
 
514
555
  ---
515
556
  **Execution Info:**
@@ -567,7 +608,7 @@ export const createJWTTokenTool = {
567
608
  },
568
609
  requestBody: {
569
610
  type: "object",
570
- description: "Request body (for POST/PUT/PATCH requests that need contentMd5)"
611
+ description: "Request body (for POST/PUT/PATCH requests that need contentSha256)"
571
612
  }
572
613
  },
573
614
  additionalProperties: false
@@ -619,14 +660,14 @@ ${tokenData.token}
619
660
  - Issued At (iat): ${tokenData.issuedAt}
620
661
  - Algorithm: RS256
621
662
  - Type: JWT
622
- ${tokenData.contentMd5 ? `- Content MD5: ${tokenData.contentMd5}` : '- Content MD5: Not included (no request body)'}
663
+ ${tokenData.contentSha256 ? `- Content SHA-256: ${tokenData.contentSha256}` : '- Content SHA-256: Not included (no request body)'}
623
664
 
624
665
  **JWT Structure (Decoded):**
625
666
  **Header:**
626
667
  \`\`\`json
627
668
  {
628
669
  "alg": "RS256",
629
- "typ": "JWT"${tokenData.contentMd5 ? `,\n "contentMd5": "${tokenData.contentMd5}"` : ''}
670
+ "typ": "JWT"${tokenData.contentSha256 ? `,\n "contentSha256": "${tokenData.contentSha256}"` : ''}
630
671
  }
631
672
  \`\`\`
632
673
 
@@ -653,7 +694,7 @@ Content-Type: application/json
653
694
  - ✅ Issuer: Your Partner ID
654
695
  - ✅ Audience: https://payware.eu
655
696
  - ✅ Issued At: Unix timestamp
656
- ${tokenData.contentMd5 ? '- ✅ Content MD5: Included for request body' : '- ℹ️ Content MD5: Not needed for GET requests'}
697
+ ${tokenData.contentSha256 ? '- ✅ Content SHA-256: Included for request body' : '- ℹ️ Content SHA-256: Not needed for GET requests'}
657
698
 
658
699
  **📝 Private Key Formats Supported:**
659
700
  - ✅ Full PEM format with headers/footers
@@ -663,9 +704,9 @@ ${tokenData.contentMd5 ? '- ✅ Content MD5: Included for request body' : '- ℹ
663
704
  ## ⚠️ **CRITICAL AUTHENTICATION REQUIREMENTS**
664
705
 
665
706
  ### JSON Body Consistency Rule
666
- ${tokenData.contentMd5 ? `**Your contentMd5 (${tokenData.contentMd5}) MUST match the exact bytes sent in HTTP request.**
707
+ ${tokenData.contentSha256 ? `**Your contentSha256 (${tokenData.contentSha256}) MUST match the exact bytes sent in HTTP request.**
667
708
 
668
- ❌ **Common Error Causing ERR_INVALID_MD5:**
709
+ ❌ **Common Error Causing ERR_INVALID_CONTENT_HASH:**
669
710
  \`\`\`python
670
711
  # Wrong - different serialization formats!
671
712
  jwt_token = create_jwt(json.dumps(payload)) # One format
@@ -674,18 +715,18 @@ requests.post(url, json=payload) # Different format!
674
715
 
675
716
  ✅ **Correct Implementation:**
676
717
  \`\`\`python
677
- # Same serialization for both MD5 and HTTP body
718
+ # Same serialization for both hash and HTTP body
678
719
  json_body = json.dumps(payload, separators=(',', ':')) # Compact format
679
720
  jwt_token = create_jwt(json_body)
680
721
  requests.post(url, data=json_body, headers=headers)
681
722
  \`\`\`
682
- ` : '**No request body - No MD5 consistency concerns for GET requests.**'}
723
+ ` : '**No request body - No hash consistency concerns for GET requests.**'}
683
724
 
684
725
  ### Troubleshooting Authentication Errors
685
726
 
686
- **ERR_INVALID_MD5** - MD5 hash mismatch:
687
- - Cause: JWT contentMd5 doesn't match HTTP request body bytes
688
- - Fix: Use identical JSON serialization for both MD5 calculation and HTTP request
727
+ **ERR_INVALID_CONTENT_HASH** - Content hash mismatch:
728
+ - Cause: JWT contentSha256 doesn't match HTTP request body bytes
729
+ - Fix: Use identical JSON serialization for both hash calculation and HTTP request
689
730
  - Common issue: \`requests.post(json=data)\` vs \`requests.post(data=json_string)\`
690
731
 
691
732
  **ERR_INVALID_SIGNATURE** - JWT signature validation failed:
@@ -718,7 +759,7 @@ const response = await axios.post(url, jsonBody, {headers: {'Authorization': \`B
718
759
  - Do not share or log this token
719
760
  - Generate new token for each request with different body
720
761
  - Ensure your public key is registered with payware
721
- - **Critical:** Always use matching JSON serialization for MD5 and HTTP body`
762
+ - **Critical:** Always use matching JSON serialization for SHA-256 hash and HTTP body`
722
763
  }]
723
764
  };
724
765
  } catch (error) {
@@ -2,7 +2,7 @@
2
2
  * JSON Consistency Utilities for payware API
3
3
  *
4
4
  * CORE REQUIREMENT: The exact same compact JSON string must be used for
5
- * both JWT contentMd5 calculation and HTTP request body to prevent MD5 mismatches.
5
+ * both JWT contentSha256 calculation and HTTP request body to prevent hash mismatches.
6
6
  *
7
7
  * SOLUTION: We use deterministic serialization (sorted keys) to guarantee
8
8
  * consistent JSON output regardless of object property insertion order.
@@ -15,10 +15,10 @@
15
15
  * Create consistent compact JSON string for payware API requests
16
16
  *
17
17
  * CORE REQUIREMENT: payware API requires that the exact same compact JSON string
18
- * is used for both JWT contentMd5 calculation and the HTTP request body.
18
+ * is used for both JWT contentSha256 calculation and the HTTP request body.
19
19
  *
20
20
  * SOLUTION: This function uses deterministic serialization with sorted keys
21
- * to guarantee the same output every time, preventing MD5 mismatch errors.
21
+ * to guarantee the same output every time, preventing hash mismatch errors.
22
22
  *
23
23
  * @param {Object} obj - Object to serialize
24
24
  * @returns {string} Compact JSON string with sorted keys (no whitespace)
@@ -72,7 +72,7 @@ function sortObjectKeys(obj) {
72
72
  *
73
73
  * payware REQUIRES:
74
74
  * 1. JSON must be compact (no extra whitespace)
75
- * 2. The EXACT same string must be used for JWT contentMd5 and HTTP body
75
+ * 2. The EXACT same string must be used for JWT contentSha256 and HTTP body
76
76
  *
77
77
  * This function ensures both requirements by using deterministic serialization.
78
78
  *
@@ -86,7 +86,7 @@ export function createMinimizedJSON(requestBody) {
86
86
  /**
87
87
  * Validate that two objects produce the same JSON string
88
88
  *
89
- * This is crucial for payware API because the JWT contentMd5 and HTTP body
89
+ * This is crucial for payware API because the JWT contentSha256 and HTTP body
90
90
  * must use the exact same string. This function helps test that requirement.
91
91
  *
92
92
  * @param {Object} obj1 - First object
File without changes
package/src/index.js CHANGED
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
package/src/isv/index.js CHANGED
@@ -28,6 +28,13 @@ import { isvDeepLinksTools } from './deep-links/index.js';
28
28
  // POI tools
29
29
  import { isvPOITools } from './poi/index.js';
30
30
 
31
+ // Shop tools
32
+ import { isvShopTools } from './shops/index.js';
33
+
34
+ // Merchant onboarding: invitations, and the reference data their forms need
35
+ import { isvInvitationTools } from './invitations/index.js';
36
+ import { isvReferenceTools } from './reference/index.js';
37
+
31
38
  // Import shared tools
32
39
  import { sharedTools } from '../shared/index.js';
33
40
 
@@ -61,7 +68,16 @@ export const isvTools = [
61
68
  ...isvDeepLinksTools,
62
69
 
63
70
  // POI management
64
- ...isvPOITools
71
+ ...isvPOITools,
72
+
73
+ // Shop lookup - needed before any call that takes a shopCode
74
+ ...isvShopTools,
75
+
76
+ // Merchant onboarding. These come BEFORE a merchant exists, so they authenticate as the ISV
77
+ // itself rather than on behalf of anyone - they are how the merchantPartnerId + oauth2Token pair
78
+ // that every other tool here requires comes into existence in the first place.
79
+ ...isvInvitationTools,
80
+ ...isvReferenceTools
65
81
  ];
66
82
 
67
83
  /**
@@ -104,6 +120,18 @@ export function getISVTools() {
104
120
  else if (tool.name.startsWith('payware_poi_')) {
105
121
  newName = tool.name; // Keep poi grouping
106
122
  }
123
+ // Shop tools keep their shops grouping
124
+ else if (tool.name.startsWith('payware_shops_')) {
125
+ newName = tool.name; // Keep shops grouping
126
+ }
127
+ // Merchant onboarding (invitations) keeps its isv grouping
128
+ else if (tool.name.startsWith('payware_isv_')) {
129
+ newName = tool.name; // Keep isv grouping
130
+ }
131
+ // Reference data keeps its reference grouping
132
+ else if (tool.name.startsWith('payware_reference_')) {
133
+ newName = tool.name; // Keep reference grouping
134
+ }
107
135
 
108
136
  return {
109
137
  ...tool,