Access Java SDK usage
Installation
Please first download an Access Java SDK by following the description in Using a generated SDK and choosing “java” as the target language.
A standalone Java project is generated that can be built with either Gradle or Maven. You may either build the project and include the created jar file as a dependency in your application, or directly copy out the generated code into your application.
The code examples in this chapter assume that you used the “Download SDK” button to generate the SDK and chose the V2 API version. By this, the generator runs with default options, using OkHttp as the HTTP client library and GSON as the serialization library.
If you used the finAPI SDK Generator instead to customize SDK generation, you may end up with a different SDK structure and divergent Java code, especially if you change the library
parameter. The code examples should still be helpful for you, however, may need to be adapted.
Overview
The src/main/java
folder contains the SDK classes. The app
subpackage contains API classes to call API service methods. For each section of the Access API (see the left sidebar of the Access API documentation), a distinct API class is available, e.g. the Java class AuthorizationAPI
contains all services below the "Authorization" section. The model
package contains the API request and response model classes.
The src/test/java
folder is not required to run the SDK, but the pre-generated tests are helpful to checkout API usage, as they contain example calls of all API services.
The easiest way to run a service is to just instantiate the API class and execute the desired service method, e.g.:
new BanksApi().getAndSearchAllBanks(<parameters>);
In this case, the API class uses a pre-configured global configuration. You can change the configuration settings globally by calling the setter methods of Configuration.getDefaultApiClient()
. This configuration will by default use https://sandbox.finapi.io as the API base path, so you need your sandbox client credentials to run the following examples.
To find out which parameters you have to provide for a service call, just navigate to the method implementation in your IDE, e.g. to BanksApi.getAndSearchAllBanks
. Each method contains an extensive Javadoc comment with the same information as on our API documentation page. The same applies to the model classes.
Usage Examples
The following examples are reduced to the necessary minimum, to help you get easily started with the SDK. However, for a productive application more topics are to be considered, please refer to the Advanced topics section for this.
Authorization and Creation of a User Identity
This section shows how the API calls described in https://documentation.finapi.io/access/authorization-and-creation-of-a-user-identity can be executed via Java SDK.
Get authorized as a client
Instantiate the AuthorizationAPI
and provide your client credentials:
AuthorizationApi authorizationApi = new AuthorizationApi();
AccessToken clientToken = authorizationApi.getToken(
"client_credentials",
"<your client_id",
"<your client secret>",
null, // request id omitted
null, // refreshToken is not required
null, // username is not required
null); // password is not required
Create an Access user
The createUser
service requires the client token, so it must be registered before the call.
Configuration.getDefaultApiClient().setAccessToken(clientToken.getAccessToken());
new UsersApi().createUser(
new UserCreateParams()
.id("<username>")
.password("<password>"),
null);
Get authorized as a user
Instantiate the AuthorizationAPI
and provide your client credentials and the user’s credentials:
AuthorizationApi authorizationApi = new AuthorizationApi();
AccessToken userToken = authorizationApi.getToken(
"password", // grant_type to request a user token
"<your client_id",
"<your client secret>",
null, // request id omitted
null, // refresh_token is not required
"<username>",
"<password>");
Search for a Bank
The createUser
service requires a client or user token to be provided, so it must be registered before the call.
Example: Search for test banks containing “finAPI” for a user
Configuration.getDefaultApiClient().setAccessToken(userToken.getAccessToken());
PageableBankList bankSearchResult = new BanksApi().getAndSearchAllBanks(
null, // ids omitted
"finAPI", // the search string
null, // supportedBankingInterfaces omitted
null, // location omitted
null, // tppAuthenticationGroupIds omitted
true, // search for test banks only
null, // page omitted
null, // perPage omitted
null, // order omitted
null); // request id omitted
List<Bank> foundBanks = bankSearchResult.getBanks();
Import a new Bank Connection - no Web Form (Licensed customers only)
As a licensed customer, to access banks via the XS2A interface you first have to upload your TPP certificate and TPP credentials for certain banks before you can do a bank connection import. Please refer to Using your own eIDAS Certificates and TPP Credentials for details.
To import a bank connection you must first search for the bank you want to import and determine its id and login credential names for the chosen interface. The following example is hard coded to the finAPI demo bank with the XS2A interface.
For API service calls that connect to a bank, you must provide PSU metadata (see the General user metadata section of our API documentation). The example assumes that you stored the metadata in the variables psuIpAddress
, psuDeviceOS,
and psuUserAgent
before.
Configuration.getDefaultApiClient().setAccessToken(userToken.getAccessToken());
BankConnectionsApi bankConnectionsApi = new BankConnectionsApi();
ImportBankConnectionParams params = new ImportBankConnectionParams()
.bankId(280001L)
.bankingInterface(XS2A)
.addLoginCredentialsItem(new LoginCredential().label("Onlinebanking-ID").value("demo"))
.addLoginCredentialsItem(new LoginCredential().label("PIN").value("demo"));
try {
BankConnection bankConnection = bankConnectionsApi.importBankConnection(
params,
psuIpAddress,
psuDeviceOS,
psuUserAgent,
null); // request id omitted
} catch (ApiException apiException) {
if (apiException.getCode() == 510) {
ErrorMessage errorMessage = ErrorMessage.fromJson(apiException.getResponseBody());
ErrorDetails errorDetails = errorMessage.getErrors().get(0);
ErrorDetailsMultiStepAuthentication multiStepAuthentication = errorDetails.getMultiStepAuthentication();
String hash = multiStepAuthentication.getHash();
// handle multistep-authentication dependent on content of 'multiStepAuthentication'
} else {
// handle other errors here
}
}
As the catch block indicates, an import normally requires a Multi-Step authentication. Use the information in the ErrorDetailsMultiStepAuthentication
object to ask your customer for the necessary input and repeat the import call with the enriched data.
Example: Provide a user-given TAN in the challengeResponse
field:
BankConnectionsApi bankConnectionsApi = new BankConnectionsApi();
ImportBankConnectionParams params = new ImportBankConnectionParams()
.bankId(280001L)
.bankingInterface(XS2A)
.addLoginCredentialsItem(new LoginCredential().label("Onlinebanking-ID").value("demo"))
.addLoginCredentialsItem(new LoginCredential().label("PIN").value("demo"))
.multiStepAuthentication(new ConnectInterfaceParamsMultiStepAuthentication()
.hash("<hash returned by initial call")
.challengeResponse("123456"));
try {
BankConnection bankConnection = bankConnectionsApi.importBankConnection(
params,
psuIpAddress,
psuDeviceOS,
psuUserAgent,
null);
} catch (ApiException apiException) {
// handle error
}
Import a new Bank Connection - with Web Form 2.0
The usage of Web Form 2.0 requires additional integration of the Web Form 2.0 SDK into your application. Please refer to Web Form 2.0 Java SDK usage for a detailed explanation and usage examples.
Check the status of the bank connection
After a bank connection import has finished successfully, it’s still in the process of downloading accounts and transactions in the background (please check Post Processing of Bank Account Import/Update for details). So before you can access accounts and transactions, you need to wait until the update status is READY
. Use the getBankConnection
service for this and provide the id of the imported bank connection:
BankConnectionsApi bankConnectionsApi = new BankConnectionsApi();
BankConnection updatedbankConnection = bankConnectionsApi.getBankConnection(
bankConnection.getId(), null);
// TODO check bankConnection.getUpdateStatus() until 'READY'
Get Accounts and Transactions
To query the account data for the imported bank connection, please use the getAndSearchAllAccounts
service. The following example queries all accounts:
AccountList allAccounts = accountsApi.getAndSearchAllAccounts(
null, // dont filter accounts ids,
null, // no search string provided,
null, // don't restrict account types
null, // don't filter bank connection ids
null, // don't restrict to a min last successful update date,
null, // don't restrict to a max last successful update date,
null, // don't restrict to a min balance
null, // don't restrict to a max balance
null); // request id omitted
The getAndSearchAllTransactions
allows you to query transactions based on many criteria. The following example shows the simplest usage omitting any search criteria, to retrieve all transactions.
TransactionsApi transactionsApi = new TransactionsApi();
PageableTransactionList transactionsPage = transactionsApi.getAndSearchAllTransactions(
"userView",
null, // dont filter transaction ids
null, // no search string provided
... // all further parameters can also be left to null
1, // fetch the first page
100, // use a page size of 100
null, // don't provide order fields
null);// omit request id
// Use transactionsPage.getTransactions() to access the transactions
// Use transactionsPage.getPaging() to check whether further pages are available
Advanced topics
Configure the API basepath
If you instantiate an API class with the default constructor as shown in the examples, the call will be executed against the finAPI sandbox (https://sandbox.finapi.io). This is correct when you start adopting the API, but to switch to our production environment, you need to override the URL to https://live.finapi.io.
The simplest option is to override the URL in the default API client:
Configuration.getDefaultApiClient().setBasePath("https://live.finapi.io");
Provide a request id
All API service methods except the OAuth2 services declare the String xRequestId
parameter. It’s optional and omitted in the examples above, but you should always provide a unique request id which is used for a single request only.
A simple approach could be to use a UUID for this:
public String requestId() {
return UUID.randomUUID().toString();
}
Register the OAuth token
All services except the OAuth services require that you either provide a client or user token along with the call. The simplest approach is to register the token directly with the global default API client that is used by all API classes by default:
Configuration.getDefaultApiClient().setAccessToken(clientToken.getAccessToken());
or
Configuration.getDefaultApiClient().setAccessToken(userToken.getAccessToken());
But if you need to serve multiple users in parallel with a server application, this no longer works. A simple option would be to instantiate a new API client and API class for each user call like
final ApiClient apiClient = new ApiClient();
apiClient.setAccessToken(clientToken.getAccessToken());
BanksApi banksApi = new BanksApi(apiClient);
Another approach could be to still use the default API client, but intercept the token handling on the OkHttpClient
level and get the token from a ThreadLocal
that is populated in each user's context:
// Global setup
final ThreadLocal<String> tokenHolder = new ThreadLocal<>();
OkHttpClient httpClient = new OkHttpClient.Builder().addInterceptor(chain ->
chain.proceed(chain.request().newBuilder()
.addHeader("Authorization", "Bearer " + tokenHolder.get())
.build())).build();
Configuration.getDefaultApiClient().setHttpClient(httpClient);
// Per user request in the user thread
tokenHolder.set(clientToken.getAccessToken());
new BanksApi().getAndSearchAllBanks(<parameters>);
Exception Handling
If an API services call returns with an error, an ApiException
is thrown that must be handled. As the ApiException
contains the raw JSON data only, you should convert it into the structured ErrorMessage
object. An exception is for HTTP 401 responses where a different error structure is returned.
try {
final Bank bankSearchResponse = new BanksApi().getBank(99999999L,requestId());
} catch (ApiException apiException) {
if (apiException.getCode() == 401) {
System.out.println("Not authenticated or invalid access_token");
} else {
System.out.println("API call failed with HTTP code " + apiException.getCode());
final ErrorMessage errorMessage = ErrorMessage.fromJson(apiException.getResponseBody());
System.out.println("error message is " + errorMessage);
}
}
Access HTTP information
The Java API service methods used in the examples only return the resource data but give no access to HTTP response information. If you are interested in that, you can use the WithHttpInfo
-suffixed variants of the service methods:
ApiResponse<Bank> bankResponse = new BanksApi().getBankWithHttpInfo(280001L, requestId());
Bank bank = bankResponse.getData();
System.out.println(bankResponse.getStatusCode());
System.out.println(bankResponse.getHeaders().get("X-Request-Id"));