Example #1
0
    def _notify_user(self, patched_product):
        if self._product_name is not None:
            try:
                not_handler = NotificationsHandler()
                order = Order.objects.get(
                    order_id=patched_product['name'].split('=')[-1])

                not_handler.send_product_upgraded_notification(
                    order,
                    order.get_product_contract(unicode(patched_product['id'])),
                    self._product_name)

            except:
                # A failure in the email notification is not relevant
                pass
    def _check_renovation_date(self, renovation_date, order, contract):
        now = datetime.now()

        timed = renovation_date - now

        if timed.days < 7:
            handler = NotificationsHandler()

            if timed.days < 0:
                # Notify that the subscription has finished
                handler.send_payment_required_notification(order, contract)

                # Set the product as suspended
                client = InventoryClient()
                client.suspend_product(contract.product_id)
            else:
                # There is less than a week remaining
                handler.send_near_expiration_notification(order, contract, timed.days)
Example #3
0
    def end_charging(self, transactions, concept):
        """
        Process the second step of a payment once the customer has approved the charge
        :param transactions: List of transactions applied including the total price and the related model
        :param concept: Concept of the charge, it can be initial, renovation, or use
        """

        # Update purchase state
        if self._order.state == 'pending':
            self._order.state = 'paid'
            self._order.save()

        time_stamp = datetime.now()

        self._order.pending_payment = {}

        for transaction in transactions:
            contract = self._order.get_item_contract(transaction['item'])
            # Update contracts
            contract.charges.append({
                'date': time_stamp,
                'cost': transaction['price'],
                'currency': transaction['currency'],
                'concept': concept
            })

            contract.last_charge = time_stamp

            self.end_processors[concept](contract, transaction)

            # If the customer has been charged create the CDR
            cdr_manager = CDRManager(self._order, contract)
            cdr_manager.generate_cdr(transaction['related_model'], unicode(time_stamp))

        self._order.save()

        # TODO: Improve the rollback in case of unexpected exception
        try:
            # Generate the invoice
            invoice_builder = InvoiceBuilder(self._order)
            invoice_builder.generate_invoice(transactions, concept)

            # Send notifications if required
            handler = NotificationsHandler()
            if concept == 'initial':
                # Send customer and provider notifications
                handler.send_acquired_notification(self._order)
                for cont in self._order.contracts:
                    handler.send_provider_notification(self._order, cont)

            elif concept == 'renovation' or concept == 'use':
                handler.send_renovation_notification(self._order, transactions)
        except:
            pass
Example #4
0
    def _send_notification(self, concept, transactions):
        # TODO: Improve the rollback in case of unexpected exception
        try:
            # Send notifications if required
            handler = NotificationsHandler()
            if concept == 'initial':
                # Send customer and provider notifications
                handler.send_acquired_notification(self._order)
                for cont in self._order.contracts:
                    handler.send_provider_notification(self._order, cont)

            elif concept == 'recurring' or concept == 'usage':
                handler.send_renovation_notification(self._order, transactions)
        except:
            pass
    def _check_renovation_date(self, renovation_date, order, contract):
        now = datetime.utcnow()

        timed = renovation_date - now

        if timed.days < 7:
            handler = NotificationsHandler()

            if timed.days < 0:
                # Suspend the access to the service
                on_product_suspended(order, contract)

                # Notify that the subscription has finished
                handler.send_payment_required_notification(order, contract)

                # Set the product as suspended
                client = InventoryClient()
                client.suspend_product(contract.product_id)

            else:
                # There is less than a week remaining
                handler.send_near_expiration_notification(
                    order, contract, timed.days)
Example #6
0
 def __init__(self, payouts, reports):
     super().__init__()
     self.payouts = payouts
     self.reports = reports
     self.notifications = NotificationsHandler()
