strapi-plugin-payone-provider 4.6.9 → 5.6.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (104) hide show
  1. package/README.md +156 -11
  2. package/admin/src/components/Initializer/index.jsx +3 -3
  3. package/admin/src/components/PluginIcon/index.jsx +3 -3
  4. package/admin/src/index.js +33 -11
  5. package/admin/src/pages/App/components/AppHeader.jsx +17 -32
  6. package/admin/src/pages/App/components/AppTabs.jsx +36 -162
  7. package/admin/src/pages/App/components/ApplePayBtn.jsx +9 -11
  8. package/admin/src/pages/App/components/ApplePayConfig.jsx +221 -161
  9. package/admin/src/pages/App/components/ApplePayConfigPanel.jsx +33 -45
  10. package/admin/src/pages/App/components/DocsPanel.jsx +66 -1726
  11. package/admin/src/pages/App/components/GooglePayConfig.jsx +136 -169
  12. package/admin/src/pages/App/components/GooglePayConfigPanel.jsx +37 -55
  13. package/admin/src/pages/App/components/GooglePaybutton.jsx +101 -43
  14. package/admin/src/pages/App/components/RenderInput.jsx +94 -0
  15. package/admin/src/pages/App/components/StatusBadge.jsx +24 -71
  16. package/admin/src/pages/App/components/configuration/ConfigurationFields.jsx +255 -0
  17. package/admin/src/pages/App/components/configuration/ConfigurationPanel.jsx +54 -0
  18. package/admin/src/pages/App/components/configuration/TestConnection.jsx +130 -0
  19. package/admin/src/pages/App/components/docs/ApplePaySection.jsx +260 -0
  20. package/admin/src/pages/App/components/docs/BaseUrlSection.jsx +53 -0
  21. package/admin/src/pages/App/components/docs/CaptureRefundSection.jsx +113 -0
  22. package/admin/src/pages/App/components/docs/CodeBlock.jsx +59 -0
  23. package/admin/src/pages/App/components/docs/CreditCardSection.jsx +93 -0
  24. package/admin/src/pages/App/components/docs/GooglePaySection.jsx +248 -0
  25. package/admin/src/pages/App/components/docs/PayPalSection.jsx +116 -0
  26. package/admin/src/pages/App/components/docs/PaymentMethodsSection.jsx +55 -0
  27. package/admin/src/pages/App/components/docs/TableOfContents.jsx +47 -0
  28. package/admin/src/pages/App/components/docs/TestCredentialsSection.jsx +304 -0
  29. package/admin/src/pages/App/components/docs/ThreeDSecureSection.jsx +188 -0
  30. package/admin/src/pages/App/components/icons/BankIcon.jsx +1 -1
  31. package/admin/src/pages/App/components/icons/ChevronDownIcon.jsx +1 -1
  32. package/admin/src/pages/App/components/icons/ChevronUpIcon.jsx +1 -1
  33. package/admin/src/pages/App/components/icons/CreditCardIcon.jsx +1 -1
  34. package/admin/src/pages/App/components/icons/ErrorIcon.jsx +1 -1
  35. package/admin/src/pages/App/components/icons/InfoIcon.jsx +1 -1
  36. package/admin/src/pages/App/components/icons/MarkCircle.jsx +19 -0
  37. package/admin/src/pages/App/components/icons/PaymentIcon.jsx +1 -1
  38. package/admin/src/pages/App/components/icons/PendingIcon.jsx +1 -1
  39. package/admin/src/pages/App/components/icons/PersonIcon.jsx +1 -1
  40. package/admin/src/pages/App/components/icons/SuccessIcon.jsx +1 -1
  41. package/admin/src/pages/App/components/icons/WalletIcon.jsx +1 -1
  42. package/admin/src/pages/App/components/payment-actions/ApplePayPanel.jsx +51 -0
  43. package/admin/src/pages/App/components/payment-actions/AuthorizationForm.jsx +341 -0
  44. package/admin/src/pages/App/components/payment-actions/CaptureForm.jsx +128 -0
  45. package/admin/src/pages/App/components/{paymentActions → payment-actions}/CardDetailsInput.jsx +77 -72
  46. package/admin/src/pages/App/components/payment-actions/PaymentActionsPanel.jsx +194 -0
  47. package/admin/src/pages/App/components/payment-actions/PaymentMethodSelector.jsx +313 -0
  48. package/admin/src/pages/App/components/payment-actions/PaymentResult.jsx +133 -0
  49. package/admin/src/pages/App/components/payment-actions/PreauthorizationForm.jsx +280 -0
  50. package/admin/src/pages/App/components/payment-actions/RefundForm.jsx +121 -0
  51. package/admin/src/pages/App/components/transaction-history/FiltersPanel.jsx +145 -0
  52. package/admin/src/pages/App/components/transaction-history/HistoryPanel.jsx +50 -0
  53. package/admin/src/pages/App/components/transaction-history/TransactionTable.jsx +163 -0
  54. package/admin/src/pages/App/components/transaction-history/details/TransactionDetails.jsx +156 -0
  55. package/admin/src/pages/App/components/{TransactionHistoryItem.jsx → transaction-history/details/TransactionHistoryItem.jsx} +16 -28
  56. package/admin/src/pages/App/index.jsx +27 -70
  57. package/admin/src/pages/App/styles.css +46 -169
  58. package/admin/src/pages/constants/paymentConstants.js +52 -16
  59. package/admin/src/pages/hooks/use-system-theme.js +27 -0
  60. package/admin/src/pages/hooks/usePaymentActions.js +273 -210
  61. package/admin/src/pages/hooks/useSettings.js +87 -48
  62. package/admin/src/pages/hooks/useTransactionHistory.js +105 -108
  63. package/admin/src/pages/utils/api.js +57 -72
  64. package/admin/src/pages/utils/applePayConstants.js +2 -28
  65. package/admin/src/pages/utils/countryLanguageUtils.js +280 -0
  66. package/admin/src/pages/utils/getInputComponent.jsx +225 -0
  67. package/admin/src/pages/utils/googlePayConstants.js +2 -9
  68. package/admin/src/pages/utils/paymentUtils.js +13 -26
  69. package/admin/src/pages/utils/tooltipHelpers.js +18 -0
  70. package/admin/src/pages/utils/transactionTableUtils.js +60 -0
  71. package/package.json +8 -14
  72. package/server/config/index.js +18 -2
  73. package/server/controllers/payone.js +98 -31
  74. package/server/policies/index.js +2 -1
  75. package/server/policies/is-auth.js +9 -3
  76. package/server/policies/is-payone-notification.js +31 -0
  77. package/server/policies/isSuperAdmin.js +7 -5
  78. package/server/routes/index.js +11 -0
  79. package/server/services/paymentService.js +6 -22
  80. package/server/services/payone.js +10 -3
  81. package/server/services/settingsService.js +13 -3
  82. package/server/services/testConnectionService.js +11 -73
  83. package/server/services/transactionService.js +62 -99
  84. package/server/services/transactionStatusService.js +87 -0
  85. package/server/utils/normalize.js +0 -12
  86. package/server/utils/paymentMethodParams.js +0 -1
  87. package/server/utils/requestBuilder.js +34 -5
  88. package/server/utils/responseParser.js +9 -14
  89. package/strapi-admin.js +3 -1
  90. package/admin/src/pages/App/components/ConfigurationPanel.jsx +0 -517
  91. package/admin/src/pages/App/components/CustomerInfoPopover.jsx +0 -147
  92. package/admin/src/pages/App/components/HistoryPanel.jsx +0 -94
  93. package/admin/src/pages/App/components/PaymentActionsPanel.jsx +0 -280
  94. package/admin/src/pages/App/components/RawDataPopover.jsx +0 -113
  95. package/admin/src/pages/App/components/TransactionHistoryTable/TransactionHistoryTableFilters.jsx +0 -113
  96. package/admin/src/pages/App/components/TransactionHistoryTable/TransactionHistoryTablePagination.jsx +0 -180
  97. package/admin/src/pages/App/components/TransactionHistoryTable/index.jsx +0 -225
  98. package/admin/src/pages/App/components/paymentActions/ApplePayPanel.jsx +0 -95
  99. package/admin/src/pages/App/components/paymentActions/AuthorizationForm.jsx +0 -197
  100. package/admin/src/pages/App/components/paymentActions/CaptureForm.jsx +0 -65
  101. package/admin/src/pages/App/components/paymentActions/PaymentMethodSelector.jsx +0 -306
  102. package/admin/src/pages/App/components/paymentActions/PaymentResult.jsx +0 -192
  103. package/admin/src/pages/App/components/paymentActions/PreauthorizationForm.jsx +0 -142
  104. package/admin/src/pages/App/components/paymentActions/RefundForm.jsx +0 -90
