4) API Contracts
4.1 List & Details

GET /api/presentations
Query: q, toptier, category, subcat, min, max
Return: cards with id, title, top_tier, subcategories, preview_image_url, slide_previews (first 6 urls for quick load), formats, price=2999.

GET /api/presentations/:id
Return full slide_previews (all 24) + download fields.

4.2 Builder State (“Add to Your Presentation Template”)

Persist user selections server-side (session or DB) to avoid loss on refresh.

Base deck

POST /api/presentations/builder/base body: { templateId } → sets base deck

GET /api/presentations/builder → returns { baseTemplateId, selectedCoverId, selectedInfographicIds: [] }

Cover/Dividers (already saved in the Cover page flow):

We already store “Add to Presentation Template” on the Cover page. Surface it here by reading user’s “staged cover” id.

Infographics (already saved):

Read the user’s “staged infographic IDs” (1–4) from the Infographics selection.

4.3 Checkout

Regular ($29.99)

POST /api/presentations/:id/checkout-regular

Validate template is active & approved

Create pending presentation_purchases row: { type: 'regular', base_template_id: :id }

Create Stripe Checkout (global product/price @ $29.99) with success URL ?success=true&purchase=<id>

Return { checkoutUrl, purchaseId }

Premium+ ($49.99)

POST /api/presentations/checkout-premium

Read builder state for user: baseTemplateId, selectedCoverId, selectedInfographicIds (0–4)

Validate base deck exists & is active

Create pending presentation_purchases row: { type: 'premium_plus', base_template_id, selected_cover_id, selected_infographic_ids }

Create Stripe Checkout (@ $49.99) with success URL ?success=true&purchase=<id>

Return { checkoutUrl, purchaseId }

4.4 Ownership & Download (same pattern as Covers/Infographics)

GET /api/presentation-purchases/:id → returns purchase row (status, download_url)

GET /api/presentation-purchases/:id/download (auth) → if status='paid', redirect to signed URL or stream file

Webhook (checkout.session.completed):

If metadata.type = regular, set download_url to the base deck’s file/zip.

If metadata.type = premium_plus, assemble a composite package:

Base deck (24 slides)

Replace the cover/divider master with selected custom Cover/Dividers (if present).

MVP: include cover/divider files as separate folder in ZIP (e.g., /Cover+Dividers/…) if actual injection is non-trivial; note in README how to apply the master.

Include up to 4 infographics (files or link.txt for Google Slides) under /Infographics/…

Upload ZIP to storage → store URL in download_url.

Auto-download: On success return, the page polls GET /api/presentation-purchases/:id until status='paid' and download_url is present, then starts download.

5) UI Behavior Details
5.1 Presentation Gallery

Card click → lightbox with:

Large preview

24 thumbnails in a vertical strip (or grid) to jump between slides

Watermark overlay on previews (same CSS as Covers)

Buttons:

Buy Now – $29.99 (Regular)

Add to Your Presentation Template (sets base deck, toast success)

5.2 Premium+ Builder

Shows base deck card (required)

Shows Cover/Dividers (pulled from user’s staged cover/dividers; show “Manage” to jump back)

Shows selected Infographics (0–4; show counts; “Manage” to jump back)

“Checkout – $49.99” button (disabled until base deck exists)

5.3 “Add to Your Presentation Template” buttons on other pages

Cover & Divider Templates page: add a secondary button “Add to Your Presentation Template” (no extra cost inside Premium+).

Infographics Templates page: each checkbox selection already flows to Premium+; add a small “Added to Presentation Template” indicator in the selection tray when present.

6) Stripe Integration (reuse)

Use the same webhook endpoint as Covers/Infographics.

Implement two new auto-create helpers for products/prices:

ensurePresentationRegularPrice()

ensurePresentationPremiumPlusPrice()

Metadata on Checkout session:

{ type: 'regular', templateId, purchaseId }

{ type: 'premium_plus', baseTemplateId, selectedCoverId, selectedInfographicIds: JSON, purchaseId }

7) Acceptance Criteria

Regular flow ($29.99)

Gallery loads decks; lightbox shows 24 slide previews.

“Buy Now – $29.99” opens Stripe Checkout.

On return, auto-download begins; purchase shows in “My Purchases” and /download works.

Premium+ flow ($49.99)

From Covers/Infographics pages, “Add to Your Presentation Template” persists selections.

In Builder, base deck required; selections (cover/dividers + 0–4 infographics) appear automatically.

Checkout creates a single purchase and, after webhook, a single ZIP link is provided; auto-download works.

Filters, Watermark, Lightbox

Filters work across gallery pages (presentations, covers, infographics).

Watermarks appear on previews in gallery & lightbox.

Lightbox supports ESC/overlay close, keyboard navigation, and thumb jump.

Security & Ownership

Non-owners can’t access /download.

Owners can re-download via My Purchases.

No console errors; mobile responsive; a11y focus rings present.

8) Edge Cases & Notes

If any selected infographic or cover is deactivated before purchase: backend validation must reject with a clear error to reselect.

If ZIP assembly takes > a few seconds after webhook: success page polling must show a toast and continue (we already do this pattern).

Google Slides-only assets: include a links.txt and README.txt inside the ZIP with URLs and instructions.

If slide “injection” (merging cover/infographics into the PPTX) is out of scope, ship the ZIP with clear folder structure; we can add a server-side merge renderer later.

9) Hand-offs / What exists to reuse

Stripe webhook, auto-create products: reuse from Covers/Infographics.

Auto-download on success: reuse polling pattern.

Watermark CSS & lightbox interactions: reuse from Covers page.

Filters / taxonomy: exact same list and controls as Infographics.

That’s the full spec, ready for Replit.