refVenuerefVenue
Docs
Server-Side Tracking

Server-Side Tracking

Track conversions from your backend server for better security and control

Server-Side Tracking

Track conversions directly from your server for more control, security, and reliability.

Why Server-Side?

Benefits:

  • More reliable: Not affected by ad blockers
  • More secure: Publishable keys never exposed to client
  • More accurate: Direct server-to-server communication
  • More control: Full control over tracking logic
  • Better for fraud prevention: Access to server-side data (IP, user agent)

When to use:

  • You process payments server-side
  • You want maximum security
  • You need to track conversions from multiple sources
  • You're building a custom integration

Quick Start

1. Get Your Publishable Keys

  1. Log in to refVenue
  2. Go to SettingsAPI Keys/Secrets
  3. Copy your Publishable Key
  4. Note your Program ID

2. Track Conversions

Make a POST request to the tracking endpoint:

const response = await fetch('https://tracking.refvenue.com/v1/track-conversion', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    program_id: 'YOUR_PROGRAM_ID',
    ref_code: 'AFFILIATE_CODE',
    publishable_key: 'YOUR_PUBLISHABLE_KEY',
    customer_email: 'customer@example.com',
    conversion_type: 'purchase',
    revenue_amount: 99.99,
    customer_ip: '203.0.113.42',
    customer_user_agent: 'Mozilla/5.0...'
  })
});
 
const result = await response.json();
console.log('Conversion tracked:', result.conversion_id);

Complete Examples

Node.js / Express

const express = require('express');
const fetch = require('node-fetch');
const cookieParser = require('cookie-parser');
 
const app = express();
app.use(express.json());
app.use(cookieParser());
 
// Helper: Get client IP address
function getClientIP(req) {
  return req.headers['x-forwarded-for']?.split(',')[0] ||
         req.headers['x-real-ip'] ||
         req.connection.remoteAddress;
}
 
// Track click when user visits with ref parameter
app.get('/', async (req, res) => {
  const refCode = req.query.ref;
 
  if (refCode) {
    // Store in cookie for future conversions
    res.cookie('affiliate_ref', refCode, {
      maxAge: 30 * 24 * 60 * 60 * 1000, // 30 days
      httpOnly: true
    });
 
    // Track the click (optional)
    try {
      await fetch('https://tracking.refvenue.com/v1/track-click', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          program_id: process.env.REFVENUE_PROGRAM_ID,
          ref_code: refCode,
          publishable_key: process.env.REFVENUE_KEY,
          ip_address: getClientIP(req),
          user_agent: req.headers['user-agent']
        })
      });
    } catch (error) {
      console.error('Click tracking failed:', error);
    }
  }
 
  res.send('Welcome!');
});
 
// Track signup
app.post('/signup', async (req, res) => {
  const { email } = req.body;
 
  // Your signup logic here
  await createUserAccount(email);
 
  // Track conversion
  const refCode = req.cookies.affiliate_ref;
 
  if (refCode) {
    try {
      const response = await fetch('https://tracking.refvenue.com/v1/track-conversion', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          program_id: process.env.REFVENUE_PROGRAM_ID,
          ref_code: refCode,
          publishable_key: process.env.REFVENUE_KEY,
          customer_email: email,
          customer_ip: getClientIP(req),
          customer_user_agent: req.headers['user-agent'],
          conversion_type: 'signup'
        })
      });
 
      const result = await response.json();
      console.log('Signup tracked:', result.conversion_id);
    } catch (error) {
      console.error('Tracking failed:', error);
    }
  }
 
  res.json({ success: true });
});
 
// Track purchase (with email OR coupon)
app.post('/purchase', async (req, res) => {
  const { email, amount, couponCode } = req.body;
 
  // Your payment processing here
  await processPayment(email, amount);
 
  // Build tracking payload
  const payload = {
    program_id: process.env.REFVENUE_PROGRAM_ID,
    publishable_key: process.env.REFVENUE_KEY,
    conversion_type: 'purchase',
    revenue_amount: amount, // Post-discount amount
    customer_ip: getClientIP(req),
    customer_user_agent: req.headers['user-agent']
  };
 
  // Use coupon code if provided, otherwise use ref from cookie
  if (couponCode) {
    payload.coupon_code = couponCode;
    // customer_email is optional with coupon
    if (email) payload.customer_email = email;
  } else {
    const refCode = req.cookies.affiliate_ref;
    if (!refCode) {
      return res.json({ success: true }); // No tracking
    }
    payload.ref_code = refCode;
    payload.customer_email = email;
  }
 
  // Track the purchase
  try {
    const response = await fetch('https://tracking.refvenue.com/v1/track-conversion', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(payload)
    });
 
    const result = await response.json();
    console.log('Purchase tracked:', result.conversion_id);
  } catch (error) {
    console.error('Tracking failed:', error);
  }
 
  res.json({ success: true });
});
 
