Is it possible to collect credit card info and address info on separate pages in Commerce 2?
Creating a checkout flow plugin may solve your problem. Create a Plugin in your custom module like an example shown below:



namespace Drupal\my_checkout_flow\Plugin\Commerce\CheckoutFlow;

use Drupal\commerce_checkout\Plugin\Commerce\CheckoutFlow\CheckoutFlowWithPanesBase;
use Drupal\Core\Form\FormStateInterface;

/**
 * @CommerceCheckoutFlow(
 *  id = "custom_checkout_flow",
 *  label = @Translation("Custom checkout flow"),
 * )
 */
class CustomCheckoutFlow extends CheckoutFlowWithPanesBase {

  /**
   * {@inheritdoc}
   */
  public function getSteps() {
    return [
      'login' =&gt; [
        'label' =&gt; $this-&gt;t('Login'),
        'previous_label' =&gt; $this-&gt;t('Return to login'),
        'has_order_summary' =&gt; FALSE,
      ],
      'card_info' =&gt; [
        'label' =&gt; $this-&gt;t('Card information'),
        'previous_label' =&gt; $this-&gt;t('Return to Card information'),
        'next_label' =&gt; $this-&gt;t('Continue to Card information'),
        'has_order_summary' =&gt; FALSE,
      ],
      'address_information' =&gt; [
        'label' =&gt; $this-&gt;t('Address information'),
        'previous_label' =&gt; $this-&gt;t('Return to Address information'),
        'next_label' =&gt; $this-&gt;t('Continue to Address information'),
        'has_order_summary' =&gt; FALSE,
      ],
      'review' =&gt; [
        'label' =&gt; $this-&gt;t('Review'),
        'has_order_summary' =&gt; TRUE,
      ],
    ] + parent::getSteps();
  }

}


After creating a plugin, you need to change the checkout flow settings for your order type.

For more information visit: https://docs.drupalcommerce.org/commerce2/developer-guide/checkout/create-custom-checkout-flow



Update

CustomCheckoutFlow

&lt;?php

namespace Drupal\my_checkout_flow\Plugin\Commerce\CheckoutFlow;

use Drupal\commerce_checkout\Plugin\Commerce\CheckoutFlow\CheckoutFlowWithPanesBase;

/**
 * @CommerceCheckoutFlow(
 *  id = "custom_checkout_flow",
 *  label = @Translation("Custom checkout flow"),
 * )
 */
class CustomCheckoutFlow extends CheckoutFlowWithPanesBase {

  /**
   * {@inheritdoc}
   */
  public function getSteps() {
    return [
      'login' =&gt; [
        'label' =&gt; $this-&gt;t('Login'),
        'previous_label' =&gt; $this-&gt;t('Go back'),
        'has_sidebar' =&gt; FALSE,
      ],
      'card_information' =&gt; [
        'label' =&gt; $this-&gt;t('Card information'),
        'previous_label' =&gt; $this-&gt;t('Go back'),
        'has_sidebar' =&gt; TRUE,
      ],
      'order_information' =&gt; [
        'label' =&gt; $this-&gt;t('Order information'),
        'has_sidebar' =&gt; TRUE,
        'next_label' =&gt; $this-&gt;t('Continue to Order information'),
        'previous_label' =&gt; $this-&gt;t('Go back'),
      ],
      'review' =&gt; [
        'label' =&gt; $this-&gt;t('Review'),
        'next_label' =&gt; $this-&gt;t('Continue to review'),
        'previous_label' =&gt; $this-&gt;t('Go back'),
        'has_sidebar' =&gt; TRUE,
      ],
    ] + parent::getSteps();
  }

}


Then create CheckoutPanes as shown below:

BillingInformation

&lt;?php

namespace Drupal\my_checkout_flow\Plugin\Commerce\CheckoutPane;

use \Drupal\commerce_checkout\Plugin\Commerce\CheckoutPane\BillingInformation;

