diff options
Diffstat (limited to 'postgresqleu/trustlypayment/util.py')
| -rw-r--r-- | postgresqleu/trustlypayment/util.py | 310 |
1 files changed, 155 insertions, 155 deletions
diff --git a/postgresqleu/trustlypayment/util.py b/postgresqleu/trustlypayment/util.py index 15a3f1e0..7a4b7a8f 100644 --- a/postgresqleu/trustlypayment/util.py +++ b/postgresqleu/trustlypayment/util.py @@ -15,158 +15,158 @@ from models import TrustlyNotification # Django intgrated wrapper for the trustly API class Trustly(TrustlyWrapper): - def __init__(self): - super(Trustly, self).__init__(settings.TRUSTLY_APIBASE, - settings.TRUSTLY_USER, - settings.TRUSTLY_PASSWORD, - settings.TRUSTLY_PRIVATE_KEY, - settings.TRUSTLY_PUBLIC_KEY, - '{0}/trustly_notification/'.format(settings.SITEBASE), - settings.CURRENCY_ABBREV, - getattr(settings, 'TRUSTLY_HOLD_NOTIFICATIONS', False), - ) - - def process_raw_trustly_notification(self, raw): - (uuid, method, data) = self.parse_notification(raw.contents) - if not data: - TrustlyLog(message="Failed to parse trustly raw notification {0}".format(raw.id), error=True).save() - return (False, uuid, method) - - n = None - with transaction.atomic(): - # Find if we have already seen this guy - try: - TrustlyNotification.objects.get(notificationid=data['notificationid']) - # If it's found, then we're happy, so keep on smiling. Flag this one as - # confirmed as well. - raw.confirmed = True - raw.save() - return (True, uuid, method) - except TrustlyNotification.DoesNotExist: - pass - - n = TrustlyNotification( - receivedat=datetime.now(), - rawnotification=raw, - method=method, - notificationid=data['notificationid'], - orderid=data['orderid'], - amount=data.has_key('amount') and Decimal(data['amount']) or None, - messageid=data['messageid'], - ) - n.save() - raw.confirmed=True - raw.save() - - # Raw is confirmed, but parsed one is still pending. So handle that one. - try: - self.process_notification(n) - except Exception, e: - self.log_and_email("Exception processing notification {0}: {1}".format(n.id, e)) - - # If we somehow failed to handle at this level, we still flag things as ok to - # Trustly, and deal with it ourselves. - # Notifications can always be re-parsed - return (True, uuid, method) - - def log_and_email(self, message): - TrustlyLog(message=message, error=True).save() - - send_simple_mail(settings.INVOICE_SENDER_EMAIL, - settings.TRUSTLY_NOTIFICATION_RECEIVER, - "Trustly payment error", - u"A trustly payment failed with the error:\n\n{0}".format(message), - ) - - @transaction.atomic - def process_notification(self, notification): - if notification.method in ('pending', 'credit'): - # Find the appropriate transaction - try: - trans = TrustlyTransaction.objects.get(orderid=notification.orderid) - except TrustlyTransaction.DoesNotExist: - self.log_and_email("Transaction {0} for notification {1} not found!".format(notification.orderid, notification.id)) - return False - if trans.amount != notification.amount: - self.log_and_email("Notification {0} for transaction {1} has invalid amount ({2} should be {3})!".format(notification.id, notification.orderid, notification.amount, trans.amount)) - return False - - if notification.method == 'pending': - # Pending is just an incremental state, so we collect it but don't do anything with - # it. - if not trans.pendingat: - trans.pendingat = datetime.now() - trans.save() - notification.confirmed=True - notification.save() - - TrustlyLog(message="Pending payment for Trustly id {0} (order {1}) received".format(trans.id, trans.orderid)).save() - - return True - else: - # Credit! The payment is completed! - if not trans.pendingat: - # We set pending in case it never showed up - trans.pendingat = datetime.now() - if trans.completedat: - self.log_and_email("Duplicate completed notification ({0}) received for transaction {1}!".format(notification.id, notification.orderid)) - return False - - trans.completedat = datetime.now() - try: - self.process_completed_payment(trans) - except TrustlyException, e: - self.log_and_email(e) - return False - trans.save() - notification.confirmed=True - notification.save() - return True - elif notification.method == 'cancel': - try: - trans = TrustlyTransaction.objects.get(orderid=notification.orderid) - if trans.pendingat: - self.log_and_email("Transaction {0} canceled by notification {1} but already in progress. Ignoring cancel!".format(notification.orderid, notification.id)) - return False - TrustlyLog(message='Transaction {0} canceled from notification'.format(notification.orderid)).save() - trans.delete() - except TrustlyTransaction.DoesNotExist: - TrustlyLog("Abandoned transaction {0} canceled from notification".format(notification.orderid)) - notification.confirmed = True - notification.save() - return True - else: - self.log_and_email("Unknown notification type '{0}' in notification {1}".format(notification.method, notification.id)) - return False - - # Can't reach here - return False - - def process_completed_payment(self, trans): - manager = InvoiceManager() - try: - invoice = Invoice.objects.get(pk=trans.invoiceid) - except Invoice.DoesNotExist: - raise TrustlyException("Received Trustly notification for non-existing invoice id {0}".format(trans.invoiceid)) - - def invoice_logger(msg): - raise TrustlyException("Trustly invoice processing failed: {0}".format(msg)) - - method = InvoicePaymentMethod.objects.get(classname='postgresqleu.util.payment.trustly.TrustlyPayment') - 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? - settings.ACCOUNTING_TRUSTLY_ACCOUNT, - 0, #XXX: if supporting fees, support fee account - [], - 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)).save() - - send_simple_mail(settings.INVOICE_SENDER_EMAIL, - settings.TRUSTLY_NOTIFICATION_RECEIVER, - "Trustly payment completed", - u"A Trustly payment of {0}{1} for invoice {2} was completed on the Trustly platform.\n\nInvoice: {3}\nRecipient name: {4}\nRecipient email: {5}\n".format(settings.CURRENCY_ABBREV, trans.amount, invoice.id, invoice.title, invoice.recipient_name, invoice.recipient_email), - ) + def __init__(self): + super(Trustly, self).__init__(settings.TRUSTLY_APIBASE, + settings.TRUSTLY_USER, + settings.TRUSTLY_PASSWORD, + settings.TRUSTLY_PRIVATE_KEY, + settings.TRUSTLY_PUBLIC_KEY, + '{0}/trustly_notification/'.format(settings.SITEBASE), + settings.CURRENCY_ABBREV, + getattr(settings, 'TRUSTLY_HOLD_NOTIFICATIONS', False), + ) + + def process_raw_trustly_notification(self, raw): + (uuid, method, data) = self.parse_notification(raw.contents) + if not data: + TrustlyLog(message="Failed to parse trustly raw notification {0}".format(raw.id), error=True).save() + return (False, uuid, method) + + n = None + with transaction.atomic(): + # Find if we have already seen this guy + try: + TrustlyNotification.objects.get(notificationid=data['notificationid']) + # If it's found, then we're happy, so keep on smiling. Flag this one as + # confirmed as well. + raw.confirmed = True + raw.save() + return (True, uuid, method) + except TrustlyNotification.DoesNotExist: + pass + + n = TrustlyNotification( + receivedat=datetime.now(), + rawnotification=raw, + method=method, + notificationid=data['notificationid'], + orderid=data['orderid'], + amount=data.has_key('amount') and Decimal(data['amount']) or None, + messageid=data['messageid'], + ) + n.save() + raw.confirmed=True + raw.save() + + # Raw is confirmed, but parsed one is still pending. So handle that one. + try: + self.process_notification(n) + except Exception, e: + self.log_and_email("Exception processing notification {0}: {1}".format(n.id, e)) + + # If we somehow failed to handle at this level, we still flag things as ok to + # Trustly, and deal with it ourselves. + # Notifications can always be re-parsed + return (True, uuid, method) + + def log_and_email(self, message): + TrustlyLog(message=message, error=True).save() + + send_simple_mail(settings.INVOICE_SENDER_EMAIL, + settings.TRUSTLY_NOTIFICATION_RECEIVER, + "Trustly payment error", + u"A trustly payment failed with the error:\n\n{0}".format(message), + ) + + @transaction.atomic + def process_notification(self, notification): + if notification.method in ('pending', 'credit'): + # Find the appropriate transaction + try: + trans = TrustlyTransaction.objects.get(orderid=notification.orderid) + except TrustlyTransaction.DoesNotExist: + self.log_and_email("Transaction {0} for notification {1} not found!".format(notification.orderid, notification.id)) + return False + if trans.amount != notification.amount: + self.log_and_email("Notification {0} for transaction {1} has invalid amount ({2} should be {3})!".format(notification.id, notification.orderid, notification.amount, trans.amount)) + return False + + if notification.method == 'pending': + # Pending is just an incremental state, so we collect it but don't do anything with + # it. + if not trans.pendingat: + trans.pendingat = datetime.now() + trans.save() + notification.confirmed=True + notification.save() + + TrustlyLog(message="Pending payment for Trustly id {0} (order {1}) received".format(trans.id, trans.orderid)).save() + + return True + else: + # Credit! The payment is completed! + if not trans.pendingat: + # We set pending in case it never showed up + trans.pendingat = datetime.now() + if trans.completedat: + self.log_and_email("Duplicate completed notification ({0}) received for transaction {1}!".format(notification.id, notification.orderid)) + return False + + trans.completedat = datetime.now() + try: + self.process_completed_payment(trans) + except TrustlyException, e: + self.log_and_email(e) + return False + trans.save() + notification.confirmed=True + notification.save() + return True + elif notification.method == 'cancel': + try: + trans = TrustlyTransaction.objects.get(orderid=notification.orderid) + if trans.pendingat: + self.log_and_email("Transaction {0} canceled by notification {1} but already in progress. Ignoring cancel!".format(notification.orderid, notification.id)) + return False + TrustlyLog(message='Transaction {0} canceled from notification'.format(notification.orderid)).save() + trans.delete() + except TrustlyTransaction.DoesNotExist: + TrustlyLog("Abandoned transaction {0} canceled from notification".format(notification.orderid)) + notification.confirmed = True + notification.save() + return True + else: + self.log_and_email("Unknown notification type '{0}' in notification {1}".format(notification.method, notification.id)) + return False + + # Can't reach here + return False + + def process_completed_payment(self, trans): + manager = InvoiceManager() + try: + invoice = Invoice.objects.get(pk=trans.invoiceid) + except Invoice.DoesNotExist: + raise TrustlyException("Received Trustly notification for non-existing invoice id {0}".format(trans.invoiceid)) + + def invoice_logger(msg): + raise TrustlyException("Trustly invoice processing failed: {0}".format(msg)) + + method = InvoicePaymentMethod.objects.get(classname='postgresqleu.util.payment.trustly.TrustlyPayment') + 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? + settings.ACCOUNTING_TRUSTLY_ACCOUNT, + 0, #XXX: if supporting fees, support fee account + [], + 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)).save() + + send_simple_mail(settings.INVOICE_SENDER_EMAIL, + settings.TRUSTLY_NOTIFICATION_RECEIVER, + "Trustly payment completed", + u"A Trustly payment of {0}{1} for invoice {2} was completed on the Trustly platform.\n\nInvoice: {3}\nRecipient name: {4}\nRecipient email: {5}\n".format(settings.CURRENCY_ABBREV, trans.amount, invoice.id, invoice.title, invoice.recipient_name, invoice.recipient_email), + ) |
