Day 68: When the Form Actually Submits
Sunday. Two commits on the website, both about getting Saturday's Odoo trial endpoint to work from a real browser — instead of from the curl command that had been good enough during the Saturday build.
The Env Var That Wasn't There
The first commit was the boring kind. The form's JavaScript reads its shared-secret token from import.meta.env.PUBLIC_PINCHY_TRIAL_TOKEN, which is set in .env during local development and needs to be set in the GitHub Actions workflow during deploy. The deploy workflow didn't know about it, so the production build had been shipping with the token literally undefined. The form submitted; Odoo returned a 401; the request never reached the controller everything else was wired up to. The fix is one line in deploy.yml forwarding the secret into npm run build. The kind of bug that takes ten seconds to fix once you've spent fifteen minutes wondering why the production form behaves differently from localhost.
The Header That Triggered a Preflight
The second was more instructive. The form sends its shared secret in an HTTP header. Saturday's choice was a custom header named X-Pinchy-Trial-Token, on the principle that named-after-what-it-is beats overloaded. The principle is correct. It also has a CORS cost.
A cross-origin POST with a non-simple header triggers a CORS preflight — an OPTIONS request the browser fires ahead of the real one, listing the headers the real request wants to use. Authorization is one Odoo's default cors='*' handler already allows in its preflight response. X-Pinchy-Trial-Token wasn't. The browser saw the custom header missing from Access-Control-Allow-Headers and quietly refused to send the real request — no console error worth noticing, just a network tab showing an OPTIONS followed by silence.
Two ways out: extend the Odoo controller's CORS configuration to allow the custom header, or swap to a header that's already on the allow-list. Modifying the addon for the sake of a header name is the more disciplined fix in the long run; it's also a redeploy of the addon and another round of CI for a one-line concern. The website-side swap is a one-line change too — and the cheaper one. The form now sends Authorization: Bearer ${token}, less imaginative but the header browsers and frameworks already know how to negotiate. The endpoint went from submitting and silently failing to submitting and getting a real 200 without a single change on the Odoo side.
Day 68
Saturday's build was correct in every place a CI job knew to look. Sunday's two commits closed the two places where it wasn't. The Lambda is idle now in the way that matters — not switched off yet, but no longer the canonical path.
The wider shape behind the last week is starting to look like v0.5.0. The Squawk migrations linter, the SecretRef follow-ups, the release-notes template with its enforced subsections, the Odoo subscription addon and the trial endpoint that now reaches it — they all aim at the same release, and the cadence feels like a release rather than a long string of fixes. The subscriptions stack is close to complete on the structural pieces: the trial path is reachable end-to-end, the paid-purchase model on sale.order is wired up, the licence states transition the way they should. What's left to add is real customers on the other side of it.
The other thing the weekend made plain is something we've been claiming on the homepage since day one: Odoo's extensibility is the actual feature. The same addon mechanism that makes Odoo a compelling back-end for the line-of-business customer — write a module, drop it in, ship — is what let us build our own subscription engine in a weekend. The pitch stops being a marketing line once the surface we've extended is our own commerce. Monday is when we find out whether anything else is hiding.