Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
29375ce
feat(auth): introduce environment variable processor
atybdot Feb 27, 2026
e15eaad
feat(auth): implement astro dashboard template
atybdot Feb 27, 2026
5b081b7
feat(auth): refine react nextjs dashboard component
atybdot Feb 28, 2026
e4093cd
feat(auth): adjust react nextjs dashboard page entry
atybdot Mar 1, 2026
6b254f7
feat(auth): refine react router dashboard template
atybdot Mar 2, 2026
a035d4b
feat(auth): introduce solidjs dashboard template
atybdot Mar 2, 2026
6b37f47
refactor(auth): update solidjs auth client logic
atybdot Mar 3, 2026
3d378eb
feat(auth): refine svelte dashboard template
atybdot Mar 3, 2026
51d686a
refactor(auth): update svelte auth client logic
atybdot Mar 3, 2026
5f6d003
feat(auth): refine tanstack router dashboard template
atybdot Mar 4, 2026
9ad347d
feat(auth): add server base logic (part 5)
atybdot Mar 7, 2026
ad322e1
feat(auth): add better-auth client for Astro and React Base
atybdot Mar 10, 2026
784ce88
setup(env): add server environment variable template
atybdot Mar 13, 2026
4dcc7cf
test(payments): Add payments-dodo CLI tests
atybdot Mar 14, 2026
b72c27c
feat(auth): Enhance Nuxt better-auth template with dashboard and clie…
atybdot Mar 15, 2026
eec8787
refactor(auth): Update Tanstack Start React dashboard template
atybdot Mar 16, 2026
6bee74b
add dodo option in CLI
atybdot Mar 18, 2026
041fe8e
refactor(payments): update payments dependency processor
atybdot Feb 26, 2026
430d2f5
enable in web front-end
atybdot Mar 18, 2026
47dd1a3
feat(deps): Add utility for dependency management in template generator
atybdot Mar 18, 2026
04a5537
feat(infra): Add Alchemy run script template sections
atybdot Mar 18, 2026
e332be1
chore(templates): Regenerate template metadata
atybdot Mar 18, 2026
30f04d9
chore: include remaining changes in packages/template-generator/src/t…
atybdot Mar 18, 2026
aab4e66
chore: include remaining changes in packages/template-generator/templ…
atybdot Mar 18, 2026
0b7975d
chore: include remaining changes in packages/template-generator/templ…
atybdot Mar 18, 2026
73c737d
chore: include remaining changes in packages/template-generator/templ…
atybdot Mar 18, 2026
cb429ef
chore: include remaining changes in packages/template-generator/templ…
atybdot Mar 18, 2026
9d650d7
chore: include remaining changes in packages/template-generator/templ…
atybdot Mar 18, 2026
f6aeaa4
chore: include remaining changes in packages/template-generator/templ…
atybdot Mar 18, 2026
de2af42
chore: include remaining changes in packages/template-generator/templ…
atybdot Mar 18, 2026
07f4ea1
chore: include remaining changes in packages/template-generator/templ…
atybdot Mar 18, 2026
054607c
feat:init mcp setup
atybdot Mar 18, 2026
90263aa
chore:add docs for dodo
atybdot Mar 18, 2026
2af43f2
fix:missing imports
atybdot Mar 18, 2026
8351acb
Merge branch 'main' into main
atybdot Mar 19, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion apps/cli/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ Options:
--orm <type> ORM type (none, drizzle, prisma, mongoose)
--dry-run Validate configuration without writing files
--auth <provider> Authentication (better-auth, clerk, none)
--payments <provider> Payments provider (polar, none)
--payments <provider> Payments provider (polar, dodo, none)
--frontend <types...> Frontend types (tanstack-router, react-router, tanstack-start, next, nuxt, svelte, solid, astro, native-bare, native-uniwind, native-unistyles, none)
--addons <types...> Additional addons (pwa, tauri, electrobun, starlight, biome, lefthook, husky, mcp, turborepo, nx, fumadocs, ultracite, oxlint, opentui, wxt, skills, none)
--examples <types...> Examples to include (todo, ai, none)
Expand Down
10 changes: 10 additions & 0 deletions apps/cli/src/helpers/addons/mcp-setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,12 @@ function getAllMcpServers(config: ProjectConfig): McpServerDef[] {
name: "polar",
target: "https://mcp.polar.sh/mcp/polar-mcp",
},
{
key: "dodo",
label: "Dodo Payments",
name: "dodo",
target: "https://docs.dodopayments.com/mcp",
},
];
}

