Esempio n. 1
0
def post_interactive_deposit(request: Request) -> Response:
    """
    """
    transaction, asset, error_resp = interactive_args_validation(request)
    if error_resp:
        return error_resp

    form = rdi.form_for_transaction(transaction)(request.POST)
    is_transaction_form = issubclass(form.__class__, TransactionForm)
    if is_transaction_form:
        form.asset = asset

    if form.is_valid():
        if is_transaction_form:
            transaction.amount_in = form.cleaned_data["amount"]
            transaction.amount_fee = calc_fee(asset,
                                              settings.OPERATION_DEPOSIT,
                                              transaction.amount_in)
            transaction.save()

        # Perform any defined post-validation logic defined by Polaris users
        rdi.after_form_validation(form, transaction)
        # Check to see if there is another form to render
        form_class = rdi.form_for_transaction(transaction)

        if form_class:
            args = {"transaction_id": transaction.id, "asset_code": asset.code}
            url = reverse("get_interactive_deposit")
            return redirect(f"{url}?{urlencode(args)}")
        else:  # Last form has been submitted
            invalidate_session(request)
            transaction.status = Transaction.STATUS.pending_user_transfer_start
            transaction.save()
            url, args = reverse("more_info"), urlencode({"id": transaction.id})
            return redirect(f"{url}?{args}")

    else:
        return Response({"form": form}, template_name="deposit/form.html")
Esempio n. 2
0
def post_interactive_deposit(request: Request) -> Response:
    """
    POST /transactions/deposit/webapp

    This endpoint processes form submissions during the deposit interactive
    flow. The following steps are taken during this process:

        1. URL arguments are parsed and validated.
        2. content_for_transaction() is called to retrieve the form used to
           submit this request. This function is implemented by the anchor.
        3. The form is used to validate the data submitted, and if the form
           is a TransactionForm, the fee for the transaction is calculated.
        4. after_form_validation() is called to allow the anchor to process
           the data submitted. This function should change the application
           state such that the next call to content_for_transaction() returns
           the next form in the flow.
        5. content_for_transaction() is called again to retrieve the next
           form to be served to the user. If a form is returned, the
           function redirects to GET /transaction/deposit/webapp. Otherwise,
           The user's session is invalidated, the transaction status is
           updated, and the function redirects to GET /more_info.
    """
    args_or_error = interactive_args_validation(request)
    if "error" in args_or_error:
        return args_or_error["error"]

    transaction = args_or_error["transaction"]
    asset = args_or_error["asset"]
    callback = args_or_error["callback"]
    amount = args_or_error["amount"]

    content = rdi.content_for_transaction(transaction)
    if not (content and content.get("form")):
        logger.error("Initial content_for_transaction() call returned None in "
                     f"POST request for transaction: {transaction.id}")
        if transaction.status != transaction.STATUS.incomplete:
            return render_error_response(
                _("The anchor did not provide content, is the interactive flow already complete?"
                  ),
                status_code=422,
                content_type="text/html",
            )
        return render_error_response(
            _("The anchor did not provide form content, unable to serve page."
              ),
            status_code=500,
            content_type="text/html",
        )

    try:
        form_class, form_args = content.get("form")
    except TypeError:
        logger.exception(
            "content_for_transaction(): 'form' key value must be a tuple")
        return render_error_response(
            _("The anchor did not provide content, unable to serve page."),
            status_code=500,
            content_type="text/html",
        )

    is_transaction_form = issubclass(form_class, TransactionForm)
    if is_transaction_form:
        form = form_class(asset, request.POST, **form_args)
    else:
        form = form_class(request.POST, **form_args)

    if form.is_valid():
        if is_transaction_form:
            fee_params = {
                "operation": settings.OPERATION_DEPOSIT,
                "asset_code": asset.code,
                **form.cleaned_data,
            }
            transaction.amount_in = form.cleaned_data["amount"]
            transaction.amount_fee = registered_fee_func(fee_params)
            transaction.save()

        rdi.after_form_validation(form, transaction)
        content = rdi.content_for_transaction(transaction)
        if content:
            args = {"transaction_id": transaction.id, "asset_code": asset.code}
            if amount:
                args["amount"] = amount
            if callback:
                args["callback"] = callback
            url = reverse("get_interactive_deposit")
            return redirect(f"{url}?{urlencode(args)}")
        else:  # Last form has been submitted
            logger.info(
                f"Finished data collection and processing for transaction {transaction.id}"
            )
            invalidate_session(request)
            transaction.status = Transaction.STATUS.pending_user_transfer_start
            transaction.save()
            url = reverse("more_info")
            args = urlencode({"id": transaction.id, "callback": callback})
            return redirect(f"{url}?{args}")

    else:
        content.update(form=form)
        return Response(content, template_name="deposit/form.html", status=422)
