@payware/mcp-server 1.0.0 → 1.1.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/package.json +1 -1
- package/src/isv/data/index.js +23 -0
- package/src/isv/deep-links/delete-product-link.js +1 -1
- package/src/isv/deep-links/delete-transaction-link.js +1 -1
- package/src/isv/deep-links/get-product-link.js +1 -1
- package/src/isv/deep-links/get-transaction-link.js +1 -1
- package/src/isv/index.js +11 -1
- package/src/isv/poi/cancel-price.js +149 -0
- package/src/isv/poi/get-poi.js +190 -0
- package/src/isv/poi/get-qrcode.js +151 -0
- package/src/isv/poi/get-status.js +176 -0
- package/src/isv/poi/index.js +32 -0
- package/src/isv/poi/list-pois.js +168 -0
- package/src/isv/poi/set-price.js +225 -0
- package/src/isv/transactions/get-status.js +1 -1
- package/src/isv/transactions/transaction-history.js +2 -1
- package/src/mcp-proxy.js +2 -2
- package/src/merchant/data/export-report.js +3 -4
- package/src/merchant/data/generate-report.js +3 -4
- package/src/merchant/data/get-export-list.js +140 -0
- package/src/merchant/data/get-report-requests.js +125 -0
- package/src/merchant/data/get-report-status.js +3 -4
- package/src/merchant/data/index.js +64 -25
- package/src/merchant/data/list-reports.js +107 -0
- package/src/merchant/deep-links/delete-product-link.js +1 -1
- package/src/merchant/deep-links/delete-transaction-link.js +1 -1
- package/src/merchant/deep-links/get-product-link.js +1 -1
- package/src/merchant/deep-links/get-transaction-link.js +1 -1
- package/src/merchant/transactions/create-transaction.js +4 -2
- package/src/merchant/transactions/get-status.js +1 -1
- package/src/merchant/transactions/process-transaction.js +10 -1
- package/src/merchant/transactions/simulate-callback.js +11 -2
- package/src/merchant/transactions/transaction-history.js +1 -1
- package/src/payment-institution/data/index.js +23 -0
- package/src/payment-institution/deep-links/delete-transaction-link.js +1 -1
- package/src/payment-institution/deep-links/get-transaction-link.js +1 -1
- package/src/payment-institution/transactions/create-pi-transaction.js +2 -0
- package/src/payment-institution/transactions/finalize-transaction.js +1 -0
- package/src/payment-institution/transactions/get-transaction-history.js +1 -1
- package/src/payment-institution/transactions/get-transaction-status.js +1 -1
- package/src/payment-institution/transactions/process-pi-transaction.js +12 -0
- package/src/payment-institution/transactions/simulate-pi-callback.js +12 -2
- package/src/payment-institution/transactions/soundbite-transaction.js +2 -0
- package/src/shared/data/data-api.js +97 -0
- package/src/utils/code-examples/merchant/transactions.js +2 -1
- package/src/utils/code-examples/payment-institution/transactions.js +1 -0
package/package.json
CHANGED
package/src/isv/data/index.js
CHANGED
|
@@ -11,6 +11,9 @@ import {
|
|
|
11
11
|
downloadExport,
|
|
12
12
|
cancelReport,
|
|
13
13
|
cancelExport,
|
|
14
|
+
listReports,
|
|
15
|
+
getReportRequests,
|
|
16
|
+
getExportList,
|
|
14
17
|
REPORT_TYPES,
|
|
15
18
|
EXPORT_FORMATS,
|
|
16
19
|
LOCALES
|
|
@@ -229,6 +232,23 @@ This feature requires proper OAuth2 authorization from the merchant and is only
|
|
|
229
232
|
};
|
|
230
233
|
|
|
231
234
|
// Create all other ISV data tools
|
|
235
|
+
export const listReportsTool = createISVDataTool('list_reports', listReports, 'List All Available Reports', {});
|
|
236
|
+
|
|
237
|
+
export const getReportRequestsTool = createISVDataTool('get_report_requests', getReportRequests, 'Get Report Request History', {
|
|
238
|
+
reportId: {
|
|
239
|
+
type: "string",
|
|
240
|
+
description: "The report unit ID (path) to get execution history for (e.g., '/processedTransactions')",
|
|
241
|
+
enum: Object.values(REPORT_TYPES)
|
|
242
|
+
}
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
export const getExportListTool = createISVDataTool('get_export_list', getExportList, 'Get Export List for Request', {
|
|
246
|
+
requestId: {
|
|
247
|
+
type: "string",
|
|
248
|
+
description: "The request ID (UUID) to get exports for"
|
|
249
|
+
}
|
|
250
|
+
});
|
|
251
|
+
|
|
232
252
|
export const getReportStatusTool = createISVDataTool('get_report_status', getReportStatus, 'Get Report Status', {
|
|
233
253
|
requestId: {
|
|
234
254
|
type: "string",
|
|
@@ -281,9 +301,12 @@ export const cancelExportTool = createISVDataTool('cancel_export', cancelExport,
|
|
|
281
301
|
* All ISV data tools
|
|
282
302
|
*/
|
|
283
303
|
export const isvDataTools = [
|
|
304
|
+
listReportsTool,
|
|
284
305
|
generateReportTool,
|
|
306
|
+
getReportRequestsTool,
|
|
285
307
|
getReportStatusTool,
|
|
286
308
|
exportReportTool,
|
|
309
|
+
getExportListTool,
|
|
287
310
|
getExportStatusTool,
|
|
288
311
|
downloadExportTool,
|
|
289
312
|
cancelReportTool,
|
|
@@ -100,7 +100,7 @@ export const deleteProductLinkTool = {
|
|
|
100
100
|
- Product ID: ${productId}
|
|
101
101
|
- Merchant: ${merchantPartnerId}
|
|
102
102
|
- Status: Successfully deleted
|
|
103
|
-
- Link URL: https://
|
|
103
|
+
- Link URL: https://go.payware.eu/${productId} (now inactive)
|
|
104
104
|
|
|
105
105
|
**ISV Authentication:**
|
|
106
106
|
- ISV Partner ID: ${isvPartnerId}
|
|
@@ -100,7 +100,7 @@ export const deleteTransactionLinkTool = {
|
|
|
100
100
|
- Transaction ID: ${transactionId}
|
|
101
101
|
- Merchant: ${merchantPartnerId}
|
|
102
102
|
- Status: Successfully deleted
|
|
103
|
-
- Link URL: https://
|
|
103
|
+
- Link URL: https://go.payware.eu/${transactionId} (now inactive)
|
|
104
104
|
|
|
105
105
|
**ISV Authentication:**
|
|
106
106
|
- ISV Partner ID: ${isvPartnerId}
|
|
@@ -13,7 +13,7 @@ export const getProductLinkTool = {
|
|
|
13
13
|
- Requires OAuth2 token in 'sub' claim
|
|
14
14
|
|
|
15
15
|
**Deep Link Format:**
|
|
16
|
-
- Web URL: https://
|
|
16
|
+
- Web URL: https://go.payware.eu/{productId}
|
|
17
17
|
- Mobile Redirect: payware://{productId}
|
|
18
18
|
|
|
19
19
|
**Use Cases:**
|
|
@@ -13,7 +13,7 @@ export const getTransactionLinkTool = {
|
|
|
13
13
|
- Requires OAuth2 token in 'sub' claim
|
|
14
14
|
|
|
15
15
|
**Deep Link Format:**
|
|
16
|
-
- Web URL: https://
|
|
16
|
+
- Web URL: https://go.payware.eu/{transactionId}
|
|
17
17
|
- Mobile Redirect: payware://{transactionId}
|
|
18
18
|
|
|
19
19
|
**Use Cases:**
|
package/src/isv/index.js
CHANGED
|
@@ -25,6 +25,9 @@ import { isvProductTools } from './products/index.js';
|
|
|
25
25
|
// Deep links tools
|
|
26
26
|
import { isvDeepLinksTools } from './deep-links/index.js';
|
|
27
27
|
|
|
28
|
+
// POI tools
|
|
29
|
+
import { isvPOITools } from './poi/index.js';
|
|
30
|
+
|
|
28
31
|
// Import shared tools
|
|
29
32
|
import { sharedTools } from '../shared/index.js';
|
|
30
33
|
|
|
@@ -55,7 +58,10 @@ export const isvTools = [
|
|
|
55
58
|
...isvProductTools,
|
|
56
59
|
|
|
57
60
|
// Deep links management
|
|
58
|
-
...isvDeepLinksTools
|
|
61
|
+
...isvDeepLinksTools,
|
|
62
|
+
|
|
63
|
+
// POI management
|
|
64
|
+
...isvPOITools
|
|
59
65
|
];
|
|
60
66
|
|
|
61
67
|
/**
|
|
@@ -94,6 +100,10 @@ export function getISVTools() {
|
|
|
94
100
|
else if (tool.name.startsWith('payware_deep_links_')) {
|
|
95
101
|
newName = tool.name; // Keep deep_links grouping
|
|
96
102
|
}
|
|
103
|
+
// POI tools keep their poi grouping
|
|
104
|
+
else if (tool.name.startsWith('payware_poi_')) {
|
|
105
|
+
newName = tool.name; // Keep poi grouping
|
|
106
|
+
}
|
|
97
107
|
|
|
98
108
|
return {
|
|
99
109
|
...tool,
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import axios from 'axios';
|
|
2
|
+
import { createJWTForPartner } from '../../core/auth/jwt-factory.js';
|
|
3
|
+
import { getSandboxUrl, getProductionUrl, getPartnerIdSafe, getPrivateKeySafe } from '../../config/env.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Cancel pending price on a POI via ISV authentication
|
|
7
|
+
* @param {Object} params - Parameters for cancel request
|
|
8
|
+
* @returns {Object} Cancel response
|
|
9
|
+
*/
|
|
10
|
+
export async function cancelPOIPrice({ poiId, merchantPartnerId, oauth2Token, useSandbox = true }) {
|
|
11
|
+
if (!poiId) {
|
|
12
|
+
throw new Error('POI ID is required');
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
if (!merchantPartnerId) {
|
|
16
|
+
throw new Error('Merchant Partner ID is required for ISV operations');
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if (!oauth2Token) {
|
|
20
|
+
throw new Error('OAuth2 token is required for ISV operations');
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const isvPartnerId = getPartnerIdSafe();
|
|
24
|
+
const privateKey = getPrivateKeySafe(useSandbox);
|
|
25
|
+
|
|
26
|
+
const jwtData = await createJWTForPartner({
|
|
27
|
+
partnerId: isvPartnerId,
|
|
28
|
+
privateKey,
|
|
29
|
+
requestBody: null,
|
|
30
|
+
merchantId: merchantPartnerId,
|
|
31
|
+
oauth2Token
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
const headers = {
|
|
35
|
+
'Authorization': `Bearer ${jwtData.token}`,
|
|
36
|
+
'Content-Type': 'application/json',
|
|
37
|
+
'Api-Version': '1'
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
try {
|
|
41
|
+
const baseUrl = useSandbox ? getSandboxUrl() : getProductionUrl();
|
|
42
|
+
const response = await axios.delete(`${baseUrl}/poi/${poiId}/price`, { headers });
|
|
43
|
+
|
|
44
|
+
return {
|
|
45
|
+
success: true,
|
|
46
|
+
requestId: response.headers['x-request-id'],
|
|
47
|
+
timestamp: new Date().toISOString()
|
|
48
|
+
};
|
|
49
|
+
} catch (error) {
|
|
50
|
+
return {
|
|
51
|
+
success: false,
|
|
52
|
+
error: {
|
|
53
|
+
message: error.response?.data?.message || error.message,
|
|
54
|
+
status: error.response?.status,
|
|
55
|
+
code: error.response?.data?.code,
|
|
56
|
+
details: error.response?.data
|
|
57
|
+
},
|
|
58
|
+
timestamp: new Date().toISOString()
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Cancel POI price tool implementation for ISV
|
|
65
|
+
*/
|
|
66
|
+
export const cancelPOIPriceTool = {
|
|
67
|
+
name: "payware_poi_cancel_price",
|
|
68
|
+
description: `Cancel a pending price on a POI, resetting it to IDLE state.
|
|
69
|
+
|
|
70
|
+
**ISV Authentication:** Uses ISV JWT with merchant partner ID and OAuth2 token.
|
|
71
|
+
**Endpoint:** DELETE /poi/{poiId}/price
|
|
72
|
+
**Use Case:** Cancel a price that was set but customer didn't pay (e.g., order cancelled).
|
|
73
|
+
|
|
74
|
+
The POI will return to IDLE state and be ready for a new price.
|
|
75
|
+
|
|
76
|
+
**Required:** POI ID, Merchant Partner ID, and OAuth2 token.`,
|
|
77
|
+
|
|
78
|
+
inputSchema: {
|
|
79
|
+
type: "object",
|
|
80
|
+
required: ["poiId", "merchantPartnerId", "oauth2Token"],
|
|
81
|
+
properties: {
|
|
82
|
+
poiId: {
|
|
83
|
+
type: "string",
|
|
84
|
+
description: "The POI identifier (format: pi + 8 alphanumeric chars, e.g., piABC12345)"
|
|
85
|
+
},
|
|
86
|
+
merchantPartnerId: {
|
|
87
|
+
type: "string",
|
|
88
|
+
description: "Partner ID of the target merchant (8 alphanumeric characters)"
|
|
89
|
+
},
|
|
90
|
+
oauth2Token: {
|
|
91
|
+
type: "string",
|
|
92
|
+
description: "OAuth2 access token obtained from the merchant"
|
|
93
|
+
},
|
|
94
|
+
useSandbox: {
|
|
95
|
+
type: "boolean",
|
|
96
|
+
description: "Use sandbox environment for testing",
|
|
97
|
+
default: true
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
},
|
|
101
|
+
|
|
102
|
+
async handler(args) {
|
|
103
|
+
const { poiId, merchantPartnerId, oauth2Token, useSandbox = true } = args;
|
|
104
|
+
|
|
105
|
+
if (!poiId) throw new Error("POI ID is required");
|
|
106
|
+
if (!merchantPartnerId) throw new Error("Merchant Partner ID is required");
|
|
107
|
+
if (!oauth2Token) throw new Error("OAuth2 token is required");
|
|
108
|
+
|
|
109
|
+
const result = await cancelPOIPrice({ poiId, merchantPartnerId, oauth2Token, useSandbox });
|
|
110
|
+
|
|
111
|
+
if (result.success) {
|
|
112
|
+
return {
|
|
113
|
+
content: [{
|
|
114
|
+
type: "text",
|
|
115
|
+
text: `✅ **Price Cancelled Successfully**
|
|
116
|
+
|
|
117
|
+
**POI ID:** ${poiId}
|
|
118
|
+
**Status:** 🟢 IDLE (ready for new price)
|
|
119
|
+
|
|
120
|
+
The POI is now ready to accept a new price.
|
|
121
|
+
|
|
122
|
+
**ISV -> Merchant:** ${getPartnerIdSafe()} -> ${merchantPartnerId}
|
|
123
|
+
**Request ID:** ${result.requestId || 'N/A'}
|
|
124
|
+
**Timestamp:** ${result.timestamp}`
|
|
125
|
+
}]
|
|
126
|
+
};
|
|
127
|
+
} else {
|
|
128
|
+
return {
|
|
129
|
+
content: [{
|
|
130
|
+
type: "text",
|
|
131
|
+
text: `❌ **Failed to Cancel Price**
|
|
132
|
+
|
|
133
|
+
**POI ID:** ${poiId}
|
|
134
|
+
|
|
135
|
+
**Error:** ${result.error.message}
|
|
136
|
+
**Code:** ${result.error.code || 'N/A'}
|
|
137
|
+
**Status:** ${result.error.status || 'N/A'}
|
|
138
|
+
|
|
139
|
+
**Common Issues:**
|
|
140
|
+
- INVALID_POI_ID (400): POI ID format invalid (must be pi + 8 alphanumeric chars)
|
|
141
|
+
- POI_NOT_FOUND (404): POI doesn't exist or doesn't belong to merchant
|
|
142
|
+
- POI_NO_PENDING_PAYMENT (409): No pending price to cancel (POI already IDLE)
|
|
143
|
+
|
|
144
|
+
**Timestamp:** ${result.timestamp}`
|
|
145
|
+
}]
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
};
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import axios from 'axios';
|
|
2
|
+
import { createJWTForPartner } from '../../core/auth/jwt-factory.js';
|
|
3
|
+
import { getSandboxUrl, getProductionUrl, getPartnerIdSafe, getPrivateKeySafe } from '../../config/env.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Get POI details via ISV authentication
|
|
7
|
+
* @param {Object} params - Parameters for get request
|
|
8
|
+
* @returns {Object} POI details response
|
|
9
|
+
*/
|
|
10
|
+
export async function getPOI({ poiId, merchantPartnerId, oauth2Token, useSandbox = true }) {
|
|
11
|
+
if (!poiId) {
|
|
12
|
+
throw new Error('POI ID is required');
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
if (!merchantPartnerId) {
|
|
16
|
+
throw new Error('Merchant Partner ID is required for ISV operations');
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if (!oauth2Token) {
|
|
20
|
+
throw new Error('OAuth2 token is required for ISV operations');
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Get ISV credentials
|
|
24
|
+
const isvPartnerId = getPartnerIdSafe();
|
|
25
|
+
const privateKey = getPrivateKeySafe(useSandbox);
|
|
26
|
+
|
|
27
|
+
// Create ISV JWT token (GET request - no body)
|
|
28
|
+
const jwtData = await createJWTForPartner({
|
|
29
|
+
partnerId: isvPartnerId,
|
|
30
|
+
privateKey,
|
|
31
|
+
requestBody: null,
|
|
32
|
+
merchantId: merchantPartnerId,
|
|
33
|
+
oauth2Token
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
// Required headers as per payware API documentation
|
|
37
|
+
const headers = {
|
|
38
|
+
'Authorization': `Bearer ${jwtData.token}`,
|
|
39
|
+
'Content-Type': 'application/json',
|
|
40
|
+
'Api-Version': '1'
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
try {
|
|
44
|
+
const baseUrl = useSandbox ? getSandboxUrl() : getProductionUrl();
|
|
45
|
+
const response = await axios.get(`${baseUrl}/poi/${poiId}`, { headers });
|
|
46
|
+
|
|
47
|
+
return {
|
|
48
|
+
success: true,
|
|
49
|
+
poi: response.data,
|
|
50
|
+
requestId: response.headers['x-request-id'],
|
|
51
|
+
timestamp: new Date().toISOString()
|
|
52
|
+
};
|
|
53
|
+
} catch (error) {
|
|
54
|
+
return {
|
|
55
|
+
success: false,
|
|
56
|
+
error: {
|
|
57
|
+
message: error.response?.data?.message || error.message,
|
|
58
|
+
status: error.response?.status,
|
|
59
|
+
code: error.response?.data?.code,
|
|
60
|
+
details: error.response?.data
|
|
61
|
+
},
|
|
62
|
+
timestamp: new Date().toISOString()
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Get POI details tool implementation for ISV
|
|
69
|
+
*/
|
|
70
|
+
export const getPOITool = {
|
|
71
|
+
name: "payware_poi_get",
|
|
72
|
+
description: `Get details of a specific POI (Point of Interaction).
|
|
73
|
+
|
|
74
|
+
**ISV Authentication:** Uses ISV JWT with merchant partner ID and OAuth2 token.
|
|
75
|
+
**Endpoint:** GET /poi/{poiId}
|
|
76
|
+
**Use Case:** Retrieve full details of a physical payment point including configuration and current state.
|
|
77
|
+
|
|
78
|
+
**Required:** POI ID, Merchant Partner ID, and OAuth2 token.`,
|
|
79
|
+
|
|
80
|
+
inputSchema: {
|
|
81
|
+
type: "object",
|
|
82
|
+
required: ["poiId", "merchantPartnerId", "oauth2Token"],
|
|
83
|
+
properties: {
|
|
84
|
+
poiId: {
|
|
85
|
+
type: "string",
|
|
86
|
+
description: "The POI identifier (format: pi + 8 alphanumeric chars, e.g., piABC12345)"
|
|
87
|
+
},
|
|
88
|
+
merchantPartnerId: {
|
|
89
|
+
type: "string",
|
|
90
|
+
description: "Partner ID of the target merchant (8 alphanumeric characters)"
|
|
91
|
+
},
|
|
92
|
+
oauth2Token: {
|
|
93
|
+
type: "string",
|
|
94
|
+
description: "OAuth2 access token obtained from the merchant"
|
|
95
|
+
},
|
|
96
|
+
useSandbox: {
|
|
97
|
+
type: "boolean",
|
|
98
|
+
description: "Use sandbox environment for testing",
|
|
99
|
+
default: true
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
},
|
|
103
|
+
|
|
104
|
+
async handler(args) {
|
|
105
|
+
const { poiId, merchantPartnerId, oauth2Token, useSandbox = true } = args;
|
|
106
|
+
|
|
107
|
+
if (!poiId) {
|
|
108
|
+
throw new Error("POI ID is required");
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (!merchantPartnerId) {
|
|
112
|
+
throw new Error("Merchant Partner ID is required for ISV operations");
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (!oauth2Token) {
|
|
116
|
+
throw new Error("OAuth2 token is required for ISV operations");
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const result = await getPOI({ poiId, merchantPartnerId, oauth2Token, useSandbox });
|
|
120
|
+
|
|
121
|
+
if (result.success) {
|
|
122
|
+
const poi = result.poi;
|
|
123
|
+
const statusEmoji = {
|
|
124
|
+
'IDLE': '🟢',
|
|
125
|
+
'READY': '🟡',
|
|
126
|
+
'BUSY': '🔴',
|
|
127
|
+
'DISABLED': '⚫'
|
|
128
|
+
}[poi.status] || '⚪';
|
|
129
|
+
|
|
130
|
+
return {
|
|
131
|
+
content: [{
|
|
132
|
+
type: "text",
|
|
133
|
+
text: `📍 **POI Details Retrieved (ISV -> Merchant: ${merchantPartnerId})**
|
|
134
|
+
|
|
135
|
+
**POI:** ${poi.name} (${poi.poiId})
|
|
136
|
+
**Status:** ${statusEmoji} ${poi.status}
|
|
137
|
+
**Active:** ${poi.isActive ? '✅ Yes' : '❌ No'}
|
|
138
|
+
|
|
139
|
+
**Location:**
|
|
140
|
+
- Shop: ${poi.shopName || 'N/A'} (${poi.shopCode || 'N/A'})
|
|
141
|
+
|
|
142
|
+
**Configuration:**
|
|
143
|
+
- Default TTL: ${poi.ttlSeconds || 120} seconds
|
|
144
|
+
- Callback URL: ${poi.callbackUrl || 'Not configured'}
|
|
145
|
+
- Linked vPOS: ${poi.linkedVposCount || 0} terminals
|
|
146
|
+
|
|
147
|
+
**Current Session:**
|
|
148
|
+
${poi.status !== 'IDLE' && poi.pendingAmount ? `- Amount: ${poi.pendingAmount} ${poi.pendingCurrency}
|
|
149
|
+
- Expires: ${poi.sessionExpiresAt || 'N/A'}
|
|
150
|
+
- Token: ${poi.sessionToken || 'N/A'}` : '- No active session'}
|
|
151
|
+
|
|
152
|
+
**Timestamps:**
|
|
153
|
+
- Created: ${poi.createdAt || 'N/A'}
|
|
154
|
+
- Updated: ${poi.updatedAt || 'N/A'}
|
|
155
|
+
|
|
156
|
+
**Full Response:**
|
|
157
|
+
\`\`\`json
|
|
158
|
+
${JSON.stringify(poi, null, 2)}
|
|
159
|
+
\`\`\`
|
|
160
|
+
|
|
161
|
+
**Request ID:** ${result.requestId || 'N/A'}
|
|
162
|
+
**Timestamp:** ${result.timestamp}`
|
|
163
|
+
}]
|
|
164
|
+
};
|
|
165
|
+
} else {
|
|
166
|
+
return {
|
|
167
|
+
content: [{
|
|
168
|
+
type: "text",
|
|
169
|
+
text: `❌ **Failed to Get POI Details**
|
|
170
|
+
|
|
171
|
+
**POI ID:** ${poiId}
|
|
172
|
+
**ISV -> Merchant:** ${getPartnerIdSafe()} -> ${merchantPartnerId}
|
|
173
|
+
|
|
174
|
+
**Error Details:**
|
|
175
|
+
- Message: ${result.error.message}
|
|
176
|
+
- Status: ${result.error.status || 'N/A'}
|
|
177
|
+
- Code: ${result.error.code || 'N/A'}
|
|
178
|
+
|
|
179
|
+
**Troubleshooting:**
|
|
180
|
+
1. Verify POI ID format (must be pi + 8 alphanumeric chars, e.g., piABC12345)
|
|
181
|
+
2. If 400 error: POI ID format is invalid (ERR_INVALID_POI_ID)
|
|
182
|
+
3. If 404 error: POI not found or doesn't belong to merchant
|
|
183
|
+
4. Check OAuth2 token validity
|
|
184
|
+
|
|
185
|
+
**Timestamp:** ${result.timestamp}`
|
|
186
|
+
}]
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
};
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import axios from 'axios';
|
|
2
|
+
import { getSandboxUrl, getProductionUrl } from '../../config/env.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Get POI QR code image
|
|
6
|
+
* Note: This endpoint is public and doesn't require authentication
|
|
7
|
+
* @param {Object} params - Parameters for QR code request
|
|
8
|
+
* @returns {Object} QR code response
|
|
9
|
+
*/
|
|
10
|
+
export async function getPOIQRCode({ poiId, format = 'PNG', useSandbox = true }) {
|
|
11
|
+
if (!poiId) {
|
|
12
|
+
throw new Error('POI ID is required');
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const headers = {
|
|
16
|
+
'Api-Version': '1'
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
try {
|
|
20
|
+
const baseUrl = useSandbox ? getSandboxUrl() : getProductionUrl();
|
|
21
|
+
const response = await axios.get(`${baseUrl}/poi/${poiId}/image`, {
|
|
22
|
+
headers,
|
|
23
|
+
params: { format },
|
|
24
|
+
responseType: 'arraybuffer'
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
const contentType = response.headers['content-type'];
|
|
28
|
+
const base64Image = Buffer.from(response.data).toString('base64');
|
|
29
|
+
|
|
30
|
+
return {
|
|
31
|
+
success: true,
|
|
32
|
+
image: {
|
|
33
|
+
base64: base64Image,
|
|
34
|
+
contentType,
|
|
35
|
+
format,
|
|
36
|
+
size: response.data.length
|
|
37
|
+
},
|
|
38
|
+
requestId: response.headers['x-request-id'],
|
|
39
|
+
timestamp: new Date().toISOString()
|
|
40
|
+
};
|
|
41
|
+
} catch (error) {
|
|
42
|
+
return {
|
|
43
|
+
success: false,
|
|
44
|
+
error: {
|
|
45
|
+
message: error.response?.data?.message || error.message,
|
|
46
|
+
status: error.response?.status,
|
|
47
|
+
code: error.response?.data?.code,
|
|
48
|
+
details: error.response?.data
|
|
49
|
+
},
|
|
50
|
+
timestamp: new Date().toISOString()
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Get POI QR code tool implementation
|
|
57
|
+
*/
|
|
58
|
+
export const getPOIQRCodeTool = {
|
|
59
|
+
name: "payware_poi_get_qrcode",
|
|
60
|
+
description: `Generate a QR code image for a POI that customers can scan.
|
|
61
|
+
|
|
62
|
+
**Endpoint:** GET /poi/{poiId}/image
|
|
63
|
+
**Authentication:** Public endpoint (no auth required)
|
|
64
|
+
**Use Case:** Generate QR codes for printing or displaying on screens.
|
|
65
|
+
|
|
66
|
+
**Formats:** PNG (default), SVG, JPG
|
|
67
|
+
|
|
68
|
+
Returns base64-encoded image data that can be saved or displayed.
|
|
69
|
+
|
|
70
|
+
**Required:** POI ID only.`,
|
|
71
|
+
|
|
72
|
+
inputSchema: {
|
|
73
|
+
type: "object",
|
|
74
|
+
required: ["poiId"],
|
|
75
|
+
properties: {
|
|
76
|
+
poiId: {
|
|
77
|
+
type: "string",
|
|
78
|
+
description: "The POI identifier (format: pi + 8 alphanumeric chars, e.g., piABC12345)"
|
|
79
|
+
},
|
|
80
|
+
format: {
|
|
81
|
+
type: "string",
|
|
82
|
+
enum: ["PNG", "SVG", "JPG"],
|
|
83
|
+
description: "Image format (default: PNG)",
|
|
84
|
+
default: "PNG"
|
|
85
|
+
},
|
|
86
|
+
useSandbox: {
|
|
87
|
+
type: "boolean",
|
|
88
|
+
description: "Use sandbox environment for testing",
|
|
89
|
+
default: true
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
|
|
94
|
+
async handler(args) {
|
|
95
|
+
const { poiId, format = 'PNG', useSandbox = true } = args;
|
|
96
|
+
|
|
97
|
+
if (!poiId) throw new Error("POI ID is required");
|
|
98
|
+
|
|
99
|
+
const result = await getPOIQRCode({ poiId, format, useSandbox });
|
|
100
|
+
|
|
101
|
+
if (result.success) {
|
|
102
|
+
const img = result.image;
|
|
103
|
+
const sizeKB = (img.size / 1024).toFixed(2);
|
|
104
|
+
|
|
105
|
+
return {
|
|
106
|
+
content: [{
|
|
107
|
+
type: "text",
|
|
108
|
+
text: `📱 **POI QR Code Generated**
|
|
109
|
+
|
|
110
|
+
**POI ID:** ${poiId}
|
|
111
|
+
**Format:** ${img.format}
|
|
112
|
+
**Size:** ${sizeKB} KB
|
|
113
|
+
**Content-Type:** ${img.contentType}
|
|
114
|
+
|
|
115
|
+
**Base64 Image Data:**
|
|
116
|
+
\`\`\`
|
|
117
|
+
${img.base64.substring(0, 100)}...
|
|
118
|
+
\`\`\`
|
|
119
|
+
|
|
120
|
+
**Usage:**
|
|
121
|
+
- Save as file: decode base64 and write to ${poiId}.${format.toLowerCase()}
|
|
122
|
+
- Display in HTML: \`<img src="data:${img.contentType};base64,${img.base64.substring(0, 20)}...">\`
|
|
123
|
+
- Print for physical placement at point of sale
|
|
124
|
+
|
|
125
|
+
**Full base64 length:** ${img.base64.length} characters
|
|
126
|
+
|
|
127
|
+
**Request ID:** ${result.requestId || 'N/A'}
|
|
128
|
+
**Timestamp:** ${result.timestamp}`
|
|
129
|
+
}]
|
|
130
|
+
};
|
|
131
|
+
} else {
|
|
132
|
+
return {
|
|
133
|
+
content: [{
|
|
134
|
+
type: "text",
|
|
135
|
+
text: `❌ **Failed to Generate QR Code**
|
|
136
|
+
|
|
137
|
+
**POI ID:** ${poiId}
|
|
138
|
+
|
|
139
|
+
**Error:** ${result.error.message}
|
|
140
|
+
**Status:** ${result.error.status || 'N/A'}
|
|
141
|
+
|
|
142
|
+
**Common Issues:**
|
|
143
|
+
- INVALID_POI_ID (400): POI ID format invalid (must be pi + 8 alphanumeric chars)
|
|
144
|
+
- POI_NOT_FOUND (404): POI doesn't exist
|
|
145
|
+
|
|
146
|
+
**Timestamp:** ${result.timestamp}`
|
|
147
|
+
}]
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
};
|