Switching from recurring plans to a single fee for Pro accounts.

master
Nick Sergeant 2014-06-23 23:55:24 -04:00
parent 9a6633bbd1
commit 66249c984e
6 changed files with 17 additions and 109 deletions

File diff suppressed because one or more lines are too long

View File

@ -1,14 +0,0 @@
(function(Site){var Snipt=snipt.module('snipt');Backbone.oldSync=Backbone.sync;Backbone.Model.prototype.idAttribute='resource_uri';var addSlash=function(str){return str+((str.length>0&&str.charAt(str.length-1)==='/')?'':'/');};Backbone.sync=function(method,model,options){options.headers=_.extend({'Authorization':'ApiKey '+window.user+':'+window.api_key},options.headers);return Backbone.oldSync(method,model,options);};Backbone.Model.prototype.url=function(){var url=this.id;if(!url){url=this.urlRoot;url=url||this.collection&&(_.isFunction(this.collection.url)?this.collection.url():this.collection.url);if(url&&this.has('id')){url=addSlash(url)+this.get('id');}}
url=url&&addSlash(url);if(typeof url==='undefined'){url='/api/private/snipt/';this.unset('id',{'silent':true});this.unset('user',{'silent':true});}
return url||null;};Site.SiteView=Backbone.View.extend({el:'body',initialize:function(opts){this.$body=$(this.el);this.$html=$('html');this.$html_body=this.$body.add(this.$html);this.$aside_main=$('aside.main',this.$body);this.$aside_nav=$('aside.nav',this.$body);this.$aside_nav_ul=$('ul',this.$aside_nav);this.$search_form=$('form.search',this.$body);this.$search_query=$('input#search-query',this.$body);this.$search_page_query=$('input.search-query',this.$body);this.$search_queries=this.$search_query.add(this.$search_page_query);this.$snipts=$('section#snipts article.snipt',this.$body);this.$modals=$('div.modal',this.$snipts);this.$main_edit=$('section#main-edit');this.$main=$('section#main');this.$keyboard_shortcuts=$('#keyboard-shortcuts',this.$body);this.keyboardShortcuts();this.inFieldLabels();var SniptListView=Snipt.SniptListView;this.snipt_list=new SniptListView({'snipts':this.$snipts});var that=this;this.$body.click(function(){if(!window.ui_halted&&!window.from_modal&&window.$selected){window.$selected.trigger('deselect');}
if(window.from_modal){window.from_modal=false;}
that.$aside_nav.removeClass('open');});this.$aside_nav_ul.click(function(e){e.stopPropagation();});$search_queries=this.$search_queries;$search_queries.focus(function(){if(window.$selected){$selected.trigger('deselect');}});this.$body.on('click','a.close',function(){$(this).parent().parent().modal('hide');window.ui_halted=false;return false;});this.$keyboard_shortcuts.on('hidden',function(){window.ui_halted=false;});if(this.$body.hasClass('pro-signup')){var $form=$('form#pro-signup');var $submit=$('button[type="submit"]',$form);var $name=$('input#name');var $cardNumber=$('input#number');var $expMonth=$('select#exp-month');var $expYear=$('select#exp-year');var $cvc=$('input#cvc');$form.submit(function(){$submit.attr('disabled','disabled');var errors=false;if(!Stripe.validateCardNumber($cardNumber.val())){$cardNumber.parents('div.control-group').addClass('error');errors=true;}else{$cardNumber.parents('div.control-group').removeClass('error');}
if(!Stripe.validateExpiry($expMonth.val(),$expYear.val())){$expMonth.parents('div.control-group').addClass('error');errors=true;}else{$expMonth.parents('div.control-group').removeClass('error');}
if(!Stripe.validateCVC($cvc.val())){$cvc.parents('div.control-group').addClass('error');errors=true;}else{$cvc.parents('div.control-group').removeClass('error');}
if(!errors){$('.payment-errors').hide();$('.payment-loading').show();Stripe.createToken({name:$name.val(),number:$cardNumber.val(),cvc:$cvc.val(),exp_month:$expMonth.val(),exp_year:$expYear.val()},that.stripeResponseHandler);}else{$submit.removeAttr('disabled');}
return false;});}
if(this.$body.hasClass('login')){$('input#id_username').focus();}
if(window.gittip_username){this.$aside_main.html(this.$aside_main.html().replace(/\[\[.*gittip.*\]\]/,'<iframe style="border: 0; margin: 0; padding: 0;" src="https://www.gittip.com/'+window.gittip_username+'/widget.html" width="48pt" height="22pt"></iframe>'));$('iframe',this.$aside_main).parent('p').prev('p').css('margin-bottom','10px');}
$('div.markdown pre code').each(function(i,e){hljs.highlightBlock(e);});var $embeddedTweets=$('div.embedded-tweet');if($embeddedTweets.length){$.each($embeddedTweets,function(){var $tweetPlaceholder=$(this);var tweetID=$tweetPlaceholder.attr('data-tweet-id');$.ajax({url:'https://api.twitter.com/1/statuses/oembed.json?id='+tweetID+'&align=center',dataType:'jsonp',type:'get',success:function(resp){$tweetPlaceholder.replaceWith($(resp.html));}});});}
window.ui_halted=false;},events:{'showKeyboardShortcuts':'showKeyboardShortcuts','click a.mini-profile':'toggleMiniProfile'},keyboardShortcuts:function(){var $body=this.$body;var that=this;$search_queries=this.$search_queries;$search_page_query=this.$search_page_query;$search_query=this.$search_query;$document=$(document);$document.bind('keydown','/',function(e){if(!window.ui_halted){e.preventDefault();if($body.hasClass('search')){$search_page_query.focus();}else{$search_query.focus();}}});$document.bind('keydown','Ctrl+h',function(e){if(!window.ui_halted){window.ui_halted=true;$body.trigger('showKeyboardShortcuts');}else{if(that.$keyboard_shortcuts.is(':visible')){that.$keyboard_shortcuts.modal('hide');}}});this.$search_queries.bind('keydown','esc',function(e){if(!window.ui_halted){e.preventDefault();this.blur();}});},showKeyboardShortcuts:function(){this.$keyboard_shortcuts.modal('toggle');},toggleMiniProfile:function(e){this.$aside_nav.toggleClass('open');return false;},inFieldLabels:function(){$('div.infield label',this.$body).inFieldLabels({fadeDuration:200});},stripeResponseHandler:function(status,response){var $form=$('form#pro-signup');if(response.error){$('button[type="submit"]',$form).removeAttr('disabled');$('.payment-loading').hide();$('.payment-errors').text(response.error.message).show();}else{var token=response.id;$('input#name').val('');$('input#number').val('');$('select#exp-month').val('');$('select#exp-year').val('');$('input#cvc').val('');$form.append("<input type='hidden' name='token' value='"+token+"'/>");$form.get(0).submit();}}});})(snipt.module('site'));

