strapi-plugin-payone-provider 1.6.2 → 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.
@@ -1,4 +1,4 @@
1
- import React, { useEffect } from "react";
1
+ import React from "react";
2
2
  import {
3
3
  Box,
4
4
  Flex,
@@ -24,13 +24,6 @@ const PaymentMethodSelector = ({
24
24
  onNavigateToConfig,
25
25
  isLiveMode,
26
26
  }) => {
27
- useEffect(() => {
28
- if (isLiveMode && paymentMethod !== "apl") {
29
- setPaymentMethod("apl");
30
- }
31
- // eslint-disable-next-line react-hooks/exhaustive-deps
32
- }, [isLiveMode]);
33
-
34
27
  return (
35
28
  <Box>
36
29
  <Flex direction="column" alignItems="stretch" gap={4}>
@@ -290,7 +283,7 @@ const PaymentMethodSelector = ({
290
283
  </Box>
291
284
  </>
292
285
  )}
293
- {supportsCaptureMode(paymentMethod) && (
286
+ {paymentMethod !== "apl" && supportsCaptureMode(paymentMethod) && (
294
287
  <Select
295
288
  label="Capture Mode"
296
289
  name="captureMode"
@@ -39,7 +39,7 @@ const PreauthorizationForm = ({
39
39
  };
40
40
 
41
41
  const handleGooglePayError = (error) => {
42
- console.error("[PreauthorizationForm] Google Pay error:", error);
42
+ console.error("Google Pay error:", error);
43
43
  };
44
44
 
45
45
  return (
@@ -22,17 +22,25 @@ const App = () => {
22
22
  const paymentActions = usePaymentActions();
23
23
 
24
24
  useEffect(() => {
25
- if (location.pathname.includes('/apple-pay-config') || location.pathname.includes('/google-pay-config')) {
25
+ if (
26
+ location.pathname.includes("/apple-pay-config") ||
27
+ location.pathname.includes("/google-pay-config")
28
+ ) {
26
29
  } else {
27
- const tabFromPath = location.pathname.includes('/history') ? 1 :
28
- location.pathname.includes('/payment-actions') ? 2 :
29
- location.pathname.includes('/documentation') ? 3 : 0;
30
+ const tabFromPath = location.pathname.includes("/history")
31
+ ? 1
32
+ : location.pathname.includes("/payment-actions")
33
+ ? 2
34
+ : location.pathname.includes("/documentation")
35
+ ? 3
36
+ : 0;
30
37
  setActiveTab(tabFromPath);
31
38
  }
32
39
  }, [location.pathname]);
33
40
 
34
- const isApplePayConfigPage = location.pathname.includes('/apple-pay-config');
35
- const isGooglePayConfigPage = location.pathname.includes('/google-pay-config');
41
+ const isApplePayConfigPage = location.pathname.includes("/apple-pay-config");
42
+ const isGooglePayConfigPage =
43
+ location.pathname.includes("/google-pay-config");
36
44
 
37
45
  if (isApplePayConfigPage) {
38
46
  return (
@@ -124,4 +132,4 @@ const App = () => {
124
132
  );
125
133
  };
126
134
 
127
- export default App;
135
+ export default App;
@@ -6,7 +6,8 @@ import {
6
6
  getAuthorizationParams,
7
7
  getCaptureParams,
8
8
  getRefundParams,
9
- generateLagOrderNumber
9
+ generateLagOrderNumber,
10
+ getValidCardExpiryDate,
10
11
  } from "../utils/paymentUtils";
11
12
  import { DEFAULT_PAYMENT_DATA } from "../constants/paymentConstants";
12
13
 
@@ -75,11 +76,14 @@ const usePaymentActions = () => {
75
76
  };
76
77
 
77
78
  const handlePreauthorization = async (tokenParam = null) => {
79
+ <<<<<<< HEAD
78
80
  console.log("[Payment] handlePreauthorization called", {
79
81
  hasToken: !!tokenParam,
80
82
  paymentMethod,
81
83
  amount: paymentAmount
82
84
  });
85
+ =======
86
+ >>>>>>> release/1.6.4
83
87
  setIsProcessingPayment(true);
84
88
  setPaymentError(null);
85
89
  setPaymentResult(null);
@@ -102,7 +106,7 @@ const usePaymentActions = () => {
102
106
  if (paymentMethod === "cc" && settings.enable3DSecure !== false) {
103
107
  if (cardtype) baseParams.cardtype = cardtype;
104
108
  if (cardpan) baseParams.cardpan = cardpan;
105
- if (cardexpiredate) baseParams.cardexpiredate = cardexpiredate;
109
+ baseParams.cardexpiredate = getValidCardExpiryDate(cardexpiredate);
106
110
  if (cardcvc2) baseParams.cardcvc2 = cardcvc2;
107
111
  }
108
112
 
@@ -152,8 +156,6 @@ const usePaymentActions = () => {
152
156
  null;
153
157
 
154
158
  if (is3DSRequiredError && !redirectUrl) {
155
- console.warn("3DS authentication required (Error 4219) but no redirect URL found in response");
156
- console.log("Full response:", JSON.stringify(responseData, null, 2));
157
159
  setPaymentError(
158
160
  "3D Secure authentication required. Please check Payone configuration and ensure redirect URLs are properly set. Error: " +
159
161
  (errorMessage || `Error code: ${errorCode}`)
@@ -177,7 +179,6 @@ const usePaymentActions = () => {
177
179
  (is3DSRequiredError && redirectUrl);
178
180
 
179
181
  if (needsRedirect && redirectUrl) {
180
- console.log("Redirecting to 3DS:", redirectUrl);
181
182
  window.location.href = redirectUrl;
182
183
  return;
183
184
  }
@@ -228,7 +229,7 @@ const usePaymentActions = () => {
228
229
  if (paymentMethod === "cc" && settings.enable3DSecure !== false) {
229
230
  if (cardtype) baseParams.cardtype = cardtype;
230
231
  if (cardpan) baseParams.cardpan = cardpan;
231
- if (cardexpiredate) baseParams.cardexpiredate = cardexpiredate;
232
+ baseParams.cardexpiredate = getValidCardExpiryDate(cardexpiredate);
232
233
  if (cardcvc2) baseParams.cardcvc2 = cardcvc2;
233
234
  }
234
235
 
@@ -278,8 +279,6 @@ const usePaymentActions = () => {
278
279
  null;
279
280
 
280
281
  if (is3DSRequiredError && !redirectUrl) {
281
- console.warn("3DS authentication required (Error 4219) but no redirect URL found in response");
282
- console.log("Full response:", JSON.stringify(responseData, null, 2));
283
282
  setPaymentError(
284
283
  "3D Secure authentication required. Please check Payone configuration and ensure redirect URLs are properly set. Error: " +
285
284
  (errorMessage || `Error code: ${errorCode}`)
@@ -303,7 +302,6 @@ const usePaymentActions = () => {
303
302
  (is3DSRequiredError && redirectUrl);
304
303
 
305
304
  if (needsRedirect && redirectUrl) {
306
- console.log("Redirecting to 3DS:", redirectUrl);
307
305
  window.location.href = redirectUrl;
308
306
  return;
309
307
  }
@@ -0,0 +1,34 @@
1
+ export const injectApplePayScript = () => {
2
+
3
+ if (typeof window === 'undefined') {
4
+ return;
5
+ }
6
+
7
+ const checkApplePay = () => {
8
+ try {
9
+ return typeof window.ApplePaySession !== 'undefined' &&
10
+ typeof window.ApplePaySession.canMakePayments === 'function';
11
+ } catch (e) {
12
+ return false;
13
+ }
14
+ };
15
+
16
+ if (checkApplePay()) {
17
+ window.dispatchEvent(new CustomEvent("applePayAvailable"));
18
+ } else {
19
+ const checkInterval = setInterval(() => {
20
+ if (checkApplePay()) {
21
+ clearInterval(checkInterval);
22
+ window.dispatchEvent(new CustomEvent("applePayAvailable"));
23
+ }
24
+ }, 200);
25
+
26
+ setTimeout(() => {
27
+ clearInterval(checkInterval);
28
+ if (!checkApplePay()) {
29
+ window.dispatchEvent(new CustomEvent("applePayNotAvailable"));
30
+ }
31
+ }, 5000);
32
+ }
33
+ };
34
+
@@ -1,3 +1,43 @@
1
+ export function getValidCardExpiryDate(cardexpiredate) {
2
+ const now = new Date();
3
+ const currentYear = now.getFullYear() % 100;
4
+ const currentMonth = now.getMonth() + 1;
5
+
6
+ if (!cardexpiredate || cardexpiredate.trim() === "") {
7
+ const nextYear = currentYear + 1;
8
+ const monthStr = String(currentMonth).padStart(2, '0');
9
+ return `${nextYear}${monthStr}`;
10
+ }
11
+
12
+ // Validate format (must be 4 digits)
13
+ if (!/^\d{4}$/.test(cardexpiredate)) {
14
+ const nextYear = currentYear + 1;
15
+ const monthStr = String(currentMonth).padStart(2, '0');
16
+ return `${nextYear}${monthStr}`;
17
+ }
18
+
19
+ // Parse YYMM format
20
+ const year = parseInt(cardexpiredate.substring(0, 2), 10);
21
+ const month = parseInt(cardexpiredate.substring(2, 4), 10);
22
+
23
+ // Validate month (1-12)
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
+ }
1
41
 
2
42
  export function generateLagOrderNumber(sequence = 1000) {
3
43
  const paddedSequence = sequence.toString().padStart(5, '0');
@@ -125,7 +165,7 @@ export const getPaymentMethodParams = (paymentMethod, options = {}) => {
125
165
  clearingtype: "cc",
126
166
  cardtype: finalCardType, // V = Visa, M = Mastercard, A = Amex
127
167
  cardpan: cardpan || "4111111111111111", // Test Visa card
128
- cardexpiredate: cardexpiredate || "2512", // MMYY format
168
+ cardexpiredate: getValidCardExpiryDate(cardexpiredate),
129
169
  cardcvc2: cardcvc2 || "123" // 3-digit security code
130
170
  };
131
171
 
@@ -374,24 +414,16 @@ export const getPaymentMethodOptions = (isLiveMode = false) => {
374
414
  { value: "wlt", label: "PayPal" },
375
415
  { value: "gpp", label: "Google Pay" },
376
416
  { value: "apl", label: "Apple Pay" },
377
- { value: "sb", label: "Sofort Banking" },
378
417
  { value: "elv", label: "SEPA Direct Debit" }
379
418
  ];
380
419
  };
381
420
 
382
- /**
383
- * Check if payment method supports capture mode
384
- * @param {string} paymentMethod - Payment method
385
- * @returns {boolean} True if supports capture mode
386
- */
421
+
387
422
  export const supportsCaptureMode = (paymentMethod) => {
388
- return paymentMethod === "wlt" || paymentMethod === "gpp" || paymentMethod === "apl"; // PayPal, Google Pay, and Apple Pay support capture mode
423
+ return paymentMethod === "wlt" || paymentMethod === "gpp" || paymentMethod === "apl";
389
424
  };
390
425
 
391
- /**
392
- * Get capture mode options
393
- * @returns {Array} Array of capture mode options
394
- */
426
+
395
427
  export const getCaptureModeOptions = () => {
396
428
  return [
397
429
  { value: "full", label: "Full Capture" },
@@ -399,14 +431,6 @@ export const getCaptureModeOptions = () => {
399
431
  ];
400
432
  };
401
433
 
402
- /**
403
- * Validate payment parameters based on Payone v1 documentation
404
- * Comprehensive validation for all operations and payment methods
405
- * @param {string} operation - Operation type (preauthorization, authorization, capture, refund)
406
- * @param {string} paymentMethod - Payment method
407
- * @param {Object} params - Parameters to validate
408
- * @returns {Object} Validation result with detailed error messages
409
- */
410
434
  export const validatePaymentParams = (operation, paymentMethod, params) => {
411
435
  const errors = [];
412
436
 
@@ -419,12 +443,10 @@ export const validatePaymentParams = (operation, paymentMethod, params) => {
419
443
  errors.push("Currency is required");
420
444
  }
421
445
 
422
- // Validate currency format (ISO 4217)
423
446
  if (params.currency && !/^[A-Z]{3}$/.test(params.currency)) {
424
447
  errors.push("Currency must be in ISO 4217 format (e.g., EUR, USD)");
425
448
  }
426
449
 
427
- // Operation specific validations (Payone v1 documentation)
428
450
  switch (operation) {
429
451
  case "preauthorization":
430
452
  if (!params.reference) {
@@ -504,7 +526,6 @@ export const validatePaymentParams = (operation, paymentMethod, params) => {
504
526
  break;
505
527
  }
506
528
 
507
- // Payment method specific validations (Payone v1 documentation)
508
529
  switch (paymentMethod) {
509
530
  case "cc":
510
531
  if (!params.cardpan || !params.cardexpiredate || !params.cardcvc2) {
@@ -517,9 +538,9 @@ export const validatePaymentParams = (operation, paymentMethod, params) => {
517
538
  if (params.cardpan && !/^\d{13,19}$/.test(params.cardpan.replace(/\s/g, ''))) {
518
539
  errors.push("Card number must be 13-19 digits");
519
540
  }
520
- // Validate expiry date format (MMYY)
541
+ // Validate expiry date format (YYMM)
521
542
  if (params.cardexpiredate && !/^\d{4}$/.test(params.cardexpiredate)) {
522
- errors.push("Card expiry date must be in MMYY format");
543
+ errors.push("Card expiry date must be in YYMM format (e.g., 2512 = December 2025)");
523
544
  }
524
545
  // Validate CVC format (3-4 digits)
525
546
  if (params.cardcvc2 && !/^\d{3,4}$/.test(params.cardcvc2)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "strapi-plugin-payone-provider",
3
- "version": "1.6.2",
3
+ "version": "1.6.4",
4
4
  "description": "Strapi plugin for Payone payment gateway integration",
5
5
  "license": "MIT",
6
6
  "maintainers": [
@@ -10,6 +10,7 @@
10
10
  }
11
11
  ],
12
12
  "dependencies": {
13
+ "apple-pay-button": "^1.2.1",
13
14
  "axios": "^1.6.3",
14
15
  "prop-types": "^15.7.2"
15
16
  },
@@ -58,53 +58,37 @@ module.exports = async ({ strapi }) => {
58
58
  try {
59
59
  const publicPath = path.join(process.cwd(), 'public');
60
60
  const wellKnownPath = path.join(publicPath, '.well-known');
61
- const filePath = path.join(wellKnownPath, 'apple-developer-merchantid-domain-association');
62
- const filePathTxt = path.join(wellKnownPath, 'apple-developer-merchant-id-domain-association.txt');
61
+ const possiblePaths = [
62
+ path.join(wellKnownPath, 'apple-developer-merchantid-domain-association'),
63
+ path.join(wellKnownPath, 'apple-developer-merchantid-domain-association.txt'),
64
+ path.join(wellKnownPath, 'apple-developer-merchant-id-domain-association.txt'),
65
+ ];
63
66
 
64
67
  let fileContent = null;
65
68
  let filePathFound = null;
66
69
 
67
- if (fs.existsSync(filePath)) {
68
- filePathFound = filePath;
69
- fileContent = fs.readFileSync(filePath, 'utf8');
70
- }
71
- else if (fs.existsSync(filePathTxt)) {
72
- filePathFound = filePathTxt;
73
- fileContent = fs.readFileSync(filePathTxt, 'utf8');
70
+ for (const filePath of possiblePaths) {
71
+ if (fs.existsSync(filePath)) {
72
+ filePathFound = filePath;
73
+ fileContent = fs.readFileSync(filePath, 'utf8');
74
+ break;
75
+ }
74
76
  }
75
77
 
76
78
  if (fileContent) {
77
79
  ctx.type = 'text/plain';
78
- ctx.body = fileContent;
79
- strapi.log.info(`[Apple Pay] Served well-known file from: ${filePathFound}`);
80
+ ctx.set('Content-Type', 'text/plain');
81
+ ctx.set('Cache-Control', 'public, max-age=31536000');
82
+ ctx.body = fileContent.trim();
80
83
  } else {
81
- strapi.log.warn(`[Apple Pay] Well-known file not found. Tried: ${filePath} and ${filePathTxt}`);
82
84
  ctx.status = 404;
83
- ctx.body = {
84
- error: {
85
- status: 404,
86
- name: "NotFoundError",
87
- message: "Apple Pay domain verification file not found",
88
- details: {
89
- expectedPaths: [
90
- filePath,
91
- filePathTxt
92
- ]
93
- }
94
- }
95
- };
85
+ ctx.type = 'text/plain';
86
+ ctx.body = 'File not found';
96
87
  }
97
88
  } catch (error) {
98
- strapi.log.error("[Apple Pay] Serve well-known file error:", error);
99
89
  ctx.status = 500;
100
- ctx.body = {
101
- error: {
102
- status: 500,
103
- name: "InternalServerError",
104
- message: error.message || "Failed to serve well-known file",
105
- details: error.stack
106
- }
107
- };
90
+ ctx.type = 'text/plain';
91
+ ctx.body = 'Internal server error';
108
92
  }
109
93
  });
110
94
 
@@ -113,6 +97,6 @@ module.exports = async ({ strapi }) => {
113
97
  strapi.server.app.use(router.allowedMethods());
114
98
  }
115
99
  } catch (error) {
116
- strapi.log.warn('Could not register 3DS callback routes:', error.message);
100
+ // Silent fail
117
101
  }
118
102
  };
@@ -7,7 +7,12 @@ const getPayoneService = (strapi) => {
7
7
  };
8
8
 
9
9
  const handleError = (ctx, error) => {
10
- ctx.strapi.log.error("Payone controller error:", error);
10
+ if (error.response || error.status >= 400) {
11
+ ctx.strapi.log.error("Payone controller error:", {
12
+ status: error.status || error.response?.status,
13
+ message: error.message
14
+ });
15
+ }
11
16
  ctx.throw(500, error);
12
17
  };
13
18
 
@@ -71,11 +76,9 @@ module.exports = ({ strapi }) => ({
71
76
  async authorization(ctx) {
72
77
  try {
73
78
  const params = ctx.request.body;
74
- strapi.log.info("Payone authorization controller called with:", params);
75
79
  const result = await getPayoneService(strapi).authorization(params);
76
80
  ctx.body = { data: result };
77
81
  } catch (error) {
78
- strapi.log.error("Payone authorization error:", error);
79
82
  handleError(ctx, error);
80
83
  }
81
84
  },
@@ -134,7 +137,6 @@ module.exports = ({ strapi }) => ({
134
137
  }
135
138
 
136
139
  const callbackData = isGetRequest ? ctx.query : ctx.request.body;
137
- strapi.log.info(`3DS ${resultType} received (${ctx.request.method}):`, callbackData);
138
140
  const result = await getPayoneService(strapi).handle3DSCallback(callbackData, resultType);
139
141
 
140
142
  if (isGetRequest) {
@@ -153,31 +155,81 @@ module.exports = ({ strapi }) => ({
153
155
 
154
156
  ctx.body = { data: result };
155
157
  } catch (error) {
156
- strapi.log.error("3DS callback error:", error);
157
158
  handleError(ctx, error);
158
159
  }
159
160
  },
160
161
 
161
162
  async validateApplePayMerchant(ctx) {
162
163
  try {
163
- strapi.log.info("[Apple Pay] Request body:", JSON.stringify(ctx.request.body, null, 2));
164
+ const settings = await getPayoneService(strapi).getSettings();
165
+ const applePayConfig = settings?.applePayConfig || {};
164
166
 
165
167
  const params = ctx.request.body;
168
+
169
+ if (!params) {
170
+ throw new Error("Request body is missing");
171
+ }
172
+
173
+ // Ensure domain is set
174
+ if (!params.domain && !params.domainName) {
175
+ params.domain = ctx.request.hostname || ctx.request.host || 'localhost';
176
+ params.domainName = params.domain;
177
+ } else if (params.domain && !params.domainName) {
178
+ params.domainName = params.domain;
179
+ } else if (params.domainName && !params.domain) {
180
+ params.domain = params.domainName;
181
+ }
182
+
183
+ if (!params.displayName) {
184
+ params.displayName = settings?.merchantName || "Store";
185
+ }
186
+
187
+ if (!params.currency) {
188
+ params.currency = applePayConfig.currencyCode || "EUR";
189
+ }
190
+ if (!params.countryCode) {
191
+ params.countryCode = applePayConfig.countryCode || "DE";
192
+ }
193
+
166
194
  let result = await getPayoneService(strapi).validateApplePayMerchant(params);
167
- strapi.log.info("[Apple Pay] Merchant validation result:", JSON.stringify(result, null, 2));
195
+
196
+ if (!result) {
197
+ throw new Error("Merchant validation returned null. Please check your Payone Apple Pay configuration.");
198
+ }
199
+
168
200
  ctx.body = { data: result };
169
201
  } catch (error) {
170
- strapi.log.error("[Apple Pay] Controller error:", {
171
- message: error.message,
172
- stack: error.stack,
173
- name: error.name
174
- });
202
+ const errorStatus = error.status || (error.message?.includes('403') ? 403 : 500);
203
+
204
+ // Only log if it's a response error
205
+ if (error.response || errorStatus === 403 || errorStatus === 401 || errorStatus >= 500) {
206
+ strapi.log.error("[Apple Pay] Controller error:", {
207
+ status: errorStatus,
208
+ message: error.message
209
+ });
210
+ }
211
+
212
+ // Extract detailed error message if available
213
+ let errorMessage = error.message || "Apple Pay merchant validation failed";
214
+ let errorDetails = "Please check your Payone Apple Pay configuration in PMI (CONFIGURATION → PAYMENT PORTALS → [Your Portal] → Apple Pay). Ensure that Merchant ID (mid) is correctly configured and Apple Pay is enabled for your portal.";
215
+
216
+ // If it's a 403 error, provide more specific guidance
217
+ if (errorStatus === 403 || error.message?.includes('403')) {
218
+ errorDetails = "403 Forbidden: Authentication failed with Payone. " +
219
+ "Please check: 1) Your Payone credentials (aid, portalid, mid, key) in plugin settings, " +
220
+ "2) Mode is set to 'live' (Apple Pay only works in live mode), " +
221
+ "3) Your domain is registered with Payone Merchant Services, " +
222
+ "4) Merchant ID (mid) matches your merchantIdentifier in PMI, " +
223
+ "5) Apple Pay is enabled for your portal in PMI.";
224
+ }
175
225
 
176
- ctx.status = error.status || 500;
226
+ ctx.status = errorStatus;
177
227
  ctx.body = {
178
228
  error: {
179
- message: error.message || "Apple Pay merchant validation failed",
180
- details: "Please check your Payone Apple Pay configuration in PMI (CONFIGURATION → PAYMENT PORTALS → [Your Portal] → Apple Pay)"
229
+ status: errorStatus,
230
+ name: error.name || "Error",
231
+ message: errorMessage,
232
+ details: errorDetails
181
233
  }
182
234
  };
183
235
  }