added example of withdrawal use cases

This commit is contained in:
Sebastian 2021-10-12 15:18:29 -03:00
parent be8e3f4b1d
commit b8d03b6b2a
No known key found for this signature in database
GPG Key ID: BE4FF68352439FC1
11 changed files with 836 additions and 125 deletions

View File

@ -51,7 +51,7 @@ check: compile
webextension: compile
cd ./packages/taler-wallet-webextension/ && ./pack.sh
.PHONY: dev-view
.PHONY: webextension-dev-view
webextension-dev-view: compile
pnpm run --filter @gnu-taler/taler-wallet-webextension storybook

View File

@ -35,6 +35,405 @@ export default {
},
};
const termsHtml = `<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Terms Of Service &#8212; Taler Terms of Service</title>
</head><body>
<div>
Terms of service
</div>
<div>
A complete separated html with it's own design
</div>
</body>
</html>
`
const termsPlain = `
Terms Of Service
****************
Last Updated: 12.4.2019
Welcome! Taler Systems SA (we, our, or us) provides a payment
service through our Internet presence (collectively the Services).
Before using our Services, please read the Terms of Service (the
Terms or the Agreement) carefully.
Overview
========
This section provides a brief summary of the highlights of this
Agreement. Please note that when you accept this Agreement, you are
accepting all of the terms and conditions and not just this section.
We and possibly other third parties provide Internet services which
interact with the Taler Wallets self-hosted personal payment
application. When using the Taler Wallet to interact with our
Services, you are agreeing to our Terms, so please read carefully.
Highlights:
-----------
* You are responsible for keeping the data in your Taler Wallet at
all times under your control. Any losses arising from you not
being in control of your private information are your problem.
* We will try to transfer funds we hold in escrow for our users to
any legal recipient to the best of our ability within the
limitations of the law and our implementation. However, the
Services offered today are highly experimental and the set of
recipients of funds is severely restricted.
* For our Services, we may charge transaction fees. The specific
fee structure is provided based on the Taler protocol and should
be shown to you when you withdraw electronic coins using a Taler
Wallet. You agree and understand that the Taler protocol allows
for the fee structure to change.
* You agree to not intentionally overwhelm our systems with
requests and follow responsible disclosure if you find security
issues in our services.
* We cannot be held accountable for our Services not being
available due to circumstances beyond our control. If we modify
or terminate our services, we will try to give you the
opportunity to recover your funds. However, given the
experimental state of the Services today, this may not be
possible. You are strongly advised to limit your use of the
Service to small-scale experiments expecting total loss of all
funds.
These terms outline approved uses of our Services. The Services and
these Terms are still at an experimental stage. If you have any
questions or comments related to this Agreement, please send us a
message to legal@taler-systems.com. If you do not agree to this
Agreement, you must not use our Services.
How you accept this policy
==========================
By sending funds to us (to top-up your Taler Wallet), you acknowledge
that you have read, understood, and agreed to these Terms. We reserve
the right to change these Terms at any time. If you disagree with the
change, we may in the future offer you with an easy option to recover
your unspent funds. However, in the current experimental period you
acknowledge that this feature is not yet available, resulting in your
funds being lost unless you accept the new Terms. If you continue to
use our Services other than to recover your unspent funds, your
continued use of our Services following any such change will signify
your acceptance to be bound by the then current Terms. Please check
the effective date above to determine if there have been any changes
since you have last reviewed these Terms.
Services
========
We will try to transfer funds that we hold in escrow for our users to
any legal recipient to the best of our ability and within the
limitations of the law and our implementation. However, the Services
offered today are highly experimental and the set of recipients of
funds is severely restricted. The Taler Wallet can be loaded by
exchanging fiat currencies against electronic coins. We are providing
this exchange service. Once your Taler Wallet is loaded with
electronic coins they can be spent for purchases if the seller is
accepting Taler as a means of payment. We are not guaranteeing that
any seller is accepting Taler at all or a particular seller. The
seller or recipient of deposits of electronic coins must specify the
target account, as per the design of the Taler protocol. They are
responsible for following the protocol and specifying the correct bank
account, and are solely liable for any losses that may arise from
specifying the wrong account. We will allow the government to link
wire transfers to the underlying contract hash. It is the
responsibility of recipients to preserve the full contracts and to pay
whatever taxes and charges may be applicable. Technical issues may
lead to situations where we are unable to make transfers at all or
lead to incorrect transfers that cannot be reversed. We will only
refuse to execute transfers if the transfers are prohibited by a
competent legal authority and we are ordered to do so.
Fees
====
You agree to pay the fees for exchanges and withdrawals completed via
the Taler Wallet ("Fees") as defined by us, which we may change from
time to time. With the exception of wire transfer fees, Taler
transaction fees are set for any electronic coin at the time of
withdrawal and fixed throughout the validity period of the respective
electronic coin. Your wallet should obtain and display applicable fees
when withdrawing funds. Fees for coins obtained as change may differ
from the fees applicable to the original coin. Wire transfer fees that
are independent from electronic coins may change annually. You
authorize us to charge or deduct applicable fees owed in connection
with deposits, exchanges and withdrawals following the rules of the
Taler protocol. We reserve the right to provide different types of
rewards to users either in the form of discount for our Services or in
any other form at our discretion and without prior notice to you.
Eligibility
===========
To be eligible to use our Services, you must be able to form legally
binding contracts or have the permission of your legal guardian. By
using our Services, you represent and warrant that you meet all
eligibility requirements that we outline in these Terms.
Financial self-responsibility
=============================
You will be responsible for maintaining the availability, integrity
and confidentiality of the data stored in your wallet. When you setup
a Taler Wallet, you are strongly advised to follow the precautionary
measures offered by the software to minimize the chances to losse
access to or control over your Wallet data. We will not be liable for
any loss or damage arising from your failure to comply with this
paragraph.
Copyrights and trademarks
=========================
The Taler Wallet is released under the terms of the GNU General Public
License (GNU GPL). You have the right to access, use, and share the
Taler Wallet, in modified or unmodified form. However, the GPL is a
strong copyleft license, which means that any derivative works must be
distributed under the same license terms as the original software. If
you have any questions, you should review the GNU GPLs full terms and
conditions at https://www.gnu.org/licenses/gpl-3.0.en.html. “Taler”
itself is a trademark of Taler Systems SA. You are welcome to use the
name in relation to processing payments using the Taler protocol,
assuming your use is compatible with an official release from the GNU
Project that is not older than two years.
Your use of our services
========================
When using our Services, you agree to not take any action that
intentionally imposes an unreasonable load on our infrastructure. If
you find security problems in our Services, you agree to first report
them to security@taler-systems.com and grant us the right to publish
your report. We warrant that we will ourselves publicly disclose any
issues reported within 3 months, and that we will not prosecute anyone
reporting security issues if they did not exploit the issue beyond a
proof-of-concept, and followed the above responsible disclosure
practice.
Limitation of liability & disclaimer of warranties
==================================================
You understand and agree that we have no control over, and no duty to
take any action regarding: Failures, disruptions, errors, or delays in
processing that you may experience while using our Services; The risk
of failure of hardware, software, and Internet connections; The risk
of malicious software being introduced or found in the software
underlying the Taler Wallet; The risk that third parties may obtain
unauthorized access to information stored within your Taler Wallet,
including, but not limited to your Taler Wallet coins or backup
encryption keys. You release us from all liability related to any
losses, damages, or claims arising from:
1. user error such as forgotten passwords, incorrectly constructed
transactions;
2. server failure or data loss;
3. unauthorized access to the Taler Wallet application;
4. bugs or other errors in the Taler Wallet software; and
5. any unauthorized third party activities, including, but not limited
to, the use of viruses, phishing, brute forcing, or other means of
attack against the Taler Wallet. We make no representations
concerning any Third Party Content contained in or accessed through
our Services.
Any other terms, conditions, warranties, or representations associated
with such content, are solely between you and such organizations
and/or individuals.
Limitation of liability
=======================
To the fullest extent permitted by applicable law, in no event will we
or any of our officers, directors, representatives, agents, servants,
counsel, employees, consultants, lawyers, and other personnel
authorized to act, acting, or purporting to act on our behalf
(collectively the Taler Parties) be liable to you under contract,
tort, strict liability, negligence, or any other legal or equitable
theory, for:
1. any lost profits, data loss, cost of procurement of substitute
goods or services, or direct, indirect, incidental, special,
punitive, compensatory, or consequential damages of any kind
whatsoever resulting from:
1. your use of, or conduct in connection with, our services;
2. any unauthorized use of your wallet and/or private key due to
your failure to maintain the confidentiality of your wallet;
3. any interruption or cessation of transmission to or from the
services; or
4. any bugs, viruses, trojan horses, or the like that are found in
the Taler Wallet software or that may be transmitted to or
through our services by any third party (regardless of the
source of origination), or
2. any direct damages.
These limitations apply regardless of legal theory, whether based on
tort, strict liability, breach of contract, breach of warranty, or any
other legal theory, and whether or not we were advised of the
possibility of such damages. Some jurisdictions do not allow the
exclusion or limitation of liability for consequential or incidental
damages, so the above limitation may not apply to you.
Warranty disclaimer
===================
Our services are provided "as is" and without warranty of any kind. To
the maximum extent permitted by law, we disclaim all representations
and warranties, express or implied, relating to the services and
underlying software or any content on the services, whether provided
or owned by us or by any third party, including without limitation,
warranties of merchantability, fitness for a particular purpose,
title, non-infringement, freedom from computer virus, and any implied
warranties arising from course of dealing, course of performance, or
usage in trade, all of which are expressly disclaimed. In addition, we
do not represent or warrant that the content accessible via the
services is accurate, complete, available, current, free of viruses or
other harmful components, or that the results of using the services
will meet your requirements. Some states do not allow the disclaimer
of implied warranties, so the foregoing disclaimers may not apply to
you. This paragraph gives you specific legal rights and you may also
have other legal rights that vary from state to state.
Indemnity
=========
To the extent permitted by applicable law, you agree to defend,
indemnify, and hold harmless the Taler Parties from and against any
and all claims, damages, obligations, losses, liabilities, costs or
debt, and expenses (including, but not limited to, attorneys fees)
arising from: (a) your use of and access to the Services; (b) any
feedback or submissions you provide to us concerning the Taler Wallet;
(c) your violation of any term of this Agreement; or (d) your
violation of any law, rule, or regulation, or the rights of any third
party.
Time limitation on claims
=========================
You agree that any claim you may have arising out of or related to
your relationship with us must be filed within one year after such
claim arises, otherwise, your claim in permanently barred.
Governing law
=============
No matter where youre located, the laws of Switzerland will govern
these Terms. If any provisions of these Terms are inconsistent with
any applicable law, those provisions will be superseded or modified
only to the extent such provisions are inconsistent. The parties agree
to submit to the ordinary courts in Zurich, Switzerland for exclusive
jurisdiction of any dispute arising out of or related to your use of
the Services or your breach of these Terms.
Termination
===========
In the event of termination concerning your use of our Services, your
obligations under this Agreement will still continue.
Discontinuance of services
==========================
We may, in our sole discretion and without cost to you, with or
without prior notice, and at any time, modify or discontinue,
temporarily or permanently, any portion of our Services. We will use
the Taler protocols provisions to notify Wallets if our Services are
to be discontinued. It is your responsibility to ensure that the Taler
Wallet is online at least once every three months to observe these
notifications. We shall not be held responsible or liable for any loss
of funds in the event that we discontinue or depreciate the Services
and your Taler Wallet fails to transfer out the coins within a three
months notification period.
No waiver
=========
Our failure to exercise or delay in exercising any right, power, or
privilege under this Agreement shall not operate as a waiver; nor
shall any single or partial exercise of any right, power, or privilege
preclude any other or further exercise thereof.
Severability
============
If it turns out that any part of this Agreement is invalid, void, or
for any reason unenforceable, that term will be deemed severable and
limited or eliminated to the minimum extent necessary.
Force majeure
=============
We shall not be held liable for any delays, failure in performance, or
interruptions of service which result directly or indirectly from any
cause or condition beyond our reasonable control, including but not
limited to: any delay or failure due to any act of God, act of civil
or military authorities, act of terrorism, civil disturbance, war,
strike or other labor dispute, fire, interruption in
telecommunications or Internet services or network provider services,
failure of equipment and/or software, other catastrophe, or any other
occurrence which is beyond our reasonable control and shall not affect
the validity and enforceability of any remaining provisions.
Assignment
==========
You agree that we may assign any of our rights and/or transfer, sub-
contract, or delegate any of our obligations under these Terms.
Entire agreement
================
This Agreement sets forth the entire understanding and agreement as to
the subject matter hereof and supersedes any and all prior
discussions, agreements, and understandings of any kind (including,
without limitation, any prior versions of this Agreement) and every
nature between us. Except as provided for above, any modification to
this Agreement must be in writing and must be signed by both parties.
Questions or comments
=====================
We welcome comments, questions, concerns, or suggestions. Please send
us a message on our contact page at legal@taler-systems.com.
`
const termsXml = `<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE document PUBLIC "+//IDN docutils.sourceforge.net//DTD Docutils Generic//EN//XML" "http://docutils.sourceforge.net/docs/ref/docutils.dtd">
<!-- Generated by Docutils 0.14 -->
@ -381,7 +780,7 @@ const termsXml = `<?xml version="1.0" encoding="utf-8"?>
</document>
`;
export const WithdrawNewTermsXML = createExample(TestedComponent, {
export const NewTerms = createExample(TestedComponent, {
knownExchanges: [{
currency: 'USD',
exchangeBaseUrl: 'exchange.demo.taler.net',
@ -417,7 +816,134 @@ export const WithdrawNewTermsXML = createExample(TestedComponent, {
},
})
export const WithdrawNewTermsReviewingXML = createExample(TestedComponent, {
export const TermsReviewingPLAIN = createExample(TestedComponent, {
knownExchanges: [{
currency: 'USD',
exchangeBaseUrl: 'exchange.demo.taler.net',
paytoUris: ['asd'],
},{
currency: 'USD',
exchangeBaseUrl: 'exchange.test.taler.net',
paytoUris: ['asd'],
}],
details: {
exchangeInfo: {
baseUrl: 'exchange.demo.taler.net'
} as ExchangeRecord,
withdrawFee: {
currency: 'USD',
fraction: 0,
value: 0
},
} as ExchangeWithdrawDetails,
amount: {
currency: 'USD',
value: 2,
fraction: 10000000
},
onSwitchExchange: async () => { },
terms: {
value: {
type: 'plain',
content: termsPlain
},
status: 'new'
},
reviewing: true
})
export const TermsReviewingHTML = createExample(TestedComponent, {
knownExchanges: [{
currency: 'USD',
exchangeBaseUrl: 'exchange.demo.taler.net',
paytoUris: ['asd'],
},{
currency: 'USD',
exchangeBaseUrl: 'exchange.test.taler.net',
paytoUris: ['asd'],
}],
details: {
exchangeInfo: {
baseUrl: 'exchange.demo.taler.net'
} as ExchangeRecord,
withdrawFee: {
currency: 'USD',
fraction: 0,
value: 0
},
} as ExchangeWithdrawDetails,
amount: {
currency: 'USD',
value: 2,
fraction: 10000000
},
onSwitchExchange: async () => { },
terms: {
value: {
type: 'html',
href: new URL(`data:text/html;base64,${Buffer.from(termsHtml).toString('base64')}`),
},
status: 'new'
},
reviewing: true
})
const termsPdf = `
%PDF-1.2
9 0 obj << >>
stream
BT/ 9 Tf(This is the Exchange TERMS OF SERVICE)' ET
endstream
endobj
4 0 obj << /Type /Page /Parent 5 0 R /Contents 9 0 R >> endobj
5 0 obj << /Kids [4 0 R ] /Count 1 /Type /Pages /MediaBox [ 0 0 180 20 ] >> endobj
3 0 obj << /Pages 5 0 R /Type /Catalog >> endobj
trailer
<< /Root 3 0 R >>
%%EOF
`
export const TermsReviewingPDF = createExample(TestedComponent, {
knownExchanges: [{
currency: 'USD',
exchangeBaseUrl: 'exchange.demo.taler.net',
paytoUris: ['asd'],
},{
currency: 'USD',
exchangeBaseUrl: 'exchange.test.taler.net',
paytoUris: ['asd'],
}],
details: {
exchangeInfo: {
baseUrl: 'exchange.demo.taler.net'
} as ExchangeRecord,
withdrawFee: {
currency: 'USD',
fraction: 0,
value: 0
},
} as ExchangeWithdrawDetails,
amount: {
currency: 'USD',
value: 2,
fraction: 10000000
},
onSwitchExchange: async () => { },
terms: {
value: {
type: 'pdf',
location: new URL(`data:text/html;base64,${Buffer.from(termsPdf).toString('base64')}`),
},
status: 'new'
},
reviewing: true
})
export const TermsReviewingXML = createExample(TestedComponent, {
knownExchanges: [{
currency: 'USD',
exchangeBaseUrl: 'exchange.demo.taler.net',
@ -454,7 +980,7 @@ export const WithdrawNewTermsReviewingXML = createExample(TestedComponent, {
reviewing: true
})
export const WithdrawNewTermsAcceptedXML = createExample(TestedComponent, {
export const NewTermsAccepted = createExample(TestedComponent, {
knownExchanges: [{
currency: 'USD',
exchangeBaseUrl: 'exchange.demo.taler.net',
@ -487,10 +1013,10 @@ export const WithdrawNewTermsAcceptedXML = createExample(TestedComponent, {
},
status: 'new'
},
accepted: true
reviewed: true
})
export const WithdrawNewTermsShowAfterAcceptedXML = createExample(TestedComponent, {
export const TermsShowAgainXML = createExample(TestedComponent, {
knownExchanges: [{
currency: 'USD',
exchangeBaseUrl: 'exchange.demo.taler.net',
@ -528,7 +1054,7 @@ export const WithdrawNewTermsShowAfterAcceptedXML = createExample(TestedComponen
reviewing: true,
})
export const WithdrawChangedTermsXML = createExample(TestedComponent, {
export const TermsChanged = createExample(TestedComponent, {
knownExchanges: [{
currency: 'USD',
exchangeBaseUrl: 'exchange.demo.taler.net',
@ -564,7 +1090,7 @@ export const WithdrawChangedTermsXML = createExample(TestedComponent, {
},
})
export const WithdrawNotFoundTermsXML = createExample(TestedComponent, {
export const TermsNotFound = createExample(TestedComponent, {
knownExchanges: [{
currency: 'USD',
exchangeBaseUrl: 'exchange.demo.taler.net',
@ -596,7 +1122,7 @@ export const WithdrawNotFoundTermsXML = createExample(TestedComponent, {
},
})
export const WithdrawAcceptedTermsXML = createExample(TestedComponent, {
export const TermsAlreadyAccepted = createExample(TestedComponent, {
knownExchanges: [{
currency: 'USD',
exchangeBaseUrl: 'exchange.demo.taler.net',
@ -629,7 +1155,7 @@ export const WithdrawAcceptedTermsXML = createExample(TestedComponent, {
})
export const WithdrawAcceptedTermsWithoutFee = createExample(TestedComponent, {
export const WithoutFee = createExample(TestedComponent, {
knownExchanges: [{
currency: 'USD',
exchangeBaseUrl: 'exchange.demo.taler.net',

View File

@ -49,7 +49,7 @@ export interface ViewProps {
onReview: (b: boolean) => void;
onAccept: (b: boolean) => void;
reviewing: boolean;
accepted: boolean;
reviewed: boolean;
confirmed: boolean;
terms: {
value?: TermsDocument;
@ -61,7 +61,7 @@ export interface ViewProps {
type TermsStatus = 'new' | 'accepted' | 'changed' | 'notfound';
type TermsDocument = TermsDocumentXml | TermsDocumentHtml;
type TermsDocument = TermsDocumentXml | TermsDocumentHtml | TermsDocumentPlain | TermsDocumentJson | TermsDocumentPdf;
interface TermsDocumentXml {
type: 'xml',
@ -70,7 +70,22 @@ interface TermsDocumentXml {
interface TermsDocumentHtml {
type: 'html',
href: string,
href: URL,
}
interface TermsDocumentPlain {
type: 'plain',
content: string,
}
interface TermsDocumentJson {
type: 'json',
data: any,
}
interface TermsDocumentPdf {
type: 'pdf',
location: URL,
}
function amountToString(text: AmountJson) {
@ -79,7 +94,7 @@ function amountToString(text: AmountJson) {
return `${amount} ${aj.currency}`
}
export function View({ details, knownExchanges, amount, onWithdraw, onSwitchExchange, terms, reviewing, onReview, onAccept, accepted, confirmed }: ViewProps) {
export function View({ details, knownExchanges, amount, onWithdraw, onSwitchExchange, terms, reviewing, onReview, onAccept, reviewed, confirmed }: ViewProps) {
const needsReview = terms.status === 'changed' || terms.status === 'new'
const [switchingExchange, setSwitchingExchange] = useState<string | undefined>(undefined)
@ -105,9 +120,6 @@ export function View({ details, knownExchanges, amount, onWithdraw, onSwitchExch
<div>
<SelectList label="Known exchanges" list={exchanges} name="" onChange={onSwitchExchange} />
</div>
<p>
This is the list of known exchanges
</p>
<LinkSuccess upperCased onClick={() => onSwitchExchange(switchingExchange)}>
{i18n.str`Confirm exchange selection`}
</LinkSuccess>
@ -118,7 +130,7 @@ export function View({ details, knownExchanges, amount, onWithdraw, onSwitchExch
</section>
}
{!reviewing && accepted &&
{!reviewing && reviewed &&
<section>
<LinkSuccess
upperCased
@ -130,11 +142,24 @@ export function View({ details, knownExchanges, amount, onWithdraw, onSwitchExch
}
{reviewing &&
<section>
<TermsOfService>
{terms.status !== 'accepted' && terms.value && terms.value.type === 'xml' && <ExchangeXmlTos doc={terms.value.document} />}
</TermsOfService>
{terms.status !== 'accepted' && terms.value && terms.value.type === 'xml' &&
<TermsOfService>
<ExchangeXmlTos doc={terms.value.document} />
</TermsOfService>
}
{terms.status !== 'accepted' && terms.value && terms.value.type === 'plain' &&
<div style={{ textAlign: 'left' }}>
<pre>{terms.value.content}</pre>
</div>
}
{terms.status !== 'accepted' && terms.value && terms.value.type === 'html' &&
<iframe src={terms.value.href.toString()} />
}
{terms.status !== 'accepted' && terms.value && terms.value.type === 'pdf' &&
<a href={terms.value.location.toString()} download="tos.pdf" >Download Terms of Service</a>
}
</section>}
{reviewing && accepted &&
{reviewing && reviewed &&
<section>
<LinkSuccess
upperCased
@ -144,14 +169,14 @@ export function View({ details, knownExchanges, amount, onWithdraw, onSwitchExch
</LinkSuccess>
</section>
}
{(reviewing || accepted) &&
{(reviewing || reviewed) &&
<section>
<CheckboxOutlined
name="terms"
enabled={accepted}
enabled={reviewed}
label={i18n.str`I accept the exchange terms of service`}
onToggle={() => {
onAccept(!accepted)
onAccept(!reviewed)
onReview(false)
}}
/>
@ -162,7 +187,7 @@ export function View({ details, knownExchanges, amount, onWithdraw, onSwitchExch
* Main action section
*/}
<section>
{terms.status === 'new' && !accepted && !reviewing &&
{terms.status === 'new' && !reviewed && !reviewing &&
<ButtonSuccess
upperCased
disabled={!details.exchangeInfo.baseUrl}
@ -171,7 +196,7 @@ export function View({ details, knownExchanges, amount, onWithdraw, onSwitchExch
{i18n.str`Review exchange terms of service`}
</ButtonSuccess>
}
{terms.status === 'changed' && !accepted &&
{terms.status === 'changed' && !reviewed && !reviewing &&
<ButtonWarning
upperCased
disabled={!details.exchangeInfo.baseUrl}
@ -180,7 +205,7 @@ export function View({ details, knownExchanges, amount, onWithdraw, onSwitchExch
{i18n.str`Review new version of terms of service`}
</ButtonWarning>
}
{(terms.status === 'accepted' || (needsReview && accepted)) &&
{(terms.status === 'accepted' || (needsReview && reviewed)) &&
<ButtonSuccess
upperCased
disabled={!details.exchangeInfo.baseUrl || confirmed}
@ -204,7 +229,7 @@ export function WithdrawPageWithParsedURI({ uri, uriInfo }: { uri: string, uriIn
const [errorAccepting, setErrorAccepting] = useState<string | undefined>(undefined)
const [reviewing, setReviewing] = useState<boolean>(false)
const [accepted, setAccepted] = useState<boolean>(false)
const [reviewed, setReviewed] = useState<boolean>(false)
const [confirmed, setConfirmed] = useState<boolean>(false)
const knownExchangesHook = useAsyncAsHook(() => listExchanges())
@ -219,7 +244,7 @@ export function WithdrawPageWithParsedURI({ uri, uriInfo }: { uri: string, uriIn
return getExchangeWithdrawalInfo({
exchangeBaseUrl: exchange,
amount: withdrawAmount,
tosAcceptedFormat: ['text/json', 'text/xml', 'text/pdf']
tosAcceptedFormat: ['text/xml']
})
})
@ -235,7 +260,7 @@ export function WithdrawPageWithParsedURI({ uri, uriInfo }: { uri: string, uriIn
const onAccept = async (): Promise<void> => {
try {
await setExchangeTosAccepted(details.exchangeInfo.baseUrl, details.tosRequested?.tosEtag)
setAccepted(true)
setReviewed(true)
} catch (e) {
if (e instanceof Error) {
setErrorAccepting(e.message)
@ -257,18 +282,7 @@ export function WithdrawPageWithParsedURI({ uri, uriInfo }: { uri: string, uriIn
}
};
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 termsContent: TermsDocument | undefined = !details.tosRequested ? undefined : parseTermsOfServiceContent(details.tosRequested.tosContentType, details.tosRequested.tosText);
const status: TermsStatus = !termsContent ? 'notfound' : (
!details.exchangeDetails.termsOfServiceAcceptedEtag ? 'new' : (
@ -285,7 +299,7 @@ export function WithdrawPageWithParsedURI({ uri, uriInfo }: { uri: string, uriIn
onSwitchExchange={setCustomExchange}
knownExchanges={knownExchanges}
confirmed={confirmed}
accepted={accepted} onAccept={onAccept}
reviewed={reviewed} onAccept={onAccept}
reviewing={reviewing} onReview={setReviewing}
// terms={[]}
/>
@ -307,3 +321,48 @@ export function WithdrawPage({ talerWithdrawUri }: Props): JSX.Element {
return <WithdrawPageWithParsedURI uri={talerWithdrawUri} uriInfo={uriInfoHook.response} />
}
function parseTermsOfServiceContent(type: string, text: string): TermsDocument | undefined {
if (type === 'text/xml') {
try {
const document = new DOMParser().parseFromString(text, "text/xml")
return { type: 'xml', document }
} catch (e) {
console.log(e)
debugger;
}
} else if (type === 'text/html') {
try {
const href = new URL(text)
return { type: 'html', href }
} catch (e) {
console.log(e)
debugger;
}
} else if (type === 'text/json') {
try {
const data = JSON.parse(text)
return { type: 'json', data }
} catch (e) {
console.log(e)
debugger;
}
} else if (type === 'text/pdf') {
try {
const location = new URL(text)
return { type: 'pdf', location }
} catch (e) {
console.log(e)
debugger;
}
} else if (type === 'text/plain') {
try {
const content = text
return { type: 'plain', content }
} catch (e) {
console.log(e)
debugger;
}
}
return undefined
}

View File

@ -25,10 +25,7 @@ export function useTalerActionURL(): [string | undefined, (s: boolean) => void]
useEffect(() => {
async function check(): Promise<void> {
const talerUri = await findTalerUriInActiveTab();
if (talerUri) {
const actionUrl = actionForTalerUri(talerUri);
setTalerActionUrl(actionUrl);
}
setTalerActionUrl(talerUri)
}
check();
}, []);
@ -36,49 +33,6 @@ export function useTalerActionURL(): [string | undefined, (s: boolean) => void]
return [url, setDismissed];
}
function actionForTalerUri(talerUri: string): string | undefined {
const uriType = classifyTalerUri(talerUri);
switch (uriType) {
case TalerUriType.TalerWithdraw:
return makeExtensionUrlWithParams("static/wallet.html#/withdraw", {
talerWithdrawUri: talerUri,
});
case TalerUriType.TalerPay:
return makeExtensionUrlWithParams("static/wallet.html#/pay", {
talerPayUri: talerUri,
});
case TalerUriType.TalerTip:
return makeExtensionUrlWithParams("static/wallet.html#/tip", {
talerTipUri: talerUri,
});
case TalerUriType.TalerRefund:
return makeExtensionUrlWithParams("static/wallet.html#/refund", {
talerRefundUri: talerUri,
});
case TalerUriType.TalerNotifyReserve:
// FIXME: implement
break;
default:
console.warn(
"Response with HTTP 402 has Taler header, but header value is not a taler:// URI.",
);
break;
}
return undefined;
}
function makeExtensionUrlWithParams(
url: string,
params?: { [name: string]: string | undefined },
): string {
const innerUrl = new URL(chrome.extension.getURL("/" + url));
if (params) {
const hParams = Object.keys(params).map(k => `${k}=${params[k]}`).join('&')
innerUrl.hash = innerUrl.hash + '?' + hParams
}
return innerUrl.href;
}
async function findTalerUriInActiveTab(): Promise<string | undefined> {
return new Promise((resolve, reject) => {
chrome.tabs.executeScript(

View File

@ -0,0 +1,52 @@
/*
This file is part of GNU Taler
(C) 2021 Taler Systems S.A.
GNU Taler is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3, or (at your option) any later version.
GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
/**
*
* @author Sebastian Javier Marchano (sebasjm)
*/
import { createExample } from '../test-utils';
import { TalerActionFound as TestedComponent } from './TalerActionFound';
export default {
title: 'popup/TalerActionFound',
component: TestedComponent,
};
export const PayAction = createExample(TestedComponent, {
url: 'taler://pay/something'
});
export const WithdrawalAction = createExample(TestedComponent, {
url: 'taler://withdraw/something'
});
export const TipAction = createExample(TestedComponent, {
url: 'taler://tip/something'
});
export const NotifyAction = createExample(TestedComponent, {
url: 'taler://notify-reserve/something'
});
export const RefundAction = createExample(TestedComponent, {
url: 'taler://refund/something'
});
export const InvalidAction = createExample(TestedComponent, {
url: 'taler://something/asd'
});

View File

@ -0,0 +1,98 @@
import { classifyTalerUri, TalerUriType } from "@gnu-taler/taler-util";
import { ButtonPrimary, ButtonSuccess, PopupBox } from "../components/styled/index";
export interface Props {
url: string;
onDismiss: (s: boolean) => void;
}
export function TalerActionFound({ url, onDismiss }: Props) {
const uriType = classifyTalerUri(url);
return <PopupBox>
<section>
<h1>Taler Action </h1>
{uriType === TalerUriType.TalerPay && <div>
<p>This page has pay action.</p>
<ButtonSuccess onClick={() => { chrome.tabs.create({ "url": actionForTalerUri(uriType, url) }); }}>
Open pay page
</ButtonSuccess>
</div>}
{uriType === TalerUriType.TalerWithdraw && <div>
<p>This page has a withdrawal action.</p>
<ButtonSuccess onClick={() => { chrome.tabs.create({ "url": actionForTalerUri(uriType, url) }); }}>
Open withdraw page
</ButtonSuccess>
</div>}
{uriType === TalerUriType.TalerTip && <div>
<p>This page has a tip action.</p>
<ButtonSuccess onClick={() => { chrome.tabs.create({ "url": actionForTalerUri(uriType, url) }); }}>
Open tip page
</ButtonSuccess>
</div>}
{uriType === TalerUriType.TalerNotifyReserve && <div>
<p>This page has a notify reserve action.</p>
<ButtonSuccess onClick={() => { chrome.tabs.create({ "url": actionForTalerUri(uriType, url) }); }}>
Notify
</ButtonSuccess>
</div>}
{uriType === TalerUriType.TalerRefund && <div>
<p>This page has a refund action.</p>
<ButtonSuccess onClick={() => { chrome.tabs.create({ "url": actionForTalerUri(uriType, url) }); }}>
Open refund page
</ButtonSuccess>
</div>}
{uriType === TalerUriType.Unknown && <div>
<p>This page has a malformed taler uri.</p>
<p>{url}</p>
</div>}
</section>
<footer>
<div />
<ButtonPrimary onClick={() => onDismiss(true)}> Dismiss </ButtonPrimary>
</footer>
</PopupBox>;
}
function actionForTalerUri(uriType: TalerUriType, talerUri: string): string | undefined {
switch (uriType) {
case TalerUriType.TalerWithdraw:
return makeExtensionUrlWithParams("static/wallet.html#/withdraw", {
talerWithdrawUri: talerUri,
});
case TalerUriType.TalerPay:
return makeExtensionUrlWithParams("static/wallet.html#/pay", {
talerPayUri: talerUri,
});
case TalerUriType.TalerTip:
return makeExtensionUrlWithParams("static/wallet.html#/tip", {
talerTipUri: talerUri,
});
case TalerUriType.TalerRefund:
return makeExtensionUrlWithParams("static/wallet.html#/refund", {
talerRefundUri: talerUri,
});
case TalerUriType.TalerNotifyReserve:
// FIXME: implement
break;
default:
console.warn(
"Response with HTTP 402 has Taler header, but header value is not a taler:// URI.",
);
break;
}
return undefined;
}
function makeExtensionUrlWithParams(
url: string,
params?: { [name: string]: string | undefined },
): string {
const innerUrl = new URL(chrome.extension.getURL("/" + url));
if (params) {
const hParams = Object.keys(params).map(k => `${k}=${params[k]}`).join('&')
innerUrl.hash = innerUrl.hash + '?' + hParams
}
return innerUrl.href;
}

View File

@ -38,6 +38,7 @@ import {
import { ProviderAddPage } from "./popup/ProviderAddPage";
import { ProviderDetailPage } from "./popup/ProviderDetailPage";
import { SettingsPage } from "./popup/Settings";
import { TalerActionFound } from "./popup/TalerActionFound";
function main(): void {
try {
@ -62,32 +63,16 @@ if (document.readyState === "loading") {
main();
}
interface Props {
url: string;
onDismiss: (s: boolean) => void;
}
function TalerActionFound({ url, onDismiss }: Props) {
return <div style={{ padding: "1em", width: 400 }}>
<h1>Taler Action </h1>
<p>This page has a Taler action.</p>
<p>
<button onClick={() => { chrome.tabs.create({ "url": url }); }}>
Open
</button>
</p>
<p>
<button onClick={() => onDismiss(true)}> Dismiss </button>
</p>
</div>
}
function Application() {
const [talerActionUrl, setDismissed] = useTalerActionURL()
if (talerActionUrl) {
return <TalerActionFound url={talerActionUrl} onDismiss={setDismissed} />
return <div>
<WalletNavBar />
<div style={{ width: 400, height: 290 }}>
<TalerActionFound url={talerActionUrl} onDismiss={setDismissed} />
</div>
</div>
}
return (

View File

@ -21,6 +21,8 @@ import { CreateManualWithdraw } from "./CreateManualWithdraw";
import * as wxApi from '../wxApi'
import { AcceptManualWithdrawalResult, AmountJson, Amounts } from "@gnu-taler/taler-util";
import { ReserveCreated } from "./ReserveCreated.js";
import { route } from 'preact-router';
import { Pages } from "../NavigationBar.js";
interface Props {
@ -58,7 +60,9 @@ export function ManualWithdrawPage({ }: Props): VNode {
}
if (success) {
return <ReserveCreated reservePub={success.reservePub} paytos={success.exchangePaytoUris} onBack={() => {}}/>
return <ReserveCreated reservePub={success.reservePub} paytos={success.exchangePaytoUris} onBack={() => {
route(Pages.balance)
}}/>
}
return <CreateManualWithdraw

View File

@ -41,3 +41,13 @@ export const OneChecked = createExample(TestedComponent, {
setDeviceName: () => Promise.resolve(),
});
export const WithOneExchange = createExample(TestedComponent, {
deviceName: 'this-is-the-device-name',
permissionsEnabled: true,
setDeviceName: () => Promise.resolve(),
knownExchanges: [{
currency: 'USD',
exchangeBaseUrl: 'http://exchange.taler',
paytoUris: ['payto://x-taler-bank/bank.rpi.sebasjm.com/exchangeminator']
}]
});

View File

@ -81,15 +81,14 @@ export function SettingsView({ knownExchanges, lang, changeLang, deviceName, set
{!knownExchanges || !knownExchanges.length ? <div>
No exchange yet!
</div> :
<dl>
{knownExchanges.map(e => <Fragment>
<dt>{e.currency}</dt>
<dd>{e.exchangeBaseUrl}</dd>
<dd>{e.paytoUris}</dd>
</Fragment>)}
</dl>
<table>
{knownExchanges.map(e => <tr>
<td>{e.currency}</td>
<td><a href={e.exchangeBaseUrl}>{e.exchangeBaseUrl}</a></td>
</tr>)}
</table>
}
<ButtonPrimary>add exchange</ButtonPrimary>
<h2><i18n.Translate>Permissions</i18n.Translate></h2>
<Checkbox label="Automatically open wallet based on page content"
name="perm"

View File

@ -2,8 +2,32 @@
<html>
<head>
<meta charset="utf-8" />
<link rel="stylesheet" type="text/css" href="/static/style/pure.css" />
<link rel="stylesheet" type="text/css" href="/static/style/popup.css" />
<style>
html {
font-family: sans-serif; /* 1 */
}
body {
margin: 0;
}
</style>
<style>
html {
}
h1 {
font-size: 2em;
}
input {
font: inherit;
}
body {
margin: 0;
font-size: 100%;
padding: 0;
overflow: hidden;
background-color: #f8faf7;
font-family: Arial, Helvetica, sans-serif;
}
</style>
<link rel="stylesheet" type="text/css" href="/dist/popupEntryPoint.css" />
<link rel="icon" href="/static/img/icon.png" />
<script src="/dist/popupEntryPoint.js"></script>