Penny Saving Challenge using Starling Bank, Lambda and Node

Posted on Jan 8, 2020

I came across a money-saving Challenge called the ‘1p Challenge’. Basically you save an amount of money based on the day number in the year.

For example, on January the 1st, you would save 1p. Towards the end of the year, say day 365, you would save £3.65

As you can imagine, this starts saving small but eventually ramps up so you will save around £667 over the year.

The problem is, it’s incredibly hard to manage yourself. Finding the time or remembering to transfer funds into another account every day is a challenge. So, like most things in my life, I decided to automate it.

I recently moved over to Starling Bank. It’s an online-only bank which has an amazing app to control your money. It also allows access for developers to an API.

Inside the Starling App, there is a concept of ‘Spaces’. These are pots of money you can use to save for things (Holidays, Life Events etc). Basically, when you move money into a ‘Space’, it’s removed from your current balance, and added to the ‘Space’ - it’s like a separate savings account. This is perfect, as I can set up a ‘Space’ called the ‘Penny Challenge’ and move money into each day…

Now, people who know me closely will know when it comes to technology, I will automate anything that I don’t like doing (lazy)! So, this is how I automated saving money…

Setting up a Starling Bank Account

If you don’t have a Starling Bank, there are obviously many other banks out there. But I chose Starling due to them providing a usable API. I know Monzo and other banks also offer APIs, so feel free to shop around.

Once I set up the account (and started using it as my main current account), I set up a new space called ‘Penny Challengee’

Starling Developer Account

To access the Starling API, you’ll need a Developer Account. It’s a pretty straight forward signup process. Once you’ve signed up, make sure you link the Developer Account to your Starling Bank account (Settings -> Accounts)

Next, you’ll need to get a Personal Access Token. This is basically your authentication token (AccessToken) when accessing the API.

When choosing the permissions, make sure you select the following:

  • savings-goal-transfer:create

Creating the application

Luckily enough, the developers at Starling provide a nice Developer SDK for you to use out of the box - hosted on GitHub. This was a great place to start.

Using Node, you can easily get started with just the following:

const Starling = require('starling-developer-sdk');

const client = new Starling({
  accessToken: '<INSERT TOP SECRET TOKEN HERRE>',
});

client.account.getAccounts(); // Lists all available accounts.

After playing around for a while, seeing what information I could get from the API. I started to create something which would move money from my current balance into the ‘Penny Challenge Space’.

Here’s the method which performs that move.

client.savingsGoal.addMoneyToSavingsGoal(
    {
      accountUid,
      savingsGoalUid,
      transferUid,
      amount,
      currency: 'GBP',
    },
  )
    .then(({ data }) => console.log(data))
    .catch((err) => console.log(err));

You’ll notice there’s a few parameters which are required.

  • accountUid : The Account UUID (from .getAccounts())
  • savingsGoalUid : The Savings Goal UUID (from .getSavingsGoals())
  • transferUid : Randomly generated UUID (I used uuid package v4)
  • amount : The amount in minor units (1 = 1p, 100 = £1)
  • current : The currency your account is using.

Getting the current day and working out the amount to save

Ok great, using the Starling API I can move money into the ‘Penny Challenge Space’. Now, I need some logic to work out the amount which needs to move, based on the day of the year.

I spent more time than I want to admit creating a method which took the current date, compared it with the 1st January, and returned the number of days in between. It was horrible and hacky, before even mentioning it didn’t take into consideration the fact that it was a leap year… Who knew working with dates is so hard?

So why reinvent the wheel? It turns out momentjs have already figured this issue out and solved it! I’ll use their package…

const moment = require('moment');

const amount = moment().dayOfYear();

This returns a value between 1 - 366. This is perfect as I can use that value as the amount (minor unit). I’ll then pass this to the addMoneyToSavingsGoal() method.

Perfect! That works, it moves the amount into the correct SavingsGoal based on the day of the year. Now I need some way to trigger it to run each day automatically…

Lambda

I decided to use AWS Lambda to trigger the money transfer each day. I could have easily used something like a Cron Job to achieve this. But hey… AWS Provides a Free Tier, so I might as well use it!

Therefore, I created a new Lambda Function. You’ll need to package your node application into a ZIP file and upload it using the AWS-CLI. Here’s a nice guide on how to do that.

Make sure you wrap your code to export the event:

exports.handler = async (event) => {
// Code here
};

CloudWatch

To trigger the Lambda script, you will need to set up a CloudWatch Rule using a Schedule. I personally set a cron expression, but you can also use the ‘Fixed rate of’ setting. Both achieve the same outcome.

So that’s it! Every morning at 9 am, CloudWatch will trigger the Lambda script to move money into the ‘Penny Challenge Space’ in Starling based on the current day of the year. Job done!

Putting it all together

Here’s the code for the app.

Setup Node:

$ npm init
$ npm install starling-developer-sdk
$ npm install uuid
$ npm install moment
$ touch index.js

index.js

const Starling = require('starling-developer-sdk');
const uuidv1 = require('uuid/v4');
const moment = require('moment');

const accountUid = '<UUID for Account>';
const savingsGoalUid = '<UUID for Savings Goal>';

const client = new Starling({
  accessToken: '<TOP SECRET PERSONAL TOKEN>',
});

exports.handler = async (event) => {
  const amount = moment().dayOfYear();
  const transferUid = uuidv1();

  return client.savingsGoal.addMoneyToSavingsGoal(
    {
      accountUid,
      savingsGoalUid,
      transferUid,
      amount,
      currency: 'GBP',
    },
  )
    .then(({ data }) => ({
      statusCode: 200,
      body: JSON.stringify(data),
    }))
    .catch((err) => ({
      statusCode: 500,
      body: JSON.stringify(err),
    }));
};

ZIP & Deploy to AWS Lambda

$ zip functionName.zip .
$ aws lambda update-function-code --function-name MyLambdaFunction --zip-file fileb://functionName.zip

As always, my blog posts are just brain dumps of things I’ve been working on in my spare time. But I hope this is useful for those looking to achieve the same thing.

I have to give credit to Andrew Barber (a fellow Scouter) - as I was scrolling through his GitLab when I came across this ‘1p Challenge’ - He’s written his own version which uses a cron job! We both seem to have gone about solving the problem in a similar way (using moment).

Thanks,

Dano!