@payware/mcp-server 1.3.3 → 1.4.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.
Files changed (89) hide show
  1. package/.env.example +4 -0
  2. package/README.md +18 -0
  3. package/package.json +7 -9
  4. package/src/auth/rsa-keys.js +12 -6
  5. package/src/auth/sandbox-setup.js +2 -0
  6. package/src/config/toolsets.js +104 -0
  7. package/src/core/auth/jwt-factory.js +4 -1
  8. package/src/core/auth/jwt-token.js +4 -0
  9. package/src/index.js +10 -6
  10. package/src/isv/data/index.js +15 -14
  11. package/src/isv/deep-links/delete-product-link.js +3 -2
  12. package/src/isv/deep-links/delete-transaction-link.js +3 -2
  13. package/src/isv/deep-links/get-product-link.js +8 -6
  14. package/src/isv/deep-links/get-transaction-link.js +8 -6
  15. package/src/isv/index.js +6 -0
  16. package/src/isv/invitations/index.js +5 -0
  17. package/src/isv/oauth2/get-token-info.js +3 -0
  18. package/src/isv/oauth2/obtain-token.js +3 -0
  19. package/src/isv/oauth2/token-helper.js +2 -0
  20. package/src/isv/poi/cancel-price.js +2 -0
  21. package/src/isv/poi/get-poi.js +2 -0
  22. package/src/isv/poi/get-qrcode.js +40 -9
  23. package/src/isv/poi/get-status.js +2 -0
  24. package/src/isv/poi/list-pois.js +2 -0
  25. package/src/isv/poi/set-price.js +2 -0
  26. package/src/isv/products/audios.js +8 -2
  27. package/src/isv/products/create-product.js +2 -0
  28. package/src/isv/products/delete-product.js +2 -0
  29. package/src/isv/products/get-product-image.js +2 -0
  30. package/src/isv/products/get-product.js +2 -0
  31. package/src/isv/products/list-products.js +2 -0
  32. package/src/isv/products/schedules.js +6 -0
  33. package/src/isv/products/update-product.js +2 -0
  34. package/src/isv/reference/index.js +2 -0
  35. package/src/isv/transactions/cancel-transaction.js +2 -0
  36. package/src/isv/transactions/create-transaction.js +2 -0
  37. package/src/isv/transactions/get-status.js +3 -1
  38. package/src/isv/transactions/process-transaction.js +2 -0
  39. package/src/isv/transactions/simulate-callback.js +2 -0
  40. package/src/isv/transactions/transaction-history.js +2 -0
  41. package/src/merchant/data/export-report.js +2 -0
  42. package/src/merchant/data/generate-report.js +2 -0
  43. package/src/merchant/data/get-export-list.js +2 -0
  44. package/src/merchant/data/get-report-requests.js +2 -0
  45. package/src/merchant/data/get-report-status.js +2 -0
  46. package/src/merchant/data/index.js +15 -10
  47. package/src/merchant/data/list-reports.js +2 -0
  48. package/src/merchant/deep-links/delete-product-link.js +3 -2
  49. package/src/merchant/deep-links/delete-transaction-link.js +3 -2
  50. package/src/merchant/deep-links/get-product-link.js +8 -6
  51. package/src/merchant/deep-links/get-transaction-link.js +8 -6
  52. package/src/merchant/index.js +7 -1
  53. package/src/merchant/logs/index.js +4 -0
  54. package/src/merchant/products/audios.js +15 -14
  55. package/src/merchant/products/create-product.js +3 -2
  56. package/src/merchant/products/delete-product.js +3 -2
  57. package/src/merchant/products/get-product-image.js +3 -2
  58. package/src/merchant/products/get-product.js +3 -2
  59. package/src/merchant/products/list-products.js +3 -2
  60. package/src/merchant/products/schedules.js +11 -10
  61. package/src/merchant/products/update-product.js +3 -2
  62. package/src/merchant/transactions/cancel-transaction.js +3 -2
  63. package/src/merchant/transactions/create-transaction.js +3 -1
  64. package/src/merchant/transactions/get-status.js +4 -3
  65. package/src/merchant/transactions/process-transaction.js +5 -4
  66. package/src/merchant/transactions/simulate-callback.js +3 -0
  67. package/src/merchant/transactions/transaction-history.js +5 -4
  68. package/src/payment-institution/data/index.js +15 -14
  69. package/src/payment-institution/deep-links/delete-transaction-link.js +3 -2
  70. package/src/payment-institution/deep-links/get-transaction-link.js +8 -6
  71. package/src/payment-institution/transactions/create-pi-transaction.js +4 -3
  72. package/src/payment-institution/transactions/finalize-transaction.js +6 -3
  73. package/src/payment-institution/transactions/get-transaction-history.js +7 -4
  74. package/src/payment-institution/transactions/get-transaction-status.js +15 -12
  75. package/src/payment-institution/transactions/process-pi-transaction.js +7 -6
  76. package/src/payment-institution/transactions/simulate-pi-callback.js +3 -0
  77. package/src/payment-institution/transactions/soundbite-transaction.js +5 -4
  78. package/src/proxy-server.js +25 -0
  79. package/src/shared/placements/api.js +51 -0
  80. package/src/shared/placements/tools.js +519 -0
  81. package/src/shared/poi/poi-create-api.js +3 -0
  82. package/src/shared/products/products-api.js +36 -25
  83. package/src/shared/shops/shops-api.js +2 -0
  84. package/src/shared/tool-annotations.js +53 -0
  85. package/src/tools/advanced-code-generator.js +3 -0
  86. package/src/utils/format-request.js +2 -0
  87. package/src/utils/json-formatter.js +2 -0
  88. package/src/utils/server-info.js +2 -0
  89. package/src/utils/code-examples.js +0 -646
