NopCommerce plugin geliştirme
Dec 16, 2023

Header

NopCommerce

NopCommerce açık kaynak kodlu , ücretsiz, tema ve eklentiler açısından oldukça zengin bir e-ticaret platformudur. Genişletilebilir bir yapıya sahiptir.

aşağıdaki adresten güncel versiyonu indirebilirsiniz.

download-nopcommerce🔗

Bu yazımda mevcut en güncel versiyon olan 4.60.5 versiyonunu kullanacağım. (.NET7)


NopCommerce plugin yapısı

NopCommerce’de Class Library projeleriyle plugin oluşturabilirsiniz. Pluginler birbirinden plugin.json içerisindeki SystemName ile ayrılır. O yüzden SystemName alanının unique olmasına dikkat etmeliyiz.

Yüklü pluginlerin bilgisi Presentation/Nop.Web/App_Data/plugin.json dosyasında bulunur. Herhangi bir plugin’ini build ettiğinizde , build olan plugin dosyaları Nop.Web/Plugins/ klasörü içerisine atılır. Plugin’in .csproj dosyasındaki OutputPath den dosya yolunu inceleyebilirsiniz.

NopCommerce’de bütün pluginler IPlugin interface’inden türer. Interfacedeki methodların detaylarına, yazının devamındaki “Plugin Geliştirme” kısmında bahsedeceğim.


Plugin tipleri

NopCommerce’de IPlugin interface’ini kullanan oldukça fazla plugin interface’i bulunmakta. bu yazıdaki versiyonda bulunan plugin interface’leri bunlar;

  • IPaymentMethod
  • IShippingRateComputationMethod
  • IPickupPointProvider
  • ITaxProvider
  • IExchangeRateProvider
  • IDiscountRequirementRule
  • IExternalAuthenticationMethod
  • IMultiFactorAuthenticationMethod
  • IWidgetPlugin
  • IMiscPlugin

Biz bu yazımızda en çok geliştirme yapılan plugin tiplerinden biri olan IPaymentMethod interface’ini kullanarak bir “Ödeme Yöntemi Plugini” oluşturalım.


Nasıl?

Başlangıç için iki farklı yöntem var , ödeme methodlarında ben genellikle temiz bir Class Library projesi açmak yerine , içerisinde en az kod bulunan ödeme yöntemi olan Nop.Plugin.Payments.Manual pluginini komple kopyalayıp , projedeki tüm değişmesi gereken class , .csproj , .json dosyalarını bir text editor (vscode) yardımıyla değiştirip sonrasında plugini solution içerisine ekliyorum. Bu tamamen kişisel tercih meselesi.

Bunun yerine Plugins klasörü altında yeni bir Class Library oluşturabilirsiniz. size kalmış. Bu yazıda ilk yöntem ile devam edelim.

Proje oluşturma

NopCommerce tarafından önerilen proje isimlendirme şekli şu şekilde;

Nop.Plugin.{Group}.{Name}

burdaki “Group” plugin grubu anlamına geliyor , biz ödeme methodu yapmaya karar verdiğimiz için bizim proje ismi şu şekilde olacak;

Nop.Plugin.Payments.YYAPay

.csproj dosyası düzenleme

öncelikle Nop.Plugin.Payments.YYAPay.csproj dosyasında aşağıdaki alanlarda değişiklikleri yapalım.

  • Copyright
  • Company
  • Authors
  • PackageProjectUrl (varsa)
  • RepositoryUrl (varsa)
  • OutputPath
