implement deletion of withdrawal transactions
This commit is contained in:
parent
1fb1827002
commit
6fc9a052b7
@ -945,4 +945,8 @@ export interface WalletCurrencyInfo {
|
||||
exchangeMasterPub: string;
|
||||
exchangeBaseUrl: string;
|
||||
}[];
|
||||
}
|
||||
|
||||
export interface DeleteTransactionRequest {
|
||||
transactionId: string;
|
||||
}
|
@ -284,6 +284,21 @@ walletCli
|
||||
});
|
||||
});
|
||||
|
||||
walletCli
|
||||
.subcommand("deleteTransaction", "delete-transaction", {
|
||||
help: "Permanently delete a transaction from the transaction list.",
|
||||
})
|
||||
.requiredArgument("transactionId", clk.STRING, {
|
||||
help: "Identifier of the transaction to delete",
|
||||
})
|
||||
.action(async (args) => {
|
||||
await withWallet(args, async (wallet) => {
|
||||
await wallet.deleteTransaction({
|
||||
transactionId: args.deleteTransaction.transactionId,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
walletCli
|
||||
.subcommand("handleUri", "handle-uri", {
|
||||
help: "Handle a taler:// URI.",
|
||||
@ -609,13 +624,13 @@ const currenciesCli = walletCli.subcommand("currencies", "currencies", {
|
||||
});
|
||||
|
||||
currenciesCli
|
||||
.subcommand("show", "show", { help: "Show currencies."})
|
||||
.subcommand("show", "show", { help: "Show currencies." })
|
||||
.action(async (args) => {
|
||||
await withWallet(args, async (wallet) => {
|
||||
const currencies = await wallet.getCurrencies();
|
||||
console.log(JSON.stringify(currencies, undefined, 2));
|
||||
});
|
||||
})
|
||||
});
|
||||
|
||||
const reservesCli = advancedCli.subcommand("reserves", "reserves", {
|
||||
help: "Manage reserves.",
|
||||
|
@ -25,7 +25,6 @@ import {
|
||||
MerchantInfo,
|
||||
Product,
|
||||
RefreshReason,
|
||||
ReserveTransaction,
|
||||
TalerErrorDetails,
|
||||
Timestamp,
|
||||
} from "@gnu-taler/taler-util";
|
||||
@ -1783,7 +1782,7 @@ class AuditorTrustStore extends Store<"auditorTrust", AuditorTrustRecord> {
|
||||
class ExchangeTrustStore extends Store<"exchangeTrust", ExchangeTrustRecord> {
|
||||
constructor() {
|
||||
super("exchangeTrust", {
|
||||
keyPath: ["currency", "exchangeBaseUrl", "exchangePub"],
|
||||
keyPath: ["currency", "exchangeBaseUrl", "exchangeMasterPub"],
|
||||
});
|
||||
}
|
||||
exchangeMasterPubIndex = new Index<
|
||||
@ -1791,7 +1790,7 @@ class ExchangeTrustStore extends Store<"exchangeTrust", ExchangeTrustRecord> {
|
||||
"exchangeMasterPubIndex",
|
||||
string,
|
||||
ExchangeTrustRecord
|
||||
>(this, "exchangeMasterPubIndex", "exchangePub");
|
||||
>(this, "exchangeMasterPubIndex", "exchangeMasterPub");
|
||||
uidIndex = new Index<
|
||||
"exchangeTrust",
|
||||
"uidIndex",
|
||||
@ -1810,6 +1809,12 @@ class ReservesStore extends Store<"reserves", ReserveRecord> {
|
||||
constructor() {
|
||||
super("reserves", { keyPath: "reservePub" });
|
||||
}
|
||||
byInitialWithdrawalGroupId = new Index<
|
||||
"reserves",
|
||||
"initialWithdrawalGroupIdIndex",
|
||||
string,
|
||||
ReserveRecord
|
||||
>(this, "initialWithdrawalGroupIdIndex", "initialWithdrawalGroupId");
|
||||
}
|
||||
|
||||
class TipsStore extends Store<"tips", TipRecord> {
|
||||
|
@ -24,6 +24,7 @@ import {
|
||||
RefundState,
|
||||
ReserveRecordStatus,
|
||||
AbortStatus,
|
||||
ReserveRecord,
|
||||
} from "../db.js";
|
||||
import { AmountJson, Amounts, timestampCmp } from "@gnu-taler/taler-util";
|
||||
import {
|
||||
@ -42,7 +43,7 @@ import { getFundingPaytoUris } from "./reserves";
|
||||
* Create an event ID from the type and the primary key for the event.
|
||||
*/
|
||||
function makeEventId(type: TransactionType, ...args: string[]): string {
|
||||
return type + ";" + args.map((x) => encodeURIComponent(x)).join(";");
|
||||
return type + ":" + args.map((x) => encodeURIComponent(x)).join(":");
|
||||
}
|
||||
|
||||
function shouldSkipCurrency(
|
||||
@ -365,3 +366,55 @@ export async function getTransactions(
|
||||
|
||||
return { transactions: [...txNotPending, ...txPending] };
|
||||
}
|
||||
|
||||
export enum TombstoneTag {
|
||||
WithdrawalGroup = "withdrawal-group",
|
||||
Reserve = "reserve",
|
||||
}
|
||||
|
||||
/**
|
||||
* Permanentely delete a transaction based on the transaction ID.
|
||||
*/
|
||||
export async function deleteTransaction(
|
||||
ws: InternalWalletState,
|
||||
transactionId: string,
|
||||
): Promise<void> {
|
||||
const [type, ...rest] = transactionId.split(":");
|
||||
|
||||
if (type === TransactionType.Withdrawal) {
|
||||
const withdrawalGroupId = rest[0];
|
||||
ws.db.runWithWriteTransaction(
|
||||
[Stores.withdrawalGroups, Stores.reserves, Stores.tombstones],
|
||||
async (tx) => {
|
||||
const withdrawalGroupRecord = await tx.get(
|
||||
Stores.withdrawalGroups,
|
||||
withdrawalGroupId,
|
||||
);
|
||||
if (withdrawalGroupRecord) {
|
||||
await tx.delete(Stores.withdrawalGroups, withdrawalGroupId);
|
||||
await tx.put(Stores.tombstones, {
|
||||
id: TombstoneTag.WithdrawalGroup + ":" + withdrawalGroupId,
|
||||
});
|
||||
return;
|
||||
}
|
||||
const reserveRecord: ReserveRecord | undefined = await tx.getIndexed(
|
||||
Stores.reserves.byInitialWithdrawalGroupId,
|
||||
withdrawalGroupId,
|
||||
);
|
||||
if (reserveRecord && !reserveRecord.initialWithdrawalStarted) {
|
||||
const reservePub = reserveRecord.reservePub;
|
||||
await tx.delete(Stores.reserves, reservePub);
|
||||
await tx.put(Stores.tombstones, {
|
||||
id: TombstoneTag.Reserve + ":" + reservePub,
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
} else if (type === TransactionType.Refund) {
|
||||
// To delete refund transactions, the whole
|
||||
// purchase should be deleted.
|
||||
throw Error("refunds cannot be deleted");
|
||||
} else {
|
||||
throw Error(`can't delete a '${type}' transaction`);
|
||||
}
|
||||
}
|
||||
|
@ -33,6 +33,7 @@ import {
|
||||
IDBVersionChangeEvent,
|
||||
Event,
|
||||
IDBCursor,
|
||||
IDBKeyPath,
|
||||
} from "@gnu-taler/idb-bridge";
|
||||
import { Logger } from "./logging";
|
||||
|
||||
|
@ -25,6 +25,7 @@
|
||||
import {
|
||||
BackupRecovery,
|
||||
codecForAny,
|
||||
DeleteTransactionRequest,
|
||||
TalerErrorCode,
|
||||
WalletCurrencyInfo,
|
||||
} from "@gnu-taler/taler-util";
|
||||
@ -92,7 +93,7 @@ import {
|
||||
withdrawTestBalance,
|
||||
} from "./operations/testing";
|
||||
import { acceptTip, prepareTip, processTip } from "./operations/tip";
|
||||
import { getTransactions } from "./operations/transactions";
|
||||
import { deleteTransaction, getTransactions } from "./operations/transactions";
|
||||
import {
|
||||
getExchangeWithdrawalInfo,
|
||||
getWithdrawalDetailsForUri,
|
||||
@ -578,6 +579,10 @@ export class Wallet {
|
||||
return getWithdrawalDetailsForUri(this.ws, talerWithdrawUri);
|
||||
}
|
||||
|
||||
async deleteTransaction(req: DeleteTransactionRequest): Promise<void> {
|
||||
return deleteTransaction(this.ws, req.transactionId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update or add exchange DB entry by fetching the /keys and /wire information.
|
||||
* Optionally link the reserve entry to the new or existing
|
||||
|
Loading…
Reference in New Issue
Block a user