nopCommerce Plugin Development
Overview
nopCommerce is an open-source e-commerce platform built on ASP.NET Core and Entity Framework Core. It powers over 60,000 online stores worldwide and provides a fully modular, plugin-based architecture that allows developers to extend every aspect of the platform -- from payment processing and shipping to tax calculation, widgets, and admin functionality -- without modifying the core source code.
The platform follows a layered architecture with clear separation between presentation (Nop.Web), business logic (Nop.Services), data access (Nop.Data), and core domain (Nop.Core). Plugins operate as self-contained .NET class library projects that hook into this architecture through well-defined interfaces, dependency injection, and an event-driven system.
nopCommerce supports two database engines: Microsoft SQL Server and PostgreSQL. The data layer uses Linq2DB as its primary ORM (replacing Entity Framework Core in recent versions) with FluentMigrator for schema migrations. The admin area uses Razor views with Kendo UI components, and the public storefront supports fully customizable themes.
Plugin development requires familiarity with C#, ASP.NET Core, dependency injection, and the nopCommerce-specific abstractions for services, data access, and UI extension. The platform provides abstract base classes and interfaces that plugins implement to integrate with the core system.
Key architectural layers that plugins interact with:
- Nop.Core -- Domain entities, configuration classes, caching interfaces, event infrastructure, and helper utilities shared across all layers.
- Nop.Data -- The data access layer providing
IRepository<T>, entity builders for schema mapping, FluentMigrator-based migrations, and the Linq2DB integration for custom queries. - Nop.Services -- Business logic layer containing service interfaces and implementations for orders, customers, products, payments, shipping, taxes, localization, settings, scheduled tasks, and logging.
- Nop.Web.Framework -- MVC infrastructure including controller base classes, model binding, tag helpers, view component base classes, HTML helpers, validators, and security filters.
- Nop.Web -- The presentation layer hosting Razor views, themes, the admin panel, and the plugin runtime directory where compiled plugin DLLs are deployed.
Plugin Structure
Every nopCommerce plugin lives in a directory under Plugins/ in the solution root, following the naming convention Nop.Plugin.{Group}.{Name} -- for example, Nop.Plugin.Payment.Stripe or Nop.Plugin.Widgets.GoogleAnalytics. The plugin group determines how nopCommerce categorizes and displays the plugin in the admin panel.
Project File (.csproj)
The plugin project file references nopCommerce core libraries and configures the build output to copy the compiled DLL into the correct runtime directory:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<OutputPath>..\..\Presentation\Nop.Web\Plugins\Nop.Plugin.Misc.MyPlugin</OutputPath>
<OutDir>$(OutputPath)</OutDir>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\Presentation\Nop.Web.Framework\Nop.Web.Framework.csproj" />
<None Include="plugin.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>
The OutputPath must point to Presentation/Nop.Web/Plugins/{PluginName} so the plugin DLL and its assets are available at runtime.
Plugin Descriptor (plugin.json)
Every plugin requires a plugin.json file that declares metadata consumed by the nopCommerce plugin system:
{
"Group": "Misc",
"FriendlyName": "My Custom Plugin",
"SystemName": "Nop.Plugin.Misc.MyPlugin",
"Version": "1.0.0",
"SupportedVersions": [ "4.70" ],
"Author": "Developer Name",
"DisplayOrder": 1,
"FileName": "Nop.Plugin.Misc.MyPlugin.dll",
"Description": "A brief description of what the plugin does.",
"LimitedToStores": [],
"LimitedToCustomerRoles": [],
"DependsOnSystemNames": []
}
The SystemName must match the assembly name exactly. SupportedVersions declares compatibility with specific nopCommerce versions and determines whether the plugin appears as installable in the admin.
BasePlugin Class
Every plugin must contain a class that extends BasePlugin. This class provides the install and uninstall lifecycle hooks:
public class MyPlugin : BasePlugin
{
public override async Task InstallAsync()
{
// Create database tables, seed default settings, add locale resources
await base.InstallAsync();
}
public override async Task UninstallAsync()
{
// Drop tables, remove settings, delete locale resources
await base.UninstallAsync();
}
}
Plugins that implement specific platform features (payment, shipping, tax, widgets) extend specialized interfaces instead of or in addition to BasePlugin. For example, a payment plugin implements IPaymentMethod, a shipping plugin implements IShippingRateComputationMethod, and a widget plugin implements IWidgetPlugin.
For complete plugin lifecycle details, directory layout, and plugin group reference, see references/plugin-architecture.md.
Dependency Injection
nopCommerce uses its own DI bootstrap mechanism built on top of IServiceCollection and Autofac. Plugins register their services by implementing the INopStartup interface:
public class NopStartup : INopStartup
{
public void ConfigureServices(IServiceCollection services, IConfiguration configuration)
{
services.AddScoped<IMyCustomService, MyCustomService>();
}
public void Configure(IApplicationBuilder application)
{
// Register middleware if needed
}
public int Order => 3000; // Higher = later in startup pipeline
}
The Order property controls when the plugin's services are registered relative to core services and other plugins. Core nopCommerce services use low order values (0-1000); plugins should use 3000+ to ensure core services are already registered.
Service lifetimes follow standard ASP.NET Core conventions: Scoped for per-request services (most common), Singleton for shared state, and Transient for lightweight stateless services. Most plugin services should be registered as Scoped.
For advanced DI patterns including Autofac integration, middleware registration, and task scheduling, see references/dependency-injection.md.
Data Access and Entity Framework
nopCommerce uses Linq2DB as its data access layer with FluentMigrator for schema migrations. Plugins define their own entities, mappings, and migrations to extend the database schema without modifying core tables.
Entity Definition
Plugin entities extend BaseEntity and define the data model:
public class MyCustomRecord : BaseEntity
{
public string Name { get; set; }
public decimal Amount { get; set; }
public int CustomerId { get; set; }
public DateTime CreatedOnUtc { get; set; }
}
Entity Builder (Schema Mapping)
Map the entity to a database table using NopEntityBuilder<T>:
public class MyCustomRecordBuilder : NopEntityBuilder<MyCustomRecord>
{
public override void MapEntity(CreateTableExpressionBuilder table)
{
table.WithColumn(nameof(MyCustomRecord.Name)).AsString(400).NotNullable()
.WithColumn(nameof(MyCustomRecord.Amount)).AsDecimal(18, 4)
.WithColumn(nameof(MyCustomRecord.CustomerId)).AsInt32();
}
}
Migrations
Use FluentMigrator to create and evolve the schema:
[NopMigration("2024-01-15 12:00:00", "MyPlugin: create MyCustomRecord table",
MigrationProcessType.Installation)]
public class CreateMyCustomRecordTable : AutoReversingMigration
{
public override void Up()
{
Create.TableFor<MyCustomRecord>();
}
}
Data access uses the IRepository<T> pattern for CRUD operations. Inject IRepository<MyCustomRecord> into services to query and manipulate plugin data.
For complete data access patterns including custom queries, Linq2DB integration, and migration strategies, see references/data-access-and-ef.md.
Extension Mechanisms
nopCommerce provides several mechanisms for plugins to extend the platform beyond basic service registration.
Event System
Subscribe to platform events using IConsumer<T>:
public class OrderPlacedHandler : IConsumer<OrderPlacedEvent>
{
public async Task HandleEventAsync(OrderPlacedEvent eventMessage)
{
var order = eventMessage.Order;
// React to order placement
}
}
Register the consumer in NopStartup and it automatically receives events published by the core system.
View Components
Extend the admin or storefront UI using ASP.NET Core view components. Widget plugins define view components that render in specific widget zones:
public class MyWidgetViewComponent : NopViewComponent
{
public async Task<IViewComponentResult> InvokeAsync(string widgetZone, object additionalData)
{
var model = new MyWidgetModel { /* ... */ };
return View("~/Plugins/Nop.Plugin.Widgets.MyWidget/Views/MyWidget.cshtml", model);
}
}
Admin Area Extension
Extend the admin panel with configuration pages, menu items, and custom views. Admin controllers inherit from BasePluginController and use Razor views with Kendo UI for data grids, forms, and dialogs.
For complete extension patterns including admin menus, sitemap providers, and JavaScript bundling, see references/admin-extension.md.
Development Setup
Prerequisites
- Visual Studio 2022 (Community or higher) with the ASP.NET and web development workload, or JetBrains Rider.
- .NET 8 SDK (or the version matching the target nopCommerce release).
- Microsoft SQL Server (Express or Developer edition) or PostgreSQL 15+.
- Git for source control.
Getting Started
- Clone the nopCommerce source from the official GitHub repository.
- Open
NopCommerce.slnin Visual Studio. - Create the plugin directory:
Plugins/Nop.Plugin.{Group}.{Name}/. - Add a new Class Library project targeting
net8.0. - Reference
Nop.Web.Frameworkfrom the project. - Create
plugin.json, theBasePluginclass, andNopStartup. - Build the solution -- the plugin DLL is copied to the
Nop.Web/Plugins/directory. - Run
Nop.Web, navigate to Admin > Configuration > Local Plugins, and install the plugin.
Database Setup
On first run, nopCommerce prompts for database configuration. Select SQL Server or PostgreSQL, provide the connection string, and the installer creates all tables automatically. Plugin migrations run when the plugin is installed from the admin panel.
Debugging
Set Nop.Web as the startup project and press F5 to debug. Breakpoints in plugin code work normally because the plugin DLL is loaded from the Plugins/ directory at runtime. Enable detailed error pages by setting ASPNETCORE_ENVIRONMENT=Development in launchSettings.json.
Testing
nopCommerce does not ship a dedicated plugin testing framework, but standard .NET testing tools apply. Use xUnit or NUnit with Moq to test plugin services by mocking IRepository<T> and core service interfaces. For integration testing, set up a test database and register plugin services manually using IServiceCollection. Test the full plugin lifecycle (install, configure, operate, uninstall) on a clean nopCommerce installation before marketplace submission.
Anti-Patterns
Avoid these common mistakes in nopCommerce plugin development:
Modifying core source files -- Never edit files in
Nop.Core,Nop.Services,Nop.Data, orNop.Webdirectly. Use the plugin system, events, and DI to extend functionality. Core modifications prevent upgrading to new nopCommerce versions.Ignoring the plugin.json SupportedVersions field -- Always specify the exact nopCommerce versions the plugin supports. Omitting this causes compatibility warnings and may prevent installation.
Using raw SQL instead of IRepository -- The repository pattern handles multi-database compatibility (SQL Server and PostgreSQL). Raw SQL queries break portability and bypass caching.
Registering services with wrong lifetimes -- Database contexts and repositories are scoped. Injecting scoped services into singletons causes runtime errors. Use
Scopedfor most plugin services.Skipping uninstall cleanup -- Always drop plugin tables, remove settings, and delete locale resources in
UninstallAsync(). Leftover data causes errors if the plugin is reinstalled or a different version is installed.Hardcoding widget zone names -- Use the constants defined in
PublicWidgetZonesandAdminWidgetZonesclasses instead of string literals. Zone names change between nopCommerce versions.Blocking the startup pipeline -- Keep
NopStartup.ConfigureServices()lightweight. Do not perform database queries, HTTP calls, or heavy initialization during service registration.Ignoring async patterns -- nopCommerce is fully async. Calling
.Resultor.Wait()on async methods causes deadlocks. Always useawaitthroughout the call chain.Missing CopyLocalLockFileAssemblies -- Without
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>in the.csproj, third-party NuGet dependencies are not copied to the plugin output directory, causingFileNotFoundExceptionat runtime.Not verifying webhook signatures -- Payment and notification webhooks from external services must be signature-verified before processing. Skipping verification exposes the plugin to spoofed events and fraudulent state changes.
For detailed guidance on each topic, see the reference files below.
Reference Files
- Plugin Architecture -- Plugin directory structure, plugin.json metadata, BasePlugin install/uninstall, plugin lifecycle, plugin groups, referencing nopCommerce libraries
- Dependency Injection -- NopStartup class, IServiceCollection registration, service lifetimes, Autofac integration, middleware, task scheduling
- Data Access and EF -- Entity classes, entity builders, FluentMigrator migrations, IRepository pattern, custom queries, Linq2DB integration
- Admin Extension -- Admin controllers and views, view components, menus and sitemap providers, configuration pages, JavaScript/CSS bundling, Kendo UI
- Marketplace Publishing -- Marketplace submission, source vs DLL packaging, documentation, version compatibility, pricing, support expectations