決済まわりを開発していると、たいていの開発者はいずれ同じ壁にぶつかります。テストには有効なIBANが必要なのに、手で入力するうまい方法がないのです。同じ少数の例をコピー&ペーストすればテストスイートは脆くなり、実在のIBANを使えばコンプライアンス上の問題がいつ起きてもおかしくありません。きれいな解決策は、プログラムで生成することです。構造的に正しく、チェックディジットが有効で、必要なだけいくつでも作れます。
このガイドでは、それをPython・JavaScript・PHPで行う方法を具体的に説明します。読み終えるころには、ファクトリーやシードスクリプト、フィクスチャ用モジュールにそのまま組み込める、依存関係の少ない小さな関数が手に入ります。コードを書かずに今すぐ数件だけ欲しい場合は、Random IBANジェネレーターが即座に生成し、CSV・JSON・TXTにエクスポートできます。
「IBANを生成する」とは実際にどういうことか
IBANは1つの不透明な番号ではなく、連結された3つの部分です。
ES 76 21000418450200051332
│ │ └── BBAN (country-specific: bank + branch + account)
│ └───── Check digits (calculated, positions 3–4)
└──────── Country code (ISO 3166-1, positions 1–2)
したがって、IBANを生成するとは、次の3つを順番に行うことを意味します。
- 国を選び、そのIBANの桁数とBBANの構成を調べる。
- その構成に合ったランダムなBBANを構築する(正しい桁数で、仕様が許す位置には文字を入れる)。
- 全体が検証を通過するように、MOD-97アルゴリズムで2桁のチェックディジットを計算する。
最初のステップには小さな参照テーブルが必要です。2つ目は単なる乱数生成です。3つ目だけが実際のロジックを伴う部分で、それはIBANを検証するときに使うのと同じ処理を、逆向きに実行したものです。
核心:チェックディジットの計算
以下のどの言語も、同じチェックディジットの処理を共有します。ISO 7064 / ISO 13616のルールは次のとおりです。
- BBANを用意し、国コードを付加し、続けて
00を付加する。 - 各文字を2桁の数字に置き換える(
A=10、B=11、…Z=35)。 - 結果を1つの大きな整数として読み取り、
98 - (整数 mod 97)を計算する。 - その結果を2桁になるようゼロ埋めする。
そのゼロ埋めした数値が、チェックディジットの組です。これを国コードとBBANの間に挿入すれば、完成したIBANはMOD-97の検証を通過します。それでは実装してみましょう。
PythonでIBANを生成する
Pythonの任意精度整数のおかげで、これはほぼ自明です。多倍長整数ライブラリは不要です。
import random
# Minimal layout table: country -> (total length, BBAN length)
IBAN_SPECS = {
"DE": 22, "ES": 24, "FR": 27, "GB": 22,
"NL": 18, "IT": 27, "PT": 25, "BE": 16,
}
def _check_digits(country: str, bban: str) -> str:
rearranged = bban + country + "00"
numeric = "".join(
str(ord(c) - 55) if c.isalpha() else c
for c in rearranged
)
check = 98 - (int(numeric) % 97)
return f"{check:02d}"
def generate_iban(country: str = "ES") -> str:
total_len = IBAN_SPECS[country]
bban_len = total_len - 4 # minus country code + check digits
bban = "".join(random.choices("0123456789", k=bban_len))
return country + _check_digits(country, bban) + bban
# Usage
print(generate_iban("DE")) # e.g. DE21 ... (valid checksum)
print([generate_iban("ES") for _ in range(5)])
ここでは簡潔さのためBBANを数字のみにしていますが、上記の国々については正しい方法です。一部の国(たとえばイギリス)は銀行コードに文字を含むため、その場合は文字の位置もランダム化する必要があります。テストデータであれば、すべて数字のBBANでたいてい十分です。MOD-97と桁数のチェックはそれでも通過するからです。
JavaScriptでIBANを生成する
JSで唯一やっかいなのは、並べ替えた数値が通常のNumberには大きすぎる点です。標準的な対処法は、値を常に97未満に保つ、逐次的に余りを求めるループです。
const IBAN_SPECS = {
DE: 22, ES: 24, FR: 27, GB: 22,
NL: 18, IT: 27, PT: 25, BE: 16,
};
function mod97(numericString) {
let remainder = 0;
for (const ch of numericString) {
remainder = (remainder * 10 + Number(ch)) % 97;
}
return remainder;
}
function checkDigits(country, bban) {
const rearranged = bban + country + '00';
const numeric = rearranged.replace(/[A-Z]/g, c =>
(c.charCodeAt(0) - 55).toString()
);
const check = 98 - mod97(numeric);
return String(check).padStart(2, '0');
}
function generateIban(country = 'ES') {
const bbanLen = IBAN_SPECS[country] - 4;
let bban = '';
for (let i = 0; i < bbanLen; i++) {
bban += Math.floor(Math.random() * 10);
}
return country + checkDigits(country, bban) + bban;
}
// Usage
console.log(generateIban('FR'));
console.log(Array.from({ length: 5 }, () => generateIban('NL')));
暗号論的により強い乱数が必要な場合は(テストデータではめったに不要です)、Math.random()をcrypto.getRandomValues()に置き換えてください。フィクスチャやデモであれば、シンプルなバージョンで十分です。
PHPでIBANを生成する
PHPでは大きな数値の剰余にbcmath拡張が必要ですが、これはほとんどのインストールに同梱されています。
<?php
const IBAN_SPECS = [
'DE' => 22, 'ES' => 24, 'FR' => 27, 'GB' => 22,
'NL' => 18, 'IT' => 27, 'PT' => 25, 'BE' => 16,
];
function checkDigits(string $country, string $bban): string {
$rearranged = $bban . $country . '00';
$numeric = '';
foreach (str_split($rearranged) as $ch) {
$numeric .= ctype_alpha($ch) ? (string)(ord($ch) - 55) : $ch;
}
$check = 98 - (int) bcmod($numeric, '97');
return str_pad((string) $check, 2, '0', STR_PAD_LEFT);
}
function generateIban(string $country = 'ES'): string {
$bbanLen = IBAN_SPECS[$country] - 4;
$bban = '';
for ($i = 0; $i < $bbanLen; $i++) {
$bban .= random_int(0, 9);
}
return $country . checkDigits($country, $bban) . $bban;
}
// Usage
echo generateIban('IT'), PHP_EOL;
rand()ではなくrandom_int()を使っている点に注目してください。こちらは現代的で偏りのない選択肢であり、ここでは追加コストもありません。
生成したIBANをテストスイートで使う
ジェネレーターができたら、リテラルをあちこちに散りばめるのではなく、テストに組み込みましょう。うまく機能するパターンをいくつか挙げます。
- ファクトリーとフィクスチャ。 ファクトリー(FactoryBoy、Fakerのプロバイダー、Laravelのファクトリーなど)の中で
generateIban()を呼び出し、テストを実行するたびに新しく有効なデータが得られるようにします。 - ステージング用のシードデータ。 複数の国にまたがる数百件のIBANでデモ用データベースを満たし、国ごとのフォーマットを検証します。
- 境界ケース。 わざと壊した文字列(桁数が誤っている、チェックディジットが不正、未対応の国)を別のセットとして用意し、検証がそれらを拒否することを確認します。
- 必要なときの決定性。 再現可能なIBANが欲しい場合は、テストの冒頭で乱数生成器のシードを固定します。ファズ的なカバレッジを得たい場合は固定しないでおきます。
何を生成するにせよ、合成データであることを明確にラベル付けしてください。これらの番号は構造的に有効でMOD-97を通過しますが、実在の口座ではなく、本番の決済経路に決して到達させてはいけません。テストデータと本番データが混ざらないようにする方法については、決済システムでIBANを安全に保存するのガイドをご覧ください。
自前のジェネレーターを書くべきでないとき
自分で作るのは緊密な統合には最適ですが、常に労力に見合うとは限りません。次のような場合はコードを省きましょう。
- 今すぐ手早く一括で必要なとき。Random IBANジェネレーターから数秒で貼り付け、CSV/JSONにエクスポートできます。
- 最小限のテーブルでは捉えきれない正しいBBANの下位構造(各国のチェックディジット、文字の位置)を含む、幅広い国のカバレッジが必要なとき。
- コードベースの外で作業しているとき。スプレッドシート、Postmanのコレクション、ドキュメントを用意している場合などです。
そして、どの方法を選んだとしても、出力を検証してください。サンプルをIBANバリデーターに通し、自分のチェックディジットのロジックが参照実装と一致することを確認します。MOD-97で何かが失敗する場合、不具合はほぼ必ず、文字から数字への変換か、BBANの桁数の1つずれ(off-by-one)にあります。
よくある質問
プログラムで生成したIBANは安全に使えますか?
はい、テスト・QA・デモ・ドキュメントには安全です。これらは構造的に有効でチェックサムの検証も通過しますが、実在の銀行口座とは結びついていません。生成したIBANを実際の取引や本番の決済システムで使ってはいけません。
IBANを生成するのに外部ライブラリは必要ですか?
いいえ。上記の例はいずれも各言語の標準ライブラリだけを使っています。Pythonの組み込みの多倍長整数、JavaScriptの逐次的に余りを求めるループ、そしてPHPに同梱のbcmathです。ライブラリは国の網羅に役立ちますが、中核となるアルゴリズムは数行です。
生成したIBANが検証に失敗するのはなぜですか?
最も多い原因は、文字から数字への変換が誤っていること(A=10からZ=35でなければなりません)、剰余のステップの前に国コードと00を付加し忘れていること、または国に対して誤ったBBANの桁数を使っていることです。失敗するケースを1件、バリデーターと照らし合わせて切り分けてください。
BBANに文字を含む国向けのIBANはどう生成しますか?
ランダムなBBANを作るステップを拡張し、その国の仕様が許す位置に文字を配置します。たとえばイギリスの銀行コードは4文字です。単一の桁数ではなく、国ごとのパターン(どの位置が文字でどの位置が数字か)を保持し、チェックディジットを計算する前に各位置をそれに従って埋めます。
一度に数千件のIBANを生成できますか?
はい。ジェネレーター関数はループ内で呼び出しても軽量なので、数千件の生成も一瞬です。コードを実行したくない場合は、Random IBANジェネレーターが一括生成と、CSV・JSON・TXTへのワンクリックでのエクスポートに対応しています。
IBANを生成することと検証することの違いは何ですか?
検証は、既存の番号についてmod 97 == 1であることを確認します。生成は、同じ計算を逆向きに実行します。まずBBANを構築し、次に98 - (mod 97)を計算して、その番号を有効にするチェックディジットを求めます。両者は同じ中核処理を共有しています。