@payware/mcp-server 1.0.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 (106) hide show
  1. package/.env.example +92 -0
  2. package/MCP-SETUP.md +190 -0
  3. package/README.md +483 -0
  4. package/check-keys.js +52 -0
  5. package/keys/README.md +112 -0
  6. package/mcp-config.json +9 -0
  7. package/package.json +66 -0
  8. package/payware-mcp-config.json +12 -0
  9. package/src/auth/rsa-keys.js +82 -0
  10. package/src/auth/sandbox-setup.js +187 -0
  11. package/src/config/env.js +229 -0
  12. package/src/config/partner-types.js +143 -0
  13. package/src/core/auth/jwt-factory.js +182 -0
  14. package/src/core/auth/jwt-token.js +735 -0
  15. package/src/core/utils/json-serializer.js +100 -0
  16. package/src/core/utils/key-utils.js +133 -0
  17. package/src/index.js +237 -0
  18. package/src/isv/data/index.js +291 -0
  19. package/src/isv/deep-links/delete-product-link.js +154 -0
  20. package/src/isv/deep-links/delete-transaction-link.js +154 -0
  21. package/src/isv/deep-links/get-product-link.js +167 -0
  22. package/src/isv/deep-links/get-transaction-link.js +167 -0
  23. package/src/isv/deep-links/index.js +23 -0
  24. package/src/isv/index.js +103 -0
  25. package/src/isv/oauth2/create-token-simple.js +46 -0
  26. package/src/isv/oauth2/get-token-info.js +157 -0
  27. package/src/isv/oauth2/obtain-token.js +189 -0
  28. package/src/isv/oauth2/token-helper.js +151 -0
  29. package/src/isv/products/audios.js +470 -0
  30. package/src/isv/products/create-product.js +314 -0
  31. package/src/isv/products/delete-product.js +119 -0
  32. package/src/isv/products/get-product-image.js +247 -0
  33. package/src/isv/products/get-product.js +168 -0
  34. package/src/isv/products/index.js +65 -0
  35. package/src/isv/products/list-products.js +136 -0
  36. package/src/isv/products/schedules.js +522 -0
  37. package/src/isv/products/update-product.js +265 -0
  38. package/src/isv/transactions/cancel-transaction.js +129 -0
  39. package/src/isv/transactions/create-transaction.js +361 -0
  40. package/src/isv/transactions/get-status.js +217 -0
  41. package/src/isv/transactions/process-transaction.js +145 -0
  42. package/src/isv/transactions/simulate-callback.js +146 -0
  43. package/src/isv/transactions/transaction-history.js +186 -0
  44. package/src/isv/utils/auth.js +159 -0
  45. package/src/mcp-proxy.js +150 -0
  46. package/src/merchant/data/export-report.js +108 -0
  47. package/src/merchant/data/generate-report.js +151 -0
  48. package/src/merchant/data/get-report-status.js +120 -0
  49. package/src/merchant/data/index.js +351 -0
  50. package/src/merchant/deep-links/delete-product-link.js +121 -0
  51. package/src/merchant/deep-links/delete-transaction-link.js +121 -0
  52. package/src/merchant/deep-links/get-product-link.js +134 -0
  53. package/src/merchant/deep-links/get-transaction-link.js +134 -0
  54. package/src/merchant/deep-links/index.js +23 -0
  55. package/src/merchant/index.js +92 -0
  56. package/src/merchant/products/audios.js +606 -0
  57. package/src/merchant/products/create-product.js +288 -0
  58. package/src/merchant/products/delete-product.js +111 -0
  59. package/src/merchant/products/get-product-image.js +252 -0
  60. package/src/merchant/products/get-product.js +144 -0
  61. package/src/merchant/products/index.js +65 -0
  62. package/src/merchant/products/list-products.js +112 -0
  63. package/src/merchant/products/schedules.js +646 -0
  64. package/src/merchant/products/update-product.js +282 -0
  65. package/src/merchant/transactions/cancel-transaction.js +241 -0
  66. package/src/merchant/transactions/create-transaction.js +482 -0
  67. package/src/merchant/transactions/get-status.js +196 -0
  68. package/src/merchant/transactions/process-transaction.js +349 -0
  69. package/src/merchant/transactions/simulate-callback.js +246 -0
  70. package/src/merchant/transactions/transaction-history.js +258 -0
  71. package/src/payment-institution/data/index.js +280 -0
  72. package/src/payment-institution/deep-links/delete-transaction-link.js +128 -0
  73. package/src/payment-institution/deep-links/get-transaction-link.js +141 -0
  74. package/src/payment-institution/deep-links/index.js +17 -0
  75. package/src/payment-institution/index.js +92 -0
  76. package/src/payment-institution/transactions/create-pi-transaction.js +484 -0
  77. package/src/payment-institution/transactions/finalize-transaction.js +340 -0
  78. package/src/payment-institution/transactions/get-transaction-history.js +269 -0
  79. package/src/payment-institution/transactions/get-transaction-status.js +234 -0
  80. package/src/payment-institution/transactions/process-pi-transaction.js +421 -0
  81. package/src/payment-institution/transactions/simulate-pi-callback.js +326 -0
  82. package/src/payment-institution/transactions/soundbite-transaction.js +269 -0
  83. package/src/proxy-server.js +205 -0
  84. package/src/shared/data/data-api.js +372 -0
  85. package/src/shared/deep-links/api.js +214 -0
  86. package/src/shared/index.js +44 -0
  87. package/src/shared/products/products-api.js +872 -0
  88. package/src/tools/advanced-code-generator.js +2247 -0
  89. package/src/utils/code-examples/common/auth-examples.js +1482 -0
  90. package/src/utils/code-examples/common/helpers.js +416 -0
  91. package/src/utils/code-examples/common/utils-examples.js +591 -0
  92. package/src/utils/code-examples/frameworks/examples.js +859 -0
  93. package/src/utils/code-examples/index.js +441 -0
  94. package/src/utils/code-examples/isv/oauth2.js +600 -0
  95. package/src/utils/code-examples/isv/products.js +666 -0
  96. package/src/utils/code-examples/isv/transactions.js +813 -0
  97. package/src/utils/code-examples/merchant/products.js +497 -0
  98. package/src/utils/code-examples/merchant/transactions.js +623 -0
  99. package/src/utils/code-examples/payment-institution/transactions.js +388 -0
  100. package/src/utils/code-examples/shared/data.js +607 -0
  101. package/src/utils/code-examples/shared/deep-links.js +662 -0
  102. package/src/utils/code-examples/shared/soundbites.js +808 -0
  103. package/src/utils/code-examples.js +646 -0
  104. package/src/utils/format-request.js +363 -0
  105. package/src/utils/json-formatter.js +249 -0
  106. package/src/utils/server-info.js +132 -0
