From 2c3602f2718c48754d23073280996bfbc294a9ee Mon Sep 17 00:00:00 2001 From: M_Viper Date: Tue, 28 Apr 2026 20:39:14 +0000 Subject: [PATCH] Edit wp-ingame-shop/wp-ingame-shop-pro.php via Git Manager GUI --- wp-ingame-shop/wp-ingame-shop-pro.php | 4400 +------------------------ 1 file changed, 1 insertion(+), 4399 deletions(-) diff --git a/wp-ingame-shop/wp-ingame-shop-pro.php b/wp-ingame-shop/wp-ingame-shop-pro.php index 12914fa..35e82be 100644 --- a/wp-ingame-shop/wp-ingame-shop-pro.php +++ b/wp-ingame-shop/wp-ingame-shop-pro.php @@ -1,4399 +1 @@ - '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 NameAktueller PreisNeuer Preis ()
- name); ?>
- item_id); ?> -
price); ?> - -
-
-

Angebot pro Item festlegen

-
- - - - - - - - - - - - - - - - - - - -
Item NameNormalpreisAls 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 NameServer (Mehrfachauswahl möglich)
- name); ?>
- item_id); ?> -
- slug, $current_servers) ? 'checked' : ''; - ?> - - -
-
- -

Kategorien pro Item zuweisen

-
- -

Fehler beim Laden der Items.

- - - - - - - - - - categories, true) ?: []; - ?> - - - - - - -
Item NameKategorie (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 NameVK-PreisAnkauf?ModusWertAnkaufspreis
- 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 - -
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - IDNameItem IDPreisStatusAktionen
- -
- - 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

- - - - - - - - - - - - - - - - - - - - - - - -
IDNameSlugAktionen
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

- - - - - - - - - - - - - - - - - - - - - - - -
IDNameSlugAktionen
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

- - - - - - - - - - - - - - - - - - - - - - - - - - -
CodeRabattGenutztGültig bisAktionen
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' - ]; - ?> - - - - - - - - - - - -
DatumSpielerInhaltPreisStatusAktionen
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:

-
    -
  • ❌ Alle Items
  • -
  • ❌ Alle Bestellungen
  • -
  • ❌ Alle Gutscheine
  • -
  • ❌ Alle Server
  • -
  • ❌ Alle Kategorien
  • -
-

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

- - - - - - - - - - - - - - - - - - - - - - - - -
RangSpielerAusgegebenBestellungen
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'] : ''; - ?> -

- - -

-

- - -

-

- - -

-