Chore: Updates to Notifications

This commit is contained in:
2026-09-28 23:54:24 +03:00
parent 76b807d672
commit 57d7a716bc
11 changed files with 75 additions and 0 deletions
+17
View File
@@ -5,6 +5,7 @@ namespace Modules\Core\Checkout\Services;
use Lunar\Exceptions\FingerprintMismatchException;
use Lunar\Exceptions\Carts\CartException;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\Event;
use Lunar\Base\Addressable;
use Lunar\DataTypes\ShippingOption;
@@ -431,6 +432,22 @@ class CheckoutService
'terms_accepted' => true,
'terms_accepted_at' => now()->toIso8601String(),
'terms_accepted_policy_version' => $policyVersion,
// Order.locale doesn't exist as a column — saved here so every
// order notification (OrderPaymentResolutionService/
// MarkOrderPlacedOnDeferredPayment fire OrderPlaced synchronously
// in THIS request, but a later one — e.g. a Stripe 3-D Secure
// webhook, or a staff status change from Filament — has no
// request-scoped locale of its own) can render in the locale the
// shopper actually checked out in, not whatever locale (or none)
// happens to be active when the notification is sent. See
// NotificationRegistry::register(), which reads this back.
'locale' => App::getLocale(),
// Lunar copies the discount onto Order.discount_breakdown but not
// the coupon CODE itself — that stays on the cart row, which may
// since have been cleared/reused for a new cart. Copied here so
// the confirmation email always shows what code THIS order was
// actually placed with.
'coupon_code' => $cart->coupon_code,
];
$order->save();
+11
View File
@@ -44,6 +44,17 @@ class NotificationRegistry
if (isset($event->delaySeconds) && $event->delaySeconds > 0) {
$notification->delay($event->delaySeconds);
}
// Every order-notification event carries ->order, whose
// ->meta['locale'] was saved at checkout (Checkout\
// Services\CheckoutService::initiatePayment()) — the
// request that actually sends this may have no locale of
// its own (a payment webhook, a queued job, a staff
// action from Filament), so this is the only reliable
// source. Falls back to the app default for an order
// placed before this existed.
if (isset($event->order) && $locale = $event->order->meta['locale'] ?? null) {
$notification->locale($locale);
}
$notification->notifiable()->notify($notification);
} catch (Throwable $e) {
report($e);
@@ -7,8 +7,23 @@ use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Support\Facades\Notification as NotificationFacade;
use Modules\Core\Notification\BaseNotification;
use Modules\Core\Order\Events\OrderCaptured;
use Modules\Core\Order\Services\OrderStatusFlow;
use Modules\Core\Order\Support\OrderReferenceDisplay;
/**
* "Payment captured" is only a meaningful, distinct update for a bank
* transfer order — placing that order and paying for it are genuinely two
* separate moments (Order::paid stays false at checkout; a shopper is
* told their order is confirmed and awaiting a wire, then separately told
* once staff mark it paid). For every other payment method (card,
* authorize-then-capture, COD), placing the order already means the
* shopper did everything they need to on their end — a card payment is
* captured in the very same request that places the order, so a second
* "payment captured" email would just repeat what OrderPlacedNotification
* already said. See OrderStatusFlow's own docblock for why payment method
* never affects the order's status SEQUENCE, only this kind of business
* decision about which events actually matter to the shopper.
*/
class OrderCapturedNotification extends BaseNotification
{
public function __construct(private readonly OrderCaptured $event) {}
@@ -25,6 +40,10 @@ class OrderCapturedNotification extends BaseNotification
public function via(object $notifiable): array
{
if (! app(OrderStatusFlow::class)->isBankTransfer($this->event->order)) {
return [];
}
return ['mail'];
}
@@ -46,6 +65,7 @@ class OrderCapturedNotification extends BaseNotification
return (new MailMessage)
->subject(__('Payment captured for your order :reference', ['reference' => $reference]))
->view('core::order.notifications.captured', [
'order' => $order,
'reference' => $reference,
'amount' => $this->event->transaction->amount->formatted,
]);
@@ -46,6 +46,7 @@ class OrderCompletedNotification extends BaseNotification
return (new MailMessage)
->subject(__('Your order :reference is complete', ['reference' => $reference]))
->view('core::order.notifications.completed', [
'order' => $order,
'reference' => $reference,
]);
}
@@ -46,6 +46,7 @@ class OrderDeliveredNotification extends BaseNotification
return (new MailMessage)
->subject(__('Your order :reference has been delivered', ['reference' => $reference]))
->view('core::order.notifications.delivered', [
'order' => $order,
'reference' => $reference,
]);
}
@@ -51,6 +51,7 @@ class OrderDispatchedNotification extends BaseNotification
return (new MailMessage)
->subject(__('Your order :reference is on its way', ['reference' => $reference]))
->view('core::order.notifications.dispatched', [
'order' => $order,
'reference' => $reference,
]);
}
@@ -56,6 +56,7 @@ class OrderPickupReadyNotification extends BaseNotification
return (new MailMessage)
->subject(__('Your order :reference is ready for pickup', ['reference' => $reference]))
->view('core::order.notifications.pickup-ready', [
'order' => $order,
'reference' => $reference,
]);
}
@@ -57,6 +57,7 @@ class OrderPlacedNotification extends BaseNotification
return (new MailMessage)
->subject(__('Your order :reference is confirmed', ['reference' => $reference]))
->view('core::order.notifications.placed', [
'order' => $order,
'reference' => $reference,
'total' => $order->total->formatted,
'lines' => $order->lines,
@@ -46,6 +46,7 @@ class OrderRefundedNotification extends BaseNotification
return (new MailMessage)
->subject(__('A refund has been issued for your order :reference', ['reference' => $reference]))
->view('core::order.notifications.refunded', [
'order' => $order,
'reference' => $reference,
'amount' => $this->event->transaction->amount->formatted,
]);
@@ -58,6 +58,7 @@ class OrderStatusUpdatedNotification extends BaseNotification
return (new MailMessage)
->subject(__('Your order :reference has been updated', ['reference' => $reference]))
->view('core::order.notifications.status-updated', [
'order' => $order,
'reference' => $reference,
'statusLabel' => config("lunar.orders.statuses.{$order->status}.label", $order->status),
]);
@@ -5,6 +5,8 @@ namespace Modules\Core\Store\Services;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Event;
use Lunar\Models\Language;
use Lunar\Models\Order;
use Modules\Core\Order\Services\OrderStatusFlow;
use Modules\Core\Store\Events\StoreDetailsUpdated;
use Modules\Core\Store\Models\StoreDetails;
@@ -35,6 +37,24 @@ class StoreDetailsService
);
}
/**
* Null for any non-bank-transfer order — the confirmation email only
* shows this block when there's actually a wire to send (see
* BankTransferPaymentDriver's own docblock for why a bank transfer
* order stays at 'awaiting_payment' until staff confirm the wire
* arrived). Null also when the store hasn't filled the field in for
* $locale, so the caller's own @if($bankTransferInstructions) guard
* covers both cases identically.
*/
public function bankTransferInstructionsFor(Order $order, string $locale): ?string
{
if (! app(OrderStatusFlow::class)->isBankTransfer($order)) {
return null;
}
return $this->current()->translate('bank_transfer_instructions', $locale) ?: null;
}
public function update(array $attributes): StoreDetails
{
$storeDetails = $this->firstOrCreate();