Skip to content

Commit

Permalink
Fixed issues #22 and #19
Browse files Browse the repository at this point in the history
  • Loading branch information
JakeUrban committed Dec 4, 2019
1 parent 5feabd7 commit ec923d5
Show file tree
Hide file tree
Showing 6 changed files with 77 additions and 87 deletions.
52 changes: 22 additions & 30 deletions polaris/polaris/deposit/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

from polaris import settings
from django.urls import reverse
from django.shortcuts import redirect
from django.views.decorators.clickjacking import xframe_options_exempt
from rest_framework import status
from rest_framework.decorators import api_view, renderer_classes
Expand Down Expand Up @@ -158,17 +159,20 @@ def interactive_deposit(request: Request) -> Response:
if not transaction_id:
return render_error_response("no 'transaction_id' provided", content_type="text/html")

try:
transaction = Transaction.objects.get(id=transaction_id)
except Transaction.objects.DoesNotExist:
return render_error_response(
"Transaction with ID not found",
content_type="text/html",
status_code=status.HTTP_404_NOT_FOUND
)

# GET: The server needs to display the form for the user to input the deposit information.
if request.method == "GET":
form = registered_deposit_integration.form()
return Response({"form": form}, template_name="deposit/form.html")

# POST: The user submitted a form with the amount to deposit.
if Transaction.objects.filter(id=transaction_id).exists():
return render_error_response(
"transaction with matching 'transaction_id' already exists",
content_type="text/html"
)
form = registered_deposit_integration.form(request.POST)
asset = Asset.objects.get(code=asset_code)
form.asset = asset
Expand All @@ -179,33 +183,13 @@ def interactive_deposit(request: Request) -> Response:
if hasattr(form, "after_validation") and callable(form.after_validation):
form.after_validation()

