February 28, 2013

Building Stripe's API

Last week, I gave my first conference talk at the API Strategy and Practice conference in New York. Pretty exciting!

I thought it would be interesting to talk about Stripe's API, particularly lessons learned and what kind of things we did to try to make the API as easy to use as possible. I've included the slides below, but most of the content isn't on the slides so I'll try to cover some of the highlights.

Of course, we don't claim to have all of the answers. Every API is different, and a lot of what you see on Stripe today is the product of a lot of thought and discussion, as well as a lot of trial and error. We're constantly experimenting and improving.

Hopefully you find something here applicable towards your own API! (:

Highlights

Make it easy to get started

It may sound like a no-brainer, but the best way to get people to try out (and hopefully eventually use) your API is to make it really easy to get started.

To that end, we do things like including pastable code snippets throughout our site and documentation. One of the first things you'll see on our front page is a curl snippet you can paste into a terminal to simulate charging a credit card.

Regardless of whether you have a Stripe account or not (if logged in, we fill in your test API key, otherwise, it's a sample account's API key), you can see the Stripe API in action.

All of our documentation code snippets are similarly possible to directly copy and paste—we try to embed as much information as possible (API keys, actual object IDs from the account, etc.) so our users don't have to.

Language-specific libraries and documentation

Since Stripe's API speaks HTTP and JSON, you could easily integrate it into your application with any standard HTTP client library. However, this still requires constructing requests and parsing responses on your own.

We maintain and support open-source libraries in some of today's most popular web languages. It turns out people are pretty attached to their favorite languages.

We had a lot of internal discussions about whether we actually wanted to support our own client bindings or allow the community to organically start and maintain the projects themselves. Is it worth owning the projects if it means that you might have to maintain libraries for languages or frameworks in which you don't have expertise?

Maybe.

Official libraries have the benefit of being consistent: they all have the same level of quality, support the same interface, and get updates at the same time. Having our own libraries also makes it easier for us to have language-specific documentation and help our users with any problems they might be having with a particular integration.

We decided that it was worth it, but this may not be the right answer for everyone.

Have a focused API, but allow flexibility

We've found that it's critically important to keep the API focused and simple.

It's often tempting to add new features that are not obviously necessary to the core API. For example, our users frequently want us to add better analytics, tax calculations, or to send customers receipts1. While these things are nice, every feature you add makes the API more complex and cluttered.

You can instead give your users the tools to be able to write their own extensions. We allow our users (and third party applications) to hook into Stripe in a couple of ways:

Webhooks

Stripe uses webhooks to let our users know when some interesting event has happened. This ranges from events triggered by an API call, like charge.succeeded or charge.refunded, to asynchronous events like customer.subscription.trial_will_end.

Our aim was to make it easy to layer additional logic on top of Stripe events (like sending customer receipts or enabling push notifications). Giving our users the ability to build this kind of customized functionality allows them to control the entire experience for their users as well.

Stripe Connect

Stripe Connect, an API we released just last year, is another way of building on top of the Stripe platform.

Connect is an OAuth2 API that allows a Stripe user to authorize access to their Stripe account to a third-party application. We've seen a variety of applications built on top of Stripe so far: marketplaces and checkout pages let users "plug in" their Stripe accounts to accept payments, and analytics dashboards fetch Stripe data in order to show interesting graphs or patterns.

Provide a testing environment

One of the most important things you need with an API is a great test/sandbox environment. This is particularly important for a payments API—our users shouldn't have to make live charges when they're trying to test their integration.

In our test environment, we allow users to send test webhooks of any type and provide handy test card numbers that trigger certain errors (like declines).

Doing this allows them to easily test the behavior of their own application in the face of different scenarios instead of having to manually trigger things that are nondeterministic, like declines, or time-dependent, like expiring subscriptions.

Help your users debug

We're developers too. We know from experience that debugging is a disproportionately large portion of the development cycle. We also (unfortunately) know that sometimes you spend a lot of time debugging something that eventually turns out to be really obvious or silly.

For common or easy errors, you (the API) likely know exactly what's wrong. So why not try to help?


    >> Stripe::Customer.create
    Stripe::AuthenticationError: No API key provided.  (HINT: set your API key
    using "Stripe.api_key = ".  You can generate API keys from the 
    Stripe web interface.  See https://stripe.com/api for details, or email
    support@stripe.com if you have any questions.)


    >> Stripe.api_key = TEST_KEY
    => ...
    >> Stripe::Charge.retrieve(LIVE_CHARGE_ID)
    Stripe::InvalidRequestError: (Status 404) No such charge: ch_17SOe5QQ2exd2S;
    a similar object exists in live mode, but a test mode key was used to make
    this request.

On the other hand, some errors are harder to diagnose (especially from the API's end, since you have limited information about what your user is actually trying to accomplish).

Where possible, we absolutely think it's worthwhile to try to anticipate our users' errors and help as much as we can.

Dealing with Change

Lastly, dealing with change is never fun. As much as you hope you'll never have to change the API, sometimes you need to make changes and sometimes those changes are backwards-incompatible.

There's no easy answer to versioning APIs. We keep a per-user version which reflects the state of the API the first time the user made an API request. Most of our new features are additions that aren't backwards incompatible, and they just work automatically for everyone.

Whenever we make a backwards-incompatible change2, however, it doesn't affect the API behavior for any of our current users. Users can then choose to explicitly upgrade their version in the dashboard (after reviewing the detailed changelogs) or can send a version override header in any API request to test the behavior of a specific version.

Questions?

If you have any questions, feel free to email or tweet at me.

Thanks for reading!

Footnotes

  1. Stripe may very well end up implementing these particular features in the future, but it's not generally feasible to try to accommodate everyone's use case.
  2. We try to avoid this as often as possible.

Credit for various parts of the presentation content go to Greg Brockman, Sidd Chandrasekaran, Evan Broder, and Ross Boucher. And of course, credit to everyone at Stripe for actually doing the things I outlined in the talk.

December 21, 2012

Private methods in Ruby

Consider the following code:

(This blew my mind the other day.)

class Hello
  def public_hello
    self.private_hello
  end

  private
  def private_hello
    puts "Hello!"
  end
end

You would expect that this would work fine: private_hello is a private method, but it's being called from within the class.

Nope.

>> hello = Hello.new
=> #<Hello:0x10d0cc200>
>> hello.public_hello
NoMethodError: private method `private_hello' called for #<Hello:0x10d0cc200>
  from (irb):3:in `public_hello'
  from (irb):13

I spent an embarassingly long time trying to figure out what was wrong ("Do I just not understand how private methods work?!"), and confused one of my coworkers as well in the process of doing so.

However, it turned out to be old news. One post puts the issue pretty succinctly:

private methods can never be called with an explicit receiver, even if the receiver is self

So, the problem with self.private_hello is that the private method is being called on an explicit receiver, even though the receiver is technically the same object—you'd need to call private_hello by itself instead.

Having learned access control modifiers in Java first, I thought this was really bizarre. I guess I need to learn Ruby a little better! (:

October 31, 2012

Discount Code Cards

I love career fairs. In college, I loved going to career fairs to get swag—EECS majors are definitely ridiculously spoiled when it comes to getting free things like shirts and food (I even got a poker set once!).

Anyway, while planning our trip to the UC Berkeley Startup Fair this year, I wanted Stripe to stand out so I thought pretty hard about what my favorite types of things to get were.

Number one was shirts, probably. We already had that covered. But I also remembered happily going to the Dropbox booth every semester to get those nifty free space cards: discount codes that you can apply to your account to get 5-10gbs of space at a time.

What if we made free Stripe processing cards? Since we'd be going to a career fair where all of the students were software engineers, and more specifically, very hacker/startup-minded, it made even more sense as a marketing effort.

Implementation

Most of the discount code implementation was already in place from our invite system, so all I really needed to do was generate an extra couple of hundred codes for use at the career fair. The hard part, however, was trying to figure out how to print them.

Traditional business card printing services won't let you print out cards with unique codes on them unless you go through some kind of custom order. I didn't have too much time to spare, so John told me about this nifty hack he used with Moo.com (I later found the same solution on Quora).

Moo lets you do this cool thing where you can print a variable number of designs per order—for example, if you want to have a different photograph on the back of each business card. We found that if you upload 100 different "designs" for 100 different cards, each card would be unique.

Their Text-o-matic tool lets you create up to 100 "designs" that are text only:

I took one look at the page and realized I could easily use a script to automatically generate all of the cards (sigh of relief when I discovered the page was not written in Flash):

// Create an array of the discount codes you want to use here
var texts = ['CODE1', 'CODE2', 'CODE3'];

// Click buttons on the page for each code to create the design
texts.each(function(text) {
  jQuery('#txtFrontText').val(text);
  jQuery('div#divMakeCard input').click();

  // Set color/size
  jQuery('#a_000000').click();
  jQuery('#aReverseColours').click();
  jQuery('#aZoomOut').click();
  jQuery('#aZoomOut').click();

  jQuery('#btnSaveCard').click();
});

Really, really simple but pretty hacky (just run this in the developer console on the web page). After this, you'll just go into their general template wizard and upload the design for the other side of the card to finish them up.

Improvements

Of course, there are several drawbacks to this approach:

  • You can't really design the "variable" side of the card. You either need to upload the images yourself, or use Text-o-matic, which has a super limited set of options (you can't even add a new line or style separate parts of the text).
  • You can only print 100 cards per order. Since the max number of designs you can upload is 100, if you try to double the card count to 200, you'll end up with every unique card twice.

So what can you do?

Probably the only legitimate way to do this is to generate the to-print PDF yourself, either using some kind of software (like InDesign) or hacking something in PostScript. I'll be sure to write a follow up post if I end up doing either!

September 9, 2012

Goodbye, Wordpress!

As you may or may not have noticed, this blog has recently undergone a pretty drastic change. Aside from pure design upgrades, I've moved off Wordpress (good riddance!) and joined the geek bandwagon by writing my blog with Jekyll. Here's my obligatory "I moved to Jekyll" blog post!

Why Jekyll?

  • Wordpress is bloated. I've never even used a third of the features—I wanted something super simple and lightweight that I could gradually add onto (if I wanted to).
  • Custom styling. Wordpress, I've found, is a nightmare to try to style. There are layers upon layers of templates, and I really hate trying to navigate through PHP code. Jekyll, by contrast, is a lot cleaner and easier to deal with.
  • Markdown/Vim > Wordpress editor. I've gotten to the point where I basically need to use Vim for everything (code, obviously, but also notes, to-do lists, etc). Markdown is great, and I like how all of my blog posts are now under version control.
  • Static pages. Blogs are almost pure static content (with the exception of comments, search, etc), so I figured that it would be simpler (and faster) to just have an entirely static site. This makes deploys super easy as well.

My setup

I'm not going to go through in depth how I got my Jekyll set up, because there are more than enough of those guides out there. Here are some of the main components that I used:

All of the templates and styles are handwritten. Being able to customize all aspects of the blog is definitely one of the major perks of using Jekyll!

March 18, 2012

Elasticsearch Space-savers

After setting up ElasticSearch, you'll be faced with the task of optimizing your index configuration for speed and for size. There are millions of documents in our index, and, for performance, it’s important that all of that be kept in memory. As a result, index size is pretty important. We spent a while tweaking ElasticSearch to minimize its index size, and it turns out that there’s a decent number of low-hanging fruit.

_source

For example, ElasticSearch stores the original data along with the indexed data for each document and returns the full document with each search result. We just wanted ElasticSearch queries to assemble a set of document IDs to be fetched from the database, so this overhead is unnecessary. Cutting this data shrank our search index four-fold.

:_source => { :enabled => false }

_all

In addition, ElasticSearch stores an aggregated _all field on each document, which contains the analyzed output from all of the other fields in the document. This doesn’t add any new information, and its purpose is just to simplify the query interface.

We don’t need to be able to query all fields (for example, we only use user IDs to partition the index); setting a flag to exclude these from the _all field and preventing them from being analyzed saved us another 4-5GB for ~2 million documents.

'id' => { :type => 'string', :include_in_all => false }

Multi-field types

Other problems were a little trickier. How do we minimize the size of the email address index given that we’d like to be able to perform both prefix and substring searches on them?

Email addresses may be very long and overflow our ngram tokenizer (which maxes out at fifteen characters), and so we decided to construct a multi-field tiered index for certain fields in to accommodate all of the search use cases. We don't know if this is a best practice, but it seems to work pretty well for us.