Automatically top up your tru.ID balance

In this tutorial, you'll learn how to detect and set up automatic topping up your balance with tru.ID's Payment API.
Greg HolmesDeveloper Experience and Relations
Last updated: 12 May 2023
tutorial cover image

Managing the balances for the various APIs you're using can often be a lot to keep on top of, with a high chance of missing out on a payment, resulting in critical parts of your service being disrupted.

You can take topping up your tru.ID balance into your own hands based on your usage, utilizing the tru.ID Payments API. The API enables you to top up automatically when you need it — ensuring there’s no disruption to the verification flow for those using your application.

Before you begin

To follow along with this tutorial, you'll need the following:

  • A tru.ID Account
  • Node.js installed locally on your computer
  • A mobile phone with an active data SIM card

Getting started

A tru.ID Account is needed to make the SIMCheck API requests, so make sure you've created one.You're also going to need some Project credentials from tru.ID to make API calls. So sign up for a tru.ID account, which comes with some free credit. We've built a CLI for you to manage your tru.ID account, projects, and credentials within your Terminal. To install the tru.ID CLI run the following command:
npm install -g @tru_id/cli
Run tru login <YOUR_IDENTITY_PROVIDER> (this is one of google, github, or microsoft) using the Identity Provider you used when signing up. This command will open a new browser window and ask you to confirm your login. A successful login will show something similar to the below:
Success. Tokens were written to /Users/user/.config/@tru_id/cli/config.json. You can now close the browser
Note: The config.json file contains some information you won't need to modify. This config file includes your Workspace Data Residency (EU, IN, US), your Workspace ID, and token information such as your scope.Create a new tru.ID project within the root directory with the following command:
tru projects:create automated-topups --project-dir .

Make a note of these tru.ID credentials because you'll need them later in the tutorial.

In your Terminal, clone the starter-files branch with the following command:

git clone -b starter-files git@github.com:tru-ID/payment-api-auto-topup-tutorial.git

If you're only interested in the finished code in main, then run:

git clone -b main git@github.com:tru-ID/payment-api-auto-topup-tutorial.git

Now, copy the .env.example file within your project directory to a new file called .env and then populate the TRU_PROJECT_CLIENT_ID and TRU_PROJECT_SECRET with your tru.ID Client ID and Secret.

If your registered data residency isn't EU, then be sure to change that part of the TRU_BASE_URL. This can be EU, IN, or US.

The starter-files branch already contains some third-party libraries needed to get you started. So in your Terminal, within the project directory, run the following command to install these third-party libraries:

npm install

Next, to run the web server, run the following command in your Terminal in the project directory:

npm start

Once you've started the server, it'll show output similar to what you see in the example below:

[nodemon] 2.0.16
[nodemon] to restart at any time, enter `rs`
[nodemon] watching path(s): *.*
[nodemon] watching extensions: js,mjs,json
[nodemon] starting `node index.js`
Example app listening at http://localhost:8080

Test SIM Check

With the starter-files branch checked out, you can now create a POST request to your web server and the path /sim-check containing a JSON body for your phone_number. Assuming you have credit and your credentials are correct, you'll receive a JSON object response similar to what's shown in the API specifications page.

You can see a sample CURL POST request below:

curl --location 'localhost:8080/sim-check' \\
--header 'Content-Type: application/json' \\
--data '{
"phone_number": "447500000001"
}'

This CURL request to your webserver results in your webserver making a POST request to tru.ID's /sim-check/v0.1/checks endpoint, and — depending on the result — returning the valid response for your webserver to handle appropriately.

Retrieve Payment Methods from tru.ID

To begin with, to retrieve payment methods and create top-ups, you'll need the Workspace credentials and the Workspace ID. These are available on your Settings page in the tru.ID console. Please note these are different from your project credentials.

Update your .env file to contain the following five:

TRU_WORKSPACE_ID=
TRU_WORKSPACE_CLIENT_ID=
TRU_WORKSPACE_SECRET=
TRU_TOP_UP_CURRENCY= # The currency you're paying in
TRU_TOP_UP_AMOUNT= # How much you would like to be automatically topped up

