def process_invoice_payment(self, invoice):
		try:
			pv = PurchasedVoucher.objects.get(pk=invoice.processorid)
		except PurchasedVoucher.DoesNotExist:
			raise Exception("Could not find voucher order %s" % invoice.processorid)

		if pv.batch:
			raise Exception("This voucher order has already been processed: %s" % invoice.processorid)

		# Set up the batch
		batch = PrepaidBatch(conference=pv.sponsor.conference,
							 regtype=pv.regtype,
							 buyer=pv.user,
							 buyername="{0} {1}".format(pv.user.first_name, pv.user.last_name),
							 sponsor=pv.sponsor)
		batch.save()

		for n in range(0, pv.num):
			v = PrepaidVoucher(conference=pv.sponsor.conference,
							   vouchervalue=base64.b64encode(os.urandom(37)).rstrip('='),
							   batch=batch)
			v.save()

		pv.batch = batch
		pv.save()

		send_simple_mail(pv.sponsor.conference.sponsoraddr,
						 pv.sponsor.conference.sponsoraddr,
						 "Sponsor %s purchased vouchers" % pv.sponsor.name,
						 "The sponsor\n%s\nhas purchased %s vouchers of type \"%s\".\n\n" % (pv.sponsor.name, pv.num, pv.regtype.regtype))
示例#2
0
    def report_unsettled_transactions(self, method, pm):
        # Number of days until we start reporting unsettled transactions

        UNSETTLED_THRESHOLD = 15
        lines = list(
            TransactionStatus.objects.filter(
                settledat__isnull=True,
                authorizedat__lt=datetime.now() -
                timedelta(days=UNSETTLED_THRESHOLD),
                paymentmethod=method).order_by('authorizedat'))
        if len(lines):
            sio = StringIO()
            sio.write(
                "The following payments have been authorized for %s, but not captured for more than %s days.\nThese probably need to be verified manually.\n\n\n"
                % (method.internaldescription, UNSETTLED_THRESHOLD))

            for l in lines:
                sio.write("%s at %s: %s (%s%s)\n" %
                          (l.pspReference, l.authorizedat, l.amount,
                           settings.SITEBASE,
                           reverse('admin:adyen_transactionstatus_change',
                                   args=(l.id, ))))

            send_simple_mail(settings.INVOICE_SENDER_EMAIL,
                             pm.config('notification_receiver'),
                             'Adyen integration unconfirmed notifications',
                             sio.getvalue())
示例#3
0
def process_capture(notification):
    if notification.success:
        # Successful capture, so we just set when the capture happened
        try:
            ts = TransactionStatus.objects.get(
                pspReference=notification.originalReference,
                paymentmethod=notification.rawnotification.paymentmethod)
            ts.capturedat = datetime.now()
            ts.save()
        except TransactionStatus.DoesNotExist:
            # We just ignore captures for non-existant transactions. This
            # seems to happen for example when a transaction is cancelled
            # on a POS terminal.
            pass
    else:
        pm = notification.rawnotification.paymentmethod.get_implementation()

        send_simple_mail(
            settings.INVOICE_SENDER_EMAIL, pm.config('notification_receiver'),
            'Unsuccessful adyen capture received',
            "A credit card capture for %s has failed on %s.\nThe reason given was:\n%s\n\nYou want to investigate this since the payment was probably flagged as completed on authorization!\n"
            % (notification.merchantReference,
               notification.rawnotification.paymentmethod.internaldescription,
               notification.reason))
    # We confirm the notification even if we sent it, since there is not much more we can do
    notification.confirmed = True
    notification.save()
    def send_for_method(self, method):
        pm = method.get_implementation()
        lines = list(
            BraintreeLog.objects.filter(
                error=True, sent=False,
                paymentmethod=method).order_by('timestamp'))

        if len(lines):
            sio = StringIO()
            sio.write(
                "The following error events have been logged by the Braintree integration:\n\n"
            )
            for l in lines:
                sio.write("%s: %20s: %s\n" %
                          (l.timestamp, l.transid, l.message))
                l.sent = True
                l.save()
            sio.write(
                "\n\n\nAll these events have now been tagged as sent, and will no longer be\nprocessed by the system in any way.\n"
            )

            send_simple_mail(settings.INVOICE_SENDER_EMAIL,
                             pm.config('notification_receiver'),
                             'Braintree integration error report',
                             sio.getvalue())
    def report_unfinished_transactions(self, method, pm):
        # Number of days until we start reporting unfinished transactions
        # Note: we only care about transactions that have actually started, where the user
        # got the first step of confirmation. The ones that were never started are uninteresting.
        UNFINISHED_THRESHOLD = 3

        lines = list(
            TrustlyTransaction.objects.filter(
                completedat__isnull=True,
                pendingat__isnull=False,
                pendingat__lt=datetime.now() -
                timedelta(days=UNFINISHED_THRESHOLD),
                paymentmethod=method).order_by('pendingat'))
        if len(lines):
            sio = StringIO()
            sio.write(
                "The following payments have been authorized for %s, but not finished for more than %s days.\nThese probably need to be verified manually.\n\n\n"
                % (method.internaldescription, UNFINISHED_THRESHOLD))

            for l in lines:
                sio.write("%s at %s: %s\n" %
                          (l.orderid, l.pendingat, l.amount))

            send_simple_mail(settings.INVOICE_SENDER_EMAIL,
                             pm.config('notification_receiver'),
                             'Trustly integration unconfirmed notifications',
                             sio.getvalue())
示例#6
0
	def _email_something(self, template_name, mail_subject, pdfname=None, pdfcontents=None, bcc=False):
		# Send off the receipt/invoice by email if possible
		if not self.invoice.recipient_email:
			return

		# Build a text email, and attach the PDF if there is one
		if self.invoice.recipient_user:
			# General URL that shows a normal invoice
			invoiceurl = '%s/invoices/%s/' % (settings.SITEBASE_SSL, self.invoice.pk)
		elif self.invoice.recipient_secret:
			# No user, but a secret, so generate a URL that can be used without
			# being logged in.
			invoiceurl = '%s/invoices/%s/%s/' % (settings.SITEBASE_SSL, self.invoice.pk, self.invoice.recipient_secret)
		else:
			invoiceurl = None

		txt = get_template('invoices/mail/%s' % template_name).render(Context({
				'invoice': self.invoice,
				'invoiceurl': invoiceurl,
				'currency_abbrev': settings.CURRENCY_ABBREV,
				'currency_symbol': settings.CURRENCY_SYMBOL,
				}))

		pdfdata = []
		if pdfname:
			pdfdata = [(pdfname, 'application/pdf',	base64.b64decode(pdfcontents)), ]

		# Queue up in the database for email sending soon
		send_simple_mail(settings.INVOICE_SENDER_EMAIL,
						 self.invoice.recipient_email,
						 mail_subject,
						 txt,
						 pdfdata,
						 bcc=bcc and settings.INVOICE_SENDER_EMAIL or None,
						 )
