You are on page 1of 12

Transaction Banking

Balance API
SPECIFICATIONS

Release: 1.2
Date: 06th June 2020
Author: Rishu Gupta
Revision History
Revision date Author Version Summary of changes
24th June 2019 Pravin Navele 1.0 Draft version
06 Mar 2020
th
Rishu Gupta 1.1
06 June 2020
th
Rishu Gupta 1.2 Checksum addition

Approvals
This document requires the following approvals:

Name Title Date of approval


Roshan Jha Senior Manager 06th June 2020
Table of Contents
Objective ................................................................................................................................... 3
Format Available ....................................................................................................................... 3
HTTP Headers ........................................................................................................................... 3
API Connect SubHeader ............................................................................................................ 3
Balance Enquiry API .................................................................................................................. 4
Security Considerations ............................................................................................................ 8
Sample Encryption Decryption code and Checksum Logic ....................................................... 8
Objective
The objective of this document is to list the specification of APIs to be used for banking service.

Format Available
JSON

HTTP Headers
The following header parameters must be sent in each request.

Fields Mandatory Description

Content-Type Y Content type of request body. i.e., application/json for


JSON type
X-IBM-Client-Id Y Unique for each client. It will be provided by Axis bank

X-IBM-Client-Secret Y Unique for each client. It will be provided by Axis bank

API Connect SubHeader


API Connect SubHeader is the common identifier and audit parameter used for all the services
built in API Connect. SubHeader structure is same for both request and response bodies of an
API Connect service.
Fields Length Mandatory Description

requestUUID 40 Y Unique alphanumeric ID (without special


characters) from the source system
serviceRequestId 30 Y Service request ID configured at API Connect for the
source system.
serviceRequestVersion 3 Y Service request version configured at API Connect
for the source system.
channelId 10 Y Channel ID configured at API Connect for the client.

Note: Channel ID is client specific which will be


configured on API Connect. It will be provided by
API Connect to all the clients. It will also be used for
decryption of the encrypted request body sent by
the client.
Balance Enquiry API

URL : https://sakshamuat.axisbank.co.in/gateway/api/txb/v1/acct-recon/get-balance

Method : POST
Non-encrypted request body fields
API Connect fields Type Repeating Mandatory Description

GetAccountBalanceRequest complex N Y Request root tag

GetAccountBalanceRequestBody complex N Y Request body tag

channelId varchar(255) N Y Channel ID configured at


API Connect for the client.

corpCode varchar2(255) N Y Corporate Code to identify


the source

corpAccNum varchar2(30) N N Corporate Debit account no

checksum varchar2(255) N Y A checksum is a value used


to verify the integrity of a
file or a data transfer. This
is
calculated only on
attributes within body.

Non-encrypted JSON request sample


{
"GetAccountBalanceRequest": {
"SubHeader": {
"requestUUID": "ABC123",
"serviceRequestId": "OpenAPI",
"serviceRequestVersion": "1.0",
"channelId": “TXB”
},
"GetAccountBalanceRequestBody": {
"channelId": "TXB",
"corpCode": "DEMOCORP11",
"corpAccNum": "248012910169",
"checksum": "6cf6321ab6f39083d2e904667aeb4654"
}
}}
Encrypted request body fields

Field Type Repeating Mandatory Description

GetAccountBalanceRequest complex N Y Request root tag

GetAccountBalanceRequestBodyEncrypted complex N Y Encrypted


request data

Encrypted JSON request sample


{
"GetAccountBalanceRequest": {
"SubHeader": {
"requestUUID": "ABC123",
"serviceRequestId": "OpenAPI",
"serviceRequestVersion": "1.0",
"channelId": “TXB”
},
"GetAccountBalanceRequestBodyEncrypted":
"YlD5rTel4O+ZyFQ7+wzdrpjWa5eiVJXWgt2diYcnr6zZl6eaIk+B7EYiZde6l0Dkp1wsd0aOk/pRPB
esuToneRuq+fX+75p43o53KVuT03ZKMSaMpGvSHRIA/XSK6Vlf"
}}
Non-encrypted response body fields
API Connect fields Type Repeating Mandatory Description

