Enable order signals in the Connect web library

You can enable the Connect library to send order signals each time a visitor places an order on your website.

An order represents a transaction placed by a user within an application. Here are some typical examples from different business domains:

  • Retail: an order confirmation
  • Travel: a booking confirmation
  • Banking/insurance: an application confirmation

Availability

The order signal is included in all Connect subscriptions (Pro, Premium and Ultimate).

Configuration

The Connect library provides a method, TLT.logSignal() with which to send the signal to the Acoustic Connect endpoint, as a JSON object. The Connect library must be initialized before the method is called.

The order signal is probably the most advanced in terms of implementation. This is mainly because you will rarely find all order details on the order confirmation page. Usually, some of them are only available earlier in the checkout process but you cannot fire the signal from there because an order ID hasn't been generated yet. In such a situation we recommend saving the data points in the session storage of the browser until the user confirms their order and the signal can be sent.

Required fields

You must populate the required fields before sending the signal: the order ID and the currency. Without them, the signal will be ignored.

FieldValuesDefinition
categoryString. Valid value - Behavior. The category of the signal. Do not edit.
currency String. Valid values - ISO 4217 currency codes.The currency used for the order
effectString. Valid values:

- negative
- positive
Describes the effect of the signal on engagement. It is intended to be used for engagement index scoring.

We suggest sending positive for all order signals.
nameString, up to 256 characters Assign a name to the signal to differentiate it from other signals.
orderId StringAn identifier assigned to the order. You can usually scrape it from the URL or from the page.
signalTypeString. Valid value -
order.
The type of signal to enable. Do not edit.

Optional fields

FieldValuesDefinition
orderDiscountNumberDiscount from the original price. If the exact amount is not provided, you could calculate the difference between the original and the current price.
orderedItemsArray of objectsThe list of products in the order. You need a separate object for each product, but not each instance of the same product - three identical items would result in one object, with a quantity of 3.
orderShippingHandlingNumberThe shipping and handling amount for the order
orderSubtotalNumberThe subtotal amount for the order (net of discount)
orderTaxNumberThe tax amount for the order
orderValueNumberThe total value of the order
signalCustomAttributesArray of objectsAllows for additional custom attributes. For each custom attribute, add an object with two string fields:name and value.

Fields supported by objects within the orderedItems array

FieldValuesRequired?Definition
actionStateStringOptionalPlaceholder to support future functionality
currencyString. Valid values - ISO 4217 currency codes.OptionalThe currency in which the price of the product is represented
discountNumberOptionalDiscount from the original price. If the exact amount is not provided, you could calculate the difference between the original and the current price.
productCategoryStringOptionalThe category that the product falls in based on the product catalog
productDescriptionStringOptionalThe description of the product
productIdStringRequiredThe identifier of the product. It may coincide with the SKU.
productImageUrlStringOptionalThe URL of the product image
productNameStringRequiredThe name of the product
productUrlStringOptionalThe URL of the product page
promotionIdStringOptionalFor the use case of on-site marketing like hero images and other calls-to-action, those calls-to-action would have a promotion ID that would "stick" to the behaviors after it.
quantityIntegerOptionalCaptures the quantity of the product purchased.
resumedCartItemStringOptionalPlaceholder to support future functionality
shoppingCartUrlStringOptionalThe URL of the shopping cart
unitPriceNumberRequiredIndicates how much the client paid for the product.
virtualCategoryStringOptionalThe category is based on how the visitor got to the product page, for example from "New arrivals" or "Sale".

Example

// Utility function to write partial signal to session storage
function store(key, value) {
    var acoStore;

    if (!sessionStorage) {
        console.warn("session storage unavailable");
        return false;
    }

    acoStore = sessionStorage.getItem("aco-store") || "{}";
    acoStore = JSON.parse(acoStore);
    // If a signal is being passed in
    if (typeof key === "object" && key.signalType && value === undefined) {
        // If a key for this signalType exists
        if (acoStore[key.signalType]) {
            acoStore[key.signalType].push(key);
        } else {
            acoStore[key.signalType] = [key];
        }
    }
    if (key && value !== undefined) {
        acoStore[key] = value;
    }
    sessionStorage.setItem("aco-store", JSON.stringify(acoStore));
    return true;
}

