From c62ba4986fbfcb8637a3befadf3d3eddbd5348ca Mon Sep 17 00:00:00 2001 From: Florian Dold Date: Wed, 17 Jan 2018 03:49:54 +0100 Subject: implement new mobile-compatible payment logic --- src/webex/messages.ts | 36 ++--- src/webex/notify.ts | 282 +++-------------------------------- src/webex/pages/confirm-contract.tsx | 83 +++++++---- src/webex/wxApi.ts | 63 ++------ src/webex/wxBackend.ts | 183 +++++++++++++++-------- 5 files changed, 220 insertions(+), 427 deletions(-) (limited to 'src/webex') diff --git a/src/webex/messages.ts b/src/webex/messages.ts index 0d0329808..0fcd6047e 100644 --- a/src/webex/messages.ts +++ b/src/webex/messages.ts @@ -44,10 +44,6 @@ export interface MessageMap { }; response: void; }; - "get-tab-cookie": { - request: { } - response: any; - }; "ping": { request: { }; response: void; @@ -67,12 +63,8 @@ export interface MessageMap { request: { reservePub: string }; response: void; }; - "generate-nonce": { - request: { } - response: string; - }; "confirm-pay": { - request: { proposalId: number; }; + request: { proposalId: number; sessionId?: string }; response: walletTypes.ConfirmPayResult; }; "check-pay": { @@ -95,10 +87,6 @@ export interface MessageMap { request: { contract: object }; response: string; }; - "save-proposal": { - request: { proposal: dbTypes.ProposalRecord }; - response: void; - }; "reserve-creation-info": { request: { baseUrl: string, amount: AmountJson }; response: walletTypes.ReserveCreationInfo; @@ -109,7 +97,7 @@ export interface MessageMap { }; "get-proposal": { request: { proposalId: number }; - response: dbTypes.ProposalRecord | undefined; + response: dbTypes.ProposalDownloadRecord | undefined; }; "get-coins": { request: { exchangeBaseUrl: string }; @@ -155,14 +143,6 @@ export interface MessageMap { request: { coinPub: string }; response: void; }; - "payment-failed": { - request: { contractTermsHash: string }; - response: void; - }; - "payment-succeeded": { - request: { contractTermsHash: string; merchantSig: string }; - response: void; - }; "check-upgrade": { request: { }; response: void; @@ -183,10 +163,6 @@ export interface MessageMap { request: { reportUid: string }; response: void; }; - "accept-refund": { - request: any; - response: void; - }; "get-purchase": { request: any; response: void; @@ -215,6 +191,14 @@ export interface MessageMap { request: { }; response: void; }; + "taler-pay": { + request: any; + response: void; + }; + "download-proposal": { + request: any; + response: void; + }; } /** diff --git a/src/webex/notify.ts b/src/webex/notify.ts index a7d393a65..e163a6272 100644 --- a/src/webex/notify.ts +++ b/src/webex/notify.ts @@ -28,13 +28,6 @@ import URI = require("urijs"); import wxApi = require("./wxApi"); -import { getTalerStampSec } from "../helpers"; -import { TipToken } from "../talerTypes"; -import { QueryPaymentResult } from "../walletTypes"; - - -import axios from "axios"; - declare var cloneInto: any; let logVerbose: boolean = false; @@ -103,42 +96,6 @@ function setStyles(installed: boolean) { } -async function handlePaymentResponse(maybeFoundResponse: QueryPaymentResult) { - if (!maybeFoundResponse.found) { - console.log("pay-failed", {hint: "payment not found in the wallet"}); - return; - } - const walletResp = maybeFoundResponse; - - logVerbose && console.log("handling taler-notify-payment: ", walletResp); - let resp; - try { - const config = { - headers: { "Content-Type": "application/json;charset=UTF-8" }, - timeout: 5000, /* 5 seconds */ - validateStatus: (s: number) => s === 200, - }; - resp = await axios.post(walletResp.contractTerms.pay_url, walletResp.payReq, config); - } catch (e) { - // Gives the user the option to retry / abort and refresh - wxApi.logAndDisplayError({ - contractTerms: walletResp.contractTerms, - message: e.message, - name: "pay-post-failed", - response: e.response, - }); - throw e; - } - const merchantResp = resp.data; - logVerbose && console.log("got success from pay_url"); - await wxApi.paymentSucceeded(walletResp.contractTermsHash, merchantResp.sig); - const nextUrl = walletResp.contractTerms.fulfillment_url; - logVerbose && console.log("taler-payment-succeeded done, going to", nextUrl); - window.location.href = nextUrl; - window.location.reload(true); -} - - function onceOnComplete(cb: () => void) { if (document.readyState === "complete") { cb(); @@ -153,234 +110,29 @@ function onceOnComplete(cb: () => void) { function init() { - // Only place where we don't use the nicer RPC wrapper, since the wallet - // backend might not be ready (during install, upgrade, etc.) - chrome.runtime.sendMessage({type: "get-tab-cookie"}, (resp) => { - logVerbose && console.log("got response for get-tab-cookie"); - if (chrome.runtime.lastError) { - logVerbose && console.log("extension not yet ready"); - window.setTimeout(init, 200); - return; - } - onceOnComplete(() => { - if (document.documentElement.getAttribute("data-taler-nojs")) { - initStyle(); - setStyles(true); - } - }); - registerHandlers(); - // Hack to know when the extension is unloaded - const port = chrome.runtime.connect(); - - port.onDisconnect.addListener(() => { - logVerbose && console.log("chrome runtime disconnected, removing handlers"); - if (document.documentElement.getAttribute("data-taler-nojs")) { - setStyles(false); - } - for (const handler of handlers) { - document.removeEventListener(handler.type, handler.listener); - } - }); - - if (resp && resp.type === "pay") { - logVerbose && console.log("doing taler.pay with", resp.payDetail); - talerPay(resp.payDetail).then(handlePaymentResponse); + onceOnComplete(() => { + if (document.documentElement.getAttribute("data-taler-nojs")) { + initStyle(); + setStyles(true); } }); -} - -type HandlerFn = (detail: any, sendResponse: (msg: any) => void) => void; - -async function downloadContract(url: string, nonce: string): Promise { - const parsed_url = new URI(url); - url = parsed_url.setQuery({nonce}).href(); - console.log("downloading contract from '" + url + "'"); - let resp; - try { - resp = await axios.get(url, { validateStatus: (s) => s === 200 }); - } catch (e) { - wxApi.logAndDisplayError({ - message: e.message, - name: "contract-download-failed", - response: e.response, - sameTab: true, - }); - throw e; - } - console.log("got response", resp); - return resp.data; -} - -async function processProposal(proposal: any) { - - if (!proposal.contract_terms) { - console.error("field proposal.contract_terms field missing"); - return; - } - - const contractHash = await wxApi.hashContract(proposal.contract_terms); - - const proposalId = await wxApi.saveProposal({ - contractTerms: proposal.contract_terms, - contractTermsHash: contractHash, - merchantSig: proposal.sig, - timestamp: (new Date()).getTime(), - }); - - const uri = new URI(chrome.extension.getURL("/src/webex/pages/confirm-contract.html")); - const params = { - proposalId: proposalId.toString(), - }; - const target = uri.query(params).href(); - document.location.replace(target); -} - - -/** - * Handle a payment request (coming either from an HTTP 402 or - * the JS wallet API). - */ -function talerPay(msg: any): Promise { - // Use a promise directly instead of of an async - // function since some paths never resolve the promise. - return new Promise(async(resolve, reject) => { - if (msg.tip) { - const tipToken = TipToken.checked(JSON.parse(msg.tip)); - - console.log("got tip token", tipToken); - - const deadlineSec = getTalerStampSec(tipToken.expiration); - if (!deadlineSec) { - wxApi.logAndDisplayError({ - message: "invalid expiration", - name: "tipping-failed", - sameTab: true, - }); - return; - } - - const merchantDomain = new URI(document.location.href).origin(); - let walletResp; - try { - walletResp = await wxApi.getTipPlanchets(merchantDomain, - tipToken.tip_id, - tipToken.amount, - deadlineSec, - tipToken.exchange_url, - tipToken.next_url); - } catch (e) { - wxApi.logAndDisplayError({ - message: e.message, - name: "tipping-failed", - response: e.response, - sameTab: true, - }); - throw e; - } - - const planchets = walletResp; - - if (!planchets) { - wxApi.logAndDisplayError({ - detail: walletResp, - message: "processing tip failed", - name: "tipping-failed", - sameTab: true, - }); - return; - } - - let merchantResp; - - try { - const config = { - validateStatus: (s: number) => s === 200, - }; - const req = { planchets, tip_id: tipToken.tip_id }; - merchantResp = await axios.post(tipToken.pickup_url, req, config); - } catch (e) { - wxApi.logAndDisplayError({ - message: e.message, - name: "tipping-failed", - response: e.response, - sameTab: true, - }); - throw e; - } - - try { - wxApi.processTipResponse(merchantDomain, tipToken.tip_id, merchantResp.data); - } catch (e) { - wxApi.logAndDisplayError({ - message: e.message, - name: "tipping-failed", - response: e.response, - sameTab: true, - }); - throw e; - } - - // Go to tip dialog page, where the user can confirm the tip or - // decline if they are not happy with the exchange. - const uri = new URI(chrome.extension.getURL("/src/webex/pages/tip.html")); - const params = { tip_id: tipToken.tip_id, merchant_domain: merchantDomain }; - const redirectUrl = uri.query(params).href(); - window.location.href = redirectUrl; - - return; - } - - if (msg.refund_url) { - console.log("processing refund"); - let resp; - try { - const config = { - validateStatus: (s: number) => s === 200, - }; - resp = await axios.get(msg.refund_url, config); - } catch (e) { - wxApi.logAndDisplayError({ - message: e.message, - name: "refund-download-failed", - response: e.response, - sameTab: true, - }); - throw e; - } - await wxApi.acceptRefund(resp.data); - const hc = resp.data.refund_permissions[0].h_contract_terms; - document.location.href = chrome.extension.getURL(`/src/webex/pages/refund.html?contractTermsHash=${hc}`); - return; - } - - // current URL without fragment - const url = new URI(document.location.href).fragment("").href(); - const res = await wxApi.queryPayment(url); - logVerbose && console.log("taler-pay: got response", res); - if (res && res.found && res.payReq) { - resolve(res); - return; + registerHandlers(); + // Hack to know when the extension is unloaded + const port = chrome.runtime.connect(); + + port.onDisconnect.addListener(() => { + logVerbose && console.log("chrome runtime disconnected, removing handlers"); + if (document.documentElement.getAttribute("data-taler-nojs")) { + setStyles(false); } - if (msg.contract_url) { - const nonce = await wxApi.generateNonce(); - const proposal = await downloadContract(msg.contract_url, nonce); - if (proposal.contract_terms.nonce !== nonce) { - console.error("stale contract"); - return; - } - await processProposal(proposal); - return; - } - - if (msg.offer_url) { - document.location.href = msg.offer_url; - return; + for (const handler of handlers) { + document.removeEventListener(handler.type, handler.listener); } - - console.log("can't proceed with payment, no way to get contract specified"); }); } +type HandlerFn = (detail: any, sendResponse: (msg: any) => void) => void; + function registerHandlers() { /** @@ -457,7 +209,7 @@ function registerHandlers() { }); addHandler("taler-pay", async(msg: any, sendResponse: any) => { - const resp = await talerPay(msg); + const resp = await wxApi.talerPay(msg); sendResponse(resp); }); } diff --git a/src/webex/pages/confirm-contract.tsx b/src/webex/pages/confirm-contract.tsx index 83de738b9..090737475 100644 --- a/src/webex/pages/confirm-contract.tsx +++ b/src/webex/pages/confirm-contract.tsx @@ -27,7 +27,7 @@ import * as i18n from "../../i18n"; import { ExchangeRecord, - ProposalRecord, + ProposalDownloadRecord, } from "../../dbTypes"; import { ContractTerms } from "../../talerTypes"; import { @@ -102,12 +102,15 @@ class Details extends React.Component { } interface ContractPromptProps { - proposalId: number; + proposalId?: number; + contractUrl?: string; + sessionId?: string; } interface ContractPromptState { - proposal: ProposalRecord|null; - error: string|null; + proposalId: number | undefined; + proposal: ProposalDownloadRecord | null; + error: string | null; payDisabled: boolean; alreadyPaid: boolean; exchanges: null|ExchangeRecord[]; @@ -130,6 +133,7 @@ class ContractPrompt extends React.Component e.master_pub); const ex = this.state.exchanges.find((e) => acceptedExchangePubs.indexOf(e.masterPublicKey) >= 0); if (ex) { - this.setState({error: msgInsufficient}); + this.setState({ error: msgInsufficient }); } else { - this.setState({error: msgNoMatch}); + this.setState({ error: msgNoMatch }); } } else { - this.setState({error: msgInsufficient}); + this.setState({ error: msgInsufficient }); } - this.setState({payDisabled: true}); + this.setState({ payDisabled: true }); } else if (payStatus.status === "paid") { - this.setState({alreadyPaid: true, payDisabled: false, error: null, payStatus}); + this.setState({ alreadyPaid: true, payDisabled: false, error: null, payStatus }); } else { - this.setState({payDisabled: false, error: null, payStatus}); + this.setState({ payDisabled: false, error: null, payStatus }); } } @@ -184,21 +200,24 @@ class ContractPrompt extends React.ComponentError: either contractUrl or proposalId must be given; + } + if (this.state.proposalId === undefined) { + return Downloading contract terms; + } if (!this.state.proposal) { return ...; } @@ -255,8 +274,18 @@ class ContractPrompt extends React.Component { const url = new URI(document.location.href); const query: any = URI.parseQuery(url.query()); - const proposalId = JSON.parse(query.proposalId); - ReactDOM.render(, document.getElementById( - "contract")!); + let proposalId; + try { + proposalId = JSON.parse(query.proposalId); + } catch { + // ignore error + } + + const sessionId = query.sessionId; + const contractUrl = query.contractUrl; + + ReactDOM.render( + , + document.getElementById("contract")!); }); diff --git a/src/webex/wxApi.ts b/src/webex/wxApi.ts index 2f7a13c48..efebf21d1 100644 --- a/src/webex/wxApi.ts +++ b/src/webex/wxApi.ts @@ -217,8 +217,8 @@ export function checkPay(proposalId: number): Promise { /** * Pay for a proposal. */ -export function confirmPay(proposalId: number): Promise { - return callBackend("confirm-pay", { proposalId }); +export function confirmPay(proposalId: number, sessionId: string | undefined): Promise { + return callBackend("confirm-pay", { proposalId, sessionId }); } /** @@ -228,15 +228,6 @@ export function hashContract(contract: object): Promise { return callBackend("hash-contract", { contract }); } - -/** - * Save a proposal in the wallet. Returns the proposal id that - * the proposal is stored under. - */ -export function saveProposal(proposal: any): Promise { - return callBackend("save-proposal", { proposal }); -} - /** * Mark a reserve as confirmed. */ @@ -251,36 +242,6 @@ export function queryPayment(url: string): Promise { return callBackend("query-payment", { url }); } -/** - * Mark a payment as succeeded. - */ -export function paymentSucceeded(contractTermsHash: string, merchantSig: string): Promise { - return callBackend("payment-succeeded", { contractTermsHash, merchantSig }); -} - -/** - * Mark a payment as succeeded. - */ -export function paymentFailed(contractTermsHash: string): Promise { - return callBackend("payment-failed", { contractTermsHash }); -} - -/** - * Get the payment cookie for the current tab, or undefined if no payment - * cookie was set. - */ -export function getTabCookie(): Promise { - return callBackend("get-tab-cookie", { }); -} - -/** - * Generate a contract nonce (EdDSA key pair), store it in the wallet's - * database and return the public key. - */ -export function generateNonce(): Promise { - return callBackend("generate-nonce", { }); -} - /** * Check upgrade information */ @@ -344,12 +305,6 @@ export function getReport(reportUid: string): Promise { return callBackend("get-report", { reportUid }); } -/** - * Apply a refund that we got from the merchant. - */ -export function acceptRefund(refundData: any): Promise { - return callBackend("accept-refund", refundData); -} /** * Look up a purchase in the wallet database from @@ -407,3 +362,17 @@ export function processTipResponse(merchantDomain: string, tipId: string, tipRes export function clearNotification(): Promise { return callBackend("clear-notification", { }); } + +/** + * Trigger taler payment processing (for payment, tipping and refunds). + */ +export function talerPay(msg: any): Promise { + return callBackend("taler-pay", msg); +} + +/** + * Download a contract. + */ +export function downloadProposal(url: string): Promise { + return callBackend("download-proposal", { url }); +} diff --git a/src/webex/wxBackend.ts b/src/webex/wxBackend.ts index 02a1543e5..c0b42a768 100644 --- a/src/webex/wxBackend.ts +++ b/src/webex/wxBackend.ts @@ -33,7 +33,6 @@ import { import { AmountJson } from "../amounts"; -import { ProposalRecord } from "../dbTypes"; import { AcceptTipRequest, ConfirmReserveRequest, @@ -41,6 +40,7 @@ import { GetTipPlanchetsRequest, Notifier, ProcessTipResponseRequest, + QueryPaymentFound, ReturnCoinsRequest, TipStatusRequest, } from "../walletTypes"; @@ -62,6 +62,7 @@ import * as wxApi from "./wxApi"; import URI = require("urijs"); import Port = chrome.runtime.Port; import MessageSender = chrome.runtime.MessageSender; +import { TipToken } from "../talerTypes"; const DB_NAME = "taler"; @@ -93,15 +94,6 @@ function handleMessage(sender: MessageSender, const db = needsWallet().db; return importDb(db, detail.dump); } - case "get-tab-cookie": { - if (!sender || !sender.tab || !sender.tab.id) { - return Promise.resolve(); - } - const id: number = sender.tab.id; - const info: any = paymentRequestCookies[id] as any; - delete paymentRequestCookies[id]; - return Promise.resolve(info); - } case "ping": { return Promise.resolve(); } @@ -138,14 +130,11 @@ function handleMessage(sender: MessageSender, const req = ConfirmReserveRequest.checked(d); return needsWallet().confirmReserve(req); } - case "generate-nonce": { - return needsWallet().generateNonce(); - } case "confirm-pay": { if (typeof detail.proposalId !== "number") { throw Error("proposalId must be number"); } - return needsWallet().confirmPay(detail.proposalId); + return needsWallet().confirmPay(detail.proposalId, detail.sessionId); } case "check-pay": { if (typeof detail.proposalId !== "number") { @@ -166,7 +155,7 @@ function handleMessage(sender: MessageSender, return Promise.resolve(msg); } } - return needsWallet().queryPayment(detail.url); + return needsWallet().queryPaymentByFulfillmentUrl(detail.url); } case "exchange-info": { if (!detail.baseUrl) { @@ -188,11 +177,6 @@ function handleMessage(sender: MessageSender, return hash; }); } - case "save-proposal": { - console.log("handling save-proposal", detail); - const checkedRecord = ProposalRecord.checked(detail.proposal); - return needsWallet().saveProposal(checkedRecord); - } case "reserve-creation-info": { if (!detail.baseUrl || typeof detail.baseUrl !== "string") { return Promise.resolve({ error: "bad url" }); @@ -261,25 +245,6 @@ function handleMessage(sender: MessageSender, } return needsWallet().payback(detail.coinPub); } - case "payment-failed": { - // For now we just update exchanges (maybe the exchange did something - // wrong and the keys were messed up). - // FIXME: in the future we should look at what actually went wrong. - console.error("payment reported as failed"); - needsWallet().updateExchanges(); - return Promise.resolve(); - } - case "payment-succeeded": { - const contractTermsHash = detail.contractTermsHash; - const merchantSig = detail.merchantSig; - if (!contractTermsHash) { - return Promise.reject(Error("contractHash missing")); - } - if (!merchantSig) { - return Promise.reject(Error("merchantSig missing")); - } - return needsWallet().paymentSucceeded(contractTermsHash, merchantSig); - } case "get-sender-wire-infos": { return needsWallet().getSenderWireInfos(); } @@ -316,8 +281,6 @@ function handleMessage(sender: MessageSender, return; case "get-report": return logging.getReport(detail.reportUid); - case "accept-refund": - return needsWallet().acceptRefund(detail.refund_permissions); case "get-purchase": { const contractTermsHash = detail.contractTermsHash; if (!contractTermsHash) { @@ -351,6 +314,28 @@ function handleMessage(sender: MessageSender, case "clear-notification": { return needsWallet().clearNotification(); } + case "download-proposal": { + return needsWallet().downloadProposal(detail.url); + } + case "taler-pay": { + const senderUrl = sender.url; + if (!senderUrl) { + console.log("can't trigger payment, no sender URL"); + return; + } + const tab = sender.tab; + if (!tab) { + console.log("can't trigger payment, no sender tab"); + return; + } + const tabId = tab.id; + if (typeof tabId !== "string") { + console.log("can't trigger payment, no sender tab id"); + return; + } + talerPay(detail, senderUrl, tabId); + return; + } default: // Exhaustiveness check. // See https://www.typescriptlang.org/docs/handbook/advanced-types.html @@ -417,13 +402,67 @@ class ChromeNotifier implements Notifier { } -/** - * Mapping from tab ID to payment information (if any). - * - * Used to pass information from an intercepted HTTP header to the content - * script on the page. - */ -const paymentRequestCookies: { [n: number]: any } = {}; +async function talerPay(fields: any, url: string, tabId: number): Promise { + if (!currentWallet) { + console.log("can't handle payment, no wallet"); + return undefined; + } + + const w = currentWallet; + + const goToPayment = (p: QueryPaymentFound): string => { + 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); + } + return url; + }; + + if (fields.resource_url) { + const p = await w.queryPaymentByFulfillmentUrl(fields.resource_url); + if (p.found) { + return goToPayment(p); + } + } + if (fields.contract_hash) { + const p = await w.queryPaymentByContractTermsHash(fields.contract_hash); + if (p.found) { + goToPayment(p); + return goToPayment(p); + } + } + if (fields.contract_url) { + const proposalId = await w.downloadProposal(fields.contract_url); + const uri = new URI(chrome.extension.getURL("/src/webex/pages/confirm-contract.html")); + if (fields.session_id) { + uri.addSearch("sessionId", fields.session_id); + } + uri.addSearch("proposalId", proposalId); + const redirectUrl = uri.href(); + return redirectUrl; + } + if (fields.offer_url) { + return fields.offer_url; + } + 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}`); + } + if (fields.tip) { + const tipToken = TipToken.checked(fields.tip); + w.processTip(tipToken); + // Go to tip dialog page, where the user can confirm the tip or + // decline if they are not happy with the exchange. + const merchantDomain = new URI(url).origin(); + const uri = new URI(chrome.extension.getURL("/src/webex/pages/tip.html")); + const params = { tip_id: tipToken.tip_id, merchant_domain: merchantDomain }; + const redirectUrl = uri.query(params).href(); + return redirectUrl; + } + return undefined; +} /** @@ -433,6 +472,11 @@ const paymentRequestCookies: { [n: number]: any } = {}; * 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) { @@ -441,9 +485,12 @@ function handleHttpPayment(headerList: chrome.webRequest.HttpHeader[], url: stri } const fields = { + contract_hash: headers["x-taler-contract-hash"], contract_url: headers["x-taler-contract-url"], offer_url: headers["x-taler-offer-url"], refund_url: headers["x-taler-refund-url"], + resource_url: headers["x-taler-resource-url"], + session_id: headers["x-taler-session-id"], tip: headers["x-taler-tip"], }; @@ -456,21 +503,33 @@ function handleHttpPayment(headerList: chrome.webRequest.HttpHeader[], url: stri return; } - const payDetail = { - contract_url: fields.contract_url, - offer_url: fields.offer_url, - refund_url: fields.refund_url, - tip: fields.tip, - }; + console.log("got pay detail", fields); - console.log("got pay detail", payDetail); + // Fast path for existing payment + if (fields.resource_url) { + const nextUrl = currentWallet.getNextUrlFromResourceUrl(fields.resource_url); + if (nextUrl) { + return { redirectUrl: nextUrl }; + } + } + // Fast path for new contract + if (!fields.contract_hash && fields.contract_url) { + const uri = new URI(chrome.extension.getURL("/src/webex/pages/confirm-contract.html")); + uri.addSearch("contractUrl", fields.contract_url); + if (fields.session_id) { + uri.addSearch("sessionId", fields.session_id); + } + return { redirectUrl: uri.href() }; + } - // This cookie will be read by the injected content script - // in the tab that displays the page. - paymentRequestCookies[tabId] = { - payDetail, - type: "pay", - }; + // 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 }); + } + }); + + return; } @@ -541,7 +600,7 @@ function handleBankRequest(wallet: Wallet, headerList: chrome.webRequest.HttpHea 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}); + chrome.tabs.update(tabId, { url: redirectUrl }); return; } -- cgit v1.2.3