GetShoppingCartSubTotal(IList <ShoppingCartItem> cart, bool includingTax) { var discountAmount = decimal.Zero; var appliedDiscounts = new List <ApplyDiscount>(); var subTotalWithoutDiscount = decimal.Zero; var subTotalWithDiscount = decimal.Zero; var taxRates = new SortedDictionary <decimal, decimal>(); if (!cart.Any()) { return(discountAmount, appliedDiscounts, subTotalWithoutDiscount, subTotalWithDiscount, taxRates); } //get the customer Customer customer = _workContext.CurrentCustomer; //sub totals decimal subTotalExclTaxWithoutDiscount = decimal.Zero; decimal subTotalInclTaxWithoutDiscount = decimal.Zero; foreach (var shoppingCartItem in cart) { var product = await _productService.GetProductById(shoppingCartItem.ProductId); if (product == null) { continue; } var subtotal = await _pricingService.GetSubTotal(shoppingCartItem, product); decimal sciSubTotal = subtotal.subTotal; decimal taxRate; var pricesExcl = await _taxService.GetProductPrice(product, sciSubTotal, false, customer); decimal sciExclTax = pricesExcl.productprice; var pricesIncl = await _taxService.GetProductPrice(product, sciSubTotal, true, customer); decimal sciInclTax = pricesIncl.productprice; taxRate = pricesIncl.taxRate; subTotalExclTaxWithoutDiscount += sciExclTax; subTotalInclTaxWithoutDiscount += sciInclTax; //tax rates decimal sciTax = sciInclTax - sciExclTax; if (taxRate > decimal.Zero && sciTax > decimal.Zero) { if (!taxRates.ContainsKey(taxRate)) { taxRates.Add(taxRate, sciTax); } else { taxRates[taxRate] = taxRates[taxRate] + sciTax; } } } //checkout attributes if (customer != null) { var checkoutAttributes = customer.GetUserFieldFromEntity <List <CustomAttribute> >(SystemCustomerFieldNames.CheckoutAttributes, _workContext.CurrentStore.Id); var attributeValues = await _checkoutAttributeParser.ParseCheckoutAttributeValue(checkoutAttributes); foreach (var attributeValue in attributeValues) { decimal taxRate; var checkoutAttributePriceExclTax = await _taxService.GetCheckoutAttributePrice(attributeValue.ca, attributeValue.cav, false, customer); decimal caExclTax = await _currencyService.ConvertFromPrimaryStoreCurrency(checkoutAttributePriceExclTax.checkoutPrice, _workContext.WorkingCurrency); var checkoutAttributePriceInclTax = await _taxService.GetCheckoutAttributePrice(attributeValue.ca, attributeValue.cav, true, customer); decimal caInclTax = await _currencyService.ConvertFromPrimaryStoreCurrency(checkoutAttributePriceInclTax.checkoutPrice, _workContext.WorkingCurrency); taxRate = checkoutAttributePriceInclTax.taxRate; subTotalExclTaxWithoutDiscount += caExclTax; subTotalInclTaxWithoutDiscount += caInclTax; //tax rates decimal caTax = caInclTax - caExclTax; if (taxRate > decimal.Zero && caTax > decimal.Zero) { if (!taxRates.ContainsKey(taxRate)) { taxRates.Add(taxRate, caTax); } else { taxRates[taxRate] = taxRates[taxRate] + caTax; } } } } //subtotal without discount subTotalWithoutDiscount = includingTax ? subTotalInclTaxWithoutDiscount : subTotalExclTaxWithoutDiscount; if (subTotalWithoutDiscount < decimal.Zero) { subTotalWithoutDiscount = decimal.Zero; } if (_shoppingCartSettings.RoundPrices) { subTotalWithoutDiscount = RoundingHelper.RoundPrice(subTotalWithoutDiscount, _workContext.WorkingCurrency); } //We calculate discount amount on order subtotal excl tax (discount first) //calculate discount amount ('Applied to order subtotal' discount) var orderSubtotalDiscount = await GetOrderSubtotalDiscount(customer, _workContext.WorkingCurrency, subTotalExclTaxWithoutDiscount); decimal discountAmountExclTax = orderSubtotalDiscount.ordersubtotaldiscount; appliedDiscounts = orderSubtotalDiscount.appliedDiscounts; if (subTotalExclTaxWithoutDiscount < discountAmountExclTax) { discountAmountExclTax = subTotalExclTaxWithoutDiscount; } decimal discountAmountInclTax = discountAmountExclTax; //subtotal with discount (excl tax) decimal subTotalExclTaxWithDiscount = subTotalExclTaxWithoutDiscount - discountAmountExclTax; decimal subTotalInclTaxWithDiscount = subTotalExclTaxWithDiscount; //add tax for shopping items & checkout attributes var tempTaxRates = new Dictionary <decimal, decimal>(taxRates); foreach (KeyValuePair <decimal, decimal> kvp in tempTaxRates) { decimal taxRate = kvp.Key; decimal taxValue = kvp.Value; if (taxValue != decimal.Zero) { //discount the tax amount that applies to subtotal items if (subTotalExclTaxWithoutDiscount > decimal.Zero) { decimal discountTax = taxRates[taxRate] * (discountAmountExclTax / subTotalExclTaxWithoutDiscount); discountAmountInclTax += discountTax; taxValue = taxRates[taxRate] - discountTax; if (_shoppingCartSettings.RoundPrices) { taxValue = RoundingHelper.RoundPrice(taxValue, _workContext.WorkingCurrency); } taxRates[taxRate] = taxValue; } //subtotal with discount (incl tax) subTotalInclTaxWithDiscount += taxValue; } } if (_shoppingCartSettings.RoundPrices) { discountAmountInclTax = RoundingHelper.RoundPrice(discountAmountInclTax, _workContext.WorkingCurrency); discountAmountExclTax = RoundingHelper.RoundPrice(discountAmountExclTax, _workContext.WorkingCurrency); } if (includingTax) { subTotalWithDiscount = subTotalInclTaxWithDiscount; discountAmount = discountAmountInclTax; } else { subTotalWithDiscount = subTotalExclTaxWithDiscount; discountAmount = discountAmountExclTax; } if (subTotalWithDiscount < decimal.Zero) { subTotalWithDiscount = decimal.Zero; } if (_shoppingCartSettings.RoundPrices) { subTotalWithDiscount = RoundingHelper.RoundPrice(subTotalWithDiscount, _workContext.WorkingCurrency); } return(discountAmount, appliedDiscounts, subTotalWithoutDiscount, subTotalWithDiscount, taxRates); }
/// <summary> /// Add order items to the request query parameters /// </summary> /// <param name="parameters">Query parameters</param> /// <param name="postProcessPaymentRequest">Payment info required for an order processing</param> private async Task AddItemsParameters(IDictionary <string, string> parameters, PostProcessPaymentRequest postProcessPaymentRequest) { //upload order items parameters.Add("cmd", "_cart"); parameters.Add("upload", "1"); var cartTotal = decimal.Zero; var roundedCartTotal = decimal.Zero; var itemCount = 1; var rate = postProcessPaymentRequest.Order.CurrencyRate; //add shopping cart items foreach (var item in postProcessPaymentRequest.Order.OrderItems) { var product = await _productService.GetProductById(item.ProductId); var roundedItemPrice = Math.Round(item.UnitPriceExclTax * rate, 2); //add query parameters parameters.Add($"item_name_{itemCount}", product.Name); parameters.Add($"amount_{itemCount}", roundedItemPrice.ToString("0.00", CultureInfo.InvariantCulture)); parameters.Add($"quantity_{itemCount}", item.Quantity.ToString()); cartTotal += (item.PriceExclTax * rate); roundedCartTotal += roundedItemPrice * item.Quantity; itemCount++; } //add checkout attributes as order items var checkoutAttributeValues = await _checkoutAttributeParser.ParseCheckoutAttributeValue(postProcessPaymentRequest.Order.CheckoutAttributesXml); var customer = await _serviceProvider.GetRequiredService <ICustomerService>().GetCustomerById(postProcessPaymentRequest.Order.CustomerId); foreach (var attributeValue in checkoutAttributeValues) { var attributePrice = await _taxService.GetCheckoutAttributePrice(attributeValue.ca, attributeValue.cav, false, customer); if (attributePrice.checkoutPrice > 0) { var roundedAttributePrice = Math.Round(attributePrice.checkoutPrice * rate, 2); //add query parameters if (attributeValue.ca != null) { parameters.Add($"item_name_{itemCount}", attributeValue.ca.Name); parameters.Add($"amount_{itemCount}", roundedAttributePrice.ToString("0.00", CultureInfo.InvariantCulture)); parameters.Add($"quantity_{itemCount}", "1"); cartTotal += attributePrice.checkoutPrice; roundedCartTotal += roundedAttributePrice; itemCount++; } } } //add shipping fee as a separate order item, if it has price var roundedShippingPrice = Math.Round(postProcessPaymentRequest.Order.OrderShippingExclTax * rate, 2); if (roundedShippingPrice > decimal.Zero) { parameters.Add($"item_name_{itemCount}", "Shipping fee"); parameters.Add($"amount_{itemCount}", roundedShippingPrice.ToString("0.00", CultureInfo.InvariantCulture)); parameters.Add($"quantity_{itemCount}", "1"); cartTotal += (postProcessPaymentRequest.Order.OrderShippingExclTax * rate); roundedCartTotal += roundedShippingPrice; itemCount++; } //add payment method additional fee as a separate order item, if it has price var roundedPaymentMethodPrice = Math.Round(postProcessPaymentRequest.Order.PaymentMethodAdditionalFeeExclTax * rate, 2); if (roundedPaymentMethodPrice > decimal.Zero) { parameters.Add($"item_name_{itemCount}", "Payment method fee"); parameters.Add($"amount_{itemCount}", roundedPaymentMethodPrice.ToString("0.00", CultureInfo.InvariantCulture)); parameters.Add($"quantity_{itemCount}", "1"); cartTotal += (postProcessPaymentRequest.Order.PaymentMethodAdditionalFeeExclTax * rate); roundedCartTotal += roundedPaymentMethodPrice; itemCount++; } //add tax as a separate order item, if it has positive amount var roundedTaxAmount = Math.Round(postProcessPaymentRequest.Order.OrderTax * rate, 2); if (roundedTaxAmount > decimal.Zero) { parameters.Add($"item_name_{itemCount}", "Tax amount"); parameters.Add($"amount_{itemCount}", roundedTaxAmount.ToString("0.00", CultureInfo.InvariantCulture)); parameters.Add($"quantity_{itemCount}", "1"); cartTotal += (postProcessPaymentRequest.Order.OrderTax * rate); roundedCartTotal += roundedTaxAmount; itemCount++; } if (cartTotal * rate > postProcessPaymentRequest.Order.OrderTotal * rate) { //get the difference between what the order total is and what it should be and use that as the "discount" var discountTotal = Math.Round(cartTotal - (postProcessPaymentRequest.Order.OrderTotal * rate), 2); roundedCartTotal -= discountTotal; //gift card or rewarded point amount applied to cart in nopCommerce - shows in PayPal as "discount" parameters.Add("discount_amount_cart", discountTotal.ToString("0.00", CultureInfo.InvariantCulture)); } //save order total that actually sent to PayPal (used for PDT order total validation) await _genericAttributeService.SaveAttribute(postProcessPaymentRequest.Order, PaypalHelper.OrderTotalSentToPayPal, roundedCartTotal); }
/// <summary> /// Add order items to the request query parameters /// </summary> /// <param name="parameters">Query parameters</param> /// <param name="postProcessPaymentRequest">Payment info required for an order processing</param> private async Task AddItemsParameters(IDictionary <string, string> parameters, Order order) { //upload order items parameters.Add("cmd", "_cart"); parameters.Add("upload", "1"); double cartTotal = 0; double roundedCartTotal = 0; var itemCount = 1; //add shopping cart items foreach (var item in order.OrderItems) { var product = await _productService.GetProductById(item.ProductId); var roundedItemPrice = Math.Round(item.UnitPriceExclTax, 2); //add query parameters parameters.Add($"item_name_{itemCount}", product.Name); parameters.Add($"amount_{itemCount}", roundedItemPrice.ToString("0.00", CultureInfo.InvariantCulture)); parameters.Add($"quantity_{itemCount}", item.Quantity.ToString()); cartTotal += (item.PriceExclTax); roundedCartTotal += roundedItemPrice * item.Quantity; itemCount++; } //add checkout attributes as order items var checkoutAttributeValues = await _checkoutAttributeParser.ParseCheckoutAttributeValue(order.CheckoutAttributes); var currencyService = _serviceProvider.GetRequiredService <ICurrencyService>(); var workContext = _serviceProvider.GetRequiredService <IWorkContext>(); var customer = await _serviceProvider.GetRequiredService <ICustomerService>().GetCustomerById(order.CustomerId); foreach (var attributeValue in checkoutAttributeValues) { var attributePrice = await _taxService.GetCheckoutAttributePrice(attributeValue.ca, attributeValue.cav, false, customer); if (attributePrice.checkoutPrice > 0) { double roundedAttributePrice = Math.Round(await currencyService.ConvertFromPrimaryStoreCurrency(attributePrice.checkoutPrice, workContext.WorkingCurrency), 2); //add query parameters if (attributeValue.ca != null) { parameters.Add($"item_name_{itemCount}", attributeValue.ca.Name); parameters.Add($"amount_{itemCount}", roundedAttributePrice.ToString("0.00", CultureInfo.InvariantCulture)); parameters.Add($"quantity_{itemCount}", "1"); cartTotal += attributePrice.checkoutPrice; roundedCartTotal += roundedAttributePrice; itemCount++; } } } //add shipping fee as a separate order item, if it has price var roundedShippingPrice = Math.Round(order.OrderShippingExclTax, 2); if (roundedShippingPrice > 0) { parameters.Add($"item_name_{itemCount}", "Shipping fee"); parameters.Add($"amount_{itemCount}", roundedShippingPrice.ToString("0.00", CultureInfo.InvariantCulture)); parameters.Add($"quantity_{itemCount}", "1"); cartTotal += (order.OrderShippingExclTax); roundedCartTotal += roundedShippingPrice; itemCount++; } //add payment method additional fee as a separate order item, if it has price var roundedPaymentMethodPrice = Math.Round(order.PaymentMethodAdditionalFeeExclTax, 2); if (roundedPaymentMethodPrice > 0) { parameters.Add($"item_name_{itemCount}", "Payment method fee"); parameters.Add($"amount_{itemCount}", roundedPaymentMethodPrice.ToString("0.00", CultureInfo.InvariantCulture)); parameters.Add($"quantity_{itemCount}", "1"); cartTotal += (order.PaymentMethodAdditionalFeeExclTax); roundedCartTotal += roundedPaymentMethodPrice; itemCount++; } //add tax as a separate order item, if it has positive amount var roundedTaxAmount = Math.Round(order.OrderTax, 2); if (roundedTaxAmount > 0) { parameters.Add($"item_name_{itemCount}", "Tax amount"); parameters.Add($"amount_{itemCount}", roundedTaxAmount.ToString("0.00", CultureInfo.InvariantCulture)); parameters.Add($"quantity_{itemCount}", "1"); cartTotal += (order.OrderTax); roundedCartTotal += roundedTaxAmount; itemCount++; } if (cartTotal > order.OrderTotal) { //get the difference between what the order total is and what it should be and use that as the "discount" var discountTotal = Math.Round(cartTotal - (order.OrderTotal), 2); roundedCartTotal -= discountTotal; //gift voucher or loyalty points amount applied to cart in nopCommerce - shows in PayPal as "discount" parameters.Add("discount_amount_cart", discountTotal.ToString("0.00", CultureInfo.InvariantCulture)); } //save order total that actually sent to PayPal (used for PDT order total validation) await _userFieldService.SaveField(order, PaypalHelper.OrderTotalSentToPayPal, roundedCartTotal); }