Files
TurboTrades/PRICING_SYSTEM.md
2026-01-10 04:57:43 +00:00

776 lines
15 KiB
Markdown

# Pricing System & Phase Detection Guide
Complete guide for the automated pricing system using SteamAPIs.com, phase detection for Doppler items, and hourly price updates.
---
## 🎯 Overview
The pricing system automatically fetches and updates market prices for CS2 and Rust items every hour using SteamAPIs.com. It includes:
- **Phase Detection** - Automatically detects Doppler phases (Ruby, Sapphire, Phase 1-4, etc.)
- **Market Price Storage** - Stores prices in database for fast access
- **Automatic Updates** - Scheduled hourly updates (configurable)
- **Manual Triggers** - Admin panel for manual price updates
- **Estimation Fallback** - Smart price estimation when market data unavailable
---
## 📊 Features
### 1. Phase Detection
Automatically detects Doppler and Gamma Doppler phases from item names and descriptions:
**Supported Phases:**
- ✅ Ruby (highest value)
- ✅ Sapphire (highest value)
- ✅ Black Pearl (rare)
- ✅ Emerald (Gamma Doppler)
- ✅ Phase 1
- ✅ Phase 2 (popular)
- ✅ Phase 3
- ✅ Phase 4 (popular)
**How It Works:**
```javascript
// Example detection from item name/description
"★ Karambit | Doppler (Factory New) - Ruby" Phase: "Ruby"
"★ M9 Bayonet | Doppler (Minimal Wear) - Phase 2" Phase: "Phase 2"
```
**Phase Multipliers (Price Impact):**
- Ruby: 3.5x base price
- Sapphire: 3.8x base price
- Emerald: 4.0x base price
- Black Pearl: 2.5x base price
- Phase 2: 1.3x base price
- Phase 4: 1.2x base price
- Phase 1: 1.0x base price
- Phase 3: 0.95x base price
### 2. Market Price Fetching
**Source:** SteamAPIs.com `/market/items/{AppID}` endpoint
**Supported Games:**
- CS2 (AppID: 730)
- Rust (AppID: 252490)
**Price Data:**
- Uses 30-day average price (most stable)
- Currency: USD
- Updates every hour
- Caches last update timestamp
**Example API Call:**
```
GET https://api.steamapis.com/market/items/730?api_key=YOUR_KEY
```
**Response Format:**
```json
{
"data": {
"AK-47 | Redline (Field-Tested)": {
"prices": {
"7": 42.50,
"30": 41.80,
"all_time": 45.00
}
}
}
}
```
### 3. Database Storage
**Item Model Fields:**
```javascript
{
name: String, // Item name (used for price matching)
price: Number, // Seller's listing price
marketPrice: Number, // Current market price from SteamAPIs
priceUpdatedAt: Date, // Last price update timestamp
phase: String, // Doppler phase (if applicable)
wear: String, // fn, mw, ft, ww, bs
statTrak: Boolean,
souvenir: Boolean
}
```
### 4. Price Estimation
When market data is unavailable, the system uses intelligent estimation:
**Factors Considered:**
- Item name patterns (knives, gloves, high-tier skins)
- Wear condition multipliers
- Phase multipliers
- StatTrak multiplier (1.5x)
- Souvenir multiplier (1.3x)
**Wear Multipliers:**
- Factory New (FN): 1.0x
- Minimal Wear (MW): 0.85x
- Field-Tested (FT): 0.70x
- Well-Worn (WW): 0.55x
- Battle-Scarred (BS): 0.40x
---
## 🔧 Setup & Configuration
### 1. Environment Variables
Add to `.env` file:
```env
# Steam API Key (SteamAPIs.com)
STEAM_APIS_KEY=your_steamapis_key_here
STEAM_API_KEY=fallback_key_here
# Admin Configuration
ADMIN_STEAM_IDS=76561198000000000,76561198111111111
# Enable automatic price updates (optional in dev)
ENABLE_PRICE_UPDATES=true
```
### 2. Admin Access
**Option A: Steam ID Whitelist**
Add admin Steam IDs to `.env`:
```env
ADMIN_STEAM_IDS=76561198000000000,76561198111111111
```
**Option B: Staff Level**
Update user in database:
```javascript
db.users.updateOne(
{ steamId: "76561198000000000" },
{ $set: { staffLevel: 3 } }
)
```
**Admin Levels:**
- 0: Regular user
- 1: Moderator
- 2: Staff
- 3+: Admin (has pricing access)
### 3. Start Price Updates
**Automatic (on server start):**
```javascript
// Runs every 1 hour automatically
pricingService.scheduleUpdates(60 * 60 * 1000);
```
**Manual via API:**
```bash
POST /api/admin/prices/schedule
{
"intervalMinutes": 60
}
```
**Disable in Development:**
```env
# Don't set ENABLE_PRICE_UPDATES or set to false
ENABLE_PRICE_UPDATES=false
```
---
## 📡 API Endpoints
### Admin Endpoints (Require Authentication + Admin Access)
#### 1. Trigger Price Update
```http
POST /api/admin/prices/update
Content-Type: application/json
Cookie: accessToken=your_jwt_token
{
"game": "cs2" // "cs2", "rust", or "all"
}
```
**Response:**
```json
{
"success": true,
"message": "Price update completed",
"data": {
"cs2": {
"success": true,
"game": "cs2",
"total": 150,
"updated": 142,
"notFound": 8,
"errors": 0,
"timestamp": "2024-01-10T12:00:00.000Z"
}
}
}
```
#### 2. Get Price Update Status
```http
GET /api/admin/prices/status
Cookie: accessToken=your_jwt_token
```
**Response:**
```json
{
"success": true,
"status": {
"cs2": {
"lastUpdate": "2024-01-10T11:00:00.000Z",
"needsUpdate": false,
"stats": {
"total": 150,
"withMarketPrice": 142,
"avgMarketPrice": 45.50,
"minMarketPrice": 0.50,
"maxMarketPrice": 8999.99
}
},
"rust": {
"lastUpdate": "2024-01-10T11:00:00.000Z",
"needsUpdate": false,
"stats": {
"total": 50,
"withMarketPrice": 45,
"avgMarketPrice": 25.30
}
}
}
}
```
#### 3. Get Items Without Prices
```http
GET /api/admin/prices/missing?game=cs2&limit=50
Cookie: accessToken=your_jwt_token
```
**Response:**
```json
{
"success": true,
"total": 8,
"items": [
{
"_id": "item_id",
"name": "AK-47 | Redline (Field-Tested)",
"game": "cs2",
"category": "rifles",
"rarity": "rare",
"wear": "ft",
"phase": null,
"seller": {
"username": "User123"
}
}
]
}
```
#### 4. Estimate Price for Specific Item
```http
POST /api/admin/prices/estimate
Content-Type: application/json
Cookie: accessToken=your_jwt_token
{
"itemId": "item_id_here"
}
```
**Response:**
```json
{
"success": true,
"message": "Price estimated and updated",
"item": {
"id": "item_id",
"name": "AK-47 | Redline (Field-Tested)",
"marketPrice": 42.50,
"priceUpdatedAt": "2024-01-10T12:00:00.000Z"
}
}
```
#### 5. Schedule Price Updates
```http
POST /api/admin/prices/schedule
Content-Type: application/json
Cookie: accessToken=your_jwt_token
{
"intervalMinutes": 60
}
```
**Options:**
- Minimum: 15 minutes
- Maximum: 1440 minutes (24 hours)
- Default: 60 minutes (1 hour)
#### 6. Get System Statistics
```http
GET /api/admin/stats
Cookie: accessToken=your_jwt_token
```
**Response:**
```json
{
"success": true,
"stats": {
"items": {
"total": 250,
"active": 200,
"sold": 50
},
"users": {
"total": 1500
},
"marketplace": {
"totalValue": 125000.00,
"totalRevenue": 75000.00
},
"recentSales": [...]
}
}
```
---
## 🔄 How It Works
### Automatic Price Updates
**Flow:**
1. **Scheduler triggers** (every 1 hour)
2. **Fetch prices** from SteamAPIs.com
3. **Parse response** - Extract item names and prices
4. **Query database** - Get all active items for game
5. **Match items** - Compare by name (exact match)
6. **Update prices** - Save marketPrice and timestamp
7. **Log results** - Report success/failures
**Example Log Output:**
```
⏰ Scheduling automatic price updates every 60 minutes
📊 Fetching CS2 market prices...
✅ Fetched 5000 prices for CS2
🔄 Updating database prices for CS2...
✅ Price update complete for CS2:
- Total items: 150
- Updated: 142
- Not found: 8
- Errors: 0
```
### Phase Detection in Inventory
**When user loads Sell page:**
1. **Fetch inventory** from Steam/SteamAPIs
2. **Parse descriptions** - Extract item descriptions
3. **Detect phase** - Scan name + description for phase keywords
4. **Store phase** - Add to item data
5. **Calculate price** - Apply phase multipliers
**Example:**
```javascript
// Item from Steam inventory
{
name: "★ Karambit | Doppler (Factory New)",
descriptions: [
{ value: "Phase 2" },
{ value: "Rare Special Item" }
]
}
// After phase detection
{
name: "★ Karambit | Doppler (Factory New)",
phase: "Phase 2",
estimatedPrice: 520.00 // Base 400 * 1.3 (Phase 2 multiplier)
}
```
### Price Calculation
**Priority Order:**
1. **Database marketPrice** (if available and recent)
2. **SteamAPIs market data** (if available)
3. **Estimation algorithm** (fallback)
**Calculation Steps:**
```javascript
let price = basePrice;
// 1. Apply wear multiplier
if (wear) {
price *= wearMultipliers[wear];
}
// 2. Apply phase multiplier
if (phase) {
price *= phaseMultipliers[phase];
}
// 3. Apply StatTrak multiplier
if (statTrak) {
price *= 1.5;
}
// 4. Apply Souvenir multiplier
if (souvenir) {
price *= 1.3;
}
return Math.round(price * 100) / 100;
```
---
## 🧪 Testing
### 1. Test Price Fetching
**Via Admin API:**
```bash
# Get auth token first (login via Steam)
# Then trigger update
curl -X POST http://localhost:3000/api/admin/prices/update \
-H "Content-Type: application/json" \
-H "Cookie: accessToken=YOUR_TOKEN" \
-d '{"game": "cs2"}'
```
**Expected Response:**
```json
{
"success": true,
"message": "Price update completed",
"data": {
"cs2": {
"success": true,
"updated": 142,
"notFound": 8
}
}
}
```
### 2. Test Phase Detection
**Load item with Doppler:**
```javascript
// In Sell page, select a Doppler knife
// Check console logs or item data
console.log(item.phase); // Should show "Ruby", "Phase 2", etc.
```
### 3. Verify Database Updates
**MongoDB Shell:**
```javascript
// Check items with market prices
db.items.find({
marketPrice: { $exists: true, $ne: null }
}).count();
// Check recent price updates
db.items.find({
priceUpdatedAt: { $gte: new Date(Date.now() - 3600000) }
});
// Check items with phases
db.items.find({
phase: { $ne: null }
});
```
### 4. Test Scheduled Updates
**Enable in development:**
```env
ENABLE_PRICE_UPDATES=true
```
**Watch logs:**
```
⏰ Starting automatic price update scheduler...
📊 Fetching CS2 market prices...
✅ Fetched 5000 prices for CS2
🔄 Updating database prices for CS2...
✅ Price update complete
```
---
## 📈 Monitoring
### Check Price Update Status
**Via Admin Dashboard:**
```
GET /api/admin/prices/status
```
**Look for:**
- Last update timestamp
- Number of items with prices
- Items missing prices
- Average market prices
### Check Logs
**Successful Update:**
```
✅ Fetched 5000 prices for CS2
✅ Price update complete for CS2:
- Total items: 150
- Updated: 142
```
**Failures:**
```
❌ Error fetching market prices for cs2: 429 Rate limit exceeded
❌ Failed to update item AK-47 | Redline: Not found in market data
```
### Database Health Check
```javascript
// Check price coverage
db.items.aggregate([
{
$group: {
_id: "$game",
total: { $sum: 1 },
withPrice: {
$sum: { $cond: [{ $ne: ["$marketPrice", null] }, 1, 0] }
}
}
}
]);
// Output:
// { _id: "cs2", total: 150, withPrice: 142 }
// { _id: "rust", total: 50, withPrice: 45 }
```
---
## 🐛 Troubleshooting
### Issue: No Prices Being Fetched
**Check:**
1.`STEAM_APIS_KEY` in `.env`
2. ✅ API key is valid
3. ✅ SteamAPIs.com service status
4. ✅ Rate limits not exceeded
**Fix:**
```bash
# Verify API key
curl "https://api.steamapis.com/market/items/730?api_key=YOUR_KEY"
# Check backend logs
# Should see: "📊 Fetching CS2 market prices..."
```
### Issue: Phase Not Detected
**Check:**
1. Item name contains phase info
2. Item description parsed correctly
3. Phase keywords match exactly
**Debug:**
```javascript
// Add logging in inventory route
console.log("Item name:", item.name);
console.log("Descriptions:", item.descriptions);
console.log("Detected phase:", item.phase);
```
### Issue: Prices Not Updating
**Check:**
1. Scheduler is running
2. Last update timestamp
3. Items match by name exactly
4. No errors in logs
**Fix:**
```bash
# Manually trigger update
POST /api/admin/prices/update
# Check status
GET /api/admin/prices/status
# Check missing prices
GET /api/admin/prices/missing
```
### Issue: Rate Limit Exceeded
**Error:** `429 Rate limit exceeded`
**Solutions:**
1. Reduce update frequency (e.g., 2 hours instead of 1)
2. Upgrade SteamAPIs.com plan
3. Implement caching
4. Batch updates
**Configure:**
```javascript
// Update every 2 hours instead
pricingService.scheduleUpdates(120 * 60 * 1000);
```
---
## 🚀 Production Recommendations
### 1. Monitoring
- Set up alerts for failed price updates
- Monitor API usage and rate limits
- Track price coverage percentage
- Log all admin actions
### 2. Optimization
- Cache price data in Redis (5-10 minutes)
- Batch API requests when possible
- Only update changed prices
- Use webhooks if available
### 3. Backup Strategy
- Keep historical price data
- Store last N successful fetches
- Fallback to estimation if API down
- Manual override capability
### 4. Security
- Restrict admin endpoints to IP whitelist
- Log all price modifications
- Implement audit trail
- Rate limit admin endpoints
---
## 📚 API Reference
### SteamAPIs.com Endpoints
**Market Items:**
```
GET /market/items/{appId}?api_key={key}
```
**Rate Limits:**
- Free: 100,000 requests/month
- Pro: Higher limits available
**Documentation:**
https://steamapis.com/docs
---
## 🎓 Examples
### Example 1: Manual Price Update
```javascript
// Admin triggers update for CS2
const response = await fetch('/api/admin/prices/update', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
credentials: 'include',
body: JSON.stringify({ game: 'cs2' })
});
const result = await response.json();
console.log(`Updated ${result.data.cs2.updated} items`);
```
### Example 2: Phase Detection
```javascript
// Service method
const phase = pricingService.detectPhase(
"★ Karambit | Doppler (Factory New)",
"Phase 2 pattern with pink/purple playside"
);
console.log(phase); // "Phase 2"
```
### Example 3: Price Estimation
```javascript
const price = await pricingService.estimatePrice({
name: "AK-47 | Redline (Field-Tested)",
wear: "ft",
phase: null,
statTrak: false,
souvenir: false
});
console.log(price); // 42.50
```
---
## ✅ Checklist
**Setup:**
- [ ] Added `STEAM_APIS_KEY` to `.env`
- [ ] Configured admin Steam IDs or staff levels
- [ ] Tested API key with manual request
- [ ] Enabled price updates in environment
**Testing:**
- [ ] Manual price update works
- [ ] Automatic updates scheduled
- [ ] Phase detection working
- [ ] Database storing prices correctly
- [ ] Admin endpoints accessible
**Production:**
- [ ] Monitoring configured
- [ ] Rate limits understood
- [ ] Backup strategy in place
- [ ] Security measures implemented
---
**Last Updated:** 2024
**Version:** 1.0
**Maintained by:** TurboTrades Team