@@ -0,0 +1,282 @@
1
+ import { updateProduct } from '../../shared/products/products-api.js';
2
+ import { getPartnerIdSafe, getPrivateKeySafe } from '../../config/env.js';
3
+ import { PRODUCT_TYPES, OVERLAP_PRIORITY } from '../../shared/products/products-api.js';
4
+
5
+ /**
6
+ * Update product tool implementation for merchants
7
+ */
8
+ export const updateProductTool = {
9
+ name: "payware_products_update_product",
10
+ description: `Update an existing product. Only provide the fields you want to change.
11
+
12
+ **Product Structure:**
13
+ 📦 **PRODUCT METADATA**: name, shortDescription, longDescription, sku, upc, shippable, active, shop
14
+ 💰 **PRODUCT DATA** (prData object): type, regularPrice, currency, overlapPriority, timeToLive, activeFrom, activeTo, timeZone
15
+ ⚙️ **PRODUCT OPTIONS** (prOptions object): imageUrl, termsUrl, termsText`,
16
+
17
+ inputSchema: {
18
+ type: "object",
19
+ properties: {
20
+ productId: {
21
+ type: "string",
22
+ description: "Product identifier to update (starts with 'pr')",
23
+ pattern: "^pr[a-zA-Z0-9]+$"
24
+ },
25
+ name: {
26
+ type: "string",
27
+ description: "Product recognizable name",
28
+ maxLength: 50
29
+ },
30
+ shortDescription: {
31
+ type: "string",
32
+ description: "Short product description",
33
+ maxLength: 150
34
+ },
35
+ longDescription: {
36
+ type: "string",
37
+ description: "Long product description",
38
+ maxLength: 1500
39
+ },
40
+ sku: {
41
+ type: "string",
42
+ description: "Stock Keeping Unit (SKU)",
43
+ maxLength: 25
44
+ },
45
+ upc: {
46
+ type: "string",
47
+ description: "Universal Product Code (UPC)",
48
+ maxLength: 15
49
+ },
50
+ shippable: {
51
+ type: "string",
52
+ enum: ["TRUE", "FALSE"],
53
+ description: "Indicates if product requires delivery address"
54
+ },
55
+ active: {
56
+ type: "string",
57
+ enum: ["TRUE", "FALSE"],
58
+ description: "Indicates if product can serve as payment source"
59
+ },
60
+ shop: {
61
+ type: "string",
62
+ description: "Shop code",
63
+ maxLength: 10
64
+ },
65
+ // Product Data Properties
66
+ productType: {
67
+ type: "string",
68
+ enum: Object.values(PRODUCT_TYPES),
69
+ description: "Type of product"
70
+ },
71
+ regularPrice: {
72
+ type: "string",
73
+ description: "Product regular price (positive number with max 2 decimals)"
74
+ },
75
+ currency: {
76
+ type: "string",
77
+ pattern: "^[A-Z]{3}$",
78
+ description: "Price currency (3-character ISO code)"
79
+ },
80
+ overlapPriority: {
81
+ type: "string",
82
+ enum: Object.values(OVERLAP_PRIORITY),
83
+ description: "Priority when overlapping price schedules exist"
84
+ },
85
+ timeToLive: {
86
+ type: "number",
87
+ description: "Time allowed for product payment in seconds (60-2592000)",
88
+ minimum: 60,
89
+ maximum: 2592000
90
+ },
91
+ activeFrom: {
92
+ type: "string",
93
+ pattern: "^\\d{4}-\\d{2}-\\d{2}$",
94
+ description: "Product active state starting date (YYYY-MM-DD format)"
95
+ },
96
+ activeTo: {
97
+ type: "string",
98
+ pattern: "^\\d{4}-\\d{2}-\\d{2}$",
99
+ description: "Product active state ending date (YYYY-MM-DD format)"
100
+ },
101
+ timeZone: {
102
+ type: "string",
103
+ description: "Product state scheduling timezone (IANA format, e.g., 'Europe/Sofia')"
104
+ },
105
+ // Product Options Properties
106
+ imageUrl: {
107
+ type: "string",
108
+ description: "URL pointing to external product image resource",
109
+ maxLength: 150,
110
+ format: "uri"
111
+ },
112
+ termsUrl: {
113
+ type: "string",
114
+ description: "URL pointing to external terms and conditions resource",
115
+ maxLength: 150,
116
+ format: "uri"
117
+ },
118
+ termsText: {
119
+ type: "string",
120
+ description: "Terms and conditions text",
121
+ maxLength: 3000
122
+ },
123
+ // Authentication
124
+ partnerId: {
125
+ type: "string",
126
+ description: "Merchant partner ID (uses PAYWARE_PARTNER_ID env var as default)",
127
+ default: getPartnerIdSafe()
128
+ },
129
+ privateKey: {
130
+ type: "string",
131
+ description: "RSA private key for authentication (uses environment-specific private key as default)",
132
+ default: getPrivateKeySafe()
133
+ },
134
+ useSandbox: {
135
+ type: "boolean",
136
+ description: "Use sandbox environment",
137
+ default: true
138
+ }
139
+ },
140
+ required: ["productId"]
141
+ },
142
+
143
+ async handler(args) {
144
+ const {
145
+ productId,
146
+ name,
147
+ shortDescription,
148
+ longDescription,
149
+ sku,
150
+ upc,
151
+ shippable,
152
+ active,
153
+ shop,
154
+ productType,
155
+ regularPrice,
156
+ currency,
157
+ overlapPriority,
158
+ timeToLive,
159
+ activeFrom,
160
+ activeTo,
161
+ timeZone,
162
+ imageUrl,
163
+ termsUrl,
164
+ termsText,
165
+ partnerId = getPartnerIdSafe(),
166
+ privateKey = getPrivateKeySafe(),
167
+ useSandbox = true
168
+ } = args;
169
+
170
+ if (!productId) {
171
+ throw new Error("Product ID is required");
172
+ }
173
+
174
+ if (!partnerId) {
175
+ throw new Error("Partner ID is required. Provide via 'partnerId' parameter or set PAYWARE_PARTNER_ID environment variable.");
176
+ }
177
+
178
+ if (!privateKey) {
179
+ throw new Error("Private key is required. Provide via 'privateKey' parameter or set environment-specific private key variable.");
180
+ }
181
+
182
+ // Build prData object only with provided fields
183
+ const prData = {};
184
+ if (productType !== undefined) prData.type = productType;
185
+ if (regularPrice !== undefined) prData.regularPrice = regularPrice;
186
+ if (currency !== undefined) prData.currency = currency;
187
+ if (overlapPriority !== undefined) prData.overlapPriority = overlapPriority;
188
+ if (timeToLive !== undefined) prData.timeToLive = timeToLive;
189
+ if (activeFrom !== undefined) prData.activeFrom = activeFrom;
190
+ if (activeTo !== undefined) prData.activeTo = activeTo;
191
+ if (timeZone !== undefined) prData.timeZone = timeZone;
192
+
193
+ // Build prOptions object only with provided fields
194
+ const prOptions = {};
195
+ if (imageUrl !== undefined) prOptions.imageUrl = imageUrl;
196
+ if (termsUrl !== undefined) prOptions.termsUrl = termsUrl;
197
+ if (termsText !== undefined) prOptions.termsText = termsText;
198
+
199
+ try {
200
+ const result = await updateProduct({
201
+ productId,
202
+ name,
203
+ shortDescription,
204
+ longDescription,
205
+ sku,
206
+ upc,
207
+ shippable,
208
+ active,
209
+ shop,
210
+ prData,
211
+ prOptions,
212
+ partnerType: 'merchant',
213
+ partnerId,
214
+ privateKey,
215
+ useSandbox
216
+ });
217
+
218
+ const product = result.data;
219
+
220
+ return {
221
+ content: [{
222
+ type: "text",
223
+ text: `✅ **Product Updated Successfully**
224
+
225
+ **Updated Product Details:**
226
+ - ID: ${product.productId}
227
+ - Name: ${product.name}
228
+ - Type: ${product.type}
229
+ - Price: ${product.regularPrice} ${product.currencyCode}
230
+ - Status: ${product.active ? '✅ Active' : '⏸️ Inactive'}
231
+ - Shop: ${product.shopCode} (${product.shopName})
232
+ ${product.shortDescription ? `- Description: ${product.shortDescription}` : ''}
233
+ ${product.sku ? `- SKU: ${product.sku}` : ''}
234
+ ${product.upc ? `- UPC: ${product.upc}` : ''}
235
+
236
+ **Product Settings:**
237
+ - Shippable: ${product.shippable ? 'Yes' : 'No'}
238
+ - Time to Live: ${product.timeToLive} seconds
239
+ - Sale Price: ${product.salePrice} ${product.currencyCode}
240
+ - Overlap Priority: ${product.overlapPriority}
241
+
242
+ **Active Period:**
243
+ - From: ${product.activeFromZoned || product.activeFrom}
244
+ - To: ${product.activeToZoned || product.activeTo}
245
+ - Timezone: ${product.timeZone}
246
+
247
+ **API Response:**
248
+ \`\`\`json
249
+ ${JSON.stringify(product, null, 2)}
250
+ \`\`\`
251
+
252
+ **API Call Details:**
253
+ - Endpoint: ${result.requestInfo.method} ${result.requestInfo.url}
254
+ - Status: ${result.requestInfo.statusCode}
255
+
256
+ **Next Steps:**
257
+ - Use \`payware_products_get_product\` to view updated product details
258
+ - Use \`payware_products_create_schedule\` to manage price schedules
259
+ - Use \`payware_products_get_product_image\` to generate QR/barcode`
260
+ }]
261
+ };
262
+ } catch (error) {
263
+ return {
264
+ content: [{
265
+ type: "text",
266
+ text: `❌ **Product Update Failed**
267
+
268
+ **Error Details:**
269
+ - Message: ${error.message}
270
+ - Product ID: ${productId}
271
+
272
+ **Troubleshooting:**
273
+ 1. Verify the product ID is correct and exists
274
+ 2. Check parameter values (dates, price format, currency)
275
+ 3. Ensure JWT token is valid and not expired
276
+ 4. Verify partner ID and API access
277
+ 5. Check that the product belongs to your account`
278
+ }]
279
+ };
280
+ }
281
+ }
282
+ };
@@ -0,0 +1,241 @@
1
+ import axios from 'axios';
2
+ import { createJWTToken } from '../../core/auth/jwt-token.js';
3
+ import { createMinimizedJSON } from '../../core/utils/json-serializer.js';
4
+ import { getSandboxUrl, getProductionUrl, getPartnerIdSafe, getPrivateKeySafe } from '../../config/env.js';
5
+
6
+ /**
7
+ * Cancel a transaction via payware API
8
+ * @param {Object} params - Transaction cancellation parameters
9
+ * @returns {Object} Transaction cancellation response
10
+ */
11
+ export async function cancelTransaction({
12
+ transactionId,
13
+ statusMessage,
14
+ partnerId,
15
+ privateKey,
16
+ useSandbox = true
17
+ }) {
18
+ if (!transactionId) {
19
+ throw new Error('Transaction ID is required');
20
+ }
21
+
22
+ if (!statusMessage) {
23
+ throw new Error('Status message is required (cancellation reason)');
24
+ }
25
+
26
+ if (statusMessage.length > 100) {
27
+ throw new Error('Status message cannot exceed 100 characters');
28
+ }
29
+
30
+ if (!partnerId || !privateKey) {
31
+ throw new Error('Partner ID and private key are required for proper JWT creation');
32
+ }
33
+
34
+ // Build request body according to documentation
35
+ const requestBody = {
36
+ status: 'CANCELLED',
37
+ statusMessage
38
+ };
39
+
40
+ // Convert to deterministic minimized JSON for MD5 calculation
41
+ const minimizedBodyString = createMinimizedJSON(requestBody);
42
+
43
+ // Create JWT token with contentMd5 for the request body (PATCH requires contentMd5)
44
+ const tokenData = createJWTToken(partnerId, privateKey, minimizedBodyString);
45
+
46
+ // Required headers as per payware API documentation
47
+ const headers = {
48
+ 'Authorization': `Bearer ${tokenData.token}`,
49
+ 'Content-Type': 'application/json',
50
+ 'Api-Version': '1' // Required: current API version
51
+ };
52
+
53
+ try {
54
+ // Send the exact minimized JSON string that was used for MD5 calculation
55
+ const baseUrl = useSandbox ? getSandboxUrl() : getProductionUrl();
56
+ const response = await axios.patch(`${baseUrl}/transactions/${transactionId}`, minimizedBodyString, {
57
+ headers,
58
+ // Tell axios to send the string as-is, don't serialize it again
59
+ transformRequest: [(data) => data]
60
+ });
61
+
62
+ return {
63
+ success: true,
64
+ transaction: response.data,
65
+ requestId: response.headers['x-request-id'],
66
+ timestamp: new Date().toISOString()
67
+ };
68
+ } catch (error) {
69
+ return {
70
+ success: false,
71
+ error: {
72
+ message: error.response?.data?.message || error.message,
73
+ status: error.response?.status,
74
+ code: error.response?.data?.errorCode,
75
+ details: error.response?.data
76
+ },
77
+ timestamp: new Date().toISOString()
78
+ };
79
+ }
80
+ }
81
+
82
+ /**
83
+ * Cancel transaction tool implementation
84
+ */
85
+ export const cancelTransactionTool = {
86
+ name: "payware_operations_cancel_transaction",
87
+ description: "Cancel an ACTIVE transaction created by this merchant. Only the merchant who created the transaction can cancel it, and only before it has been processed.",
88
+ inputSchema: {
89
+ type: "object",
90
+ additionalProperties: false,
91
+ properties: {
92
+ transactionId: {
93
+ type: "string",
94
+ description: "Transaction ID to cancel (must be ACTIVE transaction created by this merchant). Format: 2-char prefix + 8-char ID. Supports: 'pw' (standard), 'pr' (product), 'ps' (soundbite)",
95
+ pattern: "^(pw|pr|ps)[0-9A-Za-z]{8}$"
96
+ },
97
+ statusMessage: {
98
+ type: "string",
99
+ description: "Reason for cancellation (required)",
100
+ maxLength: 100,
101
+ minLength: 1
102
+ },
103
+ partnerId: {
104
+ type: "string",
105
+ description: "Partner ID from payware dashboard. Can use PAYWARE_PARTNER_ID env var as default.",
106
+ default: getPartnerIdSafe()
107
+ },
108
+ privateKey: {
109
+ type: "string",
110
+ description: "RSA private key for JWT token creation. Can use PAYWARE_PRIVATE_KEY_PATH env var as default.",
111
+ default: getPrivateKeySafe()
112
+ },
113
+ useSandbox: {
114
+ type: "boolean",
115
+ description: "Use sandbox environment for testing",
116
+ default: true
117
+ }
118
+ },
119
+ required: ["transactionId", "statusMessage"]
120
+ },
121
+
122
+ async handler(args) {
123
+ const {
124
+ transactionId,
125
+ statusMessage,
126
+ partnerId = getPartnerIdSafe(),
127
+ privateKey = getPrivateKeySafe(args.useSandbox ?? true),
128
+ useSandbox = true
129
+ } = args;
130
+
131
+ if (!transactionId) {
132
+ throw new Error("Transaction ID is required");
133
+ }
134
+
135
+ if (!statusMessage) {
136
+ throw new Error("Status message is required (reason for cancellation)");
137
+ }
138
+
139
+ if (!partnerId) {
140
+ throw new Error("Partner ID is required. Provide via 'partnerId' parameter or set PAYWARE_PARTNER_ID environment variable.");
141
+ }
142
+
143
+ if (!privateKey) {
144
+ throw new Error("Private key is required. Provide via 'privateKey' parameter or set PAYWARE_PRIVATE_KEY environment variable.");
145
+ }
146
+
147
+ const result = await cancelTransaction({
148
+ transactionId,
149
+ statusMessage,
150
+ partnerId,
151
+ privateKey,
152
+ useSandbox
153
+ });
154
+
155
+ if (result.success) {
156
+ return {
157
+ content: [{
158
+ type: "text",
159
+ text: `🚫 **Transaction Cancelled Successfully**
160
+
161
+ **Transaction ID:** ${transactionId}
162
+ **Status:** ${result.transaction.status || 'CANCELLED'}
163
+
164
+ **Cancellation Details:**
165
+ - Reason: ${statusMessage}
166
+ - Amount: ${result.transaction.amount ? `${result.transaction.amount} ${result.transaction.currency}` : 'N/A'}
167
+ - Cancelled At: ${result.timestamp}
168
+ - Cancelled By: Transaction Creator (Merchant)
169
+
170
+ **API Response:**
171
+ \`\`\`json
172
+ ${JSON.stringify(result.transaction, null, 2)}
173
+ \`\`\`
174
+
175
+ **Request ID:** ${result.requestId || 'N/A'}
176
+ **Timestamp:** ${result.timestamp}
177
+
178
+ **Important Notes:**
179
+ - ✅ Transaction successfully cancelled by its creator
180
+ - 🔒 No payment processing will occur
181
+ - 📧 Callbacks may be sent to notify interested parties
182
+ - ⏰ Cancellation is permanent and cannot be undone
183
+ - 👤 Only the merchant who created this transaction can cancel it
184
+
185
+ **Merchant Cancellation Rules:**
186
+ - ✅ Can cancel: Active transactions you created
187
+ - ❌ Cannot cancel: Transactions created by others
188
+ - ❌ Cannot cancel: Already processed transactions
189
+ - ❌ Cannot cancel: Expired transactions
190
+
191
+ **Next Steps:**
192
+ 1. Wait for callback confirmation (if configured)
193
+ 2. Update your internal systems
194
+ 3. Notify customers if applicable
195
+ 4. Create new transaction if payment still needed`
196
+ }]
197
+ };
198
+ } else {
199
+ return {
200
+ content: [{
201
+ type: "text",
202
+ text: `❌ **Transaction Cancellation Failed**
203
+
204
+ **Transaction ID:** ${transactionId}
205
+
206
+ **Error Details:**
207
+ - Message: ${result.error.message}
208
+ - Status: ${result.error.status || 'N/A'}
209
+ - Code: ${result.error.code || 'N/A'}
210
+
211
+ **Full Error Response:**
212
+ \`\`\`json
213
+ ${JSON.stringify(result.error.details || result.error, null, 2)}
214
+ \`\`\`
215
+
216
+ **Timestamp:** ${result.timestamp}
217
+
218
+ **Common Issues:**
219
+ 1. **ERR_MISSING_TRANSACTION**: Transaction not found or expired
220
+ 2. **ERR_ALREADY_PROCESSED**: Transaction already processed, cannot cancel
221
+ 3. **ERR_UNAUTHORIZED_OPERATION**: Only transaction creator can cancel
222
+ 4. **ERR_INVALID_STATUS_MESSAGE**: Status message invalid or too long
223
+
224
+ **Merchant Cancellation Restrictions:**
225
+ - ⚠️ **Creator Only**: Only the merchant who created the transaction can cancel it
226
+ - ⚠️ **Active Only**: Transaction must be in ACTIVE state (not processed/expired)
227
+ - ⚠️ **Before Processing**: Cannot cancel after merchant/PI has processed it
228
+ - ⚠️ **Before Expiry**: Cannot cancel expired transactions
229
+
230
+ **Troubleshooting:**
231
+ 1. Verify transaction ID is correct: 10 characters total, starts with 'pw' (standard), 'pr' (product), or 'ps' (soundbite)
232
+ 2. **Ensure you are the transaction creator** (not just any merchant)
233
+ 3. Check transaction hasn't been processed already by anyone
234
+ 4. Verify status message is under 100 characters
235
+ 5. Ensure transaction is still active (not expired)
236
+ 6. Confirm you created this transaction with your merchant account`
237
+ }]
238
+ };
239
+ }
240
+ }
241
+ };