def make_deposit_transfer(transfer_amount, token, receive_account, transfer_mode=None, automatically_resolve_complete=True, uuid=None, fiat_ramp=None): """ This is used for a user depositing funds to their Sempo wallet. Only interacts with Sempo float. :param transfer_amount: :param token: :param receive_account: :param transfer_mode: :param automatically_resolve_complete: :param uuid: :param fiat_ramp: A FiatRamp Object to tie to credit transfer :return: """ transfer = CreditTransfer(amount=transfer_amount, token=token, recipient_user=receive_account, transfer_type=TransferTypeEnum.DEPOSIT, transfer_mode=transfer_mode, uuid=uuid, fiat_ramp=fiat_ramp) if automatically_resolve_complete: transfer.resolve_as_completed() pusher.push_admin_credit_transfer(transfer) return transfer
def handle_transfer_success(self, transfer): target_user = User.query.get(self.chatbot_state.target_user_id) receiver_message = _('{} {} ({}) has sent you {} {}') \ .format( self.inbound_user.first_name, self.inbound_user.last_name, self.inbound_phone, self.chatbot_state.transfer_amount / 100, current_app.config['CURRENCY_NAME'] ) # TODO: CREATE RECIPIENT ALERT # if target_user.chat_source_preference == 'WHATSAPP' and target_user.phone: # # whatsapp_q.put({'phone': target_user.phone, 'message': receiver_message}) # # elif target_user.phone: # send_generic_message(target_user.phone, receiver_message) self.reset_chatbot_state() push_admin_credit_transfer(transfer) sender_message = _('Transfer Successful. Your balance is {} {}.') \ .format(self.inbound_transfer_account.balance / 100, current_app.config['CURRENCY_NAME']) return sender_message
def make_withdrawal_transfer(transfer_amount, send_account, transfer_mode=None, require_sender_approved=True, require_sufficient_balance=True, automatically_resolve_complete=True, uuid=None): transfer = create_and_commit_transfer(transfer_amount, send_account=send_account, uuid=uuid) transfer.transfer_mode = transfer_mode if require_sender_approved and not transfer.check_sender_is_approved(): message = "Sender {} is not approved".format(send_account) transfer.resolve_as_rejected(message) raise AccountNotApprovedError(message, is_sender=True) if require_sufficient_balance and not transfer.check_sender_has_sufficient_balance( ): message = "Sender {} has insufficient balance".format(send_account) transfer.resolve_as_rejected(message) raise InsufficientBalanceError(message) if automatically_resolve_complete: transfer.resolve_as_completed() pusher.push_admin_credit_transfer(transfer) return transfer
def make_blockchain_transfer(transfer_amount, send_address, token, receive_address, transfer_use=None, transfer_mode=None, require_sender_approved=False, require_sufficient_balance=False, automatically_resolve_complete=True, uuid=None, existing_blockchain_txn=False): send_address_obj = create_address_object_if_required(send_address) receive_address_obj = create_address_object_if_required(receive_address) if send_address_obj.transfer_account: sender_user = send_address_obj.transfer_account.primary_user else: sender_user = None if receive_address_obj.transfer_account: recipient_user = receive_address_obj.transfer_account.primary_user else: recipient_user = None require_recipient_approved = False transfer = make_payment_transfer(transfer_amount, token, sender_user, recipient_user, transfer_use, transfer_mode, require_sender_approved, require_recipient_approved, require_sufficient_balance, automatically_resolve_complete=False) transfer.sender_blockchain_address = send_address_obj transfer.recipient_blockchain_address = receive_address_obj transfer.transfer_type = TransferTypeEnum.PAYMENT if uuid: transfer.uuid = uuid if automatically_resolve_complete: transfer.resolve_as_completed( existing_blockchain_txn=existing_blockchain_txn) pusher.push_admin_credit_transfer(transfer) return transfer
def make_withdrawal_transfer(transfer_amount, token, send_account, transfer_mode=None, require_sender_approved=True, require_sufficient_balance=True, automatically_resolve_complete=True, uuid=None): """ This is used for a user withdrawing funds from their Sempo wallet. Only interacts with Sempo float. :param transfer_amount: :param token: :param send_account: :param transfer_mode: :param require_sender_approved: :param require_sufficient_balance: :param automatically_resolve_complete: :param uuid: :return: """ transfer = CreditTransfer(transfer_amount, token, sender_user=send_account, uuid=uuid, transfer_type=TransferTypeEnum.WITHDRAWAL) transfer.transfer_mode = transfer_mode if require_sender_approved and not transfer.check_sender_is_approved(): message = "Sender {} is not approved".format(send_account) transfer.resolve_as_rejected(message) raise AccountNotApprovedError(message, is_sender=True) if require_sufficient_balance and not transfer.check_sender_has_sufficient_balance( ): message = "Sender {} has insufficient balance".format(send_account) transfer.resolve_as_rejected(message) raise InsufficientBalanceError(message) if automatically_resolve_complete: transfer.resolve_as_completed() pusher.push_admin_credit_transfer(transfer) return transfer
def after_request(response): from server.utils import pusher if response.status_code < 300 and response.status_code >= 200: db.session.commit() for job, args, kwargs in g.executor_jobs: job.submit(*args, **kwargs) for transaction, queue in g.pending_transactions: transaction.send_blockchain_payload_to_worker(queue=queue) # Push only credit transfers, not exchanges from server.models.credit_transfer import CreditTransfer transactions = [ t[0] for t in g.pending_transactions if isinstance(t[0], CreditTransfer) ] pusher.push_admin_credit_transfer(transactions) return response
def make_disbursement_transfer(transfer_amount, receive_account, transfer_mode=None, automatically_resolve_complete=True, uuid=None): if current_app.config['USING_EXTERNAL_ERC20']: master_wallet_balance = master_wallet_funds_available() if transfer_amount > master_wallet_balance: message = "Master Wallet has insufficient funds" raise InsufficientBalanceError(message) elapsed_time('4.1: Retrieved Master Balance') if current_app.config['IS_USING_BITCOIN']: if transfer_amount < 1000 * 100: raise Exception("Minimum Transfer Amount is 1000 sat") transfer = create_and_commit_transfer(transfer_amount, receive_account=receive_account, uuid=uuid) transfer.transfer_mode = transfer_mode elapsed_time('4.3: Created and commited') if automatically_resolve_complete: transfer.resolve_as_completed() elapsed_time('4.4: Resolved as complete') pusher.push_admin_credit_transfer(transfer) elapsed_time('4.5: Pusher complete') return transfer
def make_payment_transfer( transfer_amount, token=None, send_user=None, send_transfer_account=None, receive_user=None, receive_transfer_account=None, transfer_use=None, transfer_mode=None, require_sender_approved=True, require_recipient_approved=True, require_sufficient_balance=True, automatically_resolve_complete=True, uuid=None, transfer_subtype: TransferSubTypeEnum = TransferSubTypeEnum.STANDARD, is_ghost_transfer=False, enable_pusher=True, queue='high-priority'): """ This is used for internal transfers between Sempo wallets. :param transfer_amount: :param token: :param send_user: :param send_transfer_account: :param receive_user: :param receive_transfer_account: :param transfer_use: :param transfer_mode: TransferModeEnum :param require_sender_approved: :param require_recipient_approved: :param require_sufficient_balance: :param automatically_resolve_complete: :param uuid: :param transfer_subtype: accepts TransferSubType str. :param is_ghost_transfer: if an account is created for recipient just to exchange, it's not real :param enable_pusher: :param queue: :return: """ if transfer_subtype is TransferSubTypeEnum.DISBURSEMENT: require_sender_approved = False require_recipient_approved = False require_sufficient_balance = False # primary NGO wallet to disburse from send_transfer_account = receive_user.default_organisation.queried_org_level_transfer_account if transfer_subtype is TransferSubTypeEnum.RECLAMATION: require_sender_approved = False # primary NGO wallet to reclaim to receive_transfer_account = send_user.default_organisation.queried_org_level_transfer_account if transfer_subtype is TransferSubTypeEnum.INCENTIVE: send_transfer_account = receive_transfer_account.get_float_transfer_account( ) transfer = CreditTransfer( transfer_amount, token=token, sender_user=send_user, sender_transfer_account=send_transfer_account, recipient_user=receive_user, recipient_transfer_account=receive_transfer_account, uuid=uuid, transfer_type=TransferTypeEnum.PAYMENT, transfer_subtype=transfer_subtype, transfer_mode=transfer_mode, is_ghost_transfer=is_ghost_transfer) make_cashout_incentive_transaction = False if transfer_use is not None: usages = [] try: use_ids = transfer_use.split(',') # passed as '3,4' etc. except AttributeError: use_ids = transfer_use for use_id in use_ids: if use_id != 'null': use = TransferUsage.query.get(int(use_id)) if use: usages.append(use.name) if use.is_cashout: make_cashout_incentive_transaction = True else: usages.append('Other') transfer.transfer_use = usages transfer.uuid = uuid if require_sender_approved and not transfer.check_sender_is_approved(): message = "Sender {} is not approved".format(send_transfer_account) transfer.resolve_as_rejected(message) raise AccountNotApprovedError(message, is_sender=True) if require_recipient_approved and not transfer.check_recipient_is_approved( ): message = "Recipient {} is not approved".format(receive_user) transfer.resolve_as_rejected(message) raise AccountNotApprovedError(message, is_sender=False) if require_sufficient_balance and not transfer.check_sender_has_sufficient_balance( ): message = "Sender {} has insufficient balance".format( send_transfer_account) transfer.resolve_as_rejected(message) raise InsufficientBalanceError(message) if automatically_resolve_complete: transfer.resolve_as_completed(queue=queue) if enable_pusher: pusher.push_admin_credit_transfer(transfer) if make_cashout_incentive_transaction: try: # todo: check limits apply incentive_amount = round( transfer_amount * current_app.config['CASHOUT_INCENTIVE_PERCENT'] / 100) make_payment_transfer( incentive_amount, receive_user=receive_user, transfer_subtype=TransferSubTypeEnum.INCENTIVE, transfer_mode=TransferModeEnum.INTERNAL) except Exception as e: print(e) sentry_sdk.capture_exception(e) return transfer
def make_payment_transfer(transfer_amount, send_account, receive_account, transfer_use=None, transfer_mode=None, require_sender_approved=True, require_recipient_approved=True, require_sufficient_balance=True, automatically_resolve_complete=True, uuid=None): transfer = create_and_commit_transfer(transfer_amount, send_account=send_account, receive_account=receive_account, uuid=uuid) make_cashout_incentive_transaction = False if transfer_use is not None: usages = [] try: use_ids = transfer_use.split(',') # passed as '3,4' etc. except AttributeError: use_ids = transfer_use for use_id in use_ids: if use_id != 'null': use = models.TransferUsage.query.filter_by(id=use_id).first() if use: usages.append(use.name) if use.is_cashout: make_cashout_incentive_transaction = True else: usages.append('Other') transfer.transfer_use = usages transfer.transfer_mode = transfer_mode transfer.uuid = uuid if require_sender_approved and not transfer.check_sender_is_approved(): message = "Sender {} is not approved".format(send_account) transfer.resolve_as_rejected(message) raise AccountNotApprovedError(message, is_sender=True) if require_recipient_approved and not transfer.check_recipient_is_approved( ): message = "Recipient {} is not approved".format(receive_account) transfer.resolve_as_rejected(message) raise AccountNotApprovedError(message, is_sender=False) if require_sufficient_balance and not transfer.check_sender_has_sufficient_balance( ): message = "Sender {} has insufficient balance".format(send_account) transfer.resolve_as_rejected(message) raise InsufficientBalanceError(message) if automatically_resolve_complete: transfer.resolve_as_completed() pusher.push_admin_credit_transfer(transfer) if make_cashout_incentive_transaction: try: incentive_amount = round( transfer_amount * current_app.config['CASHOUT_INCENTIVE_PERCENT'] / 100) make_disbursement_transfer(incentive_amount, receive_account) except Exception as e: print(e) sentry.captureException() return transfer
def make_blockchain_transfer(transfer_amount, send_address, receive_address, transfer_use=None, transfer_mode=None, require_sender_approved=False, require_sufficient_balance=False, automatically_resolve_complete=True, uuid=None, existing_blockchain_txn=False): send_address_obj = create_address_object_if_required(send_address) receive_address_obj = create_address_object_if_required(receive_address) if send_address_obj.transfer_account: sender_user = send_address_obj.transfer_account.primary_user else: sender_user = None if receive_address_obj.transfer_account: recipient_user = receive_address_obj.transfer_account.primary_user else: recipient_user = None require_recipient_approved = False transfer = make_payment_transfer(transfer_amount, sender_user, recipient_user, transfer_use, transfer_mode, require_sender_approved, require_recipient_approved, require_sufficient_balance, automatically_resolve_complete=False) transfer.sender_blockchain_address = send_address_obj transfer.recipient_blockchain_address = receive_address_obj transfer.transfer_type = models.TransferTypeEnum.PAYMENT if uuid: transfer.uuid = uuid if existing_blockchain_txn: existing_blockchain_txn_obj = models.BlockchainTransaction( status='SUCCESS', message='External Txn', added_date=datetime.datetime.utcnow(), hash=existing_blockchain_txn, transaction_type='transfer') existing_blockchain_txn_obj.credit_transfer = transfer db.session.add(existing_blockchain_txn_obj) if automatically_resolve_complete: transfer.resolve_as_completed( existing_blockchain_txn=existing_blockchain_txn) pusher.push_admin_credit_transfer(transfer) return transfer
def post(self, credit_transfer_id): auto_resolve = AccessControl.has_sufficient_tier( g.user.roles, 'ADMIN', 'superadmin') post_data = request.get_json() uuid = post_data.get('uuid') queue = 'low-priority' transfer_type = post_data.get('transfer_type') transfer_amount = abs( round(float(post_data.get('transfer_amount') or 0), 6)) token_id = post_data.get('token_id') target_balance = post_data.get('target_balance') transfer_use = post_data.get('transfer_use') sender_user_id = post_data.get('sender_user_id') recipient_user_id = post_data.get('recipient_user_id') # These could be phone numbers, email, nfc serial numbers, card numbers etc sender_public_identifier = post_data.get('sender_public_identifier') recipient_public_identifier = post_data.get( 'recipient_public_identifier') sender_transfer_account_id = post_data.get( 'sender_transfer_account_id') recipient_transfer_account_id = post_data.get( 'recipient_transfer_account_id') recipient_transfer_accounts_ids = post_data.get( 'recipient_transfer_accounts_ids') credit_transfers = [] response_list = [] is_bulk = False if uuid: existing_transfer = CreditTransfer.query.filter_by( uuid=uuid).first() # We return a 201 here so that the client removes the uuid from the cache response_object = { 'message': 'Transfer Successful', 'data': { 'credit_transfer': credit_transfer_schema.dump(existing_transfer).data, } } return make_response(jsonify(response_object)), 201 if transfer_amount <= 0 and not target_balance: response_object = { 'message': 'Transfer amount must be positive', } return make_response(jsonify(response_object)), 400 if recipient_transfer_accounts_ids: is_bulk = True if transfer_type not in ["DISBURSEMENT", "BALANCE"]: response_object = { 'message': 'Bulk transfer must be either disbursement or balance', } return make_response(jsonify(response_object)), 400 transfer_user_list = [] for transfer_account_id in recipient_transfer_accounts_ids: try: individual_sender_user = None individual_recipient_user = find_user_with_transfer_account_from_identifiers( None, None, transfer_account_id) transfer_user_list.append( (individual_sender_user, individual_recipient_user)) except (NoTransferAccountError, UserNotFoundError) as e: response_list.append({'status': 400, 'message': str(e)}) else: try: individual_sender_user = find_user_with_transfer_account_from_identifiers( sender_user_id, sender_public_identifier, sender_transfer_account_id) individual_recipient_user = find_user_with_transfer_account_from_identifiers( recipient_user_id, recipient_public_identifier, recipient_transfer_account_id) transfer_user_list = [(individual_sender_user, individual_recipient_user)] except Exception as e: response_object = { 'message': str(e), } return make_response(jsonify(response_object)), 400 if token_id: token = Token.query.get(token_id) if not token: response_object = {'message': 'Token not found'} return make_response(jsonify(response_object)), 404 else: active_organisation = g.active_organisation if active_organisation is None: response_object = {'message': 'Must provide token_id'} return make_response(jsonify(response_object)), 400 else: token = active_organisation.token for sender_user, recipient_user in transfer_user_list: try: if transfer_type == 'PAYMENT': transfer = make_payment_transfer( transfer_amount, token=token, send_user=sender_user, receive_user=recipient_user, transfer_use=transfer_use, uuid=uuid, automatically_resolve_complete=auto_resolve, queue=queue, enable_pusher=not is_bulk) elif transfer_type == 'RECLAMATION': transfer = make_payment_transfer( transfer_amount, token=token, send_user=sender_user, uuid=uuid, transfer_subtype=TransferSubTypeEnum.RECLAMATION, require_recipient_approved=False, automatically_resolve_complete=auto_resolve, queue=queue, enable_pusher=not is_bulk) elif transfer_type == 'DISBURSEMENT': transfer = make_payment_transfer( transfer_amount, token=token, send_user=g.user, receive_user=recipient_user, uuid=uuid, transfer_subtype=TransferSubTypeEnum.DISBURSEMENT, automatically_resolve_complete=auto_resolve, queue=queue, enable_pusher=not is_bulk) elif transfer_type == 'BALANCE': transfer = make_target_balance_transfer( target_balance, recipient_user, uuid=uuid, automatically_resolve_complete=auto_resolve, queue=queue, enable_pusher=not is_bulk) except (InsufficientBalanceError, AccountNotApprovedError, InvalidTargetBalanceError, BlockchainError, Exception) as e: if is_bulk: response_list.append({'status': 400, 'message': str(e)}) else: db.session.commit() response_object = {'message': str(e)} return make_response(jsonify(response_object)), 400 else: message = 'Transfer Successful' if auto_resolve else 'Transfer Pending. Must be approved.' if is_bulk: credit_transfers.append(transfer) response_list.append({'status': 201, 'message': message}) else: db.session.flush() credit_transfer = credit_transfer_schema.dump( transfer).data response_object = { 'message': message, 'is_create': True, 'data': { 'credit_transfer': credit_transfer, } } return make_response(jsonify(response_object)), 201 db.session.flush() if is_bulk: pusher.push_admin_credit_transfer(credit_transfers) message = 'Bulk Transfer Creation Successful' if auto_resolve else 'Bulk Transfer Pending. Must be approved.' response_object = { 'message': message, 'bulk_responses': response_list, 'data': { 'credit_transfers': credit_transfers_schema.dump(credit_transfers).data } } return make_response(jsonify(response_object)), 201