my-advent
LiveDigital Advent calendars to give away — marketing site, customer portal, back office and a multi-tenant calendar PWA.
Giving away a digital Advent calendar means picking a photo for every day until Christmas, writing something to go with it, and getting the whole thing into the hands of the person it is for in time. This project’s predecessor could already do that, but every order ended at a person who started a container by hand, issued a certificate and added an entry to the proxy. Eight orders from a single interested customer were left lying there that way, four of them with the calendar already filled — not one went online.
my-advent is the rebuild: a monorepo with two running applications. One carries the marketing site, the customer portal, the back office and the API; the other serves every customer calendar, and which one is meant is written in the address that was called. Between a paid order and a link that can be sent on there is no longer a person. If one of the automatic checks goes wrong, the customer does not wait in silence — the operator is told.
What stands out is how much fell away: no container management from inside the application, no certificate per customer, no payment service of my own, no database server. What is left is two containers, one database file and a directory of images. What also stayed are three deliberately different ways in, depending on who is arriving: whoever is given a calendar should have to set up nothing at all; whoever buys one, as little as possible; whoever runs the shop signs in properly. Each group gets as much effort as it is fair to ask of them, and none beyond that.
Decisions
One service for every calendar instead of a container per customer
Every customer used to get a running instance of their own, with a proxy entry and a certificate to match; the back office needed rights for that which amounted to those of the server administrator, and every order was a chain of steps that could break in several places. The isolation bought with all that is needed nowhere: all calendars run the same code, and the order data sits together anyway. Creating a calendar now means writing one row to the database.
A hosted checkout instead of a payment service of my own
Payment used to run through a service I had built myself: around 3,000 lines for exactly one provider, with the API secret in the log and empty error handling. The hosted checkout charges a lower fee, brings Apple Pay and Google Pay with it — the deciding point for an impulse buy of just under five euros on a phone — and produces the invoice with the tax breakdown at the same time. The price is a dependency on someone else's service; the alternative was a payment service of my own with the secret written to the log.
No user account for the recipient
The plan had been an account per calendar in a sign-in service of my own. But the complaint from real use — “you have to log in again all the time” — was not a problem of identity, it was the cookie lifetime; accounts would not have fixed it, and they would have sent grandparents who only want to look at a photo through a sign-in flow. So what was actually broken got fixed — the lifetime — and not the thing it looked like.
Build it first, pay afterwards
The other way round would have been simpler: no unpaid drafts taking up space and inviting abuse. But anyone who has picked 24 photos and written a message for each has put in half an hour, and that work is the strongest argument the product has for buying it — behind a paywall it would be wasted. Drafts are therefore created on the server before any payment, and expire after 30 days.