npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@finverse/sdk-typescript

v0.0.378

Published

OpenAPI client for @finverse/sdk-typescript

Downloads

2,006

Readme

Finverse API - TypeScript SDK

This SDK enables end-to-end backend integration with the Finverse API, including API authentication, institution linking, data retrieval, and payment operations.

Installation

npm install @finverse/sdk-typescript

API Overview

The SDK is organized into two main API categories:

| API | Purpose | |-----|---------| | Payment API | Create payment links, mandates, payments, and manage payment accounts | | Data API | Retrieve accounts, transactions, statements, balance history, and identity data |

Both APIs share authentication and linking flows via PublicApi and LinkApi.


Payment API

The Payment API enables you to create payment links for checkout, initiate payments, manage mandates, and handle payment-related operations.

Key Classes & Methods

| Class | Key Methods | |-------|-------------| | PaymentApi | createPaymentLink, getPaymentLink, cancelPaymentLink, createScheduledPayout, getPayoutById, createPaymentMethod, createPayment, getPayment, createMandate, getMandate, authorizeMandate, createPaymentUser, createPaymentAccount, listPaymentAccounts |

Payment Flow (End-to-End)

The payment link flow: CreateRedirectCallbackPollResult.

  1. Create – Create payment link via API, get url and payment_link_id. Save both and unique_reference_id.
  2. Redirect – Redirect user to the payment link URL (use HTTP 303 so the redirect is followed with GET).
  3. Callback – Finverse redirects back with payment_link_id and unique_reference_id query params. Verify they match what you stored.
  4. Poll – Poll GET /payment_links/{paymentLinkId} every 2 seconds for up to 30 seconds until session_status is "COMPLETE".
  5. Result – If COMPLETE → success; otherwise → error.

Security: FINVERSE_CLIENT_ID and FINVERSE_CLIENT_SECRET must never be committed to git or sent to the frontend. Use them only on the server (e.g. environment variables, secrets manager).

1. Authenticate: Obtain Customer Access Token

import { Configuration, PublicApi } from '@finverse/sdk-typescript';

const apiHost = process.env.FINVERSE_BASE_URL ?? "https://api.prod.finverse.net";
const clientId = process.env.FINVERSE_CLIENT_ID;
const clientSecret = process.env.FINVERSE_CLIENT_SECRET;

const configuration = new Configuration({ basePath: apiHost });
const customerTokenResp = await new PublicApi(configuration).generateCustomerAccessToken({
  client_id: clientId,
  client_secret: clientSecret,
  grant_type: 'client_credentials',
});

const customerAccessToken = customerTokenResp.data.access_token;
// Cache the token; refresh before expiry (use expires_in minus 60s buffer)

2. Create Payment Link

Payment mode – For one-time payments. Requires amount (minor units, e.g. 10000 = 100.00 HKD), currency, sender, payment_details, and link_customizations with redirect_uri:

import { Configuration, PaymentApi } from '@finverse/sdk-typescript';
import crypto from 'crypto';

const configuration = new Configuration({
  basePath: apiHost,
  accessToken: customerAccessToken,
});
const paymentApi = new PaymentApi(configuration);

const uniqueReferenceId = crypto.randomUUID();
const callbackUrl = process.env.CALLBACK_URL; // e.g. https://yoursite.com/callback

const createResp = await paymentApi.createPaymentLink({
  mode: "PAYMENT",
  amount: 10000,  // 100.00 HKD
  currency: "HKD",
  unique_reference_id: uniqueReferenceId,
  sender: {
    external_user_id: "your-internal-user-id",
    name: "Customer Name",
    email: "[email protected]",
  },
  payment_details: {
    description: "Order #12345",
    external_transaction_reference: "order-12345",  // max 35 chars
  },
  link_customizations: {
    ui_mode: "redirect",
    redirect_uri: callbackUrl,
  },
});

const paymentLinkId = createResp.data.payment_link_id;
const paymentUrl = createResp.data.url;

// Store paymentLinkId and uniqueReferenceId for callback verification

Setup mode – For saving payment methods (e.g. Click to Pay). Do not pass amount:

const createResp = await paymentApi.createPaymentLink({
  mode: "SETUP",
  currency: "HKD",
  unique_reference_id: uniqueReferenceId,
  sender: {
    external_user_id: "your-internal-user-id",
    name: "Customer Name",
    email: "[email protected]",
  },
  payment_details: {
    description: "Save payment method",
    external_transaction_reference: "setup-12345",
  },
  link_customizations: {
    ui_mode: "redirect",
    redirect_uri: callbackUrl,
  },
  payment_setup_options: {
    future_payments: "CLICK_TO_PAY",
  },
});

3. Redirect User to Payment URL

Redirect the user to paymentUrl. Use HTTP 303 so the browser follows with GET (Finverse expects GET):

4. Callback Handler: Poll for Completion

When Finverse redirects back, read payment_link_id and unique_reference_id from query params. Verify they match your stored values, then poll until session_status is COMPLETE:

