diff --git a/config/install/commerce_order.commerce_order_type.pos.yml b/config/install/commerce_order.commerce_order_type.pos.yml
index 75c263a..8b76de4 100755
--- a/config/install/commerce_order.commerce_order_type.pos.yml
+++ b/config/install/commerce_order.commerce_order_type.pos.yml
@@ -9,7 +9,7 @@ id: pos
 workflow: order_pos
 refresh_mode: customer
 refresh_frequency: 300
-sendReceipt: true
+sendReceipt: false
 receiptBcc: ''
 traits: {  }
 locked: false
diff --git a/modules/receipt/commerce_pos_receipt.libraries.yml b/modules/receipt/commerce_pos_receipt.libraries.yml
index cd93c93..3f8bba4 100755
--- a/modules/receipt/commerce_pos_receipt.libraries.yml
+++ b/modules/receipt/commerce_pos_receipt.libraries.yml
@@ -1,3 +1,12 @@
+jQuery.print:
+  remote: https://github.com/DoersGuild/jQuery.print
+  version: 1.5.1
+  license:
+    name: CC BY 3.0
+    url: https://github.com/DoersGuild/jQuery.print/blob/master/LICENSE
+    gpl-compatible: no
+  js:
+    /libraries/jQuery.print/jQuery.print.js: {}
 receipt:
   version: 1.x
   css:
@@ -10,12 +19,4 @@ receipt:
     - core/drupal
     - core/drupalSettings
     - core/drupal.ajax
-jQuery.print:
-  remote: https://github.com/DoersGuild/jQuery.print
-  version: 1.5.1
-  license:
-    name: CC BY 3.0
-    url: https://github.com/DoersGuild/jQuery.print/blob/master/LICENSE
-    gpl-compatible: no
-  js:
-    /libraries/jQuery.print/jQuery.print.js: {}
+    - commerce_pos_receipt/jQuery.print
\ No newline at end of file
diff --git a/modules/receipt/commerce_pos_receipt.module b/modules/receipt/commerce_pos_receipt.module
index 20f5b65..7c38f2a 100644
--- a/modules/receipt/commerce_pos_receipt.module
+++ b/modules/receipt/commerce_pos_receipt.module
@@ -5,6 +5,7 @@
  * Contains commerce_pos_receipt.module.
  */
 
