strapi-plugin-payone-provider 1.1.3 → 1.3.0
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 +1156 -380
- package/admin/src/index.js +4 -1
- package/admin/src/pages/App/components/AppHeader.js +37 -0
- package/admin/src/pages/App/components/AppTabs.js +134 -0
- package/admin/src/pages/App/components/ConfigurationPanel.js +34 -35
- package/admin/src/pages/App/components/GooglePaybutton.js +300 -0
- package/admin/src/pages/App/components/HistoryPanel.js +25 -38
- package/admin/src/pages/App/components/PaymentActionsPanel.js +119 -280
- package/admin/src/pages/App/components/StatusBadge.js +3 -1
- package/admin/src/pages/App/components/TransactionHistoryItem.js +4 -1
- package/admin/src/pages/App/components/paymentActions/AuthorizationForm.js +122 -0
- package/admin/src/pages/App/components/paymentActions/CaptureForm.js +64 -0
- package/admin/src/pages/App/components/paymentActions/CardDetailsInput.js +189 -0
- package/admin/src/pages/App/components/paymentActions/PaymentMethodSelector.js +52 -0
- package/admin/src/pages/App/components/paymentActions/PaymentResult.js +148 -0
- package/admin/src/pages/App/components/paymentActions/PreauthorizationForm.js +122 -0
- package/admin/src/pages/App/components/paymentActions/RefundForm.js +89 -0
- package/admin/src/pages/App/index.js +41 -465
- package/admin/src/pages/App/styles.css +294 -0
- package/admin/src/pages/constants/paymentConstants.js +37 -0
- package/admin/src/pages/hooks/usePaymentActions.js +456 -0
- package/admin/src/pages/hooks/useSettings.js +111 -0
- package/admin/src/pages/hooks/useTransactionHistory.js +87 -0
- package/admin/src/pages/utils/api.js +10 -0
- package/admin/src/pages/utils/injectGooglePayScript.js +31 -0
- package/admin/src/pages/utils/paymentUtils.js +119 -15
- package/package.json +1 -1
- package/server/controllers/payone.js +71 -64
- package/server/routes/index.js +17 -0
- package/server/services/paymentService.js +271 -0
- package/server/services/payone.js +25 -648
- package/server/services/settingsService.js +59 -0
- package/server/services/testConnectionService.js +190 -0
- package/server/services/transactionService.js +114 -0
- package/server/utils/normalize.js +51 -0
- package/server/utils/paymentMethodParams.js +126 -0
- package/server/utils/requestBuilder.js +121 -0
- package/server/utils/responseParser.js +134 -0
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const PLUGIN_NAME = "strapi-plugin-payone-provider";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Get plugin store instance
|
|
7
|
+
* @param {Object} strapi - Strapi instance
|
|
8
|
+
* @returns {Object} Plugin store
|
|
9
|
+
*/
|
|
10
|
+
const getPluginStore = (strapi) => {
|
|
11
|
+
return strapi.store({
|
|
12
|
+
environment: "",
|
|
13
|
+
type: "plugin",
|
|
14
|
+
name: PLUGIN_NAME
|
|
15
|
+
});
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Get Payone settings
|
|
20
|
+
* @param {Object} strapi - Strapi instance
|
|
21
|
+
* @returns {Promise<Object>} Settings
|
|
22
|
+
*/
|
|
23
|
+
const getSettings = async (strapi) => {
|
|
24
|
+
const pluginStore = getPluginStore(strapi);
|
|
25
|
+
return await pluginStore.get({ key: "settings" });
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Update Payone settings
|
|
30
|
+
* @param {Object} strapi - Strapi instance
|
|
31
|
+
* @param {Object} settings - Settings to update
|
|
32
|
+
* @returns {Promise<Object>} Updated settings
|
|
33
|
+
*/
|
|
34
|
+
const updateSettings = async (strapi, settings) => {
|
|
35
|
+
const pluginStore = getPluginStore(strapi);
|
|
36
|
+
await pluginStore.set({
|
|
37
|
+
key: "settings",
|
|
38
|
+
value: settings
|
|
39
|
+
});
|
|
40
|
+
return settings;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Validate settings
|
|
45
|
+
* @param {Object} settings - Settings to validate
|
|
46
|
+
* @returns {boolean} True if valid
|
|
47
|
+
*/
|
|
48
|
+
const validateSettings = (settings) => {
|
|
49
|
+
return !!(settings && settings.aid && settings.portalid && settings.key);
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
module.exports = {
|
|
53
|
+
getSettings,
|
|
54
|
+
updateSettings,
|
|
55
|
+
validateSettings,
|
|
56
|
+
getPluginStore,
|
|
57
|
+
PLUGIN_NAME
|
|
58
|
+
};
|
|
59
|
+
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const axios = require("axios");
|
|
4
|
+
const { buildClientRequestParams, toFormData } = require("../utils/requestBuilder");
|
|
5
|
+
const { parseResponse } = require("../utils/responseParser");
|
|
6
|
+
const { getSettings, validateSettings } = require("./settingsService");
|
|
7
|
+
|
|
8
|
+
const POST_GATEWAY_URL = "https://api.pay1.de/post-gateway/";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Test Payone connection
|
|
12
|
+
* @param {Object} strapi - Strapi instance
|
|
13
|
+
* @returns {Promise<Object>} Test result
|
|
14
|
+
*/
|
|
15
|
+
const testConnection = async (strapi) => {
|
|
16
|
+
try {
|
|
17
|
+
const settings = await getSettings(strapi);
|
|
18
|
+
|
|
19
|
+
if (!validateSettings(settings)) {
|
|
20
|
+
return {
|
|
21
|
+
success: false,
|
|
22
|
+
message: "Payone settings not configured. Please fill in all required fields."
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const timestamp = Date.now();
|
|
27
|
+
const testParams = {
|
|
28
|
+
request: "authorization",
|
|
29
|
+
amount: 100,
|
|
30
|
+
currency: "EUR",
|
|
31
|
+
reference: `TEST-${timestamp}`,
|
|
32
|
+
clearingtype: "cc",
|
|
33
|
+
cardtype: "V",
|
|
34
|
+
cardpan: "4111111111111111",
|
|
35
|
+
cardexpiredate: "2512",
|
|
36
|
+
cardcvc2: "123",
|
|
37
|
+
firstname: "Test",
|
|
38
|
+
lastname: "User",
|
|
39
|
+
street: "Test Street 1",
|
|
40
|
+
zip: "12345",
|
|
41
|
+
city: "Test City",
|
|
42
|
+
country: "DE",
|
|
43
|
+
email: "test@example.com",
|
|
44
|
+
salutation: "Herr",
|
|
45
|
+
gender: "m",
|
|
46
|
+
telephonenumber: "01752345678",
|
|
47
|
+
ip: "127.0.0.1",
|
|
48
|
+
customer_is_present: "yes",
|
|
49
|
+
language: "de"
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const requestParams = buildClientRequestParams(settings, testParams, strapi.log);
|
|
53
|
+
const formData = toFormData(requestParams);
|
|
54
|
+
|
|
55
|
+
const response = await axios.post(POST_GATEWAY_URL, formData, {
|
|
56
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
57
|
+
timeout: 30000
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
const result = parseResponse(response.data, strapi.log);
|
|
61
|
+
const status = result.status || result.Status || result.STATUS;
|
|
62
|
+
const errorMessage =
|
|
63
|
+
result.errormessage ||
|
|
64
|
+
result.Errormessage ||
|
|
65
|
+
result.ERRORMESSAGE ||
|
|
66
|
+
result.error ||
|
|
67
|
+
result.Error?.ErrorMessage ||
|
|
68
|
+
"";
|
|
69
|
+
const errorCode =
|
|
70
|
+
result.errorcode ||
|
|
71
|
+
result.Errorcode ||
|
|
72
|
+
result.ERRORCODE ||
|
|
73
|
+
result.Error?.ErrorCode ||
|
|
74
|
+
"";
|
|
75
|
+
const customErrorMessage =
|
|
76
|
+
result.customerrormessage ||
|
|
77
|
+
result.Customerrormessage ||
|
|
78
|
+
result.CUSTOMERRORMESSAGE ||
|
|
79
|
+
result.Error?.CustomerMessage ||
|
|
80
|
+
"";
|
|
81
|
+
|
|
82
|
+
strapi.log.info("Payone test connection response:", { status, errorCode });
|
|
83
|
+
|
|
84
|
+
// Handle error status
|
|
85
|
+
if (status === "ERROR" || status === "error") {
|
|
86
|
+
// Authentication errors
|
|
87
|
+
if (["2006", "920", "921", "922", "401", "403"].includes(errorCode)) {
|
|
88
|
+
return {
|
|
89
|
+
success: false,
|
|
90
|
+
message: `Authentication failed: ${customErrorMessage || errorMessage || "Invalid credentials"}`,
|
|
91
|
+
errorcode: errorCode
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Check for invalid credentials in message
|
|
96
|
+
const errorMessageStr = typeof errorMessage === "string" ? errorMessage : JSON.stringify(errorMessage);
|
|
97
|
+
const errorMessageLower = (errorMessageStr || "").toLowerCase();
|
|
98
|
+
const authErrorKeywords = [
|
|
99
|
+
"key incorrect",
|
|
100
|
+
"invalid key",
|
|
101
|
+
"portal key",
|
|
102
|
+
"unauthorized",
|
|
103
|
+
"not authorized",
|
|
104
|
+
"unknown aid",
|
|
105
|
+
"unknown account",
|
|
106
|
+
"unknown portal",
|
|
107
|
+
"unknown merchant",
|
|
108
|
+
"invalid aid",
|
|
109
|
+
"invalid mid",
|
|
110
|
+
"invalid portalid"
|
|
111
|
+
];
|
|
112
|
+
|
|
113
|
+
if (authErrorKeywords.some((keyword) => errorMessageLower.includes(keyword))) {
|
|
114
|
+
return {
|
|
115
|
+
success: false,
|
|
116
|
+
message: `Authentication failed: ${errorMessageStr}`,
|
|
117
|
+
errorcode: errorCode || "AUTH"
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Reference already exists (911) means credentials are working
|
|
122
|
+
if (errorCode === "911") {
|
|
123
|
+
return {
|
|
124
|
+
success: true,
|
|
125
|
+
message: "Connection successful! Your Payone credentials are valid.",
|
|
126
|
+
details: {
|
|
127
|
+
mode: settings.mode,
|
|
128
|
+
aid: settings.aid,
|
|
129
|
+
portalid: settings.portalid,
|
|
130
|
+
mid: settings.mid
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Other errors
|
|
136
|
+
return {
|
|
137
|
+
success: false,
|
|
138
|
+
message: `Connection failed: ${customErrorMessage || errorMessageStr || "Unknown error"}`,
|
|
139
|
+
errorcode: errorCode,
|
|
140
|
+
details: {
|
|
141
|
+
status,
|
|
142
|
+
errorCode,
|
|
143
|
+
rawResponse: JSON.stringify(result).substring(0, 200)
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// APPROVED status means connection is OK
|
|
149
|
+
if (status === "APPROVED" || status === "approved") {
|
|
150
|
+
return {
|
|
151
|
+
success: true,
|
|
152
|
+
message: "Connection successful! Your Payone credentials are valid.",
|
|
153
|
+
details: {
|
|
154
|
+
mode: settings.mode,
|
|
155
|
+
aid: settings.aid,
|
|
156
|
+
portalid: settings.portalid,
|
|
157
|
+
mid: settings.mid
|
|
158
|
+
}
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Unexpected response format
|
|
163
|
+
return {
|
|
164
|
+
success: false,
|
|
165
|
+
message: "Unexpected response format from Payone API",
|
|
166
|
+
response: result,
|
|
167
|
+
details: {
|
|
168
|
+
status,
|
|
169
|
+
keys: Object.keys(result),
|
|
170
|
+
rawResponse: JSON.stringify(result).substring(0, 200)
|
|
171
|
+
}
|
|
172
|
+
};
|
|
173
|
+
} catch (error) {
|
|
174
|
+
strapi.log.error("Payone test connection error:", error);
|
|
175
|
+
return {
|
|
176
|
+
success: false,
|
|
177
|
+
message: `Connection error: ${error.message || "Unknown error"}`,
|
|
178
|
+
error: error.toString(),
|
|
179
|
+
details: {
|
|
180
|
+
errorType: error.constructor.name,
|
|
181
|
+
stack: error.stack ? error.stack.substring(0, 200) : "No stack trace"
|
|
182
|
+
}
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
module.exports = {
|
|
188
|
+
testConnection
|
|
189
|
+
};
|
|
190
|
+
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const { getPluginStore, PLUGIN_NAME } = require("./settingsService");
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Log transaction to history
|
|
7
|
+
* @param {Object} strapi - Strapi instance
|
|
8
|
+
* @param {Object} transactionData - Transaction data
|
|
9
|
+
* @returns {Promise<void>}
|
|
10
|
+
*/
|
|
11
|
+
const logTransaction = async (strapi, transactionData) => {
|
|
12
|
+
const pluginStore = getPluginStore(strapi);
|
|
13
|
+
let transactionHistory =
|
|
14
|
+
(await pluginStore.get({ key: "transactionHistory" })) || [];
|
|
15
|
+
|
|
16
|
+
const logEntry = {
|
|
17
|
+
id: Date.now().toString(),
|
|
18
|
+
timestamp: new Date().toISOString(),
|
|
19
|
+
txid: transactionData.txid || null,
|
|
20
|
+
reference: transactionData.reference || null,
|
|
21
|
+
request_type:
|
|
22
|
+
transactionData.request_type || transactionData.request || "unknown",
|
|
23
|
+
amount: transactionData.amount || null,
|
|
24
|
+
currency: transactionData.currency || "EUR",
|
|
25
|
+
status: transactionData.status || transactionData.Status || "unknown",
|
|
26
|
+
error_code:
|
|
27
|
+
transactionData.error_code || transactionData.Error?.ErrorCode || null,
|
|
28
|
+
error_message:
|
|
29
|
+
transactionData.error_message ||
|
|
30
|
+
transactionData.Error?.ErrorMessage ||
|
|
31
|
+
null,
|
|
32
|
+
customer_message:
|
|
33
|
+
transactionData.customer_message ||
|
|
34
|
+
transactionData.Error?.CustomerMessage ||
|
|
35
|
+
null,
|
|
36
|
+
raw_request: transactionData.raw_request || null,
|
|
37
|
+
raw_response: transactionData.raw_response || transactionData,
|
|
38
|
+
created_at: new Date().toISOString(),
|
|
39
|
+
updated_at: new Date().toISOString()
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
transactionHistory.unshift(logEntry);
|
|
43
|
+
|
|
44
|
+
// Keep only last 1000 transactions
|
|
45
|
+
if (transactionHistory.length > 1000) {
|
|
46
|
+
transactionHistory = transactionHistory.slice(0, 1000);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
await pluginStore.set({
|
|
50
|
+
key: "transactionHistory",
|
|
51
|
+
value: transactionHistory
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
strapi.log.info("Transaction logged:", logEntry);
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Get transaction history with filters
|
|
59
|
+
* @param {Object} strapi - Strapi instance
|
|
60
|
+
* @param {Object} filters - Filter options
|
|
61
|
+
* @returns {Promise<Array>} Filtered transaction history
|
|
62
|
+
*/
|
|
63
|
+
const getTransactionHistory = async (strapi, filters = {}) => {
|
|
64
|
+
const pluginStore = getPluginStore(strapi);
|
|
65
|
+
let transactionHistory =
|
|
66
|
+
(await pluginStore.get({ key: "transactionHistory" })) || [];
|
|
67
|
+
|
|
68
|
+
// Apply filters
|
|
69
|
+
if (filters.status) {
|
|
70
|
+
transactionHistory = transactionHistory.filter(
|
|
71
|
+
(transaction) => transaction.status === filters.status
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (filters.request_type) {
|
|
76
|
+
transactionHistory = transactionHistory.filter(
|
|
77
|
+
(transaction) => transaction.request_type === filters.request_type
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (filters.txid) {
|
|
82
|
+
transactionHistory = transactionHistory.filter(
|
|
83
|
+
(transaction) => transaction.txid === filters.txid
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (filters.reference) {
|
|
88
|
+
transactionHistory = transactionHistory.filter(
|
|
89
|
+
(transaction) => transaction.reference === filters.reference
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (filters.date_from) {
|
|
94
|
+
transactionHistory = transactionHistory.filter(
|
|
95
|
+
(transaction) =>
|
|
96
|
+
new Date(transaction.timestamp) >= new Date(filters.date_from)
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (filters.date_to) {
|
|
101
|
+
transactionHistory = transactionHistory.filter(
|
|
102
|
+
(transaction) =>
|
|
103
|
+
new Date(transaction.timestamp) <= new Date(filters.date_to)
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return transactionHistory;
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
module.exports = {
|
|
111
|
+
logTransaction,
|
|
112
|
+
getTransactionHistory
|
|
113
|
+
};
|
|
114
|
+
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"use strict";
|
|
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
|
+
const normalizeReference = (input, fallbackPrefix = "REF") => {
|
|
10
|
+
try {
|
|
11
|
+
const raw = input == null ? "" : String(input);
|
|
12
|
+
let normalized = raw.replace(/[^A-Za-z0-9]/g, "");
|
|
13
|
+
|
|
14
|
+
if (!normalized) {
|
|
15
|
+
normalized = `${fallbackPrefix}${Date.now()}`;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
return normalized.length > 20 ? normalized.slice(0, 20) : normalized;
|
|
19
|
+
} catch (_) {
|
|
20
|
+
const fallback = `${fallbackPrefix}${Date.now()}`;
|
|
21
|
+
return fallback.slice(0, 20);
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
|
|
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
|
+
const normalizeCustomerId = (customerid, logger = null) => {
|
|
32
|
+
if (!customerid) {
|
|
33
|
+
const timestamp = Date.now().toString().slice(-10);
|
|
34
|
+
const random = Math.random().toString(36).substring(2, 6).toUpperCase();
|
|
35
|
+
return `${timestamp}${random}`.slice(0, 17);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const normalized = String(customerid).slice(0, 17);
|
|
39
|
+
if (customerid.length > 17 && logger) {
|
|
40
|
+
logger.warn(
|
|
41
|
+
`customerid exceeds 17 characters: ${customerid.length}, truncated to: ${normalized}`
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
return normalized;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
module.exports = {
|
|
48
|
+
normalizeReference,
|
|
49
|
+
normalizeCustomerId
|
|
50
|
+
};
|
|
51
|
+
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Add payment method specific parameters
|
|
5
|
+
* @param {Object} params - Request parameters
|
|
6
|
+
* @param {Object} logger - Logger instance
|
|
7
|
+
* @returns {Object} Updated parameters with payment method defaults
|
|
8
|
+
*/
|
|
9
|
+
const addPaymentMethodParams = (params, logger) => {
|
|
10
|
+
const updated = { ...params };
|
|
11
|
+
const clearingtype = updated.clearingtype || "cc";
|
|
12
|
+
|
|
13
|
+
// Payment method specific defaults
|
|
14
|
+
const methodDefaults = {
|
|
15
|
+
cc: {
|
|
16
|
+
cardpan: "4111111111111111",
|
|
17
|
+
cardexpiredate: "2512",
|
|
18
|
+
cardcvc2: "123",
|
|
19
|
+
cardtype: "V"
|
|
20
|
+
},
|
|
21
|
+
wlt: {
|
|
22
|
+
wallettype: "PPE"
|
|
23
|
+
},
|
|
24
|
+
gpp: {
|
|
25
|
+
clearingtype: "wlt",
|
|
26
|
+
wallettype: "GGP"
|
|
27
|
+
},
|
|
28
|
+
apl: {
|
|
29
|
+
clearingtype: "wlt",
|
|
30
|
+
wallettype: "APL"
|
|
31
|
+
},
|
|
32
|
+
elv: {
|
|
33
|
+
bankcountry: "DE",
|
|
34
|
+
iban: "DE89370400440532013000",
|
|
35
|
+
bic: "COBADEFFXXX",
|
|
36
|
+
bankaccountholder: `${updated.firstname || "Test"} ${updated.lastname || "User"}`
|
|
37
|
+
},
|
|
38
|
+
sb: {
|
|
39
|
+
bankcountry: "DE",
|
|
40
|
+
onlinebanktransfertype: "PNT"
|
|
41
|
+
},
|
|
42
|
+
gp: {
|
|
43
|
+
bankcountry: "DE",
|
|
44
|
+
onlinebanktransfertype: "GPY"
|
|
45
|
+
},
|
|
46
|
+
idl: {
|
|
47
|
+
bankcountry: "NL",
|
|
48
|
+
onlinebanktransfertype: "IDL"
|
|
49
|
+
},
|
|
50
|
+
bct: {
|
|
51
|
+
bankcountry: "BE",
|
|
52
|
+
onlinebanktransfertype: "BCT"
|
|
53
|
+
},
|
|
54
|
+
rec: {
|
|
55
|
+
recurrence: "recurring"
|
|
56
|
+
},
|
|
57
|
+
fnc: {
|
|
58
|
+
financingtype: "fnc"
|
|
59
|
+
},
|
|
60
|
+
iv: {
|
|
61
|
+
invoicetype: "invoice"
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
// Handle special cases (gpp, apl) FIRST - set wallettype BEFORE changing clearingtype
|
|
66
|
+
if (clearingtype === "gpp" || clearingtype === "apl") {
|
|
67
|
+
if (clearingtype === "gpp") {
|
|
68
|
+
updated.wallettype = "GGP";
|
|
69
|
+
} else if (clearingtype === "apl") {
|
|
70
|
+
updated.wallettype = "APL";
|
|
71
|
+
}
|
|
72
|
+
updated.clearingtype = "wlt";
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const defaults = methodDefaults[clearingtype] || methodDefaults.cc;
|
|
76
|
+
|
|
77
|
+
// Apply defaults (but don't override wallettype if already set)
|
|
78
|
+
Object.entries(defaults).forEach(([key, value]) => {
|
|
79
|
+
if (key === "wallettype" && updated.wallettype) {
|
|
80
|
+
return; // Don't override wallettype if already set
|
|
81
|
+
}
|
|
82
|
+
if (!updated[key]) {
|
|
83
|
+
updated[key] = value;
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
// Ensure wallettype is set for wallet payments
|
|
88
|
+
if (updated.clearingtype === "wlt" && !updated.wallettype) {
|
|
89
|
+
if (clearingtype === "gpp" || updated.paymentMethod === "gpp" || updated["add_paydata[paymentmethod_token_data]"]) {
|
|
90
|
+
updated.wallettype = "GGP";
|
|
91
|
+
} else if (clearingtype === "apl" || updated.paymentMethod === "apl") {
|
|
92
|
+
updated.wallettype = "APL";
|
|
93
|
+
} else {
|
|
94
|
+
updated.wallettype = "PPE";
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Remove cardtype if clearingtype is wallet payment
|
|
99
|
+
if (updated.clearingtype === "wlt" && updated.cardtype) {
|
|
100
|
+
delete updated.cardtype;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
// Common defaults
|
|
105
|
+
const commonDefaults = {
|
|
106
|
+
salutation: "Herr",
|
|
107
|
+
gender: "m",
|
|
108
|
+
telephonenumber: "01752345678",
|
|
109
|
+
ip: "127.0.0.1",
|
|
110
|
+
language: "de",
|
|
111
|
+
customer_is_present: "yes"
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
Object.entries(commonDefaults).forEach(([key, value]) => {
|
|
115
|
+
if (!updated[key]) {
|
|
116
|
+
updated[key] = value;
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
return updated;
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
module.exports = {
|
|
124
|
+
addPaymentMethodParams
|
|
125
|
+
};
|
|
126
|
+
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const crypto = require("crypto");
|
|
4
|
+
const { normalizeCustomerId } = require("./normalize");
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Build client request parameters for Payone API
|
|
8
|
+
* @param {Object} settings - Payone settings
|
|
9
|
+
* @param {Object} params - Request parameters
|
|
10
|
+
* @param {Object|null} logger - Logger instance
|
|
11
|
+
* @returns {Object} Built request parameters
|
|
12
|
+
*/
|
|
13
|
+
const buildClientRequestParams = (settings, params, logger = null) => {
|
|
14
|
+
const requestParams = {
|
|
15
|
+
request: params.request,
|
|
16
|
+
aid: settings.aid,
|
|
17
|
+
mid: settings.mid,
|
|
18
|
+
portalid: settings.portalid,
|
|
19
|
+
mode: settings.mode || "test",
|
|
20
|
+
encoding: "UTF-8",
|
|
21
|
+
...params
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
// Generate MD5 hash key
|
|
25
|
+
requestParams.key = crypto
|
|
26
|
+
.createHash("md5")
|
|
27
|
+
.update(settings.portalKey || settings.key)
|
|
28
|
+
.digest("hex");
|
|
29
|
+
|
|
30
|
+
// Normalize customer ID
|
|
31
|
+
requestParams.customerid = normalizeCustomerId(
|
|
32
|
+
requestParams.customerid,
|
|
33
|
+
logger
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
// Add 3D Secure parameters if enabled and for credit card payments
|
|
37
|
+
const isCreditCard = requestParams.clearingtype === "cc";
|
|
38
|
+
// Enable 3DS if setting is true or not explicitly false (default to enabled if not set)
|
|
39
|
+
const enable3DSecure = settings.enable3DSecure !== false;
|
|
40
|
+
|
|
41
|
+
if (isCreditCard && enable3DSecure && (params.request === "preauthorization" || params.request === "authorization")) {
|
|
42
|
+
requestParams["3dsecure"] = "yes";
|
|
43
|
+
requestParams.ecommercemode = params.ecommercemode || "internet";
|
|
44
|
+
|
|
45
|
+
// Ensure redirect URLs are always provided for 3DS
|
|
46
|
+
// These are required for 3DS authentication flow
|
|
47
|
+
if (!requestParams.successurl) {
|
|
48
|
+
requestParams.successurl = params.successurl || "https://www.example.com/success";
|
|
49
|
+
}
|
|
50
|
+
if (!requestParams.errorurl) {
|
|
51
|
+
requestParams.errorurl = params.errorurl || "https://www.example.com/error";
|
|
52
|
+
}
|
|
53
|
+
if (!requestParams.backurl) {
|
|
54
|
+
requestParams.backurl = params.backurl || "https://www.example.com/back";
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Log redirect URLs for debugging
|
|
58
|
+
if (logger) {
|
|
59
|
+
logger.info("3DS Redirect URLs:", {
|
|
60
|
+
successurl: requestParams.successurl,
|
|
61
|
+
errorurl: requestParams.errorurl,
|
|
62
|
+
backurl: requestParams.backurl
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
} else if (isCreditCard && !enable3DSecure) {
|
|
66
|
+
requestParams["3dsecure"] = "no";
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Set default values
|
|
70
|
+
const defaults = {
|
|
71
|
+
salutation: "Herr",
|
|
72
|
+
gender: "m",
|
|
73
|
+
telephonenumber: "01752345678",
|
|
74
|
+
ip: "127.0.0.1",
|
|
75
|
+
language: "de",
|
|
76
|
+
customer_is_present: "yes"
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
Object.entries(defaults).forEach(([key, value]) => {
|
|
80
|
+
if (!requestParams[key]) {
|
|
81
|
+
requestParams[key] = value;
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
// Remove cardtype if clearingtype is wallet payment
|
|
86
|
+
if (requestParams.clearingtype === "wlt" && requestParams.cardtype) {
|
|
87
|
+
delete requestParams.cardtype;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Ensure wallettype is set for wallet payments
|
|
91
|
+
if (requestParams.clearingtype === "wlt" && !requestParams.wallettype) {
|
|
92
|
+
if (requestParams["add_paydata[paymentmethod_token_data]"]) {
|
|
93
|
+
requestParams.wallettype = "GGP";
|
|
94
|
+
} else {
|
|
95
|
+
requestParams.wallettype = "PPE";
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return requestParams;
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Convert request parameters to form data
|
|
104
|
+
* @param {Object} requestParams - Request parameters
|
|
105
|
+
* @returns {URLSearchParams} Form data
|
|
106
|
+
*/
|
|
107
|
+
const toFormData = (requestParams) => {
|
|
108
|
+
const formData = new URLSearchParams();
|
|
109
|
+
for (const [key, value] of Object.entries(requestParams)) {
|
|
110
|
+
if (value !== undefined && value !== null) {
|
|
111
|
+
formData.append(key, value);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return formData;
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
module.exports = {
|
|
118
|
+
buildClientRequestParams,
|
|
119
|
+
toFormData
|
|
120
|
+
};
|
|
121
|
+
|