{"record":{"id":"cefa575dac35ddd7","repo":"antiwork/gumroad","slug":"india-card-mandate-missing-cefa57","errorCode":"india_card_mandate_missing","errorMessage":"Your card's recurring payment authorization is not active. Please update your payment method to continue.","messagePattern":"Your card's recurring payment authorization is not active\\. Please update your payment method to continue\\.","errorType":"exception","errorClass":"ChargeProcessorCardError","httpStatus":null,"severity":"error","filePath":"app/models/purchase.rb","lineNumber":4905,"sourceCode":"        return\n      end\n\n      if status == \"pending\" && source.present?\n        source.mark_indian_card_mandate_registration!\n        CheckIndianCardMandateRegistrationJob.perform_async(source.id)\n      end\n      ErrorNotifier.notify(\n        \"Off-session charge on an Indian card has no active e-mandate to reference\",\n        reference: external_id,\n        mandate_status: status,\n        fail_fast: true\n      )\n      error_code = {\n        \"missing\" => PurchaseErrorCode::INDIA_CARD_MANDATE_MISSING,\n        \"inactive\" => PurchaseErrorCode::INDIA_CARD_MANDATE_INACTIVE,\n        \"pending\" => PurchaseErrorCode::INDIA_CARD_MANDATE_PENDING,\n      }.fetch(status)\n      raise ChargeProcessorCardError.new(\n        error_code,\n        \"Your card's recurring payment authorization is not active. Please update your payment method to continue.\"\n      )\n    end\n\n    def create_charge_intent(chargeable, off_session: true)\n      with_charge_processor_error_handler do\n        amount_cents = total_transaction_cents\n        amount_for_gumroad_cents = total_transaction_amount_for_gumroad_cents\n        description = \"You bought #{link.long_url}!\"\n        mandate_options = mandate_options_for_stripe\n        mark_indian_card_mandate_registration! if mandate_options.present?\n\n        # Renewals and preorder releases rebill a saved card whose e-mandate (Indian cards)\n        # was registered at the original purchase, so a missing mandate on those charges is\n        # an anomaly worth reporting/failing on. First-time checkout charges can also run\n        # off-session (multi-seller carts) but must not be treated that way.\n        mandate_expected = is_a_saved_card_rebill?","sourceCodeStart":4887,"sourceCodeEnd":4923,"githubUrl":"https://github.com/antiwork/gumroad/blob/afeacbd394069a1cbf0c6c50ee8e900925050370/app/models/purchase.rb#L4887-L4923","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","solutions":["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.","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.","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).","Once re-registered, retry the charge; the error is specific to the saved method, not the purchase."],"exampleFix":"// before (conceptual): renewal charges blindly\nchargePurchaseOffSession(subscription.latest_purchase, savedCard)\n// after: branch Indian cards through mandate verification first\nif (card.isIndianCard && !card.hasActiveEMandate) {\n  notifyBuyerToUpdatePaymentMethod(PurchaseErrorCode.INDIA_CARD_MANDATE_MISSING)\n} else {\n  chargePurchaseOffSession(subscription.latest_purchase, savedCard)\n}","handlingStrategy":"try-catch","validationCode":"# skip off-session charge until the Indian card has an active mandate\nif card.india? && mandate_status != \"active\"\n  route_to_on_session_mandate_confirmation(purchase)\n  return\nend","typeGuard":"def active_india_mandate?(purchase)\n  return true unless purchase.card_indian?\n  %w[active].include?(purchase.saved_card_mandate_status)\nend","tryCatchPattern":"begin\n  purchase.create_charge_intent(chargeable, off_session: true)\nrescue ChargeProcessorCardError => e\n  code = e.error_code\n  if [PurchaseErrorCode::INDIA_CARD_MANDATE_MISSING, PurchaseErrorCode::INDIA_CARD_MANDATE_INACTIVE, PurchaseErrorCode::INDIA_CARD_MANDATE_PENDING].include?(code)\n    request_buyer_mandate_reauth(purchase, code) # \"pending\" may just need a retry later\n  else\n    raise\n  end\nend","preventionTips":["Complete e-mandate registration during the first on-session purchase for every Indian card.","Store and monitor mandate status; schedule renewals only when status is active.","Treat \"pending\" as wait-and-retry, not as a hard failure requiring a new card."],"tags":["india","rbi-emandate","cards","recurring-billing","stripe","off-session"],"backgroundTag":"rbi-emandate-missing","analyzedSha":"afeacbd394069a1cbf0c6c50ee8e900925050370","analyzedAt":"2026-08-21T17:58:52.159Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}