WooCommerce-Produkt nur für bestimmte E‑Mail-Domains – ein einfaches Plugin, gebaut mit ChatGPT 5

Ich brauche die Möglichkeit bei Produkten einschränken zu können, mit welcher E-Mail-Domain diese gekauft werden können. Ich habe auf Anhieb kein Plugin gefunden, welches diese Funktionalität ohne noch tausend andere Dinge zu ermöglichen, anbietet. Da die Coding-Fähigkeiten des kürzlich erschienenen ChatGPT 5 überall gelobt werden, habe ich ich KI gebeten mit den Code für ein Plugin zu generieren. Das hat im ersten Wurf mit ein paar wenigen Anpassungen geklappt.

Was das Plugin kann

  • Pro Produkt erfasse ich eine Whitelist von erlaubten Domains.
  • Im Checkout wird die Domain der Rechnungs‑E‑Mail geprüft.
  • Subdomains sind automatisch erlaubt (z. B. team.smartkmu.com).
  • IDN (z. B. müller.ch) wird nach ASCII normalisiert, damit Vergleiche sauber funktionieren.

Installation (manuell als ZIP)

  1. Ordner wc-email-domain-restrictor erstellen.
  2. Darin die Datei wc-email-domain-resctrictor.php anlegen und den untenstehenden Code hineinkopieren.
  3. Den Ordner zippen (komprimieren).
  4. In WordPress: Plugins → Installieren → Plugin hochladen → ZIP wählen → Aktivieren.

Voraussetzung: WooCommerce muss aktiv sein.

Code

<?php
/**
 * Plugin Name: WC Email Domain Restrictor (per Produkt)
 * Description: Erlaubt pro Produkt eine Whitelist von E-Mail-Domains (inkl. Subdomains); Checkout-Validierung blockiert unpassende Domains.
 * Author: ChatGPT 5 (with Andreas Von Gunten)
 * Version: 1.1.1 - 9. August 2025
 */

if (!defined('ABSPATH')) exit;

// Doppel-Ladung vermeiden (z.B. bei Must-Use + normal)
if (class_exists('WC_Email_Domain_Restrictor', false)) {
    return;
}

class WC_Email_Domain_Restrictor {
    const META_KEY = '_allowed_email_domains'; // kommagetrennt

    public function __construct() {
        // Nur im Admin die Metabox registrieren
        if (is_admin()) {
            add_action('add_meta_boxes', [$this, 'add_metabox']);
            add_action('save_post_product', [$this, 'save_metabox']);
        }
        // Frontend-Validierung nur, wenn WooCommerce-Checkout läuft
        add_action('woocommerce_checkout_process', [$this, 'validate_checkout']);
    }

    public function add_metabox() {
        add_meta_box(
            'wc_allowed_domains',
            'Erlaubte E-Mail-Domains',
            [$this, 'render_metabox'],
            'product',
            'side',
            'default'
        );
    }

    public function render_metabox($post) {
        wp_nonce_field('wc_allowed_domains_nonce', 'wc_allowed_domains_nonce_field');
        $val = get_post_meta($post->ID, self::META_KEY, true);
        ?>
        <p>Kommagetrennt, z. B. <code>smartkmu.com, datenschutzpartner.com</code> (Subdomains sind automatisch erlaubt; optional <code>*.smartkmu.com</code>).</p>
        <input type="text" style="width:100%" name="allowed_email_domains" value="<?php echo esc_attr($val); ?>">
        <?php
    }

    public function save_metabox($post_id) {
        if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) return;
        if (!isset($_POST['wc_allowed_domains_nonce_field']) ||
            !wp_verify_nonce($_POST['wc_allowed_domains_nonce_field'], 'wc_allowed_domains_nonce')) return;
        if (!current_user_can('edit_post', $post_id)) return;

