phacility/phabricator · critical · Exception

Attempting to apply a charge using an inactive payment metho

Error message

Attempting to apply a charge using an inactive payment method ("%s")!

What it means

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.

Source

Thrown at src/applications/phortune/storage/PhortuneCart.php:123

  public function willApplyCharge(
    PhabricatorUser $actor,
    PhortunePaymentProvider $provider,
    PhortunePaymentMethod $method = null) {

    $account = $this->getAccount();

    $charge = PhortuneCharge::initializeNewCharge()
      ->setAccountPHID($account->getPHID())
      ->setCartPHID($this->getPHID())
      ->setAuthorPHID($actor->getPHID())
      ->setMerchantPHID($this->getMerchant()->getPHID())
      ->setProviderPHID($provider->getProviderConfig()->getPHID())
      ->setAmountAsCurrency($this->getTotalPriceAsCurrency());

    if ($method) {
      if (!$method->isActive()) {
        throw new Exception(
          pht(
            'Attempting to apply a charge using an inactive '.
            'payment method ("%s")!',
            $method->getPHID()));
      }
      $charge->setPaymentMethodPHID($method->getPHID());
    }

    $this->openTransaction();
      $this->beginReadLocking();

        $copy = clone $this;
        $copy->reload();

        if ($copy->getStatus() !== self::STATUS_READY) {
          throw new Exception(
            pht(
              'Cart has wrong status ("%s") to call %s, expected "%s".',

View on GitHub (pinned to 5720a38cfe)

Solutions

  1. 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.
  2. Check $method->isActive() right before willApplyCharge() and surface a payment-method update step instead of charging.
  3. For subscriptions, keep the default payment method current and act on the billing-problem email Phortune sends when the method is invalid.

Example fix

// before
$method = id(new PhortunePaymentMethodQuery())
  ->setViewer($viewer)
  ->withPHIDs(array($method_phid))
  ->executeOne();
$charge = $cart->willApplyCharge($viewer, $provider, $method); // throws if removed since load

// after
$method = id(new PhortunePaymentMethodQuery())
  ->setViewer($viewer)
  ->withPHIDs(array($method_phid))
  ->withStatuses(array(PhortunePaymentMethod::STATUS_ACTIVE))
  ->executeOne();
if (!$method) {
  throw new Exception(pht('Payment method is no longer available.'));
}
$charge = $cart->willApplyCharge($viewer, $provider, $method);
Defensive patterns

Strategy: validation

Validate before calling

$method = id(new PhortunePaymentMethodQuery())
  ->setViewer($viewer)
  ->withPHIDs(array($method_phid))
  ->withStatuses(array(PhortunePaymentMethod::STATUS_ACTIVE))
  ->executeOne();
if (!$method || !$method->isActive()) {
  // ask the user to choose another payment method; do not charge
}

Type guard

function isChargeablePaymentMethod(PhortunePaymentMethod $method) {
  return $method->isActive();
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of phacility/phabricator@5720a38cfe (2026-08-21). Data as JSON: /api/errors/d68a5eeaf452a9d4. Report an issue: GitHub.