| name | paystack-tanstack-start |
| description | Integrate @alexasomba/better-auth-paystack in TanStack Start. Use for Better Auth API routes, tanstackStartCookies(), server functions, getRequestHeaders(), authClient Paystack actions, admin billing server functions, and Cloudflare Workers deployment.
|
| type | composition |
| library | @alexasomba/better-auth-paystack |
| library_version | 3.0.0 |
| license | MIT |
| compatibility | Node.js >=22.0.0; better-auth ^1.6.9; @alexasomba/paystack-node 1.10.x; @alexasomba/better-auth-paystack >=3.0.0 <4.0.0 |
| sources | ["alexasomba/better-auth-paystack:examples/tanstack/README.md","alexasomba/better-auth-paystack:examples/tanstack/src/lib/auth.ts","alexasomba/better-auth-paystack:examples/tanstack/src/lib/auth-client.ts","alexasomba/better-auth-paystack:examples/tanstack/src/routes/api/auth/$.ts"] |
Setup
Create the Better Auth server config with Paystack and tanstackStartCookies() last:
import { betterAuth } from "better-auth";
import { tanstackStartCookies } from "better-auth/tanstack-start";
import { paystack } from "@alexasomba/better-auth-paystack";
export const auth = betterAuth({
baseURL: process.env.BETTER_AUTH_URL,
plugins: [
paystack({
secretKey: process.env.PAYSTACK_SECRET_KEY!,
webhook: {
secret: process.env.PAYSTACK_WEBHOOK_SECRET!,
},
subscription: {
enabled: true,
plans: [],
},
}),
tanstackStartCookies(),
],
});
Wire the catch-all auth route:
import { createFileRoute } from "@tanstack/react-router";
import { auth } from "../../../lib/auth";
export const Route = createFileRoute("/api/auth/$")({
server: {
handlers: {
GET: ({ request }) => auth.handler(request),
POST: ({ request }) => auth.handler(request),
},
},
});
Create the client plugin:
import { createAuthClient } from "better-auth/client";
import { paystackClient } from "@alexasomba/better-auth-paystack/client";
export const authClient = createAuthClient({
plugins: [paystackClient()],
});
Core Patterns
Use Paystack client actions in client components
import { authClient } from "../lib/auth-client";
export function SubscribeButton() {
return (
<button
type="button"
onClick={async () => {
await authClient.subscription.create({
plan: "starter",
});
}}
>
Subscribe
</button>
);
}
Use client actions for checkout and user-triggered subscription lifecycle calls. Do not import server helpers into React components.
Use server functions for trusted billing operations
import { createServerFn } from "@tanstack/react-start";
import { getRequestHeaders } from "@tanstack/react-start/server";
import { auth } from "./auth";
import { syncPaystackPlans } from "@alexasomba/better-auth-paystack";
export const syncPlans = createServerFn({ method: "POST" }).handler(async () => {
const session = await auth.api.getSession({
headers: await getRequestHeaders(),
});
if (!session?.user) {
throw new Response("Unauthorized", { status: 401 });
}
await syncPaystackPlans(await auth.$context, {
secretKey: process.env.PAYSTACK_SECRET_KEY!,
});
return { ok: true };
});
Pass request headers when Better Auth needs session context.
Keep SEO and discovery route-owned
Use TanStack route head functions for titles, descriptions, Open Graph/Twitter tags, canonical
links, robots meta, and JSON-LD. Keep <HeadContent /> in the root document head and <Scripts />
in the body.
Only leaf public pages should emit canonical links. Authenticated dashboards, billing layouts, and
Paystack callback routes should be noindex and excluded from the sitemap, while still remaining
available for checkout redirects and payment verification.
Keep Cloudflare Worker dependencies compatible
The TanStack example deploys to Cloudflare Workers. Keep Better Auth companion packages on compatible versions. If @better-auth/infra pulls in @better-auth/sso, avoid mixing a beta SSO package with stable better-auth.
The known safe pin for this package version is:
overrides:
"@better-auth/sso": 1.6.9
Common Mistakes
Putting tanstackStartCookies() before Paystack
tanstackStartCookies() should be last in the Better Auth plugin array so cookie handling wraps the final auth behavior.
Omitting auth headers in server functions
Wrong:
const session = await auth.api.getSession();
Correct:
const session = await auth.api.getSession({
headers: await getRequestHeaders(),
});
Debugging only root package builds
The root package can pass vp pack while the example Worker build fails. Reproduce Worker issues with:
pnpm --filter ./examples/tanstack build
pnpm --filter ./examples/tanstack exec wrangler deploy --dry-run