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
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
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
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