776 lines
15 KiB
Markdown
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 |