# CeylonHandicrafts.lk — Transactional Email Requirements

**For:** the backend (admin-panel/Laravel) developer
**Why:** right now `MAIL_MAILER=log` in the backend's `.env` — every email the app *would* send is just written to `storage/logs/laravel.log` instead of actually going out. No customer, vendor, or admin currently receives a single automated email (not even email verification or password reset), even though the frontend already has the pages/flows that expect them. This document lists every point in the app that needs a real email, and what's needed to switch delivery on.

---

## 1. Turn on real SMTP delivery

In the backend's `.env` (and mirrored in production):

```
MAIL_MAILER=smtp
MAIL_HOST=<your SMTP host>
MAIL_PORT=587
MAIL_USERNAME=<smtp username>
MAIL_PASSWORD=<smtp password / app password>
MAIL_ENCRYPTION=tls
MAIL_FROM_ADDRESS=hello@ceylonhandicrafts.lk
MAIL_FROM_NAME="CeylonHandicrafts.lk"
```

Notes:
- Port 587 + `tls` (STARTTLS) is the usual combination; 465 + `ssl` is the alternative if the provider requires it.
- A dedicated transactional email provider (Brevo, Mailgun, Amazon SES, Postmark, Resend, SendGrid, etc.) delivers far more reliably than a personal mailbox's SMTP — most also have a native Laravel mailer (`ses`, `postmark`, `resend`, `mailgun` in `config/mail.php`) that skips `MAIL_HOST`/`MAIL_PORT` entirely and just needs an API key.
- Whichever is chosen, add SPF, DKIM and DMARC DNS records for `ceylonhandicrafts.lk` (or whatever domain `MAIL_FROM_ADDRESS` uses) — without these, mail from a brand-new sending domain lands in spam almost every time.
- After configuring, send a real test email (`php artisan tinker` → `Mail::raw('test', fn($m) => $m->to('you@example.com')->subject('test'))`) before relying on any of the flows below.

---

## 2. Required emails, by flow

Every trigger below is an **existing** action this frontend already calls (or an existing admin-panel action) — nothing here needs a new API endpoint on its own. "Frontend impact" is called out only on the two items where one exists.

### A. Account & Auth
| # | Email | Trigger | Recipient | Content |
|---|---|---|---|---|
| 1 | **Verify your email** | Account created (`POST /api/public/register`) | New account's email | Verification link → the account clicks it → backend redirects to `/email-verified` (already built) |
| 2 | **Resend verification** | `POST /api/account/email/resend` (the frontend already has a "resend" banner that calls this) | Account's email | Same as #1 |
| 3 | **Reset your password** | `POST /api/public/auth/forgot-password` | Account's email | Reset link → `/reset-password?token=...` (already built) |
| 4 | **Password changed** *(recommended)* | Successful `POST /api/public/auth/reset-password` | Account's email | Simple confirmation + "wasn't you? contact us" |

### B. Vendor Application (before an account exists)
| # | Email | Trigger | Recipient | Content |
|---|---|---|---|---|
| 5 | **Application received** | `POST /api/public/vendor-applications` (Become a Vendor signup form) | Applicant's email | Confirms receipt, restates business name + plan, sets expectation ("we review within 1–2 business days"), links to `/become-a-vendor/confirmation/{id}` |
| 6 | **Application approved** | Admin marks the application approved/active | Applicant's email | Welcome + clear next step — **see the decision flagged in §3 below** |
| 7 | **Application rejected** *(recommended)* | Admin marks the application rejected | Applicant's email | Polite decline, reason if captured, invite to reapply |
| 8 | **New application — notify your team** *(recommended)* | Same trigger as #5 | Internal ops inbox (e.g. `hello@ceylonhandicrafts.lk`) | Applicant + business + plan, link into the admin panel |

### C. Vendor Product Management
| # | Email | Trigger | Recipient | Content |
|---|---|---|---|---|
| 9 | **Product approved** | Admin sets a product's `approval_status` to `approved` | Vendor's account email | Product name + link to the live listing |
| 10 | **Product rejected** *(recommended)* | Admin sets `approval_status` to `rejected` | Vendor's account email | Product name + reason if captured, link to edit/resubmit |
| 11 | **New product submitted — notify your team** *(recommended)* | Vendor creates a product (`POST /api/account/vendor/products`, always lands `pending`) | Internal ops inbox | Vendor + product name, link to the moderation queue — consider a daily digest instead of one email per submission if volume gets high |

