nopSolutions/nopCommerce · error · NopException

Shopping cart has no recurring items

Error message

Shopping cart has no recurring items

What it means

Thrown by CreateSetupTokenAsync when the current shopping cart has no recurring items (ShoppingCartIsRecurringAsync returns false). Setup tokens are meaningful only for recurring/billing-agreement purchases, so a one-time cart is rejected before contacting PayPal. It is surfaced as the Error string.

Source

Thrown at src/Plugins/Nop.Plugin.Payments.PayPalCommerce/Services/PayPalCommerceServiceManager.cs:2621

    /// <param name="settings">Plugin settings</param>
    /// <returns>
    /// A task that represents the asynchronous operation
    /// The task result contains the setup token; error message if exists
    /// </returns>
    public async Task<(PaymentToken PaymentToken, string Error)> CreateSetupTokenAsync(PayPalCommerceSettings settings)
    {
        return await HandleFunctionAsync(async () =>
        {
            if (!IsConfigured(settings))
                throw new NopException("Plugin not configured");

            if (!settings.UseVault)
                throw new NopException("Vault disabled");

            var details = await PrepareCartDetailsAsync(ButtonPlacement.PaymentMethod);

            if (!await _shoppingCartService.ShoppingCartIsRecurringAsync(details.Cart))
                throw new NopException("Shopping cart has no recurring items");

            if (await _customerService.IsGuestAsync(details.Customer))
                throw new NopException("Anonymous checkout disabled for recurring items");

            var (error, cycleLength, cyclePeriod, totalCycles) = await _shoppingCartService.GetRecurringCycleInfoAsync(details.Cart);
            if (!string.IsNullOrEmpty(error))
                throw new NopException(error);

            var orderGuid = Guid.NewGuid().ToString();
            var items = await PrepareOrderItemsAsync(details);
            var orderAmount = await PrepareOrderMoneyAsync(details, items);
            var shipping = await PrepareShippingDetailsAsync(details, details.ShippingOption?.Name);

            var context = PrepareRecurringPaymentContext(details);
            var payer = await PrepareBillingDetailsAsync(settings, details);

            //prepare subscription (billing plan) details
            var billingCycle = new BillingCycle

View on GitHub (pinned to 64bdf2ff08)

Solutions

  1. Only invoke CreateSetupTokenAsync when the cart actually contains recurring products.
  2. Pre-check await _shoppingCartService.ShoppingCartIsRecurringAsync(cart) before showing the recurring checkout button.
  3. Verify recurring products have valid recurring cycle settings so they are recognized as recurring.

Example fix

// before
var (token, err) = await _serviceManager.CreateSetupTokenAsync(settings);

// after
var details = await PrepareCartDetailsAsync(ButtonPlacement.PaymentMethod);
if (!await _shoppingCartService.ShoppingCartIsRecurringAsync(details.Cart))
{
    NotifyError("This flow is only for recurring orders");
    return;
}
var (token, err) = await _serviceManager.CreateSetupTokenAsync(settings);
Defensive patterns

Strategy: validation

Validate before calling

if (!await _shoppingCartService.ShoppingCartIsRecurringAsync(cart))
    return;

Type guard

static async Task<bool> HasRecurring(IShoppingCartService svc, IList<ShoppingCartItem> cart) =>
    await svc.ShoppingCartIsRecurringAsync(cart);

Try / catch

var (token, error) = await mgr.CreateSetupTokenAsync(settings);
if (!string.IsNullOrEmpty(error))
    NotifyError(error);

Prevention

When it happens

Trigger: The setup-token entry point is reached for a cart containing only standard (non-recurring) products; a recurring product was removed from the cart leaving only one-time items.

Common situations: Front-end routing calls the recurring setup-token flow for all checkouts regardless of cart type; a recurring product was misconfigured (no recurring cycle) so it no longer counts as recurring.

Related errors


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