Skip to main content

Overview

The Banks API provides access to UAE banking institutions for store owners to configure their payout bank accounts. This system supports all major UAE banks and is used for financial operations and payout processing. Location: convex/shared/banks.ts

Get Banks

Retrieves all available banks in the UAE banking system.
const banks = await convex.query(api.shared.banks.getBanks);
[
  {
    "_id": "b123456789",
    "name": "Emirates NBD",
    "nameArabic": "بنك الإمارات دبي الوطني",
    "code": "EBILAEAD",
    "isActive": true,
    "sortOrder": 1,
    "_creationTime": 1640995200000
  },
  {
    "_id": "b987654321",
    "name": "First Abu Dhabi Bank",
    "nameArabic": "بنك أبوظبي الأول",
    "code": "NBADAEAD",
    "isActive": true,
    "sortOrder": 2,
    "_creationTime": 1640995200000
  },
  {
    "_id": "b555666777",
    "name": "Dubai Islamic Bank",
    "nameArabic": "بنك دبي الإسلامي",
    "code": "DUIBAE2X",
    "isActive": true,
    "sortOrder": 3,
    "_creationTime": 1640995200000
  },
  {
    "_id": "b111222333",
    "name": "Abu Dhabi Commercial Bank",
    "nameArabic": "بنك أبوظبي التجاري",
    "code": "ADCBAEAA",
    "isActive": true,
    "sortOrder": 4,
    "_creationTime": 1640995200000
  },
  {
    "_id": "b444555666",
    "name": "Mashreq Bank",
    "nameArabic": "بنك المشرق",
    "code": "BOMLAEAD",
    "isActive": true,
    "sortOrder": 5,
    "_creationTime": 1640995200000
  }
]

Seed Banks

Seeds the database with initial bank data (admin function).
await convex.mutation(api.shared.banks.seedBanks);
null

Bank Selector Component

Here’s a reusable bank selector component for your frontend:
import { useQuery } from 'convex/react';
import { api } from './convex/_generated/api';

interface BankSelectorProps {
  selectedBankId?: string;
  onBankSelect: (bankId: string, bankName: string) => void;
  placeholder?: string;
  required?: boolean;
}

function BankSelector({ selectedBankId, onBankSelect, placeholder = "Select a bank", required = false }: BankSelectorProps) {
  const banks = useQuery(api.shared.banks.getBanks);

  if (!banks) {
    return <div>Loading banks...</div>;
  }

  return (
    <div className="bank-selector">
      <label className="block text-sm font-medium text-gray-700 mb-2">
        Bank {required && <span className="text-red-500">*</span>}
      </label>
      
      <select
        value={selectedBankId || ''}
        onChange={(e) => {
          const selectedBank = banks.find(bank => bank._id === e.target.value);
          if (selectedBank) {
            onBankSelect(selectedBank._id, selectedBank.name);
          }
        }}
        className="w-full p-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
        required={required}
      >
        <option value="">{placeholder}</option>
        {banks.map((bank) => (
          <option key={bank._id} value={bank._id}>
            {bank.name} - {bank.nameArabic}
          </option>
        ))}
      </select>
      
      {selectedBankId && (
        <div className="mt-2 text-sm text-gray-600">
          Selected: {banks.find(b => b._id === selectedBankId)?.name}
        </div>
      )}
    </div>
  );
}

export default BankSelector;

Bank Account Setup Form

Complete form for setting up bank account details:
import { useState } from 'react';
import { useMutation } from 'convex/react';
import { api } from './convex/_generated/api';
import BankSelector from './BankSelector';

interface BankAccountFormProps {
  storeId: string;
  onSuccess?: () => void;
}

function BankAccountForm({ storeId, onSuccess }: BankAccountFormProps) {
  const [formData, setFormData] = useState({
    bankId: '',
    accountHolderName: '',
    IBAN: '',
    accountNumber: ''
  });
  const [isSubmitting, setIsSubmitting] = useState(false);
  const [errors, setErrors] = useState<Record<string, string>>({});

  const updateStore = useMutation(api.stores.operations.updateStore);

  const validateIBAN = (iban: string): boolean => {
    // UAE IBAN format: AE + 2 digits + 19 digits = 23 characters
    const uaeIBANRegex = /^AE\d{21}$/;
    return uaeIBANRegex.test(iban.replace(/\s/g, ''));
  };

  const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault();
    setIsSubmitting(true);
    setErrors({});

    // Validation
    const newErrors: Record<string, string> = {};
    
    if (!formData.bankId) newErrors.bankId = 'Please select a bank';
    if (!formData.accountHolderName) newErrors.accountHolderName = 'Account holder name is required';
    if (!formData.IBAN) {
      newErrors.IBAN = 'IBAN is required';
    } else if (!validateIBAN(formData.IBAN)) {
      newErrors.IBAN = 'Invalid UAE IBAN format (e.g., AE123456789012345678901)';
    }

    if (Object.keys(newErrors).length > 0) {
      setErrors(newErrors);
      setIsSubmitting(false);
      return;
    }

    try {
      await updateStore({
        bankAccount: {
          bankId: formData.bankId,
          accountHolderName: formData.accountHolderName,
          IBAN: formData.IBAN.replace(/\s/g, ''), // Remove spaces
          accountNumber: formData.accountNumber
        }
      });

      onSuccess?.();
    } catch (error) {
      console.error('Failed to update bank account:', error);
      setErrors({ submit: 'Failed to save bank account details' });
    } finally {
      setIsSubmitting(false);
    }
  };

  return (
    <form onSubmit={handleSubmit} className="space-y-6">
      <div className="bg-blue-50 p-4 rounded-lg">
        <h3 className="font-medium text-blue-900 mb-2">Bank Account Information</h3>
        <p className="text-sm text-blue-700">
          This information will be used for processing your store payouts. 
          Ensure all details are accurate to avoid payment delays.
        </p>
      </div>

      <BankSelector
        selectedBankId={formData.bankId}
        onBankSelect={(bankId, bankName) => {
          setFormData(prev => ({ ...prev, bankId }));
          setErrors(prev => ({ ...prev, bankId: '' }));
        }}
        required
      />
      {errors.bankId && <p className="text-red-500 text-sm">{errors.bankId}</p>}

      <div>
        <label className="block text-sm font-medium text-gray-700 mb-2">
          Account Holder Name <span className="text-red-500">*</span>
        </label>
        <input
          type="text"
          value={formData.accountHolderName}
          onChange={(e) => {
            setFormData(prev => ({ ...prev, accountHolderName: e.target.value }));
            setErrors(prev => ({ ...prev, accountHolderName: '' }));
          }}
          placeholder="Enter account holder name as per bank records"
          className="w-full p-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
          required
        />
        {errors.accountHolderName && <p className="text-red-500 text-sm">{errors.accountHolderName}</p>}
      </div>

      <div>
        <label className="block text-sm font-medium text-gray-700 mb-2">
          IBAN <span className="text-red-500">*</span>
        </label>
        <input
          type="text"
          value={formData.IBAN}
          onChange={(e) => {
            const value = e.target.value.toUpperCase().replace(/[^A-Z0-9]/g, '');
            setFormData(prev => ({ ...prev, IBAN: value }));
            setErrors(prev => ({ ...prev, IBAN: '' }));
          }}
          placeholder="AE123456789012345678901"
          maxLength={23}
          className="w-full p-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 font-mono"
          required
        />
        <p className="text-xs text-gray-500 mt-1">
          UAE IBAN format: AE followed by 21 digits
        </p>
        {errors.IBAN && <p className="text-red-500 text-sm">{errors.IBAN}</p>}
      </div>

      <div>
        <label className="block text-sm font-medium text-gray-700 mb-2">
          Account Number (Optional)
        </label>
        <input
          type="text"
          value={formData.accountNumber}
          onChange={(e) => setFormData(prev => ({ ...prev, accountNumber: e.target.value }))}
          placeholder="Additional account number if required"
          className="w-full p-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
        />
      </div>

      {errors.submit && (
        <div className="bg-red-50 p-4 rounded-lg">
          <p className="text-red-700 text-sm">{errors.submit}</p>
        </div>
      )}

      <div className="flex justify-end">
        <button
          type="submit"
          disabled={isSubmitting}
          className="px-6 py-3 bg-blue-600 text-white rounded-lg hover:bg-blue-700 disabled:opacity-50 disabled:cursor-not-allowed"
        >
          {isSubmitting ? 'Saving...' : 'Save Bank Details'}
        </button>
      </div>
    </form>
  );
}

export default BankAccountForm;

Supported UAE Banks

The system includes all major UAE banks:

Major Banks

  • Emirates NBD (بنك الإمارات دبي الوطني)
  • First Abu Dhabi Bank (بنك أبوظبي الأول)
  • Dubai Islamic Bank (بنك دبي الإسلامي)
  • Abu Dhabi Commercial Bank (بنك أبوظبي التجاري)

Other Banks

  • Mashreq Bank (بنك المشرق)
  • RAKBANK (بنك رأس الخيمة الوطني)
  • ADIB (بنك أبوظبي الإسلامي)
  • CBD (بنك دبي التجاري)

IBAN Validation

UAE IBAN format validation:
function validateUAEIBAN(iban: string): boolean {
  // Remove spaces and convert to uppercase
  const cleanIBAN = iban.replace(/\s/g, '').toUpperCase();
  
  // Check format: AE + 21 digits
  if (!/^AE\d{21}$/.test(cleanIBAN)) {
    return false;
  }
  
  // IBAN check digit validation (simplified)
  const rearranged = cleanIBAN.slice(4) + cleanIBAN.slice(0, 4);
  const numericString = rearranged.replace(/[A-Z]/g, (char) => 
    (char.charCodeAt(0) - 55).toString()
  );
  
  // Mod 97 check
  let remainder = 0;
  for (let i = 0; i < numericString.length; i++) {
    remainder = (remainder * 10 + parseInt(numericString[i])) % 97;
  }
  
  return remainder === 1;
}

Error Handling

Status Code: 200 (Success with empty array)
[]
Status Code: 500
{
  "error": "Failed to seed bank data",
  "details": "Database connection error"
}
Status Code: 404
{
  "error": "Bank not found",
  "bankId": "b123456789"
}
Bank data is pre-seeded during system initialization and includes all major UAE banking institutions with their official Arabic names and SWIFT codes.
Always validate IBAN format on the client side before submission to provide immediate feedback to users and reduce server-side validation errors.