diff --git a/modules/payment/config/schema/commerce_payment.schema.yml b/modules/payment/config/schema/commerce_payment.schema.yml
index 56cf9627..a62a6285 100644
--- a/modules/payment/config/schema/commerce_payment.schema.yml
+++ b/modules/payment/config/schema/commerce_payment.schema.yml
@@ -51,6 +51,13 @@ commerce_payment_gateway_configuration:
       sequence:
         type: string
 
+commerce_checkout.commerce_checkout_pane.payment_information:
+  type: commerce_checkout_pane_configuration
+  mapping:
+    free_orders:
+      type: string
+      label: 'Billing information required for free orders'
+
 commerce_checkout.commerce_checkout_pane.payment_process:
   type: commerce_checkout_pane_configuration
   mapping:
diff --git a/modules/payment/src/Plugin/Commerce/CheckoutPane/PaymentInformation.php b/modules/payment/src/Plugin/Commerce/CheckoutPane/PaymentInformation.php
index 6cce21e0..73c61e5c 100644
--- a/modules/payment/src/Plugin/Commerce/CheckoutPane/PaymentInformation.php
+++ b/modules/payment/src/Plugin/Commerce/CheckoutPane/PaymentInformation.php
@@ -20,6 +20,83 @@ use Drupal\Core\Form\FormStateInterface;
  */
 class PaymentInformation extends CheckoutPaneBase {
 
+  /**
+   * Collect billing and payment for free orders (ie: free trial, later payment.)
+   */
+  const COLLECT_ALL = 'collect_all';
+
+  /**
+   * Collect billing only for free orders (ie: collect customer information.)
+   */
+  const COLLECT_BILLING = 'collect_billing';
+
+  /**
+   * Collect no information for free orders.
+   */
+  const COLLECT_NONE = 'collect_none';
+
+  /**
+   * {@inheritdoc}
+   */
+  public function defaultConfiguration() {
+    return [
+      'free_orders' => self::COLLECT_NONE,
+    ] + parent::defaultConfiguration();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildConfigurationSummary() {
+    switch ($this->configuration['free_orders']) {
+      case self::COLLECT_ALL:
+        $summary = $this->t('Billing information required for free orders: Billing and Payment');
+        break;
+
+      case self::COLLECT_BILLING:
+        $summary = $this->t('Billing information required for free orders: Billing Only');
+        break;
+
+      case self::COLLECT_NONE:
+      default:
+        $summary = $this->t('Billing information required for free orders: None');
+    }
+
+    return $summary;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
+    $form = parent::buildConfigurationForm($form, $form_state);
+    $form['free_orders'] = [
+      '#type' => 'select',
+      '#title' => $this->t('Billing information required for free orders'),
+      '#description' => $this->t('Determines what information will be collected for free orders.'),
+      '#default_value' => $this->configuration['free_orders'],
+      '#options' => [
+        self::COLLECT_NONE => $this->t('None'),
+        self::COLLECT_BILLING => $this->t('Billing Only'),
+        self::COLLECT_ALL => $this->t('Payment and Billing'),
+      ],
+    ];
+
+    return $form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {
+    parent::submitConfigurationForm($form, $form_state);
+
+    if (!$form_state->getErrors()) {
+      $values = $form_state->getValue($form['#parents']);
+      $this->configuration['free_orders'] = $values['free_orders'];
+    }
+  }
+
   /**
    * {@inheritdoc}
    */
@@ -28,6 +105,17 @@ class PaymentInformation extends CheckoutPaneBase {
     /** @var \Drupal\commerce_payment\Entity\PaymentGatewayInterface $payment_gateway */
     $payment_gateway = $this->order->get('payment_gateway')->entity;
     if (!$payment_gateway) {
+      $billing_profile = $this->order->getBillingProfile();
+      if ($billing_profile) {
+        $profile_view_builder = $this->entityTypeManager->getViewBuilder('profile');
+        $profile_view = $profile_view_builder->view($billing_profile, 'default');
+        $summary = [
+          'label' => [
+            '#markup' => $this->t('Billing information'),
+          ],
+          'profile' => $profile_view,
+        ];
+      }
       return $summary;
     }
 
@@ -55,6 +143,16 @@ class PaymentInformation extends CheckoutPaneBase {
     return $summary;
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function isVisible() {
+    if (!$this->order->getTotalPrice()->isZero()) {
+      return TRUE;
+    }
+    return $this->configuration['free_orders'] != self::COLLECT_NONE;
+  }
+
   /**
    * {@inheritdoc}
    */
@@ -70,6 +168,7 @@ class PaymentInformation extends CheckoutPaneBase {
       return [];
     }
 
+    $collect_billing_only = $this->order->getTotalPrice()->isZero() && $this->configuration['free_orders'] == self::COLLECT_BILLING;
     $options = $this->buildPaymentMethodOptions($payment_gateways);
     $user_input = $form_state->getUserInput();
     $values = NestedArray::getValue($user_input, $pane_form['#parents']);
@@ -103,7 +202,7 @@ class PaymentInformation extends CheckoutPaneBase {
         'callback' => [get_class($this), 'ajaxRefresh'],
         'wrapper' => $pane_form['#wrapper_id'],
       ],
-      '#access' => count($options) > 1,
+      '#access' => count($options) > 1 && !$collect_billing_only,
     ];
     // Store the values for submitPaneForm().
     foreach ($options as $option_id => $option) {
@@ -118,7 +217,7 @@ class PaymentInformation extends CheckoutPaneBase {
 
     $selected_option = $pane_form['payment_method'][$default_option];
     $payment_gateway = $payment_gateways[$selected_option['#payment_gateway']];
-    if ($payment_gateway->getPlugin() instanceof SupportsStoredPaymentMethodsInterface) {
+    if (!$collect_billing_only && $payment_gateway->getPlugin() instanceof SupportsStoredPaymentMethodsInterface) {
       if (!empty($selected_option['#payment_method_type'])) {
         /** @var \Drupal\commerce_payment\PaymentMethodStorageInterface $payment_method_storage */
         $payment_method_storage = $this->entityTypeManager->getStorage('commerce_payment_method');
@@ -340,6 +439,13 @@ class PaymentInformation extends CheckoutPaneBase {
    */
   public function submitPaneForm(array &$pane_form, FormStateInterface $form_state, array &$complete_form) {
     $values = $form_state->getValue($pane_form['#parents']);
+
+    // Adding billing profile only when order total is zero and the pane config is setup to collect it.
+    if ($this->order->getTotalPrice()->isZero() && $this->configuration['free_orders'] == self::COLLECT_BILLING) {
+      $this->order->setBillingProfile($pane_form['billing_information']['#profile']);
+      return;
+    }
+
     $selected_option = $pane_form['payment_method'][$values['payment_method']];
     /** @var \Drupal\commerce_payment\PaymentGatewayStorageInterface $payment_gateway_storage */
     $payment_gateway_storage = $this->entityTypeManager->getStorage('commerce_payment_gateway');
diff --git a/modules/payment/src/Plugin/Commerce/CheckoutPane/PaymentProcess.php b/modules/payment/src/Plugin/Commerce/CheckoutPane/PaymentProcess.php
index 5784b576..b088b918 100644
--- a/modules/payment/src/Plugin/Commerce/CheckoutPane/PaymentProcess.php
+++ b/modules/payment/src/Plugin/Commerce/CheckoutPane/PaymentProcess.php
@@ -85,7 +85,9 @@ class PaymentProcess extends CheckoutPaneBase {
   public function isVisible() {
     // This pane can't be used without the PaymentInformation pane.
     $payment_info_pane = $this->checkoutFlow->getPane('payment_information');
-    return $payment_info_pane->isVisible() && $payment_info_pane->getStepId() != '_disabled';
+    // Disable if order total is 0.
+    $order_total = $this->order->getTotalPrice();
+    return $payment_info_pane->isVisible() && $payment_info_pane->getStepId() != '_disabled' && !$order_total->isZero();
   }
 
   /**
diff --git a/modules/payment/tests/src/FunctionalJavascript/PaymentCheckoutTest.php b/modules/payment/tests/src/FunctionalJavascript/PaymentCheckoutTest.php
index 31221e0d..baaf8014 100644
--- a/modules/payment/tests/src/FunctionalJavascript/PaymentCheckoutTest.php
+++ b/modules/payment/tests/src/FunctionalJavascript/PaymentCheckoutTest.php
@@ -3,9 +3,11 @@
 namespace Drupal\Tests\commerce_payment\FunctionalJavascript;
 
 use Drupal\commerce_checkout\Entity\CheckoutFlow;
+use Drupal\commerce_order\Adjustment;
 use Drupal\commerce_order\Entity\Order;
 use Drupal\commerce_payment\Entity\Payment;
 use Drupal\commerce_payment\Entity\PaymentGateway;
+use Drupal\commerce_payment\Plugin\Commerce\CheckoutPane\PaymentInformation;
 use Drupal\Tests\commerce\Functional\CommerceBrowserTestBase;
 use Drupal\Tests\commerce\FunctionalJavascript\JavascriptTestTrait;
 
@@ -527,4 +529,123 @@ class PaymentCheckoutTest extends CommerceBrowserTestBase {
     $this->assertEquals($payment->getAmount(), $order->getTotalPrice());
   }
 
+  /**
+   * Tests a free order with no billing or payment collection.
+   */
+  public function testFreeOrderCollectNone() {
+    $this->drupalGet($this->product->toUrl()->toString());
+    $this->submitForm([], 'Add to cart');
+
+    // Add an adjustment to zero out the order total.
+    $order = Order::load(1);
+    $order->addAdjustment(new Adjustment([
+      'type' => 'custom',
+      'label' => 'Surprise, it is free!',
+      'amount' => $order->getTotalPrice()->multiply('-1'),
+      'locked' => TRUE,
+    ]));
+    $order->save();
+
+    $this->drupalGet('checkout/1');
+    // @todo this is weird if a free order, "Pay" but no payment.
+    $this->submitForm([], 'Pay and complete purchase');
+    $this->assertSession()->pageTextContains('Your order number is 1. You can view your order on your account page when logged in.');
+  }
+
+  /**
+   * Tests a free order with billing and no payment collection.
+   */
+  public function testFreeOrderCollectBilling() {
+    /** @var \Drupal\commerce_checkout\Entity\CheckoutFlow $checkout_flow */
+    $checkout_flow = CheckoutFlow::load('default');
+    $plugin = $checkout_flow->getPlugin();
+    $configuration = $plugin->getConfiguration();
+    $configuration['panes']['payment_information']['free_orders'] = PaymentInformation::COLLECT_BILLING;
+    $plugin->setConfiguration($configuration);
+    $checkout_flow->save();
+
+    $this->drupalGet($this->product->toUrl()->toString());
+    $this->submitForm([], 'Add to cart');
+
+    // Add an adjustment to zero out the order total.
+    $order = Order::load(1);
+    $order->addAdjustment(new Adjustment([
+      'type' => 'custom',
+      'label' => 'Surprise, it is free!',
+      'amount' => $order->getTotalPrice()->multiply('-1'),
+      'locked' => TRUE,
+    ]));
+    $order->save();
+
+    $this->drupalGet('checkout/1');
+    $this->submitForm([
+      'payment_information[billing_information][address][0][address][given_name]' => 'Johnny',
+      'payment_information[billing_information][address][0][address][family_name]' => 'Appleseed',
+      'payment_information[billing_information][address][0][address][address_line1]' => '123 New York Drive',
+      'payment_information[billing_information][address][0][address][locality]' => 'New York City',
+      'payment_information[billing_information][address][0][address][administrative_area]' => 'NY',
+      'payment_information[billing_information][address][0][address][postal_code]' => '10001',
+    ], 'Continue to review');
+    // @todo failing, should show some sort of review.
+    $this->assertSession()->pageTextContains('Billing information');
+    $this->assertSession()->pageTextContains('Example');
+    $this->assertSession()->pageTextContains('Johnny Appleseed');
+    $this->assertSession()->pageTextContains('123 New York Drive');
+    // @todo this is weird if a free order, "Pay" but no payment.
+    $this->submitForm([], 'Pay and complete purchase');
+    $this->assertSession()->pageTextContains('Your order number is 1. You can view your order on your account page when logged in.');
+  }
+
+  /**
+   * Tests a free order with billing and payment collection.
+   */
+  public function testFreeOrderCollectAll() {
+    /** @var \Drupal\commerce_checkout\Entity\CheckoutFlow $checkout_flow */
+    $checkout_flow = CheckoutFlow::load('default');
+    $plugin = $checkout_flow->getPlugin();
+    $configuration = $plugin->getConfiguration();
+    $configuration['panes']['payment_information']['free_orders'] = PaymentInformation::COLLECT_ALL;
+    $plugin->setConfiguration($configuration);
+    $checkout_flow->save();
+
+    $this->drupalGet($this->product->toUrl()->toString());
+    $this->submitForm([], 'Add to cart');
+
+    // Add an adjustment to zero out the order total.
+    $order = Order::load(1);
+    $order->addAdjustment(new Adjustment([
+      'type' => 'custom',
+      'label' => 'Surprise, it is free!',
+      'amount' => $order->getTotalPrice()->multiply('-1'),
+      'locked' => TRUE,
+    ]));
+    $order->save();
+
+    $this->drupalGet('checkout/1');
+    $radio_button = $this->getSession()->getPage()->findField('New credit card');
+    $radio_button->click();
+    $this->waitForAjaxToFinish();
+
+    $this->submitForm([
+      'payment_information[add_payment_method][payment_details][number]' => '4012888888881881',
+      'payment_information[add_payment_method][payment_details][expiration][month]' => '02',
+      'payment_information[add_payment_method][payment_details][expiration][year]' => '2020',
+      'payment_information[add_payment_method][payment_details][security_code]' => '123',
+      'payment_information[add_payment_method][billing_information][address][0][address][given_name]' => 'Johnny',
+      'payment_information[add_payment_method][billing_information][address][0][address][family_name]' => 'Appleseed',
+      'payment_information[add_payment_method][billing_information][address][0][address][address_line1]' => '123 New York Drive',
+      'payment_information[add_payment_method][billing_information][address][0][address][locality]' => 'New York City',
+      'payment_information[add_payment_method][billing_information][address][0][address][administrative_area]' => 'NY',
+      'payment_information[add_payment_method][billing_information][address][0][address][postal_code]' => '10001',
+    ], 'Continue to review');
+    $this->assertSession()->pageTextContains('Payment information');
+    $this->assertSession()->pageTextContains('Visa ending in 1881');
+    $this->assertSession()->pageTextContains('Expires 2/2020');
+    $this->assertSession()->pageTextContains('Johnny Appleseed');
+    $this->assertSession()->pageTextContains('123 New York Drive');
+    // @todo this is weird if a free order, "Pay" but no payment.
+    $this->submitForm([], 'Pay and complete purchase');
+    $this->assertSession()->pageTextContains('Your order number is 1. You can view your order on your account page when logged in.');
+  }
+
 }
