Question / Task
Part of the Phase 1 Payment System: Design & Build Map, graduated from Design backend data model & Stripe integration flow for Phase 1 payments (see that ticket's resolution comment for full design rationale). Blocked by the migration/config ticket (voter_limit column, pricing constants).
Voter-limit resolution (packages/backend/src/Controllers/Roll/addElectionRollController.ts:86-91): replace let voterLimit = overrides[...] ?? sharedConfig.FREE_TIER_PRIVATE_VOTER_LIMIT with overrides[req.election.election_id] ?? req.election.voter_limit. The old free-tier fallback constant is no longer needed — it's baked into voter_limit's initial value.
"Payment required" response: when the limit check fails, throw a response shaped like:
{
"error": "Request Denied: this election is limited to 100 voters",
"code": "PAYMENT_REQUIRED",
"currentVoterLimit": 100,
"requestedVoterCount": 150,
"blockSize": 200,
"pricePerBlockCents": 1000
}
(This repo's errors are currently always a flat {error: string} — errorCatchMiddleware.ts — so this is a new, deliberately narrow special case, not a general error-code system.) On the frontend, AddElectionRoll.tsx's submit catch handler should check for code === 'PAYMENT_REQUIRED' and open the payment/cart modal (built in the sibling prototype ticket) instead of just showing the snackbar — this ticket only needs to emit the response shape and do the minimal catch-handler branch; the modal itself is separate frontend work tracked elsewhere on the map.
Lock down voter_limit: packages/backend/src/Controllers/Election/editElectionController.ts currently takes req.body.Election (the entire client-submitted object) and passes it straight to ElectionsModel.updateElection() — verified at editElectionController.ts:15,37. Before calling updateElection(), overwrite inputElection.voter_limit with req.election.voter_limit (the server's current value), discarding whatever the client submitted for that field, so voter_limit can only ever change via the Stripe-driven applyVoterLimitPurchase path (built in the webhook ticket).
Question / Task
Part of the Phase 1 Payment System: Design & Build Map, graduated from Design backend data model & Stripe integration flow for Phase 1 payments (see that ticket's resolution comment for full design rationale). Blocked by the migration/config ticket (
voter_limitcolumn, pricing constants).Voter-limit resolution (
packages/backend/src/Controllers/Roll/addElectionRollController.ts:86-91): replacelet voterLimit = overrides[...] ?? sharedConfig.FREE_TIER_PRIVATE_VOTER_LIMITwithoverrides[req.election.election_id] ?? req.election.voter_limit. The old free-tier fallback constant is no longer needed — it's baked intovoter_limit's initial value."Payment required" response: when the limit check fails, throw a response shaped like:
{ "error": "Request Denied: this election is limited to 100 voters", "code": "PAYMENT_REQUIRED", "currentVoterLimit": 100, "requestedVoterCount": 150, "blockSize": 200, "pricePerBlockCents": 1000 }(This repo's errors are currently always a flat
{error: string}—errorCatchMiddleware.ts— so this is a new, deliberately narrow special case, not a general error-code system.) On the frontend,AddElectionRoll.tsx's submit catch handler should check forcode === 'PAYMENT_REQUIRED'and open the payment/cart modal (built in the sibling prototype ticket) instead of just showing the snackbar — this ticket only needs to emit the response shape and do the minimal catch-handler branch; the modal itself is separate frontend work tracked elsewhere on the map.Lock down
voter_limit:packages/backend/src/Controllers/Election/editElectionController.tscurrently takesreq.body.Election(the entire client-submitted object) and passes it straight toElectionsModel.updateElection()— verified ateditElectionController.ts:15,37. Before callingupdateElection(), overwriteinputElection.voter_limitwithreq.election.voter_limit(the server's current value), discarding whatever the client submitted for that field, sovoter_limitcan only ever change via the Stripe-drivenapplyVoterLimitPurchasepath (built in the webhook ticket).