決済まわりを開発していると、テスト用に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)])
この最小例が扱うのは合計桁数とMOD-97だけです。数字のみのBBANは、表にあるすべての国の国内パターンや国内チェック規則を満たしません(たとえばイギリス、オランダ、イタリアでは特定位置に文字が必要です)。また、ランダムな銀行識別子が割り当て済み・現行である保証もありません。出力はチェックサム計算の例として扱ってください。国別フォーマットのfixtureにはSWIFTレジストリの完全なパターンと国内チェック規則を実装するか、制限事項を確認したうえでRandomIBANを利用してください。
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を通過しても、生成値が割り当て済み口座と偶然一致しないことをRandomIBANは確認できません。本番の決済経路には決して到達させないでください。テストデータと本番データが混ざらないようにする方法については、決済システムでIBANを安全に保存するのガイドをご覧ください。
自前のジェネレーターを書くべきでないとき
自分で作るのは緊密な統合には最適ですが、常に労力に見合うとは限りません。次のような場合はコードを省きましょう。
- 今すぐ手早く一括で必要なとき。Random IBANジェネレーターから数秒で貼り付け、CSV/JSONにエクスポートできます。
- 最小限のテーブルでは捉えきれない正しいBBANの下位構造(各国のチェックディジット、文字の位置)を含む、幅広い国のカバレッジが必要なとき。
- コードベースの外で作業しているとき。スプレッドシート、Postmanのコレクション、ドキュメントを用意している場合などです。
そして、どの方法を選んだとしても、出力を検証してください。サンプルをIBANバリデーターに通し、自分のチェックディジットのロジックが参照実装と一致することを確認します。MOD-97で何かが失敗する場合、不具合はほぼ必ず、文字から数字への変換か、BBANの桁数の1つずれ(off-by-one)にあります。
よくある質問
プログラムで生成したIBANは安全に使えますか?
合成値と明記し、実際の決済経路へ送られないよう遮断した環境でのフォーマット検証・QA・デモ・ドキュメントには使用できます。構造とチェックサムに合格しても未割り当てである証明にはならないため、実取引や本番システムには送信しないでください。
IBANを生成するのに外部ライブラリは必要ですか?
PythonとJavaScriptの例には外部パッケージは不要です。PHPの例では任意拡張のbcmathをインストールして有効にする必要があります。長さとMOD-97だけでなく国別の完全なBBANパターンが必要なら、専用ライブラリや追加の国別ルールが役立ちます。
生成した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)を計算し、MOD-97を通過させるチェックディジットを求めます。両者は同じ中核処理を共有しますが、国別BBANが完全であることや口座の実在を証明するものではありません。