Article · Sep 14, 2026 · 4 min read

What a Failed Ebook Purchase Taught Me About Actually Testing a Payment Flow

A few days ago someone close to me bought one of my ebooks. Stripe took the payment. The download never showed up.

That one bug report turned into a two-to-three-day deep dive across the two Astro storefronts I run: this site (formerly davinaleong.com, now davdevs.dev) and Davina’s Ministries, the new home for my Christian content. Both sell ebooks independently and both are wired to Stripe. It turned out they also shared the same weakness. I’d tested the payment thoroughly, but I’d never tested the full payment → fulfillment → delivery pipeline from end to end.

Here’s what the debugging actually looked like.

Root causes

  1. The step that fetches the ebook after a successful payment was buggy. I’d tested the “product delivered” side of the flow much less than the “payment accepted” side.
  2. I hadn’t tested the whole flow from start to finish in a way that would catch either problem.

Rebuilding the flow properly, in sandbox first

Rather than patch live, I set up proper Stripe sandbox environments for both projects and mirrored the real product catalog with sandbox products and prices. Every local Astro dev environment got pointed at the sandbox keys. I also went back through .env.example on both repos to make sure it matched the environment variables actually in use. It’s a small thing, but it’s saved me from “works on my machine” surprises before.

From there I ran the flow with Stripe’s test cards, covering successful charges, declines and everything in between.

Webhooks: the part everyone underestimates

To test webhooks locally, I exposed my dev server through ngrok and explicitly allow-listed the ngrok host in the Astro dev server config on both sites. This is also where I found my most embarrassing bug. For a stretch of testing, I was pointed at the wrong webhook endpoint entirely, so nothing was being confirmed while I assumed everything worked. Lesson relearned: before you trust any “it works” signal that depends on a webhook, check that the webhook is actually firing against the endpoint you think it is.

Once that was sorted, I added an orders table migration. Order state now has a real source of truth to reconcile against, instead of my implicitly trusting the happy path.

Storage and delivery

The manuscript files needed work too. I moved ebook manuscript storage from Vercel Blob to Cloudflare R2 and made sure every manuscript’s file key and name was accurate and could be retrieved reliably. A mismatched key here is exactly what produces “payment succeeded, download broken.” I also moved ebook cover images to my own domain instead of a third-party host. That keeps things consistent and takes one dependency out of the critical path.

Email deliverability

Order confirmation emails were going out over Gmail SMTP, which isn’t built for transactional volume or reliability. I switched to Resend and cleaned up the confirmation email styling while I was in there. A broken-looking confirmation email undermines trust just as much as a broken download link.

Going live

I waited until the full flow (checkout, webhook, order record, email and file retrieval) passed cleanly in sandbox on both sites. Only then did I wire up the real Stripe price IDs and manuscript file keys and switch both storefronts to publish all ebooks live.

Takeaways

  • Test the pipeline, not the step. A successful Stripe charge tells you almost nothing about whether the customer actually receives what they paid for.
  • Sandbox parity matters. If your sandbox products don’t mirror your live catalog, you aren’t really testing what you’re about to ship.
  • Webhooks are a common blind spot. Don’t assume it’s probably hitting the right endpoint. Verify it.
  • Treat file storage keys as part of the payment flow, not a separate concern. If a rename or a bad key breaks things after a successful charge, the customer still sees a failed sale.
  • Transactional email is infrastructure, not an afterthought. Gmail SMTP is fine for a hobby project. It’s the wrong tool once real money and real customers are involved.

Both storefronts now have a fully tested payment-to-download flow. It took an uncomfortable bug report from someone I love to push me into the end-to-end testing I should have done from day one, but I’ll take the lesson.


Originally published on DEV.