Skip to content

Payment Tracking (Stripe)

Payment tracking captures completed purchases and triggers commission calculation. This is where partners actually earn money.


Choose based on your setup:

MethodHow it worksBest for
Affitor PayRedirect to Affitor checkoutFastest setup, no Stripe changes
Bill FlowAdd metadata to your Stripe checkoutExisting Stripe integration

Coming Soon: Split Pay – Automatic fund splitting via Stripe Connect


Redirect affiliate-referred customers to Affitor’s hosted checkout. Affitor handles payment collection and tracking automatically.

npm SDK (Recommended):

import { loadAffitor } from '@affitor/tracker';
// When customer clicks "Buy" or "Subscribe"
const affitor = await loadAffitor('YOUR_PROGRAM_ID');
affitor?.redirectToCheckout({
price: 99.99,
currency: 'USD',
product_name: 'Pro Plan'
});

Script tag:

// When customer clicks "Buy" or "Subscribe"
var checkoutData = { price: 99.99, currency: 'USD', product_name: 'Pro Plan' };
if (window.affitor) {
window.affitor.redirectToCheckout(checkoutData);
} else {
window.affitorQueue = window.affitorQueue || [];
window.affitorQueue.push(['redirectToCheckout', checkoutData]);
}
ParameterTypeRequiredDescription
pricenumberYesProduct price
currencystringNoCurrency code (default: USD)
product_namestringNoProduct name for checkout
  1. Customer clicks buy → redirectToCheckout() called
  2. Affitor creates Stripe Checkout session with tracking metadata
  3. Customer completes payment on Affitor-hosted page
  4. Webhook fires → conversion tracked → commission calculated
  5. You receive payment minus commission and platform fee

Pros: Zero Stripe configuration needed
Cons: Customers see Affitor checkout, not your branded checkout


Add Affitor metadata to your existing Stripe Checkout sessions. You collect payment normally; Affitor tracks via webhooks and invoices you weekly.

Include these metadata fields when creating Stripe Checkout:

// Helper to read Affitor cookies
function getCookie(name) {
const value = `; ${document.cookie}`;
const parts = value.split(`; ${name}=`);
if (parts.length === 2) return parts.pop().split(';').shift();
return null;
}
const customerCode = getCookie('customer_code');
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',
// ✅ REQUIRED: All three tracking fields
metadata: {
customer_code: customerCode,
user_id: currentUser.id,
program_id: 'YOUR_PROGRAM_ID'
}
});
// Helper to read Affitor cookies
function getCookie(name) {
const value = `; ${document.cookie}`;
const parts = value.split(`; ${name}=`);
if (parts.length === 2) return parts.pop().split(';').shift();
return null;
}
const customerCode = getCookie('customer_code');
const session = await stripe.checkout.sessions.create({
line_items: [{
price: 'price_xxx',
quantity: 1,
}],
mode: 'subscription',
success_url: 'https://yoursite.com/success',
cancel_url: 'https://yoursite.com/cancel',
// ✅ REQUIRED: Session metadata (first payment)
metadata: {
customer_code: customerCode,
user_id: currentUser.id,
program_id: 'YOUR_PROGRAM_ID'
},
// ✅ REQUIRED: Subscription metadata (recurring payments)
subscription_data: {
metadata: {
customer_code: customerCode,
user_id: currentUser.id,
program_id: 'YOUR_PROGRAM_ID'
}
}
});
// In your checkout endpoint
app.post('/api/create-checkout', async (req, res) => {
// Extract customer_code from cookies (sent in request headers)
const cookies = req.headers.cookie || '';
const customerCode = cookies.split('; ')
.find(row => row.startsWith('customer_code='))
?.split('=')[1];
// Get user from your authentication system
const userId = req.user.id;
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',
// ✅ REQUIRED: All three tracking fields
metadata: {
customer_code: customerCode,
user_id: userId,
program_id: process.env.AFFITOR_PROGRAM_ID
}
});
res.json({ url: session.url });
});
  1. Customer clicks affiliate link → tracked with internal cookies
  2. Customer signs up → calls trackLead({ user_id }) with your internal user ID
  3. Customer clicks buy → your checkout with user_id in metadata
  4. Customer completes payment via your Stripe
  5. Stripe webhook fires → Affitor receives event
  6. Affitor looks up customer-partner using user_id → attributes to partner
  7. Commission calculated → Affitor invoices you weekly