app.listen(3000);

Python / Flask

from flask import Flask, request, jsonify, make_response
import requests
import os
from datetime import datetime, timedelta
 
app = Flask(__name__)
app.config['SECRET_KEY'] = 'your-secret-key'
 
REFVENUE_KEY = os.environ['REFVENUE_KEY']
REFVENUE_PROGRAM_ID = os.environ['REFVENUE_PROGRAM_ID']
 
def get_client_ip():
    """Get client IP address"""
    if request.headers.get('X-Forwarded-For'):
        return request.headers['X-Forwarded-For'].split(',')[0]
    elif request.headers.get('X-Real-IP'):
        return request.headers['X-Real-IP']
    else:
        return request.remote_addr
 
@app.route('/')
def index():
    """Track clicks when ref parameter is present"""
    ref_code = request.args.get('ref')
 
    if ref_code:
        # Store in cookie
        resp = make_response('Welcome!')
        resp.set_cookie(
            'affiliate_ref',
            ref_code,
            max_age=30*24*60*60,  # 30 days
            httponly=True
        )
 
        # Track the click (optional)
        try:
            requests.post(
                'https://tracking.refvenue.com/v1/track-click',
                json={
                    'program_id': REFVENUE_PROGRAM_ID,
                    'ref_code': ref_code,
                    'publishable_key': REFVENUE_KEY,
                    'ip_address': get_client_ip(),
                    'user_agent': request.headers.get('User-Agent')
                }
            )
        except Exception as e:
            print(f'Click tracking failed: {e}')
 
        return resp
 
    return 'Welcome!'
 
@app.route('/signup', methods=['POST'])
def signup():
    """Track signup conversions"""
    email = request.json['email']
 
    # Your signup logic
    # create_user_account(email)
 
    # Track conversion
    ref_code = request.cookies.get('affiliate_ref')
 
    if ref_code:
        try:
            response = requests.post(
                'https://tracking.refvenue.com/v1/track-conversion',
                json={
                    'program_id': REFVENUE_PROGRAM_ID,
                    'ref_code': ref_code,
                    'publishable_key': REFVENUE_KEY,
                    'customer_email': email,
                    'customer_ip': get_client_ip(),
                    'customer_user_agent': request.headers.get('User-Agent'),
                    'conversion_type': 'signup'
                }
            )
            result = response.json()
            print(f"Signup tracked: {result.get('conversion_id')}")
        except Exception as e:
            print(f'Tracking failed: {e}')
 
    return jsonify({'success': True})
 
@app.route('/purchase', methods=['POST'])
def purchase():
    """Track purchase conversions"""
    email = request.json['email']
    amount = request.json['amount']
    coupon_code = request.json.get('coupon_code')
 
    # Your payment processing
    # process_payment(email, amount)
 
    # Build tracking payload
    payload = {
        'program_id': REFVENUE_PROGRAM_ID,
        'publishable_key': REFVENUE_KEY,
        'conversion_type': 'purchase',
        'revenue_amount': amount,
        'customer_ip': get_client_ip(),
        'customer_user_agent': request.headers.get('User-Agent')
    }
 
    # Use coupon or ref code
    if coupon_code:
        payload['coupon_code'] = coupon_code
        if email:
            payload['customer_email'] = email
    else:
        ref_code = request.cookies.get('affiliate_ref')
        if not ref_code:
            return jsonify({'success': True})  # No tracking
 
        payload['ref_code'] = ref_code
        payload['customer_email'] = email
 
    # Track the purchase
    try:
        response = requests.post(
            'https://tracking.refvenue.com/v1/track-conversion',
            json=payload
        )
        result = response.json()
        print(f"Purchase tracked: {result.get('conversion_id')}")
    except Exception as e:
        print(f'Tracking failed: {e}')
 
    return jsonify({'success': True})
 
if __name__ == '__main__':
    app.run(debug=True)

PHP / Laravel

<?php
 
namespace App\Http\Controllers;
 
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Cookie;
 
