Esempio n. 1
0
def notify_payment(payment):
    invoice = payment.invoice
    member = invoice.member
    service = get_service_instance()
    config = service.config

    invoicing_config = get_invoicing_config_instance()
    try:
        invoice_pdf_file = generate_pdf_invoice(invoicing_config, invoice)
    except:
        invoice_pdf_file = None

    if member.email:
        invoice_url = service.url + reverse('billing:invoice_detail',
                                            args=(invoice.id, ))
        subject, message, sms_text = get_payment_confirmation_message(
            payment, member)
        html_content = get_mail_content(
            subject,
            message,
            template_name='billing/mails/notice.html',
            extra_context={
                'member_name': member.first_name,
                'invoice': invoice,
                'cta': _("View invoice"),
                'invoice_url': invoice_url
            })
        sender = '%s <no-reply@%s>' % (config.company_name, service.domain)
        msg = XEmailMessage(subject, html_content, sender, [member.email])
        if invoice_pdf_file:
            msg.attach_file(invoice_pdf_file)
        msg.content_subtype = "html"
        Thread(target=lambda m: m.send(), args=(msg, )).start()
Esempio n. 2
0
 def get(self, request, *args, **kwargs):
     action = request.GET.get('action')
     invoice_id = kwargs['invoice_id']
     try:
         invoice = Invoice.objects.select_related(
             'member', 'subscription').get(pk=invoice_id)
     except Invoice.DoesNotExist:
         raise Http404("Invoice not found")
     if action == 'cash_in':
         return self.cash_in(invoice, request)
     if action == 'generate_pdf':
         from ikwen.billing.utils import generate_pdf_invoice
         invoicing_config = get_invoicing_config_instance()
         invoice_pdf_file = generate_pdf_invoice(invoicing_config, invoice)
         media_root = getattr(settings, 'MEDIA_ROOT')
         media_url = getattr(settings, 'MEDIA_URL')
         return HttpResponseRedirect(
             invoice_pdf_file.replace(media_root, media_url))
     member = invoice.member
     if member and not member.is_ghost:
         if request.user.is_authenticated() and not request.user.is_staff:
             if request.user != member:
                 next_url = reverse('ikwen:sign_in')
                 next_url += '?next=' + reverse('billing:invoice_detail',
                                                args=(invoice.id, ))
                 return HttpResponseRedirect(next_url)
     return super(InvoiceDetail, self).get(request, *args, **kwargs)
