TransWikia.com

CS50 Credit Card Validation

Stack Overflow Asked on November 18, 2021

#include <stdio.h>
#include <cs50.h>

int main(void)
{
long cc = get_long("Credit Card: "); // gets input

long len = 0;           //intialized length
long x = cc;            // set 2nd variable = to cc to prevent manipulation of cc

while (x != 0)     // length count loop while x is divisable loop will continue will be stored as len
{
    x = x / 10;
    len++;
}


if ((len != 16) && (len != 15) && (len != 13))  //Checking for length to see if number matchs possible postive outcomes
{
    printf("INVALIDn");
    return 0;
}
//pull 2nd to last and then every other digit
long cc_num1 = ((cc % 100) / 10);
long cc_num2 = ((cc % 10000) / 1000);
long cc_num3 = ((cc % 1000000) / (100000));
long cc_num4 = ((cc % 100000000) / (10000000));
long cc_num5 = ((cc % 10000000000) / (1000000000));
long cc_num6 = ((cc % 1000000000000) / (100000000000));
long cc_num7 = ((cc % 100000000000000) / (10000000000000));
long cc_num8 = ((cc % 10000000000000000) / (1000000000000000));

cc_num1 = (cc_num1 * 2);                    //Multiply digits pulled above by 2
cc_num2 = (cc_num2 * 2);
cc_num3 = (cc_num3 * 2);
cc_num4 = (cc_num4 * 2);
cc_num5 = (cc_num5 * 2);
cc_num6 = (cc_num6 * 2);
cc_num7 = (cc_num7 * 2);
cc_num8 = (cc_num8 * 2);

cc_num1 = ((cc_num1 / 10) + (cc_num1 % 10));    //split double digits and add to signles
cc_num2 = ((cc_num2 / 10) + (cc_num2 % 10));
cc_num3 = ((cc_num3 / 10) + (cc_num3 % 10));
cc_num4 = ((cc_num4 / 10) + (cc_num4 % 10));
cc_num5 = ((cc_num5 / 10) + (cc_num5 % 10));
cc_num6 = ((cc_num6 / 10) + (cc_num6 % 10));
cc_num7 = ((cc_num7 / 10) + (cc_num7 % 10));
cc_num8 = ((cc_num8 / 10) + (cc_num8 % 10));

long cc_sum = cc_num1 + cc_num2 + cc_num3 + cc_num4 + cc_num5 + cc_num6 + cc_num7 + cc_num8; // add sum of number above

long cc_num1x = ((cc % 10) / 1);                      //pulls last digit from card then everyother digit
long cc_num2x = ((cc % 1000) / 100);
long cc_num3x = ((cc % 100000) / 10000);
long cc_num4x = ((cc % 10000000) / 1000000);
long cc_num5x = ((cc % 1000000000) / 100000000);
long cc_num6x = ((cc % 100000000000) / 10000000000);
long cc_num7x = ((cc % 10000000000000) / 1000000000000);
long cc_num8x = ((cc % 1000000000000000) / 100000000000000);


long cc_sumx = cc_num1x + cc_num2x + cc_num3x + cc_num4x + cc_num5x + cc_num6x + cc_num7x + 
               cc_num8x; //adds last and everyother digit together

long sumofsums = cc_sum + cc_sumx;  // adds sums of both sums created


if ((sumofsums % 10) != 0)   // Luhn’s Algorithm results will close if not met
{
    printf("INVALIDn");
    return 0;
}

{
    if (len == 15)                       // checks for AMEX by using length then first 2 digits 
    {
        long ax = cc / 10000000000000;
    
        if ((ax == 34 || ax == 37))    
        {
            printf("AMEXn");   
        }
        else
        {
            printf("INVALIDn");
            return 0;
        }
    }    
}

long mc = cc / 100000000000000;
long v = cc / 1000000000000000;
long v2 = cc / 1000000000000;

if (len == 16)                      // Checks for MC and Via (16 digits) by length then first 2 digits MC or 1 visa
{
    if ((mc == 51 || mc == 52 || mc == 53 || mc == 54 || mc == 55))    
    {
        printf("MASTERCARDn");   
    }
    else if (v == 4)
    {
        printf("VISAn");
    }
    else
    {
        printf("INVALIDn");
        return 0;
    }
}

if (len == 13)                                //Checks 2nd Visa length 13 digits then 1st digit 
{
    if (v2 == 4)
    {
        printf("VISAn");
    }
    else
    {
        printf("INVALIDn");
        return 0;
    }
}

}


