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
- Log in to refVenue
- Go to Settings → API Keys/Secrets
- Copy your Publishable Key
- 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]);
}
}Cookie Management
Setting the Affiliate Cookie
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);Reading the Cookie
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
| Aspect | Server-Side | Client-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 For | Backend systems | Simple websites |
Next Steps
- JavaScript Integration - Client-side alternative
- Stripe Integration - Automatic tracking
- API Reference - Complete API docs
- Webhook API - Direct API calls