Esempio n. 3
0
def post_interactive_deposit(request: Request) -> Response:
    """
    POST /transactions/deposit/webapp

    This endpoint processes form submissions during the deposit interactive
    flow. The following steps are taken during this process:

        1. URL arguments are parsed and validated.
        2. form_for_transaction() is called to retrieve the form used to
           submit this request. This function is implemented by the anchor.
        3. The form is used to validate the data submitted, and if the form
           is a TransactionForm, the fee for the transaction is calculated.
        4. after_form_validation() is called to allow the anchor to process
           the data submitted. This function should change the application
           state such that the next call to form_for_transaction() returns
           the next form in the flow.
        5. form_for_transaction() is called again to retrieve the next
           form to be served to the user. If a form is returned, the
           function redirects to GET /transaction/deposit/webapp. Otherwise,
           The user's session is invalidated, the transaction status is
           updated, and the function redirects to GET /more_info.
    """
    args_or_error = interactive_args_validation(request)
    if "error" in args_or_error:
        return args_or_error["error"]

    transaction = args_or_error["transaction"]
    asset = args_or_error["asset"]
    callback = args_or_error["callback"]
    amount = args_or_error["amount"]

    form = rdi.form_for_transaction(transaction, post_data=request.POST)
    if not form:
        logger.error("Initial form_for_transaction() call returned None in "
                     f"POST request for transaction: {transaction.id}")
        if transaction.status != transaction.STATUS.incomplete:
            return render_error_response(
                _("The anchor did not provide content, is the interactive flow already complete?"
                  ),
                status_code=422,
                content_type="text/html",
            )
        return render_error_response(
            _("The anchor did not provide form content, unable to serve page."
              ),
            status_code=500,
            content_type="text/html",
        )

    if not form.is_bound:
        # The anchor must initialize the form with the request.POST data
        logger.error(
            "form returned was not initialized with POST data, returning 500")
        return render_error_response(
            _("Unable to validate form submission."),
            status_code=500,
            content_type="text/html",
        )

    if form.is_valid():
        if issubclass(form.__class__, TransactionForm):
            transaction.amount_in = form.cleaned_data["amount"]
            transaction.save()

        rdi.after_form_validation(form, transaction)
        next_form = rdi.form_for_transaction(transaction)
        if next_form or rdi.content_for_template(
                Template.DEPOSIT, form=next_form, transaction=transaction):
            args = {"transaction_id": transaction.id, "asset_code": asset.code}
            if amount:
                args["amount"] = amount
            if callback:
                args["callback"] = callback
            url = reverse("get_interactive_deposit")
            return redirect(f"{url}?{urlencode(args)}")

        else:  # Last form has been submitted
            logger.info(
                f"Finished data collection and processing for transaction {transaction.id}"
            )
            invalidate_session(request)
            transaction.status = Transaction.STATUS.pending_user_transfer_start
            transaction.save()
            url = reverse("more_info")
            args = urlencode({"id": transaction.id, "callback": callback})
            return redirect(f"{url}?{args}")

    else:
        content = (rdi.content_for_template(
            Template.DEPOSIT, form=form, transaction=transaction) or {})
        if registered_scripts_func is not scripts:
            logger.warning(
                "DEPRECATED: the `scripts` Polaris integration function will be "
                "removed in Polaris 2.0 in favor of allowing the anchor to override "
                "and extend Polaris' Django templates. See the Template Extensions "
                "documentation for more information.")
        template_scripts = registered_scripts_func({"form": form, **content})

        url_args = {"transaction_id": transaction.id, "asset_code": asset.code}
        if callback:
            url_args["callback"] = callback
        if amount:
            url_args["amount"] = amount

        post_url = f"{reverse('post_interactive_deposit')}?{urlencode(url_args)}"
        get_url = f"{reverse('get_interactive_deposit')}?{urlencode(url_args)}"
        content.update(
            form=form,
            post_url=post_url,
            get_url=get_url,
            scripts=template_scripts,
            operation=settings.OPERATION_DEPOSIT,
            asset=asset,
            use_fee_endpoint=registered_fee_func != calculate_fee,
        )
        return Response(content,
                        template_name="polaris/deposit.html",
                        status=422)
Esempio n. 4
0
def interactive_deposit(request: Request) -> Response:
    """
    """
    # Validate query parameters: account, asset_code, transaction_id.
    asset_code = request.GET.get("asset_code")
    asset = Asset.objects.filter(code=asset_code).first()
    transaction_id = request.GET.get("transaction_id")
    if not (asset_code and asset):
        err_msg = "invalid 'asset_code'"
        return render_error_response(err_msg, content_type="text/html")
    elif not transaction_id:
        err_msg = "no 'transaction_id' provided"
        return render_error_response(err_msg, content_type="text/html")

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

    if request.method == "GET":
        err_resp = check_middleware()
        if err_resp:
            return err_resp

        form = rdi.form_for_transaction(transaction)()
        return Response({"form": form}, template_name="deposit/form.html")

    # request.method == "POST"
    form = rdi.form_for_transaction(transaction)(request.POST)
    is_transaction_form = issubclass(form.__class__, TransactionForm)
    if is_transaction_form:
        form.asset = asset

    if form.is_valid():
        if is_transaction_form:
            transaction.amount_in = form.cleaned_data["amount"]
            transaction.amount_fee = calc_fee(asset,
                                              settings.OPERATION_DEPOSIT,
                                              transaction.amount_in)
            transaction.save()

        # Perform any defined post-validation logic defined by Polaris users
        rdi.after_form_validation(form, transaction)
        # Check to see if there is another form to render
        form_class = rdi.form_for_transaction(transaction)

        if form_class:
            return Response({"form": form_class()},
                            template_name="deposit/form.html")
        else:  # Last form has been submitted
            invalidate_session(request)
            transaction.status = Transaction.STATUS.pending_user_transfer_start
            transaction.save()
            url, args = reverse("more_info"), urlencode({"id": transaction_id})
            return redirect(f"{url}?{args}")

    else:
        return Response({"form": form}, template_name="deposit/form.html")