cart = $cart;
$this->requireShipping = $cart->requireShipping();
$this->hasSubscription = $cart->hasSubscription();
$formData = Arr::get($cart->checkout_data, 'form_data', []);
$this->storeSettings = new StoreSettings();
$fallbackCountry = '';
$HTTP_CF_IP_COUNTRY = Arr::get(App::request()->server(), 'HTTP_CF_IPCOUNTRY');
if ($HTTP_CF_IP_COUNTRY) {
$fallbackCountry = $HTTP_CF_IP_COUNTRY;
}
$this->billingAddress = [
'full_name' => trim($this->cart->first_name . ' ' . $this->cart->last_name),
'country' => Arr::get($formData, 'billing_country', $fallbackCountry),
'address_1' => Arr::get($formData, 'billing_address_1', ''),
'address_2' => Arr::get($formData, 'billing_address_2', ''),
'city' => Arr::get($formData, 'billing_city', ''),
'state' => Arr::get($formData, 'billing_state', ''),
'postcode' => Arr::get($formData, 'billing_postcode', ''),
'company_name' => Arr::get($formData, 'billing_company_name', ''),
'phone' => Arr::get($formData, 'billing_phone', ''),
];
if ($this->requireShipping) {
if (Arr::get($formData, 'ship_to_different', '') === 'yes') {
$shippingCountry = Arr::get($formData, 'shipping_country', $fallbackCountry);
$shippingValidations = array_filter(CheckoutFieldsSchema::getCheckoutFieldsRequirements('shipping', 'physical'));
if (!Arr::has($shippingValidations, 'country')) {
$shippingCountry = Arr::get($formData, 'billing_country');
}
$this->shippingAddress = [
'full_name' => Arr::get($formData, 'shipping_full_name', ''),
'country' => $shippingCountry,
'address_1' => Arr::get($formData, 'shipping_address_1', ''),
'address_2' => Arr::get($formData, 'shipping_address_2', ''),
'city' => Arr::get($formData, 'shipping_city', ''),
'state' => Arr::get($formData, 'shipping_state', ''),
'postcode' => Arr::get($formData, 'shipping_postcode', ''),
'company_name' => Arr::get($formData, 'shipping_company_name', ''),
'phone' => Arr::get($formData, 'shipping_phone', ''),
];
} else {
$this->shippingAddress = $this->billingAddress;
}
}
$this->config = $config;
}
public function render($config = [])
{
if ($config) {
$this->config = wp_parse_args($config, $this->config);
}
$this->wrapperStart();
$this->renderNotices();
$this->renderCheckoutForm();
$this->wrapperEnd();
}
public function getFragment($fragmentName)
{
$maps = [
'shipping_methods' => 'renderShippingOptions',
'payment_methods' => 'renderPaymentMethods',
'cart_summary_fragment' => 'renderSummaryFragment'
];
if (isset($maps[$fragmentName])) {
ob_start();
$this->{$maps[$fragmentName]}();
return ob_get_clean();
}
return '';
}
public function wrapperStart()
{
$classNames = [
'fluent-cart-checkout-page',
'fct-checkout',
'fct-checkout-type-' . $this->cart->cart_group
];
$configClass = Arr::get($this->config, 'wrapper_class', '');
if ($configClass) {
$classNames[] = $configClass;
}
$classNames = apply_filters('fluent_cart/checkout_page_css_classes', $classNames, [
'cart' => $this->cart
]);
$classNames = array_filter(array_unique($classNames));
$atts = [
'class' => implode(' ', $classNames),
'data-fluent-cart-checkout-page' => '',
];
do_action('fluent_cart/before_checkout_page_start', [
'cart' => $this->cart
]);
?>
role="main"
aria-label="">
$this->cart
];
do_action('fluent_cart/after_checkout_page_start', $hookData);
}
public function renderNotices()
{
$notices = Arr::get($this->cart->checkout_data, '__cart_notices', []);
$hookedNotices = apply_filters('fluent_cart/checkout_page_notices', [], [
'cart' => $this->cart
]);
if (!$notices && !$hookedNotices) {
return;
}
?>
request));
$current_url = add_query_arg(App::request()->all(), $current_url);
$formAttributes = [
'method' => 'POST',
'data-fluent-cart-checkout-page-checkout-form' => '',
'class' => 'fct_checkout fluent-cart-checkout-page-checkout-form',
'action' => $current_url,
'enctype' => 'multipart/form-data',
];
do_action('fluent_cart/before_checkout_form', ['cart' => $this->cart]);
?>
$this->cart]);
}
public function wrapperEnd()
{
do_action('fluent_cart/before_checkout_page_close', [
'cart' => $this->cart
]);
?>
$this->cart
]);
}
public function renderCreateAccountField($atts = [])
{
//check store settings also
$attr_title = Arr::get($atts, 'title');
$extraClass = Arr::get($atts, 'wrapper_atts') ? '' : 'fct-has-default-font-size';
?>
storeSettings->get('user_account_creation_mode') === 'user_choice'): ?>
cart);
if (!$schema) {
return;
}
(new FormFieldRenderer())->renderSection($schema);
}
public function renderB2BToggle()
{
if (!CheckoutFieldsSchema::hasAnyBusinessFields()) {
return;
}
if (CheckoutFieldsSchema::isB2BOnlyMode()) {
$this->renderBusinessDetailsSection(true);
return;
}
$isB2B = Arr::get($this->cart->checkout_data, 'form_data.is_business', 'no') === 'yes';
$hasBusinessSection = CheckoutFieldsSchema::isCompanyNameEnabled()
|| CheckoutFieldsSchema::isLegalRegistrationIdEnabled()
|| CheckoutFieldsSchema::isVatNumberEnabled();
$extraAtts = [
'data-fluent-cart-b2b-toggle' => 'yes',
'aria-expanded' => $isB2B ? 'true' : 'false',
];
if ($hasBusinessSection) {
$extraAtts['aria-controls'] = 'fct_b2b_business_section';
}
?>
renderField([
'type' => 'checkbox',
'id' => 'is_business',
'name' => 'is_business',
'checkbox_value' => 'yes',
'label' => __('I am purchasing as a business', 'fluent-cart'),
'value' => $isB2B ? 'yes' : '',
'extra_atts' => $extraAtts,
]);
?>
renderBusinessDetailsSection($isB2B);
}
public function renderBusinessDetailsSection($isVisible)
{
$hasCompany = CheckoutFieldsSchema::isCompanyNameEnabled();
$hasLegalReg = CheckoutFieldsSchema::isLegalRegistrationIdEnabled();
$hasVat = CheckoutFieldsSchema::isVatNumberEnabled();
if (!$hasCompany && !$hasLegalReg && !$hasVat) {
return;
}
$formData = Arr::get($this->cart->checkout_data, 'form_data', []);
$isCompanyRequired = CheckoutFieldsSchema::isCompanyNameRequired();
$isLegalRequired = CheckoutFieldsSchema::isLegalRegistrationIdRequired();
$style = $isVisible ? '' : 'display:none';
?>
countries();
return [
'address_select' => [
'type' => 'address_select',
'address_type' => $type,
'options' => $allowedAddresses,
'title' => $addressLabel,
'label' => '',
'countries' => $countries,
'primary_address' => $primaryAddress,
'value' => Arr::get($primaryAddress, 'id'),
'requirements_fields' => $requirementsFields
]
];
}
return $fields;
}
return $fields;
}
public function renderAddressFields()
{
$requireShipping = $this->requireShipping;
// $formData = Arr::get($this->cart->checkout_data, 'form_data', []);
// $billingAddress = $this->billingAddress;
// $billingAddress['type'] = 'billing';
// $billingAddress['product_type'] = $requireShipping ? 'physical' : 'digital';
// $billingAddress['with_shipping'] = $requireShipping && Arr::get($formData, 'ship_to_different', '') !== 'yes';
// $billingAddress['billing_address_id'] = Arr::get($formData, 'billing_address_id', '');
// $billingFields = CheckoutFieldsSchema::getAddressBaseFields($billingAddress);
// $billingFields = $this->validateAddressField($billingAddress, $billingFields);
// foreach ($billingFields as &$field) {
// if(empty($field['wrapper_atts'])) {
// $field['wrapper_atts'] = [];
// }
// $field['wrapper_atts']['data-fluent-cart-checkout-page-form-input-wrapper'] = '';
// }
// $billingFields = $this->maybeRearrangeAddressFields($billingFields);
// $billingFields = apply_filters('fluent_cart/checkout_renderer/billing_fields', $billingFields, [
// 'checkout_renderer' => $this,
// 'cart' => $this->cart
// ]);
// $formRender = new FormFieldRenderer();
echo '';
$this->renderBillingAddressFields();
$this->renderB2BToggle();
if (!$requireShipping) {
echo '
';
return;
}
$this->renderShipToDifferentField();
do_action('fluent_cart/after_billing_fields_section', ['cart' => $this->cart]);
$this->renderShippingAddressFields();
echo '';
}
public function renderBillingAddressFields($section_title = '')
{
$formData = Arr::get($this->cart->checkout_data, 'form_data', []);
$requireShipping = $this->requireShipping;
$billingAddress = $this->billingAddress;
$billingAddress['type'] = 'billing';
$billingAddress['product_type'] = $requireShipping ? 'physical' : 'digital';
$billingAddress['with_shipping'] = $requireShipping && Arr::get($formData, 'ship_to_different', '') !== 'yes';
$billingAddress['billing_address_id'] = Arr::get($formData, 'billing_address_id', '');
$billingAddress['order_id'] = $this->cart->order_id ?? null;
$billingFields = CheckoutFieldsSchema::getAddressBaseFields($billingAddress);
$billingFields = $this->validateAddressField($billingAddress, $billingFields);
foreach ($billingFields as &$field) {
if (empty($field['wrapper_atts'])) {
$field['wrapper_atts'] = [];
}
$field['wrapper_atts']['data-fluent-cart-checkout-page-form-input-wrapper'] = '';
}
$billingFields = $this->maybeRearrangeAddressFields($billingFields);
$billingFields = apply_filters('fluent_cart/checkout_renderer/billing_fields', $billingFields, [
'checkout_renderer' => $this,
'cart' => $this->cart
]);
do_action('fluent_cart/before_billing_fields_section', ['cart' => $this->cart]);
$formRender = new FormFieldRenderer();
$title = __('Billing Address', 'fluent-cart');
if (!empty($section_title)) {
$title = $section_title;
}
$formRender->renderSection([
'id' => 'billing_address_section_section',
'type' => 'section',
'heading' => $title,
'fields' => $billingFields,
'wrapper_atts' => [
'data-fluent-cart-checkout-page-form-section' => '',
'role' => 'region',
'aria-label' => __('Billing Address', 'fluent-cart')
]
]);
}
public function renderShipToDifferentField($atts = [])
{
$formRender = new FormFieldRenderer();
$attr_title = Arr::get($atts, 'title');
$extraClass = Arr::get($atts, 'wrapper_atts') ? '' : 'fct-has-default-font-size';
$title = __('Ship to a different address?', 'fluent-cart');
if (!empty($attr_title)) {
$title = $attr_title;
}
?>
cart->checkout_data, 'form_data', []);
$formRender = new FormFieldRenderer();
$shippingAddress = $this->shippingAddress;
$shippingAddress['type'] = 'shipping';
$shippingAddress['product_type'] = 'physical';
$shippingAddress['shipping_address_id'] = Arr::get($formData, 'shipping_address_id', '');
$shippingFields = CheckoutFieldsSchema::getAddressBaseFields($shippingAddress);
$shippingFields = $this->validateAddressField($shippingAddress, $shippingFields);
$shippingFields = apply_filters('fluent_cart/checkout_renderer/shipping_fields', $shippingFields, [
'checkout_renderer' => $this,
'cart' => $this->cart
]);
foreach ($shippingFields as &$field) {
if (empty($field['wrapper_atts'])) {
$field['wrapper_atts'] = [];
}
$field['wrapper_atts']['data-fluent-cart-checkout-page-form-input-wrapper'] = '';
}
$shippingFields = $this->maybeRearrangeAddressFields($shippingFields);
$title = __('Shipping Address', 'fluent-cart');
if (!empty($section_title)) {
$title = $section_title;
}
do_action('fluent_cart/before_shipping_fields_section', ['cart' => $this->cart]);
$formRender->renderSection([
'id' => 'shipping_address_section_section',
'type' => 'section',
'heading' => $title,
'fields' => $shippingFields,
'wrapper_atts' => [
'data-fluent-cart-checkout-page-shipping-fields' => '',
'style' => Arr::get($this->cart->checkout_data, 'form_data.ship_to_different', '') === 'yes' ? '' : 'display:none',
'role' => 'region',
'aria-label' => __('Shipping Address', 'fluent-cart')
]
]);
do_action('fluent_cart/after_shipping_fields_section', ['cart' => $this->cart]);
}
public function renderOrderNoteField($attr_title = '')
{
if (
!$this->requireShipping &&
apply_filters('fluent_cart/disable_order_notes_for_digital_products', true, [
'cart' => $this->cart
])
) {
return;
}
$noteTitle = __('Leave a Note', 'fluent-cart');
if (!empty($attr_title)) {
$noteTitle = $attr_title;
}
$fieldId = 'order_notes';
(new FormFieldRenderer())->renderField([
'type' => 'textarea',
'id' => $fieldId,
'name' => 'order_notes',
'aria-label' => __('Order Notes', 'fluent-cart'),
'placeholder' => __('Notes about your order, e.g. Leave it at my doorstep.', 'fluent-cart'),
'extra_atts' => [
'rows' => 4
],
'wrapper_atts' => [
'data-fct-item-toggle' => '',
'class' => 'fct-toggle-field fct_order_note'
],
'before_callback' => function ($field) use ($fieldId, $noteTitle) {
$toggleId = 'order_notes_toggle';
$wrapperId = 'order_notes_wrapper';
?>
function ($field) {
echo '
';
}
]);
do_action('fluent_cart/after_order_notes_field', ['cart' => $this->cart]);
}
public function renderShippingOptions()
{
if (!$this->requireShipping) {
return;
}
$countryCode = $this->shippingAddress['country'] ?: $this->billingAddress['country'];
$stateCode = $this->shippingAddress['state'] ?: $this->billingAddress['state'];
$billingValidations = array_filter(CheckoutFieldsSchema::getCheckoutFieldsRequirements('billing', 'physical'));
if (!isset($billingValidations['country'])) {
$countryCode = (new StoreSettings())->get('store_country');
}
$availableShippingMethods = AddressHelper::getShippingMethods($countryCode, $stateCode);
$selectedId = Arr::get($this->cart->checkout_data, 'shipping_data.shipping_method_id', '');
$selectedId = CartHelper::resolveAutoSelectShippingMethod($this->cart, $availableShippingMethods ?: [], $selectedId);
if (!$availableShippingMethods || is_wp_error($availableShippingMethods)) {
(new ShippingMethodsRender($availableShippingMethods, $selectedId))->render();
} else {
foreach ($availableShippingMethods as $method) {
$method->charge_amount = CartHelper::calculateShippingMethodCharge($method, $this->cart->cart_data);
}
(new ShippingMethodsRender($availableShippingMethods, $selectedId))->render();
}
}
/**
* Get available shipping methods considering cart's shipping classes.
* This is used by the frontend to display profile-aware methods.
*/
public function getProfileAwareShippingMethods($countryCode, $stateCode)
{
// Get general methods (backward compatible)
$methods = AddressHelper::getShippingMethods($countryCode, $stateCode);
if (is_wp_error($methods) || empty($methods)) {
return $methods;
}
return $methods;
}
public function renderPaymentMethods($atts = [])
{
if ($this->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
]);
if (!$selectedPaymentMethod && !empty($activePaymentMethods)) {
// reset() not [0] — the filter above may return a non-zero-indexed array.
$firstMethod = reset($activePaymentMethods);
$selectedPaymentMethod = $firstMethod ? $firstMethod->getMeta('route') : '';
}
$checkoutMethodStyle = $this->storeSettings->get('checkout_method_style', 'logo');
?>
'submit',
'class' => 'fct_place_order_btn large',
'id' => 'fluent_cart_order_btn',
'data-fluent-cart-checkout-page-checkout-button' => '',
'data-value' => $placeOrderButtonText,
'disabled' => ''
];
// Parse $atts using WordPress shortcode_atts or wp_parse_args
if (!empty($atts)) {
// Extract attributes from string
$parsed = shortcode_parse_atts($atts);
if (isset($parsed['class'])) {
$attributes['class'] .= ' ' . $parsed['class'];
unset($parsed['class']);
}
$attributes = array_merge($attributes, $parsed);
}
?>
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('instructions')): ?>
getMeta('instructions')); ?>
$route,
'method_title' => $methodTitle,
'method_style' => $methodStyle,
];
$paymentMethodClass = '';
$paymentMethodClass = apply_filters('fluent_cart/payment_method_list_class', $paymentMethodClass, $pmContext);
?>
"
aria-hidden="true">
$method,
'cart' => $this->cart,
'route' => $route
]
); ?>
'sub_section',
'wrapper_class' => 'fct_2_columns fct_checkout_city_postcode',
'fields' => [
'city' => $cityField,
'postcode' => $postcodeField
],
];
}
if (isset($fields['phone'])) {
$phoneField = $fields['phone'];
unset($fields['phone']);
$fields['phone'] = $phoneField;
}
if (isset($fields['company_name'])) {
$companyField = $fields['company_name'];
unset($fields['company_name']);
$fields['company_name'] = $companyField;
}
return $fields;
}
public function agreeTerms($atts = [])
{
if (!CheckoutFieldsSchema::isTermsVisible()) {
return;
}
$termsText = CheckoutFieldsSchema::getTermsText();
$sectionId = 'agree_terms_section';
$extraClass = Arr::get($atts, 'wrapper_atts') ? '' : 'fct-has-default-font-size';
$title = Arr::get($atts, 'title');
?>
cart))->render(false);
}
}