Categorize the Transaction in The Bank’s E-Statement Using Node JS + Google Vertex AI Studio

Didik Mulyadi
8 min readJun 18, 2024

--

Photo by Google DeepMind on Unsplash

We will categorize bank transaction based on the defined categories with Google Vertex AI Studio (Gemini Generative AI).

In the previous article, we tried to extract the transactions from the BCA’s e-statement file (.pdf) into an array of strings.

Google Vertex AI Studio

It is a fully managed, unified AI development platform for building and using Generative AI (Gemini). See the prompt gallery here, we will ask the AI to categorize the transaction

Classification Search Result in Generative AI Prompt Samples

I choose “Classify Text” and open it on Google Console, the sample prompt will be put in the “Context” section.

or you can create it manually in the Vertex AI Studio > Language > Create Prompt

Let’s continue to the prompt structure.

Prompt Structure

There are 3 steps there to help the Generative AI execute the prompt.

  1. Context
    Indicate the category you require and specify the task for the AI.
  2. Examples
    Provide an example based on the real text, if there is an inaccurate result, put the text and expected result here. We want to provide some examples for each category.
  3. Test
    Put any transaction here and see the result

In the right sidebar, there is a temperature, we need to adjust the value to be low because it controls the result.

Lower temperatures are good for prompts that require a less open-ended or creative response, while higher temperatures can lead to more diverse or creative results. A temperature of 0 means that the highest probability tokens are always selected. In this case, responses for a given prompt are mostly deterministic, but a small amount of variation is still possible.

If the model returns a response that’s too generic, too short, or the model gives a fallback response, try increasing the temperature. — Google Generative AI

Here is the prompt that I used with temperature 0.

Define the categories for the text below?
Options:
- groceries
- investment
- transfer IN
- topup e-money
- transaction fee
- withdraw
- transfer OUT
- others

Please only print the one category name without anything else.

This is the sample input and output

Input and Output Sample

Here is the json format

[
{
"inputs": ["CR, Credit, SETORAN TUNAI 28/10/95031/00000"],
"outputs": ["transfer IN"]
},
{
"inputs": [
"TRSF E-BANKING DB 0610/FTFVA/WS95031 80777/TOKOPEDIA,Tokopedia,Shopee,Tiktok,Lazada,harihari,transmart,superindo,matahari,indomaret,alfa"
],
"outputs": ["groceries"]
},
{
"inputs": [
"TRSF E-BANKING DB 0710/FTFVA/WS95031 12208/SHOPEEPAY, TRSF E-BANKING DB 1410/FTFVA/WS95031 39358/OVO, DANA 17/10 TRSF E-BANKING DB, OVO, DANA, GOPAY, SHOPEEPAY"
],
"outputs": ["topup e-money"]
},
{
"inputs": [
"TRSF E-BANKING DB 2910/FTFVA/WS95031 89831/LOGAM MULIA, gold, emas, antam, reksadanam saham, IDX Mobile,MOST ,BNI Sekuritas Mobile,Mirae HOTS,BCA Sekuritas Mobile,BRI Mobile Sekuritas,Stockbit,Motion Trade,Bareksa,IPOTGO,CGS-CIMB iTrade,Trimegah Sekuritas Indonesia,Phillip Sekuritas Indonesia,KoinWorks,Ajaib,Modalku,"
],
"outputs": ["investment"]
},
{
"inputs": ["TARIKAN ATM, WITHDRAWAL"],
"outputs": ["withdraw"]
},
{
"inputs": ["BIAYA TXN, BIAYA W/D, BIAYA ADM, Administrasi"],
"outputs": ["transaction fee"]
},
{
"inputs": ["BI FAST DB BIF TRANSFER KE, Transfer to, RSF E-BANKING DB"],
"outputs": ["transfer OUT"]
},
{
"inputs": ["SALDO AWAL"],
"outputs": ["others"]
}
]

Test and Result

If your test found a mismatch, it means you should add more information in the prompt or add a new sample input and output.

investment
others
transfer out
transfer fee
transfer out
top-up e-money
groceries

Don’t forget to change the name and save the prompt.

Prompt creation form

Prompt Code

You can get the sample request with Node JS by clicking the button in the next of save button.

Prompt — Get Code

We will run it with different code, Node JS + Fetch + JWT.

Export Prompt

Now you can export the prompt to a JSON if you want to create this prompt by JSON in another project.

Export the Prompt

Here is the JSON,

