Skip to content

Commit

Permalink
clients/docs: adjust nextjs docs
Browse files Browse the repository at this point in the history
  • Loading branch information
emilwidlund committed Jan 4, 2025
1 parent d1c9ba1 commit 677f2c1
Showing 1 changed file with 55 additions and 92 deletions.
147 changes: 55 additions & 92 deletions clients/apps/web/src/app/(main)/docs/developers/guides/nextjs/page.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,10 @@ npx polar-init

## Install the Polar JavaScript SDK

To get started, you need to install the Polar JavaScript SDK. You can do this by running the following command:
To get started, you need to install the Polar JavaScript SDK and the Polar Nextjs helper package. You can do this by running the following command:

```bash
pnpm install @polar-sh/sdk
pnpm install @polar-sh/sdk @polar-sh/nextjs
```

## Setting up environment variables
Expand Down Expand Up @@ -130,15 +130,15 @@ export const ProductCard = ({ product }: ProductCardProps) => {
</ul>
</div>
<div className="flex flex-row gap-x-4 justify-between items-center">
<Link className="h-8 flex flex-row items-center justify-center rounded-full bg-white text-black font-medium px-4" href={`/checkout?priceId=${firstPrice.id}`}>Buy</Link>
<Link className="h-8 flex flex-row items-center justify-center rounded-full bg-white text-black font-medium px-4" href={`/checkout?productId=${product.id}`}>Buy</Link>
<span className="text-neutral-500">{price}</span>
</div>
</div>
)
}
```

Notice that we create a link to `/checkout` with a query parameter `priceId`. This is the ID of the price that the user will be charged for when they click the "Buy" button. We will configure this route in the next section.
Notice that we create a link to `/checkout` with a query parameter `productId`. We will configure this route in the next section.

### Displaying Products

Expand Down Expand Up @@ -177,30 +177,16 @@ Go ahead and create a new GET route in Next.js.

```typescript
// src/app/checkout/route.ts
import { api } from '@/polar'
import { type NextRequest, NextResponse } from 'next/server'

export async function GET(req: NextRequest) {
const url = new URL(req.url)
const productPriceId = url.searchParams.get('priceId') ?? ''
// Polar will replace {CHECKOUT_ID} with the actual checkout ID upon a confirmed checkout
const confirmationUrl = `${req.nextUrl.protocol}//${req.nextUrl.host}/confirmation?checkout_id={CHECKOUT_ID}`

try {
const result = await api.checkouts.custom.create({
productPriceId,
successUrl: confirmationUrl,
})

return NextResponse.redirect(result.url)
} catch (error) {
console.error(error)
return NextResponse.error()
}
}
import { Checkout } from '@polar-sh/nextjs'

export const GET = Checkout({
accessToken: process.env.POLAR_ACCESS_TOKEN!,
successUrl: '/confirmation',
server: 'sandbox', // Use this option if you're using the sandbox environment - else use 'production' or omit the parameter
});
```

We can now easily create a checkout session & redirect there by creating a link to `/checkout?priceId={priceId}`. Just like we did in the `ProductCard` component.
We can now easily create a checkout session & redirect there by creating a link to `/checkout?productId={productId}`. Just like we did in the `ProductCard` component.

### Handling the Confirmation Page

Expand All @@ -209,10 +195,10 @@ Create a new page in Next.js to handle the confirmation page. This is where the
```tsx
// src/app/confirmation/page.tsx
export default function Page({
searchParams: { checkout_id },
searchParams: { checkoutId },
}: {
searchParams: {
checkout_id: string
checkoutId: string
}
}) {
return <div>Thank you! Your checkout is now being processed.</div>
Expand Down Expand Up @@ -257,83 +243,60 @@ POLAR_WEBHOOK_SECRET="..."

### Setting up the Webhook handler

#### Verifying the signature

It's important to verify that the requests are truly coming from Polar. Our SDK comes with a builtin utility function that validates and parses the webhook payload:

```typescript
// src/app/api/webhook/polar/route.ts
import { validateEvent, WebhookVerificationError } from '@polar-sh/sdk/webhooks'

export async function POST(request: NextRequest) {
const requestBody = await request.text()
const webhookHeaders = {
'webhook-id': request.headers.get('webhook-id') ?? '',
'webhook-timestamp': request.headers.get('webhook-timestamp') ?? '',
'webhook-signature': request.headers.get('webhook-signature') ?? '',
}

let webhookPayload: ReturnType<typeof validateEvent>
try {
webhookPayload = validateEvent(
requestBody,
webhookHeaders,
env.POLAR_WEBHOOK_SECRET,
)
} catch (error) {
if (error instanceof WebhookVerificationError) {
return new NextResponse('', { status: 403 })
}
throw error
}
import { Webhooks } from "@polar-sh/nextjs";

// webhookPayload is now verified and holds the event data
}
export const POST = Webhooks({
webhookSecret: process.env.POLAR_WEBHOOK_SECRET,
onPayload: async (payload) => // Handle payload...
});
```

The webhook event is now verified and you can proceed to handle the event data.
The webhook event is now verified and you can proceed to handle the payload data.

#### Handling Webhook Events

Depending on which events you've subscribed to, you'll receive different payloads. This is where you can update your database, send notifications, etc.

```typescript
// src/app/api/webhook/polar/route.ts
export async function POST(request: NextRequest) {
// ...

switch (webhookPayload.type) {
case 'checkout.created':
// Handle the checkout created event
// supabase.from('checkouts').insert(webhookPayload.data)
break
case 'checkout.updated':
// Handle the checkout updated event
// supabase.from('checkouts').update(webhookPayload.data).match({ id: webhookPayload.data.id })
break
case 'subscription.created':
// Handle the subscription created event
break
case 'subscription.updated':
// Handle the subscription updated event
break
case 'subscription.active':
// Handle the subscription active event
break
case 'subscription.revoked':
// Handle the subscription revoked event
break
case 'subscription.canceled':
// Handle the subscription canceled event
break
default:
// Handle unknown event
console.log('Unknown event', webhookPayload.type)
break
import { Webhooks } from "@polar-sh/nextjs";

export const POST = Webhooks({
webhookSecret: process.env.POLAR_WEBHOOK_SECRET,
onPayload: async (payload) => {
switch (payload.type) {
case 'checkout.created':
// Handle the checkout created event
// supabase.from('checkouts').insert(webhookPayload.data)
break
case 'checkout.updated':
// Handle the checkout updated event
// supabase.from('checkouts').update(webhookPayload.data).match({ id: webhookPayload.data.id })
break
case 'subscription.created':
// Handle the subscription created event
break
case 'subscription.updated':
// Handle the subscription updated event
break
case 'subscription.active':
// Handle the subscription active event
break
case 'subscription.revoked':
// Handle the subscription revoked event
break
case 'subscription.canceled':
// Handle the subscription canceled event
break
default:
// Handle unknown event
console.log('Unknown event', webhookPayload.type)
break
}
}

return NextResponse.json({ received: true })
}
});
```

If you're keeping track of active and inactive subscriptions in your database, make sure to handle the `subscription.active` and `subscription.revoked` events accordingly.
Expand Down

0 comments on commit 677f2c1

Please sign in to comment.