/**
 * Provides the billing information pane.
 *
 * @CommerceCheckoutPane(
 *   id = "custom_billing_information",
 *   label = @Translation("Billing information"),
 *   default_step = "order_information",
 *   wrapper_element = "fieldset",
 * )
 */
class CustomBillingInformation extends BillingInformation {

}


PaymentInformation

&lt;?php
namespace Drupal\my_checkout_flow\Plugin\Commerce\CheckoutPane;

use Drupal\commerce_payment\Plugin\Commerce\CheckoutPane\PaymentInformation as PaymentInformationBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Render\Element;

/**
 * Provides the payment information pane.
 *
 * @CommerceCheckoutPane(
 *   id = "custom_commerce_payment_information",
 *   label = @Translation("Custom Payment information"),
 *   display_label = @Translation("Payment information"),
 *   default_step = "order_information",
 *   wrapper_element = "fieldset",
 * )
 */
class PaymentInformation extends PaymentInformationBase {

  /**
   * {@inheritdoc}
   */
  public function buildPaneForm(array $pane_form, FormStateInterface $form_state, array &amp;$complete_form) {
    $pane_form = parent::buildPaneForm($pane_form, $form_state, $complete_form);

    // Add an after build callback in order to make modifications on the address form.
    $pane_form['#after_build'][] = [$this, 'paneFormAfterBuild'];

    return $pane_form;
  }

  /**
   * After build callback for the pane form.
   */
  public function paneFormAfterBuild(array $pane_form, FormStateInterface $form_state) {
    // Get billing form element. Where it is located depends on the payment method that is chosen.
    if (isset($pane_form['add_payment_method']['billing_information'])) {
      $billing_form = &amp;$pane_form['add_payment_method']['billing_information'];
    }
    elseif (isset($pane_form['billing_information'])) {
      $billing_form = &amp;$pane_form['billing_information'];
    }
    else {
      // No billing information found.
      return $pane_form;
    }

    // Get the address form element.
    $address_form = &amp;$billing_form['address']['widget']['0']['address'];

    // Add element validation callback to autofill the address.
    $billing_form['#element_validate'] = array_merge(
      [[$this, 'profileSelectValidate']],
      \Drupal::service('element_info')-&gt;getInfoProperty('commerce_profile_select', '#element_validate', [])
    );

    // Set all address fields to non-required.
    foreach (Element::children($address_form) as $key) {
      $address_form[$key]['#required'] = FALSE;
    }

    // Hide the address form.
    $address_form['#access'] = FALSE;

    return $pane_form;
  }

  /**
   * Element validation callback for the profile select element.
   */
  public function profileSelectValidate(array &amp;$element, FormStateInterface $form_state) {
    $billing_profile = $this-&gt;order-&gt;getBillingProfile()-&gt;get('address')-&gt;getValue();
    $form_state-&gt;setValue($element['address']['widget'][0]['address']['#parents'], $billing_profile[0]);
  }

}


PaymentProcess

&lt;?php

namespace Drupal\my_checkout_flow\Plugin\Commerce\CheckoutPane;

use Drupal\commerce_payment\Plugin\Commerce\CheckoutPane\PaymentProcess as BasePaymentProcess;

/**
 * Provides the payment process pane.
 *
 * @CommerceCheckoutPane(
 *   id = "custom_payment_process",
 *   label = @Translation("Custom Payment process"),
 *   default_step = "payment",
 *   wrapper_element = "container",
 * )
 */
class PaymentProcess extends BasePaymentProcess {

  /**
   * {@inheritdoc}
   */
  public function isVisible() {
    // This pane can't be used without the PaymentInformation pane.
    $payment_info_pane = $this-&gt;checkoutFlow-&gt;getPane('custom_commerce_payment_information');
    return $payment_info_pane-&gt;isVisible() &amp;&amp; $payment_info_pane-&gt;getStepId() != '_disabled';
  }

}


After this set checkout panes for your custom checkout flow as shown in the image below:



Find full code at https://github.com/patilvishalvs/my_checkout_flow