summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFlorian Dold <florian.dold@gmail.com>2020-09-08 17:40:47 +0530
committerFlorian Dold <florian.dold@gmail.com>2020-09-08 17:40:47 +0530
commitb063382d25d1ed8572ebe2f52bf54247379300d5 (patch)
treeb60e4abf9b5285ffdf3339639ba8dae30d0bfff1
parentbe77ee284a819f7932831bd85e88c47c655addb2 (diff)
downloadwallet-core-b063382d25d1ed8572ebe2f52bf54247379300d5.tar.gz
wallet-core-b063382d25d1ed8572ebe2f52bf54247379300d5.tar.bz2
wallet-core-b063382d25d1ed8572ebe2f52bf54247379300d5.zip
tipping API and integration test
-rw-r--r--packages/taler-integrationtests/src/harness.ts67
-rw-r--r--packages/taler-integrationtests/src/merchantApiTypes.ts60
-rw-r--r--packages/taler-integrationtests/src/test-tipping.ts106
-rw-r--r--packages/taler-wallet-cli/src/index.ts4
-rw-r--r--packages/taler-wallet-core/src/db.ts2
-rw-r--r--packages/taler-wallet-core/src/operations/pending.ts2
-rw-r--r--packages/taler-wallet-core/src/operations/tip.ts81
-rw-r--r--packages/taler-wallet-core/src/types/dbTypes.ts4
-rw-r--r--packages/taler-wallet-core/src/types/notifications.ts1
-rw-r--r--packages/taler-wallet-core/src/types/talerTypes.ts17
-rw-r--r--packages/taler-wallet-core/src/types/walletTypes.ts56
-rw-r--r--packages/taler-wallet-core/src/wallet.ts19
-rw-r--r--packages/taler-wallet-webextension/src/wxApi.ts4
13 files changed, 331 insertions, 92 deletions
diff --git a/packages/taler-integrationtests/src/harness.ts b/packages/taler-integrationtests/src/harness.ts
index 620b89e76..e40798b4b 100644
--- a/packages/taler-integrationtests/src/harness.ts
+++ b/packages/taler-integrationtests/src/harness.ts
@@ -72,6 +72,10 @@ import {
CoinDumpJson,
ForceExchangeUpdateRequest,
ForceRefreshRequest,
+ PrepareTipResult,
+ PrepareTipRequest,
+ codecForPrepareTipResult,
+ AcceptTipRequest,
} from "taler-wallet-core";
import { URL } from "url";
import axios, { AxiosError } from "axios";
@@ -81,6 +85,9 @@ import {
PostOrderRequest,
PostOrderResponse,
MerchantOrderPrivateStatusResponse,
+ TippingReserveStatus,
+ TipCreateConfirmation,
+ TipCreateRequest,
} from "./merchantApiTypes";
import { ApplyRefundResponse } from "taler-wallet-core";
import { PendingOperationsResponse } from "taler-wallet-core";
@@ -1216,10 +1223,46 @@ export namespace MerchantPrivateApi {
};
}
- export async function createTippingReserve(merchantService: MerchantServiceInterface,
-
+ export async function createTippingReserve(
+ merchantService: MerchantServiceInterface,
+ instance: string,
req: CreateMerchantTippingReserveRequest,
- ): Promise<CreateMerchantTippingReserveConfirmation> {}
+ ): Promise<CreateMerchantTippingReserveConfirmation> {
+ const reqUrl = new URL(
+ `private/reserves`,
+ merchantService.makeInstanceBaseUrl(instance),
+ );
+ const resp = await axios.post(reqUrl.href, req);
+ // FIXME: validate
+ return resp.data;
+ }
+
+ export async function queryTippingReserves(
+ merchantService: MerchantServiceInterface,
+ instance: string,
+ ): Promise<TippingReserveStatus> {
+ const reqUrl = new URL(
+ `private/reserves`,
+ merchantService.makeInstanceBaseUrl(instance),
+ );
+ const resp = await axios.get(reqUrl.href);
+ // FIXME: validate
+ return resp.data;
+ }
+
+ export async function giveTip(
+ merchantService: MerchantServiceInterface,
+ instance: string,
+ req: TipCreateRequest,
+ ): Promise<TipCreateConfirmation> {
+ const reqUrl = new URL(
+ `private/tips`,
+ merchantService.makeInstanceBaseUrl(instance),
+ );
+ const resp = await axios.post(reqUrl.href, req);
+ // FIXME: validate
+ return resp.data;
+ }
}
export interface CreateMerchantTippingReserveRequest {
@@ -1238,7 +1281,7 @@ export interface CreateMerchantTippingReserveConfirmation {
reserve_pub: string;
// Wire account of the exchange where to transfer the funds
- payto_url: string;
+ payto_uri: string;
}
export class MerchantService implements MerchantServiceInterface {
@@ -1594,6 +1637,22 @@ export class WalletCli {
throw new OperationFailedError(resp.error);
}
+ async prepareTip(req: PrepareTipRequest): Promise<PrepareTipResult> {
+ const resp = await this.apiRequest("prepareTip", req);
+ if (resp.type === "response") {
+ return codecForPrepareTipResult().decode(resp.result);
+ }
+ throw new OperationFailedError(resp.error);
+ }
+
+ async acceptTip(req: AcceptTipRequest): Promise<void> {
+ const resp = await this.apiRequest("acceptTip", req);
+ if (resp.type === "response") {
+ return;
+ }
+ throw new OperationFailedError(resp.error);
+ }
+
async dumpCoins(): Promise<CoinDumpJson> {
const resp = await this.apiRequest("dumpCoins", {});
if (resp.type === "response") {
diff --git a/packages/taler-integrationtests/src/merchantApiTypes.ts b/packages/taler-integrationtests/src/merchantApiTypes.ts
index 550c5e90c..e89e32642 100644
--- a/packages/taler-integrationtests/src/merchantApiTypes.ts
+++ b/packages/taler-integrationtests/src/merchantApiTypes.ts
@@ -223,3 +223,63 @@ export interface TransactionWireReport {
// Public key of the coin for which we got the exchange error.
coin_pub: CoinPublicKeyString;
}
+
+export interface TippingReserveStatus {
+ // Array of all known reserves (possibly empty!)
+ reserves: ReserveStatusEntry[];
+}
+
+export interface ReserveStatusEntry {
+ // Public key of the reserve
+ reserve_pub: string;
+
+ // Timestamp when it was established
+ creation_time: Timestamp;
+
+ // Timestamp when it expires
+ expiration_time: Timestamp;
+
+ // Initial amount as per reserve creation call
+ merchant_initial_amount: AmountString;
+
+ // Initial amount as per exchange, 0 if exchange did
+ // not confirm reserve creation yet.
+ exchange_initial_amount: AmountString;
+
+ // Amount picked up so far.
+ pickup_amount: AmountString;
+
+ // Amount approved for tips that exceeds the pickup_amount.
+ committed_amount: AmountString;
+
+ // Is this reserve active (false if it was deleted but not purged)
+ active: boolean;
+}
+
+
+export interface TipCreateConfirmation {
+ // Unique tip identifier for the tip that was created.
+ tip_id: string;
+
+ // taler://tip URI for the tip
+ taler_tip_uri: string;
+
+ // URL that will directly trigger processing
+ // the tip when the browser is redirected to it
+ tip_status_url: string;
+
+ // when does the tip expire
+ tip_expiration: Timestamp;
+}
+
+export interface TipCreateRequest {
+ // Amount that the customer should be tipped
+ amount: AmountString;
+
+ // Justification for giving the tip
+ justification: string;
+
+ // URL that the user should be directed to after tipping,
+ // will be included in the tip_token.
+ next_url: string;
+} \ No newline at end of file
diff --git a/packages/taler-integrationtests/src/test-tipping.ts b/packages/taler-integrationtests/src/test-tipping.ts
new file mode 100644
index 000000000..ddf56c0e0
--- /dev/null
+++ b/packages/taler-integrationtests/src/test-tipping.ts
@@ -0,0 +1,106 @@
+/*
+ This file is part of GNU Taler
+ (C) 2020 Taler Systems S.A.
+
+ 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 {
+ runTest,
+ GlobalTestState,
+ MerchantPrivateApi,
+ BankAccessApi,
+ BankApi,
+} from "./harness";
+import { createSimpleTestkudosEnvironment, withdrawViaBank } from "./helpers";
+
+/**
+ * Run test for basic, bank-integrated withdrawal.
+ */
+runTest(async (t: GlobalTestState) => {
+ // Set up test environment
+
+ const {
+ wallet,
+ bank,
+ exchange,
+ merchant,
+ exchangeBankAccount,
+ } = await createSimpleTestkudosEnvironment(t);
+
+ const mbu = await BankApi.createRandomBankUser(bank);
+
+ const tipReserveResp = await MerchantPrivateApi.createTippingReserve(
+ merchant,
+ "default",
+ {
+ exchange_url: exchange.baseUrl,
+ initial_balance: "TESTKUDOS:10",
+ wire_method: "x-taler-bank",
+ },
+ );
+
+ console.log("tipReserveResp:", tipReserveResp);
+
+ t.assertDeepEqual(
+ tipReserveResp.payto_uri,
+ exchangeBankAccount.accountPaytoUri,
+ );
+
+ await BankApi.adminAddIncoming(bank, {
+ amount: "TESTKUDOS:10",
+ debitAccountPayto: mbu.accountPaytoUri,
+ exchangeBankAccount,
+ reservePub: tipReserveResp.reserve_pub,
+ });
+
+ await exchange.runWirewatchOnce();
+ await merchant.stop();
+ await merchant.start();
+ await merchant.pingUntilAvailable();
+
+ const r = await MerchantPrivateApi.queryTippingReserves(merchant, "default");
+ console.log("tipping reserves:", JSON.stringify(r, undefined, 2));
+
+ t.assertTrue(r.reserves.length === 1);
+ t.assertDeepEqual(
+ r.reserves[0].exchange_initial_amount,
+ r.reserves[0].merchant_initial_amount,
+ );
+
+ const tip = await MerchantPrivateApi.giveTip(merchant, "default", {
+ amount: "TESTKUDOS:5",
+ justification: "why not?",
+ next_url: "https://example.com/after-tip",
+ });
+
+ console.log("created tip", tip);
+
+ const ptr = await wallet.prepareTip({
+ talerTipUri: tip.taler_tip_uri,
+ });
+
+ console.log(ptr);
+
+ await wallet.acceptTip({
+ walletTipId: ptr.walletTipId,
+ });
+
+ await wallet.runUntilDone();
+
+ const bal = await wallet.getBalances();
+
+ console.log(bal);
+});
diff --git a/packages/taler-wallet-cli/src/index.ts b/packages/taler-wallet-cli/src/index.ts
index a19b8a8fc..5a662d807 100644
--- a/packages/taler-wallet-cli/src/index.ts
+++ b/packages/taler-wallet-cli/src/index.ts
@@ -288,9 +288,9 @@ walletCli
break;
case TalerUriType.TalerTip:
{
- const res = await wallet.getTipStatus(uri);
+ const res = await wallet.prepareTip(uri);
console.log("tip status", res);
- await wallet.acceptTip(res.tipId);
+ await wallet.acceptTip(res.walletTipId);
}
break;
case TalerUriType.TalerRefund:
diff --git a/packages/taler-wallet-core/src/db.ts b/packages/taler-wallet-core/src/db.ts
index b3203935e..c21ff4a43 100644
--- a/packages/taler-wallet-core/src/db.ts
+++ b/packages/taler-wallet-core/src/db.ts
@@ -8,7 +8,7 @@ import { IDBFactory, IDBDatabase } from "idb-bridge";
* with each major change. When incrementing the major version,
* the wallet should import data from the previous version.
*/
-const TALER_DB_NAME = "taler-walletdb-v10";
+const TALER_DB_NAME = "taler-walletdb-v11";
/**
* Current database minor version, should be incremented
diff --git a/packages/taler-wallet-core/src/operations/pending.ts b/packages/taler-wallet-core/src/operations/pending.ts
index 3c631eb77..91a55c705 100644
--- a/packages/taler-wallet-core/src/operations/pending.ts
+++ b/packages/taler-wallet-core/src/operations/pending.ts
@@ -368,7 +368,7 @@ async function gatherTipPending(
type: PendingOperationType.TipPickup,
givesLifeness: true,
merchantBaseUrl: tip.merchantBaseUrl,
- tipId: tip.tipId,
+ tipId: tip.walletTipId,
merchantTipId: tip.merchantTipId,
});
}
diff --git a/packages/taler-wallet-core/src/operations/tip.ts b/packages/taler-wallet-core/src/operations/tip.ts
index 7949648c5..6fe374bf0 100644
--- a/packages/taler-wallet-core/src/operations/tip.ts
+++ b/packages/taler-wallet-core/src/operations/tip.ts
@@ -16,7 +16,7 @@
import { InternalWalletState } from "./state";
import { parseTipUri } from "../util/taleruri";
-import { TipStatus, TalerErrorDetails } from "../types/walletTypes";
+import { PrepareTipResult, TalerErrorDetails } from "../types/walletTypes";
import {
TipPlanchetDetail,
codecForTipPickupGetResponse,
@@ -46,20 +46,23 @@ import { getTimestampNow } from "../util/time";
import { readSuccessResponseJsonOrThrow } from "../util/http";
import { URL } from "../util/url";
import { Logger } from "../util/logging";
+import { checkDbInvariant } from "../util/invariants";
const logger = new Logger("operations/tip.ts");
-export async function getTipStatus(
+export async function prepareTip(
ws: InternalWalletState,
talerTipUri: string,
-): Promise<TipStatus> {
+): Promise<PrepareTipResult> {
const res = parseTipUri(talerTipUri);
if (!res) {
throw Error("invalid taler://tip URI");
}
- const tipStatusUrl = new URL("tip-pickup", res.merchantBaseUrl);
- tipStatusUrl.searchParams.set("tip_id", res.merchantTipId);
+ const tipStatusUrl = new URL(
+ `tips/${res.merchantTipId}`,
+ res.merchantBaseUrl,
+ );
logger.trace("checking tip status from", tipStatusUrl.href);
const merchantResp = await ws.http.get(tipStatusUrl.href);
const tipPickupStatus = await readSuccessResponseJsonOrThrow(
@@ -68,7 +71,7 @@ export async function getTipStatus(
);
logger.trace(`status ${tipPickupStatus}`);
- const amount = Amounts.parseOrThrow(tipPickupStatus.amount);
+ const amount = Amounts.parseOrThrow(tipPickupStatus.tip_amount);
const merchantOrigin = new URL(res.merchantBaseUrl).origin;
@@ -85,7 +88,7 @@ export async function getTipStatus(
amount,
);
- const tipId = encodeCrock(getRandomBytes(32));
+ const walletTipId = encodeCrock(getRandomBytes(32));
const selectedDenoms = await selectWithdrawalDenoms(
ws,
tipPickupStatus.exchange_url,
@@ -93,11 +96,11 @@ export async function getTipStatus(
);
tipRecord = {
- tipId,
+ walletTipId: walletTipId,
acceptedTimestamp: undefined,
rejectedTimestamp: undefined,
amount,
- deadline: tipPickupStatus.stamp_expire,
+ deadline: tipPickupStatus.expiration,
exchangeUrl: tipPickupStatus.exchange_url,
merchantBaseUrl: res.merchantBaseUrl,
nextUrl: undefined,
@@ -117,18 +120,13 @@ export async function getTipStatus(
await ws.db.put(Stores.tips, tipRecord);
}
- const tipStatus: TipStatus = {
+ const tipStatus: PrepareTipResult = {
accepted: !!tipRecord && !!tipRecord.acceptedTimestamp,
- amount: Amounts.parseOrThrow(tipPickupStatus.amount),
- amountLeft: Amounts.parseOrThrow(tipPickupStatus.amount_left),
- exchangeUrl: tipPickupStatus.exchange_url,
- nextUrl: tipPickupStatus.extra.next_url,
- merchantOrigin: merchantOrigin,
- merchantTipId: res.merchantTipId,
- expirationTimestamp: tipPickupStatus.stamp_expire,
- timestamp: tipPickupStatus.stamp_created,
- totalFees: tipRecord.totalFees,
- tipId: tipRecord.tipId,
+ amount: Amounts.stringify(tipPickupStatus.tip_amount),
+ exchangeBaseUrl: tipPickupStatus.exchange_url,
+ expirationTimestamp: tipPickupStatus.expiration,
+ totalFees: Amounts.stringify(tipRecord.totalFees),
+ walletTipId: tipRecord.walletTipId,
};
return tipStatus;
@@ -152,7 +150,9 @@ async function incrementTipRetry(
t.lastError = err;
await tx.put(Stores.tips, t);
});
- ws.notify({ type: NotificationType.TipOperationError });
+ if (err) {
+ ws.notify({ type: NotificationType.TipOperationError, error: err });
+ }
}
export async function processTip(
@@ -225,15 +225,8 @@ async function processTipImpl(
}
tipRecord = await ws.db.get(Stores.tips, tipId);
- if (!tipRecord) {
- throw Error("tip not in database");
- }
-
- if (!tipRecord.planchets) {
- throw Error("invariant violated");
- }
-
- logger.trace("got planchets for tip!");
+ checkDbInvariant(!!tipRecord, "tip record should be in database");
+ checkDbInvariant(!!tipRecord.planchets, "tip record should have planchets");
// Planchets in the form that the merchant expects
const planchetsDetail: TipPlanchetDetail[] = tipRecord.planchets.map((p) => ({
@@ -241,23 +234,17 @@ async function processTipImpl(
denom_pub_hash: p.denomPubHash,
}));
- let merchantResp;
-
- const tipStatusUrl = new URL("tip-pickup", tipRecord.merchantBaseUrl);
-
- try {
- const req = { planchets: planchetsDetail, tip_id: tipRecord.merchantTipId };
- merchantResp = await ws.http.postJson(tipStatusUrl.href, req);
- if (merchantResp.status !== 200) {
- throw Error(`unexpected status ${merchantResp.status} for tip-pickup`);
- }
- logger.trace("got merchant resp:", merchantResp);
- } catch (e) {
- logger.warn("tipping failed", e);
- throw e;
- }
+ const tipStatusUrl = new URL(
+ `/tips/${tipRecord.merchantTipId}/pickup`,
+ tipRecord.merchantBaseUrl,
+ );
- const response = codecForTipResponse().decode(await merchantResp.json());
+ const req = { planchets: planchetsDetail };
+ const merchantResp = await ws.http.postJson(tipStatusUrl.href, req);
+ const response = await readSuccessResponseJsonOrThrow(
+ merchantResp,
+ codecForTipResponse(),
+ );
if (response.reserve_sigs.length !== tipRecord.planchets.length) {
throw Error("number of tip responses does not match requested planchets");
@@ -293,7 +280,7 @@ async function processTipImpl(
exchangeBaseUrl: tipRecord.exchangeUrl,
source: {
type: WithdrawalSourceType.Tip,
- tipId: tipRecord.tipId,
+ tipId: tipRecord.walletTipId,
},
timestampStart: getTimestampNow(),
withdrawalGroupId: withdrawalGroupId,
diff --git a/packages/taler-wallet-core/src/types/dbTypes.ts b/packages/taler-wallet-core/src/types/dbTypes.ts
index 0ee41a6a5..4e2ba1bb4 100644
--- a/packages/taler-wallet-core/src/types/dbTypes.ts
+++ b/packages/taler-wallet-core/src/types/dbTypes.ts
@@ -986,7 +986,7 @@ export interface TipRecord {
/**
* Tip ID chosen by the wallet.
*/
- tipId: string;
+ walletTipId: string;
/**
* The merchant's identifier for this tip.
@@ -1760,7 +1760,7 @@ class ReserveHistoryStore extends Store<ReserveHistoryRecord> {
class TipsStore extends Store<TipRecord> {
constructor() {
- super("tips", { keyPath: "tipId" });
+ super("tips", { keyPath: "walletTipId" });
}
}
diff --git a/packages/taler-wallet-core/src/types/notifications.ts b/packages/taler-wallet-core/src/types/notifications.ts
index 7a51f0d83..e1b9a7aff 100644
--- a/packages/taler-wallet-core/src/types/notifications.ts
+++ b/packages/taler-wallet-core/src/types/notifications.ts
@@ -186,6 +186,7 @@ export interface ProposalOperationErrorNotification {
export interface TipOperationErrorNotification {
type: NotificationType.TipOperationError;
+ error: TalerErrorDetails;
}
export interface WithdrawOperationErrorNotification {
diff --git a/packages/taler-wallet-core/src/types/talerTypes.ts b/packages/taler-wallet-core/src/types/talerTypes.ts
index c944f1561..52dc4cb62 100644
--- a/packages/taler-wallet-core/src/types/talerTypes.ts
+++ b/packages/taler-wallet-core/src/types/talerTypes.ts
@@ -773,17 +773,11 @@ export class WithdrawOperationStatusResponse {
* Response from the merchant.
*/
export class TipPickupGetResponse {
- extra: any;
-
- amount: string;
-
- amount_left: string;
+ tip_amount: string;
exchange_url: string;
- stamp_expire: Timestamp;
-
- stamp_created: Timestamp;
+ expiration: Timestamp;
}
export class WithdrawResponse {
@@ -1261,12 +1255,9 @@ export const codecForWithdrawOperationStatusResponse = (): Codec<
export const codecForTipPickupGetResponse = (): Codec<TipPickupGetResponse> =>
buildCodecForObject<TipPickupGetResponse>()
- .property("extra", codecForAny())
- .property("amount", codecForString())
- .property("amount_left", codecForString())
+ .property("tip_amount", codecForString())
.property("exchange_url", codecForString())
- .property("stamp_expire", codecForTimestamp)
- .property("stamp_created", codecForTimestamp)
+ .property("expiration", codecForTimestamp)
.build("TipPickupGetResponse");
export const codecForRecoupConfirmation = (): Codec<RecoupConfirmation> =>
diff --git a/packages/taler-wallet-core/src/types/walletTypes.ts b/packages/taler-wallet-core/src/types/walletTypes.ts
index 82f29c39d..fb049caf9 100644
--- a/packages/taler-wallet-core/src/types/walletTypes.ts
+++ b/packages/taler-wallet-core/src/types/walletTypes.ts
@@ -38,7 +38,7 @@ import {
ExchangeWireInfo,
DenominationSelectionInfo,
} from "./dbTypes";
-import { Timestamp } from "../util/time";
+import { Timestamp, codecForTimestamp } from "../util/time";
import {
buildCodecForObject,
codecForString,
@@ -348,23 +348,33 @@ export class ReturnCoinsRequest {
static checked: (obj: any) => ReturnCoinsRequest;
}
-/**
- * Status of processing a tip.
- */
-export interface TipStatus {
+export interface PrepareTipResult {
+ /**
+ * Unique ID for the tip assigned by the wallet.
+ * Typically different from the merchant-generated tip ID.
+ */
+ walletTipId: string;
+
+ /**
+ * Has the tip already been accepted?
+ */
accepted: boolean;
- amount: AmountJson;
- amountLeft: AmountJson;
- nextUrl: string;
- exchangeUrl: string;
- tipId: string;
- merchantTipId: string;
- merchantOrigin: string;
+ amount: AmountString;
+ totalFees: AmountString;
+ exchangeBaseUrl: string;
expirationTimestamp: Timestamp;
- timestamp: Timestamp;
- totalFees: AmountJson;
}
+export const codecForPrepareTipResult = (): Codec<PrepareTipResult> =>
+ buildCodecForObject<PrepareTipResult>()
+ .property("accepted", codecForBoolean())
+ .property("amount", codecForAmountString())
+ .property("totalFees", codecForAmountString())
+ .property("exchangeBaseUrl", codecForString())
+ .property("expirationTimestamp", codecForTimestamp)
+ .property("walletTipId", codecForString())
+ .build("PrepareTipResult");
+
export interface BenchmarkResult {
time: { [s: string]: number };
repetitions: number;
@@ -903,3 +913,21 @@ export const codecForForceRefreshRequest = (): Codec<ForceRefreshRequest> =>
buildCodecForObject<ForceRefreshRequest>()
.property("coinPubList", codecForList(codecForString()))
.build("ForceRefreshRequest");
+
+export interface PrepareTipRequest {
+ talerTipUri: string;
+}
+
+export const codecForPrepareTipRequest = (): Codec<PrepareTipRequest> =>
+ buildCodecForObject<PrepareTipRequest>()
+ .property("talerTipUri", codecForString())
+ .build("PrepareTipRequest");
+
+export interface AcceptTipRequest {
+ walletTipId: string;
+}
+
+export const codecForAcceptTipRequest = (): Codec<AcceptTipRequest> =>
+ buildCodecForObject<AcceptTipRequest>()
+ .property("walletTipId", codecForString())
+ .build("AcceptTipRequest");
diff --git a/packages/taler-wallet-core/src/wallet.ts b/packages/taler-wallet-core/src/wallet.ts
index 9666665a4..0507ac8b2 100644
--- a/packages/taler-wallet-core/src/wallet.ts
+++ b/packages/taler-wallet-core/src/wallet.ts
@@ -59,7 +59,6 @@ import {
ConfirmPayResult,
ReturnCoinsRequest,
SenderWireInfos,
- TipStatus,
PreparePayResult,
AcceptWithdrawalResponse,
PurchaseDetails,
@@ -93,6 +92,9 @@ import {
codecForSetCoinSuspendedRequest,
codecForForceExchangeUpdateRequest,
codecForForceRefreshRequest,
+ PrepareTipResult,
+ codecForPrepareTipRequest,
+ codecForAcceptTipRequest,
} from "./types/walletTypes";
import { Logger } from "./util/logging";
@@ -121,7 +123,7 @@ import {
import { processWithdrawGroup } from "./operations/withdraw";
import { getPendingOperations } from "./operations/pending";
import { getBalances } from "./operations/balance";
-import { acceptTip, getTipStatus, processTip } from "./operations/tip";
+import { acceptTip, prepareTip, processTip } from "./operations/tip";
import { TimerGroup } from "./util/timer";
import { AsyncCondition } from "./util/promiseUtils";
import { AsyncOpMemoSingle } from "./util/asyncMemo";
@@ -769,8 +771,8 @@ export class Wallet {
}
}
- async getTipStatus(talerTipUri: string): Promise<TipStatus> {
- return getTipStatus(this.ws, talerTipUri);
+ async prepareTip(talerTipUri: string): Promise<PrepareTipResult> {
+ return prepareTip(this.ws, talerTipUri);
}
async abortFailedPayment(contractTermsHash: string): Promise<void> {
@@ -1096,6 +1098,15 @@ export class Wallet {
refreshGroupId,
};
}
+ case "prepareTip": {
+ const req = codecForPrepareTipRequest().decode(payload);
+ return await this.prepareTip(req.talerTipUri);
+ }
+ case "acceptTip": {
+ const req = codecForAcceptTipRequest().decode(payload);
+ await this.acceptTip(req.walletTipId);
+ return {};
+ }
}
throw OperationFailedError.fromCode(
TalerErrorCode.WALLET_CORE_API_OPERATION_UNKNOWN,
diff --git a/packages/taler-wallet-webextension/src/wxApi.ts b/packages/taler-wallet-webextension/src/wxApi.ts
index 947b63cea..9bc4a08e6 100644
--- a/packages/taler-wallet-webextension/src/wxApi.ts
+++ b/packages/taler-wallet-webextension/src/wxApi.ts
@@ -22,12 +22,8 @@
* Imports.
*/
import {
- AmountJson,
ConfirmPayResult,
BalancesResponse,
- PurchaseDetails,
- TipStatus,
- BenchmarkResult,
PreparePayResult,
AcceptWithdrawalResponse,
WalletDiagnostics,