diff --git a/backend/src/routes/webhook.ts b/backend/src/routes/webhook.ts index 77d7522..08df9e0 100644 --- a/backend/src/routes/webhook.ts +++ b/backend/src/routes/webhook.ts @@ -5,8 +5,15 @@ import { createTicket } from "../ticketService.js"; import { renderQrPng } from "../services/qrcode.js"; import { safeEqual, nameGroup, qty, selected, addressLine } from "../fluentforms.js"; -// Adult name field bases, in order (purchaser first). -const ADULT_NAME_BASES = ["names", "names_1", "names_2", "names_3", "names_4", "names_5", "names_6", "names_7", "names_8", "names_9"]; +// Adult name field bases, in order: purchaser first, then additional regular +// adults (Adult Ticket #2–#10), then donor adult tickets (Adult Donor #1–#2). +// Donor tickets are the free/voucher adult admissions; their names live in the +// separate names_Donor_* groups but are still adults who need a gate pass. +const ADULT_NAME_BASES = [ + "names", + "names_1", "names_2", "names_3", "names_4", "names_5", "names_6", "names_7", "names_8", "names_9", + "names_Donor_1", "names_Donor_2", +]; export async function webhookRoutes(app: FastifyInstance): Promise { const handler = async (req: any, reply: any) => { diff --git a/backend/src/routes/webhookDoc.ts b/backend/src/routes/webhookDoc.ts index b5c25cb..b9930fb 100644 --- a/backend/src/routes/webhookDoc.ts +++ b/backend/src/routes/webhookDoc.ts @@ -13,6 +13,7 @@ interface Field { const FIELDS: Field[] = [ { key: "names", req: "required", type: "name (compound)", desc: "Purchaser / Adult #1 — object {first_name, middle_name, last_name}. Also accepts flat names[first_name] keys." }, { key: "names_1 … names_9", req: "optional", type: "name (compound)", desc: "Additional adult attendee names (Adults #2–#10). Empty groups are ignored. Stored as the adult-name list shown at the gate." }, + { key: "names_Donor_1 / names_Donor_2", req: "optional", type: "name (compound)", desc: "Donor (voucher) adult ticket names — the free adult admissions. Counted via item_quantity_adult_ticket_donor and added to the gate name list." }, { key: "email", req: "optional", type: "email", desc: "Purchaser email — the QR ticket is sent here (FluentForms sends the receipt separately)." }, { key: "address_1", req: "optional", type: "address (compound)", desc: "Mailing address object; joined into one line." }, { key: "item_quantity_adult_ticket_reg", req: "required", type: "quantity", desc: "Adult tickets (regular)." }, @@ -125,7 +126,7 @@ const PAGE = ` KeyRequiredTypeDescription ${rows} -

Compound name fields arrive as objects (names: {first_name,…}) or flattened names[first_name] keys — both handled. Quantity/payment fields accept numbers, money strings ("$40.00"), or {quantity} objects.

+

Compound name fields arrive as objects (names: {first_name,…}) or flattened names[first_name] keys — both handled. Quantity/payment fields accept numbers, money strings ("$40.00"), or {quantity} objects. Counts come from the item_quantity_* fields, so pure pricing line items (payment_adult_reg, payment_youth_*, payment_kids_free, payment_donor_voucher1/2, custom-payment-amount/Tax) are ignored — the vouchers hidden count is authoritative for donor vouchers.

Idempotency

Send a stable id / submission_id. A repeat returns {"status":"duplicate"} without creating a second ticket or re-emailing — safe for retries and double-submits.