PluginProbe
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler / 1.3.21
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler v1.3.21
1.6.6 1.6.5 1.6.4 1.6.3 1.6.2 1.6.1 1.6.0 1.5.4 1.5.5 1.5.3 1.5.2 1.5.1 1.5.0 1.4.2 1.4.1 1.4.0 1.3.28 1.3.27 1.3.26 1.3.25 1.3.23 1.3.22 1.3.21 1.3.20 1.3.19 All 49 releases
fluent-cart / app / Modules / Turnstile / TurnstileBoot.php

TurnstileBoot.php in FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler 1.3.21, at app/Modules/Turnstile/TurnstileBoot.php

209 lines 8.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace FluentCart\App\Modules\Turnstile;
4
5 use FluentCart\Api\ModuleSettings;
6 use FluentCart\Framework\Support\Arr;
7
8 class TurnstileBoot
9 {
10 public function register()
11 {
12 add_action('fluent_cart/before_payment_methods', [$this, 'renderTurnstileWidget'], 10, 1);
13
14 add_action('wp_footer', [$this, 'injectWidgetViaFooter'], 999);
15
16 add_action('wp_enqueue_scripts', [$this, 'enqueueTurnstileScript'], 20);
17
18 add_filter('fluent_cart/checkout/localize_data', [$this, 'localizeTurnstileData'], 10, 1);
19
20 (new TurnstileValidator())->register();
21 }
22
23 public function renderTurnstileWidget($data = [])
24 {
25 $settings = ModuleSettings::getSettings('turnstile');
26 $enableTurnstile = Arr::get($settings, 'active', 'no');
27 $siteKey = Arr::get($settings, 'site_key', '');
28
29 $isActive = ($enableTurnstile === 'yes' && !empty($siteKey));
30 ?>
31 <div class="fct-turnstile-wrapper" data-fluent-cart-turnstile-widget style="position: fixed; bottom: 0; left: 0; width: 1px; height: 1px; overflow: hidden; opacity: 0; pointer-events: none;" data-turnstile-active="<?php echo $isActive ? 'yes' : 'no'; ?>">
32 <?php if ($isActive): ?>
33 <div class="cf-turnstile"
34 data-sitekey="<?php echo esc_attr($siteKey); ?>"
35 data-callback="fluentCartTurnstileCallback"
36 data-size="flexible"
37 data-appearance="interaction-only"
38 data-theme="auto">
39 </div>
40 <?php else: ?>
41 <div class="cf-turnstile" style="display: none;"></div>
42 <?php endif; ?>
43 </div>
44 <?php
45 }
46
47 public function enqueueTurnstileScript()
48 {
49 $settings = ModuleSettings::getSettings('turnstile');
50 if (Arr::get($settings, 'active', 'no') === 'yes' && !empty(Arr::get($settings, 'site_key', ''))) {
51 if ($this->isCheckoutContext()) {
52 wp_enqueue_script(
53 'cloudflare-turnstile',
54 'https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit',
55 [],
56 null,
57 true
58 );
59 }
60 }
61 }
62
63 public function localizeTurnstileData($data)
64 {
65 $settings = ModuleSettings::getSettings('turnstile');
66 if (isset($data['fluentcart_checkout_vars'])) {
67 $data['fluentcart_checkout_vars']['turnstile'] = [
68 'enabled' => Arr::get($settings, 'active', 'no') === 'yes',
69 'site_key' => Arr::get($settings, 'site_key', '')
70 ];
71 }
72 return $data;
73 }
74
75 /**
76 * Inject widget via wp_footer as last resort
77 * This should ALWAYS work since wp_footer fires on every page
78 */
79 public function injectWidgetViaFooter()
80 {
81 if ($this->isCheckoutContext()) {
82 $widgetHtml = $this->getWidgetHtml();
83 ?>
84 <script>
85 (function() {
86 if (document.querySelector('[data-fluent-cart-turnstile-widget]')) {
87 return;
88 }
89
90 const widgetHtml = <?php echo json_encode($widgetHtml); ?>;
91
92 const checkoutForm = document.querySelector('[data-fluent-cart-checkout-page-checkout-form]');
93 if (checkoutForm) {
94 checkoutForm.insertAdjacentHTML('beforeend', widgetHtml);
95 } else {
96 document.body.insertAdjacentHTML('beforeend', widgetHtml);
97 }
98
99 function renderTurnstileWidget() {
100 const widget = document.querySelector('[data-fluent-cart-turnstile-widget] .cf-turnstile');
101 if (!widget || typeof turnstile === 'undefined') {
102 return false;
103 }
104
105 const siteKey = widget.getAttribute('data-sitekey') || window.fluentcart_checkout_vars?.turnstile?.site_key;
106 const widgetId = widget.getAttribute('data-widget-id');
107
108 if (!siteKey || widgetId) {
109 return !!widgetId;
110 }
111
112 try {
113 const renderedId = turnstile.render(widget, {
114 sitekey: siteKey,
115 callback: function(token) {
116 if (typeof window.fluentCartTurnstileCallback === 'function') {
117 window.fluentCartTurnstileCallback(token);
118 } else {
119 window.fluentCartTurnstileToken = token;
120 }
121 },
122 'expired-callback': function() {
123 window.fluentCartTurnstileToken = null;
124 if (window.fluentCartTurnstileHandlerInstance) {
125 window.fluentCartTurnstileHandlerInstance.handleExpired();
126 }
127 },
128 'error-callback': function() {
129 window.fluentCartTurnstileToken = null;
130 if (window.fluentCartTurnstileHandlerInstance) {
131 window.fluentCartTurnstileHandlerInstance.handleError();
132 }
133 },
134 size: 'flexible',
135 appearance: 'interaction-only',
136 theme: 'auto'
137 });
138 if (renderedId) {
139 widget.setAttribute('data-widget-id', renderedId);
140 }
141 return true;
142 } catch (error) {
143 return false;
144 }
145 }
146
147 if (!renderTurnstileWidget()) {
148 let attempts = 0;
149 const maxAttempts = 50; // 5 seconds
150 const checkInterval = setInterval(function() {
151 attempts++;
152 if (renderTurnstileWidget() || attempts >= maxAttempts) {
153 clearInterval(checkInterval);
154 }
155 }, 100);
156 }
157 })();
158 </script>
159 <?php
160 }
161 }
162
163 private function isCheckoutContext()
164 {
165 $templateService = \FluentCart\App\Services\TemplateService::getCurrentFcPageType();
166 if ($templateService === 'checkout') {
167 return true;
168 }
169
170 $fluentCartRequest = isset($_GET['fluent-cart']) ? sanitize_text_field(wp_unslash($_GET['fluent-cart'])) : '';
171 if ($fluentCartRequest === 'modal_checkout') {
172 return true;
173 }
174
175 if (is_page()) {
176 global $post;
177 if ($post && has_shortcode($post->post_content, 'fluent_cart_checkout')) {
178 return true;
179 }
180 }
181
182 return false;
183 }
184
185 /**
186 * Get widget HTML as string
187 */
188 private function getWidgetHtml()
189 {
190 $settings = ModuleSettings::getSettings('turnstile');
191 $enableTurnstile = Arr::get($settings, 'active', 'no');
192 $siteKey = Arr::get($settings, 'site_key', '');
193 $isActive = ($enableTurnstile === 'yes' && !empty($siteKey));
194
195 $html = '<div class="fct-turnstile-wrapper" data-fluent-cart-turnstile-widget style="position: fixed; bottom: 0; left: 0; width: 1px; height: 1px; overflow: hidden; opacity: 0; pointer-events: none;" data-turnstile-active="' . ($isActive ? 'yes' : 'no') . '">';
196
197 if ($isActive) {
198 $html .= '<div class="cf-turnstile" data-sitekey="' . esc_attr($siteKey) . '" data-callback="fluentCartTurnstileCallback" data-size="flexible" data-appearance="interaction-only" data-theme="auto"></div>';
199 } else {
200 $html .= '<div class="cf-turnstile" style="display: none;"></div>';
201 }
202
203 $html .= '</div>';
204
205 return $html;
206 }
207 }
208
209