Background Jobs with Solid Queue
Why the webhook handler doesn't send the email itself
handle_succeeded could, in theory, just call a mailer directly and send the confirmation email right there, inline, before returning { received: true }. Nothing stops you syntactically. But Stripe expects your webhook endpoint to respond FAST β if it doesn't hear back quickly, it assumes the request failed and retries. Sending an email is slow and unreliable compared to a database write: an SMTP connection can hang, a mail provider can rate-limit you, a DNS lookup can time out. Block the webhook response on any of that, and a slow mail server starts making Stripe retry a webhook that actually succeeded, which risks double-processing.
The fix is to do the slow part later, off the request entirely: enqueue a background job, respond to Stripe immediately, and let something else pick up the job whenever it's convenient. That's exactly what BookingConfirmationJob.perform_later(payment.booking_id) did in lesson 3 β perform_later doesn't run the job now, it hands it to a queue and returns instantly.