Let's continue...
In this part, we’re going to establish the actual rules, as well as start doing some testing to see that points gets added when expected etc.
In Part 1 we established a simple middleware using Next.Js – and that’s going to handle the Loyalty Program Rules we want to establish. OrderCloud will be used for all the commerce related actions, as well as storage for the points collected and used for each user.
Let’s start by looking at the main feature we will use in OrderCloud:
Spending Accounts
Spending accounts can be used for a lot of things. I our case we will use it to manage a user’s Loyalty Program Account. Find the details about spending accounts here.
There’s, as usual, a number of endpoints for managing these spending accounts in OrderCloud. What’s important for our use case are:
- POST: https://sandboxapi.ordercloud.io/v1/buyers/solitary-storefront/spendingaccounts
- This endpoint will be used to create spending accounts for each user, and at the same time use the setting where a spending account can be used as a payment method. When a user signs up for the Loyalty Program, we’ll use this endpoint to create a spending account for that user, with a balance of $0, or if we reward the user for signing up for the loyalty program, we can add this right here.
{
"ID": "00000000VUK9lbhSmlgcaQ",
"Name": "testuser01sa",
"Balance": 0,
"AllowAsPaymentMethod": true,
"StartDate": "2024-06-28T19:00:00+00:00",
"EndDate": "2025-08-30T19:00:00+00:00",
"xp": null
}
- POST: https://sandboxapi.ordercloud.io/v1/buyers/[buyerID]/spendingaccounts/assignments
- Used to assign the created account to a specific user. Example:
{
"SpendingAccountID": "00000000VUK9lbhSmlgcaQ",
"UserID": "00000000p0SJ0t0ubCKQjA"
}
- GET: https://sandboxapi.ordercloud.io/v1/me/spendingAccounts
- This endpoint will show what’s on the spending account for the current user (“/me/”), if there’s a spending account for the user that is. Example:
{
"Meta": {
"Page": 1,
"PageSize": 20,
"TotalCount": 1,
"TotalPages": 1,
"ItemRange": [
1,
1
],
"NextPageKey": null
},
"Items": [
{
"ID": "00000000VUK9lbhSmlgcaQ",
"Name": "testuser01sa",
"Balance": 0,
"AllowAsPaymentMethod": true,
"StartDate": "2024-06-28T19:00:00+00:00",
"EndDate": "2025-08-30T19:00:00+00:00",
"xp": null
}
]
}
- PATCH: https://sandboxapi.ordercloud.io/v1/buyers/solitary-storefront/spendingaccounts/[spendingAccountId]
-This endpoint will be used to update a user’s spending account, which in our context will be the actual Loyalty Program “points”:
{
"Balance": 0.2
}
With the above, we have all we need to implement the rules for our simple Loyalty Program.
One point though: With the above, we will not have persisted all actual transactions on a user’s Loyalty Program account. We will only have the actual amount that exist on a user’s account.
For an actual implementation, it would be expected by the users to be able to see all the transactions that added points to the accounts as well as transaction where the points have been used. For our small POC here, we will leave that out.
Implementing the rules
Rule 1: Join Reward - To sign up for our Loyalty Program a customer will get 500 points.
Rule 2: Purchase Reward - The customer earns 10 x the amount in $
As the spending account we created has a “balance” that can be used as actual “Dollars”, we simply times the balance number by 10. If the users has 50 points, the actual balance in OrderCloud will show 5.
Implementing rule 1: Join Reward
We will assume that when a spending account gets assigned to a user, that means that the user has accepted the terms, and “signed” up for the loyalty program. Using OrderCloud Webhooks we can get notified when this happens. Spending accounts gets created, and then the spending account gets assigned to the user. Two calls, we will only look for the assignment call.
We start by creating a webhook for when a spending account gets assigned:
Endpoint: /webhooks [POST]
{
"ID": "00000000thO0xFKbqk2G3igqbo5zyg",
"Name": "SpendingAccountAssigned",
"Description": "Triggers when a new Spending account get's assigned to a user",
"Url": "https://moose-chief-hippo.ngrok.app/api/loyalty/spendingaccountassigned",
"HashKey": "00000000352CF58A18C65BE52EC3C",
"ElevatedRoles": null,
"ConfigData": [],
"BeforeProcessRequest": false,
"ApiClientIDs": [
"00000000-edff-4c76-925c-2a81c1f86d33",
"00000000-34fc-4a22-9fe1-5857eb986f86",
"00000000-263c-4f24-b98d-90815380bd7b"
],
"WebhookRoutes": [
{
"Route": "v1/buyers/{buyerID}/spendingaccounts/assignments",
"Verb": "POST"
}
],
"DeliveryConfigID": null
}
Next we need to have an endpoint in our middleware that can catch this webhook.
Checkout the simple endpoint/route in my repository here: https://github.com/alpharv/ordercloud-customer-loyalty and take a look at the file here:https://github.com/alpharv/ordercloud-customer-loyalty/blob/master/app/api/loyalty/spendingaccountassigned/route.ts
To test out if the webhooks triggers we need to first create a spending account and then assign it to a user. To do that we simply use the endpoint listed in the beginning of this post. It’s easy to do it from OrderCloud console:
Endpoint: /v1/buyers/solitary-storefront/spendingaccounts
{
"ID": "solitary-storefront-testuser01",
"Name": "solitary-storefront-testuser01-sa",
"Balance": 5,
"AllowAsPaymentMethod": true,
"StartDate": "2024-09-15T22:40",
"EndDate": "2032-05-27T14:45"
}
Result:
{
"ID": "solitary-storefront-testuser01",
"Name": "solitary-storefront-testuser01-sa",
"Balance": 5,
"AllowAsPaymentMethod": true,
"StartDate": "2024-09-15T22:40:00+00:00",
"EndDate": "2032-05-27T14:45:00+00:00",
"xp": null
}
We also need to assign the spendingaccount to the user:
Endpoint: /v1/buyers/solitary-storefront/spendingaccounts/assignments
{
"SpendingAccountID": "00000000aEO94GgsqKjurA",
"UserID": "00000000p0SJ0t0ubCKQjA"
}
NOTE: Remember that for the webhooks to work, you have to perform the request through an API Client that’s assigned to the webhook.
Testing it out shows that the webhooks fire as expected, and we get our middleware triggered with a request body like:
"Body": {
"SpendingAccountID": "00000000aEO94GgsqKjurA",
"UserID": "00000000p0SJ0t0ubCKQjA"
}
This is basically just a passthrough, but it does identify the user. At this point we don’t actually need to do anything with the spending account. When the spending account was created it did already did get 5$ assigned. But our endpoint can be used for sending email or text welcoming the user to the Loyalty program – or other relevant actions.
Rule 2: Purchase Reward - The customer earns 100 x the amount in $
Our second rule is a little more complicated to test, as we need to have the user place orders. The task and rules are straightforward though:
- Get info from OrderCloud when an order is submitted
- Get the actual amount paid by the user (we do not give loyalty points for promotions or discounts, coupon etc.).
- Calculate the earned points, and add it the to users spending account.
First step is again to setup the Webhooks as we did with Rule 1:
{
"ID": "00000000_EyBPsI9qAlJjw",
"Name": "OrderSubmit",
"Description": "",
"Url": "https://moose-chief-hippo.ngrok.app/api/loyalty/ordersubmitted",
"HashKey": "fbece22386d0a25be134dbf6ef5c2dfaaec9e41d",
"ElevatedRoles": null,
"ConfigData": [],
"BeforeProcessRequest": false,
"ApiClientIDs": [
"00000000-263c-4f24-b98d-90815380bd7b"
],
"WebhookRoutes": [
{
"Route": "v1/orders/{direction}/{orderID}/submit",
"Verb": "POST"
}
A new API endpoint is needed – check the repository mentioned above for details. Before we start adding logic to our endpoint, we want to test that it gets triggered when a user submits an order.
There, as usual good help by looking at the documentation: https://ordercloud.io/learn/buyer-perspective/creating-your-first-order.
We will do this from the OrderCloud console. Remember to impersonate as one of the test users:
Now follow the steps in the documentation on creating your first order:
- Create an empty order (Note the ID: "ID": "_IZZ7wAR1EaFBw94niKsVA")
- Add a line item (e.g. "ID": "F3v5Ls-PCkKqKdn-QpSQYQ")
- Submit the order using the/v1/orders/Outgoing/[OrderID]/submit endpoint.
- We need to call the OrderCalculate first. This is required. See https://ordercloud.io/knowledge-base/order-checkout-integration for a full detailed explanation. This endpoint can’t be triggered from the OrderCloud portal, and is supposed to be called from the middleware as part of the checkout flow.
- Now let’s call this OrderCalculate endpoint
- OrderSubmitted
- Get the Order Total
- Calculate the earned point by rounding the Order Total up and divide it by 10. This is because we’re using the spending accounts, that can be set to be used as actual payments. We have e.g. 100 point equals $10.
- Add this to the spending account for this user account. A user can have any number of spending accounts, but we named it in a specific way so we can identify it.
- Here you will likely see this message:
{
"Errors": [
{
"ErrorCode": "Order.CannotSubmitUncalculatedOrder",
"Message": "Cannot submit an order that has not called the OrderCalculate step of the checkout integration.",
"Data": null
}
]
}
For the purpose of this tiny app, we create a “dummy” endpoint in our middleware that we can trigger from a Url like this: http://localhost:3000/api/checkout/triggercalculate
In short, we use the OrderCloud jsdk and simply use the following method:
const recalculate = await IntegrationEvents.Calculate("All", "_IZZ7wAR1EaFBw94niKsVA",
{
accessToken: accessToken,
}).catch((error)=>{console.log(error)});
Check out this full code in the repository.
Note that for this to work, you do need to have an integration event configured on the API client where you perform the above call – if not you’ll get a message/error message about that.
Again, another API endpoint has to be created to handle the integration event call from OrderCloud and point it to our middleware. This can look like this:
{
"ElevatedRoles": [
"FullAccess"
],
"ID": "checkout-commerce-integrations",
"ConfigData": null,
"EventType": "OrderCheckout",
"CustomImplementationUrl": "https://moose-chief-hippo.ngrok.app/api/checkout",
"Name": "Checkout Commerce Integrations",
"HashKey": "00000000n1oSdEbGmUdA1UyNHI6I"
}
Read more about integration events here: https://ordercloud.io/knowledge-base/order-checkout-integration.
Calling our fake/trigger endpoint like:
http://localhost:3000/api/checkout/triggercalculate?orderid=_IZZ7wAR1EaFBw94niKsVA makes our middleware call OrderCloud to trigger “Calculate”. This will make OrderCloud trigger IntegrationEvents where we have configured a call back to our middleware on the url:
/api/checkout/ordercalculate
Now, we don’t do anything for this small app, but this would be where tax etc. would be calculated on the order.
Now, we can finally again run our “SubmitOrder” and it will work fine now and the property IsSubmitted will be set to ‘true’.
Run it from the OrderCloud portal using:
https://sandboxapi.ordercloud.io/v1/orders/Outgoing/_IZZ7wAR1EaFBw94niKsVA/submit
The call to ordersumitted will also trigger our Webhook and call back to our middleware on the configured URL: /api/loyalty/ordersubmitted
This is where we’re going to calculate the points earned by the customer and add this to their “loyalty account”.
For simplicity we’re just using the “Total” on the “OrderRequest” we get back from the Webhook request from OrderCloud. We formula is simple:
Testing this is done by submitting the order from the OrderCloud portal with:
https://sandboxapi.ordercloud.io/v1/orders/Outgoing/_IZZ7wAR1EaFBw94niKsVA/submit
Resulting in the following in our console log of our middleware:
The “35” is there as I called it twice 😊
Looking at this users Spending Account for the loyalty it looks like this:
{
"ID": "solitary-storefront-testuser01",
"Name": "solitary-storefront-testuser01-sa",
"Balance": 35,
"AllowAsPaymentMethod": true,
"StartDate": "2024-09-15T22:40:00+00:00",
"EndDate": "2032-05-27T14:45:00+00:00",
"xp": null
}
Summary
This ended up being a longer work through than I intended, but as I hope it clear – this is really flexible and easy to work with. I know that Loyalty system and loyalty programs can be highly complex – but it’s clear to most people that’s been through loyalty programs – simple makes it better. That’s for both customers ant the business that offers loyalty programs. Let’s be honest – have you have worked with loytalty systems like Oracles CrowdTwist and Unity ? Did you ever calculate your ROI? No – that’s what I thought 😊
With OrderCloud and some customizations it’s already there, with not extra cost. Just saying.
Please reach out to me for more details on this, and whatever challenge you have with your current loyalty program.
OrderCloud & Loyalty Programs
It's not obvious to build a loyalty program using OrderCloud.
What I want to show with these articles are that OrderCloud is extremely flexible for a lot of VERY different use cases - and that is exactly what makes OrderCloud such a strong product.
Secondly, I have also seen customers investing in expensive Loyalty Software, that's extremely rigid, batch oriented (who said Oracle Unity and CrowdTwist?) - costing them a fortune in development and consultancy - and still not getting to where they wanted to be.
It might be wise - to look at what software you already have - and see what you can build with that. If your systems are as flexible as OrderCloud, you can get far - really really far - with an impressive ROI.
ROLAND VILLEMOES
CTO