antiwork/gumroad · error · ChargeProcessorCardError

upi_recurring_authorization_required

upi_recurring_authorization_required

Error message

Your saved UPI payment method can no longer be used. Please update your payment method to continue your membership.

What it means

ChargeProcessorCardError with PurchaseErrorCode::UPI_RECURRING_AUTHORIZATION_REQUIRED, raised by validate_upi_autopay_charge! before any Stripe call for UPI Autopay renewals. UPI exposes no reusable Mandate id (Stripe selects it from Customer + PaymentMethod), so the stored authorization is validated instead: the charge must be off-session, in INR, on a verified authorization whose stored currency is INR and whose max amount is positive, for an amount within both the stored cap and Stripe's UPI recurring limit (Checkout::PaymentMethodResolver::UPI_RECURRING_MAX_INR_CENTS). Any violation reports the precise reason via ErrorNotifier and raises with the buyer-facing 'update your payment method' message (UPI_PAYMENT_METHOD_UPDATE_MESSAGE).

Source

Thrown at app/business/payments/charging/implementations/stripe/stripe_charge_processor.rb:1434

        if !off_session
          "charge was not off-session"
        elsif currency.to_s.downcase != Currency::INR
          "charge currency was #{currency.inspect}"
        elsif !chargeable.recurring_authorization_verified?
          "authorization was not verified"
        elsif chargeable.recurring_authorization_currency.to_s.downcase != Currency::INR
          "stored authorization currency was #{chargeable.recurring_authorization_currency.inspect}"
        elsif chargeable.recurring_authorization_max_amount_cents.to_i <= 0
          "stored authorization maximum was missing"
        elsif amount_cents > chargeable.recurring_authorization_max_amount_cents.to_i
          "charge amount #{amount_cents} exceeded stored maximum #{chargeable.recurring_authorization_max_amount_cents}"
        elsif amount_cents > Checkout::PaymentMethodResolver::UPI_RECURRING_MAX_INR_CENTS
          "charge amount #{amount_cents} exceeded Stripe's UPI recurring limit"
        end
      return if reason.nil?

      ErrorNotifier.notify("UPI Autopay renewal rejected before Stripe submit", reason:)
      raise ChargeProcessorCardError.new(PurchaseErrorCode::UPI_RECURRING_AUTHORIZATION_REQUIRED, UPI_PAYMENT_METHOD_UPDATE_MESSAGE)
    end

    def get_mandate_id_from_chargeable(chargeable, merchant_account)
      if chargeable.stripe_setup_intent_id
        setup_intent = if merchant_migrated?(merchant_account)
          Stripe::SetupIntent.retrieve(chargeable.stripe_setup_intent_id, { stripe_account: merchant_account.charge_processor_merchant_id })
        else
          Stripe::SetupIntent.retrieve(chargeable.stripe_setup_intent_id)
        end
        setup_intent.mandate
      elsif chargeable.stripe_payment_intent_id
        original_payment_intent = if merchant_migrated?(merchant_account)
          Stripe::PaymentIntent.retrieve(chargeable.stripe_payment_intent_id, { stripe_account: merchant_account.charge_processor_merchant_id })
        else
          Stripe::PaymentIntent.retrieve(chargeable.stripe_payment_intent_id)
        end
        original_charge = if merchant_migrated?(merchant_account)
          Stripe::Charge.retrieve(original_payment_intent.latest_charge, { stripe_account: merchant_account.charge_processor_merchant_id })

View on GitHub (pinned to afeacbd394)

Solutions

  1. Ask the buyer to update their payment method — re-authorization creates a fresh mandate with a current cap.
  2. Identify which invariant fired: the ErrorNotifier report's reason string names it exactly (not verified / cap missing / over stored max / over Stripe limit).
  3. For price increases, run a re-authorization flow instead of charging above the stored cap.
  4. Verify recurring_authorization_* fields are persisted at UPI setup time and backfill where possible.
  5. Confirm the renewal amount is within Stripe's UPI recurring limit before scheduling the charge.

Example fix

# before: schedule the renewal at the new (higher) price
SubscriptionChargeJob.perform_later(subscription)
# after: only schedule when the stored UPI authorization covers the amount
if payment_method_upi?
  max_cents = chargeable.recurring_authorization_max_amount_cents.to_i
  if max_cents <= 0 || renewal_amount_cents > max_cents
    require_upi_reauthorization!(subscription)
  else
    SubscriptionChargeJob.perform_later(subscription)
  end
end
Defensive patterns

Strategy: validation

Validate before calling

# Ruby, before charging a UPI renewal
verified = chargeable.recurring_authorization_verified?
within_stored_cap = renewal_amount_cents <= chargeable.recurring_authorization_max_amount_cents.to_i
within_stripe_limit = renewal_amount_cents <= Checkout::PaymentMethodResolver::UPI_RECURRING_MAX_INR_CENTS
inr = currency.to_s.downcase == "inr"

if verified && within_stored_cap && within_stripe_limit && inr && off_session
  process_charge(...)
else
  require_upi_reauthorization!(subscription)
end

Try / catch

begin
  process_charge(...)
rescue ChargeProcessorCardError => e
  if e.code == PurchaseErrorCode::UPI_RECURRING_AUTHORIZATION_REQUIRED
    notify_buyer_to_update_payment_method(purchase) # re-auth creates a fresh mandate with a current cap
  else
    raise
  end
end

Prevention

When it happens

Trigger: A membership renewal on a saved UPI method where: the stored authorization was never verified, recurring_authorization_max_amount_cents is 0/missing, the renewal amount exceeds the mandate's stored cap (e.g. a price increase), the amount exceeds Stripe's UPI recurring limit, the charge is not off-session, or the currency isn't INR.

Common situations: Seller raises a subscription price above the buyer's original UPI mandate cap; buyer cancelled the autopay mandate in their UPI app; legacy UPI subscriptions saved before authorization fields were persisted; currency drift away from INR.

Related errors


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