The Vue Storefront Essentials Course is now available! Learn More
Commercetools: Checkout

Commercetools: Checkout

Check out a complete implementation with mocked payments in the unified-storefront-demo#commercetools branch.

The Storefront Starter provides flexibility to customize the checkout process based on the specific requirements of each eCommerce platform. This guide focuses on enhancing the checkout flow for Commercetools, considering its unique workflow and features.

Prerequisites

Before proceeding with the implementation, ensure that you have the following prerequisites in place:

  1. A Payment Provider for Commercetools configured.

    Vue Storefront supports multiple payment providers for Commercetools. Refer to the payments integration documentation for detailed setup instructions.

Checkout Flow Overview

In the context of Commercetools integration, we'll cover the following aspects of the checkout flow using the Storefront:

  1. Assigning an email to the order (provided by UST).
  2. Setting the shipping address (provided by UST).
  3. Setting the billing address.
  4. Selecting the shipping method (provided by UST).
  5. Performing payment.
  6. Placing an order using checkout data (provided by UST).

While many aspects of the checkout process are supported out-of-the-box by the Storefront, completing the checkout process requires handling billing address data and integrating with the payment provider.

Middleware Extension

To enable the completion of the checkout process in Commercetools, we need to extend the unified extension API with methods that handle billing address data and payment processing. Follow these steps:

  1. Extend unifiedApiExtension methods with the apiMethods property.
  2. Implement the setCartBillingAddress method to handle billing address data.
  3. Implement the createPaymentAndPlaceOrder method to process payments and place the order.
// middleware.config.ts
import {
  Config,
  Context,
  methods,
  getCartVersion,
  getNormalizerContext,
  normalizers,
  createUnifiedExtension,
  CreateCustomerAddressArgs,
  getNormalizers
} from "@vsf-enterprise/unified-api-commercetools";
import { cartActions } from "@vsf-enterprise/commercetools-api";
import { Cart } from "@vsf-enterprise/commercetools-types";

type CreatePaymentAndPlaceOrderArgs = {
  /* provide arguments required to finish checkout, e.g. payments data */
};

const apiMethods = methods<typeof normalizers>();
const unifiedApiExtension = createUnifiedExtension<Context>()({
  normalizers,
  apiMethods: {
    ...apiMethods,
    setCartBillingAddress: async (context, { address }: CreateCustomerAddressArgs) => {
      const { unnormalizeAddress, normalizeCart } = getNormalizers(context);
      const activeCart = await getCartVersion(context);

      const cart = await context.api.updateCart({
        id: activeCart.id,
        version: activeCart.version,
        actions: [cartActions.setBillingAddressAction(unnormalizeAddress(address))],
      });
      return normalizeCart(cart.data?.cart as Cart, getNormalizerContext(context));
    },
    createPaymentAndPlaceOrder: async (context, args: CreatePaymentAndPlaceOrderArgs) => {
      /*
       * Perform operations before creating order, e.g. handle payments with your payments provider
       */

      return apiMethods.placeOrder(context);
    },
  },
});

With these changes, custom setCartBillingAddress and createPaymentAndPlaceOrder methods should be now available in the SDK.

Frontend Implementation

1. Add Billing Address Form and Handle Data with SDK

In your checkout page component (pages/checkout), add a billing address form to allow users to input their billing address.

Next.js
// pages/checkout.tsx
import { CheckoutAddress } from "~/components";
import { useCartMutation } from "~/hooks/cart/utils";
import { useLockBodyScroll } from '~/hooks';
import { sdk } from "~/sdk";

export function CheckoutPage() {
  const setBillingAddress = useCartMutation(async (params) => sdk.unified.setCartBillingAddress(params));
  const billingAddressModal = useLockBodyScroll();
  // ...
  return (
    // ...
    <CheckoutAddress savedAddress={cart.billingAddress || undefined} onOpen={billingAddressModal.open} />
    {billingAddressModal.isOpen && (
      <AddressModal
        address={cart.billingAddress}
        onSubmit={(address) => setBillingAddress.mutate({ address })}
        onClose={billingAddressModal.close}
      />
    )}
    // ...
  );
}
  • Display the billing address form.
  • Update the Cart's billing data with the setCartBillingAddress method.

2. Handle Payment and Place Order

In the same checkout page component, handle payment and place the order using the payment provider component. Make sure that before redirecting the user to the order confirmation page, you invalidate the cart query to ensure that the cart is cleared and that order confirmationd data is saved in the cache.

Next.js
// pages/checkout.tsx

/* Part of this file has been omitted for readability */
import { PaymentProviderComponent } from "your-payment-provider";
import { useMutation, useQueryClient } from "@tanstack/react-query";
import { sdk } from "~/sdk";

export function CheckoutPage() {
  // ...
  const queryClient = useQueryClient();
  const placeOrder = useMutation(async (params) => sdk.unified.createPaymentAndPlaceOrder(params), {
    retry: false,
  });
  const makeOrder = async (paymentData) => {
    /*
     * Validate checkout forms and payment data
     */

    placeOrder.mutate(paymentData, {
      onSuccess: async (data) => {
        queryClient.setQueryData(["order", "confirmation"], data);
        await queryClient.invalidateQueries(["cart"]);
        return push("/order/success");
      },
      onError() {
        push(`/order/failed`);
      },
    });
  };

  return (
    // ...
    <PaymentProviderComponent onSubmit={(paymentData) => makeOrder(paymentData)} />
    // ...
  );
}

Vue Storefront supports multiple payment providers for Commercetools. Refer to the payments integration documentation for detailed setup instructions.


That's it! Your checkout page is now effectively integrated with Commercetools.