signup: flag-gated paid account option with payment in the form #149
Etiketter
Inga etiketter
area/backups
area/ci
area/control-panel
area/identity
area/infra
area/observability
area/payments
area/security
area/storage
area/web
blocked
needs-info
needs-triage
ready-for-implementation
type
bug
type
chore
type
docs
type
epic
type
feature
type
task
wontfix
Ingen milstolpe
Inget projekt
Inga tilldelade
1 deltagare
Notiser
Förfallodatum
Inget förfallodatum satt.
Beroenden
Inga beroenden satta
Referens
bitborg/bitborg-web#149
Läser in…
Hänvisa till i nytt ärende
Ingen beskrivning angiven.
Ta bort grenen "%!s()"
Borttagning av en gren är permanent. Även om den borttagna grenen kan fortsätta existera en kort tid innan den faktiskt tas bort, kan det INTE ångras i de flesta fall. Vill du fortsätta?
Offer a paid account at sign-up, behind the payments flag, taking payment as
part of the form.
Epic: bitborg-docs#5
Sibling of #145, not a duplicate: that issue is the upgrade path for an account
that already exists; this is the choice made before the account exists.
Behaviour
The two options are mutually exclusive, driven by
paymentsEnabled()(
src/lib/payments-access.ts, off by default):PAYMENTS_ENABLEDWhen the paid option is selected, payment is taken as part of sign-up rather
than deferred to a later upgrade.
Why trial disappears when payment arrives
The trial exists because there is nothing to buy yet (ADR 0036). Once there is,
offering both asks people to choose between paying and not paying for the same
capabilities. The existing
TRIAL_OPENswitch stays the mechanism for retiringthe trial; this issue is about the two flags not contradicting each other — a
state where both a trial and a paid option render should not be reachable.
Decide explicitly what happens to accounts already on the trial when the flag
flips. They convert or downgrade per ADR 0036, and that is operational work, but
the sign-up form must not be the place it is discovered.
Prerequisites already in place
paymentsEnabled()— flag, off by default, inverted polarity so a missing ormistyped variable leaves payment surfaces dark.
signup.accountType.paidand.paid.description— copy exists in bothlanguages, currently unused. The trial and paid descriptions are presently
identical; the paid one should say what is actually different (that it does not
expire).
The trap to avoid
requiresCountry()insrc/lib/eligibility.tsdecides which account types mustdeclare a country of residence, and
PAYING_ACCOUNT_TYPESis deliberatelyempty today because nothing on the form is a purchase.
Adding the paid radio without adding
"paid"to that set means country issilently never collected for the one type that needs it — for VAT and banking.
Nothing fails; it simply stops asking, and
country_code/regionstay NULL.Worth a test binding the paid option to that set so the two cannot drift apart. A
comment did not prevent the gap being left in the first place.
Out of scope
Billing state, the provider integration and subscription lifecycle all belong to
the separate payment service. The portal's job is the choice, the hand-off, and
reading status for display — never computing entitlements from it. Tier changes
flow payment → Kanidm group → reconciler, never writing to the Git application
directly.
Sequencing
Deferred until after the sign-up form refactor (#146) lands, on its own branch.
Board grooming 2026-10-03: dropped
blocked. The stated dependency, the sign-up form refactor (#146), merged on 2026-08-02. The option stays dark behindPAYMENTS_ENABLED, so it can be built now and turned on with checkout.