commit 0997ade82af82e625fb0b4da40f2543bac38992d
parent 4d3cad1b51fefea6902cc7841f7ad104ca4fda04
Author: Florian Dold <florian@dold.me>
Date: Mon, 29 Jun 2026 19:56:50 +0200
wallet-core: properly initialize kycAuthTransferOptions in deposit transactions
This commit also expands the tops-nexus-swt test to cover this.
Diffstat:
2 files changed, 102 insertions(+), 23 deletions(-)
diff --git a/packages/taler-harness/src/integrationtests/test-tops-nexus-swt.ts b/packages/taler-harness/src/integrationtests/test-tops-nexus-swt.ts
@@ -23,8 +23,8 @@ import {
j2s,
Logger,
Paytos,
- TalerProtocolDuration,
TransactionMajorState,
+ TransactionMinorState,
TransactionType,
WithdrawalType,
} from "@gnu-taler/taler-util";
@@ -36,7 +36,6 @@ import {
ExchangeService,
GlobalTestState,
LibeufinNexusService,
- MerchantService,
NexusBankAccountInfo,
setupDb,
} from "../harness/harness.js";
@@ -155,6 +154,42 @@ export async function runTopsNexusSwtTest(t: GlobalTestState) {
major: TransactionMajorState.Done,
},
});
+
+ // Now, deposit to a *different* bank account to test KYC auth for the wallet.
+
+ // Random IBAN generated via "libeufin-nexus testing iban gen --country CH"
+ const walletIban = "CH5210395CX7GTDCRC3C4";
+
+ const walletPayto = Paytos.toFullString(
+ Paytos.createIban(walletIban as IbanString, undefined, {
+ "receiver-name": "Alice",
+ }),
+ );
+
+ const depositRes = await walletClient.call(
+ WalletApiOperation.CreateDepositGroup,
+ {
+ amount: "CHF:5",
+ depositPaytoUri: walletPayto,
+ },
+ );
+
+ await walletClient.call(WalletApiOperation.TestingWaitTransactionState, {
+ transactionId: depositRes.transactionId,
+ txState: {
+ major: TransactionMajorState.Pending,
+ minor: TransactionMinorState.KycAuthRequired,
+ },
+ });
+
+ const txDet = await walletClient.call(WalletApiOperation.GetTransactionById, {
+ transactionId: depositRes.transactionId,
+ });
+
+ console.log(j2s(txDet));
+
+ t.assertTrue(txDet.type == TransactionType.Deposit);
+ t.assertTrue((txDet.kycAuthTransferInfo?.transferOptions.length ?? 0) > 0);
}
runTopsNexusSwtTest.suites = ["tops", "libeufin"];
diff --git a/packages/taler-wallet-core/src/deposits.ts b/packages/taler-wallet-core/src/deposits.ts
@@ -1042,6 +1042,42 @@ async function processDepositGroupAborting(
return waitForRefreshOnDepositGroup(wex, depositGroup);
}
+async function getAuthTransferDetails(
+ wex: WalletExecutionContext,
+ depositGroup: DepositGroupRecord,
+ exchangeBaseUrl: string,
+): Promise<{
+ options: TransferOptionRaw[];
+ transferExpiry: TalerProtocolTimestamp | undefined;
+}> {
+ let options: TransferOptionRaw[] = [];
+ let transferExpiry: TalerProtocolTimestamp | undefined = undefined;
+
+ const exch = await fetchFreshExchange(wex, exchangeBaseUrl);
+
+ for (const acct of exch.wireInfo.accounts) {
+ const optRes = await getTransferOptionsRaw(wex, {
+ acct,
+ reservePriv: depositGroup.merchantPriv,
+ reservePub: depositGroup.merchantPub,
+ transferAmount: exch.tinyAmount,
+ });
+ options.push(...optRes.transferOptions);
+ if (!transferExpiry) {
+ transferExpiry = optRes.transferExpiry;
+ } else {
+ transferExpiry = AbsoluteTime.toProtocolTimestamp(
+ AbsoluteTime.min(
+ AbsoluteTime.fromProtocolTimestamp(optRes.transferExpiry),
+ AbsoluteTime.fromProtocolTimestamp(transferExpiry),
+ ),
+ );
+ }
+ }
+
+ return { options, transferExpiry };
+}
+
/**
* Process the transaction in states where KYC is required.
* Used for both the deposit KYC and aggregate KYC.
@@ -1126,27 +1162,13 @@ async function processDepositGroupPendingKyc(
let transferExpiry: TalerProtocolTimestamp | undefined = undefined;
if (algoRes.requiresAuth) {
- const exch = await fetchFreshExchange(wex, kycInfo.exchangeBaseUrl);
-
- for (const acct of exch.wireInfo.accounts) {
- const optRes = await getTransferOptionsRaw(wex, {
- acct,
- reservePriv: depositGroup.merchantPriv,
- reservePub: depositGroup.merchantPub,
- transferAmount: exch.tinyAmount,
- });
- options.push(...optRes.transferOptions);
- if (!transferExpiry) {
- transferExpiry = optRes.transferExpiry;
- } else {
- transferExpiry = AbsoluteTime.toProtocolTimestamp(
- AbsoluteTime.min(
- AbsoluteTime.fromProtocolTimestamp(optRes.transferExpiry),
- AbsoluteTime.fromProtocolTimestamp(transferExpiry),
- ),
- );
- }
- }
+ const res = await getAuthTransferDetails(
+ wex,
+ depositGroup,
+ kycInfo.exchangeBaseUrl,
+ );
+ options = res.options;
+ transferExpiry = res.transferExpiry;
}
// Now store the result.
@@ -1172,6 +1194,9 @@ async function processDepositGroupPendingKyc(
case DepositOperationStatus.PendingDepositKycAuth:
if (!requiresAuth) {
newDg.operationStatus = DepositOperationStatus.PendingDepositKyc;
+ } else {
+ newDg.kycAuthTransferOptions = options;
+ newDg.kycAuthTransferExpiry = transferExpiry;
}
break;
default:
@@ -1319,6 +1344,18 @@ async function transitionToKycRequired(
const ctx = new DepositTransactionContext(wex, depositGroupId);
+ let options: TransferOptionRaw[] = [];
+ let transferExpiry: TalerProtocolTimestamp | undefined = undefined;
+ if (args.badKycAuth) {
+ const res = await getAuthTransferDetails(
+ wex,
+ depositGroup,
+ args.exchangeUrl,
+ );
+ options = res.options;
+ transferExpiry = res.transferExpiry;
+ }
+
await wex.runLegacyWalletDbTx(async (tx) => {
const [dg, h] = await ctx.getRecordHandle(tx);
if (!dg) {
@@ -1336,6 +1373,8 @@ async function transitionToKycRequired(
case DepositOperationStatus.PendingDeposit:
if (args.badKycAuth) {
dg.operationStatus = DepositOperationStatus.PendingDepositKycAuth;
+ dg.kycAuthTransferExpiry = transferExpiry;
+ dg.kycAuthTransferOptions = options;
} else {
dg.operationStatus = DepositOperationStatus.PendingDepositKyc;
}
@@ -1343,11 +1382,16 @@ async function transitionToKycRequired(
case DepositOperationStatus.PendingDepositKycAuth:
if (!args.badKycAuth) {
dg.operationStatus = DepositOperationStatus.PendingDepositKyc;
+ } else {
+ dg.kycAuthTransferExpiry = transferExpiry;
+ dg.kycAuthTransferOptions = options;
}
break;
case DepositOperationStatus.PendingDepositKyc:
if (args.badKycAuth) {
dg.operationStatus = DepositOperationStatus.PendingDepositKycAuth;
+ dg.kycAuthTransferExpiry = transferExpiry;
+ dg.kycAuthTransferOptions = options;
}
break;
default: