Commit Graph

6 Commits

Author SHA1 Message Date
Víctor Falcón 14b79557d7
fix: verify email via signed link without requiring login (#490)
## Problem

When the verification email link opens in a browser where the user is
not logged in (common on phones, where the link escapes the app), the
`verification.verify` route's `auth` middleware redirects to login and
the email is never verified.

## Fix

The signed verification URL already self-secures via `id` + `hash` +
signature + expiry, so the `auth` requirement was redundant. This adds a
public, signed-only verify route and points the verification email at
it.

- `Auth/VerifyEmailController` — resolves the user by `id`, validates
`hash` with `hash_equals(sha1(email))`, marks verified and fires
`Verified`. Guests → `login` with a success status; the same logged-in
user → `dashboard?verified=1`.
- New route `GET /verify-email/{id}/{hash}` with `signed` + `throttle`
(name `verification.verify.public`). Distinct URI/name so it doesn't
clash with Fortify's existing auth-protected route, which stays intact.
- `VerifyEmailNotification::verificationUrl()` overridden to sign the
public route.

Now the link verifies regardless of login state, and the user can return
to the app and sign in.

## Tests

- 6 new cases in `EmailVerificationTest` (logged-out verify, logged-in
verify, bad hash, unsigned request, already-verified no refire).
- 1 new case in `VerificationNotificationTest` asserting the email links
the public signed route.
- All pass; pint clean.
2026-06-05 10:01:32 +02:00
Víctor Falcón f408dbe4c8
feat: selective retry of failed lead email jobs (#286)
## Summary

- Adds `leads:retry-failed-jobs` artisan command to selectively retry
failed `emails`-queue jobs after a Resend rate-limit incident
- Retries jobs for verified leads and `VerifyUserLeadEmailNotification`
for unverified leads; forgets jobs for deleted leads (DDoS cleanup) or
unverified leads receiving waitlist emails
- Adds `deleteWhenMissingModels = true` to all lead mail/notification
classes so future jobs for deleted leads are silently discarded instead
of failing

## Usage

```bash
# Preview (no changes)
php artisan leads:retry-failed-jobs --dry-run

# Execute
php artisan leads:retry-failed-jobs
```

## Test plan

- [x] `leads:retry-failed-jobs` forgets jobs for deleted leads
- [x] `leads:retry-failed-jobs` retries jobs for verified leads
- [x] `leads:retry-failed-jobs` forgets waitlist jobs for unverified
leads
- [x] `leads:retry-failed-jobs` retries
`VerifyUserLeadEmailNotification` for unverified leads
- [x] `leads:retry-failed-jobs` handles mixed job types correctly
- [x] `--dry-run` does not modify `failed_jobs`
2026-04-15 08:00:29 +01:00
Víctor Falcón d0aab3d11b
feat: verify waitlist leads (#285)
## Summary
- gate waitlist signup behind email confirmation so only verified leads
receive a queue position and referral code
- add signed lead verification routes, a check-email page, and a
dedicated verification email for the waitlist flow
- update lead syncing and feature tests so only verified leads are
exported to Resend and referral movement happens after verification

## Testing
- php artisan test --compact tests/Feature/UserLeadTest.php
- php artisan test --compact
tests/Feature/ResendSyncLeadsCommandTest.php
- php artisan test --compact tests/Feature/MailSenderTest.php
- vendor/bin/pint --dirty --format agent
2026-04-14 11:26:01 +01:00
Víctor Falcón ce5692cb30
fix: split drip and default email senders (#263)
## Summary
- route drip mailables through `Álvaro and Víctor <hi@whisper.money>`
and send the rest from `Whisper Money <no-reply@whisper.money>`
- remove legacy per-mailable `Victor` sender overrides so non-drip mail
falls back to the default sender consistently
- add focused sender coverage for drip, non-drip, and verification mail
paths

## Testing
- `php artisan test --compact tests/Feature/MailSenderTest.php
tests/Feature/Jobs/Drip/SendWelcomeEmailJobTest.php`
2026-04-06 12:16:47 +02:00
Víctor Falcón 70b603e901
feat: Spanish localization (#74)
## Pending
- [x] Translate landing page
- [x] Dashboard
- [x] Accounts list page
- [x] Account page
- [x] Cashflow
- [x] Budgets
- [x] Transactions
- [x] Settings

## Screenshots
<img width="1210" height="969" alt="image"
src="https://github.com/user-attachments/assets/c7935e5c-488d-4941-8f19-8834e5668257"
/>
<img width="1211" height="972" alt="image"
src="https://github.com/user-attachments/assets/e94e1daf-233a-4a49-aa65-5678c772d178"
/>
2026-02-08 11:58:08 +01:00
Víctor Falcón 370d388d99
feat: Enable email verification on sign up (#97)
## Summary
- Enables `MustVerifyEmail` on the `User` model so new users must verify
their email before accessing protected routes
- Unverified users are redirected to `/email/verify` when attempting to
access routes behind the `verified` middleware (subscribe, onboarding,
dashboard)
- A verification email is automatically sent on registration via Fortify

## Screenshot

![Email verification
page](https://raw.githubusercontent.com/whisper-money/whisper-money/mail-verification/storage/videos/email-verification-page.png)

## Video


https://github.com/whisper-money/whisper-money/raw/mail-verification/storage/videos/ac0945c527f014b9cd657f18c911f496.webm

## Test plan
- [x] New test: registration sends a `VerifyEmail` notification
- [x] New test: newly registered users have `email_verified_at` as null
- [x] New test: unverified users are redirected to `verification.notice`
from protected routes (subscribe, onboarding, dashboard)
- [x] New test: verified users are not redirected to verification notice
- [x] Existing email verification and notification tests still pass (31
auth tests, 52 settings tests)
- [x] Browser walkthrough: registration → redirected to `/email/verify`
page with "Resend verification email" button
2026-02-03 10:15:07 +01:00