Production Stripe Billing
Stop asking Claude to reinvent
A billing specification your coding agent reads, then implements in your repository.
Stripe handles payments. Udria handles the twenty decisions about how billing state maps onto your users, organisations and entitlements — specified, tested, and adapted to the codebase you already have.
Delivered by 31 October 2026. If Production Stripe Billing is not ready by then, your reservation automatically expires and you will not be charged.
$0 is charged when you reserve. Stripe saves your card and we charge $199 once, only when the product is ready — after emailing you 3–5 days beforehand. Cancel any time before then.
claude code
What you actually receive
We ship the specification.
Production Stripe Billing is not a boilerplate, not an npm package and not a hosted service. It is the accumulated set of decisions, edge cases and tests that a correct billing implementation needs — in a form a coding agent can apply to a codebase that already exists.
The artifact
A versioned capability package: implementation instructions, architecture, specification, acceptance criteria and runnable TypeScript tests. Human-readable, written to be read by an agent. No compiled library, no runtime dependency on Udria.
How it gets in
Download the package and drop the folder at .udria/production-stripe-billing/in your repository, then tell Claude Code or Codex to install it. No CLI, nothing to trust at runtime, nothing that phones home.
What your agent produces
Code in your repository, in your conventions, against your schema — migrations, a webhook route, an entitlement resolver, seat accounting and billing UI. You own all of it.
your-app/
└─ .udria/
└─ production-stripe-billing/
├─ ARTIFACT.md master implementation instructions
├─ SKILL.md Claude Code entry point
├─ AGENTS.md Codex entry point
├─ architecture/
│ ├─ overview.md
│ ├─ compatibility.md supported stacks and rules
│ └─ decisions/ architecture decision records
├─ spec/
│ ├─ billing-domain.md
│ ├─ data-model.md
│ ├─ webhooks.md
│ ├─ idempotency-and-ordering.md
│ ├─ subscription-lifecycle.md
│ ├─ entitlements.md
│ ├─ plan-changes.md upgrades, downgrades, cancellation
│ ├─ customer-portal.md
│ └─ security.md
├─ implementation/
│ ├─ sequence.md the order your agent works in
│ └─ verification.md
├─ acceptance/
│ ├─ criteria.md
│ └─ tests/ runnable TypeScript acceptance tests
└─ troubleshooting.mdWhat the package covers
- Master implementation instructions
- Architecture and decision records
- Supported-stack and compatibility rules
- Stripe billing domain specification
- Database and data model
- Webhook behaviour
- Idempotency and event-ordering rules
- Subscription lifecycle
- Entitlements
- Upgrades, downgrades and cancellation
- Customer portal
- Security requirements
- Implementation sequence for Claude Code and Codex
- Acceptance criteria
- Runnable TypeScript acceptance tests
- Troubleshooting and verification
Runnable TypeScript acceptance tests, not just prose. Your agent wires them to your schema and runs them before it reports the work as finished.
Give the package to Claude Code or Codex. It analyses your existing repository, adapts the implementation to it, builds the capability, and verifies the result against Udria's acceptance tests.
Why not just Stripe
Stripe has solved payments.
If you have read the Stripe docs you already know most of billing is handled. We are not going to pretend otherwise, and any page that does should lose your trust.
Stripe already gives you
- Checkout and embeddable pricing tables
- Billing subscriptions, with scheduled upgrades and downgrades
- The hosted Customer Portal
- Webhook signature verification
- Dunning with Smart Retries
- Test clocks
- The Entitlements API
What is left is the seam between Stripe and your application — roughly twenty decisions about how billing state maps onto your domain model. An agent makes those decisions differently every time you ask.
Enforcing entitlements at runtime
The Entitlements API tells you what a customer is entitled to. It does not gate a request. You cannot call Stripe on every authorisation check, so every check needs a local answer that is still correct milliseconds after a webhook lands.
Your tenancy model
Stripe knows customers and subscriptions. It does not know your organisations table, your roles, or your RLS policies. Turning a subscription into “which member of which org may do what” is application work, and it is different in every application.
Seat accounting
Quantity on a subscription item is a number. Deciding what happens when the sixth person is invited to a five-seat plan — block, queue, or bill the overage — is a product decision enforced against your data, not Stripe's.
Webhook reconciliation
Stripe verifies the signature. Idempotency, out-of-order delivery, replay against your schema, and what you do when Stripe and your database disagree are all yours. This is where the expensive bugs live.
Degrading gracefully
A card fails on day 40 and Smart Retries works the dunning. What does your product actually do on day 41, day 47 and day 50? Most teams never decide this deliberately — the behaviour is whatever the code happened to do.
Testing the states that break
Test clocks exist. Suites that use them mostly don't. The states that lose customers are precisely the ones nobody wrote a test for, because reproducing them by hand takes a fortnight.
Udria specifies all twenty, with the edge cases written down and a test for each one, so your agent makes the same decisions a team that has shipped billing three times would make.
Claude writes the code.
“Add Stripe subscriptions.”
Every answer gets rediscovered, slightly differently, in every project — and you only find out which answer you got in production.
With Udria
Install production-stripe-billing from .udria/
- architecture specified
- edge cases defined
- implementation workflow
- acceptance tests included
- repository-aware adaptation
- verification before done
The agent starts from a known-good architecture instead of a blank page.
Don’t spend AI tokens rediscovering known software architecture.
The architecture
Billing as a domain, not a payment button.
Your application keeps its own billing state rather than asking Stripe a question every time it decides whether a user can do something. Stripe events reconcile into that state; entitlements read from it.
- Migrations for subscriptions, the event ledger and entitlements
- A webhook route with idempotent, replay-safe ingestion
- An entitlement resolver your authorisation checks call
- Seat accounting against your organisation membership
- Dunning and degradation states
- Pricing page, billing settings and portal hand-off in your design system
The result
A billing surface that looks like the rest of your product.
Plan, seats, renewal date, payment method and invoices — generated with your existing design system, inside your existing settings area.
Pro$49 / month
- 4 of 5 used
- 12 Sep 2026
- Visa ·· 4242
- 12 Aug 2026$196.00
- 12 Jul 2026$196.00
- 12 Jun 2026$147.00
Stripe stays yours
Your Stripe account, your keys, your payouts. Udria never sits between you and your customers’ money, and takes no percentage of it.
Your application already exists.
Most SaaS boilerplates are excellent when you’re starting from an empty repository. But what happens when you’ve already built your product?
Production Stripe Billing is designed to be installed into the application you already have. Your agent reads your users, organisations, database conventions and design system first, then adapts the billing implementation around them.
| Traditional boilerplate | Udria |
|---|---|
| Start from our repository | Keep your repository |
| Adapt your app to the template | Adapt the capability to your app |
| We ship application code | We ship the specification, your agent writes the code |
| Fixed architecture | Repository-aware integration |
| You integrate | Your coding agent integrates |
| Tests vary | Acceptance tests included and run before done |
Look inside
This is the actual package.
Everything else on this page showing a product is labelled as a preview, because the product is not finished. These two files are not previews. They are the webhook specification and one of the acceptance test files as they stand today.
For a knowledge product the sample is the proof, so judge Udria on this rather than on the marketing above it. If it does not tell you something you did not already know, do not reserve.
.udria/production-stripe-billing/spec/webhooks.md
# Webhook ingestion
> Excerpt from `.udria/production-stripe-billing/spec/webhooks.md`.
Stripe verifies the signature. Everything after that is yours, and it is where
the bugs live. This section specifies ingestion; entitlement resolution is
specified in `entitlements.md`, and event ordering in
`idempotency-and-ordering.md`.
## Invariants
The agent MUST satisfy all five before this section is considered implemented.
1. **Signature.** Reject any request whose signature fails `constructEvent`,
with 400, before the body is parsed or persisted.
2. **Idempotency.** Processing the same `event.id` twice MUST leave the
database byte-identical to processing it once.
3. **Ordering.** Stripe does not guarantee delivery order. Applying an older
event after a newer one MUST NOT move billing state backwards.
4. **Durability.** An event that has been acknowledged (2xx) MUST be
recoverable. An event that failed MUST NOT be acknowledged.
5. **Reconciliation.** Where local state and Stripe disagree, Stripe wins, and
the disagreement is recorded rather than silently overwritten.
## Ledger table
Ingestion is a two-phase write: record the event, then apply it. The ledger is
the idempotency key and the audit trail.
```sql
create table billing_events (
id text primary key, -- Stripe's event.id
type text not null,
api_version text,
payload jsonb not null,
received_at timestamptz not null default now(),
processed_at timestamptz,
failed_at timestamptz,
failure_count integer not null default 0,
last_error text
);
```
`id` is Stripe's own event id, and it is the primary key. That single choice is
what makes replay safe: the insert is the lock.
```
insert into billing_events (id, type, payload)
values ($1, $2, $3)
on conflict (id) do nothing
returning id
```
Zero rows returned means this event has been seen. Acknowledge with 200 and do
no further work.
## Ordering
Every subscription-shaped event carries the subscription object it describes.
Compare before applying:
- Prefer `created` on the **event**, not `current_period_start` on the object.
- Persist `last_event_at` on the local subscription row.
- If `event.created <= last_event_at`, discard the event as stale. Record the
discard; do not error.
This is the rule that prevents the common failure where a retried
`customer.subscription.updated` from the cancellation flow lands after the
reactivation that followed it, and quietly revokes a paying customer's access.
## Failure handling
Return a non-2xx **only** when a retry could succeed — a database outage, a
timeout. Return 2xx and record `failed_at` for anything a retry cannot fix
(an event for a customer that does not exist locally, a malformed payload).
Retrying a permanently broken event for three days produces noise, not repair.
`failure_count >= 5` MUST raise an operational alert. Deciding what "alert"
means is the host application's choice; the specification only requires that
silence is not an option.
## Events in scope for V1
| Event | Effect |
| --- | --- |
| `checkout.session.completed` | Link the Stripe customer to the local account |
| `customer.subscription.created` | Create local subscription, grant entitlements |
| `customer.subscription.updated` | Re-resolve plan, period, cancellation and seat allowance |
| `customer.subscription.deleted` | End entitlements at period end, not immediately |
| `invoice.paid` | Clear any dunning state, extend the period |
| `invoice.payment_failed` | Enter dunning; see `subscription-lifecycle.md` |
Events outside this table MUST be recorded in the ledger and ignored. Handling
an event the specification does not cover is out of scope, not a bonus..udria/production-stripe-billing/acceptance/tests/webhook-idempotency.spec.ts
// Excerpt from .udria/production-stripe-billing/acceptance/tests/
// webhook-idempotency.spec.ts
//
// Ships as a real file. Your agent wires `harness` to your schema, then runs
// the suite before it reports the implementation as finished.
import { afterEach, beforeEach, describe, expect, it } from "vitest";
import { harness, type Harness } from "./_harness";
describe("webhook ingestion", () => {
let h: Harness;
beforeEach(async () => {
h = await harness();
await h.seed.organisation({ id: "org_1", plan: "free" });
await h.seed.stripeCustomer({ id: "cus_1", organisationId: "org_1" });
});
afterEach(() => h.reset());
it("grants entitlement on a new subscription", async () => {
await h.post(
h.event("customer.subscription.created", {
id: "sub_1",
customer: "cus_1",
status: "active",
items: [{ price: h.prices.pro, quantity: 3 }],
}),
);
expect(await h.entitlements("org_1")).toMatchObject({ plan: "pro", seats: 3 });
});
it("has no duplicate effect when the same event is delivered twice", async () => {
const event = h.event("customer.subscription.created", {
id: "sub_1",
customer: "cus_1",
status: "active",
items: [{ price: h.prices.pro, quantity: 3 }],
});
await h.post(event);
const afterFirst = await h.snapshot();
const second = await h.post(event);
expect(second.status).toBe(200);
expect(await h.snapshot()).toEqual(afterFirst);
expect(await h.rows("billing_events")).toHaveLength(1);
});
it("rejects an invalid signature before touching the database", async () => {
const res = await h.postRaw(
h.event("customer.subscription.created", { id: "sub_1", customer: "cus_1" }),
{ signature: "t=1,v1=deadbeef" },
);
expect(res.status).toBe(400);
expect(await h.rows("billing_events")).toHaveLength(0);
});
it("does not move state backwards when events arrive out of order", async () => {
// Reality: the cancellation is retried and lands after the reactivation.
const cancelled = h.event(
"customer.subscription.updated",
{ id: "sub_1", customer: "cus_1", status: "canceled" },
{ created: 1_000 },
);
const reactivated = h.event(
"customer.subscription.updated",
{ id: "sub_1", customer: "cus_1", status: "active", items: [{ price: h.prices.pro }] },
{ created: 2_000 },
);
await h.post(reactivated);
await h.post(cancelled);
expect(await h.entitlements("org_1")).toMatchObject({ plan: "pro", active: true });
});
it("keeps access until period end when a subscription is cancelled", async () => {
await h.post(
h.event("customer.subscription.updated", {
id: "sub_1",
customer: "cus_1",
status: "active",
cancel_at_period_end: true,
current_period_end: h.daysFromNow(12),
}),
);
expect(await h.entitlements("org_1")).toMatchObject({ plan: "pro", active: true });
await h.clock.advance({ days: 13 });
expect(await h.entitlements("org_1")).toMatchObject({ plan: "free", active: false });
});
it("acknowledges an unrecoverable event instead of retrying it forever", async () => {
const res = await h.post(
h.event("customer.subscription.created", { id: "sub_9", customer: "cus_unknown" }),
);
expect(res.status).toBe(200);
expect(await h.row("billing_events", "failed_at")).not.toBeNull();
});
});Verification
“It compiled” isn’t the same as “billing works.”
The package doesn’t only tell your agent what to build. It tells it how to prove the implementation behaves correctly, and it does not consider the work finished until the suite runs green against your database.
acceptance suite
How it works
Three steps, inside your own repository.
Install
Drop the Udria package into .udria/ and tell Claude Code or Codex to install it.
Adapt
The agent reads your repository and maps the billing architecture onto your actual tables, roles and design system.
Verify
The included acceptance tests are wired to your schema and executed before the implementation counts as complete.
The agent does the labour. Udria provides the accumulated product and engineering knowledge.
Pricing
Founding release
One payment. No subscription, no revenue share, no marketplace membership.
$199
Charged once, on delivery. $299 at public release.
$0 today
Delivered by 31 October 2026. If Production Stripe Billing is not ready by then, your reservation automatically expires and you will not be charged.
- $0 is charged when you reserve.
- You can cancel at any time before delivery.
- We email you 3–5 days before we charge anything.
- The $199 charge happens only when Production Stripe Billing is ready for delivery.
- Every update to Production Stripe Billing v1.x
- Founding-customer status and pricing
- Direct installation support if your coding agent hits a problem with the Udria package
About copying
A specification is the most copyable thing there is. A licence line saying “one commercial project per purchase” would not change that, and we aren’t going to pretend it does. What a forwarded copy doesn’t come with is the v1.x updates, your founding price, or someone to ask when your agent gets stuck part-way through the install. That is what you are buying alongside the files.
Before you reserve — is this your stack?
V1 supports
- Next.js (App or Pages Router)
- TypeScript
- PostgreSQL or Supabase
- Claude Code or Codex
- An application that already exists
If it isn’t your stack, don’t reserve — tell us what you use instead. That list decides what we build second.
The founder
Why I’m building this
I’ve spent years building software products, and coding agents have changed the economics of making them. But something odd happens every time.
We ask Claude or Codex to build a common capability, and the model rediscovers years of accumulated knowledge from scratch — differently each time. Billing. Permissions. CRMs. Scheduling. Document signing. Approval workflows.
The code is getting cheaper. Knowing exactly what should be built is getting more valuable.
Production Stripe Billing is the first experiment. If it works, we build the same kind of capability for the next recurring problem.
— Roman Grigoriev
Udria is operated by Longrad Ltd. Questions go to hello@udria.co.
The bigger idea
Billing is only the beginning.
Our thesis is simple:
Coding agents shouldn’t reinvent solved software capabilities.
We’re starting with billing. If developers want it, we build the next one. Search Udria before you build it from scratch.
- Available first
Production Stripe Billing
Why not just use Stripe Billing, the Customer Portal and the Entitlements API?
What exactly do I download?
.udria/production-stripe-billing/ in your own repository. There is no CLI, no runtime dependency and nothing that phones home.Can I see a sample before I reserve?
Who writes the code — Udria or my agent?
Is this a SaaS boilerplate?
When will I get it, and what if you never ship?
When am I charged?
Can I cancel?
What support do founding customers get?
Is my card stored by Udria?
What stack does V1 support?
Do you host my billing or take a percentage?
Do I need to know Stripe?
Does it include Stripe fees or my AI usage?
Stop asking Claude to reinvent Stripe billing.
Install the specified version instead.
$0 today. Delivered by 31 October 2026. If Production Stripe Billing is not ready by then, your reservation automatically expires and you will not be charged.

