diff --git a/commerce_pos.links.menu.yml b/commerce_pos.links.menu.yml
index d958b53..5375811 100755
--- a/commerce_pos.links.menu.yml
+++ b/commerce_pos.links.menu.yml
@@ -29,4 +29,4 @@ commerce_pos.main:
   title: 'Point of Sale'
   route_name: 'commerce_pos.main'
   parent: 'commerce_pos.base'
-  description: 'Point of Sale Interface'
\ No newline at end of file
+  description: 'Point of Sale Interface'
diff --git a/commerce_pos.permissions.yml b/commerce_pos.permissions.yml
index ef877b2..9aaf12a 100755
--- a/commerce_pos.permissions.yml
+++ b/commerce_pos.permissions.yml
@@ -1,4 +1,4 @@
 'access commerce pos administration pages':
   title: 'Use the commerce point of sale administration pages'
 'access commerce pos pages':
-  title: 'Use the commerce point of sale standard functionality'
\ No newline at end of file
+  title: 'Use the commerce point of sale standard functionality'
diff --git a/src/Form/POSForm.php b/src/Form/POSForm.php
index a1af2a5..e653414 100644
--- a/src/Form/POSForm.php
+++ b/src/Form/POSForm.php
@@ -2,9 +2,13 @@
 
 namespace Drupal\commerce_pos\Form;
 
+use Drupal\commerce_pos_receipt\Ajax\PrintReceiptCommand;
+use Drupal\commerce_pos_receipt\Controller\PrintController;
 use Drupal\commerce_price\Price;
 use Drupal\commerce_store\CurrentStore;
 use Drupal\Component\Datetime\TimeInterface;
+use Drupal\Core\Ajax\AjaxResponse;
+use Drupal\Core\Ajax\HtmlCommand;
 use Drupal\Core\Entity\ContentEntityForm;
 use Drupal\Core\Entity\EntityManagerInterface;
 use Drupal\Core\Entity\EntityTypeBundleInfoInterface;
