IT業界のすみっこ暮らし

ふと気がついたときの記録



クレジットカード番号から発行者検索機能実装

クレジットカード番号について

http://melfina.org/wiki/?%A5%AF%A5%EC%A5%B8%A5%C3%A5%C8%A5%AB%A1%BC%A5%C9%C8%D6%B9%E6

クレジットカード番号には法則があり、先頭6桁でカードの種類を識別、分類することができる。
先頭の6桁を銀行識別番号(Bank Identification Number、略称:BIN)ないしは発行者識別番号(Issuer Identification Number、略称:IIN)と呼び、この先頭6桁でカード発行会社(イシュア、issuer)が判るようになっている。


…らしい。

無料でクレジット番号から発行者を識別できる情報を提供してるサイト

Free BIN/IIN Lookup Web Service - binlist.net

下記のページから「archived/iin-user-contributions.csv」ファイルをダウンロードする。 github.com

DB構築

CREATE TABLE `mt_credit_card_iin`( 
    `id` int(11) NOT NULL AUTO_INCREMENT,
    `iin` CHAR(6) NOT NULL,
    `card_brand` VARCHAR(64) NOT NULL,
    `card_sub_brand` VARCHAR(64),
    `card_type` VARCHAR(64) NOT NULL,
    `card_category` VARCHAR(64),
    `country_code` VARCHAR(128) NOT NULL,
    `bank_name` VARCHAR(128),
    `bank_url` VARCHAR(200),
    `bank_phone` VARCHAR(128),
    `bank_city` VARCHAR(64),
    PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin;  

エンコードなどが原因で、MySQL上のINFILEコマンド実行時に最初の1行しか読み込めない場合、一度ファイルをエクセルで開いてからCSV形式で「名前を付けて保存」をすると直る。

mysql>  LOAD DATA LOCAL INFILE 'C:\\util\\binlist-data-master\\binlist-data-master\\credit_card_iin.csv'  
    -> INTO TABLE mt_credit_card_iin 
    -> FIELDS TERMINATED BY ','  
    -> LINES TERMINATED BY '\r\n'    
    -> (iin,card_brand,card_sub_brand,card_type,card_category,country_code,bank_name,bank_url,bank_phone,bank_city); 

django rest frameworkでの実装例

クレジットカードの先頭の6桁の番号から発行者の情報を返す

Python

class CreditCardIinViewSet(viewsets.ViewSet):
    queryset = mt_credit_card_iin.objects.all()
    def list(self, request):
        iin_val = request.query_params.get('iin')
        if iin_val is not None and iin_val.isdigit() is True and 6 == len(iin_val) :
            if 0 is not mt_credit_card_iin.objects.filter(iin=iin_val).count():
                iin_infos = mt_credit_card_iin.objects.filter(iin=iin_val)[:1]
            elif 0 is not mt_credit_card_iin.objects.filter(iin__startswith=iin_val[:4]).count():
                iin_infos = mt_credit_card_iin.objects.filter(iin__startswith=iin_val[:4])[:1]
            else:
                return Response({'message': 'No results for {0}. Make sure you enter a valid BIN/IIN number.'.format(iin_val)}, status=status.HTTP_400_BAD_REQUEST)
        else:
            return Response({'message': 'No results for {0}. Make sure you enter a valid BIN/IIN number.'.format(iin_val)}, status=status.HTTP_400_BAD_REQUEST)
        serializer = CreditCardIinSerializer(iin_infos, many=True)
        serializer.data[0]['iin'] = iin_val
        return Response(serializer.data)

IPアドレス所在地検索機能の実装

一番最初にIPに紐付く地域情報DBを調べる

「ip address location database」でググるといくつかのサイトが確認できる。

www.ip2location.com

lite.ip2location.com

db-ip.com


調べた結果

調べてみると、IPから国~市情報までは無料提供もあるけど、細かい情報は大体有料な場合が多い。でも、機能の実装ならIP2Locationで提供しているLITE11で十分なのでこれをダウンロードしてみる。

lite.ip2location.com

IP2LocationはダウンロードページからDB構築手順も案内していて、色んな開発ケースに対してチュートリアルも提供しているので便利。

f:id:papamau:20170209174303p:plain

www.ip2location.com


IP2LocationでIPアドレス検索用DB構築

IPv4

CREATE TABLE `mt_ip2location_db11`(    
    `id` int(11) NOT NULL AUTO_INCREMENT,
    `ip_from` INT(10) UNSIGNED,
    `ip_to` INT(10) UNSIGNED,
    `country_code` CHAR(2),
    `country_name` VARCHAR(64),
    `region_name` VARCHAR(128),
    `city_name` VARCHAR(128),
    `latitude` DOUBLE,
    `longitude` DOUBLE,
    `zip_code` VARCHAR(30),
    `time_zone` VARCHAR(8),
    PRIMARY KEY (`id`),
    INDEX `idx_ip_from` (`ip_from`),
    INDEX `idx_ip_to` (`ip_to`),
    INDEX `idx_ip_from_to` (`ip_from`, `ip_to`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin;  

IPv6

CREATE TABLE `mt_ip2location_db11_ipv6`(   
    `id` int(11) NOT NULL AUTO_INCREMENT,
    `ip_from` DECIMAL(39,0) UNSIGNED NULL DEFAULT NULL,
    `ip_to` DECIMAL(39,0) UNSIGNED NOT NULL,
    `country_code` CHAR(2),
    `country_name` VARCHAR(64),
    `region_name` VARCHAR(128),
    `city_name` VARCHAR(128),
    `latitude` DOUBLE,
    `longitude` DOUBLE,
    `zip_code` VARCHAR(30),
    `time_zone` VARCHAR(8),
    PRIMARY KEY (`id`),
    INDEX `idx_ip_from` (`ip_from`),
    INDEX `idx_ip_to` (`ip_to`),
    INDEX `idx_ip_from_to` (`ip_from`, `ip_to`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin;  

IPv4

mysql>  LOAD DATA LOCAL INFILE 'C:\\util\\ip-address\\IP2LOCATION-LITE-DB11.CSV\\IP2LOCATION-LITE-DB11.CSV'   
    -> INTO TABLE mt_ip2location_db11    
    -> FIELDS TERMINATED BY ','  
    -> ENCLOSED BY '"'   
    -> LINES TERMINATED BY '\r\n'    
    -> (ip_from,ip_to,country_code,country_name,region_name,city_name,latitude,longitude,zip_code,time_zone);    
Query OK, 4044940 rows affected (56.48 sec) 
Records: 4044940  Deleted: 0  Skipped: 0  Warnings: 0

IPv6

mysql>  LOAD DATA LOCAL INFILE 'C:\\util\\ip-address\\IP2LOCATION-LITE-DB11.IPV6.CSV\\IP2LOCATION-LITE-DB11.IPV6.CSV' 
    -> INTO TABLE mt_ip2location_db11_ipv6   
    -> FIELDS TERMINATED BY ','  
    -> ENCLOSED BY '"'   
    -> LINES TERMINATED BY '\r\n'    
    -> (ip_from,ip_to,country_code,country_name,region_name,city_name,latitude,longitude,zip_code,time_zone);    
Query OK, 4366556 rows affected, 3 warnings (1 min 9.98 sec)    
Records: 4366556  Deleted: 0  Skipped: 0  Warnings: 3   

DBが準備できれば後はSELECTするだけなのでここまで。
※DBからIPv6の値を元に検索する方法が現在チュートリアルには存在してない、且つPythonのライブラリが存在するため、Pythonで開発する場合はライブラリを利用して実装することをお勧めする。

django rest frameworkでの実装例

IP情報からIPv4かIPv6か判別し、地域情報を返す
(IPv6はDB格納、IPv6はライブラリ(BIN)をプロジェクト内に格納)

Python

class Ip2locationViewSet(viewsets.ViewSet):
    queryset = mt_ip2location_db11.objects.all()
    def list(self, request):
        ip_val = request.query_params.get('ip')
        if ip_val is not None:
            try:
                ipnet = ipaddress.ip_network(ip_val)
                if ipnet.version == 4:
                    ipv4_val = int(ipaddress.IPv4Address(ip_val))
                    ip_infos = mt_ip2location_db11.objects.filter(ip_to__gte=ipv4_val).order_by('ip_to')[:1]
                elif ipnet.version == 6:
                    IP2LocObj = IP2Location.IP2Location();
                    IP2LocObj.open(os.path.join("data","IP2LOCATION-LITE-DB11.IPV6.BIN"));
                    ip_infos = IP2LocObj.get_all(ip_val);
                    ipv6_result = OrderedDict()
                    ipv6_result['country_code'] = ip_infos.country_short
                    ipv6_result['country_long'] = ip_infos.country_long
                    ipv6_result['region_name'] = ip_infos.region
                    ipv6_result['city_name'] = ip_infos.city
                    ipv6_result['latitude'] = str(ip_infos.latitude)
                    ipv6_result['longitude'] = str(ip_infos.longitude)
                    ipv6_result['zip_code'] = ip_infos.zipcode
                    ipv6_result['time_zone'] = ip_infos.timezone
                    ipv6_result['ip'] = ip_val
                    return Response([ipv6_result])
                else:
                    ip_infos = mt_ip2location_db11.objects.all()[:1]
                    return Response(ip_infos, status=status.HTTP_404_NOT_FOUND)
            except ValueError:
                return Response({'message': 'IP形式が正しくありません。', 'ip': ip_val}, status=status.HTTP_400_BAD_REQUEST)
        else:
            return Response({'message': 'IPアドレスの情報がありません。', 'ip': ip_val}, status=status.HTTP_400_BAD_REQUEST)
        serializer = Ip2locationSerializer(ip_infos, many=True)
        serializer.data[0]['ip'] = ip_val
        return Response(serializer.data)


その他、参考サイト

IP2Locationの開発向けのページ

www.ip2location.com

ip2location-python

pypi.python.org





プライバシーポリシー