+use Drupal\commerce_order\Entity\Order;
 use Drupal\Core\Entity\EntityInterface;
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\Core\Routing\RouteMatchInterface;
@@ -73,6 +74,7 @@ function commerce_pos_receipt_entity_operation(EntityInterface $entity) {
  */
 function commerce_pos_receipt_form_commerce_pos_alter(&$form, FormStateInterface $form_state, $form_id) {
   if ($form_state->get('step') == 'payment') {
+    $form['actions']['finish']['#submit'][] = 'commerce_pos_receipt_form_commerce_pos_submit';
     // Add a radio button to select whether the user wants to print or email the
     // receipt.
     $form['actions']['print_email_receipt'] = [
@@ -86,23 +88,41 @@ function commerce_pos_receipt_form_commerce_pos_alter(&$form, FormStateInterface
       '#default_value' => 'print',
       '#weight' => -10,
     ];
+  }
+}
 
-    $form['receipt'] = [
-      '#type' => 'container',
-    ];
-
-    $form['#attached']['library'][] = 'commerce_pos_receipt/receipt';
-    $form['#attached']['library'][] = 'commerce_pos_receipt/jQuery.print';
-
-    $form['#attached']['drupalSettings']['commercePosReceipt'] = [
-      'orderId' => $form_state->getValue('order_id'),
-    ];
-
-    $form['receipt']['contents'] = [
-      '#markup' => '<div id="commerce-pos-receipt"></div>',
-    ];
+/**
+ * Form submission handler.
+ *
+ * @see commerce_pos_receipt_form_commerce_pos_alter()
+ */
+function commerce_pos_receipt_form_commerce_pos_submit($form, FormStateInterface $form_state) {
+  $triggering_element = $form_state->getTriggeringElement();
+
+  if ($form_state->get('step') == 'payment' && $triggering_element['#element_key'] == 'finish-order') {
+    $order_id = $form_state->getValue('order_id');
+    $action = $form_state->getValue(['actions', 'print_email_receipt']);
+
+    // Send an email if we need to.
+    if ($action === 'email' || $action === 'print_and_email') {
+      $commerce_order = Order::load($order_id);
+      if (\Drupal::service('commerce_pos_receipt.receipt_mailer')->send($commerce_order)) {
+        drupal_set_message(t('An email with the receipt has been successfully sent to @mail.', [
+          '@mail' => $commerce_order->getEmail(),
+        ]));
+      }
+      else {
+        drupal_set_message(t('There was a problem sending the email to @mail.', [
+          '@mail' => $commerce_order->getEmail(),
+        ]), 'error');
+      }
+    }
 
-    $form['actions']['finish']['#attributes']['class'][] = 'commerce-pos-receipt-button';
+    // Redirect to the receipt page if we need to.
+    if ($action === 'print' || $action === 'print_and_email') {
+      // Redirect to the receipt page.
+      $form_state->setRedirect('commerce_pos_receipt.show', ['commerce_order' => $order_id]);
+    }
   }
 }
 
diff --git a/modules/receipt/commerce_pos_receipt.routing.yml b/modules/receipt/commerce_pos_receipt.routing.yml
index 12e35bb..cbba49f 100644
--- a/modules/receipt/commerce_pos_receipt.routing.yml
+++ b/modules/receipt/commerce_pos_receipt.routing.yml
@@ -1,15 +1,12 @@
 commerce_pos_receipt.ajax:
-  path: '/admin/commerce/pos/{commerce_order}/ajax-receipt/{print_or_email}'
+  path: '/admin/commerce/pos/{commerce_order}/ajax-receipt'
   defaults:
     _controller: '\Drupal\commerce_pos_receipt\Controller\PrintController::ajaxReceipt'
     _title: 'Order Receipt'
-    print_or_email: 'print'
   options:
     parameters:
       commerce_order:
         type: 'entity:commerce_order'
-      print_or_email:
-        type: 'String'
   requirements:
     _permission: 'administer commerce_order'
 
diff --git a/modules/receipt/commerce_pos_receipt.services.yml b/modules/receipt/commerce_pos_receipt.services.yml
new file mode 100755
index 0000000..829cee6
--- /dev/null
+++ b/modules/receipt/commerce_pos_receipt.services.yml
@@ -0,0 +1,7 @@
+services:
+  commerce_pos_receipt.receipt_mailer:
+    class: Drupal\commerce_pos_receipt\ReceiptMailer
+    arguments: ['@renderer', '@plugin.manager.mail', '@current_user', '@config.factory', '@logger.factory', '@commerce_pos_receipt.receipt_builder']
+  commerce_pos_receipt.receipt_builder:
+    class: Drupal\commerce_pos_receipt\ReceiptBuilder
+    arguments: ['@commerce_price.number_formatter_factory', '@entity_type.manager', '@config.factory']
diff --git a/modules/receipt/js/commerce_pos_receipt.js b/modules/receipt/js/commerce_pos_receipt.js
index 1018b78..b11808b 100644
--- a/modules/receipt/js/commerce_pos_receipt.js
+++ b/modules/receipt/js/commerce_pos_receipt.js
@@ -1,20 +1,5 @@
 (function ($, Drupal, drupalSettings) {
 
-  /**
-   * Ajax command to set the toolbar subtrees.
-   *
-   * @param {Drupal.Ajax} ajax
-   *   {@link Drupal.Ajax} object created by {@link Drupal.ajax}.
-   * @param {object} response
-   *   JSON response from the Ajax request.
-   * @param {number} [status]
-   *   XMLHttpRequest status.
-   */
-  Drupal.AjaxCommands.prototype.completeOrder = function (ajax, response, status) {
-    // Just click the button to complete the order.
-    $('.commerce-pos-receipt-button-done').click();
-  };
-
   /**
    * Ajax command to set the toolbar subtrees.
    *
@@ -28,16 +13,11 @@
   Drupal.AjaxCommands.prototype.printReceipt = function (ajax, response, status) {
     // If jQuery.print does not exist then this will not be a function. This
     // can occur if the libraries is not yet downloaded and under test.
-    if (typeof $(response.content).print === "function") {
-      $(response.content).print({
-        globalStyles: false,
-        stylesheet: drupalSettings.commercePosReceipt.cssUrl,
-        deferred: $.Deferred().done(function () {
-          // Sloppy, should probaby be nice and scalable, but that might never be needed
-          $('.commerce-pos-receipt-button-done').click();
-        })
-      });
-    }
+    if (typeof $(response.content).print === "function") {$(response.content).print({
+      globalStyles: false,
+      stylesheet: drupalSettings.commercePosReceipt.cssUrl
+
+    });}
     else {
       $('.commerce-pos-receipt-button-done').click();
     }
@@ -72,7 +52,7 @@
 
         $.ajax(
           {
-            url: "/admin/commerce/pos/" + drupalSettings.commercePosReceipt.orderId + '/ajax-receipt/' + $('input[name="actions[print_email_receipt]"]:checked').val(),
+            url: "/admin/commerce/pos/" + drupalSettings.commercePosReceipt.orderId + '/ajax-receipt',
             success: function (data) {
               var ajaxObject = Drupal.ajax(
                 {
diff --git a/modules/receipt/src/Ajax/CompleteOrderCommand.php b/modules/receipt/src/Ajax/CompleteOrderCommand.php
deleted file mode 100644
index 5c8e17a..0000000
--- a/modules/receipt/src/Ajax/CompleteOrderCommand.php
+++ /dev/null
@@ -1,21 +0,0 @@
-<?php
-
-namespace Drupal\commerce_pos_receipt\Ajax;
-
-use Drupal\Core\Ajax\CommandInterface;
-
-/**
- * AJAX command for completing the order payment process.
- */
-class CompleteOrderCommand implements CommandInterface {
-
-  /**
-   * Return an array to be run through json_encode and sent to the client.
-   */
-  public function render() {
-    return [
-      'command' => 'completeOrder',
-    ];
-  }
-
-}
diff --git a/modules/receipt/src/Controller/PrintController.php b/modules/receipt/src/Controller/PrintController.php
index 46bed87..538c78f 100644
--- a/modules/receipt/src/Controller/PrintController.php
+++ b/modules/receipt/src/Controller/PrintController.php
@@ -3,15 +3,15 @@
 namespace Drupal\commerce_pos_receipt\Controller;
 
 use Drupal\commerce_order\Entity\OrderInterface;
-use Drupal\commerce_pos_receipt\Ajax\CompleteOrderCommand;
 use Drupal\commerce_pos_receipt\Ajax\PrintReceiptCommand;
-use Drupal\commerce_price\Entity\Currency;
+use Drupal\commerce_pos_receipt\ReceiptBuilder;
 use Drupal\Core\Access\AccessResult;
 use Drupal\Core\Ajax\AjaxResponse;
 use Drupal\Core\Ajax\HtmlCommand;
 use Drupal\Core\Ajax\SettingsCommand;
 use Drupal\Core\Controller\ControllerBase;
 use Drupal\Core\Url;
+use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
  * Class PrintController.
@@ -19,43 +19,45 @@ use Drupal\Core\Url;
 class PrintController extends ControllerBase {
 
   /**
-   * A controller callback.
+   * @var \Drupal\commerce_pos_receipt\ReceiptBuilder
    */
-  public function ajaxReceipt(OrderInterface $commerce_order, $print_or_email) {
-    $renderer = \Drupal::service('renderer');
-
-    $build = $this->showReceipt($commerce_order);
-    unset($build['#receipt']['print']);
-    $build = $renderer->render($build);
+  protected $receiptBuilder;
 
-    $response = new AjaxResponse();
+  public function __construct(ReceiptBuilder $receipt_builder) {
+    $this->receiptBuilder = $receipt_builder;
+  }
 
-    // If the user opted to get an email with the receipt.
-    if ($print_or_email != 'print') {
-      $this->sendEmailReceipt($commerce_order, $build);
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container) {
+    return new static(
+      $container->get('commerce_pos_receipt.receipt_builder')
+    );
+  }
 
-      // Finally, if the user only wants an email to be sent, we just call the
-      // complete order command which submits the form as usual.
-      if ($print_or_email == 'email') {
-        $response->addCommand(new CompleteOrderCommand());
-      }
-    }
 
-    // If the user opted to print the receipt.
-    if ($print_or_email != 'email') {
-      $module_handler = \Drupal::service('module_handler');
-      $module_path = $module_handler->getModule('commerce_pos_receipt')->getPath();
+  /**
+   * A controller callback.
+   */
+  public function ajaxReceipt(OrderInterface $commerce_order) {
+    $renderer = \Drupal::service('renderer');
 
-      // TODO: could this be turned into 1 command, and if so, is that better?
-      $response->addCommand(new HtmlCommand('#commerce-pos-receipt', $build));
-      $response->addCommand(new SettingsCommand([
-        'commercePosReceipt' => [
-          'cssUrl' => Url::fromUri('base:' . $module_path . '/css/commerce_pos_receipt_print.css', ['absolute' => TRUE])->toString(),
-        ],
-      ], TRUE));
-      $response->addCommand(new PrintReceiptCommand('#commerce-pos-receipt'));
-    }
+    $build = $this->receiptBuilder->build($commerce_order);
+    $build = $renderer->render($build);
 
+    $response = new AjaxResponse();
+    $module_handler = \Drupal::service('module_handler');
+    $module_path = $module_handler->getModule('commerce_pos_receipt')->getPath();
+
+    // TODO: could this be turned into 1 command, and if so, is that better?
+    $response->addCommand(new HtmlCommand('#commerce-pos-receipt', $build));
+    $response->addCommand(new SettingsCommand([
+      'commercePosReceipt' => [
+        'cssUrl' => Url::fromUri('base:' . $module_path . '/css/commerce_pos_receipt_print.css', ['absolute' => TRUE])->toString(),
+      ],
+    ], TRUE));
+    $response->addCommand(new PrintReceiptCommand('#commerce-pos-receipt'));
     return $response;
   }
 
@@ -63,135 +65,27 @@ class PrintController extends ControllerBase {
    * A controller callback.
    */
   public function showReceipt(OrderInterface $commerce_order) {
-
-    $number_formatter_factory = \Drupal::service('commerce_price.number_formatter_factory');
-    $number_formatter = $number_formatter_factory->createInstance();
-
-    $sub_total_price = $commerce_order->getSubtotalPrice();
-    $currency = Currency::load($sub_total_price->getCurrencyCode());
-    $formatted_amount = $number_formatter->formatCurrency($sub_total_price->getNumber(), $currency);
-
-    // In the future add a setting to display group or individual for same skus.
-    $items = $commerce_order->getItems();
-    foreach ($items as $item) {
-      $totals[] = [
-        $item->getTitle() . ' (' . $item->getQuantity() . ')',
-        $number_formatter->formatCurrency($item->getAdjustedTotalPrice()->getNumber(), $currency),
-      ];
-    }
-
-    $totals[] = ['Subtotal', $formatted_amount];
-
-    // Commerce appears to have a bug where if no adjustments exist, it will
-    // return a 0 => null array, which will still trigger a foreach loop.
-    foreach ($commerce_order->collectAdjustments() as $key => $adjustment) {
-      if (!empty($adjustment)) {
-        $amount = $adjustment->getAmount();
-        $currency = Currency::load($amount->getCurrencyCode());
-        $formatted_amount = $number_formatter->formatCurrency($amount->getNumber(), $currency);
-
-        $totals[] = [
-          $adjustment->getLabel(),
-          $formatted_amount,
-        ];
-      }
-    }
-
-    // Collecting the total price on the cart.
-    $total_price = $commerce_order->getTotalPrice();
-    $formatted_amount = $number_formatter->formatCurrency($total_price->getNumber(), $currency);
-    $totals[] = ['Total', $formatted_amount];
-
-    $payment_storage = \Drupal::entityTypeManager()->getStorage('commerce_payment');
-    $payments = $payment_storage->loadMultipleByOrder($commerce_order);
-    foreach ($payments as $payment) {
-      $totals[] = ['Payment', $payment->getState()->getLabel()];
-    }
-    $ajax_url = URL::fromRoute('commerce_pos_receipt.ajax', ['commerce_order' => $commerce_order->id()], [
+    $build = $this->receiptBuilder->build($commerce_order);
+    $ajax_url = Url::fromRoute('commerce_pos_receipt.ajax', ['commerce_order' => $commerce_order->id()], [
       'attributes' => [
         'class' => ['use-ajax', 'button'],
       ],
     ]);
-
-    $config = \Drupal::config('commerce_pos_receipt.settings');
-    $build = ['#theme' => 'commerce_pos_receipt'];
-    $build['#receipt'] = [
-      'header' => [
-        '#markup' => check_markup($config->get('header'), $config->get('header_format')),
-      ],
-      'body' => [
-        '#type' => 'table',
-        '#rows' => $totals,
-      ],
-      'footer' => [
-        '#markup' => check_markup($config->get('footer'), $config->get('footer_format')),
-      ],
-      'print' => [
-        '#title' => t('Print receipt'),
-        '#prefix' => '<div id="commerce-pos-receipt"></div>',
-        '#type' => 'link',
-        '#url' => $ajax_url,
-      ],
+    // Add a print button.
+    $build['#receipt']['actions']['print'] = [
+      '#title' => t('Print receipt'),
+      '#prefix' => '<div id="commerce-pos-receipt"></div>',
+      '#type' => 'link',
+      '#url' => $ajax_url,
     ];
-    return $build;
-  }
-
-  /**
-   * Sends an email with the order receipt.
-   *
-   * @param object $commerce_order
-   *   The order entity.
-   * @param string $build
-   *   The receipt markup.
-   */
-  public function sendEmailReceipt($commerce_order, $build) {
-    $renderer = \Drupal::service('renderer');
-
-    // Send an email with the receipt.
-    $mail_manager = \Drupal::service('plugin.manager.mail');
-    $module = 'commerce_pos_receipt';
-    $key = 'commerce_pos_order_receipt';
-    $to = $commerce_order->getEmail();
-    $customer = $commerce_order->getCustomer();
-    $themed_email_message = [
-      '#theme' => 'commerce-pos-receipt-email',
-      '#customer_name' => $customer->getAccountName(),
-      '#order_id' => $commerce_order->id(),
-      '#receipt_markup' => $build,
-      '#site_name' => \Drupal::config('system.site')->get('name'),
+    // Add a new order button.
+    $build['#receipt']['actions']['new_order'] = [
+      '#title' => t('New order'),
+      '#type' => 'link',
+      '#url' => Url::fromRoute('commerce_pos.main'),
+      '#attributes' => ['class' => 'button'],
     ];
-    $params['message'] = $renderer->render($themed_email_message);
-    $params['order_id'] = $commerce_order->id();
-    $langcode = \Drupal::currentUser()->getPreferredLangcode();
-    $send = TRUE;
-
-    // Officially, send the email.
-    $result = $mail_manager->mail($module, $key, $to, $langcode, $params, NULL, $send);
-
-    $message_type = 'status';
-    // If there was a problem sending the email.
-    if ($result['result'] !== TRUE) {
-      $message = t('There was a problem sending the email to @mail.', [
-        '@mail' => $commerce_order->getEmail(),
-      ]);
-      $message_type = 'error';
-    }
-    // Else, if it was successful.
-    else {
-      $message = $this->t('An email with the receipt has been successfully sent to @mail.', [
-        '@mail' => $commerce_order->getEmail(),
-      ]);
-    }
-
-    // Display a message to the user regarding the result of sending the mail.
-    drupal_set_message($message, $message_type);
-    // Log the result in the watchdog as well.
-    if ($message_type == 'error') {
-      \Drupal::logger('commerce_pos_receipt')->error($message);
-    }
-    else {
-      \Drupal::logger('commerce_pos_receipt')->notice($message);
-    }
+    return $build;
   }
 
   /**
diff --git a/modules/receipt/src/ReceiptBuilder.php b/modules/receipt/src/ReceiptBuilder.php
new file mode 100644
index 0000000..dbeecf3
--- /dev/null
+++ b/modules/receipt/src/ReceiptBuilder.php
@@ -0,0 +1,115 @@
+<?php
+
+namespace Drupal\commerce_pos_receipt;
+
+use Drupal\commerce_order\Entity\OrderInterface;
+use Drupal\commerce_price\Entity\Currency;
+use Drupal\commerce_price\NumberFormatterFactoryInterface;
+use Drupal\Core\Config\ConfigFactoryInterface;
+use Drupal\Core\Entity\EntityTypeManagerInterface;
+
+/**
+ * Builds receipt render arrays.
+ */
+class ReceiptBuilder {
+
+  /**
+   * @var \Drupal\commerce_price\NumberFormatterFactoryInterface
+   */
+  protected $numberFormatterFactory;
+
+  /**
+   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
+   */
+  protected $entityTypeManager;
+
+  /**
+   * @var \Drupal\Core\Config\ConfigFactoryInterface
+   */
+  protected $configFactory;
+
+  /**
+   * ReceiptBuilder constructor.
+   *
+   * @param \Drupal\commerce_price\NumberFormatterFactoryInterface $number_formatter_factory
+   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
+   * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
+   */
+  public function __construct(NumberFormatterFactoryInterface $number_formatter_factory, EntityTypeManagerInterface $entity_type_manager, ConfigFactoryInterface $config_factory) {
+    $this->numberFormatterFactory = $number_formatter_factory;
+    $this->entityTypeManager = $entity_type_manager;
+    $this->configFactory = $config_factory;
+  }
+
+  /**
+   * Builds a render array for a commerce_pos receipt.
+   *
+   * @return array
+   *   A receipt as a render array.
+   */
+  public function build(OrderInterface $commerce_order) {
+    $number_formatter = $this->numberFormatterFactory->createInstance();
+
+    $sub_total_price = $commerce_order->getSubtotalPrice();
+    $currency = Currency::load($sub_total_price->getCurrencyCode());
+    $formatted_amount = $number_formatter->formatCurrency($sub_total_price->getNumber(), $currency);
+
+    // In the future add a setting to display group or individual for same skus.
+    $items = $commerce_order->getItems();
+    foreach ($items as $item) {
+      $totals[] = [
+        $item->getTitle() . ' (' . $item->getQuantity() . ')',
+        $number_formatter->formatCurrency($item->getAdjustedTotalPrice()->getNumber(), $currency),
+      ];
+    }
+
+    $totals[] = ['Subtotal', $formatted_amount];
+
+    // Commerce appears to have a bug where if no adjustments exist, it will
+    // return a 0 => null array, which will still trigger a foreach loop.
+    foreach ($commerce_order->collectAdjustments() as $key => $adjustment) {
+      if (!empty($adjustment)) {
+        $amount = $adjustment->getAmount();
+        $currency = Currency::load($amount->getCurrencyCode());
+        $formatted_amount = $number_formatter->formatCurrency($amount->getNumber(), $currency);
+
+        $totals[] = [
+          $adjustment->getLabel(),
+          $formatted_amount,
+        ];
+      }
+    }
+
+    // Collecting the total price on the cart.
+    $total_price = $commerce_order->getTotalPrice();
+    $formatted_amount = $number_formatter->formatCurrency($total_price->getNumber(), $currency);
+    $totals[] = ['Total', $formatted_amount];
+
+    $payment_storage = $this->entityTypeManager->getStorage('commerce_payment');
+    $payments = $payment_storage->loadMultipleByOrder($commerce_order);
+    foreach ($payments as $payment) {
+      $totals[] = ['Payment', $payment->getState()->getLabel()];
+    }
+
+    $config = $this->configFactory->get('commerce_pos_receipt.settings');
+    $build = ['#theme' => 'commerce_pos_receipt'];
+    $build['#receipt'] = [
+      'header' => [
+        '#type' => 'processed_text',
+        '#text' => $config->get('header'),
+        '#format' => $config->get('header_format'),
+      ],
+      'body' => [
+        '#type' => 'table',
+        '#rows' => $totals,
+      ],
+      'footer' => [
+        '#type' => 'processed_text',
+        '#text' => $config->get('footer'),
+        '#format' => $config->get('footer_format'),
+      ],
+    ];
+    return $build;
+  }
+
+}
diff --git a/modules/receipt/src/ReceiptMailer.php b/modules/receipt/src/ReceiptMailer.php
new file mode 100644
index 0000000..9dc4c88
--- /dev/null
+++ b/modules/receipt/src/ReceiptMailer.php
@@ -0,0 +1,108 @@
+<?php
+
+namespace Drupal\commerce_pos_receipt;
+
+use Drupal\commerce_order\Entity\OrderInterface;
+use Drupal\Core\Config\ConfigFactoryInterface;
+use Drupal\Core\Logger\LoggerChannelFactory;
+use Drupal\Core\Mail\MailManagerInterface;
+use Drupal\Core\Render\RendererInterface;
+use Drupal\Core\Session\AccountInterface;
+
+/**
+ * Sends receipts via email.
+ */
+class ReceiptMailer {
+
+  /**
+   * @var \Drupal\Core\Render\RendererInterface
+   */
+  protected $renderer;
+
+  /**
+   * @var \Drupal\Core\Mail\MailManagerInterface
+   */
+  protected $mailManager;
+
+  /**
+   * @var \Drupal\Core\Session\AccountInterface
+   */
+  protected $currentUser;
+
+  /**
+   * @var \Drupal\Core\Config\ConfigFactoryInterface
+   */
+  protected $configFactory;
+
+  /**
+   * @var \Drupal\Core\Logger\LoggerChannelInterface
+   */
+  protected $logger;
+
+  /**
+   * @var \Drupal\commerce_pos_receipt\ReceiptBuilder
+   */
+  protected $receiptBuilder;
+
+  /**
+   * ReceiptMailer constructor.
+   * @param \Drupal\Core\Render\RendererInterface $renderer
+   * @param \Drupal\Core\Mail\MailManagerInterface $mail_manager
+   * @param \Drupal\Core\Session\AccountInterface $current_user
+   * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
+   * @param \Drupal\Core\Logger\LoggerChannelFactory $logger_factory
+   * @param \Drupal\commerce_pos_receipt\ReceiptBuilder $receipt_builder
+   */
+  public function __construct(RendererInterface $renderer, MailManagerInterface $mail_manager, AccountInterface $current_user, ConfigFactoryInterface $config_factory, LoggerChannelFactory $logger_factory, ReceiptBuilder $receipt_builder) {
+    $this->renderer = $renderer;
+    $this->mailManager = $mail_manager;
+    $this->currentUser = $current_user;
+    $this->configFactory = $config_factory;
+    $this->logger = $logger_factory->get('commerce_pos_receipt');
+    $this->receiptBuilder = $receipt_builder;
+  }
+
+  /**
+   * Sends an email with the order receipt.
+   *
+   * @param \Drupal\commerce_order\Entity\OrderInterface $commerce_order
+   *   The order entity.
+   *
+   * @return bool
+   *   TRUE if the mail sent successfully, FALSE if not.
+   */
+  public function send(OrderInterface $commerce_order) {
+
+    $build = $this->receiptBuilder->build($commerce_order);
+    $build = $this->renderer->render($build);
+
+    // Send an email with the receipt.
+    $module = 'commerce_pos_receipt';
+    $key = 'commerce_pos_order_receipt';
+    $to = $commerce_order->getEmail();
+    $customer = $commerce_order->getCustomer();
+    $themed_email_message = [
+      '#theme' => 'commerce-pos-receipt-email',
+      '#customer_name' => $customer->getAccountName(),
+      '#order_id' => $commerce_order->id(),
+      '#receipt_markup' => $build,
+      '#site_name' => $this->configFactory->get('system.site')->get('name'),
+    ];
+    $params['message'] = $this->renderer->renderPlain($themed_email_message);
+    $params['order_id'] = $commerce_order->id();
+    $langcode = $this->currentUser->getPreferredLangcode();
+    $send = TRUE;
+
+    // Officially, send the email.
+    $result = $this->mailManager->mail($module, $key, $to, $langcode, $params, NULL, $send);
+    if ($result['result'] === TRUE) {
+      // Log successful sending. An error is logged already by the mail manager.
+      $this->logger->notice(
+        'A receipt email for order ID @order_id has been successfully sent to @mail.',
+        ['@mail' => $commerce_order->getEmail(), '@order_id' => $commerce_order->id()]
+      );
+    }
+    return (bool) $result['result'];
+  }
+
+}
diff --git a/modules/receipt/templates/commerce-pos-receipt.html.twig b/modules/receipt/templates/commerce-pos-receipt.html.twig
index dd3eae8..0b913ce 100644
--- a/modules/receipt/templates/commerce-pos-receipt.html.twig
+++ b/modules/receipt/templates/commerce-pos-receipt.html.twig
@@ -11,10 +11,9 @@
 #}
 
 {{ attach_library('commerce_pos_receipt/receipt') }}
-{{ attach_library('commerce_pos_receipt/jQuery.print') }}
 <div class="commerce-pos-receipt">
-    <div class="receipt-print">{{ receipt.print }}</div>
     <div class="receipt-header">{{ receipt.header }}</div>
     <div class="receipt-body">{{ receipt.body }}</div>
     <div class="receipt-footer">{{ receipt.footer }}</div>
 </div>
+{% if receipt.actions %}<div class="receipt-actions">{{ receipt.actions }}</div>{% endif %}
diff --git a/modules/receipt/tests/src/FunctionalJavascript/PosReceiptTest.php b/modules/receipt/tests/src/FunctionalJavascript/PosReceiptTest.php
index 0e7b88b..45805a3 100644
--- a/modules/receipt/tests/src/FunctionalJavascript/PosReceiptTest.php
+++ b/modules/receipt/tests/src/FunctionalJavascript/PosReceiptTest.php
@@ -2,6 +2,8 @@
 
 namespace Drupal\Tests\commerce_pos_receipt\FunctionalJavascript;
 
+use Drupal\Core\Test\AssertMailTrait;
+use Drupal\Core\Url;
 use Drupal\FunctionalJavascriptTests\JavascriptTestBase;
 use Drupal\Tests\commerce_pos\Functional\CommercePosCreateStoreTrait;
 
@@ -10,6 +12,7 @@ use Drupal\Tests\commerce_pos\Functional\CommercePosCreateStoreTrait;
  */
 class PosReceiptTest extends JavascriptTestBase {
   use CommercePosCreateStoreTrait;
+  use AssertMailTrait;
 
   /**
    * Modules to enable.
@@ -31,9 +34,9 @@ class PosReceiptTest extends JavascriptTestBase {
   }
 
   /**
-   * Tests receipt function.
+   * Tests email receipt function.
    */
-  public function testReceipt() {
+  public function testEmailReceipt() {
     $web_assert = $this->assertSession();
     $this->drupalGet('admin/commerce/pos/main');
     // There is only one register.
@@ -64,8 +67,21 @@ class PosReceiptTest extends JavascriptTestBase {
     // Go to the payment page.
     $this->click('.commerce-pos input[name="op"]');
     $this->click('input[name="commerce-pos-pay-keypad-add"]');
+    // Generate an email receipt.
+    $this->getSession()->getPage()->findField('edit-actions-print-email-receipt-email')->check();
+
     $this->click('input[name="commerce-pos-finish"]');
-    $this->waitForAjaxToFinish();
+
+    // Test the receipt email has been set.
+    $mail = $this->getMails()[0];
+    $this->assertEquals('Receipt for Order #1', $mail['subject']);
+    $this->assertEquals('commerce_pos_receipt_commerce_pos_order_receipt', $mail['id']);
+    $this->assertContains('$50.00', $mail['body']);
+    $this->assertCount(1, $this->getMails());
+
+    // We should be back on the main POS form.
+    $this->assertUrl(Url::fromRoute('commerce_pos.main'));
+
     // We now have a complete order and new draft order.
     $this->drupalGet('admin/commerce/orders');
     // The first row has a Show receipt action and the second does not.
@@ -74,11 +90,92 @@ class PosReceiptTest extends JavascriptTestBase {
   }
 
   /**
-   * Waits for jQuery to become active and animations to complete.
+   * Tests print receipt function.
+   */
+  public function testPrintReceipt() {
+    $web_assert = $this->assertSession();
+    $this->drupalGet('admin/commerce/pos/main');
+    // There is only one register.
+    $web_assert->fieldValueEquals('register', 1);
+    $web_assert->pageTextContains('Test register');
+    $this->drupalPostForm(NULL, [], 'Select Register');
+
+    // Now we should be able to select order items.
+    $autocomplete_field = $this->getSession()->getPage()->findField('order_items[target_id][product_selector]');
+    $autocomplete_field->setValue('Jum');
+    $this->getSession()->getDriver()->keyDown($autocomplete_field->getXpath(), 'p');
+    $web_assert->waitOnAutocomplete();
+    $results = $this->getSession()->getPage()->findAll('css', '.ui-autocomplete li');
+    // Click on of the auto-complete.
+    $results[0]->click();
+    $web_assert->assertWaitOnAjaxRequest();
+
+    // Go to the payment page.
+    $this->click('.commerce-pos input[name="op"]');
+    $this->click('input[name="commerce-pos-pay-keypad-add"]');
+    $this->click('input[name="commerce-pos-finish"]');
+
+    // Test the no email has been set.
+    $this->assertCount(0, $this->getMails());
+
+    // We should be on the receipt page.
+    $this->assertUrl(Url::fromRoute('commerce_pos_receipt.show', ['commerce_order' => 1]));
+
+    // We should have sever instances of the following text.
+    $web_assert->pageTextContains('$50.00');
+    // We can't click on the link because it depends on jQuery.print.
+    $web_assert->linkExists('Print receipt');
+
+    // Ensure that we can go back to creating orders.
+    $this->clickLink('New order');
+    $this->assertUrl(Url::fromRoute('commerce_pos.main'));
+  }
+
+  /**
+   * Tests print and email receipts function.
    */
-  protected function waitForAjaxToFinish() {
-    $condition = "(0 === jQuery.active && 0 === jQuery(':animated').length)";
-    $this->assertJsCondition($condition, 10000);
+  public function testPrintAndEmailReceipt() {
+    $web_assert = $this->assertSession();
+    $this->drupalGet('admin/commerce/pos/main');
+    // There is only one register.
+    $web_assert->fieldValueEquals('register', 1);
+    $web_assert->pageTextContains('Test register');
+    $this->drupalPostForm(NULL, [], 'Select Register');
+
+    // Now we should be able to select order items.
+    $autocomplete_field = $this->getSession()->getPage()->findField('order_items[target_id][product_selector]');
+    $autocomplete_field->setValue('Jum');
+    $this->getSession()->getDriver()->keyDown($autocomplete_field->getXpath(), 'p');
+    $web_assert->waitOnAutocomplete();
+    $results = $this->getSession()->getPage()->findAll('css', '.ui-autocomplete li');
+    // Click on of the auto-complete.
+    $results[0]->click();
+    $web_assert->assertWaitOnAjaxRequest();
+
+    // Go to the payment page.
+    $this->click('.commerce-pos input[name="op"]');
+    $this->click('input[name="commerce-pos-pay-keypad-add"]');
+    $this->getSession()->getPage()->findField('edit-actions-print-email-receipt-print-and-email')->check();
+    $this->click('input[name="commerce-pos-finish"]');
+
+    // Test the email has been sent.
+    $mail = $this->getMails()[0];
+    $this->assertEquals('Receipt for Order #1', $mail['subject']);
+    $this->assertEquals('commerce_pos_receipt_commerce_pos_order_receipt', $mail['id']);
+    $this->assertContains('$50.00', $mail['body']);
+    $this->assertCount(1, $this->getMails());
+
+    // We should be on the receipt page.
+    $this->assertUrl(Url::fromRoute('commerce_pos_receipt.show', ['commerce_order' => 1]));
+
+    // We should have sever instances of the following text.
+    $web_assert->pageTextContains('$50.00');
+    // We can't click on the link because it depends on jQuery.print.
+    $web_assert->linkExists('Print receipt');
+
+    // Ensure that we can go back to creating orders.
+    $this->clickLink('New order');
+    $this->assertUrl(Url::fromRoute('commerce_pos.main'));
   }
 
 }
