gnunet-svn
[Top][All Lists]
Advanced

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

[GNUnet-SVN] [taler-wallet-webex] branch master updated (183bf550 -> 008


From: gnunet
Subject: [GNUnet-SVN] [taler-wallet-webex] branch master updated (183bf550 -> 008926b1)
Date: Wed, 30 Aug 2017 17:08:59 +0200

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

dold pushed a change to branch master
in repository wallet-webex.

    from 183bf550 mark errata properly
     new 24e021fe don't stop injection early
     new 008926b1 compute full fees for refresh and spending

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 src/crypto/cryptoApi.ts               |   5 +-
 src/crypto/cryptoWorker.ts            |   4 +-
 src/i18n/de.po                        |  97 +++++++++---------
 src/i18n/en-US.po                     |  83 +++++++--------
 src/i18n/fr.po                        |  83 +++++++--------
 src/i18n/it.po                        |  83 +++++++--------
 src/i18n/strings.ts                   |  60 +++++------
 src/i18n/taler-wallet-webex.pot       |  83 +++++++--------
 src/types.ts                          |  36 ++++++-
 src/wallet-test.ts                    |  34 +++----
 src/wallet.ts                         | 186 +++++++++++++++++++++++++---------
 src/webex/messages.ts                 |   6 +-
 src/webex/pages/confirm-contract.html |   1 +
 src/webex/pages/confirm-contract.tsx  |  43 ++++++--
 src/webex/pages/refund.tsx            |  17 ++--
 src/webex/renderHtml.tsx              |  32 ------
 src/webex/wxApi.ts                    |   5 +
 src/webex/wxBackend.ts                |  11 +-
 18 files changed, 483 insertions(+), 386 deletions(-)

diff --git a/src/crypto/cryptoApi.ts b/src/crypto/cryptoApi.ts
index 227c3d34..00013f0d 100644
--- a/src/crypto/cryptoApi.ts
+++ b/src/crypto/cryptoApi.ts
@@ -26,6 +26,7 @@
 import {
   AmountJson,
   CoinRecord,
+  CoinWithDenom,
   ContractTerms,
   DenominationRecord,
   PayCoinInfo,
@@ -36,10 +37,6 @@ import {
   WireFee,
 } from "../types";
 
-import {
-  CoinWithDenom,
-} from "../wallet";
-
 import * as timer from "../timer";
 
 import { startWorker } from "./startWorker";
diff --git a/src/crypto/cryptoWorker.ts b/src/crypto/cryptoWorker.ts
index 1db6e62d..92b766e9 100644
--- a/src/crypto/cryptoWorker.ts
+++ b/src/crypto/cryptoWorker.ts
@@ -28,6 +28,7 @@ import {
   CoinPaySig,
   CoinRecord,
   CoinStatus,
+  CoinWithDenom,
   ContractTerms,
   DenominationRecord,
   PayCoinInfo,
@@ -41,9 +42,6 @@ import {
 import {
   canonicalJson,
 } from "../helpers";
-import {
-  CoinWithDenom,
-} from "../wallet";
 
 import {
   Amount,
diff --git a/src/i18n/de.po b/src/i18n/de.po
index cb57219c..38ce00c4 100644
--- a/src/i18n/de.po
+++ b/src/i18n/de.po
@@ -27,28 +27,28 @@ msgstr ""
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=2; plural=(n != 1);\n"
 
-#: src/webex/pages/confirm-contract.tsx:69
+#: src/webex/pages/confirm-contract.tsx:70
 #, c-format
 msgid "show more details\n"
 msgstr ""
 
-#: src/webex/pages/confirm-contract.tsx:83
+#: src/webex/pages/confirm-contract.tsx:84
 #, c-format
 msgid "Accepted exchanges:"
 msgstr ""
 
-#: src/webex/pages/confirm-contract.tsx:88
+#: src/webex/pages/confirm-contract.tsx:89
 #, c-format
 msgid "Exchanges in the wallet:"
 msgstr ""
 
-#: src/webex/pages/confirm-contract.tsx:154
+#: src/webex/pages/confirm-contract.tsx:156
 #, c-format
 msgid "You have insufficient funds of the requested currency in your wallet."
 msgstr ""
 
 #. tslint:disable-next-line:max-line-length
-#: src/webex/pages/confirm-contract.tsx:156
+#: src/webex/pages/confirm-contract.tsx:158
 #, c-format
 msgid ""
 "You do not have any funds from an exchange that is accepted by this "
@@ -56,67 +56,77 @@ msgid ""
 "wallet."
 msgstr ""
 
-#: src/webex/pages/confirm-create-reserve.tsx:213
+#: src/webex/pages/confirm-contract.tsx:214
+#, c-format
+msgid "The merchant%1$s offers you to purchase:\n"
+msgstr ""
+
+#: src/webex/pages/confirm-contract.tsx:232
+#, fuzzy, c-format
+msgid "Confirm payment"
+msgstr "Bezahlung bestätigen"
+
+#: src/webex/pages/confirm-create-reserve.tsx:179
 #, fuzzy, c-format
 msgid "Withdrawal fees:"
 msgstr "Abheben bei %1$s"
 
-#: src/webex/pages/confirm-create-reserve.tsx:214
+#: src/webex/pages/confirm-create-reserve.tsx:180
 #, c-format
 msgid "Rounding loss:"
 msgstr ""
 
-#: src/webex/pages/confirm-create-reserve.tsx:215
+#: src/webex/pages/confirm-create-reserve.tsx:181
 #, c-format
 msgid "Earliest expiration (for deposit): %1$s"
 msgstr ""
 
-#: src/webex/pages/confirm-create-reserve.tsx:220
+#: src/webex/pages/confirm-create-reserve.tsx:186
 #, c-format
 msgid "# Coins"
 msgstr ""
 
-#: src/webex/pages/confirm-create-reserve.tsx:221
+#: src/webex/pages/confirm-create-reserve.tsx:187
 #, c-format
 msgid "Value"
 msgstr ""
 
-#: src/webex/pages/confirm-create-reserve.tsx:222
+#: src/webex/pages/confirm-create-reserve.tsx:188
 #, fuzzy, c-format
 msgid "Withdraw Fee"
 msgstr "Abheben bei %1$s"
 
-#: src/webex/pages/confirm-create-reserve.tsx:223
+#: src/webex/pages/confirm-create-reserve.tsx:189
 #, c-format
 msgid "Refresh Fee"
 msgstr ""
 
-#: src/webex/pages/confirm-create-reserve.tsx:224
+#: src/webex/pages/confirm-create-reserve.tsx:190
 #, c-format
 msgid "Deposit Fee"
 msgstr ""
 
-#: src/webex/pages/confirm-create-reserve.tsx:278
+#: src/webex/pages/confirm-create-reserve.tsx:244
 #, c-format
 msgid "Select"
 msgstr ""
 
-#: src/webex/pages/confirm-create-reserve.tsx:294
+#: src/webex/pages/confirm-create-reserve.tsx:260
 #, c-format
 msgid "Error: URL may not be relative"
 msgstr ""
 
-#: src/webex/pages/confirm-create-reserve.tsx:362
+#: src/webex/pages/confirm-create-reserve.tsx:328
 #, c-format
 msgid "The exchange is trusted by the wallet.\n"
 msgstr ""
 
-#: src/webex/pages/confirm-create-reserve.tsx:368
+#: src/webex/pages/confirm-create-reserve.tsx:334
 #, c-format
 msgid "The exchange is audited by a trusted auditor.\n"
 msgstr ""
 
-#: src/webex/pages/confirm-create-reserve.tsx:374
+#: src/webex/pages/confirm-create-reserve.tsx:340
 #, c-format
 msgid ""
 "Warning:  The exchange is neither directly trusted nor audited by a trusted "
@@ -124,7 +134,7 @@ msgid ""
 "If you withdraw from this exchange, it will be trusted in the future.\n"
 msgstr ""
 
-#: src/webex/pages/confirm-create-reserve.tsx:383
+#: src/webex/pages/confirm-create-reserve.tsx:349
 #, c-format
 msgid ""
 "Using exchange provider%1$s.\n"
@@ -132,63 +142,58 @@ msgid ""
 " %2$s in fees.\n"
 msgstr ""
 
-#: src/webex/pages/confirm-create-reserve.tsx:397
+#: src/webex/pages/confirm-create-reserve.tsx:363
 #, c-format
 msgid ""
 "Waiting for a response from\n"
 " %1$s"
 msgstr ""
 
-#: src/webex/pages/confirm-create-reserve.tsx:408
-#, c-format
-msgid "A problem occured, see below. %1$s"
-msgstr ""
-
-#: src/webex/pages/confirm-create-reserve.tsx:414
+#: src/webex/pages/confirm-create-reserve.tsx:380
 #, c-format
 msgid ""
 "Information about fees will be available when an exchange provider is "
 "selected."
 msgstr ""
 
-#: src/webex/pages/confirm-create-reserve.tsx:457
+#: src/webex/pages/confirm-create-reserve.tsx:423
 #, c-format
 msgid "Accept fees and withdraw"
 msgstr ""
 
-#: src/webex/pages/confirm-create-reserve.tsx:462
+#: src/webex/pages/confirm-create-reserve.tsx:428
 #, c-format
 msgid "Change Exchange Provider"
 msgstr ""
 
-#: src/webex/pages/confirm-create-reserve.tsx:519
+#: src/webex/pages/confirm-create-reserve.tsx:485
 #, c-format
 msgid "You are about to withdraw %1$s from your bank account into your wallet."
 msgstr ""
 
-#: src/webex/pages/confirm-create-reserve.tsx:607
+#: src/webex/pages/confirm-create-reserve.tsx:570
 #, c-format
 msgid ""
 "Oops, something went wrong. The wallet responded with error status (%1$s)."
 msgstr ""
 
-#: src/webex/pages/confirm-create-reserve.tsx:616
+#: src/webex/pages/confirm-create-reserve.tsx:579
 #, c-format
 msgid "Checking URL, please wait ..."
 msgstr ""
 
-#: src/webex/pages/confirm-create-reserve.tsx:630
+#: src/webex/pages/confirm-create-reserve.tsx:593
 #, c-format
 msgid "Can't parse amount: %1$s"
 msgstr ""
 
-#: src/webex/pages/confirm-create-reserve.tsx:637
+#: src/webex/pages/confirm-create-reserve.tsx:600
 #, c-format
 msgid "Can't parse wire_types: %1$s"
 msgstr ""
 
 #. TODO:generic error reporting function or component.
-#: src/webex/pages/confirm-create-reserve.tsx:663
+#: src/webex/pages/confirm-create-reserve.tsx:626
 #, c-format
 msgid "Fatal error: \"%1$s\"."
 msgstr ""
@@ -311,19 +316,6 @@ msgstr "Bezahlung bestätigen"
 msgid "Cancel"
 msgstr "Saldo"
 
-#: src/webex/renderHtml.tsx:51
-#, fuzzy, c-format
-msgid "The merchant%1$swants to enter a contract over%2$s with you.\n"
-msgstr ""
-"%1$s\n"
-"               möchte einen Vertrag über %2$s\n"
-"               mit Ihnen abschließen."
-
-#: src/webex/renderHtml.tsx:56
-#, fuzzy, c-format
-msgid "You are about to purchase:"
-msgstr "Sie sind dabei, Folgendes zu kaufen:"
-
 #: src/wire.ts:38
 #, c-format
 msgid "Invalid Wire"
@@ -345,6 +337,17 @@ msgid "Unknown Wire Detail"
 msgstr ""
 
 #, fuzzy
+#~ msgid "The merchant%1$swants to enter a contract over%2$s with you.\n"
+#~ msgstr ""
+#~ "%1$s\n"
+#~ "               möchte einen Vertrag über %2$s\n"
+#~ "               mit Ihnen abschließen."
+
+#, fuzzy
+#~ msgid "You are about to purchase:"
+#~ msgstr "Sie sind dabei, Folgendes zu kaufen:"
+
+#, fuzzy
 #~ msgid "Withdrawal fees: %1$s"
 #~ msgstr "Abheben bei %1$s"
 
diff --git a/src/i18n/en-US.po b/src/i18n/en-US.po
index a39c8331..66d4bd11 100644
--- a/src/i18n/en-US.po
+++ b/src/i18n/en-US.po
@@ -27,28 +27,28 @@ msgstr ""
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=2; plural=(n != 1);\n"
 
-#: src/webex/pages/confirm-contract.tsx:69
+#: src/webex/pages/confirm-contract.tsx:70
 #, c-format
 msgid "show more details\n"
 msgstr ""
 
-#: src/webex/pages/confirm-contract.tsx:83
+#: src/webex/pages/confirm-contract.tsx:84
 #, c-format
 msgid "Accepted exchanges:"
 msgstr ""
 
-#: src/webex/pages/confirm-contract.tsx:88
+#: src/webex/pages/confirm-contract.tsx:89
 #, c-format
 msgid "Exchanges in the wallet:"
 msgstr ""
 
-#: src/webex/pages/confirm-contract.tsx:154
+#: src/webex/pages/confirm-contract.tsx:156
 #, c-format
 msgid "You have insufficient funds of the requested currency in your wallet."
 msgstr ""
 
 #. tslint:disable-next-line:max-line-length
-#: src/webex/pages/confirm-contract.tsx:156
+#: src/webex/pages/confirm-contract.tsx:158
 #, c-format
 msgid ""
 "You do not have any funds from an exchange that is accepted by this "
@@ -56,67 +56,77 @@ msgid ""
 "wallet."
 msgstr ""
 
-#: src/webex/pages/confirm-create-reserve.tsx:213
+#: src/webex/pages/confirm-contract.tsx:214
+#, c-format
+msgid "The merchant%1$s offers you to purchase:\n"
+msgstr ""
+
+#: src/webex/pages/confirm-contract.tsx:232
+#, c-format
+msgid "Confirm payment"
+msgstr ""
+
+#: src/webex/pages/confirm-create-reserve.tsx:179
 #, c-format
 msgid "Withdrawal fees:"
 msgstr ""
 
-#: src/webex/pages/confirm-create-reserve.tsx:214
+#: src/webex/pages/confirm-create-reserve.tsx:180
 #, c-format
 msgid "Rounding loss:"
 msgstr ""
 
-#: src/webex/pages/confirm-create-reserve.tsx:215
+#: src/webex/pages/confirm-create-reserve.tsx:181
 #, c-format
 msgid "Earliest expiration (for deposit): %1$s"
 msgstr ""
 
-#: src/webex/pages/confirm-create-reserve.tsx:220
+#: src/webex/pages/confirm-create-reserve.tsx:186
 #, c-format
 msgid "# Coins"
 msgstr ""
 
-#: src/webex/pages/confirm-create-reserve.tsx:221
+#: src/webex/pages/confirm-create-reserve.tsx:187
 #, c-format
 msgid "Value"
 msgstr ""
 
-#: src/webex/pages/confirm-create-reserve.tsx:222
+#: src/webex/pages/confirm-create-reserve.tsx:188
 #, c-format
 msgid "Withdraw Fee"
 msgstr ""
 
-#: src/webex/pages/confirm-create-reserve.tsx:223
+#: src/webex/pages/confirm-create-reserve.tsx:189
 #, c-format
 msgid "Refresh Fee"
 msgstr ""
 
-#: src/webex/pages/confirm-create-reserve.tsx:224
+#: src/webex/pages/confirm-create-reserve.tsx:190
 #, c-format
 msgid "Deposit Fee"
 msgstr ""
 
-#: src/webex/pages/confirm-create-reserve.tsx:278
+#: src/webex/pages/confirm-create-reserve.tsx:244
 #, c-format
 msgid "Select"
 msgstr ""
 
-#: src/webex/pages/confirm-create-reserve.tsx:294
+#: src/webex/pages/confirm-create-reserve.tsx:260
 #, c-format
 msgid "Error: URL may not be relative"
 msgstr ""
 
-#: src/webex/pages/confirm-create-reserve.tsx:362
+#: src/webex/pages/confirm-create-reserve.tsx:328
 #, c-format
 msgid "The exchange is trusted by the wallet.\n"
 msgstr ""
 
-#: src/webex/pages/confirm-create-reserve.tsx:368
+#: src/webex/pages/confirm-create-reserve.tsx:334
 #, c-format
 msgid "The exchange is audited by a trusted auditor.\n"
 msgstr ""
 
-#: src/webex/pages/confirm-create-reserve.tsx:374
+#: src/webex/pages/confirm-create-reserve.tsx:340
 #, c-format
 msgid ""
 "Warning:  The exchange is neither directly trusted nor audited by a trusted "
@@ -124,7 +134,7 @@ msgid ""
 "If you withdraw from this exchange, it will be trusted in the future.\n"
 msgstr ""
 
-#: src/webex/pages/confirm-create-reserve.tsx:383
+#: src/webex/pages/confirm-create-reserve.tsx:349
 #, c-format
 msgid ""
 "Using exchange provider%1$s.\n"
@@ -132,63 +142,58 @@ msgid ""
 " %2$s in fees.\n"
 msgstr ""
 
-#: src/webex/pages/confirm-create-reserve.tsx:397
+#: src/webex/pages/confirm-create-reserve.tsx:363
 #, c-format
 msgid ""
 "Waiting for a response from\n"
 " %1$s"
 msgstr ""
 
-#: src/webex/pages/confirm-create-reserve.tsx:408
-#, c-format
-msgid "A problem occured, see below. %1$s"
-msgstr ""
-
-#: src/webex/pages/confirm-create-reserve.tsx:414
+#: src/webex/pages/confirm-create-reserve.tsx:380
 #, c-format
 msgid ""
 "Information about fees will be available when an exchange provider is "
 "selected."
 msgstr ""
 
-#: src/webex/pages/confirm-create-reserve.tsx:457
+#: src/webex/pages/confirm-create-reserve.tsx:423
 #, c-format
 msgid "Accept fees and withdraw"
 msgstr ""
 
-#: src/webex/pages/confirm-create-reserve.tsx:462
+#: src/webex/pages/confirm-create-reserve.tsx:428
 #, c-format
 msgid "Change Exchange Provider"
 msgstr ""
 
-#: src/webex/pages/confirm-create-reserve.tsx:519
+#: src/webex/pages/confirm-create-reserve.tsx:485
 #, c-format
 msgid "You are about to withdraw %1$s from your bank account into your wallet."
 msgstr ""
 
-#: src/webex/pages/confirm-create-reserve.tsx:607
+#: src/webex/pages/confirm-create-reserve.tsx:570
 #, c-format
 msgid ""
 "Oops, something went wrong. The wallet responded with error status (%1$s)."
 msgstr ""
 
-#: src/webex/pages/confirm-create-reserve.tsx:616
+#: src/webex/pages/confirm-create-reserve.tsx:579
 #, c-format
 msgid "Checking URL, please wait ..."
 msgstr ""
 
-#: src/webex/pages/confirm-create-reserve.tsx:630
+#: src/webex/pages/confirm-create-reserve.tsx:593
 #, c-format
 msgid "Can't parse amount: %1$s"
 msgstr ""
 
-#: src/webex/pages/confirm-create-reserve.tsx:637
+#: src/webex/pages/confirm-create-reserve.tsx:600
 #, c-format
 msgid "Can't parse wire_types: %1$s"
 msgstr ""
 
 #. TODO:generic error reporting function or component.
-#: src/webex/pages/confirm-create-reserve.tsx:663
+#: src/webex/pages/confirm-create-reserve.tsx:626
 #, c-format
 msgid "Fatal error: \"%1$s\"."
 msgstr ""
@@ -311,16 +316,6 @@ msgstr ""
 msgid "Cancel"
 msgstr ""
 
-#: src/webex/renderHtml.tsx:51
-#, c-format
-msgid "The merchant%1$swants to enter a contract over%2$s with you.\n"
-msgstr ""
-
-#: src/webex/renderHtml.tsx:56
-#, c-format
-msgid "You are about to purchase:"
-msgstr ""
-
 #: src/wire.ts:38
 #, c-format
 msgid "Invalid Wire"
diff --git a/src/i18n/fr.po b/src/i18n/fr.po
index dc271a74..d804bd20 100644
--- a/src/i18n/fr.po
+++ b/src/i18n/fr.po
@@ -27,28 +27,28 @@ msgstr ""
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=2; plural=(n != 1);\n"
 
-#: src/webex/pages/confirm-contract.tsx:69
+#: src/webex/pages/confirm-contract.tsx:70
 #, c-format
 msgid "show more details\n"
 msgstr ""
 
-#: src/webex/pages/confirm-contract.tsx:83
+#: src/webex/pages/confirm-contract.tsx:84
 #, c-format
 msgid "Accepted exchanges:"
 msgstr ""
 
-#: src/webex/pages/confirm-contract.tsx:88
+#: src/webex/pages/confirm-contract.tsx:89
 #, c-format
 msgid "Exchanges in the wallet:"
 msgstr ""
 
-#: src/webex/pages/confirm-contract.tsx:154
+#: src/webex/pages/confirm-contract.tsx:156
 #, c-format
 msgid "You have insufficient funds of the requested currency in your wallet."
 msgstr ""
 
 #. tslint:disable-next-line:max-line-length
-#: src/webex/pages/confirm-contract.tsx:156
+#: src/webex/pages/confirm-contract.tsx:158
 #, c-format
 msgid ""
 "You do not have any funds from an exchange that is accepted by this "
@@ -56,67 +56,77 @@ msgid ""
 "wallet."
 msgstr ""
 
-#: src/webex/pages/confirm-create-reserve.tsx:213
+#: src/webex/pages/confirm-contract.tsx:214
+#, c-format
+msgid "The merchant%1$s offers you to purchase:\n"
+msgstr ""
+
+#: src/webex/pages/confirm-contract.tsx:232
+#, c-format
+msgid "Confirm payment"
+msgstr ""
+
+#: src/webex/pages/confirm-create-reserve.tsx:179
 #, c-format
 msgid "Withdrawal fees:"
 msgstr ""
 
-#: src/webex/pages/confirm-create-reserve.tsx:214
+#: src/webex/pages/confirm-create-reserve.tsx:180
 #, c-format
 msgid "Rounding loss:"
 msgstr ""
 
-#: src/webex/pages/confirm-create-reserve.tsx:215
+#: src/webex/pages/confirm-create-reserve.tsx:181
 #, c-format
 msgid "Earliest expiration (for deposit): %1$s"
 msgstr ""
 
-#: src/webex/pages/confirm-create-reserve.tsx:220
+#: src/webex/pages/confirm-create-reserve.tsx:186
 #, c-format
 msgid "# Coins"
 msgstr ""
 
-#: src/webex/pages/confirm-create-reserve.tsx:221
+#: src/webex/pages/confirm-create-reserve.tsx:187
 #, c-format
 msgid "Value"
 msgstr ""
 
-#: src/webex/pages/confirm-create-reserve.tsx:222
+#: src/webex/pages/confirm-create-reserve.tsx:188
 #, c-format
 msgid "Withdraw Fee"
 msgstr ""
 
-#: src/webex/pages/confirm-create-reserve.tsx:223
+#: src/webex/pages/confirm-create-reserve.tsx:189
 #, c-format
 msgid "Refresh Fee"
 msgstr ""
 
-#: src/webex/pages/confirm-create-reserve.tsx:224
+#: src/webex/pages/confirm-create-reserve.tsx:190
 #, c-format
 msgid "Deposit Fee"
 msgstr ""
 
-#: src/webex/pages/confirm-create-reserve.tsx:278
+#: src/webex/pages/confirm-create-reserve.tsx:244
 #, c-format
 msgid "Select"
 msgstr ""
 
-#: src/webex/pages/confirm-create-reserve.tsx:294
+#: src/webex/pages/confirm-create-reserve.tsx:260
 #, c-format
 msgid "Error: URL may not be relative"
 msgstr ""
 
-#: src/webex/pages/confirm-create-reserve.tsx:362
+#: src/webex/pages/confirm-create-reserve.tsx:328
 #, c-format
 msgid "The exchange is trusted by the wallet.\n"
 msgstr ""
 
-#: src/webex/pages/confirm-create-reserve.tsx:368
+#: src/webex/pages/confirm-create-reserve.tsx:334
 #, c-format
 msgid "The exchange is audited by a trusted auditor.\n"
 msgstr ""
 
-#: src/webex/pages/confirm-create-reserve.tsx:374
+#: src/webex/pages/confirm-create-reserve.tsx:340
 #, c-format
 msgid ""
 "Warning:  The exchange is neither directly trusted nor audited by a trusted "
@@ -124,7 +134,7 @@ msgid ""
 "If you withdraw from this exchange, it will be trusted in the future.\n"
 msgstr ""
 
-#: src/webex/pages/confirm-create-reserve.tsx:383
+#: src/webex/pages/confirm-create-reserve.tsx:349
 #, c-format
 msgid ""
 "Using exchange provider%1$s.\n"
@@ -132,63 +142,58 @@ msgid ""
 " %2$s in fees.\n"
 msgstr ""
 
-#: src/webex/pages/confirm-create-reserve.tsx:397
+#: src/webex/pages/confirm-create-reserve.tsx:363
 #, c-format
 msgid ""
 "Waiting for a response from\n"
 " %1$s"
 msgstr ""
 
-#: src/webex/pages/confirm-create-reserve.tsx:408
-#, c-format
-msgid "A problem occured, see below. %1$s"
-msgstr ""
-
-#: src/webex/pages/confirm-create-reserve.tsx:414
+#: src/webex/pages/confirm-create-reserve.tsx:380
 #, c-format
 msgid ""
 "Information about fees will be available when an exchange provider is "
 "selected."
 msgstr ""
 
-#: src/webex/pages/confirm-create-reserve.tsx:457
+#: src/webex/pages/confirm-create-reserve.tsx:423
 #, c-format
 msgid "Accept fees and withdraw"
 msgstr ""
 
-#: src/webex/pages/confirm-create-reserve.tsx:462
+#: src/webex/pages/confirm-create-reserve.tsx:428
 #, c-format
 msgid "Change Exchange Provider"
 msgstr ""
 
-#: src/webex/pages/confirm-create-reserve.tsx:519
+#: src/webex/pages/confirm-create-reserve.tsx:485
 #, c-format
 msgid "You are about to withdraw %1$s from your bank account into your wallet."
 msgstr ""
 
-#: src/webex/pages/confirm-create-reserve.tsx:607
+#: src/webex/pages/confirm-create-reserve.tsx:570
 #, c-format
 msgid ""
 "Oops, something went wrong. The wallet responded with error status (%1$s)."
 msgstr ""
 
-#: src/webex/pages/confirm-create-reserve.tsx:616
+#: src/webex/pages/confirm-create-reserve.tsx:579
 #, c-format
 msgid "Checking URL, please wait ..."
 msgstr ""
 
-#: src/webex/pages/confirm-create-reserve.tsx:630
+#: src/webex/pages/confirm-create-reserve.tsx:593
 #, c-format
 msgid "Can't parse amount: %1$s"
 msgstr ""
 
-#: src/webex/pages/confirm-create-reserve.tsx:637
+#: src/webex/pages/confirm-create-reserve.tsx:600
 #, c-format
 msgid "Can't parse wire_types: %1$s"
 msgstr ""
 
 #. TODO:generic error reporting function or component.
-#: src/webex/pages/confirm-create-reserve.tsx:663
+#: src/webex/pages/confirm-create-reserve.tsx:626
 #, c-format
 msgid "Fatal error: \"%1$s\"."
 msgstr ""
@@ -311,16 +316,6 @@ msgstr ""
 msgid "Cancel"
 msgstr ""
 
-#: src/webex/renderHtml.tsx:51
-#, c-format
-msgid "The merchant%1$swants to enter a contract over%2$s with you.\n"
-msgstr ""
-
-#: src/webex/renderHtml.tsx:56
-#, c-format
-msgid "You are about to purchase:"
-msgstr ""
-
 #: src/wire.ts:38
 #, c-format
 msgid "Invalid Wire"
diff --git a/src/i18n/it.po b/src/i18n/it.po
index dc271a74..d804bd20 100644
--- a/src/i18n/it.po
+++ b/src/i18n/it.po
@@ -27,28 +27,28 @@ msgstr ""
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=2; plural=(n != 1);\n"
 
-#: src/webex/pages/confirm-contract.tsx:69
+#: src/webex/pages/confirm-contract.tsx:70
 #, c-format
 msgid "show more details\n"
 msgstr ""
 
-#: src/webex/pages/confirm-contract.tsx:83
+#: src/webex/pages/confirm-contract.tsx:84
 #, c-format
 msgid "Accepted exchanges:"
 msgstr ""
 
-#: src/webex/pages/confirm-contract.tsx:88
+#: src/webex/pages/confirm-contract.tsx:89
 #, c-format
 msgid "Exchanges in the wallet:"
 msgstr ""
 
-#: src/webex/pages/confirm-contract.tsx:154
+#: src/webex/pages/confirm-contract.tsx:156
 #, c-format
 msgid "You have insufficient funds of the requested currency in your wallet."
 msgstr ""
 
 #. tslint:disable-next-line:max-line-length
-#: src/webex/pages/confirm-contract.tsx:156
+#: src/webex/pages/confirm-contract.tsx:158
 #, c-format
 msgid ""
 "You do not have any funds from an exchange that is accepted by this "
@@ -56,67 +56,77 @@ msgid ""
 "wallet."
 msgstr ""
 
-#: src/webex/pages/confirm-create-reserve.tsx:213
+#: src/webex/pages/confirm-contract.tsx:214
+#, c-format
+msgid "The merchant%1$s offers you to purchase:\n"
+msgstr ""
+
+#: src/webex/pages/confirm-contract.tsx:232
+#, c-format
+msgid "Confirm payment"
+msgstr ""
+
+#: src/webex/pages/confirm-create-reserve.tsx:179
 #, c-format
 msgid "Withdrawal fees:"
 msgstr ""
 
-#: src/webex/pages/confirm-create-reserve.tsx:214
+#: src/webex/pages/confirm-create-reserve.tsx:180
 #, c-format
 msgid "Rounding loss:"
 msgstr ""
 
-#: src/webex/pages/confirm-create-reserve.tsx:215
+#: src/webex/pages/confirm-create-reserve.tsx:181
 #, c-format
 msgid "Earliest expiration (for deposit): %1$s"
 msgstr ""
 
-#: src/webex/pages/confirm-create-reserve.tsx:220
+#: src/webex/pages/confirm-create-reserve.tsx:186
 #, c-format
 msgid "# Coins"
 msgstr ""
 
-#: src/webex/pages/confirm-create-reserve.tsx:221
+#: src/webex/pages/confirm-create-reserve.tsx:187
 #, c-format
 msgid "Value"
 msgstr ""
 
-#: src/webex/pages/confirm-create-reserve.tsx:222
+#: src/webex/pages/confirm-create-reserve.tsx:188
 #, c-format
 msgid "Withdraw Fee"
 msgstr ""
 
-#: src/webex/pages/confirm-create-reserve.tsx:223
+#: src/webex/pages/confirm-create-reserve.tsx:189
 #, c-format
 msgid "Refresh Fee"
 msgstr ""
 
-#: src/webex/pages/confirm-create-reserve.tsx:224
+#: src/webex/pages/confirm-create-reserve.tsx:190
 #, c-format
 msgid "Deposit Fee"
 msgstr ""
 
-#: src/webex/pages/confirm-create-reserve.tsx:278
+#: src/webex/pages/confirm-create-reserve.tsx:244
 #, c-format
 msgid "Select"
 msgstr ""
 
-#: src/webex/pages/confirm-create-reserve.tsx:294
+#: src/webex/pages/confirm-create-reserve.tsx:260
 #, c-format
 msgid "Error: URL may not be relative"
 msgstr ""
 
-#: src/webex/pages/confirm-create-reserve.tsx:362
+#: src/webex/pages/confirm-create-reserve.tsx:328
 #, c-format
 msgid "The exchange is trusted by the wallet.\n"
 msgstr ""
 
-#: src/webex/pages/confirm-create-reserve.tsx:368
+#: src/webex/pages/confirm-create-reserve.tsx:334
 #, c-format
 msgid "The exchange is audited by a trusted auditor.\n"
 msgstr ""
 
-#: src/webex/pages/confirm-create-reserve.tsx:374
+#: src/webex/pages/confirm-create-reserve.tsx:340
 #, c-format
 msgid ""
 "Warning:  The exchange is neither directly trusted nor audited by a trusted "
@@ -124,7 +134,7 @@ msgid ""
 "If you withdraw from this exchange, it will be trusted in the future.\n"
 msgstr ""
 
-#: src/webex/pages/confirm-create-reserve.tsx:383
+#: src/webex/pages/confirm-create-reserve.tsx:349
 #, c-format
 msgid ""
 "Using exchange provider%1$s.\n"
@@ -132,63 +142,58 @@ msgid ""
 " %2$s in fees.\n"
 msgstr ""
 
-#: src/webex/pages/confirm-create-reserve.tsx:397
+#: src/webex/pages/confirm-create-reserve.tsx:363
 #, c-format
 msgid ""
 "Waiting for a response from\n"
 " %1$s"
 msgstr ""
 
-#: src/webex/pages/confirm-create-reserve.tsx:408
-#, c-format
-msgid "A problem occured, see below. %1$s"
-msgstr ""
-
-#: src/webex/pages/confirm-create-reserve.tsx:414
+#: src/webex/pages/confirm-create-reserve.tsx:380
 #, c-format
 msgid ""
 "Information about fees will be available when an exchange provider is "
 "selected."
 msgstr ""
 
-#: src/webex/pages/confirm-create-reserve.tsx:457
+#: src/webex/pages/confirm-create-reserve.tsx:423
 #, c-format
 msgid "Accept fees and withdraw"
 msgstr ""
 
-#: src/webex/pages/confirm-create-reserve.tsx:462
+#: src/webex/pages/confirm-create-reserve.tsx:428
 #, c-format
 msgid "Change Exchange Provider"
 msgstr ""
 
-#: src/webex/pages/confirm-create-reserve.tsx:519
+#: src/webex/pages/confirm-create-reserve.tsx:485
 #, c-format
 msgid "You are about to withdraw %1$s from your bank account into your wallet."
 msgstr ""
 
-#: src/webex/pages/confirm-create-reserve.tsx:607
+#: src/webex/pages/confirm-create-reserve.tsx:570
 #, c-format
 msgid ""
 "Oops, something went wrong. The wallet responded with error status (%1$s)."
 msgstr ""
 
-#: src/webex/pages/confirm-create-reserve.tsx:616
+#: src/webex/pages/confirm-create-reserve.tsx:579
 #, c-format
 msgid "Checking URL, please wait ..."
 msgstr ""
 
-#: src/webex/pages/confirm-create-reserve.tsx:630
+#: src/webex/pages/confirm-create-reserve.tsx:593
 #, c-format
 msgid "Can't parse amount: %1$s"
 msgstr ""
 
-#: src/webex/pages/confirm-create-reserve.tsx:637
+#: src/webex/pages/confirm-create-reserve.tsx:600
 #, c-format
 msgid "Can't parse wire_types: %1$s"
 msgstr ""
 
 #. TODO:generic error reporting function or component.
-#: src/webex/pages/confirm-create-reserve.tsx:663
+#: src/webex/pages/confirm-create-reserve.tsx:626
 #, c-format
 msgid "Fatal error: \"%1$s\"."
 msgstr ""
@@ -311,16 +316,6 @@ msgstr ""
 msgid "Cancel"
 msgstr ""
 
-#: src/webex/renderHtml.tsx:51
-#, c-format
-msgid "The merchant%1$swants to enter a contract over%2$s with you.\n"
-msgstr ""
-
-#: src/webex/renderHtml.tsx:56
-#, c-format
-msgid "You are about to purchase:"
-msgstr ""
-
 #: src/wire.ts:38
 #, c-format
 msgid "Invalid Wire"
diff --git a/src/i18n/strings.ts b/src/i18n/strings.ts
index 75672cbd..41c8d72a 100644
--- a/src/i18n/strings.ts
+++ b/src/i18n/strings.ts
@@ -39,6 +39,12 @@ strings['de'] = {
       "You do not have any funds from an exchange that is accepted by this 
merchant. None of the exchanges accepted by the merchant is known to your 
wallet.": [
         ""
       ],
+      "The merchant%1$s offers you to purchase:\n": [
+        ""
+      ],
+      "Confirm payment": [
+        "Bezahlung bestätigen"
+      ],
       "Withdrawal fees:": [
         "Abheben bei %1$s"
       ],
@@ -84,9 +90,6 @@ strings['de'] = {
       "Waiting for a response from\n %1$s": [
         ""
       ],
-      "A problem occured, see below. %1$s": [
-        ""
-      ],
       "Information about fees will be available when an exchange provider is 
selected.": [
         ""
       ],
@@ -180,12 +183,6 @@ strings['de'] = {
       "Cancel": [
         "Saldo"
       ],
-      "The merchant%1$swants to enter a contract over%2$s with you.\n": [
-        "%1$s\n               möchte einen Vertrag über %2$s\n               
mit Ihnen abschließen."
-      ],
-      "You are about to purchase:": [
-        "Sie sind dabei, Folgendes zu kaufen:"
-      ],
       "Invalid Wire": [
         ""
       ],
@@ -225,6 +222,12 @@ strings['en-US'] = {
       "You do not have any funds from an exchange that is accepted by this 
merchant. None of the exchanges accepted by the merchant is known to your 
wallet.": [
         ""
       ],
+      "The merchant%1$s offers you to purchase:\n": [
+        ""
+      ],
+      "Confirm payment": [
+        ""
+      ],
       "Withdrawal fees:": [
         ""
       ],
@@ -270,9 +273,6 @@ strings['en-US'] = {
       "Waiting for a response from\n %1$s": [
         ""
       ],
-      "A problem occured, see below. %1$s": [
-        ""
-      ],
       "Information about fees will be available when an exchange provider is 
selected.": [
         ""
       ],
@@ -366,12 +366,6 @@ strings['en-US'] = {
       "Cancel": [
         ""
       ],
-      "The merchant%1$swants to enter a contract over%2$s with you.\n": [
-        ""
-      ],
-      "You are about to purchase:": [
-        ""
-      ],
       "Invalid Wire": [
         ""
       ],
@@ -411,6 +405,12 @@ strings['fr'] = {
       "You do not have any funds from an exchange that is accepted by this 
merchant. None of the exchanges accepted by the merchant is known to your 
wallet.": [
         ""
       ],
+      "The merchant%1$s offers you to purchase:\n": [
+        ""
+      ],
+      "Confirm payment": [
+        ""
+      ],
       "Withdrawal fees:": [
         ""
       ],
@@ -456,9 +456,6 @@ strings['fr'] = {
       "Waiting for a response from\n %1$s": [
         ""
       ],
-      "A problem occured, see below. %1$s": [
-        ""
-      ],
       "Information about fees will be available when an exchange provider is 
selected.": [
         ""
       ],
@@ -552,12 +549,6 @@ strings['fr'] = {
       "Cancel": [
         ""
       ],
-      "The merchant%1$swants to enter a contract over%2$s with you.\n": [
-        ""
-      ],
-      "You are about to purchase:": [
-        ""
-      ],
       "Invalid Wire": [
         ""
       ],
@@ -597,6 +588,12 @@ strings['it'] = {
       "You do not have any funds from an exchange that is accepted by this 
merchant. None of the exchanges accepted by the merchant is known to your 
wallet.": [
         ""
       ],
+      "The merchant%1$s offers you to purchase:\n": [
+        ""
+      ],
+      "Confirm payment": [
+        ""
+      ],
       "Withdrawal fees:": [
         ""
       ],
@@ -642,9 +639,6 @@ strings['it'] = {
       "Waiting for a response from\n %1$s": [
         ""
       ],
-      "A problem occured, see below. %1$s": [
-        ""
-      ],
       "Information about fees will be available when an exchange provider is 
selected.": [
         ""
       ],
@@ -738,12 +732,6 @@ strings['it'] = {
       "Cancel": [
         ""
       ],
-      "The merchant%1$swants to enter a contract over%2$s with you.\n": [
-        ""
-      ],
-      "You are about to purchase:": [
-        ""
-      ],
       "Invalid Wire": [
         ""
       ],
diff --git a/src/i18n/taler-wallet-webex.pot b/src/i18n/taler-wallet-webex.pot
index dc271a74..d804bd20 100644
--- a/src/i18n/taler-wallet-webex.pot
+++ b/src/i18n/taler-wallet-webex.pot
@@ -27,28 +27,28 @@ msgstr ""
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=2; plural=(n != 1);\n"
 
-#: src/webex/pages/confirm-contract.tsx:69
+#: src/webex/pages/confirm-contract.tsx:70
 #, c-format
 msgid "show more details\n"
 msgstr ""
 
-#: src/webex/pages/confirm-contract.tsx:83
+#: src/webex/pages/confirm-contract.tsx:84
 #, c-format
 msgid "Accepted exchanges:"
 msgstr ""
 
-#: src/webex/pages/confirm-contract.tsx:88
+#: src/webex/pages/confirm-contract.tsx:89
 #, c-format
 msgid "Exchanges in the wallet:"
 msgstr ""
 
-#: src/webex/pages/confirm-contract.tsx:154
+#: src/webex/pages/confirm-contract.tsx:156
 #, c-format
 msgid "You have insufficient funds of the requested currency in your wallet."
 msgstr ""
 
 #. tslint:disable-next-line:max-line-length
-#: src/webex/pages/confirm-contract.tsx:156
+#: src/webex/pages/confirm-contract.tsx:158
 #, c-format
 msgid ""
 "You do not have any funds from an exchange that is accepted by this "
@@ -56,67 +56,77 @@ msgid ""
 "wallet."
 msgstr ""
 
-#: src/webex/pages/confirm-create-reserve.tsx:213
+#: src/webex/pages/confirm-contract.tsx:214
+#, c-format
+msgid "The merchant%1$s offers you to purchase:\n"
+msgstr ""
+
+#: src/webex/pages/confirm-contract.tsx:232
+#, c-format
+msgid "Confirm payment"
+msgstr ""
+
+#: src/webex/pages/confirm-create-reserve.tsx:179
 #, c-format
 msgid "Withdrawal fees:"
 msgstr ""
 
-#: src/webex/pages/confirm-create-reserve.tsx:214
+#: src/webex/pages/confirm-create-reserve.tsx:180
 #, c-format
 msgid "Rounding loss:"
 msgstr ""
 
-#: src/webex/pages/confirm-create-reserve.tsx:215
+#: src/webex/pages/confirm-create-reserve.tsx:181
 #, c-format
 msgid "Earliest expiration (for deposit): %1$s"
 msgstr ""
 
-#: src/webex/pages/confirm-create-reserve.tsx:220
+#: src/webex/pages/confirm-create-reserve.tsx:186
 #, c-format
 msgid "# Coins"
 msgstr ""
 
-#: src/webex/pages/confirm-create-reserve.tsx:221
+#: src/webex/pages/confirm-create-reserve.tsx:187
 #, c-format
 msgid "Value"
 msgstr ""
 
-#: src/webex/pages/confirm-create-reserve.tsx:222
+#: src/webex/pages/confirm-create-reserve.tsx:188
 #, c-format
 msgid "Withdraw Fee"
 msgstr ""
 
-#: src/webex/pages/confirm-create-reserve.tsx:223
+#: src/webex/pages/confirm-create-reserve.tsx:189
 #, c-format
 msgid "Refresh Fee"
 msgstr ""
 
-#: src/webex/pages/confirm-create-reserve.tsx:224
+#: src/webex/pages/confirm-create-reserve.tsx:190
 #, c-format
 msgid "Deposit Fee"
 msgstr ""
 
-#: src/webex/pages/confirm-create-reserve.tsx:278
+#: src/webex/pages/confirm-create-reserve.tsx:244
 #, c-format
 msgid "Select"
 msgstr ""
 
-#: src/webex/pages/confirm-create-reserve.tsx:294
+#: src/webex/pages/confirm-create-reserve.tsx:260
 #, c-format
 msgid "Error: URL may not be relative"
 msgstr ""
 
-#: src/webex/pages/confirm-create-reserve.tsx:362
+#: src/webex/pages/confirm-create-reserve.tsx:328
 #, c-format
 msgid "The exchange is trusted by the wallet.\n"
 msgstr ""
 
-#: src/webex/pages/confirm-create-reserve.tsx:368
+#: src/webex/pages/confirm-create-reserve.tsx:334
 #, c-format
 msgid "The exchange is audited by a trusted auditor.\n"
 msgstr ""
 
-#: src/webex/pages/confirm-create-reserve.tsx:374
+#: src/webex/pages/confirm-create-reserve.tsx:340
 #, c-format
 msgid ""
 "Warning:  The exchange is neither directly trusted nor audited by a trusted "
@@ -124,7 +134,7 @@ msgid ""
 "If you withdraw from this exchange, it will be trusted in the future.\n"
 msgstr ""
 
-#: src/webex/pages/confirm-create-reserve.tsx:383
+#: src/webex/pages/confirm-create-reserve.tsx:349
 #, c-format
 msgid ""
 "Using exchange provider%1$s.\n"
@@ -132,63 +142,58 @@ msgid ""
 " %2$s in fees.\n"
 msgstr ""
 
-#: src/webex/pages/confirm-create-reserve.tsx:397
+#: src/webex/pages/confirm-create-reserve.tsx:363
 #, c-format
 msgid ""
 "Waiting for a response from\n"
 " %1$s"
 msgstr ""
 
-#: src/webex/pages/confirm-create-reserve.tsx:408
-#, c-format
-msgid "A problem occured, see below. %1$s"
-msgstr ""
-
-#: src/webex/pages/confirm-create-reserve.tsx:414
+#: src/webex/pages/confirm-create-reserve.tsx:380
 #, c-format
 msgid ""
 "Information about fees will be available when an exchange provider is "
 "selected."
 msgstr ""
 
-#: src/webex/pages/confirm-create-reserve.tsx:457
+#: src/webex/pages/confirm-create-reserve.tsx:423
 #, c-format
 msgid "Accept fees and withdraw"
 msgstr ""
 
-#: src/webex/pages/confirm-create-reserve.tsx:462
+#: src/webex/pages/confirm-create-reserve.tsx:428
 #, c-format
 msgid "Change Exchange Provider"
 msgstr ""
 
-#: src/webex/pages/confirm-create-reserve.tsx:519
+#: src/webex/pages/confirm-create-reserve.tsx:485
 #, c-format
 msgid "You are about to withdraw %1$s from your bank account into your wallet."
 msgstr ""
 
-#: src/webex/pages/confirm-create-reserve.tsx:607
+#: src/webex/pages/confirm-create-reserve.tsx:570
 #, c-format
 msgid ""
 "Oops, something went wrong. The wallet responded with error status (%1$s)."
 msgstr ""
 
-#: src/webex/pages/confirm-create-reserve.tsx:616
+#: src/webex/pages/confirm-create-reserve.tsx:579
 #, c-format
 msgid "Checking URL, please wait ..."
 msgstr ""
 
-#: src/webex/pages/confirm-create-reserve.tsx:630
+#: src/webex/pages/confirm-create-reserve.tsx:593
 #, c-format
 msgid "Can't parse amount: %1$s"
 msgstr ""
 
-#: src/webex/pages/confirm-create-reserve.tsx:637
+#: src/webex/pages/confirm-create-reserve.tsx:600
 #, c-format
 msgid "Can't parse wire_types: %1$s"
 msgstr ""
 
 #. TODO:generic error reporting function or component.
-#: src/webex/pages/confirm-create-reserve.tsx:663
+#: src/webex/pages/confirm-create-reserve.tsx:626
 #, c-format
 msgid "Fatal error: \"%1$s\"."
 msgstr ""
@@ -311,16 +316,6 @@ msgstr ""
 msgid "Cancel"
 msgstr ""
 
-#: src/webex/renderHtml.tsx:51
-#, c-format
-msgid "The merchant%1$swants to enter a contract over%2$s with you.\n"
-msgstr ""
-
-#: src/webex/renderHtml.tsx:56
-#, c-format
-msgid "You are about to purchase:"
-msgstr ""
-
 #: src/wire.ts:38
 #, c-format
 msgid "Invalid Wire"
diff --git a/src/types.ts b/src/types.ts
index d016b7fe..90fe7cf9 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -822,6 +822,10 @@ export enum CoinStatus {
    * Coin fully paid back.
    */
   PaybackDone,
+  /**
+   * Coin was dirty but can't be refreshed.
+   */
+  Useless,
 }
 
 
@@ -1467,7 +1471,10 @@ export function mkAmount(value: number, fraction: 
number, currency: string): Amo
 /**
  * Possible results for checkPay.
  */
-export type CheckPayResult = "paid" | "payment-possible" | 
"insufficient-balance";
+export interface CheckPayResult {
+  status: "paid" | "payment-possible" | "insufficient-balance";
+  coinSelection?: CoinSelectionResult;
+}
 
 /**
  * Possible results for confirmPay.
@@ -1695,3 +1702,30 @@ export interface PurchaseRecord {
   refundsPending: { [refundSig: string]: RefundPermission };
   refundsDone: { [refundSig: string]: RefundPermission };
 }
+
+
+/**
+ * Result of selecting coins, contains the exchange, and selected
+ * coins with their denomination.
+ */
+export interface CoinSelectionResult {
+  exchangeUrl: string;
+  cds: CoinWithDenom[];
+  totalFees: AmountJson;
+}
+
+
+/**
+ * Named tuple of coin and denomination.
+ */
+export interface CoinWithDenom {
+  /**
+   * A coin.  Must have the same denomination public key as the associated
+   * denomination.
+   */
+  coin: CoinRecord;
+  /**
+   * An associated denomination.
+   */
+  denom: DenominationRecord;
+}
diff --git a/src/wallet-test.ts b/src/wallet-test.ts
index acd776d6..037cc759 100644
--- a/src/wallet-test.ts
+++ b/src/wallet-test.ts
@@ -29,7 +29,7 @@ function a(x: string): types.AmountJson {
 }
 
 
-function fakeCwd(current: string, value: string, feeDeposit: string): 
wallet.CoinWithDenom {
+function fakeCwd(current: string, value: string, feeDeposit: string): 
types.CoinWithDenom {
   return {
     coin: {
       blindingKey: "(mock)",
@@ -64,89 +64,89 @@ function fakeCwd(current: string, value: string, 
feeDeposit: string): wallet.Coi
 
 
 test("coin selection 1", (t) => {
-  const cds: wallet.CoinWithDenom[] = [
+  const cds: types.CoinWithDenom[] = [
     fakeCwd("EUR:1.0", "EUR:1.0", "EUR:0.1"),
     fakeCwd("EUR:1.0", "EUR:1.0", "EUR:0.0"),
   ];
 
-  const res = wallet.selectPayCoins(cds, a("EUR:2.0"), a("EUR:0.1"));
+  const res = wallet.selectPayCoins([], cds, a("EUR:2.0"), a("EUR:0.1"));
   if (!res) {
     t.fail();
     return;
   }
-  t.true(res.length === 2);
+  t.true(res.cds.length === 2);
   t.pass();
 });
 
 
 test("coin selection 2", (t) => {
-  const cds: wallet.CoinWithDenom[] = [
+  const cds: types.CoinWithDenom[] = [
     fakeCwd("EUR:1.0", "EUR:1.0", "EUR:0.5"),
     fakeCwd("EUR:1.0", "EUR:1.0", "EUR:0.0"),
     // Merchant covers the fee, this one shouldn't be used
     fakeCwd("EUR:1.0", "EUR:1.0", "EUR:0.0"),
   ];
-  const res = wallet.selectPayCoins(cds, a("EUR:2.0"), a("EUR:0.5"));
+  const res = wallet.selectPayCoins([], cds, a("EUR:2.0"), a("EUR:0.5"));
   if (!res) {
     t.fail();
     return;
   }
-  t.true(res.length === 2);
+  t.true(res.cds.length === 2);
   t.pass();
 });
 
 
 test("coin selection 3", (t) => {
-  const cds: wallet.CoinWithDenom[] = [
+  const cds: types.CoinWithDenom[] = [
     fakeCwd("EUR:1.0", "EUR:1.0", "EUR:0.5"),
     fakeCwd("EUR:1.0", "EUR:1.0", "EUR:0.5"),
     // this coin should be selected instead of previous one with fee
     fakeCwd("EUR:1.0", "EUR:1.0", "EUR:0.0"),
   ];
-  const res = wallet.selectPayCoins(cds, a("EUR:2.0"), a("EUR:0.5"));
+  const res = wallet.selectPayCoins([], cds, a("EUR:2.0"), a("EUR:0.5"));
   if (!res) {
     t.fail();
     return;
   }
-  t.true(res.length === 2);
+  t.true(res.cds.length === 2);
   t.pass();
 });
 
 
 test("coin selection 4", (t) => {
-  const cds: wallet.CoinWithDenom[] = [
+  const cds: types.CoinWithDenom[] = [
     fakeCwd("EUR:1.0", "EUR:1.0", "EUR:0.5"),
     fakeCwd("EUR:1.0", "EUR:1.0", "EUR:0.5"),
     fakeCwd("EUR:1.0", "EUR:1.0", "EUR:0.5"),
   ];
-  const res = wallet.selectPayCoins(cds, a("EUR:2.0"), a("EUR:0.2"));
+  const res = wallet.selectPayCoins([], cds, a("EUR:2.0"), a("EUR:0.2"));
   if (!res) {
     t.fail();
     return;
   }
-  t.true(res.length === 3);
+  t.true(res.cds.length === 3);
   t.pass();
 });
 
 
 test("coin selection 5", (t) => {
-  const cds: wallet.CoinWithDenom[] = [
+  const cds: types.CoinWithDenom[] = [
     fakeCwd("EUR:1.0", "EUR:1.0", "EUR:0.5"),
     fakeCwd("EUR:1.0", "EUR:1.0", "EUR:0.5"),
     fakeCwd("EUR:1.0", "EUR:1.0", "EUR:0.5"),
   ];
-  const res = wallet.selectPayCoins(cds, a("EUR:4.0"), a("EUR:0.2"));
+  const res = wallet.selectPayCoins([], cds, a("EUR:4.0"), a("EUR:0.2"));
   t.true(!res);
   t.pass();
 });
 
 
 test("coin selection 6", (t) => {
-  const cds: wallet.CoinWithDenom[] = [
+  const cds: types.CoinWithDenom[] = [
     fakeCwd("EUR:1.0", "EUR:1.0", "EUR:0.5"),
     fakeCwd("EUR:1.0", "EUR:1.0", "EUR:0.5"),
   ];
-  const res = wallet.selectPayCoins(cds, a("EUR:2.0"), a("EUR:0.2"));
+  const res = wallet.selectPayCoins([], cds, a("EUR:2.0"), a("EUR:0.2"));
   t.true(!res);
   t.pass();
 });
diff --git a/src/wallet.ts b/src/wallet.ts
index 3d095fc0..f194755e 100644
--- a/src/wallet.ts
+++ b/src/wallet.ts
@@ -51,7 +51,9 @@ import {
   CheckPayResult,
   CoinPaySig,
   CoinRecord,
+  CoinSelectionResult,
   CoinStatus,
+  CoinWithDenom,
   ConfirmPayResult,
   ConfirmReserveRequest,
   ContractTerms,
@@ -72,8 +74,10 @@ import {
   PaybackConfirmation,
   PreCoinRecord,
   ProposalRecord,
+  PurchaseRecord,
   QueryPaymentResult,
   RefreshSessionRecord,
+  RefundPermission,
   ReserveCreationInfo,
   ReserveRecord,
   ReturnCoinsRequest,
@@ -82,27 +86,10 @@ import {
   WalletBalanceEntry,
   WireFee,
   WireInfo,
-  RefundPermission,
-  PurchaseRecord,
 } from "./types";
 import URI = require("urijs");
 
 
-/**
- * Named tuple of coin and denomination.
- */
-export interface CoinWithDenom {
-  /**
-   * A coin.  Must have the same denomination public key as the associated
-   * denomination.
-   */
-  coin: CoinRecord;
-  /**
-   * An associated denomination.
-   */
-  denom: DenominationRecord;
-}
-
 
 /**
  * Element of the payback list that the
@@ -370,30 +357,62 @@ function isWithdrawableDenom(d: DenominationRecord) {
 }
 
 
+
+function strcmp(s1: string, s2: string): number {
+  if (s1 < s2) {
+    return -1;
+  }
+  if (s1 > s2) {
+    return 1;
+  }
+  return 0;
+}
+
+
+interface SelectPayCoinsResult {
+  cds: CoinWithDenom[];
+  totalFees: AmountJson;
+}
+
+
 /**
- * Result of selecting coins, contains the exchange, and selected
- * coins with their denomination.
+ * Get the amount that we lose when refreshing a coin of the given denomination
+ * with a certain amount left.
+ *
+ * If the amount left is zero, then the refresh cost
+ * is also considered to be zero.  If a refresh isn't possible (e.g. due to 
lack of
+ * the right denominations), then the cost is the full amount left.
+ *
+ * Considers refresh fees, withdrawal fees after refresh and amounts too small
+ * to refresh.
  */
-export type CoinSelectionResult = {exchangeUrl: string, cds: 
CoinWithDenom[]}|undefined;
+export function getTotalRefreshCost(denoms: DenominationRecord[], 
refreshedDenom: DenominationRecord, amountLeft: AmountJson): AmountJson {
+  const withdrawAmount = Amounts.sub(amountLeft, 
refreshedDenom.feeRefresh).amount;
+  const withdrawDenoms = getWithdrawDenomList(withdrawAmount, denoms);
+  const resultingAmount = 
Amounts.add(Amounts.getZero(withdrawAmount.currency), ...withdrawDenoms.map((d) 
=> d.value)).amount;
+  const totalCost = Amounts.sub(amountLeft, resultingAmount).amount;
+  console.log("total refresh cost for", amountToPretty(amountLeft), "is", 
amountToPretty(totalCost));
+  return totalCost;
+}
+
 
 /**
  * Select coins for a payment under the merchant's constraints.
+ *
+ * @param denoms all available denoms, used to compute refresh fees
  */
-export function selectPayCoins(cds: CoinWithDenom[], paymentAmount: AmountJson,
-                               depositFeeLimit: AmountJson): 
CoinWithDenom[]|undefined {
+export function selectPayCoins(denoms: DenominationRecord[], cds: 
CoinWithDenom[], paymentAmount: AmountJson,
+                               depositFeeLimit: AmountJson): 
SelectPayCoinsResult|undefined {
   if (cds.length === 0) {
     return undefined;
   }
-  // Sort by ascending deposit fee
-  cds.sort((o1, o2) => Amounts.cmp(o1.denom.feeDeposit,
-                                   o2.denom.feeDeposit));
+  // Sort by ascending deposit fee and denomPub if deposit fee is the same
+  // (to guarantee deterministic results)
+  cds.sort((o1, o2) => Amounts.cmp(o1.denom.feeDeposit, o2.denom.feeDeposit) 
|| strcmp(o1.denom.denomPub, o2.denom.denomPub));
   const currency = cds[0].denom.value.currency;
   const cdsResult: CoinWithDenom[] = [];
-  let accFee: AmountJson = Amounts.getZero(currency);
+  let accDepositFee: AmountJson = Amounts.getZero(currency);
   let accAmount: AmountJson = Amounts.getZero(currency);
-  let isBelowFee = false;
-  let coversAmount = false;
-  let coversAmountWithFee = false;
   for (const {coin, denom} of cds) {
     if (coin.suspended) {
       continue;
@@ -405,18 +424,30 @@ export function selectPayCoins(cds: CoinWithDenom[], 
paymentAmount: AmountJson,
       continue;
     }
     cdsResult.push({coin, denom});
-    accFee = Amounts.add(denom.feeDeposit, accFee).amount;
+    accDepositFee = Amounts.add(denom.feeDeposit, accDepositFee).amount;
+    let leftAmount = Amounts.sub(coin.currentAmount, 
Amounts.sub(paymentAmount, accAmount).amount).amount;
     accAmount = Amounts.add(coin.currentAmount, accAmount).amount;
-    coversAmount = Amounts.cmp(accAmount, paymentAmount) >= 0;
-    coversAmountWithFee = Amounts.cmp(accAmount,
+    const coversAmount = Amounts.cmp(accAmount, paymentAmount) >= 0;
+    const coversAmountWithFee = Amounts.cmp(accAmount,
                                       Amounts.add(paymentAmount,
                                                   denom.feeDeposit).amount) >= 
0;
-    isBelowFee = Amounts.cmp(accFee, depositFeeLimit) <= 0;
+    const isBelowFee = Amounts.cmp(accDepositFee, depositFeeLimit) <= 0;
 
-    console.log("coin selection", { coversAmount, isBelowFee, accFee, 
accAmount, paymentAmount });
+    console.log("coin selection", { coversAmount, isBelowFee, accDepositFee, 
accAmount, paymentAmount });
 
     if ((coversAmount && isBelowFee) || coversAmountWithFee) {
-      return cdsResult;
+      let depositFeeToCover = Amounts.sub(accDepositFee, 
depositFeeLimit).amount;
+      leftAmount = Amounts.sub(leftAmount, depositFeeToCover).amount;
+      console.log("deposit fee to cover", amountToPretty(depositFeeToCover));
+
+      let totalFees: AmountJson = Amounts.getZero(currency);
+      if (coversAmountWithFee && !isBelowFee) {
+        // these are the fees the customer has to pay
+        // because the merchant doesn't cover them
+        totalFees = Amounts.sub(depositFeeLimit, accDepositFee).amount;
+      }
+      totalFees = Amounts.add(totalFees, getTotalRefreshCost(denoms, denom, 
leftAmount)).amount;
+      return { cds: cdsResult, totalFees };
     }
   }
   return undefined;
@@ -729,6 +760,8 @@ export class Wallet {
       return [];
     }
 
+    const denoms = await 
this.q().iterIndex(Stores.denominations.exchangeBaseUrlIndex, 
exchange.baseUrl).toArray();
+
     // Denomination of the first coin, we assume that all other
     // coins have the same currency
     const firstDenom = await this.q().get(Stores.denominations,
@@ -763,7 +796,11 @@ export class Wallet {
 
     console.log("coin return:  selecting from possible coins", { cds, amount } 
);
 
-    return selectPayCoins(cds, amount, amount);
+    const res = selectPayCoins(denoms, cds, amount, amount);
+    if (res) {
+      return res.cds;
+    }
+    return undefined
   }
 
 
@@ -771,7 +808,7 @@ export class Wallet {
    * Get exchanges and associated coins that are still spendable,
    * but only if the sum the coins' remaining value exceeds the payment amount.
    */
-  private async getCoinsForPayment(args: CoinsForPaymentArgs): 
Promise<CoinSelectionResult> {
+  private async getCoinsForPayment(args: CoinsForPaymentArgs): 
Promise<CoinSelectionResult|undefined> {
     const {
       allowedAuditors,
       allowedExchanges,
@@ -821,6 +858,7 @@ export class Wallet {
                                           
.iterIndex(Stores.coins.exchangeBaseUrlIndex,
                                                      exchange.baseUrl)
                                           .toArray();
+      const denoms = await 
this.q().iterIndex(Stores.denominations.exchangeBaseUrlIndex, 
exchange.baseUrl).toArray();
       if (!coins || coins.length === 0) {
         continue;
       }
@@ -862,6 +900,7 @@ export class Wallet {
         continue;
       }
 
+      let totalFees = Amounts.getZero(currency);
       let wireFee: AmountJson|undefined;
       for (const fee of (fees.feesForType[wireMethod] || [])) {
         if (fee.startStamp >= wireFeeTime && fee.endStamp <= wireFeeTime) {
@@ -873,15 +912,18 @@ export class Wallet {
       if (wireFee) {
         const amortizedWireFee = Amounts.divide(wireFee, wireFeeAmortization);
         if (Amounts.cmp(wireFeeLimit, amortizedWireFee) < 0) {
+          totalFees = Amounts.add(amortizedWireFee, totalFees).amount;
           remainingAmount = Amounts.add(amortizedWireFee, 
remainingAmount).amount;
         }
       }
 
-      const res = selectPayCoins(cds, remainingAmount, depositFeeLimit);
+      const res = selectPayCoins(denoms, cds, remainingAmount, 
depositFeeLimit);
       if (res) {
+        totalFees = Amounts.add(totalFees, res.totalFees).amount;
         return {
-          cds: res,
+          cds: res.cds,
           exchangeUrl: exchange.baseUrl,
+          totalFees,
         };
       }
     }
@@ -1014,7 +1056,7 @@ export class Wallet {
     // First check if we already payed for it.
     const purchase = await this.q().get(Stores.purchases, 
proposal.contractTermsHash);
     if (purchase) {
-      return "paid";
+      return { status: "paid" };
     }
 
     // If not already payed, check if we could pay for it.
@@ -1031,9 +1073,9 @@ export class Wallet {
 
     if (!res) {
       console.log("not confirming payment, insufficient coins");
-      return "insufficient-balance";
+      return { status: "insufficient-balance" };
     }
-    return "payment-possible";
+    return { status: "payment-possible", coinSelection: res };
   }
 
 
@@ -1653,6 +1695,7 @@ export class Wallet {
       console.log("suspending coin", c);
       c.suspended = true;
       q.put(Stores.coins, c);
+      this.notifier.notify();
     });
     await q.finish();
   }
@@ -1840,11 +1883,14 @@ export class Wallet {
       if (c.suspended) {
         return balance;
       }
-      if (!(c.status === CoinStatus.Fresh)) {
+      if (c.status === CoinStatus.Fresh) {
+        addTo(balance, "available", c.currentAmount, c.exchangeBaseUrl);
+        return balance;
+      }
+      if (c.status === CoinStatus.Dirty) {
+        addTo(balance, "pendingIncoming", c.currentAmount, c.exchangeBaseUrl);
         return balance;
       }
-      console.log("collecting balance");
-      addTo(balance, "available", c.currentAmount, c.exchangeBaseUrl);
       return balance;
     }
 
@@ -1978,6 +2024,9 @@ export class Wallet {
 
     if (newCoinDenoms.length === 0) {
       console.log(`not refreshing, available amount 
${amountToPretty(availableAmount)} too small`);
+      coin.status = CoinStatus.Useless;
+      await this.q().put(Stores.coins, coin);
+      this.notifier.notify();
       return undefined;
     }
 
@@ -2007,6 +2056,7 @@ export class Wallet {
     query.put(Stores.refresh, refreshSession, "refreshKey")
          .mutate(Stores.coins, coin.coinPub, mutateCoin);
     await query.finish();
+    this.notifier.notify();
 
     const key = query.key("refreshKey");
     if (!key || typeof key !== "number") {
@@ -2026,7 +2076,15 @@ export class Wallet {
       console.log("got old session for", oldCoinPub, session);
       this.continueRefreshSession(session);
     }
-    let refreshSession = await this.createRefreshSession(oldCoinPub);
+    const coin = await this.q().get(Stores.coins, oldCoinPub);
+    if (!coin) {
+      console.warn("can't refresh, coin not in database");
+      return;
+    }
+    if (coin.status === CoinStatus.Useless || coin.status === 
CoinStatus.Fresh) {
+      return;
+    }
+    const refreshSession = await this.createRefreshSession(oldCoinPub);
     if (!refreshSession) {
       // refreshing not necessary
       console.log("not refreshing", oldCoinPub);
@@ -2106,6 +2164,7 @@ export class Wallet {
     refreshSession.norevealIndex = norevealIndex;
 
     await this.q().put(Stores.refresh, refreshSession).finish();
+    this.notifier.notify();
   }
 
 
@@ -2186,6 +2245,7 @@ export class Wallet {
               .putAll(Stores.coins, coins)
               .put(Stores.refresh, refreshSession)
               .finish();
+    this.notifier.notify();
   }
 
 
@@ -2344,6 +2404,7 @@ export class Wallet {
     // from the reserve for the payback request.
     reserve.hasPayback = true;
     await this.q().put(Stores.coins, coin).put(Stores.reserves, reserve);
+    this.notifier.notify();
 
     const paybackRequest = await this.cryptoApi.createPaybackRequest(coin);
     const reqUrl = new URI("payback").absoluteTo(coin.exchangeBaseUrl);
@@ -2361,6 +2422,7 @@ export class Wallet {
     }
     coin.status = CoinStatus.PaybackDone;
     await this.q().put(Stores.coins, coin);
+    this.notifier.notify();
     await this.updateReserve(reservePub!);
   }
 
@@ -2502,6 +2564,7 @@ export class Wallet {
               .put(Stores.coinsReturns, coinsReturnRecord)
               .putAll(Stores.coins, payCoinInfo.map((pci) => pci.updatedCoin))
               .finish();
+    this.notifier.notify();
 
     this.depositReturnedCoins(coinsReturnRecord);
   }
@@ -2558,6 +2621,7 @@ export class Wallet {
         }
       }
       await this.q().put(Stores.coinsReturns, currentCrr);
+      this.notifier.notify();
     }
   }
 
@@ -2666,4 +2730,34 @@ export class Wallet {
   async getPurchase(contractTermsHash: string): 
Promise<PurchaseRecord|undefined> {
     return this.q().get(Stores.purchases, contractTermsHash);
   }
+
+  async getFullRefundFees(refundPermissions: RefundPermission[]): 
Promise<AmountJson> {
+    if (refundPermissions.length === 0) {
+      throw Error("no refunds given");
+    }
+    const coin0 = await this.q().get(Stores.coins, 
refundPermissions[0].coin_pub)
+    if (!coin0) {
+      throw Error("coin not found");
+    }
+    let feeAcc = Amounts.getZero(refundPermissions[0].refund_amount.currency);
+
+    const denoms = await 
this.q().iterIndex(Stores.denominations.exchangeBaseUrlIndex, 
coin0.exchangeBaseUrl).toArray();
+    for (const rp of refundPermissions) {
+      const coin = await this.q().get(Stores.coins, rp.coin_pub);
+      if (!coin) {
+        throw Error("coin not found");
+      }
+      const denom = await this.q().get(Stores.denominations, 
[coin0.exchangeBaseUrl, coin.denomPub]);
+      if (!denom) {
+        throw Error(`denom not found (${coin.denomPub})`);
+      }
+      // FIXME:  this assumes that the refund already happened.
+      // When it hasn't, the refresh cost is inaccurate.  To fix this,
+      // we need introduce a flag to tell if a coin was refunded or
+      // refreshed normally (and what about incremental refunds?)
+      const refreshCost = getTotalRefreshCost(denoms, denom, 
Amounts.sub(rp.refund_amount, rp.refund_fee).amount);
+      feeAcc = Amounts.add(feeAcc, refreshCost, rp.refund_fee).amount;
+    }
+    return feeAcc;
+  }
 }
diff --git a/src/webex/messages.ts b/src/webex/messages.ts
index 7de28b9e..122bd8fe 100644
--- a/src/webex/messages.ts
+++ b/src/webex/messages.ts
@@ -191,7 +191,11 @@ export interface MessageMap {
   "get-purchase": {
     request: any;
     response: void;
-  }
+  };
+  "get-full-refund-fees": {
+    request: { refundPermissions: types.RefundPermission[] };
+    response: void;
+  };
 }
 
 /**
diff --git a/src/webex/pages/confirm-contract.html 
b/src/webex/pages/confirm-contract.html
index 394de582..223d413d 100644
--- a/src/webex/pages/confirm-contract.html
+++ b/src/webex/pages/confirm-contract.html
@@ -5,6 +5,7 @@
   <meta charset="UTF-8">
   <title>Taler Wallet: Confirm Reserve Creation</title>
 
+  <link rel="stylesheet" type="text/css" href="../style/pure.css">
   <link rel="stylesheet" type="text/css" href="../style/wallet.css">
 
   <link rel="icon" href="/img/icon.png">
diff --git a/src/webex/pages/confirm-contract.tsx 
b/src/webex/pages/confirm-contract.tsx
index fa71b102..5436cb5a 100644
--- a/src/webex/pages/confirm-contract.tsx
+++ b/src/webex/pages/confirm-contract.tsx
@@ -25,12 +25,13 @@
  */
 import * as i18n from "../../i18n";
 import {
+  CheckPayResult,
   ContractTerms,
   ExchangeRecord,
   ProposalRecord,
 } from "../../types";
 
-import { renderContractTerms } from "../renderHtml";
+import { renderAmount } from "../renderHtml";
 import * as wxApi from "../wxApi";
 
 import * as React from "react";
@@ -113,6 +114,7 @@ interface ContractPromptState {
    * when pressing pay.
    */
   holdCheck: boolean;
+  payStatus?: CheckPayResult;
 }
 
 class ContractPrompt extends React.Component<ContractPromptProps, 
ContractPromptState> {
@@ -150,7 +152,7 @@ class ContractPrompt extends 
React.Component<ContractPromptProps, ContractPrompt
       return;
     }
     const payStatus = await wxApi.checkPay(this.props.proposalId);
-    if (payStatus === "insufficient-balance") {
+    if (payStatus.status === "insufficient-balance") {
       const msgInsufficient = i18n.str`You have insufficient funds of the 
requested currency in your wallet.`;
       // tslint:disable-next-line:max-line-length
       const msgNoMatch = i18n.str`You do not have any funds from an exchange 
that is accepted by this merchant. None of the exchanges accepted by the 
merchant is known to your wallet.`;
@@ -166,10 +168,10 @@ class ContractPrompt extends 
React.Component<ContractPromptProps, ContractPrompt
         this.setState({error: msgInsufficient});
       }
       this.setState({payDisabled: true});
-    } else if (payStatus === "paid") {
-      this.setState({alreadyPaid: true, payDisabled: false, error: null});
+    } else if (payStatus.status === "paid") {
+      this.setState({alreadyPaid: true, payDisabled: false, error: null, 
payStatus});
     } else {
-      this.setState({payDisabled: false, error: null});
+      this.setState({payDisabled: false, error: null, payStatus});
     }
   }
 
@@ -189,7 +191,7 @@ class ContractPrompt extends 
React.Component<ContractPromptProps, ContractPrompt
         document.location.href = proposal.contractTerms.fulfillment_url;
         break;
     }
-    this.setState({holdCheck: false});
+    this.setState({holdCheck: true});
   }
 
 
@@ -198,15 +200,36 @@ class ContractPrompt extends 
React.Component<ContractPromptProps, ContractPrompt
       return <span>...</span>;
     }
     const c = this.state.proposal.contractTerms;
+    let merchantName;
+    if (c.merchant && c.merchant.name) {
+      merchantName = <strong>{c.merchant.name}</strong>;
+    } else {
+      merchantName = <strong>(pub: {c.merchant_pub})</strong>;
+    }
+    const amount = <strong>{renderAmount(c.amount)}</strong>;
+    console.log("payStatus", this.state.payStatus);
     return (
       <div>
         <div>
-          {renderContractTerms(c)}
+          <i18n.Translate wrap="p">
+            The merchant <span>{merchantName}</span> {" "}
+            offers you to purchase:
+          </i18n.Translate>
+          <ul>
+            {c.products.map(
+              (p: any, i: number) => (<li key={i}>{p.description}: 
{renderAmount(p.price)}</li>))
+            }
+          </ul>
+            {(this.state.payStatus && this.state.payStatus.coinSelection) ?
+              <p>The total price is <span>{amount}</span> (plus 
<span>{renderAmount(this.state.payStatus.coinSelection.totalFees)}</span> 
fees).</p>
+              :
+              <p>The total price is <span>{amount}</span>.</p>
+            }
         </div>
-        <button onClick={() => this.doPayment()}
+        <button className="pure-button button-success"
                 disabled={this.state.payDisabled}
-                className="accept">
-          Confirm payment
+                onClick={() => this.doPayment()}>
+          {i18n.str`Confirm payment`}
         </button>
         <div>
           {(this.state.alreadyPaid ? <p className="okaybox">You already paid 
for this, clicking "Confirm payment" will not cost money again.</p> : <p />)}
diff --git a/src/webex/pages/refund.tsx b/src/webex/pages/refund.tsx
index b9506bf2..d2c21c2f 100644
--- a/src/webex/pages/refund.tsx
+++ b/src/webex/pages/refund.tsx
@@ -37,11 +37,12 @@ interface RefundStatusViewProps {
 
 interface RefundStatusViewState {
   purchase?: types.PurchaseRecord;
+  refundFees?: types.AmountJson;
   gotResult: boolean;
 }
 
 
-const RefundDetail = ({purchase}: {purchase: types.PurchaseRecord}) => {
+const RefundDetail = ({purchase, fullRefundFees}: {purchase: 
types.PurchaseRecord, fullRefundFees: types.AmountJson}) => {
   const pendingKeys = Object.keys(purchase.refundsPending);
   const doneKeys = Object.keys(purchase.refundsDone);
   if (pendingKeys.length == 0 && doneKeys.length == 0) {
@@ -54,22 +55,18 @@ const RefundDetail = ({purchase}: {purchase: 
types.PurchaseRecord}) => {
   }
 
   let amountPending = types.Amounts.getZero(currency);
-  let feesPending = types.Amounts.getZero(currency)
   for (let k of pendingKeys) {
     amountPending = types.Amounts.add(amountPending, 
purchase.refundsPending[k].refund_amount).amount;
-    feesPending = types.Amounts.add(feesPending, 
purchase.refundsPending[k].refund_fee).amount;
   }
   let amountDone = types.Amounts.getZero(currency);
-  let feesDone = types.Amounts.getZero(currency);
   for (let k of doneKeys) {
     amountDone = types.Amounts.add(amountDone, 
purchase.refundsDone[k].refund_amount).amount;
-    feesDone = types.Amounts.add(feesDone, 
purchase.refundsDone[k].refund_fee).amount;
   }
 
   return (
     <div>
-      <p>Refund fully received: <AmountDisplay amount={amountDone} /> (refund 
fees: <AmountDisplay amount={feesDone} />)</p>
-      <p>Refund incoming: <AmountDisplay amount={amountPending} /> (refund 
fees: <AmountDisplay amount={feesPending} />)</p>
+      <p>Refund fully received: <AmountDisplay amount={amountDone} /> (refund 
fees: <AmountDisplay amount={fullRefundFees} />)</p>
+      <p>Refund incoming: <AmountDisplay amount={amountPending} /></p>
     </div>
   );
 };
@@ -108,7 +105,7 @@ class RefundStatusView extends 
React.Component<RefundStatusViewProps, RefundStat
         <h1>Refund Status</h1>
         <p>Status of purchase <strong>{summary}</strong> from merchant 
<strong>{merchantName}</strong> (order id 
{purchase.contractTerms.order_id}).</p>
         <p>Total amount: <AmountDisplay amount={purchase.contractTerms.amount} 
/></p>
-        {purchase.finished ? <RefundDetail purchase={purchase} /> : 
<p>Purchase not completed.</p>}
+        {purchase.finished ? <RefundDetail purchase={purchase} 
fullRefundFees={this.state.refundFees!} /> : <p>Purchase not completed.</p>}
       </div>
     );
   }
@@ -116,7 +113,9 @@ class RefundStatusView extends 
React.Component<RefundStatusViewProps, RefundStat
   async update() {
     const purchase = await wxApi.getPurchase(this.props.contractTermsHash);
     console.log("got purchase", purchase);
-    this.setState({ purchase, gotResult: true });
+    const refundsDone = Object.keys(purchase.refundsDone).map((x) => 
purchase.refundsDone[x]);
+    const refundFees = await wxApi.getFullRefundFees( {refundPermissions: 
refundsDone });
+    this.setState({ purchase, gotResult: true, refundFees });
   }
 }
 
diff --git a/src/webex/renderHtml.tsx b/src/webex/renderHtml.tsx
index 2a5b5053..d26f726a 100644
--- a/src/webex/renderHtml.tsx
+++ b/src/webex/renderHtml.tsx
@@ -24,45 +24,13 @@
 /**
  * Imports.
  */
-import { amountToPretty } from "../helpers";
-import * as i18n from "../i18n";
 import {
   AmountJson,
   Amounts,
-  ContractTerms,
 } from "../types";
 
 import * as React from "react";
 
-/**
- * Render contract terms for the end user to view.
- */
-export function renderContractTerms(contractTerms: ContractTerms): JSX.Element 
{
-  let merchantName;
-  if (contractTerms.merchant && contractTerms.merchant.name) {
-    merchantName = <strong>{contractTerms.merchant.name}</strong>;
-  } else {
-    merchantName = <strong>(pub: {contractTerms.merchant_pub})</strong>;
-  }
-  const amount = <strong>{amountToPretty(contractTerms.amount)}</strong>;
-
-  return (
-    <div>
-      <i18n.Translate wrap="p">
-        The merchant <span>{merchantName}</span>
-        wants to enter a contract over <span>{amount}</span>{" "}
-        with you.
-      </i18n.Translate>
-      <p>{i18n.str`You are about to purchase:`}</p>
-      <ul>
-        {contractTerms.products.map(
-          (p: any, i: number) => (<li key={i}>{`${p.description}: 
${amountToPretty(p.price)}`}</li>))
-        }
-      </ul>
-    </div>
-  );
-}
-
 
 /**
  * Render amount as HTML, which non-breaking space between
diff --git a/src/webex/wxApi.ts b/src/webex/wxApi.ts
index 1423da53..096d855e 100644
--- a/src/webex/wxApi.ts
+++ b/src/webex/wxApi.ts
@@ -33,6 +33,7 @@ import {
   PreCoinRecord,
   PurchaseRecord,
   QueryPaymentResult,
+  RefundPermission,
   ReserveCreationInfo,
   ReserveRecord,
   SenderWireInfos,
@@ -345,3 +346,7 @@ export function acceptRefund(refundData: any): 
Promise<number> {
 export function getPurchase(contractTermsHash: string): 
Promise<PurchaseRecord> {
   return callBackend("get-purchase", { contractTermsHash });
 }
+
+export function getFullRefundFees(args: { refundPermissions: 
RefundPermission[] }): Promise<AmountJson> {
+  return callBackend("get-full-refund-fees", { refundPermissions: 
args.refundPermissions });
+}
diff --git a/src/webex/wxBackend.ts b/src/webex/wxBackend.ts
index 2f249af4..16da3d97 100644
--- a/src/webex/wxBackend.ts
+++ b/src/webex/wxBackend.ts
@@ -323,6 +323,8 @@ function handleMessage(sender: MessageSender,
         throw Error("contractTermsHash missing");
       }
       return needsWallet().getPurchase(contractTermsHash);
+    case "get-full-refund-fees":
+      return needsWallet().getFullRefundFees(detail.refundPermissions);
     default:
       // Exhaustiveness check.
       // See https://www.typescriptlang.org/docs/handbook/advanced-types.html
@@ -599,22 +601,23 @@ export async function wxMain() {
   };
 
   chrome.tabs.query({}, (tabs) => {
+    console.log("got tabs", tabs);
     for (const tab of tabs) {
       if (!tab.url || !tab.id) {
-        return;
+        continue;
       }
       const uri = new URI(tab.url);
       if (uri.protocol() !== "http" && uri.protocol() !== "https") {
-        return;
+        continue;
       }
       console.log("injecting into existing tab", tab.id, "with url", 
uri.href(), "protocol", uri.protocol());
-      injectScript(tab.id, { file: "/dist/contentScript-bundle.js" }, 
uri.href());
+      injectScript(tab.id, { file: "/dist/contentScript-bundle.js", runAt: 
"document_start" }, uri.href());
       const code = `
         if (("taler" in window) || 
document.documentElement.getAttribute("data-taler-nojs")) {
           document.dispatchEvent(new Event("taler-probe-result"));
         }
       `;
-      injectScript(tab.id, { code, runAt: "document_idle" }, uri.href());
+      injectScript(tab.id, { code, runAt: "document_start" }, uri.href());
     }
   });
 

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



reply via email to

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