commit 77552c979f06ad5a169aa7f74c6a34d269c21222
parent 1e556e68289d7303ffd844cad896fa05ed89078f
Author: Florian Dold <florian@dold.me>
Date: Sat, 22 Feb 2025 20:44:03 +0100
wallet-core: delay withdrawal attempt when KYC is not done
We do this instead of long-polling for KYC completion, as KYC could be
unfinished due to an operation other than withdrawal
Diffstat:
2 files changed, 49 insertions(+), 2 deletions(-)
diff --git a/packages/taler-wallet-core/src/db.ts b/packages/taler-wallet-core/src/db.ts
@@ -1512,6 +1512,11 @@ export interface WithdrawalGroupRecord {
kycAccessToken?: string;
/**
+ * Delay to wait until the next withdrawal attempt.
+ */
+ kycWithdrawalDelay?: TalerProtocolDuration;
+
+ /**
* Secret seed used to derive planchets.
* Stored since planchets are created lazily.
*/
@@ -1885,7 +1890,6 @@ export enum PeerPushDebitStatus {
PendingReady = 0x0100_0001,
AbortingDeletePurse = 0x0103_0000,
-
SuspendedCreatePurse = 0x0110_0000,
SuspendedReady = 0x0110_0001,
SuspendedAbortingDeletePurse = 0x0113_0000,
diff --git a/packages/taler-wallet-core/src/withdraw.ts b/packages/taler-wallet-core/src/withdraw.ts
@@ -2102,7 +2102,28 @@ async function processWithdrawalGroupPendingKyc(
});
} else if (kycStatusRes.status === HttpStatusCode.Accepted) {
logger.info("kyc not done yet, long-poll remains pending");
- return TaskRunResult.longpollReturnedPending();
+ // We know that KYC isn't done, but we don't know whether
+ // it's required for the withdrawal. It might only
+ // be required for a *different* operation.
+ // Thus we attempt withdrawal after a delay.
+ await ctx.transition({}, async (rec) => {
+ if (!rec) {
+ return TransitionResult.stay();
+ }
+ switch (rec.status) {
+ case WithdrawalGroupStatus.PendingKyc: {
+ // Try withdrawal again.
+ rec.status = WithdrawalGroupStatus.PendingReady;
+ rec.kycWithdrawalDelay = Duration.toTalerProtocolDuration(
+ Duration.fromSpec({ minutes: 40 }),
+ );
+ return TransitionResult.transition(rec);
+ }
+ default:
+ return TransitionResult.stay();
+ }
+ });
+ return TaskRunResult.progress();
} else {
throw Error(`unexpected response from kyc-check (${kycStatusRes.status})`);
}
@@ -2261,6 +2282,28 @@ async function processWithdrawalGroupPendingReady(
const { withdrawalGroupId } = withdrawalGroup;
const ctx = new WithdrawTransactionContext(wex, withdrawalGroupId);
+ if (withdrawalGroup.kycWithdrawalDelay) {
+ // We've been asked to delay the next withdrawal,
+ // as we're waiting for KYC.
+ const delay = Duration.fromTalerProtocolDuration(
+ withdrawalGroup.kycWithdrawalDelay,
+ );
+ await ctx.transition({}, async (wg) => {
+ if (!wg) {
+ return TransitionResult.stay();
+ }
+ switch (wg.status) {
+ case WithdrawalGroupStatus.PendingReady:
+ delete wg.kycWithdrawalDelay;
+ return TransitionResult.transition(wg);
+ default:
+ }
+ return TransitionResult.stay();
+ });
+ const nextRun = AbsoluteTime.addDuration(AbsoluteTime.now(), delay);
+ return TaskRunResult.runAgainAt(nextRun);
+ }
+
checkDbInvariant(
withdrawalGroup.denomsSel !== undefined,
"can't process uninitialized exchange",