repurchase detection

This commit is contained in:
Florian Dold 2016-02-23 14:07:53 +01:00
parent 718f81bcd8
commit 5591077fe9
7 changed files with 139 additions and 13 deletions

View File

@ -65,12 +65,36 @@ var TalerNotify;
document.addEventListener("taler-contract", function (e) {
// XXX: the merchant should just give us the parsed data ...
var offer = JSON.parse(e.detail);
if (!offer.contract) {
console.error("contract field missing");
return;
}
var msg = {
type: "check-repurchase",
detail: {
contract: offer.contract
},
};
chrome.runtime.sendMessage(msg, function (resp) {
if (resp.error) {
console.error("wallet backend error", resp);
return;
}
if (resp.isRepurchase) {
console.log("doing repurchase");
console.assert(resp.existingFulfillmentUrl);
console.assert(resp.existingContractHash);
window.location.href = subst(resp.existingFulfillmentUrl, resp.existingContractHash);
}
else {
var uri = URI(chrome.extension.getURL("pages/confirm-contract.html"));
var params = {
offer: JSON.stringify(offer),
merchantPageUrl: document.location.href,
};
document.location.href = uri.query(params).href();
}
});
});
document.addEventListener('taler-execute-payment', function (e) {
console.log("got taler-execute-payment in content page");

View File

@ -77,12 +77,40 @@ namespace TalerNotify {
document.addEventListener("taler-contract", function(e: CustomEvent) {
// XXX: the merchant should just give us the parsed data ...
let offer = JSON.parse(e.detail);
if (!offer.contract) {
console.error("contract field missing");
return;
}
let msg = {
type: "check-repurchase",
detail: {
contract: offer.contract
},
};
chrome.runtime.sendMessage(msg, (resp) => {
if (resp.error) {
console.error("wallet backend error", resp);
return;
}
if (resp.isRepurchase) {
console.log("doing repurchase");
console.assert(resp.existingFulfillmentUrl);
console.assert(resp.existingContractHash);
window.location.href = subst(resp.existingFulfillmentUrl,
resp.existingContractHash);
} else {
let uri = URI(chrome.extension.getURL("pages/confirm-contract.html"));
let params = {
offer: JSON.stringify(offer),
merchantPageUrl: document.location.href,
};
document.location.href = uri.query(params).href();
}
});
});

View File

@ -51,10 +51,21 @@ export function openTalerDb(): Promise<IDBDatabase> {
db.createObjectStore("denoms", {keyPath: "denomPub"});
const coins = db.createObjectStore("coins", {keyPath: "coinPub"});
coins.createIndex("mintBaseUrl", "mintBaseUrl");
db.createObjectStore("transactions", {keyPath: "contractHash"});
const transactions = db.createObjectStore("transactions",
{keyPath: "contractHash"});
transactions.createIndex("repurchase",
[
"contract.merchant_pub",
"contract.repurchase_correlation_id"
]);
db.createObjectStore("precoins",
{keyPath: "coinPub", autoIncrement: true});
const history = db.createObjectStore("history", {keyPath: "id", autoIncrement: true});
const history = db.createObjectStore("history",
{
keyPath: "id",
autoIncrement: true
});
history.createIndex("timestamp", "timestamp");
break;
}

View File

@ -287,6 +287,29 @@ class QueryRoot {
.then(() => promise);
}
/**
* Get one object from a store by its key.
*/
getIndexed(storeName, indexName, key): Promise<any> {
if (key === void 0) {
throw Error("key must not be undefined");
}
const {resolve, promise} = openPromise();
const doGetIndexed = (tx) => {
const req = tx.objectStore(storeName).index(indexName).get(key);
req.onsuccess = (r) => {
resolve(req.result);
};
};
this.addWork(doGetIndexed, storeName, false);
return Promise.resolve()
.then(() => this.finish())
.then(() => promise);
}
/**
* Finish the query, and start the query in the first place if necessary.
*/

View File

@ -257,6 +257,13 @@ export namespace Amounts {
}
export interface CheckRepurchaseResult {
isRepurchase: boolean;
existingContractHash?: string;
existingFulfillmentUrl?: string;
}
export interface Notifier {
notify();
}

View File

@ -32,6 +32,7 @@ import {Reserve} from "./types";
import {CryptoApi} from "./cryptoApi";
import {Coin} from "./types";
import {PayCoinInfo} from "./types";
import {CheckRepurchaseResult} from "./types";
"use strict";
@ -279,8 +280,9 @@ interface CoinPaySig {
interface Transaction {
contractHash: string;
contract: any;
contract: Contract;
payReq: any;
merchantSig: string;
}
@ -512,6 +514,7 @@ export class Wallet {
contractHash: offer.H_contract,
contract: offer.contract,
payReq: payReq,
merchantSig: offer.merchant_sig,
};
console.log("pay request");
@ -926,4 +929,29 @@ export class Wallet {
.iter("history", {indexName: "timestamp"})
.reduce(collect, [])
}
checkRepurchase(contract: Contract): Promise<CheckRepurchaseResult> {
if (!contract.repurchase_correlation_id) {
console.log("no repurchase: no correlation id");
return Promise.resolve({isRepurchase: false});
}
return Query(this.db)
.getIndexed("transactions",
"repurchase",
[contract.merchant_pub, contract.repurchase_correlation_id])
.then((result: Transaction) => {
console.log("db result", result);
let isRepurchase;
if (result) {
console.assert(result.contract.repurchase_correlation_id == contract.repurchase_correlation_id);
return {
isRepurchase: true,
existingContractHash: result.contractHash,
existingFulfillmentUrl: result.contract.fulfillment_url,
};
} else {
return {isRepurchase: false};
}
});
}
}

View File

@ -22,6 +22,7 @@ import {Checkable} from "./checkable";
import {AmountJson} from "./types";
import Port = chrome.runtime.Port;
import {Notifier} from "./types";
import {Contract} from "./wallet";
"use strict";
@ -108,6 +109,10 @@ function makeHandlers(db: IDBDatabase,
let amount = AmountJson.checked(detail.amount);
return wallet.getReserveCreationInfo(detail.baseUrl, amount);
},
["check-repurchase"]: function(detail) {
let contract = Contract.checked(detail.contract);
return wallet.checkRepurchase(contract);
},
["get-history"]: function(detail) {
// TODO: limit history length
return wallet.getHistory();