antiwork/gumroad · error · Purchase::PurchaseInvalid

Something went wrong. Please refresh the page to pre-order t

Error message

Something went wrong. Please refresh the page to pre-order the product.

What it means

In Purchase::CreateService, a purchase for a product still in preorder state must be created as a preorder authorization (purchase.is_preorder_authorization, set by the preorder checkout form/token). If the flag is absent while product.is_in_preorder_state? is true, Purchase::PurchaseInvalid is raised — per the code comment, 'This should never happen unless the request is tampered with': the preorder params were altered, dropped, or built by a client that skipped the preorder flow.

Source

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

        )
      elsif @product.product_refund_policy_enabled? && @product.product_refund_policy.present?
        # The enabled flag can be out of sync with the underlying record (the
        # ProductRefundPolicy row may have been deleted or never created), so we only
        # attach a purchase refund policy when the record actually exists.
        purchase.build_purchase_refund_policy(
          max_refund_period_in_days: @product.product_refund_policy.max_refund_period_in_days,
          title: @product.product_refund_policy.title,
          fine_print: @product.product_refund_policy.fine_print
        )
      end

      # build pre-order if purchase is for pre-order product & return
      if purchase.is_preorder_authorization
        build_preorder(locked_rate: buyer_currency_quote_rate_hint(purchase))
        return purchase, nil
      elsif product.is_in_preorder_state?
        # This should never happen unless the request is tampered with:
        raise Purchase::PurchaseInvalid, "Something went wrong. Please refresh the page to pre-order the product."
      end

      purchase.is_commission_deposit_purchase = product.native_type == Link::NATIVE_TYPE_COMMISSION

      # associate correct price for membership product
      if product.is_recurring_billing || purchase.is_installment_payment
        # For membership products, params[:price_id] should be provided but if
        # not, or if a price_id is invalid, associate the default price.
        price = params[:price_id].present? ?
          product.prices.alive.find_by_external_id(params[:price_id]) :
          product.default_price

        purchase.price = price || product.default_price

        # Check for existing subscriptions (active or restartable)
        if should_check_for_restartable_subscription?
          existing_purchase, error, sca_response = handle_existing_subscription
          return nil, nil, sca_response if sca_response.present?

View on GitHub (pinned to afeacbd394)

Solutions

  1. Reload the product page and start checkout fresh so the preorder form/token is regenerated
  2. If scripting, GET the checkout page/props first and forward the preorder params untouched
  3. Never modify or drop the is_preorder_authorization / preorder form fields
  4. Sellers: verify the product's preorder state matches what the checkout page shows before debugging further
Defensive patterns

Strategy: try-catch

Validate before calling

# Client-side: verify the product's preorder state and fetch a fresh form
def fresh_checkout_state?(product)
  loaded = Product.find_by(unique_permalink: product.unique_permalink)
  loaded.is_in_preorder_state? == form_preorder_fields_present?
end

Try / catch

begin
  Purchase::CreateService.new(user: buyer, params: purchase_params).perform
rescue Purchase::PurchaseInvalid => e
  if e.message.include?('pre-order')
    redirect_to product_path(product) # restart checkout from a fresh page
  else
    render_error(e.message)
  end
end

Prevention

When it happens

Trigger: Modifying the checkout form/hidden fields (e.g. clearing the preorder authorization flag via devtools), a stale cached checkout page from before the product entered/left preorder, or a scripted POST to the purchase endpoint that omits the preorder authorization params.

Common situations: Browser extensions or scripts that rebuild forms; buyers with a long-open tab on a product that just went into preorder; custom automation that doesn't first fetch the live checkout payload.

Related errors


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