'Cache erfolgreich geleert!']); } // Prüfe auf Updates (läuft im Admin-Hintergrund) public static function check_for_update() { $update_data = get_transient(self::$transient_name); if (false === $update_data) { $response = wp_remote_get(self::$api_url, ['timeout' => 10]); if (is_wp_error($response)) { return; // Fehler beim Abrufen, nichts tun } $body = wp_remote_retrieve_body($response); $data = json_decode($body); if (isset($data->tag_name) && isset($data->html_url)) { // Entferne 'v' vom Tag (z.B. v2.1.3 -> 2.1.3) $remote_version = ltrim($data->tag_name, 'v'); $update_data = [ 'new_version' => $remote_version, 'url' => self::$update_url, 'package_url' => isset($data->assets[0]->browser_download_url) ? $data->assets[0]->browser_download_url : '', 'changelog' => isset($data->body) ? substr($data->body, 0, 200) . '...' : '' ]; // Speichere für 12 Stunden set_transient(self::$transient_name, $update_data, 12 * HOUR_IN_SECONDS); } } } // Zeige Admin Notice wenn Update verfügbar public static function show_update_notice() { $update_data = get_transient(self::$transient_name); if ($update_data && version_compare(WIS_VERSION, $update_data['new_version'], '<')) { ?>

🚀 WP Ingame Shop Pro Update verfügbar!
Neue Version: (Du hast )
Hier im Git Repository ansehen oder unten im Widget Details prüfen.

$wpdb->get_var("SELECT COUNT(*) FROM {$wpdb->prefix}wis_items WHERE status = 'publish'"), 'orders' => $wpdb->get_var("SELECT COUNT(*) FROM {$wpdb->prefix}wis_orders WHERE status = 'completed'"), 'pending' => $wpdb->get_var("SELECT COUNT(*) FROM {$wpdb->prefix}wis_orders WHERE status IN ('pending', 'claimed')"), 'revenue' => $wpdb->get_var("SELECT SUM(price) FROM {$wpdb->prefix}wis_orders WHERE status = 'completed'"), ]; $currency = get_option('wis_currency_name', 'Coins'); // Update Status bestimmen $update_html = '✅ Aktuell (v' . WIS_VERSION . ')'; if ($update_data && version_compare(WIS_VERSION, $update_data['new_version'], '<')) { $update_html = '⚠️ Update verfügbar!'; } ?>
Items
Bestellungen
Offen
Umsatz ()

Status:

Neue Version
Updates ansehen
Bestellungen ansehen
prefix . 'wis_items'; $col = $wpdb->get_var("SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = '$table' AND COLUMN_NAME = 'custom_image_url'"); if (!$col) { $wpdb->query("ALTER TABLE $table ADD COLUMN custom_image_url varchar(500) DEFAULT NULL AFTER categories"); } // Ankauf-Spalten nachrüsten (ab v6.5) $col_sell = $wpdb->get_var("SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = '$table' AND COLUMN_NAME = 'sell_enabled'"); if (!$col_sell) { $wpdb->query("ALTER TABLE $table ADD COLUMN sell_enabled tinyint(1) NOT NULL DEFAULT 0 AFTER status"); $wpdb->query("ALTER TABLE $table ADD COLUMN sell_price_mode varchar(20) NOT NULL DEFAULT 'percent' AFTER sell_enabled"); $wpdb->query("ALTER TABLE $table ADD COLUMN sell_price_value int(11) NOT NULL DEFAULT 80 AFTER sell_price_mode"); } // Sell-Log-Tabelle anlegen $wpdb->query("CREATE TABLE IF NOT EXISTS {$wpdb->prefix}wis_sell_log ( 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_name varchar(255) NOT NULL, quantity int(11) NOT NULL DEFAULT 1, price_per_item decimal(10,2) NOT NULL, total_paid decimal(10,2) NOT NULL, sold_at datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY player_name (player_name), KEY sold_at (sold_at) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;"); self::create_tables(); self::set_default_options(); self::create_default_categories(); if (!wp_next_scheduled('wis_daily_deal_event')) { wp_schedule_event(time(), 'daily', 'wis_daily_deal_event'); } // Fly-Abo Renewal: täglich prüfen (läuft nur durch am 1. des Monats) if (!wp_next_scheduled('wis_abo_renewal_event')) { // Nächsten Mitternacht-Zeitstempel berechnen $midnight = strtotime('tomorrow midnight'); wp_schedule_event($midnight, 'daily', 'wis_abo_renewal_event'); } flush_rewrite_rules(); } public static function deactivate() { wp_clear_scheduled_hook('wis_daily_deal_event'); wp_clear_scheduled_hook('wis_abo_renewal_event'); flush_rewrite_rules(); } private static function create_tables() { global $wpdb; $charset_collate = $wpdb->get_charset_collate(); $tables = []; // Items $tables[] = "CREATE TABLE {$wpdb->prefix}wis_items ( id mediumint(9) NOT NULL AUTO_INCREMENT, item_id varchar(100) NOT NULL, name varchar(255) NOT NULL, description text, price int(11) DEFAULT 0, offer_price int(11) DEFAULT 0, is_offer tinyint(1) DEFAULT 0, is_daily_deal tinyint(1) DEFAULT 0, servers text, categories text, custom_image_url varchar(500) DEFAULT NULL, status varchar(20) DEFAULT 'draft', created_at datetime DEFAULT CURRENT_TIMESTAMP, updated_at datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY item_id (item_id), KEY status (status) ) $charset_collate;"; // Orders $tables[] = "CREATE TABLE {$wpdb->prefix}wis_orders ( 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, PRIMARY KEY (id), KEY player_name (player_name), KEY status (status) ) $charset_collate;"; // Coupons $tables[] = "CREATE TABLE {$wpdb->prefix}wis_coupons ( 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, PRIMARY KEY (id), UNIQUE KEY code (code) ) $charset_collate;"; // Servers $tables[] = "CREATE TABLE {$wpdb->prefix}wis_servers ( id mediumint(9) NOT NULL AUTO_INCREMENT, slug varchar(100) NOT NULL, name varchar(255) NOT NULL, created_at datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY slug (slug) ) $charset_collate;"; // Categories $tables[] = "CREATE TABLE {$wpdb->prefix}wis_categories ( id mediumint(9) NOT NULL AUTO_INCREMENT, name varchar(255) NOT NULL, slug varchar(100) NOT NULL, created_at datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY slug (slug) ) $charset_collate;"; require_once(ABSPATH . 'wp-admin/includes/upgrade.php'); foreach ($tables as $sql) { dbDelta($sql); } } private static function set_default_options() { $defaults = [ 'wis_currency_name' => 'Coins', 'wis_image_base_url' => 'https://git.viper.ipv64.net/M_Viper/minecraft-items/raw/branch/main/images/', 'wis_header_text' => '✅ Auto-Bilder | 💰 Sicherer Checkout | 🎮 Ingame-Bestätigung', 'wis_coupon_exclude_offers' => '0', 'wis_daily_deal_enabled' => '0', 'wis_daily_deal_discount' => '20', 'wis_api_key' => bin2hex(random_bytes(24)), ]; foreach ($defaults as $key => $value) { if (get_option($key) === false) { add_option($key, $value); } } } public static function check_api_key($request) { $key = $request->get_header('X-WIS-Key'); if (empty($key)) { $key = $request->get_param('api_key'); } $stored = get_option('wis_api_key', ''); return ($stored !== '' && hash_equals($stored, (string) $key)); } public static function spigot_permission($request) { if (!self::check_api_key($request)) { return new WP_Error('wis_unauthorized', 'Ungültiger oder fehlender API-Key.', ['status' => 401]); } return true; } private static function create_default_categories() { $default_categories = [ ['name' => 'Baublöcke', 'slug' => 'baublocke'], ['name' => 'Dekorationsblöcke', 'slug' => 'dekorationsblocke'], ['name' => 'Redstone', 'slug' => 'redstone'], ['name' => 'Transport', 'slug' => 'transport'], ['name' => 'Natur', 'slug' => 'natur'], ['name' => 'Werkzeuge & Hilfsmittel', 'slug' => 'werkzeuge-hilfsmittel'], ['name' => 'Kampf', 'slug' => 'kampf'], ['name' => 'Nahrung & Tränke', 'slug' => 'nahrung-tranke'], ['name' => 'Zutaten', 'slug' => 'zutaten'], ['name' => 'Spawn-Eier', 'slug' => 'spawn-eier'] ]; global $wpdb; foreach ($default_categories as $cat) { $exists = $wpdb->get_var($wpdb->prepare( "SELECT id FROM {$wpdb->prefix}wis_categories WHERE slug = %s", $cat['slug'] )); if (!$exists) { $wpdb->insert($wpdb->prefix . 'wis_categories', [ 'name' => $cat['name'], 'slug' => $cat['slug'] ]); } } } public static function run_daily_deal() { if (get_option('wis_daily_deal_enabled') !== '1') return; global $wpdb; $table = $wpdb->prefix . 'wis_items'; $discount = intval(get_option('wis_daily_deal_discount', 20)); $wpdb->update($table, ['is_daily_deal' => 0], ['is_daily_deal' => 1]); $item = $wpdb->get_row("SELECT * FROM $table WHERE status = 'publish' AND price > 0 ORDER BY RAND() LIMIT 1"); if ($item) { $offer_price = max(0, floor($item->price - ($item->price * ($discount / 100)))); $wpdb->update($table, [ 'is_daily_deal' => 1, 'is_offer' => 1, 'offer_price' => $offer_price ], ['id' => $item->id]); } } /** * Fly-Abo Renewal – läuft täglich, handelt aber nur am 1. des Monats. * Verlängert alle aktiven (nicht gekündigten) Abos automatisch um 30 Tage * und legt je einen Order-Eintrag als Buchungsnachweis an. * Gekündigte Abos laufen bis zum letzten Tag des laufenden Monats und werden dann entfernt. */ public static function run_abo_renewal() { // Nur am 1. des Monats ausführen if (date('j') !== '1') return; global $wpdb; $subs_table = $wpdb->prefix . 'wis_fly_abo_subs'; $orders_table = $wpdb->prefix . 'wis_orders'; // Tabelle anlegen falls noch nicht vorhanden (Migration) self::create_abo_subs_table(); // Alle aktiven, nicht gekündigten Abos verlängern $active = $wpdb->get_results( "SELECT * FROM {$subs_table} WHERE cancelled = 0 AND status = 'active'" ); foreach ($active as $sub) { // expires_at um 30 Tage verlängern $wpdb->query($wpdb->prepare( "UPDATE {$subs_table} SET expires_at = DATE_ADD(expires_at, INTERVAL 30 DAY), renewed_at = NOW(), renewal_count = renewal_count + 1 WHERE id = %d", $sub->id )); // Buchungsnachweis als Order anlegen (status: completed) $wpdb->insert($orders_table, [ 'player_name' => $sub->player_name, 'server' => $sub->server, 'item_id' => 'fly_abo_renewal', 'item_title' => '✈ Fly-Abo Verlängerung: ' . $sub->label, 'price' => $sub->price, 'quantity' => 1, 'status' => 'completed', 'response' => json_encode([ 'commands' => [[ 'type' => 'fly_abo', 'days' => 30, 'label' => $sub->label, ]], ]), ]); } // Gekündigte Abos die abgelaufen sind deaktivieren $wpdb->query( "UPDATE {$subs_table} SET status = 'expired' WHERE cancelled = 1 AND expires_at < NOW() AND status = 'active'" ); // Log $count = count($active); error_log("[WIS] Fly-Abo Renewal: {$count} Abo(s) verlängert am " . date('d.m.Y')); } public static function create_abo_subs_table() { global $wpdb; $table = $wpdb->prefix . 'wis_fly_abo_subs'; $wpdb->query("CREATE TABLE IF NOT EXISTS {$table} ( id INT AUTO_INCREMENT PRIMARY KEY, player_name VARCHAR(64) NOT NULL, server VARCHAR(64) NOT NULL DEFAULT '', label VARCHAR(128) NOT NULL, price INT NOT NULL DEFAULT 0, status VARCHAR(20) NOT NULL DEFAULT 'active', cancelled TINYINT(1) NOT NULL DEFAULT 0, cancelled_at DATETIME DEFAULT NULL, expires_at DATETIME NOT NULL, renewed_at DATETIME DEFAULT NULL, renewal_count INT NOT NULL DEFAULT 0, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, UNIQUE KEY uq_player_server (player_name, server) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;"); } public static function reset_shop() { global $wpdb; $tables = [ 'wis_items', 'wis_orders', 'wis_coupons', 'wis_servers', 'wis_categories' ]; foreach ($tables as $table) { $wpdb->query("TRUNCATE TABLE {$wpdb->prefix}$table"); } self::set_default_options(); self::create_default_categories(); return true; } } // =========================================================== // ITEM CATEGORIZER // =========================================================== class WIS_Item_Categorizer { public static function auto_categorize($item_id) { $item_id = strtolower($item_id); $item_id = str_replace('minecraft:', '', $item_id); if (strpos($item_id, 'spawn_egg') !== false) { return ['spawn-eier']; } if (preg_match('/(wooden|stone|iron|golden|diamond|netherite|copper)_(pickaxe|axe|shovel|hoe)/', $item_id)) { return ['werkzeuge-hilfsmittel']; } if (in_array($item_id, [ 'shears', 'fishing_rod', 'flint_and_steel', 'bucket', 'water_bucket', 'lava_bucket', 'milk_bucket', 'powder_snow_bucket', 'axolotl_bucket', 'cod_bucket', 'pufferfish_bucket', 'salmon_bucket', 'tadpole_bucket', 'tropical_fish_bucket', 'compass', 'recovery_compass', 'clock', 'spyglass', 'map', 'filled_map', 'brush', 'lead', 'name_tag', 'saddle', 'carrot_on_a_stick', 'warped_fungus_on_a_stick' ])) { return ['werkzeuge-hilfsmittel']; } if (preg_match('/(wooden|stone|iron|golden|diamond|netherite|copper)_(sword|spear)/', $item_id)) { return ['kampf']; } if (preg_match('/(leather|chainmail|iron|golden|diamond|netherite|copper|turtle)_(helmet|chestplate|leggings|boots|cap|tunic|pants|shell)/', $item_id)) { return ['kampf']; } if (in_array($item_id, [ 'bow', 'crossbow', 'arrow', 'spectral_arrow', 'tipped_arrow', 'shield', 'trident', 'mace', 'totem_of_undying', 'elytra', 'horse_armor', 'iron_horse_armor', 'golden_horse_armor', 'diamond_horse_armor', 'wolf_armor' ]) || strpos($item_id, 'horse_armor') !== false) { return ['kampf']; } if (preg_match('/(raw_|cooked_)?(beef|porkchop|mutton|chicken|rabbit|cod|salmon)/', $item_id)) { return ['nahrung-tranke']; } if (in_array($item_id, [ 'apple', 'golden_apple', 'enchanted_golden_apple', 'melon_slice', 'glow_berries', 'sweet_berries', 'chorus_fruit', 'carrot', 'golden_carrot', 'potato', 'baked_potato', 'poisonous_potato', 'beetroot', 'bread', 'cookie', 'cake', 'pumpkin_pie', 'dried_kelp', 'tropical_fish', 'pufferfish', 'rotten_flesh', 'spider_eye' ])) { return ['nahrung-tranke']; } if (strpos($item_id, '_stew') !== false || strpos($item_id, '_soup') !== false) { return ['nahrung-tranke']; } if (strpos($item_id, 'potion') !== false || in_array($item_id, [ 'honey_bottle', 'milk_bucket', 'glass_bottle', 'dragon_breath', 'experience_bottle', 'ominous_bottle' ])) { return ['nahrung-tranke']; } if (strpos($item_id, '_boat') !== false || strpos($item_id, '_raft') !== false) { return ['transport']; } if (strpos($item_id, 'minecart') !== false) { return ['transport']; } if (in_array($item_id, ['elytra', 'saddle', 'lead'])) { return ['transport']; } if (strpos($item_id, 'redstone') !== false && $item_id !== 'redstone_ore') { return ['redstone']; } if (in_array($item_id, [ 'repeater', 'comparator', 'observer', 'piston', 'sticky_piston', 'dispenser', 'dropper', 'hopper', 'lever', 'tripwire_hook', 'daylight_detector', 'tnt', 'target', 'lightning_rod' ]) || strpos($item_id, 'button') !== false || strpos($item_id, 'pressure_plate') !== false) { return ['redstone']; } if (strpos($item_id, '_rail') !== false || $item_id === 'rail') { return ['redstone']; } $pure_materials = [ 'stick', 'coal', 'charcoal', 'diamond', 'emerald', 'lapis_lazuli', 'iron_ingot', 'gold_ingot', 'copper_ingot', 'netherite_ingot', 'iron_nugget', 'gold_nugget', 'copper_nugget', 'raw_iron', 'raw_gold', 'raw_copper', 'netherite_scrap', 'netherite_upgrade', 'amethyst_shard', 'prismarine_shard', 'prismarine_crystals', 'quartz', 'nether_quartz', 'echo_shard', 'disc_fragment', 'string', 'feather', 'leather', 'rabbit_hide', 'slimeball', 'ender_pearl', 'ender_eye', 'blaze_rod', 'blaze_powder', 'magma_cream', 'ghast_tear', 'nether_star', 'nether_brick', 'nautilus_shell', 'heart_of_the_sea', 'scute', 'turtle_scute', 'armadillo_scute', 'bone', 'bone_meal', 'gunpowder', 'glowstone_dust', 'sugar', 'phantom_membrane', 'ink_sac', 'glow_ink_sac', 'paper', 'book', 'flint', 'fermented_spider_eye', 'glistering_melon_slice', 'rabbit_foot', 'nether_wart', 'breeze_rod', 'clay_ball', 'brick', 'firework_star', 'shulker_shell', 'popped_chorus_fruit' ]; if (in_array($item_id, $pure_materials)) { return ['zutaten']; } if (strpos($item_id, '_pottery_sherd') !== false || strpos($item_id, '_armor_trim') !== false) { return ['zutaten']; } if (in_array($item_id, [ 'dirt', 'coarse_dirt', 'rooted_dirt', 'grass_block', 'podzol', 'mycelium', 'farmland', 'dirt_path', 'sand', 'red_sand', 'gravel', 'clay', 'suspicious_sand', 'suspicious_gravel' ])) { return ['natur']; } if (strpos($item_id, '_leaves') !== false || strpos($item_id, '_sapling') !== false || strpos($item_id, 'azalea') !== false) { return ['natur']; } $flowers = [ 'dandelion', 'poppy', 'blue_orchid', 'allium', 'azure_bluet', 'red_tulip', 'orange_tulip', 'white_tulip', 'pink_tulip', 'oxeye_daisy', 'cornflower', 'lily_of_the_valley', 'wither_rose', 'sunflower', 'lilac', 'rose_bush', 'peony', 'pitcher_plant', 'pitcher_pod', 'torchflower', 'torchflower_seeds', 'pink_petals', 'spore_blossom' ]; if (in_array($item_id, $flowers)) { return ['natur']; } if (strpos($item_id, 'mushroom') !== false || strpos($item_id, 'fungus') !== false || in_array($item_id, ['short_grass', 'tall_grass', 'fern', 'large_fern', 'dead_bush'])) { return ['natur']; } if (in_array($item_id, [ 'seagrass', 'tall_seagrass', 'kelp', 'dried_kelp', 'sea_pickle', 'vine', 'weeping_vines', 'twisting_vines', 'cave_vines', 'glow_lichen', 'hanging_roots', 'mangrove_roots', 'muddy_mangrove_roots' ])) { return ['natur']; } if (strpos($item_id, '_seeds') !== false || in_array($item_id, [ 'wheat', 'beetroot', 'carrot', 'potato', 'melon', 'pumpkin', 'carved_pumpkin', 'sugar_cane', 'bamboo', 'cocoa_beans', 'sweet_berries', 'glow_berries', 'sweet_berry_bush', 'nether_wart', 'cactus', 'mangrove_propagule' ])) { return ['natur']; } if (in_array($item_id, [ 'crimson_roots', 'warped_roots', 'nether_sprouts', 'crimson_nylium', 'warped_nylium' ])) { return ['natur']; } if (in_array($item_id, [ 'moss_block', 'moss_carpet', 'big_dripleaf', 'small_dripleaf', 'lily_pad', 'bee_nest', 'honeycomb', 'honeycomb_block', 'snow', 'snowball', 'powder_snow' ])) { return ['natur']; } if (strpos($item_id, 'glass') !== false && strpos($item_id, '_pane') === false) { return ['dekorationsblocke']; } if (strpos($item_id, 'glass_pane') !== false || strpos($item_id, 'stained_glass_pane') !== false) { return ['dekorationsblocke']; } if (strpos($item_id, '_door') !== false || strpos($item_id, '_trapdoor') !== false || strpos($item_id, '_fence_gate') !== false) { return ['dekorationsblocke']; } if ((strpos($item_id, '_fence') !== false && strpos($item_id, '_fence_gate') === false) || (strpos($item_id, '_wall') !== false && !in_array($item_id, ['wall_banner', 'wall_sign', 'wall_torch'])) || $item_id === 'iron_bars') { return ['dekorationsblocke']; } if (strpos($item_id, '_stairs') !== false || strpos($item_id, '_slab') !== false) { return ['dekorationsblocke']; } if (strpos($item_id, '_carpet') !== false) { return ['dekorationsblocke']; } if (in_array($item_id, [ 'torch', 'soul_torch', 'lantern', 'soul_lantern', 'campfire', 'soul_campfire', 'end_rod', 'shroomlight', 'froglight', 'sea_lantern' ]) || strpos($item_id, '_candle') !== false || $item_id === 'candle') { return ['dekorationsblocke']; } if (in_array($item_id, [ 'crafting_table', 'furnace', 'blast_furnace', 'smoker', 'chest', 'trapped_chest', 'ender_chest', 'barrel', 'enchanting_table', 'anvil', 'chipped_anvil', 'damaged_anvil', 'grindstone', 'smithing_table', 'cartography_table', 'fletching_table', 'loom', 'stonecutter', 'brewing_stand', 'cauldron', 'composter', 'lectern', 'bookshelf', 'chiseled_bookshelf', 'bell', 'beacon', 'conduit', 'lodestone', 'respawn_anchor' ]) || strpos($item_id, '_bed') !== false || strpos($item_id, 'shulker_box') !== false) { return ['dekorationsblocke']; } if (strpos($item_id, '_sign') !== false || strpos($item_id, '_hanging_sign') !== false || strpos($item_id, '_banner') !== false || in_array($item_id, ['item_frame', 'glow_item_frame', 'painting', 'armor_stand'])) { return ['dekorationsblocke']; } if (in_array($item_id, [ 'ladder', 'scaffolding', 'chain', 'flower_pot', 'decorated_pot', 'dragon_egg', 'dragon_head', 'note_block', 'jukebox' ]) || strpos($item_id, '_head') !== false || strpos($item_id, '_skull') !== false || strpos($item_id, 'coral') !== false) { return ['dekorationsblocke']; } if (in_array($item_id, [ 'stone', 'cobblestone', 'mossy_cobblestone', 'granite', 'polished_granite', 'diorite', 'polished_diorite', 'andesite', 'polished_andesite', 'calcite', 'tuff', 'smooth_stone' ]) || strpos($item_id, 'stone_brick') !== false) { return ['baublocke']; } if (strpos($item_id, 'deepslate') !== false) { return ['baublocke']; } if (strpos($item_id, 'brick') !== false && $item_id !== 'brick' && $item_id !== 'nether_brick') { return ['baublocke']; } if (strpos($item_id, 'sandstone') !== false) { return ['baublocke']; } if (strpos($item_id, 'quartz_') !== false && strpos($item_id, 'nether_quartz') === false) { return ['baublocke']; } if (strpos($item_id, 'prismarine') !== false || strpos($item_id, 'purpur') !== false) { return ['baublocke']; } if (strpos($item_id, 'concrete') !== false || strpos($item_id, 'terracotta') !== false) { return ['baublocke']; } if (strpos($item_id, '_wool') !== false) { return ['baublocke']; } if (strpos($item_id, '_planks') !== false || (strpos($item_id, '_log') !== false && strpos($item_id, 'stripped') === false) || (strpos($item_id, '_wood') !== false && strpos($item_id, 'stripped') === false)) { return ['baublocke']; } if (strpos($item_id, 'stripped_') !== false) { return ['baublocke']; } if (in_array($item_id, ['bamboo_block', 'bamboo_mosaic', 'crimson_stem', 'warped_stem', 'crimson_hyphae', 'warped_hyphae'])) { return ['baublocke']; } if (strpos($item_id, 'copper_') !== false && strpos($item_id, '_ingot') === false && strpos($item_id, '_nugget') === false && $item_id !== 'copper_door' && $item_id !== 'copper_trapdoor') { return ['baublocke']; } if (strpos($item_id, '_block') !== false) { return ['baublocke']; } if (in_array($item_id, [ 'netherrack', 'soul_sand', 'soul_soil', 'basalt', 'polished_basalt', 'smooth_basalt', 'end_stone', 'obsidian', 'crying_obsidian' ]) || strpos($item_id, 'blackstone') !== false || strpos($item_id, 'end_stone_brick') !== false) { return ['baublocke']; } if (in_array($item_id, [ 'glowstone', 'sponge', 'wet_sponge', 'ice', 'packed_ice', 'blue_ice', 'magma_block', 'slime_block', 'honey_block', 'hay_block', 'dried_kelp_block', 'mud', 'packed_mud', 'mud_bricks', 'dripstone_block', 'amethyst_block', 'budding_amethyst' ])) { return ['baublocke']; } return ['baublocke']; } } // =========================================================== // DATABASE HELPER // =========================================================== class WIS_DB { /** * Gibt die Bild-URL eines Items zurück. * Priorität: custom_image_url > automatisch generierte URL aus item_id. */ public static function get_item_image($item) { if (!empty($item->custom_image_url)) { return esc_url($item->custom_image_url); } $img_base = get_option('wis_image_base_url', ''); $img_name = str_replace(':', '_', $item->item_id) . '.png'; return $img_base . $img_name; } public static function get_items($args = []) { global $wpdb; $table = $wpdb->prefix . 'wis_items'; $where_parts = ["1=1"]; if (isset($args['status'])) { $where_parts[] = $wpdb->prepare("status = %s", $args['status']); } if (isset($args['category_slug']) && !empty($args['category_slug'])) { $search_pattern = '%"' . $args['category_slug'] . '"%'; $where_parts[] = $wpdb->prepare("categories LIKE %s", $search_pattern); } if (isset($args['ids']) && is_array($args['ids']) && !empty($args['ids'])) { $ids = array_map('intval', $args['ids']); $placeholders = implode(',', array_fill(0, count($ids), '%d')); $where_parts[] = sprintf("id IN ($placeholders)", ...$ids); } if (isset($args['search']) && !empty($args['search'])) { $search_like = '%' . $wpdb->esc_like($args['search']) . '%'; $where_parts[] = $wpdb->prepare("(name LIKE %s OR item_id LIKE %s)", $search_like, $search_like); } $where = implode(" AND ", $where_parts); $limit = isset($args['limit']) ? "LIMIT " . intval($args['limit']) : ""; $orderby = isset($args['orderby']) ? "ORDER BY " . sanitize_text_field($args['orderby']) : "ORDER BY name ASC"; return $wpdb->get_results("SELECT * FROM $table WHERE $where $orderby $limit"); } public static function count_items($args = []) { global $wpdb; $table = $wpdb->prefix . 'wis_items'; $where_parts = ["1=1"]; if (isset($args['status'])) { $where_parts[] = $wpdb->prepare("status = %s", $args['status']); } if (isset($args['category_slug']) && !empty($args['category_slug'])) { $search_pattern = '%"' . $args['category_slug'] . '"%'; $where_parts[] = $wpdb->prepare("categories LIKE %s", $search_pattern); } if (isset($args['search']) && !empty($args['search'])) { $search_like = '%' . $wpdb->esc_like($args['search']) . '%'; $where_parts[] = $wpdb->prepare("(name LIKE %s OR item_id LIKE %s)", $search_like, $search_like); } $where = implode(" AND ", $where_parts); return (int) $wpdb->get_var("SELECT COUNT(*) FROM $table WHERE $where"); } public static function get_item($id) { global $wpdb; return $wpdb->get_row($wpdb->prepare( "SELECT * FROM {$wpdb->prefix}wis_items WHERE id = %d", $id )); } public static function get_item_by_item_id($item_id) { global $wpdb; return $wpdb->get_row($wpdb->prepare( "SELECT * FROM {$wpdb->prefix}wis_items WHERE item_id = %s", $item_id )); } public static function insert_item($data) { global $wpdb; if (empty($data['categories']) || $data['categories'] === '[]') { $auto_cats = WIS_Item_Categorizer::auto_categorize($data['item_id']); $data['categories'] = json_encode($auto_cats); } return $wpdb->insert($wpdb->prefix . 'wis_items', $data); } public static function update_item($id, $data) { global $wpdb; return $wpdb->update($wpdb->prefix . 'wis_items', $data, ['id' => $id]); } public static function delete_item($id) { global $wpdb; return $wpdb->delete($wpdb->prefix . 'wis_items', ['id' => $id]); } public static function get_servers() { global $wpdb; return $wpdb->get_results("SELECT * FROM {$wpdb->prefix}wis_servers ORDER BY name ASC"); } public static function insert_server($slug, $name) { global $wpdb; return $wpdb->insert($wpdb->prefix . 'wis_servers', [ 'slug' => sanitize_title($slug), 'name' => sanitize_text_field($name) ]); } public static function delete_server($id) { global $wpdb; return $wpdb->delete($wpdb->prefix . 'wis_servers', ['id' => $id]); } public static function get_categories() { global $wpdb; return $wpdb->get_results("SELECT * FROM {$wpdb->prefix}wis_categories ORDER BY name ASC"); } public static function insert_category($name) { global $wpdb; return $wpdb->insert($wpdb->prefix . 'wis_categories', [ 'name' => sanitize_text_field($name), 'slug' => sanitize_title($name) ]); } public static function delete_category($id) { global $wpdb; return $wpdb->delete($wpdb->prefix . 'wis_categories', ['id' => $id]); } public static function get_coupons() { global $wpdb; return $wpdb->get_results("SELECT * FROM {$wpdb->prefix}wis_coupons ORDER BY created_at DESC"); } public static function get_coupon_by_code($code) { global $wpdb; return $wpdb->get_row($wpdb->prepare( "SELECT * FROM {$wpdb->prefix}wis_coupons WHERE code = %s", strtoupper($code) )); } public static function insert_coupon($data) { global $wpdb; $data['code'] = strtoupper($data['code']); return $wpdb->insert($wpdb->prefix . 'wis_coupons', $data); } public static function update_coupon($id, $data) { global $wpdb; if (isset($data['code'])) { $data['code'] = strtoupper($data['code']); } return $wpdb->update($wpdb->prefix . 'wis_coupons', $data, ['id' => $id]); } public static function delete_coupon($id) { global $wpdb; return $wpdb->delete($wpdb->prefix . 'wis_coupons', ['id' => $id]); } public static function get_orders($limit = 100) { global $wpdb; return $wpdb->get_results($wpdb->prepare( "SELECT * FROM {$wpdb->prefix}wis_orders ORDER BY created_at DESC LIMIT %d", $limit )); } public static function get_order($id) { global $wpdb; return $wpdb->get_row($wpdb->prepare( "SELECT * FROM {$wpdb->prefix}wis_orders WHERE id = %d", $id )); } public static function insert_order($data) { global $wpdb; return $wpdb->insert($wpdb->prefix . 'wis_orders', $data); } public static function update_order_status($id, $status) { global $wpdb; return $wpdb->update( $wpdb->prefix . 'wis_orders', ['status' => $status], ['id' => $id] ); } public static function delete_order($id) { global $wpdb; return $wpdb->delete($wpdb->prefix . 'wis_orders', ['id' => $id]); } } // =========================================================== // ADMIN PAGES // =========================================================== class WIS_Admin { // Wird auf admin_init gefeuert – vor jeder HTML-Ausgabe, damit wp_redirect() funktioniert public static function handle_save_item() { if (!isset($_POST['wis_save_item'])) return; if (!current_user_can('manage_options')) return; check_admin_referer('wis_item_form'); global $wpdb; $item_type = sanitize_text_field($_POST['item_type'] ?? 'minecraft'); if ($item_type === 'fly') { $resolved_item_id = sanitize_text_field($_POST['fly_duration'] ?? 'fly_5min'); } elseif ($item_type === 'rank') { $rank_id = preg_replace('/[^a-z0-9_\-]/', '', strtolower($_POST['rank_id'] ?? 'vip')); $lp_group = preg_replace('/[^a-zA-Z0-9_\-]/', '', $_POST['lp_group'] ?? $rank_id); $default_group = preg_replace('/[^a-zA-Z0-9_\-]/', '', $_POST['default_group'] ?? 'default'); $rank_days = max(0, intval($_POST['rank_days'] ?? 30)); if (empty($lp_group)) $lp_group = $rank_id; if (empty($default_group)) $default_group = 'default'; $resolved_item_id = 'rank_' . $rank_id . '_' . $lp_group . '_' . $default_group . '_' . $rank_days; } elseif ($item_type === 'fly_abo') { $resolved_item_id = 'fly_abo'; } else { $resolved_item_id = sanitize_text_field($_POST['item_id'] ?? ''); } if (empty($resolved_item_id) || empty(trim($_POST['name'] ?? ''))) { // Fehler als transient speichern, damit page_items() ihn anzeigen kann set_transient('wis_save_error_' . get_current_user_id(), '❌ Name und Item-ID sind Pflichtfelder.', 30); wp_redirect(wp_get_referer() ?: admin_url('admin.php?page=wis_items')); exit; } $data = [ 'item_id' => $resolved_item_id, 'name' => sanitize_text_field($_POST['name']), 'description' => sanitize_textarea_field($_POST['description'] ?? ''), 'price' => intval($_POST['price'] ?? 0), 'offer_price' => intval($_POST['offer_price'] ?? 0), 'is_offer' => isset($_POST['is_offer']) ? 1 : 0, 'servers' => isset($_POST['servers']) ? json_encode(array_map('sanitize_text_field', $_POST['servers'])) : '[]', 'categories' => isset($_POST['categories']) ? json_encode(array_map('sanitize_text_field', $_POST['categories'])) : '[]', 'custom_image_url' => esc_url_raw($_POST['custom_image_url'] ?? ''), 'sell_enabled' => isset($_POST['sell_enabled']) ? 1 : 0, 'sell_price_mode' => in_array($_POST['sell_price_mode'] ?? '', ['percent','fixed','minus']) ? $_POST['sell_price_mode'] : 'percent', 'sell_price_value' => max(0, intval($_POST['sell_price_value'] ?? 80)), 'status' => (intval($_POST['price'] ?? 0) > 0 || $item_type === 'fly' || $item_type === 'rank' || $item_type === 'fly_abo') ? 'publish' : 'draft', ]; if (isset($_GET['edit'])) { $result = WIS_DB::update_item(intval($_GET['edit']), $data); if ($result !== false) { wp_redirect(admin_url('admin.php?page=wis_items&edit=' . intval($_GET['edit']) . '&saved=1')); exit; } else { set_transient('wis_save_error_' . get_current_user_id(), '❌ Fehler beim Speichern: ' . $wpdb->last_error, 30); wp_redirect(admin_url('admin.php?page=wis_items&edit=' . intval($_GET['edit']))); exit; } } else { $existing = WIS_DB::get_item_by_item_id($resolved_item_id); if ($existing) { WIS_DB::update_item($existing->id, $data); wp_redirect(admin_url('admin.php?page=wis_items&edit=' . $existing->id . '&saved=1')); exit; } $result = WIS_DB::insert_item($data); if ($result) { $new_id = $wpdb->insert_id; wp_redirect(admin_url('admin.php?page=wis_items&edit=' . $new_id . '&created=1')); exit; } else { $err = $wpdb->last_error ?: 'Unbekannter Fehler.'; set_transient('wis_save_error_' . get_current_user_id(), '❌ Fehler beim Erstellen: ' . $err, 30); wp_redirect(admin_url('admin.php?page=wis_items&add=1')); exit; } } } 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', 'Einstellungen', 'Einstellungen', 'manage_options', 'wis_shop'); add_submenu_page('wis_shop', 'Items', 'Items', 'manage_options', 'wis_items', [self::class, 'page_items']); add_submenu_page('wis_shop', 'Bestellungen', 'Bestellungen', 'manage_options', 'wis_orders', [self::class, 'page_orders']); add_submenu_page('wis_shop', 'Server', 'Server', 'manage_options', 'wis_servers', [self::class, 'page_servers']); add_submenu_page('wis_shop', 'Kategorien', 'Kategorien', 'manage_options', 'wis_categories', [self::class, 'page_categories']); add_submenu_page('wis_shop', 'Gutscheine', 'Gutscheine', 'manage_options', 'wis_coupons', [self::class, 'page_coupons']); add_submenu_page('wis_shop', 'JSON Export/Import', 'JSON Tools', 'manage_options', 'wis_json', [self::class, 'page_json']); add_submenu_page('wis_shop', 'Shop Reset', '🔄 Reset', 'manage_options', 'wis_reset', [self::class, 'page_reset']); add_submenu_page('wis_shop', 'Top Spender', 'Top Spender', 'manage_options', 'wis_top_spenders', [self::class, 'page_top_spenders']); } public static function page_overview() { if (isset($_POST['wis_save_settings']) && check_admin_referer('wis_settings')) { update_option('wis_currency_name', sanitize_text_field($_POST['wis_currency_name'])); update_option('wis_image_base_url', esc_url_raw($_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'); 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'])); update_option('wis_offline_queue_enabled', isset($_POST['wis_offline_queue_enabled']) ? '1' : '0'); echo '

