antiwork/gumroad · error · Subscription::UpdateFailed
We could not verify this card for recurring payments. Please
Error message
We could not verify this card for recurring payments. Please try the card again or use a different payment method.
What it means
For Indian cards under the india_card_mandate_reliability feature, validate_indian_card_mandate! requires an active Stripe mandate whose SetupIntent binds to the same Stripe customer and payment method, carries this subscription's external_id in metadata, and whose card_mandate_options exactly match the recomputed terms (amount_type maximum, amount, currency, reference encoding the subscription id, interval, interval_count, supported_types including "india"). Any miss — including mandate status "missing" — raises UpdateFailed and fires an ErrorNotifier event with the subscription id and mandate_status.
Source
Thrown at app/services/subscription/updater_service.rb:346
merchant_account = subscription.renewal_merchant_account
setup_intent_id = credit_card.stripe_setup_intent_id
setup_intent = ChargeProcessor.get_setup_intent(merchant_account, setup_intent_id) if setup_intent_id.present?
mandate_id = setup_intent&.mandate if setup_intent&.succeeded?
mandate = ChargeProcessor.get_mandate(merchant_account, mandate_id) if mandate_id.present?
status = mandate&.status || "missing"
payment_method_id = credit_card.processor_payment_method_id
customer_matches = setup_intent&.customer_id == credit_card.stripe_customer_id
binding_matches = setup_intent&.payment_method_id == payment_method_id &&
StripeChargeProcessor.mandate_matches_payment_method?(mandate, payment_method_id)
terms_match = indian_card_setup_intent_terms_match?(setup_intent)
unless status == "active" && customer_matches && binding_matches && terms_match
ErrorNotifier.notify(
"Indian card update rejected without an active e-mandate",
subscription: subscription.external_id,
mandate_status: status
)
raise Subscription::UpdateFailed, "We could not verify this card for recurring payments. Please try the card again or use a different payment method."
end
stripe_chargeable = chargeable&.get_chargeable_for(StripeChargeProcessor.charge_processor_id)
stripe_chargeable.validated_stripe_mandate_id = mandate.id if stripe_chargeable.respond_to?(:validated_stripe_mandate_id=)
{ clear_mandate_stop: true, stripe_mandate_id: mandate.id }
rescue ChargeProcessorError => e
ErrorNotifier.notify(e, subscription: subscription.external_id)
raise Subscription::UpdateFailed, "We could not verify this card for recurring payments. Please try the card again or use a different payment method."
end
def indian_card_mandate_validation_required?(credit_card)
subscription.india_card_mandate_reliability_enabled? &&
credit_card.stripe_charge_processor? &&
credit_card.requires_mandate?
end
def indian_card_setup_intent_terms_match?(setup_intent)
return false unless setup_intent&.usage == "off_session"View on GitHub (pinned to afeacbd394)
Solutions
- Restart the card-update flow end to end so a fresh SetupIntent with full SCA/mandate confirmation is created for this subscription.
- Verify via the Stripe API that the mandate status is "active" and setup_intent.metadata.gumroad_subscription_id matches this subscription.
- If terms mismatch: confirm the price/discount submitted is the one the mandate was authorized for; redo the flow after any price or contact-info change.
- Check the ErrorNotifier event (subscription external_id + mandate_status) for the exact failing condition.
Defensive patterns
Strategy: validation
Validate before calling
# Pre-flight: require an active mandate bound to this card before persisting the update mandate_id = ChargeProcessor.get_setup_intent(merchant_account, card.stripe_setup_intent_id)&.mandate status = ChargeProcessor.get_mandate(merchant_account, mandate_id)&.status proceed = status == "active"
Try / catch
begin
result = Subscription::UpdaterService.new(...).perform
rescue Subscription::UpdateFailed => e
result = { success: false, error_message: e.message } # buyer redoes the mandate flow with a fresh SetupIntent
end Prevention
- Complete the full SCA/mandate confirmation before submitting the membership update — never submit with a half-finished setup intent.
- Never reuse a SetupIntent across subscriptions; metadata gumroad_subscription_id is verified per subscription.
- If price or billing info changes, redo the card update so the mandate terms match the recomputed amounts.
When it happens
Trigger: Buyer supplies a requires_mandate? Stripe card while the setup intent never succeeded or was canceled, references a different customer/payment method, its metadata gumroad_subscription_id belongs to another subscription, or the mandate terms don't match the server-recomputed terms (amount/currency/interval drift between setup intent creation and update submission, e.g. price changed or a mandate-rate stamp is absent on old intents).
Common situations: Buyer abandons the 3DS/mandate confirmation step or hits browser-back after SCA; mandate revoked in the Stripe dashboard; setup intent reused from a different membership; discount or billing-info change altering expected terms between steps.
Related errors
- india_card_mandate_missing
- We could not verify this card for recurring payments. Please
- upi_recurring_authorization_required
- upi_recurring_authorization_required
- upi_recurring_authorization_required
AI-assisted analysis of antiwork/gumroad@afeacbd394 (2026-08-21).
Data as JSON: /api/errors/d7e4cff7a52fbd84.
Report an issue: GitHub.