View File

@ -142,7 +142,7 @@ class SniptValidation(Validation):
if 'pk' not in bundle.data and \
request.user.profile.get_account_age() > 7 and \
request.user.profile.is_pro == False:
errors['expired'] = "Your trial has expired. You'll need to subscribe in order to create new snipts. Read more at https://blog.snipt.net/moving-away-from-free-accounts-and-planning-for-the-future/."
errors['expired'] = "Your trial has expired. You'll need to go Pro (https://snipt.net/pro/) in order to create new snipts."
return errors

View File

@ -25,7 +25,7 @@
{% if request.GET.expired %}
<div class="alert alert-error" style="margin: 30px; padding-right: 14px;">
<a style="color: #B94A48; text-decoration: underline;" class="pull-right" href="https://blog.snipt.net/moving-away-from-free-accounts-and-planning-for-the-future/">Read more &raquo;</a>
<strong>Your trial has expired.</strong> You'll need to subscribe in order to create new snipts.
<strong>Your trial has expired.</strong> You'll need to go Pro in order to create new snipts.
</div>
{% endif %}
{% if request.GET.declined %}
@ -36,25 +36,14 @@
<form class="form-horizontal static-box" id="pro-signup" method="post" action="/pro/complete/{% if 'plan' in request.GET %}?plan={{ request.GET.plan }}{% endif %}">
<fieldset>
<div class="info">
Subscribe for just <span>$5/mo</span>, <span>$49/yr</span>, or <span>$149 once</span>.
Go <span class="pro">Pro</span> for just <span>$29</span> (one-time fee).
<p class="sub">
We believe the best way to build a superior product is to charge for it.
As a paid Snipt member, you'll have unrestricted access to the app, <a href="/api/">API</a>,
custom-domain <a href="/blogging/">blogging</a>, instant chat support, and more.
</p>
</div>
<div class="payment-loading"><span>Please wait&hellip;</span></div>
<div class="payment-errors alert alert-error"></div>
<div class="control-group">
<label class="control-label" for="name">Payment plan:</label>
<div class="controls">
<select name="plan" type="text" class="input-medium" id="plan">
<option value="snipt-monthly">$5/month</option>
<option value="snipt-yearly">$49/year</option>
<option value="onetime">$149 once</option>
</select>
</div>
</div>
<div class="control-group">
<label class="control-label" for="name">Name on card:</label>
<div class="controls">
@ -113,7 +102,7 @@
</div>
<div class="form-actions">
{% csrf_token %}
<button type="submit" class="btn btn-success">Subscribe</button>
<button type="submit" class="btn btn-success">Pay Now</button>
<div class="security">
<a href="https://stripe.com/help/security">Secure</a> by default. Every Snipt page is secure.
</div>

View File

@ -22,11 +22,10 @@
{% endif %}
<fieldset>
<div class="info">
Free for 7 days, then just <span>$5/mo</span>, <span>$49/yr</span>, or <span>$149 once</span>.
Free for 7 days, then just <span>$29</span> (one-time fee).
<p class="sub">
We believe the best way to build a superior product is to charge for it.
As a paid Snipt member, you'll have unrestricted access to the app, <a href="/api/">API</a>,
custom-domain <a href="/blogging/">blogging</a>, instant chat support, and more.
Your free trial comes with unrestricted access to the app,<br />
<a href="/api/">API</a>, instant chat support, and more.
</p>
</div>
<div class="control-group {% if form.errors.username %}error{% endif %}">

View File

@ -104,32 +104,15 @@ def pro_complete(request):
token = request.POST['token']
stripe.api_key = STRIPE_SECRET_KEY
# One-time plan.
if request.POST['plan'] == 'onetime':
customer = stripe.Customer.create(email=request.user.email,
card=token)
try:
stripe.Charge.create(amount=14900,
currency='usd',
customer=customer.id,
description='Snipt.net')
except stripe.CardError:
return HttpResponseRedirect('/pro/?declined=true')
# Recurring plans.
else:
if 'plan' in request.GET:
plan = request.GET['plan']
else:
plan = request.POST['plan']
try:
customer = stripe.Customer.create(card=token,
plan=plan,
email=request.user.email)
except stripe.CardError:
return HttpResponseRedirect('/pro/?declined=true')
customer = stripe.Customer.create(email=request.user.email,
card=token)
try:
stripe.Charge.create(amount=2900,
currency='usd',
customer=customer.id,
description='Snipt.net')
except stripe.CardError:
return HttpResponseRedirect('/pro/?declined=true')
profile = request.user.profile
profile.is_pro = True