✅ Einstellungen gespeichert!

'; } if (isset($_POST['wis_regen_key']) && check_admin_referer('wis_regen_key')) { update_option('wis_api_key', bin2hex(random_bytes(24))); echo '

🔑 Neuer API-Key generiert! Bitte in der config.yml des Spigot-Plugins aktualisieren.

'; } global $wpdb; $stats = [ 'items' => $wpdb->get_var("SELECT COUNT(*) FROM {$wpdb->prefix}wis_items"), 'orders' => $wpdb->get_var("SELECT COUNT(*) FROM {$wpdb->prefix}wis_orders"), 'servers' => $wpdb->get_var("SELECT COUNT(*) FROM {$wpdb->prefix}wis_servers"), 'coupons' => $wpdb->get_var("SELECT COUNT(*) FROM {$wpdb->prefix}wis_coupons") ]; ?>

🛒 WP Ingame Shop Pro v

📊 Statistiken

Items im Shop:
Bestellungen:
Server:
Gutscheine:

⚙️ Einstellungen

Muss mit / enden! Z.B.: https://git.viper.ipv64.net/.../images/

Gutscheine bei Angeboten
Daily Deal
Offline-Queue

Erfordert Spigot-Plugin v6.3+ mit Offline-Queue-Unterstützung

🔑 Spigot API-Key