Now that you have these saved, you can make API requests to the Payments API. Open the src/api/tru.js file, and below your imports, add the following:

const WORKSPACE_TOKEN = {
accessToken: undefined,
expiresAt: undefined,
}

This constant will contain your up-to-date workspace access token, which will get refreshed if it expires.

Now it's time to generate/retrieve your Workspace Access Token. Find the method called getAccessToken(); below this method, add a new one called getWorkspaceAccessToken(). This new method will contain the functionality that creates your application a new workspace token, or refreshes an existing one that has a lapsed expiry date.

async function getWorkspaceAccessToken() {
// check if existing valid token
if (WORKSPACE_TOKEN.accessToken !== undefined && WORKSPACE_TOKEN.expiresAt !== undefined) {
// we already have an access token let's check if it's not expired
// by removing 1 minute because access token is about to expire so it's better refresh anyway
if (
moment()
.add(1, "minute")
.isBefore(moment(new Date(WORKSPACE_TOKEN.expiresAt)))
) {
// token not expired
return WORKSPACE_TOKEN.accessToken;
}
}
const accessToken = await getAccessToken(
process.env.TRU_WORKSPACE_CLIENT_ID,
process.env.TRU_WORKSPACE_SECRET,
"console"
)
// update token cache in memory
WORKSPACE_TOKEN.accessToken = accessToken.access_token;
WORKSPACE_TOKEN.expiresAt = moment().add(accessToken.expires_in, "seconds").toString();
return accessToken.access_token;
}

The tru.js API file contains all the API requests to tru.ID. So, in this same file, you will need to request to retrieve all payment records tru.ID has for your workspace.

Create a new method called getPaymentMethods(), which will first get your active Workspace AccessToken; it will then make a GET request to /console/v0.2/workspaces/{YOUR_WORKSPACE_ID}/payment_methods. The end of this method will be to handle the response appropriately.

async function getPaymentMethods() {
const accessToken = await getWorkspaceAccessToken()
const paymentMethodsResponse = await fetch(
`${process.env.TRU_BASE_URL}/console/v0.2/workspaces/${process.env.TRU_WORKSPACE_ID}/payment_methods`,
{
headers: {
Authorization: `Bearer ${accessToken}`,
},
},
)
if (paymentMethodsResponse.status === 200) {
const data = await paymentMethodsResponse.json()
return data._embedded.payment_methods
}
const error = await paymentMethodsResponse.json()
console.log(`Error attempting to retrieve payment methods: ${ error}`)
return false
}

In your module.exports, add the getPaymentMethods method as a new line, as shown below, to be accessible elsewhere in your project:

module.exports = {
createSIMCheck,
getPaymentMethods
};

In the src/, create a new directory called utils; then, within this new directory, create a new file called payments.js. This new file will handle any payment-related actions.

At the top of this new file, add the import for the tru.js API file:

const truAPI = require('../api/tru')

Now create a new asynchronous function called getPaymentMethod() as shown below:

async function getPaymentMethod() {
}

The first step within this function is to get the payment methods from the API by calling truAPI.getPaymentMethods(). You then need to make sure payment methods are returned in the response. So within this function, add the following:

const paymentMethods = await truAPI.getPaymentMethods()
if (paymentMethods === false) {
console.log("No available payment methods. Please add a payment method.")
return false
}
if (paymentMethods.length === 0) {
console.log("No available payment methods. Please add a payment method.")
return false
}

Next, you'll need to check whether the payment methods returned are active based on their expiry date. So add the following:

const currentMonth = new Date().getMonth() + 1
const currentYear = new Date().getFullYear()
const availablePaymentMethods = []
paymentMethods.forEach(paymentMethod => {
if (paymentMethod.expiry_year === currentYear && paymentMethod.expiry_month >= currentMonth) {
availablePaymentMethods.push(paymentMethod)
} else if (paymentMethod.expiry_year >= currentYear) {
availablePaymentMethods.push(paymentMethod)
}
})

Finally, within this function, return the result:

