From 9f009873809d5b7e8d1d95d77f009862724c577b Mon Sep 17 00:00:00 2001 From: Sebastian Date: Mon, 13 Sep 2021 15:32:06 -0300 Subject: added integration with the wallet-core to get info about the last tos approved --- packages/taler-util/src/walletTypes.ts | 2 + packages/taler-wallet-core/src/db.ts | 5 +++ .../src/operations/backup/import.ts | 1 + .../taler-wallet-core/src/operations/exchanges.ts | 13 ++++-- .../taler-wallet-core/src/operations/withdraw.ts | 37 +++++++++++++++++ packages/taler-wallet-core/src/wallet.ts | 2 +- .../src/cta/Withdraw.stories.tsx | 4 +- .../taler-wallet-webextension/src/cta/Withdraw.tsx | 46 +++++++++++++++++++--- packages/taler-wallet-webextension/src/wxApi.ts | 13 +++++- 9 files changed, 109 insertions(+), 14 deletions(-) diff --git a/packages/taler-util/src/walletTypes.ts b/packages/taler-util/src/walletTypes.ts index 7ec182fa2..79403ac69 100644 --- a/packages/taler-util/src/walletTypes.ts +++ b/packages/taler-util/src/walletTypes.ts @@ -716,12 +716,14 @@ export const codecForGetWithdrawalDetailsForUri = (): Codec => buildCodecForObject() .property("exchangeBaseUrl", codecForString()) .property("amount", codecForAmountJson()) + .property("tosAcceptedFormat", codecOptional(codecForList(codecForString()))) .build("GetExchangeWithdrawalInfo"); export interface AbortProposalRequest { diff --git a/packages/taler-wallet-core/src/db.ts b/packages/taler-wallet-core/src/db.ts index 7ea8b9ead..902f749cf 100644 --- a/packages/taler-wallet-core/src/db.ts +++ b/packages/taler-wallet-core/src/db.ts @@ -449,6 +449,11 @@ export interface ExchangeDetailsRecord { */ termsOfServiceText: string | undefined; + /** + * content-type of the last downloaded termsOfServiceText. + */ + termsOfServiceContentType: string | undefined; + /** * ETag for last terms of service download. */ diff --git a/packages/taler-wallet-core/src/operations/backup/import.ts b/packages/taler-wallet-core/src/operations/backup/import.ts index 9eee34cf0..7623ab189 100644 --- a/packages/taler-wallet-core/src/operations/backup/import.ts +++ b/packages/taler-wallet-core/src/operations/backup/import.ts @@ -306,6 +306,7 @@ export async function importBackup( termsOfServiceAcceptedEtag: backupExchangeDetails.tos_accepted_etag, termsOfServiceText: undefined, termsOfServiceLastEtag: undefined, + termsOfServiceContentType: undefined, termsOfServiceAcceptedTimestamp: backupExchangeDetails.tos_accepted_timestamp, wireInfo, diff --git a/packages/taler-wallet-core/src/operations/exchanges.ts b/packages/taler-wallet-core/src/operations/exchanges.ts index cf52e00b6..9cd20c673 100644 --- a/packages/taler-wallet-core/src/operations/exchanges.ts +++ b/packages/taler-wallet-core/src/operations/exchanges.ts @@ -127,20 +127,22 @@ function getExchangeRequestTimeout(e: ExchangeRecord): Duration { return { d_ms: 5000 }; } -interface ExchangeTosDownloadResult { +export interface ExchangeTosDownloadResult { tosText: string; tosEtag: string; + tosContentType: string; } -async function downloadExchangeWithTermsOfService( +export async function downloadExchangeWithTermsOfService( exchangeBaseUrl: string, http: HttpRequestLibrary, timeout: Duration, + contentType: string, ): Promise { const reqUrl = new URL("terms", exchangeBaseUrl); reqUrl.searchParams.set("cacheBreaker", WALLET_CACHE_BREAKER_CLIENT_VERSION); const headers = { - Accept: "text/plain", + Accept: contentType, }; const resp = await http.get(reqUrl.href, { @@ -149,8 +151,9 @@ async function downloadExchangeWithTermsOfService( }); const tosText = await readSuccessResponseTextOrThrow(resp); const tosEtag = resp.headers.get("etag") || "unknown"; + const tosContentType = resp.headers.get("content-type") || "text/plain"; - return { tosText, tosEtag }; + return { tosText, tosEtag, tosContentType }; } /** @@ -469,6 +472,7 @@ async function updateExchangeFromUrlImpl( baseUrl, ws.http, timeout, + "text/plain" ); let recoupGroupId: string | undefined = undefined; @@ -506,6 +510,7 @@ async function updateExchangeFromUrlImpl( wireInfo, termsOfServiceText: tosDownload.tosText, termsOfServiceAcceptedEtag: undefined, + termsOfServiceContentType: tosDownload.tosContentType, termsOfServiceLastEtag: tosDownload.tosEtag, termsOfServiceAcceptedTimestamp: getTimestampNow(), }; diff --git a/packages/taler-wallet-core/src/operations/withdraw.ts b/packages/taler-wallet-core/src/operations/withdraw.ts index 620ad88be..f63723535 100644 --- a/packages/taler-wallet-core/src/operations/withdraw.ts +++ b/packages/taler-wallet-core/src/operations/withdraw.ts @@ -66,6 +66,8 @@ import { WALLET_BANK_INTEGRATION_PROTOCOL_VERSION, WALLET_EXCHANGE_PROTOCOL_VERSION, } from "../versions.js"; +import { stringify } from "querystring"; +import { downloadExchangeWithTermsOfService, ExchangeTosDownloadResult } from "./exchanges"; /** * Logger for this file. @@ -131,6 +133,11 @@ export interface ExchangeWithdrawDetails { */ termsOfServiceAccepted: boolean; + /** + * Tos + */ + tosRequested: ExchangeTosDownloadResult | undefined + /** * The exchange is trusted directly. */ @@ -930,6 +937,7 @@ export async function getExchangeWithdrawalInfo( ws: InternalWalletState, baseUrl: string, amount: AmountJson, + tosAcceptedFormat?: string[], ): Promise { const { exchange, @@ -996,6 +1004,34 @@ export async function getExchangeWithdrawalInfo( } } + const noTosDownloaded = + exchangeDetails.termsOfServiceContentType === undefined || + exchangeDetails.termsOfServiceAcceptedEtag === undefined || + exchangeDetails.termsOfServiceText === undefined; + + let tosFound: ExchangeTosDownloadResult | undefined = noTosDownloaded ? undefined : { + tosContentType: exchangeDetails.termsOfServiceContentType!, + tosEtag: exchangeDetails.termsOfServiceAcceptedEtag!, + tosText: exchangeDetails.termsOfServiceText!, + }; + + try { + if (tosAcceptedFormat) for (const format of tosAcceptedFormat) { + const resp = await downloadExchangeWithTermsOfService( + exchangeDetails.exchangeBaseUrl, + ws.http, + { d_ms: 1000 }, + format + ); + if (resp.tosContentType === format) { + tosFound = resp + break + } + } + } catch (e) { + tosFound = undefined + } + const withdrawFee = Amounts.sub( selectedDenoms.totalWithdrawCost, selectedDenoms.totalCoinValue, @@ -1018,6 +1054,7 @@ export async function getExchangeWithdrawalInfo( walletVersion: WALLET_EXCHANGE_PROTOCOL_VERSION, withdrawFee, termsOfServiceAccepted: tosAccepted, + tosRequested: tosFound }; return ret; } diff --git a/packages/taler-wallet-core/src/wallet.ts b/packages/taler-wallet-core/src/wallet.ts index cbaf03c3b..a0da3d356 100644 --- a/packages/taler-wallet-core/src/wallet.ts +++ b/packages/taler-wallet-core/src/wallet.ts @@ -696,7 +696,7 @@ async function dispatchRequestInternal( } case "getExchangeWithdrawalInfo": { const req = codecForGetExchangeWithdrawalInfo().decode(payload); - return await getExchangeWithdrawalInfo(ws, req.exchangeBaseUrl, req.amount); + return await getExchangeWithdrawalInfo(ws, req.exchangeBaseUrl, req.amount, req.tosAcceptedFormat); } case "acceptManualWithdrawal": { const req = codecForAcceptManualWithdrawalRequet().decode(payload); diff --git a/packages/taler-wallet-webextension/src/cta/Withdraw.stories.tsx b/packages/taler-wallet-webextension/src/cta/Withdraw.stories.tsx index 022b92454..94fdea8fb 100644 --- a/packages/taler-wallet-webextension/src/cta/Withdraw.stories.tsx +++ b/packages/taler-wallet-webextension/src/cta/Withdraw.stories.tsx @@ -35,6 +35,8 @@ export default { }; const termsXml = ` + +
Terms Of Service @@ -234,7 +236,6 @@ const termsXml = ` any lost profits, data loss, cost of procurement of substitute goods or -* TLSv1.3 (IN), TLS Unknown, Unknown (23): services, or direct, indirect, incidental, special, punitive, compensatory, or consequential damages of any kind whatsoever resulting from: @@ -377,7 +378,6 @@ const termsXml = `
- `; export const WithdrawNewTermsXML = createExample(TestedComponent, { diff --git a/packages/taler-wallet-webextension/src/cta/Withdraw.tsx b/packages/taler-wallet-webextension/src/cta/Withdraw.tsx index c0347000d..ac25bcd15 100644 --- a/packages/taler-wallet-webextension/src/cta/Withdraw.tsx +++ b/packages/taler-wallet-webextension/src/cta/Withdraw.tsx @@ -30,7 +30,7 @@ import { LogoHeader } from '../components/LogoHeader'; import { Part } from '../components/Part'; import { ButtonDestructive, ButtonSuccess, ButtonWarning, LinkSuccess, TermsOfService, WalletAction } from '../components/styled'; import { - acceptWithdrawal, getExchangeWithdrawalInfo, getWithdrawalDetailsForUri, onUpdateNotification + acceptWithdrawal, getExchangeWithdrawalInfo, getWithdrawalDetailsForUri, onUpdateNotification, setExchangeTosAccepted } from "../wxApi"; import { h } from 'preact'; @@ -48,6 +48,7 @@ export interface ViewProps { onAccept: (b: boolean) => void; reviewing: boolean; accepted: boolean; + confirmed: boolean; terms: { value?: TermsDocument; status: TermsStatus; @@ -75,7 +76,7 @@ function amountToString(text: AmountLike) { return `${amount} ${aj.currency}` } -export function View({ details, amount, onWithdraw, terms, reviewing, onReview, onAccept, accepted }: ViewProps) { +export function View({ details, amount, onWithdraw, terms, reviewing, onReview, onAccept, accepted, confirmed }: ViewProps) { const needsReview = terms.status === 'changed' || terms.status === 'new' return ( @@ -172,7 +173,7 @@ export function View({ details, amount, onWithdraw, terms, reviewing, onReview,
{i18n.str`Confirm withdrawal`} @@ -203,6 +204,7 @@ export function WithdrawPage({ talerWithdrawUri, ...rest }: Props): JSX.Element const [updateCounter, setUpdateCounter] = useState(1); const [reviewing, setReviewing] = useState(false) const [accepted, setAccepted] = useState(false) + const [confirmed, setConfirmed] = useState(false) useEffect(() => { return onUpdateNotification(() => { @@ -231,7 +233,8 @@ export function WithdrawPage({ talerWithdrawUri, ...rest }: Props): JSX.Element if (!uriInfo || !uriInfo.defaultExchangeBaseUrl) return const res = await getExchangeWithdrawalInfo({ exchangeBaseUrl: uriInfo.defaultExchangeBaseUrl, - amount: Amounts.parseOrThrow(uriInfo.amount) + amount: Amounts.parseOrThrow(uriInfo.amount), + tosAcceptedFormat: ['text/json', 'text/xml', 'text/pdf'] }) setDetails(res) } @@ -242,10 +245,19 @@ export function WithdrawPage({ talerWithdrawUri, ...rest }: Props): JSX.Element return missing withdraw uri; } + const onAccept = async (): Promise => { + if (!details) { + throw Error("can't accept, no exchange selected"); + } + await setExchangeTosAccepted(details.exchangeDetails.exchangeBaseUrl, details.tosRequested?.tosEtag) + setAccepted(true) + } + const onWithdraw = async (): Promise => { if (!details) { throw Error("can't accept, no exchange selected"); } + setConfirmed(true) console.log("accepting exchange", details.exchangeInfo.baseUrl); const res = await acceptWithdrawal(talerWithdrawUri, details.exchangeInfo.baseUrl); console.log("accept withdrawal response", res); @@ -267,11 +279,33 @@ export function WithdrawPage({ talerWithdrawUri, ...rest }: Props): JSX.Element return Getting withdrawal details.; } + let termsContent: TermsDocument | undefined = undefined; + if (details.tosRequested) { + if (details.tosRequested.tosContentType === 'text/xml') { + try { + const document = new DOMParser().parseFromString(details.tosRequested.tosText, "text/xml") + termsContent = { type: 'xml', document } + } catch (e) { + console.log(e) + debugger; + } + } + } + + const status: TermsStatus = !termsContent ? 'notfound' : ( + !details.exchangeDetails.termsOfServiceAcceptedEtag ? 'new' : ( + details.tosRequested?.tosEtag !== details.exchangeDetails.termsOfServiceAcceptedEtag ? 'changed' : 'accepted' + )) + + return diff --git a/packages/taler-wallet-webextension/src/wxApi.ts b/packages/taler-wallet-webextension/src/wxApi.ts index acb28ffec..63774b00b 100644 --- a/packages/taler-wallet-webextension/src/wxApi.ts +++ b/packages/taler-wallet-webextension/src/wxApi.ts @@ -39,6 +39,7 @@ import { RetryTransactionRequest, SetWalletDeviceIdRequest, GetExchangeWithdrawalInfo, + AcceptExchangeTosRequest, } from "@gnu-taler/taler-util"; import { AddBackupProviderRequest, BackupProviderState, OperationFailedError, RemoveBackupProviderRequest } from "@gnu-taler/taler-wallet-core"; import { BackupInfo } from "@gnu-taler/taler-wallet-core"; @@ -251,6 +252,16 @@ export function acceptWithdrawal( }); } +export function setExchangeTosAccepted( + exchangeBaseUrl: string, + etag: string | undefined +): Promise { + return callBackend("setExchangeTosAccepted", { + exchangeBaseUrl, etag + } as AcceptExchangeTosRequest) +} + + /** * Get diagnostics information */ @@ -287,7 +298,7 @@ export function getWithdrawalDetailsForUri( /** * Get diagnostics information */ - export function getExchangeWithdrawalInfo( +export function getExchangeWithdrawalInfo( req: GetExchangeWithdrawalInfo, ): Promise { return callBackend("getExchangeWithdrawalInfo", req); -- cgit v1.2.3