Skip to main content

Checkout Lifecycle

Checkout Creation​

Upon creation, checkout is assigned to a channel that influences product stock, availability, and price. The channel can not be changed after the checkout is created. Learn more about Channels.

Checkout can be created from existing orders, which is helpful for re-order functionality.

Authentication in Checkout​

Saleor allows you to manage both logged-in customers and guest users using the same API mutations. You do not need separate logic branches for different user states; instead, Saleor determines the checkout ownership based on the request's authentication context.

  • With Auth Header: If the checkout creation request includes a valid user token, the new checkout is automatically "Signed" (linked to that specific User ID).

  • Without Auth Header: The checkout is created as "Anonymous" (guest). It is identified solely by a unique token and is not linked to any user profile.

Attaching a Guest Checkout to a User​

If a guest user logs in midway through their shopping experience, you can convert their anonymous checkout into a signed one using the checkoutCustomerAttach mutation. This ensures their items are preserved and linked to their account history.

Updating Checkout​

  • Updating lines. Each line (product variant) should have at least a quantity of 1; if the line reaches a quantity of 0, it will be automatically removed. Optionally Price overwrites and Line Stacking can be controlled via API.

Completing Checkout​

When checkout is finalized/completed, it is converted into an order.

The following are the requirements to finalize the checkout:

  1. The required addresses are valid, except when skipValidation is used.

  2. Delivery options and addresses are valid. Learn more.

  3. All selected products are in stock (while purchasing, another user could already buy the last available item). See Allocations and Reservations.

  4. The payments are processed unless the Channel setting of the checkout has allowUnpaidOrders setting enabled or checkout's total is 0. If you need to bypass this setting, you use orderCreateFromCheckout.

note

If case an order is overcharged, it will still be created. The overcharge must be handled manually.

Completing Anonymous Orders​

When a guest checkout is completed, Saleor performs an automatic lookup using the provided email address.

  • If a user account with that email already exists, Saleor assigns the resulting order to that user automatically.

  • If the email does not exist at the time of purchase, Saleor still tracks the association. When a user later creates and confirms an account with that email, their previous guest orders are automatically attached to their new profile.

Checkout Expiration and Deletion​

To avoid overloading the database, unfinished and unpaid checkouts are automatically deleted after a specified period from their last modification:

  • checkouts without lines after 6 hours,
  • anonymous checkouts (neither user nor email is set) with lines after 30 days,
  • user checkouts (either user or email is set) with lines after 90 days.

Releasing Funds for Abandoned Checkouts​

Payments for items left in the cart by customers who did not complete the purchase will be refunded to the customer's account.

Abandoned checkout is the checkout that hasn't been changed in a specific period. The TTL is controlled by the environment variable: CHECKOUT_TTL_BEFORE_RELEASING_FUNDS, a default set to 6 hours.

For any transactionItem with processed funds (authorizedAmount or chargeAmount) assigned to abandoned checkout, Saleor will trigger the release action.

The release action is:

  • webhook with the event:TRANSACTION_CANCELATION_REQUESTED triggered when transactionItem contains authorized funds
  • webhook with the event: TRANSACTION_REFUND_REQUESTED triggered when transactionItem contains charged funds.

The release action is triggered only once. If a subscription for a release event is missing or the app fails to process the action, the release action needs to be handled manually.

To fetch paid checkouts, use the below query:

query checkouts($first: Int, $filter: CheckoutFilterInput) {
checkouts(first: $first, filter: $filter) {
totalCount
edges {
node {
id
totalBalance {
amount
}
}
}
}
}