{
"title": "Classify the transaction",
"description": "",
"parameters": {
"groundingPromptConfig": {
"disabled": true,
"groundingConfig": {
"sources": [
{
"type": "VERTEX_AI_SEARCH"
}
]
}
},
"stopSequences": [],
"temperature": 0,
"tokenLimits": 256,
"topP": 0.8
},
"type": "structured",
"context": "Define the categories for the text below?\nOptions:\n- groceries\n- investment\n- transfer IN\n- topup e-money\n- transaction fee\n- withdraw\n- transfer OUT\n- others\n\nPlease only print the one category name without anything else.",
"inputPrefixes": [
"Text"
],
"outputPrefixes": [
"Categories"
],
"examples": [
{
"inputs": [
"CR, Credit, SETORAN TUNAI 28/10/95031/00000"
],
"outputs": [
"transfer IN"
]
},
{
"inputs": [
"TRSF E-BANKING DB 0610/FTFVA/WS95031 80000/TOKOPEDIA,Tokopedia,Shopee,Tiktok,Lazada,harihari,transmart,superindo,matahari,indomaret,alfa"
],
"outputs": [
"groceries"
]
},
{
"inputs": [
"TRSF E-BANKING DB 0710/FTFVA/WS95031 00000/SHOPEEPAY, TRSF E-BANKING DB 1410/FTFVA/WS95031 39358/OVO, DANA 17/10 TRSF E-BANKING DB, OVO, DANA, GOPAY, SHOPEEPAY"
],
"outputs": [
"topup e-money"
]
},
{
"inputs": [
"TRSF E-BANKING DB 2910/FTFVA/WS95031 00000/LOGAM MULIA, gold, emas, antam, reksadanam saham, IDX Mobile,MOST ,BNI Sekuritas Mobile,Mirae HOTS,BCA Sekuritas Mobile,BRI Mobile Sekuritas,Stockbit,Motion Trade,Bareksa,IPOTGO,CGS-CIMB iTrade,Trimegah Sekuritas Indonesia,Phillip Sekuritas Indonesia,KoinWorks,Ajaib,Modalku,"
],
"outputs": [
"investment"
]
},
{
"inputs": [
"TARIKAN ATM, WITHDRAWAL"
],
"outputs": [
"withdraw"
]
},
{
"inputs": [
"BIAYA TXN, BIAYA W/D, BIAYA ADM, Administrasi"
],
"outputs": [
"transaction fee"
]
},
{
"inputs": [
"BI FAST DB BIF TRANSFER KE, Transfer to, RSF E-BANKING DB"
],
"outputs": [
"transfer OUT"
]
},
{
"inputs": [
"SALDO AWAL"
],
"outputs": [
"others"
]
}
],
"testData": [
{
"inputs": [
"TRSF E-BANKING DB 0610/FTFVA/WS950000 00000/TOKOPEDIA 63,028.00 DB"
]
}
],
"model": "gemini-1.5-flash-001"
}

API Integration

The API integration is already explained in this article

Preparation

We need 3 things

  1. Prepare the URL by putting your region and project ID in the URL
    https://[REGION]-aiplatform.googleapis.com/v1/projects/[PROJECT_ID]/locations/us-central1/publishers/google/models/[MODEL]:streamGenerateContent?alt=sse
    My region is us-central1
    My model is gemini-1.5-flash-001
  2. The JSON file from the prompt’s export
  3. Access token

cURL

if you are running in the local, you can get the token by gcloud auth print-access-token in your terminal

curl -X POST \
-H "Authorization: Bearer ACCESS_TOKEN" \
-H "Content-Type: application/json; charset=utf-8" \
-d @request.json \
"https://REGION-aiplatform.googleapis.com/v1/projects/PROJECT_ID/locations/us-central1/publishers/google/models/gemini-1.0-pro:streamGenerateContent?alt=sse"

Node JS with Fetch and JWT

We need to prepare the JWT access token by service account, it already explained here

Create a service account with 2 roles:
1. Service Account Token Creator
2. Vertex AI User

Create service accoutn form

Then open the service account detail > keys > Create new key > JSON.

You would get the JSON like this and put it into the .env as a string JSON (stringify) with the name “VERTEX_AI_SERVICE_ACCOUNT”

{
"type": "service_account",
"project_id": ".....",
"private_key_id": "614ff1.....822d87a...........",
"private_key": "-----BEGIN PRIVATE KEY-----...........",
"client_email": "vertexai@............iam.gserviceaccount.com",
"client_id": "102873..........",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://oauth2.googleapis.com/token",
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/vertexai%40..........iam.gserviceaccount.com",
"universe_domain": "googleapis.com"
}