<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>net7.0</TargetFramework>
    <Copyright>Copyright © YYA</Copyright>
    <Company>YYA</Company>
    <Authors>YYA</Authors>
    <PackageLicenseUrl></PackageLicenseUrl>
    <PackageProjectUrl>https://yyavci.com/</PackageProjectUrl>
    <RepositoryUrl>https://github/yyavci/YYA.Blog.NopCommercePlugin</RepositoryUrl>
    <RepositoryType>Git</RepositoryType>
    <OutputPath>....PresentationNop.WebPluginsPayments.YYAPay</OutputPath>
    <OutDir>$(OutputPath)</OutDir>
    <!--Set this parameter to true to get the dlls copied from the NuGet cache to the output of your project.
    You need to set this parameter to true if your plugin has a nuget package
    to ensure that the dlls copied from the NuGet cache to the output of your project-->
    <CopyLocalLockFileAssemblies>false</CopyLocalLockFileAssemblies>
  </PropertyGroup>

  <ItemGroup>
    <None Remove="logo.jpg" />
    <None Remove="plugin.json" />
    <None Remove="ViewsConfigure.cshtml" />
    <None Remove="ViewsPaymentInfo.cshtml" />
    <None Remove="Views_ViewImports.cshtml" />
  </ItemGroup>

  <ItemGroup>
    <Content Include="logo.jpg">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </Content>
    <Content Include="plugin.json">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </Content>
    <Content Include="ViewsConfigure.cshtml">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </Content>
    <Content Include="ViewsPaymentInfo.cshtml">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </Content>
    <Content Include="Views_ViewImports.cshtml">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </Content>
  </ItemGroup>

  <ItemGroup>
    <ProjectReference Include="....PresentationNop.WebNop.Web.csproj" />
    <ClearPluginAssemblies Include="$(MSBuildProjectDirectory)....BuildClearPluginAssemblies.proj" />
  </ItemGroup>

  <!-- This target execute after "Build" target -->
  <Target Name="NopTarget" AfterTargets="Build">
    <!-- Delete unnecessary libraries from plugins path -->
    <MSBuild Projects="@(ClearPluginAssemblies)" Properties="PluginPath=$(MSBuildProjectDirectory)$(OutDir)" Targets="NopClear" />
  </Target>

</Project>

Rename işlemleri

Kopyaladığımız Class Library’deki tüm namespace’leri kendi projemize uygun olarak değiştirelim. (Manual geçen her yeri YYAPay olarak değiştirdim)

Plugin.json dosyası

Son olarakta plugin.json dosyasını düzenleyelim. Burada SystemName ve FileName önemli.

{
  "Group": "Payment methods",
  "FriendlyName": "YYA Pay",
  "SystemName": "Payments.YYAPay",
  "Version": "1.0.0",
  "SupportedVersions": ["4.60"],
  "Author": "yya",
  "DisplayOrder": 1,
  "FileName": "Nop.Plugin.Payments.YYAPay.dll",
  "Description": "This plugin enables yya pay"
}

Projenin son görünümü;

Structure

Plugin’in solution içerisine eklenmesi

Klasörü komple kopyalayıp bir text editor (ör;vscode) yardımıyla düzenlemeleri yaptığımız için henüz plugin’inimizi solution içerisine eklememiştik.Solution Explorer‘da Plugins altına Add Existing Project… ile .csproj dosyasını seçip ekleyebiliriz.

şuan pluginimiz sorunsuz build oluyor olmalı.

ek olarak logoyu değiştirmeyi unutmayın :)


Plugin geliştirme

NopCommerce’de plugin geliştirirken öncelikle BasePlugin abstract class’ının bazı methodlarını inceleyelim. Bu methodlar tüm pluginlerde mevcut. override edilebilir.

BasePlugin methodları

GetConfigurationPageUrl

/// <summary>
/// Gets a configuration page URL
/// </summary>
public override string GetConfigurationPageUrl()
{
    return $"{_webHelper.GetStoreLocation()}Admin/PaymentYYAPay/Configure";
}

GetConfigurationPageUrl methodunda , admin panel üzerinden plugin ile ilgili tüm ayarların yapılabileceği View’in yolunu veriyoruz. Plugin içerisindeki Views/Configure.cshtml dosyasını inceleyebilirsiniz.

