Integrating with Amazon Payment System

by Sebastien Mirolo on Wed, 21 Jul 2010

In order to get payments processed through Amazon, you need to register with one of amazon payment systems. Amazon provides a lot of different services and it is a little confusing at first to understand how each service relates to each other. I figured that you first need to get an "Amazon Web Services Account" then register for an "Amazon Payments Account" and finally sign up for an "Amazon Payments Business Account". You will need a U.S.-based credit card and information about your business to complete the registration. I have created a specific e-mail address on fortylines mail server that is only used for communication with Amazon services.

Amazon provides a fully functional sandbox to test the integration of Amazon Payments in your website. All transactions are recorded and processed as in the actual payment system except no money is either transferred. You will need to create one more account, an "Amazon Payments Sandbox Account" to play in the sandbox but it seems worth doing. Getting started with Amazon Simple Pay and Amazon FPS Advanced Quick Start documentation are worth reading before moving forward with developing a prototype.

The basic model is straightforward:

  • #1 You create a request for payment
  • #2 You sign the request with Amazon's provided keys
  • #3 You serve the HTML page with the embedded request to a visitor of your website.
  • #4 The visitor clicks on "pay with Amazon" button
  • #5 The Amazon server process the payment with the visitor's consent
  • #6 The Amazon server presents an HTML page to the visitor with a redirect to a return url on your server.
  • #7 You verify the signature of the Amazon's return transaction.
  • #8 You serve a "thank you" page to the visitor.

You will need the following three keys to make it work:

  • Your Amazon Web Services Access Key
  • Your Amazon Web Services Secret Key
  • Amazon Web Services Public Certificate

You then can browse through the recorded transactions at http://payments.amazon.com (http://payments-sandbox.amazon.com while sandbox prototyping).

Details

Creating the request

Make sure that all requests have to be signed by going to http://payments-sandbox.amazon.com, and select "Your Account > Edit My Account Settings > Manage Developer and Seller Preferences, Sign the Buttons option".

It was not straightforward to figure out where to find the secret key you should use to sign the requests. This requires to sign at http://aws.amazon.com, then select "Account > Security Credentials".

If you do not need to generate requests on the fly, the Amazon website provides a form to easily create the HTML code required and it just becomes a copy/paste operation.

Amazon provides sample code in C#, Java, Perl and PHP. Since all of fortylines source code is either in C++ or Python, there was a little bit of digging to do. First, I added a dependency from semilla to libcryptopp. Libcryptopp is a mature cryptographic library written in C++, with a simple licensing scheme (i.e. somewhere along the line: you are free to do whatever you want with it, don't blame the author for anything.) and available as a package in many OS distributions. Amazon can expect a signature based on a RFC 2104-compliant HMAC of a SHA256 hash, thus the C++ code to sign the request evolves around a CryptoPP::HMAC instance.

Even though libcryptopp is available though macports, I still downloaded and build it from source while figuring out how it works. There was a little gotcha and I had to create an empty pch.h file in order to compile the library.

[host]$ cd cryptopp-5.6.0
[host]$ touch pch.h
[host]$ make                # (aka make -f GNUmakefile)

After a few invalid request due to stupid programming mistakes, Amazon presented us with a user login screen. Here you will have to use a different account than the business one. For example, since no money will be transferred while playing in the sandbox, your personal amazon account works.

Verifying the return url

It has been a little more complex to get this part working right, mainly because of international standards and implementation details I had to learn along the way. Contrary to the request creation, this time I had to run the example and carefully analyze the source code.

[host]$ cd amazon-fps-2008-09-17-java-library/src
[host]$ javac -cp .:../third-party/commons-codec-1.3/commons-codec-1.3.jar com/amazonaws/ipnreturnurlvalidation/samples/ReturnUrlVerificationSampleCode.java
[host]$ java -cp .:../third-party/commons-codec-1.3/commons-codec-1.3.jar com.amazonaws.ipnreturnurlvalidation.samples.ReturnUrlVerificationSampleCode

The first hurdle came from the PEM certificate and libcryptopp. As it turns out, there are a multitude of certificate formats, PEM and DER being very common. I tried to figure out for a while which certificate format libcryptopp accepted until I finally realized libcryptopp works with keys, not certificates. Fortunately a little bit of glue around the code provided by Geoff Beier solved that problem. On a side note, here is the command to transform PEM certificates into DER certificates on the command line:

[host]$ openssl x509 -in PKICert.pem -inform PEM -out PKICert.der -outform DER

Since it is common to represent signature in ASCII text, Base64 encoding is provided by libcryptopp. Percent-encoding, also known as uri encoding or escaping is though web-specific and not implemented as part of libcryptopp. I thus had to search for a web-centric library and after looking through different CGI libraries, I stumbled uppon liburiparser which is a strictly RFC 3986 compliant parsing library for URIs written in C under the new BSD license and available on most OS distributions. The last remaining bit was to make sure a space " " are encoded as "%20" and not "+" else the signature verification will fail.

Finally, even though liburiparser-dev is available on Ubuntu 8.10, the UriQueryListA structure is not present in that version of the library so I had to build liburiparser from source on the test machine. This, in turn, required libcpptest 1.1.0 or later for the configure to complete.

[host]$ cd cpptest-1.1.1
[host]$ ./configure --prefix=/usr/local
[host]$ make
[host]$ sudo make install
[host]$ cd ../uriparser-0.7.5
[host]$ ./configure --prefix=/usr/local
[host]$ make
[host]$ sudo make install

Additional Notes

Ideally for tero, I wanted to get a form with two fields that would be passed through the Amazon Pay System back to the returnURL. Amazon though checks for any field it does not recognize and the sandbox rejected the call with the extra unknown-to-Amazon fields. It might be possible to use a more complex version of the Amazon Flexible Pay System to achieve the goal of "one form with a pay button" but in order to get a processing system running sooner than later I used a two-pages solution, encoding the form's fields into the referenceId.