@ -0,0 +1,8 @@ | |||||
from django.contrib import admin | |||||
from .models import Subscription | |||||
@admin.register(Subscription) | |||||
class SubsAdmin(admin.ModelAdmin): | |||||
list_display = ("id", "user") |
@ -0,0 +1,5 @@ | |||||
from django.conf import settings | |||||
def payments(request): | |||||
return {'USE_PAYMENTS': settings.USE_PAYMENTS} |
@ -0,0 +1,25 @@ | |||||
# -*- coding: utf-8 -*- | |||||
from __future__ import unicode_literals | |||||
from django.db import migrations, models | |||||
from django.conf import settings | |||||
class Migration(migrations.Migration): | |||||
dependencies = [ | |||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL), | |||||
] | |||||
operations = [ | |||||
migrations.CreateModel( | |||||
name='Subscription', | |||||
fields=[ | |||||
('id', models.AutoField(serialize=False, primary_key=True, auto_created=True, verbose_name='ID')), | |||||
('customer_id', models.CharField(blank=True, max_length=36)), | |||||
('payment_method_token', models.CharField(blank=True, max_length=35)), | |||||
('subscription_id', models.CharField(blank=True, max_length=10)), | |||||
('user', models.OneToOneField(blank=True, null=True, to=settings.AUTH_USER_MODEL)), | |||||
], | |||||
), | |||||
] |
@ -0,0 +1,9 @@ | |||||
from django.contrib.auth.models import User | |||||
from django.db import models | |||||
class Subscription(models.Model): | |||||
user = models.OneToOneField(User, blank=True, null=True) | |||||
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) |
@ -0,0 +1,3 @@ | |||||
from django.test import TestCase | |||||
# Create your tests here. |
@ -0,0 +1,18 @@ | |||||
from django.conf.urls import url | |||||
from . import views | |||||
urlpatterns = [ | |||||
url(r'^pricing/$', | |||||
views.pricing, | |||||
name="hc-pricing"), | |||||
url(r'^create_subscription/$', | |||||
views.create, | |||||
name="hc-create-subscription"), | |||||
url(r'^subscription_status/$', | |||||
views.status, | |||||
name="hc-subscription-status"), | |||||
] |
@ -0,0 +1,81 @@ | |||||
import braintree | |||||
from django.conf import settings | |||||
from django.contrib.auth.decorators import login_required | |||||
from django.shortcuts import redirect, render | |||||
from .models import Subscription | |||||
def setup_braintree(): | |||||
kw = { | |||||
"merchant_id": settings.BRAINTREE_MERCHANT_ID, | |||||
"public_key": settings.BRAINTREE_PUBLIC_KEY, | |||||
"private_key": settings.BRAINTREE_PRIVATE_KEY | |||||
} | |||||
braintree.Configuration.configure(settings.BRAINTREE_ENV, **kw) | |||||
def pricing(request): | |||||
ctx = { | |||||
"page": "pricing", | |||||
} | |||||
return render(request, "payments/pricing.html", ctx) | |||||
@login_required | |||||
def create(request): | |||||
setup_braintree() | |||||
try: | |||||
sub = Subscription.objects.get(user=request.user) | |||||
except Subscription.DoesNotExist: | |||||
sub = Subscription(user=request.user) | |||||
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() | |||||
result = braintree.PaymentMethod.create({ | |||||
"customer_id": sub.customer_id, | |||||
"payment_method_nonce": request.POST["payment_method_nonce"] | |||||
}) | |||||
assert result.is_success | |||||
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 | |||||
}) | |||||
sub.subscription_id = result.subscription.id | |||||
sub.save() | |||||
return redirect("hc-subscription-status") | |||||
ctx = { | |||||
"page": "pricing", | |||||
"client_token": braintree.ClientToken.generate() | |||||
} | |||||
return render(request, "payments/create_subscription.html", ctx) | |||||
@login_required | |||||
def status(request): | |||||
setup_braintree() | |||||
sub = Subscription.objects.get(user=request.user) | |||||
subscription = braintree.Subscription.find(sub.subscription_id) | |||||
ctx = { | |||||
"page": "pricing", | |||||
"subscription": subscription | |||||
} | |||||
return render(request, "payments/status.html", ctx) |
@ -20,6 +20,7 @@ SECRET_KEY = "---" | |||||
DEBUG = True | DEBUG = True | ||||
ALLOWED_HOSTS = [] | ALLOWED_HOSTS = [] | ||||
DEFAULT_FROM_EMAIL = '[email protected]' | DEFAULT_FROM_EMAIL = '[email protected]' | ||||
USE_PAYMENTS = False | |||||
INSTALLED_APPS = ( | INSTALLED_APPS = ( | ||||
@ -35,7 +36,8 @@ INSTALLED_APPS = ( | |||||
'hc.accounts', | 'hc.accounts', | ||||
'hc.api', | 'hc.api', | ||||
'hc.front' | |||||
'hc.front', | |||||
'hc.payments' | |||||
) | ) | ||||
MIDDLEWARE_CLASSES = ( | MIDDLEWARE_CLASSES = ( | ||||
@ -62,6 +64,7 @@ TEMPLATES = [ | |||||
'django.template.context_processors.request', | 'django.template.context_processors.request', | ||||
'django.contrib.auth.context_processors.auth', | 'django.contrib.auth.context_processors.auth', | ||||
'django.contrib.messages.context_processors.messages', | 'django.contrib.messages.context_processors.messages', | ||||
'hc.payments.context_processors.payments' | |||||
], | ], | ||||
}, | }, | ||||
}, | }, | ||||
@ -0,0 +1,23 @@ | |||||
{% extends "base.html" %} | |||||
{% load staticfiles %} | |||||
{% block title %}Pricing - It's Free! - healthchecks.io{% endblock %} | |||||
{% block content %} | |||||
<form id="checkout" method="post" action="{% url 'hc-create-subscription' %}"> | |||||
{% csrf_token %} | |||||
<div id="payment-form"></div> | |||||
<input class="btn" type="submit" value="Set Up Subscription"> | |||||
</form> | |||||
<script src="https://js.braintreegateway.com/v2/braintree.js"></script> | |||||
<script> | |||||
var clientToken = "{{ client_token }}"; | |||||
braintree.setup(clientToken, "dropin", { | |||||
container: "payment-form" | |||||
}); | |||||
</script> | |||||
{% endblock %} |
@ -0,0 +1,39 @@ | |||||
{% extends "base.html" %} | |||||
{% load staticfiles %} | |||||
{% block title %}Pricing - It's Free! - healthchecks.io{% endblock %} | |||||
{% block content %} | |||||
<h1>Subscription Status</h1> | |||||
<dl> | |||||
<dt>Status</dt> | |||||
<dd>{{ subscription.status }}</dd> | |||||
<dt>Next Billing Date:</dt> | |||||
<dd>{{ subscription.next_billing_date }}</dd> | |||||
<dt>Amount</dt> | |||||
<dd>{{ subscription.price }} / Month</dd> | |||||
</dl> | |||||
<h2>Transactions</h2> | |||||
<table class="table"> | |||||
<tr> | |||||
<th>Date</th> | |||||
<th>Amount</th> | |||||
</tr> | |||||
{% for tx in subscription.transactions %} | |||||
<tr> | |||||
<td>???</td> | |||||
<td>{{ tx.amount }}</td> | |||||
</tr> | |||||
{% endfor %} | |||||
</table> | |||||
{% endblock %} |