Curriculum · Lesson 6 of 6

Shipping products, payments, and a funnel the agent maintains

Shipping products + payments · 15 min read

The first five lessons give you an agent that is safe and honest: bounded in scope and spend, unable to merge a broken build, and graded on traction rather than motion. This last lesson points all of that at revenue. You wire a real one-time checkout, put landing pages and a content funnel in front of it, and, most importantly, build the funnel so the agent keeps it current by itself instead of rotting the moment you stop editing it by hand.

Charge money before you build the machine

The tempting order is to build a members area, a licensing system, and a webhook pipeline first, then switch on payments once it is all perfect. That is months of work defending a number, zero euros, that nobody has agreed to pay. Reverse it. The smallest honest way to take money is a single hosted checkout for one clearly described thing, live this week. If nobody buys the simple version, no amount of plumbing behind it would have helped. If people do buy, you now have real signal, and real money, to justify the machinery.

So the first product is deliberately boring: one price, one page, one button that opens a hosted checkout. No accounts, no database, no entitlement logic. The payment provider hosts the card form and handles the compliance you do not want to own. Your job is only to create a checkout session and hand the buyer to it.

A one-time checkout, end to end

Keep the whole flow on the server. A checkout route creates a session for a fixed price and redirects the buyer to the provider's hosted page. The secret key never touches the browser. This is the actual shape of the route that sells this course:

// GET /api/course/checkout  (server route, nodejs runtime)
export async function GET(req) {
  const key = process.env.STRIPE_SECRET_KEY;      // server-only, never shipped to the client
  if (!key) return redirect('/course');           // no key configured: fail safe, do not crash

  const params = new URLSearchParams();
  params.set('mode', 'payment');                  // one-time, not a subscription
  params.set('line_items[0][price]', PRICE_ID);   // the price lives in the provider, not in code
  params.set('line_items[0][quantity]', '1');
  params.set('success_url', SITE + '/course/thanks?session_id={CHECKOUT_SESSION_ID}');
  params.set('cancel_url',  SITE + '/course');

  const res = await fetch('https://api.stripe.com/v1/checkout/sessions', {
    method: 'POST',
    headers: { Authorization: 'Bearer ' + key, 'content-type': 'application/x-www-form-urlencoded' },
    body: params.toString(),
  });
  const session = await res.json();
  return session.url ? redirect(session.url, 303) : redirect('/course');
}

Two things make this safe to leave an agent near. The key is read from the environment on the server, so it is not in the repo and not in any bundle the browser downloads. And the price is an identifier defined in the payment provider, not a number in your code, so the agent editing a page can change the words around the button but cannot quietly change what a buyer is charged. This is Lesson 2's deny list made concrete: the checkout route and the payment config are exactly the kind of blast-radius path the worker is fenced out of.

Confirm the payment on the server, never trust the redirect

The buyer lands back on your thank-you page with a session id in the URL. The mistake is to treat arriving on that page as proof of payment. A URL can be visited, shared, or edited by anyone; it is a claim, not a receipt. So the thank-you page takes the session id and asks the payment provider directly whether that session was actually paid, and only then shows the paid content.

// /course/thanks reads ?session_id and verifies with the provider before trusting it
async function isPaid(sessionId) {
  const s = await getCheckoutSession(sessionId);         // server-to-server, with the secret key
  return s?.payment_status === 'paid' || s?.status === 'complete';
}
// paid ? show the founder confirmation : show "we could not confirm payment yet"

This is the same principle as CI from Lesson 4. Done is not a claim the client gets to make. Whether the buyer paid is decided by the provider that took the money, checked server-side, not by whichever page they happened to reach. It is a few lines, and it is the difference between a real checkout and one anyone can walk through for free.

Sell one thing honestly, and say what it is not

A landing page that converts is not a louder page. It is a page that names one buyer, one outcome, and the exact thing they get, and then refuses to oversell it. State the price plainly. Say what is included and, just as clearly, what is not. If the product is early, say it is early. If a lesson is still coming, say when. The honesty is not a moral flourish; it is what makes the refund promise credible and the buyer comfortable, and it is what stops you writing copy the product cannot back up.

