Access failures with anonymous non-cart orders

Created on 12 April 2025, 14 days ago

I have created three separate order types for my site:

  1. Merch: physical products (using commerce_cart normally)
  2. Booking: bookings (not shipping-enabled)
  3. Vouchers: gift vouchers (shipping enabled)

All are for anonymous orders, and all use the 'Default' workflow. Each has its own dedicated checkout flow, though those for Merch and Vouchers are identical (duplicated from the built-in "Shipping" flow, with just the user login/registration step removed). Merch is what I'd call wholly "vanilla" Commerce + Shipping usage and has no issues. For Booking and Vouchers, orders are created programmatically via bespoke Forms using this basic routine in the submit handler:


//First delete any existing carts in the session
$cart_session = \Drupal::service('commerce_cart.cart_session');
$current_orders = $cart_session->getCartIds();
foreach ($current_orders as $order_id) {
	$xorder = Order::load($order_id);
	$xorder->state = 'canceled';
	$xorder->save();
	$cart_session->deleteCartId($order_id);
}
//Extending classes define ORDER_TYPE and STORE_ID constants
$order = Order::create([
	'uid'					=> \Drupal::currentUser()->id(),
	'type'					=> static::ORDER_TYPE,
	'state'					=> 'draft',
	'checkout_step'			=> 'order_information',
	'store_id'				=> static::STORE_ID,
]);
$order->save();
//Extending classes add their own order items
foreach(static::orderItems($form_state) as $item) {
	$order->addItem($item)->save();
}
if(\Drupal::currentUser()->isAnonymous()) {
	$cart_session->addCartId($order->id());
}
$form_state->setRedirect('commerce_checkout.form', ['commerce_order' => $order->id()]);

For Booking, which is not shipping-enabled, this code works without problems. For Vouchers, the destination checkout page (Order Information) shows two problems: The Order Summary sidebar does not render at all; and ajax actions (such as unchecking "Billing address is same as shipping") get an access denied error, e.g.

Path: /checkout/1/order_information?ajax_form=1&_wrapper_format=drupal_ajax. Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException: in Drupal\Core\Routing\AccessAwareRouter->checkAccess() (line 117 of /var/www/proj/web/core/lib/Drupal/Core/Routing/AccessAwareRouter.php).

Due to the errors no shipping rates are presented and the checkout cannot progress. Reloading the page results in Access Denied for the whole page.

If I try to use the same Form while logged-in as admin, the Vouchers checkout works as intended.

The combo of programmatically creating the order + anonymous orders + shipping leads to the user losing access to the whole order or to some key part of it. Can anyone advise how I can debug this further, or what my code might be missing?

💬 Support request
Status

Active

Version

2.0

Component

Code

Created by

🇬🇧United Kingdom headbank

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Comments & Activities

  • Issue created by @headbank
  • 🇮🇱Israel jsacksick

    Support requests are generally not answered from this queue.

    Furthermore, you should be using the CartProvider to get/create a cart for a given user and then the CartManager to add items to it.
    You can find examples in the Drupal\Tests\commerce_cart\Kernel\CartManagerTest.

  • 🇬🇧United Kingdom headbank

    Thanks for the reply. I used the "support request" option as I wasn't confident whether it qualified as a bug, but I'd note that this workflow does work for commerce orders when shipping isn't enabled, so you make a prescription that commerce core does not (in my usage so far).

    It's not desirable for the user to have cart-level access to the items of an order that's customised to the extent that it needs a bespoke form to generate it. To get this far I looked at other examples of people who were doing it the same way in their builds.

    Based on your feedback I will look instead at ways of ensuring the cart gets re-emptied whenever the user makes a page request outside of the checkout flow, which is fairly scorched-earth compared to what I was hoping for.

Production build 0.71.5 2024