September 20, 2022
Product
5
 min read

How we built seamless rewards redemption

Harry Ford
Harry Ford
Senior Backend Engineer

Tap and go

We built Yonder’s rewards program with one non-negotiable principle; that redeeming a reward should be remarkably easy. Just a bit better than our competitors wasn’t enough, we had to be miles ahead.

That means no coupons, no codes, no ‘activating’ rewards and no painful web experiences. Yonder rewards had to be the most intuitive and seamless credit card rewards program in the world. Delivering on that principle is really, really hard, which is why no one has done it before. We had our work cut out for us.

On top of that, we had really high expectations for how our internal teams should be able to manage and deliver our complex series of rewards benefits, called Local Experiences, to our customers. Our team needed to be able to create rich content, deliver that across our apps and content channels, and be able to make changes to a Local Experience at any time.

Here I’ll break down how we got to where we are as accurately as I can, relaying our thinking at the time, and how we learnt from each iteration of Local Experiences to arrive at the way we do it now.

Matching experiences

Every month, Yonder curate a handful of dining experiences from around London and let our members use their points for free meals or drinks (up to a fair use limit) or earn extra points just for dining there.

We had a big vision that just tapping your Yonder card at a Local Experience partner would be enough to redeem your points, and that meant being able to identify a partner instantly when a transaction was made.

V1 – Merchant Description Matching

We knew we'd have to match data coming through from our card network by looking to uniquely identify our experience merchants. Unfortunately, there's no silver bullet magic ID that tells you exactly which merchant a transaction has been made at and the location of the merchant (if only).

When you make a transaction, Mastercard send us a load of information about that transaction and within that there were some options for us identify a merchant partner. We started off with an exact match of the merchant description and its postcode to match the transaction data to what we had saved, and we were confident it must be a genuine match. However, some experience partners had multiple locations, and with a small team unable to test every location of every experience, we only realised that merchants tend to be quite liberal with the information they send back once we had more users to try out all the branches for us. We found that we were not redeeming every experience because sometimes the merchant would send a very slightly different description for their Soho or King's Cross restaurant, and sometimes even different terminals within the same branch of a restaurant would send back different data.

V2 – Adding MCC and the Levenshtein distance

