How to Build a Payment Plugin in nopCommerce 4.80

Plugin Development 12 min read August 7, 2026

How to Build a Payment Plugin in nopCommerce 4.80

A code-first walkthrough of the IPaymentMethod contract — project scaffolding, checkout UI, capture/refund/void, and PCI-safe testing — for teams integrating a gateway nopCommerce doesn't support out of the box.

BE
nopCommerce Development Team · Bangladesh Software Solution
Isometric illustration of a payment plugin module docking into nopCommerce's checkout pipeline

Every nopCommerce store ships with a handful of payment methods out of the box, plus whatever gateway-specific plugins you install from the marketplace. That covers the popular processors. It does not cover the regional acquirer your finance team just signed, the in-house PSP a client insists on keeping, or the buy-now-pay-later provider with no published nopCommerce plugin at all. When that happens, the only path forward is building the payment plugin yourself against nopCommerce's own IPaymentMethod contract.

That's a narrower, stricter job than plugin development in general. A payment plugin has to slot into the checkout flow at an exact set of hook points, declare which capabilities it actually supports — capture, refund, void, recurring billing — and do all of it without letting raw card data anywhere near your own server. In our deployments, we've built this contract for processors ranging from established regional banks to boutique PSPs with a single REST endpoint, and the mistakes are almost always the same handful: declaring a capability that isn't implemented yet, storing something PCI says you shouldn't, or skipping sandbox testing until the week of go-live.

This guide walks through that contract end to end: when a custom plugin is actually the right call, how to scaffold the project, what IPaymentMethod requires of you, how the checkout UI and settings hook in, and how to test the whole thing without a real card number ever touching your logs.

When You Actually Need a Custom Payment Plugin

Before writing a line of C#, it's worth confirming this is the right amount of engineering for the problem. Adding a new payment option to a nopCommerce store has three realistic paths, and picking the wrong one wastes either budget or reliability.

Approach What it involves Business value
Off-the-shelf plugin Install and configure a published plugin — PayPal, Stripe, and similar gateways already have one — through the admin. No custom code. Fastest path — live in hours, but only works if the gateway already has a maintained nopCommerce plugin.
Custom IPaymentMethod plugin (this guide) Implement the payment contract in-house against the gateway's own API: processing, capture/refund/void, checkout UI, settings. Full control — the only option when no plugin exists, but it's a real engineering project with PCI and testing obligations.
Turnkey professional implementation A specialized nopCommerce team builds, tests, and maintains the plugin for you, including a PCI-aware architecture and a sandbox-to-production cutover. Lowest risk — you get the same flexibility without carrying the compliance and maintenance burden alone.

If a maintained plugin for your gateway already exists, take that path — our guide to setting up PayPal in nopCommerce walks through exactly that kind of admin-side configuration, sandbox testing included. But if the gateway is regional, in-house, or genuinely absent from the plugin ecosystem, the rest of this article is where you land. The turnkey option is worth keeping in your back pocket too, once you see the compliance surface area in the security section below.

The IPaymentMethod Contract

Every payment plugin implements IPaymentMethod, an interface in nopCommerce's Nop.Services.Payments namespace that extends the general-purpose IPlugin base every plugin type shares. The extra surface it adds is what makes a payment plugin different from a widget or a shipping provider: it has to describe its own capabilities before nopCommerce will let a store manager turn on the buttons for them.

Key fact: Every capability flag on IPaymentMethodSupportCapture, SupportRefund, SupportVoid, and the rest — feeds directly into which action buttons nopCommerce's admin Order details page shows a store manager. Setting a flag to true is a promise that the matching method is fully implemented, not a placeholder for later.

Since nopCommerce moved its service layer to async/await, every member you implement returns a Task. Grouped by what they actually do, the contract breaks into three jobs:

Processing
ProcessPaymentAsync and PostProcessPaymentAsync authorize or charge the order, and handle any redirect back from a hosted gateway page.
Presentation
GetPublicViewComponent, GetPaymentInfoAsync, and ValidatePaymentFormAsync render and validate the payment step at checkout.
Lifecycle
CaptureAsync, RefundAsync, VoidAsync, and the recurring-payment methods, gated behind the Support* flags above.
Diagram of the IPaymentMethod lifecycle showing checkout calling ProcessPaymentAsync, an optional redirect through PostProcessPaymentAsync, and later admin-triggered Capture, Refund, and Void calls
The IPaymentMethod lifecycle: how nopCommerce calls into your plugin from checkout through capture, refund, and void.

PaymentMethodType: standard vs. redirection

The PaymentMethodType property decides which of those two flows applies. Standard collects payment details on your own checkout page — a hosted field or a tokenized form — and calls the gateway from the server inside ProcessPaymentAsync. Redirection sends the customer to the gateway's own hosted page and brings them back through PostProcessPaymentAsync, which is the right choice whenever the gateway, not you, needs to hold PCI scope for card entry.

Scaffolding the Plugin Project

If you haven't scaffolded a nopCommerce plugin before, start with our full walkthrough on creating a plugin in nopCommerce 4.80 — it covers the class library setup, the IPlugin/BasePlugin lifecycle, and getting a first build showing up under Local Plugins. A payment plugin uses the same project shape, with a plugin.json descriptor that tells nopCommerce it belongs in the Payments group:

{
  "Group": "Payments",
  "FriendlyName": "Acme Pay",
  "SystemName": "Payments.AcmePay",
  "Version": "1.00",
  "SupportedVersions": [ "4.80" ],
  "Author": "Your Company",
  "DisplayOrder": 1,
  "FileName": "Payments.AcmePay.dll"
}

Inside the project, keep the payment-specific pieces separated the same way the framework expects: the plugin class implementing IPaymentMethod at the root, an Areas/Admin folder for the settings controller and view, a Components folder for the checkout view component, and a Models folder for the payment-info and settings models. Nothing here is unique to payments — it's the same layout as any other plugin, just with a stricter interface to fill in.

Implementing Payment Processing

The heart of the contract is ProcessPaymentAsync. It receives a ProcessPaymentRequest carrying the order total, order GUID, and any custom values your checkout form collected, and it has to return a ProcessPaymentResult that either authorizes the order or adds an error nopCommerce shows the customer.

public class AcmePayPaymentMethod : BasePlugin, IPaymentMethod
{
    public bool SupportCapture => true;
    public bool SupportPartiallyRefund => true;
    public bool SupportRefund => true;
    public bool SupportVoid => true;
    public RecurringPaymentType RecurringPaymentType => RecurringPaymentType.NotSupported;
    public PaymentMethodType PaymentMethodType => PaymentMethodType.Standard;
    public bool SkipPaymentInfo => false;

    public async Task<ProcessPaymentResult> ProcessPaymentAsync(ProcessPaymentRequest processPaymentRequest)
    {
        var result = new ProcessPaymentResult();

        var charge = await _acmePayClient.ChargeAsync(new ChargeRequest
        {
            OrderGuid = processPaymentRequest.OrderGuid,
            AmountMinorUnits = (int)(processPaymentRequest.OrderTotal * 100),
            Token = processPaymentRequest.CustomValues["AcmePayToken"] as string
        });

        if (!charge.Success)
        {
            result.AddError(charge.DeclineReason);
            return result;
        }

        result.NewPaymentStatus = PaymentStatus.Authorized;
        result.AuthorizationTransactionId = charge.TransactionId;
        return result;
    }
}

Notice what's missing on purpose: no card number, no CVV, anywhere in that method. The checkout form tokenizes the card directly with the gateway's own client-side script and hands ProcessPaymentAsync a token, not a PAN. Your plugin never becomes a place raw card data passes through.

Watch out: webhooks can fire more than once

A gateway webhook or IPN callback can arrive twice for the same transaction — retried delivery is standard practice, not a bug. Key your order-update logic off the gateway's transaction ID rather than the fact that a webhook call happened, or a retried callback can capture or refund a payment a second time.

Building the Checkout Experience

Presentation is the other half of the contract, and it's what a customer actually sees. GetPublicViewComponent points nopCommerce at the view component that renders your payment form on the checkout page, and GetPaymentInfoAsync reads whatever the customer submitted back into a ProcessPaymentRequest before ProcessPaymentAsync ever runs.

public Type GetPublicViewComponent()
{
    return typeof(PaymentAcmePayViewComponent);
}

public async Task<IList<string>> ValidatePaymentFormAsync(IFormCollection form)
{
    var errors = new List<string>();
    if (string.IsNullOrEmpty(form["AcmePayToken"]))
        errors.Add("Payment details are missing or the token expired. Please try again.");
    return errors;
}

public async Task<ProcessPaymentRequest> GetPaymentInfoAsync(IFormCollection form)
{
    return new ProcessPaymentRequest
    {
        CustomValues =
        {
            ["AcmePayToken"] = form["AcmePayToken"].ToString()
        }
    };
}

