├── src ├── utils │ ├── errors │ │ ├── index.ts │ │ └── error.base.ts │ ├── interfaces │ │ ├── index.ts │ │ ├── env.ts │ │ └── axios.ts │ ├── constants.ts │ ├── handle-errors.ts │ ├── index.ts │ ├── exclude-field.ts │ └── url.ts ├── services │ ├── quotes │ │ ├── enum │ │ │ ├── index.ts │ │ │ └── quotes.enum.ts │ │ ├── dto │ │ │ ├── index.ts │ │ │ └── create-quote.dto.ts │ │ └── quote.ts │ ├── wallets │ │ ├── dto │ │ │ ├── index.ts │ │ │ └── wallet-logs.dto.ts │ │ └── wallet.ts │ ├── beneficiaries │ │ ├── dto │ │ │ ├── sub-dto │ │ │ │ ├── index.ts │ │ │ │ ├── address.dto.ts │ │ │ │ └── bank.dto.ts │ │ │ ├── index.ts │ │ │ ├── delete-beneficiary.dto.ts │ │ │ ├── update-beneficiary.dto.ts │ │ │ ├── list-beneficiary.dto.ts │ │ │ └── create-beneficiary.dto.ts │ │ └── beneficiary.ts │ ├── virtual-accounts │ │ ├── enum │ │ │ ├── index.ts │ │ │ ├── currency.enum.ts │ │ │ ├── channel.enum.ts │ │ │ └── status.enum.ts │ │ ├── dto │ │ │ ├── sub-dto │ │ │ │ ├── index.ts │ │ │ │ ├── kyc-corporate.dto.ts │ │ │ │ ├── ultimate-benficial-owners.dto.ts │ │ │ │ └── kyc.dto.ts │ │ │ ├── index.ts │ │ │ ├── list-sub-virtual-accounts.dto.ts │ │ │ ├── list-merchant-vaccounts.dto.ts │ │ │ ├── create-individual-sub-vaccount.dto.ts │ │ │ ├── create-instant-approval-vaccount.dto.ts │ │ │ ├── create-corporate-vaccount.dto.ts │ │ │ └── create-virtual-account.dto.ts │ │ └── vaccounts.ts │ ├── verification │ │ ├── dto │ │ │ ├── index.ts │ │ │ ├── bvn-validation.dto.ts │ │ │ └── verify-bank.dto.ts │ │ └── verify.ts │ ├── chargebacks │ │ ├── dto │ │ │ ├── index.ts │ │ │ ├── accept-chargeback.dto.ts │ │ │ └── reject-chargeback.dto.ts │ │ └── chargeback.ts │ ├── conversions │ │ ├── dto │ │ │ ├── index.ts │ │ │ ├── create-conversion.dto.ts │ │ │ └── fetch-conversion.dto.ts │ │ └── conversion.ts │ ├── payouts │ │ ├── dto │ │ │ ├── sub-dto │ │ │ │ ├── index.ts │ │ │ │ ├── payout-document.dto.ts │ │ │ │ └── payout-beneficiary.dto.ts │ │ │ ├── list-payout.dto.ts │ │ │ ├── index.ts │ │ │ ├── upload-payout.dto.ts │ │ │ ├── wallet-to-wallet.dto.ts │ │ │ └── create-payout.dto.ts │ │ ├── enum │ │ │ ├── index.ts │ │ │ ├── payout-beneficiary.enum.ts │ │ │ ├── payment-destination.enum.ts │ │ │ └── payment-scheme.enum.ts │ │ └── payout.ts │ ├── subaccounts │ │ ├── dto │ │ │ ├── index.ts │ │ │ ├── update-subaccount.dto.ts │ │ │ ├── fetch-subaccount.dto.ts │ │ │ └── create-subaccount.dto.ts │ │ └── subaccount.ts │ ├── collections │ │ ├── dto │ │ │ ├── index.ts │ │ │ ├── fetch-collection-additional-vaccount.dto.ts │ │ │ ├── pay-with-transfer-collection.dto.ts │ │ │ ├── list-collection-main-vaccount.dto.ts │ │ │ └── list-collection-multiple-vaccounts.dto.ts │ │ └── collection.ts │ ├── index.ts │ └── business-id │ │ └── business.ts ├── index.ts ├── types │ ├── beneficiaries │ │ ├── sub-dto │ │ │ ├── index.ts │ │ │ ├── address.ts │ │ │ └── bank.ts │ │ └── index.ts │ ├── wallets │ │ └── index.ts │ ├── conversions │ │ └── index.ts │ ├── index.ts │ ├── verification │ │ └── index.ts │ ├── quotes │ │ └── index.ts │ ├── subaccounts │ │ └── index.ts │ ├── collections │ │ └── index.ts │ └── payouts │ │ ├── sub-dto │ │ └── payouts-sub.ts │ │ └── index.ts ├── config │ └── envconfig.ts ├── api.ts └── fincra.ts ├── .husky ├── post-pull ├── post-checkout ├── pre-push └── pre-commit ├── .prettierrc.json ├── global.d.ts ├── test ├── env.ts └── services │ ├── business-id │ └── business.test.ts │ ├── quotes │ └── quotes.test.ts │ ├── wallets │ └── wallets.test.ts │ ├── conversions │ └── conversions.test.ts │ ├── verification │ └── verify.test.ts │ ├── chargebacks │ └── chargebacks.test.ts │ ├── subaccounts │ └── subaccounts.test.ts │ ├── collections │ └── collections.test.ts │ ├── beneficiaries │ └── beneficiaries.test.ts │ ├── payouts │ └── payouts.test.ts │ └── virtual-accounts │ └── virtual-accounts.test.ts ├── jest.config.js ├── .circleci └── config.yml ├── .prettierignore ├── .gitignore ├── .github └── workflows │ └── publish.yml ├── CONTRIBUTING.md ├── LICENCE ├── tsconfig.json ├── package.json └── README.md /src/utils/errors/index.ts: -------------------------------------------------------------------------------- 1 | export * from './error.base'; 2 | -------------------------------------------------------------------------------- /src/services/quotes/enum/index.ts: -------------------------------------------------------------------------------- 1 | export * from './quotes.enum'; 2 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './fincra'; 2 | export * from './types'; 3 | -------------------------------------------------------------------------------- /src/services/quotes/dto/index.ts: -------------------------------------------------------------------------------- 1 | export * from './create-quote.dto'; 2 | -------------------------------------------------------------------------------- /src/services/wallets/dto/index.ts: -------------------------------------------------------------------------------- 1 | export * from './wallet-logs.dto'; 2 | -------------------------------------------------------------------------------- /src/utils/interfaces/index.ts: -------------------------------------------------------------------------------- 1 | export * from './env'; 2 | export * from './axios'; 3 | -------------------------------------------------------------------------------- /.husky/post-pull: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | . "$(dirname -- "$0")/_/husky.sh" 3 | 4 | yarn 5 | -------------------------------------------------------------------------------- /src/utils/interfaces/env.ts: -------------------------------------------------------------------------------- 1 | export interface IEnvironment { 2 | sandbox: boolean; 3 | } 4 | -------------------------------------------------------------------------------- /.husky/post-checkout: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | . "$(dirname -- "$0")/_/husky.sh" 3 | 4 | yarn 5 | -------------------------------------------------------------------------------- /.husky/pre-push: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | . "$(dirname -- "$0")/_/husky.sh" 3 | 4 | yarn build 5 | -------------------------------------------------------------------------------- /src/types/beneficiaries/sub-dto/index.ts: -------------------------------------------------------------------------------- 1 | export * from './address'; 2 | export * from './bank'; 3 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | . "$(dirname -- "$0")/_/husky.sh" 3 | 4 | yarn format && git add . 5 | -------------------------------------------------------------------------------- /src/services/beneficiaries/dto/sub-dto/index.ts: -------------------------------------------------------------------------------- 1 | export * from './address.dto'; 2 | export * from './bank.dto'; 3 | -------------------------------------------------------------------------------- /src/services/virtual-accounts/enum/index.ts: -------------------------------------------------------------------------------- 1 | export * from './currency.enum'; 2 | export * from './channel.enum'; 3 | -------------------------------------------------------------------------------- /src/services/verification/dto/index.ts: -------------------------------------------------------------------------------- 1 | export * from './verify-bank.dto'; 2 | export * from './bvn-validation.dto'; 3 | -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 80, 3 | "semi": true, 4 | "singleQuote": true, 5 | "trailingComma": "es5" 6 | } 7 | -------------------------------------------------------------------------------- /src/services/chargebacks/dto/index.ts: -------------------------------------------------------------------------------- 1 | export * from './accept-chargeback.dto'; 2 | export * from './reject-chargeback.dto'; 3 | -------------------------------------------------------------------------------- /src/services/conversions/dto/index.ts: -------------------------------------------------------------------------------- 1 | export * from './create-conversion.dto'; 2 | export * from './fetch-conversion.dto'; 3 | -------------------------------------------------------------------------------- /src/services/payouts/dto/sub-dto/index.ts: -------------------------------------------------------------------------------- 1 | export * from './payout-beneficiary.dto'; 2 | export * from './payout-document.dto'; 3 | -------------------------------------------------------------------------------- /src/services/payouts/enum/index.ts: -------------------------------------------------------------------------------- 1 | export * from './payout-beneficiary.enum'; 2 | export * from './payment-destination.enum'; 3 | -------------------------------------------------------------------------------- /src/utils/interfaces/axios.ts: -------------------------------------------------------------------------------- 1 | export interface IAxiosStruct { 2 | method: string; 3 | url: string; 4 | data?: any; 5 | } 6 | -------------------------------------------------------------------------------- /src/services/virtual-accounts/enum/currency.enum.ts: -------------------------------------------------------------------------------- 1 | export enum CurrencyEnum { 2 | EUR = 'EUR', 3 | GBP = 'GBP', 4 | NGN = 'NGN', 5 | } 6 | -------------------------------------------------------------------------------- /src/services/virtual-accounts/enum/channel.enum.ts: -------------------------------------------------------------------------------- 1 | export enum ChannelEnum { 2 | vfd = 'vfd', 3 | wema = 'wema', 4 | providus = 'providus', 5 | } 6 | -------------------------------------------------------------------------------- /src/utils/constants.ts: -------------------------------------------------------------------------------- 1 | export const BASE_URL: string = 'https://sandboxapi.fincra.com'; 2 | export const BASE_URL_PROD: string = 'https://api.fincra.com'; 3 | -------------------------------------------------------------------------------- /src/services/payouts/enum/payout-beneficiary.enum.ts: -------------------------------------------------------------------------------- 1 | export enum PayoutBeneficiaryType { 2 | individual = 'individual', 3 | corporation = 'corporation', 4 | } 5 | -------------------------------------------------------------------------------- /src/services/subaccounts/dto/index.ts: -------------------------------------------------------------------------------- 1 | export * from './create-subaccount.dto'; 2 | export * from './update-subaccount.dto'; 3 | export * from './fetch-subaccount.dto'; 4 | -------------------------------------------------------------------------------- /src/services/virtual-accounts/dto/sub-dto/index.ts: -------------------------------------------------------------------------------- 1 | export * from './kyc.dto'; 2 | export * from './ultimate-benficial-owners.dto'; 3 | export * from './kyc-corporate.dto'; 4 | -------------------------------------------------------------------------------- /src/services/virtual-accounts/enum/status.enum.ts: -------------------------------------------------------------------------------- 1 | export enum AccountStatusEnum { 2 | approved = 'approved', 3 | pending = 'pending', 4 | declined = 'declined', 5 | } 6 | -------------------------------------------------------------------------------- /global.d.ts: -------------------------------------------------------------------------------- 1 | declare namespace NodeJS { 2 | interface ProcessEnv { 3 | NODE_ENV: string; 4 | FINCRA_SECRET_KEY: string; 5 | FINCRA_PUBLIC_KEY: string; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/services/payouts/enum/payment-destination.enum.ts: -------------------------------------------------------------------------------- 1 | export enum PaymentDestinationEnum { 2 | bank_account = 'bank_account', 3 | mobile_money_wallet = 'mobile_money_wallet', 4 | } 5 | -------------------------------------------------------------------------------- /test/env.ts: -------------------------------------------------------------------------------- 1 | import sanitizedConfig from '../src/config/envconfig'; 2 | 3 | export const keys = [ 4 | sanitizedConfig.FINCRA_PUBLIC_KEY, 5 | sanitizedConfig.FINCRA_SECRET_KEY, 6 | ]; 7 | -------------------------------------------------------------------------------- /src/services/payouts/enum/payment-scheme.enum.ts: -------------------------------------------------------------------------------- 1 | export enum PaymentSchemeEnum { 2 | sepa = 'sepa', 3 | sepa_instant = 'sepa_instant', 4 | chaps = 'chaps', 5 | fps = 'fps', 6 | swift = 'swift', 7 | } 8 | -------------------------------------------------------------------------------- /src/utils/handle-errors.ts: -------------------------------------------------------------------------------- 1 | export const handleErrors = (error: any) => { 2 | return error.response.data; 3 | }; 4 | 5 | export const handleAxiosError = (error: any) => { 6 | return error.message; 7 | }; 8 | -------------------------------------------------------------------------------- /src/services/payouts/dto/list-payout.dto.ts: -------------------------------------------------------------------------------- 1 | import { ListCollectionMultipleVirtualAccountsDto } from '../../collections/dto'; 2 | 3 | export class ListPayoutDto extends ListCollectionMultipleVirtualAccountsDto {} 4 | -------------------------------------------------------------------------------- /src/types/wallets/index.ts: -------------------------------------------------------------------------------- 1 | export interface WalletLogsDto { 2 | business: string; 3 | 4 | amount?: string; 5 | 6 | perPage?: string; 7 | 8 | page?: string; 9 | 10 | action?: string; 11 | } 12 | -------------------------------------------------------------------------------- /src/utils/index.ts: -------------------------------------------------------------------------------- 1 | export * from './errors'; 2 | export * from './constants'; 3 | export * from './exclude-field'; 4 | export * from './handle-errors'; 5 | export * from './url'; 6 | export * from './interfaces'; 7 | -------------------------------------------------------------------------------- /src/types/beneficiaries/sub-dto/address.ts: -------------------------------------------------------------------------------- 1 | export interface AddressDto { 2 | country?: string; 3 | 4 | state?: string; 5 | 6 | zip?: string; 7 | 8 | city?: string; 9 | 10 | street?: string; 11 | } 12 | -------------------------------------------------------------------------------- /src/services/payouts/dto/index.ts: -------------------------------------------------------------------------------- 1 | export * from './create-payout.dto'; 2 | export * from './list-payout.dto'; 3 | export * from './wallet-to-wallet.dto'; 4 | export * from './sub-dto'; 5 | export * from './upload-payout.dto'; 6 | -------------------------------------------------------------------------------- /src/services/beneficiaries/dto/index.ts: -------------------------------------------------------------------------------- 1 | export * from './create-beneficiary.dto'; 2 | export * from './sub-dto'; 3 | export * from './list-beneficiary.dto'; 4 | export * from './update-beneficiary.dto'; 5 | export * from './delete-beneficiary.dto'; 6 | -------------------------------------------------------------------------------- /src/services/conversions/dto/create-conversion.dto.ts: -------------------------------------------------------------------------------- 1 | import { IsString } from 'class-validator'; 2 | 3 | export class CreateConversionDto { 4 | @IsString() 5 | business: string; 6 | 7 | @IsString() 8 | quoteReference: string; 9 | } 10 | -------------------------------------------------------------------------------- /src/types/conversions/index.ts: -------------------------------------------------------------------------------- 1 | export interface FetchConversionDto { 2 | conversionId: string; 3 | 4 | business?: string; 5 | } 6 | 7 | export interface CreateConversionDto { 8 | business: string; 9 | 10 | quoteReference: string; 11 | } 12 | -------------------------------------------------------------------------------- /src/services/subaccounts/dto/update-subaccount.dto.ts: -------------------------------------------------------------------------------- 1 | import { CreateSubAccountDto } from './'; 2 | 3 | export interface UpdateSubAccountDto 4 | extends Omit { 5 | business: string; 6 | subAccountId: string; 7 | } 8 | -------------------------------------------------------------------------------- /src/services/collections/dto/index.ts: -------------------------------------------------------------------------------- 1 | export * from './fetch-collection-additional-vaccount.dto'; 2 | export * from './list-collection-main-vaccount.dto'; 3 | export * from './list-collection-multiple-vaccounts.dto'; 4 | export * from './pay-with-transfer-collection.dto'; 5 | -------------------------------------------------------------------------------- /src/services/quotes/enum/quotes.enum.ts: -------------------------------------------------------------------------------- 1 | export enum TransactionType { 2 | conversion = 'conversion', 3 | disbursement = 'disbursement', 4 | } 5 | export enum BeneficiaryTypes { 6 | PENDING = 'PENDING', 7 | SUCCESS = 'SUCCESS', 8 | FAILED = 'FAILED', 9 | } 10 | -------------------------------------------------------------------------------- /src/types/index.ts: -------------------------------------------------------------------------------- 1 | export * from './beneficiaries'; 2 | export * from './collections'; 3 | export * from './conversions'; 4 | export * from './payouts'; 5 | export * from './quotes'; 6 | export * from './subaccounts'; 7 | export * from './verification'; 8 | export * from './wallets'; 9 | -------------------------------------------------------------------------------- /src/services/verification/dto/bvn-validation.dto.ts: -------------------------------------------------------------------------------- 1 | import { IsNotEmpty, IsString } from 'class-validator'; 2 | 3 | export class BvnResolutionDto { 4 | @IsString() 5 | @IsNotEmpty() 6 | bvn: string; 7 | 8 | @IsString() 9 | @IsNotEmpty() 10 | business: string; 11 | } 12 | -------------------------------------------------------------------------------- /src/types/verification/index.ts: -------------------------------------------------------------------------------- 1 | export interface VerifyBankAccountDto { 2 | accountNumber: string; 3 | bankCode: string; 4 | 5 | type?: string; 6 | 7 | iban?: string; 8 | } 9 | 10 | export interface BvnResolutionDto { 11 | bvn: string; 12 | 13 | business: string; 14 | } 15 | -------------------------------------------------------------------------------- /src/services/subaccounts/dto/fetch-subaccount.dto.ts: -------------------------------------------------------------------------------- 1 | import { IsString, IsNotEmpty } from 'class-validator'; 2 | 3 | export class FetchSubAccountDto { 4 | @IsString() 5 | @IsNotEmpty() 6 | businessId: string; 7 | 8 | @IsString() 9 | @IsNotEmpty() 10 | subAccountId: string; 11 | } 12 | -------------------------------------------------------------------------------- /src/services/chargebacks/dto/accept-chargeback.dto.ts: -------------------------------------------------------------------------------- 1 | import { IsNotEmpty, IsString } from 'class-validator'; 2 | 3 | export class AcceptChargeBackDto { 4 | @IsString() 5 | @IsNotEmpty() 6 | chargeBackId: string; 7 | 8 | @IsString() 9 | @IsNotEmpty() 10 | businessId: string; 11 | } 12 | -------------------------------------------------------------------------------- /src/types/beneficiaries/sub-dto/bank.ts: -------------------------------------------------------------------------------- 1 | import { AddressDto } from '../sub-dto'; 2 | 3 | export interface BankDto { 4 | name?: string; 5 | 6 | code?: string; 7 | 8 | sortCode?: string; 9 | 10 | swiftCode?: string; 11 | 12 | branch?: string; 13 | 14 | address?: AddressDto; 15 | } 16 | -------------------------------------------------------------------------------- /src/services/beneficiaries/dto/delete-beneficiary.dto.ts: -------------------------------------------------------------------------------- 1 | import { IsNotEmpty, IsString } from 'class-validator'; 2 | 3 | export class FetchDeleteBeneficiaryDto { 4 | @IsNotEmpty() 5 | @IsString() 6 | businessId: string; 7 | 8 | @IsNotEmpty() 9 | @IsString() 10 | beneficiaryId: string; 11 | } 12 | -------------------------------------------------------------------------------- /src/services/conversions/dto/fetch-conversion.dto.ts: -------------------------------------------------------------------------------- 1 | import { IsString, IsOptional, IsNotEmpty } from 'class-validator'; 2 | 3 | export class FetchConversionDto { 4 | @IsString() 5 | @IsNotEmpty() 6 | conversionId: string; 7 | 8 | @IsOptional() 9 | @IsString() 10 | business?: string; 11 | } 12 | -------------------------------------------------------------------------------- /src/services/beneficiaries/dto/update-beneficiary.dto.ts: -------------------------------------------------------------------------------- 1 | import { CreateBeneficiaryDto } from './create-beneficiary.dto'; 2 | import { IsNotEmpty, IsString } from 'class-validator'; 3 | 4 | export class UpdateBeneficiaryDto extends CreateBeneficiaryDto { 5 | @IsNotEmpty() 6 | @IsString() 7 | beneficiaryId: string; 8 | } 9 | -------------------------------------------------------------------------------- /src/services/collections/dto/fetch-collection-additional-vaccount.dto.ts: -------------------------------------------------------------------------------- 1 | import { IsString, IsOptional, IsNotEmpty } from 'class-validator'; 2 | 3 | export class FetchCollectionVirtualAccountDto { 4 | @IsOptional() 5 | @IsString() 6 | business?: string; 7 | 8 | @IsNotEmpty() 9 | @IsString() 10 | reference: string; 11 | } 12 | -------------------------------------------------------------------------------- /src/services/virtual-accounts/dto/sub-dto/kyc-corporate.dto.ts: -------------------------------------------------------------------------------- 1 | import { KycDto, UltimateBeneficialOwnersDto } from './'; 2 | 3 | export interface KycCorporateDto extends Omit { 4 | incorporationDate?: string; 5 | businessActivityDescription?: string; 6 | ultimateBeneficialOwners?: UltimateBeneficialOwnersDto[]; 7 | } 8 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | moduleFileExtensions: ['js', 'json', 'ts'], 3 | rootDir: './', 4 | testRegex: '.*\\.test\\.ts$', 5 | transform: { 6 | '.(ts)': 'ts-jest', 7 | }, 8 | collectCoverageFrom: ['**/*.(t|j)s'], 9 | coverageDirectory: './coverage', 10 | testEnvironment: 'node', 11 | testTimeout: 120000, 12 | }; 13 | -------------------------------------------------------------------------------- /src/services/chargebacks/dto/reject-chargeback.dto.ts: -------------------------------------------------------------------------------- 1 | import { IsNotEmpty, IsString } from 'class-validator'; 2 | 3 | export class RejectChargeBackDto { 4 | @IsString() 5 | @IsNotEmpty() 6 | chargeBackId: string; 7 | 8 | @IsString() 9 | @IsNotEmpty() 10 | businessId: string; 11 | 12 | @IsString() 13 | @IsNotEmpty() 14 | reason: string; 15 | } 16 | -------------------------------------------------------------------------------- /src/utils/exclude-field.ts: -------------------------------------------------------------------------------- 1 | /// utility function to exclude certain fields that should not be shown or sent to the client 2 | export const excludeFields = (fields: string[], objects: any): any => { 3 | const exclude = new Set(fields); 4 | const result = Object.fromEntries( 5 | Object.entries(objects).filter((e) => !exclude.has(e[0])) 6 | ); 7 | return result; 8 | }; 9 | -------------------------------------------------------------------------------- /src/services/beneficiaries/dto/list-beneficiary.dto.ts: -------------------------------------------------------------------------------- 1 | import { IsNotEmpty, IsString, IsOptional } from 'class-validator'; 2 | 3 | export class ListBeneficiaryDto { 4 | @IsNotEmpty() 5 | @IsString() 6 | businessId: string; 7 | 8 | @IsOptional() 9 | @IsString() 10 | page?: string; 11 | 12 | @IsOptional() 13 | @IsString() 14 | perPage?: string; 15 | } 16 | -------------------------------------------------------------------------------- /src/services/virtual-accounts/dto/index.ts: -------------------------------------------------------------------------------- 1 | export * from './create-virtual-account.dto'; 2 | export * from './create-instant-approval-vaccount.dto'; 3 | export * from './create-individual-sub-vaccount.dto'; 4 | export * from './create-corporate-vaccount.dto'; 5 | export * from './list-sub-virtual-accounts.dto'; 6 | export * from './list-merchant-vaccounts.dto'; 7 | export * from './sub-dto'; 8 | -------------------------------------------------------------------------------- /src/services/collections/dto/pay-with-transfer-collection.dto.ts: -------------------------------------------------------------------------------- 1 | import { IsString, IsOptional, IsNotEmpty } from 'class-validator'; 2 | 3 | export class PayWithTransferDto { 4 | @IsOptional() 5 | @IsString() 6 | name?: string; 7 | 8 | @IsOptional() 9 | @IsString() 10 | merchantReference?: string; 11 | 12 | @IsNotEmpty() 13 | @IsString() 14 | expiresAt: string; 15 | } 16 | -------------------------------------------------------------------------------- /src/types/quotes/index.ts: -------------------------------------------------------------------------------- 1 | export interface CreateQuoteDto { 2 | action: string; 3 | 4 | transactionType: string; 5 | 6 | feeBearer: string; 7 | 8 | beneficiaryType: string; 9 | 10 | sourceCurrency: string; 11 | 12 | destinationCurrency: string; 13 | 14 | amount: string; 15 | 16 | business: string; 17 | 18 | paymentScheme: string; 19 | 20 | paymentDestination: string; 21 | } 22 | -------------------------------------------------------------------------------- /src/services/payouts/dto/upload-payout.dto.ts: -------------------------------------------------------------------------------- 1 | import { IsString, IsNotEmpty, IsOptional } from 'class-validator'; 2 | 3 | export class UploadPayoutDto { 4 | @IsOptional() 5 | @IsString() 6 | name?: string; 7 | 8 | @IsOptional() 9 | @IsString() 10 | type?: string; 11 | 12 | @IsOptional() 13 | @IsString() 14 | file?: string; 15 | 16 | @IsNotEmpty() 17 | @IsString() 18 | reference: string; 19 | } 20 | -------------------------------------------------------------------------------- /src/services/verification/dto/verify-bank.dto.ts: -------------------------------------------------------------------------------- 1 | import { IsNotEmpty, IsOptional, IsString } from 'class-validator'; 2 | 3 | export class VerifyBankAccountDto { 4 | @IsString() 5 | @IsNotEmpty() 6 | accountNumber: string; 7 | 8 | @IsString() 9 | @IsNotEmpty() 10 | bankCode: string; 11 | 12 | @IsOptional() 13 | @IsString() 14 | type?: string; 15 | 16 | @IsOptional() 17 | @IsString() 18 | iban?: string; 19 | } 20 | -------------------------------------------------------------------------------- /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2.1 2 | 3 | orbs: 4 | node: circleci/node@5.0.2 5 | jobs: 6 | build-and-test: 7 | executor: node/default 8 | steps: 9 | - checkout 10 | - node/install-packages: 11 | pkg-manager: yarn 12 | - run: 13 | name: Running tests 14 | command: | 15 | yarn test 16 | 17 | workflows: 18 | ship-software: 19 | jobs: 20 | - build-and-test 21 | -------------------------------------------------------------------------------- /src/services/collections/dto/list-collection-main-vaccount.dto.ts: -------------------------------------------------------------------------------- 1 | import { IsString, IsOptional } from 'class-validator'; 2 | 3 | export class ListCollectionMainVirtualAccountDto { 4 | @IsOptional() 5 | @IsString() 6 | business?: string; 7 | 8 | @IsOptional() 9 | @IsString() 10 | reference?: string; 11 | 12 | @IsOptional() 13 | @IsString() 14 | page?: string; 15 | 16 | @IsOptional() 17 | @IsString() 18 | perPage?: string; 19 | } 20 | -------------------------------------------------------------------------------- /src/services/virtual-accounts/dto/list-sub-virtual-accounts.dto.ts: -------------------------------------------------------------------------------- 1 | import { IsString, IsNotEmpty, IsOptional } from 'class-validator'; 2 | 3 | export class ListSubVirtualAccountsDto { 4 | @IsNotEmpty() 5 | @IsString() 6 | businessId: string; 7 | 8 | @IsNotEmpty() 9 | @IsString() 10 | subAccountId: string; 11 | 12 | @IsOptional() 13 | @IsString() 14 | page?: string; 15 | 16 | @IsOptional() 17 | @IsString() 18 | perPage?: string; 19 | } 20 | -------------------------------------------------------------------------------- /src/services/beneficiaries/dto/sub-dto/address.dto.ts: -------------------------------------------------------------------------------- 1 | import { IsString, IsOptional } from 'class-validator'; 2 | 3 | export class AddressDto { 4 | @IsOptional() 5 | @IsString() 6 | country?: string; 7 | 8 | @IsOptional() 9 | @IsString() 10 | state?: string; 11 | 12 | @IsOptional() 13 | @IsString() 14 | zip?: string; 15 | 16 | @IsOptional() 17 | @IsString() 18 | city?: string; 19 | 20 | @IsOptional() 21 | @IsString() 22 | street?: string; 23 | } 24 | -------------------------------------------------------------------------------- /src/services/payouts/dto/wallet-to-wallet.dto.ts: -------------------------------------------------------------------------------- 1 | import { IsString, IsNotEmpty } from 'class-validator'; 2 | export class WalletToWalletTransferDto { 3 | @IsString() 4 | @IsNotEmpty() 5 | amount: string; 6 | @IsString() 7 | @IsNotEmpty() 8 | beneficiaryWalletNumber: string; 9 | @IsString() 10 | @IsNotEmpty() 11 | business: string; 12 | @IsString() 13 | @IsNotEmpty() 14 | customerReference: string; 15 | @IsString() 16 | @IsNotEmpty() 17 | description: string; 18 | } 19 | -------------------------------------------------------------------------------- /src/services/index.ts: -------------------------------------------------------------------------------- 1 | export * from './business-id/business'; 2 | export * from './chargebacks/chargeback'; 3 | export * from './conversions/conversion'; 4 | export * from './quotes/quote'; 5 | export * from './wallets/wallet'; 6 | export * from './verification/verify'; 7 | export * from './beneficiaries/beneficiary'; 8 | export * from './payouts/payout'; 9 | export * from './subaccounts/subaccount'; 10 | export * from './collections/collection'; 11 | export * from './virtual-accounts/vaccounts'; 12 | -------------------------------------------------------------------------------- /src/types/subaccounts/index.ts: -------------------------------------------------------------------------------- 1 | export interface CreateSubAccountDto { 2 | businessId: string; 3 | 4 | name?: string; 5 | 6 | email?: string; 7 | 8 | mobile?: string; 9 | 10 | country?: string; 11 | } 12 | 13 | export interface FetchSubAccountDto { 14 | businessId: string; 15 | 16 | subAccountId: string; 17 | } 18 | 19 | export interface UpdateSubAccountDto 20 | extends Omit { 21 | business: string; 22 | subAccountId: string; 23 | } 24 | -------------------------------------------------------------------------------- /src/utils/url.ts: -------------------------------------------------------------------------------- 1 | import { BASE_URL_PROD, BASE_URL } from './constants'; 2 | import { IEnvironment } from './interfaces'; 3 | 4 | /** 5 | * If the public key starts with pk_prod, return the production URL, otherwise return the sandbox URL 6 | * @param {string} publicKey - Your public key. 7 | * @returns The base url for the public key 8 | */ 9 | export const getUrl = (env: IEnvironment = { sandbox: false }): string => { 10 | if (env.sandbox) { 11 | return BASE_URL; 12 | } 13 | return BASE_URL_PROD; 14 | }; 15 | -------------------------------------------------------------------------------- /src/services/subaccounts/dto/create-subaccount.dto.ts: -------------------------------------------------------------------------------- 1 | import { IsString, IsOptional, IsNotEmpty, IsEmail } from 'class-validator'; 2 | 3 | export class CreateSubAccountDto { 4 | @IsString() 5 | @IsNotEmpty() 6 | businessId: string; 7 | 8 | @IsOptional() 9 | @IsString() 10 | name?: string; 11 | 12 | @IsOptional() 13 | @IsString() 14 | @IsEmail() 15 | email?: string; 16 | 17 | @IsOptional() 18 | @IsString() 19 | mobile?: string; 20 | 21 | @IsOptional() 22 | @IsString() 23 | country?: string; 24 | } 25 | -------------------------------------------------------------------------------- /src/services/payouts/dto/sub-dto/payout-document.dto.ts: -------------------------------------------------------------------------------- 1 | import { IsString, IsOptional, IsNotEmpty } from 'class-validator'; 2 | 3 | export class PayoutDocumentDto { 4 | @IsString() 5 | @IsNotEmpty() 6 | type: string; 7 | 8 | @IsString() 9 | @IsNotEmpty() 10 | number: string; 11 | 12 | @IsString() 13 | @IsNotEmpty() 14 | issuedCountryCode: string; 15 | 16 | @IsString() 17 | @IsNotEmpty() 18 | issuedBy: string; 19 | 20 | @IsString() 21 | @IsNotEmpty() 22 | issuedDate: string; 23 | 24 | @IsOptional() 25 | @IsString() 26 | expirationDate?: string; 27 | } 28 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | # compiled output 2 | /dist 3 | /node_modules 4 | *.env 5 | 6 | # Logs 7 | logs 8 | *.log 9 | npm-debug.log* 10 | pnpm-debug.log* 11 | yarn-debug.log* 12 | yarn-error.log* 13 | lerna-debug.log* 14 | 15 | todo.md 16 | 17 | # OS 18 | .DS_Store 19 | junk.ts 20 | 21 | # Tests 22 | /coverage 23 | /.nyc_output 24 | envvars* 25 | 26 | # IDEs and editors 27 | /.idea 28 | .project 29 | .classpath 30 | .c9/ 31 | *.launch 32 | .settings/ 33 | *.sublime-workspace 34 | 35 | # IDE - VSCode 36 | .vscode/* 37 | !.vscode/settings.json 38 | !.vscode/tasks.json 39 | !.vscode/launch.json 40 | !.vscode/extensions.json -------------------------------------------------------------------------------- /src/services/virtual-accounts/dto/list-merchant-vaccounts.dto.ts: -------------------------------------------------------------------------------- 1 | import { IsString, IsNotEmpty, IsOptional } from 'class-validator'; 2 | 3 | export class ListMerchantVirtualAccountsDto { 4 | @IsNotEmpty() 5 | @IsString() 6 | currency: string; 7 | 8 | @IsNotEmpty() 9 | @IsString() 10 | businessName: string; 11 | 12 | @IsOptional() 13 | @IsString() 14 | issuedDate?: string; 15 | 16 | @IsOptional() 17 | @IsString() 18 | requestedDate?: string; 19 | 20 | @IsOptional() 21 | @IsString() 22 | accountNumber?: string; 23 | 24 | @IsOptional() 25 | @IsString() 26 | status?: string; 27 | } 28 | -------------------------------------------------------------------------------- /src/utils/errors/error.base.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * A custom error class for handling the library related errors. 3 | * @class BaseError 4 | */ 5 | export class BaseError extends Error { 6 | /** 7 | * The BaseError Constructor. 8 | * @param {Record} options - A configuration object for errors. 9 | * @param {String} options.message - The error message if any. 10 | * @constructor BaseError 11 | */ 12 | constructor(options: Record = {}) { 13 | super(); 14 | Error.captureStackTrace(this, this.constructor); 15 | this.name = this.constructor.name; 16 | this.message = options.message; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /test/services/business-id/business.test.ts: -------------------------------------------------------------------------------- 1 | import { Business } from '../../../src/services'; 2 | import { keys } from '../../env'; 3 | 4 | const businessTestInstance = new Business(keys[0], keys[1], { sandbox: true }); 5 | 6 | describe('service that returns the business information of a merchant', () => { 7 | it('returns the business info of a merchant', async () => { 8 | try { 9 | const result = await businessTestInstance.getBusinessId(); 10 | expect(result).toHaveBeenCalled(); 11 | expect(typeof result).toBe('object'); 12 | } catch (error) { 13 | expect(error).toBeInstanceOf(Error); 14 | } 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /src/services/wallets/dto/wallet-logs.dto.ts: -------------------------------------------------------------------------------- 1 | import { IsNotEmpty, IsString, IsOptional } from 'class-validator'; 2 | 3 | /* It's a class that defines the shape of the data that will be sent to the API */ 4 | export class WalletLogsDto { 5 | @IsString() 6 | @IsNotEmpty() 7 | business: string; 8 | 9 | @IsOptional() 10 | @IsString() 11 | @IsNotEmpty() 12 | amount?: string; 13 | 14 | @IsOptional() 15 | @IsString() 16 | @IsNotEmpty() 17 | perPage?: string; 18 | 19 | @IsOptional() 20 | @IsString() 21 | @IsNotEmpty() 22 | page?: string; 23 | 24 | @IsOptional() 25 | @IsString() 26 | @IsNotEmpty() 27 | action?: string; 28 | } 29 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # compiled output 2 | /dist 3 | /node_modules 4 | *.env 5 | 6 | # Logs 7 | logs 8 | *.log 9 | npm-debug.log* 10 | pnpm-debug.log* 11 | yarn-debug.log* 12 | yarn-error.log* 13 | lerna-debug.log* 14 | 15 | todo.md 16 | 17 | # OS 18 | .DS_Store 19 | junk.ts 20 | 21 | # Tests 22 | /coverage 23 | /.nyc_output 24 | envvars* 25 | 26 | # IDEs and editors 27 | /.idea 28 | .project 29 | .classpath 30 | .c9/ 31 | *.launch 32 | .settings/ 33 | *.sublime-workspace 34 | 35 | # IDE - VSCode 36 | .vscode/* 37 | !.vscode/settings.json 38 | !.vscode/tasks.json 39 | !.vscode/launch.json 40 | !.vscode/extensions.json 41 | 42 | file.js 43 | test.ts 44 | 45 | # gh actions 46 | # .github/ -------------------------------------------------------------------------------- /src/services/beneficiaries/dto/sub-dto/bank.dto.ts: -------------------------------------------------------------------------------- 1 | import { 2 | IsString, 3 | IsOptional, 4 | IsObject, 5 | ValidateNested, 6 | } from 'class-validator'; 7 | import { AddressDto } from './address.dto'; 8 | 9 | export class BankDto { 10 | @IsOptional() 11 | @IsString() 12 | name?: string; 13 | 14 | @IsOptional() 15 | @IsString() 16 | code?: string; 17 | 18 | @IsOptional() 19 | @IsString() 20 | sortCode?: string; 21 | 22 | @IsOptional() 23 | @IsString() 24 | swiftCode?: string; 25 | 26 | @IsOptional() 27 | @IsString() 28 | branch?: string; 29 | 30 | @IsOptional() 31 | @IsObject() 32 | @ValidateNested() 33 | address?: AddressDto; 34 | } 35 | -------------------------------------------------------------------------------- /src/services/virtual-accounts/dto/sub-dto/ultimate-benficial-owners.dto.ts: -------------------------------------------------------------------------------- 1 | import { PayoutDocumentDto } from '../../../payouts/dto'; 2 | import { 3 | IsString, 4 | IsOptional, 5 | IsNotEmpty, 6 | ValidateNested, 7 | IsObject, 8 | } from 'class-validator'; 9 | 10 | export class UltimateBeneficialOwnersDto { 11 | @IsString() 12 | @IsNotEmpty() 13 | firstName: string; 14 | 15 | @IsString() 16 | @IsNotEmpty() 17 | lastName: string; 18 | 19 | @IsString() 20 | @IsNotEmpty() 21 | ownershipPercentage: string; 22 | 23 | @IsString() 24 | @IsNotEmpty() 25 | politicallyExposedPerson: string; 26 | 27 | @IsOptional() 28 | @IsObject() 29 | @ValidateNested() 30 | document?: PayoutDocumentDto; 31 | } 32 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish to NPM 2 | on: 3 | release: 4 | types: [created] 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - name: Checkout 10 | uses: actions/checkout@v2 11 | - name: Setup Node 12 | uses: actions/setup-node@v2 13 | with: 14 | node-version: '18.x' 15 | registry-url: 'https://registry.npmjs.org' 16 | - name: Install dependencies and build 🔧 17 | run: npm i && npm run build 18 | - name: Publish package on NPM 📦 19 | run: npm publish 20 | env: 21 | NODE_AUTH_TOKEN: ${{ secrets.NPM_PUBLISH_TOKEN }} 22 | FINCRA_SECRET_KEY: ${{ secrets.FINCRA_SECRET_KEY }} 23 | FINCRA_PUBLIC_KEY: ${{ secrets.FINCRA_PUBLIC_KEY }} 24 | -------------------------------------------------------------------------------- /src/types/collections/index.ts: -------------------------------------------------------------------------------- 1 | export interface PayWithTransferDto { 2 | name?: string; 3 | 4 | merchantReference?: string; 5 | 6 | expiresAt: string; 7 | } 8 | 9 | export interface ListCollectionMultipleVirtualAccountsDto { 10 | business: string; 11 | 12 | status: string[]; 13 | 14 | sourceCurrency: string; 15 | 16 | destinationCurrency: string; 17 | 18 | subAccount: string; 19 | 20 | page?: string; 21 | 22 | perPage?: string; 23 | 24 | dateFrom: string; 25 | 26 | dateTo: string; 27 | } 28 | 29 | export interface ListCollectionMainVirtualAccountDto { 30 | business?: string; 31 | 32 | reference?: string; 33 | 34 | page?: string; 35 | 36 | perPage?: string; 37 | } 38 | 39 | export interface FetchCollectionVirtualAccountDto { 40 | business?: string; 41 | 42 | reference: string; 43 | } 44 | -------------------------------------------------------------------------------- /src/types/payouts/sub-dto/payouts-sub.ts: -------------------------------------------------------------------------------- 1 | import { AddressDto } from '../../beneficiaries/sub-dto'; 2 | 3 | export interface PayoutBeneficiaryDto { 4 | firstName?: string; 5 | 6 | lastName?: string; 7 | 8 | email?: string; 9 | 10 | type: string; 11 | 12 | accountHolderName: string; 13 | 14 | accountNumber: string; 15 | 16 | mobileMoneyCode?: string; 17 | 18 | country?: string; 19 | 20 | bankCode?: string; 21 | 22 | bankSwiftCode?: string; 23 | 24 | sortCode?: string; 25 | 26 | registrationNumber?: string; 27 | 28 | address?: AddressDto; 29 | 30 | document?: PayoutDocumentDto; 31 | } 32 | 33 | export interface PayoutDocumentDto { 34 | type: string; 35 | 36 | number: string; 37 | 38 | issuedCountryCode: string; 39 | 40 | issuedBy: string; 41 | 42 | issuedDate: string; 43 | 44 | expirationDate?: string; 45 | } 46 | -------------------------------------------------------------------------------- /src/services/collections/dto/list-collection-multiple-vaccounts.dto.ts: -------------------------------------------------------------------------------- 1 | import { 2 | IsString, 3 | IsOptional, 4 | IsNotEmpty, 5 | IsArray, 6 | IsISO8601, 7 | } from 'class-validator'; 8 | 9 | export class ListCollectionMultipleVirtualAccountsDto { 10 | @IsNotEmpty() 11 | @IsString() 12 | business: string; 13 | 14 | @IsNotEmpty() 15 | @IsArray() 16 | status: string[]; 17 | 18 | @IsNotEmpty() 19 | @IsString() 20 | sourceCurrency: string; 21 | 22 | @IsNotEmpty() 23 | @IsString() 24 | destinationCurrency: string; 25 | 26 | @IsNotEmpty() 27 | @IsString() 28 | subAccount: string; 29 | 30 | @IsOptional() 31 | @IsString() 32 | page?: string; 33 | 34 | @IsOptional() 35 | @IsString() 36 | perPage?: string; 37 | 38 | @IsNotEmpty() 39 | @IsISO8601() 40 | dateFrom: string; 41 | 42 | @IsNotEmpty() 43 | @IsISO8601() 44 | dateTo: string; 45 | } 46 | -------------------------------------------------------------------------------- /src/services/virtual-accounts/dto/create-individual-sub-vaccount.dto.ts: -------------------------------------------------------------------------------- 1 | import { KycDto } from './sub-dto'; 2 | import { 3 | IsString, 4 | IsNotEmpty, 5 | IsArray, 6 | IsOptional, 7 | IsObject, 8 | ValidateNested, 9 | } from 'class-validator'; 10 | 11 | export class CreateIndividualSubAccountDto { 12 | @IsString() 13 | @IsNotEmpty() 14 | currency: string; 15 | 16 | @IsString() 17 | @IsNotEmpty() 18 | accountType: string; 19 | 20 | @IsString() 21 | @IsNotEmpty() 22 | businessId: string; 23 | 24 | @IsString() 25 | @IsNotEmpty() 26 | subAccountId: string; 27 | 28 | @IsOptional() 29 | @IsArray() 30 | meansOfId?: string[]; 31 | 32 | @IsOptional() 33 | @IsString() 34 | utilityBill?: string; 35 | 36 | @IsOptional() 37 | @IsString() 38 | channel?: string; 39 | 40 | @IsOptional() 41 | @IsObject() 42 | @ValidateNested() 43 | KYCInformation?: KycDto; 44 | } 45 | -------------------------------------------------------------------------------- /src/services/virtual-accounts/dto/create-instant-approval-vaccount.dto.ts: -------------------------------------------------------------------------------- 1 | import { KycDto } from './sub-dto'; 2 | import { 3 | IsOptional, 4 | IsNotEmpty, 5 | IsString, 6 | IsObject, 7 | IsArray, 8 | ValidateNested, 9 | } from 'class-validator'; 10 | 11 | export class CreateInstantApprovalVirtualAccountDto { 12 | @IsNotEmpty() 13 | @IsString() 14 | businessId: string; 15 | 16 | @IsNotEmpty() 17 | @IsString() 18 | subAccountId: string; 19 | 20 | @IsNotEmpty() 21 | @IsString() 22 | currency: string; 23 | 24 | @IsNotEmpty() 25 | @IsString() 26 | accountType: string; 27 | 28 | @IsNotEmpty() 29 | @IsObject() 30 | @ValidateNested() 31 | KYCInformation: KycDto; 32 | 33 | @IsOptional() 34 | @IsArray() 35 | meansOfId?: string[]; 36 | 37 | @IsOptional() 38 | @IsString() 39 | utilityBill?: string; 40 | 41 | @IsOptional() 42 | @IsString() 43 | channel?: string; 44 | } 45 | -------------------------------------------------------------------------------- /src/types/beneficiaries/index.ts: -------------------------------------------------------------------------------- 1 | import { AddressDto, BankDto } from './sub-dto'; 2 | 3 | export interface CreateBeneficiaryDto { 4 | businessId: string; 5 | 6 | firstName: string; 7 | 8 | lastName?: string; 9 | 10 | email?: string; 11 | 12 | phoneNumber?: string; 13 | 14 | accountHolderName: string; 15 | 16 | bank?: BankDto; 17 | 18 | address?: AddressDto; 19 | 20 | type: string; 21 | 22 | currency: string; 23 | 24 | paymentDestination: string; 25 | 26 | destinationAddress: string; 27 | 28 | uniqueIdentifier?: string; 29 | } 30 | 31 | export interface ListBeneficiaryDto { 32 | businessId: string; 33 | 34 | page?: string; 35 | 36 | perPage?: string; 37 | } 38 | 39 | export interface UpdateBeneficiaryDto extends CreateBeneficiaryDto { 40 | beneficiaryId: string; 41 | } 42 | 43 | export interface FetchDeleteBeneficiaryDto { 44 | businessId: string; 45 | 46 | beneficiaryId: string; 47 | } 48 | -------------------------------------------------------------------------------- /src/services/quotes/dto/create-quote.dto.ts: -------------------------------------------------------------------------------- 1 | import { IsNotEmpty, IsString } from 'class-validator'; 2 | 3 | /* It's a class that defines the shape of the data that will be sent to the API */ 4 | export class CreateQuoteDto { 5 | @IsString() 6 | @IsNotEmpty() 7 | action: string; 8 | 9 | @IsString() 10 | @IsNotEmpty() 11 | transactionType: string; 12 | 13 | @IsString() 14 | @IsNotEmpty() 15 | feeBearer: string; 16 | 17 | @IsString() 18 | @IsNotEmpty() 19 | beneficiaryType: string; 20 | 21 | @IsString() 22 | @IsNotEmpty() 23 | sourceCurrency: string; 24 | 25 | @IsString() 26 | @IsNotEmpty() 27 | destinationCurrency: string; 28 | 29 | @IsString() 30 | @IsNotEmpty() 31 | amount: string; 32 | 33 | @IsString() 34 | @IsNotEmpty() 35 | business: string; 36 | 37 | @IsString() 38 | @IsNotEmpty() 39 | paymentScheme: string; 40 | 41 | @IsString() 42 | @IsNotEmpty() 43 | paymentDestination: string; 44 | } 45 | -------------------------------------------------------------------------------- /src/services/payouts/dto/create-payout.dto.ts: -------------------------------------------------------------------------------- 1 | import { 2 | IsString, 3 | IsNotEmpty, 4 | IsOptional, 5 | IsObject, 6 | ValidateNested, 7 | } from 'class-validator'; 8 | import { PayoutBeneficiaryDto } from './sub-dto'; 9 | export class CreatePayoutDto { 10 | @IsNotEmpty() 11 | @IsString() 12 | sourceCurrency: string; 13 | 14 | @IsNotEmpty() 15 | @IsString() 16 | destinationCurrency: string; 17 | 18 | @IsNotEmpty() 19 | @IsString() 20 | amount: string; 21 | 22 | @IsNotEmpty() 23 | @IsString() 24 | business: string; 25 | 26 | @IsNotEmpty() 27 | @IsString() 28 | description: string; 29 | 30 | @IsOptional() 31 | @IsString() 32 | customerReference?: string; 33 | 34 | @IsOptional() 35 | @IsObject() 36 | @ValidateNested() 37 | beneficiary?: PayoutBeneficiaryDto; 38 | 39 | @IsOptional() 40 | @IsString() 41 | paymentDestination?: string; 42 | 43 | @IsOptional() 44 | @IsString() 45 | paymentScheme?: string; 46 | 47 | @IsOptional() 48 | @IsString() 49 | quoteReference?: string; 50 | } 51 | -------------------------------------------------------------------------------- /src/services/virtual-accounts/dto/create-corporate-vaccount.dto.ts: -------------------------------------------------------------------------------- 1 | import { KycCorporateDto } from './sub-dto'; 2 | import { 3 | IsOptional, 4 | IsNotEmpty, 5 | IsString, 6 | IsObject, 7 | ValidateNested, 8 | } from 'class-validator'; 9 | 10 | export class CreateCorporateVirtualAccountDto { 11 | @IsNotEmpty() 12 | @IsString() 13 | businessId: string; 14 | 15 | @IsNotEmpty() 16 | @IsString() 17 | subAccountId: string; 18 | 19 | @IsNotEmpty() 20 | @IsString() 21 | currency: string; 22 | 23 | @IsNotEmpty() 24 | @IsString() 25 | accountType: string; 26 | 27 | @IsNotEmpty() 28 | @IsObject() 29 | @ValidateNested() 30 | KYCInformation: KycCorporateDto; 31 | 32 | @IsOptional() 33 | @IsString() 34 | entityName?: string; 35 | 36 | @IsOptional() 37 | @IsString() 38 | reason?: string; 39 | 40 | @IsOptional() 41 | @IsString() 42 | monthlyVolume?: string; 43 | 44 | @IsOptional() 45 | @IsString() 46 | paymentFlowDescription?: string; 47 | 48 | @IsOptional() 49 | @IsString() 50 | channel?: string; 51 | } 52 | -------------------------------------------------------------------------------- /src/types/payouts/index.ts: -------------------------------------------------------------------------------- 1 | import { ListCollectionMultipleVirtualAccountsDto } from '../collections'; 2 | import { PayoutBeneficiaryDto } from './sub-dto/payouts-sub'; 3 | 4 | export interface WalletToWalletTransferDto { 5 | amount: string; 6 | 7 | beneficiaryWalletNumber: string; 8 | 9 | business: string; 10 | 11 | customerReference: string; 12 | 13 | description: string; 14 | } 15 | 16 | export interface UploadPayoutDto { 17 | name?: string; 18 | 19 | type?: string; 20 | 21 | file?: string; 22 | 23 | reference: string; 24 | } 25 | 26 | export interface CreatePayoutDto { 27 | sourceCurrency: string; 28 | 29 | destinationCurrency: string; 30 | 31 | amount: string; 32 | 33 | business: string; 34 | 35 | description: string; 36 | 37 | customerReference?: string; 38 | 39 | beneficiary?: PayoutBeneficiaryDto; 40 | 41 | paymentDestination?: string; 42 | 43 | paymentScheme?: string; 44 | 45 | quoteReference?: string; 46 | } 47 | 48 | export interface ListPayoutDto 49 | extends ListCollectionMultipleVirtualAccountsDto {} 50 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Thank you for your contributing to this SDK, kindly follow the guidelines below: 4 | 5 | - Create a fork of the repository. 6 | - Create a new branch for your contribution. 7 | - Add your contribution to the new branch and push your changes. 8 | - Submit a pull request to the main branch. 9 | - If all goes well, the pull request will be merged to the main branch. 10 | 11 | **Recommended**: It is possible that the main branch would have been updated before you made a contribution so you can frequently keep rebasing off the main branch while you make your contributions. i.e 12 | 13 | - fetch from origin 14 | 15 | ``` 16 | $ git fetch origin 17 | ``` 18 | 19 | - then you can rebase off the main branch like so: 20 | 21 | ``` 22 | $ git rebase origin/main 23 | ``` 24 | 25 | This will keep your changes in sync with the main branch and most importantly, the git history will be clean and releasable. 26 | 27 | ### LICENSE 28 | 29 | This project is licenced under the [GNU GPL Licence](https://github.com/E-wave112/fincra-node-sdk/blob/main/LICENCE). 30 | -------------------------------------------------------------------------------- /src/services/virtual-accounts/dto/create-virtual-account.dto.ts: -------------------------------------------------------------------------------- 1 | import { KycDto } from './sub-dto'; 2 | import { 3 | IsOptional, 4 | IsNotEmpty, 5 | IsString, 6 | IsObject, 7 | ValidateNested, 8 | IsArray, 9 | } from 'class-validator'; 10 | 11 | export class CreateVirtualAccountDto { 12 | @IsNotEmpty() 13 | @IsString() 14 | currency: string; 15 | 16 | @IsNotEmpty() 17 | @IsString() 18 | accountType: string; 19 | 20 | @IsNotEmpty() 21 | @IsObject() 22 | @ValidateNested() 23 | KYCInformation: KycDto; 24 | 25 | @IsOptional() 26 | @IsArray() 27 | meansOfId?: string[]; 28 | 29 | @IsOptional() 30 | @IsString() 31 | utilityBill?: string; 32 | 33 | @IsOptional() 34 | @IsString() 35 | reason?: string; 36 | 37 | @IsOptional() 38 | @IsString() 39 | paymentFlowDescription?: string; 40 | 41 | @IsOptional() 42 | @IsString() 43 | monthlyVolume?: string; 44 | 45 | @IsOptional() 46 | @IsString() 47 | entityName?: string; 48 | 49 | @IsOptional() 50 | @IsString() 51 | attachments?: string; 52 | 53 | @IsOptional() 54 | @IsString() 55 | channel?: string; 56 | } 57 | -------------------------------------------------------------------------------- /src/services/virtual-accounts/dto/sub-dto/kyc.dto.ts: -------------------------------------------------------------------------------- 1 | import { AddressDto } from '../../../beneficiaries/dto'; 2 | import { PayoutDocumentDto } from '../../../payouts/dto'; 3 | import { 4 | IsOptional, 5 | IsString, 6 | IsObject, 7 | ValidateNested, 8 | } from 'class-validator'; 9 | 10 | export class KycDto { 11 | @IsOptional() 12 | @IsString() 13 | firstName?: string; 14 | 15 | @IsOptional() 16 | @IsString() 17 | lastName?: string; 18 | 19 | @IsOptional() 20 | @IsString() 21 | email?: string; 22 | 23 | @IsOptional() 24 | @IsString() 25 | businessName?: string; 26 | 27 | @IsOptional() 28 | @IsString() 29 | bvn?: string; 30 | 31 | @IsOptional() 32 | @IsString() 33 | birthDate?: string; 34 | 35 | @IsOptional() 36 | @IsString() 37 | occupation?: string; 38 | 39 | @IsOptional() 40 | @IsString() 41 | businessCategory?: string; 42 | 43 | @IsOptional() 44 | @IsString() 45 | additionalInfo?: string; 46 | 47 | @IsOptional() 48 | @IsObject() 49 | @ValidateNested() 50 | address?: AddressDto; 51 | 52 | @IsOptional() 53 | @IsObject() 54 | @ValidateNested() 55 | document?: PayoutDocumentDto; 56 | } 57 | -------------------------------------------------------------------------------- /src/services/business-id/business.ts: -------------------------------------------------------------------------------- 1 | import { FincraCore } from '../../api'; 2 | import { BaseError, handleErrors, IEnvironment } from '../../utils'; 3 | 4 | /** 5 | * The Business module for handling the business related operations. 6 | * @class Business 7 | * @extends FincraCore 8 | * @param {string} publicKey - The public key of the merchant 9 | * @param {string} secretKey - The secret key of the merchant 10 | * @param {IEnvironment} environment - The environment of the merchant 11 | */ 12 | export class Business extends FincraCore { 13 | constructor( 14 | publicKey: string, 15 | secretKey: string, 16 | environment?: IEnvironment 17 | ) { 18 | super(publicKey, secretKey, environment); 19 | } 20 | 21 | /** 22 | * This method gets the business id of the merchant. 23 | * @returns The business id and other information of the merchant 24 | */ 25 | public async getBusinessId() { 26 | try { 27 | const request = this.getBaseUrl(); 28 | const response = await request.get(`/profile/merchants/me`); 29 | return response.data; 30 | } catch (error) { 31 | throw new BaseError({ message: handleErrors(error) }); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /test/services/quotes/quotes.test.ts: -------------------------------------------------------------------------------- 1 | import { Quote } from '../../../src/services'; 2 | import { CreateQuoteDto } from '../../../src/services/quotes/dto'; 3 | import { keys } from '../../env'; 4 | 5 | const quoteInstance = new Quote(keys[0], keys[1], { sandbox: true }); 6 | 7 | describe('service to create a quote object for a merchant', () => { 8 | it('returns a message object on the status of a quote', async () => { 9 | try { 10 | const quote = new CreateQuoteDto(); 11 | quote.action = 'payment'; 12 | quote.amount = '2000'; 13 | quote.beneficiaryType = 'next-of-kin'; 14 | quote.business = 'd0310ab1-0352-497e-93f2-aef33e6bb043'; 15 | quote.destinationCurrency = 'NGN'; 16 | quote.feeBearer = 'customer'; 17 | quote.paymentDestination = 'bank_account'; 18 | quote.paymentScheme = ''; 19 | quote.sourceCurrency = 'NGN'; 20 | quote.transactionType = 'conversion'; 21 | 22 | const result = await quoteInstance.createQuote(quote); 23 | expect(result).toHaveBeenCalledWith(quote); 24 | expect(typeof result).toBe('object'); 25 | } catch (error) { 26 | expect(error).toBeInstanceOf(Error); 27 | } 28 | }); 29 | }); 30 | -------------------------------------------------------------------------------- /src/services/quotes/quote.ts: -------------------------------------------------------------------------------- 1 | import { FincraCore } from '../../api'; 2 | import { BaseError, handleErrors, IEnvironment } from '../../utils'; 3 | import { CreateQuoteDto } from './dto'; 4 | 5 | /** 6 | * The quote module for handling the quote related operations. 7 | * @class Quote 8 | * @extends FincraCore 9 | * @param {string} publicKey - The public key of the merchant 10 | * @param {string} secretKey - The secret key of the merchant 11 | * @param {IEnvironment} environment - The environment of the merchant 12 | * 13 | */ 14 | export class Quote extends FincraCore { 15 | constructor( 16 | publicKey: string, 17 | secretKey: string, 18 | environment?: IEnvironment 19 | ) { 20 | super(publicKey, secretKey, environment); 21 | } 22 | 23 | /** 24 | * It takes in a CreateQuoteDto object, makes a post request to the quotes endpoint, and returns the 25 | * response data 26 | * @param {CreateQuoteDto} data - The data to be sent to the server. 27 | * @returns The response from the API; a quote object 28 | */ 29 | public async createQuote(data: CreateQuoteDto) { 30 | try { 31 | const request = this.getBaseUrl(); 32 | const response = await request.post(`/quotes/generate`, data); 33 | return response.data; 34 | } catch (error) { 35 | throw new BaseError({ message: handleErrors(error) }); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/services/beneficiaries/dto/create-beneficiary.dto.ts: -------------------------------------------------------------------------------- 1 | import { 2 | IsNotEmpty, 3 | IsString, 4 | IsOptional, 5 | IsEmail, 6 | IsMobilePhone, 7 | IsObject, 8 | ValidateNested, 9 | } from 'class-validator'; 10 | import { AddressDto, BankDto } from './sub-dto'; 11 | 12 | export class CreateBeneficiaryDto { 13 | @IsNotEmpty() 14 | @IsString() 15 | businessId: string; 16 | 17 | @IsNotEmpty() 18 | @IsString() 19 | firstName: string; 20 | 21 | @IsOptional() 22 | @IsString() 23 | lastName?: string; 24 | 25 | @IsOptional() 26 | @IsString() 27 | @IsEmail() 28 | email?: string; 29 | 30 | @IsOptional() 31 | @IsMobilePhone('en-NG', { message: 'Phone number is not valid' }) 32 | @IsString() 33 | phoneNumber?: string; 34 | 35 | @IsNotEmpty() 36 | @IsString() 37 | accountHolderName: string; 38 | 39 | @IsOptional() 40 | @IsObject() 41 | @ValidateNested() 42 | bank?: BankDto; 43 | 44 | @IsOptional() 45 | @IsObject() 46 | @ValidateNested() 47 | address?: AddressDto; 48 | 49 | @IsNotEmpty() 50 | @IsString() 51 | type: string; 52 | 53 | @IsNotEmpty() 54 | @IsString() 55 | currency: string; 56 | 57 | @IsNotEmpty() 58 | @IsString() 59 | paymentDestination: string; 60 | 61 | @IsNotEmpty() 62 | @IsString() 63 | destinationAddress: string; 64 | 65 | @IsOptional() 66 | @IsString() 67 | uniqueIdentifier?: string; 68 | } 69 | -------------------------------------------------------------------------------- /src/services/payouts/dto/sub-dto/payout-beneficiary.dto.ts: -------------------------------------------------------------------------------- 1 | import { 2 | IsString, 3 | IsOptional, 4 | IsNotEmpty, 5 | IsEmail, 6 | IsObject, 7 | ValidateNested, 8 | } from 'class-validator'; 9 | import { AddressDto } from '../../../beneficiaries/dto'; 10 | import { PayoutDocumentDto } from './payout-document.dto'; 11 | export class PayoutBeneficiaryDto { 12 | @IsOptional() 13 | @IsString() 14 | firstName?: string; 15 | 16 | @IsOptional() 17 | @IsString() 18 | lastName?: string; 19 | 20 | @IsOptional() 21 | @IsString() 22 | @IsEmail() 23 | email?: string; 24 | 25 | @IsString() 26 | @IsNotEmpty() 27 | type: string; 28 | 29 | @IsString() 30 | @IsNotEmpty() 31 | accountHolderName: string; 32 | 33 | @IsString() 34 | @IsNotEmpty() 35 | accountNumber: string; 36 | 37 | @IsOptional() 38 | @IsString() 39 | mobileMoneyCode?: string; 40 | 41 | @IsOptional() 42 | @IsString() 43 | country?: string; 44 | 45 | @IsOptional() 46 | @IsString() 47 | bankCode?: string; 48 | 49 | @IsOptional() 50 | @IsString() 51 | bankSwiftCode?: string; 52 | 53 | @IsOptional() 54 | @IsString() 55 | sortCode?: string; 56 | 57 | @IsOptional() 58 | @IsString() 59 | registrationNumber?: string; 60 | 61 | @IsOptional() 62 | @IsObject() 63 | @ValidateNested() 64 | address?: AddressDto; 65 | 66 | @IsOptional() 67 | @IsObject() 68 | @ValidateNested() 69 | document?: PayoutDocumentDto; 70 | } 71 | -------------------------------------------------------------------------------- /LICENCE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2024, Osagie Iyayi Emmanuel 4 | 5 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 6 | 7 | Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 8 | 9 | Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 10 | 11 | Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 12 | 13 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /src/config/envconfig.ts: -------------------------------------------------------------------------------- 1 | import * as path from 'path'; 2 | import { config } from 'dotenv'; 3 | 4 | // Parsing the env file from the root directory of the project 5 | config({ path: path.join(__dirname, '../../.env') }); 6 | 7 | // Interface to load env variables 8 | // Note these variables can possibly be undefined 9 | // as someone could skip these varibales or not setup a .env file at all 10 | 11 | interface ENV { 12 | NODE_ENV: string | undefined; 13 | FINCRA_SECRET_KEY: string | undefined; 14 | FINCRA_PUBLIC_KEY: string | undefined; 15 | } 16 | 17 | interface Config { 18 | NODE_ENV: string; 19 | FINCRA_SECRET_KEY: string; 20 | FINCRA_PUBLIC_KEY: string; 21 | } 22 | 23 | // Loading process.env as ENV interface 24 | 25 | const getConfig = (): ENV => { 26 | return { 27 | NODE_ENV: process.env.NODE_ENV, 28 | FINCRA_SECRET_KEY: process.env.FINCRA_SECRET_KEY, 29 | FINCRA_PUBLIC_KEY: process.env.FINCRA_PUBLIC_KEY, 30 | }; 31 | }; 32 | 33 | // Throwing an Error if any field was undefined we don't 34 | // want our app to run if it can't connect to DB and ensure 35 | // that these fields are accessible. If all is good return 36 | // it as Config which just removes the undefined from our type 37 | // definition. 38 | 39 | const getSanitzedConfig = (configSys: ENV): Config => { 40 | for (const [key, value] of Object.entries(configSys)) { 41 | if (value === undefined) { 42 | throw new Error(`Missing key ${key} in .env`); 43 | } 44 | } 45 | return configSys as Config; 46 | }; 47 | 48 | const configSys = getConfig(); 49 | 50 | const sanitizedConfig = getSanitzedConfig(configSys); 51 | 52 | export default sanitizedConfig; 53 | -------------------------------------------------------------------------------- /test/services/wallets/wallets.test.ts: -------------------------------------------------------------------------------- 1 | import { Wallet } from '../../../src/services'; 2 | import { keys } from '../../env'; 3 | import { WalletLogsDto } from '../../../src/services/wallets/dto'; 4 | 5 | const walletInstance = new Wallet(keys[0], keys[1], { sandbox: true }); 6 | 7 | describe('it should list all the logs and activities of wallets performed by a business', () => { 8 | it('returns an array of log objects', async () => { 9 | try { 10 | const logs: WalletLogsDto = { 11 | business: '627fefbe5a65ec99ba9af0be', 12 | page: '1', 13 | perPage: '10', 14 | }; 15 | const result = await walletInstance.listWalletLogs(logs); 16 | expect(result).toHaveBeenCalledWith(logs); 17 | expect(typeof result).toBe('object'); 18 | } catch (error) { 19 | expect(error).toBeInstanceOf(Error); 20 | } 21 | }); 22 | }); 23 | 24 | describe('it should list all the wallets of a specific merchant', () => { 25 | it('returns an array of wallets', async () => { 26 | try { 27 | const businessId: string = '627fefbe5a65ec99ba9af0bf'; 28 | const result = await walletInstance.listWallet(businessId); 29 | expect(result).toHaveBeenCalledWith(businessId); 30 | expect(typeof result).toBe('object'); 31 | } catch (error) { 32 | expect(error).toBeInstanceOf(Error); 33 | } 34 | }); 35 | }); 36 | 37 | describe('it should return a wallet object details', () => { 38 | it('returns a wallet object', async () => { 39 | try { 40 | const walletId: string = '6b1c53ca-4f58-41f0-9fe3-4dd28a81789d'; 41 | const result = await walletInstance.getWallet(walletId); 42 | expect(result).toHaveBeenCalledWith(walletId); 43 | expect(typeof result).toBe('object'); 44 | } catch (error) { 45 | expect(error).toBeInstanceOf(Error); 46 | } 47 | }); 48 | }); 49 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | // see https://www.typescriptlang.org/tsconfig to better understand tsconfigs 3 | "include": ["src", "types"], 4 | "exclude": ["node_modules", "**/test/*"], 5 | "compilerOptions": { 6 | "module": "ESNext", 7 | "lib": ["dom", "esnext"], 8 | "target": "ES5", 9 | "importHelpers": true, 10 | // "typeRoots": ["./src/types"], 11 | // output .d.ts declaration files for consumers 12 | "declaration": true, 13 | // output .js.map sourcemap files for consumers 14 | "sourceMap": true, 15 | // match output dir to input dir. e.g. dist/index instead of dist/src/index 16 | "rootDir": "./src", 17 | // stricter type-checking for stronger correctness. Recommended by TS 18 | "strict": true, 19 | // linter checks for common issues 20 | "noImplicitReturns": true, 21 | "noFallthroughCasesInSwitch": true, 22 | // noUnused* overlap with @typescript-eslint/no-unused-vars, can disable if duplicative 23 | "noUnusedLocals": true, 24 | "noUnusedParameters": true, 25 | // use Node's module resolution algorithm, instead of the legacy TS one 26 | "moduleResolution": "node", 27 | // transpile JSX to React.createElement 28 | "jsx": "react", 29 | // interop between ESM and CJS modules. Recommended by TS 30 | "esModuleInterop": true, 31 | // significant perf increase by skipping checking .d.ts files, particularly those in node_modules. Recommended by TS 32 | "skipLibCheck": true, 33 | // error out if import and file system have a casing mismatch. Recommended by TS 34 | "forceConsistentCasingInFileNames": true, 35 | // `tsdx build` ignores this option, but it is commonly used when type-checking separately with `tsc` 36 | "noEmit": false, 37 | "strictPropertyInitialization": false, 38 | "emitDecoratorMetadata": true, 39 | "experimentalDecorators": true, 40 | "allowJs": true 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /test/services/conversions/conversions.test.ts: -------------------------------------------------------------------------------- 1 | import { Conversion } from '../../../src/services'; 2 | import { CreateConversionDto } from '../../../src/services/conversions/dto'; 3 | import { keys } from '../../env'; 4 | 5 | const conversionInstance = new Conversion(keys[0], keys[1], { sandbox: true }); 6 | 7 | describe('service to fetch all the conversions performed by a merchant', () => { 8 | it('returns an array of conversion objects', async () => { 9 | try { 10 | const businessId: string = '627fefbe5a65ec99ba9af0be'; 11 | const result = await conversionInstance.getBusinessConversions( 12 | businessId 13 | ); 14 | expect(result).toHaveBeenCalledWith(businessId); 15 | expect(typeof result).toBe('object'); 16 | } catch (error) { 17 | expect(error).toBeInstanceOf(Error); 18 | } 19 | }); 20 | }); 21 | 22 | describe('service to fetch the detail of a single conversion', () => { 23 | it('returns a conversion object', async () => { 24 | try { 25 | const conversionId = '34513461'; 26 | 27 | const result = await conversionInstance.fetchConversion(conversionId); 28 | expect(result).toHaveBeenCalledWith(conversionId); 29 | expect(typeof result).toBe('object'); 30 | } catch (error) { 31 | expect(error).toBeInstanceOf(Error); 32 | } 33 | }); 34 | }); 35 | 36 | describe('service to create a conversion for a merchant', () => { 37 | it('returns a conversion object', async () => { 38 | try { 39 | const conversion: CreateConversionDto = { 40 | business: '627fefbe5a65ec99ba9af0be', 41 | quoteReference: '123456789', 42 | }; 43 | const result = await conversionInstance.createConversion(conversion); 44 | expect(result).toHaveBeenCalledWith(conversion); 45 | expect(typeof result).toBe('object'); 46 | } catch (error) { 47 | expect(error).toBeInstanceOf(Error); 48 | } 49 | }); 50 | }); 51 | -------------------------------------------------------------------------------- /test/services/verification/verify.test.ts: -------------------------------------------------------------------------------- 1 | import { VerifyCreds } from '../../../src/services'; 2 | import { 3 | VerifyBankAccountDto, 4 | BvnResolutionDto, 5 | } from '../../../src/services/verification/dto'; 6 | import { keys } from '../../env'; 7 | 8 | const verifierInstance = new VerifyCreds(keys[0], keys[1], { sandbox: true }); 9 | 10 | describe('service to verify an account detail', () => { 11 | it('returns a message object to verify an account', async () => { 12 | try { 13 | const verifierDto = new VerifyBankAccountDto(); 14 | verifierDto.accountNumber = '0410809624'; 15 | verifierDto.bankCode = '011'; 16 | const result = await verifierInstance.verifyBankAccount(verifierDto); 17 | expect(result).toHaveBeenCalledWith(verifierDto); 18 | expect(typeof result).toBe('object'); 19 | } catch (error) { 20 | expect(error).toBeInstanceOf(Error); 21 | } 22 | }); 23 | }); 24 | 25 | describe('service to verify a successful transaction', () => { 26 | it('returns a message object to verify a successful transaction', async () => { 27 | try { 28 | const reference = 'a323f8f8f8f8f8f8'; 29 | const result = await verifierInstance.verifyPayment(reference); 30 | expect(result).toHaveBeenCalledWith(reference); 31 | expect(typeof result).toBe('object'); 32 | } catch (error) { 33 | expect(error).toBeInstanceOf(Error); 34 | } 35 | }); 36 | }); 37 | 38 | describe('service to resolve and validate a bvn', () => { 39 | it('returns a message object to verify the response object', async () => { 40 | try { 41 | const data: BvnResolutionDto = { 42 | bvn: '09292929221', 43 | business: '627fefbe5a65ec99ba9af0be', 44 | }; 45 | const result = await verifierInstance.resolveBvn(data); 46 | expect(result).toHaveBeenCalledWith(data); 47 | expect(typeof result).toBe('object'); 48 | } catch (error) { 49 | expect(error).toBeInstanceOf(Error); 50 | } 51 | }); 52 | }); 53 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.4", 3 | "license": "GPL-3.0", 4 | "main": "dist/index.js", 5 | "typings": "dist/index.d.ts", 6 | "files": [ 7 | "dist/**/*" 8 | ], 9 | "engines": { 10 | "node": ">=16" 11 | }, 12 | "scripts": { 13 | "start": "tsdx watch", 14 | "build": "tsdx build", 15 | "test": "jest --config jest.config.js --no-cache", 16 | "format": "yarn prettier --write .", 17 | "lint": "tsdx lint", 18 | "prepare": "tsdx build", 19 | "prepublishOnly": "yarn test && yarn build", 20 | "preversion": "yarn test && yarn build", 21 | "size": "size-limit", 22 | "analyze": "size-limit --why" 23 | }, 24 | "keywords": [ 25 | "fincra", 26 | "fintech", 27 | "payments", 28 | "sdk", 29 | "typings" 30 | ], 31 | "repository": { 32 | "type": "git", 33 | "url": "https://github.com/E-wave112/fincra-node-sdk.git" 34 | }, 35 | "homepage": "https://github.com/E-wave112/fincra-node-sdk#readme", 36 | "bugs": { 37 | "url": "https://github.com/E-wave112/fincra-node-sdk/issues" 38 | }, 39 | "readme": "README.md", 40 | "husky": { 41 | "hooks": {} 42 | }, 43 | "name": "fincra-node-sdk", 44 | "author": "E-wave112", 45 | "module": "dist/fincra-node-sdk.esm.js", 46 | "size-limit": [ 47 | { 48 | "path": "dist/fincra-node-sdk.cjs.production.min.js", 49 | "limit": "15 KB" 50 | }, 51 | { 52 | "path": "dist/fincra-node-sdk.esm.js", 53 | "limit": "15 KB" 54 | } 55 | ], 56 | "devDependencies": { 57 | "@size-limit/preset-small-lib": "^7.0.8", 58 | "@types/jest": "^28.1.0", 59 | "class-validator": "^0.13.2", 60 | "dotenv": "^16.0.1", 61 | "eslint": "^8.16.0", 62 | "eslint-plugin-prettier": "^3.4.1", 63 | "husky": "^8.0.1", 64 | "jest": "^28.1.0", 65 | "prettier": "2.6.2", 66 | "size-limit": "^7.0.8", 67 | "ts-jest": "^28.0.3", 68 | "ts-node": "^10.8.1", 69 | "tsdx": "^0.14.1", 70 | "tslib": "^2.4.0", 71 | "typescript": "^4.6.3" 72 | }, 73 | "dependencies": { 74 | "axios": "^0.27.2" 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/api.ts: -------------------------------------------------------------------------------- 1 | import axios, { AxiosInstance } from 'axios'; 2 | import { 3 | IAxiosStruct, 4 | IEnvironment, 5 | getUrl, 6 | BaseError, 7 | excludeFields, 8 | handleErrors, 9 | handleAxiosError, 10 | } from './utils'; 11 | 12 | /** 13 | * @class Fincra REST api initializer 14 | */ 15 | export class FincraCore { 16 | public request: AxiosInstance; 17 | 18 | /** 19 | * This is a constructor for creating a fincra core instance 20 | * @param { string } publicKey merchant public key 21 | * @param { string } secretKey merchant secret key 22 | * @param { IEnvironment } environment fincra environment 23 | * @returns { FincraCore } a fincra core instance 24 | */ 25 | constructor( 26 | public publicKey: string, 27 | public secretKey: string, 28 | public environment?: IEnvironment 29 | ) { 30 | this.publicKey = publicKey; 31 | this.secretKey = secretKey; 32 | this.environment = environment; 33 | this.request = axios.create({ 34 | baseURL: getUrl(environment), 35 | headers: { 36 | 'api-key': secretKey, 37 | Accept: 'application/json', 38 | 'Content-Type': 'application/json', 39 | }, 40 | }); 41 | } 42 | 43 | /** 44 | * It returns an AxiosInstance object for making requests to fincra api 45 | * @returns An AxiosInstance 46 | */ 47 | public getBaseUrl(): AxiosInstance { 48 | try { 49 | return this.request; 50 | } catch (error) { 51 | throw new BaseError({ message: handleErrors(error) }); 52 | } 53 | } 54 | 55 | public async useGetRequest(req: IAxiosStruct) { 56 | try { 57 | const customHeaders = excludeFields( 58 | ['common', 'delete', 'get', 'head', 'put', 'patch', 'post'], 59 | this.request.defaults.headers 60 | ); 61 | 62 | const getUrl = this.request.defaults.baseURL; 63 | const requestInstance = await axios.request({ 64 | url: `${getUrl}${req.url}`, 65 | method: req.method, 66 | headers: customHeaders, 67 | data: req.data, 68 | }); 69 | return requestInstance; 70 | } catch (error) { 71 | throw new BaseError({ message: handleAxiosError(error) }); 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/fincra.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Conversion, 3 | Business, 4 | ChargeBacks, 5 | Quote, 6 | VerifyCreds, 7 | Wallet, 8 | Payout, 9 | Subaccount, 10 | Collection, 11 | VirtualAccount, 12 | Beneficiary, 13 | } from './services'; 14 | import { IEnvironment } from './utils'; 15 | 16 | /** 17 | * The Fincra class is the main class that is used to access the other classes 18 | * @class Fincra 19 | * @param {string} publicKey - The public key of the merchant 20 | * @param {string} privateKey - The private key of the merchant 21 | * @param {IEnvironment} environment - The environment to use 22 | * @returns The Fincra class 23 | * @example 24 | * const fincra = new Fincra('pk_NjI3ZmVmYmU1YTY1ZWM5OWJhOWFmMGJlOjoxMjE2NzA=', 'hzjMvDeY0dmBrDPSxZH5exnmdNc0aUXy', {sandbox: true}); 25 | **/ 26 | export class Fincra { 27 | constructor( 28 | public publicKey: string, 29 | public secretKey: string, 30 | public environment?: IEnvironment 31 | ) { 32 | this.publicKey = publicKey; 33 | this.secretKey = secretKey; 34 | this.environment = environment; 35 | } 36 | public conversion = new Conversion( 37 | this.publicKey, 38 | this.secretKey, 39 | this.environment 40 | ); 41 | public beneficiary = new Beneficiary( 42 | this.publicKey, 43 | this.secretKey, 44 | this.environment 45 | ); 46 | public business = new Business( 47 | this.publicKey, 48 | this.secretKey, 49 | this.environment 50 | ); 51 | public chargebacks = new ChargeBacks( 52 | this.publicKey, 53 | this.secretKey, 54 | this.environment 55 | ); 56 | public quote = new Quote(this.publicKey, this.secretKey, this.environment); 57 | public verify = new VerifyCreds( 58 | this.publicKey, 59 | this.secretKey, 60 | this.environment 61 | ); 62 | public wallet = new Wallet(this.publicKey, this.secretKey, this.environment); 63 | public payouts = new Payout(this.publicKey, this.secretKey, this.environment); 64 | public subacct = new Subaccount( 65 | this.publicKey, 66 | this.secretKey, 67 | this.environment 68 | ); 69 | public collection = new Collection( 70 | this.publicKey, 71 | this.secretKey, 72 | this.environment 73 | ); 74 | public virtualAccount = new VirtualAccount( 75 | this.publicKey, 76 | this.secretKey, 77 | this.environment 78 | ); 79 | } 80 | -------------------------------------------------------------------------------- /test/services/chargebacks/chargebacks.test.ts: -------------------------------------------------------------------------------- 1 | import { ChargeBacks } from '../../../src/services'; 2 | import { 3 | AcceptChargeBackDto, 4 | RejectChargeBackDto, 5 | } from '../../../src/services/chargebacks/dto'; 6 | import { keys } from '../../env'; 7 | 8 | const chargeBackInstance = new ChargeBacks(keys[0], keys[1], { sandbox: true }); 9 | 10 | describe('this service returns all the chargebacks for a particular chargeback', () => { 11 | it('returns an array of chargeback objects', async () => { 12 | try { 13 | const businessId: string = '627fefbe5a65ec99ba9af0be'; 14 | const result = await chargeBackInstance.listChargeBacks(businessId); 15 | expect(result).toHaveBeenCalledWith(businessId); 16 | expect(typeof result).toBe('object'); 17 | } catch (error) { 18 | expect(error).toBeInstanceOf(Error); 19 | } 20 | }); 21 | }); 22 | 23 | describe('this service accepts a specific chargeback occurrence', () => { 24 | it('returns an object on the status of a chargeback, accepted or not', async () => { 25 | try { 26 | const acceptChargeBack = new AcceptChargeBackDto(); 27 | acceptChargeBack.businessId = '9cc51d7f-4357-460d-bbe7-2554d3dd6986'; 28 | acceptChargeBack.chargeBackId = '9f89eb14-7e0d-4168-baef-280549c8bd8a'; 29 | 30 | const result = await chargeBackInstance.acceptChargeBack( 31 | acceptChargeBack 32 | ); 33 | expect(result).toHaveBeenCalledWith(acceptChargeBack); 34 | expect(typeof result).toBe('object'); 35 | } catch (error) { 36 | expect(error).toBeInstanceOf(Error); 37 | } 38 | }); 39 | }); 40 | 41 | describe('this service rejects a specific chargeback occurence', () => { 42 | it('returns an object on the status of chargeback, rejected on not', async () => { 43 | try { 44 | const rejectChargeBack = new RejectChargeBackDto(); 45 | rejectChargeBack.businessId = '9cc51d7f-4357-460d-bbe7-2554d3dd6986'; 46 | rejectChargeBack.chargeBackId = '08228fb8-b24f-4217-b2e5-73287b5fcb6e'; 47 | rejectChargeBack.reason = 'suspected duplicate chargeback'; 48 | 49 | const result = await chargeBackInstance.rejectChargeBack( 50 | rejectChargeBack 51 | ); 52 | expect(result).toHaveBeenCalledWith(rejectChargeBack); 53 | expect(typeof result).toBe('object'); 54 | } catch (error) { 55 | expect(error).toBeInstanceOf(Error); 56 | } 57 | }); 58 | }); 59 | -------------------------------------------------------------------------------- /src/services/conversions/conversion.ts: -------------------------------------------------------------------------------- 1 | import { FincraCore } from '../../api'; 2 | import { BaseError, handleErrors, IEnvironment } from '../../utils'; 3 | import { CreateConversionDto } from './dto'; 4 | 5 | /** 6 | * The conversion module for handling the conversion related operations. 7 | * @class Conversion 8 | * @extends FincraCore 9 | * @param {string} publicKey - The public key of the merchant 10 | * @param {string} secretKey - The secret key of the merchant 11 | * @param {IEnvironment} environment - The environment of the merchant 12 | */ 13 | export class Conversion extends FincraCore { 14 | constructor( 15 | publicKey: string, 16 | secretKey: string, 17 | environment?: IEnvironment 18 | ) { 19 | super(publicKey, secretKey, environment); 20 | } 21 | 22 | /** 23 | * This function gets all the conversions for a business 24 | * @param {string} id - The id of the business you want to get the conversions for. 25 | * @returns An array of conversions objects 26 | */ 27 | public async getBusinessConversions(id: string) { 28 | try { 29 | const request = this.getBaseUrl(); 30 | const response = await request.get(`/conversions?business=${id}`); 31 | return response.data; 32 | } catch (error) { 33 | throw new BaseError({ message: handleErrors(error) }); 34 | } 35 | } 36 | 37 | /** 38 | * It fetches a conversion by id. 39 | * @param {string} conversionId - FetchConversionDto 40 | * @returns The conversion object 41 | */ 42 | public async fetchConversion(conversionId: string) { 43 | try { 44 | const request = this.getBaseUrl(); 45 | const response = await request.get( 46 | `/conversions/reference/${conversionId}` 47 | ); 48 | return response.data; 49 | } catch (error) { 50 | throw new BaseError({ message: handleErrors(error) }); 51 | } 52 | } 53 | 54 | /** 55 | * It creates a conversion for a business. 56 | * @param {CreateConversionDto} conversion - CreateConversionDto 57 | * @returns The response from the API which contains the conversion object 58 | */ 59 | public async createConversion(conversion: CreateConversionDto) { 60 | try { 61 | const request = this.getBaseUrl(); 62 | const response = await request.post('/conversions/initiate', conversion); 63 | return response.data; 64 | } catch (error) { 65 | throw new BaseError({ message: handleErrors(error) }); 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/services/wallets/wallet.ts: -------------------------------------------------------------------------------- 1 | import { FincraCore } from '../../api'; 2 | import { 3 | IAxiosStruct, 4 | BaseError, 5 | IEnvironment, 6 | handleAxiosError, 7 | handleErrors, 8 | } from '../../utils'; 9 | import { WalletLogsDto } from './dto'; 10 | 11 | /** 12 | * The wallet module for handling the wallet related operations. 13 | * @class Wallet 14 | * @extends FincraCore 15 | * @param {string} publicKey - The public key of the merchant 16 | * @param {string} secretKey - The secret key of the merchant 17 | * @param {IEnvironment} environment - The environment of the merchant 18 | */ 19 | export class Wallet extends FincraCore { 20 | constructor( 21 | publicKey: string, 22 | secretKey: string, 23 | environment?: IEnvironment 24 | ) { 25 | super(publicKey, secretKey, environment); 26 | } 27 | 28 | // TODO done 29 | /** 30 | * It lists all the logs and activities of wallets performed by a business 31 | * @param {WalletLogsDto} data - WalletLogsDto - the data to be sent to the API 32 | * @returns an array of wallet objects. 33 | */ 34 | public async listWalletLogs(data: WalletLogsDto) { 35 | try { 36 | const reqObj: IAxiosStruct = { 37 | method: 'GET', 38 | url: `/wallets/logs`, 39 | data: data, 40 | }; 41 | const response = await this.useGetRequest(reqObj); 42 | return response.data; 43 | } catch (error) { 44 | throw new BaseError({ message: handleAxiosError(error) }); 45 | } 46 | } 47 | 48 | /** 49 | * It lists all the wallets of a business. 50 | * @param {string} id - The id of the business 51 | * @returns an array of wallet objects. 52 | */ 53 | public async listWallet(id: string) { 54 | try { 55 | const request = this.getBaseUrl(); 56 | const response = await request.get(`/wallets/?businessID=${id}`); 57 | return response.data; 58 | } catch (error) { 59 | throw new BaseError({ message: handleErrors(error) }); 60 | } 61 | } 62 | 63 | /** 64 | * It gets a wallet by id. 65 | * @param {string} id - The id of the wallet you want to retrieve. 66 | * @returns The wallet object 67 | */ 68 | public async getWallet(id: string) { 69 | try { 70 | const request = this.getBaseUrl(); 71 | const response = await request.get(`/wallets/${id}`); 72 | return response.data; 73 | } catch (error) { 74 | throw new BaseError({ message: handleErrors(error) }); 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/services/verification/verify.ts: -------------------------------------------------------------------------------- 1 | import { FincraCore } from '../../api'; 2 | import { BaseError, handleErrors, IEnvironment } from '../../utils'; 3 | import { VerifyBankAccountDto, BvnResolutionDto } from './dto'; 4 | 5 | /** 6 | * The verify module for handling the verification and kyc related operations. 7 | * @class VerifyBankAccount 8 | * @extends FincraCore 9 | * @param {string} publicKey - The public key of the merchant 10 | * @param {string} secretKey - The secret key of the merchant 11 | * @param {IEnvironment} environment - The environment of the merchant 12 | * 13 | */ 14 | export class VerifyCreds extends FincraCore { 15 | constructor( 16 | publicKey: string, 17 | secretKey: string, 18 | environment?: IEnvironment 19 | ) { 20 | super(publicKey, secretKey, environment); 21 | } 22 | 23 | /** 24 | * It verifies a bank account 25 | * @param {VerifyBankAccountDto} data - The data object that will be sent to the API. 26 | * @returns The bank account object. 27 | */ 28 | public async verifyBankAccount(data: VerifyBankAccountDto) { 29 | try { 30 | const request = this.getBaseUrl(); 31 | const response = await request.post(`/core/accounts/resolve`, data); 32 | return response.data; 33 | } catch (error) { 34 | throw new BaseError({ message: handleErrors(error) }); 35 | } 36 | } 37 | /** 38 | * It verifies a successful transaction 39 | * @param reference - The reference of the payout you want to fetch. 40 | * @returns The transaction object. 41 | */ 42 | public async verifyPayment(reference: string) { 43 | try { 44 | const request = this.getBaseUrl(); 45 | const response = await request.get( 46 | `/checkout/payments/merchant-reference/${reference}` 47 | ); 48 | return response.data; 49 | } catch (error) { 50 | throw new BaseError({ message: handleErrors(error) }); 51 | } 52 | } 53 | 54 | /** 55 | * This methods helps resolves and validates a bvn 56 | * @param {BvnResolutionDto} data - The data object that will be sent to the API. 57 | * @returns The user details linked to the bvn 58 | */ 59 | public async resolveBvn(data: BvnResolutionDto) { 60 | try { 61 | const request = this.getBaseUrl(); 62 | const response = await request.post(`/core/bvn-verification`, data); 63 | return response.data; 64 | } catch (error) { 65 | throw new BaseError({ message: handleErrors(error) }); 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/services/chargebacks/chargeback.ts: -------------------------------------------------------------------------------- 1 | import { FincraCore } from '../../api'; 2 | import { BaseError, handleErrors, IEnvironment } from '../../utils'; 3 | import { AcceptChargeBackDto, RejectChargeBackDto } from './dto'; 4 | 5 | /** 6 | * The chargeback module for handling the chargeback related operations. 7 | * @class Chargebacks 8 | * @extends FincraCore 9 | * @param {string} publicKey - The public key of the merchant 10 | * @param {string} secretKey - The secret key of the merchant 11 | * @param {IEnvironment} environment - The environment of the merchant 12 | */ 13 | export class ChargeBacks extends FincraCore { 14 | constructor( 15 | publicKey: string, 16 | secretKey: string, 17 | environment?: IEnvironment 18 | ) { 19 | super(publicKey, secretKey, environment); 20 | } 21 | 22 | /** 23 | * It lists all chargebacks for a business 24 | * @param {string} id - The id of the business you want to get the chargebacks for. 25 | * @returns The response.data is being returned. 26 | */ 27 | public async listChargeBacks(id: string) { 28 | try { 29 | const request = this.getBaseUrl(); 30 | const response = await request.get( 31 | `/collections/chargebacks?business=${id}` 32 | ); 33 | return response.data; 34 | } catch (error) { 35 | throw new BaseError({ message: handleErrors(error) }); 36 | } 37 | } 38 | 39 | /** 40 | * This function accepts a chargeback 41 | * @param {AcceptChargeBackDto} data - AcceptChargeBackDto 42 | * @returns The response is the chargeback object with the status changed to accepted. 43 | */ 44 | public async acceptChargeBack(data: AcceptChargeBackDto) { 45 | try { 46 | const request = this.getBaseUrl(); 47 | const response = await request.patch( 48 | `/collections/chargebacks/${data.chargeBackId}/accept?business=${data.businessId}` 49 | ); 50 | return response.data; 51 | } catch (error) { 52 | throw new BaseError({ message: handleErrors(error) }); 53 | } 54 | } 55 | 56 | /** 57 | * This function rejects a chargeback 58 | * @param {RejectChargeBackDto} data - { 59 | * @returns The response is a JSON object with the following properties: 60 | */ 61 | public async rejectChargeBack(data: RejectChargeBackDto) { 62 | try { 63 | const request = this.getBaseUrl(); 64 | const response = await request.patch( 65 | `/collections/chargebacks/${data.chargeBackId}/reject?business=${data.businessId}`, 66 | { 67 | business_reject_reason: data.reason, 68 | } 69 | ); 70 | return response.data; 71 | } catch (error) { 72 | throw new BaseError({ message: handleErrors(error) }); 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /test/services/subaccounts/subaccounts.test.ts: -------------------------------------------------------------------------------- 1 | import { keys } from '../../env'; 2 | import { Subaccount } from '../../../src/services'; 3 | import { 4 | CreateSubAccountDto, 5 | FetchSubAccountDto, 6 | UpdateSubAccountDto, 7 | } from '../../../src/services/subaccounts/dto'; 8 | 9 | const subaccountInstance = new Subaccount(keys[0], keys[1], { sandbox: true }); 10 | 11 | describe('service to create a subaccount', () => { 12 | it('should return a subaccount object', async () => { 13 | try { 14 | const subaccount: CreateSubAccountDto = { 15 | name: 'Edmond', 16 | businessId: '627fefbe5a65ec99ba9af0be', 17 | email: 'edmond@kirsch.com', 18 | mobile: '07081927814', 19 | country: 'NG', 20 | }; 21 | 22 | const result = await subaccountInstance.createSubAccount(subaccount); 23 | expect(result).toHaveBeenCalledWith(subaccount); 24 | expect(typeof result).toBe('object'); 25 | } catch (error) { 26 | expect(error).toBeInstanceOf(Error); 27 | } 28 | }); 29 | }); 30 | 31 | describe('service to fetch a subaccount', () => { 32 | it('should return a subaccount object', async () => { 33 | try { 34 | const subaccount: FetchSubAccountDto = { 35 | businessId: '627fefbe5a65ec99ba9af0be', 36 | subAccountId: '62ba8f973acaf73df03238aa', 37 | }; 38 | 39 | const result = await subaccountInstance.fetchSubAccount(subaccount); 40 | expect(result).toHaveBeenCalledWith(subaccount); 41 | expect(typeof result).toBe('object'); 42 | } catch (error) { 43 | expect(error).toBeInstanceOf(Error); 44 | } 45 | }); 46 | }); 47 | 48 | describe('service to update a subaccount', () => { 49 | it('should return a subaccount object', async () => { 50 | try { 51 | const subaccount: UpdateSubAccountDto = { 52 | business: '627fefbe5a65ec99ba9af0be', 53 | subAccountId: '62ba8f973acaf73df03238aa', 54 | name: 'Eddie', 55 | email: 'edmond@kirsch.com', 56 | }; 57 | const result = await subaccountInstance.updateSubAccount(subaccount); 58 | expect(result).toHaveBeenCalledWith(subaccount); 59 | expect(typeof result).toBe('object'); 60 | } catch (error) { 61 | expect(error).toBeInstanceOf(Error); 62 | } 63 | }); 64 | }); 65 | 66 | describe('service to list subaccounts linked to a business', () => { 67 | it('should return a list of subaccount objects', async () => { 68 | try { 69 | const businessId = '627fefbe5a65ec99ba9af0be'; 70 | const result = await subaccountInstance.listSubAccounts(businessId); 71 | expect(result).toHaveBeenCalledWith(businessId); 72 | expect(typeof result).toBe('object'); 73 | } catch (error) { 74 | expect(error).toBeInstanceOf(Error); 75 | } 76 | }); 77 | }); 78 | -------------------------------------------------------------------------------- /test/services/collections/collections.test.ts: -------------------------------------------------------------------------------- 1 | import { keys } from '../../env'; 2 | import { Collection } from '../../../src/services'; 3 | import { 4 | FetchCollectionVirtualAccountDto, 5 | ListCollectionMainVirtualAccountDto, 6 | ListCollectionMultipleVirtualAccountsDto, 7 | PayWithTransferDto, 8 | } from '../../../src/services/collections/dto'; 9 | 10 | const collectionInstance = new Collection(keys[0], keys[1], { sandbox: true }); 11 | 12 | describe('service to pay with transfer for temporary virtual account', () => { 13 | it('should return a transaction object', async () => { 14 | try { 15 | const data: PayWithTransferDto = { 16 | expiresAt: '30', 17 | name: 'Edmond Kirsch', 18 | merchantReference: '627fefbe5a65ec99ba9cf0fe', 19 | }; 20 | const result = await collectionInstance.payWithTransfer(data); 21 | expect(result).toHaveBeenCalledWith(data); 22 | expect(typeof result).toBe('object'); 23 | } catch (error) { 24 | expect(error).toBeInstanceOf(Error); 25 | } 26 | }); 27 | }); 28 | 29 | describe('service to to view both a single or multiple collections of a main virtual account', () => { 30 | it('should return a collection object', async () => { 31 | try { 32 | const data: ListCollectionMainVirtualAccountDto = { 33 | business: '627fefbe5a65ec99ba9af0be', 34 | reference: '677gefbe5a65ec99ba9af3be', 35 | page: '1', 36 | perPage: '30', 37 | }; 38 | const result = await collectionInstance.listCollectionMain(data); 39 | expect(result).toHaveBeenCalledWith(data); 40 | expect(typeof result).toBe('object'); 41 | } catch (error) { 42 | expect(error).toBeInstanceOf(Error); 43 | } 44 | }); 45 | }); 46 | 47 | describe('service to to view both a single or multiple collections of additional virtual accounts by a business', () => { 48 | it('should return a collection object/array', async () => { 49 | try { 50 | const data: ListCollectionMultipleVirtualAccountsDto = { 51 | status: ['processing'], 52 | business: '627fefbe5a65ec99ba9af0be', 53 | sourceCurrency: 'NGN', 54 | destinationCurrency: 'EUR', 55 | subAccount: '62ba8f973acaf73df03238aa', 56 | page: '1', 57 | perPage: '15', 58 | dateFrom: '2022-08-19T00:00:00.000Z', 59 | dateTo: '2022-08-30T00:00:00.000Z', 60 | }; 61 | const result = await collectionInstance.listCollectionAdditional(data); 62 | expect(result).toHaveBeenCalledWith(data); 63 | expect(typeof result).toBe('object'); 64 | } catch (error) { 65 | expect(error).toBeInstanceOf(Error); 66 | } 67 | }); 68 | }); 69 | 70 | describe('service for retrieving a single collection of an additional virtual account by a reference', () => { 71 | it('should return a collection object', async () => { 72 | try { 73 | const data: FetchCollectionVirtualAccountDto = { 74 | reference: '77gefbe5a65ec99ba9af3be', 75 | business: '627fefbe5a65ec99ba9af0be', 76 | }; 77 | const result = await collectionInstance.fetchCollectionAddition(data); 78 | expect(result).toHaveBeenCalledWith(data); 79 | expect(typeof result).toBe('object'); 80 | } catch (error) { 81 | expect(error).toBeInstanceOf(Error); 82 | } 83 | }); 84 | }); 85 | -------------------------------------------------------------------------------- /src/services/subaccounts/subaccount.ts: -------------------------------------------------------------------------------- 1 | import { FincraCore } from '../../api'; 2 | import { 3 | BaseError, 4 | excludeFields, 5 | handleErrors, 6 | IEnvironment, 7 | } from '../../utils'; 8 | import { 9 | CreateSubAccountDto, 10 | UpdateSubAccountDto, 11 | FetchSubAccountDto, 12 | } from './dto'; 13 | 14 | /** 15 | * The subaccount module for handling the subaccounts related operations. 16 | * @class Subaccount 17 | * @extends FincraCore 18 | * @param {string} publicKey - The public key of the merchant 19 | * @param {string} secretKey - The secret key of the merchant 20 | * @param {IEnvironment} environment - The environment of the merchant 21 | */ 22 | export class Subaccount extends FincraCore { 23 | constructor( 24 | publicKey: string, 25 | secretKey: string, 26 | environment?: IEnvironment 27 | ) { 28 | super(publicKey, secretKey, environment); 29 | } 30 | 31 | /** 32 | * It creates a sub account for a business 33 | * @param {CreateSubAccountDto} data - CreateSubAccountDto 34 | * @returns The response from the API call, containing the sub account details 35 | */ 36 | public async createSubAccount(data: CreateSubAccountDto) { 37 | try { 38 | const request = this.getBaseUrl(); 39 | const dataBody = excludeFields(['businessId'], data); 40 | const response = await request.post( 41 | `/profile/business/${data.businessId}/sub-accounts`, 42 | dataBody 43 | ); 44 | return response.data; 45 | } catch (error) { 46 | throw new BaseError({ message: handleErrors(error) }); 47 | } 48 | } 49 | 50 | /** 51 | * It lists all the sub accounts of a business account. 52 | * @param {string} id - The id of the business account 53 | * @returns A list of sub accounts 54 | */ 55 | public async listSubAccounts(id: string) { 56 | try { 57 | const request = this.getBaseUrl(); 58 | const response = await request.get( 59 | `/profile/business/${id}/sub-accounts` 60 | ); 61 | return response.data; 62 | } catch (error) { 63 | throw new BaseError({ message: handleErrors(error) }); 64 | } 65 | } 66 | 67 | /** 68 | * It fetches a sub account from the API 69 | * @param {FetchSubAccountDto} data - FetchSubAccountDto 70 | * @returns The response data which contains the sub account details 71 | */ 72 | public async fetchSubAccount(data: FetchSubAccountDto) { 73 | try { 74 | const request = this.getBaseUrl(); 75 | const response = await request.get( 76 | `/profile/business/${data.businessId}/sub-accounts/${data.subAccountId}` 77 | ); 78 | return response.data; 79 | } catch (error) { 80 | throw new BaseError({ message: handleErrors(error) }); 81 | } 82 | } 83 | 84 | /** 85 | * It updates a sub account 86 | * @param {UpdateSubAccountDto} data - UpdateSubAccountDto 87 | * @returns The response data, containing the updated sub account details 88 | */ 89 | public async updateSubAccount(data: UpdateSubAccountDto) { 90 | try { 91 | const request = this.getBaseUrl(); 92 | const dataBody = excludeFields(['business', 'subAccountId'], data); 93 | const response = await request.patch( 94 | `/profile/business/${data.business}/sub-accounts/${data.subAccountId}`, 95 | dataBody 96 | ); 97 | return response.data; 98 | } catch (error) { 99 | throw new BaseError({ message: handleErrors(error) }); 100 | } 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /src/services/collections/collection.ts: -------------------------------------------------------------------------------- 1 | import { FincraCore } from '../../api'; 2 | import { 3 | BaseError, 4 | handleErrors, 5 | handleAxiosError, 6 | IAxiosStruct, 7 | IEnvironment, 8 | } from '../../utils'; 9 | import { 10 | FetchCollectionVirtualAccountDto, 11 | ListCollectionMainVirtualAccountDto, 12 | ListCollectionMultipleVirtualAccountsDto, 13 | PayWithTransferDto, 14 | } from './dto'; 15 | 16 | /** 17 | * The Collection module for handling the collection related operations. 18 | * @class Collection 19 | * @extends FincraCore 20 | * @param {string} publicKey - The public key of the merchant 21 | * @param {string} secretKey - The secret key of the merchant 22 | * @param {IEnvironment} environment - The environment of the merchant 23 | **/ 24 | export class Collection extends FincraCore { 25 | constructor( 26 | publicKey: string, 27 | secretKey: string, 28 | environment?: IEnvironment 29 | ) { 30 | super(publicKey, secretKey, environment); 31 | } 32 | /** 33 | * this method creates a temporary virtual account that can be used to receive funds over a time period 34 | * @param {PayWithTransferDto} data - the data to be sent to the server 35 | * @returns a virtual account object 36 | */ 37 | public async payWithTransfer(data: PayWithTransferDto) { 38 | try { 39 | const request = this.getBaseUrl(); 40 | const response = await request.post( 41 | `/profile/virtual-accounts/transfer`, 42 | data 43 | ); 44 | return response.data; 45 | } catch (error) { 46 | throw new BaseError({ message: handleErrors(error) }); 47 | } 48 | } 49 | 50 | /** 51 | * this methods returns a single or multiple collection of a main virtual account 52 | * @param {ListCollectionMainVirtualAccountDto} data - the data to be sent to the server 53 | * @returns an array of collection objects 54 | */ 55 | public async listCollectionMain(data: ListCollectionMainVirtualAccountDto) { 56 | try { 57 | const request = this.getBaseUrl(); 58 | const response = await request.get( 59 | `/wallets/topups?business=${data.business}&reference=${data.reference}&page=${data.page}&perPage=${data.perPage}` 60 | ); 61 | return response.data; 62 | } catch (error) { 63 | throw new BaseError({ message: handleErrors(error) }); 64 | } 65 | } 66 | /** 67 | * this method returns a single collection of an additional virtual account by its reference 68 | * @param {FetchCollectionVirtualAccountDto} data - the data to be sent to the server 69 | * @returns a collection object 70 | */ 71 | public async fetchCollectionAddition(data: FetchCollectionVirtualAccountDto) { 72 | try { 73 | const request = this.getBaseUrl(); 74 | const response = await request.get( 75 | `/collections/reference/${data.reference}?business=${data.business}` 76 | ); 77 | return response.data; 78 | } catch (error) { 79 | throw new BaseError({ message: handleErrors(error) }); 80 | } 81 | } 82 | 83 | // TODO: List collections for additional virtual accounts done 84 | 85 | /** 86 | * this methods returns a single or multiple collection of a additiona virtual accounts of a business 87 | * @param {ListCollectionMultipleVirtualAccountsDto} data - the data to be sent to the server 88 | * @returns an array of collection objects 89 | */ 90 | public async listCollectionAdditional( 91 | data: ListCollectionMultipleVirtualAccountsDto 92 | ) { 93 | try { 94 | const requestObj: IAxiosStruct = { 95 | method: 'GET', 96 | url: '/collections', 97 | data, 98 | }; 99 | const response = await this.useGetRequest(requestObj); 100 | return response.data; 101 | } catch (error) { 102 | throw new BaseError({ message: handleAxiosError(error) }); 103 | } 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /src/services/beneficiaries/beneficiary.ts: -------------------------------------------------------------------------------- 1 | import { FincraCore } from '../../api'; 2 | import { 3 | IAxiosStruct, 4 | BaseError, 5 | IEnvironment, 6 | excludeFields, 7 | handleErrors, 8 | handleAxiosError, 9 | } from '../../utils'; 10 | import { 11 | CreateBeneficiaryDto, 12 | FetchDeleteBeneficiaryDto, 13 | UpdateBeneficiaryDto, 14 | ListBeneficiaryDto, 15 | } from './dto'; 16 | 17 | /** 18 | * The Beneficiary module for handling the beneficary related operations. 19 | * @class Beneficiary 20 | * @extends FincraCore 21 | * @param {string} publicKey - The public key of the merchant 22 | * @param {string} secretKey - The secret key of the merchant 23 | * @param {IEnvironment} environment - The environment of the merchant 24 | */ 25 | export class Beneficiary extends FincraCore { 26 | constructor( 27 | publicKey: string, 28 | secretKey: string, 29 | environment?: IEnvironment 30 | ) { 31 | super(publicKey, secretKey, environment); 32 | } 33 | 34 | /** 35 | * allows a business/merchant to create a beneficiary 36 | * @param {CreateBeneficiaryDto} data - CreateBeneficiaryDto - This is the data that will be sent to the API. 37 | * @returns The response from the API 38 | */ 39 | public async createBeneficiary(data: CreateBeneficiaryDto) { 40 | try { 41 | const request = this.getBaseUrl(); 42 | const dataBody = excludeFields(['businessId'], data); 43 | const response = await request.post( 44 | `/profile/beneficiaries/business/${data.businessId}`, 45 | dataBody 46 | ); 47 | return response.data; 48 | } catch (error) { 49 | throw new BaseError({ message: handleErrors(error) }); 50 | } 51 | } 52 | 53 | // TODO done 54 | /** 55 | * returns all the beneficiaries linked to business/merchant 56 | * @param {ListBeneficiaryDto} data - ListBeneficiaryDto - This is the data that will be sent to the API. 57 | * @returns The response is an array of beneficiary objects 58 | */ 59 | public async listBeneficiaries(data: ListBeneficiaryDto) { 60 | try { 61 | const dataBody = excludeFields(['businessId'], data); 62 | const requestObj: IAxiosStruct = { 63 | method: 'GET', 64 | url: `/profile/beneficiaries/business/${data.businessId}`, 65 | data: dataBody, 66 | }; 67 | const response = await this.useGetRequest(requestObj); 68 | return response.data; 69 | } catch (error) { 70 | throw new BaseError({ message: handleAxiosError(error) }); 71 | } 72 | } 73 | 74 | /** 75 | * It fetches and returns the detail of a single beneficiary 76 | * @param {FetchDeleteBeneficiaryDto} data - FetchDeleteBeneficiaryDto - the data to be sent to the API 77 | * @returns The response is a beneficiary object. 78 | */ 79 | public async fetchBeneficiary(data: FetchDeleteBeneficiaryDto) { 80 | try { 81 | const request = this.getBaseUrl(); 82 | const response = await request.get( 83 | `/profile/beneficiaries/business/${data.businessId}/${data.beneficiaryId}` 84 | ); 85 | return response.data; 86 | } catch (error) { 87 | // 88 | throw new BaseError({ message: handleErrors(error) }); 89 | } 90 | } 91 | 92 | /** 93 | * allows a merchant to update any of this beneficiary details 94 | * @param {UpdateBeneficiaryDto} data - UpdateBeneficiaryDto - The data object that will be sent to the API. 95 | * @returns The response from the API 96 | */ 97 | public async updateBeneficiary(data: UpdateBeneficiaryDto) { 98 | try { 99 | const request = this.getBaseUrl(); 100 | const dataBody = excludeFields(['businessId', 'beneficiaryId'], data); 101 | const response = await request.patch( 102 | `/profile/beneficiaries/business/${data.businessId}/${data.beneficiaryId}`, 103 | dataBody 104 | ); 105 | return response.data; 106 | } catch (error) { 107 | throw new BaseError({ message: handleErrors(error) }); 108 | } 109 | } 110 | 111 | /** 112 | * It allows a merchant to remove any of his beneficiary 113 | * @param {FetchDeleteBeneficiaryDto} data - FetchDeleteBeneficiaryDto - The data object that will be sent to the API. 114 | * @returns The response from the API 115 | */ 116 | public async deleteBeneficiary(data: FetchDeleteBeneficiaryDto) { 117 | try { 118 | const request = this.getBaseUrl(); 119 | const response = await request.delete( 120 | `/profile/beneficiaries/business/${data.businessId}/${data.beneficiaryId}` 121 | ); 122 | return response.data; 123 | } catch (error) { 124 | throw new BaseError({ message: handleErrors(error) }); 125 | } 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /src/services/payouts/payout.ts: -------------------------------------------------------------------------------- 1 | import { FincraCore } from '../../api'; 2 | import { 3 | BaseError, 4 | IEnvironment, 5 | handleErrors, 6 | handleAxiosError, 7 | IAxiosStruct, 8 | } from '../../utils'; 9 | import { 10 | CreatePayoutDto, 11 | ListPayoutDto, 12 | WalletToWalletTransferDto, 13 | UploadPayoutDto, 14 | } from './dto'; 15 | 16 | /** 17 | * The Payout module for handling the payout related operations. 18 | * @class Payout 19 | * @extends FincraCore 20 | * @param {string} publicKey - The public key of the merchant 21 | * @param {string} secretKey - The secret key of the merchant 22 | * @param {IEnvironment} environment - The environment of the merchant 23 | */ 24 | export class Payout extends FincraCore { 25 | constructor( 26 | publicKey: string, 27 | secretKey: string, 28 | environment?: IEnvironment 29 | ) { 30 | super(publicKey, secretKey, environment); 31 | } 32 | 33 | /** 34 | * It allows a merchant to make a payout 35 | * @param {CreatePayoutDto} data - CreatePayoutDto 36 | * @returns The response from the API, which is the transaction object. 37 | */ 38 | public async createPayout(data: CreatePayoutDto) { 39 | try { 40 | const request = this.getBaseUrl(); 41 | const response = await request.post(`/disbursements/payouts`, data); 42 | return response.data; 43 | } catch (error) { 44 | throw new BaseError({ message: handleErrors(error) }); 45 | } 46 | } 47 | 48 | /** 49 | * It allows a merchant to make a transfer to another merchant's wallet. 50 | * @param {WalletToWalletTransferDto} data - WalletToWalletTransferDto 51 | * @returns The response from the API, which is the transaction object. 52 | */ 53 | public async walletToWalletTransfer(data: WalletToWalletTransferDto) { 54 | try { 55 | const request = this.getBaseUrl(); 56 | const response = await request.post( 57 | `/disbursements/payouts/wallets`, 58 | data 59 | ); 60 | return response.data; 61 | } catch (error) { 62 | throw new BaseError({ message: handleErrors(error) }); 63 | } 64 | } 65 | 66 | /** 67 | * It allows a merchant fetch a payout by it's transaction reference 68 | * @param {string} reference - The reference of the payout you want to fetch. 69 | * @returns The transaction object. 70 | */ 71 | public async fetchPayout(reference: string) { 72 | try { 73 | const request = this.getBaseUrl(); 74 | const response = await request.get( 75 | `/disbursements/payouts/reference/${reference}` 76 | ); 77 | return response.data; 78 | } catch (error) { 79 | throw new BaseError({ message: handleErrors(error) }); 80 | } 81 | } 82 | 83 | /** 84 | * It allows a merchant to fetch a payout by customer reference 85 | * @param {string} creference - The customer reference of the payout you want to fetch. 86 | * @returns The response is an object with the following properties: 87 | */ 88 | public async fetchCustomerPayout(creference: string) { 89 | try { 90 | const request = this.getBaseUrl(); 91 | const response = await request.get( 92 | `/disbursements/payouts/customer-reference/${creference}` 93 | ); 94 | return response.data; 95 | } catch (error) { 96 | throw new BaseError({ message: handleErrors(error) }); 97 | } 98 | } 99 | 100 | /** 101 | * This method retrieve a list of banks supported by fincra to process payments 102 | * @returns A list of banks 103 | */ 104 | public async listBanks() { 105 | try { 106 | const request = this.getBaseUrl(); 107 | const response = await request.get(`/core/banks?currency=NGN&country=NG`); 108 | return response.data; 109 | } catch (error) { 110 | throw new BaseError({ message: handleErrors(error) }); 111 | } 112 | } 113 | 114 | /** 115 | * this method allows a merchant to upload a payout document 116 | * @param {UploadPayoutDto} data - UploadPayoutDto 117 | * @returns The transaction object. 118 | */ 119 | public async uploadTransactionDocument(data: UploadPayoutDto) { 120 | try { 121 | const request = this.getBaseUrl(); 122 | const response = await request.post(`/payouts/documents-upload `, data); 123 | return response.data; 124 | } catch (error) { 125 | throw new BaseError({ message: handleErrors(error) }); 126 | } 127 | } 128 | // TODO: list payouts 129 | 130 | public async listPayouts(data: ListPayoutDto) { 131 | try { 132 | const requestObj: IAxiosStruct = { 133 | method: 'GET', 134 | url: '/disbursements/payouts', 135 | data, 136 | }; 137 | const response = await this.useGetRequest(requestObj); 138 | return response.data; 139 | } catch (error) { 140 | throw new BaseError({ message: handleAxiosError(error) }); 141 | } 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /test/services/beneficiaries/beneficiaries.test.ts: -------------------------------------------------------------------------------- 1 | import { Beneficiary } from '../../../src/services'; 2 | import { 3 | CreateBeneficiaryDto, 4 | FetchDeleteBeneficiaryDto, 5 | UpdateBeneficiaryDto, 6 | ListBeneficiaryDto, 7 | } from '../../../src/services/beneficiaries/dto'; 8 | import { keys } from '../../env'; 9 | 10 | const beneficiaryInstance = new Beneficiary(keys[0], keys[1], { 11 | sandbox: true, 12 | }); 13 | 14 | describe('service that creates a beneficiary', () => { 15 | it('returns a message object of a created beneficiary', async () => { 16 | try { 17 | const beneficiaryDetails: CreateBeneficiaryDto = { 18 | firstName: 'efe', 19 | lastName: 'ebieroma', 20 | email: 'efe@hahaha.com', 21 | phoneNumber: '09090909090', 22 | accountHolderName: 'efe stephen ebieroma', 23 | bank: { 24 | name: 'Wema Bank', 25 | code: '06', 26 | sortCode: '928927', 27 | branch: 'Ota', 28 | address: { 29 | country: 'GB', 30 | state: 'Lagos', 31 | zip: '123455', 32 | city: 'Paris', 33 | street: '12,josephus', 34 | }, 35 | }, 36 | address: { 37 | country: 'GB', 38 | state: 'Lagos', 39 | zip: '123455', 40 | city: 'Paris', 41 | street: '12,josephus', 42 | }, 43 | type: 'individual', 44 | currency: 'GBP', 45 | paymentDestination: 'bank_account', 46 | uniqueIdentifier: '4', 47 | businessId: '627fefbe5a65ec99ba9af0be', 48 | destinationAddress: 'AKoka, yaba, lagos', 49 | }; 50 | 51 | const result = await beneficiaryInstance.createBeneficiary( 52 | beneficiaryDetails 53 | ); 54 | expect(result).toHaveBeenCalledWith(beneficiaryDetails); 55 | expect(typeof result).toBe('object'); 56 | } catch (error) { 57 | expect(error).toBeInstanceOf(Error); 58 | } 59 | }); 60 | }); 61 | 62 | describe('service to return the list of all beneficiaries related to a business', () => { 63 | it('returns an array of beneficiary objects linked to a business', async () => { 64 | try { 65 | const data: ListBeneficiaryDto = { 66 | businessId: '627fefbe5a65ec99ba9af0be', 67 | page: '2', 68 | perPage: '10', 69 | }; 70 | const result = await beneficiaryInstance.listBeneficiaries(data); 71 | expect(result).toHaveBeenCalledWith(data); 72 | expect(typeof result).toBe('object'); 73 | } catch (error) { 74 | expect(error).toBeInstanceOf(Error); 75 | } 76 | }); 77 | }); 78 | 79 | describe('service to fetch a single beneficiary tied to a business', () => { 80 | it('returns a beneficiary object linked to a merchant', async () => { 81 | try { 82 | const data: FetchDeleteBeneficiaryDto = { 83 | businessId: '627fefbe5a65ec99ba9af0be', 84 | beneficiaryId: 'a5ce0826-4874-4a4b-9ec2-3f771d371ea4', 85 | }; 86 | 87 | const result = await beneficiaryInstance.fetchBeneficiary(data); 88 | expect(result).toHaveBeenCalledWith(data); 89 | expect(typeof result).toBe('object'); 90 | } catch (error) { 91 | expect(error).toBeInstanceOf(Error); 92 | } 93 | }); 94 | }); 95 | 96 | describe('service to update a beneficiary', () => { 97 | it('returns an updated beneficiary object', async () => { 98 | try { 99 | const beneficiaryDetails: UpdateBeneficiaryDto = { 100 | firstName: 'efe', 101 | lastName: 'ebieroma', 102 | email: 'efe@hahaha.com', 103 | phoneNumber: '09090909090', 104 | accountHolderName: 'efe stephen ebieroma', 105 | bank: { 106 | name: 'Wema Bank', 107 | code: '06', 108 | sortCode: '928927', 109 | branch: 'Ota', 110 | address: { 111 | country: 'GB', 112 | state: 'Lagos', 113 | zip: '123455', 114 | city: 'Paris', 115 | street: '12,josephus', 116 | }, 117 | }, 118 | address: { 119 | country: 'GB', 120 | state: 'Lagos', 121 | zip: '123455', 122 | city: 'Paris', 123 | street: '12,josephus', 124 | }, 125 | type: 'individual', 126 | currency: 'GBP', 127 | paymentDestination: 'bank_account', 128 | uniqueIdentifier: '4', 129 | businessId: '627fefbe5a65ec99ba9af0be', 130 | destinationAddress: 'AKoka, yaba, lagos', 131 | beneficiaryId: 'a5ce0826-4874-4a4b-9ec2-3f771d371ea4', 132 | }; 133 | const result = await beneficiaryInstance.updateBeneficiary( 134 | beneficiaryDetails 135 | ); 136 | expect(result).toHaveBeenCalledWith(beneficiaryDetails); 137 | expect(typeof result).toBe('object'); 138 | } catch (error) { 139 | expect(error).toBeInstanceOf(Error); 140 | } 141 | }); 142 | }); 143 | 144 | describe('service to delete a beneficiary', () => { 145 | it('returns a message confirming that a beneficiary has been deleted', async () => { 146 | try { 147 | const data: FetchDeleteBeneficiaryDto = { 148 | businessId: '627fefbe5a65ec99ba9af0be', 149 | beneficiaryId: 'a5ce0826-4874-4a4b-9ec2-3f771d371ea4', 150 | }; 151 | const result = await beneficiaryInstance.deleteBeneficiary(data); 152 | expect(result).toHaveBeenCalledWith(data); 153 | expect(typeof result).toBe('object'); 154 | } catch (error) { 155 | expect(error).toBeInstanceOf(Error); 156 | } 157 | }); 158 | }); 159 | -------------------------------------------------------------------------------- /test/services/payouts/payouts.test.ts: -------------------------------------------------------------------------------- 1 | import { keys } from '../../env'; 2 | import { Payout } from '../../../src/services'; 3 | import { 4 | CreatePayoutDto, 5 | ListPayoutDto, 6 | UploadPayoutDto, 7 | WalletToWalletTransferDto, 8 | } from '../../../src/services/payouts/dto'; 9 | 10 | const payoutInstance = new Payout(keys[0], keys[1], { sandbox: true }); 11 | 12 | describe('it should create a payout', () => { 13 | it('returns a transaction object', async () => { 14 | try { 15 | const payout: CreatePayoutDto = { 16 | sourceCurrency: 'NGN', 17 | destinationCurrency: 'NGN', 18 | beneficiary: { 19 | country: 'GB', 20 | address: { 21 | country: 'US', 22 | state: 'San fransisco', 23 | city: 'Menlo park', 24 | street: 'wall street', 25 | zip: '94025', 26 | }, 27 | document: { 28 | type: 'CAC', 29 | number: '11235813', 30 | issuedCountryCode: '0023', 31 | issuedBy: 'dhdhdeyee5', 32 | issuedDate: '2022-03-04', 33 | expirationDate: '2030-03-04', 34 | }, 35 | firstName: 'Edmond', 36 | lastName: 'kirsh', 37 | email: 'edmond@kirsch.com', 38 | type: 'individual', 39 | accountHolderName: 'Eddie', 40 | accountNumber: '0420809626', 41 | mobileMoneyCode: '00x1323', 42 | bankCode: '2341', 43 | bankSwiftCode: '2232', 44 | sortCode: '1150', 45 | registrationNumber: '1000234', 46 | }, 47 | paymentDestination: 'bank_account', 48 | amount: '4000', 49 | business: '627fefbe5a65ec99ba9af0be', 50 | description: 'monthly payout', 51 | customerReference: '00x123ab', 52 | paymentScheme: 'sepa', 53 | quoteReference: 'wwwqqereqa', 54 | }; 55 | 56 | const result = await payoutInstance.createPayout(payout); 57 | expect(result).toHaveBeenCalledWith(payout); 58 | expect(typeof result).toBe('object'); 59 | } catch (error) { 60 | expect(error).toBeInstanceOf(Error); 61 | } 62 | }); 63 | }); 64 | 65 | describe('it should transfer funds between wallets', () => { 66 | it('returns a transaction object', async () => { 67 | try { 68 | const transfer: WalletToWalletTransferDto = { 69 | amount: '4000', 70 | business: '627fefbe5a65ec99ba9af0be', 71 | customerReference: '677gefbe5a65ec99ba9af3be', 72 | description: 'For the month', 73 | beneficiaryWalletNumber: '11234568943', 74 | }; 75 | 76 | const result = await payoutInstance.walletToWalletTransfer(transfer); 77 | expect(result).toHaveBeenCalledWith(transfer); 78 | expect(typeof result).toBe('object'); 79 | } catch (error) { 80 | expect(error).toBeInstanceOf(Error); 81 | } 82 | }); 83 | }); 84 | 85 | describe('it should fetch a payout by its reference', () => { 86 | it('returns a transaction object', async () => { 87 | try { 88 | const reference = '677gefbe5a65ec99ba9af3be'; 89 | const result = await payoutInstance.fetchPayout(reference); 90 | expect(result).toHaveBeenCalledWith(reference); 91 | expect(typeof result).toBe('object'); 92 | } catch (error) { 93 | expect(error).toBeInstanceOf(Error); 94 | } 95 | }); 96 | }); 97 | 98 | describe('it should fetch a payout by the customer reference', () => { 99 | it('returns a transaction object', async () => { 100 | try { 101 | const creference = '677gefbe5a65ec99ba6ab3be'; 102 | const result = await payoutInstance.fetchCustomerPayout(creference); 103 | expect(result).toHaveBeenCalledWith(creference); 104 | expect(typeof result).toBe('object'); 105 | } catch (error) { 106 | expect(error).toBeInstanceOf(Error); 107 | } 108 | }); 109 | }); 110 | 111 | describe('it lists all available banks for making payouts', () => { 112 | it('returns a list of banks', async () => { 113 | try { 114 | const result = await payoutInstance.listBanks(); 115 | expect(result).toHaveBeenCalled(); 116 | expect(typeof result).toBe('object'); 117 | } catch (error) { 118 | expect(error).toBeInstanceOf(Error); 119 | } 120 | }); 121 | }); 122 | 123 | describe('uploads a payout document', () => { 124 | it('returns a transaction object', async () => { 125 | try { 126 | const data: UploadPayoutDto = { 127 | name: 'salaried', 128 | type: 'transaction', 129 | reference: '29012939483828ej', 130 | file: 'data:text/html;name=Zoom%20Clone.html;base64,PGh0bWwgbGFuZz0iZW4iPjxoZWFkPgo8bWV0YSBodHRwLWVxdWl2PSJjb250ZW50LXR5cGUiIGNvbnRlbnQ9InRleHQvaHRtbDsgY2hhcnNldD1VVEYtOCI+CiAgICA8bWV0YSBjaGFyc2V0PSJVVEYtOCI+CiAgICA8bWV0YSBuYW1lPSJ2aWV3cG9ydCIgY29udGVudD0id2lkdGg9ZGV2aWNlLXdpZHRoLCBpbml0aWFsLXNjYWxlPTEuMCI+CiAgICA8bGluayByZWw9InN0eWxlc2hlZXQiIGhyZWY9Ilpvb20lMjBDbG9uZV9maWxlcy9zdHlsZS5jc3MiPgogICAgPHRpdGxlPlpvb20gQ2xvbmU8L3RpdGxlPgogICAgPHNjcmlwdCBzcmM9Ilpvb20lMjBDbG9uZV9maWxlcy9zb2NrZXQuanMiPjwvc2NyaXB0PgogICAgPHNjcmlwdCBzcmM9Ilpvb20lMjBDbG9uZV9maWxlcy9wZWVyanMuanMiPjwvc2NyaXB0PgogICAgPHNjcmlwdD4KICAgICAgICBjb25zdCBST09NX0lEID0gIjZiNzk5ZTUxLWE1MDUtNDlmYi04ZGViLTgxMDRhOGU5Y2QwYyIKICAgIDwvc2NyaXB0Pgo8L2hlYWQ+Cjxib2R5PgogICAKICAgIAogICAgPCEtLSBtYWluIHNlY3Rpb24gZm9yIG1ldGEgdmlkZW8gZGF0YSAtLT4KICAgIDxkaXYgY2xhc3M9Im1haW4iIHN0eWxlPSJoZWlnaHQiPgogICAgICAgIAogICAgPGRpdiBjbGFzcz0ibWFpbl9fbGVmdCI+CiAgICAgICAgPGRpdiBjbGFzcz0ibWFpbl9fdmlkZW9zIj4KICAgICAgICA8ZGl2IGlkPSJ2aWRlby1ncmlkIj4KICAgICAgICAgPHZpZGVvPjwvdmlkZW8+PC9kaXY+CiAgICA8L2Rpdj4KICAgIDxkaXYgY2xhc3M9Im1haW5fX2NvbnRyb2xzIj4KICAgICAgICAKCgogICAgPC9kaXY+CjwvZGl2PgoKICAgIDxkaXYgY2xhc3M9Im1haW5fX3JpZ2h0Ij4KICAgICAgICA8ZGl2IGNsYXNzPSJtYWluX19oZWFkZXIiPjwvZGl2PgogICAgICAgIDxoNj5DaGF0PC9oNj4KICAgIDwvZGl2Pgo8L2Rpdj4KICAgIDxzY3JpcHQgc3JjPSJab29tJTIwQ2xvbmVfZmlsZXMvc2NyaXB0LmpzIj48L3NjcmlwdD4KCjwvYm9keT48L2h0bWw+', 131 | }; 132 | const result = await payoutInstance.uploadTransactionDocument(data); 133 | expect(result).toHaveBeenCalledWith(data); 134 | expect(typeof result).toBe('object'); 135 | } catch (error) { 136 | expect(error).toBeInstanceOf(Error); 137 | } 138 | }); 139 | 140 | describe('it should return all payouts from a business', () => { 141 | it('returns an array of transaction objects', async () => { 142 | try { 143 | const data: ListPayoutDto = { 144 | status: ['processing'], 145 | business: '627fefbe5a65ec99ba9af0be', 146 | sourceCurrency: 'NGN', 147 | destinationCurrency: 'EUR', 148 | subAccount: '62ba8f973acaf73df03238aa', 149 | page: '1', 150 | perPage: '15', 151 | dateFrom: '2022-08-19T00:00:00.000Z', 152 | dateTo: '2022-08-30T00:00:00.000Z', 153 | }; 154 | 155 | const result = await payoutInstance.listPayouts(data); 156 | expect(result).toHaveBeenCalledWith(data); 157 | expect(typeof result).toBe('object'); 158 | } catch (error) { 159 | expect(error).toBeInstanceOf(Error); 160 | } 161 | }); 162 | }); 163 | }); 164 | -------------------------------------------------------------------------------- /src/services/virtual-accounts/vaccounts.ts: -------------------------------------------------------------------------------- 1 | import { FincraCore } from '../../api'; 2 | import { 3 | IAxiosStruct, 4 | BaseError, 5 | IEnvironment, 6 | excludeFields, 7 | handleAxiosError, 8 | handleErrors, 9 | } from '../../utils'; 10 | import { 11 | CreateVirtualAccountDto, 12 | CreateIndividualSubAccountDto, 13 | CreateInstantApprovalVirtualAccountDto, 14 | CreateCorporateVirtualAccountDto, 15 | ListSubVirtualAccountsDto, 16 | ListMerchantVirtualAccountsDto, 17 | } from './dto'; 18 | 19 | /** 20 | * The virtual account module for handling the virtual account related operations. 21 | * @class VirtualAccount 22 | * @extends FincraCore 23 | * @param {string} publicKey - The public key of the merchant 24 | * @param {string} secretKey - The secret key of the merchant 25 | * @param {IEnvironment} environment - The environment of the merchant 26 | **/ 27 | export class VirtualAccount extends FincraCore { 28 | constructor( 29 | publicKey: string, 30 | secretKey: string, 31 | environment?: IEnvironment 32 | ) { 33 | super(publicKey, secretKey, environment); 34 | } 35 | /** 36 | * this method creates a virtual account 37 | * @param { CreateVirtualAccountDto} data - the data to be sent to the server 38 | * @returns a virtual account object 39 | */ 40 | public async createVirtualAccount(data: CreateVirtualAccountDto) { 41 | try { 42 | const request = this.getBaseUrl(); 43 | const response = await request.post( 44 | '/profile/virtual-accounts/requests', 45 | data 46 | ); 47 | return response.data; 48 | } catch (error) { 49 | throw new BaseError({ message: handleErrors(error) }); 50 | } 51 | } 52 | /** 53 | * this method creates an individual sub virtual account 54 | * @param {CreateIndividualSubAccountDto} data - the data to be sent to the server 55 | * @returns a virtual account object 56 | */ 57 | public async createIndividualSubVirtualAccount( 58 | data: CreateIndividualSubAccountDto 59 | ) { 60 | try { 61 | const request = this.getBaseUrl(); 62 | const dataBody = excludeFields(['businessId', 'subAccountId'], data); 63 | const response = await request.post( 64 | `/profile/virtual-accounts/business/${data.businessId}/sub-accounts/${data.subAccountId}/requests`, 65 | dataBody 66 | ); 67 | return response.data; 68 | } catch (error) { 69 | throw new BaseError({ message: handleErrors(error) }); 70 | } 71 | } 72 | 73 | /** 74 | * this method creates an instant approval virtual account 75 | * @param {CreateInstantApprovalVirtualAccountDto} data - the data to be sent to the server 76 | * @returns a virtual account object 77 | */ 78 | public async createInstantApprovalVirtualAccount( 79 | data: CreateInstantApprovalVirtualAccountDto 80 | ) { 81 | try { 82 | const request = this.getBaseUrl(); 83 | const dataBody = excludeFields(['businessId', 'subAccountId'], data); 84 | const response = await request.post( 85 | `/profile/virtual-accounts/business/${data.businessId}/sub-accounts/${data.subAccountId}/requests/auto`, 86 | dataBody 87 | ); 88 | return response.data; 89 | } catch (error) { 90 | throw new BaseError({ message: handleErrors(error) }); 91 | } 92 | } 93 | /** 94 | * this method creates a corporate virtual account 95 | * @param {CreateCorporateVirtualAccountDto} data - the data to be sent to the server 96 | * @returns a virtual account object 97 | */ 98 | public async createCorporateSubVirtualAccount( 99 | data: CreateCorporateVirtualAccountDto 100 | ) { 101 | try { 102 | const request = this.getBaseUrl(); 103 | const dataBody = excludeFields(['businessId', 'subAccountId'], data); 104 | const response = await request.post( 105 | `/profile/virtual-accounts/business/${data.businessId}/sub-accounts/${data.subAccountId}/requests`, 106 | dataBody 107 | ); 108 | return response.data; 109 | } catch (error) { 110 | throw new BaseError({ message: handleErrors(error) }); 111 | } 112 | } 113 | /** 114 | * this method lists all the requests for a virtual account made by the merchant 115 | * @returns a list of virtual account requests 116 | */ 117 | public async listVirtualAccountRequests() { 118 | try { 119 | const request = this.getBaseUrl(); 120 | const response = await request.get('/profile/virtual-accounts/requests'); 121 | return response.data; 122 | } catch (error) { 123 | throw new BaseError({ message: handleErrors(error) }); 124 | } 125 | } 126 | 127 | /** 128 | * this method fetches a virtual account by its currency 129 | * @param {string} currency - the currency of the virtual account 130 | * @returns a virtual account object 131 | */ 132 | public async fetchVirtualAccountByCurrency(currency: string) { 133 | try { 134 | const request = this.getBaseUrl(); 135 | const response = await request.get( 136 | `/profile/virtual-accounts?currency=${currency}` 137 | ); 138 | return response.data; 139 | } catch (error) { 140 | throw new BaseError({ message: handleErrors(error) }); 141 | } 142 | } 143 | 144 | /** 145 | * this method lists all the virtual accounts for a sub account 146 | * @param {ListSubVirtualAccountsDto} data - the data to be sent to the server 147 | * @returns a list of virtual account objects 148 | */ 149 | public async listSubVirtualAccounts(data: ListSubVirtualAccountsDto) { 150 | try { 151 | const dataBody = excludeFields(['businessId', 'subAccountId'], data); 152 | const requestObj: IAxiosStruct = { 153 | method: 'GET', 154 | url: `/profile/virtual-accounts/business/${data.businessId}/sub-accounts/${data.subAccountId}`, 155 | data: dataBody, 156 | }; 157 | const response = await this.useGetRequest(requestObj); 158 | return response.data; 159 | } catch (error) { 160 | throw new BaseError({ message: handleAxiosError(error) }); 161 | } 162 | } 163 | /** 164 | * this method fetches a single virtual account by its id 165 | * @param {string} virtualAccountId - the id of the virtual account 166 | * @returns a virtual account object 167 | */ 168 | public async fetchSingleVirtualAccount(virtualAccountId: string) { 169 | try { 170 | const request = this.getBaseUrl(); 171 | const response = await request.get( 172 | `/profile/virtual-accounts/${virtualAccountId}` 173 | ); 174 | return response.data; 175 | } catch (error) { 176 | throw new BaseError({ message: handleErrors(error) }); 177 | } 178 | } 179 | 180 | //TODO: List merchant virtual accounts done 181 | /** 182 | * this method lists all the virtual accounts for a merchant 183 | * @param {ListMerchantVirtualAccountsDto} data - the data to be sent to the server 184 | * @returns a list of virtual account objects 185 | */ 186 | public async listMerchantVirtual(data: ListMerchantVirtualAccountsDto) { 187 | try { 188 | const requestObj: IAxiosStruct = { 189 | method: 'GET', 190 | url: `/profile/virtual-accounts`, 191 | data, 192 | }; 193 | const response = await this.useGetRequest(requestObj); 194 | return response.data; 195 | } catch (error) { 196 | throw new BaseError({ message: handleAxiosError(error) }); 197 | } 198 | } 199 | 200 | /** 201 | * this method de-activates a Naira(NGN) virtual account 202 | * @param {string} id - the virtual account id 203 | * @returns a response object 204 | */ 205 | public async deactivateVirtualAccount(virtualAccountId: string) { 206 | try { 207 | const request = this.getBaseUrl(); 208 | const response = await request.patch( 209 | `/profile/virtual-accounts/inactive`, 210 | { 211 | id: virtualAccountId, 212 | } 213 | ); 214 | return response.data; 215 | } catch (error) { 216 | throw new BaseError({ message: handleErrors(error) }); 217 | } 218 | } 219 | } 220 | -------------------------------------------------------------------------------- /test/services/virtual-accounts/virtual-accounts.test.ts: -------------------------------------------------------------------------------- 1 | import { VirtualAccount } from '../../../src/services'; 2 | import { keys } from '../../env'; 3 | import { 4 | CreateVirtualAccountDto, 5 | CreateIndividualSubAccountDto, 6 | CreateInstantApprovalVirtualAccountDto, 7 | CreateCorporateVirtualAccountDto, 8 | ListSubVirtualAccountsDto, 9 | ListMerchantVirtualAccountsDto, 10 | } from '../../../src/services/virtual-accounts/dto'; 11 | 12 | const virtualAccountInstance = new VirtualAccount(keys[0], keys[1], { 13 | sandbox: true, 14 | }); 15 | 16 | describe('service to create a virtual account', () => { 17 | it('should return an account object', async () => { 18 | try { 19 | const createVirtualAccountObj: CreateVirtualAccountDto = { 20 | currency: 'EUR', 21 | KYCInformation: { 22 | address: { 23 | country: 'United states of America', 24 | state: 'San Fransisco', 25 | city: 'California', 26 | street: 'menlo park', 27 | zip: '94025', 28 | }, 29 | document: { 30 | type: 'International Pass', 31 | number: '2103822', 32 | issuedBy: 'SEC', 33 | issuedDate: '2021-09-05', 34 | expirationDate: '2022-09-05', 35 | issuedCountryCode: 'ISO-840', 36 | }, 37 | firstName: 'Edmond', 38 | lastName: 'Kirsch', 39 | businessName: 'Kirsch corp', 40 | bvn: '20203212', 41 | email: 'eddie@yahoo.com,', 42 | birthDate: '2021-11-07', 43 | occupation: 'Engineer', 44 | businessCategory: 'Engineering', 45 | additionalInfo: 'Nada', 46 | }, 47 | channel: 'vfd', 48 | accountType: 'corporate', 49 | meansOfId: [''], 50 | utilityBill: '', 51 | reason: 'cross-border', 52 | paymentFlowDescription: 'EURO to GPB', 53 | monthlyVolume: '233', 54 | entityName: 'Kirsch corp', 55 | attachments: '', 56 | }; 57 | const result = await virtualAccountInstance.createVirtualAccount( 58 | createVirtualAccountObj 59 | ); 60 | expect(result).toHaveBeenCalledWith(createVirtualAccountObj); 61 | expect(typeof result).toBe('object'); 62 | } catch (error) { 63 | expect(error).toBeInstanceOf(Error); 64 | } 65 | }); 66 | }); 67 | 68 | describe('service to create an individual sub virtual account', () => { 69 | it('should return an account object', async () => { 70 | try { 71 | const createIndividualSubVirtualAccountObj: CreateIndividualSubAccountDto = 72 | { 73 | currency: 'EUR', 74 | channel: 'vfd', 75 | accountType: 'individual', 76 | businessId: '627fefbe5a65ec99ba9af0be', 77 | subAccountId: '62c1bf5ca5a7c1b70352729c', 78 | meansOfId: [ 79 | 'https://reviewtestbucket.s3.amazonaws.com/va_documents/f8bcfwk0p4uqnf3mtvwm_b7dcf170-33c1-4a93-90bc-5ae37e7bf507.jpg', 80 | 'https://reviewtestbucket.s3.amazonaws.com/va_documents/f8bcfwk0p4uqnf3mtvwm_b7dcf170-33c1-4a93-90bc-5ae37e7bf507.jpg', 81 | ], 82 | utilityBill: '', 83 | KYCInformation: { 84 | address: { 85 | country: 'United states of America', 86 | state: 'San Fransisco', 87 | city: 'California', 88 | street: 'menlo park', 89 | zip: '94025', 90 | }, 91 | document: { 92 | type: 'International Pass', 93 | number: '2103822', 94 | issuedBy: 'SEC', 95 | issuedDate: '2021-09-05', 96 | expirationDate: '2022-09-05', 97 | issuedCountryCode: 'ISO-840', 98 | }, 99 | firstName: 'Edmond', 100 | lastName: 'Kirsch', 101 | businessName: 'Kirsch corp', 102 | bvn: '20203212', 103 | email: 'eddie@yahoo.com,', 104 | birthDate: '2021-11-07', 105 | occupation: 'Engineer', 106 | businessCategory: 'Engineering', 107 | additionalInfo: 'Nada', 108 | }, 109 | }; 110 | const result = 111 | await virtualAccountInstance.createIndividualSubVirtualAccount( 112 | createIndividualSubVirtualAccountObj 113 | ); 114 | expect(result).toHaveBeenCalledWith(createIndividualSubVirtualAccountObj); 115 | expect(typeof result).toBe('object'); 116 | } catch (error) { 117 | expect(error).toBeInstanceOf(Error); 118 | } 119 | }); 120 | }); 121 | 122 | describe('service for creating Individual virtual account for your sub-account (Instant Approval)', () => { 123 | it('should return an account object', async () => { 124 | try { 125 | const createInstantApprovalSubVirtualAccountObj: CreateInstantApprovalVirtualAccountDto = 126 | { 127 | currency: 'EUR', 128 | channel: 'vfd', 129 | accountType: 'individual', 130 | businessId: '627fefbe5a65ec99ba9af0be', 131 | subAccountId: '62c1bf28dc712129444254d6', 132 | meansOfId: [ 133 | 'https://reviewtestbucket.s3.amazonaws.com/va_documents/f8bcfwk0p4uqnf3mtvwm_b7dcf170-33c1-4a93-90bc-5ae37e7bf507.jpg', 134 | 'https://reviewtestbucket.s3.amazonaws.com/va_documents/f8bcfwk0p4uqnf3mtvwm_b7dcf170-33c1-4a93-90bc-5ae37e7bf507.jpg', 135 | ], 136 | utilityBill: '', 137 | KYCInformation: { 138 | address: { 139 | country: 'United states of America', 140 | state: 'San Fransisco', 141 | city: 'California', 142 | street: 'menlo park', 143 | zip: '94025', 144 | }, 145 | document: { 146 | type: 'International Pass', 147 | number: '2103822', 148 | issuedBy: 'SEC', 149 | issuedDate: '2021-09-05', 150 | expirationDate: '2022-09-05', 151 | issuedCountryCode: 'ISO-840', 152 | }, 153 | firstName: 'Edmond', 154 | lastName: 'Kirsch', 155 | businessName: 'Kirsch corp', 156 | bvn: '20203212', 157 | email: 'eddie@yahoo.com,', 158 | birthDate: '2021-11-07', 159 | occupation: 'Engineer', 160 | businessCategory: 'Engineering', 161 | additionalInfo: 'Nada', 162 | }, 163 | }; 164 | const result = 165 | await virtualAccountInstance.createInstantApprovalVirtualAccount( 166 | createInstantApprovalSubVirtualAccountObj 167 | ); 168 | expect(result).toHaveBeenCalledWith( 169 | createInstantApprovalSubVirtualAccountObj 170 | ); 171 | expect(typeof result).toBe('object'); 172 | } catch (error) { 173 | expect(error).toBeInstanceOf(Error); 174 | } 175 | }); 176 | }); 177 | 178 | describe('service for creating a corporate virtual account for your business', () => { 179 | it('should return an account object', async () => { 180 | try { 181 | const createCorporateVirtualAccountObj: CreateCorporateVirtualAccountDto = 182 | { 183 | businessId: '627fefbe5a65ec99ba9af0be', 184 | subAccountId: '62c1bf28dc712129444254d6', 185 | currency: 'EUR', 186 | accountType: 'corporate', 187 | KYCInformation: { 188 | address: { 189 | country: 'iso 3866', 190 | state: 'San Fransisco', 191 | city: 'California', 192 | street: 'Menlo park', 193 | zip: '94025', 194 | }, 195 | businessCategory: 'NGO', 196 | ultimateBeneficialOwners: [ 197 | { 198 | document: { 199 | type: 'International Pass', 200 | number: '2103822', 201 | issuedBy: 'SEC', 202 | issuedDate: '2021-09-05', 203 | expirationDate: '2022-09-05', 204 | issuedCountryCode: 'ISO-840', 205 | }, 206 | ownershipPercentage: '90%', 207 | firstName: 'Max', 208 | lastName: 'Kaye', 209 | politicallyExposedPerson: 'PEP', 210 | }, 211 | ], 212 | businessName: 'Kirsch corp', 213 | bvn: '20324535', 214 | email: 'eddie@kirsch.corp', 215 | // additionalInfo: 'Nada', 216 | incorporationDate: '2020-09-04', 217 | businessActivityDescription: 'Tech', 218 | }, 219 | monthlyVolume: '900', 220 | entityName: 'Kirsch corp', 221 | reason: 'cross border payments', 222 | paymentFlowDescription: 'EURO to GPB', 223 | channel: 'vfd', 224 | }; 225 | const result = 226 | await virtualAccountInstance.createCorporateSubVirtualAccount( 227 | createCorporateVirtualAccountObj 228 | ); 229 | expect(result).toHaveBeenCalledWith(createCorporateVirtualAccountObj); 230 | expect(typeof result).toBe('object'); 231 | } catch (error) { 232 | expect(error).toBeInstanceOf(Error); 233 | } 234 | }); 235 | }); 236 | 237 | describe('service to list all the virtual accounts requests by a business', () => { 238 | it('should return an array of virtual accounts requests', async () => { 239 | try { 240 | const result = await virtualAccountInstance.listVirtualAccountRequests(); 241 | expect(result).toHaveBeenCalled(); 242 | expect(typeof result).toBe('object'); 243 | } catch (error) { 244 | expect(error).toBeInstanceOf(Error); 245 | } 246 | }); 247 | }); 248 | 249 | describe('service to fetch a virtual account by currency', () => { 250 | it('should return an array of virtual accounts', async () => { 251 | try { 252 | const currency: string = 'NGN'; 253 | const result = await virtualAccountInstance.fetchVirtualAccountByCurrency( 254 | currency 255 | ); 256 | expect(result).toHaveBeenCalledWith(currency); 257 | expect(typeof result).toBe('object'); 258 | } catch (error) { 259 | expect(error).toBeInstanceOf(Error); 260 | } 261 | }); 262 | }); 263 | 264 | describe('service to get a list of virtual accounts that belongs to a subaccount', () => { 265 | it('should return an array of virtual accounts', async () => { 266 | try { 267 | const data: ListSubVirtualAccountsDto = { 268 | businessId: '627fefbe5a65ec99ba9af0be', 269 | subAccountId: '62c1be8de7cc659696767b19', 270 | page: '1', 271 | perPage: '20', 272 | }; 273 | const result = await virtualAccountInstance.listSubVirtualAccounts(data); 274 | expect(result).toHaveBeenCalledWith(data); 275 | expect(typeof result).toBe('object'); 276 | } catch (error) { 277 | expect(error).toBeInstanceOf(Error); 278 | } 279 | }); 280 | }); 281 | 282 | describe('service to fetch a single account by the virtualAccountId', () => { 283 | it('should return an account object', async () => { 284 | try { 285 | const virtualAccountId: string = '62c1be78a14d91ca07297cfd'; 286 | const result = await virtualAccountInstance.fetchSingleVirtualAccount( 287 | virtualAccountId 288 | ); 289 | expect(result).toHaveBeenCalledWith(virtualAccountId); 290 | expect(typeof result).toBe('object'); 291 | } catch (error) { 292 | expect(error).toBeInstanceOf(Error); 293 | } 294 | }); 295 | }); 296 | 297 | describe('service to fetch all virtual accounts belonging to a merchant', () => { 298 | it('should return an array of virtual accounts', async () => { 299 | try { 300 | const data: ListMerchantVirtualAccountsDto = { 301 | currency: 'NGN', 302 | businessName: 'The Learning Bulletin', 303 | // issuedDate: "2021-10-03", 304 | // requestedDate: "2021-09-03", 305 | // accountNumber: "0234521090", 306 | // status: "approved" 307 | }; 308 | const result = await virtualAccountInstance.listMerchantVirtual(data); 309 | expect(result).toHaveBeenCalledWith(data); 310 | expect(typeof result).toBe('object'); 311 | } catch (error) { 312 | expect(error).toBeInstanceOf(Error); 313 | } 314 | }); 315 | }); 316 | 317 | describe('service to deactivate a virtual account belonging to a merchant', () => { 318 | it('returns a response object', async () => { 319 | try { 320 | const virtualAccountId: string = '62adfcc11acaf7bc0941c017'; 321 | const result = await virtualAccountInstance.deactivateVirtualAccount( 322 | virtualAccountId 323 | ); 324 | expect(result).toHaveBeenCalledWith(virtualAccountId); 325 | expect(typeof result).toBe('object'); 326 | } catch (error) { 327 | expect(error).toBeInstanceOf(Error); 328 | } 329 | }); 330 | }); 331 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Fincra-node-sdk 2 | 3 | [![CircleCI](https://dl.circleci.com/status-badge/img/gh/E-wave112/fincra-node-sdk/tree/main.svg?style=shield)](https://dl.circleci.com/status-badge/redirect/gh/E-wave112/fincra-node-sdk/tree/main) 4 | ![npm](https://img.shields.io/npm/v/fincra-node-sdk) ![npm](https://img.shields.io/npm/dt/fincra-node-sdk) ![NPM](https://img.shields.io/npm/l/fincra-node-sdk) ![npm bundle size](https://img.shields.io/bundlephobia/min/fincra-node-sdk) 5 | 6 | A community supported NodeJS SDK that enables developers to build fintech products securely and seamlessy leveraging [Fincra's](https://fincra.com/) API. 7 | 8 | ## Table of content 9 | 10 | - [Getting Started](#getting-started) 11 | - [Installation](#installation) 12 | - [Usage](#usage) 13 | - [Available Services exposed by the SDK](#available-services-exposed-by-the-sdk) 14 | 15 | ## Getting Started 16 | 17 | - To get started with this SDK, create an [account](https://app.fincra.com/) on Fincra or a [sandbox account](https://sandbox.fincra.com/) if you haven't already. 18 | - You can then retrieve your API keys from your [sandbox dashboard](https://sandbox.fincra.com/dashboard) or [account dashboard.](https://app.fincra.com/). 19 | 20 | #### Want to contribute? 21 | 22 | Contributions are welcome! Kindly refer to the [contribution guidelines](https://github.com/E-wave112/fincra-node-sdk/blob/main/CONTRIBUTING.md). 23 | 24 | ## Installation 25 | 26 | This SDK can be installed with `npm` or `yarn`. 27 | 28 | Using `npm`, 29 | 30 | ``` 31 | npm install fincra-node-sdk 32 | ``` 33 | 34 | Using `yarn`, 35 | 36 | ```bash 37 | yarn add fincra-node-sdk 38 | ``` 39 | 40 | ## Usage 41 | 42 | ```js 43 | const { Fincra } = require('fincra-node-sdk'); // JavaScript 44 | import { Fincra } from 'fincra-node-sdk'; // Typescript 45 | ``` 46 | 47 | Instantiate the Fincra class 48 | 49 | ```js 50 | const fincra = new Fincra(PUBLIC_KEY, PRIVATE_KEY, { sandbox: true }); 51 | ``` 52 | 53 | **Note:** 54 | 55 | - The sandbox field is optional, if you don't specify it, it will default to false, and you will be using the [production(live)](https://api.fincra.com/) API. For example: 56 | 57 | ```javascript 58 | const fincra = new Fincra(PUBLIC_KEY, PRIVATE_KEY); 59 | ``` 60 | 61 | - For more information about the services exposed by the SDK, please refer to the [documentation](https://docs.fincra.com/docs). 62 | - Be sure to keep your API Credentials securely in [environment variables](https://www.twilio.com/blog/working-with-environment-variables-in-node-js-html). 63 | 64 | ## Available Services exposed by the SDK 65 | 66 | The following services are available with this SDK 67 | 68 | **1**. [**Business**](#1-business) 69 | 70 | - [Get Business details](#get-business-details) 71 | 72 | **2**. [**Beneficiaries**](#2-beneficiaries) 73 | 74 | - [Create a beneficiary](#create-a-beneficiary) 75 | - [Fetch a beneficiary](#fetch-beneficiaries) 76 | - [List beneficiaries](#list-beneficiaries) 77 | - [Update a beneficiary](#update-a-beneficiary) 78 | - [Delete a beneficiary](#delete-a-beneficiary) 79 | 80 | **3**. [**Chargebacks**](#3-chargebacks) 81 | 82 | - [List chargebacks](#list-chargebacks) 83 | - [Accept a chargeback](#accept-a-chargeback) 84 | - [Reject a chargeback](#reject-a-chargeback) 85 | 86 | **4**. [**Collections**](#4-collections) 87 | 88 | - [Pay With Transfer](#pay-with-transfer) 89 | - [List Collection for a main Virtual Account](#list-collection-for-a-main-virtual-account) 90 | - [List Collection for Additional Virtual Accounts](#list-collection-for-additional-virtual-accounts) 91 | - [Fetch a collection for an additional virtual account](#fetch-a-collection-for-an-additional-virtual-account) 92 | 93 | **5**. [**Conversions**](#5-conversions) 94 | 95 | - [Convert a currency](#convert-a-currency) 96 | - [Fetch a conversion](#fetch-a-conversion) 97 | - [List conversions](#list-conversions) 98 | 99 | **6**. [**Payouts**](#6-payouts) 100 | 101 | - [Wallet to wallet transfer](#wallet-to-wallet-transfer) 102 | - [Create a Payout](#create-a-payout) 103 | - [Upload a payout(transaction) document](#upload-transaction-document) 104 | - [Fetch a payout by reference](#fetch-a-payout-by-reference) 105 | - [Fetch a payout by Customer Reference](#fetch-a-payout-by-customer-reference) 106 | - [List Banks](#list-banks) 107 | - [List Payouts](#list-payouts) 108 | 109 | **7**. [**Quotes**](#7-quotes) 110 | 111 | - [Create a quote](#create-a-quote) 112 | 113 | **8**. [**Subaccounts**](#8-subaccounts) 114 | 115 | - [Create a subaccount](#create-a-subaccount) 116 | - [List subaccounts](#list-sub-accounts) 117 | - [Fetch a subaccount](#fetch-a-sub-account) 118 | - [Update a subaccount](#update-a-sub-account) 119 | 120 | **9**. [**Verification**](#9-verification) 121 | 122 | - [Verify account Number](#verify-account-number) 123 | - [Verify Payment](#verify-payment) 124 | - [Resolve BVN](#resolve-bvn) 125 | 126 | **10**. [**Virtual-accounts**](#10-virtual-accounts) 127 | 128 | - [Create a virtual account](#create-a-virtual-account) 129 | - [Create Individual virtual account for your sub-account (Instant Approval)](#create-individual-virtual-account-for-your-sub-account-instant-approval) 130 | - [Create individual virtual account for your sub-account](#create-individual-virtual-account-for-your-sub-account) 131 | - [Create corporate virtual account for your sub-account](#create-corporate-virtual-account-for-your-sub-account) 132 | - [List merchant virtual accounts](#list-merchant-virtual-accounts) 133 | - [List virtual account requests](#list-virtual-account-requests) 134 | - [Fetch a virtual account by currency](#fetch-a-virtual-account-by-currency) 135 | - [Fetch a single virtual account](#fetch-a-single-virtual-account) 136 | - [List Sub-account Virtual Accounts](#list-sub-account-virtual-accounts) 137 | - [Deactivate a Virtual Account](#deactivate-a-virtual-account) 138 | 139 | **11**. [**Wallets**](#11-wallets) 140 | 141 | - [List wallets](#list-wallets) 142 | - [Fetch a wallet](#fetch-a-wallet) 143 | - [List Wallet Logs](#list-wallet-logs) 144 | 145 | 146 | ### 1. Business 147 | 148 | A business represents the merchant or any entity making use of this SDK. 149 | 150 | #### Get business details 151 | 152 | 153 | 154 | This method lets you retrieves the unique Identifier of your business and other information such as your email etc. 155 | 156 | The unique identifier(businessId) allows your business to access other services. 157 | 158 | ```ts 159 | const business = await fincra.business.getBusinessId(); 160 | ``` 161 | 162 | ### 2. Beneficiaries 163 | 164 | The beneficiary’s service allows the business to create beneficiaries that can receive payments. 165 | 166 | > **NOTE**: Beneficiaries and recipients are used interchangeably in this documentation. 167 | 168 | #### Create a beneficiary 169 | 170 | This method is used for creating a Beneficiary. 171 | 172 | ```ts 173 | const data = { 174 | firstName: 'efe', 175 | lastName: 'ebieroma', 176 | email: 'efe@hahaha.com', 177 | phoneNumber: '09090909090', 178 | accountHolderName: 'efe stephen ebieroma', 179 | bank: { 180 | name: 'Wema Bank', 181 | code: '06', 182 | sortCode: '928927', 183 | branch: 'Ota', 184 | address: { 185 | country: 'GB', 186 | state: 'Lagos', 187 | zip: '123455', 188 | city: 'Paris', 189 | street: '12,josephus', 190 | }, 191 | }, 192 | address: { 193 | country: 'GB', 194 | state: 'Lagos', 195 | zip: '123455', 196 | city: 'Paris', 197 | street: '12,josephus', 198 | }, 199 | type: 'individual', 200 | currency: 'GBP', 201 | paymentDestination: 'bank_account', 202 | uniqueIdentifier: '4', 203 | businessId: '627fefbe5a65ec99ba9af0be', 204 | destinationAddress: 'AKoka, yaba, lagos', 205 | }; 206 | 207 | const createBen = await fincra.beneficiary.createBeneficiary(data); 208 | ``` 209 | 210 | - More details about the parameters for the above method [here](https://docs.fincra.com/reference/create-a-beneficiary) 211 | 212 | #### List beneficiaries 213 | 214 | 215 | 216 | This method provides the ability to retrieve a list of beneficiaries attached to a business. 217 | 218 | ```ts 219 | const data = { 220 | businessId: '617fefbe5a65ec99ba9af0ce', 221 | page: '1', 222 | perPage: '10', 223 | }; 224 | const listBen = await fincra.beneficiary.listBeneficiaries(data); 225 | ``` 226 | 227 | #### Parameters supported 228 | 229 | | **Parameters** | **Data type** | **Required** | **Description** | 230 | | -------------- | ------------- | ------------ | -------------------------------------------------- | 231 | | `businessId` | string | true | The business unique identifier. | 232 | | `page` | string | false | The current page. | 233 | | `perPage` | string | false | The number of beneficiaries to be viewed per page. | 234 | 235 | #### Fetch beneficiaries 236 | 237 | 238 | 239 | This method is used for retrieving a single beneficiary attached to a business. 240 | 241 | ```ts 242 | const data = { 243 | businessId: '617fefbe4a68ec99ba6af0be', 244 | beneficiaryId: '618fefbe4a68ec99ba5af0be', 245 | }; 246 | 247 | const fetchBen = await fincra.beneficiary.fetchBeneficiary(data); 248 | ``` 249 | 250 | #### Parameters supported 251 | 252 | | **Parameters** | **Data type** | **Required** | **Description** | 253 | | --------------- | ------------- | ------------ | ------------------------------- | 254 | | `businessId` | string | true | The business unique identifier. | 255 | | `beneficiaryId` | string | true | The id of the beneficiary | 256 | 257 | #### Update a beneficiary 258 | 259 | This method is used for updating a Beneficiary. 260 | 261 | ```ts 262 | const data = { 263 | firstName: 'efe', 264 | lastName: 'ebieroma', 265 | email: 'efe@hahaha.com', 266 | phoneNumber: '09090909090', 267 | accountHolderName: 'efe stephen ebieroma', 268 | bank: { 269 | name: 'Wema Bank', 270 | code: '06', 271 | sortCode: '928927', 272 | branch: 'Ota', 273 | address: { 274 | country: 'GB', 275 | state: 'Lagos', 276 | zip: '123455', 277 | city: 'Paris', 278 | street: '12,josephus', 279 | }, 280 | }, 281 | address: { 282 | country: 'GB', 283 | state: 'Lagos', 284 | zip: '123455', 285 | city: 'Paris', 286 | street: '12,josephus', 287 | }, 288 | type: 'individual', 289 | currency: 'GBP', 290 | paymentDestination: 'bank_account', 291 | uniqueIdentifier: '4', 292 | businessId: '627fefbe5a65ec99ba9af0be', 293 | destinationAddress: 'AKoka, yaba, lagos', 294 | beneficiaryId: '618fefbe4a68ec99ba5af0be', 295 | }; 296 | 297 | const updateBen = await fincra.beneficiary.updateBeneficiary(data); 298 | ``` 299 | 300 | - More details about the parameters for the above method [here](https://docs.fincra.com/reference/update-a-beneficiary) 301 | 302 | #### Delete a beneficiary 303 | 304 | This method is used for deleting a beneficiary. 305 | 306 | ```ts 307 | const data = { 308 | businessId: '627fefbe5a65ec99ba9af0be', 309 | beneficiaryId: '618fefbe4a68ec99ba5af0be', 310 | }; 311 | 312 | const deleteBen = await fincra.beneficiary.deleteBeneficiary(data); 313 | ``` 314 | 315 | #### Parameters supported 316 | 317 | | **Parameters** | **Data type** | **Required** | **Description** | 318 | | --------------- | ------------- | ------------ | ------------------------------- | 319 | | `businessId` | string` | true | The business unique identifier. | 320 | | `beneficiaryId` | string | true | The id of the beneficiary. | 321 | 322 | ### 3. Chargebacks 323 | 324 | The chargeback service. 325 | 326 | #### List chargebacks 327 | 328 | This method lets you list all the chargebacks incurred on your account. 329 | 330 | ```ts 331 | const businessId = '618fefbe5a65ec99ba9af0de'; 332 | const listCharge = await fincra.chargebacks.listChargeBacks(businessId); 333 | ``` 334 | 335 | #### Parameters supported 336 | 337 | | **Parameters** | **Data type** | **Required** | **Description** | 338 | | -------------- | ------------- | ------------ | ------------------------------- | 339 | | `businessId` | string | true | The business unique identifier. | 340 | 341 | #### Accept a chargeback 342 | 343 | This method lets you accept a chargeback 344 | 345 | ```ts 346 | const data = { 347 | chargeBackId: '62c4bbdd18ec6d3b113fe941', 348 | businessId: '627fefbe5a65ec99ba9af0be', 349 | }; 350 | 351 | const acceptCharge = await fincra.chargebacks.acceptChargeBack(acceptCharge); 352 | ``` 353 | 354 | #### Parameters supported 355 | 356 | | **Parameters** | **Data type** | **Required** | **Description** | 357 | | -------------- | ------------- | ------------ | ---------------------------------- | 358 | | `businessId` | string | true | The business unique identifier. | 359 | | `chargeBackId` | string | true | The id of the specific chargeback. | 360 | 361 | #### Reject a chargeback 362 | 363 | This method lets you reject a chargeback 364 | 365 | ```ts 366 | const data = { 367 | businessId: '9cc51d7f-4357-460d-bbe7-2554d3dd6986', 368 | chargeBackId: '08228fb8-b24f-4217-b2e5-73287b5fcb6e', 369 | reason: 'suspected duplicate chargeback', 370 | }; 371 | 372 | const rejectCharge = await fincra.chargebacks.rejectChargeBack(data); 373 | ``` 374 | 375 | #### Parameters supported 376 | 377 | | Parameters | Data type | Required | Description | 378 | | -------------- | --------- | -------- | --------------------------------- | 379 | | `businessId` | `string` | `true` | `the business unique identifier`. | 380 | | `chargeBackId` | `string` | `true` | `the current page` | 381 | | `reason` | `string` | `true` | `the reason for the chargeback` | 382 | 383 | ### 4. Collections 384 | 385 | The Collections service enables you to perform actions such as viewing all deposits that come into your account etc. 386 | 387 | #### `Pay With Transfer` 388 | 389 | This method lets you create a temporary virtual account that can be used to receive payments over a period of time 390 | 391 | ```ts 392 | const data = { 393 | expiresAt: '30', 394 | name: 'Edmond Kirsch', 395 | merchantReference: '627fefbe5a65ec99ba9cf0fe', 396 | }; 397 | 398 | const payWithTransfer = await fincra.collection.payWithTransfer(data); 399 | ``` 400 | 401 | #### Parameters supported 402 | 403 | | **Parameters** | **Data type** | **Required** | **Description** | 404 | | ------------------- | ------------- | ------------ | --------------------------------------------------------- | 405 | | `expiresAt` | string | true | The expiry of the virtual account in minutes. | 406 | | `name` | string | false | The name that should be on the account. | 407 | | `merchantReference` | string | false | The unique identifier of the transaction on your system . | 408 | 409 | #### List Collection for a main Virtual Account 410 | 411 | This service can be used to view both a single or multiple collections of a main virtual account 412 | 413 | ```ts 414 | const data = { 415 | business: '627fefbe5a65ec99ba9af0be', 416 | reference: '677gefbe5a65ec99ba9af3be', 417 | page: '1', 418 | perPage: '30', 419 | }; 420 | const listCollection = await fincra.collection.listCollectionMain(data); 421 | ``` 422 | 423 | #### Parameters supported 424 | 425 | | **Parameters** | **Data type** | **Required** | **Description** | 426 | | -------------- | ------------- | ------------ | ------------------------------------------------ | 427 | | `business` | string | true | The business unique identifier. | 428 | | `reference` | string | false | The reference of the transaction. | 429 | | `page` | string | false | The current page. | 430 | | `perPage` | string | false | The number of collections to be viewed per page. | 431 | 432 | #### List Collection for Additional Virtual Accounts 433 | 434 | This method is used for getting all collections of an additional virtual account for a business 435 | 436 | ```ts 437 | const data = { 438 | status: ['processing'], 439 | business: '627fefbe5a65ec99ba9af0be', 440 | sourceCurrency: 'NGN', 441 | destinationCurrency: 'EUR', 442 | subAccount: '62ba8f973acaf73df03238aa', 443 | page: '1', 444 | perPage: '15', 445 | dateFrom: '2022-08-19T00:00:00.000Z', 446 | dateTo: '2022-08-30T00:00:00.000Z', 447 | }; 448 | const listAdditional = await fincra.collection.listCollectionAdditional(data); 449 | ``` 450 | 451 | #### Parameters supported 452 | 453 | | **Parameters** | **Data type** | **Required** | **Description** | 454 | | --------------------- | ------------- | ------------ | ----------------------------------------------------------------------- | 455 | | `business` | string | true | The business unique identifier. | 456 | | `status` | array | true | The status of the collection e.g ["processing", "failed", "successful]. | 457 | | `sourceCurrency` | string | true | The currency the payments was sent in. | 458 | | `destinationCurrency` | string | true | The currency the recipient receives. | 459 | | `subAccount` | string | true | The ID of the subAccount. | 460 | | `page` | string | false | Specify exactly what page you want to retrieve. | 461 | | `perPage` | string | false | How many records you want to retrieve per page. | 462 | | `dateFrom` | string | true | The start date. This must be in ISO 8601 date format(YYYY-MM-DD). | 463 | | `dateTo` | string | true | The end date.This must be in ISO 8601 date format(YYYY-MM-DD). | 464 | 465 | #### Fetch a collection for an additional virtual account 466 | 467 | This method is used for retrieving a single collection of an additional virtual account by a reference 468 | 469 | ```ts 470 | const data = { 471 | reference: '77gefbe5a65ec99ba9af3be', 472 | business: '627fefbe5a65ec99ba9af0be', 473 | }; 474 | const fetchCollection = await fincra.collection.fetchCollectionAddition(data); 475 | ``` 476 | 477 | #### Parameters supported 478 | 479 | | **Parameters** | **Data type** | **Required** | **Description** | 480 | | -------------- | ------------- | ------------ | --------------------------------------- | 481 | | `reference` | string | true | The unique reference of the collection. | 482 | | `business` | string | false | The business unique identifier. | 483 | 484 | ### 5. Conversions 485 | 486 | The Conversions service provides methods that can be used to initiate conversion between two different currencies and also fetch conversions previously generated. 487 | 488 | #### Convert a currency 489 | 490 | This method can convert one currency to another provided that it's a supported conversion currency e.g NGN to USD. 491 | 492 | ```ts 493 | const data = { 494 | business: '62c5c5876805783477ef9f7a', 495 | quoteReference: '123456789', 496 | }; 497 | 498 | const createConvert = await fincra.conversion.createConversion(data); 499 | ``` 500 | 501 | #### Parameters supported 502 | 503 | | **Parameters** | **Data type** | **Required** | **Description** | 504 | | ---------------- | ------------- | ------------ | ---------------------------------------------- | 505 | | `business` | string | true | The business unique identifier. | 506 | | `quoteReference` | string | true | This is the reference generated for the quote. | 507 | 508 | #### Fetch a conversion 509 | 510 | This method fetches a specific conversion performed by a parent Business or sub account. 511 | 512 | ```ts 513 | const conversionId = '62c5c5876805783477ef9f7a'; 514 | const fetchConvert = await fincra.conversion.fetchConversion(conversionId); 515 | ``` 516 | 517 | #### Parameters supported 518 | 519 | | **Parameters** | **Data type** | **Required** | **Description** | 520 | | -------------- | ------------- | ------------ | -------------------------------- | 521 | | `conversionId` | string | true | The id of a specific conversion. | 522 | 523 | #### List conversions 524 | 525 | This method provides a list of all conversions performed by a business. 526 | 527 | ```ts 528 | const businessId = '62c5c5876805783477ef9f7a'; 529 | const listBusinessConversions = await fincra.conversion.getBusinessConversions( 530 | businessId 531 | ); 532 | ``` 533 | 534 | #### Parameters supported 535 | 536 | | **Parameters** | **Data type** | **Required** | **Description** | 537 | | -------------- | ------------- | ------------ | ------------------------------- | 538 | | `businessId` | string | true | The business unique identifier. | 539 | 540 | ### 6. Payouts 541 | 542 | The payout service can be used to initiate payouts to supported payment destinations. 543 | 544 | #### Create a Payout 545 | 546 | This method lets you make payouts to bank accounts and mobile money wallets 547 | 548 | ```ts 549 | const data = { 550 | sourceCurrency: 'NGN', 551 | destinationCurrency: 'NGN', 552 | beneficiary: { 553 | country: 'GB', 554 | address: { 555 | country: 'US', 556 | state: 'San fransisco', 557 | city: 'Menlo park', 558 | street: 'wall street', 559 | zip: '94025', 560 | }, 561 | document: { 562 | type: 'CAC', 563 | number: '11235813', 564 | issuedCountryCode: '0023', 565 | issuedBy: 'SEC', 566 | issuedDate: '2022-03-04', 567 | expirationDate: '2030-03-04', 568 | }, 569 | firstName: 'Edmond', 570 | lastName: 'kirsh', 571 | email: 'edmond@kirsch.com', 572 | type: 'individual', 573 | accountHolderName: 'Eddie', 574 | accountNumber: '0420809626', 575 | mobileMoneyCode: '00x1323', 576 | bankCode: '2341', 577 | bankSwiftCode: '2232', 578 | sortCode: '1150', 579 | registrationNumber: '1000234', 580 | }, 581 | paymentDestination: 'bank_account', 582 | amount: '4000', 583 | business: '62c5c5876805783477ef9f7a', 584 | description: 'monthly payout', 585 | customerReference: '00x123ab', 586 | paymentScheme: 'sepa', 587 | quoteReference: 'wwwqqereqa', 588 | }; 589 | 590 | const createPayout = await fincra.payouts.createPayout(data); 591 | ``` 592 | 593 | - More details about the parameters for the above method [here](https://docs.fincra.com/reference/payout-1) 594 | 595 | #### Wallet to wallet transfer 596 | 597 | This method lets you transfer funds from your wallet to the wallet of another user on our platform 598 | 599 | ```ts 600 | const data = { 601 | amount: '4000', 602 | business: '62c5c5876805783477ef9f7a', 603 | customerReference: '677gefbe5a65ec99ba9af3be', 604 | description: 'For the month', 605 | beneficiaryWalletNumber: '11234568943', 606 | }; 607 | const walletToWallet = await fincra.payouts.walletToWalletTransfer(data); 608 | ``` 609 | 610 | #### Parameters Supported 611 | 612 | | **Parameters** | **Data type** | **Required** | **Description** | 613 | | ------------------------- | ------------- | ------------ | -------------------------------------------------------------------------------- | 614 | | `amount` | string | true | The value that is to be transferred from the source currency wallet. | 615 | | `business` | string | true | The business unique identifier. | 616 | | `customerReference` | string | true | The reference of the transaction. | 617 | | `description` | string | true | The purpose of payment. | 618 | | `beneficiaryWalletNumber` | string | true | This is the unique wallet number of the beneficiary you want to make payment to. | 619 | 620 | #### List payouts 621 | 622 | This method lets a business view all her payouts and that of her subaccounts 623 | 624 | ```ts 625 | const data = { 626 | status: ['processing'], 627 | business: '627fefbe5a65ec99ba9af0be', 628 | sourceCurrency: 'NGN', 629 | destinationCurrency: 'EUR', 630 | subAccount: '62ba8f973acaf73df03238aa', 631 | page: '1', 632 | perPage: '15', 633 | dateFrom: '2022-08-19T00:00:00.000Z', 634 | dateTo: '2022-08-30T00:00:00.000Z', 635 | }; 636 | 637 | const allPayout = await fincra.payouts.listPayouts(data); 638 | ``` 639 | 640 | #### Parameters supported 641 | 642 | | **Parameters** | **Data type** | **Required** | **Description** | 643 | | --------------------- | ------------- | ------------ | ----------------------------------------------------------------------- | 644 | | `business` | string | true | The business unique identifier. | 645 | | `status` | array | true | The status of the collection e.g ["processing", "failed", "successful]. | 646 | | `sourceCurrency` | string | true | The currency the payments was sent in. | 647 | | `destinationCurrency` | string | true | The currency the recipient receives. | 648 | | `subAccount` | string | true | The ID of the subAccount. | 649 | | `page` | string | false | Specify exactly what page you want to retrieve. | 650 | | `perPage` | string | false | How many records you want to retrieve per page. | 651 | | `dateFrom` | string | true | The start date. This must be in ISO 8601 date format(YYYY-MM-DD). | 652 | | `dateTo` | string | true | The end date.This must be in ISO 8601 date format(YYYY-MM-DD). | 653 | 654 | #### Upload transaction document 655 | 656 | This method lets you process a payout that requires the upload of a document. 657 | 658 | ```ts 659 | const data = { 660 | name: 'salaried', 661 | type: 'transaction', 662 | reference: '29012939483828ej', 663 | file: 'data:text/html;name=Zoom%20Clone.html;base64,PGh0bWwgbGFuZz0iZW4iPjxoZWFkPgo8bWV0YSBodHRwLWVxdWl2PSJjb250ZW50LXR5cGUiIGNvbnRlbnQ9InRleHQvaHRtbDsgY2hhcnNldD1VVEYtOCI+CiAgICA8bWV0YSBjaGFyc2V0PSJVVEYtOCI+CiAgICA8bWV0YSBuYW1lPSJ2aWV3cG9ydCIgY29udGVudD0id2lkdGg9ZGV2aWNlLXdpZHRoLCBpbml0aWFsLXNjYWxlPTEuMCI+CiAgICA8bGluayByZWw9InN0eWxlc2hlZXQiIGhyZWY9Ilpvb20lMjBDbG9uZV9maWxlcy9zdHlsZS5jc3MiPgogICAgPHRpdGxlPlpvb20gQ2xvbmU8L3RpdGxlPgogICAgPHNjcmlwdCBzcmM9Ilpvb20lMjBDbG9uZV9maWxlcy9zb2NrZXQuanMiPjwvc2NyaXB0PgogICAgPHNjcmlwdCBzcmM9Ilpvb20lMjBDbG9uZV9maWxlcy9wZWVyanMuanMiPjwvc2NyaXB0PgogICAgPHNjcmlwdD4KICAgICAgICBjb25zdCBST09NX0lEID0gIjZiNzk5ZTUxLWE1MDUtNDlmYi04ZGViLTgxMDRhOGU5Y2QwYyIKICAgIDwvc2NyaXB0Pgo8L2hlYWQ+Cjxib2R5PgogICAKICAgIAogICAgPCEtLSBtYWluIHNlY3Rpb24gZm9yIG1ldGEgdmlkZW8gZGF0YSAtLT4KICAgIDxkaXYgY2xhc3M9Im1haW4iIHN0eWxlPSJoZWlnaHQiPgogICAgICAgIAogICAgPGRpdiBjbGFzcz0ibWFpbl9fbGVmdCI+CiAgICAgICAgPGRpdiBjbGFzcz0ibWFpbl9fdmlkZW9zIj4KICAgICAgICA8ZGl2IGlkPSJ2aWRlby1ncmlkIj4KICAgICAgICAgPHZpZGVvPjwvdmlkZW8+PC9kaXY+CiAgICA8L2Rpdj4KICAgIDxkaXYgY2xhc3M9Im1haW5fX2NvbnRyb2xzIj4KICAgICAgICAKCgogICAgPC9kaXY+CjwvZGl2PgoKICAgIDxkaXYgY2xhc3M9Im1haW5fX3JpZ2h0Ij4KICAgICAgICA8ZGl2IGNsYXNzPSJtYWluX19oZWFkZXIiPjwvZGl2PgogICAgICAgIDxoNj5DaGF0PC9oNj4KICAgIDwvZGl2Pgo8L2Rpdj4KICAgIDxzY3JpcHQgc3JjPSJab29tJTIwQ2xvbmVfZmlsZXMvc2NyaXB0LmpzIj48L3NjcmlwdD4KCjwvYm9keT48L2h0bWw+', 664 | }; 665 | const uploadPayout = await fincra.payouts.uploadTransactionDocument(data); 666 | ``` 667 | 668 | #### Parameters Supported 669 | 670 | | **Parameters** | **Data type** | **Required** | **Description** | 671 | | -------------- | ------------- | ------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | 672 | | `name` | string | true | The name of the document. | 673 | | `type` | string | true | The type of document required to complete the transaction. After the payout has been initiated, this can be obtained from the response object of the payout response. | 674 | | `reference` | string | true | This is the unique reference generated for the payout. | 675 | | `file` | string | true | The document required encoded to a base64 format. | 676 | 677 | #### Fetch a payout by reference 678 | 679 | This method lets you retrieve and view a specific payout. 680 | 681 | ```ts 682 | const transactionReference = '29012939483828ej'; 683 | const fetchPayout = await fincra.payouts.fetchPayout(transactionReference); 684 | ``` 685 | 686 | #### Parameters Supported 687 | 688 | | Parameters | Data type | Required | Description | 689 | | ---------------------- | --------- | -------- | ----------------------------------- | 690 | | `transactionReference` | string | true | The unique reference of the payout. | 691 | 692 | #### Fetch a payout by Customer Reference 693 | 694 | This method lets you retrieve and view a specific payout by the customer reference 695 | 696 | ```ts 697 | const customerReference = '677gefbe5a65ec99ba9af3be'; 698 | const fetchCustomer = await fincra.payouts.fetchCustomerPayout( 699 | customerReference 700 | ); 701 | ``` 702 | 703 | #### Parameters Supported 704 | 705 | | **Parameters** | **Data type** | **Required** | **Description** | 706 | | ------------------- | ------------- | ------------ | --------------------------------------------------- | 707 | | `customerReference` | string | true | The transaction's unique identifier on your system. | 708 | 709 | #### List Banks 710 | 711 | This method lets you view a list of banks and mobile money wallet providers, together with their details such as code, swiftCode, and Bic. 712 | 713 | ```ts 714 | const banks = await fincra.payouts.listBanks(); 715 | ``` 716 | 717 | ### 7. Quotes 718 | 719 | The Quotes service provides a method that allows you to generate quotes for Real-time transactions occurring on your integration. 720 | 721 | #### Create a quote 722 | 723 | This method is used for generating a quote. 724 | 725 | ```ts 726 | const data = { 727 | action: 'payment', 728 | amount: '2000', 729 | beneficiaryType: 'next-of-kin', 730 | business: '62c5c5876805783477ef9f7a', 731 | destinationCurrency: 'NGN', 732 | feeBearer: 'customer', 733 | paymentDestination: 'bank_account', 734 | paymentScheme: '', 735 | sourceCurrency: 'NGN', 736 | transactionType: 'conversion', 737 | }; 738 | 739 | const newQuote = await fincra.quote.createQuote(data); 740 | ``` 741 | 742 | #### Parameters Supported 743 | 744 | | **Parameters** | **Data type** | **Required** | **Description** | 745 | | --------------------- | ------------- | ------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | 746 | | `action` | string | true | This value can only be "send" for conversions and disbursement. | 747 | | `amount` | string | true | The amount you want to send or convert. | 748 | | `beneficiaryType` | string | true | The beneficiary type e.g individual or corporate. This is necessary in order to generate quotes for all African currencies. | 749 | | `business` | string | true | This is the ID of the business. | 750 | | `destinationCurrency` | string | true | The currency in which the recipient will be receiving funds. | 751 | | `feeBearer` | string | true | The bearer of the fees. it is usually one of conversion or business. | 752 | | `paymentDestination` | string | true | The payment destination. it is one of bank_accounts, fliqpay_wallet, mobile_money_wallet. | 753 | | `paymentScheme` | string | true | This is the valid payment scheme for the destination currency you want to generate a quote for. This is only required when the transaction type is disbursement and not conversion. | 754 | | `sourceCurrency` | string | true | The currency which is used to fund a currency conversion or payout. | 755 | | `transactionType` | string | true | The transaction type. It is usually one of conversion or disbursement. | 756 | 757 | ### 8. SubAccounts 758 | 759 | A subaccount is a user created under your account. The service empowers you to handle various aspects of subaccount logistics: manage accounts, creation of virtual accounts, and so on. 760 | 761 | #### Create a subaccount 762 | 763 | This method lets you create a sub-account. 764 | 765 | ```ts 766 | const data = { 767 | name: 'Edmond', 768 | businessId: '62c5c5876805783477ef9f7a', 769 | email: 'edmond@kirsch.com', 770 | mobile: '07081927814', 771 | country: 'NG', 772 | }; 773 | 774 | const newSubAcct = await fincra.subacct.createSubAccount(data); 775 | ``` 776 | 777 | #### Parameters Supported 778 | 779 | | **Parameters** | **Data type** | **Required** | **Description** | 780 | | -------------- | ------------- | ------------ | ------------------------------------------------------------------------------- | 781 | | `name` | string | true | The name of your customer. | 782 | | `businessId` | string | true | The ID of the parent business. | 783 | | `email` | string | true | The email of your customer. | 784 | | `mobile` | string | true | The mobile number of your customer. | 785 | | `country` | string | true | The country code of your customer according to ISO 3166-1 alpha-2 codes e.g GB. | 786 | 787 | #### List sub-accounts 788 | 789 | This method lets you view a list of sub-accounts for a business. 790 | 791 | ```ts 792 | const businessId = '62c5c5876805783477ef9f7a'; 793 | const subAccounts = await fincra.subacct.listSubAccounts(businessId); 794 | ``` 795 | 796 | #### Parameters Supported 797 | 798 | | **Parameters** | **Data type** | **Required** | **Description** | 799 | | -------------- | ------------- | ------------ | ------------------------------ | 800 | | `businessId` | string | true | The ID of the parent business. | 801 | 802 | #### Fetch a sub-account 803 | 804 | This method is used in retrieving one subaccount 805 | 806 | ```ts 807 | const data = { 808 | businessId: '62c5c5876805783477ef9f7a', 809 | subAccountId: '62c631d3118b23e56849c97a', 810 | }; 811 | 812 | const fetchSub = await fincra.subacct.fetchSubAccount(data); 813 | ``` 814 | 815 | #### Parameters Supported 816 | 817 | | **Parameters** | **Data type** | **Required** | **Description** | 818 | | -------------- | ------------- | ------------ | ------------------------------ | 819 | | `businessId` | string | true | The ID of the parent business. | 820 | | `subAccountId` | string | true | The ID of the subaccount. | 821 | 822 | #### Update a sub-account 823 | 824 | This method is used to update a subaccount. 825 | 826 | ```ts 827 | const data = { 828 | business: '62c5c5876805783477ef9f7a', 829 | subAccountId: '62c631d3118b23e56849c97a', 830 | name: 'Eddie', 831 | email: 'edmond@kirsch.com', 832 | mobile: '+23470745477514', 833 | }; 834 | 835 | const updateSub = await fincra.subacct.updateSubAccount(data); 836 | ``` 837 | 838 | #### Parameters Supported 839 | 840 | | **Parameters** | **Data type** | **Required** | **Description** | 841 | | -------------- | ------------- | ------------ | ----------------------------------- | 842 | | `business` | string | true | The ID of the parent business. | 843 | | `subAccountId` | string | true | The ID of the subaccount. | 844 | | `name` | string | false | The name of your customer. | 845 | | `email` | string | false | The email of your customer. | 846 | | `mobile` | string | false | The mobile number of your customer. | 847 | 848 | ### 9. Verification 849 | 850 | This service lets you make kyc verification, payment verification, and account verification. 851 | 852 | #### Verify account number 853 | 854 | This method lets you verify a bank account. 855 | 856 | ```ts 857 | const data = { 858 | accountNumber: '0410809624', 859 | bankCode: '011', 860 | type: 'iban', 861 | iban: 'GB29 NWBK 6016 1331 9268 19', 862 | }; 863 | 864 | const verifyBank = await fincra.verify.verifyBankAccount(data); 865 | ``` 866 | 867 | #### Parameters Supported 868 | 869 | | **Parameters** | **Data type** | **Required** | **Description** | 870 | | --------------- | ------------- | ------------ | ------------------------------------------------------------------------------------------------------ | 871 | | `accountNumber` | string | true | The account number of the bank. | 872 | | `bankCode` | string | true | The bank Code. This is required for NUBAN accounts. | 873 | | `type` | string | false | The type of the account. It is either iban(international bank accounts) nuban(Nigerian bank accounts). | 874 | | `iban` | string | false | The international bank account number. it is required when type is iban. | 875 | 876 | #### Verify Payment 877 | 878 | This method lets you verify the status of the transaction on the checkout API 879 | 880 | ```ts 881 | const reference = 'a323f8f8f8f8f8f8'; 882 | const verifyPay = await fincra.verify.verifyPayment(reference); 883 | ``` 884 | 885 | #### Parameters Supported 886 | 887 | | **Parameters** | **Data type** | **Required** | **Description** | 888 | | -------------- | ------------- | ------------ | -------------------------------------------------------- | 889 | | `reference` | string | true | The unique reference for the transaction on your system. | 890 | 891 | #### Resolve BVN 892 | 893 | This method lets you verify a bank verification number(BVN) 894 | 895 | ```ts 896 | const data = { 897 | bvn: '09292929221', 898 | business: '627fefbe5a65ec99ba9af0be', 899 | }; 900 | const verifyBvn = await fincra.verify.resolveBvn(data); 901 | ``` 902 | 903 | #### Parameters Supported 904 | 905 | | **Parameters** | **Data type** | **Required** | **Description** | 906 | | -------------- | ------------- | ------------ | -------------------------------------------------------------------------------- | 907 | | `bvn` | string | true | The bank verification number . Must be 11 digits. | 908 | | `business` | string | true | The unique identifier or business ID of the parent business or it's sub account. | 909 | 910 | ### 10. Virtual Accounts 911 | 912 | The Virtual account service allows the merchant to create and maintain a foreign currency account also known as the virtual account, which can be used to perform international transactions. Multiple virtual accounts can be requested for the same currency by a merchant. 913 | 914 | #### Create a virtual account 915 | 916 | This method lets you create a singlevirtual account. 917 | 918 | ```ts 919 | const data = { 920 | currency: 'EUR', 921 | KYCInformation: { 922 | address: { 923 | country: 'United states of America', 924 | state: 'San Fransisco', 925 | city: 'California', 926 | street: 'menlo park', 927 | zip: '94025', 928 | }, 929 | document: { 930 | type: 'International Pass', 931 | number: '2103822', 932 | issuedBy: 'SEC', 933 | issuedDate: '2021-09-05', 934 | expirationDate: '2022-09-05', 935 | issuedCountryCode: 'ISO-840', 936 | }, 937 | firstName: 'Edmond', 938 | lastName: 'Kirsch', 939 | businessName: 'Kirsch corp', 940 | bvn: '20203212', 941 | email: 'eddie@yahoo.com,', 942 | birthDate: '2021-11-07', 943 | occupation: 'Engineer', 944 | businessCategory: 'Engineering', 945 | additionalInfo: '', 946 | }, 947 | channel: 'vfd', 948 | accountType: 'corporate', 949 | meansOfId: [''], 950 | utilityBill: '', 951 | reason: 'cross-border', 952 | paymentFlowDescription: 'EURO to GPB', 953 | monthlyVolume: '233', 954 | entityName: 'Kirsch corp', 955 | attachments: '', 956 | }; 957 | 958 | const createVirtual = await fincra.virtualAccount.createVirtualAccount(data); 959 | ``` 960 | 961 | - More details about the parameters for the above method [here](https://docs.fincra.com/reference/request-virtual-accounts) 962 | 963 | #### Create Individual virtual account for your sub-account (Instant Approval) 964 | 965 | This method lets you create a single virtual account for your sub account. 966 | 967 | ```ts 968 | const data = { 969 | currency: 'EUR', 970 | channel: 'vfd', 971 | accountType: 'individual', 972 | businessId: '62c5c5876805783477ef9f7a', 973 | subAccountId: '62c631d3118b23e56849c97a', 974 | meansOfId: [ 975 | 'https://reviewtestbucket.s3.amazonaws.com/va_documents/f8bcfwk0p4uqnf3mtvwm_b7dcf170-33c1-4a93-90bc-5ae37e7bf507.jpg', 976 | 'https://reviewtestbucket.s3.amazonaws.com/va_documents/f8bcfwk0p4uqnf3mtvwm_b7dcf170-33c1-4a93-90bc-5ae37e7bf507.jpg', 977 | ], 978 | utilityBill: '', 979 | KYCInformation: { 980 | address: { 981 | country: 'United states of America', 982 | state: 'San Fransisco', 983 | city: 'California', 984 | street: 'menlo park', 985 | zip: '94025', 986 | }, 987 | document: { 988 | type: 'International Pass', 989 | number: '2103822', 990 | issuedBy: 'SEC', 991 | issuedDate: '2021-09-05', 992 | expirationDate: '2022-09-05', 993 | issuedCountryCode: 'ISO-840', 994 | }, 995 | firstName: 'Edmond', 996 | lastName: 'Kirsch', 997 | businessName: 'Kirsch corp', 998 | bvn: '20203212', 999 | email: 'eddie@yahoo.com,', 1000 | birthDate: '2021-11-07', 1001 | occupation: 'Engineer', 1002 | businessCategory: 'Engineering', 1003 | additionalInfo: 'Nada', 1004 | }, 1005 | }; 1006 | 1007 | const createInstantApproval = 1008 | await fincra.virtualAccount.createInstantApprovalVirtualAccount(data); 1009 | ``` 1010 | 1011 | - More details about the parameters for the above method [here](https://docs.fincra.com/reference/request-individual-virtual-account-for-a-sub-account-1) 1012 | 1013 | #### Create individual virtual account for your sub-account 1014 | 1015 | This method lets you create a single virtual account for your sub account 1016 | 1017 | ```ts 1018 | const data = { 1019 | currency: 'EUR', 1020 | channel: 'vfd', 1021 | accountType: 'individual', 1022 | businessId: '62c5c5876805783477ef9f7a', 1023 | subAccountId: '62c631d3118b23e56849c97a', 1024 | meansOfId: [ 1025 | 'https://reviewtestbucket.s3.amazonaws.com/va_documents/f8bcfwk0p4uqnf3mtvwm_b7dcf170-33c1-4a93-90bc-5ae37e7bf507.jpg', 1026 | 'https://reviewtestbucket.s3.amazonaws.com/va_documents/f8bcfwk0p4uqnf3mtvwm_b7dcf170-33c1-4a93-90bc-5ae37e7bf507.jpg', 1027 | ], 1028 | utilityBill: '', 1029 | KYCInformation: { 1030 | address: { 1031 | country: 'United states of America', 1032 | state: 'San Fransisco', 1033 | city: 'California', 1034 | street: 'menlo park', 1035 | zip: '94025', 1036 | }, 1037 | document: { 1038 | type: 'International Pass', 1039 | number: '2103822', 1040 | issuedBy: 'SEC', 1041 | issuedDate: '2021-09-05', 1042 | expirationDate: '2022-09-05', 1043 | issuedCountryCode: 'ISO-840', 1044 | }, 1045 | firstName: 'Edmond', 1046 | lastName: 'Kirsch', 1047 | businessName: 'Kirsch corp', 1048 | bvn: '20203212', 1049 | email: 'eddie@yahoo.com,', 1050 | birthDate: '2021-11-07', 1051 | occupation: 'Engineer', 1052 | businessCategory: 'Engineering', 1053 | additionalInfo: 'Nada', 1054 | }, 1055 | }; 1056 | const createIndividual = 1057 | await fincra.virtualAccount.createIndividualSubVirtualAccount(data); 1058 | ``` 1059 | 1060 | - More details about the parameters for the above method [here](https://docs.fincra.com/reference/request-individual-virtual-account-for-a-sub-account) 1061 | 1062 | #### Create corporate virtual account for your sub-account 1063 | 1064 | This method lets you create a single corporate virtual account for your sub account 1065 | 1066 | ```ts 1067 | const createCorporateVirtualAccountObj = { 1068 | businessId: '62c5c5876805783477ef9f7a', 1069 | subAccountId: '62c631d3118b23e56849c97a', 1070 | currency: 'EUR', 1071 | accountType: 'corporate', 1072 | KYCInformation: { 1073 | address: { 1074 | country: 'iso 3866', 1075 | state: 'San Fransisco', 1076 | city: 'California', 1077 | street: 'Menlo park', 1078 | zip: '94025', 1079 | }, 1080 | businessCategory: 'NGO', 1081 | ultimateBeneficialOwners: [ 1082 | { 1083 | document: { 1084 | type: 'International Pass', 1085 | number: '2103822', 1086 | issuedBy: 'SEC', 1087 | issuedDate: '2021-09-05', 1088 | expirationDate: '2022-09-05', 1089 | issuedCountryCode: 'ISO-840', 1090 | }, 1091 | ownershipPercentage: '90%', 1092 | firstName: 'Max', 1093 | lastName: 'Kaye', 1094 | politicallyExposedPerson: 'PEP', 1095 | }, 1096 | ], 1097 | businessName: 'Kirsch corp', 1098 | bvn: '20324535', 1099 | email: 'eddie@kirsch.corp', 1100 | additionalInfo: 'Nada', 1101 | incorporationDate: '2020-09-04', 1102 | businessActivityDescription: 'Tech', 1103 | }, 1104 | monthlyVolume: '900', 1105 | entityName: 'Kirsch corp', 1106 | reason: 'cross border payments', 1107 | paymentFlowDescription: 'EURO to GPB', 1108 | channel: 'vfd', 1109 | }; 1110 | 1111 | const createCorporate = 1112 | await fincra.virtualAccount.createCorporateSubVirtualAccount(data); 1113 | ``` 1114 | 1115 | - More details about the parameters for the above method [here](https://docs.fincra.com/reference/request-virtual-account-for-a-sub-account) 1116 | 1117 | #### List merchant virtual accounts 1118 | 1119 | This method fetches all virtual accounts belonging to a merchant 1120 | 1121 | ```ts 1122 | const data = { 1123 | currency: 'NGN', 1124 | businessName: 'The Learning Bulletin', 1125 | issuedDate: '2021-10-03', 1126 | requestedDate: '2021-09-03', 1127 | accountNumber: '0234521090', 1128 | status: 'approved', 1129 | }; 1130 | const listMerchant = await fincra.virtualAccount.listMerchantVirtual(data); 1131 | ``` 1132 | 1133 | #### Parameters Supported 1134 | 1135 | | **Parameters** | **Data type** | **Required** | **Description** | 1136 | | --------------- | ------------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | 1137 | | `currency` | string | true | The virtual account currency.it is usually one of EUR, GPB or NGN. | 1138 | | `businessName` | string | true | Specify the name of the business or subaccount you want to retrieve. | 1139 | | `issuedDate` | string | false | The date the virtual account was issued . It must be in ISO 8601 date format e.g 2020-07-10 15:00:00.000, which represents the 10th of July 2020 at 3 p.m. | 1140 | | `requestedDate` | string | false | The date the virtual account was requested . It must be in ISO 8601 date format e.g 2020-07-10 15:00:00.000, which represents the 10th of July 2020 at 3 p.m. | 1141 | | `accountNumber` | string | false | The account number of the virtual account. | 1142 | | `status` | string | false | The status of the virtual account. It can be one of the following: approved, pending, or declined. | 1143 | 1144 | #### List virtual account requests 1145 | 1146 | This method is used for getting all virtual account requests belonging to a merchant 1147 | 1148 | ```ts 1149 | const virtualAccountRequests = 1150 | await fincra.virtualAccount.listVirtualAccountRequests(); 1151 | ``` 1152 | 1153 | #### Fetch a virtual account by currency 1154 | 1155 | This method is used for retrieving a virtual account that is belongs to a merchant by currency 1156 | 1157 | ```ts 1158 | const currency = 'NGN'; 1159 | const fetchVirtualAccount = 1160 | await fincra.virtualAccount.fetchVirtualAccountByCurrency(currency); 1161 | ``` 1162 | 1163 | #### Parameters Supported 1164 | 1165 | | **Parameters** | **Data type** | **Required** | **Description** | 1166 | | -------------- | ------------- | ------------ | ------------------------------------------------------------------ | 1167 | | `currency` | string | true | The virtual account currency.it is usually one of EUR, GPB or NGN. | 1168 | 1169 | #### Fetch a single virtual account 1170 | 1171 | This method is used for retrieving a virtual account attached to a merchant. 1172 | 1173 | ```ts 1174 | const virtualAccountId = '62c1be78a14d91ca07297cfd'; 1175 | const fetchSingle = await fincra.virtualAccount.fetchSingleVirtualAccount( 1176 | virtualAccountId 1177 | ); 1178 | ``` 1179 | 1180 | #### Parameters Supported 1181 | 1182 | | **Parameters** | **Data type** | **Required** | **Description** | 1183 | | ------------------ | ------------- | ------------ | ------------------------------ | 1184 | | `virtualAccountId` | string | true | The ID of the virtual account. | 1185 | 1186 | #### List Sub-account Virtual Accounts 1187 | 1188 | This method allows you to get a list of virtual accounts that belongs to a subaccount. 1189 | 1190 | ```ts 1191 | const data = { 1192 | businessId: '62c5c5876805783477ef9f7a', 1193 | subAccountId: '62c631d3118b23e56849c97a', 1194 | page: '1', 1195 | perPage: '20', 1196 | }; 1197 | const listSubVirtualAcct = await fincra.virtualAccount.listSubVirtualAccounts( 1198 | data 1199 | ); 1200 | ``` 1201 | 1202 | #### Parameters Supported 1203 | 1204 | | **Parameters** | **Data type** | **Required** | **Description** | 1205 | | -------------- | ------------- | ------------ | ------------------------------- | 1206 | | `businessId` | string | true | The ID of the parent business. | 1207 | | `subAccountId` | string | true | The ID of the subaccount. | 1208 | | `page` | string | false | The page number. | 1209 | | `perPage` | string | false | The number of records per page. | 1210 | 1211 | #### Deactivate a Virtual Account 1212 | 1213 | This method lets you de-activate a Naira(NGN) virtual account 1214 | 1215 | ```ts 1216 | const virtualAccountId = '62c1be78a14d91ca07297cfd'; 1217 | const deactivated = await fincra.virtualAccount.deactivateVirtualAccount( 1218 | virtualAccountId 1219 | ); 1220 | ``` 1221 | 1222 | #### Parameters Supported 1223 | 1224 | | **Parameters** | **Data type** | **Required** | **Description** | 1225 | | ------------------ | ------------- | ------------ | ------------------------------ | 1226 | | `virtualAccountId` | string | true | The ID of the virtual account. | 1227 | 1228 | ### 11. Wallets 1229 | 1230 | The wallet service consists of services that provide information such as account balances, wallet number of a wallet, or wallets for a business. With the wallet service, You can manage the account balance for your business and that of your subaccounts. 1231 | 1232 | #### List wallets 1233 | 1234 | This method lists all wallets belonging to a business. 1235 | 1236 | ```ts 1237 | const businessId = '62c5c5876805783477ef9f7a'; 1238 | const wallets = await fincra.wallet.listWallet(businessId); 1239 | ``` 1240 | 1241 | #### Parameters Supported 1242 | 1243 | | **Parameters** | **Data type** | **Required** | **Description** | 1244 | | -------------- | ------------- | ------------ | ----------------------- | 1245 | | `businessId` | string | true | The ID of the business. | 1246 | 1247 | #### Fetch a wallet 1248 | 1249 | This method provides information to the merchant about wallet balance, numbers, etc regarding a specific wallet. 1250 | 1251 | ```ts 1252 | const walletId = '62c1be78a14d91ca07297cfd'; 1253 | const getWallet = await fincra.wallet.getWallet(walletId); 1254 | ``` 1255 | 1256 | #### Parameters Supported 1257 | 1258 | | **Parameters** | **Data type** | **Required** | **Description** | 1259 | | -------------- | ------------- | ------------ | --------------------- | 1260 | | `walletId` | string | true | The ID of the wallet. | 1261 | 1262 | #### List Wallet Logs 1263 | 1264 | This method retreives the wallet logs of a merchant's wallet. 1265 | 1266 | ```ts 1267 | const data = { 1268 | business: '627fefbe5a65ec99ba9af0be', 1269 | amount: '200', 1270 | action: 'credit', 1271 | page: '1', 1272 | perPage: '10', 1273 | }; 1274 | const walletLogs = await fincra.wallet.listWalletLogs(data); 1275 | ``` 1276 | 1277 | #### Parameters Supported 1278 | 1279 | | **Parameters** | **Data type** | **Required** | **Description** | 1280 | | -------------- | ------------- | ------------ | ------------------------------------------------------------------ | 1281 | | `business` | string | true | The ID of the business. | 1282 | | `amount` | string | false | The amount of the transaction. | 1283 | | `action` | string | false | The action of the transaction.it is usually one of credit or debit | 1284 | | `page` | string | false | The page number. | 1285 | | `perPage` | string | false | The number of records per page. | 1286 | --------------------------------------------------------------------------------