Product case study · October 2026
I built Certinance for UK businesses to keep certificates and other records together, track expiry dates and send reminders. This case study covers the work on the app, subscription billing, testing and the October 2026 launch.
The product
Certinance is aimed at teams in construction, property, maintenance and other businesses managing compliance documents. It keeps records, dates and supporting files together, with team permissions and options for sharing selected documents.
A static frontend with service boundaries
I built the interface with Next.js, React, TypeScript and Tailwind CSS. Next.js exports the public site and application shell as static files for Firebase Hosting. The browser then resolves the signed-in user and their workspace through Firebase Authentication and Firestore. The static export keeps hosting separate from the services that handle billing, document access and reminders.
Firestore holds organisation membership, certificate records, document-version metadata and subscription state. Cloudflare Workers handle operations that need trusted server-side credentials. That includes creating Stripe sessions and authorising private document access; neither operation relies on a secret shipped in the frontend.
Workspace membership is part of the data model
A signed-in account alone is not enough to access an organisation's records. Firestore rules and the Worker authorisation layer check active workspace membership, organisation scope and role. Owners, administrators and viewers have different permissions. Write operations also check the subscription or trial entitlement and applicable usage limits.
This distinction matters when a subscription changes or a member leaves. The interface can explain why an action is unavailable, but the backend still needs to enforce that decision independently. The rules and services are tested for those boundaries as well as the normal customer workflow.
Billing state comes from Stripe, not the success page
The billing Worker creates Stripe Checkout and Customer Portal sessions after checking the workspace owner. Checkout handles the subscription purchase; the portal gives the customer a way to manage the plan. The documented launch behaviour applies upgrades immediately with proration and downgrades at renewal.
Returning from Checkout is not treated as proof of payment. Signed Stripe webhooks drive the stored subscription state. The service retrieves the subscription, checks its organisation and billing mode, then projects the result into Firestore. Event handling distinguishes applied, duplicate, stale and ignored updates, so repeated notifications do not become repeated state changes.
Keeping a PDF and its record in sync
The customer PDF implementation uses Cloudinary authenticated assets behind a Cloudflare Worker. Firestore stores the document record and immutable file-version metadata. Before an upload or download, the service checks the user, workspace and relevant entitlement. Upload completion verifies the provider response and file identity before updating the current-version pointer.
That separation is useful for replacements: a certificate can point to a newer PDF while its previous version remains identifiable. Downloads require authorisation rather than exposing a permanent public file URL. The repository also contains an R2 storage implementation; it should not be confused with the customer Cloudinary path described here.
Scheduled reminders and delivery history
The reminder service runs in a scheduled Cloudflare Worker and sends transactional email through Resend. It reads configured reminder rules, selects recipients and records delivery state. A Durable Object coordinates claims and retries so the same expiry milestone is not repeatedly sent to the same recipient.
Provider acceptance and inbox delivery are different events. The implementation uses signed provider webhooks to update the delivery history rather than presenting a successful API request as confirmed delivery. It also keeps operational delivery records separate from the notification preferences a customer can edit.
Testing across the browser and services
The 27 October acceptance record brings together unit tests, Worker tests and Firebase rules tests: 672, 38 and 110 passing respectively. Those make up the recorded 820 passes; two optional scanner integrations were skipped. TypeScript, ESLint, the production build and SEO checks were also part of the release gate.
The production acceptance work went beyond compiling the app. It exercised document saving, private PDF handling, administrator permissions, notification persistence and scheduled delivery. The release received GO status for its approved commercial scope. These are dated acceptance results, not a claim that every later release or deployment has been checked in the same way.
Development timeline
Here’s how the launch came together. October’s work covered the commercial terms, billing, expiry notifications and the final production checks.
Commercial foundations
I set up the UK business-to-business model, subscription pricing, 14-day free trial and customer data-retention policies.
Live subscription billing
I activated live Stripe billing and Checkout: Starter at £19/month, Business at £49/month and Professional at £99/month.
Self-service plan management
I added the Stripe Customer Portal so customers could change plans themselves. Upgrades apply immediately with proration; downgrades take effect at the next renewal.
Automated expiry notifications
The reminder system went live with configurable schedules, a choice of team recipients and notification history.
LiveShare publicly announced
I announced LiveShare through the company LinkedIn page. This is the announcement date, not a claim about when development was completed.
Production hardening completed
The final production checks covered authentication, billing, document handling, secure downloads, permissions, notifications, quotas and audit records.
These were the acceptance results on 27 October 2026, rather than a guarantee about every future release.
Commercially ready
The production release received GO status for the approved commercial scope.
Customer acquisition phase
After release, I shifted my attention to finding customers, business outreach and subscription revenue.
A look at the app
What I worked on