// Utility function to retrieve signal from session storage
function retrieve(key, del) {
    var acoStore,
        value;

    if (!sessionStorage) {
        console.warn("session storage unavailable");
        return false;
    }

    acoStore = sessionStorage.getItem("aco-store") || "{}";
    acoStore = JSON.parse(acoStore);
    value = acoStore[key] || "";
    if (del && del === "delete") {
        delete acoStore[key];
        if (Object.keys(acoStore).length === 0) {
            sessionStorage.removeItem("aco-store");
        } else {
            sessionStorage.setItem("aco-store", JSON.stringify(acoStore));
        }
    }
    return value;
}

// Check that the Connect SDK is present
if (window.TLT && window.TLT.isInitialized()) {

    const signal = {
            signalType: "order",
            name: "order generated by web site",
            category: "Behavior",
            orderId: "", // Required
            orderSubtotal: 0,
            orderValue: 0,
            currency: "USD", // Required
            orderShippingHandling: 0,
            orderTax: 0,
            orderDiscount: 0,
            orderedItems: [],
            effect: "positive", // Required
            signalCustomAttributes: []
        },
        href = window.location.href;

    // On the "Place Order" screen, before the order is placed and the booking confirmation page shown
    if (href.includes("/place-order")) {
        const rows = document.querySelectorAll("tr.lineItem");
        // Iterate over the table of products and populate orderedItems array
        rows.forEach((row) => {
            const orderedItem = {
                productId: "", // Required
                productName: "", // Required
                productDescription: "",
                quantity: 0,
                unitPrice: 0, // Required
                currency: "USD", // Required
                discount: 0,
                productCategory: "",
                productUrl: "",
                productImageUrl: "",
                shoppingCartUrl: "https://" + window.location.host + "/shopping-cart",
                promotionId: "",
                virtualCategory: "",
                actionState: "",
                resumedCartItem: ""
            };
            orderedItem.productId = row.querySelector(".lineItem_sku").innerText || "";
            orderedItem.productName = row.querySelector(".lineItem_name").innerText || "";
            orderedItem.quantity = Number(row.querySelector(".lineItem_qty").innerText) || 0;
            orderedItem.productUrl = row.querySelector(".lineItem_nameLink").innerText || "";
            orderedItem.productImageUrl = row.querySelector(".lineItem_image > img").src.split("?")[0] || "";
            const discountedPrice = Number(row.querySelector(".price_sale").innerText) || 0;
            const regularPrice = Number(row.querySelector(".price_defautl").innerText) || 0;
            if (discountedPrice) {
                orderedItem.unitPrice = discountedPrice;
                orderedItem.discount = Math.round((regularPrice - discountedPrice) * 100) / 100;
            } else {
                orderedItem.unitPrice = regularPrice;
            }
            signal.orderedItems.push(orderedItem);
        });
        // Scrape the total prices from below the table
        signal.orderSubtotal = Number(document.querySelector("div.orderSummary_subtotal").innerText) || 0;
        signal.orderValue = Number(document.querySelector("div.orderSummary_total").innerText) || 0;
        signal.orderShippingHandling = Number(document.querySelector("div.orderSummary_shipping").innerText) || 0;

        // Don't send the signal now, store it until we have the order confirmation number on the next page
        store(signal);
    }

    // On the "Order Confirmation" screen, retrieve the signal from storage, add the orderId, and send it
    if (href.includes("OrderConfirmation/?OrderNumber=")) {
        const orderSignal = retrieve("order", "delete"); // Delete the signal from storage after retrieving
        orderSignal.orderId = href.split("OrderConfirmation/?OrderNumber=")[1];
        // If we have a stored signal, an orderId, and an orderedItems array, log signal
        if (orderSignal && orderSignal.orderId && orderSignal.orderedItems.length) {
            window.TLT.logSignal(orderSignal);
        }
    }
}