コード例 #1
0
 def pay(self, request, pk=None, provider=None):
     """
         Task Payment Provider Endpoint
         ---
         omit_serializer: true
         omit_parameters:
             - query
         """
     task = self.get_object()
     callback = '%s://%s/task/%s/rate/' % (request.scheme, request.get_host(), pk)
     next_url = callback
     if task and task.has_object_write_permission(request) and bitcoin_utils.is_valid_btc_address(task.btc_address):
         if provider == TASK_PAYMENT_METHOD_BITONIC:
             client = oauth1.Client(
                 BITONIC_CONSUMER_KEY, BITONIC_CONSUMER_SECRET, BITONIC_ACCESS_TOKEN, BITONIC_TOKEN_SECRET,
                 callback_uri=callback, signature_type=SIGNATURE_TYPE_QUERY
             )
             amount = task.pay
             increase_factor = 1 + (Decimal(BITONIC_PAYMENT_COST_PERCENTAGE)*Decimal(0.01))
             q_string = urlencode({
                 'ext_data': task.summary.encode('utf-8'),
                 'bitcoinaddress': task.btc_address,
                 'ordertype': 'buy',
                 'euros': amount * increase_factor
             })
             req_data = client.sign('%s/?%s' % (BITONIC_URL, q_string), http_method='GET')
             next_url = req_data[0]
     return redirect(next_url)
コード例 #2
0
def update_multi_tasks(multi_task_key, distribute=False):
    multi_task_key = clean_instance(multi_task_key, MultiTaskPaymentKey)

    if multi_task_key.distribute_only:
        connected_tasks = multi_task_key.distribute_tasks
        connected_tasks.filter(paid=True).update(
            btc_price=multi_task_key.btc_price,
            withhold_tunga_fee_distribute=multi_task_key.withhold_tunga_fee,
            btc_paid=multi_task_key.paid,
            btc_paid_at=multi_task_key.paid_at
        )
    else:
        connected_tasks = multi_task_key.tasks
        connected_tasks.filter(paid=False).update(
            payment_method=multi_task_key.payment_method,
            btc_price=multi_task_key.btc_price,
            withhold_tunga_fee=multi_task_key.withhold_tunga_fee,
            paid=multi_task_key.paid,
            paid_at=multi_task_key.paid_at,
            processing=multi_task_key.processing,
            processing_at=multi_task_key.processing_at
        )

    # Generate invoices for all connected tasks
    for task in connected_tasks.all():
        if multi_task_key.distribute_only:
            if task.paid and multi_task_key.paid:
                # Coinbase waits for 6 confirmations, so not safe to distribute yet
                # distribute_task_payment.delay(task.id)
                pass
            return

        # Save Invoice
        if not task.btc_address or not bitcoin_utils.is_valid_btc_address(task.btc_address):
            address = coinbase_utils.get_new_address(coinbase_utils.get_api_client())
            task.btc_address = address
            task.save()

        TaskInvoice.objects.create(
            task=task,
            user=multi_task_key.user,
            title=task.title,
            fee=task.pay,
            client=task.owner or task.user,
            # developer=developer,
            payment_method=multi_task_key.payment_method,
            btc_price=multi_task_key.btc_price,
            btc_address=task.btc_address,
            withhold_tunga_fee=multi_task_key.withhold_tunga_fee
        )

        if distribute and multi_task_key.paid:
            distribute_task_payment_payoneer.delay(task.id)
コード例 #3
0
ファイル: models.py プロジェクト: tunga-io/tunga-api
    def btc_address(self):
        if not self.profile:
            return None

        if self.profile.payment_method == PAYMENT_METHOD_BTC_ADDRESS:
            if bitcoin_utils.is_valid_btc_address(self.profile.btc_address):
                return self.profile.btc_address
        elif self.profile.payment_method == PAYMENT_METHOD_BTC_WALLET:
            wallet = self.profile.btc_wallet
            if wallet.provider == BTC_WALLET_PROVIDER_COINBASE:
                client = coinbase_utils.get_oauth_client(wallet.token, wallet.token_secret, self)
                return coinbase_utils.get_new_address(client)
        return None