Example #7
0
class PayoutWatcher(threading.Thread):
    def __init__(self, payouts, reports):
        super().__init__()
        self.payouts = payouts
        self.reports = reports
        self.notifications = NotificationsHandler()

    def _mark_as_paid(self, report, paid=True):
        headers = {
            'content-type': 'application/json',
            'X-Nick-Name': settings.STORE_NAME,
            'X-Roles': settings.ADMIN_ROLE,
            'X-Email': settings.WSTOREMAIL
        }

        data = [{'op': 'replace', 'path': '/paid', 'value': paid}]

        # Make request
        url = settings.RSS
        if not url.endswith('/'):
            url += '/'

        url += 'rss/settlement/reports/{}'.format(report)

        response = requests.patch(url, json=data, headers=headers)

        if response.status_code != 200:
            print("Error mark as paid report {}: {}".format(
                report, response.reason))
            return []

        return response.json()

    def _update_status(self, payout):
        rpayout = ReportsPayout.objects.get(
            payout_id=payout['batch_header']['payout_batch_id'])
        rpayout.status = payout['batch_header']['batch_status']
        rpayout.save()

    def _safe_get_semi_paid(self, report_id):
        try:
            report = ReportSemiPaid.objects.get(report=report_id)
            return report
        except ObjectDoesNotExist:
            r = ReportSemiPaid(report=report_id)
            r.save()
            return r

    def _safe_get_semi_paid_from_item(self, item):
        sender_id = item['payout_item']['sender_item_id']
        report_id = sender_id.split('_')[0]
        return self._safe_get_semi_paid(report_id)

    def _analyze_item(self, item):
        status = item['transaction_status']
        semipaid = self._safe_get_semi_paid_from_item(item)
        pitem = item['payout_item']
        mail = pitem['receiver']

        if status != 'SUCCESS':
            errors = item['errors']

            if mail not in semipaid.failed:
                semipaid.failed.append(mail)
            if mail in semipaid.success:
                semipaid.success.remove(mail)

            semipaid.errors[mail.replace(".", "(dot)")] = {
                'error_message': errors['message'],
                'error_name': errors['name'],
                'item_id': item['payout_item_id'],
                'payout_id': item['payout_batch_id'],
                'transaction_status': status,
                'transaction_id': item['transaction_id']
            }
            semipaid.save()

            try:
                # Only send the notification if it's an user error (DENIED and FAILED?)
                if status in [
                        'DENIED', 'PENDING', 'UNCLAIMED', 'RETURNED', 'ONHOLD',
                        'BLOCKED', 'FAILED'
                ]:
                    self.notifications.send_payout_error(
                        mail, errors['message'])
            except:
                pass

            return False

        if mail in semipaid.failed:
            semipaid.failed.remove(mail)
        if semipaid.errors.get(mail.replace(".", "(dot)")) is not None:
            del semipaid.errors[mail.replace(".", "(dot)")]
        if mail not in semipaid.success:
            semipaid.success.append(mail)
        semipaid.save()

        return True

    def _check_reports_payout(self, payout):
        reports_id = {
            item['payout_item']['sender_item_id'].split('_')[0]
            for item in payout['items']
        }
        for report_id in reports_id:
            filtered = list(
                filter(lambda x: x.get('id') == int(report_id), self.reports))
            if len(filtered) == 0:
                continue

            report = filtered[0]
            reportmails = [
                User.objects.get(username=report['ownerProviderId']).email
            ]
            reportmails.extend([
                User.objects.get(username=stake['stakeholderId']).email
                for stake in report.get('stakeholders', [])
            ])

            semipaid = self._safe_get_semi_paid(report_id)
            semipaid.failed = [x for x in semipaid.failed if x in reportmails
                               ]  # Clean mails not in report
            if len(semipaid.failed) == 0 and all(
                [mail in semipaid.success for mail in reportmails]):
                # Mark as paid in remote
                self._mark_as_paid(report_id)
                # Remove semipaid
                semipaid.delete()
            else:
                semipaid.save()

    def _payout_success(self, payout):
        for item in payout['items']:
            self._analyze_item(item)
        self._check_reports_payout(payout)

    def _check_payout(self, payout):
        try:
            pay = Payout.find(payout['batch_header']['payout_batch_id'])
            status = pay['batch_header']['batch_status']
            self._update_status(pay)
            if status == 'DENIED':
                return False
            if status == 'SUCCESS':
                self._payout_success(pay)
                return False
            if pay['batch_header']['batch_status'] in [
                    'PENDING', 'PROCESSING'
            ]:
                return True
            return False
        except Exception:
            return False

    def _check_payouts(self):
        new = []
        for payout in self.payouts:
            pending = self._check_payout(payout)
            if pending:
                # Still pending or processing
                new.append(payout)
        self.payouts = new

    def run(self):
        while len(self.payouts) != 0:
            self._check_payouts()
            time.sleep(1)
 def __init__(self, payouts, reports):
     threading.Thread.__init__(self)
     self.payouts = payouts
     self.reports = reports
     self.notifications = NotificationsHandler()
