strapi-plugin-payone-provider 1.6.3 → 1.6.4

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.
@@ -19,93 +19,264 @@ const parseResponse = (responseData) => {
19
19
  };
20
20
 
21
21
  const initializeApplePaySession = async (strapi, params) => {
22
+ let settings = null;
22
23
  try {
23
- const settings = await getSettings(strapi);
24
- const { displayName, domainName } = params;
24
+ settings = await getSettings(strapi);
25
+
26
+ const validationErrors = [];
27
+
28
+ if (!settings.aid || settings.aid.trim() === "") {
29
+ validationErrors.push("aid (Subaccount ID) is missing or empty");
30
+ }
31
+ if (!settings.portalid || settings.portalid.trim() === "") {
32
+ validationErrors.push("portalid (Portal ID) is missing or empty");
33
+ }
34
+ if (!settings.mid || settings.mid.trim() === "") {
35
+ validationErrors.push("mid (Merchant ID) is missing or empty");
36
+ }
37
+ if (!settings.key || settings.key.trim() === "") {
38
+ validationErrors.push("key (Portal Key) is missing or empty");
39
+ }
40
+
41
+ const mode = (settings.mode || "test").toLowerCase();
42
+ if (mode !== "live") {
43
+ validationErrors.push(`Mode is set to "${mode}" but Apple Pay only works in "live" mode according to Payone documentation`);
44
+ }
45
+
46
+ const applePayConfig = settings?.applePayConfig || {};
47
+ const currency = params.currency || applePayConfig.currencyCode || "EUR";
48
+ const countryCode = params.countryCode || applePayConfig.countryCode || "DE";
25
49
 
26
- const merchantName = displayName || "Store";
27
- const domain = domainName;
50
+ const merchantName = params.displayName || settings?.merchantName || "Store";
51
+ const domain = params.domain || params.domainName || "localhost";
28
52
 
29
53
  const baseParams = {
30
54
  request: "genericpayment",
31
55
  clearingtype: "wlt",
32
56
  wallettype: "APL",
33
- currency: params.currency,
57
+ currency: currency,
34
58
  "add_paydata[action]": "init_applepay_session",
35
59
  "add_paydata[display_name]": merchantName,
36
60
  "add_paydata[domain_name]": domain
37
61
  };
38
62
 
39
63
  const requestParams = buildClientRequestParams(settings, baseParams, strapi.log);
40
-
41
64
  const formData = toFormData(requestParams);
42
- const response = await axios.post(`${POST_GATEWAY_URL}Genericpayment`, formData, {
43
- headers: { "Content-Type": "application/x-www-form-urlencoded" },
44
- timeout: 30000
45
- });
65
+
66
+ let response;
67
+ try {
68
+ response = await axios.post(`${POST_GATEWAY_URL}Genericpayment`, formData, {
69
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
70
+ timeout: 30000,
71
+ validateStatus: function (status) {
72
+ return status >= 200 && status < 600;
73
+ }
74
+ });
75
+ } catch (axiosError) {
76
+ throw axiosError;
77
+ }
78
+
79
+ if (response.status === 403) {
80
+ const responseData = parseResponse(response.data);
81
+ const errorCode = responseData.errorcode || responseData.ErrorCode;
82
+ const errorMessage = responseData.errormessage || responseData.ErrorMessage || responseData.customermessage || responseData.CustomerMessage;
83
+
84
+ strapi.log.error("[Apple Pay] 403 Forbidden from Payone:", {
85
+ errorcode: errorCode,
86
+ errormessage: errorMessage
87
+ });
88
+
89
+ const detailedError = new Error("403 Forbidden: Authentication failed with Payone API. " +
90
+ (errorCode ? `Error Code: ${errorCode}. ` : "") +
91
+ (errorMessage ? `Error: ${errorMessage}. ` : "") +
92
+ "Please check: 1) Your Payone credentials (aid, portalid, mid, key) in plugin settings, " +
93
+ "2) Mode is set to 'live' (Apple Pay only works in live mode), " +
94
+ "3) Your domain is registered with Payone Merchant Services, " +
95
+ "4) Merchant ID (mid) matches your merchantIdentifier in PMI, " +
96
+ "5) Apple Pay is enabled for your portal in PMI.");
97
+ Object.assign(detailedError, { status: 403, response: response });
98
+ throw detailedError;
99
+ }
100
+
101
+ if (response.status >= 400 && response.status < 500) {
102
+ const responseData = parseResponse(response.data);
103
+ const errorCode = responseData.errorcode || responseData.ErrorCode;
104
+ const errorMessage = responseData.errormessage || responseData.ErrorMessage || responseData.customermessage || responseData.CustomerMessage;
105
+
106
+ strapi.log.error("[Apple Pay] Client error from Payone:", {
107
+ status: response.status,
108
+ errorcode: errorCode,
109
+ errormessage: errorMessage
110
+ });
111
+
112
+ const detailedError = new Error(`Payone API error (${response.status}): ${errorMessage || 'Unknown error'}`);
113
+ Object.assign(detailedError, { status: response.status, response: response });
114
+ throw detailedError;
115
+ }
116
+
117
+ if (response.status >= 500) {
118
+ strapi.log.error("[Apple Pay] Server error from Payone:", {
119
+ status: response.status,
120
+ statusText: response.statusText,
121
+ data: response.data
122
+ });
123
+
124
+ const detailedError = new Error(`Payone server error (${response.status}): ${response.statusText || 'Internal server error'}`);
125
+ Object.assign(detailedError, { status: response.status, response: response });
126
+ throw detailedError;
127
+ }
46
128
 
47
129
  const responseData = parseResponse(response.data);
48
130
 
49
131
  if (responseData.errorcode || responseData.ErrorCode) {
50
132
  strapi.log.error("[Apple Pay] Payone error:", {
51
133
  errorcode: responseData.errorcode || responseData.ErrorCode,
52
- errormessage: responseData.errormessage || responseData.ErrorMessage
134
+ errormessage: responseData.errormessage || responseData.ErrorMessage,
135
+ customermessage: responseData.customermessage || responseData.CustomerMessage
53
136
  });
54
137
  }
55
138
 
56
139
  return responseData;
57
140
  } catch (error) {
58
- strapi.log.error("[Apple Pay] Session initialization error:", {
59
- message: error.message,
60
- stack: error.stack,
61
- response: error.response?.data
62
- });
141
+ const errorStatus = error.response?.status || error.status;
142
+ const errorResponseData = error.response?.data;
143
+
144
+ // Provide more specific error messages
145
+ if (errorStatus === 403 || error.message?.includes('403')) {
146
+ let responseData = {};
147
+ let errorCode = null;
148
+ let errorMessage = null;
149
+
150
+ if (errorResponseData) {
151
+ try {
152
+ responseData = parseResponse(errorResponseData);
153
+ errorCode = responseData.errorcode || responseData.ErrorCode;
154
+ errorMessage = responseData.errormessage || responseData.ErrorMessage || responseData.customermessage || responseData.CustomerMessage;
155
+ } catch (parseErr) {
156
+ if (typeof errorResponseData === 'string') {
157
+ errorMessage = errorResponseData;
158
+ }
159
+ }
160
+ }
161
+
162
+ if (errorCode || errorMessage) {
163
+ strapi.log.error("[Apple Pay] 403 Forbidden from Payone:", {
164
+ errorcode: errorCode,
165
+ errormessage: errorMessage
166
+ });
167
+ }
168
+
169
+ let detailedMessage = "403 Forbidden: Authentication failed with Payone API. ";
170
+
171
+ if (errorCode) {
172
+ detailedMessage += `Error Code: ${errorCode}. `;
173
+ }
174
+
175
+ if (errorMessage) {
176
+ detailedMessage += `Error: ${errorMessage}. `;
177
+ }
178
+
179
+ detailedMessage += "Please check:\n" +
180
+ "1. Your Payone credentials (aid, portalid, mid, key) in plugin settings\n" +
181
+ "2. Mode is set to 'live' (Apple Pay only works in live mode according to Payone docs)\n" +
182
+ "3. Your domain is registered with Payone Merchant Services\n" +
183
+ "4. Merchant ID (mid) matches your merchantIdentifier in PMI\n" +
184
+ "5. Apple Pay is enabled for your portal in PMI (CONFIGURATION → PAYMENT PORTALS → [Your Portal] → Payment type configuration tab)";
185
+
186
+ throw new Error(detailedMessage);
187
+ } else if (errorStatus === 401 || error.message?.includes('401')) {
188
+ if (errorResponseData) {
189
+ const responseData = parseResponse(errorResponseData);
190
+ strapi.log.error("[Apple Pay] 401 Unauthorized from Payone:", {
191
+ errorcode: responseData.errorcode || responseData.ErrorCode,
192
+ errormessage: responseData.errormessage || responseData.ErrorMessage
193
+ });
194
+ }
195
+ throw new Error("401 Unauthorized: Invalid credentials. Please verify your Payone key in plugin settings.");
196
+ } else if (errorStatus && errorStatus >= 500) {
197
+ const responseData = errorResponseData ? parseResponse(errorResponseData) : {};
198
+ strapi.log.error("[Apple Pay] Payone server error:", {
199
+ status: error.response?.status,
200
+ errorcode: responseData.errorcode || responseData.ErrorCode,
201
+ errormessage: responseData.errormessage || responseData.ErrorMessage
202
+ });
203
+ throw new Error(`Payone server error (${error.response?.status}): ${error.response?.statusText || 'Internal server error'}`);
204
+ }
205
+
63
206
  throw error;
64
207
  }
65
208
  };
