diff --git a/modules/kiwibank/README.md b/modules/kiwibank/README.md
new file mode 100644
index 00000000..21f5acd8
--- /dev/null
+++ b/modules/kiwibank/README.md
@@ -0,0 +1,4 @@
+# [weboob-kiwibank](https://github.com/infertux/weboob-kiwibank)
+
+[Kiwibank](http://www.kiwibank.co.nz/) module for the [Weboob](http://weboob.org/) project
+
diff --git a/modules/kiwibank/__init__.py b/modules/kiwibank/__init__.py
new file mode 100644
index 00000000..f12139c5
--- /dev/null
+++ b/modules/kiwibank/__init__.py
@@ -0,0 +1,23 @@
+# -*- coding: utf-8 -*-
+
+# Copyright(C) 2015 Cédric Félizard
+#
+# This file is part of weboob.
+#
+# weboob is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# weboob is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with weboob. If not, see .
+
+
+from .module import KiwibankModule
+
+__all__ = ['KiwibankModule']
diff --git a/modules/kiwibank/browser.py b/modules/kiwibank/browser.py
new file mode 100644
index 00000000..5fdea61b
--- /dev/null
+++ b/modules/kiwibank/browser.py
@@ -0,0 +1,62 @@
+# -*- coding: utf-8 -*-
+
+# Copyright(C) 2015 Cédric Félizard
+#
+# This file is part of weboob.
+#
+# weboob is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# weboob is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with weboob. If not, see .
+
+
+from weboob.browser import LoginBrowser, URL, need_login
+from weboob.exceptions import BrowserIncorrectPassword
+from .pages import LoginPage, AccountPage, HistoryPage
+
+
+__all__ = ['Kiwibank']
+
+
+class HistoryUnavailable(Exception):
+ pass
+
+
+class Kiwibank(LoginBrowser):
+ BASEURL = 'https://www.ib.kiwibank.co.nz/mobile/'
+ CERTHASH = ['5dc8be7430a2e37fab4dbfe232038ec60feed827d7ce0f68613532676962c197']
+ TIMEOUT = 30
+
+ login = URL('login/', LoginPage)
+ login_error = URL('login-error/', LoginPage)
+ accounts = URL('accounts/$', AccountPage)
+ account = URL('/accounts/view/[0-9A-F]+$', HistoryPage)
+
+ def do_login(self):
+ self.login.stay_or_go()
+ self.page.login(self.username, self.password)
+
+ if self.login.is_here() or self.login_error.is_here():
+ raise BrowserIncorrectPassword()
+
+ @need_login
+ def get_accounts(self):
+ self.accounts.stay_or_go()
+ return self.page.get_accounts()
+
+ @need_login
+ def get_history(self, account):
+ if account._link is None:
+ raise HistoryUnavailable()
+
+ self.location(account._link)
+
+ return self.page.get_history()
diff --git a/modules/kiwibank/favicon.png b/modules/kiwibank/favicon.png
new file mode 100644
index 00000000..1fab8861
Binary files /dev/null and b/modules/kiwibank/favicon.png differ
diff --git a/modules/kiwibank/module.py b/modules/kiwibank/module.py
new file mode 100644
index 00000000..f9ca1589
--- /dev/null
+++ b/modules/kiwibank/module.py
@@ -0,0 +1,55 @@
+# -*- coding: utf-8 -*-
+
+# Copyright(C) 2015 Cédric Félizard
+#
+# This file is part of weboob.
+#
+# weboob is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# weboob is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with weboob. If not, see .
+
+
+from weboob.capabilities.bank import CapBank, AccountNotFound
+from weboob.capabilities.base import find_object
+from weboob.tools.backend import Module, BackendConfig
+from weboob.tools.value import ValueBackendPassword
+from .browser import Kiwibank
+
+
+__all__ = ['KiwibankModule']
+
+
+class KiwibankModule(Module, CapBank):
+ NAME = 'kiwibank'
+ MAINTAINER = u'Cédric Félizard'
+ EMAIL = 'cedric@felizard.fr'
+ VERSION = '1.0.0'
+ LICENSE = 'AGPLv3+'
+ DESCRIPTION = u'Kiwibank'
+ CONFIG = BackendConfig(
+ ValueBackendPassword('username', label='Username', masked=False),
+ ValueBackendPassword('password', label='Password'),
+ )
+ BROWSER = Kiwibank
+
+ def create_default_browser(self):
+ return self.create_browser(self.config['username'].get(), self.config['password'].get())
+
+ def iter_accounts(self):
+ return self.browser.get_accounts()
+
+ def get_account(self, _id):
+ return find_object(self.browser.get_accounts(), id=_id, error=AccountNotFound)
+
+ def iter_history(self, account):
+ for transaction in self.browser.get_history(account):
+ yield transaction
diff --git a/modules/kiwibank/pages.py b/modules/kiwibank/pages.py
new file mode 100644
index 00000000..6a1f00d4
--- /dev/null
+++ b/modules/kiwibank/pages.py
@@ -0,0 +1,91 @@
+# -*- coding: utf-8 -*-
+
+# Copyright(C) 2015 Cédric Félizard
+#
+# This file is part of weboob.
+#
+# weboob is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# weboob is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with weboob. If not, see .
+
+
+import datetime
+import re
+from decimal import Decimal
+from weboob.browser.pages import HTMLPage, LoggedPage
+from weboob.capabilities.bank import Account
+from weboob.tools.capabilities.bank.transactions import AmericanTransaction as EnglishTransaction
+
+
+__all__ = ['LoginPage', 'AccountPage', 'HistoryPage']
+
+
+class LoginPage(HTMLPage):
+ def login(self, username, password):
+ form = self.get_form(name='aspnetForm')
+ form['ctl00$chi$txtUserName'] = username
+ form['ctl00$chi$txtPassword'] = password
+ form.submit()
+
+
+class AccountPage(LoggedPage, HTMLPage):
+ def get_accounts(self):
+ for el in self.doc.getroot().cssselect('div#content tr.row'):
+ account = Account()
+
+ balance = el.cssselect('td.Balance')[0].text
+ account.balance = Decimal(Transaction.clean_amount(balance))
+ account.id = el.cssselect('span')[0].text.strip()
+ account.currency = u'NZD' # TODO: handle other currencies
+
+ if el.cssselect('td.AccountName > a'):
+ label_el = el.cssselect('td.AccountName > a')[0]
+ account._link = label_el.get('href')
+ else:
+ label_el = el.cssselect('td.AccountName')[0]
+ account._link = None
+
+ account.label = unicode(label_el.text.strip())
+
+ yield account
+
+
+class HistoryPage(LoggedPage, HTMLPage):
+ def get_history(self):
+ # TODO: get more results from "next" page, only 15 transactions per page
+ for el in self.doc.getroot().cssselect('div#content tr.row'):
+ transaction = Transaction()
+
+ label = unicode(el.cssselect('td.tranDesc')[0].text)
+ transaction.label = label
+
+ for pattern, _type in Transaction.PATTERNS:
+ match = pattern.match(label)
+ if match:
+ transaction.type = _type
+ break
+
+ date = el.cssselect('td.tranDate')[0].text
+ transaction.date = datetime.datetime.strptime(date, '%d %b \'%y')
+
+ amount = el.cssselect('td.tranAmnt')[0].text
+ transaction.amount = Decimal(Transaction.clean_amount(amount))
+
+ yield transaction
+
+
+class Transaction(EnglishTransaction):
+ PATTERNS = [
+ (re.compile(r'^POS W/D (?P.*)'), EnglishTransaction.TYPE_CARD),
+ (re.compile(r'^ATM W/D (?P.*)'), EnglishTransaction.TYPE_WITHDRAWAL),
+ (re.compile(r'^(PAY|FROM) (?P.*)'), EnglishTransaction.TYPE_TRANSFER),
+ ]
diff --git a/modules/kiwibank/test.py b/modules/kiwibank/test.py
new file mode 100644
index 00000000..fcb428ad
--- /dev/null
+++ b/modules/kiwibank/test.py
@@ -0,0 +1,34 @@
+# -*- coding: utf-8 -*-
+
+# Copyright(C) 2015 Cédric Félizard
+#
+# This file is part of weboob.
+#
+# weboob is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# weboob is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with weboob. If not, see .
+
+
+from weboob.tools.test import BackendTest
+
+
+__all__ = ['KiwibankTest']
+
+
+class KiwibankTest(BackendTest):
+ MODULE = 'kiwibank'
+
+ def test_kiwibank(self):
+ l = list(self.backend.iter_accounts())
+ if len(l) > 0:
+ a = l[0]
+ list(self.backend.iter_history(a))