示例#7
0
def send_conference_sponsor_notification(conference, subject, message):
    if conference.sponsoraddr:
        send_simple_mail(conference.sponsoraddr,
                         conference.sponsoraddr,
                         subject,
                         message,
                         sendername=conference.conferencename)
    def handle(self, *args, **options):
        for method in InvoicePaymentMethod.objects.filter(
                active=True,
                classname='postgresqleu.util.payment.paypal.Paypal'):
            pm = method.get_implementation()

            api = PaypalAPI(pm)

            # We only ever care about the primary currency
            paypal_balance = api.get_primary_balance()

            accounting_balance = get_latest_account_balance(
                pm.config('accounting_income'))

            if accounting_balance != paypal_balance:
                send_simple_mail(
                    settings.INVOICE_SENDER_EMAIL,
                    pm.config('report_receiver'), 'Paypal balance mismatch!',
                    """Paypal balance ({0}) does not match the accounting system ({1}) for payment method {2}!

    This could be because some entry has been missed in the accouting
    (automatic or manual), or because of an ongoing booking of something
    that the system doesn't know about.

    Better go check manually!
    """.format(paypal_balance, accounting_balance, method.internaldescription))
示例#9
0
    def process_completed_payment(self, trans):
        manager = InvoiceManager()
        invoice = self.get_invoice_for_transaction(trans)

        def invoice_logger(msg):
            raise TrustlyException("Trustly invoice processing failed: {0}".format(msg))

        method = trans.paymentmethod
        pm = method.get_implementation()

        manager.process_incoming_payment_for_invoice(invoice,
                                                     trans.amount,
                                                     'Trustly id {0}'.format(trans.id),
                                                     0,  # XXX: we pay zero now, but should perhaps support fees?
                                                     pm.config('accounting_income'),
                                                     pm.config('accounting_fee'),
                                                     [],
                                                     invoice_logger,
                                                     method)

        TrustlyLog(message="Completed payment for Trustly id {0} (order {1}), {2}{3}, invoice {4}".format(trans.id, trans.orderid, settings.CURRENCY_ABBREV, trans.amount, invoice.id), paymentmethod=method).save()

        send_simple_mail(settings.INVOICE_SENDER_EMAIL,
                         pm.config('notification_receiver'),
                         "Trustly payment completed",
                         "A Trustly payment for {0} of {1}{2} for invoice {3} was completed on the Trustly platform.\n\nInvoice: {4}\nRecipient name: {5}\nRecipient email: {6}\n".format(
                             method.internaldescription,
                             settings.CURRENCY_ABBREV,
                             trans.amount,
                             invoice.id,
                             invoice.title,
                             invoice.recipient_name,
                             invoice.recipient_email),
                         )
示例#10
0
    def postpone_invoice_autocancel(self, invoice, mintime, reason, silent=False):
        # Extend an invoice to be valid at least mintime into the future. Unless
        # silent is set, a notification will be sent to the invoice address if
        # this happens. No notification is sent to the end user.
        if invoice.paidat:
            # Already paid. Could happen if payment notification is delivered concurrently,
            # so just ignore it.
            return False
        if not invoice.canceltime:
            return False
        if invoice.canceltime > timezone.now() + mintime:
            return False

        # Else we need to extend it, so do it
        oldtime = invoice.canceltime
        invoice.canceltime = timezone.now() + mintime
        invoice.save()

        InvoiceHistory(invoice=invoice, txt='Extended until {0}: {1}'.format(invoice.canceltime, reason)).save()

        if not silent:
            send_simple_mail(settings.INVOICE_SENDER_EMAIL,
                             settings.INVOICE_NOTIFICATION_RECEIVER,
                             "Invoice {0} automatically extended".format(invoice.id),
                             """The invoice with id {0} has had it's automatic cancel time extended
from {1} to {2}.

The reason for this was:
{3}

The invoice remains active regardless of the original cancel time, and will
keep getting extended until the process is manually stopped. A new notification
will be sent after each extension.
""".format(invoice.id, oldtime, invoice.canceltime, reason))
示例#11
0
def automatch_bank_transaction_rule(trans, matcher):
    # We only do exact matching, fuzzyness is handled elsewhere
    if trans.amount == matcher.amount and re.match(matcher.pattern, trans.transtext, re.I):
        # Flag the journal entry as closed since this transaction now arrived
        if matcher.journalentry.closed:
            send_simple_mail(settings.INVOICE_SENDER_EMAIL,
                             settings.INVOICE_NOTIFICATION_RECEIVER,
                             "Bank payment pattern match for closed entry received",
                             "A bank tranksaction of {0}{1} with text\n{2}\nmatched journal entry {3}, but this entry was already closed!\n\nNeeds manual examination!".format(
                                 trans.amount,
                                 settings.CURRENCY_ABBREV,
                                 trans.transtext,
                                 matcher.journalentry,
                             ))

            InvoiceLog(message="Bank transaction of {0}{1} with text {2} matched journal entry {3}, but this entry was already closed!".format(
                trans.amount,
                settings.CURRENCY_ABBREV,
                trans.transtext,
                matcher.journalentry,
            )).save()
        else:
            matcher.journalentry.closed = True
            matcher.journalentry.save()

            InvoiceLog(message="Matched bank transaction of {0}{1} with text {2} to journal entry {3}.".format(
                trans.amount,
                settings.CURRENCY_ABBREV,
                trans.transtext,
                matcher.journalentry,
            )).save()

        return True
示例#12
0
 def send_notification_email(self, subject, contents):
     send_simple_mail(
         settings.SCHEDULED_JOBS_EMAIL_SENDER,
         settings.SCHEDULED_JOBS_EMAIL,
         subject,
         contents,
     )
示例#13
0
def admin_email(request):
	if request.method == 'POST':
		form = EmailSendForm(data=request.POST)
		if form.is_valid():
			# Ok, actually send the email. This is the scary part!
			ids = form.data['ids'].split(',')
			members = Member.objects.filter(pk__in=ids)
			emails = [r.user.email for r in members]
			for e in emails:
				send_simple_mail(form.data['sender'], e, form.data['subject'], form.data['text'])

			messages.info(request, 'Sent email to %s recipients' % len(emails))
			return HttpResponseRedirect('/admin/membership/member/?' + form.data['returnurl'])
		else:
			ids = form.data['ids'].split(',')
	else:
		ids = request.GET['ids']
		form = EmailSendForm(initial={'ids': ids, 'returnurl': request.GET['orig']})
		ids = ids.split(',')

	recipients = [m.user.email for m in Member.objects.filter(pk__in=ids)]
	return render_to_response('membership/admin_email.html', {
		'form': form,
		'recipientlist': ', '.join(recipients),
		})
    def handle(self, *args, **options):
        accounts = []
        for pm in InvoicePaymentMethod.objects.filter(
                active=True,
                config__has_key='file_upload_interval',
                config__file_upload_interval__gt=0):
            if not BankFileUpload.objects.filter(
                    method=pm,
                    created__gt=timezone.now() - timedelta(
                        days=pm.config['file_upload_interval'])).exists():
                accounts.append(pm.internaldescription)

        if accounts:
            send_simple_mail(
                settings.INVOICE_SENDER_EMAIL,
                settings.INVOICE_NOTIFICATION_RECEIVER,
                "Bank accounts are missing uploaded files",
                """The following bank accounts have not received an uploaded file within the
configured interval:

* {0}

Uploading can be done at {1}/admin/invoices/bankfiles/
""".format(
                    "\n* ".join(accounts),
                    settings.SITEBASE,
                ))
