One of the most important aspects of a software business is the platform used to actually sell and deliver the software to the customers. Besides the software-specific platforms like RegNow, ShareIt, etc. there are some generic e-commerce platforms like PayPal which offer some advantages, the main advantage (for me at least) being that once a customer paid for a software, the money is already available in my account with no need to wait for the end of the month to get paid. Also, in many cases the commission per sale is lower.
What I did notice is that not many software companies are using PayPal as a software e-commerce platform because it is not tailored for software delivery. However it does offer the Instant Payment Notification API (PayPal IPN) which can be used to build such a platform.
In this article I will present a simple yet flexible web service for software delivery using PayPal IPN. It features methods for sending easily customizable emails to the customers once a payment was made, for delivering the generated license keys (also has an option to generate license keys), and for recording the sales into a database. Basically, you set some parameters in the database and in the configuration file, and you are ready to go. The source code is written in ASP.NET/C# and is available for download with the included archive.
The PayPal IPN web service
A simple ASP.NET ASHX handler is used to implement the web service. The handler receives the PayPal IPN request, does all the required checks as per PayPal documentation, and then sends the customized email to the customer containing the license key for the purchased product. It also handles license key management and records the purchase in a specific table from the database. See the PayPal.ashx file for more details.
The Licensing database
The web services includes a database with 3 tables:
The Products table stores information about the products that are being sold: name, price, currency, license key template (optional, if a license key generator is used instead of the LicenseKeys table to fetch license keys)
The Purchases tables stores information about product purchases: Product Id, Customer Name, Customer Email, Country, License Key, etc. An important element recorded in this table is the PayPal transaction id. PayPal has a habit of re-sending the IPN notifications, so it's mandatory to check if a transaction has already been processed before attempting to process it.
The LicenseKeys table acts like a pool of license keys. A license key is removed from this table and sent to the customer for each product sold. You are responsible to fill this database from time to time with new license keys (or use a 3rd party key generator instead, read on).
The templatized email sender
The TemplateMailer class included in the App_Code folder sends customized emails using email templates. An email template is a HTML document (stored in App_Data folder) which contains placeholders of the form ##PARAMETER_NAME## which are replaced with actual values before an email is being sent. The actual parameter->value replacement set is sent as a dictionary to the template mailer. The <title> tag contains the email subject. The subject can also contain parameter placeholders. Using this approach, the ##PRODUCT_NAME## and ##LICENSE_KEY## parameters are substituted with the actual values before the email is sent to the customer.
License Key Generation
The web service includes two ways to get the license keys needed to be sent to customers:
The first method is to get the needed license keys from the LicenseKeys table. You must insert sufficient keys in this table for the web service to use whenever there is a payment made for a specific product. The included database comes with some sample keys already inserted.
The second method is the use a 3rd party key generator. In this sample I used the SoftActivate Licensing SDK from
www.softactivate.com because this is what I am using for my products. This generates short license keys that are digitally signed, using elliptic curve cryptography. Unfortunately it is not free and a license for it must be purchased (it runs in demo mode in this sample) but it is quite cheap.
The selection between the two modes for a particular product id is based on whether the LicenseKeyTemplate column value corresponding to that product is NULL or not. If NULL, the first method is selected. Otherwise the second method is selected.
Logging Errors and Exceptions
If something goes wrong during a PayPal notification request, the file Logs\PayPal.txt is appended with an explanative message. This log file should be inspected from time to time to make sure that all the license keys have been successfully delivered to customers.
Hosting the web service on shared hosting environments (e.g., GoDaddy shared hosting)
Ensure that the web service works in Medium Trust environments
The service is designed to work in medium trust, however, some extra permissions are needed. The WebPermission and a specific SocketPermission are needed for the web service to function. It needs these permissions to authenticate the incoming requests to PayPal, and to send the actual emails. Luckily, GoDaddy does allow these permissions. But you must use their SMTP server for this. The GoDaddy SMTP server for shared hosting is 'relay-hosting.secureserver.net' on port 25, with no authentication.
Avoid the sent email reaching the customer's 'junk mail' folder
Let's say your email address from which you want the customer to receive email is 'firstname.lastname@example.org'. In this case, you must make sure that the domain 'mycompany.com' has an appropriate SPF record registered in DNS, such that the destination email servers can see the sender server as legitimate. On GoDaddy shared hosting, I found out that including 'spf.secureserver.net' in the SPF record of the domain 'mycompany.com' solves this issue. Here is an example SPF record that should work: 'v=spf1 include:spf.secureserver.net ~all '.
Points of interest
What I did learn during developing and testing of this web service was just how limiting some shared hosting environments are. You must be very careful avoiding the medium trust issues, avoiding your email going to the spam folders, and also avoiding duplicate email sent to customers due to the fact that PayPal resends the IPN notifications in some cases. All of these are explained above. One other interesting thing to note is how the LicenseKeys table was used as a queue (so you can atomically "pop" elements) by using some SQL Server specific features like ROWLOCK and OUTPUT directive.
Placing multiple PayPal buttons on the same HTML page (in ASP.NET)
One particularly important issue when dealing with PayPal buttons and ASPX pages is an apparently simple one: placing multiple PayPal buttons on the same page. Looking closely at the generated HTML code for a PayPal button you will notice that the button code is a HTML <form> element and its contents. But when rendering an ASPX page, the page content is already enclosed in <form> tags by the ASPX engine, so placing the PayPal button inside the generated page results in nested <form> elements, which is not allowable by the HTML standard and leads to many problems.
The solution is to disable the generation of the <form> tags by the ASPX engine, leaving only the <form> tags for the PayPal buttons. I found this useful blog post showing how to do this: http://jerschneid.blogspot.com/2007/03/hide-form-tag-but-leave-content.html .This approach also has some limitations but it largely works.
Using the code
The following steps are necessary in order to test and debug the web service on development computers:
- Unzip the files into a designated folder
- Open the web service with Visual Studio, with the File->Open->Web Site (NOT File->Open->Project/Solution !
- Edit the web.config file and add some valid SMTP settings into the designated key/value pairs
- Start the debug with the Default.aspx page. Press the "Test" button. If everything goes well, "SUCCESS" will appear and the email
- Create or edit the PayPal "Buy Now" buttons and include the web service url (the URL to the PayPal.ashx page)
in the notify_url custom variable of the button. Also, please note that the optional item number for the product on the "Buy Now" button must
be the same as the product id from the database ! For testing purposes, you can either use the PayPal Sandbox or create the product with a very
low price of $0.01. This way permits you to test the button.
The following steps are necessary in order to configure the web service for production use:
- Unzip the files into a designated folder
- Edit the web.config file and enter your SMTP server settings into the designated key/value pairs that you will find there, including host name, port, username, password (leave empty for no authentication) and whether or not the SMTP server requires a SSL/TLS connection
- Set your PayPal email address in the web.config file
- Edit the Products table in the Licensing database, entering information about your products (name, price, etc.)
- Insert some license keys into the LicenseKeys table (or use the included key generator)
- Create or edit the PayPal "Buy Now" buttons and include the web service url (the URL to the PayPal.ashx page) in the notify_url custom variable of the button. Also, please not that the optional item number for the product on the "Buy Now" button must be the same as the product id from the database !
- Edit the email templates from the App_Data folder to suit your needs
- This should be all. However, if you are using shared hosting, you need to re-create the database from the App_Data folder onto the shared hosting database server. You can use SQL Server Management Studio Express for this (the database backup/restore functions).
May 12, 2012: Initial publication.
May 16, 2012: Added some useful information on how to insert multiple PayPal buttons on the same ASPX page. Also some small corrections and additions.