wallet-core: report possible actions in transactions list

This commit is contained in:
Florian Dold 2023-05-30 15:58:28 +02:00
parent 000359a5e7
commit 2a92ca8732
No known key found for this signature in database
GPG Key ID: D2E4F00F29D02A4B
12 changed files with 407 additions and 55 deletions

View File

@ -125,6 +125,15 @@ export enum TransactionMinorState {
AcceptRefund = "accept-refund",
}
export enum TransactionAction {
Delete = "delete",
Suspend = "suspend",
Resume = "resume",
Abort = "abort",
Fail = "fail",
Retry = "retry",
}
export interface TransactionsResponse {
// a list of past and pending transactions sorted by pending, timestamp and transactionId.
// In case two events are both pending and have the same timestamp,
@ -149,6 +158,11 @@ export interface TransactionCommon {
*/
txState: TransactionState;
/**
* Possible transitions based on the current state.
*/
txActions: string[];
/**
* Raw amount of the transaction (exclusive of fees or other extra costs).
*/

View File

@ -1131,7 +1131,7 @@ export enum PurchaseStatus {
/**
* Proposal downloaded, but the user needs to accept/reject it.
*/
Proposed = 30,
DialogProposed = 30,
/**
* The user has rejected the proposal.

View File

@ -418,7 +418,7 @@ export async function exportBackup(
break;
case PurchaseStatus.PendingPayingReplay:
case PurchaseStatus.PendingDownloadingProposal:
case PurchaseStatus.Proposed:
case PurchaseStatus.DialogProposed:
case PurchaseStatus.PendingPaying:
propStatus = BackupProposalStatus.Proposed;
break;

View File

@ -578,7 +578,7 @@ export async function importBackup(
proposalStatus = PurchaseStatus.Done;
break;
case BackupProposalStatus.Proposed:
proposalStatus = PurchaseStatus.Proposed;
proposalStatus = PurchaseStatus.DialogProposed;
break;
case BackupProposalStatus.PermanentlyFailed:
proposalStatus = PurchaseStatus.AbortedIncompletePayment;

View File

@ -59,11 +59,11 @@ import {
TransactionType,
URL,
WireFee,
TransactionAction,
} from "@gnu-taler/taler-util";
import {
DenominationRecord,
DepositGroupRecord,
OperationStatus,
DepositElementStatus,
} from "../db.js";
import { TalerError } from "@gnu-taler/taler-util";
@ -115,6 +115,7 @@ export function computeDepositTransactionStatus(
major: TransactionMajorState.Done,
};
}
// FIXME: We should actually use separate pending states for this!
case DepositOperationStatus.Pending: {
const numTotal = dg.payCoinSelection.coinPubs.length;
let numDeposited = 0;
@ -134,9 +135,6 @@ export function computeDepositTransactionStatus(
}
}
logger.info(`num total ${numTotal}`);
logger.info(`num deposited ${numDeposited}`);
if (numKycRequired > 0) {
return {
major: TransactionMajorState.Pending,
@ -181,6 +179,57 @@ export function computeDepositTransactionStatus(
}
}
export function computeDepositTransactionActions(
dg: DepositGroupRecord,
): TransactionAction[] {
switch (dg.operationStatus) {
case DepositOperationStatus.Finished: {
return [TransactionAction.Delete];
}
case DepositOperationStatus.Pending: {
const numTotal = dg.payCoinSelection.coinPubs.length;
let numDeposited = 0;
let numKycRequired = 0;
let numWired = 0;
for (let i = 0; i < numTotal; i++) {
if (dg.depositedPerCoin[i]) {
numDeposited++;
}
switch (dg.transactionPerCoin[i]) {
case DepositElementStatus.KycRequired:
numKycRequired++;
break;
case DepositElementStatus.Wired:
numWired++;
break;
}
}
if (numKycRequired > 0) {
return [TransactionAction.Suspend, TransactionAction.Fail];
}
if (numDeposited == numTotal) {
return [TransactionAction.Suspend, TransactionAction.Fail];
}
return [TransactionAction.Suspend, TransactionAction.Abort];
}
case DepositOperationStatus.Suspended:
return [TransactionAction.Resume];
case DepositOperationStatus.Aborting:
return [TransactionAction.Fail, TransactionAction.Suspend];
case DepositOperationStatus.Aborted:
return [TransactionAction.Delete];
case DepositOperationStatus.Failed:
return [TransactionAction.Delete];
case DepositOperationStatus.SuspendedAborting:
return [TransactionAction.Resume, TransactionAction.Fail];
default:
throw Error(`unexpected deposit group state (${dg.operationStatus})`);
}
}
export async function suspendDepositGroup(
ws: InternalWalletState,
depositGroupId: string,
@ -309,7 +358,7 @@ export async function abortDepositGroup(
notifyTransition(ws, transactionId, transitionInfo);
}
export async function cancelAbortingDepositGroup(
export async function failDepositTransaction(
ws: InternalWalletState,
depositGroupId: string,
): Promise<void> {

View File

@ -70,6 +70,7 @@ import {
TalerProtocolTimestamp,
TalerProtocolViolationError,
TalerUriAction,
TransactionAction,
TransactionMajorState,
TransactionMinorState,
TransactionState,
@ -547,7 +548,7 @@ async function processDownloadProposal(
await tx.purchases.put(p);
}
} else {
p.purchaseStatus = PurchaseStatus.Proposed;
p.purchaseStatus = PurchaseStatus.DialogProposed;
await tx.purchases.put(p);
}
const newTxState = computePayMerchantTransactionState(p);
@ -994,7 +995,7 @@ export async function checkPaymentByProposalId(
return tx.purchases.get(proposalId);
});
if (!purchase || purchase.purchaseStatus === PurchaseStatus.Proposed) {
if (!purchase || purchase.purchaseStatus === PurchaseStatus.DialogProposed) {
// If not already paid, check if we could pay for it.
const res = await selectPayCoinsNew(ws, {
auditors: [],
@ -1417,7 +1418,7 @@ export async function confirmPay(
}
const oldTxState = computePayMerchantTransactionState(p);
switch (p.purchaseStatus) {
case PurchaseStatus.Proposed:
case PurchaseStatus.DialogProposed:
p.payInfo = {
payCoinSelection: coinSelection,
payCoinSelectionUid: encodeCrock(getRandomBytes(16)),
@ -1498,7 +1499,7 @@ export async function processPurchase(
case PurchaseStatus.FailedClaim:
case PurchaseStatus.Done:
case PurchaseStatus.RepurchaseDetected:
case PurchaseStatus.Proposed:
case PurchaseStatus.DialogProposed:
case PurchaseStatus.AbortedProposalRefused:
case PurchaseStatus.AbortedIncompletePayment:
case PurchaseStatus.SuspendedAbortingWithRefund:
@ -1713,7 +1714,7 @@ export async function refuseProposal(
logger.trace(`proposal ${proposalId} not found, won't refuse proposal`);
return undefined;
}
if (proposal.purchaseStatus !== PurchaseStatus.Proposed) {
if (proposal.purchaseStatus !== PurchaseStatus.DialogProposed) {
return undefined;
}
const oldTxState = computePayMerchantTransactionState(proposal);
@ -1791,7 +1792,7 @@ export async function abortPayMerchant(
ws.workAvailable.trigger();
}
export async function cancelAbortingPaymentTransaction(
export async function failPaymentTransaction(
ws: InternalWalletState,
proposalId: string,
): Promise<void> {
@ -2023,7 +2024,7 @@ export function computePayMerchantTransactionState(
major: TransactionMajorState.SuspendedAborting,
};
// Dialog States
case PurchaseStatus.Proposed:
case PurchaseStatus.DialogProposed:
return {
major: TransactionMajorState.Dialog,
minor: TransactionMinorState.MerchantOrderProposed,
@ -2060,6 +2061,68 @@ export function computePayMerchantTransactionState(
}
}
export function computePayMerchantTransactionActions(
purchaseRecord: PurchaseRecord,
): TransactionAction[] {
switch (purchaseRecord.purchaseStatus) {
// Pending States
case PurchaseStatus.PendingDownloadingProposal:
return [TransactionAction.Suspend, TransactionAction.Abort];
case PurchaseStatus.PendingPaying:
return [TransactionAction.Suspend, TransactionAction.Abort];
case PurchaseStatus.PendingPayingReplay:
// Special "abort" since it goes back to "done".
return [TransactionAction.Suspend, TransactionAction.Abort];
case PurchaseStatus.PendingQueryingAutoRefund:
// Special "abort" since it goes back to "done".
return [TransactionAction.Suspend, TransactionAction.Abort];
case PurchaseStatus.PendingQueryingRefund:
// Special "abort" since it goes back to "done".
return [TransactionAction.Suspend, TransactionAction.Abort];
case PurchaseStatus.PendingAcceptRefund:
// Special "abort" since it goes back to "done".
return [TransactionAction.Suspend, TransactionAction.Abort];
// Suspended Pending States
case PurchaseStatus.SuspendedDownloadingProposal:
return [TransactionAction.Resume, TransactionAction.Abort];
case PurchaseStatus.SuspendedPaying:
return [TransactionAction.Resume, TransactionAction.Abort];
case PurchaseStatus.SuspendedPayingReplay:
// Special "abort" since it goes back to "done".
return [TransactionAction.Resume, TransactionAction.Abort];
case PurchaseStatus.SuspendedQueryingAutoRefund:
// Special "abort" since it goes back to "done".
return [TransactionAction.Resume, TransactionAction.Abort];
case PurchaseStatus.SuspendedQueryingRefund:
// Special "abort" since it goes back to "done".
return [TransactionAction.Resume, TransactionAction.Abort];
case PurchaseStatus.SuspendedPendingAcceptRefund:
// Special "abort" since it goes back to "done".
return [TransactionAction.Resume, TransactionAction.Abort];
// Aborting States
case PurchaseStatus.AbortingWithRefund:
return [TransactionAction.Fail, TransactionAction.Suspend];
case PurchaseStatus.SuspendedAbortingWithRefund:
return [TransactionAction.Fail, TransactionAction.Resume];
// Dialog States
case PurchaseStatus.DialogProposed:
return [];
// Final States
case PurchaseStatus.AbortedProposalRefused:
return [TransactionAction.Delete];
case PurchaseStatus.Done:
return [TransactionAction.Delete];
case PurchaseStatus.RepurchaseDetected:
return [TransactionAction.Delete];
case PurchaseStatus.AbortedIncompletePayment:
return [TransactionAction.Delete];
case PurchaseStatus.FailedClaim:
return [TransactionAction.Delete];
case PurchaseStatus.FailedAbort:
return [TransactionAction.Delete];
}
}
async function processPurchaseAutoRefund(
ws: InternalWalletState,
purchase: PurchaseRecord,

View File

@ -79,6 +79,7 @@ import {
TransactionMajorState,
TransactionMinorState,
TalerPreciseTimestamp,
TransactionAction,
} from "@gnu-taler/taler-util";
import { SpendCoinDetails } from "../crypto/cryptoImplementation.js";
import {
@ -2008,7 +2009,36 @@ export function computePeerPushDebitTransactionState(
case PeerPushPaymentInitiationStatus.Failed:
return {
major: TransactionMajorState.Failed,
}
};
}
}
export function computePeerPushDebitTransactionActions(
ppiRecord: PeerPushPaymentInitiationRecord,
): TransactionAction[] {
switch (ppiRecord.status) {
case PeerPushPaymentInitiationStatus.PendingCreatePurse:
return [TransactionAction.Abort, TransactionAction.Suspend];
case PeerPushPaymentInitiationStatus.PendingReady:
return [TransactionAction.Abort, TransactionAction.Suspend];
case PeerPushPaymentInitiationStatus.Aborted:
return [TransactionAction.Delete];
case PeerPushPaymentInitiationStatus.AbortingDeletePurse:
return [TransactionAction.Suspend, TransactionAction.Fail];
case PeerPushPaymentInitiationStatus.AbortingRefresh:
return [TransactionAction.Suspend, TransactionAction.Fail];
case PeerPushPaymentInitiationStatus.SuspendedAbortingDeletePurse:
return [TransactionAction.Resume, TransactionAction.Fail];
case PeerPushPaymentInitiationStatus.SuspendedAbortingRefresh:
return [TransactionAction.Resume, TransactionAction.Fail];
case PeerPushPaymentInitiationStatus.SuspendedCreatePurse:
return [TransactionAction.Resume, TransactionAction.Abort];
case PeerPushPaymentInitiationStatus.SuspendedReady:
return [TransactionAction.Suspend, TransactionAction.Abort];
case PeerPushPaymentInitiationStatus.Done:
return [TransactionAction.Delete];
case PeerPushPaymentInitiationStatus.Failed:
return [TransactionAction.Delete];
}
}
@ -2072,7 +2102,7 @@ export async function abortPeerPushDebitTransaction(
notifyTransition(ws, transactionId, transitionInfo);
}
export async function cancelAbortingPeerPushDebitTransaction(
export async function failPeerPushDebitTransaction(
ws: InternalWalletState,
pursePub: string,
) {
@ -2316,8 +2346,7 @@ export async function abortPeerPullDebitTransaction(
notifyTransition(ws, transactionId, transitionInfo);
}
export async function cancelAbortingPeerPullDebitTransaction(
export async function failPeerPullDebitTransaction(
ws: InternalWalletState,
peerPullPaymentIncomingId: string,
) {
@ -2501,7 +2530,6 @@ export async function suspendPeerPushCreditTransaction(
notifyTransition(ws, transactionId, transitionInfo);
}
export async function abortPeerPushCreditTransaction(
ws: InternalWalletState,
peerPushPaymentIncomingId: string,
@ -2568,7 +2596,7 @@ export async function abortPeerPushCreditTransaction(
notifyTransition(ws, transactionId, transitionInfo);
}
export async function cancelAbortingPeerPushCreditTransaction(
export async function failPeerPushCreditTransaction(
ws: InternalWalletState,
peerPushPaymentIncomingId: string,
) {
@ -2765,7 +2793,7 @@ export async function abortPeerPullCreditTransaction(
notifyTransition(ws, transactionId, transitionInfo);
}
export async function cancelAbortingPeerPullCreditTransaction(
export async function failPeerPullCreditTransaction(
ws: InternalWalletState,
pursePub: string,
) {
@ -2996,12 +3024,41 @@ export function computePeerPushCreditTransactionState(
};
case PeerPushPaymentIncomingStatus.Aborted:
return {
major: TransactionMajorState.Aborted
major: TransactionMajorState.Aborted,
};
case PeerPushPaymentIncomingStatus.Failed:
return {
major: TransactionMajorState.Failed,
}
};
default:
assertUnreachable(pushCreditRecord.status);
}
}
export function computePeerPushCreditTransactionActions(
pushCreditRecord: PeerPushPaymentIncomingRecord,
): TransactionAction[] {
switch (pushCreditRecord.status) {
case PeerPushPaymentIncomingStatus.DialogProposed:
return [];
case PeerPushPaymentIncomingStatus.PendingMerge:
return [TransactionAction.Abort, TransactionAction.Suspend];
case PeerPushPaymentIncomingStatus.Done:
return [TransactionAction.Delete];
case PeerPushPaymentIncomingStatus.PendingMergeKycRequired:
return [TransactionAction.Abort, TransactionAction.Suspend];
case PeerPushPaymentIncomingStatus.PendingWithdrawing:
return [TransactionAction.Suspend, TransactionAction.Fail];
case PeerPushPaymentIncomingStatus.SuspendedMerge:
return [TransactionAction.Resume, TransactionAction.Abort];
case PeerPushPaymentIncomingStatus.SuspendedMergeKycRequired:
return [TransactionAction.Resume, TransactionAction.Abort];
case PeerPushPaymentIncomingStatus.SuspendedWithdrawing:
return [TransactionAction.Resume, TransactionAction.Fail];
case PeerPushPaymentIncomingStatus.Aborted:
return [TransactionAction.Delete];
case PeerPushPaymentIncomingStatus.Failed:
return [TransactionAction.Delete];
default:
assertUnreachable(pushCreditRecord.status);
}
@ -3076,6 +3133,39 @@ export function computePeerPullCreditTransactionState(
}
}
export function computePeerPullCreditTransactionActions(
pullCreditRecord: PeerPullPaymentInitiationRecord,
): TransactionAction[] {
switch (pullCreditRecord.status) {
case PeerPullPaymentInitiationStatus.PendingCreatePurse:
return [TransactionAction.Abort, TransactionAction.Suspend];
case PeerPullPaymentInitiationStatus.PendingMergeKycRequired:
return [TransactionAction.Abort, TransactionAction.Suspend];
case PeerPullPaymentInitiationStatus.PendingReady:
return [TransactionAction.Abort, TransactionAction.Suspend];
case PeerPullPaymentInitiationStatus.DonePurseDeposited:
return [TransactionAction.Delete];
case PeerPullPaymentInitiationStatus.PendingWithdrawing:
return [TransactionAction.Abort, TransactionAction.Suspend];
case PeerPullPaymentInitiationStatus.SuspendedCreatePurse:
return [TransactionAction.Resume, TransactionAction.Abort];
case PeerPullPaymentInitiationStatus.SuspendedReady:
return [TransactionAction.Abort, TransactionAction.Resume];
case PeerPullPaymentInitiationStatus.SuspendedWithdrawing:
return [TransactionAction.Resume, TransactionAction.Fail];
case PeerPullPaymentInitiationStatus.SuspendedMergeKycRequired:
return [TransactionAction.Resume, TransactionAction.Fail];
case PeerPullPaymentInitiationStatus.Aborted:
return [TransactionAction.Delete];
case PeerPullPaymentInitiationStatus.AbortingDeletePurse:
return [TransactionAction.Suspend, TransactionAction.Fail];
case PeerPullPaymentInitiationStatus.Failed:
return [TransactionAction.Delete];
case PeerPullPaymentInitiationStatus.SuspendedAbortingDeletePurse:
return [TransactionAction.Resume, TransactionAction.Fail];
}
}
export function computePeerPullDebitTransactionState(
pullDebitRecord: PeerPullPaymentIncomingRecord,
): TransactionState {
@ -3119,3 +3209,26 @@ export function computePeerPullDebitTransactionState(
};
}
}
export function computePeerPullDebitTransactionActions(
pullDebitRecord: PeerPullPaymentIncomingRecord,
): TransactionAction[] {
switch (pullDebitRecord.status) {
case PeerPullDebitRecordStatus.DialogProposed:
return [];
case PeerPullDebitRecordStatus.PendingDeposit:
return [TransactionAction.Abort, TransactionAction.Suspend];
case PeerPullDebitRecordStatus.DonePaid:
return [TransactionAction.Delete];
case PeerPullDebitRecordStatus.SuspendedDeposit:
return [TransactionAction.Resume, TransactionAction.Abort];
case PeerPullDebitRecordStatus.Aborted:
return [TransactionAction.Delete];
case PeerPullDebitRecordStatus.AbortingRefresh:
return [TransactionAction.Fail, TransactionAction.Suspend];
case PeerPullDebitRecordStatus.Failed:
return [TransactionAction.Delete];
case PeerPullDebitRecordStatus.SuspendedAbortingRefresh:
return [TransactionAction.Resume, TransactionAction.Fail];
}
}

View File

@ -50,6 +50,7 @@ import {
TalerErrorDetail,
TalerPreciseTimestamp,
TalerProtocolTimestamp,
TransactionAction,
TransactionMajorState,
TransactionState,
TransactionType,
@ -96,7 +97,10 @@ import {
PendingTaskType,
WalletConfig,
} from "../index.js";
import { constructTransactionIdentifier, notifyTransition } from "./transactions.js";
import {
constructTransactionIdentifier,
notifyTransition,
} from "./transactions.js";
const logger = new Logger("refresh.ts");
@ -1076,8 +1080,12 @@ export async function createRefreshGroup(
* Timestamp after which the wallet would do the next check for an auto-refresh.
*/
function getAutoRefreshCheckThreshold(d: DenominationRecord): AbsoluteTime {
const expireWithdraw = AbsoluteTime.fromProtocolTimestamp(d.stampExpireWithdraw);
const expireDeposit = AbsoluteTime.fromProtocolTimestamp(d.stampExpireDeposit);
const expireWithdraw = AbsoluteTime.fromProtocolTimestamp(
d.stampExpireWithdraw,
);
const expireDeposit = AbsoluteTime.fromProtocolTimestamp(
d.stampExpireDeposit,
);
const delta = AbsoluteTime.difference(expireWithdraw, expireDeposit);
const deltaDiv = durationMul(delta, 0.75);
return AbsoluteTime.addDuration(expireWithdraw, deltaDiv);
@ -1087,8 +1095,12 @@ function getAutoRefreshCheckThreshold(d: DenominationRecord): AbsoluteTime {
* Timestamp after which the wallet would do an auto-refresh.
*/
function getAutoRefreshExecuteThreshold(d: DenominationRecord): AbsoluteTime {
const expireWithdraw = AbsoluteTime.fromProtocolTimestamp(d.stampExpireWithdraw);
const expireDeposit = AbsoluteTime.fromProtocolTimestamp(d.stampExpireDeposit);
const expireWithdraw = AbsoluteTime.fromProtocolTimestamp(
d.stampExpireWithdraw,
);
const expireDeposit = AbsoluteTime.fromProtocolTimestamp(
d.stampExpireDeposit,
);
const delta = AbsoluteTime.difference(expireWithdraw, expireDeposit);
const deltaDiv = durationMul(delta, 0.5);
return AbsoluteTime.addDuration(expireWithdraw, deltaDiv);
@ -1175,7 +1187,8 @@ export async function autoRefresh(
logger.info(
`next refresh check at ${AbsoluteTime.toIsoString(minCheckThreshold)}`,
);
exchange.nextRefreshCheck = AbsoluteTime.toPreciseTimestamp(minCheckThreshold);
exchange.nextRefreshCheck =
AbsoluteTime.toPreciseTimestamp(minCheckThreshold);
await tx.exchanges.put(exchange);
});
return OperationAttemptResult.finishedEmpty();
@ -1204,6 +1217,21 @@ export function computeRefreshTransactionState(
}
}
export function computeRefreshTransactionActions(
rg: RefreshGroupRecord,
): TransactionAction[] {
switch (rg.operationStatus) {
case RefreshOperationStatus.Finished:
return [TransactionAction.Delete];
case RefreshOperationStatus.Failed:
return [TransactionAction.Delete];
case RefreshOperationStatus.Pending:
return [TransactionAction.Suspend, TransactionAction.Fail];
case RefreshOperationStatus.Suspended:
return [TransactionAction.Resume, TransactionAction.Fail];
}
}
export async function suspendRefreshGroup(
ws: InternalWalletState,
refreshGroupId: string,
@ -1292,7 +1320,7 @@ export async function resumeRefreshGroup(
notifyTransition(ws, transactionId, transitionInfo);
}
export async function cancelAbortingRefreshGroup(
export async function failRefreshGroup(
ws: InternalWalletState,
refreshGroupId: string,
): Promise<void> {
@ -1321,7 +1349,7 @@ export async function abortRefreshGroup(
let newStatus: RefreshOperationStatus | undefined;
switch (dg.operationStatus) {
case RefreshOperationStatus.Finished:
break;;
break;
case RefreshOperationStatus.Pending:
case RefreshOperationStatus.Suspended:
newStatus = RefreshOperationStatus.Failed;

View File

@ -36,6 +36,7 @@ import {
TalerPreciseTimestamp,
TalerProtocolTimestamp,
TipPlanchetDetail,
TransactionAction,
TransactionMajorState,
TransactionMinorState,
TransactionState,
@ -111,6 +112,24 @@ export function computeTipTransactionStatus(
}
}
export function computeTipTransactionActions(
tipRecord: TipRecord,
): TransactionAction[] {
switch (tipRecord.status) {
case TipRecordStatus.Done:
return [TransactionAction.Delete];
case TipRecordStatus.Aborted:
return [TransactionAction.Delete];
case TipRecordStatus.PendingPickup:
return [TransactionAction.Suspend, TransactionAction.Fail];
case TipRecordStatus.SuspendidPickup:
return [TransactionAction.Resume, TransactionAction.Fail];
default:
assertUnreachable(tipRecord.status);
}
}
export async function prepareTip(
ws: InternalWalletState,
talerTipUri: string,
@ -526,7 +545,7 @@ export async function resumeTipTransaction(
notifyTransition(ws, transactionId, transitionInfo);
}
export async function cancelAbortingTipTransaction(
export async function failTipTransaction(
ws: InternalWalletState,
walletTipId: string,
): Promise<void> {

View File

@ -73,32 +73,34 @@ import { constructTaskIdentifier, TaskIdentifiers } from "../util/retries.js";
import { resetOperationTimeout, TombstoneTag } from "./common.js";
import {
abortDepositGroup,
cancelAbortingDepositGroup,
failDepositTransaction,
computeDepositTransactionStatus,
deleteDepositGroup,
resumeDepositGroup,
suspendDepositGroup,
computeDepositTransactionActions,
} from "./deposits.js";
import { getExchangeDetails } from "./exchanges.js";
import {
abortPayMerchant,
cancelAbortingPaymentTransaction,
failPaymentTransaction,
computePayMerchantTransactionState,
computeRefundTransactionState,
expectProposalDownload,
extractContractData,
resumePayMerchant,
suspendPayMerchant,
computePayMerchantTransactionActions,
} from "./pay-merchant.js";
import {
abortPeerPullCreditTransaction,
abortPeerPullDebitTransaction,
abortPeerPushCreditTransaction,
abortPeerPushDebitTransaction,
cancelAbortingPeerPullCreditTransaction,
cancelAbortingPeerPullDebitTransaction,
cancelAbortingPeerPushCreditTransaction,
cancelAbortingPeerPushDebitTransaction,
failPeerPullCreditTransaction,
failPeerPullDebitTransaction,
failPeerPushCreditTransaction,
failPeerPushDebitTransaction,
computePeerPullCreditTransactionState,
computePeerPullDebitTransactionState,
computePeerPushCreditTransactionState,
@ -111,28 +113,35 @@ import {
suspendPeerPullDebitTransaction,
suspendPeerPushCreditTransaction,
suspendPeerPushDebitTransaction,
computePeerPushDebitTransactionActions,
computePeerPullDebitTransactionActions,
computePeerPullCreditTransactionActions,
computePeerPushCreditTransactionActions,
} from "./pay-peer.js";
import {
abortRefreshGroup,
cancelAbortingRefreshGroup,
failRefreshGroup,
computeRefreshTransactionState,
resumeRefreshGroup,
suspendRefreshGroup,
computeRefreshTransactionActions,
} from "./refresh.js";
import {
abortTipTransaction,
cancelAbortingTipTransaction,
failTipTransaction,
computeTipTransactionStatus,
resumeTipTransaction,
suspendTipTransaction,
computeTipTransactionActions,
} from "./tip.js";
import {
abortWithdrawalTransaction,
augmentPaytoUrisForWithdrawal,
cancelAbortingWithdrawalTransaction,
failWithdrawalTransaction,
computeWithdrawalTransactionStatus,
resumeWithdrawalTransaction,
suspendWithdrawalTransaction,
computeWithdrawalTransactionActions,
} from "./withdraw.js";
const logger = new Logger("taler-wallet-core:transactions.ts");
@ -429,6 +438,7 @@ function buildTransactionForPushPaymentDebit(
return {
type: TransactionType.PeerPushDebit,
txState: computePeerPushDebitTransactionState(pi),
txActions: computePeerPushDebitTransactionActions(pi),
amountEffective: pi.totalCost,
amountRaw: pi.amount,
exchangeBaseUrl: pi.exchangeBaseUrl,
@ -456,6 +466,7 @@ function buildTransactionForPullPaymentDebit(
return {
type: TransactionType.PeerPullDebit,
txState: computePeerPullDebitTransactionState(pi),
txActions: computePeerPullDebitTransactionActions(pi),
amountEffective: pi.coinSel?.totalCost
? pi.coinSel?.totalCost
: Amounts.stringify(pi.contractTerms.amount),
@ -503,6 +514,7 @@ function buildTransactionForPeerPullCredit(
return {
type: TransactionType.PeerPullCredit,
txState: computePeerPullCreditTransactionState(pullCredit),
txActions: computePeerPullCreditTransactionActions(pullCredit),
amountEffective: Amounts.stringify(wsr.denomsSel.totalCoinValue),
amountRaw: Amounts.stringify(wsr.instructedAmount),
exchangeBaseUrl: wsr.exchangeBaseUrl,
@ -533,6 +545,7 @@ function buildTransactionForPeerPullCredit(
return {
type: TransactionType.PeerPullCredit,
txState: computePeerPullCreditTransactionState(pullCredit),
txActions: computePeerPullCreditTransactionActions(pullCredit),
amountEffective: Amounts.stringify(pullCredit.estimatedAmountEffective),
amountRaw: Amounts.stringify(peerContractTerms.amount),
exchangeBaseUrl: pullCredit.exchangeBaseUrl,
@ -569,6 +582,7 @@ function buildTransactionForPeerPushCredit(
return {
type: TransactionType.PeerPushCredit,
txState: computePeerPushCreditTransactionState(pushInc),
txActions: computePeerPushCreditTransactionActions(pushInc),
amountEffective: Amounts.stringify(wsr.denomsSel.totalCoinValue),
amountRaw: Amounts.stringify(wsr.instructedAmount),
exchangeBaseUrl: wsr.exchangeBaseUrl,
@ -588,6 +602,7 @@ function buildTransactionForPeerPushCredit(
return {
type: TransactionType.PeerPushCredit,
txState: computePeerPushCreditTransactionState(pushInc),
txActions: computePeerPushCreditTransactionActions(pushInc),
// FIXME: This is wrong, needs to consider fees!
amountEffective: Amounts.stringify(peerContractTerms.amount),
amountRaw: Amounts.stringify(peerContractTerms.amount),
@ -615,6 +630,7 @@ function buildTransactionForBankIntegratedWithdraw(
return {
type: TransactionType.Withdrawal,
txState: computeWithdrawalTransactionStatus(wgRecord),
txActions: computeWithdrawalTransactionActions(wgRecord),
amountEffective: Amounts.stringify(wgRecord.denomsSel.totalCoinValue),
amountRaw: Amounts.stringify(wgRecord.instructedAmount),
withdrawalDetails: {
@ -656,6 +672,7 @@ function buildTransactionForManualWithdraw(
return {
type: TransactionType.Withdrawal,
txState: computeWithdrawalTransactionStatus(withdrawalGroup),
txActions: computeWithdrawalTransactionActions(withdrawalGroup),
amountEffective: Amounts.stringify(
withdrawalGroup.denomsSel.totalCoinValue,
),
@ -706,6 +723,7 @@ function buildTransactionForRefund(
refundGroupId: refundRecord.refundGroupId,
}),
txState: computeRefundTransactionState(refundRecord),
txActions: [],
paymentInfo,
};
}
@ -725,6 +743,7 @@ function buildTransactionForRefresh(
return {
type: TransactionType.Refresh,
txState: computeRefreshTransactionState(refreshGroupRecord),
txActions: computeRefreshTransactionActions(refreshGroupRecord),
refreshReason: refreshGroupRecord.reason,
amountEffective: Amounts.stringify(
Amounts.zeroOfCurrency(refreshGroupRecord.currency),
@ -759,6 +778,7 @@ function buildTransactionForDeposit(
return {
type: TransactionType.Deposit,
txState: computeDepositTransactionStatus(dg),
txActions: computeDepositTransactionActions(dg),
amountRaw: Amounts.stringify(dg.effectiveDepositAmount),
amountEffective: Amounts.stringify(dg.totalPayCost),
timestamp: dg.timestampCreated,
@ -791,6 +811,7 @@ function buildTransactionForTip(
return {
type: TransactionType.Tip,
txState: computeTipTransactionStatus(tipRecord),
txActions: computeTipTransactionActions(tipRecord),
amountEffective: Amounts.stringify(tipRecord.tipAmountEffective),
amountRaw: Amounts.stringify(tipRecord.tipAmountRaw),
timestamp: tipRecord.acceptedTimestamp,
@ -860,6 +881,7 @@ async function buildTransactionForPurchase(
return {
type: TransactionType.Payment,
txState: computePayMerchantTransactionState(purchaseRecord),
txActions: computePayMerchantTransactionActions(purchaseRecord),
amountRaw: Amounts.stringify(contractData.amount),
amountEffective: Amounts.stringify(purchaseRecord.payInfo.totalPayCost),
totalRefundRaw: Amounts.stringify(zero), // FIXME!
@ -1493,7 +1515,7 @@ export async function suspendTransaction(
}
}
export async function cancelAbortingTransaction(
export async function failTransaction(
ws: InternalWalletState,
transactionId: string,
): Promise<void> {
@ -1503,34 +1525,34 @@ export async function cancelAbortingTransaction(
}
switch (tx.tag) {
case TransactionType.Deposit:
await cancelAbortingDepositGroup(ws, tx.depositGroupId);
await failDepositTransaction(ws, tx.depositGroupId);
return;
case TransactionType.InternalWithdrawal:
case TransactionType.Withdrawal:
await cancelAbortingWithdrawalTransaction(ws, tx.withdrawalGroupId);
await failWithdrawalTransaction(ws, tx.withdrawalGroupId);
return;
case TransactionType.Payment:
await cancelAbortingPaymentTransaction(ws, tx.proposalId);
await failPaymentTransaction(ws, tx.proposalId);
return;
case TransactionType.Refund:
throw Error("can't do cancel-aborting on refund transaction");
case TransactionType.Tip:
await cancelAbortingTipTransaction(ws, tx.walletTipId);
await failTipTransaction(ws, tx.walletTipId);
return;
case TransactionType.Refresh:
await cancelAbortingRefreshGroup(ws, tx.refreshGroupId);
await failRefreshGroup(ws, tx.refreshGroupId);
return;
case TransactionType.PeerPullCredit:
await cancelAbortingPeerPullCreditTransaction(ws, tx.pursePub);
await failPeerPullCreditTransaction(ws, tx.pursePub);
return;
case TransactionType.PeerPullDebit:
await cancelAbortingPeerPullDebitTransaction(ws, tx.peerPullPaymentIncomingId);
await failPeerPullDebitTransaction(ws, tx.peerPullPaymentIncomingId);
return;
case TransactionType.PeerPushCredit:
await cancelAbortingPeerPushCreditTransaction(ws, tx.peerPushPaymentIncomingId);
await failPeerPushCreditTransaction(ws, tx.peerPushPaymentIncomingId);
return;
case TransactionType.PeerPushDebit:
await cancelAbortingPeerPushDebitTransaction(ws, tx.pursePub);
await failPeerPushDebitTransaction(ws, tx.pursePub);
return;
default:
assertUnreachable(tx);

View File

@ -67,6 +67,7 @@ import {
TransactionMajorState,
TransactionMinorState,
TalerPreciseTimestamp,
TransactionAction,
} from "@gnu-taler/taler-util";
import { EddsaKeypair } from "../crypto/cryptoImplementation.js";
import {
@ -338,7 +339,7 @@ export async function abortWithdrawalTransaction(
notifyTransition(ws, transactionId, transitionInfo);
}
export async function cancelAbortingWithdrawalTransaction(
export async function failWithdrawalTransaction(
ws: InternalWalletState,
withdrawalGroupId: string,
) {
@ -483,6 +484,49 @@ export function computeWithdrawalTransactionStatus(
}
}
export function computeWithdrawalTransactionActions(
wgRecord: WithdrawalGroupRecord,
): TransactionAction[] {
switch (wgRecord.status) {
case WithdrawalGroupStatus.FailedBankAborted:
return [TransactionAction.Delete];
case WithdrawalGroupStatus.Finished:
return [TransactionAction.Delete];
case WithdrawalGroupStatus.PendingRegisteringBank:
return [TransactionAction.Suspend, TransactionAction.Abort];
case WithdrawalGroupStatus.PendingReady:
return [TransactionAction.Suspend, TransactionAction.Abort];
case WithdrawalGroupStatus.PendingQueryingStatus:
return [TransactionAction.Suspend, TransactionAction.Abort];
case WithdrawalGroupStatus.PendingWaitConfirmBank:
return [TransactionAction.Suspend, TransactionAction.Abort];
case WithdrawalGroupStatus.AbortingBank:
return [TransactionAction.Suspend, TransactionAction.Fail];
case WithdrawalGroupStatus.SuspendedAbortingBank:
return [TransactionAction.Resume, TransactionAction.Fail];
case WithdrawalGroupStatus.SuspendedQueryingStatus:
return [TransactionAction.Resume, TransactionAction.Abort];
case WithdrawalGroupStatus.SuspendedRegisteringBank:
return [TransactionAction.Resume, TransactionAction.Abort]
case WithdrawalGroupStatus.SuspendedWaitConfirmBank:
return [TransactionAction.Resume, TransactionAction.Abort];
case WithdrawalGroupStatus.SuspendedReady:
return [TransactionAction.Resume, TransactionAction.Abort];
case WithdrawalGroupStatus.PendingAml:
return [TransactionAction.Resume, TransactionAction.Abort];
case WithdrawalGroupStatus.PendingKyc:
return [TransactionAction.Resume, TransactionAction.Abort];
case WithdrawalGroupStatus.SuspendedAml:
return [TransactionAction.Resume, TransactionAction.Abort];
case WithdrawalGroupStatus.SuspendedKyc:
return [TransactionAction.Resume, TransactionAction.Abort]
case WithdrawalGroupStatus.FailedAbortingBank:
return [TransactionAction.Delete];
case WithdrawalGroupStatus.AbortedExchange:
return [TransactionAction.Delete];
}
}
/**
* Get information about a withdrawal from
* a taler://withdraw URI by asking the bank.

View File

@ -232,7 +232,7 @@ import {
import { acceptTip, prepareTip, processTip } from "./operations/tip.js";
import {
abortTransaction,
cancelAbortingTransaction,
failTransaction,
deleteTransaction,
getTransactionById,
getTransactions,
@ -1233,7 +1233,7 @@ async function dispatchRequestInternal<Op extends WalletApiOperation>(
}
case WalletApiOperation.CancelAbortingTransaction: {
const req = codecForCancelAbortingTransactionRequest().decode(payload);
await cancelAbortingTransaction(ws, req.transactionId);
await failTransaction(ws, req.transactionId);
return {};
}
case WalletApiOperation.ResumeTransaction: {