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.

187 lines
5.8 KiB

9 years ago
9 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. def _get_braintree_subscription(self):
  58. if not hasattr(self, "_sub"):
  59. self._sub = braintree.Subscription.find(self.subscription_id)
  60. return self._sub
  61. def get_client_token(self):
  62. assert self.customer_id
  63. return braintree.ClientToken.generate({"customer_id": self.customer_id})
  64. def update_payment_method(self, nonce):
  65. assert self.subscription_id
  66. result = braintree.Subscription.update(
  67. self.subscription_id, {"payment_method_nonce": nonce}
  68. )
  69. if not result.is_success:
  70. return result
  71. def update_address(self, post_data):
  72. # Create customer record if it does not exist:
  73. if not self.customer_id:
  74. result = braintree.Customer.create({"email": self.user.email})
  75. if not result.is_success:
  76. return result
  77. self.customer_id = result.customer.id
  78. self.save()
  79. payload = {key: str(post_data.get(key)) for key in ADDRESS_KEYS}
  80. if self.address_id:
  81. result = braintree.Address.update(
  82. self.customer_id, self.address_id, payload
  83. )
  84. else:
  85. payload["customer_id"] = self.customer_id
  86. result = braintree.Address.create(payload)
  87. if result.is_success:
  88. self.address_id = result.address.id
  89. self.save()
  90. if not result.is_success:
  91. return result
  92. def setup(self, plan_id, nonce):
  93. result = braintree.Subscription.create(
  94. {"payment_method_nonce": nonce, "plan_id": plan_id}
  95. )
  96. if result.is_success:
  97. self.subscription_id = result.subscription.id
  98. self.plan_id = plan_id
  99. if plan_id == "P20":
  100. self.plan_name = "Business ($20 / month)"
  101. elif plan_id == "Y192":
  102. self.plan_name = "Business ($192 / year)"
  103. elif plan_id == "P80":
  104. self.plan_name = "Business Plus ($80 / month)"
  105. elif plan_id == "Y768":
  106. self.plan_name = "Business Plus ($768 / year)"
  107. elif plan_id == "S5":
  108. self.plan_name = "Supporter ($5 / month)"
  109. elif plan_id == "S48":
  110. self.plan_name = "Supporter ($48 / year)"
  111. self.save()
  112. return result
  113. def cancel(self):
  114. if self.subscription_id:
  115. braintree.Subscription.cancel(self.subscription_id)
  116. self.subscription_id = ""
  117. self.plan_id = ""
  118. self.plan_name = ""
  119. self.save()
  120. def pm_is_card(self):
  121. pm = self.payment_method
  122. return isinstance(pm, braintree.credit_card.CreditCard)
  123. def pm_is_paypal(self):
  124. pm = self.payment_method
  125. return isinstance(pm, braintree.paypal_account.PayPalAccount)
  126. def next_billing_date(self):
  127. o = self._get_braintree_subscription()
  128. return o.next_billing_date
  129. @property
  130. def address(self):
  131. if not hasattr(self, "_address"):
  132. try:
  133. self._address = braintree.Address.find(
  134. self.customer_id, self.address_id
  135. )
  136. except braintree.exceptions.NotFoundError:
  137. self._address = None
  138. return self._address
  139. @property
  140. def transactions(self):
  141. if not hasattr(self, "_tx"):
  142. if not self.customer_id:
  143. self._tx = []
  144. else:
  145. self._tx = list(
  146. braintree.Transaction.search(
  147. braintree.TransactionSearch.customer_id == self.customer_id
  148. )
  149. )
  150. return self._tx