strapi-plugin-payone-provider 1.4.2 → 1.5.2
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 +179 -22
- package/admin/src/pages/App/components/AppHeader.js +22 -4
- package/admin/src/pages/App/components/AppTabs.js +25 -1
- package/admin/src/pages/App/components/ApplePayButton.js +737 -0
- package/admin/src/pages/App/components/ApplePayConfig.js +364 -0
- package/admin/src/pages/App/components/ApplePayConfigPanel.js +81 -0
- package/admin/src/pages/App/components/ConfigurationPanel.js +19 -3
- package/admin/src/pages/App/components/DocsPanel.js +1057 -0
- package/admin/src/pages/App/components/GooglePayConfig.js +217 -0
- package/admin/src/pages/App/components/GooglePayConfigPanel.js +82 -0
- package/admin/src/pages/App/components/GooglePaybutton.js +1 -1
- package/admin/src/pages/App/components/PaymentActionsPanel.js +24 -6
- package/admin/src/pages/App/components/paymentActions/AuthorizationForm.js +60 -4
- package/admin/src/pages/App/components/paymentActions/CaptureForm.js +1 -0
- package/admin/src/pages/App/components/paymentActions/CardDetailsInput.js +18 -16
- package/admin/src/pages/App/components/paymentActions/PaymentMethodSelector.js +106 -2
- package/admin/src/pages/App/components/paymentActions/PreauthorizationForm.js +64 -4
- package/admin/src/pages/App/components/paymentActions/RefundForm.js +1 -0
- package/admin/src/pages/App/index.js +70 -1
- package/admin/src/pages/hooks/usePaymentActions.js +13 -2
- package/admin/src/pages/hooks/useSettings.js +2 -0
- package/admin/src/pages/utils/applePayConstants.js +222 -0
- package/admin/src/pages/utils/googlePayConstants.js +79 -0
- package/admin/src/pages/utils/paymentUtils.js +22 -74
- package/package.json +1 -1
- package/server/bootstrap.js +5 -1
- package/server/config/index.js +5 -1
- package/server/controllers/payone.js +10 -0
- package/server/routes/index.js +17 -0
- package/server/services/applePayService.js +261 -0
- package/server/services/payone.js +10 -0
- package/server/utils/paymentMethodParams.js +19 -2
|
@@ -0,0 +1,737 @@
|
|
|
1
|
+
import React, { useEffect, useRef, useState } from "react";
|
|
2
|
+
import { Box, Flex, Typography } from "@strapi/design-system";
|
|
3
|
+
import { DEFAULT_APPLE_PAY_CONFIG } from "../../utils/applePayConstants";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Apple Pay Button Component using Payment Request API
|
|
7
|
+
* Based on Apple Pay documentation:
|
|
8
|
+
* https://developer.apple.com/documentation/applepayontheweb/creating-an-apple-pay-session
|
|
9
|
+
* https://developer.apple.com/documentation/applepayontheweb/displaying-apple-pay-buttons-using-css
|
|
10
|
+
*
|
|
11
|
+
* Supports Payment Request API (works in Chrome, Edge, Safari, etc.)
|
|
12
|
+
* and falls back to Apple Pay JS API if needed
|
|
13
|
+
*/
|
|
14
|
+
const ApplePayButton = ({
|
|
15
|
+
amount,
|
|
16
|
+
currency = "EUR",
|
|
17
|
+
countryCode = "DE",
|
|
18
|
+
merchantCapabilities = ["supports3DS"],
|
|
19
|
+
supportedNetworks = ["visa", "masterCard", "girocard"],
|
|
20
|
+
buttonStyle = "black",
|
|
21
|
+
buttonType = "pay",
|
|
22
|
+
requestPayerName = false,
|
|
23
|
+
requestBillingAddress = false,
|
|
24
|
+
requestPayerEmail = false,
|
|
25
|
+
requestPayerPhone = false,
|
|
26
|
+
requestShipping = false,
|
|
27
|
+
shippingType = "shipping",
|
|
28
|
+
merchantIdentifier = null,
|
|
29
|
+
onTokenReceived,
|
|
30
|
+
onError,
|
|
31
|
+
settings
|
|
32
|
+
}) => {
|
|
33
|
+
const [isReady, setIsReady] = useState(false);
|
|
34
|
+
const [isLoading, setIsLoading] = useState(true);
|
|
35
|
+
const [isAvailable, setIsAvailable] = useState(false);
|
|
36
|
+
const [errorMessage, setErrorMessage] = useState(null);
|
|
37
|
+
const buttonContainerRef = useRef(null);
|
|
38
|
+
const paymentRequestRef = useRef(null);
|
|
39
|
+
|
|
40
|
+
const checkApplePayAvailability = async () => {
|
|
41
|
+
try {
|
|
42
|
+
console.log("[Apple Pay] Checking availability...");
|
|
43
|
+
|
|
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');
|
|
49
|
+
|
|
50
|
+
console.log("[Apple Pay] Secure context:", isSecure, "Protocol:", window.location?.protocol);
|
|
51
|
+
|
|
52
|
+
// First, check if Payment Request API is available
|
|
53
|
+
// Payment Request API works on HTTP too, but Apple Pay JS API requires HTTPS
|
|
54
|
+
if (typeof window === 'undefined' || !window.PaymentRequest) {
|
|
55
|
+
console.log("[Apple Pay] Payment Request API not available");
|
|
56
|
+
|
|
57
|
+
// Fallback: Check Apple Pay JS API (for Safari, requires HTTPS)
|
|
58
|
+
if (typeof window !== 'undefined' && window.ApplePaySession && isSecure) {
|
|
59
|
+
try {
|
|
60
|
+
const canMakePayments = ApplePaySession.canMakePayments();
|
|
61
|
+
console.log("[Apple Pay] Apple Pay JS API available:", canMakePayments);
|
|
62
|
+
return { available: canMakePayments, method: 'applePayJS' };
|
|
63
|
+
} catch (error) {
|
|
64
|
+
console.error("[Apple Pay] Apple Pay JS API error (likely insecure context):", error.message);
|
|
65
|
+
return { available: false, method: null, error: 'insecure_context' };
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (!isSecure && typeof window !== 'undefined' && window.ApplePaySession) {
|
|
70
|
+
console.warn("[Apple Pay] Apple Pay JS API requires HTTPS. Using Payment Request API fallback.");
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
console.log("[Apple Pay] No Apple Pay support found");
|
|
74
|
+
return { available: false, method: null };
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
console.log("[Apple Pay] Payment Request API available, checking Apple Pay support...");
|
|
78
|
+
|
|
79
|
+
// Check if Apple Pay payment method is supported
|
|
80
|
+
const applePayMethod = {
|
|
81
|
+
supportedMethods: "https://apple.com/apple-pay",
|
|
82
|
+
data: {
|
|
83
|
+
version: 3,
|
|
84
|
+
merchantIdentifier: merchantIdentifier || settings?.merchantIdentifier || settings?.mid || "merchant.com.payone.test",
|
|
85
|
+
merchantCapabilities: merchantCapabilities,
|
|
86
|
+
supportedNetworks: supportedNetworks,
|
|
87
|
+
countryCode: countryCode
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
// Create a test payment request
|
|
92
|
+
// Payment Request API works on HTTP too, but Apple Pay may require HTTPS
|
|
93
|
+
let testRequest;
|
|
94
|
+
try {
|
|
95
|
+
testRequest = new PaymentRequest(
|
|
96
|
+
[applePayMethod],
|
|
97
|
+
{
|
|
98
|
+
total: {
|
|
99
|
+
label: "Test",
|
|
100
|
+
amount: { value: "0.01", currency: currency }
|
|
101
|
+
}
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
requestPayerName: false,
|
|
105
|
+
requestBillingAddress: false,
|
|
106
|
+
requestPayerEmail: false,
|
|
107
|
+
requestPayerPhone: false,
|
|
108
|
+
requestShipping: false
|
|
109
|
+
}
|
|
110
|
+
);
|
|
111
|
+
} catch (e) {
|
|
112
|
+
console.error("[Apple Pay] Error creating PaymentRequest:", e);
|
|
113
|
+
// If PaymentRequest creation fails, it might be due to insecure context
|
|
114
|
+
if (e.message && e.message.includes('insecure')) {
|
|
115
|
+
return { available: false, method: null, error: 'insecure_context' };
|
|
116
|
+
}
|
|
117
|
+
return { available: false, method: null };
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Check if can make payment
|
|
121
|
+
if (testRequest.canMakePayment) {
|
|
122
|
+
try {
|
|
123
|
+
const canPay = await testRequest.canMakePayment();
|
|
124
|
+
console.log("[Apple Pay] canMakePayment result:", canPay);
|
|
125
|
+
|
|
126
|
+
if (canPay) {
|
|
127
|
+
return { available: true, method: 'paymentRequest' };
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// If PaymentRequest says no, try Apple Pay JS API as fallback (only on HTTPS)
|
|
131
|
+
if (typeof window !== 'undefined' && window.ApplePaySession && isSecure) {
|
|
132
|
+
try {
|
|
133
|
+
const canMakePaymentsJS = ApplePaySession.canMakePayments();
|
|
134
|
+
console.log("[Apple Pay] Fallback to Apple Pay JS API:", canMakePaymentsJS);
|
|
135
|
+
return { available: canMakePaymentsJS, method: 'applePayJS' };
|
|
136
|
+
} catch (e) {
|
|
137
|
+
console.error("[Apple Pay] Apple Pay JS API error:", e.message);
|
|
138
|
+
// If it's insecure context error, Payment Request API might still work
|
|
139
|
+
if (e.message && e.message.includes('insecure')) {
|
|
140
|
+
// Payment Request API might work even on HTTP
|
|
141
|
+
return { available: false, method: null, error: 'insecure_context' };
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return { available: false, method: null };
|
|
147
|
+
} catch (e) {
|
|
148
|
+
console.error("[Apple Pay] Error checking canMakePayment:", e);
|
|
149
|
+
|
|
150
|
+
// If it's insecure context error, we can't use Apple Pay JS API
|
|
151
|
+
if (e.message && e.message.includes('insecure')) {
|
|
152
|
+
console.warn("[Apple Pay] Insecure context detected. Apple Pay requires HTTPS.");
|
|
153
|
+
return { available: false, method: null, error: 'insecure_context' };
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// For other errors, try Apple Pay JS API as fallback (only on HTTPS)
|
|
157
|
+
if (typeof window !== 'undefined' && window.ApplePaySession && isSecure) {
|
|
158
|
+
try {
|
|
159
|
+
const canMakePaymentsJS = ApplePaySession.canMakePayments();
|
|
160
|
+
return { available: canMakePaymentsJS, method: 'applePayJS' };
|
|
161
|
+
} catch (jsError) {
|
|
162
|
+
console.error("[Apple Pay] Apple Pay JS API error:", jsError.message);
|
|
163
|
+
return { available: false, method: null };
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
return { available: false, method: null };
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
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
|
|
174
|
+
|
|
175
|
+
if (isSecure) {
|
|
176
|
+
console.log("[Apple Pay] canMakePayment not available, assuming support (secure context)");
|
|
177
|
+
return { available: true, method: 'paymentRequest' };
|
|
178
|
+
} else {
|
|
179
|
+
console.warn("[Apple Pay] canMakePayment not available and insecure context");
|
|
180
|
+
return { available: false, method: null, error: 'insecure_context' };
|
|
181
|
+
}
|
|
182
|
+
} catch (error) {
|
|
183
|
+
console.error("[Apple Pay] Error checking availability:", error);
|
|
184
|
+
|
|
185
|
+
// Check if it's insecure context error
|
|
186
|
+
if (error.message && error.message.includes('insecure')) {
|
|
187
|
+
console.warn("[Apple Pay] Insecure context - Apple Pay requires HTTPS");
|
|
188
|
+
return { available: false, method: null, error: 'insecure_context' };
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Fallback: Try Apple Pay JS API (only on HTTPS)
|
|
192
|
+
// Reuse isSecure from line 47
|
|
193
|
+
|
|
194
|
+
if (typeof window !== 'undefined' && window.ApplePaySession && isSecure) {
|
|
195
|
+
try {
|
|
196
|
+
const canMakePayments = ApplePaySession.canMakePayments();
|
|
197
|
+
return { available: canMakePayments, method: 'applePayJS' };
|
|
198
|
+
} catch (e) {
|
|
199
|
+
console.error("[Apple Pay] Apple Pay JS API error:", e.message);
|
|
200
|
+
if (e.message && e.message.includes('insecure')) {
|
|
201
|
+
return { available: false, method: null, error: 'insecure_context' };
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
return { available: false, method: null };
|
|
207
|
+
}
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
useEffect(() => {
|
|
211
|
+
const scriptUrl = "https://applepay.cdn-apple.com/jsapi/1.latest/apple-pay-sdk.js";
|
|
212
|
+
|
|
213
|
+
console.log("[Apple Pay] Loading Apple Pay SDK script...");
|
|
214
|
+
|
|
215
|
+
if (document.querySelector(`script[src="${scriptUrl}"]`)) {
|
|
216
|
+
console.log("[Apple Pay] Script already loaded");
|
|
217
|
+
// Script already loaded, check if it's ready
|
|
218
|
+
if (typeof window !== 'undefined' && window.ApplePaySession) {
|
|
219
|
+
initializeButton();
|
|
220
|
+
} else {
|
|
221
|
+
// Wait a bit for script to initialize
|
|
222
|
+
setTimeout(() => initializeButton(), 500);
|
|
223
|
+
}
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
const script = document.createElement("script");
|
|
228
|
+
script.src = scriptUrl;
|
|
229
|
+
script.crossOrigin = "anonymous";
|
|
230
|
+
script.async = true;
|
|
231
|
+
|
|
232
|
+
script.onload = () => {
|
|
233
|
+
console.log("[Apple Pay] SDK script loaded successfully");
|
|
234
|
+
setTimeout(() => {
|
|
235
|
+
initializeButton();
|
|
236
|
+
}, 500);
|
|
237
|
+
};
|
|
238
|
+
|
|
239
|
+
script.onerror = (error) => {
|
|
240
|
+
console.error("[Apple Pay] Failed to load SDK script:", error);
|
|
241
|
+
setIsLoading(false);
|
|
242
|
+
setIsAvailable(false);
|
|
243
|
+
setErrorMessage("Failed to load Apple Pay SDK. Please check Content Security Policy settings.");
|
|
244
|
+
|
|
245
|
+
// Even if script fails, try to use Payment Request API
|
|
246
|
+
console.log("[Apple Pay] Trying Payment Request API as fallback...");
|
|
247
|
+
setTimeout(() => {
|
|
248
|
+
initializeButton();
|
|
249
|
+
}, 1000);
|
|
250
|
+
};
|
|
251
|
+
|
|
252
|
+
try {
|
|
253
|
+
document.head.appendChild(script);
|
|
254
|
+
console.log("[Apple Pay] Script element added to head");
|
|
255
|
+
} catch (error) {
|
|
256
|
+
console.error("[Apple Pay] Error adding script to head:", error);
|
|
257
|
+
// Try to use Payment Request API without SDK
|
|
258
|
+
setTimeout(() => {
|
|
259
|
+
initializeButton();
|
|
260
|
+
}, 1000);
|
|
261
|
+
}
|
|
262
|
+
}, []);
|
|
263
|
+
|
|
264
|
+
const initializeButton = async () => {
|
|
265
|
+
try {
|
|
266
|
+
console.log("[Apple Pay] Initializing button...");
|
|
267
|
+
|
|
268
|
+
const isSecure = typeof window !== 'undefined' &&
|
|
269
|
+
(window.location.protocol === 'https:' ||
|
|
270
|
+
window.location.hostname === 'localhost' ||
|
|
271
|
+
window.location.hostname === '127.0.0.1');
|
|
272
|
+
|
|
273
|
+
console.log("[Apple Pay] Secure context check:", {
|
|
274
|
+
protocol: window.location?.protocol,
|
|
275
|
+
hostname: window.location?.hostname,
|
|
276
|
+
isSecure: isSecure
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
const isLocalhost = typeof window !== 'undefined' &&
|
|
280
|
+
(window.location.hostname === 'localhost' ||
|
|
281
|
+
window.location.hostname === '127.0.0.1');
|
|
282
|
+
|
|
283
|
+
if (!isSecure && !isLocalhost && window.location?.protocol === 'http:') {
|
|
284
|
+
const errorMsg = "Apple Pay requires HTTPS. Please access this page via HTTPS (https://yourdomain.com) instead of HTTP. Localhost (http://localhost) is allowed for development.";
|
|
285
|
+
setErrorMessage(errorMsg);
|
|
286
|
+
setIsLoading(false);
|
|
287
|
+
setIsAvailable(false);
|
|
288
|
+
console.warn("[Apple Pay]", errorMsg);
|
|
289
|
+
console.warn("[Apple Pay] Current URL:", window.location.href);
|
|
290
|
+
return;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// Log context information
|
|
294
|
+
console.log("[Apple Pay] Context info:", {
|
|
295
|
+
protocol: window.location?.protocol,
|
|
296
|
+
hostname: window.location?.hostname,
|
|
297
|
+
isSecure: isSecure,
|
|
298
|
+
isLocalhost: isLocalhost,
|
|
299
|
+
fullUrl: window.location?.href
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
// Check availability
|
|
303
|
+
const availability = await checkApplePayAvailability();
|
|
304
|
+
console.log("[Apple Pay] Availability check result:", availability);
|
|
305
|
+
|
|
306
|
+
setIsAvailable(availability.available);
|
|
307
|
+
|
|
308
|
+
if (!availability.available) {
|
|
309
|
+
let errorMsg = "Apple Pay is not available on this device or browser.";
|
|
310
|
+
|
|
311
|
+
if (isLocalhost) {
|
|
312
|
+
errorMsg = "Apple Pay is not available on localhost. Apple Pay requires a registered domain with HTTPS. " +
|
|
313
|
+
"For testing, please use a production domain with HTTPS or test on a device with Safari (iOS/macOS). " +
|
|
314
|
+
"You can still test the payment flow by using the 'Process Preauthorization' button without Apple Pay token.";
|
|
315
|
+
} else if (availability.error === 'insecure_context') {
|
|
316
|
+
errorMsg = "Apple Pay requires HTTPS. Please access this page via HTTPS (https://yourdomain.com) instead of HTTP.";
|
|
317
|
+
} else if (typeof window !== 'undefined' && window.PaymentRequest) {
|
|
318
|
+
errorMsg += " Payment Request API is available but Apple Pay is not supported. " +
|
|
319
|
+
"Please use Safari on iOS, macOS, or iPadOS, or Chrome/Edge on supported devices. " +
|
|
320
|
+
"Note: Apple Pay requires a registered domain and cannot work on localhost.";
|
|
321
|
+
} else if (typeof window !== 'undefined' && window.ApplePaySession) {
|
|
322
|
+
errorMsg += " Apple Pay JS API is available but cannot make payments. " +
|
|
323
|
+
"Please check your device settings and ensure you have a card added to Wallet.";
|
|
324
|
+
} else {
|
|
325
|
+
errorMsg += " Please use Safari on iOS, macOS, or iPadOS, or a browser that supports Payment Request API (Chrome, Edge, Safari).";
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
setErrorMessage(errorMsg);
|
|
329
|
+
setIsLoading(false);
|
|
330
|
+
console.warn("[Apple Pay] Not available:", errorMsg);
|
|
331
|
+
return;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
console.log("[Apple Pay] Button initialized successfully, method:", availability.method);
|
|
335
|
+
setIsReady(true);
|
|
336
|
+
setIsLoading(false);
|
|
337
|
+
} catch (error) {
|
|
338
|
+
console.error("[Apple Pay] Initialization error:", error);
|
|
339
|
+
setIsLoading(false);
|
|
340
|
+
setIsAvailable(false);
|
|
341
|
+
|
|
342
|
+
// Check for insecure context error
|
|
343
|
+
if (error.message && error.message.includes('insecure')) {
|
|
344
|
+
setErrorMessage("Apple Pay requires HTTPS. Please access this page via HTTPS (https://yourdomain.com) instead of HTTP. Localhost is allowed for development.");
|
|
345
|
+
} else {
|
|
346
|
+
setErrorMessage(error.message || "Failed to initialize Apple Pay");
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
if (onError) {
|
|
350
|
+
onError(error);
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
};
|
|
354
|
+
|
|
355
|
+
const handleApplePayClick = async () => {
|
|
356
|
+
console.log("[Apple Pay] Button clicked, checking readiness...", {
|
|
357
|
+
isReady,
|
|
358
|
+
isAvailable,
|
|
359
|
+
amount,
|
|
360
|
+
currency,
|
|
361
|
+
countryCode,
|
|
362
|
+
protocol: window.location?.protocol,
|
|
363
|
+
hostname: window.location?.hostname
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
// Check HTTPS requirement
|
|
367
|
+
const isSecure = typeof window !== 'undefined' &&
|
|
368
|
+
(window.location.protocol === 'https:' ||
|
|
369
|
+
window.location.hostname === 'localhost' ||
|
|
370
|
+
window.location.hostname === '127.0.0.1');
|
|
371
|
+
|
|
372
|
+
if (!isSecure && window.location?.protocol === 'http:') {
|
|
373
|
+
const errorMsg = "Apple Pay requires HTTPS. Please access this page via HTTPS.";
|
|
374
|
+
console.error("[Apple Pay]", errorMsg);
|
|
375
|
+
if (onError) {
|
|
376
|
+
onError(new Error(errorMsg));
|
|
377
|
+
}
|
|
378
|
+
return;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
if (!isReady || !isAvailable) {
|
|
382
|
+
console.error("[Apple Pay] Not ready or not available");
|
|
383
|
+
if (onError) {
|
|
384
|
+
onError(new Error("Apple Pay is not ready"));
|
|
385
|
+
}
|
|
386
|
+
return;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
try {
|
|
390
|
+
const amountValue = (parseFloat(amount) / 100).toFixed(2);
|
|
391
|
+
const gatewayMerchantId = settings?.mid || settings?.portalid || '';
|
|
392
|
+
const merchantId = merchantIdentifier || gatewayMerchantId || "merchant.com.payone.test";
|
|
393
|
+
|
|
394
|
+
console.log("[Apple Pay] Starting payment request:", {
|
|
395
|
+
amount: amountValue,
|
|
396
|
+
currency,
|
|
397
|
+
merchantId,
|
|
398
|
+
countryCode,
|
|
399
|
+
supportedNetworks,
|
|
400
|
+
merchantCapabilities
|
|
401
|
+
});
|
|
402
|
+
|
|
403
|
+
// Define PaymentMethodData for Apple Pay
|
|
404
|
+
const paymentMethodData = [{
|
|
405
|
+
supportedMethods: "https://apple.com/apple-pay",
|
|
406
|
+
data: {
|
|
407
|
+
version: 3,
|
|
408
|
+
merchantIdentifier: merchantId,
|
|
409
|
+
merchantCapabilities: merchantCapabilities,
|
|
410
|
+
supportedNetworks: supportedNetworks,
|
|
411
|
+
countryCode: countryCode,
|
|
412
|
+
currencyCode: currency
|
|
413
|
+
}
|
|
414
|
+
}];
|
|
415
|
+
|
|
416
|
+
// Define PaymentDetails
|
|
417
|
+
const paymentDetails = {
|
|
418
|
+
total: {
|
|
419
|
+
label: settings?.merchantName || "Demo Payment",
|
|
420
|
+
amount: {
|
|
421
|
+
value: amountValue,
|
|
422
|
+
currency: currency
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
};
|
|
426
|
+
|
|
427
|
+
// Define PaymentOptions
|
|
428
|
+
const paymentOptions = {
|
|
429
|
+
requestPayerName: requestPayerName,
|
|
430
|
+
requestBillingAddress: requestBillingAddress,
|
|
431
|
+
requestPayerEmail: requestPayerEmail,
|
|
432
|
+
requestPayerPhone: requestPayerPhone,
|
|
433
|
+
requestShipping: requestShipping,
|
|
434
|
+
shippingType: shippingType
|
|
435
|
+
};
|
|
436
|
+
|
|
437
|
+
// Create PaymentRequest
|
|
438
|
+
const request = new PaymentRequest(paymentMethodData, paymentDetails, paymentOptions);
|
|
439
|
+
|
|
440
|
+
// Handle merchant validation
|
|
441
|
+
request.onmerchantvalidation = async (event) => {
|
|
442
|
+
console.log("[Apple Pay] Merchant validation requested:", {
|
|
443
|
+
validationURL: event.validationURL,
|
|
444
|
+
domain: window.location.hostname
|
|
445
|
+
});
|
|
446
|
+
|
|
447
|
+
try {
|
|
448
|
+
// Call backend to validate merchant with Payone
|
|
449
|
+
const merchantSessionPromise = validateMerchantWithPayone(event.validationURL, {
|
|
450
|
+
mid: gatewayMerchantId,
|
|
451
|
+
portalid: settings?.portalid,
|
|
452
|
+
domain: window.location.hostname,
|
|
453
|
+
displayName: settings?.merchantName || "Test Store"
|
|
454
|
+
});
|
|
455
|
+
|
|
456
|
+
merchantSessionPromise.then(session => {
|
|
457
|
+
console.log("[Apple Pay] Merchant session received:", {
|
|
458
|
+
hasMerchantIdentifier: !!session.merchantIdentifier,
|
|
459
|
+
domainName: session.domainName,
|
|
460
|
+
displayName: session.displayName
|
|
461
|
+
});
|
|
462
|
+
}).catch(err => {
|
|
463
|
+
console.error("[Apple Pay] Merchant session error:", err);
|
|
464
|
+
});
|
|
465
|
+
|
|
466
|
+
event.complete(merchantSessionPromise);
|
|
467
|
+
} catch (error) {
|
|
468
|
+
console.error("[Apple Pay] Merchant validation error:", error);
|
|
469
|
+
if (onError) {
|
|
470
|
+
onError(error);
|
|
471
|
+
}
|
|
472
|
+
// Complete with empty object - Payone will handle validation
|
|
473
|
+
event.complete({});
|
|
474
|
+
}
|
|
475
|
+
};
|
|
476
|
+
|
|
477
|
+
// Handle payment method change
|
|
478
|
+
request.onpaymentmethodchange = (event) => {
|
|
479
|
+
// Update payment details if needed based on payment method
|
|
480
|
+
const paymentDetailsUpdate = {
|
|
481
|
+
total: paymentDetails.total
|
|
482
|
+
};
|
|
483
|
+
event.updateWith(paymentDetailsUpdate);
|
|
484
|
+
};
|
|
485
|
+
|
|
486
|
+
// Handle shipping option change
|
|
487
|
+
request.onshippingoptionchange = (event) => {
|
|
488
|
+
const paymentDetailsUpdate = {
|
|
489
|
+
total: paymentDetails.total
|
|
490
|
+
};
|
|
491
|
+
event.updateWith(paymentDetailsUpdate);
|
|
492
|
+
};
|
|
493
|
+
|
|
494
|
+
// Handle shipping address change
|
|
495
|
+
request.onshippingaddresschange = (event) => {
|
|
496
|
+
const paymentDetailsUpdate = {};
|
|
497
|
+
event.updateWith(paymentDetailsUpdate);
|
|
498
|
+
};
|
|
499
|
+
|
|
500
|
+
// Show payment sheet and get response
|
|
501
|
+
console.log("[Apple Pay] Showing payment sheet...");
|
|
502
|
+
let response;
|
|
503
|
+
try {
|
|
504
|
+
response = await request.show();
|
|
505
|
+
} catch (error) {
|
|
506
|
+
console.error("[Apple Pay] Error showing payment sheet:", error);
|
|
507
|
+
if (onError) {
|
|
508
|
+
onError(error);
|
|
509
|
+
}
|
|
510
|
+
return;
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
console.log("[Apple Pay] Payment response received:", {
|
|
514
|
+
hasDetails: !!response.details,
|
|
515
|
+
payerName: response.payerName,
|
|
516
|
+
shippingAddress: !!response.shippingAddress
|
|
517
|
+
});
|
|
518
|
+
|
|
519
|
+
// Extract payment token
|
|
520
|
+
const paymentToken = response.details?.paymentToken || response.details?.token;
|
|
521
|
+
|
|
522
|
+
if (!paymentToken) {
|
|
523
|
+
console.error("[Apple Pay] Payment token is missing from response");
|
|
524
|
+
try {
|
|
525
|
+
await response.complete("fail");
|
|
526
|
+
} catch (completeError) {
|
|
527
|
+
console.error("[Apple Pay] Error completing payment with fail:", completeError);
|
|
528
|
+
}
|
|
529
|
+
if (onError) {
|
|
530
|
+
onError(new Error("Apple Pay token is missing"));
|
|
531
|
+
}
|
|
532
|
+
return;
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
console.log("[Apple Pay] Payment token received:", {
|
|
536
|
+
hasToken: !!paymentToken,
|
|
537
|
+
tokenType: typeof paymentToken
|
|
538
|
+
});
|
|
539
|
+
|
|
540
|
+
// Convert token to base64 string for Payone
|
|
541
|
+
let tokenString;
|
|
542
|
+
try {
|
|
543
|
+
if (typeof paymentToken === 'string') {
|
|
544
|
+
tokenString = paymentToken;
|
|
545
|
+
} else {
|
|
546
|
+
tokenString = JSON.stringify(paymentToken);
|
|
547
|
+
}
|
|
548
|
+
// Base64 encode for Payone
|
|
549
|
+
tokenString = btoa(unescape(encodeURIComponent(tokenString)));
|
|
550
|
+
} catch (e) {
|
|
551
|
+
console.error("Token encoding error:", e);
|
|
552
|
+
tokenString = btoa(unescape(encodeURIComponent(JSON.stringify(paymentToken))));
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
// Call the callback with the token BEFORE completing payment
|
|
556
|
+
// This ensures the token is processed before the dialog closes
|
|
557
|
+
console.log("[Apple Pay] Sending token to callback");
|
|
558
|
+
let callbackSuccess = true;
|
|
559
|
+
if (onTokenReceived) {
|
|
560
|
+
try {
|
|
561
|
+
// If callback is async, wait for it
|
|
562
|
+
const callbackResult = onTokenReceived(tokenString, {
|
|
563
|
+
paymentToken: paymentToken,
|
|
564
|
+
billingContact: response.payerName || response.details?.billingContact,
|
|
565
|
+
shippingContact: response.shippingAddress || response.details?.shippingAddress,
|
|
566
|
+
shippingOption: response.shippingOption || response.details?.shippingOption
|
|
567
|
+
});
|
|
568
|
+
|
|
569
|
+
// If callback returns a promise, wait for it
|
|
570
|
+
if (callbackResult && typeof callbackResult.then === 'function') {
|
|
571
|
+
await callbackResult;
|
|
572
|
+
}
|
|
573
|
+
} catch (callbackError) {
|
|
574
|
+
console.error("[Apple Pay] Error in token callback:", callbackError);
|
|
575
|
+
callbackSuccess = false;
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
// Complete payment with success or fail based on callback result
|
|
580
|
+
console.log("[Apple Pay] Completing payment with status:", callbackSuccess ? "success" : "fail");
|
|
581
|
+
try {
|
|
582
|
+
await response.complete(callbackSuccess ? "success" : "fail");
|
|
583
|
+
console.log("[Apple Pay] Payment completed successfully");
|
|
584
|
+
} catch (completeError) {
|
|
585
|
+
console.error("[Apple Pay] Error completing payment:", completeError);
|
|
586
|
+
if (onError) {
|
|
587
|
+
onError(completeError);
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
} catch (error) {
|
|
592
|
+
console.error("[Apple Pay] Payment error:", {
|
|
593
|
+
message: error.message,
|
|
594
|
+
stack: error.stack,
|
|
595
|
+
name: error.name
|
|
596
|
+
});
|
|
597
|
+
if (onError) {
|
|
598
|
+
onError(error);
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
};
|
|
602
|
+
|
|
603
|
+
// Validate merchant with Payone
|
|
604
|
+
const validateMerchantWithPayone = async (validationURL, config) => {
|
|
605
|
+
console.log("[Apple Pay] Validating merchant with Payone:", {
|
|
606
|
+
validationURL,
|
|
607
|
+
config: {
|
|
608
|
+
mid: config.mid,
|
|
609
|
+
portalid: config.portalid,
|
|
610
|
+
domain: config.domain,
|
|
611
|
+
displayName: config.displayName
|
|
612
|
+
}
|
|
613
|
+
});
|
|
614
|
+
|
|
615
|
+
try {
|
|
616
|
+
// Call Strapi backend to validate with Payone
|
|
617
|
+
const response = await fetch('/api/strapi-plugin-payone-provider/validate-apple-pay-merchant', {
|
|
618
|
+
method: 'POST',
|
|
619
|
+
headers: {
|
|
620
|
+
'Content-Type': 'application/json',
|
|
621
|
+
},
|
|
622
|
+
body: JSON.stringify({
|
|
623
|
+
validationURL,
|
|
624
|
+
...config
|
|
625
|
+
})
|
|
626
|
+
});
|
|
627
|
+
|
|
628
|
+
console.log("[Apple Pay] Validation response status:", response.status);
|
|
629
|
+
|
|
630
|
+
if (response.ok) {
|
|
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
|
+
}
|
|
647
|
+
} catch (error) {
|
|
648
|
+
console.error("[Apple Pay] Merchant validation error:", {
|
|
649
|
+
message: error.message,
|
|
650
|
+
stack: error.stack
|
|
651
|
+
});
|
|
652
|
+
// Return empty object - Payone will handle validation
|
|
653
|
+
return {};
|
|
654
|
+
}
|
|
655
|
+
};
|
|
656
|
+
|
|
657
|
+
return (
|
|
658
|
+
<Box width="100%">
|
|
659
|
+
<Flex direction="column" gap={3} alignItems="stretch">
|
|
660
|
+
{isLoading && (
|
|
661
|
+
<Typography variant="pi" textColor="neutral600" style={{ textAlign: "left" }}>
|
|
662
|
+
Checking Apple Pay availability...
|
|
663
|
+
</Typography>
|
|
664
|
+
)}
|
|
665
|
+
{!isLoading && !isAvailable && (
|
|
666
|
+
<Box style={{ width: "100%", display: "flex", flexDirection: "row", alignItems: "flex-start", gap: "8px" }}>
|
|
667
|
+
{errorMessage && (
|
|
668
|
+
<Typography variant="sigma" textColor="neutral500" style={{ textAlign: "left", fontSize: "12px" }}>
|
|
669
|
+
{errorMessage}
|
|
670
|
+
</Typography>
|
|
671
|
+
)}
|
|
672
|
+
</Box>
|
|
673
|
+
)}
|
|
674
|
+
{!isLoading && isAvailable && (
|
|
675
|
+
<>
|
|
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
|
+
<Box ref={buttonContainerRef} style={{ minHeight: "40px", width: "100%", display: "flex", justifyContent: "flex-start" }}>
|
|
683
|
+
{typeof window !== 'undefined' && window.customElements && window.customElements.get('apple-pay-button') ? (
|
|
684
|
+
<apple-pay-button
|
|
685
|
+
buttonstyle={buttonStyle}
|
|
686
|
+
type={buttonType}
|
|
687
|
+
locale="en-US"
|
|
688
|
+
onClick={handleApplePayClick}
|
|
689
|
+
style={{
|
|
690
|
+
width: "200px",
|
|
691
|
+
height: "40px",
|
|
692
|
+
cursor: isReady ? "pointer" : "not-allowed",
|
|
693
|
+
opacity: isReady ? 1 : 0.5
|
|
694
|
+
}}
|
|
695
|
+
/>
|
|
696
|
+
) : (
|
|
697
|
+
<button
|
|
698
|
+
type="button"
|
|
699
|
+
onClick={handleApplePayClick}
|
|
700
|
+
disabled={!isReady}
|
|
701
|
+
style={{
|
|
702
|
+
appearance: "none",
|
|
703
|
+
WebkitAppearance: "-apple-pay-button",
|
|
704
|
+
ApplePayButtonType: buttonType,
|
|
705
|
+
ApplePayButtonStyle: buttonStyle,
|
|
706
|
+
height: "40px",
|
|
707
|
+
width: "200px",
|
|
708
|
+
border: "none",
|
|
709
|
+
borderRadius: "4px",
|
|
710
|
+
cursor: isReady ? "pointer" : "not-allowed",
|
|
711
|
+
opacity: isReady ? 1 : 0.5,
|
|
712
|
+
backgroundColor: buttonStyle === "black" ? "#000" : buttonStyle === "white" ? "#fff" : "transparent",
|
|
713
|
+
color: buttonStyle === "black" ? "#fff" : "#000",
|
|
714
|
+
borderWidth: buttonStyle === "white-outline" ? "1px" : "0",
|
|
715
|
+
borderStyle: buttonStyle === "white-outline" ? "solid" : "none",
|
|
716
|
+
borderColor: buttonStyle === "white-outline" ? "#000" : "transparent",
|
|
717
|
+
fontSize: "16px",
|
|
718
|
+
fontWeight: "400",
|
|
719
|
+
fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif",
|
|
720
|
+
display: "flex",
|
|
721
|
+
alignItems: "center",
|
|
722
|
+
justifyContent: "center"
|
|
723
|
+
}}
|
|
724
|
+
className="apple-pay-button-custom"
|
|
725
|
+
>
|
|
726
|
+
{buttonType === "pay" ? "Pay" : buttonType === "buy" ? "Buy" : buttonType === "donate" ? "Donate" : "Pay with Apple Pay"}
|
|
727
|
+
</button>
|
|
728
|
+
)}
|
|
729
|
+
</Box>
|
|
730
|
+
</>
|
|
731
|
+
)}
|
|
732
|
+
</Flex>
|
|
733
|
+
</Box>
|
|
734
|
+
);
|
|
735
|
+
};
|
|
736
|
+
|
|
737
|
+
export default ApplePayButton;
|