PluginProbe
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler / 1.7.1
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler v1.7.1
1.7.1 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 All 51 releases
← All changes | app/Services/Renderer/ModalCheckoutRenderer.php +300 -51 1.3.23 → 1.7.1 View file →
@@ -3,19 +3,12 @@
3 3 namespace FluentCart\App\Services\Renderer;
4 4
5 5 use FluentCart\Api\PaymentMethods;
6 6 use FluentCart\Api\Resource\CustomerResource;
7 -use FluentCart\Api\Resource\FrontendResource\CustomerAddressResource;
8 7 use FluentCart\Api\StoreSettings;
9 -use FluentCart\App\App;
10 -use FluentCart\App\Helpers\AddressHelper;
11 -use FluentCart\App\Helpers\CartHelper;
12 8 use FluentCart\App\Helpers\Helper;
13 9 use FluentCart\App\Models\Cart;
14 -use FluentCart\App\Models\ProductVariation;
15 -use FluentCart\App\Services\Localization\LocalizationManager;
16 10 use FluentCart\App\Services\URL;
17 -use FluentCart\App\Vite;
18 11 use FluentCart\Framework\Support\Arr;
19 12 use FluentCart\App\Models\ProductMeta;
20 13
21 14 class ModalCheckoutRenderer
@@ -39,8 +32,16 @@
39 32 private $cartSummaryRenderer;
40 33
41 34 private $customer;
42 35
36 + /**
37 + * Memoised result of the before_payment_methods placement filter. Null until
38 + * resolved; see beforePaymentMethodsPlacement() for why it is asked only once.
39 + *
40 + * @var string|null
41 + */
42 + private $paymentMethodsHookPlacement = null;
43 +
43 44 public function __construct(Cart $cart, $config = [])
44 45 {
45 46 $this->cart = $cart;
46 47 $this->config = $config;
@@ -66,9 +67,10 @@
66 67 public function getFragment($fragmentName)
67 68 {
68 69 $maps = [
69 70 'payment_methods' => 'renderPaymentMethods',
70 - 'summary_group' => 'renderSummaryGroup'
71 + 'summary_group' => 'renderSummaryGroup',
72 + 'checkout_summary' => 'renderCheckoutSummary'
71 73 ];
72 74
73 75 if(isset($maps[$fragmentName])) {
74 76 ob_start();
@@ -110,8 +112,28 @@
110 112 }
111 113
112 114 public function renderForm()
113 115 {
116 + // Without a billing country in checkout_data, the tax recalc below
117 + // produces no per-item tax. Pre-fill from the current customer's
118 + // primary billing address (if any) so the per-item tax badge can
119 + // render on the very first modal open instead of only after the
120 + // customer interacts with the address form.
121 + $this->maybePopulateFormDataFromCustomer();
122 +
123 + // Trigger the recalc only when the cart hasn't been taxed yet. Once
124 + // line_meta.tax_config is on the item, the badge already renders from
125 + // existing data — another recalc here would just duplicate work
126 + // (Tax + Shipping each call $cart->save() on this action).
127 + // Payload shape matches Cart::addItem() so every handler receives the
128 + // data it expects.
129 + if (empty(Arr::get($this->cart->cart_data, '0.line_meta.tax_config'))) {
130 + do_action('fluent_cart/cart/cart_data_items_updated', [
131 + 'cart' => $this->cart,
132 + 'scope' => 'modal_open',
133 + 'scope_data' => null,
134 + ]);
135 + }
114 136 ?>
115 137 <div class="fct-modal-checkout-form-wrapper" data-fluent-cart-checkout-page>
116 138 <form
117 139 class="fct-modal-checkout-form"
@@ -130,13 +152,81 @@
130 152 <?php
131 153 }
132 154
133 155
134 - public function renderCheckoutDetails()
156 + private function maybePopulateFormDataFromCustomer()
135 157 {
158 + if (!$this->customer) {
159 + return;
160 + }
161 +
162 + $checkoutData = is_array($this->cart->checkout_data) ? $this->cart->checkout_data : [];
163 + $formData = Arr::get($checkoutData, 'form_data', []);
164 +
165 + // Never clobber an answer the customer has actively chosen; only
166 + // pre-fill when the cart has no billing country yet.
167 + if (!empty(Arr::get($formData, 'billing_country'))) {
168 + return;
169 + }
170 +
171 + $primaryBilling = $this->customer->primary_billing_address;
172 + if (!$primaryBilling) {
173 + return;
174 + }
175 +
176 + foreach ($primaryBilling->getFormattedDataForCheckout('billing_') as $key => $value) {
177 + if (!isset($formData[$key]) || $formData[$key] === '' || $formData[$key] === null) {
178 + $formData[$key] = $value;
179 + }
180 + }
181 +
182 + $checkoutData['form_data'] = $formData;
183 + $this->cart->checkout_data = $checkoutData;
184 + // No explicit save — the recalc action that follows persists this
185 + // together with the newly computed tax data, avoiding a double write.
186 + }
187 +
188 + /**
189 + * Where `fluent_cart/before_payment_methods` fires in the modal.
190 + *
191 + * 'payment' (default) wraps the payment-method list, matching CheckoutRenderer.
192 + * 'details' restores the pre-fix position in the address/shipping pane, for a
193 + * store whose add-on positioned itself around that spot.
194 + *
195 + * Resolved ONCE per renderer and memoised. The two call sites are mutually
196 + * exclusive, so the hook must fire exactly once — but that only holds if both
197 + * sites agree, and a filter is free to be stateful or context-sensitive. Asking
198 + * it twice would let it answer differently and either fire the hook twice or
199 + * swallow it entirely. Ask once, remember the answer.
200 + *
201 + * @return string 'payment'|'details'
202 + */
203 + private function beforePaymentMethodsPlacement(): string
204 + {
205 + if ($this->paymentMethodsHookPlacement !== null) {
206 + return $this->paymentMethodsHookPlacement;
207 + }
208 +
209 + $placement = apply_filters('fluent_cart/modal_checkout/before_payment_methods_placement', 'payment', [
210 + 'cart' => $this->cart,
211 + ]);
212 +
213 + // Anything unrecognised falls back to the correct placement rather than
214 + // silently dropping the hook.
215 + $this->paymentMethodsHookPlacement = $placement === 'details' ? 'details' : 'payment';
216 +
217 + return $this->paymentMethodsHookPlacement;
218 + }
219 +
220 + public function renderCheckoutDetails()
221 + {
136 222 ?>
137 223 <div class="fct-modal-checkout-details">
138 - <?php $this->renderCheckoutSummary();?>
224 + <!-- Stable swap target: tax recalculation re-renders the item card
225 + here via the checkout_summary fragment. -->
226 + <div data-fct-modal-checkout-summary>
227 + <?php $this->renderCheckoutSummary(); ?>
228 + </div>
139 229
140 230 <?php $this->checkoutRenderer->renderAddressFields(); ?>
141 231
142 232 <div class="fct_checkout_shipping_methods <?php echo $this->requireShipping ? '' : 'is-hidden' ?>">
@@ -142,10 +232,28 @@
142 232 <div class="fct_checkout_shipping_methods <?php echo $this->requireShipping ? '' : 'is-hidden' ?>">
143 233 <?php $this->checkoutRenderer->renderShippingOptions(); ?>
144 234 </div>
145 235
146 - <?php do_action('fluent_cart/before_payment_methods', ['cart' => $this->cart]); ?>
236 + <?php
237 + /**
238 + * The position `fluent_cart/before_payment_methods` used to
239 + * occupy in the modal. Given its own name so an add-on that
240 + * genuinely wants this pane — under shipping, above the terms
241 + * — has a stable place to render, instead of relying on a hook
242 + * whose name promises it sits before the payment methods.
243 + *
244 + * @param array $data ['cart' => Cart]
245 + */
246 + do_action('fluent_cart/modal_checkout/after_shipping_methods', ['cart' => $this->cart]);
147 247
248 + // Opt-in restoration of the old placement, for a store whose
249 + // add-on positioned itself around the previous spot. Mutually
250 + // exclusive with the call site in renderCheckoutBilling().
251 + if ($this->beforePaymentMethodsPlacement() === 'details') {
252 + do_action('fluent_cart/before_payment_methods', ['cart' => $this->cart]);
253 + }
254 + ?>
255 +
148 256 <?php $this->checkoutRenderer->agreeTerms(); ?>
149 257
150 258 <?php $this->renderSummaryGroup(); ?>
151 259
@@ -157,11 +265,24 @@
157 265 public function renderCheckoutSummary()
158 266 {
159 267 $title = Arr::get($this->cart->cart_data, '0.title', '');
160 268 $postTitle = Arr::get($this->cart->cart_data, '0.post_title', '');
161 - $subTotal = Helper::toDecimal(Arr::get($this->cart->cart_data, '0.subtotal', 0));
162 - $media = Arr::get($this->cart->cart_data, '0.featured_media', '');
269 + $subTotal = Helper::toDecimal(Arr::get($this->cart->cart_data, '0.subtotal', 0));
270 + $couponDiscount = (int) Arr::get($this->cart->cart_data, '0.coupon_discount', 0);
271 + $subtotalRaw = (int) Arr::get($this->cart->cart_data, '0.subtotal', 0);
272 + $lineTotalRaw = (int) Arr::get($this->cart->cart_data, '0.line_total', 0);
273 + $hasCouponDiscount = $couponDiscount > 0 && $lineTotalRaw < $subtotalRaw;
274 + $media = Helper::productImageOrPlaceholder(Arr::get($this->cart->cart_data, '0.featured_media', ''));
163 275
276 + // Mirror CartItemRenderer's event payload so the shared line-item hooks
277 + // (e.g. the per-item tax breakdown) also fire in modal checkout.
278 + $lineItemEventInfo = [
279 + 'item' => Arr::get($this->cart->cart_data, '0', []),
280 + 'cart' => $this->cart,
281 + 'product' => null,
282 + 'variant' => null,
283 + ];
284 +
164 285 ?>
165 286 <div class="fct-modal-checkout-summary">
166 287 <div class="fct-modal-cs-img">
167 288 <img src="<?php echo esc_url($media);?>" alt="<?php echo esc_attr($postTitle);?>">
@@ -177,13 +298,27 @@
177 298 - <?php echo esc_html($title);?>
178 299 </h3>
179 300 </div>
180 301
181 - <span class="fct-modal-cs-line-price">
182 - <?php echo $subTotal;?>
183 - </span>
302 + <?php if ($hasCouponDiscount) : ?>
303 + <div class="fct-modal-cs-price-wrapper">
304 + <span class="fct-modal-cs-line-price fct-modal-cs-line-price--original" aria-label="<?php esc_attr_e('Original price', 'fluent-cart'); ?>">
305 + <?php echo esc_html(Helper::toDecimal($subtotalRaw)); ?>
306 + </span>
307 + <span class="fct-modal-cs-line-price fct-modal-cs-line-price--discounted" aria-label="<?php esc_attr_e('Discounted price', 'fluent-cart'); ?>">
308 + <?php echo esc_html(Helper::toDecimal($lineTotalRaw)); ?>
309 + </span>
310 + </div>
311 + <?php else : ?>
312 + <span class="fct-modal-cs-line-price">
313 + <?php echo esc_html($subTotal); ?>
314 + </span>
315 + <?php endif; ?>
316 + <?php do_action('fluent_cart/cart/line_item/after_total', $lineItemEventInfo); ?>
184 317 </div>
185 318
319 + <?php do_action('fluent_cart/cart/line_item/footer_start', $lineItemEventInfo); ?>
320 +
186 321 <?php $this->renderPaymentTypeInfo(); ?>
187 322
188 323 <?php if($this->matchedVariation) :?>
189 324 <div class="fct-modal-cs-license">
@@ -211,9 +346,8 @@
211 346 $otherInfo = Arr::get($this->cart->cart_data, '0.other_info', []);
212 347 $paymentType = Arr::get($otherInfo, 'payment_type', '');
213 348 $itemPrice = Arr::get($this->cart->cart_data, '0.unit_price', 0);
214 349
215 -
216 350 if ($paymentType === 'subscription') {
217 351 $subscriptionInfo = Helper::generateSubscriptionInfo($otherInfo, $itemPrice);
218 352 $setupFeeInfo = Helper::generateSetupFeeInfo($otherInfo);
219 353 $trialInfo = Helper::generateTrialInfo($otherInfo);
@@ -239,9 +373,31 @@
239 373 </span>
240 374 <?php endif; ?>
241 375 </div>
242 376 <?php
377 + return;
243 378 }
379 +
380 + $quantity = (int) Arr::get($this->cart->cart_data, '0.quantity', 1);
381 + if ($quantity < 2) {
382 + return;
383 + }
384 +
385 + $lineItemEventInfo = [
386 + 'item' => Arr::get($this->cart->cart_data, '0', []),
387 + 'cart' => $this->cart,
388 + 'product' => null,
389 + 'variant' => null,
390 + ];
391 + ?>
392 + <div class="fct-modal-cs-payment-info">
393 + <?php
394 + /* translators: %1$s: formatted unit price */
395 + printf(esc_html__('%1$s each', 'fluent-cart'), esc_html(Helper::toDecimal($itemPrice)));
396 + ?>
397 + <?php do_action('fluent_cart/cart/line_item/unit_price_hint', $lineItemEventInfo); ?>
398 + </div>
399 + <?php
244 400 }
245 401
246 402 public function renderPromoCode() {
247 403 ?>
@@ -374,8 +530,58 @@
374 530
375 531 }
376 532
377 533
534 + /**
535 + * First/Last name inputs for the modal. Enabling either in Checkout Fields
536 + * replaces the single Full name input. The field definitions come from
537 + * CheckoutFieldsSchema, shared with the regular checkout; the modal only
538 + * moves the label out of the placeholder.
539 + *
540 + * First name is always rendered and required, because checkout validation
541 + * always asks for it in this mode. That keeps an older saved configuration
542 + * with only Last name enabled working.
543 + *
544 + * @return array[] field configs for FormFieldRenderer::renderField()
545 + */
546 + private function getFirstLastNameFields(): array
547 + {
548 + $schema = CheckoutFieldsSchema::getNameEmailFieldsSchema($this->cart);
549 + $fields = [];
550 +
551 + foreach (['billing_first_name', 'billing_last_name'] as $name) {
552 + $field = Arr::get($schema, 'fields.' . $name);
553 + if ($name === 'billing_first_name') {
554 + $field = wp_parse_args($field ?: [], [
555 + 'name' => $name,
556 + 'id' => $name,
557 + 'type' => 'text',
558 + 'data-type' => 'text',
559 + 'aria-label' => __('First Name', 'fluent-cart'),
560 + 'autocomplete' => 'given-name',
561 + 'value' => '',
562 + ]);
563 + $field['required'] = 'yes';
564 + }
565 +
566 + if (!$field) {
567 + continue;
568 + }
569 +
570 + $field['label'] = Arr::get($field, 'aria-label') . (Arr::get($field, 'required') ? ' *' : '');
571 + $field['placeholder'] = $name === 'billing_first_name' ? __('Jon', 'fluent-cart') : __('Doe', 'fluent-cart');
572 +
573 + $customerValue = $this->customer->{str_replace('billing_', '', $name)} ?? '';
574 + if ($customerValue) {
575 + $field['value'] = $customerValue;
576 + }
577 +
578 + $fields[] = $field;
579 + }
580 +
581 + return $fields;
582 + }
583 +
378 584 public function renderCheckoutBilling() {
379 585 $user = wp_get_current_user();
380 586 $formRender = new FormFieldRenderer();
381 587 $fullName = trim(
@@ -392,31 +598,44 @@
392 598 $emailField = Arr::get($fieldsSchema, 'basic_info.email', []);
393 599 $isRequiredFullName = Arr::get($fullNameField, 'required', 'no') === 'yes' ? 'yes' : '';
394 600 $isRequiredEmail = Arr::get($emailField, 'required', 'no') === 'yes' ? 'yes' : '';
395 601
602 + $isFullName = CheckoutFieldsSchema::isFullNameRequired();
603 + $firstLastNameFields = $isFullName ? [] : $this->getFirstLastNameFields();
604 + // First and last name share one row, so email gets the next row to itself.
605 + $emailColClass = count($firstLastNameFields) === 2 ? 'col-12' : 'col-6';
606 +
396 607 ?>
397 608 <div class="fct-modal-checkout-billing-wrap">
398 609 <!-- Account Details -->
399 610 <div class="fct-modal-account-details" data-fct-checkout-form-section>
400 611 <div class="fct-modal-form-info">
401 - <div class="fct-modal-form-field col-6">
612 + <?php if ($isFullName) : ?>
613 + <div class="fct-modal-form-field col-6">
614 + <?php
615 + $formRender->renderField([
616 + 'label' => esc_attr__('Full name', 'fluent-cart') . ($isRequiredFullName ? ' *' : ''),
617 + 'id' => 'billing_full_name',
618 + 'type' => 'text',
619 + 'placeholder' => __('Jon Doe', 'fluent-cart'),
620 + 'name' => 'billing_full_name',
621 + 'autocomplete' => 'given-name',
622 + 'aria-label' => esc_attr__('Full name', 'fluent-cart'),
623 + 'required' => $isRequiredFullName,
624 + 'value' => $fullName
625 + ]);
626 + ?>
627 + </div>
628 + <?php else : ?>
629 + <?php foreach ($firstLastNameFields as $nameField) : ?>
630 + <div class="fct-modal-form-field col-6">
631 + <?php $formRender->renderField($nameField); ?>
632 + </div>
633 + <?php endforeach; ?>
634 + <?php endif; ?>
635 + <div class="fct-modal-form-field <?php echo esc_attr($emailColClass); ?>">
402 636 <?php
403 637 $formRender->renderField([
404 - 'label' => esc_attr__('Full name', 'fluent-cart') . ($isRequiredFullName ? ' *' : ''),
405 - 'id' => 'billing_full_name',
406 - 'type' => 'text',
407 - 'placeholder' => __('Jon Doe', 'fluent-cart'),
408 - 'name' => 'billing_full_name',
409 - 'autocomplete' => 'given-name',
410 - 'aria-label' => esc_attr__('Full name', 'fluent-cart'),
411 - 'required' => $isRequiredFullName,
412 - 'value' => $fullName
413 - ]);
414 - ?>
415 - </div>
416 - <div class="fct-modal-form-field col-6">
417 - <?php
418 - $formRender->renderField([
419 638 'label' => esc_attr__('Email address', 'fluent-cart') . ($isRequiredEmail ? ' *' : ''),
420 639 'id' => 'billing_email',
421 640 'type' => 'text',
422 641 'placeholder' => '[email protected]',
@@ -428,23 +647,8 @@
428 647 'disabled' => (bool)($this->customer->email ?? $user->user_email)
429 648 ]);
430 649 ?>
431 650 </div>
432 -
433 -<!-- <div class="fct-modal-form-field col-3">-->
434 -<!-- --><?php
435 -// $formRender->renderField([
436 -// 'label' => __('Last name', 'fluent-cart'),
437 -// 'id' => 'billing_last_name',
438 -// 'type' => 'text',
439 -// 'placeholder' => __('Doe', 'fluent-cart'),
440 -// 'name' => 'billing_last_name',
441 -// 'autocomplete' => 'given-name',
442 -// 'required' => true,
443 -// 'value' => $this->customer->last_name ?? ''
444 -// ]);
445 -// ?>
446 -<!-- </div>-->
447 651 </div>
448 652
449 653 <?php $this->checkoutRenderer->renderCreateAccountField(); ?>
450 654
@@ -459,12 +663,35 @@
459 663 <?php echo esc_html__('Billing information', 'fluent-cart');?>
460 664 </h4>
461 665 </header>
462 666
667 + <?php
668 + /**
669 + * Fired here, wrapping the payment methods, exactly as
670 + * CheckoutRenderer does. It used to fire from
671 + * renderCheckoutDetails() — the address/shipping pane — which
672 + * is a different part of the modal entirely, so anything
673 + * hooked to it (the saved-payment-method picker, Turnstile)
674 + * rendered detached from the methods it belongs to. A hook
675 + * named "before payment methods" has to fire before the
676 + * payment methods.
677 + */
678 + if ($this->beforePaymentMethodsPlacement() === 'payment') {
679 + do_action('fluent_cart/before_payment_methods', ['cart' => $this->cart]);
680 + }
681 + ?>
682 +
463 683 <div class="fct_checkout_payment_methods" data-fluent-cart-checkout-payment-methods>
464 684 <?php $this->renderPaymentMethods(); ?>
465 685 </div>
466 686
687 + <?php
688 + // The modal never fired this at all, so add-ons rendering
689 + // below the methods (the save-my-card consent box) were
690 + // simply absent from quick checkout.
691 + do_action('fluent_cart/after_payment_methods', ['cart' => $this->cart]);
692 + ?>
693 +
467 694 <div class="fct-modal-checkout-btn-wrap">
468 695 <?php (new CheckoutRenderer($this->cart))->renderCheckoutButton(); ?>
469 696 </div>
470 697 </div>
@@ -483,8 +710,9 @@
483 710 }
484 711
485 712 $selectedPaymentMethod = Arr::get($this->cart->checkout_data, 'form_data._fct_pay_method', '');
486 713 $activePaymentMethods = PaymentMethods::getActiveMethodInstance($this->cart);
714 + $hadActiveMethods = !empty($activePaymentMethods);
487 715
488 716 $activePaymentMethods = apply_filters('fluent_cart/checkout_active_payment_methods', $activePaymentMethods, [
489 717 'cart' => $this->cart
490 718 ]);
@@ -497,11 +725,28 @@
497 725 });
498 726 }
499 727
500 728 if (!$selectedPaymentMethod && !empty($activePaymentMethods)) {
501 - $selectedPaymentMethod = $activePaymentMethods[0] ? $activePaymentMethods[0]->getMeta('route') : '';
729 + // reset() not [0] — array_filter above preserves keys, so key 0 may be gone.
730 + $firstMethod = reset($activePaymentMethods);
731 + $selectedPaymentMethod = $firstMethod ? $firstMethod->getMeta('route') : '';
502 732 }
503 733
734 + /**
735 + * The payment method rendered as checked. An add-on that renders its own
736 + * selector outside this list (e.g. saved payment methods) returns a route
737 + * that is not in the list, so no gateway here is checked — the page then
738 + * arrives with the add-on's choice already selected instead of a gateway
739 + * being checked first and switched over by JS after load.
740 + *
741 + * @param string $selectedPaymentMethod
742 + * @param array $context ['cart' => Cart, 'payment_methods' => array]
743 + */
744 + $selectedPaymentMethod = (string) apply_filters('fluent_cart/checkout/selected_payment_method', $selectedPaymentMethod, [
745 + 'cart' => $this->cart,
746 + 'payment_methods' => $activePaymentMethods
747 + ]);
748 +
504 749 $checkoutMethodStyle = $this->storeSettings->get('checkout_method_style', 'logo');
505 750
506 751 ?>
507 752 <div id="fluent_payment_methods" class="fct_modal_payment_methods fluent_payment_methods">
@@ -519,9 +764,13 @@
519 764 ]);
520 765 } ?>
521 766 <?php else: ?>
522 767 <?php
523 - $emptyText = esc_html__('No Payment method is activated for this site yet.', 'fluent-cart');
768 + if ($hadActiveMethods) {
769 + $emptyText = esc_html__('None of the available payment methods can process this order. Please contact the store.', 'fluent-cart');
770 + } else {
771 + $emptyText = esc_html__('No Payment method is activated for this site yet.', 'fluent-cart');
772 + }
524 773 if (current_user_can('manage_options')) {
525 774 $emptyText .= '<a href="' . esc_url(URL::getDashboardUrl('settings/payments')) . '" target="_blank">' . esc_html__('Activate from settings.', 'fluent-cart') . '</a>';
526 775 }
527 776 echo '<div class="fct-empty-state">' . wp_kses_post($emptyText) . '</div>';
@@ -608,9 +857,9 @@
608 857 'route' => $route,
609 858 'method_title' => $methodTitle,
610 859 'method_style' => $methodStyle,
611 860 ];
612 - $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.');
861 + $paymentMethodClass = '';
613 862 $paymentMethodClass = apply_filters('fluent_cart/payment_method_list_class', $paymentMethodClass, $pmContext);
614 863
615 864 ?>
616 865 <div class="fluent-cart-checkout_embed_payment_wrapper">