summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFlorian Dold <florian.dold@gmail.com>2018-01-22 01:12:08 +0100
committerFlorian Dold <florian.dold@gmail.com>2018-01-22 01:12:08 +0100
commitae177549a5818e2698253ef17a11b1effbd66fdb (patch)
tree7ac7765d2af595b1d22367740d88a397b6b9868a
parent1671d9a508b803af31762bcd9508e70eb40e7b48 (diff)
downloadwallet-core-ae177549a5818e2698253ef17a11b1effbd66fdb.tar.gz
wallet-core-ae177549a5818e2698253ef17a11b1effbd66fdb.tar.bz2
wallet-core-ae177549a5818e2698253ef17a11b1effbd66fdb.zip
implement flicker-free refunds
-rw-r--r--src/wallet.ts8
-rw-r--r--src/webex/messages.ts4
-rw-r--r--src/webex/pages/refund.tsx33
-rw-r--r--src/webex/wxApi.ts7
-rw-r--r--src/webex/wxBackend.ts22
5 files changed, 63 insertions, 11 deletions
diff --git a/src/wallet.ts b/src/wallet.ts
index 9498fe820..01db8c612 100644
--- a/src/wallet.ts
+++ b/src/wallet.ts
@@ -2532,6 +2532,10 @@ export class Wallet {
}
}
+ /**
+ * Accept a refund, return the contract hash for the contract
+ * that was involved in the refund.
+ */
async acceptRefund(refundUrl: string): Promise<string> {
console.log("processing refund");
let resp;
@@ -2598,7 +2602,8 @@ export class Wallet {
return refundPermissions[0].h_contract_terms;
}
- async submitRefunds(contractTermsHash: string): Promise<void> {
+
+ private async submitRefunds(contractTermsHash: string): Promise<void> {
const purchase = await this.q().get(Stores.purchases, contractTermsHash);
if (!purchase) {
console.error("not submitting refunds, contract terms not found:", contractTermsHash);
@@ -2644,7 +2649,6 @@ export class Wallet {
return c;
};
-
await this.q()
.mutate(Stores.purchases, contractTermsHash, transformPurchase)
.mutate(Stores.coins, perm.coin_pub, transformCoin)
diff --git a/src/webex/messages.ts b/src/webex/messages.ts
index e1bd6f12c..2219cdf1d 100644
--- a/src/webex/messages.ts
+++ b/src/webex/messages.ts
@@ -195,6 +195,10 @@ export interface MessageMap {
request: { contractTermsHash: string, sessionId: string | undefined };
response: void;
};
+ "accept-refund": {
+ request: { refundUrl: string }
+ response: string;
+ };
}
/**
diff --git a/src/webex/pages/refund.tsx b/src/webex/pages/refund.tsx
index 3e82f3667..8164eb664 100644
--- a/src/webex/pages/refund.tsx
+++ b/src/webex/pages/refund.tsx
@@ -35,10 +35,12 @@ import { AmountDisplay } from "../renderHtml";
import * as wxApi from "../wxApi";
interface RefundStatusViewProps {
- contractTermsHash: string;
+ contractTermsHash?: string;
+ refundUrl?: string;
}
interface RefundStatusViewState {
+ contractTermsHash?: string;
purchase?: dbTypes.PurchaseRecord;
refundFees?: AmountJson;
gotResult: boolean;
@@ -102,13 +104,22 @@ class RefundStatusView extends React.Component<RefundStatusViewProps, RefundStat
}
render(): JSX.Element {
+ if (!this.props.contractTermsHash && !this.props.refundUrl) {
+ return (
+ <div id="main">
+ <span>Error: Neither contract terms hash nor refund url given.</span>
+ </div>
+ );
+ }
const purchase = this.state.purchase;
if (!purchase) {
+ let message;
if (this.state.gotResult) {
- return <span>No purchase with contract terms hash {this.props.contractTermsHash} found</span>;
+ message = <span>No purchase with contract terms hash {this.props.contractTermsHash} found</span>;
} else {
- return <span>...</span>;
+ message = <span>...</span>;
}
+ return <div id="main">{message}</div>;
}
const merchantName = purchase.contractTerms.merchant.name || "(unknown)";
const summary = purchase.contractTerms.summary || purchase.contractTerms.order_id;
@@ -128,7 +139,16 @@ class RefundStatusView extends React.Component<RefundStatusViewProps, RefundStat
}
async update() {
- const purchase = await wxApi.getPurchase(this.props.contractTermsHash);
+ let contractTermsHash = this.state.contractTermsHash;
+ if (!contractTermsHash) {
+ const refundUrl = this.props.refundUrl;
+ if (!refundUrl) {
+ console.error("neither contractTermsHash nor refundUrl is given");
+ return;
+ }
+ contractTermsHash = await wxApi.acceptRefund(refundUrl);
+ }
+ const purchase = await wxApi.getPurchase(contractTermsHash);
console.log("got purchase", purchase);
const refundsDone = Object.keys(purchase.refundsDone).map((x) => purchase.refundsDone[x]);
const refundFees = await wxApi.getFullRefundFees( {refundPermissions: refundsDone });
@@ -147,8 +167,9 @@ async function main() {
return;
}
- const contractTermsHash = query.contractTermsHash || "(none)";
- ReactDOM.render(<RefundStatusView contractTermsHash={contractTermsHash} />, container);
+ const contractTermsHash = query.contractTermsHash;
+ const refundUrl = query.refundUrl;
+ ReactDOM.render(<RefundStatusView contractTermsHash={contractTermsHash} refundUrl={refundUrl} />, container);
}
document.addEventListener("DOMContentLoaded", () => main());
diff --git a/src/webex/wxApi.ts b/src/webex/wxApi.ts
index 566f45265..8a7bf8250 100644
--- a/src/webex/wxApi.ts
+++ b/src/webex/wxApi.ts
@@ -363,3 +363,10 @@ export function talerPay(msg: any): Promise<void> {
export function downloadProposal(url: string): Promise<number> {
return callBackend("download-proposal", { url });
}
+
+/**
+ * Download a refund and accept it.
+ */
+export function acceptRefund(refundUrl: string): Promise<string> {
+ return callBackend("accept-refund", { refundUrl });
+}
diff --git a/src/webex/wxBackend.ts b/src/webex/wxBackend.ts
index 26b8ff2cf..98b543d28 100644
--- a/src/webex/wxBackend.ts
+++ b/src/webex/wxBackend.ts
@@ -292,6 +292,8 @@ function handleMessage(sender: MessageSender,
}
case "get-full-refund-fees":
return needsWallet().getFullRefundFees(detail.refundPermissions);
+ case "accept-refund":
+ return needsWallet().acceptRefund(detail.refundUrl);
case "get-tip-status": {
const tipToken = TipToken.checked(detail.tipToken);
return needsWallet().getTipStatus(tipToken);
@@ -430,8 +432,8 @@ async function talerPay(fields: any, url: string, tabId: number): Promise<string
}
if (fields.refund_url) {
console.log("processing refund");
- const hc = await w.acceptRefund(fields.refund_url);
- return chrome.extension.getURL(`/src/webex/pages/refund.html?contractTermsHash=${hc}`);
+ const uri = new URI(chrome.extension.getURL("/src/webex/pages/refund.html"));
+ return uri.query({ refundUrl: fields.refund_url }).href();
}
if (fields.tip) {
const uri = new URI(chrome.extension.getURL("/src/webex/pages/tip.html"));
@@ -507,10 +509,24 @@ function handleHttpPayment(headerList: chrome.webRequest.HttpHeader[], url: stri
return { redirectUrl: uri.href() };
}
+ // Synchronous fast path for refund
+ if (fields.refund_url) {
+ console.log("processing refund");
+ const uri = new URI(chrome.extension.getURL("/src/webex/pages/refund.html"));
+ uri.query({ refundUrl: fields.refund_url });
+ return { redirectUrl: uri.href };
+ }
+
// We need to do some asynchronous operation, we can't directly redirect
talerPay(fields, url, tabId).then((nextUrl) => {
if (nextUrl) {
- chrome.tabs.update(tabId, { url: 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",
+ });
}
});