Newer
Older
# -*- coding: utf-8 -*-
# Copyright(C) 2018 Roger Philibert
#
# This file is part of weboob.
#
# weboob is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with weboob. If not, see <http://www.gnu.org/licenses/>.
from __future__ import unicode_literals
from datetime import date, timedelta
from weboob.browser.filters.standard import (
CleanDecimal, CleanText, DateTime, Currency,
Format,
)
from weboob.capabilities.base import empty
from weboob.browser.filters.json import Dict
from weboob.browser.exceptions import ClientError
from weboob.exceptions import BrowserIncorrectPassword
from weboob.browser.browsers import APIBrowser
from weboob.capabilities.bank import Account, Transaction
class SwileBrowser(APIBrowser):
BASEURL = 'https://customer-api.swile.co'
def __init__(self, login, password, *args, **kwargs):
"""SwileBrowser needs login and password to fetch Swile API"""
super(SwileBrowser, self).__init__(*args, **kwargs)
# self.session.headers are the HTTP headers for Swile API requests
self.session.headers['x-api-key'] = '644a4ef497286a229aaf8205c2dc12a9086310a8'
self.session.headers['x-lunchr-app-version'] = 'b6c6ca66c79ca059222779fe8f1ac98c8485b9f0'
self.session.headers['x-lunchr-platform'] = 'web'
# self.credentials is the HTTP POST data used in self._auth()
self.credentials = {
'user': {
'email': login,
'password': password,
}
}
"""Authenticate to Swile API using self.credentials.
If authentication succeeds, authorization header is set in self.headers
and response's json payload is returned unwrapped into dictionary.
"""
try:
response = self.open('/api/v0/users/login', data=self.credentials)
except ClientError as e:
json = e.response.json()
if e.response.status_code == 401:
message = json['result']['error']['message']
raise BrowserIncorrectPassword(message)
raise e
json = Dict('user')(response.json())
self.session.headers['Authorization'] = 'Bearer ' + Dict('token')(json)
return json
json = self._auth()
account = Account(id=Dict('id')(json))
# Check if account have a card
balance = Dict('meal_voucher_info/balance/value', default=None)(json)
if empty(balance):
return
account.balance = CleanDecimal.SI(balance)(json)
account.label = Format('%s %s', CleanText(Dict('first_name')), CleanText(Dict('last_name')))(json)
account.currency = Currency(Dict('meal_voucher_info/balance/currency/iso_3'))(json)
account.cardlimit = CleanDecimal.SI(Dict('meal_voucher_info/daily_balance/value'))(json)
yield account
# make sure we have today's transactions
before = date.today() + timedelta(days=1)
for page in range(200): # limit pagination
response = self.open(
'/api/v0/payments_history',
params={
'per': 20,
'before': before.isoformat(),
# don't pass page= param, it works but
# it's slower than the before= param
},
)
json = response.json()
if len(Dict('payments_history')(json)) == 0:
break
for payment in Dict('payments_history')(json):
if 'refunding_transaction' in payment:
refund = self._parse_transaction(payment['refunding_transaction'])
refund.type = Transaction.TYPE_CARD
yield refund
transaction = self._parse_transaction(payment)
if transaction:
# this is a millisecond-precise datetime (with a timezone).
# fortunately, the api excludes transactions occuring at the exact datetime we pass.
# if the page boundary is hit on transactions occurring at the same datetime, we might lose some of them though.
before = transaction.date
else:
raise Exception("that's a lot of transactions, probable infinite loop?")
def _parse_transaction(self, payment):
transaction = Transaction()
transaction_id = Dict('transaction_number', default=None)(payment)
# Check if transaction_id is None which indicates failed transaction
if transaction_id is None:
return
transaction.id = transaction_id
transaction.date = DateTime(Dict('executed_at'))(payment)
transaction.rdate = DateTime(Dict('created_at'))(payment)
types = {
'ORDER': Transaction.TYPE_CARD, # order on swile website
'LUNCHR_CARD_PAYMENT': Transaction.TYPE_CARD, # pay in shop
'MEAL_VOUCHER_CREDIT': Transaction.TYPE_DEPOSIT,
# type can be null for refunds
}
transaction.type = types.get(Dict('type')(payment), Transaction.TYPE_UNKNOWN)
transaction.label = Dict('name')(payment)
transaction.amount = CleanDecimal(Dict('amount/value'))(payment)
return transaction