antiwork/gumroad · error · Purchase::PurchaseInvalid

Invalid free trial information provided. Please try again.

Error message

Invalid free trial information provided. Please try again.

What it means

For a product with free_trial_enabled?, the checkout must echo the trial terms back: params[:perceived_free_trial_duration] must carry both :amount and :unit. Missing or incomplete params raise 'Invalid free trial information provided' so a trial purchase cannot be created from ambiguous terms. Gift purchases skip this validation entirely.

Source

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

    end

    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

View on GitHub (pinned to afeacbd394)

Solutions

  1. Refresh the checkout page to pick up the product's current trial terms.
  2. Integrators: always send perceived_free_trial_duration: { amount:, unit: } mirroring the product's free_trial_duration_amount/unit.
  3. Re-fetch product state immediately before submit and reconcile the params.

Example fix

# before: trial terms not echoed
params[:perceived_free_trial_duration] = nil # product.free_trial_enabled?

# after: echo the product's current terms
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[:gift].blank?
  trial = params[:perceived_free_trial_duration]
  params[:perceived_free_trial_duration] = { amount: product.free_trial_duration_amount, unit: product.free_trial_duration_unit } if trial.blank? || trial[:amount].blank? || trial[:unit].blank?
end

Type guard

def complete_trial_params?(trial_params)
  trial_params.present? && trial_params[:amount].present? && trial_params[:unit].present?
end

Try / catch

begin
  Purchase::CreateService.new(product:, params:, buyer:).perform
rescue Purchase::PurchaseInvalid => e
  # refresh flow: re-fetch product and rebuild trial params, then let the buyer resubmit
  redirect_to_checkout if e.message == 'Invalid free trial information provided. Please try again.'
end

Prevention

When it happens

Trigger: product.free_trial_enabled? true, not a gift, and params[:perceived_free_trial_duration] is nil, or its :amount or :unit key is missing/blank.

Common situations: Checkout page loaded before the seller enabled the trial (stale state); API integrations that omit the trial echo fields; client sending only one of the two fields.

Related errors


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