implement deletion of withdrawal transactions
This commit is contained in:
parent
1fb1827002
commit
6fc9a052b7
@ -946,3 +946,7 @@ export interface WalletCurrencyInfo {
|
|||||||
exchangeBaseUrl: 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
|
walletCli
|
||||||
.subcommand("handleUri", "handle-uri", {
|
.subcommand("handleUri", "handle-uri", {
|
||||||
help: "Handle a taler:// URI.",
|
help: "Handle a taler:// URI.",
|
||||||
@ -615,7 +630,7 @@ currenciesCli
|
|||||||
const currencies = await wallet.getCurrencies();
|
const currencies = await wallet.getCurrencies();
|
||||||
console.log(JSON.stringify(currencies, undefined, 2));
|
console.log(JSON.stringify(currencies, undefined, 2));
|
||||||
});
|
});
|
||||||
})
|
});
|
||||||
|
|
||||||
const reservesCli = advancedCli.subcommand("reserves", "reserves", {
|
const reservesCli = advancedCli.subcommand("reserves", "reserves", {
|
||||||
help: "Manage reserves.",
|
help: "Manage reserves.",
|
||||||
|
@ -25,7 +25,6 @@ import {
|
|||||||
MerchantInfo,
|
MerchantInfo,
|
||||||
Product,
|
Product,
|
||||||
RefreshReason,
|
RefreshReason,
|
||||||
ReserveTransaction,
|
|
||||||
TalerErrorDetails,
|
TalerErrorDetails,
|
||||||
Timestamp,
|
Timestamp,
|
||||||
} from "@gnu-taler/taler-util";
|
} from "@gnu-taler/taler-util";
|
||||||
@ -1783,7 +1782,7 @@ class AuditorTrustStore extends Store<"auditorTrust", AuditorTrustRecord> {
|
|||||||
class ExchangeTrustStore extends Store<"exchangeTrust", ExchangeTrustRecord> {
|
class ExchangeTrustStore extends Store<"exchangeTrust", ExchangeTrustRecord> {
|
||||||
constructor() {
|
constructor() {
|
||||||
super("exchangeTrust", {
|
super("exchangeTrust", {
|
||||||
keyPath: ["currency", "exchangeBaseUrl", "exchangePub"],
|
keyPath: ["currency", "exchangeBaseUrl", "exchangeMasterPub"],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
exchangeMasterPubIndex = new Index<
|
exchangeMasterPubIndex = new Index<
|
||||||
@ -1791,7 +1790,7 @@ class ExchangeTrustStore extends Store<"exchangeTrust", ExchangeTrustRecord> {
|
|||||||
"exchangeMasterPubIndex",
|
"exchangeMasterPubIndex",
|
||||||
string,
|
string,
|
||||||
ExchangeTrustRecord
|
ExchangeTrustRecord
|
||||||
>(this, "exchangeMasterPubIndex", "exchangePub");
|
>(this, "exchangeMasterPubIndex", "exchangeMasterPub");
|
||||||
uidIndex = new Index<
|
uidIndex = new Index<
|
||||||
"exchangeTrust",
|
"exchangeTrust",
|
||||||
"uidIndex",
|
"uidIndex",
|
||||||
@ -1810,6 +1809,12 @@ class ReservesStore extends Store<"reserves", ReserveRecord> {
|
|||||||
constructor() {
|
constructor() {
|
||||||
super("reserves", { keyPath: "reservePub" });
|
super("reserves", { keyPath: "reservePub" });
|
||||||
}
|
}
|
||||||
|
byInitialWithdrawalGroupId = new Index<
|
||||||
|
"reserves",
|
||||||
|
"initialWithdrawalGroupIdIndex",
|
||||||
|
string,
|
||||||
|
ReserveRecord
|
||||||
|
>(this, "initialWithdrawalGroupIdIndex", "initialWithdrawalGroupId");
|
||||||
}
|
}
|
||||||
|
|
||||||
class TipsStore extends Store<"tips", TipRecord> {
|
class TipsStore extends Store<"tips", TipRecord> {
|
||||||
|
@ -24,6 +24,7 @@ import {
|
|||||||
RefundState,
|
RefundState,
|
||||||
ReserveRecordStatus,
|
ReserveRecordStatus,
|
||||||
AbortStatus,
|
AbortStatus,
|
||||||
|
ReserveRecord,
|
||||||
} from "../db.js";
|
} from "../db.js";
|
||||||
import { AmountJson, Amounts, timestampCmp } from "@gnu-taler/taler-util";
|
import { AmountJson, Amounts, timestampCmp } from "@gnu-taler/taler-util";
|
||||||
import {
|
import {
|
||||||
@ -42,7 +43,7 @@ import { getFundingPaytoUris } from "./reserves";
|
|||||||
* Create an event ID from the type and the primary key for the event.
|
* Create an event ID from the type and the primary key for the event.
|
||||||
*/
|
*/
|
||||||
function makeEventId(type: TransactionType, ...args: string[]): string {
|
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(
|
function shouldSkipCurrency(
|
||||||
@ -365,3 +366,55 @@ export async function getTransactions(
|
|||||||
|
|
||||||
return { transactions: [...txNotPending, ...txPending] };
|
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,
|
IDBVersionChangeEvent,
|
||||||
Event,
|
Event,
|
||||||
IDBCursor,
|
IDBCursor,
|
||||||
|
IDBKeyPath,
|
||||||
} from "@gnu-taler/idb-bridge";
|
} from "@gnu-taler/idb-bridge";
|
||||||
import { Logger } from "./logging";
|
import { Logger } from "./logging";
|
||||||
|
|
||||||
|
@ -25,6 +25,7 @@
|
|||||||
import {
|
import {
|
||||||
BackupRecovery,
|
BackupRecovery,
|
||||||
codecForAny,
|
codecForAny,
|
||||||
|
DeleteTransactionRequest,
|
||||||
TalerErrorCode,
|
TalerErrorCode,
|
||||||
WalletCurrencyInfo,
|
WalletCurrencyInfo,
|
||||||
} from "@gnu-taler/taler-util";
|
} from "@gnu-taler/taler-util";
|
||||||
@ -92,7 +93,7 @@ import {
|
|||||||
withdrawTestBalance,
|
withdrawTestBalance,
|
||||||
} from "./operations/testing";
|
} from "./operations/testing";
|
||||||
import { acceptTip, prepareTip, processTip } from "./operations/tip";
|
import { acceptTip, prepareTip, processTip } from "./operations/tip";
|
||||||
import { getTransactions } from "./operations/transactions";
|
import { deleteTransaction, getTransactions } from "./operations/transactions";
|
||||||
import {
|
import {
|
||||||
getExchangeWithdrawalInfo,
|
getExchangeWithdrawalInfo,
|
||||||
getWithdrawalDetailsForUri,
|
getWithdrawalDetailsForUri,
|
||||||
@ -578,6 +579,10 @@ export class Wallet {
|
|||||||
return getWithdrawalDetailsForUri(this.ws, talerWithdrawUri);
|
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.
|
* Update or add exchange DB entry by fetching the /keys and /wire information.
|
||||||
* Optionally link the reserve entry to the new or existing
|
* Optionally link the reserve entry to the new or existing
|
||||||
|
Loading…
Reference in New Issue
Block a user