Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
36 changes: 5 additions & 31 deletions usage-limit-subscription/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ This project demonstrates the "Usage-Limit Subscription Template Pricing Model".
- Node.js >= 18.18.0
- Bun >= 1.3.1
- PostgreSQL database
- `yalc` (for linking local Flowglad packages) - Install globally with `npm install -g yalc` or `bun install -g yalc`

## Getting Started

Expand All @@ -46,37 +45,14 @@ This will enable all the subscription plans, usage meters, and features defined

**Important:** This project is part of a monorepo. You must install dependencies from the root of the monorepo first, then navigate into this example directory.

From the root of the monorepo:
Navigate into this project directory and install dependencies:

```bash
cd usage-limit-subscription
bun install
```

Then navigate into this example directory:

```bash
cd examples/usage-limit-subscription
```

### 3. Link Flowglad Packages

This example project uses `yalc` to link local Flowglad packages for development. You must link the packages before running the project:

```bash
bun run link:packages
```

This command will:
- Add Flowglad packages to yalc's local registry
- Link them into this project's `node_modules`
- Update dependencies

**Note:** If you need to unlink packages later (e.g., to use published npm packages), run:
```bash
bun run unlink:packages
```

### 4. Set Up Environment Variables
### 3. Set Up Environment Variables

Copy the example environment file:

Expand All @@ -95,7 +71,7 @@ Fill in the required values in `.env.local`:
- **`FLOWGLAD_SECRET_KEY`** - Secret key for Flowglad API calls
- Get your secret key from: [https://flowglad.com](https://flowglad.com)

### 5. Set Up Database
### 4. Set Up Database

Generate and run database migrations:

Expand All @@ -104,7 +80,7 @@ bun db:generate
bun db:migrate
```

### 6. Start Development Server
### 5. Start Development Server

```bash
bun dev
Expand All @@ -123,8 +99,6 @@ Open [http://localhost:3000](http://localhost:3000) to see the application.
- `bun db:generate` - Generate database migrations
- `bun db:migrate` - Run database migrations
- `bun db:studio` - Open Drizzle Studio (database GUI)
- `bun link:packages` - Link local Flowglad packages using yalc (required before first run)
- `bun unlink:packages` - Unlink Flowglad packages and restore to npm registry versions

## Project Structure

Expand Down
49 changes: 10 additions & 39 deletions usage-limit-subscription/bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion usage-limit-subscription/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
"unlink:packages": "yalc remove --all && bun install"
},
"dependencies": {
"@flowglad/nextjs": "0.12.3",
"@flowglad/nextjs": "0.15.0",
"@radix-ui/react-dropdown-menu": "^2.1.16",
"@radix-ui/react-progress": "^1.1.8",
"@radix-ui/react-slot": "^1.2.3",
Expand All @@ -35,6 +35,7 @@
"drizzle-orm": "^0.44.7",
"embla-carousel-autoplay": "^8.6.0",
"embla-carousel-react": "^8.6.0",
"flowglad": "^0.0.1",
"lucide-react": "^0.548.0",
"next": "15.5.6",
"pg": "^8.16.3",
Expand Down
23 changes: 17 additions & 6 deletions usage-limit-subscription/src/app/api/flowglad/[...path]/route.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,18 @@
// /api/flowglad/[...path]/route.ts
import { createAppRouterRouteHandler } from '@flowglad/nextjs/server';
import { flowgladServer } from '@/lib/flowglad';
import { nextRouteHandler } from '@flowglad/nextjs/server';
import { flowglad } from '@/lib/flowglad';
import { auth } from '@/lib/auth';
import { headers } from 'next/headers';

const routeHandler = createAppRouterRouteHandler(flowgladServer);

export { routeHandler as GET, routeHandler as POST };
export const { GET, POST } = nextRouteHandler({
flowglad,
getCustomerExternalId: async (req) => {
const session = await auth.api.getSession({
headers: await headers(),
});
const userId = session?.user?.id;
if (!userId) {
throw new Error('User not found');
}
return userId;
},
});
16 changes: 14 additions & 2 deletions usage-limit-subscription/src/app/api/usage-events/route.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { flowgladServer } from '@/lib/flowglad';
import { flowglad } from '@/lib/flowglad';
import { findUsagePriceByMeterSlug } from '@/lib/billing-helpers';
import { auth } from '@/lib/auth';
import { headers } from 'next/headers';
import { NextResponse } from 'next/server';
import { z } from 'zod';

Expand Down Expand Up @@ -48,7 +50,18 @@ export async function POST(request: Request) {
transactionId ||
`usage_${Date.now()}_${Math.random().toString(36).substring(7)}`;

// Get customer ID from session
const session = await auth.api.getSession({ headers: await headers() });
const userId = session?.user?.id;
if (!userId) {
return NextResponse.json(
{ error: 'User not found' },
{ status: 401 }
);
}

// Get billing information to extract required IDs
const flowgladServer = flowglad(userId);
const billing = await flowgladServer.getBilling();

if (!billing.customer) {
Expand Down Expand Up @@ -104,7 +117,6 @@ export async function POST(request: Request) {
const usageEvent = await flowgladServer.createUsageEvent({
subscriptionId,
priceId,
usageMeterId,
amount: amountNumber,
transactionId: finalTransactionId,
});
Expand Down
40 changes: 22 additions & 18 deletions usage-limit-subscription/src/lib/flowglad.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,23 @@
import { FlowgladServer } from '@flowglad/nextjs/server';
import { auth } from '@/lib/auth';
import { headers } from 'next/headers';
import { FlowgladServer } from '@flowglad/nextjs/server'
import { auth } from '@/lib/auth'
import { headers } from 'next/headers'

//Flowglad doesn't currently support BetterAuth, so we need to handle the session ourselves.
export const flowgladServer = new FlowgladServer({
getRequestingCustomer: async () => {
// If request is provided (from API route), use it; otherwise will need headers
const session = await auth.api.getSession({ headers: await headers() });
if (!session?.user) {
throw new Error('Unauthorized: No session found');
}
return {
externalId: session?.user?.id,
name: session?.user?.name ?? '',
email: session?.user?.email ?? '',
};
},
});
export const flowglad = (customerExternalId: string) => {
return new FlowgladServer({
customerExternalId,
getCustomerDetails: async () => {
const session = await auth.api.getSession({
headers: await headers(),
});

if (!session?.user) {
throw new Error('User not authenticated');
}

return {
email: session.user.email || '',
name: session.user.name || '',
};
},
});
};