示例#15
0
    def log_and_email(self, message, paymentmethod):
        TrustlyLog(message=message, error=True, paymentmethod=paymentmethod).save()

        send_simple_mail(settings.INVOICE_SENDER_EMAIL,
                         self.pm.config('notification_receiver'),
                         "Trustly payment error",
                         "A trustly payment for {0} failed with the error:\n\n{1}".format(paymentmethod.internaldescription, message),
                         )
示例#16
0
def send_conference_notification(conference, subject, message):
    if conference.notifyaddr:
        send_simple_mail(conference.notifyaddr,
                         conference.notifyaddr,
                         subject,
                         message,
                         sendername=conference.conferencename)
    send_org_notification(conference, message)
示例#17
0
def sendmail(request):
    authenticate_backend_group(request, 'Membership administrators')

    if request.method == 'POST':
        idlist = list(map(int, request.POST['idlist'].split(',')))
    else:
        idlist = list(map(int, request.GET['idlist'].split(',')))

    cfg = get_config()

    recipients = Member.objects.filter(pk__in=idlist)

    initial = {
        '_from':
        '{0} <{1}>'.format(settings.ORG_NAME, cfg.sender_email),
        'recipients':
        escape(", ".join([
            '{0} <{1}>'.format(x.fullname, x.user.email) for x in recipients
        ])),
        'idlist':
        ",".join(map(str, idlist)),
    }

    if request.method == 'POST':
        p = request.POST.copy()
        p['recipients'] = initial['recipients']
        form = BackendMemberSendEmailForm(data=p, initial=initial)
        if form.is_valid():
            with transaction.atomic():
                for r in recipients:
                    msgtxt = "{0}\n\n-- \nThis message was sent to members of {1}\n".format(
                        form.cleaned_data['message'], settings.ORG_NAME)
                    send_simple_mail(
                        cfg.sender_email,
                        r.user.email,
                        form.cleaned_data['subject'],
                        msgtxt,
                        sendername=settings.ORG_NAME,
                        receivername=r.fullname,
                    )
                messages.info(request,
                              "Email sent to %s attendees" % len(recipients))

            return HttpResponseRedirect("../")
    else:
        form = BackendMemberSendEmailForm(initial=initial)

    return render(
        request, 'confreg/admin_backend_form.html', {
            'basetemplate': 'adm/admin_base.html',
            'form': form,
            'what': 'new email',
            'savebutton': 'Send email',
            'cancelurl': '../',
            'breadcrumbs': [
                ('../', 'Members'),
            ],
        })
def report_unconfirmed_notifications():
	lines = list(Notification.objects.filter(confirmed=False, receivedat__lt=datetime.now()-timedelta(days=1)).order_by('eventDate'))
	if len(lines):
		sio = StringIO()
		sio.write("The following notifications have not been confirmed in the Adyen integration.\nThese need to be manually processed and then flagged as confirmed!\n\nThis list only contains unconfirmed events older than 24 hours.\n\n\n")
		for l in lines:
			sio.write("%s: %s (%s%s)\n" % (l.eventDate, l.eventCode, settings.SITEBASE_SSL, urlresolvers.reverse('admin:adyen_notification_change', args=(l.id,))))

		send_simple_mail(settings.INVOICE_SENDER_EMAIL,
						 settings.ADYEN_NOTIFICATION_RECEIVER,
						 'Adyen integration unconfirmed notifications',
						 sio.getvalue())
    def report_unconfirmed_notifications(self, method, pm):
        lines = list(Notification.objects.filter(confirmed=False, receivedat__lt=timezone.now() - timedelta(days=1), rawnotification__paymentmethod=method).order_by('eventDate'))
        if len(lines):
            sio = StringIO()
            sio.write("The following notifications have not been confirmed in the Adyen integration for %s.\nThese need to be manually processed and then flagged as confirmed!\n\nThis list only contains unconfirmed events older than 24 hours.\n\n\n" % method.internaldescription)
            for l in lines:
                sio.write("%s: %s (%s%s)\n" % (l.eventDate, l.eventCode, settings.SITEBASE, reverse('admin:adyen_notification_change', args=(l.id,))))

            send_simple_mail(settings.INVOICE_SENDER_EMAIL,
                             pm.config('notification_receiver'),
                             'Adyen integration unconfirmed notifications',
                             sio.getvalue())
示例#20
0
    def check_refunds(self, method, pm):
        for r in StripeRefund.objects.filter(
                paymentmethod=method,
                completedat__isnull=True,
                invoicerefundid__issued__lt=timezone.now() -
                timedelta(hours=6)):

            send_simple_mail(
                settings.INVOICE_SENDER_EMAIL,
                pm.config('notification_receiver'), 'Stripe stalled refund!',
                """Stripe refund {0} for {1} has been stalled for more than 6 hours!

This is probably not normal and should be checked!
""".format(r.id, method.internaldescription))
示例#21
0
    def handle(self, *args, **options):
        refunds = InvoiceRefund.objects.filter(issued__isnull=True)
        for r in refunds:
            manager = InvoiceManager()

            # One transaction for each object, and make sure it's properly
            # locked by using select for update, in case we get a notification
            # delivered while we are still processing.
            with transaction.atomic():
                rr = InvoiceRefund.objects.select_for_update().filter(
                    pk=r.pk)[0]
                if not rr.invoice.can_autorefund:
                    # How did we end up in the queue?!
                    raise CommandError(
                        "Invoice {0} listed for refund, but provider is not capable of refunds!"
                        .format(r.invoice.id))

                # Calling autorefund will update the InvoiceRefund object
                # after calling the APIs, so nothing more to do here.

                if manager.autorefund_invoice(rr):
                    self.stdout.write(
                        "Issued API refund of invoice {0}.".format(
                            rr.invoice.pk))
                else:
                    self.stdout.write(
                        "Failed to issue API refund for invoice {0}, will keep trying."
                        .format(rr.invoice.pk))

        # Send alerts for any refunds that have been issued but that have not completed within
        # 3 days (completely arbitrary, but normally it happens within seconds/minutes/hours).
        stalledrefunds = InvoiceRefund.objects.filter(
            issued__isnull=False,
            completed__isnull=True,
            issued__lt=datetime.now() - timedelta(days=3))
        if stalledrefunds:
            send_simple_mail(
                settings.INVOICE_SENDER_EMAIL,
                settings.INVOICE_NOTIFICATION_RECEIVER,
                "Stalled invoice refunds",
                """One or more invoice refunds appear to be stalled.
These refunds have been issued to the provider, but no confirmation has
shown up. This requires manual investigation.

The following invoices have stalled refunds:

{0}

Better go check!
""".format("\n".join([r.invoice.invoicestr for r in stalledrefunds])))
示例#22
0
    def handle(self, *args, **options):
        # Expiry of additional options is based on when the registration was last modified, and not
        # the actual additional option. But the 99.9% case is people who are not touching their
        # registration at all, so this is not a problem. If they go in and change their address,
        # they get an additional grace period only.
        #
        # We also don't expire any registrations that have an invoice or a bulk registration. Those
        # are handled by the invoice cancellation.
        #
        # Pending additional options make no difference here because they are not actually added
        # to the registration, and the pending order itself will be canceled along with the invoice.
        #
        # And no - the django ORM does not like to do date math in the WHERE clause, AFAICT, when the
        # values come from different tables.
        regs = ConferenceRegistration.objects.filter(
            payconfirmedat__isnull=True,
            invoice__isnull=True,
            bulkpayment__isnull=True,
            additionaloptions__invoice_autocancel_hours__isnull=False,
        ).extra(where=[
            "lastmodified < CURRENT_TIMESTAMP - confreg_conferenceadditionaloption.invoice_autocancel_hours * '1 hour'::interval",
        ])

        expired = defaultdict(list)
        num = 0
        for r in regs:
            expired[r.conference].extend([(r.firstname + ' ' + r.lastname, x)
                                          for x in expire_additional_options(r)
                                          ])
            num += len(expired[r.conference])

        if num:
            for conference, expired in list(expired.items()):
                s = StringIO()
                s.write(
                    """The following additional options have been removed from pending
registrations (without invoice or bulk payment) based on the invoice
autocancel hours, to make room for other attendees:

""")
                for name, option in expired:
                    s.write("{0:<40}{1}\n".format(name, option))
                s.write("\n\n")
                send_simple_mail(
                    conference.notifyaddr,
                    conference.notifyaddr,
                    'Additional options removed from pending registrations',
                    s.getvalue(),
                    sendername=conference.conferencename)
    def fetch_one_statement(self, method):
        pm = method.get_implementation()
        if not pm.config('send_statements'):
            return

        # We fetch for the *previous* month. Take todays day, truncate it to the month,
        # subtract one day to get the last day of the previous month, and then truncate
        # again to the first of that month.
        d = (datetime.date.today().replace(day=1) -
             datetime.timedelta(days=1)).replace(day=1)

        if TransferwiseMonthlyStatement.objects.filter(paymentmethod=method,
                                                       month=d).exists():
            return

        # Else we don't have it, so download it
        api = pm.get_api()
        r = api.get_binary(
            'borderless-accounts/{0}/statement.pdf'.format(
                api.get_account()['id']), {
                    'currency':
                    settings.CURRENCY_ABBREV,
                    'intervalStart':
                    api.format_date(d),
                    'intervalEnd':
                    api.format_date(datetime.date.today().replace(day=1)),
                })
        statement = TransferwiseMonthlyStatement(
            paymentmethod=method,
            month=d,
            contents=r.read(),
        )
        statement.save()

        send_simple_mail(
            settings.INVOICE_SENDER_EMAIL,
            pm.config('notification_receiver'),
            'TransferWise monthly statement for {}'.format(
                statement.month.strftime("%B %Y")),
            "The TransferWise monthly statement for {0} for the month of {1} is attached."
            .format(
                method.internaldescription,
                statement.month.strftime("%B %Y"),
            ),
            attachments=[
                ('TransferWise_{}.pdf'.format(
                    statement.month.strftime("%b_%Y")), 'application/pdf',
                 statement.contents),
            ])
