We ran a hackathon with members of the Programmable Banking Community to see what exciting projects teams could build using the programmable banking tech in a short space of time.
In this demo, Michiel Kruger, Dean Lambrechts and Ron George walk us through how they built a solution with programmable banking that allows companies to better manage how company credit cards are used. Their solution integrates with Sage HR, and allows HR or payroll teams to customise their expenditure in various ways, set limits on cards and block unauthorised transactions. Check it out here!
Click here to download Michiel’s presentation slide deck.
Check out the repo here.
Transcript of the demo
The problem that Dean, Ron and I identified, and decided to tackle, was the management of company credit cards. It’s a very manual process whereby when an employee joins a company and there’s an application form that needs to go to the bank. On the application form, you’d have already specified the limits, and you would have selected the spend categories, which is a massive list as you all know. And this application form will go to the bank and a card will be returned.
Now, later on, you want to change the spend categories, or the card limits, and that process is quite a tedious process to go through. Actually, it’s a resubmission of the application form signed by company representatives, and it usually takes a long time – anything from two weeks to a month sometimes to get a turn around and get this activated on the cards.
And another big thing that we identified is the canceling of credit cards when an employee leaves the company. I’ve seen this a lot in audit, and more often than not, the role of managing the company credit card sits in finance, and when HR terminates somebody on the point of payroll system, or somebody leaves or resigns, they neglect to inform the person to cancel the credit card.
So there’s still an active credit card out there for the employee that doesn’t work there anymore. That’s one of the things. Then, the spending versus budget – this is always an afterthought. We receive credit card statements at the end of the month, it gets imported into the finance system and then you see somebody who already overspent.
So we want to actually be more proactive about this. And that’s where this all started. So yeah, that’s the problem statement. How it works then: What we’ve done is we’ve built a solution that integrates with SAGE HR. It will automatically download all the employees that have been captured on the payrolls or the HR system.
One of the main features is when an employee gets terminated by HR, it will automatically block all card transactions thanks to Investec’s programmable banking. So, that’s one of the things.
And then we’ve built a front end where you can set daily and monthly spend limits. This will basically put it in the hands of the company again to immediately raise a limit or lower a limit as needed. You can also set merchant groups and country groups. But we’ll dig into this in the demo, then we can get into a bit more detail there.
Our solution was built on Google Cloud Platform. We made use of Cloud Functions, Cloud Pub/Sub, and our backend is in Cloud Firestore. The frontend is a Firebase web app.
So I think let’s go into a demo. Let me just switch over to Firestore here. This is our backend for the collections and documents, and then all the fields within each document. We’ve got an ‘employees’ collection here that we want to demo first.
So what we’ll do is we’ll create a new employee on Sage. This is Sage HR – it’s a cloud-based payroll and HR solution. Let’s create a new employee. Say someone starts working here, we’ll just call it Demo001, work email, Demo001@dmr.co.za.
Let’s make his start date first of the month. We have to assign a position or say it’s without a position. Just quickly select everything here that we need to select for the employee.
As he’s saying there, this is linked to Sage itself, so these things can be populated by the Sage app.
Yeah. Once I click ‘Save’ here, you’ll see a new employee pops up. So now, Dean, can you kick off the cloud function that extracts the employees for us? At the moment, it’s not scheduled. We’re kicking it off manually to demo this. As you can see there, the new employee showed up. If we go into the employee, you’ll see all the details populated, Demo001.
Also, note, right at the bottom, we’ve got a field called “terminated”. That’s a field we populate ourselves based on some logic in the Sage data. And it’s showing false at the moment. So this is an active employee, and now are able to assign a card to this employee.
So we’ve built a frontend whereby we can assign cards. So now there’s a new employee – Demo001 – and we can select that employee, and then we can select all available cards.
OneCard is available – that is our simulation card ID that we have on here. We’ll select that card and assign it to this employee. Now the card is assigned. If we go back you’ll see there’s a new collection that … of another collection where we’ve linked this employee to that card now.
On the “manage” tab, we can set all the limits. So let’s select “employee” again. You can also have multiple cards for one employee. Sometimes there’s a fuel card and a credit card. So we’ll select the OneCard that’s assigned to this employee. The details for the employee also populates. And here we’ve got the merchant groups.
Since it’s a long list of merchant groups, we’ve created a grouping of merchants. So in our database, you’ll see there’s a merchant group, and the merchants collection is also here. From here, we’ve created, say for example, a manager role, and we’ve created a commuter role. You can assign multiple merchants to this role basically. It’s just to make it easier on the frontend to select multiple spend merchant groups.
So we’ll select “manager” for this one, and we’ll submit the changes. We do the country groups exactly the same way. We’ll assign this employee to SA only.
Sorry Michiel – just to explain there. So you may have an employee that goes to multiple countries and has a credit card that is enabled. You can take your clients out and buy dinner and do things, but only in a specific country.
At the moment, we only have two options, but you could actually expand that and allow an employee to use a card in multiple countries. Or you could say Southern Africa, and that will group multiple countries in this group and this person is allowed to use this card in all of the allowed countries. Or South Africa only if it is only South Africa, and he can only spend money locally.
What’s nice is that it can be customised for whatever the client would want to deploy this solution for, which is awesome.
So for this one, we’ll select SA only for now and apply the changes. The balances here would only populate if there are actually balances. It’s a new employee so there are no balances.
But here, at the bottom, we can set spend limits. Monthly and daily. So we’ll set R1,000 for this employee and R100 for a day. It is cents amounts so that’s why there are so many zeros.
And we can submit changes. As soon as we submit the changes, you’ll see it populates on the employee card collection. It will populate all the spend limits for the employee. I think now we can simulate the transaction. We got logged out here, so let me just quickly log back in.
We use cent amounts because that is what the programmable banking interface actually sends to us, it’s cent amounts. We’d like to change it to rands or whatever. But we do allow for a daily limit and a monthly limit.
If someone didn’t want a daily limit, they could set their daily limit to the monthly limit. Essentially, it allows for anyone to specify “do you want someone to spend only x amount per day, or do you want them to spend x amount for a month?” But we’ve given that flexibility where you can specify someone can only buy beer for R100 a day and R10,000 a month if you want.
If he’s added to that merchant category.
We are allowed to specify that bottle stores are not allowed. You can’t buy beer, but you can go to Pick ‘n’ Pay and buy wine and it’s fine.
Yeah, exactly. Don’t expose the loop holes now so early.
Exactly. Okay, so first thing, let’s post a transaction that would be allowed. It’s R90, it’s below the R100 daily limit, it’s the correct merchant and it’s also the correct country. So when you simulate this transaction, our cloud function that is called for the pre-transaction returns a response.
It returned the response “authorisation true”, which we pass back and then it says “transaction authorised”. This is just for login purposes so that we can also see on the Investec side why a transaction would be declined or not.
Let’s try to do another transection of R90. This also updated, let me just switch to our employee cards again. You’ll see the R90 was posted here now. Also, the limit is still R100 and there’s only R10 left to spend for the day. So if we want to spend R90 again, and we simulate a transaction –
This is on the same day. So we had a daily limit of 100 and now we have a second transaction of 90 on the same day.
So now the authorisation is “false” and the authorisation reason is “unsuccessful”. Transaction cents amount of 90 is greater than the daily balance cents amount of R10, or the monthly cents amount of R910. So this is for the spend category.
So let’s just change it back to the number that is allowed. We can spend R5, but let’s select another merchant category – say “Advertising Services”. This person is not supposed to be spending on this category so let’s simulate another transaction.
Again, false. This time, the authorisation reason is “Unsuccessful: Merchant not authorised”. So let’s just change it back to this merchant. This merchant is allowed, so if we post a transaction of R5 now it should go through. But let’s change the country to Angola and see if he can do a transaction there.
It returns the authorisation reason “false”, and then “Unsuccessful: Country not authorised”. So this is us in a nutshell when it comes to the solution, or the app.
Are there any questions at this point while we are still in the frontend and backend and everything here?
Are you going to show the termination as well?
Oh the termination, yes, let’s do a termination. Almost forgot about that one, which is one of the biggest ones actually. Let’s switch over to the employees here. And this Demo001, let’s say he resigned. He got a new job very quickly, but let’s make it as of yesterday he’s no longer working here and we terminate him on the payroll system.
Now you need to click off the cloud function again on your side, Dean. Now, the termination shows through here. It changed this field when we executed the function and if we try to do another allowable transaction –
The resolution of this is based on how often we would schedule the lookup on Sage HR. If you do it once a day at 6:00 am, and somebody’s terminated at 12:00 pm, it wouldn’t show up. But if we had an hourly check against the HR database, it would update this person’s termination status when it’s rung. I’m not sure if there’s a way to actually have a real time push from HR without it, but –
Unless there’s webhooks or something. In some cases, there are webhooks, which would be great. But HR systems are not that sophisticated as of yet, from what I’ve seen at least.
Okay, so let’s simulate the transaction that should be allowed based on all the other criteria, except for the fact that this employee has now in fact been terminated on the HR system. Now it returns the response “false” again, “Unsuccessful: Employee is terminated”.
And that is everything. We’ve got everything now.
So challenges we’ve encountered. The merchants, the json file that is on the Investec programmable banking platform and the country codes – they aren’t accessible through the API as of yet. I don’t know if that is a plan, but to deploy the solution, we had to manually import that into a collection on our side, on our backend as well as the cards.
If a company has lots of cards, it would be great if one can, in some way via API, or in some other way, just access the available cards and what the people’s names are that are associated with the cards.
In that way, it can be deployed to a client much quicker without manually capturing, and then capturing it when you load cards. That’s the only thing that we thought was a real challenge for us.
It would help if there was an API Core, or something that we could refresh, because when a new merchant comes along or a new country code, we’d want to be informed of the fact that these are new merchants. It would be nice if there was an API Core and we can exchange them and then refresh the list of allowable merchants.
Especially around COVID, a bottle store shouldn’t be an available merchant while there’s a big lockdown. Or a country for some reason that’s not allowed anymore – it would be nice if we could get an updated list of these things.
A list for one would be good, and then an updated list at whatever time. If there’s an API Core and we could refresh it on an hourly basis, or if you want the resolution to be by the minute, then every minute we can go and refresh this.
Especially cards that get changed frequently. I mean countries remain the same, but then there’re wars, so let’s see.
Future enhancements. There’s a couple of things that we thought of that we wanted to do, and we actually added a couple now as well, but the most important one for us is actually to be able to integrate with other payroll systems as well.
Not all systems have an open API that you can actually connect to. So some of this would probably be on-prem manual uploads as well. It is all possible with Google Cloud Platform. It is obviously just a little bit more development to do that, but that’s one of the things to make the solution more flexible.
We would like to add currencies to the validations. So spending in the US, but still being rands, or spending US dollars in South Africa, based on the location – we would like to build that in as part of the solution as well.
Another thing is to add temporary employee card spend logic to authorise once-off transactions and travel spend for specific periods. So say for example we’ve assigned the daily limit and we’ve assigned the monthly limit to an employee, but he has to go away immediately for two weeks to another country and temporarily spend in that country only for two weeks. We would like to add another tab on our frontend to be able to set limits on a temporary basis. And do exactly the same thing, just for a temporary, with the time limit basically.
Then, we’d like to add an interface to update merchants and country groups, like we discussed on the previous slide as well. Also, build in-spend reporting. We did the initial part of this whereby we send the transactions to be queried as well. So I think once it’s in  query, you can do all sorts of reporting on top of the data and bring that in, in the frontend as well.
So if you load a user’s profile, you can see his spend history and trends and all those kinds of things as well. Then, integrative accounting software and tools. So, if you can bring in another leg into this, that would also be amazing. And that’s where we’ll start using the OpenAPI to post transactions to the finance system.
I think that has been done before by someone else, and that’s one of the things we would want to add to this solution as well. And then also WhatsApp notifications on spend limits so that someone with a card is absolutely aware of how much he can still spend and what he spent up to now.
And then, Michiel, also reasons why his transaction was declined at a POS. Something that says, “you have exceeded your daily limit”, or “you’re not allowed to spend here”, or “Maverick’s is certainly not on cards”, or whatever it is. But just inform the person swiping his card why his transaction declined.
Also a contact number. I know I was using someone’s card blocking app to test the Hackathon and then I went to the shops and I forgot that it was blocked. And I was like ahhh.
Were you notified of why your transaction was blocked?
No, no one was notified. I was just blocked from spending.
We won’t ask what you spent it on, but okay … It would be nice if the person’s whose card it’s linked to is notified. Also, as a way of figuring out fraud. If somebody else is using your card in some dodgy place, you get notified immediately.
Or maybe the company would want to let him know that he was fired … true spending.
Just some good news. A lot of the work we’re doing with OneCard is to try and refine these declined codes, because it’s actually quite complex the amount of system that can decline you through this transaction, not just us. And trying to get some visibility into when you get declined, who is it, is it the POS or the acquisition banker – whatever, so it’s quite a pain.
But we are definitely doing some good work to try and get more clarity on why these things are declining sometimes. It was a bit of a shot in the dark sometimes. With all of the systems involved.
I suppose there are multiple systems that could decline you and there’s one of five possible intermediaries that could decline you for various reasons and you need to tell the person at the POS why his transaction was declined.
I think WhatsApp is a good tool to use as a medium of informing the person at the POS why their transaction was declined. And instead of he swiped R1,000 and now suddenly he is declined, you want to tell the person at the POS, “Oh, my boss said no.”
Get involved in the Programmable Banking Community
If you want to see more from what the community has been up to, you can: