taler-typescript-core

Wallet core logic and WebUIs for various components
Log | Files | Refs | Submodules | README | LICENSE

commit a3d92271706ecacf2cce2adc87d53b4b7854fde6
parent 644073c3c1c073cac22ab49e22ea96ba8598191f
Author: Florian Dold <florian@dold.me>
Date:   Thu, 26 Feb 2026 22:45:15 +0100

refactor type of getCurrentInstanceKycStatus to align with HTTP API spec

Diffstat:
Mpackages/merchant-backoffice-ui/src/Routing.tsx | 5++---
Mpackages/merchant-backoffice-ui/src/components/menu/SideBar.tsx | 5++---
Mpackages/merchant-backoffice-ui/src/hooks/instance.ts | 7+------
Mpackages/merchant-backoffice-ui/src/paths/instance/kyc/list/index.tsx | 9++++++---
Mpackages/taler-harness/src/harness/tops.ts | 3---
Mpackages/taler-harness/src/integrationtests/test-kyc-merchant-deposit-rewrite.ts | 27++++++++++++++-------------
Mpackages/taler-harness/src/integrationtests/test-merchant-bank-bad-wire-target.ts | 21++++++---------------
Mpackages/taler-harness/src/integrationtests/test-merchant-kyc-auth-multi.ts | 3---
Mpackages/taler-harness/src/integrationtests/test-tops-aml-basic.ts | 32+++++++++++++++++++-------------
Mpackages/taler-harness/src/integrationtests/test-tops-aml-custom-addr-postal.ts | 2--
Mpackages/taler-harness/src/integrationtests/test-tops-aml-custom-addr-sms.ts | 51++++++++++++++++++++++++++++++++-------------------
Mpackages/taler-harness/src/integrationtests/test-tops-merchant-tos.ts | 1-
Mpackages/taler-util/src/http-client/merchant.ts | 24+++++++++++-------------
13 files changed, 93 insertions(+), 97 deletions(-)