def report_loglines():
	lines = list(AdyenLog.objects.filter(error=True,sent=False).order_by('timestamp'))
	if len(lines):
		sio = StringIO()
		sio.write("The following error events have been logged by the Adyen integration:\n\n")
		for l in lines:
			sio.write("%s: %20s: %s\n" % (l.timestamp, l.pspReference, l.message))
			l.sent = True
			l.save()
		sio.write("\n\n\nAll these events have now been tagged as sent, and will no longer be\nprocessed by the system in any way.\n")

		send_simple_mail(settings.INVOICE_SENDER_EMAIL,
						 settings.ADYEN_NOTIFICATION_RECEIVER,
						 'Adyen integration error report',
						 sio.getvalue())
    def report_loglines(self, method, pm):
        lines = list(AdyenLog.objects.filter(error=True, sent=False, paymentmethod=method).order_by('timestamp'))
        if len(lines):
            sio = StringIO()
            sio.write("The following error events have been logged by the Adyen integration for %s:\n\n" % method.internaldescription)
            for l in lines:
                sio.write("%s: %20s: %s\n" % (l.timestamp, l.pspReference, l.message))
                l.sent = True
                l.save()
            sio.write("\n\n\nAll these events have now been tagged as sent, and will no longer be\nprocessed by the system in any way.\n")

            send_simple_mail(settings.INVOICE_SENDER_EMAIL,
                             pm.config('notification_receiver'),
                             'Adyen integration error report',
                             sio.getvalue())
示例#26
0
    def handle(self, *args, **options):
        # Any entries that actually have an invoice will be canceled by the invoice
        # system, as the expiry time of the invoice is set synchronized. In this
        # run, we only care about offers that have not been picked up at all.
        wlentries = RegistrationWaitlistEntry.objects.filter(
            registration__payconfirmedat__isnull=True,
            registration__invoice__isnull=True,
            offerexpires__lt=datetime.now())

        for w in wlentries:
            reg = w.registration

            # Create a history entry so we know exactly when it happened
            RegistrationWaitlistHistory(waitlist=w,
                                        text="Offer expired at {0}".format(
                                            w.offerexpires)).save()

            # Notify conference organizers
            send_simple_mail(
                reg.conference.notifyaddr,
                reg.conference.notifyaddr,
                'Waitlist expired',
                'User {0} {1} <{2}> did not complete the registration before the waitlist offer expired.'
                .format(reg.firstname, reg.lastname, reg.email),
                sendername=reg.conference.conferencename)

            # Also send an email to the user
            send_conference_mail(
                reg.conference,
                reg.email,
                'Your waitlist offer has expired',
                'confreg/mail/waitlist_expired.txt',
                {
                    'conference': reg.conference,
                    'reg': reg,
                    'offerexpires': w.offerexpires,
                },
                receivername=reg.fullname,
            )

            # Now actually expire the offer
            w.offeredon = None
            w.offerexpires = None
            # Move the user to the back of the waitlist (we have a history entry for the
            # initial registration date, so it's still around)
            w.enteredon = datetime.now()

            w.save()
def report_unsettled_transactions():
	# Number of days until we start reporting unsettled transactions

	UNSETTLED_THRESHOLD=15
	lines = list(TransactionStatus.objects.filter(settledat__isnull=True, authorizedat__lt=datetime.now()-timedelta(days=UNSETTLED_THRESHOLD)).order_by('authorizedat'))
	if len(lines):
		sio = StringIO()
		sio.write("The following payments have been authorized, but not captured for more than %s days.\nThese probably need to be verified manually.\n\n\n" % UNSETTLED_THRESHOLD)

		for l in lines:
			sio.write("%s at %s: %s (%s%s)\n" % (l.pspReference, l.authorizedat, l.amount, settings.SITEBASE_SSL, urlresolvers.reverse('admin:adyen_transactionstatus_change', args=(l.id,))))

		send_simple_mail(settings.INVOICE_SENDER_EMAIL,
						 settings.ADYEN_NOTIFICATION_RECEIVER,
						 'Adyen integration unconfirmed notifications',
						 sio.getvalue())
