gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[GNUnet-SVN] [taler-wallet-core] branch master updated: support for tipp


From: gnunet
Subject: [GNUnet-SVN] [taler-wallet-core] branch master updated: support for tipping protocol changes
Date: Fri, 30 Aug 2019 17:28:02 +0200

This is an automated email from the git hooks/post-receive script.

dold pushed a commit to branch master
in repository wallet-core.

The following commit(s) were added to refs/heads/master by this push:
     new 5ec34429 support for tipping protocol changes
5ec34429 is described below

commit 5ec344290efd937fa82c0704bc7c204a0bf14c78
Author: Florian Dold <address@hidden>
AuthorDate: Fri Aug 30 17:27:59 2019 +0200

    support for tipping protocol changes
---
 src/dbTypes.ts                   | 175 ++++++++++++++++----------
 src/headless/taler-wallet-cli.ts |  18 ++-
 src/talerTypes.ts                | 122 +++++++-----------
 src/taleruri.ts                  |  51 ++++++++
 src/wallet.ts                    | 258 +++++++++++++++++++--------------------
 src/walletTypes.ts               |   8 +-
 src/webex/messages.ts            |   6 +-
 src/webex/pages/tip.tsx          | 203 +++++++++++++-----------------
 src/webex/wxApi.ts               |  11 +-
 src/webex/wxBackend.ts           |  11 +-
 10 files changed, 441 insertions(+), 422 deletions(-)

diff --git a/src/dbTypes.ts b/src/dbTypes.ts
index d9fd2e9d..17e7a89b 100644
--- a/src/dbTypes.ts
+++ b/src/dbTypes.ts
@@ -14,7 +14,6 @@
  TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 
-
 /**
  * Types for records stored in the wallet's database.
  *
@@ -36,11 +35,7 @@ import {
   TipResponse,
 } from "./talerTypes";
 
-import {
-  Index,
-  Store,
-} from "./query";
-
+import { Index, Store } from "./query";
 
 /**
  * Current database version, should be incremented
@@ -50,7 +45,6 @@ import {
  */
 export const WALLET_DB_VERSION = 26;
 
-
 /**
  * A reserve record as stored in the wallet's database.
  */
@@ -81,12 +75,11 @@ export interface ReserveRecord {
    */
   timestamp_depleted: number;
 
-
   /**
    * Time when the information about this reserve was posted to the bank.
-   * 
+   *
    * Only applies if bankWithdrawStatusUrl is defined.
-   * 
+   *
    * Set to 0 if that hasn't happened yet.
    */
   timestamp_reserve_info_posted: number;
@@ -137,7 +130,6 @@ export interface ReserveRecord {
   bankWithdrawStatusUrl?: string;
 }
 
-
 /**
  * Auditor record as stored with currencies in the exchange database.
  */
@@ -156,7 +148,6 @@ export interface AuditorRecord {
   expirationStamp: number;
 }
 
-
 /**
  * Exchange for currencies as stored in the wallet's currency
  * information database.
@@ -172,7 +163,6 @@ export interface ExchangeForCurrencyRecord {
   baseUrl: string;
 }
 
-
 /**
  * Information about a currency as displayed in the wallet's database.
  */
@@ -195,7 +185,6 @@ export interface CurrencyRecord {
   exchanges: ExchangeForCurrencyRecord[];
 }
 
-
 /**
  * Status of a denomination.
  */
@@ -214,7 +203,6 @@ export enum DenominationStatus {
   VerifiedBad,
 }
 
-
 /**
  * Denomination record as stored in the wallet's database.
  */
@@ -321,7 +309,6 @@ export class DenominationRecord {
   static checked: (obj: any) => Denomination;
 }
 
-
 /**
  * Exchange record as stored in the wallet's database.
  */
@@ -362,7 +349,6 @@ export interface ExchangeRecord {
   protocolVersion?: string;
 }
 
-
 /**
  * A coin that isn't yet signed by an exchange.
  */
@@ -385,7 +371,6 @@ export interface PreCoinRecord {
   isFromTip: boolean;
 }
 
-
 /**
  * Planchet for a coin during refrehs.
  */
@@ -408,7 +393,6 @@ export interface RefreshPreCoinRecord {
   blindingKey: string;
 }
 
-
 /**
  * Status of a coin.
  */
@@ -441,13 +425,8 @@ export enum CoinStatus {
    * Coin was dirty but can't be refreshed.
    */
   Useless,
-  /**
-   * The coin was withdrawn for a tip that the user hasn't accepted yet.
-   */
-  TainedByTip,
 }
 
-
 /**
  * CoinRecord as stored in the "coins" data store
  * of the wallet database.
@@ -506,7 +485,7 @@ export interface CoinRecord {
    * Reserve public key for the reserve we got this coin from,
    * or zero when we got the coin from refresh.
    */
-  reservePub: string|undefined;
+  reservePub: string | undefined;
 
   /**
    * Status of the coin.
@@ -514,7 +493,6 @@ export interface CoinRecord {
   status: CoinStatus;
 }
 
-
 /**
  * Proposal record, stored in the wallet's database.
  */
@@ -576,7 +554,6 @@ export class ProposalDownloadRecord {
   static checked: (obj: any) => ProposalDownloadRecord;
 }
 
-
 /**
  * Wire fees for an exchange.
  */
@@ -592,7 +569,6 @@ export interface ExchangeWireFeesRecord {
   feesForType: { [wireMethod: string]: WireFee[] };
 }
 
-
 /**
  * Status of a tip we got from a merchant.
  */
@@ -613,12 +589,7 @@ export interface TipRecord {
    */
   amount: AmountJson;
 
-  /**
-   * Coin public keys from the planchets.
-   * This field is redundant and used for indexing the record via
-   * a multi-entry index to look up tip records by coin public key.
-   */
-  coinPubs: string[];
+  totalFees: AmountJson;
 
   /**
    * Timestamp, the tip can't be picked up anymore after this deadline.
@@ -641,7 +612,14 @@ export interface TipRecord {
    * Planchets, the members included in TipPlanchetDetail will be sent to the
    * merchant.
    */
-  planchets: TipPlanchet[];
+  planchets?: TipPlanchet[];
+
+  /**
+   * Coin public keys from the planchets.
+   * This field is redundant and used for indexing the record via
+   * a multi-entry index to look up tip records by coin public key.
+   */
+  coinPubs: string[];
 
   /**
    * Response if the merchant responded,
@@ -657,11 +635,12 @@ export interface TipRecord {
   /**
    * URL to go to once the tip has been accepted.
    */
-  nextUrl: string;
+  nextUrl?: string;
 
   timestamp: number;
-}
 
+  pickupUrl: string;
+}
 
 /**
  * Ongoing refresh
@@ -740,7 +719,6 @@ export interface RefreshSessionRecord {
   id?: number;
 }
 
-
 /**
  * Tipping planchet stored in the database.
  */
