Working with the PayPal API
Recently I created a sample application that used various parts of the PayPal API. Although I won't be making the complete sample available in this post, I'll share some bits and mostly my experience in working with the PayPal API.
Since many of our clients need to handle credit card payments, the PayPal API provides just about all the goodies one could imagine to implement secure online payment processing, without reinventing the wheel. Several third-party tools are out there to further enhance the developer's toolkit for orchestrating all of this, but I won't be covering those in this post.
So I sat down to write my proverbial "Hello World" (or better yet, "Hello Money!") sample application to understand the pieces and flow. Should be simple, right? Well, it was a bit more involved than I thought! Isn't everything?
Step #1 - I first needed to choose the API I was going to use. The PayPal Developer Central site gives you a number of options, but for my sample implementation the Name-Value Pair Interface seemed like the way to go because we will, in all liklihood need to impelemnt this API for a number of development platforms, not just ASP.NET. So converting my sample implementation to another coding language/platform should be fairly easy.
Step #2 - I next downloaded the NVP API, unzipped it, and looked at the samples. Wow, lots of stuff here. I opened up the handy PayPal Name-Value Pair API Developer's guide for some casual reading - yikes! There's over 200 pages of details, requirements, and various details. It's a lot of stuff to digest. But the good news is that it's all fairly straightforward.
Step #3 - at this point I must confess that I looked around for wrapper APIs that could possibly make this task even simpler. Alas, there are indeed some great third-party "kits" out there to help wrap this stuff a little neater), but I forged ahead, knowing that once I had a simple example working I could (just maybe) roll my own wrapper.
Step #4 - To make an API call, you need to create a "sandbox" account with PayPal (and this part doesn't cost anything); so I created a developer "sandbox" account at this link. If you want to invoke PayPal's "DoDirectPayment" API you will need to sign up for a a paid account - it's currently $30 per month (as of this writing) . For my sample implementation this was unnecessary as I was going to focus on the "Express Checkout" API operations. In a nutshell (and I always picture Austin Powers whenever I use that segue: "no, this is me in a nutshell.."), the Express Checkout API provides a mechanism to essentially delegate all (or most of) the details of handling the payment directly through PayPal with the additional benefit of having the process transact mostly through your site. I guess one potential downside is that your customers will need a PayPal account.
Once authenticated we call PayPal's "SetExpressCheckout" method. The main purpose of this is to get the return of the "transaction ID" wich is passed via the TOKEN parameter. I'll spare you most of the setup details (the API provides some helper classes that are useful), but we essentially pack the argument values into a "NameValueCollection" buffer like this:
encoder["METHOD"] = "SetExpressCheckout";
encoder["CURRENCYCODE"] = "USD";
encoder["PAYMENTACTION"] = "Sale";
encoder["AMT"] = amount;
encoder["RETURNURL"] = returnURL;
encoder["CANCELURL"] = cancelURL;
And this is fairly simple - naturally, we need to set up the returnURL and cancelURL back to our site. There are optional values that can be passed as well.
Step #5 - After calling "SetExpressCheckout" and getting the return value(s), I then called the "_express-checkout" method. This is essentially a "redirect" to the site and, again, in a nutshell the C# code to pack up the minimum values and redirect looks something like this:
encoder["cmd"] = "_express-checkout";
encoder["token"] = transactionID;
encoder["AMT"] = amount;
encoder["CURRENCYCODE"] = "USD";
encoder["RETURNURL"] = returnURL;
encoder["CANCELURL"] = cancelURL;
string encodedArgs = encoder.Encode();
Response.Redirect(String.Format("{0}?{1}", paypalExpressCheckoutServer, encodedArgs), true);
Step #6 - we're almost done, but not quite. Recall that we needed to set up a "returnURL" and a "cancelURL" as I called them. When we redirect to the PayPal Express Checkout server URL the buyer will authenticate and press the continue button. This will in turn redirect them back to our "returnURL" page, and it is here that we want to complete the transaction. The normal purchase cycle should include some sort of confirmation, so that's what my sample implementation does. Because we were passed the "TOKEN" parameter back to our page (or transaction ID) we can involke the GetExpressCheckoutDetails method to ascertain what we need to from the buyer, their shipping amount and any other details. For simplicity, my sample implementation creates a simple report to display and confirm the user's information:
paymentReport.Append("***** PAYMENT INFORMATION ****<br />");
paymentReport.AppendFormat("AMT={0}<br />", decoder["AMT"]);
paymentReport.AppendFormat("ITEMAMT={0}<br />", decoder["ITEMAMT"]);
paymentReport.AppendFormat("PAYERSTATUS={0}<br />", decoder["PAYERSTATUS"]);
paymentReport.AppendFormat("BUSINESS={0}<br />", decoder["BUSINESS"]);
paymentReport.AppendFormat("SALUTATION={0}<br />", decoder["SALUTATION"]);
paymentReport.AppendFormat("FIRSTNAME={0}<br />", decoder["FIRSTNAME"]);
paymentReport.AppendFormat("LASTNAME={0}<br />", decoder["LASTNAME"]);
paymentReport.AppendFormat("COUNTRYCODE={0}<br />", decoder["COUNTRYCODE"]);
paymentReport.AppendFormat("PHONENUM={0}<br />", decoder["PHONENUM"]);
paymentReport.AppendFormat("EMAIL={0}<br />", decoder["EMAIL"]);
paymentReport.AppendFormat("NOTE={0}<br />", decoder["NOTE"]);
paymentReport.AppendFormat("ADDRESSSTATUS={0}<br />", decoder["ADDRESSSTATUS"]);
paymentReport.AppendFormat("SHIPTOSTREET={0}<br />", decoder["SHIPTOSTREET"]);
paymentReport.AppendFormat("SHIPTOSTREET2={0}<br />", decoder["SHIPTOSTREET2"]);
paymentReport.AppendFormat("SHIPTOCITY={0}<br />", decoder["SHIPTOCITY"]);
paymentReport.AppendFormat("SHIPTOSTREET2={0}<br />", decoder["SHIPTOSTREET2"]);
paymentReport.AppendFormat("SHIPTOSTATE={0}<br />", decoder["SHIPTOSTATE"]);
paymentReport.AppendFormat("SHIPTOZIP={0}<br />", decoder["SHIPTOZIP"]);
paymentReport.AppendFormat("SHIPTOCOUNTRYCODE={0}<br />", decoder["SHIPTOCOUNTRYCODE"]);
In conclusion, I found working with the PayPal API to be fairly straightforward - but it's by no means a trivial thing to implement. In fact, their documentation recommends that you have a developer to help with the web site integration of their API. The many third-party tools out there that wrap some of this logic and flow would definitely be worth a look.
One observation that wasn't clear to me when working with the API through the "sandbox": when testing my sample application, I wasn't able to get a successful transaction unless I was signed in with my PayPal sandbox account with another browser window open at the same time (separate from my sample application). This sort of makes sense, but was not at all intuitive, to me at least.
Best of luck with the PayPal API. And if you are a merchant wishing to integrate a secure payment processing mechanism into your own web site, let us know!