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:Copy
// 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:Copy
// 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:Copy
// 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:Copy
// 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:Copy
// 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:Copy
// 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:Copy
// 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
- Always use idempotency keys for POST requests
- Implement retry logic for transient failures
- Cache frequently accessed data to reduce API calls
- Validate webhooks to ensure security
- Handle errors gracefully with proper error types
- Use batch processing for multiple items
- Monitor rate limits and implement backoff
Next Steps
- Review Code Examples for more integration code
- Check API Reference for endpoint details
- Explore Configuration Guide for setup options