def process_payment_accounting_report(self, report):
        method = report.paymentmethod
        pm = method.get_implementation()

        sio = io.StringIO(report.contents)
        reader = csv.DictReader(sio, delimiter=',')
        for l in reader:
            # SentForSettle is what we call capture, so we track that
            # Settled is when we actually receive the money
            # Changes in Sep 2015 means Settled is sometimes SettledBulk
            # Everything else we ignore
            if l['Record Type'] == 'SentForSettle' or l['Record Type'] == 'Settled' or l['Record Type'] == 'SettledBulk':
                # Find the actual payment
                pspref = l['Psp Reference']
                bookdate = l['Booking Date']
                try:
                    trans = TransactionStatus.objects.get(pspReference=pspref, paymentmethod=method)
                except TransactionStatus.DoesNotExist:
                    # Yes, for now we rollback the whole processing of this one
                    raise Exception('Transaction %s not found!' % pspref)
                if l['Record Type'] == 'SentForSettle':
                    # If this is a POS transaction, it typically received a
                    # separate CAPTURE notification, in which case the capture
                    # date is already set. But if not, we'll set it to the
                    # sent for settle date.
                    if not trans.capturedat:
                        trans.capturedat = bookdate
                        trans.method = l['Payment Method']
                        trans.save()
                        AdyenLog(message='Transaction %s captured at %s' % (pspref, bookdate), error=False, paymentmethod=method).save()
                        if self.verbose:
                            self.stdout.write("Sent for settle on {0}".format(pspref))
                elif l['Record Type'] in ('Settled', 'SettledBulk'):
                    if trans.settledat is not None:
                        # Transaction already settled. But we might be reprocessing
                        # the report, so verify if the previously settled one is
                        # *identical*.
                        if trans.settledamount == Decimal(l['Main Amount']).quantize(Decimal('0.01')):
                            self.stderr.write("Transaction {0} already settled at {2}, ignoring (NOT creating accounting record)!".format(pspref, trans.settledat))
                            continue
                        else:
                            raise CommandError('Transaction {0} settled more than once?!'.format(pspref))
                    if not trans.capturedat:
                        trans.capturedat = bookdate

                    trans.settledat = bookdate
                    trans.settledamount = Decimal(l['Main Amount']).quantize(Decimal('0.01'))
                    trans.save()
                    if self.verbose:
                        self.stdout.write("Settled {0}, total amount {1}".format(pspref, trans.settledamount))
                    AdyenLog(message='Transaction %s settled at %s' % (pspref, bookdate), error=False, paymentmethod=method).save()

                    # Settled transactions create a booking entry
                    accstr = "Adyen settlement %s" % pspref
                    accrows = [
                        (pm.config('accounting_authorized'), accstr, -trans.amount, None),
                        (pm.config('accounting_payable'), accstr, trans.settledamount, None),
                        (pm.config('accounting_fee'), accstr, trans.amount - trans.settledamount, trans.accounting_object),
                        ]
                    create_accounting_entry(accrows, False)
    def process_reports(self):
        # Process all downloaded but unprocessed reports

        for report in Report.objects.filter(downloadedat__isnull=False, processedat=None).order_by('downloadedat'):
            try:
                with transaction.atomic():
                    if self.verbose:
                        self.stdout.write("Processing {0}".format(report.url))

                    # To know what to do, we look at the filename of the report URL
                    filename = report.url.split('/')[-1]
                    if filename.startswith('payments_accounting_report_'):
                        self.process_payment_accounting_report(report)
                    elif filename.startswith('received_payments_report'):
                        self.process_received_payments_report(report)
                    elif filename.startswith('settlement_detail_report_batch_'):
                        self.process_settlement_detail_report_batch(report)
                    else:
                        raise CommandError('Unknown report type in file "{0}"'.format(filename))

                    # If successful, flag as processed and add the log
                    report.processedat = timezone.now()
                    report.save()
                    AdyenLog(message='Processed report %s' % report.url, error=False, paymentmethod=report.paymentmethod).save()
            except Exception as ex:
                self.stderr.write("Failed to process report {0}: {1}".format(report.url, ex))
                AdyenLog(message='Failed to process report %s: %s' % (report.url, ex), error=True, paymentmethod=report.paymentmethod).save()
Example #3
0
    def handle(self, *args, **options):
        with transaction.atomic():
            try:
                rawnotification = RawNotification.objects.get(pk=options['id'])
            except RawNotification.DoesNotExist:
                raise CommandError("Notification {0} not found.".format(
                    options['id']))

            if rawnotification.confirmed:
                raise CommandError(
                    "Notification {0} is already processed.".format(
                        options['id']))

            # Rebuild a POST dictionary with the contents of this request
            POST = QueryDict(rawnotification.contents, "utf8")

            AdyenLog(pspReference=rawnotification.id,
                     message='Reprocessing RAW notification id %s' %
                     rawnotification.id,
                     error=False,
                     paymentmethod=rawnotification.paymentmethod).save()

            process_raw_adyen_notification(rawnotification, POST)
            self.stdout.write(
                "Completed reprocessing raw notification {0}".format(
                    rawnotification.id))