diff --git a/packages/merchant-backoffice-ui/src/Routing.tsx b/packages/merchant-backoffice-ui/src/Routing.tsx @@ -27,6 +27,7 @@ import { } from "@gnu-taler/taler-util"; import { NotificationCard, + NotificationCardBulma, urlPattern, useTranslationContext, } from "@gnu-taler/web-util/browser"; @@ -36,7 +37,6 @@ import { Route, Router, route } from "preact-router"; import { useEffect, useErrorBoundary, useState } from "preact/hooks"; import { Loading } from "./components/exception/loading.js"; import { Menu, NotConnectedAppMenu } from "./components/menu/index.js"; -import { NotificationCardBulma } from "@gnu-taler/web-util/browser"; import { useSessionContext } from "./context/session.js"; import { useInstanceBankAccounts } from "./hooks/bank.js"; import { useInstanceKYCDetails } from "./hooks/instance.js"; @@ -84,8 +84,8 @@ import TemplateUsePage from "./paths/instance/templates/use/index.js"; import TokenFamilyCreatePage from "./paths/instance/tokenfamilies/create/index.js"; import TokenFamilyListPage from "./paths/instance/tokenfamilies/list/index.js"; import TokenFamilyUpdatePage from "./paths/instance/tokenfamilies/update/index.js"; -import TransferListPage from "./paths/instance/transfers/list/index.js"; import TransferDetailPage from "./paths/instance/transfers/list/DetailsPage.js"; +import TransferListPage from "./paths/instance/transfers/list/index.js"; import InstanceUpdatePage, { AdminUpdate as InstanceAdminUpdatePage, Props as InstanceUpdatePageProps, @@ -953,7 +953,6 @@ function KycBanner(): VNode { kycStatus !== undefined && !(kycStatus instanceof TalerError) && kycStatus.type === "ok" && - kycStatus.body.kycRequired && kycStatus.body.kyc_data.findIndex( (d) => d.payto_kycauths !== undefined || d.access_token !== undefined, ) !== -1; diff --git a/packages/merchant-backoffice-ui/src/components/menu/SideBar.tsx b/packages/merchant-backoffice-ui/src/components/menu/SideBar.tsx @@ -28,10 +28,10 @@ import { import { useTranslationContext } from "@gnu-taler/web-util/browser"; import { ComponentChildren, Fragment, h, VNode } from "preact"; import { useSessionContext } from "../../context/session.js"; +import { useSettingsContext } from "../../context/settings.js"; import { useInstanceKYCDetailsLongPolling } from "../../hooks/instance.js"; import { UIElement, usePreference } from "../../hooks/preference.js"; import { LangSelector } from "./LangSelector.js"; -import { useSettingsContext } from "../../context/settings.js"; const TALER_SCREEN_ID = 17; // const GIT_HASH = typeof __GIT_HASH__ !== "undefined" ? __GIT_HASH__ : undefined; @@ -49,8 +49,7 @@ export function Sidebar({ mobile }: Props): VNode { const allKycData = kycStatus !== undefined && !(kycStatus instanceof TalerError) && - kycStatus.type === "ok" && - kycStatus.body.kycRequired === true + kycStatus.type === "ok" ? kycStatus.body.kyc_data : []; diff --git a/packages/merchant-backoffice-ui/src/hooks/instance.ts b/packages/merchant-backoffice-ui/src/hooks/instance.ts @@ -68,12 +68,7 @@ export function useInstanceKYCDetailsLongPolling() { const result = useLongPolling( data, (result) => { - if ( - !result || - result.type === "fail" || - !result.body.kycRequired || - !result.body.etag - ) { + if (!result || result.type === "fail" || !result.body.etag) { return undefined; } return result.body.etag; diff --git a/packages/merchant-backoffice-ui/src/paths/instance/kyc/list/index.tsx b/packages/merchant-backoffice-ui/src/paths/instance/kyc/list/index.tsx @@ -76,6 +76,12 @@ export default function ListKYC(_p: Props): VNode { case HttpStatusCode.NotFound: { return <div />; } + case HttpStatusCode.NoContent: { + return <div>no kyc required</div>; + } + case HttpStatusCode.NotModified: { + return <div />; + } default: { assertUnreachable(result); } @@ -83,9 +89,6 @@ export default function ListKYC(_p: Props): VNode { } const status = result.body; - if (!status.kycRequired) { - return <div>no kyc required</div>; - } return ( <Fragment> {showingInstructions !== undefined ? ( diff --git a/packages/taler-harness/src/harness/tops.ts b/packages/taler-harness/src/harness/tops.ts @@ -859,8 +859,6 @@ export async function doTopsKycAuth( t.assertDeepEqual(kycStatus.case, "ok"); - t.assertTrue(kycStatus.body.kycRequired === true); - if ( kycStatus.body.kyc_data[0].status === MerchantAccountKycStatus.EXCHANGE_UNREACHABLE @@ -910,7 +908,6 @@ export async function doTopsKycAuth( ); logger.info(`kyc status after transfer: ${j2s(kycStatus)}`); t.assertDeepEqual(kycStatus.case, "ok"); - t.assertTrue(kycStatus.body.kycRequired === true); t.assertDeepEqual(kycStatus.body.kyc_data[0].status, "kyc-required"); t.assertTrue(typeof kycStatus.body.kyc_data[0].access_token === "string"); accessToken = kycStatus.body.kyc_data[0].access_token as AccessToken; diff --git a/packages/taler-harness/src/integrationtests/test-kyc-merchant-deposit-rewrite.ts b/packages/taler-harness/src/integrationtests/test-kyc-merchant-deposit-rewrite.ts @@ -21,10 +21,8 @@ import { Configuration, encodeCrock, hashNormalizedPaytoUri, - isOperationOk, j2s, Logger, - MerchantAccountKycRedirectsResponse, MerchantAccountKycStatus, PaytoString, succeedOrThrow, @@ -122,13 +120,14 @@ export async function runKycMerchantDepositRewriteTest(t: GlobalTestState) { let merchantBankAccount: PaytoString | undefined; { const kycStatus = await retryUntilNonNull(async () => { - const d = await merchantApi.getCurrentInstanceKycStatus(merchantAdminAccessToken); - t.assertTrue(d.type === "ok") - t.assertTrue(d.body.kycRequired === true) + const d = await merchantApi.getCurrentInstanceKycStatus( + merchantAdminAccessToken, + ); + t.assertTrue(d.type === "ok"); if (d.body.kyc_data[0].payto_kycauths) { return d.body; } - const res = d.body + const res = d.body; if ( res && @@ -205,9 +204,10 @@ export async function runKycMerchantDepositRewriteTest(t: GlobalTestState) { { const kycStatus = await retryUntilNonNull(async () => { await merchant.runKyccheckOnce(); - const d = await merchantApi.getCurrentInstanceKycStatus(merchantAdminAccessToken); - t.assertTrue(d.type === "ok") - t.assertTrue(d.body.kycRequired === true) + const d = await merchantApi.getCurrentInstanceKycStatus( + merchantAdminAccessToken, + ); + t.assertTrue(d.type === "ok"); if (d.body.kyc_data[0].payto_kycauths) { return d.body; } @@ -255,13 +255,14 @@ export async function runKycMerchantDepositRewriteTest(t: GlobalTestState) { // Required, since the merchant only checks infrequently. await merchant.runKyccheckOnce(); // Now we can check the status - const d = await merchantApi.getCurrentInstanceKycStatus(merchantAdminAccessToken); - t.assertTrue(d.type === "ok") - t.assertTrue(d.body.kycRequired === true) + const d = await merchantApi.getCurrentInstanceKycStatus( + merchantAdminAccessToken, + ); + t.assertTrue(d.type === "ok"); if (d.body.kyc_data[0].payto_kycauths) { return d.body; } - const res = d.body + const res = d.body; if ((res?.kyc_data[0].limits?.length ?? 0) === 0) { return res; diff --git a/packages/taler-harness/src/integrationtests/test-merchant-bank-bad-wire-target.ts b/packages/taler-harness/src/integrationtests/test-merchant-bank-bad-wire-target.ts @@ -18,22 +18,14 @@ * Imports. */ import { - AbsoluteTime, - AmountString, Duration, - PreparePayResultType, TalerExchangeHttpClient, TalerMerchantApi, TalerMerchantInstanceHttpClient, j2s, succeedOrThrow, } from "@gnu-taler/taler-util"; -import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; -import { - applyTimeTravelV2, - createSimpleTestkudosEnvironmentV3, - withdrawViaBankV3, -} from "../harness/environments.js"; +import { createSimpleTestkudosEnvironmentV3 } from "../harness/environments.js"; import { GlobalTestState } from "../harness/harness.js"; /** @@ -63,19 +55,18 @@ export async function runMerchantBankBadWireTargetTest(t: GlobalTestState) { const exchangeClient = new TalerExchangeHttpClient(exchange.baseUrl); - const keys = succeedOrThrow(await exchangeClient.getKeys()) + const keys = succeedOrThrow(await exchangeClient.getKeys()); // KYC should be disabled - t.assertTrue(!keys.kyc_enabled) - + t.assertTrue(!keys.kyc_enabled); + const merchantClient = new TalerMerchantInstanceHttpClient( merchant.makeInstanceBaseUrl("minst2"), ); const d = await merchantClient.getCurrentInstanceKycStatus(accessToken); - t.assertTrue(d.type === "ok") - t.assertTrue(d.body.kycRequired === true) - const kycStatus = d.body + t.assertTrue(d.type === "ok"); + const kycStatus = d.body; t.assertTrue(!!kycStatus); diff --git a/packages/taler-harness/src/integrationtests/test-merchant-kyc-auth-multi.ts b/packages/taler-harness/src/integrationtests/test-merchant-kyc-auth-multi.ts @@ -122,7 +122,6 @@ async function doAccountKycAuth( await merchantClient.getCurrentInstanceKycStatus(merchantAccessToken, {}), ); console.log(`kyc res: ${j2s(kycRes1)}`); - t.assertDeepEqual(kycRes1.kycRequired, true); const myRow = kycRes1.kyc_data.find( (x) => x.exchange_url === exchange.baseUrl, ); @@ -156,7 +155,6 @@ async function doAccountKycAuth( ); logger.info(`kyc status after transfer: ${j2s(kycStatus)}`); t.assertDeepEqual(kycStatus.case, "ok"); - t.assertTrue(kycStatus.body.kycRequired === true); const myRow = kycStatus.body.kyc_data.find( (x) => x.exchange_url === exchange.baseUrl && @@ -263,7 +261,6 @@ export async function runMerchantKycAuthMultiTest(t: GlobalTestState) { ); logger.info(`kyc status after transfer: ${j2s(kycStatus)}`); t.assertDeepEqual(kycStatus.case, "ok"); - t.assertTrue(kycStatus.body.kycRequired === true); const myRow = kycStatus.body.kyc_data.find( (x) => x.exchange_url === exchange.baseUrl && diff --git a/packages/taler-harness/src/integrationtests/test-tops-aml-basic.ts b/packages/taler-harness/src/integrationtests/test-tops-aml-basic.ts @@ -76,9 +76,10 @@ export async function runTopsAmlBasicTest(t: GlobalTestState) { ); // Do KYC auth transfer { - const kycStatus = await merchantClient.getCurrentInstanceKycStatus(merchantAdminAccessToken); - t.assertTrue(kycStatus.type === "ok") - t.assertTrue(kycStatus.body.kycRequired === true) + const kycStatus = await merchantClient.getCurrentInstanceKycStatus( + merchantAdminAccessToken, + ); + t.assertTrue(kycStatus.type === "ok"); console.log(`kyc status: ${j2s(kycStatus)}`); t.assertDeepEqual(kycStatus.body.kyc_data[0].status, "kyc-wire-required"); @@ -108,12 +109,14 @@ export async function runTopsAmlBasicTest(t: GlobalTestState) { // Wait for auth transfer to be registered by the exchange { - const kycStatus = await merchantClient.getCurrentInstanceKycStatus(merchantAdminAccessToken, { - reason: KycStatusLongPollingReason.AUTH_TRANSFER, // FIXME: deprecated - timeout: 30000, - }); - t.assertTrue(kycStatus.type === "ok") - t.assertTrue(kycStatus.body.kycRequired === true) + const kycStatus = await merchantClient.getCurrentInstanceKycStatus( + merchantAdminAccessToken, + { + reason: KycStatusLongPollingReason.AUTH_TRANSFER, // FIXME: deprecated + timeout: 30000, + }, + ); + t.assertTrue(kycStatus.type === "ok"); logger.info(`kyc status after transfer: ${j2s(kycStatus)}`); t.assertDeepEqual(kycStatus.body.kyc_data[0].status, "kyc-required"); @@ -164,10 +167,13 @@ export async function runTopsAmlBasicTest(t: GlobalTestState) { await merchant.runKyccheckOnce(); { - const kycStatus = await merchantClient.getCurrentInstanceKycStatus(merchantAdminAccessToken, { - reason: KycStatusLongPollingReason.AUTH_TRANSFER, // FIXME: deprecated - timeout: 30000, - }); + const kycStatus = await merchantClient.getCurrentInstanceKycStatus( + merchantAdminAccessToken, + { + reason: KycStatusLongPollingReason.AUTH_TRANSFER, // FIXME: deprecated + timeout: 30000, + }, + ); logger.info(`kyc status after accept-tos: ${j2s(kycStatus)}`); } diff --git a/packages/taler-harness/src/integrationtests/test-tops-aml-custom-addr-postal.ts b/packages/taler-harness/src/integrationtests/test-tops-aml-custom-addr-postal.ts @@ -66,7 +66,6 @@ export async function runTopsAmlCustomAddrPostalTest(t: GlobalTestState) { timeout: 30000, }); t.assertTrue(kycStatus.type === "ok") - t.assertTrue(kycStatus.body.kycRequired === true) console.log(`kyc status: ${j2s(kycStatus)}`); @@ -102,7 +101,6 @@ export async function runTopsAmlCustomAddrPostalTest(t: GlobalTestState) { timeout: 30000, }); t.assertTrue(kycStatus.type === "ok") - t.assertTrue(kycStatus.body.kycRequired === true) logger.info(`kyc status after transfer: ${j2s(kycStatus)}`); t.assertDeepEqual(kycStatus.body.kyc_data[0].status, "kyc-required"); diff --git a/packages/taler-harness/src/integrationtests/test-tops-aml-custom-addr-sms.ts b/packages/taler-harness/src/integrationtests/test-tops-aml-custom-addr-sms.ts @@ -25,8 +25,8 @@ import { j2s, KycStatusLongPollingReason, Logger, - OfficerSession, OfficerId, + OfficerSession, parsePaytoUriOrThrow, succeedOrThrow, TalerExchangeHttpClient, @@ -45,8 +45,14 @@ const logger = new Logger("test-tops-aml.ts"); export async function runTopsAmlCustomAddrSmsTest(t: GlobalTestState) { // Set up test environment - const { exchange, amlKeypair, merchant, bank, wireGatewayApi, merchantAdminAccessToken } = - await createTopsEnvironment(t); + const { + exchange, + amlKeypair, + merchant, + bank, + wireGatewayApi, + merchantAdminAccessToken, + } = await createTopsEnvironment(t); const challengerSms = await startFakeChallenger({ port: 6002, @@ -58,12 +64,14 @@ export async function runTopsAmlCustomAddrSmsTest(t: GlobalTestState) { ); // Do KYC auth transfer { - const kycStatus = await merchantClient.getCurrentInstanceKycStatus(merchantAdminAccessToken, { - reason: KycStatusLongPollingReason.AUTH_TRANSFER, // FIXME: deprecated - timeout: 30000, - }); - t.assertTrue(kycStatus.type === "ok") - t.assertTrue(kycStatus.body.kycRequired === true) + const kycStatus = await merchantClient.getCurrentInstanceKycStatus( + merchantAdminAccessToken, + { + reason: KycStatusLongPollingReason.AUTH_TRANSFER, // FIXME: deprecated + timeout: 30000, + }, + ); + t.assertTrue(kycStatus.type === "ok"); console.log(`kyc status: ${j2s(kycStatus)}`); @@ -94,12 +102,14 @@ export async function runTopsAmlCustomAddrSmsTest(t: GlobalTestState) { // Wait for auth transfer to be registered by the exchange { - const kycStatus = await merchantClient.getCurrentInstanceKycStatus(merchantAdminAccessToken, { - reason: KycStatusLongPollingReason.AUTH_TRANSFER, // FIXME: deprecated - timeout: 30000, - }); - t.assertTrue(kycStatus.type === "ok") - t.assertTrue(kycStatus.body.kycRequired === true) + const kycStatus = await merchantClient.getCurrentInstanceKycStatus( + merchantAdminAccessToken, + { + reason: KycStatusLongPollingReason.AUTH_TRANSFER, // FIXME: deprecated + timeout: 30000, + }, + ); + t.assertTrue(kycStatus.type === "ok"); logger.info(`kyc status after transfer: ${j2s(kycStatus)}`); @@ -155,10 +165,13 @@ export async function runTopsAmlCustomAddrSmsTest(t: GlobalTestState) { await merchant.runKyccheckOnce(); { - const kycStatus = await merchantClient.getCurrentInstanceKycStatus(merchantAdminAccessToken, { - reason: KycStatusLongPollingReason.AUTH_TRANSFER, // FIXME: deprecated - timeout: 30000, - }); + const kycStatus = await merchantClient.getCurrentInstanceKycStatus( + merchantAdminAccessToken, + { + reason: KycStatusLongPollingReason.AUTH_TRANSFER, // FIXME: deprecated + timeout: 30000, + }, + ); logger.info(`kyc status after accept-tos: ${j2s(kycStatus)}`); } diff --git a/packages/taler-harness/src/integrationtests/test-tops-merchant-tos.ts b/packages/taler-harness/src/integrationtests/test-tops-merchant-tos.ts @@ -111,7 +111,6 @@ export async function runTopsMerchantTosTest(t: GlobalTestState) { ), ); logger.info(`kyc status after accept-tos: ${j2s(kycStatus)}`); - t.assertDeepEqual(kycStatus.kycRequired, true); const myRow = kycStatus.kyc_data.find( (x) => x.exchange_url === exchange.baseUrl, ); diff --git a/packages/taler-util/src/http-client/merchant.ts b/packages/taler-util/src/http-client/merchant.ts @@ -93,6 +93,7 @@ import { opEmptySuccess, opFixedSuccess, opKnownAlternativeHttpFailure, + opKnownFailureWithBody, opKnownHttpFailure, opKnownTalerFailure, opUnknownHttpFailure, @@ -171,15 +172,10 @@ export enum TalerMerchantManagementCacheEviction { DELETE_INSTANCE, } -export type MerchantKycStatusResult = - | { - kycRequired: false | undefined; - } - | { - kycRequired: true; - kyc_data: TalerMerchantApi.MerchantAccountKycRedirect[]; - etag?: string; - }; +export interface MerchantKycStatusResult { + kyc_data: TalerMerchantApi.MerchantAccountKycRedirect[]; + etag?: string; +} /** * Protocol version spoken with the core bank. @@ -843,6 +839,8 @@ export class TalerMerchantInstanceHttpClient { params: TalerMerchantApi.GetKycStatusRequestParams = {}, ): Promise< | OperationOk<MerchantKycStatusResult> + | OperationFail<HttpStatusCode.NoContent> + | OperationAlternative<HttpStatusCode.NotModified, { etag?: string }> | OperationFail<HttpStatusCode.Unauthorized> | OperationFail<HttpStatusCode.NotFound> | OperationFail<HttpStatusCode.ServiceUnavailable> @@ -896,19 +894,19 @@ export class TalerMerchantInstanceHttpClient { switch (resp.status) { case HttpStatusCode.Ok: { const f = await opSuccessFromHttp(resp, codecForAccountKycRedirects()); - return opFixedSuccess({ etag, kycRequired: true as const, ...f.body }); + return opFixedSuccess({ etag, ...f.body }); } case HttpStatusCode.NoContent: - return opFixedSuccess({ etag, kycRequired: false as const }); + return opKnownHttpFailure(resp.status, resp); case HttpStatusCode.NotModified: - return opFixedSuccess({ etag, kycRequired: undefined }); + return opKnownFailureWithBody(resp.status, { etag }); case HttpStatusCode.Unauthorized: // FIXME: missing in docs return opKnownHttpFailure(resp.status, resp); case HttpStatusCode.NotFound: // FIXME: missing in docs return opKnownHttpFailure(resp.status, resp); case HttpStatusCode.ServiceUnavailable: return opKnownHttpFailure(resp.status, resp); - case HttpStatusCode.GatewayTimeout: + case HttpStatusCode.GatewayTimeout: // FIXME: missing in docs return opKnownHttpFailure(resp.status, resp); default: return opUnknownHttpFailure(resp);