connect_errno ) return null; $conn->set_charset('utf8mb4'); return $conn; } public static function get_all_abos( string $mc_name ): array { global $wpdb; $result = ['fly_used_sec' => 0, 'plot_extra' => 0]; // Fly-Abo aus WordPress DB $fly = $wpdb->get_row( $wpdb->prepare( "SELECT label, price, status, cancelled, cancelled_at, expires_at, renewal_count FROM {$wpdb->prefix}wis_fly_abo_subs WHERE player_name = %s ORDER BY id DESC LIMIT 1", $mc_name ), ARRAY_A ); if ( $fly ) { $expires = new DateTime( $fly['expires_at'] ); $cancelled = (int)$fly['cancelled'] === 1; $is_active = $fly['status'] === 'active' && $expires > new DateTime(); $result['fly_abo'] = [ 'label' => $fly['label'], 'monthly_price' => (int)$fly['price'], 'cancelled' => $cancelled ? 1 : 0, 'cancellation_reason' => $cancelled ? 'user' : null, 'next_billing' => ( new DateTime('first day of next month') )->format('d.m.Y'), 'period_end' => $expires->format('d.m.Y'), 'is_active' => $is_active, ]; } // Item-Abos aus WordPress DB (wis_item_abo_subs) $item_abos = $wpdb->get_results( $wpdb->prepare( "SELECT id, label, item_id, daily_qty, cancelled, DATE_FORMAT(expires_at, '%%d.%%m.%%Y') AS expires_at_fmt FROM {$wpdb->prefix}wis_item_abo_subs WHERE player_name = %s AND status = 'active' AND expires_at > NOW() ORDER BY created_at ASC", $mc_name ), ARRAY_A ); if ( $item_abos ) { foreach ( $item_abos as &$row ) { $row['id'] = (int) $row['id']; $row['daily_qty'] = (int) $row['daily_qty']; $row['cancelled'] = (bool) $row['cancelled']; } unset($row); $result['item_abos'] = $item_abos; } else { $result['item_abos'] = []; } // Spigot MySQL für Fly-Usage + Plot-Daten $conn = self::get_spigot_db(); if ( $conn ) { foreach ([ ["SELECT used_sec FROM wis_fly_abo_usage WHERE player_name = ? AND usage_date = CURDATE()", 'fly_used_sec', 'used_sec'], ] as [$sql, $key, $field]) { $stmt = $conn->prepare($sql); if ($stmt) { $stmt->bind_param('s', $mc_name); $stmt->execute(); $r = $stmt->get_result()->fetch_assoc(); $result[$key] = $r ? (int)$r[$field] : 0; $stmt->close(); } } $stmt = $conn->prepare( "SELECT label, abo_slots, monthly_price, cancelled, cancellation_reason, DATE_FORMAT(next_billing_date,'%d.%m.%Y') AS next_billing, DATE_FORMAT(period_end,'%d.%m.%Y') AS period_end FROM wis_plot_abos WHERE player_name = ? AND period_end >= CURDATE()" ); if ($stmt) { $stmt->bind_param('s', $mc_name); $stmt->execute(); $r = $stmt->get_result()->fetch_assoc(); if ($r) $result['plot_abo'] = $r; $stmt->close(); } $stmt = $conn->prepare("SELECT extra_slots FROM wis_plot_extra_slots WHERE player_name = ?"); if ($stmt) { $stmt->bind_param('s', $mc_name); $stmt->execute(); $r = $stmt->get_result()->fetch_assoc(); $result['plot_extra'] = $r ? (int)$r['extra_slots'] : 0; $stmt->close(); } $conn->close(); } return $result; } public static function cancel_abo( string $mc_name, string $type, int $abo_id = 0 ): bool { global $wpdb; if ( $type === 'fly_abo' ) { $rows = $wpdb->update( $wpdb->prefix . 'wis_fly_abo_subs', ['cancelled' => 1, 'cancelled_at' => current_time('mysql')], ['player_name' => $mc_name, 'status' => 'active', 'cancelled' => 0] ); return $rows > 0; } if ( $type === 'plot_abo' ) { $conn = self::get_spigot_db(); if (!$conn) return false; $stmt = $conn->prepare("UPDATE wis_plot_abos SET cancelled=1, cancellation_reason='user', period_end=LAST_DAY(CURDATE()) WHERE player_name=? AND period_end>=CURDATE() AND cancelled=0"); if (!$stmt) { $conn->close(); return false; } $stmt->bind_param('s', $mc_name); $stmt->execute(); $a = $stmt->affected_rows; $stmt->close(); $conn->close(); return $a > 0; } if ( $type === 'item_abo' ) { if ( $abo_id <= 0 ) return false; // Sicherheitscheck: Abo muss dem Spieler gehören $existing = $wpdb->get_row( $wpdb->prepare( "SELECT id, label FROM {$wpdb->prefix}wis_item_abo_subs WHERE id = %d AND player_name = %s AND status = 'active' AND cancelled = 0", $abo_id, $mc_name ) ); if ( ! $existing ) return false; $rows = $wpdb->update( $wpdb->prefix . 'wis_item_abo_subs', ['cancelled' => 1, 'cancelled_at' => current_time('mysql')], ['id' => $abo_id] ); return $rows > 0; } return false; } public static function format_seconds( int $sec ): string { if ($sec >= 3600) { $h = intdiv($sec,3600); $m = intdiv($sec%3600,60); return $h.'h'.($m>0?' '.$m.'min':''); } if ($sec >= 60) { return intdiv($sec,60).'min'; } return $sec.'s'; } public static function render_tab( string $mc_name, int $profile_id ): string { $abos = self::get_all_abos($mc_name); $fly_abo = $abos['fly_abo'] ?? null; $plot_abo = $abos['plot_abo'] ?? null; $item_abos = $abos['item_abos'] ?? []; $plot_extra = (int)($abos['plot_extra'] ?? 0); $fly_used = (int)($abos['fly_used_sec'] ?? 0); $fly_max = (int) get_option('wis_fly_abo_max_daily_hours', 6) * 3600; $currency = esc_html(get_option('wis_currency_name', '$')); $shop_url = esc_url(get_option('wis_shop_url', home_url('/ingame-shop/'))); $nonce = wp_create_nonce('wbf_nonce'); $ajax_url = admin_url('admin-ajax.php'); $has_any = $fly_abo || $plot_abo || $plot_extra > 0 || !empty($item_abos); ob_start(); ?>