Expand Down Expand Up @@ -260,6 +266,10 @@ export function getRecommendedMcpServers(
recommendedServerKeys.push("polar");
}

if (config.payments === "dodo") {
recommendedServerKeys.push("dodo");
}

return uniqueValues(recommendedServerKeys)
.map((serverKey) => serversByKey.get(serverKey))
.filter((server): server is McpServerDef => server !== undefined);
Expand Down
9 changes: 7 additions & 2 deletions apps/cli/src/prompts/payments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,12 @@ export async function getPaymentsChoice(
return "none" as Payments;
}

const isPolarCompatible =
const isProviderCompatible =
auth === "better-auth" &&
backend !== "convex" &&
(frontends?.length === 0 || splitFrontends(frontends).web.length > 0);

if (!isPolarCompatible) {
if (!isProviderCompatible) {
return "none" as Payments;
}

Expand All @@ -31,6 +31,11 @@ export async function getPaymentsChoice(
label: "Polar",
hint: "Turn your software into a business. 6 lines of code.",
},
{
value: "dodo" as Payments,
label: "Dodo Payments",
hint: "Payments, billing, and distribution. One integration.",
},
{
value: "none" as Payments,
label: "None",
Expand Down
7 changes: 4 additions & 3 deletions apps/cli/src/utils/compatibility-rules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -317,17 +317,18 @@ export function validatePaymentsCompatibility(
): ValidationResult {
if (!payments || payments === "none") return Result.ok(undefined);

if (payments === "polar") {
if (payments === "polar" || payments === "dodo") {
const providerLabel = payments === "polar" ? "Polar" : "Dodo";
if (!auth || auth === "none" || auth !== "better-auth") {
return validationErr(
"Polar payments requires Better Auth. Please use '--auth better-auth' or choose a different payments provider.",
`${providerLabel} payments requires Better Auth. Please use '--auth better-auth' or choose a different payments provider.`,
);
}

const { web } = splitFrontends(frontends);
if (web.length === 0 && frontends.length > 0) {
return validationErr(
"Polar payments requires a web frontend or no frontend. Please select a web frontend or choose a different payments provider.",
`${providerLabel} payments requires a web frontend or no frontend. Please select a web frontend or choose a different payments provider.`,
);
}
}
Expand Down
6 changes: 6 additions & 0 deletions apps/cli/test/addon-setup-regressions.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,12 @@ describe("Addon setup regressions", () => {
expect(betterTStackServer?.target).toBe("bunx create-better-t-stack@latest mcp");
});

it("recommends the Dodo MCP server when Dodo payments are selected", () => {
const servers = getRecommendedMcpServers(createProjectConfig({ payments: "dodo" }), "project");

expect(servers.some((server) => server.key === "dodo")).toBe(true);
});

it("preserves explicit empty MCP selections in silent mode", async () => {
const projectDir = path.join(SMOKE_DIR, "mcp-explicit-empty");
await fs.remove(projectDir);
Expand Down
22 changes: 22 additions & 0 deletions apps/cli/test/integration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -521,6 +521,28 @@ describe("Integration Tests - Real World Scenarios", () => {
expectError(result, "Polar payments requires Better Auth");
});

it("should fail with Dodo payments incompatibility", async () => {
const result = await runTRPCTest({
projectName: "dodo-no-auth-fail",
backend: "hono",
runtime: "bun",
database: "none",
orm: "none",
auth: "none",
payments: "dodo",
api: "trpc",
frontend: ["tanstack-router"],
addons: ["turborepo"],
examples: ["none"],
dbSetup: "none",
webDeploy: "none",
serverDeploy: "none",
expectError: true,
});

expectError(result, "Dodo payments requires Better Auth");
});

it("should fail with deployment constraint violation", async () => {
const result = await runTRPCTest({
projectName: "web-deploy-no-frontend-fail",
Expand Down
66 changes: 66 additions & 0 deletions apps/cli/test/payments-dodo.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { describe, expect, it } from "bun:test";
import path from "node:path";

import fs from "fs-extra";

import { create } from "../src/index";

const SMOKE_DIR_PATH = path.join(import.meta.dir, "..", ".smoke");

describe("Dodo payments generation", () => {
it("emits Dodo deps and env vars for a reference stack", async () => {
const projectPath = path.join(SMOKE_DIR_PATH, "dodo-reference-stack");
await fs.remove(projectPath);

const result = await create(projectPath, {
frontend: ["next"],
backend: "hono",
runtime: "node",
database: "none",
orm: "none",
api: "trpc",
auth: "better-auth",
payments: "dodo",
addons: ["turborepo"],
examples: ["none"],
dbSetup: "none",
webDeploy: "none",
serverDeploy: "none",
install: false,
git: true,
packageManager: "bun",
disableAnalytics: true,
directoryConflict: "overwrite",
});

expect(result.isOk()).toBe(true);

const authPkgPath = path.join(projectPath, "packages", "auth", "package.json");
const webPkgPath = path.join(projectPath, "apps", "web", "package.json");
const serverEnvPath = path.join(projectPath, "apps", "server", ".env");
const paymentsPath = path.join(projectPath, "packages", "auth", "src", "lib", "payments.ts");
const successPath = path.join(
projectPath,
"apps",
"web",
"src",
"app",
"payment",
"success",
"page.tsx",
);

const authPkg = await fs.readJson(authPkgPath);
const webPkg = await fs.readJson(webPkgPath);
const serverEnv = await fs.readFile(serverEnvPath, "utf8");

expect(authPkg.dependencies?.["@dodopayments/better-auth"]).toBeDefined();
expect(authPkg.dependencies?.dodopayments).toBeDefined();
expect(webPkg.dependencies?.["@dodopayments/better-auth"]).toBeDefined();
expect(serverEnv).toContain("DODO_PAYMENTS_API_KEY=");
expect(serverEnv).toContain("DODO_PAYMENTS_WEBHOOK_SECRET=");
expect(serverEnv).toContain("DODO_PAYMENTS_ENVIRONMENT=");
expect(await fs.pathExists(paymentsPath)).toBe(true);
expect(await fs.pathExists(successPath)).toBe(true);
});
});
5 changes: 3 additions & 2 deletions apps/web/content/docs/cli/compatibility.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ Clerk authentication requires:

### Payments Requirements

Polar payments require:
Polar and Dodo payments require:

- Better-Auth authentication
- A web frontend (or no frontend selected)
Expand Down Expand Up @@ -258,11 +258,12 @@ create-better-t-stack --runtime workers --backend hono
create-better-t-stack --frontend tanstack-router
```

### "Polar payments requires Better Auth"
### "Polar/Dodo payments requires Better Auth"

```bash
# Fix by using Better-Auth
create-better-t-stack --payments polar --auth better-auth
create-better-t-stack --payments dodo --auth better-auth

# Or use Clerk without Polar payments
create-better-t-stack --auth clerk --backend hono --frontend tanstack-router
Expand Down
2 changes: 1 addition & 1 deletion apps/web/content/docs/cli/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ create-better-t-stack [project-directory] [options]
- `--orm <type>`: `none`, `drizzle`, `prisma`, `mongoose`
- `--api <type>`: `none`, `trpc`, `orpc`
- `--auth <provider>`: `better-auth`, `clerk`, `none` (see [Options](/docs/cli/options#authentication))
- `--payments <provider>`: `polar`, `none`
- `--payments <provider>`: `polar`, `dodo`, `none`
- `--db-setup <setup>`: `none`, `turso`, `d1`, `neon`, `supabase`, `prisma-postgres`, `planetscale`, `mongodb-atlas`, `docker`
- `--examples <types...>`: `none`, `todo`, `ai`
- `--web-deploy <setup>`: `none`, `cloudflare`
Expand Down
4 changes: 3 additions & 1 deletion apps/web/content/docs/cli/options.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -287,12 +287,14 @@ Payments provider:

- `none`: No payments integration
- `polar`: Polar payments integration
- `dodo`: Dodo Payments integration

```bash
create-better-t-stack --payments polar --auth better-auth
create-better-t-stack --payments dodo --auth better-auth
```

**Note:** Polar payments requires Better-Auth authentication.
**Note:** Payments providers require Better-Auth authentication.

## Addons

Expand Down
2 changes: 1 addition & 1 deletion apps/web/content/docs/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ See the full list in the [CLI Reference](/docs/cli). Key flags:
- `--orm`: drizzle, prisma, mongoose, none
- `--api`: trpc, orpc, none
- `--auth`: better-auth, clerk, none
- `--payments`: polar, none
- `--payments`: polar, dodo, none
- `--addons`: turborepo, nx, pwa, tauri, electrobun, biome, lefthook, husky, starlight, fumadocs, ultracite, oxlint, mcp, opentui, wxt, skills, none
- `--examples`: todo, ai, none

Expand Down
20 changes: 11 additions & 9 deletions apps/web/src/app/(home)/new/_components/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -546,21 +546,22 @@ export const analyzeStackCompatibility = (stack: StackState): CompatibilityResul
// PAYMENTS CONSTRAINTS
// ============================================

if (nextStack.payments === "polar") {
if (nextStack.payments === "polar" || nextStack.payments === "dodo") {
const providerLabel = nextStack.payments === "polar" ? "Polar" : "Dodo";
if (nextStack.auth !== "better-auth") {
nextStack.payments = "none";
changed = true;
changes.push({
category: "payments",
message: "Payments set to 'None' (Polar requires Better Auth)",
message: `Payments set to 'None' (${providerLabel} requires Better Auth)`,
});
}
if (nextStack.backend === "convex") {
nextStack.payments = "none";
changed = true;
changes.push({
category: "payments",
message: "Payments set to 'None' (Polar incompatible with Convex)",
message: `Payments set to 'None' (${providerLabel} incompatible with Convex)`,
});
}
const hasWebFrontend = nextStack.webFrontend.some((f) => f !== "none");
Expand All @@ -569,7 +570,7 @@ export const analyzeStackCompatibility = (stack: StackState): CompatibilityResul
changed = true;
changes.push({
category: "payments",
message: "Payments set to 'None' (Polar requires web frontend)",
message: `Payments set to 'None' (${providerLabel} requires web frontend)`,
});
}
}
Expand Down Expand Up @@ -754,8 +755,8 @@ export const getDisabledReason = (
return `Convex AI example only supports React-based frontends (not ${frontendName})`;
}
}
if (category === "payments" && optionId === "polar") {
return "Polar is not compatible with Convex";
if (category === "payments" && (optionId === "polar" || optionId === "dodo")) {
return `${optionId === "polar" ? "Polar" : "Dodo"} is not compatible with Convex`;
}
}

Expand Down Expand Up @@ -1008,12 +1009,13 @@ export const getDisabledReason = (
// ============================================
// PAYMENTS CONSTRAINTS
// ============================================
if (category === "payments" && optionId === "polar") {
if (category === "payments" && (optionId === "polar" || optionId === "dodo")) {
const providerLabel = optionId === "polar" ? "Polar" : "Dodo";
if (currentStack.auth !== "better-auth") {
return "Polar requires Better Auth";
return `${providerLabel} requires Better Auth`;
}
if (!currentStack.webFrontend.some((f) => f !== "none")) {
return "Polar requires a web frontend";
return `${providerLabel} requires a web frontend`;
}
}

Expand Down
9 changes: 9 additions & 0 deletions apps/web/src/lib/constant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -455,6 +455,15 @@ export const TECH_OPTIONS: Record<
color: "from-purple-400 to-purple-600",
default: false,
},
{
id: "dodo",
name: "Dodo Payments",
description: "Payments, billing, and distribution. One integration.",
// need to update the dodo logo in r2
icon: `${ICON_BASE_URL}/dodo.svg`,
color: "from-amber-400 to-amber-600",
Comment on lines +462 to +464

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Replace the placeholder Dodo icon reference or provide a safe fallback.

The inline note indicates the asset is not ready yet, so this entry can render a broken icon in the UI. Please either ship the icon asset now or use a temporary fallback until it exists.

default: false,
},
{
id: "none",
name: "No Payments",
Expand Down
15 changes: 15 additions & 0 deletions packages/template-generator/src/processors/env-vars.ts
Original file line number Diff line number Diff line change
Expand Up @@ -448,6 +448,21 @@ function buildServerVars(
value: `${corsOrigin}/success?checkout_id={CHECKOUT_ID}`,
condition: payments === "polar",
},
{
key: "DODO_PAYMENTS_API_KEY",
value: "",
condition: payments === "dodo",
},
{
key: "DODO_PAYMENTS_WEBHOOK_SECRET",
value: "",
condition: payments === "dodo",
},
{
key: "DODO_PAYMENTS_ENVIRONMENT",
value: "test_mode",
condition: payments === "dodo",
},
{
key: "CORS_ORIGIN",
value: corsOrigin,
Expand Down
Loading
Loading