InstallAsync

/// <summary>
/// Install the plugin
/// </summary>
/// <returns>A task that represents the asynchronous operation</returns>
public override async Task InstallAsync()
{
    //settings
    var settings = new YYAPayPaymentSettings
    {
        TransactMode = TransactMode.Pending
    };
    await _settingService.SaveSettingAsync(settings);
    //locales
    await _localizationService.AddOrUpdateLocaleResourceAsync(new Dictionary<string, string>
    {
        ["Plugins.Payments.YYAPay.Instructions"] = "This payment method stores credit card information in database (it's not sent to any third-party processor). In order to store credit card information, you must be PCI compliant.",
        ["Plugins.Payments.YYAPay.Fields.AdditionalFee"] = "Additional fee",
        ["Plugins.Payments.YYAPay.Fields.AdditionalFee.Hint"] = "Enter additional fee to charge your customers.",
        ["Plugins.Payments.YYAPay.Fields.AdditionalFeePercentage"] = "Additional fee. Use percentage",
        ["Plugins.Payments.YYAPay.Fields.AdditionalFeePercentage.Hint"] = "Determines whether to apply a percentage additional fee to the order total. If not enabled, a fixed value is used.",
        ["Plugins.Payments.YYAPay.Fields.TransactMode"] = "After checkout mark payment as",
        ["Plugins.Payments.YYAPay.Fields.TransactMode.Hint"] = "Specify transaction mode.",
        ["Plugins.Payments.YYAPay.PaymentMethodDescription"] = "Pay by credit / debit card"
    });
    await base.InstallAsync();
}

InstallAsync methodu içerisinde ise , plugin yüklenirken çalışacak kodlar eklenmeli. burda dil etiketlerini , plugin’in default ayarlarını ekleyebilirsiniz.

UninstallAsync

/// <summary>
/// Uninstall the plugin
/// </summary>
/// <returns>A task that represents the asynchronous operation</returns>
public override async Task UninstallAsync()
{
    //settings
    await _settingService.DeleteSettingAsync<YYAPayPaymentSettings>();
    //locales
    await _localizationService.DeleteLocaleResourcesAsync("Plugins.Payments.YYAPay");
    await base.UninstallAsync();
}

Plugin silinirken çalışacak kodlar ise UninstallAsync methodu içerisinde yer alır.

Bunların haricinde UpdateAsync , PreparePluginToUninstallAsync gibi methodlarda mevcut.

IPaymentMethod interface methodları

ProcessPaymentAsync

/// <summary>
/// Process a payment
/// </summary>
/// <param name="processPaymentRequest">Payment info required for an order processing</param>
/// <returns>
/// A task that represents the asynchronous operation
/// The task result contains the process payment result
/// </returns>
public Task<ProcessPaymentResult> ProcessPaymentAsync(ProcessPaymentRequest processPaymentRequest)
{
    var result = new ProcessPaymentResult
    {
        AllowStoringCreditCardNumber = true
    };
    switch (_yyaPayPaymentSettings.TransactMode)
    {
        case TransactMode.Pending:
            result.NewPaymentStatus = PaymentStatus.Pending;
            break;
        case TransactMode.Authorize:
            result.NewPaymentStatus = PaymentStatus.Authorized;
            break;
        case TransactMode.AuthorizeAndCapture:
            result.NewPaymentStatus = PaymentStatus.Paid;
            break;
        default:
            result.AddError("Not supported transaction type");
            break;
    }
    return Task.FromResult(result);
}

ProcessPaymentAsync methodu sipariş oluşmadan önce çalışır ve herhangi bir yönlendirme yapmadan direkt ödeme alacağınız ödeme tipleri için burayı kullanabilirsiniz.

PostProcessPaymentAsync

Task PostProcessPaymentAsync(PostProcessPaymentRequest postProcessPaymentRequest);

