Back to guides
Next.jsIntermediate35 minutes

Next.js affiliate tracking with RefCampaign

Add RefCampaign to a Next.js app with browser click capture, email fallback attribution, and Stripe Checkout metadata.

4 min read

This guide adds RefCampaign to a Next.js app using the npm SDK. The browser captures the affiliate session, your app identifies the user after signup, and your checkout route passes the RefCampaign session into Stripe metadata.

Use this path when your app already has a Next.js frontend and a server route that creates Stripe Checkout sessions.

Prerequisites

  • A RefCampaign merchant account with one active campaign.
  • A RefCampaign public key and secret key from your dashboard.
  • A Next.js App Router project.
  • Stripe Checkout already installed or ready to install.

Install the SDK:

pnpm add @refcampaign/sdk stripe

Add the secret key to your server environment:

REFCAMPAIGN_SECRET_KEY=sk_live_your_refcampaign_secret
STRIPE_SECRET_KEY=sk_live_your_stripe_secret
NEXT_PUBLIC_APP_URL=https://yourapp.com

Capture affiliate clicks in the browser

Create a small client component that runs once near the root of your app.

// app/refcampaign-client.tsx
'use client'

import { useEffect } from 'react'
import { RefCampaignBrowser } from '@refcampaign/sdk'

export function RefCampaignClient() {
  useEffect(() => {
    RefCampaignBrowser.captureSession()
  }, [])

  return null
}

Mount it in your root layout so every landing page can capture affiliate visits.

// app/layout.tsx
import { RefCampaignClient } from './refcampaign-client'

export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en">
      <body>
        <RefCampaignClient />
        {children}
      </body>
    </html>
  )
}

Identify users after signup or login

Call identify() as soon as your app knows the user's email. RefCampaign hashes the email in the browser and attaches it to the current click. This gives you a fallback when the user converts on another device or after Safari clears cookies.

// app/auth/identify-refcampaign.tsx
'use client'

import { useEffect } from 'react'
import { RefCampaignBrowser } from '@refcampaign/sdk'

type IdentifyRefCampaignProps = {
  email: string | null
}

export function IdentifyRefCampaign({ email }: IdentifyRefCampaignProps) {
  useEffect(() => {
    if (!email) return
    void RefCampaignBrowser.identify(email)
  }, [email])

  return null
}

Render this component after login or signup, where your session provider exposes the user email.

<IdentifyRefCampaign email={user.email} />

Create Stripe Checkout with RefCampaign metadata

Your checkout route should read the RefCampaign session cookie and pass it to RefCampaignServer.getStripeMetadata().

// app/api/checkout/route.ts
import { NextRequest, NextResponse } from 'next/server'
import Stripe from 'stripe'
import { RefCampaignServer } from '@refcampaign/sdk'

const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!)
const refcampaign = new RefCampaignServer(process.env.REFCAMPAIGN_SECRET_KEY!)

export async function POST(request: NextRequest) {
  const { priceId } = await request.json()
  const sessionId = request.cookies.get('_rc_sid')?.value

  const checkout = await stripe.checkout.sessions.create({
    mode: 'subscription',
    line_items: [{ price: priceId, quantity: 1 }],
    subscription_data: {
      metadata: refcampaign.getStripeMetadata(sessionId),
    },
    success_url: `${process.env.NEXT_PUBLIC_APP_URL}/billing/success`,
    cancel_url: `${process.env.NEXT_PUBLIC_APP_URL}/pricing`,
  })

  return NextResponse.json({ url: checkout.url })
}

For one-time payments, put the metadata on payment_intent_data instead:

const checkout = await stripe.checkout.sessions.create({
  mode: 'payment',
  line_items: [{ price: priceId, quantity: 1 }],
  payment_intent_data: {
    metadata: refcampaign.getStripeMetadata(sessionId),
  },
  success_url: `${process.env.NEXT_PUBLIC_APP_URL}/billing/success`,
  cancel_url: `${process.env.NEXT_PUBLIC_APP_URL}/pricing`,
})

Test locally

Run your app and open an affiliate tracking link from your RefCampaign dashboard. After the redirect lands on your local or preview app, check the browser storage and cookie list for _rc_sid.

Then create a checkout session and inspect the Stripe object:

  • subscriptions should contain metadata.refcampaign_session;
  • one-time payments should contain the same key on the Payment Intent;
  • no checkout should fail if _rc_sid is absent.

Common mistakes

Do not call captureSession() only on the pricing page. Affiliates often link to blog posts, comparison pages, or docs pages before the visitor reaches pricing.

Do not block checkout when no RefCampaign session exists. Direct customers should still buy normally.

Do not put subscription metadata on the Stripe Customer object. A customer can subscribe more than once through different affiliate journeys, so subscription-level metadata keeps attribution tied to the right purchase.

Next steps

Read the Stripe affiliate tracking guide for webhook-specific details, or use the SDK reference when you need every method signature. If you are still designing the program, the SaaS affiliate setup guide covers commission rules and operational defaults.