lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]

On Mon, 15 Jun 2020 at 21:15, Wilmar Pérez <> wrote:
> I have an application collecting credit card data. Before sending the information out to the payment entity I am trying to make sure the information entered is, at least, valid. I already worked out the card number and cvv numbers but I am not so sure about the expiry date. The format I get the info is MMYY. So what I am doing is:

You should avoid posting HTML to this email list. I can view it OK but
others might not. And when I click reply it discards all the
formatting and put all the code on one line.

> I do not know if this is the most elegant solution but it works.

Your code is inconsistent, it reads "local card_expiry_date = 'YYMM'",
while "card_exp_year = string.sub(card_expiry_date, 3, 4)" suggests
the format is MMYY. My code belows assume YYMM.

> However it has a huge bug: it will fail at the end of the century. Since I only know the last two digits of the expiry date year, if a card expires in 2102 for instance and we were in 2099 my logic would wrongly reject the date (02 is less than 99).
> I am very aware that me an my simple app will likely not be around by then but it bugs me to leave it like this.
> Can anyone please suggest a proper way to do this validation?

Here's my take at the problem:

local max_lifetime = 20 -- years (cannot be more than 50)

local function check(date, now)
    if not now then
        now = tonumber('!%Y%m')) -- year and month in UTC
    date = date:match('^%d%d%d%d$') -- 4 digits, nothing else
    if not date then return false end
    date = assert(tonumber(date))
    if date % 100 > 12 then return false end -- reject bad month
    date = (now // 10000 + 1) * 10000 + date -- assume next (+1) century
    -- bring back all dates beyond max lifetime into the past
    local max_expiry = now + max_lifetime * 100
    while date > max_expiry do
        date = date - 10000
    -- if it's still in the future, it's valid
    return date >= now

card_expiry_date = 'YYMM' -- replace this with four digits
print(">", check(card_expiry_date))

And here are the test cases I used (try it before the end of the month):

assert(check('0106', 209905)) -- check century wrap

assert(not check('2005'))
assert(not check('1907'))
assert(not check('4105')) -- beyond 20 years
assert(not check('2013')) -- invalid month
assert(not check('201231')) -- invalid format
assert(not check('20dec')) -- invalid format
assert(not check('2106', 209905)) -- check century wrap

max_lifetime = 50
assert(check('7005')) -- may 2070
assert(check('7006')) -- june 2070
assert(not check('7007')) -- july 1970
max_lifetime = 20