aboutsummaryrefslogtreecommitdiff
path: root/packages/taler-wallet-core
diff options
context:
space:
mode:
authorSebastian <sebasjm@gmail.com>2022-11-24 23:16:01 -0300
committerSebastian <sebasjm@gmail.com>2022-11-24 23:16:01 -0300
commite05ba843a061c8050648ce922f36ed3d8e1cf24a (patch)
tree4daf3eccc5f2976b980e884499a756cc6f864c6e /packages/taler-wallet-core
parent88618df7b870732f4f29a80686dd4f4cf20887f8 (diff)
downloadwallet-core-e05ba843a061c8050648ce922f36ed3d8e1cf24a.tar.xz
fix 7465
Diffstat (limited to 'packages/taler-wallet-core')
-rw-r--r--packages/taler-wallet-core/src/db.ts27
-rw-r--r--packages/taler-wallet-core/src/operations/attention.ts145
-rw-r--r--packages/taler-wallet-core/src/operations/backup/index.ts140
-rw-r--r--packages/taler-wallet-core/src/operations/pay-merchant.ts43
-rw-r--r--packages/taler-wallet-core/src/util/assertUnreachable.ts2
-rw-r--r--packages/taler-wallet-core/src/wallet-api-types.ts37
-rw-r--r--packages/taler-wallet-core/src/wallet.ts19
7 files changed, 385 insertions, 28 deletions
diff --git a/packages/taler-wallet-core/src/db.ts b/packages/taler-wallet-core/src/db.ts
index bbd93f669..2bf417cac 100644
--- a/packages/taler-wallet-core/src/db.ts
+++ b/packages/taler-wallet-core/src/db.ts
@@ -48,6 +48,9 @@ import {
WireInfo,
HashCodeString,
Amounts,
+ AttentionPriority,
+ AttentionInfo,
+ AbsoluteTime,
} from "@gnu-taler/taler-util";
import {
describeContents,
@@ -1540,6 +1543,8 @@ export interface BackupProviderRecord {
*/
currentPaymentProposalId?: string;
+ shouldRetryFreshProposal: boolean;
+
/**
* Proposals that were used to pay (or attempt to pay) the provider.
*
@@ -1841,6 +1846,21 @@ export interface ContractTermsRecord {
contractTermsRaw: any;
}
+export interface UserAttentionRecord {
+ info: AttentionInfo;
+
+ entityId: string;
+ /**
+ * When the notification was created.
+ */
+ createdMs: number;
+
+ /**
+ * When the user mark this notification as read.
+ */
+ read: TalerProtocolTimestamp | undefined;
+}
+
/**
* Schema definition for the IndexedDB
* wallet database.
@@ -2137,6 +2157,13 @@ export const WalletStoresV1 = {
}),
{},
),
+ userAttention: describeStore(
+ "userAttention",
+ describeContents<UserAttentionRecord>({
+ keyPath: ["entityId", "info.type"],
+ }),
+ {},
+ ),
};
/**
diff --git a/packages/taler-wallet-core/src/operations/attention.ts b/packages/taler-wallet-core/src/operations/attention.ts
new file mode 100644
index 000000000..95db7bde0
--- /dev/null
+++ b/packages/taler-wallet-core/src/operations/attention.ts
@@ -0,0 +1,145 @@
+/*
+ This file is part of GNU Taler
+ (C) 2019 GNUnet e.V.
+
+ GNU Taler is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+
+/**
+ * Imports.
+ */
+import {
+ AbsoluteTime,
+ AttentionInfo,
+ Logger,
+ TalerProtocolTimestamp,
+ UserAttentionByIdRequest,
+ UserAttentionPriority,
+ UserAttentionsCountResponse,
+ UserAttentionsRequest,
+ UserAttentionsResponse,
+ UserAttentionUnreadList,
+} from "@gnu-taler/taler-util";
+import { InternalWalletState } from "../internal-wallet-state.js";
+
+const logger = new Logger("operations/attention.ts");
+
+export async function getUserAttentionsUnreadCount(
+ ws: InternalWalletState,
+ req: UserAttentionsRequest,
+): Promise<UserAttentionsCountResponse> {
+ const total = await ws.db
+ .mktx((x) => [x.userAttention])
+ .runReadOnly(async (tx) => {
+ let count = 0;
+ await tx.userAttention.iter().forEach((x) => {
+ if (
+ req.priority !== undefined &&
+ UserAttentionPriority[x.info.type] !== req.priority
+ )
+ return;
+ if (x.read !== undefined) return;
+ count++;
+ });
+
+ return count;
+ });
+
+ return { total };
+}
+
+export async function getUserAttentions(
+ ws: InternalWalletState,
+ req: UserAttentionsRequest,
+): Promise<UserAttentionsResponse> {
+ return await ws.db
+ .mktx((x) => [x.userAttention])
+ .runReadOnly(async (tx) => {
+ const pending: UserAttentionUnreadList = [];
+ await tx.userAttention.iter().forEach((x) => {
+ if (
+ req.priority !== undefined &&
+ UserAttentionPriority[x.info.type] !== req.priority
+ )
+ return;
+ pending.push({
+ info: x.info,
+ when: {
+ t_ms: x.createdMs,
+ },
+ read: x.read !== undefined,
+ });
+ });
+
+ return { pending };
+ });
+}
+
+export async function markAttentionRequestAsRead(
+ ws: InternalWalletState,
+ req: UserAttentionByIdRequest,
+): Promise<void> {
+ await ws.db
+ .mktx((x) => [x.userAttention])
+ .runReadWrite(async (tx) => {
+ const ua = await tx.userAttention.get([req.entityId, req.type]);
+ if (!ua) throw Error("attention request not found");
+ tx.userAttention.put({
+ ...ua,
+ read: TalerProtocolTimestamp.now(),
+ });
+ });
+}
+
+/**
+ * the wallet need the user attention to complete a task
+ * internal API
+ *
+ * @param ws
+ * @param info
+ */
+export async function addAttentionRequest(
+ ws: InternalWalletState,
+ info: AttentionInfo,
+ entityId: string,
+): Promise<void> {
+ await ws.db
+ .mktx((x) => [x.userAttention])
+ .runReadWrite(async (tx) => {
+ await tx.userAttention.put({
+ info,
+ entityId,
+ createdMs: AbsoluteTime.now().t_ms as number,
+ read: undefined,
+ });
+ });
+}
+
+/**
+ * user completed the task, attention request is not needed
+ * internal API
+ *
+ * @param ws
+ * @param created
+ */
+export async function removeAttentionRequest(
+ ws: InternalWalletState,
+ req: UserAttentionByIdRequest,
+): Promise<void> {
+ await ws.db
+ .mktx((x) => [x.userAttention])
+ .runReadWrite(async (tx) => {
+ const ua = await tx.userAttention.get([req.entityId, req.type]);
+ if (!ua) throw Error("attention request not found");
+ await tx.userAttention.delete([req.entityId, req.type]);
+ });
+}
diff --git a/packages/taler-wallet-core/src/operations/backup/index.ts b/packages/taler-wallet-core/src/operations/backup/index.ts
index aed37b865..eef838b0c 100644
--- a/packages/taler-wallet-core/src/operations/backup/index.ts
+++ b/packages/taler-wallet-core/src/operations/backup/index.ts
@@ -27,6 +27,7 @@
import {
AbsoluteTime,
AmountString,
+ AttentionType,
BackupRecovery,
buildCodecForObject,
buildCodecForUnion,
@@ -57,13 +58,17 @@ import {
kdf,
Logger,
notEmpty,
+ PaymentStatus,
+ PreparePayResult,
PreparePayResultType,
RecoveryLoadRequest,
RecoveryMergeStrategy,
+ ReserveTransactionType,
rsaBlind,
secretbox,
secretbox_open,
stringToBytes,
+ TalerErrorCode,
TalerErrorDetail,
TalerProtocolTimestamp,
URL,
@@ -80,6 +85,7 @@ import {
ConfigRecordKey,
WalletBackupConfState,
} from "../../db.js";
+import { TalerError } from "../../errors.js";
import { InternalWalletState } from "../../internal-wallet-state.js";
import { assertUnreachable } from "../../util/assertUnreachable.js";
import {
@@ -96,6 +102,7 @@ import {
RetryTags,
scheduleRetryInTx,
} from "../../util/retries.js";
+import { addAttentionRequest, removeAttentionRequest } from "../attention.js";
import {
checkPaymentByProposalId,
confirmPay,
@@ -198,6 +205,7 @@ async function computeBackupCryptoData(
);
}
for (const purch of backupContent.purchases) {
+ if (!purch.contract_terms_raw) continue;
const { h: contractTermsHash } = await cryptoApi.hashString({
str: canonicalJson(purch.contract_terms_raw),
});
@@ -251,7 +259,7 @@ function getNextBackupTimestamp(): TalerProtocolTimestamp {
async function runBackupCycleForProvider(
ws: InternalWalletState,
args: BackupForProviderArgs,
-): Promise<OperationAttemptResult<unknown, { talerUri: string }>> {
+): Promise<OperationAttemptResult<unknown, { talerUri?: string }>> {
const provider = await ws.db
.mktx((x) => [x.backupProviders])
.runReadOnly(async (tx) => {
@@ -292,6 +300,10 @@ async function runBackupCycleForProvider(
provider.baseUrl,
);
+ if (provider.shouldRetryFreshProposal) {
+ accountBackupUrl.searchParams.set("fresh", "yes");
+ }
+
const resp = await ws.http.fetch(accountBackupUrl.href, {
method: "POST",
body: encBackup,
@@ -324,6 +336,12 @@ async function runBackupCycleForProvider(
};
await tx.backupProviders.put(prov);
});
+
+ removeAttentionRequest(ws, {
+ entityId: provider.baseUrl,
+ type: AttentionType.BackupUnpaid,
+ });
+
return {
type: OperationAttemptResultType.Finished,
result: undefined,
@@ -340,8 +358,51 @@ async function runBackupCycleForProvider(
//We can't delay downloading the proposal since we need the id
//FIXME: check download errors
+ let res: PreparePayResult | undefined = undefined;
+ try {
+ res = await preparePayForUri(ws, talerUri);
+ } catch (e) {
+ const error = TalerError.fromException(e);
+ if (!error.hasErrorCode(TalerErrorCode.WALLET_ORDER_ALREADY_CLAIMED)) {
+ throw error;
+ }
+ }
+
+ if (
+ res === undefined ||
+ res.status === PreparePayResultType.AlreadyConfirmed
+ ) {
+ //claimed
+
+ await ws.db
+ .mktx((x) => [x.backupProviders, x.operationRetries])
+ .runReadWrite(async (tx) => {
+ const prov = await tx.backupProviders.get(provider.baseUrl);
+ if (!prov) {
+ logger.warn("backup provider not found anymore");
+ return;
+ }
+ const opId = RetryTags.forBackup(prov);
+ await scheduleRetryInTx(ws, tx, opId);
+ prov.shouldRetryFreshProposal = true;
+ prov.state = {
+ tag: BackupProviderStateTag.Retrying,
+ };
+ await tx.backupProviders.put(prov);
+ });
- const res = await preparePayForUri(ws, talerUri);
+ return {
+ type: OperationAttemptResultType.Pending,
+ result: {
+ talerUri,
+ },
+ };
+ }
+ const result = res;
+
+ if (result.status === PreparePayResultType.Lost) {
+ throw Error("invalid state, could not get proposal for backup");
+ }
await ws.db
.mktx((x) => [x.backupProviders, x.operationRetries])
@@ -353,13 +414,24 @@ async function runBackupCycleForProvider(
}
const opId = RetryTags.forBackup(prov);
await scheduleRetryInTx(ws, tx, opId);
- prov.currentPaymentProposalId = res.proposalId;
+ prov.currentPaymentProposalId = result.proposalId;
+ prov.shouldRetryFreshProposal = false;
prov.state = {
tag: BackupProviderStateTag.Retrying,
};
await tx.backupProviders.put(prov);
});
+ addAttentionRequest(
+ ws,
+ {
+ type: AttentionType.BackupUnpaid,
+ provider_base_url: provider.baseUrl,
+ talerUri,
+ },
+ provider.baseUrl,
+ );
+
return {
type: OperationAttemptResultType.Pending,
result: {
@@ -384,6 +456,12 @@ async function runBackupCycleForProvider(
};
await tx.backupProviders.put(prov);
});
+
+ removeAttentionRequest(ws, {
+ entityId: provider.baseUrl,
+ type: AttentionType.BackupUnpaid,
+ });
+
return {
type: OperationAttemptResultType.Finished,
result: undefined,
@@ -564,7 +642,7 @@ interface AddBackupProviderOk {
}
interface AddBackupProviderPaymentRequired {
status: "payment-required";
- talerUri: string;
+ talerUri?: string;
}
interface AddBackupProviderError {
status: "error";
@@ -580,7 +658,7 @@ export const codecForAddBackupProviderPaymenrRequired =
(): Codec<AddBackupProviderPaymentRequired> =>
buildCodecForObject<AddBackupProviderPaymentRequired>()
.property("status", codecForConstString("payment-required"))
- .property("talerUri", codecForString())
+ .property("talerUri", codecOptional(codecForString()))
.build("AddBackupProviderPaymentRequired");
export const codecForAddBackupProviderError =
@@ -655,6 +733,7 @@ export async function addBackupProvider(
storageLimitInMegabytes: terms.storage_limit_in_megabytes,
supportedProtocolVersion: terms.version,
},
+ shouldRetryFreshProposal: false,
paymentProposalIds: [],
baseUrl: canonUrl,
uids: [encodeCrock(getRandomBytes(32))],
@@ -779,10 +858,12 @@ export interface ProviderPaymentUnpaid {
export interface ProviderPaymentInsufficientBalance {
type: ProviderPaymentType.InsufficientBalance;
+ amount: AmountString;
}
export interface ProviderPaymentPending {
type: ProviderPaymentType.Pending;
+ talerUri?: string;
}
export interface ProviderPaymentPaid {
@@ -810,32 +891,40 @@ async function getProviderPaymentInfo(
ws,
provider.currentPaymentProposalId,
);
- if (status.status === PreparePayResultType.InsufficientBalance) {
- return {
- type: ProviderPaymentType.InsufficientBalance,
- };
- }
- if (status.status === PreparePayResultType.PaymentPossible) {
- return {
- type: ProviderPaymentType.Pending,
- };
- }
- if (status.status === PreparePayResultType.AlreadyConfirmed) {
- if (status.paid) {
+
+ switch (status.status) {
+ case PreparePayResultType.InsufficientBalance:
return {
- type: ProviderPaymentType.Paid,
- paidUntil: AbsoluteTime.addDuration(
- AbsoluteTime.fromTimestamp(status.contractTerms.timestamp),
- durationFromSpec({ years: 1 }),
- ),
+ type: ProviderPaymentType.InsufficientBalance,
+ amount: status.amountRaw,
};
- } else {
+ case PreparePayResultType.PaymentPossible:
return {
type: ProviderPaymentType.Pending,
+ talerUri: status.talerUri,
};
- }
+ case PreparePayResultType.Lost:
+ return {
+ type: ProviderPaymentType.Unpaid,
+ };
+ case PreparePayResultType.AlreadyConfirmed:
+ if (status.paid) {
+ return {
+ type: ProviderPaymentType.Paid,
+ paidUntil: AbsoluteTime.addDuration(
+ AbsoluteTime.fromTimestamp(status.contractTerms.timestamp),
+ durationFromSpec({ years: 1 }), //FIXME: take this from the contract term
+ ),
+ };
+ } else {
+ return {
+ type: ProviderPaymentType.Pending,
+ talerUri: status.talerUri,
+ };
+ }
+ default:
+ assertUnreachable(status);
}
- throw Error("not reached");
}
/**
@@ -936,6 +1025,7 @@ async function backupRecoveryTheirs(
baseUrl: prov.url,
name: prov.name,
paymentProposalIds: [],
+ shouldRetryFreshProposal: false,
state: {
tag: BackupProviderStateTag.Ready,
nextBackupTimestamp: TalerProtocolTimestamp.now(),
diff --git a/packages/taler-wallet-core/src/operations/pay-merchant.ts b/packages/taler-wallet-core/src/operations/pay-merchant.ts
index 6246951ad..d3d0a12bd 100644
--- a/packages/taler-wallet-core/src/operations/pay-merchant.ts
+++ b/packages/taler-wallet-core/src/operations/pay-merchant.ts
@@ -72,6 +72,7 @@ import {
TalerProtocolTimestamp,
TransactionType,
URL,
+ constructPayUri,
} from "@gnu-taler/taler-util";
import { EddsaKeypair } from "../crypto/cryptoImplementation.js";
import {
@@ -1290,7 +1291,10 @@ export async function checkPaymentByProposalId(
return tx.purchases.get(proposalId);
});
if (!proposal) {
- throw Error(`could not get proposal ${proposalId}`);
+ // throw Error(`could not get proposal ${proposalId}`);
+ return {
+ status: PreparePayResultType.Lost,
+ };
}
if (proposal.purchaseStatus === PurchaseStatus.RepurchaseDetected) {
const existingProposalId = proposal.repurchaseProposalId;
@@ -1316,6 +1320,14 @@ export async function checkPaymentByProposalId(
proposalId = proposal.proposalId;
+ const talerUri = constructPayUri(
+ proposal.merchantBaseUrl,
+ proposal.orderId,
+ proposal.lastSessionId ?? proposal.downloadSessionId ?? "",
+ proposal.claimToken,
+ proposal.noncePriv,
+ );
+
// First check if we already paid for it.
const purchase = await ws.db
.mktx((x) => [x.purchases])
@@ -1345,6 +1357,7 @@ export async function checkPaymentByProposalId(
proposalId: proposal.proposalId,
noncePriv: proposal.noncePriv,
amountRaw: Amounts.stringify(d.contractData.amount),
+ talerUri,
};
}
@@ -1360,6 +1373,7 @@ export async function checkPaymentByProposalId(
amountEffective: Amounts.stringify(totalCost),
amountRaw: Amounts.stringify(res.paymentAmount),
contractTermsHash: d.contractData.contractTermsHash,
+ talerUri,
};
}
@@ -1396,6 +1410,7 @@ export async function checkPaymentByProposalId(
amountRaw: Amounts.stringify(download.contractData.amount),
amountEffective: Amounts.stringify(purchase.payInfo?.totalPayCost!),
proposalId,
+ talerUri,
};
} else if (!purchase.timestampFirstSuccessfulPay) {
const download = await expectProposalDownload(ws, purchase);
@@ -1407,6 +1422,7 @@ export async function checkPaymentByProposalId(
amountRaw: Amounts.stringify(download.contractData.amount),
amountEffective: Amounts.stringify(purchase.payInfo?.totalPayCost!),
proposalId,
+ talerUri,
};
} else {
const paid =
@@ -1423,6 +1439,7 @@ export async function checkPaymentByProposalId(
amountEffective: Amounts.stringify(purchase.payInfo?.totalPayCost!),
...(paid ? { nextUrl: download.contractData.orderId } : {}),
proposalId,
+ talerUri,
};
}
}
@@ -1468,7 +1485,7 @@ export async function preparePayForUri(
);
}
- let proposalId = await startDownloadProposal(
+ const proposalId = await startDownloadProposal(
ws,
uriResult.merchantBaseUrl,
uriResult.orderId,
@@ -1930,6 +1947,28 @@ export async function processPurchasePay(
);
}
+ if (resp.status === HttpStatusCode.Gone) {
+ const errDetails = await readUnexpectedResponseDetails(resp);
+ logger.warn("unexpected 410 response for /pay");
+ logger.warn(j2s(errDetails));
+ await ws.db
+ .mktx((x) => [x.purchases])
+ .runReadWrite(async (tx) => {
+ const purch = await tx.purchases.get(proposalId);
+ if (!purch) {
+ return;
+ }
+ // FIXME: Should be some "PayPermanentlyFailed" and error info should be stored
+ purch.purchaseStatus = PurchaseStatus.PaymentAbortFinished;
+ await tx.purchases.put(purch);
+ });
+ throw makePendingOperationFailedError(
+ errDetails,
+ TransactionType.Payment,
+ proposalId,
+ );
+ }
+
if (resp.status === HttpStatusCode.Conflict) {
const err = await readTalerErrorResponse(resp);
if (
diff --git a/packages/taler-wallet-core/src/util/assertUnreachable.ts b/packages/taler-wallet-core/src/util/assertUnreachable.ts
index ffdf88f04..1819fd09e 100644
--- a/packages/taler-wallet-core/src/util/assertUnreachable.ts
+++ b/packages/taler-wallet-core/src/util/assertUnreachable.ts
@@ -15,5 +15,5 @@
*/
export function assertUnreachable(x: never): never {
- throw new Error("Didn't expect to get here");
+ throw new Error(`Didn't expect to get here ${x}`);
}
diff --git a/packages/taler-wallet-core/src/wallet-api-types.ts b/packages/taler-wallet-core/src/wallet-api-types.ts
index 04c1bb6b4..f4fb16e80 100644
--- a/packages/taler-wallet-core/src/wallet-api-types.ts
+++ b/packages/taler-wallet-core/src/wallet-api-types.ts
@@ -71,6 +71,9 @@ import {
KnownBankAccounts,
ListKnownBankAccountsRequest,
ManualWithdrawalDetails,
+ UserAttentionsCountResponse,
+ UserAttentionsRequest,
+ UserAttentionsResponse,
PrepareDepositRequest,
PrepareDepositResponse,
PreparePayRequest,
@@ -102,6 +105,7 @@ import {
WithdrawFakebankRequest,
WithdrawTestBalanceRequest,
WithdrawUriInfoResponse,
+ UserAttentionByIdRequest,
} from "@gnu-taler/taler-util";
import { WalletContractData } from "./db.js";
import {
@@ -133,6 +137,9 @@ export enum WalletApiOperation {
GetWithdrawalDetailsForAmount = "getWithdrawalDetailsForAmount",
AcceptManualWithdrawal = "acceptManualWithdrawal",
GetBalances = "getBalances",
+ GetUserAttentionRequests = "getUserAttentionRequests",
+ GetUserAttentionUnreadCount = "getUserAttentionUnreadCount",
+ MarkAttentionRequestAsRead = "markAttentionRequestAsRead",
GetPendingOperations = "getPendingOperations",
SetExchangeTosAccepted = "setExchangeTosAccepted",
ApplyRefund = "applyRefund",
@@ -749,6 +756,33 @@ export type WithdrawFakebankOp = {
/**
* Get wallet-internal pending tasks.
*/
+export type GetUserAttentionRequests = {
+ op: WalletApiOperation.GetUserAttentionRequests;
+ request: UserAttentionsRequest;
+ response: UserAttentionsResponse;
+};
+
+/**
+ * Get wallet-internal pending tasks.
+ */
+export type MarkAttentionRequestAsRead = {
+ op: WalletApiOperation.MarkAttentionRequestAsRead;
+ request: UserAttentionByIdRequest;
+ response: EmptyObject;
+};
+
+/**
+ * Get wallet-internal pending tasks.
+ */
+export type GetUserAttentionsUnreadCount = {
+ op: WalletApiOperation.GetUserAttentionUnreadCount;
+ request: UserAttentionsRequest;
+ response: UserAttentionsCountResponse;
+};
+
+/**
+ * Get wallet-internal pending tasks.
+ */
export type GetPendingTasksOp = {
op: WalletApiOperation.GetPendingOperations;
request: EmptyObject;
@@ -798,6 +832,9 @@ export type WalletOperations = {
[WalletApiOperation.GetTransactionById]: GetTransactionByIdOp;
[WalletApiOperation.RetryPendingNow]: RetryPendingNowOp;
[WalletApiOperation.GetPendingOperations]: GetPendingTasksOp;
+ [WalletApiOperation.GetUserAttentionRequests]: GetUserAttentionRequests;
+ [WalletApiOperation.GetUserAttentionUnreadCount]: GetUserAttentionsUnreadCount;
+ [WalletApiOperation.MarkAttentionRequestAsRead]: MarkAttentionRequestAsRead;
[WalletApiOperation.DumpCoins]: DumpCoinsOp;
[WalletApiOperation.SetCoinSuspended]: SetCoinSuspendedOp;
[WalletApiOperation.ForceRefresh]: ForceRefreshOp;
diff --git a/packages/taler-wallet-core/src/wallet.ts b/packages/taler-wallet-core/src/wallet.ts
index 9fa0e32ba..5ad86dfe8 100644
--- a/packages/taler-wallet-core/src/wallet.ts
+++ b/packages/taler-wallet-core/src/wallet.ts
@@ -55,6 +55,7 @@ import {
codecForInitiatePeerPushPaymentRequest,
codecForIntegrationTestArgs,
codecForListKnownBankAccounts,
+ codecForUserAttentionsRequest,
codecForPrepareDepositRequest,
codecForPreparePayRequest,
codecForPreparePeerPullPaymentRequest,
@@ -98,6 +99,7 @@ import {
URL,
WalletCoreVersion,
WalletNotification,
+ codecForUserAttentionByIdRequest,
} from "@gnu-taler/taler-util";
import { TalerCryptoInterface } from "./crypto/cryptoImplementation.js";
import {
@@ -148,6 +150,11 @@ import {
import { setWalletDeviceId } from "./operations/backup/state.js";
import { getBalances } from "./operations/balance.js";
import {
+ getUserAttentions,
+ getUserAttentionsUnreadCount,
+ markAttentionRequestAsRead,
+} from "./operations/attention.js";
+import {
getExchangeTosStatus,
makeExchangeListItem,
runOperationWithErrorReporting,
@@ -1094,6 +1101,18 @@ async function dispatchRequestInternal<Op extends WalletApiOperation>(
case WalletApiOperation.GetBalances: {
return await getBalances(ws);
}
+ case WalletApiOperation.GetUserAttentionRequests: {
+ const req = codecForUserAttentionsRequest().decode(payload);
+ return await getUserAttentions(ws, req);
+ }
+ case WalletApiOperation.MarkAttentionRequestAsRead: {
+ const req = codecForUserAttentionByIdRequest().decode(payload);
+ return await markAttentionRequestAsRead(ws, req);
+ }
+ case WalletApiOperation.GetUserAttentionUnreadCount: {
+ const req = codecForUserAttentionsRequest().decode(payload);
+ return await getUserAttentionsUnreadCount(ws, req);
+ }
case WalletApiOperation.GetPendingOperations: {
return await getPendingOperations(ws);
}