@payware/mcp-server 1.0.0 → 1.1.1
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/README.md +42 -4
- 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/README.md
CHANGED
|
@@ -1,18 +1,41 @@
|
|
|
1
1
|
# payware MCP Server
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/@payware/mcp-server)
|
|
4
|
+
|
|
5
|
+
Official MCP (Model Context Protocol) server for payware payment API integration. This server enables AI assistants to interact with payware APIs, automating integration workflows for all partner types.
|
|
6
|
+
|
|
7
|
+
## Who Is This For?
|
|
8
|
+
|
|
9
|
+
- **Merchants** - Accept payments, manage products, generate transaction reports
|
|
10
|
+
- **ISVs (Independent Software Vendors)** - Build multi-merchant platforms with OAuth2 token management
|
|
11
|
+
- **Payment Institutions** - Process A2A transfers, handle Payment Choice Architecture with multiple payment methods
|
|
12
|
+
|
|
13
|
+
## What's New in v1.1.0
|
|
14
|
+
|
|
15
|
+
- **Point of Interaction (POI)** - Full POI management: set prices, get status, generate QR codes, list and manage POIs
|
|
16
|
+
- **Payment Choice Architecture** - Support for `paymentMethod` field (A2A, CARD_FUNDED, BNPL, INSTANT_CREDIT) in transaction processing
|
|
4
17
|
|
|
5
18
|
## Features
|
|
6
19
|
|
|
7
20
|
### 🔐 Authentication Tools
|
|
8
21
|
- **RSA Key Generation**: Generate secure 2048-bit RSA key pairs
|
|
9
|
-
- **JWT Token Creation**: Create properly formatted JWT tokens
|
|
22
|
+
- **JWT Token Creation**: Create properly formatted JWT tokens
|
|
10
23
|
- **Sandbox Setup**: Configure sandbox authentication
|
|
11
24
|
|
|
12
25
|
### 💳 Transaction Tools
|
|
13
26
|
- **Create Transaction**: Support PLAIN, QR, and BARCODE transactions
|
|
27
|
+
- **Process Transaction**: Process transactions with Payment Choice Architecture support
|
|
14
28
|
- **Transaction Status**: Check transaction status by ID
|
|
15
|
-
- **
|
|
29
|
+
- **Transaction History**: Get finalized transaction details
|
|
30
|
+
- **Callback Simulation**: Test callback scenarios for merchants and payment institutions
|
|
31
|
+
|
|
32
|
+
### 📍 Point of Interaction (POI) Tools
|
|
33
|
+
- **Set Price**: Configure POI with amount, currency, and payment details
|
|
34
|
+
- **Cancel Price**: Clear active price from POI
|
|
35
|
+
- **Get POI**: Retrieve POI details and current state
|
|
36
|
+
- **Get Status**: Check POI transaction status
|
|
37
|
+
- **Get QR Code**: Generate QR code for POI payments
|
|
38
|
+
- **List POIs**: List all POIs for a merchant
|
|
16
39
|
|
|
17
40
|
### 🛠️ Utility Tools
|
|
18
41
|
- **Advanced Code Generation**: Generate complete integration code across 8 languages (Python, Node.js, PHP, Java, C#, Go, Ruby, cURL) with 16+ framework support
|
|
@@ -25,8 +48,15 @@ Minimal MCP server for payware API integration. Enables quick merchant integrati
|
|
|
25
48
|
|
|
26
49
|
### 1. Installation
|
|
27
50
|
|
|
51
|
+
**From npm (recommended):**
|
|
28
52
|
```bash
|
|
29
|
-
|
|
53
|
+
npm install -g @payware/mcp-server
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
**From source:**
|
|
57
|
+
```bash
|
|
58
|
+
git clone https://github.com/payware/mcp-server.git
|
|
59
|
+
cd mcp-server
|
|
30
60
|
npm install
|
|
31
61
|
```
|
|
32
62
|
|
|
@@ -309,6 +339,14 @@ Generate production-ready code examples for any payware operation.
|
|
|
309
339
|
- `get_product` - Get product details
|
|
310
340
|
- `list_products` - List all products
|
|
311
341
|
|
|
342
|
+
**POI Operations (isv):**
|
|
343
|
+
- `set_price` - Set price on a Point of Interaction device
|
|
344
|
+
- `cancel_price` - Cancel active price on POI
|
|
345
|
+
- `get_poi` - Get POI details
|
|
346
|
+
- `get_poi_status` - Get POI transaction status
|
|
347
|
+
- `get_poi_qrcode` - Generate QR code for POI
|
|
348
|
+
- `list_pois` - List all POIs for a merchant
|
|
349
|
+
|
|
312
350
|
**OAuth2 Operations (isv only):**
|
|
313
351
|
- `obtain_token` - Obtain OAuth2 access token
|
|
314
352
|
- `get_token_info` - Get OAuth2 token information
|
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
|
+
};
|