STNDRD
STNDRD is a live fitness subscription platform associated with Chris Bumstead. The mobile app is the primary product; the web platform carries marketing and onboarding, a custom Stripe checkout, subscription management, member and admin experiences, campaign launches, community access, referrals, attribution, and lifecycle email. It has been in market for over two years and serves tens of thousands of active customers, including more than 10,000 active web subscribers.
I led web engineering: the architecture, the billing and identity systems, and the two engineers building on top of them.
At a glance
- Role: Web engineering lead; core billing, identity, migration, and integration architecture; day-to-day leadership of two engineers.
- Stage: Live for more than two years and serving tens of thousands of active customers, including 10K+ active web subscribers.
- Core constraint: Keep one entitlement model across Stripe web billing, mobile app-store billing, legacy identities, and a new app launch.
- Strongest evidence: Staged migration of thousands of legacy records, cross-platform entitlement continuity, and support tooling for sensitive identity changes.
Context
Selling the same membership on the web and in app stores means running two billing systems for one customer. Stripe handles web subscriptions; the app stores handle mobile. Nothing about that arrangement guarantees the two agree on whether a given person has paid.
A new app launch then raised the stakes. Thousands of legacy members, distributed globally, had accounts, memberships, and subscriptions spread across a legacy data export, Stripe, RevenueCat, and Firebase. Some had duplicate customer records. Others held subscriptions to products that no longer existed. All of them expected to open the new app and still have what they had already bought.
A wrong write during that transition would not surface as a bug report. It would surface as a paying member locked out of something they were still being billed for.
My role
I owned web architecture, standards, and release decisions, and led two engineers day to day — breaking requirements into owned work, reviewing pull requests, mentoring them through third-party integrations, and coordinating with the client and the mobile team.
I first-authored the core subscription platform: the billing API, the original checkout, the Stripe, RevenueCat, and Firebase integration, the migration tooling, and the foundation of the standalone checkout application. Most of what grew on top of that — plan switching, promotions, campaigns, referrals, dashboard features — was built by the team within those boundaries.
Architecture
A Next.js marketing and onboarding site routes members into a separate Next.js application that owns checkout, authentication, and the member dashboard. Firebase Authentication and server sessions guard the account surfaces. A billing and integration API runs on Firebase Cloud Functions with Express, and Firestore holds user projections and identity mappings.
Two decisions shape everything else.
Entitlement is its own layer. Rather than letting Stripe and the app stores each answer "has this person paid?" on their own terms, RevenueCat sits above both as the entitlement authority. Stripe stays the web billing system, and both web and mobile ask RevenueCat about access. Lifecycle events from both sides flow into the Cloud Functions API, which coordinates subscription state, community access, attribution events, and lifecycle email from one place.
Checkout does not live in the marketing site. Marketing pages change constantly — campaigns, landing pages, copy. Checkout and account management cannot. Moving them into a separately deployable application behind a permanent redirect strategy meant the highest-risk paths stopped inheriting the release cadence of the lowest-risk ones. Mobile later reused that same checkout through a WebView bridge, inside the billing and identity boundaries already established.
Stack
Web — Next.js · React · TypeScript
Backend — Firebase Cloud Functions · Express · TypeScript · Firestore
Billing and entitlement — Stripe · RevenueCat
Integrations — Circle.so · Klaviyo · React Email · Meta Conversions API · ReferralHero
Engineering challenges
Migrating thousands of live members into a new system
The problem. The new app launch required moving thousands of globally distributed legacy users across with their account, membership, and subscription relationships intact — out of a CSV export, reconciled against Stripe, RevenueCat, and Firebase.
Why it was hard. Every row in that export was a live paying relationship. A direct import would have been quick and unsafe: the export held duplicate customer identities, subscriptions attached to several historical products, and records missing fields the new system required. Writing straight into live identity and entitlement stores means finding the bad rows after they have already taken effect.
The decision. Separate validation from writes completely. Nothing touches a live store until a full, approved dataset exists.
Implementation. A staged TypeScript pipeline parsed and validated every record with Zod, reconciled identities across the legacy export, Stripe, and RevenueCat, mapped historical products onto the correct RevenueCat entitlements, and checked uniqueness to block duplicate identifiers before anything was created. That produced a validated dataset as its own reviewable artifact. Only then were approved records written into Firebase and the RevenueCat mappings populated.
Outcome. The new system received migrated user, membership, and subscription records rather than a bare user table with entitlement left to reconstruct afterwards. Because validation was a separate stage, malformed and conflicting records were caught before they could reach a live account.
The same member on two billing platforms
The problem. Someone could have bought on mobile years earlier, then subscribed again through web checkout. Two identities, two billing systems, one person who expects one membership.
Why it was hard. Identity was the fragile part, not billing. Historical mobile members were keyed by an identifier from the legacy system, while web checkout created customers by email. Case differences alone could split one person in two, and a split identity means either duplicate charges or lost access — both of which read to the member as the platform being broken.
The decision. Make the join explicit and store it, rather than hoping two systems would independently agree.
Implementation. A lowercase-email-to-legacy-identifier store with a resolver in front of it, so a member arriving through web checkout resolves to their existing historical identity instead of a new one. Customer lookup on the Stripe side cut down on duplicate customer creation. Legacy products were mapped into the correct RevenueCat entitlements, so a subscription bought under a retired product still granted the right access. I also worked with the mobile developers on recognizing web-originated subscribers, so entitlement checks resolved the same way on both platforms.
Outcome. A returning legacy member can sign in or register with the email they always used and recover the membership already attached to it, instead of repurchasing or rebuilding their history.
Identity changes that could not be automated
The problem. Some members no longer had access to the historical email their membership was attached to, which put them out of reach of the recovery path above.
Why it was hard. Changing the email on an account means changing identity across authentication, billing, and entitlement at once. Accepting a new address from whoever happens to be holding the session is an account-takeover path, so this could not be self-service.
The decision. Keep a person in the loop, but give them a real tool instead of manual database edits.
Implementation. An authenticated support dashboard that migrates a subscription and moves a member's identity from an old email to a new one, reconciling the authentication and subscription records across the platforms involved.
Outcome. Support resolves identity and subscription problems directly, and the sensitive path stays behind staff authentication rather than being driven by user input.
Community access without a second login
The problem. A special program launch needed existing members inside a Circle.so community. Circle can host its own accounts, which would have meant a second identity system, a second password, and no link to whether the member was still subscribed.
Why it was hard. Community access is an entitlement, and entitlements change. A disconnected community login keeps working after a subscription lapses, and there is no clean way to reconcile the two populations after the fact.
The decision. Treat Circle as a consumer of the identity and entitlement systems that already existed rather than a parallel one.
Implementation. A custom OAuth bridge on Firebase Cloud Functions that reuses the member's existing authenticated Firebase identity to move eligible members into Circle onboarding. Subscription lifecycle events then grant or remove community access as entitlement state changes.
Outcome. Members entered the program with the account they already had, and community access stayed inside the subscription infrastructure. That flow is still part of the live platform.
Outcome
The platform is live and has been in market for over two years, serving tens of thousands of active customers, including more than 10,000 active web subscribers. Web billing, cross-platform entitlement, community access, attribution, and lifecycle communication still run through the architecture described here.
The migration moved legacy members into the new system with their memberships and subscription state intact, and the identity recovery and support paths built around it are what let existing members continue instead of starting over.
What I'm most satisfied with: two engineers extended this platform across checkout, plan switching, promotions, campaigns, referrals, and dashboard work without the architecture fragmenting underneath them.