Root cause: OrderController.Continue (GET /order/continue) was calling
MarkTokenUsed the moment the link loaded, not when the customer
actually submitted the order. This burns a one-time link before the
customer opens it whenever anything else issues a GET first - most
likely mail security link-scanners (e.g. Outlook Safe Links) that
pre-fetch URLs found in email bodies, which matches the admin site's
mailto-link workflow.
- OrderController.Continue: only checks TokenUsedAt/TokenExpiresAt for
validity now, no longer marks the token used
- OrderApiController.SubmitOrderDetails: rejects an already-used token
(410), and marks the token used only after the order details are
successfully saved
Extends the jurisdiction API cache to store JCode/Name pairs (not just
codes) and looks up the matching Name for an order's already-validated
JurisdictionNumber when rendering /order/continue. When a match is
found, the Municipality field is pre-filled and locked read-only;
otherwise it falls back to an empty, editable field so the user can
enter it manually.
RouteKit Classic ASP MVC starter for the Purple Envelope election-materials
order site: request-order flow, token-based order continuation, SurveyJS
order-details form with server-side validation and a themed confirmation
email, plus the framework core, generators, and dev-only aspunit test harness.