Try your search with a different keyword or use * as a wildcard.
using System.Xml;
using System.Xml.Serialization;
using Microsoft.AspNetCore.Http;
using Nop.Core;
using Nop.Core.Domain.Orders;
using Nop.Core.Domain.Payments;
using Nop.Core.Http.Extensions;
using Nop.Services.Catalog;
using Nop.Services.Customers;
namespace Nop.Services.Payments;
///
/// Payment service
///
public partial class PaymentService : IPaymentService
{
#region Fields
protected readonly ICustomerService _customerService;
protected readonly IHttpContextAccessor _httpContextAccessor;
protected readonly IPaymentPluginManager _paymentPluginManager;
protected readonly IPriceCalculationService _priceCalculationService;
protected readonly PaymentSettings _paymentSettings;
protected readonly ShoppingCartSettings _shoppingCartSettings;
#endregion
#region Ctor
public PaymentService(ICustomerService customerService,
IHttpContextAccessor httpContextAccessor,
IPaymentPluginManager paymentPluginManager,
IPriceCalculationService priceCalculationService,
PaymentSettings paymentSettings,
ShoppingCartSettings shoppingCartSettings)
{
_customerService = customerService;
_httpContextAccessor = httpContextAccessor;
_paymentPluginManager = paymentPluginManager;
_priceCalculationService = priceCalculationService;
_paymentSettings = paymentSettings;
_shoppingCartSettings = shoppingCartSettings;
}
#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 virtual async Task ProcessPaymentAsync(ProcessPaymentRequest processPaymentRequest)
{
if (processPaymentRequest.OrderTotal == decimal.Zero)
{
var result = new ProcessPaymentResult
{
NewPaymentStatus = PaymentStatus.Paid
};
return result;
}
//We should strip out any white space or dash in the CC number entered.
if (!string.IsNullOrWhiteSpace(processPaymentRequest.CreditCardNumber))
{
processPaymentRequest.CreditCardNumber = processPaymentRequest.CreditCardNumber.Replace(" ", string.Empty);
processPaymentRequest.CreditCardNumber = processPaymentRequest.CreditCardNumber.Replace("-", string.Empty);
}
var customer = await _customerService.GetCustomerByIdAsync(processPaymentRequest.CustomerId);
var paymentMethod = await _paymentPluginManager
.LoadPluginBySystemNameAsync(processPaymentRequest.PaymentMethodSystemName, customer, processPaymentRequest.StoreId)
?? throw new NopException("Payment method couldn't be loaded");
return await paymentMethod.ProcessPaymentAsync(processPaymentRequest);
}
///
/// 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 virtual async Task PostProcessPaymentAsync(PostProcessPaymentRequest postProcessPaymentRequest)
{
//already paid or order.OrderTotal == decimal.Zero
if (postProcessPaymentRequest.Order.PaymentStatus == PaymentStatus.Paid)
return;
var customer = await _customerService.GetCustomerByIdAsync(postProcessPaymentRequest.Order.CustomerId);
var paymentMethod = await _paymentPluginManager
.LoadPluginBySystemNameAsync(postProcessPaymentRequest.Order.PaymentMethodSystemName, customer, postProcessPaymentRequest.Order.StoreId)
?? throw new NopException("Payment method couldn't be loaded");
await paymentMethod.PostProcessPaymentAsync(postProcessPaymentRequest);
}
///
/// 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 virtual async Task CanRePostProcessPaymentAsync(Order order)
{
ArgumentNullException.ThrowIfNull(order);
if (!_paymentSettings.AllowRePostingPayments)
return false;
var customer = await _customerService.GetCustomerByIdAsync(order.CustomerId);
var paymentMethod = await _paymentPluginManager.LoadPluginBySystemNameAsync(order.PaymentMethodSystemName, customer, order.StoreId);
if (paymentMethod == null)
return false; //Payment method couldn't be loaded (for example, was uninstalled)
if (paymentMethod.PaymentMethodType != PaymentMethodType.Redirection)
return false; //this option is available only for redirection payment methods
if (order.Deleted)
return false; //do not allow for deleted orders
if (order.OrderStatus == OrderStatus.Cancelled)
return false; //do not allow for cancelled orders
if (order.PaymentStatus != PaymentStatus.Pending)
return false; //payment status should be Pending
return await paymentMethod.CanRePostProcessPaymentAsync(order);
}
///
/// Gets an additional handling fee of a payment method
///
/// Shopping cart
/// Payment method system name
///
/// A task that represents the asynchronous operation
/// The task result contains the additional handling fee
///
public virtual async Task GetAdditionalHandlingFeeAsync(IList cart, string paymentMethodSystemName)
{
if (string.IsNullOrEmpty(paymentMethodSystemName))
return decimal.Zero;
var customer = await _customerService.GetCustomerByIdAsync(cart.FirstOrDefault()?.CustomerId ?? 0);
var paymentMethod = await _paymentPluginManager.LoadPluginBySystemNameAsync(paymentMethodSystemName, customer, cart.FirstOrDefault()?.StoreId ?? 0);
if (paymentMethod == null)
return decimal.Zero;
var result = await paymentMethod.GetAdditionalHandlingFeeAsync(cart);
if (result < decimal.Zero)
result = decimal.Zero;
if (!_shoppingCartSettings.RoundPricesDuringCalculation)
return result;
result = await _priceCalculationService.RoundPriceAsync(result);
return result;
}
///
/// Gets a value indicating whether capture is supported by payment method
///
/// Payment method system name
///
/// A task that represents the asynchronous operation
/// The task result contains a value indicating whether capture is supported
///
public virtual async Task SupportCaptureAsync(string paymentMethodSystemName)
{
var paymentMethod = await _paymentPluginManager.LoadPluginBySystemNameAsync(paymentMethodSystemName);
if (paymentMethod == null)
return false;
return paymentMethod.SupportCapture;
}
///
/// Captures payment
///
/// Capture payment request
///
/// A task that represents the asynchronous operation
/// The task result contains the capture payment result
///
public virtual async Task CaptureAsync(CapturePaymentRequest capturePaymentRequest)
{
var paymentMethod = await _paymentPluginManager.LoadPluginBySystemNameAsync(capturePaymentRequest.Order.PaymentMethodSystemName)
?? throw new NopException("Payment method couldn't be loaded");
return await paymentMethod.CaptureAsync(capturePaymentRequest);
}
///
/// Gets a value indicating whether partial refund is supported by payment method
///
/// Payment method system name
///
/// A task that represents the asynchronous operation
/// The task result contains a value indicating whether partial refund is supported
///
public virtual async Task SupportPartiallyRefundAsync(string paymentMethodSystemName)
{
var paymentMethod = await _paymentPluginManager.LoadPluginBySystemNameAsync(paymentMethodSystemName);
if (paymentMethod == null)
return false;
return paymentMethod.SupportPartiallyRefund;
}
///
/// Gets a value indicating whether refund is supported by payment method
///
/// Payment method system name
///
/// A task that represents the asynchronous operation
/// The task result contains a value indicating whether refund is supported
///
public virtual async Task SupportRefundAsync(string paymentMethodSystemName)
{
var paymentMethod = await _paymentPluginManager.LoadPluginBySystemNameAsync(paymentMethodSystemName);
if (paymentMethod == null)
return false;
return paymentMethod.SupportRefund;
}
///
/// Refunds a payment
///
/// Request
///
/// A task that represents the asynchronous operation
/// The task result contains the result
///
public virtual async Task RefundAsync(RefundPaymentRequest refundPaymentRequest)
{
var paymentMethod = await _paymentPluginManager.LoadPluginBySystemNameAsync(refundPaymentRequest.Order.PaymentMethodSystemName)
?? throw new NopException("Payment method couldn't be loaded");
return await paymentMethod.RefundAsync(refundPaymentRequest);
}
///
/// Gets a value indicating whether void is supported by payment method
///
/// Payment method system name
///
/// A task that represents the asynchronous operation
/// The task result contains a value indicating whether void is supported
///
public virtual async Task SupportVoidAsync(string paymentMethodSystemName)
{
var paymentMethod = await _paymentPluginManager.LoadPluginBySystemNameAsync(paymentMethodSystemName);
if (paymentMethod == null)
return false;
return paymentMethod.SupportVoid;
}
///
/// Voids a payment
///
/// Request
///
/// A task that represents the asynchronous operation
/// The task result contains the result
///
public virtual async Task VoidAsync(VoidPaymentRequest voidPaymentRequest)
{
var paymentMethod = await _paymentPluginManager.LoadPluginBySystemNameAsync(voidPaymentRequest.Order.PaymentMethodSystemName)
?? throw new NopException("Payment method couldn't be loaded");
return await paymentMethod.VoidAsync(voidPaymentRequest);
}
///
/// Gets a recurring payment type of payment method
///
/// Payment method system name
///
/// A task that represents the asynchronous operation
/// The task result contains a recurring payment type of payment method
///
public virtual async Task GetRecurringPaymentTypeAsync(string paymentMethodSystemName)
{
var paymentMethod = await _paymentPluginManager.LoadPluginBySystemNameAsync(paymentMethodSystemName);
if (paymentMethod == null)
return RecurringPaymentType.NotSupported;
return paymentMethod.RecurringPaymentType;
}
///
/// 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 virtual async Task ProcessRecurringPaymentAsync(ProcessPaymentRequest processPaymentRequest)
{
if (processPaymentRequest.OrderTotal == decimal.Zero)
{
var result = new ProcessPaymentResult
{
NewPaymentStatus = PaymentStatus.Paid
};
return result;
}
var customer = await _customerService.GetCustomerByIdAsync(processPaymentRequest.CustomerId);
var paymentMethod = await _paymentPluginManager
.LoadPluginBySystemNameAsync(processPaymentRequest.PaymentMethodSystemName, customer, processPaymentRequest.StoreId)
?? throw new NopException("Payment method couldn't be loaded");
return await paymentMethod.ProcessRecurringPaymentAsync(processPaymentRequest);
}
///
/// Cancels a recurring payment
///
/// Request
///
/// A task that represents the asynchronous operation
/// The task result contains the result
///
public virtual async Task CancelRecurringPaymentAsync(CancelRecurringPaymentRequest cancelPaymentRequest)
{
if (cancelPaymentRequest.Order.OrderTotal == decimal.Zero)
return new CancelRecurringPaymentResult();
var paymentMethod = await _paymentPluginManager.LoadPluginBySystemNameAsync(cancelPaymentRequest.Order.PaymentMethodSystemName)
?? throw new NopException("Payment method couldn't be loaded");
return await paymentMethod.CancelRecurringPaymentAsync(cancelPaymentRequest);
}
///
/// Gets masked credit card number
///
/// Credit card number
/// Masked credit card number
public virtual string GetMaskedCreditCardNumber(string creditCardNumber)
{
if (string.IsNullOrEmpty(creditCardNumber))
return string.Empty;
if (creditCardNumber.Length <= 4)
return creditCardNumber;
var last4 = creditCardNumber[(creditCardNumber.Length - 4)..creditCardNumber.Length];
var maskedChars = string.Empty;
for (var i = 0; i < creditCardNumber.Length - 4; i++)
{
maskedChars += "*";
}
return maskedChars + last4;
}
///
/// Serialize CustomValues of ProcessPaymentRequest
///
/// Request
/// Serialized CustomValues
public virtual string SerializeCustomValues(ProcessPaymentRequest request)
{
ArgumentNullException.ThrowIfNull(request);
if (!request.CustomValues.Any())
return null;
//XmlSerializer won't serialize objects that implement IDictionary by default.
//http://msdn.microsoft.com/en-us/magazine/cc164135.aspx
//also see http://ropox.ru/tag/ixmlserializable/ (Russian language)
var ds = new DictionarySerializer(request.CustomValues);
var xs = new XmlSerializer(typeof(DictionarySerializer));
using var textWriter = new StringWriter();
using (var xmlWriter = XmlWriter.Create(textWriter))
{
xs.Serialize(xmlWriter, ds);
}
var result = textWriter.ToString();
return result;
}
///
/// Deserialize CustomValues of Order
///
/// Order
/// Serialized CustomValues CustomValues
public virtual Dictionary DeserializeCustomValues(Order order)
{
ArgumentNullException.ThrowIfNull(order);
if (string.IsNullOrWhiteSpace(order.CustomValuesXml))
return new Dictionary();
var serializer = new XmlSerializer(typeof(DictionarySerializer));
using var textReader = new StringReader(order.CustomValuesXml);
using var xmlReader = XmlReader.Create(textReader);
if (serializer.Deserialize(xmlReader) is DictionarySerializer ds)
return ds.Dictionary;
return [];
}
///
/// Generate an order GUID
///
/// Process payment request
public virtual async Task GenerateOrderGuidAsync(ProcessPaymentRequest processPaymentRequest)
{
if (processPaymentRequest == null)
return;
//we should use the same GUID for multiple payment attempts
//this way a payment gateway can prevent security issues such as credit card brute-force attacks
//in order to avoid any possible limitations by payment gateway we reset GUID periodically
var previousPaymentRequest = await _httpContextAccessor.HttpContext.Session.GetAsync("OrderPaymentInfo");
if (_paymentSettings.RegenerateOrderGuidInterval > 0 &&
previousPaymentRequest != null &&
previousPaymentRequest.OrderGuidGeneratedOnUtc.HasValue)
{
var interval = DateTime.UtcNow - previousPaymentRequest.OrderGuidGeneratedOnUtc.Value;
if (interval.TotalSeconds < _paymentSettings.RegenerateOrderGuidInterval)
{
processPaymentRequest.OrderGuid = previousPaymentRequest.OrderGuid;
processPaymentRequest.OrderGuidGeneratedOnUtc = previousPaymentRequest.OrderGuidGeneratedOnUtc;
}
}
if (processPaymentRequest.OrderGuid == Guid.Empty)
{
processPaymentRequest.OrderGuid = Guid.NewGuid();
processPaymentRequest.OrderGuidGeneratedOnUtc = DateTime.UtcNow;
}
}
#endregion
}