@extends('layouts.admin-modern') @section('title', 'Pilotage des paiements') @php $money = fn ($value) => number_format((float) ($value ?? 0), 2, ',', ' ') . ' EUR'; $statusBadge = function (?string $status): string { return match (strtolower((string) $status)) { 'paid', 'completed', 'released', 'validated', 'returned' => 'bg-green-100 text-green-800 border border-green-200', 'pending', 'processing', 'held', 'pending_capture', 'under_review' => 'bg-amber-100 text-amber-800 border border-amber-200', 'refunded', 'partially_refunded', 'partial', 'partial_refund' => 'bg-sky-100 text-sky-800 border border-sky-200', 'failed', 'cancelled', 'rejected', 'retained' => 'bg-red-100 text-red-800 border border-red-200', 'disputed', 'open' => 'bg-violet-100 text-violet-800 border border-violet-200', default => 'bg-slate-100 text-slate-800 border border-slate-200', }; }; $statusLabel = function (?string $status): string { return match (strtolower((string) $status)) { 'paid' => 'Paye', 'completed' => 'Complete', 'released' => 'Libere', 'pending' => 'En attente', 'processing' => 'Traitement', 'held' => 'Bloque', 'pending_capture' => 'Autorise', 'refunded' => 'Rembourse', 'partially_refunded' => 'Rembourse partiel', 'partial_refund' => 'Rembourse partiel', 'partial' => 'Partiel', 'failed' => 'Echec', 'cancelled' => 'Annule', 'rejected' => 'Refuse', 'disputed' => 'Litige', 'open' => 'Ouvert', 'under_review' => 'En revue', 'returned' => 'Restitue', 'retained' => 'Retenue', default => ucfirst(str_replace('_', ' ', (string) $status)), }; }; $transactionSource = function ($transaction): string { if ($transaction->food_order_id) return 'Food'; if ($transaction->equipment_rental_id) return 'Location'; if ($transaction->booking_id) return 'Service'; return 'Paiement'; }; $transactionReference = function ($transaction): string { if ($transaction->booking?->booking_number) return $transaction->booking->booking_number; if ($transaction->equipmentRentalRequest?->request_number) return $transaction->equipmentRentalRequest->request_number; if ($transaction->foodOrder?->order_number) return $transaction->foodOrder->order_number; return 'TX-' . $transaction->id; }; $transactionClient = function ($transaction): string { return $transaction->foodOrder?->client?->name ?? $transaction->equipmentRentalRequest?->client?->user?->name ?? $transaction->booking?->client?->user?->name ?? $transaction->user?->name ?? 'N/A'; }; $transactionPrestataire = function ($transaction): string { return $transaction->foodOrder?->prestataire?->user?->name ?? $transaction->equipmentRentalRequest?->prestataire?->user?->name ?? $transaction->booking?->prestataire?->user?->name ?? 'N/A'; }; $escrowValue = function ($row, array $keys, $default = null) { foreach ($keys as $key) { if (isset($row->{$key}) && $row->{$key} !== null && $row->{$key} !== '') { return $row->{$key}; } } return $default; }; $escrowTypeLabel = function (?string $type): string { $type = (string) $type; return match (true) { str_contains($type, 'Booking') => 'Service', str_contains($type, 'EquipmentRental') => 'Location', str_contains($type, 'UrgentSale') => 'Vente urgente', default => class_basename($type ?: 'Escrow'), }; }; $fieldClass = 'rounded-xl border border-slate-300 bg-white px-4 py-2.5 text-sm text-slate-900'; $textAreaClass = 'rounded-xl border border-slate-300 bg-white px-3 py-2 text-sm text-slate-900'; $primaryButtonClass = 'inline-flex items-center justify-center rounded-xl bg-slate-900 px-4 py-2.5 text-sm font-semibold text-white'; $secondaryButtonClass = 'inline-flex items-center justify-center rounded-xl bg-slate-100 px-4 py-2.5 text-sm font-semibold text-slate-700'; $successButtonClass = 'inline-flex items-center justify-center rounded-xl bg-green-700 px-4 py-2.5 text-sm font-semibold text-white'; $warningButtonClass = 'inline-flex items-center justify-center rounded-xl bg-amber-600 px-4 py-2.5 text-sm font-semibold text-white'; $dangerButtonClass = 'inline-flex items-center justify-center rounded-xl bg-red-700 px-4 py-2.5 text-sm font-semibold text-white'; $infoButtonClass = 'inline-flex items-center justify-center rounded-xl bg-indigo-700 px-4 py-2.5 text-sm font-semibold text-white'; $kpiCards = [ [ 'label' => 'Volume brut', 'value' => $money($stats['gross_volume'] ?? 0), 'value_class' => 'text-slate-900', 'hint' => 'Tous flux confondus', 'icon' => 'fa-wallet', 'icon_wrap_class' => 'bg-slate-100', 'icon_class' => 'text-slate-700 text-xl', ], [ 'label' => 'Paiements valides', 'value' => number_format((int) ($stats['validated_count'] ?? 0)), 'value_class' => 'text-green-700', 'hint' => $money($stats['deposit_amount'] ?? 0) . " d'acomptes traces", 'icon' => 'fa-check-circle', 'icon_wrap_class' => 'bg-green-100', 'icon_class' => 'text-green-700 text-xl', ], [ 'label' => 'En attente / bloques', 'value' => number_format((int) ($stats['pending_count'] ?? 0)), 'value_class' => 'text-amber-700', 'hint' => $money($stats['escrow_held_amount'] ?? 0) . ' sous escrow', 'icon' => 'fa-hourglass-half', 'icon_wrap_class' => 'bg-amber-100', 'icon_class' => 'text-amber-700 text-xl', ], [ 'label' => 'Demandes remboursement', 'value' => number_format((int) ($stats['refund_pending_count'] ?? 0)), 'value_class' => 'text-rose-700', 'hint' => $money($stats['refund_pending_amount'] ?? 0) . ' a traiter', 'icon' => 'fa-rotate-left', 'icon_wrap_class' => 'bg-rose-100', 'icon_class' => 'text-rose-700 text-xl', ], [ 'label' => 'Escrows en cours', 'value' => number_format((int) ($stats['escrow_held_count'] ?? 0)), 'value_class' => 'text-indigo-700', 'hint' => $money($stats['escrow_held_amount'] ?? 0), 'icon' => 'fa-shield-halved', 'icon_wrap_class' => 'bg-indigo-100', 'icon_class' => 'text-indigo-700 text-xl', ], [ 'label' => 'Cautions ouvertes', 'value' => number_format((int) ($stats['caution_open_count'] ?? 0)), 'value_class' => 'text-cyan-700', 'hint' => $money($stats['caution_open_amount'] ?? 0), 'icon' => 'fa-box-open', 'icon_wrap_class' => 'bg-cyan-100', 'icon_class' => 'text-cyan-700 text-xl', ], [ 'label' => 'Food a capturer', 'value' => number_format((int) ($stats['food_pending_capture_count'] ?? 0)), 'value_class' => 'text-fuchsia-700', 'hint' => $money($stats['food_pending_capture_amount'] ?? 0), 'icon' => 'fa-bag-shopping', 'icon_wrap_class' => 'bg-fuchsia-100', 'icon_class' => 'text-fuchsia-700 text-xl', ], [ 'label' => 'Paiements en echec', 'value' => number_format((int) ($stats['failed_count'] ?? 0)), 'value_class' => 'text-red-700', 'hint' => $money($stats['refunded_amount'] ?? 0) . ' deja remboursee', 'icon' => 'fa-triangle-exclamation', 'icon_wrap_class' => 'bg-red-100', 'icon_class' => 'text-red-700 text-xl', ], ]; $quickLinks = [ ['href' => '#actions-urgentes', 'label' => 'Actions urgentes', 'class' => 'bg-slate-900 text-white'], ['href' => '#transactions', 'label' => 'Transactions', 'class' => 'bg-slate-100 text-slate-700'], ['href' => '#escrows', 'label' => 'Escrows', 'class' => 'bg-slate-100 text-slate-700'], ['href' => '#remboursements', 'label' => 'Remboursements', 'class' => 'bg-slate-100 text-slate-700'], ]; @endphp @section('content')
Supervision des paiements, acomptes, cautions, escrows, remboursements et actions critiques.
Console de controle
Consulte les flux modernes et legacy, puis traite les dossiers urgents sans parcourir plusieurs pages.
{{ $kpiCard['label'] }}
{{ $kpiCard['value'] }}
{{ $kpiCard['hint'] }}
Filtres
Recherche par reference, statut, source, mode de paiement ou periode.
Priorites
Les dossiers ci-dessous concentrent les actions manuelles importantes.
Remboursements
Approuve ou rejette les demandes avec une note admin.
{{ $money($refund->amount) }} sur {{ $refund->transaction?->id ? 'TX-' . $refund->transaction->id : 'transaction non reliee' }}
Motif
{{ $refund->reason ?: 'Aucun motif fourni.' }}
Email client: {{ $refund->user?->email ?? 'N/A' }}
Food
Capture, annulation d'autorisation ou remboursement selon l'etat du dossier.
{{ $money($foodOrder->amount_held ?? $foodOrder->total) }}
Ouvrir les actions
Litiges escrow
Analyse le litige et tranche avec une resolution tracee.
{{ $escrowTypeLabel($dispute->escrowable_type) }}
Description
{{ $dispute->description }}
Cautions materiel
Traite l'etat du materiel et la retenue eventuelle sur caution.
{{ $money($rentalRequest->security_deposit) }}
Ouvrir les actions
Flux modernes
Historique detaille des paiements modernes avec action de remboursement.
| Reference | Flux | Parties | Montant | Statut | Date | Details et actions |
|---|---|---|---|---|---|---|
|
{{ $transactionReference($transaction) }}
TX-{{ $transaction->id }}
|
{{ $transactionSource($transaction) }} |
{{ $transactionClient($transaction) }}
Prestataire: {{ $transactionPrestataire($transaction) }}
|
{{ $money($transaction->amount) }} | {{ $statusLabel($transaction->status) }} | {{ optional($transaction->created_at)->format('d/m/Y H:i') }} |
Voir / rembourserType: {{ $transaction->type ?? 'payment' }}
Methode: {{ $transaction->payment_method ?? 'N/A' }} / {{ $transaction->provider ?? 'N/A' }}
Stripe: {{ $transaction->stripe_payment_intent_id ?? 'N/A' }}
Transaction externe: {{ $transaction->transaction_id ?? 'N/A' }}
@if(in_array((string) $transaction->status, ['paid', 'completed', 'held', 'released', 'partially_refunded'], true))
@endif
|
Source historique
Anciennes lignes lues depuis la table legacy `transactions`.
| Reference | Client | Prestataire | Montant | Type | Statut | Date |
|---|---|---|---|---|---|---|
| {{ $legacyTransaction->reference ?? ('LG-' . $legacyTransaction->id) }} | {{ $legacyTransaction->user_name ?? $legacyTransaction->user_email ?? 'N/A' }} | {{ $legacyTransaction->prestataire_name ?? 'N/A' }} | {{ $money($legacyTransaction->amount) }} | {{ $legacyTransaction->type ?? 'payment' }} | {{ $statusLabel($legacyTransaction->status) }} | {{ \Carbon\Carbon::parse($legacyTransaction->created_at)->format('d/m/Y H:i') }} |
Escrows
Suivi des montants bloques, des etapes et des actions de liberation ou remboursement.
Historique
Journal de suivi des demandes et statuts de remboursement.
| ID | Client | Transaction | Montant | Statut | Motif |
|---|---|---|---|---|---|
| RF-{{ $refund->id }} | {{ $refund->user?->name ?? 'N/A' }} | {{ $refund->transaction?->transaction_id ?? ('TX-' . ($refund->transaction_id ?? 'N/A')) }} | {{ $money($refund->amount) }} | {{ $statusLabel($refund->status) }} | {{ \Illuminate\Support\Str::limit($refund->reason, 80) }} |