@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-typescriptAPI 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: Create → Redirect → Callback → Poll → Result.
- Create – Create payment link via API, get
urlandpayment_link_id. Save both andunique_reference_id. - Redirect – Redirect user to the payment link URL (use HTTP 303 so the redirect is followed with GET).
- Callback – Finverse redirects back with
payment_link_idandunique_reference_idquery params. Verify they match what you stored. - Poll – Poll
GET /payment_links/{paymentLinkId}every 2 seconds for up to 30 seconds untilsession_statusis"COMPLETE". - Result – If
COMPLETE→ success; otherwise → error.
Security:
FINVERSE_CLIENT_IDandFINVERSE_CLIENT_SECRETmust 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 verificationSetup 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 pageData 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 .dataUse 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_secretto 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.envand 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
