@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/src/isv/poi/set-price.js
CHANGED
|
@@ -8,7 +8,7 @@ import { apiErrorResult } from '../../shared/api-errors.js';
|
|
|
8
8
|
* @param {Object} params - Parameters for set price request
|
|
9
9
|
* @returns {Object} Set price response
|
|
10
10
|
*/
|
|
11
|
-
export async function setPOIPrice({ poiId, amount, currency,
|
|
11
|
+
export async function setPOIPrice({ poiId, amount, currency, ttlSeconds, merchantPartnerId, oauth2Token, useSandbox = true }) {
|
|
12
12
|
if (!poiId) {
|
|
13
13
|
throw new Error('POI ID is required');
|
|
14
14
|
}
|
|
@@ -21,10 +21,6 @@ export async function setPOIPrice({ poiId, amount, currency, reasonL1, reasonL2,
|
|
|
21
21
|
throw new Error('Currency is required');
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
if (!reasonL1) {
|
|
25
|
-
throw new Error('Reason (reasonL1) is required');
|
|
26
|
-
}
|
|
27
|
-
|
|
28
24
|
if (!merchantPartnerId) {
|
|
29
25
|
throw new Error('Merchant Partner ID is required for ISV operations');
|
|
30
26
|
}
|
|
@@ -36,17 +32,16 @@ export async function setPOIPrice({ poiId, amount, currency, reasonL1, reasonL2,
|
|
|
36
32
|
const isvPartnerId = getPartnerIdSafe();
|
|
37
33
|
const privateKey = getPrivateKeySafe(useSandbox);
|
|
38
34
|
|
|
39
|
-
//
|
|
35
|
+
// amount, currency and ttlSeconds are the only fields the server reads (POIPriceRequest); anything
|
|
36
|
+
// else is ignored without an error. This used to send reasonL1 (and demand it), reasonL2,
|
|
37
|
+
// timeToLive, callbackUrl and passbackParams - none of which the server has - so a caller relying
|
|
38
|
+
// on the per-price callbackUrl got no callbacks at all. The callback URL belongs to the POI.
|
|
40
39
|
const requestBody = {
|
|
41
40
|
amount: String(amount),
|
|
42
|
-
currency: currency.toUpperCase()
|
|
43
|
-
reasonL1
|
|
41
|
+
currency: currency.toUpperCase()
|
|
44
42
|
};
|
|
45
43
|
|
|
46
|
-
if (
|
|
47
|
-
if (timeToLive) requestBody.timeToLive = timeToLive;
|
|
48
|
-
if (callbackUrl) requestBody.callbackUrl = callbackUrl;
|
|
49
|
-
if (passbackParams) requestBody.passbackParams = passbackParams;
|
|
44
|
+
if (ttlSeconds) requestBody.ttlSeconds = ttlSeconds;
|
|
50
45
|
|
|
51
46
|
const jwtData = await createJWTForPartner({
|
|
52
47
|
partnerId: isvPartnerId,
|
|
@@ -88,13 +83,21 @@ export const setPOIPriceTool = {
|
|
|
88
83
|
**Endpoint:** PUT /poi/{poiId}/price
|
|
89
84
|
**Use Case:** Set the amount a customer should pay when they scan the POI.
|
|
90
85
|
|
|
91
|
-
The POI
|
|
86
|
+
The POI moves to READY and waits for a scan until the TTL expires. The scan creates the transaction and
|
|
87
|
+
the POI is BUSY until the sale is final.
|
|
88
|
+
|
|
89
|
+
**Only amount, currency and ttlSeconds are accepted.** There is no description, callback URL or passback
|
|
90
|
+
field on a price - the callback URL is set on the POI when it is created, and a POI without one sends no
|
|
91
|
+
callbacks.
|
|
92
|
+
|
|
93
|
+
**Keep the returned sessionToken** until the sale has a final status: pass it to
|
|
94
|
+
payware_poi_get_status to read that sale's transaction and outcome.
|
|
92
95
|
|
|
93
|
-
**Required:** POI ID, amount, currency,
|
|
96
|
+
**Required:** POI ID, amount, currency, Merchant Partner ID, and OAuth2 token.`,
|
|
94
97
|
|
|
95
98
|
inputSchema: {
|
|
96
99
|
type: "object",
|
|
97
|
-
required: ["poiId", "amount", "currency", "
|
|
100
|
+
required: ["poiId", "amount", "currency", "merchantPartnerId", "oauth2Token"],
|
|
98
101
|
properties: {
|
|
99
102
|
poiId: {
|
|
100
103
|
type: "string",
|
|
@@ -102,31 +105,15 @@ The POI will transition to READY state and wait for a customer scan until the TT
|
|
|
102
105
|
},
|
|
103
106
|
amount: {
|
|
104
107
|
type: "string",
|
|
105
|
-
description: "Payment amount (e.g., '25.50')"
|
|
108
|
+
description: "Payment amount (e.g., '25.50'), positive and no finer than the currency's smallest unit"
|
|
106
109
|
},
|
|
107
110
|
currency: {
|
|
108
111
|
type: "string",
|
|
109
112
|
description: "ISO 4217 currency code (e.g., 'EUR', 'USD', 'GBP')"
|
|
110
113
|
},
|
|
111
|
-
|
|
112
|
-
type: "string",
|
|
113
|
-
description: "Payment description (e.g., 'Table 5', 'Order #123')"
|
|
114
|
-
},
|
|
115
|
-
reasonL2: {
|
|
116
|
-
type: "string",
|
|
117
|
-
description: "Additional description (optional)"
|
|
118
|
-
},
|
|
119
|
-
timeToLive: {
|
|
114
|
+
ttlSeconds: {
|
|
120
115
|
type: "integer",
|
|
121
|
-
description: "Seconds until price expires (60-600
|
|
122
|
-
},
|
|
123
|
-
callbackUrl: {
|
|
124
|
-
type: "string",
|
|
125
|
-
description: "Override callback URL for this payment (optional)"
|
|
126
|
-
},
|
|
127
|
-
passbackParams: {
|
|
128
|
-
type: "string",
|
|
129
|
-
description: "Data to pass back in callback (optional, max 200 chars)"
|
|
116
|
+
description: "Seconds until the price expires if nobody scans it (60-600). Defaults to the POI's own ttlSeconds."
|
|
130
117
|
},
|
|
131
118
|
merchantPartnerId: {
|
|
132
119
|
type: "string",
|
|
@@ -145,18 +132,16 @@ The POI will transition to READY state and wait for a customer scan until the TT
|
|
|
145
132
|
},
|
|
146
133
|
|
|
147
134
|
async handler(args) {
|
|
148
|
-
const { poiId, amount, currency,
|
|
135
|
+
const { poiId, amount, currency, ttlSeconds, merchantPartnerId, oauth2Token, useSandbox = true } = args;
|
|
149
136
|
|
|
150
137
|
if (!poiId) throw new Error("POI ID is required");
|
|
151
138
|
if (!amount) throw new Error("Amount is required");
|
|
152
139
|
if (!currency) throw new Error("Currency is required");
|
|
153
|
-
if (!reasonL1) throw new Error("Reason (reasonL1) is required");
|
|
154
140
|
if (!merchantPartnerId) throw new Error("Merchant Partner ID is required");
|
|
155
141
|
if (!oauth2Token) throw new Error("OAuth2 token is required");
|
|
156
142
|
|
|
157
143
|
const result = await setPOIPrice({
|
|
158
|
-
poiId, amount, currency,
|
|
159
|
-
merchantPartnerId, oauth2Token, useSandbox
|
|
144
|
+
poiId, amount, currency, ttlSeconds, merchantPartnerId, oauth2Token, useSandbox
|
|
160
145
|
});
|
|
161
146
|
|
|
162
147
|
if (result.success) {
|
|
@@ -169,19 +154,17 @@ The POI will transition to READY state and wait for a customer scan until the TT
|
|
|
169
154
|
|
|
170
155
|
**POI ID:** ${data.poiId || poiId}
|
|
171
156
|
**Status:** 🟡 ${data.status || 'READY'}
|
|
172
|
-
|
|
173
|
-
**Payment Details:**
|
|
174
|
-
- Amount: **${data.amount || amount} ${data.currency || currency}**
|
|
175
|
-
- Reason: ${reasonL1}${reasonL2 ? ` - ${reasonL2}` : ''}
|
|
157
|
+
**Amount:** ${amount} ${currency.toUpperCase()}
|
|
176
158
|
|
|
177
159
|
**Session:**
|
|
178
|
-
- Token: ${data.sessionToken || 'N/A'}
|
|
179
|
-
-
|
|
160
|
+
- Token: ${data.sessionToken || 'N/A'} - keep it until the sale is final
|
|
161
|
+
- Price expires if not scanned: ${data.expiresAt || 'N/A'}
|
|
180
162
|
|
|
181
163
|
**Next Steps:**
|
|
182
|
-
1.
|
|
183
|
-
2.
|
|
184
|
-
3.
|
|
164
|
+
1. The customer scans the POI (QR code, NFC tag or BLE); the transaction is created and the POI is BUSY
|
|
165
|
+
2. Outcome by callback, if the POI has a callbackUrl: poi.scanned with the transaction id, then TRANSACTION_FINALIZED with the final status (both carry the poiId)
|
|
166
|
+
3. Or poll payware_poi_get_status every 2-3 seconds with this sessionToken until transactionStatus is final
|
|
167
|
+
4. To cancel, use payware_poi_cancel_price - possible only before the scan
|
|
185
168
|
|
|
186
169
|
**ISV -> Merchant:** ${getPartnerIdSafe()} -> ${merchantPartnerId}
|
|
187
170
|
**Request ID:** ${result.requestId || 'N/A'}
|
|
@@ -202,12 +185,14 @@ The POI will transition to READY state and wait for a customer scan until the TT
|
|
|
202
185
|
**Status:** ${result.error.status || 'N/A'}
|
|
203
186
|
|
|
204
187
|
**Common Issues:**
|
|
205
|
-
-
|
|
206
|
-
-
|
|
207
|
-
-
|
|
208
|
-
-
|
|
209
|
-
-
|
|
210
|
-
-
|
|
188
|
+
- ERR_INVALID_POI_ID (400): POI ID format invalid (must be pi + 8 alphanumeric chars)
|
|
189
|
+
- ERR_POI_NOT_FOUND (404): POI doesn't exist or doesn't belong to the merchant
|
|
190
|
+
- ERR_SHOP_NOT_IN_SCOPE (403): the POI's shop is not assigned to you
|
|
191
|
+
- ERR_POI_DISABLED (409): POI is disabled
|
|
192
|
+
- ERR_POI_ALREADY_HAS_PENDING_PRICE (409): POI already has a pending price (cancel it first)
|
|
193
|
+
- ERR_INVALID_AMOUNT (400): amount finer than the currency's smallest unit
|
|
194
|
+
- ERR_MISSING_CURRENCY (400): currency missing or unknown
|
|
195
|
+
- ERR_VALIDATION_FAILED (400): amount missing or not positive, or ttlSeconds outside 60-600
|
|
211
196
|
|
|
212
197
|
**Timestamp:** ${result.timestamp}`
|
|
213
198
|
}]
|
|
@@ -23,10 +23,12 @@ export const simulateCallbackTool = {
|
|
|
23
23
|
so nothing here reaches the network. What you get back is the exact payload shape a real
|
|
24
24
|
TRANSACTION_FINALIZED callback carries, which is what a handler needs in order to be written.
|
|
25
25
|
|
|
26
|
-
**
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
26
|
+
**Where the callback goes.** A transaction you create on a merchant's behalf calls back to the
|
|
27
|
+
\`callbackUrl\` you give when creating it; a POI sale calls back to the POI's \`callbackUrl\`. Both are
|
|
28
|
+
signed with your ISV callback key, so verify them with your own payware public key. Give no callbackUrl
|
|
29
|
+
and that transaction sends no callbacks at all - the merchant's default URL is not used. Without a backend, follow the
|
|
30
|
+
transaction with \`payware_operations_get_transaction_status\` while it is ACTIVE and then
|
|
31
|
+
\`payware_operations_get_transaction_history\` once, or a POI sale with \`payware_poi_get_status\`.
|
|
30
32
|
|
|
31
33
|
Four properties of the real payload that are easy to get wrong:
|
|
32
34
|
|
|
@@ -38,9 +40,11 @@ Four properties of the real payload that are easy to get wrong:
|
|
|
38
40
|
**3. \`EXPIRED\` is a non-payment, not a failure.** The payer never paid within \`timeToLive\`. Release
|
|
39
41
|
the order or the stock; do not treat it as an error to retry.
|
|
40
42
|
|
|
41
|
-
**4. Deduplicate on \`(transactionId, callbackType)\`.** payware retries
|
|
42
|
-
|
|
43
|
-
guarantees.
|
|
43
|
+
**4. Deduplicate on \`(transactionId, callbackType)\`.** payware retries up to 15 times, backing off
|
|
44
|
+
from about a second to at most 5 minutes between attempts, until the endpoint answers any 2xx. That
|
|
45
|
+
pair is stable across retries and is the key payware guarantees.
|
|
46
|
+
|
|
47
|
+
A POI sale's callback also carries \`poiId\`, the POI that created the transaction.
|
|
44
48
|
|
|
45
49
|
**Not on this payload:** \`deliveryAddress\` (removed 2026-08-07 - fetch it from
|
|
46
50
|
\`payware_operations_get_transaction_history\`), \`producerPartnerId\`, \`transactionType\` and
|
|
@@ -99,6 +103,10 @@ guarantees.
|
|
|
99
103
|
enum: ['A2A', 'CARD_FUNDED', 'BNPL', 'INSTANT_CREDIT'],
|
|
100
104
|
description: 'Payment method chosen by the customer. A2A = direct transfer. CARD_FUNDED = card-linked account. BNPL = buy now pay later. INSTANT_CREDIT = credit line.'
|
|
101
105
|
},
|
|
106
|
+
poiId: {
|
|
107
|
+
type: 'string',
|
|
108
|
+
description: 'Set to simulate a POI sale: the callback then carries the poiId of the POI that created the transaction'
|
|
109
|
+
},
|
|
102
110
|
merchantPartnerId: {
|
|
103
111
|
type: 'string',
|
|
104
112
|
description: 'Partner ID of the merchant whose callback this simulates. Used for labelling only - no call is made on their behalf.'
|
|
@@ -123,6 +131,7 @@ guarantees.
|
|
|
123
131
|
feeRate = '0.0150',
|
|
124
132
|
statusMessage,
|
|
125
133
|
paymentMethod,
|
|
134
|
+
poiId,
|
|
126
135
|
merchantPartnerId
|
|
127
136
|
} = args;
|
|
128
137
|
|
|
@@ -131,7 +140,7 @@ guarantees.
|
|
|
131
140
|
}
|
|
132
141
|
|
|
133
142
|
const payload = generateMockCallback(transactionId, status, {
|
|
134
|
-
amount, currency, fee, feeFixed, feeRate, statusMessage, paymentMethod
|
|
143
|
+
amount, currency, fee, feeFixed, feeRate, statusMessage, paymentMethod, poiId
|
|
135
144
|
});
|
|
136
145
|
|
|
137
146
|
const delivery = callbackUrl ? await simulateCallbackDelivery(callbackUrl, payload) : null;
|
|
@@ -141,7 +150,7 @@ guarantees.
|
|
|
141
150
|
type: 'text',
|
|
142
151
|
text: `🔔 **Simulated Callback Payload**${merchantPartnerId ? ` (ISV ${getPartnerIdSafe()} -> Merchant ${merchantPartnerId})` : ''}
|
|
143
152
|
|
|
144
|
-
This is what payware would POST to the
|
|
153
|
+
This is what payware would POST to the transaction's (or POI's) \`callbackUrl\`. No request was made.
|
|
145
154
|
|
|
146
155
|
\`\`\`json
|
|
147
156
|
${JSON.stringify(payload, null, 2)}
|
|
@@ -153,7 +162,7 @@ ${delivery.note}
|
|
|
153
162
|
` : '**No callbackUrl given** - payload only.'}
|
|
154
163
|
|
|
155
164
|
**Building the handler:**
|
|
156
|
-
1. Answer **
|
|
165
|
+
1. Answer any **2xx** or payware retries - up to 15 times, backing off from about a second to 5 minutes.
|
|
157
166
|
2. Deduplicate on \`(transactionId, callbackType)\`; retries repeat the same pair.
|
|
158
167
|
3. Read properties defensively: absent means absent, never zero.
|
|
159
168
|
4. Treat \`EXPIRED\` as a non-payment, not an error to retry.
|