Pros: Simple integration, use your existing checkout, no cookie reading required
Cons: Requires metadata setup, weekly invoice payment


When Affitor receives a payment webhook from Stripe, it uses a 4-step fallback mechanism to find the customer-partner relationship and attribute the sale correctly.

Payment Event Received (Stripe webhook)
Step 1: Lookup by customer_code (from cookie)
Found? ──Yes──→ ✅ Link to attribution
No (cookies deleted)
Step 2: Lookup by email
Found? ──Yes──→ ✅ Link + Update stripe_customer_id
│ 📍 Last-click attribution calculated from here
│ (CPL/CPS tracking from lead/signup event)
No
Step 3: Lookup by stripe_customer_id (returning customer)
OR email_hash (from Stripe email)
Found? ──Yes──→ ✅ Link to attribution
No
Step 4: Lookup by advertiser_user_id (if provided)
Found? ──Yes──→ ✅ Link to attribution
No
❌ No attribution found (organic sale)
→ Payment NOT tracked (discarded)
ScenarioLookup MethodExample
Normal flowcustomer_code (Step 1)Customer has cookies intact
Cookies clearedemail (Step 2)Customer cleared browser data
Returning customerstripe_customer_id (Step 3)Customer made previous purchase
Cross-deviceemail_hash (Step 3)Customer clicked on mobile, bought on desktop
Custom trackingadvertiser_user_id (Step 4)You provided custom user ID

When the webhook fires, Affitor extracts:

  • Payment confirmation
  • program_id from metadata
  • customer_code from metadata (if available)
  • user_id from metadata (if available)
  • Customer email from Stripe
  • stripe_customer_id from Stripe

Once a customer-partner relationship is found:

  1. Verifies attribution is within 60-day window
  2. Creates commission record
  3. Updates stripe_customer_id if not already set (Step 2)

If no attribution is found after all 4 steps, the payment is considered an organic sale and is NOT tracked (discarded). This ensures only affiliate-referred sales generate commissions.


FieldTypeRequiredDescription
customer_codestringYesCustomer identifier from Affitor cookie (set automatically when user clicks affiliate link)
user_idstringYesYour internal user ID (must match the value from trackLead())
program_idstringYesYour Affitor program ID

All three fields are required for reliable payment attribution.

  • Customer Code: Set automatically by Affitor tracker when user clicks affiliate link. Read from cookie using the helper function.
  • User ID Must Match: The user_id must exactly match the value you passed during trackLead().
  • Program ID: Find your program ID in the Affitor dashboard under Settings.
  • Hybrid Attribution: Backend tries customer_code first (primary), falls back to user_id if cookies are blocked.

  1. Go to Affitor dashboard → Settings → Payments Tracker
  2. Stripe Connect Integration should show “Connected” after you’ve completed a connect
  3. Payments Tracker should show a blue checkmark

Check:

  • All three required fields included in Stripe metadata:
    • customer_code (from Affitor cookie)
    • user_id (your internal user ID)
    • program_id (your Affitor program ID)
  • Cookie reading function working correctly
  • Cookies not blocked by browser (check browser console)
  • user_id matches the value used in trackLead() during signup
  • Webhook endpoint configured correctly
  • Webhook events selected (checkout.session.completed)
  • User signed up via affiliate link (customer-partner record exists)

Check:

  • Customer clicked partner’s link before signing up
  • trackLead() was called during signup with correct user_id
  • Customer-partner relationship exists in database
  • customer_code in metadata matches the cookie value
  • user_id in Stripe metadata exactly matches signup user_id

Check:

  • subscription_data.metadata includes all three fields:
    • customer_code
    • user_id
    • program_id
  • invoice.payment_succeeded webhook event selected
  • Partner within commission duration window
  • Original checkout has customer-partner relationship

Payment tracking is set up. See the complete flow: