fix(whatsapp): let bookings actually pay + wallet-first display + clarify/voice parity
The in-chat booking pay-tail (confirm -> review -> gateway/wallet) advances in the core ONLY
by a tap, but the WhatsApp bridge renders those steps as text and forwarded the user's typed
reply as a normal message — which the core routes to question (booking parked). So a
WhatsApp booking could never complete payment: a typed "confirm"/gateway-number did nothing.
Verified at the core level (typed reply -> kind=None, stuck; the tap -> wallet_paid /
payment_link).
Bridge-side fix (no contract change — it produces the existing tap vocabulary):
- Stash the live pay-tail step (+ the gateway option->id map) on render; in onDiscover map a typed reply to the tap it stands for: a gateway NUMBER -> tap:gateway:{id}, and confirm/cancel/edit words (en/fa/ar) -> tap:pax:ok / tap:pax:cancel / tap:pax:type and tap:wallet:ok / tap:wallet:cancel. Checked before the multi-day number path so a gateway "1" can't re-select flight 1; a non-match (a real question / edited details) still falls through to the core untouched.
- Wallet-first display parity: renderConfirmWallet now shows the balance, the partial gate ("pay X from credit, Y on a gateway"), and the cash-then-credit split; renderGateway shows the "X from your credit; pay remaining Y" line on a partial split. (Fields were already in the facts.)
- Handle clarify (flight_route/flight_date/hotel_dates -> localized text prompts; no calendar widget on a text channel) and confirm_select.
- Echo the voice transcript (
🎤 + text) after STT — Telegram parity + a mis-heard-city guard. - Remove the dead pre-one-brain bookFlight/selectGateway/extractPassenger from ai.js.
- Tests for the new pure mappers (matchBookingWord/payTailPayload/gatewayRowsFromFacts); 8/8 bridge tests pass.
NOT yet live-tested on real Twilio/WhatsApp — smoke-test one booking through to pay before this reaches shared beta.
Co-Authored-By: Claude Opus 4.8 (1M context) noreply@anthropic.com