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.

202 lines
6.1 KiB

9 years ago
9 years ago
5 years ago
  1. from django.conf import settings
  2. from django.contrib.auth.models import User
  3. from django.db import models
  4. if settings.USE_PAYMENTS:
  5. import braintree
  6. else:
  7. # hc.payments tests mock this object, so tests should
  8. # still be able to run:
  9. braintree = None
  10. ADDRESS_KEYS = (
  11. "company",
  12. "street_address",
  13. "extended_address",
  14. "locality",
  15. "region",
  16. "postal_code",
  17. "country_code_alpha2",
  18. )
  19. class SubscriptionManager(models.Manager):
  20. def for_user(self, user):
  21. sub, created = Subscription.objects.get_or_create(user_id=user.id)
  22. return sub
  23. def by_transaction(self, transaction_id):
  24. try:
  25. tx = braintree.Transaction.find(transaction_id)
  26. except braintree.exceptions.NotFoundError:
  27. return None, None
  28. try:
  29. sub = self.get(customer_id=tx.customer_details.id)
  30. except Subscription.DoesNotExist:
  31. return None, None
  32. return sub, tx
  33. def by_braintree_webhook(self, request):
  34. sig = str(request.POST["bt_signature"])
  35. payload = str(request.POST["bt_payload"])
  36. doc = braintree.WebhookNotification.parse(sig, payload)
  37. assert doc.kind == "subscription_charged_successfully"
  38. sub = self.get(subscription_id=doc.subscription.id)
  39. return sub, doc.subscription.transactions[0]
  40. class Subscription(models.Model):
  41. user = models.OneToOneField(User, models.CASCADE, blank=True, null=True)
  42. customer_id = models.CharField(max_length=36, blank=True)
  43. payment_method_token = models.CharField(max_length=35, blank=True)
  44. subscription_id = models.CharField(max_length=10, blank=True)
  45. plan_id = models.CharField(max_length=10, blank=True)
  46. plan_name = models.CharField(max_length=50, blank=True)
  47. address_id = models.CharField(max_length=2, blank=True)
  48. send_invoices = models.BooleanField(default=True)
  49. invoice_email = models.EmailField(blank=True)
  50. objects = SubscriptionManager()
  51. @property
  52. def payment_method(self):
  53. if not hasattr(self, "_pm"):
  54. o = self._get_braintree_subscription()
  55. self._pm = braintree.PaymentMethod.find(o.payment_method_token)
  56. return self._pm
  57. @property
  58. def is_supporter(self):
  59. return self.plan_id in ("S5", "S48")
  60. @property
  61. def is_business(self):
  62. return self.plan_id in ("P20", "Y192")
  63. @property
  64. def is_business_plus(self):
  65. return self.plan_id in ("P80", "Y768")
  66. def is_annual(self):
  67. return self.plan_id in ("S48", "Y192", "Y768")
  68. def _get_braintree_subscription(self):
  69. if not hasattr(self, "_sub"):
  70. self._sub = braintree.Subscription.find(self.subscription_id)
  71. return self._sub
  72. def get_client_token(self):
  73. assert self.customer_id
  74. return braintree.ClientToken.generate({"customer_id": self.customer_id})
  75. def update_payment_method(self, nonce):
  76. assert self.subscription_id
  77. result = braintree.Subscription.update(
  78. self.subscription_id, {"payment_method_nonce": nonce}
  79. )
  80. if not result.is_success:
  81. return result
  82. def update_address(self, post_data):
  83. # Create customer record if it does not exist:
  84. if not self.customer_id:
  85. result = braintree.Customer.create({"email": self.user.email})
  86. if not result.is_success:
  87. return result
  88. self.customer_id = result.customer.id
  89. self.save()
  90. payload = {key: str(post_data.get(key)) for key in ADDRESS_KEYS}
  91. if self.address_id:
  92. result = braintree.Address.update(
  93. self.customer_id, self.address_id, payload
  94. )
  95. else:
  96. payload["customer_id"] = self.customer_id
  97. result = braintree.Address.create(payload)
  98. if result.is_success:
  99. self.address_id = result.address.id
  100. self.save()
  101. if not result.is_success:
  102. return result
  103. def setup(self, plan_id, nonce):
  104. result = braintree.Subscription.create(
  105. {"payment_method_nonce": nonce, "plan_id": plan_id}
  106. )
  107. if result.is_success:
  108. self.subscription_id = result.subscription.id
  109. self.plan_id = plan_id
  110. if plan_id == "P20":
  111. self.plan_name = "Business ($20 / month)"
  112. elif plan_id == "Y192":
  113. self.plan_name = "Business ($192 / year)"
  114. elif plan_id == "P80":
  115. self.plan_name = "Business Plus ($80 / month)"
  116. elif plan_id == "Y768":
  117. self.plan_name = "Business Plus ($768 / year)"
  118. elif plan_id == "S5":
  119. self.plan_name = "Supporter ($5 / month)"
  120. elif plan_id == "S48":
  121. self.plan_name = "Supporter ($48 / year)"
  122. self.save()
  123. return result
  124. def cancel(self):
  125. if self.subscription_id:
  126. braintree.Subscription.cancel(self.subscription_id)
  127. self.subscription_id = ""
  128. self.plan_id = ""
  129. self.plan_name = ""
  130. self.save()
  131. def pm_is_card(self):
  132. pm = self.payment_method
  133. return isinstance(pm, braintree.credit_card.CreditCard)
  134. def pm_is_paypal(self):
  135. pm = self.payment_method
  136. return isinstance(pm, braintree.paypal_account.PayPalAccount)
  137. def next_billing_date(self):
  138. o = self._get_braintree_subscription()
  139. return o.next_billing_date
  140. @property
  141. def address(self):
  142. if not hasattr(self, "_address"):
  143. try:
  144. self._address = braintree.Address.find(
  145. self.customer_id, self.address_id
  146. )
  147. except braintree.exceptions.NotFoundError:
  148. self._address = None
  149. return self._address
  150. @property
  151. def transactions(self):
  152. if not hasattr(self, "_tx"):
  153. if not self.customer_id:
  154. self._tx = []
  155. else:
  156. self._tx = list(
  157. braintree.Transaction.search(
  158. braintree.TransactionSearch.customer_id == self.customer_id
  159. )
  160. )
  161. return self._tx