diff options
author | Florian Dold <florian@dold.me> | 2024-09-05 21:15:22 +0200 |
---|---|---|
committer | Florian Dold <florian@dold.me> | 2024-09-05 21:15:22 +0200 |
commit | 5402b00b796011005f52e200c856832f225ffaf9 (patch) | |
tree | 5836ea9f7ebd72921bbd9f7ad4a725205c6e3003 | |
parent | 0f9ff6e37fe45e2f2e4db95a517a35a3df5cea6e (diff) | |
download | wallet-core-5402b00b796011005f52e200c856832f225ffaf9.tar.xz |
wallet-core: support KYC directly on deposit in deposit tx, test
-rw-r--r-- | packages/taler-harness/src/integrationtests/test-kyc-deposit-deposit.ts | 295 | ||||
-rw-r--r-- | packages/taler-harness/src/integrationtests/testrunner.ts | 2 | ||||
-rw-r--r-- | packages/taler-wallet-core/src/balance.ts | 8 | ||||
-rw-r--r-- | packages/taler-wallet-core/src/db.ts | 13 | ||||
-rw-r--r-- | packages/taler-wallet-core/src/deposits.ts | 144 | ||||
-rw-r--r-- | packages/taler-wallet-core/src/pay-peer-push-credit.ts | 7 |
6 files changed, 423 insertions, 46 deletions
diff --git a/packages/taler-harness/src/integrationtests/test-kyc-deposit-deposit.ts b/packages/taler-harness/src/integrationtests/test-kyc-deposit-deposit.ts new file mode 100644 index 000000000..2ee58c7d8 --- /dev/null +++ b/packages/taler-harness/src/integrationtests/test-kyc-deposit-deposit.ts @@ -0,0 +1,295 @@ +/* + This file is part of GNU Taler + (C) 2020 Taler Systems S.A. + + GNU Taler is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ + +/** + * Imports. + */ +import { + TalerCorebankApiClient, + TransactionMajorState, + TransactionMinorState, +} from "@gnu-taler/taler-util"; +import { + createSyncCryptoApi, + EddsaKeypairStrings, + WalletApiOperation, +} from "@gnu-taler/taler-wallet-core"; +import { CoinConfig, defaultCoinConfig } from "../harness/denomStructures.js"; +import { + BankService, + DbInfo, + ExchangeService, + generateRandomPayto, + GlobalTestState, + HarnessExchangeBankAccount, + setupDb, + WalletClient, + WalletService, +} from "../harness/harness.js"; +import { + EnvOptions, + postAmlDecisionNoRules, + withdrawViaBankV3, +} from "../harness/helpers.js"; + +interface KycTestEnv { + commonDb: DbInfo; + bankClient: TalerCorebankApiClient; + exchange: ExchangeService; + exchangeBankAccount: HarnessExchangeBankAccount; + walletClient: WalletClient; + walletService: WalletService; + amlKeypair: EddsaKeypairStrings; +} + +async function createKycTestkudosEnvironment( + t: GlobalTestState, + coinConfig: CoinConfig[] = defaultCoinConfig.map((x) => x("TESTKUDOS")), + opts: EnvOptions = {}, +): Promise<KycTestEnv> { + const db = await setupDb(t); + + const bank = await BankService.create(t, { + allowRegistrations: true, + currency: "TESTKUDOS", + database: db.connStr, + httpPort: 8082, + }); + + const exchange = ExchangeService.create(t, { + name: "testexchange-1", + currency: "TESTKUDOS", + httpPort: 8081, + database: db.connStr, + }); + + let receiverName = "Exchange"; + let exchangeBankUsername = "exchange"; + let exchangeBankPassword = "mypw"; + let exchangePaytoUri = generateRandomPayto(exchangeBankUsername); + + await exchange.addBankAccount("1", { + accountName: exchangeBankUsername, + accountPassword: exchangeBankPassword, + wireGatewayApiBaseUrl: new URL( + "accounts/exchange/taler-wire-gateway/", + bank.baseUrl, + ).href, + accountPaytoUri: exchangePaytoUri, + }); + + bank.setSuggestedExchange(exchange, exchangePaytoUri); + + await bank.start(); + + await bank.pingUntilAvailable(); + + const bankClient = new TalerCorebankApiClient(bank.corebankApiBaseUrl, { + auth: { + username: "admin", + password: "adminpw", + }, + }); + + await bankClient.registerAccountExtended({ + name: receiverName, + password: exchangeBankPassword, + username: exchangeBankUsername, + is_taler_exchange: true, + payto_uri: exchangePaytoUri, + }); + + const ageMaskSpec = opts.ageMaskSpec; + + if (ageMaskSpec) { + exchange.enableAgeRestrictions(ageMaskSpec); + // Enable age restriction for all coins. + exchange.addCoinConfigList( + coinConfig.map((x) => ({ + ...x, + name: `${x.name}-age`, + ageRestricted: true, + })), + ); + // For mixed age restrictions, we also offer coins without age restrictions + if (opts.mixedAgeRestriction) { + exchange.addCoinConfigList( + coinConfig.map((x) => ({ ...x, ageRestricted: false })), + ); + } + } else { + exchange.addCoinConfigList(coinConfig); + } + + await exchange.modifyConfig(async (config) => { + config.setString("exchange", "enable_kyc", "yes"); + + config.setString("KYC-RULE-R1", "operation_type", "deposit"); + config.setString("KYC-RULE-R1", "enabled", "yes"); + config.setString("KYC-RULE-R1", "exposed", "yes"); + config.setString("KYC-RULE-R1", "is_and_combinator", "yes"); + config.setString("KYC-RULE-R1", "threshold", "TESTKUDOS:5"); + config.setString("KYC-RULE-R1", "timeframe", "1d"); + config.setString("KYC-RULE-R1", "next_measures", "M1"); + + config.setString("KYC-MEASURE-M1", "check_name", "C1"); + config.setString("KYC-MEASURE-M1", "context", "{}"); + config.setString("KYC-MEASURE-M1", "program", "P1"); + + config.setString("AML-PROGRAM-P1", "command", "/bin/true"); + config.setString("AML-PROGRAM-P1", "enabled", "true"); + config.setString("AML-PROGRAM-P1", "description", "this does nothing"); + config.setString("AML-PROGRAM-P1", "fallback", "M1"); + + config.setString("KYC-CHECK-C1", "type", "INFO"); + config.setString("KYC-CHECK-C1", "description", "my check!"); + config.setString("KYC-CHECK-C1", "fallback", "M1"); + }); + + await exchange.start(); + + const cryptoApi = createSyncCryptoApi(); + const amlKeypair = await cryptoApi.createEddsaKeypair({}); + + await exchange.enableAmlAccount(amlKeypair.pub, "Alice"); + + const walletService = new WalletService(t, { + name: "wallet", + useInMemoryDb: true, + }); + await walletService.start(); + await walletService.pingUntilAvailable(); + + const walletClient = new WalletClient({ + name: "wallet", + unixPath: walletService.socketPath, + onNotification(n) { + console.log("got notification", n); + }, + }); + await walletClient.connect(); + await walletClient.client.call(WalletApiOperation.InitWallet, { + config: { + testing: { + skipDefaults: true, + }, + }, + }); + + console.log("setup done!"); + + return { + commonDb: db, + exchange, + amlKeypair, + walletClient, + walletService, + bankClient, + exchangeBankAccount: { + accountName: "", + accountPassword: "", + accountPaytoUri: "", + wireGatewayApiBaseUrl: "", + }, + }; +} + +export async function runKycDepositDepositTest(t: GlobalTestState) { + // Set up test environment + + const { walletClient, bankClient, exchange, amlKeypair } = + await createKycTestkudosEnvironment(t); + + // Withdraw digital cash into the wallet. + + const wres = await withdrawViaBankV3(t, { + bankClient, + amount: "TESTKUDOS:50", + exchange: exchange, + walletClient: walletClient, + }); + + await wres.withdrawalFinishedCond; + + const depositResp = await walletClient.call( + WalletApiOperation.CreateDepositGroup, + { + amount: "TESTKUDOS:10", + depositPaytoUri: wres.accountPaytoUri, + }, + ); + + await walletClient.call(WalletApiOperation.TestingWaitTransactionState, { + transactionId: depositResp.transactionId, + txState: { + major: TransactionMajorState.Pending, + minor: TransactionMinorState.Track, + }, + }); + + await exchange.runAggregatorOnceWithTimetravel({ + timetravelMicroseconds: 1000 * 1000 * 60 * 60 * 3, + }); + + await exchange.runTransferOnceWithTimetravel({ + timetravelMicroseconds: 1000 * 1000 * 60 * 60 * 3, + }); + + console.log("waiting for kyc-required"); + + await walletClient.call(WalletApiOperation.TestingWaitTransactionState, { + transactionId: depositResp.transactionId, + txState: { + major: TransactionMajorState.Pending, + minor: TransactionMinorState.KycRequired, + }, + }); + + const txDetails = await walletClient.call( + WalletApiOperation.GetTransactionById, + { + transactionId: depositResp.transactionId, + }, + ); + + const kycPaytoHash = txDetails.kycPaytoHash; + + t.assertTrue(!!kycPaytoHash); + + await postAmlDecisionNoRules(t, { + amlPriv: amlKeypair.priv, + amlPub: amlKeypair.pub, + exchangeBaseUrl: exchange.baseUrl, + paytoHash: kycPaytoHash, + }); + + await exchange.runAggregatorOnceWithTimetravel({ + timetravelMicroseconds: 1000 * 1000 * 60 * 60 * 3, + }); + + await exchange.runTransferOnceWithTimetravel({ + timetravelMicroseconds: 1000 * 1000 * 60 * 60 * 3, + }); + + await walletClient.call(WalletApiOperation.TestingWaitTransactionState, { + transactionId: depositResp.transactionId, + txState: { + major: TransactionMajorState.Done, + }, + }); +} + +runKycDepositDepositTest.suites = ["wallet"]; diff --git a/packages/taler-harness/src/integrationtests/testrunner.ts b/packages/taler-harness/src/integrationtests/testrunner.ts index f7ae50149..32bac3fdc 100644 --- a/packages/taler-harness/src/integrationtests/testrunner.ts +++ b/packages/taler-harness/src/integrationtests/testrunner.ts @@ -49,6 +49,7 @@ import { runFeeRegressionTest } from "./test-fee-regression.js"; import { runForcedSelectionTest } from "./test-forced-selection.js"; import { runKycBalanceWithdrawalTest } from "./test-kyc-balance-withdrawal.js"; import { runKycDepositAggregateTest } from "./test-kyc-deposit-aggregate.js"; +import { runKycDepositDepositTest } from "./test-kyc-deposit-deposit.js"; import { runKycExchangeWalletTest } from "./test-kyc-exchange-wallet.js"; import { runKycFormWithdrawalTest } from "./test-kyc-form-withdrawal.js"; import { runKycNewMeasureTest } from "./test-kyc-new-measure.js"; @@ -260,6 +261,7 @@ const allTests: TestMainFunction[] = [ runKycFormWithdrawalTest, runKycBalanceWithdrawalTest, runKycNewMeasureTest, + runKycDepositDepositTest, ]; export interface TestRunSpec { diff --git a/packages/taler-wallet-core/src/balance.ts b/packages/taler-wallet-core/src/balance.ts index 396efda1f..99979f59a 100644 --- a/packages/taler-wallet-core/src/balance.ts +++ b/packages/taler-wallet-core/src/balance.ts @@ -449,14 +449,14 @@ export async function getBalancesInsideTransaction( for (const [e, x] of Object.entries(perExchange)) { const currency = Amounts.currencyOf(dgRecord.amount); switch (dgRecord.operationStatus) { - case DepositOperationStatus.SuspendedKyc: - case DepositOperationStatus.PendingKyc: + case DepositOperationStatus.SuspendedAggregateKyc: + case DepositOperationStatus.PendingAggregateKyc: await balanceStore.setFlagOutgoingKyc(currency, e); } switch (dgRecord.operationStatus) { - case DepositOperationStatus.SuspendedKyc: - case DepositOperationStatus.PendingKyc: + case DepositOperationStatus.SuspendedAggregateKyc: + case DepositOperationStatus.PendingAggregateKyc: case DepositOperationStatus.PendingTrack: case DepositOperationStatus.SuspendedAborting: case DepositOperationStatus.SuspendedDeposit: diff --git a/packages/taler-wallet-core/src/db.ts b/packages/taler-wallet-core/src/db.ts index 99b69135a..ba33c5555 100644 --- a/packages/taler-wallet-core/src/db.ts +++ b/packages/taler-wallet-core/src/db.ts @@ -1725,15 +1725,18 @@ export interface BackupProviderRecord { export enum DepositOperationStatus { PendingDeposit = 0x0100_0000, + SuspendedDeposit = 0x0110_0000, + PendingTrack = 0x0100_0001, - PendingKyc = 0x0100_0002, + SuspendedTrack = 0x0110_0001, - Aborting = 0x0103_0000, + PendingAggregateKyc = 0x0100_0002, + SuspendedAggregateKyc = 0x0110_0002, - SuspendedDeposit = 0x0110_0000, - SuspendedTrack = 0x0110_0001, - SuspendedKyc = 0x0110_0002, + PendingDepositKyc = 0x0100_0003, + SuspendedDepositKyc = 0x0110_0003, + Aborting = 0x0103_0000, SuspendedAborting = 0x0113_0000, Finished = 0x0500_0000, diff --git a/packages/taler-wallet-core/src/deposits.ts b/packages/taler-wallet-core/src/deposits.ts index da69530f7..371d7cf70 100644 --- a/packages/taler-wallet-core/src/deposits.ts +++ b/packages/taler-wallet-core/src/deposits.ts @@ -63,6 +63,7 @@ import { checkLogicInvariant, codecForAccountKycStatus, codecForBatchDepositSuccess, + codecForLegitimizationNeededResponse, codecForTackTransactionAccepted, codecForTackTransactionWired, encodeCrock, @@ -94,7 +95,6 @@ import { DepositElementStatus, DepositGroupRecord, DepositInfoPerExchange, - DepositKycInfo, DepositOperationStatus, DepositTrackingInfo, RefreshOperationStatus, @@ -293,11 +293,23 @@ export class DepositTransactionContext implements TransactionContext { const oldState = computeDepositTransactionStatus(dg); let newOpStatus: DepositOperationStatus | undefined; switch (dg.operationStatus) { + case DepositOperationStatus.Aborted: + case DepositOperationStatus.Failed: + case DepositOperationStatus.Finished: + case DepositOperationStatus.SuspendedAborting: + case DepositOperationStatus.SuspendedAggregateKyc: + case DepositOperationStatus.SuspendedDeposit: + case DepositOperationStatus.SuspendedDepositKyc: + case DepositOperationStatus.SuspendedTrack: + break; + case DepositOperationStatus.PendingDepositKyc: + newOpStatus = DepositOperationStatus.SuspendedDepositKyc; + break; case DepositOperationStatus.PendingDeposit: newOpStatus = DepositOperationStatus.SuspendedDeposit; break; - case DepositOperationStatus.PendingKyc: - newOpStatus = DepositOperationStatus.SuspendedKyc; + case DepositOperationStatus.PendingAggregateKyc: + newOpStatus = DepositOperationStatus.SuspendedAggregateKyc; break; case DepositOperationStatus.PendingTrack: newOpStatus = DepositOperationStatus.SuspendedTrack; @@ -305,6 +317,8 @@ export class DepositTransactionContext implements TransactionContext { case DepositOperationStatus.Aborting: newOpStatus = DepositOperationStatus.SuspendedAborting; break; + default: + assertUnreachable(dg.operationStatus); } if (!newOpStatus) { return undefined; @@ -376,18 +390,32 @@ export class DepositTransactionContext implements TransactionContext { const oldState = computeDepositTransactionStatus(dg); let newOpStatus: DepositOperationStatus | undefined; switch (dg.operationStatus) { + case DepositOperationStatus.Aborted: + case DepositOperationStatus.Aborting: + case DepositOperationStatus.Failed: + case DepositOperationStatus.Finished: + case DepositOperationStatus.PendingAggregateKyc: + case DepositOperationStatus.PendingDeposit: + case DepositOperationStatus.PendingDepositKyc: + case DepositOperationStatus.PendingTrack: + break; + case DepositOperationStatus.SuspendedDepositKyc: + newOpStatus = DepositOperationStatus.PendingDepositKyc; + break; case DepositOperationStatus.SuspendedDeposit: newOpStatus = DepositOperationStatus.PendingDeposit; break; case DepositOperationStatus.SuspendedAborting: newOpStatus = DepositOperationStatus.Aborting; break; - case DepositOperationStatus.SuspendedKyc: - newOpStatus = DepositOperationStatus.PendingKyc; + case DepositOperationStatus.SuspendedAggregateKyc: + newOpStatus = DepositOperationStatus.PendingAggregateKyc; break; case DepositOperationStatus.SuspendedTrack: newOpStatus = DepositOperationStatus.PendingTrack; break; + default: + assertUnreachable(dg.operationStatus); } if (!newOpStatus) { return undefined; @@ -459,7 +487,7 @@ export function computeDepositTransactionStatus( major: TransactionMajorState.Pending, minor: TransactionMinorState.Deposit, }; - case DepositOperationStatus.PendingKyc: + case DepositOperationStatus.PendingAggregateKyc: return { major: TransactionMajorState.Pending, minor: TransactionMinorState.KycRequired, @@ -469,7 +497,7 @@ export function computeDepositTransactionStatus( major: TransactionMajorState.Pending, minor: TransactionMinorState.Track, }; - case DepositOperationStatus.SuspendedKyc: + case DepositOperationStatus.SuspendedAggregateKyc: return { major: TransactionMajorState.Suspended, minor: TransactionMinorState.KycRequired, @@ -499,6 +527,18 @@ export function computeDepositTransactionStatus( return { major: TransactionMajorState.SuspendedAborting, }; + case DepositOperationStatus.PendingDepositKyc: + return { + major: TransactionMajorState.Pending, + // We lie to the UI by hiding the specific KYC state. + minor: TransactionMinorState.KycRequired, + }; + case DepositOperationStatus.SuspendedDepositKyc: + return { + major: TransactionMajorState.Suspended, + // We lie to the UI by hiding the specific KYC state. + minor: TransactionMinorState.KycRequired, + }; default: assertUnreachable(dg.operationStatus); } @@ -534,7 +574,7 @@ export function computeDepositTransactionActions( return [TransactionAction.Delete]; case DepositOperationStatus.SuspendedAborting: return [TransactionAction.Resume, TransactionAction.Fail]; - case DepositOperationStatus.PendingKyc: + case DepositOperationStatus.PendingAggregateKyc: return [ TransactionAction.Retry, TransactionAction.Suspend, @@ -546,10 +586,14 @@ export function computeDepositTransactionActions( TransactionAction.Suspend, TransactionAction.Abort, ]; - case DepositOperationStatus.SuspendedKyc: + case DepositOperationStatus.SuspendedAggregateKyc: return [TransactionAction.Resume, TransactionAction.Fail]; case DepositOperationStatus.SuspendedTrack: return [TransactionAction.Resume, TransactionAction.Abort]; + case DepositOperationStatus.PendingDepositKyc: + return [TransactionAction.Resume, TransactionAction.Abort]; + case DepositOperationStatus.SuspendedDepositKyc: + return [TransactionAction.Suspend, TransactionAction.Abort]; default: assertUnreachable(dg.operationStatus); } @@ -778,6 +822,10 @@ async function processDepositGroupAborting( return waitForRefreshOnDepositGroup(wex, depositGroup); } +/** + * Process the transaction in states where KYC is required. + * Used for both the deposit KYC and aggregate KYC. + */ async function processDepositGroupPendingKyc( wex: WalletExecutionContext, depositGroup: DepositGroupRecord, @@ -832,8 +880,6 @@ async function processDepositGroupPendingKyc( if ( kycStatusRes.status === HttpStatusCode.Ok || - //FIXME: NoContent is not expected https://docs.taler.net/core/api-exchange.html#post--purses-$PURSE_PUB-merge - // remove after the exchange is fixed or clarified kycStatusRes.status === HttpStatusCode.NoContent ) { const transitionInfo = await wex.db.runReadWriteTx( @@ -843,20 +889,26 @@ async function processDepositGroupPendingKyc( if (!newDg) { return; } - if (newDg.operationStatus !== DepositOperationStatus.PendingKyc) { - return; - } const oldTxState = computeDepositTransactionStatus(newDg); - newDg.operationStatus = DepositOperationStatus.PendingTrack; - const newTxState = computeDepositTransactionStatus(newDg); + switch (newDg.operationStatus) { + case DepositOperationStatus.PendingAggregateKyc: + newDg.operationStatus = DepositOperationStatus.PendingTrack; + break; + case DepositOperationStatus.PendingDeposit: + newDg.operationStatus = DepositOperationStatus.PendingDeposit; + break; + default: + return; + } await tx.depositGroups.put(newDg); await ctx.updateTransactionMeta(tx); + const newTxState = computeDepositTransactionStatus(newDg); return { oldTxState, newTxState }; }, ); notifyTransition(wex, transactionId, transitionInfo); } else if (kycStatusRes.status === HttpStatusCode.Accepted) { - // FIXME: Do we have to update the URL here? + logger.info("kyc still pending"); } else { throw Error(`unexpected response from kyc-check (${kycStatusRes.status})`); } @@ -907,7 +959,7 @@ async function getLastWithdrawalKeyPair( async function transitionToKycRequired( wex: WalletExecutionContext, depositGroup: DepositGroupRecord, - kycInfo: DepositKycInfo, + kycPaytoHash: string, exchangeUrl: string, ): Promise<TaskRunResult> { const { depositGroupId } = depositGroup; @@ -924,7 +976,7 @@ async function transitionToKycRequired( accountPub: lastReserveKeypair.pub, }); - const url = new URL(`kyc-check/${kycInfo.paytoHash}`, exchangeUrl); + const url = new URL(`kyc-check/${kycPaytoHash}`, exchangeUrl); logger.info(`kyc url ${url.href}`); const kycStatusResp = await wex.http.fetch(url.href, { method: "GET", @@ -948,14 +1000,20 @@ async function transitionToKycRequired( if (!dg) { return undefined; } - if (dg.operationStatus !== DepositOperationStatus.PendingTrack) { - return undefined; - } const oldTxState = computeDepositTransactionStatus(dg); - dg.operationStatus = DepositOperationStatus.PendingKyc; + switch (dg.operationStatus) { + case DepositOperationStatus.PendingTrack: + dg.operationStatus = DepositOperationStatus.PendingAggregateKyc; + break; + case DepositOperationStatus.PendingDeposit: + dg.operationStatus = DepositOperationStatus.PendingDepositKyc; + break; + default: + return; + } dg.kycInfo = { exchangeBaseUrl: exchangeUrl, - paytoHash: kycInfo.paytoHash, + paytoHash: kycPaytoHash, accessToken: statusResp.access_token, }; await tx.depositGroups.put(dg); @@ -1026,15 +1084,10 @@ async function processDepositGroupPendingTrack( const paytoHash = encodeCrock( hashTruncate32(stringToBytes(depositGroup.wire.payto_uri + "\0")), ); - const kycInfo: DepositKycInfo = { - paytoHash, - exchangeBaseUrl: exchangeBaseUrl, - accessToken: undefined, - }; return transitionToKycRequired( wex, depositGroup, - kycInfo, + paytoHash, exchangeBaseUrl, ); } else { @@ -1292,7 +1345,7 @@ async function processDepositGroupPendingDeposit( } // We need to do one batch per exchange. - for (const exchangeUrl of exchanges.values()) { + for (const exchangeBaseUrl of exchanges.values()) { const coins: BatchDepositRequestCoin[] = []; const batchIndexes: number[] = []; @@ -1309,7 +1362,7 @@ async function processDepositGroupPendingDeposit( for (let i = 0; i < depositPermissions.length; i++) { const perm = depositPermissions[i]; - if (perm.exchange_url != exchangeUrl) { + if (perm.exchange_url != exchangeBaseUrl) { continue; } coins.push({ @@ -1325,7 +1378,7 @@ async function processDepositGroupPendingDeposit( // Check for cancellation before making network request. cancellationToken?.throwIfCancelled(); - const url = new URL(`batch-deposit`, exchangeUrl); + const url = new URL(`batch-deposit`, exchangeBaseUrl); logger.info(`depositing to ${url.href}`); logger.trace(`deposit request: ${j2s(batchReq)}`); const httpResp = await wex.http.fetch(url.href, { @@ -1333,6 +1386,28 @@ async function processDepositGroupPendingDeposit( body: batchReq, cancellationToken: cancellationToken, }); + + switch (httpResp.status) { + case HttpStatusCode.Accepted: + case HttpStatusCode.Ok: + break; + case HttpStatusCode.UnavailableForLegalReasons: { + const kycLegiNeededResp = await readResponseJsonOrThrow( + httpResp, + codecForLegitimizationNeededResponse(), + ); + logger.info( + `kyc legitimization needed response: ${j2s(kycLegiNeededResp)}`, + ); + return transitionToKycRequired( + wex, + depositGroup, + kycLegiNeededResp.h_payto, + exchangeBaseUrl, + ); + } + } + await readSuccessResponseJsonOrThrow( httpResp, codecForBatchDepositSuccess(), @@ -1406,7 +1481,8 @@ export async function processDepositGroup( switch (depositGroup.operationStatus) { case DepositOperationStatus.PendingTrack: return processDepositGroupPendingTrack(wex, depositGroup); - case DepositOperationStatus.PendingKyc: + case DepositOperationStatus.PendingAggregateKyc: + case DepositOperationStatus.PendingDepositKyc: return processDepositGroupPendingKyc(wex, depositGroup); case DepositOperationStatus.PendingDeposit: return processDepositGroupPendingDeposit(wex, depositGroup); diff --git a/packages/taler-wallet-core/src/pay-peer-push-credit.ts b/packages/taler-wallet-core/src/pay-peer-push-credit.ts index fd6d40bcf..865e7e6a3 100644 --- a/packages/taler-wallet-core/src/pay-peer-push-credit.ts +++ b/packages/taler-wallet-core/src/pay-peer-push-credit.ts @@ -986,9 +986,10 @@ async function handlePendingMerge( }); if (mergeHttpResp.status === HttpStatusCode.UnavailableForLegalReasons) { - const respJson = await mergeHttpResp.json(); - const kycLegiNeededResp = - codecForLegitimizationNeededResponse().decode(respJson); + const kycLegiNeededResp = await readResponseJsonOrThrow( + mergeHttpResp, + codecForLegitimizationNeededResponse(), + ); logger.info( `kyc legitimization needed response: ${j2s(kycLegiNeededResp)}`, ); |