url-based pay/withdraw, use react hooks

This commit is contained in:
Florian Dold 2019-08-29 23:12:55 +02:00
parent 1390175a9a
commit defbf625bd
No known key found for this signature in database
GPG Key ID: D2E4F00F29D02A4B
27 changed files with 2751 additions and 2954 deletions

View File

@ -104,6 +104,7 @@ const tsBaseArgs = {
allowJs: true,
checkJs: true,
incremental: true,
esModuleInterop: true,
};

View File

@ -18,7 +18,7 @@
"devDependencies": {
"@types/react": "^16.4.0",
"@types/react-dom": "^16.0.0",
"ava": "^1.4.1",
"ava": "^2.3.0",
"awesome-typescript-loader": "^5.2.1",
"glob": "^7.1.1",
"gulp": "^4.0.0",
@ -44,10 +44,10 @@
"uglify-js": "^3.0.27",
"vinyl": "^2.2.0",
"vinyl-fs": "^3.0.3",
"webpack": "^4.29.6",
"webpack": "^4.39.3",
"webpack-bundle-analyzer": "^3.0.2",
"webpack-cli": "^3.1.0",
"webpack-merge": "^4.1.0"
"webpack-merge": "^4.2.2"
},
"dependencies": {
"@types/chrome": "^0.0.88",

View File

@ -149,7 +149,7 @@ program
const {
reservePub,
confirmTransferUrl,
} = await wallet.createReserveFromWithdrawUrl(
} = await wallet.acceptWithdrawal(
withdrawUrl,
selectedExchange,
);
@ -191,11 +191,6 @@ program
process.exit(0);
return;
}
if (result.status === "session-replayed") {
console.log("already paid! (replayed in different session)");
process.exit(0);
return;
}
if (result.status === "payment-possible") {
console.log("paying ...");
} else {

View File

@ -27,7 +27,6 @@ export interface HttpResponse {
responseJson: object & any;
}
/**
* The request library is bundled into an interface to make mocking easy.
*/
@ -37,15 +36,16 @@ export interface HttpRequestLibrary {
postJson(url: string, body: any): Promise<HttpResponse>;
}
/**
* An implementation of the [[HttpRequestLibrary]] using the
* browser's XMLHttpRequest.
*/
export class BrowserHttpLib implements HttpRequestLibrary {
private req(method: string,
private req(
method: string,
url: string,
options?: any): Promise<HttpResponse> {
options?: any,
): Promise<HttpResponse> {
return new Promise<HttpResponse>((resolve, reject) => {
const myRequest = new XMLHttpRequest();
myRequest.open(method, url);
@ -54,11 +54,36 @@ export class BrowserHttpLib implements HttpRequestLibrary {
} else {
myRequest.send();
}
myRequest.addEventListener("readystatechange", (e) => {
myRequest.onerror = e => {
console.error("http request error");
reject(Error("could not make XMLHttpRequest"));
};
myRequest.addEventListener("readystatechange", e => {
if (myRequest.readyState === XMLHttpRequest.DONE) {
const responseJson = JSON.parse(myRequest.responseText);
if (myRequest.status === 0) {
reject(Error("HTTP Request failed (status code 0, maybe URI scheme is wrong?)"))
return;
}
if (myRequest.status != 200) {
reject(
Error(
`HTTP Response with unexpected status code ${myRequest.status}: ${myRequest.statusText}`,
),
);
return;
}
let responseJson;
try {
responseJson = JSON.parse(myRequest.responseText);
} catch (e) {
reject(Error("Invalid JSON from HTTP response"));
return;
}
if (responseJson === null || typeof responseJson !== "object") {
reject(Error("Invalid JSON from HTTP response"));
return;
}
const resp = {
responseJson: responseJson,
@ -70,27 +95,22 @@ export class BrowserHttpLib implements HttpRequestLibrary {
});
}
get(url: string) {
return this.req("get", url);
}
postJson(url: string, body: any) {
return this.req("post", url, {req: JSON.stringify(body)});
return this.req("post", url, { req: JSON.stringify(body) });
}
postForm(url: string, form: any) {
return this.req("post", url, {req: form});
return this.req("post", url, { req: form });
}
}
/**
* Exception thrown on request errors.
*/
export class RequestException {
constructor(public detail: any) {
}
constructor(public detail: any) {}
}

View File

@ -31,6 +31,8 @@ import * as React from "react";
const jed = setupJed();
let enableTracing = false;
/**
* Set up jed library for internationalization,
@ -94,7 +96,7 @@ function stringifyChildren(children: any): string {
return `%${n++}$s`;
});
const s = ss.join("").replace(/ +/g, " ").trim();
console.log("translation lookup", JSON.stringify(s));
enableTracing && console.log("translation lookup", JSON.stringify(s));
return s;
}

View File

@ -37,28 +37,28 @@ msgstr ""
msgid "time (ms/op)"
msgstr ""
#: src/webex/pages/confirm-contract.tsx:78
#: src/webex/pages/pay.tsx:78
#, c-format
msgid "show more details"
msgstr ""
#: src/webex/pages/confirm-contract.tsx:92
#: src/webex/pages/pay.tsx:92
#, c-format
msgid "Accepted exchanges:"
msgstr ""
#: src/webex/pages/confirm-contract.tsx:97
#: src/webex/pages/pay.tsx:97
#, c-format
msgid "Exchanges in the wallet:"
msgstr ""
#: src/webex/pages/confirm-contract.tsx:219
#: src/webex/pages/pay.tsx:219
#, 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:221
#: src/webex/pages/pay.tsx:221
#, c-format
msgid ""
"You do not have any funds from an exchange that is accepted by this "
@ -66,180 +66,58 @@ msgid ""
"wallet."
msgstr ""
#: src/webex/pages/confirm-contract.tsx:322
#: src/webex/pages/pay.tsx:322
#, fuzzy, c-format
msgid "Confirm payment"
msgstr "Bezahlung bestätigen"
#: src/webex/pages/confirm-contract.tsx:332
#: src/webex/pages/pay.tsx:332
#, fuzzy, c-format
msgid "Submitting payment"
msgstr "Bezahlung bestätigen"
#: src/webex/pages/confirm-contract.tsx:343
#: src/webex/pages/pay.tsx:343
#, c-format
msgid ""
"You already paid for this, clicking \"Confirm payment\" will not cost money "
"again."
msgstr ""
#: src/webex/pages/confirm-contract.tsx:357
#: src/webex/pages/pay.tsx:357
#, fuzzy, c-format
msgid "Aborting payment ..."
msgstr "Bezahlung bestätigen"
#: src/webex/pages/confirm-contract.tsx:359
#: src/webex/pages/pay.tsx:359
#, c-format
msgid "Payment aborted!"
msgstr ""
#: src/webex/pages/confirm-contract.tsx:362
#: src/webex/pages/pay.tsx:362
#, fuzzy, c-format
msgid "Retry Payment"
msgstr "Bezahlung bestätigen"
#: src/webex/pages/confirm-contract.tsx:365
#: src/webex/pages/pay.tsx:365
#, fuzzy, c-format
msgid "Abort Payment"
msgstr "Bezahlung bestätigen"
#: src/webex/pages/confirm-contract.tsx:374
#: src/webex/pages/pay.tsx:374
#, fuzzy, c-format
msgid "The merchant %1$s offers you to purchase:"
msgstr "Der Händler %1$s möchte einen Vertrag über %2$s mit Ihnen abschließen."
#: src/webex/pages/confirm-contract.tsx:383
#: src/webex/pages/pay.tsx:383
#, c-format
msgid "The total price is %1$s (plus %2$s fees)."
msgstr ""
#: src/webex/pages/confirm-contract.tsx:387
#: src/webex/pages/pay.tsx:387
#, c-format
msgid "The total price is %1$s."
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:128
#, c-format
msgid "Select"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:145
#, c-format
msgid "Error: URL may not be relative"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:160
#, c-format
msgid "Invalid exchange URL (%1$s)"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:210
#, c-format
msgid "The exchange is trusted by the wallet."
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:216
#, c-format
msgid "The exchange is audited by a trusted auditor."
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:222
#, c-format
msgid ""
"Warning: The exchange is neither directly trusted nor audited by a trusted "
"auditor. If you withdraw from this exchange, it will be trusted in the "
"future."
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:231
#, c-format
msgid ""
"Using exchange provider %1$s. The exchange provider will charge %2$s in fees."
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:243
#, c-format
msgid "Waiting for a response from %1$s %2$s"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:260
#, c-format
msgid ""
"Information about fees will be available when an exchange provider is "
"selected."
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:279
#, c-format
msgid ""
"Your wallet (protocol version %1$s) might be outdated.%2$s The exchange has "
"a higher, incompatible protocol version (%3$s)."
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:290
#, c-format
msgid ""
"The chosen exchange (protocol version %1$s might be outdated.%2$s The "
"exchange has a lower, incompatible protocol version than your wallet "
"(protocol version %3$s)."
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:309
#, c-format
msgid "Accept fees and withdraw"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:314
#, c-format
msgid "Change Exchange Provider"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:335
#, c-format
msgid ""
"Please select an exchange. You can review the details before after your "
"selection."
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:341
#: src/webex/pages/confirm-create-reserve.tsx:353
#, c-format
msgid "Select %1$s"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:370
#, 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:459
#, c-format
msgid ""
"Oops, something went wrong. The wallet responded with error status (%1$s)."
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:468
#, c-format
msgid "Checking URL, please wait ..."
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:482
#, c-format
msgid "Can't parse amount: %1$s"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:489
#, c-format
msgid "Can't parse wire_types: %1$s"
msgstr ""
#. #-#-#-#-# - (PACKAGE VERSION) #-#-#-#-#
#. TODO:generic error reporting function or component.
#: src/webex/pages/confirm-create-reserve.tsx:519 src/webex/pages/tip.tsx:180
#, c-format
msgid "Fatal error: \"%1$s\"."
msgstr ""
#: src/webex/pages/popup.tsx:165
#, c-format
msgid "Balance"
@ -379,6 +257,127 @@ msgstr "Bezahlung bestätigen"
msgid "Cancel"
msgstr "Saldo"
#. #-#-#-#-# - (PACKAGE VERSION) #-#-#-#-#
#. TODO:generic error reporting function or component.
#: src/webex/pages/tip.tsx:180 src/webex/pages/withdraw.tsx:519
#, c-format
msgid "Fatal error: \"%1$s\"."
msgstr ""
#: src/webex/pages/withdraw.tsx:128
#, c-format
msgid "Select"
msgstr ""
#: src/webex/pages/withdraw.tsx:145
#, c-format
msgid "Error: URL may not be relative"
msgstr ""
#: src/webex/pages/withdraw.tsx:160
#, c-format
msgid "Invalid exchange URL (%1$s)"
msgstr ""
#: src/webex/pages/withdraw.tsx:210
#, c-format
msgid "The exchange is trusted by the wallet."
msgstr ""
#: src/webex/pages/withdraw.tsx:216
#, c-format
msgid "The exchange is audited by a trusted auditor."
msgstr ""
#: src/webex/pages/withdraw.tsx:222
#, c-format
msgid ""
"Warning: The exchange is neither directly trusted nor audited by a trusted "
"auditor. If you withdraw from this exchange, it will be trusted in the "
"future."
msgstr ""
#: src/webex/pages/withdraw.tsx:231
#, c-format
msgid ""
"Using exchange provider %1$s. The exchange provider will charge %2$s in fees."
msgstr ""
#: src/webex/pages/withdraw.tsx:243
#, c-format
msgid "Waiting for a response from %1$s %2$s"
msgstr ""
#: src/webex/pages/withdraw.tsx:260
#, c-format
msgid ""
"Information about fees will be available when an exchange provider is "
"selected."
msgstr ""
#: src/webex/pages/withdraw.tsx:279
#, c-format
msgid ""
"Your wallet (protocol version %1$s) might be outdated.%2$s The exchange has "
"a higher, incompatible protocol version (%3$s)."
msgstr ""
#: src/webex/pages/withdraw.tsx:290
#, c-format
msgid ""
"The chosen exchange (protocol version %1$s might be outdated.%2$s The "
"exchange has a lower, incompatible protocol version than your wallet "
"(protocol version %3$s)."
msgstr ""
#: src/webex/pages/withdraw.tsx:309
#, c-format
msgid "Accept fees and withdraw"
msgstr ""
#: src/webex/pages/withdraw.tsx:314
#, c-format
msgid "Change Exchange Provider"
msgstr ""
#: src/webex/pages/withdraw.tsx:335
#, c-format
msgid ""
"Please select an exchange. You can review the details before after your "
"selection."
msgstr ""
#: src/webex/pages/withdraw.tsx:341 src/webex/pages/withdraw.tsx:353
#, c-format
msgid "Select %1$s"
msgstr ""
#: src/webex/pages/withdraw.tsx:370
#, c-format
msgid "You are about to withdraw %1$s from your bank account into your wallet."
msgstr ""
#: src/webex/pages/withdraw.tsx:459
#, c-format
msgid ""
"Oops, something went wrong. The wallet responded with error status (%1$s)."
msgstr ""
#: src/webex/pages/withdraw.tsx:468
#, c-format
msgid "Checking URL, please wait ..."
msgstr ""
#: src/webex/pages/withdraw.tsx:482
#, c-format
msgid "Can't parse amount: %1$s"
msgstr ""
#: src/webex/pages/withdraw.tsx:489
#, c-format
msgid "Can't parse wire_types: %1$s"
msgstr ""
#: src/webex/renderHtml.tsx:225
#, fuzzy, c-format
msgid "Withdrawal fees:"

View File

@ -37,28 +37,28 @@ msgstr ""
msgid "time (ms/op)"
msgstr ""
#: src/webex/pages/confirm-contract.tsx:78
#: src/webex/pages/pay.tsx:78
#, c-format
msgid "show more details"
msgstr ""
#: src/webex/pages/confirm-contract.tsx:92
#: src/webex/pages/pay.tsx:92
#, c-format
msgid "Accepted exchanges:"
msgstr ""
#: src/webex/pages/confirm-contract.tsx:97
#: src/webex/pages/pay.tsx:97
#, c-format
msgid "Exchanges in the wallet:"
msgstr ""
#: src/webex/pages/confirm-contract.tsx:219
#: src/webex/pages/pay.tsx:219
#, 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:221
#: src/webex/pages/pay.tsx:221
#, c-format
msgid ""
"You do not have any funds from an exchange that is accepted by this "
@ -66,180 +66,58 @@ msgid ""
"wallet."
msgstr ""
#: src/webex/pages/confirm-contract.tsx:322
#: src/webex/pages/pay.tsx:322
#, c-format
msgid "Confirm payment"
msgstr ""
#: src/webex/pages/confirm-contract.tsx:332
#: src/webex/pages/pay.tsx:332
#, c-format
msgid "Submitting payment"
msgstr ""
#: src/webex/pages/confirm-contract.tsx:343
#: src/webex/pages/pay.tsx:343
#, c-format
msgid ""
"You already paid for this, clicking \"Confirm payment\" will not cost money "
"again."
msgstr ""
#: src/webex/pages/confirm-contract.tsx:357
#: src/webex/pages/pay.tsx:357
#, c-format
msgid "Aborting payment ..."
msgstr ""
#: src/webex/pages/confirm-contract.tsx:359
#: src/webex/pages/pay.tsx:359
#, c-format
msgid "Payment aborted!"
msgstr ""
#: src/webex/pages/confirm-contract.tsx:362
#: src/webex/pages/pay.tsx:362
#, c-format
msgid "Retry Payment"
msgstr ""
#: src/webex/pages/confirm-contract.tsx:365
#: src/webex/pages/pay.tsx:365
#, c-format
msgid "Abort Payment"
msgstr ""
#: src/webex/pages/confirm-contract.tsx:374
#: src/webex/pages/pay.tsx:374
#, c-format
msgid "The merchant %1$s offers you to purchase:"
msgstr ""
#: src/webex/pages/confirm-contract.tsx:383
#: src/webex/pages/pay.tsx:383
#, c-format
msgid "The total price is %1$s (plus %2$s fees)."
msgstr ""
#: src/webex/pages/confirm-contract.tsx:387
#: src/webex/pages/pay.tsx:387
#, c-format
msgid "The total price is %1$s."
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:128
#, c-format
msgid "Select"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:145
#, c-format
msgid "Error: URL may not be relative"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:160
#, c-format
msgid "Invalid exchange URL (%1$s)"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:210
#, c-format
msgid "The exchange is trusted by the wallet."
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:216
#, c-format
msgid "The exchange is audited by a trusted auditor."
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:222
#, c-format
msgid ""
"Warning: The exchange is neither directly trusted nor audited by a trusted "
"auditor. If you withdraw from this exchange, it will be trusted in the "
"future."
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:231
#, c-format
msgid ""
"Using exchange provider %1$s. The exchange provider will charge %2$s in fees."
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:243
#, c-format
msgid "Waiting for a response from %1$s %2$s"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:260
#, c-format
msgid ""
"Information about fees will be available when an exchange provider is "
"selected."
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:279
#, c-format
msgid ""
"Your wallet (protocol version %1$s) might be outdated.%2$s The exchange has "
"a higher, incompatible protocol version (%3$s)."
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:290
#, c-format
msgid ""
"The chosen exchange (protocol version %1$s might be outdated.%2$s The "
"exchange has a lower, incompatible protocol version than your wallet "
"(protocol version %3$s)."
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:309
#, c-format
msgid "Accept fees and withdraw"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:314
#, c-format
msgid "Change Exchange Provider"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:335
#, c-format
msgid ""
"Please select an exchange. You can review the details before after your "
"selection."
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:341
#: src/webex/pages/confirm-create-reserve.tsx:353
#, c-format
msgid "Select %1$s"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:370
#, 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:459
#, c-format
msgid ""
"Oops, something went wrong. The wallet responded with error status (%1$s)."
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:468
#, c-format
msgid "Checking URL, please wait ..."
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:482
#, c-format
msgid "Can't parse amount: %1$s"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:489
#, c-format
msgid "Can't parse wire_types: %1$s"
msgstr ""
#. #-#-#-#-# - (PACKAGE VERSION) #-#-#-#-#
#. TODO:generic error reporting function or component.
#: src/webex/pages/confirm-create-reserve.tsx:519 src/webex/pages/tip.tsx:180
#, c-format
msgid "Fatal error: \"%1$s\"."
msgstr ""
#: src/webex/pages/popup.tsx:165
#, c-format
msgid "Balance"
@ -370,6 +248,127 @@ msgstr ""
msgid "Cancel"
msgstr ""
#. #-#-#-#-# - (PACKAGE VERSION) #-#-#-#-#
#. TODO:generic error reporting function or component.
#: src/webex/pages/tip.tsx:180 src/webex/pages/withdraw.tsx:519
#, c-format
msgid "Fatal error: \"%1$s\"."
msgstr ""
#: src/webex/pages/withdraw.tsx:128
#, c-format
msgid "Select"
msgstr ""
#: src/webex/pages/withdraw.tsx:145
#, c-format
msgid "Error: URL may not be relative"
msgstr ""
#: src/webex/pages/withdraw.tsx:160
#, c-format
msgid "Invalid exchange URL (%1$s)"
msgstr ""
#: src/webex/pages/withdraw.tsx:210
#, c-format
msgid "The exchange is trusted by the wallet."
msgstr ""
#: src/webex/pages/withdraw.tsx:216
#, c-format
msgid "The exchange is audited by a trusted auditor."
msgstr ""
#: src/webex/pages/withdraw.tsx:222
#, c-format
msgid ""
"Warning: The exchange is neither directly trusted nor audited by a trusted "
"auditor. If you withdraw from this exchange, it will be trusted in the "
"future."
msgstr ""
#: src/webex/pages/withdraw.tsx:231
#, c-format
msgid ""
"Using exchange provider %1$s. The exchange provider will charge %2$s in fees."
msgstr ""
#: src/webex/pages/withdraw.tsx:243
#, c-format
msgid "Waiting for a response from %1$s %2$s"
msgstr ""
#: src/webex/pages/withdraw.tsx:260
#, c-format
msgid ""
"Information about fees will be available when an exchange provider is "
"selected."
msgstr ""
#: src/webex/pages/withdraw.tsx:279
#, c-format
msgid ""
"Your wallet (protocol version %1$s) might be outdated.%2$s The exchange has "
"a higher, incompatible protocol version (%3$s)."
msgstr ""
#: src/webex/pages/withdraw.tsx:290
#, c-format
msgid ""
"The chosen exchange (protocol version %1$s might be outdated.%2$s The "
"exchange has a lower, incompatible protocol version than your wallet "
"(protocol version %3$s)."
msgstr ""
#: src/webex/pages/withdraw.tsx:309
#, c-format
msgid "Accept fees and withdraw"
msgstr ""
#: src/webex/pages/withdraw.tsx:314
#, c-format
msgid "Change Exchange Provider"
msgstr ""
#: src/webex/pages/withdraw.tsx:335
#, c-format
msgid ""
"Please select an exchange. You can review the details before after your "
"selection."
msgstr ""
#: src/webex/pages/withdraw.tsx:341 src/webex/pages/withdraw.tsx:353
#, c-format
msgid "Select %1$s"
msgstr ""
#: src/webex/pages/withdraw.tsx:370
#, c-format
msgid "You are about to withdraw %1$s from your bank account into your wallet."
msgstr ""
#: src/webex/pages/withdraw.tsx:459
#, c-format
msgid ""
"Oops, something went wrong. The wallet responded with error status (%1$s)."
msgstr ""
#: src/webex/pages/withdraw.tsx:468
#, c-format
msgid "Checking URL, please wait ..."
msgstr ""
#: src/webex/pages/withdraw.tsx:482
#, c-format
msgid "Can't parse amount: %1$s"
msgstr ""
#: src/webex/pages/withdraw.tsx:489
#, c-format
msgid "Can't parse wire_types: %1$s"
msgstr ""
#: src/webex/renderHtml.tsx:225
#, c-format
msgid "Withdrawal fees:"

View File

@ -37,28 +37,28 @@ msgstr ""
msgid "time (ms/op)"
msgstr ""
#: src/webex/pages/confirm-contract.tsx:78
#: src/webex/pages/pay.tsx:78
#, c-format
msgid "show more details"
msgstr ""
#: src/webex/pages/confirm-contract.tsx:92
#: src/webex/pages/pay.tsx:92
#, c-format
msgid "Accepted exchanges:"
msgstr ""
#: src/webex/pages/confirm-contract.tsx:97
#: src/webex/pages/pay.tsx:97
#, c-format
msgid "Exchanges in the wallet:"
msgstr ""
#: src/webex/pages/confirm-contract.tsx:219
#: src/webex/pages/pay.tsx:219
#, 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:221
#: src/webex/pages/pay.tsx:221
#, c-format
msgid ""
"You do not have any funds from an exchange that is accepted by this "
@ -66,180 +66,58 @@ msgid ""
"wallet."
msgstr ""
#: src/webex/pages/confirm-contract.tsx:322
#: src/webex/pages/pay.tsx:322
#, c-format
msgid "Confirm payment"
msgstr ""
#: src/webex/pages/confirm-contract.tsx:332
#: src/webex/pages/pay.tsx:332
#, c-format
msgid "Submitting payment"
msgstr ""
#: src/webex/pages/confirm-contract.tsx:343
#: src/webex/pages/pay.tsx:343
#, c-format
msgid ""
"You already paid for this, clicking \"Confirm payment\" will not cost money "
"again."
msgstr ""
#: src/webex/pages/confirm-contract.tsx:357
#: src/webex/pages/pay.tsx:357
#, c-format
msgid "Aborting payment ..."
msgstr ""
#: src/webex/pages/confirm-contract.tsx:359
#: src/webex/pages/pay.tsx:359
#, c-format
msgid "Payment aborted!"
msgstr ""
#: src/webex/pages/confirm-contract.tsx:362
#: src/webex/pages/pay.tsx:362
#, c-format
msgid "Retry Payment"
msgstr ""
#: src/webex/pages/confirm-contract.tsx:365
#: src/webex/pages/pay.tsx:365
#, c-format
msgid "Abort Payment"
msgstr ""
#: src/webex/pages/confirm-contract.tsx:374
#: src/webex/pages/pay.tsx:374
#, c-format
msgid "The merchant %1$s offers you to purchase:"
msgstr ""
#: src/webex/pages/confirm-contract.tsx:383
#: src/webex/pages/pay.tsx:383
#, c-format
msgid "The total price is %1$s (plus %2$s fees)."
msgstr ""
#: src/webex/pages/confirm-contract.tsx:387
#: src/webex/pages/pay.tsx:387
#, c-format
msgid "The total price is %1$s."
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:128
#, c-format
msgid "Select"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:145
#, c-format
msgid "Error: URL may not be relative"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:160
#, c-format
msgid "Invalid exchange URL (%1$s)"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:210
#, c-format
msgid "The exchange is trusted by the wallet."
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:216
#, c-format
msgid "The exchange is audited by a trusted auditor."
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:222
#, c-format
msgid ""
"Warning: The exchange is neither directly trusted nor audited by a trusted "
"auditor. If you withdraw from this exchange, it will be trusted in the "
"future."
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:231
#, c-format
msgid ""
"Using exchange provider %1$s. The exchange provider will charge %2$s in fees."
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:243
#, c-format
msgid "Waiting for a response from %1$s %2$s"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:260
#, c-format
msgid ""
"Information about fees will be available when an exchange provider is "
"selected."
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:279
#, c-format
msgid ""
"Your wallet (protocol version %1$s) might be outdated.%2$s The exchange has "
"a higher, incompatible protocol version (%3$s)."
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:290
#, c-format
msgid ""
"The chosen exchange (protocol version %1$s might be outdated.%2$s The "
"exchange has a lower, incompatible protocol version than your wallet "
"(protocol version %3$s)."
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:309
#, c-format
msgid "Accept fees and withdraw"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:314
#, c-format
msgid "Change Exchange Provider"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:335
#, c-format
msgid ""
"Please select an exchange. You can review the details before after your "
"selection."
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:341
#: src/webex/pages/confirm-create-reserve.tsx:353
#, c-format
msgid "Select %1$s"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:370
#, 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:459
#, c-format
msgid ""
"Oops, something went wrong. The wallet responded with error status (%1$s)."
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:468
#, c-format
msgid "Checking URL, please wait ..."
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:482
#, c-format
msgid "Can't parse amount: %1$s"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:489
#, c-format
msgid "Can't parse wire_types: %1$s"
msgstr ""
#. #-#-#-#-# - (PACKAGE VERSION) #-#-#-#-#
#. TODO:generic error reporting function or component.
#: src/webex/pages/confirm-create-reserve.tsx:519 src/webex/pages/tip.tsx:180
#, c-format
msgid "Fatal error: \"%1$s\"."
msgstr ""
#: src/webex/pages/popup.tsx:165
#, c-format
msgid "Balance"
@ -370,6 +248,127 @@ msgstr ""
msgid "Cancel"
msgstr ""
#. #-#-#-#-# - (PACKAGE VERSION) #-#-#-#-#
#. TODO:generic error reporting function or component.
#: src/webex/pages/tip.tsx:180 src/webex/pages/withdraw.tsx:519
#, c-format
msgid "Fatal error: \"%1$s\"."
msgstr ""
#: src/webex/pages/withdraw.tsx:128
#, c-format
msgid "Select"
msgstr ""
#: src/webex/pages/withdraw.tsx:145
#, c-format
msgid "Error: URL may not be relative"
msgstr ""
#: src/webex/pages/withdraw.tsx:160
#, c-format
msgid "Invalid exchange URL (%1$s)"
msgstr ""
#: src/webex/pages/withdraw.tsx:210
#, c-format
msgid "The exchange is trusted by the wallet."
msgstr ""
#: src/webex/pages/withdraw.tsx:216
#, c-format
msgid "The exchange is audited by a trusted auditor."
msgstr ""
#: src/webex/pages/withdraw.tsx:222
#, c-format
msgid ""
"Warning: The exchange is neither directly trusted nor audited by a trusted "
"auditor. If you withdraw from this exchange, it will be trusted in the "
"future."
msgstr ""
#: src/webex/pages/withdraw.tsx:231
#, c-format
msgid ""
"Using exchange provider %1$s. The exchange provider will charge %2$s in fees."
msgstr ""
#: src/webex/pages/withdraw.tsx:243
#, c-format
msgid "Waiting for a response from %1$s %2$s"
msgstr ""
#: src/webex/pages/withdraw.tsx:260
#, c-format
msgid ""
"Information about fees will be available when an exchange provider is "
"selected."
msgstr ""
#: src/webex/pages/withdraw.tsx:279
#, c-format
msgid ""
"Your wallet (protocol version %1$s) might be outdated.%2$s The exchange has "
"a higher, incompatible protocol version (%3$s)."
msgstr ""
#: src/webex/pages/withdraw.tsx:290
#, c-format
msgid ""
"The chosen exchange (protocol version %1$s might be outdated.%2$s The "
"exchange has a lower, incompatible protocol version than your wallet "
"(protocol version %3$s)."
msgstr ""
#: src/webex/pages/withdraw.tsx:309
#, c-format
msgid "Accept fees and withdraw"
msgstr ""
#: src/webex/pages/withdraw.tsx:314
#, c-format
msgid "Change Exchange Provider"
msgstr ""
#: src/webex/pages/withdraw.tsx:335
#, c-format
msgid ""
"Please select an exchange. You can review the details before after your "
"selection."
msgstr ""
#: src/webex/pages/withdraw.tsx:341 src/webex/pages/withdraw.tsx:353
#, c-format
msgid "Select %1$s"
msgstr ""
#: src/webex/pages/withdraw.tsx:370
#, c-format
msgid "You are about to withdraw %1$s from your bank account into your wallet."
msgstr ""
#: src/webex/pages/withdraw.tsx:459
#, c-format
msgid ""
"Oops, something went wrong. The wallet responded with error status (%1$s)."
msgstr ""
#: src/webex/pages/withdraw.tsx:468
#, c-format
msgid "Checking URL, please wait ..."
msgstr ""
#: src/webex/pages/withdraw.tsx:482
#, c-format
msgid "Can't parse amount: %1$s"
msgstr ""
#: src/webex/pages/withdraw.tsx:489
#, c-format
msgid "Can't parse wire_types: %1$s"
msgstr ""
#: src/webex/renderHtml.tsx:225
#, c-format
msgid "Withdrawal fees:"

View File

@ -37,28 +37,28 @@ msgstr ""
msgid "time (ms/op)"
msgstr ""
#: src/webex/pages/confirm-contract.tsx:78
#: src/webex/pages/pay.tsx:78
#, c-format
msgid "show more details"
msgstr ""
#: src/webex/pages/confirm-contract.tsx:92
#: src/webex/pages/pay.tsx:92
#, c-format
msgid "Accepted exchanges:"
msgstr ""
#: src/webex/pages/confirm-contract.tsx:97
#: src/webex/pages/pay.tsx:97
#, c-format
msgid "Exchanges in the wallet:"
msgstr ""
#: src/webex/pages/confirm-contract.tsx:219
#: src/webex/pages/pay.tsx:219
#, 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:221
#: src/webex/pages/pay.tsx:221
#, c-format
msgid ""
"You do not have any funds from an exchange that is accepted by this "
@ -66,180 +66,58 @@ msgid ""
"wallet."
msgstr ""
#: src/webex/pages/confirm-contract.tsx:322
#: src/webex/pages/pay.tsx:322
#, c-format
msgid "Confirm payment"
msgstr ""
#: src/webex/pages/confirm-contract.tsx:332
#: src/webex/pages/pay.tsx:332
#, c-format
msgid "Submitting payment"
msgstr ""
#: src/webex/pages/confirm-contract.tsx:343
#: src/webex/pages/pay.tsx:343
#, c-format
msgid ""
"You already paid for this, clicking \"Confirm payment\" will not cost money "
"again."
msgstr ""
#: src/webex/pages/confirm-contract.tsx:357
#: src/webex/pages/pay.tsx:357
#, c-format
msgid "Aborting payment ..."
msgstr ""
#: src/webex/pages/confirm-contract.tsx:359
#: src/webex/pages/pay.tsx:359
#, c-format
msgid "Payment aborted!"
msgstr ""
#: src/webex/pages/confirm-contract.tsx:362
#: src/webex/pages/pay.tsx:362
#, c-format
msgid "Retry Payment"
msgstr ""
#: src/webex/pages/confirm-contract.tsx:365
#: src/webex/pages/pay.tsx:365
#, c-format
msgid "Abort Payment"
msgstr ""
#: src/webex/pages/confirm-contract.tsx:374
#: src/webex/pages/pay.tsx:374
#, c-format
msgid "The merchant %1$s offers you to purchase:"
msgstr ""
#: src/webex/pages/confirm-contract.tsx:383
#: src/webex/pages/pay.tsx:383
#, c-format
msgid "The total price is %1$s (plus %2$s fees)."
msgstr ""
#: src/webex/pages/confirm-contract.tsx:387
#: src/webex/pages/pay.tsx:387
#, c-format
msgid "The total price is %1$s."
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:128
#, c-format
msgid "Select"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:145
#, c-format
msgid "Error: URL may not be relative"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:160
#, c-format
msgid "Invalid exchange URL (%1$s)"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:210
#, c-format
msgid "The exchange is trusted by the wallet."
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:216
#, c-format
msgid "The exchange is audited by a trusted auditor."
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:222
#, c-format
msgid ""
"Warning: The exchange is neither directly trusted nor audited by a trusted "
"auditor. If you withdraw from this exchange, it will be trusted in the "
"future."
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:231
#, c-format
msgid ""
"Using exchange provider %1$s. The exchange provider will charge %2$s in fees."
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:243
#, c-format
msgid "Waiting for a response from %1$s %2$s"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:260
#, c-format
msgid ""
"Information about fees will be available when an exchange provider is "
"selected."
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:279
#, c-format
msgid ""
"Your wallet (protocol version %1$s) might be outdated.%2$s The exchange has "
"a higher, incompatible protocol version (%3$s)."
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:290
#, c-format
msgid ""
"The chosen exchange (protocol version %1$s might be outdated.%2$s The "
"exchange has a lower, incompatible protocol version than your wallet "
"(protocol version %3$s)."
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:309
#, c-format
msgid "Accept fees and withdraw"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:314
#, c-format
msgid "Change Exchange Provider"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:335
#, c-format
msgid ""
"Please select an exchange. You can review the details before after your "
"selection."
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:341
#: src/webex/pages/confirm-create-reserve.tsx:353
#, c-format
msgid "Select %1$s"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:370
#, 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:459
#, c-format
msgid ""
"Oops, something went wrong. The wallet responded with error status (%1$s)."
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:468
#, c-format
msgid "Checking URL, please wait ..."
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:482
#, c-format
msgid "Can't parse amount: %1$s"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:489
#, c-format
msgid "Can't parse wire_types: %1$s"
msgstr ""
#. #-#-#-#-# - (PACKAGE VERSION) #-#-#-#-#
#. TODO:generic error reporting function or component.
#: src/webex/pages/confirm-create-reserve.tsx:519 src/webex/pages/tip.tsx:180
#, c-format
msgid "Fatal error: \"%1$s\"."
msgstr ""
#: src/webex/pages/popup.tsx:165
#, c-format
msgid "Balance"
@ -370,6 +248,127 @@ msgstr ""
msgid "Cancel"
msgstr ""
#. #-#-#-#-# - (PACKAGE VERSION) #-#-#-#-#
#. TODO:generic error reporting function or component.
#: src/webex/pages/tip.tsx:180 src/webex/pages/withdraw.tsx:519
#, c-format
msgid "Fatal error: \"%1$s\"."
msgstr ""
#: src/webex/pages/withdraw.tsx:128
#, c-format
msgid "Select"
msgstr ""
#: src/webex/pages/withdraw.tsx:145
#, c-format
msgid "Error: URL may not be relative"
msgstr ""
#: src/webex/pages/withdraw.tsx:160
#, c-format
msgid "Invalid exchange URL (%1$s)"
msgstr ""
#: src/webex/pages/withdraw.tsx:210
#, c-format
msgid "The exchange is trusted by the wallet."
msgstr ""
#: src/webex/pages/withdraw.tsx:216
#, c-format
msgid "The exchange is audited by a trusted auditor."
msgstr ""
#: src/webex/pages/withdraw.tsx:222
#, c-format
msgid ""
"Warning: The exchange is neither directly trusted nor audited by a trusted "
"auditor. If you withdraw from this exchange, it will be trusted in the "
"future."
msgstr ""
#: src/webex/pages/withdraw.tsx:231
#, c-format
msgid ""
"Using exchange provider %1$s. The exchange provider will charge %2$s in fees."
msgstr ""
#: src/webex/pages/withdraw.tsx:243
#, c-format
msgid "Waiting for a response from %1$s %2$s"
msgstr ""
#: src/webex/pages/withdraw.tsx:260
#, c-format
msgid ""
"Information about fees will be available when an exchange provider is "
"selected."
msgstr ""
#: src/webex/pages/withdraw.tsx:279
#, c-format
msgid ""
"Your wallet (protocol version %1$s) might be outdated.%2$s The exchange has "
"a higher, incompatible protocol version (%3$s)."
msgstr ""
#: src/webex/pages/withdraw.tsx:290
#, c-format
msgid ""
"The chosen exchange (protocol version %1$s might be outdated.%2$s The "
"exchange has a lower, incompatible protocol version than your wallet "
"(protocol version %3$s)."
msgstr ""
#: src/webex/pages/withdraw.tsx:309
#, c-format
msgid "Accept fees and withdraw"
msgstr ""
#: src/webex/pages/withdraw.tsx:314
#, c-format
msgid "Change Exchange Provider"
msgstr ""
#: src/webex/pages/withdraw.tsx:335
#, c-format
msgid ""
"Please select an exchange. You can review the details before after your "
"selection."
msgstr ""
#: src/webex/pages/withdraw.tsx:341 src/webex/pages/withdraw.tsx:353
#, c-format
msgid "Select %1$s"
msgstr ""
#: src/webex/pages/withdraw.tsx:370
#, c-format
msgid "You are about to withdraw %1$s from your bank account into your wallet."
msgstr ""
#: src/webex/pages/withdraw.tsx:459
#, c-format
msgid ""
"Oops, something went wrong. The wallet responded with error status (%1$s)."
msgstr ""
#: src/webex/pages/withdraw.tsx:468
#, c-format
msgid "Checking URL, please wait ..."
msgstr ""
#: src/webex/pages/withdraw.tsx:482
#, c-format
msgid "Can't parse amount: %1$s"
msgstr ""
#: src/webex/pages/withdraw.tsx:489
#, c-format
msgid "Can't parse wire_types: %1$s"
msgstr ""
#: src/webex/renderHtml.tsx:225
#, c-format
msgid "Withdrawal fees:"

View File

@ -92,90 +92,6 @@ strings['de'] = {
null,
""
],
"Select": [
null,
""
],
"Error: URL may not be relative": [
null,
""
],
"Invalid exchange URL (%1$s)": [
null,
""
],
"The exchange is trusted by the wallet.": [
null,
""
],
"The exchange is audited by a trusted auditor.": [
null,
""
],
"Warning: The exchange is neither directly trusted nor audited by a trusted auditor. If you withdraw from this exchange, it will be trusted in the future.": [
null,
""
],
"Using exchange provider %1$s. The exchange provider will charge %2$s in fees.": [
null,
""
],
"Waiting for a response from %1$s %2$s": [
null,
""
],
"Information about fees will be available when an exchange provider is selected.": [
null,
""
],
"Your wallet (protocol version %1$s) might be outdated.%2$s The exchange has a higher, incompatible protocol version (%3$s).": [
null,
""
],
"The chosen exchange (protocol version %1$s might be outdated.%2$s The exchange has a lower, incompatible protocol version than your wallet (protocol version %3$s).": [
null,
""
],
"Accept fees and withdraw": [
null,
""
],
"Change Exchange Provider": [
null,
""
],
"Please select an exchange. You can review the details before after your selection.": [
null,
""
],
"Select %1$s": [
null,
""
],
"You are about to withdraw %1$s from your bank account into your wallet.": [
null,
""
],
"Oops, something went wrong. The wallet responded with error status (%1$s).": [
null,
""
],
"Checking URL, please wait ...": [
null,
""
],
"Can't parse amount: %1$s": [
null,
""
],
"Can't parse wire_types: %1$s": [
null,
""
],
"Fatal error: \"%1$s\".": [
null,
""
],
"Balance": [
null,
"Saldo"
@ -280,6 +196,90 @@ strings['de'] = {
null,
"Saldo"
],
"Fatal error: \"%1$s\".": [
null,
""
],
"Select": [
null,
""
],
"Error: URL may not be relative": [
null,
""
],
"Invalid exchange URL (%1$s)": [
null,
""
],
"The exchange is trusted by the wallet.": [
null,
""
],
"The exchange is audited by a trusted auditor.": [
null,
""
],
"Warning: The exchange is neither directly trusted nor audited by a trusted auditor. If you withdraw from this exchange, it will be trusted in the future.": [
null,
""
],
"Using exchange provider %1$s. The exchange provider will charge %2$s in fees.": [
null,
""
],
"Waiting for a response from %1$s %2$s": [
null,
""
],
"Information about fees will be available when an exchange provider is selected.": [
null,
""
],
"Your wallet (protocol version %1$s) might be outdated.%2$s The exchange has a higher, incompatible protocol version (%3$s).": [
null,
""
],
"The chosen exchange (protocol version %1$s might be outdated.%2$s The exchange has a lower, incompatible protocol version than your wallet (protocol version %3$s).": [
null,
""
],
"Accept fees and withdraw": [
null,
""
],
"Change Exchange Provider": [
null,
""
],
"Please select an exchange. You can review the details before after your selection.": [
null,
""
],
"Select %1$s": [
null,
""
],
"You are about to withdraw %1$s from your bank account into your wallet.": [
null,
""
],
"Oops, something went wrong. The wallet responded with error status (%1$s).": [
null,
""
],
"Checking URL, please wait ...": [
null,
""
],
"Can't parse amount: %1$s": [
null,
""
],
"Can't parse wire_types: %1$s": [
null,
""
],
"Withdrawal fees:": [
null,
"Abheben bei"
@ -408,90 +408,6 @@ strings['en-US'] = {
null,
""
],
"Select": [
null,
""
],
"Error: URL may not be relative": [
null,
""
],
"Invalid exchange URL (%1$s)": [
null,
""
],
"The exchange is trusted by the wallet.": [
null,
""
],
"The exchange is audited by a trusted auditor.": [
null,
""
],
"Warning: The exchange is neither directly trusted nor audited by a trusted auditor. If you withdraw from this exchange, it will be trusted in the future.": [
null,
""
],
"Using exchange provider %1$s. The exchange provider will charge %2$s in fees.": [
null,
""
],
"Waiting for a response from %1$s %2$s": [
null,
""
],
"Information about fees will be available when an exchange provider is selected.": [
null,
""
],
"Your wallet (protocol version %1$s) might be outdated.%2$s The exchange has a higher, incompatible protocol version (%3$s).": [
null,
""
],
"The chosen exchange (protocol version %1$s might be outdated.%2$s The exchange has a lower, incompatible protocol version than your wallet (protocol version %3$s).": [
null,
""
],
"Accept fees and withdraw": [
null,
""
],
"Change Exchange Provider": [
null,
""
],
"Please select an exchange. You can review the details before after your selection.": [
null,
""
],
"Select %1$s": [
null,
""
],
"You are about to withdraw %1$s from your bank account into your wallet.": [
null,
""
],
"Oops, something went wrong. The wallet responded with error status (%1$s).": [
null,
""
],
"Checking URL, please wait ...": [
null,
""
],
"Can't parse amount: %1$s": [
null,
""
],
"Can't parse wire_types: %1$s": [
null,
""
],
"Fatal error: \"%1$s\".": [
null,
""
],
"Balance": [
null,
""
@ -596,6 +512,90 @@ strings['en-US'] = {
null,
""
],
"Fatal error: \"%1$s\".": [
null,
""
],
"Select": [
null,
""
],
"Error: URL may not be relative": [
null,
""
],
"Invalid exchange URL (%1$s)": [
null,
""
],
"The exchange is trusted by the wallet.": [
null,
""
],
"The exchange is audited by a trusted auditor.": [
null,
""
],
"Warning: The exchange is neither directly trusted nor audited by a trusted auditor. If you withdraw from this exchange, it will be trusted in the future.": [
null,
""
],
"Using exchange provider %1$s. The exchange provider will charge %2$s in fees.": [
null,
""
],
"Waiting for a response from %1$s %2$s": [
null,
""
],
"Information about fees will be available when an exchange provider is selected.": [
null,
""
],
"Your wallet (protocol version %1$s) might be outdated.%2$s The exchange has a higher, incompatible protocol version (%3$s).": [
null,
""
],
"The chosen exchange (protocol version %1$s might be outdated.%2$s The exchange has a lower, incompatible protocol version than your wallet (protocol version %3$s).": [
null,
""
],
"Accept fees and withdraw": [
null,
""
],
"Change Exchange Provider": [
null,
""
],
"Please select an exchange. You can review the details before after your selection.": [
null,
""
],
"Select %1$s": [
null,
""
],
"You are about to withdraw %1$s from your bank account into your wallet.": [
null,
""
],
"Oops, something went wrong. The wallet responded with error status (%1$s).": [
null,
""
],
"Checking URL, please wait ...": [
null,
""
],
"Can't parse amount: %1$s": [
null,
""
],
"Can't parse wire_types: %1$s": [
null,
""
],
"Withdrawal fees:": [
null,
""
@ -724,90 +724,6 @@ strings['fr'] = {
null,
""
],
"Select": [
null,
""
],
"Error: URL may not be relative": [
null,
""
],
"Invalid exchange URL (%1$s)": [
null,
""
],
"The exchange is trusted by the wallet.": [
null,
""
],
"The exchange is audited by a trusted auditor.": [
null,
""
],
"Warning: The exchange is neither directly trusted nor audited by a trusted auditor. If you withdraw from this exchange, it will be trusted in the future.": [
null,
""
],
"Using exchange provider %1$s. The exchange provider will charge %2$s in fees.": [
null,
""
],
"Waiting for a response from %1$s %2$s": [
null,
""
],
"Information about fees will be available when an exchange provider is selected.": [
null,
""
],
"Your wallet (protocol version %1$s) might be outdated.%2$s The exchange has a higher, incompatible protocol version (%3$s).": [
null,
""
],
"The chosen exchange (protocol version %1$s might be outdated.%2$s The exchange has a lower, incompatible protocol version than your wallet (protocol version %3$s).": [
null,
""
],
"Accept fees and withdraw": [
null,
""
],
"Change Exchange Provider": [
null,
""
],
"Please select an exchange. You can review the details before after your selection.": [
null,
""
],
"Select %1$s": [
null,
""
],
"You are about to withdraw %1$s from your bank account into your wallet.": [
null,
""
],
"Oops, something went wrong. The wallet responded with error status (%1$s).": [
null,
""
],
"Checking URL, please wait ...": [
null,
""
],
"Can't parse amount: %1$s": [
null,
""
],
"Can't parse wire_types: %1$s": [
null,
""
],
"Fatal error: \"%1$s\".": [
null,
""
],
"Balance": [
null,
""
@ -912,6 +828,90 @@ strings['fr'] = {
null,
""
],
"Fatal error: \"%1$s\".": [
null,
""
],
"Select": [
null,
""
],
"Error: URL may not be relative": [
null,
""
],
"Invalid exchange URL (%1$s)": [
null,
""
],
"The exchange is trusted by the wallet.": [
null,
""
],
"The exchange is audited by a trusted auditor.": [
null,
""
],
"Warning: The exchange is neither directly trusted nor audited by a trusted auditor. If you withdraw from this exchange, it will be trusted in the future.": [
null,
""
],
"Using exchange provider %1$s. The exchange provider will charge %2$s in fees.": [
null,
""
],
"Waiting for a response from %1$s %2$s": [
null,
""
],
"Information about fees will be available when an exchange provider is selected.": [
null,
""
],
"Your wallet (protocol version %1$s) might be outdated.%2$s The exchange has a higher, incompatible protocol version (%3$s).": [
null,
""
],
"The chosen exchange (protocol version %1$s might be outdated.%2$s The exchange has a lower, incompatible protocol version than your wallet (protocol version %3$s).": [
null,
""
],
"Accept fees and withdraw": [
null,
""
],
"Change Exchange Provider": [
null,
""
],
"Please select an exchange. You can review the details before after your selection.": [
null,
""
],
"Select %1$s": [
null,
""
],
"You are about to withdraw %1$s from your bank account into your wallet.": [
null,
""
],
"Oops, something went wrong. The wallet responded with error status (%1$s).": [
null,
""
],
"Checking URL, please wait ...": [
null,
""
],
"Can't parse amount: %1$s": [
null,
""
],
"Can't parse wire_types: %1$s": [
null,
""
],
"Withdrawal fees:": [
null,
""
@ -1040,90 +1040,6 @@ strings['it'] = {
null,
""
],
"Select": [
null,
""
],
"Error: URL may not be relative": [
null,
""
],
"Invalid exchange URL (%1$s)": [
null,
""
],
"The exchange is trusted by the wallet.": [
null,
""
],
"The exchange is audited by a trusted auditor.": [
null,
""
],
"Warning: The exchange is neither directly trusted nor audited by a trusted auditor. If you withdraw from this exchange, it will be trusted in the future.": [
null,
""
],
"Using exchange provider %1$s. The exchange provider will charge %2$s in fees.": [
null,
""
],
"Waiting for a response from %1$s %2$s": [
null,
""
],
"Information about fees will be available when an exchange provider is selected.": [
null,
""
],
"Your wallet (protocol version %1$s) might be outdated.%2$s The exchange has a higher, incompatible protocol version (%3$s).": [
null,
""
],
"The chosen exchange (protocol version %1$s might be outdated.%2$s The exchange has a lower, incompatible protocol version than your wallet (protocol version %3$s).": [
null,
""
],
"Accept fees and withdraw": [
null,
""
],
"Change Exchange Provider": [
null,
""
],
"Please select an exchange. You can review the details before after your selection.": [
null,
""
],
"Select %1$s": [
null,
""
],
"You are about to withdraw %1$s from your bank account into your wallet.": [
null,
""
],
"Oops, something went wrong. The wallet responded with error status (%1$s).": [
null,
""
],
"Checking URL, please wait ...": [
null,
""
],
"Can't parse amount: %1$s": [
null,
""
],
"Can't parse wire_types: %1$s": [
null,
""
],
"Fatal error: \"%1$s\".": [
null,
""
],
"Balance": [
null,
""
@ -1228,6 +1144,90 @@ strings['it'] = {
null,
""
],
"Fatal error: \"%1$s\".": [
null,
""
],
"Select": [
null,
""
],
"Error: URL may not be relative": [
null,
""
],
"Invalid exchange URL (%1$s)": [
null,
""
],
"The exchange is trusted by the wallet.": [
null,
""
],
"The exchange is audited by a trusted auditor.": [
null,
""
],
"Warning: The exchange is neither directly trusted nor audited by a trusted auditor. If you withdraw from this exchange, it will be trusted in the future.": [
null,
""
],
"Using exchange provider %1$s. The exchange provider will charge %2$s in fees.": [
null,
""
],
"Waiting for a response from %1$s %2$s": [
null,
""
],
"Information about fees will be available when an exchange provider is selected.": [
null,
""
],
"Your wallet (protocol version %1$s) might be outdated.%2$s The exchange has a higher, incompatible protocol version (%3$s).": [
null,
""
],
"The chosen exchange (protocol version %1$s might be outdated.%2$s The exchange has a lower, incompatible protocol version than your wallet (protocol version %3$s).": [
null,
""
],
"Accept fees and withdraw": [
null,
""
],
"Change Exchange Provider": [
null,
""
],
"Please select an exchange. You can review the details before after your selection.": [
null,
""
],
"Select %1$s": [
null,
""
],
"You are about to withdraw %1$s from your bank account into your wallet.": [
null,
""
],
"Oops, something went wrong. The wallet responded with error status (%1$s).": [
null,
""
],
"Checking URL, please wait ...": [
null,
""
],
"Can't parse amount: %1$s": [
null,
""
],
"Can't parse wire_types: %1$s": [
null,
""
],
"Withdrawal fees:": [
null,
""
@ -1356,90 +1356,6 @@ strings['sv'] = {
null,
"Det totala priset är %1$s."
],
"Select": [
null,
"Välj"
],
"Error: URL may not be relative": [
null,
""
],
"Invalid exchange URL (%1$s)": [
null,
""
],
"The exchange is trusted by the wallet.": [
null,
"Tjänsteleverantörer i plånboken:"
],
"The exchange is audited by a trusted auditor.": [
null,
""
],
"Warning: The exchange is neither directly trusted nor audited by a trusted auditor. If you withdraw from this exchange, it will be trusted in the future.": [
null,
""
],
"Using exchange provider %1$s. The exchange provider will charge %2$s in fees.": [
null,
""
],
"Waiting for a response from %1$s %2$s": [
null,
""
],
"Information about fees will be available when an exchange provider is selected.": [
null,
""
],
"Your wallet (protocol version %1$s) might be outdated.%2$s The exchange has a higher, incompatible protocol version (%3$s).": [
null,
"tjänsteleverantörer plånboken"
],
"The chosen exchange (protocol version %1$s might be outdated.%2$s The exchange has a lower, incompatible protocol version than your wallet (protocol version %3$s).": [
null,
"tjänsteleverantörer plånboken"
],
"Accept fees and withdraw": [
null,
"Acceptera avgifter och utbetala"
],
"Change Exchange Provider": [
null,
"Ändra tjänsteleverantörer"
],
"Please select an exchange. You can review the details before after your selection.": [
null,
""
],
"Select %1$s": [
null,
"Välj %1$s"
],
"You are about to withdraw %1$s from your bank account into your wallet.": [
null,
"Du är på väg att ta ut\n %1$s från ditt bankkonto till din plånbok.\n"
],
"Oops, something went wrong. The wallet responded with error status (%1$s).": [
null,
"plånboken"
],
"Checking URL, please wait ...": [
null,
""
],
"Can't parse amount: %1$s": [
null,
""
],
"Can't parse wire_types: %1$s": [
null,
""
],
"Fatal error: \"%1$s\".": [
null,
""
],
"Balance": [
null,
"Balans"
@ -1544,6 +1460,90 @@ strings['sv'] = {
null,
"Avbryt"
],
"Fatal error: \"%1$s\".": [
null,
""
],
"Select": [
null,
"Välj"
],
"Error: URL may not be relative": [
null,
""
],
"Invalid exchange URL (%1$s)": [
null,
""
],
"The exchange is trusted by the wallet.": [
null,
"Tjänsteleverantörer i plånboken:"
],
"The exchange is audited by a trusted auditor.": [
null,
""
],
"Warning: The exchange is neither directly trusted nor audited by a trusted auditor. If you withdraw from this exchange, it will be trusted in the future.": [
null,
""
],
"Using exchange provider %1$s. The exchange provider will charge %2$s in fees.": [
null,
""
],
"Waiting for a response from %1$s %2$s": [
null,
""
],
"Information about fees will be available when an exchange provider is selected.": [
null,
""
],
"Your wallet (protocol version %1$s) might be outdated.%2$s The exchange has a higher, incompatible protocol version (%3$s).": [
null,
"tjänsteleverantörer plånboken"
],
"The chosen exchange (protocol version %1$s might be outdated.%2$s The exchange has a lower, incompatible protocol version than your wallet (protocol version %3$s).": [
null,
"tjänsteleverantörer plånboken"
],
"Accept fees and withdraw": [
null,
"Acceptera avgifter och utbetala"
],
"Change Exchange Provider": [
null,
"Ändra tjänsteleverantörer"
],
"Please select an exchange. You can review the details before after your selection.": [
null,
""
],
"Select %1$s": [
null,
"Välj %1$s"
],
"You are about to withdraw %1$s from your bank account into your wallet.": [
null,
"Du är på väg att ta ut\n %1$s från ditt bankkonto till din plånbok.\n"
],
"Oops, something went wrong. The wallet responded with error status (%1$s).": [
null,
"plånboken"
],
"Checking URL, please wait ...": [
null,
""
],
"Can't parse amount: %1$s": [
null,
""
],
"Can't parse wire_types: %1$s": [
null,
""
],
"Withdrawal fees:": [
null,
"Utbetalnings avgifter:"

View File

@ -37,28 +37,28 @@ msgstr ""
msgid "time (ms/op)"
msgstr ""
#: src/webex/pages/confirm-contract.tsx:78
#: src/webex/pages/pay.tsx:78
#, fuzzy, c-format
msgid "show more details"
msgstr "visa mer"
#: src/webex/pages/confirm-contract.tsx:92
#: src/webex/pages/pay.tsx:92
#, c-format
msgid "Accepted exchanges:"
msgstr "Accepterade tjänsteleverantörer:"
#: src/webex/pages/confirm-contract.tsx:97
#: src/webex/pages/pay.tsx:97
#, c-format
msgid "Exchanges in the wallet:"
msgstr "Tjänsteleverantörer i plånboken:"
#: src/webex/pages/confirm-contract.tsx:219
#: src/webex/pages/pay.tsx:219
#, c-format
msgid "You have insufficient funds of the requested currency in your wallet."
msgstr "plånboken"
#. tslint:disable-next-line:max-line-length
#: src/webex/pages/confirm-contract.tsx:221
#: src/webex/pages/pay.tsx:221
#, c-format
msgid ""
"You do not have any funds from an exchange that is accepted by this "
@ -66,17 +66,17 @@ msgid ""
"wallet."
msgstr "plånboken"
#: src/webex/pages/confirm-contract.tsx:322
#: src/webex/pages/pay.tsx:322
#, c-format
msgid "Confirm payment"
msgstr "Godkän betalning"
#: src/webex/pages/confirm-contract.tsx:332
#: src/webex/pages/pay.tsx:332
#, c-format
msgid "Submitting payment"
msgstr "Bekräftar betalning"
#: src/webex/pages/confirm-contract.tsx:343
#: src/webex/pages/pay.tsx:343
#, c-format
msgid ""
"You already paid for this, clicking \"Confirm payment\" will not cost money "
@ -85,165 +85,41 @@ msgstr ""
"Du har redan betalat för det här, om du trycker \"Godkän betalning\" "
"debiteras du inte igen"
#: src/webex/pages/confirm-contract.tsx:357
#: src/webex/pages/pay.tsx:357
#, fuzzy, c-format
msgid "Aborting payment ..."
msgstr "Bekräftar betalning"
#: src/webex/pages/confirm-contract.tsx:359
#: src/webex/pages/pay.tsx:359
#, c-format
msgid "Payment aborted!"
msgstr ""
#: src/webex/pages/confirm-contract.tsx:362
#: src/webex/pages/pay.tsx:362
#, c-format
msgid "Retry Payment"
msgstr ""
#: src/webex/pages/confirm-contract.tsx:365
#: src/webex/pages/pay.tsx:365
#, fuzzy, c-format
msgid "Abort Payment"
msgstr "Godkän betalning"
#: src/webex/pages/confirm-contract.tsx:374
#: src/webex/pages/pay.tsx:374
#, fuzzy, c-format
msgid "The merchant %1$s offers you to purchase:"
msgstr "Säljaren %1$s erbjuder följande:"
#: src/webex/pages/confirm-contract.tsx:383
#: src/webex/pages/pay.tsx:383
#, fuzzy, c-format
msgid "The total price is %1$s (plus %2$s fees)."
msgstr "Det totala priset är %1$s (plus %2$s avgifter).\n"
#: src/webex/pages/confirm-contract.tsx:387
#: src/webex/pages/pay.tsx:387
#, fuzzy, c-format
msgid "The total price is %1$s."
msgstr "Det totala priset är %1$s."
#: src/webex/pages/confirm-create-reserve.tsx:128
#, c-format
msgid "Select"
msgstr "Välj"
#: src/webex/pages/confirm-create-reserve.tsx:145
#, c-format
msgid "Error: URL may not be relative"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:160
#, c-format
msgid "Invalid exchange URL (%1$s)"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:210
#, fuzzy, c-format
msgid "The exchange is trusted by the wallet."
msgstr "Tjänsteleverantörer i plånboken:"
#: src/webex/pages/confirm-create-reserve.tsx:216
#, c-format
msgid "The exchange is audited by a trusted auditor."
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:222
#, c-format
msgid ""
"Warning: The exchange is neither directly trusted nor audited by a trusted "
"auditor. If you withdraw from this exchange, it will be trusted in the "
"future."
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:231
#, c-format
msgid ""
"Using exchange provider %1$s. The exchange provider will charge %2$s in fees."
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:243
#, c-format
msgid "Waiting for a response from %1$s %2$s"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:260
#, c-format
msgid ""
"Information about fees will be available when an exchange provider is "
"selected."
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:279
#, fuzzy, c-format
msgid ""
"Your wallet (protocol version %1$s) might be outdated.%2$s The exchange has "
"a higher, incompatible protocol version (%3$s)."
msgstr "tjänsteleverantörer plånboken"
#: src/webex/pages/confirm-create-reserve.tsx:290
#, fuzzy, c-format
msgid ""
"The chosen exchange (protocol version %1$s might be outdated.%2$s The "
"exchange has a lower, incompatible protocol version than your wallet "
"(protocol version %3$s)."
msgstr "tjänsteleverantörer plånboken"
#: src/webex/pages/confirm-create-reserve.tsx:309
#, c-format
msgid "Accept fees and withdraw"
msgstr "Acceptera avgifter och utbetala"
#: src/webex/pages/confirm-create-reserve.tsx:314
#, c-format
msgid "Change Exchange Provider"
msgstr "Ändra tjänsteleverantörer"
#: src/webex/pages/confirm-create-reserve.tsx:335
#, c-format
msgid ""
"Please select an exchange. You can review the details before after your "
"selection."
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:341
#: src/webex/pages/confirm-create-reserve.tsx:353
#, fuzzy, c-format
msgid "Select %1$s"
msgstr "Välj %1$s"
#: src/webex/pages/confirm-create-reserve.tsx:370
#, fuzzy, c-format
msgid "You are about to withdraw %1$s from your bank account into your wallet."
msgstr ""
"Du är på väg att ta ut\n"
" %1$s från ditt bankkonto till din plånbok.\n"
#: src/webex/pages/confirm-create-reserve.tsx:459
#, fuzzy, c-format
msgid ""
"Oops, something went wrong. The wallet responded with error status (%1$s)."
msgstr "plånboken"
#: src/webex/pages/confirm-create-reserve.tsx:468
#, c-format
msgid "Checking URL, please wait ..."
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:482
#, c-format
msgid "Can't parse amount: %1$s"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:489
#, c-format
msgid "Can't parse wire_types: %1$s"
msgstr ""
#. #-#-#-#-# - (PACKAGE VERSION) #-#-#-#-#
#. TODO:generic error reporting function or component.
#: src/webex/pages/confirm-create-reserve.tsx:519 src/webex/pages/tip.tsx:180
#, c-format
msgid "Fatal error: \"%1$s\"."
msgstr ""
#: src/webex/pages/popup.tsx:165
#, c-format
msgid "Balance"
@ -376,6 +252,129 @@ msgstr "Bekräfta"
msgid "Cancel"
msgstr "Avbryt"
#. #-#-#-#-# - (PACKAGE VERSION) #-#-#-#-#
#. TODO:generic error reporting function or component.
#: src/webex/pages/tip.tsx:180 src/webex/pages/withdraw.tsx:519
#, c-format
msgid "Fatal error: \"%1$s\"."
msgstr ""
#: src/webex/pages/withdraw.tsx:128
#, c-format
msgid "Select"
msgstr "Välj"
#: src/webex/pages/withdraw.tsx:145
#, c-format
msgid "Error: URL may not be relative"
msgstr ""
#: src/webex/pages/withdraw.tsx:160
#, c-format
msgid "Invalid exchange URL (%1$s)"
msgstr ""
#: src/webex/pages/withdraw.tsx:210
#, fuzzy, c-format
msgid "The exchange is trusted by the wallet."
msgstr "Tjänsteleverantörer i plånboken:"
#: src/webex/pages/withdraw.tsx:216
#, c-format
msgid "The exchange is audited by a trusted auditor."
msgstr ""
#: src/webex/pages/withdraw.tsx:222
#, c-format
msgid ""
"Warning: The exchange is neither directly trusted nor audited by a trusted "
"auditor. If you withdraw from this exchange, it will be trusted in the "
"future."
msgstr ""
#: src/webex/pages/withdraw.tsx:231
#, c-format
msgid ""
"Using exchange provider %1$s. The exchange provider will charge %2$s in fees."
msgstr ""
#: src/webex/pages/withdraw.tsx:243
#, c-format
msgid "Waiting for a response from %1$s %2$s"
msgstr ""
#: src/webex/pages/withdraw.tsx:260
#, c-format
msgid ""
"Information about fees will be available when an exchange provider is "
"selected."
msgstr ""
#: src/webex/pages/withdraw.tsx:279
#, fuzzy, c-format
msgid ""
"Your wallet (protocol version %1$s) might be outdated.%2$s The exchange has "
"a higher, incompatible protocol version (%3$s)."
msgstr "tjänsteleverantörer plånboken"
#: src/webex/pages/withdraw.tsx:290
#, fuzzy, c-format
msgid ""
"The chosen exchange (protocol version %1$s might be outdated.%2$s The "
"exchange has a lower, incompatible protocol version than your wallet "
"(protocol version %3$s)."
msgstr "tjänsteleverantörer plånboken"
#: src/webex/pages/withdraw.tsx:309
#, c-format
msgid "Accept fees and withdraw"
msgstr "Acceptera avgifter och utbetala"
#: src/webex/pages/withdraw.tsx:314
#, c-format
msgid "Change Exchange Provider"
msgstr "Ändra tjänsteleverantörer"
#: src/webex/pages/withdraw.tsx:335
#, c-format
msgid ""
"Please select an exchange. You can review the details before after your "
"selection."
msgstr ""
#: src/webex/pages/withdraw.tsx:341 src/webex/pages/withdraw.tsx:353
#, fuzzy, c-format
msgid "Select %1$s"
msgstr "Välj %1$s"
#: src/webex/pages/withdraw.tsx:370
#, fuzzy, c-format
msgid "You are about to withdraw %1$s from your bank account into your wallet."
msgstr ""
"Du är på väg att ta ut\n"
" %1$s från ditt bankkonto till din plånbok.\n"
#: src/webex/pages/withdraw.tsx:459
#, fuzzy, c-format
msgid ""
"Oops, something went wrong. The wallet responded with error status (%1$s)."
msgstr "plånboken"
#: src/webex/pages/withdraw.tsx:468
#, c-format
msgid "Checking URL, please wait ..."
msgstr ""
#: src/webex/pages/withdraw.tsx:482
#, c-format
msgid "Can't parse amount: %1$s"
msgstr ""
#: src/webex/pages/withdraw.tsx:489
#, c-format
msgid "Can't parse wire_types: %1$s"
msgstr ""
#: src/webex/renderHtml.tsx:225
#, c-format
msgid "Withdrawal fees:"

View File

@ -37,28 +37,28 @@ msgstr ""
msgid "time (ms/op)"
msgstr ""
#: src/webex/pages/confirm-contract.tsx:78
#: src/webex/pages/pay.tsx:78
#, c-format
msgid "show more details"
msgstr ""
#: src/webex/pages/confirm-contract.tsx:92
#: src/webex/pages/pay.tsx:92
#, c-format
msgid "Accepted exchanges:"
msgstr ""
#: src/webex/pages/confirm-contract.tsx:97
#: src/webex/pages/pay.tsx:97
#, c-format
msgid "Exchanges in the wallet:"
msgstr ""
#: src/webex/pages/confirm-contract.tsx:219
#: src/webex/pages/pay.tsx:219
#, 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:221
#: src/webex/pages/pay.tsx:221
#, c-format
msgid ""
"You do not have any funds from an exchange that is accepted by this "
@ -66,180 +66,58 @@ msgid ""
"wallet."
msgstr ""
#: src/webex/pages/confirm-contract.tsx:322
#: src/webex/pages/pay.tsx:322
#, c-format
msgid "Confirm payment"
msgstr ""
#: src/webex/pages/confirm-contract.tsx:332
#: src/webex/pages/pay.tsx:332
#, c-format
msgid "Submitting payment"
msgstr ""
#: src/webex/pages/confirm-contract.tsx:343
#: src/webex/pages/pay.tsx:343
#, c-format
msgid ""
"You already paid for this, clicking \"Confirm payment\" will not cost money "
"again."
msgstr ""
#: src/webex/pages/confirm-contract.tsx:357
#: src/webex/pages/pay.tsx:357
#, c-format
msgid "Aborting payment ..."
msgstr ""
#: src/webex/pages/confirm-contract.tsx:359
#: src/webex/pages/pay.tsx:359
#, c-format
msgid "Payment aborted!"
msgstr ""
#: src/webex/pages/confirm-contract.tsx:362
#: src/webex/pages/pay.tsx:362
#, c-format
msgid "Retry Payment"
msgstr ""
#: src/webex/pages/confirm-contract.tsx:365
#: src/webex/pages/pay.tsx:365
#, c-format
msgid "Abort Payment"
msgstr ""
#: src/webex/pages/confirm-contract.tsx:374
#: src/webex/pages/pay.tsx:374
#, c-format
msgid "The merchant %1$s offers you to purchase:"
msgstr ""
#: src/webex/pages/confirm-contract.tsx:383
#: src/webex/pages/pay.tsx:383
#, c-format
msgid "The total price is %1$s (plus %2$s fees)."
msgstr ""
#: src/webex/pages/confirm-contract.tsx:387
#: src/webex/pages/pay.tsx:387
#, c-format
msgid "The total price is %1$s."
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:128
#, c-format
msgid "Select"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:145
#, c-format
msgid "Error: URL may not be relative"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:160
#, c-format
msgid "Invalid exchange URL (%1$s)"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:210
#, c-format
msgid "The exchange is trusted by the wallet."
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:216
#, c-format
msgid "The exchange is audited by a trusted auditor."
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:222
#, c-format
msgid ""
"Warning: The exchange is neither directly trusted nor audited by a trusted "
"auditor. If you withdraw from this exchange, it will be trusted in the "
"future."
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:231
#, c-format
msgid ""
"Using exchange provider %1$s. The exchange provider will charge %2$s in fees."
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:243
#, c-format
msgid "Waiting for a response from %1$s %2$s"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:260
#, c-format
msgid ""
"Information about fees will be available when an exchange provider is "
"selected."
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:279
#, c-format
msgid ""
"Your wallet (protocol version %1$s) might be outdated.%2$s The exchange has "
"a higher, incompatible protocol version (%3$s)."
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:290
#, c-format
msgid ""
"The chosen exchange (protocol version %1$s might be outdated.%2$s The "
"exchange has a lower, incompatible protocol version than your wallet "
"(protocol version %3$s)."
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:309
#, c-format
msgid "Accept fees and withdraw"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:314
#, c-format
msgid "Change Exchange Provider"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:335
#, c-format
msgid ""
"Please select an exchange. You can review the details before after your "
"selection."
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:341
#: src/webex/pages/confirm-create-reserve.tsx:353
#, c-format
msgid "Select %1$s"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:370
#, 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:459
#, c-format
msgid ""
"Oops, something went wrong. The wallet responded with error status (%1$s)."
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:468
#, c-format
msgid "Checking URL, please wait ..."
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:482
#, c-format
msgid "Can't parse amount: %1$s"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:489
#, c-format
msgid "Can't parse wire_types: %1$s"
msgstr ""
#. #-#-#-#-# - (PACKAGE VERSION) #-#-#-#-#
#. TODO:generic error reporting function or component.
#: src/webex/pages/confirm-create-reserve.tsx:519 src/webex/pages/tip.tsx:180
#, c-format
msgid "Fatal error: \"%1$s\"."
msgstr ""
#: src/webex/pages/popup.tsx:165
#, c-format
msgid "Balance"
@ -370,6 +248,127 @@ msgstr ""
msgid "Cancel"
msgstr ""
#. #-#-#-#-# - (PACKAGE VERSION) #-#-#-#-#
#. TODO:generic error reporting function or component.
#: src/webex/pages/tip.tsx:180 src/webex/pages/withdraw.tsx:519
#, c-format
msgid "Fatal error: \"%1$s\"."
msgstr ""
#: src/webex/pages/withdraw.tsx:128
#, c-format
msgid "Select"
msgstr ""
#: src/webex/pages/withdraw.tsx:145
#, c-format
msgid "Error: URL may not be relative"
msgstr ""
#: src/webex/pages/withdraw.tsx:160
#, c-format
msgid "Invalid exchange URL (%1$s)"
msgstr ""
#: src/webex/pages/withdraw.tsx:210
#, c-format
msgid "The exchange is trusted by the wallet."
msgstr ""
#: src/webex/pages/withdraw.tsx:216
#, c-format
msgid "The exchange is audited by a trusted auditor."
msgstr ""
#: src/webex/pages/withdraw.tsx:222
#, c-format
msgid ""
"Warning: The exchange is neither directly trusted nor audited by a trusted "
"auditor. If you withdraw from this exchange, it will be trusted in the "
"future."
msgstr ""
#: src/webex/pages/withdraw.tsx:231
#, c-format
msgid ""
"Using exchange provider %1$s. The exchange provider will charge %2$s in fees."
msgstr ""
#: src/webex/pages/withdraw.tsx:243
#, c-format
msgid "Waiting for a response from %1$s %2$s"
msgstr ""
#: src/webex/pages/withdraw.tsx:260
#, c-format
msgid ""
"Information about fees will be available when an exchange provider is "
"selected."
msgstr ""
#: src/webex/pages/withdraw.tsx:279
#, c-format
msgid ""
"Your wallet (protocol version %1$s) might be outdated.%2$s The exchange has "
"a higher, incompatible protocol version (%3$s)."
msgstr ""
#: src/webex/pages/withdraw.tsx:290
#, c-format
msgid ""
"The chosen exchange (protocol version %1$s might be outdated.%2$s The "
"exchange has a lower, incompatible protocol version than your wallet "
"(protocol version %3$s)."
msgstr ""
#: src/webex/pages/withdraw.tsx:309
#, c-format
msgid "Accept fees and withdraw"
msgstr ""
#: src/webex/pages/withdraw.tsx:314
#, c-format
msgid "Change Exchange Provider"
msgstr ""
#: src/webex/pages/withdraw.tsx:335
#, c-format
msgid ""
"Please select an exchange. You can review the details before after your "
"selection."
msgstr ""
#: src/webex/pages/withdraw.tsx:341 src/webex/pages/withdraw.tsx:353
#, c-format
msgid "Select %1$s"
msgstr ""
#: src/webex/pages/withdraw.tsx:370
#, c-format
msgid "You are about to withdraw %1$s from your bank account into your wallet."
msgstr ""
#: src/webex/pages/withdraw.tsx:459
#, c-format
msgid ""
"Oops, something went wrong. The wallet responded with error status (%1$s)."
msgstr ""
#: src/webex/pages/withdraw.tsx:468
#, c-format
msgid "Checking URL, please wait ..."
msgstr ""
#: src/webex/pages/withdraw.tsx:482
#, c-format
msgid "Can't parse amount: %1$s"
msgstr ""
#: src/webex/pages/withdraw.tsx:489
#, c-format
msgid "Can't parse wire_types: %1$s"
msgstr ""
#: src/webex/renderHtml.tsx:225
#, c-format
msgid "Withdrawal fees:"

View File

@ -105,6 +105,8 @@ import {
WalletBalanceEntry,
PreparePayResult,
DownloadedWithdrawInfo,
WithdrawDetails,
AcceptWithdrawalResponse,
} from "./walletTypes";
import { openPromise } from "./promiseUtils";
import { parsePayUri, parseWithdrawUri } from "./taleruri";
@ -717,6 +719,12 @@ export class Wallet {
return t;
}
getNextUrl(contractTerms: ContractTerms): string {
const fu = new URI(contractTerms.fulfillment_url);
fu.addSearch("order_id", contractTerms.order_id);
return fu.href();
}
async preparePay(url: string): Promise<PreparePayResult> {
const uriResult = parsePayUri(url);
@ -760,17 +768,20 @@ export class Wallet {
uriResult.sessionId,
);
return {
status: "session-replayed",
status: "paid",
contractTerms: existingPayment.contractTerms,
nextUrl: this.getNextUrl(existingPayment.contractTerms),
};
}
}
if (checkResult.status === "paid") {
const nextUrl = this.getNextUrl(proposal.contractTerms);
return {
status: "paid",
contractTerms: proposal.contractTerms,
proposalId: proposal.id!,
nextUrl,
};
}
if (checkResult.status === "insufficient-balance") {
@ -912,14 +923,6 @@ export class Wallet {
modifiedCoins.push(c);
}
const fu = new URI(purchase.contractTerms.fulfillment_url);
fu.addSearch("order_id", purchase.contractTerms.order_id);
if (merchantResp.session_sig) {
purchase.lastSessionSig = merchantResp.session_sig;
purchase.lastSessionId = sessionId;
fu.addSearch("session_sig", merchantResp.session_sig);
}
await this.q()
.putAll(Stores.coins, modifiedCoins)
.put(Stores.purchases, purchase)
@ -928,7 +931,7 @@ export class Wallet {
this.refresh(c.coin_pub);
}
const nextUrl = fu.href();
const nextUrl = this.getNextUrl(purchase.contractTerms);
this.cachedNextUrl[purchase.contractTerms.fulfillment_url] = {
nextUrl,
lastSessionId: sessionId,
@ -1150,6 +1153,54 @@ export class Wallet {
return t;
}
private async sendReserveInfoToBank(reservePub: string) {
const reserve = await this.q().get<ReserveRecord>(
Stores.reserves,
reservePub,
);
if (!reserve) {
throw Error("reserve not in db");
}
const bankStatusUrl = reserve.bankWithdrawStatusUrl;
if (!bankStatusUrl) {
throw Error("reserve not confirmed yet, and no status URL available.");
}
const now = new Date().getTime();
let status;
try {
const statusResp = await this.http.get(bankStatusUrl);
status = WithdrawOperationStatusResponse.checked(statusResp.responseJson);
} catch (e) {
console.log("bank error response", e);
throw e;
}
if (status.transfer_done) {
await this.q().mutate(Stores.reserves, reservePub, r => {
r.timestamp_confirmed = now;
return r;
});
} else if (reserve.timestamp_reserve_info_posted === 0) {
try {
if (!status.selection_done) {
const bankResp = await this.http.postJson(bankStatusUrl, {
reserve_pub: reservePub,
selected_exchange: reserve.exchangeWire,
});
}
} catch (e) {
console.log("bank error response", e);
throw e;
}
await this.q().mutate(Stores.reserves, reservePub, r => {
r.timestamp_reserve_info_posted = now;
return r;
});
}
}
/**
* First fetch information requred to withdraw from the reserve,
* then deplete the reserve, withdrawing coins until it is empty.
@ -1192,42 +1243,11 @@ export class Wallet {
);
}
maxTimeout = 2000;
const now = new Date().getTime();
let status;
try {
const statusResp = await this.http.get(bankStatusUrl);
status = WithdrawOperationStatusResponse.checked(
statusResp.responseJson,
);
} catch (e) {
console.log("bank error response", e);
throw e;
}
if (status.transfer_done) {
await this.q().mutate(Stores.reserves, reservePub, r => {
r.timestamp_confirmed = now;
return r;
});
} else if (reserve.timestamp_reserve_info_posted === 0) {
try {
if (!status.selection_done) {
const bankResp = await this.http.postJson(bankStatusUrl, {
reserve_pub: reservePub,
selected_exchange: reserve.exchangeWire,
});
}
} catch (e) {
console.log("bank error response", e);
throw e;
}
await this.q().mutate(Stores.reserves, reservePub, r => {
r.timestamp_reserve_info_posted = now;
return r;
});
/* This path is only taken if the wallet crashed after a withdraw was accepted,
* and before the information could be sent to the bank. */
await this.sendReserveInfoToBank(reservePub);
throw Error("waiting for reserve to be confirmed");
}
}
const updatedReserve = await this.updateReserve(reservePub);
await this.depleteReserve(updatedReserve);
@ -1836,6 +1856,24 @@ export class Wallet {
return { isTrusted, isAudited };
}
async getWithdrawDetails(
talerPayUri: string,
maybeSelectedExchange?: string,
): Promise<WithdrawDetails> {
const info = await this.downloadWithdrawInfo(talerPayUri);
let rci: ReserveCreationInfo | undefined = undefined;
if (maybeSelectedExchange) {
rci = await this.getReserveCreationInfo(
maybeSelectedExchange,
info.amount,
);
}
return {
withdrawInfo: info,
reserveCreationInfo: rci,
};
}
async getReserveCreationInfo(
baseUrl: string,
amount: AmountJson,
@ -3514,16 +3552,6 @@ export class Wallet {
);
}
/**
* Synchronously get the paid URL for a resource from the plain fulfillment
* URL. Returns undefined if the fulfillment URL is not a resource that was
* payed for, or if it is not cached anymore. Use the asynchronous
* queryPaymentByFulfillmentUrl to avoid false negatives.
*/
getNextUrlFromResourceUrl(resourceUrl: string): NextUrlResult | undefined {
return this.cachedNextUrl[resourceUrl];
}
/**
* Remove unreferenced / expired data from the wallet's database
* based on the current system time.
@ -3557,10 +3585,10 @@ export class Wallet {
};
}
async createReserveFromWithdrawUrl(
async acceptWithdrawal(
talerWithdrawUri: string,
selectedExchange: string,
): Promise<{ reservePub: string; confirmTransferUrl?: string }> {
): Promise<AcceptWithdrawalResponse> {
const withdrawInfo = await this.downloadWithdrawInfo(talerWithdrawUri);
const exchangeWire = await this.getExchangePaytoUri(
selectedExchange,
@ -3573,6 +3601,7 @@ export class Wallet {
senderWire: withdrawInfo.senderWire,
exchangeWire: exchangeWire,
});
await this.sendReserveInfoToBank(reserve.reservePub);
return {
reservePub: reserve.reservePub,
confirmTransferUrl: withdrawInfo.confirmTransferUrl,

View File

@ -37,12 +37,7 @@ import {
ExchangeWireFeesRecord,
TipRecord,
} from "./dbTypes";
import {
CoinPaySig,
ContractTerms,
PayReq,
} from "./talerTypes";
import { CoinPaySig, ContractTerms, PayReq } from "./talerTypes";
/**
* Response for the create reserve request to the wallet.
@ -69,7 +64,6 @@ export class CreateReserveResponse {
static checked: (obj: any) => CreateReserveResponse;
}
/**
* Information about what will happen when creating a reserve.
*
@ -138,7 +132,7 @@ export interface ReserveCreationInfo {
*
* Older exchanges don't return version information.
*/
versionMatch: LibtoolVersion.VersionMatchResult|undefined;
versionMatch: LibtoolVersion.VersionMatchResult | undefined;
/**
* Libtool-style version string for the exchange or "unknown"
@ -152,6 +146,10 @@ export interface ReserveCreationInfo {
walletVersion: string;
}
export interface WithdrawDetails {
withdrawInfo: DownloadedWithdrawInfo;
reserveCreationInfo: ReserveCreationInfo | undefined;
}
/**
* Mapping from currency/exchange to detailed balance
@ -169,7 +167,6 @@ export interface WalletBalance {
byCurrency: { [currency: string]: WalletBalanceEntry };
}
/**
* Detailed wallet balance for a particular currency.
*/
@ -192,7 +189,6 @@ export interface WalletBalanceEntry {
paybackAmount: AmountJson;
}
/**
* Coins used for a payment, with signatures authorizing the payment and the
* coins with remaining value updated to accomodate for a payment.
@ -203,7 +199,6 @@ export interface PayCoinInfo {
sigs: CoinPaySig[];
}
/**
* Listener for notifications from the wallet.
*/
@ -214,15 +209,17 @@ export interface Notifier {
notify(): void;
}
/**
* For terseness.
*/
export function mkAmount(value: number, fraction: number, currency: string): AmountJson {
return {value, fraction, currency};
export function mkAmount(
value: number,
fraction: number,
currency: string,
): AmountJson {
return { value, fraction, currency };
}
/**
* Possible results for checkPay.
*/
@ -231,7 +228,6 @@ export interface CheckPayResult {
coinSelection?: CoinSelectionResult;
}
/**
* Result for confirmPay
*/
@ -239,7 +235,6 @@ export interface ConfirmPayResult {
nextUrl: string;
}
/**
* Activity history record.
*/
@ -266,7 +261,6 @@ export interface HistoryRecord {
detail: any;
}
/**
* Query payment response when the payment was found.
*/
@ -274,7 +268,6 @@ export interface QueryPaymentNotFound {
found: false;
}
/**
* Query payment response when the payment wasn't found.
*/
@ -288,7 +281,6 @@ export interface QueryPaymentFound {
proposalId: number;
}
/**
* Information about all sender wire details known to the wallet,
* as well as exchanges that accept these wire types.
@ -306,7 +298,6 @@ export interface SenderWireInfos {
senderWires: string[];
}
/**
* Request to mark a reserve as confirmed.
*/
@ -351,7 +342,6 @@ export class CreateReserveRequest {
static checked: (obj: any) => CreateReserveRequest;
}
/**
* Request to mark a reserve as confirmed.
*/
@ -371,7 +361,6 @@ export class ConfirmReserveRequest {
static checked: (obj: any) => ConfirmReserveRequest;
}
/**
* Wire coins to the user's own bank account.
*/
@ -403,7 +392,6 @@ export class ReturnCoinsRequest {
static checked: (obj: any) => ReturnCoinsRequest;
}
/**
* Result of selecting coins, contains the exchange, and selected
* coins with their denomination.
@ -418,7 +406,6 @@ export interface CoinSelectionResult {
totalAmount: AmountJson;
}
/**
* Named tuple of coin and denomination.
*/
@ -446,7 +433,6 @@ export interface TipStatus {
tipRecord?: TipRecord;
}
/**
* Badge that shows activity for the wallet.
*/
@ -477,7 +463,6 @@ export interface BenchmarkResult {
repetitions: number;
}
/**
* Cached next URL for a particular session id.
*/
@ -486,14 +471,38 @@ export interface NextUrlResult {
lastSessionId: string | undefined;
}
export interface PreparePayResult {
status: "paid" | "session-replayed" | "insufficient-balance" | "payment-possible" | "error";
contractTerms?: ContractTerms;
error?: string;
export type PreparePayResult =
| PreparePayResultError
| PreparePayResultInsufficientBalance
| PreparePayResultPaid
| PreparePayResultPaymentPossible;
export interface PreparePayResultPaymentPossible {
status: "payment-possible";
proposalId?: number;
contractTerms?: ContractTerms;
totalFees?: AmountJson;
}
export interface PreparePayResultInsufficientBalance {
status: "insufficient-balance";
proposalId?: number;
contractTerms?: ContractTerms;
totalFees?: AmountJson;
}
export interface PreparePayResultError {
status: "error";
error: string;
}
export interface PreparePayResultPaid {
status: "paid";
proposalId?: number;
contractTerms?: ContractTerms;
nextUrl: string;
}
export interface DownloadedWithdrawInfo {
selectionDone: boolean;
transferDone: boolean;
@ -504,3 +513,8 @@ export interface DownloadedWithdrawInfo {
wireTypes: string[];
extractedStatusUrl: string;
}
export interface AcceptWithdrawalResponse {
reservePub: string;
confirmTransferUrl?: string;
}

View File

@ -32,12 +32,12 @@ import { UpgradeResponse } from "./wxApi";
* Message type information.
*/
export interface MessageMap {
"balances": {
request: { };
balances: {
request: {};
response: walletTypes.WalletBalance;
};
"dump-db": {
request: { };
request: {};
response: any;
};
"import-db": {
@ -46,18 +46,18 @@ export interface MessageMap {
};
response: void;
};
"ping": {
request: { };
ping: {
request: {};
response: void;
};
"reset-db": {
request: { };
request: {};
response: void;
};
"create-reserve": {
request: {
amount: AmountJson;
exchange: string
exchange: string;
};
response: void;
};
@ -70,11 +70,11 @@ export interface MessageMap {
response: walletTypes.ConfirmPayResult;
};
"check-pay": {
request: { proposalId: number; };
request: { proposalId: number };
response: walletTypes.CheckPayResult;
};
"query-payment": {
request: { };
request: {};
response: dbTypes.PurchaseRecord;
};
"exchange-info": {
@ -90,11 +90,11 @@ export interface MessageMap {
response: string;
};
"reserve-creation-info": {
request: { baseUrl: string, amount: AmountJson };
request: { baseUrl: string; amount: AmountJson };
response: walletTypes.ReserveCreationInfo;
};
"get-history": {
request: { };
request: {};
response: walletTypes.HistoryRecord[];
};
"get-proposal": {
@ -110,7 +110,7 @@ export interface MessageMap {
response: any;
};
"get-currencies": {
request: { };
request: {};
response: dbTypes.CurrencyRecord[];
};
"update-currency": {
@ -118,7 +118,7 @@ export interface MessageMap {
response: void;
};
"get-exchanges": {
request: { };
request: {};
response: dbTypes.ExchangeRecord[];
};
"get-reserves": {
@ -126,7 +126,7 @@ export interface MessageMap {
response: dbTypes.ReserveRecord[];
};
"get-payback-reserves": {
request: { };
request: {};
response: dbTypes.ReserveRecord[];
};
"withdraw-payback-reserve": {
@ -146,15 +146,15 @@ export interface MessageMap {
response: void;
};
"check-upgrade": {
request: { };
request: {};
response: UpgradeResponse;
};
"get-sender-wire-infos": {
request: { };
request: {};
response: walletTypes.SenderWireInfos;
};
"return-coins": {
request: { };
request: {};
response: void;
};
"log-and-display-error": {
@ -182,7 +182,7 @@ export interface MessageMap {
response: walletTypes.TipStatus;
};
"clear-notification": {
request: { };
request: {};
response: void;
};
"taler-pay": {
@ -194,23 +194,36 @@ export interface MessageMap {
response: number;
};
"submit-pay": {
request: { contractTermsHash: string, sessionId: string | undefined };
request: { contractTermsHash: string; sessionId: string | undefined };
response: walletTypes.ConfirmPayResult;
};
"accept-refund": {
request: { refundUrl: string }
request: { refundUrl: string };
response: string;
};
"abort-failed-payment": {
request: { contractTermsHash: string }
request: { contractTermsHash: string };
response: void;
};
"benchmark-crypto": {
request: { repetitions: number }
request: { repetitions: number };
response: walletTypes.BenchmarkResult;
};
"get-withdraw-details": {
request: { talerWithdrawUri: string; maybeSelectedExchange: string | undefined };
response: walletTypes.WithdrawDetails;
};
"accept-withdrawal": {
request: { talerWithdrawUri: string; selectedExchange: string };
response: walletTypes.AcceptWithdrawalResponse;
};
"prepare-pay": {
request: { talerPayUri: string };
response: walletTypes.PreparePayResult;
};
}
/**
* String literal types for messages.
*/
@ -219,14 +232,19 @@ export type MessageType = keyof MessageMap;
/**
* Make a request whose details match the request type.
*/
export function makeRequest<T extends MessageType>(type: T, details: MessageMap[T]["request"]) {
export function makeRequest<T extends MessageType>(
type: T,
details: MessageMap[T]["request"],
) {
return { type, details };
}
/**
* Make a response that matches the request type.
*/
export function makeResponse<T extends MessageType>(type: T, response: MessageMap[T]["response"]) {
export function makeResponse<T extends MessageType>(
type: T,
response: MessageMap[T]["response"],
) {
return response;
}

View File

@ -1,417 +0,0 @@
/*
This file is part of TALER
(C) 2015 GNUnet e.V.
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3, or (at your option) any later version.
TALER is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
/**
* Page shown to the user to confirm entering
* a contract.
*/
/**
* Imports.
*/
import * as i18n from "../../i18n";
import { runOnceWhenReady } from "./common";
import {
ExchangeRecord,
ProposalDownloadRecord,
} from "../../dbTypes";
import { ContractTerms } from "../../talerTypes";
import {
CheckPayResult,
} from "../../walletTypes";
import { renderAmount } from "../renderHtml";
import * as wxApi from "../wxApi";
import * as React from "react";
import * as ReactDOM from "react-dom";
import URI = require("urijs");
import { WalletApiError } from "../wxApi";
import * as Amounts from "../../amounts";
interface DetailState {
collapsed: boolean;
}
interface DetailProps {
contractTerms: ContractTerms;
collapsed: boolean;
exchanges: ExchangeRecord[] | undefined;
}
class Details extends React.Component<DetailProps, DetailState> {
constructor(props: DetailProps) {
super(props);
console.log("new Details component created");
this.state = {
collapsed: props.collapsed,
};
console.log("initial state:", this.state);
}
render() {
if (this.state.collapsed) {
return (
<div>
<button className="linky"
onClick={() => { this.setState({collapsed: false} as any); }}>
<i18n.Translate wrap="span">
show more details
</i18n.Translate>
</button>
</div>
);
} else {
return (
<div>
<button className="linky"
onClick={() => this.setState({collapsed: true} as any)}>
i18n.str`show fewer details`
</button>
<div>
{i18n.str`Accepted exchanges:`}
<ul>
{this.props.contractTerms.exchanges.map(
(e) => <li>{`${e.url}: ${e.master_pub}`}</li>)}
</ul>
{i18n.str`Exchanges in the wallet:`}
<ul>
{(this.props.exchanges || []).map(
(e: ExchangeRecord) =>
<li>{`${e.baseUrl}: ${e.masterPublicKey}`}</li>)}
</ul>
</div>
</div>);
}
}
}
interface ContractPromptProps {
proposalId?: number;
contractUrl?: string;
sessionId?: string;
resourceUrl?: string;
}
interface ContractPromptState {
proposalId: number | undefined;
proposal: ProposalDownloadRecord | undefined;
checkPayError: string | undefined;
confirmPayError: object | undefined;
payDisabled: boolean;
alreadyPaid: boolean;
exchanges: ExchangeRecord[] | undefined;
/**
* Don't request updates to proposal state while
* this is set to true, to avoid UI flickering
* when pressing pay.
*/
holdCheck: boolean;
payStatus?: CheckPayResult;
replaying: boolean;
payInProgress: boolean;
payAttempt: number;
working: boolean;
abortDone: boolean;
abortStarted: boolean;
}
class ContractPrompt extends React.Component<ContractPromptProps, ContractPromptState> {
constructor(props: ContractPromptProps) {
super(props);
this.state = {
abortDone: false,
abortStarted: false,
alreadyPaid: false,
checkPayError: undefined,
confirmPayError: undefined,
exchanges: undefined,
holdCheck: false,
payAttempt: 0,
payDisabled: true,
payInProgress: false,
proposal: undefined,
proposalId: props.proposalId,
replaying: false,
working: false,
};
}
componentWillMount() {
this.update();
}
componentWillUnmount() {
// FIXME: abort running ops
}
async update() {
if (this.props.resourceUrl) {
const p = await wxApi.queryPaymentByFulfillmentUrl(this.props.resourceUrl);
console.log("query for resource url", this.props.resourceUrl, "result", p);
if (p && p.finished) {
if (p.lastSessionSig === undefined || p.lastSessionSig === this.props.sessionId) {
const nextUrl = new URI(p.contractTerms.fulfillment_url);
nextUrl.addSearch("order_id", p.contractTerms.order_id);
if (p.lastSessionSig) {
nextUrl.addSearch("session_sig", p.lastSessionSig);
}
location.replace(nextUrl.href());
return;
} else {
// We're in a new session
this.setState({ replaying: true });
// FIXME: This could also go wrong. However the payment
// was already successful once, so we can just retry and not refund it.
const payResult = await wxApi.submitPay(p.contractTermsHash, this.props.sessionId);
console.log("payResult", payResult);
location.replace(payResult.nextUrl);
return;
}
}
}
let proposalId = this.props.proposalId;
if (proposalId === undefined) {
if (this.props.contractUrl === undefined) {
// Nothing we can do ...
return;
}
proposalId = await wxApi.downloadProposal(this.props.contractUrl);
}
const proposal = await wxApi.getProposal(proposalId);
this.setState({ proposal, proposalId });
this.checkPayment();
const exchanges = await wxApi.getExchanges();
this.setState({ exchanges });
}
async checkPayment() {
window.setTimeout(() => this.checkPayment(), 500);
if (this.state.holdCheck) {
return;
}
const proposalId = this.state.proposalId;
if (proposalId === undefined) {
return;
}
const payStatus = await wxApi.checkPay(proposalId);
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.`;
if (this.state.exchanges && this.state.proposal) {
const acceptedExchangePubs = this.state.proposal.contractTerms.exchanges.map((e) => e.master_pub);
const ex = this.state.exchanges.find((e) => acceptedExchangePubs.indexOf(e.masterPublicKey) >= 0);
if (ex) {
this.setState({ checkPayError: msgInsufficient });
} else {
this.setState({ checkPayError: msgNoMatch });
}
} else {
this.setState({ checkPayError: msgInsufficient });
}
this.setState({ payDisabled: true });
} else if (payStatus.status === "paid") {
this.setState({ alreadyPaid: true, payDisabled: false, checkPayError: undefined, payStatus });
} else {
this.setState({ payDisabled: false, checkPayError: undefined, payStatus });
}
}
async doPayment() {
const proposal = this.state.proposal;
this.setState({ holdCheck: true, payAttempt: this.state.payAttempt + 1});
if (!proposal) {
return;
}
const proposalId = proposal.id;
if (proposalId === undefined) {
console.error("proposal has no id");
return;
}
console.log("confirmPay with", proposalId, "and", this.props.sessionId);
let payResult;
this.setState({ working: true });
try {
payResult = await wxApi.confirmPay(proposalId, this.props.sessionId);
} catch (e) {
if (!(e instanceof WalletApiError)) {
throw e;
}
this.setState({ confirmPayError: e.detail });
return;
}
console.log("payResult", payResult);
document.location.replace(payResult.nextUrl);
this.setState({ holdCheck: true });
}
async abortPayment() {
const proposal = this.state.proposal;
this.setState({ holdCheck: true, abortStarted: true });
if (!proposal) {
return;
}
wxApi.abortFailedPayment(proposal.contractTermsHash);
this.setState({ abortDone: true });
}
render() {
if (this.props.contractUrl === undefined && this.props.proposalId === undefined) {
return <span>Error: either contractUrl or proposalId must be given</span>;
}
if (this.state.replaying) {
return <span>Re-submitting existing payment</span>;
}
if (this.state.proposalId === undefined) {
return <span>Downloading contract terms</span>;
}
if (!this.state.proposal) {
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(Amounts.parseOrThrow(c.amount))}</strong>;
console.log("payStatus", this.state.payStatus);
let products = null;
if (c.products.length) {
products = (
<div>
<span>The following items are included:</span>
<ul>
{c.products.map(
(p: any, i: number) => (<li key={i}>{p.description}: {renderAmount(p.price)}</li>))
}
</ul>
</div>
);
}
const ConfirmButton = () => (
<button className="pure-button button-success"
disabled={this.state.payDisabled}
onClick={() => this.doPayment()}>
{i18n.str`Confirm payment`}
</button>
);
const WorkingButton = () => (
<div>
<button className="pure-button button-success"
disabled={this.state.payDisabled}
onClick={() => this.doPayment()}>
<span><object className="svg-icon svg-baseline" data="/img/spinner-bars.svg" /> </span>
{i18n.str`Submitting payment`}
</button>
</div>
);
const ConfirmPayDialog = () => (
<div>
{this.state.working ? WorkingButton() : ConfirmButton()}
<div>
{(this.state.alreadyPaid
? <p className="okaybox">
{i18n.str`You already paid for this, clicking "Confirm payment" will not cost money again.`}
</p>
: <p />)}
{(this.state.checkPayError ? <p className="errorbox">{this.state.checkPayError}</p> : <p />)}
</div>
<Details exchanges={this.state.exchanges} contractTerms={c} collapsed={!this.state.checkPayError}/>
</div>
);
const PayErrorDialog = () => (
<div>
<p>There was an error paying (attempt #{this.state.payAttempt}):</p>
<pre>{JSON.stringify(this.state.confirmPayError)}</pre>
{ this.state.abortStarted
? <span>{i18n.str`Aborting payment ...`}</span>
: this.state.abortDone
? <span>{i18n.str`Payment aborted!`}</span>
: <>
<button className="pure-button" onClick={() => this.doPayment()}>
{i18n.str`Retry Payment`}
</button>
<button className="pure-button" onClick={() => this.abortPayment()}>
{i18n.str`Abort Payment`}
</button>
</>
}
</div>
);
return (
<div>
<i18n.Translate wrap="p">
The merchant{" "}<span>{merchantName}</span> offers you to purchase:
</i18n.Translate>
<div style={{"textAlign": "center"}}>
<strong>{c.summary}</strong>
</div>
<strong></strong>
{products}
{(this.state.payStatus && this.state.payStatus.coinSelection)
? <i18n.Translate wrap="p">
The total price is <span>{amount} </span>
(plus <span>{renderAmount(this.state.payStatus.coinSelection.totalFees)}</span> fees).
</i18n.Translate>
:
<i18n.Translate wrap="p">The total price is <span>{amount}</span>.</i18n.Translate>
}
{ this.state.confirmPayError
? PayErrorDialog()
: ConfirmPayDialog()
}
</div>
);
}
}
runOnceWhenReady(() => {
const url = new URI(document.location.href);
const query: any = URI.parseQuery(url.query());
let proposalId;
try {
proposalId = JSON.parse(query.proposalId);
} catch {
// ignore error
}
const sessionId = query.sessionId;
const contractUrl = query.contractUrl;
const resourceUrl = query.resourceUrl;
ReactDOM.render(
<ContractPrompt {...{ proposalId, contractUrl, sessionId, resourceUrl }}/>,
document.getElementById("contract")!);
});

View File

@ -1,526 +0,0 @@
/*
This file is part of TALER
(C) 2015-2016 GNUnet e.V.
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3, or (at your option) any later version.
TALER is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
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.
*
* @author Florian Dold
*/
import { canonicalizeBaseUrl } from "../../helpers";
import * as i18n from "../../i18n";
import { AmountJson } from "../../amounts";
import * as Amounts from "../../amounts";
import {
CurrencyRecord,
} from "../../dbTypes";
import {
CreateReserveResponse,
ReserveCreationInfo,
} from "../../walletTypes";
import { ImplicitStateComponent, StateHolder } from "../components";
import {
WalletApiError,
createReserve,
getCurrency,
getExchangeInfo,
getReserveCreationInfo,
} from "../wxApi";
import {
WithdrawDetailView,
renderAmount,
} from "../renderHtml";
import * as React from "react";
import * as ReactDOM from "react-dom";
import URI = require("urijs");
function delay<T>(delayMs: number, value: T): Promise<T> {
return new Promise<T>((resolve, reject) => {
setTimeout(() => resolve(value), delayMs);
});
}
class EventTrigger {
private triggerResolve: any;
private triggerPromise: Promise<boolean>;
constructor() {
this.reset();
}
private reset() {
this.triggerPromise = new Promise<boolean>((resolve, reject) => {
this.triggerResolve = resolve;
});
}
trigger() {
this.triggerResolve(false);
this.reset();
}
async wait(delayMs: number): Promise<boolean> {
return await Promise.race([this.triggerPromise, delay(delayMs, true)]);
}
}
interface ExchangeSelectionProps {
suggestedExchangeUrl: string;
amount: AmountJson;
callback_url: string;
wt_types: string[];
currencyRecord: CurrencyRecord|null;
sender_wire: string | undefined;
}
interface ManualSelectionProps {
onSelect(url: string): void;
initialUrl: string;
}
class ManualSelection extends ImplicitStateComponent<ManualSelectionProps> {
private url: StateHolder<string> = this.makeState("");
private errorMessage: StateHolder<string|null> = this.makeState(null);
private isOkay: StateHolder<boolean> = this.makeState(false);
private updateEvent = new EventTrigger();
constructor(p: ManualSelectionProps) {
super(p);
this.url(p.initialUrl);
this.update();
}
render() {
return (
<div className="pure-g pure-form pure-form-stacked">
<div className="pure-u-1">
<label>URL</label>
<input className="url" type="text" spellCheck={false}
value={this.url()}
key="exchange-url-input"
onInput={(e) => this.onUrlChanged((e.target as HTMLInputElement).value)}
onChange={(e) => this.onUrlChanged((e.target as HTMLInputElement).value)} />
</div>
<div className="pure-u-1">
<button className="pure-button button-success"
disabled={!this.isOkay()}
onClick={() => this.props.onSelect(this.url())}>
{i18n.str`Select`}
</button>
<span> </span>
{this.errorMessage()}
</div>
</div>
);
}
async update() {
this.errorMessage(null);
this.isOkay(false);
if (!this.url()) {
return;
}
const parsedUrl = new URI(this.url()!);
if (parsedUrl.is("relative")) {
this.errorMessage(i18n.str`Error: URL may not be relative`);
this.isOkay(false);
return;
}
try {
const url = canonicalizeBaseUrl(this.url()!);
await getExchangeInfo(url);
console.log("getExchangeInfo returned");
this.isOkay(true);
} catch (e) {
if (!(e instanceof WalletApiError)) {
// maybe it's something more serious, don't handle here!
throw e;
}
console.log(`got error "${e.message} "with detail`, e.detail);
this.errorMessage(i18n.str`Invalid exchange URL (${e.message})`);
}
}
async onUrlChanged(s: string) {
this.url(s);
this.errorMessage(null);
this.isOkay(false);
this.updateEvent.trigger();
const waited = await this.updateEvent.wait(200);
if (waited) {
// Run the actual update if nobody else preempted us.
this.update();
}
}
}
class ExchangeSelection extends ImplicitStateComponent<ExchangeSelectionProps> {
private statusString: StateHolder<string|null> = this.makeState(null);
private reserveCreationInfo: StateHolder<ReserveCreationInfo|null> = this.makeState(
null);
private url: StateHolder<string|null> = this.makeState(null);
private selectingExchange: StateHolder<boolean> = this.makeState(false);
constructor(props: ExchangeSelectionProps) {
super(props);
const prefilledExchangesUrls = [];
if (props.currencyRecord) {
const exchanges = props.currencyRecord.exchanges.map((x) => x.baseUrl);
prefilledExchangesUrls.push(...exchanges);
}
if (props.suggestedExchangeUrl) {
prefilledExchangesUrls.push(props.suggestedExchangeUrl);
}
if (prefilledExchangesUrls.length !== 0) {
this.url(prefilledExchangesUrls[0]);
this.forceReserveUpdate();
} else {
this.selectingExchange(true);
}
}
renderFeeStatus() {
const rci = this.reserveCreationInfo();
if (rci) {
const totalCost = Amounts.add(rci.overhead, rci.withdrawFee).amount;
let trustMessage;
if (rci.isTrusted) {
trustMessage = (
<i18n.Translate wrap="p">
The exchange is trusted by the wallet.
</i18n.Translate>
);
} else if (rci.isAudited) {
trustMessage = (
<i18n.Translate wrap="p">
The exchange is audited by a trusted auditor.
</i18n.Translate>
);
} else {
trustMessage = (
<i18n.Translate wrap="p">
Warning: The exchange is neither directly trusted nor audited by a trusted auditor.
If you withdraw from this exchange, it will be trusted in the future.
</i18n.Translate>
);
}
return (
<div>
<i18n.Translate wrap="p">
Using exchange provider <strong>{this.url()}</strong>.
The exchange provider will charge
{" "}<span>{renderAmount(totalCost)}</span>{" "}
in fees.
</i18n.Translate>
{trustMessage}
</div>
);
}
if (this.url() && !this.statusString()) {
const shortName = new URI(this.url()!).host();
return (
<i18n.Translate wrap="p">
Waiting for a response from
<span> </span>
<em>{shortName}</em>
</i18n.Translate>
);
}
if (this.statusString()) {
return (
<p>
<strong style={{color: "red"}}>{this.statusString()}</strong>
</p>
);
}
return (
<p>
{i18n.str`Information about fees will be available when an exchange provider is selected.`}
</p>
);
}
renderUpdateStatus() {
const rci = this.reserveCreationInfo();
if (!rci) {
return null;
}
if (!rci.versionMatch) {
return null;
}
if (rci.versionMatch.compatible) {
return null;
}
if (rci.versionMatch.currentCmp === -1) {
return (
<p className="errorbox">
<i18n.Translate wrap="span">
Your wallet (protocol version <span>{rci.walletVersion}</span>) might be outdated.<span> </span>
The exchange has a higher, incompatible
protocol version (<span>{rci.exchangeVersion}</span>).
</i18n.Translate>
</p>
);
}
if (rci.versionMatch.currentCmp === 1) {
return (
<p className="errorbox">
<i18n.Translate wrap="span">
The chosen exchange (protocol version <span>{rci.exchangeVersion}</span> might be outdated.<span> </span>
The exchange has a lower, incompatible
protocol version than your wallet (protocol version <span>{rci.walletVersion}</span>).
</i18n.Translate>
</p>
);
}
throw Error("not reached");
}
renderConfirm() {
return (
<div>
{this.renderFeeStatus()}
<p>
<button className="pure-button button-success"
disabled={this.reserveCreationInfo() === null}
onClick={() => this.confirmReserve()}>
{i18n.str`Accept fees and withdraw`}
</button>
{ " " }
<button className="pure-button button-secondary"
onClick={() => this.selectingExchange(true)}>
{i18n.str`Change Exchange Provider`}
</button>
</p>
{this.renderUpdateStatus()}
<WithdrawDetailView rci={this.reserveCreationInfo()} />
</div>
);
}
select(url: string) {
this.reserveCreationInfo(null);
this.url(url);
this.selectingExchange(false);
this.forceReserveUpdate();
}
renderSelect() {
const exchanges = (this.props.currencyRecord && this.props.currencyRecord.exchanges) || [];
console.log(exchanges);
return (
<div>
{i18n.str`Please select an exchange. You can review the details before after your selection.`}
{this.props.suggestedExchangeUrl && (
<div>
<h2>Bank Suggestion</h2>
<button className="pure-button button-success" onClick={() => this.select(this.props.suggestedExchangeUrl)}>
<i18n.Translate wrap="span">
Select <strong>{this.props.suggestedExchangeUrl}</strong>
</i18n.Translate>
</button>
</div>
)}
{exchanges.length > 0 && (
<div>
<h2>Known Exchanges</h2>
{exchanges.map((e) => (
<button key={e.baseUrl} className="pure-button button-success" onClick={() => this.select(e.baseUrl)}>
<i18n.Translate>
Select <strong>{e.baseUrl}</strong>
</i18n.Translate>
</button>
))}
</div>
)}
<h2>i18n.str`Manual Selection`</h2>
<ManualSelection initialUrl={this.url() || ""} onSelect={(url: string) => this.select(url)} />
</div>
);
}
render(): JSX.Element {
return (
<div>
<i18n.Translate wrap="p">
You are about to withdraw
{" "}<strong>{renderAmount(this.props.amount)}</strong>{" "}
from your bank account into your wallet.
</i18n.Translate>
{this.selectingExchange() ? this.renderSelect() : this.renderConfirm()}
</div>
);
}
confirmReserve() {
this.confirmReserveImpl(this.reserveCreationInfo()!,
this.url()!,
this.props.amount,
this.props.callback_url,
this.props.sender_wire);
}
/**
* Do an update of the reserve creation info, without any debouncing.
*/
async forceReserveUpdate() {
this.reserveCreationInfo(null);
try {
const url = canonicalizeBaseUrl(this.url()!);
const r = await getReserveCreationInfo(url,
this.props.amount);
console.log("get exchange info resolved");
this.reserveCreationInfo(r);
console.dir(r);
} catch (e) {
console.log("get exchange info rejected", e);
this.statusString(`Error: ${e.message}`);
// Re-try every 5 seconds as long as there is a problem
setTimeout(() => this.statusString() ? this.forceReserveUpdate() : undefined, 5000);
}
}
async confirmReserveImpl(rci: ReserveCreationInfo,
exchange: string,
amount: AmountJson,
callback_url: string,
sender_wire: string | undefined) {
const rawResp = await createReserve({
amount,
exchange: canonicalizeBaseUrl(exchange),
senderWire: sender_wire,
});
if (!rawResp) {
throw Error("empty response");
}
// FIXME: filter out types that bank/exchange don't have in common
const exchangeWireAccounts = [];
for (let acct of rci.exchangeWireAccounts) {
const payto = new URI(acct);
if (payto.scheme() != "payto") {
console.warn("unknown wire account URI scheme", acct);
continue;
}
if (this.props.wt_types.includes(payto.authority())) {
exchangeWireAccounts.push(acct);
}
}
const chosenAcct = exchangeWireAccounts[0];
if (!chosenAcct) {
throw Error("no exchange account matches the bank's supported types");
}
if (!rawResp.error) {
const resp = CreateReserveResponse.checked(rawResp);
const q: {[name: string]: string|number} = {
amount_currency: amount.currency,
amount_fraction: amount.fraction,
amount_value: amount.value,
exchange: resp.exchange,
exchange_wire_details: chosenAcct,
reserve_pub: resp.reservePub,
};
const url = new URI(callback_url).addQuery(q);
if (!url.is("absolute")) {
throw Error("callback url is not absolute");
}
console.log("going to", url.href());
document.location.href = url.href();
} else {
this.statusString(
i18n.str`Oops, something went wrong. The wallet responded with error status (${rawResp.error}).`);
}
}
renderStatus(): any {
if (this.statusString()) {
return <p><strong style={{color: "red"}}>{this.statusString()}</strong></p>;
} else if (!this.reserveCreationInfo()) {
return <p>{i18n.str`Checking URL, please wait ...`}</p>;
}
return "";
}
}
async function main() {
try {
const url = new URI(document.location.href);
const query: any = URI.parseQuery(url.query());
let amount;
try {
amount = AmountJson.checked(JSON.parse(query.amount));
} catch (e) {
throw Error(i18n.str`Can't parse amount: ${e.message}`);
}
const callback_url = query.callback_url;
let wt_types;
try {
wt_types = JSON.parse(query.wt_types);
} catch (e) {
throw Error(i18n.str`Can't parse wire_types: ${e.message}`);
}
let sender_wire;
if (query.sender_wire) {
let senderWireUri = new URI(query.sender_wire);
if (senderWireUri.scheme() != "payto") {
throw Error("sender wire info must be a payto URI");
}
sender_wire = query.sender_wire;
}
const suggestedExchangeUrl = query.suggested_exchange_url;
const currencyRecord = await getCurrency(amount.currency);
const args = {
amount,
callback_url,
currencyRecord,
sender_wire,
suggestedExchangeUrl,
wt_types,
};
ReactDOM.render(<ExchangeSelection {...args} />, document.getElementById(
"exchange-selection")!);
} catch (e) {
// TODO: provide more context information, maybe factor it out into a
// TODO:generic error reporting function or component.
document.body.innerText = i18n.str`Fatal error: "${e.message}".`;
console.error("got error", e);
}
}
document.addEventListener("DOMContentLoaded", () => {
main();
});

View File

@ -11,7 +11,7 @@
<link rel="icon" href="/img/icon.png">
<script src="/dist/page-common-bundle.js"></script>
<script src="/dist/confirm-contract-bundle.js"></script>
<script src="/dist/pay-bundle.js"></script>
<style>
button.accept {

173
src/webex/pages/pay.tsx Normal file
View File

@ -0,0 +1,173 @@
/*
This file is part of TALER
(C) 2015 GNUnet e.V.
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3, or (at your option) any later version.
TALER is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
/**
* Page shown to the user to confirm entering
* a contract.
*/
/**
* Imports.
*/
import * as i18n from "../../i18n";
import { runOnceWhenReady } from "./common";
import { ExchangeRecord, ProposalDownloadRecord } from "../../dbTypes";
import { ContractTerms } from "../../talerTypes";
import { CheckPayResult, PreparePayResult } from "../../walletTypes";
import { renderAmount } from "../renderHtml";
import * as wxApi from "../wxApi";
import React, { useState, useEffect } from "react";
import * as ReactDOM from "react-dom";
import URI = require("urijs");
import { WalletApiError } from "../wxApi";
import * as Amounts from "../../amounts";
function TalerPayDialog({ talerPayUri }: { talerPayUri: string }) {
const [payStatus, setPayStatus] = useState<PreparePayResult | undefined>();
const [payErrMsg, setPayErrMsg] = useState<string | undefined>("");
const [numTries, setNumTries] = useState(0);
let totalFees: Amounts.AmountJson | undefined = undefined;
useEffect(() => {
const doFetch = async () => {
const p = await wxApi.preparePay(talerPayUri);
setPayStatus(p);
};
doFetch();
});
if (!payStatus) {
return <span>Loading payment information ...</span>;
}
if (payStatus.status === "error") {
return <span>Error: {payStatus.error}</span>;
}
if (payStatus.status === "payment-possible") {
totalFees = payStatus.totalFees;
}
if (payStatus.status === "paid" && numTries === 0) {
return (
<span>
You have already paid for this article. Click{" "}
<a href={payStatus.nextUrl}>here</a> to view it again.
</span>
);
}
const contractTerms = payStatus.contractTerms;
if (!contractTerms) {
return (
<span>
Error: did not get contract terms from merchant or wallet backend.
</span>
);
}
let merchantName: React.ReactElement;
if (contractTerms.merchant && contractTerms.merchant.name) {
merchantName = <strong>{contractTerms.merchant.name}</strong>;
} else {
merchantName = <strong>(pub: {contractTerms.merchant_pub})</strong>;
}
const amount = (
<strong>{renderAmount(Amounts.parseOrThrow(contractTerms.amount))}</strong>
);
const doPayment = async () => {
setNumTries(numTries + 1);
try {
const res = await wxApi.confirmPay(payStatus!.proposalId!, undefined);
document.location.href = res.nextUrl;
} catch (e) {
console.error(e);
setPayErrMsg(e.message);
}
};
return (
<div>
<p>
<i18n.Translate wrap="p">
The merchant <span>{merchantName}</span> offers you to purchase:
</i18n.Translate>
<div style={{ textAlign: "center" }}>
<strong>{contractTerms.summary}</strong>
</div>
{totalFees ? (
<i18n.Translate wrap="p">
The total price is <span>{amount} </span>
(plus <span>{renderAmount(totalFees)}</span> fees).
</i18n.Translate>
) : (
<i18n.Translate wrap="p">
The total price is <span>{amount}</span>.
</i18n.Translate>
)}
</p>
{payErrMsg ? (
<div>
<p>Payment failed: {payErrMsg}</p>
<button
className="pure-button button-success"
onClick={() => doPayment()}
>
{i18n.str`Retry`}
</button>
</div>
) : (
<div>
<button
className="pure-button button-success"
onClick={() => doPayment()}
>
{i18n.str`Confirm payment`}
</button>
</div>
)}
</div>
);
}
runOnceWhenReady(() => {
try {
const url = new URI(document.location.href);
const query: any = URI.parseQuery(url.query());
let talerPayUri = query.talerPayUri;
ReactDOM.render(
<TalerPayDialog talerPayUri={talerPayUri} />,
document.getElementById("contract")!,
);
} catch (e) {
ReactDOM.render(
<span>Fatal error: {e.message}</span>,
document.getElementById("contract")!,
);
console.error(e);
}
});

View File

@ -10,7 +10,7 @@
<link rel="stylesheet" type="text/css" href="../style/wallet.css">
<script src="/dist/page-common-bundle.js"></script>
<script src="/dist/confirm-create-reserve-bundle.js"></script>
<script src="/dist/withdraw-bundle.js"></script>
</head>

View File

@ -0,0 +1,231 @@
/*
This file is part of TALER
(C) 2015-2016 GNUnet e.V.
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3, or (at your option) any later version.
TALER is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
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.
*
* @author Florian Dold
*/
import { canonicalizeBaseUrl } from "../../helpers";
import * as i18n from "../../i18n";
import { AmountJson } from "../../amounts";
import * as Amounts from "../../amounts";
import { CurrencyRecord } from "../../dbTypes";
import {
CreateReserveResponse,
ReserveCreationInfo,
WithdrawDetails,
} from "../../walletTypes";
import { ImplicitStateComponent, StateHolder } from "../components";
import { WithdrawDetailView, renderAmount } from "../renderHtml";
import React, { useState, useEffect } from "react";
import * as ReactDOM from "react-dom";
import URI = require("urijs");
import { getWithdrawDetails, acceptWithdrawal } from "../wxApi";
function NewExchangeSelection(props: { talerWithdrawUri: string }) {
const [details, setDetails] = useState<WithdrawDetails | undefined>();
const [selectedExchange, setSelectedExchange] = useState<
string | undefined
>();
const talerWithdrawUri = props.talerWithdrawUri;
const [cancelled, setCancelled] = useState(false);
const [selecting, setSelecting] = useState(false);
const [customUrl, setCustomUrl] = useState<string>("");
const [errMsg, setErrMsg] = useState<string | undefined>("");
useEffect(() => {
const fetchData = async () => {
console.log("getting from", talerWithdrawUri);
let d: WithdrawDetails | undefined = undefined;
try {
d = await getWithdrawDetails(talerWithdrawUri, selectedExchange);
} catch (e) {
console.error("error getting withdraw details", e);
setErrMsg(e.message);
return;
}
console.log("got withdrawDetails", d);
if (!selectedExchange && d.withdrawInfo.suggestedExchange) {
console.log("setting selected exchange");
setSelectedExchange(d.withdrawInfo.suggestedExchange);
}
setDetails(d);
};
fetchData();
}, [selectedExchange, errMsg, selecting]);
if (errMsg) {
return (
<div>
<i18n.Translate wrap="p">
Could not get details for withdraw operation:
</i18n.Translate>
<p style={{ color: "red" }}>{errMsg}</p>
<p>
<span
role="button"
tabIndex={0}
style={{ textDecoration: "underline", cursor: "pointer" }}
onClick={() => {
setSelecting(true);
setErrMsg(undefined);
setSelectedExchange(undefined);
setDetails(undefined);
}}
>
{i18n.str`Chose different exchange provider`}
</span>
</p>
</div>
);
}
if (!details) {
return <span>Loading...</span>;
}
if (cancelled) {
return <span>Withdraw operation has been cancelled.</span>;
}
if (selecting) {
const bankSuggestion = details && details.withdrawInfo.suggestedExchange;
return (
<div>
{i18n.str`Please select an exchange. You can review the details before after your selection.`}
{bankSuggestion && (
<div>
<h2>Bank Suggestion</h2>
<button
className="pure-button button-success"
onClick={() => {
setDetails(undefined);
setSelectedExchange(bankSuggestion);
setSelecting(false);
}}
>
<i18n.Translate wrap="span">
Select <strong>{bankSuggestion}</strong>
</i18n.Translate>
</button>
</div>
)}
<h2>Custom Selection</h2>
<p>
<input
type="text"
onChange={e => setCustomUrl(e.target.value)}
value={customUrl}
/>
</p>
<button
className="pure-button button-success"
onClick={() => {
setDetails(undefined);
setSelectedExchange(customUrl);
setSelecting(false);
}}
>
<i18n.Translate wrap="span">Select custom exchange</i18n.Translate>
</button>
</div>
);
}
const accept = async () => {
console.log("accepting exchange", selectedExchange);
const res = await acceptWithdrawal(talerWithdrawUri, selectedExchange!);
console.log("accept withdrawal response", res);
if (res.confirmTransferUrl) {
document.location.href = res.confirmTransferUrl;
}
};
return (
<div>
<i18n.Translate wrap="p">
You are about to withdraw{" "}
<strong>{renderAmount(details.withdrawInfo.amount)}</strong> from your
bank account into your wallet.
</i18n.Translate>
<div>
<button
className="pure-button button-success"
disabled={!selectedExchange}
onClick={() => accept()}
>
{i18n.str`Accept fees and withdraw`}
</button>
<p>
<span
role="button"
tabIndex={0}
style={{ textDecoration: "underline", cursor: "pointer" }}
onClick={() => setSelecting(true)}
>
{i18n.str`Chose different exchange provider`}
</span>
<br />
<span
role="button"
tabIndex={0}
style={{ textDecoration: "underline", cursor: "pointer" }}
onClick={() => setCancelled(true)}
>
{i18n.str`Cancel withdraw operation`}
</span>
</p>
{details.reserveCreationInfo ? (
<WithdrawDetailView rci={details.reserveCreationInfo} />
) : null}
</div>
</div>
);
}
async function main() {
try {
const url = new URI(document.location.href);
const query: any = URI.parseQuery(url.query());
let talerWithdrawUri = query.talerWithdrawUri;
if (!talerWithdrawUri) {
throw Error("withdraw URI required");
}
ReactDOM.render(
<NewExchangeSelection talerWithdrawUri={talerWithdrawUri} />,
document.getElementById("exchange-selection")!,
);
} catch (e) {
// TODO: provide more context information, maybe factor it out into a
// TODO:generic error reporting function or component.
document.body.innerText = i18n.str`Fatal error: "${e.message}".`;
console.error("got error", e);
}
}
document.addEventListener("DOMContentLoaded", () => {
main();
});

View File

@ -137,6 +137,11 @@ button.linky {
cursor:pointer;
}
.blacklink a:link, .blacklink a:visited, .blacklink a:hover, .blacklink a:active {
color: #000;
}
table, th, td {
border: 1px solid black;
}

View File

@ -79,6 +79,8 @@ export interface UpgradeResponse {
export class WalletApiError extends Error {
constructor(message: string, public detail: any) {
super(message);
// restore prototype chain
Object.setPrototypeOf(this, new.target.prototype);
}
}
@ -401,3 +403,24 @@ export function abortFailedPayment(contractTermsHash: string) {
export function benchmarkCrypto(repetitions: number): Promise<BenchmarkResult> {
return callBackend("benchmark-crypto", { repetitions });
}
/**
* Get details about a withdraw operation.
*/
export function getWithdrawDetails(talerWithdrawUri: string, maybeSelectedExchange: string | undefined) {
return callBackend("get-withdraw-details", { talerWithdrawUri, maybeSelectedExchange });
}
/**
* Get details about a pay operation.
*/
export function preparePay(talerPayUri: string) {
return callBackend("prepare-pay", { talerPayUri });
}
/**
* Get details about a withdraw operation.
*/
export function acceptWithdrawal(talerWithdrawUri: string, selectedExchange: string) {
return callBackend("accept-withdrawal", { talerWithdrawUri, selectedExchange });
}

View File

@ -339,6 +339,20 @@ function handleMessage(
}
return needsWallet().benchmarkCrypto(detail.repetitions);
}
case "get-withdraw-details": {
return needsWallet().getWithdrawDetails(
detail.talerWithdrawUri,
detail.maybeSelectedExchange,
);
}
case "accept-withdrawal": {
return needsWallet().acceptWithdrawal(
detail.talerWithdrawUri,
detail.selectedExchange,
);
}
case "prepare-pay":
return needsWallet().preparePay(detail.talerPayUri);
default:
// Exhaustiveness check.
// See https://www.typescriptlang.org/docs/handbook/advanced-types.html
@ -523,190 +537,6 @@ function makeSyncWalletRedirect(
return { redirectUrl: outerUrl.href() };
}
/**
* Handle a HTTP response that has the "402 Payment Required" status.
* In this callback we don't have access to the body, and must communicate via
* shared state with the content script that will later be run later
* in this tab.
*/
function handleHttpPayment(
headerList: chrome.webRequest.HttpHeader[],
url: string,
tabId: number,
): any {
if (!currentWallet) {
console.log("can't handle payment, no wallet");
return;
}
const headers: { [s: string]: string } = {};
for (const kv of headerList) {
if (kv.value) {
headers[kv.name.toLowerCase()] = kv.value;
}
}
const decodeIfDefined = (url?: string) =>
url ? decodeURIComponent(url) : undefined;
const fields = {
contract_url: decodeIfDefined(headers["taler-contract-url"]),
offer_url: decodeIfDefined(headers["taler-offer-url"]),
refund_url: decodeIfDefined(headers["taler-refund-url"]),
resource_url: decodeIfDefined(headers["taler-resource-url"]),
session_id: decodeIfDefined(headers["taler-session-id"]),
tip: decodeIfDefined(headers["taler-tip"]),
};
const talerHeaderFound =
Object.keys(fields).filter((x: any) => (fields as any)[x]).length !== 0;
if (!talerHeaderFound) {
// looks like it's not a taler request, it might be
// for a different payment system (or the shop is buggy)
console.log("ignoring non-taler 402 response");
return;
}
console.log("got pay detail", fields);
// Synchronous fast path for existing payment
if (fields.resource_url) {
const result = currentWallet.getNextUrlFromResourceUrl(fields.resource_url);
if (
result &&
(fields.session_id === undefined ||
fields.session_id === result.lastSessionId)
) {
return { redirectUrl: result.nextUrl };
}
}
// Synchronous fast path for new contract
if (fields.contract_url) {
return makeSyncWalletRedirect("confirm-contract.html", tabId, url, {
contractUrl: fields.contract_url,
resourceUrl: fields.resource_url,
sessionId: fields.session_id,
});
}
// Synchronous fast path for tip
if (fields.tip) {
return makeSyncWalletRedirect("tip.html", tabId, url, {
tip_token: fields.tip,
});
}
// Synchronous fast path for refund
if (fields.refund_url) {
console.log("processing refund");
return makeSyncWalletRedirect("refund.html", tabId, url, {
refundUrl: fields.refund_url,
});
}
// We need to do some asynchronous operation, we can't directly redirect
talerPay(fields, url, tabId).then(nextUrl => {
if (nextUrl) {
// We use chrome.tabs.executeScript instead of chrome.tabs.update
// because the latter is buggy when it does not execute in the same
// (micro-?)task as the header callback.
chrome.tabs.executeScript({
code: `document.location.href = decodeURIComponent("${encodeURI(
nextUrl,
)}");`,
runAt: "document_start",
});
}
});
return;
}
function handleBankRequest(
wallet: Wallet,
headerList: chrome.webRequest.HttpHeader[],
url: string,
tabId: number,
): any {
const headers: { [s: string]: string } = {};
for (const kv of headerList) {
if (kv.value) {
headers[kv.name.toLowerCase()] = kv.value;
}
}
const operation = headers["taler-operation"];
if (!operation) {
// Not a taler related request.
return;
}
if (operation === "confirm-reserve") {
const reservePub = headers["taler-reserve-pub"];
if (reservePub !== undefined) {
console.log(`confirming reserve ${reservePub} via 201`);
wallet.confirmReserve({ reservePub });
} else {
console.warn(
"got 'Taler-Operation: confirm-reserve' without 'Taler-Reserve-Pub'",
);
}
return;
}
if (operation === "create-reserve") {
const amount = headers["taler-amount"];
if (!amount) {
console.log("202 not understood (Taler-Amount missing)");
return;
}
const callbackUrl = headers["taler-callback-url"];
if (!callbackUrl) {
console.log("202 not understood (Taler-Callback-Url missing)");
return;
}
try {
JSON.parse(amount);
} catch (e) {
const errUri = new URI(
chrome.extension.getURL("/src/webex/pages/error.html"),
);
const p = {
message: `Can't parse amount ("${amount}"): ${e.message}`,
};
const errRedirectUrl = errUri.query(p).href();
// FIXME: use direct redirect when https://bugzilla.mozilla.org/show_bug.cgi?id=707624 is fixed
chrome.tabs.update(tabId, { url: errRedirectUrl });
return;
}
const wtTypes = headers["taler-wt-types"];
if (!wtTypes) {
console.log("202 not understood (Taler-Wt-Types missing)");
return;
}
const params = {
amount,
bank_url: url,
callback_url: new URI(callbackUrl).absoluteTo(url),
sender_wire: headers["taler-sender-wire"],
suggested_exchange_url: headers["taler-suggested-exchange"],
wt_types: wtTypes,
};
const uri = new URI(
chrome.extension.getURL("/src/webex/pages/confirm-create-reserve.html"),
);
const redirectUrl = uri.query(params).href();
console.log("redirecting to", redirectUrl);
// FIXME: use direct redirect when https://bugzilla.mozilla.org/show_bug.cgi?id=707624 is fixed
chrome.tabs.update(tabId, { url: redirectUrl });
return;
}
console.log("Ignoring unknown (X-)Taler-Operation:", operation);
}
// Rate limit cache for executePayment operations, to break redirect loops
let rateLimitCache: { [n: number]: number } = {};
@ -931,19 +761,59 @@ export async function wxMain() {
}
if (details.statusCode === 402) {
console.log(`got 402 from ${details.url}`);
return handleHttpPayment(
details.responseHeaders || [],
details.url,
details.tabId,
);
} else if (details.statusCode === 202) {
return handleBankRequest(
wallet!,
details.responseHeaders || [],
details.url,
details.tabId,
for (let header of details.responseHeaders || []) {
if (header.name.toLowerCase() === "taler") {
const talerUri = header.value || "";
if (!talerUri.startsWith("taler://")) {
console.warn(
"Response with HTTP 402 has Taler header, but header value is not a taler:// URI.",
);
break;
}
if (talerUri.startsWith("taler://withdraw/")) {
return makeSyncWalletRedirect(
"withdraw.html",
details.tabId,
details.url,
{
talerWithdrawUri: talerUri,
},
);
} else if (talerUri.startsWith("taler://pay/")) {
return makeSyncWalletRedirect(
"pay.html",
details.tabId,
details.url,
{
talerPayUri: talerUri,
},
);
} else if (talerUri.startsWith("taler://tip/")) {
return makeSyncWalletRedirect(
"tip.html",
details.tabId,
details.url,
{
talerTipUri: talerUri,
},
);
} else if (talerUri.startsWith("taler://refund/")) {
return makeSyncWalletRedirect(
"refund.html",
details.tabId,
details.url,
{
talerRefundUri: talerUri,
},
);
} else {
console.warn("Unknown action in taler:// URI, ignoring.");
}
break;
}
}
}
return {};
},
{ urls: ["<all_urls>"] },
["responseHeaders", "blocking"],

View File

@ -19,7 +19,8 @@
"noImplicitAny": true,
"allowJs": true,
"checkJs": true,
"incremental": true
"incremental": true,
"esModuleInterop": true
},
"files": [
"src/amounts.ts",
@ -72,10 +73,9 @@
"src/webex/pages/auditors.tsx",
"src/webex/pages/benchmark.tsx",
"src/webex/pages/common.ts",
"src/webex/pages/confirm-contract.tsx",
"src/webex/pages/confirm-create-reserve.tsx",
"src/webex/pages/error.tsx",
"src/webex/pages/logs.tsx",
"src/webex/pages/pay.tsx",
"src/webex/pages/payback.tsx",
"src/webex/pages/popup.tsx",
"src/webex/pages/redirect.js",
@ -85,6 +85,7 @@
"src/webex/pages/show-db.ts",
"src/webex/pages/tip.tsx",
"src/webex/pages/tree.tsx",
"src/webex/pages/withdraw.tsx",
"src/webex/renderHtml.tsx",
"src/webex/wxApi.ts",
"src/webex/wxBackend.ts",

View File

@ -77,8 +77,8 @@ module.exports = function (env) {
"add-auditor": "./src/webex/pages/add-auditor.tsx",
"auditors": "./src/webex/pages/auditors.tsx",
"benchmark": "./src/webex/pages/benchmark.tsx",
"confirm-contract": "./src/webex/pages/confirm-contract.tsx",
"confirm-create-reserve": "./src/webex/pages/confirm-create-reserve.tsx",
"pay": "./src/webex/pages/pay.tsx",
"withdraw": "./src/webex/pages/withdraw.tsx",
"error": "./src/webex/pages/error.tsx",
"logs": "./src/webex/pages/logs.tsx",
"payback": "./src/webex/pages/payback.tsx",

1214
yarn.lock

File diff suppressed because it is too large Load Diff