Upgrading From 3.19 To 3.20
To follow the zero-downtime strategy when upgrading to 3.20, It is recommended to first migrate to latest 3.19.X and turn on the Celery worker to process all data migrations asynchronously. Otherwise, you will need to downtime your solution to ensure correct data migration.
Private media storage
Since Saleor 3.20 webhook event delivery payloads are stored as files using dedicated private storage.
Make sure PRIVATE_FILE_STORAGE
is configured properly in settings.py
before upgrade.
Please refer to Amazon S3 or Google Cloud Storage (GCS) guidelines if you are using cloud storage.
Automatic checkout completion
Saleor 3.20 added new functionality to automatically complete Checkout
that have been paid by the customer.
👉 To learn more, about how this feature works and how to enable it, read the Automatic Checkout completion docs.
By "paid" in this guide we mean that the TransactionItems
connected to Checkout
have fully covered Checkout.totalPrice
, which results in Checkout.authorizeStatus
being set to FULL
.
Learn more about this in Transaction docs
This feature is disabled for existing instances by default and needs to be enabled manually in the channel settings.
New Saleor instances created from version 3.20 onwards will have this feature enabled by default.
This removes the need for storefront implementation to call checkoutComplete
mutation in order to create Order
.
Previously a store might have had missing orders, due to checkoutComplete
mutation not being always called. This could have happened for many unexpected reasons: network issues, storefront bug, customer leaving website, closing tab before being redirected from payment provider, etc.
Before Saleor 3.20 you might have used other measures to mitigate missing orders issue, for example by using CHECKOUT_FULLY_PAID
webhook and calling checkoutComplete
mutation on your backend.
After migrating to Saleor 3.20 and enabling this feature for all your channels, you can safely get rid of this workaround.
Keep in mind that CHECKOUT_FULLY_PAID
webhook would have only been triggered if you also used TransactionFlowStrategy.CHARGE
for your channel. Automatic checkout completion doesn't have this problem.
Before you enable this feature
Enabling this feature might break your current storefront implementation and services connected to Saleor that use Checkout
object.
Before you enable this feature make sure that you don't rely on any Checkout
query or mutation that updates Checkout
after transaction is created.
Automatic checkout completion works just like calling checkoutComplete
mutation, which converts Checkout
object into Order
.
After completion checkout
query with a id
of already completed Checkout
will return null
.
You can determine if checkout was automatically completed by making checkout
query:
query CheckoutDetails($id: ID!) {
checkout(id: $id) {
id
authorizeStatus
}
}
If Saleor returns null
, then you can assume that it was automatically completed:
{
"data": {
"checkout": null
},
}
If automatic checkout completion is not enabled in a Channel
, storefront should checkoutComplete
mutation, once authorizeStatus
is set to FULL
:
{
"data": {
"checkout": {
"id": "Q2hlY2tvdXQ6",
"authorizeStatus": "FULL"
}
}
}
The checkout may not be automatically completed due to issues that arise during validation.
You can identify any existing problems using the Checkout.problems
query.
For more information, refer to the Checkout Problems page.
To determine if Checkout
was completed you can also rely on response from transactionInitialize
or transactionProcess
mutations by checking the TransactionEvent
object returned in the mutation response.
Such transaction is considered successful in a context of a Checkout
when the TransactionEvent
's amount
is equal to Checkout.totalPrice
and its type
is one of these:
AUTHORIZATION_SUCCESS
AUTHORIZATION_REQUEST
CHARGE_SUCCESS
CHARGE_REQUEST
Note: payment provider might sometimes return lower amount than the one you have requested. In that case Checkout
will not be paid.
Your logic also needs to take into consideration multiple transactions if you use split-payments. If you use them, it might be easier to make separate checkout
query, described in previous section.
In order to get the Order
details after payment you can use:
checkoutComplete
mutation
This query can be called directly by customers in a storefront.
This mutation is idempotent, which means it can be called multiple times, but only one Order
will be created. If checkoutComplete
is invoked multiple times it will return the same Order
object.
mutation CompleteCheckout($id: ID!) {
checkoutComplete(id: $id) {
order {
id
}
}
}
If Checkout
has been paid, the response will contain Order
object:
{
"data": {
"checkoutComplete": {
"order": {
"id": "T3JkZXI6NzQ="
}
}
}
}
If Checkout
hasn't been paid, the response will contain an error:
{
"data": {
"checkoutComplete": {
"errors": [
{
"code": "CHECKOUT_NOT_FULLY_PAID"
}
]
}
}
}
orders
query
This query requires MANAGE_ORDERS permission, it cannot be used directly by customers in a storefront.
orders
query filter, has an option to provide checkoutIds
or checkoutTokens
to get Order
object that was created by specified Checkout
:
query OrderDetails($checkoutId: ID!) {
orders(filter: {checkoutIds: [$checkoutId]}, first: 1) {
edges {
node {
id
}
}
}
}
query OrderDetails($checkoutToken: UUID!) {
orders(filter: {checkoutTokens: [$checkoutToken]}, first: 1) {
edges {
node {
id
}
}
}
}
If you use orderCreateFromCheckout
mutation with parameter removeCheckout: false
multiple Orders
may be created from a single Checkout
.
Please note that this situation doesn't apply to checkoutComplete
mutation or automatic checkout completions. Therefore, we can safely assume that only one Order
can be created from Checkout
. This is why this query uses first: 1
parameter.