feat: stripe plugin to handle subscriptions and customers (#1588)

* init

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* feat(stripe): enable subscription support and update pricing plans

* feat(stripe): add Vitest configuration and initial tests for Stripe integration

* feat(stripe): implement setCookieToHeader function and update tests for customer creation and subscription handling

* feat(stripe): add seats support for subscriptions and update related endpoints

* feat(stripe): update schema to include unique referenceId, stripeSubscriptionId, and periodEnd fields

* wip docs

* docs

* docs: imporves

* fix(stripe): update webhook handlers to use correct subscription identification

* refactor(stripe): simplify customer management by storing Stripe customer ID directly on user

* chore(stripe): update package configuration and build setup

- Migrated from tsup to unbuild for build configuration
- Updated package.json with improved export and dependency management
- Added build configuration for better module support
- Removed tsup configuration file

* chore(stripe): update pnpm lockfile dependencies

- Moved `better-auth` from devDependencies to dependencies
- Added `zod` as a direct dependency
- Reorganized package dependencies in the lockfile

* feat(stripe): enhance subscription management and error handling

- Added toast error handling for subscription upgrades in the dashboard
- Updated Stripe price IDs for different plans
- Improved Stripe plugin documentation with beta warning and team subscription details
- Implemented intermediate redirect for checkout success to handle race conditions
- Added support for fetching and updating subscription status after checkout
- Fixed Next.js cookie handling and build configuration

* chore: update snapshot
This commit is contained in:
Bereket Engida
2025-03-01 01:20:17 +03:00
committed by GitHub
parent e613bf492c
commit 4f56078e4b
34 changed files with 3381 additions and 67 deletions

View File

@@ -18,6 +18,9 @@ import { MysqlDialect } from "kysely";
import { createPool } from "mysql2/promise";
import { nextCookies } from "better-auth/next-js";
import { passkey } from "better-auth/plugins/passkey";
import { stripe } from "@better-auth/stripe";
import { Stripe } from "stripe";
import Database from "better-sqlite3";
const from = process.env.BETTER_AUTH_EMAIL || "delivered@resend.dev";
const to = process.env.TEST_EMAIL || "";
@@ -37,12 +40,18 @@ if (!dialect) {
throw new Error("No dialect found");
}
const PROFESSION_PRICE_ID = {
default: "price_1QxWZ5LUjnrYIrml5Dnwnl0X",
annual: "price_1QxWZTLUjnrYIrmlyJYpwyhz",
};
const STARTER_PRICE_ID = {
default: "price_1QxWWtLUjnrYIrmleljPKszG",
annual: "price_1QxWYqLUjnrYIrmlonqPThVF",
};
export const auth = betterAuth({
appName: "Better Auth Demo",
database: {
dialect,
type: "sqlite",
},
database: new Database("./stripe.db"),
emailVerification: {
async sendVerificationEmail({ user, url }) {
const res = await resend.emails.send({
@@ -149,5 +158,31 @@ export const auth = betterAuth({
loginPage: "/sign-in",
}),
oneTap(),
stripe({
stripeClient: new Stripe(process.env.STRIPE_KEY!),
stripeWebhookSecret: process.env.STRIPE_WEBHOOK_SECRET!,
subscription: {
enabled: true,
requireEmailVerification: true,
plans: [
{
name: "Starter",
priceId: STARTER_PRICE_ID.default,
annualDiscountPriceId: STARTER_PRICE_ID.annual,
freeTrial: {
days: 7,
},
},
{
name: "Professional",
priceId: PROFESSION_PRICE_ID.default,
annualDiscountPriceId: PROFESSION_PRICE_ID.annual,
},
{
name: "Enterprise",
},
],
},
}),
],
});