Esempio n. 3
0
def confirm_invoice_payment(request, *args, **kwargs):
    """
    This function has no URL associated with it.
    It serves as ikwen setting "MOMO_AFTER_CHECKOUT"
    """
    from echo.models import Balance
    from echo.utils import LOW_MAIL_LIMIT, notify_for_low_messaging_credit, notify_for_empty_messaging_credit
    tx = kwargs.get('transaction')
    now = datetime.now()
    service = get_service_instance()
    config = service.config
    invoicing_config = get_invoicing_config_instance()
    invoice_id = request.session['object_id']
    amount = request.session['amount']
    invoice = Invoice.objects.select_related('subscription').get(pk=invoice_id)
    invoice.paid += amount
    invoice.status = Invoice.PAID
    if invoicing_config.processing_fees_on_customer:
        invoice.processing_fees = config.ikwen_share_fixed
    invoice.save()
    payment = Payment.objects.create(invoice=invoice, method=Payment.MOBILE_MONEY,
                                     amount=amount, processor_tx_id=tx.processor_tx_id)
    subscription = invoice.subscription
    if invoicing_config.separate_billing_cycle:
        extra_months = request.session['extra_months']
        total_months = invoice.months_count + extra_months
        days = get_days_count(total_months)
    else:
        extra_months = 0
        days = invoice.subscription.product.duration
        total_months = None
    if subscription.status == Service.SUSPENDED:
        invoicing_config = get_invoicing_config_instance()
        days -= invoicing_config.tolerance  # Catch-up days that were offered before service suspension
        expiry = now + timedelta(days=days)
        expiry = expiry.date()
    elif subscription.expiry:
        expiry = subscription.expiry + timedelta(days=days)
    else:
        expiry = now + timedelta(days=days)
        expiry = expiry.date()
    subscription.expiry = expiry
    subscription.status = Service.ACTIVE
    subscription.save()
    mean = request.session['mean']
    share_payment_and_set_stats(invoice, total_months, mean)
    member = request.user
    sudo_group = Group.objects.using(UMBRELLA).get(name=SUDO)
    add_event(service, PAYMENT_CONFIRMATION, member=member, object_id=invoice.id)
    add_event(service, PAYMENT_CONFIRMATION, group_id=sudo_group.id, object_id=invoice.id)

    if invoicing_config.return_url:
        params = {'reference_id': subscription.reference_id, 'invoice_number': invoice.number,
                  'amount_paid': amount, 'processor_tx_id': tx.processor_tx_id, 'extra_months': extra_months}
        Thread(target=notify_event, args=(service, invoicing_config.return_url, params)).start()

    try:
        invoice_pdf_file = generate_pdf_invoice(invoicing_config, invoice)
    except:
        invoice_pdf_file = None

    balance, update = Balance.objects.using(WALLETS_DB_ALIAS).get_or_create(service_id=service.id)
    if member.email:
        if 0 < balance.mail_count < LOW_MAIL_LIMIT:
            notify_for_low_messaging_credit(service, balance)
        if balance.mail_count <= 0:
            notify_for_empty_messaging_credit(service, balance)
        else:
            try:
                currency = Currency.active.default().symbol
            except:
                currency = config.currency_code
            invoice_url = service.url + reverse('billing:invoice_detail', args=(invoice.id,))
            subject, message, sms_text = get_payment_confirmation_message(payment, member)
            html_content = get_mail_content(subject, message, template_name='billing/mails/notice.html',
                                            extra_context={'member_name': member.first_name, 'invoice': invoice,
                                                           'cta': _("View invoice"), 'invoice_url': invoice_url,
                                                           'currency': currency})
            sender = '%s <no-reply@%s>' % (config.company_name, service.domain)
            msg = XEmailMessage(subject, html_content, sender, [member.email])
            msg.content_subtype = "html"
            if invoice_pdf_file:
                msg.attach_file(invoice_pdf_file)
            balance.mail_count -= 1
            balance.save()
            Thread(target=lambda m: m.send(), args=(msg,)).start()
    return HttpResponseRedirect(request.session['return_url'])
Esempio n. 4
0
def confirm_service_invoice_payment(request, *args, **kwargs):
    """
    This view is run after successful user cashout "MOMO_AFTER_CHECKOUT"
    """
    status = request.GET['status']
    message = request.GET['message']
    operator_tx_id = request.GET['operator_tx_id']
    phone = request.GET['phone']
    tx_id = kwargs['tx_id']
    extra_months = int(kwargs['extra_months'])
    try:
        tx = MoMoTransaction.objects.using(WALLETS_DB_ALIAS).get(pk=tx_id, is_running=True)
        if not getattr(settings, 'DEBUG', False):
            tx_timeout = getattr(settings, 'IKWEN_PAYMENT_GATEWAY_TIMEOUT', 15) * 60
            expiry = tx.created_on + timedelta(seconds=tx_timeout)
            if datetime.now() > expiry:
                return HttpResponse("Transaction %s timed out." % tx_id)

        tx.status = status
        tx.message = 'OK' if status == MoMoTransaction.SUCCESS else message
        tx.processor_tx_id = operator_tx_id
        tx.phone = phone
        tx.is_running = False
        tx.save()
    except:
        raise Http404("Transaction %s not found" % tx_id)
    if status != MoMoTransaction.SUCCESS:
        return HttpResponse("Notification for transaction %s received with status %s" % (tx_id, status))
    invoice_id = tx.object_id
    amount = tx.amount
    signature = tx.task_id

    callback_signature = kwargs.get('signature')
    no_check_signature = request.GET.get('ncs')
    if getattr(settings, 'DEBUG', False):
        if not no_check_signature:
            if callback_signature != signature:
                return HttpResponse('Invalid transaction signature')
    else:
        if callback_signature != signature:
            return HttpResponse('Invalid transaction signature')
    now = datetime.now()
    ikwen_service = get_service_instance()
    invoice = Invoice.objects.get(pk=invoice_id)
    invoice.paid += amount
    invoice.status = Invoice.PAID
    invoice.save()
    payment = Payment.objects.create(invoice=invoice, method=Payment.MOBILE_MONEY,
                                     amount=amount, processor_tx_id=tx.processor_tx_id)
    service = invoice.service
    total_months = invoice.months_count + extra_months
    days = get_days_count(total_months)
    invoicing_config = get_invoicing_config_instance()
    if service.status == Service.SUSPENDED:
        days -= invoicing_config.tolerance  # Catch-up days that were offered before service suspension
        expiry = now + timedelta(days=days)
        expiry = expiry.date()
    elif service.expiry:
        expiry = service.expiry + timedelta(days=days)
    else:
        expiry = now + timedelta(days=days)
        expiry = expiry.date()
    service.expiry = expiry
    service.status = Service.ACTIVE
    if invoice.is_one_off:
        service.version = Service.FULL
        try:
            support_bundle = SupportBundle.objects.get(type=SupportBundle.TECHNICAL, channel=SupportBundle.PHONE, cost=0)
            token = ''.join([random.SystemRandom().choice(string.digits) for i in range(6)])
            support_expiry = now + timedelta(days=support_bundle.duration)
            SupportCode.objects.create(service=service, token=token, bundle=support_bundle,
                                       balance=support_bundle.quantity, expiry=support_expiry)
            logger.debug("Free Support Code created for %s" % service)
        except SupportBundle.DoesNotExist:
            logger.error("Free Support Code not created for %s" % service, exc_info=True)
    service.save()
    mean = tx.wallet
    is_early_payment = False
    if service.app.slug == 'kakocase' or service.app.slug == 'webnode':
        if invoice.due_date <= now.date():
            is_early_payment = True
        refill_tsunami_messaging_bundle(service, is_early_payment)
    share_payment_and_set_stats(invoice, total_months, mean)
    member = service.member
    vendor = service.retailer
    vendor_is_dara = vendor and vendor.app.slug == DARAJA
    if vendor and not vendor_is_dara:
        add_database_to_settings(vendor.database)
        sudo_group = Group.objects.using(vendor.database).get(name=SUDO)
    else:
        vendor = ikwen_service
        sudo_group = Group.objects.using(UMBRELLA).get(name=SUDO)
    add_event(vendor, PAYMENT_CONFIRMATION, member=member, object_id=invoice.id)
    add_event(vendor, PAYMENT_CONFIRMATION, group_id=sudo_group.id, object_id=invoice.id)

    try:
        invoice_pdf_file = generate_pdf_invoice(invoicing_config, invoice)
    except:
        invoice_pdf_file = None

    if member.email:
        activate(member.language)
        invoice_url = service.url + reverse('billing:invoice_detail', args=(invoice.id,))
        subject, message, sms_text = get_payment_confirmation_message(payment, member)
        html_content = get_mail_content(subject, message, service=vendor, template_name='billing/mails/notice.html',
                                        extra_context={'member_name': member.first_name, 'invoice': invoice,
                                                       'cta': _("View invoice"), 'invoice_url': invoice_url,
                                                       'early_payment': is_early_payment})
        sender = '%s <no-reply@%s>' % (vendor.config.company_name, ikwen_service.domain)
        msg = XEmailMessage(subject, html_content, sender, [member.email])
        if vendor != ikwen_service and not vendor_is_dara:
            msg.service = vendor
        if invoice_pdf_file:
            msg.attach_file(invoice_pdf_file)
        msg.content_subtype = "html"
        if getattr(settings, 'UNIT_TESTING', False):
            msg.send()
        else:
            Thread(target=lambda m: m.send(), args=(msg,)).start()
    return HttpResponse("Notification received")