### D. Orders (both Vendor-Listed and Managed Sale products)
| # | Email | Trigger | Recipient | Content |
|---|---|---|---|---|
| 12 | **Order confirmation** | `POST /api/account/orders` (checkout, already wired) | Customer's account email | Order number, items, total, payment method — for bank transfer, "we'll confirm once payment is received" |
| 13 | **Order shipped** | Item's `shipping_status` set to `shipped` (`PATCH /api/account/vendor/orders/{orderId}/items/{itemId}/status`, already called from the vendor dashboard) | Customer's account email | Which item(s) shipped, tracking info if captured |
| 14 | **Order delivered** *(recommended)* | Same endpoint, status `delivered` | Customer's account email | Delivery confirmation + link to the order detail page |
| 15 | **New order — notify the vendor** | Same trigger as #12, once per vendor whose product is in the order | That vendor's account email | Their items in the order, delivery details, link to `/vendor/orders` |
| 16 | **New order — notify your team** *(recommended)* | Same trigger as #12, only when the order includes a "Sold by CeylonHandicrafts.lk" item | Internal ops inbox | Triora Living is seller of record for these — your team needs to fulfil them directly |

### E. Leads & Messaging (non-checkout enquiries)
| # | Email | Trigger | Recipient | Content |
|---|---|---|---|---|
| 17 | **New enquiry — notify the vendor** | `POST /api/account/leads` (the inquiry form on a non-buyable product) | That vendor's account email | Buyer name, product, message, link to `/vendor/leads/{id}` |
| 18 | **New message — notify the other party** *(recommended)* | A reply posted in a lead/conversation thread | Whichever side didn't send it | Short preview + link to the thread. Worth batching (e.g. max one email per thread per 15–30 min) so an active back-and-forth doesn't spam either side |

### F. Vendor Subscription (`normal_vendor` plans only)
| # | Email | Trigger | Recipient | Content |
|---|---|---|---|---|
| 19 | **Payment proof submitted — notify your team** | `POST /api/account/vendor/subscription/subscribe` (already wired) | Internal ops/finance inbox | Vendor, plan, bank name, transaction reference, receipt link — so payment can be verified manually |
| 20 | **Subscription activated** | Admin approves the payment / marks the subscription active | Vendor's account email | Plan name, active period if tracked, link to `/vendor/subscription` |
| 21 | **Subscription expiring soon** *(recommended, only if expiry dates are tracked)* | Scheduled job | Vendor's account email | Renewal reminder + instructions |

### G. Moderation
| # | Email | Trigger | Recipient | Content |
|---|---|---|---|---|
| 22 | **Account/listing suspended** *(recommended)* | Admin suspends an account or listing | Affected vendor | What was suspended, why, and how to respond — this is what the Vendor Agreement page already promises customers/vendors ("we will explain the reason and provide a reasonable opportunity to respond") |

---

## 3. The one open decision

Items **#6 (application approved)** and, by extension, **#20 (subscription activated)** depend on a workflow decision only the backend side can make:

- **Option A — admin approval auto-creates the Account.** The approval action creates an `Account` row (`account_type: normal_vendor` or `marketplace_vendor`) and the email includes a "set your password" link (same pattern as password reset). Cleaner UX, no duplicate data entry, but needs a small frontend addition: a "set your initial password" variant of the existing reset-password page (trivial — it already has the form, just needs a different landing copy/redirect).
- **Option B — the applicant self-registers separately.** The approval email just says "you're approved — create your account at `/register` using this email address" and the two records (`VendorApplication` and `Account`) stay linked by matching email, or get linked manually by an admin. No frontend change needed, but it's an extra manual step for the vendor and a small risk of the emails not matching.

Recommend Option A. Either way, flag this back once decided — happy to build the frontend piece if Option A is chosen.

---

## 4. Checklist for the backend developer

- [ ] Set real SMTP (or provider API) credentials in `.env`; switch `MAIL_MAILER` off `log`.
- [ ] Add SPF/DKIM/DMARC DNS records for the sending domain.
- [ ] Send one real test email end-to-end before relying on any flow below.
- [ ] Implement a Mailable/Notification for each **Required** item above (1, 2, 3, 5, 6, 9, 12, 13, 15, 17, 19, 20 — 12 emails), hooked into the existing controller actions already listed next to each.
- [ ] Decide the vendor-application-approval flow (§3) and confirm with the frontend side.
- [ ] The **recommended** items (4, 7, 8, 10, 11, 14, 16, 18, 21, 22) can follow after the required set ships.

No new API endpoints or request/response shapes are needed for any of this — every trigger listed already exists and is already called by this Next.js frontend (or is an existing admin-panel action). This is purely a backend mail-configuration and Mailable-class task.
