Skip navigation

A requirement that came up for the web project I’m working on was a way to have users get a unique code that can be redeemed on the site. I was told that on a previous project they used a single code for each redeemable item, which did not work well at all. I came up with, what I think is, a pretty elegant solution.

I first looked to CD keys and product activation codes, like the ones for Xbox Live, as a base. They cover the following requirements:

  1. Each code is unique
  2. Each code can only be used once by a single user
  3. The code is made up of letters and numbers
  4. The code is a uniform length
  5. The code is formated in such a way that it is easily read and reproduced

A randomly generated number was suggested to me, but that would not satisfy the first and fourth requirement. Randomness is terrible for uniqueness. There is always a chance that the random number will collide with one that already exists, even if the range is huge. The number also might not be big enough to provide a uniform length for the key and cannot be properly formated.

So, random numbers are out. Some function is needed that provides unique values with little to no collisions possible. The solution is a hash function. Hashing functions are perfect for generating unique values of uniform length with little to no collisions. There are many types of hashing functions to choose from: CRC, MD5, SHA, Adler, Jenkins, Tiger, Whirlpool, the list goes on. The hash for this does not need to be secure, nor does it need to generate hundreds and hundreds of bits; we just need it to convert some arbitrary data into a standard format. Because of this, I chose the MD5 hashing function. I don’t care about securing data and it provides a hash of 128bits that shouldn’t collide.

Now that I have a good function to create unique numbers, I need a way to display the numbers in a human readable format that can easily be reproduced (i.e. typed in). The hash from an MD5 comes back as a  base16, or hexadecimal, number string. This is acceptable as a human readable code, but it’s quite long and easily recognizable as a hex number. If the base of the number were increased, more letters would be used and the length of the number string would decrease. Interestingly enough, a 32 character long hexadecimal number converts to a 25 character long base36 number. Base36 is perfect for a code as it includes numbers 0-9 and letters A-Z and is easily broken up into five groups of five characters.

So, the final flow is this:

  1. Choose some arbitrary data, preferably unique in some way
  2. MD5 hash it
  3. Convert the base16 hash to base36
  4. Breakup the string into five groups of five characters

And the final output looks like this: C6JZN-G41D2-8KWGO-K4GSW-444WC.

After figuring this all out, it was quite trivial to program (you can do it in one line of PHP). This method of generating unique, redeemable codes is simple, robust and actually quite elegant.

Some things to note:

  • I use a string that has an incremented number appended at the end to generate a series of codes. This provides the best case for non-colliding hashes.
  • Sometimes the base36 number is less than 25 characters long. In those cases, perpending zeros is the best solution. Adding any other number or letter would increase the chance for collision.
  • When displaying the code, convert it to uppercase as it’s easiest to read. Also, choose a font were it is easy to distinguish between O and 0 (the letter ‘O’ and the number zero), as well as I and 1 (the letter ‘I’ and the number one).