66
209
 
67
210
  const validateApplePayMerchant = async (strapi, params) => {
68
- const settings = await getSettings(strapi);
69
-
70
- if (!validateSettings(settings)) {
71
- strapi.log.error("Payone settings not configured");
72
- return null;
73
- }
211
+ try {
212
+ const settings = await getSettings(strapi);
74
213
 
75
- const sessionResponse = await initializeApplePaySession(strapi, params);
76
- const applePaySessionBase64 = sessionResponse["add_paydata[applepay_payment_session]"] ||
77
- sessionResponse.add_paydata?.applepay_payment_session;
214
+ if (!validateSettings(settings)) {
215
+ throw new Error("Payone settings are not properly configured. Please check your plugin settings (aid, portalid, mid, key).");
216
+ }
78
217
 
79
- if (sessionResponse.status === "OK" && applePaySessionBase64 && applePaySessionBase64.length > 0) {
80
- const merchantSessionJson = Buffer.from(applePaySessionBase64, 'base64').toString('utf-8');
81
- const merchantSession = JSON.parse(merchantSessionJson);
218
+ // Get currency and country from Apple Pay config
219
+ const applePayConfig = settings?.applePayConfig || {};
220
+ const currency = params.currency || applePayConfig.currencyCode || "EUR";
221
+ const countryCode = params.countryCode || applePayConfig.countryCode || "DE";
82
222
 
83
- if (merchantSession.epochTimestamp && merchantSession.epochTimestamp > 1000000000000) {
84
- merchantSession.epochTimestamp = Math.floor(merchantSession.epochTimestamp / 1000);
223
+ // Update params with config values
224
+ if (!params.currency && applePayConfig.currencyCode) {
225
+ params.currency = applePayConfig.currencyCode;
85
226
  }
86
-
87
- if (merchantSession.expiresAt && merchantSession.expiresAt > 1000000000000) {
88
- merchantSession.expiresAt = Math.floor(merchantSession.expiresAt / 1000);
227
+ if (!params.countryCode && applePayConfig.countryCode) {
228
+ params.countryCode = applePayConfig.countryCode;
89
229
  }
90
230
 
91
- if (!merchantSession.merchantIdentifier ||
92
- merchantSession.merchantIdentifier === 'undefined' ||
93
- merchantSession.merchantIdentifier === 'null') {
94
- strapi.log.error("Decoded merchant session has invalid merchantIdentifier");
95
- }
231
+ const sessionResponse = await initializeApplePaySession(strapi, params);
96
232
 
97
- return merchantSession;
98
- }
233
+ const applePaySessionBase64 = sessionResponse["add_paydata[applepay_payment_session]"] ||
234
+ sessionResponse.add_paydata?.applepay_payment_session;
99
235
 
100
- const errorCode = sessionResponse.errorcode || sessionResponse.ErrorCode;
101
- const errorMessage = sessionResponse.errormessage || sessionResponse.ErrorMessage ||
102
- sessionResponse.errortxt || sessionResponse.ErrorTxt;
236
+ if (sessionResponse.status === "OK" && applePaySessionBase64 && applePaySessionBase64.length > 0) {
237
+ try {
238
+ const merchantSessionJson = Buffer.from(applePaySessionBase64, 'base64').toString('utf-8');
239
+ const merchantSession = JSON.parse(merchantSessionJson);
103
240
 
104
- strapi.log.error(
105
- `Payone Apple Pay initialization failed: ${errorCode ? `Error ${errorCode}` : ''} ${errorMessage || 'Unknown error'}`
106
- );
241
+ if (merchantSession.epochTimestamp && merchantSession.epochTimestamp > 1000000000000) {
242
+ merchantSession.epochTimestamp = Math.floor(merchantSession.epochTimestamp / 1000);
243
+ }
107
244
 
108
- return null;
245
+ if (merchantSession.expiresAt && merchantSession.expiresAt > 1000000000000) {
246
+ merchantSession.expiresAt = Math.floor(merchantSession.expiresAt / 1000);
247
+ }
248
+
249
+ if (!merchantSession.merchantIdentifier ||
250
+ merchantSession.merchantIdentifier === 'undefined' ||
251
+ merchantSession.merchantIdentifier === 'null') {
252
+ merchantSession.merchantIdentifier = settings.mid || settings.merchantIdentifier || settings.portalid;
253
+ }
254
+
255
+ if (!merchantSession.merchantIdentifier) {
256
+ throw new Error("Merchant identifier is missing. Please configure Merchant ID (mid) in plugin settings.");
257
+ }
258
+
259
+ return merchantSession;
260
+ } catch (parseError) {
261
+ throw new Error(`Failed to parse merchant session from Payone: ${parseError.message}`);
262
+ }
263
+ }
264
+
265
+ const errorCode = sessionResponse.errorcode || sessionResponse.ErrorCode;
266
+ const errorMessage = sessionResponse.errormessage || sessionResponse.ErrorMessage ||
267
+ sessionResponse.errortxt || sessionResponse.ErrorTxt;
268
+
269
+ strapi.log.error("[Apple Pay] Payone Apple Pay initialization failed:", {
270
+ errorcode: errorCode,
271
+ errormessage: errorMessage
272
+ });
273
+
274
+ throw new Error(
275
+ `Payone Apple Pay initialization failed: ${errorCode ? `Error ${errorCode}` : 'Unknown error'} - ${errorMessage || 'Please check your Payone Apple Pay configuration in PMI'}`
276
+ );
277
+ } catch (error) {
278
+ throw error;
279
+ }
109
280
  };
110
281
 
111
282
  module.exports = {
@@ -26,7 +26,6 @@ const sendRequest = async (strapi, params) => {
26
26
  });
27
27
 
28
28
  const responseData = parseResponse(response.data, strapi.log);
29
-
30
29
  const errorCode =
31
30
  responseData.errorcode ||
32
31
  responseData.ErrorCode ||
@@ -43,10 +42,6 @@ const sendRequest = async (strapi, params) => {
43
42
  responseData.redirectUrl = redirectUrl;
44
43
  responseData.is3DSRequired = is3DSRequiredError;
45
44
 
46
- if (is3DSRequiredError && !redirectUrl) {
47
- strapi.log.warn("3DS authentication required (Error 4219) but no redirect URL found. May need 3dscheck request.");
48
- strapi.log.info("Full response data:", JSON.stringify(responseData, null, 2));
49
- }
50
45
  }
51
46
 
52
47
  const errorMessage =
@@ -177,14 +172,6 @@ const handle3DSCallback = async (strapi, callbackData, resultType = 'callback')
177
172
  status = parsedData.status || parsedData.Status || 'PENDING';
178
173
  }
179
174
 
180
- strapi.log.info("3DS callback processed:", {
181
- resultType,
182
- status,
183
- txid,
184
- reference,
185
- callbackData
186
- });
187
-
188
175
  return {
189
176
  success: resultType === 'success',
190
177
  status: status,
@@ -4,6 +4,7 @@ const axios = require("axios");
4
4
  const { buildClientRequestParams, toFormData } = require("../utils/requestBuilder");
5
5
  const { parseResponse } = require("../utils/responseParser");
6
6
  const { getSettings, validateSettings } = require("./settingsService");
7
+ const { addPaymentMethodParams } = require("../utils/paymentMethodParams");
7
8
 
8
9
  const POST_GATEWAY_URL = "https://api.pay1.de/post-gateway/";
9
10
 
@@ -27,7 +28,6 @@ const testConnection = async (strapi) => {
27
28
  clearingtype: "cc",
28
29
  cardtype: "V",
29
30
  cardpan: "4111111111111111",
30
- cardexpiredate: "2512",
31
31
  cardcvc2: "123",
32
32
  firstname: "Test",
33
33
  lastname: "User",
@@ -44,7 +44,8 @@ const testConnection = async (strapi) => {
44
44
  language: "de"
45
45
  };
46
46
 
47
- const requestParams = buildClientRequestParams(settings, testParams, strapi.log);
47
+ const updatedParams = addPaymentMethodParams(testParams, strapi.log);
48
+ const requestParams = buildClientRequestParams(settings, updatedParams, strapi.log);
48
49
  const formData = toFormData(requestParams);
49
50
 
50
51
  const response = await axios.post(POST_GATEWAY_URL, formData, {
@@ -1,15 +1,52 @@
1
1
  "use strict";
2
2
 
3
3
 
4
+ function getValidCardExpiryDate(cardexpiredate) {
5
+ const now = new Date();
6
+ const currentYear = now.getFullYear() % 100;
7
+ const currentMonth = now.getMonth() + 1;
8
+
9
+ if (!cardexpiredate || cardexpiredate.trim() === "") {
10
+ const nextYear = currentYear + 1;
11
+ const monthStr = String(currentMonth).padStart(2, '0');
12
+ return `${nextYear}${monthStr}`;
13
+ }
14
+
15
+ if (!/^\d{4}$/.test(cardexpiredate)) {
16
+ const nextYear = currentYear + 1;
17
+ const monthStr = String(currentMonth).padStart(2, '0');
18
+ return `${nextYear}${monthStr}`;
19
+ }
20
+
21
+ const year = parseInt(cardexpiredate.substring(0, 2), 10);
22
+ const month = parseInt(cardexpiredate.substring(2, 4), 10);
23
+
24
+ if (month < 1 || month > 12) {
25
+ const nextYear = currentYear + 1;
26
+ const monthStr = String(currentMonth).padStart(2, '0');
27
+ return `${nextYear}${monthStr}`;
28
+ }
29
+
30
+ const currentDate = new Date(2000 + currentYear, currentMonth - 1);
31
+ const expiryDate = new Date(2000 + year, month - 1);
32
+
33
+ if (expiryDate < currentDate) {
34
+ const nextYear = currentYear + 1;
35
+ const monthStr = String(currentMonth).padStart(2, '0');
36
+ return `${nextYear}${monthStr}`;
37
+ }
38
+
39
+ return cardexpiredate;
40
+ }
41
+
4
42
  const addPaymentMethodParams = (params, logger) => {
5
43
  const updated = { ...params };
6
44
  const clearingtype = updated.clearingtype || "cc";
7
45
 
8
- // Payment method specific defaults
9
46
  const methodDefaults = {
10
47
  cc: {
11
48
  cardpan: "4111111111111111",
12
- cardexpiredate: "2512",
49
+ cardexpiredate: getValidCardExpiryDate(null),
13
50
  cardcvc2: "123",
14
51
  cardtype: "V"
15
52
  },
@@ -72,7 +109,13 @@ const addPaymentMethodParams = (params, logger) => {
72
109
  if (key === "wallettype" && updated.wallettype) {
73
110
  return;
74
111
  }
75
- if (!updated[key]) {
112
+ if (key === "cardexpiredate") {
113
+ if (!updated[key] || updated[key].trim() === "") {
114
+ updated[key] = getValidCardExpiryDate(null);
115
+ } else {
116
+ updated[key] = getValidCardExpiryDate(updated[key]);
117
+ }
118
+ } else if (!updated[key]) {
76
119
  updated[key] = value;
77
120
  }
78
121
  });