antiwork/gumroad · error · Subscription::UpdateFailed

error_message (from get_chargeable, charge-processor card er

Error message

error_message (from get_chargeable, charge-processor card errors)

What it means

Raised as Subscription::UpdateFailed when Subscription::UpdaterService.perform cannot build a chargeable from the submitted card data: get_chargeable returns an error string instead of a chargeable object. The string comes from CardParamsHelper.check_for_errors (mapped through PurchaseErrorCode.customer_error_message for buyer-card errors, otherwise the generic temporary-problem message) or from build_chargeable returning nil ("We couldn't charge your card. Try again or use a different card."). perform rescues it and returns { success: false, error_message: }, so callers see a failed result, not an exception.

Source

Thrown at app/services/subscription/updater_service.rb:117

    begin
      ActiveRecord::Base.transaction do
        # Update subscription contact info
        if params[:contact_info].present?
          params[:contact_info][:country] = ISO3166::Country[params[:contact_info][:country]]&.common_name
          original_purchase.is_updated_original_subscription_purchase = true
          original_purchase.update!(params[:contact_info])
        end

        # Update card if necessary
        unless use_existing_card?
          had_saved_card = subscription.credit_card.present?

          # (a) Get chargeable. Return if error
          error_message = get_chargeable
          if error_message.present?
            logger.info("SubscriptionUpdater: Error fetching chargeable for subscription #{subscription.external_id}: #{error_message}")
            raise Subscription::UpdateFailed, error_message
          end

          # (b) Create new credit card. Return if error.
          replacement_card = CreditCard.create(chargeable, card_data_handling_mode, logged_in_user)

          unless replacement_card.errors.empty?
            logger.info("SubscriptionUpdater: Error creating new credit card for subscription #{subscription.external_id}: #{replacement_card.errors.full_messages}")
            raise Subscription::UpdateFailed, replacement_card.errors.messages[:base].first
          end

          if indian_card_mandate_validation_required?(replacement_card)
            # A plan update builds its replacement purchase before mandate validation. Keep the
            # new card available for that build, but persist it only after validation succeeds.
            subscription.credit_card = replacement_card
          else
            associate_replacement_card!(replacement_card, had_saved_card:, **validate_indian_card_mandate!(replacement_card))
            replacement_card = nil
          end

View on GitHub (pinned to afeacbd394)

Solutions

  1. Have the buyer re-enter card details and resubmit so a fresh, unconsumed card token is produced.
  2. Grep the Rails log for "SubscriptionUpdater: Error building chargeable" with the subscription external_id — it includes the raw error_message and card_error_code identifying the failing param.
  3. Verify the request actually carries the card fields the checkout flow captured and that params[:use_existing_card] matches intent (false means new card data is required).
  4. If the non-card-error branch fired ("There is a temporary problem..."), check charge processor configuration/credentials and retry once healthy.

Example fix

# before: params from a stale form, token already consumed
result = Subscription::UpdaterService.new(subscription:, params:, logged_in_user:, gumroad_guid:, remote_ip:).perform
# => { success: false, error_message: "We couldn't charge your card. Try again or use a different card." }

# after: always send the card fields the form just captured
params.merge!(use_existing_card: false) # plus fresh encrypted card / stripe token fields
result = Subscription::UpdaterService.new(subscription:, params:, logged_in_user:, gumroad_guid:, remote_ip:).perform
return render_error(result[:error_message]) unless result[:success]
Defensive patterns

Strategy: validation

Validate before calling

# Fail fast before invoking the updater when new-card data is requested but absent
use_existing = ActiveModel::Type::Boolean.new.cast(params[:use_existing_card])
if !use_existing && CardParamsHelper.build_chargeable(params.merge(product_permalink: subscription.link.unique_permalink)).nil?
  return { success: false, error_message: "We couldn't charge your card. Try again or use a different card." }
end

Try / catch

# perform() already rescues Subscription::UpdateFailed into the result hash
result = Subscription::UpdaterService.new(subscription:, params:, logged_in_user:, gumroad_guid:, remote_ip:).perform
unless result[:success]
  render json: { success: false, error: result[:error_message] }, status: :unprocessable_entity
end

Prevention

When it happens

Trigger: Updating or restarting a membership with params[:use_existing_card] false while the request lacks valid card data: a missing or already-consumed Stripe single-use token, stale encrypted card fields, a PayPal billing agreement id the helper rejects, or any card_data_handling_error carrying a card_error_code. Also when build_chargeable(params.merge(product_permalink: ...)) returns nil because no usable payment token is present in params.

Common situations: Checkout form submitted long after the card token was minted (expired/consumed), stale page after a Stripe key rotation, integration code forgetting to forward the encrypted card params, or test environments without valid charge processor credentials producing the temporary-problem branch.

Related errors


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