Appearance
Create a checkout
In this chapter you will learn how to
- Fetch and display payment and shipping information
- Create an order summary (totals, taxes)
- Place an order
Shipping and payment information
WARNING
Please remember that payment and shipping methods shouldn't be cached. It is important to refresh data before displaying it.
Before fetching, ensure the cart is not empty by using refreshCart
in the useCart
composable.
Get shipping methods
ts
const { getShippingMethods } = useCheckout();
await getShippingMethods();
Display shipping methods
vue
<script setup lang="ts">
const {
shippingMethods,
setShippingMethod,
selectedShippingMethod: shippingMethod,
getShippingMethods,
} = useCheckout();
const selectedShippingMethod = computed({
get(): string {
return shippingMethod.value?.id || "";
},
async set(shippingMethodId: string) {
await setShippingMethod({ id: shippingMethodId });
},
});
</script>
<template>
<div v-for="shippingMethod in shippingMethods" :key="shippingMethod.id">
<input
:id="shippingMethod.id"
v-model="selectedShippingMethod"
:value="shippingMethod.id"
name="shipping-method"
type="radio"
/>
<label :for="shippingMethod.id">
{{ shippingMethod.name }}
</label>
</div>
</template>
The shipping method position on the list is determined by the position
field settled on the admin panel. Sorting logic in useCheckout.ts::getShippingMethods()
You can also display:
- Shipping delivery time
- Shipping icon
- Shipping description
Get payment methods
ts
const { getPaymentMethods } = useCheckout();
await getPaymentMethods();
Display payment methods
vue
<script setup lang="ts">
const {
paymentMethods,
selectedPaymentMethod: paymentMethod,
setPaymentMethod,
} = useCheckout();
const selectedPaymentMethod = computed({
get(): string {
return paymentMethod.value?.id || "";
},
async set(paymentMethodId: string) {
await setPaymentMethod({ id: paymentMethodId });
},
});
</script>
<template>
<div v-for="paymentMethod in paymentMethods" :key="paymentMethod.id">
<input
:id="paymentMethod.id"
v-model="selectedPaymentMethod"
:value="paymentMethod.id"
name="payment-method"
type="radio"
/>
<label :for="paymentMethod.id">
{{ paymentMethod.name }}
</label>
</div>
</template>
Personal information
Each guest user has to provide billing data. Those data will be used to create a standard or temporary account.
vue
<script setup lang="ts">
const state = reactive({
salutationId: "",
firstName: "",
lastName: "",
email: "",
password: "",
guest: false,
billingAddress: {
street: "",
zipcode: "",
city: "",
countryId: "",
},
});
const { register } = useUser();
const { getCountries } = useCountries();
const { getSalutations } = useSalutations();
const invokeSubmit = () => {
register(state);
};
</script>
<template>
<form
id="checkout-billing-address"
name="checkout-billing-address"
method="post"
@submit.prevent="invokeSubmit"
>
<label for="salutation">Salutation</label>
<select id="salutation" v-model="state.salutationId" name="salutation">
<option disabled selected value="">Choose salutation...</option>
<option
v-for="salutation in getSalutations"
:key="salutation.id"
:value="salutation.id"
>
{{ salutation.displayName }}
</option>
</select>
<label for="first-name">First name</label>
<input
id="first-name"
v-model="state.firstName"
type="text"
name="first-name"
/>
<label for="last-name">Last name</label>
<input
id="last-name"
v-model="state.lastName"
type="text"
name="last-name"
/>
<input id="create-account" v-model="state.guest" type="checkbox" />
<label for="create-account">Do not create a customer account.</label>
<label for="email-address">Email address</label>
<input
id="email-address"
v-model="state.email"
type="email"
name="email-address"
/>
<div v-if="!state.guest">
<label for="password">Password</label>
<input
id="password"
v-model="state.password"
type="password"
name="password"
/>
</div>
<label for="street-address">Street address</label>
<input
id="street-address"
v-model="state.billingAddress.street"
type="text"
name="street-address"
/>
<label for="postal-code">ZIP / Postal code</label>
<input
id="postal-code"
v-model="state.billingAddress.zipcode"
type="text"
name="postal-code"
/>
<label for="city">City</label>
<input
id="city"
v-model="state.billingAddress.city"
type="text"
name="city"
/>
<label for="country">Country</label>
<select
id="country"
v-model="state.billingAddress.countryId"
name="country"
>
<option disabled selected value="">Choose country...</option>
<option
v-for="country in getCountries"
:key="country.id"
:value="country.id"
>
{{ country.name }}
</option>
</select>
<button type="submit">Save</button>
</form>
</template>
Order summary
We can use some helper methods from useCart
composable to display an order summary and format prices.
Refer to formatting prices for more information on displaying prices.
WARNING
Totals should not be calculated by the frontend. All calculations should be done on the backend side.
vue
<script setup lang="ts">
const { refreshCart, cartItems, subtotal, totalPrice, shippingTotal } =
useCart();
const { getFormattedPrice } = usePrice();
await refreshCart();
</script>
<template>
<div>
<div>
<p>Subtotal</p>
<p>{{ getFormattedPrice(subtotal) }}</p>
</div>
<div>
<p>Shipping estimate</p>
<p>{{ getFormattedPrice(shippingTotal) }}</p>
</div>
<div>
<p>Order total</p>
<p>{{ getFormattedPrice(totalPrice) }}</p>
</div>
</div>
</template>
Place an order
Placing an order requires
- A valid shipping address
- A selected payment method
- A selected shipping method
After placing an order with the createOrder
method, the cart is refreshed automatically.
ts
const { createOrder } = useCheckout();
const { refreshCart } = useCart();
const order = await createOrder();
refreshCart();
After creating an order, you can fetch order data. orderId
is returned by the createOrder
method from the useCheckout
composable. The backend allows fetching orders related only to the current user by checking the session.
ts
const {
loadOrderDetails,
personalDetails,
billingAddress,
shippingAddress,
order,
} = useOrderDetails({ order: { id: orderId } as any });
await loadOrderDetails();