load(); $today = gmdate('Y-m-d'); $data['summary']['currency'] = $currency ?: ($data['summary']['currency'] ?? 'USD'); // Recent live events ring buffer if (!isset($data['events']) || !is_array($data['events'])) { $data['events'] = []; } $reason = !empty($meta['reason']) ? (string) $meta['reason'] : ($isRescued ? 'Sent by server' : 'Delivered by browser'); array_unshift($data['events'], [ 'event' => $eventName, 'id' => '#' . ltrim((string) $orderId, '#'), 'amount' => (float) $amount, 'currency' => $currency, 'rescued' => (bool) $isRescued, 'reason' => $reason, 'timestamp' => gmdate('Y-m-d H:i:s'), ]); if (count($data['events']) > 20) { $data['events'] = array_slice($data['events'], 0, 20); } $this->ensureBucket($data, $today); $data['daily'][$today]['orders']++; $data['daily'][$today]['revenue'] += (float) $amount; if ($isRescued) { $data['daily'][$today]['rescued']++; $data['daily'][$today]['rescued_revenue'] += (float) $amount; } $this->pruneOldEntries($data); $this->save($data); } /** * Records a refund deduction against net revenue. * * @param float $amount */ public function recordRefund(float $amount): void { $data = $this->load(); $today = gmdate('Y-m-d'); $this->ensureBucket($data, $today); $data['daily'][$today]['refunds'] = (float) ($data['daily'][$today]['refunds'] ?? 0.0) + $amount; $this->pruneOldEntries($data); $this->save($data); } /** * Returns the recent live server-side dispatched events. * * @return array */ public function getRecentEvents(): array { $data = $this->load(); if (!empty($data['events']) && is_array($data['events'])) { return $data['events']; } return []; } /** * Totals for the last 30 days. * * @return array */ public function getSummary(): array { $data = $this->load(); $cutoff = gmdate('Y-m-d', strtotime('-' . (self::PERIOD_DAYS - 1) . ' days')); $summary = [ 'total_orders' => 0, 'rescued_orders' => 0, 'total_revenue' => 0.0, 'rescued_revenue' => 0.0, 'refund_revenue' => 0.0, ]; foreach ((array) ($data['daily'] ?? []) as $date => $bucket) { if ((string) $date < $cutoff) { continue; } $summary['total_orders'] += (int) ($bucket['orders'] ?? 0); $summary['rescued_orders'] += (int) ($bucket['rescued'] ?? 0); $summary['total_revenue'] += (float) ($bucket['revenue'] ?? 0.0); $summary['rescued_revenue'] += (float) ($bucket['rescued_revenue'] ?? 0.0); $summary['refund_revenue'] += (float) ($bucket['refunds'] ?? 0.0); } $total = $summary['total_orders']; $summary['rescue_rate_percent'] = ($total > 0) ? round(($summary['rescued_orders'] / $total) * 100, 1) : 0.0; $summary['net_revenue'] = max(0.0, $summary['total_revenue'] - $summary['refund_revenue']); $summary['currency'] = (string) ($data['summary']['currency'] ?? 'USD'); $summary['period_days'] = self::PERIOD_DAYS; return $summary; } /** * Creates today's daily bucket if missing. * * @param array &$data * @param string $date */ private function ensureBucket(array &$data, string $date): void { if (!isset($data['daily'][$date])) { $data['daily'][$date] = [ 'orders' => 0, 'rescued' => 0, 'revenue' => 0.0, 'rescued_revenue' => 0.0, 'refunds' => 0.0, ]; } } /** * Returns daily statistics buckets. * * @return array */ public function getDailyStats(): array { $data = $this->load(); return $data['daily'] ?? []; } /** * Prunes daily buckets older than 30 days. * * @param array &$data * @param int $daysToKeep */ public function pruneOldEntries(array &$data, int $daysToKeep = 30): void { if (empty($data['daily']) || !is_array($data['daily'])) { return; } $cutoff = strtotime("-{$daysToKeep} days"); foreach (array_keys($data['daily']) as $dateStr) { $ts = strtotime($dateStr); if ($ts !== false && $ts < $cutoff) { unset($data['daily'][$dateStr]); } } } /** * Resets telemetry metrics. */ public function reset(): void { delete_option(self::OPTION_KEY); } /** * Loads telemetry data with defaults. * * @return array */ private function load(): array { $data = get_option(self::OPTION_KEY, []); if (!is_array($data) || empty($data)) { $data = [ 'summary' => [ 'currency' => 'USD', ], 'daily' => [], ]; } return $data; } /** * Persists telemetry data. * * @param array $data */ private function save(array $data): void { update_option(self::OPTION_KEY, $data, false); } }