--=========================================================================== -- Copywright 2002, joel crainshaw & chet west --============================================================================= -- DESCRIPTION -- "fingerprints" credit cards based on LUUHHN algorithm -- doesn't tell if a particular number if vvalid, but if that number -- COULD be valid --============================================================================= -- MODIFICATION HISTORY -- Person Date Comments -- --------- ---------- ------------------------------------------ -- joel 09/05/2001 Initial Creation -- joel 11/08/2001 fix bug; strip spaces from number --=========================================================================== -- base algorithm from http://www.beachnet.com/~hstiles/cardtype.html -- uses standard LUHN algo -- -- table indicates card type, prefix, length and algo for known cards ---------------- -- CARD TYPE....................Prefix.........Length..Check digit algorithm -- MASTERCARD...................51-55..........16......mod 10 -- VISA.........................4..............13,16...mod 10 -- AMEX.........................34,37..........15......mod 10 -- Diners Club/Carte Blanche....300-305,36,38..14......mod 10 -- Discover.....................6011...........16......mod 10 -- enRoute......................2014,2149......15......any -- JCB..........................3..............16......mod 10 -- JCB..........................2131,1800......15......mod 10 --=========================================================================== FUNCTION cc_fingerprint ( p_ccn VARCHAR2 ) RETURN VARCHAR2 IS ccn VARCHAR2 (400) := nosymbols (p_ccn, LENGTH (p_ccn)); clen NUMBER := LENGTH (ccn); tot NUMBER := 0; val NUMBER := 0; c1 NUMBER; c2 NUMBER; c3 NUMBER; c4 NUMBER; ctype VARCHAR2 (160) := 'CARD'; calg VARCHAR2 (160) := 'UNKNOWN'; result VARCHAR2 (160); ----- BEGIN ccn := REPLACE (LTRIM (RTRIM (ccn)), ' ', ''); IF isnumber (ccn) = 0 THEN c1 := TO_NUMBER (SUBSTR (ccn, 1, 1)); c2 := TO_NUMBER (SUBSTR (ccn, 1, 2)); c3 := TO_NUMBER (SUBSTR (ccn, 1, 3)); c4 := TO_NUMBER (SUBSTR (ccn, 1, 4)); IF clen = 13 THEN IF c1 IN (4) THEN ctype := 'VISA'; calg := 'MOD10'; END IF; ELSIF clen = 14 THEN IF c2 IN (36, 38) THEN ctype := 'DINERS CLUB/CARTE BLANCHE'; calg := 'MOD10'; ELSIF c3 IN (300, 301, 302, 303, 304, 305) THEN ctype := 'DINERS CLUB/CARTE BLANCHE'; calg := 'MOD10'; END IF; ELSIF clen = 15 THEN IF c2 IN (34, 37) THEN ctype := 'AMEX'; calg := 'MOD10'; ELSIF c4 IN (2014, 2149) THEN ctype := 'enROUTE'; calg := 'ANY'; ELSIF c4 IN (2131, 1800) THEN ctype := 'JBC'; calg := 'MOD10'; END IF; ELSIF clen = 16 THEN IF c1 IN (4) THEN ctype := 'VISA'; calg := 'MOD10'; ELSIF c1 IN (3) THEN ctype := 'JBC'; calg := 'MOD10'; ELSIF c2 IN (51, 52, 53, 54, 55) THEN ctype := 'MASTERCARD'; calg := 'MOD10'; ELSIF c4 IN (6011) THEN ctype := 'DISCOVER'; calg := 'MOD10'; END IF; END IF; IF calg = 'MOD10' THEN FOR i IN REVERSE 1 .. LENGTH (ccn) LOOP IF TO_NUMBER (SUBSTR (TO_CHAR (i), LENGTH (i), 1)) IN (1, 3, 5, 7, 9) THEN val := SUBSTR (ccn, i, 1) * 2; tot := tot + SUBSTR (val, 1, 1); tot := tot + NVL (SUBSTR (val, 2, 1), 0); ELSE tot := tot + SUBSTR (ccn, i, 1); END IF; END LOOP; IF MOD (tot, 10) = 0 THEN result := 'VALID'; ELSE result := 'INVALID'; END IF; ELSIF calg = 'ANY' THEN -----any number is valid for card result := 'VALID'; ELSE -----card type unknown, therefore fingerprint unknown result := 'UNKNOWN'; END IF; result := result || ' ' || ctype || ' FINGERPRINT'; ELSE result := 'CARD CONTAINS NON NUMERIC SYMBOLS'; END IF; RETURN (result); END;