cart = $cart; $this->config = $config; $this->requireShipping = $cart->requireShipping(); $this->storeSettings = new StoreSettings(); $this->checkoutRenderer = new CheckoutRenderer($cart); $this->cartSummaryRenderer = new CartSummaryRender($cart); $this->hasSubscription = $cart->hasSubscription(); $this->customer = CustomerResource::getCurrentCustomer(); $this->productMeta = ProductMeta::query()->where('object_id', Arr::get($this->cart->cart_data, '0.post_id'))->where('meta_key', 'license_settings')->first(); $variations = $this->productMeta->meta_value['variations'] ?? []; $cartItem = $this->cart->cart_data[0] ?? []; $objectId = $cartItem['object_id'] ?? null; $this->matchedVariation = $objectId && isset($variations[$objectId]) ? $variations[$objectId] : null; } public function getFragment($fragmentName) { $maps = [ 'payment_methods' => 'renderPaymentMethods', 'summary_group' => 'renderSummaryGroup', 'checkout_summary' => 'renderCheckoutSummary' ]; if(isset($maps[$fragmentName])) { ob_start(); $this->{$maps[$fragmentName]}(); return ob_get_clean(); } return ''; } public function render() { ?> maybePopulateFormDataFromCustomer(); // Trigger the recalc only when the cart hasn't been taxed yet. Once // line_meta.tax_config is on the item, the badge already renders from // existing data — another recalc here would just duplicate work // (Tax + Shipping each call $cart->save() on this action). // Payload shape matches Cart::addItem() so every handler receives the // data it expects. if (empty(Arr::get($this->cart->cart_data, '0.line_meta.tax_config'))) { do_action('fluent_cart/cart/cart_data_items_updated', [ 'cart' => $this->cart, 'scope' => 'modal_open', 'scope_data' => null, ]); } ?>
renderCheckoutDetails(); ?> renderCheckoutBilling(); ?>
customer) { return; } $checkoutData = is_array($this->cart->checkout_data) ? $this->cart->checkout_data : []; $formData = Arr::get($checkoutData, 'form_data', []); // Never clobber an answer the customer has actively chosen; only // pre-fill when the cart has no billing country yet. if (!empty(Arr::get($formData, 'billing_country'))) { return; } $primaryBilling = $this->customer->primary_billing_address; if (!$primaryBilling) { return; } foreach ($primaryBilling->getFormattedDataForCheckout('billing_') as $key => $value) { if (!isset($formData[$key]) || $formData[$key] === '' || $formData[$key] === null) { $formData[$key] = $value; } } $checkoutData['form_data'] = $formData; $this->cart->checkout_data = $checkoutData; // No explicit save — the recalc action that follows persists this // together with the newly computed tax data, avoiding a double write. } /** * Where `fluent_cart/before_payment_methods` fires in the modal. * * 'payment' (default) wraps the payment-method list, matching CheckoutRenderer. * 'details' restores the pre-fix position in the address/shipping pane, for a * store whose add-on positioned itself around that spot. * * Resolved ONCE per renderer and memoised. The two call sites are mutually * exclusive, so the hook must fire exactly once — but that only holds if both * sites agree, and a filter is free to be stateful or context-sensitive. Asking * it twice would let it answer differently and either fire the hook twice or * swallow it entirely. Ask once, remember the answer. * * @return string 'payment'|'details' */ private function beforePaymentMethodsPlacement(): string { if ($this->paymentMethodsHookPlacement !== null) { return $this->paymentMethodsHookPlacement; } $placement = apply_filters('fluent_cart/modal_checkout/before_payment_methods_placement', 'payment', [ 'cart' => $this->cart, ]); // Anything unrecognised falls back to the correct placement rather than // silently dropping the hook. $this->paymentMethodsHookPlacement = $placement === 'details' ? 'details' : 'payment'; return $this->paymentMethodsHookPlacement; } public function renderCheckoutDetails() { ?>
renderCheckoutSummary(); ?>
checkoutRenderer->renderAddressFields(); ?>
checkoutRenderer->renderShippingOptions(); ?>
Cart] */ do_action('fluent_cart/modal_checkout/after_shipping_methods', ['cart' => $this->cart]); // Opt-in restoration of the old placement, for a store whose // add-on positioned itself around the previous spot. Mutually // exclusive with the call site in renderCheckoutBilling(). if ($this->beforePaymentMethodsPlacement() === 'details') { do_action('fluent_cart/before_payment_methods', ['cart' => $this->cart]); } ?> checkoutRenderer->agreeTerms(); ?> renderSummaryGroup(); ?> renderCheckoutReviews();?>
cart->cart_data, '0.title', ''); $postTitle = Arr::get($this->cart->cart_data, '0.post_title', ''); $subTotal = Helper::toDecimal(Arr::get($this->cart->cart_data, '0.subtotal', 0)); $couponDiscount = (int) Arr::get($this->cart->cart_data, '0.coupon_discount', 0); $subtotalRaw = (int) Arr::get($this->cart->cart_data, '0.subtotal', 0); $lineTotalRaw = (int) Arr::get($this->cart->cart_data, '0.line_total', 0); $hasCouponDiscount = $couponDiscount > 0 && $lineTotalRaw < $subtotalRaw; $media = Arr::get($this->cart->cart_data, '0.featured_media', ''); // Mirror CartItemRenderer's event payload so the shared line-item hooks // (e.g. the per-item tax breakdown) also fire in modal checkout. $lineItemEventInfo = [ 'item' => Arr::get($this->cart->cart_data, '0', []), 'cart' => $this->cart, 'product' => null, 'variant' => null, ]; ?>
<?php echo esc_attr($postTitle);?>

