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.
|Figure 2. Making Order Arrangments: Because the first step in filling an order is to make order arrangements, customer information was captured on a previous form, and this page confirms the order totals. The $627.90 total gets sent to PayPal.|
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.
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 below:
// *** Handle PayPal seperate from ProcessCard() since
// *** it requires passing off to another page on the
// *** PayPal Site. Request returns to this page a
// *** Cancel or Success querystring
if (this.txtCCType.Text == "PP" &&
// *** We have to Save Invoice so IPN confirm can
// *** see it!
// *** We'll mark it as an UNPROCESSED order.
this.invRow.Ccresult = "UNPROCESSED";
if ( !Invoice.Save(true) )
// *** Now redirect to PayPal
// *** We're processing Credit Cards
// *** The method decides whether or not CC processing
// *** takes place
if ( !this.ProcessCreditCard() )
|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.|
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 objectthis 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 the following code:
/// Redirects current request to PayPal site passing
/// a querystring. PayPal should return with
/// ?PayPal=Cancel or ?PayPal=Success
/// Routine stores all the form vars so they can be
/// restored later
private void HandlePayPalRedirection()
DataRowContainers.wws_invoiceRow rowInv =
// *** Set flag so we know we redirected to
// *** avoid spoofing
Session["PayPal_Redirected"] = "True";
// *** Save the Notes and Types so we can restore
// *** them later when PayPal returns to this page
Session["PayPal_Notes"] = this.txtNotes.Text;
Session["PayPal_ToolUsed"] = this.txtToolUsed.Text;
PayPalHelper PayPal = new PayPalHelper();
PayPal.Amount = rowInv.Invtotal;
PayPal.InvoiceNo = rowInv.Invno;
PayPal.BuyerEmail = this.Invoice.Customer.
PayPal.ItemName = App.Configuration.StoreName +
" Invoice #" + rowInv.Invno;
PayPal.SuccessUrl =Request.Url + "?PayPal=Success";
PayPal.CancelUrl = Request.Url + "?PayPal=Cancel";
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.
Wind Web Store Invoice
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. Here's the GetSubmitUrl()
public string GetSubmitUrl()
StringBuilder url = new StringBuilder();
url.Append( this.PayPalBaseUrl +
HttpUtility.UrlEncode( AccountEmail ) );
if( BuyerEmail != null && BuyerEmail != "" )
HttpUtility.UrlEncode( BuyerEmail ) );
if (Amount != 0.00M)
if( LogoUrl != null && LogoUrl != "" )
HttpUtility.UrlEncode( LogoUrl ) );
if( ItemName != null && ItemName != "" )
HttpUtility.UrlEncode( ItemName ) );
if( InvoiceNo != null && InvoiceNo != "" )
HttpUtility.UrlEncode( InvoiceNo ) );
if( SuccessUrl != null && SuccessUrl != "" )
if( CancelUrl != null && CancelUrl != "" )
|Figure 3. Logging In: The first PayPal page prompts the user to log in. Notice that all of the passed order information is displayed on this form.|
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 (see 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, so 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.
|Figure 4. Confirmation: The PayPal order confirmation reviews the final payment status and lets your customer confirm the payment.||
|Figure 5: The final PayPal confirmation page confirms the actual payment. The Continue link takes you back to the original site.|
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.|
method for the form contains a small block of code like this.
if (Request.QueryString["PayPal"] != null)
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 1
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 1
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.
event handler has only one hook to PayPal in it, which is the check for PayPal redirection mentioned earlier:
if (this.txtCCType.Text == "PP" &&
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
|Figure 6. Order Confirmation Page: The order confirmation page displays the final order content on the e-commerce site. Note that at this point, you should not confirm any items to customers.|
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 == "")
...and one in the button click that fires the redirection:
if (this.txtCCType.Text == "PP" &&
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 downloadable code
that accompanies this article.