CCrypto VPN public website https://vpn.ccrypto.org/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

194 lines
5.8 KiB

  1. from django.db import models
  2. from django.conf import settings
  3. from django.utils.translation import ugettext_lazy as _
  4. from jsonfield import JSONField
  5. from datetime import timedelta
  6. from ccvpn.common import get_price
  7. from .backends import BackendBase
  8. backends_settings = settings.PAYMENTS_BACKENDS
  9. assert isinstance(backends_settings, dict)
  10. CURRENCY_CODE, CURRENCY_NAME = settings.PAYMENTS_CURRENCY
  11. STATUS_CHOICES = (
  12. ('new', _("Waiting for payment")),
  13. ('confirmed', _("Confirmed")),
  14. ('cancelled', _("Cancelled")),
  15. ('rejected', _("Rejected by processor")),
  16. ('error', _("Payment processing failed")),
  17. )
  18. # A Subscription is created with status='new'. When getting back from PayPal,
  19. # it may get upgraded to 'unconfirmed'. It will be set 'active' with the first
  20. # confirmed payment.
  21. # 'unconfirmed' exists to prevent creation of a second Subscription while
  22. # waiting for the first one to be confirmed.
  23. SUBSCR_STATUS_CHOICES = (
  24. ('new', _("Created")),
  25. ('unconfirmed', _("Waiting for payment")),
  26. ('active', _("Active")),
  27. ('cancelled', _("Cancelled")),
  28. ('error', _("Error")),
  29. )
  30. SUBSCR_PERIOD_CHOICES = (
  31. ('3m', _("Every 3 months")),
  32. ('6m', _("Every 6 months")),
  33. ('12m', _("Every year")),
  34. )
  35. # All known backends (classes)
  36. BACKENDS = {}
  37. BACKEND_CHOICES = []
  38. # All enabled backends (configured instances)
  39. ACTIVE_BACKENDS = {}
  40. ACTIVE_BACKEND_CHOICES = []
  41. for cls in BackendBase.__subclasses__():
  42. name = cls.backend_id
  43. assert isinstance(name, str)
  44. if name not in backends_settings:
  45. continue
  46. backend_settings = backends_settings.get(name, {})
  47. for k, v in backend_settings.items():
  48. if hasattr(v, '__call__'):
  49. backend_settings[k] = v()
  50. obj = cls(backend_settings)
  51. if not obj.backend_enabled:
  52. if name in backends_settings:
  53. raise Exception("Invalid settings for payment backend %r" % name)
  54. BACKENDS[name] = obj
  55. BACKEND_CHOICES.append((name, cls.backend_verbose_name))
  56. if obj.backend_enabled:
  57. ACTIVE_BACKENDS[name] = obj
  58. ACTIVE_BACKEND_CHOICES.append((name, cls.backend_verbose_name))
  59. BACKEND_CHOICES = sorted(BACKEND_CHOICES, key=lambda x: x[0])
  60. ACTIVE_BACKEND_CHOICES = sorted(ACTIVE_BACKEND_CHOICES, key=lambda x: x[0])
  61. def period_months(p):
  62. return {
  63. '3m': 3,
  64. '6m': 6,
  65. '12m': 12,
  66. }[p]
  67. class Payment(models.Model):
  68. """ Just a payment.
  69. If subscription is not null, it has been automatically issued.
  70. backend_extid is the external transaction ID, backend_data is other
  71. things that should only be used by the associated backend.
  72. """
  73. user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
  74. backend_id = models.CharField(max_length=16, choices=BACKEND_CHOICES)
  75. status = models.CharField(max_length=16, choices=STATUS_CHOICES, default='new')
  76. created = models.DateTimeField(auto_now_add=True)
  77. modified = models.DateTimeField(auto_now=True)
  78. confirmed_on = models.DateTimeField(null=True, blank=True)
  79. amount = models.IntegerField()
  80. paid_amount = models.IntegerField(default=0)
  81. time = models.DurationField()
  82. subscription = models.ForeignKey('Subscription', null=True, blank=True)
  83. status_message = models.TextField(blank=True, null=True)
  84. backend_extid = models.CharField(max_length=64, null=True, blank=True)
  85. backend_data = JSONField(blank=True)
  86. @property
  87. def currency_code(self):
  88. return CURRENCY_CODE
  89. @property
  90. def currency_name(self):
  91. return CURRENCY_NAME
  92. @property
  93. def backend(self):
  94. """ Returns a global instance of the backend
  95. :rtype: BackendBase
  96. """
  97. return BACKENDS[self.backend_id]
  98. def get_amount_display(self):
  99. return '%.2f %s' % (self.amount / 100, CURRENCY_NAME)
  100. @property
  101. def is_confirmed(self):
  102. return self.status == 'confirmed'
  103. class Meta:
  104. ordering = ('-created', )
  105. @classmethod
  106. def create_payment(self, backend_id, user, months):
  107. payment = Payment(
  108. user=user,
  109. backend_id=backend_id,
  110. status='new',
  111. time=timedelta(days=30 * months),
  112. amount=get_price() * months
  113. )
  114. return payment
  115. class Subscription(models.Model):
  116. """ Recurring payment subscription. """
  117. user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
  118. backend_id = models.CharField(max_length=16, choices=BACKEND_CHOICES)
  119. created = models.DateTimeField(auto_now_add=True)
  120. period = models.CharField(max_length=16, choices=SUBSCR_PERIOD_CHOICES)
  121. last_confirmed_payment = models.DateTimeField(blank=True, null=True)
  122. status = models.CharField(max_length=16, choices=SUBSCR_STATUS_CHOICES, default='new')
  123. backend_extid = models.CharField(max_length=64, null=True, blank=True)
  124. backend_data = JSONField(blank=True)
  125. @property
  126. def backend(self):
  127. """ Returns a global instance of the backend
  128. :rtype: BackendBase
  129. """
  130. return BACKENDS[self.backend_id]
  131. @property
  132. def months(self):
  133. return period_months(self.period)
  134. @property
  135. def period_amount(self):
  136. return self.months * get_price()
  137. @property
  138. def next_renew(self):
  139. """ Approximate date of the next payment """
  140. if self.last_confirmed_payment:
  141. return self.last_confirmed_payment + timedelta(days=self.months * 30)
  142. return self.created + timedelta(days=self.months * 30)
  143. @property
  144. def monthly_amount(self):
  145. return get_price()
  146. def create_payment(self):
  147. payment = Payment(
  148. user=self.user,
  149. backend_id=self.backend_id,
  150. status='new',
  151. time=timedelta(days=30 * self.months),
  152. amount=get_price() * self.months,
  153. subscription=self,
  154. )
  155. return payment