Class: Retailer::WalmartProbe

Inherits:
Object
  • Object
show all
Defined in:
app/services/retailer/walmart_probe.rb

Overview

Runs a retailer price/stock probe for a Walmart Marketplace catalog item by
asking Walmart, instead of scraping walmart.com.

We are the SELLER on these listings, so the Marketplace API is the system of
record: it is authoritative, free, immune to the bot-blocking that makes the
public page unreadable, and — uniquely — it says WHY a listing is not
purchasable. A scrape can only report "the page had no price", which is the
same output for a sold-out item, an unpublished one, a Walmart-side listing
error, and a delisting. Those need four different responses.

Measured 2026-08-09 against the 15 Walmart items the scraper had given up on:
every one answered, 14 with a live price. Four were PUBLISHED (the listing
was fine and the scrape was simply wrong), six UNPUBLISHED, four
SYSTEM_PROBLEM — recoverable revenue that read as an unreachable page — and
one genuine 404.

Mirrors CostcoProbe, the existing precedent for "this retailer has
an API, stop scraping it". Both PriceChecker and
BatchPriceChecker delegate here so probe bookkeeping stays in one
place.

Constant Summary collapse

SCRAPER_SOURCE =
'walmart_seller_api'
BUYABLE_PUBLISHED_STATUS =

publishedStatus values that mean a shopper can actually buy it. Anything
else is a listing problem with a name, and the name is the useful part.

'PUBLISHED'
PROFILES =

Marketplace profile per catalog.

{
  CatalogConstants::WALMART_SELLER_USA => { profile: :walmart_seller_us_api, currency: 'USD' },
  CatalogConstants::WALMART_SELLER_CANADA => { profile: :walmart_seller_ca_api, currency: 'CAD' }
}.freeze

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(logger: Rails.logger) ⇒ WalmartProbe

Returns a new instance of WalmartProbe.



40
41
42
# File 'app/services/retailer/walmart_probe.rb', line 40

def initialize(logger: Rails.logger)
  @logger = logger
end

Class Method Details

.handles?(catalog_id) ⇒ Boolean

Returns whether this probe handles the catalog.

Parameters:

  • catalog_id (Integer)

Returns:

  • (Boolean)

    whether this probe handles the catalog



38
# File 'app/services/retailer/walmart_probe.rb', line 38

def self.handles?(catalog_id) = PROFILES.key?(catalog_id)

Instance Method Details

#probe(catalog_item, config: PROFILES[catalog_item.catalog_id]) ⇒ CatalogItemRetailerProbe?

Probe one Walmart catalog item and record the result.

Parameters:

  • catalog_item (CatalogItem)
  • config (Hash) (defaults to: PROFILES[catalog_item.catalog_id])

    marketplace profile; injectable so tests never need to
    create a catalog carrying a production catalog id. Doing that in the shared
    test database makes every other test that resolves that id see the factory
    row instead of the real catalog.

Returns:

Raises:

  • (ArgumentError)


52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
# File 'app/services/retailer/walmart_probe.rb', line 52

def probe(catalog_item, config: PROFILES[catalog_item.catalog_id])
  return nil unless catalog_item.probeable?
  raise ArgumentError, "No Walmart profile for catalog #{catalog_item.catalog_id}" if config.nil?

  check = catalog_item.retailer_probes.build(
    status: 'pending',
    url: catalog_item.url,
    scraper_source: SCRAPER_SOURCE,
    currency: config[:currency]
  )

  run(check, catalog_item, config)
  check.save!

  update_catalog_item(catalog_item, check)
  if check.status == 'success'
    Retailer::ProbeAutoSkipper.record_success!(catalog_item)
  else
    Retailer::ProbeAutoSkipper.maybe_skip!(catalog_item)
  end

  check
end