Ensure that your product is delivered, even when PDT fails and the return page never shows, by introducing redundancy with IPN.
PayPal's PDT system [Hack #85] automatically redirects your customers back to your web page after they pay and sends the transaction information along with them. While this is an effective way to deliver products and services to your customers without forcing them to wait for IPN to contact your server, it's certainly not infallible. If you care about record keeping, you'll want to use IPN to record payment details into a database [Hack #82] so that you don't miss any payments.
This hack shows how to coordinate PDT with IPN to ensure that every transaction is processed by your server. The potential problem here is that when using PDT, or even the return variable feature, your customer can be redirected back to your web site before the IPN system has finished processing. You can address this issue by checking your local database to see whether or not the transaction details have been inserted yet; this refreshes the return page until the order has been processed and the IPN data has been received.
The following ASP code simply reads the transaction data passed from PDT and then checks your local database to see if the IPN has finished processing the transaction. If not, it repeatedly refreshes the page (every five seconds) until it finds the corresponding transaction in the database. Use this as your PDT return page:
<% 1. Response.AddHeader "Pragma","no-cache" Response.Expires = 0 Response.buffer = true Response.clear 2. 'Create transaction id variable Dim txn_id txn_id = Request("txn_id") 3. 'Check if IPN has been processed with database query and recordset Dim rsOrderCheck Set rsOrderCheck = Server.CreateObject("ADODB.Recordset") rsOrderCheck.ActiveConnection = MM_connPayloadz_STRING rsOrderCheck.Source = "SELECT tblOrderDetails.* FROM tblOrderDetails WHERE tblOrderDetails.txn_id = '" & txn_id & "'" rsOrderCheck.Open( ) 4. 'Count how many times you refresh the browser Dim vRCount If Request("rcount") = "" Then vRCount = 1 Else vRCount = cInt(Request("rcount")) + 1 End If %> <html> <head> <% If rsOrderCheck.EOF And rsOrderCheck.BOF Then 'ipn not processed yet %> 5. <meta http-equiv="refresh" content="5;URL= http://paypalhacks.com/pdtpage.asp?txn_id=<%=Request("txn_id")% &rcount=<%=vRCount%>"> <% End If %> </head> <body> <% If rsOrderCheck.EOF And rsOrderCheck.BOF Then 'ipn not processed yet %> 6. Please wait while we locate your order. This may take up to 30 seconds. <% Else 'ipn has been processed %> 7. IPN has been processed, insert content here. <% End If %> </body> </html>
Line 1 tells the browser and server not to cache the page content, but rather to expire it immediately; this makes sure that new content appears when it is available. Then, line 2 initializes the transaction ID, and line 3 checks it against the database. Line 5 contains the meta refresh tag, which refreshes the page automatically if the recordset is empty (e.g., if IPN has not processed the order yet).
Place your own messages on lines 6 and 7 to inform the customer that the order is still being processed and that the order is ready, respectively.
Normally, the IPN system contacts your server and completes the process in a matter of seconds after the customer pays. However, there are times when the IPN system can take longer (up to several minutes or even hours). This can be caused by load on the PayPal system, on your site, or any number of other possibilities. In the event of such a delay, the repeated refreshing of the page is likely to induce seizure in your customer or, at the very least, try his patience.
To address this issue, you might want to limit the number of times the browser is refreshed and display a message to the customer if that limit is reached (something to the effect that his order is still being processed and he should contact you or get a cup of coffee or something). Simply add the following snippet of code before the opening <html> tag:
<% 'Redirect customer to order search timeout page If vRCount => 5 Then Response.Redirect("ordertimeout.asp") End If %>
This code simply checks the number of times the browser has been refreshed (vRCount, set in the original code) and interrupts the process after five unsuccessful tries (this means that at least 30 seconds have passed since the customer was first sent to your PDT page).