if (availablePaymentMethods.length === 0) {
console.log("No available payment methods. Please add an up to date payment method.")
return false
}
return availablePaymentMethods

At the bottom of the file, add the following export:

module.exports = {
getPaymentMethod
}

Create a Top-Up

Now that you have the functionality to retrieve payment methods, you'll need to trigger the TopUp API request. So, first in src/api/tru.js, create a new method called: createTopUp() with three arguments:

  • paymentMethodId: the ID linking that payment method in tru.ID to charge,
  • currency: the currency the top-up request will be made in, for example EUR for Euros,
  • amount: the amount you'd like to top up with.
async function createTopUp(paymentMethodId, currency, amount) {
}

The first thing this method will need to do is retrieve the workspace access token, so add the following:

const accessToken = await getWorkspaceAccessToken()

Now you'll need to make the API request to tru.id's APIs. This request will make a POST request to /console/v0.2/workspaces/{YOUR_WORKSPACE_ID}/topups. The headers will contain your access token, and the body a JSON object of the three parameters passed into the method.

Add the following to your function:

const topUpResponse = await fetch(`${process.env.TRU_BASE_URL}/console/v0.2/workspaces/${process.env.TRU_WORKSPACE_ID}/topups`, {
method: 'POST',
headers: {
Authorization: `Bearer ${accessToken}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
payment_method_id: paymentMethodId,
currency,
amount
}),
});
return topUpResponse

Now add createTopUp to your exports at the bottom of the file, as shown here:

module.exports = {
createSIMCheck,
getPaymentMethods,
createTopUp
};

Back within your src/utils/payments.js file, create a new method called topup. This new function will take two arguments, currency and amount.

The new function will call your newly createTopUp API function and handle the response appropriately. So in the payments.js file, add:

async function topup(currency, amount) {
}

The first thing this function does is to get the payment methods by calling getPaymentMethod() and then verify that there is a valid payment method. So add this functionality to the method:

const paymentMethods = await getPaymentMethod()
if (paymentMethods.length === 0) {
console.log('Unable to top up. No payment methods available')
return false
}

Now, create the top-up, and return the relevant response depending on whether the top-up was successful or not:

const topupAttempt = await truAPI.createTopUp(paymentMethods[0].id, currency, amount)
if (topupAttempt.status === 201) {
const body = await topupAttempt.json()
console.log(`Top up successful: ${body}`)
return true
}
console.log('Unable to create top up')
return false

Finally, add the topup method to your exports:

module.exports = {
getPaymentMethod,
topup
}

Retry a SIMCheck once topped up

You may have noticed that none of this functionality gets called yet in your application! At this point, you're going to incorporate it into your SIMCheck flow. Open the file src/routes/tru.js, and at the top of this, add the payments utility file import:

const truPayments = require('../utils/payments')

Find the line: console.log('SIMCheck returned 402, insufficient credit.') and below this, add the following to trigger the top-up request:

const topup = await truPayments.topup(process.env.TRU_TOP_UP_CURRENCY, process.env.TRU_TOP_UP_AMOUNT)

If the top-up attempt was successful, you'll try to retrigger the SIMCheck with the following:

if (topup !== false) {
console.log('Topup successful.. retrying SIMCheck')
const simCheckRetry = await truAPI.createSIMCheck(phone_number)
const retryResponse = await simCheckRetry.json()
if (simCheckRetry.status !== 201) {
console.log('Second attempt at creating SIMCheck failed')
return res.status(400).json({ error: "Unable to run SIMCheck, please try later."})
}
console.log('SIMCheck retry successful.. results:')
console.log(retryResponse)
return res.status(simCheckRetry.status).json(retryResponse)
}

Otherwise, you'll return the error that's already there: Unable to run SIMCheck, please try again in a few minutes.

Wrapping up

That's it! You've now introduced an automatic top-up method within your application if any of your tru.ID checks fail with an HTTP status code 402. This tutorial should help reduce disruptions to a key part of your application flow.

Resources

Download our Developer Console mobile app
Made withacross the 🌍
© 2024 4Auth Limited. All rights reserved. tru.ID is the trading name of 4Auth Limited.