strapi-plugin-payone-provider 1.5.2 → 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.
- package/admin/src/pages/App/components/ApplePayButton.js +165 -59
- package/admin/src/pages/App/components/paymentActions/AuthorizationForm.js +24 -7
- package/admin/src/pages/App/components/paymentActions/PreauthorizationForm.js +24 -7
- package/admin/src/pages/hooks/usePaymentActions.js +38 -2
- package/package.json +1 -1
- package/server/controllers/payone.js +19 -1
- package/server/services/applePayService.js +64 -25
|
@@ -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
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
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;
|
|
49
60
|
|
|
50
|
-
console.log("[Apple Pay] Secure context
|
|
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
|
+
});
|
|
69
|
+
|
|
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,
|
|
172
|
-
//
|
|
173
|
-
|
|
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 (
|
|
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
|
-
//
|
|
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 &&
|
|
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
|
-
|
|
514
|
+
// Only call onError if it's defined
|
|
515
|
+
if (typeof onError === 'function') {
|
|
470
516
|
onError(error);
|
|
471
517
|
}
|
|
472
|
-
// Complete with
|
|
473
|
-
|
|
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 (
|
|
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;
|
|
@@ -553,37 +608,85 @@ const ApplePayButton = ({
|
|
|
553
608
|
}
|
|
554
609
|
|
|
555
610
|
// Call the callback with the token BEFORE completing payment
|
|
556
|
-
// This ensures the token is
|
|
611
|
+
// This ensures the token is saved before the dialog closes
|
|
557
612
|
console.log("[Apple Pay] Sending token to callback");
|
|
558
613
|
let callbackSuccess = true;
|
|
614
|
+
let callbackError = null;
|
|
615
|
+
|
|
559
616
|
if (onTokenReceived) {
|
|
560
617
|
try {
|
|
561
|
-
//
|
|
618
|
+
// Call the callback to save the token
|
|
619
|
+
// The callback should set the token in state and return success immediately
|
|
620
|
+
// It should NOT process the payment yet - that will happen when user clicks the button
|
|
562
621
|
const callbackResult = onTokenReceived(tokenString, {
|
|
563
622
|
paymentToken: paymentToken,
|
|
564
623
|
billingContact: response.payerName || response.details?.billingContact,
|
|
565
624
|
shippingContact: response.shippingAddress || response.details?.shippingAddress,
|
|
566
625
|
shippingOption: response.shippingOption || response.details?.shippingOption
|
|
567
626
|
});
|
|
568
|
-
|
|
569
|
-
// If callback returns a promise, wait for it
|
|
627
|
+
|
|
628
|
+
// If callback returns a promise, wait for it to resolve or reject
|
|
570
629
|
if (callbackResult && typeof callbackResult.then === 'function') {
|
|
571
|
-
|
|
630
|
+
try {
|
|
631
|
+
const result = await callbackResult;
|
|
632
|
+
console.log("[Apple Pay] Token callback completed successfully:", result);
|
|
633
|
+
// Check if result indicates success
|
|
634
|
+
if (result && result.success === false) {
|
|
635
|
+
callbackSuccess = false;
|
|
636
|
+
callbackError = new Error(result.message || "Token callback returned failure");
|
|
637
|
+
}
|
|
638
|
+
} catch (error) {
|
|
639
|
+
console.error("[Apple Pay] Token callback promise rejected:", error);
|
|
640
|
+
callbackSuccess = false;
|
|
641
|
+
callbackError = error;
|
|
642
|
+
}
|
|
643
|
+
} else if (callbackResult === false) {
|
|
644
|
+
// If callback explicitly returns false, treat as failure
|
|
645
|
+
callbackSuccess = false;
|
|
646
|
+
console.warn("[Apple Pay] Token callback returned false");
|
|
647
|
+
} else {
|
|
648
|
+
// If callback returns a value (not a promise), assume success
|
|
649
|
+
console.log("[Apple Pay] Token callback returned synchronously");
|
|
572
650
|
}
|
|
573
|
-
} catch (
|
|
574
|
-
console.error("[Apple Pay] Error in token callback:",
|
|
651
|
+
} catch (error) {
|
|
652
|
+
console.error("[Apple Pay] Error in token callback:", error);
|
|
575
653
|
callbackSuccess = false;
|
|
654
|
+
callbackError = error;
|
|
576
655
|
}
|
|
656
|
+
} else {
|
|
657
|
+
console.warn("[Apple Pay] No onTokenReceived callback provided");
|
|
658
|
+
// If no callback, we should still complete the payment
|
|
659
|
+
// But mark as success since we can't determine the result
|
|
660
|
+
callbackSuccess = true;
|
|
577
661
|
}
|
|
578
662
|
|
|
579
663
|
// Complete payment with success or fail based on callback result
|
|
664
|
+
// IMPORTANT: Only call complete() after the callback has fully finished
|
|
580
665
|
console.log("[Apple Pay] Completing payment with status:", callbackSuccess ? "success" : "fail");
|
|
666
|
+
|
|
581
667
|
try {
|
|
582
|
-
|
|
583
|
-
|
|
668
|
+
// Use a small delay to ensure state updates are complete
|
|
669
|
+
// This prevents the dialog from closing before the token is saved
|
|
670
|
+
await new Promise(resolve => setTimeout(resolve, 200));
|
|
671
|
+
|
|
672
|
+
const completionStatus = callbackSuccess ? "success" : "fail";
|
|
673
|
+
await response.complete(completionStatus);
|
|
674
|
+
console.log("[Apple Pay] Payment completed with status:", completionStatus);
|
|
675
|
+
|
|
676
|
+
// If there was an error, notify the error handler
|
|
677
|
+
if (!callbackSuccess && callbackError && onError) {
|
|
678
|
+
onError(callbackError);
|
|
679
|
+
}
|
|
584
680
|
} catch (completeError) {
|
|
585
681
|
console.error("[Apple Pay] Error completing payment:", completeError);
|
|
586
|
-
if
|
|
682
|
+
// Try to complete with fail status if there's an error
|
|
683
|
+
try {
|
|
684
|
+
await response.complete("fail");
|
|
685
|
+
} catch (finalError) {
|
|
686
|
+
console.error("[Apple Pay] Failed to complete payment even with fail status:", finalError);
|
|
687
|
+
}
|
|
688
|
+
// Only call onError if it's defined
|
|
689
|
+
if (typeof onError === 'function') {
|
|
587
690
|
onError(completeError);
|
|
588
691
|
}
|
|
589
692
|
}
|
|
@@ -594,7 +697,14 @@ const ApplePayButton = ({
|
|
|
594
697
|
stack: error.stack,
|
|
595
698
|
name: error.name
|
|
596
699
|
});
|
|
597
|
-
if (
|
|
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') {
|
|
598
708
|
onError(error);
|
|
599
709
|
}
|
|
600
710
|
}
|
|
@@ -613,43 +723,45 @@ const ApplePayButton = ({
|
|
|
613
723
|
});
|
|
614
724
|
|
|
615
725
|
try {
|
|
616
|
-
|
|
617
|
-
|
|
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`, {
|
|
618
731
|
method: 'POST',
|
|
619
|
-
|
|
620
|
-
'Content-Type': 'application/json',
|
|
621
|
-
},
|
|
622
|
-
body: JSON.stringify({
|
|
732
|
+
body: {
|
|
623
733
|
validationURL,
|
|
624
734
|
...config
|
|
625
|
-
}
|
|
735
|
+
}
|
|
626
736
|
});
|
|
627
737
|
|
|
628
|
-
console.log("[Apple Pay]
|
|
738
|
+
console.log("[Apple Pay] Merchant session received from backend:", {
|
|
739
|
+
hasData: !!merchantSession.data,
|
|
740
|
+
merchantIdentifier: merchantSession.data?.merchantIdentifier
|
|
741
|
+
});
|
|
629
742
|
|
|
630
|
-
|
|
631
|
-
const merchantSession = await response.json();
|
|
632
|
-
console.log("[Apple Pay] Merchant session received from backend:", {
|
|
633
|
-
hasData: !!merchantSession.data,
|
|
634
|
-
merchantIdentifier: merchantSession.data?.merchantIdentifier
|
|
635
|
-
});
|
|
636
|
-
return merchantSession.data || merchantSession;
|
|
637
|
-
} else {
|
|
638
|
-
// If validation fails, return empty object - Payone will handle it
|
|
639
|
-
const errorText = await response.text();
|
|
640
|
-
console.warn("[Apple Pay] Merchant validation failed:", {
|
|
641
|
-
status: response.status,
|
|
642
|
-
statusText: response.statusText,
|
|
643
|
-
error: errorText
|
|
644
|
-
});
|
|
645
|
-
return {};
|
|
646
|
-
}
|
|
743
|
+
return merchantSession.data || merchantSession;
|
|
647
744
|
} catch (error) {
|
|
648
745
|
console.error("[Apple Pay] Merchant validation error:", {
|
|
649
746
|
message: error.message,
|
|
747
|
+
status: error.response?.status,
|
|
748
|
+
statusText: error.response?.statusText,
|
|
749
|
+
data: error.response?.data,
|
|
650
750
|
stack: error.stack
|
|
651
751
|
});
|
|
652
|
-
|
|
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
|
|
653
765
|
return {};
|
|
654
766
|
}
|
|
655
767
|
};
|
|
@@ -673,12 +785,6 @@ const ApplePayButton = ({
|
|
|
673
785
|
)}
|
|
674
786
|
{!isLoading && isAvailable && (
|
|
675
787
|
<>
|
|
676
|
-
<Typography variant="sigma" textColor="neutral700" fontWeight="semiBold" style={{ textAlign: "left" }}>
|
|
677
|
-
Apple Pay Payment
|
|
678
|
-
</Typography>
|
|
679
|
-
<Typography variant="pi" textColor="neutral600" style={{ textAlign: "left" }}>
|
|
680
|
-
Click the button below to pay with Apple Pay. The token will be automatically sent to Payone.
|
|
681
|
-
</Typography>
|
|
682
788
|
<Box ref={buttonContainerRef} style={{ minHeight: "40px", width: "100%", display: "flex", justifyContent: "flex-start" }}>
|
|
683
789
|
{typeof window !== 'undefined' && window.customElements && window.customElements.get('apple-pay-button') ? (
|
|
684
790
|
<apple-pay-button
|
|
@@ -44,16 +44,33 @@ const AuthorizationForm = ({
|
|
|
44
44
|
|
|
45
45
|
const handleApplePayToken = async (token, paymentData) => {
|
|
46
46
|
if (!token) {
|
|
47
|
+
console.error("[Apple Pay] Token is missing in handleApplePayToken");
|
|
47
48
|
return Promise.reject(new Error("Token is missing"));
|
|
48
49
|
}
|
|
50
|
+
|
|
51
|
+
console.log("[Apple Pay] handleApplePayToken called with token:", {
|
|
52
|
+
hasToken: !!token,
|
|
53
|
+
tokenLength: token?.length,
|
|
54
|
+
paymentData: !!paymentData
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
// IMPORTANT: Set token in state immediately (synchronously)
|
|
58
|
+
// This ensures the token is saved before the dialog closes
|
|
49
59
|
setApplePayToken(token);
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
60
|
+
|
|
61
|
+
console.log("[Apple Pay] Token saved to state successfully");
|
|
62
|
+
|
|
63
|
+
// Don't call onAuthorization immediately
|
|
64
|
+
// Let the user manually trigger the payment using the button
|
|
65
|
+
// This prevents the dialog from closing prematurely if there's an error
|
|
66
|
+
// The dialog will close with success, and the user will see the "Process Authorization" button
|
|
67
|
+
|
|
68
|
+
// Return success immediately so the dialog closes properly
|
|
69
|
+
// The actual payment processing will happen when the user clicks the button
|
|
70
|
+
return Promise.resolve({
|
|
71
|
+
success: true,
|
|
72
|
+
message: "Token received successfully. Please click 'Process Authorization' to complete the payment."
|
|
73
|
+
});
|
|
57
74
|
};
|
|
58
75
|
|
|
59
76
|
const handleApplePayError = (error) => {
|
|
@@ -44,16 +44,33 @@ const PreauthorizationForm = ({
|
|
|
44
44
|
|
|
45
45
|
const handleApplePayToken = async (token, paymentData) => {
|
|
46
46
|
if (!token) {
|
|
47
|
+
console.error("[Apple Pay] Token is missing in handleApplePayToken");
|
|
47
48
|
return Promise.reject(new Error("Token is missing"));
|
|
48
49
|
}
|
|
50
|
+
|
|
51
|
+
console.log("[Apple Pay] handleApplePayToken called with token:", {
|
|
52
|
+
hasToken: !!token,
|
|
53
|
+
tokenLength: token?.length,
|
|
54
|
+
paymentData: !!paymentData
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
// IMPORTANT: Set token in state immediately (synchronously)
|
|
58
|
+
// This ensures the token is saved before the dialog closes
|
|
49
59
|
setApplePayToken(token);
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
60
|
+
|
|
61
|
+
console.log("[Apple Pay] Token saved to state successfully");
|
|
62
|
+
|
|
63
|
+
// Don't call onPreauthorization immediately
|
|
64
|
+
// Let the user manually trigger the payment using the button
|
|
65
|
+
// This prevents the dialog from closing prematurely if there's an error
|
|
66
|
+
// The dialog will close with success, and the user will see the "Process Preauthorization" button
|
|
67
|
+
|
|
68
|
+
// Return success immediately so the dialog closes properly
|
|
69
|
+
// The actual payment processing will happen when the user clicks the button
|
|
70
|
+
return Promise.resolve({
|
|
71
|
+
success: true,
|
|
72
|
+
message: "Token received successfully. Please click 'Process Preauthorization' to complete the payment."
|
|
73
|
+
});
|
|
57
74
|
};
|
|
58
75
|
|
|
59
76
|
const handleApplePayError = (error) => {
|
|
@@ -83,6 +83,12 @@ const usePaymentActions = () => {
|
|
|
83
83
|
};
|
|
84
84
|
|
|
85
85
|
const handlePreauthorization = async (tokenParam = null) => {
|
|
86
|
+
console.log("[Payment] handlePreauthorization called", {
|
|
87
|
+
hasToken: !!tokenParam,
|
|
88
|
+
paymentMethod,
|
|
89
|
+
amount: paymentAmount
|
|
90
|
+
});
|
|
91
|
+
|
|
86
92
|
setIsProcessingPayment(true);
|
|
87
93
|
setPaymentError(null);
|
|
88
94
|
setPaymentResult(null);
|
|
@@ -211,16 +217,31 @@ const usePaymentActions = () => {
|
|
|
211
217
|
|
|
212
218
|
setPaymentResult(responseData);
|
|
213
219
|
|
|
220
|
+
console.log("[Payment] Preauthorization result:", {
|
|
221
|
+
status,
|
|
222
|
+
hasError: !!errorCode,
|
|
223
|
+
errorCode,
|
|
224
|
+
errorMessage
|
|
225
|
+
});
|
|
226
|
+
|
|
214
227
|
if (status === "APPROVED") {
|
|
215
228
|
handlePaymentSuccess("Preauthorization completed successfully");
|
|
229
|
+
// Return success result for Apple Pay callback
|
|
230
|
+
return { success: true, data: responseData };
|
|
216
231
|
} else {
|
|
232
|
+
const errorMsg = errorMessage || `Unexpected status: ${status}`;
|
|
217
233
|
handlePaymentError(
|
|
218
|
-
{ message:
|
|
234
|
+
{ message: errorMsg },
|
|
219
235
|
`Preauthorization completed with status: ${status}`
|
|
220
236
|
);
|
|
237
|
+
// Return error result for Apple Pay callback
|
|
238
|
+
throw new Error(errorMsg);
|
|
221
239
|
}
|
|
222
240
|
} catch (error) {
|
|
241
|
+
console.error("[Payment] Preauthorization error:", error);
|
|
223
242
|
handlePaymentError(error, "Preauthorization failed");
|
|
243
|
+
// Re-throw error so Apple Pay callback knows it failed
|
|
244
|
+
throw error;
|
|
224
245
|
} finally {
|
|
225
246
|
setIsProcessingPayment(false);
|
|
226
247
|
}
|
|
@@ -356,16 +377,31 @@ const usePaymentActions = () => {
|
|
|
356
377
|
|
|
357
378
|
setPaymentResult(responseData);
|
|
358
379
|
|
|
380
|
+
console.log("[Payment] Authorization result:", {
|
|
381
|
+
status,
|
|
382
|
+
hasError: !!errorCode,
|
|
383
|
+
errorCode,
|
|
384
|
+
errorMessage
|
|
385
|
+
});
|
|
386
|
+
|
|
359
387
|
if (status === "APPROVED") {
|
|
360
388
|
handlePaymentSuccess("Authorization completed successfully");
|
|
389
|
+
// Return success result for Apple Pay callback
|
|
390
|
+
return { success: true, data: responseData };
|
|
361
391
|
} else {
|
|
392
|
+
const errorMsg = errorMessage || `Unexpected status: ${status}`;
|
|
362
393
|
handlePaymentError(
|
|
363
|
-
{ message:
|
|
394
|
+
{ message: errorMsg },
|
|
364
395
|
`Authorization completed with status: ${status}`
|
|
365
396
|
);
|
|
397
|
+
// Return error result for Apple Pay callback
|
|
398
|
+
throw new Error(errorMsg);
|
|
366
399
|
}
|
|
367
400
|
} catch (error) {
|
|
401
|
+
console.error("[Payment] Authorization error:", error);
|
|
368
402
|
handlePaymentError(error, "Authorization failed");
|
|
403
|
+
// Re-throw error so Apple Pay callback knows it failed
|
|
404
|
+
throw error;
|
|
369
405
|
} finally {
|
|
370
406
|
setIsProcessingPayment(false);
|
|
371
407
|
}
|
package/package.json
CHANGED
|
@@ -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
|
-
|
|
60
|
-
|
|
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
|
-
|
|
91
|
-
|
|
92
|
-
|
|
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
|
-
|
|
162
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
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
|
-
|
|
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 = {
|