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 IDlayr balance into your own hands based on your usage, utilizing the IDlayr 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 IDlayr Account
- Node.js installed locally on your computer
- A mobile phone with an active data SIM card
Getting started
A IDlayr 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 IDlayr to make API calls. So sign up for a IDlayr account, which comes with some free credit. We've built a CLI for you to manage your IDlayr account, projects, and credentials within your Terminal. To install the IDlayr CLI run the following command:npm install -g @tru_id/cli
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
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 IDlayr project within the root directory with the following command:tru projects:create automated-topups --project-dir .
Make a note of these IDlayr 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 [email protected]: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 [email protected]: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 IDlayr 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 IDlayr'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 IDlayr
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 IDlayr 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 inTRU_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 tokenif (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 anywayif (moment().add(1, "minute").isBefore(moment(new Date(WORKSPACE_TOKEN.expiresAt)))) {// token not expiredreturn WORKSPACE_TOKEN.accessToken;}}const accessToken = await getAccessToken(process.env.TRU_WORKSPACE_CLIENT_ID,process.env.TRU_WORKSPACE_SECRET,"console")// update token cache in memoryWORKSPACE_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 IDlayr. So, in this same file, you will need to request to retrieve all payment records IDlayr 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() + 1const 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 IDlayr to charge,currency
: the currency the top-up request will be made in, for exampleEUR
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 IDlayr'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 IDlayr checks fail with an HTTP status code 402. This tutorial should help reduce disruptions to a key part of your application flow.