I’m using Umbraco Commerce, and I noticed there is an Abandoned Cart E-mail option. This seems to work great, but I stumbled upon a little problem.
Is it possible to send these e-mails only to members who have a checkbox set on their profile (either send only if “allowAbandonedCartEmail” is true, or don’t send if “disallowAbandonedCartEmail” is true.)?
The whole flow and everything works as I want it, but I just have members who don’t want to recieve these e-mails.
I started with the EmailSendingNotification, and retrieve the member. But it doesn’t seem possible to cancel the sending of the e-mail from here.
Do I need to build my own AbandonedCartHandler just to filter which carts I want and don’t want to e-mail? Or can I hook in on another event somewhere and keep all default Commerce handling?
I had a look at the docs and I can’t see anything that allows you to change the logic or cancel an abandoned cart email. Your best option would be to disable the built-in mechanism and write your own using the CartAbandonedNotification event.
Here’s the solution AI came back with:
You’re right that EmailSendingNotification is a dead end here — it doesn’t give you a way to cancel the send, so you can’t filter from there.
The cleaner approach is to handle the CartAbandonedNotification event instead and do the sending yourself, so you’re in full control of who gets an email. The trick is that this works in two parts:
Clear the Abandoned Cart Email Template in Store Settings > Cart Settings. With no template set, Commerce stops sending its own email, but detection keeps running and the notification still fires. This is what lets you take over the send.
Handle CartAbandonedNotification, loop the order IDs, resolve the member, check your flag, and only send to those who haven’t opted out.
The registration has to go inside the AddUmbracoCommerce(...) callback in a composer — that’s a common gotcha; registered on the bare IUmbracoBuilder it compiles but never fires:
public class CommerceComposer : IComposer
{
public void Compose(IUmbracoBuilder builder)
{
builder.AddUmbracoCommerce(commerceBuilder =>
{
commerceBuilder
.WithNotificationEvent<CartAbandonedNotification>()
.RegisterHandler<AbandonedCartHandler>();
});
}
}
And the handler, with the member-flag check:
public class AbandonedCartHandler : NotificationEventHandlerBase<CartAbandonedNotification>
{
private readonly IServiceProvider _serviceProvider;
public AbandonedCartHandler(IServiceProvider serviceProvider)
=> _serviceProvider = serviceProvider;
public override void Handle(CartAbandonedNotification evt)
{
using var scope = _serviceProvider.CreateScope();
var orderService = scope.ServiceProvider.GetRequiredService<IOrderService>();
var memberService = scope.ServiceProvider.GetRequiredService<IMemberService>();
foreach (var orderId in evt.OrderIds)
{
var order = orderService.GetOrder(orderId);
if (order?.CustomerInfo is null) continue;
// however you've stored the member reference on the order at checkout
if (!Guid.TryParse(order.CustomerInfo.CustomerReference, out var memberKey))
continue; // guest checkout -> skip
var member = memberService.GetByKey(memberKey);
if (member is null) continue;
// opt-out style: skip if they've ticked the box
if (member.GetValue<bool>("disallowAbandonedCartEmail")) continue;
// (or opt-in: if (!member.GetValue<bool>("allowAbandonedCartEmail")) continue;)
// send your email here
}
}
}
Two things to check on your side:
How the member is attached to the order at checkout. I’ve used CustomerInfo.CustomerReference above, but check what you actually set when the member places the order — it might be a custom order property or an email match against IMemberService. That lookup is the bit to get right.
One honest caveat: I’m confident clearing the template stops the native send (the docs note that with no template, no email goes out but notifications/webhooks still fire), but I haven’t seen it confirmed whether the notification fires alongside the built-in send or instead of it. So test it — clear the template, trigger an abandoned cart, and make sure no duplicate native email goes out. If it does, we can dig further.
The one trade-off is you lose the built-in conversion-rate widget tracking, since that’s tied to Commerce sending the email itself. If you rely on that, worth factoring in.
Hi @justin-nevitech
Thanks for this solution. I might try this one.
Yesterday, a colleague of mine came up with another possible solution. He made a new Pipeline Task, which he inserted before the RaiseSendingEventTask within the EmailPipeline.
In this task, we check the member, and if this member shouldn’t be e-mailed a notification, it returns a Fail result.
This seems to work, although I don’t know if this conflicts with the conversion-rate.