> ## Documentation Index
> Fetch the complete documentation index at: https://twigz.arqqin.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Banks

> UAE banking institutions for store payout configurations

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

<CodeGroup>
  ```typescript TypeScript theme={null}
  const banks = await convex.query(api.shared.banks.getBanks);
  ```

  ```javascript JavaScript theme={null}
  const banks = await convex.query("shared.banks.getBanks");
  ```

  ```python Python theme={null}
  import requests

  response = requests.post(
      f"{CONVEX_URL}/api/query",
      json={
          "path": "shared.banks.getBanks",
          "args": {}
      },
      headers={"Authorization": f"Bearer {token}"}
  )

  banks = response.json()
  ```
</CodeGroup>

<ResponseExample>
  ```json Response theme={null}
  [
    {
      "_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
    }
  ]
  ```
</ResponseExample>

## Seed Banks

Seeds the database with initial bank data (admin function).

<CodeGroup>
  ```typescript TypeScript theme={null}
  await convex.mutation(api.shared.banks.seedBanks);
  ```

  ```javascript JavaScript theme={null}
  await convex.mutation("shared.banks.seedBanks");
  ```
</CodeGroup>

<ResponseExample>
  ```json Response theme={null}
  null
  ```
</ResponseExample>

## Bank Selector Component

Here's a reusable bank selector component for your frontend:

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

  ```javascript Vue Component theme={null}
  <template>
    <div class="bank-selector">
      <label class="block text-sm font-medium text-gray-700 mb-2">
        Bank <span v-if="required" class="text-red-500">*</span>
      </label>
      
      <select
        v-model="selectedBank"
        @change="onBankChange"
        :required="required"
        class="w-full p-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
      >
        <option value="">{{ placeholder }}</option>
        <option v-for="bank in banks" :key="bank._id" :value="bank._id">
          {{ bank.name }} - {{ bank.nameArabic }}
        </option>
      </select>
      
      <div v-if="selectedBank" class="mt-2 text-sm text-gray-600">
        Selected: {{ getSelectedBankName() }}
      </div>
    </div>
  </template>

  <script>
  export default {
    props: {
      initialBankId: String,
      placeholder: {
        type: String,
        default: 'Select a bank'
      },
      required: {
        type: Boolean,
        default: false
      }
    },
    
    emits: ['bank-select'],
    
    data() {
      return {
        banks: [],
        selectedBank: this.initialBankId || ''
      };
    },
    
    async mounted() {
      await this.loadBanks();
    },
    
    methods: {
      async loadBanks() {
        try {
          this.banks = await convex.query("shared.banks.getBanks");
        } catch (error) {
          console.error('Failed to load banks:', error);
        }
      },
      
      onBankChange() {
        const selectedBankData = this.banks.find(bank => bank._id === this.selectedBank);
        if (selectedBankData) {
          this.$emit('bank-select', {
            bankId: selectedBankData._id,
            bankName: selectedBankData.name,
            bankCode: selectedBankData.code
          });
        }
      },
      
      getSelectedBankName() {
        const bank = this.banks.find(b => b._id === this.selectedBank);
        return bank ? bank.name : '';
      }
    }
  };
  </script>
  ```
</CodeGroup>

## Bank Account Setup Form

Complete form for setting up bank account details:

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

## Supported UAE Banks

The system includes all major UAE banks:

<CardGroup cols={2}>
  <Card title="Major Banks" icon="building-columns">
    * Emirates NBD (بنك الإمارات دبي الوطني)
    * First Abu Dhabi Bank (بنك أبوظبي الأول)
    * Dubai Islamic Bank (بنك دبي الإسلامي)
    * Abu Dhabi Commercial Bank (بنك أبوظبي التجاري)
  </Card>

  <Card title="Other Banks" icon="university">
    * Mashreq Bank (بنك المشرق)
    * RAKBANK (بنك رأس الخيمة الوطني)
    * ADIB (بنك أبوظبي الإسلامي)
    * CBD (بنك دبي التجاري)
  </Card>
</CardGroup>

## IBAN Validation

UAE IBAN format validation:

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

<AccordionGroup>
  <Accordion title="No banks found">
    **Status Code**: `200` (Success with empty array)

    ```json theme={null}
    []
    ```
  </Accordion>

  <Accordion title="Bank seeding failed">
    **Status Code**: `500`

    ```json theme={null}
    {
      "error": "Failed to seed bank data",
      "details": "Database connection error"
    }
    ```
  </Accordion>

  <Accordion title="Invalid bank ID">
    **Status Code**: `404`

    ```json theme={null}
    {
      "error": "Bank not found",
      "bankId": "b123456789"
    }
    ```
  </Accordion>
</AccordionGroup>

<Note>
  Bank data is pre-seeded during system initialization and includes all major UAE banking institutions with their official Arabic names and SWIFT codes.
</Note>

<Tip>
  Always validate IBAN format on the client side before submission to provide immediate feedback to users and reduce server-side validation errors.
</Tip>