@@ -754,7 +732,6 @@ export interface TipPlanchet {
   denomPub: string;
 }
 
-
 /**
  * Wire fee for one wire method as stored in the
  * wallet's database.
@@ -861,7 +838,6 @@ export interface PurchaseRecord {
   abortDone: boolean;
 }
 
-
 /**
  * Information about wire information for bank accounts we withdrew coins from.
  */
@@ -869,7 +845,6 @@ export interface SenderWireRecord {
   paytoUri: string;
 }
 
-
 /**
  * Configuration key/value entries to configure
  * the wallet.
@@ -879,7 +854,6 @@ export interface ConfigRecord {
   value: any;
 }
 
-
 /**
  * Coin that we're depositing ourselves.
  */
@@ -893,7 +867,6 @@ export interface DepositCoin {
   depositedSig?: string;
 }
 
-
 /**
  * Record stored in the wallet's database when the user sends coins back to
  * their own bank account.  Stores the status of coins that are deposited to
@@ -927,7 +900,6 @@ export interface CoinsReturnRecord {
   wire: any;
 }
 
-
 /* tslint:disable:completed-docs */
 
 /**
@@ -939,7 +911,11 @@ export namespace Stores {
       super("exchanges", { keyPath: "baseUrl" });
     }
 
-    pubKeyIndex = new Index<string, ExchangeRecord>(this, "pubKeyIndex", 
"masterPublicKey");
+    pubKeyIndex = new Index<string, ExchangeRecord>(
+      this,
+      "pubKeyIndex",
+      "masterPublicKey",
+    );
   }
 
   class CoinsStore extends Store<CoinRecord> {
@@ -947,8 +923,16 @@ export namespace Stores {
       super("coins", { keyPath: "coinPub" });
     }
 
-    exchangeBaseUrlIndex = new Index<string, CoinRecord>(this, 
"exchangeBaseUrl", "exchangeBaseUrl");
-    denomPubIndex = new Index<string, CoinRecord>(this, "denomPubIndex", 
"denomPub");
+    exchangeBaseUrlIndex = new Index<string, CoinRecord>(
+      this,
+      "exchangeBaseUrl",
+      "exchangeBaseUrl",
+    );
+    denomPubIndex = new Index<string, CoinRecord>(
+      this,
+      "denomPubIndex",
+      "denomPub",
+    );
   }
 
   class ProposalsStore extends Store<ProposalDownloadRecord> {
@@ -958,8 +942,16 @@ export namespace Stores {
         keyPath: "id",
       });
     }
-    urlIndex = new Index<string, ProposalDownloadRecord>(this, "urlIndex", 
"url");
-    timestampIndex = new Index<string, ProposalDownloadRecord>(this, 
"timestampIndex", "timestamp");
+    urlIndex = new Index<string, ProposalDownloadRecord>(
+      this,
+      "urlIndex",
+      "url",
+    );
+    timestampIndex = new Index<string, ProposalDownloadRecord>(
+      this,
+      "timestampIndex",
+      "timestamp",
+    );
   }
 
   class PurchasesStore extends Store<PurchaseRecord> {
@@ -967,23 +959,46 @@ export namespace Stores {
       super("purchases", { keyPath: "contractTermsHash" });
     }
 
-    fulfillmentUrlIndex = new Index<string, PurchaseRecord>(this,
-                                                            
"fulfillmentUrlIndex",
-                                                            
"contractTerms.fulfillment_url");
-    orderIdIndex = new Index<string, PurchaseRecord>(this, "orderIdIndex", 
"contractTerms.order_id");
-    timestampIndex = new Index<string, PurchaseRecord>(this, "timestampIndex", 
"timestamp");
+    fulfillmentUrlIndex = new Index<string, PurchaseRecord>(
+      this,
+      "fulfillmentUrlIndex",
+      "contractTerms.fulfillment_url",
+    );
+    orderIdIndex = new Index<string, PurchaseRecord>(
+      this,
+      "orderIdIndex",
+      "contractTerms.order_id",
+    );
+    timestampIndex = new Index<string, PurchaseRecord>(
+      this,
+      "timestampIndex",
+      "timestamp",
+    );
   }
 
   class DenominationsStore extends Store<DenominationRecord> {
     constructor() {
       // cast needed because of bug in type annotations
-      super("denominations",
-            {keyPath: ["exchangeBaseUrl", "denomPub"] as any as IDBKeyPath});
+      super("denominations", {
+        keyPath: (["exchangeBaseUrl", "denomPub"] as any) as IDBKeyPath,
+      });
     }
 
-    denomPubHashIndex = new Index<string, DenominationRecord>(this, 
"denomPubHashIndex", "denomPubHash");
-    exchangeBaseUrlIndex = new Index<string, DenominationRecord>(this, 
"exchangeBaseUrlIndex", "exchangeBaseUrl");
-    denomPubIndex = new Index<string, DenominationRecord>(this, 
"denomPubIndex", "denomPub");
+    denomPubHashIndex = new Index<string, DenominationRecord>(
+      this,
+      "denomPubHashIndex",
+      "denomPubHash",
+    );
+    exchangeBaseUrlIndex = new Index<string, DenominationRecord>(
+      this,
+      "exchangeBaseUrlIndex",
+      "exchangeBaseUrl",
+    );
+    denomPubIndex = new Index<string, DenominationRecord>(
+      this,
+      "denomPubIndex",
+      "denomPub",
+    );
   }
 
   class CurrenciesStore extends Store<CurrencyRecord> {
@@ -1008,16 +1023,35 @@ export namespace Stores {
     constructor() {
       super("reserves", { keyPath: "reserve_pub" });
     }
-    timestampCreatedIndex = new Index<string, ReserveRecord>(this, 
"timestampCreatedIndex", "created");
-    timestampConfirmedIndex = new Index<string, ReserveRecord>(this, 
"timestampConfirmedIndex", "timestamp_confirmed");
-    timestampDepletedIndex = new Index<string, ReserveRecord>(this, 
"timestampDepletedIndex", "timestamp_depleted");
+    timestampCreatedIndex = new Index<string, ReserveRecord>(
+      this,
+      "timestampCreatedIndex",
+      "created",
+    );
+    timestampConfirmedIndex = new Index<string, ReserveRecord>(
+      this,
+      "timestampConfirmedIndex",
+      "timestamp_confirmed",
+    );
+    timestampDepletedIndex = new Index<string, ReserveRecord>(
+      this,
+      "timestampDepletedIndex",
+      "timestamp_depleted",
+    );
   }
 
   class TipsStore extends Store<TipRecord> {
     constructor() {
-      super("tips", { keyPath: ["tipId", "merchantDomain"] as any as 
IDBKeyPath });
+      super("tips", {
+        keyPath: (["tipId", "merchantDomain"] as any) as IDBKeyPath,
+      });
     }
-    coinPubIndex = new Index<string, TipRecord>(this, "coinPubIndex", 
"coinPubs", { multiEntry: true });
+    coinPubIndex = new Index<string, TipRecord>(
+      this,
+      "coinPubIndex",
+      "coinPubs",
+      { multiEntry: true },
+    );
   }
 
   class SenderWiresStore extends Store<SenderWireRecord> {
@@ -1027,15 +1061,22 @@ export namespace Stores {
   }
 
   export const coins = new CoinsStore();
-  export const coinsReturns = new Store<CoinsReturnRecord>("coinsReturns", 
{keyPath: "contractTermsHash"});
+  export const coinsReturns = new Store<CoinsReturnRecord>("coinsReturns", {
+    keyPath: "contractTermsHash",
+  });
   export const config = new ConfigStore();
   export const currencies = new CurrenciesStore();
   export const denominations = new DenominationsStore();
   export const exchangeWireFees = new ExchangeWireFeesStore();
   export const exchanges = new ExchangeStore();
-  export const precoins = new Store<PreCoinRecord>("precoins", {keyPath: 
"coinPub"});
+  export const precoins = new Store<PreCoinRecord>("precoins", {
+    keyPath: "coinPub",
+  });
   export const proposals = new ProposalsStore();
-  export const refresh = new Store<RefreshSessionRecord>("refresh", {keyPath: 
"id", autoIncrement: true});
+  export const refresh = new Store<RefreshSessionRecord>("refresh", {
+    keyPath: "id",
+    autoIncrement: true,
+  });
   export const reserves = new ReservesStore();
   export const purchases = new PurchasesStore();
   export const tips = new TipsStore();
diff --git a/src/headless/taler-wallet-cli.ts b/src/headless/taler-wallet-cli.ts
index 659cffe6..86eaec64 100644
--- a/src/headless/taler-wallet-cli.ts
+++ b/src/headless/taler-wallet-cli.ts
@@ -127,7 +127,7 @@ program
   });
 
 program
-  .command("withdraw-url <withdraw-url>")
+  .command("withdraw-uri <withdraw-uri>")
   .action(async (withdrawUrl, cmdObj) => {
     applyVerbose(program.verbose);
     console.log("withdrawing", withdrawUrl);
@@ -166,7 +166,21 @@ program
   });
 
 program
-  .command("pay-url <pay-url>")
+  .command("tip-uri <tip-uri>")
+  .action(async (tipUri, cmdObj) => {
+    applyVerbose(program.verbose);
+    console.log("getting tip", tipUri);
+    const wallet = await getDefaultNodeWallet({
+      persistentStoragePath: walletDbPath,
+    });
+    const res = await wallet.getTipStatus(tipUri);
+    console.log("tip status", res);
+    await wallet.acceptTip(tipUri);
+    wallet.stop();
+  });
+
+program
+  .command("pay-uri <pay-uri")
   .option("-y, --yes", "automatically answer yes to prompts")
   .action(async (payUrl, cmdObj) => {
     applyVerbose(program.verbose);
diff --git a/src/talerTypes.ts b/src/talerTypes.ts
index 360be333..73b97c93 100644
--- a/src/talerTypes.ts
+++ b/src/talerTypes.ts
@@ -32,7 +32,6 @@ import * as Amounts from "./amounts";
 
 import { timestampCheck } from "./helpers";
 
-
 /**
  * Denomination as found in the /keys response from the exchange.
  */
