All articles
Systems4 min read

The sales infrastructure I built for my first digital product

There is no payment in this infrastructure, and no orders. I built delivery, permission and the ability to say no first — and the most expensive lesson came from a new flaw that one of my fixes created.

There is no payment in this infrastructure.

The orders table is empty, no payment provider has been chosen, and no screen that takes a card has been written. When I say "sales infrastructure" I don't mean checkout. I mean everything around it. And that order is a decision, not a gap.

Taking money is useless if you can't deliver the thing. Once delivery is proven to work, adding payment is a day of work. The reverse is not true.

Delivery first, and delivery means email

The product is an ebook, so delivery is an email. That sounds like a two-hour job. It wasn't.

Email HTML is not web HTML. Gmail doesn't support var(), so none of the site's design tokens work. Outlook on Windows renders with the Word engine, which means the layout has to be tables and padding on a link is ignored, so buttons are built as tables. SVG isn't drawn at all, so the brand mark is converted to PNG. Outlook desktop blocks images by default, so the brand is stated in text with the mark sitting next to it.

Then the whole email inverted on an iPhone. Declaring color-scheme: light does not stop a client from inverting you; the email has to manage dark mode itself. Inline styles beat a <style> block, so every rule in the dark palette carries !important.

None of these details matter individually. What matters is this: delivery contained more fragile parts than a checkout screen would have.

Permission: no consent checkbox, and there shouldn't be one

The newsletter form has no "I have read the privacy notice" checkbox. That is not an oversight.

Under Turkish data protection law, a privacy notice is information, not consent, and asking for consent to it is specifically discouraged by the regulator. Delivering the resource rests on performance of a contract, so separate explicit consent isn't required either. Commercial electronic message consent is a different legal instrument entirely and cannot be merged with those two.

Because a written rule doesn't mean an enforced rule, a script audits the source: if a consent checkbox ever appears in that form, the audit says so.

The real work is in being able to say no

The unsubscribe link goes to a page, and a button on that page unsubscribes. It doesn't unsubscribe directly.

Here's why. Links in email aren't only clicked by humans. Gmail, Outlook and corporate security gateways pre-open every link in an incoming message. Wire unsubscribe to a GET and people who never even opened the email get dropped from your list by a scanner, and nobody notices. The loss is silent.

Alongside it sits RFC 8058 one-click unsubscribe: Gmail's own "unsubscribe" button posts a form-encoded POST to a separate endpoint. Both shapes are parsed in one place, because solving them in two places means the two drift apart eventually.

Also, the row isn't deleted. unsubscribed_at gets marked. Deleting would look tidier, but a deleted row doesn't stop the same address being added again tomorrow, and that person will quite reasonably say "I unsubscribed and it's still coming."

The new flaw a fix created

This section carries the most expensive lesson in the whole build.

An unsubscribed address could never subscribe again. Fill in the form, see "You're on the list," receive nothing, forever. From the person's side that is indistinguishable from a broken site.

The fix looked obvious: send a confirmation email to the unsubscribed address and let the owner decide about coming back. A form submission can't override an unsubscribe request, because we have no evidence that whoever filled in the form owns that address. Anyone who knows the address can type it.

The audit didn't stop there, and I'm glad it didn't. In the first design, repetition was only thinned by a 24-hour window, which means it wasn't stopped. A third party who knew someone's address could submit the form once a day and have our domain send mail to an unsubscribed person indefinitely. The victim had no way out: the message itself says "you don't need to do anything," the unsubscribe link re-unsubscribes someone already unsubscribed, and no "never ask again" record was being kept.

The state before the fix was a guaranteed zero emails. So the fix, while closing one flaw, produced something worse than what it replaced. The answer was an attempt counter: a hard ceiling on how many confirmation emails that address can ever receive.

The rule I kept from it: closing a flaw must never produce a state worse than the one before it. And I noticed that not while writing the fix, but while auditing it.

What I measure and what I don't

How many people saw the resource page and how many submitted the form: both counted in my own table. The counter stores nothing about the visitor. No IP, no hash, no session, no cookie. The cost is that unique visitors can't be counted. I accepted that, because what this table is for is a ratio.

The conversion rate shows as empty rather than zero when views are zero. "Nobody saw it" and "they saw it and didn't convert" are entirely different facts, and papering over a division by zero with a zero would mean reporting a failure that never happened.

What I don't know

I don't know that this infrastructure sells anything, because nothing has sold yet. What I've written is not the story of a working system. It's the inventory of a built one, and the reasoning behind its decisions.

I'm also not sure the order is right. "Delivery first, payment second" makes sense to me, but I haven't run the reverse and compared. It's possible that nothing you build without a checkout tests demand at all, and that I spent months building infrastructure for something nobody wants. I'll only find that out when payment goes in.

Build One File, Sell It Again and Again

Free resource

Build One File, Sell It Again and Again

Digital products with AI: what, why, where — and one step to take today for each of 12 concrete products.

No spam, leave whenever you like. When you sign up you can read how your email address is handled in the Data Protection Notice and the Privacy Policy.