コード例 #4
0
ファイル: models.py プロジェクト: ziwer1/tunga-api
    def btc_address(self):
        if not self.profile:
            return None

        if self.profile.payment_method == PAYMENT_METHOD_BTC_ADDRESS:
            if bitcoin_utils.is_valid_btc_address(self.profile.btc_address):
                return self.profile.btc_address
        elif self.profile.payment_method == PAYMENT_METHOD_BTC_WALLET:
            wallet = self.profile.btc_wallet
            if wallet.provider == BTC_WALLET_PROVIDER_COINBASE:
                client = coinbase_utils.get_oauth_client(
                    wallet.token, wallet.token_secret, self)
                return coinbase_utils.get_new_address(client)
        return None
コード例 #5
0
def process_invoices(pk,
                     invoice_types=('client', ),
                     user_id=None,
                     developer_ids=None,
                     is_admin=False,
                     filepath=None):
    """
    :param pk: id of the task
    :param invoice_types: tuple of invoice types to generate e.g 'client', 'developer', 'tunga'
    :param user_id: user viewing the invoice(s)
    :param developer_ids: participant invoices to generate. only applies to 'developer' and 'tunga' invoices
    :param is_admin: is requester an admin?
    :param filepath: file to store invoice in
    :return:
    """
    all_invoices = list()

    if pk == 'all':
        tasks = Task.objects.filter(closed=True, taskinvoice__isnull=False)
        if user_id and not is_admin:
            tasks = tasks.filter(
                Q(user_id=user_id) | Q(owner_id=user_id) | Q(pm_id=user_id)
                | Q(participant__user_id=user_id))
        tasks = tasks.distinct()
    else:
        tasks = Task.objects.filter(id=pk)

    for task in tasks:
        invoice = task.invoice
        if invoice:
            invoice = invoice.clean_invoice()
            if invoice.number:
                initial_invoice_data = TaskInvoiceSerializer(invoice).data
                initial_invoice_data[
                    'date'] = task.invoice.created_at.strftime('%d %B %Y')

                task_owner = task.user
                if task.owner:
                    task_owner = task.owner

                participation_shares = task.get_participation_shares()
                common_developer_info = list()
                for share_info in participation_shares:
                    participant = share_info['participant']
                    developer, created = DeveloperNumber.objects.get_or_create(
                        user=participant.user)

                    amount_details = invoice.get_amount_details(
                        share=share_info['share'])

                    if (not developer_ids or participant.user.id in developer_ids) and \
                            not (participant.prepaid or (participant.prepaid is None and participant.user.is_internal)):
                        common_developer_info.append({
                            'developer':
                            InvoiceUserSerializer(participant.user).data,
                            'amount':
                            amount_details,
                            'dev_number':
                            developer.number or '',
                            'participant':
                            participant
                        })

                for invoice_type in invoice_types:
                    if invoice_type == 'developer' and invoice.version > 1:
                        continue
                    task_developers = []
                    invoice_data = copy(initial_invoice_data)

                    if invoice_type == 'client':
                        invoice_data['number_client'] = invoice.invoice_id(
                            invoice_type='client')
                        task_developers = [dict()]
                    else:
                        for common_info in common_developer_info:
                            final_dev_info = copy(common_info)
                            final_dev_info['number'] = invoice.invoice_id(
                                invoice_type=invoice_type,
                                user=common_info['participant']
                                and common_info['participant'].user or None)

                            participant_payment_method = None
                            if common_info['participant']:
                                try:
                                    participant_payment = common_info[
                                        'participant'].participantpayment_set.filter(
                                        ).latest('created_at')
                                    if participant_payment and bitcoin_utils.is_valid_btc_address(
                                            participant_payment.destination):
                                        participant_payment_method = PAYMENT_METHOD_BITCOIN
                                except:
                                    pass

                            final_dev_info[
                                'payment_method'] = participant_payment_method
                            task_developers.append(final_dev_info)

                    invoice_data['developers'] = task_developers

                    all_invoices.append(
                        dict(invoice_type=invoice_type,
                             invoice=invoice_data,
                             location=invoice_type == 'client'
                             and invoice.vat_location_client
                             or VAT_LOCATION_WORLD))
    ctx = dict(invoices=all_invoices)

    rendered_html = render_to_string("tunga/pdf/invoice.html",
                                     context=ctx).encode(encoding="UTF-8")
    if filepath:
        HTML(string=rendered_html, encoding='utf-8').write_pdf(filepath)
    return rendered_html