Esempio n. 5
0
def confirm_invoice_payment(request, *args, **kwargs):
    tx = kwargs[
        'tx']  # Decoration with @momo_gateway_callback makes 'tx' available in kwargs
    school = Service.objects.get(pk=tx.service_id)
    school_config = SchoolConfig.objects.get(service=school)
    ikwen_charges = tx.amount * school_config.ikwen_share_rate / 100

    tx.fees = ikwen_charges
    tx.save()
    mean = tx.wallet
    db = school.database
    add_database(db)
    invoice = Invoice.objects.using(db).get(pk=tx.object_id)
    payment = Payment.objects.using(db).create(
        invoice=invoice,
        method=Payment.MOBILE_MONEY,
        amount=tx.amount,
        processor_tx_id=tx.processor_tx_id)
    invoice.paid = tx.amount
    invoice.status = Invoice.PAID
    invoice.save()
    student = invoice.student
    if not school_config.is_public or (school_config.is_public
                                       and not invoice.is_tuition):
        amount = tx.amount - ikwen_charges
        school.raise_balance(amount, provider=mean)
    else:
        amount = tx.amount
    student.set_has_new(using=school.database)
    student.save(using='default')

    set_counters(school)
    increment_history_field(school, 'turnover_history', tx.amount)
    increment_history_field(school, 'earnings_history', amount)
    increment_history_field(school, 'transaction_count_history')
    increment_history_field(school, 'transaction_earnings_history', amount)

    member = invoice.member
    if member.email:
        try:
            currency = Currency.active.default().symbol
        except:
            currency = school_config.currency_code
        invoice_url = school.url + reverse('billing:invoice_detail',
                                           args=(invoice.id, ))
        subject, message, sms_text = get_payment_confirmation_message(
            payment, member)
        html_content = get_mail_content(
            subject,
            message,
            template_name='billing/mails/notice.html',
            extra_context={
                'member_name': member.first_name,
                'invoice': invoice,
                'cta': _("View invoice"),
                'invoice_url': invoice_url,
                'currency': currency
            })
        sender = '%s <no-reply@%s>' % (school_config.company_name,
                                       school.domain)
        msg = XEmailMessage(subject, html_content, sender, [member.email])
        msg.content_subtype = "html"
        bcc = [
            email.strip()
            for email in school_config.notification_emails.split(',')
            if email.strip()
        ]
        bcc += [school_config.contact_email, school.member.email]
        msg.bcc = list(set(bcc))
        try:
            invoicing_config = get_invoicing_config_instance()
            invoice_pdf_file = generate_pdf_invoice(invoicing_config, invoice)
            msg.attach_file(invoice_pdf_file)
        except:
            pass
    return HttpResponse(
        "Notification for transaction %s received with status %s" %
        (tx.id, tx.status))
Esempio n. 6
0
def confirm_my_kids_payment(request, *args, **kwargs):
    tx = kwargs['tx']
    invoice = Invoice.objects.get(pk=tx.object_id)
    school = invoice.school
    school_config = SchoolConfig.objects.get(service=school)
    ikwen_charges = tx.amount * school_config.my_kids_share_rate / 100
    tx.fees = ikwen_charges
    tx.save(using='wallets')
    mean = tx.wallet

    amount = tx.amount - ikwen_charges
    school.raise_balance(amount, provider=mean)
    share_payment_and_set_stats(invoice, mean, tx)

    invoice.paid = invoice.amount
    invoice.status = Invoice.PAID
    invoice.save()

    member = invoice.member
    student = invoice.student
    max_expiry = datetime(day=31, month=8, year=get_school_year() + 1)
    days = get_billing_cycle_days_count(invoice.my_kids_cycle)
    expiry = datetime.now() + timedelta(days=days)
    expiry = min(expiry, max_expiry)
    student.my_kids_expiry = expiry
    student.my_kids_expired = False
    student.save()

    db = school.database
    add_database(db)
    invoice.save(using=db)
    payment = Payment(invoice=invoice,
                      method=Payment.MOBILE_MONEY,
                      amount=tx.amount,
                      processor_tx_id=tx.processor_tx_id)
    if school_config.my_kids_share_rate < 100:
        # Payment appears in school log panel only if the have something to collect out of that
        payment.save(using=db)

    if member.email:
        try:
            currency = Currency.active.default().symbol
        except:
            currency = school_config.currency_code
        invoice_url = school.url + reverse('billing:invoice_detail',
                                           args=(invoice.id, ))
        subject, message, sms_text = get_payment_confirmation_message(
            payment, member)
        html_content = get_mail_content(
            subject,
            message,
            template_name='billing/mails/notice.html',
            extra_context={
                'member_name': member.first_name,
                'invoice': invoice,
                'cta': _("View invoice"),
                'invoice_url': invoice_url,
                'currency': currency
            })
        sender = '%s <no-reply@%s>' % (school_config.company_name,
                                       school.domain)
        msg = XEmailMessage(subject, html_content, sender, [member.email])
        msg.content_subtype = "html"
        try:
            invoicing_config = get_invoicing_config_instance()
            invoice_pdf_file = generate_pdf_invoice(invoicing_config, invoice)
            msg.attach_file(invoice_pdf_file)
        except:
            pass
    return HttpResponse(
        "Notification for transaction %s received with status %s" %
        (tx.id, tx.status))