PostProcessPaymentAsync sipariş oluştuktan sonra çalışan methoddur. Bazı ödeme yöntemlerinde 3rd party payment gatewaylere yönlendirme yapmak gerekiyor. Bu methodda yönlendirme işlemini yapabilirsiniz.(3d ödemeler)

Presentation/Nop.Web.Framework/RemotePost classını gateway’e yönlendirme işlemleri için kullanabilirsiniz. (bunu ayrı bir blog yazısı konusu yapabiliriz)

GetAdditionalHandlingFeeAsync

Task<decimal> GetAdditionalHandlingFeeAsync(IList<ShoppingCartItem> cart);

Bu ödeme yöntemi için ek ücret alacaksanız bu methodda ek ücret bilgisini göndermeniz gerekir.

RefundAsync | VoidAsync

Task<RefundPaymentResult> RefundAsync(RefundPaymentRequest refundPaymentRequest);

Task<VoidPaymentResult> VoidAsync(VoidPaymentRequest voidPaymentRequest);

RefundAsync transaction tamamlandıktan yapılacak iade ve kısmi iade işlemlerinde kullanılacak methoddur.

VoidAsync henüz ödeme transactionı tamamlanmamış ödemelerde bu method ile ödeme iptali yapılır.

Dependency Injection | Routing

Plugin içerisinde dependency injection için , plugin klasörü altında /Infrastructure/ klasörü açılıp içerisine , NopStartup.cs classı oluşturulur. Bu class INopStartup interface’ini implement etmeli.

örnek class;

/// <summary>
/// Represents object for the configuring services on application startup
/// </summary>
public class NopStartup : INopStartup
{
    /// <summary>
    /// Add and configure any of the middleware
    /// </summary>
    /// <param name="services">Collection of service descriptors</param>
    /// <param name="configuration">Configuration of the application</param>
    public void ConfigureServices(IServiceCollection services, IConfiguration configuration)
    {
        services.AddHttpClient<OnboardingHttpClient>().WithProxy();
        services.AddScoped<ServiceManager>();
    }
    /// <summary>
    /// Configure the using of added middleware
    /// </summary>
    /// <param name="application">Builder for configuring an application's request pipeline</param>
    public void Configure(IApplicationBuilder application)
    {
    }
    /// <summary>
    /// Gets order of this startup configuration implementation
    /// </summary>
    public int Order => 3000;
}

ConfigureServices içerisinde dependency injection işlemlerini yapabilirsiniz. Başka middleware işlemleriniz varsa bu class doğru yer.

Routing işlemleri için ise , yine /Infrastructure/ klasörü altında RouteProvider.cs class’ı oluşturulur. Tüm routing işlemlerini bu class içerisinde yapabilirsiniz.

örnek olarak aşağıdaki paypal plugin’inin class’ını inceleyebilirsiniz.

public class RouteProvider : IRouteProvider
{
    /// <summary>
    /// Register routes
    /// </summary>
    /// <param name="endpointRouteBuilder">Route builder</param>
    public void RegisterRoutes(IEndpointRouteBuilder endpointRouteBuilder)
    {
        endpointRouteBuilder.MapControllerRoute(PayPalCommerceDefaults.ConfigurationRouteName,
            "Admin/PayPalCommerce/Configure",
            new { controller = "PayPalCommerce", action = "Configure" });
        endpointRouteBuilder.MapControllerRoute(PayPalCommerceDefaults.WebhookRouteName,
            "Plugins/PayPalCommerce/Webhook",
            new { controller = "PayPalCommerceWebhook", action = "WebhookHandler" });
    }
    /// <summary>
    /// Gets a priority of route provider
    /// </summary>
    public int Priority => 0;
}

Oluşturduğunuz tüm view’lerde ve plugin.json dosyasında “Build Action” seçeneği “Content”

“Copy to output directory” seçeneği ise , “Copy if newer” seçilmeli.


Kaynaklar