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 };
});
Using Middleware for Automatic Cookie Extraction
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! 🚀