Fundraising in PDF.
[wolnelektury.git] / src / club / payu / models.py
index 4ab2c0a..af38448 100644 (file)
@@ -1,38 +1,50 @@
+# This file is part of Wolne Lektury, licensed under GNU Affero GPLv3 or later.
+# Copyright © Fundacja Wolne Lektury. 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
+from django.utils.timezone import now
 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('token jednorazowy', max_length=255)
+    reusable_token = models.CharField('token wielokrotnego użytku', max_length=255, null=True, blank=True)
+    created_at = models.DateTimeField('utworzony', auto_now_add=True)
 
     class Meta:
         abstract = True
+        verbose_name = 'token PayU karty płatniczej'
+        verbose_name_plural = 'tokeny PayU kart płatniczych'
 
 
 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, blank=True)   # TODO: redundant?
+    customer_ip = models.GenericIPAddressField('adres IP klienta')
+    order_id = models.CharField('ID zamówienia', max_length=255, blank=True)
 
     status = models.CharField(max_length=128, blank=True, choices=[
-        ('PENDING', 'Pending'),
-        ('WAITING_FOR_CONFIRMATION', 'Waiting for confirmation'),
-        ('COMPLETED', 'Completed'),
-        ('CANCELED', 'Canceled'),
-        ('REJECTED', 'Rejected'),
+        ('PENDING', 'Czeka'),
+        ('WAITING_FOR_CONFIRMATION', 'Czeka na potwierdzenie'),
+        ('COMPLETED', 'Ukończone'),
+        ('CANCELED', 'Anulowane'),
+        ('REJECTED', 'Odrzucone'),
+
+        ('ERR-INVALID_TOKEN', 'Błędny 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
+        verbose_name = 'Zamówienie PayU'
+        verbose_name_plural = 'Zamówienia PayU'
 
     # These need to be provided in a subclass.
 
@@ -64,6 +76,9 @@ class Order(models.Model):
     def get_notify_url(self):
         raise NotImplementedError
 
+    def get_thanks_url(self):
+        raise NotImplementedError
+
     def status_updated(self):
         pass
 
@@ -72,6 +87,11 @@ class Order(models.Model):
     def get_pos(self):
         return POSS[self.pos_id]
 
+    def get_continue_url(self):
+        return "https://{}{}".format(
+            Site.objects.get_current().domain,
+            self.get_thanks_url())
+
     def get_representation(self, token=None):
         rep = {
             "notifyUrl": self.get_notify_url(),
@@ -79,7 +99,7 @@ class Order(models.Model):
             "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(),
@@ -116,20 +136,30 @@ class Order(models.Model):
                 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.get_thanks_url())
 
 
 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('treść')
+    received_at = models.DateTimeField('odebrana', auto_now_add=True)
 
     class Meta:
         abstract = True
+        verbose_name = 'notyfikacja PayU'
+        verbose_name_plural = 'notyfikacje PayU'
 
     def get_status(self):
         return json.loads(self.body)['order']['status']
@@ -138,5 +168,7 @@ class Notification(models.Model):
         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()