@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.
- package/.env.example +92 -0
- package/MCP-SETUP.md +190 -0
- package/README.md +483 -0
- package/check-keys.js +52 -0
- package/keys/README.md +112 -0
- package/mcp-config.json +9 -0
- package/package.json +66 -0
- package/payware-mcp-config.json +12 -0
- package/src/auth/rsa-keys.js +82 -0
- package/src/auth/sandbox-setup.js +187 -0
- package/src/config/env.js +229 -0
- package/src/config/partner-types.js +143 -0
- package/src/core/auth/jwt-factory.js +182 -0
- package/src/core/auth/jwt-token.js +735 -0
- package/src/core/utils/json-serializer.js +100 -0
- package/src/core/utils/key-utils.js +133 -0
- package/src/index.js +237 -0
- package/src/isv/data/index.js +291 -0
- package/src/isv/deep-links/delete-product-link.js +154 -0
- package/src/isv/deep-links/delete-transaction-link.js +154 -0
- package/src/isv/deep-links/get-product-link.js +167 -0
- package/src/isv/deep-links/get-transaction-link.js +167 -0
- package/src/isv/deep-links/index.js +23 -0
- package/src/isv/index.js +103 -0
- package/src/isv/oauth2/create-token-simple.js +46 -0
- package/src/isv/oauth2/get-token-info.js +157 -0
- package/src/isv/oauth2/obtain-token.js +189 -0
- package/src/isv/oauth2/token-helper.js +151 -0
- package/src/isv/products/audios.js +470 -0
- package/src/isv/products/create-product.js +314 -0
- package/src/isv/products/delete-product.js +119 -0
- package/src/isv/products/get-product-image.js +247 -0
- package/src/isv/products/get-product.js +168 -0
- package/src/isv/products/index.js +65 -0
- package/src/isv/products/list-products.js +136 -0
- package/src/isv/products/schedules.js +522 -0
- package/src/isv/products/update-product.js +265 -0
- package/src/isv/transactions/cancel-transaction.js +129 -0
- package/src/isv/transactions/create-transaction.js +361 -0
- package/src/isv/transactions/get-status.js +217 -0
- package/src/isv/transactions/process-transaction.js +145 -0
- package/src/isv/transactions/simulate-callback.js +146 -0
- package/src/isv/transactions/transaction-history.js +186 -0
- package/src/isv/utils/auth.js +159 -0
- package/src/mcp-proxy.js +150 -0
- package/src/merchant/data/export-report.js +108 -0
- package/src/merchant/data/generate-report.js +151 -0
- package/src/merchant/data/get-report-status.js +120 -0
- package/src/merchant/data/index.js +351 -0
- package/src/merchant/deep-links/delete-product-link.js +121 -0
- package/src/merchant/deep-links/delete-transaction-link.js +121 -0
- package/src/merchant/deep-links/get-product-link.js +134 -0
- package/src/merchant/deep-links/get-transaction-link.js +134 -0
- package/src/merchant/deep-links/index.js +23 -0
- package/src/merchant/index.js +92 -0
- package/src/merchant/products/audios.js +606 -0
- package/src/merchant/products/create-product.js +288 -0
- package/src/merchant/products/delete-product.js +111 -0
- package/src/merchant/products/get-product-image.js +252 -0
- package/src/merchant/products/get-product.js +144 -0
- package/src/merchant/products/index.js +65 -0
- package/src/merchant/products/list-products.js +112 -0
- package/src/merchant/products/schedules.js +646 -0
- package/src/merchant/products/update-product.js +282 -0
- package/src/merchant/transactions/cancel-transaction.js +241 -0
- package/src/merchant/transactions/create-transaction.js +482 -0
- package/src/merchant/transactions/get-status.js +196 -0
- package/src/merchant/transactions/process-transaction.js +349 -0
- package/src/merchant/transactions/simulate-callback.js +246 -0
- package/src/merchant/transactions/transaction-history.js +258 -0
- package/src/payment-institution/data/index.js +280 -0
- package/src/payment-institution/deep-links/delete-transaction-link.js +128 -0
- package/src/payment-institution/deep-links/get-transaction-link.js +141 -0
- package/src/payment-institution/deep-links/index.js +17 -0
- package/src/payment-institution/index.js +92 -0
- package/src/payment-institution/transactions/create-pi-transaction.js +484 -0
- package/src/payment-institution/transactions/finalize-transaction.js +340 -0
- package/src/payment-institution/transactions/get-transaction-history.js +269 -0
- package/src/payment-institution/transactions/get-transaction-status.js +234 -0
- package/src/payment-institution/transactions/process-pi-transaction.js +421 -0
- package/src/payment-institution/transactions/simulate-pi-callback.js +326 -0
- package/src/payment-institution/transactions/soundbite-transaction.js +269 -0
- package/src/proxy-server.js +205 -0
- package/src/shared/data/data-api.js +372 -0
- package/src/shared/deep-links/api.js +214 -0
- package/src/shared/index.js +44 -0
- package/src/shared/products/products-api.js +872 -0
- package/src/tools/advanced-code-generator.js +2247 -0
- package/src/utils/code-examples/common/auth-examples.js +1482 -0
- package/src/utils/code-examples/common/helpers.js +416 -0
- package/src/utils/code-examples/common/utils-examples.js +591 -0
- package/src/utils/code-examples/frameworks/examples.js +859 -0
- package/src/utils/code-examples/index.js +441 -0
- package/src/utils/code-examples/isv/oauth2.js +600 -0
- package/src/utils/code-examples/isv/products.js +666 -0
- package/src/utils/code-examples/isv/transactions.js +813 -0
- package/src/utils/code-examples/merchant/products.js +497 -0
- package/src/utils/code-examples/merchant/transactions.js +623 -0
- package/src/utils/code-examples/payment-institution/transactions.js +388 -0
- package/src/utils/code-examples/shared/data.js +607 -0
- package/src/utils/code-examples/shared/deep-links.js +662 -0
- package/src/utils/code-examples/shared/soundbites.js +808 -0
- package/src/utils/code-examples.js +646 -0
- package/src/utils/format-request.js +363 -0
- package/src/utils/json-formatter.js +249 -0
- package/src/utils/server-info.js +132 -0
|
@@ -0,0 +1,859 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Framework-specific integration examples
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { ExampleGenerator } from '../common/helpers.js';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Django REST Framework integration
|
|
9
|
+
*/
|
|
10
|
+
export const DjangoExamples = {
|
|
11
|
+
/**
|
|
12
|
+
* Django view for transaction creation
|
|
13
|
+
*/
|
|
14
|
+
transactionView: `
|
|
15
|
+
# views.py - Django REST Framework implementation
|
|
16
|
+
from rest_framework.views import APIView
|
|
17
|
+
from rest_framework.response import Response
|
|
18
|
+
from rest_framework import status
|
|
19
|
+
from django.conf import settings
|
|
20
|
+
import logging
|
|
21
|
+
|
|
22
|
+
from .payware_client import PaywareClient
|
|
23
|
+
from .serializers import TransactionSerializer
|
|
24
|
+
|
|
25
|
+
logger = logging.getLogger(__name__)
|
|
26
|
+
|
|
27
|
+
class PaywareTransactionView(APIView):
|
|
28
|
+
"""
|
|
29
|
+
Handle payware transaction operations
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
def __init__(self):
|
|
33
|
+
super().__init__()
|
|
34
|
+
self.payware_client = PaywareClient(
|
|
35
|
+
partner_id=settings.PAYWARE_PARTNER_ID,
|
|
36
|
+
private_key_path=settings.PAYWARE_PRIVATE_KEY_PATH,
|
|
37
|
+
use_sandbox=settings.PAYWARE_USE_SANDBOX
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
def post(self, request):
|
|
41
|
+
"""Create a new transaction"""
|
|
42
|
+
serializer = TransactionSerializer(data=request.data)
|
|
43
|
+
|
|
44
|
+
if not serializer.is_valid():
|
|
45
|
+
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
|
46
|
+
|
|
47
|
+
try:
|
|
48
|
+
# Prepare transaction data
|
|
49
|
+
transaction_data = {
|
|
50
|
+
'trData': {
|
|
51
|
+
'amount': serializer.validated_data['amount'],
|
|
52
|
+
'currency': serializer.validated_data.get('currency', 'EUR'),
|
|
53
|
+
'reasonL1': serializer.validated_data['description']
|
|
54
|
+
},
|
|
55
|
+
'trOptions': {
|
|
56
|
+
'type': 'PLAIN',
|
|
57
|
+
'timeToLive': 120
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
# Create transaction via payware API
|
|
62
|
+
result = self.payware_client.create_transaction(transaction_data)
|
|
63
|
+
|
|
64
|
+
if result:
|
|
65
|
+
# Log successful transaction creation
|
|
66
|
+
logger.info(f"Transaction created: {result.get('transactionId')}")
|
|
67
|
+
|
|
68
|
+
return Response({
|
|
69
|
+
'success': True,
|
|
70
|
+
'transaction_id': result.get('transactionId'),
|
|
71
|
+
'amount': result.get('amount'),
|
|
72
|
+
'status': result.get('status'),
|
|
73
|
+
'payment_url': result.get('paymentUrl')
|
|
74
|
+
}, status=status.HTTP_201_CREATED)
|
|
75
|
+
else:
|
|
76
|
+
logger.error("Failed to create transaction")
|
|
77
|
+
return Response({
|
|
78
|
+
'success': False,
|
|
79
|
+
'error': 'Transaction creation failed'
|
|
80
|
+
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
|
81
|
+
|
|
82
|
+
except Exception as e:
|
|
83
|
+
logger.error(f"Transaction creation error: {str(e)}")
|
|
84
|
+
return Response({
|
|
85
|
+
'success': False,
|
|
86
|
+
'error': 'Internal server error'
|
|
87
|
+
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
|
88
|
+
|
|
89
|
+
def get(self, request, transaction_id=None):
|
|
90
|
+
"""Get transaction status"""
|
|
91
|
+
if not transaction_id:
|
|
92
|
+
return Response({
|
|
93
|
+
'error': 'Transaction ID required'
|
|
94
|
+
}, status=status.HTTP_400_BAD_REQUEST)
|
|
95
|
+
|
|
96
|
+
try:
|
|
97
|
+
result = self.payware_client.get_transaction_status(transaction_id)
|
|
98
|
+
|
|
99
|
+
if result:
|
|
100
|
+
return Response({
|
|
101
|
+
'success': True,
|
|
102
|
+
'transaction_id': transaction_id,
|
|
103
|
+
'status': result.get('status'),
|
|
104
|
+
'amount': result.get('amount'),
|
|
105
|
+
'currency': result.get('currency'),
|
|
106
|
+
'created_at': result.get('createdAt'),
|
|
107
|
+
'updated_at': result.get('updatedAt')
|
|
108
|
+
})
|
|
109
|
+
else:
|
|
110
|
+
return Response({
|
|
111
|
+
'error': 'Transaction not found'
|
|
112
|
+
}, status=status.HTTP_404_NOT_FOUND)
|
|
113
|
+
|
|
114
|
+
except Exception as e:
|
|
115
|
+
logger.error(f"Get transaction error: {str(e)}")
|
|
116
|
+
return Response({
|
|
117
|
+
'error': 'Internal server error'
|
|
118
|
+
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
|
119
|
+
`,
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Django settings configuration
|
|
123
|
+
*/
|
|
124
|
+
settings: `
|
|
125
|
+
# settings.py - Django configuration for payware
|
|
126
|
+
import os
|
|
127
|
+
from pathlib import Path
|
|
128
|
+
|
|
129
|
+
BASE_DIR = Path(__file__).resolve().parent.parent
|
|
130
|
+
|
|
131
|
+
# payware Configuration
|
|
132
|
+
PAYWARE_PARTNER_ID = os.getenv('PAYWARE_PARTNER_ID')
|
|
133
|
+
PAYWARE_PRIVATE_KEY_PATH = os.path.join(BASE_DIR, 'keys', 'sandbox-merchant-private-key.pem')
|
|
134
|
+
PAYWARE_USE_SANDBOX = os.getenv('PAYWARE_USE_SANDBOX', 'true').lower() == 'true'
|
|
135
|
+
PAYWARE_SANDBOX_URL = 'https://sandbox.payware.eu/api'
|
|
136
|
+
PAYWARE_PRODUCTION_URL = 'https://api.payware.eu/api'
|
|
137
|
+
|
|
138
|
+
# Logging configuration
|
|
139
|
+
LOGGING = {
|
|
140
|
+
'version': 1,
|
|
141
|
+
'disable_existing_loggers': False,
|
|
142
|
+
'handlers': {
|
|
143
|
+
'file': {
|
|
144
|
+
'level': 'INFO',
|
|
145
|
+
'class': 'logging.FileHandler',
|
|
146
|
+
'filename': 'payware.log',
|
|
147
|
+
},
|
|
148
|
+
'console': {
|
|
149
|
+
'level': 'DEBUG',
|
|
150
|
+
'class': 'logging.StreamHandler',
|
|
151
|
+
},
|
|
152
|
+
},
|
|
153
|
+
'loggers': {
|
|
154
|
+
'payware': {
|
|
155
|
+
'handlers': ['file', 'console'],
|
|
156
|
+
'level': 'INFO',
|
|
157
|
+
'propagate': True,
|
|
158
|
+
},
|
|
159
|
+
},
|
|
160
|
+
}
|
|
161
|
+
`,
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Django URL patterns
|
|
165
|
+
*/
|
|
166
|
+
urls: `
|
|
167
|
+
# urls.py - Django URL configuration
|
|
168
|
+
from django.urls import path
|
|
169
|
+
from .views import PaywareTransactionView
|
|
170
|
+
|
|
171
|
+
urlpatterns = [
|
|
172
|
+
path('api/transactions/', PaywareTransactionView.as_view(), name='create_transaction'),
|
|
173
|
+
path('api/transactions/<str:transaction_id>/', PaywareTransactionView.as_view(), name='get_transaction'),
|
|
174
|
+
]
|
|
175
|
+
`
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Express.js integration
|
|
180
|
+
*/
|
|
181
|
+
export const ExpressExamples = {
|
|
182
|
+
/**
|
|
183
|
+
* Express.js routes for payware integration
|
|
184
|
+
*/
|
|
185
|
+
routes: `
|
|
186
|
+
// routes/payware.js - Express.js implementation
|
|
187
|
+
const express = require('express');
|
|
188
|
+
const router = express.Router();
|
|
189
|
+
const PaywareClient = require('../lib/payware-client');
|
|
190
|
+
const { body, param, validationResult } = require('express-validator');
|
|
191
|
+
|
|
192
|
+
// Initialize payware client
|
|
193
|
+
const paywareClient = new PaywareClient({
|
|
194
|
+
partnerId: process.env.PAYWARE_PARTNER_ID,
|
|
195
|
+
privateKeyPath: process.env.PAYWARE_PRIVATE_KEY_PATH,
|
|
196
|
+
useSandbox: process.env.PAYWARE_USE_SANDBOX === 'true'
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Create transaction
|
|
201
|
+
*/
|
|
202
|
+
router.post('/transactions',
|
|
203
|
+
// Validation middleware
|
|
204
|
+
body('amount').isFloat({ min: 0.01 }).withMessage('Amount must be a positive number'),
|
|
205
|
+
body('currency').optional().isIn(['EUR', 'USD', 'GBP']).withMessage('Invalid currency'),
|
|
206
|
+
body('description').isLength({ min: 1, max: 255 }).withMessage('Description required'),
|
|
207
|
+
|
|
208
|
+
async (req, res) => {
|
|
209
|
+
// Check validation results
|
|
210
|
+
const errors = validationResult(req);
|
|
211
|
+
if (!errors.isEmpty()) {
|
|
212
|
+
return res.status(400).json({
|
|
213
|
+
success: false,
|
|
214
|
+
errors: errors.array()
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
try {
|
|
219
|
+
const { amount, currency = 'EUR', description } = req.body;
|
|
220
|
+
|
|
221
|
+
// Prepare transaction data
|
|
222
|
+
const transactionData = {
|
|
223
|
+
trData: {
|
|
224
|
+
amount: amount.toString(),
|
|
225
|
+
currency,
|
|
226
|
+
reasonL1: description
|
|
227
|
+
},
|
|
228
|
+
trOptions: {
|
|
229
|
+
type: 'PLAIN',
|
|
230
|
+
timeToLive: 120
|
|
231
|
+
}
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
// Create transaction
|
|
235
|
+
const result = await paywareClient.createTransaction(transactionData);
|
|
236
|
+
|
|
237
|
+
if (result) {
|
|
238
|
+
console.log(\`Transaction created: \${result.transactionId}\`);
|
|
239
|
+
|
|
240
|
+
res.status(201).json({
|
|
241
|
+
success: true,
|
|
242
|
+
transactionId: result.transactionId,
|
|
243
|
+
amount: result.amount,
|
|
244
|
+
status: result.status,
|
|
245
|
+
paymentUrl: result.paymentUrl
|
|
246
|
+
});
|
|
247
|
+
} else {
|
|
248
|
+
res.status(500).json({
|
|
249
|
+
success: false,
|
|
250
|
+
error: 'Transaction creation failed'
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
} catch (error) {
|
|
255
|
+
console.error('Transaction creation error:', error);
|
|
256
|
+
res.status(500).json({
|
|
257
|
+
success: false,
|
|
258
|
+
error: 'Internal server error'
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
);
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Get transaction status
|
|
266
|
+
*/
|
|
267
|
+
router.get('/transactions/:transactionId',
|
|
268
|
+
param('transactionId').isLength({ min: 1 }).withMessage('Transaction ID required'),
|
|
269
|
+
|
|
270
|
+
async (req, res) => {
|
|
271
|
+
const errors = validationResult(req);
|
|
272
|
+
if (!errors.isEmpty()) {
|
|
273
|
+
return res.status(400).json({
|
|
274
|
+
success: false,
|
|
275
|
+
errors: errors.array()
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
try {
|
|
280
|
+
const { transactionId } = req.params;
|
|
281
|
+
const result = await paywareClient.getTransactionStatus(transactionId);
|
|
282
|
+
|
|
283
|
+
if (result) {
|
|
284
|
+
res.json({
|
|
285
|
+
success: true,
|
|
286
|
+
transactionId,
|
|
287
|
+
status: result.status,
|
|
288
|
+
amount: result.amount,
|
|
289
|
+
currency: result.currency,
|
|
290
|
+
createdAt: result.createdAt,
|
|
291
|
+
updatedAt: result.updatedAt
|
|
292
|
+
});
|
|
293
|
+
} else {
|
|
294
|
+
res.status(404).json({
|
|
295
|
+
success: false,
|
|
296
|
+
error: 'Transaction not found'
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
} catch (error) {
|
|
301
|
+
console.error('Get transaction error:', error);
|
|
302
|
+
res.status(500).json({
|
|
303
|
+
success: false,
|
|
304
|
+
error: 'Internal server error'
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
);
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* Process transaction (merchant)
|
|
312
|
+
*/
|
|
313
|
+
router.post('/transactions/:transactionId/process',
|
|
314
|
+
param('transactionId').isLength({ min: 1 }).withMessage('Transaction ID required'),
|
|
315
|
+
body('account').isLength({ min: 1 }).withMessage('Account required'),
|
|
316
|
+
body('friendlyName').isLength({ min: 1 }).withMessage('Friendly name required'),
|
|
317
|
+
|
|
318
|
+
async (req, res) => {
|
|
319
|
+
const errors = validationResult(req);
|
|
320
|
+
if (!errors.isEmpty()) {
|
|
321
|
+
return res.status(400).json({
|
|
322
|
+
success: false,
|
|
323
|
+
errors: errors.array()
|
|
324
|
+
});
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
try {
|
|
328
|
+
const { transactionId } = req.params;
|
|
329
|
+
const { account, friendlyName } = req.body;
|
|
330
|
+
|
|
331
|
+
const processData = {
|
|
332
|
+
account,
|
|
333
|
+
friendlyName
|
|
334
|
+
};
|
|
335
|
+
|
|
336
|
+
const result = await paywareClient.processTransaction(transactionId, processData);
|
|
337
|
+
|
|
338
|
+
if (result) {
|
|
339
|
+
res.json({
|
|
340
|
+
success: true,
|
|
341
|
+
transactionId,
|
|
342
|
+
status: result.status,
|
|
343
|
+
processedAt: result.processedAt
|
|
344
|
+
});
|
|
345
|
+
} else {
|
|
346
|
+
res.status(500).json({
|
|
347
|
+
success: false,
|
|
348
|
+
error: 'Transaction processing failed'
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
} catch (error) {
|
|
353
|
+
console.error('Process transaction error:', error);
|
|
354
|
+
res.status(500).json({
|
|
355
|
+
success: false,
|
|
356
|
+
error: 'Internal server error'
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
);
|
|
361
|
+
|
|
362
|
+
module.exports = router;
|
|
363
|
+
`,
|
|
364
|
+
|
|
365
|
+
/**
|
|
366
|
+
* Express.js middleware
|
|
367
|
+
*/
|
|
368
|
+
middleware: `
|
|
369
|
+
// middleware/payware-auth.js - Express.js authentication middleware
|
|
370
|
+
const PaywareAuthenticator = require('../lib/payware-authenticator');
|
|
371
|
+
|
|
372
|
+
/**
|
|
373
|
+
* Middleware to authenticate payware API requests
|
|
374
|
+
*/
|
|
375
|
+
function paywareAuthMiddleware(req, res, next) {
|
|
376
|
+
try {
|
|
377
|
+
// Initialize authenticator if not already done
|
|
378
|
+
if (!req.paywareAuth) {
|
|
379
|
+
req.paywareAuth = new PaywareAuthenticator({
|
|
380
|
+
partnerId: process.env.PAYWARE_PARTNER_ID,
|
|
381
|
+
privateKeyPath: process.env.PAYWARE_PRIVATE_KEY_PATH,
|
|
382
|
+
useSandbox: process.env.PAYWARE_USE_SANDBOX === 'true'
|
|
383
|
+
});
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
next();
|
|
387
|
+
} catch (error) {
|
|
388
|
+
console.error('payware authentication setup error:', error);
|
|
389
|
+
res.status(500).json({
|
|
390
|
+
success: false,
|
|
391
|
+
error: 'Authentication setup failed'
|
|
392
|
+
});
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
/**
|
|
397
|
+
* Middleware to log payware API requests
|
|
398
|
+
*/
|
|
399
|
+
function paywareLoggingMiddleware(req, res, next) {
|
|
400
|
+
const start = Date.now();
|
|
401
|
+
|
|
402
|
+
// Log request
|
|
403
|
+
console.log(\`[payware] \${req.method} \${req.path} - Request started\`);
|
|
404
|
+
|
|
405
|
+
// Override res.json to log response
|
|
406
|
+
const originalJson = res.json;
|
|
407
|
+
res.json = function(body) {
|
|
408
|
+
const duration = Date.now() - start;
|
|
409
|
+
console.log(\`[payware] \${req.method} \${req.path} - \${res.statusCode} (\${duration}ms)\`);
|
|
410
|
+
|
|
411
|
+
if (res.statusCode >= 400) {
|
|
412
|
+
console.error(\`[payware] Error response:\`, body);
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
return originalJson.call(this, body);
|
|
416
|
+
};
|
|
417
|
+
|
|
418
|
+
next();
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
module.exports = {
|
|
422
|
+
paywareAuthMiddleware,
|
|
423
|
+
paywareLoggingMiddleware
|
|
424
|
+
};
|
|
425
|
+
`,
|
|
426
|
+
|
|
427
|
+
/**
|
|
428
|
+
* Express.js app setup
|
|
429
|
+
*/
|
|
430
|
+
app: `
|
|
431
|
+
// app.js - Express.js application setup
|
|
432
|
+
const express = require('express');
|
|
433
|
+
const cors = require('cors');
|
|
434
|
+
const helmet = require('helmet');
|
|
435
|
+
const rateLimit = require('express-rate-limit');
|
|
436
|
+
require('dotenv').config();
|
|
437
|
+
|
|
438
|
+
// Import routes and middleware
|
|
439
|
+
const paywareRoutes = require('./routes/payware');
|
|
440
|
+
const { paywareAuthMiddleware, paywareLoggingMiddleware } = require('./middleware/payware-auth');
|
|
441
|
+
|
|
442
|
+
const app = express();
|
|
443
|
+
|
|
444
|
+
// Security middleware
|
|
445
|
+
app.use(helmet());
|
|
446
|
+
app.use(cors({
|
|
447
|
+
origin: process.env.ALLOWED_ORIGINS?.split(',') || ['http://localhost:3000'],
|
|
448
|
+
credentials: true
|
|
449
|
+
}));
|
|
450
|
+
|
|
451
|
+
// Rate limiting
|
|
452
|
+
const limiter = rateLimit({
|
|
453
|
+
windowMs: 15 * 60 * 1000, // 15 minutes
|
|
454
|
+
max: 100, // limit each IP to 100 requests per windowMs
|
|
455
|
+
message: 'Too many requests from this IP'
|
|
456
|
+
});
|
|
457
|
+
app.use('/api/', limiter);
|
|
458
|
+
|
|
459
|
+
// Body parsing
|
|
460
|
+
app.use(express.json({ limit: '10mb' }));
|
|
461
|
+
app.use(express.urlencoded({ extended: true }));
|
|
462
|
+
|
|
463
|
+
// payware-specific middleware
|
|
464
|
+
app.use('/api/payware', paywareLoggingMiddleware);
|
|
465
|
+
app.use('/api/payware', paywareAuthMiddleware);
|
|
466
|
+
|
|
467
|
+
// Routes
|
|
468
|
+
app.use('/api/payware', paywareRoutes);
|
|
469
|
+
|
|
470
|
+
// Health check endpoint
|
|
471
|
+
app.get('/health', (req, res) => {
|
|
472
|
+
res.json({
|
|
473
|
+
status: 'healthy',
|
|
474
|
+
timestamp: new Date().toISOString(),
|
|
475
|
+
payware: {
|
|
476
|
+
configured: !!(process.env.PAYWARE_PARTNER_ID && process.env.PAYWARE_PRIVATE_KEY_PATH),
|
|
477
|
+
sandbox: process.env.PAYWARE_USE_SANDBOX === 'true'
|
|
478
|
+
}
|
|
479
|
+
});
|
|
480
|
+
});
|
|
481
|
+
|
|
482
|
+
// Error handling middleware
|
|
483
|
+
app.use((error, req, res, next) => {
|
|
484
|
+
console.error('Application error:', error);
|
|
485
|
+
|
|
486
|
+
res.status(error.status || 500).json({
|
|
487
|
+
success: false,
|
|
488
|
+
error: process.env.NODE_ENV === 'production'
|
|
489
|
+
? 'Internal server error'
|
|
490
|
+
: error.message
|
|
491
|
+
});
|
|
492
|
+
});
|
|
493
|
+
|
|
494
|
+
// 404 handler
|
|
495
|
+
app.use((req, res) => {
|
|
496
|
+
res.status(404).json({
|
|
497
|
+
success: false,
|
|
498
|
+
error: 'Endpoint not found'
|
|
499
|
+
});
|
|
500
|
+
});
|
|
501
|
+
|
|
502
|
+
const PORT = process.env.PORT || 3000;
|
|
503
|
+
|
|
504
|
+
app.listen(PORT, () => {
|
|
505
|
+
console.log(\`Server running on port \${PORT}\`);
|
|
506
|
+
console.log(\`payware sandbox mode: \${process.env.PAYWARE_USE_SANDBOX === 'true'}\`);
|
|
507
|
+
});
|
|
508
|
+
|
|
509
|
+
module.exports = app;
|
|
510
|
+
`
|
|
511
|
+
};
|
|
512
|
+
|
|
513
|
+
/**
|
|
514
|
+
* Laravel integration
|
|
515
|
+
*/
|
|
516
|
+
export const LaravelExamples = {
|
|
517
|
+
/**
|
|
518
|
+
* Laravel controller for payware
|
|
519
|
+
*/
|
|
520
|
+
controller: `
|
|
521
|
+
<?php
|
|
522
|
+
// app/Http/Controllers/PaywareController.php - Laravel implementation
|
|
523
|
+
|
|
524
|
+
namespace App\\Http\\Controllers;
|
|
525
|
+
|
|
526
|
+
use Illuminate\\Http\\Request;
|
|
527
|
+
use Illuminate\\Http\\Response;
|
|
528
|
+
use Illuminate\\Support\\Facades\\Log;
|
|
529
|
+
use Illuminate\\Support\\Facades\\Validator;
|
|
530
|
+
use App\\Services\\PaywareService;
|
|
531
|
+
use App\\Http\\Requests\\CreateTransactionRequest;
|
|
532
|
+
|
|
533
|
+
class PaywareController extends Controller
|
|
534
|
+
{
|
|
535
|
+
protected $paywareService;
|
|
536
|
+
|
|
537
|
+
public function __construct(PaywareService $paywareService)
|
|
538
|
+
{
|
|
539
|
+
$this->paywareService = $paywareService;
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
/**
|
|
543
|
+
* Create a new transaction
|
|
544
|
+
*/
|
|
545
|
+
public function createTransaction(CreateTransactionRequest $request)
|
|
546
|
+
{
|
|
547
|
+
try {
|
|
548
|
+
$validated = $request->validated();
|
|
549
|
+
|
|
550
|
+
$transactionData = [
|
|
551
|
+
'trData' => [
|
|
552
|
+
'amount' => $validated['amount'],
|
|
553
|
+
'currency' => $validated['currency'] ?? 'EUR',
|
|
554
|
+
'reasonL1' => $validated['description']
|
|
555
|
+
],
|
|
556
|
+
'trOptions' => [
|
|
557
|
+
'type' => 'PLAIN',
|
|
558
|
+
'timeToLive' => 120
|
|
559
|
+
]
|
|
560
|
+
];
|
|
561
|
+
|
|
562
|
+
$result = $this->paywareService->createTransaction($transactionData);
|
|
563
|
+
|
|
564
|
+
if ($result) {
|
|
565
|
+
Log::info('Transaction created', ['transaction_id' => $result['transactionId']]);
|
|
566
|
+
|
|
567
|
+
return response()->json([
|
|
568
|
+
'success' => true,
|
|
569
|
+
'transaction_id' => $result['transactionId'],
|
|
570
|
+
'amount' => $result['amount'],
|
|
571
|
+
'status' => $result['status'],
|
|
572
|
+
'payment_url' => $result['paymentUrl']
|
|
573
|
+
], Response::HTTP_CREATED);
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
return response()->json([
|
|
577
|
+
'success' => false,
|
|
578
|
+
'error' => 'Transaction creation failed'
|
|
579
|
+
], Response::HTTP_INTERNAL_SERVER_ERROR);
|
|
580
|
+
|
|
581
|
+
} catch (\\Exception $e) {
|
|
582
|
+
Log::error('Transaction creation error', ['error' => $e->getMessage()]);
|
|
583
|
+
|
|
584
|
+
return response()->json([
|
|
585
|
+
'success' => false,
|
|
586
|
+
'error' => 'Internal server error'
|
|
587
|
+
], Response::HTTP_INTERNAL_SERVER_ERROR);
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
/**
|
|
592
|
+
* Get transaction status
|
|
593
|
+
*/
|
|
594
|
+
public function getTransactionStatus(string $transactionId)
|
|
595
|
+
{
|
|
596
|
+
try {
|
|
597
|
+
$result = $this->paywareService->getTransactionStatus($transactionId);
|
|
598
|
+
|
|
599
|
+
if ($result) {
|
|
600
|
+
return response()->json([
|
|
601
|
+
'success' => true,
|
|
602
|
+
'transaction_id' => $transactionId,
|
|
603
|
+
'status' => $result['status'],
|
|
604
|
+
'amount' => $result['amount'],
|
|
605
|
+
'currency' => $result['currency'],
|
|
606
|
+
'created_at' => $result['createdAt'],
|
|
607
|
+
'updated_at' => $result['updatedAt']
|
|
608
|
+
]);
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
return response()->json([
|
|
612
|
+
'success' => false,
|
|
613
|
+
'error' => 'Transaction not found'
|
|
614
|
+
], Response::HTTP_NOT_FOUND);
|
|
615
|
+
|
|
616
|
+
} catch (\\Exception $e) {
|
|
617
|
+
Log::error('Get transaction error', ['error' => $e->getMessage()]);
|
|
618
|
+
|
|
619
|
+
return response()->json([
|
|
620
|
+
'success' => false,
|
|
621
|
+
'error' => 'Internal server error'
|
|
622
|
+
], Response::HTTP_INTERNAL_SERVER_ERROR);
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
/**
|
|
627
|
+
* Process transaction
|
|
628
|
+
*/
|
|
629
|
+
public function processTransaction(Request $request, string $transactionId)
|
|
630
|
+
{
|
|
631
|
+
$validator = Validator::make($request->all(), [
|
|
632
|
+
'account' => 'required|string|max:255',
|
|
633
|
+
'friendly_name' => 'required|string|max:255'
|
|
634
|
+
]);
|
|
635
|
+
|
|
636
|
+
if ($validator->fails()) {
|
|
637
|
+
return response()->json([
|
|
638
|
+
'success' => false,
|
|
639
|
+
'errors' => $validator->errors()
|
|
640
|
+
], Response::HTTP_BAD_REQUEST);
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
try {
|
|
644
|
+
$processData = [
|
|
645
|
+
'account' => $request->account,
|
|
646
|
+
'friendlyName' => $request->friendly_name
|
|
647
|
+
];
|
|
648
|
+
|
|
649
|
+
$result = $this->paywareService->processTransaction($transactionId, $processData);
|
|
650
|
+
|
|
651
|
+
if ($result) {
|
|
652
|
+
return response()->json([
|
|
653
|
+
'success' => true,
|
|
654
|
+
'transaction_id' => $transactionId,
|
|
655
|
+
'status' => $result['status'],
|
|
656
|
+
'processed_at' => $result['processedAt']
|
|
657
|
+
]);
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
return response()->json([
|
|
661
|
+
'success' => false,
|
|
662
|
+
'error' => 'Transaction processing failed'
|
|
663
|
+
], Response::HTTP_INTERNAL_SERVER_ERROR);
|
|
664
|
+
|
|
665
|
+
} catch (\\Exception $e) {
|
|
666
|
+
Log::error('Process transaction error', ['error' => $e->getMessage()]);
|
|
667
|
+
|
|
668
|
+
return response()->json([
|
|
669
|
+
'success' => false,
|
|
670
|
+
'error' => 'Internal server error'
|
|
671
|
+
], Response::HTTP_INTERNAL_SERVER_ERROR);
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
`,
|
|
676
|
+
|
|
677
|
+
/**
|
|
678
|
+
* Laravel service class
|
|
679
|
+
*/
|
|
680
|
+
service: `
|
|
681
|
+
<?php
|
|
682
|
+
// app/Services/PaywareService.php - Laravel service for payware integration
|
|
683
|
+
|
|
684
|
+
namespace App\\Services;
|
|
685
|
+
|
|
686
|
+
use Illuminate\\Support\\Facades\\Http;
|
|
687
|
+
use Illuminate\\Support\\Facades\\Log;
|
|
688
|
+
use Illuminate\\Support\\Facades\\Cache;
|
|
689
|
+
use App\\Exceptions\\PaywareException;
|
|
690
|
+
|
|
691
|
+
class PaywareService
|
|
692
|
+
{
|
|
693
|
+
protected $partnerId;
|
|
694
|
+
protected $privateKeyPath;
|
|
695
|
+
protected $useSandbox;
|
|
696
|
+
protected $baseUrl;
|
|
697
|
+
|
|
698
|
+
public function __construct()
|
|
699
|
+
{
|
|
700
|
+
$this->partnerId = config('services.payware.partner_id');
|
|
701
|
+
$this->privateKeyPath = config('services.payware.private_key_path');
|
|
702
|
+
$this->useSandbox = config('services.payware.use_sandbox', true);
|
|
703
|
+
$this->baseUrl = $this->useSandbox
|
|
704
|
+
? config('services.payware.sandbox_url')
|
|
705
|
+
: config('services.payware.production_url');
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
/**
|
|
709
|
+
* Create JWT token for API authentication
|
|
710
|
+
*/
|
|
711
|
+
protected function createJWTToken($requestBody = null)
|
|
712
|
+
{
|
|
713
|
+
// Load private key
|
|
714
|
+
$privateKey = file_get_contents($this->privateKeyPath);
|
|
715
|
+
if (!$privateKey) {
|
|
716
|
+
throw new PaywareException('Unable to load private key');
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
// Create JWT header
|
|
720
|
+
$header = [
|
|
721
|
+
'alg' => 'RS256',
|
|
722
|
+
'typ' => 'JWT'
|
|
723
|
+
];
|
|
724
|
+
|
|
725
|
+
// Add content MD5 for requests with body
|
|
726
|
+
$bodyString = null;
|
|
727
|
+
if ($requestBody) {
|
|
728
|
+
$bodyString = $this->createMinimizedJSON($requestBody);
|
|
729
|
+
$contentMd5 = base64_encode(md5($bodyString, true));
|
|
730
|
+
$header['contentMd5'] = $contentMd5;
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
// Create JWT payload
|
|
734
|
+
$payload = [
|
|
735
|
+
'iss' => $this->partnerId,
|
|
736
|
+
'aud' => 'https://payware.eu',
|
|
737
|
+
'iat' => time()
|
|
738
|
+
];
|
|
739
|
+
|
|
740
|
+
// Create JWT token
|
|
741
|
+
$token = \\Firebase\\JWT\\JWT::encode($payload, $privateKey, 'RS256', null, $header);
|
|
742
|
+
|
|
743
|
+
return [
|
|
744
|
+
'token' => $token,
|
|
745
|
+
'bodyString' => $bodyString
|
|
746
|
+
];
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
/**
|
|
750
|
+
* Create minimized JSON for deterministic MD5
|
|
751
|
+
*/
|
|
752
|
+
protected function createMinimizedJSON($data)
|
|
753
|
+
{
|
|
754
|
+
if (is_array($data)) {
|
|
755
|
+
ksort($data);
|
|
756
|
+
foreach ($data as $key => $value) {
|
|
757
|
+
$data[$key] = $this->createMinimizedJSON($value);
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
return json_encode($data, JSON_UNESCAPED_SLASHES);
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
/**
|
|
765
|
+
* Make authenticated API request
|
|
766
|
+
*/
|
|
767
|
+
protected function makeAPIRequest($endpoint, $method = 'GET', $requestBody = null)
|
|
768
|
+
{
|
|
769
|
+
$tokenData = $this->createJWTToken($requestBody);
|
|
770
|
+
|
|
771
|
+
$headers = [
|
|
772
|
+
'Authorization' => 'Bearer ' . $tokenData['token'],
|
|
773
|
+
'Content-Type' => 'application/json',
|
|
774
|
+
'Api-Version' => '1'
|
|
775
|
+
];
|
|
776
|
+
|
|
777
|
+
$url = $this->baseUrl . $endpoint;
|
|
778
|
+
|
|
779
|
+
$response = Http::withHeaders($headers)
|
|
780
|
+
->timeout(30)
|
|
781
|
+
->{strtolower($method)}($url, $requestBody ? json_decode($tokenData['bodyString'], true) : []);
|
|
782
|
+
|
|
783
|
+
if ($response->successful()) {
|
|
784
|
+
return $response->json();
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
Log::error('payware API request failed', [
|
|
788
|
+
'endpoint' => $endpoint,
|
|
789
|
+
'method' => $method,
|
|
790
|
+
'status' => $response->status(),
|
|
791
|
+
'response' => $response->body()
|
|
792
|
+
]);
|
|
793
|
+
|
|
794
|
+
return null;
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
/**
|
|
798
|
+
* Create transaction
|
|
799
|
+
*/
|
|
800
|
+
public function createTransaction(array $transactionData)
|
|
801
|
+
{
|
|
802
|
+
return $this->makeAPIRequest('/transactions', 'POST', $transactionData);
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
/**
|
|
806
|
+
* Get transaction status
|
|
807
|
+
*/
|
|
808
|
+
public function getTransactionStatus(string $transactionId)
|
|
809
|
+
{
|
|
810
|
+
return $this->makeAPIRequest("/transactions/{$transactionId}");
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
/**
|
|
814
|
+
* Process transaction
|
|
815
|
+
*/
|
|
816
|
+
public function processTransaction(string $transactionId, array $processData)
|
|
817
|
+
{
|
|
818
|
+
return $this->makeAPIRequest("/transactions/{$transactionId}", 'POST', $processData);
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
/**
|
|
822
|
+
* Cancel transaction
|
|
823
|
+
*/
|
|
824
|
+
public function cancelTransaction(string $transactionId, array $cancelData)
|
|
825
|
+
{
|
|
826
|
+
return $this->makeAPIRequest("/transactions/{$transactionId}", 'PATCH', $cancelData);
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
`,
|
|
830
|
+
|
|
831
|
+
/**
|
|
832
|
+
* Laravel configuration
|
|
833
|
+
*/
|
|
834
|
+
config: `
|
|
835
|
+
<?php
|
|
836
|
+
// config/services.php - Laravel services configuration
|
|
837
|
+
|
|
838
|
+
return [
|
|
839
|
+
// ... other services
|
|
840
|
+
|
|
841
|
+
'payware' => [
|
|
842
|
+
'partner_id' => env('PAYWARE_PARTNER_ID'),
|
|
843
|
+
'private_key_path' => env('PAYWARE_PRIVATE_KEY_PATH'),
|
|
844
|
+
'use_sandbox' => env('PAYWARE_USE_SANDBOX', true),
|
|
845
|
+
'sandbox_url' => env('PAYWARE_SANDBOX_URL', 'https://sandbox.payware.eu/api'),
|
|
846
|
+
'production_url' => env('PAYWARE_PRODUCTION_URL', 'https://api.payware.eu/api'),
|
|
847
|
+
],
|
|
848
|
+
];
|
|
849
|
+
`
|
|
850
|
+
};
|
|
851
|
+
|
|
852
|
+
/**
|
|
853
|
+
* Export all framework examples
|
|
854
|
+
*/
|
|
855
|
+
export const FrameworkExamples = {
|
|
856
|
+
django: DjangoExamples,
|
|
857
|
+
express: ExpressExamples,
|
|
858
|
+
laravel: LaravelExamples
|
|
859
|
+
};
|