# Email goes through OneSignal, and only the e-mails someone needs are sent

The site sent e-mail the way the CodeCanyon CMS did: SMTP, about 35 send paths, one e-mail per order
status to the customer, the seller and the admin, welcome mails, wallet and refund mails, and a few
broken ones (an invoice mail that called `->to()` instead of `->send()`, a low-stock mail sent with raw
PHP `mail()` to fixed addresses including a personal Gmail). Since 2026-09-25 everything but the
low-rating alert has been dropped by `MAIL_SENDING_ENABLED=false`. The ticket "verify the sending of
e-mails and notifications" asked to move e-mail to OneSignal, remove the unnecessary e-mails and verify
the notifications of an order status change.

Decided with the user on 2026-11-09 (a grilling round; every recommendation accepted):

- **One transport, not one service per e-mail:** a Laravel mail transport `onesignal`
  (`App\Services\Email\OneSignalTransport`, `MAIL_DRIVER=onesignal`), so every send path - `Mail::send`,
  mailables, mail notifications - goes through it without its caller changing, and the
  `MAIL_SENDING_ENABLED` switch and the low-rating exception keep working. SMTP stays selectable until
  the sending domain is verified in OneSignal.
- **No hidden fallback:** a OneSignal refusal is logged and the e-mail is lost; it is never retried over
  SMTP, which would hide an unverified domain.
- **Transactional by default** (`include_unsubscribed`, no unsubscribe link, no click tracking): a code
  or a password-reset link must arrive and work as written. Only the admin newsletter is marketing.
- **Plain addresses** (`include_email_tokens`), not aliases: OneSignal makes an email subscription per
  address, accepted; nothing in the push flows targets e-mail.
- **No order e-mail at all.** Customers get the push and the inbox entry (`CustomerOrderStatusPush`),
  sellers and staff their panels; most customers have no e-mail address (phone sign-in). This also
  removed a crash: `order_email` threw outside its `try` on a guest order or a seller without a shop.
- **Kept:** e-mail verification codes, forgot password by e-mail, the seller registration notice to the
  admin (a new seller needs approval), seller payout and withdraw-request mails, the low-rating alert, the newsletter and the admin test mail.
- **Removed:** welcome mails and the new-customer notice to the admin, wallet, refund, delivery boy,
  auction, chat, low stock, Contact Us to staff (every request is handled on WhatsApp from Admin →
  Contacts; decided later the same day), the invoice mail, the debug route `/wallet_payment_email_test` and the
  unused mailables. The `email_templates` rows are untouched; Admin → Email Templates lists only the
  ones still sent (`EmailUtility::SENT_TEMPLATES`).
- **Order status notifications** were verified through the real screens
  (`OrderStatusNotificationPathsTest`). Fixed on the way: `DbNotification` threw with nobody signed in
  (Paymera / eCash callbacks wrote no seller "paid" entry and could skip the seller's new-order alert),
  the admin status list hit `findOrFail` + `dd()` on a missing customer account after saving the
  status, and the customer status types had no Arabic (migration `2026_11_09_000001` adds it where
  none exists).

## Considered Options

- **A `OneSignalEmail::send()` service called by each kept e-mail:** rejected - every caller changes,
  and a path someone forgets keeps going over SMTP.
- **Fall back to SMTP when OneSignal refuses:** rejected - the switch is the `.env` value; a silent
  fallback hides a misconfigured domain.
- **Keep the order e-mails for sellers and admins:** rejected - a big checkout sent the admin dozens of
  mails, sellers already get panel, app and WhatsApp alerts, and staff the bell and its browser push.

## Not in this decision

- The delivery-boy API (`POST api/v2/delivery-boy/change-delivery-status` takes any order and status
  from any signed-in account): seen during the check, left for its own fix.
- The "rate your order" push still arrives together with "delivered".
- Ringing the staff bell for cancels, returns and refunds, and addressing it to every staff member
  instead of the single `get_admin()` account: a follow-up.
