redirect after success #7357
This commit is contained in:
parent
6ddb2de842
commit
59d235e8d2
@ -21,19 +21,19 @@ import { ButtonHandler } from "../../mui/handlers.js";
|
||||
import { compose, StateViewMap } from "../../utils/index.js";
|
||||
import * as wxApi from "../../wxApi.js";
|
||||
import { useComponentState } from "./state.js";
|
||||
import { CompletedView, LoadingUriView, ReadyView } from "./views.js";
|
||||
import { LoadingUriView, ReadyView } from "./views.js";
|
||||
|
||||
export interface Props {
|
||||
talerDepositUri: string | undefined;
|
||||
amountStr: AmountString | undefined;
|
||||
cancel: () => Promise<void>;
|
||||
onSuccess: (tx: string) => Promise<void>;
|
||||
}
|
||||
|
||||
export type State =
|
||||
| State.Loading
|
||||
| State.LoadingUriError
|
||||
| State.Ready
|
||||
| State.Completed;
|
||||
| State.Ready;
|
||||
|
||||
export namespace State {
|
||||
export interface Loading {
|
||||
@ -62,7 +62,6 @@ export namespace State {
|
||||
const viewMapping: StateViewMap<State> = {
|
||||
loading: Loading,
|
||||
"loading-uri": LoadingUriView,
|
||||
completed: CompletedView,
|
||||
ready: ReadyView,
|
||||
};
|
||||
|
||||
|
@ -21,12 +21,9 @@ import * as wxApi from "../../wxApi.js";
|
||||
import { Props, State } from "./index.js";
|
||||
|
||||
export function useComponentState(
|
||||
{ talerDepositUri, amountStr, cancel }: Props,
|
||||
{ talerDepositUri, amountStr, cancel, onSuccess }: Props,
|
||||
api: typeof wxApi,
|
||||
): State {
|
||||
const [result, setResult] = useState<CreateDepositGroupResponse | undefined>(
|
||||
undefined,
|
||||
);
|
||||
|
||||
const info = useAsyncAsHook(async () => {
|
||||
if (!talerDepositUri) throw Error("ERROR_NO-URI-FOR-DEPOSIT");
|
||||
@ -51,14 +48,7 @@ export function useComponentState(
|
||||
const { deposit, uri, amount } = info.response;
|
||||
async function doDeposit(): Promise<void> {
|
||||
const resp = await api.createDepositGroup(uri, Amounts.stringify(amount));
|
||||
setResult(resp);
|
||||
}
|
||||
|
||||
if (result !== undefined) {
|
||||
return {
|
||||
status: "completed",
|
||||
error: undefined,
|
||||
};
|
||||
onSuccess(resp.transactionId);
|
||||
}
|
||||
|
||||
return {
|
||||
|
@ -35,6 +35,7 @@ describe("Deposit CTA states", () => {
|
||||
cancel: async () => {
|
||||
null;
|
||||
},
|
||||
onSuccess: async () => { null; },
|
||||
},
|
||||
{
|
||||
prepareRefund: async () => ({}),
|
||||
@ -75,13 +76,14 @@ describe("Deposit CTA states", () => {
|
||||
cancel: async () => {
|
||||
null;
|
||||
},
|
||||
onSuccess: async () => { null; },
|
||||
},
|
||||
{
|
||||
prepareDeposit: async () =>
|
||||
({
|
||||
effectiveDepositAmount: Amounts.parseOrThrow("EUR:1"),
|
||||
totalDepositCost: Amounts.parseOrThrow("EUR:1.2"),
|
||||
} as PrepareDepositResponse as any),
|
||||
({
|
||||
effectiveDepositAmount: Amounts.parseOrThrow("EUR:1"),
|
||||
totalDepositCost: Amounts.parseOrThrow("EUR:1.2"),
|
||||
} as PrepareDepositResponse as any),
|
||||
createDepositGroup: async () => ({}),
|
||||
} as any,
|
||||
),
|
||||
|
@ -40,24 +40,6 @@ export function LoadingUriView({ error }: State.LoadingUriError): VNode {
|
||||
/>
|
||||
);
|
||||
}
|
||||
export function CompletedView(state: State.Completed): VNode {
|
||||
const { i18n } = useTranslationContext();
|
||||
|
||||
return (
|
||||
<WalletAction>
|
||||
<LogoHeader />
|
||||
|
||||
<SubTitle>
|
||||
<i18n.Translate>Digital cash deposit</i18n.Translate>
|
||||
</SubTitle>
|
||||
<section>
|
||||
<p>
|
||||
<i18n.Translate>deposit completed</i18n.Translate>
|
||||
</p>
|
||||
</section>
|
||||
</WalletAction>
|
||||
);
|
||||
}
|
||||
|
||||
export function ReadyView(state: State.Ready): VNode {
|
||||
const { i18n } = useTranslationContext();
|
||||
|
@ -17,7 +17,7 @@
|
||||
import { Loading } from "../../components/Loading.js";
|
||||
import { HookError } from "../../hooks/useAsyncAsHook.js";
|
||||
import { compose, StateViewMap } from "../../utils/index.js";
|
||||
import { LoadingUriView, ReadyView, CreatedView } from "./views.js";
|
||||
import { LoadingUriView, ReadyView } from "./views.js";
|
||||
import * as wxApi from "../../wxApi.js";
|
||||
import { useComponentState } from "./state.js";
|
||||
import { AmountJson, TalerErrorDetail } from "@gnu-taler/taler-util";
|
||||
@ -26,12 +26,12 @@ import { ButtonHandler, TextFieldHandler } from "../../mui/handlers.js";
|
||||
export interface Props {
|
||||
amount: string;
|
||||
onClose: () => Promise<void>;
|
||||
onSuccess: (tx: string) => Promise<void>;
|
||||
}
|
||||
|
||||
export type State =
|
||||
| State.Loading
|
||||
| State.LoadingUriError
|
||||
| State.Created
|
||||
| State.Ready;
|
||||
|
||||
export namespace State {
|
||||
@ -49,11 +49,6 @@ export namespace State {
|
||||
error: undefined;
|
||||
cancel: ButtonHandler;
|
||||
}
|
||||
export interface Created extends BaseInfo {
|
||||
status: "created";
|
||||
talerUri: string;
|
||||
copyToClipboard: ButtonHandler;
|
||||
}
|
||||
export interface Ready extends BaseInfo {
|
||||
status: "ready";
|
||||
create: ButtonHandler;
|
||||
@ -70,7 +65,6 @@ export namespace State {
|
||||
const viewMapping: StateViewMap<State> = {
|
||||
loading: Loading,
|
||||
"loading-uri": LoadingUriView,
|
||||
created: CreatedView,
|
||||
ready: ReadyView,
|
||||
};
|
||||
|
||||
|
@ -22,13 +22,12 @@ import * as wxApi from "../../wxApi.js";
|
||||
import { Props, State } from "./index.js";
|
||||
|
||||
export function useComponentState(
|
||||
{ amount: amountStr, onClose }: Props,
|
||||
{ amount: amountStr, onClose, onSuccess }: Props,
|
||||
api: typeof wxApi,
|
||||
): State {
|
||||
const amount = Amounts.parseOrThrow(amountStr);
|
||||
|
||||
const [subject, setSubject] = useState("");
|
||||
const [talerUri, setTalerUri] = useState("");
|
||||
|
||||
const hook = useAsyncAsHook(api.listExchanges);
|
||||
const [exchangeIdx, setExchangeIdx] = useState("0");
|
||||
@ -49,22 +48,6 @@ export function useComponentState(
|
||||
};
|
||||
}
|
||||
|
||||
if (talerUri) {
|
||||
return {
|
||||
status: "created",
|
||||
talerUri,
|
||||
error: undefined,
|
||||
cancel: {
|
||||
onClick: onClose,
|
||||
},
|
||||
copyToClipboard: {
|
||||
onClick: async () => {
|
||||
navigator.clipboard.writeText(talerUri);
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const exchanges = hook.response.exchanges.filter(
|
||||
(e) => e.currency === amount.currency,
|
||||
);
|
||||
@ -74,7 +57,7 @@ export function useComponentState(
|
||||
);
|
||||
const selected = exchanges[Number(exchangeIdx)];
|
||||
|
||||
async function accept(): Promise<string> {
|
||||
async function accept(): Promise<void> {
|
||||
try {
|
||||
const resp = await api.initiatePeerPullPayment({
|
||||
amount: Amounts.stringify(amount),
|
||||
@ -83,7 +66,8 @@ export function useComponentState(
|
||||
summary: subject,
|
||||
},
|
||||
});
|
||||
return resp.talerUri;
|
||||
|
||||
onSuccess(resp.transactionId);
|
||||
} catch (e) {
|
||||
if (e instanceof TalerError) {
|
||||
setOperationError(e.errorDetail);
|
||||
@ -103,10 +87,7 @@ export function useComponentState(
|
||||
invalid: !subject || Amounts.isZero(amount),
|
||||
exchangeUrl: selected.exchangeBaseUrl,
|
||||
create: {
|
||||
onClick: async () => {
|
||||
const uri = await accept();
|
||||
setTalerUri(uri);
|
||||
},
|
||||
onClick: accept
|
||||
},
|
||||
cancel: {
|
||||
onClick: onClose,
|
||||
|
@ -20,19 +20,12 @@
|
||||
*/
|
||||
|
||||
import { createExample } from "../../test-utils.js";
|
||||
import { ReadyView, CreatedView } from "./views.js";
|
||||
import { ReadyView } from "./views.js";
|
||||
|
||||
export default {
|
||||
title: "wallet/invoice create",
|
||||
};
|
||||
|
||||
export const ShowQr = createExample(CreatedView, {
|
||||
talerUri:
|
||||
"taler://pay-pull/exchange.taler.ar/HS585JK0QCXHJ8Z8QWZA3EBAY5WY7XNC1RR2MHJXSH2Z4WP0YPJ0",
|
||||
cancel: {},
|
||||
copyToClipboard: {},
|
||||
});
|
||||
|
||||
export const Ready = createExample(ReadyView, {
|
||||
chosenAmount: {
|
||||
currency: "ARS",
|
||||
|
@ -45,38 +45,6 @@ export function LoadingUriView({ error }: State.LoadingUriError): VNode {
|
||||
);
|
||||
}
|
||||
|
||||
export function CreatedView({
|
||||
talerUri,
|
||||
copyToClipboard,
|
||||
cancel,
|
||||
}: State.Created): VNode {
|
||||
const { i18n } = useTranslationContext();
|
||||
return (
|
||||
<WalletAction>
|
||||
<LogoHeader />
|
||||
<SubTitle>
|
||||
<i18n.Translate>Digital cash invoice</i18n.Translate>
|
||||
</SubTitle>
|
||||
<section>
|
||||
<p>
|
||||
<i18n.Translate>Show this QR to pay the invoice</i18n.Translate>
|
||||
</p>
|
||||
<QR text={talerUri} />
|
||||
</section>
|
||||
<section>
|
||||
or
|
||||
<Button onClick={copyToClipboard.onClick}>
|
||||
<i18n.Translate>Copy the invoice URI</i18n.Translate>
|
||||
</Button>
|
||||
</section>
|
||||
<section>
|
||||
<Link upperCased onClick={cancel.onClick}>
|
||||
<i18n.Translate>Close</i18n.Translate>
|
||||
</Link>
|
||||
</section>
|
||||
</WalletAction>
|
||||
);
|
||||
}
|
||||
|
||||
export function ReadyView({
|
||||
invalid,
|
||||
|
@ -32,6 +32,7 @@ export interface Props {
|
||||
talerPayPullUri: string;
|
||||
onClose: () => Promise<void>;
|
||||
goToWalletManualWithdraw: (amount?: string) => Promise<void>;
|
||||
onSuccess: (tx: string) => Promise<void>;
|
||||
}
|
||||
|
||||
export type State =
|
||||
|
@ -30,7 +30,7 @@ import * as wxApi from "../../wxApi.js";
|
||||
import { Props, State } from "./index.js";
|
||||
|
||||
export function useComponentState(
|
||||
{ talerPayPullUri, onClose, goToWalletManualWithdraw }: Props,
|
||||
{ talerPayPullUri, onClose, goToWalletManualWithdraw, onSuccess }: Props,
|
||||
api: typeof wxApi,
|
||||
): State {
|
||||
const hook = useAsyncAsHook(async () => {
|
||||
@ -149,7 +149,7 @@ export function useComponentState(
|
||||
const resp = await api.acceptPeerPullPayment({
|
||||
peerPullPaymentIncomingId,
|
||||
});
|
||||
await onClose();
|
||||
onSuccess(resp.transactionId);
|
||||
} catch (e) {
|
||||
if (e instanceof TalerError) {
|
||||
setOperationError(e.errorDetail);
|
||||
|
@ -35,6 +35,7 @@ export interface Props {
|
||||
talerPayUri?: string;
|
||||
goToWalletManualWithdraw: (amount?: string) => Promise<void>;
|
||||
cancel: () => Promise<void>;
|
||||
onSuccess: (tx: string) => Promise<void>;
|
||||
}
|
||||
|
||||
export type State =
|
||||
@ -43,7 +44,6 @@ export type State =
|
||||
| State.Ready
|
||||
| State.NoEnoughBalance
|
||||
| State.NoBalanceForCurrency
|
||||
| State.Completed
|
||||
| State.Confirmed;
|
||||
|
||||
export namespace State {
|
||||
@ -86,13 +86,6 @@ export namespace State {
|
||||
balance: AmountJson;
|
||||
}
|
||||
|
||||
export interface Completed extends BaseInfo {
|
||||
status: "completed";
|
||||
payStatus: PreparePayResult;
|
||||
payResult: ConfirmPayResult;
|
||||
paymentError?: TalerError;
|
||||
balance: AmountJson;
|
||||
}
|
||||
}
|
||||
|
||||
const viewMapping: StateViewMap<State> = {
|
||||
@ -101,7 +94,6 @@ const viewMapping: StateViewMap<State> = {
|
||||
"no-balance-for-currency": BaseView,
|
||||
"no-enough-balance": BaseView,
|
||||
confirmed: BaseView,
|
||||
completed: BaseView,
|
||||
ready: BaseView,
|
||||
};
|
||||
|
||||
|
@ -31,12 +31,9 @@ import * as wxApi from "../../wxApi.js";
|
||||
import { Props, State } from "./index.js";
|
||||
|
||||
export function useComponentState(
|
||||
{ talerPayUri, cancel, goToWalletManualWithdraw }: Props,
|
||||
{ talerPayUri, cancel, goToWalletManualWithdraw, onSuccess }: Props,
|
||||
api: typeof wxApi,
|
||||
): State {
|
||||
const [payResult, setPayResult] = useState<ConfirmPayResult | undefined>(
|
||||
undefined,
|
||||
);
|
||||
const [payErrMsg, setPayErrMsg] = useState<TalerError | undefined>(undefined);
|
||||
|
||||
const hook = useAsyncAsHook(async () => {
|
||||
@ -104,17 +101,6 @@ export function useComponentState(
|
||||
|
||||
const foundAmount = Amounts.parseOrThrow(foundBalance.available);
|
||||
|
||||
if (payResult) {
|
||||
return {
|
||||
status: "completed",
|
||||
balance: foundAmount,
|
||||
payStatus,
|
||||
paymentError: payErrMsg,
|
||||
payResult,
|
||||
...baseResult,
|
||||
};
|
||||
}
|
||||
|
||||
if (payStatus.status === PreparePayResultType.InsufficientBalance) {
|
||||
return {
|
||||
status: "no-enough-balance",
|
||||
@ -157,7 +143,7 @@ export function useComponentState(
|
||||
console.log(`should d to ${fu}`);
|
||||
}
|
||||
}
|
||||
setPayResult(res);
|
||||
onSuccess(res.transactionId);
|
||||
} catch (e) {
|
||||
if (e instanceof TalerError) {
|
||||
setPayErrMsg(e);
|
||||
|
@ -334,80 +334,4 @@ export const AlreadyConfirmedByOther = createExample(BaseView, {
|
||||
},
|
||||
});
|
||||
|
||||
export const AlreadyPaidWithoutFulfillment = createExample(BaseView, {
|
||||
status: "completed",
|
||||
error: undefined,
|
||||
amount: Amounts.parseOrThrow("USD:10"),
|
||||
balance: {
|
||||
currency: "USD",
|
||||
fraction: 40000000,
|
||||
value: 11,
|
||||
},
|
||||
|
||||
uri: "taler://pay/merchant-backend.taler/2021.242-01G2X4275RBWG/?c=66BE594PDZR24744J6EQK52XM0",
|
||||
payResult: {
|
||||
type: ConfirmPayResultType.Done,
|
||||
contractTerms: {} as any,
|
||||
transactionId: "",
|
||||
},
|
||||
payStatus: {
|
||||
status: PreparePayResultType.AlreadyConfirmed,
|
||||
amountEffective: "USD:10",
|
||||
amountRaw: "USD:10",
|
||||
contractTerms: {
|
||||
merchant: {
|
||||
name: "the merchant",
|
||||
logo: merchantIcon,
|
||||
website: "https://www.themerchant.taler",
|
||||
email: "contact@merchant.taler",
|
||||
},
|
||||
summary: "some beers",
|
||||
amount: "USD:10",
|
||||
} as Partial<ContractTerms> as any,
|
||||
contractTermsHash: "123456",
|
||||
proposalId: "96YY92RQZGF3V7TJSPN4SF9549QX7BRF88Q5PYFCSBNQ0YK4RPK0",
|
||||
paid: true,
|
||||
},
|
||||
});
|
||||
|
||||
export const AlreadyPaidWithFulfillment = createExample(BaseView, {
|
||||
status: "completed",
|
||||
error: undefined,
|
||||
amount: Amounts.parseOrThrow("USD:10"),
|
||||
balance: {
|
||||
currency: "USD",
|
||||
fraction: 40000000,
|
||||
value: 11,
|
||||
},
|
||||
|
||||
uri: "taler://pay/merchant-backend.taler/2021.242-01G2X4275RBWG/?c=66BE594PDZR24744J6EQK52XM0",
|
||||
payResult: {
|
||||
type: ConfirmPayResultType.Done,
|
||||
contractTerms: {
|
||||
fulfillment_message: "thanks for buying!",
|
||||
fulfillment_url: "https://demo.taler.net",
|
||||
} as Partial<ContractTerms> as any,
|
||||
transactionId: "",
|
||||
},
|
||||
payStatus: {
|
||||
status: PreparePayResultType.AlreadyConfirmed,
|
||||
amountEffective: "USD:10",
|
||||
amountRaw: "USD:10",
|
||||
contractTerms: {
|
||||
merchant: {
|
||||
name: "the merchant",
|
||||
logo: merchantIcon,
|
||||
website: "https://www.themerchant.taler",
|
||||
email: "contact@merchant.taler",
|
||||
},
|
||||
fulfillment_url: "https://demo.taler.net",
|
||||
fulfillment_message:
|
||||
"congratulations! you are looking at the fulfillment message! ",
|
||||
summary: "some beers",
|
||||
amount: "USD:10",
|
||||
} as Partial<ContractTerms> as any,
|
||||
contractTermsHash: "123456",
|
||||
proposalId: "96YY92RQZGF3V7TJSPN4SF9549QX7BRF88Q5PYFCSBNQ0YK4RPK0",
|
||||
paid: true,
|
||||
},
|
||||
});
|
||||
|
@ -75,6 +75,7 @@ describe("Payment CTA states", () => {
|
||||
talerPayUri: undefined,
|
||||
cancel: nullFunction,
|
||||
goToWalletManualWithdraw: nullFunction,
|
||||
onSuccess: async () => { null; },
|
||||
},
|
||||
{
|
||||
onUpdateNotification: nullFunction,
|
||||
@ -110,18 +111,19 @@ describe("Payment CTA states", () => {
|
||||
talerPayUri: "taller://pay",
|
||||
cancel: nullFunction,
|
||||
goToWalletManualWithdraw: nullFunction,
|
||||
onSuccess: async () => { null; },
|
||||
},
|
||||
{
|
||||
onUpdateNotification: nullFunction,
|
||||
preparePay: async () =>
|
||||
({
|
||||
amountRaw: "USD:10",
|
||||
status: PreparePayResultType.InsufficientBalance,
|
||||
} as Partial<PreparePayResult>),
|
||||
({
|
||||
amountRaw: "USD:10",
|
||||
status: PreparePayResultType.InsufficientBalance,
|
||||
} as Partial<PreparePayResult>),
|
||||
getBalance: async () =>
|
||||
({
|
||||
balances: [],
|
||||
} as Partial<BalancesResponse>),
|
||||
({
|
||||
balances: [],
|
||||
} as Partial<BalancesResponse>),
|
||||
} as Partial<typeof wxApi> as any,
|
||||
),
|
||||
);
|
||||
@ -152,22 +154,23 @@ describe("Payment CTA states", () => {
|
||||
talerPayUri: "taller://pay",
|
||||
cancel: nullFunction,
|
||||
goToWalletManualWithdraw: nullFunction,
|
||||
onSuccess: async () => { null; },
|
||||
},
|
||||
{
|
||||
onUpdateNotification: nullFunction,
|
||||
preparePay: async () =>
|
||||
({
|
||||
amountRaw: "USD:10",
|
||||
status: PreparePayResultType.InsufficientBalance,
|
||||
} as Partial<PreparePayResult>),
|
||||
({
|
||||
amountRaw: "USD:10",
|
||||
status: PreparePayResultType.InsufficientBalance,
|
||||
} as Partial<PreparePayResult>),
|
||||
getBalance: async () =>
|
||||
({
|
||||
balances: [
|
||||
{
|
||||
available: "USD:5",
|
||||
},
|
||||
],
|
||||
} as Partial<BalancesResponse>),
|
||||
({
|
||||
balances: [
|
||||
{
|
||||
available: "USD:5",
|
||||
},
|
||||
],
|
||||
} as Partial<BalancesResponse>),
|
||||
} as Partial<typeof wxApi> as any,
|
||||
),
|
||||
);
|
||||
@ -198,23 +201,24 @@ describe("Payment CTA states", () => {
|
||||
talerPayUri: "taller://pay",
|
||||
cancel: nullFunction,
|
||||
goToWalletManualWithdraw: nullFunction,
|
||||
onSuccess: async () => { null; },
|
||||
},
|
||||
{
|
||||
onUpdateNotification: nullFunction,
|
||||
preparePay: async () =>
|
||||
({
|
||||
amountRaw: "USD:10",
|
||||
amountEffective: "USD:10",
|
||||
status: PreparePayResultType.PaymentPossible,
|
||||
} as Partial<PreparePayResult>),
|
||||
({
|
||||
amountRaw: "USD:10",
|
||||
amountEffective: "USD:10",
|
||||
status: PreparePayResultType.PaymentPossible,
|
||||
} as Partial<PreparePayResult>),
|
||||
getBalance: async () =>
|
||||
({
|
||||
balances: [
|
||||
{
|
||||
available: "USD:15",
|
||||
},
|
||||
],
|
||||
} as Partial<BalancesResponse>),
|
||||
({
|
||||
balances: [
|
||||
{
|
||||
available: "USD:15",
|
||||
},
|
||||
],
|
||||
} as Partial<BalancesResponse>),
|
||||
} as Partial<typeof wxApi> as any,
|
||||
),
|
||||
);
|
||||
@ -247,23 +251,24 @@ describe("Payment CTA states", () => {
|
||||
talerPayUri: "taller://pay",
|
||||
cancel: nullFunction,
|
||||
goToWalletManualWithdraw: nullFunction,
|
||||
onSuccess: async () => { null; },
|
||||
},
|
||||
{
|
||||
onUpdateNotification: nullFunction,
|
||||
preparePay: async () =>
|
||||
({
|
||||
amountRaw: "USD:9",
|
||||
amountEffective: "USD:10",
|
||||
status: PreparePayResultType.PaymentPossible,
|
||||
} as Partial<PreparePayResult>),
|
||||
({
|
||||
amountRaw: "USD:9",
|
||||
amountEffective: "USD:10",
|
||||
status: PreparePayResultType.PaymentPossible,
|
||||
} as Partial<PreparePayResult>),
|
||||
getBalance: async () =>
|
||||
({
|
||||
balances: [
|
||||
{
|
||||
available: "USD:15",
|
||||
},
|
||||
],
|
||||
} as Partial<BalancesResponse>),
|
||||
({
|
||||
balances: [
|
||||
{
|
||||
available: "USD:15",
|
||||
},
|
||||
],
|
||||
} as Partial<BalancesResponse>),
|
||||
} as Partial<typeof wxApi> as any,
|
||||
),
|
||||
);
|
||||
@ -296,28 +301,29 @@ describe("Payment CTA states", () => {
|
||||
talerPayUri: "taller://pay",
|
||||
cancel: nullFunction,
|
||||
goToWalletManualWithdraw: nullFunction,
|
||||
onSuccess: async () => { null; },
|
||||
},
|
||||
{
|
||||
onUpdateNotification: nullFunction,
|
||||
preparePay: async () =>
|
||||
({
|
||||
amountRaw: "USD:9",
|
||||
amountEffective: "USD:10",
|
||||
status: PreparePayResultType.PaymentPossible,
|
||||
} as Partial<PreparePayResult>),
|
||||
({
|
||||
amountRaw: "USD:9",
|
||||
amountEffective: "USD:10",
|
||||
status: PreparePayResultType.PaymentPossible,
|
||||
} as Partial<PreparePayResult>),
|
||||
getBalance: async () =>
|
||||
({
|
||||
balances: [
|
||||
{
|
||||
available: "USD:15",
|
||||
},
|
||||
],
|
||||
} as Partial<BalancesResponse>),
|
||||
({
|
||||
balances: [
|
||||
{
|
||||
available: "USD:15",
|
||||
},
|
||||
],
|
||||
} as Partial<BalancesResponse>),
|
||||
confirmPay: async () =>
|
||||
({
|
||||
type: ConfirmPayResultType.Done,
|
||||
contractTerms: {},
|
||||
} as Partial<ConfirmPayResult>),
|
||||
({
|
||||
type: ConfirmPayResultType.Done,
|
||||
contractTerms: {},
|
||||
} as Partial<ConfirmPayResult>),
|
||||
} as Partial<typeof wxApi> as any,
|
||||
),
|
||||
);
|
||||
@ -340,18 +346,18 @@ describe("Payment CTA states", () => {
|
||||
r.payHandler.onClick();
|
||||
}
|
||||
|
||||
await waitNextUpdate();
|
||||
// await waitNextUpdate();
|
||||
|
||||
{
|
||||
const r = getLastResultOrThrow();
|
||||
if (r.status !== "completed") expect.fail();
|
||||
expect(r.balance).deep.equal(Amounts.parseOrThrow("USD:15"));
|
||||
expect(r.amount).deep.equal(Amounts.parseOrThrow("USD:9"));
|
||||
// expect(r.totalFees).deep.equal(Amounts.parseOrThrow("USD:1"));
|
||||
// if (r.payResult.type !== ConfirmPayResultType.Done) expect.fail();
|
||||
// expect(r.payResult.contractTerms).not.undefined;
|
||||
// expect(r.payHandler.onClick).undefined;
|
||||
}
|
||||
// {
|
||||
// const r = getLastResultOrThrow();
|
||||
// if (r.status !== "completed") expect.fail();
|
||||
// expect(r.balance).deep.equal(Amounts.parseOrThrow("USD:15"));
|
||||
// expect(r.amount).deep.equal(Amounts.parseOrThrow("USD:9"));
|
||||
// // expect(r.totalFees).deep.equal(Amounts.parseOrThrow("USD:1"));
|
||||
// // if (r.payResult.type !== ConfirmPayResultType.Done) expect.fail();
|
||||
// // expect(r.payResult.contractTerms).not.undefined;
|
||||
// // expect(r.payHandler.onClick).undefined;
|
||||
// }
|
||||
|
||||
await assertNoPendingUpdate();
|
||||
});
|
||||
@ -364,28 +370,29 @@ describe("Payment CTA states", () => {
|
||||
talerPayUri: "taller://pay",
|
||||
cancel: nullFunction,
|
||||
goToWalletManualWithdraw: nullFunction,
|
||||
onSuccess: async () => { null; },
|
||||
},
|
||||
{
|
||||
onUpdateNotification: nullFunction,
|
||||
preparePay: async () =>
|
||||
({
|
||||
amountRaw: "USD:9",
|
||||
amountEffective: "USD:10",
|
||||
status: PreparePayResultType.PaymentPossible,
|
||||
} as Partial<PreparePayResult>),
|
||||
({
|
||||
amountRaw: "USD:9",
|
||||
amountEffective: "USD:10",
|
||||
status: PreparePayResultType.PaymentPossible,
|
||||
} as Partial<PreparePayResult>),
|
||||
getBalance: async () =>
|
||||
({
|
||||
balances: [
|
||||
{
|
||||
available: "USD:15",
|
||||
},
|
||||
],
|
||||
} as Partial<BalancesResponse>),
|
||||
({
|
||||
balances: [
|
||||
{
|
||||
available: "USD:15",
|
||||
},
|
||||
],
|
||||
} as Partial<BalancesResponse>),
|
||||
confirmPay: async () =>
|
||||
({
|
||||
type: ConfirmPayResultType.Pending,
|
||||
lastError: { code: 1 },
|
||||
} as Partial<ConfirmPayResult>),
|
||||
({
|
||||
type: ConfirmPayResultType.Pending,
|
||||
lastError: { code: 1 },
|
||||
} as Partial<ConfirmPayResult>),
|
||||
} as Partial<typeof wxApi> as any,
|
||||
),
|
||||
);
|
||||
@ -447,23 +454,24 @@ describe("Payment CTA states", () => {
|
||||
talerPayUri: "taller://pay",
|
||||
cancel: nullFunction,
|
||||
goToWalletManualWithdraw: nullFunction,
|
||||
onSuccess: async () => { null; },
|
||||
},
|
||||
{
|
||||
onUpdateNotification: subscriptions.saveSubscription,
|
||||
preparePay: async () =>
|
||||
({
|
||||
amountRaw: "USD:9",
|
||||
amountEffective: "USD:10",
|
||||
status: PreparePayResultType.PaymentPossible,
|
||||
} as Partial<PreparePayResult>),
|
||||
({
|
||||
amountRaw: "USD:9",
|
||||
amountEffective: "USD:10",
|
||||
status: PreparePayResultType.PaymentPossible,
|
||||
} as Partial<PreparePayResult>),
|
||||
getBalance: async () =>
|
||||
({
|
||||
balances: [
|
||||
{
|
||||
available: Amounts.stringify(availableBalance),
|
||||
},
|
||||
],
|
||||
} as Partial<BalancesResponse>),
|
||||
({
|
||||
balances: [
|
||||
{
|
||||
available: Amounts.stringify(availableBalance),
|
||||
},
|
||||
],
|
||||
} as Partial<BalancesResponse>),
|
||||
} as Partial<typeof wxApi> as any,
|
||||
),
|
||||
);
|
||||
|
@ -63,7 +63,6 @@ export function LoadingUriView({ error }: State.LoadingUriError): VNode {
|
||||
type SupportedStates =
|
||||
| State.Ready
|
||||
| State.Confirmed
|
||||
| State.Completed
|
||||
| State.NoBalanceForCurrency
|
||||
| State.NoEnoughBalance;
|
||||
|
||||
@ -167,7 +166,6 @@ export function BaseView(state: SupportedStates): VNode {
|
||||
/>
|
||||
)}
|
||||
</section>
|
||||
{state.status !== "completed" ? (
|
||||
<ButtonsSection
|
||||
amount={state.amount}
|
||||
balance={state.balance}
|
||||
@ -176,7 +174,6 @@ export function BaseView(state: SupportedStates): VNode {
|
||||
payHandler={state.status === "ready" ? state.payHandler : undefined}
|
||||
goToWalletManualWithdraw={state.goToWalletManualWithdraw}
|
||||
/>
|
||||
) : undefined}
|
||||
<section>
|
||||
<Link upperCased onClick={state.cancel}>
|
||||
<i18n.Translate>Cancel</i18n.Translate>
|
||||
@ -285,35 +282,6 @@ function ShowImportantMessage({ state }: { state: SupportedStates }): VNode {
|
||||
);
|
||||
}
|
||||
|
||||
if (state.status == "completed") {
|
||||
const { payResult, paymentError } = state;
|
||||
if (paymentError) {
|
||||
return <ErrorTalerOperation error={paymentError.errorDetail} />;
|
||||
}
|
||||
if (payResult.type === ConfirmPayResultType.Done) {
|
||||
return (
|
||||
<SuccessBox>
|
||||
<h3>
|
||||
<i18n.Translate>Payment complete</i18n.Translate>
|
||||
</h3>
|
||||
<p>
|
||||
{!payResult.contractTerms.fulfillment_message ? (
|
||||
payResult.contractTerms.fulfillment_url ? (
|
||||
<i18n.Translate>
|
||||
You are going to be redirected to $
|
||||
{payResult.contractTerms.fulfillment_url}
|
||||
</i18n.Translate>
|
||||
) : (
|
||||
<i18n.Translate>You can close this page.</i18n.Translate>
|
||||
)
|
||||
) : (
|
||||
payResult.contractTerms.fulfillment_message
|
||||
)}
|
||||
</p>
|
||||
</SuccessBox>
|
||||
);
|
||||
}
|
||||
}
|
||||
return <Fragment />;
|
||||
}
|
||||
|
||||
|
@ -22,7 +22,6 @@ import { compose, StateViewMap } from "../../utils/index.js";
|
||||
import * as wxApi from "../../wxApi.js";
|
||||
import { useComponentState } from "./state.js";
|
||||
import {
|
||||
CompletedView,
|
||||
IgnoredView,
|
||||
InProgressView,
|
||||
LoadingUriView,
|
||||
@ -32,6 +31,7 @@ import {
|
||||
export interface Props {
|
||||
talerRefundUri?: string;
|
||||
cancel: () => Promise<void>;
|
||||
onSuccess: (tx: string) => Promise<void>;
|
||||
}
|
||||
|
||||
export type State =
|
||||
@ -39,8 +39,7 @@ export type State =
|
||||
| State.LoadingUriError
|
||||
| State.Ready
|
||||
| State.Ignored
|
||||
| State.InProgress
|
||||
| State.Completed;
|
||||
| State.InProgress;
|
||||
|
||||
export namespace State {
|
||||
export interface Loading {
|
||||
@ -79,17 +78,12 @@ export namespace State {
|
||||
status: "in-progress";
|
||||
error: undefined;
|
||||
}
|
||||
export interface Completed extends BaseInfo {
|
||||
status: "completed";
|
||||
error: undefined;
|
||||
}
|
||||
}
|
||||
|
||||
const viewMapping: StateViewMap<State> = {
|
||||
loading: Loading,
|
||||
"loading-uri": LoadingUriView,
|
||||
"in-progress": InProgressView,
|
||||
completed: CompletedView,
|
||||
ignored: IgnoredView,
|
||||
ready: ReadyView,
|
||||
};
|
||||
|
@ -21,7 +21,7 @@ import * as wxApi from "../../wxApi.js";
|
||||
import { Props, State } from "./index.js";
|
||||
|
||||
export function useComponentState(
|
||||
{ talerRefundUri, cancel }: Props,
|
||||
{ talerRefundUri, cancel, onSuccess }: Props,
|
||||
api: typeof wxApi,
|
||||
): State {
|
||||
const [ignored, setIgnored] = useState(false);
|
||||
@ -51,8 +51,9 @@ export function useComponentState(
|
||||
const { refund, uri } = info.response;
|
||||
|
||||
const doAccept = async (): Promise<void> => {
|
||||
await api.applyRefund(uri);
|
||||
info.retry();
|
||||
const res = await api.applyRefund(uri);
|
||||
|
||||
onSuccess(res.transactionId);
|
||||
};
|
||||
|
||||
const doIgnore = async (): Promise<void> => {
|
||||
@ -75,13 +76,6 @@ export function useComponentState(
|
||||
};
|
||||
}
|
||||
|
||||
if (Amounts.isZero(baseInfo.awaitingAmount)) {
|
||||
return {
|
||||
status: "completed",
|
||||
...baseInfo,
|
||||
};
|
||||
}
|
||||
|
||||
if (refund.pending) {
|
||||
return {
|
||||
status: "in-progress",
|
||||
|
@ -23,7 +23,6 @@ import { Amounts } from "@gnu-taler/taler-util";
|
||||
import beer from "../../../static-dev/beer.png";
|
||||
import { createExample } from "../../test-utils.js";
|
||||
import {
|
||||
CompletedView,
|
||||
IgnoredView,
|
||||
InProgressView,
|
||||
ReadyView,
|
||||
@ -32,15 +31,6 @@ export default {
|
||||
title: "cta/refund",
|
||||
};
|
||||
|
||||
export const Complete = createExample(CompletedView, {
|
||||
status: "completed",
|
||||
amount: Amounts.parseOrThrow("USD:1"),
|
||||
granted: Amounts.parseOrThrow("USD:1"),
|
||||
error: undefined,
|
||||
merchantName: "the merchant",
|
||||
products: undefined,
|
||||
});
|
||||
|
||||
export const InProgress = createExample(InProgressView, {
|
||||
status: "in-progress",
|
||||
error: undefined,
|
||||
|
@ -40,6 +40,7 @@ describe("Refund CTA states", () => {
|
||||
cancel: async () => {
|
||||
null;
|
||||
},
|
||||
onSuccess: async () => { null; },
|
||||
},
|
||||
{
|
||||
prepareRefund: async () => ({}),
|
||||
@ -79,25 +80,26 @@ describe("Refund CTA states", () => {
|
||||
cancel: async () => {
|
||||
null;
|
||||
},
|
||||
onSuccess: async () => { null; },
|
||||
},
|
||||
{
|
||||
prepareRefund: async () =>
|
||||
({
|
||||
effectivePaid: "EUR:2",
|
||||
awaiting: "EUR:2",
|
||||
gone: "EUR:0",
|
||||
granted: "EUR:0",
|
||||
pending: false,
|
||||
proposalId: "1",
|
||||
info: {
|
||||
contractTermsHash: "123",
|
||||
merchant: {
|
||||
name: "the merchant name",
|
||||
},
|
||||
orderId: "orderId1",
|
||||
summary: "the summary",
|
||||
({
|
||||
effectivePaid: "EUR:2",
|
||||
awaiting: "EUR:2",
|
||||
gone: "EUR:0",
|
||||
granted: "EUR:0",
|
||||
pending: false,
|
||||
proposalId: "1",
|
||||
info: {
|
||||
contractTermsHash: "123",
|
||||
merchant: {
|
||||
name: "the merchant name",
|
||||
},
|
||||
} as PrepareRefundResult as any),
|
||||
orderId: "orderId1",
|
||||
summary: "the summary",
|
||||
},
|
||||
} as PrepareRefundResult as any),
|
||||
applyRefund: async () => ({}),
|
||||
onUpdateNotification: async () => ({}),
|
||||
} as any,
|
||||
@ -136,25 +138,26 @@ describe("Refund CTA states", () => {
|
||||
cancel: async () => {
|
||||
null;
|
||||
},
|
||||
onSuccess: async () => { null; },
|
||||
},
|
||||
{
|
||||
prepareRefund: async () =>
|
||||
({
|
||||
effectivePaid: "EUR:2",
|
||||
awaiting: "EUR:2",
|
||||
gone: "EUR:0",
|
||||
granted: "EUR:0",
|
||||
pending: false,
|
||||
proposalId: "1",
|
||||
info: {
|
||||
contractTermsHash: "123",
|
||||
merchant: {
|
||||
name: "the merchant name",
|
||||
},
|
||||
orderId: "orderId1",
|
||||
summary: "the summary",
|
||||
({
|
||||
effectivePaid: "EUR:2",
|
||||
awaiting: "EUR:2",
|
||||
gone: "EUR:0",
|
||||
granted: "EUR:0",
|
||||
pending: false,
|
||||
proposalId: "1",
|
||||
info: {
|
||||
contractTermsHash: "123",
|
||||
merchant: {
|
||||
name: "the merchant name",
|
||||
},
|
||||
} as PrepareRefundResult as any),
|
||||
orderId: "orderId1",
|
||||
summary: "the summary",
|
||||
},
|
||||
} as PrepareRefundResult as any),
|
||||
applyRefund: async () => ({}),
|
||||
onUpdateNotification: async () => ({}),
|
||||
} as any,
|
||||
@ -220,25 +223,27 @@ describe("Refund CTA states", () => {
|
||||
cancel: async () => {
|
||||
null;
|
||||
},
|
||||
onSuccess: async () => { null; },
|
||||
|
||||
},
|
||||
{
|
||||
prepareRefund: async () =>
|
||||
({
|
||||
awaiting: Amounts.stringify(awaiting),
|
||||
effectivePaid: "EUR:2",
|
||||
gone: "EUR:0",
|
||||
granted: Amounts.stringify(granted),
|
||||
pending,
|
||||
proposalId: "1",
|
||||
info: {
|
||||
contractTermsHash: "123",
|
||||
merchant: {
|
||||
name: "the merchant name",
|
||||
},
|
||||
orderId: "orderId1",
|
||||
summary: "the summary",
|
||||
({
|
||||
awaiting: Amounts.stringify(awaiting),
|
||||
effectivePaid: "EUR:2",
|
||||
gone: "EUR:0",
|
||||
granted: Amounts.stringify(granted),
|
||||
pending,
|
||||
proposalId: "1",
|
||||
info: {
|
||||
contractTermsHash: "123",
|
||||
merchant: {
|
||||
name: "the merchant name",
|
||||
},
|
||||
} as PrepareRefundResult as any),
|
||||
orderId: "orderId1",
|
||||
summary: "the summary",
|
||||
},
|
||||
} as PrepareRefundResult as any),
|
||||
applyRefund: async () => ({}),
|
||||
onUpdateNotification: subscriptions.saveSubscription,
|
||||
} as any,
|
||||
@ -286,7 +291,7 @@ describe("Refund CTA states", () => {
|
||||
{
|
||||
const state = getLastResultOrThrow();
|
||||
|
||||
if (state.status !== "completed") expect.fail("3");
|
||||
if (state.status !== "ready") expect.fail("3");
|
||||
if (state.error) expect.fail();
|
||||
expect(state.merchantName).eq("the merchant name");
|
||||
expect(state.products).undefined;
|
||||
|
@ -92,32 +92,6 @@ export function InProgressView(state: State.InProgress): VNode {
|
||||
</WalletAction>
|
||||
);
|
||||
}
|
||||
export function CompletedView(state: State.Completed): VNode {
|
||||
const { i18n } = useTranslationContext();
|
||||
|
||||
return (
|
||||
<WalletAction>
|
||||
<LogoHeader />
|
||||
|
||||
<SubTitle>
|
||||
<i18n.Translate>Digital cash refund</i18n.Translate>
|
||||
</SubTitle>
|
||||
<section>
|
||||
<p>
|
||||
<i18n.Translate>this refund is already accepted.</i18n.Translate>
|
||||
</p>
|
||||
</section>
|
||||
<section>
|
||||
<Part
|
||||
big
|
||||
title={<i18n.Translate>Total to refunded</i18n.Translate>}
|
||||
text={<Amount value={state.granted} />}
|
||||
kind="negative"
|
||||
/>
|
||||
</section>
|
||||
</WalletAction>
|
||||
);
|
||||
}
|
||||
export function ReadyView(state: State.Ready): VNode {
|
||||
const { i18n } = useTranslationContext();
|
||||
return (
|
||||
|
@ -32,6 +32,7 @@ import {
|
||||
export interface Props {
|
||||
talerTipUri?: string;
|
||||
onCancel: () => Promise<void>;
|
||||
onSuccess: (tx: string) => Promise<void>;
|
||||
}
|
||||
|
||||
export type State =
|
||||
|
@ -21,7 +21,7 @@ import * as wxApi from "../../wxApi.js";
|
||||
import { Props, State } from "./index.js";
|
||||
|
||||
export function useComponentState(
|
||||
{ talerTipUri, onCancel }: Props,
|
||||
{ talerTipUri, onCancel, onSuccess }: Props,
|
||||
api: typeof wxApi,
|
||||
): State {
|
||||
const [tipIgnored, setTipIgnored] = useState(false);
|
||||
@ -48,8 +48,11 @@ export function useComponentState(
|
||||
const { tip } = tipInfo.response;
|
||||
|
||||
const doAccept = async (): Promise<void> => {
|
||||
await api.acceptTip({ walletTipId: tip.walletTipId });
|
||||
const res = await api.acceptTip({ walletTipId: tip.walletTipId });
|
||||
|
||||
//FIX: this may not be seen since we are moving to the success also
|
||||
tipInfo.retry();
|
||||
onSuccess(res.transactionId)
|
||||
};
|
||||
|
||||
const baseInfo = {
|
||||
|
@ -34,6 +34,7 @@ describe("Tip CTA states", () => {
|
||||
onCancel: async () => {
|
||||
null;
|
||||
},
|
||||
onSuccess: async () => { null; },
|
||||
},
|
||||
{
|
||||
prepareTip: async () => ({}),
|
||||
@ -74,16 +75,17 @@ describe("Tip CTA states", () => {
|
||||
onCancel: async () => {
|
||||
null;
|
||||
},
|
||||
onSuccess: async () => { null; },
|
||||
},
|
||||
{
|
||||
prepareTip: async () =>
|
||||
({
|
||||
accepted: tipAccepted,
|
||||
exchangeBaseUrl: "exchange url",
|
||||
merchantBaseUrl: "merchant url",
|
||||
tipAmountEffective: "EUR:1",
|
||||
walletTipId: "tip_id",
|
||||
} as PrepareTipResult as any),
|
||||
({
|
||||
accepted: tipAccepted,
|
||||
exchangeBaseUrl: "exchange url",
|
||||
merchantBaseUrl: "merchant url",
|
||||
tipAmountEffective: "EUR:1",
|
||||
walletTipId: "tip_id",
|
||||
} as PrepareTipResult as any),
|
||||
acceptTip: async () => {
|
||||
tipAccepted = true;
|
||||
},
|
||||
@ -134,15 +136,16 @@ describe("Tip CTA states", () => {
|
||||
onCancel: async () => {
|
||||
null;
|
||||
},
|
||||
onSuccess: async () => { null; },
|
||||
},
|
||||
{
|
||||
prepareTip: async () =>
|
||||
({
|
||||
exchangeBaseUrl: "exchange url",
|
||||
merchantBaseUrl: "merchant url",
|
||||
tipAmountEffective: "EUR:1",
|
||||
walletTipId: "tip_id",
|
||||
} as PrepareTipResult as any),
|
||||
({
|
||||
exchangeBaseUrl: "exchange url",
|
||||
merchantBaseUrl: "merchant url",
|
||||
tipAmountEffective: "EUR:1",
|
||||
walletTipId: "tip_id",
|
||||
} as PrepareTipResult as any),
|
||||
acceptTip: async () => ({}),
|
||||
} as any,
|
||||
),
|
||||
@ -188,16 +191,17 @@ describe("Tip CTA states", () => {
|
||||
onCancel: async () => {
|
||||
null;
|
||||
},
|
||||
onSuccess: async () => { null; },
|
||||
},
|
||||
{
|
||||
prepareTip: async () =>
|
||||
({
|
||||
accepted: true,
|
||||
exchangeBaseUrl: "exchange url",
|
||||
merchantBaseUrl: "merchant url",
|
||||
tipAmountEffective: "EUR:1",
|
||||
walletTipId: "tip_id",
|
||||
} as PrepareTipResult as any),
|
||||
({
|
||||
accepted: true,
|
||||
exchangeBaseUrl: "exchange url",
|
||||
merchantBaseUrl: "merchant url",
|
||||
tipAmountEffective: "EUR:1",
|
||||
walletTipId: "tip_id",
|
||||
} as PrepareTipResult as any),
|
||||
acceptTip: async () => ({}),
|
||||
} as any,
|
||||
),
|
||||
|
@ -21,17 +21,17 @@ import { ButtonHandler, TextFieldHandler } from "../../mui/handlers.js";
|
||||
import { compose, StateViewMap } from "../../utils/index.js";
|
||||
import * as wxApi from "../../wxApi.js";
|
||||
import { useComponentState } from "./state.js";
|
||||
import { LoadingUriView, ReadyView, CreatedView } from "./views.js";
|
||||
import { LoadingUriView, ReadyView } from "./views.js";
|
||||
|
||||
export interface Props {
|
||||
amount: string;
|
||||
onClose: () => Promise<void>;
|
||||
onSuccess: (tx: string) => Promise<void>;
|
||||
}
|
||||
|
||||
export type State =
|
||||
| State.Loading
|
||||
| State.LoadingUriError
|
||||
| State.Created
|
||||
| State.Ready;
|
||||
|
||||
export namespace State {
|
||||
@ -49,11 +49,6 @@ export namespace State {
|
||||
error: undefined;
|
||||
cancel: ButtonHandler;
|
||||
}
|
||||
export interface Created extends BaseInfo {
|
||||
status: "created";
|
||||
talerUri: string;
|
||||
copyToClipboard: ButtonHandler;
|
||||
}
|
||||
export interface Ready extends BaseInfo {
|
||||
status: "ready";
|
||||
invalid: boolean;
|
||||
@ -69,7 +64,6 @@ export namespace State {
|
||||
const viewMapping: StateViewMap<State> = {
|
||||
loading: Loading,
|
||||
"loading-uri": LoadingUriView,
|
||||
created: CreatedView,
|
||||
ready: ReadyView,
|
||||
};
|
||||
|
||||
|
@ -21,34 +21,18 @@ import * as wxApi from "../../wxApi.js";
|
||||
import { Props, State } from "./index.js";
|
||||
|
||||
export function useComponentState(
|
||||
{ amount: amountStr, onClose }: Props,
|
||||
{ amount: amountStr, onClose, onSuccess }: Props,
|
||||
api: typeof wxApi,
|
||||
): State {
|
||||
const amount = Amounts.parseOrThrow(amountStr);
|
||||
|
||||
const [subject, setSubject] = useState("");
|
||||
const [talerUri, setTalerUri] = useState("");
|
||||
const [operationError, setOperationError] = useState<
|
||||
TalerErrorDetail | undefined
|
||||
>(undefined);
|
||||
|
||||
if (talerUri) {
|
||||
return {
|
||||
status: "created",
|
||||
talerUri,
|
||||
error: undefined,
|
||||
cancel: {
|
||||
onClick: onClose,
|
||||
},
|
||||
copyToClipboard: {
|
||||
onClick: async () => {
|
||||
navigator.clipboard.writeText(talerUri);
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
async function accept(): Promise<string> {
|
||||
async function accept(): Promise<void> {
|
||||
try {
|
||||
const resp = await api.initiatePeerPushPayment({
|
||||
amount: Amounts.stringify(amount),
|
||||
@ -56,7 +40,7 @@ export function useComponentState(
|
||||
summary: subject,
|
||||
},
|
||||
});
|
||||
return resp.talerUri;
|
||||
onSuccess(resp.transactionId);
|
||||
} catch (e) {
|
||||
if (e instanceof TalerError) {
|
||||
setOperationError(e.errorDetail);
|
||||
@ -77,10 +61,7 @@ export function useComponentState(
|
||||
onInput: async (e) => setSubject(e),
|
||||
},
|
||||
create: {
|
||||
onClick: async () => {
|
||||
const uri = await accept();
|
||||
setTalerUri(uri);
|
||||
},
|
||||
onClick: accept
|
||||
},
|
||||
chosenAmount: amount,
|
||||
toBeReceived: amount,
|
||||
|
@ -20,19 +20,12 @@
|
||||
*/
|
||||
|
||||
import { createExample } from "../../test-utils.js";
|
||||
import { ReadyView, CreatedView } from "./views.js";
|
||||
import { ReadyView } from "./views.js";
|
||||
|
||||
export default {
|
||||
title: "wallet/transfer create",
|
||||
};
|
||||
|
||||
export const ShowQr = createExample(CreatedView, {
|
||||
talerUri:
|
||||
"taler://pay-push/exchange.taler.ar/HS585JK0QCXHJ8Z8QWZA3EBAY5WY7XNC1RR2MHJXSH2Z4WP0YPJ0",
|
||||
cancel: {},
|
||||
copyToClipboard: {},
|
||||
});
|
||||
|
||||
export const Ready = createExample(ReadyView, {
|
||||
chosenAmount: {
|
||||
currency: "ARS",
|
||||
|
@ -38,34 +38,6 @@ export function LoadingUriView({ error }: State.LoadingUriError): VNode {
|
||||
);
|
||||
}
|
||||
|
||||
export function CreatedView({
|
||||
talerUri,
|
||||
copyToClipboard,
|
||||
cancel,
|
||||
}: State.Created): VNode {
|
||||
const { i18n } = useTranslationContext();
|
||||
return (
|
||||
<WalletAction>
|
||||
<LogoHeader />
|
||||
<SubTitle>
|
||||
<i18n.Translate>Digital cash transfer</i18n.Translate>
|
||||
</SubTitle>
|
||||
<section>
|
||||
<p>Show this QR to receive the transfer</p>
|
||||
<QR text={talerUri} />
|
||||
</section>
|
||||
<section>
|
||||
or
|
||||
<Button onClick={copyToClipboard.onClick}>Copy the transfer URI</Button>
|
||||
</section>
|
||||
<section>
|
||||
<Link upperCased onClick={cancel.onClick}>
|
||||
<i18n.Translate>Close</i18n.Translate>
|
||||
</Link>
|
||||
</section>
|
||||
</WalletAction>
|
||||
);
|
||||
}
|
||||
|
||||
export function ReadyView({
|
||||
subject,
|
||||
|
@ -30,6 +30,7 @@ import { LoadingUriView, ReadyView } from "./views.js";
|
||||
export interface Props {
|
||||
talerPayPushUri: string;
|
||||
onClose: () => Promise<void>;
|
||||
onSuccess: (tx: string) => Promise<void>;
|
||||
}
|
||||
|
||||
export type State = State.Loading | State.LoadingUriError | State.Ready;
|
||||
|
@ -27,7 +27,7 @@ import * as wxApi from "../../wxApi.js";
|
||||
import { Props, State } from "./index.js";
|
||||
|
||||
export function useComponentState(
|
||||
{ talerPayPushUri, onClose }: Props,
|
||||
{ talerPayPushUri, onClose, onSuccess }: Props,
|
||||
api: typeof wxApi,
|
||||
): State {
|
||||
const hook = useAsyncAsHook(async () => {
|
||||
@ -68,7 +68,7 @@ export function useComponentState(
|
||||
const resp = await api.acceptPeerPushPayment({
|
||||
peerPushPaymentIncomingId,
|
||||
});
|
||||
await onClose();
|
||||
onSuccess(resp.transactionId)
|
||||
} catch (e) {
|
||||
if (e instanceof TalerError) {
|
||||
setOperationError(e.errorDetail);
|
||||
|
@ -26,7 +26,6 @@ import {
|
||||
useComponentStateFromURI,
|
||||
} from "./state.js";
|
||||
import {
|
||||
CompletedView,
|
||||
LoadingExchangeView,
|
||||
LoadingInfoView,
|
||||
LoadingUriView,
|
||||
@ -36,11 +35,13 @@ import {
|
||||
export interface PropsFromURI {
|
||||
talerWithdrawUri: string | undefined;
|
||||
cancel: () => Promise<void>;
|
||||
onSuccess: (txid: string) => Promise<void>;
|
||||
}
|
||||
|
||||
export interface PropsFromParams {
|
||||
amount: string;
|
||||
cancel: () => Promise<void>;
|
||||
onSuccess: (txid: string) => Promise<void>;
|
||||
}
|
||||
|
||||
export type State =
|
||||
@ -48,8 +49,7 @@ export type State =
|
||||
| State.LoadingUriError
|
||||
| State.LoadingExchangeError
|
||||
| State.LoadingInfoError
|
||||
| State.Success
|
||||
| State.Completed;
|
||||
| State.Success;
|
||||
|
||||
export namespace State {
|
||||
export interface Loading {
|
||||
@ -69,11 +69,6 @@ export namespace State {
|
||||
error: HookError;
|
||||
}
|
||||
|
||||
export type Completed = {
|
||||
status: "completed";
|
||||
error: undefined;
|
||||
};
|
||||
|
||||
export type Success = {
|
||||
status: "success";
|
||||
error: undefined;
|
||||
@ -100,7 +95,6 @@ const viewMapping: StateViewMap<State> = {
|
||||
"loading-uri": LoadingUriView,
|
||||
"loading-exchange": LoadingExchangeView,
|
||||
"loading-info": LoadingInfoView,
|
||||
completed: CompletedView,
|
||||
success: SuccessView,
|
||||
};
|
||||
|
||||
|
@ -23,7 +23,7 @@ import * as wxApi from "../../wxApi.js";
|
||||
import { PropsFromURI, PropsFromParams, State } from "./index.js";
|
||||
|
||||
export function useComponentStateFromParams(
|
||||
{ amount, cancel }: PropsFromParams,
|
||||
{ amount, cancel, onSuccess }: PropsFromParams,
|
||||
api: typeof wxApi,
|
||||
): State {
|
||||
const [ageRestricted, setAgeRestricted] = useState(0);
|
||||
@ -40,8 +40,8 @@ export function useComponentStateFromParams(
|
||||
// get the first exchange with the currency as the default one
|
||||
const exchange = exchangeHookDep
|
||||
? exchangeHookDep.exchanges.find(
|
||||
(e) => e.currency === chosenAmount.currency,
|
||||
)
|
||||
(e) => e.currency === chosenAmount.currency,
|
||||
)
|
||||
: undefined;
|
||||
/**
|
||||
* For the exchange selected, bring the status of the terms of service
|
||||
@ -90,7 +90,6 @@ export function useComponentStateFromParams(
|
||||
undefined,
|
||||
);
|
||||
const [doingWithdraw, setDoingWithdraw] = useState<boolean>(false);
|
||||
const [withdrawCompleted, setWithdrawCompleted] = useState<boolean>(false);
|
||||
|
||||
if (!exchangeHook) return { status: "loading", error: undefined };
|
||||
if (exchangeHook.hasError) {
|
||||
@ -122,7 +121,7 @@ export function useComponentStateFromParams(
|
||||
Amounts.stringify(amount),
|
||||
);
|
||||
|
||||
setWithdrawCompleted(true);
|
||||
onSuccess(response.transactionId);
|
||||
} catch (e) {
|
||||
if (e instanceof TalerError) {
|
||||
setWithdrawError(e);
|
||||
@ -143,9 +142,6 @@ export function useComponentStateFromParams(
|
||||
if (!amountHook.response) {
|
||||
return { status: "loading", error: undefined };
|
||||
}
|
||||
if (withdrawCompleted) {
|
||||
return { status: "completed", error: undefined };
|
||||
}
|
||||
|
||||
const withdrawalFee = Amounts.sub(
|
||||
amountHook.response.amount.raw,
|
||||
@ -156,8 +152,8 @@ export function useComponentStateFromParams(
|
||||
const { state: termsState } = (!terms
|
||||
? undefined
|
||||
: terms.hasError
|
||||
? undefined
|
||||
: terms.response) || { state: undefined };
|
||||
? undefined
|
||||
: terms.response) || { state: undefined };
|
||||
|
||||
async function onAccept(accepted: boolean): Promise<void> {
|
||||
if (!termsState || !exchange) return;
|
||||
@ -194,10 +190,10 @@ export function useComponentStateFromParams(
|
||||
//TODO: calculate based on exchange info
|
||||
const ageRestriction = ageRestrictionEnabled
|
||||
? {
|
||||
list: ageRestrictionOptions,
|
||||
value: String(ageRestricted),
|
||||
onChange: async (v: string) => setAgeRestricted(parseInt(v, 10)),
|
||||
}
|
||||
list: ageRestrictionOptions,
|
||||
value: String(ageRestricted),
|
||||
onChange: async (v: string) => setAgeRestricted(parseInt(v, 10)),
|
||||
}
|
||||
: undefined;
|
||||
|
||||
return {
|
||||
@ -218,19 +214,19 @@ export function useComponentStateFromParams(
|
||||
tosProps: !termsState
|
||||
? undefined
|
||||
: {
|
||||
onAccept,
|
||||
onReview: setReviewing,
|
||||
reviewed: reviewed,
|
||||
reviewing: reviewing,
|
||||
terms: termsState,
|
||||
},
|
||||
onAccept,
|
||||
onReview: setReviewing,
|
||||
reviewed: reviewed,
|
||||
reviewing: reviewing,
|
||||
terms: termsState,
|
||||
},
|
||||
mustAcceptFirst,
|
||||
cancel,
|
||||
};
|
||||
}
|
||||
|
||||
export function useComponentStateFromURI(
|
||||
{ talerWithdrawUri, cancel }: PropsFromURI,
|
||||
{ talerWithdrawUri, cancel, onSuccess }: PropsFromURI,
|
||||
api: typeof wxApi,
|
||||
): State {
|
||||
const [ageRestricted, setAgeRestricted] = useState(0);
|
||||
@ -303,7 +299,6 @@ export function useComponentStateFromURI(
|
||||
undefined,
|
||||
);
|
||||
const [doingWithdraw, setDoingWithdraw] = useState<boolean>(false);
|
||||
const [withdrawCompleted, setWithdrawCompleted] = useState<boolean>(false);
|
||||
|
||||
if (!uriInfoHook) return { status: "loading", error: undefined };
|
||||
if (uriInfoHook.hasError) {
|
||||
@ -343,8 +338,10 @@ export function useComponentStateFromURI(
|
||||
);
|
||||
if (res.confirmTransferUrl) {
|
||||
document.location.href = res.confirmTransferUrl;
|
||||
} else {
|
||||
onSuccess(res.transactionId)
|
||||
}
|
||||
setWithdrawCompleted(true);
|
||||
|
||||
} catch (e) {
|
||||
if (e instanceof TalerError) {
|
||||
setWithdrawError(e);
|
||||
@ -365,9 +362,6 @@ export function useComponentStateFromURI(
|
||||
if (!amountHook.response) {
|
||||
return { status: "loading", error: undefined };
|
||||
}
|
||||
if (withdrawCompleted) {
|
||||
return { status: "completed", error: undefined };
|
||||
}
|
||||
|
||||
const withdrawalFee = Amounts.sub(
|
||||
amountHook.response.amount.raw,
|
||||
@ -378,8 +372,8 @@ export function useComponentStateFromURI(
|
||||
const { state: termsState } = (!terms
|
||||
? undefined
|
||||
: terms.hasError
|
||||
? undefined
|
||||
: terms.response) || { state: undefined };
|
||||
? undefined
|
||||
: terms.response) || { state: undefined };
|
||||
|
||||
async function onAccept(accepted: boolean): Promise<void> {
|
||||
if (!termsState || !thisExchange) return;
|
||||
@ -416,10 +410,10 @@ export function useComponentStateFromURI(
|
||||
//TODO: calculate based on exchange info
|
||||
const ageRestriction = ageRestrictionEnabled
|
||||
? {
|
||||
list: ageRestrictionOptions,
|
||||
value: String(ageRestricted),
|
||||
onChange: async (v: string) => setAgeRestricted(parseInt(v, 10)),
|
||||
}
|
||||
list: ageRestrictionOptions,
|
||||
value: String(ageRestricted),
|
||||
onChange: async (v: string) => setAgeRestricted(parseInt(v, 10)),
|
||||
}
|
||||
: undefined;
|
||||
|
||||
return {
|
||||
@ -441,12 +435,12 @@ export function useComponentStateFromURI(
|
||||
tosProps: !termsState
|
||||
? undefined
|
||||
: {
|
||||
onAccept,
|
||||
onReview: setReviewing,
|
||||
reviewed: reviewed,
|
||||
reviewing: reviewing,
|
||||
terms: termsState,
|
||||
},
|
||||
onAccept,
|
||||
onReview: setReviewing,
|
||||
reviewed: reviewed,
|
||||
reviewing: reviewing,
|
||||
terms: termsState,
|
||||
},
|
||||
mustAcceptFirst,
|
||||
cancel,
|
||||
};
|
||||
|
@ -21,7 +21,7 @@
|
||||
|
||||
import { createExample } from "../../test-utils.js";
|
||||
import { TermsState } from "../../utils/index.js";
|
||||
import { CompletedView, SuccessView } from "./views.js";
|
||||
import { SuccessView } from "./views.js";
|
||||
|
||||
export default {
|
||||
title: "cta/withdraw",
|
||||
@ -179,11 +179,6 @@ export const EditExchangeModified = createExample(SuccessView, {
|
||||
tosProps: normalTosState,
|
||||
});
|
||||
|
||||
export const CompletedWithoutBankURL = createExample(CompletedView, {
|
||||
status: "completed",
|
||||
error: undefined,
|
||||
});
|
||||
|
||||
export const WithAgeRestriction = createExample(SuccessView, {
|
||||
error: undefined,
|
||||
status: "success",
|
||||
|
@ -68,6 +68,7 @@ describe("Withdraw CTA states", () => {
|
||||
cancel: async () => {
|
||||
null;
|
||||
},
|
||||
onSuccess: async () => { null },
|
||||
},
|
||||
{
|
||||
listExchanges: async () => ({ exchanges }),
|
||||
@ -108,6 +109,7 @@ describe("Withdraw CTA states", () => {
|
||||
cancel: async () => {
|
||||
null;
|
||||
},
|
||||
onSuccess: async () => { null },
|
||||
},
|
||||
{
|
||||
listExchanges: async () => ({ exchanges }),
|
||||
@ -150,6 +152,7 @@ describe("Withdraw CTA states", () => {
|
||||
cancel: async () => {
|
||||
null;
|
||||
},
|
||||
onSuccess: async () => { null },
|
||||
},
|
||||
{
|
||||
listExchanges: async () => ({ exchanges }),
|
||||
@ -160,10 +163,10 @@ describe("Withdraw CTA states", () => {
|
||||
}),
|
||||
getExchangeWithdrawalInfo:
|
||||
async (): Promise<ExchangeWithdrawDetails> =>
|
||||
({
|
||||
withdrawalAmountRaw: "ARS:2",
|
||||
withdrawalAmountEffective: "ARS:2",
|
||||
} as any),
|
||||
({
|
||||
withdrawalAmountRaw: "ARS:2",
|
||||
withdrawalAmountEffective: "ARS:2",
|
||||
} as any),
|
||||
getExchangeTos: async (): Promise<GetExchangeTosResult> => ({
|
||||
contentType: "text",
|
||||
content: "just accept",
|
||||
@ -224,6 +227,7 @@ describe("Withdraw CTA states", () => {
|
||||
cancel: async () => {
|
||||
null;
|
||||
},
|
||||
onSuccess: async () => { null },
|
||||
},
|
||||
{
|
||||
listExchanges: async () => ({ exchanges }),
|
||||
@ -234,10 +238,10 @@ describe("Withdraw CTA states", () => {
|
||||
}),
|
||||
getExchangeWithdrawalInfo:
|
||||
async (): Promise<ExchangeWithdrawDetails> =>
|
||||
({
|
||||
withdrawalAmountRaw: "ARS:2",
|
||||
withdrawalAmountEffective: "ARS:2",
|
||||
} as any),
|
||||
({
|
||||
withdrawalAmountRaw: "ARS:2",
|
||||
withdrawalAmountEffective: "ARS:2",
|
||||
} as any),
|
||||
getExchangeTos: async (): Promise<GetExchangeTosResult> => ({
|
||||
contentType: "text",
|
||||
content: "just accept",
|
||||
|
@ -76,29 +76,6 @@ export function LoadingInfoView({ error }: State.LoadingInfoError): VNode {
|
||||
);
|
||||
}
|
||||
|
||||
export function CompletedView(state: State.Completed): VNode {
|
||||
const { i18n } = useTranslationContext();
|
||||
return (
|
||||
<WalletAction>
|
||||
<LogoHeader />
|
||||
<SubTitle>
|
||||
<i18n.Translate>Digital cash withdrawal</i18n.Translate>
|
||||
</SubTitle>
|
||||
<SuccessBox>
|
||||
<h3>
|
||||
<i18n.Translate>Withdrawal in process...</i18n.Translate>
|
||||
</h3>
|
||||
<p>
|
||||
<i18n.Translate>
|
||||
You can close the page now. Check your bank if the transaction need
|
||||
a confirmation step to be completed
|
||||
</i18n.Translate>
|
||||
</p>
|
||||
</SuccessBox>
|
||||
</WalletAction>
|
||||
);
|
||||
}
|
||||
|
||||
export function SuccessView(state: State.Success): VNode {
|
||||
const { i18n } = useTranslationContext();
|
||||
return (
|
||||
|
@ -249,41 +249,49 @@ export function Application(): VNode {
|
||||
redirectTo(Pages.ctaWithdrawManual({ amount }))
|
||||
}
|
||||
cancel={() => redirectTo(Pages.balance)}
|
||||
onSuccess={(tid:string) => redirectTo(Pages.balanceTransaction({ tid }))}
|
||||
/>
|
||||
<Route
|
||||
path={Pages.ctaRefund}
|
||||
component={RefundPage}
|
||||
cancel={() => redirectTo(Pages.balance)}
|
||||
onSuccess={(tid:string) => redirectTo(Pages.balanceTransaction({ tid }))}
|
||||
/>
|
||||
<Route
|
||||
path={Pages.ctaTips}
|
||||
component={TipPage}
|
||||
onCancel={() => redirectTo(Pages.balance)}
|
||||
onSuccess={(tid:string) => redirectTo(Pages.balanceTransaction({ tid }))}
|
||||
/>
|
||||
<Route
|
||||
path={Pages.ctaWithdraw}
|
||||
component={WithdrawPageFromURI}
|
||||
cancel={() => redirectTo(Pages.balance)}
|
||||
onSuccess={(tid:string) => redirectTo(Pages.balanceTransaction({ tid }))}
|
||||
/>
|
||||
<Route
|
||||
path={Pages.ctaWithdrawManual.pattern}
|
||||
component={WithdrawPageFromParams}
|
||||
cancel={() => redirectTo(Pages.balance)}
|
||||
onSuccess={(tid:string) => redirectTo(Pages.balanceTransaction({ tid }))}
|
||||
/>
|
||||
<Route
|
||||
path={Pages.ctaDeposit}
|
||||
component={DepositPageCTA}
|
||||
cancel={() => redirectTo(Pages.balance)}
|
||||
onSuccess={(tid:string) => redirectTo(Pages.balanceTransaction({ tid }))}
|
||||
/>
|
||||
<Route
|
||||
path={Pages.ctaInvoiceCreate.pattern}
|
||||
component={InvoiceCreatePage}
|
||||
onClose={() => redirectTo(Pages.balance)}
|
||||
onSuccess={(tid:string) => redirectTo(Pages.balanceTransaction({ tid }))}
|
||||
/>
|
||||
<Route
|
||||
path={Pages.ctaTransferCreate.pattern}
|
||||
component={TransferCreatePage}
|
||||
onClose={() => redirectTo(Pages.balance)}
|
||||
onSuccess={(tid:string) => redirectTo(Pages.balanceTransaction({ tid }))}
|
||||
/>
|
||||
<Route
|
||||
path={Pages.ctaInvoicePay}
|
||||
@ -292,11 +300,13 @@ export function Application(): VNode {
|
||||
redirectTo(Pages.ctaWithdrawManual({ amount }))
|
||||
}
|
||||
onClose={() => redirectTo(Pages.balance)}
|
||||
onSuccess={(tid:string) => redirectTo(Pages.balanceTransaction({ tid }))}
|
||||
/>
|
||||
<Route
|
||||
path={Pages.ctaTransferPickup}
|
||||
component={TransferPickupPage}
|
||||
onClose={() => redirectTo(Pages.balance)}
|
||||
onSuccess={(tid:string) => redirectTo(Pages.balanceTransaction({ tid }))}
|
||||
/>
|
||||
|
||||
{/**
|
||||
|
@ -634,7 +634,7 @@ export function TransactionView({
|
||||
text={transaction.exchangeBaseUrl}
|
||||
kind="neutral"
|
||||
/>
|
||||
{transaction.pending && (
|
||||
{transaction.pending && ( /** pending is not-pay */
|
||||
<Part
|
||||
title={<i18n.Translate>URI</i18n.Translate>}
|
||||
text={<ShowQrWithCopy text={transaction.talerUri} />}
|
||||
@ -720,13 +720,13 @@ export function TransactionView({
|
||||
text={transaction.exchangeBaseUrl}
|
||||
kind="neutral"
|
||||
/>
|
||||
{transaction.pending && (
|
||||
{/* {transaction.pending && ( //pending is not-received
|
||||
)} */}
|
||||
<Part
|
||||
title={<i18n.Translate>URI</i18n.Translate>}
|
||||
text={<ShowQrWithCopy text={transaction.talerUri} />}
|
||||
kind="neutral"
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<Part
|
||||
title={<i18n.Translate>Details</i18n.Translate>}
|
||||
text={
|
||||
|
Loading…
Reference in New Issue
Block a user