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'], '<')) {
- ?>
-
- $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:
-
-
-
-
-
-
-
-
-
- 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:
-
-
-
-
-
-
-
🔑 Spigot API-Key
-
Dieser Key muss in der config.yml des Spigot-Plugins als api-key eingetragen werden.
-
-
- 📋 Kopieren
-
-
-
- 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.
-
-
-
- ✅ 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 '';
- }
- }
-
- // 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 '';
- }
-
- 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 = [];
- }
-
- ?>
-
- 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);
- ?>
-
-
-
-
-
-
-
- Alle
-
-
-
- name); ?>
-
-
-
-
-
-
-
-
- Massenaktionen wählen
-
- Massenaktionen
- Preis ändern
- Angebot ändern
- Server zuweisen
- Kategorie zuweisen
- Aktivieren/Deaktivieren
- Ankauf konfigurieren
-
-
-
-
-
- 1): ?>
- Einträge
-
-
- Einträge
-
-
-
-
-
-
-
- 1): ?>
-
-
-
-
-
-
- ✅ 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 '';
- }
-
- $servers = WIS_DB::get_servers();
- ?>
-
-
Server
-
-
-
Neuen Server erstellen
-
-
-
-
-
-
-
-
-
-
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 '';
- }
-
- $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 '';
- } else {
- $data['used_count'] = 0;
- WIS_DB::insert_coupon($data);
- echo '';
- }
- }
-
- if (isset($_GET['action'], $_GET['id']) && $_GET['action'] === 'delete') {
- check_admin_referer('wis_coupon_action', '_wpnonce');
- WIS_DB::delete_coupon(intval($_GET['id']));
- echo '';
- }
-
- 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');
- ?>
-
-
-
-
-
-
-
-
- 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 '';
- }
-
- 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
-
-
- ID id); ?>
- Datum created_at))); ?>
- Spieler player_name); ?>
- Server server); ?>
- Zusammenfassung item_title); ?>
- Preis price); ?>
- Status status] ?? $order->status; ?>
-
- Details (JSON)
- response); ?>
-
-
-
-
-
-
Bestellungen
-
-
-
-
- Datum
- Spieler
- Inhalt
- Preis
- Status
- Aktionen
-
-
-
-
- Noch keine Bestellungen vorhanden.
-
-
- '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'
- ];
- ?>
-
- 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 '
'.esc_textarea($json_output).' ';
- echo '
💾 Als items.json herunterladen
';
- echo '';
- echo '
📤 Nächste Schritte: ';
- echo '
';
- echo 'Lade die JSON-Datei herunter ';
- echo 'Gehe zu deinem Gitea Repository ';
- echo 'Lade die items.json hoch unter: https://git.viper.ipv64.net/M_Viper/WP-Ingame-Shop-Pro ';
- echo 'Klicke dann auf den Quick-Import Button unten! ';
- 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!
-
-
-
-
- ⚡ Quick-Import starten
-
-
-
-
-
-
📥 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
-
-
-
-
- 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();
- ?>
-
-
-
-
-
-
-
-
-
- Alle Server
-
- name); ?>
-
-
-
-
- 🛒 Warenkorb
- 0
-
-
-
-
-
-
-
-
- name); ?>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
🛒 Dein Warenkorb
-
-
Dein Warenkorb ist leer
-
-
-
-
🎫 Gutscheincode
-
-
- Einlösen
-
-
-
-
-
- Gesamt:
- 0
-
-
-
- -- Server wählen --
-
- name); ?>
-
-
-
-
-
-
- 💰 Kauf abschließen
- Abbrechen
-
-
-
-
-
-
-
-
- '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
-
-
-
-
-
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'] : '';
- ?>
-
- Titel:
-
-
-
- Button Text:
-
-
-
- Shop URL:
-
-
-