Esempio n. 7
0
def send_invoices():
    """
    This cron task simply sends the Invoice *invoicing_gap* days before Subscription *expiry*
    """
    vendor, config, invoicing_config, connection = _init_base_vars()
    now = timezone.now()
    count, total_amount = 0, 0
    reminder_date_time = now + timedelta(days=invoicing_config.gap)
    subscription_qs = Subscription.objects.filter(status=Subscription.ACTIVE,
                                                  monthly_cost__gt=0, expiry__lt=reminder_date_time.date())
    logger.debug("%d Service candidate for invoice issuance." % subscription_qs.count())
    for subscription in subscription_qs:
        if getattr(settings, 'IS_IKWEN', False):
            if subscription.version == Service.FREE:
                continue
        try:
            pending_invoice = Invoice.objects.get(subscription=subscription, status=Invoice.PENDING)
            logger.debug("%s found for %s. Skipping" % (pending_invoice, subscription))
            continue  # Continue if a Pending invoice for this Subscription is found
        except Invoice.DoesNotExist:
            pass
        member = subscription.member
        number = get_next_invoice_number()
        months_count = None
        if config.__dict__.get('separate_billing_cycle', True):
            months_count = get_billing_cycle_months_count(subscription.billing_cycle)
            amount = subscription.monthly_cost * months_count
        else:
            amount = subscription.product.cost

        path_before = getattr(settings, 'BILLING_BEFORE_NEW_INVOICE', None)
        if path_before:
            before_new_invoice = import_by_path(path_before)
            val = before_new_invoice(subscription)
            if val is not None:  # Returning a not None value cancels the generation of a new Invoice for this Service
                continue

        entries = []
        if type(subscription) is Service:
            from daraja.models import DARAJA
            partner = subscription.retailer
            if partner and partner.app.slug != DARAJA:
                retail_config = ApplicationRetailConfig.objects.get(partner=partner, app=subscription.app)
                ikwen_price = retail_config.ikwen_monthly_cost
            else:
                ikwen_price = subscription.monthly_cost
            hosting = IkwenInvoiceItem(label=_('Website hosting'), price=ikwen_price, amount=subscription.monthly_cost)
            short_description = _("Project %s" % subscription.domain)
            entry = InvoiceEntry(item=hosting, short_description=short_description, quantity=months_count, total=amount)
            entries = [entry]
            try:
                cr_op_profile = CROperatorProfile.objects.get(service=subscription, is_active=True)
                if cr_op_profile.monthly_cost > 0:
                    plan = cr_op_profile.plan
                    cr_monthly_cost = cr_op_profile.monthly_cost
                    cr_item = IkwenInvoiceItem(label=_('Continuous Rewarding'), price=cr_monthly_cost, amount=cr_monthly_cost)
                    short_description = plan.name
                    cr_amount = months_count * cr_monthly_cost
                    amount += cr_amount
                    entry = InvoiceEntry(item=cr_item, short_description=short_description, quantity=months_count, total=cr_amount)
                    entries.append(entry)
            except CROperatorProfile.DoesNotExist:
                pass
        invoice = Invoice.objects.create(subscription=subscription, member=subscription.member, amount=amount,
                                         number=number, due_date=subscription.expiry, months_count=months_count,
                                         entries=entries, last_reminder=now)
        count += 1
        total_amount += amount
        add_event(vendor, NEW_INVOICE_EVENT, member=member, object_id=invoice.id)

        paid_by_wallet_debit = False
        if getattr(settings, 'IS_IKWEN', False) and subscription.balance >= invoice.amount:
            pay_with_wallet_balance(invoice)
            paid_by_wallet_debit = True
            logger.debug("Invoice for %s paid by wallet debit" % subscription.domain)

        subject, message, sms_text = get_invoice_generated_message(invoice)

        if member.email:
            activate(member.language)
            invoice_url = 'http://ikwen.com' + reverse('billing:invoice_detail', args=(invoice.id,))
            if paid_by_wallet_debit:
                subject = _("Thanks for your payment")
                invoice_url = 'http://ikwen.com' + reverse('billing:invoice_detail', args=(invoice.id,))
                context = {'wallet_debit': True, 'invoice': invoice, 'config': config,
                           'member_name': member.first_name, 'invoice_url': invoice_url, 'cta': _("View invoice")}
                html_content = get_mail_content(subject, '', template_name='billing/mails/wallet_debit_notice.html',
                                                extra_context=context)
            else:
                context = {'invoice_url': invoice_url, 'cta': _("Pay now"), 'currency': config.currency_symbol,
                           'service': vendor, 'config': config, 'logo': config.logo,
                           'project_name': vendor.project_name, 'company_name': config.company_name,
                           'member_name': member.first_name, 'invoice': invoice}
                html_content = get_mail_content(subject, message, template_name='billing/mails/notice.html',
                                                extra_context=context)
            # Sender is simulated as being no-reply@company_name_slug.com to avoid the mail
            # to be delivered to Spams because of origin check.
            sender = '%s <no-reply@%s>' % (config.company_name, vendor.domain)
            msg = EmailMessage(subject, html_content, sender, [member.email])
            try:
                invoice_pdf_file = generate_pdf_invoice(invoicing_config, invoice)
                msg.attach_file(invoice_pdf_file)
            except:
                pass
            if paid_by_wallet_debit:
                msg.bcc = ['*****@*****.**']
            msg.content_subtype = "html"
            invoice.last_reminder = timezone.now()
            try:
                if msg.send():
                    logger.debug("1st Invoice reminder for %s sent to %s" % (subscription.domain, member.email))
                    if not paid_by_wallet_debit:
                        invoice.reminders_sent = 1
                        invoice.save()
                else:
                    logger.error(u"Invoice #%s generated but mail not sent to %s" % (number, member.email),
                                 exc_info=True)
            except:
                logger.error(u"Connexion error on Invoice #%s to %s" % (number, member.email), exc_info=True)

        if sms_text:
            if member.phone:
                if config.sms_sending_method == Config.HTTP_API:
                    send_sms(member.phone, sms_text)
                else:
                    QueuedSMS.objects.create(recipient=member.phone, text=sms_text)

        path_after = getattr(settings, 'BILLING_AFTER_NEW_INVOICE', None)
        if path_after:
            after_new_invoice = import_by_path(path_after)
            after_new_invoice(invoice)

    try:
        connection.close()
    finally:
        if count > 0:
            report = SendingReport.objects.create(count=count, total_amount=total_amount)
            sudo_group = Group.objects.get(name=SUDO)
            add_event(vendor, INVOICES_SENT_EVENT, group_id=sudo_group.id, object_id=report.id)