@@ -2,6 +2,7 @@ import axios from 'axios';
2
2
  import { createJWTToken } from '../../core/auth/jwt-token.js';
3
3
  import { getSandboxUrl, getProductionUrl, getPartnerIdSafe, getPrivateKeySafe } from '../../config/env.js';
4
4
  import { apiErrorResult } from '../../shared/api-errors.js';
5
+ import { READS } from '../../shared/tool-annotations.js';
5
6
 
6
7
  /**
7
8
  * Get transaction status via payware API (active transactions)
@@ -54,6 +55,7 @@ export async function getPITransactionStatus({
54
55
  */
55
56
  export const getPITransactionStatusTool = {
56
57
  name: "payware_operations_get_transaction_status",
58
+ annotations: READS,
57
59
  description: `Get the current status of an active payware transaction as a financial institution.
58
60
 
59
61
  **Use Cases:**
@@ -96,7 +98,7 @@ Four rules a payer-facing app must follow:
96
98
  4. **A missing field is usually a plan boundary, not an error.** \`imageUrl\` and \`termsUrl\` are
97
99
  Standard and above; \`termsText\` and the descriptions are universal.
98
100
 
99
- For a \`pr\`/\`ps\` identifier the values are resolved live (product -> shop -> merchant fallback, then
101
+ For a \`pr\`, \`pl\` or \`ps\` identifier the values are resolved live (product -> shop -> merchant fallback, then
100
102
  plan gating); for the resulting \`pw\` transaction they are the snapshot frozen at creation, so what
101
103
  you get always reflects what the payer could have been shown at scan time.
102
104
 
@@ -105,14 +107,14 @@ strings at the smallest unit their own currency can be paid in - not always two
105
107
  **the value to echo back unchanged at finalize**; \`feeFixed\` and \`feeRate\` are informational, and
106
108
  recomputing the fee from them is rejected with ERR_FEE_MISMATCH.
107
109
 
108
- **Note:** This endpoint only returns active transactions. Use \`payware_pi_get_transaction_history\` for completed/expired transactions.`,
110
+ **Note:** This endpoint only returns active transactions. Use \`payware_operations_get_transaction_history\` for completed/expired transactions.`,
109
111
  inputSchema: {
110
112
  type: "object",
111
113
  properties: {
112
114
  transactionId: {
113
115
  type: "string",
114
- description: "Transaction ID to check status. Format: 2-char prefix + 8-char ID. Supports: 'pw' (standard), 'pr' (product), 'ps' (soundbite)",
115
- pattern: "^(pw|pr|ps)[0-9A-Za-z]{8}$"
116
+ description: "Transaction ID to check status. Format: 2-char prefix + 8-char ID. Supports: 'pw' (standard), 'pr' (product), 'pl' (placement: pays as its product, and the payment is attributed to the placement), 'ps' (soundbite)",
117
+ pattern: "^(pw|pr|pl|ps)[0-9A-Za-z]{8}$"
116
118
  },
117
119
  partnerId: {
118
120
  type: "string",
@@ -121,8 +123,7 @@ recomputing the fee from them is rejected with ERR_FEE_MISMATCH.
121
123
  },
122
124
  privateKey: {
123
125
  type: "string",
124
- description: "RSA private key for JWT token creation. Uses environment-specific private key as default.",
125
- default: getPrivateKeySafe()
126
+ description: "RSA private key for JWT token creation. Uses environment-specific private key as default."
126
127
  },
127
128
  useSandbox: {
128
129
  type: "boolean",
@@ -159,6 +160,8 @@ recomputing the fee from them is rejected with ERR_FEE_MISMATCH.
159
160
  privateKey,
160
161
  useSandbox
161
162
  });
163
+ // The success text quotes the endpoint. The helper above keeps its own baseUrl, so it needs one here.
164
+ const baseUrl = useSandbox ? getSandboxUrl() : getProductionUrl();
162
165
 
163
166
  if (result.success) {
164
167
  const tx = result.transaction;
@@ -209,8 +212,8 @@ ${JSON.stringify(result.transaction, null, 2)}
209
212
  **Next Steps:**
210
213
  1. ${!tx.payerAccount ? 'Transaction waiting for payer to process' : 'Transaction processed, waiting for finalization'}
211
214
  2. ${tx.timeToLive && parseInt(tx.timeToLive) < 60 ? '⚠️ Transaction expires soon!' : 'Monitor remaining TTL'}
212
- 3. Use \`payware_pi_process_transaction\` if you need to process this transaction
213
- 4. Use \`payware_pi_finalize_transaction\` to confirm/decline after processing
215
+ 3. Use \`payware_operations_process_transaction\` if you need to process this transaction
216
+ 4. Use \`payware_operations_finalize_transaction\` to confirm/decline after processing
214
217
 
215
218
  **⚠️ Transaction State:**
216
219
  - This is an **active** transaction
@@ -240,18 +243,18 @@ ${JSON.stringify(result.error.details || result.error, null, 2)}
240
243
 
241
244
  **Common Issues:**
242
245
  1. **ERR_MISSING_TRANSACTION (404)**: Transaction not found, expired, or already completed
243
- 2. **Invalid Transaction ID**: Check transaction ID format (10 chars: 'pw'/'pr'/'ps' + 8 chars)
246
+ 2. **Invalid Transaction ID**: Check transaction ID format (10 chars: 'pw'/'pr'/'pl'/'ps' + 8 chars)
244
247
  3. **Authentication Issues**: Verify JWT token and partner credentials
245
248
 
246
249
  **Troubleshooting:**
247
- 1. Verify transaction ID is correct: 10 characters total, starts with 'pw' (standard), 'pr' (product), or 'ps' (soundbite)
250
+ 1. Verify transaction ID is correct: 10 characters total, starts with 'pw' (standard), 'pr' (product), 'pl' (placement) or 'ps' (soundbite)
248
251
  2. Check if transaction has expired or been completed
249
- 3. Use \`payware_pi_get_transaction_history\` for completed/expired transactions
252
+ 3. Use \`payware_operations_get_transaction_history\` for completed/expired transactions
250
253
  4. Verify your partner credentials and API access
251
254
  5. Ensure transaction was created or you have access to it
252
255
 
253
256
  **Alternative Actions:**
254
- - If transaction is completed: Use \`payware_pi_get_transaction_history\`
257
+ - If transaction is completed: Use \`payware_operations_get_transaction_history\`
255
258
  - If transaction ID is wrong: Verify the correct transaction ID
256
259
  - If access issues: Check your payment institution credentials`
257
260
  }]
@@ -3,6 +3,7 @@ import { createJWTToken } from '../../core/auth/jwt-token.js';
3
3
  import { createMinimizedJSON } from '../../core/utils/json-serializer.js';
4
4
  import { getSandboxUrl, getProductionUrl, getPartnerIdSafe, getPrivateKeySafe } from '../../config/env.js';
5
5
  import { apiErrorResult } from '../../shared/api-errors.js';
6
+ import { COMMITS } from '../../shared/tool-annotations.js';
6
7
 
7
8
  /**
8
9
  * Process a transaction as a payment institution via payware API
@@ -112,6 +113,7 @@ export async function processPITransaction({
112
113
  */
113
114
  export const processPITransactionTool = {
114
115
  name: "payware_operations_process_transaction",
116
+ annotations: COMMITS,
115
117
  description: `Process a transaction as a payment institution. Payment institutions can process transactions created by others and must provide account/friendlyName details.
116
118
 
117
119
  **Use Cases:**
@@ -148,8 +150,8 @@ export const processPITransactionTool = {
148
150
  properties: {
149
151
  transactionId: {
150
152
  type: "string",
151
- description: "Transaction ID to process. Format: 2-char prefix + 8-char ID. Supports: 'pw' (standard), 'pr' (product), 'ps' (soundbite)",
152
- pattern: "^(pw|pr|ps)[0-9A-Za-z]{8}$"
153
+ description: "Transaction ID to process. Format: 2-char prefix + 8-char ID. Supports: 'pw' (standard), 'pr' (product), 'pl' (placement: pays as its product, and the payment is attributed to the placement), 'ps' (soundbite)",
154
+ pattern: "^(pw|pr|pl|ps)[0-9A-Za-z]{8}$"
153
155
  },
154
156
  account: {
155
157
  type: "string",
@@ -205,8 +207,7 @@ export const processPITransactionTool = {
205
207
  },
206
208
  privateKey: {
207
209
  type: "string",
208
- description: "RSA private key for JWT token creation. Uses environment-specific private key as default.",
209
- default: getPrivateKeySafe()
210
+ description: "RSA private key for JWT token creation. Uses environment-specific private key as default."
210
211
  },
211
212
  // Payment method for POI transactions
212
213
  paymentMethod: {
@@ -373,7 +374,7 @@ ${JSON.stringify(result.transaction, null, 2)}
373
374
  1. **TRANSACTION_PROCESSED** callback will be sent to counterparty
374
375
  2. Wait for callback notification to your URL (if provided)
375
376
  3. Monitor transaction status for completion
376
- 4. Use \`payware_pi_finalize_transaction\` to confirm/decline the transaction
377
+ 4. Use \`payware_operations_finalize_transaction\` to confirm/decline the transaction
377
378
  5. Handle final TRANSACTION_FINALIZED callbacks
378
379
 
379
380
  **⚠️ Important:**
@@ -412,7 +413,7 @@ ${JSON.stringify(result.error.details || result.error, null, 2)}
412
413
  7. **ERR_SPECIFYING_CURRENCY_NOT_ALLOWED**: Cannot change currency on merchant transactions
413
414
 
414
415
  **Troubleshooting:**
415
- 1. Verify transaction ID is correct: 10 characters total, starts with 'pw' (standard), 'pr' (product), or 'ps' (soundbite)
416
+ 1. Verify transaction ID is correct: 10 characters total, starts with 'pw' (standard), 'pr' (product), 'pl' (placement) or 'ps' (soundbite)
416
417
  2. Ensure account and friendlyName are provided
417
418
  3. Check currency matches original transaction currency
418
419
  4. Verify callback URL is provided (especially for payee role)
@@ -10,6 +10,8 @@
10
10
  * @param {Object} options - Additional options
11
11
  * @returns {Object} Mock callback payload
12
12
  */
13
+ import { LOCAL } from '../../shared/tool-annotations.js';
14
+
13
15
  export function generateMockPICallback(transactionId, callbackType = 'TRANSACTION_PROCESSED', options = {}) {
14
16
  const {
15
17
  amount = '57.00',
@@ -146,6 +148,7 @@ export async function simulatePICallbackDelivery(callbackUrl, payload) {
146
148
  */
147
149
  export const simulatePICallbackTool = {
148
150
  name: "payware_operations_simulate_callback",
151
+ annotations: LOCAL,
149
152
  description: "Simulate payment institution callback scenarios for testing webhook handling. Payment institutions receive TRANSACTION_PROCESSED when merchant processes their transaction, and TRANSACTION_FINALIZED when transaction is completed.",
150
153
  inputSchema: {
151
154
  type: "object",
@@ -4,6 +4,7 @@ import { createJWTToken } from '../../core/auth/jwt-token.js';
4
4
  import { getSandboxUrl, getProductionUrl, getPartnerIdSafe, getPrivateKeySafe } from '../../config/env.js';
5
5
  import { readFileSync } from 'fs';
6
6
  import { apiErrorResult } from '../../shared/api-errors.js';
7
+ import { CREATES } from '../../shared/tool-annotations.js';
7
8
 
8
9
  /**
9
10
  * Get transaction information using Soundbite file via payware API
@@ -73,6 +74,7 @@ export async function getSoundbiteTransaction({
73
74
  */
74
75
  export const soundbiteTransactionTool = {
75
76
  name: "payware_operations_soundbite_transaction",
77
+ annotations: CREATES,
76
78
  description: `Get transaction information using a Soundbite SDK output file. This allows payment institutions to process transactions using audio-based identifiers.
77
79
 
78
80
  **Soundbite Integration:**
@@ -105,8 +107,7 @@ export const soundbiteTransactionTool = {
105
107
  },
106
108
  privateKey: {
107
109
  type: "string",
108
- description: "RSA private key for JWT token creation. Uses environment-specific private key as default.",
109
- default: getPrivateKeySafe()
110
+ description: "RSA private key for JWT token creation. Uses environment-specific private key as default."
110
111
  },
111
112
  useSandbox: {
112
113
  type: "boolean",
@@ -199,8 +200,8 @@ ${JSON.stringify(result.transaction, null, 2)}
199
200
  1. Transaction successfully identified from soundbite file
200
201
  2. Use transaction ID: \`${tx.transactionId || 'N/A'}\` for further operations
201
202
  3. ${!tx.payerAccount ? 'Process this transaction if you are the payer' : 'Transaction ready for finalization'}
202
- 4. Use \`payware_pi_process_transaction\` to process payment
203
- 5. Use \`payware_pi_finalize_transaction\` to confirm/decline
203
+ 4. Use \`payware_operations_process_transaction\` to process payment
204
+ 5. Use \`payware_operations_finalize_transaction\` to confirm/decline
204
205
 
205
206
  **♿ Accessibility Note:**
206
207
  - Soundbite provides audio-based alternative to QR/barcode scanning
@@ -33,6 +33,31 @@ class PaywareMCPProxy {
33
33
  const allowedOrigins = (process.env.PAYWARE_MCP_PROXY_ORIGINS
34
34
  || `http://localhost:${this.port},http://127.0.0.1:${this.port}`)
35
35
  .split(',').map((o) => o.trim()).filter(Boolean);
36
+
37
+ // DNS rebinding and cross-site requests. The CORS allowlist below decides which pages may READ a
38
+ // response; it never refuses a request. A page whose hostname has been rebound to 127.0.0.1 is
39
+ // same-origin with this server, so CORS does not apply to it at all, and a cross-site form POST
40
+ // needs no preflight - either way the route runs and signs as the partner. Two checks refuse them:
41
+ // Host must be a name this server answers to (a rebound page sends its own hostname there), and an
42
+ // Origin, which browsers send on every POST, must be allowlisted. Clients that are not browsers -
43
+ // curl, the bridge - send no Origin and pass. PAYWARE_MCP_PROXY_HOSTS (comma-separated host:port)
44
+ // widens the Host list deliberately.
45
+ const bindHost = process.env.PAYWARE_MCP_PROXY_HOST || '127.0.0.1';
46
+ const allowedHosts = new Set((process.env.PAYWARE_MCP_PROXY_HOSTS
47
+ || `localhost:${this.port},127.0.0.1:${this.port},${bindHost}:${this.port}`)
48
+ .split(',').map((h) => h.trim().toLowerCase()).filter(Boolean));
49
+ this.app.use((req, res, next) => {
50
+ const host = (req.headers.host || '').toLowerCase();
51
+ if (!allowedHosts.has(host)) {
52
+ return res.status(403).json({ error: 'Forbidden', message: `Host ${host || '(none)'} is not allowed` });
53
+ }
54
+ const origin = req.headers.origin;
55
+ if (origin !== undefined && !allowedOrigins.includes(origin)) {
56
+ return res.status(403).json({ error: 'Forbidden', message: `Origin ${origin} is not allowed` });
57
+ }
58
+ next();
59
+ });
60
+
36
61
  this.app.use(cors({
37
62
  origin: allowedOrigins,
38
63
  credentials: true
@@ -0,0 +1,51 @@
1
+ import axios from 'axios';
2
+ import { createMinimizedJSON } from '../../core/utils/json-serializer.js';
3
+ import { getSandboxUrl, getProductionUrl } from '../../config/env.js';
4
+ import { createAuthHeaders } from '../auth-headers.js';
5
+ import { apiErrorResult } from '../api-errors.js';
6
+
7
+ /**
8
+ * One call to the campaigns, placements and airings API, as a merchant or as an ISV acting for one.
9
+ *
10
+ * Built on the shared `createAuthHeaders`, so the on-behalf JWT (`aud` = the merchant, `sub` = its
11
+ * OAuth2 token) and the content hash come from the same code every newer tool uses. The body is
12
+ * serialized once and that exact string is both hashed and sent; `transformRequest` stops axios from
13
+ * serializing it a second time.
14
+ *
15
+ * Never throws for an HTTP error: it returns `apiErrorResult`, whose `details` keeps the response
16
+ * body, which is where a refused replace-all lists its rows.
17
+ *
18
+ * @param {Object} params
19
+ * @param {string} params.method HTTP verb
20
+ * @param {string} params.path path under the API root, ids already encoded
21
+ * @param {Object} [params.query] query parameters; undefined values are left out
22
+ * @param {*} [params.body] request body, serialized deterministically
23
+ * @param {boolean} [params.binary] read the response as bytes (images)
24
+ * @param {Object} params.auth from the role: partnerType, credentials and useSandbox
25
+ */
26
+ export async function placementsRequest({ method, path, query, body, binary = false, auth }) {
27
+ const minimizedBody = body === undefined ? undefined : createMinimizedJSON(body);
28
+ const headers = await createAuthHeaders(auth.partnerType, { ...auth, requestBody: minimizedBody });
29
+ const baseUrl = auth.useSandbox ? getSandboxUrl() : getProductionUrl();
30
+ const params = query && Object.fromEntries(Object.entries(query).filter(([, v]) => v !== undefined && v !== null));
31
+
32
+ try {
33
+ const response = await axios({
34
+ method,
35
+ url: `${baseUrl}${path}`,
36
+ headers,
37
+ ...(params && Object.keys(params).length > 0 && { params }),
38
+ ...(minimizedBody !== undefined && { data: minimizedBody, transformRequest: [(data) => data] }),
39
+ ...(binary && { responseType: 'arraybuffer' })
40
+ });
41
+ return {
42
+ success: true,
43
+ status: response.status,
44
+ data: response.data,
45
+ contentType: response.headers['content-type'],
46
+ requestId: response.headers['x-request-id']
47
+ };
48
+ } catch (error) {
49
+ return apiErrorResult(error);
50
+ }
51
+ }