def handle_transfer_to_blockchain_address(transfer_amount,
                                          sender_transfer_account,
                                          recipient_blockchain_address,
                                          transfer_use,
                                          transfer_mode,
                                          uuid=None):

    if transfer_amount > sender_transfer_account.balance:
        response_object = {
            'message': 'Insufficient funds',
            'feedback': True,
        }
        return make_response(jsonify(response_object)), 400

    try:
        transfer = make_blockchain_transfer(
            transfer_amount,
            sender_transfer_account.token,
            sender_transfer_account.blockchain_address,
            recipient_blockchain_address,
            transfer_use,
            transfer_mode,
            uuid=None)

        # This is the top-level commit for this flow
        db.session.commit()

    except AccountNotApprovedError as e:
        response_object = {
            'message': "Sender is not approved",
            'feedback': True,
        }
        return make_response(jsonify(response_object)), 400

    except InsufficientBalanceError as e:
        response_object = {
            'message': "Insufficient balance",
            'feedback': True,
        }
        return make_response(jsonify(response_object)), 400

    response_object = {
        'message': 'Payment Successful',
        'feedback': True,
        'data': {
            'credit_transfer': me_credit_transfer_schema.dump(transfer).data
        }
    }

    return make_response(jsonify(response_object)), 201
Example #2
0
    def post(self):

        post_data = request.get_json()

        uuid = post_data.get('uuid')
        created = post_data.get('created')

        transfer_use = post_data.get('transfer_use')

        transfer_amount = round(float(post_data.get('transfer_amount', 0)), 6)

        transfer_random_key = post_data.get('transfer_random_key')

        pin = post_data.get('pin')

        nfc_serial_number = post_data.get('nfc_id')

        user_id = post_data.get('user_id')
        public_identifier = post_data.get('public_identifier')
        transfer_account_id = post_data.get('transfer_account_id')

        qr_data = post_data.get('qr_data')
        if qr_data is not None:
            qr_data = str(qr_data).strip(" ").strip("\t")

        is_sending = post_data.get('is_sending', False)

        authorised = False

        if uuid:
            existing_transfer = CreditTransfer.query.filter_by(
                uuid=uuid).first()

            if existing_transfer:
                # We return a 201 here so that the client removes the uuid from the cache
                response_object = {
                    'message': 'Transfer already in cache',
                    'data': {
                        'credit_transfer':
                        me_credit_transfer_schema.dump(existing_transfer).data,
                    }
                }
                return make_response(jsonify(response_object)), 201

        if qr_data:

            split_qr_data = qr_data.split('-')

            if len(split_qr_data) == 1:
                # No hyphen, so assume qr code encode the public serial number

                counterparty_user = User.query.filter(
                    func.lower(User.public_serial_number) == func.lower(
                        qr_data)).first()

            else:
                user_id = int(split_qr_data[1])

                counterparty_user = User.query.get(user_id)

                if not counterparty_user:
                    response_object = {
                        'message': 'No such user for ID {}'.format(user_id),
                        'feedback': True,
                    }
                    return make_response(jsonify(response_object)), 400

                if not is_sending:
                    transfer_amount = int(split_qr_data[0])
                    qr_hash = split_qr_data[2]

                    user_secret = counterparty_user.secret

                    if not check_for_any_valid_hash(transfer_amount,
                                                    user_secret, qr_hash):
                        response_object = {
                            'message': 'Invalid QR Code',
                            'feedback': True,
                        }
                        return make_response(jsonify(response_object)), 401

                    authorised = True

        elif nfc_serial_number:
            # We treat NFC serials differently because they're automatically authorised under the current version
            counterparty_user = User.query.filter_by(
                nfc_serial_number=nfc_serial_number).first()
            authorised = True

            if not counterparty_user:
                response_object = {
                    'message':
                    'No such user for NFC serial number {}'.format(
                        nfc_serial_number),
                    'feedback':
                    True
                }
                return make_response(jsonify(response_object)), 400

        else:
            try:
                counterparty_user = find_user_with_transfer_account_from_identifiers(
                    user_id, public_identifier, transfer_account_id)

            except (NoTransferAccountError, UserNotFoundError) as e:

                if not Web3.isAddress(public_identifier.strip(
                        'ethereum:')) or not is_sending:
                    response_object = {'message': str(e), 'feedback': True}
                    return make_response(jsonify(response_object)), 400

                #We're sending directly to a blockchain address
                return handle_transfer_to_blockchain_address(
                    transfer_amount,
                    g.user,
                    public_identifier.strip('ethereum:'),
                    transfer_use,
                    uuid=uuid)

            if not counterparty_user:
                response_object = {
                    'message': 'User not found',
                    'feedback': True
                }
                return make_response(jsonify(response_object)), 400

            authorised = counterparty_user.verify_password(str(pin))

        if is_sending:
            authorised = True

        if not authorised:
            responseObject = {
                'message': 'Not Authorised',
                'feedback': True,
            }
            return make_response(jsonify(responseObject)), 401

        if is_sending:
            send_user = g.user
            receive_user = counterparty_user

        else:
            send_user = counterparty_user
            receive_user = g.user

        if transfer_amount == 0 or transfer_amount > send_user.transfer_account.balance:

            db.session.commit()

            responseObject = {
                'message': 'Insufficient funds',
                'feedback': True,
            }
            return make_response(jsonify(responseObject)), 400

        try:
            transfer = make_payment_transfer(transfer_amount,
                                             send_user,
                                             receive_user,
                                             transfer_use,
                                             uuid=uuid)
        except AccountNotApprovedError as e:
            db.session.commit()

            if e.is_sender is True:
                responseObject = {
                    'message': "Sender is not approved",
                    'feedback': True,
                }
                return make_response(jsonify(responseObject)), 400
            elif e.is_sender is False:
                responseObject = {
                    'message': "Recipient is not approved",
                    'feedback': True,
                }
                return make_response(jsonify(responseObject)), 400
            else:
                responseObject = {
                    'message': "Account is not approved",
                    'feedback': True,
                }
                return make_response(jsonify(responseObject)), 400

        except InsufficientBalanceError as e:
            db.session.commit()

            responseObject = {
                'message': "Insufficient balance",
                'feedback': True,
            }
            return make_response(jsonify(responseObject)), 400

        if created:
            try:
                transfer.created = datetime.datetime.strptime(
                    created, "%Y-%m-%dT%H:%M:%S.%fz")
            except ValueError as e:
                pass

        if is_sending:
            push_user_transfer_confirmation(receive_user, transfer_random_key)

        db.session.commit()

        responseObject = {
            'message': 'Payment Successful',
            'first_name': counterparty_user.first_name,
            'last_name': counterparty_user.last_name,
            'feedback': True,
            'data': {
                'credit_transfer':
                me_credit_transfer_schema.dump(transfer).data
            }
        }

        return make_response(jsonify(responseObject)), 201
    def post(self):

        post_data = request.get_json()

        uuid = post_data.get('uuid')
        created = post_data.get('created')

        transfer_use = post_data.get('transfer_use')
        try:
            use_ids = transfer_use.split(',')  # passed as '3,4' etc.
        except AttributeError:
            use_ids = transfer_use
        transfer_mode = post_data.get('transfer_mode')

        transfer_amount = round(Decimal(post_data.get('transfer_amount', 0)), 6)

        transfer_random_key = post_data.get('transfer_random_key')

        pin = post_data.get('pin')

        nfc_serial_number = post_data.get('nfc_id')

        user_id = post_data.get('user_id')
        public_identifier = post_data.get('public_identifier')
        transfer_account_id = post_data.get('transfer_account_id')

        qr_data = post_data.get('qr_data')
        if qr_data is not None:
            qr_data = str(qr_data).strip(" ").strip("\t")

        my_transfer_account_id = post_data.get("my_transfer_account_id")

        is_sending = post_data.get('is_sending', False)

        transfer_card = None
        my_transfer_account = None
        authorised = False
        if transfer_account_id:
            counterparty_transfer_account = TransferAccount.query.get(transfer_account_id)
        else:
            counterparty_transfer_account = None

        if uuid:
            existing_transfer = CreditTransfer.query.filter_by(uuid=uuid).first()

            if existing_transfer:
                # We return a 201 here so that the client removes the uuid from the cache
                response_object = {
                    'message': 'Transfer already in cache',
                    'data': {
                        'credit_transfer': me_credit_transfer_schema.dump(existing_transfer).data,
                    }
                }
                return make_response(jsonify(response_object)), 201

        if qr_data:

            split_qr_data = qr_data.split('-')

            transfer_amount = int(split_qr_data[0])
            transfer_account_id = int(split_qr_data[1])
            user_id = int(split_qr_data[2])
            qr_hash = split_qr_data[3]

            counterparty_user = User.query.get(user_id)

            if not counterparty_user:
                response_object = {
                    'message': 'No such user for ID {}'.format(user_id),
                    'feedback': True,
                }
                return make_response(jsonify(response_object)), 404

            counterparty_transfer_account = TransferAccount.query.get(transfer_account_id)

            if not counterparty_transfer_account:
                response_object = {
                    'message': 'No such Transfer Account for ID {}'.format(transfer_account_id),
                    'feedback': True,
                }
                return make_response(jsonify(response_object)), 404

            if counterparty_transfer_account not in counterparty_user.transfer_accounts:
                if not counterparty_transfer_account:
                    response_object = {
                        'message': 'User {} not authorised for Transfer Account {}.'
                            .format(user_id, transfer_account_id),
                        'feedback': True,
                    }
                    return make_response(jsonify(response_object)), 401

            my_transfer_account = find_transfer_accounts_with_matching_token(
                g.user, counterparty_transfer_account.token
            )

            user_secret = counterparty_user.secret

            if not check_for_any_valid_hash(transfer_amount, transfer_account_id, user_secret, qr_hash):
                response_object = {
                    'message': 'Invalid QR Code',
                    'feedback': True,
                }
                return make_response(jsonify(response_object)), 401

            authorised = True

        elif nfc_serial_number:
            # We treat NFC serials differently because they're automatically authorised under the current version
            transfer_card = TransferCard.query.filter_by(nfc_serial_number=nfc_serial_number).first()

            if transfer_card:
                counterparty_user = transfer_card.user
                counterparty_transfer_account = transfer_card.transfer_account

            if not transfer_card or not counterparty_user or not counterparty_transfer_account:
                response_object = {
                    'message': 'Card not found',
                    'feedback': True
                }
                return make_response(jsonify(response_object)), 404

            authorised = True


        else:
            try:
                counterparty_user, _ = find_user_with_transfer_account_from_identifiers(
                    user_id,
                    public_identifier,
                    transfer_account_id)

            except (NoTransferAccountError, UserNotFoundError) as e:

                if not Web3.isAddress(public_identifier.strip('ethereum:')) or not is_sending:
                    response_object = {
                        'message': str(e),
                        'feedback': True
                    }
                    return make_response(jsonify(response_object)), 400

                my_transfer_account = TransferAccount.query.get(my_transfer_account_id)

                if not my_transfer_account:
                    response_object = {
                        'message': 'Transfer Account not found for my_transfer_account_id {}'.format(
                            my_transfer_account_id)
                    }
                    return make_response(jsonify(response_object)), 400

                #We're sending directly to a blockchain address
                return handle_transfer_to_blockchain_address(transfer_amount,
                                                             my_transfer_account,
                                                             public_identifier.strip('ethereum:'),
                                                             transfer_use,
                                                             transfer_mode=TransferModeEnum.EXTERNAL,
                                                             uuid=uuid)

            if not counterparty_user:
                response_object = {
                    'message': 'User not found',
                    'feedback': True
                }
                return make_response(jsonify(response_object)), 400

            authorised = counterparty_user.verify_password(str(pin))

        if is_sending:
            authorised = True

        if not authorised:
            response_object = {
                'message': 'Not Authorised',
                'feedback': True,
            }
            return make_response(jsonify(response_object)), 401

        if not my_transfer_account:
            if not my_transfer_account_id:
                response_object = {
                    'message': 'You must provide your Transfer Account ID',
                }
                return make_response(jsonify(response_object)), 400

            my_transfer_account = TransferAccount.query.get(my_transfer_account_id)

            if not my_transfer_account:
                response_object = {
                    'message': 'Transfer Account not found for my_transfer_account_id {}'.format(my_transfer_account_id)
                }
                return make_response(jsonify(response_object)), 400

        if my_transfer_account not in g.user.transfer_accounts:
            response_object = {
                'message': 'Transfer account provided does not belong to user',
            }
            return make_response(jsonify(response_object)), 401

        if is_sending:
            send_user = g.user
            send_transfer_account = my_transfer_account
            receive_user = counterparty_user
            receive_transfer_account = counterparty_transfer_account

        else:
            if counterparty_transfer_account is None:
                response_object = {
                    'message': 'Counterparty Transfer Account not specified'
                }
                return make_response(jsonify(response_object)), 400

            send_user = counterparty_user
            send_transfer_account = counterparty_transfer_account
            receive_user = g.user
            receive_transfer_account = my_transfer_account

        if transfer_amount == 0 or transfer_amount > send_transfer_account.balance:

            db.session.commit()

            response_object = {
                'message': 'Insufficient funds',
                'feedback': True,
            }
            return make_response(jsonify(response_object)), 400

        try:
            transfer = make_payment_transfer(transfer_amount=transfer_amount,
                                             send_user=send_user,
                                             send_transfer_account=send_transfer_account,
                                             receive_user=receive_user,
                                             receive_transfer_account=receive_transfer_account,
                                             transfer_use=transfer_use,
                                             transfer_mode=transfer_mode,
                                             uuid=uuid,
                                             transfer_card=transfer_card)

        except AccountNotApprovedError as e:
            db.session.commit()

            if e.is_sender is True:
                response_object = {
                    'message': "Sender is not approved",
                    'feedback': True,
                }
                return make_response(jsonify(response_object)), 400
            elif e.is_sender is False:
                response_object = {
                    'message': "Recipient is not approved",
                    'feedback': True,
                }
                return make_response(jsonify(response_object)), 400
            else:
                response_object = {
                    'message': "Account is not approved",
                    'feedback': True,
                }
                return make_response(jsonify(response_object)), 400


        except InsufficientBalanceError as e:
            db.session.commit()

            response_object = {
                'message': "Insufficient balance",
                'feedback': True,
            }
            return make_response(jsonify(response_object)), 400

        if created:
            try:
                transfer.created = datetime.datetime.strptime(created, "%Y-%m-%dT%H:%M:%S.%fz")
            except ValueError as e:
                pass

        if is_sending:
            push_user_transfer_confirmation(receive_user, transfer_random_key)

        db.session.commit()

        response_object = {
            'message': 'Payment Successful',
            'first_name': counterparty_user.first_name,
            'last_name': counterparty_user.last_name,
            'feedback': True,
            'data': {
                'credit_transfer': me_credit_transfer_schema.dump(transfer).data
            }
        }

        return make_response(jsonify(response_object)), 201