aboutsummaryrefslogtreecommitdiff
path: root/packages/taler-wallet-core/src/operations/deposits.ts
diff options
context:
space:
mode:
Diffstat (limited to 'packages/taler-wallet-core/src/operations/deposits.ts')
-rw-r--r--packages/taler-wallet-core/src/operations/deposits.ts114
1 files changed, 81 insertions, 33 deletions
diff --git a/packages/taler-wallet-core/src/operations/deposits.ts b/packages/taler-wallet-core/src/operations/deposits.ts
index f5ea41e01..6e56b0897 100644
--- a/packages/taler-wallet-core/src/operations/deposits.ts
+++ b/packages/taler-wallet-core/src/operations/deposits.ts
@@ -40,6 +40,7 @@ import {
j2s,
Logger,
MerchantContractTerms,
+ NotificationType,
parsePaytoUri,
PayCoinSelection,
PrepareDepositRequest,
@@ -49,9 +50,9 @@ import {
TalerErrorCode,
TalerProtocolTimestamp,
TrackTransaction,
+ TransactionMajorState,
+ TransactionMinorState,
TransactionState,
- TransactionStateInfo,
- TransactionSubstate,
TransactionType,
URL,
WireFee,
@@ -60,13 +61,16 @@ import {
DenominationRecord,
DepositGroupRecord,
OperationStatus,
- TransactionStatus,
+ DepositElementStatus,
} from "../db.js";
import { TalerError } from "@gnu-taler/taler-util";
import { getTotalRefreshCost, KycPendingInfo, KycUserType } from "../index.js";
import { InternalWalletState } from "../internal-wallet-state.js";
import { readSuccessResponseJsonOrThrow } from "@gnu-taler/taler-util/http";
-import { OperationAttemptResult } from "../util/retries.js";
+import {
+ OperationAttemptResult,
+ OperationAttemptResultType,
+} from "../util/retries.js";
import { spendCoins } from "./common.js";
import { getExchangeDetails } from "./exchanges.js";
import {
@@ -89,15 +93,13 @@ const logger = new Logger("deposits.ts");
* Get the (DD37-style) transaction status based on the
* database record of a deposit group.
*/
-export async function computeDepositTransactionStatus(
- ws: InternalWalletState,
+export function computeDepositTransactionStatus(
dg: DepositGroupRecord,
-): Promise<TransactionStateInfo> {
+): TransactionState {
switch (dg.operationStatus) {
case OperationStatus.Finished: {
return {
- txState: TransactionState.Done,
- txSubstate: TransactionSubstate.None,
+ major: TransactionMajorState.Done,
};
}
case OperationStatus.Pending: {
@@ -110,10 +112,10 @@ export async function computeDepositTransactionStatus(
numDeposited++;
}
switch (dg.transactionPerCoin[i]) {
- case TransactionStatus.KycRequired:
+ case DepositElementStatus.KycRequired:
numKycRequired++;
break;
- case TransactionStatus.Wired:
+ case DepositElementStatus.Wired:
numWired++;
break;
}
@@ -121,21 +123,21 @@ export async function computeDepositTransactionStatus(
if (numKycRequired > 0) {
return {
- txState: TransactionState.Pending,
- txSubstate: TransactionSubstate.DepositKycRequired,
+ major: TransactionMajorState.Pending,
+ minor: TransactionMinorState.KycRequired,
};
}
if (numDeposited == numTotal) {
return {
- txState: TransactionState.Pending,
- txSubstate: TransactionSubstate.DepositPendingTrack,
+ major: TransactionMajorState.Pending,
+ minor: TransactionMinorState.Track,
};
}
return {
- txState: TransactionState.Pending,
- txSubstate: TransactionSubstate.DepositPendingInitial,
+ major: TransactionMajorState.Pending,
+ minor: TransactionMinorState.Deposit,
};
}
default:
@@ -221,6 +223,13 @@ export async function processDepositGroup(
return OperationAttemptResult.finishedEmpty();
}
+ const transactionId = constructTransactionIdentifier({
+ tag: TransactionType.Deposit,
+ depositGroupId,
+ });
+
+ const txStateOld = computeDepositTransactionStatus(depositGroup);
+
const contractData = extractContractData(
depositGroup.contractTermsRaw,
depositGroup.contractTermsHash,
@@ -239,7 +248,7 @@ export async function processDepositGroup(
for (let i = 0; i < depositPermissions.length; i++) {
const perm = depositPermissions[i];
- let updatedDeposit: boolean | undefined = undefined;
+ let updatedDeposit: boolean = false;
if (!depositGroup.depositedPerCoin[i]) {
const requestBody: ExchangeDepositRequest = {
@@ -270,7 +279,7 @@ export async function processDepositGroup(
updatedDeposit = true;
}
- let updatedTxStatus: TransactionStatus | undefined = undefined;
+ let updatedTxStatus: DepositElementStatus | undefined = undefined;
type ValueOf<T> = T[keyof T];
let newWiredTransaction:
@@ -280,12 +289,12 @@ export async function processDepositGroup(
}
| undefined;
- if (depositGroup.transactionPerCoin[i] !== TransactionStatus.Wired) {
- const track = await trackDepositPermission(ws, depositGroup, perm);
+ if (depositGroup.transactionPerCoin[i] !== DepositElementStatus.Wired) {
+ const track = await trackDeposit(ws, depositGroup, perm);
if (track.type === "accepted") {
if (!track.kyc_ok && track.requirement_row !== undefined) {
- updatedTxStatus = TransactionStatus.KycRequired;
+ updatedTxStatus = DepositElementStatus.KycRequired;
const { requirement_row: requirementRow } = track;
const paytoHash = encodeCrock(
hashTruncate32(stringToBytes(depositGroup.wire.payto_uri + "\0")),
@@ -297,10 +306,10 @@ export async function processDepositGroup(
"individual",
);
} else {
- updatedTxStatus = TransactionStatus.Accepted;
+ updatedTxStatus = DepositElementStatus.Accepted;
}
} else if (track.type === "wired") {
- updatedTxStatus = TransactionStatus.Wired;
+ updatedTxStatus = DepositElementStatus.Wired;
const payto = parsePaytoUri(depositGroup.wire.payto_uri);
if (!payto) {
@@ -327,11 +336,11 @@ export async function processDepositGroup(
id: track.exchange_sig,
};
} else {
- updatedTxStatus = TransactionStatus.Unknown;
+ updatedTxStatus = DepositElementStatus.Unknown;
}
}
- if (updatedTxStatus !== undefined || updatedDeposit !== undefined) {
+ if (updatedTxStatus !== undefined || updatedDeposit) {
await ws.db
.mktx((x) => [x.depositGroups])
.runReadWrite(async (tx) => {
@@ -358,18 +367,18 @@ export async function processDepositGroup(
}
}
- await ws.db
+ const txStatusNew = await ws.db
.mktx((x) => [x.depositGroups])
.runReadWrite(async (tx) => {
const dg = await tx.depositGroups.get(depositGroupId);
if (!dg) {
- return;
+ return undefined;
}
let allDepositedAndWired = true;
for (let i = 0; i < depositGroup.depositedPerCoin.length; i++) {
if (
!depositGroup.depositedPerCoin[i] ||
- depositGroup.transactionPerCoin[i] !== TransactionStatus.Wired
+ depositGroup.transactionPerCoin[i] !== DepositElementStatus.Wired
) {
allDepositedAndWired = false;
break;
@@ -380,8 +389,36 @@ export async function processDepositGroup(
dg.operationStatus = OperationStatus.Finished;
await tx.depositGroups.put(dg);
}
+ return computeDepositTransactionStatus(dg);
+ });
+
+ if (!txStatusNew) {
+ // Doesn't exist anymore!
+ return OperationAttemptResult.finishedEmpty();
+ }
+
+ // Notify if state transitioned
+ if (
+ txStateOld.major !== txStatusNew.major ||
+ txStateOld.minor !== txStatusNew.minor
+ ) {
+ ws.notify({
+ type: NotificationType.TransactionStateTransition,
+ transactionId,
+ oldTxState: txStateOld,
+ newTxState: txStatusNew,
});
- return OperationAttemptResult.finishedEmpty();
+ }
+
+ // FIXME: consider other cases like aborting, suspend, ...
+ if (
+ txStatusNew.major === TransactionMajorState.Pending ||
+ txStatusNew.major === TransactionMajorState.Aborting
+ ) {
+ return OperationAttemptResult.pendingEmpty();
+ } else {
+ return OperationAttemptResult.finishedEmpty();
+ }
}
async function getExchangeWireFee(
@@ -428,7 +465,7 @@ async function getExchangeWireFee(
return fee;
}
-async function trackDepositPermission(
+async function trackDeposit(
ws: InternalWalletState,
depositGroup: DepositGroupRecord,
dp: CoinDepositPermission,
@@ -448,6 +485,7 @@ async function trackDepositPermission(
});
url.searchParams.set("merchant_sig", sigResp.sig);
const httpResp = await ws.http.fetch(url.href, { method: "GET" });
+ logger.trace(`deposits response status: ${httpResp.status}`);
switch (httpResp.status) {
case HttpStatusCode.Accepted: {
const accepted = await readSuccessResponseJsonOrThrow(
@@ -710,7 +748,7 @@ export async function createDepositGroup(
timestampCreated: AbsoluteTime.toTimestamp(now),
timestampFinished: undefined,
transactionPerCoin: payCoinSel.coinSel.coinPubs.map(
- () => TransactionStatus.Unknown,
+ () => DepositElementStatus.Unknown,
),
payCoinSelection: payCoinSel.coinSel,
payCoinSelectionUid: encodeCrock(getRandomBytes(32)),
@@ -733,7 +771,7 @@ export async function createDepositGroup(
depositGroupId,
});
- await ws.db
+ const newTxState = await ws.db
.mktx((x) => [
x.depositGroups,
x.coins,
@@ -752,8 +790,18 @@ export async function createDepositGroup(
refreshReason: RefreshReason.PayDeposit,
});
await tx.depositGroups.put(depositGroup);
+ return computeDepositTransactionStatus(depositGroup);
});
+ ws.notify({
+ type: NotificationType.TransactionStateTransition,
+ transactionId,
+ oldTxState: {
+ major: TransactionMajorState.None,
+ },
+ newTxState,
+ });
+
return {
depositGroupId,
transactionId,