summaryrefslogtreecommitdiff
path: root/talerbank/app/funds.py
blob: e68e9f5ab6c559a2a884d96e3a9e994fff293125 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
#  This file is part of TALER
#  (C) 2014, 2015, 2016 INRIA
#
#  TALER 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, or (at your option) any later version.
#
#  TALER 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 General Public License for more details.
#
#  You should have received a copy of the GNU General Public License along with
#  TALER; see the file COPYING.  If not, If not, see <http://www.gnu.org/licenses/>
#
#  @author Marcello Stanisci

from . import schemas
from .errors import WrongMethod, BadWireDetails
from . import amounts
from .models import BankAccount, History
from django.views.decorators.csrf import csrf_exempt
from django.http import JsonResponse
from django.shortcuts import render, redirect
from django.http import HttpResponseServerError
from django.contrib.auth.decorators import login_required
from urllib.parse import urlparse, urljoin
import requests
import time
import json
import logging

logger = logging.getLogger(__name__)



def check_exchange_account_no(account_no):
    try:
        BankAccount.objects.get(account_no=account_no)
    except BankAccount.DoesNotExist:
        raise errors.ExchangeUnknown()


class Reserve:
    def __init__(self, amount, exchange, exchange_account, reserve_pub, wire_details, wiredetails_counter=0):
        schemas.validate_amount(amount)
        self.amount = amount
        self.exchange = exchange
        self.exchange_account = exchange_account
        self.reserve_pub = reserve_pub
        self.wire_details = wire_details
        self.wire_details['uid'] = wiredetails_counter

# The CSRF exempt is due to the fact Django looks for an anti-CSRF token
# In any POST it gets. Since the following function is meant to serve mints,
# And mints don't send any such token, it is necessary to disable it.
# Those tokens are normally hidden fields in Django-generated HTML forms.


@csrf_exempt
def add_incoming(request):
    if request.method != 'POST':
        raise WrongMethod('GET')
    data = json.loads(request.body.decode('utf-8'))
    schemas.validate_incoming_request(data)
    logger.info("add_incoming for debit account %s and credit accout %s", data['debit_account'], data['credit_account'])

    wire_transfer_in_out(data['amount'],
                         data['debit_account'],
                         data['credit_account'],
                         data['wtid'])
    return JsonResponse({'outcome': 'ok'}, status=200)


@login_required
def withdraw_process(request):
    return render(request, 'withdraw.html', {'account_no': request.session["account_no"]})


def create_reserve_at_exchange(request, success_url, reserve_set):
    if not isinstance(reserve_set, Reserve):
        return HttpResponseServerError
    o = urlparse(reserve_set.exchange)
    if o.scheme == '' or o.netloc == '' or o.path != '/':
        return JsonResponse({'error': 'bad exchange base url',
                             'given_url': reserve_set.mint}, status=400)
    amount = {'value': reserve_set.amount['value'],
              'fraction': reserve_set.amount['fraction'],
              'currency': reserve_set.amount['currency']}
    # Should raise ExchangeUnknown ..
    check_exchange_account_no(reserve_set.exchange_account)
    json_body = {'reserve_pub': reserve_set.reserve_pub,
                 'execution_date': "/Date(" + str(int(time.time())) + ")/",
                 'wire': reserve_set.wire_details,
                 'amount': amount}
    res = requests.post(urljoin(reserve_set.exchange, '/admin/add/incoming'), json=json_body)
    logger.info("making request to /admin/add/incoming with %s", json_body)
    if res.status_code != 200:
        return JsonResponse({'error': "internal error",
                             'hint': "wire transfer failed (bad response)",
                             'status': res.status_code,
                             'body': res.text}, status=500)
    wire_transfer_in_out(amount,
                         request.session['account_no'],
                         reserve_set.exchange_account,
                         "withdrawal")
    request.session['withdrawal_successful'] = True
    return redirect('/profile')



def wire_transfer_in_out(amount,
                         debit,
                         credit,
                         wtid):
    if debit == credit:
        raise BadWireDetails(hint="debit and credit accounts are the same")
    debit_account = BankAccount.objects.get(account_no=debit)
    credit_account = BankAccount.objects.get(account_no=credit)
    wire_transfer(amount,
                  debit,
                  "OUT",
                  wtid,
                  credit_account.user.username + " (account #" + str(credit) + ")")
    wire_transfer(amount,
                  credit,
                  "IN",
                  wtid,
                  debit_account.user.username + " (account #" + str(debit) + ")")


# transfer funds from/to 'account_no' (used as a subroutine)
def wire_transfer(amount,
                  account_no,
                  direction,
                  wtid="not given",
                  counterpart="unknown"):
    account = BankAccount.objects.get(account_no=account_no)
    if account.currency != amount['currency']:
        logger.error("currency %s and currency %s mismatch", account.currency, amount['currency'])
        raise errors.CurrencyMismatch()
    float_amount = amounts.floatify(amount)
    if "IN" == direction:
        account.balance += float_amount
    else:
        account.balance -= float_amount
    account.save()
    history_item = History(amount=float_amount,
                           currency=amount['currency'],
                           direction=direction,
                           counterpart=counterpart,
                           subject=wtid,
                           account=account)
    history_item.save()