nopSolutions/nopCommerce · error · Exception

One page checkout is disabled

Error message

One page checkout is disabled

What it means

Thrown by the OpcSaveBilling action when _orderSettings.OnePageCheckoutEnabled is false, yet a one-page-checkout (OPC) AJAX POST was received. nopCommerce guards every OPC step with this check so a store running multi-page checkout cannot be driven through OPC endpoints. The message is a hardcoded English literal returned inside the JSON error payload { error=1, message }. It indicates a configuration/flow mismatch, not a malformed request.

Solutions

  1. Enable OnePageCheckoutEnabled in admin (Order settings) if OPC is the intended flow.
  2. If OPC is intentionally off, ensure clients never call OpcSaveBilling: redirect the OPC page to the standard BillingAddress action.
  3. Purge output/CDN cache so browsers stop posting to OPC endpoints after the setting flipped.
  4. If a custom theme hardcodes OPC AJAX calls, switch it to the multi-page controller flow.

Example fix

// before (admin Order settings)
orderSettings.OnePageCheckoutEnabled = false;
// after
orderSettings.OnePageCheckoutEnabled = true; // enables OpcSaveBilling/OpcSaveShipping/etc.
Defensive patterns

Strategy: validation

Validate before calling

// Client: only render/submit the OPC billing step when OPC is on.
const settings = await fetch('/api/checkout-settings').then(r => r.json());
if (!settings.onePageCheckoutEnabled) { location.href = '/checkout/billingaddress'; return; }

Try / catch

// The controller wraps the action in try/catch and returns JSON; handle it.
const res = await fetch('/Checkout/OpcSaveBilling', { method:'POST', body });
const data = await res.json();
if (data.error) {
  if (data.message === 'One page checkout is disabled') location.href = '/checkout/billingaddress';
  else showUserError(data.message);
}

Prevention

When it happens

Trigger: A client POSTs to the OpcSaveBilling billing step while _orderSettings.OnePageCheckoutEnabled == false. Typical when an admin disabled OPC but a shopper's stale browser tab still renders the OPC UI, or a custom integration calls OpcSaveBilling directly against a multi-page-checkout store.

Common situations: Admin toggled 'One page checkout enabled' off (Configuration > Order settings); a theme deployed that defaults to standard multi-page checkout; CDN/page cache still serving the old OPC page after the switch; integration tests calling OPC endpoints after a config change.

Related errors


AI-assisted analysis of nopSolutions/nopCommerce@64bdf2ff08 (2026-08-13). Data as JSON: /api/errors/25fc341a10b2de70. Report an issue: GitHub.

Appendix: source

Thrown at src/Presentation/Nop.Web/Controllers/CheckoutController.cs:1535

    [HttpPost]
    public virtual async Task<IActionResult> OpcSaveBilling(CheckoutBillingAddressModel model, IFormCollection form)
    {
        try
        {
            //validation
            if (_orderSettings.CheckoutDisabled)
                throw new Exception(await _localizationService.GetResourceAsync("Checkout.Disabled"));

            var customer = await _workContext.GetCurrentCustomerAsync();
            var store = await _storeContext.GetCurrentStoreAsync();
            var cart = await _shoppingCartService.GetShoppingCartAsync(customer, ShoppingCartType.ShoppingCart, store.Id);

            if (!cart.Any())
                throw new Exception("Your cart is empty");

            if (!_orderSettings.OnePageCheckoutEnabled)
                throw new Exception("One page checkout is disabled");

            if (await _customerService.IsGuestAsync(customer) && !_orderSettings.AnonymousCheckoutAllowed)
                throw new Exception("Anonymous checkout is not allowed");

            _ = int.TryParse(form["billing_address_id"], out var billingAddressId);

            if (billingAddressId > 0)
            {
                //existing address
                var address = await _customerService.GetCustomerAddressAsync(customer.Id, billingAddressId)
                              ?? throw new Exception(await _localizationService.GetResourceAsync("Checkout.Address.NotFound"));

                customer.BillingAddressId = address.Id;
                await _customerService.UpdateCustomerAsync(customer);
            }
            else
            {
                if (await _customerService.IsGuestAsync(customer) && _taxSettings.EuVatEnabled && _taxSettings.EuVatEnabledForGuests)

View on GitHub (pinned to 64bdf2ff08)