E-commerce Protection
Protect checkout flows, inventory, and prevent scalping
E-commerce Protection
This guide covers bot protection strategies for e-commerce applications, including checkout flow protection, inventory defense, and scalping prevention. E-commerce sites face unique threats from automated traffic seeking to exploit limited inventory, scrape pricing, or abuse promotions.
Common Threats
Checkout Abuse
- Card testing - Bots validating stolen credit cards with small purchases
- Promo abuse - Automated redemption of discount codes
- Account takeover - Credential stuffing on login/checkout
Inventory Attacks
- Scalping - Bots purchasing limited items for resale
- Denial of inventory - Holding items in carts without purchasing
- Flash sale abuse - Automated purchasing during limited-time sales
Competitive Intelligence
- Price scraping - Competitors monitoring your pricing
- Catalog scraping - Copying product data and images
Integration Strategy
Layered Protection
Apply different thresholds at each stage of the purchase funnel:
Browse (low friction) → Add to Cart (monitor) → Checkout (verify) → Payment (strict)
BotSigged.init({
apiKey: 'your-api-key',
botScoreThresholds: {
medium: 40,
high: 60,
critical: 80,
},
});
Page-Specific Configuration
Configure protection based on page sensitivity:
// Product pages - monitor only
const productConfig = {
apiKey: 'your-api-key',
action: 'none',
onScoreUpdate: (score) => {
analytics.track('product_view_score', { score: score.bot_score });
},
};
// Checkout - active protection
const checkoutConfig = {
apiKey: 'your-api-key',
actionThreshold: 60,
action: 'challenge',
formProtection: {
mode: 'holdUntilFormScored',
maxHoldTime: 5000,
},
};
Checkout Flow Protection
Multi-Step Checkout
Track scores across checkout steps:
class CheckoutFlow {
constructor() {
this.stepScores = [];
this.botSigged = BotSigged.init({
apiKey: 'your-api-key',
onScoreUpdate: (score) => {
this.recordScore(score);
},
});
}
recordScore(score) {
this.stepScores.push({
step: this.currentStep,
score: score.bot_score,
timestamp: Date.now(),
});
}
async proceedToStep(nextStep) {
const avgScore = this.getAverageScore();
// Block if consistently suspicious
if (avgScore > 70 && this.stepScores.length >= 2) {
throw new Error('Unable to proceed. Please contact support.');
}
// Challenge on payment step
if (nextStep === 'payment' && avgScore > 50) {
await this.botSigged.triggerChallenge('high');
}
this.currentStep = nextStep;
}
getAverageScore() {
if (this.stepScores.length === 0) return 0;
const sum = this.stepScores.reduce((acc, s) => acc + s.score, 0);
return sum / this.stepScores.length;
}
}
Express Checkout Protection
For one-click or express checkout:
async function expressCheckout(savedPaymentMethod) {
const { score, timedOut } = await botSigged.waitUntilReady();
// Stricter threshold for express checkout
if (score && score > 50) {
// Fall back to regular checkout
return redirectToFullCheckout();
}
// Proceed with express checkout
return processPayment(savedPaymentMethod);
}
Guest Checkout
Guest checkout is higher risk - apply stricter rules:
const guestCheckoutConfig = {
apiKey: 'your-api-key',
actionThreshold: 50, // Lower threshold than authenticated
action: 'challenge',
formProtection: {
mode: 'holdUntilFormScored',
maxHoldTime: 5000,
},
};
Add to Cart Protection
Rate-Limited Cart Additions
Combine BotSigged with rate limiting:
class CartProtection {
constructor(botSigged) {
this.botSigged = botSigged;
this.addAttempts = [];
}
async addToCart(productId, quantity) {
// Check rate limit
this.cleanOldAttempts();
if (this.addAttempts.length > 10) {
throw new Error('Too many requests. Please slow down.');
}
// Check bot score
const score = this.botSigged.getLastScore();
if (score && score.bot_score > 60) {
// Require challenge for suspected bots
await this.botSigged.triggerChallenge('medium');
}
this.addAttempts.push(Date.now());
return fetch('/api/cart/add', {
method: 'POST',
headers: {
'X-BotSigged-Session': this.botSigged.getSessionId(),
},
body: JSON.stringify({ productId, quantity }),
});
}
cleanOldAttempts() {
const oneMinuteAgo = Date.now() - 60000;
this.addAttempts = this.addAttempts.filter((t) => t > oneMinuteAgo);
}
}
Limited Quantity Items
Extra protection for high-demand products:
async function addLimitedItem(productId) {
const { score, timedOut } = await botSigged.waitUntilReady();
// Strict threshold for limited items
if (score && score > 40) {
showMessage('Please complete verification to add this item.');
await botSigged.triggerChallenge('high');
// Re-check after challenge
const newScore = botSigged.getLastScore();
if (newScore && newScore.bot_score > 40) {
throw new Error('Unable to add item. Please try again later.');
}
}
return addToCart(productId);
}
Inventory Protection
Cart Abandonment Defense
Prevent bots from holding inventory:
// Server-side: Release held inventory after timeout
async function releaseAbandonedCarts() {
const abandonedCarts = await db.carts.findMany({
where: {
updatedAt: { lt: new Date(Date.now() - 15 * 60 * 1000) }, // 15 min
status: 'active',
},
});
for (const cart of abandonedCarts) {
// Check session bot score
const session = await botSiggedApi.getSession(cart.sessionId);
// Release immediately if high bot score
if (session.bot_score > 70) {
await releaseCartInventory(cart.id);
} else {
// Give humans more time
if (cart.updatedAt < Date.now() - 30 * 60 * 1000) {
await releaseCartInventory(cart.id);
}
}
}
}
Inventory Reservation Queue
For flash sales or limited drops:
class InventoryQueue {
constructor(productId, maxQuantity) {
this.productId = productId;
this.maxQuantity = maxQuantity;
this.queue = [];
}
async requestReservation(sessionId, quantity) {
// Get bot score from BotSigged API
const session = await fetch(`${BOTSIGGED_API}/sessions/${sessionId}`);
const { bot_score } = await session.json();
// Priority based on bot score (lower = higher priority)
const priority = bot_score;
this.queue.push({ sessionId, quantity, priority, timestamp: Date.now() });
this.queue.sort((a, b) => a.priority - b.priority);
return this.processQueue();
}
processQueue() {
let remaining = this.maxQuantity;
const reservations = [];
for (const request of this.queue) {
if (remaining <= 0) break;
const reserved = Math.min(request.quantity, remaining);
reservations.push({ sessionId: request.sessionId, quantity: reserved });
remaining -= reserved;
}
return reservations;
}
}
Payment Protection
Pre-Payment Verification
Verify before processing payment:
async function processPayment(paymentData) {
const { score } = await botSigged.waitUntilReady();
// Critical threshold for payments
if (score && score > 80) {
return {
success: false,
error: 'Unable to process payment. Please contact support.',
};
}
// Challenge for medium-high scores
if (score && score > 60) {
const challengeResult = await botSigged.triggerChallenge('critical');
if (!challengeResult.solved) {
return {
success: false,
error: 'Verification failed. Please try again.',
};
}
}
// Include session ID for server-side verification
return fetch('/api/payments', {
method: 'POST',
headers: {
'X-BotSigged-Session': botSigged.getSessionId(),
},
body: JSON.stringify(paymentData),
});
}
Server-Side Payment Validation
// Server-side payment handler
async function handlePayment(req, res) {
const sessionId = req.headers['x-botsigged-session'];
// Verify session with BotSigged API
const session = await fetch(`${BOTSIGGED_API}/sessions/${sessionId}`);
const { bot_score, classification, triggered_rules } = await session.json();
// Log for fraud analysis
await logPaymentAttempt({
sessionId,
botScore: bot_score,
classification,
triggeredRules: triggered_rules,
amount: req.body.amount,
timestamp: new Date(),
});
// Block high-risk transactions
if (bot_score > 85) {
return res.status(403).json({
error: 'Transaction blocked for security reasons',
});
}
// Flag for manual review
if (bot_score > 60) {
await flagForReview(sessionId, req.body);
}
// Process payment
return processPaymentWithProvider(req.body);
}
Promo Code Protection
Client-Side Validation
async function applyPromoCode(code) {
const { score } = await botSigged.waitUntilReady();
// Challenge suspected bots before applying codes
if (score && score > 50) {
await botSigged.triggerChallenge('medium');
}
return fetch('/api/promo/apply', {
method: 'POST',
headers: {
'X-BotSigged-Session': botSigged.getSessionId(),
},
body: JSON.stringify({ code }),
});
}
Server-Side Rate Limiting by Score
// Server-side promo handler
const promoAttempts = new Map(); // sessionId -> attempts
async function handlePromoCode(req, res) {
const sessionId = req.headers['x-botsigged-session'];
const session = await botSiggedApi.getSession(sessionId);
// Stricter rate limits for higher scores
const maxAttempts = session.bot_score > 50 ? 3 : 10;
const attempts = promoAttempts.get(sessionId) || 0;
if (attempts >= maxAttempts) {
return res.status(429).json({ error: 'Too many attempts' });
}
promoAttempts.set(sessionId, attempts + 1);
// Validate and apply code
return applyPromoCode(req.body.code);
}
Analytics & Monitoring
Track Bot Activity
BotSigged.init({
apiKey: 'your-api-key',
onScoreUpdate: (score) => {
// Track all sessions
analytics.track('botsigged_score', {
score: score.bot_score,
classification: score.classification,
rules: score.triggered_rules,
page: window.location.pathname,
});
},
onHighBotScore: (event) => {
// Alert on high scores
analytics.track('suspected_bot', {
score: event.score,
level: event.level,
sessionId: event.sessionId,
});
},
});
Conversion Funnel Analysis
Compare conversion rates by bot score:
// Track funnel events with score
function trackFunnelEvent(event, data = {}) {
const score = botSigged.getLastScore();
analytics.track(event, {
...data,
botScore: score?.bot_score,
classification: score?.classification,
sessionId: botSigged.getSessionId(),
});
}
// Usage
trackFunnelEvent('product_viewed', { productId: '123' });
trackFunnelEvent('added_to_cart', { productId: '123', quantity: 1 });
trackFunnelEvent('checkout_started');
trackFunnelEvent('payment_submitted');
trackFunnelEvent('order_completed', { orderId: 'abc' });
React E-commerce Example
Complete checkout component:
import { useState } from 'react';
import { useBotSigged } from '@/lib/botsigged';
export function Checkout({ cart }) {
const { sdk, score, isReady } = useBotSigged();
const [step, setStep] = useState('shipping');
const [error, setError] = useState(null);
const isBlocked = score && score.bot_score > 80;
const needsChallenge = score && score.bot_score > 60;
async function handleSubmitPayment(paymentData) {
setError(null);
if (isBlocked) {
setError('Unable to process order. Please contact support.');
return;
}
if (needsChallenge) {
const result = await sdk.triggerChallenge('high');
if (!result.solved) {
setError('Verification required. Please try again.');
return;
}
}
try {
const response = await fetch('/api/orders', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-BotSigged-Session': sdk.getSessionId(),
},
body: JSON.stringify({
cart,
payment: paymentData,
}),
});
if (!response.ok) throw new Error('Order failed');
const order = await response.json();
window.location.href = `/orders/${order.id}/confirmation`;
} catch (err) {
setError('Unable to complete order. Please try again.');
}
}
return (
<div className="checkout">
{error && <div className="error">{error}</div>}
{!isReady && <div className="loading">Verifying session...</div>}
{step === 'shipping' && (
<ShippingForm onComplete={() => setStep('payment')} />
)}
{step === 'payment' && (
<PaymentForm
onSubmit={handleSubmitPayment}
disabled={isBlocked || !isReady}
/>
)}
</div>
);
}
Best Practices
Don’t Over-Block
False positives hurt sales. Start with monitoring and adjust:
- Week 1: Monitor only, collect baseline data
- Week 2: Enable challenges for critical scores (80+)
- Week 3: Lower threshold based on data
- Ongoing: Review blocked sessions weekly
Provide Clear Feedback
Don’t leave users confused:
// Bad
throw new Error('Error');
// Good
throw new Error('Unable to complete your request. If you believe this is an error, please contact [email protected]');
Have a Manual Override
For customer support to unblock legitimate users:
// Check for support override token
const overrideToken = req.headers['x-support-override'];
if (overrideToken && validateSupportToken(overrideToken)) {
// Skip bot check
return processOrder(req.body);
}
Test Your Thresholds
Use the zoo tools to verify your protection:
cd zoo
npm run test-headless # Should be blocked/challenged
npm run test-stealth # Test against stealth bots