@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.
- package/.env.example +92 -0
- package/MCP-SETUP.md +190 -0
- package/README.md +483 -0
- package/check-keys.js +52 -0
- package/keys/README.md +112 -0
- package/mcp-config.json +9 -0
- package/package.json +66 -0
- package/payware-mcp-config.json +12 -0
- package/src/auth/rsa-keys.js +82 -0
- package/src/auth/sandbox-setup.js +187 -0
- package/src/config/env.js +229 -0
- package/src/config/partner-types.js +143 -0
- package/src/core/auth/jwt-factory.js +182 -0
- package/src/core/auth/jwt-token.js +735 -0
- package/src/core/utils/json-serializer.js +100 -0
- package/src/core/utils/key-utils.js +133 -0
- package/src/index.js +237 -0
- package/src/isv/data/index.js +291 -0
- package/src/isv/deep-links/delete-product-link.js +154 -0
- package/src/isv/deep-links/delete-transaction-link.js +154 -0
- package/src/isv/deep-links/get-product-link.js +167 -0
- package/src/isv/deep-links/get-transaction-link.js +167 -0
- package/src/isv/deep-links/index.js +23 -0
- package/src/isv/index.js +103 -0
- package/src/isv/oauth2/create-token-simple.js +46 -0
- package/src/isv/oauth2/get-token-info.js +157 -0
- package/src/isv/oauth2/obtain-token.js +189 -0
- package/src/isv/oauth2/token-helper.js +151 -0
- package/src/isv/products/audios.js +470 -0
- package/src/isv/products/create-product.js +314 -0
- package/src/isv/products/delete-product.js +119 -0
- package/src/isv/products/get-product-image.js +247 -0
- package/src/isv/products/get-product.js +168 -0
- package/src/isv/products/index.js +65 -0
- package/src/isv/products/list-products.js +136 -0
- package/src/isv/products/schedules.js +522 -0
- package/src/isv/products/update-product.js +265 -0
- package/src/isv/transactions/cancel-transaction.js +129 -0
- package/src/isv/transactions/create-transaction.js +361 -0
- package/src/isv/transactions/get-status.js +217 -0
- package/src/isv/transactions/process-transaction.js +145 -0
- package/src/isv/transactions/simulate-callback.js +146 -0
- package/src/isv/transactions/transaction-history.js +186 -0
- package/src/isv/utils/auth.js +159 -0
- package/src/mcp-proxy.js +150 -0
- package/src/merchant/data/export-report.js +108 -0
- package/src/merchant/data/generate-report.js +151 -0
- package/src/merchant/data/get-report-status.js +120 -0
- package/src/merchant/data/index.js +351 -0
- package/src/merchant/deep-links/delete-product-link.js +121 -0
- package/src/merchant/deep-links/delete-transaction-link.js +121 -0
- package/src/merchant/deep-links/get-product-link.js +134 -0
- package/src/merchant/deep-links/get-transaction-link.js +134 -0
- package/src/merchant/deep-links/index.js +23 -0
- package/src/merchant/index.js +92 -0
- package/src/merchant/products/audios.js +606 -0
- package/src/merchant/products/create-product.js +288 -0
- package/src/merchant/products/delete-product.js +111 -0
- package/src/merchant/products/get-product-image.js +252 -0
- package/src/merchant/products/get-product.js +144 -0
- package/src/merchant/products/index.js +65 -0
- package/src/merchant/products/list-products.js +112 -0
- package/src/merchant/products/schedules.js +646 -0
- package/src/merchant/products/update-product.js +282 -0
- package/src/merchant/transactions/cancel-transaction.js +241 -0
- package/src/merchant/transactions/create-transaction.js +482 -0
- package/src/merchant/transactions/get-status.js +196 -0
- package/src/merchant/transactions/process-transaction.js +349 -0
- package/src/merchant/transactions/simulate-callback.js +246 -0
- package/src/merchant/transactions/transaction-history.js +258 -0
- package/src/payment-institution/data/index.js +280 -0
- package/src/payment-institution/deep-links/delete-transaction-link.js +128 -0
- package/src/payment-institution/deep-links/get-transaction-link.js +141 -0
- package/src/payment-institution/deep-links/index.js +17 -0
- package/src/payment-institution/index.js +92 -0
- package/src/payment-institution/transactions/create-pi-transaction.js +484 -0
- package/src/payment-institution/transactions/finalize-transaction.js +340 -0
- package/src/payment-institution/transactions/get-transaction-history.js +269 -0
- package/src/payment-institution/transactions/get-transaction-status.js +234 -0
- package/src/payment-institution/transactions/process-pi-transaction.js +421 -0
- package/src/payment-institution/transactions/simulate-pi-callback.js +326 -0
- package/src/payment-institution/transactions/soundbite-transaction.js +269 -0
- package/src/proxy-server.js +205 -0
- package/src/shared/data/data-api.js +372 -0
- package/src/shared/deep-links/api.js +214 -0
- package/src/shared/index.js +44 -0
- package/src/shared/products/products-api.js +872 -0
- package/src/tools/advanced-code-generator.js +2247 -0
- package/src/utils/code-examples/common/auth-examples.js +1482 -0
- package/src/utils/code-examples/common/helpers.js +416 -0
- package/src/utils/code-examples/common/utils-examples.js +591 -0
- package/src/utils/code-examples/frameworks/examples.js +859 -0
- package/src/utils/code-examples/index.js +441 -0
- package/src/utils/code-examples/isv/oauth2.js +600 -0
- package/src/utils/code-examples/isv/products.js +666 -0
- package/src/utils/code-examples/isv/transactions.js +813 -0
- package/src/utils/code-examples/merchant/products.js +497 -0
- package/src/utils/code-examples/merchant/transactions.js +623 -0
- package/src/utils/code-examples/payment-institution/transactions.js +388 -0
- package/src/utils/code-examples/shared/data.js +607 -0
- package/src/utils/code-examples/shared/deep-links.js +662 -0
- package/src/utils/code-examples/shared/soundbites.js +808 -0
- package/src/utils/code-examples.js +646 -0
- package/src/utils/format-request.js +363 -0
- package/src/utils/json-formatter.js +249 -0
- package/src/utils/server-info.js +132 -0
|
@@ -0,0 +1,482 @@
|
|
|
1
|
+
import axios from 'axios';
|
|
2
|
+
import { createJWTToken, generateContentMd5 } 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
|
+
* Create a transaction via payware API
|
|
8
|
+
* @param {Object} params - Transaction parameters
|
|
9
|
+
* @returns {Object} Transaction response
|
|
10
|
+
*/
|
|
11
|
+
export async function createTransaction({
|
|
12
|
+
type = 'PLAIN',
|
|
13
|
+
amount,
|
|
14
|
+
currency = 'EUR',
|
|
15
|
+
reasonL1,
|
|
16
|
+
reasonL2,
|
|
17
|
+
callbackUrl,
|
|
18
|
+
account,
|
|
19
|
+
friendlyName,
|
|
20
|
+
shop,
|
|
21
|
+
timeToLive = 120,
|
|
22
|
+
partnerId,
|
|
23
|
+
privateKey,
|
|
24
|
+
passbackParams,
|
|
25
|
+
// QR Options
|
|
26
|
+
qrFormat,
|
|
27
|
+
qrBorder,
|
|
28
|
+
qrErrorCorrection,
|
|
29
|
+
qrScale,
|
|
30
|
+
qrVersion,
|
|
31
|
+
// Barcode Options
|
|
32
|
+
barFormat,
|
|
33
|
+
barModuleWidth,
|
|
34
|
+
barBarHeight,
|
|
35
|
+
barFontSize,
|
|
36
|
+
barHumanReadableLocation,
|
|
37
|
+
useSandbox = true
|
|
38
|
+
}) {
|
|
39
|
+
if (!reasonL1) {
|
|
40
|
+
throw new Error('reasonL1 is required (transaction grounds description)');
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (amount !== undefined && (typeof amount !== 'string' && typeof amount !== 'number')) {
|
|
44
|
+
throw new Error('Amount must be a string or number representing currency value (e.g., "25.50" or 25.50)');
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (amount !== undefined && parseFloat(amount) < 0) {
|
|
48
|
+
throw new Error('Amount must be non-negative');
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (!['PLAIN', 'QR', 'BARCODE'].includes(type)) {
|
|
52
|
+
throw new Error('Transaction type must be PLAIN, QR, or BARCODE');
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (!partnerId || !privateKey) {
|
|
56
|
+
throw new Error('Partner ID and private key are required for proper JWT creation');
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Build QR options if applicable
|
|
60
|
+
const qrOptions = {};
|
|
61
|
+
if (type === 'QR') {
|
|
62
|
+
if (qrFormat) qrOptions.qrFormat = qrFormat;
|
|
63
|
+
if (qrBorder !== undefined) qrOptions.qrBorder = qrBorder;
|
|
64
|
+
if (qrErrorCorrection) qrOptions.qrErrorCorrection = qrErrorCorrection;
|
|
65
|
+
if (qrScale) qrOptions.qrScale = qrScale;
|
|
66
|
+
if (qrVersion) qrOptions.qrVersion = qrVersion;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Build barcode options if applicable
|
|
70
|
+
const barOptions = {};
|
|
71
|
+
if (type === 'BARCODE') {
|
|
72
|
+
if (barFormat) barOptions.barFormat = barFormat;
|
|
73
|
+
if (barModuleWidth) barOptions.barModuleWidth = barModuleWidth;
|
|
74
|
+
if (barBarHeight) barOptions.barBarHeight = barBarHeight;
|
|
75
|
+
if (barFontSize) barOptions.barFontSize = barFontSize;
|
|
76
|
+
if (barHumanReadableLocation) barOptions.barHumanReadableLocation = barHumanReadableLocation;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Build request body according to documentation structure
|
|
80
|
+
const requestBody = {
|
|
81
|
+
...(account && { account }),
|
|
82
|
+
...(friendlyName && { friendlyName }),
|
|
83
|
+
...(shop && { shop }),
|
|
84
|
+
...(callbackUrl && { callbackUrl }),
|
|
85
|
+
...(passbackParams && {
|
|
86
|
+
passbackParams: typeof passbackParams === 'string' ? passbackParams : JSON.stringify(passbackParams)
|
|
87
|
+
}),
|
|
88
|
+
trData: {
|
|
89
|
+
amount: amount !== undefined ? amount.toString() : '0.00',
|
|
90
|
+
currency,
|
|
91
|
+
reasonL1,
|
|
92
|
+
...(reasonL2 && { reasonL2 })
|
|
93
|
+
},
|
|
94
|
+
trOptions: {
|
|
95
|
+
type,
|
|
96
|
+
timeToLive
|
|
97
|
+
},
|
|
98
|
+
...(type === 'QR' && Object.keys(qrOptions).length > 0 && { qrOptions }),
|
|
99
|
+
...(type === 'BARCODE' && Object.keys(barOptions).length > 0 && { barOptions })
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
// Convert to deterministic minimized JSON as required by payware API for MD5 calculation
|
|
103
|
+
const minimizedBodyString = createMinimizedJSON(requestBody);
|
|
104
|
+
|
|
105
|
+
// Create JWT token with contentMd5 for the request body (POST requires contentMd5)
|
|
106
|
+
const tokenData = createJWTToken(partnerId, privateKey, minimizedBodyString);
|
|
107
|
+
|
|
108
|
+
// Required headers as per payware API documentation
|
|
109
|
+
const headers = {
|
|
110
|
+
'Authorization': `Bearer ${tokenData.token}`,
|
|
111
|
+
'Content-Type': 'application/json',
|
|
112
|
+
'Api-Version': '1' // Required: current API version
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
try {
|
|
116
|
+
|
|
117
|
+
// Send the exact minimized JSON string that was used for MD5 calculation
|
|
118
|
+
// This ensures the server calculates the same MD5 hash
|
|
119
|
+
const baseUrl = useSandbox ? getSandboxUrl() : getProductionUrl();
|
|
120
|
+
const response = await axios.post(`${baseUrl}/transactions`, minimizedBodyString, {
|
|
121
|
+
headers,
|
|
122
|
+
// Tell axios to send the string as-is, don't serialize it again
|
|
123
|
+
transformRequest: [(data) => data]
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
return {
|
|
127
|
+
success: true,
|
|
128
|
+
transaction: response.data,
|
|
129
|
+
requestId: response.headers['x-request-id'],
|
|
130
|
+
timestamp: new Date().toISOString()
|
|
131
|
+
};
|
|
132
|
+
} catch (error) {
|
|
133
|
+
// Provide helpful error message for MD5 mismatches
|
|
134
|
+
let enhancedMessage = error.response?.data?.message || error.message;
|
|
135
|
+
|
|
136
|
+
if (error.response?.data?.code === 'ERR_INVALID_MD5' ||
|
|
137
|
+
enhancedMessage?.includes('MD5') ||
|
|
138
|
+
enhancedMessage?.includes('contentMd5')) {
|
|
139
|
+
enhancedMessage = `MD5 Mismatch Error: The contentMd5 in JWT header doesn't match the request body.
|
|
140
|
+
|
|
141
|
+
Cause: Different JSON strings were used for JWT contentMd5 calculation and HTTP body.
|
|
142
|
+
Solution: Ensure the EXACT same compact JSON string is used for both purposes.
|
|
143
|
+
|
|
144
|
+
Original error: ${enhancedMessage}`;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return {
|
|
148
|
+
success: false,
|
|
149
|
+
error: {
|
|
150
|
+
message: enhancedMessage,
|
|
151
|
+
status: error.response?.status,
|
|
152
|
+
code: error.response?.data?.code,
|
|
153
|
+
details: error.response?.data,
|
|
154
|
+
helpUrl: error.response?.data?.code === 'ERR_INVALID_MD5' ?
|
|
155
|
+
'https://github.com/payware/mcp-server#md5-consistency' : undefined
|
|
156
|
+
},
|
|
157
|
+
timestamp: new Date().toISOString()
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Create transaction tool implementation
|
|
164
|
+
*/
|
|
165
|
+
export const createTransactionTool = {
|
|
166
|
+
name: "payware_operations_create_transaction",
|
|
167
|
+
description: `Create a new payware transaction. Parameters map to the following API structure:
|
|
168
|
+
|
|
169
|
+
📋 **ROOT LEVEL** (Transaction metadata):
|
|
170
|
+
- shop, account, friendlyName, callbackUrl, passbackParams
|
|
171
|
+
|
|
172
|
+
💰 **TRANSACTION DATA** (trData object):
|
|
173
|
+
- amount, currency, reasonL1, reasonL2
|
|
174
|
+
|
|
175
|
+
⚙️ **TRANSACTION OPTIONS** (trOptions object):
|
|
176
|
+
- type, timeToLive
|
|
177
|
+
|
|
178
|
+
📱 **QR OPTIONS** (qrOptions object - only when type=QR):
|
|
179
|
+
- qrFormat, qrBorder, qrErrorCorrection, qrScale, qrVersion
|
|
180
|
+
|
|
181
|
+
🔗 **BARCODE OPTIONS** (barOptions object - only when type=BARCODE):
|
|
182
|
+
- barFormat, barModuleWidth, barBarHeight, barFontSize, barHumanReadableLocation
|
|
183
|
+
|
|
184
|
+
🔐 **AUTHENTICATION** (not sent to API, used for JWT signing):
|
|
185
|
+
- partnerId, privateKey
|
|
186
|
+
|
|
187
|
+
**Actual API Request Structure:**
|
|
188
|
+
\`\`\`json
|
|
189
|
+
{
|
|
190
|
+
"shop": "...",
|
|
191
|
+
"account": "...",
|
|
192
|
+
"friendlyName": "...",
|
|
193
|
+
"callbackUrl": "...",
|
|
194
|
+
"passbackParams": "...",
|
|
195
|
+
"trData": {
|
|
196
|
+
"amount": "25.50",
|
|
197
|
+
"currency": "EUR",
|
|
198
|
+
"reasonL1": "...",
|
|
199
|
+
"reasonL2": "..."
|
|
200
|
+
},
|
|
201
|
+
"trOptions": {
|
|
202
|
+
"type": "QR",
|
|
203
|
+
"timeToLive": 300
|
|
204
|
+
},
|
|
205
|
+
"qrOptions": { ... }
|
|
206
|
+
}
|
|
207
|
+
\`\`\``,
|
|
208
|
+
inputSchema: {
|
|
209
|
+
type: "object",
|
|
210
|
+
properties: {
|
|
211
|
+
type: {
|
|
212
|
+
type: "string",
|
|
213
|
+
enum: ["PLAIN", "QR", "BARCODE"],
|
|
214
|
+
description: "Transaction type"
|
|
215
|
+
},
|
|
216
|
+
amount: {
|
|
217
|
+
type: "number",
|
|
218
|
+
description: "Amount as currency value (e.g., 25.50 for €25.50 or '15.75' for $15.75). Can be 0 for amount-flexible transactions. Max 36 chars including decimals",
|
|
219
|
+
minimum: 0,
|
|
220
|
+
maximum: 9999999999999.99
|
|
221
|
+
},
|
|
222
|
+
currency: {
|
|
223
|
+
type: "string",
|
|
224
|
+
pattern: "^[A-Z]{3}$",
|
|
225
|
+
description: "Transaction currency (ISO 3-character code)"
|
|
226
|
+
},
|
|
227
|
+
reasonL1: {
|
|
228
|
+
type: "string",
|
|
229
|
+
description: "Transaction grounds description (required)",
|
|
230
|
+
maxLength: 100
|
|
231
|
+
},
|
|
232
|
+
reasonL2: {
|
|
233
|
+
type: "string",
|
|
234
|
+
description: "Transaction grounds description continuation (optional)",
|
|
235
|
+
maxLength: 100
|
|
236
|
+
},
|
|
237
|
+
account: {
|
|
238
|
+
type: "string",
|
|
239
|
+
description: "📋 ROOT LEVEL: Account identifier (if not supplied, determined from payware portal)",
|
|
240
|
+
maxLength: 36
|
|
241
|
+
},
|
|
242
|
+
friendlyName: {
|
|
243
|
+
type: "string",
|
|
244
|
+
description: "📋 ROOT LEVEL: Account holder recognizable name (if not supplied, merchant alias is used)",
|
|
245
|
+
maxLength: 100
|
|
246
|
+
},
|
|
247
|
+
shop: {
|
|
248
|
+
type: "string",
|
|
249
|
+
description: "📋 ROOT LEVEL: Shop code (if not supplied, default shop is used)",
|
|
250
|
+
maxLength: 10
|
|
251
|
+
},
|
|
252
|
+
timeToLive: {
|
|
253
|
+
type: "number",
|
|
254
|
+
description: "⚙️ TRANSACTION OPTIONS: Time allowed for payment initiation in seconds",
|
|
255
|
+
minimum: 60,
|
|
256
|
+
maximum: 600
|
|
257
|
+
},
|
|
258
|
+
callbackUrl: {
|
|
259
|
+
type: "string",
|
|
260
|
+
description: "📋 ROOT LEVEL: HTTPS URL to receive transaction status callbacks (must be https://)",
|
|
261
|
+
format: "uri"
|
|
262
|
+
},
|
|
263
|
+
partnerId: {
|
|
264
|
+
type: "string",
|
|
265
|
+
description: "🔐 AUTHENTICATION: Partner ID from payware dashboard (not sent in API request). Uses PAYWARE_PARTNER_ID env var as default."
|
|
266
|
+
},
|
|
267
|
+
privateKey: {
|
|
268
|
+
type: "string",
|
|
269
|
+
description: "🔐 AUTHENTICATION: RSA private key for JWT token creation (not sent in API request). Accepts PEM format with/without headers or raw base64 content. Uses environment-specific private key as default."
|
|
270
|
+
},
|
|
271
|
+
passbackParams: {
|
|
272
|
+
type: "string",
|
|
273
|
+
description: "📋 ROOT LEVEL: Additional parameters passed back in callbacks (max 200 chars)",
|
|
274
|
+
maxLength: 200
|
|
275
|
+
},
|
|
276
|
+
// QR Code Options
|
|
277
|
+
qrFormat: {
|
|
278
|
+
type: "string",
|
|
279
|
+
enum: ["PNG", "JPEG", "GIF", "BMP", "SVG"],
|
|
280
|
+
description: "📱 QR OPTIONS: Image format for QR code (only when type=QR, default: SVG)"
|
|
281
|
+
},
|
|
282
|
+
qrBorder: {
|
|
283
|
+
type: "number",
|
|
284
|
+
minimum: 1,
|
|
285
|
+
maximum: 10,
|
|
286
|
+
description: "📱 QR OPTIONS: Border size in modules around QR code (only when type=QR, default: 4)"
|
|
287
|
+
},
|
|
288
|
+
qrErrorCorrection: {
|
|
289
|
+
type: "string",
|
|
290
|
+
enum: ["LOW", "MEDIUM", "QUARTILE", "HIGH"],
|
|
291
|
+
description: "📱 QR OPTIONS: Error correction level - LOW (7%), MEDIUM (15%), QUARTILE (25%), HIGH (30%) (only when type=QR, default: QUARTILE)"
|
|
292
|
+
},
|
|
293
|
+
qrScale: {
|
|
294
|
+
type: "number",
|
|
295
|
+
minimum: 1,
|
|
296
|
+
maximum: 100,
|
|
297
|
+
description: "📱 QR OPTIONS: Size of each module in pixels, irrelevant for SVG (only when type=QR, default: 16)"
|
|
298
|
+
},
|
|
299
|
+
qrVersion: {
|
|
300
|
+
type: "number",
|
|
301
|
+
minimum: 1,
|
|
302
|
+
maximum: 40,
|
|
303
|
+
description: "📱 QR OPTIONS: QR code version, higher versions can store more data (only when type=QR, default: 10)"
|
|
304
|
+
},
|
|
305
|
+
// Barcode Options
|
|
306
|
+
barFormat: {
|
|
307
|
+
type: "string",
|
|
308
|
+
enum: ["PNG", "SVG", "JPG"],
|
|
309
|
+
description: "🔗 BARCODE OPTIONS: Image format for barcode (only when type=BARCODE, default: SVG)"
|
|
310
|
+
},
|
|
311
|
+
barModuleWidth: {
|
|
312
|
+
type: "number",
|
|
313
|
+
minimum: 1,
|
|
314
|
+
maximum: 5,
|
|
315
|
+
description: "Barcode module width in pixels (applies when type=BARCODE)"
|
|
316
|
+
},
|
|
317
|
+
barBarHeight: {
|
|
318
|
+
type: "number",
|
|
319
|
+
minimum: 10,
|
|
320
|
+
maximum: 200,
|
|
321
|
+
description: "Barcode height in pixels (applies when type=BARCODE)"
|
|
322
|
+
},
|
|
323
|
+
barFontSize: {
|
|
324
|
+
type: "number",
|
|
325
|
+
minimum: 8,
|
|
326
|
+
maximum: 24,
|
|
327
|
+
description: "Barcode text font size (applies when type=BARCODE)"
|
|
328
|
+
},
|
|
329
|
+
barHumanReadableLocation: {
|
|
330
|
+
type: "string",
|
|
331
|
+
enum: ["TOP", "BOTTOM", "NONE"],
|
|
332
|
+
description: "Location of human readable text (applies when type=BARCODE)"
|
|
333
|
+
},
|
|
334
|
+
useSandbox: {
|
|
335
|
+
type: "boolean",
|
|
336
|
+
description: "Use sandbox environment for testing"
|
|
337
|
+
}
|
|
338
|
+
},
|
|
339
|
+
required: ["currency", "reasonL1"],
|
|
340
|
+
additionalProperties: false
|
|
341
|
+
},
|
|
342
|
+
|
|
343
|
+
async handler(args) {
|
|
344
|
+
const {
|
|
345
|
+
type = 'PLAIN',
|
|
346
|
+
amount,
|
|
347
|
+
currency = 'EUR',
|
|
348
|
+
reasonL1,
|
|
349
|
+
reasonL2,
|
|
350
|
+
callbackUrl,
|
|
351
|
+
account,
|
|
352
|
+
friendlyName,
|
|
353
|
+
shop,
|
|
354
|
+
timeToLive = 120,
|
|
355
|
+
partnerId = getPartnerIdSafe(),
|
|
356
|
+
privateKey = getPrivateKeySafe(args.useSandbox ?? true),
|
|
357
|
+
passbackParams,
|
|
358
|
+
// QR Options
|
|
359
|
+
qrFormat,
|
|
360
|
+
qrBorder,
|
|
361
|
+
qrErrorCorrection,
|
|
362
|
+
qrScale,
|
|
363
|
+
qrVersion,
|
|
364
|
+
// Barcode Options
|
|
365
|
+
barFormat,
|
|
366
|
+
barModuleWidth,
|
|
367
|
+
barBarHeight,
|
|
368
|
+
barFontSize,
|
|
369
|
+
barHumanReadableLocation,
|
|
370
|
+
useSandbox = true
|
|
371
|
+
} = args;
|
|
372
|
+
|
|
373
|
+
if (!reasonL1) {
|
|
374
|
+
throw new Error("reasonL1 is required (transaction grounds description)");
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
if (!partnerId) {
|
|
378
|
+
throw new Error("Partner ID is required. Provide via 'partnerId' parameter or set PAYWARE_PARTNER_ID environment variable.");
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
if (!privateKey) {
|
|
382
|
+
throw new Error("Private key is required. Provide via 'privateKey' parameter or set PAYWARE_PRIVATE_KEY environment variable.");
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
const result = await createTransaction({
|
|
386
|
+
type,
|
|
387
|
+
amount,
|
|
388
|
+
currency,
|
|
389
|
+
reasonL1,
|
|
390
|
+
reasonL2,
|
|
391
|
+
callbackUrl,
|
|
392
|
+
account,
|
|
393
|
+
friendlyName,
|
|
394
|
+
shop,
|
|
395
|
+
timeToLive,
|
|
396
|
+
partnerId,
|
|
397
|
+
privateKey,
|
|
398
|
+
passbackParams,
|
|
399
|
+
// QR Options
|
|
400
|
+
qrFormat,
|
|
401
|
+
qrBorder,
|
|
402
|
+
qrErrorCorrection,
|
|
403
|
+
qrScale,
|
|
404
|
+
qrVersion,
|
|
405
|
+
// Barcode Options
|
|
406
|
+
barFormat,
|
|
407
|
+
barModuleWidth,
|
|
408
|
+
barBarHeight,
|
|
409
|
+
barFontSize,
|
|
410
|
+
barHumanReadableLocation,
|
|
411
|
+
useSandbox
|
|
412
|
+
});
|
|
413
|
+
|
|
414
|
+
if (result.success) {
|
|
415
|
+
// New transactions are always ACTIVE when created
|
|
416
|
+
const status = result.transaction.status || 'ACTIVE';
|
|
417
|
+
const statusEmoji = '⏳';
|
|
418
|
+
|
|
419
|
+
return {
|
|
420
|
+
content: [{
|
|
421
|
+
type: "text",
|
|
422
|
+
text: `💳 **Transaction Created Successfully**
|
|
423
|
+
|
|
424
|
+
**Transaction Details:**
|
|
425
|
+
- ID: ${result.transaction.transactionId || 'N/A'}
|
|
426
|
+
- Type: ${type}
|
|
427
|
+
- Status: ${statusEmoji} ${status}
|
|
428
|
+
- Amount: ${amount} ${currency}
|
|
429
|
+
- Description: ${reasonL1}${reasonL2 ? ` (${reasonL2})` : ''}
|
|
430
|
+
${callbackUrl ? `- Callback URL: ${callbackUrl}` : ''}
|
|
431
|
+
|
|
432
|
+
**API Response:**
|
|
433
|
+
\`\`\`json
|
|
434
|
+
${JSON.stringify(result.transaction, null, 2)}
|
|
435
|
+
\`\`\`
|
|
436
|
+
|
|
437
|
+
**API Call Details:**
|
|
438
|
+
- Endpoint: POST ${baseUrl}/transactions
|
|
439
|
+
- Response Status: ${result.transaction ? 'Success' : 'Failed'}
|
|
440
|
+
- Request ID: ${result.requestId || 'N/A'}
|
|
441
|
+
- Timestamp: ${result.timestamp}
|
|
442
|
+
|
|
443
|
+
**Next Steps:**
|
|
444
|
+
1. Save the transaction ID: \`${result.transaction.transactionId || 'N/A'}\`
|
|
445
|
+
2. Use \`payware_transactions_get_transaction_status\` to check status
|
|
446
|
+
3. Wait for callback notification (if callback URL provided)
|
|
447
|
+
4. Process payment through payware interface`
|
|
448
|
+
}]
|
|
449
|
+
};
|
|
450
|
+
} else {
|
|
451
|
+
return {
|
|
452
|
+
content: [{
|
|
453
|
+
type: "text",
|
|
454
|
+
text: `❌ **Transaction Creation Failed**
|
|
455
|
+
|
|
456
|
+
**Error Details:**
|
|
457
|
+
- Message: ${result.error.message}
|
|
458
|
+
- Status: ${result.error.status || 'N/A'}
|
|
459
|
+
- Code: ${result.error.code || 'N/A'}
|
|
460
|
+
|
|
461
|
+
**API Call Details:**
|
|
462
|
+
- Endpoint: POST ${baseUrl}/transactions
|
|
463
|
+
- Headers: Api-Version=1, Content-Type=application/json
|
|
464
|
+
|
|
465
|
+
**Full Error Response:**
|
|
466
|
+
\`\`\`json
|
|
467
|
+
${JSON.stringify(result.error.details || result.error, null, 2)}
|
|
468
|
+
\`\`\`
|
|
469
|
+
|
|
470
|
+
**Timestamp:** ${result.timestamp}
|
|
471
|
+
|
|
472
|
+
**Troubleshooting:**
|
|
473
|
+
1. Verify JWT token is valid and not expired
|
|
474
|
+
2. Check transaction parameters (amount, type, currency)
|
|
475
|
+
3. Ensure private key matches public key registered with payware
|
|
476
|
+
4. Verify partner ID and API access
|
|
477
|
+
5. Check sandbox API status`
|
|
478
|
+
}]
|
|
479
|
+
};
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
};
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import axios from 'axios';
|
|
2
|
+
import { createJWTToken } from '../../core/auth/jwt-token.js';
|
|
3
|
+
import { getSandboxUrl, getProductionUrl, getPartnerIdSafe, getPrivateKeySafe } from '../../config/env.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Get transaction status from payware API
|
|
7
|
+
* @param {Object} params - Parameters for status request
|
|
8
|
+
* @returns {Object} Transaction status response
|
|
9
|
+
*/
|
|
10
|
+
export async function getTransactionStatus({ transactionId, partnerId, privateKey, useSandbox = true }) {
|
|
11
|
+
if (!transactionId) {
|
|
12
|
+
throw new Error('Transaction ID is required');
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
if (!partnerId || !privateKey) {
|
|
16
|
+
throw new Error('Partner ID and private key are required for proper JWT creation');
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// Create JWT token without body (GET request - no contentMd5 required)
|
|
20
|
+
const tokenData = createJWTToken(partnerId, privateKey, null);
|
|
21
|
+
|
|
22
|
+
// Required headers as per payware API documentation
|
|
23
|
+
const headers = {
|
|
24
|
+
'Authorization': `Bearer ${tokenData.token}`,
|
|
25
|
+
'Content-Type': 'application/json',
|
|
26
|
+
'Api-Version': '1' // Required: current API version
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
try {
|
|
30
|
+
const baseUrl = useSandbox ? getSandboxUrl() : getProductionUrl();
|
|
31
|
+
const response = await axios.get(`${baseUrl}/transactions/${transactionId}`, { headers });
|
|
32
|
+
|
|
33
|
+
return {
|
|
34
|
+
success: true,
|
|
35
|
+
transaction: response.data,
|
|
36
|
+
requestId: response.headers['x-request-id'],
|
|
37
|
+
timestamp: new Date().toISOString()
|
|
38
|
+
};
|
|
39
|
+
} catch (error) {
|
|
40
|
+
return {
|
|
41
|
+
success: false,
|
|
42
|
+
error: {
|
|
43
|
+
message: error.response?.data?.message || error.message,
|
|
44
|
+
status: error.response?.status,
|
|
45
|
+
code: error.response?.data?.code,
|
|
46
|
+
details: error.response?.data
|
|
47
|
+
},
|
|
48
|
+
timestamp: new Date().toISOString()
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Format transaction status for display
|
|
55
|
+
*/
|
|
56
|
+
function formatTransactionStatus(transaction) {
|
|
57
|
+
const statusEmojis = {
|
|
58
|
+
'ACTIVE': '⏳'
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
// If a transaction is returned from GET /transactions/{id}, it's always ACTIVE
|
|
62
|
+
// (transactions with final statuses are only available via /transactions-history)
|
|
63
|
+
const status = transaction.status || 'ACTIVE';
|
|
64
|
+
const emoji = statusEmojis[status] || '⏳';
|
|
65
|
+
|
|
66
|
+
return {
|
|
67
|
+
emoji,
|
|
68
|
+
status,
|
|
69
|
+
formatted: `${emoji} ${status}`
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Get transaction status tool implementation
|
|
75
|
+
*/
|
|
76
|
+
export const getTransactionStatusTool = {
|
|
77
|
+
name: "payware_operations_get_transaction_status",
|
|
78
|
+
description: `Get status of an ACTIVE transaction by ID (for completed/expired transactions use transaction history tool).
|
|
79
|
+
|
|
80
|
+
**Endpoint:** GET /transactions/{transactionId}
|
|
81
|
+
**Use Case:** Check status of ACTIVE transactions only. This endpoint only returns transactions with ACTIVE status.
|
|
82
|
+
**Note:** For transactions with final statuses (CONFIRMED, DECLINED, FAILED, EXPIRED, CANCELLED), use 'payware_transactions_get_history' instead.`,
|
|
83
|
+
inputSchema: {
|
|
84
|
+
type: "object",
|
|
85
|
+
properties: {
|
|
86
|
+
transactionId: {
|
|
87
|
+
type: "string",
|
|
88
|
+
description: "Transaction ID to check status for"
|
|
89
|
+
},
|
|
90
|
+
partnerId: {
|
|
91
|
+
type: "string",
|
|
92
|
+
description: "Partner ID from payware dashboard. Can use PAYWARE_PARTNER_ID env var as default.",
|
|
93
|
+
default: getPartnerIdSafe()
|
|
94
|
+
},
|
|
95
|
+
privateKey: {
|
|
96
|
+
type: "string",
|
|
97
|
+
description: "RSA private key for JWT token creation. Can use PAYWARE_PRIVATE_KEY_PATH env var as default.",
|
|
98
|
+
default: getPrivateKeySafe()
|
|
99
|
+
},
|
|
100
|
+
useSandbox: {
|
|
101
|
+
type: "boolean",
|
|
102
|
+
description: "Use sandbox environment for testing",
|
|
103
|
+
default: true
|
|
104
|
+
}
|
|
105
|
+
},
|
|
106
|
+
required: ["transactionId"],
|
|
107
|
+
additionalProperties: false
|
|
108
|
+
},
|
|
109
|
+
|
|
110
|
+
async handler(args) {
|
|
111
|
+
const { transactionId, partnerId = getPartnerIdSafe(), privateKey = getPrivateKeySafe(args.useSandbox ?? true), useSandbox = true } = args;
|
|
112
|
+
|
|
113
|
+
if (!transactionId) {
|
|
114
|
+
throw new Error("Transaction ID is required");
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (!partnerId) {
|
|
118
|
+
throw new Error("Partner ID is required. Provide via 'partnerId' parameter or set PAYWARE_PARTNER_ID environment variable.");
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (!privateKey) {
|
|
122
|
+
throw new Error("Private key is required. Provide via 'privateKey' parameter or set PAYWARE_PRIVATE_KEY environment variable.");
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const result = await getTransactionStatus({ transactionId, partnerId, privateKey, useSandbox });
|
|
126
|
+
|
|
127
|
+
if (result.success) {
|
|
128
|
+
const statusInfo = formatTransactionStatus(result.transaction);
|
|
129
|
+
|
|
130
|
+
return {
|
|
131
|
+
content: [{
|
|
132
|
+
type: "text",
|
|
133
|
+
text: `📊 **Transaction Status Retrieved**
|
|
134
|
+
|
|
135
|
+
**Transaction:** ${transactionId}
|
|
136
|
+
**Status:** ${statusInfo.formatted}
|
|
137
|
+
|
|
138
|
+
**Transaction Details:**
|
|
139
|
+
- ID: ${result.transaction.id || 'N/A'}
|
|
140
|
+
- Type: ${result.transaction.type || 'N/A'}
|
|
141
|
+
- Amount: ${result.transaction.trData ? `${result.transaction.trData.amount} ${result.transaction.trData.currency}` : 'N/A'}
|
|
142
|
+
- Description: ${result.transaction.description || 'N/A'}
|
|
143
|
+
- Created: ${result.transaction.createdAt || 'N/A'}
|
|
144
|
+
- Updated: ${result.transaction.updatedAt || 'N/A'}
|
|
145
|
+
|
|
146
|
+
**Full Response:**
|
|
147
|
+
\`\`\`json
|
|
148
|
+
${JSON.stringify(result.transaction, null, 2)}
|
|
149
|
+
\`\`\`
|
|
150
|
+
|
|
151
|
+
**Request ID:** ${result.requestId || 'N/A'}
|
|
152
|
+
**Timestamp:** ${result.timestamp}
|
|
153
|
+
|
|
154
|
+
**Status Meanings:**
|
|
155
|
+
- ⏳ ACTIVE: Active transaction pending processing or finalizing
|
|
156
|
+
|
|
157
|
+
**Note:** Only ACTIVE transactions are returned by this endpoint. For final statuses (CONFIRMED, DECLINED, FAILED, EXPIRED, CANCELLED), use the transaction history tool.`
|
|
158
|
+
}]
|
|
159
|
+
};
|
|
160
|
+
} else {
|
|
161
|
+
return {
|
|
162
|
+
content: [{
|
|
163
|
+
type: "text",
|
|
164
|
+
text: `❌ **Failed to Get Transaction Status**
|
|
165
|
+
|
|
166
|
+
**Transaction ID:** ${transactionId}
|
|
167
|
+
|
|
168
|
+
**Error Details:**
|
|
169
|
+
- Message: ${result.error.message}
|
|
170
|
+
- Status: ${result.error.status || 'N/A'}
|
|
171
|
+
- Code: ${result.error.code || 'N/A'}
|
|
172
|
+
|
|
173
|
+
**Full Error Response:**
|
|
174
|
+
\`\`\`json
|
|
175
|
+
${JSON.stringify(result.error.details || result.error, null, 2)}
|
|
176
|
+
\`\`\`
|
|
177
|
+
|
|
178
|
+
**Timestamp:** ${result.timestamp}
|
|
179
|
+
|
|
180
|
+
**Possible Causes:**
|
|
181
|
+
1. Transaction ID not found or invalid
|
|
182
|
+
2. JWT token expired or invalid
|
|
183
|
+
3. No access permission to this transaction
|
|
184
|
+
4. API connectivity issues
|
|
185
|
+
5. Transaction belongs to different partner
|
|
186
|
+
|
|
187
|
+
**Troubleshooting:**
|
|
188
|
+
1. Verify the transaction ID is correct
|
|
189
|
+
2. Check if JWT token is valid and not expired
|
|
190
|
+
3. Ensure you have access to this transaction
|
|
191
|
+
4. Try creating a new transaction if this one is very old`
|
|
192
|
+
}]
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
};
|