Example #4
0
 def download_reports(self):
     # Download all currently pending reports (that we can)
     for report in Report.objects.filter(
             downloadedat=None).order_by('receivedat'):
         pm = report.paymentmethod.get_implementation()
         try:
             with transaction.atomic():
                 if self.verbose:
                     self.stdout.write("Downloading {0}".format(report.url))
                 resp = requests.get(report.url,
                                     auth=HTTPBasicAuth(
                                         pm.config('report_user'),
                                         pm.config('report_password')))
                 if resp.status_code != 200:
                     self.stderr.write(
                         "Downloaded report {0} and got status code {1}. Not storing, will try again."
                         .format(report.url, resp.status_code))
                 elif len(resp.text) == 0:
                     self.stderr.write(
                         "Downloaded report {0} and got zero bytes (no header). Not storing, will try again."
                         .format(report.url))
                 else:
                     report.downloadedat = datetime.now()
                     report.contents = resp.text
                     report.save()
                     AdyenLog(message='Downloaded report {0}'.format(
                         report.url),
                              error=False,
                              paymentmethod=report.paymentmethod).save()
         except Exception as ex:
             self.stderr.write("Failed to download report {0}: {1}".format(
                 report.url, ex))
             # This might fail again if we had a db problem, but it should be OK as long as it
             # was just a download issue which is most likely.
             AdyenLog(message='Failed to download report %s: %s' %
                      (report.url, ex),
                      error=True,
                      paymentmethod=report.paymentmethod).save()
    def handle(self, *args, **options):
        with transaction.atomic():
            try:
                notification = Notification.objects.get(pspReference=options['pspreference'])
            except Notification.DoesNotExist:
                raise CommandError("Notification {0} does not exist.".format(options['pspreference']))

            AdyenLog(pspReference=notification.pspReference,
                     message='Reprocessing notification id {0}'.format(notification.id),
                     error=False,
                     paymentmethod=notification.rawnotification.paymentmethod).save()

            process_one_notification(notification)
            self.stdout.write("Completed reprocessing notification {0}.".format(notification.pspReference))
Example #6
0
    def download_reports(self):
        # Download all currently pending reports (that we can)
        for report in Report.objects.filter(
                downloadedat=None).order_by('receivedat'):
            pm = report.paymentmethod.get_implementation()
            if not report.url.endswith('.csv'):
                with transaction.atomic():
                    report.downloadedat = timezone.now()
                    report.processedat = timezone.now()
                    report.save()
                    if report.url.endswith('.pdf') or report.url.endswith(
                            '.xlsx'):
                        # For known file-types, just log it and not as an error.
                        AdyenLog(
                            message=
                            "Report {} is not of type csv, ignoring but flagging as downloaded and processed"
                            .format(report.url),
                            error=False,
                            paymentmethod=report.paymentmethod).save()
                    else:
                        # For unknown types, log as an error in case this is something broken.
                        AdyenLog(
                            message=
                            "Report {} is of unknown type, ignoring but flagging as downloaded and processed"
                            .format(report.url),
                            error=True,
                            paymentmethod=report.paymentmethod).save()
                continue

            # Now that we know it's a CSV, download the contents of the report
            try:
                with transaction.atomic():
                    if self.verbose:
                        self.stdout.write("Downloading {0}".format(report.url))
                    resp = requests.get(report.url,
                                        auth=HTTPBasicAuth(
                                            pm.config('report_user'),
                                            pm.config('report_password')))
                    if resp.status_code != 200:
                        self.stderr.write(
                            "Downloaded report {0} and got status code {1}. Not storing, will try again."
                            .format(report.url, resp.status_code))
                    elif len(resp.text) == 0:
                        self.stderr.write(
                            "Downloaded report {0} and got zero bytes (no header). Not storing, will try again."
                            .format(report.url))
                    else:
                        report.downloadedat = timezone.now()
                        report.contents = resp.text
                        report.save()
                        AdyenLog(message='Downloaded report {0}'.format(
                            report.url),
                                 error=False,
                                 paymentmethod=report.paymentmethod).save()
            except Exception as ex:
                self.stderr.write("Failed to download report {0}: {1}".format(
                    report.url, ex))
                # This might fail again if we had a db problem, but it should be OK as long as it
                # was just a download issue which is most likely.
                AdyenLog(message='Failed to download report %s: %s' %
                         (report.url, ex),
                         error=True,
                         paymentmethod=report.paymentmethod).save()