@@ -51,13 +51,7 @@ const sendRequest = async (strapi, params) => {
51
51
  });
52
52
 
53
53
  const responseData = parseResponse(response.data, strapi.log);
54
- const errorCode =
55
- responseData.errorcode ||
56
- responseData.ErrorCode ||
57
- responseData.Error?.ErrorCode ||
58
- responseData.error_code ||
59
- null;
60
-
54
+ const errorCode = responseData?.Error?.ErrorCode;
61
55
  const requires3DSErrorCodes = ["4219", 4219];
62
56
  const is3DSRequiredError = requires3DSErrorCodes.includes(errorCode);
63
57
 
@@ -69,21 +63,11 @@ const sendRequest = async (strapi, params) => {
69
63
 
70
64
  }
71
65
 
72
- const errorMessage =
73
- responseData.errormessage ||
74
- responseData.ErrorMessage ||
75
- responseData.Error?.ErrorMessage ||
76
- responseData.error_message ||
77
- null;
78
-
79
- const customerMessage =
80
- responseData.customermessage ||
81
- responseData.CustomerMessage ||
82
- responseData.Error?.CustomerMessage ||
83
- responseData.customer_message ||
84
- null;
85
-
86
- const status = (responseData.status || responseData.Status || "unknown").toUpperCase();
66
+ const errorMessage = responseData?.Error?.ErrorMessage || null;
67
+
68
+ const customerMessage = responseData?.Error?.CustomerMessage || null;
69
+
70
+ const status = (responseData?.status || responseData?.Status || "unknown").toUpperCase() || null;
87
71
 
