Skip to content

MEMBERS-DUES-001 — Build server-authoritative annual membership checkout and renewal #114

Description

@daliu

Officer impact: The website becomes the authoritative place to see whether a person has paid membership for the current annual term; officers stop treating a role toggle, email match, redirect, or spreadsheet checkbox as proof of membership.

Officer documentation: Implementation children must update OFFICER_START_HERE.md, docs/officers/EVENTS_SHOP_MEMBERS.md, docs/officers/SYSTEM_MAPS.md and its text alternative, and the glossary for any unavoidable payment/membership terms.

Deployment evidence: None at issue creation. Each child must separately prove source changed, tests passed, merged, website published, runmprc.com verified, Firebase deployed, payment/provider configuration completed, and production behavior verified. Until then this capability is NOT AVAILABLE YET.

Product decision

  • Full membership records live on the MPRC website/backend.
  • Membership is renewed annually and current access depends on approved payment evidence for the applicable term.
  • Historical Google Forms/Sheets import is deferred; it is not part of this tracker unless BACKEND-001 — Audit the Firebase cutover and approve legacy data disposition #113 later approves a separate migration.
  • A membership record may exist before the person creates a website account. Account association is a separate, audited action under MEMBERS-ADMIN-001.
  • The first online dues flow is a one-time annual payment, not an automatically recurring subscription. Any future subscription design requires a separate owner decision and issue.

Problem

Current source represents membership only as an Auth custom claim and a mirrored members/{uid}.role value (unverified, member, or admin). It has no membership term, dues/payment evidence, renewal history, plan/household state, expiry, grace, refund/dispute behavior, source/provenance, or account-independent membership record. Direct role changes can therefore outlive dues and cannot explain why access was granted.

Stripe is not currently live. Existing payment work covers race and merchandise correctness, not annual membership entitlement.

Target invariants

  • Firestore is authoritative for club membership operations; verified Stripe state is authoritative for Stripe money.
  • A browser, redirect, email, payment screenshot, client field, spreadsheet checkbox, or Auth provider never activates membership.
  • Online activation requires a verified server-side payment transition for the exact annual term and approved plan/amount/currency.
  • An approved off-platform payment may be recorded only through a separately authorized, audited manual command. It must never fabricate Stripe state.
  • Each renewal creates a new immutable term/evidence relationship; it does not overwrite prior membership history.
  • Member custom claims and UI access are derived projections of one active membership term, not the membership source of truth.
  • Membership and payment state remain separate. Pending, failed, expired, refunded, disputed, waived/comped, and manually confirmed states are explicit.
  • A membership can be unlinked from an Auth UID until an officer completes the controlled association workflow.

Tracker scope

This is a size-L tracker, not one coding assignment. Split implementation into dependency-ordered children with one observable outcome each, normally:

  1. Annual term, membership, plan, payment-evidence reference, source/provenance, and pure transition contract with additive compatibility mapping.
  2. Idempotent annual dues command/Checkout creation using server-approved plan and amount snapshots.
  3. Verified payment-event reduction into membership activation/renewal, including duplicate, out-of-order, delayed, failed, expired, refunded, and disputed cases.
  4. Expiry/reconciliation job and derived Auth-claim/access refresh/revocation without deleting membership history.
  5. Approved manual/off-platform payment evidence command, only if owner policy permits it, kept separate from account association.
  6. Minimum member/account status projection and renewal UX after the server contract is proven.

Owner decisions required before relevant children

Do not invent:

  • calendar-year versus anniversary terms, effective/start/end dates, or grace period;
  • individual, household, non-resident, sponsored/comped, or other plan eligibility;
  • prices, early-bird windows, currency, accepted offline methods, or who may confirm them;
  • refund, dispute, chargeback, cancellation, transfer, lapse, reactivation, or hardship policy;
  • waiver/insurance evidence relationship, accounting retention, tax, or member communications.

Record approved values as versioned server configuration rather than hardcoded UI copy.

Acceptance criteria

  • Every membership has a stable non-email ID, versioned plan/term, explicit status, source/provenance, created/updated actors, and payment-evidence relationship appropriate to its source.
  • A membership may exist without an account; creating an Auth account does not activate or silently associate it.
  • Unpaid, failed, cancelled, mismatched, replayed, wrong-term, wrong-plan, wrong-amount/currency, test/live-mismatch, or client-forged payment data cannot activate membership.
  • Repeated checkout/renewal requests and duplicate/out-of-order provider events produce one coherent term and no duplicate entitlement.
  • Renewal preserves earlier terms; overlapping/duplicate household or individual records enter a review state rather than granting twice.
  • Expiry, approved refund/chargeback/dispute outcomes, suspension, and offboarding deterministically update the current entitlement projection and force safe token refresh/revocation.
  • Admin/browser clients cannot write canonical membership, payment evidence, current entitlement, or audit records directly.
  • Member pricing and protected content consume the server-derived active-membership projection rather than Auth provider or raw profile fields.
  • Tests use synthetic identities, payment fixtures/test mode, and Firebase emulators only; production projects/providers are blocked.
  • Additive migration/dry-run reports are idempotent, but no historical roster import runs until BACKEND-001 — Audit the Firebase cutover and approve legacy data disposition #113 approves a separate scope.
  • Backup-officer review proves the documented current procedure requires no terminal, source edit, secret handling, or direct Firestore mutation.

Dependencies and coordination

Explicitly out of scope

  • Bulk import of historical Sheets/Forms or data from the inaccessible Firebase project.
  • Silent account association by matching email.
  • Recurring subscriptions, automatic bank/Venmo reconciliation, or storing bank/payment credentials.
  • Race/merchandise checkout, refunds, taxes, shipping, discounts, or waiver-policy decisions.
  • Direct production writes, live Stripe objects, provider-console changes, or shared-account credentials.

Claim protocol

Creation is not a claim, and this size-L tracker must not be claimed as one implementation. Publish/assign one dependency-ready child at a time. Before work begins, the child must be assigned and have a timestamped CLAIMED by <canonical agent> at <UTC>; branch <branch> comment that is re-read; earliest valid claim wins.

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:firebaseFirebase services and dataarea:membershipMembership lifecycle and reconciliationarea:stripeStripe payments and financial lifecyclepriority:P0Launch blocker or urgent security risksize:LLarge cross-boundary issuestatus:proposedDesigned but dependencies or decisions remaintype:architectureArchitecture and contract designtype:featureProduct capabilitytype:reliabilityReliability and recovery

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions