Building Marketplace Integration with Trading Card API
Learn how to integrate trading card marketplaces and commerce platforms with Trading Card API. This guide covers everything from product catalog management to secure transaction processing.
π― What You'll Buildβ
By the end of this guide, you'll have a complete marketplace integration that can:
- Sync product catalogs with accurate card data and metadata
- Manage inventory in real-time across multiple sales channels
- Process transactions securely with proper fraud protection
- Handle pricing dynamically based on market conditions
- Authenticate cards using comprehensive API data
- Scale operations to handle high-volume trading
π Prerequisitesβ
- Experience with e-commerce platforms and payment processing
- Understanding of REST APIs and webhook systems
- A Trading Card API account with marketplace-tier access
- Payment processor account (Stripe, PayPal, etc.)
- Database system for transaction and inventory management
π Marketplace Architecture Overviewβ
A robust trading card marketplace requires several integrated systems:
βββββββββββββββββββ ββββββββββββββββββββ βββββββββββββββββββ
β Trading Card β β Marketplace β β Payment & β
β API β β Platform β β Fulfillment β
β β β β β β
β β’ Card Data ββββββ β’ Product CatalogβββββΊβ β’ Payment Proc. β
β β’ Authenticationβ β β’ Inventory Mgmt β β β’ Shipping β
β β’ Market Data β β β’ Order System β β β’ Tax Calc. β
βββββββββββββββββββ ββββββββββββββββββββ βββββββββββββββββββ
β
βΌ
ββββββββββββββββββββ
β User Accounts β
β & Reviews β
ββββββββββββββββββββ
Step 1: Product Catalog Integrationβ
Automated Listing Creationβ
The foundation of any marketplace is accurate product listings. Trading Card API provides the rich metadata needed for compelling product pages.
class MarketplaceCatalogManager {
constructor(apiKey, marketplace) {
this.cardApi = new TradingCardAPI(apiKey);
this.marketplace = marketplace;
this.listingCache = new Map();
}
async createProductListing(cardId, sellerData) {
try {
// Get comprehensive card data from Trading Card API
const cardData = await this.cardApi.getCardWithDetails(cardId);
// Build marketplace listing
const listing = {
title: this.generateListingTitle(cardData),
description: this.generateListingDescription(cardData),
category: this.mapToMarketplaceCategory(cardData),
condition: sellerData.condition,
price: sellerData.price,
quantity: sellerData.quantity,
images: this.prepareListingImages(cardData, sellerData),
specifications: this.buildSpecifications(cardData),
shipping: sellerData.shipping,
returns: sellerData.returns || this.getDefaultReturnPolicy(),
metadata: {
tradingCardApiId: cardId,
authenticity: 'verified-via-api',
lastUpdated: new Date()
}
};
// Create listing on marketplace platform
const marketplaceListing = await this.marketplace.createListing(listing);
// Store mapping for future updates
await this.storeListing(cardId, marketplaceListing.id, sellerData.sellerId);
return {
success: true,
listingId: marketplaceListing.id,
listingUrl: marketplaceListing.url,
cardData: cardData
};
} catch (error) {
return this.handleListingError(error, cardId, sellerData);
}
}
generateListingTitle(cardData) {
const parts = [];
// Add year if available
if (cardData.year) parts.push(cardData.year);
// Add brand
if (cardData.brand) parts.push(cardData.brand);
// Add player/card name
parts.push(cardData.name);
// Add card number if available
if (cardData.number) parts.push(`#${cardData.number}`);
// Add set name
if (cardData.setName) parts.push(`- ${cardData.setName}`);
return parts.join(' ');
}
generateListingDescription(cardData) {
let description = `**${cardData.name}**\n\n`;
// Card details
if (cardData.setName) {
description += `**Set:** ${cardData.setName}\n`;
}
if (cardData.year) {
description += `**Year:** ${cardData.year}\n`;
}
if (cardData.brand) {
description += `**Brand:** ${cardData.brand}\n`;
}
if (cardData.number) {
description += `**Card Number:** ${cardData.number}\n`;
}
// Player information
if (cardData.playerInfo) {
description += `\n**Player Information:**\n`;
description += `- **Name:** ${cardData.playerInfo.name}\n`;
if (cardData.playerInfo.position) {
description += `- **Position:** ${cardData.playerInfo.position}\n`;
}
if (cardData.teamInfo) {
description += `- **Team:** ${cardData.teamInfo.name}\n`;
}
}
// Add authenticity guarantee
description += `\n**β
Authenticity Guaranteed**\n`;
description += `Card data verified through Trading Card API's comprehensive database.\n`;
// Add condition note
description += `\n**Condition Notes:**\n`;
description += `Please see item specifics for detailed condition information.\n`;
return description;
}
buildSpecifications(cardData) {
const specs = {};
if (cardData.year) specs['Year'] = cardData.year;
if (cardData.brand) specs['Brand/Manufacturer'] = cardData.brand;
if (cardData.setName) specs['Set'] = cardData.setName;
if (cardData.number) specs['Card Number'] = cardData.number;
if (cardData.playerInfo?.name) specs['Player'] = cardData.playerInfo.name;
if (cardData.teamInfo?.name) specs['Team'] = cardData.teamInfo.name;
// Add API verification
specs['Data Source'] = 'Trading Card API Verified';
specs['Authenticity'] = 'API Authenticated';
return specs;
}
mapToMarketplaceCategory(cardData) {
// Map Trading Card API data to marketplace categories
const sport = cardData.sport || 'trading-cards';
const year = cardData.year;
const categoryMapping = {
'baseball': 'Sports Mem, Cards & Fan Shop > Sports Trading Cards > Baseball Cards',
'basketball': 'Sports Mem, Cards & Fan Shop > Sports Trading Cards > Basketball Cards',
'football': 'Sports Mem, Cards & Fan Shop > Sports Trading Cards > Football Cards',
'hockey': 'Sports Mem, Cards & Fan Shop > Sports Trading Cards > Hockey Cards',
'soccer': 'Sports Mem, Cards & Fan Shop > Sports Trading Cards > Soccer Cards'
};
return categoryMapping[sport] || 'Collectibles > Trading Cards';
}
}
Bulk Listing Managementβ
import asyncio
from typing import List, Dict
from dataclasses import dataclass
from datetime import datetime
@dataclass
class BulkListingResult:
total_processed: int
successful_listings: int
failed_listings: int
errors: List[Dict]
estimated_revenue: float
class BulkMarketplaceManager:
def __init__(self, catalog_manager, rate_limiter):
self.catalog_manager = catalog_manager
self.rate_limiter = rate_limiter
self.batch_size = 10 # Process 10 listings at a time
async def bulk_create_listings(self, seller_id: str, card_inventory: List[Dict]) -> BulkListingResult:
"""Create multiple marketplace listings efficiently"""
result = BulkListingResult(
total_processed=len(card_inventory),
successful_listings=0,
failed_listings=0,
errors=[],
estimated_revenue=0.0
)
# Process in batches to avoid overwhelming APIs
for i in range(0, len(card_inventory), self.batch_size):
batch = card_inventory[i:i + self.batch_size]
batch_results = await self.process_listing_batch(seller_id, batch)
# Aggregate results
result.successful_listings += batch_results['successful']
result.failed_listings += batch_results['failed']
result.errors.extend(batch_results['errors'])
result.estimated_revenue += batch_results['revenue']
# Rate limiting between batches
await self.rate_limiter.wait_if_needed()
return result
async def process_listing_batch(self, seller_id: str, card_batch: List[Dict]) -> Dict:
"""Process a batch of card listings concurrently"""
tasks = []
for card_item in card_batch:
task = self.create_single_listing(seller_id, card_item)
tasks.append(task)
# Execute all listings in batch concurrently
batch_results = await asyncio.gather(*tasks, return_exceptions=True)
successful = 0
failed = 0
errors = []
revenue = 0.0
for i, result in enumerate(batch_results):
if isinstance(result, Exception):
failed += 1
errors.append({
'card_index': i,
'card_id': card_batch[i].get('card_id'),
'error': str(result)
})
elif result.get('success'):
successful += 1
revenue += result.get('listing_price', 0.0)
else:
failed += 1
errors.append({
'card_index': i,
'card_id': card_batch[i].get('card_id'),
'error': result.get('error', 'Unknown error')
})
return {
'successful': successful,
'failed': failed,
'errors': errors,
'revenue': revenue
}
async def create_single_listing(self, seller_id: str, card_item: Dict) -> Dict:
"""Create a single marketplace listing with error handling"""
try:
listing_result = await self.catalog_manager.create_product_listing(
card_item['card_id'],
{
'sellerId': seller_id,
'condition': card_item['condition'],
'price': card_item['price'],
'quantity': card_item['quantity'],
'shipping': card_item.get('shipping'),
'returns': card_item.get('returns')
}
)
if listing_result['success']:
return {
'success': True,
'listing_id': listing_result['listingId'],
'listing_price': card_item['price']
}
else:
return {
'success': False,
'error': listing_result.get('error', 'Failed to create listing')
}
except Exception as e:
return {
'success': False,
'error': f"Exception during listing creation: {str(e)}"
}
async def update_inventory_from_sales(self, seller_id: str) -> Dict:
"""Update marketplace listings based on sales and inventory changes"""
try:
# Get all active listings for seller
active_listings = await self.get_seller_active_listings(seller_id)
updates_made = 0
deactivated_listings = 0
for listing in active_listings:
# Check current inventory
current_inventory = await self.get_card_inventory(
seller_id,
listing['card_id'],
listing['condition']
)
if current_inventory == 0:
# Deactivate sold out listings
await self.marketplace.deactivate_listing(listing['marketplace_listing_id'])
deactivated_listings += 1
elif current_inventory != listing['quantity']:
# Update quantity
await self.marketplace.update_listing_quantity(
listing['marketplace_listing_id'],
current_inventory
)
updates_made += 1
return {
'success': True,
'updates_made': updates_made,
'deactivated_listings': deactivated_listings,
'processed_listings': len(active_listings)
}
except Exception as e:
return {
'success': False,
'error': str(e)
}
Step 2: Real-time Inventory Managementβ
Inventory Synchronization Systemβ
class InventoryManager {
constructor(database, marketplaceAPIs, cardAPI) {
this.database = database;
this.marketplaces = marketplaceAPIs; // Array of different marketplace APIs
this.cardAPI = cardAPI;
this.inventoryWebhooks = new Map();
}
async syncInventoryAcrossMarketplaces(sellerId) {
const sellerInventory = await this.database.getSellerInventory(sellerId);
const activeListings = await this.database.getActiveListings(sellerId);
const syncResults = [];
for (const inventoryItem of sellerInventory) {
// Find all marketplace listings for this card/condition combination
const relatedListings = activeListings.filter(listing =>
listing.cardId === inventoryItem.cardId &&
listing.condition === inventoryItem.condition
);
for (const listing of relatedListings) {
try {
const marketplace = this.marketplaces.find(m => m.platform === listing.platform);
if (inventoryItem.availableQuantity === 0) {
// Remove sold-out items
await marketplace.deactivateListing(listing.marketplaceListingId);
await this.database.markListingInactive(listing.id);
syncResults.push({
action: 'deactivated',
listingId: listing.id,
reason: 'sold_out'
});
} else if (inventoryItem.availableQuantity !== listing.listedQuantity) {
// Update quantity
await marketplace.updateListingQuantity(
listing.marketplaceListingId,
inventoryItem.availableQuantity
);
await this.database.updateListingQuantity(
listing.id,
inventoryItem.availableQuantity
);
syncResults.push({
action: 'updated_quantity',
listingId: listing.id,
oldQuantity: listing.listedQuantity,
newQuantity: inventoryItem.availableQuantity
});
}
} catch (error) {
syncResults.push({
action: 'error',
listingId: listing.id,
error: error.message
});
}
}
}
return {
syncedAt: new Date(),
sellerId,
results: syncResults,
totalListingsProcessed: activeListings.length
};
}
async handleSale(saleData) {
// Process a completed sale and update inventory
const { listingId, quantitySold, buyerId, salePrice, transactionId } = saleData;
try {
// Get listing details
const listing = await this.database.getListing(listingId);
// Update inventory
await this.database.updateInventoryQuantity(
listing.sellerId,
listing.cardId,
listing.condition,
-quantitySold // Decrease by amount sold
);
// Record transaction
await this.database.recordTransaction({
transactionId,
listingId,
sellerId: listing.sellerId,
buyerId,
cardId: listing.cardId,
condition: listing.condition,
quantity: quantitySold,
unitPrice: salePrice,
totalPrice: salePrice * quantitySold,
timestamp: new Date(),
status: 'completed'
});
// Update listing quantity or deactivate if sold out
const remainingQuantity = listing.listedQuantity - quantitySold;
if (remainingQuantity === 0) {
await this.marketplace.deactivateListing(listing.marketplaceListingId);
await this.database.markListingInactive(listingId);
} else {
await this.marketplace.updateListingQuantity(
listing.marketplaceListingId,
remainingQuantity
);
await this.database.updateListingQuantity(listingId, remainingQuantity);
}
// Sync inventory across all marketplaces for this seller
await this.syncInventoryAcrossMarketplaces(listing.sellerId);
return {
success: true,
remainingQuantity,
transactionRecorded: true
};
} catch (error) {
console.error('Error handling sale:', error);
return {
success: false,
error: error.message
};
}
}
async setupWebhookHandlers() {
// Set up webhooks for real-time inventory updates
this.inventoryWebhooks.set('sale_completed', async (data) => {
await this.handleSale(data);
});
this.inventoryWebhooks.set('listing_updated', async (data) => {
await this.syncListingUpdate(data);
});
this.inventoryWebhooks.set('return_processed', async (data) => {
await this.handleReturn(data);
});
}
async handleReturn(returnData) {
const { transactionId, quantityReturned, returnReason } = returnData;
try {
// Get original transaction
const transaction = await this.database.getTransaction(transactionId);
// Return inventory to seller
await this.database.updateInventoryQuantity(
transaction.sellerId,
transaction.cardId,
transaction.condition,
quantityReturned // Add back returned quantity
);
// Record return transaction
await this.database.recordReturn({
originalTransactionId: transactionId,
quantityReturned,
returnReason,
processedAt: new Date(),
status: 'completed'
});
// Reactivate or update marketplace listings
await this.reactivateListingAfterReturn(transaction, quantityReturned);
return { success: true };
} catch (error) {
console.error('Error handling return:', error);
return { success: false, error: error.message };
}
}
}
Step 3: Dynamic Pricing Engineβ
Market-Based Pricing Systemβ
import statistics
from datetime import datetime, timedelta
from typing import Dict, List, Optional
class DynamicPricingEngine:
def __init__(self, price_data_sources, trading_card_api):
self.price_sources = price_data_sources
self.card_api = trading_card_api
self.pricing_rules = {}
def calculate_optimal_price(self, card_id: str, condition: str, seller_preferences: Dict) -> Dict:
"""Calculate optimal listing price based on market data and seller preferences"""
try:
# Get card information for context
card_data = self.card_api.get_card_details(card_id)
# Gather market data from multiple sources
market_data = self.gather_market_data(card_data, condition)
if not market_data['has_sufficient_data']:
return self.fallback_pricing(card_data, condition, seller_preferences)
# Calculate base price from market data
base_price = self.calculate_base_price(market_data)
# Apply seller preferences and adjustments
final_price = self.apply_pricing_strategy(
base_price,
market_data,
seller_preferences
)
return {
'recommended_price': round(final_price, 2),
'price_range': {
'min': round(final_price * 0.85, 2),
'max': round(final_price * 1.15, 2)
},
'market_data': market_data,
'confidence_level': self.calculate_confidence(market_data),
'pricing_factors': self.explain_pricing_factors(market_data, seller_preferences),
'last_updated': datetime.now().isoformat()
}
except Exception as e:
return {
'error': f"Pricing calculation failed: {str(e)}",
'fallback_price': self.get_fallback_price(card_id, condition)
}
def gather_market_data(self, card_data: Dict, condition: str) -> Dict:
"""Collect pricing data from multiple sources"""
market_data = {
'recent_sales': [],
'current_listings': [],
'historical_average': None,
'market_trend': 'stable',
'has_sufficient_data': False
}
# Build search query for external price sources
search_query = self.build_price_search_query(card_data, condition)
# Gather data from each source
for source in self.price_sources:
try:
source_data = source.get_price_data(search_query, condition)
if source_data['recent_sales']:
market_data['recent_sales'].extend(source_data['recent_sales'])
if source_data['current_listings']:
market_data['current_listings'].extend(source_data['current_listings'])
except Exception as e:
print(f"Price source {source.name} failed: {e}")
continue
# Process and analyze collected data
if len(market_data['recent_sales']) >= 3:
market_data['has_sufficient_data'] = True
market_data['historical_average'] = self.calculate_sales_average(market_data['recent_sales'])
market_data['market_trend'] = self.analyze_price_trend(market_data['recent_sales'])
return market_data
def calculate_base_price(self, market_data: Dict) -> float:
"""Calculate base price from market data"""
recent_sales = market_data['recent_sales']
current_listings = market_data['current_listings']
if not recent_sales:
# Use current listings if no sales data
if current_listings:
listing_prices = [listing['price'] for listing in current_listings]
return statistics.median(listing_prices)
return 0.0
# Weight recent sales more heavily
sales_prices = []
now = datetime.now()
for sale in recent_sales:
sale_date = datetime.fromisoformat(sale['date'])
days_ago = (now - sale_date).days
# Weight newer sales more heavily
weight = max(1.0 - (days_ago / 30), 0.1) # Decay over 30 days
for _ in range(int(weight * 10)): # Apply weight by repetition
sales_prices.append(sale['price'])
return statistics.median(sales_prices)
def apply_pricing_strategy(self, base_price: float, market_data: Dict, preferences: Dict) -> float:
"""Apply seller preferences and market conditions to base price"""
adjusted_price = base_price
# Apply seller strategy
strategy = preferences.get('pricing_strategy', 'market')
if strategy == 'aggressive':
# Price below market for quick sale
adjusted_price *= 0.90
elif strategy == 'premium':
# Price above market for higher margins
adjusted_price *= 1.10
elif strategy == 'competitive':
# Match lowest current listing
current_listings = market_data['current_listings']
if current_listings:
lowest_listing = min(listing['price'] for listing in current_listings)
adjusted_price = min(adjusted_price, lowest_listing * 0.99)
# Apply market trend adjustments
trend = market_data['market_trend']
if trend == 'rising':
adjusted_price *= 1.05 # 5% premium for rising market
elif trend == 'falling':
adjusted_price *= 0.95 # 5% discount for falling market
# Apply minimum/maximum price constraints
min_price = preferences.get('minimum_price', 0.99)
max_price = preferences.get('maximum_price', float('inf'))
adjusted_price = max(min_price, min(max_price, adjusted_price))
return adjusted_price
def analyze_price_trend(self, recent_sales: List[Dict]) -> str:
"""Analyze price trend from recent sales data"""
if len(recent_sales) < 5:
return 'insufficient_data'
# Sort by date
sorted_sales = sorted(recent_sales, key=lambda x: x['date'])
prices = [sale['price'] for sale in sorted_sales]
# Simple linear trend analysis
x = list(range(len(prices)))
slope = self.calculate_slope(x, prices)
if slope > 0.5:
return 'rising'
elif slope < -0.5:
return 'falling'
else:
return 'stable'
def calculate_slope(self, x: List[int], y: List[float]) -> float:
"""Calculate slope of price trend"""
n = len(x)
if n < 2:
return 0
sum_x = sum(x)
sum_y = sum(y)
sum_xy = sum(x[i] * y[i] for i in range(n))
sum_x2 = sum(x[i] ** 2 for i in range(n))
slope = (n * sum_xy - sum_x * sum_y) / (n * sum_x2 - sum_x ** 2)
return slope
Step 4: Order Processing and Fulfillmentβ
Complete Order Management Systemβ
class OrderFulfillmentSystem {
constructor(database, shippingProviders, paymentProcessor) {
this.database = database;
this.shipping = shippingProviders;
this.payment = paymentProcessor;
this.orderStatuses = ['pending', 'paid', 'processing', 'shipped', 'delivered', 'completed'];
}
async processNewOrder(orderData) {
try {
// Validate order and check inventory
const validation = await this.validateOrder(orderData);
if (!validation.valid) {
return { success: false, error: validation.error };
}
// Create order record
const order = await this.createOrderRecord(orderData);
// Reserve inventory
await this.reserveInventory(order.items);
// Process payment
const paymentResult = await this.processPayment(order);
if (!paymentResult.success) {
await this.releaseInventory(order.items);
return { success: false, error: 'Payment processing failed' };
}
// Update order status
await this.updateOrderStatus(order.id, 'paid');
// Generate shipping label and tracking
const shippingResult = await this.createShipment(order);
// Send confirmation email
await this.sendOrderConfirmation(order, shippingResult);
return {
success: true,
orderId: order.id,
trackingNumber: shippingResult.trackingNumber,
estimatedDelivery: shippingResult.estimatedDelivery
};
} catch (error) {
console.error('Order processing error:', error);
return { success: false, error: error.message };
}
}
async validateOrder(orderData) {
const { items, shippingAddress, paymentMethod } = orderData;
// Validate each item
for (const item of items) {
// Verify card exists in Trading Card API
const cardData = await this.cardAPI.getCard(item.cardId);
if (!cardData) {
return { valid: false, error: `Card ${item.cardId} not found` };
}
// Check inventory availability
const inventory = await this.database.getInventoryItem(
item.sellerId,
item.cardId,
item.condition
);
if (!inventory || inventory.availableQuantity < item.quantity) {
return {
valid: false,
error: `Insufficient inventory for ${cardData.name}`
};
}
// Validate condition matches
if (item.condition !== inventory.condition) {
return {
valid: false,
error: `Condition mismatch for ${cardData.name}`
};
}
}
// Validate shipping address
const addressValidation = await this.validateShippingAddress(shippingAddress);
if (!addressValidation.valid) {
return { valid: false, error: 'Invalid shipping address' };
}
// Validate payment method
const paymentValidation = await this.payment.validatePaymentMethod(paymentMethod);
if (!paymentValidation.valid) {
return { valid: false, error: 'Invalid payment method' };
}
return { valid: true };
}
async createShipment(order) {
// Calculate shipping requirements
const shippingReqs = await this.calculateShippingRequirements(order);
// Select best shipping provider based on cost and speed
const bestProvider = await this.selectShippingProvider(shippingReqs);
// Create shipping label
const shipment = await bestProvider.createShipment({
fromAddress: order.sellerAddress,
toAddress: order.shippingAddress,
package: shippingReqs.packageDimensions,
service: shippingReqs.serviceLevel,
insurance: shippingReqs.insuranceValue,
signature: shippingReqs.requiresSignature
});
// Update order with tracking information
await this.database.updateOrderShipping(order.id, {
shippingProvider: bestProvider.name,
trackingNumber: shipment.trackingNumber,
shippingCost: shipment.cost,
estimatedDelivery: shipment.estimatedDelivery,
insuranceValue: shippingReqs.insuranceValue
});
return shipment;
}
async calculateShippingRequirements(order) {
let totalValue = 0;
let totalWeight = 0;
let requiresSignature = false;
let packageDimensions = { length: 6, width: 4, height: 1 }; // Standard card package
for (const item of order.items) {
const itemValue = item.unitPrice * item.quantity;
totalValue += itemValue;
// Estimate weight (standard card ~1g, graded card ~30g)
const isGraded = item.grade !== null;
const itemWeight = isGraded ? 30 * item.quantity : 1 * item.quantity;
totalWeight += itemWeight;
// High-value items require signature
if (itemValue > 250) {
requiresSignature = true;
}
// Adjust package size for graded cards
if (isGraded) {
packageDimensions.height = Math.max(packageDimensions.height, 2);
}
}
return {
totalValue,
totalWeight,
packageDimensions,
serviceLevel: totalValue > 100 ? 'priority' : 'standard',
insuranceValue: Math.min(totalValue, 5000), // Max insurance
requiresSignature,
requiresAdultSignature: totalValue > 1000
};
}
}
Step 5: Authentication and Fraud Preventionβ
Card Authentication Systemβ
class CardAuthenticationService {
constructor(tradingCardAPI, database) {
this.cardAPI = tradingCardAPI;
this.database = database;
this.fraudDetection = new FraudDetectionEngine();
}
async authenticateCard(cardId, sellerProvidedData) {
try {
// Get authoritative card data from Trading Card API
const apiCardData = await this.cardAPI.getCardWithDetails(cardId);
// Compare seller data with API data
const authenticationResult = this.compareCardData(apiCardData, sellerProvidedData);
// Check for red flags
const fraudCheck = await this.fraudDetection.checkListing(cardId, sellerProvidedData);
// Generate authentication score
const authScore = this.calculateAuthenticationScore(authenticationResult, fraudCheck);
return {
cardId,
authenticated: authScore >= 85,
authenticationScore: authScore,
apiDataMatch: authenticationResult,
fraudFlags: fraudCheck.flags,
recommendations: this.generateAuthRecommendations(authScore, fraudCheck),
verifiedAt: new Date()
};
} catch (error) {
return {
cardId,
authenticated: false,
error: error.message,
recommendations: ['Manual review required due to authentication error']
};
}
}
compareCardData(apiData, sellerData) {
const comparison = {
name: this.fuzzyMatch(apiData.name, sellerData.name),
year: apiData.year === sellerData.year,
brand: this.fuzzyMatch(apiData.brand, sellerData.brand),
setName: this.fuzzyMatch(apiData.setName, sellerData.setName),
cardNumber: apiData.number === sellerData.cardNumber,
player: apiData.playerInfo ? this.fuzzyMatch(apiData.playerInfo.name, sellerData.playerName) : true
};
const matchPercentage = Object.values(comparison).reduce((sum, match) => sum + (match ? 1 : 0), 0) / Object.keys(comparison).length * 100;
return {
individual: comparison,
overallMatch: matchPercentage,
discrepancies: Object.entries(comparison)
.filter(([field, matches]) => !matches)
.map(([field]) => field)
};
}
fuzzyMatch(str1, str2, threshold = 0.8) {
if (!str1 || !str2) return false;
// Simple fuzzy matching algorithm
const similarity = this.calculateSimilarity(str1.toLowerCase(), str2.toLowerCase());
return similarity >= threshold;
}
calculateSimilarity(str1, str2) {
const matrix = [];
for (let i = 0; i <= str2.length; i++) {
matrix[i] = [i];
}
for (let j = 0; j <= str1.length; j++) {
matrix[0][j] = j;
}
for (let i = 1; i <= str2.length; i++) {
for (let j = 1; j <= str1.length; j++) {
if (str2.charAt(i - 1) === str1.charAt(j - 1)) {
matrix[i][j] = matrix[i - 1][j - 1];
} else {
matrix[i][j] = Math.min(
matrix[i - 1][j - 1] + 1,
matrix[i][j - 1] + 1,
matrix[i - 1][j] + 1
);
}
}
}
const maxLength = Math.max(str1.length, str2.length);
return (maxLength - matrix[str2.length][str1.length]) / maxLength;
}
calculateAuthenticationScore(authResult, fraudCheck) {
let score = authResult.overallMatch;
// Penalize for fraud flags
score -= fraudCheck.flags.length * 10;
// Bonus for high-confidence matches
if (authResult.overallMatch > 95) {
score += 10;
}
return Math.max(0, Math.min(100, score));
}
generateAuthRecommendations(authScore, fraudCheck) {
const recommendations = [];
if (authScore < 85) {
recommendations.push('Manual verification recommended before listing');
}
if (fraudCheck.flags.length > 0) {
recommendations.push('Review fraud detection flags before approving');
}
if (authScore >= 95) {
recommendations.push('High confidence - suitable for automatic approval');
}
return recommendations;
}
}
class FraudDetectionEngine {
async checkListing(cardId, sellerData) {
const flags = [];
// Check for common fraud patterns
// 1. Unrealistic pricing
const marketPrice = await this.getMarketPrice(cardId, sellerData.condition);
if (marketPrice && sellerData.price < marketPrice * 0.3) {
flags.push({
type: 'suspicious_pricing',
severity: 'high',
description: 'Listed price significantly below market value'
});
}
// 2. New seller with high-value items
const sellerHistory = await this.getSellerHistory(sellerData.sellerId);
if (sellerHistory.totalSales < 5 && sellerData.price > 500) {
flags.push({
type: 'new_seller_high_value',
severity: 'medium',
description: 'New seller listing high-value item'
});
}
// 3. Duplicate listings check
const similarListings = await this.findSimilarActiveListings(cardId, sellerData);
if (similarListings.length > 0) {
flags.push({
type: 'potential_duplicate',
severity: 'low',
description: 'Similar listing found from same seller'
});
}
return {
flags,
riskLevel: this.calculateRiskLevel(flags),
requiresManualReview: flags.some(flag => flag.severity === 'high')
};
}
calculateRiskLevel(flags) {
const severityWeights = { low: 1, medium: 3, high: 5 };
const totalRisk = flags.reduce((sum, flag) => sum + severityWeights[flag.severity], 0);
if (totalRisk >= 8) return 'high';
if (totalRisk >= 4) return 'medium';
return 'low';
}
}
Step 6: Multi-Platform Integrationβ
Marketplace Platform Adaptersβ
from abc import ABC, abstractmethod
from typing import Dict, List, Optional
import requests
class MarketplaceAdapter(ABC):
"""Abstract base class for marketplace integrations"""
@abstractmethod
async def create_listing(self, listing_data: Dict) -> Dict:
pass
@abstractmethod
async def update_listing(self, listing_id: str, updates: Dict) -> Dict:
pass
@abstractmethod
async def deactivate_listing(self, listing_id: str) -> Dict:
pass
@abstractmethod
async def get_listing_performance(self, listing_id: str) -> Dict:
pass
class EbayMarketplaceAdapter(MarketplaceAdapter):
def __init__(self, app_id: str, cert_id: str, dev_id: str, user_token: str):
self.app_id = app_id
self.cert_id = cert_id
self.dev_id = dev_id
self.user_token = user_token
self.base_url = 'https://api.ebay.com'
async def create_listing(self, listing_data: Dict) -> Dict:
"""Create eBay listing using Trading Card API data"""
try:
# Map to eBay listing format
ebay_listing = {
'Item': {
'Title': listing_data['title'][:80], # eBay title limit
'Description': self.format_ebay_description(listing_data),
'PrimaryCategory': {'CategoryID': self.get_ebay_category_id(listing_data)},
'StartPrice': listing_data['price'],
'CategoryMappingAllowed': True,
'Country': 'US',
'Currency': 'USD',
'DispatchTimeMax': 3,
'ListingDuration': 'Days_7',
'ListingType': 'FixedPriceItem',
'PaymentMethods': ['PayPal', 'CreditCard'],
'PictureDetails': {
'PictureURL': listing_data['images']
},
'PostalCode': listing_data['seller_postal_code'],
'Quantity': listing_data['quantity'],
'ReturnPolicy': {
'ReturnsAcceptedOption': 'ReturnsAccepted',
'RefundOption': 'MoneyBack',
'ReturnsWithinOption': 'Days_30'
},
'ShippingDetails': self.build_shipping_details(listing_data),
'ItemSpecifics': {
'NameValueList': self.build_item_specifics(listing_data)
}
}
}
# Call eBay API
response = await self.make_ebay_api_call('AddFixedPriceItem', ebay_listing)
return {
'success': True,
'listing_id': response['ItemID'],
'listing_url': f"https://www.ebay.com/itm/{response['ItemID']}",
'fees': response.get('Fees', {})
}
except Exception as e:
return {
'success': False,
'error': str(e)
}
def format_ebay_description(self, listing_data: Dict) -> str:
"""Format description for eBay with HTML styling"""
html_description = f"""
<div style="font-family: Arial, sans-serif; max-width: 800px;">
<h2>{listing_data['title']}</h2>
<div style="background-color: #f8f9fa; padding: 15px; margin: 10px 0; border-radius: 5px;">
<h3>β
Verified by Trading Card API</h3>
<p>This card's authenticity and details have been verified through Trading Card API's comprehensive database.</p>
</div>
<h3>Card Details</h3>
<table style="width: 100%; border-collapse: collapse;">
"""
# Add specifications table
for key, value in listing_data['specifications'].items():
html_description += f"""
<tr style="border-bottom: 1px solid #ddd;">
<td style="padding: 8px; font-weight: bold;">{key}</td>
<td style="padding: 8px;">{value}</td>
</tr>
"""
html_description += """
</table>
<h3>Condition</h3>
<p>Item condition is accurately described. Please review all photos carefully.</p>
<h3>Shipping & Returns</h3>
<p>Fast, secure shipping with tracking. 30-day returns accepted.</p>
</div>
"""
return html_description
def build_item_specifics(self, listing_data: Dict) -> List[Dict]:
"""Build eBay item specifics from Trading Card API data"""
specifics = []
specs_mapping = {
'Year': listing_data['specifications'].get('Year'),
'Brand': listing_data['specifications'].get('Brand/Manufacturer'),
'Set': listing_data['specifications'].get('Set'),
'Card Number': listing_data['specifications'].get('Card Number'),
'Player': listing_data['specifications'].get('Player'),
'Team': listing_data['specifications'].get('Team'),
'Condition': listing_data['condition'],
'Graded': 'Yes' if listing_data.get('grade') else 'No'
}
for name, value in specs_mapping.items():
if value:
specifics.append({
'Name': name,
'Value': str(value)
})
return specifics
class TCGPlayerMarketplaceAdapter(MarketplaceAdapter):
def __init__(self, api_key: str, app_id: str):
self.api_key = api_key
self.app_id = app_id
self.base_url = 'https://api.tcgplayer.com'
async def create_listing(self, listing_data: Dict) -> Dict:
"""Create TCGPlayer listing"""
try:
# Map to TCGPlayer format
tcg_listing = {
'productId': await self.find_tcg_product_id(listing_data),
'condition': self.map_condition_to_tcg(listing_data['condition']),
'price': listing_data['price'],
'quantity': listing_data['quantity'],
'language': 'English',
'notes': listing_data.get('seller_notes', ''),
'expeditedShipping': listing_data.get('expedited_shipping', False)
}
response = await self.make_tcg_api_call('POST', '/inventory/products', tcg_listing)
return {
'success': True,
'listing_id': response['results'][0]['productConditionId'],
'listing_url': f"https://www.tcgplayer.com/product/{response['results'][0]['productId']}"
}
except Exception as e:
return {
'success': False,
'error': str(e)
}
async def find_tcg_product_id(self, listing_data: Dict) -> Optional[int]:
"""Find matching TCGPlayer product ID using Trading Card API data"""
search_params = {
'productName': listing_data['title'],
'setName': listing_data['specifications'].get('Set'),
'limit': 10
}
response = await self.make_tcg_api_call('GET', '/catalog/products', search_params)
# Find best match
for product in response.get('results', []):
if self.is_product_match(product, listing_data):
return product['productId']
return None
def map_condition_to_tcg(self, condition: str) -> str:
"""Map general condition to TCGPlayer conditions"""
condition_mapping = {
'M': 'Near Mint',
'NM': 'Near Mint',
'EX': 'Lightly Played',
'VG': 'Moderately Played',
'G': 'Heavily Played',
'P': 'Damaged'
}
return condition_mapping.get(condition, 'Near Mint')
class MarketplaceOrchestrator:
def __init__(self, adapters: Dict[str, MarketplaceAdapter]):
self.adapters = adapters
self.cross_listing_rules = {}
async def cross_list_card(self, card_id: str, listing_data: Dict, target_platforms: List[str]) -> Dict:
"""Create listings across multiple marketplaces simultaneously"""
results = {}
for platform in target_platforms:
if platform not in self.adapters:
results[platform] = {'success': False, 'error': 'Platform not supported'}
continue
try:
adapter = self.adapters[platform]
# Apply platform-specific adjustments
platform_listing = self.adjust_for_platform(listing_data, platform)
# Create listing
result = await adapter.create_listing(platform_listing)
results[platform] = result
# Add small delay between platforms to avoid overwhelming
await asyncio.sleep(1)
except Exception as e:
results[platform] = {'success': False, 'error': str(e)}
return {
'card_id': card_id,
'platforms_attempted': len(target_platforms),
'successful_listings': len([r for r in results.values() if r.get('success')]),
'results': results,
'cross_listed_at': datetime.now().isoformat()
}
def adjust_for_platform(self, base_listing: Dict, platform: str) -> Dict:
"""Adjust listing data for specific platform requirements"""
adjusted = base_listing.copy()
if platform == 'ebay':
# eBay-specific adjustments
adjusted['title'] = adjusted['title'][:80] # Title length limit
adjusted['requires_paypal'] = True
elif platform == 'tcgplayer':
# TCGPlayer-specific adjustments
adjusted['requires_condition_details'] = True
adjusted['max_quantity'] = 99
elif platform == 'mercari':
# Mercari-specific adjustments
adjusted['title'] = adjusted['title'][:40] # Shorter title limit
adjusted['description'] = adjusted['description'][:1000] # Description limit
return adjusted
Step 7: Analytics and Reportingβ
Marketplace Performance Analyticsβ
class MarketplaceAnalytics {
constructor(database, marketplaceAdapters) {
this.database = database;
this.marketplaces = marketplaceAdapters;
}
async generateSellerDashboard(sellerId, timeframe = 30) {
const endDate = new Date();
const startDate = new Date(endDate.getTime() - (timeframe * 24 * 60 * 60 * 1000));
const dashboard = {
overview: await this.getSellerOverview(sellerId, startDate, endDate),
sales: await this.getSalesAnalytics(sellerId, startDate, endDate),
inventory: await this.getInventoryAnalytics(sellerId),
performance: await this.getListingPerformance(sellerId, startDate, endDate),
recommendations: await this.generateSellerRecommendations(sellerId)
};
return dashboard;
}
async getSellerOverview(sellerId, startDate, endDate) {
const metrics = await this.database.getSellerMetrics(sellerId, startDate, endDate);
return {
totalRevenue: metrics.totalRevenue,
totalSales: metrics.salesCount,
averageOrderValue: metrics.totalRevenue / metrics.salesCount,
activeListings: metrics.activeListings,
totalInventoryValue: metrics.inventoryValue,
topSellingCard: metrics.topSellingCard,
conversionRate: (metrics.salesCount / metrics.totalViews) * 100
};
}
async getSalesAnalytics(sellerId, startDate, endDate) {
const salesData = await this.database.getSalesData(sellerId, startDate, endDate);
// Group sales by day for trend analysis
const dailySales = this.groupSalesByDay(salesData);
// Analyze by marketplace platform
const platformBreakdown = this.analyzeSalesByPlatform(salesData);
// Top selling cards
const topCards = this.getTopSellingCards(salesData);
return {
dailyTrend: dailySales,
platformPerformance: platformBreakdown,
topSellingCards: topCards,
averageDaysToSell: this.calculateAverageDaysToSell(salesData),
seasonalTrends: this.analyzeSeasonalTrends(salesData)
};
}
async getListingPerformance(sellerId, startDate, endDate) {
const listings = await this.database.getListingAnalytics(sellerId, startDate, endDate);
return {
totalListings: listings.length,
soldListings: listings.filter(l => l.status === 'sold').length,
activeListings: listings.filter(l => l.status === 'active').length,
averageViewsPerListing: listings.reduce((sum, l) => sum + l.views, 0) / listings.length,
averageWatchersPerListing: listings.reduce((sum, l) => sum + l.watchers, 0) / listings.length,
conversionRate: (listings.filter(l => l.status === 'sold').length / listings.length) * 100,
listingsNeedingAttention: this.identifyUnderperformingListings(listings)
};
}
async generateSellerRecommendations(sellerId) {
const recommendations = [];
// Analyze seller performance
const performance = await this.database.getSellerPerformanceMetrics(sellerId);
// Price optimization recommendations
if (performance.avgDaysToSell > 14) {
recommendations.push({
type: 'pricing',
priority: 'high',
title: 'Consider Price Optimization',
description: 'Your listings are taking longer than average to sell. Consider reviewing your pricing strategy.',
action: 'Review pricing compared to market rates'
});
}
// Inventory recommendations
const lowStockItems = await this.database.getLowStockItems(sellerId);
if (lowStockItems.length > 0) {
recommendations.push({
type: 'inventory',
priority: 'medium',
title: 'Restock Popular Items',
description: `${lowStockItems.length} popular items are running low on inventory.`,
action: 'Review restock opportunities'
});
}
// Platform expansion recommendations
const platformPerformance = await this.analyzePlatformOpportunities(sellerId);
if (platformPerformance.suggestedPlatforms.length > 0) {
recommendations.push({
type: 'expansion',
priority: 'low',
title: 'Expand to New Platforms',
description: `Consider listing on ${platformPerformance.suggestedPlatforms.join(', ')} for increased reach.`,
action: 'Evaluate new marketplace opportunities'
});
}
return recommendations;
}
identifyUnderperformingListings(listings) {
// Find listings that need attention
return listings.filter(listing => {
const daysSinceListed = (new Date() - new Date(listing.createdAt)) / (1000 * 60 * 60 * 24);
const hasLowEngagement = listing.views < 5 && listing.watchers < 2;
const isStale = daysSinceListed > 30;
return hasLowEngagement || isStale;
}).map(listing => ({
listingId: listing.id,
cardName: listing.cardName,
daysSinceListed: Math.floor((new Date() - new Date(listing.createdAt)) / (1000 * 60 * 60 * 24)),
views: listing.views,
watchers: listing.watchers,
suggestedAction: this.suggestListingAction(listing)
}));
}
suggestListingAction(listing) {
const daysSinceListed = (new Date() - new Date(listing.createdAt)) / (1000 * 60 * 60 * 24);
if (listing.views < 3 && daysSinceListed > 7) {
return 'Consider reducing price or improving title/description';
}
if (listing.watchers > 5 && daysSinceListed > 14) {
return 'High interest but no sales - check if price is competitive';
}
if (daysSinceListed > 30) {
return 'Relist with fresh title and updated market price';
}
return 'Monitor performance';
}
}
Step 8: Payment Processing Integrationβ
Secure Transaction Handlingβ
import stripe
from decimal import Decimal
from datetime import datetime
class PaymentProcessor:
def __init__(self, stripe_secret_key: str, platform_fee_percentage: float = 2.9):
stripe.api_key = stripe_secret_key
self.platform_fee = platform_fee_percentage / 100
async def process_card_purchase(self, order_data: Dict) -> Dict:
"""Process payment for trading card purchase"""
try:
# Calculate amounts
subtotal = self.calculate_subtotal(order_data['items'])
tax_amount = await self.calculate_tax(order_data)
shipping_cost = self.calculate_shipping_cost(order_data)
platform_fee = subtotal * self.platform_fee
total_amount = subtotal + tax_amount + shipping_cost
seller_payout = subtotal - platform_fee
# Create Stripe Payment Intent
payment_intent = stripe.PaymentIntent.create(
amount=int(total_amount * 100), # Stripe uses cents
currency='usd',
metadata={
'order_id': order_data['order_id'],
'seller_id': order_data['seller_id'],
'buyer_id': order_data['buyer_id'],
'card_count': len(order_data['items']),
'platform_fee': platform_fee,
'seller_payout': seller_payout
},
transfer_data={
'destination': order_data['seller_stripe_account'],
'amount': int(seller_payout * 100)
},
application_fee_amount=int(platform_fee * 100)
)
# Record transaction intent
await self.record_payment_intent(order_data['order_id'], payment_intent)
return {
'success': True,
'payment_intent_id': payment_intent.id,
'client_secret': payment_intent.client_secret,
'amount_breakdown': {
'subtotal': subtotal,
'tax': tax_amount,
'shipping': shipping_cost,
'platform_fee': platform_fee,
'total': total_amount,
'seller_payout': seller_payout
}
}
except stripe.error.StripeError as e:
return {
'success': False,
'error': f"Payment processing error: {str(e)}"
}
except Exception as e:
return {
'success': False,
'error': f"Unexpected error: {str(e)}"
}
async def handle_payment_webhook(self, webhook_data: Dict) -> Dict:
"""Handle Stripe webhook events"""
event_type = webhook_data['type']
if event_type == 'payment_intent.succeeded':
return await self.handle_successful_payment(webhook_data['data']['object'])
elif event_type == 'payment_intent.payment_failed':
return await self.handle_failed_payment(webhook_data['data']['object'])
elif event_type == 'transfer.created':
return await self.handle_seller_payout(webhook_data['data']['object'])
else:
return {'processed': False, 'reason': f'Unhandled event type: {event_type}'}
async def handle_successful_payment(self, payment_intent: Dict) -> Dict:
"""Process successful payment completion"""
order_id = payment_intent['metadata']['order_id']
try:
# Update order status
await self.database.update_order_status(order_id, 'paid')
# Trigger fulfillment process
await self.trigger_fulfillment(order_id)
# Send confirmation emails
await self.send_payment_confirmation(order_id)
# Update seller dashboard
await self.update_seller_metrics(payment_intent['metadata']['seller_id'])
return {
'processed': True,
'order_id': order_id,
'action': 'payment_confirmed'
}
except Exception as e:
# Payment succeeded but post-processing failed
await self.handle_post_payment_error(order_id, str(e))
return {
'processed': False,
'error': f"Post-payment processing failed: {str(e)}"
}
async def calculate_tax(self, order_data: Dict) -> float:
"""Calculate sales tax based on buyer location and card types"""
# Tax calculation for trading cards varies by jurisdiction
buyer_location = order_data['shipping_address']
items = order_data['items']
# This would integrate with tax calculation services like Avalara or TaxJar
tax_rate = await self.get_tax_rate(buyer_location, 'trading_cards')
subtotal = self.calculate_subtotal(items)
return subtotal * tax_rate
def calculate_subtotal(self, items: List[Dict]) -> float:
"""Calculate order subtotal"""
return sum(item['price'] * item['quantity'] for item in items)
def calculate_shipping_cost(self, order_data: Dict) -> float:
"""Calculate shipping cost based on items and destination"""
items = order_data['items']
destination = order_data['shipping_address']
# Basic shipping calculation
total_value = self.calculate_subtotal(items)
item_count = sum(item['quantity'] for item in items)
# Base shipping cost
base_cost = 4.99
# Additional cost per extra item
if item_count > 1:
base_cost += (item_count - 1) * 0.50
# Insurance for high-value orders
if total_value > 100:
base_cost += 2.00 # Insurance fee
# Expedited shipping option
if order_data.get('expedited_shipping'):
base_cost *= 2.5
return base_cost
Step 9: Compliance and Securityβ
PCI Compliance and Data Protectionβ
class ComplianceManager {
constructor(config) {
this.config = config;
this.auditLogger = new AuditLogger();
}
async validateTransaction(transactionData) {
const validations = {
pciCompliance: await this.validatePCICompliance(transactionData),
dataProtection: await this.validateDataProtection(transactionData),
fraudPrevention: await this.validateFraudPrevention(transactionData),
taxCompliance: await this.validateTaxCompliance(transactionData)
};
const allValid = Object.values(validations).every(v => v.valid);
await this.auditLogger.logComplianceCheck({
transactionId: transactionData.id,
validations,
result: allValid ? 'passed' : 'failed',
timestamp: new Date()
});
return {
compliant: allValid,
validations,
requiredActions: this.generateComplianceActions(validations)
};
}
async validatePCICompliance(transactionData) {
// Ensure no sensitive payment data is stored
const violations = [];
// Check if any sensitive data is being stored
if (transactionData.cardNumber) {
violations.push('Card number should not be stored');
}
if (transactionData.cvv) {
violations.push('CVV should never be stored');
}
// Verify encryption for any stored payment references
if (transactionData.paymentMethodId && !this.isEncrypted(transactionData.paymentMethodId)) {
violations.push('Payment method references must be encrypted');
}
return {
valid: violations.length === 0,
violations,
recommendation: 'Use payment processor tokens instead of storing sensitive data'
};
}
async validateDataProtection(transactionData) {
// Validate data minimization and user consent
const checks = {
hasUserConsent: await this.verifyUserConsent(transactionData.buyerId),
dataMinimization: this.checkDataMinimization(transactionData),
retentionPolicy: this.checkRetentionCompliance(transactionData),
accessControls: this.validateAccessControls(transactionData)
};
return {
valid: Object.values(checks).every(Boolean),
checks,
recommendation: 'Ensure GDPR/CCPA compliance for all user data'
};
}
async validateTaxCompliance(transactionData) {
// Verify proper tax calculation and reporting
const taxValidation = {
correctRate: await this.verifyTaxRate(transactionData),
properDocumentation: this.checkTaxDocumentation(transactionData),
reportingReady: this.validateTaxReporting(transactionData)
};
return {
valid: Object.values(taxValidation).every(Boolean),
details: taxValidation,
recommendation: 'Ensure all transactions are properly documented for tax reporting'
};
}
generateComplianceActions(validations) {
const actions = [];
Object.entries(validations).forEach(([area, validation]) => {
if (!validation.valid) {
actions.push({
area,
priority: this.getCompliancePriority(area),
action: validation.recommendation,
deadline: this.getComplianceDeadline(area)
});
}
});
return actions.sort((a, b) => this.priorityOrder[a.priority] - this.priorityOrder[b.priority]);
}
getCompliancePriority(area) {
const priorities = {
pciCompliance: 'critical',
fraudPrevention: 'high',
dataProtection: 'high',
taxCompliance: 'medium'
};
return priorities[area] || 'low';
}
}
Step 10: Webhook Integration and Real-time Updatesβ
Marketplace Event Handlingβ
from flask import Flask, request, jsonify
import hmac
import hashlib
from typing import Dict
class MarketplaceWebhookHandler:
def __init__(self, inventory_manager, order_processor, analytics_tracker):
self.inventory_manager = inventory_manager
self.order_processor = order_processor
self.analytics = analytics_tracker
self.webhook_secrets = {} # Platform-specific webhook secrets
def setup_webhook_endpoints(self, app: Flask):
"""Set up webhook endpoints for different marketplaces"""
@app.route('/webhooks/ebay', methods=['POST'])
async def handle_ebay_webhook():
return await self.process_ebay_webhook(request)
@app.route('/webhooks/tcgplayer', methods=['POST'])
async def handle_tcgplayer_webhook():
return await self.process_tcgplayer_webhook(request)
@app.route('/webhooks/stripe', methods=['POST'])
async def handle_stripe_webhook():
return await self.process_stripe_webhook(request)
async def process_ebay_webhook(self, request) -> Dict:
"""Process eBay marketplace webhooks"""
try:
# Verify webhook signature
if not self.verify_ebay_webhook(request):
return {'error': 'Invalid webhook signature'}, 401
webhook_data = request.get_json()
event_type = webhook_data.get('metadata', {}).get('topic')
if event_type == 'FIXED_PRICE_TRANSACTION':
# Handle completed sale
return await self.handle_ebay_sale(webhook_data)
elif event_type == 'AUCTION_TRANSACTION':
# Handle auction completion
return await self.handle_ebay_auction_completion(webhook_data)
elif event_type == 'ITEM_LISTED':
# Handle new listing confirmation
return await self.handle_listing_confirmation(webhook_data, 'ebay')
elif event_type == 'ITEM_REVISED':
# Handle listing updates
return await self.handle_listing_revision(webhook_data, 'ebay')
elif event_type == 'RETURN_CREATED':
# Handle return request
return await self.handle_return_request(webhook_data, 'ebay')
return {'status': 'event_not_handled', 'event_type': event_type}
except Exception as e:
await self.log_webhook_error('ebay', str(e), request.get_data())
return {'error': 'Webhook processing failed'}, 500
async def handle_ebay_sale(self, webhook_data: Dict) -> Dict:
"""Process eBay sale completion"""
transaction_data = webhook_data['summary']
sale_info = {
'marketplace': 'ebay',
'marketplace_transaction_id': transaction_data['transactionId'],
'listing_id': transaction_data['itemId'],
'buyer_id': transaction_data['buyer']['username'],
'quantity_sold': transaction_data['quantityPurchased'],
'sale_price': float(transaction_data['totalPrice']['value']),
'sale_date': transaction_data['creationDate'],
'payment_status': transaction_data['paymentSummary']['paymentStatus']
}
# Find our internal listing
internal_listing = await self.database.get_listing_by_marketplace_id(
sale_info['listing_id'],
'ebay'
)
if not internal_listing:
return {'error': 'Internal listing not found'}
# Process the sale
result = await self.inventory_manager.handle_sale({
'listingId': internal_listing['id'],
'quantitySold': sale_info['quantity_sold'],
'buyerId': sale_info['buyer_id'],
'salePrice': sale_info['sale_price'],
'transactionId': sale_info['marketplace_transaction_id']
})
# Update analytics
await self.analytics.record_sale(sale_info)
# Send seller notification
await self.send_sale_notification(internal_listing['seller_id'], sale_info)
return {'status': 'sale_processed', 'internal_order_id': result.get('orderId')}
def verify_ebay_webhook(self, request) -> bool:
"""Verify eBay webhook signature"""
signature = request.headers.get('X-EBAY-SIGNATURE')
if not signature:
return False
# eBay webhook verification process
payload = request.get_data()
expected_signature = self.calculate_ebay_signature(payload)
return hmac.compare_digest(signature, expected_signature)
def calculate_ebay_signature(self, payload: bytes) -> str:
"""Calculate expected eBay webhook signature"""
secret = self.webhook_secrets.get('ebay', '').encode()
signature = hmac.new(secret, payload, hashlib.sha256).hexdigest()
return f"sha256={signature}"
async def setup_marketplace_webhooks(self):
"""Register webhook endpoints with each marketplace"""
# eBay webhook setup
await self.register_ebay_webhooks()
# TCGPlayer webhook setup
await self.register_tcgplayer_webhooks()
# Other marketplace webhooks
# await self.register_other_webhooks()
async def register_ebay_webhooks(self):
"""Register webhooks with eBay"""
webhook_topics = [
'FIXED_PRICE_TRANSACTION',
'AUCTION_TRANSACTION',
'ITEM_LISTED',
'ITEM_REVISED',
'RETURN_CREATED'
]
for topic in webhook_topics:
try:
# Register webhook with eBay API
webhook_config = {
'topic': topic,
'endpoint': f"{self.config['base_url']}/webhooks/ebay",
'format': 'JSON'
}
# This would call eBay's webhook registration API
# await self.ebay_api.register_webhook(webhook_config)
print(f"Registered eBay webhook for {topic}")
except Exception as e:
print(f"Failed to register eBay webhook for {topic}: {e}")
π― Best Practices Summaryβ
Performance Optimizationβ
- Batch API operations to minimize rate limiting
- Cache card data aggressively since it changes infrequently
- Use CDN for card images and static marketplace assets
- Implement pagination for large product catalogs
Security Best Practicesβ
- Never store payment card data - use tokenization
- Validate all webhook signatures to prevent tampering
- Implement rate limiting on all public endpoints
- Use HTTPS everywhere for data transmission
- Encrypt sensitive data at rest and in transit
User Experienceβ
- Provide detailed card authentication information in listings
- Show real-time inventory availability
- Implement advanced search with Trading Card API filters
- Offer multiple payment options and secure checkout
Business Intelligenceβ
- Track seller performance metrics across platforms
- Monitor market trends for pricing optimization
- Analyze buyer behavior to improve recommendations
- Generate compliance reports for tax and regulatory requirements
π Scaling Considerationsβ
Infrastructure Requirementsβ
- Load balancers for high-traffic marketplace operations
- Message queues for processing webhook events
- Database replication for read-heavy operations
- CDN integration for global card image delivery
Multi-Region Supportβ
- Localized pricing in different currencies
- Regional marketplace compliance (EU GDPR, California CCPA)
- Multi-language support for international sellers
- Currency conversion with real-time exchange rates
This comprehensive guide provides the foundation for building professional marketplace integrations that leverage Trading Card API's rich data to create compelling, authentic, and secure trading experiences.
Next Stepsβ
- Review API Authentication - Set up secure API access
- Explore Card Endpoints - Understand available card data
- Study Collection Management - Learn complementary features
- Join Developer Community - Connect with other marketplace builders
Building a trading card marketplace? Our developer community has extensive experience with commerce integrations and can provide guidance on complex implementation challenges.