Dieser Key muss in der config.yml des Spigot-Plugins als api-key eingetragen werden.

Geschützte Endpunkte: pending_orders, execute_order, complete_order, cancel_order, pending_offline

= 0) { $data['price'] = $new_price; } } break; case 'offer': $is_offer = isset($_POST['item_offers'][$id]) ? 1 : 0; $data['is_offer'] = $is_offer; if (isset($_POST['item_offer_prices'][$id])) { $offer_price = intval($_POST['item_offer_prices'][$id]); $data['offer_price'] = $is_offer ? $offer_price : 0; } else if (!$is_offer) { $data['offer_price'] = 0; } break; case 'status': $new_status = sanitize_text_field($_POST['bulk_status']); if (in_array($new_status, ['publish', 'draft'])) { $data['status'] = $new_status; } break; case 'server': if (isset($_POST['item_servers'][$id])) { $data['servers'] = json_encode($_POST['item_servers'][$id]); } else { $data['servers'] = '[]'; } break; case 'category': if (isset($_POST['item_cat_assignments'][$id])) { $data['categories'] = json_encode($_POST['item_cat_assignments'][$id]); } break; case 'sell': $data['sell_enabled'] = isset($_POST['item_sell_enabled'][$id]) ? 1 : 0; $mode = sanitize_text_field($_POST['item_sell_mode'][$id] ?? 'percent'); if (!in_array($mode, ['percent', 'fixed', 'minus'])) $mode = 'percent'; $data['sell_price_mode'] = $mode; $data['sell_price_value'] = max(0, intval($_POST['item_sell_value'][$id] ?? 80)); break; } if (!empty($data)) { WIS_DB::update_item($id, $data); $count++; } } echo '

