cart_data)) { return false; } return !empty($cart->cart_data); } /** * @param object $cart * @return object|null A live FluentCart\App\Services\Renderer\CheckoutRenderer instance. */ public static function newCheckoutRenderer($cart) { if (!class_exists(self::CHECKOUT_RENDERER_CLASS)) { return null; } try { $class = self::CHECKOUT_RENDERER_CLASS; return new $class($cart); } catch (\Throwable $error) { self::logError('newCheckoutRenderer', $error); return null; } } /** * @return object|null A live FluentCart\App\Services\Renderer\FormFieldRenderer instance. */ public static function newFormFieldRenderer() { if (!class_exists(self::FORM_FIELD_RENDERER_CLASS)) { return null; } try { $class = self::FORM_FIELD_RENDERER_CLASS; return new $class(); } catch (\Throwable $error) { self::logError('newFormFieldRenderer', $error); return null; } } /** * @param object $cart * @return object|null A live FluentCart\App\Services\Renderer\CartSummaryRender instance. */ public static function newCartSummaryRenderer($cart) { if (!class_exists(self::CART_SUMMARY_RENDER_CLASS)) { return null; } try { $class = self::CART_SUMMARY_RENDER_CLASS; return new $class($cart); } catch (\Throwable $error) { self::logError('newCartSummaryRenderer', $error); return null; } } /** * @param object $cart * @return object|null A live FluentCart\App\Services\Renderer\ModalCheckoutRenderer instance. */ public static function newModalCheckoutRenderer($cart) { if (!class_exists(self::MODAL_CHECKOUT_RENDERER_CLASS)) { return null; } try { $class = self::MODAL_CHECKOUT_RENDERER_CLASS; return new $class($cart); } catch (\Throwable $error) { self::logError('newModalCheckoutRenderer', $error); return null; } } /** * @return array */ public static function getAddressBaseFields(array $config, string $scope = 'render'): array { if (!class_exists(self::CHECKOUT_FIELDS_SCHEMA_CLASS) || !method_exists(self::CHECKOUT_FIELDS_SCHEMA_CLASS, 'getAddressBaseFields')) { return []; } try { $fields = call_user_func([self::CHECKOUT_FIELDS_SCHEMA_CLASS, 'getAddressBaseFields'], $config, $scope); return is_array($fields) ? $fields : []; } catch (\Throwable $error) { self::logError('getAddressBaseFields', $error); return []; } } /** * @param object|null $cart * @return array */ public static function getNameEmailFieldsSchema($cart = null, string $scope = 'render'): array { if (!class_exists(self::CHECKOUT_FIELDS_SCHEMA_CLASS) || !method_exists(self::CHECKOUT_FIELDS_SCHEMA_CLASS, 'getNameEmailFieldsSchema')) { return []; } try { $schema = call_user_func([self::CHECKOUT_FIELDS_SCHEMA_CLASS, 'getNameEmailFieldsSchema'], $cart, $scope); return is_array($schema) ? $schema : []; } catch (\Throwable $error) { self::logError('getNameEmailFieldsSchema', $error); return []; } } public static function isTermsVisible(): bool { if (!class_exists(self::CHECKOUT_FIELDS_SCHEMA_CLASS) || !method_exists(self::CHECKOUT_FIELDS_SCHEMA_CLASS, 'isTermsVisible')) { // Fail open exactly like the live renderer would if the schema class were // present (isTermsVisible() defaults true in 1.3.27); absence of the class // itself is caught earlier by isAvailable() guards in the calling element. return true; } try { return (bool) call_user_func([self::CHECKOUT_FIELDS_SCHEMA_CLASS, 'isTermsVisible']); } catch (\Throwable $error) { self::logError('isTermsVisible', $error); return true; } } public static function getTermsText(): string { if (!class_exists(self::CHECKOUT_FIELDS_SCHEMA_CLASS) || !method_exists(self::CHECKOUT_FIELDS_SCHEMA_CLASS, 'getTermsText')) { return __('I agree to the terms and conditions.', 'sparkcore-commerce'); } try { return (string) call_user_func([self::CHECKOUT_FIELDS_SCHEMA_CLASS, 'getTermsText']); } catch (\Throwable $error) { self::logError('getTermsText', $error); return __('I agree to the terms and conditions.', 'sparkcore-commerce'); } } /** * The two verified filters (task instruction 3): submit-button copy goes through * `fluent_cart/checkout_page_order_button_text` exactly like the live * `CheckoutRenderer::renderCheckoutButton()` does, so a site-wide filter hooked to that * tag affects the atomized submit button identically to the native one. */ public static function submitButtonText(string $default): string { if (!function_exists('apply_filters')) { return $default; } return (string) apply_filters('fluent_cart/checkout_page_order_button_text', $default); } /** * Renders the native `[fluent_cart_checkout]` shortcode. This is BOTH the * feature-flag-off default path and the CheckoutStructureValidator fallback path (task * instructions 4 and 4): an invalid or disabled atomized tree always degrades to * exactly what a site running the plain shortcode would see, never a broken partial * render. */ public static function renderNativeCheckout(): void { if (function_exists('shortcode_exists') && shortcode_exists('fluent_cart_checkout')) { echo wp_kses(Kses::prepare(function_exists('do_shortcode') ? do_shortcode('[fluent_cart_checkout]') : ''), Kses::allowed()); return; } // Guarded fallback mirroring the shortcode's own call path bit for bit, same // discipline as bd-elements/FC_Login/ssr.php's documented OR-pattern: FluentCart // 1.3.27 DOES register `fluent_cart_checkout` (verified, // Contracts.php shortcodeContracts()), so this branch is a defensive // future-proofing path, not a currently-exercised one. if (class_exists(self::CHECKOUT_PAGE_HANDLER_CLASS)) { try { $class = self::CHECKOUT_PAGE_HANDLER_CLASS; (new $class())->render(); return; } catch (\Throwable $error) { self::logError('renderNativeCheckout', $error); } } echo wp_kses(\FluentCartBreakdance\Compatibility\FluentCartAdapter::notice( __('The checkout is unavailable right now.', 'sparkcore-commerce'), 'broken' ), Kses::allowed()); } /** * Renders FluentCart's own empty-cart state (`CartRenderer::renderEmpty()`), used by * FC_Checkout when the resolved cart genuinely has no items -- the same empty-state * branch `CheckoutPageHandler::render()` itself takes. */ public static function renderEmptyCart(): void { if (!class_exists(self::CART_RENDERER_CLASS) || !method_exists(self::CART_RENDERER_CLASS, 'renderEmpty')) { echo wp_kses(\FluentCartBreakdance\Compatibility\FluentCartAdapter::notice( __('Your cart is empty.', 'sparkcore-commerce'), 'empty' ), Kses::allowed()); return; } try { $class = self::CART_RENDERER_CLASS; (new $class())->renderEmpty(); } catch (\Throwable $error) { self::logError('renderEmptyCart', $error); echo wp_kses(\FluentCartBreakdance\Compatibility\FluentCartAdapter::notice( __('Your cart is empty.', 'sparkcore-commerce'), 'empty' ), Kses::allowed()); } } // ------------------------------------------------------------------ // Builder-path anonymized placeholder data (task instruction 3, binding: // "GEANONIMISEERDE placeholderdata"). Every value below is a static, non-PII example; // none of it is ever derived from a real customer, cart, or order. // ------------------------------------------------------------------ /** * @return array A builder-preview billing address config for * CheckoutFieldsSchema::getAddressBaseFields(), scope 'builder' (all fields * shown, none filtered by store requirements -- "verborgen secties zichtbaar in * builder"). */ public static function anonymizedBillingAddressConfig(): array { return [ 'type' => 'billing', 'product_type' => 'physical', 'with_shipping' => false, 'full_name' => __('Jordan Rivera', 'sparkcore-commerce'), 'country' => '', 'address_1' => __('123 Example Street', 'sparkcore-commerce'), 'address_2' => '', 'city' => __('Sampletown', 'sparkcore-commerce'), 'state' => '', 'postcode' => '12345', 'company_name' => '', 'phone' => '', ]; } /** * @return array A builder-preview shipping address config, same * anonymization discipline as anonymizedBillingAddressConfig(). */ public static function anonymizedShippingAddressConfig(): array { return [ 'type' => 'shipping', 'product_type' => 'physical', 'full_name' => __('Jordan Rivera', 'sparkcore-commerce'), 'country' => '', 'address_1' => __('123 Example Street', 'sparkcore-commerce'), 'address_2' => '', 'city' => __('Sampletown', 'sparkcore-commerce'), 'state' => '', 'postcode' => '12345', 'company_name' => '', 'phone' => '', ]; } /** * @return array A static builder-preview * shipping-methods list (FC_Checkout_Shipping_Methods has no real Cart to derive * AddressHelper::getShippingMethods() from in the builder). */ public static function anonymizedShippingMethods(): array { return [ ['label' => __('Standard Shipping', 'sparkcore-commerce'), 'price' => __('$5.00', 'sparkcore-commerce')], ['label' => __('Express Shipping', 'sparkcore-commerce'), 'price' => __('$15.00', 'sparkcore-commerce')], ]; } /** * @return array A static builder-preview * payment-methods list (FC_Checkout_Payment_Methods has no real Cart/store * payment-gateway configuration to derive PaymentMethods::getActiveMethodInstance() * from in the builder). */ public static function anonymizedPaymentMethods(): array { return [ ['route' => 'card', 'title' => __('Credit / Debit Card', 'sparkcore-commerce')], ['route' => 'paypal', 'title' => __('PayPal', 'sparkcore-commerce')], ]; } /** * @return array A static builder-preview * order summary line-item list for FC_Checkout_Order_Summary. */ public static function anonymizedOrderSummaryItems(): array { return [ ['title' => __('Example Product', 'sparkcore-commerce'), 'price' => __('$49.00', 'sparkcore-commerce')], ]; } private static function logError(string $context, \Throwable $error): void { if (defined('WP_DEBUG') && WP_DEBUG) { // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- Diagnostic logging, only reached when WP_DEBUG is enabled. error_log('SparkCore Commerce checkout adapter error (' . $context . '): ' . $error->getMessage()); } } }