The view component itself renders a partial view with the gateway's hosted-field JavaScript included, which posts a token into a hidden AcmePayToken field before the checkout form submits. If your gateway also needs the store to expose order data back to it — for a status lookup or a signed callback endpoint — our practical walkthrough of nopCommerce's API documentation covers what's actually available for that without building a bespoke controller from scratch.

Capture, Refund & Void Support

Whether you need the lifecycle methods at all depends on how ProcessPaymentAsync settles the charge. Two shapes cover almost every gateway.

Same-session sale
ProcessPaymentAsync both authorizes and captures the funds immediately. SupportCapture stays false, and there's no separate CaptureAsync to implement — the order is paid the moment checkout completes.
Authorize, then capture later
ProcessPaymentAsync only authorizes; a store manager captures from the Order details page whenever the item ships. CaptureAsync, RefundAsync, and VoidAsync all need real implementations, called against the gateway's transaction ID from that original authorization.

The Support flags aren't a checklist to fill in later — they're a promise to nopCommerce's admin UI. Set SupportRefund to true before Refund is implemented, and a store manager can click a button that throws in production.

BSS Engineering

Refunds and voids are issued against that stored transaction ID, never against the card itself. Your plugin only ever needs to remember a token and a transaction reference — the card data lives at the gateway, permanently.

Security, PCI Compliance & Testing

A custom payment plugin carries a compliance surface that a theme or a shipping-rate plugin never touches. Getting the architecture right up front — tokenize at the gateway, never store a PAN — is what keeps your store's PCI DSS scope down to a self-assessment questionnaire instead of a full on-site audit. Our broader nopCommerce security hardening checklist covers the rest of the store; this section is specific to what a payment plugin adds on top of it.

Infographic showing four PCI scope reduction practices: no card data stored, tokenized at the gateway, TLS 1.2+ enforced, and sandbox tested before go-live
Where PCI scope actually lives when your plugin tokenizes at the gateway instead of touching card data directly.

Before you flip the switch to production

  • No raw card number, CVV, or track data ever reaches your database or your logs — tokenize at the gateway or through a hosted field instead.
  • TLS 1.2 or higher is enforced on every endpoint your plugin calls, with certificate validation left on in production.
  • Sandbox credentials are exercised through a full order: authorize, capture, partial refund, and void, not just a single successful charge.
  • Webhook and IPN callbacks verify a signature before touching order state — the payload alone is never trusted.
  • Declined, expired-token, and network-timeout paths are tested deliberately, not just the happy path.
  • API keys and secrets are stored through nopCommerce's settings service, never hard-coded or checked into source control.

Frequently Asked Questions

One plugin maps to one gateway integration and one PaymentMethodType. If you need to support several gateways, build or install a separate IPaymentMethod plugin for each — nopCommerce lets a store enable multiple payment methods at checkout and the customer picks one.
Yes. Since nopCommerce moved its service layer to async/await, IPaymentMethod's members are Task-returning, including ProcessPaymentAsync, CaptureAsync, RefundAsync, and VoidAsync. A synchronous implementation will not compile against the 4.80 interface.
Standard collects payment details on your own checkout page, usually through a hosted field or tokenized form, and calls the gateway from the server. Redirection sends the customer to the gateway's own hosted page and brings them back through PostProcessPaymentAsync, which is the right choice when the gateway needs to hold PCI scope instead of you.
Yes. Every major gateway ships a sandbox environment with test card numbers and simulated webhooks. Build and test the entire plugin against sandbox credentials first; switching to live credentials should be a settings change, not a code change.
No, and you shouldn't. Refunds and voids are issued against the gateway's transaction ID from the original authorization, not the card itself — the gateway holds the card data, and your plugin only ever holds a token and a transaction reference.

Final Thoughts

Building a nopCommerce payment plugin is a contract-driven job, not a creative one. Implement IPaymentMethod's processing, presentation, and lifecycle members honestly, keep card data at the gateway instead of on your own server, and test every capability you turn on before a store manager ever sees the button for it. Do that, and the plugin behaves exactly like the built-in gateways sitting next to it in the checkout list — which is the whole point.

If the gateway you need already has a maintained plugin, take the shorter path in our PayPal setup guide below. If it doesn't, or the compliance and testing surface here is more than your team wants to own directly, our engineers build and harden custom nopCommerce payment integrations for a living — reach out and we'll scope it.