receipts resend
[wolnelektury.git] / src / club / payu / models.py
index 4ab2c0a..10dd60a 100644 (file)
@@ -1,38 +1,51 @@
+# This file is part of Wolnelektury, licensed under GNU Affero GPLv3 or later.
+# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
+#
 import json
 from urllib.parse import urlencode
 from urllib.request import HTTPError
 from django.contrib.sites.models import Site
 from django.db import models
 from django.urls import reverse
 import json
 from urllib.parse import urlencode
 from urllib.request import HTTPError
 from django.contrib.sites.models import Site
 from django.db import models
 from django.urls import reverse
+from django.utils.timezone import now
+from django.utils.translation import ugettext_lazy as _
 from . import POSS
 
 
 class CardToken(models.Model):
     """ This should be attached to a payment schedule. """
 from . import POSS
 
 
 class CardToken(models.Model):
     """ This should be attached to a payment schedule. """
-    pos_id = models.CharField(max_length=255)
-    disposable_token = models.CharField(max_length=255)
-    reusable_token = models.CharField(max_length=255, null=True, blank=True)
-    created_at = models.DateTimeField(auto_now_add=True)
+    pos_id = models.CharField(_('POS id'), max_length=255)
+    disposable_token = models.CharField(_('disposable token'), max_length=255)
+    reusable_token = models.CharField(_('reusable token'), max_length=255, null=True, blank=True)
+    created_at = models.DateTimeField(_('created_at'), auto_now_add=True)
 
     class Meta:
         abstract = True
 
     class Meta:
         abstract = True
+        verbose_name = _('PayU card token')
+        verbose_name_plural = _('PayU card tokens')
 
 
 class Order(models.Model):
 
 
 class Order(models.Model):
-    pos_id = models.CharField(max_length=255)   # TODO: redundant?
-    customer_ip = models.GenericIPAddressField()
-    order_id = models.CharField(max_length=255, blank=True)
+    pos_id = models.CharField(_('POS id'), max_length=255)   # TODO: redundant?
+    customer_ip = models.GenericIPAddressField(_('customer IP'))
+    order_id = models.CharField(_('order ID'), max_length=255, blank=True)
 
     status = models.CharField(max_length=128, blank=True, choices=[
 
     status = models.CharField(max_length=128, blank=True, choices=[
-        ('PENDING', 'Pending'),
-        ('WAITING_FOR_CONFIRMATION', 'Waiting for confirmation'),
-        ('COMPLETED', 'Completed'),
-        ('CANCELED', 'Canceled'),
-        ('REJECTED', 'Rejected'),
+        ('PENDING', _('Pending')),
+        ('WAITING_FOR_CONFIRMATION', _('Waiting for confirmation')),
+        ('COMPLETED', _('Completed')),
+        ('CANCELED', _('Canceled')),
+        ('REJECTED', _('Rejected')),
+
+        ('ERR-INVALID_TOKEN', _('Invalid token')),
     ])
     ])
+    created_at = models.DateTimeField(null=True, blank=True, auto_now_add=True)
+    completed_at = models.DateTimeField(null=True, blank=True)
 
     class Meta:
         abstract = True
 
     class Meta:
         abstract = True
+        verbose_name = _('PayU order')
+        verbose_name_plural = _('PayU orders')
 
     # These need to be provided in a subclass.
 
 
     # These need to be provided in a subclass.
 
@@ -79,7 +92,7 @@ class Order(models.Model):
             "merchantPosId": self.pos_id,
             "currencyCode": self.get_pos().currency_code,
             "totalAmount": str(int(self.get_amount() * 100)),
             "merchantPosId": self.pos_id,
             "currencyCode": self.get_pos().currency_code,
             "totalAmount": str(int(self.get_amount() * 100)),
-            "extOrderId": "wolne-lektury-rcz-%d" % self.pk,
+            "extOrderId": "wolne-lektury-%d" % self.pk,
 
             "buyer": self.get_buyer() or {},
             "continueUrl": self.get_continue_url(),
 
             "buyer": self.get_buyer() or {},
             "continueUrl": self.get_continue_url(),
@@ -116,20 +129,30 @@ class Order(models.Model):
                 token.save()
             # else?
 
                 token.save()
             # else?
 
-        self.order_id = response['orderId']
-        self.save()
+        if 'orderId' not in response:
+            code = response.get('status', {}).get('codeLiteral', '')
+            if code:
+                self.status = 'ERR-' + str(code)
+                self.save()
+                self.status_updated()
+            else:
+                raise ValueError("Expecting dict with `orderId` key, got: %s" % response)
+        else:
+            self.order_id = response['orderId']
+            self.save()
 
 
-        
-        return response.get('redirectUri', self.schedule.get_absolute_url())
+        return response.get('redirectUri', self.schedule.get_thanks_url())
 
 
 class Notification(models.Model):
     """ Add `order` FK to real Order model. """
 
 
 class Notification(models.Model):
     """ Add `order` FK to real Order model. """
-    body = models.TextField()
-    received_at = models.DateTimeField(auto_now_add=True)
+    body = models.TextField(_('body'))
+    received_at = models.DateTimeField(_('received_at'), auto_now_add=True)
 
     class Meta:
         abstract = True
 
     class Meta:
         abstract = True
+        verbose_name = _('PayU notification')
+        verbose_name_plural = _('PayU notifications')
 
     def get_status(self):
         return json.loads(self.body)['order']['status']
 
     def get_status(self):
         return json.loads(self.body)['order']['status']
@@ -138,5 +161,7 @@ class Notification(models.Model):
         status = self.get_status()
         if self.order.status not in (status, 'COMPLETED'):
             self.order.status = status
         status = self.get_status()
         if self.order.status not in (status, 'COMPLETED'):
             self.order.status = status
+            if status == 'COMPLETED':
+                self.order.completed_at = now()
             self.order.save()
             self.order.status_updated()
             self.order.save()
             self.order.status_updated()