Skip to content
prod e051e98
Browse

4 · Email (SMTP)

Objective — wire transactional email and prove it actually sends — the hard part is finding where SMTP resolves at runtime, because the most common vendor family loads it from the DB and the panel’s warning banner lies about it.

Transactional email is a MUST — password resets, invoices, and notifications all depend on it, and section 1 of page 3 warned you not to change the admin email until mail works. The hard part isn’t the SMTP fields; it’s knowing where the app actually reads them from — and on the most common CodeCanyon vendor family, the admin UI actively lies about that.

Before touching any field, decide where SMTP config actually resolves at runtime. The brand survey (page 2) already flagged whether the vendor ships a CustomConfigProvider; confirm it now.

flowchart TD
Q{"Does a CustomConfigProvider /<br/>SettingsProvider rewrite mail config<br/>at boot from a DB table?"}
Q -->|Yes — Froiden family| DB["DB table is truth<br/>(email_settings).<br/>Configure the ADMIN PANEL.<br/>.env MAIL_* = fallback only"]
Q -->|No admin Email tab at all| ENV["Configure shared/.env MAIL_*<br/>(see Phase 8 email infra)"]
Q -->|Panel present, no provider| PANEL["Admin panel writes config<br/>directly — use it"]
  1. Grep for a provider that rewrites mail config at runtime.

    Terminal window
    grep -rn "mail\.mailers\.smtp\|Config::set.*mail" app/Providers/ app/Http/Middleware/ 2>/dev/null | head
    find app/ -name "*ConfigProvider*" -o -name "*SettingsProvider*" 2>/dev/null
    # Expected: a provider that calls Config::set('mail...'), or no match
    • ✅ If a provider calls Config::set('mail.mailers.smtp.*', DB::table(...)), the database overrides .env and the admin panel is the source of truth; otherwise .env is.

The lesson generalizes beyond this one banner:

Admin → Config → SMTP (or Settings → Email). This is a panel form a person fills and a panel button a person clicks — the test send and the inbox check are human-verified.

  1. Fill the SMTP fields from your Phase 4 SMTP credentials + brand profile.

    FieldValue
    Mail From NameApp name (brand profile)
    Mail From Emailadmin@[DOMAIN]
    SMTP Hoste.g. smtp.hostinger.com
    SMTP Port587 (TLS) or 465 (SSL)
    Username / Passwordmailbox creds — paste the password via the clipboard bridge from page 3 §1
    EncryptionTLS (587) / SSL (465)
    • ✅ All fields are filled from real credentials; the password was pasted via the clipboard bridge.
  2. Save, then click the panel’s own “Test SMTP” button — not artisan tinker. Tinker sends through Laravel’s default mailer, which a CLI context may not have reconfigured via the provider yet.

    • ✅ “Test SMTP” succeeds and the dashboard “SMTP details are not correct” banner disappears — that’s your real success signal.
  3. Trigger a password-reset on a test account and confirm the email arrives.

    • ✅ A real password-reset email lands end-to-end in the inbox.

3. Set a sensible .env fallback (defence in depth)

Section titled “3. Set a sensible .env fallback (defence in depth)”

Even when the DB path is truth, leave real MAIL_* values in shared/.env. If the email_settings row is corrupted or the provider query fails, Laravel falls back to .env. Real values = mail still works; garbage values = mail breaks silently.

  1. Append the fallback idempotently (Python-free here, but grep -q guards against duplicates; special-char passwords stay out of this block).

    Terminal window
    grep -q "^MAIL_HOST=" shared/.env || cat >> shared/.env <<'EOF'
    # SMTP fallback (.env defaults; runtime config overridden by CustomConfigProvider from DB)
    MAIL_MAILER=smtp
    MAIL_HOST=smtp.hostinger.com
    MAIL_PORT=465
    MAIL_ENCRYPTION=ssl
    MAIL_FROM_ADDRESS=admin@[DOMAIN]
    MAIL_FROM_NAME="[APP_NAME]"
    EOF
    # Expected: the MAIL_* block is appended once (no duplicate on re-run)
    • shared/.env carries real MAIL_* fallback values, appended only once.
  1. Review the email templates (if the vendor exposes Admin → Email Templates) — welcome, password-reset, invoice, and notification — setting the sender name, logo, and footer to match your brand (the email header logo you uploaded on page 3 lives here on some vendors).

    • ✅ Sender name, logo, and footer match the brand on every reviewed template.
  2. Confirm no SMTP credentials are committed to git — they belong in shared/.env on the server and your password manager only.

    • ✅ No SMTP secrets are tracked in git.

Do not mark this step done until every box below is checked.

  • 🤖 Source confirmed — where SMTP resolves at runtime (DB-via-provider vs .env) is confirmed; the Froiden banner was verified against source, not trusted.
  • 👤 Panel SMTP saved — “Test SMTP” passes and the dashboard error banner is gone.
  • 👤 Reset email received — a real password-reset email arrived end-to-end.
  • 🤖 .env fallback setshared/.env carries real MAIL_* fallback values; no SMTP secrets in git.
  • 👤 Templates reviewed — sender, logo, footer reviewed if the vendor exposes them.