Develop your own backend¶
AB_PROVIDER¶
The interface between AqBanking and banking service providers (like HBCI/FinTS etc) is the class AB_PROVIDER.
AqBanking mainly uses two structures for communication with applications and with providers: AB_TRANSACTION (used as commands to send to banks and also as importable transactions, transfer requests etc) and AB_ACCOUNT_SPEC which specifies accounts a provider supports.
AB_ACCOUNT_SPEC is the most important link between AqBanking and banking service providers and its also the class that gets presented to applications, so in any case your module needs to provide at least a way to create and use such objects.
Most providers internally have some data about a user (user id, credentials, server address etc) and also data about bank accounts assigned to a user (e.g. what kind of jobs are supported etc).
To make things easier for the developer who wants to implement a banking protocol AqBanking provides some structures:- AB_USER (for user-specific stuff like user id, server info etc.)
- AB_ACCOUNT (for bank account information, obviously).
These object types are not used by AqBanking itself and are also not presented to applications.
If your provider implementation uses AB_ACCOUNT and AB_USER it can make use of the convenience functions in this group here (like AB_Provider_GetAccount, AB_Provider_GetUser etc.) to easily read, write, add and delete user and account objects.
The simplest banking backend provider would need to implement the following callback functions:- AB_Provider_Init
- AB_Provider_Fini
- AB_Provider_SendCommands
- AB_Provider_CreateUserObject
- AB_Provider_CreateAccountObject
- AB_Provider_UpdateAccountSpec (only if the backend provides transaction limits or other specialties)
The most simple backend is "aqnone" (see src/libs/plugins/backends/aqnone). It doesn't implement any online banking but allows for creation of users and accounts. You can use that backend as a template and implement the needed callback functions mentioned above.
This is an example from the AqHBCI module (src/libs/plugins/backends/aqhbci/banking/provider.c)
[provider.c]
#include "provider_p.h"
/* This is needed to use the GWEN_INHERIT mechanism with the AB_PROVIDER extension of AqHBCI */
GWEN_INHERIT(AB_PROVIDER, AH_PROVIDER);
AB_PROVIDER *AH_Provider_new(AB_BANKING *ab, const char *name)
{
AB_PROVIDER *pro;
AH_PROVIDER *hp;
pro=AB_Provider_new(ab, name);
assert(pro);
/* set backend ("virtual") functions */
AB_Provider_SetInitFn(pro, AH_Provider_Init);
AB_Provider_SetFiniFn(pro, AH_Provider_Fini);
/* basic functions needed for online banking */
AB_Provider_SetSendCommandsFn(pro, AH_Provider_SendCommands);
AB_Provider_SetCreateAccountObjectsFn(pro, AH_Provider_CreateAccountObject);
AB_Provider_SetCreateUserObjectsFn(pro, AH_Provider_CreateUserObject);
AB_Provider_SetUpdateAccountSpecFn(pro, AH_Provider_UpdateAccountSpec);
AB_Provider_SetControlFn(pro, AH_Control);
/* AqHBCI provides GUI dialogs for creation of users and accounts */
AB_Provider_SetGetNewUserDialogFn(pro, AH_Provider_GetNewUserDialog);
AB_Provider_SetGetEditUserDialogFn(pro, AH_Provider_GetEditUserDialog);
AB_Provider_SetGetUserTypeDialogFn(pro, AH_Provider_GetUserTypeDialog);
AB_Provider_SetGetEditAccountDialogFn(pro, AH_Provider_GetEditAccountDialog);
AB_Provider_AddFlags(pro,
AB_PROVIDER_FLAGS_HAS_NEWUSER_DIALOG |
AB_PROVIDER_FLAGS_HAS_EDITUSER_DIALOG |
AB_PROVIDER_FLAGS_HAS_EDITACCOUNT_DIALOG |
AB_PROVIDER_FLAGS_HAS_USERTYPE_DIALOG);
GWEN_NEW_OBJECT(AH_PROVIDER, hp);
GWEN_INHERIT_SETDATA(AB_PROVIDER, AH_PROVIDER, pro, hp,
AH_Provider_FreeData);
hp->hbci=AH_HBCI_new(pro);
assert(hp->hbci);
hp->dbTempConfig=GWEN_DB_Group_new("tmpConfig");
return pro;
}
...
/* implementation of the callback function to create an AB_USER object */
AB_USER *AH_Provider_CreateUserObject(AB_PROVIDER *pro)
{
return AH_User_new(pro); /* see implementation notes below */
}
...
/* implementation of the callback function to create an AB_ACCOUNT object */
AB_ACCOUNT *AH_Provider_CreateAccountObject(AB_PROVIDER *pro)
{
return AH_Account_new(pro);
}
[provider_p.h]
typedef struct AH_PROVIDER AH_PROVIDER;
struct AH_PROVIDER {
AH_HBCI *hbci;
GWEN_DB_NODE *dbTempConfig;
};
Working With Users¶
AB_USER is mainly used to hold information about the user who has accounts at an institution. AqBanking stores such objects in its internal database but knows only most basic things about a user (e.g. name, user id etc.). If your provider has some more information about users (like server url etc.) it wants to store you should generate your own object type derived from AB_USER. The way to achieve this in AqBanking is to use the GWEN_INHERIT mechanism.
The following example code is a modification of code from the AqHBCI module (src/libs/plugins/backends/aqhbci/banking/user.c):
...
/* This is needed to use the GWEN_INHERIT mechanism with the AB_USER extension of AqHBCI */
GWEN_INHERIT(AB_USER, AH_USER)
...
AB_USER *AH_User_new(AB_PROVIDER *pro)
{
AB_USER *u;
AH_USER *ue;
assert(pro);
u=AB_User_new();
assert(u);
GWEN_NEW_OBJECT(AH_USER, ue);
GWEN_INHERIT_SETDATA(AB_USER, AH_USER, u, ue, AH_User_freeData);
AB_User_SetProvider(u, pro);
AB_User_SetBackendName(u, "aqhbci");
ue->readFromDbFn=AB_User_SetReadFromDbFn(u, AH_User_ReadFromDb);
ue->writeToDbFn=AB_User_SetWriteToDbFn(u, AH_User_WriteToDb);
/* setup some internal data for AqHBCI */
ue->tanMethodList[0]=-1;
ue->tanMethodCount=0;
AB_User_SetCountry(u, "de");
return u;
}
...
int AH_User_ReadFromDb(AB_USER *u, GWEN_DB_NODE *db)
{
AH_USER *ue;
int rv;
GWEN_DB_NODE *dbP;
AB_PROVIDER *pro;
const char *s;
assert(u);
ue=GWEN_INHERIT_GETDATA(AB_USER, AH_USER, u);
assert(ue);
DBG_INFO(AQHBCI_LOGDOMAIN, "Reading user from db (%u)", (unsigned int) GWEN_DB_GetIntValue(db, "uniqueId", 0, 0));
/* save provider, because AB_User_ReadFromDb clears it */
pro=AB_User_GetProvider(u);
/* read data for base class (AB_USER) */
rv=(ue->readFromDbFn)(u, db);
if (rv<0) {
DBG_INFO(AQHBCI_LOGDOMAIN, "here (%d)", rv);
return rv;
}
/* set provider again */
AB_User_SetProvider(u, pro);
/* read data for provider */
dbP=GWEN_DB_GetGroup(db, GWEN_DB_FLAGS_DEFAULT, "data/backend");
/* read HBCI-sepcific user information from the given GWEN_DB */
s=GWEN_DB_GetCharValue(db, "cryptMode", 0, "unknown");
ue->cryptMode=AH_CryptMode_fromString(s);
...
return 0;
}
int AH_User_WriteToDb(const AB_USER *u, GWEN_DB_NODE *db)
{
AH_USER *ue;
int rv;
GWEN_DB_NODE *dbP;
assert(u);
ue=GWEN_INHERIT_GETDATA(AB_USER, AH_USER, u);
assert(ue);
DBG_INFO(AQHBCI_LOGDOMAIN, "Writing user db (%u)", (unsigned int) AB_User_GetUniqueId(u));
/* write data for base class (AB_USER) */
rv=(ue->writeToDbFn)(u, db);
if (rv<0) {
DBG_INFO(AQHBCI_LOGDOMAIN, "here (%d)", rv);
return rv;
}
/* write HBCI-specific data */
dbP=GWEN_DB_GetGroup(db, GWEN_DB_FLAGS_DEFAULT, "data/backend");
/* write HBCI-specific data to the given GWEN_DB */
...
return 0;
}