There has to be a better way then the way I am planning to do this. The Length count loop is fine until 10 digits but then pulls random numbers.

The every other digit formula seems like it can be done through recursion but I am blanking on that. Since the number is limited to 16 at most the formula I am using seems to work.

  1. Determine if card is 15 || 16 || 13 digits if not mark In valid in IF Else loop
  2. Use CC check sum formula If else loop (In valid if it doesn’t meet Criteria)
  3. Look at 2 Starting numbers to determine AX, MC or Visa
    #include <stdio.h>
    #include <cs50.h>
    #include <string.h>
    
    
    int main(void)
    {
        long cc = get_long("Credit Card: " );  // gets input
    
    
        int len = 0;           //intialized length
        int x = cc;            // set 2nd variable = to cc to prevent manipulation of cc
    
        while(x != 0)      // length count loop while x is divisable loop will continue will be stored as len
        {
            x = x/10;
            len++;
        }
    
        printf("%in", len);     // REMOVE !!!!!!!!!!! BUG TEST
    
                                              //pull 2nd to last and then every other digit 
        int cc_num1 = ((cc % 100)/10);
        int cc_num2 = ((cc % 10000)/1000);
        int cc_num3 = ((cc % 1000000)/(100000));
        int cc_num4 = ((cc % 100000000)/(10000000));
        int cc_num5 = ((cc % 10000000000)/(1000000000));
        int cc_num6 = ((cc % 1000000000000)/(100000000000));
        int cc_num7 = ((cc % 100000000000000)/(10000000000000));
        int cc_num8 = ((cc % 10000000000000000)/(1000000000000000));
    
    
        printf("%i %i %i %i %i %i %i %i", cc_num1, cc_num2, cc_num3, cc_num4 , cc_num5, cc_num6 , cc_num7 , cc_num8 );
    
    
    }
    

One Answer

Let's acknowledge the elephant in the room first.

long cc = get_long("Credit Card: " );

...

int x = cc;

The C standard specifies long to be at least 32 bits, whereas int must be at least 16 bits. The actual values are dependent on your system and your library implementation of course. But more often than not, long will be capable of storing more bits than an int. As is the case here. This means "numbers with more than 10 digits", essentially numbers that are too large to be stored into an int, will cause undefined behavior. To know exactly which number is the upper limit for int in your system/environment, you may print the value of INT_MAX, defined in limits.h.

The solution is, of course, to store the long variable in another long variable, not an int. Or, simply pass the value to a function that does the necessary work. Putting everything in main isn't being very organized now is it.

How about we make a function that basically prints all the details about a card given the card's number?

The signature will look like-

void print_card_details(long num)

Now we need a function to put the card through luhn's algorithm. We can also make a function for that-

int is_valid(long num)
{
    int curr_digit, add_digit, prod_sum = 0, sum = 0;
    for (int digit_count = 0; num != 0; num /= 10, digit_count++)
    {
        // Strip each digit from number, starting from the end
        curr_digit = num % 10;
        if (digit_count % 2 != 0)
        {
            // Every 2nd digit from the right goes through this
            // The current digit gets doubled
            // The digits of that result are added to the sum
            add_digit = curr_digit * 2;
            prod_sum += add_digit % 10 + add_digit / 10;
        }
        else
        {
            // The remaining digits go through this
            // They are all summed up
            sum += curr_digit;
        }
    }
    if ((prod_sum + sum) % 10 != 0)
    {
        // If the sum of prod_sum + sum doesn't end in 0
        // It is invalid
        return 0;
    }
    else
    {
        // The card is valid
        return 1;
    }
}

The conventional way to iterate through the digits of a number is not to bruteforcefully divide arbitrary powers of 10 manually, but to iterate through it and divide and modulus by 10. For example, this snippet-

