4

By using python language, what would be a clever / efficient way of generating promotion codes. Like to be used for generating special numbers for discount coupons. like: 1027828-1

Thanks

BlueRaja - Danny Pflughoeft
  • 84,206
  • 33
  • 197
  • 283
Hellnar
  • 62,315
  • 79
  • 204
  • 279

5 Answers5

8

The following isn't particularly pythonic or particularly efficient, but it might suffice:

 import random
 def get_promo_code(num_chars):
     code_chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'
     code = ''
     for i in range(0, num_chars):
         slice_start = random.randint(0, len(code_chars) - 1)
         code += code_chars[slice_start: slice_start + 1]
     return code
carl
  • 49,756
  • 17
  • 74
  • 82
GreenMatt
  • 18,244
  • 7
  • 53
  • 79
  • If I remember correctly, using random.randint is based on the WH pseudo random generator (65535^3 unique states) and is seeded by the current time when started. An attacker can actually try to synchronize his random generator with the one used to generate the promo codes, given that he is able to obtain promo codes from a series, and is roughly able to guess when the codes were generated. With today's computing power, this could end up as a feasible attack... – Dog eat cat world Jun 28 '11 at 18:18
  • Ok, I checked the random code, and it have changed since I've last had been working with it. Now it will by default seed the generator from the OS's random pool, if the OS support it. – Dog eat cat world Jun 28 '11 at 18:38
  • 4
    Might want to remove 0,1,I,O, and maybe L from that list, since they can be easily confused by the reader in a promo_code where there's no context to help figure out what the letter should be. Good solution because this kind of thing is easy to accomodate with this code. – rossdavidh Nov 18 '11 at 20:30
5

1027828-1 is extremely small. An attacker can make a ~million guesses using only a couple lines of code and maybe a few days.

This is a good way to produce a hard to predict number using python, it works under linux and windows. It is base64'ed for binary safety, depending what you are doing with it you might want to urllib.urlencode() but I would avoid base10 because it doesn't store as much information.

import os
import base64

def secure_rand(len=8):
    token=os.urandom(len)
    return base64.b64encode(token)

print(secure_rand())

As a side note this is generating a full byte, which is base256. 256^8 is 18446744073709551616 which should be large enough.

As it was pointed out base64 isn't a very good token for humans to use. Consider an alternate encoding like url-safe base64 or perhaps humanhash as they would be easier to type in.

rook
  • 66,304
  • 38
  • 162
  • 239
  • 7
    base64 is *not suitable* for a promo code. it's case-sensitive, contains punctuation (which can break urls and forum posts), is long, and easy for customers to misspell. this means stores make less revenue. see couponcabin.com for an example of how to create a code. – jspcal Jan 22 '10 at 06:20
  • 2
    For python3.6 , the secrets module can be used. – luistm Aug 21 '19 at 10:11
  • @jspcal good point, I was focused more on the security aspect of the question, not the user experience. Perhaps humanhash would be a good method here, although this aspect might be deserving of its own question entirely. – rook Aug 22 '19 at 17:08
1

if you need a 6-digit # you could do this until you found a unique value:

import random
print str(random.randint(100000, 999999))

or go sequentially...

jspcal
  • 50,847
  • 7
  • 72
  • 76
1

Try this:

import random

coupon = open("coupons.txt", "a")

def generate(amount):

    for x in range(0, amount):

        a = random.randint(1000, 9999)
        a = str(a)
        b = random.randint(1000, 9999)
        b = str(b)
        c = random.randint(1000, 9999)
        c = str(c)

        total = ""
        total = str(total)
        total = a + " " + b + " " + c

        coupon.write(total)
        coupon.write("\n")


amount = int(input("How many coupons do you want to generate: "))

generate(amount)

coupon.close()

print("\nCode's have been generated!")

You can make the coupons as long as you want. They save into a txt file called coupons.txt also.

toshiro92
  • 1,287
  • 5
  • 28
  • 42
Spirit
  • 11
  • 3
0

I've come up with an answer for this that I think is fairly clever, but relies on a couple of assumptions that might not be true for your situation.

  • The resulting code is purely numeric.
  • The resulting code is technically variable-length; [10, 20].

If these work for you, then so might this solution:

def code(seed = None):
    if (not seed) or (type(seed) != str) or (len(seed) < 10):
        seed = str(uuid.uuid4())[:10]

    code = ""
    for character in seed:
        value = str(ord(character))
        code += value

    return code[:20]

In this function, a string-typed seed is used as the base of the code. For each character in the string, convert it into its ASCII representation, then append it to the code.

By default, the function yields codes like this: '97534957569756524557', and can be invoked with any arbitrary seed. For example...

code("pcperini's answer") == '11299112101114105110'
code(str(time.time())) == '49524956514950505257'
Patrick Perini
  • 22,555
  • 12
  • 59
  • 88