コード例 #6
0
def validate_btc_address_or_none(value):
    error_msg = 'Invalid Bitcoin address.'
    if value is not None and re.match(r"[a-zA-Z1-9]{27,35}$", value) is None:
        raise ValidationError(error_msg)
    if value is not None and not is_valid_btc_address(value):
        raise ValidationError(error_msg)
コード例 #7
0
    def invoice(self, request, pk=None):
        """
        Task Invoice Endpoint
        ---
        request_serializer: TaskPaymentSerializer
        response_serializer: TaskInvoiceSerializer
        omit_parameters:
            - query
        """
        task = get_object_or_404(self.get_queryset(), pk=pk)
        self.check_object_permissions(request, task)

        invoice = task.invoice

        if request.method == 'POST':
            serializer = self.get_serializer(data=request.data)
            serializer.is_valid(raise_exception=True)

            fee = serializer.validated_data['fee']
            payment_method = serializer.validated_data['payment_method']

            if fee < task.pay:
                raise ValidationError({
                    'fee': 'You cannot reduce the fee for the task, Please contact [email protected] for assistance'
                })

            task.bid = fee
            task.payment_method = payment_method

            btc_price = coinbase_utils.get_btc_price(task.currency)
            task.btc_price = btc_price

            if not task.btc_address or not bitcoin_utils.is_valid_btc_address(task.btc_address):
                address = coinbase_utils.get_new_address(coinbase_utils.get_api_client())
                task.btc_address = address

            task.full_clean()
            task.save()

            developer = None
            try:
                assignee = task.participation_set.filter(accepted=True).order_by('-assignee').earliest('created_at')
                developer = assignee.user
            except:
                pass

            if not developer:
                raise ValidationError({
                    'fee': 'Please assign a developer to the task or contact [email protected] for assistance'
                })

            # Save Invoice
            invoice = TaskInvoice.objects.create(
                task=task,
                title=task.title,
                fee=task.pay,
                client=task.user,
                developer=developer,
                payment_method=task.payment_method,
                btc_price=btc_price,
                btc_address=task.btc_address
            )

            if task.payment_method == TASK_PAYMENT_METHOD_BANK:
                # Send notification for requested invoice
                send_task_invoice_request_email.delay(task.id)

        response_serializer = TaskInvoiceSerializer(invoice, context={'request': request})
        return Response(response_serializer.data)