-

renderPaymentTypeInfo(); ?> matchedVariation) :?>
matchedVariation, 'activation_limit'); if (empty($limit) || (int) $limit === 0) { echo esc_html__('Unlimited Activation', 'fluent-cart'); } else { echo esc_html(sprintf(__('Activation limit %d', 'fluent-cart'), $limit)); } ?>
cart->cart_data, '0.other_info', []); $paymentType = Arr::get($otherInfo, 'payment_type', ''); $itemPrice = Arr::get($this->cart->cart_data, '0.unit_price', 0); if ($paymentType === 'subscription') { $subscriptionInfo = Helper::generateSubscriptionInfo($otherInfo, $itemPrice); $setupFeeInfo = Helper::generateSetupFeeInfo($otherInfo); $trialInfo = Helper::generateTrialInfo($otherInfo); ?>
true]); ?>, ,
cart->cart_data, '0.quantity', 1); if ($quantity < 2) { return; } $lineItemEventInfo = [ 'item' => Arr::get($this->cart->cart_data, '0', []), 'cart' => $this->cart, 'product' => null, 'variant' => null, ]; ?>
cartSummaryRenderer->renderItemsFooter(); ?>
cart->getDiscountLines(); if (!$discounts) { return; } ?>
$discount_data): ?>
I got the annual unlimited websites premium version and I'm upgrading to the Lifetime Unlimited Websites plan real soon. Building WordPress website
Bryson Suleiman, Founder and WordPress Expert at Webbryson
customer->first_name ?? '') . ' ' . ($this->customer->last_name ?? '') ); if (!$fullName && $user) { $fullName = $user->display_name; } $fieldsSchema = CheckoutFieldsSchema::getFieldsSettings(); $fullNameField = Arr::get($fieldsSchema, 'basic_info.full_name', []); $emailField = Arr::get($fieldsSchema, 'basic_info.email', []); $isRequiredFullName = Arr::get($fullNameField, 'required', 'no') === 'yes' ? 'yes' : ''; $isRequiredEmail = Arr::get($emailField, 'required', 'no') === 'yes' ? 'yes' : ''; ?>
renderField([ 'label' => esc_attr__('Full name', 'fluent-cart') . ($isRequiredFullName ? ' *' : ''), 'id' => 'billing_full_name', 'type' => 'text', 'placeholder' => __('Jon Doe', 'fluent-cart'), 'name' => 'billing_full_name', 'autocomplete' => 'given-name', 'aria-label' => esc_attr__('Full name', 'fluent-cart'), 'required' => $isRequiredFullName, 'value' => $fullName ]); ?>
renderField([ 'label' => esc_attr__('Email address', 'fluent-cart') . ($isRequiredEmail ? ' *' : ''), 'id' => 'billing_email', 'type' => 'text', 'placeholder' => 'jon.doe@example.com', 'name' => 'billing_email', 'autocomplete' => 'email', 'required' => $isRequiredEmail, 'aria-label' => esc_attr__('Email address', 'fluent-cart'), 'value' => $this->customer->email ?? $user->user_email, 'disabled' => (bool)($this->customer->email ?? $user->user_email) ]); ?>
renderField([ // 'label' => __('Last name', 'fluent-cart'), // 'id' => 'billing_last_name', // 'type' => 'text', // 'placeholder' => __('Doe', 'fluent-cart'), // 'name' => 'billing_last_name', // 'autocomplete' => 'given-name', // 'required' => true, // 'value' => $this->customer->last_name ?? '' // ]); // ?>
checkoutRenderer->renderCreateAccountField(); ?>

beforePaymentMethodsPlacement() === 'payment') { do_action('fluent_cart/before_payment_methods', ['cart' => $this->cart]); } ?>
renderPaymentMethods(); ?>
$this->cart]); ?>
cart))->renderCheckoutButton(); ?>
cart->getEstimatedTotal() <= 0) { if (!$this->cart->hasSubscription() || $this->cart->getEstimatedRecurringTotal() <= 0) { return ''; } } $selectedPaymentMethod = Arr::get($this->cart->checkout_data, 'form_data._fct_pay_method', ''); $activePaymentMethods = PaymentMethods::getActiveMethodInstance($this->cart); $hadActiveMethods = !empty($activePaymentMethods); $activePaymentMethods = apply_filters('fluent_cart/checkout_active_payment_methods', $activePaymentMethods, [ 'cart' => $this->cart ]); // filter the active payment methods to only include the selected payment method $selectedModalPaymentMethod = apply_filters('fluent_cart/modal_checkout/filter_active_payment_methods', []); if (!empty($selectedModalPaymentMethod)) { $activePaymentMethods = array_filter($activePaymentMethods, function ($method) use ($selectedModalPaymentMethod) { return in_array($method->getMeta('route'), $selectedModalPaymentMethod); }); } if (!$selectedPaymentMethod && !empty($activePaymentMethods)) { // reset() not [0] — array_filter above preserves keys, so key 0 may be gone. $firstMethod = reset($activePaymentMethods); $selectedPaymentMethod = $firstMethod ? $firstMethod->getMeta('route') : ''; } $checkoutMethodStyle = $this->storeSettings->get('checkout_method_style', 'logo'); ?>
getMeta('route')); $this->renderPaymentMethod($method, [ 'selected_id' => $selectedPaymentMethod, 'style' => $checkoutMethodStyle, 'aria_checked' => $isSelected ? 'true' : 'false', 'role' => 'radio' ]); } ?> ' . esc_html__('Activate from settings.', 'fluent-cart') . ''; } echo '
' . wp_kses_post($emptyText) . '
'; ?>
renderPaymentMethodContent($method, [ 'style' => $checkoutMethodStyle ]); } ?>
getMeta('route'); $methodTitle = $method->getMeta('title'); $methodStyle = Arr::get($config, 'style', 'logo'); $inputAttributes = array_filter([ 'class' => 'form-radio-input', 'type' => 'radio', 'name' => '_fct_pay_method', 'id' => 'fluent_cart_payment_method_' . $route, 'value' => $route, 'required' => true, 'checked' => $route === Arr::get($config, 'selected_id', '') ? 'true' : '', 'role' => Arr::get($config, 'role', 'radio'), 'aria-checked' => Arr::get($config, 'aria_checked', 'false'), ]); $wrapperClass = $methodStyle === 'logo' ? 'fct_payment_method_logo' : 'fct_payment_method'; $wrapperAttributes = [ 'class' => $wrapperClass . ' ' . 'fct_payment_method_wrapper fct_payment_method_' . $route, 'tabindex' => '0', 'role' => 'presentation' ]; ?>
> />
getMeta('route'); $methodTitle = $method->getMeta('title'); $methodStyle = Arr::get($config, 'style', 'logo'); $pmContext = [ 'route' => $route, 'method_title' => $methodTitle, 'method_style' => $methodStyle, ]; $paymentMethodClass = ''; $paymentMethodClass = apply_filters('fluent_cart/payment_method_list_class', $paymentMethodClass, $pmContext); ?>
getMeta('instructions')): ?>
getMeta('instructions')); ?>
" aria-hidden="true"> $method, 'cart' => $this->cart, 'route' => $route ] ); ?>