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, allowJs: true,
checkJs: true, checkJs: true,
incremental: true, incremental: true,
esModuleInterop: true,
}; };

View File

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

View File

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

View File

@ -27,7 +27,6 @@ export interface HttpResponse {
responseJson: object & any; responseJson: object & any;
} }
/** /**
* The request library is bundled into an interface to make mocking easy. * 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>; postJson(url: string, body: any): Promise<HttpResponse>;
} }
/** /**
* An implementation of the [[HttpRequestLibrary]] using the * An implementation of the [[HttpRequestLibrary]] using the
* browser's XMLHttpRequest. * browser's XMLHttpRequest.
*/ */
export class BrowserHttpLib implements HttpRequestLibrary { export class BrowserHttpLib implements HttpRequestLibrary {
private req(method: string, private req(
url: string, method: string,
options?: any): Promise<HttpResponse> { url: string,
options?: any,
): Promise<HttpResponse> {
return new Promise<HttpResponse>((resolve, reject) => { return new Promise<HttpResponse>((resolve, reject) => {
const myRequest = new XMLHttpRequest(); const myRequest = new XMLHttpRequest();
myRequest.open(method, url); myRequest.open(method, url);
@ -54,11 +54,36 @@ export class BrowserHttpLib implements HttpRequestLibrary {
} else { } else {
myRequest.send(); 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) { 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") { if (responseJson === null || typeof responseJson !== "object") {
reject(Error("Invalid JSON from HTTP response")); reject(Error("Invalid JSON from HTTP response"));
return;
} }
const resp = { const resp = {
responseJson: responseJson, responseJson: responseJson,
@ -70,27 +95,22 @@ export class BrowserHttpLib implements HttpRequestLibrary {
}); });
} }
get(url: string) { get(url: string) {
return this.req("get", url); return this.req("get", url);
} }
postJson(url: string, body: any) { 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) { postForm(url: string, form: any) {
return this.req("post", url, {req: form}); return this.req("post", url, { req: form });
} }
} }
/** /**
* Exception thrown on request errors. * Exception thrown on request errors.
*/ */
export class RequestException { 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(); const jed = setupJed();
let enableTracing = false;
/** /**
* Set up jed library for internationalization, * Set up jed library for internationalization,
@ -94,7 +96,7 @@ function stringifyChildren(children: any): string {
return `%${n++}$s`; return `%${n++}$s`;
}); });
const s = ss.join("").replace(/ +/g, " ").trim(); const s = ss.join("").replace(/ +/g, " ").trim();
console.log("translation lookup", JSON.stringify(s)); enableTracing && console.log("translation lookup", JSON.stringify(s));
return s; return s;
} }

View File

@ -37,28 +37,28 @@ msgstr ""
msgid "time (ms/op)" msgid "time (ms/op)"
msgstr "" msgstr ""
#: src/webex/pages/confirm-contract.tsx:78 #: src/webex/pages/pay.tsx:78
#, c-format #, c-format
msgid "show more details" msgid "show more details"
msgstr "" msgstr ""
#: src/webex/pages/confirm-contract.tsx:92 #: src/webex/pages/pay.tsx:92
#, c-format #, c-format
msgid "Accepted exchanges:" msgid "Accepted exchanges:"
msgstr "" msgstr ""
#: src/webex/pages/confirm-contract.tsx:97 #: src/webex/pages/pay.tsx:97
#, c-format #, c-format
msgid "Exchanges in the wallet:" msgid "Exchanges in the wallet:"
msgstr "" msgstr ""
#: src/webex/pages/confirm-contract.tsx:219 #: src/webex/pages/pay.tsx:219
#, c-format #, c-format
msgid "You have insufficient funds of the requested currency in your wallet." msgid "You have insufficient funds of the requested currency in your wallet."
msgstr "" msgstr ""
#. tslint:disable-next-line:max-line-length #. tslint:disable-next-line:max-line-length
#: src/webex/pages/confirm-contract.tsx:221 #: src/webex/pages/pay.tsx:221
#, c-format #, c-format
msgid "" msgid ""
"You do not have any funds from an exchange that is accepted by this " "You do not have any funds from an exchange that is accepted by this "
@ -66,180 +66,58 @@ msgid ""
"wallet." "wallet."
msgstr "" msgstr ""
#: src/webex/pages/confirm-contract.tsx:322 #: src/webex/pages/pay.tsx:322
#, fuzzy, c-format #, fuzzy, c-format
msgid "Confirm payment" msgid "Confirm payment"
msgstr "Bezahlung bestätigen" msgstr "Bezahlung bestätigen"
#: src/webex/pages/confirm-contract.tsx:332 #: src/webex/pages/pay.tsx:332
#, fuzzy, c-format #, fuzzy, c-format
msgid "Submitting payment" msgid "Submitting payment"
msgstr "Bezahlung bestätigen" msgstr "Bezahlung bestätigen"
#: src/webex/pages/confirm-contract.tsx:343 #: src/webex/pages/pay.tsx:343
#, c-format #, c-format
msgid "" msgid ""
"You already paid for this, clicking \"Confirm payment\" will not cost money " "You already paid for this, clicking \"Confirm payment\" will not cost money "
"again." "again."
msgstr "" msgstr ""
#: src/webex/pages/confirm-contract.tsx:357 #: src/webex/pages/pay.tsx:357
#, fuzzy, c-format #, fuzzy, c-format
msgid "Aborting payment ..." msgid "Aborting payment ..."
msgstr "Bezahlung bestätigen" msgstr "Bezahlung bestätigen"
#: src/webex/pages/confirm-contract.tsx:359 #: src/webex/pages/pay.tsx:359
#, c-format #, c-format
msgid "Payment aborted!" msgid "Payment aborted!"
msgstr "" msgstr ""
#: src/webex/pages/confirm-contract.tsx:362 #: src/webex/pages/pay.tsx:362
#, fuzzy, c-format #, fuzzy, c-format
msgid "Retry Payment" msgid "Retry Payment"
msgstr "Bezahlung bestätigen" msgstr "Bezahlung bestätigen"
#: src/webex/pages/confirm-contract.tsx:365 #: src/webex/pages/pay.tsx:365
#, fuzzy, c-format #, fuzzy, c-format
msgid "Abort Payment" msgid "Abort Payment"
msgstr "Bezahlung bestätigen" msgstr "Bezahlung bestätigen"
#: src/webex/pages/confirm-contract.tsx:374 #: src/webex/pages/pay.tsx:374
#, fuzzy, c-format #, fuzzy, c-format
msgid "The merchant %1$s offers you to purchase:" 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." 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 #, c-format
msgid "The total price is %1$s (plus %2$s fees)." msgid "The total price is %1$s (plus %2$s fees)."
msgstr "" msgstr ""
#: src/webex/pages/confirm-contract.tsx:387 #: src/webex/pages/pay.tsx:387
#, c-format #, c-format
msgid "The total price is %1$s." msgid "The total price is %1$s."
msgstr "" 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 #: src/webex/pages/popup.tsx:165
#, c-format #, c-format
msgid "Balance" msgid "Balance"
@ -379,6 +257,127 @@ msgstr "Bezahlung bestätigen"
msgid "Cancel" msgid "Cancel"
msgstr "Saldo" 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 #: src/webex/renderHtml.tsx:225
#, fuzzy, c-format #, fuzzy, c-format
msgid "Withdrawal fees:" msgid "Withdrawal fees:"

View File

@ -37,28 +37,28 @@ msgstr ""
msgid "time (ms/op)" msgid "time (ms/op)"
msgstr "" msgstr ""
#: src/webex/pages/confirm-contract.tsx:78 #: src/webex/pages/pay.tsx:78
#, c-format #, c-format
msgid "show more details" msgid "show more details"
msgstr "" msgstr ""
#: src/webex/pages/confirm-contract.tsx:92 #: src/webex/pages/pay.tsx:92
#, c-format #, c-format
msgid "Accepted exchanges:" msgid "Accepted exchanges:"
msgstr "" msgstr ""
#: src/webex/pages/confirm-contract.tsx:97 #: src/webex/pages/pay.tsx:97
#, c-format #, c-format
msgid "Exchanges in the wallet:" msgid "Exchanges in the wallet:"
msgstr "" msgstr ""
#: src/webex/pages/confirm-contract.tsx:219 #: src/webex/pages/pay.tsx:219
#, c-format #, c-format
msgid "You have insufficient funds of the requested currency in your wallet." msgid "You have insufficient funds of the requested currency in your wallet."
msgstr "" msgstr ""
#. tslint:disable-next-line:max-line-length #. tslint:disable-next-line:max-line-length
#: src/webex/pages/confirm-contract.tsx:221 #: src/webex/pages/pay.tsx:221
#, c-format #, c-format
msgid "" msgid ""
"You do not have any funds from an exchange that is accepted by this " "You do not have any funds from an exchange that is accepted by this "
@ -66,180 +66,58 @@ msgid ""
"wallet." "wallet."
msgstr "" msgstr ""
#: src/webex/pages/confirm-contract.tsx:322 #: src/webex/pages/pay.tsx:322
#, c-format #, c-format
msgid "Confirm payment" msgid "Confirm payment"
msgstr "" msgstr ""
#: src/webex/pages/confirm-contract.tsx:332 #: src/webex/pages/pay.tsx:332
#, c-format #, c-format
msgid "Submitting payment" msgid "Submitting payment"
msgstr "" msgstr ""
#: src/webex/pages/confirm-contract.tsx:343 #: src/webex/pages/pay.tsx:343
#, c-format #, c-format
msgid "" msgid ""
"You already paid for this, clicking \"Confirm payment\" will not cost money " "You already paid for this, clicking \"Confirm payment\" will not cost money "
"again." "again."
msgstr "" msgstr ""
#: src/webex/pages/confirm-contract.tsx:357 #: src/webex/pages/pay.tsx:357
#, c-format #, c-format
msgid "Aborting payment ..." msgid "Aborting payment ..."
msgstr "" msgstr ""
#: src/webex/pages/confirm-contract.tsx:359 #: src/webex/pages/pay.tsx:359
#, c-format #, c-format
msgid "Payment aborted!" msgid "Payment aborted!"
msgstr "" msgstr ""
#: src/webex/pages/confirm-contract.tsx:362 #: src/webex/pages/pay.tsx:362
#, c-format #, c-format
msgid "Retry Payment" msgid "Retry Payment"
msgstr "" msgstr ""
#: src/webex/pages/confirm-contract.tsx:365 #: src/webex/pages/pay.tsx:365
#, c-format #, c-format
msgid "Abort Payment" msgid "Abort Payment"
msgstr "" msgstr ""
#: src/webex/pages/confirm-contract.tsx:374 #: src/webex/pages/pay.tsx:374
#, c-format #, c-format
msgid "The merchant %1$s offers you to purchase:" msgid "The merchant %1$s offers you to purchase:"
msgstr "" msgstr ""
#: src/webex/pages/confirm-contract.tsx:383 #: src/webex/pages/pay.tsx:383
#, c-format #, c-format
msgid "The total price is %1$s (plus %2$s fees)." msgid "The total price is %1$s (plus %2$s fees)."
msgstr "" msgstr ""
#: src/webex/pages/confirm-contract.tsx:387 #: src/webex/pages/pay.tsx:387
#, c-format #, c-format
msgid "The total price is %1$s." msgid "The total price is %1$s."
msgstr "" 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 #: src/webex/pages/popup.tsx:165
#, c-format #, c-format
msgid "Balance" msgid "Balance"
@ -370,6 +248,127 @@ msgstr ""
msgid "Cancel" msgid "Cancel"
msgstr "" 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 #: src/webex/renderHtml.tsx:225
#, c-format #, c-format
msgid "Withdrawal fees:" msgid "Withdrawal fees:"

View File

@ -37,28 +37,28 @@ msgstr ""
msgid "time (ms/op)" msgid "time (ms/op)"
msgstr "" msgstr ""
#: src/webex/pages/confirm-contract.tsx:78 #: src/webex/pages/pay.tsx:78
#, c-format #, c-format
msgid "show more details" msgid "show more details"
msgstr "" msgstr ""
#: src/webex/pages/confirm-contract.tsx:92 #: src/webex/pages/pay.tsx:92
#, c-format #, c-format
msgid "Accepted exchanges:" msgid "Accepted exchanges:"
msgstr "" msgstr ""
#: src/webex/pages/confirm-contract.tsx:97 #: src/webex/pages/pay.tsx:97
#, c-format #, c-format
msgid "Exchanges in the wallet:" msgid "Exchanges in the wallet:"
msgstr "" msgstr ""
#: src/webex/pages/confirm-contract.tsx:219 #: src/webex/pages/pay.tsx:219
#, c-format #, c-format
msgid "You have insufficient funds of the requested currency in your wallet." msgid "You have insufficient funds of the requested currency in your wallet."
msgstr "" msgstr ""
#. tslint:disable-next-line:max-line-length #. tslint:disable-next-line:max-line-length
#: src/webex/pages/confirm-contract.tsx:221 #: src/webex/pages/pay.tsx:221
#, c-format #, c-format
msgid "" msgid ""
"You do not have any funds from an exchange that is accepted by this " "You do not have any funds from an exchange that is accepted by this "
@ -66,180 +66,58 @@ msgid ""
"wallet." "wallet."
msgstr "" msgstr ""
#: src/webex/pages/confirm-contract.tsx:322 #: src/webex/pages/pay.tsx:322
#, c-format #, c-format
msgid "Confirm payment" msgid "Confirm payment"
msgstr "" msgstr ""
#: src/webex/pages/confirm-contract.tsx:332 #: src/webex/pages/pay.tsx:332
#, c-format #, c-format
msgid "Submitting payment" msgid "Submitting payment"
msgstr "" msgstr ""
#: src/webex/pages/confirm-contract.tsx:343 #: src/webex/pages/pay.tsx:343
#, c-format #, c-format
msgid "" msgid ""
"You already paid for this, clicking \"Confirm payment\" will not cost money " "You already paid for this, clicking \"Confirm payment\" will not cost money "
"again." "again."
msgstr "" msgstr ""
#: src/webex/pages/confirm-contract.tsx:357 #: src/webex/pages/pay.tsx:357
#, c-format #, c-format
msgid "Aborting payment ..." msgid "Aborting payment ..."
msgstr "" msgstr ""
#: src/webex/pages/confirm-contract.tsx:359 #: src/webex/pages/pay.tsx:359
#, c-format #, c-format
msgid "Payment aborted!" msgid "Payment aborted!"
msgstr "" msgstr ""
#: src/webex/pages/confirm-contract.tsx:362 #: src/webex/pages/pay.tsx:362
#, c-format #, c-format
msgid "Retry Payment" msgid "Retry Payment"
msgstr "" msgstr ""
#: src/webex/pages/confirm-contract.tsx:365 #: src/webex/pages/pay.tsx:365
#, c-format #, c-format
msgid "Abort Payment" msgid "Abort Payment"
msgstr "" msgstr ""
#: src/webex/pages/confirm-contract.tsx:374 #: src/webex/pages/pay.tsx:374
#, c-format #, c-format
msgid "The merchant %1$s offers you to purchase:" msgid "The merchant %1$s offers you to purchase:"
msgstr "" msgstr ""
#: src/webex/pages/confirm-contract.tsx:383 #: src/webex/pages/pay.tsx:383
#, c-format #, c-format
msgid "The total price is %1$s (plus %2$s fees)." msgid "The total price is %1$s (plus %2$s fees)."
msgstr "" msgstr ""
#: src/webex/pages/confirm-contract.tsx:387 #: src/webex/pages/pay.tsx:387
#, c-format #, c-format
msgid "The total price is %1$s." msgid "The total price is %1$s."
msgstr "" 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 #: src/webex/pages/popup.tsx:165
#, c-format #, c-format
msgid "Balance" msgid "Balance"
@ -370,6 +248,127 @@ msgstr ""
msgid "Cancel" msgid "Cancel"
msgstr "" 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 #: src/webex/renderHtml.tsx:225
#, c-format #, c-format
msgid "Withdrawal fees:" msgid "Withdrawal fees:"

View File

@ -37,28 +37,28 @@ msgstr ""
msgid "time (ms/op)" msgid "time (ms/op)"
msgstr "" msgstr ""
#: src/webex/pages/confirm-contract.tsx:78 #: src/webex/pages/pay.tsx:78
#, c-format #, c-format
msgid "show more details" msgid "show more details"
msgstr "" msgstr ""
#: src/webex/pages/confirm-contract.tsx:92 #: src/webex/pages/pay.tsx:92
#, c-format #, c-format
msgid "Accepted exchanges:" msgid "Accepted exchanges:"
msgstr "" msgstr ""
#: src/webex/pages/confirm-contract.tsx:97 #: src/webex/pages/pay.tsx:97
#, c-format #, c-format
msgid "Exchanges in the wallet:" msgid "Exchanges in the wallet:"
msgstr "" msgstr ""
#: src/webex/pages/confirm-contract.tsx:219 #: src/webex/pages/pay.tsx:219
#, c-format #, c-format
msgid "You have insufficient funds of the requested currency in your wallet." msgid "You have insufficient funds of the requested currency in your wallet."
msgstr "" msgstr ""
#. tslint:disable-next-line:max-line-length #. tslint:disable-next-line:max-line-length
#: src/webex/pages/confirm-contract.tsx:221 #: src/webex/pages/pay.tsx:221
#, c-format #, c-format
msgid "" msgid ""
"You do not have any funds from an exchange that is accepted by this " "You do not have any funds from an exchange that is accepted by this "
@ -66,180 +66,58 @@ msgid ""
"wallet." "wallet."
msgstr "" msgstr ""
#: src/webex/pages/confirm-contract.tsx:322 #: src/webex/pages/pay.tsx:322
#, c-format #, c-format
msgid "Confirm payment" msgid "Confirm payment"
msgstr "" msgstr ""
#: src/webex/pages/confirm-contract.tsx:332 #: src/webex/pages/pay.tsx:332
#, c-format #, c-format
msgid "Submitting payment" msgid "Submitting payment"
msgstr "" msgstr ""
#: src/webex/pages/confirm-contract.tsx:343 #: src/webex/pages/pay.tsx:343
#, c-format #, c-format
msgid "" msgid ""
"You already paid for this, clicking \"Confirm payment\" will not cost money " "You already paid for this, clicking \"Confirm payment\" will not cost money "
"again." "again."
msgstr "" msgstr ""
#: src/webex/pages/confirm-contract.tsx:357 #: src/webex/pages/pay.tsx:357
#, c-format #, c-format
msgid "Aborting payment ..." msgid "Aborting payment ..."
msgstr "" msgstr ""
#: src/webex/pages/confirm-contract.tsx:359 #: src/webex/pages/pay.tsx:359
#, c-format #, c-format
msgid "Payment aborted!" msgid "Payment aborted!"
msgstr "" msgstr ""
#: src/webex/pages/confirm-contract.tsx:362 #: src/webex/pages/pay.tsx:362
#, c-format #, c-format
msgid "Retry Payment" msgid "Retry Payment"
msgstr "" msgstr ""
#: src/webex/pages/confirm-contract.tsx:365 #: src/webex/pages/pay.tsx:365
#, c-format #, c-format
msgid "Abort Payment" msgid "Abort Payment"
msgstr "" msgstr ""
#: src/webex/pages/confirm-contract.tsx:374 #: src/webex/pages/pay.tsx:374
#, c-format #, c-format
msgid "The merchant %1$s offers you to purchase:" msgid "The merchant %1$s offers you to purchase:"
msgstr "" msgstr ""
#: src/webex/pages/confirm-contract.tsx:383 #: src/webex/pages/pay.tsx:383
#, c-format #, c-format
msgid "The total price is %1$s (plus %2$s fees)." msgid "The total price is %1$s (plus %2$s fees)."
msgstr "" msgstr ""
#: src/webex/pages/confirm-contract.tsx:387 #: src/webex/pages/pay.tsx:387
#, c-format #, c-format
msgid "The total price is %1$s." msgid "The total price is %1$s."
msgstr "" 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 #: src/webex/pages/popup.tsx:165
#, c-format #, c-format
msgid "Balance" msgid "Balance"
@ -370,6 +248,127 @@ msgstr ""
msgid "Cancel" msgid "Cancel"
msgstr "" 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 #: src/webex/renderHtml.tsx:225
#, c-format #, c-format
msgid "Withdrawal fees:" msgid "Withdrawal fees:"

View File

@ -92,90 +92,6 @@ strings['de'] = {
null, 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": [ "Balance": [
null, null,
"Saldo" "Saldo"
@ -280,6 +196,90 @@ strings['de'] = {
null, null,
"Saldo" "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:": [ "Withdrawal fees:": [
null, null,
"Abheben bei" "Abheben bei"
@ -408,90 +408,6 @@ strings['en-US'] = {
null, 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": [ "Balance": [
null, null,
"" ""
@ -596,6 +512,90 @@ strings['en-US'] = {
null, 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:": [ "Withdrawal fees:": [
null, null,
"" ""
@ -724,90 +724,6 @@ strings['fr'] = {
null, 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": [ "Balance": [
null, null,
"" ""
@ -912,6 +828,90 @@ strings['fr'] = {
null, 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:": [ "Withdrawal fees:": [
null, null,
"" ""
@ -1040,90 +1040,6 @@ strings['it'] = {
null, 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": [ "Balance": [
null, null,
"" ""
@ -1228,6 +1144,90 @@ strings['it'] = {
null, 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:": [ "Withdrawal fees:": [
null, null,
"" ""
@ -1356,90 +1356,6 @@ strings['sv'] = {
null, null,
"Det totala priset är %1$s." "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": [ "Balance": [
null, null,
"Balans" "Balans"
@ -1544,6 +1460,90 @@ strings['sv'] = {
null, null,
"Avbryt" "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:": [ "Withdrawal fees:": [
null, null,
"Utbetalnings avgifter:" "Utbetalnings avgifter:"

View File

@ -37,28 +37,28 @@ msgstr ""
msgid "time (ms/op)" msgid "time (ms/op)"
msgstr "" msgstr ""
#: src/webex/pages/confirm-contract.tsx:78 #: src/webex/pages/pay.tsx:78
#, fuzzy, c-format #, fuzzy, c-format
msgid "show more details" msgid "show more details"
msgstr "visa mer" msgstr "visa mer"
#: src/webex/pages/confirm-contract.tsx:92 #: src/webex/pages/pay.tsx:92
#, c-format #, c-format
msgid "Accepted exchanges:" msgid "Accepted exchanges:"
msgstr "Accepterade tjänsteleverantörer:" msgstr "Accepterade tjänsteleverantörer:"
#: src/webex/pages/confirm-contract.tsx:97 #: src/webex/pages/pay.tsx:97
#, c-format #, c-format
msgid "Exchanges in the wallet:" msgid "Exchanges in the wallet:"
msgstr "Tjänsteleverantörer i plånboken:" msgstr "Tjänsteleverantörer i plånboken:"
#: src/webex/pages/confirm-contract.tsx:219 #: src/webex/pages/pay.tsx:219
#, c-format #, c-format
msgid "You have insufficient funds of the requested currency in your wallet." msgid "You have insufficient funds of the requested currency in your wallet."
msgstr "plånboken" msgstr "plånboken"
#. tslint:disable-next-line:max-line-length #. tslint:disable-next-line:max-line-length
#: src/webex/pages/confirm-contract.tsx:221 #: src/webex/pages/pay.tsx:221
#, c-format #, c-format
msgid "" msgid ""
"You do not have any funds from an exchange that is accepted by this " "You do not have any funds from an exchange that is accepted by this "
@ -66,17 +66,17 @@ msgid ""
"wallet." "wallet."
msgstr "plånboken" msgstr "plånboken"
#: src/webex/pages/confirm-contract.tsx:322 #: src/webex/pages/pay.tsx:322
#, c-format #, c-format
msgid "Confirm payment" msgid "Confirm payment"
msgstr "Godkän betalning" msgstr "Godkän betalning"
#: src/webex/pages/confirm-contract.tsx:332 #: src/webex/pages/pay.tsx:332
#, c-format #, c-format
msgid "Submitting payment" msgid "Submitting payment"
msgstr "Bekräftar betalning" msgstr "Bekräftar betalning"
#: src/webex/pages/confirm-contract.tsx:343 #: src/webex/pages/pay.tsx:343
#, c-format #, c-format
msgid "" msgid ""
"You already paid for this, clicking \"Confirm payment\" will not cost money " "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\" " "Du har redan betalat för det här, om du trycker \"Godkän betalning\" "
"debiteras du inte igen" "debiteras du inte igen"
#: src/webex/pages/confirm-contract.tsx:357 #: src/webex/pages/pay.tsx:357
#, fuzzy, c-format #, fuzzy, c-format
msgid "Aborting payment ..." msgid "Aborting payment ..."
msgstr "Bekräftar betalning" msgstr "Bekräftar betalning"
#: src/webex/pages/confirm-contract.tsx:359 #: src/webex/pages/pay.tsx:359
#, c-format #, c-format
msgid "Payment aborted!" msgid "Payment aborted!"
msgstr "" msgstr ""
#: src/webex/pages/confirm-contract.tsx:362 #: src/webex/pages/pay.tsx:362
#, c-format #, c-format
msgid "Retry Payment" msgid "Retry Payment"
msgstr "" msgstr ""
#: src/webex/pages/confirm-contract.tsx:365 #: src/webex/pages/pay.tsx:365
#, fuzzy, c-format #, fuzzy, c-format
msgid "Abort Payment" msgid "Abort Payment"
msgstr "Godkän betalning" msgstr "Godkän betalning"
#: src/webex/pages/confirm-contract.tsx:374 #: src/webex/pages/pay.tsx:374
#, fuzzy, c-format #, fuzzy, c-format
msgid "The merchant %1$s offers you to purchase:" msgid "The merchant %1$s offers you to purchase:"
msgstr "Säljaren %1$s erbjuder följande:" msgstr "Säljaren %1$s erbjuder följande:"
#: src/webex/pages/confirm-contract.tsx:383 #: src/webex/pages/pay.tsx:383
#, fuzzy, c-format #, fuzzy, c-format
msgid "The total price is %1$s (plus %2$s fees)." msgid "The total price is %1$s (plus %2$s fees)."
msgstr "Det totala priset är %1$s (plus %2$s avgifter).\n" 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 #, fuzzy, c-format
msgid "The total price is %1$s." msgid "The total price is %1$s."
msgstr "Det totala priset är %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 #: src/webex/pages/popup.tsx:165
#, c-format #, c-format
msgid "Balance" msgid "Balance"
@ -376,6 +252,129 @@ msgstr "Bekräfta"
msgid "Cancel" msgid "Cancel"
msgstr "Avbryt" 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 #: src/webex/renderHtml.tsx:225
#, c-format #, c-format
msgid "Withdrawal fees:" msgid "Withdrawal fees:"

View File

@ -37,28 +37,28 @@ msgstr ""
msgid "time (ms/op)" msgid "time (ms/op)"
msgstr "" msgstr ""
#: src/webex/pages/confirm-contract.tsx:78 #: src/webex/pages/pay.tsx:78
#, c-format #, c-format
msgid "show more details" msgid "show more details"
msgstr "" msgstr ""
#: src/webex/pages/confirm-contract.tsx:92 #: src/webex/pages/pay.tsx:92
#, c-format #, c-format
msgid "Accepted exchanges:" msgid "Accepted exchanges:"
msgstr "" msgstr ""
#: src/webex/pages/confirm-contract.tsx:97 #: src/webex/pages/pay.tsx:97
#, c-format #, c-format
msgid "Exchanges in the wallet:" msgid "Exchanges in the wallet:"
msgstr "" msgstr ""
#: src/webex/pages/confirm-contract.tsx:219 #: src/webex/pages/pay.tsx:219
#, c-format #, c-format
msgid "You have insufficient funds of the requested currency in your wallet." msgid "You have insufficient funds of the requested currency in your wallet."
msgstr "" msgstr ""
#. tslint:disable-next-line:max-line-length #. tslint:disable-next-line:max-line-length
#: src/webex/pages/confirm-contract.tsx:221 #: src/webex/pages/pay.tsx:221
#, c-format #, c-format
msgid "" msgid ""
"You do not have any funds from an exchange that is accepted by this " "You do not have any funds from an exchange that is accepted by this "
@ -66,180 +66,58 @@ msgid ""
"wallet." "wallet."
msgstr "" msgstr ""
#: src/webex/pages/confirm-contract.tsx:322 #: src/webex/pages/pay.tsx:322
#, c-format #, c-format
msgid "Confirm payment" msgid "Confirm payment"
msgstr "" msgstr ""
#: src/webex/pages/confirm-contract.tsx:332 #: src/webex/pages/pay.tsx:332
#, c-format #, c-format
msgid "Submitting payment" msgid "Submitting payment"
msgstr "" msgstr ""
#: src/webex/pages/confirm-contract.tsx:343 #: src/webex/pages/pay.tsx:343
#, c-format #, c-format
msgid "" msgid ""
"You already paid for this, clicking \"Confirm payment\" will not cost money " "You already paid for this, clicking \"Confirm payment\" will not cost money "
"again." "again."
msgstr "" msgstr ""
#: src/webex/pages/confirm-contract.tsx:357 #: src/webex/pages/pay.tsx:357
#, c-format #, c-format
msgid "Aborting payment ..." msgid "Aborting payment ..."
msgstr "" msgstr ""
#: src/webex/pages/confirm-contract.tsx:359 #: src/webex/pages/pay.tsx:359
#, c-format #, c-format
msgid "Payment aborted!" msgid "Payment aborted!"
msgstr "" msgstr ""
#: src/webex/pages/confirm-contract.tsx:362 #: src/webex/pages/pay.tsx:362
#, c-format #, c-format
msgid "Retry Payment" msgid "Retry Payment"
msgstr "" msgstr ""
#: src/webex/pages/confirm-contract.tsx:365 #: src/webex/pages/pay.tsx:365
#, c-format #, c-format
msgid "Abort Payment" msgid "Abort Payment"
msgstr "" msgstr ""
#: src/webex/pages/confirm-contract.tsx:374 #: src/webex/pages/pay.tsx:374
#, c-format #, c-format
msgid "The merchant %1$s offers you to purchase:" msgid "The merchant %1$s offers you to purchase:"
msgstr "" msgstr ""
#: src/webex/pages/confirm-contract.tsx:383 #: src/webex/pages/pay.tsx:383
#, c-format #, c-format
msgid "The total price is %1$s (plus %2$s fees)." msgid "The total price is %1$s (plus %2$s fees)."
msgstr "" msgstr ""
#: src/webex/pages/confirm-contract.tsx:387 #: src/webex/pages/pay.tsx:387
#, c-format #, c-format
msgid "The total price is %1$s." msgid "The total price is %1$s."
msgstr "" 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 #: src/webex/pages/popup.tsx:165
#, c-format #, c-format
msgid "Balance" msgid "Balance"
@ -370,6 +248,127 @@ msgstr ""
msgid "Cancel" msgid "Cancel"
msgstr "" 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 #: src/webex/renderHtml.tsx:225
#, c-format #, c-format
msgid "Withdrawal fees:" msgid "Withdrawal fees:"

View File

@ -105,6 +105,8 @@ import {
WalletBalanceEntry, WalletBalanceEntry,
PreparePayResult, PreparePayResult,
DownloadedWithdrawInfo, DownloadedWithdrawInfo,
WithdrawDetails,
AcceptWithdrawalResponse,
} from "./walletTypes"; } from "./walletTypes";
import { openPromise } from "./promiseUtils"; import { openPromise } from "./promiseUtils";
import { parsePayUri, parseWithdrawUri } from "./taleruri"; import { parsePayUri, parseWithdrawUri } from "./taleruri";
@ -717,6 +719,12 @@ export class Wallet {
return t; 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> { async preparePay(url: string): Promise<PreparePayResult> {
const uriResult = parsePayUri(url); const uriResult = parsePayUri(url);
@ -760,17 +768,20 @@ export class Wallet {
uriResult.sessionId, uriResult.sessionId,
); );
return { return {
status: "session-replayed", status: "paid",
contractTerms: existingPayment.contractTerms, contractTerms: existingPayment.contractTerms,
nextUrl: this.getNextUrl(existingPayment.contractTerms),
}; };
} }
} }
if (checkResult.status === "paid") { if (checkResult.status === "paid") {
const nextUrl = this.getNextUrl(proposal.contractTerms);
return { return {
status: "paid", status: "paid",
contractTerms: proposal.contractTerms, contractTerms: proposal.contractTerms,
proposalId: proposal.id!, proposalId: proposal.id!,
nextUrl,
}; };
} }
if (checkResult.status === "insufficient-balance") { if (checkResult.status === "insufficient-balance") {
@ -912,14 +923,6 @@ export class Wallet {
modifiedCoins.push(c); 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() await this.q()
.putAll(Stores.coins, modifiedCoins) .putAll(Stores.coins, modifiedCoins)
.put(Stores.purchases, purchase) .put(Stores.purchases, purchase)
@ -928,7 +931,7 @@ export class Wallet {
this.refresh(c.coin_pub); this.refresh(c.coin_pub);
} }
const nextUrl = fu.href(); const nextUrl = this.getNextUrl(purchase.contractTerms);
this.cachedNextUrl[purchase.contractTerms.fulfillment_url] = { this.cachedNextUrl[purchase.contractTerms.fulfillment_url] = {
nextUrl, nextUrl,
lastSessionId: sessionId, lastSessionId: sessionId,
@ -1150,6 +1153,54 @@ export class Wallet {
return t; 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, * First fetch information requred to withdraw from the reserve,
* then deplete the reserve, withdrawing coins until it is empty. * then deplete the reserve, withdrawing coins until it is empty.
@ -1192,41 +1243,10 @@ export class Wallet {
); );
} }
maxTimeout = 2000; maxTimeout = 2000;
const now = new Date().getTime(); /* This path is only taken if the wallet crashed after a withdraw was accepted,
let status; * and before the information could be sent to the bank. */
try { await this.sendReserveInfoToBank(reservePub);
const statusResp = await this.http.get(bankStatusUrl); throw Error("waiting for reserve to be confirmed");
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;
});
throw Error("waiting for reserve to be confirmed");
}
} }
const updatedReserve = await this.updateReserve(reservePub); const updatedReserve = await this.updateReserve(reservePub);
@ -1836,6 +1856,24 @@ export class Wallet {
return { isTrusted, isAudited }; 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( async getReserveCreationInfo(
baseUrl: string, baseUrl: string,
amount: AmountJson, 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 * Remove unreferenced / expired data from the wallet's database
* based on the current system time. * based on the current system time.
@ -3557,10 +3585,10 @@ export class Wallet {
}; };
} }
async createReserveFromWithdrawUrl( async acceptWithdrawal(
talerWithdrawUri: string, talerWithdrawUri: string,
selectedExchange: string, selectedExchange: string,
): Promise<{ reservePub: string; confirmTransferUrl?: string }> { ): Promise<AcceptWithdrawalResponse> {
const withdrawInfo = await this.downloadWithdrawInfo(talerWithdrawUri); const withdrawInfo = await this.downloadWithdrawInfo(talerWithdrawUri);
const exchangeWire = await this.getExchangePaytoUri( const exchangeWire = await this.getExchangePaytoUri(
selectedExchange, selectedExchange,
@ -3573,6 +3601,7 @@ export class Wallet {
senderWire: withdrawInfo.senderWire, senderWire: withdrawInfo.senderWire,
exchangeWire: exchangeWire, exchangeWire: exchangeWire,
}); });
await this.sendReserveInfoToBank(reserve.reservePub);
return { return {
reservePub: reserve.reservePub, reservePub: reserve.reservePub,
confirmTransferUrl: withdrawInfo.confirmTransferUrl, confirmTransferUrl: withdrawInfo.confirmTransferUrl,

View File

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

View File

@ -32,12 +32,12 @@ import { UpgradeResponse } from "./wxApi";
* Message type information. * Message type information.
*/ */
export interface MessageMap { export interface MessageMap {
"balances": { balances: {
request: { }; request: {};
response: walletTypes.WalletBalance; response: walletTypes.WalletBalance;
}; };
"dump-db": { "dump-db": {
request: { }; request: {};
response: any; response: any;
}; };
"import-db": { "import-db": {
@ -46,18 +46,18 @@ export interface MessageMap {
}; };
response: void; response: void;
}; };
"ping": { ping: {
request: { }; request: {};
response: void; response: void;
}; };
"reset-db": { "reset-db": {
request: { }; request: {};
response: void; response: void;
}; };
"create-reserve": { "create-reserve": {
request: { request: {
amount: AmountJson; amount: AmountJson;
exchange: string exchange: string;
}; };
response: void; response: void;
}; };
@ -70,11 +70,11 @@ export interface MessageMap {
response: walletTypes.ConfirmPayResult; response: walletTypes.ConfirmPayResult;
}; };
"check-pay": { "check-pay": {
request: { proposalId: number; }; request: { proposalId: number };
response: walletTypes.CheckPayResult; response: walletTypes.CheckPayResult;
}; };
"query-payment": { "query-payment": {
request: { }; request: {};
response: dbTypes.PurchaseRecord; response: dbTypes.PurchaseRecord;
}; };
"exchange-info": { "exchange-info": {
@ -90,11 +90,11 @@ export interface MessageMap {
response: string; response: string;
}; };
"reserve-creation-info": { "reserve-creation-info": {
request: { baseUrl: string, amount: AmountJson }; request: { baseUrl: string; amount: AmountJson };
response: walletTypes.ReserveCreationInfo; response: walletTypes.ReserveCreationInfo;
}; };
"get-history": { "get-history": {
request: { }; request: {};
response: walletTypes.HistoryRecord[]; response: walletTypes.HistoryRecord[];
}; };
"get-proposal": { "get-proposal": {
@ -110,7 +110,7 @@ export interface MessageMap {
response: any; response: any;
}; };
"get-currencies": { "get-currencies": {
request: { }; request: {};
response: dbTypes.CurrencyRecord[]; response: dbTypes.CurrencyRecord[];
}; };
"update-currency": { "update-currency": {
@ -118,7 +118,7 @@ export interface MessageMap {
response: void; response: void;
}; };
"get-exchanges": { "get-exchanges": {
request: { }; request: {};
response: dbTypes.ExchangeRecord[]; response: dbTypes.ExchangeRecord[];
}; };
"get-reserves": { "get-reserves": {
@ -126,7 +126,7 @@ export interface MessageMap {
response: dbTypes.ReserveRecord[]; response: dbTypes.ReserveRecord[];
}; };
"get-payback-reserves": { "get-payback-reserves": {
request: { }; request: {};
response: dbTypes.ReserveRecord[]; response: dbTypes.ReserveRecord[];
}; };
"withdraw-payback-reserve": { "withdraw-payback-reserve": {
@ -146,15 +146,15 @@ export interface MessageMap {
response: void; response: void;
}; };
"check-upgrade": { "check-upgrade": {
request: { }; request: {};
response: UpgradeResponse; response: UpgradeResponse;
}; };
"get-sender-wire-infos": { "get-sender-wire-infos": {
request: { }; request: {};
response: walletTypes.SenderWireInfos; response: walletTypes.SenderWireInfos;
}; };
"return-coins": { "return-coins": {
request: { }; request: {};
response: void; response: void;
}; };
"log-and-display-error": { "log-and-display-error": {
@ -182,7 +182,7 @@ export interface MessageMap {
response: walletTypes.TipStatus; response: walletTypes.TipStatus;
}; };
"clear-notification": { "clear-notification": {
request: { }; request: {};
response: void; response: void;
}; };
"taler-pay": { "taler-pay": {
@ -194,23 +194,36 @@ export interface MessageMap {
response: number; response: number;
}; };
"submit-pay": { "submit-pay": {
request: { contractTermsHash: string, sessionId: string | undefined }; request: { contractTermsHash: string; sessionId: string | undefined };
response: walletTypes.ConfirmPayResult; response: walletTypes.ConfirmPayResult;
}; };
"accept-refund": { "accept-refund": {
request: { refundUrl: string } request: { refundUrl: string };
response: string; response: string;
}; };
"abort-failed-payment": { "abort-failed-payment": {
request: { contractTermsHash: string } request: { contractTermsHash: string };
response: void; response: void;
}; };
"benchmark-crypto": { "benchmark-crypto": {
request: { repetitions: number } request: { repetitions: number };
response: walletTypes.BenchmarkResult; 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. * String literal types for messages.
*/ */
@ -219,14 +232,19 @@ export type MessageType = keyof MessageMap;
/** /**
* Make a request whose details match the request type. * 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 }; return { type, details };
} }
/** /**
* Make a response that matches the request type. * 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; 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"> <link rel="icon" href="/img/icon.png">
<script src="/dist/page-common-bundle.js"></script> <script src="/dist/page-common-bundle.js"></script>
<script src="/dist/confirm-contract-bundle.js"></script> <script src="/dist/pay-bundle.js"></script>
<style> <style>
button.accept { 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"> <link rel="stylesheet" type="text/css" href="../style/wallet.css">
<script src="/dist/page-common-bundle.js"></script> <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> </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; cursor:pointer;
} }
.blacklink a:link, .blacklink a:visited, .blacklink a:hover, .blacklink a:active {
color: #000;
}
table, th, td { table, th, td {
border: 1px solid black; border: 1px solid black;
} }

View File

@ -79,6 +79,8 @@ export interface UpgradeResponse {
export class WalletApiError extends Error { export class WalletApiError extends Error {
constructor(message: string, public detail: any) { constructor(message: string, public detail: any) {
super(message); 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> { export function benchmarkCrypto(repetitions: number): Promise<BenchmarkResult> {
return callBackend("benchmark-crypto", { repetitions }); 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); 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: default:
// Exhaustiveness check. // Exhaustiveness check.
// See https://www.typescriptlang.org/docs/handbook/advanced-types.html // See https://www.typescriptlang.org/docs/handbook/advanced-types.html
@ -523,190 +537,6 @@ function makeSyncWalletRedirect(
return { redirectUrl: outerUrl.href() }; 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 // Rate limit cache for executePayment operations, to break redirect loops
let rateLimitCache: { [n: number]: number } = {}; let rateLimitCache: { [n: number]: number } = {};
@ -931,19 +761,59 @@ export async function wxMain() {
} }
if (details.statusCode === 402) { if (details.statusCode === 402) {
console.log(`got 402 from ${details.url}`); console.log(`got 402 from ${details.url}`);
return handleHttpPayment( for (let header of details.responseHeaders || []) {
details.responseHeaders || [], if (header.name.toLowerCase() === "taler") {
details.url, const talerUri = header.value || "";
details.tabId, if (!talerUri.startsWith("taler://")) {
); console.warn(
} else if (details.statusCode === 202) { "Response with HTTP 402 has Taler header, but header value is not a taler:// URI.",
return handleBankRequest( );
wallet!, break;
details.responseHeaders || [], }
details.url, if (talerUri.startsWith("taler://withdraw/")) {
details.tabId, 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>"] }, { urls: ["<all_urls>"] },
["responseHeaders", "blocking"], ["responseHeaders", "blocking"],

View File

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

View File

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

1214
yarn.lock

File diff suppressed because it is too large Load Diff