amount_in = form.cleaned_data["amount"]
amount_fee = calc_fee(asset, settings.OPERATION_DEPOSIT, amount_in)
transaction = Transaction(
id=transaction_id,
stellar_account=account,
asset=asset,
kind=Transaction.KIND.deposit,
status=Transaction.STATUS.pending_user_transfer_start,
amount_in=amount_in,
amount_fee=amount_fee,
to_address=account,
transaction.amount_in = form.cleaned_data["amount"]
transaction.amount_fee = calc_fee(
asset, settings.OPERATION_DEPOSIT, transaction.amount_in
)
transaction.save()

serializer = TransactionSerializer(
transaction,
context={"more_info_url": _construct_more_info_url(request)},
)
tx_json = json.dumps({"transaction": serializer.data})
return Response(
{
"tx_json": tx_json,
"transaction": transaction,
"asset_code": transaction.asset.code,
},
template_name="transaction/more_info.html",
)
return redirect(f"{reverse('more_info')}?{urlencode({'id': transaction_id})}")
else:
return Response({"form": form}, template_name="deposit/form.html")

Expand Down Expand Up @@ -244,6 +228,14 @@ def deposit(account: str, request: Request) -> Response:

# Construct interactive deposit pop-up URL.
transaction_id = create_transaction_id()
Transaction.objects.create(
id=transaction_id,
stellar_account=account,
asset=asset,
kind=Transaction.KIND.deposit,
status=Transaction.STATUS.pending_user_transfer_start,
to_address=account
)
url = _construct_interactive_url(request, asset_code, stellar_account, transaction_id)
return Response(
{"type": "interactive_customer_info_needed", "url": url, "id": transaction_id},
Expand Down
2 changes: 1 addition & 1 deletion polaris/polaris/fee/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ def fee(account: str, request: Request) -> Response:
amount_str = request.GET.get("amount")
try:
amount = float(amount_str)
except (TypeError, ValueError):
except (ValueError, TypeError):
return render_error_response("invalid 'amount'")

# Validate that the operation, and the specified type (if provided)
Expand Down
6 changes: 4 additions & 2 deletions polaris/polaris/tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@

from polaris.models import Asset, Transaction

STELLAR_ACCOUNT_1 = "GBCTKB22TYTLXHDWVENZGWMJWJ5YK2GTSF7LHAGMTSNAGLLSZVXRGXEW"
STELLAR_ACCOUNT_2 = "GAB4FHP66SOQ4L22WQGW7BQCHGWRFWXQ6MWBZV2YRVTXSK3QPNFOTM3T"
STELLAR_ACCOUNT_1 = "GAIRMDK7VDAXKXCX54UQ7WQUXZVITPBBYH33ADXQIADMDTDVJMQGBQ6V"
STELLAR_ACCOUNT_1_SEED = "SBB57BRFU7OFBVGUNJH4PMTQR72VCGKKFXBRQJJX7CHRSTZATAB5645L"
STELLAR_ACCOUNT_2 = "GAWGLF7Y6WFNPMFLIZ7AZU7TCHRRMTVKSB64XUSLJUGMXS3KFCOZXJWC"
STELLAR_ACCOUNT_2_SEED = "SAANDCFGMTWUQX27URREU47QL2HSJRCTB6YXZIOBHZJCAUBBEFJTGASY"


@pytest.fixture(scope="session", name="usd_asset_factory")
Expand Down
17 changes: 10 additions & 7 deletions polaris/polaris/tests/deposit_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from django.core.management import call_command

from polaris import settings
from polaris.tests.conftest import STELLAR_ACCOUNT_1_SEED
from polaris.management.commands.create_stellar_deposit import (
SUCCESS_XDR,
TRUSTLINE_FAILURE_XDR,
Expand Down Expand Up @@ -304,6 +305,7 @@ def test_deposit_confirm_success(
follow=True,
**header
)

assert response.status_code == 200
content = json.loads(response.content)
transaction = content["transaction"]
Expand Down Expand Up @@ -429,9 +431,7 @@ def test_deposit_stellar_success(
@pytest.mark.django_db
@patch("stellar_sdk.server.Server.fetch_base_fee", return_value=100)
@patch("stellar_sdk.server.Server.submit_transaction", return_value=HORIZON_SUCCESS_RESPONSE)
@patch("polaris.helpers.check_auth", side_effect=mock_check_auth_success)
def test_deposit_interactive_confirm_success(
mock_check,
mock_submit,
mock_base_fee,
client,
Expand All @@ -441,11 +441,16 @@ def test_deposit_interactive_confirm_success(
`GET /deposit` and `GET /transactions/deposit/webapp` succeed with valid `account`
and `asset_code`.
"""
del mock_check, mock_submit, mock_base_fee
del mock_submit, mock_base_fee
deposit = acc1_usd_deposit_transaction_factory()

encoded_jwt = sep10(client, deposit.stellar_account, STELLAR_ACCOUNT_1_SEED)
header = {"HTTP_AUTHORIZATION": f"Bearer {encoded_jwt}"}

response = client.post(
DEPOSIT_PATH, {"asset_code": "USD", "account": deposit.stellar_account},
follow=True
follow=True,
**header
)
content = json.loads(response.content)
assert response.status_code == 200
Expand All @@ -455,14 +460,12 @@ def test_deposit_interactive_confirm_success(
url = content["url"]
amount = 20
response = client.post(url, {"amount": amount})
assert response.status_code == 200
assert response.status_code == 302
assert (
Transaction.objects.get(id=transaction_id).status
== Transaction.STATUS.pending_user_transfer_start
)

encoded_jwt = sep10(client, client_address, client_seed)
header = {"HTTP_AUTHORIZATION": f"Bearer {encoded_jwt}"}
response = client.get(
f"/transactions/deposit/confirm_transaction?amount={amount}&transaction_id={transaction_id}",
follow=True,
Expand Down
12 changes: 6 additions & 6 deletions polaris/polaris/tests/withdraw_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ def test_withdraw_interactive_failure_no_memotype(
response = client.post(
url, {"amount": 20, "bank_account": "123456", "bank": "Bank"}
)
assert response.status_code == 200
assert response.status_code == 302
assert (
Transaction.objects.get(id=transaction_id).status
== Transaction.STATUS.pending_user_transfer_start
Expand Down Expand Up @@ -154,7 +154,7 @@ def test_withdraw_interactive_failure_incorrect_memotype(
response = client.post(
url, {"amount": 20, "bank_account": "123456", "bank": "Bank"}
)
assert response.status_code == 200
assert response.status_code == 302
assert (
Transaction.objects.get(id=transaction_id).status
== Transaction.STATUS.pending_user_transfer_start
Expand Down Expand Up @@ -184,7 +184,7 @@ def test_withdraw_interactive_failure_no_memo(
response = client.post(
url, {"amount": 20, "bank_account": "123456", "bank": "Bank"}
)
assert response.status_code == 200
assert response.status_code == 302
assert (
Transaction.objects.get(id=transaction_id).status
== Transaction.STATUS.pending_user_transfer_start
Expand Down Expand Up @@ -214,7 +214,7 @@ def test_withdraw_interactive_failure_incorrect_memo(
response = client.post(
url, {"amount": 20, "bank_account": "123456", "bank": "Bank"}
)
assert response.status_code == 200
assert response.status_code == 302
assert (
Transaction.objects.get(id=transaction_id).status
== Transaction.STATUS.pending_user_transfer_start
Expand All @@ -241,7 +241,7 @@ def test_withdraw_interactive_success_transaction_unsuccessful(
response = client.post(
url, {"amount": 50, "bank_account": "123456", "bank": "Bank"}
)
assert response.status_code == 200
assert response.status_code == 302
transaction = Transaction.objects.get(id=transaction_id)
assert transaction.status == Transaction.STATUS.pending_user_transfer_start

Expand Down Expand Up @@ -280,7 +280,7 @@ def test_withdraw_interactive_success_transaction_successful(
response = client.post(
url, {"amount": 50, "bank_account": "123456", "bank": "Bank"}
)
assert response.status_code == 200
assert response.status_code == 302
transaction = Transaction.objects.get(id=transaction_id)
assert transaction.status == Transaction.STATUS.pending_user_transfer_start

Expand Down
75 changes: 34 additions & 41 deletions polaris/polaris/withdraw/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,14 @@

from polaris import settings
from django.urls import reverse
from django.core.exceptions import ValidationError
from django.views.decorators.clickjacking import xframe_options_exempt
from django.shortcuts import redirect
from rest_framework.decorators import api_view, renderer_classes
from rest_framework.response import Response
from rest_framework.request import Request
from rest_framework.renderers import TemplateHTMLRenderer, JSONRenderer
from rest_framework import status

from polaris.helpers import (
render_error_response,
Expand Down Expand Up @@ -66,18 +69,21 @@ def interactive_withdraw(request: Request) -> Response:
if not asset_code or not Asset.objects.filter(code=asset_code).exists():
return render_error_response("invalid 'asset_code'", content_type="text/html")

try:
transaction = Transaction.objects.get(id=transaction_id)
except (Transaction.DoesNotExist, ValidationError):
return render_error_response(
"Transaction with ID not found",
content_type="text/html",
status_code=status.HTTP_404_NOT_FOUND
)

# GET: The server needs to display the form for the user to input withdrawal information.
if request.method == "GET":
form = registered_withdrawal_integration.form()
resp_data = {"form": form, "account": request.GET.get("account")}
return Response(resp_data, template_name="withdraw/form.html")

# POST: The user submitted a form with the withdrawal info.
if Transaction.objects.filter(id=transaction_id).exists():
return render_error_response(
"transaction with matching 'transaction_id' already exists",
content_type="text/html"
)
form = registered_withdrawal_integration.form(request.POST)
asset = Asset.objects.get(code=asset_code)
form.asset = asset
Expand All @@ -89,44 +95,13 @@ def interactive_withdraw(request: Request) -> Response:
if hasattr(form, "after_validation") and callable(form.after_validation):
form.after_validation()

amount_in = form.cleaned_data["amount"]
amount_fee = calc_fee(asset, settings.OPERATION_WITHDRAWAL, amount_in)

# We use the transaction ID as a memo on the Stellar transaction for the
# payment in the withdrawal. This lets us identify that as uniquely
# corresponding to this `Transaction` in the database. But a UUID4 is a 32
# character hex string, while the Stellar HashMemo requires a 64 character
# hex-encoded (32 byte) string. So, we zero-pad the ID to create an
# appropriately sized string for the `HashMemo`.
transaction_id_hex = uuid.UUID(transaction_id).hex
withdraw_memo = "0" * (64 - len(transaction_id_hex)) + transaction_id_hex
transaction = Transaction(
id=transaction_id,
stellar_account=request.POST.get("account"),
asset=asset,
kind=Transaction.KIND.withdrawal,
status=Transaction.STATUS.pending_user_transfer_start,
amount_in=amount_in,
amount_fee=amount_fee,
withdraw_anchor_account=settings.STELLAR_DISTRIBUTION_ACCOUNT_ADDRESS,
withdraw_memo=withdraw_memo,
withdraw_memo_type=Transaction.MEMO_TYPES.hash,
transaction.amount_in = form.cleaned_data["amount"]
transaction.amount_fee = calc_fee(
asset, settings.OPERATION_WITHDRAWAL, transaction.amount_in
)
transaction.save()

serializer = TransactionSerializer(
transaction,
context={"more_info_url": _construct_more_info_url(request)},
)
tx_json = json.dumps({"transaction": serializer.data})
return Response(
{
"tx_json": tx_json,
"transaction": transaction,
"asset_code": asset_code,
},
template_name="transaction/more_info.html"
)
return redirect(f"{reverse('more_info')}?{urlencode({'id': transaction_id})}")
else:
resp_data = {"form": form, "account": request.POST.get("account")}
return Response(resp_data, template_name="withdraw/form.html")
Expand All @@ -151,7 +126,25 @@ def withdraw(account: str, request: Request) -> Response:
if not asset or not asset.withdrawal_enabled:
return render_error_response(f"invalid operation for asset {asset_code}")

# We use the transaction ID as a memo on the Stellar transaction for the
# payment in the withdrawal. This lets us identify that as uniquely
# corresponding to this `Transaction` in the database. But a UUID4 is a 32
# character hex string, while the Stellar HashMemo requires a 64 character
# hex-encoded (32 byte) string. So, we zero-pad the ID to create an
# appropriately sized string for the `HashMemo`.
transaction_id = create_transaction_id()
transaction_id_hex = transaction_id.hex
withdraw_memo = "0" * (64 - len(transaction_id_hex)) + transaction_id_hex
Transaction.objects.create(
id=transaction_id,
stellar_account=account,
asset=asset,
kind=Transaction.KIND.withdrawal,
status=Transaction.STATUS.pending_user_transfer_start,
withdraw_anchor_account=settings.STELLAR_DISTRIBUTION_ACCOUNT_ADDRESS,
withdraw_memo=withdraw_memo,
withdraw_memo_type=Transaction.MEMO_TYPES.hash,
)
url = _construct_interactive_url(request, asset_code, transaction_id, account)
return Response(
{"type": "interactive_customer_info_needed", "url": url, "id": transaction_id},
Expand Down

0 comments on commit ec923d5

Please sign in to comment.