diff --git a/ee/app/assets/javascripts/subscriptions/buy_minutes/components/app.vue b/ee/app/assets/javascripts/subscriptions/buy_minutes/components/app.vue
index 956879ab1ffa66309a7e021fbcd3f5c68c08e77b..7848179cf59f85c6728a988c5c0bfcabbfff6483 100644
--- a/ee/app/assets/javascripts/subscriptions/buy_minutes/components/app.vue
+++ b/ee/app/assets/javascripts/subscriptions/buy_minutes/components/app.vue
@@ -7,11 +7,13 @@ import { ERROR_FETCHING_DATA_HEADER, ERROR_FETCHING_DATA_DESCRIPTION } from '~/e
 import plansQuery from '../../graphql/queries/plans.customer.query.graphql';
 import { planTags, CUSTOMER_CLIENT } from '../constants';
 import Checkout from './checkout.vue';
+import OrderSummary from './order_summary.vue';
 
 export default {
   components: {
     Checkout,
     GlEmptyState,
+    OrderSummary,
     StepOrderApp,
   },
   i18n: {
@@ -59,6 +61,8 @@ export default {
     <template #checkout>
       <checkout :plans="plans" />
     </template>
-    <template #order-summary></template>
+    <template #order-summary>
+      <order-summary :plans="plans" />
+    </template>
   </step-order-app>
 </template>
diff --git a/ee/app/assets/javascripts/subscriptions/buy_minutes/components/checkout.vue b/ee/app/assets/javascripts/subscriptions/buy_minutes/components/checkout.vue
index a8d215181512e3fddb1a2b49905ae7b329555742..a1d54d62757570ca148d6001e917d5679c57dbe4 100644
--- a/ee/app/assets/javascripts/subscriptions/buy_minutes/components/checkout.vue
+++ b/ee/app/assets/javascripts/subscriptions/buy_minutes/components/checkout.vue
@@ -33,12 +33,12 @@ export default {
 <template>
   <div
     v-if="!$apollo.loading"
-    class="checkout gl-flex gl-flex-column gl-justify-content-between w-100"
+    class="checkout gl-display-flex gl-flex-direction-column gl-justify-content-between w-100"
   >
     <div class="full-width">
       <progress-bar v-if="isNewUser" :steps="$options.steps" :current-step="$options.currentStep" />
       <div class="flash-container"></div>
-      <h2 class="gl-mt-4 gl-mb-3 gl-mb-lg-5">{{ $options.i18n.checkout }}</h2>
+      <h2 class="gl-mt-6 gl-mb-7 gl-mb-lg-5">{{ $options.i18n.checkout }}</h2>
       <subscription-details :plans="plans" />
     </div>
   </div>
diff --git a/ee/app/assets/javascripts/subscriptions/buy_minutes/components/order_summary.vue b/ee/app/assets/javascripts/subscriptions/buy_minutes/components/order_summary.vue
new file mode 100644
index 0000000000000000000000000000000000000000..1e31d322e8cf03c6a68ed7cba5b842ff0f6384bb
--- /dev/null
+++ b/ee/app/assets/javascripts/subscriptions/buy_minutes/components/order_summary.vue
@@ -0,0 +1,153 @@
+<script>
+import { GlIcon } from '@gitlab/ui';
+import STATE_QUERY from 'ee/subscriptions/graphql/queries/state.query.graphql';
+import { TAX_RATE, NEW_GROUP } from 'ee/subscriptions/new/constants';
+import formattingMixins from 'ee/subscriptions/new/formatting_mixins';
+import { sprintf, s__ } from '~/locale';
+import SummaryDetails from './order_summary/summary_details.vue';
+
+export default {
+  components: {
+    SummaryDetails,
+    GlIcon,
+  },
+  mixins: [formattingMixins],
+  props: {
+    plans: {
+      type: Array,
+      required: true,
+    },
+  },
+  apollo: {
+    state: {
+      query: STATE_QUERY,
+      update(data) {
+        return data;
+      },
+      result({ data }) {
+        this.subscription = data.subscription;
+        this.namespaces = data.namespaces;
+        this.isSetupForCompany = data.isSetupForCompany;
+        this.fullName = data.fullName;
+        this.customer = data.customer;
+      },
+    },
+  },
+  data() {
+    return {
+      subscription: {},
+      namespaces: [],
+      isSetupForCompany: false,
+      collapsed: true,
+      fullName: null,
+      customer: {},
+    };
+  },
+  computed: {
+    selectedPlan() {
+      return this.plans.find((plan) => plan.code === this.subscription.planId);
+    },
+    selectedPlanPrice() {
+      return this.selectedPlan.pricePerYear;
+    },
+    selectedGroup() {
+      return this.namespaces.find((group) => group.id === this.subscription.namespaceId);
+    },
+    totalExVat() {
+      return this.subscription.quantity * this.selectedPlanPrice;
+    },
+    vat() {
+      return TAX_RATE * this.totalExVat;
+    },
+    totalAmount() {
+      return this.totalExVat + this.vat;
+    },
+    usersPresent() {
+      return this.subscription.quantity > 0;
+    },
+    isGroupSelected() {
+      return this.subscription.namespaceId && this.subscription.namespaceId !== NEW_GROUP;
+    },
+    isSelectedGroupPresent() {
+      return (
+        this.isGroupSelected &&
+        this.namespaces.some((namespace) => namespace.id === this.subscription.namespaceId)
+      );
+    },
+    name() {
+      if (this.isSetupForCompany && this.customer.company) {
+        return this.customer.company;
+      }
+
+      if (this.isGroupSelected && this.isSelectedGroupPresent) {
+        return this.selectedGroup.name;
+      }
+
+      if (this.isSetupForCompany) {
+        return s__('Checkout|Your organization');
+      }
+
+      return this.fullName;
+    },
+    titleWithName() {
+      return sprintf(this.$options.i18n.title, { name: this.name });
+    },
+  },
+  methods: {
+    toggleCollapse() {
+      this.collapsed = !this.collapsed;
+    },
+  },
+  i18n: {
+    title: s__("Checkout|%{name}'s GitLab subscription"),
+  },
+  taxRate: TAX_RATE,
+};
+</script>
+<template>
+  <div
+    v-if="!$apollo.loading && (!isGroupSelected || isSelectedGroupPresent)"
+    class="order-summary gl-display-flex gl-flex-direction-column gl-flex-grow-1 gl-mt-2 mt-lg-5"
+  >
+    <div class="d-lg-none">
+      <div @click="toggleCollapse">
+        <h4 class="d-flex justify-content-between gl-font-lg" :class="{ 'gl-mb-7': !collapsed }">
+          <div class="d-flex">
+            <gl-icon v-if="collapsed" name="chevron-right" :size="18" use-deprecated-sizes />
+            <gl-icon v-else name="chevron-down" :size="18" use-deprecated-sizes />
+            <div>{{ titleWithName }}</div>
+          </div>
+          <div class="gl-ml-3">{{ formatAmount(totalAmount, usersPresent) }}</div>
+        </h4>
+      </div>
+      <summary-details
+        v-show="!collapsed"
+        :vat="vat"
+        :total-ex-vat="totalExVat"
+        :users-present="usersPresent"
+        :selected-plan-text="selectedPlan.name"
+        :selected-plan-price="selectedPlanPrice"
+        :total-amount="totalAmount"
+        :number-of-users="subscription.quantity"
+        :tax-rate="$options.taxRate"
+      />
+    </div>
+    <div class="d-none d-lg-block">
+      <div class="append-bottom-20">
+        <h4>
+          {{ titleWithName }}
+        </h4>
+      </div>
+      <summary-details
+        :vat="vat"
+        :total-ex-vat="totalExVat"
+        :users-present="usersPresent"
+        :selected-plan-text="selectedPlan.name"
+        :selected-plan-price="selectedPlanPrice"
+        :total-amount="totalAmount"
+        :number-of-users="subscription.quantity"
+        :tax-rate="$options.taxRate"
+      />
+    </div>
+  </div>
+</template>
diff --git a/ee/app/assets/javascripts/subscriptions/buy_minutes/components/order_summary/summary_details.vue b/ee/app/assets/javascripts/subscriptions/buy_minutes/components/order_summary/summary_details.vue
new file mode 100644
index 0000000000000000000000000000000000000000..860ae9e83ac97d150d465310a0af6d19810747eb
--- /dev/null
+++ b/ee/app/assets/javascripts/subscriptions/buy_minutes/components/order_summary/summary_details.vue
@@ -0,0 +1,104 @@
+<script>
+import formattingMixins from 'ee/subscriptions/new/formatting_mixins';
+import { s__ } from '~/locale';
+
+export default {
+  mixins: [formattingMixins],
+  props: {
+    vat: {
+      type: Number,
+      required: true,
+    },
+    totalExVat: {
+      type: Number,
+      required: true,
+    },
+    usersPresent: {
+      type: Boolean,
+      required: true,
+    },
+    selectedPlanText: {
+      type: String,
+      required: true,
+    },
+    selectedPlanPrice: {
+      type: Number,
+      required: true,
+    },
+    totalAmount: {
+      type: Number,
+      required: true,
+    },
+    numberOfUsers: {
+      type: Number,
+      required: true,
+    },
+    taxRate: {
+      type: Number,
+      required: false,
+      default: null,
+    },
+  },
+  computed: {
+    startDate() {
+      return new Date(Date.now());
+    },
+    endDate() {
+      return new Date(this.startDate).setFullYear(this.startDate.getFullYear() + 1);
+    },
+  },
+  i18n: {
+    selectedPlanText: s__('Checkout|%{selectedPlanText} plan'),
+    numberOfUsers: s__('Checkout|(x%{numberOfUsers})'),
+    pricePerUserPerYear: s__('Checkout|$%{selectedPlanPrice} per user per year'),
+    dates: s__('Checkout|%{startDate} - %{endDate}'),
+    subtotal: s__('Checkout|Subtotal'),
+    tax: s__('Checkout|Tax'),
+    total: s__('Checkout|Total'),
+  },
+};
+</script>
+<template>
+  <div>
+    <div class="d-flex justify-content-between bold gl-mt-3 gl-mb-3">
+      <div class="js-selected-plan">
+        {{ sprintf($options.i18n.selectedPlanText, { selectedPlanText }) }}
+        <span v-if="usersPresent" class="js-number-of-users">{{
+          sprintf($options.i18n.numberOfUsers, { numberOfUsers })
+        }}</span>
+      </div>
+      <div class="js-amount">{{ formatAmount(totalExVat, usersPresent) }}</div>
+    </div>
+    <div class="text-secondary js-per-user">
+      {{
+        sprintf($options.i18n.pricePerUserPerYear, {
+          selectedPlanPrice: selectedPlanPrice.toLocaleString(),
+        })
+      }}
+    </div>
+    <div class="text-secondary js-dates">
+      {{
+        sprintf($options.i18n.dates, {
+          startDate: formatDate(startDate),
+          endDate: formatDate(endDate),
+        })
+      }}
+    </div>
+    <div v-if="taxRate">
+      <div class="border-bottom gl-mt-3 gl-mb-3"></div>
+      <div class="d-flex justify-content-between text-secondary">
+        <div>{{ $options.i18n.subtotal }}</div>
+        <div class="js-total-ex-vat">{{ formatAmount(totalExVat, usersPresent) }}</div>
+      </div>
+      <div class="d-flex justify-content-between text-secondary">
+        <div>{{ $options.i18n.tax }}</div>
+        <div class="js-vat">{{ formatAmount(vat, usersPresent) }}</div>
+      </div>
+    </div>
+    <div class="border-bottom gl-mt-3 gl-mb-3"></div>
+    <div class="d-flex justify-content-between bold gl-font-lg">
+      <div>{{ $options.i18n.total }}</div>
+      <div class="js-total-amount">{{ formatAmount(totalAmount, usersPresent) }}</div>
+    </div>
+  </div>
+</template>
diff --git a/ee/app/assets/javascripts/subscriptions/buy_minutes/graphql/resolvers.js b/ee/app/assets/javascripts/subscriptions/buy_minutes/graphql/resolvers.js
index 31eeca798373d02bfcd3f7ee50206049d8884d9c..00aa22c4a5993845c34eb46d413944701d143f2b 100644
--- a/ee/app/assets/javascripts/subscriptions/buy_minutes/graphql/resolvers.js
+++ b/ee/app/assets/javascripts/subscriptions/buy_minutes/graphql/resolvers.js
@@ -34,13 +34,13 @@ export const resolvers = {
       return SubscriptionsApi.createSubscription(groupId, customer, subscription);
     },
     updateState: (_, { input }, { cache }) => {
-      const { state: oldState } = cache.readQuery({ query: STATE_QUERY });
+      const oldState = cache.readQuery({ query: STATE_QUERY });
 
       const state = produce(oldState, (draftState) => {
         merge(draftState, input);
       });
 
-      cache.writeQuery({ query: STATE_QUERY, data: { state } });
+      cache.writeQuery({ query: STATE_QUERY, data: state });
     },
   },
 };
diff --git a/ee/spec/frontend/subscriptions/buy_minutes/components/checkout/order_summary_spec.js b/ee/spec/frontend/subscriptions/buy_minutes/components/checkout/order_summary_spec.js
new file mode 100644
index 0000000000000000000000000000000000000000..1d0de49cdd2d9446588aa335f27e594019968d61
--- /dev/null
+++ b/ee/spec/frontend/subscriptions/buy_minutes/components/checkout/order_summary_spec.js
@@ -0,0 +1,225 @@
+import { mount, createLocalVue } from '@vue/test-utils';
+import { merge } from 'lodash';
+import VueApollo from 'vue-apollo';
+import OrderSummary from 'ee/subscriptions/buy_minutes/components/order_summary.vue';
+import subscriptionsResolvers from 'ee/subscriptions/buy_minutes/graphql/resolvers';
+import stateQuery from 'ee/subscriptions/graphql/queries/state.query.graphql';
+import purchaseFlowResolvers from 'ee/vue_shared/purchase_flow/graphql/resolvers';
+import {
+  mockCiMinutesPlans,
+  stateData as mockStateData,
+} from 'ee_jest/subscriptions/buy_minutes/mock_data';
+import createMockApollo from 'helpers/mock_apollo_helper';
+
+const localVue = createLocalVue();
+localVue.use(VueApollo);
+
+describe('Order Summary', () => {
+  const resolvers = { ...purchaseFlowResolvers, ...subscriptionsResolvers };
+  const initialStateData = {
+    subscription: { planId: 'silver' },
+  };
+  let wrapper;
+
+  const createMockApolloProvider = (stateData = {}) => {
+    const mockApollo = createMockApollo([], resolvers);
+
+    const data = merge({}, mockStateData, initialStateData, stateData);
+
+    mockApollo.clients.defaultClient.cache.writeQuery({
+      query: stateQuery,
+      data,
+    });
+
+    return mockApollo;
+  };
+
+  const createComponent = (stateData) => {
+    const apolloProvider = createMockApolloProvider(stateData);
+
+    wrapper = mount(OrderSummary, {
+      localVue,
+      apolloProvider,
+      propsData: {
+        plans: mockCiMinutesPlans,
+      },
+    });
+  };
+
+  afterEach(() => {
+    wrapper.destroy();
+  });
+
+  describe('Changing the company name', () => {
+    describe('When purchasing for a single user', () => {
+      beforeEach(() => {
+        createComponent({ isSetupForCompany: false });
+      });
+
+      it('should display the title with the passed name', () => {
+        expect(wrapper.find('h4').text()).toContain("Full Name's GitLab subscription");
+      });
+    });
+
+    describe('When purchasing for a company or group', () => {
+      describe('Without a group name provided', () => {
+        beforeEach(() => {
+          createComponent({ isSetupForCompany: true });
+        });
+
+        it('should display the title with the default name', () => {
+          expect(wrapper.find('h4').text()).toContain("Your organization's GitLab subscription");
+        });
+      });
+
+      describe('With a group name provided', () => {
+        beforeEach(() => {
+          createComponent({
+            isSetupForCompany: true,
+            customer: { company: 'My group' },
+          });
+        });
+
+        it('when given a group name, it should display the title with the group name', () => {
+          expect(wrapper.find('h4').text()).toContain("My group's GitLab subscription");
+        });
+      });
+    });
+  });
+
+  describe('Changing the plan', () => {
+    beforeEach(() => {
+      createComponent();
+    });
+
+    describe('the selected plan', () => {
+      it('should display the chosen plan', () => {
+        expect(wrapper.find('.js-selected-plan').text()).toContain('silver plan');
+      });
+
+      it('should display the correct formatted amount price per user', () => {
+        expect(wrapper.find('.js-per-user').text()).toContain('$228 per user per year');
+      });
+    });
+
+    describe('the default plan', () => {
+      beforeEach(() => {
+        createComponent({
+          subscription: { planId: 'bronze', quantity: 1 },
+        });
+      });
+
+      it('should display the chosen plan', () => {
+        expect(wrapper.find('.js-selected-plan').text()).toContain('bronze plan');
+      });
+
+      it('should display the correct formatted amount price per user', () => {
+        expect(wrapper.find('.js-per-user').text()).toContain('$48 per user per year');
+      });
+
+      it('should display the correct formatted total amount', () => {
+        expect(wrapper.find('.js-total-amount').text()).toContain('$48');
+      });
+    });
+  });
+
+  describe('Changing the number of users', () => {
+    beforeEach(() => {
+      createComponent({
+        subscription: { planId: 'silver', quantity: 1 },
+      });
+    });
+
+    describe('the default of 1 selected user', () => {
+      it('should display the correct number of users', () => {
+        expect(wrapper.find('.js-number-of-users').text()).toContain('(x1)');
+      });
+
+      it('should display the correct formatted amount price per user', () => {
+        expect(wrapper.find('.js-per-user').text()).toContain('$228 per user per year');
+      });
+
+      it('should display the correct multiplied formatted amount of the chosen plan', () => {
+        expect(wrapper.find('.js-amount').text()).toContain('$228');
+      });
+
+      it('should display the correct formatted total amount', () => {
+        expect(wrapper.find('.js-total-amount').text()).toContain('$228');
+      });
+    });
+
+    describe('3 selected users', () => {
+      beforeEach(() => {
+        createComponent({
+          subscription: { planId: 'silver', quantity: 3 },
+        });
+      });
+
+      it('should display the correct number of users', () => {
+        expect(wrapper.find('.js-number-of-users').text()).toContain('(x3)');
+      });
+
+      it('should display the correct formatted amount price per user', () => {
+        expect(wrapper.find('.js-per-user').text()).toContain('$228 per user per year');
+      });
+
+      it('should display the correct multiplied formatted amount of the chosen plan', () => {
+        expect(wrapper.find('.js-amount').text()).toContain('$684');
+      });
+
+      it('should display the correct formatted total amount', () => {
+        expect(wrapper.find('.js-total-amount').text()).toContain('$684');
+      });
+    });
+
+    describe('no selected users', () => {
+      beforeEach(() => {
+        createComponent({
+          subscription: { planId: 'silver', quantity: 0 },
+        });
+      });
+
+      it('should not display the number of users', () => {
+        expect(wrapper.find('.js-number-of-users').exists()).toBe(false);
+      });
+
+      it('should display the correct formatted amount price per user', () => {
+        expect(wrapper.find('.js-per-user').text()).toContain('$228 per user per year');
+      });
+
+      it('should not display the amount', () => {
+        expect(wrapper.find('.js-amount').text()).toContain('-');
+      });
+
+      it('should display the correct formatted total amount', () => {
+        expect(wrapper.find('.js-total-amount').text()).toContain('-');
+      });
+    });
+
+    describe('date range', () => {
+      beforeEach(() => {
+        createComponent();
+      });
+
+      it('shows the formatted date range from the start date to one year in the future', () => {
+        expect(wrapper.find('.js-dates').text()).toContain('Jul 6, 2020 - Jul 6, 2021');
+      });
+    });
+
+    describe('tax rate', () => {
+      beforeEach(() => {
+        createComponent();
+      });
+
+      describe('a tax rate of 0', () => {
+        it('should not display the total amount excluding vat', () => {
+          expect(wrapper.find('.js-total-ex-vat').exists()).toBe(false);
+        });
+
+        it('should not display the vat amount', () => {
+          expect(wrapper.find('.js-vat').exists()).toBe(false);
+        });
+      });
+    });
+  });
+});