Source

identitypass/services/bvn-verification.service.ts

import {
  IBVNVerificationService,
  BVNVerificationRequest,
  BVNWithFaceVerificationRequest,
  BVNAdvanceVerificationRequest,
  BVNByPhoneNumberRequest,
  BVNVerificationData,
  VerificationResponse,
} from 'porkate-valid8';
import { BaseIdentityPassService } from './base.service';

/**
 * IdentityPass BVN Verification Service
 *
 * @see https://docs.prembly.com/reference/bvn-basic
 * @see https://docs.prembly.com/reference/bvn-advance
 * @see https://docs.prembly.com/reference/bvn-face-validation
 * @see https://docs.prembly.com/reference/get-bvn-with-phone-number-1
 */
export class IdentityPassBVNService
  extends BaseIdentityPassService
  implements IBVNVerificationService
{
  /**
   * BVN Basic Verification - Validate a bank verification number
   * @see https://docs.prembly.com/reference/bvn-basic
   */
  async verifyBVN(
    data: BVNVerificationRequest,
  ): Promise<VerificationResponse<BVNVerificationData>> {
    return this.makeRequest(
      '/verification/bvn_validation',
      {
        number: data?.bvn,
      },
      this.mapBVNData,
    );
  }

  /**
   * BVN Advance Verification - Get detailed BVN information
   * @see https://docs.prembly.com/reference/bvn-advance
   */
  async verifyBVNAdvance(
    data: BVNAdvanceVerificationRequest,
  ): Promise<VerificationResponse<BVNVerificationData>> {
    return this.makeRequest(
      '/verification/bvn',
      {
        number: data?.bvn,
      },
      this.mapBVNData,
    );
  }

  /**
   * BVN with Face Validation - Verify BVN against user's image
   * @see https://docs.prembly.com/reference/bvn-face-validation
   */
  async verifyBVNWithFace(
    data: BVNWithFaceVerificationRequest,
  ): Promise<VerificationResponse<BVNVerificationData>> {
    return this.makeRequest(
      '/verification/bvn_w_face',
      {
        number: data?.bvn,
        image: data?.image,
      },
      this.mapBVNData,
    );
  }

  /**
   * Get BVN by Phone Number - Retrieve BVN information using phone number
   * @see https://docs.prembly.com/reference/get-bvn-with-phone-number-1
   */
  async getBVNByPhoneNumber(
    data: BVNByPhoneNumberRequest,
  ): Promise<VerificationResponse<BVNVerificationData>> {
    return this.makeRequest(
      '/verification/bvn_with_phone_advance',
      {
        phone_number: data?.phoneNumber,
      },
      this.mapBVNData,
    );
  }

  /**
   * Map BVN API response to BVNVerificationData
   * Handles various response field formats from Prembly API
   */
  private mapBVNData(
    verificationData: Record<string, unknown>,
    payload: Record<string, unknown>,
  ): BVNVerificationData {
    return {
      bvn:
        (verificationData?.bvn as string) ||
        (verificationData?.number as string) ||
        (payload?.number as string),
      firstName:
        (verificationData?.firstName as string) ||
        (verificationData?.first_name as string) ||
        (verificationData?.firstname as string) ||
        '',
      lastName:
        (verificationData?.lastName as string) ||
        (verificationData?.last_name as string) ||
        (verificationData?.lastname as string) ||
        '',
      middleName:
        (verificationData?.middleName as string) ||
        (verificationData?.middle_name as string) ||
        (verificationData?.middlename as string),
      dateOfBirth:
        (verificationData?.dateOfBirth as string) ||
        (verificationData?.date_of_birth as string) ||
        (verificationData?.dob as string) ||
        '',
      phoneNumber:
        (verificationData?.phoneNumber as string) ||
        (verificationData?.phoneNumber1 as string) ||
        (verificationData?.phone_number as string),
      phoneNumber2:
        (verificationData?.phoneNumber2 as string) || (verificationData?.phone_number2 as string),
      email: verificationData?.email as string,
      gender: verificationData?.gender as string,
      address:
        (verificationData?.residentialAddress as string) ||
        (verificationData?.residential_address as string) ||
        (verificationData?.address as string),
      photo:
        (verificationData?.photo as string) ||
        (verificationData?.base64Image as string) ||
        (verificationData?.image as string),
      enrollmentBank:
        (verificationData?.enrollmentBank as string) ||
        (verificationData?.enrollment_bank as string),
      enrollmentBranch:
        (verificationData?.enrollmentBranch as string) ||
        (verificationData?.enrollment_branch as string),
      registrationDate:
        (verificationData?.registrationDate as string) ||
        (verificationData?.registration_date as string),
      watchListed: this.parseWatchListed(
        verificationData?.watchListed || verificationData?.watch_listed,
      ),
      levelOfAccount: verificationData?.levelOfAccount as string,
      lgaOfOrigin: verificationData?.lgaOfOrigin as string,
      lgaOfResidence: verificationData?.lgaOfResidence as string,
      maritalStatus: verificationData?.maritalStatus as string,
      nin: verificationData?.nin as string,
      nameOnCard: verificationData?.nameOnCard as string,
      nationality: verificationData?.nationality as string,
      stateOfOrigin: verificationData?.stateOfOrigin as string,
      stateOfResidence: verificationData?.stateOfResidence as string,
      title: verificationData?.title as string,
      ...verificationData,
    };
  }

  /**
   * Parse watchListed value to boolean
   * Handles various formats: "YES"/"NO", "Yes"/"No", true/false
   */
  private parseWatchListed(value: unknown): boolean | undefined {
    if (value === undefined || value === null) return undefined;
    if (typeof value === 'boolean') return value;
    if (typeof value === 'string') {
      const normalized = value.trim().toUpperCase();
      if (normalized === 'YES' || normalized === 'TRUE') return true;
      if (normalized === 'NO' || normalized === 'FALSE') return false;
    }
    return undefined;
  }
}