update SPA for #7810

This commit is contained in:
Sebastian 2023-05-02 12:56:09 -03:00
parent a957e61a9c
commit da519af01f
No known key found for this signature in database
GPG Key ID: 173909D1A5F66069
4 changed files with 228 additions and 60 deletions

View File

@ -48,6 +48,64 @@ type Amount = string;
type UUID = string;
type Integer = number;
interface WireAccount {
// payto:// URI identifying the account and wire method
payto_uri: string;
// URI to convert amounts from or to the currency used by
// this wire account of the exchange. Missing if no
// conversion is applicable.
conversion_url?: string;
// Restrictions that apply to bank accounts that would send
// funds to the exchange (crediting this exchange bank account).
// Optional, empty array for unrestricted.
credit_restrictions: AccountRestriction[];
// Restrictions that apply to bank accounts that would receive
// funds from the exchange (debiting this exchange bank account).
// Optional, empty array for unrestricted.
debit_restrictions: AccountRestriction[];
// Signature using the exchange's offline key over
// a TALER_MasterWireDetailsPS
// with purpose TALER_SIGNATURE_MASTER_WIRE_DETAILS.
master_sig: EddsaSignature;
}
type AccountRestriction = RegexAccountRestriction | DenyAllAccountRestriction;
// Account restriction that disables this type of
// account for the indicated operation categorically.
interface DenyAllAccountRestriction {
type: "deny";
}
// Accounts interacting with this type of account
// restriction must have a payto://-URI matching
// the given regex.
interface RegexAccountRestriction {
type: "regex";
// Regular expression that the payto://-URI of the
// partner account must follow. The regular expression
// should follow posix-egrep, but without support for character
// classes, GNU extensions, back-references or intervals. See
// https://www.gnu.org/software/findutils/manual/html_node/find_html/posix_002degrep-regular-expression-syntax.html
// for a description of the posix-egrep syntax. Applications
// may support regexes with additional features, but exchanges
// must not use such regexes.
payto_regex: string;
// Hint for a human to understand the restriction
// (that is hopefully easier to comprehend than the regex itself).
human_hint: string;
// Map from IETF BCP 47 language tags to localized
// human hints.
human_hint_i18n?: { [lang_tag: string]: string };
}
export namespace ExchangeBackend {
interface WireResponse {
// Master public key of the exchange, must match the key returned in /keys.
@ -61,14 +119,6 @@ export namespace ExchangeBackend {
// to wire fees.
fees: { method: AggregateTransferFee };
}
interface WireAccount {
// payto:// URI identifying the account and wire method
payto_uri: string;
// Signature using the exchange's offline key
// with purpose TALER_SIGNATURE_MASTER_WIRE_DETAILS.
master_sig: EddsaSignature;
}
interface AggregateTransferFee {
// Per transfer wire transfer fee.
wire_fee: Amount;
@ -1028,8 +1078,8 @@ export namespace MerchantBackend {
// Public key identifying the reserve
reserve_pub: EddsaPublicKey;
// Wire account of the exchange where to transfer the funds
payto_uri: string;
// Wire accounts of the exchange where to transfer the funds.
accounts: WireAccount[];
}
interface TipCreateRequest {
// Amount that the customer should be tipped
@ -1084,9 +1134,10 @@ export namespace MerchantBackend {
// Is this reserve active (false if it was deleted but not purged)?
active: boolean;
// URI to use to fill the reserve, can be NULL
// Array of wire accounts of the exchange that could
// be used to fill the reserve, can be NULL
// if the reserve is inactive or was already filled
payto_uri: string;
accounts?: WireAccount[];
// URL of the exchange hosting the reserve,
// NULL if the reserve is inactive

View File

@ -16,3 +16,4 @@
export * as details from "./details/stories.js";
export * as kycList from "./kyc/list/ListPage.stories.js";
export * as reserve from "./reserves/create/CreatedSuccessfully.stories.js";

View File

@ -21,6 +21,7 @@
import { h, VNode, FunctionalComponent } from "preact";
import { CreatedSuccessfully as TestedComponent } from "./CreatedSuccessfully.js";
import { tests } from "@gnu-taler/web-util/lib/index.browser";
export default {
title: "Pages/Reserve/CreatedSuccessfully",
@ -31,16 +32,7 @@ export default {
},
};
function createExample<Props>(
Component: FunctionalComponent<Props>,
props: Partial<Props>,
) {
const r = (args: any) => <Component {...args} />;
r.args = props;
return r;
}
export const Example = createExample(TestedComponent, {
export const OneBankAccount = tests.createExample(TestedComponent, {
entity: {
request: {
exchange_url: "http://exchange.taler/",
@ -48,7 +40,80 @@ export const Example = createExample(TestedComponent, {
wire_method: "x-taler-bank",
},
response: {
payto_uri: "payto://x-taler-bank/bank.taler:8080/exchange_account",
accounts: [
{
payto_uri: "payto://x-taler-bank/bank.taler:8080/exchange_account",
credit_restrictions: [],
debit_restrictions: [],
master_sig: "asd",
conversion_url: "",
},
],
reserve_pub: "WEQWDASDQWEASDADASDQWEQWEASDAS",
},
},
});
export const ThreeBankAccount = tests.createExample(TestedComponent, {
entity: {
request: {
exchange_url: "http://exchange.taler/",
initial_balance: "TESTKUDOS:1",
wire_method: "x-taler-bank",
},
response: {
accounts: [
{
payto_uri: "payto://x-taler-bank/bank.taler:8080/exchange_account",
credit_restrictions: [],
debit_restrictions: [],
master_sig: "asd",
conversion_url: "",
},
{
payto_uri: "payto://x-taler-bank/bank1.taler:8080/asd",
credit_restrictions: [],
debit_restrictions: [],
master_sig: "asd",
conversion_url: "",
},
{
payto_uri: "payto://x-taler-bank/bank2.taler:8080/qwe",
credit_restrictions: [],
debit_restrictions: [],
master_sig: "asd",
conversion_url: "",
},
],
reserve_pub: "WEQWDASDQWEASDADASDQWEQWEASDAS",
},
},
});
export const NoBankAccount = tests.createExample(TestedComponent, {
entity: {
request: {
exchange_url: "http://exchange.taler/",
initial_balance: "TESTKUDOS:1",
wire_method: "x-taler-bank",
},
response: {
accounts: [
{
payto_uri: "payo://x-talr-bank/bank.taler:8080/exchange_account",
credit_restrictions: [],
debit_restrictions: [],
master_sig: "asd",
conversion_url: "",
},
{
payto_uri: "payto://x-taler-bank",
credit_restrictions: [],
debit_restrictions: [],
master_sig: "asd",
conversion_url: "",
},
],
reserve_pub: "WEQWDASDQWEASDADASDQWEQWEASDAS",
},
},

View File

@ -16,7 +16,7 @@
import { parsePaytoUri, stringifyPaytoUri } from "@gnu-taler/taler-util";
import { useTranslationContext } from "@gnu-taler/web-util/lib/index.browser";
import { h, VNode } from "preact";
import { Fragment, h, VNode } from "preact";
import { QR } from "../../../../components/exception/QR.js";
import { CreatedSuccessfully as Template } from "../../../../components/notifications/CreatedSuccessfully.js";
import { MerchantBackend } from "../../../../declaration.js";
@ -32,18 +32,29 @@ interface Props {
onCreateAnother?: () => void;
}
function isNotUndefined<X>(x: X | undefined): x is X {
return !!x;
}
export function CreatedSuccessfully({
entity,
onConfirm,
onCreateAnother,
}: Props): VNode {
const p = parsePaytoUri(entity.response.payto_uri);
if (p) {
p.params["message"] = entity.response.reserve_pub;
p.params["amount"] = entity.request.initial_balance;
}
const accountsInfo = !entity.response.accounts
? []
: entity.response.accounts
.map((acc) => {
const p = parsePaytoUri(acc.payto_uri);
if (p) {
p.params["message"] = entity.response.reserve_pub;
p.params["amount"] = entity.request.initial_balance;
}
return p;
})
.filter(isNotUndefined);
const link = !p ? entity.response.payto_uri : stringifyPaytoUri(p);
const links = accountsInfo.map((a) => stringifyPaytoUri(a));
const { i18n } = useTranslationContext();
return (
<Template onConfirm={onConfirm} onCreateAnother={onCreateAnother}>
@ -63,18 +74,6 @@ export function CreatedSuccessfully({
</div>
</div>
</div>
<div class="field is-horizontal">
<div class="field-label is-normal">
<label class="label">Exchange bank account</label>
</div>
<div class="field-body is-flex-grow-3">
<div class="field">
<p class="control">
<input readonly class="input" value={entity.response.payto_uri} />
</p>
</div>
</div>
</div>
<div class="field is-horizontal">
<div class="field-label is-normal">
<label class="label">Wire transfer subject</label>
@ -91,24 +90,76 @@ export function CreatedSuccessfully({
</div>
</div>
</div>
<p class="is-size-5">
<i18n.Translate>
To complete the setup of the reserve, you must now initiate a wire
transfer using the given wire transfer subject and crediting the
specified amount to the indicated account of the exchange.
</i18n.Translate>
</p>
<p class="is-size-5">
<i18n.Translate>
If your system supports RFC 8905, you can do this by opening this URI:
</i18n.Translate>
</p>
<pre>
<a target="_blank" rel="noreferrer" href={link}>
{link}
</a>
</pre>
<QR text={link} />
{links.length === 0 ? (
<Fragment>
<p class="is-size-5">
The response of the reserve creation have invalid accounts. List of
invalid payto URIs below:
</p>
<ul>
{entity.response.accounts.map((a, idx) => {
return <li key={idx}>{a.payto_uri}</li>;
})}
</ul>
</Fragment>
) : links.length === 1 ? (
<Fragment>
<p class="is-size-5">
<i18n.Translate>
To complete the setup of the reserve, you must now initiate a wire
transfer using the given wire transfer subject and crediting the
specified amount to the indicated account of the exchange.
</i18n.Translate>
</p>
<p style={{ margin: 10 }}>
<b>Exchange bank account</b>
</p>
<QR text={links[0]} />
<p class="is-size-5">
<i18n.Translate>
If your system supports RFC 8905, you can do this by opening this
URI:
</i18n.Translate>
</p>
<pre>
<a target="_blank" rel="noreferrer" href={links[0]}>
{links[0]}
</a>
</pre>
</Fragment>
) : (
<div>
<p class="is-size-5">
<i18n.Translate>
To complete the setup of the reserve, you must now initiate a wire
transfer using the given wire transfer subject and crediting the
specified amount to one of the indicated account of the exchange.
</i18n.Translate>
</p>
<p style={{ margin: 10 }}>
<b>Exchange bank accounts</b>
</p>
<p class="is-size-5">
<i18n.Translate>
If your system supports RFC 8905, you can do this by clicking on
the URI below the QR code:
</i18n.Translate>
</p>
{links.map((link) => {
return (
<Fragment>
<QR text={link} />
<pre>
<a target="_blank" rel="noreferrer" href={link}>
{link}
</a>
</pre>
</Fragment>
);
})}
</div>
)}
</Template>
);
}