Skip to main content

Overview

This guide provides reusable integration patterns and code snippets you can use across your Vantio integration. These patterns follow best practices and handle common scenarios.

Referral Flow Pattern

Complete pattern for handling the referral flow from QR scan to earning:
// patterns/referral-flow.js
class ReferralFlowHandler {
  constructor(vantioClient) {
    this.client = vantioClient;
  }
  
  async handleQRScan(qrCodeData) {
    // QR scan creates an impression
    // This is typically handled automatically by Vantio
    // You receive the impression_id from the QR scan
    return qrCodeData.impression_id;
  }
  
  async handleSignup(impressionId, userData) {
    // Create referral when user signs up
    const referral = await this.client.createReferral({
      impression_id: impressionId,
      first_name: userData.firstName,
      last_name: userData.lastName,
      email: userData.email,
      idempotency_key: `ref_${userData.userId}_${Date.now()}`
    });
    
    return referral;
  }
  
  async handlePurchase(referralId, orderData) {
    // Create earning when referred customer makes purchase
    const commissionRate = 0.10; // 10%
    const amountInCents = Math.round(orderData.total * commissionRate * 100);
    
    const earning = await this.client.createEarning({
      referral_id: referralId,
      amount: amountInCents,
      currency: 'USD',
      type: 'purchase',
      description: `Commission from order #${orderData.orderId}`,
      metadata: {
        order_id: orderData.orderId,
        commission_rate: `${commissionRate * 100}%`
      },
      idempotency_key: `earn_${orderData.orderId}_${Date.now()}`
    });
    
    // Update referral status to converted
    await this.client.updateReferral(referralId, {
      status: 'converted'
    });
    
    return earning;
  }
}

// Usage
const flowHandler = new ReferralFlowHandler(vantioClient);

// 1. User scans QR code (impression created automatically)
const impressionId = await flowHandler.handleQRScan(qrCodeData);

// 2. User signs up
const referral = await flowHandler.handleSignup(impressionId, {
  userId: 'user_123',
  firstName: 'John',
  lastName: 'Doe',
  email: '[email protected]'
});

// 3. User makes purchase
const earning = await flowHandler.handlePurchase(referral.id, {
  orderId: 'order_456',
  total: 100.00
});

Idempotency Pattern

Ensure requests are idempotent to prevent duplicates:
// patterns/idempotency.js
class IdempotentRequest {
  constructor() {
    this.processedKeys = new Set();
  }
  
  generateKey(prefix, identifier) {
    return `${prefix}_${identifier}_${Date.now()}`;
  }
  
  async execute(key, requestFn) {
    if (this.processedKeys.has(key)) {
      console.log(`Request ${key} already processed, skipping`);
      return null;
    }
    
    try {
      const result = await requestFn();
      this.processedKeys.add(key);
      return result;
    } catch (error) {
      // Remove key on error so it can be retried
      this.processedKeys.delete(key);
      throw error;
    }
  }
}

// Usage
const idempotent = new IdempotentRequest();

const key = idempotent.generateKey('earn', orderId);
await idempotent.execute(key, async () => {
  return await vantioClient.createEarning({
    ...earningData,
    idempotency_key: key
  });
});

Error Handling Pattern

Comprehensive error handling for API requests:
// patterns/error-handling.js
class VantioError extends Error {
  constructor(message, status, code) {
    super(message);
    this.status = status;
    this.code = code;
    this.name = 'VantioError';
  }
}

async function handleAPIRequest(requestFn) {
  try {
    return await requestFn();
  } catch (error) {
    if (error.response) {
      const { status, data } = error.response;
      
      switch (status) {
        case 400:
          throw new VantioError(
            data.message || 'Bad request',
            status,
            'BAD_REQUEST'
          );
        case 401:
          throw new VantioError(
            'Invalid or missing API key',
            status,
            'UNAUTHORIZED'
          );
        case 404:
          throw new VantioError(
            'Resource not found',
            status,
            'NOT_FOUND'
          );
        case 429:
          throw new VantioError(
            'Rate limit exceeded',
            status,
            'RATE_LIMIT'
          );
        default:
          throw new VantioError(
            data.message || 'API request failed',
            status,
            'UNKNOWN_ERROR'
          );
      }
    }
    
    throw error;
  }
}

