Background
⌘K

How to integrate Affonso with next-safe-action?

Learn how to use Affonso affiliate tracking with next-safe-action Server Actions in Next.js.

Silvestro
Written by Silvestro
Updated more than a month ago

next-safe-action is a popular library for type-safe Server Actions in Next.js. Here's how to integrate Affonso affiliate tracking with your Server Actions.

Basic Server Action Integration

First, make sure the Affonso tracking script is installed in your Next.js app. Then use the affonso_referral cookie in your Server Actions:

'use server';

import { z } from 'zod';
import { cookies } from 'next/headers';
import { actionClient } from './safe-action';

const checkoutSchema = z.object({
  email: z.string().email(),
  priceId: z.string(),
});

export const createCheckoutAction = actionClient
  .inputSchema(checkoutSchema)
  .action(async ({ parsedInput: { email, priceId } }) => {
    // Extract affiliate referral cookie
    const cookieStore = await cookies(); // Next.js 15+
    // const cookieStore = cookies(); // Next.js 13-14
    const affiliateReferral = cookieStore.get('affonso_referral')?.value;

    // Create Stripe checkout session with affiliate data
    const session = await stripe.checkout.sessions.create({
      customer_email: email,
      line_items: [{ price: priceId, quantity: 1 }],
      mode: 'subscription',
      success_url: `${process.env.NEXT_PUBLIC_APP_URL}/success`,
      cancel_url: `${process.env.NEXT_PUBLIC_APP_URL}/cancel`,
      // Pass affiliate referral as metadata
      metadata: affiliateReferral
        ? { affonso_referral: affiliateReferral }
        : {},
    });

    return { checkoutUrl: session.url };
  });

You can create a middleware that automatically extracts the affiliate referral:

import { createSafeActionClient } from 'next-safe-action';
import { cookies } from 'next/headers';

export const actionClientWithAffonso = createSafeActionClient({
  // Handle server errors
  handleServerError(e) {
    console.error('Action error:', e.message);
    return 'Something went wrong';
  },
}).use(async ({ next }) => {
  // Extract affiliate referral in middleware
  const cookieStore = await cookies(); // Next.js 15+
  const affiliateReferral = cookieStore.get('affonso_referral')?.value;

  // Pass to action context
  return next({
    ctx: {
      affiliateReferral,
    },
  });
});

Then use it in your actions:

export const createCheckoutAction = actionClientWithAffonso
  .inputSchema(checkoutSchema)
  .action(
    async ({ parsedInput: { email, priceId }, ctx: { affiliateReferral } }) => {
      // affiliateReferral is now available in context
      const session = await stripe.checkout.sessions.create({
        customer_email: email,
        line_items: [{ price: priceId, quantity: 1 }],
        mode: 'subscription',
        success_url: `${process.env.NEXT_PUBLIC_APP_URL}/success`,
        cancel_url: `${process.env.NEXT_PUBLIC_APP_URL}/cancel`,
        metadata: affiliateReferral
          ? { affonso_referral: affiliateReferral }
          : {},
      });

      return { checkoutUrl: session.url };
    }
  );

Frontend Usage

Use the action in your React components:

'use client';

import { useAction } from 'next-safe-action/hooks';
import { createCheckoutAction } from './actions';

export default function CheckoutForm() {
  const { execute, result, isExecuting } = useAction(createCheckoutAction);

  const handleSubmit = (formData: FormData) => {
    execute({
      email: formData.get('email') as string,
      priceId: 'price_1234567890',
    });
  };

  return (
    <form action={handleSubmit}>
      <input type='email' name='email' required />
      <button type='submit' disabled={isExecuting}>
        {isExecuting ? 'Processing...' : 'Subscribe'}
      </button>

      {result?.data?.checkoutUrl &&
        (window.location.href = result.data.checkoutUrl)}
    </form>
  );
}

Best Practices

1. Error Handling

Always handle cases where the affiliate referral might not be present:

metadata: affiliateReferral ? { affonso_referral: affiliateReferral } : {};

2. Type Safety

Define types for your context:

type ActionContext = {
  affiliateReferral?: string;
};

export const actionClientWithAffonso = createSafeActionClient<ActionContext>({
  // ... config
});

3. Logging

Log affiliate referrals for debugging:

.use(async ({ next }) => {
  const affiliateReferral = cookieStore.get('affonso_referral')?.value;

  if (affiliateReferral) {
    console.log('Affiliate referral found:', affiliateReferral);
  }

  return next({ ctx: { affiliateReferral } });
});

Multiple Payment Providers

The same pattern works with other payment providers:

// Polar
const session = await polar.checkouts.create({
  // ...
  metadata: affiliateReferral ? { affonso_referral: affiliateReferral } : {},
});

// Dodo Payments
const session = await dodo.checkout.create({
  // ...
  metadata_affonso_referral: affiliateReferral,
});

This integration gives you the full power of next-safe-action's type safety while seamlessly passing affiliate referral data to your payment providers! 🚀

Was this article helpful?

If you still need help, our support team is here for you.

Contact Support
bg

Ready to Scale Your SaaS?

Affonso is the easiest way to launch your own affiliate program. We take care of the technical stuff, so you can focus on growing your business.