Part C — Full code
Backend — routes/checkoutRoutes.js
// routes/checkoutRoutes.js
const express = require("express");
const Stripe = require("stripe");

const router = express.Router();
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY, {
  apiVersion: "2024-06-20",
});

/**
 * Expected body:
 * {
 *   items: [
 *     // One-off stock image
 *     { kind: "stock", assetId: "abc123", name: "Black Brick Texture", priceCents: 699, qty: 1 },
 *     // OR subscription by lookup key
 *     { kind: "price", lookupKey: "stock_starter_mo", qty: 1 }
 *   ],
 *   successPath: "/thank-you",
 *   cancelPath: "/cart"
 * }
 */
router.post("/create-session", async (req, res) => {
  try {
    const { items = [], successPath = "/", cancelPath = "/cart" } = req.body || {};
    if (!Array.isArray(items) || items.length === 0) {
      return res.status(400).json({ error: "Cart is empty" });
    }

    // Build line items
    const line_items = [];
    const purchasedAssetIds = [];

    for (const item of items) {
      if (item.kind === "stock") {
        // One-off custom price with metadata name
        const unit_amount = Number(item.priceCents || 699);
        const qty = Number(item.qty || 1);
        purchasedAssetIds.push(item.assetId);

        line_items.push({
          price_data: {
            currency: "usd",
            unit_amount,
            product_data: {
              name: `Stock Photo: ${item.name || item.assetId}`,
            },
          },
          quantity: qty,
        });
      } else if (item.kind === "price") {
        // Use a recurring price by lookup key
        if (!item.lookupKey) continue;
        const prices = await stripe.prices.list({ lookup_keys: [item.lookupKey], expand: ["data.product"] });
        const price = prices.data?.[0];
        if (!price) throw new Error(`Price not found for lookupKey ${item.lookupKey}`);
        line_items.push({
          price: price.id,
          quantity: Number(item.qty || 1),
        });
      }
    }

    if (line_items.length === 0) {
      return res.status(400).json({ error: "No valid items" });
    }

    const domain = process.env.DOMAIN_URL || process.env.FRONTEND_URL || "http://localhost:3000";
    const session = await stripe.checkout.sessions.create({
      mode: line_items.some(li => !li.price) ? "payment" : "subscription", // if any price_data exists, it's a one-time payment; otherwise subscription
      line_items,
      success_url: `${domain}${successPath}?session_id={CHECKOUT_SESSION_ID}`,
      cancel_url: `${domain}${cancelPath}`,
      // attach user + assets for fulfillment
      metadata: {
        userId: String(req.user?.id || "anon"),
        assetIds: purchasedAssetIds.join(","), // "" if none
      },
      allow_promotion_codes: true,
    });

    return res.json({ url: session.url });
  } catch (err) {
    console.error("create-session error", err);
    return res.status(500).json({ error: "Failed to create checkout" });
  }
});

module.exports = router;