I don’t think that I’ve ever done a licensing or purchasing flow exactly the same way twice. Well, a couple times, but mostly I experiment with both major changes and minor tweaks.
This time around for the Super Simple Photo Resizer Social Edition, I’m doing things more or less right. There are a few things that still could be improved, and a few things that I’m not really all that happy with, but like my friend Nick Longo says, “Release at 80%.”
However, overall, I think the current functional set that I’ve got mapped out for Photo Resizer Social Edition are pretty good, and will deliver a solid user experience. My goals on the user-experience side are:
- Make it easy to purchase
- Make it easy to get and enter the license
- Make it easy to get a lost license back
Each of those has a real upside for users, and an upside for me as well (respectively):
- I make more money
- Fewer support emails
- Fewer support emails
But I also have some other goals.
- Cut down on piracy
- Cut down on piracy
- Cut down on piracy
For a little guy like me, piracy hurts. Badly. It also hurts users, because it removes the financial incentive to continue development. You’d be surprised at just how many people like to get paid for their work, and how much they enjoy having food on the table and paying their bills. 😉
I recently got an email from someone who purchased some other software that I write. This is part of that email:
I could have pirated the software if I wished as it is easy to crack which is something you need to work on, but I thought the software is so good that I would pay you the money for a genuine copy and support its future development.
I’m glad he bought a copy! It quite literally puts food on the table.
However, I don’t want to get into piracy here. Instead, I’d like to go over some of the things I’m doing with the new licensing scheme…
The flow looks something like this (click to zoom):
Now, I’m leaving out a lot there because it would just be boring details, but that’s the basic flow.
The user gets 2 emails:
- PayPal receipt
- License email with account information
Now, in the past I’ve done licensing manually, which has been in many ways pretty easy, but it’s not been without its pain as well.
I’ve previously mentioned that I’m using the Infralution toolchain for licensing, but nothing is perfect “out of the box”, so I’ve done a good amount of customization. Having been burnt very badly in the past, I tend to purchase source code licenses whenever I can afford to. This leaves me the freedom to get down into things and tweak, fix, or just pimp-out stuff. Infralution offers source code along with their various products, which is another reason why I strongly recommend them to people whenever the topic of software licensing or payment integration arises.
I’ve also raved about how much I love DotNetNuke. It’s a fantastic CMS/portal written in ASP.NET (VB.NET in the past and now in C#), and comes with a BSDish license. However, I’ve been hesitant in the past to do much integration due to time constraints and other factors. However, this time around, I’m aiming to get things done more or less “right”.
Neither the Infralution ILS nor their IPN.NET integrate into DotNetNuke, so all of that is goodness that I need to take care of. Luckily, DotNetNuke is very well designed and integration isn’t really much of a problem. It’s really only a matter of getting it done and testing things out to make sure that they work. The trick is to try and make certain that I don’t step outside of the framework, and don’t create potential future issues for when I upgrade the DNN version. (Moving from DNN v2.1.2 to v3.x was a NIGHTMARE.)
ILS and IPN.NET take care of all the license generation and authentication, so the only thing needed is to get that into DNN. The relevant user-portion of the database is (click to zoom):
To get ILS and IPN.NET working there, I needed to create 2 new entries in the database:
- A Role for licensees
- A ProfileProperty to store licenses
No new tables needed to be added, and no table modifications were needed. So, everything fits into DNN nice and cleanly.
A Note About the Database: When installing DNN, I would highly recommend using object qualifiers. These are prefixes for the database objects (tables, stored procedures, views), and will help to guarantee that well written modules that use them do not have conflicts. IPN.NET and ILS do not support object qualifiers, and have table names that could be used by other modules. As such, I’m very happy that I always stick with the wisdom of using object qualifiers because it’s made things much safer, and has allowed me the freedom to use the DNN database for the IPN.NET and ILS Authentication Server. Later on, this will let me create custom modules or do additional programming that let me access all user data in 1 place. The advantages here are never-ending.
When a user purchases a license through PayPal, PayPal then securely contacts my IPN.NET instance, and I then process the transaction by either updating the user’s existing profile, or creating a new user profile for the user and updating it with their new license information.
Since IPN.NET has no DNN integration, everything needs to work around that, and consequently, needs to be done at a low-level. Luckily, the DNN community is quite large, and there’s a lot of code out there to help with very low-level stuff like this.
The first step to integration is to create a user, and there’s some existing code out there that helped me get jump started:
Creating a DotNetNuke User via SQL
So starting from there, I’ve got a new account for the user and it has their license securely stored.
Next, there’s a password problem there… I certainly don’t want to use a known password, so that needs to change. Again, thanks to the DNN community, I’ve got the solution:
Updating a DotNetNuke User Password via SQL
However, what password do I create? What I wanted to do initially was to create a pass phrase instead of a password. However, being the grammar-Nazi that I am, and being constitutionally incapable of generating icky passphrases, I realized after some investigation that any decent passphrase generator would take me far longer than I was prepared to spend. So… Thank you again to the .NET community, I have a ready-made password generator that creates strong passwords of an arbitrary length:
So that problem is also solved, in more detail that I’d care to bother with too! It creates passwords that look like these:
- a+7P9A*rp6F&
- So5={9WsM+g7
- Ax8$+3JwD=a2
- Ky8-i?H6Dw3&9%sS$rX54Q!f{2eLPp_7
The nice things about that password generator are that it
- Leaves out ambiguous characters, e.g. I vs. | vs. l vs. 1 or 0 vs. o vs. O
- Lets you choose the length of the password
- Mixes in upper and lower case letters, numbers, and symbols
As far as being able to remember them? Bah! Not going to happen. Which is why I wanted to have a pass phrase generator, but, you do what you can with the time you have.
Now, code-wise, there are a few points where things need to get done:
- MS SQL Server stored procedures
- The IPN.NET project
- Default.aspx.cs
- Add in the RandomPassword.cs class
- Add in the “DnnLicenseIssuer.cs” class
- Modify the “Keys.htm” file
The MS SQL Server stored procedures won’t have any impact on DNN if done right. The prerequisite PropertyDefinitions and Roles need to be there, but that’s simple enough.
The RandomPassword.cs and DnnLicenseIssuer.cs classes won’t have any impact on the IPN.NET installation, so there’s a lot of freedom there.
However, changes to the Default.aspx.cs file will break across IPN.NET upgrades unless care is taken to migrate those changes to any new IPN.NET version. Luckily, everything that needs to be done can be done in this method:
private void ProcessPurchaseItem(PurchaseItem item, int quantity)
However, if you’ve actually sold a license, and the payment is successful, there’s only 1 spot that you need to modify, which will look something like this:
DnnLicenseIssuer dnnLi = new DnnLicenseIssuer(ipn, item, keys, false); if (dnnLi.HasExistingAccount) { // Do stuff for license email } else { // Do stuff for license email }
So you can easily minimize the impact there and make future upgrades very easily.
The “Do stuff” part contains customizations that I’ve written for the license email. If users have an existing account, then they get a different email than users that I’ve created an account for.
Of course it is possible to go further, and create users irrespective of whether the license payment succeeded or failed, and follow up later, however, it’s not one of my requirements at the moment.
The End Game
There’s a plan behind all this though. I’m not going to spill the beans quite yet, and it won’t get done for some time yet. However, all of this has a forward-looking purpose, and when I finally make it to that stage, I’ll post back on what I’m doing and the results.
Until then, check back at Super Simple over the next few weeks as the new Photo Resizer Social Edition is coming out very soon.
Cheers,
Ryan