// Usage
try {
  const referral = await handleAPIRequest(() =>
    vantioClient.createReferral(referralData)
  );
} catch (error) {
  if (error instanceof VantioError) {
    console.error(`Vantio Error [${error.code}]: ${error.message}`);
  } else {
    console.error('Unexpected error:', error);
  }
}

Retry Pattern

Retry failed requests with exponential backoff:
// patterns/retry.js
async function retryWithBackoff(fn, maxRetries = 3, baseDelay = 1000) {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    try {
      return await fn();
    } catch (error) {
      if (attempt === maxRetries - 1) throw error;
      
      // Don't retry on client errors (4xx)
      if (error.status >= 400 && error.status < 500) {
        throw error;
      }
      
      // Exponential backoff
      const delay = baseDelay * Math.pow(2, attempt);
      await new Promise(resolve => setTimeout(resolve, delay));
    }
  }
}

// Usage
const referral = await retryWithBackoff(() =>
  vantioClient.createReferral(referralData)
);

Batch Processing Pattern

Process multiple items efficiently:
// patterns/batch-processing.js
async function processBatch(items, processor, concurrency = 5) {
  const results = [];
  const errors = [];
  
  for (let i = 0; i < items.length; i += concurrency) {
    const batch = items.slice(i, i + concurrency);
    
    const batchResults = await Promise.allSettled(
      batch.map(item => processor(item))
    );
    
    batchResults.forEach((result, index) => {
      if (result.status === 'fulfilled') {
        results.push(result.value);
      } else {
        errors.push({
          item: batch[index],
          error: result.reason
        });
      }
    });
  }
  
  return { results, errors };
}

// Usage: Create multiple referrals
const signups = [
  { impressionId: 'imp_1', firstName: 'John', ... },
  { impressionId: 'imp_2', firstName: 'Jane', ... },
  // ... more signups
];

const { results, errors } = await processBatch(
  signups,
  async (signup) => {
    return await vantioClient.createReferral({
      impression_id: signup.impressionId,
      first_name: signup.firstName,
      last_name: signup.lastName,
      email: signup.email
    });
  },
  5 // Process 5 at a time
);

Webhook Validation Pattern

Validate and process webhooks securely:
// patterns/webhook-validation.js
const crypto = require('crypto');

function validateWebhook(payload, signature, secret) {
  const hmac = crypto.createHmac('sha256', secret);
  const digest = hmac.update(JSON.stringify(payload)).digest('hex');
  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(digest)
  );
}

// Express.js webhook handler
app.post('/webhooks/vantio', express.raw({ type: 'application/json' }), (req, res) => {
  const signature = req.headers['x-vantio-signature'];
  const secret = process.env.VANTIO_WEBHOOK_SECRET;
  
  if (!validateWebhook(req.body, signature, secret)) {
    return res.status(401).json({ error: 'Invalid signature' });
  }
  
  const event = JSON.parse(req.body);
  // Process event...
  
  res.json({ received: true });
});

Caching Pattern

Cache API responses to reduce calls:
// patterns/caching.js
class Cache {
  constructor(ttl = 300000) { // 5 minutes default
    this.cache = new Map();
    this.ttl = ttl;
  }
  
  get(key) {
    const item = this.cache.get(key);
    if (!item) return null;
    
    if (Date.now() > item.expires) {
      this.cache.delete(key);
      return null;
    }
    
    return item.value;
  }
  
  set(key, value) {
    this.cache.set(key, {
      value,
      expires: Date.now() + this.ttl
    });
  }
}

// Usage with API client
const cache = new Cache(300000); // 5 minutes

async function getCachedUser(userId) {
  const cacheKey = `user_${userId}`;
  let user = cache.get(cacheKey);
  
  if (!user) {
    user = await vantioClient.getUser(userId);
    cache.set(cacheKey, user);
  }
  
  return user;
}

Best Practices

  1. Always use idempotency keys for POST requests
  2. Implement retry logic for transient failures
  3. Cache frequently accessed data to reduce API calls
  4. Validate webhooks to ensure security
  5. Handle errors gracefully with proper error types
  6. Use batch processing for multiple items
  7. Monitor rate limits and implement backoff

Next Steps