```html

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.0 had rejected ui_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-sites served through CloudFront distribution E2Q4UU71SRNTMB
  • Lambda invocation: Called the checkout Lambda directly with test payloads to inspect the actual response body
  • Frontend JS inspection: Grepped the live index.html for 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 E2Q4UU71SRNTMB caching and serving all pages with a default TTL
  • API Gateway: Endpoint n0nh1zscq4 routing POST /checkout requests to the Lambda
  • Lambda: Function adam-cherry-checkout handling payment intent creation and CORS
  • Route53 / DNS: Namecheap explicit CNAME for adamcherrycomics pointing 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:

  1. Load the Stripe.js v3 library
  2. Call const stripe = Stripe(publishableKey) to initialize the Stripe object
  3. Receive the PaymentIntent client_secret from our Lambda
  4. 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-sites with the same key structure
  • Invalidated CloudFront distribution E2Q4UU71SRNTMB by creating a wildcard invalidation path (or explicit path for index.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 Origin header from the incoming request
  • Return that origin in the CORS response header (after validation against a whitelist)
  • Ensure the Access-Control-Allow-Credentials: true and 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.com is 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.