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
Raised as ChargeProcessorCardError (code upi_recurring_authorization_required) by Purchase#fail_upi_recurring_authorization! on the renewal path (create_charge_intent for subscriptions). It fires before Stripe is called when a UPI Autopay renewal fails a pre-submit reason (e.g. mandate cap, mandate state, currency conditions in mandate cap conversion), with the same buyer-facing 'update your payment method' message. The purchase id and reason are sent to ErrorNotifier as "UPI Autopay renewal rejected before Stripe submit".
Source
Thrown at app/models/purchase.rb:4836
purchases: [self],
amount_cents: total_transaction_cents,
gumroad_amount_cents: total_transaction_amount_for_gumroad_cents,
required_currency:,
**required_currency_errors
).perform
return {} if result.blank?
{
processor_amount_cents: result.processor_amount_cents,
processor_currency: result.processor_currency,
processor_gumroad_amount_cents: result.processor_gumroad_amount_cents,
stripe_fx_quote_id: result.stripe_fx_quote_id,
}
end
def fail_upi_recurring_authorization!(reason)
ErrorNotifier.notify("UPI Autopay renewal rejected before Stripe submit", reason:, purchase_id: id)
raise ChargeProcessorCardError.new(
PurchaseErrorCode::UPI_RECURRING_AUTHORIZATION_REQUIRED,
StripeChargeProcessor::UPI_PAYMENT_METHOD_UPDATE_MESSAGE
)
end
def defer_upi_recurring_renewal!(reason)
ErrorNotifier.notify("UPI Autopay renewal deferred before Stripe submit", reason:, purchase_id: id)
raise ChargeProcessorUnavailableError, "UPI Autopay renewals are temporarily paused"
end
# Converts the RBI e-mandate cap into the currency this charge will actually settle in. See
# Charge::CreateService#mandate_options_in_charge_currency for the full reasoning; this is the
# renewal-path counterpart, kept deliberately identical in behaviour.
def mandate_options_in_charge_currency(mandate_options, presentment_args, canonical_amount_cents)
return mandate_options if mandate_options.blank?
presentment_currency = presentment_args[:processor_currency]
return mandate_options if presentment_currency.blank? || presentment_currency == Currency::USDView on GitHub (pinned to afeacbd394)
Solutions
- Notify the buyer to update/re-authorize their payment method — the saved UPI mandate can no longer be charged; the error is by design terminal for that method.
- Check the ErrorNotifier payload (reason, purchase_id) to distinguish cap-exceeded from mandate-inactive; a cap breach may instead warrant plan adjustment (lower renewal amount) before re-authorization.
- Verify the mandate cap conversion (see the comment on converting the RBI e-mandate cap into the settlement currency) is using the current FX quote — a stale rate can wrongly classify a renewal as over-cap.
- If the reason looks transient (quote/currency related), confirm the code chose fail_upi_recurring_authorization! rather than defer_upi_recurring_renewal! — the latter pauses and retries rather than failing the member.
Example fix
# before: billing job treats every renewal failure alike
begin
purchase.create_charge_intent(chargeable)
rescue ChargeProcessorCardError
purchase.mark_failed
end
# after: branch on the UPI code to trigger re-auth flow
begin
purchase.create_charge_intent(chargeable)
rescue ChargeProcessorCardError => e
if e.error_code == PurchaseErrorCode::UPI_RECURRING_AUTHORIZATION_REQUIRED
SubscriptionPaymentMethodUpdateMailer.notify(purchase.subscription)
else
purchase.mark_failed
end
end Defensive patterns
Strategy: try-catch
Validate before calling
# before scheduling a UPI renewal, check the mandate's usability signal you already store if subscription.saved_upi? && !subscription.upi_mandate_usable? PaymentMethodUpdateMailer.notify(subscription) next end
Type guard
def upi_renewal_chargeable?(purchase)
chargeable = purchase.stripe_chargeable
chargeable.nil? || !chargeable.respond_to?(:recurring_authorization_verified_at) ||
chargeable.recurring_authorization_verified_at.present?
end Try / catch
begin
purchase.create_charge_intent(chargeable)
rescue ChargeProcessorCardError => e
case e.error_code
when PurchaseErrorCode::UPI_RECURRING_AUTHORIZATION_REQUIRED
subscription.notify_buyer_reauth_required # terminal: do not retry with same method
else
raise
end
end Prevention
- Track mandate state changes (revocation notifications) and proactively email buyers before the renewal date.
- Keep renewal amounts within the registered RBI mandate cap; convert the cap in the settlement currency with a fresh quote.
- Distinguish terminal fail_upi_recurring_authorization! from transient defer_upi_recurring_renewal! in alerting — only the former should page.
When it happens
Trigger: A subscription renewal (Purchase#create_charge_intent) whose saved chargeable is UPI hits a rejection reason: RBI e-mandate amount cap would be exceeded, the mandate is no longer active, or the converted cap in the charge currency makes the renewal non-compliant. defer_upi_recurring_renewal! is the transient sibling (raises ChargeProcessorUnavailableError); this one is the terminal branch.
Common situations: Indian Rupee subscriptions on UPI Autopay at renewal time: buyer's mandate hit the RBI per-transaction or per-period cap; mandate revoked in the buyer's UPI app; amount or currency of the renewal drifted from what the mandate authorizes; sandbox mandates without real caps.
Related errors
- india_card_mandate_missing
- upi_recurring_authorization_required
- upi_recurring_authorization_required
- india_card_mandate_missing
- upi_recurring_authorization_required
AI-assisted analysis of antiwork/gumroad@afeacbd394 (2026-08-21).
Data as JSON: /api/errors/58a19b21b8d236a3.
Report an issue: GitHub.