✅ ' . $count . ' Items erfolgreich aktualisiert!

'; } } if (isset($_POST['wis_bulk_apply']) && !empty($_POST['wis_bulk_action']) && isset($_POST['item_ids'])) { $action = sanitize_text_field($_POST['wis_bulk_action']); $selected_ids = array_map('intval', $_POST['item_ids']); $all_servers = WIS_DB::get_servers(); $all_categories = WIS_DB::get_categories(); $currency = get_option('wis_currency_name', 'Coins'); $items_to_edit = WIS_DB::get_items(['ids' => $selected_ids]); ?>

📦 Mehrfachbearbeitung

Aktion: für Items.

Preis pro Item festlegen

Item Name Aktueller Preis Neuer Preis ()
name); ?>
item_id); ?>
price); ?>

Angebot pro Item festlegen

Item Name Normalpreis Als Angebot? Angebotspreis
name); ?>
item_id); ?>
price); ?> is_offer, 1); ?> onchange="toggleOfferPrice(this, id; ?>)"> is_offer): ?>disabled>

Server pro Item zuweisen

Keine Server vorhanden.

servers, true) ?: []; ?>
Item Name Server (Mehrfachauswahl möglich)
name); ?>
item_id); ?>
slug, $current_servers) ? 'checked' : ''; ?>

Kategorien pro Item zuweisen

Fehler beim Laden der Items.

categories, true) ?: []; ?>
Item Name Kategorie (Mehrfachauswahl mit STRG+Klick)
name); ?>
item_id); ?>

Ankauf pro Item konfigurieren

sell_enabled); $s_mode = $item->sell_price_mode ?? 'percent'; $s_value = $item->sell_price_value ?? 80; ?>
Item Name VK-Preis Ankauf? Modus Wert Ankaufspreis
name); ?>
item_id); ?>
price); ?> onchange="wisUpdateSellRow(id; ?>, price); ?>)"> price * $s_value / 100, 2); elseif ($s_mode === 'minus') $sp = max(0, $item->price - $s_value); else $sp = max(0, $s_value); echo number_format($sp, 2) . ' ' . esc_html($currency); } else { echo ''; } ?>

Abbrechen

✅ Item gelöscht!

'; } elseif ($_GET['action'] === 'toggle_status') { $item = WIS_DB::get_item($id); $new_status = ($item->status === 'publish') ? 'draft' : 'publish'; WIS_DB::update_item($id, ['status' => $new_status]); echo '

✅ Status geändert!

'; } } // Fehlermeldung aus handle_save_item() anzeigen (falls Validierung fehlschlug) $save_error = get_transient('wis_save_error_' . get_current_user_id()); if ($save_error) { delete_transient('wis_save_error_' . get_current_user_id()); echo '

' . esc_html($save_error) . '

'; } if (isset($_GET['created'])) { echo '

✅ Item erfolgreich erstellt!

'; } if (isset($_GET['saved'])) { echo '

✅ Item gespeichert!

'; } if (isset($_GET['edit']) || isset($_GET['add'])) { $item = isset($_GET['edit']) ? WIS_DB::get_item(intval($_GET['edit'])) : null; $servers = WIS_DB::get_servers(); $categories = WIS_DB::get_categories(); $currency = get_option('wis_currency_name', 'Coins'); $item_servers = $item ? json_decode($item->servers, true) : []; $item_cats = $item ? json_decode($item->categories, true) : []; if (!$item && isset($_GET['add'])) { $item_cats = []; } ?>

← Zurück zur Liste
sell_enabled)) ? '' : 'style="display:none"'; ?>>
item_id, $fly_ids); $is_rank = $item && preg_match('/^rank_([^_]+(?:_[^_]+)*)_([a-zA-Z0-9_\-]+)_([a-zA-Z0-9_\-]+)_(\d+)$/', $item->item_id, $rm); // Format: rank_{rank_id}_{lp_group}_{default_group}_{days} // Fallback für altes Format rank_{rank_id}_{days} $is_rank_old = !$is_rank && $item && preg_match('/^rank_(.+)_(\d+)$/', $item->item_id, $rm_old); $is_fly_abo = $item && ($item->item_id === 'fly_abo' || preg_match('/^fly_abo_\d+$/', $item->item_id)); $cur_abo_days = 0; // nicht mehr relevant $cur_rank_id = $is_rank ? $rm[1] : ($is_rank_old ? $rm_old[1] : 'vip'); $cur_lp_group = $is_rank ? $rm[2] : $cur_rank_id; $cur_default_group = $is_rank ? $rm[3] : 'default'; $cur_rank_days = $is_rank ? intval($rm[4]) : ($is_rank_old ? intval($rm_old[2]) : 30); $cur_label = $item ? esc_attr($item->name) : ''; $detected_type = $is_fly ? 'fly' : (($is_rank || $is_rank_old) ? 'rank' : ($is_fly_abo ? 'fly_abo' : 'minecraft')); ?>

Z.B.: minecraft:diamond (Kategorien werden automatisch zugewiesen)

Eigene Bild-URL – überschreibt die automatische Minecraft-Item-ID-URL.
Ideal für Fly-Gutscheine, VIP-Pakete etc. Leer lassen = Standard.

Vorschau Vorschau

Optional: Wenn gesetzt, wird der normale Preis durchgestrichen

 

Beispiele: 80 % → 80 % des VK-Preises  |  Modus «minus 10» → VK-Preis − 10 |  Fixer Preis 15 → immer 15 

Markierungen
Server

Keine Server vorhanden. Server erstellen

Kategorien

Keine Kategorien vorhanden.

Bei neuem Item werden Kategorien automatisch basierend auf der Item-ID gesetzt

prefix . 'wis_items'; // Admin-Liste: alle Items anzeigen (publish + draft), Query ohne doppeltes prepare() $where_parts = []; $where_vals = []; if (!empty($current_category)) { $where_parts[] = "categories LIKE %s"; $where_vals[] = '%"' . $wpdb->esc_like($current_category) . '"%'; } if (!empty($search_query)) { $like = '%' . $wpdb->esc_like($search_query) . '%'; $where_parts[] = "(name LIKE %s OR item_id LIKE %s)"; $where_vals[] = $like; $where_vals[] = $like; } $where_sql = !empty($where_parts) ? 'WHERE ' . implode(' AND ', $where_parts) : ''; $where_vals[] = $per_page; $where_vals[] = $offset; $items = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM $table_items $where_sql ORDER BY name ASC LIMIT %d OFFSET %d", $where_vals ) ); $currency = get_option('wis_currency_name', 'Coins'); $base_url = admin_url('admin.php?page=wis_items'); if (!empty($current_category)) $base_url .= '&wis_category=' . urlencode($current_category); if (!empty($search_query)) $base_url .= '&wis_search=' . urlencode($search_query); ?>

Items Neu erstellen

✕ Zurücksetzen
Item(s) gefunden – Suche:

1): ?> Einträge 1): ?> « Seite von » Einträge

ID Name Item ID Preis Status Aktionen
id); ?> name); ?> item_id, $rm2)) { $rd = intval($rm2[4]); echo '👑 LP: ' . esc_html($rm2[2]) . ' — ' . ($rd === 0 ? 'dauerhaft' : esc_html($rd) . ' Tage') . ''; } elseif (preg_match('/^rank_(.+)_(\d+)$/', $item->item_id, $rm2)) { $rd = intval($rm2[2]); echo '👑 ' . esc_html($rm2[1]) . ' — ' . ($rd === 0 ? 'dauerhaft' : esc_html($rd) . ' Tage') . ''; } elseif ($item->item_id === 'fly_abo' || preg_match('/^fly_abo_/', $item->item_id)) { echo '✈ Fly-Abo — monatlich'; } else { echo '' . esc_html($item->item_id) . ''; } ?> price); ?> status === 'publish'): ?> ✅ Aktiv 📝 Entwurf Bearbeiten Löschen
1): ?>
Einträge 1): ?> « Seite von »

✅ Server erstellt!

'; } if (isset($_GET['action'], $_GET['id']) && $_GET['action'] === 'delete') { check_admin_referer('wis_server_action', '_wpnonce'); WIS_DB::delete_server(intval($_GET['id'])); echo '

✅ Server gelöscht!

'; } $servers = WIS_DB::get_servers(); ?>

Server

Neuen Server erstellen

Kleinbuchstaben ohne Leerzeichen

Vorhandene Server

ID Name Slug Aktionen
Noch keine Server vorhanden.
id); ?> name); ?> slug); ?> Löschen

✅ Kategorie erstellt!

'; } if (isset($_GET['action'], $_GET['id']) && $_GET['action'] === 'delete') { check_admin_referer('wis_category_action', '_wpnonce'); WIS_DB::delete_category(intval($_GET['id'])); echo '

