Getting Started

This guide uses Prisma for database access and GitHub as the authentication provider.

Install packages

For Auth & Payment:

pnpm install -D @airbadge/sveltekit

For Prisma:

pnpm install -D prisma @prisma/client @auth/prisma-adapter

Configure environment

Add environment variables to .env:

# Stripe private key
# Find it here: https://dashboard.stripe.com/test/apikeys
SECRET_STRIPE_KEY=sk_...

# Domain to use for URLs:
DOMAIN=http://localhost:5173

# Database URL for the Auth.js database adapter
# For examples, see: https://www.prisma.io/docs/orm/reference/connection-urls
DATABASE_URL="postgresql://postgres:postgres@localhost:5432/mydb?schema=public"

# Client Id and Client Secret of GitHub OAuth app
# In GitHub: Settings -> Developer Settings -> OAuth Apps
AUTH_GITHUB_ID=
AUTH_GITHUB_SECRET=

Setup database

First, initialize Prisma:

pnpm prisma init

Then, update your prisma/schema.prisma:

datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
}

generator client {
  provider = "prisma-client-js"
}

model Account {
  id                String  @id @default(cuid())
  userId            String
  type              String
  provider          String
  providerAccountId String
  refresh_token     String? @db.Text
  access_token      String? @db.Text
  expires_at        Int?
  token_type        String?
  scope             String?
  id_token          String? @db.Text
  session_state     String?

  user User @relation(fields: [userId], references: [id], onDelete: Cascade)

  @@unique([provider, providerAccountId])
}

model Session {
  id           String   @id @default(cuid())
  sessionToken String   @unique
  userId       String
  expires      DateTime
  user         User     @relation(fields: [userId], references: [id], onDelete: Cascade)
}

model User {
  id            String    @id @default(cuid())
  name          String?
  email         String?   @unique
  emailVerified DateTime?
  image         String?
  accounts      Account[]
  sessions      Session[]

  // customizations for AirBadge
  customerId         String?
  subscriptionId     String?
  subscriptionStatus SubscriptionStatus?
  plan               String?
  priceId            String?
}

// customization for AirBadge
enum SubscriptionStatus {
  INCOMPLETE
  INCOMPLETE_EXPIRED
  TRIALING
  ACTIVE
  PAST_DUE
  CANCELED
  UNPAID
}

model VerificationToken {
  identifier String
  token      String   @unique
  expires    DateTime

  @@unique([identifier, token])
}

Finally, push the schema:

pnpm prisma db push

Create pricing

Create some plans and pricing. This can be done via the Stripe Dashboard or from the Stripe CLI.

To create them with the CLI:

# login to Stripe
stripe login

# create products & pricing
stripe prices create \
  --product-data.name="Basic Plan" \
  --lookup-key=basic_monthly \
  --currency=usd \
  --unit-amount=1000 \
  --recurring.interval=month

stripe prices create \
  --product-data.name="Pro Plan" \
  --lookup-key=pro_monthly \
  --currency=usd \
  --unit-amount=2500 \
  --recurring.interval=month

stripe prices create \
  --product-data.name="Enterprise Plan" \
  --lookup-key=enterprise_monthly \
  --currency=usd \
  --unit-amount=10000 \
  --recurring.interval=month

Configure SvelteKit

Configure authentication and billing options in src/hooks.server.js:

import { SvelteKitAuth } from '@airbadge/sveltekit'

// use GitHub OAuth provider
import GitHub from '@auth/sveltekit/providers/github'

// use Prisma database adapter
import { PrismaAdapter } from '@auth/prisma-adapter'

// import Prisma client for database adapter
import { PrismaClient } from '@prisma/client'

// init database client
const db = new PrismaClient()

// add Auth.js + Stripe handler
// API is similar to Auth.js
export const { handle } = SvelteKitAuth({
  adapter: PrismaAdapter(db),
  providers: [ GitHub ]
})

Forward webhooks

Webhook handling is built-in. Just forward webhooks via Stripe’s CLI.

stripe listen --forward-to localhost:5173/billing/webhooks

Celebrate! 🎉

You now have a working SaaS app!

Visit http://localhost:5173/billing/checkout?id=pro_monthly to sign up and pay.