Team billing info and ability to cancel subscription.
parent
79d077def9
commit
4127ba79b1
|
@ -23,6 +23,14 @@ jQuery(function($) {
|
||||||
var pre = $pres.eq(i);
|
var pre = $pres.eq(i);
|
||||||
pre.width(pre.parents('section.code').width() - 30);
|
pre.width(pre.parents('section.code').width() - 30);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$('form#cancel-team-subscription').submit(function() {
|
||||||
|
if (confirm('Are you sure you want to cancel your subscription?\n\nYou will no longer be able to create new Snipts under this team. This action is effective immediately and we unfortunately cannot issue any refunds.')) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// Angular app init.
|
// Angular app init.
|
||||||
|
|
|
@ -23,6 +23,7 @@ if (typeof angular !== 'undefined') {
|
||||||
// Controllers.
|
// Controllers.
|
||||||
controllers.TeamController = function($scope, $timeout, TeamStorage) {
|
controllers.TeamController = function($scope, $timeout, TeamStorage) {
|
||||||
$scope.users = [];
|
$scope.users = [];
|
||||||
|
$scope.search = '';
|
||||||
$scope.$watch('search', function(val) {
|
$scope.$watch('search', function(val) {
|
||||||
$timeout.cancel($scope.timeout);
|
$timeout.cancel($scope.timeout);
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,11 @@
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
{% if 'team-cancelled' in request.GET %}
|
||||||
|
<div class="alert alert-success" style="margin: 30px;">
|
||||||
|
Your team plan has been succesfully cancelled.
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
<section class="snipts" id="snipts" ng-controller="SniptListController"
|
<section class="snipts" id="snipts" ng-controller="SniptListController"
|
||||||
{% if request.user.profile.list_view == 'C' %}
|
{% if request.user.profile.list_view == 'C' %}
|
||||||
ng-cloak ng-show="$root.account.id"
|
ng-cloak ng-show="$root.account.id"
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% load team_tags %}
|
||||||
|
|
||||||
{% block page-title %}Team Billing{% endblock %}
|
{% block page-title %}Team Billing{% endblock %}
|
||||||
|
|
||||||
{% block body-class %}account {{ block.super }}{% endblock %}
|
{% block body-class %}account {{ block.super }}{% endblock %}
|
||||||
|
@ -28,26 +30,29 @@
|
||||||
</aside>
|
</aside>
|
||||||
<section class="content">
|
<section class="content">
|
||||||
<div class="def" data-title="Plan">
|
<div class="def" data-title="Plan">
|
||||||
25 users
|
{{ name }}
|
||||||
</div>
|
</div>
|
||||||
<div class="def" data-title="Price">
|
<div class="def" data-title="Price">
|
||||||
$49.00 USD / month
|
${{ amount|currency_convert }}.00 USD / {{ interval }}
|
||||||
</div>
|
</div>
|
||||||
<div class="def" data-title="Card">
|
<div class="def" data-title="Card">
|
||||||
xxxx-xxxx-xxxx-4242
|
xxxx-xxxx-xxxx-{{ last4 }}
|
||||||
</div>
|
</div>
|
||||||
<div class="def" data-title="Status">
|
<div class="def" data-title="Status">
|
||||||
Active
|
{{ status }}
|
||||||
</div>
|
</div>
|
||||||
<div class="def" data-title="Team since">
|
<div class="def" data-title="Team since">
|
||||||
August 29, 2015
|
{{ team.created|date:'M d, Y' }}
|
||||||
</div>
|
</div>
|
||||||
<div class="def" data-title="Next bill date" ng-show="user.stripeAccount.status != 'inactive'">
|
<div class="def" data-title="Next bill date" ng-show="user.stripeAccount.status != 'inactive'">
|
||||||
November 29, 2015
|
{{ nextBill|to_date|date:'M d, Y' }}
|
||||||
</div>
|
</div>
|
||||||
<p class="alert alert-info group" style="padding-right: 8px;">
|
<form class="alert alert-info group" style="margin: 15px; padding-right: 8px;" id="cancel-team-subscription" action="/{{ team.slug }}/billing/cancel/" method="post">
|
||||||
<a href="/{{ team.slug }}/billing/cancel/" class="btn btn-danger pull-right">Cancel subscription</a>
|
{% csrf_token %}
|
||||||
</p>
|
<button type="submit" class="btn btn-danger pull-right">
|
||||||
|
Cancel subscription
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
</section>
|
</section>
|
||||||
</section>
|
</section>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import datetime
|
||||||
|
|
||||||
from django import template
|
from django import template
|
||||||
|
|
||||||
register = template.Library()
|
register = template.Library()
|
||||||
|
@ -6,3 +8,13 @@ register = template.Library()
|
||||||
@register.filter
|
@register.filter
|
||||||
def user_is_member(team, user):
|
def user_is_member(team, user):
|
||||||
return team.user_is_member(user)
|
return team.user_is_member(user)
|
||||||
|
|
||||||
|
|
||||||
|
@register.filter
|
||||||
|
def currency_convert(amount):
|
||||||
|
return amount / 100
|
||||||
|
|
||||||
|
|
||||||
|
@register.filter
|
||||||
|
def to_date(timestamp):
|
||||||
|
return datetime.datetime.fromtimestamp(float(timestamp))
|
||||||
|
|
|
@ -17,4 +17,7 @@ urlpatterns = \
|
||||||
name='team-members'),
|
name='team-members'),
|
||||||
url(r'^(?P<username>[^/]+)/billing/$',
|
url(r'^(?P<username>[^/]+)/billing/$',
|
||||||
views.team_billing,
|
views.team_billing,
|
||||||
name='team-billing'))
|
name='team-billing'),
|
||||||
|
url(r'^(?P<username>[^/]+)/billing/cancel/$',
|
||||||
|
views.cancel_team_subscription,
|
||||||
|
name='team-cancel-subscription'))
|
||||||
|
|
112
teams/views.py
112
teams/views.py
|
@ -20,12 +20,76 @@ def for_teams(request):
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
@render_to('teams/for-teams-complete.html')
|
||||||
|
def for_teams_complete(request):
|
||||||
|
if request.method == 'POST' and request.user.is_authenticated():
|
||||||
|
|
||||||
|
token = request.POST['token']
|
||||||
|
stripe.api_key = os.environ.get('STRIPE_SECRET_KEY',
|
||||||
|
settings.STRIPE_SECRET_KEY)
|
||||||
|
|
||||||
|
plan = request.POST['plan']
|
||||||
|
|
||||||
|
try:
|
||||||
|
customer = stripe.Customer.create(card=token,
|
||||||
|
plan=plan,
|
||||||
|
email=request.user.email)
|
||||||
|
except stripe.CardError, e:
|
||||||
|
error_message = e.json_body['error']['message']
|
||||||
|
return HttpResponseRedirect('/for-teams/?declined=%s' %
|
||||||
|
error_message or
|
||||||
|
'Your card was declined.')
|
||||||
|
|
||||||
|
team = Team(name=request.POST['team-name'],
|
||||||
|
email=request.POST['email'],
|
||||||
|
plan=plan,
|
||||||
|
owner=request.user)
|
||||||
|
team.stripe_id = customer.id
|
||||||
|
team.save()
|
||||||
|
|
||||||
|
user = User.objects.create_user(team.slug,
|
||||||
|
team.email,
|
||||||
|
str(uuid.uuid4()))
|
||||||
|
|
||||||
|
team.user = user
|
||||||
|
team.save()
|
||||||
|
|
||||||
|
return {
|
||||||
|
'team': team
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
return HttpResponseBadRequest()
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
@render_to('teams/team-billing.html')
|
@render_to('teams/team-billing.html')
|
||||||
def team_billing(request, username):
|
def team_billing(request, username):
|
||||||
team = get_object_or_404(Team, slug=username, disabled=False)
|
team = get_object_or_404(Team, slug=username, disabled=False)
|
||||||
if team.owner != request.user:
|
if team.owner != request.user:
|
||||||
raise Http404
|
raise Http404
|
||||||
|
|
||||||
|
stripe.api_key = os.environ.get('STRIPE_SECRET_KEY',
|
||||||
|
settings.STRIPE_SECRET_KEY)
|
||||||
|
customer = stripe.Customer.retrieve(team.stripe_id)
|
||||||
|
|
||||||
|
data = {
|
||||||
|
'last4': customer.active_card.last4,
|
||||||
|
'created': customer.created,
|
||||||
|
'email': customer.email,
|
||||||
|
'team': team
|
||||||
|
}
|
||||||
|
|
||||||
|
if customer.subscription:
|
||||||
|
data['amount'] = customer.subscription.plan.amount
|
||||||
|
data['interval'] = customer.subscription.plan.interval
|
||||||
|
data['name'] = customer.subscription.plan.name
|
||||||
|
data['status'] = customer.subscription.status
|
||||||
|
data['nextBill'] = customer.subscription.current_period_end
|
||||||
|
else:
|
||||||
|
data['status'] = 'inactive'
|
||||||
|
|
||||||
|
return data
|
||||||
return {
|
return {
|
||||||
'team': team
|
'team': team
|
||||||
}
|
}
|
||||||
|
@ -72,42 +136,22 @@ def remove_team_member(request, username, member):
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
@render_to('teams/for-teams-complete.html')
|
def cancel_team_subscription(request, username):
|
||||||
def for_teams_complete(request):
|
|
||||||
if request.method == 'POST' and request.user.is_authenticated():
|
|
||||||
|
|
||||||
token = request.POST['token']
|
if request.method != 'POST':
|
||||||
stripe.api_key = os.environ.get('STRIPE_SECRET_KEY',
|
raise Http404
|
||||||
settings.STRIPE_SECRET_KEY)
|
|
||||||
|
|
||||||
plan = request.POST['plan']
|
team = get_object_or_404(Team, slug=username, disabled=False)
|
||||||
|
if team.owner != request.user:
|
||||||
|
raise Http404
|
||||||
|
|
||||||
try:
|
stripe.api_key = os.environ.get('STRIPE_SECRET_KEY',
|
||||||
customer = stripe.Customer.create(card=token,
|
settings.STRIPE_SECRET_KEY)
|
||||||
plan=plan,
|
customer = stripe.Customer.retrieve(team.stripe_id)
|
||||||
email=request.user.email)
|
customer.delete()
|
||||||
except stripe.CardError, e:
|
|
||||||
error_message = e.json_body['error']['message']
|
|
||||||
return HttpResponseRedirect('/for-teams/?declined=%s' %
|
|
||||||
error_message or
|
|
||||||
'Your card was declined.')
|
|
||||||
|
|
||||||
team = Team(name=request.POST['team-name'],
|
team.disabled = True
|
||||||
email=request.POST['email'],
|
team.stripe_id = None
|
||||||
plan=plan,
|
team.save()
|
||||||
owner=request.user)
|
|
||||||
team.stripe_id = customer.id
|
|
||||||
team.save()
|
|
||||||
|
|
||||||
user = User.objects.create_user(team.slug,
|
return HttpResponseRedirect('/' + team.slug + '/?team-cancelled=true')
|
||||||
team.email,
|
|
||||||
str(uuid.uuid4()))
|
|
||||||
|
|
||||||
team.user = user
|
|
||||||
team.save()
|
|
||||||
|
|
||||||
return {
|
|
||||||
'team': team
|
|
||||||
}
|
|
||||||
else:
|
|
||||||
return HttpResponseBadRequest()
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
<a href="/signup" class="snipt-promo">
|
<a href="/signup" class="snipt-promo">
|
||||||
<button class="btn btn-success btn-large pull-right">Sign up »</button>
|
<button class="btn btn-success btn-large pull-right">Sign up »</button>
|
||||||
Sign up for Snipt!<br /><span style="font-size: 16px;">Post public snipts for free. Private snipts just $5/mo.</span>
|
Sign up for Snipt!<br /><span style="font-size: 16px;">Post public snipts for free.</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
Loading…
Reference in New Issue