Skip to end of metadata
Go to start of metadata

You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 4 Next »


Glossary

  • Offer: Offers are discounted products - this may come in the form of individual item discounts, bundle discounts, dollars-off discounts, or percent-off discounts.

  • System-Wide Offer: System-wide offers are the ‘base’ offers in the loyalty offers system. These are available to all users who meet the required criteria for redemption and do not require any steps or processes to be assigned to a specific user. You’ll see these referenced as GLOBAL offers.

  • Configuration Offer: Config. offers are one-half of the required components to create a personalized offer. These are templates that only exist to provide the configuration for personalized offers. As such, they will not appear on their own on the offers page if they are not referenced by a personalized offer and the entity by itself does not represent a redeemable incentive.

  • Personalized Offer: Personalized offers are offers assigned specifically to a particular user. Every time a personalized offer is assigned to a user, it creates a unique offer record that can only be used for that particular user.

  • Rewards: Rewards are a special type of product that usually has a price of zero, and can be exchanged for points accumulated from previous purchases.

  • Loyalty Middleware: Public facing API for Loyalty. Consists of both a REST API and a GraphQL endpoint.

  • Loyalty Engine: The core Loyalty component that handles everything behind the scenes.

  • Loyalty Platform: Refers to both Middleware and Engine as a whole.

  • Sanity: RBI’s Content Management System (CMS).


Introduction

All offers, regardless of their type, are stored in our Loyalty Platform. We also use a CMS platform called Sanity, where our team and the franchisee can set up marketing materials such as the offer's name, description, and images. Additionally, they can define within Sanity how the offer works, including rules for redemption, limitations, any requirements, and some other miscellaneous aspects of each offer.

It’s important to understand that both of these systems store and handle data separately from each other, some data gets synced between platforms, and some data does not. Sanity for example has all the images for any given offer, whereas the Loyalty Platform does not. The same is true in the other direction, the Loyalty Platform contains the records for Personalized offers, whereas Sanity does not.

The way we like to think about it is, that for any content displayed to the final user, we want to usually use the data from Sanity. For any business rules and logic, like which offer should be displayed to which user we want to use the data from the Loyalty Platform.

Also, please keep in mind that Personalized offers will only come from the Loyalty Platform, and their Sanity ID will point to a Configuration Offer, like a Many-to-One relationship.


Kiosk Integration: Displaying Offers

To display valid offers and rewards on the UI, the client needs to retrieve data from two sources: the Loyalty Engine (Through the Loyalty Middleware) and Sanity (RBI’s CMS). The Loyalty Engine will provide incentives for which the user is "eligible," meaning they satisfy all necessary criteria (rules). Meanwhile, Sanity will offer information about active incentives, and reward categories, as well as all visual display content, such as names, images, descriptions, and more.

The client must hit both of the previously mentioned data sources and intersect the data to decide what to present. The sequence in which these queries are executed will depend on the client and its implementation. It is advised to first query Sanity for all "sorted offers" (active system-wide offers), “config. offers” (possible personalized offers for any given user), and "reward categories" (active rewards), and then utilize those identifiers to request pertinent information or interact with the Loyalty Platform.

Recommended Reading

Querying Sanity

There are two ways of querying Sanity:

  • Using GraphQL.

  • Using GROQ.

We strongly suggest using GraphQL, as this is a more widely known querying language. Additionally, using the provided GraphQL playground, vendors will be able to see the whole schema during development and make necessary adjustments to their queries with ease.

Option 1) Using Sanity GraphQL

  • Replace staging_bk_de with the {{stage}}_{{brand}}_{{country}} that you desire to query.

  • Sanity GraphQL can be hit via a POST request.

  • Please reference the POST URL structure for each of the sample queries below.

  • Please use this playground to play with the queries below and examine the schema for fields you may wish to request.

Example Offers Query
  • The Sanity ID for the live offers document, for the most part, will be feature-loyalty-offers-ui-singleton, however, some markets have drifted from this convention.

  • Sample Query:

query getSanityOffers {
	LoyaltyOffersUI(id: "feature-loyalty-offers-ui-singleton") {
	# Available for all users.
	# You can use these to lookup assets from global.
    sortedSystemwideOffers {
      _id
      loyaltyEngineId
      name {
        enRaw
      }
    }
    # These are config. offers (templates).
    # You should use these only to display content (images, names, etc).
    # You can use these to lookup assets from the actual Personalized offers.
    liveConfigOffers {
      _id
      loyaltyEngineId
      name {
        enRaw
      }
    }
  }
}

