PluginProbe
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler / 1.6.5
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler v1.6.5
1.7.0 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 All 50 releases
← All changes | app/Services/Renderer/ModalCheckoutRenderer.php +98 -2 1.6.0 → 1.6.5 View file →
@@ -39,8 +39,16 @@
39 39 private $cartSummaryRenderer;
40 40
41 41 private $customer;
42 42
43 + /**
44 + * Memoised result of the before_payment_methods placement filter. Null until
45 + * resolved; see beforePaymentMethodsPlacement() for why it is asked only once.
46 + *
47 + * @var string|null
48 + */
49 + private $paymentMethodsHookPlacement = null;
50 +
43 51 public function __construct(Cart $cart, $config = [])
44 52 {
45 53 $this->cart = $cart;
46 54 $this->config = $config;
@@ -183,8 +191,40 @@
183 191 // No explicit save — the recalc action that follows persists this
184 192 // together with the newly computed tax data, avoiding a double write.
185 193 }
186 194
195 + /**
196 + * Where `fluent_cart/before_payment_methods` fires in the modal.
197 + *
198 + * 'payment' (default) wraps the payment-method list, matching CheckoutRenderer.
199 + * 'details' restores the pre-fix position in the address/shipping pane, for a
200 + * store whose add-on positioned itself around that spot.
201 + *
202 + * Resolved ONCE per renderer and memoised. The two call sites are mutually
203 + * exclusive, so the hook must fire exactly once — but that only holds if both
204 + * sites agree, and a filter is free to be stateful or context-sensitive. Asking
205 + * it twice would let it answer differently and either fire the hook twice or
206 + * swallow it entirely. Ask once, remember the answer.
207 + *
208 + * @return string 'payment'|'details'
209 + */
210 + private function beforePaymentMethodsPlacement(): string
211 + {
212 + if ($this->paymentMethodsHookPlacement !== null) {
213 + return $this->paymentMethodsHookPlacement;
214 + }
215 +
216 + $placement = apply_filters('fluent_cart/modal_checkout/before_payment_methods_placement', 'payment', [
217 + 'cart' => $this->cart,
218 + ]);
219 +
220 + // Anything unrecognised falls back to the correct placement rather than
221 + // silently dropping the hook.
222 + $this->paymentMethodsHookPlacement = $placement === 'details' ? 'details' : 'payment';
223 +
224 + return $this->paymentMethodsHookPlacement;
225 + }
226 +
187 227 public function renderCheckoutDetails()
188 228 {
189 229 ?>
190 230 <div class="fct-modal-checkout-details">
@@ -199,10 +239,28 @@
199 239 <div class="fct_checkout_shipping_methods <?php echo $this->requireShipping ? '' : 'is-hidden' ?>">
200 240 <?php $this->checkoutRenderer->renderShippingOptions(); ?>
201 241 </div>
202 242
203 - <?php do_action('fluent_cart/before_payment_methods', ['cart' => $this->cart]); ?>
243 + <?php
244 + /**
245 + * The position `fluent_cart/before_payment_methods` used to
246 + * occupy in the modal. Given its own name so an add-on that
247 + * genuinely wants this pane — under shipping, above the terms
248 + * — has a stable place to render, instead of relying on a hook
249 + * whose name promises it sits before the payment methods.
250 + *
251 + * @param array $data ['cart' => Cart]
252 + */
253 + do_action('fluent_cart/modal_checkout/after_shipping_methods', ['cart' => $this->cart]);
204 254
255 + // Opt-in restoration of the old placement, for a store whose
256 + // add-on positioned itself around the previous spot. Mutually
257 + // exclusive with the call site in renderCheckoutBilling().
258 + if ($this->beforePaymentMethodsPlacement() === 'details') {
259 + do_action('fluent_cart/before_payment_methods', ['cart' => $this->cart]);
260 + }
261 + ?>
262 +
205 263 <?php $this->checkoutRenderer->agreeTerms(); ?>
206 264
207 265 <?php $this->renderSummaryGroup(); ?>
208 266
@@ -564,12 +622,35 @@
564 622 <?php echo esc_html__('Billing information', 'fluent-cart');?>
565 623 </h4>
566 624 </header>
567 625
626 + <?php
627 + /**
628 + * Fired here, wrapping the payment methods, exactly as
629 + * CheckoutRenderer does. It used to fire from
630 + * renderCheckoutDetails() — the address/shipping pane — which
631 + * is a different part of the modal entirely, so anything
632 + * hooked to it (the saved-payment-method picker, Turnstile)
633 + * rendered detached from the methods it belongs to. A hook
634 + * named "before payment methods" has to fire before the
635 + * payment methods.
636 + */
637 + if ($this->beforePaymentMethodsPlacement() === 'payment') {
638 + do_action('fluent_cart/before_payment_methods', ['cart' => $this->cart]);
639 + }
640 + ?>
641 +
568 642 <div class="fct_checkout_payment_methods" data-fluent-cart-checkout-payment-methods>
569 643 <?php $this->renderPaymentMethods(); ?>
570 644 </div>
571 645
646 + <?php
647 + // The modal never fired this at all, so add-ons rendering
648 + // below the methods (the save-my-card consent box) were
649 + // simply absent from quick checkout.
650 + do_action('fluent_cart/after_payment_methods', ['cart' => $this->cart]);
651 + ?>
652 +
572 653 <div class="fct-modal-checkout-btn-wrap">
573 654 <?php (new CheckoutRenderer($this->cart))->renderCheckoutButton(); ?>
574 655 </div>
575 656 </div>
@@ -608,8 +689,23 @@
608 689 $firstMethod = reset($activePaymentMethods);
609 690 $selectedPaymentMethod = $firstMethod ? $firstMethod->getMeta('route') : '';
610 691 }
611 692
693 + /**
694 + * The payment method rendered as checked. An add-on that renders its own
695 + * selector outside this list (e.g. saved payment methods) returns a route
696 + * that is not in the list, so no gateway here is checked — the page then
697 + * arrives with the add-on's choice already selected instead of a gateway
698 + * being checked first and switched over by JS after load.
699 + *
700 + * @param string $selectedPaymentMethod
701 + * @param array $context ['cart' => Cart, 'payment_methods' => array]
702 + */
703 + $selectedPaymentMethod = (string) apply_filters('fluent_cart/checkout/selected_payment_method', $selectedPaymentMethod, [
704 + 'cart' => $this->cart,
705 + 'payment_methods' => $activePaymentMethods
706 + ]);
707 +
612 708 $checkoutMethodStyle = $this->storeSettings->get('checkout_method_style', 'logo');
613 709
614 710 ?>
615 711 <div id="fluent_payment_methods" class="fct_modal_payment_methods fluent_payment_methods">
@@ -720,9 +816,9 @@
720 816 'route' => $route,
721 817 'method_title' => $methodTitle,
722 818 'method_style' => $methodStyle,
723 819 ];
724 - $paymentMethodClass = apply_filters_deprecated('fluent_cart_payment_method_list_class', ['', $pmContext], '1.3.16', 'fluent_cart/payment_method_list_class', 'Use fluent_cart/payment_method_list_class instead of fluent_cart_payment_method_list_class.');
820 + $paymentMethodClass = '';
725 821 $paymentMethodClass = apply_filters('fluent_cart/payment_method_list_class', $paymentMethodClass, $pmContext);
726 822
727 823 ?>
728 824 <div class="fluent-cart-checkout_embed_payment_wrapper">