nopCommerce プラグインが重要な理由
nopCommerce は世界中で 60,000 を超えるオンラインストアを支えています。.NET 上で動作する主要なオープンソース e コマースプラットフォームであり、そのプラグインアーキテクチャがエコシステム全体を動かすエンジンとなっています。あらゆる決済ゲートウェイ連携、あらゆる配送キャリア、あらゆるウィジェット、そしてあらゆるカスタム機能がプラグインとして存在します。
nopCommerce プラグインの構築方法を理解すれば、市場で最も高機能な e コマースプラットフォームの 1 つを拡張でき、それをビジネスとして展開することも可能です。本ガイドでは、最初のプラグインの雛形作成から本番環境へのデプロイパターンまで、すべてを解説します。
私たちは 2009 年から nopCommerce プラグインを構築しており、カタログには 38 以上のプラグインを揃えています。その中には 12 種類のブラジル向け決済ゲートウェイ連携や、このプラットフォームで唯一となる完全な PIX 決済ソリューションも含まれます。本ガイドは、数百万件の取引を処理するプラグインを構築してきた中で学んだ知見を反映したものです。
プラグインアーキテクチャ: その基盤
プラグインインターフェースの階層構造
すべての nopCommerce プラグインは、Nop.Services.Plugins 内の IPlugin インターフェースから始まります。すべてを一から実装させるのではなく、nopCommerce は定型処理を担う抽象クラス BasePlugin と、一般的なプラグイン種別向けの一連の特化インターフェースを提供しています。
| インターフェース | 目的 | 例 |
|---|---|---|
| IPaymentMethod | 決済処理 (オーソリ、キャプチャ、返金、取消) | PayPal、Stripe、PIX、MercadoPago |
| IShippingRateComputationMethod | キャリアからの配送料を計算 | UPS、FedEx、Correios (ブラジル) |
| ITaxProvider | 管轄区域ごとの税率を計算 | Avalara、TaxJar |
| IWidgetPlugin | 特定のゾーンに UI ウィジェットを描画 | ライブチャット、ニュースレターのポップアップ、Trello カード |
| IExchangeRateProvider | 通貨の為替レートを取得 | ECB、ブラジル中央銀行 |
| IDiscountRequirementRule | カスタム割引ロジック | 「ブラジルの顧客は 10% オフ」 |
| IExternalAuthenticationMethod | ソーシャルログインと SSO | Google、Facebook、Azure AD |
| IMultiFactorAuthenticationMethod | MFA プラグイン (4.40 以降) | Google Authenticator、SMS OTP |
| IPickupPointProvider | 店舗受け取り拠点 | ローカル店舗ネットワーク、ロッカーシステム |
| IMiscPlugin | 汎用的な拡張 | DNS 管理、アナリティクス、各種連携 |
ユースケースに最も適したインターフェースを選択してください。IPaymentMethod は決済のライフサイクル全体 (ProcessPaymentAsync、PostProcessPaymentAsync、RefundAsync、VoidAsync、CaptureAsync) を提供する一方、IMiscPlugin は標準的なカテゴリに当てはまらないものに対応するための逃げ道となります。
プロジェクト構成: ステップバイステップ
1. クラスライブラリプロジェクトを作成する
.NET 9 をターゲットとする新しい Class Library プロジェクトを作成します。推奨される命名規則は Nop.Plugin.{Group}.{Name} です。
Nop.Plugin.Payments.PayPalCommerce
Nop.Plugin.Shipping.FedEx
Nop.Plugin.Widgets.WhatsApp
Nop.Plugin.Misc.GoDaddy
プロジェクトはソリューションルートの \Plugins ディレクトリに配置します (デプロイ先である \Nop.Web\Plugins の中ではありません)。.csproj の出力パスを $(SolutionDir)\Presentation\Nop.Web\Plugins\{PluginOutputDirectory} に設定し、CopyLocalLockFileAssemblies を有効にして NuGet の依存関係が出力先にコピーされるようにします。
2. plugin.json を作成する
すべてのプラグインには、メタデータを記述した plugin.json ファイルが必要です。
{
"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"
}
SystemName はアセンブリ名と一致している必要があります。SupportedVersions は、どの nopCommerce バージョンでこのプラグインをインストールできるかを決定します。nopCommerce をアップグレードしてプラグインが動かなくなった場合は、まずこのフィールドを確認してください。多くのプラグインは、サポート対象バージョンを更新するだけで済みます。
3. プラグインクラスを実装する
適切なプラグインインターフェースを実装し、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);
}
}
InstallAsync、UninstallAsync、UpdateAsync のオーバーライドは、ライフサイクルのフックとなります。必ず基底クラスの実装を呼び出してください。base.InstallAsync() がプラグインシステムへの登録を処理します。
4. 設定 (管理 UI) を追加する
設定を持つプラグインの場合は、MVC コントローラー、モデル、Razor ビューを作成します。
- Controllers/{Name}Controller.cs —
[AuthorizeAdmin]と[Area(AreaNames.ADMIN)]で修飾します。BasePluginControllerを継承します。 - Models/ConfigurationModel.cs — 設定用プロパティと、ローカライズのための
[NopResourceDisplayName]属性を持つ素朴なクラスです。 - Views/Configure.cshtml —
_ConfigurePluginレイアウトを使用します。_ViewImports.cshtmlも含めてください (既存のプラグインからコピーします)。
設定用ルートは、IRouteProvider を実装する RouteProvider.cs に登録します。
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;
}
決済プラグインの構築: 実務パターン
決済プラグインは最も一般的で、かつ最も複雑なプラグイン種別です。以下は、SplatDev で用いている本番向けパターンです。
中核となるコンポーネント
- 設定クラス: API キー、URL、モード (サンドボックス/本番)、機能トグルのプロパティを持つ
ISettingsを実装したクラスです。シークレットはISettingServiceに保存し、認証情報を決してハードコードしないでください。 - Defaults クラス: ルート名、システム名、設定 URL、API エンドポイントなどの定数を持つ静的クラスです。マジックストリングをロジックから排除します。
- サービスクラス: 実際の連携ロジックです。決済ゲートウェイへの HTTP 呼び出し、レスポンスの解析、Webhook の処理を担います。DI 経由で注入し、テスト可能に保ちます (インターフェース + 実装)。
- イベントコンシューマー:
IConsumer<OrderPlacedEvent>のようなインターフェースを実装してストアのイベントに反応します。取引確認の送信や、決済後のワークフローのトリガーなどを行います。
決済フロー: 実装が必須のメソッド
| メソッド | 呼び出されるタイミング | 行うべき処理 |
|---|---|---|
ProcessPaymentAsync |
顧客が「支払う」をクリックしたとき | 決済をオーソリまたはキャプチャします。成功/失敗と取引 ID を返します。 |
PostProcessPaymentAsync |
決済処理の完了後 | 外部ゲートウェイ (PayPal、PagSeguro) にリダイレクトするか、確認ページを表示します。 |
RefundAsync |
管理者が返金を開始したとき | ゲートウェイを通じて返金を処理します。成功/失敗を返します。 |
VoidAsync |
管理者がオーソリを取り消したとき | 保留中のオーソリを取り消します。決済がまだキャプチャされていない場合にのみ適用できます。 |
CaptureAsync |
管理者がオーソリ済みの決済をキャプチャするとき | 以前にオーソリした金額をキャプチャします。オーソリ後にキャプチャするフローで一般的です。 |
GetPaymentMethodDescriptionAsync |
チェックアウトページの描画時 | 決済方法の表示名、説明、ロゴを返します。 |
GetAdditionalHandlingFeeAsync |
注文合計の計算時 | 決済方法が課す追加手数料を返します。なければゼロを返します。 |
ブラジル向け決済プラグインの例: PIX
ブラジルの即時決済システムである PIX には、特定のフローが必要です。ペイロード (EMV 文字列) を含む QR コードを生成し、顧客に表示し、決済を受領した際の Webhook 確認を待ち受けます。以下がそのパターンです。
- ProcessPaymentAsync: PSP (PagSeguro、MercadoPago など) を呼び出して PIX の請求を作成します。取引 ID (
txid) と EMV ペイロードを保存します。 - PostProcessPaymentAsync: (EMV ペイロードから生成した) QR コードと「コピー&ペースト」用コードを描画します。決済ステータスを
Pendingに設定します。 - Webhook ハンドラー: PIX が決済を確認すると、PSP が Webhook を送信します。注文ステータスを更新し、
OrderPaidEventをトリガーします。
これは、SplatDev のブラジル向け決済プラグイン 12 種すべての背後にあるパターンです。PagSeguro、MercadoPago、Cielo、Banco Inter、Pagar.me、Rede、GetNet、PicPay、NuPay、Braspag、InfinityPay を通じて、PIX、boleto、分割払いのクレジットカード決済を処理しています。
ウィジェット: テーマに手を加えずに UI を追加する
ウィジェットプラグインは IWidgetPlugin を実装し、どのウィジェットゾーンに描画するかを指定する必要があります。
public Task<IList<string>> GetWidgetZonesAsync()
{
return Task.FromResult<IList<string>>(new List<string>
{
PublicWidgetZones.HomepageBeforeBestSellers,
PublicWidgetZones.ProductDetailsAfterPictures
});
}
次に GetPublicViewComponentAsync を実装して、ウィジェットの HTML を返します。ウィジェットシステムは、登録した各ゾーンに対してこのメソッドを呼び出します。
一般的なウィジェットのパターン: ニュースレター登録ポップアップ、ライブチャットボタン、信頼バッジ、クッキー同意バナー、WhatsApp のフローティングボタン、最近閲覧した商品、ソーシャルプルーフ通知など。
データベースアクセス: IRepository<T> とマイグレーション
nopCommerce はデータアクセスに IRepository<T> を介したリポジトリパターンを使用します。カスタムテーブルを必要とするプラグインの場合は次のようにします。
- エンティティクラスを定義します (
Idプロパティを持つ必要があります)。 - エンティティを Entity Framework Core に登録する
ObjectContextを作成します。 InstallAsync内で_dbContext.InstallAsync()を使用してテーブルを作成します。UninstallAsync内で_dbContext.UninstallAsync()を使用してテーブルを削除します。
プラグインのバージョン間のスキーマ移行については、現在のスキーマバージョンを確認して必要な ALTER TABLE 文を適用する MigrationManager を UpdateAsync のオーバーライドに追加することを検討してください。これはプラグインにとって FluentMigrator よりも簡潔で、移行ロジックを一箇所にまとめられます。
テスト: プラグインを製品として扱う
SplatDev では、すべてのプラグインを独自のテストスイートを備えた製品として扱っています。
- ユニットテスト (xUnit): サービス層を単体でテストします。
ISettingService、IRepository<T>、および任意の HTTP クライアントをモックします。決済ロジック、税計算、配送料計算は、ビジネスロジックに対して 100% のユニットテストカバレッジを持つべきです。 - 統合テスト: テスト用の nopCommerce インスタンスを立ち上げ、プラグインがインストール、設定され、ライフサイクル全体を通じて処理されることを検証します。決済ゲートウェイにはサンドボックス/テスト用の認証情報を使用します。
- ローカライゼーションテスト: サポートを謳うすべての言語について、ロケールリソースが存在することを検証します。リソース文字列の欠落は、最も一般的なプラグインの不具合です。
- StyleCop 準拠: nopCommerce は StyleCop への準拠を求めます。CI パイプラインで実行してください。コードレビューの前に細かな問題を検出できます。
よくある落とし穴 (とその回避方法)
1. base.InstallAsync() を呼び出さない
InstallAsync をオーバーライドしながら基底クラスを呼び出さないと、プラグインはプラグインシステムに登録されず、管理画面に表示されません。オーバーライドの最後に必ず await base.InstallAsync() を含めてください。
2. 認証情報をハードコードする
API キー、トークン、シークレットは ISettingService に保存し、plugin.json やクラス定数としてハードコードしないでください。設定ページで管理者がこれらの値を構成できるようにし、適切な場合には暗号化して永続化すべきです。
3. バージョン互換性を無視する
nopCommerce のバージョンはプラグインの動作を壊すことがあります。plugin.json の SupportedVersions フィールドは飾りではありません。nopCommerce はプラグインを読み込む前にこれを確認します。nopCommerce をアップグレードする際は、新しいバージョンに対してプラグインをテストし、このフィールドを更新してください。テストせずに番号だけを上げないでください。
4. リクエストスレッドをブロックする
すべてのプラグインメソッドが非同期なのには理由があります。決済プロセッサが外部 API を呼び出す場合、その呼び出しは非同期であるべきです。.Result や .Wait() でスレッドをブロックすると、負荷時にパフォーマンスが低下し、デッドロックを引き起こす可能性があります。
5. イベントコンシューマーがない
設定ページしか提供しないプラグインは、要点を外しています。nopCommerce プラグインの真の力は、イベントコンシューマー、すなわち OrderPlacedEvent、CustomerRegisteredEvent、ShipmentDeliveredEvent といったストアのイベントに反応することから生まれます。プラグインを管理 UI だけでなく、ストアのビジネスイベントに組み込んでください。
nopCommerce 4.90: プラグイン開発者向けの新機能
nopCommerce 4.90 は .NET 9 をターゲットとし、プラグイン開発者向けにいくつかの改善をもたらします。
- .NET 9 ランタイム: プラグインはこれまでで最速の .NET 上で動作し、JIT の改善、GC の動的適応、ループ最適化の恩恵を自動的に受けられます。
- C# 13 サポート: プラグインコードで
paramsコレクション、ref structインターフェース、partial プロパティを使用できます。 - NuGet 依存関係の更新: 基盤となるパッケージ (Autofac、FluentValidation など) が更新されました。プラグインの依存関係ツリーをテストしてください。
- IMultiFactorAuthenticationMethod: MFA プラグインが、独自のインターフェースを持つ第一級の概念となりました。
- プラグインテンプレートの改善: 公式の nopCommerce プラグイン用 Visual Studio テンプレートが 4.90 向けに更新されました。これを出発点として使用してください。
この先へ進むために
nopCommerce プラグイン開発は、キャリアを築くスキルです。nopCommerce のマーケットプレイスは活発で成長しており、しっかり作られたプラグインは更新とサポートを通じて継続的な収益を生み出します。SplatDev では、プラグインが中核事業です。store.splatdev.com の当社ストアには 38 以上のプラグインが揃っており、長年にわたり Gold Solutions Partner を務めています。
nopCommerce 上で e コマースプロジェクトを構築するなら、すべてを一から作る必要はありません。当社のカタログは、ブラジル向け決済、配送 (Correios、JadLog、Braspress)、ERP 連携、カスタム機能を網羅しています。当社にないものが必要であれば、カスタムプラグインも構築します。
nopCommerce ストアの構築や拡張の準備はできていますか。 当社のチームは 2009 年からこれを行っており、エコシステムの中でほぼ誰よりも長い経験を持っています。
または、当社のプラグインカタログをご覧ください: store.splatdev.com