'array', 'sanitize_callback' => [$this, 'sanitize_settings'], ]); } public function sanitize_settings($input) { $sanitized = []; // Produkte sanitieren if (isset($input['products']) && is_array($input['products'])) { $sanitized['products'] = []; foreach ($input['products'] as $key => $product) { $sanitized['products'][sanitize_key($key)] = [ 'label' => sanitize_text_field($product['label'] ?? ''), 'description' => sanitize_textarea_field($product['description'] ?? ''), 'base_price' => floatval($product['base_price'] ?? 0), ]; } } // Preise sanitieren if (isset($input['prices']) && is_array($input['prices'])) { $sanitized['prices'] = []; foreach ($input['prices'] as $key => $value) { $sanitized['prices'][sanitize_key($key)] = floatval($value); } } // Dynamische Preisformeln sanitieren if (isset($input['dynamic_pricing'])) { $sanitized['dynamic_pricing'] = [ 'business_formula' => sanitize_textarea_field($input['dynamic_pricing']['business_formula'] ?? ''), 'private_formula' => sanitize_textarea_field($input['dynamic_pricing']['private_formula'] ?? ''), 'business_min_quantity' => intval($input['dynamic_pricing']['business_min_quantity'] ?? 0), 'private_min_quantity' => intval($input['dynamic_pricing']['private_min_quantity'] ?? 0), 'business_normal_quantity' => intval($input['dynamic_pricing']['business_normal_quantity'] ?? 0), 'private_normal_quantity' => intval($input['dynamic_pricing']['private_normal_quantity'] ?? 0), ]; } // Backend-Verbindung sanitieren if (isset($input['backend_connection'])) { $sanitized['backend_connection'] = [ 'api_url' => esc_url_raw($input['backend_connection']['api_url'] ?? ''), 'api_token' => sanitize_text_field($input['backend_connection']['api_token'] ?? ''), 'webhook_url_business' => esc_url_raw($input['backend_connection']['webhook_url_business'] ?? ''), 'webhook_url_private' => esc_url_raw($input['backend_connection']['webhook_url_private'] ?? ''), 'redirect_url_business' => esc_url_raw($input['backend_connection']['redirect_url_business'] ?? ''), 'redirect_url_private' => esc_url_raw($input['backend_connection']['redirect_url_private'] ?? ''), ]; } // REST API Key sanitieren if (isset($input['api_security'])) { $sanitized['api_security'] = [ 'api_key' => sanitize_text_field($input['api_security']['api_key'] ?? ''), ]; } // PayPal-Verbindung sanitieren if (isset($input['paypal'])) { $sanitized['paypal'] = [ 'enabled' => !empty($input['paypal']['enabled']), 'mode' => sanitize_text_field($input['paypal']['mode'] ?? 'sandbox'), 'client_id_sandbox' => sanitize_text_field($input['paypal']['client_id_sandbox'] ?? ''), 'client_secret_sandbox' => sanitize_text_field($input['paypal']['client_secret_sandbox'] ?? ''), 'client_id_live' => sanitize_text_field($input['paypal']['client_id_live'] ?? ''), 'client_secret_live' => sanitize_text_field($input['paypal']['client_secret_live'] ?? ''), ]; } // Schriftmuster und Platzhalter-Hilfe sanitieren if (isset($input['font_sample'])) { $sanitized['font_sample'] = [ 'url' => esc_url_raw($input['font_sample']['url'] ?? ''), 'placeholder_help_url' => esc_url_raw($input['font_sample']['placeholder_help_url'] ?? ''), ]; } return $sanitized; } public function render_settings_page(): void { if (!current_user_can('manage_options')) { return; } $settings = $this->get_settings(); ?>

📦 Produkte

Verwalten Sie Namen, Beschreibungen und Startpreise für alle Produkte.

render_product_card('businessbriefe', 'Businessbriefe', $settings); ?> render_product_card('business-postkarten', 'Business Postkarten', $settings); ?> render_product_card('follow-ups', 'Follow-ups', $settings); ?> render_product_card('einladungen', 'Einladungen', $settings); ?> render_product_card('private-briefe', 'Private Briefe', $settings); ?>

📐 Format Aufpreise

Aufpreise wenn bei bestimmten Produkten das Format gewechselt wird.

Aufpreis pro Stück wenn bei Postkarten oder Einladungen auf A4 gewechselt wird

🚚 Versand & Umschlag

Portokosten für Versand innerhalb Deutschlands (0% MwSt.)
Portokosten für Auslandsversand (0% MwSt.)
Service-Aufschlag für Direktversand (19% MwSt.)
Grundpreis für Kuvert
Aufschlag für Beschriftung des Umschlags (Empfängeradresse oder individueller Text)

✨ Zusatzleistungen

Einmalige Einrichtungsgebühr für die API-Anbindung

📊 Follow-ups Preis-Multiplikatoren

Die Gesamtkosten pro Schriftstück werden mit diesen Multiplikatoren je nach Menge multipliziert.

Menge Multiplikator
5 - 49 Stück ×
50 - 199 Stück ×
200 - 499 Stück ×
500 - 999 Stück ×
1000+ Stück ×

📋 Steuern

Gilt für alle Positionen inkl. Versand

🧮 Dynamische Preisberechnung

Konfigurieren Sie die dynamische Preisberechnung basierend auf Mengen. Die Formeln unterstützen Platzhalter wie %qty% (aktuelle Menge), %norm_b% (Normalpreis Menge Business), %mind_b% (Mind. Menge Business), %norm_p% (Normalpreis Menge Privat), %mind_p% (Mind. Menge Privat).

Business

Minimale Menge für Business-Bestellungen (außer Follow-ups)
Ab dieser Menge gilt der Normalpreis (Multiplikator = 1)
JavaScript-Formel zur Berechnung des Preis-Multiplikators.
Platzhalter verwenden: %qty% für aktuelle Menge, %norm_b% für Normalpreis-Menge Business, %mind_b% für Mindestmenge Business.
Beispiel: (%qty% >= %norm_b%) ? 1 : (2 - Math.sqrt(%qty% / %norm_b%))
Dies bedeutet: Wenn die Menge >= Normalpreis-Menge ist, dann Multiplikator = 1, sonst dynamische Berechnung.

Privat

Minimale Menge für Private Bestellungen
Ab dieser Menge gilt der Normalpreis (Multiplikator = 1)
JavaScript-Formel zur Berechnung des Preis-Multiplikators.
Platzhalter verwenden: %qty% für aktuelle Menge, %norm_p% für Normalpreis-Menge Privat, %mind_p% für Mindestmenge Privat.
Beispiel: (%qty% >= %norm_p%) ? 1 : (2 - Math.sqrt(%qty% / %norm_p%))
Dies bedeutet: Wenn die Menge >= Normalpreis-Menge ist, dann Multiplikator = 1, sonst dynamische Berechnung.
Verfügbare Platzhalter:
  • %qty% - Aktuelle Menge (eingegebene Stückzahl)
  • %norm_b% - Normalpreis Menge Business
  • %mind_b% - Mindestmenge Business
  • %norm_p% - Normalpreis Menge Privat
  • %mind_p% - Mindestmenge Privat

🔌 Backend-Verbindung

Konfigurieren Sie die Verbindung zum Backend-System für erweiterte Funktionen.

Basis-URL des Backend-Systems (z.B. https://api.example.com)
Authentifizierungs-Token für API-Zugriff
Webhook wird nach Klick auf "Jetzt kostenpflichtig bestellen" für Geschäftskunden aufgerufen
Webhook wird nach erfolgreicher PayPal-Zahlung für Privatkunden aufgerufen
Weiterleitung nach Bestellung für Geschäftskunden (nach Klick auf "Jetzt kostenpflichtig bestellen")
Weiterleitung nach erfolgreicher PayPal-Zahlung für Privatkunden

🔐 REST API Sicherheit

Konfigurieren Sie einen API-Key für die REST-API-Endpunkte. Dieser Key muss im Header X-Skrift-API-Key mitgesendet werden.

Leer lassen um API-Key-Prüfung zu deaktivieren (nicht empfohlen für Produktion)

✍️ Schriftmuster (Vorschau-Fallback)

Wenn die Vorschau-Generierung fehlschlägt oder nicht verfügbar ist, wird ein "Schriftmuster ansehen"-Link angezeigt. Der Link öffnet sich in einem neuen Tab.

URL zur Schriftmuster-Seite (wird in neuem Tab geöffnet)
URL zur Platzhalter-Hilfeseite (wird bei Platzhalter-Infotexten als Link angezeigt)

💳 PayPal-Verbindung (nur Privatkunden)

Konfigurieren Sie die PayPal-Zahlungsintegration. PayPal ist nur für Privatkunden aktiviert.

Aktiviert PayPal als Zahlungsoption für Privatkunden
Sandbox für Tests, Live für echte Zahlungen

Sandbox-Zugangsdaten (Test)

Live-Zugangsdaten (Produktion)

Hinweis: Um PayPal zu aktivieren, benötigen Sie ein PayPal Business-Konto. Sie erhalten die API-Zugangsdaten im PayPal Developer Dashboard.

URL-Parameter

Der Konfigurator unterstützt folgende URL-Parameter:

Parameter Werte Beschreibung
?businessbriefe
?business-postkarten
?follow-ups
?einladungen
?private-briefe
Produkt direkt vorauswählen. Der Produktauswahlschritt wird übersprungen.
quantity Zahl (z.B. 100) Menge vorausfüllen.
format a4, a6h, a6q Format vorauswählen. a6h = A6 Hochformat, a6q = A6 Querformat.
noPrice Preise im Konfigurator ausblenden.
noLimits Keine Mindestmengen. Erlaubt Bestellungen ab 1 Stück.

Beispiele

[ 'businessbriefe' => [ 'label' => 'Businessbriefe', 'description' => 'Professionelle handgeschriebene Korrespondenz', 'base_price' => 2.50, ], 'business-postkarten' => [ 'label' => 'Business Postkarten', 'description' => 'Professionelle handgeschriebene Korrespondenz', 'base_price' => 1.80, ], 'follow-ups' => [ 'label' => 'Follow-ups', 'description' => 'Professionelle handgeschriebene Korrespondenz', 'base_price' => 2.50, ], 'einladungen' => [ 'label' => 'Einladungen', 'description' => 'Professionelle handgeschriebene Korrespondenz', 'base_price' => 1.80, ], 'private-briefe' => [ 'label' => 'Private Briefe', 'description' => 'Professionelle handgeschriebene Korrespondenz', 'base_price' => 2.50, ], ], 'prices' => [ // Versand & Umschlag 'shipping_domestic' => 0.95, // Porto Inland 'shipping_international' => 1.25, // Porto Ausland 'shipping_service' => 0.95, // Serviceaufschlag Versand 'shipping_bulk' => 4.95, // Bulkversand einmalig 'envelope_base' => 0.50, // Kuvert Grundpreis 'envelope_labeling' => 0.50, // Aufschlag Beschriftung // Legacy fields for backwards compatibility 'shipping_direct' => 2.40, 'envelope_recipient_address' => 0.50, 'envelope_custom_text' => 0.30, // Format Aufpreise 'a4_upgrade_surcharge' => 0.50, // Zusatzleistungen 'motif_upload' => 0.30, 'motif_printed' => 0.00, 'motif_design' => 0.00, 'textservice' => 0.00, 'api_connection' => 250.00, // Follow-ups Multiplikatoren 'followup_mult_5_49' => 2.0, 'followup_mult_50_199' => 1.7, 'followup_mult_200_499' => 1.4, 'followup_mult_500_999' => 1.2, 'followup_mult_1000_plus' => 1.0, // Steuern 'tax_rate' => 19, 'shipping_tax_rate' => 0, ], 'dynamic_pricing' => [ 'business_formula' => "(%qty% >= %norm_b%) ? 1 : (2 - Math.sqrt(%qty% / %norm_b%))", 'private_formula' => "(%qty% >= %norm_p%) ? 1 : (2 - Math.sqrt(%qty% / %norm_p%))", 'business_min_quantity' => 50, 'private_min_quantity' => 10, 'business_normal_quantity' => 200, 'private_normal_quantity' => 50, ], 'backend_connection' => [ 'api_url' => '', 'api_token' => '', 'webhook_url_business' => '', 'webhook_url_private' => '', 'redirect_url_business' => '', 'redirect_url_private' => '', ], 'paypal' => [ 'enabled' => false, 'mode' => 'sandbox', 'client_id_sandbox' => '', 'client_secret_sandbox' => '', 'client_id_live' => '', 'client_secret_live' => '', ], 'api_security' => [ 'api_key' => '', ], 'font_sample' => [ 'url' => '', 'placeholder_help_url' => '', ], ]; $saved = get_option(self::OPTION_KEY, []); // Merge nested arrays properly $merged = $defaults; foreach (['products', 'prices', 'dynamic_pricing', 'backend_connection', 'paypal', 'api_security', 'font_sample'] as $section) { if (isset($saved[$section]) && is_array($saved[$section])) { $merged[$section] = array_merge($defaults[$section], $saved[$section]); } } return $merged; } /** * Prüft ob ein API-Key gültig ist */ public static function validate_api_key($provided_key): bool { $settings = self::get_settings(); $stored_key = $settings['api_security']['api_key'] ?? ''; // Wenn kein Key konfiguriert ist, ist alles erlaubt (für Entwicklung) if (empty($stored_key)) { return true; } // Key vergleichen (timing-safe) return hash_equals($stored_key, $provided_key); } /** * Permission Callback für REST API mit API-Key */ public static function rest_api_key_permission($request): bool { $api_key = $request->get_header('X-Skrift-API-Key'); return self::validate_api_key($api_key ?? ''); } } new Skrift_Konfigurator_Admin_Settings();