Skip to content

Plugins in Pecunia (english)

Mike Lischke edited this page Jun 14, 2015 · 5 revisions

Plugins in Pecunia serve as source of transaction informations which are not available via the HBCI/FinTS protocol. These can be credit card informations, Paypal or other sources - which can be accessed via the http(s) protocol.

Plugins are implemented using Javascript, where each plugin has to reside in its own file. The js files have to be placed into the data folder of Pecunia, in a subfolder named Plugins. This folder will be created and filled with standard plugins by Pecunia if they are not yet there. You can find the data folder using Pecunia's preferences -> Speicherort -> Finder... (UI is not yet localized). One level above that folder you can find Pecunia's data folder which also contains the mentioned Plugins folder as well as other resources. Plugins are loaded by Pecunia at startup. Hence, for an update of plugin you have to restart Pecunia.

Basic facts

  • Plugins are identified by a special ID which is assigned to an account that is handled by the plugin.
  • A plugin can also be used by multiple accounts. However, an account can always only be associated with a single plugin.
  • Plugins each run always in an own JS context and are executed in parallel. Pecunia takes care to serialize specific subtasks like PIN queries.
  • The work context itself does not contain a DOM and cannot be used to navigate to webpages. Instead Pecunia provides access to an instance of a WebView (named webClient) which is used for all queries, manipulations etc. With this programmers have the full power of WebKit in their hands.
  • A test application has been created to ease plugin development, without having to run Pecunia actually. More about that in an own wiki document.
  • Even though the parser reports many errors it can still happen that a script cannot be loaded because of syntax errors, but Pecunia doesn't get notified about them. For this reason plugin scripts must end with a single line true;. This becomes the return value for the entire script and is only returned if the script could truly be parsed. Hence it's a good means for Pecunia to determine if the script has been loaded successfully.
  • Even with the raise of IBAN + BIC, the plugins usually still use the old bank codes. At least for banks in Germany.

Plugin Basics

To recognize and register a plugin it has not only to be in the Plugins folder but must contain a variable named name that contains a plugin ID of the form: "pecunia.plugin.xxx" where xxx is an arbitrary string to identify the plugin uniquely. Furthermore a variable "description" is required that contains a clear text description of the plugin. Best is to use a short description of the account type that can be shown in a selection box. A number of optional variables completes the set of informations, like a license, the author and his homepage etc. A typical set looks like this:

var name = "pecunia.plugin.dkbvisa";
var author = "Mike Lischke";
var description = "DKB Kreditkartenkonto";
var homePage = "http://pecuniabanking.de";
var license = "CC BY-NC-ND 4.0 - http://creativecommons.org/licenses/by-nc-nd/4.0/deed.de";
var version = "1.0";

Essentially web queries are handled asynchronously. You can navigate to a new webaddress by setting a new value in the webClient.URL member. If the call is successful Pecunia calls a callback function that must be defined in the plugin script and which has been assigned to the webClient.callback member. This callback takes a single bool parameter which can be used by the test environment to tell the script to run only a single step of its state machine (if there's one at all). Because of the asynch nature of the navigation it is recommended to implement a state machine like approach to handle the different navigation steps in the process.

Auto Detection

The Plugin system includes a simple function to find an appropriate plugin for a given account + bank code combination. That way the user often doesn't have to manually assign a plugin. For this feature to work a plugin has to provide a function canHandle which returns true or false depending on whether it can handle the account or not. The first plugin returning true here "wins". Example:

function canHandle(account, bankCode) {
    if (bankCode != "12030000")
        return false;

    // Credit cards are the only accounts with 16 digits.
    if (account.length != 16)
        return false;

    return true;
}

Error Messages

Different ways of reporting special situations are integrated in the work context, which allow send messages to Pecunia and/or the user. One is the logger variable with its functions logError, logWarning, logInfo, logDebug and logVerbose. These functions write the message into the Pecunia log. In the test environment these messages are shown in a text control instead for instant validation. Additionally there's the function reportError, which allows to show a dialog with a message to the user (e.g. if the login process failed, because of a wrong PIN). The usual JS functions alert() and window.alert() don't work. All error/log functions take a single string as parameter (the message).

Work flow

At the time being there's only one workflow possible: get financial statements. For this to work the plugin has to provide a function with this signature and name:

function getStatements(user, bankCode, password, from, to, numbers) {
    ...
}

The parameters are (in this order): the user ID for login, the bank code, the password, begin and end date, as well as an array with account numbers (or credit card numbers, if it is about credit card accounts). The result of this function must be "true" to indicate if the process could be started successfully. From the "numbers" being an array one can see that Pecunia groups account that use the same plugin into a single plugin invocation. This often saves a lot of time, since no extra login process must be started for each account.

Once the plugin collected all the necessary data it must convert that a binary JSON format and has to call the function webClient.resultsArrived, passing in the JSON data. A textual expression of the binary JSON format is listed below:

[ { result of first account }, { result of second account }, ... ]

Result map:

{
    "isCreditCard": true/false,
    "account": "xxxx", (account/credit card number)
    "bankCode": "xxxx", (bank code)
    "balance": "100.0 EUR", (balance as string, correct conversion to a number happens in Pecunia)
    "statements": [ statement ],
    "lastSettleDate": Date (date of last update)
}

Statement:

{
    "final" = true/false, (false for preliminary statements)
    "valutaDate" = Date, (value data)
    "date" = Date, (booking date)
    "transactionText" = "...", (purpose)
    "value" = "123.456,00 EUR", (value as string, correct conversion to a number happens in Pecunia)
    "originalValue" = ditto like "value" (for values that came from different currencies)
}

Two things are important here: date values are passed around as JS dates (for calls to getStatements as well as in the results). Money values must be passed as strings always, as converting to in JS to a double or float will cause rounding errors. The currency in the value is option (default is EUR).

Correct conversion of money values

In order to avoid rounding errors when converting a value string to a number Pecunia does not use normal float or double types, but does a full conversion to a decimal value. To make this work the app needs to know details about the number format, which the plugin should provide in a further variable, whose structure is like this:

var numberInfo = {
    "decimalSeparator": ",",    // Default is .
    "groupSeparator": ".",      // Default is ,
    "groupingSize": 3,          // Number of digits in a group. Default is 3.
    "maximumFractionalDigit": 2 // Default is 2.
};

Any missing value is replaced by a default value (as described above). At the moment it's not possible to have different number formats in the values. If there's a need to support that later we can always extend the plugin system quite easily.

The end

The current plugin implementation is still in a early state there might be issues here and there. Such issues should be reported in our bug tracker or mailing list. Because of the great integration of JS with Obj-C/Swift it is quite easy to extend the plugin system in the future. The entire system is very flexible and it would be easily possible to defer complex processes/computations etc. to Pecunia. It's possible to provide access to almost anything in the scripts.