{"record":{"id":"5305ec50a6b443d8","repo":"antiwork/gumroad","slug":"error-message-purchase-charge-error","errorCode":null,"errorMessage":"error_message (purchase charge error)","messagePattern":"error_message \\(purchase charge error\\)","errorType":"exception","errorClass":"Subscription::UpdateFailed","httpStatus":null,"severity":"error","filePath":"app/services/subscription/updater_service.rb","lineNumber":569,"sourceCode":"        subscription.original_purchase.schedule_workflows_for_variants(excluded_variants: original_purchase.variant_attributes) unless same_variants?\n        {\n          success: true,\n          next: logged_in_user && Rails.application.routes.url_helpers.library_purchase_url(upgrade_purchase.external_id, host: \"#{PROTOCOL}://#{DOMAIN}\"),\n          success_message:,\n        }\n      elsif upgrade_purchase.in_progress? && upgrade_purchase.charge_intent&.requires_action?\n        {\n          success: true,\n          requires_card_action: true,\n          client_secret: upgrade_purchase.charge_intent.client_secret,\n          purchase: {\n            id: upgrade_purchase.secure_external_id(scope: \"confirm\", expires_at: 1.hour.from_now),\n            stripe_connect_account_id: upgrade_purchase.merchant_account.is_a_stripe_connect_account? ? upgrade_purchase.merchant_account.charge_processor_merchant_id : nil\n          }\n        }\n      else\n        logger.info(\"SubscriptionUpdater: Error charging user for subscription #{subscription.external_id}: #{error_message}\")\n        raise Subscription::UpdateFailed, error_message\n      end\n    end\n\n    def send_subscription_updated_api_notification\n      return if api_notification_sent\n      return unless tiered_membership?\n      return if same_plan_and_price?\n      unless new_purchase.present?\n        ErrorNotifier.notify(\"SubscriptionUpdater: new_purchase missing when sending API notification\")\n        return\n      end\n\n      self.api_notification_sent = true\n      subscription.send_updated_notifification_webhook(\n        plan_change_type: downgrade? ? \"downgrade\" : \"upgrade\",\n        effective_as_of: (downgrade? && !apply_plan_change_immediately?) ? subscription.end_time_of_last_paid_period : new_purchase.created_at,\n        old_recurrence: original_recurrence,\n        new_recurrence: price.recurrence,","sourceCodeStart":551,"sourceCodeEnd":587,"githubUrl":"https://github.com/antiwork/gumroad/blob/afeacbd394069a1cbf0c6c50ee8e900925050370/app/services/subscription/updater_service.rb#L551-L587","documentation":"In charge_user!, after subscription.charge! for a prorated upgrade or restart charge, the purchase is neither successful nor in-progress-awaiting-card-action. error_message is taken from upgrade_purchase.errors.full_messages.first or upgrade_purchase.error_code and raised as Subscription::UpdateFailed (rescued into { success: false, error_message: }). If this was a resubscribe, the subscription is also unsubscribed and failed via unsubscribe_and_fail!.","triggerScenarios":"Card declined or insufficient funds on the prorated/restart charge; processor risk block; expired saved card charged off_session; charge intent requiring SCA when the flow could not surface card action; processor error during the charge.","commonSituations":"Buyer with an expired card upgrading tiers after failed renewals; retry flow triggered from a failed-charge email (params[:declined]); Stripe/PayPal risk rules on unusual amounts; off_session charges on cards requiring authentication.","solutions":["Show error_message to the buyer; for declines, prompt for a different payment method (the failed-charge-email retry flow exists for this).","Classify via upgrade_purchase.error_code in the DB/logs: buyer declines vs processor errors need different handling.","If the code indicates required authentication, complete the card-action flow (the requires_card_action result branch) instead of re-charging.","For genuine processor errors, retry after the incident resolves."],"exampleFix":null,"handlingStrategy":"fallback","validationCode":null,"typeGuard":null,"tryCatchPattern":"result = Subscription::UpdaterService.new(...).perform\nif result[:success] && result[:requires_card_action]\n  complete_3ds(result[:client_secret], result[:purchase][:id]) # finish SCA, then confirm\nelsif !result[:success]\n  offer_different_payment_method(result[:error_message]) # decline codes are not retryable with the same card\nend","preventionTips":["Handle the requires_card_action result branch before treating a charge as failed.","Classify upgrade_purchase.error_code: buyer declines need a new card; processor errors can be retried.","Keep saved cards current — expired cards charged off_session are the most common restart-upgrade failure."],"tags":["subscription","charge","card-declined","upgrade","stripe"],"backgroundTag":"card-declined","analyzedSha":"afeacbd394069a1cbf0c6c50ee8e900925050370","analyzedAt":"2026-08-21T17:58:52.159Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}