class PayoutWatcher(threading.Thread):
    def __init__(self, payouts, reports):
        threading.Thread.__init__(self)
        self.payouts = payouts
        self.reports = reports
        self.notifications = NotificationsHandler()

    def _mark_as_paid(self, report, paid=True):
        headers = {
            "content-type": "application/json",
            "X-Nick-Name": settings.STORE_NAME,
            "X-Roles": "provider",
            "X-Email": settings.WSTOREMAIL,
        }

        data = [{"op": "replace", "path": "/paid", "value": paid}]

        # Make request
        url = settings.RSS
        if not url.endswith("/"):
            url += "/"

        url += "rss/settlement/reports/{}".format(report)

        response = requests.patch(url, json=data, headers=headers)

        if response.status_code != 200:
            print("Error mark as paid report {}: {}".format(report, response.reason))
            return []

        return response.json()

    def _update_status(self, payout):
        rpayout = ReportsPayout.objects.get(payout_id=payout["batch_header"]["payout_batch_id"])
        rpayout.status = payout["batch_header"]["batch_status"]
        rpayout.save()

    def _safe_get_semi_paid(self, report_id):
        try:
            report = ReportSemiPaid.objects.get(report=report_id)
            return report
        except ObjectDoesNotExist:
            r = ReportSemiPaid(report=report_id)
            r.save()
            return r

    def _safe_get_semi_paid_from_item(self, item):
        sender_id = item["payout_item"]["sender_item_id"]
        report_id = sender_id.split("_")[0]
        return self._safe_get_semi_paid(report_id)

    def _analyze_item(self, item):
        status = item["transaction_status"]
        semipaid = self._safe_get_semi_paid_from_item(item)
        pitem = item["payout_item"]
        mail = pitem["receiver"]

        if status != "SUCCESS":
            errors = item["errors"]

            if mail not in semipaid.failed:
                semipaid.failed.append(mail)
            if mail in semipaid.success:
                semipaid.success.remove(mail)

            semipaid.errors[mail.replace(".", "(dot)")] = {
                "error_message": errors["message"],
                "error_name": errors["name"],
                "item_id": item["payout_item_id"],
                "payout_id": item["payout_batch_id"],
                "transaction_status": status,
                "transaction_id": item["transaction_id"],
            }
            semipaid.save()

            try:
                # Only send the notification if it's an user error (DENIED and FAILED?)
                if status in ["DENIED", "PENDING", "UNCLAIMED", "RETURNED", "ONHOLD", "BLOCKED", "FAILED"]:
                    self.notifications.send_payout_error(mail, errors["message"])
            except:
                pass

            return False

        if mail in semipaid.failed:
            semipaid.failed.remove(mail)
        if semipaid.errors.get(mail.replace(".", "(dot)")) is not None:
            del semipaid.errors[mail.replace(".", "(dot)")]
        if mail not in semipaid.success:
            semipaid.success.append(mail)
        semipaid.save()

        return True

    def _check_reports_payout(self, payout):
        reports_id = {item["payout_item"]["sender_item_id"].split("_")[0] for item in payout["items"]}
        for report_id in reports_id:
            filtered = list(filter(lambda x: x.get("id") == int(report_id), self.reports))
            if len(filtered) == 0:
                continue

            report = filtered[0]
            reportmails = [User.objects.get(username=report["ownerProviderId"]).email]
            reportmails.extend(
                [User.objects.get(username=stake["stakeholderId"]).email for stake in report.get("stakeholders", [])]
            )

            semipaid = self._safe_get_semi_paid(report_id)
            semipaid.failed = [x for x in semipaid.failed if x in reportmails]  # Clean mails not in report
            if len(semipaid.failed) == 0 and all([mail in semipaid.success for mail in reportmails]):
                # Mark as paid in remote
                self._mark_as_paid(report_id)
                # Remove semipaid
                semipaid.delete()
            else:
                semipaid.save()

    def _payout_success(self, payout):
        for item in payout["items"]:
            self._analyze_item(item)
        self._check_reports_payout(payout)

    def _check_payout(self, payout):
        try:
            pay = Payout.find(payout["batch_header"]["payout_batch_id"])
            status = pay["batch_header"]["batch_status"]
            self._update_status(pay)
            if status == "DENIED":
                return False
            if status == "SUCCESS":
                self._payout_success(pay)
                return False
            if pay["batch_header"]["batch_status"] in ["PENDING", "PROCESSING"]:
                return True
            return False
        except Exception:
            return False

    def _check_payouts(self):
        new = []
        for payout in self.payouts:
            pending = self._check_payout(payout)
            if pending:
                # Still pending or processing
                new.append(payout)
        self.payouts = new

    def run(self):
        while len(self.payouts) != 0:
            self._check_payouts()
            time.sleep(1)
    def end_charging(self, transactions, concept):
        """
        Process the second step of a payment once the customer has approved the charge
        :param transactions: List of transactions applied including the total price and the related model
        :param concept: Concept of the charge, it can be initial, renovation, or use
        """

        # Update purchase state
        if self._order.state == 'pending':
            self._order.state = 'paid'
            self._order.save()

        time_stamp = datetime.utcnow()

        self._order.pending_payment = {}

        invoice_builder = InvoiceBuilder(self._order)
        billing_client = BillingClient() if concept != 'initial' else None

        for transaction in transactions:
            contract = self._order.get_item_contract(transaction['item'])
            contract.last_charge = time_stamp

            valid_from, valid_to = self.end_processors[concept](contract, transaction)

            # If the customer has been charged create the CDR
            cdr_manager = CDRManager(self._order, contract)
            cdr_manager.generate_cdr(transaction['related_model'], time_stamp.isoformat() + 'Z')

            # Generate the invoice
            invoice_path = ''
            try:
                invoice_path = invoice_builder.generate_invoice(contract, transaction, concept)
            except:
                pass

            # Update contracts
            charge = Charge(
                date=time_stamp,
                cost=transaction['price'],
                duty_free=transaction['duty_free'],
                currency=transaction['currency'],
                concept=concept,
                invoice=invoice_path
            )
            contract.charges.append(charge)

            # Send the charge to the billing API to allow user accesses
            if concept != 'initial':
                # When the change concept is initial, the product has not been yet created in the inventory
                billing_client.create_charge(charge, contract.product_id, start_date=valid_from, end_date=valid_to)

        self._order.save()

        # TODO: Improve the rollback in case of unexpected exception
        try:
            # Send notifications if required
            handler = NotificationsHandler()
            if concept == 'initial':
                # Send customer and provider notifications
                handler.send_acquired_notification(self._order)
                for cont in self._order.contracts:
                    handler.send_provider_notification(self._order, cont)

            elif concept == 'recurring' or concept == 'usage':
                handler.send_renovation_notification(self._order, transactions)
        except:
            pass
Example #11
0
 def __init__(self, payouts, reports):
     threading.Thread.__init__(self)
     self.payouts = payouts
     self.reports = reports
     self.notifications = NotificationsHandler()