コード例 #8
0
def distribute_task_payment(task, force_distribution=False, destination=None, target_payment=None):
    if not force_distribution:
        return

    task = clean_instance(task, Task)

    if not task.paid:
        return

    if task.pay_distributed and not target_payment:
        return

    pay_description = task.summary

    participation_shares = task.get_payment_shares()
    # Distribute all payments for this task
    payments = TaskPayment.objects.filter(
        Q(multi_pay_key__tasks=task) | Q(multi_pay_key__distribute_tasks=task) | (Q(task=task) & Q(processed=False)),
        received_at__isnull=False, payment_type=PAYMENT_METHOD_BITCOIN
    )

    if target_payment:
        payments.filter(id=target_payment)

    task_distribution = []
    for payment in payments:
        portion_distribution = []
        for item in participation_shares:
            participant = item['participant']
            share = item['share']
            share_amount = Decimal(share) * payment.task_pay_share(task)
            portion_sent = False

            if not participant.user:
                continue

            participant_pay, created = ParticipantPayment.objects.get_or_create(
                source=payment, participant=participant
            )
            payment_method = participant.user.payment_method
            if created or (participant_pay and participant_pay.status in [STATUS_PENDING, STATUS_RETRY]):
                if destination or payment_method in [PAYMENT_METHOD_BTC_ADDRESS, PAYMENT_METHOD_BTC_WALLET]:
                    pay_destination = destination
                    if not pay_destination:
                        pay_destination = participant_pay.destination
                    if not (pay_destination and bitcoin_utils.is_valid_btc_address(pay_destination)):
                        pay_destination = participant.user.btc_address
                    tunga_wallet_balance = coinbase_utils.get_account_balance()
                    transaction = send_payment_share(
                        destination=pay_destination,
                        amount=share_amount,
                        idem=str(participant_pay.idem_key),
                        description='{} - {}'.format(pay_description, participant.user.display_name)
                    )
                    if transaction.status not in [
                        coinbase_utils.TRANSACTION_STATUS_FAILED, coinbase_utils.TRANSACTION_STATUS_EXPIRED,
                        coinbase_utils.TRANSACTION_STATUS_CANCELED
                    ]:
                        participant_pay.ref = transaction.id
                        participant_pay.btc_sent = abs(Decimal(transaction.amount.amount))
                        participant_pay.status = STATUS_PROCESSING
                        participant_pay.sent_at = datetime.datetime.utcnow()
                        participant_pay.save()
                        portion_sent = True
                elif payment_method == PAYMENT_METHOD_MOBILE_MONEY:
                    tunga_wallet_balance = coinbase_utils.get_account_balance()
                    if tunga_wallet_balance > share_amount:
                        # Only attempt BitPesa payment if Wallet has enough balance
                        recipients = [
                            {
                                bitpesa.KEY_REQUESTED_AMOUNT: float(
                                    bitcoin_utils.get_valid_btc_amount(share_amount)
                                ),
                                bitpesa.KEY_REQUESTED_CURRENCY: CURRENCY_BTC,
                                bitpesa.KEY_PAYOUT_METHOD: {
                                    bitpesa.KEY_TYPE: bitpesa.get_pay_out_method(participant.user.mobile_money_cc),
                                    bitpesa.KEY_DETAILS: {
                                        bitpesa.KEY_FIRST_NAME: participant.user.first_name,
                                        bitpesa.KEY_LAST_NAME: participant.user.last_name,
                                        bitpesa.KEY_PHONE_NUMBER: participant.user.mobile_money_number
                                    }
                                }
                            }
                        ]
                        bitpesa_nonce = str(uuid4())
                        transaction = bitpesa.create_transaction(
                            BITPESA_SENDER, recipients, input_currency=CURRENCY_BTC,
                            transaction_id=participant_pay.id, nonce=bitpesa_nonce
                        )
                        if transaction:
                            participant_pay.external_created_at = datetime.datetime.utcnow()
                            participant_pay.ref = transaction.get(bitpesa.KEY_ID, None)
                            participant_pay.status = STATUS_INITIATED
                            participant_pay.extra = bitpesa_nonce
                            participant_pay.save()

                            if complete_bitpesa_payment(transaction):
                                portion_sent = True
                    else:
                        # TODO: Notify via Slack of failed payment due to balance
                        pass
            elif participant_pay and payment_method == PAYMENT_METHOD_MOBILE_MONEY and \
                            participant_pay.status == STATUS_INITIATED:
                tunga_wallet_balance = coinbase_utils.get_account_balance()
                if tunga_wallet_balance > share_amount:
                    # Only attempt BitPesa payment if Wallet has enough balance
                    transaction = bitpesa.get_transaction(participant_pay.ref)
                    if transaction and complete_bitpesa_payment(transaction):
                        portion_sent = True
                else:
                    # TODO: Notify via Slack of failed payment due to balance
                    pass

            portion_distribution.append(portion_sent)
        if portion_distribution and False not in portion_distribution:
            payment.processed = True
            payment.save()
            task_distribution.append(True)
        else:
            task_distribution.append(False)
    if task_distribution and not (False in task_distribution):
        task.pay_distributed = True
        task.save()
