Ejemplo n.º 1
0
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
Ejemplo n.º 2
0
    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
Ejemplo n.º 3
0
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
Ejemplo n.º 4
0
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
Ejemplo n.º 5
0
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
Ejemplo n.º 6
0
    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
Ejemplo n.º 7
0
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
Ejemplo n.º 8
0
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
Ejemplo n.º 9
0
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
Ejemplo n.º 10
0
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