رفتن به متن راهنما
همه‌ی راهنماها

شبا و کارت بانکی

اعتبارسنجی شمارهٔ شبا (IBAN) با ISO 7064 mod-97-10 و شمارهٔ کارت بانکی با چک‌سام Luhn، و پیدا کردن بانک صادرکننده.

برنامه RTLY-Kit 0.2.0آخرین بررسی زمان خواندن 6 دقیقه

در این صفحه

شروع سریع

PHP
<?php
require 'vendor/autoload.php';

use RtlyKit\Validation\BankCard;
use RtlyKit\Validation\Sheba;
use function RtlyKit\is_bank_card;
use function RtlyKit\is_sheba;

var_dump(Sheba::isValid('IR800170000000000000000123'));   // bool(true)
var_dump(is_sheba('800170000000000000000123'));           // bool(true)، IR خودکار اضافه می‌شود
var_dump(BankCard::isValid('6037-9912-3456-7893'));       // bool(true)
var_dump(is_bank_card('۶۰۳۷۹۹۱۲۳۴۵۶۷۸۹۳'));               // bool(true)، رقم‌های فارسی

echo Sheba::getBankName('IR800170000000000000000123'), "\n";   // بانک ملی ایران
echo BankCard::getBankName('6219861234567898'), "\n";          // بانک سامان

شماره‌های این مثال‌ها ساختگی‌اند. چک‌سامشان درست است، ولی به هیچ حساب واقعی تعلق ندارند. هر دو کلاس قرارداد Validator را دارند، برای ورودی بد خطا پرتاب نمی‌کنند و توابع کمکی validate_sheba() و validate_bank_card() هم دارند.

شبا (IBAN)

قالب

شمارهٔ شبا همان IBAN ایرانی است: IR، دو رقم کنترل و یک BBAN ۲۲ رقمی، روی‌هم ۲۶ نویسه. سه رقم اول BBAN بانک را مشخص می‌کند.

Text
IR 80 017 0000000000000000123
│  │  │   └─ بقیهٔ BBAN (۱۹ رقم)
│  │  └──── کد بانک (۳ رقم)
│  └─────── رقم‌های کنترل
└────────── کد کشور

یکسان‌سازی

Sheba::normalize() حرف‌ها را بزرگ می‌کند، رقم‌های فارسی و عربی را انگلیسی می‌کند و فاصله، خط تیره و نیم‌فاصله را حذف می‌کند. رشته‌ای که دقیقاً ۲۴ رقم باشد (شکلی که بانک‌ها معمولاً بدون IR چاپ می‌کنند) پیشوند IR می‌گیرد. ورودی دیگر همان‌طور که تایپ شده می‌ماند و در بررسی قالب رد می‌شود:

PHP
echo Sheba::normalize('800170000000000000000123');        // IR800170000000000000000123
echo Sheba::normalize('ir80 0170 0000');                   // IR8001700000
echo json_encode(Sheba::validate('ir80-0170-0000-0000-0000-0001-23')->isValid());   // true

چه چیزی بررسی می‌شود

  1. نوع و اندازه ( invalid_type ، input_too_long ).
  2. قالب: IR و بعد دقیقاً ۲۴ رقم، وگرنه invalid_format .
  3. چک‌سام ISO 7064 mod-97-10: چهار نویسهٔ اول به آخر می‌روند، I و R به ۱۸ و ۲۷ تبدیل می‌شوند و عدد به‌دست‌آمده بر ۹۷ باید باقیمانده ۱ بدهد. وگرنه invalid_checksum .

پیدا کردن بانک پیش از چک‌سام انجام می‌شود. برای همین حتی اگر رقم کنترل غلط باشد، کد بانک در details() هست:

PHP
$r = Sheba::validate('IR060170000000000000000123');
var_dump($r->isValid());       // bool(false)
print_r($r->errors());         // Array ( [0] => invalid_checksum )
echo $r->details()['bank_name']; // بانک ملی ایران

$ok = Sheba::validate('IR800170000000000000000123');
echo json_encode($ok->details(), JSON_UNESCAPED_UNICODE);
// {"normalized":"IR800170000000000000000123","bank_code":"017","bank_name":"بانک ملی ایران"}

$unknown = Sheba::validate('IR049990000000000000000123');
var_dump($unknown->isValid(), $unknown->details()['bank_name']);   // bool(true)  NULL

کلیدهای جزئیات: normalized، bank_code (رشتهٔ سه‌رقمی، یا null اگر قالب رد شده باشد) و bank_name (یا null وقتی کد در جدول نیست). شبایی که چک‌سامش درست است و کد بانکش ناشناخته است معتبر است و فقط نام ندارد.

نام بانک‌ها. نام‌ها همان نام بانک صادرکننده در زمان تخصیص کد هستند. کد بانک‌هایی که بعداً ادغام شده‌اند، نام اولشان را نگه می‌دارد.

شمارهٔ کارت بانکی

قالب و بررسی‌ها

کارت بانکی ایران ۱۶ رقم دارد. BankCard::normalize() بعد از تبدیل رقم‌های فارسی و عربی، فقط رقم‌ها را نگه می‌دارد. پس فاصله و خط تیره و هر جداکنندهٔ دیگر مشکلی ندارد. بررسی‌ها به این ترتیب‌اند:

  1. نوع و اندازه.
  2. دقیقاً ۱۶ رقم، وگرنه invalid_format .
  3. اگر هر ۱۶ رقم یکی باشد (مثل 0000000000000000 )، repeated_digits .
  4. چک‌سام Luhn، وگرنه invalid_checksum . از راست، هر رقم دوم دو برابر می‌شود (اگر از ۹ بیشتر شد، ۹ کم می‌شود) و جمع رقم‌ها باید بر ۱۰ بخش‌پذیر باشد.
PHP
$ok = BankCard::validate('6037-9912 3456 7893');
echo json_encode($ok->details(), JSON_UNESCAPED_UNICODE);
// {"normalized":"6037991234567893","bin":"603799","bank_name":"بانک ملی ایران"}

print_r(BankCard::validate('6037991234567890')->errors());   // [invalid_checksum]
print_r(BankCard::validate('0000000000000000')->errors());   // [repeated_digits]
print_r(BankCard::validate('1234')->errors());               // [invalid_format]

$x = BankCard::validate('9999991234567893');   // چک‌سام درست، BIN در جدول نیست
var_dump($x->isValid(), $x->details()['bank_name']);   // bool(true)  NULL

کلیدهای جزئیات: normalized، bin (شش رقم اول، یا null اگر قالب رد شده باشد) و bank_name. مثل شبا، پیدا کردن BIN پیش از بررسی تکرار رقم‌ها و چک‌سام انجام می‌شود.

BankCard::getBankName() و Sheba::getBankName() میان‌بر هستند. فقط برای شمارهٔ معتبر نام را می‌دهند و در غیر این صورت null:

PHP
var_dump(BankCard::getBankName('6037991234567890'));   // NULL، چون چک‌سام رد می‌شود
var_dump(BankCard::getBankName('6037991234567893'));   // string(26) "بانک ملی ایران"

داده‌های تشخیص بانک

جدولکلیدتعداد
BIN کارتشش رقم اول کارت۳۹
کد بانک شباسه رقم بعد از IRkk۳۸

هر مورد وقتی وارد جدول شده که دست‌کم دو منبع (جدول‌های عمومی پیش‌شمارهٔ بانک‌ها و مجموعه‌دادهٔ جامعهٔ توسعه‌دهندگان) دربارهٔ آن هم‌نظر بوده‌اند. موردی که فقط یک منبع داشته یا منبع‌ها دربارهٔ آن اختلاف داشته‌اند، وارد نشده است. ۱۹ کد بانک شبا را با مشخصات رسمی IBAN بانک مرکزی (که روی سایت بانک ملی ایران منتشر شده) هم مقایسه کرده‌ایم و همه برابر بودند.

خوب است بدانید. جدول‌ها کوچک‌اند. بسیاری از کارت‌های معتبر مال بانکی هستند که در فهرست نیست و برای آن‌ها bank_name برابر null است. بانک‌ها ادغام می‌شوند، نامشان عوض می‌شود و بازه‌ها را دوباره می‌دهند. پس نام بانک را به شکل «اشاره» نشان دهید و تصمیم مالی یا حقوقی را روی آن بنا نکنید. فهرست رسمی BIN در دسترس نبود. جدول‌ها در 2026-10-08 با چند صفحهٔ عمومی دوباره سنجیده شدند. جزئیات در دقت و داده است.

معتبر بودن هیچ‌وقت به این جدول‌ها بستگی ندارد. شماره‌ای که چک‌سامش درست باشد معتبر است، چه بانکش شناخته شود چه نه.

نکته‌های کاربردی

  • چک‌سام اشتباه تایپی را می‌گیرد، نه شمارهٔ جعلی را. نمی‌تواند بگوید کارتی وجود دارد یا حسابی مال کسی است.
  • شمارهٔ کارت را فقط برای قالب بررسی کنید. شمارهٔ کامل کارت را لاگ نکنید و CVV2 یا رمز دوم را ذخیره نکنید.
  • مقدار یکسان‌شده را ذخیره کنید تا 6037-9912-3456-7893 و 6037991234567893 یک رکورد باشند.
  • شمارهٔ کارت و شبا را رشته نگه دارید. عدد ۱۶ رقمی ممکن است از دقت عدد JSON یا عدد اعشاری بیشتر باشد.

سقف اندازه و قاعدهٔ نوع ورودی مثل بقیهٔ اعتبارسنج‌هاست. ورودی چطور پردازش می‌شود را ببینید.