Files
finanzen/convex/bank/fintsSession.ts

162 lines
4.4 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"use node";
import {
FinTSClient,
FinTSConfig,
type BankingInformation,
type ClientResponse,
} from "lib-fints";
import { resolveFintsEnvFields } from "./fintsConfig";
export type FintsInteractionKind =
| { type: "sync"; syncSystemId: boolean }
| { type: "balance"; accountNumber: string }
| { type: "statements"; accountNumber: string; from?: string; to?: string; preferCamt: boolean };
export type SerializedFintsSession = {
bankingInformation: BankingInformation;
tanMethodId?: number;
tanMediaName?: string;
tanReference: string;
tanContinuation: "sync" | "balance" | "statements";
};
export type FintsEnvConfig = {
productId: string;
productVersion: string;
url: string;
blz: string;
login: string;
pin: string;
tanMethodId?: number;
tanMediaName?: string;
bankingInformation?: BankingInformation;
};
/** @deprecated Import from ./fintsConfig */
export { FINTS_PRODUCT_ID_PLACEHOLDER, getFintsConfigStatus } from "./fintsConfig";
export type { FintsConfigStatus } from "./fintsConfig";
export function resolveFintsEnv(overrides?: {
blz?: string;
url?: string;
login?: string;
productId?: string;
productVersion?: string;
tanMethodId?: number;
tanMediaName?: string;
bankingInformationJson?: string;
pin?: string;
}): FintsEnvConfig {
const fields = resolveFintsEnvFields(overrides);
if (fields.usesProductIdPlaceholder) {
console.warn("[fints] FINTS_PRODUCT_ID fehlt Platzhalter wird verwendet");
}
let bankingInformation: BankingInformation | undefined;
if (overrides?.bankingInformationJson) {
bankingInformation = JSON.parse(overrides.bankingInformationJson) as BankingInformation;
}
return {
productId: fields.productId,
productVersion: fields.productVersion,
url: fields.url,
blz: fields.blz,
login: fields.login,
pin: fields.pin,
tanMethodId: overrides?.tanMethodId,
tanMediaName: overrides?.tanMediaName,
bankingInformation,
};
}
export function createFinTsClient(config: FintsEnvConfig): FinTSClient {
const fintsConfig = config.bankingInformation
? FinTSConfig.fromBankingInformation(
config.productId,
config.productVersion,
config.bankingInformation,
config.login,
config.pin,
config.tanMethodId,
config.tanMediaName,
)
: FinTSConfig.forFirstTimeUse(
config.productId,
config.productVersion,
config.url,
config.blz,
config.login,
config.pin,
);
const client = new FinTSClient(fintsConfig);
if (config.tanMethodId) {
client.selectTanMethod(config.tanMethodId);
}
if (config.tanMediaName) {
client.selectTanMedia(config.tanMediaName);
}
return client;
}
export function buildSessionSnapshot(
client: FinTSClient,
tanReference: string,
tanContinuation: SerializedFintsSession["tanContinuation"],
): SerializedFintsSession {
return {
bankingInformation: client.config.bankingInformation,
tanMethodId: client.config.selectedTanMethod?.id,
tanMediaName: client.config.selectedTanMethod?.activeTanMedia?.[0],
tanReference,
tanContinuation,
};
}
export async function continueWithTan(
client: FinTSClient,
session: SerializedFintsSession,
tan?: string,
): Promise<ClientResponse> {
switch (session.tanContinuation) {
case "sync":
return await client.synchronizeWithTan(session.tanReference, tan);
case "balance":
return await client.getAccountBalanceWithTan(session.tanReference, tan);
case "statements":
return await client.getAccountStatementsWithTan(session.tanReference, tan);
}
}
export function encodePhotoTan(response: ClientResponse): {
mimeType?: string;
base64?: string;
} {
if (!response.tanPhoto?.image) return {};
const bytes =
response.tanPhoto.image instanceof Uint8Array
? response.tanPhoto.image
: new Uint8Array(response.tanPhoto.image);
let binary = "";
for (const byte of bytes) {
binary += String.fromCharCode(byte);
}
return {
mimeType: response.tanPhoto.mimeType,
base64: btoa(binary),
};
}
export function pickDecoupledTanMethod(client: FinTSClient): number | undefined {
const methods = client.config.availableTanMethods;
const decoupled = methods.find((m) => m.isDecoupled);
if (decoupled) return decoupled.id;
return methods[0]?.id;
}
export function sleep(ms: number): Promise<void> {
return new Promise((resolve) => setTimeout(resolve, ms));
}