def run():
	# Expire members (and let them know it happened)
	expired = Member.objects.filter(paiduntil__lt=datetime.now())
	for m in expired:
		MemberLog(member=m, timestamp=datetime.now(), message='Membership expired').save()
		# Generate an email to the user
		txt = get_template('membership/mail/expired.txt').render(Context({
					'member': m,
					}))
		send_simple_mail(settings.MEMBERSHIP_SENDER_EMAIL,
						 m.user.email,
						 "Your PostgreSQL Europe membership has expired",
						 txt)
		print "Expired member %s (paid until %s)" % (m, m.paiduntil)
		# An expired member has no membersince and no paiduntil.
		m.membersince = None
		m.paiduntil = None
		m.save()




	# Send warnings to members about to expire. We explicitly avoid sending
	# a warning in the last 24 hours before expire, so we don't end up sending
	# both a warning and an expiry within minutes in case the cronjob runs on
	# slightly different times.
	
	warning = Member.objects.filter(
		Q(paiduntil__gt=datetime.now() - timedelta(days=1)) &
		Q(paiduntil__lt=datetime.now() + timedelta(days=30)) &
		(
			Q(expiry_warning_sent__lt=datetime.now() - timedelta(days=10)) |
			Q(expiry_warning_sent__isnull=True)
			))
	for m in warning:
		MemberLog(member=m, timestamp=datetime.now(), message='Membership expiry warning sent to %s' % m.user.email).save()
		# Generate an email to the user
		txt = get_template('membership/mail/warning.txt').render(Context({
					'member': m,
					}))
		send_simple_mail(settings.MEMBERSHIP_SENDER_EMAIL,
						 m.user.email,
						 "Your PostgreSQL Europe membership will expire soon",
						 txt)
		print "Sent warning to member %s (paid until %s, last warned %s)" % (m, m.paiduntil, m.expiry_warning_sent)
		m.expiry_warning_sent = datetime.now()
		m.save()
示例#29
0
    def process_invoice_payment(self, invoice):
        try:
            pv = PurchasedVoucher.objects.get(pk=invoice.processorid)
        except PurchasedVoucher.DoesNotExist:
            raise Exception("Could not find voucher order %s" % invoice.processorid)

        if pv.batch:
            raise Exception("This voucher order has already been processed: %s" % invoice.processorid)

        # Set up the batch
        batch = PrepaidBatch(conference=pv.conference,
                             regtype=pv.regtype,
                             buyer=pv.user,
                             buyername="{0} {1}".format(pv.user.first_name, pv.user.last_name),
                             sponsor=pv.sponsor)
        batch.save()

        for n in range(0, pv.num):
            v = PrepaidVoucher(conference=pv.conference,
                               vouchervalue=base64.b64encode(os.urandom(37)).rstrip(b'=').decode('utf8'),
                               batch=batch)
            v.save()

        pv.batch = batch
        pv.save()

        if pv.sponsor:
            send_simple_mail(pv.conference.sponsoraddr,
                             pv.conference.sponsoraddr,
                             "Sponsor %s purchased vouchers" % pv.sponsor.name,
                             "The sponsor\n%s\nhas purchased %s vouchers of type \"%s\".\n\n" % (pv.sponsor.name, pv.num, pv.regtype.regtype),
                             sendername=pv.sponsor.conference.conferencename)
        else:
            # For non-sponsors, there is no dashboard available, so we send the actual vouchers in an
            # email directly.
            send_conference_mail(pv.conference,
                                 pv.batch.buyer.email,
                                 "Entry vouchers to {}".format(pv.conference.conferencename),
                                 'confreg/mail/prepaid_vouchers.txt',
                                 {
                                     'batch': batch,
                                     'vouchers': batch.prepaidvoucher_set.all(),
                                     'conference': pv.conference,
                                 },
                                 sender=pv.conference.contactaddr,
            )
示例#30
0
def process_stripe_checkout(co):
    if co.completedat:
        # Already completed, so don't do anything with it
        return

    with transaction.atomic():
        method = co.paymentmethod
        pm = method.get_implementation()
        api = StripeApi(pm)

        # Update the status from the API
        if api.update_checkout_status(co):
            # Went from unpaid to paid, so Do The Magic (TM)
            manager = InvoiceManager()
            invoice = Invoice.objects.get(pk=co.invoiceid)

            def invoice_logger(msg):
                raise StripeException(
                    "Stripe invoice processing failed: {0}".format(msg))

            manager.process_incoming_payment_for_invoice(
                invoice, co.amount, 'Stripe checkout id {0}'.format(co.id),
                co.fee, pm.config('accounting_income'),
                pm.config('accounting_fee'), [], invoice_logger, method)

            StripeLog(
                message=
                "Completed payment for Stripe id {0} ({1}{2}, invoice {3})".
                format(co.id, settings.CURRENCY_ABBREV, co.amount, invoice.id),
                paymentmethod=method).save()

            send_simple_mail(
                settings.INVOICE_SENDER_EMAIL,
                pm.config('notification_receiver'), "Stripe payment completed",
                "A Stripe payment for {0} of {1}{2} for invoice {3} was completed.\n\nInvoice: {4}\nRecipient name: {5}\nRecipient email: {6}\n"
                .format(
                    method.internaldescription,
                    settings.CURRENCY_ABBREV,
                    co.amount,
                    invoice.id,
                    invoice.title,
                    invoice.recipient_name,
                    invoice.recipient_email,
                ))
示例#31
0
    def report_for_method(self, method):
        pm = method.get_implementation()

        entries = ErrorLog.objects.filter(sent=False,
                                          paymentmethod=method).order_by('id')
        if len(entries):
            msg = """
Events reported by the paypal integration for {0}:

{1}
""".format(
                method.internaldescription,
                "\n".join([
                    "{0}: {1}".format(e.timestamp, e.message) for e in entries
                ]),
            )

            send_simple_mail(settings.INVOICE_SENDER_EMAIL,
                             pm.config('report_receiver'),
                             'Paypal Integration Report', msg)
            entries.update(sent=True)

        entries = TransactionInfo.objects.filter(
            matched=False).order_by('timestamp')
        if len(entries):
            msg = """
The following payments have been received but not matched to anything in
the system for {0}:

{1}

These will keep being reported until there is a match found or they are
manually dealt with in the admin interface!
""".format(
                method.internaldescription, "\n".join([
                    "{0}: {1} ({2}) sent {3} with text '{4}'".format(
                        e.timestamp, e.sender, e.sendername, e.amount,
                        e.transtext) for e in entries
                ]))

            send_simple_mail(settings.INVOICE_SENDER_EMAIL,
                             pm.config('report_receiver'),
                             'Paypal Unmatched Transactions', msg)
    def handle(self, *args, **options):
        for conference in Conference.objects.filter(active=True):
            # One transaction for each open conference that has registration
            # open. If registration isn't open then there is nowhere to
            # register, so don't even try.
            with transaction.atomic():
                whatstr = StringIO()

                if conference.registrationtype_set.filter(specialtype__in=('spk', 'spkr')).exists():
                    self.remind_pending_speakers(whatstr, conference)
                    self.remind_unregistered_speakers(whatstr, conference)

                self.remind_pending_registrations(whatstr, conference)
                self.remind_pending_multiregs(whatstr, conference)

                # Do we need to send a central mail?
                if whatstr.tell():
                    # More than one character, so we have done something. Send
                    # a report to the conference organizers about it.
                    send_simple_mail(conference.notifyaddr,
                                     conference.notifyaddr,
                                     "Reminders sent",
                                     whatstr.getvalue(),
                                     sendername=conference.conferencename,
                                     receivername=conference.conferencename,
                                     )

        for conference in Conference.objects.filter(callforpapersopen=True):
            # One transaction for each conference with call for papers open, to send reminders
            # for things related to the cfp.
            with transaction.atomic():
                whatstr = StringIO()
                self.remind_empty_submissions(whatstr, conference)
                self.remind_empty_speakers(whatstr, conference)

                if whatstr.tell():
                    send_simple_mail(conference.notifyaddr,
                                     conference.notifyaddr,
                                     "CfP reminders sent",
                                     whatstr.getvalue(),
                                     sendername=conference.conferencename,
                                     receivername=conference.conferencename,
                                 )
