let’s make the title read “IBrandBiz | #XXXXXXXXXX”, show the file type, and make the button conditional (Download when licensed/has quota, otherwise Purchase). Here’s a tight Replit prompt + the code.

Prompt (paste to Replit)

Update Stock Photos UI/BE:

On every card & modal header, show the asset title as “IBrandBiz | #<10-digit code>” and display “File type: PNG/JPG/…”. Use asset.code from the API; if missing, derive it from asset.id with SHA-256 → digits (same as backend).

Add GET /api/stock/:id/entitlement that returns { licensed, quotaRemaining, canDownloadOriginal } using our entitlements service.

Frontend: add useEntitlement(assetId) hook. In the card/modal:

If licensed → button label Download (goes to /api/stock/:id/download).

Else if quotaRemaining > 0 → Download (uses 1 credit) (same endpoint).

Else → Purchase $6.99 (adds {kind:"stock"…} to cart and opens drawer).
Keep all watermark/storage code unchanged.

Backend — add to routes/stockLibraryRoutes.js (new endpoint)
// NEW: entitlement status for a single asset
const { hasLicense, getTotalRemaining } = require("../services/entitlements");

router.get("/:id/entitlement", async (req, res) => {
  try {
    const { id } = req.params;
    const userId = req.user?.id || null;

    const licensed = userId ? hasLicense(userId, id) : false;
    const quotaRemaining = userId ? getTotalRemaining(userId) : 0;

    res.json({
      userId: userId || null,
      licensed,
      quotaRemaining,
      canDownloadOriginal: licensed || quotaRemaining > 0,
    });
  } catch (e) {
    console.error("entitlement error", e);
    res.status(500).json({ error: "entitlement_failed" });
  }
});


Place it near your other /api/stock/:id/* routes. No other server changes needed.

Frontend — src/hooks/useEntitlement.ts
import { useEffect, useState } from "react";

type Ent = {
  licensed: boolean;
  quotaRemaining: number;
  canDownloadOriginal: boolean;
};

export function useEntitlement(assetId: string | undefined) {
  const [state, setState] = useState<Ent & { loading: boolean; error?: string }>({
    licensed: false,
    quotaRemaining: 0,
    canDownloadOriginal: false,
    loading: true,
  });

  useEffect(() => {
    let cancel = false;
    if (!assetId) return;
    setState((s) => ({ ...s, loading: true }));
    fetch(`/api/stock/${assetId}/entitlement`)
      .then((r) => r.json())
      .then((data) => !cancel && setState({ ...data, loading: false }))
      .catch((e) => !cancel && setState((s) => ({ ...s, loading: false, error: String(e) })));
    return () => {
      cancel = true;
    };
  }, [assetId]);

  return state;
}

Frontend — util to show code + file type
// src/utils/assetDisplay.ts
export function fileTypeFrom(asset: { mimeType?: string; name?: string }) {
  const t =
    asset.mimeType?.split("/")[1] ||
    (asset.name || "").split(".").pop() ||
    "";
  return t.toUpperCase();
}

// Derive the same 10-digit code if backend hasn't sent asset.code
export async function toTenDigitCode(seed: string): Promise<string> {
  const enc = new TextEncoder().encode(seed);
  const buf = await crypto.subtle.digest("SHA-256", enc);
  const hex = [...new Uint8Array(buf)]
    .map((b) => b.toString(16).padStart(2, "0"))
    .join("");
  const digits = hex.replace(/[a-f]/g, (c) => (parseInt(c, 16) % 10).toString()).slice(0, 10);
  return digits.padEnd(10, "0");
}

Frontend — update your Stock card component
// src/components/StockCard.tsx (or wherever you render the grid cards)
import React, { useEffect, useState } from "react";
import { useCart } from "../context/CartContext";
import { useEntitlement } from "../hooks/useEntitlement";
import { fileTypeFrom, toTenDigitCode } from "../utils/assetDisplay";

type Asset = { id: string; name: string; previewUrl: string; mimeType?: string; code?: string };

export default function StockCard({ asset }: { asset: Asset }) {
  const { addItem, open } = useCart();
  const ent = useEntitlement(asset.id);

  const [code, setCode] = useState(asset.code || "");
  useEffect(() => {
    let ok = true;
    (async () => {
      if (asset.code) return setCode(asset.code);
      const c = await toTenDigitCode(asset.id);
      if (ok) setCode(c);
    })();
    return () => { ok = false; };
  }, [asset.id, asset.code]);

  const type = fileTypeFrom(asset);

  const handlePurchase = () => {
    addItem({
      kind: "stock",
      assetId: asset.id,
      name: `IBrandBiz | #${code}`,
      priceCents: 699,
      qty: 1,
      previewUrl: asset.previewUrl,
    });
    open();
  };

  const handleDownload = () => {
    window.location.href = `/api/stock/${asset.id}/download`;
  };

  const button = (() => {
    if (ent.loading) return <button className="text-sm bg-gray-200 px-3 py-1 rounded">Checking…</button>;
    if (ent.licensed) return (
      <button onClick={handleDownload} className="text-sm bg-black text-white px-3 py-1 rounded">
        Download
      </button>
    );
    if (ent.quotaRemaining > 0) return (
      <button onClick={handleDownload} className="text-sm bg-black text-white px-3 py-1 rounded">
        Download (uses 1 credit)
      </button>
    );
    return (
      <button onClick={handlePurchase} className="text-sm bg-black text-white px-3 py-1 rounded">
        Purchase $6.99
      </button>
    );
  })();

  return (
    <div className="border rounded p-2">
      <img src={asset.previewUrl} className="w-full h-40 object-cover rounded" />
      <div className="mt-2">
        <div className="text-sm font-semibold truncate">IBrandBiz | #{code}</div>
        <div className="text-xs text-gray-500">File type: {type || "—"}</div>
      </div>
      <div className="mt-2 flex items-center justify-between">
        <div className="text-xs text-gray-500 truncate">{asset.name}</div>
        {button}
      </div>
    </div>
  );
}

(Optional) Modal header tweak

Wherever you render the fullscreen preview header, mirror the same two lines:

<h3 className="text-lg font-semibold">IBrandBiz | #{code}</h3>
<p className="text-xs text-gray-400">File type: {type}</p>


(Reuse the same code/type helpers as above.)

Should the button be conditional?

Yes. UX-wise this is 🔥:

Licensed → “Download” (no confusion).

Has credits → “Download (uses 1 credit)” (transparent spend).

No credits → “Purchase $6.99” (clear path to buy).

The backend you already have will enforce it; this just makes the UI honest and smooth.