@payware/mcp-server 1.3.2 → 1.3.3
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/oauth2/get-token-info.js +7 -5
- package/src/isv/oauth2/obtain-token.js +6 -4
- package/src/isv/oauth2/token-helper.js +26 -26
- package/src/isv/poi/cancel-price.js +7 -2
- package/src/isv/poi/get-poi.js +2 -3
- package/src/isv/poi/get-status.js +194 -168
- package/src/isv/poi/list-pois.js +4 -6
- package/src/isv/poi/set-price.js +38 -53
- package/src/isv/transactions/simulate-callback.js +19 -10
- package/src/merchant/transactions/simulate-callback.js +322 -313
- package/src/merchant/transactions/transaction-history.js +2 -2
- 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/simulate-pi-callback.js +385 -375
- package/src/payment-institution/transactions/soundbite-transaction.js +1 -1
- package/src/shared/poi/poi-create-api.js +1 -1
- package/src/tools/advanced-code-generator.js +14 -265
- package/src/utils/code-examples/common/auth-examples.js +1 -1
- package/src/utils/code-examples/isv/products.js +1 -1
- package/src/utils/code-examples/isv/transactions.js +1 -1
- package/src/utils/code-examples/merchant/transactions.js +1 -1
- package/src/utils/code-examples.js +2 -2
package/package.json
CHANGED
|
@@ -76,18 +76,20 @@ Auth: Requires ISV JWT token`,
|
|
|
76
76
|
text: `📋 **OAuth2 Token Information**
|
|
77
77
|
|
|
78
78
|
🔑 **Token Details:**
|
|
79
|
-
- **Access Token**: ${result.
|
|
80
|
-
- **Token Type**: ${result.
|
|
79
|
+
- **Access Token**: ${result.accessToken}
|
|
80
|
+
- **Token Type**: ${result.tokenType}
|
|
81
81
|
- **Status**: ${statusEmoji[result.status]} ${result.status}
|
|
82
82
|
- **Description**: ${statusDescription[result.status]}
|
|
83
83
|
- **Scope**: ${result.scope}
|
|
84
|
-
- **Expires**: ${result.
|
|
84
|
+
- **Expires**: ${result.expiresIn == null ? 'No expiration date' : result.expiresIn === 0 ? 'Expired - rotate with POST /oauth2/tokens/{token}/rotate' : `${result.expiresIn} seconds (~${Math.floor(result.expiresIn / 86400)} days)`}
|
|
85
85
|
|
|
86
|
-
${result.status === 'GRANTED' ?
|
|
86
|
+
${result.status === 'GRANTED' && result.expiresIn === 0 ?
|
|
87
|
+
'⌛ **Token has expired - rotate it with POST /oauth2/tokens/{token}/rotate (no merchant consent needed)**' :
|
|
88
|
+
result.status === 'GRANTED' ?
|
|
87
89
|
'✅ **Token is ready for use in API requests**' :
|
|
88
90
|
result.status === 'PENDING' ?
|
|
89
91
|
'⏳ **Token is pending - wait for merchant approval**' :
|
|
90
|
-
'❌ **Token is revoked
|
|
92
|
+
'❌ **Token is revoked, and revocation is final.** Requesting a token again returns this same revoked token; the merchant must consent again through an AUTHORIZATION_ONLY invitation (payware_isv_create_invitation)'
|
|
91
93
|
}
|
|
92
94
|
|
|
93
95
|
📋 **Usage Notes:**
|
|
@@ -12,7 +12,10 @@ export const obtainTokenTool = {
|
|
|
12
12
|
ISVs must be authorized by payware registered merchants to make requests on their behalf.
|
|
13
13
|
This endpoint allows ISVs to request a token using the merchant's credentials.
|
|
14
14
|
|
|
15
|
-
IMPORTANT:
|
|
15
|
+
IMPORTANT: A token requested this way expires 180 days after the merchant grants authorization.
|
|
16
|
+
expiresIn in the response gives the seconds remaining (null = no expiration date). Rotate it with
|
|
17
|
+
POST /oauth2/tokens/{token}/rotate, before or after expiry - no merchant consent is needed.
|
|
18
|
+
Requesting again for the same merchant returns the existing token rather than a second one.
|
|
16
19
|
Store tokens securely using strong encryption.
|
|
17
20
|
|
|
18
21
|
Endpoint: POST /oauth2/tokens
|
|
@@ -99,7 +102,7 @@ Auth: Requires ISV JWT token with aud: "https://payware.eu"`,
|
|
|
99
102
|
- **Token Type**: ${result.tokenType}
|
|
100
103
|
- **Status**: ${result.status}
|
|
101
104
|
- **Scope**: ${result.scope}
|
|
102
|
-
- **Expires**: ${result.expiresIn
|
|
105
|
+
- **Expires**: ${result.expiresIn == null ? 'No expiration date' : result.expiresIn === 0 ? 'Expired - rotate with POST /oauth2/tokens/{token}/rotate' : `${result.expiresIn} seconds (~${Math.floor(result.expiresIn / 86400)} days)`}
|
|
103
106
|
|
|
104
107
|
📋 **Copy this token for next operations:**
|
|
105
108
|
\`${result.accessToken}\`
|
|
@@ -110,7 +113,7 @@ Auth: Requires ISV JWT token with aud: "https://payware.eu"`,
|
|
|
110
113
|
- Use this token in the "sub" claim of future JWT requests
|
|
111
114
|
|
|
112
115
|
📋 **Next Steps:**
|
|
113
|
-
1. Check token status: Use token \`${result.accessToken}\` with
|
|
116
|
+
1. Check token status: Use token \`${result.accessToken}\` with payware_authorization_oauth2_get_token_info
|
|
114
117
|
2. Wait for merchant approval (status will change from PENDING to GRANTED)
|
|
115
118
|
3. Use this token in JWT "sub" claim for API requests to merchant data
|
|
116
119
|
4. Set JWT "aud" claim to the merchant's partnerId (${finalClientId}) for API calls`
|
|
@@ -191,7 +194,6 @@ ${JSON.stringify(errorDetails, null, 2)}
|
|
|
191
194
|
- Verify merchant partnerId (clientId) is correct
|
|
192
195
|
- Ensure merchant secret is properly base64 encoded
|
|
193
196
|
- Check if merchant account is active
|
|
194
|
-
- Confirm no existing token for this merchant
|
|
195
197
|
- Verify ISV JWT token is properly signed for OAuth2 requests`
|
|
196
198
|
}]
|
|
197
199
|
};
|
|
@@ -54,24 +54,30 @@ Use this when you need a token but aren't sure if one exists or what it is.`,
|
|
|
54
54
|
text: `📋 **OAuth2 Token Management Help**
|
|
55
55
|
|
|
56
56
|
🔑 **About OAuth2 Tokens:**
|
|
57
|
-
-
|
|
58
|
-
- Tokens
|
|
59
|
-
-
|
|
57
|
+
- Requesting a token again for the same merchant returns the existing one, not a second one
|
|
58
|
+
- Tokens requested this way expire 180 days after the merchant grants authorization
|
|
59
|
+
- Rotate with POST /oauth2/tokens/{token}/rotate - works on expired tokens too, no merchant consent needed
|
|
60
|
+
- Token status: PENDING → GRANTED (after merchant approval); REVOKED is final
|
|
60
61
|
|
|
61
62
|
🎯 **To get your token:**
|
|
62
63
|
|
|
63
|
-
**Option 1:
|
|
64
|
-
Use: \`
|
|
65
|
-
-
|
|
64
|
+
**Option 1: Request a token**
|
|
65
|
+
Use: \`payware_authorization_oauth2_obtain_token\`
|
|
66
|
+
- Returns a new PENDING token, or the existing token if this merchant already has one
|
|
66
67
|
- On failure the server names the reason (e.g. client not found, client disabled)
|
|
67
68
|
|
|
68
69
|
**Option 2: Check a known token**
|
|
69
|
-
Use: \`
|
|
70
|
-
- Shows current status (PENDING/GRANTED/REVOKED)
|
|
70
|
+
Use: \`payware_authorization_oauth2_get_token_info\` with your saved token
|
|
71
|
+
- Shows current status (PENDING/GRANTED/REVOKED) and expiry
|
|
71
72
|
|
|
72
|
-
**Option 3:
|
|
73
|
-
|
|
74
|
-
|
|
73
|
+
**Option 3: If you lost the token**
|
|
74
|
+
Nothing needs revoking - the token can be retrieved:
|
|
75
|
+
- Request it again with Option 1, which returns the existing token, or
|
|
76
|
+
- Call GET /oauth2/tokens, which lists every token issued to your ISV with its value and status
|
|
77
|
+
|
|
78
|
+
⚠️ **Never ask the merchant to revoke a token to get a new one.** Revocation is final: requesting
|
|
79
|
+
again afterwards returns the same REVOKED token, and the only way back is a fresh consent through an
|
|
80
|
+
AUTHORIZATION_ONLY invitation (\`payware_isv_create_invitation\`). To replace a token value, rotate it.
|
|
75
81
|
|
|
76
82
|
🔐 **Security:**
|
|
77
83
|
- Always store tokens securely
|
|
@@ -94,25 +100,19 @@ Use: \`payware_oauth2_get_token_info\` with your saved token
|
|
|
94
100
|
type: 'text',
|
|
95
101
|
text: `⚠️ **Token Already Exists for This Merchant**
|
|
96
102
|
|
|
97
|
-
🔍 **
|
|
103
|
+
🔍 **A token already exists for this merchant, and it can be retrieved.**
|
|
98
104
|
|
|
99
105
|
📋 **To find your token:**
|
|
100
106
|
|
|
101
|
-
**Option 1:
|
|
102
|
-
-
|
|
103
|
-
- Environment variables file
|
|
104
|
-
- Secure configuration management
|
|
105
|
-
- Password manager
|
|
107
|
+
**Option 1: List your tokens**
|
|
108
|
+
Call GET /oauth2/tokens - it lists every token issued to your ISV with its value and status.
|
|
106
109
|
|
|
107
110
|
**Option 2: If you have the token value**
|
|
108
|
-
Use: \`
|
|
109
|
-
|
|
110
|
-
**Option 3: Create new token**
|
|
111
|
-
1. Contact the merchant (Partner ID: ${process.env.PAYWARE_OAUTH_CLIENT_ID || 'not configured'})
|
|
112
|
-
2. Ask them to revoke the existing token
|
|
113
|
-
3. Then use \`payware_oauth2_obtain_token\` to create a new one
|
|
111
|
+
Use: \`payware_authorization_oauth2_get_token_info\` with your token to check its status
|
|
114
112
|
|
|
115
|
-
|
|
113
|
+
**Option 3: Need a different value?**
|
|
114
|
+
Rotate it with POST /oauth2/tokens/{token}/rotate. Do not ask the merchant to revoke it - revocation
|
|
115
|
+
is final, and requesting again afterwards returns the same REVOKED token.
|
|
116
116
|
|
|
117
117
|
📋 **For immediate help:**
|
|
118
118
|
Run this tool with action='help' for more detailed guidance.`
|
|
@@ -138,8 +138,8 @@ ${token}
|
|
|
138
138
|
\`\`\`
|
|
139
139
|
|
|
140
140
|
🔄 **Next Steps:**
|
|
141
|
-
1. **SAVE THIS TOKEN** securely (you
|
|
142
|
-
2. Check status: \`
|
|
141
|
+
1. **SAVE THIS TOKEN** securely (if you lose it, GET /oauth2/tokens lists it again)
|
|
142
|
+
2. Check status: \`payware_authorization_oauth2_get_token_info\` with token: \`${token}\`
|
|
143
143
|
3. Wait for merchant approval (status will change to GRANTED)
|
|
144
144
|
4. Use token in API requests once GRANTED`
|
|
145
145
|
}]
|
|
@@ -61,9 +61,14 @@ export const cancelPOIPriceTool = {
|
|
|
61
61
|
|
|
62
62
|
**ISV Authentication:** Uses ISV JWT with merchant partner ID and OAuth2 token.
|
|
63
63
|
**Endpoint:** DELETE /poi/{poiId}/price
|
|
64
|
-
**Use Case:** Cancel a price that was set but
|
|
64
|
+
**Use Case:** Cancel a price that was set but not yet scanned (e.g., order cancelled).
|
|
65
65
|
|
|
66
|
-
The POI
|
|
66
|
+
The POI returns to IDLE and is ready for a new price.
|
|
67
|
+
|
|
68
|
+
**Only before the scan.** The scan is the customer's bank processing the payment, so from then on a POI
|
|
69
|
+
sale cannot be cancelled by the merchant or an ISV, on any plan: this answers 409
|
|
70
|
+
ERR_POI_NO_PENDING_PAYMENT, and cancelling the transaction itself answers 409 ERR_ALREADY_PROCESSED. The
|
|
71
|
+
sale completes or expires; the POI accepts the next price meanwhile.
|
|
67
72
|
|
|
68
73
|
**Required:** POI ID, Merchant Partner ID, and OAuth2 token.`,
|
|
69
74
|
|
package/src/isv/poi/get-poi.js
CHANGED
|
@@ -115,8 +115,7 @@ export const getPOITool = {
|
|
|
115
115
|
const statusEmoji = {
|
|
116
116
|
'IDLE': '🟢',
|
|
117
117
|
'READY': '🟡',
|
|
118
|
-
'BUSY': '🔴'
|
|
119
|
-
'DISABLED': '⚫'
|
|
118
|
+
'BUSY': '🔴'
|
|
120
119
|
}[poi.status] || '⚪';
|
|
121
120
|
|
|
122
121
|
return {
|
|
@@ -137,7 +136,7 @@ export const getPOITool = {
|
|
|
137
136
|
- Linked vPOS: ${poi.linkedVposCount || 0} terminals
|
|
138
137
|
|
|
139
138
|
**Current Session:**
|
|
140
|
-
${poi.status
|
|
139
|
+
${poi.status === 'READY' && poi.pendingAmount ? `- Amount: ${poi.pendingAmount} ${poi.pendingCurrency}
|
|
141
140
|
- Expires: ${poi.sessionExpiresAt || 'N/A'}
|
|
142
141
|
- Token: ${poi.sessionToken || 'N/A'}` : '- No active session'}
|
|
143
142
|
|
|
@@ -1,168 +1,194 @@
|
|
|
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
|
-
import { apiErrorResult } from '../../shared/api-errors.js';
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Get POI status via ISV authentication
|
|
8
|
-
* @param {Object} params - Parameters for status request
|
|
9
|
-
* @returns {Object} POI status response
|
|
10
|
-
*/
|
|
11
|
-
export async function getPOIStatus({ poiId, merchantPartnerId, oauth2Token, useSandbox = true }) {
|
|
12
|
-
if (!poiId) {
|
|
13
|
-
throw new Error('POI ID is required');
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
if (!merchantPartnerId) {
|
|
17
|
-
throw new Error('Merchant Partner ID is required for ISV operations');
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
if (!oauth2Token) {
|
|
21
|
-
throw new Error('OAuth2 token is required for ISV operations');
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
const isvPartnerId = getPartnerIdSafe();
|
|
25
|
-
const privateKey = getPrivateKeySafe(useSandbox);
|
|
26
|
-
|
|
27
|
-
const jwtData = await createJWTForPartner({
|
|
28
|
-
partnerId: isvPartnerId,
|
|
29
|
-
privateKey,
|
|
30
|
-
requestBody: null,
|
|
31
|
-
merchantId: merchantPartnerId,
|
|
32
|
-
oauth2Token
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
const headers = {
|
|
36
|
-
'Authorization': `Bearer ${jwtData.token}`,
|
|
37
|
-
'Content-Type': 'application/json',
|
|
38
|
-
'Api-Version': '1'
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
try {
|
|
42
|
-
const baseUrl = useSandbox ? getSandboxUrl() : getProductionUrl();
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
**
|
|
68
|
-
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
${
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
**
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
}
|
|
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
|
+
import { apiErrorResult } from '../../shared/api-errors.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Get POI status via ISV authentication
|
|
8
|
+
* @param {Object} params - Parameters for status request
|
|
9
|
+
* @returns {Object} POI status response
|
|
10
|
+
*/
|
|
11
|
+
export async function getPOIStatus({ poiId, sessionToken, merchantPartnerId, oauth2Token, useSandbox = true }) {
|
|
12
|
+
if (!poiId) {
|
|
13
|
+
throw new Error('POI ID is required');
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
if (!merchantPartnerId) {
|
|
17
|
+
throw new Error('Merchant Partner ID is required for ISV operations');
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
if (!oauth2Token) {
|
|
21
|
+
throw new Error('OAuth2 token is required for ISV operations');
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const isvPartnerId = getPartnerIdSafe();
|
|
25
|
+
const privateKey = getPrivateKeySafe(useSandbox);
|
|
26
|
+
|
|
27
|
+
const jwtData = await createJWTForPartner({
|
|
28
|
+
partnerId: isvPartnerId,
|
|
29
|
+
privateKey,
|
|
30
|
+
requestBody: null,
|
|
31
|
+
merchantId: merchantPartnerId,
|
|
32
|
+
oauth2Token
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
const headers = {
|
|
36
|
+
'Authorization': `Bearer ${jwtData.token}`,
|
|
37
|
+
'Content-Type': 'application/json',
|
|
38
|
+
'Api-Version': '1'
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
try {
|
|
42
|
+
const baseUrl = useSandbox ? getSandboxUrl() : getProductionUrl();
|
|
43
|
+
// The session token pins one sale: with it the response also carries that session's
|
|
44
|
+
// transactionId and transactionStatus. A GET is signed without a body, so the query string is free.
|
|
45
|
+
const query = sessionToken ? `?sessionToken=${encodeURIComponent(sessionToken)}` : '';
|
|
46
|
+
const response = await axios.get(`${baseUrl}/poi/${poiId}/status${query}`, { headers });
|
|
47
|
+
|
|
48
|
+
return {
|
|
49
|
+
success: true,
|
|
50
|
+
status: response.data,
|
|
51
|
+
requestId: response.headers['x-request-id'],
|
|
52
|
+
timestamp: new Date().toISOString()
|
|
53
|
+
};
|
|
54
|
+
} catch (error) {
|
|
55
|
+
return apiErrorResult(error);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Get POI status tool implementation for ISV
|
|
61
|
+
*/
|
|
62
|
+
export const getPOIStatusTool = {
|
|
63
|
+
name: "payware_poi_get_status",
|
|
64
|
+
description: `Get the current status of a POI and the outcome of its sales.
|
|
65
|
+
|
|
66
|
+
**ISV Authentication:** Uses ISV JWT with merchant partner ID and OAuth2 token.
|
|
67
|
+
**Endpoint:** GET /poi/{poiId}/status[?sessionToken=...]
|
|
68
|
+
**Use Case:** Follow a sale to its outcome - the backup to callbacks, and on Basic (no transaction
|
|
69
|
+
endpoints) the only way when a callback is lost or there is no backend.
|
|
70
|
+
|
|
71
|
+
**POI States:**
|
|
72
|
+
- IDLE: no pending price and no sale running
|
|
73
|
+
- READY: price set, waiting for a scan (wins over BUSY: the POI can take the next price while a sale runs)
|
|
74
|
+
- BUSY: a customer scanned and the sale is running
|
|
75
|
+
|
|
76
|
+
**Every response** carries lastTransactionId / lastTransactionStatus - the POI's most recent sale, running
|
|
77
|
+
(ACTIVE) or finished (CONFIRMED, DECLINED, FAILED, CANCELLED, EXPIRED). Once the POI is back to IDLE,
|
|
78
|
+
lastTransactionStatus is how the last sale ended.
|
|
79
|
+
|
|
80
|
+
**With sessionToken** (from payware_poi_set_price) the response also carries transactionId /
|
|
81
|
+
transactionStatus for exactly that sale - use it when the POI may be re-armed before the previous sale
|
|
82
|
+
finishes. Poll every 2-3 seconds and stop at a final status; the read is rate limited.
|
|
83
|
+
|
|
84
|
+
**Required:** POI ID, Merchant Partner ID, and OAuth2 token.`,
|
|
85
|
+
|
|
86
|
+
inputSchema: {
|
|
87
|
+
type: "object",
|
|
88
|
+
required: ["poiId", "merchantPartnerId", "oauth2Token"],
|
|
89
|
+
properties: {
|
|
90
|
+
poiId: {
|
|
91
|
+
type: "string",
|
|
92
|
+
description: "The POI identifier (format: pi + 8 alphanumeric chars, e.g., piABC12345)"
|
|
93
|
+
},
|
|
94
|
+
sessionToken: {
|
|
95
|
+
type: "string",
|
|
96
|
+
description: "Optional. The sessionToken returned by payware_poi_set_price; returns that sale's transaction and status"
|
|
97
|
+
},
|
|
98
|
+
merchantPartnerId: {
|
|
99
|
+
type: "string",
|
|
100
|
+
description: "Partner ID of the target merchant (8 alphanumeric characters)"
|
|
101
|
+
},
|
|
102
|
+
oauth2Token: {
|
|
103
|
+
type: "string",
|
|
104
|
+
description: "OAuth2 access token obtained from the merchant"
|
|
105
|
+
},
|
|
106
|
+
useSandbox: {
|
|
107
|
+
type: "boolean",
|
|
108
|
+
description: "Use sandbox environment for testing",
|
|
109
|
+
default: true
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
},
|
|
113
|
+
|
|
114
|
+
async handler(args) {
|
|
115
|
+
const { poiId, sessionToken, merchantPartnerId, oauth2Token, useSandbox = true } = args;
|
|
116
|
+
|
|
117
|
+
if (!poiId) {
|
|
118
|
+
throw new Error("POI ID is required");
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (!merchantPartnerId) {
|
|
122
|
+
throw new Error("Merchant Partner ID is required");
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if (!oauth2Token) {
|
|
126
|
+
throw new Error("OAuth2 token is required");
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const result = await getPOIStatus({ poiId, sessionToken, merchantPartnerId, oauth2Token, useSandbox });
|
|
130
|
+
|
|
131
|
+
if (result.success) {
|
|
132
|
+
const status = result.status;
|
|
133
|
+
const statusEmoji = {
|
|
134
|
+
'IDLE': '🟢',
|
|
135
|
+
'READY': '🟡',
|
|
136
|
+
'BUSY': '🔴'
|
|
137
|
+
}[status.status] || '⚪';
|
|
138
|
+
const meaning = {
|
|
139
|
+
'IDLE': status.lastTransactionId
|
|
140
|
+
? `No sale running. The last sale ended ${status.lastTransactionStatus}.`
|
|
141
|
+
: 'No sale running, and the POI has not had a sale yet.',
|
|
142
|
+
'READY': 'Price set, waiting for a customer to scan.',
|
|
143
|
+
'BUSY': 'A customer scanned and the sale is running.'
|
|
144
|
+
}[status.status] || '';
|
|
145
|
+
|
|
146
|
+
const pendingInfo = status.status === 'READY' ? `
|
|
147
|
+
**Pending Price:**
|
|
148
|
+
- Amount: ${status.pendingAmount} ${status.pendingCurrency}
|
|
149
|
+
- Session expires: ${status.sessionExpiresAt || 'N/A'}` : '';
|
|
150
|
+
|
|
151
|
+
const lastSale = status.lastTransactionId ? `
|
|
152
|
+
**Last Sale:** ${status.lastTransactionId} - ${status.lastTransactionStatus}` : '';
|
|
153
|
+
|
|
154
|
+
const pinnedSale = sessionToken ? `
|
|
155
|
+
**Sale for this session token:** ${status.transactionId
|
|
156
|
+
? `${status.transactionId} - ${status.transactionStatus}`
|
|
157
|
+
: 'not scanned yet (or the token does not belong to this POI)'}` : '';
|
|
158
|
+
|
|
159
|
+
return {
|
|
160
|
+
content: [{
|
|
161
|
+
type: "text",
|
|
162
|
+
text: `${statusEmoji} **POI Status: ${status.status}**
|
|
163
|
+
|
|
164
|
+
**POI ID:** ${status.poiId}
|
|
165
|
+
**ISV -> Merchant:** ${getPartnerIdSafe()} -> ${merchantPartnerId}
|
|
166
|
+
${pendingInfo}${lastSale}${pinnedSale}
|
|
167
|
+
|
|
168
|
+
**Meaning:** ${meaning}
|
|
169
|
+
|
|
170
|
+
**Request ID:** ${result.requestId || 'N/A'}
|
|
171
|
+
**Timestamp:** ${result.timestamp}`
|
|
172
|
+
}]
|
|
173
|
+
};
|
|
174
|
+
} else {
|
|
175
|
+
return {
|
|
176
|
+
content: [{
|
|
177
|
+
type: "text",
|
|
178
|
+
text: `❌ **Failed to Get POI Status**
|
|
179
|
+
|
|
180
|
+
**POI ID:** ${poiId}
|
|
181
|
+
**Error:** ${result.error.message}
|
|
182
|
+
**Status:** ${result.error.status || 'N/A'}
|
|
183
|
+
|
|
184
|
+
**Common Issues:**
|
|
185
|
+
- ERR_INVALID_POI_ID (400): POI ID format invalid (must be pi + 8 alphanumeric chars)
|
|
186
|
+
- ERR_POI_NOT_FOUND (404): POI doesn't exist, or is disabled
|
|
187
|
+
- ERR_SHOP_NOT_IN_SCOPE (403): the POI's shop is not assigned to you
|
|
188
|
+
|
|
189
|
+
**Timestamp:** ${result.timestamp}`
|
|
190
|
+
}]
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
};
|
package/src/isv/poi/list-pois.js
CHANGED
|
@@ -64,7 +64,7 @@ export const listPOIsTool = {
|
|
|
64
64
|
**Endpoint:** GET /poi
|
|
65
65
|
**Use Case:** Retrieve all physical payment points configured for a merchant.
|
|
66
66
|
|
|
67
|
-
POI states: IDLE (
|
|
67
|
+
POI states: IDLE (no price, no sale running), READY (price set, waiting for a scan), BUSY (scanned, sale running). A disabled POI is not a state: it shows isActive false.
|
|
68
68
|
|
|
69
69
|
**Required:** Merchant Partner ID and OAuth2 token for ISV authentication.`,
|
|
70
70
|
|
|
@@ -106,13 +106,12 @@ POI states: IDLE (ready), READY (price set), BUSY (payment in progress), DISABLE
|
|
|
106
106
|
const statusEmoji = {
|
|
107
107
|
'IDLE': '🟢',
|
|
108
108
|
'READY': '🟡',
|
|
109
|
-
'BUSY': '🔴'
|
|
110
|
-
'DISABLED': '⚫'
|
|
109
|
+
'BUSY': '🔴'
|
|
111
110
|
}[poi.status] || '⚪';
|
|
112
111
|
|
|
113
112
|
return `${statusEmoji} **${poi.name}** (${poi.poiId})
|
|
114
113
|
Shop: ${poi.shopName || poi.shopCode}
|
|
115
|
-
Status: ${poi.status}${poi.pendingAmount && poi.pendingAmount !== '0.00' ? ` | Pending: ${poi.pendingAmount} ${poi.pendingCurrency}` : ''}`;
|
|
114
|
+
Status: ${poi.status}${poi.isActive === false ? ' (disabled)' : ''}${poi.pendingAmount && poi.pendingAmount !== '0.00' ? ` | Pending: ${poi.pendingAmount} ${poi.pendingCurrency}` : ''}`;
|
|
116
115
|
}).join('\n\n');
|
|
117
116
|
|
|
118
117
|
return {
|
|
@@ -127,8 +126,7 @@ ${poisList || 'No POIs found for this merchant.'}
|
|
|
127
126
|
**Status Legend:**
|
|
128
127
|
🟢 IDLE - Ready for new payment
|
|
129
128
|
🟡 READY - Price set, waiting for customer
|
|
130
|
-
🔴 BUSY -
|
|
131
|
-
⚫ DISABLED - Not accepting payments
|
|
129
|
+
🔴 BUSY - Scanned, sale running (payware_poi_get_status shows its outcome)
|
|
132
130
|
|
|
133
131
|
**Request ID:** ${result.requestId || 'N/A'}
|
|
134
132
|
**Timestamp:** ${result.timestamp}`
|