✅ Kategorie gelöscht!

'; } $categories = WIS_DB::get_categories(); ?>

Kategorien

Neue Kategorie erstellen

Vorhandene Kategorien

ID Name Slug Aktionen
Noch keine Kategorien vorhanden.
id); ?> name); ?> slug); ?> Löschen
sanitize_text_field($_POST['code']), 'value' => intval($_POST['value']), 'type' => sanitize_text_field($_POST['type']), 'usage_limit' => intval($_POST['usage_limit']), 'expiry' => !empty($_POST['expiry']) ? sanitize_text_field($_POST['expiry']) : null ]; if (isset($_GET['edit'])) { unset($data['used_count']); WIS_DB::update_coupon(intval($_GET['edit']), $data); echo '

✅ Gutschein gespeichert!

'; } else { $data['used_count'] = 0; WIS_DB::insert_coupon($data); echo '

✅ Gutschein erstellt!

'; } } if (isset($_GET['action'], $_GET['id']) && $_GET['action'] === 'delete') { check_admin_referer('wis_coupon_action', '_wpnonce'); WIS_DB::delete_coupon(intval($_GET['id'])); echo '

✅ Gutschein gelöscht!

'; } if (isset($_GET['add']) || isset($_GET['edit'])) { global $wpdb; $coupon = isset($_GET['edit']) ? $wpdb->get_row($wpdb->prepare("SELECT * FROM {$wpdb->prefix}wis_coupons WHERE id = %d", intval($_GET['edit']))) : null; $currency = get_option('wis_currency_name', 'Coins'); ?>

← Zurück zur Liste

Z.B.: SUMMER20

Bei Festbetrag: Betrag in . Bei Prozent: Zahl ohne %

Optional

Gutscheine Neu erstellen

Code Rabatt Genutzt Gültig bis Aktionen
Noch keine Gutscheine vorhanden.
code); ?> type === 'percent'): ?> value); ?>% value); ?> used_count); ?> / usage_limit); ?> expiry ? esc_html(date('d.m.Y', strtotime($coupon->expiry))) : '∞'; ?> Bearbeiten Löschen

✅ Bestellung gelöscht!

'; } if (isset($_GET['action'], $_GET['id']) && $_GET['action'] === 'complete') { check_admin_referer('wis_order_action', '_wpnonce'); WIS_DB::update_order_status(intval($_GET['id']), 'completed'); echo '

✅ Status geändert!

'; } if (isset($_GET['view'])) { $order = WIS_DB::get_order(intval($_GET['view'])); if (!$order) { echo '

Bestellung nicht gefunden.

'; return; } $currency = get_option('wis_currency_name', 'Coins'); $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' ]; ?>

Bestellung #id; ?> - Details

← Zurück
IDid); ?>
Datumcreated_at))); ?>
Spielerplayer_name); ?>
Serverserver); ?>
Zusammenfassungitem_title); ?>
Preisprice); ?>
Statusstatus] ?? $order->status; ?>
Details (JSON) response); ?>

Bestellungen

'Warte', 'claimed' => '📡 Abgeholt', 'processing' => 'Geben...', 'completed' => 'Fertig', 'cancelled' => 'Abgebrochen', 'failed' => 'Fehler' ]; $status_colors = [ 'pending' => '#ffc107', 'claimed' => '#17a2b8', 'processing' => '#0073aa', 'completed' => 'green', 'cancelled' => 'red', 'failed' => 'red' ]; ?>
Datum Spieler Inhalt Preis Status Aktionen
Noch keine Bestellungen vorhanden.
created_at))); ?> player_name); ?> item_title, 0, 50)) . (strlen($order->item_title) > 50 ? '...' : ''); ?> price); ?> status] ?? $order->status; ?> 👁️ Details 🗑️ Löschen
'publish']); $img_base = get_option('wis_image_base_url', ''); $json_data = ['items' => []]; foreach ($items as $item) { $json_data['items'][] = [ 'id' => $item->item_id, 'name' => $item->name, 'description' => $item->description, 'price' => intval($item->price), 'image' => WIS_DB::get_item_image($item) ]; } $json_output = json_encode($json_data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); echo '

📦 JSON Export

'; echo '

✅ JSON erfolgreich generiert!

'; echo ''; echo '

'; echo ''; echo '

📤 Nächste Schritte:

'; echo '
    '; echo '
  1. Lade die JSON-Datei herunter
  2. '; echo '
  3. Gehe zu deinem Gitea Repository
  4. '; echo '
  5. Lade die items.json hoch unter: https://git.viper.ipv64.net/M_Viper/WP-Ingame-Shop-Pro
  6. '; echo '
  7. Klicke dann auf den Quick-Import Button unten!
  8. '; echo '
'; echo '
'; return; } $default_url = 'https://git.viper.ipv64.net/M_Viper/WP-Ingame-Shop-Pro/raw/branch/main/items.json'; ?>

📦 JSON Export/Import

📤 JSON Export

Generiere eine JSON-Datei mit allen deinen Items für Gitea.

⚡ Quick-Import von Gitea

Importiert direkt von deinem Gitea Repository!

📥 JSON Import (Manuelle URL)

Importiere Items aus einer beliebigen JSON-URL.


✅ Shop wurde komplett zurückgesetzt!

'; } ?>

🔄 Shop Reset

⚠️ WARNUNG

Diese Aktion löscht ALLE Daten:

Diese Aktion kann NICHT rückgängig gemacht werden!

get_results(" SELECT player_name, SUM(price) as total_spent, COUNT(*) as order_count FROM {$wpdb->prefix}wis_orders WHERE status = 'completed' GROUP BY player_name ORDER BY total_spent DESC LIMIT 50 "); ?>

🏆 Top Spender

Spieler mit den höchsten Gesamtausgaben

