prefix . 'wis_orders';
$coupon_table_name = $wpdb->prefix . 'wis_coupons';
$charset_collate = $wpdb->get_charset_collate();
$sql = "CREATE TABLE $table_name (
id mediumint(9) NOT NULL AUTO_INCREMENT,
player_name varchar(100) NOT NULL,
server varchar(100) NOT NULL,
item_id varchar(100) NOT NULL,
item_title varchar(255) NOT NULL,
price int(11) NOT NULL,
quantity int(11) DEFAULT 1,
status varchar(20) DEFAULT 'pending',
response text,
created_at datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
PRIMARY KEY (id)
) $charset_collate;";
$sql2 = "CREATE TABLE $coupon_table_name (
id mediumint(9) NOT NULL AUTO_INCREMENT,
code varchar(50) NOT NULL,
value int(11) NOT NULL,
type varchar(10) DEFAULT 'fixed',
usage_limit int(11) DEFAULT 1,
used_count int(11) DEFAULT 0,
expiry date DEFAULT NULL,
created_at datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
PRIMARY KEY (id)
) $charset_collate;";
require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
dbDelta($sql);
dbDelta($sql2);
// Alte Installationen updaten (type Spalte hinzufügen)
$existing_columns = $wpdb->get_col_info("SELECT * FROM $coupon_table_name LIMIT 1");
if(!in_array('type', $existing_columns)) {
$wpdb->query("ALTER TABLE $coupon_table_name ADD COLUMN type varchar(10) DEFAULT 'fixed'");
}
}
public static function setup_daily_deal() {
// Initial setup beim ersten Aktivieren
}
public static function run_daily_deal() {
if(get_option('wis_daily_deal_enabled') === '0') return;
global $wpdb;
$discount_percent = intval(get_option('wis_daily_deal_discount', 20));
// 1. Aktuellen Daily Deal deaktivieren
$wpdb->update(
$wpdb->prefix.'postmeta',
['meta_value' => 0],
['meta_key' => '_wis_daily_deal', 'meta_value' => 1]
);
// 2. Zufälliges Item auswählen
$args = [
'post_type' => 'wis_item',
'post_status' => 'publish',
'posts_per_page' => 1,
'meta_query' => [
'relation' => 'AND',
['key' => '_wis_price', 'value' => 0, 'compare' => '>'],
['key' => '_wis_daily_deal', 'compare' => 'NOT EXISTS'] // Eines holen, das noch kein Daily Deal ist
]
];
$items = get_posts($args);
if(!empty($items)) {
$item_id = $items[0]->ID;
$price = intval(get_post_meta($item_id, '_wis_price', true));
// Rabatt berechnen
$offer_price = max(0, floor($price - ($price * ($discount_percent / 100))));
// Flags setzen
update_post_meta($item_id, '_wis_daily_deal', 1);
update_post_meta($item_id, '_wis_is_offer', 1);
update_post_meta($item_id, '_wis_offer_price', $offer_price);
}
}
}
// ===========================================================
// 2. ADMIN & CPT
// ===========================================================
class WIS_CPT {
public static function register_post_types() {
register_post_type('wis_item', [
'labels' => ['name' => 'Shop Items', 'singular_name' => 'Shop Item'],
'public' => false,
'show_ui' => true,
'show_in_menu' => false,
'show_in_admin_all_list' => true,
'capability_type' => 'post',
'capabilities' => [
'edit_post' => 'manage_options',
'read_post' => 'manage_options',
'delete_post' => 'manage_options',
'edit_posts' => 'manage_options',
'delete_posts' => 'manage_options',
'delete_others_posts' => 'manage_options'
],
'supports' => ['title', 'editor'],
'taxonomies' => ['wis_category']
]);
register_post_type('wis_server', [
'labels' => ['name' => 'Servers', 'singular_name' => 'Server'],
'public' => false,
'show_ui' => true,
'show_in_menu' => false,
'capability_type' => 'post',
'supports' => ['title']
]);
register_post_type('wis_coupon', [
'labels' => ['name' => 'Gutscheine', 'singular_name' => 'Gutschein'],
'public' => false,
'show_ui' => true,
'show_in_menu' => false,
'capability_type' => 'post',
'supports' => ['title']
]);
// NEU: Kategorien Taxonomie
$labels = [
'name' => 'Kategorien',
'singular_name' => 'Kategorie',
'search_items' => 'Kategorien suchen',
'all_items' => 'Alle Kategorien',
'parent_item' => 'Übergeordnete Kategorie',
'edit_item' => 'Kategorie bearbeiten',
'update_item' => 'Kategorie aktualisieren',
'add_new_item' => 'Neue Kategorie',
'new_item_name' => 'Kategorie Name',
'menu_name' => 'Kategorien',
'popular_items' => 'Beliebte Kategorien',
'separate_items_with_commas' => "Kategorien mit Kommas trennen",
'add_or_remove_items' => 'Kategorien hinzufügen oder entfernen',
'choose_from_most_used' => 'Häufigste Kategorien auswählen',
'not_found' => 'Keine Kategorien gefunden'
];
$args = [
'hierarchical' => true,
'labels' => $labels,
'show_ui' => true,
'show_in_menu' => true, // Erscheint als Menüpunkt
'show_in_nav_menus' => false,
'show_admin_column' => true,
'public' => false,
'rewrite' => false,
];
register_taxonomy('wis_category', 'wis_item', $args);
}
public static function add_meta_boxes() {
add_meta_box('wis_item_meta', 'Item Einstellungen', [self::class, 'render_item_meta'], 'wis_item', 'normal', 'high');
add_meta_box('wis_server_meta', 'Server Einstellungen', [self::class, 'render_server_meta'], 'wis_server', 'normal', 'high');
add_meta_box('wis_coupon_meta', 'Gutschein Einstellungen', [self::class, 'render_coupon_meta'], 'wis_coupon', 'normal', 'high');
}
public static function render_item_meta($post) {
wp_nonce_field('wis_save_item', 'wis_item_nonce');
$price = get_post_meta($post->ID,'_wis_price',true);
$item_id = get_post_meta($post->ID,'_wis_item_id',true);
$servers = get_post_meta($post->ID,'_wis_servers',true);
if(!is_array($servers)) $servers = [];
$is_offer = get_post_meta($post->ID,'_wis_is_offer',true);
$is_daily_deal = get_post_meta($post->ID,'_wis_daily_deal',true);
$offer_price = get_post_meta($post->ID,'_wis_offer_price',true);
$manual_daily = get_post_meta($post->ID,'_wis_manual_daily_deal',true);
$all_servers = get_posts(['post_type'=>'wis_server','numberposts'=>-1]);
$currency = get_option('wis_currency_name', 'Coins');
$exclude_offers = get_option('wis_coupon_exclude_offers');
// NEU: Kategorien
$cats = wp_get_object_terms($post->ID, 'wis_category');
$cat_ids = array_map(function($t) { return $t->term_id; }, $cats);
$current_status = get_post_status($post->ID);
$is_active = ($current_status === 'publish') && ($price > 0);
$status_msg = $is_active ? '✅ Aktiv im Shop ' : '⏸️ Inaktiv (Kein Preis oder Entwurf) ';
echo '
'.$status_msg.'
';
echo 'Titel (Anzeige im Shop) ';
echo '
Wird automatisch aus dem Post-Titel übernommen
';
echo 'Preis ('.esc_html($currency).')
';
echo 'Wenn du hier einen Preis einträgst (>0), wird das Item automatisch aktiv.
';
echo 'Item ID (z.B. minecraft:diamond) ';
echo '
✅ Daraus wird automatisch das Bild generiert.
';
// NEU: Kategorie Auswahl
echo 'Kategorie(n) ';
$terms = get_terms([
'taxonomy' => 'wis_category',
'hide_empty' => false,
]);
echo '
';
if(!empty($terms)) {
foreach($terms as $term) {
$selected = in_array($term->term_id, $cat_ids) ? 'checked' : '';
echo '';
echo ' '.esc_html($term->name);
echo ' ';
}
} else {
echo 'Noch keine Kategorien erstellt. ';
}
echo '
';
echo '';
echo '
Daily Deal (Angebot des Tages)
';
echo '
Manuell als "Angebot des Tages" setzen 🎁 ';
echo 'Überschreibt die Automatik bis Mitternacht. Praktisch zum Testen. ';
if($is_daily_deal) {
echo 'ℹ️ Info: Dieses Item ist aktuell das Angebot des Tages. ';
echo 'Der Rabatt wurde automatisch berechnet.';
}
echo '
';
echo '';
echo '
Angebot / Sale
';
echo '
Als "Angebot" markieren 🔥 ';
echo 'Hervorgehobenes Item im Shop (rot/gold Badge). ';
if($exclude_offers) {
echo 'Hinweis: Gutscheine sind aktuell global für Angebote deaktiviert.';
}
echo '
';
echo '
Angebots-Preis (Optional - wird ignoriert wenn Daily Deal aktiv) ';
echo ' ';
echo 'Wenn ausgefüllt, wird der normale Preis durchgestrichen und dieser angezeigt.
';
echo '
';
echo 'Beschreibung ';
echo '
Dies ist der lange Text, der unter dem Titel im Shop angezeigt wird.
';
echo 'Server zuweisen (Mehrfachauswahl möglich) ';
echo '
';
if(empty($all_servers)) echo 'Noch keine Server erstellt. ';
foreach($all_servers as $s){
$checked = in_array($s->post_name, $servers) ? 'checked' : '';
echo '';
echo ' ';
echo esc_html($s->post_title);
echo ' ';
}
echo '
';
}
public static function render_server_meta($post){
echo '';
echo 'Info: Da keine RCON-Verbindung benötigt wird (Vault/Plugin bestätigt), sind hier keine weiteren Einstellungen nötig. ';
echo 'Der Servername dient nur zur Identifikat im Shop-Frontend.';
echo '
';
}
public static function render_coupon_meta($post) {
wp_nonce_field('wis_save_coupon', 'wis_coupon_nonce');
$code = get_post_meta($post->ID,'_wis_code',true);
$value = get_post_meta($post->ID,'_wis_value',true);
$limit = get_post_meta($post->ID,'_wis_usage_limit',true);
$expiry = get_post_meta($post->ID,'_wis_expiry',true);
$type = get_post_meta($post->ID,'_wis_type',true);
$exclude_offers = get_option('wis_coupon_exclude_offers');
if(empty($type)) $type = 'fixed';
echo 'Gutschein Code ';
echo ' ';
echo '
Rabattart ';
echo '';
echo 'Festbetrag (z.B. -100 Coins) ';
echo 'Prozentual (z.B. 10% Rabatt) ';
echo ' ';
echo '
Wert ';
echo ' ';
if($exclude_offers) {
echo '
';
echo '⚠️ Achtung: In den Shop-Einstellungen ist aktiviert, dass Gutscheine nicht auf Angebot-Items (Sale) angewendet werden. Dieser Gutschein wirkt nur auf reguläre Items.';
echo '
';
}
echo 'Nutzungslimit ';
echo ' ';
echo '
Ablaufdatum (Optional) ';
echo ' ';
}
public static function save_meta($post_id){
if(defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) return;
if(get_post_type($post_id)==='wis_item' && isset($_POST['wis_item_nonce']) && wp_verify_nonce($_POST['wis_item_nonce'],'wis_save_item')){
update_post_meta($post_id,'_wis_price',intval($_POST['wis_price']));
update_post_meta($post_id,'_wis_item_id',sanitize_text_field($_POST['wis_item_id']));
$is_offer = isset($_POST['wis_is_offer']) ? 1 : 0;
update_post_meta($post_id,'_wis_is_offer', $is_offer);
update_post_meta($post_id,'_wis_offer_price', intval($_POST['wis_offer_price']));
// NEU: Manual Daily Deal Logic
$manual_daily = isset($_POST['wis_manual_daily_deal']) ? 1 : 0;
if($manual_daily) {
update_post_meta($post_id,'_wis_daily_deal', 1);
update_post_meta($post_id,'_wis_is_offer', 1);
} else {
// Wenn manuell abgewählt, löschen wir das Flag, damit der Cron ran darf
// update_post_meta($post_id,'_wis_daily_deal', 0); // Optional: Reset flag
}
$servers = isset($_POST['wis_servers']) && is_array($_POST['wis_servers']) ? array_map('sanitize_text_field', $_POST['wis_servers']) : [];
update_post_meta($post_id,'_wis_servers', $servers);
$cats = isset($_POST['wis_cats']) ? array_map('intval', $_POST['wis_cats']) : [];
wp_set_object_terms($post_id, $cats, 'wis_category');
}
if(get_post_type($post_id)==='wis_server' && isset($_POST['wis_server_nonce']) && wp_verify_nonce($_POST['wis_server_nonce'],'wis_save_server')){
// Nichts zu tun
}
if(get_post_type($post_id)==='wis_coupon' && isset($_POST['wis_coupon_nonce']) && wp_verify_nonce($_POST['wis_coupon_nonce'],'wis_save_coupon')){
$code = sanitize_text_field(strtoupper($_POST['wis_code']));
$value = intval($_POST['wis_value']);
$limit = intval($_POST['wis_usage_limit']);
$expiry = sanitize_text_field($_POST['wis_expiry']);
$type = sanitize_text_field($_POST['wis_type']);
if(!in_array($type, ['fixed', 'percent'])) $type = 'fixed';
update_post_meta($post_id,'_wis_code', $code);
update_post_meta($post_id,'_wis_value', $value);
update_post_meta($post_id,'_wis_type', $type);
update_post_meta($post_id,'_wis_usage_limit', $limit);
update_post_meta($post_id,'_wis_expiry', $expiry);
global $wpdb;
$table_name = $wpdb->prefix . 'wis_coupons';
$data = ['code' => $code, 'value' => $value, 'type' => $type, 'usage_limit' => $limit, 'expiry' => $expiry];
$existing = $wpdb->get_row($wpdb->prepare("SELECT id FROM $table_name WHERE code = %s", $code));
if($existing) {
$wpdb->update($table_name, $data, ['id' => $existing->id]);
} else {
$wpdb->insert($table_name, $data);
}
$db_coupon = $wpdb->get_row($wpdb->prepare("SELECT used_count FROM $table_name WHERE code = %s", $code));
if($db_coupon) {
update_post_meta($post_id, '_wis_used_count', $db_coupon->used_count);
}
}
}
public static function auto_update_status($post_id) {
static $processing = [];
if (isset($processing[$post_id])) return;
$processing[$post_id] = true;
if (get_post_type($post_id) != 'wis_item') {
unset($processing[$post_id]);
return;
}
$price = get_post_meta($post_id, '_wis_price', true);
$current_status = get_post_status($post_id);
$new_status = ($price > 0) ? 'publish' : 'draft';
if ($current_status !== $new_status) {
remove_action('save_post', [__CLASS__, 'auto_update_status'], 10);
wp_update_post(['ID' => $post_id, 'post_status' => $new_status]);
add_action('save_post', [__CLASS__, 'auto_update_status'], 10);
}
unset($processing[$post_id]);
}
public static function delete_coupon_from_table($post_id) {
if(get_post_type($post_id) != 'wis_coupon') return;
$code = get_post_meta($post_id, '_wis_code', true);
if($code) {
global $wpdb;
$wpdb->delete($wpdb->prefix . 'wis_coupons', ['code' => $code]);
}
}
}
// ===========================================================
// Bulk Delete Funktionalität
// ===========================================================
class WIS_Bulk_Actions {
public static function register_bulk_actions($bulk_actions) {
$bulk_actions['delete'] = __('Löschen', 'wis');
return $bulk_actions;
}
public static function handle_bulk_actions($redirect_to, $action, $post_ids) {
if ($action !== 'delete') return $redirect_to;
$deleted = 0;
foreach ($post_ids as $post_id) {
if (current_user_can('delete_post', $post_id)) {
wp_delete_post($post_id, true);
$deleted++;
}
}
if ($deleted > 0) {
$redirect_to = add_query_arg([
'bulk_deleted' => $deleted,
'post_type' => get_post_type($post_ids[0] ?? 0)
], $redirect_to);
}
return $redirect_to;
}
public static function admin_notices() {
if (!empty($_GET['bulk_deleted'])) {
$count = intval($_GET['bulk_deleted']);
$msg = sprintf(_n('%d Eintrag gelöscht.', '%d Einträge gelöscht.', $count, 'wis'), $count);
echo '
';
}
}
}
// ===========================================================
// 3. ADMIN MENU
// ===========================================================
class WIS_Admin {
public static function register_menu() {
add_menu_page('Ingame Shop', 'Ingame Shop', 'manage_options', 'wis_shop', [self::class, 'page_overview'], 'dashicons-cart', 6);
add_submenu_page('wis_shop', 'Bestellungen', 'Bestellungen', 'manage_options', 'wis_orders', [self::class, 'page_orders']);
add_submenu_page('wis_shop', 'Top Spender', 'Top Spender', 'manage_options', 'wis_top_spenders', [self::class, 'page_top_spenders']);
add_submenu_page('wis_shop', 'Items', 'Items', 'manage_options', 'edit.php?post_type=wis_item');
add_submenu_page('wis_shop', 'Kategorien', 'Kategorien', 'manage_options', 'edit-tags.php?taxonomy=wis_category');
add_submenu_page('wis_shop', 'Servers', 'Servers', 'manage_options', 'edit.php?post_type=wis_server');
add_submenu_page('wis_shop', 'Gutscheine', 'Gutscheine', 'manage_options', 'edit.php?post_type=wis_coupon');
}
public static function page_overview() {
if(isset($_POST['wis_save_settings']) && check_admin_referer('wis_settings_nonce')) {
update_option('wis_currency_name', sanitize_text_field($_POST['wis_currency_name']));
update_option('wis_image_base_url', sanitize_text_field($_POST['wis_image_base_url']));
update_option('wis_header_text', sanitize_textarea_field($_POST['wis_header_text']));
update_option('wis_coupon_exclude_offers', isset($_POST['wis_coupon_exclude_offers']) ? '1' : '0');
// NEU: Daily Deal Settings
update_option('wis_daily_deal_enabled', isset($_POST['wis_daily_deal_enabled']) ? '1' : '0');
update_option('wis_daily_deal_discount', intval($_POST['wis_daily_deal_discount']));
echo '✅ Einstellungen gespeichert!
';
}
$currency = get_option('wis_currency_name', 'Coins');
$img_base = get_option('wis_image_base_url', 'https://assets.minecraft-ids.com/1_21_10/');
$header_text = get_option('wis_header_text', '✅ Auto-Bilder | 💰 Sicherer Checkout');
$exclude_offers = get_option('wis_coupon_exclude_offers', '0');
// NEU: Daily Deal Settings
$daily_deal_enabled = get_option('wis_daily_deal_enabled', '0');
$daily_deal_discount = get_option('wis_daily_deal_discount', '20');
echo '🛒 Ingame Shop Pro v1.0.0 ';
echo '
✅ Feature Übersicht ';
echo '🛒 Warenkorb: Kauf mehrerer Items auf einmal. ';
echo '🔗 Einzelne Bestellung: Alles wird in einer einzigen Datenbankzeile gespeichert. ';
echo '📋 Detail-Anzeige: Im Backend siehst du genau "1x Diamant, 64x Stein" etc. & Gutschein. ';
echo '✨ Sauberer Prozess: Alle Items werden sofort nach einer Bestätigung ausgegeben. ';
echo '🔥 Angebote: Items können hervorgehoben werden. ';
echo '🎁 Daily Deal: Automatisches Angebot des Tages. ';
echo '🎫 Smart Coupons: Gutscheine (Festbetrag & Prozent) werden korrekt bei reinen Angeboten ignoriert. ';
echo '🏷️ Kategorien: Items können in Kategorien gruppiert werden. ';
echo '🏆 Top Spender: Statistiken im Backend. ';
echo ' ';
echo '
';
echo '
⚙️ Globale Einstellungen ';
echo '
';
echo '
';
echo '
';
echo '
🚀 Bulk Import ';
echo '
Smart Import: Lädt Items in Paketen (20 pro Durchlauf). Vorhandene Items werden übersprungen.
';
echo '
';
echo '
Import URL: ';
echo '
';
echo '
';
echo '1. Daten laden ';
echo '2. Import starten ';
echo '
';
echo '
';
echo '
';
echo '0 / 0 0%
';
echo '
';
echo '
';
?>
';
}
public static function page_top_spenders() {
global $wpdb;
$currency = get_option('wis_currency_name', 'Coins');
$results = $wpdb->get_results("
SELECT player_name, SUM(price) as total_spent, COUNT(*) as order_count
FROM {$wpdb->prefix}wis_orders
WHERE status = 'completed' OR status = 'cancelled'
GROUP BY player_name
ORDER BY total_spent DESC
LIMIT 50
");
echo '
🏆 Top Spender ';
echo '
Hier siehst du die Spieler, die insgesamt am meisten in deinem Shop ausgegeben haben.
';
echo '
';
echo 'Rang Spieler Ausgegeben (Total) Anzahl Bestellungen ';
if(empty($results)) {
echo 'Noch keine Statistiken vorhanden. ';
} else {
$rank = 1;
foreach($results as $row) {
$total = intval($row->total_spent);
$count = intval($row->order_count);
echo '';
echo '#' . esc_html($rank++) . ' ';
echo '' . esc_html($row->player_name) . ' ';
echo '' . esc_html($total) . ' ' . esc_html($currency) . ' ';
echo '' . esc_html($count) . ' ';
echo ' ';
}
}
echo '
';
echo '
';
}
public static function page_orders() {
global $wpdb;
$currency = get_option('wis_currency_name', 'Coins');
if(isset($_GET['action']) && isset($_GET['order_id']) && isset($_GET['_wpnonce']) && wp_verify_nonce($_GET['_wpnonce'], 'wis_order_action')) {
$action = sanitize_text_field($_GET['action']);
$id = intval($_GET['order_id']);
if($action === 'delete') { $wpdb->delete($wpdb->prefix.'wis_orders', ['id' => $id]); echo '
'; }
elseif($action === 'complete') { $wpdb->update($wpdb->prefix.'wis_orders', ['status' => 'completed'], ['id' => $id]); echo '
'; }
}
if(isset($_GET['order_id'])) {
$order = $wpdb->get_row($wpdb->prepare("SELECT * FROM {$wpdb->prefix}wis_orders WHERE id = %d", intval($_GET['order_id'])));
if($order) {
$status_colors = ['pending' => '#ffc107', 'processing' => '#0073aa', 'completed' => 'green', 'cancelled' => 'red', 'failed' => 'red'];
$status_labels = ['pending' => 'Warte auf Ingame', 'processing' => 'In Bearbeitung', 'completed' => 'Erledigt', 'cancelled' => 'Abgebrochen', 'failed' => 'Fehler'];
$status_label = $status_labels[$order->status] ?? $order->status;
$status_color = $status_colors[$order->status] ?? 'red';
$items_list = '
Keine Daten ';
$coupon_row = '
Kein Gutschein ';
$json_data = $order->response;
$decoded = json_decode($json_data, true);
if(is_array($decoded)) {
if(isset($decoded['items']) && is_array($decoded['items'])) {
$items_list = '
';
foreach($decoded['items'] as $item) {
$amount = isset($item['amount']) ? intval($item['amount']) :1;
$id = isset($item['id']) ? esc_html($item['id']) : 'Unbekannt';
$items_list .= "{$amount}x {$id} ";
}
$items_list .= ' ';
if(isset($decoded['coupon']) && !empty($decoded['coupon']['code'])) {
$code = esc_html($decoded['coupon']['code']);
$disc = intval($decoded['coupon']['discount']);
$coupon_row = "
{$code} (-{$disc} {$currency})";
}
} elseif(!empty($decoded)) {
$items_list = '
';
foreach($decoded as $item) {
$amount = isset($item['amount']) ? intval($item['amount']) :1;
$id = isset($item['id']) ? esc_html($item['id']) : 'Unbekannt';
$items_list .= "{$amount}x {$id} ";
}
$items_list .= ' ';
}
}
echo '
Bestellung #'.$order->id.' - Details ';
echo '
← Zurück ';
echo '
';
echo 'ID '.$order->id.' ';
echo 'Datum '.date('d.m.Y H:i', strtotime($order->created_at)).' ';
echo 'Spieler '.$order->player_name.' ';
echo 'Server '.$order->server.' ';
echo 'Zusammenfassung '.esc_html($order->item_title).' ';
echo 'Gutschein '.$coupon_row.' ';
echo 'Gekaufte Items '.$items_list.' ';
echo 'Gesamtpreis '.$order->price.' '.esc_html($currency).' ';
echo 'Status '.esc_html($status_label).' ';
echo 'JSON (Debug) '.esc_html($json_data).' ';
echo '
';
echo '
';
return;
}
}
$orders = $wpdb->get_results("SELECT * FROM {$wpdb->prefix}wis_orders ORDER BY created_at DESC LIMIT 100");
echo '
Bestellungen ';
echo '
Datum Spieler Inhalt Preis Status Aktion ';
if(empty($orders)) { echo 'Noch keine Bestellungen vorhanden. '; }
foreach($orders as $o){
$status_map = ['pending' => 'Warte', 'processing' => 'Geben...', 'completed' => 'Fertig', 'cancelled' => 'Abgebrochen', 'failed' => 'Fehler'];
$status_label = $status_map[$o->status] ?? $o->status;
$status_colors = ['pending' => '#ffc107', 'processing' => '#0073aa', 'completed' => 'green', 'cancelled' => 'red', 'failed' => 'red'];
$status_color = $status_colors[$o->status] ?? 'red';
$delete_url = wp_nonce_url(admin_url('admin.php?page=wis_orders&action=delete&order_id='.$o->id), 'wis_order_action');
$details_url = admin_url('admin.php?page=wis_orders&order_id='.$o->id);
$item_label = strlen($o->item_title) > 50 ? substr($o->item_title, 0, 50).'...' : $o->item_title;
echo '';
echo ''.esc_html(date('d.m.Y H:i', strtotime($o->created_at))).' ';
echo ''.esc_html($o->player_name).' ';
echo ''.esc_html($item_label).' ';
echo ''.esc_html($o->price).' '.esc_html($currency).' ';
echo ''.esc_html($status_label).' ';
echo '';
echo '👁️ Details ';
echo '🗑️ Löschen ';
echo ' ';
echo ' ';
}
echo '
';
}
}
// ===========================================================
// 4. API - AUTOMATION & CONFIRMATION
// ===========================================================
class WIS_API {
public static function register_routes() {
register_rest_route('wis/v1','/order', ['methods'=>'POST','callback'=>[self::class,'create_order'],'permission_callback'=> '__return_true']);
register_rest_route('wis/v1','/pending_orders', ['methods'=>'GET','callback'=>[self::class,'get_pending_orders'],'permission_callback'=> '__return_true']);
register_rest_route('wis/v1','/execute_order', ['methods'=>'POST','callback'=>[self::class,'execute_order'],'permission_callback'=> '__return_true']);
register_rest_route('wis/v1','/complete_order', ['methods'=>'POST','callback'=>[self::class,'complete_order'],'permission_callback'=> '__return_true']);
register_rest_route('wis/v1','/cancel_order', ['methods'=>'POST','callback'=>[self::class,'cancel_order'],'permission_callback'=> '__return_true']);
register_rest_route('wis/v1','/fetch_remote_data', ['methods'=>'POST','callback'=>[self::class,'fetch_remote_data'],'permission_callback'=> '__return_true']);
register_rest_route('wis/v1','/import_batch', ['methods'=>'POST','callback'=>[self::class,'import_batch'],'permission_callback'=> '__return_true']);
register_rest_route('wis/v1','/validate_coupon', ['methods'=>'POST','callback'=>[self::class,'validate_coupon'],'permission_callback'=> '__return_true']);
}
public static function create_order($request) {
global $wpdb;
$data = $request->get_json_params();
$player = sanitize_text_field($data['player'] ?? '');
$cart = $data['cart'] ?? [];
$server_slug = sanitize_text_field($data['server'] ?? '');
$coupon_code = isset($data['coupon_code']) ? sanitize_text_field(strtoupper($data['coupon_code'])) : '';
if(!$player || empty($cart) || !$server_slug) { return new WP_REST_Response(['success'=>false,'message'=>'Fehlende Eingabedaten'], 400); }
$exclude_offers_setting = get_option('wis_coupon_exclude_offers', '0');
$valid_cart = [];
$total_normal = 0;
$total_offer = 0;
foreach($cart as $item) {
$post_id = intval($item['id'] ?? 0);
$qty = intval($item['quantity'] ?? 1);
if($post_id <= 0 || $qty <= 0) continue;
$price = intval(get_post_meta($post_id, '_wis_price', true));
$item_servers = get_post_meta($post_id, '_wis_servers', true);
$is_offer = get_post_meta($post_id, '_wis_is_offer', true);
if(!is_array($item_servers)) $item_servers = [];
if($price <= 0) continue;
if(!in_array($server_slug, $item_servers)) continue;
$offer_price = intval(get_post_meta($post_id, '_wis_offer_price', true));
$display_price = ($offer_price > 0) ? $offer_price : $price;
$real_item_id = get_post_meta($post_id, '_wis_item_id', true);
$valid_cart[] = [
'id' => $post_id,
'title' => get_the_title($post_id),
'price' => $display_price,
'qty' => $qty,
'is_offer' => $is_offer,
'real_item_id' => $real_item_id
];
$item_total = $display_price * $qty;
if($is_offer && $exclude_offers_setting) {
$total_offer += $item_total;
} else {
$total_normal += $item_total;
}
}
if(empty($valid_cart)) return new WP_REST_Response(['success'=>false,'message'=>'Keine gültigen Items im Warenkorb.'], 400);
// Gutschein Logik
$coupon_discount = 0;
$coupon_msg = '';
$coupon_applied = false;
$currency = get_option('wis_currency_name', 'Coins');
if(!empty($coupon_code)) {
$coupon = $wpdb->get_row($wpdb->prepare("SELECT * FROM {$wpdb->prefix}wis_coupons WHERE code = %s", $coupon_code));
if($coupon) {
// Basis Checks
if($coupon->expiry && date('Y-m-d') > $coupon->expiry) {
$coupon_msg = 'Gutschein abgelaufen.';
} elseif($coupon->used_count >= $coupon->usage_limit) {
$coupon_msg = 'Gutschein wurde zu oft verwendet.';
} else {
// Prüfen ob Gutschein angewendet werden darf
$apply_coupon = false;
$calculate_discount = false;
if($exclude_offers_setting === '1') {
if($total_normal > 0) {
$apply_coupon = true;
}
} else {
if($total_normal > 0 || $total_offer > 0) {
$apply_coupon = true;
}
}
if($apply_coupon) {
$calculate_discount = true;
}
}
} else {
$coupon_msg = 'Ungültiger Gutscheincode.';
}
}
// Wenn angewendet, berechnen
if($calculate_discount) {
$coupon_type = $coupon->type;
if($coupon_type === 'percent') {
$coupon_discount = $total_normal * ($coupon->value / 100);
$coupon_msg = "Gutschein eingelöst: -{$coupon->value}% (-{$coupon_discount} {$currency})";
} else {
$coupon_discount = $coupon->value;
$coupon_msg = "Gutschein eingelöst: -{$coupon_discount} {$currency}";
}
// Aufrunden
$coupon_discount = floor($coupon_discount);
$wpdb->update($wpdb->prefix.'wis_coupons', ['used_count' => $coupon->used_count + 1], ['id' => $coupon->id]);
$coupon_applied = true;
}
$final_normal = max(0, $total_normal - $coupon_discount);
$final_price = $final_normal + $total_offer;
// Speichern in DB
$items_payload = [];
$title_parts = [];
foreach($valid_cart as $item) {
$items_payload[] = [
'id' => $item['real_item_id'],
'amount' => $item['qty']
];
$title_parts[] = $item['qty'] . 'x ' . $item['title'];
}
$readable_title = "Warenkorb: " . implode(', ', $title_parts);
if (strlen($readable_title) > 240) $readable_title = substr($readable_title, 0, 237) . '...';
$coupon_data = [];
if($coupon_applied) {
$coupon_data = [
'code' => $coupon_code,
'discount' => $coupon_discount
];
}
$json_payload = json_encode([
'items' => $items_payload,
'coupon' => $coupon_data
]);
$inserted = $wpdb->insert($wpdb->prefix.'wis_orders', [
'player_name' => $player,
'server' => $server_slug,
'item_id' => 'multi_item_cart',
'item_title' => $readable_title,
'price' => $final_price,
'quantity' => count($valid_cart),
'status' => 'pending',
'response' => $json_payload,
'created_at' => current_time('mysql')
]);
if($inserted) {
$msg = '✅ Bestellung erfolgreich erstellt!';
if($coupon_msg) $msg .= ' ('.$coupon_msg.')';
return new WP_REST_Response(['success'=>true,'message'=>$msg], 200);
} else {
return new WP_REST_Response(['success'=>false,'message'=>'Fehler beim Speichern.'], 500);
}
}
public static function validate_coupon($request) {
global $wpdb;
$data = $request->get_json_params();
$code = sanitize_text_field(strtoupper($data['code'] ?? ''));
$cart = $data['cart'] ?? [];
$currency = get_option('wis_currency_name', 'Coins');
if(!$code) {
return new WP_REST_Response(['success'=>false, 'message'=>'Kein Code angegeben']);
}
$coupon = $wpdb->get_row($wpdb->prepare("SELECT * FROM {$wpdb->prefix}wis_coupons WHERE code = %s", $code));
if(!$coupon) {
return new WP_REST_Response(['success'=>false, 'message'=>'Gutschein nicht gefunden']);
}
if($coupon->expiry && date('Y-m-d') > $coupon->expiry) {
return new WP_REST_Response(['success'=>false, 'message'=>'Gutschein abgelaufen']);
}
if($coupon->used_count >= $coupon->usage_limit) {
return new WP_REST_Response(['success'=>false, 'message'=>'Dieser Gutschein wurde bereits maximal eingelöst']);
}
$exclude_offers_setting = get_option('wis_coupon_exclude_offers', '0');
if($exclude_offers_setting === '1' && !empty($cart)) {
$has_normal_items = false;
foreach($cart as $item) {
$post_id = intval($item['id'] ?? 0);
$is_offer = get_post_meta($post_id, '_wis_is_offer', true);
if(empty($is_offer) || $is_offer == 0) {
$has_normal_items = true;
break;
}
}
if(!$has_normal_items) {
return new WP_REST_Response(['success'=>false, 'message'=>'⚠️ Dieser Gutschein gilt nicht für Angebote.']);
}
}
$message = '';
if($coupon->type === 'percent') {
$message = "Gutschein gültig (-{$coupon->value}%)";
} else {
$message = "Gutschein gültig (-{$coupon->value} {$currency})";
}
return new WP_REST_Response(['success'=>true, 'type'=>$coupon->type, 'value'=>$coupon->value, 'message'=>$message]);
}
public static function get_pending_orders($request) {
global $wpdb;
$player = sanitize_text_field($request->get_param('player'));
if(!$player) return new WP_REST_Response(['orders'=>[]]);
$results = $wpdb->get_results($wpdb->prepare("SELECT * FROM {$wpdb->prefix}wis_orders WHERE player_name = %s AND status = 'pending' ORDER BY created_at ASC LIMIT 10", $player));
return new WP_REST_Response(['orders' => $results]);
}
public static function execute_order($request) {
global $wpdb;
$data = $request->get_json_params();
$id = intval($data['id'] ?? 0);
if(!$id) return new WP_REST_Response(['success'=>false], 400);
$order = $wpdb->get_row($wpdb->prepare("SELECT * FROM {$wpdb->prefix}wis_orders WHERE id = %d", $id));
if($order) {
$wpdb->update($wpdb->prefix.'wis_orders', ['status' => 'processing'], ['id' => $id]);
return new WP_REST_Response(['success'=>true]);
}
return new WP_REST_Response(['success'=>false], 400);
}
public static function complete_order($request) {
global $wpdb;
$data = $request->get_json_params();
$id = intval($data['id'] ?? 0);
if(!$id) return new WP_REST_Response(['success'=>false], 400);
$wpdb->update($wpdb->prefix.'wis_orders', ['status' => 'completed'], ['id' => $id]);
return new WP_REST_Response(['success'=>true]);
}
public static function cancel_order($request) {
global $wpdb;
$data = $request->get_json_params();
$id = intval($data['id'] ?? 0);
if(!$id) return new WP_REST_Response(['success'=>false], 400);
$updated = $wpdb->update($wpdb->prefix.'wis_orders', ['status' => 'cancelled'], ['id' => $id]);
if($updated !== false) {
return new WP_REST_Response(['success'=>true]);
}
return new WP_REST_Response(['success'=>false], 400);
}
public static function fetch_remote_data($request) {
$url = esc_url_raw($request->get_json_params()['url']);
$response = wp_remote_get($url, ['timeout' => 30]);
if(is_wp_error($response)) return new WP_REST_Response(['success'=>false, 'message'=>$response->get_error_message()], 400);
$body = wp_remote_retrieve_body($response);
$data = json_decode($body, true);
$import_data = [];
if (isset($data['items']) && is_array($data['items'])) { $import_data = $data['items']; } elseif (is_array($data)) { $import_data = $data; }
if(empty($import_data)) return new WP_REST_Response(['success'=>false, 'message'=>'Keine gültigen Daten gefunden'], 400);
return new WP_REST_Response(['success'=>true, 'items' => $import_data]);
}
public static function import_batch($request) {
$items = $request->get_json_params()['items'] ?? [];
if(!is_array($items) || empty($items)) return new WP_REST_Response(['success'=>false], 400);
set_time_limit(300);
$processed = 0; $skipped = 0;
foreach($items as $item) {
$item_id_val = $item['id'] ?? ''; $title_val = $item['name'] ?? 'Unbekannt';
if(empty($item_id_val)) continue;
$exists = get_posts(['post_type' => 'wis_item', 'meta_key' => '_wis_item_id', 'meta_value' => $item_id_val, 'posts_per_page' => 1]);
if($exists) { $skipped++; continue; }
$new_post_id = wp_insert_post(['post_title' => $title_val, 'post_content' => '', 'post_type' => 'wis_item', 'post_status' => 'draft', 'meta_input' => ['_wis_item_id' => $item_id_val, '_wis_price' => 0]]);
if(!is_wp_error($new_post_id)) $processed++;
}
return new WP_REST_Response(['success'=>true, 'processed' => $processed, 'skipped' => $skipped]);
}
}
// ===========================================================
// 5. SHORTCODE
// ===========================================================
class WIS_Shortcode {
public static function register_shortcode() {
add_shortcode('ingame_shop_form', [self::class, 'render_form']);
}
public static function render_form() {
$servers = get_posts(['post_type'=>'wis_server','numberposts'=>-1]);
$items = get_posts(['post_type'=>'wis_item','numberposts'=>-1]);
$currency = get_option('wis_currency_name', 'Coins');
$img_base = get_option('wis_image_base_url', 'https://assets.minecraft-ids.com/1_21_10/');
$header_text = get_option('wis_header_text', '✅ Auto-Bilder | 💰 Sicherer Checkout');
$exclude_offers = get_option('wis_coupon_exclude_offers', '0');
// NEU: Kategorien
$categories = get_terms(['taxonomy'=>'wis_category', 'hide_empty'=>true]);
ob_start();
?>
Alle
=esc_html($cat->name)?>
Keine Items im Shop verfügbar.
ID, '_wis_servers', true);
if(!is_array($item_servers)) $item_servers = [];
$price = get_post_meta($item->ID, '_wis_price', true);
$item_id_code = get_post_meta($item->ID, '_wis_item_id', true);
$item_desc = $item->post_content;
$is_offer = get_post_meta($item->ID, '_wis_is_offer', true);
$is_daily_deal = get_post_meta($item->ID, '_wis_daily_deal', true);
$offer_price = get_post_meta($item->ID, '_wis_offer_price', true);
$display_price = ($offer_price > 0) ? $offer_price : $price;
$show_old_price = ($offer_price > 0 && $offer_price != $price);
// NEU: Kategorien
$item_cats = wp_get_post_terms($item->ID, 'wis_category');
$cat_ids = array_map(function($t) { return $t->term_id; }, $item_cats);
$cat_data = json_encode($cat_ids);
$img_name = str_replace(':', '_', $item_id_code) . '.png';
$full_img_url = $img_base . $img_name;
$server_names = [];
foreach($item_servers as $slug) {
$srv = get_page_by_path($slug, OBJECT, 'wis_server');
if($srv) $server_names[] = $srv->post_title;
}
$servers_display = !empty($server_names) ? implode(', ', $server_names) : 'Kein Server';
$servers_data = json_encode($item_servers);
?>
🎁 Angebot des Tages
🔥 Angebot
=esc_html($item->post_title)?>
=esc_html($price)?> =esc_html($currency)?>
=esc_html($display_price)?> =esc_html($currency)?>
📡 =esc_html($servers_display)?>
⚠️ Gutschein ungültig
-
+
➕ In den Warenkorb
🛒 Dein Warenkorb
Gesamt:
0 =esc_html($currency)?>
-- Server wählen --
=esc_html($s->post_title)?>
💰 Kauf abschließen
Abbrechen