コード例 #9
0
def distribute_task_payment(task):
    task = clean_instance(task, Task)
    if not task.paid:
        return

    if task.pay_distributed:
        return

    pay_description = task.summary

    participation_shares = task.get_payment_shares()
    # Distribute all payments for this task
    payments = TaskPayment.objects.filter(
        Q(multi_pay_key__tasks=task) | Q(multi_pay_key__distribute_tasks=task)
        | (Q(task=task) & Q(processed=False)),
        received_at__isnull=False,
        payment_type=TASK_PAYMENT_METHOD_BITCOIN)
    task_distribution = []
    for payment in payments:
        portion_distribution = []
        for item in participation_shares:
            participant = item['participant']
            share = item['share']
            portion_sent = False

            if not participant.user:
                continue

            participant_pay, created = ParticipantPayment.objects.get_or_create(
                source=payment, participant=participant)
            payment_method = participant.user.payment_method
            if created or (participant_pay
                           and participant_pay.status == STATUS_PENDING):
                if payment_method in [
                        PAYMENT_METHOD_BTC_ADDRESS, PAYMENT_METHOD_BTC_WALLET
                ]:
                    if not (participant_pay.destination
                            and bitcoin_utils.is_valid_btc_address(
                                participant_pay.destination)):
                        participant_pay.destination = participant.user.btc_address
                    transaction = send_payment_share(
                        destination=participant_pay.destination,
                        amount=Decimal(share) * payment.task_btc_share(task),
                        idem=str(participant_pay.idem_key),
                        description='%s - %s' %
                        (pay_description, participant.user.display_name))
                    if transaction.status not in [
                            coinbase_utils.TRANSACTION_STATUS_FAILED,
                            coinbase_utils.TRANSACTION_STATUS_EXPIRED,
                            coinbase_utils.TRANSACTION_STATUS_CANCELED
                    ]:
                        participant_pay.ref = transaction.id
                        participant_pay.btc_sent = abs(
                            Decimal(transaction.amount.amount))
                        participant_pay.status = STATUS_PROCESSING
                        participant_pay.save()
                        portion_sent = True
                elif payment_method == PAYMENT_METHOD_MOBILE_MONEY:
                    share_amount = Decimal(share) * payment.task_btc_share(
                        task)
                    recipients = [{
                        bitpesa.KEY_REQUESTED_AMOUNT:
                        float(
                            bitcoin_utils.get_valid_btc_amount(share_amount)),
                        bitpesa.KEY_REQUESTED_CURRENCY:
                        CURRENCY_BTC,
                        bitpesa.KEY_PAYOUT_METHOD: {
                            bitpesa.KEY_TYPE:
                            bitpesa.get_pay_out_method(
                                participant.user.mobile_money_cc),
                            bitpesa.KEY_DETAILS: {
                                bitpesa.KEY_FIRST_NAME:
                                participant.user.first_name,
                                bitpesa.KEY_LAST_NAME:
                                participant.user.last_name,
                                bitpesa.KEY_PHONE_NUMBER:
                                participant.user.mobile_money_number
                            }
                        }
                    }]
                    bitpesa_nonce = str(uuid4())
                    transaction = bitpesa.create_transaction(
                        BITPESA_SENDER,
                        recipients,
                        input_currency=CURRENCY_BTC,
                        transaction_id=participant_pay.id,
                        nonce=bitpesa_nonce)
                    if transaction:
                        participant_pay.ref = transaction.get(
                            bitpesa.KEY_ID, None)
                        participant_pay.status = STATUS_INITIATED
                        participant_pay.extra = bitpesa_nonce
                        participant_pay.save()

                        if complete_bitpesa_payment(transaction):
                            portion_sent = True
            elif participant_pay and payment_method == PAYMENT_METHOD_MOBILE_MONEY and \
                            participant_pay.status == STATUS_INITIATED:
                transaction_details = bitpesa.call_api(
                    bitpesa.get_endpoint_url('transactions/%s' %
                                             participant_pay.ref),
                    'GET',
                    str(uuid4()),
                    data={})
                transaction = transaction_details.json().get(
                    bitpesa.KEY_OBJECT)
                if transaction and complete_bitpesa_payment(transaction):
                    portion_sent = True

            portion_distribution.append(portion_sent)
        if portion_distribution and False not in portion_distribution:
            payment.processed = True
            payment.save()
            task_distribution.append(True)
        else:
            task_distribution.append(False)
    if task_distribution and not (False in task_distribution):
        task.pay_distributed = True
        task.save()
