strapi-plugin-payone-provider 1.5.3 → 1.5.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,5 +1,7 @@
1
1
  import React, { useEffect, useRef, useState } from "react";
2
2
  import { Box, Flex, Typography } from "@strapi/design-system";
3
+ import { request } from "@strapi/helper-plugin";
4
+ import pluginId from "../../../pluginId";
3
5
  import { DEFAULT_APPLE_PAY_CONFIG } from "../../utils/applePayConstants";
4
6
 
5
7
  /**
@@ -41,13 +43,41 @@ const ApplePayButton = ({
41
43
  try {
42
44
  console.log("[Apple Pay] Checking availability...");
43
45
 
44
- // Check if we're on HTTPS (required for Apple Pay JS API)
45
- const isSecure = typeof window !== 'undefined' &&
46
- (window.location.protocol === 'https:' ||
47
- window.location.hostname === 'localhost' ||
48
- window.location.hostname === '127.0.0.1');
46
+ // Check secure context using browser's native property
47
+ // This is the most reliable way to check if we're in a secure context
48
+ const isSecureContext = typeof window !== 'undefined' && window.isSecureContext;
49
+ const protocol = typeof window !== 'undefined' ? window.location.protocol : '';
50
+ const hostname = typeof window !== 'undefined' ? window.location.hostname : '';
51
+ const isLocalhost = hostname === 'localhost' || hostname === '127.0.0.1';
52
+
53
+ // For Apple Pay, we need a secure context (HTTPS or localhost)
54
+ // window.isSecureContext is true for:
55
+ // - HTTPS pages
56
+ // - localhost (even on HTTP)
57
+ // - 127.0.0.1 (even on HTTP)
58
+ // - file:// URLs
59
+ const isSecure = isSecureContext || isLocalhost;
60
+
61
+ console.log("[Apple Pay] Secure context check:", {
62
+ isSecureContext: isSecureContext,
63
+ protocol: protocol,
64
+ hostname: hostname,
65
+ isLocalhost: isLocalhost,
66
+ isSecure: isSecure,
67
+ fullUrl: typeof window !== 'undefined' ? window.location.href : ''
68
+ });
49
69
 
50
- console.log("[Apple Pay] Secure context:", isSecure, "Protocol:", window.location?.protocol);
70
+ // If not secure, log detailed information
71
+ if (!isSecure) {
72
+ console.error("[Apple Pay] NOT in secure context!", {
73
+ isSecureContext: isSecureContext,
74
+ protocol: protocol,
75
+ hostname: hostname,
76
+ isLocalhost: isLocalhost,
77
+ fullUrl: typeof window !== 'undefined' ? window.location.href : '',
78
+ reason: !isSecureContext ? "window.isSecureContext is false" : "Unknown"
79
+ });
80
+ }
51
81
 
52
82
  // First, check if Payment Request API is available
53
83
  // Payment Request API works on HTTP too, but Apple Pay JS API requires HTTPS
@@ -168,15 +198,23 @@ const ApplePayButton = ({
168
198
  }
169
199
  }
170
200
 
171
- // If canMakePayment is not available, assume it's available (for older browsers)
172
- // But only if we're in a secure context
173
- // Reuse isSecure from line 47
201
+ // If canMakePayment is not available, check secure context again
202
+ // Re-check secure context to ensure we have the latest state
203
+ const isSecureContextFinal = typeof window !== 'undefined' && window.isSecureContext;
204
+ const hostnameFinal = typeof window !== 'undefined' ? window.location.hostname : '';
205
+ const isLocalhostFinal = hostnameFinal === 'localhost' || hostnameFinal === '127.0.0.1';
206
+ const isSecureFinal = isSecureContextFinal || isLocalhostFinal;
174
207
 
175
- if (isSecure) {
208
+ if (isSecureFinal) {
176
209
  console.log("[Apple Pay] canMakePayment not available, assuming support (secure context)");
177
210
  return { available: true, method: 'paymentRequest' };
178
211
  } else {
179
212
  console.warn("[Apple Pay] canMakePayment not available and insecure context");
213
+ console.warn("[Apple Pay] Context details:", {
214
+ isSecureContext: isSecureContextFinal,
215
+ hostname: hostnameFinal,
216
+ isLocalhost: isLocalhostFinal
217
+ });
180
218
  return { available: false, method: null, error: 'insecure_context' };
181
219
  }
182
220
  } catch (error) {
@@ -189,9 +227,13 @@ const ApplePayButton = ({
189
227
  }
190
228
 
191
229
  // Fallback: Try Apple Pay JS API (only on HTTPS)
192
- // Reuse isSecure from line 47
230
+ // Re-check secure context
231
+ const isSecureContextFallback = typeof window !== 'undefined' && window.isSecureContext;
232
+ const hostnameFallback = typeof window !== 'undefined' ? window.location.hostname : '';
233
+ const isLocalhostFallback = hostnameFallback === 'localhost' || hostnameFallback === '127.0.0.1';
234
+ const isSecureFallback = isSecureContextFallback || isLocalhostFallback;
193
235
 
194
- if (typeof window !== 'undefined' && window.ApplePaySession && isSecure) {
236
+ if (typeof window !== 'undefined' && window.ApplePaySession && isSecureFallback) {
195
237
  try {
196
238
  const canMakePayments = ApplePaySession.canMakePayments();
197
239
  return { available: canMakePayments, method: 'applePayJS' };
@@ -461,16 +503,22 @@ const ApplePayButton = ({
461
503
  });
462
504
  }).catch(err => {
463
505
  console.error("[Apple Pay] Merchant session error:", err);
506
+ // Don't call onError here - let the dialog handle it
464
507
  });
465
508
 
509
+ // Complete with the merchant session promise
510
+ // If it fails, Apple Pay will handle it gracefully
466
511
  event.complete(merchantSessionPromise);
467
512
  } catch (error) {
468
513
  console.error("[Apple Pay] Merchant validation error:", error);
469
- if (onError) {
514
+ // Only call onError if it's defined
515
+ if (typeof onError === 'function') {
470
516
  onError(error);
471
517
  }
472
- // Complete with empty object - Payone will handle validation
473
- event.complete({});
518
+ // Complete with a promise that resolves to empty object
519
+ // This allows the dialog to continue even if validation fails
520
+ // Apple Pay will show an error but won't close immediately
521
+ event.complete(Promise.resolve({}));
474
522
  }
475
523
  };
476
524
 
@@ -504,7 +552,14 @@ const ApplePayButton = ({
504
552
  response = await request.show();
505
553
  } catch (error) {
506
554
  console.error("[Apple Pay] Error showing payment sheet:", error);
507
- if (onError) {
555
+ // Check if error is due to cancellation (user cancelled)
556
+ if (error.message && error.message.includes('Cancelled')) {
557
+ console.log("[Apple Pay] User cancelled the payment");
558
+ // Don't call onError for user cancellation
559
+ return;
560
+ }
561
+ // Only call onError if it's defined and it's not a cancellation
562
+ if (typeof onError === 'function') {
508
563
  onError(error);
509
564
  }
510
565
  return;
@@ -630,7 +685,8 @@ const ApplePayButton = ({
630
685
  } catch (finalError) {
631
686
  console.error("[Apple Pay] Failed to complete payment even with fail status:", finalError);
632
687
  }
633
- if (onError) {
688
+ // Only call onError if it's defined
689
+ if (typeof onError === 'function') {
634
690
  onError(completeError);
635
691
  }
636
692
  }
@@ -641,7 +697,14 @@ const ApplePayButton = ({
641
697
  stack: error.stack,
642
698
  name: error.name
643
699
  });
644
- if (onError) {
700
+ // Check if error is due to cancellation (user cancelled)
701
+ if (error.message && error.message.includes('Cancelled')) {
702
+ console.log("[Apple Pay] User cancelled the payment");
703
+ // Don't call onError for user cancellation
704
+ return;
705
+ }
706
+ // Only call onError if it's defined and it's not a cancellation
707
+ if (typeof onError === 'function') {
645
708
  onError(error);
646
709
  }
647
710
  }
@@ -660,43 +723,45 @@ const ApplePayButton = ({
660
723
  });
661
724
 
662
725
  try {
663
- // Call Strapi backend to validate with Payone
664
- const response = await fetch('/api/strapi-plugin-payone-provider/validate-apple-pay-merchant', {
726
+ console.log("[Apple Pay] Making validation request using Strapi helper...");
727
+
728
+ // Use Strapi helper-plugin's request function which automatically handles authentication
729
+ // This uses the admin API route which requires admin authentication
730
+ const merchantSession = await request(`/${pluginId}/validate-apple-pay-merchant`, {
665
731
  method: 'POST',
666
- headers: {
667
- 'Content-Type': 'application/json',
668
- },
669
- body: JSON.stringify({
732
+ body: {
670
733
  validationURL,
671
734
  ...config
672
- })
735
+ }
673
736
  });
674
737
 
675
- console.log("[Apple Pay] Validation response status:", response.status);
738
+ console.log("[Apple Pay] Merchant session received from backend:", {
739
+ hasData: !!merchantSession.data,
740
+ merchantIdentifier: merchantSession.data?.merchantIdentifier
741
+ });
676
742
 
677
- if (response.ok) {
678
- const merchantSession = await response.json();
679
- console.log("[Apple Pay] Merchant session received from backend:", {
680
- hasData: !!merchantSession.data,
681
- merchantIdentifier: merchantSession.data?.merchantIdentifier
682
- });
683
- return merchantSession.data || merchantSession;
684
- } else {
685
- // If validation fails, return empty object - Payone will handle it
686
- const errorText = await response.text();
687
- console.warn("[Apple Pay] Merchant validation failed:", {
688
- status: response.status,
689
- statusText: response.statusText,
690
- error: errorText
691
- });
692
- return {};
693
- }
743
+ return merchantSession.data || merchantSession;
694
744
  } catch (error) {
695
745
  console.error("[Apple Pay] Merchant validation error:", {
696
746
  message: error.message,
747
+ status: error.response?.status,
748
+ statusText: error.response?.statusText,
749
+ data: error.response?.data,
697
750
  stack: error.stack
698
751
  });
699
- // Return empty object - Payone will handle validation
752
+
753
+ // Log specific error details
754
+ if (error.response?.status === 403) {
755
+ console.error("[Apple Pay] 403 Forbidden - Authentication failed. Make sure you are logged in as admin.");
756
+ } else if (error.response?.status === 401) {
757
+ console.error("[Apple Pay] 401 Unauthorized - Please log in again.");
758
+ } else if (error.response?.status >= 500) {
759
+ console.error("[Apple Pay] Server error - Check server logs for details.");
760
+ }
761
+
762
+ // If validation fails, return empty object
763
+ // This allows the dialog to continue, though Apple Pay may show an error
764
+ // The user can still proceed with payment if they want
700
765
  return {};
701
766
  }
702
767
  };
@@ -720,12 +785,6 @@ const ApplePayButton = ({
720
785
  )}
721
786
  {!isLoading && isAvailable && (
722
787
  <>
723
- <Typography variant="sigma" textColor="neutral700" fontWeight="semiBold" style={{ textAlign: "left" }}>
724
- Apple Pay Payment
725
- </Typography>
726
- <Typography variant="pi" textColor="neutral600" style={{ textAlign: "left" }}>
727
- Click the button below to pay with Apple Pay. The token will be automatically sent to Payone.
728
- </Typography>
729
788
  <Box ref={buttonContainerRef} style={{ minHeight: "40px", width: "100%", display: "flex", justifyContent: "flex-start" }}>
730
789
  {typeof window !== 'undefined' && window.customElements && window.customElements.get('apple-pay-button') ? (
731
790
  <apple-pay-button
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "strapi-plugin-payone-provider",
3
- "version": "1.5.3",
3
+ "version": "1.5.4",
4
4
  "description": "Strapi plugin for Payone payment gateway integration",
5
5
  "license": "MIT",
6
6
  "maintainers": [
@@ -17,7 +17,7 @@ const getPayoneService = (strapi) => {
17
17
  * @param {Error} error - Error object
18
18
  */
