13桁の法人番号は、計算式だけで真偽の半分が分かります。先頭のチェックディジットを検算すれば、番号が形式どおりかどうかは手元で確かめられるからです。ただ、残りの半分は計算では分かりません。その番号の法人が実在して、いまも閉鎖されずに生きているか、という半分です。
この「残り半分」で引っかかった申請が、実際にありました。新規取引先の登録申請をAIにチェックさせていたときのことです。申請された商号は登記上の商号と一致しています。それなのに、入力されていた法人番号は合併で閉鎖された法人のもので、存続会社には別の法人番号がありました。商号だけを見ていたら、そのまま通していたかもしれません。
この不一致を見つけたのはAIです。バクラクの申請内容と国税庁の情報を照合させて、法人番号の形式を確かめたうえで、商号、インボイス番号、閉鎖年月日、承継先を調べ、問題のある申請だけを「要確認」に振り分けてもらいました。
といっても、会社が実在するかどうかをAIの知識だけで推測させたわけではありません。法人番号は計算式で検算し、会社情報は国税庁の公式Web-APIから取得します。そして、最後に承認するか差し戻すかを決めるのは人です。
商号が合っていても、法人番号は閉鎖済みかもしれない
13桁の法人番号はチェックディジットで検算し、商号、閉鎖年月日、承継先は国税庁の法人番号Web-APIで照合する。AIの仕事は全件をOKと要確認に分けるところまで。人が判断するのは、要確認になった申請だけ。
連載「経理をAIに任せる」(全4回)
- 経理AIの内部統制はどう設計する?作業と責任、証跡の分け方
- 新規取引先チェックをAIで自動化|合併で閉鎖された法人番号を見つけた方法(この記事)
- 経費精算のレシートと明細を、AIに全部突合させてみた
- 会計ソフトのAPI比較|AI経理で確認したい4つの選定基準
この実例でAIに任せたのは、公式情報を集めて不一致を見つけるところまでです。引っかかった申請をどう扱うかは、人が根拠を見て決めました。
新規取引先チェックで見ている4つの項目
そもそも新規取引先の登録申請で私が確認しているのは、少なくとも次の4つです。
- 法人が実在するか
- 申請された商号と登記上の商号が一致するか
- インボイス登録番号と法人番号が整合するか
- 法人番号が正しい形式で、その法人が閉鎖または合併済みではないか
13桁がそろって見えても、入力ミスはあり得ます。それに今回のように、商号は合っているのに中の番号だけが古い、というケースも実際にあるわけです。だから、番号の形式を検算する工程と、公式情報と照合する工程を分けることにしました。
申請の取得と、法人番号13桁の検算
この工程で確かめるのは、番号の形式だけです。流れの最初は、バクラクから申請中の新規取引先申請を取ってくるところ。ベンダーAPIに関わる部分は、公開できる範囲に絞った擬似コードにしています。
# バクラクAPIで「申請中の新規取引先申請」を取得し、
# 各申請から 法人番号・取引先名・インボイス番号 を取り出す
applications = bakuraku.fetch_requests(status="IN_PROGRESS", form="新規取引先申請")
取り出した法人番号は、まず手元で検算します。法人番号は、12桁の基礎番号の前にチェックディジット1桁を付けた13桁で、計算方法は国税庁が公開しています。つまり、番号が形式どおりかどうかは、APIに聞くまでもなく確かめられます。
def validate_corporate_number(number: str) -> bool:
"""法人番号13桁のチェックディジットを検証する。"""
if not number or len(number) != 13 or not number.isdigit():
return False
check_digit = int(number[0])
base = number[1:] # 下位12桁が基礎番号
total = 0
for i, ch in enumerate(reversed(base)):
weight = 2 if i % 2 == 1 else 1 # 右から 奇数桁=1, 偶数桁=2
total += int(ch) * weight
return (9 - (total % 9)) == check_digit
ただ、この検算で分かるのは「番号が計算式に合っているか」だけなんです。その法人が実在するのか、いまも同じ番号が有効なのかは、ここでは何も分かりません。
実在と閉鎖状態は、国税庁の法人番号Web-APIで照合する
法人の実在と商号、閉鎖状態は、国税庁の法人番号Web-APIに問い合わせます。Web-API Ver.4はCSV(Unicode)を指定して法人情報を取得でき、利用には無料で発行されるアプリIDが必要です。
import csv, requests
NTA_API = "https://api.houjin-bangou.nta.go.jp"
def nta_lookup(numbers: list[str], app_id: str, version: str = "4") -> dict:
"""国税庁 法人番号Web-APIで 実在・商号・閉鎖 を照会する。"""
resp = requests.get(
f"{NTA_API}/{version}/num",
params={
"id": app_id, # 無料発行のアプリID
"number": ",".join(numbers), # 最大10件まとめて
"type": "02", # CSV(Unicode)で受け取る
"history": "0", # 最新の登記のみ
},
)
resp.raise_for_status()
records = {}
for row in csv.reader(resp.content.decode("utf-8").splitlines()):
if len(row) <= 20:
continue # ヘッダー行などをskip
records[row[1]] = { # row[1] = 法人番号
"name": row[6], # 商号
"closeDate": row[18], # 閉鎖年月日(合併・解散など)
"successor": row[20], # 承継先の法人番号
}
return records
今回の件で効いたのは、このうち閉鎖年月日と承継先の項目でした。商号が一致するかだけでなく、「その番号がいまも生きているか」まで同じ照会で確認できます。あとは取得した情報と申請内容を比べて、問題がなければOK、条件に1つでも当てはまれば要確認、という判定にしました。
issues = []
if not validate_corporate_number(corp):
issues.append("法人番号CD不正")
if invoice and invoice != "T" + corp:
issues.append("インボイス番号不整合")
if record is None:
issues.append("国税庁に該当なし")
elif record["closeDate"]: # ← ここが効いた
issues.append("閉鎖/合併(承継先: " + (record["successor"] or "不明") + ")")
elif normalize(client_name) != normalize(record["name"]):
issues.append("商号不一致")
verdict = "要確認" if issues else "OK"
見てのとおり、法人番号や会社情報をAIの知識で判定させている箇所はありません。判定材料は、計算結果とAPIのレスポンスだけです。それでも、商号の表記をそろえる方法や例外条件が間違っていれば、誤判定は起きます。だから、コードが動いたかどうかだけでなく、出てきた判定結果まで確かめるようにしています。
商号は合っていた。それでも閉鎖年月日が返ってきた
で、冒頭の申請です。APIの応答には閉鎖年月日が入っていて、合併後の承継先として別の法人番号も返ってきました。申請された商号と登記上の商号を見比べても、違和感はどこにもありません。番号の中身だけが古かったわけです。
この申請は自動承認せず、閉鎖年月日と承継先を根拠に「要確認」で止めました。申請者へ差し戻したのは私です。AIに任せたのは、全件を同じ条件で調べて、人が見るべき申請を絞り込むところまででした。
アプリIDがない間はWeb検索で暫定確認
国税庁のWeb-APIには、1つ前提があります。無料とはいえ、アプリIDが発行されるまでは使えないことです。発行を待つ間など、すぐにAPIを使えない場合のために、法人番号データベースの houjin.info やhoujin.jpをAIに検索させて、商号や所在地、閉鎖状態、承継先を確認する手順も用意しました。
- 番号から商号、所在地、登記状態、承継先を確認する
- 申請された商号や住所と照合し、不一致があれば「要確認」にする
- 参照URLと確認日を残し、番号をAIに生成させない
- 重要な判断は国税庁の公表サイトまたは公式APIで再確認する
この手順はプロジェクトのREADMEに置いて、AIが作業を始めるときに読める場所にしています。とはいえ、第三者サイトの更新時点や表示内容が、公式情報と同じとは限りません。なので位置づけは、あくまで暫定確認と根拠集めです。APIの代わりにずっと使うつもりはありません。
照合コードもAIに書いてもらった
ここまで載せたコードを、私がすべて手で書いたわけではありません。確認したい項目と正常例、異常例、出力形式をAIに渡して、初稿と修正案を作ってもらいました。私がやったのは、チェックディジットの計算式とAPIのレスポンス、実際の判定結果を確かめることです。
実装を一から書けない私でも、業務の要件を説明して結果を確かめることで、こういう確認ツールを作れるようになりました。もっとも、コードが動いたからといって、そのまま会社の承認ルールに使えるとは限りません。
同じ考え方は経費精算にも広げていて、レシートと申請明細は全件突合し、画像しかない証憑だけをAI-OCRに回しています。
経費精算のレシート照合をAI-OCRで自動化|分割計上も検知経費精算のレシートと申請明細を全件突合しました。文字を取り出せるPDFはPythonで照合し、画像だけを…振り返ると、商号が合っていた閉鎖法人の申請を止められたのは、AIの推測が当たったからではありません。13桁の検算と国税庁の情報を根拠に「要確認」で止め、最後は人が差し戻すという流れを、先に作っておいたからです。



