antiwork/gumroad · error · Purchase::PurchaseInvalid

Tip is not allowed for this product

Error message

Tip is not allowed for this product

What it means

Raised when the checkout submits a positive tip (params[:tip_cents] present and > 0) on a purchase that cannot accept one: either the seller has not enabled tipping (seller.tipping_enabled?, a per-seller checkout setting) or the product is a tiered membership (product.not_is_tiered_membership? is false). It stops Gumroad from collecting a tip the seller/product does not support.

Source

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

          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])
            ) :
            nil
        )
        raise Purchase::PurchaseInvalid, purchase.upsell_purchase.errors.first.message unless purchase.upsell_purchase.valid?
      end

      if params[:tip_cents].present? && params[:tip_cents] > 0
        raise Purchase::PurchaseInvalid, "Tip is not allowed for this product" unless purchase.seller.tipping_enabled? && product.not_is_tiered_membership?

        raise Purchase::PurchaseInvalid, "Tip is too large for this purchase" if (purchase_params[:perceived_price_cents].ceil - params[:tip_cents].floor) < purchase.minimum_paid_price_cents

        purchase.build_tip(value_cents: params[:tip_cents], value_usd_cents: get_usd_cents(product.price_currency_type, params[:tip_cents]))
      end

      validate_bundle_products

      purchase.prepare_for_charge!(locked_rate: buyer_currency_quote_rate_hint(purchase))

      purchase.build_purchase_wallet_type(wallet_type: params[:wallet_type]) if params[:wallet_type].present?

      payment_flow_attributes = PurchasePaymentFlow.attributes_for_checkout_params(params)
      # The purchase may still be unsaved here: `prepare_for_charge!` leaves it
      # unpersisted when a validation fails (for example, an invalid email).
      # Creating the dependent analytics row on an unsaved parent raises
      # ActiveRecord::RecordNotSaved, and a purchase that never saved has no
      # payment flow worth recording anyway.

View on GitHub (pinned to afeacbd394)

Solutions

  1. Do not send tip_cents (or send 0) for this checkout.
  2. Seller: enable tipping in checkout settings if tips should be collected.
  3. Integrators: render the tip input only when seller.tipping_enabled? is true and the product is not a tiered membership.

Example fix

# before: tip submitted unconditionally
params[:tip_cents] = 200

# after: tip only when the purchase allows it
params[:tip_cents] = (seller.tipping_enabled? && !product.is_tiered_membership?) ? 200 : 0
Defensive patterns

Strategy: validation

Validate before calling

tip_allowed = seller.tipping_enabled? && product.not_is_tiered_membership?
params[:tip_cents] = 0 if params[:tip_cents].to_i > 0 && !tip_allowed

Type guard

def tip_allowed?(seller, product)
  seller.tipping_enabled? && product.not_is_tiered_membership?
end

Try / catch

begin
  Purchase::CreateService.new(product:, params:, buyer:).perform
rescue Purchase::PurchaseInvalid => e
  if e.message == 'Tip is not allowed for this product'
    params[:tip_cents] = 0 # resubmit without the tip
  end
end

Prevention

When it happens

Trigger: params[:tip_cents] > 0 together with purchase.seller.tipping_enabled? == false, OR product.is_tiered_membership? == true, in the same Purchase::CreateService#perform call.

Common situations: A custom or embedded checkout that always includes tip_cents; a buyer holding a stale page open while the seller turns tipping off; tipping UI incorrectly rendered on membership-tier products.

Related errors


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