antiwork/gumroad · error · ChargeProcessorCardError

india_card_mandate_missing

india_card_mandate_missing

Error message

Your card's recurring payment authorization is not active. Please update your payment method to continue.

What it means

Raised as ChargeProcessorCardError (code india_card_mandate_missing, or india_card_mandate_inactive/pending depending on status) when an off-session charge on an Indian card has no active e-mandate to attach. Under RBI rules, card recurring payments in India require a registered e-mandate; the code maps the mandate's status ("missing"/"inactive"/"pending") to a specific PurchaseErrorCode and tells the buyer to update their payment method. It is reported to ErrorNotifier with fail_fast: true.

Source

Thrown at app/models/purchase.rb:4905

        return
      end

      if status == "pending" && source.present?
        source.mark_indian_card_mandate_registration!
        CheckIndianCardMandateRegistrationJob.perform_async(source.id)
      end
      ErrorNotifier.notify(
        "Off-session charge on an Indian card has no active e-mandate to reference",
        reference: external_id,
        mandate_status: status,
        fail_fast: true
      )
      error_code = {
        "missing" => PurchaseErrorCode::INDIA_CARD_MANDATE_MISSING,
        "inactive" => PurchaseErrorCode::INDIA_CARD_MANDATE_INACTIVE,
        "pending" => PurchaseErrorCode::INDIA_CARD_MANDATE_PENDING,
      }.fetch(status)
      raise ChargeProcessorCardError.new(
        error_code,
        "Your card's recurring payment authorization is not active. Please update your payment method to continue."
      )
    end

    def create_charge_intent(chargeable, off_session: true)
      with_charge_processor_error_handler do
        amount_cents = total_transaction_cents
        amount_for_gumroad_cents = total_transaction_amount_for_gumroad_cents
        description = "You bought #{link.long_url}!"
        mandate_options = mandate_options_for_stripe
        mark_indian_card_mandate_registration! if mandate_options.present?

        # Renewals and preorder releases rebill a saved card whose e-mandate (Indian cards)
        # was registered at the original purchase, so a missing mandate on those charges is
        # an anomaly worth reporting/failing on. First-time checkout charges can also run
        # off-session (multi-seller carts) but must not be treated that way.
        mandate_expected = is_a_saved_card_rebill?

View on GitHub (pinned to afeacbd394)

Solutions

  1. Send the buyer through an on-session payment flow to re-register/refresh the e-mandate (the mandate_options path — mark_indian_card_mandate_registration! shows the registration lane) and confirm the mandate reaches active status before the next renewal.
  2. Check the mandate status value in the ErrorNotifier payload (reference: external_id, mandate_status:) — "pending" means wait and retry after confirmation rather than demanding a new card.
  3. Audit why the charge ran off-session without mandate_options_for_stripe yielding options: ensure mandate creation at first purchase succeeded for Indian cards (checkout config, currency INR, amount within mandate cap).
  4. Once re-registered, retry the charge; the error is specific to the saved method, not the purchase.

Example fix

// before (conceptual): renewal charges blindly
chargePurchaseOffSession(subscription.latest_purchase, savedCard)
// after: branch Indian cards through mandate verification first
if (card.isIndianCard && !card.hasActiveEMandate) {
  notifyBuyerToUpdatePaymentMethod(PurchaseErrorCode.INDIA_CARD_MANDATE_MISSING)
} else {
  chargePurchaseOffSession(subscription.latest_purchase, savedCard)
}
Defensive patterns

Strategy: try-catch

Validate before calling

# skip off-session charge until the Indian card has an active mandate
if card.india? && mandate_status != "active"
  route_to_on_session_mandate_confirmation(purchase)
  return
end

Type guard

def active_india_mandate?(purchase)
  return true unless purchase.card_indian?
  %w[active].include?(purchase.saved_card_mandate_status)
end

Try / catch

begin
  purchase.create_charge_intent(chargeable, off_session: true)
rescue ChargeProcessorCardError => e
  code = e.error_code
  if [PurchaseErrorCode::INDIA_CARD_MANDATE_MISSING, PurchaseErrorCode::INDIA_CARD_MANDATE_INACTIVE, PurchaseErrorCode::INDIA_CARD_MANDATE_PENDING].include?(code)
    request_buyer_mandate_reauth(purchase, code) # "pending" may just need a retry later
  else
    raise
  end
end

Prevention

When it happens

Trigger: Calling purchase.create_charge_intent(chargeable, off_session: true) where mandate_options_for_stripe returns blank because the saved Indian card's mandate status is "missing", "inactive", or "pending" — i.e. a subscription renewal or other merchant-initiated transaction on an INR Indian card with no usable e-mandate reference (external_id).

Common situations: Buyer's bank expired or suspended the e-mandate; mandate registration never completed (AOTP flow abandoned at checkout); mandate still in "pending" confirmation when the first renewal fired; renewals attempted after RBI cap changes invalidated old mandates; cards replaced/reissued losing the mandate.

Related errors


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