antiwork/gumroad · error · Subscription::UpdateFailed

error_message (purchase charge error)

Error message

error_message (purchase charge error)

What it means

In charge_user!, after subscription.charge! for a prorated upgrade or restart charge, the purchase is neither successful nor in-progress-awaiting-card-action. error_message is taken from upgrade_purchase.errors.full_messages.first or upgrade_purchase.error_code and raised as Subscription::UpdateFailed (rescued into { success: false, error_message: }). If this was a resubscribe, the subscription is also unsubscribed and failed via unsubscribe_and_fail!.

Source

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

        subscription.original_purchase.schedule_workflows_for_variants(excluded_variants: original_purchase.variant_attributes) unless same_variants?
        {
          success: true,
          next: logged_in_user && Rails.application.routes.url_helpers.library_purchase_url(upgrade_purchase.external_id, host: "#{PROTOCOL}://#{DOMAIN}"),
          success_message:,
        }
      elsif upgrade_purchase.in_progress? && upgrade_purchase.charge_intent&.requires_action?
        {
          success: true,
          requires_card_action: true,
          client_secret: upgrade_purchase.charge_intent.client_secret,
          purchase: {
            id: upgrade_purchase.secure_external_id(scope: "confirm", expires_at: 1.hour.from_now),
            stripe_connect_account_id: upgrade_purchase.merchant_account.is_a_stripe_connect_account? ? upgrade_purchase.merchant_account.charge_processor_merchant_id : nil
          }
        }
      else
        logger.info("SubscriptionUpdater: Error charging user for subscription #{subscription.external_id}: #{error_message}")
        raise Subscription::UpdateFailed, error_message
      end
    end

    def send_subscription_updated_api_notification
      return if api_notification_sent
      return unless tiered_membership?
      return if same_plan_and_price?
      unless new_purchase.present?
        ErrorNotifier.notify("SubscriptionUpdater: new_purchase missing when sending API notification")
        return
      end

      self.api_notification_sent = true
      subscription.send_updated_notifification_webhook(
        plan_change_type: downgrade? ? "downgrade" : "upgrade",
        effective_as_of: (downgrade? && !apply_plan_change_immediately?) ? subscription.end_time_of_last_paid_period : new_purchase.created_at,
        old_recurrence: original_recurrence,
        new_recurrence: price.recurrence,

View on GitHub (pinned to afeacbd394)

Solutions

  1. Show error_message to the buyer; for declines, prompt for a different payment method (the failed-charge-email retry flow exists for this).
  2. Classify via upgrade_purchase.error_code in the DB/logs: buyer declines vs processor errors need different handling.
  3. If the code indicates required authentication, complete the card-action flow (the requires_card_action result branch) instead of re-charging.
  4. For genuine processor errors, retry after the incident resolves.
Defensive patterns

Strategy: fallback

Try / catch

result = Subscription::UpdaterService.new(...).perform
if result[:success] && result[:requires_card_action]
  complete_3ds(result[:client_secret], result[:purchase][:id]) # finish SCA, then confirm
elsif !result[:success]
  offer_different_payment_method(result[:error_message]) # decline codes are not retryable with the same card
end

Prevention

When it happens

Trigger: Card declined or insufficient funds on the prorated/restart charge; processor risk block; expired saved card charged off_session; charge intent requiring SCA when the flow could not surface card action; processor error during the charge.

Common situations: Buyer with an expired card upgrading tiers after failed renewals; retry flow triggered from a failed-charge email (params[:declined]); Stripe/PayPal risk rules on unusual amounts; off_session charges on cards requiring authentication.

Related errors


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