Use the service account to generate the JWT access token, and then install the google auth library package to run this file.

const { JWT } = require("google-auth-library");

/**
{
type: "service_account",
project_id: "",
private_key_id: "",
private_key: "",
client_email: "",
client_id: "",
auth_uri: "",
token_uri: "",
auth_provider_x509_cert_url: "",
client_x509_cert_url: "",
universe_domain: "",
};
*/
const serviceAccount = JSON.parse(process.env.VERTEX_AI_SERVICE_ACCOUNT)

async function getJWTAccessToken() {
const jwt = new JWT({
email: serviceAccount.client_email,
key: serviceAccount.private_key,
scopes: ["https://www.googleapis.com/auth/cloud-platform"],
});

const as = await jwt.getAccessToken();

return as.token;
}

async function main() {
const accessToken = await getJWTAccessToken();

const model = "gemini-1.5-flash-001";
const text = "17/10 DB OTOMATIS KOR BIAYA W/D 3,500.00 DB 7,554,013.52";

const body = {
contents: [
{
role: "user",
parts: [
{
text: `Define the category for the text below?
Options:
- groceries
- investment
- transfer IN
- topup e-money
- transaction fee
- withdraw
- transfer OUT
- others

Please only print the one category name without anything else.

Text: CR, Credit, SETORAN TUNAI 28/10/95031/00000
Category: transfer IN

Text: TRSF E-BANKING DB 0610/FTFVA/WS95031 00000/TOKOPEDIA,Tokopedia,Shopee,Tiktok,Lazada,harihari,transmart,superindo,matahari,indomaret,alfa
Category: groceries

Text: TRSF E-BANKING DB 0710/FTFVA/WS95031 00000/SHOPEEPAY, TRSF E-BANKING DB 1410/FTFVA/WS95031 39358/OVO, DANA 17/10 TRSF E-BANKING DB, OVO, DANA, GOPAY, SHOPEEPAY
Category: topup e-money

Text: TRSF E-BANKING DB 2910/FTFVA/WS95031 00000/LOGAM MULIA, gold, emas, antam, reksadanam saham, IDX Mobile,MOST ,BNI Sekuritas Mobile,Mirae HOTS,BCA Sekuritas Mobile,BRI Mobile Sekuritas,Stockbit,Motion Trade,Bareksa,IPOTGO,CGS-CIMB iTrade,Trimegah Sekuritas Indonesia,Phillip Sekuritas Indonesia,KoinWorks,Ajaib,Modalku,
Category: investment

Text: TARIKAN ATM, WITHDRAWAL
Category: withdraw

Text: BIAYA TXN, BIAYA W/D, BIAYA ADM, Administrasi
Category: transaction fee

Text: BI FAST DB BIF TRANSFER KE, Transfer to, RSF E-BANKING DB
Category: transfer OUT

Text: SALDO AWAL
Category: others

Text: ${text}
Category:
`,
},
],
},
],
generationConfig: {
maxOutputTokens: 256,
temperature: 0,
topP: 0.8,
},
safetySettings: [
{
category: "HARM_CATEGORY_HATE_SPEECH",
threshold: "BLOCK_MEDIUM_AND_ABOVE",
},
{
category: "HARM_CATEGORY_DANGEROUS_CONTENT",
threshold: "BLOCK_MEDIUM_AND_ABOVE",
},
{
category: "HARM_CATEGORY_SEXUALLY_EXPLICIT",
threshold: "BLOCK_MEDIUM_AND_ABOVE",
},
{
category: "HARM_CATEGORY_HARASSMENT",
threshold: "BLOCK_MEDIUM_AND_ABOVE",
},
],
};

const classifyTextRes = await fetch(
`https://us-central1-aiplatform.googleapis.com/v1/projects/instaroom-photobooth/locations/instaroom-photobooth/publishers/google/models/${model}:streamGenerateContent`,
{
method: "POST",
body: JSON.stringify(body),
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${accessToken}`,
},
}
).then((r) => r.json());

const category = classifyTextRes
.map((c) => c.candidates[0].content.parts[0].text.replace("\n", "").trim())
.join(" ")
.trim();

console.log({ category });
}

main();

module.exports = main;

Result { category: ‘transaction fee’ }

Thank you for reading my article!
Reach me on Linkedin: https://www.linkedin.com/in/didikmulyadi

--

--