Fiserv Banking APIs Made Simple — Part 2: API Calls and Data Mapping

Chintana Wilamuna
5 min readMay 30, 2023

Part 1 of the this blog covered connecting to Fiserv APIs with relative ease using Ballerina language. This blog explore doing API calls and data mapping for exposing a backend for front-end or a domain API for apps.

Once successfully authenticated, the rest of the API calls from the catalog require a different set of headers to be sent with the request.

Setting custom headers

To make it easy, we can have a function that returns a request object that sets all the custom header info. Then we can set the payload for the specific API and call it.

function getRequestWithHeaders(Token token) returns http:Request|error {
http:Request req = new;
req.setHeader("Authorization", "Bearer " + token.access_token);
json efxHeader = {
"OrganizationId": orgId,
"TrnId": uuid:createType4AsString()
};
req.setHeader("EfxHeader", efxHeader.toString());
req.setHeader("Host", hostname);
req.setHeader("Accept", "application/json");
return req;
}

Let’s see what’s going on here,

  • The function accepts a token object that’s generated in part 1 of the blog.
  • Create a new request object
  • Set Authorization header and set the access token as the value
  • Create the EFXHeader that’s required by Fisev. This takes 2 paramers. OrgID should correspond to the organization ID that’s given by Fiserv workspace credentials. TrnID is a UUID to identify the transaction
  • We set the Host header and Accept header. Accept header sets the content type our program would understand and tell the server to send data using the same content type. Sometimes servers send data using one type regardless of the accept header without any warning. In this example, if you set it to any other type than JSON the Fiserv servers will complain and send an HTTP 406 Not Acceptable error message and list application/json as the only acceptable representation

After this we can define a resource function based on our use case. Let’s define a function to get account details.

resource function get accountDetails() returns error? {
Token authToken = check getAccessToken();
http:Request req = check getRequestWithHeaders(authToken);
req.setJsonPayload({
"AcctSel": {
"AcctKeys": {
"AcctId": "5041733",
"AcctType": "DDA"
}
}
});
http:Client reqClient = check new(hostname);
json res = check reqClient->/banking/efx/v1/acctservice/acctmgmt/accounts/secured.post(req);
io:println("--> Response: ", res);
}

Note that getAccessToken() method was defined in part 1 of this post.

  • Here we set the payload that’s accepted by the respective API call. This is well documented in Fiserv API docs.
  • We define a new HTTP client and do a POST call with the request object. This will send an HTTP POST with all the custom header and payload data

Once executed, the result will be a rather long JSON payload with lots of fields and all information about the particular account.

Data Mapping

When providing a simplified message to apps and users, we don’t need to send this huge payload as is. That’s where data mapping feature of Ballerina comes handy. Let’s define the shape of message we want to send to the app. This should include just enough information for the app to render that portion of the page.

type AccountDetails record {
string AccountID;
string AccountType;
decimal AvailableBalance;
};

Once we have the shape and attribute names, next up is to map the big JSON and get the correct attributes from there. There are two ways of going about this. One way would be to paste the JSON message to the IDE and use the Ballerina IDE feature ‘Paste JSON as Record’. This will convert the JSON payload to Ballerina types automatically. One problem however is that when the message is larger, it’ll create a lot of type definitions with all the attributes.

Second way is to define a type with just enough attributes so that we can process it. For this use case, that’s better IMO. Based on the JSON that’s returned by Fiserv, the types look like below,

type AccountResponse record {
AccountRec AcctRec;

};

type AccountRec record {
AccountKeys AcctKeys;
DepositInfo DepositAcctInfo;
};

type AccountKeys record {
string AcctId;
};

type DepositInfo record {
string Desc;
AccountBalance[] AcctBal;
};

type AccountBalance record {
string BalType;
CurrentAmount CurAmt;

};

type CurrentAmount record {
decimal Amt;
};

After that using the low code view let’s add a data mapper object. Then define input and output types based on the types we have.

Data mapper component added using the low code view of Ballerina

Note that for mapping the available balance we need to add a custom function. This function will iterate through the AccountBalance array, get the account balance for AvailCash attribute. Once we’re done the source will look like below

function getAvailableCash(AccountBalance[] accbal) returns decimal {
foreach AccountBalance bal in accbal {
if bal.BalType == "AvailCash" {
return bal.CurAmt.Amt;
}
}
return -1.0;
}

function transform(AccountResponse accountResponse) returns AccountDetails => {
AccountID: accountResponse.AcctRec.AcctKeys.AcctId,
AccountType: accountResponse.AcctRec.DepositAcctInfo.Desc,
AvailableBalance: getAvailableCash(accountResponse.AcctRec.DepositAcctInfo.AcctBal)
};

One of the major advantages in data mapping component is the flexibility to add custom functions. So we’re not limited by a fixed set of transformations provided by some of the transformation oriented products out there.

Final step is to change the return type of our main RESTful function and return transformed data. Changed bits in bold.

resource function get accountDetails() returns AccountDetails|error? {
Token authToken = check getAccessToken();
[jwt:Header, jwt:Payload] [header, payload] = check jwt:decode(authToken.access_token);
io:println("Header: ", header);
io:println("Payload: ", payload);

http:Request req = check getRequestWithHeaders(authToken);
req.setJsonPayload({
"AcctSel": {
"AcctKeys": {
"AcctId": "5041733",
"AcctType": "DDA"
}
}
});
http:Client reqClient = check new (hostname);
json res = check reqClient->/banking/efx/v1/acctservice/acctmgmt/accounts/secured.post(req);
AccountResponse accRes = check res.cloneWithType(AccountResponse);
AccountDetails accDetails = transform(accRes);
return accDetails
;
}

When we run this the output of this service will look like below

{
"AccountID": "5041733",
"AccountType": "CHECKING",
"AvailableBalance": 468.88
}

In Summary

  1. In part 1 of this story we explored how to authenticate into Fiserv APIs
  2. In this story we explored how to call the account details API from Fiserv API catalog
  3. Use data mapping to reduce the payload to a simple message that’s easy to process by an app

You can find the full source of this here in GitHub.

--

--