Scanner Foil Pricing & Cart Upgrade: Track Both Finishes
2025-10-05
Scanner Foil Pricing & Cart Upgrade
We've just shipped a major upgrade to the MTG card scanner and cart system that addresses one of the most common requests: proper foil and non-foil pricing support.
What's New
1. Dual Price Display in Scanner
When you scan a card, you now see both foil and non-foil prices side-by-side (when available):
Before:
- Single price shown (could be foil or non-foil, unclear)
- No way to compare finishes
- Manual TCGplayer lookup needed for other finish
After:
- Non-Foil and Foil prices displayed clearly
- Side-by-side comparison for quick decisions
- Falls back gracefully when only one finish is available
Example:
Non-Foil: $4.99 Foil: $12.50
This is especially useful for:
- Budget brewing - choosing cheaper finish
- Bling builds - knowing foil upgrade cost
- Value assessment - spotting foil premiums
- Trade evaluation - accurate finish-specific pricing
2. Foil Toggle in Cart
The scan cart now includes a foil checkbox for each card:
- Check the box ā prices update to foil
- Uncheck the box ā prices revert to non-foil
- Total cart price reflects your choices
- Toggle anytime to compare builds
Cart Display:
Rhystic Study
Jumpstart
Qty: [ā] 1 [+] ā Foil Remove
$9.99 (Non-Foil) Ć 1 = $9.99
After checking foil:
Rhystic Study
Jumpstart
Qty: [ā] 1 [+] ā Foil Remove
$32.50 (Foil) Ć 1 = $32.50
3. Accurate Total Calculation
The cart total now respects your finish choices:
- Non-foil cards ā uses non-foil pricing
- Foil cards ā uses foil pricing
- Mixed cart ā calculates each item correctly
- Real-time updates when toggling foil
Before:
Total: $45.00 (unclear which finishes)
After:
3 items - Total: $47.50
- 2 non-foil cards: $18.50
- 1 foil card: $29.00
Technical Implementation
Price Data Structure
Cards now carry structured pricing with both finishes:
interface ScanCartPricing {
normal?: {
market?: number;
low?: number;
mid?: number;
// ... other fields
};
foil?: {
market?: number;
low?: number;
mid?: number;
// ... other fields
};
}
Smart Fallbacks
The system intelligently handles missing data:
- Prefers structured pricing (
item.pricing.foil
/item.pricing.normal
) - Falls back to legacy tcgcsv (when subType indicates finish)
- Uses Scryfall prices (from
price_usd
/price_usd_foil
) - Gracefully degrades when only one finish available
Price Selection Logic
When displaying prices in the cart:
// User selected foil? Try foil price first
const activeVariant = item.isFoil ? item.pricing?.foil : item.pricing?.normal;
// Not available? Fall back to other finish
const fallbackVariant = item.isFoil ? item.pricing?.normal : item.pricing?.foil;
// Pick best available price
const price = activeVariant?.market ??
activeVariant?.mid ??
activeVariant?.low ??
fallbackVariant?.market ??
// ... continue chain
This ensures accurate pricing even when data is incomplete.
Real-World Use Cases
1. Budget Deck Building
You're building a Commander deck with a $150 budget:
- Scan cards as you find them (at card shows, LGS bulk bins)
- Scanner shows both prices ā choose non-foil for budget
- Cart tracks your choices ā see running total
- Stay under budget by selecting cheaper finishes
Example Workflow:
- Scan Sol Ring ā Non-foil $1.50, Foil $8.00
- Keep non-foil ā unchecked
- Cart shows: $1.50
- Scan Rhystic Study ā Non-foil $9.99, Foil $32.50
- Keep non-foil ā unchecked
- Cart shows: $11.49 total
- Continue scanning...
2. Foil Upgrade Planning
You have a deck in non-foil and want to bling it out:
- Scan your current cards
- Check the foil box for each card
- See the upgrade cost instantly
- Prioritize which cards to foil first
Example:
Current deck (non-foil): $200.00
Full foil upgrade: $450.00
Upgrade cost: $250.00
Expensive foils to skip:
- Rhystic Study (+$22.50)
- Cyclonic Rift (+$18.00)
- Smothering Tithe (+$15.50)
Skip those 3 ā save $56.00
3. Card Show Shopping
At Minnesota Card Show or other events:
- Scan cards from vendor tables
- See both finishes ā decide on the spot
- Add to cart with correct finish
- Negotiate with accurate pricing info
Vendor: "This foil is $15"
You: scans card "Scanner shows foil market at $12.50, can you do $12?"
4. Collection Valuation
Tracking your collection's worth by finish:
- Scan entire collection
- Toggle foil for cards you own in foil
- Get accurate total reflecting actual finishes
- Export cart to share with insurance/trade partners
Price Data Sources
Pricing comes from multiple sources:
Primary: TCGcsv Nightly Cache
- Refreshed daily at 21:10 UTC (10 min after TCGcsv updates)
- Includes finish variants (normal, foil, etched)
- Market, low, mid prices for accurate ranges
- Direct product URLs for TCGplayer links
Fallback: Scryfall Bulk Data
- Basic USD prices for normal and foil
- Used when TCGcsv missing (old sets, promos)
- Single price point (not low/mid/high)
Finish Detection
- TCGcsv subType field ā "Foil", "Normal", "Etched"
- Scryfall fields ā
price_usd
(normal),price_usd_foil
(foil) - Smart parsing ā "Non-Foil Foil" handled correctly
Scanner Display Logic
The scanner shows pricing based on what's available:
Both Finishes Available
āāāāāāāāāāāāāāāāāāāāāāā
ā Non-Foil Foil ā
ā $4.99 $12.50 ā
āāāāāāāāāāāāāāāāāāāāāāā
[Buy on TCGplayer]
Only Non-Foil Available
āāāāāāāāāāāāāāāāāāāāāāā
ā $4.99 ā
ā Low: $4.00 Mid: $5 ā
āāāāāāāāāāāāāāāāāāāāāāā
[Buy on TCGplayer]
Only Foil Available
āāāāāāāāāāāāāāāāāāāāāāā
ā Foil ā
ā $12.50 ā
ā Low: $10 Mid: $13 ā
āāāāāāāāāāāāāāāāāāāāāāā
[Buy on TCGplayer]
Cart Features
Foil Checkbox
- Only shows when pricing data available for both finishes
- Checkbox state persists in localStorage
- Instant updates when toggled
- Visual feedback ā "(Foil)" or "(Non-Foil)" label
Per-Item Pricing
Each cart item shows:
- Unit price with finish label
- Quantity multiplier
- Line total (unit Ć quantity)
Example:
$9.99 (Non-Foil) Ć 2 = $19.98
Cart Total
Bottom of cart shows:
- Item count (total cards across all entries)
- Estimated total (sum of all line totals)
3 items - $47.50
Save & Share
Cart state includes foil flags:
- Save cart ā preserves foil selections
- Load shared cart ā restores finish choices
- URL sharing ā complete cart state in link
Limitations & Future Plans
Current Limitations
-
No foil toggle on scanner cards
- Can't select finish before adding to cart
- Must add to cart first, then toggle
- Planned: Foil/non-foil buttons on scanner matches
-
No bulk foil toggle
- Must toggle each card individually
- Planned: "All foil" / "All non-foil" buttons
-
Pricing gaps for old sets
- Some old foils missing from TCGcsv
- Falls back to Scryfall (less detailed)
- Planned: Additional price sources
-
No special finishes
- Etched, extended art, showcase not separated
- Lumped into foil or non-foil
- Planned: Full finish taxonomy support
Future Enhancements
Short Term:
- Pre-select finish in scanner before adding to cart
- Bulk foil toggle for entire cart
- Price history graphs (foil vs non-foil trends)
Medium Term:
- Extended art, showcase, borderless pricing
- Foil premium % display ("Foil is 2.5x normal")
- Budget-optimized recommendations ("Save $X by going non-foil")
Long Term:
- Condition tracking (NM, LP, MP with price adjustments)
- Language variants with pricing
- Full set completion tracking with finish goals
Technical Details
API Response Structure
The /api/cards/scan
endpoint now returns:
{
matches: [{
name: "Rhystic Study",
pricing: {
normal: {
market: 9.99,
low: 8.50,
mid: 10.00,
source: "tcgcsv"
},
foil: {
market: 32.50,
low: 28.00,
mid: 33.00,
source: "tcgcsv"
}
}
}]
}
Cart Storage
localStorage cart entries include:
{
"id": "card-uuid",
"name": "Rhystic Study",
"quantity": 2,
"isFoil": false,
"pricing": {
"normal": { "market": 9.99, ... },
"foil": { "market": 32.50, ... }
}
}
Price Calculation
The getUnitPrice()
function:
function getUnitPrice(item: ScanCartItem): number | null {
// Prefer user's selected finish
const primary = item.isFoil ? item.pricing?.foil : item.pricing?.normal;
// Fall back to other finish if primary missing
const secondary = item.isFoil ? item.pricing?.normal : item.pricing?.foil;
// Try primary ā secondary ā legacy tcgcsv
return pickBestPrice(primary) ??
pickBestPrice(secondary) ??
pickBestPrice(item.tcgcsv);
}
Developer Notes
Testing Checklist
When testing foil/non-foil functionality:
- Scan card with both finishes available
- Verify both prices display in scanner
- Add to cart ā defaults to non-foil
- Toggle foil checkbox ā price updates
- Verify cart total reflects foil state
- Save cart ā reload ā foil state persists
- Scan card with only non-foil ā no foil checkbox
- Scan card with only foil ā displays correctly
- Mixed cart (some foil, some non-foil) ā total correct
Code Locations
Scanner Display:
components/CardScanner.tsx
lines 900-1050- Price formatting:
formatPriceValue()
helper - Fallback logic for legacy tcgcsv data
Cart Sidebar:
components/ScanCartSidebar.tsx
lines 220-280- Foil checkbox: lines 250-260
- Price display: lines 265-280
Cart Context:
components/ScanCartContext.tsx
setFoil()
function: lines 280-285getUnitPrice()
helper: lines 175-190
API Endpoint:
app/api/cards/scan/route.ts
buildPricing()
function: lines 65-115- Finish detection: lines 70-85
User Feedback
Early testing revealed several insights:
What Users Love
- "Finally can see foil premiums!"
- "Cart total is actually accurate now"
- "Love the checkbox - so simple"
- "Side-by-side comparison is perfect"
What Users Want Next
- "Let me pick foil/non-foil in scanner"
- "Bulk toggle for whole cart"
- "Show me % difference between finishes"
- "Extended art prices too please"
Conclusion
The foil/non-foil pricing upgrade makes the scanner and cart significantly more useful for:
- Budget builders who need accurate non-foil pricing
- Bling enthusiasts tracking foil upgrade costs
- Traders valuing cards by exact finish
- Collectors managing mixed finish collections
Key Wins:
- ā Both prices visible in scanner
- ā Cart tracks finish per card
- ā Accurate totals reflecting finish choices
- ā Persistent state across save/load
- ā Graceful fallbacks for missing data
Next Steps:
- Add finish selection in scanner UI
- Implement bulk foil toggle
- Expand to extended art and showcase variants
This update represents a major step toward comprehensive finish-aware pricing that respects how players actually build and value their MTG collections.
Updated: October 5, 2025
Feature shipped in production