@@ -91,6 +95,10 @@ class POSForm extends ContentEntityForm {
    * Build the POS Order Form.
    */
   protected function buildOrderForm(array $form, FormStateInterface $form_state) {
+    /* @var \Drupal\commerce_order\Entity\Order $order */
+    $order = $this->entity;
+    $form_state->set('commerce_pos_order_id', $order->id());
+
     $form = parent::buildForm($form, $form_state);
 
     $form['customer'] = [
@@ -229,6 +237,13 @@ class POSForm extends ContentEntityForm {
       '#disabled' => !$balance_paid,
       '#name' => 'commerce-pos-finish',
       '#submit' => ['::submitForm'],
+      '#attributes' => [
+        'class' => ['use-ajax-submit'],
+      ],
+      '#ajax' => [
+        'callback' => [get_class($this), 'ajaxReceipt'],
+        'wrapper' => 'commerce-pos-receipt',
+      ],
       '#element_key' => 'finish-order',
     ];
 
@@ -378,8 +393,8 @@ class POSForm extends ContentEntityForm {
 
     $totals[] = [$this->t('Subtotal'), $formatted_amount];
 
-    // Commerce appears to have a bug where if not adjustments exist, it will return a
-    // 0 => null array, which will still trigger a foreach loop.
+    // Commerce appears to have a bug where if not adjustments exist, it
+    // will return a 0 => null array, which will still trigger a foreach loop.
     foreach ($order->collectAdjustments() as $key => $adjustment) {
       if (!empty($adjustment)) {
         $amount = $adjustment->getAmount();
@@ -516,4 +531,24 @@ class POSForm extends ContentEntityForm {
 
   }
 
+  /**
+   * AJAX callback for the finish button.
+   */
+  public function ajaxReceipt($form, $form_state) {
+    $renderer = \Drupal::service('renderer');
+    $callbackObject = $form_state->getBuildInfo()['callback_object'];
+    $commerce_order = $callbackObject->entity;
+
+    $printController = new PrintController();
+    $build = $printController->showReceipt($commerce_order);
+    unset($build['#receipt']['print']);
+    $response = new AjaxResponse();
+
+    // TODO: could this be turned into 1 command, and if so, is that better?
+    $response->addCommand(new HtmlCommand('#commerce-pos-receipt', $renderer->render($build)));
+    $response->addCommand(new PrintReceiptCommand('#commerce-pos-receipt'));
+
+    return $response;
+  }
+
 }
diff --git a/src/Plugin/Field/FieldWidget/PosOrderItemWidget.php b/src/Plugin/Field/FieldWidget/PosOrderItemWidget.php
index f9f2052..d991088 100644
--- a/src/Plugin/Field/FieldWidget/PosOrderItemWidget.php
+++ b/src/Plugin/Field/FieldWidget/PosOrderItemWidget.php
@@ -432,7 +432,9 @@ class PosOrderItemWidget extends WidgetBase implements WidgetInterface, Containe
    */
   protected function addOrderItem(FieldItemListInterface $items, array &$form, FormStateInterface &$form_state) {
     // Loading the product variation object.
-    $product_variation = ProductVariation::load($form_state->getValue(['order_items', 'target_id', 'product_selector']));
+    $product_variation = ProductVariation::load($form_state->getValue([
+      'order_items', 'target_id', 'product_selector',
+    ]));
     // If we've not loaded a product variation then exit doing nothing.
     if (!$product_variation) {
       // There's nothing to do.
diff --git a/tests/src/FunctionalJavascript/PosFormTest.php b/tests/src/FunctionalJavascript/PosFormTest.php
index ff8315a..4bf2d1c 100644
--- a/tests/src/FunctionalJavascript/PosFormTest.php
+++ b/tests/src/FunctionalJavascript/PosFormTest.php
@@ -42,20 +42,34 @@ class PosFormTest extends JavascriptTestBase {
     $register->save();
 
     $variations = [
-      $this->createProductionVariation(['title' => 'T-shirt XL', 'price' => new Price("23.20", 'USD')]),
+      $this->createProductionVariation([
+        'title' => 'T-shirt XL',
+        'price' => new Price("23.20", 'USD'),
+      ]),
       $this->createProductionVariation(['title' => 'T-shirt L']),
       $this->createProductionVariation(['title' => 'T-shirt M']),
     ];
 
-    $this->createProduct(['variations' => $variations, 'title' => 'T-shirt', 'stores' => [$test_store]]);
+    $this->createProduct([
+      'variations' => $variations,
+      'title' => 'T-shirt',
+      'stores' => [$test_store],
+    ]);
 
     $variations = [
-      $this->createProductionVariation(['title' => 'Jumper XL', 'price' => new Price("50", 'USD')]),
+      $this->createProductionVariation([
+        'title' => 'Jumper XL',
+        'price' => new Price("50", 'USD'),
+      ]),
       $this->createProductionVariation(['title' => 'Jumper L']),
       $this->createProductionVariation(['title' => 'Jumper M']),
     ];
 
-    $this->createProduct(['variations' => $variations, 'title' => 'Jumper', 'stores' => [$test_store]]);
+    $this->createProduct([
+      'variations' => $variations,
+      'title' => 'Jumper',
+      'stores' => [$test_store],
+    ]);
 
     // @todo work out the expected permissions to view products etc...
     $this->drupalLogin($this->rootUser);
@@ -141,7 +155,6 @@ class PosFormTest extends JavascriptTestBase {
     $web_assert->fieldValueEquals('order_items[target_id][order_items][0][unit_price][number]', '40.50');
     // (3 * 40.5) + (1 * 23.20)
     $web_assert->pageTextContains('To Pay $144.70');
-
     // Click on the buttons to remove all the jumpers.
     $this->getSession()->getPage()->findButton('remove_order_item_1')->click();
     $web_assert->assertWaitOnAjaxRequest();
diff --git a/tests/src/Kernel/UPCTest.php b/tests/src/Kernel/UPCTest.php
index 6d5d4e9..c58e47f 100644
--- a/tests/src/Kernel/UPCTest.php
+++ b/tests/src/Kernel/UPCTest.php
@@ -70,7 +70,8 @@ class UPCTest extends CommerceKernelTestBase {
       $this->assertEquals($variation->get('field_upc')->getValue()[0]['value'], '12345');
     }
 
-    // Check that if we try and load a upc that doesn't exist we don't get anything.
+    // Check that if we try and load a upc that doesn't exist
+    // we don't get anything.
     $variations = $upc->lookup('77777');
     $this->assertEmpty($variations);
   }
