antiwork/gumroad · error · Purchase::PurchaseInvalid

The product's free trial has changed, please refresh the pag

Error message

The product's free trial has changed, please refresh the page!

What it means

The submitted trial terms no longer match the product: free_trial_params[:amount].to_i differs from product.free_trial_duration_amount, or the unit differs from product.free_trial_duration_unit. This is a stale-checkout guard - the seller changed the trial after the buyer loaded the page, and purchasing against outdated terms would misrepresent what the buyer agreed to.

Source

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

    def perceived_price_matches_accepted_offer?(offer_code)
      return false unless offer_code

      original_offer_code = purchase.offer_code
      purchase.offer_code = offer_code
      purchase.minimum_paid_price_cents + params[:tip_cents].to_i == purchase_params[:perceived_price_cents].to_i
    ensure
      purchase.offer_code = original_offer_code if purchase
    end

    def validate_perceived_free_trial_params
      return if is_gift?

      free_trial_params = params[:perceived_free_trial_duration]
      if product.free_trial_enabled?
        if !free_trial_params.present? || !free_trial_params[:amount].present? || !free_trial_params[:unit].present?
          raise Purchase::PurchaseInvalid, "Invalid free trial information provided. Please try again."
        elsif free_trial_params[:amount].to_i != product.free_trial_duration_amount || free_trial_params[:unit] != product.free_trial_duration_unit
          raise Purchase::PurchaseInvalid, "The product's free trial has changed, please refresh the page!"
        end
      elsif free_trial_params.present?
        raise Purchase::PurchaseInvalid, "Invalid free trial information provided. Please try again."
      end
    end

    def validate_bundle_products
      return unless product.is_bundle?

      product.bundle_products.alive.each do |bundle_product|
        if params[:bundle_products].none? { _1[:product_id] == bundle_product.product.external_id && _1[:variant_id] == bundle_product.variant&.external_id && _1[:quantity].to_i == bundle_product.quantity }
          raise Purchase::PurchaseInvalid, "The bundle's contents have changed. Please refresh the page!"
        end

        validate_bundle_component_inventory(bundle_product)
      end
    end

View on GitHub (pinned to afeacbd394)

Solutions

  1. Refresh the checkout page and resubmit with the updated terms.
  2. Integrators: re-fetch free_trial_duration_amount/unit at submit time and rebuild the params from it.
  3. Treat this raise as a signal to restart checkout, not to retry identical params.

Example fix

# before: cached trial terms submitted after the seller changed them
params[:perceived_free_trial_duration] = cached_trial # { amount: 7, unit: 'day' }, product now 14 days

# after: rebuild from current product state
params[:perceived_free_trial_duration] = { amount: product.free_trial_duration_amount, unit: product.free_trial_duration_unit }
Defensive patterns

Strategy: validation

Validate before calling

if product.free_trial_enabled?
  params[:perceived_free_trial_duration] = { amount: product.free_trial_duration_amount, unit: product.free_trial_duration_unit }
end

Type guard

def trial_params_match_product?(trial_params, product)
  trial_params[:amount].to_i == product.free_trial_duration_amount && trial_params[:unit] == product.free_trial_duration_unit
end

Try / catch

begin
  Purchase::CreateService.new(product:, params:, buyer:).perform
rescue Purchase::PurchaseInvalid => e
  # stale page: rebuild trial params from the product and restart checkout
  redirect_to_checkout if e.message.start_with?("The product's free trial has changed")
end

Prevention

When it happens

Trigger: Product has a trial, the request echoes a complete { amount:, unit: } hash, but amount.to_i != product.free_trial_duration_amount OR unit != product.free_trial_duration_unit.

Common situations: Seller edits trial duration/unit while buyers are mid-checkout; cached product JSON in custom checkout integrations; long-lived checkout tabs.

Related errors


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