Update (August 30, 2018): API design has changed a lot since John wrote this post in 2012. With the advent of GraphQL, Braintree is developing an entirely new, modern payments API. You can read more about it here.
At Braintree, we receive nearly 1.5 million API calls per day from thousands of our customers. Instead of a public REST API, we provide client libraries in seven languages to ease integration with our gateway. This covers almost every modern web application language. Using our Java library, we can also reach other JVM languages like Clojure and Scala. As you can imagine, these libraries are built on top of a REST API. Occasionally we are asked why we don't document and expose the REST API, and this inevitably sparks a discussion on our team about the tradeoffs. During these discussions there are three topics that often come up: Security, Platform Support, and Backwards Compatibility. These three topics have kept us in favor of providing client libraries instead of direct access to the low level API.
One of the issues we've found in building the client libraries is the poor implementations of TLS/SSL in almost every programming language we support. Although most languages bundle a HTTP client (with TLS/SSL support) in their standard library, a surprising number have bad default configurations. This problem has recently been called The Most Dangerous Code in the World in a paper by academic researchers.
The worst offense is not verifying certificates by default. Validating the certificate you receive while negotiating TLS/SSL is vital for ensuring the security of the connection. This chain of trust from the certificate authority is the only way to be sure you're talking to the correct server.
Each of our client libraries is configured to validate the certificate of our servers when making a connection. In addition we ship a set of root certificate authorities that all of our HTTPS certificates will chain from. This gives us some extra assurance that there is not a rogue certificate that is pretending to be our API servers.
Another benefit of producing our own client libraries is we can ensure each one has great platform support. With each new feature to our gateway we simultaneously update every client library with the necessary code to enable that feature. For example, one of the more complicated features of our gateway is advanced search and we try to make sure each language has the best possible interface to this functionality. By snapshotting and paging the results correctly we can not only make the feature fast for the consumer but also prevent unnecessary load being put on our servers.
Since we control the client library we can also align certain defaults, such as timeouts. Talking to the backend payment processing networks can occasionally be a slow operation and we can set the maximum amount of time for our clients to wait in the event we have to retry the connection.
Having great platform support also means we can fix bugs as soon as possible and add tests to prevent regressions. If we relied on the community to provide the client libraries there may be a large variation in the quality and support of each library. This can create a frustrating situation for merchants wishing to integrate with Braintree.
Supporting backwards compatibility and semantic versioning is something we are dedicated to at Braintree. Once you've integrated with our gateway we are committed to keeping your integration working without changes on your side, forever. All of this is done while still iterating quickly. We try to deploy new improvements to the production gateway on weekly basis.
The main way we achieve this level of backwards compatibility is through a comprehensive build suite, something we've written about in the past. It is no secret that the Braintree development team embraces Test Driven Development and testing in general. For each supported client library version we run a compatibility build against the branches of development on our gateway. This gives us the feedback we need to be sure we haven't introduced a breaking change accidentally.
By having a full list of every library that merchants use, we can ensure every single one of them still works with any change we make. If you used the REST API directly there any number of things that could break even from small changes on the server side. It is not hard to imagine an implementation that becomes dependent on the order of elements in an API response. In these situations even additive changes could break a poorly implemented response parser.
Taking this approach has not been without its drawbacks. Since we provide all of the client libraries, we have to maintain many separate code bases and must be proficient in all of the languages they're written in. Our goal is to provide a client library that is idiomatic and immediately usable in every language we support. That means when adding a new feature it has to be developed seven different times in slightly different ways.
This also means we have not supported every language that applications are being built in. There are also some stacks that we can't support even though a library exists for that language (e.g. EventMachine in Ruby). We have weighed the tradeoffs and kept to our native library approach for now.
These are the primary reasons we've chosen to keep our REST API private but it doesn't mean we will never open it up. We're interested in hearing how other companies handle these problems with exposing such a low level API. Let us know what you think about the approach we have taken in the comments or stop a Braintreep at a conference/meetup.