Documentation Index
Fetch the complete documentation index at: https://mintlify.com/polarsource/polar/llms.txt
Use this file to discover all available pages before exploring further.
Integrate Polar seamlessly into your Astro application, whether you’re using SSR, SSG, or hybrid rendering modes.
Installation
npm install @polar-sh/sdk
Setup
Configure environment variables
Add your Polar credentials to .env:POLAR_ACCESS_TOKEN=your_access_token_here
PUBLIC_POLAR_SERVER_URL=https://api.polar.sh
Variables prefixed with PUBLIC_ are exposed to the client. Keep your access token private.
Create Polar utilities
Create a utility file for Polar client instances:import { PolarCore } from '@polar-sh/sdk/core'
export function getPolarClient() {
return new PolarCore({
serverURL: import.meta.env.PUBLIC_POLAR_SERVER_URL || 'https://api.polar.sh',
security: {
bearerAuth: import.meta.env.POLAR_ACCESS_TOKEN,
},
})
}
export function getPublicPolarClient() {
return new PolarCore({
serverURL: import.meta.env.PUBLIC_POLAR_SERVER_URL || 'https://api.polar.sh',
})
}
Use in Astro components
Fetch data in your Astro components:---
import { getPolarClient } from '@/lib/polar'
import { productsSearch } from '@polar-sh/sdk/funcs/productsSearch'
const client = getPolarClient()
const { ok, value: products } = await productsSearch(client, {
organizationId: 'your-org-id',
})
if (!ok || !products) {
return Astro.redirect('/error')
}
---
<html>
<head>
<title>Products</title>
</head>
<body>
<h1>Products</h1>
<ul>
{products.items.map((product) => (
<li>
<h2>{product.name}</h2>
<p>{product.description}</p>
</li>
))}
</ul>
</body>
</html>
Server Endpoints
Create API endpoints for server-side operations:
src/pages/api/checkout.ts
import { getPolarClient } from '@/lib/polar'
import { checkoutsCreate } from '@polar-sh/sdk/funcs/checkoutsCreate'
import type { APIRoute } from 'astro'
export const POST: APIRoute = async ({ request }) => {
const { productId } = await request.json()
const client = getPolarClient()
const { ok, value: checkout, error } = await checkoutsCreate(client, {
productId,
successUrl: `${import.meta.env.SITE}/success`,
})
if (!ok) {
return new Response(
JSON.stringify({ error: error?.message || 'Failed to create checkout' }),
{ status: 400 }
)
}
return new Response(JSON.stringify(checkout), {
status: 200,
headers: {
'Content-Type': 'application/json',
},
})
}
Webhook Endpoint
Handle Polar webhooks with API routes:
src/pages/api/webhooks/polar.ts
import { getPolarClient } from '@/lib/polar'
import { webhooksValidatePayload } from '@polar-sh/sdk/funcs/webhooksValidatePayload'
import type { APIRoute } from 'astro'
export const POST: APIRoute = async ({ request }) => {
const signature = request.headers.get('webhook-signature')
const body = await request.text()
if (!signature) {
return new Response(JSON.stringify({ error: 'Missing signature' }), {
status: 401,
})
}
const client = getPolarClient()
const { ok, value: event } = await webhooksValidatePayload(client, {
webhookSignatureHeader: signature,
payload: body,
})
if (!ok) {
return new Response(JSON.stringify({ error: 'Invalid signature' }), {
status: 401,
})
}
// Handle the event
switch (event.type) {
case 'checkout.completed':
console.log('Checkout completed:', event.data)
break
case 'subscription.created':
console.log('Subscription created:', event.data)
break
}
return new Response(JSON.stringify({ received: true }), {
status: 200,
})
}
Client-Side Integration
Use Astro’s client directives for interactive components:
src/components/BuyButton.astro
---
import type { Product } from '@polar-sh/sdk/models/components'
interface Props {
product: Product
}
const { product } = Astro.props
---
<button
class="buy-button"
data-product-id={product.id}
>
Buy {product.name}
</button>
<script>
const buttons = document.querySelectorAll('.buy-button')
buttons.forEach((button) => {
button.addEventListener('click', async () => {
const productId = button.getAttribute('data-product-id')
const response = await fetch('/api/checkout', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ productId }),
})
const data = await response.json()
if (data.url) {
window.location.href = data.url
}
})
})
</script>
<style>
.buy-button {
padding: 0.5rem 1rem;
background-color: #000;
color: #fff;
border: none;
border-radius: 0.5rem;
cursor: pointer;
}
</style>
React/Vue/Svelte Components
Use Astro’s framework integrations with Polar:
src/components/ProductCard.tsx
import { useState } from 'react'
import type { Product } from '@polar-sh/sdk/models/components'
interface Props {
product: Product
}
export function ProductCard({ product }: Props) {
const [loading, setLoading] = useState(false)
const handleBuy = async () => {
setLoading(true)
try {
const response = await fetch('/api/checkout', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ productId: product.id }),
})
const data = await response.json()
if (data.url) {
window.location.href = data.url
}
} finally {
setLoading(false)
}
}
return (
<div className="product-card">
<h3>{product.name}</h3>
<p>{product.description}</p>
<button onClick={handleBuy} disabled={loading}>
{loading ? 'Loading...' : 'Buy Now'}
</button>
</div>
)
}
Use it in your Astro page:
---
import { ProductCard } from '@/components/ProductCard'
import { getPolarClient } from '@/lib/polar'
import { productsSearch } from '@polar-sh/sdk/funcs/productsSearch'
const client = getPolarClient()
const { ok, value: products } = await productsSearch(client, {
organizationId: 'your-org-id',
})
---
<html>
<body>
<h1>Products</h1>
{products?.items.map((product) => (
<ProductCard client:load product={product} />
))}
</body>
</html>
Dynamic Routes
Fetch product data for dynamic routes:
src/pages/products/[id].astro
---
import { getPolarClient } from '@/lib/polar'
import { productsGet } from '@polar-sh/sdk/funcs/productsGet'
const { id } = Astro.params
const client = getPolarClient()
const { ok, value: product } = await productsGet(client, { id: id! })
if (!ok || !product) {
return Astro.redirect('/404')
}
---
<html>
<head>
<title>{product.name}</title>
<meta name="description" content={product.description || ''} />
</head>
<body>
<h1>{product.name}</h1>
<p>{product.description}</p>
</body>
</html>
Static Site Generation
Generate static pages at build time:
src/pages/products/[id].astro
---
import { getPolarClient } from '@/lib/polar'
import { productsGet, productsSearch } from '@polar-sh/sdk/funcs'
export async function getStaticPaths() {
const client = getPolarClient()
const { ok, value: products } = await productsSearch(client, {
organizationId: 'your-org-id',
})
if (!ok || !products) {
return []
}
return products.items.map((product) => ({
params: { id: product.id },
props: { product },
}))
}
const { product } = Astro.props
---
<html>
<head>
<title>{product.name}</title>
</head>
<body>
<h1>{product.name}</h1>
<p>{product.description}</p>
</body>
</html>
Middleware
Add authentication or session management:
import { defineMiddleware } from 'astro:middleware'
import { getPolarClient } from './lib/polar'
import { customersGet } from '@polar-sh/sdk/funcs/customersGet'
export const onRequest = defineMiddleware(async (context, next) => {
const sessionToken = context.cookies.get('polar_session')?.value
if (sessionToken) {
const client = getPolarClient()
const { ok, value: customer } = await customersGet(client, {
id: sessionToken,
})
if (ok && customer) {
context.locals.customer = customer
}
}
return next()
})
Best Practices
Use server endpoints
Keep sensitive operations in API routes to protect your access token.
Optimize builds
Use SSG for product pages when possible to improve performance.
Type safety
Import TypeScript types from the SDK for better development experience.
Cache wisely
Use Astro’s built-in caching for frequently accessed data.
Next Steps
Checkout
Implement Polar Checkout in your Astro site.
Webhooks
Handle real-time events from Polar.
TypeScript SDK
Explore the full TypeScript SDK documentation.
API Reference
Browse the complete API reference.