Esempio n. 8
0
def confirm_service_invoice_payment(request, *args, **kwargs):
    """
    This view is run after successful user cashout "MOMO_AFTER_CHECKOUT"
    """
    tx = kwargs[
        'tx']  # Decoration with @momo_gateway_callback makes 'tx' available in kwargs
    extra_months = int(kwargs['extra_months'])
    payment = Payment.objects.select_related().get(pk=tx.object_id)
    payment.processor_tx_id = tx.processor_tx_id
    payment.save()
    invoice = payment.invoice
    now = datetime.now()
    ikwen_service = get_service_instance()
    invoice.paid += tx.amount
    if invoice.paid >= invoice.amount:
        invoice.status = Invoice.PAID
    invoice.save()
    service = invoice.service
    total_months = invoice.months_count + extra_months
    days = get_days_count(total_months)
    invoicing_config = get_invoicing_config_instance()
    if service.status == Service.SUSPENDED:
        days -= invoicing_config.tolerance  # Catch-up days that were offered before service suspension
        expiry = now + timedelta(days=days)
        expiry = expiry.date()
    elif service.expiry:
        expiry = service.expiry + timedelta(days=days)
    else:
        expiry = now + timedelta(days=days)
        expiry = expiry.date()
    service.expiry = expiry
    service.status = Service.ACTIVE
    if invoice.is_one_off:
        service.version = Service.FULL
        try:
            support_bundle = SupportBundle.objects.get(
                type=SupportBundle.TECHNICAL,
                channel=SupportBundle.PHONE,
                cost=0)
            token = ''.join([
                random.SystemRandom().choice(string.digits) for i in range(6)
            ])
            support_expiry = now + timedelta(days=support_bundle.duration)
            SupportCode.objects.create(service=service,
                                       token=token,
                                       bundle=support_bundle,
                                       balance=support_bundle.quantity,
                                       expiry=support_expiry)
            logger.debug("Free Support Code created for %s" % service)
        except SupportBundle.DoesNotExist:
            logger.error("Free Support Code not created for %s" % service,
                         exc_info=True)
    service.save()
    mean = tx.wallet
    is_early_payment = False
    if service.app.slug == 'kakocase' or service.app.slug == 'webnode':
        if invoice.due_date <= now.date():
            is_early_payment = True
        refill_tsunami_messaging_bundle(service, is_early_payment)
    share_payment_and_set_stats(invoice, total_months, mean, tx)
    member = service.member
    vendor = service.retailer
    vendor_is_dara = vendor and vendor.app.slug == DARAJA
    if vendor and not vendor_is_dara:
        add_database_to_settings(vendor.database)
        sudo_group = Group.objects.using(vendor.database).get(name=SUDO)
    else:
        vendor = ikwen_service
        sudo_group = Group.objects.using(UMBRELLA).get(name=SUDO)
    add_event(vendor,
              PAYMENT_CONFIRMATION,
              member=member,
              object_id=invoice.id)
    add_event(vendor,
              PAYMENT_CONFIRMATION,
              group_id=sudo_group.id,
              object_id=invoice.id)

    try:
        invoice_pdf_file = generate_pdf_invoice(invoicing_config, invoice)
    except:
        invoice_pdf_file = None

    if member.email:
        activate(member.language)
        invoice_url = ikwen_service.url + reverse('billing:invoice_detail',
                                                  args=(invoice.id, ))
        subject, message, sms_text = get_payment_confirmation_message(
            payment, member)
        html_content = get_mail_content(
            subject,
            message,
            service=vendor,
            template_name='billing/mails/notice.html',
            extra_context={
                'member_name': member.first_name,
                'invoice': invoice,
                'cta': _("View invoice"),
                'invoice_url': invoice_url,
                'early_payment': is_early_payment
            })
        sender = '%s <no-reply@%s>' % (vendor.config.company_name,
                                       ikwen_service.domain)
        msg = XEmailMessage(subject, html_content, sender, [member.email])
        if vendor != ikwen_service and not vendor_is_dara:
            msg.service = vendor
        if invoice_pdf_file:
            msg.attach_file(invoice_pdf_file)
        msg.content_subtype = "html"
        if getattr(settings, 'UNIT_TESTING', False):
            msg.send()
        else:
            Thread(target=lambda m: m.send(), args=(msg, )).start()
    return HttpResponse("Notification received")