strapi-plugin-payone-provider 5.6.11 → 5.6.13

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.
@@ -4,14 +4,27 @@ import {
4
4
  SingleSelect,
5
5
  SingleSelectOption,
6
6
  Switch,
7
- DesignSystemProvider,
8
7
  Tooltip,
9
8
  Textarea,
10
9
  Toggle,
11
10
  Checkbox,
12
- Typography,
11
+ Typography
13
12
  } from "@strapi/design-system";
14
13
  import { Information } from "@strapi/icons";
14
+
15
+ const TooltipIcon = ({ tooltipContent }) => {
16
+ if (!tooltipContent) return null;
17
+ return (
18
+ <Tooltip label={tooltipContent ?? ""}>
19
+ <Information
20
+ style={{
21
+ cursor: "pointer"
22
+ }}
23
+ />
24
+ </Tooltip>
25
+ );
26
+ };
27
+
15
28
  export const InputComponent = ({
16
29
  inputType,
17
30
  name,
@@ -23,8 +36,6 @@ export const InputComponent = ({
23
36
  placeholder = "",
24
37
  onLabel = "True",
25
38
  offLabel = "False",
26
- systemTheme,
27
- theme,
28
39
  className = "payment-input",
29
40
  type = "text",
30
41
  ...props
@@ -43,16 +54,7 @@ export const InputComponent = ({
43
54
  type={type}
44
55
  endAction={
45
56
  tooltipContent ? (
46
- <DesignSystemProvider theme={theme}>
47
- <Tooltip label={tooltipContent ?? ""}>
48
- <Information
49
- style={{
50
- cursor: "pointer",
51
- color: systemTheme === "dark" ? "#fff" : "#000",
52
- }}
53
- />
54
- </Tooltip>
55
- </DesignSystemProvider>
57
+ <TooltipIcon tooltipContent={tooltipContent} />
56
58
  ) : null
57
59
  }
58
60
  {...props}
@@ -71,16 +73,7 @@ export const InputComponent = ({
71
73
  required={required}
72
74
  startAction={
73
75
  tooltipContent ? (
74
- <DesignSystemProvider theme={theme}>
75
- <Tooltip label={tooltipContent ?? ""}>
76
- <Information
77
- style={{
78
- cursor: "pointer",
79
- color: systemTheme === "dark" ? "#fff" : "#000",
80
- }}
81
- />
82
- </Tooltip>
83
- </DesignSystemProvider>
76
+ <TooltipIcon tooltipContent={tooltipContent} />
84
77
  ) : null
85
78
  }
86
79
  {...props}
@@ -143,16 +136,7 @@ export const InputComponent = ({
143
136
  type="date"
144
137
  startAction={
145
138
  tooltipContent ? (
146
- <DesignSystemProvider theme={theme}>
147
- <Tooltip label={tooltipContent ?? ""}>
148
- <Information
149
- style={{
150
- cursor: "pointer",
151
- color: systemTheme === "dark" ? "#fff" : "#000",
152
- }}
153
- />
154
- </Tooltip>
155
- </DesignSystemProvider>
139
+ <TooltipIcon tooltipContent={tooltipContent} />
156
140
  ) : null
157
141
  }
158
142
  {...props}
@@ -179,7 +163,7 @@ export const InputComponent = ({
179
163
  if (typeof onChange === "function") {
180
164
  const syntheticEvent = {
181
165
  target: { value: selectedValue },
182
- currentTarget: { value: selectedValue },
166
+ currentTarget: { value: selectedValue }
183
167
  };
184
168
  onChange(syntheticEvent);
185
169
  }
@@ -188,16 +172,7 @@ export const InputComponent = ({
188
172
  placeholder={placeholder}
189
173
  startIcon={
190
174
  tooltipContent ? (
191
- <DesignSystemProvider theme={theme}>
192
- <Tooltip label={tooltipContent ?? ""}>
193
- <Information
194
- style={{
195
- cursor: "pointer",
196
- color: systemTheme === "dark" ? "#fff" : "#000",
197
- }}
198
- />
199
- </Tooltip>
200
- </DesignSystemProvider>
175
+ <TooltipIcon tooltipContent={tooltipContent} />
201
176
  ) : null
202
177
  }
203
178
  {...props}
@@ -31,8 +31,9 @@ export const formatDate = (dateString) => {
31
31
  };
32
32
 
33
33
  export const getPaymentMethodName = (clearingtype, wallettype) => {
34
+
34
35
  switch (clearingtype) {
35
- case "cc":
36
+ case "cc" || "card" | "c":
36
37
  return "Credit Card";
37
38
  case "sb":
38
39
  return "Online Banking";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "strapi-plugin-payone-provider",
3
- "version": "5.6.11",
3
+ "version": "5.6.13",
4
4
  "description": "Strapi plugin for Payone payment gateway integration",
5
5
  "license": "MIT",
6
6
  "maintainers": [
@@ -0,0 +1,5 @@
1
+ const transactionsContentType = require('./transactions/index.js')
2
+
3
+ module.exports = {
4
+ 'transaction': transactionsContentType
5
+ }
@@ -0,0 +1,5 @@
1
+ const schema = require('./schema.json');
2
+
3
+ module.exports = {
4
+ schema
5
+ }
@@ -0,0 +1,87 @@
1
+ {
2
+ "kind": "collectionType",
3
+ "collectionName": "transactions",
4
+ "info": {
5
+ "singularName": "transaction",
6
+ "pluralName": "transactions",
7
+ "displayName": "Transaction"
8
+ },
9
+ "options": {
10
+ "draftAndPublish": false
11
+ },
12
+ "pluginOptions": {
13
+ "content-manager": {
14
+ "visible": false
15
+ },
16
+ "content-type-builder": {
17
+ "visible": false
18
+ }
19
+ },
20
+ "attributes": {
21
+ "txid": {
22
+ "type": "string",
23
+ "required": false,
24
+ "default": "NO TXID"
25
+ },
26
+ "reference": {
27
+ "type": "string",
28
+ "required": false,
29
+ "default": "NO REFERENCE"
30
+ },
31
+ "invoiceid": {
32
+ "type": "string",
33
+ "required": false,
34
+ "default": "NO INVOICE ID"
35
+ },
36
+ "amount": {
37
+ "type": "string",
38
+ "required": false,
39
+ "default": "0"
40
+ },
41
+ "currency": {
42
+ "type": "string",
43
+ "required": false,
44
+ "default": "EUR"
45
+ },
46
+ "status": {
47
+ "type": "string",
48
+ "required": false,
49
+ "default": "unknown"
50
+ },
51
+ "error_code": {
52
+ "type": "string",
53
+ "required": false,
54
+ "default": "NO ERROR CODE"
55
+ },
56
+ "request_type": {
57
+ "type": "string",
58
+ "required": false,
59
+ "default": "unknown"
60
+ },
61
+ "error_message": {
62
+ "type": "string",
63
+ "required": false,
64
+ "default": "NO ERROR MESSAGE"
65
+ },
66
+ "customer_message": {
67
+ "type": "string",
68
+ "required": false,
69
+ "default": "NO CUSTOMER MESSAGE"
70
+ },
71
+ "body": {
72
+ "type": "json",
73
+ "required": false,
74
+ "default": {}
75
+ },
76
+ "raw_request": {
77
+ "type": "json",
78
+ "required": false,
79
+ "default": {}
80
+ },
81
+ "raw_response": {
82
+ "type": "json",
83
+ "required": false,
84
+ "default": {}
85
+ }
86
+ }
87
+ }
@@ -156,12 +156,22 @@ module.exports = ({ strapi }) => ({
156
156
 
157
157
  async getTransactionHistory(ctx) {
158
158
  try {
159
- const { filters = {}, pagination = {} } = ctx.query || {};
159
+ const { filters: rawFilters = {}, pagination = {} } = ctx.query || {};
160
160
  const page = parseInt(pagination.page || "1", 10);
161
161
  const pageSize = parseInt(pagination.pageSize || "10", 10);
162
162
 
163
+ const filters = {};
164
+ if (rawFilters && typeof rawFilters === "object") {
165
+ for (const [key, value] of Object.entries(rawFilters)) {
166
+ const v = value == null ? "" : String(value).trim();
167
+ if (v !== "" && v.toLowerCase() !== "all") {
168
+ filters[key] = value;
169
+ }
170
+ }
171
+ }
172
+
163
173
  const result = await getPayoneService(strapi).getTransactionHistory({
164
- filters: filters || {},
174
+ filters,
165
175
  pagination: { page, pageSize }
166
176
  });
167
177
 
@@ -299,19 +309,20 @@ module.exports = ({ strapi }) => ({
299
309
 
300
310
  async handleTransactionStatus(ctx) {
301
311
  try {
302
- const notificationData = ctx.request.body || {};
303
- await getPayoneService(strapi).processTransactionStatus(notificationData);
304
-
305
- ctx.status = 200;
306
- ctx.body = "TSOK";
307
- ctx.type = "text/plain";
308
- console.log(`[Payone TransactionStatus] Responded TSOK`);
312
+ if (!ctx.state.payoneAllowed) {
313
+ console.log("[Payone] Notification ignored (policy failed)");
314
+ } else {
315
+ const notificationData = ctx.request.body || {};
316
+ console.log(notificationData);
317
+ await getPayoneService(strapi).processTransactionStatus(notificationData);
318
+ }
309
319
  } catch (error) {
310
- console.log("[Payone TransactionStatus] Error handling notification:", error);
311
- ctx.status = 200;
312
- ctx.body = "TSOK";
313
- ctx.type = "text/plain";
320
+ console.log("[Payone TransactionStatus] Error:", error);
314
321
  }
322
+
323
+ ctx.status = 200;
324
+ ctx.body = "TSOK";
325
+ ctx.type = "text/plain";
315
326
  }
316
327
 
317
328
  });
package/server/index.js CHANGED
@@ -8,6 +8,7 @@ const controllers = require("./controllers");
8
8
  const routes = require("./routes");
9
9
  const services = require("./services");
10
10
  const policies = require("./policies");
11
+ const contentTypes = require("./content-types");
11
12
 
12
13
  module.exports = {
13
14
  register,
@@ -15,6 +16,7 @@ module.exports = {
15
16
  destroy,
16
17
  config,
17
18
  controllers,
19
+ contentTypes,
18
20
  routes,
19
21
  services,
20
22
  policies
@@ -1,30 +1,19 @@
1
- "use strict";
1
+ module.exports = async (ctx) => {
2
+ const { request } = ctx;
2
3
 
3
- module.exports = async (policyContext, config, { strapi }) => {
4
- const { request } = policyContext;
5
- const userAgent = request.header["user-agent"] || request.header["User-Agent"] || "";
6
- const clientIp = request.ip || request.connection?.remoteAddress || "";
4
+ const userAgent = request.headers["user-agent"] || "";
5
+ const clientIp =
6
+ request.headers["x-payone-client-ip"]?.trim() ||
7
+ request.headers["x-forwarded-for"]?.split(",")[0]?.trim() ||
8
+ request.ip ||
9
+ "";
7
10
 
8
- if (userAgent !== "PAYONE FinanceGate") {
9
- console.log(`[Payone TransactionStatus] Invalid User-Agent: ${userAgent}, IP: ${clientIp}`);
10
- return false;
11
- }
12
-
13
-
14
- const isValidIp = (ip) => {
15
- if (ip.startsWith("185.60.20.")) {
16
- return true;
17
- }
11
+ const isValid = userAgent === "PAYONE FinanceGate" && (clientIp.startsWith("185.60.20.") || clientIp === "54.246.203.105");
18
12
 
19
- if (ip === "54.246.203.105") {
20
- return true;
21
- }
22
- return false;
23
- };
13
+ ctx.state.payoneAllowed = isValid;
24
14
 
25
- if (!isValidIp(clientIp)) {
26
- console.log(`[Payone TransactionStatus] Invalid IP address: ${clientIp}, User-Agent: ${userAgent}`);
27
- return false;
15
+ if (!isValid) {
16
+ console.log("[Payone] Policy failed", { userAgent, clientIp });
28
17
  }
29
18
 
30
19
  return true;
@@ -1,139 +1,172 @@
1
1
  "use strict";
2
2
 
3
- const { getPluginStore } = require("./settingsService");
3
+ const { sanitizeSensitive } = require("../utils/sanitize");
4
4
 
5
- const logTransaction = async (strapi, transactionData) => {
6
- const pluginStore = getPluginStore(strapi);
7
- let transactionHistory =
8
- (await pluginStore.get({ key: "transactionHistory" })) || [];
9
-
10
- const logEntry = {
11
- id: Date.now().toString(),
12
- timestamp: new Date().toISOString(),
13
- txid: transactionData.txid || null,
14
- reference: transactionData.reference || null,
15
- invoiceid: transactionData.invoiceid || null,
16
- request_type:
17
- transactionData.request_type || transactionData.request || "unknown",
18
- amount: transactionData.amount || null,
19
- currency: transactionData.currency || "EUR",
20
- status: transactionData.status || transactionData.Status || "unknown",
21
- error_code:
22
- transactionData.error_code || transactionData.Error?.ErrorCode || null,
23
- error_message:
24
- transactionData.error_message ||
25
- transactionData.Error?.ErrorMessage ||
26
- null,
27
- customer_message:
28
- transactionData.customer_message ||
29
- transactionData.Error?.CustomerMessage ||
30
- null,
31
- body: transactionData || null,
32
- created_at: new Date().toISOString(),
33
- updated_at: new Date().toISOString()
34
- };
5
+ const TRANSACTION_UID = "plugin::strapi-plugin-payone-provider.transaction";
35
6
 
36
- transactionHistory.unshift(logEntry);
7
+ const logTransaction = async (strapi, transactionData) => {
8
+ try {
9
+ const data = {
10
+ txid: transactionData.txid || 'NO TXID',
11
+ reference: transactionData.reference || 'NO REFERENCE',
12
+ invoiceid: transactionData.raw_request.invoiceid || 'NO INVOICE ID',
13
+ request_type: transactionData.request_type || "unknown",
14
+ amount: transactionData.amount || "0",
15
+ currency: transactionData.currency || "EUR",
16
+ status: transactionData.status || transactionData.raw_response.Status || "unknown",
17
+ error_code: transactionData.error_code || "NO ERROR CODE",
18
+ error_message: transactionData.error_message || "NO ERROR MESSAGE",
19
+ customer_message: transactionData.customer_message || "NO CUSTOMER MESSAGE",
20
+ body: transactionData ? { ...transactionData, raw_request: sanitizeSensitive(transactionData.raw_request), raw_response: sanitizeSensitive(transactionData.raw_response) } : {},
21
+ raw_request: sanitizeSensitive(transactionData.raw_request || {}),
22
+ raw_response: sanitizeSensitive(transactionData.raw_response || {}),
23
+ };
24
+
25
+ const entry = await strapi.db.query(TRANSACTION_UID).create({ data });
26
+ console.info("Transaction logged to DB:", {
27
+ id: entry.id,
28
+ txid: entry.txid,
29
+ status: entry.status
30
+ });
37
31
 
38
- if (transactionHistory.length > 1000) {
39
- transactionHistory = transactionHistory.slice(0, 1000);
32
+ return entry;
33
+ } catch (error) {
34
+ console.error("Failed to log transaction:", error);
40
35
  }
36
+ };
41
37
 
42
- await pluginStore.set({
43
- key: "transactionHistory",
44
- value: transactionHistory
45
- });
46
38
 
47
- console.log(`Transaction logged: ${logEntry}`);
48
- };
39
+ const hasFilterValue = (v) =>
40
+ typeof v === "string" && v.trim() !== "" && v.trim().toLowerCase() !== "all";
49
41
 
42
+ const buildWhereFromFilters = (filters = {}) => {
43
+ const conditions = [];
50
44
 
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);
45
+ if (hasFilterValue(filters.search)) {
46
+ const search = String(filters.search).trim();
47
+ conditions.push({
48
+ $or: [
49
+ { txid: { $containsi: search } },
50
+ { reference: { $containsi: search } },
51
+ ],
59
52
  });
60
53
  }
61
54
 
62
- if (filters.status) {
63
- result = result.filter(
64
- (t) => (t.status || "").toUpperCase() === filters.status.toUpperCase()
65
- );
66
- }
67
-
68
- if (filters.request_type) {
69
- result = result.filter((t) => t.request_type === filters.request_type);
55
+ if (hasFilterValue(filters.status)) {
56
+ conditions.push({ status: { $eqi: String(filters.status).trim() } });
70
57
  }
71
58
 
72
- if (filters.payment_method) {
73
- result = result.filter((t) => {
74
- const clearingtype = t.raw_request?.clearingtype;
75
- const wallettype = t.raw_request?.wallettype;
76
-
77
- switch (filters.payment_method) {
78
- case "credit_card":
79
- return clearingtype === "cc";
80
- case "paypal":
81
- return clearingtype === "wlt" && wallettype === "PPE";
82
- case "google_pay":
83
- return clearingtype === "wlt" && ["GPY", "GOOGLEPAY"].includes(wallettype);
84
- case "apple_pay":
85
- return clearingtype === "wlt" && ["APL", "APPLEPAY"].includes(wallettype);
86
- case "sofort":
87
- return clearingtype === "sb";
88
- case "sepa":
89
- return clearingtype === "elv";
90
- default:
91
- return true;
92
- }
93
- });
59
+ if (hasFilterValue(filters.request_type)) {
60
+ conditions.push({ request_type: String(filters.request_type).trim() });
94
61
  }
95
62
 
96
- if (filters.date_from) {
63
+ if (hasFilterValue(filters.date_from)) {
97
64
  const dateFrom = new Date(filters.date_from);
98
65
  dateFrom.setHours(0, 0, 0, 0);
99
- result = result.filter(
100
- (t) => new Date(t.timestamp) >= dateFrom
101
- );
66
+ conditions.push({ createdAt: { $gte: dateFrom.toISOString() } });
102
67
  }
103
68
 
104
- if (filters.date_to) {
69
+ if (hasFilterValue(filters.date_to)) {
105
70
  const dateTo = new Date(filters.date_to);
106
71
  dateTo.setHours(23, 59, 59, 999);
107
- result = result.filter(
108
- (t) => new Date(t.timestamp) <= dateTo
109
- );
72
+ conditions.push({ createdAt: { $lte: dateTo.toISOString() } });
110
73
  }
111
74
 
112
- return result;
113
- };
75
+ if (hasFilterValue(filters.payment_method)) {
76
+ switch (filters.payment_method) {
77
+ case "credit_card":
78
+ conditions.push({ raw_request: { $containsi: '"clearingtype":"cc"' } });
79
+ break;
80
+ case "paypal":
81
+ conditions.push({
82
+ $and: [
83
+ { raw_request: { $containsi: '"clearingtype":"wlt"' } },
84
+ { raw_request: { $containsi: '"wallettype":"PPE"' } },
85
+ ],
86
+ });
87
+ break;
88
+ case "google_pay":
89
+ conditions.push({
90
+ $and: [
91
+ { raw_request: { $containsi: '"clearingtype":"wlt"' } },
92
+ {
93
+ $or: [
94
+ { raw_request: { $containsi: '"wallettype":"GPY"' } },
95
+ { raw_request: { $containsi: '"wallettype":"GOOGLEPAY"' } },
96
+ ],
97
+ },
98
+ ],
99
+ });
100
+ break;
101
+ case "apple_pay":
102
+ conditions.push({
103
+ $and: [
104
+ { raw_request: { $containsi: '"clearingtype":"wlt"' } },
105
+ {
106
+ $or: [
107
+ { raw_request: { $containsi: '"wallettype":"APL"' } },
108
+ { raw_request: { $containsi: '"wallettype":"APPLEPAY"' } },
109
+ ],
110
+ },
111
+ ],
112
+ });
113
+ break;
114
+ case "sofort":
115
+ conditions.push({ raw_request: { $containsi: '"clearingtype":"sb"' } });
116
+ break;
117
+ case "sepa":
118
+ conditions.push({ raw_request: { $containsi: '"clearingtype":"elv"' } });
119
+ break;
120
+ default:
121
+ break;
122
+ }
123
+ }
114
124
 
115
- const getTransactionHistory = async (strapi, { filters = {}, pagination = {} }) => {
116
- const pluginStore = getPluginStore(strapi);
125
+ if (conditions.length === 0) return undefined;
126
+ if (conditions.length === 1) return conditions[0];
127
+ return { $and: conditions };
128
+ };
117
129
 
118
- let transactions =
119
- (await pluginStore.get({ key: "transactionHistory" })) || [];
130
+ const ALLOWED_SORT_FIELDS = [
131
+ "txid",
132
+ "reference",
133
+ "amount",
134
+ "request_type",
135
+ "status",
136
+ "createdAt",
137
+ "updatedAt",
138
+ ];
139
+
140
+ const getTransactionHistory = async (
141
+ strapi,
142
+ { filters = {}, pagination = {}, sort_by, sort_order }
143
+ ) => {
144
+ const page = Math.max(1, Number(pagination.page) || 1);
145
+ const pageSize = Math.min(100, Math.max(1, Number(pagination.pageSize) || 10));
146
+ const offset = (page - 1) * pageSize;
147
+
148
+ const where = buildWhereFromFilters(filters);
149
+
150
+ const sortField =
151
+ sort_by && ALLOWED_SORT_FIELDS.includes(sort_by) ? sort_by : "createdAt";
152
+ const order = sort_order === "asc" ? "asc" : "desc";
153
+
154
+ const queryOptions = {
155
+ orderBy: { [sortField]: order },
156
+ limit: pageSize,
157
+ offset,
158
+ };
159
+ if (where !== undefined) queryOptions.where = where;
120
160
 
121
- transactions = applyFilters(transactions, filters);
122
- const page = Number(pagination.page) || 1;
123
- const pageSize = Number(pagination.pageSize) || 10;
161
+ const [data, total] = await strapi.db
162
+ .query(TRANSACTION_UID)
163
+ .findWithCount(queryOptions);
124
164
 
125
- const total = transactions.length;
126
165
  const pageCount = Math.max(1, Math.ceil(total / pageSize));
127
-
128
- const validPage = Math.min(Math.max(1, page), pageCount);
129
-
130
- const start = (validPage - 1) * pageSize;
131
- const end = Math.min(start + pageSize, total);
132
-
133
- const paginatedData = start < total ? transactions.slice(start, end) : [];
166
+ const validPage = Math.min(page, pageCount);
134
167
 
135
168
  return {
136
- data: paginatedData,
169
+ data,
137
170
  pagination: {
138
171
  page: validPage,
139
172
  pageSize,