Skip to content

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

Closed

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.

Activity

  1. added
    priority:P0Launch blocker or urgent security risk
    area:membershipMembership lifecycle and reconciliation
    area:firebaseFirebase services and data
    size:LLarge cross-boundary issue
    status:proposedDesigned but dependencies or decisions remain
    area:stripeStripe payments and financial lifecycle
    on Jul 13, 2026
  2. daliu commented on Jul 22, 2026

    @daliu
    ContributorAuthor

    Dependency correction #451 is claimed from exact main 0b9960f. It makes the already-required paid-but-unlinked membership sequence representable in the pure unused authority contract while preserving link-first compatibility and fail-closed entitlement. Parent #114 remains open; no payment policy, Stripe/runtime, persistence, migration, deployment, or live behavior is in scope.

  3. daliu commented on Jul 22, 2026

    @daliu
    ContributorAuthor

    Corrective dependency #451 merged through PR #452 as exact main 937e336031ffe064aa120460327538aab0d45d30; exact-main CI 29920636668 passed. Paid/current evidence can now remain safely unlinked in the pure authority contract, and later association preserves it without granting anything beforehand. Parent #114 remains open. No payment policy, Stripe/runtime, persistence, migration, deployment, provider, or live behavior changed.

  4. daliu commented on Oct 11, 2026

    @daliu
    ContributorAuthor

    Readiness check, 2026-10-11: Dave requested membership and event signup/cancellation next, with low cost and staging before production or real payments. Reviewed current membership authority/status helpers and race signup/admin cancellation. The membership helpers remain unwired; the legacy admin cancel action only changes Firestore and is not a safe paid cancellation workflow. Four existing synthetic unit suites passed on Node 22: 273 tests (membershipAuthority, membershipAccountStatusProjection, raceCheckoutValidation, adminRegistrationAction). This is local regression evidence, not new implementation or hosted staging proof. A cancellation policy decision is pending: access through the already-paid term versus immediate offboarding; neither implies a refund. Recommended delivery order is officer-confirmed existing dues and free-event signup/cancellation before a separate paid-race lifecycle. No new policy, grant, IAM change, provider call, staging deployment, production write or payment test performed. Existing #81/#114/#115 dependencies remain open. Officer impact: no new officer or member workflow is available. Officer documentation: None — no workflow changed; decision and next-step handoff recorded privately. Deployment evidence: no website, Firebase or outside-provider deployment in this follow-up.

  5. daliu commented on Oct 11, 2026

    @daliu
    ContributorAuthor

    Owner decision resolved, October 11, 2026: Dave selected Continue through the paid term. Voluntary membership cancellation must preserve access through the already-paid annual term and issue no automatic refund; security suspension remains separate. Focused dependency-clearing child #705 / PR #706 records a versioned backend policy and 15 existing-authority boundary regressions. Full local backend: 7609 pass / 64 skipped; lint passes. This does not implement a cancellation command or launch membership/events; hosted staging proof and the parent remain open. No production/provider/payment change.

  6. daliu commented on Oct 15, 2026

    @daliu
    ContributorAuthor

    October 15 direct owner decisions for the minimum member release:

    • $25 individual and $30 family, full calendar year.
    • Verified email, accepted waiver and confirmed paid current term suffice; no further discretionary membership approval.
    • Online dues are included in the first launch. Officers retain a private admin dues-confirmation convenience; do not advertise external payment options/instructions publicly.
    • Participating family members have separate verified accounts, share one paid Family ID and do not pay again. A bare Family ID must not grant access; private single-use invitations are the proposed secure enrollment path.
    • Privacy/retention/waiver, including guardian/minor handling, remain undecided. Requested drafts are being prepared, not approved or wired into acceptance.
    • Standing build/deploy authority remains; paid-race checkout/refunds/transfers remain a separate later release. No actual charge, provider, production or real-data action has occurred.

    First focused runtime source change is #737: canonical current-member admission for new free-event signup with expiry-safe cancellation and immutable retry. This does not complete #81/#115/#114 or activate real membership/payments.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions