rudimentary 402 handling

This commit is contained in:
Florian Dold 2016-09-08 17:26:31 +02:00
parent 4b67f220e7
commit a164ea9183
7 changed files with 170 additions and 207 deletions

View File

@ -31,7 +31,135 @@
namespace TalerNotify { namespace TalerNotify {
const PROTOCOL_VERSION = 1; const PROTOCOL_VERSION = 1;
console.log("Taler injected", chrome.runtime.id); /**
* Wallet-internal version of offerContractFrom, used for 402 payments.
*/
function internalOfferContractFrom(url: string) {
function handle_contract(contract_wrapper) {
var cEvent = new CustomEvent("taler-confirm-contract", {
detail: {
contract_wrapper: contract_wrapper,
replace_navigation: true
}
});
document.dispatchEvent(cEvent);
}
var contract_request = new XMLHttpRequest();
console.log("downloading contract from '" + url + "'")
contract_request.open("GET", url, true);
contract_request.onload = function (e) {
if (contract_request.readyState == 4) {
if (contract_request.status == 200) {
console.log("response text:",
contract_request.responseText);
var contract_wrapper = JSON.parse(contract_request.responseText);
if (!contract_wrapper) {
console.error("response text was invalid json");
alert("Failure to download contract (invalid json)");
return;
}
handle_contract(contract_wrapper);
} else {
alert("Failure to download contract from merchant " +
"(" + contract_request.status + "):\n" +
contract_request.responseText);
}
}
};
contract_request.onerror = function (e) {
alert("Failure requesting the contract:\n"
+ contract_request.statusText);
};
contract_request.send();
}
/**
* Wallet-internal version of executeContract, used for 402 payments.
*/
function internalExecuteContract(contractHash: string, payUrl: string,
offerUrl: string) {
/**
* Handle a failed payment.
*
* Try to notify the wallet first, before we show a potentially
* synchronous error message (such as an alert) or leave the page.
*/
function handleFailedPayment(status) {
let timeoutHandle = null;
function err() {
alert("Payment failed: Unexpected status code for $pay_url: " + status);
}
function onResp() {
if (timeoutHandle != null) {
clearTimeout(timeoutHandle);
timeoutHandle = null;
}
err();
}
function onTimeout() {
timeoutHandle = null;
err();
}
let eve = new CustomEvent('taler-payment-failed', {detail: {}});
document.dispatchEvent(eve);
document.addEventListener("taler-payment-failed-ok", onResp, false);
timeoutHandle = setTimeout(onTimeout, 200);
}
function handleResponse(evt) {
console.log("handling taler-notify-payment");
// Payment timeout in ms.
let timeout_ms = 1000;
// Current request.
let r;
let timeoutHandle = null;
function sendPay() {
r = new XMLHttpRequest();
r.open("post", payUrl);
r.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
r.send(JSON.stringify(evt.detail.payment));
r.onload = function() {
switch (r.status) {
case 200:
window.location.href = subst(evt.detail.contract.fulfillment_url,
evt.detail.H_contract);
window.location.reload(true);
break;
default:
handleFailedPayment(r.status);
break;
}
r = null;
if (timeoutHandle != null) {
clearTimeout(timeoutHandle);
timeoutHandle = null;
}
};
function retry() {
if (r) {
r.abort();
r = null;
}
timeout_ms = Math.min(timeout_ms * 2, 10 * 1000);
console.log("sendPay timed out, retrying in ", timeout_ms, "ms");
sendPay();
}
timeoutHandle = setTimeout(retry, timeout_ms);
}
sendPay();
}
let detail = {
H_contract: contractHash,
offering_url: offerUrl
};
document.addEventListener("taler-notify-payment", handleResponse, false);
let eve = new CustomEvent('taler-fetch-payment', {detail: detail});
document.dispatchEvent(eve);
}
function subst(url: string, H_contract) { function subst(url: string, H_contract) {
url = url.replace("${H_contract}", H_contract); url = url.replace("${H_contract}", H_contract);
@ -42,13 +170,12 @@ namespace TalerNotify {
const handlers = []; const handlers = [];
function init() { function init() {
chrome.runtime.sendMessage({type: "ping"}, () => { chrome.runtime.sendMessage({type: "ping"}, (resp) => {
if (chrome.runtime.lastError) { if (chrome.runtime.lastError) {
console.log("extension not yet ready"); console.log("extension not yet ready");
window.setTimeout(init, 200); window.setTimeout(init, 200);
return; return;
} }
console.log("got pong");
registerHandlers(); registerHandlers();
// Hack to know when the extension is unloaded // Hack to know when the extension is unloaded
let port = chrome.runtime.connect(); let port = chrome.runtime.connect();
@ -59,6 +186,19 @@ namespace TalerNotify {
document.removeEventListener(handler.type, handler.listener); document.removeEventListener(handler.type, handler.listener);
} }
}); });
console.log(resp);
if (resp.type === "fetch") {
console.log("it's fetch");
internalOfferContractFrom(resp.contractUrl);
document.documentElement.style.visibility = "hidden";
} else if (resp.type === "execute") {
console.log("it's execute");
document.documentElement.style.visibility = "hidden";
internalExecuteContract(resp.contractHash, resp.payUrl, resp.offerUrl);
}
}); });
} }

View File

@ -242,9 +242,16 @@ function getWithdrawDenomList(amountAvailable: AmountJson,
let remaining = Amounts.copy(amountAvailable); let remaining = Amounts.copy(amountAvailable);
let ds: Denomination[] = []; let ds: Denomination[] = [];
console.log("available denoms");
console.log(denoms);
denoms = denoms.filter(isWithdrawableDenom); denoms = denoms.filter(isWithdrawableDenom);
denoms.sort((d1, d2) => Amounts.cmp(d2.value, d1.value)); denoms.sort((d1, d2) => Amounts.cmp(d2.value, d1.value));
console.log("withdrawable denoms");
console.log(denoms);
// This is an arbitrary number of coins // This is an arbitrary number of coins
// we can withdraw in one go. It's not clear if this limit // we can withdraw in one go. It's not clear if this limit
// is useful ... // is useful ...

View File

@ -54,7 +54,7 @@ function makeHandlers(db: IDBDatabase,
return exportDb(db); return exportDb(db);
}, },
["ping"]: function(detail, sender) { ["ping"]: function(detail, sender) {
return Promise.resolve({}); return Promise.resolve(paymentRequestCache[sender.tab.id]);
}, },
["reset"]: function(detail, sender) { ["reset"]: function(detail, sender) {
if (db) { if (db) {
@ -237,8 +237,13 @@ class ChromeNotifier implements Notifier {
} }
/**
* Mapping from tab ID to payment information (if any).
*/
let paymentRequestCache = {};
function handleHttpPayment(headerList: chrome.webRequest.HttpHeader[], function handleHttpPayment(headerList: chrome.webRequest.HttpHeader[],
url): any { url: string, tabId: number): any {
const headers = {}; const headers = {};
for (let kv of headerList) { for (let kv of headerList) {
headers[kv.name.toLowerCase()] = kv.value; headers[kv.name.toLowerCase()] = kv.value;
@ -246,12 +251,8 @@ function handleHttpPayment(headerList: chrome.webRequest.HttpHeader[],
const contractUrl = headers["x-taler-contract-url"]; const contractUrl = headers["x-taler-contract-url"];
if (contractUrl !== undefined) { if (contractUrl !== undefined) {
// The web shop is proposing a contract, we need to fetch it paymentRequestCache[tabId] = {type: "fetch", contractUrl};
// and show it to the user return;
const walletUrl = URI(chrome.extension.getURL(
"pages/offer-contract-from.html"));
walletUrl.query({contractUrl});
return {redirectUrl: walletUrl.href()};
} }
const contractHash = headers["x-taler-contract-hash"]; const contractHash = headers["x-taler-contract-hash"];
@ -265,10 +266,13 @@ function handleHttpPayment(headerList: chrome.webRequest.HttpHeader[],
// Offer URL is optional // Offer URL is optional
const offerUrl = headers["x-taler-offer-url"]; const offerUrl = headers["x-taler-offer-url"];
const walletUrl = URI(chrome.extension.getURL( paymentRequestCache[tabId] = {
"pages/execute-payment.html")); type: "execute",
walletUrl.query({contractHash, offerUrl, payUrl}); offerUrl,
return {redirectUrl: walletUrl.href()}; payUrl,
contractHash
};
return;
} }
// looks like it's not a taler request, it might be // looks like it's not a taler request, it might be
@ -331,7 +335,10 @@ export function wxMain() {
if (details.statusCode != 402) { if (details.statusCode != 402) {
return; return;
} }
return handleHttpPayment(details.responseHeaders, details.url); console.log(`got 402 from ${details.url}`);
return handleHttpPayment(details.responseHeaders,
details.url,
details.tabId);
}, {urls: ["<all_urls>"]}, ["responseHeaders", "blocking"]); }, {urls: ["<all_urls>"]}, ["responseHeaders", "blocking"]);

View File

@ -1,80 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>Taler Wallet: Confirm Reserve Creation</title>
<link rel="stylesheet" type="text/css" href="../style/lang.css">
<script src="../lib/vendor/URI.js"></script>
<script src="../lib/vendor/mithril.js"></script>
<script src="../lib/vendor/lodash.core.min.js"></script>
<script src="../lib/vendor/system-csp-production.src.js"></script>
<script src="../lib/vendor/jed.js"></script>
<script src="../i18n/strings.js"></script>
<script src="../lib/i18n.js"></script>
<script src="../lib/module-trampoline.js"></script>
<style>
#main {
border: solid 1px black;
border-radius: 10px;
margin: auto;
max-width: 50%;
padding: 2em;
}
button.accept {
background-color: #5757D2;
border: 1px solid black;
border-radius: 5px;
margin: 1em 0;
padding: 0.5em;
font-weight: bold;
color: white;
}
button.linky {
background:none!important;
border:none;
padding:0!important;
font-family:arial,sans-serif;
color:#069;
text-decoration:underline;
cursor:pointer;
}
input.url {
width: 25em;
}
button.accept:disabled {
background-color: #dedbe8;
border: 1px solid white;
border-radius: 5px;
margin: 1em 0;
padding: 0.5em;
font-weight: bold;
color: #2C2C2C;
}
.errorbox {
border: 1px solid;
display: inline-block;
margin: 1em;
padding: 1em;
font-weight: bold;
background: #FF8A8A;
}
</style>
</head>
<body>
<section id="main">
<h1>GNU Taler Wallet</h1>
Executing payment ...
</section>
</body>
</html>

View File

@ -1,80 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>Taler Wallet: Confirm Reserve Creation</title>
<link rel="stylesheet" type="text/css" href="../style/lang.css">
<script src="../lib/vendor/URI.js"></script>
<script src="../lib/vendor/mithril.js"></script>
<script src="../lib/vendor/lodash.core.min.js"></script>
<script src="../lib/vendor/system-csp-production.src.js"></script>
<script src="../lib/vendor/jed.js"></script>
<script src="../i18n/strings.js"></script>
<script src="../lib/i18n.js"></script>
<script src="../lib/module-trampoline.js"></script>
<style>
#main {
border: solid 1px black;
border-radius: 10px;
margin: auto;
max-width: 50%;
padding: 2em;
}
button.accept {
background-color: #5757D2;
border: 1px solid black;
border-radius: 5px;
margin: 1em 0;
padding: 0.5em;
font-weight: bold;
color: white;
}
button.linky {
background:none!important;
border:none;
padding:0!important;
font-family:arial,sans-serif;
color:#069;
text-decoration:underline;
cursor:pointer;
}
input.url {
width: 25em;
}
button.accept:disabled {
background-color: #dedbe8;
border: 1px solid white;
border-radius: 5px;
margin: 1em 0;
padding: 0.5em;
font-weight: bold;
color: #2C2C2C;
}
.errorbox {
border: 1px solid;
display: inline-block;
margin: 1em;
padding: 1em;
font-weight: bold;
background: #FF8A8A;
}
</style>
</head>
<body>
<section id="main">
<h1>GNU Taler Wallet</h1>
Fetching contract ...
</section>
</body>
</html>

View File

@ -1,30 +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/>
*/
/**
* Download a contract from a Url (given as query parameter 'contractUrl' to
* this page) and offer the contract to the user.
*
* @author Florian Dold
*/
export function main() {
const url = URI(document.location.href);
const query: any = URI.parseQuery(url.query());
}

View File

@ -32,7 +32,6 @@
"pages/show-db.ts", "pages/show-db.ts",
"pages/confirm-contract.tsx", "pages/confirm-contract.tsx",
"pages/confirm-create-reserve.tsx", "pages/confirm-create-reserve.tsx",
"pages/offer-contract-from.tsx",
"test/tests/taler.ts" "test/tests/taler.ts"
] ]
} }