def post_interactive_withdraw(request: Request) -> Response: """ POST /transactions/withdraw/webapp This endpoint processes form submissions during the withdraw 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 = rwi.content_for_transaction(transaction) if not (content and content.get("form")): logger.error( "Initial content_for_transaction() call returned None " f"for {transaction.id}" ) return render_error_response( _("The anchor did not provide a content, unable to serve page."), status_code=500, content_type="text/html", ) form_class = content.get("form") form = form_class(request.POST) is_transaction_form = issubclass(form_class, TransactionForm) if is_transaction_form: form.asset = asset if form.is_valid(): if is_transaction_form: fee_params = { "operation": settings.OPERATION_WITHDRAWAL, "asset_code": asset.code, **form.cleaned_data, } transaction.amount_in = form.cleaned_data["amount"] transaction.amount_fee = registered_fee_func(fee_params) transaction.save() rwi.after_form_validation(form, transaction) content = rwi.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_withdraw") 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="withdraw/form.html")
def get_interactive_withdraw(request: Request) -> Response: """ GET /transactions/withdraw/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/withdraw/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 = rwi.interactive_url(request, transaction, asset, amount, callback) if url: # The anchor uses a standalone interactive flow return redirect(url) content = rwi.content_for_transaction(transaction) if not content: logger.error("The anchor did not provide a form, unable to serve page.") return render_error_response( _("The anchor did not provide a form, unable to serve page."), status_code=500, content_type="text/html", ) if content.get("form"): form_class = content.pop("form") if issubclass(form_class, TransactionForm) and amount: content["form"] = form_class({"amount": amount}) else: content["form"] = form_class() 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_withdraw')}?{urlencode(url_args)}" get_url = f"{reverse('get_interactive_withdraw')}?{urlencode(url_args)}" content.update( post_url=post_url, get_url=get_url, scripts=registered_javascript_func() ) return Response(content, template_name="withdraw/form.html")