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,908 +0,0 @@
1
- import React, { useEffect, useRef, useState } from "react";
2
- import { Box, Flex, Typography } from "@strapi/design-system";
3
- import { request } from "@strapi/helper-plugin";
4
- import pluginId from "../../../pluginId";
5
-
6
- const ApplePayButton = ({
7
- amount,
8
- currency = "EUR",
9
- countryCode = "DE",
10
- merchantCapabilities = ["supports3DS"],
11
- supportedNetworks = ["visa", "masterCard", "girocard"],
12
- buttonStyle = "black",
13
- buttonType = "pay",
14
- requestPayerName = false,
15
- requestBillingAddress = false,
16
- requestPayerEmail = false,
17
- requestPayerPhone = false,
18
- requestShipping = false,
19
- shippingType = "shipping",
20
- merchantIdentifier = null,
21
- onTokenReceived,
22
- onError,
23
- settings
24
- }) => {
25
- const [isReady, setIsReady] = useState(false);
26
- const [isLoading, setIsLoading] = useState(true);
27
- const [isAvailable, setIsAvailable] = useState(false);
28
- const [errorMessage, setErrorMessage] = useState(null);
29
- const buttonContainerRef = useRef(null);
30
-
31
- const checkApplePayAvailability = async () => {
32
- try {
33
- const isSecureContext = typeof window !== 'undefined' && window.isSecureContext;
34
- const protocol = typeof window !== 'undefined' ? window.location.protocol : '';
35
- const hostname = typeof window !== 'undefined' ? window.location.hostname : '';
36
- const isLocalhost = hostname === 'localhost' || hostname === '127.0.0.1';
37
- const isSecure = isSecureContext || isLocalhost;
38
-
39
- console.log("[Apple Pay] Secure context check:", {
40
- isSecureContext: isSecureContext,
41
- protocol: protocol,
42
- hostname: hostname,
43
- isLocalhost: isLocalhost,
44
- isSecure: isSecure,
45
- fullUrl: typeof window !== 'undefined' ? window.location.href : ''
46
- });
47
-
48
- if (!isSecure) {
49
- console.error("[Apple Pay] NOT in secure context!", {
50
- isSecureContext: isSecureContext,
51
- protocol: protocol,
52
- hostname: hostname,
53
- isLocalhost: isLocalhost,
54
- fullUrl: typeof window !== 'undefined' ? window.location.href : '',
55
- reason: !isSecureContext ? "window.isSecureContext is false" : "Unknown"
56
- });
57
- }
58
-
59
- if (typeof window === 'undefined' || !window.PaymentRequest) {
60
- console.log("[Apple Pay] Payment Request API not available");
61
- if (typeof window !== 'undefined' && window.ApplePaySession && isSecure) {
62
- try {
63
- const canMakePayments = ApplePaySession.canMakePayments();
64
- console.log("[Apple Pay] Apple Pay JS API available:", canMakePayments);
65
- return { available: canMakePayments, method: 'applePayJS' };
66
- } catch (error) {
67
- console.error("[Apple Pay] Apple Pay JS API error (likely insecure context):", error.message);
68
- return { available: false, method: null, error: 'insecure_context' };
69
- }
70
- }
71
-
72
- if (!isSecure && typeof window !== 'undefined' && window.ApplePaySession) {
73
- console.warn("[Apple Pay] Apple Pay JS API requires HTTPS. Using Payment Request API fallback.");
74
- }
75
-
76
- console.log("[Apple Pay] No Apple Pay support found");
77
- return { available: false, method: null };
78
- }
79
-
80
- console.log("[Apple Pay] Payment Request API available, checking Apple Pay support...");
81
-
82
- const applePayMethod = {
83
- supportedMethods: "https://apple.com/apple-pay",
84
- data: {
85
- version: 3,
86
- merchantIdentifier: settings?.mid,
87
- merchantCapabilities: merchantCapabilities,
88
- supportedNetworks: supportedNetworks,
89
- countryCode: countryCode
90
- }
91
- };
92
-
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
- if (testRequest.canMakePayment) {
121
- try {
122
- const canPay = await testRequest.canMakePayment();
123
- console.log("[Apple Pay] canMakePayment result:", canPay);
124
-
125
- if (canPay) {
126
- return { available: true, method: 'paymentRequest' };
127
- }
128
-
129
- if (typeof window !== 'undefined' && window.ApplePaySession && isSecure) {
130
- try {
131
- const canMakePaymentsJS = ApplePaySession.canMakePayments();
132
- console.log("[Apple Pay] Fallback to Apple Pay JS API:", canMakePaymentsJS);
133
- return { available: canMakePaymentsJS, method: 'applePayJS' };
134
- } catch (e) {
135
- console.error("[Apple Pay] Apple Pay JS API error:", e.message);
136
- if (e.message && e.message.includes('insecure')) {
137
- return { available: false, method: null, error: 'insecure_context' };
138
- }
139
- }
140
- }
141
-
142
- return { available: false, method: null };
143
- } catch (e) {
144
- console.error("[Apple Pay] Error checking canMakePayment:", e);
145
-
146
- if (e.message && e.message.includes('insecure')) {
147
- console.warn("[Apple Pay] Insecure context detected. Apple Pay requires HTTPS.");
148
- return { available: false, method: null, error: 'insecure_context' };
149
- }
150
-
151
- // For other errors, try Apple Pay JS API as fallback (only on HTTPS)
152
- if (typeof window !== 'undefined' && window.ApplePaySession && isSecure) {
153
- try {
154
- const canMakePaymentsJS = ApplePaySession.canMakePayments();
155
- return { available: canMakePaymentsJS, method: 'applePayJS' };
156
- } catch (jsError) {
157
- console.error("[Apple Pay] Apple Pay JS API error:", jsError.message);
158
- return { available: false, method: null };
159
- }
160
- }
161
-
162
- return { available: false, method: null };
163
- }
164
- }
165
-
166
- const isSecureContextFinal = typeof window !== 'undefined' && window.isSecureContext;
167
- const hostnameFinal = typeof window !== 'undefined' ? window.location.hostname : '';
168
- const isLocalhostFinal = hostnameFinal === 'localhost' || hostnameFinal === '127.0.0.1';
169
- const isSecureFinal = isSecureContextFinal || isLocalhostFinal;
170
-
171
- if (isSecureFinal) {
172
- console.log("[Apple Pay] canMakePayment not available, assuming support (secure context)");
173
- return { available: true, method: 'paymentRequest' };
174
- } else {
175
- console.warn("[Apple Pay] canMakePayment not available and insecure context");
176
- console.warn("[Apple Pay] Context details:", {
177
- isSecureContext: isSecureContextFinal,
178
- hostname: hostnameFinal,
179
- isLocalhost: isLocalhostFinal
180
- });
181
- return { available: false, method: null, error: 'insecure_context' };
182
- }
183
- } catch (error) {
184
- console.error("[Apple Pay] Error checking availability:", error);
185
-
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
- const isSecureContextFallback = typeof window !== 'undefined' && window.isSecureContext;
192
- const hostnameFallback = typeof window !== 'undefined' ? window.location.hostname : '';
193
- const isLocalhostFallback = hostnameFallback === 'localhost' || hostnameFallback === '127.0.0.1';
194
- const isSecureFallback = isSecureContextFallback || isLocalhostFallback;
195
-
196
- if (typeof window !== 'undefined' && window.ApplePaySession && isSecureFallback) {
197
- try {
198
- const canMakePayments = ApplePaySession.canMakePayments();
199
- return { available: canMakePayments, method: 'applePayJS' };
200
- } catch (e) {
201
- console.error("[Apple Pay] Apple Pay JS API error:", e.message);
202
- if (e.message && e.message.includes('insecure')) {
203
- return { available: false, method: null, error: 'insecure_context' };
204
- }
205
- }
206
- }
207
-
208
- return { available: false, method: null };
209
- }
210
- };
211
-
212
- useEffect(() => {
213
- const scriptUrl = "https://applepay.cdn-apple.com/jsapi/1.latest/apple-pay-sdk.js";
214
-
215
- console.log("[Apple Pay] Loading Apple Pay SDK script...");
216
-
217
- if (document.querySelector(`script[src="${scriptUrl}"]`)) {
218
- console.log("[Apple Pay] Script already loaded");
219
- // Script already loaded, check if it's ready
220
- if (typeof window !== 'undefined' && window.ApplePaySession) {
221
- initializeButton();
222
- } else {
223
- // Wait a bit for script to initialize
224
- setTimeout(() => initializeButton(), 500);
225
- }
226
- return;
227
- }
228
-
229
- const script = document.createElement("script");
230
- script.src = scriptUrl;
231
- script.crossOrigin = "anonymous";
232
- script.async = true;
233
-
234
- script.onload = () => {
235
- console.log("[Apple Pay] SDK script loaded successfully");
236
- setTimeout(() => {
237
- initializeButton();
238
- }, 500);
239
- };
240
-
241
- script.onerror = (error) => {
242
- console.error("[Apple Pay] Failed to load SDK script:", error);
243
- setIsLoading(false);
244
- setIsAvailable(false);
245
- setErrorMessage("Failed to load Apple Pay SDK. Please check Content Security Policy settings.");
246
-
247
- // Even if script fails, try to use Payment Request API
248
- console.log("[Apple Pay] Trying Payment Request API as fallback...");
249
- setTimeout(() => {
250
- initializeButton();
251
- }, 1000);
252
- };
253
-
254
- try {
255
- document.head.appendChild(script);
256
- console.log("[Apple Pay] Script element added to head");
257
- } catch (error) {
258
- console.error("[Apple Pay] Error adding script to head:", error);
259
- // Try to use Payment Request API without SDK
260
- setTimeout(() => {
261
- initializeButton();
262
- }, 1000);
263
- }
264
- }, []);
265
-
266
- const initializeButton = async () => {
267
- try {
268
- console.log("[Apple Pay] Initializing button...");
269
-
270
- const isSecure = typeof window !== 'undefined' &&
271
- (window.location.protocol === 'https:' ||
272
- window.location.hostname === 'localhost' ||
273
- window.location.hostname === '127.0.0.1');
274
-
275
- console.log("[Apple Pay] Secure context check:", {
276
- protocol: window.location?.protocol,
277
- hostname: window.location?.hostname,
278
- isSecure: isSecure
279
- });
280
-
281
- const isLocalhost = typeof window !== 'undefined' &&
282
- (window.location.hostname === 'localhost' ||
283
- window.location.hostname === '127.0.0.1');
284
-
285
- if (!isSecure && !isLocalhost && window.location?.protocol === 'http:') {
286
- 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.";
287
- setErrorMessage(errorMsg);
288
- setIsLoading(false);
289
- setIsAvailable(false);
290
- console.warn("[Apple Pay]", errorMsg);
291
- console.warn("[Apple Pay] Current URL:", window.location.href);
292
- return;
293
- }
294
-
295
- // Log context information
296
- console.log("[Apple Pay] Context info:", {
297
- protocol: window.location?.protocol,
298
- hostname: window.location?.hostname,
299
- isSecure: isSecure,
300
- isLocalhost: isLocalhost,
301
- fullUrl: window.location?.href
302
- });
303
-
304
- // Check availability
305
- const availability = await checkApplePayAvailability();
306
- console.log("[Apple Pay] Availability check result:", availability);
307
-
308
- setIsAvailable(availability.available);
309
-
310
- if (!availability.available) {
311
- let errorMsg = "Apple Pay is not available on this device or browser.";
312
-
313
- if (isLocalhost) {
314
- errorMsg = "Apple Pay is not available on localhost. Apple Pay requires a registered domain with HTTPS. " +
315
- "For testing, please use a production domain with HTTPS or test on a device with Safari (iOS/macOS). " +
316
- "You can still test the payment flow by using the 'Process Preauthorization' button without Apple Pay token.";
317
- } else if (availability.error === 'insecure_context') {
318
- errorMsg = "Apple Pay requires HTTPS. Please access this page via HTTPS (https://yourdomain.com) instead of HTTP.";
319
- } else if (typeof window !== 'undefined' && window.PaymentRequest) {
320
- errorMsg += " Payment Request API is available but Apple Pay is not supported. " +
321
- "Please use Safari on iOS, macOS, or iPadOS, or Chrome/Edge on supported devices. " +
322
- "Note: Apple Pay requires a registered domain and cannot work on localhost.";
323
- } else if (typeof window !== 'undefined' && window.ApplePaySession) {
324
- errorMsg += " Apple Pay JS API is available but cannot make payments. " +
325
- "Please check your device settings and ensure you have a card added to Wallet.";
326
- } else {
327
- errorMsg += " Please use Safari on iOS, macOS, or iPadOS, or a browser that supports Payment Request API (Chrome, Edge, Safari).";
328
- }
329
-
330
- setErrorMessage(errorMsg);
331
- setIsLoading(false);
332
- console.warn("[Apple Pay] Not available:", errorMsg);
333
- return;
334
- }
335
-
336
- console.log("[Apple Pay] Button initialized successfully, method:", availability.method);
337
- setIsReady(true);
338
- setIsLoading(false);
339
- } catch (error) {
340
- console.error("[Apple Pay] Initialization error:", error);
341
- setIsLoading(false);
342
- setIsAvailable(false);
343
-
344
- // Check for insecure context error
345
- if (error.message && error.message.includes('insecure')) {
346
- setErrorMessage("Apple Pay requires HTTPS. Please access this page via HTTPS (https://yourdomain.com) instead of HTTP. Localhost is allowed for development.");
347
- } else {
348
- setErrorMessage(error.message || "Failed to initialize Apple Pay");
349
- }
350
-
351
- if (onError) {
352
- onError(error);
353
- }
354
- }
355
- };
356
-
357
- const handleApplePayClick = async () => {
358
- console.log("[Apple Pay] Button clicked, checking readiness...", {
359
- isReady,
360
- isAvailable,
361
- amount,
362
- currency,
363
- countryCode,
364
- protocol: window.location?.protocol,
365
- hostname: window.location?.hostname
366
- });
367
-
368
- // Check HTTPS requirement
369
- const isSecure = typeof window !== 'undefined' &&
370
- (window.location.protocol === 'https:' ||
371
- window.location.hostname === 'localhost' ||
372
- window.location.hostname === '127.0.0.1');
373
-
374
- if (!isSecure && window.location?.protocol === 'http:') {
375
- const errorMsg = "Apple Pay requires HTTPS. Please access this page via HTTPS.";
376
- console.error("[Apple Pay]", errorMsg);
377
- if (onError) {
378
- onError(new Error(errorMsg));
379
- }
380
- return;
381
- }
382
-
383
- if (!isReady || !isAvailable) {
384
- console.error("[Apple Pay] Not ready or not available");
385
- if (onError) {
386
- onError(new Error("Apple Pay is not ready"));
387
- }
388
- return;
389
- }
390
-
391
- try {
392
- const amountValue = (parseFloat(amount) / 100).toFixed(2);
393
- const gatewayMerchantId = settings?.mid;
394
- const merchantId = gatewayMerchantId
395
-
396
- console.log("[Apple Pay] Starting payment request:", {
397
- amount: amountValue,
398
- currency,
399
- merchantId,
400
- countryCode,
401
- supportedNetworks,
402
- merchantCapabilities
403
- });
404
-
405
- const paymentMethodData = [{
406
- supportedMethods: "https://apple.com/apple-pay",
407
- data: {
408
- version: 3,
409
- merchantIdentifier: merchantId,
410
- merchantCapabilities: merchantCapabilities,
411
- supportedNetworks: supportedNetworks,
412
- countryCode: countryCode,
413
- currencyCode: currency
414
- }
415
- }];
416
-
417
- // Define PaymentDetails
418
- const paymentDetails = {
419
- total: {
420
- label: settings?.merchantName || "Demo Payment",
421
- amount: {
422
- value: amountValue,
423
- currency: currency
424
- }
425
- }
426
- };
427
-
428
- // Define PaymentOptions
429
- const paymentOptions = {
430
- requestPayerName: requestPayerName,
431
- requestBillingAddress: requestBillingAddress,
432
- requestPayerEmail: requestPayerEmail,
433
- requestPayerPhone: requestPayerPhone,
434
- requestShipping: requestShipping,
435
- shippingType: shippingType
436
- };
437
-
438
- // Create PaymentRequest
439
- const request = new PaymentRequest(paymentMethodData, paymentDetails, paymentOptions);
440
-
441
- // Handle merchant validation
442
- request.onmerchantvalidation = async (event) => {
443
- console.log("[Apple Pay] Merchant validation requested:", {
444
- validationURL: event.validationURL,
445
- domain: window.location.hostname
446
- });
447
-
448
- try {
449
- // Call backend to validate merchant with Payone
450
- const merchantSessionPromise = validateMerchantWithPayone(event.validationURL, {
451
- mid: gatewayMerchantId,
452
- portalid: settings?.portalid,
453
- domain: window.location.hostname,
454
- displayName: settings?.merchantName || "Test Store"
455
- });
456
-
457
- merchantSessionPromise.then(session => {
458
- console.log("[Apple Pay] ========== MERCHANT SESSION RECEIVED ==========");
459
- console.log("[Apple Pay] Full Merchant Session (JSON):", JSON.stringify(session, null, 2));
460
- console.log("[Apple Pay] Session Type:", typeof session);
461
- console.log("[Apple Pay] Is Object:", session instanceof Object);
462
- console.log("[Apple Pay] Session Keys:", Object.keys(session || {}));
463
- console.log("[Apple Pay] Session Keys Count:", Object.keys(session || {}).length);
464
- console.log("[Apple Pay] Has Merchant Identifier:", !!session?.merchantIdentifier);
465
- console.log("[Apple Pay] Merchant Identifier:", session?.merchantIdentifier);
466
- console.log("[Apple Pay] Has Merchant Session Identifier:", !!session?.merchantSessionIdentifier);
467
- console.log("[Apple Pay] Merchant Session Identifier:", session?.merchantSessionIdentifier);
468
- console.log("[Apple Pay] Domain Name:", session?.domainName);
469
- console.log("[Apple Pay] Display Name:", session?.displayName);
470
- console.log("[Apple Pay] Epoch Timestamp:", session?.epochTimestamp);
471
- console.log("[Apple Pay] Expires At:", session?.expiresAt);
472
- console.log("[Apple Pay] Nonce:", session?.nonce);
473
-
474
- // Log all properties
475
- console.log("[Apple Pay] All Session Properties:");
476
- if (session && typeof session === 'object') {
477
- for (const [key, value] of Object.entries(session)) {
478
- if (typeof value === 'string' && value.length > 200) {
479
- console.log(`[Apple Pay] ${key}:`, value.substring(0, 200) + "... (truncated, length: " + value.length + ")");
480
- } else {
481
- console.log(`[Apple Pay] ${key}:`, value);
482
- }
483
- }
484
- }
485
- console.log("[Apple Pay] ==========================================");
486
-
487
- // Validate merchant session
488
- if (!session || (!session.merchantIdentifier && !session.merchantSessionIdentifier)) {
489
- console.error("[Apple Pay] Invalid merchant session - missing merchantIdentifier");
490
- console.error("[Apple Pay] Session object:", JSON.stringify(session, null, 2));
491
- throw new Error("Invalid merchant session: missing merchantIdentifier");
492
- }
493
- }).catch(err => {
494
- console.error("[Apple Pay] Merchant session error:", err);
495
- // Re-throw so Payment Request API knows validation failed
496
- throw err;
497
- });
498
-
499
- // Complete with the merchant session promise
500
- // If the promise rejects, Payment Request API will close the dialog
501
- // This is expected behavior - we cannot proceed without a valid merchant session
502
- event.complete(merchantSessionPromise);
503
- } catch (error) {
504
- console.error("[Apple Pay] Merchant validation error:", {
505
- message: error.message,
506
- stack: error.stack,
507
- response: error.response
508
- });
509
-
510
- // Call onError to notify the user
511
- if (typeof onError === 'function') {
512
- onError(new Error(`Apple Pay merchant validation failed: ${error.message}. Please check your Payone Apple Pay configuration in PMI (CONFIGURATION → PAYMENT PORTALS → [Your Portal] → Apple Pay).`));
513
- }
514
-
515
- // Complete with a rejected promise
516
- // This will cause Apple Pay to close the dialog, which is expected behavior
517
- // We cannot proceed without a valid merchant session from Payone
518
- event.complete(Promise.reject(error));
519
- }
520
- };
521
-
522
- // Handle payment method change
523
- request.onpaymentmethodchange = (event) => {
524
- // Update payment details if needed based on payment method
525
- const paymentDetailsUpdate = {
526
- total: paymentDetails.total
527
- };
528
- event.updateWith(paymentDetailsUpdate);
529
- };
530
-
531
- // Handle shipping option change
532
- request.onshippingoptionchange = (event) => {
533
- const paymentDetailsUpdate = {
534
- total: paymentDetails.total
535
- };
536
- event.updateWith(paymentDetailsUpdate);
537
- };
538
-
539
- // Handle shipping address change
540
- request.onshippingaddresschange = (event) => {
541
- const paymentDetailsUpdate = {};
542
- event.updateWith(paymentDetailsUpdate);
543
- };
544
-
545
- // Show payment sheet and get response
546
- console.log("[Apple Pay] Showing payment sheet...");
547
- let response;
548
- try {
549
- response = await request.show();
550
- console.log("[Apple Pay] Payment sheet shown successfully");
551
- } catch (error) {
552
- console.error("[Apple Pay] Error showing payment sheet:", {
553
- name: error.name,
554
- message: error.message,
555
- stack: error.stack
556
- });
557
-
558
- // Check if error is due to cancellation (user cancelled)
559
- // Payment Request API throws "AbortError" when user cancels
560
- if (error.name === 'AbortError' ||
561
- (error.message && (
562
- error.message.includes('Cancelled') ||
563
- error.message.includes('cancel') ||
564
- error.message.includes('abort')
565
- ))) {
566
- console.log("[Apple Pay] User cancelled the payment");
567
- // Don't call onError for user cancellation
568
- return;
569
- }
570
-
571
- // If it's a merchant validation error, log it specifically
572
- if (error.message && (
573
- error.message.includes('merchant') ||
574
- error.message.includes('validation') ||
575
- error.message.includes('identifier')
576
- )) {
577
- console.error("[Apple Pay] Merchant validation failed - this may cause the dialog to close");
578
- if (typeof onError === 'function') {
579
- onError(new Error("Merchant validation failed. Please check your Apple Pay configuration and merchant identifier in Payone settings."));
580
- }
581
- } else {
582
- // For other errors, call onError
583
- if (typeof onError === 'function') {
584
- onError(error);
585
- }
586
- }
587
- return;
588
- }
589
-
590
- console.log("[Apple Pay] Payment response received:", {
591
- hasDetails: !!response.details,
592
- payerName: response.payerName,
593
- shippingAddress: !!response.shippingAddress
594
- });
595
-
596
- // Extract payment token
597
- const paymentToken = response.details?.paymentToken || response.details?.token;
598
-
599
- if (!paymentToken) {
600
- console.error("[Apple Pay] Payment token is missing from response");
601
- try {
602
- await response.complete("fail");
603
- } catch (completeError) {
604
- console.error("[Apple Pay] Error completing payment with fail:", completeError);
605
- }
606
- if (onError) {
607
- onError(new Error("Apple Pay token is missing"));
608
- }
609
- return;
610
- }
611
-
612
- console.log("[Apple Pay] Payment token received:", {
613
- hasToken: !!paymentToken,
614
- tokenType: typeof paymentToken
615
- });
616
-
617
- // Convert token to base64 string for Payone
618
- let tokenString;
619
- try {
620
- if (typeof paymentToken === 'string') {
621
- tokenString = paymentToken;
622
- } else {
623
- tokenString = JSON.stringify(paymentToken);
624
- }
625
- // Base64 encode for Payone
626
- tokenString = btoa(unescape(encodeURIComponent(tokenString)));
627
- } catch (e) {
628
- console.error("Token encoding error:", e);
629
- tokenString = btoa(unescape(encodeURIComponent(JSON.stringify(paymentToken))));
630
- }
631
-
632
- // Call the callback with the token BEFORE completing payment
633
- // This ensures the token is saved before the dialog closes
634
- console.log("[Apple Pay] Sending token to callback");
635
- let callbackSuccess = true;
636
- let callbackError = null;
637
-
638
- if (onTokenReceived) {
639
- try {
640
- // Call the callback to save the token
641
- // The callback should set the token in state and return success immediately
642
- // It should NOT process the payment yet - that will happen when user clicks the button
643
- const callbackResult = onTokenReceived(tokenString, {
644
- paymentToken: paymentToken,
645
- billingContact: response.payerName || response.details?.billingContact,
646
- shippingContact: response.shippingAddress || response.details?.shippingAddress,
647
- shippingOption: response.shippingOption || response.details?.shippingOption
648
- });
649
-
650
- // If callback returns a promise, wait for it to resolve or reject
651
- if (callbackResult && typeof callbackResult.then === 'function') {
652
- try {
653
- const result = await callbackResult;
654
- console.log("[Apple Pay] Token callback completed successfully:", result);
655
- // Check if result indicates success
656
- if (result && result.success === false) {
657
- callbackSuccess = false;
658
- callbackError = new Error(result.message || "Token callback returned failure");
659
- }
660
- } catch (error) {
661
- console.error("[Apple Pay] Token callback promise rejected:", error);
662
- callbackSuccess = false;
663
- callbackError = error;
664
- }
665
- } else if (callbackResult === false) {
666
- // If callback explicitly returns false, treat as failure
667
- callbackSuccess = false;
668
- console.warn("[Apple Pay] Token callback returned false");
669
- } else {
670
- // If callback returns a value (not a promise), assume success
671
- console.log("[Apple Pay] Token callback returned synchronously");
672
- }
673
- } catch (error) {
674
- console.error("[Apple Pay] Error in token callback:", error);
675
- callbackSuccess = false;
676
- callbackError = error;
677
- }
678
- } else {
679
- console.warn("[Apple Pay] No onTokenReceived callback provided");
680
- // If no callback, we should still complete the payment
681
- // But mark as success since we can't determine the result
682
- callbackSuccess = true;
683
- }
684
-
685
- // Complete payment with success or fail based on callback result
686
- // IMPORTANT: Only call complete() after the callback has fully finished
687
- console.log("[Apple Pay] Completing payment with status:", callbackSuccess ? "success" : "fail");
688
-
689
- try {
690
- // Use a small delay to ensure state updates are complete
691
- // This prevents the dialog from closing before the token is saved
692
- await new Promise(resolve => setTimeout(resolve, 200));
693
-
694
- const completionStatus = callbackSuccess ? "success" : "fail";
695
- await response.complete(completionStatus);
696
- console.log("[Apple Pay] Payment completed with status:", completionStatus);
697
-
698
- // If there was an error, notify the error handler
699
- if (!callbackSuccess && callbackError && onError) {
700
- onError(callbackError);
701
- }
702
- } catch (completeError) {
703
- console.error("[Apple Pay] Error completing payment:", completeError);
704
- // Try to complete with fail status if there's an error
705
- try {
706
- await response.complete("fail");
707
- } catch (finalError) {
708
- console.error("[Apple Pay] Failed to complete payment even with fail status:", finalError);
709
- }
710
- // Only call onError if it's defined
711
- if (typeof onError === 'function') {
712
- onError(completeError);
713
- }
714
- }
715
-
716
- } catch (error) {
717
- console.error("[Apple Pay] Payment error:", {
718
- message: error.message,
719
- stack: error.stack,
720
- name: error.name
721
- });
722
- // Check if error is due to cancellation (user cancelled)
723
- if (error.message && error.message.includes('Cancelled')) {
724
- console.log("[Apple Pay] User cancelled the payment");
725
- // Don't call onError for user cancellation
726
- return;
727
- }
728
- // Only call onError if it's defined and it's not a cancellation
729
- if (typeof onError === 'function') {
730
- onError(error);
731
- }
732
- }
733
- };
734
-
735
- const validateMerchantWithPayone = async (validationURL, config) => {
736
- console.log("[Apple Pay] Validating merchant with Payone:", {
737
- validationURL,
738
- config: {
739
- mid: config.mid,
740
- portalid: config.portalid,
741
- domain: config.domain,
742
- displayName: config.displayName
743
- }
744
- });
745
-
746
- try {
747
- console.log("[Apple Pay] Making validation request using Strapi helper...");
748
-
749
- // Use Strapi helper-plugin's request function which automatically handles authentication
750
- // This uses the admin API route which requires admin authentication
751
- const merchantSession = await request(`/${pluginId}/validate-apple-pay-merchant`, {
752
- method: 'POST',
753
- body: {
754
- validationURL,
755
- ...config
756
- }
757
- });
758
-
759
- console.log("[Apple Pay] ========== BACKEND RESPONSE START ==========");
760
- console.log("[Apple Pay] Full Backend Response:", JSON.stringify(merchantSession, null, 2));
761
- console.log("[Apple Pay] Response Type:", typeof merchantSession);
762
- console.log("[Apple Pay] Has Data:", !!merchantSession.data);
763
- console.log("[Apple Pay] Has Error:", !!merchantSession.error);
764
-
765
- if (merchantSession.data) {
766
- console.log("[Apple Pay] Data Object:", merchantSession.data);
767
- console.log("[Apple Pay] Data Type:", typeof merchantSession.data);
768
- console.log("[Apple Pay] Data Keys:", Object.keys(merchantSession.data));
769
- console.log("[Apple Pay] Data Keys Count:", Object.keys(merchantSession.data).length);
770
- console.log("[Apple Pay] Merchant Identifier:", merchantSession.data.merchantIdentifier);
771
- console.log("[Apple Pay] Domain Name:", merchantSession.data.domainName);
772
- console.log("[Apple Pay] Display Name:", merchantSession.data.displayName);
773
- console.log("[Apple Pay] Epoch Timestamp:", merchantSession.data.epochTimestamp);
774
- console.log("[Apple Pay] Expires At:", merchantSession.data.expiresAt);
775
- }
776
-
777
- if (merchantSession.error) {
778
- console.log("[Apple Pay] Error Object:", merchantSession.error);
779
- console.log("[Apple Pay] Error Message:", merchantSession.error.message);
780
- console.log("[Apple Pay] Error Details:", merchantSession.error.details);
781
- }
782
-
783
- console.log("[Apple Pay] ========== BACKEND RESPONSE END ==========");
784
-
785
- // Check if there's an error in the response
786
- if (merchantSession.error) {
787
- console.error("[Apple Pay] Backend returned error:", merchantSession.error);
788
- throw new Error(merchantSession.error.message || "Apple Pay merchant validation failed");
789
- }
790
-
791
- // Validate merchant session
792
- const session = merchantSession.data || merchantSession;
793
-
794
- // Check if session is empty object
795
- if (!session || Object.keys(session).length === 0) {
796
- console.error("[Apple Pay] Empty merchant session received from backend");
797
- console.error("[Apple Pay] Full response:", JSON.stringify(merchantSession, null, 2));
798
- throw new Error("Empty merchant session received. This usually means Payone did not return a valid merchant session. Please check your Payone Apple Pay configuration in PMI (CONFIGURATION → PAYMENT PORTALS → [Your Portal] → Apple Pay).");
799
- }
800
-
801
- if (!session.merchantIdentifier && !session.merchantSessionIdentifier) {
802
- console.error("[Apple Pay] Invalid merchant session - missing merchantIdentifier");
803
- console.error("[Apple Pay] Session object:", JSON.stringify(session, null, 2));
804
- console.error("[Apple Pay] Full response:", JSON.stringify(merchantSession, null, 2));
805
- throw new Error("Invalid merchant session: missing merchantIdentifier. Please check your Payone Apple Pay configuration in PMI (CONFIGURATION → PAYMENT PORTALS → [Your Portal] → Apple Pay). The merchant session must come from Payone after successful Apple Pay onboarding.");
806
- }
807
-
808
- return session;
809
- } catch (error) {
810
- console.error("[Apple Pay] Merchant validation error:", {
811
- message: error.message,
812
- status: error.response?.status,
813
- statusText: error.response?.statusText,
814
- data: error.response?.data,
815
- stack: error.stack
816
- });
817
-
818
- // Log specific error details
819
- if (error.response?.status === 403) {
820
- console.error("[Apple Pay] 403 Forbidden - Authentication failed. Make sure you are logged in as admin.");
821
- } else if (error.response?.status === 401) {
822
- console.error("[Apple Pay] 401 Unauthorized - Please log in again.");
823
- } else if (error.response?.status >= 500) {
824
- console.error("[Apple Pay] Server error - Check server logs for details.");
825
- }
826
-
827
- // If validation fails, we cannot proceed
828
- // Apple Pay requires a valid merchant session from Payone
829
- console.error("[Apple Pay] Merchant validation failed - cannot proceed without valid session");
830
- throw error;
831
- }
832
- };
833
-
834
- return (
835
- <Box width="100%">
836
- <Flex direction="column" gap={3} alignItems="stretch">
837
- {isLoading && (
838
- <Typography variant="pi" textColor="neutral600" style={{ textAlign: "left" }}>
839
- Checking Apple Pay availability...
840
- </Typography>
841
- )}
842
- {!isLoading && !isAvailable && (
843
- <Box style={{ width: "100%", display: "flex", flexDirection: "row", alignItems: "flex-start", gap: "8px" }}>
844
- {errorMessage && (
845
- <Typography variant="sigma" textColor="neutral500" style={{ textAlign: "left", fontSize: "12px" }}>
846
- {errorMessage}
847
- </Typography>
848
- )}
849
- </Box>
850
- )}
851
- {!isLoading && isAvailable && (
852
- <>
853
- <Box ref={buttonContainerRef} style={{ minHeight: "40px", width: "100%", display: "flex", justifyContent: "flex-start" }}>
854
- {typeof window !== 'undefined' && window.customElements && window.customElements.get('apple-pay-button') ? (
855
- <apple-pay-button
856
- buttonstyle={buttonStyle}
857
- type={buttonType}
858
- locale="en-US"
859
- onClick={handleApplePayClick}
860
- style={{
861
- width: "200px",
862
- height: "40px",
863
- cursor: isReady ? "pointer" : "not-allowed",
864
- opacity: isReady ? 1 : 0.5
865
- }}
866
- />
867
- ) : (
868
- <button
869
- type="button"
870
- onClick={handleApplePayClick}
871
- disabled={!isReady}
872
- style={{
873
- appearance: "none",
874
- WebkitAppearance: "-apple-pay-button",
875
- ApplePayButtonType: buttonType,
876
- ApplePayButtonStyle: buttonStyle,
877
- height: "40px",
878
- width: "200px",
879
- border: "none",
880
- borderRadius: "4px",
881
- cursor: isReady ? "pointer" : "not-allowed",
882
- opacity: isReady ? 1 : 0.5,
883
- backgroundColor: buttonStyle === "black" ? "#000" : buttonStyle === "white" ? "#fff" : "transparent",
884
- color: buttonStyle === "black" ? "#fff" : "#000",
885
- borderWidth: buttonStyle === "white-outline" ? "1px" : "0",
886
- borderStyle: buttonStyle === "white-outline" ? "solid" : "none",
887
- borderColor: buttonStyle === "white-outline" ? "#000" : "transparent",
888
- fontSize: "16px",
889
- fontWeight: "400",
890
- fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif",
891
- display: "flex",
892
- alignItems: "center",
893
- justifyContent: "center"
894
- }}
895
- className="apple-pay-button-custom"
896
- >
897
- {buttonType === "pay" ? "Pay" : buttonType === "buy" ? "Buy" : buttonType === "donate" ? "Donate" : "Pay with Apple Pay"}
898
- </button>
899
- )}
900
- </Box>
901
- </>
902
- )}
903
- </Flex>
904
- </Box>
905
- );
906
- };
907
-
908
- export default ApplePayButton;