strapi-plugin-payone-provider 1.1.3 → 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.
- package/README.md +1156 -380
- package/admin/src/index.js +4 -1
- package/admin/src/pages/App/components/AppHeader.js +37 -0
- package/admin/src/pages/App/components/AppTabs.js +134 -0
- package/admin/src/pages/App/components/ConfigurationPanel.js +34 -35
- package/admin/src/pages/App/components/GooglePaybutton.js +300 -0
- package/admin/src/pages/App/components/HistoryPanel.js +25 -38
- package/admin/src/pages/App/components/PaymentActionsPanel.js +119 -280
- package/admin/src/pages/App/components/StatusBadge.js +3 -1
- package/admin/src/pages/App/components/TransactionHistoryItem.js +4 -1
- package/admin/src/pages/App/components/paymentActions/AuthorizationForm.js +122 -0
- package/admin/src/pages/App/components/paymentActions/CaptureForm.js +64 -0
- package/admin/src/pages/App/components/paymentActions/CardDetailsInput.js +189 -0
- package/admin/src/pages/App/components/paymentActions/PaymentMethodSelector.js +52 -0
- package/admin/src/pages/App/components/paymentActions/PaymentResult.js +148 -0
- package/admin/src/pages/App/components/paymentActions/PreauthorizationForm.js +122 -0
- package/admin/src/pages/App/components/paymentActions/RefundForm.js +89 -0
- package/admin/src/pages/App/index.js +41 -465
- package/admin/src/pages/App/styles.css +294 -0
- package/admin/src/pages/constants/paymentConstants.js +37 -0
- package/admin/src/pages/hooks/usePaymentActions.js +456 -0
- package/admin/src/pages/hooks/useSettings.js +111 -0
- package/admin/src/pages/hooks/useTransactionHistory.js +87 -0
- package/admin/src/pages/utils/api.js +10 -0
- package/admin/src/pages/utils/injectGooglePayScript.js +31 -0
- package/admin/src/pages/utils/paymentUtils.js +119 -15
- package/package.json +1 -1
- package/server/controllers/payone.js +71 -64
- package/server/routes/index.js +17 -0
- package/server/services/paymentService.js +271 -0
- package/server/services/payone.js +25 -648
- package/server/services/settingsService.js +59 -0
- package/server/services/testConnectionService.js +190 -0
- package/server/services/transactionService.js +114 -0
- package/server/utils/normalize.js +51 -0
- package/server/utils/paymentMethodParams.js +126 -0
- package/server/utils/requestBuilder.js +121 -0
- package/server/utils/responseParser.js +134 -0
|
@@ -0,0 +1,456 @@
|
|
|
1
|
+
import { useState, useEffect } from "react";
|
|
2
|
+
import { useNotification } from "@strapi/helper-plugin";
|
|
3
|
+
import payoneRequests from "../utils/api";
|
|
4
|
+
import {
|
|
5
|
+
getPreauthorizationParams,
|
|
6
|
+
getAuthorizationParams,
|
|
7
|
+
getCaptureParams,
|
|
8
|
+
getRefundParams
|
|
9
|
+
} from "../utils/paymentUtils";
|
|
10
|
+
import { DEFAULT_PAYMENT_DATA } from "../constants/paymentConstants";
|
|
11
|
+
|
|
12
|
+
const usePaymentActions = () => {
|
|
13
|
+
const toggleNotification = useNotification();
|
|
14
|
+
|
|
15
|
+
// Load settings to get enable3DSecure value
|
|
16
|
+
const [settings, setSettings] = useState({ enable3DSecure: false });
|
|
17
|
+
|
|
18
|
+
useEffect(() => {
|
|
19
|
+
const loadSettings = async () => {
|
|
20
|
+
try {
|
|
21
|
+
const response = await payoneRequests.getSettings();
|
|
22
|
+
if (response?.data) {
|
|
23
|
+
setSettings(response.data);
|
|
24
|
+
}
|
|
25
|
+
} catch (error) {
|
|
26
|
+
// Silent fail
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
loadSettings();
|
|
30
|
+
}, []);
|
|
31
|
+
|
|
32
|
+
// Payment form state
|
|
33
|
+
const [paymentAmount, setPaymentAmount] = useState("1000");
|
|
34
|
+
|
|
35
|
+
// Generate reference automatically
|
|
36
|
+
const generateReference = (prefix = "REF") => {
|
|
37
|
+
const timestamp = Date.now().toString(36).toUpperCase();
|
|
38
|
+
const random = Math.random().toString(36).substring(2, 6).toUpperCase();
|
|
39
|
+
return `${prefix}-${timestamp}${random}`.slice(0, 20);
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const [preauthReference, setPreauthReference] = useState(generateReference("PRE"));
|
|
43
|
+
const [authReference, setAuthReference] = useState(generateReference("AUTH"));
|
|
44
|
+
const [captureTxid, setCaptureTxid] = useState("");
|
|
45
|
+
const [refundTxid, setRefundTxid] = useState("");
|
|
46
|
+
const [refundSequenceNumber, setRefundSequenceNumber] = useState("2");
|
|
47
|
+
const [refundReference, setRefundReference] = useState("");
|
|
48
|
+
const [paymentMethod, setPaymentMethod] = useState("cc");
|
|
49
|
+
const [captureMode, setCaptureMode] = useState("full");
|
|
50
|
+
const [googlePayToken, setGooglePayToken] = useState(null);
|
|
51
|
+
|
|
52
|
+
// Card details for 3DS testing
|
|
53
|
+
const [cardtype, setCardtype] = useState("");
|
|
54
|
+
const [cardpan, setCardpan] = useState("");
|
|
55
|
+
const [cardexpiredate, setCardexpiredate] = useState("");
|
|
56
|
+
const [cardcvc2, setCardcvc2] = useState("");
|
|
57
|
+
|
|
58
|
+
// Payment processing state
|
|
59
|
+
const [isProcessingPayment, setIsProcessingPayment] = useState(false);
|
|
60
|
+
const [paymentResult, setPaymentResult] = useState(null);
|
|
61
|
+
const [paymentError, setPaymentError] = useState(null);
|
|
62
|
+
|
|
63
|
+
const handlePaymentError = (error, defaultMessage) => {
|
|
64
|
+
const errorMessage =
|
|
65
|
+
error.response?.data?.data?.Error?.ErrorMessage ||
|
|
66
|
+
error.message ||
|
|
67
|
+
defaultMessage;
|
|
68
|
+
setPaymentError(errorMessage);
|
|
69
|
+
toggleNotification({
|
|
70
|
+
type: "warning",
|
|
71
|
+
message: defaultMessage
|
|
72
|
+
});
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
const handlePaymentSuccess = (message) => {
|
|
76
|
+
toggleNotification({
|
|
77
|
+
type: "success",
|
|
78
|
+
message
|
|
79
|
+
});
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
const handlePreauthorization = async (tokenParam = null) => {
|
|
83
|
+
setIsProcessingPayment(true);
|
|
84
|
+
setPaymentError(null);
|
|
85
|
+
setPaymentResult(null);
|
|
86
|
+
try {
|
|
87
|
+
// Auto-generate reference if empty
|
|
88
|
+
const finalPreauthReference = preauthReference.trim() || generateReference("PRE");
|
|
89
|
+
if (!preauthReference.trim()) {
|
|
90
|
+
setPreauthReference(finalPreauthReference);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Determine currency based on card type
|
|
94
|
+
// American Express typically requires USD, other cards use EUR
|
|
95
|
+
const currency = (paymentMethod === "cc" && cardtype === "A") ? "USD" : "EUR";
|
|
96
|
+
|
|
97
|
+
const baseParams = {
|
|
98
|
+
amount: parseInt(paymentAmount),
|
|
99
|
+
currency: currency,
|
|
100
|
+
reference: finalPreauthReference,
|
|
101
|
+
enable3DSecure: settings.enable3DSecure !== false,
|
|
102
|
+
...DEFAULT_PAYMENT_DATA
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
// Add card details if credit card payment and 3DS enabled
|
|
106
|
+
if (paymentMethod === "cc" && settings.enable3DSecure !== false) {
|
|
107
|
+
if (cardtype) baseParams.cardtype = cardtype;
|
|
108
|
+
if (cardpan) baseParams.cardpan = cardpan;
|
|
109
|
+
if (cardexpiredate) baseParams.cardexpiredate = cardexpiredate;
|
|
110
|
+
if (cardcvc2) baseParams.cardcvc2 = cardcvc2;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const needsRedirectUrls =
|
|
114
|
+
(paymentMethod === "cc" && settings.enable3DSecure !== false) ||
|
|
115
|
+
["wlt", "gpp", "apl", "sb"].includes(paymentMethod);
|
|
116
|
+
|
|
117
|
+
if (needsRedirectUrls) {
|
|
118
|
+
const baseUrl = window.location.origin;
|
|
119
|
+
baseParams.successurl = `${baseUrl}/admin/plugins/strapi-plugin-payone-provider/payment/success`;
|
|
120
|
+
baseParams.errorurl = `${baseUrl}/admin/plugins/strapi-plugin-payone-provider/payment/error`;
|
|
121
|
+
baseParams.backurl = `${baseUrl}/admin/plugins/strapi-plugin-payone-provider/payment/back`;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const tokenToUse = tokenParam || googlePayToken;
|
|
125
|
+
if (paymentMethod === "gpp" && tokenToUse) {
|
|
126
|
+
baseParams.googlePayToken = tokenToUse;
|
|
127
|
+
baseParams.settings = settings;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const params = getPreauthorizationParams(paymentMethod, baseParams);
|
|
131
|
+
|
|
132
|
+
const result = await payoneRequests.preauthorization(params);
|
|
133
|
+
const responseData = result?.data || result;
|
|
134
|
+
|
|
135
|
+
// Log full response
|
|
136
|
+
console.log("Preauthorization Response:", responseData);
|
|
137
|
+
console.log("Response Status:", responseData.status || responseData.Status);
|
|
138
|
+
console.log("Response Error Code:", responseData.errorcode || responseData.errorCode || responseData.ErrorCode);
|
|
139
|
+
console.log("Response Error Message:", responseData.errormessage || responseData.errorMessage || responseData.ErrorMessage);
|
|
140
|
+
console.log("All redirect URL fields:", {
|
|
141
|
+
redirectUrl: responseData.redirectUrl,
|
|
142
|
+
redirecturl: responseData.redirecturl,
|
|
143
|
+
RedirectUrl: responseData.RedirectUrl,
|
|
144
|
+
redirect_url: responseData.redirect_url,
|
|
145
|
+
url: responseData.url,
|
|
146
|
+
Url: responseData.Url
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
const status = (responseData.status || responseData.Status || "").toUpperCase();
|
|
150
|
+
const errorCode = responseData.errorcode || responseData.errorCode || responseData.ErrorCode;
|
|
151
|
+
const errorMessage = responseData.errormessage || responseData.errorMessage || responseData.ErrorMessage;
|
|
152
|
+
|
|
153
|
+
// Check for 3DS required error (4219)
|
|
154
|
+
const requires3DSErrorCodes = ["4219", 4219];
|
|
155
|
+
const is3DSRequiredError = requires3DSErrorCodes.includes(errorCode);
|
|
156
|
+
|
|
157
|
+
// Check all possible redirect URL fields
|
|
158
|
+
const redirectUrl =
|
|
159
|
+
responseData.redirectUrl ||
|
|
160
|
+
responseData.redirecturl ||
|
|
161
|
+
responseData.RedirectUrl ||
|
|
162
|
+
responseData.redirect_url ||
|
|
163
|
+
responseData.url ||
|
|
164
|
+
responseData.Url ||
|
|
165
|
+
null;
|
|
166
|
+
|
|
167
|
+
// If 3DS required but no redirect URL, show helpful message
|
|
168
|
+
if (is3DSRequiredError && !redirectUrl) {
|
|
169
|
+
console.warn("3DS authentication required (Error 4219) but no redirect URL found in response");
|
|
170
|
+
console.log("Full response:", JSON.stringify(responseData, null, 2));
|
|
171
|
+
setPaymentError(
|
|
172
|
+
"3D Secure authentication required. Please check Payone configuration and ensure redirect URLs are properly set. Error: " +
|
|
173
|
+
(errorMessage || `Error code: ${errorCode}`)
|
|
174
|
+
);
|
|
175
|
+
setPaymentResult(responseData);
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Check for other errors (but not 3DS required)
|
|
180
|
+
if ((status === "ERROR" || status === "INVALID" || errorCode) && !is3DSRequiredError) {
|
|
181
|
+
setPaymentError(
|
|
182
|
+
errorMessage ||
|
|
183
|
+
`Payment failed with error code: ${errorCode || "Unknown"}` ||
|
|
184
|
+
"Preauthorization failed"
|
|
185
|
+
);
|
|
186
|
+
setPaymentResult(responseData);
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const needsRedirect = responseData.requires3DSRedirect ||
|
|
191
|
+
(status === "REDIRECT" && redirectUrl) ||
|
|
192
|
+
(is3DSRequiredError && redirectUrl);
|
|
193
|
+
|
|
194
|
+
if (needsRedirect && redirectUrl) {
|
|
195
|
+
console.log("Redirecting to 3DS:", redirectUrl);
|
|
196
|
+
window.location.href = redirectUrl;
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
setPaymentResult(responseData);
|
|
201
|
+
|
|
202
|
+
if (status === "APPROVED") {
|
|
203
|
+
handlePaymentSuccess("Preauthorization completed successfully");
|
|
204
|
+
} else {
|
|
205
|
+
handlePaymentError(
|
|
206
|
+
{ message: `Unexpected status: ${status}` },
|
|
207
|
+
`Preauthorization completed with status: ${status}`
|
|
208
|
+
);
|
|
209
|
+
}
|
|
210
|
+
} catch (error) {
|
|
211
|
+
handlePaymentError(error, "Preauthorization failed");
|
|
212
|
+
} finally {
|
|
213
|
+
setIsProcessingPayment(false);
|
|
214
|
+
}
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
const handleAuthorization = async (tokenParam = null) => {
|
|
218
|
+
setIsProcessingPayment(true);
|
|
219
|
+
setPaymentError(null);
|
|
220
|
+
setPaymentResult(null);
|
|
221
|
+
|
|
222
|
+
try {
|
|
223
|
+
// Auto-generate reference if empty
|
|
224
|
+
const finalAuthReference = authReference.trim() || generateReference("AUTH");
|
|
225
|
+
if (!authReference.trim()) {
|
|
226
|
+
setAuthReference(finalAuthReference);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// Determine currency based on card type
|
|
230
|
+
// American Express typically requires USD, other cards use EUR
|
|
231
|
+
const currency = (paymentMethod === "cc" && cardtype === "A") ? "USD" : "EUR";
|
|
232
|
+
|
|
233
|
+
const baseParams = {
|
|
234
|
+
amount: parseInt(paymentAmount),
|
|
235
|
+
currency: currency,
|
|
236
|
+
reference: finalAuthReference,
|
|
237
|
+
enable3DSecure: settings.enable3DSecure !== false,
|
|
238
|
+
...DEFAULT_PAYMENT_DATA
|
|
239
|
+
};
|
|
240
|
+
|
|
241
|
+
// Add card details if credit card payment and 3DS enabled
|
|
242
|
+
if (paymentMethod === "cc" && settings.enable3DSecure !== false) {
|
|
243
|
+
if (cardtype) baseParams.cardtype = cardtype;
|
|
244
|
+
if (cardpan) baseParams.cardpan = cardpan;
|
|
245
|
+
if (cardexpiredate) baseParams.cardexpiredate = cardexpiredate;
|
|
246
|
+
if (cardcvc2) baseParams.cardcvc2 = cardcvc2;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
const needsRedirectUrls =
|
|
250
|
+
(paymentMethod === "cc" && settings.enable3DSecure !== false) ||
|
|
251
|
+
["wlt", "gpp", "apl", "sb"].includes(paymentMethod);
|
|
252
|
+
|
|
253
|
+
if (needsRedirectUrls) {
|
|
254
|
+
const baseUrl = window.location.origin;
|
|
255
|
+
baseParams.successurl = `${baseUrl}/admin/plugins/strapi-plugin-payone-provider/payment/success`;
|
|
256
|
+
baseParams.errorurl = `${baseUrl}/admin/plugins/strapi-plugin-payone-provider/payment/error`;
|
|
257
|
+
baseParams.backurl = `${baseUrl}/admin/plugins/strapi-plugin-payone-provider/payment/back`;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
const tokenToUse = tokenParam || googlePayToken;
|
|
261
|
+
if (paymentMethod === "gpp" && tokenToUse) {
|
|
262
|
+
baseParams.googlePayToken = tokenToUse;
|
|
263
|
+
baseParams.settings = settings;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
const params = getAuthorizationParams(paymentMethod, baseParams);
|
|
267
|
+
|
|
268
|
+
const result = await payoneRequests.authorization(params);
|
|
269
|
+
const responseData = result?.data || result;
|
|
270
|
+
|
|
271
|
+
// Log full response
|
|
272
|
+
console.log("Authorization Response:", responseData);
|
|
273
|
+
console.log("Response Status:", responseData.status || responseData.Status);
|
|
274
|
+
console.log("Response Error Code:", responseData.errorcode || responseData.errorCode || responseData.ErrorCode);
|
|
275
|
+
console.log("Response Error Message:", responseData.errormessage || responseData.errorMessage || responseData.ErrorMessage);
|
|
276
|
+
console.log("All redirect URL fields:", {
|
|
277
|
+
redirectUrl: responseData.redirectUrl,
|
|
278
|
+
redirecturl: responseData.redirecturl,
|
|
279
|
+
RedirectUrl: responseData.RedirectUrl,
|
|
280
|
+
redirect_url: responseData.redirect_url,
|
|
281
|
+
url: responseData.url,
|
|
282
|
+
Url: responseData.Url
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
const status = (responseData.status || responseData.Status || "").toUpperCase();
|
|
286
|
+
const errorCode = responseData.errorcode || responseData.errorCode || responseData.ErrorCode;
|
|
287
|
+
const errorMessage = responseData.errormessage || responseData.errorMessage || responseData.ErrorMessage;
|
|
288
|
+
|
|
289
|
+
// Check for 3DS required error (4219)
|
|
290
|
+
const requires3DSErrorCodes = ["4219", 4219];
|
|
291
|
+
const is3DSRequiredError = requires3DSErrorCodes.includes(errorCode);
|
|
292
|
+
|
|
293
|
+
// Check all possible redirect URL fields
|
|
294
|
+
const redirectUrl =
|
|
295
|
+
responseData.redirectUrl ||
|
|
296
|
+
responseData.redirecturl ||
|
|
297
|
+
responseData.RedirectUrl ||
|
|
298
|
+
responseData.redirect_url ||
|
|
299
|
+
responseData.url ||
|
|
300
|
+
responseData.Url ||
|
|
301
|
+
null;
|
|
302
|
+
|
|
303
|
+
// If 3DS required but no redirect URL, show helpful message
|
|
304
|
+
if (is3DSRequiredError && !redirectUrl) {
|
|
305
|
+
console.warn("3DS authentication required (Error 4219) but no redirect URL found in response");
|
|
306
|
+
console.log("Full response:", JSON.stringify(responseData, null, 2));
|
|
307
|
+
setPaymentError(
|
|
308
|
+
"3D Secure authentication required. Please check Payone configuration and ensure redirect URLs are properly set. Error: " +
|
|
309
|
+
(errorMessage || `Error code: ${errorCode}`)
|
|
310
|
+
);
|
|
311
|
+
setPaymentResult(responseData);
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
// Check for other errors (but not 3DS required)
|
|
316
|
+
if ((status === "ERROR" || status === "INVALID" || errorCode) && !is3DSRequiredError) {
|
|
317
|
+
setPaymentError(
|
|
318
|
+
errorMessage ||
|
|
319
|
+
`Payment failed with error code: ${errorCode || "Unknown"}` ||
|
|
320
|
+
"Authorization failed"
|
|
321
|
+
);
|
|
322
|
+
setPaymentResult(responseData);
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
const needsRedirect = responseData.requires3DSRedirect ||
|
|
327
|
+
(status === "REDIRECT" && redirectUrl) ||
|
|
328
|
+
(is3DSRequiredError && redirectUrl);
|
|
329
|
+
|
|
330
|
+
if (needsRedirect && redirectUrl) {
|
|
331
|
+
console.log("Redirecting to 3DS:", redirectUrl);
|
|
332
|
+
window.location.href = redirectUrl;
|
|
333
|
+
return;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
setPaymentResult(responseData);
|
|
337
|
+
|
|
338
|
+
if (status === "APPROVED") {
|
|
339
|
+
handlePaymentSuccess("Authorization completed successfully");
|
|
340
|
+
} else {
|
|
341
|
+
handlePaymentError(
|
|
342
|
+
{ message: `Unexpected status: ${status}` },
|
|
343
|
+
`Authorization completed with status: ${status}`
|
|
344
|
+
);
|
|
345
|
+
}
|
|
346
|
+
} catch (error) {
|
|
347
|
+
handlePaymentError(error, "Authorization failed");
|
|
348
|
+
} finally {
|
|
349
|
+
setIsProcessingPayment(false);
|
|
350
|
+
}
|
|
351
|
+
};
|
|
352
|
+
|
|
353
|
+
const handleCapture = async () => {
|
|
354
|
+
if (!captureTxid.trim()) {
|
|
355
|
+
setPaymentError("Transaction ID is required for capture");
|
|
356
|
+
return;
|
|
357
|
+
}
|
|
358
|
+
setIsProcessingPayment(true);
|
|
359
|
+
setPaymentError(null);
|
|
360
|
+
setPaymentResult(null);
|
|
361
|
+
try {
|
|
362
|
+
const params = getCaptureParams(paymentMethod, {
|
|
363
|
+
txid: captureTxid,
|
|
364
|
+
amount: parseInt(paymentAmount),
|
|
365
|
+
currency: "EUR",
|
|
366
|
+
captureMode: captureMode,
|
|
367
|
+
sequencenumber: 1
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
const result = await payoneRequests.capture(params);
|
|
371
|
+
setPaymentResult(result);
|
|
372
|
+
handlePaymentSuccess("Capture completed successfully");
|
|
373
|
+
} catch (error) {
|
|
374
|
+
handlePaymentError(error, "Capture failed");
|
|
375
|
+
} finally {
|
|
376
|
+
setIsProcessingPayment(false);
|
|
377
|
+
}
|
|
378
|
+
};
|
|
379
|
+
|
|
380
|
+
const handleRefund = async () => {
|
|
381
|
+
if (!refundTxid.trim()) {
|
|
382
|
+
setPaymentError("Transaction ID is required for refund");
|
|
383
|
+
return;
|
|
384
|
+
}
|
|
385
|
+
setIsProcessingPayment(true);
|
|
386
|
+
setPaymentError(null);
|
|
387
|
+
setPaymentResult(null);
|
|
388
|
+
try {
|
|
389
|
+
const params = getRefundParams(paymentMethod, {
|
|
390
|
+
txid: refundTxid,
|
|
391
|
+
sequencenumber: parseInt(refundSequenceNumber),
|
|
392
|
+
amount: parseInt(paymentAmount),
|
|
393
|
+
currency: "EUR",
|
|
394
|
+
reference: refundReference || `REFUND-${Date.now()}`
|
|
395
|
+
});
|
|
396
|
+
|
|
397
|
+
const result = await payoneRequests.refund(params);
|
|
398
|
+
setPaymentResult(result);
|
|
399
|
+
handlePaymentSuccess("Refund completed successfully");
|
|
400
|
+
} catch (error) {
|
|
401
|
+
handlePaymentError(error, "Refund failed");
|
|
402
|
+
} finally {
|
|
403
|
+
setIsProcessingPayment(false);
|
|
404
|
+
}
|
|
405
|
+
};
|
|
406
|
+
|
|
407
|
+
return {
|
|
408
|
+
// Form state
|
|
409
|
+
paymentAmount,
|
|
410
|
+
setPaymentAmount,
|
|
411
|
+
preauthReference,
|
|
412
|
+
setPreauthReference,
|
|
413
|
+
authReference,
|
|
414
|
+
setAuthReference,
|
|
415
|
+
captureTxid,
|
|
416
|
+
setCaptureTxid,
|
|
417
|
+
refundTxid,
|
|
418
|
+
setRefundTxid,
|
|
419
|
+
refundSequenceNumber,
|
|
420
|
+
setRefundSequenceNumber,
|
|
421
|
+
refundReference,
|
|
422
|
+
setRefundReference,
|
|
423
|
+
paymentMethod,
|
|
424
|
+
setPaymentMethod,
|
|
425
|
+
captureMode,
|
|
426
|
+
setCaptureMode,
|
|
427
|
+
|
|
428
|
+
// Processing state
|
|
429
|
+
isProcessingPayment,
|
|
430
|
+
paymentResult,
|
|
431
|
+
paymentError,
|
|
432
|
+
|
|
433
|
+
// Handlers
|
|
434
|
+
handlePreauthorization,
|
|
435
|
+
handleAuthorization,
|
|
436
|
+
handleCapture,
|
|
437
|
+
handleRefund,
|
|
438
|
+
|
|
439
|
+
// Google Pay
|
|
440
|
+
googlePayToken,
|
|
441
|
+
setGooglePayToken,
|
|
442
|
+
|
|
443
|
+
// Card details for 3DS
|
|
444
|
+
cardtype,
|
|
445
|
+
setCardtype,
|
|
446
|
+
cardpan,
|
|
447
|
+
setCardpan,
|
|
448
|
+
cardexpiredate,
|
|
449
|
+
setCardexpiredate,
|
|
450
|
+
cardcvc2,
|
|
451
|
+
setCardcvc2
|
|
452
|
+
};
|
|
453
|
+
};
|
|
454
|
+
|
|
455
|
+
export default usePaymentActions;
|
|
456
|
+
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { useState, useEffect } from "react";
|
|
2
|
+
import { useNotification } from "@strapi/helper-plugin";
|
|
3
|
+
import payoneRequests from "../utils/api";
|
|
4
|
+
|
|
5
|
+
const useSettings = () => {
|
|
6
|
+
const toggleNotification = useNotification();
|
|
7
|
+
const [settings, setSettings] = useState({
|
|
8
|
+
aid: "",
|
|
9
|
+
portalid: "",
|
|
10
|
+
mid: "",
|
|
11
|
+
key: "",
|
|
12
|
+
mode: "test",
|
|
13
|
+
api_version: "3.10",
|
|
14
|
+
enable3DSecure: false
|
|
15
|
+
});
|
|
16
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
17
|
+
const [isSaving, setIsSaving] = useState(false);
|
|
18
|
+
const [isTesting, setIsTesting] = useState(false);
|
|
19
|
+
const [testResult, setTestResult] = useState(null);
|
|
20
|
+
|
|
21
|
+
useEffect(() => {
|
|
22
|
+
loadSettings();
|
|
23
|
+
}, []);
|
|
24
|
+
|
|
25
|
+
const loadSettings = async () => {
|
|
26
|
+
setIsLoading(true);
|
|
27
|
+
try {
|
|
28
|
+
const response = await payoneRequests.getSettings();
|
|
29
|
+
if (response?.data) setSettings(response.data);
|
|
30
|
+
} catch (error) {
|
|
31
|
+
toggleNotification({
|
|
32
|
+
type: "warning",
|
|
33
|
+
message: "Failed to load settings"
|
|
34
|
+
});
|
|
35
|
+
} finally {
|
|
36
|
+
setIsLoading(false);
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
const handleInputChange = (field, value) => {
|
|
41
|
+
setSettings((prev) => ({ ...prev, [field]: value }));
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const handleSave = async () => {
|
|
45
|
+
setIsSaving(true);
|
|
46
|
+
try {
|
|
47
|
+
await payoneRequests.updateSettings(settings);
|
|
48
|
+
toggleNotification({
|
|
49
|
+
type: "success",
|
|
50
|
+
message: "Settings saved successfully"
|
|
51
|
+
});
|
|
52
|
+
} catch (error) {
|
|
53
|
+
toggleNotification({
|
|
54
|
+
type: "warning",
|
|
55
|
+
message: "Failed to save settings"
|
|
56
|
+
});
|
|
57
|
+
} finally {
|
|
58
|
+
setIsSaving(false);
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
const handleTestConnection = async () => {
|
|
63
|
+
setIsTesting(true);
|
|
64
|
+
setTestResult(null);
|
|
65
|
+
try {
|
|
66
|
+
const response = await payoneRequests.testConnection();
|
|
67
|
+
if (response.data) {
|
|
68
|
+
const result = response.data;
|
|
69
|
+
setTestResult(result);
|
|
70
|
+
if (result.success !== undefined) {
|
|
71
|
+
toggleNotification({
|
|
72
|
+
type: Boolean(result.success) ? "success" : "warning",
|
|
73
|
+
message: result.message || "Test completed"
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
} else {
|
|
77
|
+
throw new Error("Invalid response format from server");
|
|
78
|
+
}
|
|
79
|
+
} catch (error) {
|
|
80
|
+
toggleNotification({
|
|
81
|
+
type: "warning",
|
|
82
|
+
message: "Failed to test connection"
|
|
83
|
+
});
|
|
84
|
+
setTestResult({
|
|
85
|
+
success: false,
|
|
86
|
+
message:
|
|
87
|
+
"Failed to test connection. Please check your network and server logs for details.",
|
|
88
|
+
details: {
|
|
89
|
+
errorCode: "NETWORK",
|
|
90
|
+
rawResponse: error.message || "Network error"
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
} finally {
|
|
94
|
+
setIsTesting(false);
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
return {
|
|
99
|
+
settings,
|
|
100
|
+
isLoading,
|
|
101
|
+
isSaving,
|
|
102
|
+
isTesting,
|
|
103
|
+
testResult,
|
|
104
|
+
handleInputChange,
|
|
105
|
+
handleSave,
|
|
106
|
+
handleTestConnection
|
|
107
|
+
};
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
export default useSettings;
|
|
111
|
+
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { useState, useEffect } from "react";
|
|
2
|
+
import { useNotification } from "@strapi/helper-plugin";
|
|
3
|
+
import payoneRequests from "../utils/api";
|
|
4
|
+
|
|
5
|
+
const PAGE_SIZE = 10;
|
|
6
|
+
|
|
7
|
+
const useTransactionHistory = () => {
|
|
8
|
+
const toggleNotification = useNotification();
|
|
9
|
+
const [transactionHistory, setTransactionHistory] = useState([]);
|
|
10
|
+
const [isLoadingHistory, setIsLoadingHistory] = useState(false);
|
|
11
|
+
const [selectedTransaction, setSelectedTransaction] = useState(null);
|
|
12
|
+
const [currentPage, setCurrentPage] = useState(1);
|
|
13
|
+
const [filters, setFilters] = useState({
|
|
14
|
+
status: "",
|
|
15
|
+
request_type: "",
|
|
16
|
+
txid: "",
|
|
17
|
+
reference: "",
|
|
18
|
+
date_from: "",
|
|
19
|
+
date_to: ""
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
useEffect(() => {
|
|
23
|
+
loadTransactionHistory();
|
|
24
|
+
}, []);
|
|
25
|
+
|
|
26
|
+
const loadTransactionHistory = async () => {
|
|
27
|
+
setIsLoadingHistory(true);
|
|
28
|
+
try {
|
|
29
|
+
const result = await payoneRequests.getTransactionHistory(filters);
|
|
30
|
+
setTransactionHistory(result.data || []);
|
|
31
|
+
setCurrentPage(1);
|
|
32
|
+
} catch (error) {
|
|
33
|
+
toggleNotification({
|
|
34
|
+
type: "warning",
|
|
35
|
+
message: "Failed to load transaction history"
|
|
36
|
+
});
|
|
37
|
+
} finally {
|
|
38
|
+
setIsLoadingHistory(false);
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const handleFilterChange = (field, value) => {
|
|
43
|
+
setFilters((prev) => ({ ...prev, [field]: value }));
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
const handleFilterApply = () => {
|
|
47
|
+
loadTransactionHistory();
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const handleTransactionSelect = (transaction) => {
|
|
51
|
+
if (selectedTransaction?.id === transaction?.id) {
|
|
52
|
+
setSelectedTransaction(null);
|
|
53
|
+
} else {
|
|
54
|
+
setSelectedTransaction(transaction);
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
const handlePageChange = (page) => {
|
|
59
|
+
setCurrentPage(page);
|
|
60
|
+
setSelectedTransaction(null);
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
// Pagination calculations
|
|
64
|
+
const totalPages = Math.ceil(transactionHistory.length / PAGE_SIZE);
|
|
65
|
+
const startIndex = (currentPage - 1) * PAGE_SIZE;
|
|
66
|
+
const endIndex = startIndex + PAGE_SIZE;
|
|
67
|
+
const paginatedTransactions = transactionHistory.slice(startIndex, endIndex);
|
|
68
|
+
|
|
69
|
+
return {
|
|
70
|
+
transactionHistory,
|
|
71
|
+
paginatedTransactions,
|
|
72
|
+
isLoadingHistory,
|
|
73
|
+
selectedTransaction,
|
|
74
|
+
filters,
|
|
75
|
+
currentPage,
|
|
76
|
+
totalPages,
|
|
77
|
+
pageSize: PAGE_SIZE,
|
|
78
|
+
handleFilterChange,
|
|
79
|
+
handleFilterApply,
|
|
80
|
+
handleTransactionSelect,
|
|
81
|
+
handlePageChange,
|
|
82
|
+
loadTransactionHistory
|
|
83
|
+
};
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
export default useTransactionHistory;
|
|
87
|
+
|
|
@@ -69,6 +69,16 @@ const payoneRequests = {
|
|
|
69
69
|
"Content-Type": "application/json"
|
|
70
70
|
}
|
|
71
71
|
});
|
|
72
|
+
},
|
|
73
|
+
|
|
74
|
+
handle3DSCallback: (data) => {
|
|
75
|
+
return request(`/${pluginId}/3ds-callback`, {
|
|
76
|
+
method: "POST",
|
|
77
|
+
body: data,
|
|
78
|
+
headers: {
|
|
79
|
+
"Content-Type": "application/json"
|
|
80
|
+
}
|
|
81
|
+
});
|
|
72
82
|
}
|
|
73
83
|
};
|
|
74
84
|
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export const injectGooglePayScript = () => {
|
|
2
|
+
const scriptUrl = "https://pay.google.com/gp/p/js/pay.js";
|
|
3
|
+
|
|
4
|
+
if (document.querySelector(`script[src="${scriptUrl}"]`)) {
|
|
5
|
+
if (typeof window !== 'undefined' && typeof window.google !== 'undefined' && window.google.payments?.api?.PaymentsClient) {
|
|
6
|
+
window.dispatchEvent(new CustomEvent("googlePayScriptLoaded"));
|
|
7
|
+
}
|
|
8
|
+
return;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const script = document.createElement("script");
|
|
12
|
+
script.src = scriptUrl;
|
|
13
|
+
script.async = true;
|
|
14
|
+
|
|
15
|
+
script.onload = () => {
|
|
16
|
+
setTimeout(() => {
|
|
17
|
+
if (typeof window !== 'undefined' && typeof window.google !== 'undefined' && window.google.payments?.api?.PaymentsClient) {
|
|
18
|
+
window.dispatchEvent(new CustomEvent("googlePayScriptLoaded"));
|
|
19
|
+
} else {
|
|
20
|
+
window.dispatchEvent(new CustomEvent("googlePayScriptLoaded"));
|
|
21
|
+
}
|
|
22
|
+
}, 500);
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
script.onerror = () => {
|
|
26
|
+
window.dispatchEvent(new CustomEvent("googlePayScriptError"));
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
document.head.appendChild(script);
|
|
30
|
+
};
|
|
31
|
+
|