Promotions
Introduction
The Promotion feature presents the user with various promotions that have specific conditions, which need to be fulfilled to receive promotion benefits.
Promotion types
There are two types of promotions:
- Automatic discount
- Buy X get Y
The API supports various conditions that can be adjusted for certain types of promotions. We've currently implemented flows for a selected few of them. The following promotion examples have been implemented:
Automatic discount
- Add 20% for three items of a particular product
- You will add the product that has the applicable promotion in it, and you will need to add at least three items of the same product inside the basket to have a discount for it
- Add 40% when the basket exceeds the 100$
- The discount will be applied when the sum in the basket reaches 100$ and the promotion needs to be used for at least one basket item
Buy X get Y
- Get a gift for at least two items
- You can choose a gift for a certain product when you add at least two items of the same product
- Get a gift when the same items reach 100$
- You can choose a gift for a certain product when the price sum of all the same items reaches 100$
How does it work?
We're fetching all active promotions inside our app and showing them via a cross-page sticky banner. The user will be presented with a countdown displaying the end of the promotion. Promotions are also able to display additional information that depends on the promotion type and conditions (such as progress bar, category button, etc.).
Promotion Custom data
A Promotion has a customData
object within its payload. Through the customData
field, additional data can be added to the promotion to address the individual needs of each shop. The customData
can also be typed to more specifically match the data in the field.
// ~/types/promotion.ts
import type { PromotionCustomData } from '@scayle/storefront-nuxt';
type CustomData = PromotionCustomData &
Partial<{
product: {
promotionId: number
badgeLabel: string
}
giftConditions: Partial<{
productId: number
minQuantity: number
}>
category: {
id: number
ctaLabel: string
}
headlineParts: string[]
terms: string
minOrderValue: number
colorHex: string
}>
Keep in mind that this is only a compile-time check and if the customData
that comes from the API is different from the declared type, parts of the feature will be broken, and they will require adjustments in various places.
Connection between promotion & product
For the relationship between a promotion and a product, it is essential to have an ID that connects the promotion to the relevant product. First, we must add promotion
inside the product with params
as part of the attributes.withKey
fetch the promotion attribute with the product.
// ~/constants/product.ts
const PRODUCT_WITH_PARAMS: ProductWith = {
attributes: {
withKey: ['promotion']
// ...
}
After we enable the promotion
attribute within the product, we can get the ID like this:
const productPromotionId = getFirstAttributeValue(product.attributes, 'promotion')?.id;
This ID needs to be the same as it is on the promotion.customData.product.promotionId
for a particular promotion. If it's the same, then this promotion applies to the product.
Minimum order value for automatic discount promotions
To have the progress bar for automatic discounts inside the banner work correctly, we must provide minOrderValue
within the promotion.customData
. minOrderValue
needs to be aligned with the conditions configured via promotion to create/update payload endpoints
Gift conditions
The current gift flow requires giftConditons
to be defined in promotion.customData
. Inside the giftConditions
object, our important value is minQuantity
. That value also needs to be aligned with the quantity condition within itemConditions
when defining the payload on promotion create/update endpoints.
Basket flow
The first thing we need to do is to adjust the with
params as well so that we have promotion
on the basket item level and applicablePromotions
on the basket date level.
// ~/constants/basket.ts
export const BASKET_WITH_PARAMS: BasketWithOptions = {
items: {
promotion: true,
// ...
},
applicablePromotions: true,
};
When adding the product to the basket, we must send promotionId
as a payload for the applicable promotion. Then, we can go to the basket and see if the discount is applied. If not, that means that some of the conditions are not met. We can check this in basketItem.promotion.failedConditions
. If conditions are not met, the failed conditions array will be filled with the invalid conditions. Another way of detecting the promotion validation is the isValid
flag. When we add a free gift to the basket, we must remember that the promotionId
is just sent for the gift and not for the product the promotion refers to. This is because the gift
promotion is not necessarily coupled to a single product (that depends on conditions).
Important ⚠️
To be able to apply multiple different promotions in the basket, promotion needs to be configured appropriately via Promotions API:
"siblingPromotions": {
"enabled": true,
}
Or it can be configured via the SCAYLE Panel by turning on the The promotion should be combinable with other promotions
within the Combine Promotions
section.
Automatic discount flow
When an item is added to the basket that has the automatic discount promotion applied to it:
- If the conditions are not met, the banner with the countdown and title will be shown.
- Once the conditions are fulfilled, the banner disappears, and a new
Hurry to save
banner appears in thesummary
section.
This also works in reverse.
Buy X get Y flow
The user will be presented with a gift slider when the item is added to the basket with the gift promotion connected. When a user adds the gift to the basket, and all conditions are already met, the product will have the free
label. A discount will be applied. If conditions are not met, the slider will disappear and the new banner will be shown above the product to which the gift is referring, with the countdown and conditional message. In that message, we'll have a quantity left indicator, which tells the user when the conditions will be met. After we complete the requirements, the banner will disappear, and the free
label with a discount will be on the gift product. If the user, for some reason, decides to make a payment without fulfilling the gift conditions, the gift product will act as if it is a regular product without a discount.
Important ⚠️
To have the whole flow work properly, promotion must have a condition that refers only to one product. itemConditions
part of the payload should look like this when hitting the promotions API:
"itemConditions": [
{
"key": "product_id",
"condition": "item.product.id == 1082",
"level": "item"
}
]
Alternatively, we can configure it via the SCAYLE Panel in the advanced
promotions section. Then we can add item
conditions and fill the condition
input with this
item.product.id == 1082
Developer should NOT connect multiple products to the identical Buy X get Y promotion. If that happens, the condition above should be modified to match all the product IDs. That is currently not tested, and it might break the flow.
Multiple applicable promotions
Currently, we do not support applying multiple promotions to a single product. However, we can still show the numerous applicable promotions on the PDP. To decide which one will be shown, it is used the priority
flag within the promotion
payload. The promotion that has the lowest priority value has the highest priority. We also show the priority UI indicator within the PDP to tell the user which promotion will be applied.
Promotions API
Promotions are exposed via a Promotions
endpoint. Please contact your SCAYLE Representative should you require more information on how to access the Promotions
endpoint for your specific project or check our Developer Guide.
Then, we can perform the CRUD operations. The most important operations for us are create
, update
and list
.
This can all be done via the SCAYLE Panel.