Rang Spieler Ausgegeben Bestellungen
Noch keine Statistiken vorhanden.
# player_name); ?> total_spent)); ?> order_count); ?>
'POST', 'callback' => [self::class, 'import_json'], 'permission_callback' => '__return_true', ]); register_rest_route('wis/v1', '/order', [ 'methods' => 'POST', 'callback' => [self::class, 'create_order'], 'permission_callback' => '__return_true', ]); register_rest_route('wis/v1', '/validate_coupon', [ 'methods' => 'POST', 'callback' => [self::class, 'validate_coupon'], 'permission_callback' => '__return_true', ]); register_rest_route('wis/v1', '/shop_items', [ 'methods' => 'GET', 'callback' => [self::class, 'get_shop_items'], 'permission_callback' => '__return_true', ]); register_rest_route('wis/v1', '/pending_orders', [ 'methods' => 'GET', 'callback' => [self::class, 'get_pending_orders'], 'permission_callback' => [WIS_Activator::class, 'spigot_permission'], ]); register_rest_route('wis/v1', '/execute_order', [ 'methods' => 'POST', 'callback' => [self::class, 'execute_order'], 'permission_callback' => [WIS_Activator::class, 'spigot_permission'], ]); register_rest_route('wis/v1', '/complete_order', [ 'methods' => 'POST', 'callback' => [self::class, 'complete_order'], 'permission_callback' => [WIS_Activator::class, 'spigot_permission'], ]); register_rest_route('wis/v1', '/cancel_order', [ 'methods' => 'POST', 'callback' => [self::class, 'cancel_order'], 'permission_callback' => [WIS_Activator::class, 'spigot_permission'], ]); register_rest_route('wis/v1', '/pending_offline', [ 'methods' => 'GET', 'callback' => [self::class, 'get_pending_offline'], 'permission_callback' => [WIS_Activator::class, 'spigot_permission'], ]); register_rest_route('wis/v1', '/orders_history', [ 'methods' => 'GET', 'callback' => [self::class, 'get_orders_history'], 'permission_callback' => [WIS_Activator::class, 'spigot_permission'], ]); // Ankauf-Endpunkte (ab v6.5) register_rest_route('wis/v1', '/sell_items', [ 'methods' => 'GET', 'callback' => [self::class, 'get_sell_items'], 'permission_callback' => [WIS_Activator::class, 'spigot_permission'], ]); register_rest_route('wis/v1', '/sell_item', [ 'methods' => 'POST', 'callback' => [self::class, 'process_sell'], 'permission_callback' => [WIS_Activator::class, 'spigot_permission'], ]); } // ========================================================= // ANKAUF – Endpunkte (ab v6.5) // ========================================================= /** * GET /wis/v1/sell_items?server= * Liefert alle Items die auf dem angegebenen Server ankaufbar sind. */ public static function get_sell_items($request) { global $wpdb; $server = sanitize_text_field($request->get_param('server') ?? ''); $currency = get_option('wis_currency_name', 'Coins'); $table = $wpdb->prefix . 'wis_items'; $items = $wpdb->get_results( "SELECT id, item_id, name, price, sell_price_mode, sell_price_value FROM $table WHERE status = 'publish' AND sell_enabled = 1" ); $result = []; foreach ($items as $item) { // Ankaufspreis berechnen $sell_price = self::calc_sell_price( (int) $item->price, $item->sell_price_mode, (int) $item->sell_price_value ); if ($sell_price <= 0) continue; // Server-Filter if (!empty($server)) { $servers = $wpdb->get_var($wpdb->prepare( "SELECT servers FROM $table WHERE id = %d", $item->id )); $srv_list = json_decode($servers, true) ?: []; if (!empty($srv_list) && !in_array(strtolower($server), array_map('strtolower', $srv_list))) { continue; } } $result[] = [ 'item_id' => $item->item_id, 'name' => $item->name, 'buy_price' => (int) $item->price, 'sell_price' => $sell_price, ]; } return new WP_REST_Response([ 'items' => $result, 'currency' => $currency, ]); } /** * POST /wis/v1/sell_item * Body: { "player": "Steve", "server": "survival", "item_id": "minecraft:diamond", "quantity": 5 } * Antwortet mit dem Betrag der gutgeschrieben werden soll. */ public static function process_sell($request) { global $wpdb; $player = sanitize_text_field($request->get_param('player') ?? ''); $server = sanitize_text_field($request->get_param('server') ?? ''); $item_id = sanitize_text_field($request->get_param('item_id') ?? ''); $quantity = max(1, intval($request->get_param('quantity') ?? 1)); if (!$player || !$item_id) { return new WP_REST_Response(['success' => false, 'message' => 'Fehlende Parameter'], 400); } $table = $wpdb->prefix . 'wis_items'; // Item suchen (case-insensitive, mit/ohne minecraft: prefix) $clean_id = strtolower(str_replace('minecraft:', '', $item_id)); $row = $wpdb->get_row($wpdb->prepare( "SELECT id, item_id, name, price, sell_enabled, sell_price_mode, sell_price_value, servers FROM $table WHERE sell_enabled = 1 AND (LOWER(item_id) = %s OR LOWER(REPLACE(item_id,'minecraft:','')) = %s) LIMIT 1", strtolower($item_id), $clean_id )); if (!$row) { return new WP_REST_Response(['success' => false, 'message' => 'Item nicht ankaufbar'], 404); } $sell_price = self::calc_sell_price( (int) $row->price, $row->sell_price_mode, (int) $row->sell_price_value ); if ($sell_price <= 0) { return new WP_REST_Response(['success' => false, 'message' => 'Ankaufspreis ist 0'], 422); } $total = round($sell_price * $quantity, 2); // Sell-Log schreiben $wpdb->insert($wpdb->prefix . 'wis_sell_log', [ 'player_name' => $player, 'server' => $server, 'item_id' => $row->item_id, 'item_name' => $row->name, 'quantity' => $quantity, 'price_per_item' => $sell_price, 'total_paid' => $total, ]); return new WP_REST_Response([ 'success' => true, 'item_id' => $row->item_id, 'item_name' => $row->name, 'quantity' => $quantity, 'price_per_item' => $sell_price, 'total' => $total, ]); } /** * Berechnet den Ankaufspreis aus Verkaufspreis + Modus. * mode = "percent" → value ist ein Prozentwert des VK-Preises (z.B. 80 = 80 %) * mode = "fixed" → value ist ein absoluter Festpreis * mode = "minus" → value ist ein absoluter Abzug vom VK-Preis */ private static function calc_sell_price(int $buy_price, string $mode, int $value): float { switch ($mode) { case 'fixed': return max(0, $value); case 'minus': return max(0, $buy_price - $value); case 'percent': default: return max(0, round($buy_price * $value / 100, 2)); } } public static function get_shop_items($request) { $page = max(1, intval($request->get_param('page') ?? 1)); $per_page = 24; $category = sanitize_text_field($request->get_param('category') ?? ''); $search = sanitize_text_field($request->get_param('search') ?? ''); global $wpdb; $table = $wpdb->prefix . 'wis_items'; $where_parts = ["status = 'publish'"]; if (!empty($category)) { $search_pattern = '%"' . $category . '"%'; $where_parts[] = $wpdb->prepare("categories LIKE %s", $search_pattern); } if (!empty($search)) { $search_like = '%' . $wpdb->esc_like($search) . '%'; $where_parts[] = $wpdb->prepare("(name LIKE %s OR item_id LIKE %s)", $search_like, $search_like); } $where_sql = implode(" AND ", $where_parts); $total = (int) $wpdb->get_var("SELECT COUNT(*) FROM $table WHERE $where_sql"); $offset = ($page - 1) * $per_page; $items = $wpdb->get_results($wpdb->prepare( "SELECT * FROM $table WHERE $where_sql ORDER BY name ASC LIMIT %d OFFSET %d", $per_page, $offset )); $img_base = get_option('wis_image_base_url', ''); $currency = get_option('wis_currency_name', 'Coins'); $result = []; foreach ($items as $item) { $result[] = [ 'id' => $item->id, 'item_id' => $item->item_id, 'name' => $item->name, 'description' => $item->description, 'price' => intval($item->price), 'offer_price' => intval($item->offer_price), 'is_offer' => (bool) $item->is_offer, 'is_daily_deal' => (bool) $item->is_daily_deal, 'servers' => json_decode($item->servers, true) ?: [], 'categories' => json_decode($item->categories, true) ?: [], 'image' => WIS_DB::get_item_image($item), 'has_custom_image' => !empty($item->custom_image_url), ]; } // Fly-Items nach Dauer sortieren: 5min→15min→30min→1h→2h→3h $fly_order = ['fly_5min'=>1,'fly_15min'=>2,'fly_30min'=>3,'fly_1h'=>4,'fly_2h'=>5,'fly_3h'=>6]; usort($result, function($a, $b) use ($fly_order) { $ap = isset($fly_order[$a['item_id']]) ? $fly_order[$a['item_id']] : 999; $bp = isset($fly_order[$b['item_id']]) ? $fly_order[$b['item_id']] : 999; if ($ap !== 999 || $bp !== 999) return $ap - $bp; return strcmp($a['name'], $b['name']); }); return new WP_REST_Response([ 'items' => $result, 'total' => $total, 'page' => $page, 'per_page' => $per_page, 'total_pages' => max(1, (int) ceil($total / $per_page)), 'currency' => $currency, ]); } 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' => []]); } $table = $wpdb->prefix . 'wis_orders'; $results = $wpdb->get_results($wpdb->prepare( "SELECT * FROM $table WHERE player_name = %s AND status = 'pending' ORDER BY created_at ASC LIMIT 5", $player )); if (!empty($results)) { $ids = implode(',', array_map(fn($o) => intval($o->id), $results)); $wpdb->query("UPDATE $table SET status = 'claimed' WHERE id IN ($ids) AND status = 'pending'"); } return new WP_REST_Response(['orders' => $results]); } public static function get_pending_offline($request) { if (get_option('wis_offline_queue_enabled', '0') !== '1') { return new WP_REST_Response(['orders' => [], 'message' => 'Offline-Queue deaktiviert']); } global $wpdb; $player = sanitize_text_field($request->get_param('player')); if (!$player) { return new WP_REST_Response(['orders' => []]); } $table = $wpdb->prefix . 'wis_orders'; $results = $wpdb->get_results($wpdb->prepare( "SELECT * FROM $table WHERE player_name = %s AND status = 'pending' ORDER BY created_at ASC LIMIT 10", $player )); if (!empty($results)) { $ids = implode(',', array_map(fn($o) => intval($o->id), $results)); $wpdb->query("UPDATE $table SET status = 'claimed' WHERE id IN ($ids) AND status = 'pending'"); } return new WP_REST_Response(['orders' => $results]); } public static function get_orders_history($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 id, item_title, price, status, created_at FROM {$wpdb->prefix}wis_orders WHERE player_name = %s ORDER BY created_at DESC 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); $wpdb->query($wpdb->prepare( "UPDATE {$wpdb->prefix}wis_orders SET status = 'processing' WHERE id = %d AND status IN ('pending','claimed')", $id )); return new WP_REST_Response(['success' => true]); } 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); $wpdb->update( $wpdb->prefix . 'wis_orders', ['status' => 'cancelled'], ['id' => $id] ); return new WP_REST_Response(['success' => true]); } public static function import_json($request) { $data = $request->get_json_params(); $url = esc_url_raw($data['url'] ?? ''); if (!$url) { return new WP_REST_Response(['success' => false, 'message' => 'Keine URL angegeben'], 400); } $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); $json = json_decode($body, true); if (!isset($json['items']) || !is_array($json['items'])) { return new WP_REST_Response(['success' => false, 'message' => 'Ungültiges JSON Format'], 400); } $imported = 0; $skipped = 0; foreach ($json['items'] as $item) { $item_id = sanitize_text_field($item['id'] ?? ''); $name = sanitize_text_field($item['name'] ?? 'Unbekannt'); if (empty($item_id)) continue; $exists = WIS_DB::get_item_by_item_id($item_id); if ($exists) { $skipped++; continue; } WIS_DB::insert_item([ 'item_id' => $item_id, 'name' => $name, 'description' => sanitize_textarea_field($item['description'] ?? ''), 'price' => intval($item['price'] ?? 0), 'status' => intval($item['price'] ?? 0) > 0 ? 'publish' : 'draft', 'servers' => '[]', 'categories' => '[]' ]); $imported++; } return new WP_REST_Response(['success' => true, 'imported' => $imported, 'skipped' => $skipped]); } public static function create_order($request) { global $wpdb; $data = $request->get_json_params(); $player = sanitize_text_field($data['player'] ?? ''); $cart = $data['cart'] ?? []; $server = sanitize_text_field($data['server'] ?? ''); $coupon_code = isset($data['coupon_code']) ? sanitize_text_field(strtoupper($data['coupon_code'])) : ''; if (!$player || empty($cart) || !$server) { return new WP_REST_Response(['success' => false, 'message' => 'Fehlende Daten'], 400); } $exclude_offers = get_option('wis_coupon_exclude_offers', '0'); $currency = get_option('wis_currency_name', 'Coins'); $valid_cart = []; $total_normal = 0; $total_offer = 0; foreach ($cart as $item_data) { $item = WIS_DB::get_item(intval($item_data['id'] ?? 0)); if (!$item || $item->status !== 'publish') continue; $qty = intval($item_data['quantity'] ?? 1); if ($qty <= 0) continue; $servers = json_decode($item->servers, true); if (!in_array($server, $servers ?: [])) continue; $price = $item->offer_price > 0 ? $item->offer_price : $item->price; $valid_cart[] = [ 'id' => $item->item_id, 'title' => $item->name, 'price' => $price, 'qty' => $qty, 'is_offer' => $item->is_offer ]; $item_total = $price * $qty; if ($item->is_offer && $exclude_offers === '1') { $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'], 400); } $coupon_discount = 0; $coupon_msg = ''; $coupon_applied = false; if (!empty($coupon_code)) { $coupon = WIS_DB::get_coupon_by_code($coupon_code); if ($coupon) { if ($coupon->expiry && date('Y-m-d') > $coupon->expiry) { $coupon_msg = 'Gutschein abgelaufen'; } elseif ($coupon->used_count >= $coupon->usage_limit) { $coupon_msg = 'Gutschein bereits aufgebraucht'; } else { if ($exclude_offers === '1' && $total_normal <= 0) { $coupon_msg = 'Gutschein gilt nicht für Angebote'; } else { if ($coupon->type === 'percent') { $coupon_discount = floor($total_normal * ($coupon->value / 100)); } else { $coupon_discount = $coupon->value; } WIS_DB::update_coupon($coupon->id, ['used_count' => $coupon->used_count + 1]); $coupon_applied = true; $coupon_msg = "Gutschein eingelöst: -{$coupon_discount} {$currency}"; } } } else { $coupon_msg = 'Ungültiger Code'; } } $final_price = max(0, $total_normal - $coupon_discount) + $total_offer; // Fly-Dauern (item_id → Sekunden) // Fly-Dauern und lesbares Label (wird dem Spieler auf dem Code angezeigt) $fly_durations = [ 'fly_5min' => 5 * 60, 'fly_15min' => 15 * 60, 'fly_30min' => 30 * 60, 'fly_1h' => 1 * 3600, 'fly_2h' => 2 * 3600, 'fly_3h' => 3 * 3600, ]; $fly_labels = [ 'fly_5min' => '5 Minuten Fly', 'fly_15min' => '15 Minuten Fly', 'fly_30min' => '30 Minuten Fly', 'fly_1h' => '1 Stunde Fly', 'fly_2h' => '2 Stunden Fly', 'fly_3h' => '3 Stunden Fly', ]; $items_payload = []; $commands_payload = []; $title_parts = []; foreach ($valid_cart as $item) { $item_id = $item['id']; // Das ist der item_id-String aus wis_items if (isset($fly_durations[$item_id])) { // Fly-Gutschein: pro Stueck einen eigenen Code-Eintrag (qty > 1 = mehrere Codes) $base_sec = $fly_durations[$item_id]; $base_label = $fly_labels[$item_id]; for ($q = 0; $q < intval($item['qty']); $q++) { $commands_payload[] = [ 'type' => 'fly', 'duration_sec' => $base_sec, 'label' => $base_label, ]; } $title_parts[] = $item['qty'] . 'x ' . $item['title']; } elseif (preg_match('/^rank_([^_]+)_([a-zA-Z0-9_\-]+)_([a-zA-Z0-9_\-]+)_(\d+)$/', $item_id, $rm)) { // Neues Format: rank_{rank_id}_{lp_group}_{default_group}_{days} $rank_id = $rm[1]; $lp_group = $rm[2]; $default_group = $rm[3]; $rank_days = intval($rm[4]); for ($q = 0; $q < intval($item['qty']); $q++) { $commands_payload[] = [ 'type' => 'rank', 'rank_id' => $rank_id, 'lp_group' => $lp_group, 'default_group' => $default_group, 'label' => $item['title'], 'days' => $rank_days, ]; } $title_parts[] = $item['qty'] . 'x ' . $item['title']; } elseif (preg_match('/^rank_(.+)_(\d+)$/', $item_id, $rm)) { // Altes Format (Fallback): rank_{rank_id}_{days} $rank_id = $rm[1]; $rank_days = intval($rm[2]); for ($q = 0; $q < intval($item['qty']); $q++) { $commands_payload[] = [ 'type' => 'rank', 'rank_id' => $rank_id, 'lp_group'=> $rank_id, 'default_group' => 'default', 'label' => $item['title'], 'days' => $rank_days, ]; } $title_parts[] = $item['qty'] . 'x ' . $item['title']; } elseif (preg_match('/^fly_abo$/', $item_id) || preg_match('/^fly_abo_\d*$/', $item_id)) { // Fly-Abo: monatlich abonniert, Preis = Monatsbeitrag // qty > 1 wird ignoriert (nur 1 Abo pro Spieler möglich, Mehrfachkauf = keine Wirkung) $commands_payload[] = [ 'type' => 'fly_abo', 'label' => $item['title'], ]; $title_parts[] = $item['title']; } else { // Normales Item $items_payload[] = [ 'id' => $item_id, 'amount' => $item['qty'], ]; $title_parts[] = $item['qty'] . 'x ' . $item['title']; } } $title = "Warenkorb: " . implode(', ', $title_parts); if (strlen($title) > 240) $title = substr($title, 0, 237) . '...'; $payload = [ 'items' => $items_payload, 'commands' => $commands_payload, 'coupon' => $coupon_applied ? ['code' => $coupon_code, 'discount' => $coupon_discount] : [], ]; WIS_DB::insert_order([ 'player_name' => $player, 'server' => $server, 'item_id' => 'cart', 'item_title' => $title, 'price' => $final_price, 'quantity' => count($valid_cart), 'status' => 'pending', 'response' => json_encode($payload) ]); // Fly-Abo Abonnements registrieren / verlängern foreach ($commands_payload as $cmd) { if (($cmd['type'] ?? '') !== 'fly_abo') continue; $abo_days = intval($cmd['days'] ?? 30); $abo_label = sanitize_text_field($cmd['label'] ?? 'Fly-Abo'); $abo_price = 0; // Preis aus dem Warenkorb ermitteln (erstes passendes fly_abo Item) foreach ($valid_cart as $ci) { if (preg_match('/^fly_abo_\d+$/', $ci['id'])) { $abo_price = intval($ci['price'] ?? 0); break; } } global $wpdb; WIS_Activator::create_abo_subs_table(); $subs_table = $wpdb->prefix . 'wis_fly_abo_subs'; $existing = $wpdb->get_row($wpdb->prepare( "SELECT * FROM {$subs_table} WHERE player_name = %s AND server = %s", $player, $server )); if ($existing && $existing->status === 'active') { // Bestehendes aktives Abo verlängern (kumulativ) $wpdb->query($wpdb->prepare( "UPDATE {$subs_table} SET expires_at = DATE_ADD(IF(expires_at > NOW(), expires_at, NOW()), INTERVAL %d DAY), cancelled = 0, cancelled_at = NULL, label = %s, price = %d WHERE id = %d", $abo_days, $abo_label, $abo_price, $existing->id )); } else { // Neues Abo anlegen $wpdb->replace($subs_table, [ 'player_name' => $player, 'server' => $server, 'label' => $abo_label, 'price' => $abo_price, 'status' => 'active', 'cancelled' => 0, 'expires_at' => date('Y-m-d H:i:s', strtotime("+{$abo_days} days")), ]); } } $msg = '✅ Bestellung erfolgreich!'; if ($coupon_msg) $msg .= ' (' . $coupon_msg . ')'; return new WP_REST_Response(['success' => true, 'message' => $msg]); } public static function validate_coupon($request) { $data = $request->get_json_params(); $code = sanitize_text_field(strtoupper($data['code'] ?? '')); $cart = $data['cart'] ?? []; if (!$code) { return new WP_REST_Response(['success' => false, 'message' => 'Kein Code']); } $coupon = WIS_DB::get_coupon_by_code($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' => 'Bereits aufgebraucht']); } $exclude_offers = get_option('wis_coupon_exclude_offers', '0'); if ($exclude_offers === '1' && !empty($cart)) { $has_normal = false; foreach ($cart as $item_data) { $item = WIS_DB::get_item(intval($item_data['id'] ?? 0)); if ($item && !$item->is_offer) { $has_normal = true; break; } } if (!$has_normal) { return new WP_REST_Response(['success' => false, 'message' => 'Gutschein gilt nicht für Angebote']); } } $currency = get_option('wis_currency_name', 'Coins'); $msg = $coupon->type === 'percent' ? "Gutschein gültig (-{$coupon->value}%)" : "Gutschein gültig (-{$coupon->value} {$currency})"; return new WP_REST_Response([ 'success' => true, 'type' => $coupon->type, 'value' => $coupon->value, 'message' => $msg ]); } } // =========================================================== // SHORTCODE - FRONTEND SHOP // =========================================================== class WIS_Shortcode { public static function register() { add_shortcode('ingame_shop_form', [self::class, 'render']); } public static function render() { $servers = WIS_DB::get_servers(); $categories = WIS_DB::get_categories(); $currency = get_option('wis_currency_name', 'Coins'); $header_text = get_option('wis_header_text', ''); $exclude_offers = get_option('wis_coupon_exclude_offers', '0'); $first_category = !empty($categories) ? $categories[0]->slug : ''; ob_start(); ?>