Example Rewards Query
  • The Sanity ID for the live offers document, for the most part, will be reward-list-singleton, however, some markets have drifted from this convention.

  • Sample Query:

query getSanityRewards {
  RewardList(id:"reward-list-singleton") {
    rewardCategories {
      _id
      label {
        en
      }
      rewards {
        ... on Reward {
          _id
          name {
            en
          }
        }
      }
    }
  }
}

Option 2) Using a GROQ Query

  • Replace staging_bk_de with the {{stage}}_{{brand}}_{{country}} that you desire to query.

  • Replace {{GROQ_QUERY}} with the desired GROQ query.

https://czqk28jt.api.sanity.io/v2021-10-21/data/query/staging_bk_de?query={{GROQ_QUERY}}

Example Offers Query
https://czqk28jt.api.sanity.io/v2021-10-21/data/query/staging_bk_de?query=*%5B_type+in+%5B%27systemwideOffer%27%2C%27configOffer%27%5D%26%26_id+in*%5B_id%3D%3D%27feature-loyalty-offers-ui-singleton%27%5D%7B%22refs%22%3A%40.liveConfigOffers%5B%5D._ref%2B%40.sortedSystemwideOffers%5B%5D._ref%7D.refs%5B%5D+%5D%7B_id%2C_type%2Cname%2Cincentives%2Cdescription%2ClocalizedImage%2CofferPrice%2CrestaurantAvailabilityValidation%7BvalidationStatus%2ClastValidated%7D%2Crules%2Cdaypart%2CshortCode%2CredemptionMethod%2CloyaltyEngineId%7D

Example Rewards Query
// Pending...

Querying The Loyalty Platform

  • Replace euc1 with the AWS Short Region that your market belongs to. Please communicate with RBI reps to know which region your market is hosted.

  • Replace dev-bk with {{stage}}-{{brand}} that you desire to query. An example URL for ES (Spain) would be https://euw3-dev-bk-loyalty-middleware.rbictg.com/graphql

  • When querying the loyalty platform, please make sure to set the x-ui-region header to the country you want to query (uppercase):

  • Please use the playground to play with the queries below and examine the schema for fields you may wish to request.

Example Offers Query

query getLoyaltyOffers {
  loyaltyOffersV2(loyaltyId: "<USER_LOYALTY_ID>", where: { omitInvalids: false }) {
    id
    name
    type
    sanityId
    errors {
      code
      ruleId
    }
  }
}
  • Some where filters that may be relevant to Kiosk:

    • ids - A collection of Engine IDs for offers/rewards. This is the loyaltyEngineId field on every sanity offer/reward document.

    • omitInvalids - This controls whether invalid incentives will be returned. Only incentives that failed “fixable” rules will be returned if this is set to false. A fixable rule is one which the user can change behavior to make the rule pass, like the minSpend, the user can add more items to their cart to meet the requirement. An absolute failure like date-band will not be returned regardless of what the value of this filter is.

      • This is useful for displaying error / in-line messages in the UI.

    • serviceMode - Some incentives can only be redeemed if the service mode meets the required ruleset.

    • storeId - Some incentives can only be redeemed at certain stores.

    • subtotalAmount - Some incentives require a minimum spend.

    • cartEntries - Some incentives require certain items to be in the cart, this rule uses the Sanity ID of each RBI product.

    • paymentMethod - Some incentives require certain payment methods to be used.

Example Offers Query Response

  • The id property in this response is the loyaltyEngineId that you may find in Sanity.

  • The sanityId property in this response is the _id that you may find in Sanity.

  • A type: "GLOBAL" offer refers to a Systemwide Offer.

"data": {
    "loyaltyOffersV2": [
      {
        "id": "a4c24211-2d36-47be-bad8-8deea437dbfa",
        "name": "$6 Let's Get This Bacon Meal",
        "type": "GLOBAL",
        "sanityId": "AAA-AAA-AAA",
        "errors": null
      },
      {
        "id": "fbba4e93-eecd-42b6-a4ed-333856d47bda",
        "name": "Delivery Offer - $8 The Spicy One Meal",
        "type": "GLOBAL",
        "sanityId": "BBB-BBB-BBB",
        "errors": null
      },
      {
        "id": "2a7b2f20-1662-4854-9f4b-e9974200f294",
        "name": "Support 50% Discount Offer",
        "type": "PERSONALIZED",
        "sanityId": "CCC-CCC-CCC",
        "errors": null
      },
      {
        "id": "6646b80b-dea9-4bf4-81f5-390554926987",
        "name": "Loyalty Upsize Swap",
        "errors": [
          {
            "code": "unsatisfied-cart-requirements",
            "ruleId": "cart-requirement"
          }
        ]
      }
    ]
  }
}