Put a real guarantee next to the button and mean it. A plain refund, offered without friction, costs you very little on a small honest product and removes most of the reason a first buyer hesitates. The rule that keeps this safe with an agent in the loop: the agent may write and improve the copy, but the promise it makes has to be one you will actually honour by hand, because a refund is money leaving a real account. Claims about price, delivery, and refunds belong in the reviewed change like everything else, not invented fresh on a page.

The funnel has to maintain itself, or it rots

A checkout with nothing in front of it sells to nobody. The front of the funnel is content: articles, guides, and small free tools that answer a real question someone is already searching for, each linking to the next relevant piece and, where it fits, to the paid product. The problem is that a content funnel built by hand decays the week you stop touching it. Links go stale, the sitemap drifts, a new page never gets cross-linked, and the whole thing slowly stops compounding.

The fix is the one idea that makes an agent-run funnel work: derive the funnel from data, so adding one entry updates every surface at once. Content lives in a registry, a list the code reads. The index page, the individual pages, the navigation, the internal links between related pieces, the structured data for search engines, and the sitemap are all generated from that same list. Adding an article is one entry, and it appears everywhere it should, correctly linked, without a human remembering to touch six files.

// one entry in the content registry...
{ slug: 'the-cost-line-your-invoice-hides', title: '...', cluster: 'saas-cost',
  related: ['a-related-article'], affiliate: { /* honest recommendation */ } }

// ...and the build derives, from that one list:
//   /articles                 index page          (lists it)
//   /articles/[slug]           the page itself     (renders it)
//   internal links + clusters  related reading     (cross-links it)
//   JSON-LD                    structured data     (marks it up for search)
//   sitemap.xml                discovery           (submits it)

Now the agent's content work is a data edit that cannot half-happen. It adds an entry, the funnel wires itself, and the change ships through the same protected branch and required check as everything else. This is why the funnel stays current with an agent running it and rots without one: the maintenance is not a weekly chore someone forgets, it is a property of how the content is stored.

Keep the funnel honest as it grows

An agent that publishes to a self-wiring funnel can also fill it with padding, and padding is its own kind of rot: pages that rank for nothing, recommendations that serve the affiliate fee rather than the reader, numbers that were true once. The oversight loop from Lesson 5 is what holds the line. The CEO role grades the funnel on whether real people arrive and act, not on how many pages exist, and it kills a content rabbit hole the same way it kills any other. Two rules do most of the work: publish only when there is a genuine question to answer, not to hit a count, and every number and every recommendation has to be literally true and worth the reader's trust, because the funnel's only durable asset is that trust.

How this site does it

Everything above is running on the page you are reading. This course is sold through exactly that one-time checkout: a server route creates a hosted payment session, the buyer pays on the provider's page, and the thank-you page verifies the payment server-side before it shows the founder confirmation. The secret key stays on the server and the price is defined in the provider, so the worker agent can rewrite this sales copy but cannot touch what you are charged. The front of the funnel is this site's articles, guides, news, and free tools, every one of them derived from a registry so a single new entry lists, links, marks up, and sitemaps itself. The agent extends that funnel through reviewed, gated pull requests, and the CEO loop keeps it pointed at real traction instead of raw volume.

You can watch the whole machine in public. The playbook walks through the products and the funnel in the agent's own words, and the live log shows the content shipping and the offer being maintained, honestly, every run.

You now have the whole system

That is the operating model, end to end. Constraints the agent cannot talk its way around. A budget it stops at, on both meters. A gate it cannot merge past. An oversight loop that grades it on outcomes. And now a real product, a verified checkout, and a funnel that maintains itself as the agent grows it. None of the six pieces is clever on its own. Together they are the difference between a model running in a loop and a small business you can honestly leave running. Take the version of each you have built alongside these lessons, point it at one real product, and let it ship, inside the frame, one reviewed change at a time.