tos
This commit is contained in:
parent
99bccae9fe
commit
6415564b92
@ -26,12 +26,20 @@ import {
|
|||||||
} from "../headless/helpers";
|
} from "../headless/helpers";
|
||||||
import { openPromise, OpenedPromise } from "../util/promiseUtils";
|
import { openPromise, OpenedPromise } from "../util/promiseUtils";
|
||||||
import fs = require("fs");
|
import fs = require("fs");
|
||||||
import { HttpRequestLibrary, HttpResponse, HttpRequestOptions } from "../util/http";
|
import {
|
||||||
|
HttpRequestLibrary,
|
||||||
|
HttpResponse,
|
||||||
|
HttpRequestOptions,
|
||||||
|
Headers,
|
||||||
|
} from "../util/http";
|
||||||
|
|
||||||
// @ts-ignore: special built-in module
|
// @ts-ignore: special built-in module
|
||||||
//import akono = require("akono");
|
//import akono = require("akono");
|
||||||
|
|
||||||
export { handleWorkerError, handleWorkerMessage } from "../crypto/workers/nodeThreadWorker";
|
export {
|
||||||
|
handleWorkerError,
|
||||||
|
handleWorkerMessage,
|
||||||
|
} from "../crypto/workers/nodeThreadWorker";
|
||||||
|
|
||||||
export class AndroidHttpLib implements HttpRequestLibrary {
|
export class AndroidHttpLib implements HttpRequestLibrary {
|
||||||
useNfcTunnel: boolean = false;
|
useNfcTunnel: boolean = false;
|
||||||
@ -66,7 +74,11 @@ export class AndroidHttpLib implements HttpRequestLibrary {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
postJson(url: string, body: any, opt?: HttpRequestOptions): Promise<import("../util/http").HttpResponse> {
|
postJson(
|
||||||
|
url: string,
|
||||||
|
body: any,
|
||||||
|
opt?: HttpRequestOptions,
|
||||||
|
): Promise<import("../util/http").HttpResponse> {
|
||||||
if (this.useNfcTunnel) {
|
if (this.useNfcTunnel) {
|
||||||
const myId = this.requestId++;
|
const myId = this.requestId++;
|
||||||
const p = openPromise<HttpResponse>();
|
const p = openPromise<HttpResponse>();
|
||||||
@ -89,11 +101,14 @@ export class AndroidHttpLib implements HttpRequestLibrary {
|
|||||||
const myId = msg.id;
|
const myId = msg.id;
|
||||||
const p = this.requestMap[myId];
|
const p = this.requestMap[myId];
|
||||||
if (!p) {
|
if (!p) {
|
||||||
console.error(`no matching request for tunneled HTTP response, id=${myId}`);
|
console.error(
|
||||||
|
`no matching request for tunneled HTTP response, id=${myId}`,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
const headers = new Headers();
|
||||||
if (msg.status != 0) {
|
if (msg.status != 0) {
|
||||||
const resp: HttpResponse = {
|
const resp: HttpResponse = {
|
||||||
headers: {},
|
headers,
|
||||||
status: msg.status,
|
status: msg.status,
|
||||||
json: async () => JSON.parse(msg.responseText),
|
json: async () => JSON.parse(msg.responseText),
|
||||||
text: async () => msg.responseText,
|
text: async () => msg.responseText,
|
||||||
@ -146,7 +161,7 @@ export function installAndroidWalletListener() {
|
|||||||
};
|
};
|
||||||
const w = await getDefaultNodeWallet(walletArgs);
|
const w = await getDefaultNodeWallet(walletArgs);
|
||||||
maybeWallet = w;
|
maybeWallet = w;
|
||||||
w.runRetryLoop().catch((e) => {
|
w.runRetryLoop().catch(e => {
|
||||||
console.error("Error during wallet retry loop", e);
|
console.error("Error during wallet retry loop", e);
|
||||||
});
|
});
|
||||||
wp.resolve(w);
|
wp.resolve(w);
|
||||||
@ -191,7 +206,10 @@ export function installAndroidWalletListener() {
|
|||||||
}
|
}
|
||||||
case "confirmPay": {
|
case "confirmPay": {
|
||||||
const wallet = await wp.promise;
|
const wallet = await wp.promise;
|
||||||
result = await wallet.confirmPay(msg.args.proposalId, msg.args.sessionId);
|
result = await wallet.confirmPay(
|
||||||
|
msg.args.proposalId,
|
||||||
|
msg.args.sessionId,
|
||||||
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "startTunnel": {
|
case "startTunnel": {
|
||||||
@ -206,14 +224,28 @@ export function installAndroidWalletListener() {
|
|||||||
httpLib.handleTunnelResponse(msg.args);
|
httpLib.handleTunnelResponse(msg.args);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "getWithdrawalInfo": {
|
case "getWithdrawDetailsForUri": {
|
||||||
const wallet = await wp.promise;
|
const wallet = await wp.promise;
|
||||||
result = await wallet.getWithdrawalInfo(msg.args.talerWithdrawUri);
|
result = await wallet.getWithdrawDetailsForUri(
|
||||||
|
msg.args.talerWithdrawUri,
|
||||||
|
msg.args.selectedExchange,
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "acceptExchangeTermsOfService": {
|
||||||
|
const wallet = await wp.promise;
|
||||||
|
result = await wallet.acceptExchangeTermsOfService(
|
||||||
|
msg.args.exchangeBaseUrl,
|
||||||
|
msg.args.etag,
|
||||||
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "acceptWithdrawal": {
|
case "acceptWithdrawal": {
|
||||||
const wallet = await wp.promise;
|
const wallet = await wp.promise;
|
||||||
result = await wallet.acceptWithdrawal(msg.args.talerWithdrawUri, msg.args.selectedExchange);
|
result = await wallet.acceptWithdrawal(
|
||||||
|
msg.args.talerWithdrawUri,
|
||||||
|
msg.args.selectedExchange,
|
||||||
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "reset": {
|
case "reset": {
|
||||||
@ -234,7 +266,7 @@ export function installAndroidWalletListener() {
|
|||||||
maybeWallet = undefined;
|
maybeWallet = undefined;
|
||||||
const w = await getDefaultNodeWallet(walletArgs);
|
const w = await getDefaultNodeWallet(walletArgs);
|
||||||
maybeWallet = w;
|
maybeWallet = w;
|
||||||
w.runRetryLoop().catch((e) => {
|
w.runRetryLoop().catch(e => {
|
||||||
console.error("Error during wallet retry loop", e);
|
console.error("Error during wallet retry loop", e);
|
||||||
});
|
});
|
||||||
wp.resolve(w);
|
wp.resolve(w);
|
||||||
|
@ -455,9 +455,10 @@ export interface ExchangeDetails {
|
|||||||
lastUpdateTime: Timestamp;
|
lastUpdateTime: Timestamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum ExchangeUpdateStatus {
|
export const enum ExchangeUpdateStatus {
|
||||||
FETCH_KEYS = "fetch_keys",
|
FETCH_KEYS = "fetch_keys",
|
||||||
FETCH_WIRE = "fetch_wire",
|
FETCH_WIRE = "fetch_wire",
|
||||||
|
FETCH_TERMS = "fetch_terms",
|
||||||
FINISHED = "finished",
|
FINISHED = "finished",
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -494,6 +495,26 @@ export interface ExchangeRecord {
|
|||||||
*/
|
*/
|
||||||
timestampAdded: Timestamp;
|
timestampAdded: Timestamp;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Terms of service text or undefined if not downloaded yet.
|
||||||
|
*/
|
||||||
|
termsOfServiceText: string | undefined;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ETag for last terms of service download.
|
||||||
|
*/
|
||||||
|
termsOfServiceLastEtag: string | undefined;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ETag for last terms of service download.
|
||||||
|
*/
|
||||||
|
termsOfServiceAcceptedEtag: string | undefined;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ETag for last terms of service download.
|
||||||
|
*/
|
||||||
|
termsOfServiceAcceptedTimestamp: Timestamp | undefined;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Time when the update to the exchange has been started or
|
* Time when the update to the exchange has been started or
|
||||||
* undefined if no update is in progress.
|
* undefined if no update is in progress.
|
||||||
|
@ -28,6 +28,7 @@ import Axios, { AxiosPromise, AxiosResponse } from "axios";
|
|||||||
import {
|
import {
|
||||||
HttpRequestLibrary,
|
HttpRequestLibrary,
|
||||||
HttpRequestOptions,
|
HttpRequestOptions,
|
||||||
|
Headers,
|
||||||
} from "../util/http";
|
} from "../util/http";
|
||||||
import * as amounts from "../util/amounts";
|
import * as amounts from "../util/amounts";
|
||||||
import { Bank } from "./bank";
|
import { Bank } from "./bank";
|
||||||
@ -83,8 +84,12 @@ export class NodeHttpLib implements HttpRequestLibrary {
|
|||||||
}
|
}
|
||||||
return responseJson;
|
return responseJson;
|
||||||
};
|
};
|
||||||
|
const headers = new Headers();
|
||||||
|
for (const hn of Object.keys(resp.headers)) {
|
||||||
|
headers.set(hn, resp.headers[hn]);
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
headers: resp.headers,
|
headers,
|
||||||
status: resp.status,
|
status: resp.status,
|
||||||
text: async () => resp.data,
|
text: async () => resp.data,
|
||||||
json: makeJson,
|
json: makeJson,
|
||||||
|
@ -230,8 +230,8 @@ walletCli
|
|||||||
break;
|
break;
|
||||||
case TalerUriType.TalerWithdraw:
|
case TalerUriType.TalerWithdraw:
|
||||||
{
|
{
|
||||||
const withdrawInfo = await wallet.getWithdrawalInfo(uri);
|
const withdrawInfo = await wallet.getWithdrawDetailsForUri(uri);
|
||||||
const selectedExchange = withdrawInfo.suggestedExchange;
|
const selectedExchange = withdrawInfo.bankWithdrawDetails.suggestedExchange;
|
||||||
if (!selectedExchange) {
|
if (!selectedExchange) {
|
||||||
console.error("no suggested exchange!");
|
console.error("no suggested exchange!");
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
|
@ -24,7 +24,7 @@
|
|||||||
*/
|
*/
|
||||||
export interface HttpResponse {
|
export interface HttpResponse {
|
||||||
status: number;
|
status: number;
|
||||||
headers: { [name: string]: string };
|
headers: Headers;
|
||||||
json(): Promise<any>;
|
json(): Promise<any>;
|
||||||
text(): Promise<string>;
|
text(): Promise<string>;
|
||||||
}
|
}
|
||||||
@ -33,6 +33,31 @@ export interface HttpRequestOptions {
|
|||||||
headers?: { [name: string]: string };
|
headers?: { [name: string]: string };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Headers, roughly modeled after the fetch API's headers object.
|
||||||
|
*/
|
||||||
|
export class Headers {
|
||||||
|
private headerMap = new Map<string, string>();
|
||||||
|
|
||||||
|
get(name: string): string | null {
|
||||||
|
const r = this.headerMap.get(name.toLowerCase());
|
||||||
|
if (r) {
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
set(name: string, value: string): void {
|
||||||
|
const normalizedName = name.toLowerCase();
|
||||||
|
const existing = this.headerMap.get(normalizedName);
|
||||||
|
if (existing !== undefined) {
|
||||||
|
this.headerMap.set(normalizedName, existing + "," + value);
|
||||||
|
} else {
|
||||||
|
this.headerMap.set(normalizedName, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The request library is bundled into an interface to m responseJson: object & any;ake mocking easy.
|
* The request library is bundled into an interface to m responseJson: object & any;ake mocking easy.
|
||||||
*/
|
*/
|
||||||
@ -103,12 +128,12 @@ export class BrowserHttpLib implements HttpRequestLibrary {
|
|||||||
const arr = headers.trim().split(/[\r\n]+/);
|
const arr = headers.trim().split(/[\r\n]+/);
|
||||||
|
|
||||||
// Create a map of header names to values
|
// Create a map of header names to values
|
||||||
const headerMap: { [name: string]: string } = {};
|
const headerMap = new Headers();
|
||||||
arr.forEach(function(line) {
|
arr.forEach(function(line) {
|
||||||
const parts = line.split(": ");
|
const parts = line.split(": ");
|
||||||
const header = parts.shift();
|
const header = parts.shift();
|
||||||
const value = parts.join(": ");
|
const value = parts.join(": ");
|
||||||
headerMap[header!] = value;
|
headerMap.set(header!, value);
|
||||||
});
|
});
|
||||||
const resp: HttpResponse = {
|
const resp: HttpResponse = {
|
||||||
status: myRequest.status,
|
status: myRequest.status,
|
||||||
|
@ -16,11 +16,7 @@
|
|||||||
|
|
||||||
import { InternalWalletState } from "./state";
|
import { InternalWalletState } from "./state";
|
||||||
import { WALLET_CACHE_BREAKER_CLIENT_VERSION } from "../wallet";
|
import { WALLET_CACHE_BREAKER_CLIENT_VERSION } from "../wallet";
|
||||||
import {
|
import { KeysJson, Denomination, ExchangeWireJson } from "../talerTypes";
|
||||||
KeysJson,
|
|
||||||
Denomination,
|
|
||||||
ExchangeWireJson,
|
|
||||||
} from "../talerTypes";
|
|
||||||
import { getTimestampNow, OperationError } from "../walletTypes";
|
import { getTimestampNow, OperationError } from "../walletTypes";
|
||||||
import {
|
import {
|
||||||
ExchangeRecord,
|
ExchangeRecord,
|
||||||
@ -222,6 +218,62 @@ async function updateExchangeWithKeys(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function updateExchangeWithTermsOfService(
|
||||||
|
ws: InternalWalletState,
|
||||||
|
exchangeBaseUrl: string,
|
||||||
|
) {
|
||||||
|
const exchange = await oneShotGet(ws.db, Stores.exchanges, exchangeBaseUrl);
|
||||||
|
if (!exchange) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (exchange.updateStatus != ExchangeUpdateStatus.FETCH_TERMS) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const reqUrl = new URL("terms", exchangeBaseUrl);
|
||||||
|
reqUrl.searchParams.set("cacheBreaker", WALLET_CACHE_BREAKER_CLIENT_VERSION);
|
||||||
|
const headers = {
|
||||||
|
Accept: "text/plain",
|
||||||
|
};
|
||||||
|
|
||||||
|
const resp = await ws.http.get(reqUrl.href, { headers });
|
||||||
|
if (resp.status !== 200) {
|
||||||
|
throw Error(`/terms response has unexpected status code (${resp.status})`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const tosText = await resp.text();
|
||||||
|
const tosEtag = resp.headers.get("etag") || undefined;
|
||||||
|
|
||||||
|
await runWithWriteTransaction(ws.db, [Stores.exchanges], async tx => {
|
||||||
|
const r = await tx.get(Stores.exchanges, exchangeBaseUrl);
|
||||||
|
if (!r) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (r.updateStatus != ExchangeUpdateStatus.FETCH_TERMS) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
r.termsOfServiceText = tosText;
|
||||||
|
r.termsOfServiceLastEtag = tosEtag;
|
||||||
|
r.updateStatus = ExchangeUpdateStatus.FINISHED;
|
||||||
|
await tx.put(Stores.exchanges, r);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function acceptExchangeTermsOfService(
|
||||||
|
ws: InternalWalletState,
|
||||||
|
exchangeBaseUrl: string,
|
||||||
|
etag: string | undefined,
|
||||||
|
) {
|
||||||
|
await runWithWriteTransaction(ws.db, [Stores.exchanges], async tx => {
|
||||||
|
const r = await tx.get(Stores.exchanges, exchangeBaseUrl);
|
||||||
|
if (!r) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
r.termsOfServiceAcceptedEtag = etag;
|
||||||
|
r.termsOfServiceAcceptedTimestamp = getTimestampNow();
|
||||||
|
await tx.put(Stores.exchanges, r);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetch wire information for an exchange and store it in the database.
|
* Fetch wire information for an exchange and store it in the database.
|
||||||
*
|
*
|
||||||
@ -309,7 +361,7 @@ async function updateExchangeWithWireInfo(
|
|||||||
accounts: wireInfo.accounts,
|
accounts: wireInfo.accounts,
|
||||||
feesForType: feesForType,
|
feesForType: feesForType,
|
||||||
};
|
};
|
||||||
r.updateStatus = ExchangeUpdateStatus.FINISHED;
|
r.updateStatus = ExchangeUpdateStatus.FETCH_TERMS;
|
||||||
r.lastError = undefined;
|
r.lastError = undefined;
|
||||||
await tx.put(Stores.exchanges, r);
|
await tx.put(Stores.exchanges, r);
|
||||||
});
|
});
|
||||||
@ -350,6 +402,10 @@ async function updateExchangeFromUrlImpl(
|
|||||||
updateStarted: now,
|
updateStarted: now,
|
||||||
updateReason: "initial",
|
updateReason: "initial",
|
||||||
timestampAdded: getTimestampNow(),
|
timestampAdded: getTimestampNow(),
|
||||||
|
termsOfServiceAcceptedEtag: undefined,
|
||||||
|
termsOfServiceAcceptedTimestamp: undefined,
|
||||||
|
termsOfServiceLastEtag: undefined,
|
||||||
|
termsOfServiceText: undefined,
|
||||||
};
|
};
|
||||||
await oneShotPut(ws.db, Stores.exchanges, newExchangeRecord);
|
await oneShotPut(ws.db, Stores.exchanges, newExchangeRecord);
|
||||||
} else {
|
} else {
|
||||||
@ -373,6 +429,7 @@ async function updateExchangeFromUrlImpl(
|
|||||||
|
|
||||||
await updateExchangeWithKeys(ws, baseUrl);
|
await updateExchangeWithKeys(ws, baseUrl);
|
||||||
await updateExchangeWithWireInfo(ws, baseUrl);
|
await updateExchangeWithWireInfo(ws, baseUrl);
|
||||||
|
await updateExchangeWithTermsOfService(ws, baseUrl);
|
||||||
|
|
||||||
const updatedExchange = await oneShotGet(ws.db, Stores.exchanges, baseUrl);
|
const updatedExchange = await oneShotGet(ws.db, Stores.exchanges, baseUrl);
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@ import { TipStatus, getTimestampNow, OperationError, NotificationType } from "..
|
|||||||
import { TipPickupGetResponse, TipPlanchetDetail, TipResponse } from "../talerTypes";
|
import { TipPickupGetResponse, TipPlanchetDetail, TipResponse } from "../talerTypes";
|
||||||
import * as Amounts from "../util/amounts";
|
import * as Amounts from "../util/amounts";
|
||||||
import { Stores, PlanchetRecord, WithdrawalSessionRecord, initRetryInfo, updateRetryInfoTimeout } from "../dbTypes";
|
import { Stores, PlanchetRecord, WithdrawalSessionRecord, initRetryInfo, updateRetryInfoTimeout } from "../dbTypes";
|
||||||
import { getWithdrawDetailsForAmount, getVerifiedWithdrawDenomList, processWithdrawSession } from "./withdraw";
|
import { getExchangeWithdrawalInfo, getVerifiedWithdrawDenomList, processWithdrawSession } from "./withdraw";
|
||||||
import { getTalerStampSec, extractTalerStampOrThrow } from "../util/helpers";
|
import { getTalerStampSec, extractTalerStampOrThrow } from "../util/helpers";
|
||||||
import { updateExchangeFromUrl } from "./exchanges";
|
import { updateExchangeFromUrl } from "./exchanges";
|
||||||
import { getRandomBytes, encodeCrock } from "../crypto/talerCrypto";
|
import { getRandomBytes, encodeCrock } from "../crypto/talerCrypto";
|
||||||
@ -58,7 +58,7 @@ export async function getTipStatus(
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
if (!tipRecord) {
|
if (!tipRecord) {
|
||||||
const withdrawDetails = await getWithdrawDetailsForAmount(
|
const withdrawDetails = await getExchangeWithdrawalInfo(
|
||||||
ws,
|
ws,
|
||||||
tipPickupStatus.exchange_url,
|
tipPickupStatus.exchange_url,
|
||||||
amount,
|
amount,
|
||||||
|
@ -29,8 +29,8 @@ import * as Amounts from "../util/amounts";
|
|||||||
import {
|
import {
|
||||||
getTimestampNow,
|
getTimestampNow,
|
||||||
AcceptWithdrawalResponse,
|
AcceptWithdrawalResponse,
|
||||||
DownloadedWithdrawInfo,
|
BankWithdrawDetails,
|
||||||
ReserveCreationInfo,
|
ExchangeWithdrawDetails,
|
||||||
WithdrawDetails,
|
WithdrawDetails,
|
||||||
OperationError,
|
OperationError,
|
||||||
NotificationType,
|
NotificationType,
|
||||||
@ -106,12 +106,12 @@ export function getWithdrawDenomList(
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Get information about a withdrawal from
|
* Get information about a withdrawal from
|
||||||
* a taler://withdraw URI.
|
* a taler://withdraw URI by asking the bank.
|
||||||
*/
|
*/
|
||||||
export async function getWithdrawalInfo(
|
async function getBankWithdrawalInfo(
|
||||||
ws: InternalWalletState,
|
ws: InternalWalletState,
|
||||||
talerWithdrawUri: string,
|
talerWithdrawUri: string,
|
||||||
): Promise<DownloadedWithdrawInfo> {
|
): Promise<BankWithdrawDetails> {
|
||||||
const uriResult = parseWithdrawUri(talerWithdrawUri);
|
const uriResult = parseWithdrawUri(talerWithdrawUri);
|
||||||
if (!uriResult) {
|
if (!uriResult) {
|
||||||
throw Error("can't parse URL");
|
throw Error("can't parse URL");
|
||||||
@ -140,7 +140,7 @@ export async function acceptWithdrawal(
|
|||||||
talerWithdrawUri: string,
|
talerWithdrawUri: string,
|
||||||
selectedExchange: string,
|
selectedExchange: string,
|
||||||
): Promise<AcceptWithdrawalResponse> {
|
): Promise<AcceptWithdrawalResponse> {
|
||||||
const withdrawInfo = await getWithdrawalInfo(ws, talerWithdrawUri);
|
const withdrawInfo = await getBankWithdrawalInfo(ws, talerWithdrawUri);
|
||||||
const exchangeWire = await getExchangePaytoUri(
|
const exchangeWire = await getExchangePaytoUri(
|
||||||
ws,
|
ws,
|
||||||
selectedExchange,
|
selectedExchange,
|
||||||
@ -572,11 +572,11 @@ async function processWithdrawSessionImpl(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getWithdrawDetailsForAmount(
|
export async function getExchangeWithdrawalInfo(
|
||||||
ws: InternalWalletState,
|
ws: InternalWalletState,
|
||||||
baseUrl: string,
|
baseUrl: string,
|
||||||
amount: AmountJson,
|
amount: AmountJson,
|
||||||
): Promise<ReserveCreationInfo> {
|
): Promise<ExchangeWithdrawDetails> {
|
||||||
const exchangeInfo = await updateExchangeFromUrl(ws, baseUrl);
|
const exchangeInfo = await updateExchangeFromUrl(ws, baseUrl);
|
||||||
const exchangeDetails = exchangeInfo.details;
|
const exchangeDetails = exchangeInfo.details;
|
||||||
if (!exchangeDetails) {
|
if (!exchangeDetails) {
|
||||||
@ -650,7 +650,15 @@ export async function getWithdrawDetailsForAmount(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const ret: ReserveCreationInfo = {
|
let tosAccepted = false;
|
||||||
|
|
||||||
|
if (exchangeInfo.termsOfServiceAcceptedTimestamp) {
|
||||||
|
if (exchangeInfo.termsOfServiceAcceptedEtag == exchangeInfo.termsOfServiceLastEtag) {
|
||||||
|
tosAccepted = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const ret: ExchangeWithdrawDetails = {
|
||||||
earliestDepositExpiration,
|
earliestDepositExpiration,
|
||||||
exchangeInfo,
|
exchangeInfo,
|
||||||
exchangeWireAccounts,
|
exchangeWireAccounts,
|
||||||
@ -665,6 +673,7 @@ export async function getWithdrawDetailsForAmount(
|
|||||||
walletVersion: WALLET_PROTOCOL_VERSION,
|
walletVersion: WALLET_PROTOCOL_VERSION,
|
||||||
wireFees: exchangeWireInfo,
|
wireFees: exchangeWireInfo,
|
||||||
withdrawFee: acc,
|
withdrawFee: acc,
|
||||||
|
termsOfServiceAccepted: tosAccepted,
|
||||||
};
|
};
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@ -674,17 +683,17 @@ export async function getWithdrawDetailsForUri(
|
|||||||
talerWithdrawUri: string,
|
talerWithdrawUri: string,
|
||||||
maybeSelectedExchange?: string,
|
maybeSelectedExchange?: string,
|
||||||
): Promise<WithdrawDetails> {
|
): Promise<WithdrawDetails> {
|
||||||
const info = await getWithdrawalInfo(ws, talerWithdrawUri);
|
const info = await getBankWithdrawalInfo(ws, talerWithdrawUri);
|
||||||
let rci: ReserveCreationInfo | undefined = undefined;
|
let rci: ExchangeWithdrawDetails | undefined = undefined;
|
||||||
if (maybeSelectedExchange) {
|
if (maybeSelectedExchange) {
|
||||||
rci = await getWithdrawDetailsForAmount(
|
rci = await getExchangeWithdrawalInfo(
|
||||||
ws,
|
ws,
|
||||||
maybeSelectedExchange,
|
maybeSelectedExchange,
|
||||||
info.amount,
|
info.amount,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
withdrawInfo: info,
|
bankWithdrawDetails: info,
|
||||||
reserveCreationInfo: rci,
|
exchangeWithdrawDetails: rci,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -37,9 +37,8 @@ import * as Amounts from "./util/amounts";
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
acceptWithdrawal,
|
acceptWithdrawal,
|
||||||
getWithdrawalInfo,
|
|
||||||
getWithdrawDetailsForUri,
|
getWithdrawDetailsForUri,
|
||||||
getWithdrawDetailsForAmount,
|
getExchangeWithdrawalInfo,
|
||||||
} from "./wallet-impl/withdraw";
|
} from "./wallet-impl/withdraw";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@ -79,7 +78,7 @@ import {
|
|||||||
TipStatus,
|
TipStatus,
|
||||||
WalletBalance,
|
WalletBalance,
|
||||||
PreparePayResult,
|
PreparePayResult,
|
||||||
DownloadedWithdrawInfo,
|
BankWithdrawDetails,
|
||||||
WithdrawDetails,
|
WithdrawDetails,
|
||||||
AcceptWithdrawalResponse,
|
AcceptWithdrawalResponse,
|
||||||
PurchaseDetails,
|
PurchaseDetails,
|
||||||
@ -88,6 +87,7 @@ import {
|
|||||||
HistoryQuery,
|
HistoryQuery,
|
||||||
WalletNotification,
|
WalletNotification,
|
||||||
NotificationType,
|
NotificationType,
|
||||||
|
ExchangeWithdrawDetails,
|
||||||
} from "./walletTypes";
|
} from "./walletTypes";
|
||||||
import { Logger } from "./util/logging";
|
import { Logger } from "./util/logging";
|
||||||
|
|
||||||
@ -97,6 +97,7 @@ import {
|
|||||||
updateExchangeFromUrl,
|
updateExchangeFromUrl,
|
||||||
getExchangeTrust,
|
getExchangeTrust,
|
||||||
getExchangePaytoUri,
|
getExchangePaytoUri,
|
||||||
|
acceptExchangeTermsOfService,
|
||||||
} from "./wallet-impl/exchanges";
|
} from "./wallet-impl/exchanges";
|
||||||
import { processReserve } from "./wallet-impl/reserves";
|
import { processReserve } from "./wallet-impl/reserves";
|
||||||
|
|
||||||
@ -167,8 +168,11 @@ export class Wallet {
|
|||||||
return getExchangePaytoUri(this.ws, exchangeBaseUrl, supportedTargetTypes);
|
return getExchangePaytoUri(this.ws, exchangeBaseUrl, supportedTargetTypes);
|
||||||
}
|
}
|
||||||
|
|
||||||
getWithdrawDetailsForAmount(baseUrl: any, amount: AmountJson): any {
|
getWithdrawDetailsForAmount(
|
||||||
return getWithdrawDetailsForAmount(this.ws, baseUrl, amount);
|
exchangeBaseUrl: string,
|
||||||
|
amount: AmountJson,
|
||||||
|
): Promise<ExchangeWithdrawDetails> {
|
||||||
|
return getExchangeWithdrawalInfo(this.ws, exchangeBaseUrl, amount);
|
||||||
}
|
}
|
||||||
|
|
||||||
addNotificationListener(f: (n: WalletNotification) => void): void {
|
addNotificationListener(f: (n: WalletNotification) => void): void {
|
||||||
@ -194,13 +198,21 @@ export class Wallet {
|
|||||||
await updateExchangeFromUrl(this.ws, pending.exchangeBaseUrl, forceNow);
|
await updateExchangeFromUrl(this.ws, pending.exchangeBaseUrl, forceNow);
|
||||||
break;
|
break;
|
||||||
case "refresh":
|
case "refresh":
|
||||||
await processRefreshSession(this.ws, pending.refreshSessionId, forceNow);
|
await processRefreshSession(
|
||||||
|
this.ws,
|
||||||
|
pending.refreshSessionId,
|
||||||
|
forceNow,
|
||||||
|
);
|
||||||
break;
|
break;
|
||||||
case "reserve":
|
case "reserve":
|
||||||
await processReserve(this.ws, pending.reservePub, forceNow);
|
await processReserve(this.ws, pending.reservePub, forceNow);
|
||||||
break;
|
break;
|
||||||
case "withdraw":
|
case "withdraw":
|
||||||
await processWithdrawSession(this.ws, pending.withdrawSessionId, forceNow);
|
await processWithdrawSession(
|
||||||
|
this.ws,
|
||||||
|
pending.withdrawSessionId,
|
||||||
|
forceNow,
|
||||||
|
);
|
||||||
break;
|
break;
|
||||||
case "proposal-choice":
|
case "proposal-choice":
|
||||||
// Nothing to do, user needs to accept/reject
|
// Nothing to do, user needs to accept/reject
|
||||||
@ -524,6 +536,13 @@ export class Wallet {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async acceptExchangeTermsOfService(
|
||||||
|
exchangeBaseUrl: string,
|
||||||
|
etag: string | undefined,
|
||||||
|
) {
|
||||||
|
return acceptExchangeTermsOfService(this.ws, exchangeBaseUrl, etag);
|
||||||
|
}
|
||||||
|
|
||||||
async getDenoms(exchangeUrl: string): Promise<DenominationRecord[]> {
|
async getDenoms(exchangeUrl: string): Promise<DenominationRecord[]> {
|
||||||
const denoms = await oneShotIterIndex(
|
const denoms = await oneShotIterIndex(
|
||||||
this.db,
|
this.db,
|
||||||
@ -663,6 +682,10 @@ export class Wallet {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inform the wallet that the status of a reserve has changed (e.g. due to a
|
||||||
|
* confirmation from the bank.).
|
||||||
|
*/
|
||||||
public async handleNotifyReserve() {
|
public async handleNotifyReserve() {
|
||||||
const reserves = await oneShotIter(this.db, Stores.reserves).toArray();
|
const reserves = await oneShotIter(this.db, Stores.reserves).toArray();
|
||||||
for (const r of reserves) {
|
for (const r of reserves) {
|
||||||
@ -687,20 +710,6 @@ export class Wallet {
|
|||||||
// strategy to test it.
|
// strategy to test it.
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get information about a withdrawal from
|
|
||||||
* a taler://withdraw URI.
|
|
||||||
*/
|
|
||||||
async getWithdrawalInfo(
|
|
||||||
talerWithdrawUri: string,
|
|
||||||
): Promise<DownloadedWithdrawInfo> {
|
|
||||||
try {
|
|
||||||
return getWithdrawalInfo(this.ws, talerWithdrawUri);
|
|
||||||
} finally {
|
|
||||||
this.latch.trigger();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async acceptWithdrawal(
|
async acceptWithdrawal(
|
||||||
talerWithdrawUri: string,
|
talerWithdrawUri: string,
|
||||||
selectedExchange: string,
|
selectedExchange: string,
|
||||||
|
@ -70,7 +70,7 @@ export class CreateReserveResponse {
|
|||||||
*
|
*
|
||||||
* Sent to the wallet frontend to be rendered and shown to the user.
|
* Sent to the wallet frontend to be rendered and shown to the user.
|
||||||
*/
|
*/
|
||||||
export interface ReserveCreationInfo {
|
export interface ExchangeWithdrawDetails {
|
||||||
/**
|
/**
|
||||||
* Exchange that the reserve will be created at.
|
* Exchange that the reserve will be created at.
|
||||||
*/
|
*/
|
||||||
@ -107,6 +107,11 @@ export interface ReserveCreationInfo {
|
|||||||
*/
|
*/
|
||||||
isAudited: boolean;
|
isAudited: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Did the user already accept the current terms of service for the exchange?
|
||||||
|
*/
|
||||||
|
termsOfServiceAccepted: boolean;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The exchange is trusted directly.
|
* The exchange is trusted directly.
|
||||||
*/
|
*/
|
||||||
@ -148,8 +153,8 @@ export interface ReserveCreationInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface WithdrawDetails {
|
export interface WithdrawDetails {
|
||||||
withdrawInfo: DownloadedWithdrawInfo;
|
bankWithdrawDetails: BankWithdrawDetails;
|
||||||
reserveCreationInfo: ReserveCreationInfo | undefined;
|
exchangeWithdrawDetails: ExchangeWithdrawDetails | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -449,7 +454,7 @@ export interface PreparePayResultPaid {
|
|||||||
nextUrl: string;
|
nextUrl: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DownloadedWithdrawInfo {
|
export interface BankWithdrawDetails {
|
||||||
selectionDone: boolean;
|
selectionDone: boolean;
|
||||||
transferDone: boolean;
|
transferDone: boolean;
|
||||||
amount: AmountJson;
|
amount: AmountJson;
|
||||||
|
@ -75,7 +75,7 @@ export interface MessageMap {
|
|||||||
};
|
};
|
||||||
"reserve-creation-info": {
|
"reserve-creation-info": {
|
||||||
request: { baseUrl: string; amount: AmountJson };
|
request: { baseUrl: string; amount: AmountJson };
|
||||||
response: walletTypes.ReserveCreationInfo;
|
response: walletTypes.ExchangeWithdrawDetails;
|
||||||
};
|
};
|
||||||
"get-history": {
|
"get-history": {
|
||||||
request: {};
|
request: {};
|
||||||
|
@ -57,9 +57,9 @@ function NewExchangeSelection(props: { talerWithdrawUri: string }) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
console.log("got withdrawDetails", d);
|
console.log("got withdrawDetails", d);
|
||||||
if (!selectedExchange && d.withdrawInfo.suggestedExchange) {
|
if (!selectedExchange && d.bankWithdrawDetails.suggestedExchange) {
|
||||||
console.log("setting selected exchange");
|
console.log("setting selected exchange");
|
||||||
setSelectedExchange(d.withdrawInfo.suggestedExchange);
|
setSelectedExchange(d.bankWithdrawDetails.suggestedExchange);
|
||||||
}
|
}
|
||||||
setDetails(d);
|
setDetails(d);
|
||||||
};
|
};
|
||||||
@ -101,7 +101,7 @@ function NewExchangeSelection(props: { talerWithdrawUri: string }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (selecting) {
|
if (selecting) {
|
||||||
const bankSuggestion = details && details.withdrawInfo.suggestedExchange;
|
const bankSuggestion = details && details.bankWithdrawDetails.suggestedExchange;
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{i18n.str`Please select an exchange. You can review the details before after your selection.`}
|
{i18n.str`Please select an exchange. You can review the details before after your selection.`}
|
||||||
@ -157,7 +157,7 @@ function NewExchangeSelection(props: { talerWithdrawUri: string }) {
|
|||||||
<div>
|
<div>
|
||||||
<i18n.Translate wrap="p">
|
<i18n.Translate wrap="p">
|
||||||
You are about to withdraw{" "}
|
You are about to withdraw{" "}
|
||||||
<strong>{renderAmount(details.withdrawInfo.amount)}</strong> from your
|
<strong>{renderAmount(details.bankWithdrawDetails.amount)}</strong> from your
|
||||||
bank account into your wallet.
|
bank account into your wallet.
|
||||||
</i18n.Translate>
|
</i18n.Translate>
|
||||||
<div>
|
<div>
|
||||||
@ -188,8 +188,8 @@ function NewExchangeSelection(props: { talerWithdrawUri: string }) {
|
|||||||
</span>
|
</span>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
{details.reserveCreationInfo ? (
|
{details.exchangeWithdrawDetails ? (
|
||||||
<WithdrawDetailView rci={details.reserveCreationInfo} />
|
<WithdrawDetailView rci={details.exchangeWithdrawDetails} />
|
||||||
) : null}
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -26,7 +26,7 @@
|
|||||||
import { AmountJson } from "../util/amounts";
|
import { AmountJson } from "../util/amounts";
|
||||||
import * as Amounts from "../util/amounts";
|
import * as Amounts from "../util/amounts";
|
||||||
import { DenominationRecord } from "../dbTypes";
|
import { DenominationRecord } from "../dbTypes";
|
||||||
import { ReserveCreationInfo } from "../walletTypes";
|
import { ExchangeWithdrawDetails } from "../walletTypes";
|
||||||
import * as moment from "moment";
|
import * as moment from "moment";
|
||||||
import * as i18n from "../i18n";
|
import * as i18n from "../i18n";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
@ -126,7 +126,7 @@ export class Collapsible extends React.Component<
|
|||||||
}
|
}
|
||||||
|
|
||||||
function AuditorDetailsView(props: {
|
function AuditorDetailsView(props: {
|
||||||
rci: ReserveCreationInfo | null;
|
rci: ExchangeWithdrawDetails | null;
|
||||||
}): JSX.Element {
|
}): JSX.Element {
|
||||||
const rci = props.rci;
|
const rci = props.rci;
|
||||||
console.log("rci", rci);
|
console.log("rci", rci);
|
||||||
@ -163,7 +163,7 @@ function AuditorDetailsView(props: {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function FeeDetailsView(props: {
|
function FeeDetailsView(props: {
|
||||||
rci: ReserveCreationInfo | null;
|
rci: ExchangeWithdrawDetails | null;
|
||||||
}): JSX.Element {
|
}): JSX.Element {
|
||||||
const rci = props.rci;
|
const rci = props.rci;
|
||||||
if (!rci) {
|
if (!rci) {
|
||||||
@ -271,7 +271,7 @@ function FeeDetailsView(props: {
|
|||||||
* Shows details about a withdraw request.
|
* Shows details about a withdraw request.
|
||||||
*/
|
*/
|
||||||
export function WithdrawDetailView(props: {
|
export function WithdrawDetailView(props: {
|
||||||
rci: ReserveCreationInfo | null;
|
rci: ExchangeWithdrawDetails | null;
|
||||||
}): JSX.Element {
|
}): JSX.Element {
|
||||||
const rci = props.rci;
|
const rci = props.rci;
|
||||||
return (
|
return (
|
||||||
|
@ -34,7 +34,7 @@ import {
|
|||||||
import {
|
import {
|
||||||
BenchmarkResult,
|
BenchmarkResult,
|
||||||
ConfirmPayResult,
|
ConfirmPayResult,
|
||||||
ReserveCreationInfo,
|
ExchangeWithdrawDetails,
|
||||||
SenderWireInfos,
|
SenderWireInfos,
|
||||||
TipStatus,
|
TipStatus,
|
||||||
WalletBalance,
|
WalletBalance,
|
||||||
@ -102,7 +102,7 @@ async function callBackend<T extends MessageType>(
|
|||||||
* from a given reserve.
|
* from a given reserve.
|
||||||
*/
|
*/
|
||||||
export function getReserveCreationInfo(baseUrl: string,
|
export function getReserveCreationInfo(baseUrl: string,
|
||||||
amount: AmountJson): Promise<ReserveCreationInfo> {
|
amount: AmountJson): Promise<ExchangeWithdrawDetails> {
|
||||||
return callBackend("reserve-creation-info", { baseUrl, amount });
|
return callBackend("reserve-creation-info", { baseUrl, amount });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user