Example Rewards Query

query getLoyaltyRewards {
  loyaltyRewards(loyaltyId: "<USER_LOYALTY_ID>", where: { omitInvalids: false }) {
    id
    name
    sanityId
    errors {
      code
      ruleId
    }
  }
}
  • Same where filters from the offers query are available here.

Example Rewards Response

  • The id property in this response is the loyaltyEngineId that you may find in Sanity.

  • The sanityId property in this response is the _id that you may find in Sanity.

{
  "data": {
    "loyaltyRewardsV2": [
      {
        "id": "3b5bdddc-c7c9-42d8-9332-b0c3f36c8e1c",
        "name": "Value Powerade Zero",
        "sanityId": "DDDD-DDD-DDD",
        "errors": null
      },
      {
        "id": "728aaedd-3234-4eae-89af-35cd5672adcb",
        "name": "Large Dr. Pepper",
        "sanityId": "EEEE-EEE-EEE",
        "errors": null
      },
      {
        "id": "480122d7-86ff-4db0-965c-7dbb72bb5179",
        "name": "Value Soft Drinks",
        "sanityId": "FFFF-FFF-FFF",
        "errors": null
      }
    ]
  }
}

Kiosk Integration: Redeeming & Transactions

Identify (POST)

Use Case: Successfully identify the Loyalty user along with any potential rewards/offers that they had pre-selected within the app.

Endpoint URL: https://{reg}-{env}-{brand}-loyalty-middleware.rbictg.com/loyalty/identify

Expected Request Payload:

{
    "identifier": "$6digitCode or $loyaltyId",
     "posVendor": {
        "matchingId": "$smgCode",
        "operator": "$operatorId",
        "posType": "$posVendorName",
        "storeId": "$rbiStoreId",
        "supportingPos": "POS vendor name (optional field)", // vendor to return plu for
        "terminal": "$terminalNumber",
        "transactionId": "$posTransactionId"
    }
}

Expected Response Payload:

{
    "balances": [
        {
            "amount": 14464,
            "currency": "points"
        }
    ],
    "loyaltyUser": {
        "created": "2021-08-10T12:18:39.180Z",
        "id": "$loyaltyUserId",
        "name": "$loyaltyUserName"
    },
    "order": [
        {
            "incentiveId": "$rewardSanityId",
            "loyaltyEngineId": "$rewardLoyaltyEngineId",
            "price": 0,
            "productId": "$rewardPlu",
            "productType": "REWARD",
            "quantity": 1,
            "referenceId": "01" // unique line id
        },
        {
            "incentiveId": "$offerSanityId",
            "loyaltyEngineId": "$offerLoyaltyEngineId",
            "productId": "$offerPlu",
            "productType": "OFFER",
            "quantity": 1,
            "referenceId": "02" // unique line id
        }
    ],
    "transactionId": "$loyaltyTransactionId"
}

Transaction Validation (POST)

Use Case: Kiosk sends the Loyalty API a preliminary version of the user’s order so that the Loyalty Platform can evaluate the order and determine if the user meets eligibility requirements of any included incentives (Rewards or Offers). This request should be an exact mirror of what the POS expects to send when they actually claim the transaction.

Endpoint URL: https://{reg}-{env}-{brand}-loyalty-middleware.rbictg.com/loyalty/transaction/pos/validate/{loyaltyTransactionId}

Expected Request Payload:

{
    "channel": "Restaurant",
    "created": "2021-05-04T13:39:47Z",
    "loyaltyId": "$loyaltyUserId",
    "serviceMode": "$serviceMode",
    "status": "PENDING or CLAIMED",
    "transactionDetails": {
        "currency": "USD",
        "order": [
            {
                "name": "med whopper combo",
                "price": 5.00,
                "productId": "$plu",
                "productType": "combo",
                "quantity": 1,
                "referenceId": "1", // line id
                "tax": 0
            },
            {
                "name": "whopper",
                "parentReferenceId"?: "1", // line id of parent (for children elements)
                "price": 0,
                "productId": "$plu",
                "productType": "item",
                "quantity": 1,
                "referenceId": "2", // line id
                "tax": 0
            },
            {
                "name": "med fries",
                "parentReferenceId"?: "1", // line id of parent (for children elements)
                "price": 0,
                "productId": "$plu",
                "productType": "item",
                "quantity": 1,
                "referenceId": "3", // line id
                "tax": 0
            },
            {
                "incentiveId": "$sanityRewardId",
                "name": "free drink reward",
                "price": 0,
                "productId": "$rewardPlu",
                "productType": "REWARD",
                "quantity": 1,
                "referenceId": "4", // line id
                "tax": 0
            },
            {
                "incentiveId": "$sanityOfferId",
                "loyaltyEngineId": "$offerLoyaltyEngineId", // (Optional) Use this field for all Offers if supporting Personalized Offers
                "name": "$2.99 chicken sandwich meal",
                "price": 2.99,
                "productId": "$offerPlu",
                "productType": "OFFER",
                "quantity": 1,
                "referenceId": "5", // line id
                "tax": 0.50
            }
        ],
        "payments": [
            {
                "amount": 500,
                "type": "$paymentMethod",
                "ccToken"?: "$panToken"
            }
        ],
        "posVendor": {
            "matchingId": "$smgCode",
            "operator": "$operatorId",
            "posType": "$posVendorName",
            "storeId": "$rbiStoreId",
            "supportingPos": "POS vendor name (optional field)", // vendor to return plu for
            "terminal": "$terminalNumber",
            "transactionId": "$posTransactionId"
        }
    },
    "transactionId": "$loyaltyTransactionId"
}

Expected Response Payload:

{
    "loyaltyId": "$loyaltyUserId",
    "points": 14464,
    "pointsEarned": 50,
    "pointsRedeemed": 750,
    "transactionId": "$loyaltyTransactionId"
}

Expected Error Response:

{
  "code": "RulesError",
  "details": {
    "ruleEvaluation": [
      {
        "code": "insufficient-point-balance", // reason for rule failure
        "currentValue": 0, // what value was provided
        "message": "User does not have sufficient funds to redeem reward",
        "ruleId": "point-balance", // name of the rule
        "targetValue": 250 // what value is needed
      }
    ]
  },
  "message": "Rule evaluation failed"
}

 

Transaction Update (PUT) - Claim / Commit

Use Case: Kiosk sends this transaction request with the order details to update the loyalty transaction. If the Kiosk states this transaction should be claimed, this will result in the user earning and redeeming eligible points.

Endpoint URL: https://{reg}-{env}-{brand}-loyalty-middleware.rbictg.com/loyalty/transaction/pos/{loyaltyTransactionId}

Expected Request Payload:

{
    "channel": "Restaurant",
    "created": "2021-05-04T13:39:47Z",
    "loyaltyId": "$loyaltyUserId",
    "serviceMode": "$serviceMode",
    "status": "PENDING or CLAIMED",
    "transactionDetails": {
        "currency": "USD",
        "order": [
            {
                "name": "med whopper combo",
                "price": 5.00,
                "productId": "$plu",
                "productType": "combo",
                "quantity": 1,
                "referenceId": "1", // line id
                "tax": 0
            },
            {
                "name": "whopper",
                "parentReferenceId"?: "1", // line id of parent (for children elements)
                "price": 0,
                "productId": "$plu",
                "productType": "item",
                "quantity": 1,
                "referenceId": "2", // line id
                "tax": 0
            },
            {
                "name": "med fries",
                "parentReferenceId"?: "1", // line id of parent (for children elements)
                "price": 0,
                "productId": "$plu",
                "productType": "item",
                "quantity": 1,
                "referenceId": "3", // line id
                "tax": 0
            },
            {
                "incentiveId": "$sanityRewardId",
                "name": "free drink reward",
                "price": 0,
                "productId": "$rewardPlu",
                "productType": "REWARD",
                "quantity": 1,
                "referenceId": "4", // line id
                "tax": 0
            },
            {
                "incentiveId": "$sanityOfferReward",
                "loyaltyEngineId": "$offerLoyaltyEngineId", // (Optional) Use this field for all Offers if supporting Personalized Offers
                "name": "$2.99 chicken sandwich meal",
                "price": 2.99,
                "productId": "$offerPlu",
                "productType": "OFFER",
                "quantity": 1,
                "referenceId": "5", // line id
                "tax": 0.50
            }
        ],
        "payments": [
            {
                "amount": 500,
                "type": "$paymentMethod",
                "ccToken"?: "$panToken"
            }
        ],
        "posVendor": {
            "matchingId": "$smgCode",
            "operator": "$operatorId",
            "posType": "$posVendorName",
            "storeId": "$rbiStoreId",
            "supportingPos": "POS vendor name (optional field)", // vendor to return plu for
            "terminal": "$terminalNumber",
            "transactionId": "$posTransactionId"
        }
    },
    "transactionId": "$loyaltyTransactionId"
}

