You are creating a new plan file in the .agents/plans/ directory.
link from the landing page,
add upgrade under the dropdown
user can go to their polar profile to cancel their plan, etc etc
I need you to use context7 to get the docs for polar and polar combined with better-auth and implement the auth fully in the application.` as the file slug (kebab-case recommended)
2. Gather content - Collect or generate the plan content
3. Create the file - Use the create-plan script to generate the file
The script will automatically:
happy-blue-moon){id}-{slug}.md in .agents/plans/If you have the content ready, run:
For multi-line content, use heredoc:
You can also pipe content:
The created file will have:
The title is automatically formatted from the slug (e.g., my-feature -> My Feature).
user-authentication, api-integration)Create a plan file using the slug from `Look on the landing page, the I want you to implement full payment with all the parameters on the pricing page implemented, in all aspects.
link from the landing page,
add upgrade under the dropdown
user can go to their polar profile to cancel their plan, etc etc
I need you to use context7 to get the docs for polar and polar combined with better-auth and implement the auth fully in the application.` and appropriate content for the planning task.
Planning slug and docs retrieval
Checking plans directory
Planning directory verification steps
[
{
"content": "Fetch Context7 docs for Polar and Better Auth integration requirements",
"status": "in_progress",
"priority": "high"
},
{
"content": "Draft kebab-case slug and actionable implementation plan content",
"status": "pending",
"priority": "high"
},
{
"content": "Run create-plan script and verify generated file in .agents/plans",
"status": "pending",
"priority": "high"
}
]Preparing parallel library resolution
Checking current integration requirements first by pulling official docs for Polar and Better Auth via Context7, then I’ll draft the plan content and generate the .agents/plans file.
Available Libraries: Each result includes: - Library ID: Context7-compatible identifier (format: /org/project) - Name: Library or package name - Description: Short summary - Code Snippets: Number of available code examples - Source Reputation: Authority indicator (High, Medium, Low, or Unknown) - Benchmark Score: Quality indicator (100 is the highest score) - Versions: List of versions if available. Use one of those versions if the user provides a version in their query. The format of the version is /org/project/version. For best results, select libraries based on name match, source reputation, snippet coverage, benchmark score, and relevance to your use case. ---------- - Title: Polar - Context7-compatible library ID: /polarsource/polar - Description: Polar is an open-source payments infrastructure platform for developers, enabling them to sell digital products and subscriptions while handling billing, receipts, and taxes. - Code Snippets: 607 - Source Reputation: High - Benchmark Score: 77.1 ---------- - Title: Polar SDK - Context7-compatible library ID: /polarsource/polar-python - Description: The Polar SDK is a Python client library designed for seamless interaction with the Polar API, providing comprehensive tools for managing various Polar resources and handling webhooks. - Code Snippets: 1027 - Source Reputation: High - Benchmark Score: 96.7 ---------- - Title: Polar JS - Context7-compatible library ID: /polarsource/polar-js - Description: A developer-friendly and type-safe TypeScript SDK for leveraging the Polar API, also functioning as an MCP server for AI applications. - Code Snippets: 2666 - Source Reputation: High - Benchmark Score: 85.7 ---------- - Title: Polar Sh SDK - Context7-compatible library ID: /polarsource/polar-php - Description: A developer-friendly and type-safe PHP SDK designed to leverage the Polar API, providing access to its HTTP and Webhooks functionalities. - Code Snippets: 1594 - Source Reputation: High - Benchmark Score: 70.5 ---------- - Title: Laravel Polar - Context7-compatible library ID: /danestves/laravel-polar - Description: A Laravel package to seamlessly integrate Polar.sh subscriptions and payments, providing tools for handling subscriptions, recurring payments, webhooks, and a fluent API. - Code Snippets: 179 - Source Reputation: High - Benchmark Score: 70.6
Available Libraries: Each result includes: - Library ID: Context7-compatible identifier (format: /org/project) - Name: Library or package name - Description: Short summary - Code Snippets: Number of available code examples - Source Reputation: Authority indicator (High, Medium, Low, or Unknown) - Benchmark Score: Quality indicator (100 is the highest score) - Versions: List of versions if available. Use one of those versions if the user provides a version in their query. The format of the version is /org/project/version. For best results, select libraries based on name match, source reputation, snippet coverage, benchmark score, and relevance to your use case. ---------- - Title: Better Auth - Context7-compatible library ID: /better-auth/better-auth - Description: Better Auth is a framework-agnostic authentication and authorization library for TypeScript, offering a comprehensive feature set and a plugin ecosystem for advanced functionalities. - Code Snippets: 2752 - Source Reputation: High - Benchmark Score: 74.5 - Versions: v1.2.9, better-auth@1.3.4, v1_3_8, v1_3_10_beta_6, _better_auth_stripe_1_3_4, __branch__chore_option-exports ---------- - Title: Better Auth - Context7-compatible library ID: /llmstxt/better-auth_llms_txt - Description: Better Auth is a flexible authentication library that supports email/password, social providers, and can be extended with plugins like username, magic link, and passkey. - Code Snippets: 1875 - Source Reputation: High - Benchmark Score: 84 ---------- - Title: Better Auth - Context7-compatible library ID: /websites/better-auth - Description: Better Auth is a framework-agnostic authentication and authorization framework for TypeScript, offering a comprehensive feature set and a plugin ecosystem for advanced functionalities like 2FA and SSO. - Code Snippets: 1774 - Source Reputation: High - Benchmark Score: 75.5 ---------- - Title: Better Auth - Context7-compatible library ID: /get-convex/better-auth - Description: Convex Better Auth is a component that integrates Better Auth with Convex for enhanced authentication in applications. - Code Snippets: 231 - Source Reputation: High - Benchmark Score: 76.5 ---------- - Title: Better Auth Paystack Plugin - Context7-compatible library ID: /alexasomba/better-auth-paystack - Description: A Better Auth plugin that integrates Paystack for customer creation, checkout, and Paystack-native subscription flows, providing optional customer creation on sign-up, webhook verification, and local subscription management. - Code Snippets: 108 - Source Reputation: High - Benchmark Score: 88.9
Planning parallel doc queries
### Create Polar Checkout Handler in Next.js
Source: https://github.com/polarsource/polar/blob/main/docs/integrate/sdk/adapters/nextjs.mdx
Sets up a Next.js API route to handle Polar checkouts. It requires an access token and success URL, with options for return URL, environment (sandbox/production), and theme.
```typescript
// checkout/route.ts
import { Checkout } from "@polar-sh/nextjs";
export const GET = Checkout({
accessToken: process.env.POLAR_ACCESS_TOKEN,
successUrl: process.env.SUCCESS_URL,
returnUrl: "https://myapp.com", // An optional URL which renders a back-button in the Checkout
server: "sandbox", // Use sandbox if you're testing Polar - omit the parameter or pass 'production' otherwise
theme: "dark", // Enforces the theme - System-preferred theme will be set if left omitted
});
```
--------------------------------
### Generate Polar Checkout Sessions in Next.js
Source: https://github.com/polarsource/polar/blob/main/docs/guides/nextjs.mdx
This code sets up a GET route in Next.js to generate Polar checkout sessions. It requires the Polar access token and allows specifying success and cancellation URLs, along with the server environment (sandbox/production).
```typescript
// src/app/checkout/route.ts
import { Checkout } from "@polar-sh/nextjs";
export const GET = Checkout({
accessToken: process.env.POLAR_ACCESS_TOKEN!,
successUrl: "/confirmation?checkout_id={CHECKOUT_ID}",
server: "sandbox", // Use this option if you're using the sandbox environment - else use 'production' or omit the parameter
});
```
--------------------------------
### Handle Polar Webhooks in Next.js
Source: https://github.com/polarsource/polar/blob/main/docs/guides/nextjs.mdx
This example shows how to set up a POST endpoint in Next.js to handle incoming webhook events from Polar. It requires a webhook secret for verification and provides a callback function to process the event payload.
```typescript
// src/app/api/webhook/polar/route.ts
import { Webhooks } from "@polar-sh/nextjs";
export const POST = Webhooks({
webhookSecret: process.env.POLAR_WEBHOOK_SECRET,
onPayload: async (payload) => // Handle payload...
});
```
--------------------------------
### Create Polar Webhook Handler in Next.js
Source: https://github.com/polarsource/polar/blob/main/docs/integrate/sdk/adapters/nextjs.mdx
Establishes a Next.js API route to process incoming Polar webhooks securely. It requires a webhook secret and an `onPayload` handler to process event data.
```typescript
// api/webhook/polar/route.ts
import { Webhooks } from "@polar-sh/nextjs";
export const POST = Webhooks({
webhookSecret: process.env.POLAR_WEBHOOK_SECRET!,
onPayload: async (payload) => {
// Handle the payload
// No need to return an acknowledge response
},
});
```
--------------------------------
### Create Polar Customer Portal Handler in Next.js
Source: https://github.com/polarsource/polar/blob/main/docs/integrate/sdk/adapters/nextjs.mdx
Implements a Next.js API route for the Polar customer portal. It requires an access token and a function to resolve the customer's Polar ID. Options for return URL and environment are available.
```typescript
// portal/route.ts
import { CustomerPortal } from "@polar-sh/nextjs";
export const GET = CustomerPortal({
accessToken: process.env.POLAR_ACCESS_TOKEN,
getCustomerId: (req: NextRequest) => "", // Function to resolve a Polar Customer ID
returnUrl: "https://myapp.com", // An optional URL which renders a back-button in the Customer Portal
server: "sandbox", // Use sandbox if you're testing Polar - omit the parameter or pass 'production' otherwise
});
```### Configure Portal Plugin for Customer Management
Source: https://github.com/better-auth/better-auth/blob/canary/docs/content/docs/plugins/polar.mdx
Initialize the Portal plugin alongside Checkout to enable customer self-service management of purchases, orders, subscriptions, and benefits through the Polar Customer Portal.
```typescript
import { polar, checkout, portal } from "@polar-sh/better-auth";
const auth = betterAuth({
// ... Better Auth config
plugins: [
polar({
...
use: [
checkout(...),
portal()
],
})
]
});
```
--------------------------------
### Configure Polar Webhooks Plugin with Event Handlers
Source: https://github.com/better-auth/better-auth/blob/canary/docs/content/docs/plugins/polar.mdx
Sets up webhook handlers for Polar events including customer state changes and order payments. Supports 25+ granular event handlers plus a catch-all onPayload handler. Requires POLAR_WEBHOOK_SECRET environment variable and endpoint configuration at /polar/webhooks.
```typescript
import { polar, webhooks } from "@polar-sh/better-auth";
const auth = betterAuth({
// ... Better Auth config
plugins: [
polar({
...
use: [
webhooks({
secret: process.env.POLAR_WEBHOOK_SECRET,
onCustomerStateChanged: (payload) => // Triggered when anything regarding a customer changes
onOrderPaid: (payload) => // Triggered when an order was paid (purchase, subscription renewal, etc.)
... // Over 25 granular webhook handlers
onPayload: (payload) => // Catch-all for all events
})
],
})
]
});
```
--------------------------------
### Configure Checkout Plugin in Better-Auth
Source: https://github.com/better-auth/better-auth/blob/canary/docs/content/docs/plugins/polar.mdx
Initialize the Checkout plugin in Better-Auth configuration with optional product slugs and checkout success URL. This enables users to initiate checkout sessions with Polar products.
```typescript
import { polar, checkout } from "@polar-sh/better-auth";
const auth = betterAuth({
// ... Better Auth config
plugins: [
polar({
...
use: [
checkout({
// Optional field - will make it possible to pass a slug to checkout instead of Product ID
products: [ { productId: "123-456-789", slug: "pro" } ],
// Relative URL to return to when checkout is successfully completed
successUrl: "/success?checkout_id={CHECKOUT_ID}",
// Whether you want to allow unauthenticated checkout sessions or not
authenticatedUsersOnly: true
})
],
})
]
});
```
--------------------------------
### Redirect User to Polar Customer Portal
Source: https://github.com/better-auth/better-auth/blob/canary/docs/content/docs/plugins/polar.mdx
Navigate the authenticated user to the Polar Customer Portal where they can manage orders, purchases, subscriptions, benefits, and view their account details.
```typescript
await authClient.customer.portal();
```
### Configuring BetterAuth Server > Core Plugins
Source: https://github.com/better-auth/better-auth/blob/canary/docs/content/docs/plugins/polar.mdx
The Polar plugin comes with multiple additional plugins that extend functionality. The Checkout plugin enables seamless checkout integration with configurable product mappings and success URLs. The Portal plugin allows customers to manage their orders, subscriptions, and granted benefits. The Usage plugin provides a simple extension for listing customer meters and ingesting events for usage-based billing. The Webhooks plugin listens for relevant Polar events with over 25 granular webhook handlers including onCustomerStateChanged, onOrderPaid, and a catch-all onPayload handler.Planning targeted documentation queries
### Configure Better Auth Server with Polar Plugin
Source: https://github.com/better-auth/better-auth/blob/canary/docs/content/docs/plugins/polar.mdx
Initializes the Better Auth server with the Polar plugin, integrating various Polar functionalities like checkout, customer portal, usage tracking, and webhook handling. It accepts a Polar SDK client instance, supports automatic customer creation on signup, and allows for custom customer creation parameters.
```typescript
import { betterAuth } from "better-auth";
import { polar, checkout, portal, usage, webhooks } from "@polar-sh/better-auth";
import { Polar } from "@polar-sh/sdk";
const polarClient = new Polar({
accessToken: process.env.POLAR_ACCESS_TOKEN,
// Use 'sandbox' if you're using the Polar Sandbox environment
// Remember that access tokens, products, etc. are completely separated between environments.
// Access tokens obtained in Production are for instance not usable in the Sandbox environment.
server: 'sandbox'
});
const auth = betterAuth({
// ... Better Auth config
plugins: [
polar({
client: polarClient,
createCustomerOnSignUp: true,
getCustomerCreateParams: ({ user }, request) => ({
metadata: {
myCustomProperty: 123
}
}),
use: [
checkout({
products: [
{
productId: "123-456-789", // ID of Product from Polar Dashboard
slug: "pro" // Custom slug for easy reference in Checkout URL, e.g. /checkout/pro
}
],
successUrl: "/success?checkout_id={CHECKOUT_ID}",
authenticatedUsersOnly: true
}),
portal(),
usage(),
webhooks({
secret: process.env.POLAR_WEBHOOK_SECRET,
onCustomerStateChanged: (payload) => null, // Triggered when anything regarding a customer changes
onOrderPaid: (payload) => null, // Triggered when an order was paid (purchase, subscription renewal, etc.)
// ... // Over 25 granular webhook handlers
onPayload: (payload) => null // Catch-all for all events
})
]
})
]
});
```
--------------------------------
### Configure Polar Webhooks Plugin with Event Handlers
Source: https://github.com/better-auth/better-auth/blob/canary/docs/content/docs/plugins/polar.mdx
Sets up webhook handlers for Polar events including customer state changes and order payments. Supports 25+ granular event handlers plus a catch-all onPayload handler. Requires POLAR_WEBHOOK_SECRET environment variable and endpoint configuration at /polar/webhooks.
```typescript
import { polar, webhooks } from "@polar-sh/better-auth";
const auth = betterAuth({
// ... Better Auth config
plugins: [
polar({
...
use: [
webhooks({
secret: process.env.POLAR_WEBHOOK_SECRET,
onCustomerStateChanged: (payload) => // Triggered when anything regarding a customer changes
onOrderPaid: (payload) => // Triggered when an order was paid (purchase, subscription renewal, etc.)
... // Over 25 granular webhook handlers
onPayload: (payload) => // Catch-all for all events
})
],
})
]
});
```
--------------------------------
### Configure Checkout Plugin in Better-Auth
Source: https://github.com/better-auth/better-auth/blob/canary/docs/content/docs/plugins/polar.mdx
Initialize the Checkout plugin in Better-Auth configuration with optional product slugs and checkout success URL. This enables users to initiate checkout sessions with Polar products.
```typescript
import { polar, checkout } from "@polar-sh/better-auth";
const auth = betterAuth({
// ... Better Auth config
plugins: [
polar({
...
use: [
checkout({
// Optional field - will make it possible to pass a slug to checkout instead of Product ID
products: [ { productId: "123-456-789", slug: "pro" } ],
// Relative URL to return to when checkout is successfully completed
successUrl: "/success?checkout_id={CHECKOUT_ID}",
// Whether you want to allow unauthenticated checkout sessions or not
authenticatedUsersOnly: true
})
],
})
]
});
```
--------------------------------
### Install Better Auth Polar Plugin and SDK
Source: https://github.com/better-auth/better-auth/blob/canary/docs/content/docs/plugins/polar.mdx
Installs the required `better-auth`, `@polar-sh/better-auth` (the Polar plugin), and `@polar-sh/sdk` packages using pnpm to enable Polar integration with Better Auth.
```bash
pnpm add better-auth @polar-sh/better-auth @polar-sh/sdk
```
--------------------------------
### Set Polar Access Token Environment Variable
Source: https://github.com/better-auth/better-auth/blob/canary/docs/content/docs/plugins/polar.mdx
Configures the `POLAR_ACCESS_TOKEN` environment variable with a Polar Organization Access Token. This token is essential for the server-side Better Auth instance to securely communicate with the Polar API.
```bash
# .env
POLAR_ACCESS_TOKEN=[REDACTED]
```### POST /v1/checkouts/
Source: https://context7.com/polarsource/polar/llms.txt
Create a checkout session for customers to complete purchases with customizable success URLs and metadata.
```APIDOC
## POST /v1/checkouts/
### Description
Create a checkout session for customers to complete purchases with customizable success URLs and metadata.
### Method
POST
### Endpoint
/v1/checkouts/
### Parameters
#### Request Body
- **product_price_id** (string) - Required - The ID of the product price to include in the checkout.
- **success_url** (string) - Optional - The URL to redirect the customer to after a successful purchase.
- **customer_email** (string) - Optional - The email address of the customer.
- **customer_name** (string) - Optional - The name of the customer.
- **metadata** (object) - Optional - Key-value pairs for additional checkout information.
- **allow_discount_codes** (boolean) - Optional - Whether to allow discount codes to be applied.
### Request Example
```json
{
"product_price_id": "750e8400-e29b-41d4-a716-446655440002",
"success_url": "https://example.com/success",
"customer_email": "customer@example.com",
"customer_name": "John Doe",
"metadata": {
"campaign": "winter_sale",
"referrer": "partner_site"
},
"allow_discount_codes": true
}
```
### Response
#### Success Response (200)
- **id** (string) - The unique identifier of the checkout session.
- **client_secret** (string) - A secret used to confirm the checkout session.
- **url** (string) - The URL to redirect the customer to for checkout.
- **status** (string) - The current status of the checkout session (e.g., "open", "confirmed", "completed", "expired").
- **expires_at** (string) - The timestamp when the checkout session expires.
- **payment_processor** (string) - The payment processor used (e.g., "stripe").
- **amount** (integer) - The total amount of the checkout in the smallest currency unit.
- **tax_amount** (integer) - The tax amount in the smallest currency unit.
- **currency** (string) - The currency of the checkout (e.g., "usd").
- **product_price** (object) - Details about the product price.
- **id** (string) - The ID of the product price.
- **product_id** (string) - The ID of the product.
#### Response Example
```json
{
"id": "d50e8400-e29b-41d4-a716-446655440009",
"client_secret": "checkout_secret_xxxxxxxxxxxxx",
"url": "https://polar.sh/checkout/d50e8400-e29b-41d4-a716-446655440009",
"status": "open",
"expires_at": "2025-01-15T15:00:00Z",
"payment_processor": "stripe",
"amount": 2900,
"tax_amount": 290,
"currency": "usd",
"product_price": {
"id": "750e8400-e29b-41d4-a716-446655440002",
"product_id": "650e8400-e29b-41d4-a716-446655440001"
}
}
```
```
--------------------------------
### Create Checkout Session using Checkout Service in Python
Source: https://context7.com/polarsource/polar/llms.txt
Details the process of creating a checkout session for a product price. This function requires a database session, the product price ID, and the customer's email address. It sets up checkout parameters like success URL, discount code allowance, and custom metadata. The output is the created checkout session object.
```python
from polar.checkout.service import checkout as checkout_service
from polar.session import AsyncSession
from polar.auth.models import AuthSubject
from polar.models.checkout import CheckoutCreate
from uuid import UUID
async def create_checkout_example(
session: AsyncSession,
auth_subject: AuthSubject, # Assuming auth_subject is available and needed for authorization
product_price_id: UUID,
customer_email: str
):
"""Create a checkout session."""
checkout_create = CheckoutCreate(
product_price_id=product_price_id,
customer_email=customer_email,
success_url="https://example.com/success",
allow_discount_codes=True,
metadata={"campaign": "spring_sale"}
)
checkout = await checkout_service.create(
session=session,
auth_subject=auth_subject,
checkout_create=checkout_create
)
await session.commit()
return checkout
```
--------------------------------
### Generate Polar Checkout Sessions in Next.js
Source: https://github.com/polarsource/polar/blob/main/docs/guides/nextjs.mdx
This code sets up a GET route in Next.js to generate Polar checkout sessions. It requires the Polar access token and allows specifying success and cancellation URLs, along with the server environment (sandbox/production).
```typescript
// src/app/checkout/route.ts
import { Checkout } from "@polar-sh/nextjs";
export const GET = Checkout({
accessToken: process.env.POLAR_ACCESS_TOKEN!,
successUrl: "/confirmation?checkout_id={CHECKOUT_ID}",
server: "sandbox", // Use this option if you're using the sandbox environment - else use 'production' or omit the parameter
});
```
--------------------------------
### Create Checkout Session for Seats (TypeScript)
Source: https://github.com/polarsource/polar/blob/main/docs/guides/seat-based-pricing.mdx
This snippet demonstrates creating a checkout session for a seat-based product using the Polar SDK. It allows a customer to specify the number of seats they wish to purchase, and the SDK automatically calculates the total price based on the defined tiers. It requires a product price ID and the desired number of seats, returning a checkout session object with a URL for redirection. The '@polar-sh/sdk' is a dependency.
```typescript
const checkout = await polar.checkouts.create({
product_price_id: "price_123",
seats: 5, // Customer selects quantity
success_url: "https://yourapp.com/success",
customer_email: "billing@company.com"
});
// Redirect to checkout.url
```
--------------------------------
### React Query: Integrate Checkout
Source: https://context7.com/polarsource/polar/llms.txt
Enables embedding checkout sessions within an application by using a hook to create checkout sessions. Redirects the user to the Polar checkout URL upon successful creation. Dependencies include '@/hooks/queries/checkouts'.
```typescript
import { useCreateCheckout } from '@/hooks/queries/checkouts'
function CheckoutButton({ productPriceId }: { productPriceId: string }) {
const createCheckout = useCreateCheckout()
const handleCheckout = async () => {
const result = await createCheckout.mutateAsync({
product_price_id: productPriceId,
success_url: 'https://myapp.com/success',
customer_email: 'customer@example.com',
metadata: { source: 'app_checkout' }
})
if (!result.error) {
const checkoutUrl = `https://polar.sh/checkout/${result.data.id}/public/${result.data.client_secret}`
window.location.href = checkoutUrl
}
}
return <button onClick={handleCheckout}>Purchase Now</button>
}
```Planning docs for auth client
### Redirect User to Polar Customer Portal
Source: https://github.com/better-auth/better-auth/blob/canary/docs/content/docs/plugins/polar.mdx
Navigate the authenticated user to the Polar Customer Portal where they can manage orders, purchases, subscriptions, benefits, and view their account details.
```typescript
await authClient.customer.portal();
```
--------------------------------
### Initialize Checkout Session with Product ID or Slug
Source: https://github.com/better-auth/better-auth/blob/canary/docs/content/docs/plugins/polar.mdx
Trigger a checkout session using the authClient.checkout() method. Supports passing Polar Product IDs directly or predefined product slugs from configuration. Automatically includes authenticated user as customer.
```typescript
await authClient.checkout({
// Any Polar Product ID can be passed here
products: ["e651f46d-ac20-4f26-b769-ad088b123df2"],
// Or, if you setup "products" in the Checkout Config, you can pass the slug
slug: "pro",
});
```
--------------------------------
### Configure Portal Plugin for Customer Management
Source: https://github.com/better-auth/better-auth/blob/canary/docs/content/docs/plugins/polar.mdx
Initialize the Portal plugin alongside Checkout to enable customer self-service management of purchases, orders, subscriptions, and benefits through the Polar Customer Portal.
```typescript
import { polar, checkout, portal } from "@polar-sh/better-auth";
const auth = betterAuth({
// ... Better Auth config
plugins: [
polar({
...
use: [
checkout(...),
portal()
],
})
]
});
```
--------------------------------
### Configure Better Auth Server with Polar Plugin
Source: https://github.com/better-auth/better-auth/blob/canary/docs/content/docs/plugins/polar.mdx
Initializes the Better Auth server with the Polar plugin, integrating various Polar functionalities like checkout, customer portal, usage tracking, and webhook handling. It accepts a Polar SDK client instance, supports automatic customer creation on signup, and allows for custom customer creation parameters.
```typescript
import { betterAuth } from "better-auth";
import { polar, checkout, portal, usage, webhooks } from "@polar-sh/better-auth";
import { Polar } from "@polar-sh/sdk";
const polarClient = new Polar({
accessToken: process.env.POLAR_ACCESS_TOKEN,
// Use 'sandbox' if you're using the Polar Sandbox environment
// Remember that access tokens, products, etc. are completely separated between environments.
// Access tokens obtained in Production are for instance not usable in the Sandbox environment.
server: 'sandbox'
});
const auth = betterAuth({
// ... Better Auth config
plugins: [
polar({
client: polarClient,
createCustomerOnSignUp: true,
getCustomerCreateParams: ({ user }, request) => ({
metadata: {
myCustomProperty: 123
}
}),
use: [
checkout({
products: [
{
productId: "123-456-789", // ID of Product from Polar Dashboard
slug: "pro" // Custom slug for easy reference in Checkout URL, e.g. /checkout/pro
}
],
successUrl: "/success?checkout_id={CHECKOUT_ID}",
authenticatedUsersOnly: true
}),
portal(),
usage(),
webhooks({
secret: process.env.POLAR_WEBHOOK_SECRET,
onCustomerStateChanged: (payload) => null, // Triggered when anything regarding a customer changes
onOrderPaid: (payload) => null, // Triggered when an order was paid (purchase, subscription renewal, etc.)
// ... // Over 25 granular webhook handlers
onPayload: (payload) => null // Catch-all for all events
})
]
})
]
});
```
--------------------------------
### List Authenticated User Subscriptions
Source: https://github.com/better-auth/better-auth/blob/canary/docs/content/docs/plugins/polar.mdx
Retrieves subscriptions for the authenticated user with pagination and filtering options. Supports passing a referenceId to check organization subscriptions instead of user subscriptions, useful for determining organization access.
```typescript
const { data: subscriptions } = await authClient.customer.subscriptions.list({
query: {
page: 1,
limit: 10,
active: true,
},
});
```Considering docs resolution strategy
[
{
"content": "Fetch Context7 docs for Polar and Better Auth integration requirements",
"status": "completed",
"priority": "high"
},
{
"content": "Draft kebab-case slug and actionable implementation plan content",
"status": "in_progress",
"priority": "high"
},
{
"content": "Run create-plan script and verify generated file in .agents/plans",
"status": "pending",
"priority": "high"
}
]Planning repo context gathering
Found 18 match(es) in 14 file(s)
/Users/duncan/dev/bmrks/components/landing.tsx
15: import { LandingPricing } from "@/components/landing-pricing";
/Users/duncan/dev/bmrks/lib/auth-client.ts
1: import { createAuthClient } from "better-auth/react";
/Users/duncan/dev/bmrks/components/ui/context-menu.tsx
15: <ContextMenuPrimitive.Portal data-slot="context-menu-portal" {...props} />
/Users/duncan/dev/bmrks/lib/auth.ts
1: import { betterAuth } from "better-auth";
2: import { createAuthMiddleware } from "better-auth/api";
3: import { prismaAdapter } from "better-auth/adapters/prisma";
4: import { nextCookies } from "better-auth/next-js";
/Users/duncan/dev/bmrks/components/ui/drawer.tsx
23: return <DrawerPrimitive.Portal data-slot="drawer-portal" {...props} />
51: <DrawerPortal data-slot="drawer-portal">
/Users/duncan/dev/bmrks/package.json
35: "better-auth": "^1.4.1",
/Users/duncan/dev/bmrks/components/ui/dropdown-menu.tsx
14: return <MenuPrimitive.Portal data-slot="dropdown-menu-portal" {...props} />
/Users/duncan/dev/bmrks/components/ui/sheet.tsx
23: return <SheetPrimitive.Portal data-slot="sheet-portal" {...props} />
/Users/duncan/dev/bmrks/components/ui/alert-dialog.tsx
21: <AlertDialogPrimitive.Portal data-slot="alert-dialog-portal" {...props} />
/Users/duncan/dev/bmrks/components/ui/hover-card.tsx
30: <PreviewCardPrimitive.Portal data-slot="hover-card-portal">
/Users/duncan/dev/bmrks/app/api/auth/[...all]/route.ts
2: import { toNextJsHandler } from "better-auth/next-js";
/Users/duncan/dev/bmrks/components/ui/chart.tsx
58: "[&_.recharts-cartesian-axis-tick_text]:fill-muted-foreground [&_.recharts-cartesian-grid_line[stroke='#ccc']]:stroke-border/50 [&_.recharts-curve.recharts-tooltip-cursor]:stroke-border [&_.recharts-polar-grid_[stroke='#ccc']]:stroke-border [&_.recharts-radial-bar-background-sector]:fill-muted [&_.recharts-rectangle.recharts-tooltip-cursor]:fill-muted [&_.recharts-reference-line_[stroke='#ccc']]:stroke-border flex aspect-video justify-center text-xs [&_.recharts-dot[stroke='#fff']]:stroke-transparent [&_.recharts-layer]:outline-hidden [&_.recharts-sector]:outline-hidden [&_.recharts-sector[stroke='#fff']]:stroke-transparent [&_.recharts-surface]:outline-hidden",
/Users/duncan/dev/bmrks/components/ui/dialog.tsx
19: return <DialogPrimitive.Portal data-slot="dialog-portal" {...props} />
/Users/duncan/dev/bmrks/components/ui/menubar.tsx
48: return <DropdownMenuPortal data-slot="menubar-portal" {...props} />
[Agent Usage Reminder]
You called a search/fetch tool directly without leveraging specialized agents.
RECOMMENDED: Use task with explore/librarian agents for better results:
```
// Parallel exploration - fire multiple agents simultaneously
task(agent="explore", prompt="Find all files matching pattern X")
task(agent="explore", prompt="Search for implementation of Y")
task(agent="librarian", prompt="Lookup documentation for Z")
// Then continue your work while they run in background
// System will notify you when each completes
```
WHY:
- Agents can perform deeper, more thorough searches
- Background tasks run in parallel, saving time
- Specialized agents have domain expertise
- Reduces context window usage in main session
ALWAYS prefer: Multiple parallel task calls > Direct tool calls
Planning UI inspection
I pulled the relevant Polar + Better Auth docs and now I’m mapping that into this repo’s existing landing/pricing/auth structure so the plan file is implementation-ready.
Found 133 match(es) in 20 file(s)
/Users/duncan/dev/bmrks/components/landing-pricing.tsx
137: const [billingCycle, setBillingCycle] = useState<BillingCycle>("yearly");
138: const selectedPricing = PRO_PLAN_PRICING[billingCycle];
149: Choose the plan that fits your workflow.
152: <BillingToggle value={billingCycle} onChange={setBillingCycle} />
/Users/duncan/dev/bmrks/components/settings-dialog.tsx
20: import type { ProfileData } from "@/components/dashboard-content";
/Users/duncan/dev/bmrks/components/bulk-move-dialog.tsx
18: } from "@/components/ui/dropdown-menu";
/Users/duncan/dev/bmrks/components/signup-form.tsx
80: router.push("/dashboard");
/Users/duncan/dev/bmrks/components/multi-select-toolbar.tsx
10: } from "@/components/ui/dropdown-menu";
/Users/duncan/dev/bmrks/components/login-form.tsx
70: router.push("/dashboard");
/Users/duncan/dev/bmrks/components/landing.tsx
14: import { DashboardDemoLazy as DashboardDemo } from "@/components/dashboard-demo-lazy";
15: import { LandingPricing } from "@/components/landing-pricing";
/Users/duncan/dev/bmrks/components/header.tsx
12: } from "@/components/ui/dropdown-menu";
54: import type { ProfileData } from "@/components/dashboard-content";
/Users/duncan/dev/bmrks/components/oauth-button.tsx
32: callbackURL: "/dashboard",
/Users/duncan/dev/bmrks/components/bookmark-list.tsx
12: } from "@/components/ui/context-menu";
29: import { ContextMenuSeparator } from "@/components/ui/context-menu";
/Users/duncan/dev/bmrks/components/dashboard-demo-lazy.tsx
6: () => import("@/components/dashboard-demo").then((m) => m.DashboardDemo),
/Users/duncan/dev/bmrks/components/export-dialog.tsx
18: } from "@/components/ui/dropdown-menu";
/Users/duncan/dev/bmrks/components/dashboard-content.tsx
12: import { BookmarkListSkeleton } from "@/components/dashboard-skeleton";
/Users/duncan/dev/bmrks/components/ui/context-menu.tsx
4: import { ContextMenu as ContextMenuPrimitive } from "@base-ui/react/context-menu"
10: return <ContextMenuPrimitive.Root data-slot="context-menu" {...props} />
15: <ContextMenuPrimitive.Portal data-slot="context-menu-portal" {...props} />
25: data-slot="context-menu-trigger"
54: data-slot="context-menu-content"
65: <ContextMenuPrimitive.Group data-slot="context-menu-group" {...props} />
78: data-slot="context-menu-label"
97: data-slot="context-menu-item"
101: "focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/20 data-[variant=destructive]:focus:text-destructive data-[variant=destructive]:*:[svg]:text-destructive focus:*:[svg]:text-accent-foreground gap-1.5 rounded-md px-1.5 py-1 text-sm [&_svg:not([class*='size-'])]:size-4 group/context-menu-item relative flex cursor-default items-center outline-hidden select-none data-disabled:pointer-events-none data-disabled:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0",
109: function ContextMenuSub({ ...props }: ContextMenuPrimitive.SubmenuRoot.Props) {
111: <ContextMenuPrimitive.SubmenuRoot data-slot="context-menu-sub" {...props} />
120: }: ContextMenuPrimitive.SubmenuTrigger.Props & {
124: <ContextMenuPrimitive.SubmenuTrigger
125: data-slot="context-menu-sub-trigger"
135: </ContextMenuPrimitive.SubmenuTrigger>
144: data-slot="context-menu-sub-content"
160: data-slot="context-menu-checkbox-item"
184: data-slot="context-menu-radio-group"
197: data-slot="context-menu-radio-item"
221: data-slot="context-menu-separator"
234: data-slot="context-menu-shortcut"
235: className={cn("text-muted-foreground group-focus/context-menu-item:text-accent-foreground ml-auto text-xs tracking-widest", className)}
/Users/duncan/dev/bmrks/components/ui/navigation-menu.tsx
1: import { NavigationMenu as NavigationMenuPrimitive } from "@base-ui/react/navigation-menu"
14: data-slot="navigation-menu"
16: "max-w-max group/navigation-menu relative flex max-w-max flex-1 items-center justify-center",
33: data-slot="navigation-menu-list"
49: data-slot="navigation-menu-item"
57: "bg-background hover:bg-muted focus:bg-muted data-open:hover:bg-muted data-open:focus:bg-muted data-open:bg-muted/50 focus-visible:ring-ring/50 data-popup-open:bg-muted/50 data-popup-open:hover:bg-muted rounded-lg px-2.5 py-1.5 text-sm font-medium transition-all focus-visible:ring-[3px] focus-visible:outline-1 disabled:opacity-50 group/navigation-menu-trigger inline-flex h-9 w-max items-center justify-center disabled:pointer-events-none outline-none"
67: data-slot="navigation-menu-trigger"
72: <IconChevronDown className="relative top-[1px] ml-1 size-3 transition duration-300 group-data-open/navigation-menu-trigger:rotate-180 group-data-popup-open/navigation-menu-trigger:rotate-180" aria-hidden="true" />
83: data-slot="navigation-menu-content"
85: [Omitted long matching line]
128: data-slot="navigation-menu-link"
129: className={cn("data-active:focus:bg-muted data-active:hover:bg-muted data-active:bg-muted/50 focus-visible:ring-ring/50 hover:bg-muted focus:bg-muted flex items-center gap-2 rounded-lg p-2 text-sm transition-all outline-none focus-visible:ring-[3px] focus-visible:outline-1 [&_svg:not([class*='size-'])]:size-4 [[data-slot=navigation-menu-content]_&]:rounded-md", className)}
141: data-slot="navigation-menu-indicator"
/Users/duncan/dev/bmrks/components/ui/calendar.tsx
68: dropdowns: cn(
70: defaultClassNames.dropdowns
72: dropdown_root: cn(
73: "relative cn-calendar-dropdown-root rounded-(--cell-radius)",
74: defaultClassNames.dropdown_root
76: dropdown: cn(
78: defaultClassNames.dropdown
/Users/duncan/dev/bmrks/components/ui/menubar.tsx
4: import { Menu as MenuPrimitive } from "@base-ui/react/menu"
5: import { Menubar as MenubarPrimitive } from "@base-ui/react/menubar"
22: } from "@/components/ui/dropdown-menu"
28: data-slot="menubar"
36: return <DropdownMenu data-slot="menubar-menu" {...props} />
42: return <DropdownMenuGroup data-slot="menubar-group" {...props} />
48: return <DropdownMenuPortal data-slot="menubar-portal" {...props} />
57: data-slot="menubar-trigger"
76: data-slot="menubar-content"
94: data-slot="menubar-item"
97: className={cn("focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/20 data-[variant=destructive]:focus:text-destructive data-[variant=destructive]:*:[svg]:!text-destructive not-data-[variant=destructive]:focus:**:text-accent-foreground gap-1.5 rounded-md px-1.5 py-1 text-sm data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg:not([class*='size-'])]:size-4 group/menubar-item", className)}
111: data-slot="menubar-checkbox-item"
133: return <DropdownMenuRadioGroup data-slot="menubar-radio-group" {...props} />
143: data-slot="menubar-radio-item"
168: data-slot="menubar-label"
182: data-slot="menubar-separator"
195: data-slot="menubar-shortcut"
196: className={cn("text-muted-foreground group-focus/menubar-item:text-accent-foreground text-xs tracking-widest ml-auto", className)}
205: return <DropdownMenuSub data-slot="menubar-sub" {...props} />
217: data-slot="menubar-sub-trigger"
231: data-slot="menubar-sub-content"
/Users/duncan/dev/bmrks/components/ui/dropdown-menu.tsx
4: import { Menu as MenuPrimitive } from "@base-ui/react/menu"
10: return <MenuPrimitive.Root data-slot="dropdown-menu" {...props} />
14: return <MenuPrimitive.Portal data-slot="dropdown-menu-portal" {...props} />
18: return <MenuPrimitive.Trigger data-slot="dropdown-menu-trigger" {...props} />
43: data-slot="dropdown-menu-content"
53: return <MenuPrimitive.Group data-slot="dropdown-menu-group" {...props} />
65: data-slot="dropdown-menu-label"
84: data-slot="dropdown-menu-item"
88: "focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/20 data-[variant=destructive]:focus:text-destructive data-[variant=destructive]:*:[svg]:text-destructive not-data-[variant=destructive]:focus:**:text-accent-foreground gap-1.5 rounded-md px-1.5 py-1 text-sm [&_svg:not([class*='size-'])]:size-4 group/dropdown-menu-item relative flex cursor-default items-center outline-hidden select-none data-disabled:pointer-events-none data-disabled:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0",
96: function DropdownMenuSub({ ...props }: MenuPrimitive.SubmenuRoot.Props) {
97: return <MenuPrimitive.SubmenuRoot data-slot="dropdown-menu-sub" {...props} />
105: }: MenuPrimitive.SubmenuTrigger.Props & {
109: <MenuPrimitive.SubmenuTrigger
110: data-slot="dropdown-menu-sub-trigger"
120: </MenuPrimitive.SubmenuTrigger>
134: data-slot="dropdown-menu-sub-content"
153: data-slot="dropdown-menu-checkbox-item"
163: data-slot="dropdown-menu-checkbox-item-indicator"
178: data-slot="dropdown-menu-radio-group"
191: data-slot="dropdown-menu-radio-item"
200: data-slot="dropdown-menu-radio-item-indicator"
218: data-slot="dropdown-menu-separator"
231: data-slot="dropdown-menu-shortcut"
232: className={cn("text-muted-foreground group-focus/dropdown-menu-item:text-accent-foreground ml-auto text-xs tracking-widest", className)}
/Users/duncan/dev/bmrks/components/ui/item.tsx
49: xs: "gap-2 px-2.5 py-2 [[data-slot=dropdown-menu-content]_&]:p-0",
/Users/duncan/dev/bmrks/components/ui/sidebar.tsx
462: data-slot="sidebar-menu"
463: data-sidebar="menu"
473: data-slot="sidebar-menu-item"
474: data-sidebar="menu-item"
475: className={cn("group/menu-item relative", className)}
482: "ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground active:bg-sidebar-accent active:text-sidebar-accent-foreground data-active:bg-sidebar-accent data-active:text-sidebar-accent-foreground data-open:hover:bg-sidebar-accent data-open:hover:text-sidebar-accent-foreground gap-2 rounded-md p-2 text-left text-sm transition-[width,height,padding] group-has-data-[sidebar=menu-action]/menu-item:pr-8 group-data-[collapsible=icon]:size-8! group-data-[collapsible=icon]:p-2! focus-visible:ring-2 data-active:font-medium peer/menu-button flex w-full items-center overflow-hidden outline-hidden group/menu-button disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 [&>span:last-child]:truncate [&_svg]:size-4 [&_svg]:shrink-0",
526: slot: "sidebar-menu-button",
527: sidebar: "menu-button",
570: "text-sidebar-foreground ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground peer-hover/menu-button:text-sidebar-accent-foreground absolute top-1.5 right-1 aspect-square w-5 rounded-md p-0 peer-data-[size=default]/menu-button:top-1.5 peer-data-[size=lg]/menu-button:top-2.5 peer-data-[size=sm]/menu-button:top-1 focus-visible:ring-2 [&>svg]:size-4 flex items-center justify-center outline-hidden transition-transform group-data-[collapsible=icon]:hidden after:absolute after:-inset-2 md:after:hidden [&>svg]:shrink-0",
572: "peer-data-active/menu-button:text-sidebar-accent-foreground group-focus-within/menu-item:opacity-100 group-hover/menu-item:opacity-100 data-open:opacity-100 md:opacity-0",
580: slot: "sidebar-menu-action",
581: sidebar: "menu-action",
592: data-slot="sidebar-menu-badge"
593: data-sidebar="menu-badge"
595: "text-sidebar-foreground peer-hover/menu-button:text-sidebar-accent-foreground peer-data-active/menu-button:text-sidebar-accent-foreground pointer-events-none absolute right-1 flex h-5 min-w-5 rounded-md px-1 text-xs font-medium peer-data-[size=default]/menu-button:top-1.5 peer-data-[size=lg]/menu-button:top-2.5 peer-data-[size=sm]/menu-button:top-1 flex items-center justify-center tabular-nums select-none group-data-[collapsible=icon]:hidden",
617: data-slot="sidebar-menu-skeleton"
618: data-sidebar="menu-skeleton"
625: data-sidebar="menu-skeleton-icon"
630: data-sidebar="menu-skeleton-text"
644: data-slot="sidebar-menu-sub"
645: data-sidebar="menu-sub"
658: data-slot="sidebar-menu-sub-item"
659: data-sidebar="menu-sub-item"
660: className={cn("group/menu-sub-item relative", className)}
690: slot: "sidebar-menu-sub-button",
691: sidebar: "menu-sub-button",
[Agent Usage Reminder]
You called a search/fetch tool directly without leveraging specialized agents.
RECOMMENDED: Use task with explore/librarian agents for better results:
```
// Parallel exploration - fire multiple agents simultaneously
task(agent="explore", prompt="Find all files matching pattern X")
task(agent="explore", prompt="Search for implementation of Y")
task(agent="librarian", prompt="Lookup documentation for Z")
// Then continue your work while they run in background
// System will notify you when each completes
```
WHY:
- Agents can perform deeper, more thorough searches
- Background tasks run in parallel, saving time
- Specialized agents have domain expertise
- Reduces context window usage in main session
ALWAYS prefer: Multiple parallel task calls > Direct tool calls
Identifying key files to read
Inspecting landing component for anchors
Exploring subscription data models
Found 5 match(es) in 2 file(s)
/Users/duncan/dev/bmrks/components/landing-pricing.tsx
137: const [billingCycle, setBillingCycle] = useState<BillingCycle>("yearly");
138: const selectedPricing = PRO_PLAN_PRICING[billingCycle];
149: Choose the plan that fits your workflow.
152: <BillingToggle value={billingCycle} onChange={setBillingCycle} />
/Users/duncan/dev/bmrks/components/ui/chart.tsx
58: "[&_.recharts-cartesian-axis-tick_text]:fill-muted-foreground [&_.recharts-cartesian-grid_line[stroke='#ccc']]:stroke-border/50 [&_.recharts-curve.recharts-tooltip-cursor]:stroke-border [&_.recharts-polar-grid_[stroke='#ccc']]:stroke-border [&_.recharts-radial-bar-background-sector]:fill-muted [&_.recharts-rectangle.recharts-tooltip-cursor]:fill-muted [&_.recharts-reference-line_[stroke='#ccc']]:stroke-border flex aspect-video justify-center text-xs [&_.recharts-dot[stroke='#fff']]:stroke-transparent [&_.recharts-layer]:outline-hidden [&_.recharts-sector]:outline-hidden [&_.recharts-sector[stroke='#fff']]:stroke-transparent [&_.recharts-surface]:outline-hidden",
[Agent Usage Reminder]
You called a search/fetch tool directly without leveraging specialized agents.
RECOMMENDED: Use task with explore/librarian agents for better results:
```
// Parallel exploration - fire multiple agents simultaneously
task(agent="explore", prompt="Find all files matching pattern X")
task(agent="explore", prompt="Search for implementation of Y")
task(agent="librarian", prompt="Lookup documentation for Z")
// Then continue your work while they run in background
// System will notify you when each completes
```
WHY:
- Agents can perform deeper, more thorough searches
- Background tasks run in parallel, saving time
- Specialized agents have domain expertise
- Reduces context window usage in main session
ALWAYS prefer: Multiple parallel task calls > Direct tool calls
Checking billing dependencies
Drafting concise action plan
### Generate Prisma Schema with Better Auth CLI Source: https://github.com/better-auth/better-auth/blob/canary/docs/content/docs/adapters/prisma.mdx Command-line interface command to automatically generate or update Prisma database schema based on Better Auth configuration and installed plugins. Supports schema generation but not direct migrations. ```bash npx auth@latest generate ``` -------------------------------- ### Generate Better Auth models in Prisma schema Source: https://github.com/better-auth/better-auth/blob/canary/docs/content/docs/integrations/nitro.mdx This command uses the Better Auth CLI to automatically add required authentication models to your `schema.prisma` file, based on the provided Better Auth configuration file. ```bash npx auth generate --config server/utils/auth.ts ``` -------------------------------- ### Manage Better Auth Schemas and Migrations using CLI (Bash) Source: https://context7.com/better-auth/better-auth/llms.txt This section provides command-line interface (CLI) commands for managing Better Auth. It includes commands to generate ORM schemas for popular ORMs like Drizzle and Prisma, apply database migrations specifically for the Kysely adapter, and generate a secure secret key. ```bash # Generate schema for your ORM (Drizzle, Prisma) npx auth@latest generate # Apply migrations directly (Kysely adapter only) npx auth@latest migrate # Generate secret key openssl rand -base64 32 ``` ### Using Plugins > Database Migration Source: https://github.com/better-auth/better-auth/blob/canary/docs/content/docs/basic-usage.mdx After adding a plugin to your Better Auth configuration, you must update your database schema to include the required tables for that plugin. You can accomplish this by running the `migrate` command to automatically apply migrations, or by using the `generate` command to create the schema and handle the migration manually. This ensures your database is properly configured to support the new plugin functionality. -------------------------------- ### Nitro Integration > Update Prisma Schema Source: https://github.com/better-auth/better-auth/blob/canary/docs/content/docs/integrations/nitro.mdx Use the Better Auth CLI to update your Prisma schema with the required models by running the following command: `npx auth generate --config server/utils/auth.ts`. The `--config` flag is used to specify the path to the file where you have created your Better Auth instance. Head over to the `prisma/schema.prisma` file & save the file to trigger the format on save. After saving the file, you can run the `npx prisma db push` command to update the database schema.
Considering plugin client setup
### Configure Portal Plugin for Customer Management
Source: https://github.com/better-auth/better-auth/blob/canary/docs/content/docs/plugins/polar.mdx
Initialize the Portal plugin alongside Checkout to enable customer self-service management of purchases, orders, subscriptions, and benefits through the Polar Customer Portal.
```typescript
import { polar, checkout, portal } from "@polar-sh/better-auth";
const auth = betterAuth({
// ... Better Auth config
plugins: [
polar({
...
use: [
checkout(...),
portal()
],
})
]
});
```
--------------------------------
### Configure Polar Usage Plugin with Better Auth
Source: https://github.com/better-auth/better-auth/blob/canary/docs/content/docs/plugins/polar.mdx
Initializes the Polar plugin with usage-based billing support in Better Auth configuration. Includes checkout, portal, and usage modules for complete billing functionality.
```typescript
import { polar, checkout, portal, usage } from "@polar-sh/better-auth";
const auth = betterAuth({
// ... Better Auth config
plugins: [
polar({
...
use: [
checkout(...),
portal(),
usage()
],
})
]
});
```
--------------------------------
### Redirect User to Polar Customer Portal
Source: https://github.com/better-auth/better-auth/blob/canary/docs/content/docs/plugins/polar.mdx
Navigate the authenticated user to the Polar Customer Portal where they can manage orders, purchases, subscriptions, benefits, and view their account details.
```typescript
await authClient.customer.portal();
```
### Portal Plugin > Benefits, Orders & Subscriptions
Source: https://github.com/better-auth/better-auth/blob/canary/docs/content/docs/plugins/polar.mdx
The portal plugin enhances the BetterAuth Client with three convenient methods specifically for listing benefits, orders, and subscriptions that are relevant to the authenticated user or customer. These methods are built upon and utilize the Polar CustomerPortal APIs, ensuring a consistent and robust way to retrieve customer-specific data. This allows for easy integration of customer-related information directly into your application's UI.
--------------------------------
### Portal Plugin
Source: https://github.com/better-auth/better-auth/blob/canary/docs/content/docs/plugins/polar.mdx
The Portal Plugin is designed to enable customer management of their purchases, orders, and subscriptions directly within your application. You integrate it into your Better Auth configuration alongside other plugins like Checkout. This plugin extends the BetterAuth Client with a dedicated set of customer management methods, conveniently scoped under `authClient.customer`, providing a centralized interface for customer-related operations.Seeking auth client setup info
### Configure Client-Side Plugins with createAuthClient
Source: https://github.com/better-auth/better-auth/blob/canary/docs/content/docs/concepts/plugins.mdx
Initialize client plugins using the createAuthClient function from better-auth/client. Most plugins require both server and client plugins to function correctly.
```typescript
import { createAuthClient } from "better-auth/client";
const authClient = createAuthClient({
plugins: [
// Add your client plugins here
]
});
```
--------------------------------
### Infer Server Plugin Endpoints in Better Auth Client Plugin (TypeScript)
Source: https://github.com/better-auth/better-auth/blob/canary/docs/content/docs/concepts/plugins.mdx
This example shows how a Better Auth client plugin can automatically infer endpoints defined in a corresponding server plugin. By using the `$InferServerPlugin` key and referencing the server plugin's return type, the client can gain type-safe access to server endpoints. It also highlights the automatic conversion of kebab-case paths to camelCase for client-side access.
```ts
import type { BetterAuthClientPlugin } from "better-auth/client";
import type { myPlugin } from "./plugin";
const myPluginClient = () => {
return {
id: "my-plugin",
$InferServerPlugin: {} as ReturnType<typeof myPlugin>,
} satisfies BetterAuthClientPlugin
}
```
--------------------------------
### Initialize Birthday Plugin on Client
Source: https://github.com/better-auth/better-auth/blob/canary/docs/content/docs/guides/your-first-plugin.mdx
Configures the Better Auth client to use the birthday client plugin by importing and adding it to the plugins array in the createAuthClient configuration. This enables client-side access to birthday plugin functionality.
```typescript
import { createAuthClient } from "better-auth/client";
import { birthdayClientPlugin } from "./birthday-plugin/client";
const authClient = createAuthClient({
plugins: [
birthdayClientPlugin()
]
});
```
--------------------------------
### Configure Better Auth Passkey Client Plugin
Source: https://github.com/better-auth/better-auth/blob/canary/packages/passkey/README.md
Initialize the Better Auth client with the passkey plugin to enable passkey authentication on the frontend. This provides client-side methods for passkey registration and authentication.
```typescript
import { createAuthClient } from 'better-auth/client';
import { passkeyClient } from '@better-auth/passkey/client';
export const authClient = createAuthClient({
plugins: [passkeyClient()],
});
```
--------------------------------
### Integrate Better Auth Plugins with Lynx Client
Source: https://github.com/better-auth/better-auth/blob/canary/docs/content/docs/integrations/lynx.mdx
This TypeScript code illustrates how to extend the `createAuthClient` instance by incorporating Better Auth plugins. Plugins, such as `magicLinkClient`, are passed through the `plugins` array in the client configuration, enabling additional authentication methods.
```ts
import { createAuthClient } from "better-auth/lynx"
import { magicLinkClient } from "better-auth/client/plugins"
const authClient = createAuthClient({
plugins: [
magicLinkClient()
]
})
// Use plugin methods
await authClient.signIn.magicLink({
email: "test@email.com"
})
```Checking for polar client plugin
### Configure BetterAuth Client with Polar (TypeScript)
Source: https://github.com/polarsource/polar/blob/main/docs/integrate/sdk/adapters/better-auth.mdx
This code illustrates how to configure the BetterAuth client to interact with Polar functionalities. It utilizes `createAuthClient` and imports the necessary `polarClient` from '@polar-sh/better-auth'.
```typescript
import { createAuthClient } from "better-auth/react";
import { polarClient } from "@polar-sh/better-auth"; // [!code ++]
import { organizationClient } from "better-auth/client/plugins"; // [!code ++]
// All Polar plugins, etc. should be attached to BetterAuth server
export const authClient = createAuthClient({
plugins: [polarClient()], // [!code ++]
});
```
--------------------------------
### Configure and Use Checkout Plugin with BetterAuth
Source: https://github.com/polarsource/polar/blob/main/docs/integrate/sdk/adapters/better-auth.mdx
This snippet shows how to integrate the checkout plugin into the BetterAuth client configuration. It includes optional parameters for product mappings, success and return URLs, authentication settings, and theme enforcement.
```typescript
import {
polar,
checkout // [!code ++]
} from "@polar-sh/better-auth";
const auth = betterAuth({
// ... Better Auth config
plugins: [
polar({
...
use: [
checkout({ // [!code ++]
// Optional field - will make it possible to pass a slug to checkout instead of Product ID
products: [ { productId: "123-456-789", slug: "pro" } ], // [!code ++]
// Relative URL to return to when checkout is successfully completed
successUrl: "/success?checkout_id={CHECKOUT_ID}", // [!code ++]
// Wheather you want to allow unauthenticated checkout sessions or not
authenticatedUsersOnly: true, // [!code ++]
// An optional URL which renders a back-button in the Checkout
returnUrl: "https://myapp.com" // [!code ++]
}) // [!code ++]
],
})
]
});
```
--------------------------------
### Configure BetterAuth Server with Polar Plugins (TypeScript)
Source: https://github.com/polarsource/polar/blob/main/docs/integrate/sdk/adapters/better-auth.mdx
This example demonstrates configuring the BetterAuth server to integrate with Polar. It includes setting up the Polar client, enabling features like checkout, customer portal, usage tracking, and webhook handling. Dependencies include '@polar-sh/better-auth' and '@polar-sh/sdk'.
```typescript
import {
betterAuth
} from "better-auth";
import {
polar,
checkout,
portal,
usage,
webhooks
} from "@polar-sh/better-auth"; // [!code ++]
import { Polar } from "@polar-sh/sdk"; // [!code ++]
const polarClient = new Polar({
accessToken: process.env.POLAR_ACCESS_TOKEN,
// Use 'sandbox' if you're using the Polar Sandbox environment
// Remember that access tokens, products, etc. are completely separated between environments.
// Access tokens obtained in Production are for instance not usable in the Sandbox environment.
server: 'sandbox' // [!code ++]
}); // [!code ++]
const auth = betterAuth({
// ... Better Auth config
plugins: [
polar({
client: polarClient, // [!code ++]
createCustomerOnSignUp: true, // [!code ++]
use: [
checkout({
products: [
{
productId: "123-456-789", // ID of Product from Polar Dashboard // [!code ++]
slug: "pro" // Custom slug for easy reference in Checkout URL, e.g. /checkout/pro // [!code ++]
}
],
successUrl: "/success?checkout_id={CHECKOUT_ID}", // [!code ++]
authenticatedUsersOnly: true // [!code ++]
}), // [!code ++]
portal(), // [!code ++]
usage(), // [!code ++]
webhooks({
secret: process.env.POLAR_WEBHOOK_SECRET, // [!code ++]
onCustomerStateChanged: (payload) => // Triggered when anything regarding a customer changes // [!code ++]
onOrderPaid: (payload) => // Triggered when an order was paid (purchase, subscription renewal, etc.) // [!code ++]
... // Over 25 granular webhook handlers // [!code ++]
onPayload: (payload) => // Catch-all for all events // [!code ++]
}) // [!code ++]
],
})
]
});
```
--------------------------------
### Initiate Checkout Sessions with BetterAuth Client
Source: https://github.com/polarsource/polar/blob/main/docs/integrate/sdk/adapters/better-auth.mdx
This snippet demonstrates how to create checkout sessions using the `checkout` method on the BetterAuth client after the checkout plugin has been configured. It shows how to specify products by their IDs or by using slugs defined in the plugin configuration.
```typescript
await authClient.checkout({
// Polar Product IDs
products: ["e651f46d-ac20-4f26-b769-ad088b123df2"], // [!code ++]
// OR
// if "products" in passed in the checkout plugin's config, you may pass the slug
// slug: "pro", // [!code ++]
});
```
--------------------------------
### BetterAuth Checkout with Organization Integration
Source: https://github.com/polarsource/polar/blob/main/docs/integrate/sdk/adapters/better-auth.mdx
This example shows how to associate checkout sessions with an organization by passing the organization ID as the `referenceId`. This allows for tracking purchases made by organization members and is saved in the metadata of related checkout, order, and subscription objects.
```typescript
const organizationId = (await authClient.organization.list())?.data?.[0]?.id,
await authClient.checkout({
// Any Polar Product ID can be passed here
products: ["e651f46d-ac20-4f26-b769-ad088b123df2"],
// Or, if you setup "products" in the Checkout Config, you can pass the slug
slug: 'pro',
// Reference ID will be saved as `referenceId` in the metadata of the checkout, order & subscription object
referenceId: organizationId
});
```Drafting concise plan content
[
{
"content": "Fetch Context7 docs for Polar and Better Auth integration requirements",
"status": "completed",
"priority": "high"
},
{
"content": "Draft kebab-case slug and actionable implementation plan content",
"status": "completed",
"priority": "high"
},
{
"content": "Run create-plan script and verify generated file in .agents/plans",
"status": "in_progress",
"priority": "high"
}
]Planning slugged plan file creation
lsapp assets bun.lock components components.json dev.db docker-compose.yml eslint.config.mjs extension extension-dist extension-profile hooks idea.pem idea2.pem instrumentation-client.ts lib next-env.d.ts next.config.ts node_modules package.json postcss.config.mjs prisma prisma.config.ts proxy.ts public readme.md release-notes roadmap.pem roadmap2.pem server tsconfig.json tsconfig.tsbuildinfo
Drafting implementation plan outline
npx tsx ~/.config/opencode/scripts/create-plan.ts "landing-page-full-payment-pricing-upgrade-polar-profile-cancel-better-auth" <<'HEREDOC'
## Outcome
Ship end-to-end paid plan flow: landing/pricing checkout, in-app upgrade entry, Polar customer portal cancel/manage, and Better Auth + Polar subscription sync.
## Context7 constraints (authoritative)
- Better Auth Polar plugin: `polar({ client, createCustomerOnSignUp, use: [checkout(), portal(), webhooks()] })`.
- Client actions: `authClient.checkout(...)`, `authClient.customer.portal()`, `authClient.customer.subscriptions.list(...)`.
- Polar checkout core params to wire end-to-end: `product_price_id` (or mapped slug), `success_url`, `customer_email`, `metadata`, optional discount/seat params when supported.
## Plan
1. Baseline + config
- Audit current files: `components/landing-pricing.tsx`, `components/landing.tsx`, `components/header.tsx`, `lib/auth.ts`, `lib/auth-client.ts`, `prisma/schema.prisma`.
- Add deps: `@polar-sh/better-auth`, `@polar-sh/sdk`.
- Add env contract: `POLAR_ACCESS_TOKEN`, `POLAR_WEBHOOK_SECRET`, `POLAR_SERVER`, `POLAR_PRO_MONTHLY_PRODUCT_ID` (or price ID), `POLAR_PRO_YEARLY_PRODUCT_ID`, `NEXT_PUBLIC_APP_URL`.
2. Server auth + billing integration
- Extend `lib/auth.ts` with Polar client and Better Auth Polar plugin.
- Enable: checkout (monthly/yearly mappings + success/return URLs + auth-only policy), portal, webhooks.
- Add webhook handlers to persist entitlement state in app DB (active/canceled/trialing, plan, renewal/cancel timestamps, Polar customer id).
- Regenerate/update Better Auth Prisma models; apply migration/push safely.
3. Client auth integration
- Extend `lib/auth-client.ts` with `polarClient()` so checkout/portal/subscription methods are typed on client.
- Add a small billing helper wrapper for checkout payload creation and error handling.
4. Landing + pricing conversion
- Replace static `/signup` CTA in `components/landing-pricing.tsx` with plan-aware actions:
- signed out: redirect to signup/login with intended plan + cycle.
- signed in: call `authClient.checkout` with mapped monthly/yearly product + metadata (`source: landing_pricing`, cycle, user id/ref id if needed).
- Ensure all pricing parameters used in app-level payload builder; keep yearly/monthly toggle behavior intact.
- Add pricing-to-checkout navigation from landing where needed.
5. Dashboard dropdown upgrade + portal
- In `components/header.tsx` add:
- `Upgrade` item (opens Pro checkout flow).
- `Manage Billing` / `Billing Portal` item (calls `authClient.customer.portal()` for cancel/manage).
- Hide/disable Upgrade when user already has active Pro; show manage billing instead.
6. Entitlement sync + feature gating
- Derive `isPro` from webhook-synced state (not optimistic client state).
- Update relevant server/client checks for Pro-only features.
- Add fallback reconciliation path using `authClient.customer.subscriptions.list({ active: true })` for drift recovery.
7. Verification
- Sandbox flow test: new checkout, upgrade, renewal event, cancellation in Polar portal, access downgrade.
- Verify webhook signature handling and idempotency.
- Run `bun run typecheck`, `bun run lint`, focused tests, and `bun run build`.
## Done criteria
- Pricing cards initiate correct Polar checkout for monthly/yearly.
- Dashboard dropdown contains working Upgrade + Billing Portal actions.
- User can cancel/manage in Polar portal; app entitlement updates via webhook.
- Auth + billing integration fully typed, migrated, and passing lint/typecheck/build.
## Unresolved questions
- Exact Polar product/price IDs and whether checkout should use product IDs vs price IDs in this app.
- Final Pro feature gate list (which capabilities require active subscription now).
- Preferred post-checkout and post-portal return URLs (dashboard only vs contextual return).
HEREDOCCreated plan: /Users/duncan/dev/bmrks/.agents/plans/gentle-jade-leaf-landing-page-full-payment-pricing-upgrade-polar-profile-cancel-better-auth.md ID: gentle-jade-leaf Filename: gentle-jade-leaf-landing-page-full-payment-pricing-upgrade-polar-profile-cancel-better-auth.md
Planning file content verification
Verifying frontmatter presence
[
{
"content": "Fetch Context7 docs for Polar and Better Auth integration requirements",
"status": "completed",
"priority": "high"
},
{
"content": "Draft kebab-case slug and actionable implementation plan content",
"status": "completed",
"priority": "high"
},
{
"content": "Run create-plan script and verify generated file in .agents/plans",
"status": "completed",
"priority": "high"
}
]Summarizing plan creation details
Created and verified.
npx tsx ~/.config/opencode/scripts/create-plan.ts with slug landing-page-full-payment-pricing-upgrade-polar-profile-cancel-better-auth..agents/plans/gentle-jade-leaf-landing-page-full-payment-pricing-upgrade-polar-profile-cancel-better-auth.md.## Unresolved questions section at the end of the plan per plan-mode guidance.task_id: ses_38d096f9effe72COm8I2Qwl3ZZ (for resuming to continue this task if needed) <task_result> </task_result>
[search-mode]
MAXIMIZE SEARCH EFFORT. Launch multiple background agents IN PARALLEL:
not soft, read the @components/landing-pricing.tsx and implement all of them, not some, all of them.
Planning multi-agent search and todos
Quick summary of that task-tool run: it returned a task_id (ses_38d096f9effe72COm8I2Qwl3ZZ) but no actual result payload, so no interview findings were produced yet.
I’m now continuing with full implementation (not partial): wiring Polar + Better Auth, upgrading pricing actions, and adding upgrade/manage-billing controls in the dropdown. I’ll verify everything with typecheck/lint/build afterward.
[
{
"content": "Reconfirm Polar + Better Auth docs via Context7 and map required flows",
"status": "in_progress",
"priority": "high"
},
{
"content": "Deeply scan codebase for pricing/auth/header integration points and existing patterns",
"status": "pending",
"priority": "high"
},
{
"content": "Implement Polar + Better Auth server integration and billing data model/webhooks",
"status": "pending",
"priority": "high"
},
{
"content": "Implement client billing integration on landing pricing (all checkout parameters)",
"status": "pending",
"priority": "high"
},
{
"content": "Add Upgrade and Manage Billing entries in user dropdown with proper gating",
"status": "pending",
"priority": "high"
},
{
"content": "Run db generation/migration + diagnostics + typecheck + lint + build",
"status": "pending",
"priority": "high"
}
]Available Libraries: Each result includes: - Library ID: Context7-compatible identifier (format: /org/project) - Name: Library or package name - Description: Short summary - Code Snippets: Number of available code examples - Source Reputation: Authority indicator (High, Medium, Low, or Unknown) - Benchmark Score: Quality indicator (100 is the highest score) - Versions: List of versions if available. Use one of those versions if the user provides a version in their query. The format of the version is /org/project/version. For best results, select libraries based on name match, source reputation, snippet coverage, benchmark score, and relevance to your use case. ---------- - Title: Better Auth - Context7-compatible library ID: /better-auth/better-auth - Description: Better Auth is a framework-agnostic authentication and authorization library for TypeScript, offering a comprehensive feature set and a plugin ecosystem for advanced functionalities. - Code Snippets: 2752 - Source Reputation: High - Benchmark Score: 74.5 - Versions: v1.2.9, better-auth@1.3.4, v1_3_8, v1_3_10_beta_6, _better_auth_stripe_1_3_4, __branch__chore_option-exports ---------- - Title: Polar - Context7-compatible library ID: /websites/polar_sh - Description: Polar is a complete billing infrastructure and Merchant of Record solution that enables developers to sell digital products and subscriptions globally, handling tax compliance, automated access, and payment processing with lower fees. - Code Snippets: 10 - Source Reputation: High - Benchmark Score: 62.1 ---------- - Title: Better Auth Paystack Plugin - Context7-compatible library ID: /alexasomba/better-auth-paystack - Description: A Better Auth plugin that integrates Paystack for customer creation, checkout, and Paystack-native subscription flows, providing optional customer creation on sign-up, webhook verification, and local subscription management. - Code Snippets: 108 - Source Reputation: High - Benchmark Score: 88.9 ---------- - Title: Better Auth UI - Context7-compatible library ID: /websites/better-auth-ui - Description: Better Auth UI provides ready-to-use shadcn/ui styled components for rapidly implementing customizable authentication features in Next.js and React applications, seamlessly integrating with `better-auth`. - Code Snippets: 510 - Source Reputation: Medium - Benchmark Score: 78.2 ---------- - Title: Better Auth UI - Context7-compatible library ID: /llmstxt/better-auth-ui_llms_txt - Description: Better Auth UI provides ready-to-use shadcn/ui styled components for rapidly implementing authentication features in Next.js and React applications, seamlessly integrating with better-auth. - Code Snippets: 581 - Source Reputation: Medium - Benchmark Score: 78.5
Available Libraries: Each result includes: - Library ID: Context7-compatible identifier (format: /org/project) - Name: Library or package name - Description: Short summary - Code Snippets: Number of available code examples - Source Reputation: Authority indicator (High, Medium, Low, or Unknown) - Benchmark Score: Quality indicator (100 is the highest score) - Versions: List of versions if available. Use one of those versions if the user provides a version in their query. The format of the version is /org/project/version. For best results, select libraries based on name match, source reputation, snippet coverage, benchmark score, and relevance to your use case. ---------- - Title: Better Auth - Context7-compatible library ID: /better-auth/better-auth - Description: Better Auth is a framework-agnostic authentication and authorization library for TypeScript, offering a comprehensive feature set and a plugin ecosystem for advanced functionalities. - Code Snippets: 2752 - Source Reputation: High - Benchmark Score: 74.5 - Versions: v1.2.9, better-auth@1.3.4, v1_3_8, v1_3_10_beta_6, _better_auth_stripe_1_3_4, __branch__chore_option-exports ---------- - Title: Better Auth - Context7-compatible library ID: /llmstxt/better-auth_llms_txt - Description: Better Auth is a flexible authentication library that supports email/password, social providers, and can be extended with plugins like username, magic link, and passkey. - Code Snippets: 1875 - Source Reputation: High - Benchmark Score: 84 ---------- - Title: Better Auth - Context7-compatible library ID: /websites/better-auth - Description: Better Auth is a framework-agnostic authentication and authorization framework for TypeScript, offering a comprehensive feature set and a plugin ecosystem for advanced functionalities like 2FA and SSO. - Code Snippets: 1774 - Source Reputation: High - Benchmark Score: 75.5 ---------- - Title: Better Auth - Context7-compatible library ID: /get-convex/better-auth - Description: Convex Better Auth is a component that integrates Better Auth with Convex for enhanced authentication in applications. - Code Snippets: 231 - Source Reputation: High - Benchmark Score: 76.5 ---------- - Title: Better Auth - Context7-compatible library ID: /websites/better-auth_introduction - Description: Better Auth is a framework-agnostic authentication and authorization framework for TypeScript. It offers comprehensive features and a plugin ecosystem for advanced functionalities like 2FA, multi-tenancy, and SSO. - Code Snippets: 8 - Source Reputation: High - Benchmark Score: 4
Available Libraries: Each result includes: - Library ID: Context7-compatible identifier (format: /org/project) - Name: Library or package name - Description: Short summary - Code Snippets: Number of available code examples - Source Reputation: Authority indicator (High, Medium, Low, or Unknown) - Benchmark Score: Quality indicator (100 is the highest score) - Versions: List of versions if available. Use one of those versions if the user provides a version in their query. The format of the version is /org/project/version. For best results, select libraries based on name match, source reputation, snippet coverage, benchmark score, and relevance to your use case. ---------- - Title: Polar - Context7-compatible library ID: /polarsource/polar - Description: Polar is an open-source payments infrastructure platform for developers, enabling them to sell digital products and subscriptions while handling billing, receipts, and taxes. - Code Snippets: 607 - Source Reputation: High - Benchmark Score: 77.1 ---------- - Title: Polar SDK - Context7-compatible library ID: /polarsource/polar-python - Description: The Polar SDK is a Python client library designed for seamless interaction with the Polar API, providing comprehensive tools for managing various Polar resources and handling webhooks. - Code Snippets: 1027 - Source Reputation: High - Benchmark Score: 96.7 ---------- - Title: Polar JS - Context7-compatible library ID: /polarsource/polar-js - Description: A developer-friendly and type-safe TypeScript SDK for leveraging the Polar API, also functioning as an MCP server for AI applications. - Code Snippets: 2666 - Source Reputation: High - Benchmark Score: 85.7 ---------- - Title: Polar (llmstxt) - Context7-compatible library ID: /llmstxt/polar_sh_llms_txt - Description: Polar is a platform that helps creators and communities manage subscriptions, benefits, and payments, offering tools for API integration and customer management. - Code Snippets: 2501 - Source Reputation: High - Benchmark Score: 58.4 ---------- - Title: Polar Sh SDK - Context7-compatible library ID: /polarsource/polar-php - Description: A developer-friendly and type-safe PHP SDK designed to leverage the Polar API, providing access to its HTTP and Webhooks functionalities. - Code Snippets: 1594 - Source Reputation: High - Benchmark Score: 70.5
Background task launched. Task ID: bg_f9b40869 Description: Map auth and session data flow for billing integration Agent: explore Status: running System notifies on completion. Use `background_output` with task_id="bg_f9b40869" to check. <task_metadata> session_id: ses_38d00d526ffeuSuN0SVHd7vdz0 </task_metadata> to continue: task(session_id="ses_38d00d526ffeuSuN0SVHd7vdz0", prompt="...")
Background task launched. Task ID: bg_369bd9be Description: Map landing pricing and checkout trigger integration points Agent: explore Status: running System notifies on completion. Use `background_output` with task_id="bg_369bd9be" to check. <task_metadata> session_id: ses_38d00d506ffezlmkm7ZuQG43yL </task_metadata> to continue: task(session_id="ses_38d00d506ffezlmkm7ZuQG43yL", prompt="...")
Background task launched. Task ID: bg_f44ec255 Description: Collect authoritative Better Auth plus Polar plugin docs details Agent: librarian Status: error System notifies on completion. Use `background_output` with task_id="bg_f44ec255" to check. <task_metadata> session_id: undefined </task_metadata>
Background task launched. Task ID: bg_6191dbef Description: Collect Polar checkout parameter and subscription lifecycle details Agent: librarian Status: running System notifies on completion. Use `background_output` with task_id="bg_6191dbef" to check. <task_metadata> session_id: ses_38d00d54effeB2F83s7heeMCfG </task_metadata> to continue: task(session_id="ses_38d00d54effeB2F83s7heeMCfG", prompt="...")
System Reminder
[BACKGROUND TASK COMPLETED] **ID:** `bg_f9b40869` **Description:** Map auth and session data flow for billing integration **Duration:** 23s **1 task still in progress.** You WILL be notified when ALL complete. Do NOT poll - continue productive work. Use `background_output(task_id="bg_f9b40869")` to retrieve this result when ready.
System Reminder
[ALL BACKGROUND TASKS COMPLETE] **Completed:** - `bg_f9b40869`: Map auth and session data flow for billing integration - `bg_369bd9be`: Map landing pricing and checkout trigger integration points Use `background_output(task_id="<id>")` to retrieve each result.
Task Result
Task ID: bg_f9b40869
Description: Map auth and session data flow for billing integration
Duration: 23s
Session ID: ses_38d00d526ffeuSuN0SVHd7vdz0
---
<analysis>
**Literal Request**: Find all files and functions that fetch auth/session/user profile data and pass it into dashboard/header UI.
**Actual Need**: Map out the auth data flow to extend the user session/profile with subscription status and Polar customer fields for plan gating.
**Success Looks Like**: A clear map of data fetching points (server-side and client-side) and data contracts where new billing fields should be injected.
</analysis>
Based on the codebase analysis, here is the map of auth and session data flow for the dashboard and header.
### 1. Data Fetching & Flow Map
| Layer | File Path | Function/Component | Data Fetched | Destination |
|:---|:---|:---|:---|:---|
| **Server (Auth)** | `/Users/duncan/dev/bmrks/lib/auth.ts` | `auth` (Better Auth) | Core session + user fields | `getSession` in `auth-server.ts` |
| **Server (Query)** | `/Users/duncan/dev/bmrks/lib/auth-server.ts` | `getSession()` | `Session \| null` | Page components (Dashboard, Login) |
| **Server (Page)** | `/Users/duncan/dev/bmrks/app/dashboard/page.tsx` | `DashboardData()` | `session`, `user` profile from Prisma | `DashboardContent` component |
| **Client (UI)** | `/Users/duncan/dev/bmrks/components/dashboard-content.tsx` | `DashboardContent` | `session`, `profile` props | `Header` component |
| **Client (Header)** | `/Users/duncan/dev/bmrks/components/header.tsx` | `Header` | `userName`, `userEmail`, `profile` | `SettingsDialog` |
| **Client (Auth)** | `/Users/duncan/dev/bmrks/lib/auth-client.ts` | `authClient` | Client-side session state | `SettingsDialog`, `Header` |
### 2. Data Contracts
#### User Profile (`lib/auth.ts` / Prisma)
The `user` model in `prisma/schema.prisma` is the source of truth.
**Where to inject**: Add `subscriptionStatus`, `polarCustomerId`, and `plan` fields here.
#### Session Object (`lib/auth.ts`)
Better Auth infers the session type:
```typescript
export type Session = typeof auth.$Infer.Session;
export type User = Session["user"];
```
#### Profile Data Contract (`components/dashboard-content.tsx`)
This interface is used to pass profile data from the server-side fetch in the page to the client-side components.
```typescript
export interface ProfileData {
image: string | null;
username: string | null;
// Add new fields here:
// subscriptionStatus?: string;
// plan?: 'free' | 'pro';
}
```
### 3. Injection Plan
To add subscription status and Polar fields without breaking flows:
1. **Database Layer**:
- Update `prisma/schema.prisma` to include Polar fields (e.g., `polarCustomerId`, `subscriptionId`, `plan`).
- Run migration.
2. **Auth Layer**:
- Update Better Auth configuration in `/Users/duncan/dev/bmrks/lib/auth.ts` to include the new user fields in the session payload using the `user` property in the options.
3. **Data Fetching**:
- Update the `db.user.findUnique` call in `/Users/duncan/dev/bmrks/app/dashboard/page.tsx` (lines 25-36) to select the new subscription fields.
4. **Prop Propagation**:
- Update `ProfileData` interface in `/Users/duncan/dev/bmrks/components/dashboard-content.tsx`.
- Pass these fields into `DashboardContent` and then into `Header` in `components/header.tsx`.
5. **UI Integration**:
- In `components/header.tsx`, add a "Billing" or "Upgrade" link in the `DropdownMenuContent` (around line 350).
- Use the injected `plan` or `subscriptionStatus` to conditionally render "Upgrade to Pro" or "Manage Subscription".
<results>
<files>
- /Users/duncan/dev/bmrks/prisma/schema.prisma — Primary data model for User.
- /Users/duncan/dev/bmrks/lib/auth.ts — Better Auth configuration for session management.
- /Users/duncan/dev/bmrks/app/dashboard/page.tsx — Server-side data fetching for the dashboard.
- /Users/duncan/dev/bmrks/components/dashboard-content.tsx — Client-side entry point and ProfileData interface definition.
- /Users/duncan/dev/bmrks/components/header.tsx — Header UI where subscription status will be displayed.
</files>
<answer>
The auth data flow starts with **Better Auth** in `lib/auth.ts`, backed by **Prisma**. The session is retrieved server-side in `app/dashboard/page.tsx` via `getSession()` and combined with a direct Prisma query to fetch extended profile fields. This data is passed as props to the `DashboardContent` client component, which then distributes it to the `Header` and its child `SettingsDialog`.
To support Polar subscriptions, you should extend the Prisma `user` model, update the server-side fetch in the dashboard page, and update the `ProfileData` interface to propagate the new state to the UI.
</answer>
<next_steps>
1. Define the new Polar fields in `prisma/schema.prisma`.
2. Update the `db.user.findUnique` selection in `app/dashboard/page.tsx` to include these fields.
3. Extend the `ProfileData` interface in `components/dashboard-content.tsx`.
4. Implement the "Billing" entry in `components/header.tsx` using the new props.
</next_steps>
</results>Task Result
Task ID: bg_369bd9be
Description: Map landing pricing and checkout trigger integration points
Duration: 24s
Session ID: ses_38d00d506ffezlmkm7ZuQG43yL
---
<analysis>
**Literal Request**: Identify all landing/pricing components and call sites for pricing CTAs and checkout actions.
**Actual Need**: Map out the specific files and logic required to transition the static pricing UI into a functional checkout flow, including the payload structure and existing patterns for async actions and feedback.
**Success Looks Like**: A clear integration plan identifying the target components, the required RPC/server structure for billing, and the client-side implementation details (hooks, toasts, state) for monthly/yearly toggle support.
</analysis>
<results>
<files>
- /Users/duncan/dev/bmrks/components/landing-pricing.tsx — Contains the `LandingPricing` and `BillingToggle` components where "Sign up" CTAs are currently hardcoded as links.
- /Users/duncan/dev/bmrks/components/landing.tsx — The container component that renders `LandingPricing`.
- /Users/duncan/dev/bmrks/server/router.ts — The central RPC router where a new `billing` namespace should be added.
- /Users/duncan/dev/bmrks/server/procedures/billing.ts — (To be created) Recommended location for `createCheckoutSession` RPC procedure.
- /Users/duncan/dev/bmrks/components/dashboard-content.tsx — Reference for `useMutation` and `sonner` toast patterns used in the codebase.
</files>
<answer>
**1. Pricing Component Analysis**
- **Location**: `/Users/duncan/dev/bmrks/components/landing-pricing.tsx`
- **Current State**: Uses a `billingCycle` state ("monthly" | "yearly"). CTAs are `<a>` tags pointing to `/signup`.
- **Target Logic**:
- The "Free" plan CTA should remain as a link to `/signup` or trigger a specific free-tier onboarding.
- The "Pro" plan CTA needs to be wired to a `checkout` action that sends the `billingCycle` payload.
**2. Existing Patterns**
- **Async Actions**: The codebase uses `@tanstack/react-query`'s `useMutation` for side effects.
- **RPC Layer**: Powered by `@orpc/server`. Routes are defined in `server/router.ts` and consumed via a typed client (likely `orpc` or similar, based on `dashboard-content.tsx` usage).
- **Feedback**: `sonner` is the standard for toasts. Common pattern:
```tsx
const { mutate, isPending } = useMutation({
mutationFn: (input) => rpc.billing.createCheckout(input),
onSuccess: (data) => window.location.href = data.url,
onError: () => toast.error("Failed to start checkout")
});
```
- **Auth**: `better-auth` is used. Checkout actions should likely be `authed` procedures, meaning users must be logged in before initiating payment.
**3. Integration Plan**
- **Server Side**:
- Define `createCheckoutSession` in a new procedure file (e.g., `server/procedures/billing.ts`).
- Input: `z.object({ interval: z.enum(["monthly", "yearly"]) })`.
- Output: `z.object({ url: z.string() })`.
- Add `billing` namespace to `server/router.ts`.
- **Client Side**:
- Update `LandingPricing` to accept a `user` prop to determine if it should redirect to `/login` or trigger the RPC.
- Replace `<a>` with a `<button>` for the Pro plan that calls the checkout mutation.
- Use `useTransition` or `isPending` from `useMutation` to show loading states on the button.
</answer>
<next_steps>
1. Create `server/procedures/billing.ts` to handle checkout session generation.
2. Register the new procedure in `server/router.ts`.
3. Refactor `LandingPricing` to use `useMutation` for the Pro CTA instead of a static link.
4. Implement a `useSession` check in `LandingPricing` to handle the "Login to upgrade" flow vs "Instant checkout" flow.
</next_steps>
</results>Task not found: bg_6191dbef
Task not found: bg_f44ec255
Planning exhaustive code exploration
Great — I’ve pulled the completed background findings and they align with your instruction: implement all pricing behavior, not partial.
Now I’m moving straight into code changes across pricing, auth, billing state, and dropdown UX so monthly/yearly checkout, upgrade, and portal cancellation all work end-to-end. I’ll run full verification after implementation.
[SYSTEM DIRECTIVE: OH-MY-OPENCODE - TODO CONTINUATION]
Incomplete tasks remain in your todo list. Continue working on the next pending task.
[Status: 0/6 completed, 6 remaining]
Remaining tasks:
Planning billing integration
[
{
"content": "Reconfirm Polar + Better Auth docs via Context7 and map required flows",
"status": "in_progress",
"priority": "high"
},
{
"content": "Deeply scan codebase for pricing/auth/header integration points and existing patterns",
"status": "pending",
"priority": "high"
},
{
"content": "Implement Polar + Better Auth server integration and billing data model/webhooks",
"status": "pending",
"priority": "high"
},
{
"content": "Implement client billing integration on landing pricing (all checkout parameters)",
"status": "pending",
"priority": "high"
},
{
"content": "Add Upgrade and Manage Billing entries in user dropdown with proper gating",
"status": "pending",
"priority": "high"
},
{
"content": "Run db generation/migration + diagnostics + typecheck + lint + build",
"status": "pending",
"priority": "high"
}
]Reconfirming documentation completeness
### Integrate Polar Webhooks Plugin with BetterAuth
Source: https://github.com/polarsource/polar/blob/main/docs/integrate/sdk/adapters/better-auth.mdx
This TypeScript example demonstrates how to add the Polar webhooks plugin to the BetterAuth client configuration. It includes setting the webhook secret and configuring specific event handlers like onCustomerStateChanged and onOrderPaid.
```typescript
import {
polar,
webhooks // [!code ++]
} from "@polar-sh/better-auth";
const auth = betterAuth({
// ... Better Auth config
plugins: [
polar({
...
use: [
webhooks({ // [!code ++]
secret: process.env.POLAR_WEBHOOK_SECRET, // [!code ++]
onCustomerStateChanged: (payload) => // Triggered when anything regarding a customer changes // [!code ++]
onOrderPaid: (payload) => // Triggered when an order was paid (purchase, subscription renewal, etc.) // [!code ++]
... // Over 25 granular webhook handlers // [!code ++]
onPayload: (payload) => // Catch-all for all events // [!code ++]
}) // [!code ++]
],
})
]
});
```
--------------------------------
### Configure BetterAuth Server with Polar Plugins (TypeScript)
Source: https://github.com/polarsource/polar/blob/main/docs/integrate/sdk/adapters/better-auth.mdx
This example demonstrates configuring the BetterAuth server to integrate with Polar. It includes setting up the Polar client, enabling features like checkout, customer portal, usage tracking, and webhook handling. Dependencies include '@polar-sh/better-auth' and '@polar-sh/sdk'.
```typescript
import {
betterAuth
} from "better-auth";
import {
polar,
checkout,
portal,
usage,
webhooks
} from "@polar-sh/better-auth"; // [!code ++]
import { Polar } from "@polar-sh/sdk"; // [!code ++]
const polarClient = new Polar({
accessToken: process.env.POLAR_ACCESS_TOKEN,
// Use 'sandbox' if you're using the Polar Sandbox environment
// Remember that access tokens, products, etc. are completely separated between environments.
// Access tokens obtained in Production are for instance not usable in the Sandbox environment.
server: 'sandbox' // [!code ++]
}); // [!code ++]
const auth = betterAuth({
// ... Better Auth config
plugins: [
polar({
client: polarClient, // [!code ++]
createCustomerOnSignUp: true, // [!code ++]
use: [
checkout({
products: [
{
productId: "123-456-789", // ID of Product from Polar Dashboard // [!code ++]
slug: "pro" // Custom slug for easy reference in Checkout URL, e.g. /checkout/pro // [!code ++]
}
],
successUrl: "/success?checkout_id={CHECKOUT_ID}", // [!code ++]
authenticatedUsersOnly: true // [!code ++]
}), // [!code ++]
portal(), // [!code ++]
usage(), // [!code ++]
webhooks({
secret: process.env.POLAR_WEBHOOK_SECRET, // [!code ++]
onCustomerStateChanged: (payload) => // Triggered when anything regarding a customer changes // [!code ++]
onOrderPaid: (payload) => // Triggered when an order was paid (purchase, subscription renewal, etc.) // [!code ++]
... // Over 25 granular webhook handlers // [!code ++]
onPayload: (payload) => // Catch-all for all events // [!code ++]
}) // [!code ++]
],
})
]
});
```
--------------------------------
### Create Webhook Endpoint
Source: https://context7.com/polarsource/polar/llms.txt
Set up webhook endpoints to receive real-time event notifications.
```APIDOC
## POST /v1/webhooks/endpoints
### Description
Creates a new webhook endpoint to receive event notifications.
### Method
POST
### Endpoint
/v1/webhooks/endpoints
### Parameters
#### Request Body
- **url** (string) - Required - The URL of the webhook endpoint.
- **organization_id** (string) - Required - The ID of the organization.
- **events** (array of strings) - Required - A list of event types to subscribe to.
### Request Example
```json
{
"url": "https://api.example.com/webhooks/polar",
"organization_id": "550e8400-e29b-41d4-a716-446655440000",
"events": [
"order.created",
"subscription.created",
"subscription.updated",
"subscription.canceled",
"benefit.granted",
"benefit.revoked"
]
}
```
### Response
#### Success Response (200)
- **id** (string) - The ID of the created webhook endpoint.
- **url** (string) - The URL of the webhook endpoint.
- **secret** (string) - The secret key for signature verification.
- **events** (array of strings) - The subscribed event types.
- **created_at** (string) - The timestamp when the webhook was created.
#### Response Example
```json
{
"id": "f50e8400-e29b-41d4-a716-446655440011",
"url": "https://api.example.com/webhooks/polar",
"secret": "whsec_xxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"events": [
"order.created",
"subscription.created",
"subscription.updated",
"subscription.canceled",
"benefit.granted",
"benefit.revoked"
],
"created_at": "2025-01-15T14:00:00Z"
}
```
### Additional Information
**Webhook Payload Signature Verification**: Use the provided `secret` to verify webhook signatures using HMAC SHA-256.
```
--------------------------------
### Create Webhook Endpoint using curl
Source: https://context7.com/polarsource/polar/llms.txt
This snippet shows how to create a webhook endpoint to receive real-time event notifications for various activities like orders and subscriptions. It requires a URL, organization ID, and a list of events to subscribe to. The API uses a POST request to /v1/webhooks/endpoints.
```bash
curl -X POST "https://api.polar.sh/v1/webhooks/endpoints" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"url": "https://api.example.com/webhooks/polar",
"organization_id": "550e8400-e29b-41d4-a716-446655440000",
"events": [
"order.created",
"subscription.created",
"subscription.updated",
"subscription.canceled",
"benefit.granted",
"benefit.revoked"
]
}'
```
--------------------------------
### Create Nuxt Webhook Handler
Source: https://github.com/polarsource/polar/blob/main/docs/integrate/sdk/adapters/nuxt.mdx
Configure a server route to receive and process webhook events from Polar. This handler uses the `Webhooks` utility, requiring a webhook secret for signature verification. It includes a general `onPayload` handler for all events, with options for specific event types.
```typescript
// server/routes/webhook/polar.post.ts
export default defineEventHandler((event) => {
const {
private: { polarWebhookSecret },
} = useRuntimeConfig();
const webhooksHandler = Webhooks({
webhookSecret: polarWebhookSecret,
onPayload: async (payload) => {
// Handle the payload
// No need to return an acknowledge response
},
});
return webhooksHandler(event);
});
```### Configure Portal Plugin for Customer Management
Source: https://github.com/better-auth/better-auth/blob/canary/docs/content/docs/plugins/polar.mdx
Initialize the Portal plugin alongside Checkout to enable customer self-service management of purchases, orders, subscriptions, and benefits through the Polar Customer Portal.
```typescript
import { polar, checkout, portal } from "@polar-sh/better-auth";
const auth = betterAuth({
// ... Better Auth config
plugins: [
polar({
...
use: [
checkout(...),
portal()
],
})
]
});
```
--------------------------------
### Redirect User to Polar Customer Portal
Source: https://github.com/better-auth/better-auth/blob/canary/docs/content/docs/plugins/polar.mdx
Navigate the authenticated user to the Polar Customer Portal where they can manage orders, purchases, subscriptions, benefits, and view their account details.
```typescript
await authClient.customer.portal();
```
--------------------------------
### List Customer Orders with Billing Type Filter
Source: https://github.com/better-auth/better-auth/blob/canary/docs/content/docs/plugins/polar.mdx
Retrieve a paginated list of customer orders including purchases and subscription renewals. Supports filtering by billing type (one_time or recurring) and pagination controls.
```typescript
const { data: orders } = await authClient.customer.orders.list({
query: {
page: 1,
limit: 10,
productBillingType: "one_time", // or 'recurring'
},
});
```
--------------------------------
### List Authenticated User Subscriptions
Source: https://github.com/better-auth/better-auth/blob/canary/docs/content/docs/plugins/polar.mdx
Retrieves subscriptions for the authenticated user with pagination and filtering options. Supports passing a referenceId to check organization subscriptions instead of user subscriptions, useful for determining organization access.
```typescript
const { data: subscriptions } = await authClient.customer.subscriptions.list({
query: {
page: 1,
limit: 10,
active: true,
},
});
```
--------------------------------
### Initialize Checkout Session with Product ID or Slug
Source: https://github.com/better-auth/better-auth/blob/canary/docs/content/docs/plugins/polar.mdx
Trigger a checkout session using the authClient.checkout() method. Supports passing Polar Product IDs directly or predefined product slugs from configuration. Automatically includes authenticated user as customer.
```typescript
await authClient.checkout({
// Any Polar Product ID can be passed here
products: ["e651f46d-ac20-4f26-b769-ad088b123df2"],
// Or, if you setup "products" in the Checkout Config, you can pass the slug
slug: "pro",
});
```### Generate Prisma Schema with Better Auth CLI Source: https://github.com/better-auth/better-auth/blob/canary/docs/content/docs/adapters/prisma.mdx Command-line interface command to automatically generate or update Prisma database schema based on Better Auth configuration and installed plugins. Supports schema generation but not direct migrations. ```bash npx auth@latest generate ``` -------------------------------- ### Manage Better Auth Schemas and Migrations using CLI (Bash) Source: https://context7.com/better-auth/better-auth/llms.txt This section provides command-line interface (CLI) commands for managing Better Auth. It includes commands to generate ORM schemas for popular ORMs like Drizzle and Prisma, apply database migrations specifically for the Kysely adapter, and generate a secure secret key. ```bash # Generate schema for your ORM (Drizzle, Prisma) npx auth@latest generate # Apply migrations directly (Kysely adapter only) npx auth@latest migrate # Generate secret key openssl rand -base64 32 ``` -------------------------------- ### Generate Database Schema for Better Auth Plugins Source: https://github.com/better-auth/better-auth/blob/canary/docs/content/docs/basic-usage.mdx Generates the required database schema for Better Auth plugins using the generate command. This creates the necessary tables and migrations for plugin functionality. ```bash npx auth generate ``` -------------------------------- ### Generate Better Auth models in Prisma schema Source: https://github.com/better-auth/better-auth/blob/canary/docs/content/docs/integrations/nitro.mdx This command uses the Better Auth CLI to automatically add required authentication models to your `schema.prisma` file, based on the provided Better Auth configuration file. ```bash npx auth generate --config server/utils/auth.ts ``` -------------------------------- ### Generate Better Auth Database Schema with CLI Source: https://github.com/better-auth/better-auth/blob/canary/docs/content/docs/guides/workos-migration-guide.mdx Run CLI commands to generate and migrate database schema for Better Auth. Use 'migrate' for default adapters or 'generate' for ORM-based adapters like Prisma or Drizzle. ```bash npx auth migrate ``` ```bash npx auth generate ```
Marking progress and planning deep scan
I’ve refreshed docs with Context7 and confirmed the concrete APIs we need (authClient.checkout, authClient.customer.portal, subscription list, Polar webhook plugin). Next I’m mapping every integration point in this repo before patching.
[
{
"content": "Reconfirm Polar + Better Auth docs via Context7 and map required flows",
"status": "completed",
"priority": "high"
},
{
"content": "Deeply scan codebase for pricing/auth/header integration points and existing patterns",
"status": "in_progress",
"priority": "high"
},
{
"content": "Implement Polar + Better Auth server integration and billing data model/webhooks",
"status": "pending",
"priority": "high"
},
{
"content": "Implement client billing integration on landing pricing (all checkout parameters)",
"status": "pending",
"priority": "high"
},
{
"content": "Add Upgrade and Manage Billing entries in user dropdown with proper gating",
"status": "pending",
"priority": "high"
},
{
"content": "Run db generation/migration + diagnostics + typecheck + lint + build",
"status": "pending",
"priority": "high"
}
]Found 255 match(es) in 39 file(s)
/Users/duncan/dev/bmrks/instrumentation-client.ts
9: person_profiles: "identified_only",
/Users/duncan/dev/bmrks/server/index.ts
1: export { router, type Router } from "./router";
2: export { base, authed } from "./context";
/Users/duncan/dev/bmrks/lib/feed.ts
45: const profileUrl = `${baseUrl}/u/${user.username}`;
48: ? `${profileUrl}/feed.xml?group=${encodeURIComponent(slug)}`
49: : `${profileUrl}/feed.xml`;
60: const guid = bookmark.url || `${profileUrl}#${bookmark.title}`;
78: <link>${escapeXml(profileUrl)}</link>
94: const profileUrl = `${baseUrl}/u/${user.username}`;
97: ? `${profileUrl}/feed.atom?group=${encodeURIComponent(slug)}`
98: : `${profileUrl}/feed.atom`;
108: const id = bookmark.url || `${profileUrl}#${encodeURIComponent(bookmark.title)}`;
128: <link href="${escapeXml(profileUrl)}" />
134: <id>${escapeXml(profileUrl)}</id>
/Users/duncan/dev/bmrks/hooks/use-focus-refetch.ts
5: import { orpc } from "@/lib/orpc";
21: queryClient.invalidateQueries({ queryKey: orpc.group.key() });
24: queryKey: orpc.bookmark.list.key({ input: { groupId: group.id } }),
/Users/duncan/dev/bmrks/lib/auth-client.ts
3: export const authClient = createAuthClient();
9: useSession,
12: } = authClient;
/Users/duncan/dev/bmrks/components/landing-pricing.tsx
39: label: "Public profile",
/Users/duncan/dev/bmrks/server/context.ts
1: import { ORPCError, os } from "@orpc/server";
7: const session: Session | null = await auth.api.getSession({
19: export const authed = base.use(({ context, next }) => {
/Users/duncan/dev/bmrks/app/rpc/[[...rest]]/route.ts
1: import { RPCHandler } from "@orpc/server/fetch";
2: import { onError } from "@orpc/server";
3: import { router } from "@/server";
5: const handler = new RPCHandler(router, {
/Users/duncan/dev/bmrks/app/api/og/route.tsx
5: import { getPublicProfileData } from "@/server/queries/public-profile";
/Users/duncan/dev/bmrks/app/login/page.tsx
3: import { getSession } from "@/lib/auth-server";
7: const session = await getSession();
/Users/duncan/dev/bmrks/server/router.ts
17: } from "./procedures/bookmarks";
22: } from "./procedures/profile";
23: import { getPublicProfile } from "./procedures/public";
25: export const router = base.router({
44: profile: {
54: export type Router = typeof router;
/Users/duncan/dev/bmrks/lib/orpc.ts
3: import type { RouterClient } from "@orpc/server";
4: import { RPCLink } from "@orpc/client/fetch";
5: import { createORPCClient } from "@orpc/client";
6: import { createTanstackQueryUtils } from "@orpc/tanstack-query";
20: export const orpc = createTanstackQueryUtils(client);
/Users/duncan/dev/bmrks/components/dashboard-demo.tsx
298: const router = useRouter();
333: router.push("/login");
/Users/duncan/dev/bmrks/app/u/[username]/page.tsx
4: import { getPublicProfileData } from "@/server/queries/public-profile";
5: import { getSession } from "@/lib/auth-server";
7: import { PublicProfileContent } from "./public-profile-content";
64: type: "profile",
84: getSession(),
/Users/duncan/dev/bmrks/server/procedures/public.ts
3: import { ORPCError } from "@orpc/server";
4: import { getPublicProfileData } from "../queries/public-profile";
/Users/duncan/dev/bmrks/components/toaster-provider.tsx
6: () => import("@/components/ui/sonner").then((m) => m.Toaster),
/Users/duncan/dev/bmrks/server/procedures/profile.ts
1: import { authed } from "../context";
5: import { ORPCError } from "@orpc/server";
7: export const getProfile = authed.handler(async ({ context }) => {
23: export const updateProfile = authed
65: export const checkUsername = authed
/Users/duncan/dev/bmrks/components/settings-dialog.tsx
10: import { toast } from "sonner";
11: import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
13: import { authClient } from "@/lib/auth-client";
14: import { client, orpc } from "@/lib/orpc";
67: profile?: ProfileData;
75: profile,
78: const router = useRouter();
100: toast.error("Only PNG, JPEG, WebP, and GIF files are supported");
105: toast.error("Avatar must be 2MB or smaller");
125: toast.success("Avatar updated");
126: router.refresh();
128: toast.error(error instanceof Error ? error.message : "Failed to upload avatar");
150: toast.success("Avatar removed");
151: router.refresh();
153: toast.error(error instanceof Error ? error.message : "Failed to remove avatar");
166: const { error } = await authClient.updateUser({ name: name.trim() });
170: toast.error("Failed to update name");
175: toast.success("Name updated");
177: router.refresh();
182: toast.error("Install the Chrome extension to import bookmarks", {
192: const loadingId = toast.loading("Importing browser bookmarks...");
202: toast.error("Please log in to import bookmarks");
204: toast.error(result.message || "Failed to import bookmarks. Please try again.");
214: toast.success(parts.join(". "));
216: queryClient.invalidateQueries({ queryKey: orpc.bookmark.key() });
217: queryClient.invalidateQueries({ queryKey: orpc.group.key() });
219: toast.error("Failed to import bookmarks. Please try again.");
221: toast.dismiss(loadingId);
236: <TabsTrigger value="profile">Public Profile</TabsTrigger>
363: <TabsContent value="profile">
364: {profile && <ProfileTab profile={profile} onOpenChange={onOpenChange} />}
373: profile: ProfileData;
377: function ProfileTab({ profile, onOpenChange }: ProfileTabProps) {
378: const router = useRouter();
380: const [username, setUsername] = useState(profile.username ?? "");
381: const [bio, setBio] = useState(profile.bio ?? "");
382: const [github, setGithub] = useState(profile.github ?? "");
383: const [twitter, setTwitter] = useState(profile.twitter ?? "");
385: profile.website?.replace(/^https?:\/\//, "") ?? "",
388: profile.isProfilePublic,
408: ...orpc.profile.checkUsername.queryOptions({
422: const updateMutation = useMutation({
423: mutationFn: (data: Parameters<typeof client.profile.update>[0]) =>
424: client.profile.update(data),
427: toast.success("Profile updated");
429: router.refresh();
432: toast.error(err.message || "Failed to update profile");
439: const originalWebsite = profile.website?.replace(/^https?:\/\//, "") ?? "";
441: username !== (profile.username ?? "") ||
442: bio !== (profile.bio ?? "") ||
443: github !== (profile.github ?? "") ||
444: twitter !== (profile.twitter ?? "") ||
446: isProfilePublic !== profile.isProfilePublic;
483: toast.success("Link copied");
/Users/duncan/dev/bmrks/app/u/[username]/feed-response.ts
2: import { getPublicProfileData } from "@/server/queries/public-profile";
/Users/duncan/dev/bmrks/components/dashboard-content.tsx
5: import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
6: import { toast } from "sonner";
39: import { client, orpc } from "@/lib/orpc";
60: profile: ProfileData;
63: const groupListKey = () => orpc.group.list.queryKey() as readonly unknown[];
65: orpc.bookmark.list.queryKey({
73: profile,
92: ...orpc.group.list.queryOptions(),
99: const hasUsername = profile.username !== null;
142: ...orpc.bookmark.list.queryOptions({
157: ...orpc.bookmark.list.queryOptions({
168: const createBookmarkMutation = useMutation({
169: ...orpc.bookmark.create.mutationOptions(),
172: queryKey: orpc.bookmark.list.queryKey({
176: await queryClient.cancelQueries({ queryKey: orpc.group.list.queryKey() });
227: toast.error("Failed to create bookmark");
234: queryKey: orpc.bookmark.list.key({
238: queryClient.invalidateQueries({ queryKey: orpc.group.key() });
242: const updateBookmarkMutation = useMutation({
267: await queryClient.cancelQueries({ queryKey: orpc.group.list.queryKey() });
269: queryKey: orpc.bookmark.list.queryKey({
275: queryKey: orpc.bookmark.list.queryKey({
385: toast.error("Failed to update bookmark");
390: queryKey: orpc.bookmark.list.key({
397: queryKey: orpc.bookmark.list.key({
402: queryClient.invalidateQueries({ queryKey: orpc.group.key() });
406: const createGroupMutation = useMutation({
416: await queryClient.cancelQueries({ queryKey: orpc.group.list.queryKey() });
457: toast.error("Failed to create group");
460: queryClient.invalidateQueries({ queryKey: orpc.group.key() });
464: const deleteGroupMutation = useMutation({
465: ...orpc.group.delete.mutationOptions(),
467: await queryClient.cancelQueries({ queryKey: orpc.group.list.queryKey() });
500: toast.error("Failed to delete group");
503: queryClient.invalidateQueries({ queryKey: orpc.group.key() });
507: const refetchBookmarkMutation = useMutation({
512: queryKey: orpc.bookmark.list.key({
516: toast.success("Metadata refreshed");
519: toast.error("Failed to refresh metadata");
523: const bulkDeleteMutation = useMutation({
536: queryKey: orpc.bookmark.list.queryKey({
540: await queryClient.cancelQueries({ queryKey: orpc.group.list.queryKey() });
585: toast.error("Failed to delete bookmarks");
590: queryKey: orpc.bookmark.list.key({
595: queryClient.invalidateQueries({ queryKey: orpc.group.key() });
599: const bulkMoveMutation = useMutation({
613: queryKey: orpc.bookmark.list.queryKey({
618: queryKey: orpc.bookmark.list.queryKey({
622: await queryClient.cancelQueries({ queryKey: orpc.group.list.queryKey() });
697: toast.error("Failed to move bookmarks");
702: queryKey: orpc.bookmark.list.key({
709: queryKey: orpc.bookmark.list.key({
714: queryClient.invalidateQueries({ queryKey: orpc.group.key() });
718: const setVisibilityMutation = useMutation({
719: ...orpc.bookmark.setVisibility.mutationOptions(),
723: queryKey: orpc.bookmark.list.queryKey({
749: toast.error("Failed to update visibility");
754: queryKey: orpc.bookmark.list.key({
762: const bulkSetVisibilityMutation = useMutation({
763: ...orpc.bookmark.bulkSetVisibility.mutationOptions(),
767: queryKey: orpc.bookmark.list.queryKey({
793: toast.error("Failed to update visibility");
798: queryKey: orpc.bookmark.list.key({
806: const setGroupVisibilityMutation = useMutation({
807: ...orpc.group.setVisibility.mutationOptions(),
809: await queryClient.cancelQueries({ queryKey: orpc.group.list.queryKey() });
823: toast.success(
834: toast.error("Failed to update group visibility");
837: queryClient.invalidateQueries({ queryKey: orpc.group.key() });
908: toast.success(`Copied ${selectedIds.size} URLs`);
910: toast.error("No URLs to copy");
922: toast.success(`Moved ${selectedIds.size} bookmarks`);
933: toast.success(`Deleted ${selectedIds.size} bookmarks`);
1101: toast.success("Bookmark hidden from public profile");
1104: toast.success("Bookmark visible on public profile");
1116: toast.success(`${selectedIds.size} bookmarks made public`);
1128: toast.success(`${selectedIds.size} bookmarks made private`);
1263: userImage={profile.image}
1264: username={profile.username}
1265: profile={profile}
/Users/duncan/dev/bmrks/components/signup-form.tsx
34: const router = useRouter();
80: router.push("/dashboard");
/Users/duncan/dev/bmrks/components/header.tsx
75: profile?: ProfileData;
94: profile,
100: const router = useRouter();
121: router.push("/login");
442: ? "All bookmarks in this group will become publicly visible on your profile."
443: : "All bookmarks in this group will no longer be publicly visible on your profile."}
497: profile={profile}
/Users/duncan/dev/bmrks/server/procedures/bookmarks.ts
1: import { authed } from "../context";
16: import { ORPCError } from "@orpc/server";
28: export const listBookmarks = authed
41: export const createBookmark = authed
96: export const updateBookmark = authed
120: export const deleteBookmark = authed
129: export const listGroups = authed.handler(async ({ context }) => {
148: export const createGroup = authed
160: export const updateGroup = authed
171: export const deleteGroup = authed
180: export const refetchBookmark = authed
204: export const bulkDeleteBookmarks = authed
213: export const bulkMoveBookmarks = authed
228: export const setBookmarkVisibility = authed
238: export const bulkSetVisibility = authed
253: export const setGroupVisibility = authed
/Users/duncan/dev/bmrks/components/oauth-button.tsx
5: import { toast } from "sonner";
36: toast.error(error.message ?? "Something went wrong. Please try again.");
/Users/duncan/dev/bmrks/components/export-dialog.tsx
25: import { toast } from "sonner";
104: toast.success(
279: toast.success(
/Users/duncan/dev/bmrks/app/changelog/page.tsx
50: { text: "RSS/Atom feeds for public profiles", pr: 49 },
61: { text: "Twitter profile pictures showing generic X favicon instead of actual avatars", pr: 50 },
65: { text: "Broken favicons, missing referrer, and public profile auto-enable" },
77: { text: "Settings dialog with profile management and name editing", pr: 35 },
/Users/duncan/dev/bmrks/components/login-form.tsx
31: const router = useRouter();
70: router.push("/dashboard");
/Users/duncan/dev/bmrks/app/api/avatar/route.ts
6: import { getSession } from "@/lib/auth-server";
129: const session = await getSession();
227: const session = await getSession();
/Users/duncan/dev/bmrks/app/privacy/page.tsx
68: we collect your name, email address, and profile photo (if
/Users/duncan/dev/bmrks/app/signup/page.tsx
3: import { getSession } from "@/lib/auth-server";
7: const session = await getSession();
/Users/duncan/dev/bmrks/app/layout.tsx
6: import { ToasterProvider } from "@/components/toaster-provider";
/Users/duncan/dev/bmrks/app/api/extension/import/route.ts
2: import { getSession } from "@/lib/auth-server";
43: const session = await getSession();
/Users/duncan/dev/bmrks/app/dashboard/page.tsx
3: import { getSession } from "@/lib/auth-server";
9: const session = await getSession();
75: profile={
/Users/duncan/dev/bmrks/lib/orpc.server.ts
3: import { createRouterClient } from "@orpc/server";
4: import { router } from "@/server";
6: export const serverClient = createRouterClient(router);
/Users/duncan/dev/bmrks/components/ui/sonner.tsx
4: import { Toaster as Sonner, type ToasterProps } from "sonner"
13: className="toaster group"
39: toastOptions={{
41: toast: "cn-toast",
/Users/duncan/dev/bmrks/app/api/extension/bookmark/check/route.ts
2: import { getSession } from "@/lib/auth-server";
52: const session = await getSession();
/Users/duncan/dev/bmrks/app/api/extension/bookmark/route.ts
2: import { getSession } from "@/lib/auth-server";
144: const session = await getSession();
/Users/duncan/dev/bmrks/lib/auth-server.ts
6: export async function getSession(): Promise<Session | null> {
8: return auth.api.getSession({ headers: headersList });
/Users/duncan/dev/bmrks/app/admin/page.tsx
5: import { getSession } from "@/lib/auth-server";
167: const session = await getSession();
No matches found
Preparing key file reads
Inspecting auth route conflicts
Evaluating better-auth dependency usage
Designing subscription data model and checkout integration
Reassessing OAuth redirect handling
Planning backend integration
Outlining Polar integration plan
bun add @polar-sh/better-auth @polar-sh/sdk[1.30ms] ".env" bun add v1.3.2 (b131639c) Resolving dependencies Resolved, downloaded and extracted [157] Saved lockfile installed @polar-sh/better-auth@1.8.1 installed @polar-sh/sdk@0.43.1 62 packages installed [11.20s]
Exploring type definitions with grep