88
72
  await logTransaction(strapi, {
89
73
  txid: extractTxId(responseData) || params.txid || null,
@@ -5,6 +5,7 @@ const transactionService = require("./transactionService");
5
5
  const paymentService = require("./paymentService");
6
6
  const testConnectionService = require("./testConnectionService");
7
7
  const applePayService = require("./applePayService");
8
+ const transactionStatusService = require("./transactionStatusService");
8
9
 
9
10
  module.exports = ({ strapi }) => ({
10
11
  // Settings
@@ -16,7 +17,6 @@ module.exports = ({ strapi }) => ({
16
17
  return await settingsService.updateSettings(strapi, settings);
17
18
  },
18
19
 
19
- // Payment operations
20
20
  async preauthorization(params) {
21
21
  return await paymentService.preauthorization(strapi, params);
22
22
  },
@@ -38,8 +38,9 @@ module.exports = ({ strapi }) => ({
38
38
  return await transactionService.logTransaction(strapi, transactionData);
39
39
  },
40
40
 
41
- async getTransactionHistory(filters = {}) {
42
- return await transactionService.getTransactionHistory(strapi, filters);
41
+
42
+ async getTransactionHistory({ filters = {}, pagination = {} }) {
43
+ return await transactionService.getTransactionHistory(strapi, { filters, pagination });
43
44
  },
44
45
 
45
46
  // Test connection
@@ -59,5 +60,11 @@ module.exports = ({ strapi }) => ({
59
60
 
60
61
  async initializeApplePaySession(params) {
61
62
  return await applePayService.initializeApplePaySession(strapi, params);
63
+ },
64
+
65
+ // TransactionStatus Notification
66
+ async processTransactionStatus(notificationData) {
67
+ return await transactionStatusService.processTransactionStatus(strapi, notificationData);
62
68
  }
69
+
63
70
  });
@@ -12,16 +12,26 @@ const getPluginStore = (strapi) => {
12
12
 
13
13
  const getSettings = async (strapi) => {
14
14
  const pluginStore = getPluginStore(strapi);
15
- return await pluginStore.get({ key: "settings" });
15
+ const settings = await pluginStore.get({ key: "settings" }) || {};
16
+
17
+ return settings;
16
18
  };
17
19
 
18
20
  const updateSettings = async (strapi, settings) => {
19
21
  const pluginStore = getPluginStore(strapi);
22
+ const currentSettings = await getSettings(strapi) || {};
23
+ const mergedSettings = {
24
+ ...currentSettings,
25
+ ...settings
26
+ };
27
+
28
+
20
29
  await pluginStore.set({
21
30
  key: "settings",
22
- value: settings
31
+ value: mergedSettings
23
32
  });
24
- return settings;
33
+
34
+ return mergedSettings;
25
35
  };
26
36
 
27
37
  const validateSettings = (settings) => {
@@ -54,82 +54,19 @@ const testConnection = async (strapi) => {
54
54
  });
55
55
 
56
56
  const result = parseResponse(response.data, strapi.log);
57
- const status = result.status || result.Status || result.STATUS;
58
- const errorMessage =
59
- result.errormessage ||
60
- result.Errormessage ||
61
- result.ERRORMESSAGE ||
62
- result.error ||
63
- result.Error?.ErrorMessage ||
64
- "";
65
- const errorCode =
66
- result.errorcode ||
67
- result.Errorcode ||
68
- result.ERRORCODE ||
69
- result.Error?.ErrorCode ||
70
- "";
71
- const customErrorMessage =
72
- result.customerrormessage ||
73
- result.Customerrormessage ||
74
- result.CUSTOMERRORMESSAGE ||
75
- result.Error?.CustomerMessage ||
76
- "";
57
+ const status = result?.status || result?.Status || result?.STATUS;
58
+ const errorMessage = result?.Error?.ErrorMessage;
59
+ const errorCode = result?.Error?.ErrorCode;
60
+ const customErrorMessage = result?.Error?.CustomerMessage;
77
61
 
78
62
  if (status === "ERROR" || status === "error") {
79
- if (["2006", "920", "921", "922", "401", "403"].includes(errorCode)) {
80
- return {
81
- success: false,
82
- message: `Authentication failed: ${customErrorMessage || errorMessage || "Invalid credentials"}`,
83
- errorcode: errorCode
84
- };
85
- }
86
-
87
- const errorMessageStr = typeof errorMessage === "string" ? errorMessage : JSON.stringify(errorMessage);
88
- const errorMessageLower = (errorMessageStr || "").toLowerCase();
89
- const authErrorKeywords = [
90
- "key incorrect",
91
- "invalid key",
92
- "portal key",
93
- "unauthorized",
94
- "not authorized",
95
- "unknown aid",
96
- "unknown account",
97
- "unknown portal",
98
- "unknown merchant",
99
- "invalid aid",
100
- "invalid mid",
101
- "invalid portalid"
102
- ];
103
-
104
- if (authErrorKeywords.some((keyword) => errorMessageLower.includes(keyword))) {
105
- return {
106
- success: false,
107
- message: `Authentication failed: ${errorMessageStr}`,
108
- errorcode: errorCode || "AUTH"
109
- };
110
- }
111
-
112
- if (errorCode === "911") {
113
- return {
114
- success: true,
115
- message: "Connection successful! Your Payone credentials are valid.",
116
- details: {
117
- mode: settings.mode,
118
- aid: settings.aid,
119
- portalid: settings.portalid,
120
- mid: settings.mid
121
- }
122
- };
123
- }
124
-
125
63
  return {
126
64
  success: false,
127
- message: `Connection failed: ${customErrorMessage || errorMessageStr || "Unknown error"}`,
128
- errorcode: errorCode,
129
- details: {
130
- status,
131
- errorCode,
132
- rawResponse: JSON.stringify(result).substring(0, 200)
65
+ message: `Test connection failed: ${errorCode}`,
66
+ error: {
67
+ ErrorCode: errorCode,
68
+ ErrorMessage: errorMessage,
69
+ CustomerMessage: customErrorMessage
133
70
  }
134
71
  };
135
72
  }
@@ -157,11 +94,12 @@ const testConnection = async (strapi) => {
157
94
  rawResponse: JSON.stringify(result).substring(0, 200)
158
95
  }
159
96
  };
97
+
160
98
  } catch (error) {
161
99
  strapi.log.error("Payone test connection error:", error);
162
100
  return {
163
101
  success: false,
164
- message: `Connection error: ${error.message || "Unknown error"}`,
102
+ message: `Test connection error: ${error.message || "Unknown error"}`,
165
103
  error: error.toString(),
166
104
  details: {
167
105
  errorType: error.constructor.name,
@@ -2,20 +2,6 @@
2
2
 
3
3
  const { getPluginStore } = require("./settingsService");
4
4
 
5
- const sanitizeRawRequest = (rawRequest) => {
6
- if (!rawRequest || typeof rawRequest !== "object") return rawRequest
7
- const sanitized = { ...rawRequest };
8
- const sensitiveFields = ["cardpan", "cardexpiredate", "cardcvc2"];
9
-
10
- sensitiveFields.forEach((field) => {
11
- if (sanitized[field] && typeof sanitized[field] === "string") {
12
- sanitized[field] = "*".repeat(sanitized[field].length);
13
- }
14
- });
15
-
16
- return sanitized;
17
- };
18
-
19
5
  const logTransaction = async (strapi, transactionData) => {
20
6
  const pluginStore = getPluginStore(strapi);
21
7
  let transactionHistory =
@@ -42,19 +28,15 @@ const logTransaction = async (strapi, transactionData) => {
42
28
  transactionData.customer_message ||
43
29
  transactionData.Error?.CustomerMessage ||
44
30
  null,
45
- body: transactionData ? { ...transactionData, raw_request: sanitizeRawRequest(transactionData.raw_request) } : null,
46
- raw_request: transactionData.raw_request
47
- ? sanitizeRawRequest(transactionData.raw_request)
48
- : null,
49
- raw_response: sanitizeRawRequest(transactionData.raw_response) || transactionData,
31
+ body: transactionData || null,
50
32
  created_at: new Date().toISOString(),
51
33
  updated_at: new Date().toISOString()
52
34
  };
53
35
 
54
36
  transactionHistory.unshift(logEntry);
55
37
 
56
- if (transactionHistory.length > 5000) {
57
- transactionHistory = transactionHistory.slice(0, 5000);
38
+ if (transactionHistory.length > 1000) {
39
+ transactionHistory = transactionHistory.slice(0, 1000);
58
40
  }
59
41
 
60
42
  await pluginStore.set({
@@ -62,39 +44,35 @@ const logTransaction = async (strapi, transactionData) => {
62
44
  value: transactionHistory
63
45
  });
64
46
 
65
- strapi.log.info("Transaction logged:", logEntry);
47
+ console.log(`Transaction logged: ${logEntry}`);
66
48
  };
67
49
 
68
- const getTransactionHistory = async (strapi, filters = {}) => {
69
- const pluginStore = getPluginStore(strapi);
70
- let transactionHistory =
71
- (await pluginStore.get({ key: "transactionHistory" })) || [];
72
50
 
73
- if (filters.search) {
74
- const searchLower = filters.search.toLowerCase().trim();
75
- transactionHistory = transactionHistory.filter((transaction) => {
76
- const status = (transaction.status || "").toLowerCase();
77
- const txid = (transaction.txid || "").toLowerCase();
78
- const reference = (transaction.reference || "").toLowerCase();
79
-
80
- return (
81
- status.includes(searchLower) ||
82
- txid.includes(searchLower) ||
83
- reference.includes(searchLower)
84
- );
51
+ const applyFilters = (transactions, filters = {}) => {
52
+ let result = [...transactions];
53
+ if (filters.search && typeof filters.search === 'string' && filters.search.trim() !== '') {
54
+ const search = filters.search.toLowerCase().trim();
55
+ result = result.filter((t) => {
56
+ const txid = (t.txid || "").toString().toLowerCase();
57
+ const reference = (t.reference || "").toString().toLowerCase();
58
+ return txid.includes(search) || reference.includes(search);
85
59
  });
86
60
  }
87
61
 
88
- if (filters.request_type) {
89
- transactionHistory = transactionHistory.filter(
90
- (transaction) => transaction.request_type === filters.request_type
62
+ if (filters.status) {
63
+ result = result.filter(
64
+ (t) => (t.status || "").toUpperCase() === filters.status.toUpperCase()
91
65
  );
92
66
  }
93
67
 
68
+ if (filters.request_type) {
69
+ result = result.filter((t) => t.request_type === filters.request_type);
70
+ }
71
+
94
72
  if (filters.payment_method) {
95
- transactionHistory = transactionHistory.filter((transaction) => {
96
- const clearingtype = transaction.raw_request?.clearingtype || "";
97
- const wallettype = transaction.raw_request?.wallettype || "";
73
+ result = result.filter((t) => {
74
+ const clearingtype = t.raw_request?.clearingtype;
75
+ const wallettype = t.raw_request?.wallettype;
98
76
 
99
77
  switch (filters.payment_method) {
100
78
  case "credit_card":
@@ -102,82 +80,67 @@ const getTransactionHistory = async (strapi, filters = {}) => {
102
80
  case "paypal":
103
81
  return clearingtype === "wlt" && wallettype === "PPE";
104
82
  case "google_pay":
105
- return clearingtype === "wlt" && (wallettype === "GPY" || wallettype === "GOOGLEPAY");
83
+ return clearingtype === "wlt" && ["GPY", "GOOGLEPAY"].includes(wallettype);
106
84
  case "apple_pay":
107
- return clearingtype === "wlt" && (wallettype === "APL" || wallettype === "APPLEPAY");
85
+ return clearingtype === "wlt" && ["APL", "APPLEPAY"].includes(wallettype);
108
86
  case "sofort":
109
87
  return clearingtype === "sb";
110
88
  case "sepa":
111
89
  return clearingtype === "elv";
112
90
  default:
113
- return false;
91
+ return true;
114
92
  }
115
93
  });
116
94
  }
117
95
 
118
96
  if (filters.date_from) {
119
- transactionHistory = transactionHistory.filter(
120
- (transaction) =>
121
- new Date(transaction.timestamp) >= new Date(filters.date_from)
97
+ const dateFrom = new Date(filters.date_from);
98
+ dateFrom.setHours(0, 0, 0, 0);
99
+ result = result.filter(
100
+ (t) => new Date(t.timestamp) >= dateFrom
122
101
  );
123
102
  }
124
103
 
125
104
  if (filters.date_to) {
126
- transactionHistory = transactionHistory.filter(
127
- (transaction) =>
128
- new Date(transaction.timestamp) <= new Date(filters.date_to)
105
+ const dateTo = new Date(filters.date_to);
106
+ dateTo.setHours(23, 59, 59, 999);
107
+ result = result.filter(
108
+ (t) => new Date(t.timestamp) <= dateTo
129
109
  );
130
110
  }
131
111
 
132
- if (filters.status) {
133
- transactionHistory = transactionHistory.filter(
134
- (transaction) => transaction.status === filters.status
135
- );
136
- }
112
+ return result;
113
+ };
137
114
 
138
- // Apply sorting
139
- if (filters.sort_by && filters.sort_order) {
140
- const sortOrder = filters.sort_order === "desc" ? -1 : 1;
141
-
142
- transactionHistory.sort((a, b) => {
143
- let aValue, bValue;
144
-
145
- switch (filters.sort_by) {
146
- case "amount":
147
- aValue = a.amount || 0;
148
- bValue = b.amount || 0;
149
- break;
150
- case "created_at":
151
- aValue = new Date(a.created_at || a.timestamp || 0).getTime();
152
- bValue = new Date(b.created_at || b.timestamp || 0).getTime();
153
- break;
154
- case "status":
155
- aValue = (a.status || "").toLowerCase();
156
- bValue = (b.status || "").toLowerCase();
157
- break;
158
- case "reference":
159
- aValue = (a.reference || "").toLowerCase();
160
- bValue = (b.reference || "").toLowerCase();
161
- break;
162
- case "method":
163
- const aClearingType = a.raw_request?.clearingtype || "";
164
- const bClearingType = b.raw_request?.clearingtype || "";
165
- const aWalletType = a.raw_request?.wallettype || "";
166
- const bWalletType = b.raw_request?.wallettype || "";
167
- aValue = `${aClearingType}_${aWalletType}`.toLowerCase();
168
- bValue = `${bClearingType}_${bWalletType}`.toLowerCase();
169
- break;
170
- default:
171
- return 0;
172
- }
115
+ const getTransactionHistory = async (strapi, { filters = {}, pagination = {} }) => {
116
+ const pluginStore = getPluginStore(strapi);
173
117
 
174
- if (aValue < bValue) return -1 * sortOrder;
175
- if (aValue > bValue) return 1 * sortOrder;
176
- return 0;
177
- });
178
- }
118
+ let transactions =
119
+ (await pluginStore.get({ key: "transactionHistory" })) || [];
120
+
121
+ transactions = applyFilters(transactions, filters);
122
+ const page = Number(pagination.page) || 1;
123
+ const pageSize = Number(pagination.pageSize) || 10;
124
+
125
+ const total = transactions.length;
126
+ const pageCount = Math.max(1, Math.ceil(total / pageSize));
127
+
128
+ const validPage = Math.min(Math.max(1, page), pageCount);
179
129
 
180
- return transactionHistory;
130
+ const start = (validPage - 1) * pageSize;
131
+ const end = Math.min(start + pageSize, total);
132
+
133
+ const paginatedData = start < total ? transactions.slice(start, end) : [];
134
+
135
+ return {
136
+ data: paginatedData,
137
+ pagination: {
138
+ page: validPage,
139
+ pageSize,
140
+ pageCount,
141
+ total,
142
+ },
143
+ };
181
144
  };
182
145
 
183
146
  module.exports = {
@@ -0,0 +1,87 @@
1
+ "use strict";
2
+
3
+ const crypto = require("crypto");
4
+ const { getPluginStore, getSettings } = require("./settingsService");
5
+
6
+ const verifyHash = (notificationData, portalKey) => {
7
+ const {
8
+ portalid = "",
9
+ aid = "",
10
+ txid = "",
11
+ sequencenumber = "",
12
+ price = "",
13
+ currency = "",
14
+ mode = "",
15
+ } = notificationData;
16
+
17
+ const hashString = `${portalid}${aid}${txid}${sequencenumber}${price}${currency}${mode}${portalKey}`;
18
+ const expectedHash = crypto.createHash("md5").update(hashString).digest("hex");
19
+
20
+ return expectedHash.toLowerCase() === (notificationData.key || "").toLowerCase();
21
+ };
22
+
23
+ const processTransactionStatus = async (strapi, notificationData) => {
24
+ try {
25
+ const settings = await getSettings(strapi);
26
+ const txid = notificationData.txid;
27
+
28
+ if (!settings || !settings.key) {
29
+ console.log("[Payone TransactionStatus] Settings not found or key missing");
30
+ return;
31
+ }
32
+
33
+ const isValid = verifyHash(notificationData, settings.key);
34
+ if (!isValid) {
35
+ console.log(`[Payone TransactionStatus] Hash verification failed txid: ${txid}`);
36
+ return;
37
+ }
38
+
39
+ if (notificationData.portalid !== settings.portalid || notificationData.aid !== settings.aid) {
40
+ console.log(`[Payone TransactionStatus] Portal ID or AID mismatch txid: ${txid}`);
41
+ return;
42
+ }
43
+
44
+ const pluginStore = getPluginStore(strapi);
45
+ let transactionHistory = (await pluginStore.get({ key: "transactionHistory" })) || [];
46
+
47
+ const transaction = transactionHistory.find((t) => t.txid === txid || t.id === txid);
48
+
49
+ if (transaction) {
50
+ Object.assign(transaction, {
51
+ ...notificationData,
52
+ status: notificationData?.transaction_status,
53
+ txaction: notificationData?.txaction,
54
+ txtime: notificationData?.txtime,
55
+ sequencenumber: notificationData?.sequencenumber,
56
+ balance: notificationData?.balance,
57
+ receivable: notificationData?.receivable,
58
+ price: notificationData?.price,
59
+ amount: notificationData?.price ? parseFloat(notificationData?.price) * 100 : transaction?.amount,
60
+ userid: notificationData?.userid,
61
+ updated_at: new Date().toISOString(),
62
+ body: {
63
+ ...transaction?.body,
64
+ ...notificationData,
65
+ status: notificationData?.transaction_status
66
+ }
67
+ });
68
+
69
+ await pluginStore.set({
70
+ key: "transactionHistory",
71
+ value: transactionHistory,
72
+ });
73
+
74
+ console.log(`[Payone TransactionStatus] Successfully updated transaction txid: ${txid}`);
75
+ } else {
76
+ console.log(`[Payone TransactionStatus] Transaction ${txid} not found in history. Notification ignored.`);
77
+ }
78
+
79
+ } catch (error) {
80
+ console.log(`[Payone TransactionStatus] Error processing notification: ${error}`);
81
+ }
82
+ };
83
+
84
+ module.exports = {
85
+ verifyHash,
86
+ processTransactionStatus,
87
+ };
@@ -1,11 +1,5 @@
1
1
  "use strict";
2
2
 
3
- /**
4
- * Normalize reference string for Payone API
5
- * @param {string} input - Input reference
6
- * @param {string} fallbackPrefix - Fallback prefix if input is empty
7
- * @returns {string} Normalized reference (max 20 chars)
8
- */
9
3
  const normalizeReference = (input, fallbackPrefix = "REF") => {
10
4
  try {
11
5
  const raw = input == null ? "" : String(input);
@@ -22,12 +16,6 @@ const normalizeReference = (input, fallbackPrefix = "REF") => {
22
16
  }
23
17
  };
24
18
 
25
- /**
26
- * Normalize customer ID for Payone API (max 17 characters)
27
- * @param {string|null} customerid - Customer ID
28
- * @param {Object|null} logger - Logger instance
29
- * @returns {string} Normalized customer ID
30
- */
31
19
  const normalizeCustomerId = (customerid, logger = null) => {
32
20
  if (!customerid) {
33
21
  const timestamp = Date.now().toString().slice(-10);
@@ -56,7 +56,6 @@ const addPaymentMethodParams = (params, logger) => {
56
56
  'clearingtype', 'paymentMethod', 'settings', 'enable3DSecure', 'ecommercemode'
57
57
  ]);
58
58
 
59
- // Extract custom params that are not in known params
60
59
  Object.keys(updated).forEach(key => {
61
60
  if (!knownParams.has(key) && !key.startsWith('add_paydata[')) {
62
61
  customParams[key] = updated[key];
@@ -2,6 +2,38 @@
2
2
 
3
3
  const crypto = require("crypto");
4
4
  const { normalizeCustomerId } = require("./normalize");
5
+ const calculateKeyHash = (settings, params) => {
6
+ const portalKey = settings.portalKey || settings.key;
7
+ const portalid = String(settings.portalid || "");
8
+ const aid = String(settings.aid || "");
9
+ const mode = String(settings.mode || "test");
10
+
11
+ const requestType = params.request || "";
12
+
13
+ // For Capture and Refund operations
14
+ if (requestType === "capture" || requestType === "refund") {
15
+ const txid = String(params.txid || "");
16
+ const sequencenumber = String(params.sequencenumber || "");
17
+ const amount = String(params.amount || "");
18
+ const currency = String(params.currency || "EUR");
19
+
20
+ const hashString = `${portalid}${aid}${txid}${sequencenumber}${amount}${currency}${mode}${portalKey}`;
21
+ return crypto.createHash("md5").update(hashString).digest("hex");
22
+ }
23
+
24
+ // For Preauthorization and Authorization operations
25
+ if (requestType === "preauthorization" || requestType === "authorization") {
26
+ const amount = String(params.amount || "");
27
+ const currency = String(params.currency || "EUR");
28
+ const reference = String(params.reference || "");
29
+
30
+ const hashString = `${portalid}${aid}${amount}${currency}${reference}${mode}${portalKey}`;
31
+ return crypto.createHash("md5").update(hashString).digest("hex");
32
+ }
33
+
34
+ const hashString = `${portalid}${aid}${mode}${portalKey}`;
35
+ return crypto.createHash("md5").update(hashString).digest("hex");
36
+ };
5
37
 
6
38
  const buildClientRequestParams = (settings, params, logger = null) => {
7
39
  const requestParams = {
@@ -14,16 +46,13 @@ const buildClientRequestParams = (settings, params, logger = null) => {
14
46
  ...params
15
47
  };
16
48
 
17
- requestParams.key = crypto
18
- .createHash("md5")
19
- .update(settings.portalKey || settings.key)
20
- .digest("hex");
21
-
22
49
  requestParams.customerid = normalizeCustomerId(
23
50
  requestParams.customerid,
24
51
  logger
25
52
  );
26
53
 
54
+ requestParams.key = calculateKeyHash(settings, requestParams);
55
+
27
56
  const isCreditCard = requestParams.clearingtype === "cc";
28
57
  const enable3DSecure = settings.enable3DSecure !== false;
29
58
 
@@ -20,17 +20,12 @@ const parseResponse = (responseText, logger) => {
20
20
  }
21
21
  }
22
22
 
23
- // Parse URL-encoded response
24
23
  const params = new URLSearchParams(responseText);
25
24
  const response = {};
26
25
  for (const [key, value] of params) {
27
- // Store both lowercase and original case
28
26
  response[key.toLowerCase()] = value;
29
27
  response[key] = value;
30
-
31
- // Also handle add_paydata fields with brackets
32
- // Payone returns: add_paydata[applepay_payment_session]=BASE64_STRING
33
- // URLSearchParams handles brackets, but we need to ensure we can access it
28
+
34
29
  if (key.includes('add_paydata') || key.includes('addPaydata')) {
35
30
  // Store with original key format
36
31
  response[key] = value;
@@ -67,11 +62,11 @@ const extractTxId = (data) => {
67
62
  const requires3DSRedirect = (data) => {
68
63
  const status = (data.status || data.Status || "").toUpperCase();
69
64
  const errorCode = data.errorcode || data.ErrorCode || data.Error?.ErrorCode;
70
-
65
+
71
66
  // Check for redirect URL in various possible fields
72
- const redirecturl =
73
- data.redirecturl ||
74
- data.RedirectUrl ||
67
+ const redirecturl =
68
+ data.redirecturl ||
69
+ data.RedirectUrl ||
75
70
  data.redirect_url ||
76
71
  data.redirectUrl ||
77
72
  data.RedirectURL ||
@@ -96,7 +91,7 @@ const requires3DSRedirect = (data) => {
96
91
  const isErrorResponse = (data) => {
97
92
  const status = (data.status || data.Status || "").toUpperCase();
98
93
  const errorCode = data.errorcode || data.ErrorCode || data.Error?.ErrorCode;
99
-
94
+
100
95
  return status === "ERROR" || status === "INVALID" || !!errorCode;
101
96
  };
102
97
 
@@ -107,9 +102,9 @@ const isErrorResponse = (data) => {
107
102
  */
108
103
  const get3DSRedirectUrl = (data) => {
109
104
  // Check all possible redirect URL fields
110
- const redirecturl =
111
- data.redirecturl ||
112
- data.RedirectUrl ||
105
+ const redirecturl =
106
+ data.redirecturl ||
107
+ data.RedirectUrl ||
113
108
  data.redirect_url ||
114
109
  data.redirectUrl ||
115
110
  data.RedirectURL ||