{"record":{"id":"c7e601777a461037","repo":"antiwork/gumroad","slug":"purchase-price-is-invalid-please-check-the-price","errorCode":null,"errorMessage":"Purchase price is invalid. Please check the price.","messagePattern":"Purchase price is invalid\\. Please check the price\\.","errorType":"exception","errorClass":"Purchase::PurchaseInvalid","httpStatus":null,"severity":"error","filePath":"app/services/purchase/create_service.rb","lineNumber":347,"sourceCode":"      raise Purchase::PurchaseInvalid, \"You can't gift your own product. To give it away for free, create a 100% off discount code under Checkout > Discounts and share the checkout link.\" if buyer == product.user\n      raise Purchase::PurchaseInvalid, \"You cannot gift a product to yourself. Please try gifting to another email.\" if giftee_email == purchase_params[:email]\n      raise Purchase::PurchaseInvalid, \"Gift purchases cannot be on installment plans.\" if params[:pay_in_installments]\n\n      if product.can_gift?\n        gift = product.gifts.build(giftee_email:, gift_note: gift_params[:gift_note], gifter_email: params[:purchase][:email], is_recipient_hidden: gift_params[:giftee_email].blank?)\n        error_message = gift.save ? nil : gift.errors.full_messages[0]\n        raise Purchase::PurchaseInvalid, error_message if error_message.present?\n\n        gift\n      else\n        error_message = product.user.gifting_disabled? ? \"The creator has disabled gifting for their products.\" : \"Gifting is not yet enabled for pre-orders.\"\n        raise Purchase::PurchaseInvalid, error_message\n      end\n    end\n\n    def validate_perceived_price\n      if purchase_params[:perceived_price_cents] && !Purchase::MAX_PRICE_RANGE.cover?(purchase_params[:perceived_price_cents])\n        raise Purchase::PurchaseInvalid, \"Purchase price is invalid. Please check the price.\"\n      end\n    end\n\n    def validate_zip_code\n      country_code_for_validation = purchase_params[:country].presence || purchase_params[:sales_tax_country_code_election]\n\n      if purchase_params[:perceived_price_cents].to_i > 0 && country_code_for_validation == Compliance::Countries::USA.alpha2 && UsZipCodes.identify_state_code(purchase_params[:zip_code]).nil?\n        Rails.logger.info(\"Zip code #{purchase_params[:zip_code]} is invalid, customer email #{purchase_params[:email]}\")\n        raise Purchase::PurchaseInvalid, \"You entered a ZIP Code that doesn't exist within your country.\"\n      end\n    end\n\n    def perceived_price_matches_accepted_offer?(offer_code)\n      return false unless offer_code\n\n      original_offer_code = purchase.offer_code\n      purchase.offer_code = offer_code\n      purchase.minimum_paid_price_cents + params[:tip_cents].to_i == purchase_params[:perceived_price_cents].to_i","sourceCodeStart":329,"sourceCodeEnd":365,"githubUrl":"https://github.com/antiwork/gumroad/blob/afeacbd394069a1cbf0c6c50ee8e900925050370/app/services/purchase/create_service.rb#L329-L365","documentation":"validate_perceived_price raises when perceived_price_cents falls outside Purchase::MAX_PRICE_RANGE (-2_147_483_647..2_147_483_647, the signed 32-bit integer bounds of the price column, app/models/purchase.rb:51). Values beyond that cannot be stored, so checkout aborts before the purchase is built.","triggerScenarios":"params[:purchase][:perceived_price_cents] is non-nil and outside the int32 range - typically dollars converted to cents twice, float multiplication overflow, or a tampered request probing the bounds.","commonSituations":"Client multiplies a dollar amount by 100 twice; price parsed as dollars in one layer and cents in another; PWYW inputs accepted without an upper bound; automated requests submitting extreme values.","solutions":["Send integer cents, converted from the decimal amount exactly once.","Validate or clamp the client input to the int32 range before submit.","If a legitimate charge truly exceeds the range, it exceeds what Gumroad can price - split it or contact support."],"exampleFix":"# before: cents conversion applied twice\nparams[:purchase][:perceived_price_cents] = (price_dollars * 100 * 100).to_i\n\n# after: convert to integer cents exactly once\nparams[:purchase][:perceived_price_cents] = (price_dollars * 100).to_i","handlingStrategy":"validation","validationCode":"price_cents = purchase_params[:perceived_price_cents].to_i\nraise ArgumentError, 'perceived_price_cents out of range' unless Purchase::MAX_PRICE_RANGE.cover?(price_cents)","typeGuard":"def valid_price_cents?(cents)\n  cents.is_a?(Numeric) && Purchase::MAX_PRICE_RANGE.cover?(cents.to_i)\nend","tryCatchPattern":"begin\n  Purchase::CreateService.new(product:, params:, buyer:).perform\nrescue Purchase::PurchaseInvalid => e\n  # a range violation is a client bug - log it and reject, do not retry the same value\n  report_client_bug(e.message) if e.message == 'Purchase price is invalid. Please check the price.'\nend","preventionTips":["Convert dollars to integer cents exactly once, at the boundary.","Bound pay-what-you-want inputs to a sane maximum well below int32.","Never round-trip prices through floats; keep cents as integers end to end."],"tags":["price-validation","integer-overflow","int32","checkout","gumroad"],"backgroundTag":"price-out-of-range","analyzedSha":"afeacbd394069a1cbf0c6c50ee8e900925050370","analyzedAt":"2026-08-21T17:58:52.159Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}