Tony Morinello

All work

Full-stack · Rules and data

OttTotesCo.

Booking, delivery scheduling and payments for a tote-rental business, with the owner's back office built in.

Role
Sole designer and developer
Built with
C#, ASP.NET Core Razor Pages, SQL Server, EF Core, Stripe, Tailwind
Walkthrough: Ott Totes Co., silent, 5 steps
Step one of the booking: four tote packages, with 20 totes for $99 chosen.

Ott Totes Co. is a booking site and back office for a business that rents reusable moving totes. Customers choose a package, pick drop-off and pickup dates, sign the rental agreement and pay, with no account needed. The owners run deliveries, inventory, pricing and reviews from one portal.

The problem

A date can be offered only when several limits agree. There have to be enough totes for the whole rental, plus time to clean them. The day has to be a working day outside any blocked dates and under the cap on deliveries, with a time window still open on both the drop-off and pickup days.

Customers pay in full at checkout, so the price on screen has to be what Stripe charges. Pennsylvania sales tax depends on where the totes are delivered, and ZIP codes in the service area cross county lines. And two customers can reach checkout for the last totes at the same moment.

What I built

The booking wizard asks for a package, dates and time windows, the delivery address, and a signature typed as a name. Stripe Checkout takes the payment and saves the card, so damage or an unreturned tote can be charged later; no card data touches the application. The server re-quotes the price on every change, delivery and tax included, and copies the prices onto the booking when it’s made. A rate change next season leaves old receipts alone.

The owner’s portal opens on today’s deliveries and collections in time order, with overdue rentals at the top. The calendar shows how many totes are committed each day, and a form blocks a whole day or a single time window. Collected totes go into cleaning before they can be booked again. Other pages cover inventory, pricing, time windows, reviews, the mailing list and sales tax, with the figures for filing broken out by county.

Key decisions

Guest checkout, with optional accounts

The owners said a sign-up step would cost bookings, so the wizard asks for nothing but an email address. That makes the confirmation email the way back to a booking, through a long, unguessable link. An optional account lists a customer’s rentals by email address, including bookings made before it existed. Because it shows names, addresses and phone numbers, the address has to be verified before anyone can sign in.

Hold the totes, and honor a late payment

Totes are reserved when checkout starts and released by a background job if payment never arrives, so two people can’t take the last twenty. Stripe’s notice and the customer’s return page both confirm through the same method, and an atomic status swap lets only one of them win. If a payment lands after the hold has expired, the booking is still confirmed, because the customer did pay, and the owner sorts out any clash by hand. The alternative was charging someone for a rental the system had dropped.

Tax from the street address

Tax is worked out from the street address, never the ZIP. Towns that sit wholly inside one county come from a table; anything else, such as Trafford, is geocoded. If an address can’t be placed, checkout refuses the booking instead of falling back to the state rate, which would under-collect on every Allegheny County delivery it missed. The occasional booking is lost that way. Each booking records which tax rule applied and how its rate was decided, so a charge can be explained years later.

A closer look

The first step offers four package sizes. This booking takes 20 totes for $99.

The calendar strikes out closed days, Sundays here. The customer picks an afternoon drop-off window and a morning collection window.

Once the address is in, the review step adds tax by delivery location, 6% here, for a total of $104.94. The customer signs by typing their name.

In demo mode a stand-in replaces Stripe’s checkout. The confirmation page then lists what happens next and shows the receipt.

The owner’s calendar shows each day’s deliveries and collections, how many of the demo’s 60 totes are committed, and a form for blocking dates.

How I checked it

The tests are xUnit, run against a real SQL Server LocalDB database, with files for availability, pricing, checkout, the booking lifecycle, tax and an end-to-end booking. Several pin down the decisions above. Two simultaneous checkouts for the last totes produce exactly one winner. A paid booking whose hold expired is still confirmed. A later price rise doesn’t rewrite an existing booking. GitHub Actions builds and tests every push to main and every pull request.

For the walkthrough I ran the app end to end in demo mode with invented customers. Five bookings went through the public flow and the demo checkout, and I worked through the owner’s dashboard, calendar and booking pages.