'offen -- Kunde ist beim Anbieter', 'pending' => 'in Pruefung beim Anbieter', 'paid' => 'bezahlt', 'cancelled' => 'abgebrochen / storniert', 'mismatch' => 'BETRAG WEICHT AB -- pruefen!', ]; private const SOURCE = [ 'return' => 'Rueckkehr des Kunden vom Anbieter', 'express' => 'Express-Kauf aus dem Warenkorb', 'webhook' => 'Meldung des Anbieters (Webhook)', 'reconcile' => 'Abgleicher (Cron)', ]; private const ACTION = [ 'create' => 'Vorgang beim Anbieter angelegt', 'start' => 'Kunde zum Anbieter weitergeleitet', 'capture' => 'Einzug (Capture)', 'webhook' => 'Meldung vom Anbieter', 'mail' => 'Bestellbestaetigung an Kunden', 'cancel' => 'Storno', 'mismatch' => 'Betragsabweichung', 'express_create' => 'Express-Kauf gestartet', 'express_get' => 'Express: Freigabe abgeholt', 'express_patch' => 'Express: Betrag angepasst', 'express_reapprove' => 'Express: zweite Freigabe noetig', ]; /** * @return array|null Felder fuers Template oder null ohne Vorgang */ public static function forOrder(\mysqli $db, int $orderId, ?object $base = null): ?array { if ($orderId <= 0) { return null; } $stmt = $db->prepare('SELECT * FROM payment_transactions WHERE order_id=? ORDER BY id DESC LIMIT 1'); if (!$stmt) { return null; } $stmt->bind_param('i', $orderId); $stmt->execute(); $tx = $stmt->get_result()->fetch_assoc(); $stmt->close(); if (!$tx) { return null; } $module = (string) $tx['provider']; $label = $module; if ($base !== null && Registry::has($module)) { $p = Registry::get($module, $base); if ($p) { $label = $p->label(); } } $status = (string) $tx['status']; $detail = (string) ($tx['detail'] ?? ''); if ($status === 'paid') { // markPaid() legt in detail nur die Quelle ab (return/express/webhook/reconcile). $detail = 'bezahlt, bestaetigt ueber: ' . (self::SOURCE[$detail] ?? $detail); } return [ 'provider' => $module, 'provider_label' => $label, 'status' => $status, 'status_label' => self::STATUS[$status] ?? $status, 'reference' => (string) $tx['reference'], 'capture_id' => (string) ($tx['capture_id'] ?? ''), 'expected_amount' => \number_format((float) $tx['expected_amount'], 2, ',', '.') . ' ' . $tx['currency'], 'paid_amount' => $tx['paid_amount'] !== null ? \number_format((float) $tx['paid_amount'], 2, ',', '.') . ' ' . $tx['currency'] : '', 'created_at' => self::de((string) $tx['created_at']), 'paid_at' => self::de((string) ($tx['paid_at'] ?? '')), 'updated_at' => self::de((string) $tx['updated_at']), 'detail' => $detail, 'history' => self::history($orderId), ]; } /** @return list */ private static function history(int $orderId): array { $out = []; foreach (Log::forOrder($orderId, 40) as $row) { $a = (string) $row['action']; $out[] = [ 'zeit' => (string) $row['zeit'], 'aktion' => self::ACTION[$a] ?? $a, 'status' => (string) ($row['status'] ?? ''), 'http' => $row['http_status'] !== null && $row['http_status'] !== '' ? 'HTTP ' . $row['http_status'] : '', 'detail' => (string) ($row['detail'] ?? ''), ]; } return $out; } private static function de(string $dt): string { if ($dt === '' || \str_starts_with($dt, '0000')) { return ''; } $t = \strtotime($dt); return $t ? \date('d.m.Y H:i:s', $t) : $dt; } }