← All projects

In acceptance testing

SaaS management platform for insurance intermediaries

Insurance sub-agents manage clients, agreements with insurance companies, policies, due dates and payments across scattered Excel files. Every piece of information lives in a different file, and nobody has the full picture.

Role
Sole developer
Period
April → October 2026
Status
Complete in staging, with formal acceptance testing under way together with the client: every point is closed, released and verified. I delivered a user manual and technical documentation. Going live in production is the next step.
Stack
Laravel 13
PHP 8.4
Livewire 4
Flux
Tailwind 4
MySQL 8.4
Scout + Meilisearch
Cashier + Stripe
Brevo
Spatie MediaLibrary
PHPUnit 12
Playwright
Forge

A subscription platform where each intermediary has their own space, with data kept separate from everyone else’s. Three areas: one for the intermediary, one for their policyholders and one for whoever runs the platform.

Intermediary area

  • Private and business clients, with calculation and validation of Italian tax codes and VAT numbers, EU and foreign ones included, and duplicate blocking that explains why.
  • Agency agreements with multiple insurance companies and products, with commissions by type.
  • Negotiations with several quotes, even from different companies: confirming one creates the policy.
  • Policies with their full lifecycle: renewal, replacement, scheduled suspension with instalments recalculated, multi-year and single-premium policies. Every step stays in the history.
  • Due dates split between instalments to collect and renewals, with 30-day windows and overdue items. From there you collect, send reminders and export.
  • Cash sheets that fill themselves from collected payments, exportable to a formatted Excel file.
  • A folder-based document archive with a PDF viewer and no public links.
  • Emails sent from the app, and client replies that land on their own in the right history.
  • Global search across 6 kinds of data and portfolio analysis: KPIs, clients with no policy in a given line of business, a client map, three years of collected payments.
  • Bulk Excel import with system-generated templates, a row-by-row preview and a progress bar.
  • Two-factor authentication, a due-date feed to plug into your own calendar, subscription by card or bank transfer, full data export and account deletion after 30 days.

Policyholder area

  • An app you can install on your phone. Policyholders sign in with their tax code and a one-time code sent by email, with no password.
  • They see their policies and instalments, download documents and message their intermediary.

Platform administration

  • Monthly and annual recurring revenue, retention, average revenue per customer and conversion.
  • User management, with the option to step into an account to provide support.
  • Plans kept in sync with Stripe, refunds, Stripe and mail server keys editable from the interface and stored encrypted.
  • Versioned legal texts with consent tracking, and an audit log.

Subscriptions that cost nobody money

Upgrading takes effect immediately and shows the proration before you confirm. Downgrading happens at the end of the period, so you get full use of what you already paid for. Cancellation is never immediate, and if Stripe sends the same notification twice, the second one is ignored.

A silent bug after an upgrade

After a Livewire upgrade, one type of field had stopped sending its value to the server, with no error on screen. The result was wrong amounts. I tracked it down and fixed it in all 23 places where it appeared.

Works locally, fails on the server

On the staging server some €650 amounts were being reset to zero, while locally everything ran smoothly. The cause was messy real-world data that didn’t exist in development.

Since then I follow a fixed rule: first I write a test that reproduces the server’s state, then I fix the code. And for data that is already damaged, I write a command that repairs it.

The import that kept timing out

I measured 21 database queries per imported row and brought them down to 10. A test fails if the count goes above 12, so the problem can’t come back. Files are processed in chunks of 250 rows, and the system even recognises Apple Numbers files disguised as .xlsx.

Privacy down to the details

The email sign-in code always responds in the same amount of time, so nobody can find out who has an account. Password recovery works the same way and never reveals which emails exist. And the policyholder app never stores private pages on the device, because a phone can be shared.

The year 2038 limit

An extension to 2050 kept failing for no obvious reason. The column type used for dates doesn’t go past 2038, so I replaced it where needed.

1,253 automated tests, up from 47 in July
265 commits, all mine
99 business actions and 54 models
86 Livewire components
3 + 2 login types and installable apps
15,000+ instalments of real data in staging