We quickly added an MCC (merchant category code) to the mix and started using the Levenshtein distance of the raw description from the card network, as well as clean description we got from our transaction enrichment partners compared to the description we were expecting from the merchant. The Levenshtein distance calculates how close a string is to another string, so Crust Bros and Crust Brothers would record a high score where Crust Bros and Pizza hut would not (the Hut won't be featuring as an experience anytime soon). To calculate the right number for the Levenshtein we used test driven development with various test cases of real merchant data that had come through. When we were expecting a match we looked at the numbers that were coming through compared to our expected data.

Here’s a test:

This approach meant we tended to catch when merchants had different data at different terminals and we even had an extra piece of data we could match on to ensure it was, in fact, an experience the user was visiting. There was only one problem with this: we no longer required a postcode to match. This meant we occasionally got matches with MCC and a close description that weren't actual experiences. MCCs are significantly less unique than postcodes; that is, most restaurants send back an MCC of 5812 or 5813. We were therefore occasionally catching restaurants that had similar names to our experiences, but weren't actual experiences that we offered, as they matched on the MCC and description.

When we got false positives for experiences we added in these test cases. After a bit of tweaking we came to a better number for the distance we considered a match for postcode, MCC and when we had both pieces of information.

V3 – How we match now

We knew we needed more information we could match on so we turned to a few different IDs: the merchant ID, terminal ID and sub-merchant ID. We knew these were sometimes duplicated or would change for some merchants (for example if a merchant was using a curve terminal or if they got a new card terminal). We use these three IDs along with the description, postcode and MCC to match experiences by giving them a score. And this If the transaction meets the threshold we've set, we would consider it a match. With this implemented, we have matched 99% of Local Experiences, and not yet had an incorrect redemption at an experience we don't offer. In the rare case that we don't catch one, we add the data that we missed the first time to ensure we don't miss it again.

Again we pursued test driven development to ensure we had appropriate scoring for each of the different fields. For example we new postcode was a more unique field than MCC, it was given a score of 0.5 for a match instead of MCC which was given 0.1.

Here’s a test for the updated version of the matching:

Yonder Treats are a series of smaller experiences, like a coffee or pint, and we typically have around 40-50 locations for each treat (for example, we had over 50 pubs for Yonder Pints). Each location needs matching independently, which provided an additional challenge, as we needed separate matching records for each location of an experience, and then to find the closest match for each transaction to determine whether it was an experience or not. There's also a lot of data we need to manually find so we can match every different location accurately.

Making Local Experiences self-serve

On top of all the above, we had to ensure that no engineering work required to set up new experiences. The nature of our rewards program means it’s changing often, so an engineering bottleneck significantly impacts the customer value if we can’t move quickly.

It starts with our team settling on the rich content, the redemption value and fair use limit for each Local Experience partner. We use a CMS to house our experiences content, but also found it useful for containing the matching information. Once we’ve added a new experience, Dato sends a webhook and the experience gets added to the backend database.

We also need to be able to see the experiences before they go live to check they look right and test the matching works, so we have a couple of options that change who sees experiences and, more importantly, who can redeem them. Through our CMS, we can control the status of experiences, who can see them (staff or members), and schedule experiences to go live in the future at a date we set. This gives us flexibility to test Experiences in the app before a member sees them. In the future we’d love to add a testing group of customers who can help us test experience redemption before it goes live to our wider customer base. The CMS gives us the freedom to do all of this easily.

Harry Ford
Harry Ford
Senior Backend Engineer

Becoming a Yonder member could 

improve

your credit rating

Check you’re eligible before applying
You can see if you’re eligible without affecting your credit score. If you continue, we’ll carry out a full credit check.
Borrow what you can afford
When you use a credit card, you need to pay off your balance at the end of the month. Only spend what you can afford to repay.
Improve your credit over time
Making your monthly payment on time could help improve your credit rating. Just like missing a payment can impact it.

Become a member today

Become a full Yonder member
£15/mo, cancel anytime
new member offer
Get 1 month free membership
Earn 5 points per £1 spent
Worldwide travel insurance
No fees abroad
Stunning premium card (3 month min)
Plus 10,000 bonus points
Worth £50 at Yonder Experiences
REPRESENTATIVE example
Purchase rate
29.97% (var)
Representative
66.7% APR (var)
WTF is APR?!
Based on a
£1,200 limit
Learn more
Or try our free membership
£0/mo, no commitment
Same Yonder, just fewer features
Apply to upgrade anytime
Earn 1 point per £1 spent
Worldwide travel insurance
No fees abroad
Slick plastic card
Plus 10,000 bonus points
Worth £50 at Yonder Experiences
REPRESENTATIVE example
Purchase rate
32.9% (var)
Representative
32.9% APR (var)
WTF is APR?!
Based on a
£1,000 limit
Learn more
Become a full Yonder member
£15/mo, cancel anytime
new member offer
Get 1 month free membership
Earn 5 points per £1 spent
Worldwide travel insurance
No fees abroad
Stunning premium card (3 month min)
Plus 10,000 bonus points
Worth £50 at Yonder Experiences
REPRESENTATIVE example
Purchase rate
29.94% (var)
Representative
66.7% APR (var)
WTF is APR?!
Based on a
£1,200 limit
Learn more
Or try our free membership
£0/mo, no commitment
Same Yonder, just fewer features
Apply to upgrade anytime
Earn 1 point per £1 spent
Worldwide travel insurance
No fees abroad
Slick plastic card
Plus 10,000 bonus points
Worth £50 at Yonder Experiences
REPRESENTATIVE example
Purchase rate
32.9% (var)
Representative
32.9% APR (var)
WTF is APR?!
Based on a
£1,200 limit
Learn more

See if you’re eligible

No impact on your credit score
Takes less than 60 seconds
Check my eligibility
By continuing, you agree to our App Terms & Privacy Policy
Nearly there, {Name}

You’re eligible for Yonder membership!

Finish your application and get your card