session ) { return false; } return $this->get_cart_required_age() > 0; } /** * User must complete age verification before placing order (controls CSS/JS that lock Place order). */ private function checkout_place_order_locked() { if ( ! $this->checkout_has_age_restricted_cart() ) { return false; } return ! AgeOnce_API::is_verified( true, $this->get_cart_required_age() ); } /** * Scope “disabled” Place order styles to unverified flow only (after OAuth redirect, button must work). * * @param string[] $classes Body classes. * @return string[] */ public function checkout_body_class( $classes ) { if ( $this->checkout_place_order_locked() ) { $classes[] = 'ageonce-checkout-requires-verification'; } return $classes; } /** * Enqueue checkout CSS/JS when cart requires age verification (classic and Checkout Block). */ public function enqueue_checkout_assets() { if ( ! $this->checkout_has_age_restricted_cart() ) { return; } $version = defined( 'AGEONCE_VERIFICATION_VERSION' ) ? AGEONCE_VERIFICATION_VERSION : '1.0.0'; $base = defined( 'AGEONCE_VERIFICATION_PLUGIN_FILE' ) ? AGEONCE_VERIFICATION_PLUGIN_FILE : dirname( __DIR__ ) . '/ageonce-verification.php'; wp_enqueue_style( 'ageonce-checkout-age-gate', plugins_url( 'assets/css/checkout-age-gate.css', $base ), array(), $version ); $script_deps = array( 'jquery' ); if ( wp_script_is( 'wp-data', 'registered' ) ) { $script_deps[] = 'wp-data'; } wp_enqueue_script( 'ageonce-checkout-age-gate', plugins_url( 'assets/js/checkout-age-gate.js', $base ), $script_deps, $version, true ); $checkout_data = array( 'disableButtonTitle' => __( 'Please verify your age above', 'age-verification-with-ageonce' ), 'gateHtml' => $this->get_age_gate_html(), 'requiresVerification' => $this->checkout_place_order_locked(), ); wp_add_inline_script( 'ageonce-checkout-age-gate', 'window.ageonceCheckout = ' . wp_json_encode( $checkout_data ) . ';', 'before' ); } /** * Styles for the order Age Verification meta box (admin). * * @param string $hook_suffix Current admin page hook. */ public function enqueue_order_edit_assets( $hook_suffix ) { $screen = function_exists( 'get_current_screen' ) ? get_current_screen() : null; if ( ! $screen ) { return; } $screens = array( 'shop_order', 'woocommerce_page_wc-orders' ); if ( ! in_array( $screen->id, $screens, true ) ) { return; } $version = defined( 'AGEONCE_VERIFICATION_VERSION' ) ? AGEONCE_VERIFICATION_VERSION : '1.0.0'; $base = defined( 'AGEONCE_VERIFICATION_PLUGIN_FILE' ) ? AGEONCE_VERIFICATION_PLUGIN_FILE : dirname( __DIR__ ) . '/ageonce-verification.php'; wp_enqueue_style( 'ageonce-admin-order', plugins_url( 'assets/css/admin-order.css', $base ), array(), $version ); } private function get_cart_required_age() { if ( ! WC()->cart ) { return 0; } $max_age = 0; foreach ( WC()->cart->get_cart() as $cart_item ) { $item_age = AgeOnce_Restrictions::get_required_age_for_content( $cart_item['product_id'] ); if ( $item_age > $max_age ) { $max_age = $item_age; } } return $max_age; } public function render_age_gate_area() { // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- HTML is escaped in get_age_gate_html(). echo $this->get_age_gate_html(); } /** * Inject the age gate above Place order on WooCommerce Checkout Block. * React hydration may replace this markup; checkout-age-gate.js re-inserts it if missing. * * @param string $block_content Inner block HTML. * @return string */ public function inject_checkout_block_age_gate( $block_content ) { if ( ! function_exists( 'is_checkout' ) || ! is_checkout() || is_order_received_page() ) { return $block_content; } $gate_html = $this->get_age_gate_html(); if ( $gate_html === '' ) { return $block_content; } return $gate_html . $block_content; } /** * Markup for the checkout age gate or the verified notice. * * @return string */ private function get_age_gate_html() { if ( 'yes' !== get_option( 'ageonce_enabled' ) ) { return ''; } $required_age = $this->get_cart_required_age(); if ( $required_age === 0 ) { return ''; } $is_verified = AgeOnce_API::is_verified( true, $required_age ); ob_start(); if ( $is_verified ) { ?>
get_cart_required_age(); if ( $required_age === 0 ) { return; } if ( AgeOnce_API::is_verified( true, $required_age ) ) { if ( $order instanceof WC_Order ) { $this->apply_verification_meta( $order ); } return; } if ( class_exists( '\Automattic\WooCommerce\StoreApi\Exceptions\RouteException' ) ) { throw new \Automattic\WooCommerce\StoreApi\Exceptions\RouteException( 'ageonce_unverified', esc_html__( 'Please verify your age to proceed.', 'age-verification-with-ageonce' ), 403 ); } } /** * Persist verification onto a Checkout Block order after Store API validation. * * @param WC_Order $order Order about to be paid. */ public function add_verification_to_store_api_order( $order ) { if ( ! $order instanceof WC_Order ) { return; } if ( ! AgeOnce_API::is_verified( true, $this->get_cart_required_age() ) ) { return; } $this->apply_verification_meta( $order ); $order->save(); } public function intercept_checkout() { if ( 'yes' !== get_option( 'ageonce_enabled' ) ) { return; } $required_age = $this->get_cart_required_age(); if ( $required_age === 0 ) { return; } $is_verified = AgeOnce_API::is_verified( true, $required_age ); if ( ! $is_verified ) { wc_clear_notices(); wc_add_notice( __( 'Please verify your age to proceed.', 'age-verification-with-ageonce' ), 'error' ); if ( wp_doing_ajax() ) { wp_send_json( array( 'result' => 'failure', 'messages' => '', 'refresh' => 'true', 'reload' => 'true', ) ); } else { wp_safe_redirect( wc_get_checkout_url() ); exit; } } } public function add_verification_to_order_object( $order, $data ) { if ( ! AgeOnce_API::is_verified( true, $this->get_cart_required_age() ) ) { return; } $this->apply_verification_meta( $order ); } public function save_order_meta_legacy( $order_id ) { if ( ! AgeOnce_API::is_verified( true, $this->get_cart_required_age() ) ) { return; } $order = wc_get_order( $order_id ); if ( $order ) { $this->apply_verification_meta( $order ); $order->save(); } } /** * Copy the current AgeOnce verification onto an order. * * @param WC_Order $order Order being created. */ private function apply_verification_meta( $order ) { $order->update_meta_data( '_ageonce_status', 'verified' ); $tx_id = AgeOnce_API::get_transaction_id(); if ( $tx_id !== '' ) { $order->update_meta_data( '_ageonce_transaction_id', $tx_id ); } $verified_at = ( function_exists( 'WC' ) && WC()->session ) ? WC()->session->get( 'ageonce_timestamp' ) : ''; if ( ! $verified_at ) { $user_id = get_current_user_id(); $stored = $user_id > 0 ? get_user_meta( $user_id, '_ageonce_timestamp', true ) : ''; $verified_at = $stored ? $stored : current_time( 'mysql' ); } $order->update_meta_data( '_ageonce_timestamp', $verified_at ); } public function add_order_meta_box() { $screens = array( 'shop_order', 'woocommerce_page_wc-orders' ); foreach ( $screens as $screen ) { add_meta_box( 'ageonce_status', __( 'Age Verification', 'age-verification-with-ageonce' ), array( $this, 'render_meta_box' ), $screen, 'side', 'high' ); } } public function render_meta_box( $post_or_order_object ) { $order = ( $post_or_order_object instanceof WC_Order ) ? $post_or_order_object : wc_get_order( $post_or_order_object->ID ); if ( ! $order ) { return; } $status = $order->get_meta( '_ageonce_status' ); $tx_id = $order->get_meta( '_ageonce_transaction_id' ); $timestamp = $order->get_meta( '_ageonce_timestamp' ); if ( $status === 'verified' ) { echo ''; } else { echo ''; } } }