19
19
  const handleError = (ctx, error) => {
20
- strapi.log.error("Payone controller error:", error);
20
+ ctx.strapi.log.error("Payone controller error:", error);
21
21
  ctx.throw(500, error);
22
22
  };
23
23
 
@@ -180,10 +180,28 @@ module.exports = ({ strapi }) => ({
180
180
 
181
181
  async validateApplePayMerchant(ctx) {
182
182
  try {
183
+ strapi.log.info("[Apple Pay] Merchant validation request received");
184
+ strapi.log.info("[Apple Pay] Request body:", JSON.stringify(ctx.request.body, null, 2));
185
+ strapi.log.info("[Apple Pay] User:", ctx.state.user ? {
186
+ id: ctx.state.user.id,
187
+ email: ctx.state.user.email,
188
+ roles: ctx.state.user.roles?.map(r => r.code)
189
+ } : "No user");
190
+
183
191
  const params = ctx.request.body;
184
192
  const result = await getPayoneService(strapi).validateApplePayMerchant(params);
193
+
194
+ strapi.log.info("[Apple Pay] Merchant validation result:", {
195
+ hasResult: !!result,
196
+ hasMerchantIdentifier: !!result.merchantIdentifier
197
+ });
198
+
185
199
  ctx.body = { data: result };
186
200
  } catch (error) {
201
+ strapi.log.error("[Apple Pay] Controller error:", {
202
+ message: error.message,
203
+ stack: error.stack
204
+ });
187
205
  handleError(ctx, error);
188
206
  }
189
207
  }
@@ -24,7 +24,7 @@ const initializeApplePaySession = async (strapi, params) => {
24
24
  try {
25
25
  strapi.log.info("[Apple Pay] Initializing Apple Pay session with Payone");
26
26
  strapi.log.info("[Apple Pay] Request params:", JSON.stringify(params, null, 2));
27
-
27
+
28
28
  const settings = await getSettings(strapi);
29
29
 
30
30
  if (!validateSettings(settings)) {
@@ -55,9 +55,9 @@ const initializeApplePaySession = async (strapi, params) => {
55
55
  const mode = settings.mode || "test"; // test or live
56
56
 
57
57
  // Get domain from params or settings or server config
58
- const domain = domainName || settings.domainName ||
59
- (strapi.config.get("server.url") ? new URL(strapi.config.get("server.url")).hostname : null) ||
60
- "localhost";
58
+ const domain = domainName || settings.domainName ||
59
+ (strapi.config.get("server.url") ? new URL(strapi.config.get("server.url")).hostname : null) ||
60
+ "localhost";
61
61
 
62
62
  // Build request parameters for Apple Pay session initialization
63
63
  // According to Payone documentation: request="genericpayment"
@@ -87,14 +87,38 @@ const initializeApplePaySession = async (strapi, params) => {
87
87
 
88
88
  const formData = toFormData(requestParams);
89
89
 
90
- const response = await axios.post(POST_GATEWAY_URL, formData, {
91
- headers: { "Content-Type": "application/x-www-form-urlencoded" },
92
- timeout: 30000
90
+ strapi.log.info("[Apple Pay] Sending request to Payone API:", {
91
+ url: POST_GATEWAY_URL,
92
+ params: {
93
+ ...requestParams,
94
+ key: "***HIDDEN***" // Hide API key in logs
95
+ }
93
96
  });
94
97
 
98
+ let response;
99
+ try {
100
+ response = await axios.post(POST_GATEWAY_URL, formData, {
101
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
102
+ timeout: 30000
103
+ });
104
+ } catch (axiosError) {
105
+ strapi.log.error("[Apple Pay] Payone API request failed:", {
106
+ message: axiosError.message,
107
+ status: axiosError.response?.status,
108
+ statusText: axiosError.response?.statusText,
109
+ data: axiosError.response?.data,
110
+ config: {
111
+ url: axiosError.config?.url,
112
+ method: axiosError.config?.method
113
+ }
114
+ });
115
+ throw axiosError;
116
+ }
117
+
95
118
  strapi.log.info("[Apple Pay] Payone response received:", {
96
119
  status: response.status,
97
- statusText: response.statusText
120
+ statusText: response.statusText,
121
+ headers: response.headers
98
122
  });
99
123
 
100
124
  // Parse response
@@ -102,7 +126,7 @@ const initializeApplePaySession = async (strapi, params) => {
102
126
 
103
127
  strapi.log.info("[Apple Pay] Session initialization response:", JSON.stringify(responseData, null, 2));
104
128
  strapi.log.info("[Apple Pay] Response status:", responseData.status || responseData.Status);
105
-
129
+
106
130
  if (responseData.errorcode || responseData.ErrorCode) {
107
131
  strapi.log.warn("[Apple Pay] Response contains error:", {
108
132
  errorcode: responseData.errorcode || responseData.ErrorCode,
@@ -135,7 +159,7 @@ const validateApplePayMerchant = async (strapi, params) => {
135
159
  mid: params.mid,
136
160
  portalid: params.portalid
137
161
  }, null, 2));
138
-
162
+
139
163
  const settings = await getSettings(strapi);
140
164
 
141
165
  if (!validateSettings(settings)) {
@@ -155,11 +179,11 @@ const validateApplePayMerchant = async (strapi, params) => {
155
179
  const merchantName = displayName || settings.merchantName || settings.displayName || "Test Store";
156
180
  const merchantId = mid || settings.mid || settings.merchantIdentifier;
157
181
  const portalId = portalid || settings.portalid;
158
-
182
+
159
183
  // Get domain from params or settings or server config
160
- const domainName = domain || settings.domainName ||
161
- (strapi.config.get("server.url") ? new URL(strapi.config.get("server.url")).hostname : null) ||
162
- "localhost";
184
+ const domainName = domain || settings.domainName ||
185
+ (strapi.config.get("server.url") ? new URL(strapi.config.get("server.url")).hostname : null) ||
186
+ "localhost";
163
187
 
164
188
  // For Payone integration without developer account,
165
189
  // Payone handles merchant validation
@@ -174,24 +198,39 @@ const validateApplePayMerchant = async (strapi, params) => {
174
198
  strapi.log.info("[Apple Pay] Initializing session with params:", JSON.stringify(sessionParams, null, 2));
175
199
 
176
200
  // Initialize Apple Pay session with Payone
177
- const sessionResponse = await initializeApplePaySession(strapi, sessionParams);
201
+ let sessionResponse;
202
+ try {
203
+ sessionResponse = await initializeApplePaySession(strapi, sessionParams);
204
+ } catch (error) {
205
+ strapi.log.error("[Apple Pay] Failed to initialize session with Payone:", {
206
+ message: error.message,
207
+ status: error.response?.status,
208
+ data: error.response?.data
209
+ });
210
+ // Return empty object on error - Payment Request API will handle it
211
+ return {};
212
+ }
178
213
 
179
214
  strapi.log.info("[Apple Pay] Session initialization result:", {
180
215
  status: sessionResponse.status || sessionResponse.Status,
181
- hasMerchantIdentifier: !!(sessionResponse.merchantIdentifier || sessionResponse.merchantSessionIdentifier)
216
+ hasMerchantIdentifier: !!(sessionResponse.merchantIdentifier || sessionResponse.merchantSessionIdentifier),
217
+ fullResponse: JSON.stringify(sessionResponse, null, 2)
182
218
  });
183
219
 
184
220
  // If session initialization is successful, return merchant session
185
221
  // Payone will provide the merchant identifier and validation data
186
- if (sessionResponse.status === "APPROVED" || sessionResponse.status === "REDIRECT") {
222
+ // Check for both uppercase and lowercase status
223
+ const responseStatus = sessionResponse.status || sessionResponse.Status;
224
+ if (responseStatus === "APPROVED" || responseStatus === "REDIRECT" ||
225
+ responseStatus === "approved" || responseStatus === "redirect") {
187
226
  strapi.log.info("[Apple Pay] Session approved, creating merchant session object");
188
227
  // Get merchant identifier from Payone response or settings
189
- const merchantIdentifier = sessionResponse.merchantIdentifier ||
190
- sessionResponse.merchantSessionIdentifier ||
191
- settings.merchantIdentifier ||
192
- settings.mid ||
193
- settings.portalid ||
194
- `merchant.${domainName}`;
228
+ const merchantIdentifier = sessionResponse.merchantIdentifier ||
229
+ sessionResponse.merchantSessionIdentifier ||
230
+ settings.merchantIdentifier ||
231
+ settings.mid ||
232
+ settings.portalid ||
233
+ `merchant.${domainName}`;
195
234
 
196
235
  // Return merchant session object
197
236
  // In a real implementation, you would get this from Payone's response
@@ -249,8 +288,8 @@ const parseResponse = (responseData, logger) => {
249
288
  * Generate nonce for merchant session
250
289
  */
251
290
  const generateNonce = () => {
252
- return Math.random().toString(36).substring(2, 15) +
253
- Math.random().toString(36).substring(2, 15);
291
+ return Math.random().toString(36).substring(2, 15) +
292
+ Math.random().toString(36).substring(2, 15);
254
293
  };
255
294
 
256
295
  module.exports = {