Skip to main content

Signup Orchestration — Design Reference

Status: Core flow, payment model, and the Stripe ownership question are all settled. Two real strategic questions remain open (Section 6). Not yet built. Purpose: a single, durable reference for the cross-service signup design worked through across keystone-spike-build-log.md (Entries 12–13) and keystone-build-log.md (Entries 1–2) — those entries are the chronological record of how this was decided; this document is the settled what, kept current as the design evolves.


1. The overall flow​

Payment is collected before any provisioning happens — nothing gets created until Stripe confirms the charge succeeded. This is a deliberate, settled decision (Section 2), not an oversight: the original sketch of this flow had the form submission itself trigger everything, which risked real orphaned accounts if someone abandoned checkout. The corrected version starts the whole sequence from Stripe's own webhook instead.

Why Command Center orchestrates, not Keystone or RoundTrip: signup's primary decision is commercial (which product, which tier, what price) — credential creation is secondary to that. Command Center already owns the commercial data (Tenant, Subscription); Keystone deliberately owns nothing beyond credentials, and RoundTrip is just one of several products this flow can lead to.

Why the Keystone call happens before Tenant/Subscription creation, not after: the person's plaintext password should exist in memory for the shortest possible window — one request to Keystone — rather than being held anywhere in Command Center's own process while other steps run.

A real, honest UX tradeoff worth naming: the person now waits briefly on a "finishing setup" screen after Stripe redirects them back, rather than being instantly signed in the moment they submit payment — since the real orchestration only starts once the webhook arrives, asynchronous relative to the checkout redirect completing.


2. Payment model — card required, decided with real data​

Decided: card required at signup, not deferred to end-of-trial. This reopens a prior working assumption (the original marketing promise of "no credit card needed") and needs the marketing copy on traxsgroup.com/roundtrips.app updated to match — not yet done.

Real research behind this, not just instinct (searched 2026-09-12): two independent, credible primary sources agree on the direction. ChartMogul's 2026 study of 200 real SaaS products found card-required trials convert at 30–60% versus 9–25% for no-card trials. First Page Sage, tracking 86 real B2B SaaS clients independently, found nearly the same split (48.8% vs. 18.2%). More useful than the raw percentage: ChartMogul's own per-1,000-visitor model shows no-card trials producing 45 signups → 3.6 paying customers, versus card-required trials producing 35 signups → 10.5 paying customers — roughly 3x more total paying customers, despite fewer people trying it at all.

The real, load-bearing caveat, directly from ChartMogul's own report: this advantage holds "unless the product warrants the friction of adding a credit card." RoundTrip is a considered B2B field-service purchase, not an impulse-try consumer app — the profile where this pattern is expected to hold, not backfire. (Also worth remembering going forward, independent of this decision: activation rate — whether someone actually experiences real value during the trial — reportedly drives more of the eventual conversion outcome than the card requirement itself.)

No current live-traffic risk in making this change: neither traxsgroup.com nor roundtrips.app currently has a working signup flow generating real trial customers, so there's no existing user base to disrupt or field complaints from — this is a clean, low-risk moment to make the switch.


3. Stripe moves to Command Center — a real, deliberate reversal​

This reopens command-center-database.md's own earlier decision to keep Stripe processing local to each product (RoundTrip's own webhook handler), deferred centralization until "Waypoint actually has real billing and there's a real second case to generalize from." The new, multi-product signup flow is exactly that second case — RoundTrip's existing Stripe integration has no relationship at all to a customer whose first product is Waypoint.

Real reasons this is the right moment, not just a theoretically cleaner design:

  • command-center-brd.md §3 already names "one Stripe customer per Traxs tenant" as a stated objective — per-product Stripe integrations would create a separate Stripe customer per product instead, directly working against that goal.
  • The bundle discount logic (10%/20%, symmetric regardless of adoption order) is inherently cross-product — no single product's isolated Stripe integration has the visibility to compute it correctly.
  • RoundTrip has zero real paying customers today — the same "cheapest this migration will ever be" window that's justified every other RoundTrip-side Keystone change in this project applies here too.

Not yet designed: the actual migration of RoundTrip's existing, real, working Stripe webhook handler (services-stripe.md) into Command Center — API keys, Price IDs, the webhook endpoint itself, and the real event-handling logic (customer.subscription.created, invoice.payment_failed, etc.) all need a genuine migration plan, not just a "move it" decision. Pete has confirmed comfort with fully rebuilding this to fit the new shape rather than needing to preserve every implementation detail.


