diff --git a/hc/payments/models.py b/hc/payments/models.py index 30045204..4a8df4bd 100644 --- a/hc/payments/models.py +++ b/hc/payments/models.py @@ -1,3 +1,4 @@ +import braintree from django.contrib.auth.models import User from django.db import models @@ -7,3 +8,25 @@ class Subscription(models.Model): customer_id = models.CharField(max_length=36, blank=True) payment_method_token = models.CharField(max_length=35, blank=True) subscription_id = models.CharField(max_length=10, blank=True) + + def _get_braintree_sub(self): + if not hasattr(self, "_sub"): + print("getting subscription over network") + self._sub = braintree.Subscription.find(self.subscription_id) + + return self._sub + + def is_active(self): + if not self.subscription_id: + return False + + o = self._get_braintree_sub() + return o.status == "Active" + + def price(self): + o = self._get_braintree_sub() + return int(o.price) + + def next_billing_date(self): + o = self._get_braintree_sub() + return o.next_billing_date diff --git a/hc/payments/urls.py b/hc/payments/urls.py index 500291b3..053acb64 100644 --- a/hc/payments/urls.py +++ b/hc/payments/urls.py @@ -7,12 +7,16 @@ urlpatterns = [ views.pricing, name="hc-pricing"), - url(r'^create_subscription/$', - views.create, - name="hc-create-subscription"), + url(r'^pricing/create_plan/$', + views.create_plan, + name="hc-create-plan"), - url(r'^subscription_status/$', - views.status, - name="hc-subscription-status"), + url(r'^pricing/update_plan/$', + views.update_plan, + name="hc-update-plan"), + + url(r'^pricing/cancel_plan/$', + views.cancel_plan, + name="hc-cancel-plan"), ] diff --git a/hc/payments/views.py b/hc/payments/views.py index e56f36f2..9bf9bde4 100644 --- a/hc/payments/views.py +++ b/hc/payments/views.py @@ -2,6 +2,7 @@ import braintree from django.conf import settings from django.contrib.auth.decorators import login_required from django.shortcuts import redirect, render +from django.views.decorators.http import require_POST from .models import Subscription @@ -17,29 +18,35 @@ def setup_braintree(): def pricing(request): + setup_braintree() + + 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() } + return render(request, "payments/pricing.html", ctx) @login_required -def create(request): +@require_POST +def create_plan(request): setup_braintree() - - try: - sub = Subscription.objects.get(user=request.user) - except Subscription.DoesNotExist: - sub = Subscription(user=request.user) + sub = Subscription.objects.get(user=request.user) + if not sub.customer_id: + result = braintree.Customer.create({}) + assert result.is_success + sub.customer_id = result.customer.id sub.save() - if request.method == "POST": - if not sub.customer_id: - result = braintree.Customer.create({}) - assert result.is_success - sub.customer_id = result.customer.id - sub.save() - + if "payment_method_nonce" in request.POST: result = braintree.PaymentMethod.create({ "customer_id": sub.customer_id, "payment_method_nonce": request.POST["payment_method_nonce"] @@ -48,34 +55,47 @@ def create(request): sub.payment_method_token = result.payment_method.token sub.save() - result = braintree.Subscription.create({ - "payment_method_token": sub.payment_method_token, - "plan_id": "pww", - "price": 5 - }) + price = int(request.POST["price"]) + assert price in (2, 5, 10, 15, 20, 25, 50, 100) - sub.subscription_id = result.subscription.id - sub.save() + result = braintree.Subscription.create({ + "payment_method_token": sub.payment_method_token, + "plan_id": "P%d" % price, + "price": price + }) - return redirect("hc-subscription-status") + sub.subscription_id = result.subscription.id + sub.save() - ctx = { - "page": "pricing", - "client_token": braintree.ClientToken.generate() - } - return render(request, "payments/create_subscription.html", ctx) + return redirect("hc-pricing") @login_required -def status(request): +@require_POST +def update_plan(request): setup_braintree() - sub = Subscription.objects.get(user=request.user) - subscription = braintree.Subscription.find(sub.subscription_id) - ctx = { - "page": "pricing", - "subscription": subscription + price = int(request.POST["price"]) + assert price in (2, 5, 10, 15, 20, 25, 50, 100) + + fields = { + "plan_id": "P%s" % price, + "price": price } - return render(request, "payments/status.html", ctx) + braintree.Subscription.update(sub.subscription_id, fields) + return redirect("hc-pricing") + + +@login_required +@require_POST +def cancel_plan(request): + setup_braintree() + sub = Subscription.objects.get(user=request.user) + + braintree.Subscription.cancel(sub.subscription_id) + sub.subscription_id = "" + sub.save() + + return redirect("hc-pricing") diff --git a/static/css/pricing.css b/static/css/pricing.css index 5bcd2eab..648a4737 100644 --- a/static/css/pricing.css +++ b/static/css/pricing.css @@ -100,3 +100,11 @@ -webkit-animation-name: tadaIn; animation-name: tadaIn; } + +#pww-switch-btn { + display: none; +} + +#subscription-status form { + display: inline-block; +} \ No newline at end of file diff --git a/static/js/pricing.js b/static/js/pricing.js index 22ceb3a1..b20d7511 100644 --- a/static/js/pricing.js +++ b/static/js/pricing.js @@ -1,13 +1,28 @@ $(function () { - var prices = [2, 5, 10, 15, 20, 25, 50, 75, 100, 125, 150, 175, 200]; - var priceIdx = 2; + var prices = [2, 5, 10, 15, 20, 25, 50, 100]; + var initialPrice = parseInt($("#pricing-value").text()); + var priceIdx = prices.indexOf(initialPrice); + + function updateDisplayPrice(price) { + $("#pricing-value").text(price); + $(".selected-price").val(price); + $("#pww-switch-btn").text("Switch to $" + price + " / mo"); + + if (price == initialPrice) { + $("#pww-selected-btn").show(); + $("#pww-switch-btn").hide(); + } else { + $("#pww-selected-btn").hide(); + $("#pww-switch-btn").show(); + } + } $("#pay-plus").click(function() { - if (priceIdx >= 12) + if (priceIdx > 6) return; priceIdx += 1; - $("#pricing-value").text(prices[priceIdx]); + updateDisplayPrice(prices[priceIdx]); $("#piggy").removeClass().addClass("tada animated").one('webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend', function(){ $(this).removeClass(); @@ -20,7 +35,7 @@ $(function () { return; priceIdx -= 1; - $("#pricing-value").text(prices[priceIdx]); + updateDisplayPrice(prices[priceIdx]); $("#piggy").removeClass().addClass("tadaIn animated").one('webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend', function(){ $(this).removeClass(); @@ -28,5 +43,15 @@ $(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"); + }); }); \ No newline at end of file diff --git a/templates/payments/create_subscription.html b/templates/payments/create_subscription.html deleted file mode 100644 index 0e6065fc..00000000 --- a/templates/payments/create_subscription.html +++ /dev/null @@ -1,23 +0,0 @@ -{% extends "base.html" %} -{% load staticfiles %} - -{% block title %}Pricing - It's Free! - healthchecks.io{% endblock %} - -{% block content %} - -
- - - - -{% endblock %} \ No newline at end of file diff --git a/templates/payments/pricing.html b/templates/payments/pricing.html index 920f4917..b355f6a4 100644 --- a/templates/payments/pricing.html +++ b/templates/payments/pricing.html @@ -8,17 +8,40 @@+ You are currently paying ${{ sub.price }}/month. + Next billing date will be {{ sub.next_billing_date }}.
++ Thank you for supporting healthchecks.io! +
+ Update Payment Method + +€0 / Month
+$0 / Month
- €10 / Month + + {% if sub.is_active %} + ${{ sub.price }} / Month + {% else %} + $10 / Month + {% endif %} +
Date | -Amount | -
---|---|
??? | -{{ tx.amount }} | -