I'm setting up a simple webshop using Umbraco and Merchello for the first time. For now, the checkout is made in one step where the anonymous customer enters shipping- and billing information in a form and just confirms the order.
Everything works great the first time I complete the checkout process. However, if I then go back to the products page, add a few items to the basket and then try to checkout again the items are never added to the invoice. Only shipment rate and a tax item with value 0 is added. Is this something related to caching or am I just missing something in the checkout-process?
Here is my Checkout action:
[HttpPost]
public ActionResult Checkout(CustomerInfoViewModel model)
{
if (!ModelState.IsValid)
return CurrentUmbracoPage();
var checkoutManager = Basket.GetCheckoutManager();
//Create adresses
var billingAddress = new Address
{
Name = model.BillingName,
Address1 = model.BillingAddress,
PostalCode = model.BillingZipCode,
Locality = model.BillingCity,
};
var shippingAddress = new Address
{
Name = model.Name,
Email = model.Email,
Address1 = model.Street,
PostalCode = model.ZipCode,
Locality = model.City
};
//Set billingadress
if (!string.IsNullOrEmpty(billingAddress.Address1) &&
!string.IsNullOrEmpty(billingAddress.PostalCode) &&
!string.IsNullOrEmpty(billingAddress.Locality))
{
checkoutManager.Customer.SaveBillToAddress(billingAddress);
}
else
{
checkoutManager.Customer.SaveBillToAddress(shippingAddress);
}
//Set shippingaddress
checkoutManager.Customer.SaveShipToAddress(shippingAddress);
var shipment = Basket.PackageBasket(shippingAddress).FirstOrDefault();
if (shipment != null)
{
//Create invoice
var invoice = checkoutManager.Payment.PrepareInvoice();
//Add shipment to invoice
var shipmentRateQuotes = shipment.ShipmentRateQuotes().ToList();
if (shipmentRateQuotes.Count > 0 )
{
checkoutManager.Shipping.SaveShipmentRateQuote(shipmentRateQuotes.First());
}
}
//Payment
var paymentMethod = MerchelloContext.Current.Gateways.Payment.GetPaymentGatewayMethods().FirstOrDefault(p => p.PaymentMethod?.Name == _paymentMethod);
if (paymentMethod != null)
{
var payment = checkoutManager.Payment.AuthorizePayment(paymentMethod);
//var payment = invoice.AuthorizePayment(paymentMethod);
if (payment.Payment.Success)
{
//Add note to invoice if anything to add
if (!string.IsNullOrEmpty(model.Message))
{
var invoiceAgain = MerchelloServices.InvoiceService.GetByKey(payment.Invoice.Key);
if (invoiceAgain != null)
{
var note = MerchelloServices.NoteService.CreateNote(invoiceAgain.Key, EntityType.Invoice, model.Message);
note.CreateDate = DateTime.Now;
note.InternalOnly = false;
note.Author = shippingAddress.Name;
invoiceAgain.Notes = new List<INote> { note };
MerchelloServices.NoteService.Save(note);
//Must re-save the invoice to add the note
MerchelloServices.InvoiceService.Save(invoiceAgain);
}
}
Notification.Trigger("OrderConfirmation", payment, new List<string>{ model.Email });
//Store invoiceKey on Customer for reciept generation
CustomerContext.SetValue("invoiceKey", payment.Invoice.Key.ToString());
}
}
return RedirectToCurrentUmbracoPage();
}
There should not be any caching issues in the checkout process. The cache keys is generated using a "version key" (generated GUID) each time the basket is saved - so if you have started a checkout, or modify the basket in any way (quantity, new line item, remove a line item ... etc.) a new version key is generated which invalidates any current checkout. The CheckoutContextSettings are used when the CheckoutManager resets when the version key check fails or a checkout has been completed - in otherwords the process should be starting over completely.
I don't see anything in the code snippet that immediately jumps out that would affect this behavior - but there are a couple of areas you could put break points and figure out what is going on.
In the following snippet you could put a break point on the line where you are preparing the invoice and check if the items have been added.
Note: Preparing the invoice here is not necessary. PrepareInvoice is typically used to show the customer a preview of what is going to be billed. This call is done internally in the AuthorizePayment process.
var shipment = Basket.PackageBasket(shippingAddress).FirstOrDefault();
if (shipment != null)
{
//Create invoice
var invoice = checkoutManager.Payment.PrepareInvoice();
//Add shipment to invoice
var shipmentRateQuotes = shipment.ShipmentRateQuotes().ToList();
if (shipmentRateQuotes.Count > 0 )
{
checkoutManager.Shipping.SaveShipmentRateQuote(shipmentRateQuotes.First());
}
}
You should not need to add the notes after the invoice is created. This can be done through the CheckoutManager directly.
Before you Authorize the payment try:
checkoutManager.Extended.AddNote(model.Message);
The only difference should be that the BillingAddress email address value would be used as the note.Author.
-- back to the issue --
Can you confirm that your basket page implementation is working after the first checkout? e.g. You can adjust quantities and add/remove items.
Are there any errors in the logs when you try to checkout a second time?
Thanks for your reply! I've done some debugging of the Checkout action and it seems like the products are never added to the checkoutManager.Context.ItemCache.Items list in the second order. Here's some more info on what's going on:
When checking out the first time the checkoutManager.Context is flagged as IsNew = true, VersionKey = {8d3a35d9-92a5-4a8c-b2e7-bafd0953e2f8}. The ItemCache.Items contains the products.
After the call to PackageBasket the shipment.Items contains the products.
When the Payment is authorized the paymen.Invoice.Items contains the products, 1 Tax item and 1 shipment rate item.
The confirmation email is sent and the basket is cleared. A sales-item is generated and contains all expected info.
If I go back to the products page now I can add new products to the basket. When checking out again out after this the checkoutManager.Context is not flagged as new, IsNew = false, VersionKey = {e18a0923-2183-48c8-b999-298150e63d1a}. The Context.ItemChache.Items contains 0 items.
After the call to PackageBasket the shipment.Items contains the products. When the payment is authorized the payment.Invoice.Items only contains 1 Tax item (amount 0) and 1 shipment rate item.
The confirmation email is sent and the basket is cleared. A sales-item is generated and contains all info except the products.
There are no errors in the logs. Any idea what's happening? Is everything related to a "shopping-session" cleared internally when the payment is authorized or am I supposed to do anything to mark the checkout process as complete?
It may be that an ItemCache object is found in the runtime cache and the method is returning early with an instance of the CheckoutContext based on the cached version. This would explain the IsNewVersion being false, but would not explain no product line items as I'd expect it would contain the items from the previous order ...
And you mentioned that your notification was sending so the payment result is returning a success - this is checked when clearing the CheckoutManager as well so that is unlikely causing the issue.
Things to try
Verify that the Basket version key is equal to the ItemCache version key AND that the version key has changed from the first order to the second (should never be the same).
Verify that the ItemCache has items immediately after the var checkoutManager = Basket.GetCheckoutManager();
What base class are you using for your controller? Are you using one of Merchello's or a custom implementation? Thinking about the Basket property here ...
The Basket version key is equal to the ItemCache version key in both orders and changes between the orders. So far so good. However, the ItemCache has 0 items in the second order. The Basket contains the items but the checkoutManager.Context.ItemCache does not.
I use Merchello's base classes for my controllers. MerchelloRenderMvcController for the Checkout page controller and MerchelloSurfaceController for the checkout form controller where the Checkout action i posted earlier is implemented.
This is how the items are added to the basket:
public class ShopController : MerchelloSurfaceController
{
[HttpPost]
public ActionResult AddToCart(ProductViewModel product, string buyProduct)
{
try
{
Guid productId;
if (Guid.TryParse(buyProduct, out productId))
{
var helper = new MerchelloHelper(false);
var item = helper.Query.Product.GetByKey(productId);
if (item != null)
{
Basket.AddItem(item, item.Name, 1);
}
}
}
catch (Exception e)
{
//TODO: Log
throw;
}
return RedirectToCurrentUmbracoPage();
}
}
It's probably technically an error - what I think was probably happening is the items were being persisted in the runtime cache and not the database (until save is called).
Invoice items missing in second order
Hi!
I'm setting up a simple webshop using Umbraco and Merchello for the first time. For now, the checkout is made in one step where the anonymous customer enters shipping- and billing information in a form and just confirms the order.
Everything works great the first time I complete the checkout process. However, if I then go back to the products page, add a few items to the basket and then try to checkout again the items are never added to the invoice. Only shipment rate and a tax item with value 0 is added. Is this something related to caching or am I just missing something in the checkout-process?
Here is my Checkout action:
Any help would be very appreciated! :)
Hey Gustav,
There should not be any caching issues in the checkout process. The cache keys is generated using a "version key" (generated GUID) each time the basket is saved - so if you have started a checkout, or modify the basket in any way (quantity, new line item, remove a line item ... etc.) a new version key is generated which invalidates any current checkout. The
CheckoutContextSettings
are used when the CheckoutManager resets when the version key check fails or a checkout has been completed - in otherwords the process should be starting over completely.I don't see anything in the code snippet that immediately jumps out that would affect this behavior - but there are a couple of areas you could put break points and figure out what is going on.
In the following snippet you could put a break point on the line where you are preparing the invoice and check if the items have been added.
Note: Preparing the invoice here is not necessary. PrepareInvoice is typically used to show the customer a preview of what is going to be billed. This call is done internally in the AuthorizePayment process.
You should not need to add the notes after the invoice is created. This can be done through the
CheckoutManager
directly.Before you Authorize the payment try:
The only difference should be that the BillingAddress email address value would be used as the note.Author.
-- back to the issue --
Can you confirm that your basket page implementation is working after the first checkout? e.g. You can adjust quantities and add/remove items.
Are there any errors in the logs when you try to checkout a second time?
Hi Rusty,
Thanks for your reply! I've done some debugging of the Checkout action and it seems like the products are never added to the checkoutManager.Context.ItemCache.Items list in the second order. Here's some more info on what's going on:
When checking out the first time the checkoutManager.Context is flagged as IsNew = true, VersionKey = {8d3a35d9-92a5-4a8c-b2e7-bafd0953e2f8}. The ItemCache.Items contains the products.
After the call to PackageBasket the shipment.Items contains the products. When the Payment is authorized the paymen.Invoice.Items contains the products, 1 Tax item and 1 shipment rate item.
The confirmation email is sent and the basket is cleared. A sales-item is generated and contains all expected info.
If I go back to the products page now I can add new products to the basket. When checking out again out after this the checkoutManager.Context is not flagged as new, IsNew = false, VersionKey = {e18a0923-2183-48c8-b999-298150e63d1a}. The Context.ItemChache.Items contains 0 items.
After the call to PackageBasket the shipment.Items contains the products. When the payment is authorized the payment.Invoice.Items only contains 1 Tax item (amount 0) and 1 shipment rate item.
The confirmation email is sent and the basket is cleared. A sales-item is generated and contains all info except the products.
There are no errors in the logs. Any idea what's happening? Is everything related to a "shopping-session" cleared internally when the payment is authorized or am I supposed to do anything to mark the checkout process as complete?
Everything should be cleared internally - but maybe that's the issue in your workflow, just trying to figure out how that would be possible....
The context is actually created here:
https://github.com/Merchello/Merchello/blob/merchello-dev/src/Merchello.Core/Checkout/CheckoutContext.cs#L228
It may be that an ItemCache object is found in the runtime cache and the method is returning early with an instance of the CheckoutContext based on the cached version. This would explain the
IsNewVersion
being false, but would not explain no product line items as I'd expect it would contain the items from the previous order ...The
CheckoutManager
is reset after a successful payment: https://github.com/Merchello/Merchello/blob/merchello-dev/src/Merchello.Web/UmbracoApplicationEventHandler.cs#L441Depending on your context settings, this will clear things like the addresses, notes, ship rate quotes, etc.
I've verified the event is getting raised for AuthorizePayment:
https://github.com/Merchello/Merchello/blob/merchello-dev/src/Merchello.Web/CheckoutManagers/BasketCheckoutPaymentManager.cs#L90
And you mentioned that your notification was sending so the payment result is returning a success - this is checked when clearing the CheckoutManager as well so that is unlikely causing the issue.
Things to try
Verify that the Basket version key is equal to the ItemCache version key AND that the version key has changed from the first order to the second (should never be the same).
Verify that the ItemCache has items immediately after the
var checkoutManager = Basket.GetCheckoutManager();
What base class are you using for your controller? Are you using one of Merchello's or a custom implementation? Thinking about the
Basket
property here ...The Basket version key is equal to the ItemCache version key in both orders and changes between the orders. So far so good. However, the ItemCache has 0 items in the second order. The Basket contains the items but the checkoutManager.Context.ItemCache does not.
I use Merchello's base classes for my controllers. MerchelloRenderMvcController for the Checkout page controller and MerchelloSurfaceController for the checkout form controller where the Checkout action i posted earlier is implemented.
This is how the items are added to the basket:
Try adding
Basket.Save()
after adding the item.Yes, saving the basket dit the trick! Now every order contains the expected items.
How come it worked for the first order but not the following ones when I just added to the basket without saving?
Thank you so much for helping me out!
It's probably technically an error - what I think was probably happening is the items were being persisted in the runtime cache and not the database (until save is called).
is working on a reply...