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>);
CODE

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
CODE

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);
CODE

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>");
CODE

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();
CODE

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 psuUserAgentbefore.

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 {
    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
    }
}
CODE

 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 {
    bankConnectionsApi.importBankConnection(
        params, 
        psuIpAddress, 
        psuDeviceOS,
        psuUserAgent, 
        null);  
} catch (ApiException apiException) {
    // handle error
}
CODE

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");
CODE

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.ToString();
}    
CODE

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());
CODE

or

Configuration.getDefaultApiClient().setAccessToken(userToken.getAccessToken());
CODE

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);
CODE

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>);
CODE

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);
    }
}
CODE

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"));
CODE