@affiliateo/web
v4.2.0
Published
Affiliateo web SDK — affiliate attribution and tracking for web apps
Downloads
1,861
Maintainers
Readme
@affiliateo/web
Affiliateo web SDK. track affiliate visits and pass attribution metadata to your Stripe checkout.
Install
npm install @affiliateo/webSetup
- Connect your Stripe account on the Affiliateo dashboard
- Wrap your app in
<AffiliateoProvider>with your campaign ID - At checkout time, call
getAttribution()and pass the result to Stripe metadata
Step 1. wrap your app
// app/layout.tsx (Next.js App Router) or _app.tsx (Pages Router)
import { AffiliateoProvider } from '@affiliateo/web';
export default function RootLayout({ children }) {
return (
<AffiliateoProvider campaignId="YOUR_CAMPAIGN_ID">
{children}
</AffiliateoProvider>
);
}The provider:
- Reads
?ref=from the URL when a visitor arrives - Stores attribution in
localStorageso it survives navigation - Sends pageview events to Affiliateo for click tracking
Step 2. in your checkout button (client-side)
'use client'
import { getAttribution } from '@affiliateo/web';
async function handleCheckout() {
const attribution = getAttribution();
const res = await fetch('/api/checkout', {
method: 'POST',
body: JSON.stringify({ attribution }),
});
const { url } = await res.json();
window.location.href = url;
}getAttribution() reads affiliateo_ref, affiliateo_visitor_id, and affiliateo_campaign_id from localStorage and returns them as an object, ready to pass to Stripe.
Step 3. in your checkout API route (server-side)
// app/api/checkout/route.ts
export async function POST(req) {
const { attribution } = await req.json();
const session = await stripe.checkout.sessions.create({
line_items: [{ price: 'price_xxx', quantity: 1 }],
mode: 'payment',
success_url: 'https://yoursite.com/success',
cancel_url: 'https://yoursite.com/cancel',
metadata: attribution,
});
return Response.json({ url: session.url });
}That's it. Affiliateo's webhook will automatically read the metadata and credit the right affiliate when the payment succeeds.
How it works
- Visitor clicks affiliate link →
yoursite.com?ref=pinkproton <AffiliateoProvider>reads?ref=pinkprotonfrom the URL and saves it inlocalStorage- As the visitor browses, attribution stays in their browser
- At checkout,
getAttribution()reads localStorage and returns the attribution object - Your server passes it as Stripe metadata when creating the checkout session
- When Stripe fires the
checkout.session.completedwebhook, Affiliateo reads the metadata and credits the affiliate
Why localStorage and not cookies?
- Not blocked by Safari ITP or most privacy tools
- No CSRF concerns
GDPR / ePrivacy. merchant responsibility
The SDK writes persistent identifiers (affiliateo_visitor_id, affiliateo_ref, affiliateo_campaign_id) to the visitor's localStorage and sends pageview events that include the visitor's IP, user agent, and approximate geolocation to Affiliateo's servers. Under EU ePrivacy (Article 5(3)) and GDPR, persistent identifiers stored on a visitor's device require prior consent the same way cookies do.
If you serve visitors in the EU/EEA, UK, or any jurisdiction with equivalent rules, you are responsible for obtaining consent before this script loads, the same way you would for Google Analytics, Meta Pixel, or any other analytics tool. Typical approaches:
- Gate the
<AffiliateoProvider>(or the<script src="…/t.js">tag) behind your cookie banner's "accept" callback so it only loads after consent. - Document Affiliateo in your privacy policy as a processor (see our Data Processing Agreement) and list the categories of data collected.
Affiliateo acts as your data processor for visitor analytics. You are the controller and decide the lawful basis for processing your visitors' data.
Alternative: vanilla HTML (no React)
If you're not using React, use the tracking script directly instead of the provider:
<script defer
data-campaign="YOUR_CAMPAIGN_ID"
src="https://affiliateo.com/t.js"
></script>Then in your checkout flow (vanilla JS):
const attribution = {
affiliateo_ref: localStorage.getItem('affiliateo_ref'),
affiliateo_visitor_id: localStorage.getItem('affiliateo_visitor_id'),
affiliateo_campaign_id: localStorage.getItem('affiliateo_campaign_id'),
};
// Send to your server with the rest of your checkout payload
fetch('/api/checkout', {
method: 'POST',
body: JSON.stringify({ attribution }),
});Track conversion goals
Pageviews auto-track. For specific moments that matter to your funnel
(signup_completed, trial_started, initiate_checkout, etc.), use
track() or HTML data attributes.
From React
'use client'
import { useEffect } from 'react'
import { useAffiliateRef } from '@affiliateo/web'
export default function SignupSuccess() {
const { track } = useAffiliateRef()
useEffect(() => {
track('signup_completed', { plan: 'free' })
}, [])
return <div>Welcome!</div>
}HTML data attributes (zero JS, works everywhere)
<button data-affiliateo-goal="initiate_checkout"
data-affiliateo-goal-price="49"
data-affiliateo-goal-plan="pro">
Subscribe
</button>The SDK auto-fires the goal on click. Optional data-affiliateo-goal-*
attributes become metadata (kebab-case → snake_case, up to 10 params).
Scroll-depth tracking
<section data-affiliateo-scroll="pricing_viewed">
...your pricing content...
</section>Fires the first time any pixel of the section overlaps the viewport. Maximum permissiveness — if the user scrolled enough to see even a sliver, it counts. Earlier versions required 50% visibility, which was unreachable for tall sections (a pricing page with comparison tables silently dropped out of every funnel). Great for landing pages where you want to know which sections convert.
Goal naming rules
Lowercase letters, digits, underscores, hyphens, colons. Max 64 chars. Metadata bounded to 4KB JSON.
Identify signed-in users (cross-device stitching)
Without identify(), the same person browsing on phone and buying on
laptop counts as two visitors. Call it once after sign-in:
'use client'
import { useEffect } from 'react'
import { useAffiliateRef } from '@affiliateo/web'
import { useUser } from './your-auth'
export default function AuthBoundary({ children }) {
const { identify } = useAffiliateRef()
const user = useUser()
useEffect(() => {
if (user) identify(user.id)
}, [user])
return <>{children}</>
}Idempotent. safe to fire on every page load while a user is signed in. user_id only. The SDK does not accept, collect, or transmit email or any other PII.
API
<AffiliateoProvider campaignId="..." apiUrl?="...">
React provider. Wrap your app once. Reads ?ref= from URL, persists to localStorage, sends pageview events, sets up data-attribute + scroll listeners.
Props:
campaignId(required): your Affiliateo campaign IDapiUrl(optional): defaults tohttps://affiliateo.com
useAffiliateRef()
React hook that returns { refCode, visitorId, track, identify }.
track(eventName, metadata?). fire a custom goalidentify(userId). link this visitor to a merchant user_id (no PII)
getAttribution()
Client-side function. Returns an object with affiliateo_ref, affiliateo_visitor_id, and affiliateo_campaign_id from localStorage. Pass directly as Stripe metadata.
Framework support
Works with any JavaScript framework or vanilla JS:
- Next.js (App Router & Pages Router)
- Remix / React Router
- Vite + React
- Vanilla JS/HTML (use the
<script>tag instead)