        if (isset($_POST['allowed_email_domains'])) {
            $raw  = strtolower(trim(wp_unslash($_POST['allowed_email_domains'])));
            $norm = preg_replace('/[\s;]+/', ',', $raw);
            $norm = preg_replace('/,+/', ',', trim($norm, ', '));
            update_post_meta($post_id, self::META_KEY, $norm);
        }
    }

    /** Normalisiert Domain (IDN→ASCII, Trim, Kleinbuchstaben) */
    private static function wc_edr_normalize_domain($domain) {
        $d = strtolower(trim($domain));
        $d = preg_replace('/^\*\.\s*/', '', $d); // führendes '*.' entfernen
        $d = ltrim($d, '.'); // führende Punkte entfernen

        // Sicherer Aufruf: Funktion vorhanden? Konstante vorhanden? Sonst ohne Variant-Param.
        if (function_exists('idn_to_ascii')) {
            $variant = defined('INTL_IDNA_VARIANT_UTS46') ? INTL_IDNA_VARIANT_UTS46 : 0;
            // @ unterdrückt edge-case Notices alter intl-Builds
            $ascii = @idn_to_ascii($d, IDNA_DEFAULT, $variant);
            if ($ascii) $d = $ascii;
        }
        return $d;
    }

    /** Prüft, ob $emailDomain exakt oder als Subdomain zu $allowed passt (mit Punkt-Grenze) */
    private static function wc_edr_domain_matches($emailDomain, $allowed) {
        $emailDomain = self::wc_edr_normalize_domain($emailDomain);
        $allowed     = self::wc_edr_normalize_domain($allowed);

        if ($emailDomain === $allowed) return true;

        // Subdomain-Match: endet mit ".{$allowed}"
        $needle = '.' . $allowed;
        return (strlen($emailDomain) > strlen($allowed) && substr($emailDomain, -strlen($needle)) === $needle);
    }

    /** Checkout-Validierung: pro Cart-Item Domain gegen Produkt-Whitelist prüfen */
    public function validate_checkout() {
        // Falls WooCommerce nicht initialisiert ist (z.B. bei Deaktivierungen): sauber raus
        if (!function_exists('WC') || !WC()) return;

        $billing_email = isset($_POST['billing_email']) ? sanitize_email(wp_unslash($_POST['billing_email'])) : '';
        if (empty($billing_email) || strpos($billing_email, '@') === false) return;

        $email_domain = strtolower(substr(strrchr($billing_email, '@'), 1));
        $email_domain = self::wc_edr_normalize_domain($email_domain);
        if (empty($email_domain)) return;

        $cart = WC()->cart;
        if (!$cart || !method_exists($cart, 'get_cart')) return;

        foreach ($cart->get_cart() as $item) {
            $product = isset($item['data']) ? $item['data'] : null;
            if (!$product || !is_a($product, 'WC_Product')) continue;

            $pid     = $product->get_id(); // Variation-ID, falls vorhanden
            $domains = get_post_meta($pid, self::META_KEY, true);

            if (!$domains && $product->is_type('variation')) {
                $domains = get_post_meta($product->get_parent_id(), self::META_KEY, true);
            }
            if (!$domains) continue;

            $allowed = array_filter(array_map('trim', explode(',', strtolower($domains))));
            $allowed = array_map([self::class, 'wc_edr_normalize_domain'], $allowed);

            $ok = false;
            foreach ($allowed as $allow) {
                if (self::wc_edr_domain_matches($email_domain, $allow)) { $ok = true; break; }
            }

            if (!$ok) {
                $pretty = implode(', ', $allowed);
                wc_add_notice(
                    sprintf(
                        '«%s» kann nur mit einer E-Mail-Domain aus %s gekauft werden (inkl. Subdomains). Deine Domain ist «%s».',
                        esc_html($product->get_name()),   // Escaping ergänzt
                        esc_html($pretty),
                        esc_html($email_domain)
                    ),
                    'error'
                );
            }
        }
    }
}

// Sicheres Bootstrapping: nur instanzieren, wenn WooCommerce vorhanden ODER wir im Admin sind (Metabox).
add_action('plugins_loaded', function() {
    if (is_admin() || class_exists('WooCommerce')) {
        new WC_Email_Domain_Restrictor();
    }
});

// (Optional) Schutz, falls WC deaktiviert wird: Admin-Hinweis statt Fatal.
add_action('admin_init', function () {
    if (!class_exists('WooCommerce')) {
        // Nichts weiter tun – unsere Frontend-Hooks sind ohnehin inert.
        return;
    }
});

Weitere Beiträge