@payware/mcp-server 1.2.0 → 1.3.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 (58) hide show
  1. package/README.md +5 -0
  2. package/package.json +69 -66
  3. package/src/auth/sandbox-setup.js +186 -186
  4. package/src/isv/index.js +29 -1
  5. package/src/isv/invitations/index.js +471 -0
  6. package/src/isv/oauth2/obtain-token.js +19 -8
  7. package/src/isv/oauth2/token-helper.js +5 -2
  8. package/src/isv/poi/cancel-price.js +2 -10
  9. package/src/isv/poi/create.js +19 -0
  10. package/src/isv/poi/get-poi.js +2 -10
  11. package/src/isv/poi/get-qrcode.js +2 -10
  12. package/src/isv/poi/get-status.js +2 -10
  13. package/src/isv/poi/index.js +5 -0
  14. package/src/isv/poi/list-pois.js +2 -10
  15. package/src/isv/poi/set-price.js +217 -225
  16. package/src/isv/products/create-product.js +313 -313
  17. package/src/isv/products/update-product.js +264 -264
  18. package/src/isv/reference/index.js +249 -0
  19. package/src/isv/shops/index.js +22 -0
  20. package/src/isv/transactions/cancel-transaction.js +41 -10
  21. package/src/isv/transactions/create-transaction.js +20 -4
  22. package/src/isv/transactions/get-status.js +18 -11
  23. package/src/isv/transactions/process-transaction.js +160 -42
  24. package/src/isv/transactions/simulate-callback.js +128 -109
  25. package/src/isv/transactions/transaction-history.js +86 -90
  26. package/src/merchant/data/generate-report.js +19 -1
  27. package/src/merchant/index.js +21 -1
  28. package/src/merchant/logs/index.js +366 -0
  29. package/src/merchant/products/create-product.js +287 -287
  30. package/src/merchant/products/update-product.js +281 -281
  31. package/src/merchant/shops/index.js +18 -0
  32. package/src/merchant/transactions/cancel-transaction.js +10 -13
  33. package/src/merchant/transactions/create-transaction.js +88 -8
  34. package/src/merchant/transactions/get-status.js +40 -11
  35. package/src/merchant/transactions/process-transaction.js +2 -10
  36. package/src/merchant/transactions/simulate-callback.js +68 -9
  37. package/src/merchant/transactions/transaction-history.js +275 -257
  38. package/src/payment-institution/transactions/create-pi-transaction.js +2 -10
  39. package/src/payment-institution/transactions/finalize-transaction.js +333 -340
  40. package/src/payment-institution/transactions/get-transaction-history.js +2 -10
  41. package/src/payment-institution/transactions/get-transaction-status.js +38 -11
  42. package/src/payment-institution/transactions/process-pi-transaction.js +2 -10
  43. package/src/payment-institution/transactions/simulate-pi-callback.js +49 -9
  44. package/src/payment-institution/transactions/soundbite-transaction.js +2 -10
  45. package/src/shared/api-errors.js +188 -0
  46. package/src/shared/auth-headers.js +62 -0
  47. package/src/shared/data/data-api.js +11 -10
  48. package/src/shared/deep-links/api.js +5 -4
  49. package/src/shared/logs/logs-api.js +242 -0
  50. package/src/shared/poi/poi-create-api.js +419 -0
  51. package/src/shared/products/products-api.js +17 -16
  52. package/src/shared/shops/shops-api.js +155 -0
  53. package/src/tools/advanced-code-generator.js +2255 -2246
  54. package/src/utils/code-examples/common/auth-examples.js +1484 -1481
  55. package/src/utils/code-examples/common/helpers.js +7 -4
  56. package/src/utils/code-examples/isv/oauth2.js +458 -600
  57. package/src/utils/code-examples/isv/transactions.js +815 -812
  58. package/MCP-SETUP.md +0 -190
@@ -4,6 +4,7 @@ import { createJWTToken } from '../../core/auth/jwt-token.js';
4
4
  import { createJWTForPartner } from '../../core/auth/jwt-factory.js';
5
5
  import { createMinimizedJSON } from '../../core/utils/json-serializer.js';
6
6
  import { getSandboxUrl, getProductionUrl } from '../../config/env.js';
