Why nopCommerce Plugins Matter
nopCommerce powers over 60,000 online stores worldwide. It's the leading open-source e-commerce platform on .NET — and its plugin architecture is the engine behind that ecosystem. Every payment gateway integration, every shipping carrier, every widget, and every custom feature lives as a plugin.
If you understand how to build nopCommerce plugins, you can extend one of the most capable e-commerce platforms on the market — and build a business doing it. This guide covers everything from your first plugin scaffold to production deployment patterns.
We've been building nopCommerce plugins since 2009, with 38+ plugins in our catalog — including 12 Brazilian payment gateway integrations and the only complete PIX payment solution on the platform. This guide reflects what we've learned building plugins that process millions of transactions.
Plugin Architecture: The Foundation
The Plugin Interface Hierarchy
Every nopCommerce plugin starts with the IPlugin interface in Nop.Services.Plugins. Rather than making you implement everything from scratch, nopCommerce provides BasePlugin — an abstract class that handles the boilerplate — and a set of specialized interfaces for common plugin types:
| Interface | Purpose | Example |
|---|---|---|
| IPaymentMethod | Payment processing (authorize, capture, refund, void) | PayPal, Stripe, PIX, MercadoPago |
| IShippingRateComputationMethod | Calculate shipping rates from carriers | UPS, FedEx, Correios (Brazil) |
| ITaxProvider | Calculate tax rates by jurisdiction | Avalara, TaxJar |
| IWidgetPlugin | Render UI widgets in specific zones | Live chat, newsletter popup, Trello cards |
| IExchangeRateProvider | Fetch currency exchange rates | ECB, Central Bank of Brazil |
| IDiscountRequirementRule | Custom discount logic | "Customer from Brazil gets 10% off" |
| IExternalAuthenticationMethod | Social login and SSO | Google, Facebook, Azure AD |
| IMultiFactorAuthenticationMethod | MFA plugins (since 4.40) | Google Authenticator, SMS OTP |
| IPickupPointProvider | In-store pickup locations | Local store network, locker systems |
| IMiscPlugin | General-purpose extensions | DNS management, analytics, integrations |
Choose the most specific interface for your use case. IPaymentMethod gives you the full payment lifecycle — ProcessPaymentAsync, PostProcessPaymentAsync, RefundAsync, VoidAsync, CaptureAsync — while IMiscPlugin is the escape hatch for anything that doesn't fit a standard category.
Project Structure: Step by Step
1. Create the Class Library Project
Create a new Class Library project targeting .NET 9. The recommended naming convention is Nop.Plugin.{Group}.{Name}:
Nop.Plugin.Payments.PayPalCommerce
Nop.Plugin.Shipping.FedEx
Nop.Plugin.Widgets.WhatsApp
Nop.Plugin.Misc.GoDaddy
Place the project in the \Plugins directory at the solution root (not inside \Nop.Web\Plugins, which is the deployment target). Set the .csproj output path to $(SolutionDir)\Presentation\Nop.Web\Plugins\{PluginOutputDirectory} and enable CopyLocalLockFileAssemblies to ensure NuGet dependencies are copied to output.
2. Create plugin.json
Every plugin requires a plugin.json file with metadata:
{
"Group": "Payment methods",
"FriendlyName": "My Payment Gateway",
"SystemName": "Payments.MyGateway",
"Version": "1.00",
"SupportedVersions": [ "4.90" ],
"Author": "SplatDev",
"DisplayOrder": 1,
"FileName": "Nop.Plugin.Payments.MyGateway.dll",
"Description": "Process payments via My Gateway"
}
The SystemName must match your assembly name. SupportedVersions determines which nopCommerce versions can install the plugin. If you upgrade nopCommerce and a plugin breaks, check this field first — many plugins only need their supported versions updated.
3. Implement the Plugin Class
Create a class that implements the appropriate plugin interface and derives from BasePlugin:
public class MyPaymentProcessor : BasePlugin, IPaymentMethod
{
public override Task InstallAsync()
{
// Insert settings, locale resources, or database tables
return base.InstallAsync();
}
public override Task UninstallAsync()
{
// Clean up settings, locale resources, or database tables
return base.UninstallAsync();
}
public override Task UpdateAsync(string currentVersion, string targetVersion)
{
// Handle version migrations
return base.UpdateAsync(currentVersion, targetVersion);
}
public Task<ProcessPaymentResult> ProcessPaymentAsync(ProcessPaymentRequest request)
{
// Your payment logic here
}
public override string GetConfigurationPageUrl()
{
return _nopUrlHelper.RouteUrl(MyDefaults.Route.Configuration);
}
}
The InstallAsync, UninstallAsync, and UpdateAsync overrides are your lifecycle hooks. Always call the base implementation — base.InstallAsync() handles registration in the plugin system.
4. Add Configuration (Admin UI)
For plugins with settings, create an MVC controller, a model, and a Razor view:
- Controllers/{Name}Controller.cs — decorated with
[AuthorizeAdmin]and[Area(AreaNames.ADMIN)]. Inherit fromBasePluginController. - Models/ConfigurationModel.cs — a plain class with your settings properties and
[NopResourceDisplayName]attributes for localization. - Views/Configure.cshtml — uses the
_ConfigurePluginlayout. Also include_ViewImports.cshtml(copy from an existing plugin).
Register the configuration route in a RouteProvider.cs implementing IRouteProvider:
public class RouteProvider : IRouteProvider
{
public void RegisterRoutes(IEndpointRouteBuilder endpointRouteBuilder)
{
endpointRouteBuilder.MapControllerRoute(
name: MyDefaults.Route.Configuration,
pattern: "Admin/MyGateway/Configure",
defaults: new { controller = "MyGateway", action = "Configure", area = AreaNames.ADMIN });
}
public int Priority => 0;
}
Building a Payment Plugin: The Real-World Pattern
Payment plugins are the most common — and most complex — plugin type. Here's the production pattern we use at SplatDev:
Core Components
- Settings class: A class implementing
ISettingswith properties for API keys, URLs, modes (sandbox/production), and feature toggles. Store secrets inISettingService— never hard-code credentials. - Defaults class: A static class with constants — route names, system name, configuration URLs, API endpoints. Keeps magic strings out of your logic.
- Service class: Your actual integration logic — HTTP calls to the payment gateway, response parsing, webhook handling. Inject via DI and keep it testable (interface + implementation).
- Event consumers: Implement
IConsumer<OrderPlacedEvent>and similar interfaces to react to store events — send transaction confirmations, trigger post-payment workflows.
Payment Flow: The Methods You Must Implement
| Method | When Called | What It Should Do |
|---|---|---|
ProcessPaymentAsync |
Customer clicks "Pay" | Authorize or capture the payment. Return success/failure and transaction ID. |
PostProcessPaymentAsync |
After payment processing completes | Redirect to external gateway (PayPal, PagSeguro) or show a confirmation page. |
RefundAsync |
Admin initiates refund | Process the refund through the gateway. Return success/failure. |
VoidAsync |
Admin voids an authorization | Void the pending authorization. Only applicable if payment wasn't yet captured. |
CaptureAsync |
Admin captures an authorized payment | Capture a previously authorized amount. Common for authorization-then-capture flows. |
GetPaymentMethodDescriptionAsync |
Checkout page renders | Return the display name, description, and logo for the payment method. |
GetAdditionalHandlingFeeAsync |
Order total calculation | Return any extra fee the payment method charges, or zero. |
Brazilian Payment Plugin Example: PIX
Brazil's PIX instant payment system requires a specific flow: generate a QR code with a payload (EMV string), display it to the customer, and listen for a webhook confirmation when the payment is received. Here's the pattern:
- ProcessPaymentAsync: Call the PSP (PagSeguro, MercadoPago, etc.) to create a PIX charge. Store the transaction ID (
txid) and the EMV payload. - PostProcessPaymentAsync: Render the QR code (generated from the EMV payload) and the "copy-paste" code. Set the payment status to
Pending. - Webhook handler: The PSP sends a webhook when PIX confirms payment. Update the order status and trigger
OrderPaidEvent.
This is the pattern behind all 12 of SplatDev's Brazilian payment plugins — processing PIX, boleto, and installment-based credit card payments through PagSeguro, MercadoPago, Cielo, Banco Inter, Pagar.me, Rede, GetNet, PicPay, NuPay, Braspag, and InfinityPay.
Widgets: Adding UI Without Touching Themes
Widget plugins implement IWidgetPlugin and must specify which widget zones they render in:
public Task<IList<string>> GetWidgetZonesAsync()
{
return Task.FromResult<IList<string>>(new List<string>
{
PublicWidgetZones.HomepageBeforeBestSellers,
PublicWidgetZones.ProductDetailsAfterPictures
});
}
Then implement GetPublicViewComponentAsync to return the widget's HTML. The widget system calls this method for each zone you've registered.
Common widget patterns: newsletter signup popups, live chat buttons, trust badges, cookie consent banners, WhatsApp floating buttons, recently viewed products, and social proof notifications.
Database Access: IRepository<T> and Migrations
nopCommerce uses a repository pattern via IRepository<T> for data access. For plugins that need custom tables:
- Define your entity class (must have an
Idproperty). - Create an
ObjectContextthat registers your entity with Entity Framework Core. - In
InstallAsync, use_dbContext.InstallAsync()to create the tables. - In
UninstallAsync, use_dbContext.UninstallAsync()to drop them.
For schema migrations between plugin versions, consider adding a MigrationManager to your UpdateAsync override that checks the current schema version and applies necessary ALTER TABLE statements. This is simpler than FluentMigrator for plugins and keeps your migration logic in one place.
Testing: Treat Your Plugin Like a Product
At SplatDev, we treat every plugin as a product with its own test suite:
- Unit tests (xUnit): Test your service layer in isolation. Mock
ISettingService,IRepository<T>, and any HTTP clients. Your payment logic, tax calculations, and shipping rate computations should have 100% unit test coverage on the business logic. - Integration tests: Spin up a test nopCommerce instance and verify your plugin installs, configures, and processes through the full lifecycle. Use sandbox/test credentials for payment gateways.
- Localization tests: Verify all locale resources exist for every language you claim to support. Missing resource strings are the most common plugin bug.
- StyleCop compliance: nopCommerce expects StyleCop compliance. Run it in your CI pipeline — it catches subtle issues before code review.
Common Pitfalls (And How to Avoid Them)
1. Not Calling base.InstallAsync()
If you override InstallAsync without calling the base, your plugin won't register with the plugin system and won't appear in the admin. Always include await base.InstallAsync() at the end of your override.
2. Hard-Coding Credentials
Store API keys, tokens, and secrets in ISettingService — never hard-code them in plugin.json or as class constants. The settings page should allow admins to configure these values, and they should be persisted encrypted when appropriate.
3. Ignoring Version Compatibility
nopCommerce versions break plugins. The SupportedVersions field in plugin.json isn't decorative — nopCommerce checks it before loading your plugin. When upgrading nopCommerce, test your plugin against the new version and update this field. Don't just bump the number without testing.
4. Blocking the Request Thread
All plugin methods are async for a reason. If your payment processor calls an external API, that call should be async. Blocking the thread with .Result or .Wait() will degrade performance under load and can cause deadlocks.
5. No Event Consumers
Plugins that only provide a configuration page are missing the point. The real power of nopCommerce plugins comes from event consumers — reacting to store events like OrderPlacedEvent, CustomerRegisteredEvent, or ShipmentDeliveredEvent. Wire your plugin into the store's business events, not just the admin UI.
nopCommerce 4.90: What's New for Plugin Developers
nopCommerce 4.90 targets .NET 9 and brings several improvements for plugin developers:
- .NET 9 runtime: Your plugins run on the fastest .NET yet — benefiting from JIT improvements, GC dynamic adaptation, and loop optimizations automatically.
- C# 13 support: Use
paramscollections,ref structinterfaces, and partial properties in your plugin code. - Updated NuGet dependencies: Underlying packages (Autofac, FluentValidation, etc.) have been updated. Test your plugin's dependency tree.
- IMultiFactorAuthenticationMethod: MFA plugins are now a first-class concept with their own interface.
- Improved plugin template: The official nopCommerce plugin Visual Studio template has been updated for 4.90 — use it as your starting point.
Where to Go From Here
nopCommerce plugin development is a career-building skill. The nopCommerce marketplace is active and growing, and well-built plugins generate recurring revenue through updates and support. At SplatDev, plugins are our core business — our Store at store.splatdev.com carries 38+ plugins, and we've been a Gold Solutions Partner for years.
If you're building an e-commerce project on nopCommerce, you don't need to build everything from scratch. Our catalog covers Brazilian payments, shipping (Correios, JadLog, Braspress), ERP integrations, and custom functionality. If you need something we don't have, we build custom plugins too.
Ready to build or extend your nopCommerce store? Our team has been doing this since 2009 — longer than almost anyone in the ecosystem.
Work With Our nopCommerce Team
Or browse our plugin catalog: store.splatdev.com