Why we ditched PayPal for Stripe

A recent backwards-incompatible change to the PayPal Adaptive API led toour web application being unable to accept new users for about five days a while back. We were getting a very vague “Session has expired” error, and PayPal support couldn’t figure out what was wrong. Nothing in that section of the codebase had changed for months, the breakage randomly started.

It ended up being that a recent PayPal API update had made a previously optional field required, and they only updated their PDF documentation (I use their HTML docs). There was no communication of the change in requirements leading up to the breakage, and we were alerted to the issue by frustrated customers calling support (instead of PayPal).

This was the second such breakage during our year or so of using PayPal’s Adaptive API. Granted, we use some of their more advanced split payments and invoicing features, this should not happen. Additionally, we weren’t happy with the user experience during checkout, which featured:

  • Re-directing users to PayPal. User enters credit card details or logs into their PayPal account.
  • User confirms they want to allow us to bill them.
  • User is re-directed to our site, where we again confirm their selection.
  • User gets what they paid for (an enrollment in a course).

A combination of being unhappy with customer support and the disruption of our checkout process with redirects led to us searching for alternatives.

The search begins

Initially, we looked at other similar (but more simple) services. WePay was at the top of our list. This is a young but promising service that makes collecting payments much easier than PayPal. They have a Python API (that we contributed some to), and good documentation. However, they still needed us to redirect our customer to WePay’s site, which ultimately led to their being scratched from the list.

We investigated some traditional payment processors like Braintree and Authorize.net, and while they looked like great services, we needed our customers (schools and businesses) to be able to get set up to receive payments quickly. A lot of traditional gateways have long, complicated setup processes, often involving sales calls and working with banks. Each of our customers would have had to go through this process, which would have presented a huge barrier in the setup process. They also typically require you to send credit card data through your servers, which means obtaining a level of PCI Compliance (not that we aren’t already security-conscious).

Another service we had heard great things about was Stripe. Like WePay, Stripe is a relatively new service, but has backing from former PayPal execs, along with some other great names. Diving in to figure out what the excitement about, we weren’t disappointed.

Stripe gets it right

The refreshing thing about Stripe is that they weren’t afraid to break convention. Instead of redirecting customers to Stripe, or sending their credit card data to their servers from ours (which you can still do if you want), they wrote a nifty bit of javascript called stripe.js. This little guy sends credit card data from a form on your site directly to Stripe’s servers (over SSL), which returns a unique token for that credit card. Said token is then submitted to your server via GET/POST/whatever, and your server uses the token instead of the user’s credit card number.

No redirects are necessary, we avoid having to hassle with monetary and time expense of PCI compliance, and the checkout experience is seamless for our user. We condensed everything down to one click of a button, staying 100% on our branded site.

Top-top notch customer support

The other thing that really shocked us after dealing with PayPal is Stripe’s customer support. We navigated to their support page to see where we could ask some questions early in the process. Imagine our shock when we saw a Campfire chatroom linked there. I cautiously entered the room, not sure what to expect. As the room loaded and I listened in on conversations, I realized that this chatroom has the Stripe developers in it, as well as the business-oriented staff.

Encouraged, I started my deluge of questions to determine whether Stripe would be an option for us. The Stripe staff answered everything consistently, thoroughly, and quickly. I stopped by a good number of times during the two weeks we were researching alternatives, and they were patient every time.

Let’s contrast this to our PayPal setup process during our early development:

  • Jump back and forth between some really convoluted, duplicated content across the then new x.com and their old developer doc CMS.
  • Guess which form to fill out. Send it in.
  • Get to work using their simple NVP API. Learn that what we were doing required the use of a completely different service.
  • Fill out more forms, switch to Web Payments Pro. Learn that this too isn’t what we needed.
  • Switch to the Adaptive Payments API. This particular API was actually pretty easy to work with, though a lot more complicated than Stripe’s.

And then our more recent complete service outage:

  • Enter a ticket in PayPal explaining that our customers weren’t able to checkout, and were seeing a very unhelpful error message.
  • Get a really vague, un-helpful answer back from someone we can’t actively engage with.
  • Get our ticket passed from person to person, having to explain things over again.
  • Having days go by until our issue gets passed to an engineer capable of diagnosing our issue.

PayPal’s mindshare and feature list are huge, but support is such a critical consideration. If we had been in anything but our current closed beta state, this would have been a major catastrophe.

Stripe, now with more Python

Another handy tidbit is that Stripe does have an officially maintained and supported Python library, stripe-python. You can talk to the same developers who maintain this in the aforementiponed Campfire chatroom, as well.

Bashing PayPal is becoming the stylish thing to do, but…

I’m going to get on my podium here for a second, so feel free to navigate somewhere else if this kind of thing bothers you.

A year ago, I usually brushed most harsh criticism of PayPal off. They had been good enough for my personal and professional use. We didn’t really have any reason to deal with their support before our current project, so it was a nice, cozy, detached relationship.

Dealing with their signup process, and later having to deal with their awful support during a service interruption soured me to them quite a bit. Hearing about how they have increasingly taken to freezing accounts for silly reasons has made the negative light I view them in even more intense.

Do consider alternatives before taking your business to this bloated, slow, and unsupportive behemoth. The small guys are going to want your business more, and do more to keep it. Few can go toe-to-toe with PayPal on a per-feature basis, but rarely do you use even the majority of the feature set.

Python and AWS Cookbook (Ebook) 50% off!

Mitch Garnaat’s excellent Python and AWS Cookbook is now 50% off($6.49) in Ebook format (ePub, Mobi, PDF). The book features some great recipes, straight from the maintainer of boto.

While the book isn’t unfriendly to those looking at boto for the first time, it really shines for those who have done some tinkering with boto in the past. Mitch gets right to the point, providing ample explanations for each recipe. The EC2 sections were particularly useful for me. We use boto heavily at DUO, but I managed to learn some great new tricks with instance management by reading through the examples.

For about $7, this is a great read, and a great way to show appreciation for an excellent project.

Addendum:There is no monetary motivation for my cheerleading; I thought I’d share this with others (the special is easy to miss).

nginx AWS ELB name resolution with resolvers

If you are running nginx as a proxy in front of An Amazon Web ServicesElastic Load Balancer (ELB), it is not safe to merely define an upstream using the hostname of ELB and call it a day. By default, nginx will only do name resolution at startup time, caching the resolved IP address infinitely.

ELB instances scale up and down based on your traffic levels, often changing IP addresses in the process. It seems to be that increased traffic leads to Amazon spawning a new, beefier ELB instance, then changing the DNS record to point at the new instance. It’ll keep the old ELB instance around for a little while to give you time to resolve the new one, but the old instance (using the old IP) will be retired after a short period of time. We need nginx to be able to periodically re-resolve the load balancer’s hostname so service interruptions aren’t encountered due to the IP address change.

The fix

Fortuantely, this one is really simple to remedy. You need only use the resolver config directive in your nginx config. By specifying a DNS server with the resolver directive from within nginx, you signify that it should check with said server every five minutes (by default) to see if the upstream ELB has changed IPs. This is done in a non-blocking manner, and should pose no real threat to your server’s throughput.

The other critical piece is that you must add a $request_uri to the end of whatever proxy_pass value you’ve specified. DNS caching will remain without this, meaning you are no better off. See the example below.

Example

http {
   [...]

   # Causes DNS resolution of upstreams every 5 minutes.
   resolver 172.16.0.23;

   [...]

   server {
      [...]

      proxy_pass http://somewhere.com$request_uri

      [...]
   }
}

The resolver directive can be used in http, server, and location sections, so you can get as specific or as broad as you’d like.

The future fix

A later version of nginx will honor DNS TTLs, so look forward to that. I’ll try to remember to update this article when this lands.

media-nommer scampers closer to initial release

Our Python-based, open source (BSD) distributed encoding system,media-nommer, is inching closer to what we’ll call an initial 1.0 release. We’d love to have other developers take a look, try our documentation, and help us chase down flaws before we stick a 1.0 on this thing and put it out there for others to rip to pieces.

Here are the basics:

  • An orchestrator/management daemon runs on some arbitrary machine of your choice (your own, in EC2, Rackspace, wherever). It provides a simple JSON API that your applications can send encoding jobs to. It also handles spinning up new instances and communicating job state to your applications.
  • Encoding nodes are spawned on Amazon EC2, with lots of configurable options to determine resource usage. The system can scale as far as EC2 will let you keep spinning up instances.
  • Encoding is handled through “Nommers“. Each Nommer wraps a different kind of encoding software. The only Nommer currently is FFmpegNommer, which wraps ffmpeg. We’d love to see some other audio and video-related Nommers added (mencoder, anyone?).
  • The EC2 encoder nodes are never in direct contact with the master management daemon. The management daemon can be stopped and restarted at a later date (or on another machine) without any interruptions or data loss. No firewall holes needed.

We are dogfooding media-nommer like crazy on two very large projects at DUO, and it’s working really well for us. However, we’d love to have some other eyes and hands on the project, so please do consider checking it out. If you find yourself using Zencoder or Encoding.com with any kind of regularity, media-nommer just may save you a lot of money.

An embarrassing S3 saga

Around July of 2009, a post was made to the AWS Developer Forums for S3(which the S3 team reads and responds to) asking for the Access-Control-Allow-Origin header to be allowed on keys. The primary motivator for this is that sites would be able to store things like webfonts in S3.

The S3 team replied shortly after the initial post stating that this wasn’t in their immediate roadmap, but they’d watch the thread to help set priorities in the future. Fast-forward over two years, 101 replies, and 24,000 thread views later and we find ourselves still lacking this capability. If you look at the S3 AWS forum, you’re likely to see ‘Access-Control-Allow-Origin header‘ still near the top.

It’s a shame that a reasonably simple request, which would greatly improve S3’s utility for webfont-bearing sites, has failed to even get a “We’re working on this” from the S3 team. We can’t expect them to drop what they’re doing and get on this, but they’ve remained completely non-committal on this.

The workarounds

The way we get around this issue is that we either:

  • Serve webfonts from an nginx instance (not ideal, we’re using S3 so we don’t have to host media)
  • Use a competitor’s service (Rackspace CloudFiles) just for fonts. Other CDNs and object stores support this header.