GetAccountBalanceResponse complex N Y Response root tag

GetAccountBalanceResponseBody complex N Y Response body tag

status N Y It indicates the


status of the
response
data complex N N Actual data in
response in JSON
format
channelId N N Bank will share the
identifier for API

corpCode N N Corporate Code to


identify the source
corpAccNum N N Corporate Debit
account no
Balance N N Balance fetched
from core
message N Y Message as per the
status
checksum varchar2(500) N Y A checksum is a
value used to verify
the integrity of a
file or a data
transfer. This is
calculated only on
attributes within
data.

Non-encrypted JSON response sample


{
"GetAccountBalanceResponse": {
"SubHeader": {
"requestUUID": "ABC123",
"serviceRequestId": "OpenAPI",
"serviceRequestVersion": "1.0",
"channelId": “TXB”
},
"GetAccountBalanceResponseBody": {
"data": {
"channelId": "TXB",
"corpAccNum": "248012910169",
"corpCode": "DEMOCORP11",
"Balance": "-454327168887.67",
"checksum": "d331ca31b40d76879252b9cd021b3215"
},
"message": "Success",
"status": "S"
}
}
}
Encrypted response body fields

Field Type Repeating Mandatory Description

GetAccountBalanceResponse complex N Y Response root tag

GetAccountBalanceResponseBodyEncrypted complex N Y Encrypted


