…does nerdy things.

Chasing Those Long Tail Keywords

Filed under: Work — Bryan on Oct 23rd, 2011 @ 6:34 pm

With Snapier.com, my early stage startup, we’re betting that a large fraction of our future customers will find us from organic searches. However, we know better than to bet the house on a single, high value keyword like API integration. So, we’re trying to think a little like Hacker News’ resident SEO expert Patrick McKenzie (patio11):

…go for long tailed keywords, and make sure you have a scalable system to create the content.

The solution is our directory of API integrations, the Zapbook. We’ve built a quick and dirty system of categorizing what we are calling “snaps”, little pieces of content that fall into categories that encompass popular web services. The hope is that we can build up a powerhouse of extremely useful (if narrow) solutions that will rank highly across very specific long tail searches.

Some things we spent some time thinking about:

Common sense URL structure:

We wanted to be sure that we wouldn’t be shooting ourselves in the foot if we decide to make changes later on down the road. So we kept URL structure really simple and obvious. Ideally, we should be able to restructure the way a user searches and navigates the Zapbook without altering the URLs Google has indexed.

Simplified content creatation:

Our Snapbook is no good without content, so we made it super simple to manually add small pieces of content as we go along the path of building our company. Convenience is key. Eventually, it should be trivial to let users augment content as they use the site, or have services like Textbroker automatically handle this.

Measurable value of content:

We can track every user that signs up after landing on a piece of deep content. From that, we can calculate the value of an average (or specific) piece of content, allowing us to justify spending more or less time or money on content creation.

20 Minutes With Stripe and Django

Filed under: Work — Bryan on Oct 11th, 2011 @ 12:14 am

If you use this, make sure you are PCI compliant, otherwise explore stripe.js…

In case you haven’t heard, payment gateways, merchant accounts and all that jazz are now obsolete thanks to Stripe. Stripe offers a simple to set up payment service with an absolutely wonderful API. Instead of comparing and contrasting dozens of merchant accounts and struggling with arcane API’s, with Stripe you input your name, address, SSN, and bank account information (among a few other things) and… you’re done.

In addition to a sane API, they offer great libraries for PHP, Python and Ruby along with wonderful documentation. Kudos to the developer team at Stripe, they’ve really outdone themselves.

Enough praising Stripe, let’s learn how to integrate it with Django. This should only take 20 minutes or so, and you shouldn’t have a problem bending this method to your will.

Things we need:

  1. Some way to store the sale in our database.
  2. A form that will validate the card details.
  3. A template to display the form with proper, human readable errors.
  4. The Stripe Python library:
    sudo pip install --index-url https://code.stripe.com --upgrade stripe

Luckily, we need not start from scratch on everything, as there is a very useful snippet for a credit card form we can start with. With that in mind, let’s lay out our project. I’ve decided to create a sales app that will contain: a Sale model, a SalePaymentForm, and a single view for displaying and parsing the form. First up, the Sale model in mystore/sales/models.py:

from django.db import models
 
import settings
 
class Sale(models.Model):
    def __init__(self, *args, **kwargs):
        super(Sale, self).__init__(*args, **kwargs)
 
        # bring in stripe, and get the api key from settings.py
        import stripe
        stripe.api_key = settings.STRIPE_API_KEY
 
        self.stripe = stripe
 
    # store the stripe charge id for this sale
    charge_id = models.CharField(max_length=32)
 
    # you could also store other information about the sale
    # but I'll leave that to you!
 
    def charge(self, price_in_cents, number, exp_month, exp_year, cvc):
        """
        Takes a the price and credit card details: number, exp_month,
        exp_year, cvc.
 
        Returns a tuple: (Boolean, Class) where the boolean is if
        the charge was successful, and the class is response (or error)
        instance.
        """
 
        if self.charge_id: # don't let this be charged twice!
            return False, Exception(message="Already charged.")
 
        try:
            response = self.stripe.Charge.create(
                amount = price_in_cents,
                currency = "usd",
                card = {
                    "number" : number,
                    "exp_month" : exp_month,
                    "exp_year" : exp_year,
                    "cvc" : cvc,
 
                    #### it is recommended to include the address!
                    #"address_line1" : self.address1,
                    #"address_line2" : self.address2,
                    #"daddress_zip" : self.zip_code,
                    #"address_state" : self.state,
                },
                description='Thank you for your purchase!')
 
            self.charge_id = response.id
 
        except self.stripe.CardError, ce:
            # charge failed
            return False, ce
 
        return True, response

A couple things to point out before moving on. One, we initialize the Stripe API in the __init__ method and use the API key you should set in settings.py as STRIPE_API_KEY = “somelongstripefromstripe”. Two, we wrap the actual charge API call in a try/except block so we can catch any errors Stripe might throw. Notice that we return that error as it is important to show the real, human readable error to the end user. You’ll see how in a second. Three, we make sure to set the charge_id on the mode, but we’ll need to remember to call the save() method.

Next, we need a form to handle the user’s data. Luckily, that snippet from before will come in handy, as the only thing we need to do is switch out the payment implementation. So, without further ado, here is the SalePaymentForm in mystore/sales/forms.py:

from datetime import date, datetime
from calendar import monthrange
 
from django import forms
 
from sales.models import Sale
 
class CreditCardField(forms.IntegerField):
    def clean(self, value):
        """Check if given CC number is valid and one of the
           card types we accept"""
        if value and (len(value) < 13 or len(value) > 16):
            raise forms.ValidationError("Please enter in a valid "+\
                "credit card number.")
        return super(CreditCardField, self).clean(value)
 
