Case study · Business systems

Property direct mail and tax payment platform

A Laravel platform for land investors: automated direct-mail offer campaigns, property and sales tracking, tax-payment checks and accounting sync.

Sector
Real estate investing
Category
Business systems
Maturity
Established Laravel app migrating from a legacy codebase; deploy tooling and feature and browser tests present

At a glance

Who it's for
Real estate investor account owners, their sub-users, and platform administrators.
Core stack
PHP 8.2, Laravel 11, MySQL, Stripe
What sets it apart
One system that connects offer-letter campaigns, property pipeline records, tax-payment checks and accounting for land investors.

What it looks like in use

A mailing queue that sends letters at a steady pace, and a tax payments list where a property tax check is issued by a team member who has permission.

Mailing queue and tax payments
Mailing queueImport Mailing Data
Offer letters
Letter
Queued
Letter
Sent
List imported from a CSV fileQueued
Letters go out a few at a timeSending
Sending stops when the daily limit is reachedDaily limit
Mailing settings
Sent by print-and-mail serviceActive
Pause sendingNot paused
Printed mail is billed once a dayAutomatic
Team members see only what they are allowed toPermissions
Tax paymentsIssued Checks
Properties
Search by APN
010-100-512
Example County, tax bill for the year
Check issued
Only team members with permission can issue checks.
Issue Check
Illustrative screen sample data, not client data

From a list of owners to a paid tax bill

  1. You upload your list

    You choose a spreadsheet file of land owners and the letters join a queue.

  2. Letters go out at a steady pace

    A few go out at a time, so you never send too many at once or past your daily limit.

  3. A mail service prints and posts them

    Each letter is printed and mailed for you, and the cost is billed to your account through Stripe once a day.

  4. You pay a property tax bill by check

    You find the property, and an allowed team member issues the county tax check.

  5. Sales reach your accounts

    When a property sells, the sale is recorded and copied into your accounting for you.

The challenge

Land and property investors send large volumes of offer letters to owners, then must track every property, closing, due-diligence step, sale and county tax payment.

In spreadsheets, that means manual mailing, scattered files, repeated re-keying into accounting and no controlled way to pay property taxes by check. Mail also has to be paced: sending too many letters at once, or outside a user's quota and paused settings, is risky. Each account may have many team members who should see only what their permissions allow.

The solution

The platform imports mailing data from CSV files and queues it. A scheduled job drips letters out through a print-and-mail API or uploads them to a cloud storage folder. Users manage organisations, trusts, properties, closings, due diligence, sales and tax payments from one interface, with documents such as deeds generated from templates.

Printed mail is billed through Stripe by a daily process, and tax-payment checks are issued through the same mail provider. Sales are pushed to Xero as manual journals. Account owners manage sub-users, permissions and EULA acceptance.

For your technical teamhow it works under the hood

One drip run, step by step

Sequence of one mail drip run and daily billingSequence diagram with five participants: scheduler, drip job, database, delivery and Stripe. Step 1: the scheduler starts the drip job. Step 2: the drip job checks the user and quota against the database. Then in a loop: step 3, the database returns the next queued mailing; step 4, the drip job sends the letter through the mail API or uploads it to a cloud folder; step 5, delivery accepts it; step 6, the drip job saves the result and counts it against the quota. Step 7: separately, a daily billing job charges the account through Stripe for mail sent.Schedulerscheduled commandDrip jobdripping commandDatabasemailing queueDeliverymail API or folderStripecard billingLOOP1 start drip run2 check user and quota3 next queued mailing4 send letter or upload5 accepted6 save result, count quota7 daily billing charge
Read top to bottom. The drip job only sends while a user is active, has quota left and has queued mail; otherwise the user is skipped until a later run. Billing is a separate daily command, not part of the send loop.

Key features

What the system does, as built in the codebase.

  • 01

    CSV mailing import and dripping queue

    Mapped uploads become queued mailings released on a schedule, within per-user daily limits and pause settings.

  • 02

    Print-and-mail or cloud-folder delivery

    Letters are sent through a mail API or exported to a user's storage account.

  • 03

    Property, trust and organisation records

    Advanced list filters, comments with user tagging and activity logs.

  • 04

    Closings, due diligence and sales

    Workflow objects that link mailings, properties and sales.

  • 05

    Deed document generation

    Word-based deed templates with storage and locator services.

  • 06

    Tax payment checks

    Search properties by parcel number and issue checks per organisation or trust, with test and live modes.

  • 07

    Accounting sync

    Sales become manual journals in Xero, with token renewal and bank balance import.

  • 08

    Account, subscription and permission management

    Account owners and sub-users, impersonation for support, and subscription sync from checkout and Stripe.

