Documentation Index
Fetch the complete documentation index at: https://paystack-sdk.efobi.dev/llms.txt
Use this file to discover all available pages before exploring further.
The Verification API helps you validate bank account numbers and card BINs before processing transactions. This reduces failed transactions and improves user experience.
Overview
Verification services include:
- Bank account name resolution
- Account number validation
- Card BIN lookup
- Bank information retrieval
Some verification endpoints may incur charges. Check Paystack’s pricing page for details.
Resolve Bank Account
Retrieve the account name for a bank account number.
Initialize the SDK
import { Paystack } from '@efobi/paystack';
const paystack = new Paystack(process.env.PAYSTACK_SECRET_KEY!);
Resolve the account
const { data, error } = await paystack.verification.resolveAccount({
account_number: '0123456789',
bank_code: '058' // GTBank code
});
if (error) {
console.error('Resolution failed:', error);
return;
}
if (data) {
console.log('Account Name:', data.data.account_name);
console.log('Account Number:', data.data.account_number);
// Display to user for confirmation
const confirmed = await confirmWithUser(
`Is this correct? ${data.data.account_name}`
);
}
Parameters
Bank account number to resolve
Use account resolution to verify recipient details before creating transfer recipients or initiating transfers.
Validate Bank Account
Validate a customer’s account using various verification methods.
const { data, error } = await paystack.verification.validateAccount({
account_name: 'John Doe',
account_number: '0123456789',
account_type: 'personal', // or 'business'
bank_code: '058',
country_code: 'NG',
document_type: 'identityNumber',
document_number: '1234567890'
});
if (data) {
console.log('Verified:', data.data.verified);
console.log('Message:', data.data.verificationMessage);
if (data.data.verified) {
console.log('Account validation successful!');
// Proceed with transaction
} else {
console.log('Validation failed:', data.data.verificationMessage);
// Show error to user
}
}
Validation Parameters
Account type: personal or business
Two-letter country code (e.g., NG, GH, ZA)
Type of identity document: identityNumber, passportNumber, businessRegistrationNumber
Account validation may incur charges per request. Use it only when necessary.
Resolve Card BIN
Get information about a card from its BIN (first 6 digits).
const { data, error } = await paystack.verification.resolveCardBin({
card_bin: '539983' // First 6 digits of card
});
if (data) {
console.log('Card details:', {
bin: data.data.bin,
brand: data.data.brand, // visa, mastercard, verve
subBrand: data.data.sub_brand,
cardType: data.data.card_type, // debit or credit
bank: data.data.bank,
countryCode: data.data.country_code,
countryName: data.data.country_name
});
// Show card info to user
displayCardInfo(data.data);
}
Response Fields
Card BIN (first 6 digits)
Card brand: visa, mastercard, verve, etc.
Card sub-brand (e.g., ELECTRON, CLASSIC)
Card type: debit or credit
Linked bank ID in Paystack system
Error Handling
Handle verification errors appropriately:
try {
const { data, error } = await paystack.verification.resolveAccount({
account_number: accountNumber,
bank_code: bankCode
});
if (error) {
console.error('Verification error:', error);
return {
success: false,
message: 'Unable to verify account details'
};
}
if (!data.status) {
// API returned error
return {
success: false,
message: data.message || 'Account not found'
};
}
return {
success: true,
accountName: data.data.account_name,
accountNumber: data.data.account_number
};
} catch (err) {
console.error('Unexpected error:', err);
return {
success: false,
message: 'Verification service unavailable'
};
}
Best Practices
- Cache resolution results to avoid repeated API calls
- Show account names to users for confirmation
- Handle network errors gracefully
- Validate input before making API calls
- Use BIN lookup to provide better UX during card entry
- Implement retries for transient failures
- Log verification attempts for debugging
- Consider rate limits when doing bulk verifications
Common Scenarios
Transfer Recipient Creation
async function createVerifiedRecipient(
accountNumber: string,
bankCode: string
) {
// Step 1: Resolve account name
const { data, error } = await paystack.verification.resolveAccount({
account_number: accountNumber,
bank_code: bankCode
});
if (error || !data) {
throw new Error('Could not verify account');
}
const accountName = data.data.account_name;
// Step 2: Confirm with user
const confirmed = await confirmAccountDetails({
accountName,
accountNumber,
bankCode
});
if (!confirmed) {
throw new Error('User rejected account details');
}
// Step 3: Create transfer recipient
const recipient = await paystack.transferRecipient.create({
type: 'nuban',
name: accountName,
account_number: accountNumber,
bank_code: bankCode,
currency: 'NGN'
});
return recipient.data?.data?.recipient_code;
}
Card Payment Validation
async function validateCardBeforePayment(cardNumber: string) {
const bin = cardNumber.substring(0, 6);
const { data, error } = await paystack.verification.resolveCardBin({
card_bin: bin
});
if (error || !data) {
return {
valid: false,
message: 'Could not validate card'
};
}
const cardInfo = data.data;
// Check if card is supported
if (!['visa', 'mastercard', 'verve'].includes(cardInfo.brand.toLowerCase())) {
return {
valid: false,
message: `${cardInfo.brand} cards are not supported`
};
}
// Show card details to user
return {
valid: true,
cardInfo: {
brand: cardInfo.brand,
type: cardInfo.card_type,
bank: cardInfo.bank,
country: cardInfo.country_name
}
};
}
Batch Account Verification
async function verifyMultipleAccounts(
accounts: Array<{ accountNumber: string; bankCode: string }>
) {
const results = [];
for (const account of accounts) {
try {
const { data, error } = await paystack.verification.resolveAccount({
account_number: account.accountNumber,
bank_code: account.bankCode
});
if (data && data.status) {
results.push({
accountNumber: account.accountNumber,
bankCode: account.bankCode,
accountName: data.data.account_name,
verified: true
});
} else {
results.push({
accountNumber: account.accountNumber,
bankCode: account.bankCode,
verified: false,
error: data?.message || 'Verification failed'
});
}
} catch (err) {
results.push({
accountNumber: account.accountNumber,
bankCode: account.bankCode,
verified: false,
error: 'Network error'
});
}
// Rate limiting: wait 100ms between requests
await new Promise(resolve => setTimeout(resolve, 100));
}
return results;
}
import { useState, useEffect } from 'react';
function BankAccountForm() {
const [accountNumber, setAccountNumber] = useState('');
const [bankCode, setBankCode] = useState('');
const [accountName, setAccountName] = useState('');
const [verifying, setVerifying] = useState(false);
const [verified, setVerified] = useState(false);
useEffect(() => {
// Auto-verify when both fields are filled
if (accountNumber.length === 10 && bankCode) {
verifyAccount();
}
}, [accountNumber, bankCode]);
const verifyAccount = async () => {
setVerifying(true);
setVerified(false);
try {
const response = await fetch('/api/verify-account', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ accountNumber, bankCode })
});
const result = await response.json();
if (result.success) {
setAccountName(result.accountName);
setVerified(true);
} else {
setAccountName('');
alert('Could not verify account');
}
} catch (error) {
console.error('Verification failed:', error);
} finally {
setVerifying(false);
}
};
return (
<form>
<input
value={accountNumber}
onChange={(e) => setAccountNumber(e.target.value)}
placeholder="Account Number"
maxLength={10}
/>
<select value={bankCode} onChange={(e) => setBankCode(e.target.value)}>
<option value="">Select Bank</option>
<option value="058">GTBank</option>
<option value="033">UBA</option>
{/* More banks */}
</select>
{verifying && <p>Verifying...</p>}
{verified && accountName && (
<div className="success">
<p>✓ Account Name: {accountName}</p>
</div>
)}
</form>
);
}
Caching Strategies
Cache verification results to reduce API calls:
import { LRUCache } from 'lru-cache';
// Create cache with 1000 entries, 1 hour TTL
const verificationCache = new LRUCache<string, any>({
max: 1000,
ttl: 1000 * 60 * 60 // 1 hour
});
async function resolveAccountCached(
accountNumber: string,
bankCode: string
) {
const cacheKey = `${bankCode}:${accountNumber}`;
// Check cache first
const cached = verificationCache.get(cacheKey);
if (cached) {
console.log('Using cached result');
return cached;
}
// Verify with API
const { data, error } = await paystack.verification.resolveAccount({
account_number: accountNumber,
bank_code: bankCode
});
if (data && data.status) {
// Cache successful result
verificationCache.set(cacheKey, data.data);
return data.data;
}
throw new Error(error?.message || 'Verification failed');
}
Rate Limiting
Implement rate limiting for bulk verifications:
import pLimit from 'p-limit';
// Limit to 5 concurrent requests
const limit = pLimit(5);
async function verifyAccountsBulk(
accounts: Array<{ accountNumber: string; bankCode: string }>
) {
const promises = accounts.map(account =>
limit(async () => {
const { data } = await paystack.verification.resolveAccount({
account_number: account.accountNumber,
bank_code: account.bankCode
});
return {
...account,
accountName: data?.data?.account_name,
verified: !!data?.status
};
})
);
return Promise.all(promises);
}