import { Configuration, PaymentApi } from '@finverse/sdk-typescript';

const paymentLinkId = req.query.payment_link_id as string;  // from callback URL
const uniqueReferenceId = req.query.unique_reference_id as string;

// Verify uniqueReferenceId matches what you stored
// if (uniqueReferenceId !== storedUniqueReferenceId) { return error; }

const configuration = new Configuration({
  basePath: apiHost,
  accessToken: customerAccessToken,
});
const paymentApi = new PaymentApi(configuration);

const POLL_INTERVAL_MS = 2000;
const POLL_TIMEOUT_MS = 30000;
const startTime = Date.now();

while (Date.now() - startTime < POLL_TIMEOUT_MS) {
  const getResp = await paymentApi.getPaymentLink(paymentLinkId);
  const sessionStatus = getResp.data.session_status;

  if (sessionStatus === "COMPLETE") {
    // Success – redirect to success page
    // Log getResp.data.payment for payment details
    break;
  }

  await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS));
}

// If loop ended without COMPLETE → timeout, show error page

Data API

The Data API enables you to retrieve financial data: accounts, transactions, statements, balance history, identity, and income estimates.

Key Classes & Methods

| Class | Key Methods | |-------|-------------| | LoginIdentityApi | getLoginIdentity, listAccounts, getAccount, getAccountNumber, listTransactionsByLoginIdentityId, listTransactionsByAccountId, getStatements, getStatement, getBalanceHistory, getIdentity, getIncomeEstimateByLoginIdentityId, listCardDetails, refreshLoginIdentity |

Data Retrieval Flow (End-to-End)

1. Authenticate: Obtain Customer Access Token

import { Configuration, PublicApi } from '@finverse/sdk-typescript';

const apiHost = process.env.FINVERSE_BASE_URL ?? "https://api.prod.finverse.net";
const clientId = process.env.FINVERSE_CLIENT_ID;
const clientSecret = process.env.FINVERSE_CLIENT_SECRET;
const redirectUri = process.env.REDIRECT_URI;

const configuration = new Configuration({ basePath: apiHost });
const customerTokenResp = await new PublicApi(configuration).generateCustomerAccessToken({
  client_id: clientId,
  client_secret: clientSecret,
  grant_type: 'client_credentials',
});

const customerAccessToken = customerTokenResp.data.access_token;

2. Link New Institution: Obtain Link Token and Link URL

import { Configuration, LinkApi } from '@finverse/sdk-typescript';

const userId = "someUserId";
const state = "someUniqueState";
const linkConfig = new Configuration({
  basePath: apiHost,
  accessToken: customerAccessToken,
});
const linkTokenResp = await new LinkApi(linkConfig).generateLinkToken({
  client_id: clientId,
  user_id: userId,
  redirect_uri: redirectUri,
  state: state,
  response_mode: "form_post",
  response_type: "code",
  grant_type: "client_credentials",
});

console.log("linkUrl:", linkTokenResp.data.link_url);

3. Finalize Linking: Exchange Code for Login Identity Access Token

import { LinkApi, TokenGrantTypeEnum } from '@finverse/sdk-typescript';

const code = "obtainAfterLink"; // from Finverse Link UI callback
const loginIdentityTokenResp = await new LinkApi(linkConfig).token(
  TokenGrantTypeEnum.AuthorizationCode,
  code,
  clientId,
  redirectUri,
);
const loginIdentityToken = loginIdentityTokenResp.data.access_token;

4. Retrieve data: Get Login Identity

import { Configuration, LoginIdentityApi } from '@finverse/sdk-typescript';

const dataConfig = new Configuration({
  basePath: apiHost,
  accessToken: loginIdentityToken,
});
const loginIdentityResp = await new LoginIdentityApi(dataConfig).getLoginIdentity();
console.log("login identity:", loginIdentityResp.data.login_identity);

5. Poll Login Identity Status Until Ready

import type { AxiosResponse } from 'axios';
import type { GetLoginIdentityByIdResponse } from '@finverse/sdk-typescript';

enum FinalStatus {
  ERROR = 'ERROR',
  DATA_RETRIEVAL_PARTIALLY_SUCCESSFUL = 'DATA_RETRIEVAL_PARTIALLY_SUCCESSFUL',
  DATA_RETRIEVAL_COMPLETE = 'DATA_RETRIEVAL_COMPLETE',
}

let loginIdentity: AxiosResponse<GetLoginIdentityByIdResponse>;
for (let i = 0; i < 20; i++) {
  loginIdentity = await new LoginIdentityApi(dataConfig).getLoginIdentity();
  const status = loginIdentity.data.login_identity.status;
  if (
    status === FinalStatus.ERROR ||
    status === FinalStatus.DATA_RETRIEVAL_COMPLETE ||
    status === FinalStatus.DATA_RETRIEVAL_PARTIALLY_SUCCESSFUL
  ) {
    break;
  }
  await new Promise((resolve) => setTimeout(resolve, 3000));
}

6. Get Accounts

