The ScandiPWA checkout page enables users to provide the necessary information to complete an order, similar to Magento 2.
When the checkout page needs to be loaded, ScandiPWA renders the Checkout
component.
The Checkout
component defines three default steps to complete an order. The first step is shipping, which handles the customer's address and delivery options. The second step is billing, which is divided into handling billing address and payment method options and completing the transaction. The final step is called “details”, which displays the transaction result.
Understanding these steps is essential for customizing the checkout process. For example, adding a custom payment method, which often involves extending the savePaymentMethodAndPlaceOrder
function of the Checkout
container. Which sometimes may need to redirect the user to a third-party site and then properly handle the return.
The main responsibility of the Checkout
component, which renders the checkout page, is to display a summary of the order and handle the various steps of the checkout process:
<aside> ✅ You can check the "Network" tab in your browser's developer tools to see the GraphQL responses. This can help you to understand!
</aside>
<aside> ℹ️ If guest checkout is allowed and the user is not authenticated, this step includes an email field to save in the cart, and later the user can register an account. This feature can be disabled in the admin.
</aside>
In this step, ScandiPWA renders the CheckoutShipping
component. This component is responsible for rendering the shipping address form, delivery options, and a button to proceed to billing.
renderContent
function return from the CheckoutShipping
component.
The renderAddressBook
function essentially displays the CheckoutAddressBook
component, which distinguishes between signed-in users and guest users.
The renderDelivery
function will display the CheckoutDeliveryOptions
component, which shows the available delivery options for the user to select.
The renderActions
renders the “Proceed to billing” button.
Note that when the user clicks on this button, the onShippingSuccess function of the CheckoutShipping container is called.
ScandiPWA sends a GraphQL mutation to update the shipping methods and costs when the user updates the shipping address.
What happens when the user clicks to proceed to billing?
After the user has selected the shipping method and clicked to proceed:
If the user is not authenticated and guest checkout is allowed, make a GraphQL mutation to set the guest email on the cart.
Send a GraphQL mutation to save the provided address in the cart as both shipping and billing addresses.
This mutation also returns the payment methods that will be used in the billing step.
saveAddressInformation
mutation returnProceeds to the next step and renders the CheckoutBilling
component in place of the CheckoutShipping
component.
In this step, The Checkout
component renders CheckoutBilling
.
CheckoutBilling
componentIn the CheckoutBilling
component render method:
The renderAddresses
function is basically responsible for allowing the user to select the billing address.
By default, the billing address is set to be the same as the shipping address. However, the user can uncheck this option and view their registered addresses if they are logged in. Alternatively, if the user has no registered address or is a guest user, they can fill out a form to add a new one.
The renderPayments
function basically renders the CheckoutPayments
component if payment methods are available. It is responsible for enabling the user to select the payment method.
<aside>
✅ Note that in the previous step, the saveAddressInformation
mutation returned the available payment methods.
</aside>
The CheckoutPayments
will render a heading, the payments options, and the selected payment:
renderContent
function return from the CheckoutPayments
component
renderHeading
function by default only renders ‘Payment method’ in a h2.
The renderPayments
function will call the renderPayment
function for each payment method. This will define what will be rendered for each payment method option.
renderPayment
function from CheckoutPayments
componentBy default, the CheckoutPayment
component is rendered for every payment method option. However, when working with payment integrations, it is common to extend this functionality to render a different component for different types of payment methods.
The renderSelectedPayment
function utilizes the paymentRenderMap
to check if there is anything associated with the selected payment method. If there is, it renders it.
paymentRenderMap: Record<string, () => ReactElement> = {
[PaymentMethods.PURCHASE_ORDER]: this.renderPurchaseOrderPayment.bind(this),
};
In this case, if the selected method is PURCHASE_ORDER
the renderSelectedPayment
will render the renderPurchaseOrderPayment
function.
A common feature is to extend the paymentRenderMap
object to include a button to replace the default complete order button.
The renderTAC
- renders terms and conditions if accepted.
The renderActions
- will basically render the button to complete the order.
Note that when the user clicks the complete order button, the onBillingSuccess function of the CheckoutBilling container is called.
The renderPopup
- renders the popup message from the terms and conditions if any.
What happens when the user clicks to complete the order?
When the user clicks the "Complete Order" button, the onBillingSuccess function of the CheckoutBilling container is called to process the address and payment method form and save this information using the savePaymentInformation function from the Checkout container.
Process the billing address and payment method information on the form. This is done in the onBillingSuccess function of the CheckoutBilling container.
Check if the user is authenticated. If the user is not authenticated, it will either save the email of the user as a guest by calling the setGuestEmailOnCart
mutation or create a user depending on the component configuration.
Save the billing address information. This is done by sending the GraphQL mutation setBillingAddressOnCart
defined in the getSetBillingAddressOnCart
function of the Checkout.query
.
Save the payment information. This is done by sending the GraphQL mutation setPaymentMethodOnCart
defined in the getSetPaymentMethodOnCartMutation
function of the Checkout.query
.
proceeds to complete the transaction.
<aside> ⚠️ Note that in reality, this process is done all at once. However, for educational purposes, the order placement will be handled separately in the next section.
</aside>
In this step, ScandiPWA creates an order using the placeOrder
mutation. For this to be possible, ScandiPWA must have already:
<aside> ✅ Note that all of this is done during the shipping and billing steps.
</aside>
When ScandiPWA needs to complete a transaction, it:
Sends a placeOrder
mutation to convert the cart into an order and returns the order ID.
Remove cart information and payment totals from localStorage
. This is important to avoid conflicts with the cart that will be created.
Create an empty cart. This is important for enabling the user to make another purchase.
mapDispatchToProps
Note that if the user is signed in, it will call the resetCart
prop defined in the mapDispatchToProps
. For guest users, it will call the resetGuestCart
prop. Both of these are responsible for calling the createEmptyCart
mutation.
Display order information.
<aside> ✅ The ScandiPWA marketplace offers a variety of extensions to integrate payment methods easily!
</aside>
By default, ScandiPWA only enables payment methods that do not require any additional steps after sending the placeOrder
mutation.
But you may want to add a payment method that requires additional behavior before or after placing the order. One example is adding a credit card payment that needs to redirect the user to a third-party site to complete the payment.
An example of this is the Mollie payment integration, which extends ScandiPWA checkout to:
Redirect the user to a third-party site to complete the payment after placing the order. This is usually done by extending the savePaymentMethodAndPlaceOrder function of the Checkout.container.js.
Process the customer return from the third-party site. This is usually done by extending the __construct and componentDidMount functions of the Checkout container.
To implement a payment method is necessary to understand how the payment process work and adjust ScandiPWA components to follow the same pattern.
<aside> ✅ A good starting point is to study the payment integration for Magento, particularly the frontend layout.
</aside>
Tutorial: Mollie Payment Method Integration
During the billing step of the checkout process, the CheckoutPayments
component is responsible for displaying all available payment method options.
renderPayment
function of the CheckoutPayments
componentThe renderPayment
function is called for each payment method to determine which component should be rendered.
By default, the CheckoutPayment
component is rendered for each payment method. To change the rendered component, you need to extend the renderPayment
function to return the appropriate component based on the payment method code.
plugin/CheckoutPayments.plugin.js
The plugin example utilizes a custom PaypalPayment
component. If you want to develop your own, you can use the CheckoutPayment component as a starting point. It should use the following props:
isSelected
is a boolean value that indicates whether the payment method is currently selected.method
- Contains information about the payment method.onClick
- Must be called passing the method
prop when this payment method is selected.<aside> 🚨 Replacing the default button with a custom component may impact the checkout process. Therefore, you should ensure that it completes successfully. Understanding the billing process may help you.
</aside>
The CheckoutPayments
components make use of the paymentRenderMap
property to verify if there is something associated with the selected payment method to render in the renderSelectedPayment function.
paymentRenderMap
property of the CheckoutPayments
componentTo add a custom button depending on the payment method selected, you can add the custom component to the paymentRenderMap
property and hide the default button.
paymentRenderMap
propertyTo implement this, you must extend the paymentRenderMap
using a plugin:
CheckoutPayments.plugin.js
<aside>
ℹ️ Note that you must also include a plugin to the containerProps
to allow the component to access setOrderButtonVisibility
, which will be used in the component.
</aside>