Team billing info and ability to cancel subscription.
parent
79d077def9
commit
4127ba79b1
|
@ -23,6 +23,14 @@ jQuery(function($) {
|
|||
var pre = $pres.eq(i);
|
||||
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.
|
||||
|
|
|
@ -23,6 +23,7 @@ if (typeof angular !== 'undefined') {
|
|||
// Controllers.
|
||||
controllers.TeamController = function($scope, $timeout, TeamStorage) {
|
||||
$scope.users = [];
|
||||
$scope.search = '';
|
||||
$scope.$watch('search', function(val) {
|
||||
$timeout.cancel($scope.timeout);
|
||||
|
||||
|
|
|
@ -12,6 +12,11 @@
|
|||
{% endblock %}
|
||||
|
||||
{% 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"
|
||||
{% if request.user.profile.list_view == 'C' %}
|
||||
ng-cloak ng-show="$root.account.id"
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% load team_tags %}
|
||||
|
||||
{% block page-title %}Team Billing{% endblock %}
|
||||
|
||||
{% block body-class %}account {{ block.super }}{% endblock %}
|
||||
|
@ -28,26 +30,29 @@
|
|||
</aside>
|
||||
<section class="content">
|
||||
<div class="def" data-title="Plan">
|
||||
25 users
|
||||
{{ name }}
|
||||
</div>
|
||||
<div class="def" data-title="Price">
|
||||
$49.00 USD / month
|
||||
${{ amount|currency_convert }}.00 USD / {{ interval }}
|
||||
</div>
|
||||
<div class="def" data-title="Card">
|
||||
xxxx-xxxx-xxxx-4242
|
||||
xxxx-xxxx-xxxx-{{ last4 }}
|
||||
</div>
|
||||
<div class="def" data-title="Status">
|
||||
Active
|
||||
{{ status }}
|
||||
</div>
|
||||
<div class="def" data-title="Team since">
|
||||
August 29, 2015
|
||||
{{ team.created|date:'M d, Y' }}
|
||||
</div>
|
||||
<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>
|
||||
<p class="alert alert-info group" style="padding-right: 8px;">
|
||||
<a href="/{{ team.slug }}/billing/cancel/" class="btn btn-danger pull-right">Cancel subscription</a>
|
||||
</p>
|
||||
<form class="alert alert-info group" style="margin: 15px; padding-right: 8px;" id="cancel-team-subscription" action="/{{ team.slug }}/billing/cancel/" method="post">
|
||||
{% csrf_token %}
|
||||
<button type="submit" class="btn btn-danger pull-right">
|
||||
Cancel subscription
|
||||
</button>
|
||||
</form>
|
||||
</section>
|
||||
</section>
|
||||
{% endblock %}
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import datetime
|
||||
|
||||
from django import template
|
||||
|
||||
register = template.Library()
|
||||
|
@ -6,3 +8,13 @@ register = template.Library()
|
|||
@register.filter
|
||||
def user_is_member(team, 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'),
|
||||
url(r'^(?P<username>[^/]+)/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 {}
|
||||
|
||||
|
||||
@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
|
||||
@render_to('teams/team-billing.html')
|
||||
def team_billing(request, username):
|
||||
team = get_object_or_404(Team, slug=username, disabled=False)
|
||||
if team.owner != request.user:
|
||||
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 {
|
||||
'team': team
|
||||
}
|
||||
|
@ -72,42 +136,22 @@ def remove_team_member(request, username, member):
|
|||
|
||||
|
||||
@login_required
|
||||
@render_to('teams/for-teams-complete.html')
|
||||
def for_teams_complete(request):
|
||||
if request.method == 'POST' and request.user.is_authenticated():
|
||||
def cancel_team_subscription(request, username):
|
||||
|
||||
token = request.POST['token']
|
||||
stripe.api_key = os.environ.get('STRIPE_SECRET_KEY',
|
||||
settings.STRIPE_SECRET_KEY)
|
||||
if request.method != 'POST':
|
||||
raise Http404
|
||||
|
||||
plan = request.POST['plan']
|
||||
team = get_object_or_404(Team, slug=username, disabled=False)
|
||||
if team.owner != request.user:
|
||||
raise Http404
|
||||
|
||||
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.')
|
||||
stripe.api_key = os.environ.get('STRIPE_SECRET_KEY',
|
||||
settings.STRIPE_SECRET_KEY)
|
||||
customer = stripe.Customer.retrieve(team.stripe_id)
|
||||
customer.delete()
|
||||
|
||||
team = Team(name=request.POST['team-name'],
|
||||
email=request.POST['email'],
|
||||
plan=plan,
|
||||
owner=request.user)
|
||||
team.stripe_id = customer.id
|
||||
team.save()
|
||||
team.disabled = True
|
||||
team.stripe_id = None
|
||||
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()
|
||||
return HttpResponseRedirect('/' + team.slug + '/?team-cancelled=true')
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<a href="/signup" class="snipt-promo">
|
||||
<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>
|
||||
|
|
Loading…
Reference in New Issue