@@ -114,7 +113,6 @@ export class Denomination {
   static checked: (obj: any) => Denomination;
 }
 
-
 /**
  * Signature by the auditor that a particular denomination key is audited.
  */
@@ -133,7 +131,6 @@ export class AuditorDenomSig {
   auditor_sig: string;
 }
 
-
 /**
  * Auditor information as given by the exchange in /keys.
  */
@@ -158,7 +155,6 @@ export class Auditor {
   denomination_keys: AuditorDenomSig[];
 }
 
-
 /**
  * Request that we send to the exchange to get a payback.
  */
@@ -191,7 +187,6 @@ export interface PaybackRequest {
   coin_sig: string;
 }
 
-
 /**
  * Response that we get from the exchange for a payback request.
  */
@@ -242,7 +237,6 @@ export class PaybackConfirmation {
   static checked: (obj: any) => PaybackConfirmation;
 }
 
-
 /**
  * Deposit permission for a single coin.
  */
@@ -274,7 +268,6 @@ export interface CoinPaySig {
   exchange_url: string;
 }
 
-
 /**
  * Information about an exchange as stored inside a
  * merchant's contract terms.
@@ -300,11 +293,10 @@ export class ExchangeHandle {
   static checked: (obj: any) => ExchangeHandle;
 }
 
-
 /**
  * Contract terms from a merchant.
  */