示例#33
0
    def verify_balance(self, method, pm):
        # Verify the balance against Stripe
        api = StripeApi(pm)
        stripe_balance = api.get_balance()
        accounting_balance = get_latest_account_balance(
            pm.config('accounting_income'))

        if accounting_balance != stripe_balance:
            send_simple_mail(
                settings.INVOICE_SENDER_EMAIL,
                pm.config('notification_receiver'), 'Stripe balance mismatch!',
                """Stripe balance ({0}) for {1} does not match the accounting system ({2})!

This could be because some entry has been missed in the accounting
(automatic or manual), or because of an ongoing booking of something
that the system doesn't know about.

Better go check manually!
""".format(stripe_balance, method.internaldescription, accounting_balance))
def set_timezone_name(apps, schema_editor):
    # Do our best to figure out the timezone names, and send a report to the admin.
    status = io.StringIO()
    status.write(
        "A field for conference timezone has been added, and the following\n")
    status.write(
        "mappings have been done. Make sure you review these and ADJUST this\n"
    )
    status.write(
        "for all conferences before applying the updates that enable full\n")
    status.write("timezone support later!\n\n")

    success = 0
    failed = 0

    Conf = apps.get_model('confreg', 'conference')
    for c in Conf.objects.all().order_by('series', '-startdate'):
        (tzname, matchwhat) = get_matching_timezone(c.location)
        if tzname:
            success += 1
            c.tzname = tzname
            c.save()
            status.write("{} - matched {} to timezone {} using {}\n".format(
                c.conferencename, c.location, c.tzname, matchwhat))
        else:
            failed += 1
            c.tzname = settings.TIME_ZONE
            c.save()
            status.write(
                "{} - found no match for {}, set timezone to {}\n".format(
                    c.conferencename, c.location, c.tzname))

    if success > 0 or failed > 0:
        status.write(
            "\n\n{} successful mappings and {} failed.\n\nMake sure these are verified before proceeding with migrations!\n"
            .format(success, failed))
        send_simple_mail(
            settings.DEFAULT_EMAIL,
            settings.DEFAULT_EMAIL,
            "Timezone field added to conferences",
            status.getvalue(),
        )
示例#35
0
    def handle(self, *args, **options):
        trans = PendingBankTransaction.objects.filter(
            created__lte=datetime.now() - timedelta(hours=72))
        if trans:
            send_simple_mail(
                settings.INVOICE_SENDER_EMAIL,
                settings.INVOICE_NOTIFICATION_RECEIVER,
                "Pending bank transactions present",
                """There are currently {0} bank transactions that have been pending for more than 24 hours.
This means transactions that were not matched to any invoice or
any expected payout, and thus needs to be processed manually.

{1}

Processing happens at {2}/admin/invoices/banktransactions/
""".format(
                    len(trans), "\n".join([
                        "{0:10f}   {1}".format(t.amount, t.transtext)
                        for t in trans
                    ]), settings.SITEBASE))
	def process_invoice_payment(self, invoice):
		try:
			sponsor = Sponsor.objects.get(pk=invoice.processorid)
		except Sponsor.DoesNotExist:
			raise Exception("Could not find conference sponsor %s" % invoice.processorid)

		if sponsor.confirmed:
			# This sponsorship was already confirmed. Typical case for this is the contract
			# was signed manually, and then the invoice was generated. In this case, we just
			# don't care, so we return without updating the date of the confirmation.
			return

		confirm_sponsor(sponsor, "Invoice payment")

		conference = sponsor.conference

		send_simple_mail(conference.sponsoraddr,
						 conference.sponsoraddr,
						 "Confirmed sponsor: %s" % sponsor.name,
						 "The sponsor\n%s\nhas completed payment of the sponsorship invoice,\nand is now activated.\nBenefits are not claimed yet." % sponsor.name)
示例#37
0
def send_conference_mail(conference,
                         receiver,
                         subject,
                         templatename,
                         templateattr={},
                         attachments=None,
                         bcc=None,
                         receivername=None,
                         sender=None,
                         sendername=None):
    if not ((conference and conference.jinjaenabled and conference.jinjadir) or
            os.path.exists(os.path.join(JINJA_TEMPLATE_ROOT, templatename))):
        raise Exception("Mail template not found")

    send_simple_mail(
        sender or conference.contactaddr, receiver,
        "[{0}] {1}".format(conference.conferencename, subject),
        render_jinja_conference_template(conference, templatename,
                                         templateattr), attachments, bcc,
        sendername or conference.conferencename, receivername)
def confirm_sponsor(sponsor, who):
	# Confirm a sponsor, including sending the confirmation email.
	# This will save the specified sponsor model as well, but the function
	# expects to be wrapped in external transaction handler.
	sponsor.confirmed = True
	sponsor.confirmedat = datetime.now()
	sponsor.confirmedby = who
	sponsor.save()

	msgtxt = get_template('confsponsor/mail/sponsor_confirmed.txt').render(Context({
		'sponsor': sponsor,
		'conference': sponsor.conference,
		'SITEBASE': settings.SITEBASE_SSL,
	}))
	for manager in sponsor.managers.all():
		send_simple_mail(sponsor.conference.sponsoraddr,
						 manager.email,
						 u"[{0}] Sponsorship confirmed".format(sponsor.conference),
						 msgtxt,
						 sendername=sponsor.conference.conferencename,
						 receivername=u'{0} {1}'.format(manager.first_name, manager.last_name))
    def report_unconfirmed_notifications(self, method, pm):
        lines = list(
            TrustlyNotification.objects.filter(
                confirmed=False,
                receivedat__lt=datetime.now() - timedelta(days=1),
                rawnotification__paymentmethod=method).order_by('receivedat'))
        if len(lines):
            sio = StringIO()
            sio.write(
                "The following notifications have not been confirmed in the Trustly integration for {0}.\nThese need to be manually processed and then flagged as confirmed!\n\nThis list only contains unconfirmed events older than 24 hours.\n\n\n"
                .format(method.internaldescription))
            for l in lines:
                sio.write("%s: %s\n" % (
                    l.receivedat,
                    l.method,
                ))

            send_simple_mail(settings.INVOICE_SENDER_EMAIL,
                             pm.config('notification_receiver'),
                             'Trustly integration unconfirmed notifications',
                             sio.getvalue())
