@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,349 @@
1
+ import axios from 'axios';
2
+ import { createJWTToken } from '../../core/auth/jwt-token.js';
3
+ import { createMinimizedJSON } from '../../core/utils/json-serializer.js';
4
+ import { getSandboxUrl, getProductionUrl, getPartnerIdSafe, getPrivateKeySafe } from '../../config/env.js';
5
+
6
+ /**
7
+ * Process a transaction via payware API (for merchants accepting payments)
8
+ * @param {Object} params - Transaction processing parameters
9
+ * @returns {Object} Transaction processing response
10
+ */
11
+ export async function processTransaction({
12
+ transactionId,
13
+ amount,
14
+ currency,
15
+ reasonL1,
16
+ reasonL2,
17
+ account,
18
+ friendlyName,
19
+ shop,
20
+ callbackUrl,
21
+ passbackParams,
22
+ timeToLive = 120,
23
+ partnerId,
24
+ privateKey,
25
+ useSandbox = true
26
+ }) {
27
+ if (!transactionId) {
28
+ throw new Error('Transaction ID is required');
29
+ }
30
+
31
+ if (!amount) {
32
+ throw new Error('Amount is required for processing');
33
+ }
34
+
35
+ if (typeof amount !== 'string' && typeof amount !== 'number') {
36
+ throw new Error('Amount must be a string or number representing currency value (e.g., "25.50" or 25.50)');
37
+ }
38
+
39
+ if (parseFloat(amount) <= 0) {
40
+ throw new Error('Amount must be positive for processing');
41
+ }
42
+
43
+ if (!reasonL1) {
44
+ throw new Error('reasonL1 is required (transaction grounds description)');
45
+ }
46
+
47
+ if (!partnerId || !privateKey) {
48
+ throw new Error('Partner ID and private key are required for proper JWT creation');
49
+ }
50
+
51
+ // Build request body according to documentation
52
+ const requestBody = {
53
+ ...(account && { account }),
54
+ ...(friendlyName && { friendlyName }),
55
+ ...(shop && { shop }),
56
+ ...(callbackUrl && { callbackUrl }),
57
+ ...(passbackParams && { passbackParams }),
58
+ trData: {
59
+ amount: amount.toString(),
60
+ currency,
61
+ reasonL1,
62
+ ...(reasonL2 && { reasonL2 })
63
+ },
64
+ trOptions: {
65
+ timeToLive
66
+ }
67
+ };
68
+
69
+ // Convert to deterministic minimized JSON for MD5 calculation
70
+ const minimizedBodyString = createMinimizedJSON(requestBody);
71
+
72
+ // Create JWT token with contentMd5 for the request body (POST requires contentMd5)
73
+ const tokenData = createJWTToken(partnerId, privateKey, minimizedBodyString);
74
+
75
+ // Required headers as per payware API documentation
76
+ const headers = {
77
+ 'Authorization': `Bearer ${tokenData.token}`,
78
+ 'Content-Type': 'application/json',
79
+ 'Api-Version': '1' // Required: current API version
80
+ };
81
+
82
+ try {
83
+ const baseUrl = useSandbox ? getSandboxUrl() : getProductionUrl();
84
+ // Send the exact minimized JSON string that was used for MD5 calculation
85
+ const response = await axios.post(`${baseUrl}/transactions/${transactionId}`, minimizedBodyString, {
86
+ headers,
87
+ // Tell axios to send the string as-is, don't serialize it again
88
+ transformRequest: [(data) => data]
89
+ });
90
+
91
+ return {
92
+ success: true,
93
+ transaction: response.data,
94
+ requestId: response.headers['x-request-id'],
95
+ timestamp: new Date().toISOString()
96
+ };
97
+ } catch (error) {
98
+ return {
99
+ success: false,
100
+ error: {
101
+ message: error.response?.data?.message || error.message,
102
+ status: error.response?.status,
103
+ code: error.response?.data?.errorCode,
104
+ details: error.response?.data
105
+ },
106
+ timestamp: new Date().toISOString()
107
+ };
108
+ }
109
+ }
110
+
111
+ /**
112
+ * Process transaction tool implementation
113
+ */
114
+ export const processTransactionTool = {
115
+ name: "payware_operations_process_transaction",
116
+ description: `Process a PEER transaction (for merchants accepting payments by scanning customer QR/barcode).
117
+
118
+ **Use Case:** Merchant scans customer's payware QR code or barcode to accept payment.
119
+ **Note:** Only PEER transactions (created by customers) can be processed by merchants.
120
+ **Flow:** Customer creates transaction → Merchant scans QR/barcode → Merchant processes transaction
121
+
122
+ **API Request Structure:**
123
+ \`\`\`json
124
+ {
125
+ "shop": "...",
126
+ "account": "...",
127
+ "friendlyName": "...",
128
+ "callbackUrl": "...",
129
+ "passbackParams": "...",
130
+ "trData": {
131
+ "amount": "25.50", // REQUIRED for processing
132
+ "currency": "EUR", // REQUIRED for processing
133
+ "reasonL1": "...", // REQUIRED for processing
134
+ "reasonL2": "..."
135
+ },
136
+ "trOptions": {
137
+ "timeToLive": 300 // Overwrites original transaction TTL
138
+ }
139
+ }
140
+ \`\`\``,
141
+ inputSchema: {
142
+ type: "object",
143
+ additionalProperties: false,
144
+ properties: {
145
+ transactionId: {
146
+ type: "string",
147
+ description: "Transaction ID to process. Format: 2-char prefix + 8-char ID. Supports: 'pw' (standard), 'pr' (product), 'ps' (soundbite)",
148
+ pattern: "^(pw|pr|ps)[0-9A-Za-z]{8}$"
149
+ },
150
+ amount: {
151
+ type: ["number", "string"],
152
+ description: "Amount as currency value to process (e.g., 25.50 for €25.50). Must be positive",
153
+ minimum: 0.01,
154
+ maximum: 9999.99
155
+ },
156
+ currency: {
157
+ type: "string",
158
+ pattern: "^[A-Z]{3}$",
159
+ description: "Transaction currency (ISO 3-character code)",
160
+ default: "EUR"
161
+ },
162
+ reasonL1: {
163
+ type: "string",
164
+ description: "Transaction grounds description (required)",
165
+ maxLength: 100
166
+ },
167
+ reasonL2: {
168
+ type: "string",
169
+ description: "Transaction grounds description continuation (optional)",
170
+ maxLength: 100
171
+ },
172
+ account: {
173
+ type: "string",
174
+ description: "Account identifier (optional - uses default if not provided)",
175
+ maxLength: 36
176
+ },
177
+ friendlyName: {
178
+ type: "string",
179
+ description: "Account holder recognizable name (optional)",
180
+ maxLength: 100
181
+ },
182
+ shop: {
183
+ type: "string",
184
+ description: "Shop code (optional - uses default if not provided)",
185
+ maxLength: 10
186
+ },
187
+ callbackUrl: {
188
+ type: "string",
189
+ description: "URL to receive transaction status callbacks",
190
+ format: "uri"
191
+ },
192
+ passbackParams: {
193
+ type: "string",
194
+ description: "Parameters passed back through callback",
195
+ maxLength: 200
196
+ },
197
+ timeToLive: {
198
+ type: "number",
199
+ description: "Time allowed for transaction processing in seconds",
200
+ minimum: 60,
201
+ maximum: 600,
202
+ default: 120
203
+ },
204
+ partnerId: {
205
+ type: "string",
206
+ description: "Partner ID from payware dashboard. Can use PAYWARE_PARTNER_ID env var as default.",
207
+ default: getPartnerIdSafe()
208
+ },
209
+ privateKey: {
210
+ type: "string",
211
+ description: "RSA private key for JWT token creation. Can use PAYWARE_PRIVATE_KEY_PATH env var as default.",
212
+ default: getPrivateKeySafe()
213
+ },
214
+ useSandbox: {
215
+ type: "boolean",
216
+ description: "Use sandbox environment for testing",
217
+ default: true
218
+ }
219
+ },
220
+ required: ["transactionId", "amount", "currency", "reasonL1"]
221
+ },
222
+
223
+ async handler(args) {
224
+ const {
225
+ transactionId,
226
+ amount,
227
+ currency = 'EUR',
228
+ reasonL1,
229
+ reasonL2,
230
+ account,
231
+ friendlyName,
232
+ shop,
233
+ callbackUrl,
234
+ passbackParams,
235
+ timeToLive = 120,
236
+ partnerId = getPartnerIdSafe(),
237
+ privateKey = getPrivateKeySafe(args.useSandbox ?? true),
238
+ useSandbox = true
239
+ } = args;
240
+
241
+ if (!transactionId) {
242
+ throw new Error("Transaction ID is required");
243
+ }
244
+
245
+ if (!amount || amount <= 0) {
246
+ throw new Error("Amount is required and must be positive");
247
+ }
248
+
249
+ if (!reasonL1) {
250
+ throw new Error("reasonL1 is required (transaction grounds description)");
251
+ }
252
+
253
+ if (!partnerId) {
254
+ throw new Error("Partner ID is required. Provide via 'partnerId' parameter or set PAYWARE_PARTNER_ID environment variable.");
255
+ }
256
+
257
+ if (!privateKey) {
258
+ throw new Error("Private key is required. Provide via 'privateKey' parameter or set PAYWARE_PRIVATE_KEY environment variable.");
259
+ }
260
+
261
+ const result = await processTransaction({
262
+ transactionId,
263
+ amount,
264
+ currency,
265
+ reasonL1,
266
+ reasonL2,
267
+ account,
268
+ friendlyName,
269
+ shop,
270
+ callbackUrl,
271
+ passbackParams,
272
+ timeToLive,
273
+ partnerId,
274
+ privateKey,
275
+ useSandbox
276
+ });
277
+
278
+ if (result.success) {
279
+ return {
280
+ content: [{
281
+ type: "text",
282
+ text: `🔄 **Transaction Processing Initiated**
283
+
284
+ **Transaction ID:** ${transactionId}
285
+ **Processing Status:** ✅ Successful
286
+
287
+ **Transaction Details:**
288
+ - Amount: ${amount} ${currency}
289
+ - Reason: ${reasonL1}${reasonL2 ? ` (${reasonL2})` : ''}
290
+ - Time to Live: ${timeToLive} seconds
291
+ ${callbackUrl ? `- Callback URL: ${callbackUrl}` : ''}
292
+ ${passbackParams ? `- Passback Params: ${passbackParams}` : ''}
293
+
294
+ **API Response:**
295
+ \`\`\`json
296
+ ${JSON.stringify(result.transaction, null, 2)}
297
+ \`\`\`
298
+
299
+ **Request ID:** ${result.requestId || 'N/A'}
300
+ **Timestamp:** ${result.timestamp}
301
+
302
+ **Next Steps:**
303
+ 1. Transaction control transferred to payer
304
+ 2. Wait for callback notification (if URL provided)
305
+ 3. Monitor transaction status for completion
306
+ 4. Handle CONFIRMED/DECLINED/FAILED states
307
+
308
+ **⚠️ Important:**
309
+ - Only merchants (payees) can process transactions
310
+ - Processing transfers control to the paying party
311
+ - Transaction will expire if not completed within TTL`
312
+ }]
313
+ };
314
+ } else {
315
+ return {
316
+ content: [{
317
+ type: "text",
318
+ text: `❌ **Transaction Processing Failed**
319
+
320
+ **Transaction ID:** ${transactionId}
321
+
322
+ **Error Details:**
323
+ - Message: ${result.error.message}
324
+ - Status: ${result.error.status || 'N/A'}
325
+ - Code: ${result.error.code || 'N/A'}
326
+
327
+ **Full Error Response:**
328
+ \`\`\`json
329
+ ${JSON.stringify(result.error.details || result.error, null, 2)}
330
+ \`\`\`
331
+
332
+ **Timestamp:** ${result.timestamp}
333
+
334
+ **Common Issues:**
335
+ 1. **ERR_MISSING_TRANSACTION**: Transaction ID not found or expired
336
+ 2. **ERR_UNKNOWN_ROLE**: Transaction generated by payee (can't process own transaction)
337
+ 3. **ERR_ALREADY_PROCESSED**: Transaction already processed
338
+ 4. **ERR_SPECIFYING_AMOUNT_NOT_ALLOWED**: Amount exceeds transaction limit
339
+
340
+ **Troubleshooting:**
341
+ 1. Verify transaction ID is correct: 10 characters total, starts with 'pw' (standard), 'pr' (product), or 'ps' (soundbite)
342
+ 2. Ensure you're not the transaction creator (merchants process peer transactions)
343
+ 3. Check transaction hasn't already been processed
344
+ 4. Verify amount doesn't exceed original transaction amount`
345
+ }]
346
+ };
347
+ }
348
+ }
349
+ };
@@ -0,0 +1,246 @@
1
+ /**
2
+ * Simulate callback scenarios for testing
3
+ */
4
+
5
+ /**
6
+ * Generate mock callback payload
7
+ * @param {string} transactionId - Transaction ID
8
+ * @param {string} status - Transaction status (CONFIRMED, DECLINED, FAILED, EXPIRED, CANCELLED)
9
+ * @param {Object} options - Additional options
10
+ * @returns {Object} Mock callback payload
11
+ */
12
+ export function generateMockCallback(transactionId, status = 'CONFIRMED', options = {}) {
13
+ const {
14
+ amount = '57.60',
15
+ currency = 'EUR',
16
+ payerAmount = null,
17
+ payeeAmount = null,
18
+ statusMessage = null,
19
+ passbackParams = null
20
+ } = options;
21
+
22
+ // Calculate fee like Java implementation: |payerAmount - amount| or |payeeAmount - amount|
23
+ const transactionAmount = parseFloat(amount);
24
+ let fee = '0.00';
25
+ if (payerAmount !== null) {
26
+ fee = Math.abs(parseFloat(payerAmount) - transactionAmount).toFixed(2);
27
+ } else if (payeeAmount !== null) {
28
+ fee = Math.abs(parseFloat(payeeAmount) - transactionAmount).toFixed(2);
29
+ }
30
+
31
+ const currentTime = Date.now();
32
+ const basePayload = {
33
+ callbackType: 'TRANSACTION_FINALIZED',
34
+ transactionId,
35
+ passbackParams,
36
+ amount,
37
+ fee,
38
+ currency,
39
+ status,
40
+ statusMessage,
41
+ created: currentTime - 300000, // Created 5 minutes ago (milliseconds)
42
+ ...(status !== 'EXPIRED' && { finalized: currentTime })
43
+ };
44
+
45
+ // Add status-specific statusMessage
46
+ switch (status) {
47
+ case 'CONFIRMED':
48
+ return {
49
+ ...basePayload,
50
+ statusMessage: statusMessage || 'Transaction confirmed'
51
+ };
52
+
53
+ case 'DECLINED':
54
+ return {
55
+ ...basePayload,
56
+ statusMessage: statusMessage || 'Insufficient funds'
57
+ };
58
+
59
+ case 'FAILED':
60
+ return {
61
+ ...basePayload,
62
+ statusMessage: statusMessage || 'Payment processing failed'
63
+ };
64
+
65
+ case 'EXPIRED':
66
+ return {
67
+ ...basePayload,
68
+ statusMessage: statusMessage || 'Transaction expired',
69
+ finalized: undefined // Remove finalized for expired transactions
70
+ };
71
+
72
+ case 'CANCELLED':
73
+ return {
74
+ ...basePayload,
75
+ statusMessage: statusMessage || 'Transaction cancelled'
76
+ };
77
+
78
+ default:
79
+ return basePayload;
80
+ }
81
+ }
82
+
83
+ /**
84
+ * Simulate callback delivery
85
+ * @param {string} callbackUrl - URL to send callback to
86
+ * @param {Object} payload - Callback payload
87
+ * @returns {Object} Simulation result
88
+ */
89
+ export async function simulateCallbackDelivery(callbackUrl, payload) {
90
+ if (!callbackUrl || !callbackUrl.startsWith('http')) {
91
+ return {
92
+ success: false,
93
+ error: 'Invalid callback URL provided'
94
+ };
95
+ }
96
+
97
+ // In a real implementation, this would actually send HTTP POST
98
+ // For simulation, we just return what would be sent
99
+ return {
100
+ success: true,
101
+ url: callbackUrl,
102
+ payload,
103
+ headers: {
104
+ 'Content-Type': 'application/json',
105
+ 'Authorization': 'Bearer mock-jwt-token-' + Date.now()
106
+ },
107
+ simulatedAt: new Date().toISOString(),
108
+ note: 'This is a simulation - no actual HTTP request was made. Real payware callbacks use JWT tokens in Authorization header.'
109
+ };
110
+ }
111
+
112
+ /**
113
+ * Simulate callback tool implementation
114
+ */
115
+ export const simulateCallbackTool = {
116
+ name: "payware_operations_simulate_callback",
117
+ description: "Simulate callback scenarios for testing webhook handling",
118
+ inputSchema: {
119
+ type: "object",
120
+ properties: {
121
+ transactionId: {
122
+ type: "string",
123
+ description: "Transaction ID to simulate callback for"
124
+ },
125
+ status: {
126
+ type: "string",
127
+ enum: ["CONFIRMED", "DECLINED", "FAILED", "EXPIRED", "CANCELLED"],
128
+ description: "Transaction status to simulate",
129
+ default: "CONFIRMED"
130
+ },
131
+ callbackUrl: {
132
+ type: "string",
133
+ description: "URL where callback would be sent (for simulation only)",
134
+ format: "uri"
135
+ },
136
+ amount: {
137
+ type: "string",
138
+ description: "Transaction amount (e.g., '57.60')",
139
+ default: "57.60"
140
+ },
141
+ currency: {
142
+ type: "string",
143
+ enum: ["EUR", "USD", "GBP"],
144
+ description: "Transaction currency",
145
+ default: "EUR"
146
+ },
147
+ fee: {
148
+ type: "string",
149
+ description: "Fee amount charged by payment institution (e.g., '0.00' for no fee)",
150
+ default: "0.00"
151
+ },
152
+ statusMessage: {
153
+ type: "string",
154
+ description: "Custom status message for the transaction callback"
155
+ }
156
+ },
157
+ required: ["transactionId"],
158
+ additionalProperties: false
159
+ },
160
+
161
+ async handler(args) {
162
+ const {
163
+ transactionId,
164
+ status = 'CONFIRMED',
165
+ callbackUrl,
166
+ amount = '57.60',
167
+ currency = 'EUR',
168
+ fee = '0.00',
169
+ statusMessage
170
+ } = args;
171
+
172
+ if (!transactionId) {
173
+ throw new Error("Transaction ID is required");
174
+ }
175
+
176
+ // Generate mock callback payload
177
+ const payload = generateMockCallback(transactionId, status, {
178
+ amount,
179
+ currency,
180
+ fee,
181
+ statusMessage
182
+ });
183
+
184
+ // Simulate callback delivery if URL provided
185
+ let deliveryResult = null;
186
+ if (callbackUrl) {
187
+ deliveryResult = await simulateCallbackDelivery(callbackUrl, payload);
188
+ }
189
+
190
+ const statusEmojis = {
191
+ 'CONFIRMED': '✅',
192
+ 'DECLINED': '❌',
193
+ 'FAILED': '💥',
194
+ 'EXPIRED': '⏰',
195
+ 'CANCELLED': '🚫'
196
+ };
197
+
198
+ const statusEmoji = statusEmojis[status] || '❓';
199
+
200
+ return {
201
+ content: [{
202
+ type: "text",
203
+ text: `🔄 **Callback Simulation Complete**
204
+
205
+ **Transaction:** ${transactionId}
206
+ **Simulated Status:** ${statusEmoji} ${status}
207
+
208
+ **Mock Callback Payload:**
209
+ \`\`\`json
210
+ ${JSON.stringify(payload, null, 2)}
211
+ \`\`\`
212
+
213
+ ${deliveryResult ? `
214
+ **Callback Delivery Simulation:**
215
+ ${deliveryResult.success ? '✅ Callback would be delivered successfully' : '❌ Callback delivery would fail'}
216
+
217
+ **Delivery Details:**
218
+ - URL: ${deliveryResult.url || 'N/A'}
219
+ - Headers: ${JSON.stringify(deliveryResult.headers || {}, null, 2)}
220
+ - Simulated At: ${deliveryResult.simulatedAt || 'N/A'}
221
+
222
+ **Note:** ${deliveryResult.note || 'No actual HTTP request was made'}
223
+ ` : '**No callback URL provided** - only payload generated'}
224
+
225
+ **Status Explanations:**
226
+ - ✅ **CONFIRMED**: Successfully finalized
227
+ - ❌ **DECLINED**: Declined by the user, processing or finalizing payment institutions
228
+ - 💥 **FAILED**: Failed due to technical reasons or other
229
+ - ⏰ **EXPIRED**: Time to live of the transaction has passed
230
+ - 🚫 **CANCELLED**: Transaction canceled by the originator
231
+
232
+ **Integration Testing:**
233
+ 1. Use this payload to test your callback handler
234
+ 2. Verify your system processes each status correctly
235
+ 3. Check error handling for DECLINED/FAILED statuses
236
+ 4. Ensure proper logging and monitoring
237
+
238
+ **Next Steps:**
239
+ 1. Implement callback endpoint to receive this payload
240
+ 2. Test with different status scenarios
241
+ 3. Add signature verification in production
242
+ 4. Set up proper error handling and retry logic`
243
+ }]
244
+ };
245
+ }
246
+ };