NopCommerce plugin geliştirme
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.
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ü;
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.