class AffiliateController extends Controller
{
    private $refvenueKey;
    private $refvenueProgramId;
 
    public function __construct()
    {
        $this->refvenueKey = env('REFVENUE_KEY');
        $this->refvenueProgramId = env('REFVENUE_PROGRAM_ID');
    }
 
    /**
     * Track clicks when ref parameter is present
     */
    public function index(Request $request)
    {
        $refCode = $request->query('ref');
 
        if ($refCode) {
            // Store in cookie
            Cookie::queue('affiliate_ref', $refCode, 30 * 24 * 60); // 30 days
 
            // Track the click (optional)
            try {
                Http::post('https://tracking.refvenue.com/v1/track-click', [
                    'program_id' => $this->refvenueProgramId,
                    'ref_code' => $refCode,
                    'publishable_key' => $this->refvenueKey,
                    'ip_address' => $request->ip(),
                    'user_agent' => $request->userAgent()
                ]);
            } catch (\Exception $e) {
                \Log::error('Click tracking failed: ' . $e->getMessage());
            }
        }
 
        return view('welcome');
    }
 
    /**
     * Track signup
     */
    public function signup(Request $request)
    {
        $email = $request->input('email');
 
        // Your signup logic
        // User::create(['email' => $email]);
 
        // Track conversion
        $refCode = $request->cookie('affiliate_ref');
 
        if ($refCode) {
            try {
                $response = Http::post('https://tracking.refvenue.com/v1/track-conversion', [
                    'program_id' => $this->refvenueProgramId,
                    'ref_code' => $refCode,
                    'publishable_key' => $this->refvenueKey,
                    'customer_email' => $email,
                    'customer_ip' => $request->ip(),
                    'customer_user_agent' => $request->userAgent(),
                    'conversion_type' => 'signup'
                ]);
 
                $result = $response->json();
                \Log::info('Signup tracked: ' . $result['conversion_id']);
            } catch (\Exception $e) {
                \Log::error('Tracking failed: ' . $e->getMessage());
            }
        }
 
        return response()->json(['success' => true]);
    }
 
    /**
     * Track purchase
     */
    public function purchase(Request $request)
    {
        $email = $request->input('email');
        $amount = $request->input('amount');
        $couponCode = $request->input('coupon_code');
 
        // Your payment processing
        // Payment::process($email, $amount);
 
        // Build tracking payload
        $payload = [
            'program_id' => $this->refvenueProgramId,
            'publishable_key' => $this->refvenueKey,
            'conversion_type' => 'purchase',
            'revenue_amount' => $amount,
            'customer_ip' => $request->ip(),
            'customer_user_agent' => $request->userAgent()
        ];
 
        // Use coupon or ref code
        if ($couponCode) {
            $payload['coupon_code'] = $couponCode;
            if ($email) {
                $payload['customer_email'] = $email;
            }
        } else {
            $refCode = $request->cookie('affiliate_ref');
            if (!$refCode) {
                return response()->json(['success' => true]);
            }
 
            $payload['ref_code'] = $refCode;
            $payload['customer_email'] = $email;
        }
 
        // Track the purchase
        try {
            $response = Http::post('https://tracking.refvenue.com/v1/track-conversion', $payload);
            $result = $response->json();
            \Log::info('Purchase tracked: ' . $result['conversion_id']);
        } catch (\Exception $e) {
            \Log::error('Tracking failed: ' . $e->getMessage());
        }
 
        return response()->json(['success' => true]);
    }
}

When a user visits with ?ref=CODE:

// Node.js/Express
res.cookie('affiliate_ref', refCode, {
  maxAge: 30 * 24 * 60 * 60 * 1000, // 30 days
  httpOnly: true,
  secure: process.env.NODE_ENV === 'production',
  sameSite: 'lax'
});
 
// Python/Flask
from datetime import datetime, timedelta
resp.set_cookie(
    'affiliate_ref',
    ref_code,
    max_age=30*24*60*60,  # 30 days
    httponly=True,
    secure=True,  # HTTPS only
    samesite='Lax'
)
 
// PHP/Laravel
Cookie::queue('affiliate_ref', $refCode, 30 * 24 * 60, '/', null, true, true);

When tracking conversions:

// Node.js/Express (with cookie-parser)
const refCode = req.cookies.affiliate_ref;
 
// Python/Flask
ref_code = request.cookies.get('affiliate_ref')
 
// PHP/Laravel
$refCode = $request->cookie('affiliate_ref');

