Debugging and Fixing Adam Cherry Comics Stripe Checkout: Embedded Modal vs. Hosted Redirect
During a recent development session on the adamcherrycomics.dangerouscentaur.com e-commerce site, we discovered that the checkout flow was in a broken state despite what the project handoff documentation claimed. This post walks through the diagnosis, the architectural mismatch we uncovered, and the fixes applied to restore a working embedded-modal Stripe checkout.
The Problem: Documentation vs. Reality
The project handoff stated that Adam Cherry Comics had been migrated from a "DM to Buy" flow to a Stripe-hosted redirect checkout. However, when we inspected the live Lambda function adam-cherry-checkout (deployed via CloudFormation in AWS profile finalconstructclean, API Gateway n0nh1zscq4), we found something unexpected:
- The Lambda handler was already returning a PaymentIntent client_secret (format:
pi_...), not a Checkout Session URL - This indicated an embedded-modal flow, not hosted redirect
- The handoff claimed
stripe-python 15.1.0had rejectedui_mode="embedded" - Reality: the code was already using
ui_mode="embedded"
This mismatch meant the documentation was stale. We needed to inspect the actual frontend to understand what was really happening.
Verification and Root Cause Analysis
We performed the following checks:
- All pages returning 200: Verified via curl and Playwright against S3 bucket
dc-sitesserved through CloudFront distributionE2Q4UU71SRNTMB - Lambda invocation: Called the checkout Lambda directly with test payloads to inspect the actual response body
- Frontend JS inspection: Grepped the live
index.htmlfor Stripe initialization code in the modal flow
Key finding: the <script src="https://js.stripe.com/v3/"> tag was missing from the HTML. This was documented in the handoff as having been removed, but it should never have been. Without this script tag, the browser cannot call Stripe() to initialize the Stripe object, and thus cannot open the embedded modal.
Infrastructure and Deployment Pipeline
The Adam Cherry Comics infrastructure spans several AWS services:
- S3: Static site assets in bucket
dc-sites - CloudFront: Distribution ID
E2Q4UU71SRNTMBcaching and serving all pages with a default TTL - API Gateway: Endpoint
n0nh1zscq4routingPOST /checkoutrequests to the Lambda - Lambda: Function
adam-cherry-checkouthandling payment intent creation and CORS - Route53 / DNS: Namecheap explicit CNAME for
adamcherrycomicspointing to the DC subdomain (wildcard CNAME was being shadowed)
The checkout flow is:
Browser → CloudFront (S3 assets) → User clicks "Buy"
↓
Modal JS calls fetch() to API Gateway /checkout
↓
Lambda receives POST, calls stripe.PaymentIntent.create()
↓
Lambda returns { client_secret: "pi_..." }
↓
Frontend passes client_secret to Stripe.js, opens embedded modal
↓
User completes payment, browser receives webhook/redirect callback
The Fix: Restoring the Missing Stripe.js Script Tag
The root cause was straightforward: the frontend HTML did not include the Stripe JavaScript library. We patched index.html to restore the missing script tag in the <head> section:
<script src="https://js.stripe.com/v3/"></script>
This single line allows the browser to:
- Load the Stripe.js v3 library
- Call
const stripe = Stripe(publishableKey)to initialize the Stripe object - Receive the PaymentIntent client_secret from our Lambda
- Call
stripe.confirmPayment()to render the embedded modal and handle the payment
Deployment and Cache Invalidation
After patching index.html` on the EC2 development instance (where the ACC repo was synced via rsync), we:
- Uploaded the patched version to S3 bucket
dc-siteswith the same key structure - Invalidated CloudFront distribution
E2Q4UU71SRNTMBby creating a wildcard invalidation path (or explicit path forindex.html) - Waited for CloudFront to propagate the new version globally (typically < 1 minute)
We verified the fix with a cache-bust fetch:
curl -H "Cache-Control: no-cache" https://adamcherrycomics.dangerouscentaur.com/index.html | grep "js.stripe.com"
Then ran Playwright smoke tests against the staging environment to confirm the modal opens and payment flow is responsive.
Lambda CORS and Origin Handling
During smoke testing, we discovered CORS preflight failures. The Lambda was hardcoding a single origin in the `Access-Control-Allow-Origin` response header. We patched checkout_connect.py (the Lambda source) to:
- Extract the
Originheader from the incoming request - Return that origin in the CORS response header (after validation against a whitelist)
- Ensure the
Access-Control-Allow-Credentials: trueand allowed methods/headers are set
This pattern allows both staging and production origins to work:
Staging: https://adamcherrycomics.dangerouscentaur.com
Production: (apex domain, once acquired)
We also verified API Gateway had CORS enabled for the POST /checkout method and OPTIONS preflight, and added the staging origin to the allowed list.
Open Items and Next Steps
Several tasks remain:
- Adam confirmation: The owner has not yet confirmed the embedded modal checkout works end-to-end. Waiting on feedback.
- Apex domain:
adamcherrycomics.comis not owned; the site currently lives only on the DC subdomain. Acquiring the apex domain and updating Route53 / DNS will be required for a professional launch.