forked from GithubBackups/healthchecks
Braintree integration WIP.
This commit is contained in:
parent
53edb555dc
commit
263f50058e
@ -16,6 +16,13 @@ class Subscription(models.Model):
|
|||||||
|
|
||||||
return self._sub
|
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):
|
def is_active(self):
|
||||||
if not self.subscription_id:
|
if not self.subscription_id:
|
||||||
return False
|
return False
|
||||||
@ -30,3 +37,23 @@ class Subscription(models.Model):
|
|||||||
def next_billing_date(self):
|
def next_billing_date(self):
|
||||||
o = self._get_braintree_sub()
|
o = self._get_braintree_sub()
|
||||||
return o.next_billing_date
|
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
|
||||||
|
@ -19,4 +19,8 @@ urlpatterns = [
|
|||||||
views.cancel_plan,
|
views.cancel_plan,
|
||||||
name="hc-cancel-plan"),
|
name="hc-cancel-plan"),
|
||||||
|
|
||||||
|
url(r'^pricing/get_client_token/$',
|
||||||
|
views.get_client_token,
|
||||||
|
name="hc-get-client-token"),
|
||||||
|
|
||||||
]
|
]
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
import braintree
|
import braintree
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib.auth.decorators import login_required
|
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.shortcuts import redirect, render
|
||||||
from django.views.decorators.http import require_POST
|
from django.views.decorators.http import require_POST
|
||||||
|
|
||||||
@ -17,32 +19,55 @@ def setup_braintree():
|
|||||||
braintree.Configuration.configure(settings.BRAINTREE_ENV, **kw)
|
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):
|
def pricing(request):
|
||||||
setup_braintree()
|
setup_braintree()
|
||||||
|
|
||||||
try:
|
sub = None
|
||||||
sub = Subscription.objects.get(user=request.user)
|
if request.user.is_authenticated():
|
||||||
except Subscription.DoesNotExist:
|
try:
|
||||||
sub = Subscription(user=request.user)
|
sub = Subscription.objects.get(user=request.user)
|
||||||
sub.save()
|
except Subscription.DoesNotExist:
|
||||||
|
sub = Subscription(user=request.user)
|
||||||
|
sub.save()
|
||||||
|
|
||||||
ctx = {
|
ctx = {
|
||||||
"page": "pricing",
|
"page": "pricing",
|
||||||
"sub": sub,
|
"sub": sub
|
||||||
"client_token": braintree.ClientToken.generate()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return render(request, "payments/pricing.html", ctx)
|
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
|
@login_required
|
||||||
@require_POST
|
@require_POST
|
||||||
def create_plan(request):
|
def create_plan(request):
|
||||||
|
price = int(request.POST["price"])
|
||||||
|
assert price in (2, 5, 10, 15, 20, 25, 50, 100)
|
||||||
|
|
||||||
setup_braintree()
|
setup_braintree()
|
||||||
sub = Subscription.objects.get(user=request.user)
|
sub = Subscription.objects.get(user=request.user)
|
||||||
if not sub.customer_id:
|
if not sub.customer_id:
|
||||||
result = braintree.Customer.create({})
|
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.customer_id = result.customer.id
|
||||||
sub.save()
|
sub.save()
|
||||||
|
|
||||||
@ -51,19 +76,22 @@ def create_plan(request):
|
|||||||
"customer_id": sub.customer_id,
|
"customer_id": sub.customer_id,
|
||||||
"payment_method_nonce": request.POST["payment_method_nonce"]
|
"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.payment_method_token = result.payment_method.token
|
||||||
sub.save()
|
sub.save()
|
||||||
|
|
||||||
price = int(request.POST["price"])
|
|
||||||
assert price in (2, 5, 10, 15, 20, 25, 50, 100)
|
|
||||||
|
|
||||||
result = braintree.Subscription.create({
|
result = braintree.Subscription.create({
|
||||||
"payment_method_token": sub.payment_method_token,
|
"payment_method_token": sub.payment_method_token,
|
||||||
"plan_id": "P%d" % price,
|
"plan_id": "P%d" % price,
|
||||||
"price": price
|
"price": price
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if not result.is_success:
|
||||||
|
return log_and_bail(request, result)
|
||||||
|
|
||||||
sub.subscription_id = result.subscription.id
|
sub.subscription_id = result.subscription.id
|
||||||
sub.save()
|
sub.save()
|
||||||
|
|
||||||
|
@ -107,4 +107,19 @@
|
|||||||
|
|
||||||
#subscription-status form {
|
#subscription-status form {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#payment-method-modal .modal-header {
|
||||||
|
border-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#payment-method-modal .modal-footer {
|
||||||
|
border-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.error-message {
|
||||||
|
font-family: monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -43,15 +43,14 @@ $(function () {
|
|||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
$("#pww-create-payment-method").click(function(){
|
$("#pww-create-payment-method").click(function() {
|
||||||
var $modal = $("#payment-method-modal");
|
$.getJSON("/pricing/get_client_token/", function(data) {
|
||||||
var clientToken = $modal.attr("data-client-token");
|
var $modal = $("#payment-method-modal");
|
||||||
|
braintree.setup(data.client_token, "dropin", {
|
||||||
braintree.setup(clientToken, "dropin", {
|
container: "payment-form"
|
||||||
container: "payment-form"
|
});
|
||||||
});
|
$modal.modal("show");
|
||||||
|
})
|
||||||
$modal.modal("show");
|
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
@ -8,17 +8,38 @@
|
|||||||
<!-- Plans -->
|
<!-- Plans -->
|
||||||
<section id="plans">
|
<section id="plans">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
|
{% if messages %}
|
||||||
|
<div class="alert alert-danger">
|
||||||
|
<p>
|
||||||
|
<strong>We're sorry!</strong> There was a problem setting
|
||||||
|
up the subscription. Response from payment processor:</p>
|
||||||
|
|
||||||
|
{% for message in messages %}
|
||||||
|
<p class="error-message">{{ message }}</p>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
{% if sub.is_active %}
|
{% if sub.is_active %}
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-12">
|
<div class="col-md-12">
|
||||||
<div id="subscription-status" class="jumbotron">
|
<div id="subscription-status" class="jumbotron">
|
||||||
<p>
|
<p>
|
||||||
You are currently paying <strong>${{ sub.price }}/month</strong>.
|
You are currently paying <strong>${{ sub.price }}/month</strong>
|
||||||
Next billing date will be {{ sub.next_billing_date }}.</p>
|
|
||||||
|
{% 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 %}
|
||||||
|
</p>
|
||||||
<p>
|
<p>
|
||||||
|
Next billing date will be {{ sub.next_billing_date }}.
|
||||||
Thank you for supporting healthchecks.io!
|
Thank you for supporting healthchecks.io!
|
||||||
</p>
|
</p>
|
||||||
<a class="btn btn-default" href="#">Update Payment Method</a>
|
|
||||||
<form method="post" action="{% url 'hc-cancel-plan' %}">
|
<form method="post" action="{% url 'hc-cancel-plan' %}">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
<button type="submit" class="btn btn-default">
|
<button type="submit" class="btn btn-default">
|
||||||
@ -125,22 +146,9 @@
|
|||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
{% else %}
|
{% else %}
|
||||||
{% if sub.payment_method_token %}
|
|
||||||
<form method="post" action="{% url 'hc-create-plan' %}">
|
|
||||||
{% csrf_token %}
|
|
||||||
<input class="selected-price" type="hidden" name="price" value="10" />
|
|
||||||
<button
|
|
||||||
type="submit"
|
|
||||||
class="btn btn-lg btn-default">
|
|
||||||
Select (direct)
|
|
||||||
</button>
|
|
||||||
</form>
|
|
||||||
{% else %}
|
|
||||||
<button
|
<button
|
||||||
id="pww-create-payment-method"
|
id="pww-create-payment-method"
|
||||||
class="btn btn-lg btn-default">Select (form)</button>
|
class="btn btn-lg btn-default">Select</button>
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% else %}
|
{% else %}
|
||||||
<a class="btn btn-lg btn-primary" href="{% url 'hc-login' %}">Get Started</a>
|
<a class="btn btn-lg btn-primary" href="{% url 'hc-login' %}">Get Started</a>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user