response data
Encrypted JSON response sample
{
"GetAccountBalanceResponse": {
"SubHeader": {
"requestUUID": "ABC123",
"serviceRequestId": "OpenAPI",
"serviceRequestVersion": "1.0",
"channelId": “TXB”
},
"GetAccountBalanceResponseBodyEncrypted":
"NBHhSBbz6giQZShEuzRaDMqLXzi8EvxUiu15KYzm+3UDK1O3SQ1GuyUeyTZz+Ocu7dXHI69rN
vWqXTLWieYxgx4wCVYE4rN0VMGjby9saFI="
}}
Non-encrypted JSON failure response samples
Checksum verification failed
{
"GetAccountBalanceResponse": {
"SubHeader": {
"requestUUID": "ABC123",
"serviceRequestId": "OpenAPI",
"serviceRequestVersion": "1.0",
"channelId": “TXB”
},
"GetAccountBalanceResponseBody": {
"data": "",
"message": "Checksum verification failed",
"status": "F"
}
}
Invalid Account number
{
"GetAccountBalanceResponse": {
"SubHeader": {
"requestUUID": "ABC123",
"serviceRequestId": "OpenAPI",
"serviceRequestVersion": "1.0",
"channelId": “TXB”
},
"GetAccountBalanceResponseBody":{
"data": "",
"message": "Debit account number incorrect",
"status": "F"
}
}
Security Considerations
Following are the security considerations which will need to be followed by the consumers for
successful connectivity with the application:
1. HTTPS and two way SSL
All consumers will be needed to invoke the application over HTTPS protocol. We also have two
way SSL established. This means that we would be validating the consumer certificate. Hence it is
required that the consumer has a certificate and the same is shared with us as a prerequisite.
2. IP whitelisting
We allow only select IP addresses to access our application over the internet. Hence as a consumer
it may be required to whitelist all the ip addresses that the consumer would be consuming the
application from. This step is a prerequisite to setup successful connectivity.
3. Symmetric encryption
The API to be invoked accepts the request body encrypted using encryption algorithm. The
body has to be encrypted using AES-128 encryption. The encryption key will be provided and
will be different for each consumer.

Sample Encryption Decryption code and Checksum Logic

import java.io.ByteArrayOutputStream;
import java.security.spec.AlgorithmParameterSpec;
import java.util.Arrays;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

import com.sun.org.apache.xml.internal.security.utils.Base64;

public class AESUtilImpl


{
private static final String ALGORITHM = "AES"; private static final String
CIPHER_ALGORITHAM = "AES/CBC/PKCS5PADDING"; private static final String KEY =
"29C1EB633ECAB0CA0F52B588AE92EA31";

//Encrypt request
public String aes128Encrypt(String plainText) throws Exception
{
byte[] iv = new byte[] { (byte) 0x8E, 0x12, 0x39, (byte) 0x9C, 0x07,
0x72, 0x6F, 0x5A, (byte) 0x8E, 0x12, 0x39, (byte) 0x9C, 0x07,
0x72, 0x6F, 0x5A };

AlgorithmParameterSpec paramSpec = new IvParameterSpec(iv);


/** Generate a secret key from the hex string as key */
SecretKeySpec skeySpec = getSecretKeySpecFromHexString(ALGORITHM, KEY);
/** Creating a cipher instance with the algorithm and padding */ Cipher cipher =
Cipher.getInstance(CIPHER_ALGORITHAM); cipher.init(Cipher.ENCRYPT_MODE,
skeySpec, paramSpec);
/** generating the encrypted result */ byte[] encrypted =
cipher.doFinal(plainText.getBytes("UTF-8")); // To add iv in encrypted string.
byte[] encryptedWithIV = copyIVAndCipher(encrypted, iv); String
encryptedResult = Base64.encode(encryptedWithIV);
return encryptedResult;
}

//Encrypt response
public String aes128Decrypt(String encryptedText) throws Exception
{
SecretKeySpec skeySpec = getSecretKeySpecFromHexString(ALGORITHM, KEY); byte[]
encryptedIVandTextAsBytes = Base64.decode(encryptedText);
/** First 16 bytes are always the IV */
byte[] iv = Arrays.copyOf(encryptedIVandTextAsBytes, 16);

byte[] ciphertextByte = Arrays.copyOfRange(encryptedIVandTextAsBytes, 16,


encryptedIVandTextAsBytes.length);
// Decrypt the message
Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHAM);
cipher.init(Cipher.DECRYPT_MODE, skeySpec, new IvParameterSpec(iv)); byte[]
decryptedTextBytes = cipher.doFinal(ciphertextByte); String decryptedResult = new
String(decryptedTextBytes, "UTF-8");
return decryptedResult;
}

private static SecretKeySpec getSecretKeySpecFromHexString(String algoCommonName,String hexString)


throws Exception {
byte [] encodedBytes = hexStrToByteArray(hexString) ; return new SecretKeySpec(encodedBytes,
algoCommonName);
}

private static byte[] hexStrToByteArray(String hex) {


ByteArrayOutputStream baos = new ByteArrayOutputStream(hex.length() / 2);

for (int i = 0; i < hex.length(); i += 2) { String


output = hex.substring(i, i + 2); int decimal =
Integer.parseInt(output, 16);
baos.write(decimal);
}
return baos.toByteArray();
}

public static byte[] copyIVAndCipher(byte[] encryptedText, byte[] iv) throws Exception {


ByteArrayOutputStream os = new ByteArrayOutputStream();
os.write(iv);
os.write(encryptedText);
return os.toByteArray();
}

}
Checksum Logic

Sample Request:
{
“data”:{
attr1: val1,
attr2 : val2,
.
.
attrN : valN
}
}
Checksum string = val1+val2+…+valN;
public static Object validateInfo(String value) {
return StringUtils.isNotEmpty(value) && "null" != value ? value : StringUtils.EMPTY;
}

Attribute Value extractor


//To retrieve the checksum string, convert JSON object into map of request of data attribute. Only Body part
of request to be considered. public String generateCheckSum(LinkedHashMap<String, Object> requestMap)
throws Exception {
StringBuilder finalChkSum = new StringBuilder();
StringBuilder keys = new StringBuilder(); try {
if(null==requestMap) {
return null;
}

for(Map.Entry<String, Object> entry: requestMap.entrySet()) {


if(!entry.getKey().equals("checksum")) {
if(entry.getValue() instanceof List) { List<Object>
tempLst=((List)entry.getValue());
if(!CollectionUtils.isEmpty(tempLst) && (tempLst.get(0) instanceof Map)) {
List<? extends Map<String, Object>> innerObjectMap
= (List<? extends Map<String, Object>>) entry.getValue();

for(Map<String ,Object> innerMap : innerObjectMap) { for(Entry<?


extends String, ? extends Object> entryInn : innerMap.entrySet())
{
keys.append(entryInn.getKey());

finalChkSum.append(
getInnerLevel2Map(
entryInn.getValue(),finalChkSum));
}
}
}else if(!CollectionUtils.isEmpty(tempLst)) { for(Object
strValues : tempLst) { finalChkSum.append(
validateInfo(
String.valueOf(strValues)));
}
}

} else if(entry.getValue() instanceof Map){


Map<? extends String, ? extends Object> innerObjectMap2
= (Map<? extends String, ? extends Object>) entry.getValue();
for(Entry<? extends String, ? extends Object> entryInn :
innerObjectMap2.entrySet()) {
keys.append(entryInn.getKey());
finalChkSum.append(
validateInfo(
String.valueOf(entryInn.getValue())));
}
}else {
finalChkSum.append(
validateInfo(
String.valueOf(entry.getValue())));
}
}
}
} catch (Exception e) {
logger.error(e);
}
return String.valueOf(
encodeCheckSumWithSHA256(
finalChkSum.toString().trim()));
}
private String getInnerLevel2Map(Object entryInnLvl2,StringBuilder finalChkSum123) {
StringBuilder finalChkSum = new StringBuilder();
StringBuilder keys = new StringBuilder();
if(entryInnLvl2 instanceof List) {
List<Object> tempLst=((List)entryInnLvl2);
if(!CollectionUtils.isEmpty(tempLst) && (tempLst.get(0) instanceof Map)) {

List<? extends Map<String, Object>> innerObjectMap =


(List<? extends Map<String, Object>>) entryInnLvl2;
for(Map<String ,Object> innerMap : innerObjectMap) {
for(Entry<? extends String, ? extends Object> entryInn : innerMap.entrySet()) {
keys.append(entryInn.getKey());
finalChkSum.append(
validateInfo(String.valueOf(entryInn.getValue())));
}
}
}else if(!CollectionUtils.isEmpty(tempLst)) {
for(Object strValues : tempLst) {
finalChkSum.append(
validateInfo(
String.valueOf(strValues)));
}
}

} else if(entryInnLvl2 instanceof Map){


Map<? extends String, ? extends Object> innerObjectMap2 =
(Map<? extends String, ? extends Object>) entryInnLvl2;
for(Entry<? extends String, ? extends Object> entryInn : innerObjectMap2.entrySet()) {
keys.append(entryInn.getKey());

finalChkSum.append(
validateInfo(
String.valueOf(entryInn.getValue())
));
}
}else {
finalChkSum.append(
validateInfo(
String.valueOf(entryInnLvl2)));
}
return finalChkSum.toString();
}

Hashing Algorithm
//Based on the final value of string, checksum will be generated using MD5 algorithm. public
static String encodeCheckSumWithSHA256(String data) {
MessageDigest md;
StringBuilder sb = new StringBuilder();
String response = null;
try {
md = MessageDigest.getInstance(“MD5”);
md.update(data.getBytes(StandardCharsets.UTF_8));
// Get the hashbytes byte[]
hashBytes = md.digest(); // Convert
hash bytes to hex format
for (byte b : hashBytes) {
sb.append(String.format("%02x", b));
}
response = sb.toString();
}catch (Exception e) {
throw new RuntimeException("Internal server error");
}
return response;
}}}

You might also like