The form sends payment_ice as a descriptive label, e.g. "One Ice
ticket good for one bag per day (3 total bags)". The old parser pulled
the first number ("3") and treated it as 3 tickets, then multiplied by
3 bags/ticket → 9 bags for one ice ticket (18 for two). New
iceBagsFromPayment reads the "(N total bags)" the label states
directly, with worded-count and numeric dollar/count fallbacks for
forward compatibility. 1 ice → 3 bags, 2 → 6. 5 new tests.
Scanner: Ice mode now defaults the check-in count to 1 (a bag at a
time) instead of all remaining bags; staff can bump it up.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Children now free through age 12:
- Scannable/paid ticket total = adults + youth 13-16 only; kids 12 &
under (0-4, 5-9, 10-12) are stored but not counted (charging starts
at 13). computeTotal + freeKidsCount in fields.ts, webhook guard,
scan/admin badges, event-report labels, docs, and personas updated.
Vendor booth webhooks (vendors.beartariacampgrounds.com):
- New /vendor-webhook/food (2 named pass-holders) and
/vendor-webhook/non-food (1 pass-holder), reusing WEBHOOK_SECRET.
Booth name -> ticket title; each named person = one entry pass;
tagged with a "Food Vendor"/"Vendor" Ticket Type (badge on scan +
event-report rollup). Idempotent + QR email like the attendee hook.
- Extracted shared FluentForms parsing (nameGroup/qty/selected/
addressLine/readDonor) into fluentforms.ts; attendee webhook now
imports it. 13 new unit tests.
Public lookup CORS is now a comma-separated allowlist; the caller's
Origin is echoed only if it matches. tickets + vendors both allowed
on donor-eligibility and ticket-vouchers.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>