using Microsoft.AspNetCore.Http; using Nop.Core; using Nop.Core.Domain.Orders; using Nop.Core.Domain.Payments; using Nop.Plugin.Payments.Manual.Components; using Nop.Plugin.Payments.Manual.Models; using Nop.Plugin.Payments.Manual.Validators; using Nop.Services.Configuration; using Nop.Services.Localization; using Nop.Services.Orders; using Nop.Services.Payments; using Nop.Services.Plugins; namespace Nop.Plugin.Payments.Manual; /// /// Manual payment processor /// public class ManualPaymentProcessor : BasePlugin, IPaymentMethod { #region Fields protected readonly ILocalizationService _localizationService; protected readonly IOrderTotalCalculationService _orderTotalCalculationService; protected readonly ISettingService _settingService; protected readonly IWebHelper _webHelper; protected readonly ManualPaymentSettings _manualPaymentSettings; #endregion #region Ctor public ManualPaymentProcessor(ILocalizationService localizationService, IOrderTotalCalculationService orderTotalCalculationService, ISettingService settingService, IWebHelper webHelper, ManualPaymentSettings manualPaymentSettings) { _localizationService = localizationService; _orderTotalCalculationService = orderTotalCalculationService; _settingService = settingService; _webHelper = webHelper; _manualPaymentSettings = manualPaymentSettings; } #endregion #region Methods /// /// Process a payment /// /// Payment info required for an order processing /// /// A task that represents the asynchronous operation /// The task result contains the process payment result /// public Task ProcessPaymentAsync(ProcessPaymentRequest processPaymentRequest) { var result = new ProcessPaymentResult { AllowStoringCreditCardNumber = true }; switch (_manualPaymentSettings.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); } /// /// Post process payment (used by payment gateways that require redirecting to a third-party URL) /// /// Payment info required for an order processing /// A task that represents the asynchronous operation public Task PostProcessPaymentAsync(PostProcessPaymentRequest postProcessPaymentRequest) { //nothing return Task.CompletedTask; } /// /// Returns a value indicating whether payment method should be hidden during checkout /// /// Shopping cart /// /// A task that represents the asynchronous operation /// The task result contains true - hide; false - display. /// public Task HidePaymentMethodAsync(IList cart) { //you can put any logic here //for example, hide this payment method if all products in the cart are downloadable //or hide this payment method if current customer is from certain country return Task.FromResult(false); } /// /// Gets additional handling fee /// /// /// A task that represents the asynchronous operation /// The task result contains the additional handling fee /// public async Task GetAdditionalHandlingFeeAsync(IList cart) { return await _orderTotalCalculationService.CalculatePaymentAdditionalFeeAsync(cart, _manualPaymentSettings.AdditionalFee, _manualPaymentSettings.AdditionalFeePercentage); } /// /// Captures payment /// /// Capture payment request /// /// A task that represents the asynchronous operation /// The task result contains the capture payment result /// public Task CaptureAsync(CapturePaymentRequest capturePaymentRequest) { return Task.FromResult(new CapturePaymentResult { Errors = new[] { "Capture method not supported" } }); } /// /// Refunds a payment /// /// Request /// /// A task that represents the asynchronous operation /// The task result contains the result /// public Task RefundAsync(RefundPaymentRequest refundPaymentRequest) { return Task.FromResult(new RefundPaymentResult { Errors = new[] { "Refund method not supported" } }); } /// /// Voids a payment /// /// Request /// /// A task that represents the asynchronous operation /// The task result contains the result /// public Task VoidAsync(VoidPaymentRequest voidPaymentRequest) { return Task.FromResult(new VoidPaymentResult { Errors = new[] { "Void method not supported" } }); } /// /// Process recurring payment /// /// Payment info required for an order processing /// /// A task that represents the asynchronous operation /// The task result contains the process payment result /// public Task ProcessRecurringPaymentAsync(ProcessPaymentRequest processPaymentRequest) { var result = new ProcessPaymentResult { AllowStoringCreditCardNumber = true }; switch (_manualPaymentSettings.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); } /// /// Cancels a recurring payment /// /// Request /// /// A task that represents the asynchronous operation /// The task result contains the result /// public Task CancelRecurringPaymentAsync(CancelRecurringPaymentRequest cancelPaymentRequest) { //always success return Task.FromResult(new CancelRecurringPaymentResult()); } /// /// Gets a value indicating whether customers can complete a payment after order is placed but not completed (for redirection payment methods) /// /// Order /// /// A task that represents the asynchronous operation /// The task result contains the result /// public Task CanRePostProcessPaymentAsync(Order order) { ArgumentNullException.ThrowIfNull(order); //it's not a redirection payment method. So we always return false return Task.FromResult(false); } /// /// Validate payment form /// /// The parsed form values /// /// A task that represents the asynchronous operation /// The task result contains the list of validating errors /// public Task> ValidatePaymentFormAsync(IFormCollection form) { var warnings = new List(); //validate var validator = new PaymentInfoValidator(_localizationService); var model = new PaymentInfoModel { CardholderName = form["CardholderName"], CardNumber = form["CardNumber"], CardCode = form["CardCode"], ExpireMonth = form["ExpireMonth"], ExpireYear = form["ExpireYear"] }; var validationResult = validator.Validate(model); if (!validationResult.IsValid) warnings.AddRange(validationResult.Errors.Select(error => error.ErrorMessage)); return Task.FromResult>(warnings); } /// /// Get payment information /// /// The parsed form values /// /// A task that represents the asynchronous operation /// The task result contains the payment info holder /// public Task GetPaymentInfoAsync(IFormCollection form) { return Task.FromResult(new ProcessPaymentRequest { CreditCardType = form["CreditCardType"], CreditCardName = form["CardholderName"], CreditCardNumber = form["CardNumber"], CreditCardExpireMonth = int.Parse(form["ExpireMonth"]), CreditCardExpireYear = int.Parse(form["ExpireYear"]), CreditCardCvv2 = form["CardCode"] }); } /// /// Gets a configuration page URL /// public override string GetConfigurationPageUrl() { return $"{_webHelper.GetStoreLocation()}Admin/PaymentManual/Configure"; } /// /// Gets a type of a view component for displaying plugin in public store ("payment info" checkout step) /// /// View component type public Type GetPublicViewComponent() { return typeof(PaymentManualViewComponent); } /// /// Install the plugin /// /// A task that represents the asynchronous operation public override async Task InstallAsync() { //settings var settings = new ManualPaymentSettings { TransactMode = TransactMode.Pending }; await _settingService.SaveSettingAsync(settings); //locales await _localizationService.AddOrUpdateLocaleResourceAsync(new Dictionary { ["Plugins.Payments.Manual.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.Manual.Fields.AdditionalFee"] = "Additional fee", ["Plugins.Payments.Manual.Fields.AdditionalFee.Hint"] = "Enter additional fee to charge your customers.", ["Plugins.Payments.Manual.Fields.AdditionalFeePercentage"] = "Additional fee. Use percentage", ["Plugins.Payments.Manual.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.Manual.Fields.TransactMode"] = "After checkout mark payment as", ["Plugins.Payments.Manual.Fields.TransactMode.Hint"] = "Specify transaction mode.", ["Plugins.Payments.Manual.PaymentMethodDescription"] = "Pay by credit / debit card" }); await base.InstallAsync(); } /// /// Uninstall the plugin /// /// A task that represents the asynchronous operation public override async Task UninstallAsync() { //settings await _settingService.DeleteSettingAsync(); //locales await _localizationService.DeleteLocaleResourcesAsync("Plugins.Payments.Manual"); await base.UninstallAsync(); } /// /// Gets a payment method description that will be displayed on checkout pages in the public store /// /// /// return description of this payment method to be display on "payment method" checkout step. good practice is to make it localizable /// for example, for a redirection payment method, description may be like this: "You will be redirected to PayPal site to complete the payment" /// /// A task that represents the asynchronous operation public async Task GetPaymentMethodDescriptionAsync() { return await _localizationService.GetResourceAsync("Plugins.Payments.Manual.PaymentMethodDescription"); } #endregion #region Properties /// /// Gets a value indicating whether capture is supported /// public bool SupportCapture => false; /// /// Gets a value indicating whether partial refund is supported /// public bool SupportPartiallyRefund => false; /// /// Gets a value indicating whether refund is supported /// public bool SupportRefund => false; /// /// Gets a value indicating whether void is supported /// public bool SupportVoid => false; /// /// Gets a recurring payment type of payment method /// public RecurringPaymentType RecurringPaymentType => RecurringPaymentType.Manual; /// /// Gets a payment method type /// public PaymentMethodType PaymentMethodType => PaymentMethodType.Standard; /// /// Gets a value indicating whether we should display a payment information page for this plugin /// public bool SkipPaymentInfo => false; #endregion }