phacility/phabricator · error · Exception

This order can not be cancelled because it has already been

Error message

This order can not be cancelled because it has already been completed.

What it means

PhortuneCartImplementation::assertCanCancelOrder rejects cancellation when the cart status is STATUS_PURCHASED - a completed order must go through refunds at the charge level, not cancellation. Cancel exists for pre-purchase and holding states; once purchased, the money path is willRefundCharge/didRefundCharge via the provider.

Source

Thrown at src/applications/phortune/cart/PhortuneCartImplementation.php:30

    PhabricatorUser $viewer,
    array $carts);

  abstract public function getName(PhortuneCart $cart);
  abstract public function getCancelURI(PhortuneCart $cart);
  abstract public function getDoneURI(PhortuneCart $cart);

  public function getDescription(PhortuneCart $cart) {
    return null;
  }

  public function getDoneActionName(PhortuneCart $cart) {
    return pht('Return to Application');
  }

  public function assertCanCancelOrder(PhortuneCart $cart) {
    switch ($cart->getStatus()) {
      case PhortuneCart::STATUS_PURCHASED:
        throw new Exception(
          pht(
            'This order can not be cancelled because it has already been '.
            'completed.'));
        break;
    }
  }

  public function assertCanRefundOrder(PhortuneCart $cart) {
    return;
  }

  abstract public function willCreateCart(
    PhabricatorUser $viewer,
    PhortuneCart $cart);

}

View on GitHub (pinned to 5720a38cfe)

Solutions

  1. Use the refund flow (refund charges via the provider) instead of cancelling a purchased cart
  2. Check cart status before offering a cancel action - only non-PURCHASED carts may be cancelled
  3. In custom cart implementations, implement assertCanRefundOrder() to expose a refund path for purchased carts

Example fix

// before
$implementation->assertCanCancelOrder($cart); // throws on PURCHASED
// after
if ($cart->getStatus() === PhortuneCart::STATUS_PURCHASED) {
  $implementation->assertCanRefundOrder($cart);
} else {
  $implementation->assertCanCancelOrder($cart);
}
Defensive patterns

Strategy: validation

Validate before calling

// Route by cart status before offering the action
if ($cart->getStatus() === PhortuneCart::STATUS_PURCHASED) {
  $implementation->assertCanRefundOrder($cart);
} else {
  $implementation->assertCanCancelOrder($cart);
}

Type guard

function canCancelCart(PhortuneCart $cart) {
  return $cart->getStatus() !== PhortuneCart::STATUS_PURCHASED;
}

Prevention

When it happens

Trigger: Invoking the cart cancel flow on a cart whose status is already PURCHASED; a product implementation whose UI still shows a Cancel button after checkout completes; admin scripts calling assertCanCancelOrder over historical carts.

Common situations: Buyers clicking Cancel on a completed order page; operators attempting to undo a purchase by cancelling; stale carts from testing where status moved on between page load and click.

Related errors


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