strapi-plugin-payone-provider 1.4.0 → 1.4.2
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/admin/src/pages/hooks/usePaymentActions.js +30 -16
- package/admin/src/pages/utils/paymentUtils.js +15 -0
- package/package.json +1 -1
- package/server/bootstrap.js +42 -0
- package/server/controllers/payone.js +29 -3
- package/server/services/paymentService.js +32 -26
- package/server/services/payone.js +2 -2
|
@@ -5,7 +5,8 @@ import {
|
|
|
5
5
|
getPreauthorizationParams,
|
|
6
6
|
getAuthorizationParams,
|
|
7
7
|
getCaptureParams,
|
|
8
|
-
getRefundParams
|
|
8
|
+
getRefundParams,
|
|
9
|
+
generateLagOrderNumber
|
|
9
10
|
} from "../utils/paymentUtils";
|
|
10
11
|
import { DEFAULT_PAYMENT_DATA } from "../constants/paymentConstants";
|
|
11
12
|
|
|
@@ -32,15 +33,16 @@ const usePaymentActions = () => {
|
|
|
32
33
|
// Payment form state
|
|
33
34
|
const [paymentAmount, setPaymentAmount] = useState("1000");
|
|
34
35
|
|
|
35
|
-
// Generate reference
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
36
|
+
// Generate order reference using generateLagOrderNumber
|
|
37
|
+
// Sequence number starts from 1000 and increments based on timestamp
|
|
38
|
+
const generateOrderReference = () => {
|
|
39
|
+
// Use timestamp to generate unique sequence (1000 to 99999 range)
|
|
40
|
+
const sequence = 1000 + Math.floor((Date.now() % 99000));
|
|
41
|
+
return generateLagOrderNumber(sequence);
|
|
40
42
|
};
|
|
41
43
|
|
|
42
|
-
const [preauthReference, setPreauthReference] = useState(
|
|
43
|
-
const [authReference, setAuthReference] = useState(
|
|
44
|
+
const [preauthReference, setPreauthReference] = useState(generateOrderReference());
|
|
45
|
+
const [authReference, setAuthReference] = useState(generateOrderReference());
|
|
44
46
|
const [captureTxid, setCaptureTxid] = useState("");
|
|
45
47
|
const [refundTxid, setRefundTxid] = useState("");
|
|
46
48
|
const [refundSequenceNumber, setRefundSequenceNumber] = useState("2");
|
|
@@ -85,7 +87,7 @@ const usePaymentActions = () => {
|
|
|
85
87
|
setPaymentResult(null);
|
|
86
88
|
try {
|
|
87
89
|
// Auto-generate reference if empty
|
|
88
|
-
const finalPreauthReference = preauthReference.trim() ||
|
|
90
|
+
const finalPreauthReference = preauthReference.trim() || generateOrderReference();
|
|
89
91
|
if (!preauthReference.trim()) {
|
|
90
92
|
setPreauthReference(finalPreauthReference);
|
|
91
93
|
}
|
|
@@ -116,9 +118,15 @@ const usePaymentActions = () => {
|
|
|
116
118
|
|
|
117
119
|
if (needsRedirectUrls) {
|
|
118
120
|
const baseUrl = window.location.origin;
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
121
|
+
// Detect current context (admin or content-ui) from pathname
|
|
122
|
+
const currentPath = window.location.pathname;
|
|
123
|
+
const isContentUI = currentPath.includes('/content-ui') || currentPath.includes('/content-manager');
|
|
124
|
+
const basePath = isContentUI ? '/content-ui' : '/admin';
|
|
125
|
+
const pluginPath = '/plugins/strapi-plugin-payone-provider/payment';
|
|
126
|
+
|
|
127
|
+
baseParams.successurl = `${baseUrl}${basePath}${pluginPath}/success`;
|
|
128
|
+
baseParams.errorurl = `${baseUrl}${basePath}${pluginPath}/error`;
|
|
129
|
+
baseParams.backurl = `${baseUrl}${basePath}${pluginPath}/back`;
|
|
122
130
|
}
|
|
123
131
|
|
|
124
132
|
const tokenToUse = tokenParam || googlePayToken;
|
|
@@ -221,7 +229,7 @@ const usePaymentActions = () => {
|
|
|
221
229
|
|
|
222
230
|
try {
|
|
223
231
|
// Auto-generate reference if empty
|
|
224
|
-
const finalAuthReference = authReference.trim() ||
|
|
232
|
+
const finalAuthReference = authReference.trim() || generateOrderReference();
|
|
225
233
|
if (!authReference.trim()) {
|
|
226
234
|
setAuthReference(finalAuthReference);
|
|
227
235
|
}
|
|
@@ -252,9 +260,15 @@ const usePaymentActions = () => {
|
|
|
252
260
|
|
|
253
261
|
if (needsRedirectUrls) {
|
|
254
262
|
const baseUrl = window.location.origin;
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
263
|
+
// Detect current context (admin or content-ui) from pathname
|
|
264
|
+
const currentPath = window.location.pathname;
|
|
265
|
+
const isContentUI = currentPath.includes('/content-ui') || currentPath.includes('/content-manager');
|
|
266
|
+
const basePath = isContentUI ? '/content-ui' : '/admin';
|
|
267
|
+
const pluginPath = '/plugins/strapi-plugin-payone-provider/payment';
|
|
268
|
+
|
|
269
|
+
baseParams.successurl = `${baseUrl}${basePath}${pluginPath}/success`;
|
|
270
|
+
baseParams.errorurl = `${baseUrl}${basePath}${pluginPath}/error`;
|
|
271
|
+
baseParams.backurl = `${baseUrl}${basePath}${pluginPath}/back`;
|
|
258
272
|
}
|
|
259
273
|
|
|
260
274
|
const tokenToUse = tokenParam || googlePayToken;
|
|
@@ -17,6 +17,21 @@
|
|
|
17
17
|
* - SEPA Direct Debit (elv)
|
|
18
18
|
*/
|
|
19
19
|
|
|
20
|
+
/**
|
|
21
|
+
* Generate order reference number
|
|
22
|
+
* @param {number} sequence - Sequence number (default: 1000)
|
|
23
|
+
* @returns {string} Generated order reference (format: ORD-XXXXX-XXXX)
|
|
24
|
+
*/
|
|
25
|
+
export function generateLagOrderNumber(sequence = 1000) {
|
|
26
|
+
const paddedSequence = sequence.toString().padStart(5, '0');
|
|
27
|
+
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
|
|
28
|
+
let randomPart = '';
|
|
29
|
+
for (let i = 0; i < 4; i++) {
|
|
30
|
+
randomPart += chars.charAt(Math.floor(Math.random() * chars.length));
|
|
31
|
+
}
|
|
32
|
+
return `ORD-${paddedSequence}-${randomPart}`;
|
|
33
|
+
}
|
|
34
|
+
|
|
20
35
|
/**
|
|
21
36
|
* Get base parameters for all payment methods
|
|
22
37
|
* Based on Payone v1 API Documentation
|
package/package.json
CHANGED
package/server/bootstrap.js
CHANGED
|
@@ -23,4 +23,46 @@ module.exports = async ({ strapi }) => {
|
|
|
23
23
|
}
|
|
24
24
|
});
|
|
25
25
|
}
|
|
26
|
+
|
|
27
|
+
// Register custom routes for 3DS redirects at root level
|
|
28
|
+
// These routes handle redirects from Payone after 3DS authentication
|
|
29
|
+
// They work with both /admin/ and /content-ui/ paths
|
|
30
|
+
const pluginName = "strapi-plugin-payone-provider";
|
|
31
|
+
|
|
32
|
+
// Get the controller
|
|
33
|
+
const getController = () => {
|
|
34
|
+
return strapi.plugin(pluginName).controller("payone");
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
// All routes use the same handler - it detects success/error/back from URL path
|
|
38
|
+
// Routes match the plugin's internal payment callback URLs
|
|
39
|
+
const routes = [
|
|
40
|
+
"/admin/plugins/strapi-plugin-payone-provider/payment/success",
|
|
41
|
+
"/admin/plugins/strapi-plugin-payone-provider/payment/error",
|
|
42
|
+
"/admin/plugins/strapi-plugin-payone-provider/payment/back",
|
|
43
|
+
"/content-ui/plugins/strapi-plugin-payone-provider/payment/success",
|
|
44
|
+
"/content-ui/plugins/strapi-plugin-payone-provider/payment/error",
|
|
45
|
+
"/content-ui/plugins/strapi-plugin-payone-provider/payment/back"
|
|
46
|
+
];
|
|
47
|
+
|
|
48
|
+
// Register routes using Koa router
|
|
49
|
+
try {
|
|
50
|
+
const Router = require('@koa/router');
|
|
51
|
+
const router = new Router();
|
|
52
|
+
|
|
53
|
+
routes.forEach(route => {
|
|
54
|
+
router.get(route, async (ctx) => {
|
|
55
|
+
const controller = getController();
|
|
56
|
+
return await controller.handle3DSCallback(ctx);
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
// Add router to the server app
|
|
61
|
+
if (strapi.server.app && typeof strapi.server.app.use === 'function') {
|
|
62
|
+
strapi.server.app.use(router.routes());
|
|
63
|
+
strapi.server.app.use(router.allowedMethods());
|
|
64
|
+
}
|
|
65
|
+
} catch (error) {
|
|
66
|
+
strapi.log.warn('Could not register 3DS callback routes:', error.message);
|
|
67
|
+
}
|
|
26
68
|
};
|
|
@@ -137,13 +137,39 @@ module.exports = ({ strapi }) => ({
|
|
|
137
137
|
/**
|
|
138
138
|
* Handle 3D Secure callback from Payone
|
|
139
139
|
* This endpoint receives the callback after customer completes 3DS authentication
|
|
140
|
+
* Works with both /admin/ and /content-ui/ paths
|
|
140
141
|
*/
|
|
141
142
|
async handle3DSCallback(ctx) {
|
|
142
143
|
try {
|
|
143
|
-
|
|
144
|
+
const isGetRequest = ctx.request.method === "GET";
|
|
145
|
+
const currentPath = ctx.request.url;
|
|
146
|
+
|
|
147
|
+
let resultType = "callback";
|
|
148
|
+
if (currentPath.includes("/success")) {
|
|
149
|
+
resultType = "success";
|
|
150
|
+
} else if (currentPath.includes("/error")) {
|
|
151
|
+
resultType = "error";
|
|
152
|
+
} else if (currentPath.includes("/back")) {
|
|
153
|
+
resultType = "cancelled";
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const callbackData = isGetRequest ? ctx.query : ctx.request.body;
|
|
157
|
+
strapi.log.info(`3DS ${resultType} received (${ctx.request.method}):`, callbackData);
|
|
158
|
+
const result = await getPayoneService(strapi).handle3DSCallback(callbackData, resultType);
|
|
159
|
+
|
|
160
|
+
if (isGetRequest) {
|
|
161
|
+
const isContentUI = currentPath.includes('/content-ui');
|
|
162
|
+
const basePath = isContentUI ? '/content-ui' : '/admin';
|
|
163
|
+
const pluginPath = '/plugins/strapi-plugin-payone-provider';
|
|
144
164
|
|
|
145
|
-
|
|
146
|
-
|
|
165
|
+
const queryParams = new URLSearchParams();
|
|
166
|
+
queryParams.set('3ds', resultType);
|
|
167
|
+
if (result.txid) queryParams.set('txid', result.txid);
|
|
168
|
+
if (result.status) queryParams.set('status', result.status);
|
|
169
|
+
|
|
170
|
+
const redirectUrl = `${basePath}${pluginPath}?${queryParams.toString()}`;
|
|
171
|
+
return ctx.redirect(redirectUrl);
|
|
172
|
+
}
|
|
147
173
|
|
|
148
174
|
ctx.body = { data: result };
|
|
149
175
|
} catch (error) {
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
3
|
const axios = require("axios");
|
|
4
|
-
const { normalizeReference } = require("../utils/normalize");
|
|
5
4
|
const { buildClientRequestParams, toFormData } = require("../utils/requestBuilder");
|
|
6
5
|
const { addPaymentMethodParams } = require("../utils/paymentMethodParams");
|
|
7
6
|
const { parseResponse, extractTxId, requires3DSRedirect, get3DSRedirectUrl } = require("../utils/responseParser");
|
|
@@ -24,12 +23,7 @@ const sendRequest = async (strapi, params) => {
|
|
|
24
23
|
throw new Error("Payone settings not configured");
|
|
25
24
|
}
|
|
26
25
|
|
|
27
|
-
|
|
28
|
-
if (["authorization", "preauthorization", "refund"].includes(reqType)) {
|
|
29
|
-
const prefix =
|
|
30
|
-
reqType === "refund" ? "REF" : reqType === "preauthorization" ? "PRE" : "AUTH";
|
|
31
|
-
params.reference = normalizeReference(params.reference, prefix);
|
|
32
|
-
}
|
|
26
|
+
// Reference is saved as-is without normalization
|
|
33
27
|
|
|
34
28
|
const requestParams = buildClientRequestParams(settings, params, strapi.log);
|
|
35
29
|
const formData = toFormData(requestParams);
|
|
@@ -219,36 +213,48 @@ const refund = async (strapi, params) => {
|
|
|
219
213
|
/**
|
|
220
214
|
* Handle 3D Secure callback from Payone
|
|
221
215
|
* This processes the callback after customer completes 3DS authentication
|
|
216
|
+
* Note: Payone's redirect callback typically doesn't include transaction details -
|
|
217
|
+
* the URL path (success/error/back) indicates the result.
|
|
222
218
|
* @param {Object} strapi - Strapi instance
|
|
223
|
-
* @param {Object} callbackData - Callback data from Payone
|
|
219
|
+
* @param {Object} callbackData - Callback data from Payone (may be empty or minimal)
|
|
220
|
+
* @param {string} resultType - Result type from URL path: 'success', 'error', 'cancelled', or 'callback'
|
|
224
221
|
* @returns {Promise<Object>} Processed callback result
|
|
225
222
|
*/
|
|
226
|
-
const handle3DSCallback = async (strapi, callbackData) => {
|
|
223
|
+
const handle3DSCallback = async (strapi, callbackData, resultType = 'callback') => {
|
|
227
224
|
try {
|
|
228
|
-
|
|
225
|
+
// Parse any data that Payone might have sent
|
|
226
|
+
const parsedData = callbackData && Object.keys(callbackData).length > 0
|
|
227
|
+
? parseResponse(callbackData, strapi.log)
|
|
228
|
+
: {};
|
|
229
229
|
|
|
230
|
-
// Extract transaction information
|
|
230
|
+
// Extract transaction information if available
|
|
231
231
|
const txid = extractTxId(parsedData);
|
|
232
|
-
const status = parsedData.status || parsedData.Status || "unknown";
|
|
233
232
|
const reference = parsedData.reference || parsedData.Reference || null;
|
|
234
233
|
|
|
235
|
-
//
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
234
|
+
// Determine status from resultType (URL path) since Payone callback may not include status
|
|
235
|
+
let status;
|
|
236
|
+
if (resultType === 'success') {
|
|
237
|
+
status = 'APPROVED';
|
|
238
|
+
} else if (resultType === 'error') {
|
|
239
|
+
status = 'ERROR';
|
|
240
|
+
} else if (resultType === 'cancelled') {
|
|
241
|
+
status = 'CANCELLED';
|
|
242
|
+
} else {
|
|
243
|
+
// Fallback to parsed data if available
|
|
244
|
+
status = parsedData.status || parsedData.Status || 'PENDING';
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// Log for debugging purposes only (not saved to transaction history)
|
|
248
|
+
strapi.log.info("3DS callback processed:", {
|
|
249
|
+
resultType,
|
|
250
|
+
status,
|
|
251
|
+
txid,
|
|
252
|
+
reference,
|
|
253
|
+
callbackData
|
|
248
254
|
});
|
|
249
255
|
|
|
250
256
|
return {
|
|
251
|
-
success:
|
|
257
|
+
success: resultType === 'success',
|
|
252
258
|
status: status,
|
|
253
259
|
txid: txid,
|
|
254
260
|
reference: reference,
|
|
@@ -50,7 +50,7 @@ module.exports = ({ strapi }) => ({
|
|
|
50
50
|
},
|
|
51
51
|
|
|
52
52
|
// 3D Secure callback handler
|
|
53
|
-
async handle3DSCallback(callbackData) {
|
|
54
|
-
return await paymentService.handle3DSCallback(strapi, callbackData);
|
|
53
|
+
async handle3DSCallback(callbackData, resultType) {
|
|
54
|
+
return await paymentService.handle3DSCallback(strapi, callbackData, resultType);
|
|
55
55
|
}
|
|
56
56
|
});
|