Example #1
0
def deposit(account: str, request: Request) -> Response:
    """
    `POST /transactions/deposit/interactive` initiates the deposit and returns an interactive
    deposit form to the user.
    """
    asset_code = request.POST.get("asset_code")
    stellar_account = request.POST.get("account")

    # Verify that the request is valid.
    if not all([asset_code, stellar_account]):
        return render_error_response(
            "`asset_code` and `account` are required parameters")

    # Verify that the asset code exists in our database, with deposit enabled.
    asset = Asset.objects.filter(code=asset_code).first()
    if not asset or not asset.deposit_enabled:
        return render_error_response(
            f"invalid operation for asset {asset_code}")

    try:
        Keypair.from_public_key(stellar_account)
    except Ed25519PublicKeyInvalidError:
        return render_error_response("invalid 'account'")

    # Verify the optional request arguments.
    verify_optional_args = _verify_optional_args(request)
    if verify_optional_args:
        return verify_optional_args

    # 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.incomplete,
        to_address=account,
    )
    url = rdi.interactive_url(request, str(transaction_id), stellar_account,
                              asset_code)
    return Response(
        {
            "type": "interactive_customer_info_needed",
            "url": url,
            "id": transaction_id
        },
        status=status.HTTP_200_OK,
    )
Example #2
0
def get_interactive_deposit(request: Request) -> Response:
    """
    GET /transactions/deposit/webapp

    This endpoint retrieves the next form to be served to the user in the
    interactive flow. The following steps are taken during this process:

        1. URL arguments are parsed and validated.
        2. interactive_url() is called to determine whether or not the anchor
           uses an external service for the interactive flow. If a URL is
           returned, this function redirects to the URL. However, the session
           cookie should still be included in the response so future calls to
           GET /transactions/deposit/interactive/complete are authenticated.
        3. content_for_transaction() is called to retrieve the next form to
           render to the user. `amount` is prepopulated in the form if it was
           passed as a parameter to this endpoint and the form is a subclass
           of TransactionForm.
        4. get and post URLs are constructed with the appropriate arguments
           and passed to the response to be rendered to the user.
    """
    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"]

    url = rdi.interactive_url(request, transaction, asset, amount, callback)
    if url:  # The anchor uses a standalone interactive flow
        return redirect(url)

    content = rdi.content_for_transaction(transaction)
    if not content:
        logger.error(
            "The anchor did not provide content, unable to serve page.")
        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 content, unable to serve page."),
            status_code=500,
            content_type="text/html",
        )

    scripts = registered_scripts_func(content)

    if content.get("form"):
        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."),
                content_type="text/html",
            )
        is_transaction_form = issubclass(form_class, TransactionForm)
        if is_transaction_form:
            content["form"] = form_class(asset,
                                         initial={"amount": amount},
                                         test_value="103",
                                         **form_args)
        else:
            content["form"] = form_class(**form_args)

    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(post_url=post_url, get_url=get_url, scripts=scripts)

    return Response(content, template_name="deposit/form.html")
Example #3
0
def get_interactive_deposit(request: Request) -> Response:
    """
    GET /transactions/deposit/webapp

    This endpoint retrieves the next form to be served to the user in the
    interactive flow. The following steps are taken during this process:

        1. URL arguments are parsed and validated.
        2. interactive_url() is called to determine whether or not the anchor
           uses an external service for the interactive flow. If a URL is
           returned, this function redirects to the URL. However, the session
           cookie should still be included in the response so future calls to
           GET /transactions/deposit/interactive/complete are authenticated.
        3. form_for_transaction() is called to retrieve the next form to
           render to the user.
        4. get and post URLs are constructed with the appropriate arguments
           and passed to the response to be rendered to the user.
    """
    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"]

    url = rdi.interactive_url(request, transaction, asset, amount, callback)
    if url:  # The anchor uses a standalone interactive flow
        return redirect(url)

    form = rdi.form_for_transaction(transaction, amount=amount)
    content = rdi.content_for_template(Template.DEPOSIT,
                                       form=form,
                                       transaction=transaction)
    if not (form or content):
        logger.error(
            "The anchor did not provide content, unable to serve page.")
        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 content, unable to serve page."),
            status_code=500,
            content_type="text/html",
        )
    elif content is None:
        content = {}

    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.")
    if form:
        template_scripts = registered_scripts_func({"form": form, **content})
    else:
        template_scripts = registered_scripts_func(content)

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

    toml_data = registered_toml_func()
    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,
        org_logo_url=toml_data.get("DOCUMENTATION", {}).get("ORG_LOGO"),
    )

    return Response(content, template_name="polaris/deposit.html")