Customizing Transactional Emails
This guide will teach you how to customize transactional emails provided by Solidus.
Solidus has built-in transactional emails that notify customers of various events associated with their order. For example, the following actions can trigger an email:
- Completing order checkout
- Shipping an order
- Cancelling an order
- Completing a batch promtion codes creation (in the admin panel)
Solidus has built-in emails for all of the above scenarios and more. However, given that the default Solidus emails are intended to be very plain, you will likely wish to customize them for your store. You may also want to add new emails, such as a welcome email when a customer creates a new account, an abandoned cart email when a customer leaves their cart without checking out, or a notification that a customer's credit cart has expired if your store uses recurring subscriptions.
Solidus transactional emails use Action Mailer, which is built into Rails. Therefore, most concepts and customizations that apply to emails in Rails also apply to Solidus. Reviewing the Rails Action Mailer Documentation will give you some good ideas about how transactional emails can be customized in Solidus.
Action Mailer emails have two parts:
- A view or layout where you can compose and style the email. This could be an
html.erb
file, or a.txt.erb
file for text-only emails. - A mailer, which behaves very similar to a Rails controller. Like a controller, customizing the mailer will let you determine what information is available in your email's view.
Customizing your emails
Suppose we want to inform our customers about the estimated time of arrival (ETA) for their packages. What we'll need to do is:
- Customize the right mailer to make it retrieve the ETA for the package.
- Customize the email template to render the retrieved ETA along with the other information.
Replacing the default mailer
Let's start by overriding the default mailer to retrieve this information along with all the other things that you currently see in the email. The mailer we want to change is called CartonMailer and is responsible to retrieve the information about the shipped package (called Carton), set subject, to and from of the email, and deliver the actual email.
If we want to override its code we can create a new mailer and instruct Solidus to use our own instead of the default one. Let's create it first:
# frozen_string_literal: true
module AmazingStore
class CartonMailer < Spree::BaseMailer
# Send an email to customers to notify that an individual carton has been
# shipped. If a carton contains items from multiple orders then this will be
# called with that carton one time for each order.
#
# This custom version also retrieve the estimated time of arrival.
#
# @option options carton [Spree::Carton] the shipped carton
# @option options order [Spree::Order] one of the orders with items in the carton
# @option options resend [Boolean] indicates whether the email is a 'resend' (e.g.
# triggered by the admin "resend" button)
# @return [Mail::Message]
def shipped_email(options)
@order = options.fetch(:order)
@carton = options.fetch(:carton)
@manifest = @carton.manifest_for_order(@order)
# Here you can add your custom code to calculate the ETA of the package:
@eta = DateTime.now + 1.day
options = { resend: false }.merge(options)
@store = @order.store
subject = (options[:resend] ? "[#{t('spree.resend').upcase}] " : '')
subject += "#{@store.name} #{t('spree.shipment_mailer.shipped_email.subject')} ##{@order.number}"
mail(to: @order.email, from: from_address(@store), subject: subject)
end
end
end
Now we need to configure Solidus to use this new mailer instead of the default one:
Spree.config do |config|
config.carton_shipped_email_class = 'AmazingStore::CartonMailer'
end
This is the list of all the extension points available in Solidus today:
order_mailer_class
which defaults to Spree::OrderMailercarton_shipped_email_class
which defaults to Spree::CartonMailerreimbursement_mailer_class
which defaults to Spree::ReimbursementMailerpromotion_code_batch_mailer_class
which defaults to Spree::PromotionCodeBatchMailer
Customize the email template
Next step is adding this new information somewhere in the actual email template, which
represents what your customers will see in their inbox. The default templates for the
original carton mailers in Solidus are located at
core/app/views/spree/carton_mailer/
.
Email templates are made of two main parts:
- A layout, which is identical in all emails
- A content, which is different based on the content.
In this example, we are going to customize the latter, but if you need to make changes to the layout, you can just copy/paste the one provided by Solidus in your own application and change it based on your style preferences.
We can copy paste those files into our project at app/views/awesome_store/carton_mailer/
.
Now, rendering our ETA is a matter of adding something like this where we prefer within the
shipped_email.*.erb
templates:
Will be at your door by <%= @eta.to_s(:short) %>
We can preview how our email looks like taking advantage of
ActionMailer::Preview
:
with your local Rails server enabled, visit
http://localhost:3000/rails/mailers/ to preview the layouts of
mailers provided by Solidus.
And that's it, congratulations! You properly customized your mailer adding a critical information for your users.
Looking for Authentication emails?
Good question, authentication emails (like password reset email or account confirmation email) are not
provided in Solidus core, mainly because the authentication plugin
(solidus_auth_devise) is optional. If you are
using it and you want to customize its emails, you can find this
specific mailer in the solidus_auth_devise
extension
and apply the same procedure followed for customizing the core mailers. The only difference will be with the configuration:
because we use Devise for authentication in this case, we can use
its own preference to choose which class to use as the authentication mailer:
Devise.setup do |config|
config.mailer = 'AmazingStore::UserMailer'
end
Adding new transactional emails
If you want to add new transactional emails to your store, you can create your own mailer following the Action Mailer guide as any other Rails application.
If the event that triggers this new email comes from Solidus, we highly recommend to keep your architecture as orthogonal as possible by subscribing to one of the core events.
For example, if we created a new mailer for sending an email to the admin users when a new order is finalized, this could be the code that trigger the email delivery:
# frozen_string_literal: true
module AwesomeStore
class MailerSubscriber
include Omnes::Subscriber
handle :order_finalized,
with: :send_admin_confirmation_email,
id: :admin_order_mailer_send_confirmation_email
# Sends order confirmation email to the admin
#
# @param event [Omnes::UnstructuredEvent]
def send_admin_confirmation_email(event)
order = event[:order]
AmazingStore::AdminMailer.confirm_email(order).deliver_later
end
end
end