@sellthatai/paykit
v0.1.0
Published
Server-side client and React paywall components for SellThat.ai PayKit entitlements.
Downloads
88
Readme
@sellthatai/paykit
Server-side client and a buyer-facing checkout button for SellThat.ai PayKit.
Use it to:
- Sell a product with a drop-in
<SellThatButton />(browser-safe, no secret). - Gate access on your server with
entitlements.check()using your API key.
Install
npm install @sellthatai/paykitreact is an optional peer dependency — you only need it for the button.
Get an API key
In your SellThat dashboard, open Developer settings → API keys and create a
key with the entitlements:read scope. Keep it secret and store it as the
SELLTHAT_API_KEY environment variable on your server.
1. Sell — the checkout button (client)
import { SellThatButton } from "@sellthatai/paykit/react";
export function PricingCard() {
return (
<SellThatButton
buttonId="btn_xxxxxxxxxxxx"
buyerEmail="[email protected]"
className="rounded-lg bg-blue-600 px-4 py-2 text-white"
>
Get access — $29
</SellThatButton>
);
}The button carries no secret. It sends the buyer to the SellThat hosted checkout page for that payment button.
2. Gate — entitlement check (server)
Never call this from the browser. Your API key must stay server-side.
import { createSellThatClient } from "@sellthatai/paykit";
const sellthat = createSellThatClient(); // reads SELLTHAT_API_KEY
const entitlement = await sellthat.entitlements.check({
email: "[email protected]",
productId: 123,
});
if (entitlement.active) {
// grant access
} else {
// show paywall — entitlement.status is "past_due" | "cancelled" | "expired" | "none"
}List everything a customer owns across your products:
const all = await sellthat.entitlements.list("[email protected]");Copy-paste: Express paywall guard
import express from "express";
import { createSellThatClient } from "@sellthatai/paykit";
const sellthat = createSellThatClient();
const app = express();
// Protect a route: only paying customers get through.
app.get("/premium/:resource", async (req, res) => {
const email = req.session?.user?.email; // however you identify the user
if (!email) return res.status(401).send("Please sign in.");
const { active } = await sellthat.entitlements.check({
email,
productId: 123,
});
if (!active) return res.status(402).send("This content requires a purchase.");
res.send("Here is your premium content.");
});Copy-paste: React route guard
Do the check on your server, expose a tiny /api/has-access endpoint, and let
the client read the boolean. The secret never leaves your backend.
// server: /api/has-access
import { createSellThatClient } from "@sellthatai/paykit";
const sellthat = createSellThatClient();
app.get("/api/has-access", async (req, res) => {
const email = req.session?.user?.email;
if (!email) return res.json({ active: false });
const { active } = await sellthat.entitlements.check({ email, productId: 123 });
res.json({ active });
});// client: route guard
import { useEffect, useState } from "react";
import { SellThatButton } from "@sellthatai/paykit/react";
export function PremiumRoute({ children }: { children: React.ReactNode }) {
const [state, setState] = useState<"loading" | "yes" | "no">("loading");
useEffect(() => {
fetch("/api/has-access")
.then((r) => r.json())
.then((d) => setState(d.active ? "yes" : "no"))
.catch(() => setState("no"));
}, []);
if (state === "loading") return <p>Checking access…</p>;
if (state === "no") {
return (
<div>
<p>Unlock this content.</p>
<SellThatButton buttonId="btn_xxxxxxxxxxxx">Buy now — $29</SellThatButton>
</div>
);
}
return <>{children}</>;
}Entitlement shape
interface Entitlement {
email: string;
product_id: number;
product_type: string | null;
plan: string | null;
active: boolean;
status: "active" | "past_due" | "cancelled" | "expired" | "none";
access_until: string | null; // ISO date for time-limited access, else null
source: string | null; // "purchase" | "invite" | "free_claim"
granted_at: string | null;
}active is your single source of truth for "let them in". A cancelled
subscription keeps active: true until the paid period actually ends.
Configuration
| Option / env | Purpose |
| -------------------- | -------------------------------------------------- |
| SELLTHAT_API_KEY | Your secret key (required, server-side only). |
| SELLTHAT_API_URL | Override the API base URL (defaults to production). |
| createSellThatClient({ apiKey, baseUrl, fetch }) | Programmatic overrides. |
