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

Metorial: MCP Server skalierbar betreiben

Metorial ist eine Open-Source-Plattform, die es ermöglicht, MCP Server (Model Context Protocol) skalierbar im Netz zu betreiben. Statt sich um Infrastruktur, Security und Scaling zu kümmern, können Entwickler:innen ihre MCP Server mit wenigen Klicks deployen und mit AI-Agenten verbinden. Die Plattform bietet Zugang zu über 600 fertigen Integrationen und kann

Weiterlesen

Relace: Spezialanbieter für Coding Agents und Vibe-Coding Plattformen

Relace.ai positioniert sich als neue Infrastrukturebene für KI-gestütztes Programmieren. Während Plattformen wie Replit, Lovable, Bolt oder Mocha auf Full-Service-Erlebnisse setzen, fokussiert sich Relace auf die zugrunde liegende Technologie: Modelle und Tools für Teams, die eigene Coding Agents oder Vibe-Coding-Plattformen aufbauen wollen. Die Relace-Modelle sind auf drei Kernbereiche spezialisiert: Code-Retrieval, Code-Merging

Weiterlesen