strapi-plugin-payone-provider 5.6.10 → 5.6.12
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 +99 -608
- package/admin/src/components/PluginIcon/index.jsx +14 -3
- package/admin/src/pages/App/components/AppHeader.jsx +1 -1
- package/admin/src/pages/App/components/AppTabs.jsx +8 -5
- package/admin/src/pages/App/components/StatusBadge.jsx +69 -17
- package/admin/src/pages/App/components/configuration/ConfigurationPanel.jsx +2 -2
- package/admin/src/pages/App/components/icons/index.jsx +1 -0
- package/admin/src/pages/App/components/transaction-history/FiltersPanel.jsx +4 -3
- package/admin/src/pages/App/components/transaction-history/TransactionTable.jsx +6 -5
- package/admin/src/pages/App/components/transaction-history/details/TransactionDetails.jsx +10 -10
- package/admin/src/pages/hooks/useSettings.js +0 -1
- package/admin/src/pages/utils/api.js +3 -2
- package/admin/src/pages/utils/transactionTableUtils.js +2 -1
- package/package.json +2 -2
- package/server/bootstrap.js +5 -6
- package/server/content-types/index.js +5 -0
- package/server/content-types/transactions/index.js +5 -0
- package/server/content-types/transactions/schema.json +87 -0
- package/server/controllers/payone.js +24 -13
- package/server/index.js +2 -0
- package/server/policies/is-payone-notification.js +12 -23
- package/server/services/transactionService.js +137 -104
- package/server/services/transactionStatusService.js +55 -61
- package/server/utils/requestBuilder.js +4 -32
- package/server/utils/sanitize.js +42 -0
|
@@ -1,6 +1,17 @@
|
|
|
1
|
-
import * as React from '@strapi/strapi/admin';
|
|
2
|
-
import { PuzzlePiece } from "@strapi/icons";
|
|
3
1
|
|
|
4
|
-
const PluginIcon = () =>
|
|
2
|
+
const PluginIcon = () => (
|
|
3
|
+
<svg
|
|
4
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
5
|
+
xmlSpace="preserve"
|
|
6
|
+
width={18}
|
|
7
|
+
height={18}
|
|
8
|
+
fill="currentColor"
|
|
9
|
+
version="1.1"
|
|
10
|
+
viewBox="0 0 438.254 438.254"
|
|
11
|
+
>
|
|
12
|
+
<path d="M402.612 177.546H368.25V63.187c0-19.652-15.988-35.64-35.64-35.64H35.641C15.988 27.547 0 43.535 0 63.187v161.88c0 19.652 15.988 35.64 35.641 35.64h34.357v114.358c0 19.653 15.989 35.642 35.642 35.642h296.973c19.652 0 35.642-15.989 35.642-35.642V213.188c-.001-19.653-15.99-35.642-35.643-35.642m-64.362-45.839H30v-20h308.25zM35.641 57.547h296.97c3.109 0 5.64 2.53 5.64 5.64v28.52H30v-28.52c0-3.11 2.53-5.64 5.641-5.64m0 173.16a5.647 5.647 0 0 1-5.641-5.64v-73.36h308.25v25.839H105.64c-19.652 0-35.642 15.989-35.642 35.642v17.519zm372.613 144.358a5.65 5.65 0 0 1-5.642 5.642H105.64a5.65 5.65 0 0 1-5.642-5.642V213.188a5.65 5.65 0 0 1 5.642-5.642h296.973a5.65 5.65 0 0 1 5.642 5.642z"></path>
|
|
13
|
+
<path d="M358.585 287.966c-6.941 0-13.48 1.74-19.216 4.797-5.885-3.15-12.429-4.797-19.265-4.797-22.607 0-41 18.393-41 41s18.393 41 41 41c6.835 0 13.379-1.647 19.265-4.796a40.74 40.74 0 0 0 19.216 4.796c22.607 0 41-18.393 41-41s-18.393-41-41-41m-59.481 41c0-11.579 9.421-21 21-21 1.065 0 2.113.09 3.145.243-3.593 6.093-5.664 13.186-5.664 20.757s2.071 14.664 5.664 20.757c-1.032.153-2.08.243-3.145.243-11.58 0-21-9.42-21-21m59.481 21c-11.579 0-21-9.421-21-21s9.421-21 21-21 21 9.421 21 21-9.421 21-21 21M177.126 241.707h-28c-5.522 0-10 4.477-10 10s4.478 10 10 10h28c5.522 0 10-4.477 10-10s-4.478-10-10-10M237.793 241.707h-28c-5.522 0-10 4.477-10 10s4.478 10 10 10h28c5.522 0 10-4.477 10-10s-4.478-10-10-10M308.459 251.707c0-5.523-4.478-10-10-10h-28c-5.522 0-10 4.477-10 10s4.478 10 10 10h28c5.522 0 10-4.477 10-10M331.126 261.707h28c5.522 0 10-4.477 10-10s-4.478-10-10-10h-28c-5.522 0-10 4.477-10 10s4.478 10 10 10"></path>
|
|
14
|
+
</svg>
|
|
15
|
+
);
|
|
5
16
|
|
|
6
17
|
export default PluginIcon;
|
|
@@ -22,19 +22,22 @@ const AppTabs = ({
|
|
|
22
22
|
}
|
|
23
23
|
>
|
|
24
24
|
<Tabs.List style={{ width: "100%" }}>
|
|
25
|
-
<Tabs.Trigger value="tab-1">
|
|
26
|
-
<Tabs.Trigger value="tab-2">
|
|
25
|
+
<Tabs.Trigger value="tab-1">Transaction History</Tabs.Trigger>
|
|
26
|
+
<Tabs.Trigger value="tab-2">Configuration</Tabs.Trigger>
|
|
27
27
|
<Tabs.Trigger value="tab-3">Payment Actions</Tabs.Trigger>
|
|
28
28
|
<Tabs.Trigger value="tab-4">Documentation</Tabs.Trigger>
|
|
29
29
|
</Tabs.List>
|
|
30
30
|
<Tabs.Content value="tab-1">
|
|
31
|
-
<
|
|
31
|
+
<HistoryPanel />
|
|
32
32
|
</Tabs.Content>
|
|
33
|
-
|
|
34
33
|
<Tabs.Content value="tab-2">
|
|
35
|
-
<
|
|
34
|
+
<ConfigurationPanel
|
|
35
|
+
settings={settings}
|
|
36
|
+
onNavigateToConfig={onNavigateToConfig}
|
|
37
|
+
/>
|
|
36
38
|
</Tabs.Content>
|
|
37
39
|
|
|
40
|
+
|
|
38
41
|
<Tabs.Content value="tab-3">
|
|
39
42
|
<PaymentActionsPanel
|
|
40
43
|
onNavigateToConfig={onNavigateToConfig}
|
|
@@ -1,13 +1,15 @@
|
|
|
1
|
-
import * as React from "
|
|
1
|
+
import * as React from "react";
|
|
2
2
|
import {
|
|
3
3
|
Badge,
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
Box,
|
|
5
|
+
Flex,
|
|
6
|
+
Typography,
|
|
7
7
|
} from "@strapi/design-system";
|
|
8
|
-
import {
|
|
8
|
+
import { MarkCircle } from "./icons";
|
|
9
|
+
|
|
10
|
+
const StatusBadge = ({ status, transaction }) => {
|
|
11
|
+
const [isHovered, setIsHovered] = React.useState(false);
|
|
9
12
|
|
|
10
|
-
const StatusBadge = ({ status, size = "S" }) => {
|
|
11
13
|
const statusColors = {
|
|
12
14
|
APPROVED: "success200",
|
|
13
15
|
PENDING: "warning200",
|
|
@@ -16,19 +18,69 @@ const StatusBadge = ({ status, size = "S" }) => {
|
|
|
16
18
|
INVALID: "danger200",
|
|
17
19
|
REDIRECT: "success100",
|
|
18
20
|
};
|
|
19
|
-
|
|
20
|
-
const
|
|
21
|
+
|
|
22
|
+
const getDisplayText = () => {
|
|
23
|
+
if (status === "ERROR" && transaction?.raw_response?.Error?.ErrorCode) {
|
|
24
|
+
return `${status} - ${transaction.raw_response.Error.ErrorCode}`;
|
|
25
|
+
}
|
|
26
|
+
return status;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const displayText = getDisplayText();
|
|
30
|
+
const errorMessage = status === "ERROR" && transaction?.raw_response?.Error?.ErrorMessage
|
|
31
|
+
? transaction.raw_response.Error.ErrorMessage
|
|
32
|
+
: null;
|
|
33
|
+
|
|
34
|
+
const errorCode = status === "ERROR" && transaction?.raw_response?.Error?.ErrorCode
|
|
35
|
+
? transaction.raw_response.Error.ErrorCode
|
|
36
|
+
: null;
|
|
37
|
+
|
|
38
|
+
const showExclamationIcon = status === "ERROR" && !errorCode && !errorMessage;
|
|
39
|
+
|
|
21
40
|
return (
|
|
22
|
-
<
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
41
|
+
<Box
|
|
42
|
+
position="relative"
|
|
43
|
+
onMouseEnter={() => setIsHovered(true)}
|
|
44
|
+
onMouseLeave={() => setIsHovered(false)}
|
|
45
|
+
style={{ display: "inline-block", cursor: status === "ERROR" ? "pointer" : "default" }}
|
|
46
|
+
>
|
|
47
|
+
<Flex gap={2} alignItems="center">
|
|
48
|
+
<Badge backgroundColor={statusColors[status] || "warning100"}>
|
|
49
|
+
{displayText}
|
|
30
50
|
</Badge>
|
|
31
|
-
|
|
51
|
+
{showExclamationIcon && (
|
|
52
|
+
<MarkCircle color="danger500"
|
|
53
|
+
style={{
|
|
54
|
+
width: "16px",
|
|
55
|
+
height: "16px"
|
|
56
|
+
}}
|
|
57
|
+
/>
|
|
58
|
+
)}
|
|
59
|
+
</Flex>
|
|
60
|
+
{isHovered && errorMessage && (
|
|
61
|
+
<Box
|
|
62
|
+
position="absolute"
|
|
63
|
+
zIndex={1000}
|
|
64
|
+
bottom="100%"
|
|
65
|
+
left="50%"
|
|
66
|
+
transform="translateX(-50%)"
|
|
67
|
+
marginBottom={2}
|
|
68
|
+
padding={3}
|
|
69
|
+
background="neutral900"
|
|
70
|
+
hasRadius
|
|
71
|
+
style={{
|
|
72
|
+
whiteSpace: "pre-line",
|
|
73
|
+
minWidth: "200px",
|
|
74
|
+
maxWidth: "300px",
|
|
75
|
+
boxShadow: "0 4px 12px rgba(0, 0, 0, 0.15)",
|
|
76
|
+
}}
|
|
77
|
+
>
|
|
78
|
+
<Typography variant="pi" textColor="neutral0" style={{ fontSize: "12px" }}>
|
|
79
|
+
Error: {errorMessage}
|
|
80
|
+
</Typography>
|
|
81
|
+
</Box>
|
|
82
|
+
)}
|
|
83
|
+
</Box>
|
|
32
84
|
);
|
|
33
85
|
};
|
|
34
86
|
|
|
@@ -3,7 +3,7 @@ import { Box, Flex, Typography } from "@strapi/design-system";
|
|
|
3
3
|
import ConfigurationFields from "./ConfigurationFields";
|
|
4
4
|
import TestConnection from "./TestConnection";
|
|
5
5
|
|
|
6
|
-
const ConfigurationPanel = ({ settings }) => {
|
|
6
|
+
const ConfigurationPanel = ({ settings, onNavigateToConfig }) => {
|
|
7
7
|
return (
|
|
8
8
|
<Flex direction="column" alignItems="stretch" gap={8} paddingTop={8}>
|
|
9
9
|
<Box>
|
|
@@ -31,7 +31,7 @@ const ConfigurationPanel = ({ settings }) => {
|
|
|
31
31
|
settings={settings.settings}
|
|
32
32
|
onInputChange={settings.handleInputChange}
|
|
33
33
|
onPaymentMethodToggle={settings.handlePaymentMethodToggle}
|
|
34
|
-
onNavigateToConfig={
|
|
34
|
+
onNavigateToConfig={onNavigateToConfig}
|
|
35
35
|
/>
|
|
36
36
|
|
|
37
37
|
<TestConnection
|
|
@@ -9,3 +9,4 @@ export { default as PendingIcon } from './PendingIcon.jsx';
|
|
|
9
9
|
export { default as InfoIcon } from './InfoIcon.jsx';
|
|
10
10
|
export { default as ChevronDownIcon } from './ChevronDownIcon.jsx';
|
|
11
11
|
export { default as ChevronUpIcon } from './ChevronUpIcon.jsx';
|
|
12
|
+
export { default as MarkCircle } from './MarkCircle.jsx';
|
|
@@ -95,12 +95,13 @@ const FiltersPanel = ({ filters, handleFiltersChange }) => {
|
|
|
95
95
|
<RenderInput
|
|
96
96
|
key={filter.name}
|
|
97
97
|
name={filter.name}
|
|
98
|
-
value={filters[filter.name]
|
|
98
|
+
value={filters[filter.name] ?? ""}
|
|
99
99
|
onChange={(e) => {
|
|
100
|
-
const
|
|
100
|
+
const raw =
|
|
101
101
|
filter.inputType === "textInput"
|
|
102
102
|
? e.target.value
|
|
103
|
-
: e.target?.value
|
|
103
|
+
: e.target?.value ?? e;
|
|
104
|
+
const value = raw === "all" || raw === undefined ? "" : String(raw);
|
|
104
105
|
handleFiltersChange({ [filter.name]: value });
|
|
105
106
|
}}
|
|
106
107
|
inputType={filter.inputType}
|
|
@@ -11,6 +11,7 @@ import TransactionDetails from "./details/TransactionDetails";
|
|
|
11
11
|
import FiltersPanel from "./FiltersPanel";
|
|
12
12
|
import useTransactionHistory from "../../../hooks/useTransactionHistory";
|
|
13
13
|
import StatusBadge from "../StatusBadge";
|
|
14
|
+
|
|
14
15
|
const TransactionTable = () => {
|
|
15
16
|
const {
|
|
16
17
|
transactions,
|
|
@@ -87,8 +88,8 @@ const TransactionTable = () => {
|
|
|
87
88
|
<Table.Cell>
|
|
88
89
|
<Typography variant="pi">
|
|
89
90
|
{getPaymentMethodName(
|
|
90
|
-
transaction.raw_request?.clearingtype,
|
|
91
|
-
transaction.raw_request?.wallettype
|
|
91
|
+
(transaction.raw_request?.clearingtype || transaction.body?.raw_request?.clearingtype),
|
|
92
|
+
(transaction.raw_request?.wallettype || transaction.body?.raw_request?.wallettype)
|
|
92
93
|
)}
|
|
93
94
|
</Typography>
|
|
94
95
|
</Table.Cell>
|
|
@@ -98,16 +99,16 @@ const TransactionTable = () => {
|
|
|
98
99
|
</Typography>
|
|
99
100
|
</Table.Cell>
|
|
100
101
|
<Table.Cell>
|
|
101
|
-
<StatusBadge status={transaction?.status}
|
|
102
|
+
<StatusBadge status={transaction?.status} transaction={transaction} />
|
|
102
103
|
</Table.Cell>
|
|
103
104
|
<Table.Cell>
|
|
104
105
|
<Typography variant="pi" textColor="neutral600">
|
|
105
|
-
{formatDate(transaction.created_at)}
|
|
106
|
+
{formatDate(transaction.created_at ?? transaction.createdAt)}
|
|
106
107
|
</Typography>
|
|
107
108
|
</Table.Cell>
|
|
108
109
|
<Table.Cell>
|
|
109
110
|
<Typography variant="pi" textColor="neutral600">
|
|
110
|
-
{formatDate(transaction.updated_at)}
|
|
111
|
+
{formatDate(transaction.updated_at ?? transaction.updatedAt)}
|
|
111
112
|
</Typography>
|
|
112
113
|
</Table.Cell>
|
|
113
114
|
<Table.Cell>
|
|
@@ -47,8 +47,8 @@ const TransactionDetails = ({ transaction }) => {
|
|
|
47
47
|
Name:
|
|
48
48
|
</Typography>
|
|
49
49
|
<Typography variant="pi" textColor="neutral800">
|
|
50
|
-
{transaction.raw_request?.firstname}{" "}
|
|
51
|
-
{transaction.raw_request?.lastname}
|
|
50
|
+
{transaction.raw_request?.firstname || transaction.body?.raw_request?.firstname }{" "}
|
|
51
|
+
{transaction.raw_request?.lastname || transaction.body?.raw_request?.lastname}
|
|
52
52
|
</Typography>
|
|
53
53
|
</Flex>
|
|
54
54
|
<Flex gap={3}>
|
|
@@ -56,7 +56,7 @@ const TransactionDetails = ({ transaction }) => {
|
|
|
56
56
|
Email:
|
|
57
57
|
</Typography>
|
|
58
58
|
<Typography variant="pi" textColor="neutral800">
|
|
59
|
-
{transaction.raw_request?.email || "N/A"}
|
|
59
|
+
{transaction.raw_request?.email || transaction.body?.raw_request?.email || "N/A"}
|
|
60
60
|
</Typography>
|
|
61
61
|
</Flex>
|
|
62
62
|
<Flex gap={3}>
|
|
@@ -64,7 +64,7 @@ const TransactionDetails = ({ transaction }) => {
|
|
|
64
64
|
Phone:
|
|
65
65
|
</Typography>
|
|
66
66
|
<Typography variant="pi" textColor="neutral800">
|
|
67
|
-
{transaction.raw_request?.telephonenumber || "N/A"}
|
|
67
|
+
{transaction.raw_request?.telephonenumber || transaction.body?.raw_request?.telephonenumber || "N/A"}
|
|
68
68
|
</Typography>
|
|
69
69
|
</Flex>
|
|
70
70
|
<Flex gap={3}>
|
|
@@ -72,8 +72,8 @@ const TransactionDetails = ({ transaction }) => {
|
|
|
72
72
|
Address:
|
|
73
73
|
</Typography>
|
|
74
74
|
<Typography variant="pi" textColor="neutral800">
|
|
75
|
-
{transaction.raw_request?.street}, {transaction.raw_request?.zip}{" "}
|
|
76
|
-
{transaction.raw_request?.city}
|
|
75
|
+
{transaction.raw_request?.street || transaction.body?.raw_request?.street}, {transaction.raw_request?.zip || transaction.body?.raw_request?.zip}{" "}
|
|
76
|
+
{transaction.raw_request?.city || transaction.body?.raw_request?.city}
|
|
77
77
|
</Typography>
|
|
78
78
|
</Flex>
|
|
79
79
|
</Flex>
|
|
@@ -94,7 +94,7 @@ const TransactionDetails = ({ transaction }) => {
|
|
|
94
94
|
{transaction.txid || "N/A"}
|
|
95
95
|
</Typography>
|
|
96
96
|
</Flex>
|
|
97
|
-
{transaction.raw_request?.clearingtype === "cc" && (
|
|
97
|
+
{transaction.raw_request?.clearingtype === "cc" || transaction.body?.raw_request?.clearingtype === "cc" && (
|
|
98
98
|
<>
|
|
99
99
|
<Flex gap={3}>
|
|
100
100
|
<Typography
|
|
@@ -105,7 +105,7 @@ const TransactionDetails = ({ transaction }) => {
|
|
|
105
105
|
Card Type:
|
|
106
106
|
</Typography>
|
|
107
107
|
<Typography variant="pi" textColor="neutral800">
|
|
108
|
-
{getCardTypeName(transaction.raw_request?.cardtype)}
|
|
108
|
+
{getCardTypeName(transaction.raw_request?.cardtype || transaction.body?.raw_request?.cardtype)}
|
|
109
109
|
</Typography>
|
|
110
110
|
</Flex>
|
|
111
111
|
<Flex gap={3}>
|
|
@@ -118,7 +118,7 @@ const TransactionDetails = ({ transaction }) => {
|
|
|
118
118
|
</Typography>
|
|
119
119
|
<Typography variant="pi" textColor="neutral800">
|
|
120
120
|
**** **** ****{" "}
|
|
121
|
-
{transaction.raw_request?.cardpan?.slice(-4) || "****"}
|
|
121
|
+
{transaction.raw_request?.cardpan?.slice(-4) || transaction.body?.raw_request?.cardpan?.slice(-4) || "****"}
|
|
122
122
|
</Typography>
|
|
123
123
|
</Flex>
|
|
124
124
|
</>
|
|
@@ -128,7 +128,7 @@ const TransactionDetails = ({ transaction }) => {
|
|
|
128
128
|
Mode:
|
|
129
129
|
</Typography>
|
|
130
130
|
<Typography variant="pi" textColor="neutral800">
|
|
131
|
-
{transaction.raw_request?.mode || "N/A"}
|
|
131
|
+
{transaction.raw_request?.mode || transaction.body?.raw_request?.mode || "N/A"}
|
|
132
132
|
</Typography>
|
|
133
133
|
</Flex>
|
|
134
134
|
</Flex>
|
|
@@ -137,7 +137,6 @@ const useSettings = () => {
|
|
|
137
137
|
setTestResult(null);
|
|
138
138
|
try {
|
|
139
139
|
const response = await testConnection();
|
|
140
|
-
console.log("response in handleTestConnection:", response.data);
|
|
141
140
|
if (response.data && response.data.success) {
|
|
142
141
|
setTestResult(response.data);
|
|
143
142
|
toggleNotification({
|
|
@@ -12,10 +12,11 @@ const usePayoneRequests = () => {
|
|
|
12
12
|
const getTransactionHistory = (params = {}) => {
|
|
13
13
|
const queryParams = new URLSearchParams();
|
|
14
14
|
|
|
15
|
-
if (params.filters) {
|
|
15
|
+
if (params.filters && typeof params.filters === "object") {
|
|
16
16
|
Object.keys(params.filters).forEach((key) => {
|
|
17
17
|
const value = params.filters[key];
|
|
18
|
-
|
|
18
|
+
const v = value == null ? "" : String(value).trim();
|
|
19
|
+
if (v !== "" && v.toLowerCase() !== "all") {
|
|
19
20
|
queryParams.append(`filters[${key}]`, String(value));
|
|
20
21
|
}
|
|
21
22
|
});
|
|
@@ -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.
|
|
3
|
+
"version": "5.6.12",
|
|
4
4
|
"description": "Strapi plugin for Payone payment gateway integration",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"maintainers": [
|
|
@@ -42,4 +42,4 @@
|
|
|
42
42
|
"kind": "plugin",
|
|
43
43
|
"displayName": "Strapi Payone Provider"
|
|
44
44
|
}
|
|
45
|
-
}
|
|
45
|
+
}
|
package/server/bootstrap.js
CHANGED
|
@@ -22,12 +22,11 @@ module.exports = async ({ strapi }) => {
|
|
|
22
22
|
displayName: "",
|
|
23
23
|
domainName: "",
|
|
24
24
|
merchantIdentifier: "",
|
|
25
|
-
enableCreditCard:
|
|
26
|
-
enablePayPal:
|
|
27
|
-
enableGooglePay:
|
|
28
|
-
enableApplePay:
|
|
29
|
-
|
|
30
|
-
enableSepaDirectDebit: true
|
|
25
|
+
enableCreditCard: false,
|
|
26
|
+
enablePayPal: false,
|
|
27
|
+
enableGooglePay: false,
|
|
28
|
+
enableApplePay: false,
|
|
29
|
+
enableSepaDirectDebit: false
|
|
31
30
|
}
|
|
32
31
|
});
|
|
33
32
|
}
|
|
@@ -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
|
|
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
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
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
|
|
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
|
-
|
|
1
|
+
module.exports = async (ctx) => {
|
|
2
|
+
const { request } = ctx;
|
|
2
3
|
|
|
3
|
-
|
|
4
|
-
const
|
|
5
|
-
|
|
6
|
-
|
|
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
|
-
|
|
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
|
-
|
|
20
|
-
return true;
|
|
21
|
-
}
|
|
22
|
-
return false;
|
|
23
|
-
};
|
|
13
|
+
ctx.state.payoneAllowed = isValid;
|
|
24
14
|
|
|
25
|
-
if (!
|
|
26
|
-
console.log(
|
|
27
|
-
return false;
|
|
15
|
+
if (!isValid) {
|
|
16
|
+
console.log("[Payone] Policy failed", { userAgent, clientIp });
|
|
28
17
|
}
|
|
29
18
|
|
|
30
19
|
return true;
|