antiwork/gumroad · error · Purchase::PurchaseInvalid

The cart does not have any products to which the upsell appl

Error message

The cart does not have any products to which the upsell applies.

What it means

Raised by Gumroad's Purchase::CreateService when a checkout submits params[:accepted_offer] for a cross-sell upsell that keeps (rather than replaces) the buyer's selected products, but no product remaining in the cart - excluding the product being purchased - falls inside the upsell's scope. The scope is the seller's entire catalog when the upsell is universal, otherwise its explicitly selected_products. It prevents a stale or tampered accepted offer from applying a cross-sell discount to a cart that no longer contains anything the upsell covers.

Source

Thrown at app/services/purchase/create_service.rb:118

        valid_items = if purchase.offer_code.universal
          excluded_permalinks = purchase.offer_code.excluded_products.pluck(:unique_permalink)
          valid_items.reject { excluded_permalinks.include?(_1[:permalink]) }
        else
          valid_items.filter { purchase.offer_code.products.find_by(unique_permalink: _1[:permalink]).present? }
        end
        if valid_items.map { _1[:price_cents].to_i }.sum < purchase.offer_code.minimum_amount_cents
          raise Purchase::PurchaseInvalid, "Sorry, you have not met the offer code's minimum amount."
        end
      end

      if params[:accepted_offer].present?
        upsell = Upsell.available_to_customers.find_by_external_id(params[:accepted_offer][:id])
        raise Purchase::PurchaseInvalid, "Sorry, this offer is no longer available." unless upsell.present?
        if upsell.cross_sell?
          if upsell.not_replace_selected_products?
            cart_product_permalinks = params[:cart_items].reject { _1[:permalink] == product.unique_permalink }.map { _1[:permalink] }
            if upsell.not_is_content_upsell? && (upsell.universal ? product.user.products : upsell.selected_products).where(unique_permalink: cart_product_permalinks).empty?
              raise Purchase::PurchaseInvalid, "The cart does not have any products to which the upsell applies."
            end
          end

          # The original discount is retained if it is better than the upsell
          # discount. The client can't automatically set the upsell discount
          # because it doesn't have a "code". Thus, upsell discount should only
          # be applied when the purchase does not already have a discount code.
          if purchase.offer_code.blank? && upsell.offer_code&.evaluate_for_buyer(buyer, product: purchase.link).present? &&
             (!params[:is_purchasing_power_parity_discounted] || perceived_price_matches_accepted_offer?(upsell.offer_code))
            purchase.offer_code = upsell.offer_code
          end
        end
        purchase.build_upsell_purchase(
          upsell:,
          selected_product: Link.find_by_external_id(params[:accepted_offer][:original_product_id]),
          upsell_variant: params[:accepted_offer][:original_variant_id].present? ?
            upsell.upsell_variants.alive.find_by(
              selected_variant: BaseVariant.find_by_external_id(params[:accepted_offer][:original_variant_id])

View on GitHub (pinned to afeacbd394)

Solutions

  1. Reload the checkout page and re-accept the upsell so the accepted offer matches the current cart.
  2. Before submitting, confirm the cart contains at least one other product in the upsell's scope (its selected_products, or any product by the same seller when the upsell is universal).
  3. If you administer the seller's account, verify the cross-sell upsell's 'applies to' product selection still matches what buyers actually have in their carts.

Example fix

# before: accepted_offer submitted even after the eligible item was removed from the cart
params[:accepted_offer] = { id: upsell.external_id } # cart no longer has any product the upsell applies to

# after: submit the accepted offer only while it still applies to the cart
cart_permalinks = cart_items.reject { _1[:permalink] == product.unique_permalink }.map { _1[:permalink] }
scope = upsell.universal ? product.user.products : upsell.selected_products
params[:accepted_offer] = scope.where(unique_permalink: cart_permalinks).exists? ? { id: upsell.external_id } : nil
Defensive patterns

Strategy: validation

Validate before calling

if params[:accepted_offer].present?
  upsell = Upsell.available_to_customers.find_by_external_id(params[:accepted_offer][:id])
  if upsell&.cross_sell? && upsell.not_replace_selected_products? && upsell.not_is_content_upsell?
    cart_permalinks = params[:cart_items].reject { _1[:permalink] == product.unique_permalink }.map { _1[:permalink] }
    scope = upsell.universal ? product.user.products : upsell.selected_products
    params[:accepted_offer] = nil if scope.where(unique_permalink: cart_permalinks).empty?
  end
end

Type guard

def upsell_applies_to_cart?(upsell, cart_items, product)
  return true unless upsell.cross_sell? && upsell.not_replace_selected_products? && upsell.not_is_content_upsell?
  permalinks = cart_items.reject { _1[:permalink] == product.unique_permalink }.map { _1[:permalink] }
  scope = upsell.universal ? product.user.products : upsell.selected_products
  scope.where(unique_permalink: permalinks).exists?
end

Try / catch

begin
  purchase, error = Purchase::CreateService.new(product:, params:, buyer:).perform
rescue Purchase::PurchaseInvalid => e
  # user-facing message: revalidate upsell applicability against the current cart, then prompt refresh
  render_checkout_error(e.message) if e.message == 'The cart does not have any products to which the upsell applies.'
end

Prevention

When it happens

Trigger: POST checkout with params[:accepted_offer][:id] naming an Upsell that is cross_sell?, has not_replace_selected_products?, and is not a content upsell, where params[:cart_items] (minus the current product's unique_permalink) contains no permalink matching (upsell.universal ? product.user.products : upsell.selected_products).

Common situations: Buyer removes the only eligible product from a multi-item cart but the accepted offer captured earlier is still submitted; seller edits an upsell's selected products or universal flag while buyers sit on stale checkout pages; API integrations that replay a stored accepted_offer against a rebuilt cart.

Related errors


AI-assisted analysis of antiwork/gumroad@afeacbd394 (2026-08-21). Data as JSON: /api/errors/390d76c827ce231e. Report an issue: GitHub.