7
+ import { describeApiError } from '../api-errors.js';
7
8
 
8
9
  /**
9
10
  * Product types
@@ -192,7 +193,7 @@ export async function createProduct({
192
193
  }
193
194
  };
194
195
  } catch (error) {
195
- throw new Error(`Failed to create product: ${error.response?.data?.message || error.message}`);
196
+ throw describeApiError(error, 'create product');
196
197
  }
197
198
  }
198
199
 
@@ -223,7 +224,7 @@ export async function getProducts({
223
224
  }
224
225
  };
225
226
  } catch (error) {
226
- throw new Error(`Failed to get products: ${error.response?.data?.message || error.message}`);
227
+ throw describeApiError(error, 'get products');
227
228
  }
228
229
  }
229
230
 
@@ -259,7 +260,7 @@ export async function getProduct({
259
260
  }
260
261
  };
261
262
  } catch (error) {
262
- throw new Error(`Failed to get product: ${error.response?.data?.message || error.message}`);
263
+ throw describeApiError(error, 'get product');
263
264
  }
264
265
  }
265
266
 
@@ -322,7 +323,7 @@ export async function updateProduct({
322
323
  }
323
324
  };
324
325
  } catch (error) {
325
- throw new Error(`Failed to update product: ${error.response?.data?.message || error.message}`);
326
+ throw describeApiError(error, 'update product');
326
327
  }
327
328
  }
328
329
 
@@ -357,7 +358,7 @@ export async function deleteProduct({
357
358
  }
358
359
  };
359
360
  } catch (error) {
360
- throw new Error(`Failed to delete product: ${error.response?.data?.message || error.message}`);
361
+ throw describeApiError(error, 'delete product');
361
362
  }
362
363
  }
363
364
 
@@ -410,7 +411,7 @@ export async function getProductImage({
410
411
  }
411
412
  };
412
413
  } catch (error) {
413
- throw new Error(`Failed to get product image: ${error.response?.data?.message || error.message}`);
414
+ throw describeApiError(error, 'get product image');
414
415
  }
415
416
  }
416
417
 
@@ -469,7 +470,7 @@ export async function createPriceSchedule({
469
470
  }
470
471
  };
471
472
  } catch (error) {
472
- throw new Error(`Failed to create price schedule: ${error.response?.data?.message || error.message}`);
473
+ throw describeApiError(error, 'create price schedule');
473
474
  }
474
475
  }
475
476
 
@@ -505,7 +506,7 @@ export async function getProductSchedules({
505
506
  }
506
507
  };
507
508
  } catch (error) {
508
- throw new Error(`Failed to get product schedules: ${error.response?.data?.message || error.message}`);
509
+ throw describeApiError(error, 'get product schedules');
509
510
  }
510
511
  }
511
512
 
@@ -545,7 +546,7 @@ export async function getSchedule({
545
546
  }
546
547
  };
547
548
  } catch (error) {
548
- throw new Error(`Failed to get schedule: ${error.response?.data?.message || error.message}`);
549
+ throw describeApiError(error, 'get schedule');
549
550
  }
550
551
  }
551
552
 
@@ -603,7 +604,7 @@ export async function updatePriceSchedule({
603
604
  }
604
605
  };
605
606
  } catch (error) {
606
- throw new Error(`Failed to update schedule: ${error.response?.data?.message || error.message}`);
607
+ throw describeApiError(error, 'update schedule');
607
608
  }
608
609
  }
609
610
 
@@ -642,7 +643,7 @@ export async function deletePriceSchedule({
642
643
  }
643
644
  };
644
645
  } catch (error) {
645
- throw new Error(`Failed to delete schedule: ${error.response?.data?.message || error.message}`);
646
+ throw describeApiError(error, 'delete schedule');
646
647
  }
647
648
  }
648
649
 
@@ -702,7 +703,7 @@ export async function registerAudio({
702
703
  }
703
704
  };
704
705
  } catch (error) {
705
- throw new Error(`Failed to register audio: ${error.response?.data?.message || error.message}`);
706
+ throw describeApiError(error, 'register audio');
706
707
  }
707
708
  }
708
709
 
@@ -738,7 +739,7 @@ export async function getProductAudios({
738
739
  }
739
740
  };
740
741
  } catch (error) {
741
- throw new Error(`Failed to get product audios: ${error.response?.data?.message || error.message}`);
742
+ throw describeApiError(error, 'get product audios');
742
743
  }
743
744
  }
744
745
 
@@ -778,7 +779,7 @@ export async function getAudio({
778
779
  }
779
780
  };
780
781
  } catch (error) {
781
- throw new Error(`Failed to get audio: ${error.response?.data?.message || error.message}`);
782
+ throw describeApiError(error, 'get audio');
782
783
  }
783
784
  }
784
785
 
@@ -828,7 +829,7 @@ export async function updateAudio({
828
829
  }
829
830
  };
830
831
  } catch (error) {
831
- throw new Error(`Failed to update audio: ${error.response?.data?.message || error.message}`);
832
+ throw describeApiError(error, 'update audio');
832
833
  }
833
834
  }
834
835
 
@@ -867,6 +868,6 @@ export async function deleteAudio({
867
868
  }
868
869
  };
869
870
  } catch (error) {
870
- throw new Error(`Failed to delete audio: ${error.response?.data?.message || error.message}`);
871
+ throw describeApiError(error, 'delete audio');
871
872
  }
872
873
  }
@@ -0,0 +1,155 @@
1
+ import axios from 'axios';
2
+ import { createAuthHeaders } from '../auth-headers.js';
3
+ import { apiErrorResult } from '../api-errors.js';
4
+ import { getSandboxUrl, getProductionUrl } from '../../config/env.js';
5
+
6
+ /**
7
+ * Shops - the merchant outlets a transaction, product or POI belongs to.
8
+ *
9
+ * `shopCode` is required or defaulted on most write paths in this server (creating a transaction,
10
+ * creating a product, creating a POI, batching POIs), and until now nothing here could tell you what
11
+ * a valid one was. An assistant had two options: leave `shop` out and hope the merchant's default is
12
+ * the right outlet, or invent a code and get a 400. Listing them is one GET.
13
+ */
14
+
15
+ /**
16
+ * List the shops the caller can act on.
17
+ *
18
+ * @param {Object} params
19
+ * @param {string} params.partnerType 'merchant' | 'isv'
20
+ * @param {boolean} params.useSandbox
21
+ * @returns {Object} { success, shops, count, requestId, timestamp } or an apiErrorResult
22
+ */
23
+ export async function listShops({ partnerType, useSandbox = true, ...authParams }) {
24
+ const headers = await createAuthHeaders(partnerType, authParams);
25
+
26
+ try {
27
+ const baseUrl = useSandbox ? getSandboxUrl() : getProductionUrl();
28
+ const response = await axios.get(`${baseUrl}/shops`, { headers });
29
+
30
+ return {
31
+ success: true,
32
+ shops: response.data,
33
+ count: Array.isArray(response.data) ? response.data.length : 0,
34
+ requestId: response.headers['x-request-id'],
35
+ timestamp: new Date().toISOString()
36
+ };
37
+ } catch (error) {
38
+ return apiErrorResult(error);
39
+ }
40
+ }
41
+
42
+ /**
43
+ * Shared list-shops tool. Registered for both the merchant and ISV roles; `partnerType` decides how
44
+ * the request is signed and, on the server side, whose shops come back.
45
+ */
46
+ export function buildListShopsTool({ partnerType, resolveAuth }) {
47
+ const isIsv = partnerType === 'isv';
48
+
49
+ return {
50
+ name: 'payware_shops_list',
51
+ description: `List the merchant outlets (shops) the caller can act on.
52
+
53
+ **Endpoint:** GET /shops
54
+
55
+ **Use this before any call that takes a \`shop\` / \`shopCode\`.** Creating a transaction, creating a
56
+ product, creating a POI and batching POIs all take one, and an invalid code is a 400
57
+ (\`ERR_INVALID_SHOP_CODE\`). Omitting it falls back to the merchant's default shop, which is a
58
+ reasonable guess for a single-outlet merchant and the wrong outlet for everyone else - the sale lands
59
+ against the wrong shop, and nothing about the response says so.
60
+
61
+ **Returned per shop:** \`shopCode\` (the value to pass), \`name\`, and the address/contact fields the
62
+ merchant configured.
63
+ ${isIsv ? `
64
+ ⚠️ **ISVs see only their assigned shops.** This returns the shops the merchant put in your scope, not
65
+ every shop the merchant owns. That is deliberate and it is the same rule the write paths enforce:
66
+ naming a shop outside your scope answers **403 \`ERR_SHOP_NOT_IN_SCOPE\`**. So if a shop you expect is
67
+ missing here, the merchant has not assigned it - ask them to, rather than passing the code anyway.
68
+
69
+ ⚠️ **The MERCHANT's plan governs.** An on-behalf call inherits the acting merchant's plan, so this
70
+ answers **403** when that merchant is on **Basic**.
71
+ ` : `
72
+ ⚠️ **Requires Standard or Premium.** A Basic merchant gets **403**.
73
+ `}
74
+ **Note on financial institutions:** banks do not own shops. A bank principal calling this gets 403,
75
+ not an empty list.`,
76
+
77
+ inputSchema: {
78
+ type: 'object',
79
+ required: isIsv ? ['merchantPartnerId', 'oauth2Token'] : [],
80
+ properties: {
81
+ ...(isIsv
82
+ ? {
83
+ merchantPartnerId: {
84
+ type: 'string',
85
+ description: 'Partner ID of the target merchant (8 alphanumeric characters)'
86
+ },
87
+ oauth2Token: {
88
+ type: 'string',
89
+ description: 'OAuth2 access token obtained from the merchant'
90
+ }
91
+ }
92
+ : {
93
+ partnerId: {
94
+ type: 'string',
95
+ description: 'Partner ID from the payware dashboard. Uses PAYWARE_PARTNER_ID env var as default.'
96
+ },
97
+ privateKey: {
98
+ type: 'string',
99
+ description: 'RSA private key for JWT signing. Uses the environment-specific key as default.'
100
+ }
101
+ }),
102
+ useSandbox: {
103
+ type: 'boolean',
104
+ description: 'Use sandbox environment for testing',
105
+ default: true
106
+ }
107
+ }
108
+ },
109
+
110
+ async handler(args) {
111
+ const { useSandbox = true } = args;
112
+ const authParams = resolveAuth(args);
113
+ const result = await listShops({ partnerType, useSandbox, ...authParams });
114
+
115
+ if (!result.success) {
116
+ return {
117
+ content: [{
118
+ type: 'text',
119
+ text: `❌ **Failed to List Shops**
120
+
121
+ - Message: ${result.error.message}
122
+ - Status: ${result.error.status || 'N/A'}
123
+ - Code: ${result.error.errorCode || 'N/A'}
124
+ ${result.error.correlationId ? `- Correlation ID: ${result.error.correlationId}` : ''}
125
+ ${result.error.guidance ? `\n${result.error.guidance}` : ''}
126
+
127
+ **Timestamp:** ${result.timestamp}`
128
+ }]
129
+ };
130
+ }
131
+
132
+ const shops = Array.isArray(result.shops) ? result.shops : [];
133
+ const list = shops.map(shop => {
134
+ const location = [shop.city, shop.country].filter(Boolean).join(', ');
135
+ return `🏬 **${shop.name || '(unnamed)'}** - \`${shop.shopCode}\`${location ? `\n ${location}` : ''}`;
136
+ }).join('\n');
137
+
138
+ return {
139
+ content: [{
140
+ type: 'text',
141
+ text: `🏬 **Shops (${result.count})**
142
+
143
+ ${list || 'No shops returned.'}
144
+
145
+ Pass a \`shopCode\` above as the \`shop\` parameter when creating a transaction, product or POI.
146
+ ${shops.length === 0 ? `
147
+ **An empty list is not necessarily an error.**${isIsv ? ' As an ISV you see only the shops this merchant assigned to your scope - an empty list means none have been assigned yet.' : ' It means no shop is configured for this merchant yet.'}` : ''}
148
+
149
+ **Request ID:** ${result.requestId || 'N/A'}
150
+ **Timestamp:** ${result.timestamp}`
151
+ }]
152
+ };
153
+ }
154
+ };
155
+ }