How do I create a date of birth field in the WooCommerce checkout with age validation?

/**
 * WooCommerce date of birth field with age validation
 * Insert this code into the functions.php of your theme or into a plugin
 */

// 1. add date of birth field to checkout
add_action('woocommerce_after_checkout_billing_form', 'add_birthdate_checkout_field');

function add_birthdate_checkout_field($checkout) {
    echo '<div id="birthdate_checkout_field"><h3>' . __('Geburtsdatum') . '</h3>';
    
    woocommerce_form_field('billing_birthdate', array(
        'type'          => 'date',
        'class'         => array('form-row-wide'),
        'label'         => __('Geburtsdatum *'),
        'placeholder'   => __('TT.MM.JJJJ'),
        'required'      => true,
        'custom_attributes' => array(
            'max' => date('Y-m-d') // Maximaldatum ist heute
        )
    ), $checkout->get_value('billing_birthdate'));
    
    echo '</div>';
}

// 2. validate date of birth field (age must be at least 18 years)
add_action('woocommerce_checkout_process', 'validate_birthdate_checkout_field');

function validate_birthdate_checkout_field() {
    if (!$_POST['billing_birthdate']) {
        wc_add_notice(__('Bitte geben Sie Ihr Geburtsdatum an.'), 'error');
        return;
    }
    
    $birthdate = sanitize_text_field($_POST['billing_birthdate']);
    $birth_timestamp = strtotime($birthdate);
    
    if (!$birth_timestamp) {
        wc_add_notice(__('Bitte geben Sie ein gültiges Geburtsdatum an.'), 'error');
        return;
    }
    
    // Calculate age
    $today = new DateTime();
    $birth = new DateTime($birthdate);
    $age = $today->diff($birth)->y;
    
    if ($age < 18) {
        wc_add_notice(__('Sie müssen mindestens 18 Jahre alt sein, um eine Bestellung aufgeben zu können.'), 'error');
    }
    
    // Additional validation: Date of birth must not be in the future
    if ($birth > $today) {
        wc_add_notice(__('Das Geburtsdatum darf nicht in der Zukunft liegen.'), 'error');
    }
}

// 3. save date of birth in the order
add_action('woocommerce_checkout_update_order_meta', 'save_birthdate_checkout_field');

function save_birthdate_checkout_field($order_id) {
    if (!empty($_POST['billing_birthdate'])) {
        $birthdate = sanitize_text_field($_POST['billing_birthdate']);
        update_post_meta($order_id, 'billing_birthdate', $birthdate);
        
        // Auch im Benutzerprofil speichern, wenn Benutzer eingeloggt ist
        $user_id = get_current_user_id();
        if ($user_id) {
            update_user_meta($user_id, 'billing_birthdate', $birthdate);
        }
    }
}

// 4. display date of birth in the order details (Admin)
add_action('woocommerce_admin_order_data_after_billing_address', 'display_birthdate_in_admin_order_meta', 10, 1);

function display_birthdate_in_admin_order_meta($order) {
    $birthdate = get_post_meta($order->get_id(), 'billing_birthdate', true);
    if ($birthdate) {
        $formatted_date = date('d.m.Y', strtotime($birthdate));
        echo '<p><strong>' . __('Geburtsdatum') . ':</strong> ' . $formatted_date . '</p>';
    }
}

// 5. add date of birth field to user account
add_action('woocommerce_edit_account_form', 'add_birthdate_to_account_form');

function add_birthdate_to_account_form() {
    $user_id = get_current_user_id();
    $birthdate = get_user_meta($user_id, 'billing_birthdate', true);
    ?>
    <p class="woocommerce-form-row woocommerce-form-row--wide form-row form-row-wide">
        <label for="billing_birthdate"><?php _e('Geburtsdatum'); ?> <span class="required">*</span></label>
        <input type="date" class="woocommerce-Input woocommerce-Input--text input-text" 
               name="billing_birthdate" id="billing_birthdate" 
               value="<?php echo esc_attr($birthdate); ?>" 
               max="<?php echo date('Y-m-d'); ?>" required />
    </p>
    <?php
}

// 6. validate and save date of birth from account form
add_action('woocommerce_save_account_details', 'save_birthdate_account_details', 10, 1);

