takescake

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:

  1. Prefers structured pricing (item.pricing.foil / item.pricing.normal)
  2. Falls back to legacy tcgcsv (when subType indicates finish)
  3. Uses Scryfall prices (from price_usd / price_usd_foil)
  4. 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:

  1. Scan cards as you find them (at card shows, LGS bulk bins)
  2. Scanner shows both prices → choose non-foil for budget
  3. Cart tracks your choices → see running total
  4. 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:

  1. Scan your current cards
  2. Check the foil box for each card
  3. See the upgrade cost instantly
  4. 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:

  1. Scan cards from vendor tables
  2. See both finishes → decide on the spot
  3. Add to cart with correct finish
  4. 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:

  1. Scan entire collection
  2. Toggle foil for cards you own in foil
  3. Get accurate total reflecting actual finishes
  4. 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

  1. 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
  2. No bulk foil toggle

    • Must toggle each card individually
    • Planned: "All foil" / "All non-foil" buttons
  3. Pricing gaps for old sets

    • Some old foils missing from TCGcsv
    • Falls back to Scryfall (less detailed)
    • Planned: Additional price sources
  4. 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-285
  • getUnitPrice() 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:

  1. āœ… Both prices visible in scanner
  2. āœ… Cart tracks finish per card
  3. āœ… Accurate totals reflecting finish choices
  4. āœ… Persistent state across save/load
  5. āœ… 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

Related Posts