Skip to content

PlaceOrder container

The PlaceOrder container is designed to handle the final step in the checkout process, where the user confirms and places their order. You can configure it to disable the button, perform some validations before submitting the form, handle the place order action, and manage the content slot for the place order button.

PlaceOrder configurations

The PlaceOrder container provides the following configuration options:

OptionTypeReq?Description
disabledbooleanNoDisables the Place Order button.
handleValidationfunctionNoA function that performs validation checks and returns a boolean indicating whether the validation passed; in case it returns false, the order will not be placed.
handlePlaceOrderfunctionYesAn asynchronous function that handles the order placement process. It receives a context object containing the selected payment method code and the cart ID.
slotsobjectNoObject with the content to be displayed on the container. This slot allows setting the PlaceOrder container content dynamically based on the selected payment method.

These configuration options are implementing the PlaceOrderProps interface:

PlaceOrderProps interface

The PlaceOrder container receives an object as parameter which implements the PlaceOrderProps interface with the following properties:

export interface PlaceOrderProps extends HTMLAttributes<HTMLDivElement> {
disabled?: boolean;
handleValidation?: () => boolean;
handlePlaceOrder: (ctx: HandlePlaceOrderContext) => Promise<void>;
slots?: {
Content?: SlotProps<ContentSlotContext>;
};
}
  • The disabled property set to true forces the Place Order button to be disabled.
  • The handleValidation property is a handler used to perform some validation checks before submitting the checkout forms and placing the order.
  • The handlePlaceOrder property is a handler used to perform some actions related to the order placement process. It is executed when the Place Order button is clicked and if the handleValidation returns true (in case it is provided, otherwise it will be executed without validation checks).
  • The asynchronous function provided accepts a context as parameter which implements the HandlePlaceOrderContext interface:

    export interface HandlePlaceOrderContext {
    code: string;
    cartId: string;
    }
  • The slots property is an object containing the following properties:
  • Content property

    The Content property is a handler used to render the PlaceOrder container content based on the selected payment method code:

    export type SlotProps<T = any> = (
    ctx: T & DefaultSlotContext<T>,
    element: HTMLDivElement | null
    ) => Promise<void> | void;
    export interface ContentSlotContext {
    code: string;
    }

    Example 1: Render performing validations and a handler for order placement

    The following example renders the PlaceOrder container on a checkout page using the PaymentServices drop-in component as a payment method. It includes functionality to validate login, shipping, and billing forms before placing an order. If the validation passes, it attempts to place the order and handles any errors.

    // Checkout Dropin
    import PlaceOrder from '@dropins/storefront-checkout/containers/PlaceOrder.js';
    import { render as CheckoutProvider } from '@dropins/storefront-checkout/render.js';
    // Order Dropin Modules
    import * as orderApi from '@dropins/storefront-order/api.js';
    // Payment Services Dropin
    import { PaymentMethodCode } from '@dropins/storefront-payment-services/api.js';
    const LOGIN_FORM_NAME = 'login-form';
    const SHIPPING_FORM_NAME = 'selectedShippingAddress';
    const BILLING_FORM_NAME = 'selectedBillingAddress';
    const $placeOrder = checkoutFragment.querySelector('.checkout__place-order');
    const shippingFormRef = { current: null };
    const billingFormRef = { current: null };
    const creditCardFormRef = { current: null };
    CheckoutProvider.render(PlaceOrder, {
    handleValidation: () => {
    let success = true;
    const { forms } = document;
    const loginForm = forms[LOGIN_FORM_NAME];
    if (loginForm) {
    success = loginForm.checkValidity();
    if (!success) scrollToElement($login);
    }
    const shippingForm = forms[SHIPPING_FORM_NAME];
    if (
    success
    && shippingFormRef.current
    && shippingForm
    && shippingForm.checkVisibility()
    ) {
    success = shippingFormRef.current.handleValidationSubmit(false);
    }
    const billingForm = forms[BILLING_FORM_NAME];
    if (
    success
    && billingFormRef.current
    && billingForm
    && billingForm.checkVisibility()
    ) {
    success = billingFormRef.current.handleValidationSubmit(false);
    }
    return success;
    },
    handlePlaceOrder: async ({ cartId, code }) => {
    await displayOverlaySpinner();
    try {
    // Payment Services credit card
    if (code === PaymentMethodCode.CREDIT_CARD) {
    if (!creditCardFormRef.current) {
    console.error('Credit card form not rendered.');
    return;
    }
    if (!creditCardFormRef.current.validate()) {
    // Credit card form invalid; abort order placement
    return;
    }
    // Submit Payment Services credit card form
    await creditCardFormRef.current.submit();
    }
    // Place order
    await orderApi.placeOrder(cartId);
    } catch (error) {
    console.error(error);
    throw error;
    } finally {
    removeOverlaySpinner();
    }
    },
    })($placeOrder),

    Example 2: Render providing explicit content for the button

    The following example renders the PlaceOrder container on a checkout page providing different text for the Place Order button depending on the selected payment method.

    // Checkout Dropin
    import PlaceOrder from '@dropins/storefront-checkout/containers/PlaceOrder.js';
    import { render as CheckoutProvider } from '@dropins/storefront-checkout/render.js';
    // Order Dropin Modules
    import * as orderApi from '@dropins/storefront-order/api.js';
    const $placeOrder = checkoutFragment.querySelector('.checkout__place-order');
    CheckoutProvider.render(PlaceOrder, {
    handlePlaceOrder: async ({ cartId }) => {
    orderApi.placeOrder(cartId).catch(console.error);
    },
    slots: {
    Content: (ctx) => {
    const content = document.createElement('span');
    ctx.appendChild(content);
    function setContent(currentCtx) {
    switch (currentCtx.code) {
    case 'checkmo': {
    content.textContent = 'Pay Now';
    break;
    }
    case 'banktransfer': {
    content.textContent = 'Make a transfer';
    break;
    }
    default: {
    content.textContent = currentCtx.dictionary.Checkout.PlaceOrder.button;
    }
    }
    }
    setContent(ctx);
    ctx.onChange(setContent);
    },
    },
    })($placeOrder),