function save_birthdate_account_details($user_id) {
    if (isset($_POST['billing_birthdate'])) {
        $birthdate = sanitize_text_field($_POST['billing_birthdate']);
        
        // Validation
        if (empty($birthdate)) {
            wc_add_notice(__('Bitte geben Sie Ihr Geburtsdatum an.'), 'error');
            return;
        }
        
        $birth_timestamp = strtotime($birthdate);
        if (!$birth_timestamp) {
            wc_add_notice(__('Bitte geben Sie ein gültiges Geburtsdatum an.'), 'error');
            return;
        }
        
        // Age validation
        $today = new DateTime();
        $birth = new DateTime($birthdate);
        $age = $today->diff($birth)->y;
        
        if ($age < 18) {
            wc_add_notice(__('Sie müssen mindestens 18 Jahre alt sein.'), 'error');
            return;
        }
        
        if ($birth > $today) {
            wc_add_notice(__('Das Geburtsdatum darf nicht in der Zukunft liegen.'), 'error');
            return;
        }
        
        // Save if validation successful
        update_user_meta($user_id, 'billing_birthdate', $birthdate);
    }
}

// 7. display date of birth in account details (readonly)
add_action('woocommerce_account_dashboard', 'display_birthdate_in_account_dashboard');

function display_birthdate_in_account_dashboard() {
    $user_id = get_current_user_id();
    $birthdate = get_user_meta($user_id, 'billing_birthdate', true);
    
    if ($birthdate) {
        $formatted_date = date('d.m.Y', strtotime($birthdate));
        echo '<p><strong>' . __('Ihr Geburtsdatum') . ':</strong> ' . $formatted_date . '</p>';
    }
}

// 8. add CSS for better display
add_action('wp_head', 'birthdate_checkout_styles');

function birthdate_checkout_styles() {
    ?>
    <style>
    #birthdate_checkout_field {
        margin: 20px 0;
        padding: 15px;
        border: 1px solid #ddd;
        border-radius: 4px;
        background-color: #f9f9f9;
    }
    
    #birthdate_checkout_field h3 {
        margin-top: 0;
        color: #333;
        font-size: 18px;
    }
    
    #billing_birthdate {
        width: 100%;
        padding: 10px;
        border: 1px solid #ccc;
        border-radius: 4px;
        font-size: 16px;
    }
    
    .woocommerce-error {
        background-color: #e74c3c;
        color: white;
        padding: 10px;
        border-radius: 4px;
        margin: 10px 0;
    }
    </style>
    <?php
}

// 9. add JavaScript for better UX
add_action('wp_footer', 'birthdate_checkout_scripts');

function birthdate_checkout_scripts() {
    if (is_checkout() || is_account_page()) {
        ?>
        <script>
        jQuery(document).ready(function($) {
            // Automatic age calculation and warning
            $('#billing_birthdate').on('change', function() {
                var birthdate = new Date($(this).val());
                var today = new Date();
                var age = today.getFullYear() - birthdate.getFullYear();
                var m = today.getMonth() - birthdate.getMonth();
                
                if (m < 0 || (m === 0 && today.getDate() < birthdate.getDate())) {
                    age--;
                }
                
                // Show warning if under 18
                $('.age-warning').remove();
                if (age < 18 && age >= 0) {
                    $(this).after('<div class="age-warning" style="color: red; font-weight: bold; margin-top: 5px;">Sie müssen mindestens 18 Jahre alt sein, um bestellen zu können.</div>');
                }
            });
        });
        </script>
        <?php
    }
}

// 10. display date of birth in e-mail notifications
add_action('woocommerce_email_customer_details', 'add_birthdate_to_emails', 25, 4);

function add_birthdate_to_emails($order, $sent_to_admin, $plain_text, $email) {
    $birthdate = get_post_meta($order->get_id(), 'billing_birthdate', true);
    
    if ($birthdate) {
        $formatted_date = date('d.m.Y', strtotime($birthdate));
        
        if ($plain_text) {
            echo "\nGeburtsdatum: " . $formatted_date . "\n";
        } else {
            echo '<p><strong>Geburtsdatum:</strong> ' . $formatted_date . '</p>';
        }
    }
}

// 11. request date of birth for existing users at first checkout
add_action('woocommerce_checkout_init', 'populate_birthdate_for_existing_users');

function populate_birthdate_for_existing_users($checkout) {
    if (is_user_logged_in()) {
        $user_id = get_current_user_id();
        $birthdate = get_user_meta($user_id, 'billing_birthdate', true);
        
        if ($birthdate) {
            $checkout->checkout_fields['billing']['billing_birthdate']['default'] = $birthdate;
        }
    }
}