def withdraw(request): """ `GET /withdraw` initiates the withdrawal and returns an interactive withdrawal form to the user. """ asset_code = request.GET.get("asset_code") if not asset_code: return render_error_response("'asset_code' is required") # TODO: Verify optional arguments. # Verify that the asset code exists in our database, with withdraw enabled. asset = Asset.objects.filter(code=asset_code).first() if not asset or not asset.withdrawal_enabled: return render_error_response( f"invalid operation for asset {asset_code}") transaction_id = create_transaction_id() url = _construct_interactive_url(request, transaction_id) return Response( { "type": "interactive_customer_info_needed", "url": url, "id": transaction_id }, status=status.HTTP_403_FORBIDDEN, )
def deposit(account: str, request: Request) -> Response: """ POST /transactions/deposit/interactive Creates an `incomplete` deposit Transaction object in the database and returns the URL entry-point for the interactive flow. """ asset_code = request.POST.get("asset_code") stellar_account = request.POST.get("account") lang = request.POST.get("lang") if lang: err_resp = validate_language(lang) if err_resp: return err_resp activate_lang_for_request(lang) # 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: return render_error_response(_("unknown asset: %s") % asset_code) elif not asset.deposit_enabled: return render_error_response(_("invalid operation for asset %s") % asset_code) try: Keypair.from_public_key(stellar_account) except Ed25519PublicKeyInvalidError: return render_error_response(_("invalid 'account'")) # 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, ) logger.info(f"Created deposit transaction {transaction_id}") url = interactive_url( request, str(transaction_id), stellar_account, asset_code, settings.OPERATION_DEPOSIT, ) return Response( {"type": "interactive_customer_info_needed", "url": url, "id": transaction_id}, status=status.HTTP_200_OK, )
def withdraw(account: str, request: Request) -> Response: """ POST /transactions/withdraw/interactive Creates an `incomplete` withdraw Transaction object in the database and returns the URL entry-point for the interactive flow. """ lang = request.POST.get("lang") asset_code = request.POST.get("asset_code") if lang: err_resp = validate_language(lang) if err_resp: return err_resp activate_lang_for_request(lang) if not asset_code: return render_error_response(_("'asset_code' is required")) # Verify that the asset code exists in our database, with withdraw enabled. asset = Asset.objects.filter(code=asset_code).first() if not asset or not asset.withdrawal_enabled: return render_error_response( _("invalid operation for asset %s") % asset_code) elif asset.code not in settings.ASSETS: return render_error_response( _("unsupported asset type: %s") % asset_code) distribution_address = settings.ASSETS[ asset.code]["DISTRIBUTION_ACCOUNT_ADDRESS"] # 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.incomplete, withdraw_anchor_account=distribution_address, withdraw_memo=withdraw_memo, withdraw_memo_type=Transaction.MEMO_TYPES.hash, ) logger.info(f"Created withdrawal transaction {transaction_id}") url = interactive_url(request, str(transaction_id), account, asset_code, settings.OPERATION_WITHDRAWAL) return Response({ "type": "interactive_customer_info_needed", "url": url, "id": transaction_id })
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, )
def withdraw(account: str, request: Request) -> Response: """ `POST /transactions/withdraw` initiates the withdrawal and returns an interactive withdrawal form to the user. """ asset_code = request.POST.get("asset_code") if not asset_code: return render_error_response("'asset_code' is required") # TODO: Verify optional arguments. # Verify that the asset code exists in our database, with withdraw enabled. asset = Asset.objects.filter(code=asset_code).first() 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.incomplete, withdraw_anchor_account=settings.STELLAR_DISTRIBUTION_ACCOUNT_ADDRESS, withdraw_memo=withdraw_memo, withdraw_memo_type=Transaction.MEMO_TYPES.hash, ) url = _construct_interactive_url(request, str(transaction_id), account, asset_code) return Response({ "type": "interactive_customer_info_needed", "url": url, "id": transaction_id })
def deposit(request): """ `GET /deposit` initiates the deposit and returns an interactive deposit form to the user. """ asset_code = request.GET.get("asset_code") stellar_account = request.GET.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() url = _construct_interactive_url(request, transaction_id) return Response( { "type": "interactive_customer_info_needed", "url": url, "id": transaction_id }, status=status.HTTP_403_FORBIDDEN, )