forked from GithubBackups/healthchecks
Payments with Braintree, WIP
This commit is contained in:
parent
f08221a1db
commit
7039188482
@ -11,7 +11,6 @@ urlpatterns = [
|
|||||||
url(r'^checks/([\w-]+)/email/$', views.email_preview),
|
url(r'^checks/([\w-]+)/email/$', views.email_preview),
|
||||||
url(r'^checks/([\w-]+)/remove/$', views.remove_check, name="hc-remove-check"),
|
url(r'^checks/([\w-]+)/remove/$', views.remove_check, name="hc-remove-check"),
|
||||||
url(r'^checks/([\w-]+)/log/$', views.log, name="hc-log"),
|
url(r'^checks/([\w-]+)/log/$', views.log, name="hc-log"),
|
||||||
url(r'^pricing/$', views.pricing, name="hc-pricing"),
|
|
||||||
url(r'^docs/$', views.docs, name="hc-docs"),
|
url(r'^docs/$', views.docs, name="hc-docs"),
|
||||||
url(r'^about/$', views.about, name="hc-about"),
|
url(r'^about/$', views.about, name="hc-about"),
|
||||||
url(r'^integrations/$', views.channels, name="hc-channels"),
|
url(r'^integrations/$', views.channels, name="hc-channels"),
|
||||||
|
@ -47,10 +47,6 @@ def index(request):
|
|||||||
return render(request, "front/welcome.html", ctx)
|
return render(request, "front/welcome.html", ctx)
|
||||||
|
|
||||||
|
|
||||||
def pricing(request):
|
|
||||||
return render(request, "front/pricing.html", {"page": "pricing"})
|
|
||||||
|
|
||||||
|
|
||||||
def docs(request):
|
def docs(request):
|
||||||
if "welcome_code" in request.session:
|
if "welcome_code" in request.session:
|
||||||
code = request.session["welcome_code"]
|
code = request.session["welcome_code"]
|
||||||
|
0
hc/payments/__init__.py
Normal file
0
hc/payments/__init__.py
Normal file
8
hc/payments/admin.py
Normal file
8
hc/payments/admin.py
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
from django.contrib import admin
|
||||||
|
from .models import Subscription
|
||||||
|
|
||||||
|
|
||||||
|
@admin.register(Subscription)
|
||||||
|
class SubsAdmin(admin.ModelAdmin):
|
||||||
|
|
||||||
|
list_display = ("id", "user")
|
5
hc/payments/context_processors.py
Normal file
5
hc/payments/context_processors.py
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
from django.conf import settings
|
||||||
|
|
||||||
|
|
||||||
|
def payments(request):
|
||||||
|
return {'USE_PAYMENTS': settings.USE_PAYMENTS}
|
25
hc/payments/migrations/0001_initial.py
Normal file
25
hc/payments/migrations/0001_initial.py
Normal file
@ -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
hc/payments/migrations/__init__.py
Normal file
0
hc/payments/migrations/__init__.py
Normal file
9
hc/payments/models.py
Normal file
9
hc/payments/models.py
Normal file
@ -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)
|
3
hc/payments/tests.py
Normal file
3
hc/payments/tests.py
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
from django.test import TestCase
|
||||||
|
|
||||||
|
# Create your tests here.
|
18
hc/payments/urls.py
Normal file
18
hc/payments/urls.py
Normal file
@ -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"),
|
||||||
|
|
||||||
|
]
|
81
hc/payments/views.py
Normal file
81
hc/payments/views.py
Normal file
@ -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 = 'healthchecks@example.org'
|
DEFAULT_FROM_EMAIL = 'healthchecks@example.org'
|
||||||
|
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'
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -6,4 +6,5 @@ urlpatterns = [
|
|||||||
url(r'^accounts/', include('hc.accounts.urls')),
|
url(r'^accounts/', include('hc.accounts.urls')),
|
||||||
url(r'^', include('hc.api.urls')),
|
url(r'^', include('hc.api.urls')),
|
||||||
url(r'^', include('hc.front.urls')),
|
url(r'^', include('hc.front.urls')),
|
||||||
|
url(r'^', include('hc.payments.urls')),
|
||||||
]
|
]
|
||||||
|
@ -20,7 +20,9 @@
|
|||||||
<link rel="stylesheet" href="{% static 'css/my_checks.css' %}" type="text/css">
|
<link rel="stylesheet" href="{% static 'css/my_checks.css' %}" type="text/css">
|
||||||
<link rel="stylesheet" href="{% static 'css/my_checks_mobile.css' %}" type="text/css">
|
<link rel="stylesheet" href="{% static 'css/my_checks_mobile.css' %}" type="text/css">
|
||||||
<link rel="stylesheet" href="{% static 'css/my_checks_desktop.css' %}" type="text/css">
|
<link rel="stylesheet" href="{% static 'css/my_checks_desktop.css' %}" type="text/css">
|
||||||
|
{% if USE_PAYMENTS %}
|
||||||
<link rel="stylesheet" href="{% static 'css/pricing.css' %}" type="text/css">
|
<link rel="stylesheet" href="{% static 'css/pricing.css' %}" type="text/css">
|
||||||
|
{% endif %}
|
||||||
<link rel="stylesheet" href="{% static 'css/syntax.css' %}" type="text/css">
|
<link rel="stylesheet" href="{% static 'css/syntax.css' %}" type="text/css">
|
||||||
<link rel="stylesheet" href="{% static 'css/channels.css' %}" type="text/css">
|
<link rel="stylesheet" href="{% static 'css/channels.css' %}" type="text/css">
|
||||||
<link rel="stylesheet" href="{% static 'css/channel_checks.css' %}" type="text/css">
|
<link rel="stylesheet" href="{% static 'css/channel_checks.css' %}" type="text/css">
|
||||||
@ -80,9 +82,11 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
|
||||||
|
{% if USE_PAYMENTS %}
|
||||||
<li {% if page == 'pricing' %} class="active" {% endif %}>
|
<li {% if page == 'pricing' %} class="active" {% endif %}>
|
||||||
<a href="{% url 'hc-pricing' %}">Pricing</a>
|
<a href="{% url 'hc-pricing' %}">Pricing</a>
|
||||||
</li>
|
</li>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
<li {% if page == 'docs' %} class="active" {% endif %}>
|
<li {% if page == 'docs' %} class="active" {% endif %}>
|
||||||
<a href="{% url 'hc-docs' %}">Docs</a>
|
<a href="{% url 'hc-docs' %}">Docs</a>
|
||||||
|
23
templates/payments/create_subscription.html
Normal file
23
templates/payments/create_subscription.html
Normal file
@ -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 %}
|
@ -37,5 +37,4 @@
|
|||||||
</section>
|
</section>
|
||||||
<!-- /Plans -->
|
<!-- /Plans -->
|
||||||
|
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
39
templates/payments/status.html
Normal file
39
templates/payments/status.html
Normal file
@ -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 %}
|
Loading…
x
Reference in New Issue
Block a user