The Payment Intent Lifecycle in AI Travel Booking
From 'book it' in the chat to confirmed reservation — every step of the payment flow including 3D Secure, webhook processing, and reconciliation.

The traveler says "book it" in the chat. From that moment, we have about 5 seconds to charge their card, confirm the reservation with the travel data provider, and show them a confirmation. Five seconds. If it takes longer, anxiety sets in. If it fails, trust evaporates.
Those 5 seconds involve a multi-step state machine that transitions through creation, authorization, capture, and reconciliation. Each step can fail independently, and the recovery path depends on which step failed. Getting this right is the difference between a booking platform and a hobby project.
Payment intent creation

When the traveler confirms a booking, we create a payment intent. This is a pre-authorization: we tell the payment processor "we intend to charge this card $847 for a flight." The processor validates the card, checks available funds, and holds the amount.
The intent creation includes an idempotency key derived from the client-generated attempt id. If our system creates the same intent twice (due to a network retry), the processor returns the existing intent rather than creating a duplicate. First layer of idempotency.
The intent is not a charge. It's a hold. The card is validated, the funds are reserved, but no money has moved yet. This distinction matters because the next step (confirming the reservation with the travel data provider) might fail. If it does, we release the hold and the traveler's card is never actually charged.
3D Secure challenges
Some card issuers require 3D Secure verification: an additional authentication step where the traveler confirms the payment through their bank's interface. This is a redirect flow. The traveler leaves our payment sheet, completes the bank's challenge, and returns.
In a traditional e-commerce checkout, 3DS is a minor interruption. In a chat-based interface, it's more disruptive because the traveler is mid-conversation. The payment sheet opens as a modal overlay, the 3DS challenge appears within it, and when it completes, the modal closes and the chat resumes.
We handle 3DS asynchronously. When the payment intent requires 3DS, we transition the booking state to "awaiting_authentication." The client displays the 3DS challenge. When the traveler completes it, the client notifies the server, and the booking flow resumes.
If the traveler abandons the 3DS challenge (closes the modal, navigates away), the payment intent remains in an incomplete state. We set a timeout: if the 3DS challenge isn't completed within a window, we cancel the intent and inform the traveler. The fare hold is released.
Payment capture

Once the card is authorized (either directly or after 3DS), we capture the payment. Capture converts the authorization into an actual charge. Money moves from the traveler's account to ours.
Capture happens only after we've confirmed the reservation with the travel data provider. The sequence is strict: authorize the card, confirm the seat, then capture the payment. If the seat confirmation fails (fare expired, no availability), we void the authorization without capturing. The traveler is never charged for a failed booking.
This ordering protects the traveler. They're only charged when we have a confirmed reservation. A failure at any earlier step results in no charge.
Webhook confirmation
After capture, the payment processor sends a webhook event confirming the charge. This webhook is the authoritative confirmation that money has been collected. Our booking system processes this webhook and updates the booking record from "payment_processing" to "payment_confirmed."
We process webhooks in background jobs, not synchronously. The webhook endpoint acknowledges receipt immediately (returning a 200 status) and enqueues the actual processing as a job. This prevents timeout issues: webhook processing involves database updates, notification triggers, and reconciliation checks that might take longer than the processor's webhook timeout window.
The webhook handler verifies the signature on every event. The processor signs each webhook with a secret. We validate this signature to ensure the event is authentic and hasn't been tampered with. An attacker who can send fake webhook events could mark bookings as paid without actual payment. Signature verification prevents this.
We also handle webhook replays. The processor might resend an event if our initial acknowledgment was slow. Our handler checks whether the event has already been processed (by event ID) and skips duplicates. Idempotent webhook processing.
Failure handling in the chat
When a payment fails, the AI agent needs to communicate this clearly. "Your payment was declined" isn't helpful. The traveler needs to know why and what to do.
We map payment failure codes to human-readable explanations:
- Insufficient funds: "Your card doesn't have enough available balance for this purchase. Try a different payment method."
- Card declined: "Your card was declined. This sometimes happens with international transactions. Try another card or contact your bank."
- 3DS failure: "The additional verification with your bank didn't complete. Let's try the payment again."
- Network error: "We had a temporary issue processing the payment. Your card has not been charged. Would you like to try again?"
The agent presents the explanation in the natural flow of the conversation. It doesn't dump an error code. It explains what happened and offers a path forward. This is one of the advantages of a conversational booking interface: the agent can handle errors conversationally rather than showing a generic error page.
Reconciliation
After the booking is confirmed and the payment is captured, we reconcile. This means verifying that every payment record matches exactly one booking record, with matching amounts.
Reconciliation runs as a scheduled job. It compares payment records against booking records and flags mismatches. A payment without a booking means something went wrong in the booking confirmation step. A booking without a payment means the webhook was missed or the capture failed.
Mismatches are rare because the system is designed to prevent them. But rare is not zero, and the financial consequences of undetected mismatches compound over time. Reconciliation catches drift before it becomes significant.
The state machine
The complete payment lifecycle is a state machine with these transitions:
Created -> Authorized (card validated and funds held) Authorized -> Awaiting 3DS (if required by issuer) Awaiting 3DS -> Authorized (3DS completed) Awaiting 3DS -> Cancelled (3DS abandoned or failed) Authorized -> Captured (reservation confirmed, payment collected) Authorized -> Voided (reservation failed, hold released) Captured -> Reconciled (payment matched to booking record) Captured -> Refunded (traveler cancelled, money returned)
Each state transition is recorded with a timestamp and the triggering event. This audit trail is essential for debugging, customer support, and financial compliance. When a traveler asks "what happened to my payment?" we can trace the exact sequence of events and timestamps.
The state machine is also the basis for monitoring. We track how long payments spend in each state. A payment stuck in "authorized" for too long means the booking confirmation is failing. A payment stuck in "awaiting 3DS" means travelers are abandoning the verification. Each metric tells a story about the booking experience.
Nowah is an AI travel agent that searches and books real flights and hotels through conversation — no filters, no thirty open tabs. Plan your next trip.