How it fits together

From an uploaded mailing list to a paid check and a posted journal.

  1. CSV importcolumn mapping, validation
  2. Dripping queueper-user quotas, scheduled release
  3. Deliveryprint-and-mail API or cloud folder
  4. Core recordsproperties, trusts, closings, sales
  5. PaymentsStripe billing, tax-payment checks
  6. AccountingXero manual journals
Mapped CSV data enters a dripping queue that releases mail within quotas. Letters go out by print-and-mail API or cloud folder. Core property and sales records connect to payments, and sales flow on to accounting as manual journals.
  • Scheduled commands drive dripping, billing and journal uploads.
  • Subscription events sync from checkout and Stripe.

Implementation notes

Engineering decisions worth knowing about.

  1. Cron-driven pipelines

    Artisan commands handle dripping, billing, status updates, expiry emails and report mailing.

  2. Daily Stripe billing job

    Mail costs are charged by class of mail and print options, with test-mode mail exempt.

  3. Database triggers

    Triggers keep property fields in sync with mailings when mailings are deleted.

  4. Strangler-style migration

    The legacy PHP app is autoloaded alongside the Laravel app, with legacy redirects and legacy-enabled test cases.

  5. Policy-based authorisation

    Per-feature permissions, such as issuing checks, with feature flags via Laravel Pennant.

  6. Sensitive value handling

    An encryption service, a custom password hasher, session tracking and EULA gates.

  7. Layered testing

    PHPUnit feature tests, Dusk browser tests and a Codeception acceptance suite for the legacy code, with Sentry for error reporting.

Technology stack

Taken from the project's dependency and config files.

Backend
  • PHP 8.2
  • Laravel 11
  • Laravel Cashier
  • Pennant
  • Telescope
Frontend
  • Blade views
  • Alpine.js
  • Tailwind CSS
  • Vite
Data
  • MySQL or MariaDB with triggers
Integrations
  • Lob
  • Stripe
  • Xero
  • Dropbox
  • SamCart
  • AWS S3
Documents
  • PhpWord
  • mPDF
  • PDF parser
Monitoring
  • Sentry
Testing
  • PHPUnit
  • Laravel Dusk
  • Codeception
  • Paratest
Tooling
  • Makefile build and deploy targets
  • Laravel Pint
For your technical teamtechnology layers

Technology layers

Technology stack layers of the property mail platformLayered diagram, top to bottom: frontend (Blade views, Alpine.js, Tailwind CSS, Vite); backend (PHP 8.2, Laravel 11, Laravel Cashier, Pennant, Telescope); documents (PhpWord, mPDF, PDF parser); data (MySQL or MariaDB with triggers); integrations with outside services (Lob, Stripe, Xero, Dropbox, SamCart, AWS S3); monitoring (Sentry); testing (PHPUnit, Laravel Dusk, Codeception, Paratest); tooling (Makefile build and deploy targets, Laravel Pint).FRONTENDBlade viewsAlpine.jsTailwind CSSViteBACKENDPHP 8.2Laravel 11Laravel CashierPennantTelescopeDOCUMENTSPhpWordmPDFPDF parserDATAMySQL or MariaDB with triggersINTEGRATIONSLobStripeXeroDropboxSamCartAWS S3MONITORINGSentryTESTINGPHPUnitLaravel DuskCodeceptionParatestTOOLINGMakefile build and deploy targetsLaravel Pint
Server-rendered Laravel sits on top of one relational database. Outside services are shown dashed, and testing and tooling sit around the app.

Business value

What the system enables. No numbers are claimed.

  • Reduces manual mailing runs by pacing letters automatically within account limits.
  • Keeps properties, closings and sales in one place instead of scattered spreadsheets.
  • Reduces re-keying of sales into accounting by pushing journals automatically.
  • Gives account owners control over who can issue checks or access which organisation.
  • Provides a safe test mode before live mail or check issuing.

Need a system like this built?

Tell us what it needs to do and we will scope it with you on a call.

Discuss a build