Debugging and Stabilizing Stripe Embedded Checkout on adamcherrycomics.dangerouscentaur.com
Overview
During a routine handoff review and verification session for the Adam Cherry Comics site, we discovered that checkout was broken in production despite the infrastructure appearing healthy. The Lambda function was returning PaymentIntent client secrets instead of properly initializing Stripe's embedded checkout modal on the frontend. This post covers the diagnosis, the root cause (missing Stripe.js script tag), and the fixes applied across the frontend, Lambda, and infrastructure layers.
What Was Done
1. Verification and Diagnosis
We started by verifying the live site status:
- All pages at
https://adamcherrycomics.dangerouscentaur.com/returned HTTP 200 ✅ - Invoked Lambda function
adam-cherry-checkoutdirectly and received a PaymentIntent client_secret response - Inspected live frontend HTML for Stripe initialization markers
- Grepped the live
index.htmlforStripe()andopenCheckoutcalls
The critical finding: the <script src="https://js.stripe.com/v3/"> tag was missing from the live index.html, which meant the frontend had no Stripe library loaded and could not process the PaymentIntent or mount the embedded checkout modal.
2. Frontend Patch: Restore Stripe.js and Modal Initialization
We created /tmp/patch_acc_checkout.py to programmatically inject the missing script tag and verify the checkout modal initialization code was present.
Changes made to index.html:
- Inserted
<script src="https://js.stripe.com/v3/"></script>in the document head - Verified the modal JS block containing
Stripe(publishable_key)initialization was intact - Confirmed the
openCheckout()function was properly wired to the buy buttons - Validated that the modal HTML structure included the
<div id="payment-element">container
Deployed patched index.html to s3://dc-sites/adamcherrycomics.dangerouscentaur.com/index.html and invalidated CloudFront distribution E2Q4UU71SRNTMB.
3. Lambda CORS and Origin Handling
Initial browser smoke tests against staging failed with CORS preflight errors. The Lambda function at /projects/adamcherrycomics.md (source on EC2 in the ACC repo) had hardcoded RETURN_URL and did not dynamically derive the origin from the incoming request.
Lambda patches applied:
- Modified
checkout.pyhandler to extract the origin from theOriginrequest header - Updated
RETURN_URLto be computed at runtime:origin + "/checkout-success"instead of hardcoded domain - Added explicit
Access-Control-Allow-Originresponse header with the request's origin value - Configured API Gateway CORS to permit
POST /checkoutfromhttps://adamcherrycomics.dangerouscentaur.com
Lambda environment was rebuilt and redeployed to adam-cherry-checkout via the configured CI/CD pipeline.
4. Stripe Embedded Checkout Configuration
The Lambda function was already using ui_mode="embedded" (contrary to the initial handoff notes), which is the correct configuration per the platform's modal-only policy. However, we needed to verify the Stripe SDK version and payment mode:
- Confirmed
stripe-python 15.1.0was pinned in the Lambda layer - Verified
payment_method_types=["card"]for card-only checkout - Validated that Checkout Session creation included
success_urlandcancel_urlparameters derived from the request origin
5. Deployment and Testing
Created /tmp/smoke_acc_staging.py using Playwright to run end-to-end browser tests:
- Load product page on staging
- Click buy button for a $10 product
- Verify modal appears and Stripe.js iframe loads
- Inspect CloudWatch Logs for Lambda invocation and any errors
- Verify PaymentIntent returns with
client_secret
After each fix (Stripe.js injection, CORS, Lambda return URL), re-ran smoke tests and tailed CloudWatch logs in real time to catch any remaining issues.
6. Mount-Clear and Cache Handling
Created /tmp/patch_mount_clear.py to ensure staging and prod S3 mount points were properly cleared before deployment, preventing stale index.html from being served. This was especially critical because CloudFront caching and S3 object replication required explicit cache invalidation after the patch.
Cache invalidation steps:
- Created CloudFront invalidation for
/*on distributionE2Q4UU71SRNTMB - Waited for invalidation to complete (typically 30–60 seconds)
- Performed cache-busting fetch:
curl -H "Cache-Control: no-cache" https://adamcherrycomics.dangerouscentaur.com/index.html - Verified version header and asset timestamps in response
Technical Details and Architecture
Stripe Embedded Checkout Flow
The corrected flow is now:
- User clicks "Buy" button on a product (e.g., $10 print)
- Frontend calls
openCheckout(product_id, price) - Frontend POST to
https://n0nh1zscq4.execute-api.us-east-1.amazonaws.com/checkoutwith product metadata - Lambda
adam-cherry-checkoutcreates Stripe Checkout Session withui_mode="embedded" - Lambda returns
{ "clientSecret": "cs_..." } - Frontend calls
stripe.confirmPayment({ clientSecret, elements }) - Stripe renders payment form inside the modal
<div id="payment-element"> - User enters card and completes payment
- Stripe redirects to
success_urlderived from request origin
Infrastructure Stack
- S3 bucket:
dc-sites(multi-site bucket with prefixadamcherrycomics.dangerouscentaur.com