Error Handling

Always handle tracking errors gracefully:

async function trackConversion(data) {
  try {
    const response = await fetch('https://tracking.refvenue.com/v1/track-conversion', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(data)
    });
 
    if (response.status === 429) {
      console.warn('Rate limit exceeded');
      return { success: false, reason: 'rate_limit' };
    }
 
    if (response.status === 404) {
      console.warn('Invalid affiliate or coupon code');
      return { success: false, reason: 'invalid_code' };
    }
 
    if (!response.ok) {
      const error = await response.json();
      console.error('Tracking failed:', error);
      return { success: false, reason: 'api_error', error };
    }
 
    const result = await response.json();
    return { success: true, data: result };
 
  } catch (error) {
    console.error('Network error:', error);
    return { success: false, reason: 'network_error', error };
  }
}

Best Practices

1. Always Pass Customer Data

Include IP and user agent for fraud detection:

{
  customer_ip: getClientIP(req),
  customer_user_agent: req.headers['user-agent']
}

2. Track After Success

Only track conversions after they succeed:

// ✅ Good
await processPayment();
await trackConversion();
 
// ❌ Bad
await trackConversion();
await processPayment();  // What if this fails?

3. Use Environment Variables

Never hardcode Publishable keys:

# .env
REFVENUE_KEY=refv_live_...
REFVENUE_PROGRAM_ID=prog_...

4. Don't Block User Flow

Tracking failures shouldn't block users:

try {
  await trackConversion(data);
} catch (error) {
  // Log error but continue
  console.error('Tracking failed:', error);
}
 
// Continue with user flow
return res.json({ success: true });

5. Pass Post-Discount Amount

Always pass the net amount:

const originalPrice = 100;
const discountAmount = 20;
const finalAmount = originalPrice - discountAmount;
 
await trackConversion({
  revenue_amount: finalAmount  // 80, not 100
});

Advanced Patterns

Retry Logic

Implement retries for reliability:

async function trackWithRetry(data, maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    try {
      const response = await fetch('https://tracking.refvenue.com/v1/track-conversion', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(data)
      });
 
      if (response.ok) {
        return await response.json();
      }
 
      if (response.status === 429) {
        // Rate limited, wait and retry
        await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1)));
        continue;
      }
 
      // Other errors, don't retry
      break;
 
    } catch (error) {
      if (i === maxRetries - 1) throw error;
      await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1)));
    }
  }
}

Queue System

For high-volume applications, use a queue:

// Using Bull queue (Redis)
const Queue = require('bull');
const trackingQueue = new Queue('tracking', process.env.REDIS_URL);
 
// Add to queue
trackingQueue.add({
  program_id: 'prog_...',
  ref_code: 'ABC123',
  customer_email: 'user@example.com',
  conversion_type: 'purchase',
  revenue_amount: 99.99
});
 
// Process queue
trackingQueue.process(async (job) => {
  const response = await fetch('https://tracking.refvenue.com/v1/track-conversion', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(job.data)
  });
 
  return await response.json();
});

Testing

Development Environment

Use a test program for development:

const REFVENUE_CONFIG = {
  programId: process.env.NODE_ENV === 'production'
    ? 'prog_live_...'
    : 'prog_test_...',
  key: process.env.NODE_ENV === 'production'
    ? process.env.REFVENUE_KEY_LIVE
    : process.env.REFVENUE_KEY_TEST
};

Mock Tracking in Tests

Mock the tracking endpoint:

// Jest example
jest.mock('node-fetch');
 
test('tracks conversion', async () => {
  fetch.mockResolvedValue({
    ok: true,
    json: async () => ({
      success: true,
      conversion_id: 'conv_123'
    })
  });
 
  await trackConversion({ /* data */ });
 
  expect(fetch).toHaveBeenCalledWith(
    'https://tracking.refvenue.com/v1/track-conversion',
    expect.any(Object)
  );
});

Comparison: Server-Side vs Client-Side

AspectServer-SideClient-Side (JS)
Security✅ Keys server-side⚠️ Keys in browser
Reliability✅ No ad blockers⚠️ Can be blocked
Setup Complexity⚠️ More code✅ Simple script tag
Flexibility✅ Full control⚠️ Limited
IP Address✅ Accurate⚠️ May be proxied
User Agent✅ Accurate✅ Accurate
Cookie Access⚠️ Need to set✅ Automatic
Best ForBackend systemsSimple websites

Next Steps

Support