const accountsResp = await new LoginIdentityApi(dataConfig).listAccounts();
console.log("accounts:", accountsResp.data.accounts);

7. Get Transactions (with pagination)

let offset = 0;
while (true) {
  const transactionsResp = await new LoginIdentityApi(dataConfig).listTransactionsByLoginIdentityId(
    offset,
    500  // limit: default 500, max 1000
  );
  const transactions = transactionsResp.data.transactions ?? [];
  console.log(`total: ${transactionsResp.data.total_transactions}, transactions: ${transactions.length}`);
  offset += transactions.length;
  if (offset >= transactionsResp.data.total_transactions) {
    break;
  }
}

8. Get Statements

import { writeFileSync } from 'fs';

const statementsResp = await new LoginIdentityApi(dataConfig).getStatements();
const statements = statementsResp.data.statements ?? [];
console.log("statements:", statements);

// Download a statement (assuming at least one exists)
const statementId = statements[0].id;
const statementResp = await new LoginIdentityApi(dataConfig).getStatement(
  statementId,
  true,
  { responseType: "arraybuffer" }
);
writeFileSync("statement.pdf", Buffer.from(statementResp.data));

Best Practices

Use async/await

All SDK methods return Promises. Use async/await for cleaner, readable code and to avoid callback hell:

async function fetchAccounts() {
  const config = new Configuration({ basePath: apiHost, accessToken: token });
  const response = await new LoginIdentityApi(config).listAccounts();
  return response.data.accounts;
}

Handle API responses correctly

API methods return Axios AxiosResponse objects. Access the payload via .data:

const tokenResp = await new PublicApi(config).generateCustomerAccessToken({ ... });
const accessToken = tokenResp.data.access_token;  // Correct: use .data

const accountsResp = await new LoginIdentityApi(config).listAccounts();
const accounts = accountsResp.data.accounts;  // Correct: use .data

Use exported types and interfaces

Leverage the SDK's exported types for type safety and better IDE support:

import type {
  Account,
  CreatePaymentRequest,
  GetLoginIdentityByIdResponse,
} from '@finverse/sdk-typescript';

const paymentRequest: CreatePaymentRequest = {
  // ...
};

Handle errors with try/catch

Wrap API calls in try/catch blocks. API errors are returned as HTTP error responses; the SDK may throw on network failures or validation errors:

import axios from 'axios';

try {
  const response = await new LoginIdentityApi(config).listAccounts();
  return response.data.accounts;
} catch (error) {
  if (axios.isAxiosError(error) && error.response) {
    console.error("API error:", error.response.status, error.response.data);
  } else {
    throw error;
  }
}

Handle validation errors

When required parameters are missing, the SDK throws errors. Wrap API calls in try/catch to handle validation and other failures gracefully.

Reuse Configuration instances

Create a Configuration once per token/context and reuse it for multiple API calls:

import { Configuration, LinkApi, PaymentApi } from '@finverse/sdk-typescript';

const config = new Configuration({
  basePath: apiHost,
  accessToken: customerAccessToken,
});
const linkApi = new LinkApi(config);
const paymentApi = new PaymentApi(config);
const linkTokenResp = await linkApi.generateLinkToken({ ... });
const paymentResp = await paymentApi.createPayment(paymentRequest);

Client secret: never commit, server-side only

Never commit your client secret to version control. The client secret must remain confidential and be used only on the server.

  • Server-side only: This SDK is designed for backend use. Never expose client_secret to the browser, mobile apps, or any client-side code.
  • Use environment variables or a secrets manager: Load credentials at runtime from secure storage.
  • Add to .gitignore: Ensure .env and any files containing secrets are never committed.
// ✅ Correct: Load from environment (server-side only)
const clientId = process.env.FINVERSE_CLIENT_ID;
const clientSecret = process.env.FINVERSE_CLIENT_SECRET;  // Never log, never send to client
const redirectUri = process.env.REDIRECT_URI;

Payment idempotency

For payment and mandate operations, use idempotency keys to safely retry requests without creating duplicate payments. If a request fails due to network issues, you can retry with the same key—the API will return the original result instead of creating a duplicate.

Methods that support idempotency keys: createPayment, createMandate, createMandateForExistingSender, and createScheduledPayout on PaymentApi.

import { PaymentApi } from '@finverse/sdk-typescript';
import crypto from 'crypto';

// Generate a unique key per logical operation (e.g., per checkout or mandate setup)
const idempotencyKey = crypto.randomUUID();

// Safe to retry—same key returns same result
await new PaymentApi(config).createPayment(paymentRequest, idempotencyKey);
await new PaymentApi(config).createMandate(mandateRequest, idempotencyKey);

Best practices:

  • Use one idempotency key per unique payment or mandate.
  • Store the key with your order/mandate record so you can retry with the same key if needed.

Resources for AI Agents

For AI agents implementing Finverse integrations, the Finverse AI repository contains skills, implementation guides, and reference documentation for payment flows, data retrieval, and other Finverse API patterns. Follow this link for detailed integration instructions: https://github.com/finversetech/ai