示例#40
0
def process_one_notification(notification):
	# Now do notification specific processing
	if not notification.live:
		# This one is in the test system only! So we just send
		# an email, because we're lazy
		send_simple_mail(settings.INVOICE_SENDER_EMAIL,
						 settings.ADYEN_NOTIFICATION_RECEIVER,
						 'Received adyen notification type %s from the test system!' % notification.eventCode,
						 "An Adyen notification with live set to false has been received.\nYou probably want to check that out manually - it's in the database, but has received no further processing.\n",
			AdyenLog(pspReference=notification.pspReference, message='Received notification of type %s from the test system!' % notification.eventCode, error=True).save()
		)
		notification.confirmed = True
		notification.save()
	elif notification.eventCode == 'AUTHORISATION':
		process_authorization(notification)
	elif notification.eventCode == 'REPORT_AVAILABLE':
		process_new_report(notification)
	elif notification.eventCode == 'CAPTURE':
		process_capture(notification)
	elif notification.eventCode == 'REFUND':
		process_refund(notification)
	elif notification.eventCode in ('UNSPECIFIED', ):
		# Any events that we just ignore still need to be flagged as
		# confirmed
		notification.confirmed = True
		notification.save()
		AdyenLog(pspReference=notification.pspReference, message='Received notification of type %s, ignored' % notification.eventCode).save()
	else:
		# Received an event that needs manual processing because we
		# don't know what to do with it. To make sure we can react
		# quickly to this, generate an immediate email for this.
		send_simple_mail(settings.INVOICE_SENDER_EMAIL,
						 settings.ADYEN_NOTIFICATION_RECEIVER,
						 'Received unknown Adyen notification of type %s' % notification.eventCode,
						 "An unknown Adyen notification of type %s has been received.\n\nYou'll need to go process this one manually:\n%s" % (
							 notification.eventCode,
							 urlresolvers.reverse('admin:adyen_notification_change', args=(notification.id,)),
						 )
		)
		AdyenLog(pspReference=notification.pspReference, message='Received notification of unknown type %s' % notification.eventCode, error=True).save()
示例#41
0
def process_capture(notification):
	if notification.success:
		# Successful capture, so we just set when the capture happened
		try:
			ts = TransactionStatus.objects.get(pspReference=notification.originalReference)
			ts.capturedat = datetime.now()
			ts.save()
		except TransactionStatus.DoesNotExist:
			# We just ignore captures for non-existant transactions. This
			# seems to happen for example when a transaction is cancelled
			# on a POS terminal.
			pass
	else:
		send_simple_mail(settings.INVOICE_SENDER_EMAIL,
						 settings.ADYEN_NOTIFICATION_RECEIVER,
						 'Unsuccessful adyen capture received',
						 "A creditcard capture for %s has failed.\nThe reason given was:\n%s\n\nYou want to investigate this since the payment was probably flagged as completed on authorization!\n" % (
							 notification.merchantReference,
							 notification.reason))
	# We confirm the notification even if we sent it, since there is not much more we can do
	notification.confirmed = True
	notification.save()
示例#42
0
def process_refund(notification):
	# Store the refund, and send an email!
	if notification.success:
		try:
			ts = TransactionStatus.objects.get(pspReference=notification.originalReference)
			refund = Refund(notification=notification, transaction=ts, refund_amount=notification.amount)
			refund.save()

			# Generate an open accounting record for this refund.
			# We expect this happens so seldom that we can just deal with
			# manually finishing off the accounting records.
			urls = [
				"https://ca-live.adyen.com/ca/ca/accounts/showTx.shtml?pspReference=%s&txType=Payment&accountKey=MerchantAccount.%s" % (notification.pspReference, notification.merchantAccountCode),
			]
			accrows = [
				(settings.ACCOUNTING_ADYEN_REFUNDS_ACCOUNT,
				 "Refund of %s (transaction %s) "  % (ts.notes, ts.pspReference),
				 -refund.refund_amount,
				 None),
			]

			send_simple_mail(settings.INVOICE_SENDER_EMAIL,
							 settings.ADYEN_NOTIFICATION_RECEIVER,
							 'Adyen refund received',
							 "A refund of %s%s for transaction %s was processed\n\nNOTE! You must complete the accounting system entry manually for refunds!" % (settings.CURRENCY_ABBREV, notification.amount, notification.originalReference))

			create_accounting_entry(date.today(), accrows, True, urls)

		except TransactionStatus.DoesNotExist:
			send_simple_mail(settings.INVOICE_SENDER_EMAIL,
							 settings.ADYEN_NOTIFICATION_RECEIVER,
							 'Adyen refund received for nonexisting transaction',
							 "A refund for %s was received, but the transaction does not exist!\n\nYou probably want to investigate this!\n" % notification.originalReference)
	else:
		send_simple_mail(settings.INVOICE_SENDER_EMAIL,
						 settings.ADYEN_NOTIFICATION_RECEIVER,
						 'Unsuccessful adyen refund received',
						 "A refund for %s has failed.\nThe reason given was:\n%s\n\nYou probably want to investigate this!\n" % (
							 notification.merchantReference,
							 notification.reason))
	notification.confirmed = True
	notification.save()
示例#43
0
									   valdate=valdate,
									   amount=amount,
									   description=description,
									   balance=balance).save()
			except Exception, e:
				print "Exception '%s' when parsing row %s" % (e, row)

	# Now send things off if there is anything to send
	with transaction.commit_on_success():
		if CMutuelTransaction.objects.filter(sent=False).exclude(description__startswith='VIR STG ADYEN ').exists():
			sio = cStringIO.StringIO()
			sio.write("One or more new transactions have been recorded in the Credit Mutuel account:\n\n")
			sio.write("%-10s  %15s  %s\n" % ('Date', 'Amount', 'Description'))
			sio.write("-" * 50)
			sio.write("\n")
			for cmt in CMutuelTransaction.objects.filter(sent=False).order_by('opdate'):
				# Maybe this shouldn't be hardcoded, but for now it is.
				# Exclude Adyen transactions, since they are already reported separately.
				# Still flag them as sent though, so they don't queue up forever.
				if not cmt.description.startswith('VIR STG ADYEN '):
					sio.write("%10s  %15s  %s\n" % (cmt.opdate, cmt.amount, cmt.description))

				cmt.sent = True
				cmt.save()
			send_simple_mail(settings.INVOICE_SENDER_EMAIL,
							 settings.INVOICE_SENDER_EMAIL,
							 'New Credit Mutuel transactions',
							 sio.getvalue())

	connection.close()
示例#44
0
def process_authorization(notification):
	if notification.success:
		# This is a successful notification, so flag this invoice
		# as paid. We also create a TransactionStatus for it, so that
		# can validate that it goes from authorized->captured.
		trans = TransactionStatus(pspReference=notification.pspReference,
								  notification=notification,
								  authorizedat=datetime.now(),
								  amount=notification.amount,
								  method=notification.paymentMethod,
								  notes=notification.merchantReference,
								  capturedat=None)
		trans.save()

		# Generate urls pointing back to this entry in the Adyen online
		# system, for inclusion in accounting records.
		urls = ["https://ca-live.adyen.com/ca/ca/accounts/showTx.shtml?pspReference=%s&txType=Payment&accountKey=MerchantAccount.%s" % (notification.pspReference, notification.merchantAccountCode),]

		# We can receive authorizations on non-primary Adyen merchant
		# accounts. This happens for example with payments from POS
		# terminals. In those cases, just send an email, and don't
		# try to match it to any invoices.
		# We still store and track the transaction.
		if notification.merchantAccountCode != settings.ADYEN_MERCHANTACCOUNT:
			send_simple_mail(settings.INVOICE_SENDER_EMAIL,
							 settings.ADYEN_NOTIFICATION_RECEIVER,
							 'Manual Adyen payment authorized',
							 "An Adyen payment of %s%s was authorized on the Adyen platform.\nThis payment was not from the automated system, it was manually authorized, probably from a POS terminal.\nReference: %s\nAdyen reference: %s\nMerchant account: %s\n" % (settings.CURRENCY_ABBREV, notification.amount, notification.merchantReference, notification.pspReference, notification.merchantAccountCode))
			notification.confirmed = True
			notification.save()

			# For manual payments, we can only create an open-ended entry
			# in the accounting
			accstr = "Manual Adyen payment: %s (%s)" % (notification.merchantReference, notification.pspReference)
			accrows = [
				(settings.ACCOUNTING_ADYEN_AUTHORIZED_ACCOUNT, accstr, trans.amount, None),
				]
			create_accounting_entry(date.today(), accrows, True, urls)
			return

		# Process a payment on the primary account
		manager = InvoiceManager()
		try:
			# Figure out the invoiceid
			if not notification.merchantReference.startswith(settings.ADYEN_MERCHANTREF_PREFIX):
				raise AdyenProcessingException('Merchant reference does not start with %s' % settings.ADYEN_MERCHANTREF_PREFIX)
			invoiceid = int(notification.merchantReference[len(settings.ADYEN_MERCHANTREF_PREFIX):])

			# Get the actual invoice
			try:
				invoice = Invoice.objects.get(pk=invoiceid)
			except Invoice.DoesNotExist:
				raise AdyenProcessingException('Invoice with id %s does not exist' % invoiceid)

			def invoice_logger(msg):
				raise AdyenProcessingException('Invoice processing failed: %s', msg)

			manager.process_incoming_payment_for_invoice(invoice, notification.amount, 'Adyen id %s' % notification.pspReference, 0, settings.ACCOUNTING_ADYEN_AUTHORIZED_ACCOUNT, 0, urls, invoice_logger)

			if invoice.accounting_object:
				# Store the accounting object so we can properly tag the
				# fee for it when we process the settlement (since we don't
				# actually know the fee yet)
				trans.accounting_object = invoice.accounting_object
				trans.save()

			# If nothing went wrong, then this invoice is now fully
			# flagged as paid in the system.
			send_simple_mail(settings.INVOICE_SENDER_EMAIL,
							 settings.ADYEN_NOTIFICATION_RECEIVER,
							 'Adyen payment authorized',
							 "An Adyen payment of %s%s with reference %s was authorized on the Adyen platform.\nInvoice: %s\nRecipient name: %s\nRecipient user: %s\nAdyen reference: %s\n" % (settings.CURRENCY_ABBREV, notification.amount, notification.merchantReference, invoice.title, invoice.recipient_name, invoice.recipient_email, notification.pspReference))

		except AdyenProcessingException, ex:
			# Generate an email telling us about this exception!
			send_simple_mail(settings.INVOICE_SENDER_EMAIL,
							 settings.ADYEN_NOTIFICATION_RECEIVER,
							 'Exception occured processing Adyen notification',
							 "An exception occured processing the notification for %s:\n\n%s\n" % (
								 notification.merchantReference,
								 ex)
						 )
			# We have stored the notification already, but we want
			# to make sure it's not *confirmed*. That way it'll keep
			# bugging the user. So, return here instead of confirming
			# it.
			return
示例#45
0
def process_raw_adyen_notification(raw, POST):
	# Process a single raw Adyen notification. Must *not* be called in
	# a transactional context, as it manages it's own.

	# Now open a transaction for actually processing what we get
	with transaction.commit_on_success():
		# Set it to confirmed - if we were unable to process the RAW one,
		# this will be rolled back by the transaction, and that's the only
		# thing that htis flag means. Anything else is handled by the
		# regular notification.
		raw.confirmed = True
		raw.save()

		# Have we already seen this notification before?
		notlist = list(Notification.objects.filter(pspReference=POST['pspReference'], eventCode=POST['eventCode'], merchantAccountCode=POST['merchantAccountCode']))
		if len(notlist) == 1:
			# Found it before!
			notification = notlist[0]

			# According to Adyen integration manual, the only case when
			# we need to process this is when it goes from
			# success=False -> success=True.
			if not notification.success and POST['success'] == 'true':
				# We'll implement this one later, but for now trigger a
				# manual email so we don't loose things.
				send_simple_mail(settings.INVOICE_SENDER_EMAIL,
								 settings.ADYEN_NOTIFICATION_RECEIVER,
								 'Received adyen notification type %s that went from failure to success!' % notification.eventCode,
							 "An Adyen notification that went from failure to success has been received.\nThe system doesn't know how to handle this yet, so you'll need to go take a manual look!\n",
							 )
				AdyenLog(pspReference=notification.pspReference, message='Received success->fail notification of type %s, unhandled' % notification.eventCode, error=True).save()
			else:
				AdyenLog(pspReference=notification.pspReference, message='Received duplicate %s notification' % notification.eventCode).save()
				# Don't actually do any processing here either
		else:
			# Not found, so create
			notification = Notification()
			notification.rawnotification = raw
			notification.eventDate = POST['eventDate']
			notification.eventCode = POST['eventCode']
			notification.live = (POST['live'] == 'true')
			notification.success = (POST['success'] == 'true')
			notification.pspReference = POST['pspReference']
			notification.originalReference = POST['originalReference']
			notification.merchantReference = POST['merchantReference']
			notification.merchantAccountCode = POST['merchantAccountCode']
			notification.paymentMethod = POST['paymentMethod']
			notification.reason = POST['reason']
			try:
				notification.amount = int(POST['value'])/100 # We only deal in whole euros
			except:
				# Invalid amount, set to -1
				AdyenLog(pspReference=notification.pspReference, message='Received invalid amount %s' % POST['value'], error=True).save()
				notification.amount = -1
			if POST['currency'] != settings.CURRENCY_ABBREV:
				# For some reason, *report* notifications specifically get delivered with
				# a hard-coded value of EUR, even though they have no currency inside them.
				if notification.eventCode != 'REPORT_AVAILABLE':
					AdyenLog(pspReference=notification.pspReference, message='Received invalid currency %s' % POST['currency'], error=True).save()
					notification.amount = -2

			# Save this unconfirmed for now
			notification.save()

			# Process this notification, which includes flagging invoices
			# as paid.
			process_one_notification(notification)

			# Log the fact that we received it
			AdyenLog(pspReference=notification.pspReference, message='Processed %s notification for %s' % (notification.eventCode, notification.merchantReference)).save()

	# Return that we've consumed the report outside the transaction, in
	# the unlikely event that the COMMIT is what failed
	return True
示例#46
0
							 'Exception occured processing Adyen notification',
							 "An exception occured processing the notification for %s:\n\n%s\n" % (
								 notification.merchantReference,
								 ex)
						 )
			# We have stored the notification already, but we want
			# to make sure it's not *confirmed*. That way it'll keep
			# bugging the user. So, return here instead of confirming
			# it.
			return
	else:
		send_simple_mail(settings.INVOICE_SENDER_EMAIL,
						 settings.ADYEN_NOTIFICATION_RECEIVER,
						 'Unsuccessful Adyen authorization received',
						 "A creditcard authorization for %s on account %s has failed.\nThe reason given was:\n%s\n\nYou don't need to take any further action, nothing has been confirmed in the systems." % (
							 notification.merchantReference,
							 notification.merchantAccountCode,
							 notification.reason,
							 )
						 )
	notification.confirmed = True
	notification.save()

def process_capture(notification):
	if notification.success:
		# Successful capture, so we just set when the capture happened
		try:
			ts = TransactionStatus.objects.get(pspReference=notification.originalReference)
			ts.capturedat = datetime.now()
			ts.save()
		except TransactionStatus.DoesNotExist: