E-commerce applications require user-friendly mechanisms for payment.
Although e-commerce sites usually use full credit card processing gateways, offering PayPal for payment provides an option for those who don't want to send credit card information across the Internet. If you run a Web shop that uses direct credit card processing and you want to integrate PayPal, you'll find that using PayPal as a processing service is not as straightforward as using a payment gateway. In this article, I'll describe how you can minimize the external PayPal interaction and work the PayPal payment into your order processing workflow to provide a seamless interface using ASP.NET and C#.
Payment processing on the Web is a big industry and there are myriad services and providers allowing you to process credit cards and bank cards over Internet connections. If you're running your own e-commerce applications, you generally want to use an API so the payment processing can be directly integrated into your own application. This makes sure that the user of your application sees a consistent Web interface. As far as the user is concerned, they never leave your site; your Web back end makes the remote calls against the payment processing service and returns the success or failure of the request.
PayPal is quick to set up, doesn't have any startup costs, and doesn't require reams of paperwork, like other merchant services.
Payment processing for credit card providers provides two types of interfaces: an API- or HTML-based payment interface. The API allows direct calls from within another application and HTML-based access whisks users off to the provider's site, which handles the payment detail entry and processing and returns users back to your site. For a professional-looking site, API-based interfaces are a better choice. But HTML-based interfaces are often cheaper and in some cases?like PayPal?are unavoidable, as it is the only interface offered.
Why PayPal?
On my own Web site's West Wind Web Store, I use API-based processing with Authorize.NET to perform payment processing. This works great: it's fast and provides an integrated mechanism with seamless integration. I sell software products online and from time to time, there are customers who are still squeamish about using their credit cards online?often customers from Europe?who would rather use an alternate payment mechanism. So a while back, I looked at integrating PayPal into my store's software.
PayPal may not be the first choice for order processing in a custom e-commerce application, but it does provide another payment option for your customers. Some customers (at least in my business) have a hard time finding the right payment mechanism and PayPal has been the answer in a number of cases.
I've been surprised how many orders I get in my store through PayPal. When I originally hooked this up a few months back, I thought of it as a novelty and another bullet item for the store's software, but there are quite a few people left in this world who don't trust the Internet (or Internet vendors, for that matter) with their credit cards. PayPal is nice in that they provide the ability to keep the credit card from any vendor at all?you only have to worry that PayPal itself stays safe and honest.
On the merchant front, I've found a lot of people using PayPal functionality as their main payment mechanism instead of merchant services. A lot of small operations or personal sites don't want to deal with the hassles of setting up merchant services. I can't blame them?most merchant providers, or at least the resellers, are crooks trying to make a quick buck off the unsuspecting vendor. Getting set up also takes some amount of paperwork and often can take weeks. Luckily, that seems to be changing somewhat with better and more consistent gateway services appearing with reasonable prices (my experience with MerchantPlus and Authorize.Net has been a real joy). Still, with PayPal, you hit the ground running as soon as you have configured your account and provided a bank account. You can be up and running on the same day.
On the down side, PayPal is a purely HTML-based Web interface. If you need to integrate payment processing into a non-browser-based application, PayPal is not an option because PayPal works exclusively through the Web browser interface. So if your business also takes phone orders and has an offline POS system, PayPal is a hassle because you'd still have to enter orders on the Web. PayPal also has rather high sales percentages that are much higher than merchant accounts (unless you are super high-risk). Always be sure to do the math when comparing merchant providers. A little time up front may save you a lot of money in the long run?percentages are what often make or break your sales margins.
PayPal as the Middle Man
PayPal acts as a middle man between the buyer and seller so that the buyer never has to expose payment information to the seller directly. Instead, the buyer registers a credit card or bank account with PayPal, maps the account to an e-mail address, and then uses PayPal to make purchases. The advantage here is that the seller never gets the buyer's payment information and therefore can't abuse it (accidentally or otherwise) in any way.
So what do you need to accept payments? The nice thing about PayPal is that it's easy to sign up to accept payments. If you already have a PayPal account for buying things online, you are already set up to receive payments. All you need is to have a bank account so payments can be deposited into it. That's it!
How PayPal Works
PayPal is not a payment gateway and they don't provide a direct API interface to their service, except for very large/high volume customers. Instead, like the HTML-based services mentioned earlier, the buyer has to go to the PayPal site, log in, and accept the payment request. This process can be automated by configuring PayPal to return to specific URLs in the case of success or failure and providing a callback confirmation URL that allows you to verify that PayPal processed a payment on your behalf. This configuration is done via POST data or query strings passed to the PayPal pages.
The PayPal process requires a browser-based Web interface and you must redirect from your site to PayPal to process payments.
PayPal supports a variety of order and inventory management features, but in an e-commerce site scenario such as mine, all of this is handled by my own Web application?all I want PayPal to do in my Web store is process the final order amount.
As you might expect from this description, this process is fairly involved if you want to completely integrate the payment into your application. The process goes like this:
- The user picks items in the store's Web site.
- The user fills out order information and goes to the store's order page.
- The store provides the order total.
- The user accepts the order of multiple items and the final amount.
- The user selects PayPal as the payment type.
- The user gets redirected to the PayPal site.
- The PayPal site takes the user through the payment process.
- On success, PayPal redirects the user's browser back to the store's order confirmation page.
- On failure, PayPal redirects the user's browser back to the store's order form (and redisplays order information).
- The store confirms with PayPal to process the order and PayPal confirms that the order went through.
Figure 1 shows the general flow of the process.
This process looks pretty complicated and although there is quite a bit of back and forth, it's pretty straightforward once you understand the flow. You need two pages on your site to make this work: the Order page and the Instant Payment Notification Callback page.
Order Page
The order page form is the jump off and return point. In my example, it's called OrderForm.aspx. This page needs some internal logic to redirect to PayPal if PayPal is selected as the payment type. I'll provide you with a helper class that makes this a snap by simply setting a few properties.
The same OrderForm.aspx also serves as the return page from PayPal on completion. When you redirect to PayPal, you provide URLs for successful and cancelled operations, and these URLs point back to this page with special query string parameters (like: OrderForm.aspx?PayPal=Success). I like to use a single page here to keep the PayPal-specific logic in one place, but you can also use separate pages.
Unlike credit card processing, it's somewhat difficult to keep PayPal logic inside of business objects because this interaction relies on the HTTP mechanisms and page callback, so some of the routing logic ends up embedded as part of your Web UI layer (ASPX code-behind). The helper class abstracts most of the internal knowledge but some operational code still squeaks into the ASP.NET page.
For my Web store application, it also makes sense to return to this page in order to complete the order process. The order form can simply pick up right where it left off before redirecting to PayPal. In short, returning makes PayPal integration easy with minimal changes to the existing processing.
It's important to understand that a return from PayPal does not guarantee that the order went through. It's easy to spoof the return URL you sent to PayPal because it's visible on the query string (or if you POST in the POST buffer). Therefore, a user could simply type in the confirmation URL directly and you should not yet confirm the order. You can manually check for orders on the PayPal site or wait for PayPal's confirmation e-mails, which let you know for sure that the order was processed in a manual way.
Instant Payment Notification Callback Page
To automate the confirmation process, PayPal can optionally ping you at another URL with order completion information. This is where the second Instant Payment Notification (IPN) confirmation page shown in Figure 1 comes in. IPN uses a Web-based callback mechanism that calls a pre-configured URL on your site. The IPN must be enabled on the PayPal site and when enabled, the IPN sends a confirmation to you at this URL after the order is processed. PayPal expects a return from you within a certain timeframe (a few minutes) and returns a response to confirm that the customer has paid. To do this technique, you have to POST the data back to PayPal by echoing back all the form data that PayPal sends to you.
The IPN is optional, but it's a requirement if you need to confirm your orders immediately with your customers. For example, on the West Wind site, new software purchases are confirmed by sending an e-mail with download URLs to obtain the software almost immediately. For credit card transactions, the e-mail is sent immediately as part of the Confirmation.aspx page. When the order is verified and complete, e-mail confirmations are sent. For PayPal orders, this cannot be done through the confirmation page, so the confirmation page just displays the completion notice. The IPN return page is then used to send the final e-mail confirmations for the download links.
A Walk Through the Process
Let's look at the Web store process when users pay with PayPal using the IPN mechanism described above. The process starts out on the order form of the main Web store application. The shopping cart is managed by the e-commerce application and the order form is the final step in the order process. A previous form has already collected customer information, so the page shown in Figure 2 only collects the payment information plus any informational pieces from the customer.
For PayPal processing, the customer selects the PayPal payment method and clicks on Place Your Order to proceed. For normal credit card processing, this page contacts the credit card company's processor directly and confirms the order. This process is linear and happens on a single page request.
Working with PayPal's HTTP-based redirections and callbacks make it difficult to isolate payment processing into your business layer. Instead, you end up with some processing logic in your ASP.NET UI layer.
But for PayPal, you need to intercept the request, go off to PayPal, and return back to this page after PayPal has taken the customer's money. The OrderForm.aspx page intercepts the straight order process and instead redirects to PayPal using the code shown in Listing 1.
The if block checks for PayPal payments and a flag that makes sure you truly want to redirect rather than handle a return from a PayPal page. If so, HandlePayPalRedirection() is called and performs the redirection by building up a URL that gets sent to PayPal. The method also stores several of the current form's input values to a session object?this is so that when you return from PayPal to this page, the data the user just typed in can be filled back in. In other words, the relevant state of this form is saved, and it will restore when you return to the page. The redirection process in HandlePayPalRedirection() is shown in Listing 2.
Notice the use of the PayPalHelper class, which I'll describe a little later. This reusable class provides a simple interface to perform some parsing tasks. In this case, the GetSubmitUrl() method is used to create a query string that returns a fully qualified PayPal URL that takes your customer to the PayPal site with your account and payment information selected.
The URL generated looks something like this.
https://www.paypal.com/cgi-bin/webscr?
cmd=_xclick&business=YourEmail@YourCompany.com
&email=CustomerEmail@Company.com
&amount=310.96&image_url=https://www.west-
wind.com/images/wwtoollogo_text.gif&item_name=West
Wind Web Store Invoice #8d5c9631&invoice=8d5c9631&return
=https://www.west-wind.com/wwWebStore/OrderForm.aspx?
PayPal=Success&cancel_return=https://www.west-
wind.com/wwWebStore/OrderForm.aspx?PayPal=Cancel
The content needs to be UrlEncoded, which is also handled by the above class. The GetSubmitUrl() method is pretty simple; it does little more than take the various property values set on the class and builds the query string from it. GetSubmitUrl() is shown in Listing 3.
The result from this method is then used in a Response.Redirect() that sends the customer off to PayPal's server. In Figure 3, you can see the transition. Notice that the redirection took you to a semi-custom page that has all of the order information and the company account information in it.
At this point, the user enters a password or, if there's no PayPal account yet, creates a new one. Clicking the Continue button brings up the PayPal order confirmation page (Figure 4).
Once the user clicks the Pay button, a final confirmation page is displayed confirming that the payment was made. This is the last page in the batch and clicking the Continue button on the form (shown in Figure 5) returns you to the main application.
The return click uses the success return URL provided as part of that long query string shown earlier. In this case, the page returns to the URL, https://www.west-wind.com/wwStore/orderform.aspx?PayPal=Success, in the case of my Web store.
The West Wind Web Store then checks for the PayPal query string value and, based on that value, handles the processing of this order. Remember that when the request was originally sent to PayPal, some information was saved about the order form; the field values were entered for the various form controls. The field values were captured to session variables and now they can be retrieved and reassigned to the appropriate controls to make the form appear very much like the form before switching to PayPal.
In order to confirm payments, make sure you verify PayPal return URLs by using IPN or waiting for manual confirmation. Otherwise, there's no guarantee your URL wasn't spoofed.
The Page_Load() for the form contains a small block of code like this.
if (Request.QueryString["PayPal"] != null)
this.HandlePayPalReturn();
The HandlePayPalReturn() method then performs the page reconstruction by restoring the saved values from the session back into the form and simulating a button click operation. In short, the state of the page is restored to what it was prior to going off to PayPal and the button click makes it behave just like a local order process. Listing 4 demonstrates how this works.
Notice the PayPal_Redirected session variable that was set in HandlePayPalRedirection(). This flag insures that the page was fired from a PayPal request. Because you want to store this value server side, you can minimize spoofing attempts fired without actually initiating an order. Once you know that you are on a legitimate redirection, you can remove the flag. You can also set an internal flag, PayPalReturnRequest, to true, so that any code following this method call knows that the order is already processed. Specifically, it makes sure that you don't process credit cards in addition to PayPal, and more importantly, that you don't redirect back to PayPal again.
The rest of the code in Listing 4 deals with restoring the various text boxes with the saved values from the session object. I recommend that if you have more than a few values, you use an object to store in the session rather than single values, but since I only had four values including the flags, I didn't bother. Using an object makes assignments and retrieval easier as it preserves type information for the fields, plus it keeps the session object tidier.
Finally, if all goes well, you call the btnSubmit_Click() event handler directly to imitate a form submission. Because you've reassigned the various textboxes from the session, the logic can now work as if you were dealing with a POST request even though PayPal returned as a GET. You can assume that you've successfully processed a PayPal payment and act accordingly. But you still haven't verified that the PayPal transaction actually retrieved payment.
The btnSubmit_Click() event handler only has one hook to PayPal in it, which is the check for PayPal redirection mentioned earlier:
if (this.txtCCType.Text == "PP" &&
!this.PayPalReturnRequest)
this.HandlePayPalRedirection();
This time around, the this.PayPalReturnRequest flag will be true and you'll skip right past the redirection and proceed with the rest of the order processing. The business logic of this page is smart enough to know not to send PayPal orders to credit card processing, but if you needed to, you could check either CCType or the PayPalReturnRequest flag to skip over any code you don't want fired when processing a PayPal return.
Otherwise, the order processing is left completely unchanged and the end result of this operation is that the order gets processed and the customer sees an order confirmation page, as shown in Figure 6.
The order confirmation page should not disburse any products or access to services yet. If you recall, the return URL from PayPal is provided as part of the URL, so it's easy for anybody to see this URL and simply type it in even if the PayPal order did not go through.
You can also POST information to PayPal, which makes the request a little less prone to spoofing. But POSTing is significantly more complicated using ASP.NET because an ASP.NET page can't easily POST content to another page. Even then, it's much more work to set up POST variables properly on a page. It's better to live with the possible tempering and rely on the IPN confirmation, or even manual checking on PayPal, as the final evidence that the order went through.
Note that this process has hooked PayPal processing into the ASPX page with two simple if blocks: one at the end of the Page_Load() that handles a return request from PayPal:
if (Request.QueryString["PayPal"] != null &&
this.txtCCType.SelectedValue == "")
this.HandlePayPalReturn();
and one in the button click that fires the redirection:
if (this.txtCCType.Text == "PP" &&
!this.PayPalReturnRequest)
this.HandlePayPalRedirection();
The rest of the code is completely isolated from the order-processing logic. To help you understand the bigger picture, I'm providing the complete application-specific Orderform.aspx in the download that accompanies this article.
Instant Payment Notification
If you are processing orders only occasionally or you don't immediately fulfill orders online, you might be finished at this point. But unless you check with PayPal or PayPal notifies you, you have no guarantee that PayPal actually processed the specified amount.
In order for your application to be securely notified of transactions on the server, you need to implement IPN. In a nutshell, IPN provides a Web-based callback mechanism for your application to independently receive confirmation from PayPal that a transaction was made to your account. PayPal POSTs back all the order information too, so you can verify that the order was actually confirmed for the amount that you originally asked. (Another possible scam is to exit the current order and come back in and transfer money for a different amount. You'd get a confirmation but it won't be for the right amount!)
Because PayPal calls this URL directly, the URL for this page is not apparent to the user and is therefore more reliable. PayPal also sends information about the order back to you, so that you can double check that the important information (like the invoice number and order amount) match what you thought the customer should pay for.
IPN must be explicitly enabled on the PayPal site. The URL is configured like this: log on to your account, go to Profile and then Selling Preferences. Choose Instant Payment Notifications. Check the enable box and provide a URL on your site to which you want the IPN to post. In the case above, I want to have PayPal post back to my site to a special page called PayPalIPNConfirmation.aspx. You can see what a typical IPN POST looks like in Listing 5.
As you can see, just about everything that was originally entered comes back to you, plus some PayPal internal stuff. The things you probably want to look at are the invoice and payment_gross values, which let you quickly validate that what you sent in comes back to you.
This IPN POST-back page should be a non-visual ASPX page (or an HttpHandler). You can remove all HTML code other than the <%@PAGE %> directive. The code in this page receives what amounts to a POST-back from PayPal that echoes the order information. The implementation for my Web store is shown in Listing 6.
Most of this code is application-specific and deals with confirming the invoice once the callback has been verified. If the order is okay, a confirmation e-mail?and in some cases, download links?can be sent to the customer (using Invoice.SendEmailConfirmation()). If it fails in any way, the error is logged into an application log.
The real work related to IPN is handled in the PayPalHelper class and the IPNPostDataToPayPal() method. IPN works by having PayPal post back all the order information in POST format. The IPN protocol requires that your handler return all the FORM variables PayPal posts by posting them back to PayPal. In other words, you need to echo back all the POSTed form variables to PayPal, plus add a cmd POST value with the value _notify-validate to let PayPal know you're returning an IPN signature. Listing 7 shows the code in the PayPalHelper class that accomplishes this task.
The code reads the incoming Form variables and echoes them back by looping through the Request.Form collection and writing each key back into the PostBuffer of the HTTP client. The code starts by performing a couple of validations against the incoming POST data. Specifically, check the PayPal account to which you're receiving and the order amount to make sure that there isn't any sort of spoofing going on (perhaps the user decided to place a separate order with a different order amount). If you need to do additional checks, you can do that too by looking at the Form variables in the PayPalIPNConfirmation.aspx code.
The code then loops through all of the incoming Form variables and posts them right back to PayPal. I'm using a wrapper class around the .NET WebRequest class called wwHttp to simplify the posting process. The wwHTTP automatically URL-encodes form variables and returns a plain string result. You can find this class described and available for download in a previous CoDe article, Retrieving HTTP content with .NET article (Code Magazine May/June 2002, by Rick Strahl (http://www.code-magazine.com/SearchResults.aspx?search=Retrieving%20Http). The class is also provided with the accompanying source code.
On success, PayPal returns a simple value: VERIFIED. Now you have programmatically verified that the order is valid. The result is then returned to the IPN confirmation ASPX page, which confirms the order.
Debugging IPN
Testing the IPN verification is important. Keep in mind that IPN works by having PayPal call back your application at a specific URL. This means that if you're testing and you want to use the debugger locally, you have to work on a publicly accessible IP address that an external client (PayPal) can reach. If you sit behind a firewall, proxy, or even a DHCP connection, it's not going to work. To make things worse, I couldn't get IPN to work with the SandBox; it only worked for me on the live site. The SandBox account had no IPN configuration options, which means you have to run live transactions to test/debug IPN. Make sure you place SMALL orders and don't accidentally order your most expensive items. The PayPal commission might kill ya! <g>
If you can't access a public IP to debug, you have to work the old fashioned way with messages dumped into Trace or Event logs or by sending yourself e-mail with status messages from within the IPN code. IPN is great, but plan on spending some time debugging this interface half blind if you don't have an open IP Address.
Summary
PayPal may not be the first choice for order processing in a custom e-commerce application, but it does provide another popular payment option for your customers. Since I implemented PayPal support in my Web store, I've been surprised how many orders come in using PayPal payments.
In this article, I've pulled together all the pieces you need to integrate PayPal into your own e-commerce front end seamlessly. The process is straightforward once you understand the interaction of the various pieces that PayPal provides and with the helper classes provided, integration into your own ASP.NET-based applications should be a snap.
If you have questions or comments about this or related topics, you can post a message in the White Papers section of our message board. Source code for this article is at http://www.west-wind.com/presentations/paypalintegration/paypalintegration.zip.