コード例 #10
0
ファイル: background.py プロジェクト: tunga-io/tunga-api
def process_invoices(pk, invoice_types=('client',), user_id=None, developer_ids=None, is_admin=False, filepath=None):
    """
    :param pk: id of the task
    :param invoice_types: tuple of invoice types to generate e.g 'client', 'developer', 'tunga'
    :param user_id: user viewing the invoice(s)
    :param developer_ids: participant invoices to generate. only applies to 'developer' and 'tunga' invoices
    :param is_admin: is requester an admin?
    :param filepath: file to store invoice in
    :return:
    """
    all_invoices = list()

    if pk == 'all':
        tasks = Task.objects.filter(closed=True, taskinvoice__isnull=False)
        if user_id and not is_admin:
            tasks = tasks.filter(
                Q(user_id=user_id) | Q(owner_id=user_id) | Q(pm_id=user_id) | Q(participant__user_id=user_id))
        tasks = tasks.distinct()
    else:
        tasks = Task.objects.filter(id=pk)

    for task in tasks:
        invoice = task.invoice
        if invoice:
            invoice = invoice.clean_invoice()
            if invoice.number:
                initial_invoice_data = TaskInvoiceSerializer(invoice).data
                initial_invoice_data['date'] = task.invoice.created_at.strftime('%d %B %Y')

                task_owner = task.user
                if task.owner:
                    task_owner = task.owner

                participation_shares = task.get_participation_shares()
                common_developer_info = list()
                for share_info in participation_shares:
                    participant = share_info['participant']
                    developer, created = DeveloperNumber.objects.get_or_create(user=participant.user)

                    amount_details = invoice.get_amount_details(share=share_info['share'])

                    if (not developer_ids or participant.user.id in developer_ids) and \
                            not (participant.prepaid or (participant.prepaid is None and participant.user.is_internal)):
                        common_developer_info.append({
                            'developer': InvoiceUserSerializer(participant.user).data,
                            'amount': amount_details,
                            'dev_number': developer.number or '',
                            'participant': participant
                        })

                for invoice_type in invoice_types:
                    if invoice_type == 'developer' and invoice.version > 1:
                        continue
                    task_developers = []
                    invoice_data = copy(initial_invoice_data)

                    if invoice_type == 'client':
                        invoice_data['number_client'] = invoice.invoice_id(invoice_type='client')
                        task_developers = [dict()]
                    else:
                        for common_info in common_developer_info:
                            final_dev_info = copy(common_info)
                            final_dev_info['number'] = invoice.invoice_id(
                                invoice_type=invoice_type,
                                user=common_info['participant'] and common_info['participant'].user or None
                            )

                            participant_payment_method = None
                            if common_info['participant']:
                                try:
                                    participant_payment = common_info['participant'].participantpayment_set.filter().latest('created_at')
                                    if participant_payment and bitcoin_utils.is_valid_btc_address(participant_payment.destination):
                                        participant_payment_method = PAYMENT_METHOD_BITCOIN
                                except:
                                    pass

                            final_dev_info['payment_method'] = participant_payment_method
                            task_developers.append(final_dev_info)

                    invoice_data['developers'] = task_developers

                    all_invoices.append(
                        dict(
                            invoice_type=invoice_type,
                            invoice=invoice_data,
                            location=invoice_type == 'client' and invoice.vat_location_client or VAT_LOCATION_WORLD
                        )
                    )
    ctx = dict(
        invoices=all_invoices
    )

    rendered_html = render_to_string("tunga/pdf/invoice.html", context=ctx).encode(encoding="UTF-8")
    if filepath:
        HTML(string=rendered_html, encoding='utf-8').write_pdf(filepath)
    return rendered_html