{"record":{"id":"d68a5eeaf452a9d4","repo":"phacility/phabricator","slug":"attempting-to-apply-a-charge-using-an-inactive-pay","errorCode":null,"errorMessage":"Attempting to apply a charge using an inactive payment method (\"%s\")!","messagePattern":"Attempting to apply a charge using an inactive payment method \\(\"(.+?)\"\\)!","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"critical","filePath":"src/applications/phortune/storage/PhortuneCart.php","lineNumber":123,"sourceCode":"\n  public function willApplyCharge(\n    PhabricatorUser $actor,\n    PhortunePaymentProvider $provider,\n    PhortunePaymentMethod $method = null) {\n\n    $account = $this->getAccount();\n\n    $charge = PhortuneCharge::initializeNewCharge()\n      ->setAccountPHID($account->getPHID())\n      ->setCartPHID($this->getPHID())\n      ->setAuthorPHID($actor->getPHID())\n      ->setMerchantPHID($this->getMerchant()->getPHID())\n      ->setProviderPHID($provider->getProviderConfig()->getPHID())\n      ->setAmountAsCurrency($this->getTotalPriceAsCurrency());\n\n    if ($method) {\n      if (!$method->isActive()) {\n        throw new Exception(\n          pht(\n            'Attempting to apply a charge using an inactive '.\n            'payment method (\"%s\")!',\n            $method->getPHID()));\n      }\n      $charge->setPaymentMethodPHID($method->getPHID());\n    }\n\n    $this->openTransaction();\n      $this->beginReadLocking();\n\n        $copy = clone $this;\n        $copy->reload();\n\n        if ($copy->getStatus() !== self::STATUS_READY) {\n          throw new Exception(\n            pht(\n              'Cart has wrong status (\"%s\") to call %s, expected \"%s\".',","sourceCodeStart":105,"sourceCodeEnd":141,"githubUrl":"https://github.com/phacility/phabricator/blob/5720a38cfe95b00ca4be5016dd0d2f3195f4fa04/src/applications/phortune/storage/PhortuneCart.php#L105-L141","documentation":"PhortuneCart::willApplyCharge() throws before opening the charge transaction because the supplied PhortunePaymentMethod is not active. Charging a removed or disabled instrument is never valid: the method was likely revoked between selection and charge, and billing against it would be unauthorized. The guard keeps the charge ledger consistent with the method table.","triggerScenarios":"Calling $cart->willApplyCharge($actor, $provider, $method) with $method->isActive() false - typically a stale method object loaded before the user removed/disabled it, or a subscription whose defaultPaymentMethodPHID now points at a non-active method.","commonSituations":"Automatic subscription billing racing a user deleting their card in another session; checkout flows holding a method object across multiple request steps; payment methods created directly in the database without STATUS_ACTIVE during testing.","solutions":["Reload the method immediately before charging using PhortunePaymentMethodQuery with withStatuses(array(PhortunePaymentMethod::STATUS_ACTIVE)); a removed method then simply fails to load and you can prompt for a new one.","Check $method->isActive() right before willApplyCharge() and surface a payment-method update step instead of charging.","For subscriptions, keep the default payment method current and act on the billing-problem email Phortune sends when the method is invalid."],"exampleFix":"// before\n$method = id(new PhortunePaymentMethodQuery())\n  ->setViewer($viewer)\n  ->withPHIDs(array($method_phid))\n  ->executeOne();\n$charge = $cart->willApplyCharge($viewer, $provider, $method); // throws if removed since load\n\n// after\n$method = id(new PhortunePaymentMethodQuery())\n  ->setViewer($viewer)\n  ->withPHIDs(array($method_phid))\n  ->withStatuses(array(PhortunePaymentMethod::STATUS_ACTIVE))\n  ->executeOne();\nif (!$method) {\n  throw new Exception(pht('Payment method is no longer available.'));\n}\n$charge = $cart->willApplyCharge($viewer, $provider, $method);","handlingStrategy":"validation","validationCode":"$method = id(new PhortunePaymentMethodQuery())\n  ->setViewer($viewer)\n  ->withPHIDs(array($method_phid))\n  ->withStatuses(array(PhortunePaymentMethod::STATUS_ACTIVE))\n  ->executeOne();\nif (!$method || !$method->isActive()) {\n  // ask the user to choose another payment method; do not charge\n}","typeGuard":"function isChargeablePaymentMethod(PhortunePaymentMethod $method) {\n  return $method->isActive();\n}","tryCatchPattern":null,"preventionTips":["Always load payment methods with withStatuses(array(STATUS_ACTIVE)) right before charging.","Never cache a method object across long-running request steps.","For subscriptions, react promptly to billing-problem notifications about invalid methods."],"tags":["phortune","phabricator","php","payments","payment-method","charge"],"backgroundTag":"inactive-payment-method","analyzedSha":"5720a38cfe95b00ca4be5016dd0d2f3195f4fa04","analyzedAt":"2026-08-21T05:07:25.672Z","schemaVersion":2},"datasetVersion":"2026-08-21T11:28:35.574Z"}