new: send alert email when Paybox payment arrives on cancelled order
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.
This commit is contained in:
51
app/Mail/AlertePaiementAnnule.php
Normal file
51
app/Mail/AlertePaiementAnnule.php
Normal file
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
namespace App\Mail;
|
||||
|
||||
use App\Models\Core\Mail\MailTemplate;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Mail\Mailables\Address;
|
||||
use Illuminate\Mail\Mailables\Envelope;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Spatie\MailTemplates\TemplateMailable;
|
||||
|
||||
class AlertePaiementAnnule extends TemplateMailable
|
||||
{
|
||||
use Queueable, SerializesModels;
|
||||
|
||||
public $subject;
|
||||
|
||||
public $numero_commande;
|
||||
|
||||
public $date_commande;
|
||||
|
||||
public $montant;
|
||||
|
||||
public $client_nom;
|
||||
|
||||
public $client_email;
|
||||
|
||||
public $reference_paiement;
|
||||
|
||||
protected static $templateModelClass = MailTemplate::class;
|
||||
|
||||
public function __construct($order, $reference)
|
||||
{
|
||||
$this->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,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
DB::table('mail_templates')->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 '<table style="min-width: 100%;" width="100%" cellspacing="0" cellpadding="0">'
|
||||
.'<tbody><tr><td>'
|
||||
.'<table style="border-collapse: collapse; background-color: #ffffff; margin: 0 auto; width: 600px;" cellspacing="0" cellpadding="0" border="0" align="center">'
|
||||
.'<tbody><tr>'
|
||||
.'<td style="margin: 0px auto; padding: 32px 0px 24px 4px; vertical-align: top;" align="center">'
|
||||
.'<a href="https://www.jardinenvie.com/" style="text-decoration: none; color: #000000;">'
|
||||
.'<img alt="Jardin\'enVie" src="https://boutique.jardinenvie.com/img/logo.png" style="margin: 0px auto; display: block;" width="300" height="138" border="0" /></a></td>'
|
||||
.'</tr></tbody></table>'
|
||||
.'<table style="border-collapse: collapse; background-color: #ffffff; margin: 0 auto; width: 600px;" cellspacing="0" cellpadding="0" border="0" align="center">'
|
||||
.'<tbody><tr><td style="margin: 0 auto; padding: 0px; vertical-align: top;">'
|
||||
.'<table style="border-collapse: collapse; margin: 0 auto; width: 512px;" cellspacing="0" cellpadding="0" border="0" align="center">'
|
||||
.'<tbody>'
|
||||
.'<tr><td style="font-family: \'Nunito Sans\', Arial, sans-serif; line-height: 40px; color: #c0392b; font-size: 28px; padding: 20px 0px 10px 0px; font-weight: 800; text-align: center;">'
|
||||
.'⚠ Paiement sur commande annulée</td></tr>'
|
||||
.'<tr><td style="font-family: \'Nunito Sans\', Arial, sans-serif; line-height: 28px; color: #000000; font-size: 16px; text-align: center; padding: 10px 0 30px 0;">'
|
||||
.'<p>Un paiement Paybox a été reçu sur une commande <strong>annulée</strong>.</p>'
|
||||
.'<p style="font-size: 20px; font-weight: bold; color: #c0392b; padding: 10px 0;">Un remboursement est probablement nécessaire.</p>'
|
||||
.'<table style="margin: 15px auto; text-align: left; border-collapse: collapse;" cellpadding="8">'
|
||||
.'<tr><td style="font-weight: bold; padding-right: 15px;">Commande :</td><td>{{numero_commande}}</td></tr>'
|
||||
.'<tr><td style="font-weight: bold; padding-right: 15px;">Date :</td><td>{{date_commande}}</td></tr>'
|
||||
.'<tr><td style="font-weight: bold; padding-right: 15px;">Montant :</td><td style="font-size: 18px; font-weight: bold; color: #c0392b;">{{montant}}</td></tr>'
|
||||
.'<tr><td style="font-weight: bold; padding-right: 15px;">Client :</td><td>{{client_nom}}</td></tr>'
|
||||
.'<tr><td style="font-weight: bold; padding-right: 15px;">Email :</td><td>{{client_email}}</td></tr>'
|
||||
.'<tr><td style="font-weight: bold; padding-right: 15px;">Réf. paiement :</td><td>{{reference_paiement}}</td></tr>'
|
||||
.'</table>'
|
||||
.'<p style="padding-top: 15px; color: #666;">Veuillez procéder au remboursement du client dans les plus brefs délais.</p>'
|
||||
.'</td></tr>'
|
||||
.'</tbody></table>'
|
||||
.'</td></tr></tbody></table>'
|
||||
.'<table style="border-collapse: collapse; background-color: #ffffff; margin: 0 auto; width: 600px; text-align: center;" cellspacing="0" cellpadding="0" border="0" align="center">'
|
||||
.'<tbody><tr><td style="font-family: \'Nunito Sans\', Arial, sans-serif; font-size: 12px; font-weight: 600; line-height: 20px; padding: 16px; color: #999999;">'
|
||||
.'Jardin\'enVie Artisan Semencier<br />429 route des chaux, 26500 Bourg les Valence - Drôme'
|
||||
.'</td></tr></tbody></table>'
|
||||
.'</td></tr></tbody></table>';
|
||||
}
|
||||
|
||||
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';
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user