It is important to know that the changes are only needed at certain situation. If you not satisfy with above link answer and try to know the changed files. Then here are the change files that I mailed to the questioner. I have change the source code for the questioner's requirement. But I believe it is possible by plugin also. Here are the steps for nopcommerce3.9.
1. Go to the Nop.Web==>Controllers ==>CheckoutController.cs and replace
protected virtual JsonResult OpcLoadStepAfterShippingMethod(List<ShoppingCartItem> cart)
method by the bellow one
protected virtual JsonResult OpcLoadStepAfterShippingMethod(List<ShoppingCartItem> cart)
{
//Check whether payment workflow is required
//we ignore reward points during cart total calculation
bool isPaymentWorkflowRequired = _orderProcessingService.IsPaymentWorkflowRequired(cart, false);
//Intentionally make the isPaymentWorkflowRequired false. Default behavior change here
isPaymentWorkflowRequired = false;
if (isPaymentWorkflowRequired)
{
//filter by country
int filterByCountryId = 0;
if (_addressSettings.CountryEnabled &&
_workContext.CurrentCustomer.BillingAddress != null &&
_workContext.CurrentCustomer.BillingAddress.Country != null)
{
filterByCountryId = _workContext.CurrentCustomer.BillingAddress.Country.Id;
}
//payment is required
var paymentMethodModel = _checkoutModelFactory.PreparePaymentMethodModel(cart, filterByCountryId);
if (_paymentSettings.BypassPaymentMethodSelectionIfOnlyOne &&
paymentMethodModel.PaymentMethods.Count == 1 && !paymentMethodModel.DisplayRewardPoints)
{
//if we have only one payment method and reward points are disabled or the current customer doesn't have any reward points
//so customer doesn't have to choose a payment method
var selectedPaymentMethodSystemName = paymentMethodModel.PaymentMethods[0].PaymentMethodSystemName;
_genericAttributeService.SaveAttribute(_workContext.CurrentCustomer,
SystemCustomerAttributeNames.SelectedPaymentMethod,
selectedPaymentMethodSystemName, _storeContext.CurrentStore.Id);
var paymentMethodInst = _paymentService.LoadPaymentMethodBySystemName(selectedPaymentMethodSystemName);
if (paymentMethodInst == null ||
!paymentMethodInst.IsPaymentMethodActive(_paymentSettings) ||
!_pluginFinder.AuthenticateStore(paymentMethodInst.PluginDescriptor, _storeContext.CurrentStore.Id) ||
!_pluginFinder.AuthorizedForUser(paymentMethodInst.PluginDescriptor, _workContext.CurrentCustomer))
throw new Exception("Selected payment method can't be parsed");
return OpcLoadStepAfterPaymentMethod(paymentMethodInst, cart);
}
//customer have to choose a payment method
return Json(new
{
update_section = new UpdateSectionJsonModel
{
name = "payment-method",
html = this.RenderPartialViewToString("OpcPaymentMethods", paymentMethodModel)
},
goto_section = "payment_method"
});
}
//payment is not required
_genericAttributeService.SaveAttribute<string>(_workContext.CurrentCustomer,
SystemCustomerAttributeNames.SelectedPaymentMethod, null, _storeContext.CurrentStore.Id);
var confirmOrderModel = _checkoutModelFactory.PrepareConfirmOrderModel(cart);
return Json(new
{
update_section = new UpdateSectionJsonModel
{
name = "confirm-order",
html = this.RenderPartialViewToString("OpcConfirmOrder", confirmOrderModel)
},
goto_section = "confirm_order"
});
}
2. Go to the Libraries==>Nop.Services==>Orders==>OrderProcessingService.cs and replace
public virtual PlaceOrderResult PlaceOrder(ProcessPaymentRequest processPaymentRequest) method by the bellow code
public virtual PlaceOrderResult PlaceOrder(ProcessPaymentRequest processPaymentRequest)
{
if (processPaymentRequest == null)
throw new ArgumentNullException("processPaymentRequest");
var result = new PlaceOrderResult();
try
{
if (processPaymentRequest.OrderGuid == Guid.Empty)
processPaymentRequest.OrderGuid = Guid.NewGuid();
//prepare order details
var details = PreparePlaceOrderDetails(processPaymentRequest);
#region Payment workflow
//process payment
ProcessPaymentResult processPaymentResult = null;
//skip payment workflow if order total equals zero
var skipPaymentWorkflow = details.OrderTotal == decimal.Zero;
//Intentionally make the skipPaymentWorkflow false. Default behavior change here.
skipPaymentWorkflow = true;
if (!skipPaymentWorkflow)
{
var paymentMethod = _paymentService.LoadPaymentMethodBySystemName(processPaymentRequest.PaymentMethodSystemName);
if (paymentMethod == null)
throw new NopException("Payment method couldn't be loaded");
//ensure that payment method is active
if (!paymentMethod.IsPaymentMethodActive(_paymentSettings))
throw new NopException("Payment method is not active");
if (details.IsRecurringShoppingCart)
{
//recurring cart
switch (_paymentService.GetRecurringPaymentType(processPaymentRequest.PaymentMethodSystemName))
{
case RecurringPaymentType.NotSupported:
throw new NopException("Recurring payments are not supported by selected payment method");
case RecurringPaymentType.Manual:
case RecurringPaymentType.Automatic:
processPaymentResult = _paymentService.ProcessRecurringPayment(processPaymentRequest);
break;
default:
throw new NopException("Not supported recurring payment type");
}
}
else
//standard cart
processPaymentResult = _paymentService.ProcessPayment(processPaymentRequest);
}
else
//payment is not required
processPaymentResult = new ProcessPaymentResult { NewPaymentStatus = PaymentStatus.Paid };
if (processPaymentResult == null)
throw new NopException("processPaymentResult is not available");
#endregion
if (processPaymentResult.Success)
{
#region Save order details
var order = SaveOrderDetails(processPaymentRequest, processPaymentResult, details);
result.PlacedOrder = order;
//move shopping cart items to order items
foreach (var sc in details.Cart)
{
//prices
decimal taxRate;
List<DiscountForCaching> scDiscounts;
decimal discountAmount;
int? maximumDiscountQty;
var scUnitPrice = _priceCalculationService.GetUnitPrice(sc);
var scSubTotal = _priceCalculationService.GetSubTotal(sc, true, out discountAmount, out scDiscounts, out maximumDiscountQty);
var scUnitPriceInclTax = _taxService.GetProductPrice(sc.Product, scUnitPrice, true, details.Customer, out taxRate);
var scUnitPriceExclTax = _taxService.GetProductPrice(sc.Product, scUnitPrice, false, details.Customer, out taxRate);
var scSubTotalInclTax = _taxService.GetProductPrice(sc.Product, scSubTotal, true, details.Customer, out taxRate);
var scSubTotalExclTax = _taxService.GetProductPrice(sc.Product, scSubTotal, false, details.Customer, out taxRate);
var discountAmountInclTax = _taxService.GetProductPrice(sc.Product, discountAmount, true, details.Customer, out taxRate);
var discountAmountExclTax = _taxService.GetProductPrice(sc.Product, discountAmount, false, details.Customer, out taxRate);
foreach (var disc in scDiscounts)
if (!details.AppliedDiscounts.ContainsDiscount(disc))
details.AppliedDiscounts.Add(disc);
//attributes
var attributeDescription = _productAttributeFormatter.FormatAttributes(sc.Product, sc.AttributesXml, details.Customer);
var itemWeight = _shippingService.GetShoppingCartItemWeight(sc);
//save order item
var orderItem = new OrderItem
{
OrderItemGuid = Guid.NewGuid(),
Order = order,
ProductId = sc.ProductId,
UnitPriceInclTax = scUnitPriceInclTax,
UnitPriceExclTax = scUnitPriceExclTax,
PriceInclTax = scSubTotalInclTax,
PriceExclTax = scSubTotalExclTax,
OriginalProductCost = _priceCalculationService.GetProductCost(sc.Product, sc.AttributesXml),
AttributeDescription = attributeDescription,
AttributesXml = sc.AttributesXml,
Quantity = sc.Quantity,
DiscountAmountInclTax = discountAmountInclTax,
DiscountAmountExclTax = discountAmountExclTax,
DownloadCount = 0,
IsDownloadActivated = false,
LicenseDownloadId = 0,
ItemWeight = itemWeight,
RentalStartDateUtc = sc.RentalStartDateUtc,
RentalEndDateUtc = sc.RentalEndDateUtc
};
order.OrderItems.Add(orderItem);
_orderService.UpdateOrder(order);
//gift cards
if (sc.Product.IsGiftCard)
{
string giftCardRecipientName;
string giftCardRecipientEmail;
string giftCardSenderName;
string giftCardSenderEmail;
string giftCardMessage;
_productAttributeParser.GetGiftCardAttribute(sc.AttributesXml, out giftCardRecipientName,
out giftCardRecipientEmail, out giftCardSenderName, out giftCardSenderEmail, out giftCardMessage);
for (var i = 0; i < sc.Quantity; i++)
{
_giftCardService.InsertGiftCard(new GiftCard
{
GiftCardType = sc.Product.GiftCardType,
PurchasedWithOrderItem = orderItem,
Amount = sc.Product.OverriddenGiftCardAmount.HasValue ? sc.Product.OverriddenGiftCardAmount.Value : scUnitPriceExclTax,
IsGiftCardActivated = false,
GiftCardCouponCode = _giftCardService.GenerateGiftCardCode(),
RecipientName = giftCardRecipientName,
RecipientEmail = giftCardRecipientEmail,
SenderName = giftCardSenderName,
SenderEmail = giftCardSenderEmail,
Message = giftCardMessage,
IsRecipientNotified = false,
CreatedOnUtc = DateTime.UtcNow
});
}
}
//inventory
_productService.AdjustInventory(sc.Product, -sc.Quantity, sc.AttributesXml,
string.Format(_localizationService.GetResource("Admin.StockQuantityHistory.Messages.PlaceOrder"), order.Id));
}
//clear shopping cart
details.Cart.ToList().ForEach(sci => _shoppingCartService.DeleteShoppingCartItem(sci, false));
//discount usage history
foreach (var discount in details.AppliedDiscounts)
{
var d = _discountService.GetDiscountById(discount.Id);
if (d != null)
{
_discountService.InsertDiscountUsageHistory(new DiscountUsageHistory
{
Discount = d,
Order = order,
CreatedOnUtc = DateTime.UtcNow
});
}
}
//gift card usage history
if (details.AppliedGiftCards != null)
foreach (var agc in details.AppliedGiftCards)
{
agc.GiftCard.GiftCardUsageHistory.Add(new GiftCardUsageHistory
{
GiftCard = agc.GiftCard,
UsedWithOrder = order,
UsedValue = agc.AmountCanBeUsed,
CreatedOnUtc = DateTime.UtcNow
});
_giftCardService.UpdateGiftCard(agc.GiftCard);
}
//recurring orders
if (details.IsRecurringShoppingCart)
{
//create recurring payment (the first payment)
var rp = new RecurringPayment
{
CycleLength = processPaymentRequest.RecurringCycleLength,
CyclePeriod = processPaymentRequest.RecurringCyclePeriod,
TotalCycles = processPaymentRequest.RecurringTotalCycles,
StartDateUtc = DateTime.UtcNow,
IsActive = true,
CreatedOnUtc = DateTime.UtcNow,
InitialOrder = order,
};
_orderService.InsertRecurringPayment(rp);
switch (_paymentService.GetRecurringPaymentType(processPaymentRequest.PaymentMethodSystemName))
{
case RecurringPaymentType.NotSupported:
//not supported
break;
case RecurringPaymentType.Manual:
rp.RecurringPaymentHistory.Add(new RecurringPaymentHistory
{
RecurringPayment = rp,
CreatedOnUtc = DateTime.UtcNow,
OrderId = order.Id,
});
_orderService.UpdateRecurringPayment(rp);
break;
case RecurringPaymentType.Automatic:
//will be created later (process is automated)
break;
default:
break;
}
}
#endregion
//notifications
SendNotificationsAndSaveNotes(order);
//reset checkout data
_customerService.ResetCheckoutData(details.Customer, processPaymentRequest.StoreId, clearCouponCodes: true, clearCheckoutAttributes: true);
_customerActivityService.InsertActivity("PublicStore.PlaceOrder", _localizationService.GetResource("ActivityLog.PublicStore.PlaceOrder"), order.Id);
//check order status
CheckOrderStatus(order);
//raise event
_eventPublisher.Publish(new OrderPlacedEvent(order));
if (order.PaymentStatus == PaymentStatus.Paid)
ProcessOrderPaid(order);
}
else
foreach (var paymentError in processPaymentResult.Errors)
result.AddError(string.Format(_localizationService.GetResource("Checkout.PaymentError"), paymentError));
}
catch (Exception exc)
{
_logger.Error(exc.Message, exc);
result.AddError(exc.Message);
}
#region Process errors
if (!result.Success)
{
//log errors
var logError = result.Errors.Aggregate("Error while placing order. ",
(current, next) => string.Format("{0}Error {1}: {2}. ", current, result.Errors.IndexOf(next) + 1, next));
var customer = _customerService.GetCustomerById(processPaymentRequest.CustomerId);
_logger.Error(logError, customer: customer);
}
#endregion
return result;
}
3. Please go to the Nop.Web==>Views==>Checkout and replace OnePageCheckout.cshtml view's code by the bellow one
@{
//step numbers
int billingAddressStepNumber = 1;
int shippingAddressStepNumber = 2;
int shippingMethodStepNumber = 3;
int paymentMethodStepNumber = 4;
int paymentInfoStepNumber = 5;
int confirmOrderStepNumber = 6;
if (!Model.ShippingRequired)
{
paymentMethodStepNumber = paymentMethodStepNumber - 2;
paymentInfoStepNumber = paymentInfoStepNumber - 2;
confirmOrderStepNumber = confirmOrderStepNumber - 2;
}
if (Model.DisableBillingAddressCheckoutStep)
{
shippingAddressStepNumber--;
shippingMethodStepNumber--;
paymentMethodStepNumber--;
paymentInfoStepNumber--;
confirmOrderStepNumber--;
}
}
by
@{
//step numbers
int billingAddressStepNumber = 1;
int shippingAddressStepNumber = 2;
int shippingMethodStepNumber = 3;
//int paymentMethodStepNumber = 4;
//int paymentInfoStepNumber = 5;
int confirmOrderStepNumber = 4;
if (!Model.ShippingRequired)
{
//paymentMethodStepNumber = paymentMethodStepNumber - 2;
//paymentInfoStepNumber = paymentInfoStepNumber - 2;
confirmOrderStepNumber = confirmOrderStepNumber - 2;
}
if (Model.DisableBillingAddressCheckoutStep)
{
shippingAddressStepNumber--;
shippingMethodStepNumber--;
//paymentMethodStepNumber--;
//paymentInfoStepNumber--;
confirmOrderStepNumber--;
}
}
The order look like the bellow image at admin side