diff --git a/hc/payments/models.py b/hc/payments/models.py index 4a8df4bd..1ed8dbe9 100644 --- a/hc/payments/models.py +++ b/hc/payments/models.py @@ -16,6 +16,13 @@ class Subscription(models.Model): return self._sub + def _get_braintree_payment_method(self): + if not hasattr(self, "_pm"): + print("getting payment method over network") + self._pm = braintree.PaymentMethod.find(self.payment_method_token) + + return self._pm + def is_active(self): if not self.subscription_id: return False @@ -30,3 +37,23 @@ class Subscription(models.Model): def next_billing_date(self): o = self._get_braintree_sub() return o.next_billing_date + + def pm_is_credit_card(self): + return isinstance(self._get_braintree_payment_method(), + braintree.credit_card.CreditCard) + + def pm_is_paypal(self): + return isinstance(self._get_braintree_payment_method(), + braintree.paypal_account.PayPalAccount) + + def card_type(self): + o = self._get_braintree_payment_method() + return o.card_type + + def last_4(self): + o = self._get_braintree_payment_method() + return o.last_4 + + def paypal_email(self): + o = self._get_braintree_payment_method() + return o.email diff --git a/hc/payments/urls.py b/hc/payments/urls.py index 053acb64..07863d14 100644 --- a/hc/payments/urls.py +++ b/hc/payments/urls.py @@ -19,4 +19,8 @@ urlpatterns = [ views.cancel_plan, name="hc-cancel-plan"), + url(r'^pricing/get_client_token/$', + views.get_client_token, + name="hc-get-client-token"), + ] diff --git a/hc/payments/views.py b/hc/payments/views.py index 9bf9bde4..f49911b8 100644 --- a/hc/payments/views.py +++ b/hc/payments/views.py @@ -1,6 +1,8 @@ import braintree from django.conf import settings from django.contrib.auth.decorators import login_required +from django.contrib import messages +from django.http import JsonResponse from django.shortcuts import redirect, render from django.views.decorators.http import require_POST @@ -17,32 +19,55 @@ def setup_braintree(): braintree.Configuration.configure(settings.BRAINTREE_ENV, **kw) +@login_required +def get_client_token(request): + sub = Subscription.objects.get(user=request.user) + client_token = braintree.ClientToken.generate({ + "customer_id": sub.customer_id + }) + + return JsonResponse({"client_token": client_token}) + + def pricing(request): setup_braintree() - try: - sub = Subscription.objects.get(user=request.user) - except Subscription.DoesNotExist: - sub = Subscription(user=request.user) - sub.save() + sub = None + if request.user.is_authenticated(): + try: + sub = Subscription.objects.get(user=request.user) + except Subscription.DoesNotExist: + sub = Subscription(user=request.user) + sub.save() ctx = { "page": "pricing", - "sub": sub, - "client_token": braintree.ClientToken.generate() + "sub": sub } return render(request, "payments/pricing.html", ctx) +def log_and_bail(request, result): + for error in result.errors.deep_errors: + messages.error(request, error.message) + + return redirect("hc-pricing") + + @login_required @require_POST def create_plan(request): + price = int(request.POST["price"]) + assert price in (2, 5, 10, 15, 20, 25, 50, 100) + setup_braintree() sub = Subscription.objects.get(user=request.user) if not sub.customer_id: result = braintree.Customer.create({}) - assert result.is_success + if not result.is_success: + return log_and_bail(request, result) + sub.customer_id = result.customer.id sub.save() @@ -51,19 +76,22 @@ def create_plan(request): "customer_id": sub.customer_id, "payment_method_nonce": request.POST["payment_method_nonce"] }) - assert result.is_success + + if not result.is_success: + return log_and_bail(request, result) + sub.payment_method_token = result.payment_method.token sub.save() - price = int(request.POST["price"]) - assert price in (2, 5, 10, 15, 20, 25, 50, 100) - result = braintree.Subscription.create({ "payment_method_token": sub.payment_method_token, "plan_id": "P%d" % price, "price": price }) + if not result.is_success: + return log_and_bail(request, result) + sub.subscription_id = result.subscription.id sub.save() diff --git a/static/css/pricing.css b/static/css/pricing.css index 648a4737..2b89e338 100644 --- a/static/css/pricing.css +++ b/static/css/pricing.css @@ -107,4 +107,19 @@ #subscription-status form { display: inline-block; -} \ No newline at end of file +} + +#payment-method-modal .modal-header { + border-bottom: 0; +} + +#payment-method-modal .modal-footer { + border-top: 0; +} + + +.error-message { + font-family: monospace; +} + + diff --git a/static/js/pricing.js b/static/js/pricing.js index b20d7511..28817274 100644 --- a/static/js/pricing.js +++ b/static/js/pricing.js @@ -43,15 +43,14 @@ $(function () { }); - $("#pww-create-payment-method").click(function(){ - var $modal = $("#payment-method-modal"); - var clientToken = $modal.attr("data-client-token"); - - braintree.setup(clientToken, "dropin", { - container: "payment-form" - }); - - $modal.modal("show"); + $("#pww-create-payment-method").click(function() { + $.getJSON("/pricing/get_client_token/", function(data) { + var $modal = $("#payment-method-modal"); + braintree.setup(data.client_token, "dropin", { + container: "payment-form" + }); + $modal.modal("show"); + }) }); }); \ No newline at end of file diff --git a/templates/payments/pricing.html b/templates/payments/pricing.html index b355f6a4..9ebb18fa 100644 --- a/templates/payments/pricing.html +++ b/templates/payments/pricing.html @@ -8,17 +8,38 @@
+ {% if messages %} +
+

+ We're sorry! There was a problem setting + up the subscription. Response from payment processor:

+ + {% for message in messages %} +

{{ message }}

+ {% endfor %} +
+ {% endif %} + {% if sub.is_active %}

- You are currently paying ${{ sub.price }}/month. - Next billing date will be {{ sub.next_billing_date }}.

+ You are currently paying ${{ sub.price }}/month + + {% if sub.pm_is_credit_card %} + using {{ sub.card_type }} card + ending with {{ sub.last_4 }}. + {% endif %} + + {% if sub.pm_is_paypal %} + using PayPal account {{ sub.paypal_email }}. + {% endif %} +

+ Next billing date will be {{ sub.next_billing_date }}. Thank you for supporting healthchecks.io!

- Update Payment Method
{% csrf_token %}
{% else %} - {% if sub.payment_method_token %} -
- {% csrf_token %} - - -
- {% else %} - {% endif %} - + class="btn btn-lg btn-default">Select {% endif %} {% else %} Get Started