Expected Response Payload:

{
    "loyaltyId": "$loyaltyUserId",
    "points": 14464,
    "pointsEarned": 50,
    "pointsRedeemed": 750,
    "transactionId": "$loyaltyTransactionId"
}

 

Unclaimed Transaction (POST)

Use Case: Kiosk sends a request to Loyalty even when a user does not identify w/ the Loyalty program. This allows us to still create a transaction for the purpose of metrics, analytics, and potentially receipt code claiming

Endpoint URL: https://{reg}-{env}-{brand}-loyalty-middleware.rbictg.com/loyalty/transaction/pos

Expected Request Payload:

{
    "channel": "Restaurant",
    "created": "2021-05-04T13:39:47Z",
    "serviceMode": "$serviceMode",
    "status": "CLAIMED",
    "transactionDetails": {
        "currency": "USD",
        "order": [
            {
                "name": "some incentive name",
                "price": 5.00,
                "productId": "$incentivePlu",
                "productType": "offer",
                "quantity": 1,
                "referenceId": "1",
                "tax": 0
            },
            {
                "name": "med whopper combo",
                "price": 5.00,
                "productId": "$plu",
                "productType": "combo",
                "quantity": 1,
                "referenceId": "1",
                "tax": 0
            },
            {
                "name": "whopper",
                "parentReferenceId"?: "1",
                "price": 0,
                "productId": "$plu",
                "productType": "item",
                "quantity": 1,
                "referenceId": "2",
                "tax": 0
            },
            {
                "name": "med fries",
                "parentReferenceId"?: "1",
                "price": 0,
                "productId": "$plu",
                "productType": "item",
                "quantity": 1,
                "referenceId": "3",
                "tax": 0
            }
        ],
        "payments": [
            {
                "amount": 500,
                "type": "$paymentMethod",
                "ccToken"?: "$panToken"
            }
        ],
        "posVendor": {
            "matchingId": "$smgCode",
            "operator": "$operatorId",
            "posType": "$posVendorName",
            "storeId": "$rbiStoreId",
            "terminal": "$terminalNumber",
            "transactionId": "$posTransactionId"
        }
    }
}

Expected Response Payload:

{
  "presentation": {
    "footer": "receipt footer",
    "header": "receipt header",
    "marketing": "receipt marketing promo",
    "identifier": "Transaction ID: "$loyaltyTransactionId"
  },
  "transactionId": "$loyaltyTransactionId"
}

 

Void Transaction (PUT)

Use Case: Loyalty transaction needs to be voided/refunded.

Endpoint URL: https://{reg}-{env}-{brand}-loyalty-middleware.rbictg.com/loyalty/transaction/pos/{loyaltyTransactionId}/void

Order information does not need to be included in the Void Transaction Request

Expected Request Payload:

{
    "channel": "Restaurant",
    "created": "2021-05-04T13:39:47Z",
    "serviceMode": "$serviceMode",
    "status": "PENDING or CLAIMED",
    "transactionDetails": {
        "currency": "USD",
        "order": [
            {
                "name": "some incentive name",
                "price": 5.00,
                "productId": "$incentivePlu",
                "productType": "offer",
                "quantity": 1,
                "referenceId": "1",
                "tax": 0
            },
            {
                "name": "med whopper combo",
                "price": 5.00,
                "productId": "$plu",
                "productType": "combo",
                "quantity": 1,
                "referenceId": "1",
                "tax": 0
            },
            {
                "name": "whopper",
                "parentReferenceId"?: "1",
                "price": 0,
                "productId": "$plu",
                "productType": "item",
                "quantity": 1,
                "referenceId": "2",
                "tax": 0
            },
            {
                "name": "med fries",
                "parentReferenceId"?: "1",
                "price": 0,
                "productId": "$plu",
                "productType": "item",
                "quantity": 1,
                "referenceId": "3",
                "tax": 0
            }
        ],
        "payments": [
            {
                "amount": 500,
                "type": "$paymentMethod",
                "ccToken"?: "$panToken"
            }
        ],
        "posVendor": {
            "matchingId": "$smgCode",
            "operator": "$operatorId",
            "posType": "$posVendorName",
            "storeId": "$rbiStoreId",
            "terminal": "$terminalNumber",
            "transactionId": "$posTransactionId"
        }
    }
}

Expected Response Payload:

{
  "transactionId": "$loyaltyTransactionId"
}

 

  • No labels