Skip to content

PlaceOrder container

The PlaceOrder container handles the final step in the checkout process, where the user confirms and places an order. Configure it to disable the button, perform 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:

Option Type Req? Description
activebooleanNo Activates/deactivates the container (default value is true).
disabledbooleanNo Disables the Place Order button.
handleValidationfunctionNo Performs validation checks and returns a boolean or Promise<boolean>. Supports asynchronous validation for server-side fraud checks. The order proceeds only when this function returns true (or a promise that resolves to true).
handlePlaceOrderfunctionYes Handles the order placement process asynchronously. Receives a context object containing the selected payment method code and the cart ID.
slotsobjectNo Sets the PlaceOrder container content dynamically based on the selected payment method.

These configuration options implement the PlaceOrderProps interface:

PlaceOrderProps interface

The PlaceOrder container receives an object that implements the PlaceOrderProps interface:

export interface PlaceOrderProps extends HTMLAttributes<HTMLDivElement> {
active?: boolean;
disabled?: boolean;
handleValidation?: () => boolean | Promise<boolean>;
handlePlaceOrder: (ctx: HandlePlaceOrderContext) => Promise<void>;
slots?: {
Content?: SlotProps<ContentSlotContext>;
};
}
  • The active property controls whether the container responds to system events and renders. Set to true (default) for reactive mode. Set to false to deactivate and hide the container.

  • The disabled property forces the Place Order button into a disabled state when set to true.

  • The handleValidation property performs validation checks before submitting the checkout forms and placing the order. It returns a boolean synchronously or a Promise<boolean> for asynchronous validation (for example, server-side fraud checks).

  • The handlePlaceOrder property executes when the user clicks the Place Order button and handleValidation returns true (when provided). It accepts a context parameter that implements the HandlePlaceOrderContext interface:

    export interface HandlePlaceOrderContext {
    code: string;
    cartId: string;
    }
  • The slots property contains the following:

    • The Content slot renders 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, billing, and terms & conditions 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';
import { scrollToElement } from '../../scripts/checkout.js';
const LOGIN_FORM_NAME = 'login-form';
const SHIPPING_FORM_NAME = 'selectedShippingAddress';
const BILLING_FORM_NAME = 'selectedBillingAddress';
const TERMS_AND_CONDITIONS_FORM_NAME = 'checkout-terms-and-conditions__form';
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);
}
const termsAndConditionsForm = forms[TERMS_AND_CONDITIONS_FORM_NAME];
if (success && termsAndConditionsForm) {
success = termsAndConditionsForm.checkValidity();
if (!success) scrollToElement($termsAndConditions);
}
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),