4. Retry mechanics — SignupAttempt​

"No half-provisioned state" was a hard requirement (Entry 13), which makes a real distributed-transaction problem out of a three-step, three-service sequence. The resolution: no rollback, no saga — every step is safely repeatable, and a failure just means retrying the whole thing until it completes.

SignupAttempt
SignupAttemptId Guid PK
IdempotencyKey string, unique (client-generated, persisted to sessionStorage)
Email string
ProductSlug string
TierName string
TenantId Guid?, nullable
KeystoneUserId Guid?, nullable
TenantUserProvisioned bool
CreatedUtc datetime
CompletedUtc datetime?, nullable

The three nullable/boolean fields are the retry logic — nothing more sophisticated than "check the field, skip the step if it's already set":

A 409 Conflict from either Keystone's registration endpoint or RoundTrip's provisioning endpoint is treated as "this step already succeeded," not a real failure — both endpoints are safe to call twice with the same input.

Resolved (2026-09, confirmed by Command Center's real, tested integration — command-center-keystone-integration-summary.md): the gap originally named here applied to /account/register's bare 409, which genuinely carries no body. Once /v1/accounts/provision was built (keystone-build-log.md Entry 4) specifically for this call, the problem no longer exists — every call, first attempt or any retry, returns a real 200 with the actual UserId in the body (wasAlreadyRegistered: true on a repeat). The check is a live existence lookup by email on every call, not an attempt counter, so this holds for any number of retries, not just a second one. A related, lower-probability gap remains, worth naming plainly rather than re-hiding: the check-then-create sequence is technically racy under true concurrency — two calls arriving at the exact same instant could both pass the existence check before either inserts. Accepted as out of scope, since Command Center's retries are sequential from one orchestrator, never concurrent.

Sync only, no background jobs: a few quick retries with short backoff, then an honest "please try again" message if that's not enough. Command Center has no job-queue infrastructure today, and building one now would be engineering against a failure mode with zero real evidence behind it yet.


5. RoundTrip's provisioning step — and a real consequence worth seeing clearly​

RoundTrip's own real, built endpoint (command-center-tenant-admin-provisioning.md) handles the actual TenantAdmin creation — extracted from the existing ProvisionTenantJob Hangfire job into a shared service, now also callable synchronously. Genuinely proven idempotent via a real mutation-testing pass, not just assumed.

The real consequence, worth stating plainly rather than leaving implicit: since RoundTrip hasn't migrated to Keystone-based authentication yet, a person whose first product is RoundTrip ends up with two separate credentials — a real, working Keystone password (which grants nothing on RoundTrip today), and a separate Entra identity with its own temp password and invitation email, which is what actually gets them into RoundTrip. This is reasoned as a consequence of RoundTrip being deliberately last in keystone-framing.md §8's migration sequence, not a flaw in this design — but it's a real product decision, not a technical footnote, and needs explicit sign-off before RoundTrip is offered as a signup option (Section 6).


6. Which document says what​

This document explains the design. For anything else:

DocumentWhat it's for
keystone-spike-build-log.md / keystone-build-log.mdThe chronological record of how each decision here was reached — real dead ends included
command-center-tenant-admin-provisioning.mdRoundTrip's own real, as-built provisioning endpoint — the authoritative source for its exact request/response shape
services-stripe.mdRoundTrip's current, real Stripe integration — the starting point for the migration into Command Center (Section 3), not yet designed
keystone-prd.md §3.11Currently stale on the related entitlement-at-issuance question (Entry 13 corrected the design; the PRD itself hasn't been rewritten yet)
This documentThe settled shape of the signup flow itself, kept current as it evolves

7. Open — real decisions, not yet made​

  1. Whether to disable RoundTrip's registration now, as a deliberate, reversible move to stop new RoundTrip signups while its real Keystone migration happens on its own timeline, free of new-customer pressure.
  2. Whether Waypoint, not RoundTrip, should be the first product this flow actually offers — real, current context (Waypoint's auth was built as bare-minimum scaffolding, not something considered a keeper) strengthens keystone-framing.md §8's already-decided sequencing (Waypoint before RoundTrip), but this hasn't been confirmed against Waypoint's actual present state in detail.
  3. The actual Stripe migration plan (Section 3) — moving RoundTrip's real, working integration into Command Center needs its own real design pass, not yet started.

Items 1–2 are explicitly flagged as needing real thought — see keystone-build-log.md Entry 1 for the discussion that raised them.