17

What's the best way to generate unique codes to use as coupon codes?

Thanks.

donald
  • 23,587
  • 42
  • 142
  • 223
  • Name them? I mean, some human has to concieve of the coupon and what it does. Have them provide a name. Just make sure the name hasn't been used before. – Mark Thomas Dec 29 '10 at 22:06
  • possible duplicate of [Generating unique, hard-to-guess "coupon" codes](http://stackoverflow.com/questions/22333237/generating-unique-hard-to-guess-coupon-codes) – baxang Dec 22 '14 at 05:15

6 Answers6

39

In Ruby's standard library there is SecureRandom for this:

SecureRandom.hex(3)

The length of the output is double of what the length input specified.

Ryan Bigg
  • 106,965
  • 23
  • 235
  • 261
  • 1
    +1 for having security in mind. A user being able to guess or generate valid coupon codes could be anywhere from annoying to really bad. The OP needs to be concerned not just with the cryptographic strength of the random number generator, but with the ratio between the size of the "key space" and the number of coupons that will potentially be issued. The lower that ratio, the more likely someone will be able to guess a coupon code at random. – Wayne Conrad Dec 30 '10 at 10:15
  • 1
    Removed as of rails 3.2 - use simply SecureRandom from stdlib. see https://github.com/plataformatec/devise/pull/1087 – Ivan -Oats- Storck Apr 19 '12 at 20:58
  • 1
    Updated the answer to reflect that. – Ryan Bigg Apr 22 '12 at 02:09
7

What you want is to generate a GUID. See here:

guid generator in ruby

Community
  • 1
  • 1
zsalzbank
  • 9,685
  • 1
  • 26
  • 39
4

Maybe try this, seems to be more proof than just generating some random key. They say: UUID generator for producing universally unique identifiers based on RFC 4122 (http://www.ietf.org/rfc/rfc4122.txt). http://rubygems.org/gems/uuid

gem install uuid
cd /myproject/path
uuid-setup

In your code

require_gem 'uuid'
my_unique_id_var = UUID.new

Reference: http://railsforum.com/viewtopic.php?id=12616#p44545

dombesz
  • 7,890
  • 5
  • 38
  • 47
3

You can do something like this too:

chars = ('a'..'z').to_a + ('A'..'Z').to_a
def String.random_alphanumeric(size=16)
    (0...size).collect { chars[Kernel.rand(chars.length)] }.join
end

But then you would have to compare against a database to make sure it is not used yet.

Bachan Smruty
  • 5,686
  • 1
  • 18
  • 23
zsalzbank
  • 9,685
  • 1
  • 26
  • 39
2

Note: There is a same question

Recently I wrote coupon-code gem that does exactly the same thing. The algorithm borrowed from Algorithm::CouponCode CPAN module.

A coupon code should not only be unique, but also easy to read and type while it still is secure. Neil's explanation and solution is great. This gem provides a convenient way to do it and a bonus validation feature.

>> require 'coupon_code'
>> code = CouponCode.generate
=> "1K7Q-CTFM-LMTC"
>> CouponCode.validate(code)
=> "1K7Q-CTFM-LMTC"
>> CouponCode.validate('1K7Q-CTFM-LMTO') # Invalid code
=> nil
Community
  • 1
  • 1
baxang
  • 3,627
  • 1
  • 29
  • 27
2

If you don't want to waste comparing against the database (not a super expensive operation), you can guarantee that Time is always unique because it only occurs once

md5(Time.now.to_i.to_s+Time.now.usec.to_s)
sethvargo
  • 26,739
  • 10
  • 86
  • 156
  • I like this one, although SHA2 would be even more secure (but slower). – jschorr Dec 30 '10 at 18:47
  • 1
    Unless you pair this with the user's name or id you'd run the risk of race conditions - two processes generating the same time/coupon code for different users. It'd be rare but possible and very hard to debug. Try `md5(current_user.id.to_s + Time.now.to_i.to_s+Time.now.usec.to_s)` instead. – jim Sep 26 '11 at 05:06
  • Time is not unique. See Daylight Savings Time; you get a whole hour worth of repeated time once a year. And then there's the matter of being able to change the system clock. Use a GUID/UIID – Will Green Aug 09 '13 at 12:14