class CCExpWidget(forms.MultiWidget):
    """ Widget containing two select boxes for selecting the month and year"""
    def decompress(self, value):
        return [value.month, value.year] if value else [None, None]
 
    def format_output(self, rendered_widgets):
        html = u' / '.join(rendered_widgets)
        return u'<span style="white-space: nowrap;">%s</span>' % html
 
class CCExpField(forms.MultiValueField):
    EXP_MONTH = [(x, x) for x in xrange(1, 13)]
    EXP_YEAR = [(x, x) for x in xrange(date.today().year,
                                       date.today().year + 15)]
    default_error_messages = {
        'invalid_month': u'Enter a valid month.',
        'invalid_year': u'Enter a valid year.',
    }
 
    def __init__(self, *args, **kwargs):
        errors = self.default_error_messages.copy()
        if 'error_messages' in kwargs:
            errors.update(kwargs['error_messages'])
        fields = (
            forms.ChoiceField(choices=self.EXP_MONTH,
                error_messages={'invalid': errors['invalid_month']}),
            forms.ChoiceField(choices=self.EXP_YEAR,
                error_messages={'invalid': errors['invalid_year']}),
        )
        super(CCExpField, self).__init__(fields, *args, **kwargs)
        self.widget = CCExpWidget(widgets =
            [fields[0].widget, fields[1].widget])
 
    def clean(self, value):
        exp = super(CCExpField, self).clean(value)
        if date.today() &gt; exp:
            raise forms.ValidationError(
            "The expiration date you entered is in the past.")
        return exp
 
    def compress(self, data_list):
        if data_list:
            if data_list[1] in forms.fields.EMPTY_VALUES:
                error = self.error_messages['invalid_year']
                raise forms.ValidationError(error)
            if data_list[0] in forms.fields.EMPTY_VALUES:
                error = self.error_messages['invalid_month']
                raise forms.ValidationError(error)
            year = int(data_list[1])
            month = int(data_list[0])
            # find last day of the month
            day = monthrange(year, month)[1]
            return date(year, month, day)
        return None
 
class SalePaymentForm(forms.Form):
    number = CreditCardField(required=True, label="Card Number")
    expiration = CCExpField(required=True, label="Expiration")
    cvc = forms.IntegerField(required=True, label="CCV Number",
        max_value=9999, widget=forms.TextInput(attrs={'size': '4'}))
 
    def clean(self):
        """
        The clean method will effectively charge the card and create a new
        Sale instance. If it fails, it simply raises the error given from
        Stripe's library as a standard ValidationError for proper feedback.
        """
        cleaned = super(SalePaymentForm, self).clean()
 
        if not self.errors:
            number = self.cleaned_data["number"]
            exp_month = self.cleaned_data["expiration"].month
            exp_year = self.cleaned_data["expiration"].year
            cvc = self.cleaned_data["cvc"]
 
            sale = Sale()
 
            # let's charge $10.00 for this particular item
            success, instance = sale.charge(1000, number, exp_month,
                                                exp_year, cvc)
 
            if not success:
                raise forms.ValidationError("Error: %s" % instance.message)
            else:
                instance.save()
                # we were successful! do whatever you will here...
                # perhaps you'd like to send an email...
                pass
 
        return cleaned

I’ve removed a few of the bits from the snippet, mainly to simplify things, as we don’t need to filter out Discover cards or the like. The primary thing to notice is how if the charge isn’t successful, we raise a ValidationError and pass in the message from Stripe’s exception. This will allow us to display it as a normal error on the form. But before we we move on to the view, notice how we initialize a blank Sale? The charge() method doesn’t require a saved model instance, but we make sure to save it if it is successful. It might be more appropriate to make this a ModelForm, but for now, it works.

Let’s take a look at mystore/sales/urls.py:

from django.conf.urls.defaults import *
from sales import views
 
urlpatterns = patterns('',
    url(r'^charge/$', views.charge, name="charge"),
)

Nothing fancy here. Let’s take a look at and mystore/sales/views.py:

from django.shortcuts import render_to_response
from django.http import HttpResponse
from django.template import RequestContext
 
from sales.models import Sale
from sales.forms import SalePaymentForm
 
def charge(request):
    if request.method == "POST":
        form = SalePaymentForm(request.POST)
 
        if form.is_valid(): # charges the card
            return HttpResponse("Success! We've charged your card!")
    else:
        form = SalePaymentForm()
 
    return render_to_response("sales/charge.html",
                        RequestContext( request, {'form': form} ) )

Once again, nothing very fancy here either. Let’s take a look at templates/sales/charge.html:

<html>
<head>
  <title>Stripe Example</title>
</head>
<body>
 
<div class="wrapper">
 
  {% for key, value in form.errors.items %}
      <p>{{ value }}</p>
  {% endfor %}
 
  <form action="" method="post">{% csrf_token %}
 
    {% for field in form %}
      <div class="field-wrapper">
 
        <div class="field-label">
          {{ field.label_tag }}:
        </div>
 
        <div class="field-field">
          {{ field }}
          {{ field.errors }}
        </div>
 
      </div>
    {% endfor %}
 
    <br>
    <input type="submit" value="Charge Me!" />
  </form>
 
</div>
 
</body>
</html>

This should come as no surprise to you, but there isn’t much going on here. Probably the only slightly odd thing is the for loop over the form.errors dictionary, as this is where we will grab the ValidationError placed on the form itself as we caught it during the charge phase.

Besides for setting up the standard Django stuff like the database and admin bits (mystore/sales/admin.py), that pretty much covers it.

This is a method similar to what we’re using on our new product Zapier, so go grab the code off of GitHub!

All articles are licensed under a Attribution-Noncommercial-Share Alike 3.0 Unported License. All files/themes are released under the GPL License where applicable. © 2012 Bryan Helmig Hosted on Webfaction.