docs
  1. SCAYLE Resource Center
  2. Developer Guide
  3. Features
  4. Promotions

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 the summary 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.