From 5325fa1f061949048c1497ac22ce061ef6b4f3a8 Mon Sep 17 00:00:00 2001 From: Valentin Lab Date: Fri, 20 Feb 2026 11:52:03 +0100 Subject: [PATCH] new: send alert email when Paybox payment arrives on cancelled order MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When a Paybox callback confirms payment on an order with status 4 (Annulé), the payment is still recorded but the order status is no longer forced to « Préparation ». Instead, an alert email is sent to ``commande@jardinenvie.com`` warning that a refund is likely needed. New ``AlertePaiementAnnule`` mailable with DB template providing order ref, amount, customer info and payment reference. New method ``OrderMails::sendCancelledOrderPaymentAlert()`` handles the dispatch. --- app/Mail/AlertePaiementAnnule.php | 51 +++++++++++ app/Repositories/Shop/OrderMails.php | 23 +++++ app/Repositories/Shop/Paybox.php | 24 ++++-- ...t_mail_template_alerte_paiement_annule.php | 86 +++++++++++++++++++ 4 files changed, 178 insertions(+), 6 deletions(-) create mode 100644 app/Mail/AlertePaiementAnnule.php create mode 100644 database/migrations/shop/2026_02_20_120000_insert_mail_template_alerte_paiement_annule.php diff --git a/app/Mail/AlertePaiementAnnule.php b/app/Mail/AlertePaiementAnnule.php new file mode 100644 index 00000000..4772be88 --- /dev/null +++ b/app/Mail/AlertePaiementAnnule.php @@ -0,0 +1,51 @@ +numero_commande = $order->ref; + $this->date_commande = $order->created_at->format('d/m/Y H:i'); + $this->montant = number_format($order->total_shipped, 2, ',', ' ').' €'; + $this->client_nom = $order->customer + ? $order->customer->last_name.' '.$order->customer->first_name + : 'Client supprimé'; + $this->client_email = $order->customer->email ?? 'inconnu'; + $this->reference_paiement = $reference; + } + + public function envelope() + { + return new Envelope( + from: new Address('boutique@jardinenvie.com', 'Jardin\'en\'Vie'), + subject: $this->subject, + ); + } +} diff --git a/app/Repositories/Shop/OrderMails.php b/app/Repositories/Shop/OrderMails.php index 869664c4..f87935e8 100644 --- a/app/Repositories/Shop/OrderMails.php +++ b/app/Repositories/Shop/OrderMails.php @@ -3,9 +3,11 @@ namespace App\Repositories\Shop; use App\Mail\Acheminement; +use App\Mail\AlertePaiementAnnule; use App\Mail\ConfirmationCommande; use App\Mail\Preparation; use Illuminate\Support\Facades\Mail; +use Illuminate\Support\Facades\Log; class OrderMails { @@ -37,4 +39,25 @@ class OrderMails return Mail::to($order->customer->email)->send($mail); } + + public static function sendCancelledOrderPaymentAlert($orderId, $reference) + { + $order = Orders::get($orderId, ['customer']); + + try { + Mail::to('commande@jardinenvie.com') + ->send(new AlertePaiementAnnule($order, $reference)); + Log::info('Cancelled order payment alert sent', [ + 'order_id' => $orderId, + 'order_ref' => $order->ref, + 'reference' => $reference, + ]); + } catch (\Throwable $e) { + Log::error('Failed to send cancelled order payment alert', [ + 'order_id' => $orderId, + 'order_ref' => $order->ref, + 'exception' => $e->getMessage(), + ]); + } + } } diff --git a/app/Repositories/Shop/Paybox.php b/app/Repositories/Shop/Paybox.php index 6ca3195f..63b37365 100644 --- a/app/Repositories/Shop/Paybox.php +++ b/app/Repositories/Shop/Paybox.php @@ -104,7 +104,9 @@ class Paybox return true; } - DB::transaction(function () use ($invoice, $order, $reference, $payload, $existingPayment, &$shouldNotify) { + $isCancelled = (int) $order->status === 4; + + DB::transaction(function () use ($invoice, $order, $reference, $payload, $existingPayment, &$shouldNotify, $isCancelled) { $attributes = [ 'payment_type' => 1, 'amount' => $invoice->total_shipped, @@ -134,14 +136,24 @@ class Paybox Invoices::checkPayments($invoice->id); - $paidStatus = Orders::getStatusByName('Préparation'); - if ($paidStatus !== '' && (int) $order->status !== (int) $paidStatus) { - $order->status = $paidStatus; - $order->save(); + if (! $isCancelled) { + $paidStatus = Orders::getStatusByName('Préparation'); + if ($paidStatus !== '' && (int) $order->status !== (int) $paidStatus) { + $order->status = $paidStatus; + $order->save(); + } } }); - if ($shouldNotify) { + if ($isCancelled && $shouldNotify) { + Log::warning('Paybox payment received on cancelled order', [ + 'order_id' => $order->id, + 'order_ref' => $order->ref, + 'invoice_id' => $invoice->id, + 'reference' => $reference, + ]); + OrderMails::sendCancelledOrderPaymentAlert($order->id, $reference); + } elseif ($shouldNotify) { try { OrderMails::sendOrderConfirmed($order->id); } catch (\Throwable $exception) { diff --git a/database/migrations/shop/2026_02_20_120000_insert_mail_template_alerte_paiement_annule.php b/database/migrations/shop/2026_02_20_120000_insert_mail_template_alerte_paiement_annule.php new file mode 100644 index 00000000..aa88577d --- /dev/null +++ b/database/migrations/shop/2026_02_20_120000_insert_mail_template_alerte_paiement_annule.php @@ -0,0 +1,86 @@ +insert([ + 'mailable' => 'App\\Mail\\AlertePaiementAnnule', + 'subject' => json_encode(['fr' => '[URGENT] Paiement reçu sur commande annulée {{numero_commande}}'], JSON_UNESCAPED_UNICODE), + 'html_template' => json_encode(['fr' => $this->getHtmlTemplate()], JSON_UNESCAPED_UNICODE), + 'text_template' => json_encode(['fr' => $this->getTextTemplate()], JSON_UNESCAPED_UNICODE), + 'created_at' => now(), + 'updated_at' => now(), + ]); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + DB::table('mail_templates') + ->where('mailable', 'App\\Mail\\AlertePaiementAnnule') + ->delete(); + } + + private function getHtmlTemplate(): string + { + return '' + .'
' + .'' + .'' + .'' + .'
' + .'' + .'Jardin\'enVie
' + .'' + .'
' + .'' + .'' + .'' + .'' + .'
' + .'⚠ Paiement sur commande annulée
' + .'

Un paiement Paybox a été reçu sur une commande annulée.

' + .'

Un remboursement est probablement nécessaire.

' + .'' + .'' + .'' + .'' + .'' + .'' + .'' + .'
Commande :{{numero_commande}}
Date :{{date_commande}}
Montant :{{montant}}
Client :{{client_nom}}
Email :{{client_email}}
Réf. paiement :{{reference_paiement}}
' + .'

Veuillez procéder au remboursement du client dans les plus brefs délais.

' + .'
' + .'
' + .'' + .'
' + .'Jardin\'enVie Artisan Semencier
429 route des chaux, 26500 Bourg les Valence - Drôme' + .'
' + .'
'; + } + + private function getTextTemplate(): string + { + return "⚠ PAIEMENT SUR COMMANDE ANNULÉE\n\n" + ."Un paiement Paybox a été reçu sur une commande annulée.\n" + ."Un remboursement est probablement nécessaire.\n\n" + ."Commande : {{numero_commande}}\n" + ."Date : {{date_commande}}\n" + ."Montant : {{montant}}\n" + ."Client : {{client_nom}}\n" + ."Email : {{client_email}}\n" + ."Réf. paiement : {{reference_paiement}}\n\n" + ."Veuillez procéder au remboursement du client dans les plus brefs délais.\n\n" + ."Jardin'enVie Artisan Semencier\n" + .'429 route des chaux, 26500 Bourg les Valence - Drôme'; + } +};