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
40 changes: 6 additions & 34 deletions tiered-usage-gated-subscription/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ This project demonstrates the "Tiered Usage-Gated Subscription Template Pricing
- Node.js >= 18.18.0
- Bun >= 1.3.1
- PostgreSQL database (or Docker to run the included `docker-compose.yml`)
- `yalc` (for linking local Flowglad packages) - Install globally with `npm install -g yalc` or `bun install -g yalc`

## Getting Started

Expand All @@ -44,39 +43,14 @@ This will enable all the subscription plans, usage meters, and features defined

### 2. Install Dependencies

**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 tiered-usage-gated-subscription
bun install
```

Then navigate into this example directory:

```bash
cd examples/tiered-usage-gated-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 +69,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

If you don't have a PostgreSQL database available, you can use the included `docker-compose.yml` file to spin up a local PostgreSQL instance:

Expand Down Expand Up @@ -124,7 +98,7 @@ docker-compose down

**Note:** If you already have a PostgreSQL database, you can skip this step and use your existing database connection string instead.

### 6. Run Database Migrations
### 5. Run Database Migrations

Generate and run database migrations:

Expand All @@ -133,7 +107,7 @@ bun db:generate
bun db:migrate
```

### 7. Start Development Server
### 6. Start Development Server

```bash
bun dev
Expand All @@ -152,8 +126,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: 9 additions & 40 deletions tiered-usage-gated-subscription/bun.lock

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

2 changes: 1 addition & 1 deletion tiered-usage-gated-subscription/next-env.d.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/// <reference types="next" />
/// <reference types="next/image-types/global" />
import './.next/dev/types/routes.d.ts';
/// <reference path="./.next/types/routes.d.ts" />

// NOTE: This file should not be edited
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
6 changes: 2 additions & 4 deletions tiered-usage-gated-subscription/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,10 @@
"type-check": "tsc --noEmit",
"db:generate": "drizzle-kit generate --config=drizzle.config.ts",
"db:migrate": "drizzle-kit migrate --config=drizzle.config.ts",
"db:studio": "drizzle-kit studio --config=drizzle.config.ts",
"link:packages": "yalc add @flowglad/nextjs @flowglad/react @flowglad/server @flowglad/shared @flowglad/types && yalc update && bun install --force && yalc update",
"unlink:packages": "yalc remove --all && bun install"
"db:studio": "drizzle-kit studio --config=drizzle.config.ts"
},
"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 Down
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;
},
});
23 changes: 15 additions & 8 deletions tiered-usage-gated-subscription/src/app/api/usage-events/route.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { flowgladServer } from '@/lib/flowglad';
import { flowglad } from '@/lib/flowglad';
import {
findUsagePriceBySlug,
findUsageMeterBySlug,
} 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 @@ -54,7 +56,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 @@ -83,7 +96,6 @@ export async function POST(request: Request) {
usageMeterSlug,
billing.pricingModel
);
console.log(usageMeter);
if (!usageMeter) {
return NextResponse.json(
{
Expand Down Expand Up @@ -133,15 +145,10 @@ export async function POST(request: Request) {
);
}

const priceId = usagePrice.id;
const usageMeterId = usagePrice.usageMeterId;

// Create usage event with all required IDs
// Note: customerId is automatically resolved from the session by FlowgladServer
const usageEvent = await flowgladServer.createUsageEvent({
subscriptionId,
priceId,
usageMeterId,
priceSlug,
amount: amountNumber,
transactionId: finalTransactionId,
});
Comment on lines 149 to 154

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

# Check package.json for @flowglad/nextjs version
fd -t f "package.json" | head -5 | xargs -I {} sh -c 'echo "=== {} ===" && cat {}'

Repository: flowglad/examples

Length of output: 7031


🌐 Web query:

@flowglad/nextjs 0.15.0 createUsageEvent API documentation

💡 Result:

createUsageEvent (FlowgladServer) — summary

  • Purpose: record a metered usage event tied to a customer/subscription/price/usage meter (used for usage-based billing). [1][2]
  • Call (example):
    await flowgladServer.createUsageEvent({
    amount: number,
    priceId: string,
    subscriptionId: string,
    usageMeterId: string,
    transactionId: string, // idempotency key
    usageDate?: number, // ms since epoch (optional)
    properties?: Record<string, unknown> // optional; required for count_distinct_properties meters
    }) . [2]
  • Notes:
    • transactionId prevents duplicate events (idempotency). [2]
    • For "sum" aggregation, amount is the quantity; for "count_distinct_properties" send properties (usually amount = 1) and include the property to dedupe (e.g., userId). [1][2]
    • usageDate defaults to now if omitted; events are aggregated into the customer's current billing period. [1][2]

Sources:

  • Flowglad Usage docs (Usage-based billing — createUsageEvent details). [1]
  • Flowglad Server SDK API reference (createUsageEvent signature + examples). [2]

API mismatch: createUsageEvent requires priceId and usageMeterId, not priceSlug.

The call at lines 149–154 passes priceSlug, but the @flowglad/nextjs 0.15.0 API signature requires priceId and usageMeterId as separate parameters. This will cause a runtime error. Update the call to include the required parameters:

const usageEvent = await flowgladServer.createUsageEvent({
  subscriptionId,
  priceId,
  usageMeterId,
  amount: amountNumber,
  transactionId: finalTransactionId,
});
🤖 Prompt for AI Agents
In tiered-usage-gated-subscription/src/app/api/usage-events/route.ts around
lines 149–154, the call to flowgladServer.createUsageEvent passes priceSlug but
the library expects priceId and usageMeterId; replace priceSlug with the two
required fields (priceId and usageMeterId) in the object passed to
createUsageEvent, ensuring subscriptionId, amount (amountNumber) and
transactionId (finalTransactionId) remain included.

Expand Down
Loading