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,
                              require_sufficient_balance=False)

    if automatically_resolve_complete:
        transfer.resolve_as_complete_and_trigger_blockchain()

    return transfer
Beispiel #2
0
    def post(self):
        # Handle Parameters
        post_data = request.get_json()
        recipient_blockchain_address = post_data.get(
            'recipient_blockchain_address', None)
        transfer_amount = post_data.get('transfer_amount', None)
        master_wallet = g.active_organisation.org_level_transfer_account
        receive_transfer_account = create_transfer_account_if_required(
            recipient_blockchain_address, g.active_organisation.token,
            TransferAccountType.EXTERNAL)

        error_message = None
        if not recipient_blockchain_address:
            error_message = '"recipient_blockchain_address" parameter required'
        if not transfer_amount:
            error_message = '"transfer_amount" parameter required'
        if not error_message and not isinstance(transfer_amount, int):
            try:
                transfer_amount = int(transfer_amount)
            except:
                error_message = '"transfer_amount must be an integer'
        if not master_wallet:
            error_message = f'Organisation {g.active_organisation.id} has no master wallet'
        if error_message:
            return {'message': error_message}, 400

        transfer = CreditTransfer(
            transfer_amount,
            g.active_organisation.token,
            sender_transfer_account=master_wallet,
            recipient_transfer_account=receive_transfer_account,
            transfer_type=TransferTypeEnum.
            PAYMENT,  # PAYMENT, since WITHDRAWAL is to the float account by definition
            require_sufficient_balance=True)
        # Make the transfer!
        transfer.resolve_as_complete_and_trigger_blockchain()

        credit_transfer = credit_transfer_schema.dump(transfer)
        response_object = {
            'message': 'Transfer Successful',
            'data': {
                'credit_transfer': credit_transfer,
            }
        }
        return response_object, 201
Beispiel #3
0
def make_withdrawal_transfer(transfer_amount,
                             token,
                             send_user=None,
                             sender_transfer_account=None,
                             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_user,
        sender_transfer_account=sender_transfer_account,
        uuid=uuid,
        transfer_type=TransferTypeEnum.WITHDRAWAL,
        transfer_mode=transfer_mode,
        require_sufficient_balance=require_sufficient_balance
    )

    if require_sender_approved and not transfer.check_sender_is_approved():
        message = "Sender {} is not approved".format(sender_transfer_account)
        transfer.resolve_as_rejected(message)
        raise AccountNotApprovedError(message, is_sender=True)

    if automatically_resolve_complete:
        transfer.resolve_as_complete_and_trigger_blockchain()

    return transfer
Beispiel #4
0
def create_transfer(amount,
                    sender_user,
                    recipient_user,
                    token,
                    subtype=TransferSubTypeEnum.STANDARD,
                    transfer_usages=None,
                    created_offset=0,
                    transfer_mode=None):
    transfer = CreditTransfer(amount=amount,
                              sender_user=sender_user,
                              recipient_user=recipient_user,
                              token=token,
                              uuid=str(uuid4()))

    db.session.add(transfer)
    # Mimics before_request hook
    g.pending_transactions = []

    if transfer_usages:
        transfer.transfer_usages = transfer_usages

    transfer.resolve_as_complete_and_trigger_blockchain()

    transfer.transfer_type = TransferTypeEnum.PAYMENT
    transfer.transfer_subtype = subtype
    transfer.transfer_mode = transfer_mode

    transfer.created = datetime.utcnow() - timedelta(days=created_offset)

    # Commit to prevent memory errors with large numbers of txns counts
    db.session.commit()

    # Mimic after request hook midway through process
    for transaction, queue in g.pending_transactions:
        transaction.send_blockchain_payload_to_worker(queue=queue)

    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,
        queue='high-priority',
        batch_uuid=None,
        transfer_type=TransferTypeEnum.PAYMENT,
        transfer_card=None):
    """
    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 queue:
    :param batch_uuid:
    :param transfer_type: the type of transfer
    :param transfer_card: the card that was used to make the payment
    :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 = send_transfer_account or 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 = send_transfer_account or receive_transfer_account.token.float_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=transfer_type,
        transfer_subtype=transfer_subtype,
        transfer_mode=transfer_mode,
        transfer_card=transfer_card,
        is_ghost_transfer=is_ghost_transfer,
        require_sufficient_balance=require_sufficient_balance)

    make_cashout_incentive_transaction = False

    if transfer_use is not None:
        for use_id in transfer_use:
            if use_id != 'null':
                use = TransferUsage.query.get(int(use_id))
                if use:
                    transfer.transfer_usages.append(use)
                    if use.is_cashout:
                        make_cashout_incentive_transaction = True
                else:
                    raise Exception(f'{use_id} not a valid transfer usage')

    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 automatically_resolve_complete:
        transfer.resolve_as_complete_and_trigger_blockchain(
            queue=queue, batch_uuid=batch_uuid)

    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