🛒 Ingame Shop


Lade Items…

🛒 Dein Warenkorb

Dein Warenkorb ist leer
'Zeigt Daily Deal oder Angebot an'] ); } public function widget($args, $instance) { echo $args['before_widget']; if (!empty($instance['title'])) { echo $args['before_title'] . apply_filters('widget_title', $instance['title']) . $args['after_title']; } global $wpdb; $currency = get_option('wis_currency_name', 'Coins'); $img_base = get_option('wis_image_base_url', ''); $item = $wpdb->get_row("SELECT * FROM {$wpdb->prefix}wis_items WHERE is_daily_deal = 1 AND status = 'publish' LIMIT 1"); if (!$item) { $item = $wpdb->get_row("SELECT * FROM {$wpdb->prefix}wis_items WHERE is_offer = 1 AND status = 'publish' ORDER BY updated_at DESC LIMIT 1"); } if ($item) { $price = $item->offer_price > 0 ? $item->offer_price : $item->price; $show_old = $item->offer_price > 0 && $item->offer_price != $item->price; $img_url = WIS_DB::get_item_image($item); $target_url = !empty($instance['shop_url']) ? $instance['shop_url'] : home_url(); ?>
is_daily_deal): ?>
🎁 DAILY DEAL
is_offer): ?>
🔥 ANGEBOT
<?php echo esc_attr($item->name); ?>

name); ?>

price); ?>
🛒

Kein Angebot verfügbar.

'; } echo $args['after_widget']; } public function form($instance) { $title = !empty($instance['title']) ? $instance['title'] : '🔥 Angebot des Tages'; $btn_text = !empty($instance['btn_text']) ? $instance['btn_text'] : 'Zum Shop'; $shop_url = !empty($instance['shop_url']) ? $instance['shop_url'] : ''; ?>