/** * This file defines most of the API calls offered * by Nexus and Sandbox. They don't have state, * therefore got moved away from libeufin.ts where * the services get actually started and managed. */ import { URL } from "@gnu-taler/taler-util"; import { createPlatformHttpLib, makeBasicAuthHeader, } from "@gnu-taler/taler-util/http"; import { LibeufinNexusTransactions, LibeufinSandboxAdminBankAccountBalance, NexusBankConnections, NexusFacadeListResponse, NexusGetPermissionsResponse, NexusNewTransactionsInfo, NexusTask, NexusTaskCollection, NexusUserResponse, } from "./libeufin.js"; export interface LibeufinSandboxServiceInterface { baseUrl: string; } export interface LibeufinNexusServiceInterface { baseUrl: string; } export interface CreateEbicsSubscriberRequest { hostID: string; userID: string; partnerID: string; systemID?: string; } export interface BankAccountInfo { iban: string; bic: string; name: string; label: string; } export interface CreateEbicsBankConnectionRequest { name: string; // connection name. ebicsURL: string; hostID: string; userID: string; partnerID: string; systemID?: string; } export interface UpdateNexusUserRequest { newPassword: string; } export interface NexusAuth { auth: { username: string; password: string; }; } export interface PostNexusTaskRequest { name: string; cronspec: string; type: string; // fetch | submit params: | { level: string; // report | statement | all rangeType: string; // all | since-last | previous-days | latest } | {}; } export interface CreateNexusUserRequest { username: string; password: string; } export interface PostNexusPermissionRequest { action: "revoke" | "grant"; permission: { subjectType: string; subjectId: string; resourceType: string; resourceId: string; permissionName: string; }; } export interface CreateAnastasisFacadeRequest { name: string; connectionName: string; accountName: string; currency: string; reserveTransferLevel: "report" | "statement" | "notification"; } export interface CreateTalerWireGatewayFacadeRequest { name: string; connectionName: string; accountName: string; currency: string; reserveTransferLevel: "report" | "statement" | "notification"; } export interface SandboxAccountTransactions { payments: { accountLabel: string; creditorIban: string; creditorBic?: string; creditorName: string; debtorIban: string; debtorBic: string; debtorName: string; amount: string; currency: string; subject: string; date: string; creditDebitIndicator: "debit" | "credit"; accountServicerReference: string; }[]; } export interface DeleteBankConnectionRequest { bankConnectionId: string; } export interface SimulateIncomingTransactionRequest { debtorIban: string; debtorBic: string; debtorName: string; /** * Subject / unstructured remittance info. */ subject: string; /** * Decimal amount without currency. */ amount: string; } export interface CreateEbicsBankAccountRequest { subscriber: { hostID: string; partnerID: string; userID: string; systemID?: string; }; // IBAN iban: string; // BIC bic: string; // human name name: string; label: string; } export interface LibeufinSandboxAddIncomingRequest { creditorIban: string; creditorBic: string; creditorName: string; debtorIban: string; debtorBic: string; debtorName: string; subject: string; amount: string; currency: string; uid: string; direction: string; } const libeufinHttpLib = createPlatformHttpLib(); /** * APIs spread across Legacy and Access, it is therefore * the "base URL" relative to which API every call addresses. */ export namespace LibeufinSandboxApi { // Creates one bank account via the Access API. // Need the /demobanks/$id/access-api as the base URL export async function createDemobankAccount( username: string, password: string, libeufinSandboxService: LibeufinSandboxServiceInterface, iban: string | null = null, ): Promise { let url = new URL("testing/register", libeufinSandboxService.baseUrl); await libeufinHttpLib.fetch(url.href, { method: "POST", body: { username: username, password: password, iban: iban, }, }); } // Need /demobanks/$id as the base URL export async function createDemobankEbicsSubscriber( req: CreateEbicsSubscriberRequest, demobankAccountLabel: string, libeufinSandboxService: LibeufinSandboxServiceInterface, username: string = "admin", password: string = "secret", ): Promise { // baseUrl should already be pointed to one demobank. let url = new URL("ebics/subscribers", libeufinSandboxService.baseUrl); await libeufinHttpLib.fetch(url.href, { method: "POST", body: { userID: req.userID, hostID: req.hostID, partnerID: req.partnerID, demobankAccountLabel: demobankAccountLabel, }, }); } export async function rotateKeys( libeufinSandboxService: LibeufinSandboxServiceInterface, hostID: string, ): Promise { const baseUrl = libeufinSandboxService.baseUrl; let url = new URL(`admin/ebics/hosts/${hostID}/rotate-keys`, baseUrl); await libeufinHttpLib.fetch(url.href, { method: "POST", body: {}, }); } export async function createEbicsHost( libeufinSandboxService: LibeufinSandboxServiceInterface, hostID: string, ): Promise { const baseUrl = libeufinSandboxService.baseUrl; let url = new URL("admin/ebics/hosts", baseUrl); await libeufinHttpLib.fetch(url.href, { method: "POST", body: { hostID, ebicsVersion: "2.5", }, headers: { Authorization: makeBasicAuthHeader("admin", "secret") }, }); } export async function createBankAccount( libeufinSandboxService: LibeufinSandboxServiceInterface, req: BankAccountInfo, ): Promise { const baseUrl = libeufinSandboxService.baseUrl; let url = new URL(`admin/bank-accounts/${req.label}`, baseUrl); await libeufinHttpLib.fetch(url.href, { method: "POST", body: req, headers: { Authorization: makeBasicAuthHeader("admin", "secret") }, }); } /** * This function is useless. It creates a Ebics subscriber * but never gives it a bank account. To be removed */ export async function createEbicsSubscriber( libeufinSandboxService: LibeufinSandboxServiceInterface, req: CreateEbicsSubscriberRequest, ): Promise { const baseUrl = libeufinSandboxService.baseUrl; let url = new URL("admin/ebics/subscribers", baseUrl); await libeufinHttpLib.fetch(url.href, { method: "POST", body: req, headers: { Authorization: makeBasicAuthHeader("admin", "secret") }, }); } /** * Create a new bank account and associate it to * a existing EBICS subscriber. */ export async function createEbicsBankAccount( libeufinSandboxService: LibeufinSandboxServiceInterface, req: CreateEbicsBankAccountRequest, ): Promise { const baseUrl = libeufinSandboxService.baseUrl; let url = new URL("admin/ebics/bank-accounts", baseUrl); await libeufinHttpLib.fetch(url.href, { method: "POST", body: req, headers: { Authorization: makeBasicAuthHeader("admin", "secret") }, }); } export async function simulateIncomingTransaction( libeufinSandboxService: LibeufinSandboxServiceInterface, accountLabel: string, req: SimulateIncomingTransactionRequest, ): Promise { const baseUrl = libeufinSandboxService.baseUrl; let url = new URL( `admin/bank-accounts/${accountLabel}/simulate-incoming-transaction`, baseUrl, ); await libeufinHttpLib.fetch(url.href, { method: "POST", body: req, headers: { Authorization: makeBasicAuthHeader("admin", "secret") }, }); } export async function getAccountTransactions( libeufinSandboxService: LibeufinSandboxServiceInterface, accountLabel: string, ): Promise { const baseUrl = libeufinSandboxService.baseUrl; let url = new URL( `admin/bank-accounts/${accountLabel}/transactions`, baseUrl, ); const res = await libeufinHttpLib.fetch(url.href, { headers: { Authorization: makeBasicAuthHeader("admin", "secret") }, }); return (await res.json()) as SandboxAccountTransactions; } export async function getCamt053( libeufinSandboxService: LibeufinSandboxServiceInterface, accountLabel: string, ): Promise { const baseUrl = libeufinSandboxService.baseUrl; let url = new URL("admin/payments/camt", baseUrl); return await libeufinHttpLib.fetch(url.href, { method: "POST", headers: { Authorization: makeBasicAuthHeader("admin", "secret") }, body: { bankaccount: accountLabel, type: 53, }, }); } export async function getAccountInfoWithBalance( libeufinSandboxService: LibeufinSandboxServiceInterface, accountLabel: string, ): Promise { const baseUrl = libeufinSandboxService.baseUrl; let url = new URL(`admin/bank-accounts/${accountLabel}`, baseUrl); const res = await libeufinHttpLib.fetch(url.href, { headers: { Authorization: makeBasicAuthHeader("admin", "secret") }, }); return res.json(); } } export namespace LibeufinNexusApi { export async function getAllConnections( nexus: LibeufinNexusServiceInterface, ): Promise { let url = new URL("bank-connections", nexus.baseUrl); const res = await libeufinHttpLib.fetch(url.href, { headers: { Authorization: makeBasicAuthHeader("admin", "secret") }, }); return res.json(); } export async function deleteBankConnection( libeufinNexusService: LibeufinNexusServiceInterface, req: DeleteBankConnectionRequest, ): Promise { const baseUrl = libeufinNexusService.baseUrl; let url = new URL("bank-connections/delete-connection", baseUrl); await libeufinHttpLib.fetch(url.href, { method: "POST", headers: { Authorization: makeBasicAuthHeader("admin", "secret") }, body: req, }); } export async function createEbicsBankConnection( libeufinNexusService: LibeufinNexusServiceInterface, req: CreateEbicsBankConnectionRequest, ): Promise { const baseUrl = libeufinNexusService.baseUrl; let url = new URL("bank-connections", baseUrl); await libeufinHttpLib.fetch(url.href, { method: "POST", headers: { Authorization: makeBasicAuthHeader("admin", "secret") }, body: { source: "new", type: "ebics", name: req.name, data: { ebicsURL: req.ebicsURL, hostID: req.hostID, userID: req.userID, partnerID: req.partnerID, systemID: req.systemID, }, }, }); } export async function getBankAccount( libeufinNexusService: LibeufinNexusServiceInterface, accountName: string, ): Promise { const baseUrl = libeufinNexusService.baseUrl; let url = new URL(`bank-accounts/${accountName}`, baseUrl); const resp = await libeufinHttpLib.fetch(url.href, { headers: { Authorization: makeBasicAuthHeader("admin", "secret") }, }); return resp.json(); } export async function submitInitiatedPayment( libeufinNexusService: LibeufinNexusServiceInterface, accountName: string, paymentId: string, ): Promise { const baseUrl = libeufinNexusService.baseUrl; let url = new URL( `bank-accounts/${accountName}/payment-initiations/${paymentId}/submit`, baseUrl, ); await libeufinHttpLib.fetch(url.href, { method: "POST", headers: { Authorization: makeBasicAuthHeader("admin", "secret") }, body: {}, }); } export async function fetchAccounts( libeufinNexusService: LibeufinNexusServiceInterface, connectionName: string, ): Promise { const baseUrl = libeufinNexusService.baseUrl; let url = new URL( `bank-connections/${connectionName}/fetch-accounts`, baseUrl, ); await libeufinHttpLib.fetch(url.href, { method: "POST", headers: { Authorization: makeBasicAuthHeader("admin", "secret") }, body: {}, }); } export async function importConnectionAccount( libeufinNexusService: LibeufinNexusServiceInterface, connectionName: string, offeredAccountId: string, nexusBankAccountId: string, ): Promise { const baseUrl = libeufinNexusService.baseUrl; let url = new URL( `bank-connections/${connectionName}/import-account`, baseUrl, ); await libeufinHttpLib.fetch(url.href, { method: "POST", headers: { Authorization: makeBasicAuthHeader("admin", "secret") }, body: { offeredAccountId, nexusBankAccountId, }, }); } export async function connectBankConnection( libeufinNexusService: LibeufinNexusServiceInterface, connectionName: string, ): Promise { const baseUrl = libeufinNexusService.baseUrl; let url = new URL(`bank-connections/${connectionName}/connect`, baseUrl); await libeufinHttpLib.fetch(url.href, { method: "POST", headers: { Authorization: makeBasicAuthHeader("admin", "secret") }, body: {}, }); } export async function getPaymentInitiations( libeufinNexusService: LibeufinNexusServiceInterface, accountName: string, username: string = "admin", password: string = "test", ): Promise { const baseUrl = libeufinNexusService.baseUrl; let url = new URL( `/bank-accounts/${accountName}/payment-initiations`, baseUrl, ); let response = await libeufinHttpLib.fetch(url.href, { headers: { Authorization: makeBasicAuthHeader("admin", "secret") }, }); const respJson = await response.json(); console.log( `Payment initiations of: ${accountName}`, JSON.stringify(respJson, null, 2), ); } // Uses the Anastasis API to get a list of transactions. export async function getAnastasisTransactions( libeufinNexusService: LibeufinNexusServiceInterface, anastasisBaseUrl: string, // FIXME: Nail down type! params: {}, // of the request: {delta: 5, ..} username: string = "admin", password: string = "test", ): Promise { let url = new URL("history/incoming", anastasisBaseUrl); for (const [k, v] of Object.entries(params)) { url.searchParams.set(k, String(v)); } let response = await libeufinHttpLib.fetch(url.href, { headers: { Authorization: makeBasicAuthHeader("admin", "secret") }, }); return response.json(); } // FIXME: this function should return some structured // object that represents a history. export async function getAccountTransactions( libeufinNexusService: LibeufinNexusServiceInterface, accountName: string, username: string = "admin", password: string = "test", ): Promise { const baseUrl = libeufinNexusService.baseUrl; let url = new URL(`/bank-accounts/${accountName}/transactions`, baseUrl); let response = await libeufinHttpLib.fetch(url.href, { headers: { Authorization: makeBasicAuthHeader("admin", "secret") }, }); return response.json(); } export async function fetchTransactions( libeufinNexusService: LibeufinNexusServiceInterface, accountName: string, rangeType: string = "all", level: string = "report", username: string = "admin", password: string = "test", ): Promise { const baseUrl = libeufinNexusService.baseUrl; let url = new URL( `/bank-accounts/${accountName}/fetch-transactions`, baseUrl, ); const resp = await libeufinHttpLib.fetch(url.href, { method: "POST", headers: { Authorization: makeBasicAuthHeader("admin", "secret") }, body: { rangeType: rangeType, level: level, }, }); return resp.json(); } export async function changePassword( libeufinNexusService: LibeufinNexusServiceInterface, username: string, req: UpdateNexusUserRequest, auth: NexusAuth, ): Promise { const baseUrl = libeufinNexusService.baseUrl; let url = new URL(`/users/${username}/password`, baseUrl); await libeufinHttpLib.fetch(url.href, { method: "POST", headers: { Authorization: makeBasicAuthHeader("admin", "secret") }, body: req, }); } export async function getUser( libeufinNexusService: LibeufinNexusServiceInterface, auth: NexusAuth, ): Promise { const baseUrl = libeufinNexusService.baseUrl; let url = new URL(`/user`, baseUrl); const resp = await libeufinHttpLib.fetch(url.href, { headers: { Authorization: makeBasicAuthHeader("admin", "secret") }, }); return resp.json(); } export async function createUser( libeufinNexusService: LibeufinNexusServiceInterface, req: CreateNexusUserRequest, ): Promise { const baseUrl = libeufinNexusService.baseUrl; let url = new URL(`/users`, baseUrl); await libeufinHttpLib.fetch(url.href, { method: "POST", headers: { Authorization: makeBasicAuthHeader("admin", "secret") }, body: req, }); } export async function getAllPermissions( libeufinNexusService: LibeufinNexusServiceInterface, ): Promise { const baseUrl = libeufinNexusService.baseUrl; let url = new URL(`/permissions`, baseUrl); const resp = await libeufinHttpLib.fetch(url.href, { headers: { Authorization: makeBasicAuthHeader("admin", "secret") }, }); return resp.json(); } export async function postPermission( libeufinNexusService: LibeufinNexusServiceInterface, req: PostNexusPermissionRequest, ): Promise { const baseUrl = libeufinNexusService.baseUrl; let url = new URL(`/permissions`, baseUrl); await libeufinHttpLib.fetch(url.href, { method: "POST", headers: { Authorization: makeBasicAuthHeader("admin", "secret") }, body: req, }); } export async function getAllTasks( libeufinNexusService: LibeufinNexusServiceInterface, bankAccountName: string, ): Promise { const baseUrl = libeufinNexusService.baseUrl; let url = new URL(`/bank-accounts/${bankAccountName}/schedule`, baseUrl); const resp = await libeufinHttpLib.fetch(url.href, { headers: { Authorization: makeBasicAuthHeader("admin", "secret") }, }); return resp.json(); } export async function getTask( libeufinNexusService: LibeufinNexusServiceInterface, bankAccountName: string, // When void, the request returns the list of all the // tasks under this bank account. taskName: string, ): Promise { const baseUrl = libeufinNexusService.baseUrl; let url = new URL( `/bank-accounts/${bankAccountName}/schedule/${taskName}`, baseUrl, ); if (taskName) url = new URL(taskName, `${url.href}/`); const resp = await libeufinHttpLib.fetch(url.href, { headers: { Authorization: makeBasicAuthHeader("admin", "secret") }, }); return resp.json(); } export async function deleteTask( libeufinNexusService: LibeufinNexusServiceInterface, bankAccountName: string, taskName: string, ): Promise { const baseUrl = libeufinNexusService.baseUrl; let url = new URL( `/bank-accounts/${bankAccountName}/schedule/${taskName}`, baseUrl, ); await libeufinHttpLib.fetch(url.href, { method: "DELETE", headers: { Authorization: makeBasicAuthHeader("admin", "secret") }, }); } export async function postTask( libeufinNexusService: LibeufinNexusServiceInterface, bankAccountName: string, req: PostNexusTaskRequest, ): Promise { const baseUrl = libeufinNexusService.baseUrl; let url = new URL(`/bank-accounts/${bankAccountName}/schedule`, baseUrl); await libeufinHttpLib.fetch(url.href, { method: "POST", headers: { Authorization: makeBasicAuthHeader("admin", "secret") }, body: req, }); } export async function deleteFacade( libeufinNexusService: LibeufinNexusServiceInterface, facadeName: string, ): Promise { const baseUrl = libeufinNexusService.baseUrl; let url = new URL(`facades/${facadeName}`, baseUrl); await libeufinHttpLib.fetch(url.href, { method: "DELETE", headers: { Authorization: makeBasicAuthHeader("admin", "secret") }, }); } export async function getAllFacades( libeufinNexusService: LibeufinNexusServiceInterface, ): Promise { const baseUrl = libeufinNexusService.baseUrl; let url = new URL("facades", baseUrl); const resp = await libeufinHttpLib.fetch(url.href, { headers: { Authorization: makeBasicAuthHeader("admin", "secret") }, }); // FIXME: Just return validated, typed response here! return resp.json(); } export async function createAnastasisFacade( libeufinNexusService: LibeufinNexusServiceInterface, req: CreateAnastasisFacadeRequest, ): Promise { const baseUrl = libeufinNexusService.baseUrl; let url = new URL("facades", baseUrl); await libeufinHttpLib.fetch(url.href, { method: "POST", headers: { Authorization: makeBasicAuthHeader("admin", "secret") }, body: { name: req.name, type: "anastasis", config: { bankAccount: req.accountName, bankConnection: req.connectionName, currency: req.currency, reserveTransferLevel: req.reserveTransferLevel, }, }, }); } export async function createTwgFacade( libeufinNexusService: LibeufinNexusServiceInterface, req: CreateTalerWireGatewayFacadeRequest, ): Promise { const baseUrl = libeufinNexusService.baseUrl; let url = new URL("facades", baseUrl); await libeufinHttpLib.fetch(url.href, { method: "POST", headers: { Authorization: makeBasicAuthHeader("admin", "secret") }, body: { name: req.name, type: "taler-wire-gateway", config: { bankAccount: req.accountName, bankConnection: req.connectionName, currency: req.currency, reserveTransferLevel: req.reserveTransferLevel, }, }, }); } export async function submitAllPaymentInitiations( libeufinNexusService: LibeufinNexusServiceInterface, accountId: string, ): Promise { const baseUrl = libeufinNexusService.baseUrl; let url = new URL( `/bank-accounts/${accountId}/submit-all-payment-initiations`, baseUrl, ); await libeufinHttpLib.fetch(url.href, { method: "POST", headers: { Authorization: makeBasicAuthHeader("admin", "secret") }, body: {}, }); } }