-@Checkable.Class({validate: true})
+@Checkable.Class({ validate: true })
 export class ContractTerms {
   static validate(x: ContractTerms) {
     if (x.exchanges.length === 0) {
@@ -447,7 +439,6 @@ export class ContractTerms {
   static checked: (obj: any) => ContractTerms;
 }
 
-
 /**
  * Payment body sent to the merchant's /pay.
  */
@@ -474,7 +465,6 @@ export interface PayReq {
   mode: "pay" | "abort-refund";
 }
 
-
 /**
  * Refund permission in the format that the merchant gives it to us.
  */
@@ -516,7 +506,6 @@ export class MerchantRefundPermission {
   static checked: (obj: any) => MerchantRefundPermission;
 }
 
-
 /**
  * Refund request sent to the exchange.
  */
@@ -560,7 +549,6 @@ export interface RefundRequest {
   merchant_sig: string;
 }
 
-
 /**
  * Response for a refund pickup or a /pay in abort mode.
  */
@@ -591,7 +579,6 @@ export class MerchantRefundResponse {
   static checked: (obj: any) => MerchantRefundResponse;
 }
 
-
 /**
  * Planchet detail sent to the merchant.
  */
@@ -607,7 +594,6 @@ export interface TipPlanchetDetail {
   coin_ev: string;
 }
 
-
 /**
  * Request sent to the merchant to pick up a tip.
  */
@@ -641,7 +627,6 @@ export class ReserveSigSingleton {
   static checked: (obj: any) => ReserveSigSingleton;
 }
 
-
 /**
  * Response to /reserve/status
  */
@@ -690,56 +675,6 @@ export class TipResponse {
 }
 
 /**
- * Token containing all the information for the wallet
- * to process a tip.  Given by the merchant to the wallet.
- */
-@Checkable.Class()
-export class TipToken {
-  /**
-   * Expiration for the tip.
-   */
-  @Checkable.String(timestampCheck)
-  expiration: string;
-
-  /**
-   * URL of the exchange that the tip can be withdrawn from.
-   */
-  @Checkable.String()
-  exchange_url: string;
-
-  /**
-   * Merchant's URL to pick up the tip.
-   */
-  @Checkable.String()
-  pickup_url: string;
-
-  /**
-   * Merchant-chosen tip identifier.
-   */
-  @Checkable.String()
-  tip_id: string;
-
-  /**
-   * Amount of tip.
-   */
-  @Checkable.String()
-  amount: string;
-
-  /**
-   * URL to navigate after finishing tip processing.
-   */
-  @Checkable.String()
-  next_url: string;
-
-  /**
-   * Create a TipToken from untyped JSON.
-   * Validates the schema and throws on error.
-   */
-  static checked: (obj: any) => TipToken;
-}
-
-
-/**
  * Element of the payback list that the
  * exchange gives us in /keys.
  */
@@ -752,11 +687,10 @@ export class Payback {
   h_denom_pub: string;
 }
 
-
 /**
  * Structure that the exchange gives us in /keys.
  */
-@Checkable.Class({extra: true})
+@Checkable.Class({ extra: true })
 export class KeysJson {
   /**
    * List of offered denominations.
@@ -808,7 +742,6 @@ export class KeysJson {
   static checked: (obj: any) => KeysJson;
 }
 
-
 /**
  * Wire fees as anounced by the exchange.
  */
@@ -851,8 +784,7 @@ export class WireFeesJson {
   static checked: (obj: any) => WireFeesJson;
 }
 
-
-@Checkable.Class({extra: true})
+@Checkable.Class({ extra: true })
 export class AccountInfo {
   @Checkable.String()
   url: string;
@@ -861,10 +793,12 @@ export class AccountInfo {
   master_sig: string;
 }
 
-
-@Checkable.Class({extra: true})
+@Checkable.Class({ extra: true })
 export class ExchangeWireJson {
-  @Checkable.Map(Checkable.String(), Checkable.List(Checkable.Value(() => 
WireFeesJson)))
+  @Checkable.Map(
+    Checkable.String(),
+    Checkable.List(Checkable.Value(() => WireFeesJson)),
+  )
   fees: { [methodName: string]: WireFeesJson[] };
 
   @Checkable.List(Checkable.Value(() => AccountInfo))
@@ -873,18 +807,16 @@ export class ExchangeWireJson {
   static checked: (obj: any) => ExchangeWireJson;
 }
 
-
 /**
  * Wire detail, arbitrary object that must at least
  * contain a "type" key.
  */
 export type WireDetail = object & { type: string };
 
-
 /**
  * Proposal returned from the contract URL.
  */
-@Checkable.Class({extra: true})
+@Checkable.Class({ extra: true })
 export class Proposal {
   /**
    * Contract terms for the propoal.
@@ -909,7 +841,7 @@ export class Proposal {
 /**
  * Response from the internal merchant API.
  */
-@Checkable.Class({extra: true})
+@Checkable.Class({ extra: true })
 export class CheckPaymentResponse {
   @Checkable.Boolean()
   paid: boolean;
@@ -939,7 +871,7 @@ export class CheckPaymentResponse {
 /**
  * Response from the bank.
  */
-@Checkable.Class({extra: true})
+@Checkable.Class({ extra: true })
 export class WithdrawOperationStatusResponse {
   @Checkable.Boolean()
   selection_done: boolean;
@@ -967,4 +899,34 @@ export class WithdrawOperationStatusResponse {
    * member.
    */
   static checked: (obj: any) => WithdrawOperationStatusResponse;
-}
\ No newline at end of file
+}
+
+/**
+ * Response from the merchant.
+ */
+@Checkable.Class({ extra: true })
+export class TipPickupGetResponse {
+  @Checkable.AnyObject()
+  extra: any;
+
+  @Checkable.String()
+  amount: string;
+
+  @Checkable.String()
+  amount_left: string;
+
+  @Checkable.String()
+  exchange_url: string;
+
+  @Checkable.String()
+  stamp_expire: string;
+
+  @Checkable.String()
+  stamp_created: string;
+
+  /**
+   * Verify that a value matches the schema of this class and convert it into a
+   * member.
+   */
+  static checked: (obj: any) => TipPickupGetResponse;
+}
diff --git a/src/taleruri.ts b/src/taleruri.ts
index fa305d1d..f5fc7742 100644
--- a/src/taleruri.ts
+++ b/src/taleruri.ts
@@ -26,6 +26,13 @@ export interface WithdrawUriResult {
   statusUrl: string;
 }
 
+export interface TipUriResult {
+  tipPickupUrl: string;
+  tipId: string;
+  merchantInstance: string;
+  merchantOrigin: string;
+}
+
 export function parseWithdrawUri(s: string): WithdrawUriResult | undefined {
   const parsedUri = new URI(s);
   if (parsedUri.scheme() !== "taler") {
@@ -104,3 +111,47 @@ export function parsePayUri(s: string): PayUriResult | 
undefined {
     sessionId: maybeSessionid,
   };
 }
+
+export function parseTipUri(s: string): TipUriResult | undefined {
+  const parsedUri = new URI(s);
+  if (parsedUri.scheme() != "taler") {
+    return undefined;
+  }
+  if (parsedUri.authority() != "tip") {
+    return undefined;
+  }
+
+  let [_, host, maybePath, maybeInstance, tipId] = parsedUri.path().split("/");
+
+  if (!host) {
+    return undefined;
+  }
+
+  if (!maybePath) {
+    return undefined;
+  }
+
+  if (!tipId) {
+    return undefined;
+  }
+
+  if (maybePath === "-") {
+    maybePath = "public/tip-pickup";
+  } else {
+    maybePath = decodeURIComponent(maybePath);
+  }
+  if (maybeInstance === "-") {
+    maybeInstance = "default";
+  }
+
+  const tipPickupUrl = new URI(
+    "https://"; + host + "/" + decodeURIComponent(maybePath),
+  ).href();
+
+  return {
+    tipPickupUrl,
+    tipId: tipId,
+    merchantInstance: maybeInstance,
+    merchantOrigin: new URI(tipPickupUrl).origin(),
+  };
+}
diff --git a/src/wallet.ts b/src/wallet.ts
index e476c94f..fd1be529 100644
--- a/src/wallet.ts
+++ b/src/wallet.ts
@@ -80,8 +80,8 @@ import {
   ReserveStatus,
   TipPlanchetDetail,
   TipResponse,
-  TipToken,
   WithdrawOperationStatusResponse,
+  TipPickupGetResponse,
 } from "./talerTypes";
 import {
   Badge,
@@ -109,7 +109,7 @@ import {
   AcceptWithdrawalResponse,
 } from "./walletTypes";
 import { openPromise } from "./promiseUtils";
-import { parsePayUri, parseWithdrawUri } from "./taleruri";
+import { parsePayUri, parseWithdrawUri, parseTipUri } from "./taleruri";
 
 interface SpeculativePayData {
   payCoinInfo: PayCoinInfo;
@@ -345,7 +345,7 @@ export class Wallet {
   private timerGroup: TimerGroup;
   private speculativePayData: SpeculativePayData | undefined;
   private cachedNextUrl: { [fulfillmentUrl: string]: NextUrlResult } = {};
-  private activeTipOperations: { [s: string]: Promise<TipRecord> } = {};
+  private activeTipOperations: { [s: string]: Promise<void> } = {};
   private activeProcessReserveOperations: {
     [reservePub: string]: Promise<void>;
   } = {};
@@ -1351,33 +1351,7 @@ export class Wallet {
           .add(Stores.coins, coin)
           .finish();
 
-        if (coin.status === CoinStatus.TainedByTip) {
-          const tip = await this.q().getIndexed(
-            Stores.tips.coinPubIndex,
-            coin.coinPub,
-          );
-          if (!tip) {
-            throw Error(
-              `inconsistent DB: tip for coin pub ${coin.coinPub} not found.`,
-            );
-          }
-
-          if (tip.accepted) {
-            console.log("untainting already accepted tip");
-            // Transactionally set coin to fresh.
-            const mutateCoin = (c: CoinRecord) => {
-              if (c.status === CoinStatus.TainedByTip) {
-                c.status = CoinStatus.Fresh;
-              }
-              return c;
-            };
-            await this.q().mutate(Stores.coins, coin.coinPub, mutateCoin);
-            // Show notifications only for accepted tips
-            this.badge.showNotification();
-          }
-        } else {
-          this.badge.showNotification();
-        }
+        this.badge.showNotification();
 
         this.notifier.notify();
         op.resolve();
@@ -1566,7 +1540,7 @@ export class Wallet {
       denomSig,
       exchangeBaseUrl: pc.exchangeBaseUrl,
       reservePub: pc.reservePub,
-      status: pc.isFromTip ? CoinStatus.TainedByTip : CoinStatus.Fresh,
+      status: CoinStatus.Fresh,
     };
     return coin;
   }
@@ -1856,14 +1830,14 @@ export class Wallet {
     return { isTrusted, isAudited };
   }
 
-  async getWithdrawDetails(
-    talerPayUri: string,
+  async getWithdrawDetailsForUri(
+    talerWithdrawUri: string,
     maybeSelectedExchange?: string,
   ): Promise<WithdrawDetails> {
-    const info = await this.downloadWithdrawInfo(talerPayUri);
+    const info = await this.downloadWithdrawInfo(talerWithdrawUri);
     let rci: ReserveCreationInfo | undefined = undefined;
     if (maybeSelectedExchange) {
-      rci = await this.getReserveCreationInfo(
+      rci = await this.getWithdrawDetailsForAmount(
         maybeSelectedExchange,
         info.amount,
       );
@@ -1874,7 +1848,7 @@ export class Wallet {
     };
   }
 
-  async getReserveCreationInfo(
+  async getWithdrawDetailsForAmount(
     baseUrl: string,
     amount: AmountJson,
   ): Promise<ReserveCreationInfo> {
@@ -3331,14 +3305,13 @@ export class Wallet {
     return feeAcc;
   }
 
-  async processTip(tipToken: TipToken): Promise<TipRecord> {
-    const merchantDomain = new URI(tipToken.pickup_url).origin();
-    const key = tipToken.tip_id + merchantDomain;
-
+  async acceptTip(talerTipUri: string): Promise<void> {
+    const { tipId, merchantOrigin } = await this.getTipStatus(talerTipUri);
+    const key = `${tipId}${merchantOrigin}`;
     if (this.activeTipOperations[key]) {
       return this.activeTipOperations[key];
     }
-    const p = this.processTipImpl(tipToken);
+    const p = this.acceptTipImpl(tipId, merchantOrigin);
     this.activeTipOperations[key] = p;
     try {
       return await p;
@@ -3347,56 +3320,61 @@ export class Wallet {
     }
   }
 
-  private async processTipImpl(tipToken: TipToken): Promise<TipRecord> {
-    console.log("got tip token", tipToken);
-
-    const merchantDomain = new URI(tipToken.pickup_url).origin();
-
-    const deadlineSec = getTalerStampSec(tipToken.expiration);
-    if (!deadlineSec) {
-      throw Error("tipping failed (invalid expiration)");
+  private async acceptTipImpl(
+    tipId: string,
+    merchantOrigin: string,
+  ): Promise<void> {
+    let tipRecord = await this.q().get(Stores.tips, [tipId, merchantOrigin]);
+    if (!tipRecord) {
+      throw Error("tip not in database");
     }
 
-    let tipRecord = await this.q().get(Stores.tips, [
-      tipToken.tip_id,
-      merchantDomain,
-    ]);
+    tipRecord.accepted = true;
 
-    if (tipRecord && tipRecord.pickedUp) {
-      return tipRecord;
+    // Create one transactional query, within this transaction
+    // both the tip will be marked as accepted and coins
+    // already withdrawn will be untainted.
+    await this.q()
+      .put(Stores.tips, tipRecord)
+      .finish();
+
+    if (tipRecord.pickedUp) {
+      console.log("tip already picked up");
+      return;
     }
-    const tipAmount = Amounts.parseOrThrow(tipToken.amount);
-    await this.updateExchangeFromUrl(tipToken.exchange_url);
+    await this.updateExchangeFromUrl(tipRecord.exchangeUrl);
     const denomsForWithdraw = await this.getVerifiedWithdrawDenomList(
-      tipToken.exchange_url,
-      tipAmount,
+      tipRecord.exchangeUrl,
+      tipRecord.amount,
     );
-    const planchets = await Promise.all(
-      denomsForWithdraw.map(d => this.cryptoApi.createTipPlanchet(d)),
-    );
-    const coinPubs: string[] = planchets.map(x => x.coinPub);
-    const now = new Date().getTime();
-    tipRecord = {
-      accepted: false,
-      amount: Amounts.parseOrThrow(tipToken.amount),
-      coinPubs,
-      deadline: deadlineSec,
-      exchangeUrl: tipToken.exchange_url,
-      merchantDomain,
-      nextUrl: tipToken.next_url,
-      pickedUp: false,
-      planchets,
-      timestamp: now,
-      tipId: tipToken.tip_id,
-    };
 
-    let merchantResp;
+    if (!tipRecord.planchets) {
+      const planchets = await Promise.all(
+        denomsForWithdraw.map(d => this.cryptoApi.createTipPlanchet(d)),
+      );
+      const coinPubs: string[] = planchets.map(x => x.coinPub);
 
-    tipRecord = await this.q().putOrGetExisting(Stores.tips, tipRecord, [
-      tipRecord.tipId,
-      merchantDomain,
-    ]);
-    this.notifier.notify();
+      await this.q().mutate(Stores.tips, [tipId, merchantOrigin], r => {
+        if (!r.planchets) {
+          r.planchets = planchets;
+          r.coinPubs = coinPubs;
+        }
+        return r;
+      });
+
+      this.notifier.notify();
+    }
+
+    tipRecord = await this.q().get(Stores.tips, [tipId, merchantOrigin]);
+    if (!tipRecord) {
+      throw Error("tip not in database");
+    }
+
+    if (!tipRecord.planchets) {
+      throw Error("invariant violated");
+    }
+
+    console.log("got planchets for tip!");
 
     // Planchets in the form that the merchant expects
     const planchetsDetail: TipPlanchetDetail[] = tipRecord.planchets.map(p => 
({
@@ -3404,9 +3382,12 @@ export class Wallet {
       denom_pub_hash: p.denomPubHash,
     }));
 
+    let merchantResp;
+
     try {
-      const req = { planchets: planchetsDetail, tip_id: tipToken.tip_id };
-      merchantResp = await this.http.postJson(tipToken.pickup_url, req);
+      const req = { planchets: planchetsDetail, tip_id: tipId };
+      merchantResp = await this.http.postJson(tipRecord.pickupUrl, req);
+      console.log("got merchant resp:", merchantResp);
     } catch (e) {
       console.log("tipping failed", e);
       throw e;
@@ -3434,7 +3415,7 @@ export class Wallet {
         withdrawSig: response.reserve_sigs[i].reserve_sig,
       };
       await this.q().put(Stores.precoins, preCoin);
-      this.processPreCoin(preCoin.coinPub);
+      await this.processPreCoin(preCoin.coinPub);
     }
 
     tipRecord.pickedUp = true;
@@ -3443,61 +3424,75 @@ export class Wallet {
       .put(Stores.tips, tipRecord)
       .finish();
     this.notifier.notify();
-
-    return tipRecord;
+    this.badge.showNotification();
+    return;
   }
 
-  /**
-   * Start using the coins from a tip.
-   */
-  async acceptTip(tipToken: TipToken): Promise<void> {
-    const tipId = tipToken.tip_id;
-    const merchantDomain = new URI(tipToken.pickup_url).origin();
-    const tipRecord = await this.q().get(Stores.tips, [tipId, merchantDomain]);
-    if (!tipRecord) {
-      throw Error("tip not found");
+  async getTipStatus(talerTipUri: string): Promise<TipStatus> {
+    const res = parseTipUri(talerTipUri);
+    if (!res) {
+      throw Error("invalid taler://tip URI");
     }
-    tipRecord.accepted = true;
 
-    // Create one transactional query, within this transaction
-    // both the tip will be marked as accepted and coins
-    // already withdrawn will be untainted.
-    const q = this.q();
+    const tipStatusUrl = new URI(res.tipPickupUrl)
+      .addQuery({
+        instance: res.merchantInstance,
+        tip_id: res.tipId,
+      })
+      .href();
+    console.log("checking tip status from", tipStatusUrl);
+    const merchantResp = await this.http.get(tipStatusUrl);
+    console.log("resp:", merchantResp.responseJson);
+    const tipPickupStatus = TipPickupGetResponse.checked(
+      merchantResp.responseJson,
+    );
 
-    q.put(Stores.tips, tipRecord);
+    console.log("status", tipPickupStatus);
 
-    const updateCoin = (c: CoinRecord) => {
-      if (c.status === CoinStatus.TainedByTip) {
-        c.status = CoinStatus.Fresh;
-      }
-      return c;
-    };
+    let amount = Amounts.parseOrThrow(tipPickupStatus.amount);
 
-    for (const coinPub of tipRecord.coinPubs) {
-      q.mutate(Stores.coins, coinPub, updateCoin);
-    }
+    let tipRecord = await this.q().get(Stores.tips, [
+      res.tipId,
+      res.merchantOrigin,
+    ]);
+    if (!tipRecord) {
+      const withdrawDetails = await this.getWithdrawDetailsForAmount(
+        tipPickupStatus.exchange_url,
+        amount,
+      );
 
-    await q.finish();
-    this.badge.showNotification();
-    this.notifier.notify();
-  }
+      tipRecord = {
+        accepted: false,
+        amount,
+        coinPubs: [],
+        deadline: getTalerStampSec(tipPickupStatus.stamp_expire)!,
+        exchangeUrl: tipPickupStatus.exchange_url,
+        merchantDomain: res.merchantOrigin,
+        nextUrl: undefined,
+        pickedUp: false,
+        planchets: undefined,
+        response: undefined,
+        timestamp: new Date().getTime(),
+        tipId: res.tipId,
+        pickupUrl: res.tipPickupUrl,
+        totalFees: Amounts.add(withdrawDetails.overhead, 
withdrawDetails.withdrawFee).amount,
+      };
+      await this.q().put(Stores.tips, tipRecord);
+    }
 
-  async getTipStatus(tipToken: TipToken): Promise<TipStatus> {
-    const tipId = tipToken.tip_id;
-    const merchantDomain = new URI(tipToken.pickup_url).origin();
-    const tipRecord = await this.q().get(Stores.tips, [tipId, merchantDomain]);
-    const amount = Amounts.parseOrThrow(tipToken.amount);
-    const exchangeUrl = tipToken.exchange_url;
-    this.processTip(tipToken);
-    const nextUrl = tipToken.next_url;
     const tipStatus: TipStatus = {
       accepted: !!tipRecord && tipRecord.accepted,
-      amount,
-      exchangeUrl,
-      merchantDomain,
-      nextUrl,
-      tipRecord,
+      amount: Amounts.parseOrThrow(tipPickupStatus.amount),
+      amountLeft: Amounts.parseOrThrow(tipPickupStatus.amount_left),
+      exchangeUrl: tipPickupStatus.exchange_url,
+      nextUrl: tipPickupStatus.extra.next_url,
+      merchantOrigin: res.merchantOrigin,
+      tipId: res.tipId,
+      expirationTimestamp: getTalerStampSec(tipPickupStatus.stamp_expire)!,
+      timestamp: getTalerStampSec(tipPickupStatus.stamp_created)!,
+      totalFees: tipRecord.totalFees,
     };
+
     return tipStatus;
   }
 
@@ -3526,11 +3521,6 @@ export class Wallet {
     const abortReq = { ...purchase.payReq, mode: "abort-refund" };
 
     try {
-      const config = {
-        headers: { "Content-Type": "application/json;charset=UTF-8" },
-        timeout: 5000 /* 5 seconds */,
-        validateStatus: (s: number) => s === 200,
-      };
       resp = await this.http.postJson(purchase.contractTerms.pay_url, 
abortReq);
     } catch (e) {
       // Gives the user the option to retry / abort and refresh
diff --git a/src/walletTypes.ts b/src/walletTypes.ts
index c657ac02..aec02937 100644
--- a/src/walletTypes.ts
+++ b/src/walletTypes.ts
@@ -427,10 +427,14 @@ export interface CoinWithDenom {
 export interface TipStatus {
   accepted: boolean;
   amount: AmountJson;
+  amountLeft: AmountJson;
   nextUrl: string;
-  merchantDomain: string;
   exchangeUrl: string;
-  tipRecord?: TipRecord;
+  tipId: string;
+  merchantOrigin: string;
+  expirationTimestamp: number;
+  timestamp: number;
+  totalFees: AmountJson;
 }
 
 /**
diff --git a/src/webex/messages.ts b/src/webex/messages.ts
index ca0e1c7e..f1046d5c 100644
--- a/src/webex/messages.ts
+++ b/src/webex/messages.ts
@@ -174,11 +174,11 @@ export interface MessageMap {
     response: AmountJson;
   };
   "accept-tip": {
-    request: { tipToken: talerTypes.TipToken };
-    response: walletTypes.TipStatus;
+    request: { talerTipUri: string };
+    response: void;
   };
   "get-tip-status": {
-    request: { tipToken: talerTypes.TipToken };
+    request: { talerTipUri: string };
     response: walletTypes.TipStatus;
   };
   "clear-notification": {
diff --git a/src/webex/pages/tip.tsx b/src/webex/pages/tip.tsx
index c13120c4..a3f5c38c 100644
--- a/src/webex/pages/tip.tsx
+++ b/src/webex/pages/tip.tsx
@@ -14,7 +14,6 @@
  TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 
-
 /**
  * Page shown to the user to confirm creation
  * of a reserve, usually requested by the bank.
@@ -28,152 +27,114 @@ import URI = require("urijs");
 
 import * as i18n from "../../i18n";
 
-import {
-  acceptTip,
-  getReserveCreationInfo,
-  getTipStatus,
-} from "../wxApi";
+import { acceptTip, getReserveCreationInfo, getTipStatus } from "../wxApi";
 
-import {
-  WithdrawDetailView,
-  renderAmount,
-} from "../renderHtml";
+import { WithdrawDetailView, renderAmount } from "../renderHtml";
 
 import * as Amounts from "../../amounts";
-import { TipToken } from "../../talerTypes";
-import { ReserveCreationInfo, TipStatus } from "../../walletTypes";
+import { useState, useEffect } from "react";
+import { TipStatus } from "../../walletTypes";
 
-interface TipDisplayProps {
-  tipToken: TipToken;
+interface LoadingButtonProps {
+  loading: boolean;
 }
 
-interface TipDisplayState {
-  tipStatus?: TipStatus;
-  rci?: ReserveCreationInfo;
-  working: boolean;
-  discarded: boolean;
+function LoadingButton(
+  props:
+    & React.PropsWithChildren<LoadingButtonProps>
+    & React.DetailedHTMLProps<
+        React.ButtonHTMLAttributes<HTMLButtonElement>,
+        HTMLButtonElement
+      >,
+) {
+  return (
+    <button
+      className="pure-button pure-button-primary"
+      type="button"
+      {...props}
+    >
+      {props.loading ? <span><object className="svg-icon svg-baseline" 
data="/img/spinner-bars.svg" /></span> : null}
+      {props.children}
+    </button>
+  );
 }
 
-class TipDisplay extends React.Component<TipDisplayProps, TipDisplayState> {
-  constructor(props: TipDisplayProps) {
-    super(props);
-    this.state = { working: false, discarded: false };
-  }
-
-  async update() {
-    const tipStatus = await getTipStatus(this.props.tipToken);
-    this.setState({ tipStatus });
-    const rci = await getReserveCreationInfo(tipStatus.exchangeUrl, 
tipStatus.amount);
-    this.setState({ rci });
-  }
-
-  componentDidMount() {
-    this.update();
-    const port = chrome.runtime.connect();
-    port.onMessage.addListener((msg: any) => {
-      if (msg.notify) {
-        console.log("got notified");
-        this.update();
-      }
-    });
-    this.update();
-  }
-
-  renderExchangeInfo() {
-    const rci = this.state.rci;
-    if (!rci) {
-      return <p>Waiting for info about exchange ...</p>;
-    }
-    const totalCost = Amounts.add(rci.overhead, rci.withdrawFee).amount;
-    return (
-      <div>
-        <p>
-          The tip is handled by the exchange 
<strong>{rci.exchangeInfo.baseUrl}</strong>.{" "}
-          The exchange provider will charge
-          {" "}
-          <strong>{renderAmount(totalCost)}</strong>
-          {" "}.
-        </p>
-        <WithdrawDetailView rci={rci} />
-      </div>
-    );
+function TipDisplay(props: { talerTipUri: string }) {
+  const [tipStatus, setTipStatus] = useState<TipStatus | undefined>(undefined);
+  const [discarded, setDiscarded] = useState(false);
+  const [loading, setLoading] = useState(false);
+  const [finished, setFinished] = useState(false);
+
+  useEffect(() => {
+    const doFetch = async () => {
+      const ts = await getTipStatus(props.talerTipUri);
+      setTipStatus(ts);
+    };
+    doFetch();
+  }, []);
+
+  if (discarded) {
+    return <span>You've discarded the tip.</span>;
   }
 
-  accept() {
-    this.setState({ working: true});
-    acceptTip(this.props.tipToken);
+  if (finished) {
+    return <span>Tip has been accepted!</span>;
   }
 
-  discard() {
-    this.setState({ discarded: true });
+  if (!tipStatus) {
+    return <span>Loading ...</span>;
   }
 
-  render(): JSX.Element {
-    const ts = this.state.tipStatus;
-    if (!ts) {
-      return <p>Processing ...</p>;
-    }
-
-    const renderAccepted = () => (
-      <>
-        <p>You've accepted this tip! <a href={ts.nextUrl}>Go back to 
merchant</a></p>
-        {this.renderExchangeInfo()}
-      </>
-    );
-
-    const renderButtons = () => (
-      <>
+  const discard = () => {
+    setDiscarded(true);
+  };
+
+  const accept = async () => {
+    setLoading(true);
+    await acceptTip(props.talerTipUri);
+    setFinished(true);
+  };
+
+  return (
+    <div>
+      <h2>Tip Received!</h2>
+      <p>
+        You received a tip of 
<strong>{renderAmount(tipStatus.amount)}</strong>{" "}
+        from <span> </span>
+        <strong>{tipStatus.merchantOrigin}</strong>.
+      </p>
+      <p>
+        The tip is handled by the exchange{" "}
+        <strong>{tipStatus.exchangeUrl}</strong>. This exchange will charge 
fees
+        of <strong>{renderAmount(tipStatus.totalFees)}</strong> for this
+        operation.
+      </p>
       <form className="pure-form">
-        <button
-            className="pure-button pure-button-primary"
-            type="button"
-            disabled={!(this.state.rci && this.state.tipStatus && 
this.state.tipStatus.tipRecord)}
-            onClick={() => this.accept()}>
-          { this.state.working
-            ? <span><object className="svg-icon svg-baseline" 
data="/img/spinner-bars.svg" /> </span>
-            : null }
-          Accept tip
-        </button>
+        <LoadingButton loading={loading} onClick={() => accept()}>
+          AcceptTip
+        </LoadingButton>
         {" "}
-        <button className="pure-button" type="button" onClick={() => 
this.discard()}>
+        <button className="pure-button" type="button" onClick={() => 
discard()}>
           Discard tip
         </button>
       </form>
-      { this.renderExchangeInfo() }
-      </>
-    );
-
-    const renderDiscarded = () => (
-      <p>You've discarded this tip. <a href={ts.nextUrl}>Go back to 
merchant.</a></p>
-    );
-
-    return (
-      <div>
-        <h2>Tip Received!</h2>
-        <p>You received a tip of <strong>{renderAmount(ts.amount)}</strong> 
from <span> </span>
-        <strong>{ts.merchantDomain}</strong>.</p>
-        {
-          this.state.discarded
-          ? renderDiscarded()
-          : ts.accepted
-          ? renderAccepted()
-          : renderButtons()
-        }
-      </div>
-    );
-  }
+    </div>
+  );
 }
 
 async function main() {
   try {
     const url = new URI(document.location.href);
     const query: any = URI.parseQuery(url.query());
+    const talerTipUri = query.talerTipUri;
+    if (typeof talerTipUri !== "string") {
+      throw Error("talerTipUri must be a string");
+    }
 
-    const tipToken = TipToken.checked(JSON.parse(query.tip_token));
-
-    ReactDOM.render(<TipDisplay tipToken={tipToken} />,
-                    document.getElementById("container")!);
-
+    ReactDOM.render(
+      <TipDisplay talerTipUri={talerTipUri} />,
+      document.getElementById("container")!,
+    );
   } catch (e) {
     // TODO: provide more context information, maybe factor it out into a
     // TODO:generic error reporting function or component.
diff --git a/src/webex/wxApi.ts b/src/webex/wxApi.ts
index feabc781..fd01aed3 100644
--- a/src/webex/wxApi.ts
+++ b/src/webex/wxApi.ts
@@ -45,7 +45,6 @@ import {
 
 import {
   MerchantRefundPermission,
-  TipToken,
 } from "../talerTypes";
 
 import { MessageMap, MessageType } from "./messages";
@@ -349,15 +348,15 @@ export function getFullRefundFees(args: { 
refundPermissions: MerchantRefundPermi
 /**
  * Get the status of processing a tip.
  */
-export function getTipStatus(tipToken: TipToken): Promise<TipStatus> {
-  return callBackend("get-tip-status", { tipToken });
+export function getTipStatus(talerTipUri: string): Promise<TipStatus> {
+  return callBackend("get-tip-status", { talerTipUri });
 }
 
 /**
  * Mark a tip as accepted by the user.
  */
-export function acceptTip(tipToken: TipToken): Promise<TipStatus> {
-  return callBackend("accept-tip", { tipToken });
+export function acceptTip(talerTipUri: string): Promise<void> {
+  return callBackend("accept-tip", { talerTipUri });
 }
 
 
@@ -423,4 +422,4 @@ export function preparePay(talerPayUri: string) {
  */
 export function acceptWithdrawal(talerWithdrawUri: string, selectedExchange: 
string) {
   return callBackend("accept-withdrawal", { talerWithdrawUri, selectedExchange 
});
-}
\ No newline at end of file
+}
diff --git a/src/webex/wxBackend.ts b/src/webex/wxBackend.ts
index d31ea388..5bff4fe0 100644
--- a/src/webex/wxBackend.ts
+++ b/src/webex/wxBackend.ts
@@ -50,7 +50,6 @@ import * as wxApi from "./wxApi";
 import URI = require("urijs");
 import Port = chrome.runtime.Port;
 import MessageSender = chrome.runtime.MessageSender;
-import { TipToken } from "../talerTypes";
 import { BrowserCryptoWorkerFactory } from "../crypto/cryptoApi";
 
 const NeedsWallet = Symbol("NeedsWallet");
@@ -182,7 +181,7 @@ function handleMessage(
         return Promise.resolve({ error: "bad url" });
       }
       const amount = AmountJson.checked(detail.amount);
-      return needsWallet().getReserveCreationInfo(detail.baseUrl, amount);
+      return needsWallet().getWithdrawDetailsForAmount(detail.baseUrl, amount);
     }
     case "get-history": {
       // TODO: limit history length
@@ -295,12 +294,10 @@ function handleMessage(
     case "accept-refund":
       return needsWallet().acceptRefund(detail.refundUrl);
     case "get-tip-status": {
-      const tipToken = TipToken.checked(detail.tipToken);
-      return needsWallet().getTipStatus(tipToken);
+      return needsWallet().getTipStatus(detail.talerTipUri);
     }
     case "accept-tip": {
-      const tipToken = TipToken.checked(detail.tipToken);
-      return needsWallet().acceptTip(tipToken);
+      return needsWallet().acceptTip(detail.talerTipUri);
     }
     case "clear-notification": {
       return needsWallet().clearNotification();
@@ -340,7 +337,7 @@ function handleMessage(
       return needsWallet().benchmarkCrypto(detail.repetitions);
     }
     case "get-withdraw-details": {
-      return needsWallet().getWithdrawDetails(
+      return needsWallet().getWithdrawDetailsForUri(
         detail.talerWithdrawUri,
         detail.maybeSelectedExchange,
       );

-- 
To stop receiving notification emails like this one, please contact
address@hidden.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]