while (x != 0)
{
    printf("Current digit: %dn", x % 10);
    x /= 10;
}

Will print all digits of the number stored in x. This is essentially what we've used in the luhn's algorithm loop. Except we also keep a count of the total digits, because we only want every second digit starting from the end. How do we know the current digit qualifies this criteria? We check if the current digit_count is even (by dividing by 2 and checking the leftover is 0).

The formula that follows-

add_digit = curr_digit * 2;
prod_sum += add_digit % 10 + add_digit / 10;

is basically the implementation of this-

Multiply every other digit by 2, starting with the number’s second-to-last digit, and then add those products’ digits together.

Make sure only the digits of the resulting add_digit is added. So if add_digit ended up being 12. We need to add 1 + 2. That's exactly what add_digit % 10 + add_digit / 10 does. 12 % 10 is, of course, 2. And 12 / 10 is 1.

This function returns 1 if the card is valid, 0 if it's not. You can fit this up in your main function and check the return value to know whether the card is valid.

If it is valid, move on to the next step of checking the number of digits the card has, as well as what it begins with.

We can make a loop to count the number of digits, as well as store the very first and second digit of the number.

int len = 0;
int curr_digit = 0, prev_digit = 0;
while(num != 0)
{
    prev_digit = curr_digit;
    curr_digit = num % 10;
    num /= 10;
    len++;
}

This will give you the length of the card number. Notice, in the last iteration, the value of prev_digit is the second digit and the curr_digit is the first. So curr_digit * 10 + prev_digit will yield the first 2 numbers (together) that the credit card number begins with.

Finally, you just need a bunch of simple if/else clauses to verify which card it is. You're only asked to check for a very small subset as well. So here it is-

// Construct the 2 digit number that this card num begins with
int begins_with = curr_digit * 10 + prev_digit;
if (len == 13 && begins_with / 10 == 4)
{
    // We know only VISA uses 13 digits
    // And it begins with 4 (second digit does not matter)
    printf("VISAn");
}
else if (len == 15 && begins_with == 34 ||)
{
    // We know only AMEX uses 15 digits
    printf("AMEXn");
}
else if (len == 16)
{
    // Both VISA and MASTERCARD use 16 digits
    if (curr_digit == 4)
    {
        // But VISA card number begins with 4
        printf("VISAn");
    }
    else if (curr_digit == 5)
    {
        // MASTERCARD number begins with 5
        printf("MASTERCARDn");
    }
    else
    {
        // Out of context for this problem
        printf("INVALIDn");
    }
}
else
{
    // Out of context for this problem
    printf("INVALIDn");
}

Put that all together, and you should hopefully get

void print_card_details(long num)
{
    if (!is_valid(num))
    {   
        // Card did not pass luhn's algo
        printf("INVALIDn");
        return;
    }
    int len = 0;
    int curr_digit = 0, prev_digit = 0;
    while(num != 0)
    {
        prev_digit = curr_digit;
        curr_digit = num % 10;
        num /= 10;
        len++;
    }
    // Construct the 2 digit number that this card num begins with
    int begins_with = curr_digit * 10 + prev_digit;
    if (len == 13 && curr_digit == 4)
    {
        // We know only VISA uses 13 digits
        // And it begins with 4 (second digit does not matter)
        printf("VISAn");
    }
    else if (len == 15 && (begins_with == 34 || begins_with == 37))
    {
        // We know only AMEX uses 15 digits
        printf("AMEXn");
    }
    else if (len == 16)
    {
        // Both VISA and MASTERCARD use 16 digits
        if (curr_digit == 4)
        {
            // But VISA card number begins with 4
            printf("VISAn");
        }
        else if (begins_with >= 51 && begins_with <= 55)
        {
            // MASTERCARD number begins with 51, 52, 53, 54, or 55
            printf("MASTERCARDn");
        }
        else
        {
            // Out of context for this problem
            printf("INVALIDn");
        }
    }
    else
    {
        // Out of context for this problem
        printf("INVALIDn");
    }
    return;
}

Answered by Chase on November 18, 2021

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP