> ## 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.

# Verification Guide

> Verify bank accounts and card details with the Paystack TypeScript SDK

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

<Note>
  Some verification endpoints may incur charges. Check Paystack's pricing page for details.
</Note>

## Resolve Bank Account

Retrieve the account name for a bank account number.

<Steps>
  <Step title="Initialize the SDK">
    ```typescript theme={null}
    import { Paystack } from '@efobi/paystack';

    const paystack = new Paystack(process.env.PAYSTACK_SECRET_KEY!);
    ```
  </Step>

  <Step title="Resolve the account">
    ```typescript theme={null}
    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}`
      );
    }
    ```
  </Step>
</Steps>

### Parameters

<ParamField path="account_number" type="string" required>
  Bank account number to resolve
</ParamField>

<ParamField path="bank_code" type="string" required>
  Bank code (see [Miscellaneous API](/api-reference/miscellaneous#listbanks) for bank codes)
</ParamField>

<Tip>
  Use account resolution to verify recipient details before creating transfer recipients or initiating transfers.
</Tip>

## Validate Bank Account

Validate a customer's account using various verification methods.

```typescript theme={null}
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

<ParamField path="account_name" type="string" required>
  Name on the bank account
</ParamField>

<ParamField path="account_number" type="string" required>
  Bank account number
</ParamField>

<ParamField path="account_type" type="string" required>
  Account type: `personal` or `business`
</ParamField>

<ParamField path="bank_code" type="string" required>
  Bank code
</ParamField>

<ParamField path="country_code" type="string" required>
  Two-letter country code (e.g., `NG`, `GH`, `ZA`)
</ParamField>

<ParamField path="document_type" type="string" required>
  Type of identity document: `identityNumber`, `passportNumber`, `businessRegistrationNumber`
</ParamField>

<ParamField path="document_number" type="string" required>
  Identity document number
</ParamField>

<Warning>
  Account validation may incur charges per request. Use it only when necessary.
</Warning>

## Resolve Card BIN

Get information about a card from its BIN (first 6 digits).

```typescript theme={null}
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

<ParamField path="bin" type="string">
  Card BIN (first 6 digits)
</ParamField>

<ParamField path="brand" type="string">
  Card brand: `visa`, `mastercard`, `verve`, etc.
</ParamField>

<ParamField path="sub_brand" type="string">
  Card sub-brand (e.g., `ELECTRON`, `CLASSIC`)
</ParamField>

<ParamField path="card_type" type="string">
  Card type: `debit` or `credit`
</ParamField>

<ParamField path="bank" type="string">
  Issuing bank name
</ParamField>

<ParamField path="country_code" type="string">
  Two-letter country code
</ParamField>

<ParamField path="country_name" type="string">
  Full country name
</ParamField>

<ParamField path="linked_bank_id" type="number">
  Linked bank ID in Paystack system
</ParamField>

## Error Handling

Handle verification errors appropriately:

```typescript theme={null}
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

<Tip>
  1. **Cache resolution results** to avoid repeated API calls
  2. **Show account names** to users for confirmation
  3. **Handle network errors** gracefully
  4. **Validate input** before making API calls
  5. **Use BIN lookup** to provide better UX during card entry
  6. **Implement retries** for transient failures
  7. **Log verification attempts** for debugging
  8. **Consider rate limits** when doing bulk verifications
</Tip>

## Common Scenarios

### Transfer Recipient Creation

```typescript theme={null}
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

```typescript theme={null}
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

```typescript theme={null}
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;
}
```

### Form Validation with Real-time Verification

```typescript theme={null}
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:

```typescript theme={null}
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:

```typescript theme={null}
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);
}
```

## Related Resources

* [Transfers Guide](/guides/transfers) - Use verification before creating recipients
* [Verification API Reference](/api-reference/verification) - Full API documentation
* [Miscellaneous API](/api-reference/miscellaneous) - Get list of banks and their codes
* [Transfer Recipients](/api-reference/recipient) - Create verified recipients
