Whenever your customer completes payment, PayPal can submit instant payment notification to your website. Almost all payment buttons (BuyNow button, Donation button, Upload Complete Cart button etc) comes with an event named IPN_Notified. You can handle this event and get all the transaction details as shown here:
So, you can track the payment and generate License Key / download link of your digital product, send email to your buyer with the License Key / Shipping information etc all from the IPN_Notified event. But wait a minute! Someone can attempt fraud on your site and spoof a transaction. Possible Fraud Attempts:
- Someone can post a false IPN message to your website.
- Someone can manipulate the price amount (0.01$), quantity etc parameters and submit a custom form (not generated by your PayPal button) on your behalf. If someone just changes the price amount, quantity etc and keep the notification URL unchanged, then, as soon as the payment is made by that manipulated form, your website will fire IPN_Notification. So, it is a fraud attempt indeed.
- Someone can keep the price amount intact, but manipulate the recipient email address to his/her email address and submit a custom form on your behalf. Whenever the fraud person submits that manipulated form, he/she will pay the exact amount you asked for, but the money will not be deposited to your PayPal account, instead it will be deposited into the fraud person's PayPal account.
- Whenever someone purchases a product from your website, PayPal shares the transaction id with the customer as well. So, the old customer can submit an IPN post to your website simulating an old purchase (with the old transaction id) so that your automated product delivery program can ship an additional copy of the digital product to your old customer.
So, what can you do to protect these fraud attempts ? Solution: You have 2 options. 1 - In your IPN_Notified event handler method, Check and avoid all of the possible scenario mentioned above by writing code.2 - Use Encrypted Payment Button and block non encrypted payments from your PayPal profile. I will explain both of the options one by one. Please review both of the options and find out which option is easier for you to adopt. Option # 1 : Checking and avoiding the fraud attempts within IPN_Notified event by writing code:Whenever an IPN message is submitted to your website, this control captures that message and query the original PayPal server about the posted transaction. PayPal Server checks the transaction details and confirms the control if the IPN message was really sent by PayPal server. If PayPal really sent the IPN then, you will get e.IPN.Status = PayPalIPN.StatusCodes.Verified. If PayPal server cannot recognize the message, it will return the Status = 'Invalid' and so you will get the value e.IPN.Status = PayPalIPN.StatusCodes.Invalid. So it is very much important that you enclose your product shipping logic with the if statement that checks if the IPN status is Verified or not as shown here: (Even if you are using Encrypted Payment button, you need to verify an IPN message as shown here)
Now, in order to protect from Fraud Attempt # 2 (price manipulation), compare the (price of the item x quantity) from your database with the paid amount from IPN. Say for example, you have your product database as shown below:
|Item Name||Item Number||Unit Price|
|MS Word for Dummies||Digital Book # 1||23.45|
|Learning ASP.NET in 7 days||Digital book # 2||45.23|
So, get the Item Number and Quantity information from IPN, query your database for the unit price of the associated item number and evaluate the expression as shown below:
So far, we protected from the 2 possible fraud attempts. Now, we need to check if the recipient email address and business email address was manipulated or not as mentioned in possible fraud attempt # 3. Please check the following snippet for the solution.
Finally, we need to protect the fraud attempt # 4, 'attack with old transaction id'. It is the best practice to record all IPN data in your database every time the IPN_Notified event is fired. So, in your product shipment logic, query your database if the posted IPN's transaction id is already there in your database. If so, reject the IPN and do not ship your product. It is not that simple to decide. If your customer pays using eCheck, then, you will get another IPN when the eCheck is cleared and the new IPN will have the same transaction id. So, you should also check if the transaction type was eCheck or not. Please consult the class library reference which contains complete class diagram of all IPN Notified event arg, so you will be able to locate the right property that will give you information about Transaction Type (eCheck, Authorization etc). Anyway, for simplicity, I am updating the snippet with the transaction id query so that you can avoid fraud attempt # 4.
Option # 2 : Use Encrypted Payment Button and block non encrypted payments from your PayPal profile:
Encrypted Payment button is an HTML form rendered by the Control where all the sensitive fields (item_name, item_number, quantity, amount etc) are encrypted using your certificates. You can learn how to generate encrypted button from this chapter. Once you turned on Encrypted Payment Button generation from your BuyNow button (or other buttons), you should Block Non-encrypted Website Payment from your PayPal profile. By doing so, you are confirmed that, when IPN_Notified event is fired, it is very your BuyNow button generated transaction where no one could manipulate the fields and so you can safely trust the IPN data you receive from the event argument object. But, hey ! Using Encrypted Payment Button wont restrict anyone to post false IPN to your IPN handling URL. So, you must always check if the IPN message was really sent by PayPal server. (Fraud Attempt # 1, remember ?). So, there is no alternative to use following snippet no matter either you use Encrypted Payment button or not.
Comparison between the proposed 2 options:
For most of the scenario, option #1 is good enough and performance friendly, but less secured than option # 2. Option #2 is good as no one can see what parameters are being passed to PayPal. Also no one can create their own button and submit to PayPal on your behalf. But it has a price. Generating Encrypted Button on the fly can be slower depending on the processor speed of your server. But slow does not mean bad. You can show a progress dialog box at loading time in your website conveying some waiting message so that your website visitor wont mind waiting for few more seconds. With option #2, the page load time can be significantly considerable if you have many PayPal controls on your page. For example, if you have placed Add to Cart button within a grid view where grid view has many rows. In that case, the page can load slower than your expectation and so Option # 1 can be a good choice. But please do not take this message as a decision. Many web hosting server has very high speed processor. So, if you like to use Encrypted Payment buttons, then, first try in your hosting server and see if you can be happy with the loading time.