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.
@@ -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 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);
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(generateReference("PRE"));
43
- const [authReference, setAuthReference] = useState(generateReference("AUTH"));
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() || generateReference("PRE");
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
- 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`;
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() || generateReference("AUTH");
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
- 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`;
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "strapi-plugin-payone-provider",
3
- "version": "1.4.0",
3
+ "version": "1.4.2",
4
4
  "description": "Strapi plugin for Payone payment gateway integration",
5
5
  "license": "MIT",
6
6
  "maintainers": [
@@ -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
- strapi.log.info("3DS callback received:", ctx.request.body);
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
- const callbackData = ctx.request.body;
146
- const result = await getPayoneService(strapi).handle3DSCallback(callbackData);
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
- const reqType = params.request;
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
- const parsedData = parseResponse(callbackData, strapi.log);
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
- // Log the callback transaction
236
- await logTransaction(strapi, {
237
- txid: txid || null,
238
- reference: reference || null,
239
- status: status,
240
- request_type: "3ds_callback",
241
- amount: parsedData.amount || null,
242
- currency: parsedData.currency || "EUR",
243
- raw_request: callbackData,
244
- raw_response: parsedData,
245
- error_code: parsedData.Error?.ErrorCode || null,
246
- error_message: parsedData.Error?.ErrorMessage || null,
247
- customer_message: parsedData.Error?.CustomerMessage || null
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: status.toUpperCase() === "APPROVED" || status.toUpperCase() === "REDIRECT",
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
  });