diff --git a/modules/checkout/src/Plugin/Commerce/CheckoutPane/CustomerComments.php b/modules/checkout/src/Plugin/Commerce/CheckoutPane/CustomerComments.php
new file mode 100644
index 0000000000000000000000000000000000000000..4f9f17f0eda10ee6dbc44405760e048a1462a79f
--- /dev/null
+++ b/modules/checkout/src/Plugin/Commerce/CheckoutPane/CustomerComments.php
@@ -0,0 +1,60 @@
+<?php
+
+namespace Drupal\commerce_checkout\Plugin\Commerce\CheckoutPane;
+
+use Drupal\Component\Utility\Html;
+use Drupal\Core\Form\FormStateInterface;
+
+/**
+ * Provides the customer comments pane.
+ *
+ * @CommerceCheckoutPane(
+ *    id = "customer_comments",
+ *    label = @Translation("Comments"),
+ *    default_step = "_disabled",
+ *    wrapper_element = "fieldset",
+ *  )
+ */
+class CustomerComments extends CheckoutPaneBase implements CheckoutPaneInterface {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildPaneSummary() {
+    $summary = parent::buildPaneSummary();
+    if ($comments = $this->order->getCustomerComments()) {
+      $summary[] = ['#markup' => $comments];
+    }
+
+    return $summary;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildPaneForm(array $pane_form, FormStateInterface $form_state, array &$complete_form) {
+    $pane_form['comments'] = [
+      '#type' => 'textarea',
+      '#title' => $this->t('Comments'),
+      '#title_display' => 'invisible',
+      '#default_value' => $this->order->getCustomerComments(),
+    ];
+
+    return $pane_form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitPaneForm(array &$pane_form, FormStateInterface $form_state, array &$complete_form) {
+    parent::submitPaneForm($pane_form, $form_state, $complete_form);
+    if (!empty($form_state->getValue('customer_comments')['comments'])) {
+      $comment = nl2br(Html::escape($form_state->getValue('customer_comments')['comments']));
+      $this->order->setCustomerComments($comment);
+    }
+    else {
+      $this->order->set('customer_comments', NULL);
+    }
+  }
+
+}
diff --git a/modules/log/commerce_log.commerce_log_templates.yml b/modules/log/commerce_log.commerce_log_templates.yml
index 469d9d5bb0aa1cea0c2ad02253809e05cace780b..00ab3851bc34d51c29af098bc0665a59b5b4a520 100644
--- a/modules/log/commerce_log.commerce_log_templates.yml
+++ b/modules/log/commerce_log.commerce_log_templates.yml
@@ -64,6 +64,10 @@ commerce_order_admin_comment:
   category: commerce_order
   label: 'Admin comment'
   template: '<p><strong>Admin comment:</strong><br /> {{ comment }}</p>'
+commerce_order_from_customer_comment:
+  category: commerce_order
+  label: 'Comment from customer'
+  template: '<p><strong>From customer:</strong><br /> {{ comment|raw }}</p>'
 payment_added:
   category: commerce_payment
   label: 'Payment added'
diff --git a/modules/log/src/EventSubscriber/CheckoutEventSubscriber.php b/modules/log/src/EventSubscriber/CheckoutEventSubscriber.php
index 2c499ba50b44caa3af0a8e7e2c4728ebd4e8426d..c359fea64e8d43ea570400c939dafb77b89098fd 100644
--- a/modules/log/src/EventSubscriber/CheckoutEventSubscriber.php
+++ b/modules/log/src/EventSubscriber/CheckoutEventSubscriber.php
@@ -44,6 +44,11 @@ class CheckoutEventSubscriber implements EventSubscriberInterface {
   public function onCheckoutCompletion(OrderEvent $event) {
     $order = $event->getOrder();
     $this->logStorage->generate($order, 'checkout_complete')->save();
+    if ($comments = $order->getCustomerComments()) {
+      $this->logStorage->generate($order, 'commerce_order_from_customer_comment', [
+        'comment' => $comments,
+      ])->save();
+    }
   }
 
 }
diff --git a/modules/order/commerce_order.install b/modules/order/commerce_order.install
index 8b720660d40fcc65a18c97da2cf9c016d16eb5ce..69a330d77ac17514b69da342a935bb867001b8aa 100644
--- a/modules/order/commerce_order.install
+++ b/modules/order/commerce_order.install
@@ -419,5 +419,22 @@ function commerce_order_update_8221() {
   }
 
   return "The views.view.commerce_user_orders couldn't be updated as the default path for the title field has been overridden with custom value. Test if rewrite pattern is correct.";
+}
+
+/**
+ * Add the "customer_comments" field to orders.
+ */
+function commerce_order_update_8222() {
+  $storage_definition = BaseFieldDefinition::create('string_long')
+    ->setLabel(t('Customer comments'))
+    ->setDisplayOptions('view', [
+      'type' => 'string',
+      'label' => 'above',
+      'settings' => [],
+    ])
+    ->setDisplayConfigurable('form', TRUE)
+    ->setDisplayConfigurable('view', TRUE);
 
+  $update_manager = \Drupal::entityDefinitionUpdateManager();
+  $update_manager->installFieldStorageDefinition('customer_comments', 'commerce_order', 'commerce_order', $storage_definition);
 }
diff --git a/modules/order/src/Entity/Order.php b/modules/order/src/Entity/Order.php
index 3866ea5b7a4826e87a34bb05268de2c9c6ede969..9abdf766deda33cc24ae9e8808ee0b43028b0c02 100644
--- a/modules/order/src/Entity/Order.php
+++ b/modules/order/src/Entity/Order.php
@@ -694,6 +694,21 @@ class Order extends CommerceContentEntityBase implements OrderInterface {
     return $date;
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function getCustomerComments(): ?string {
+    return $this->get('customer_comments')->value;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setCustomerComments($comments): static {
+    $this->set('customer_comments', $comments);
+    return $this;
+  }
+
   /**
    * {@inheritdoc}
    */
@@ -995,6 +1010,16 @@ class Order extends CommerceContentEntityBase implements OrderInterface {
       ])
       ->setDisplayConfigurable('view', TRUE);
 
+    $fields['customer_comments'] = BaseFieldDefinition::create('string_long')
+      ->setLabel(t('Customer comments'))
+      ->setDisplayOptions('view', [
+        'type' => 'string',
+        'label' => 'above',
+        'settings' => [],
+      ])
+      ->setDisplayConfigurable('form', TRUE)
+      ->setDisplayConfigurable('view', TRUE);
+
     return $fields;
   }
 
diff --git a/modules/order/src/Entity/OrderInterface.php b/modules/order/src/Entity/OrderInterface.php
index 72e50f830227c71de898452d0e0ae999dd958b75..e19cc4d5a8baad62a11de3f31179a86ad062863f 100644
--- a/modules/order/src/Entity/OrderInterface.php
+++ b/modules/order/src/Entity/OrderInterface.php
@@ -468,4 +468,22 @@ interface OrderInterface extends ContentEntityInterface, EntityAdjustableInterfa
    */
   public function getCalculationDate();
 
+  /**
+   * Gets the customer comments.
+   *
+   * @return string|null
+   *   The customer comments.
+   */
+  public function getCustomerComments(): ?string;
+
+  /**
+   * Sets the customer comments.
+   *
+   * @param string $comments
+   *   The customer comments.
+   *
+   * @return $this
+   */
+  public function setCustomerComments(string $comments): static;
+
 }
diff --git a/modules/order/templates/commerce-order-receipt.html.twig b/modules/order/templates/commerce-order-receipt.html.twig
index a77c744cecb489bcb1651d85d52bbad4fcf51e20..7de7d77f5edab5036d81dae635fca11eb8d81e26 100644
--- a/modules/order/templates/commerce-order-receipt.html.twig
+++ b/modules/order/templates/commerce-order-receipt.html.twig
@@ -136,6 +136,22 @@
             </p>
           </td>
         </tr>
+        {% if order_entity.getCustomerComments %}
+          <tr>
+            <td>
+              <table style="width: 100%; padding-top:15px; padding-bottom: 15px; text-align: left; border-top: 1px solid #cccccc; ">
+                <tbody>
+                <tr>
+                  <td style="padding-top: 5px; font-weight: bold;">{{ 'Customer comments'|t }}</td>
+                </tr>
+                <tr>
+                  <td><p>{{ order_entity.getCustomerComments|raw }}</p></td>
+                </tr>
+                </tbody>
+              </table>
+            </td>
+          </tr>
+        {% endif %}
         <tr>
           <td>
             {% block additional_information %}