aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/headless/taler-wallet-cli.ts14
-rw-r--r--src/taleruri.ts53
-rw-r--r--src/wallet.ts17
-rw-r--r--src/webex/pages/pay.tsx13
-rw-r--r--src/webex/pages/refund.tsx8
-rw-r--r--src/webex/pages/tip.tsx29
-rw-r--r--src/webex/renderHtml.tsx25
-rw-r--r--src/webex/wxBackend.ts2
8 files changed, 121 insertions, 40 deletions
diff --git a/src/headless/taler-wallet-cli.ts b/src/headless/taler-wallet-cli.ts
index 86eaec64c..bfa1ac4b9 100644
--- a/src/headless/taler-wallet-cli.ts
+++ b/src/headless/taler-wallet-cli.ts
@@ -179,6 +179,20 @@ program
wallet.stop();
});
+
+
+ program
+ .command("refund-uri <refund-uri>")
+ .action(async (refundUri, cmdObj) => {
+ applyVerbose(program.verbose);
+ console.log("getting refund", refundUri);
+ const wallet = await getDefaultNodeWallet({
+ persistentStoragePath: walletDbPath,
+ });
+ await wallet.applyRefund(refundUri);
+ wallet.stop();
+ });
+
program
.command("pay-uri <pay-uri")
.option("-y, --yes", "automatically answer yes to prompts")
diff --git a/src/taleruri.ts b/src/taleruri.ts
index f5fc77421..bd01abb65 100644
--- a/src/taleruri.ts
+++ b/src/taleruri.ts
@@ -26,6 +26,10 @@ export interface WithdrawUriResult {
statusUrl: string;
}
+export interface RefundUriResult {
+ refundUrl: string;
+}
+
export interface TipUriResult {
tipPickupUrl: string;
tipId: string;
@@ -155,3 +159,52 @@ export function parseTipUri(s: string): TipUriResult | undefined {
merchantOrigin: new URI(tipPickupUrl).origin(),
};
}
+
+export function parseRefundUri(s: string): RefundUriResult | undefined {
+ const parsedUri = new URI(s);
+ if (parsedUri.scheme() != "taler") {
+ return undefined;
+ }
+ if (parsedUri.authority() != "refund") {
+ return undefined;
+ }
+
+ let [
+ _,
+ host,
+ maybePath,
+ maybeInstance,
+ orderId,
+ ] = parsedUri.path().split("/");
+
+ if (!host) {
+ return undefined;
+ }
+
+ if (!maybePath) {
+ return undefined;
+ }
+
+ if (!orderId) {
+ return undefined;
+ }
+
+ if (maybePath === "-") {
+ maybePath = "public/refund";
+ } else {
+ maybePath = decodeURIComponent(maybePath);
+ }
+ if (maybeInstance === "-") {
+ maybeInstance = "default";
+ }
+
+ const refundUrl = new URI(
+ "https://" + host + "/" + decodeURIComponent(maybePath),
+ )
+ .addQuery({ instance: maybeInstance, order_id: orderId })
+ .href();
+
+ return {
+ refundUrl,
+ };
+} \ No newline at end of file
diff --git a/src/wallet.ts b/src/wallet.ts
index fd1be5293..00a82f92c 100644
--- a/src/wallet.ts
+++ b/src/wallet.ts
@@ -109,7 +109,7 @@ import {
AcceptWithdrawalResponse,
} from "./walletTypes";
import { openPromise } from "./promiseUtils";
-import { parsePayUri, parseWithdrawUri, parseTipUri } from "./taleruri";
+import { parsePayUri, parseWithdrawUri, parseTipUri, parseRefundUri } from "./taleruri";
interface SpeculativePayData {
payCoinInfo: PayCoinInfo;
@@ -3109,7 +3109,7 @@ export class Wallet {
}
}
- async acceptRefundResponse(
+ private async acceptRefundResponse(
refundResponse: MerchantRefundResponse,
): Promise<string> {
const refundPermissions = refundResponse.refund_permissions;
@@ -3149,8 +3149,7 @@ export class Wallet {
.finish();
this.notifier.notify();
- // Start submitting it but don't wait for it here.
- this.submitRefunds(hc);
+ await this.submitRefunds(hc);
return hc;
}
@@ -3159,7 +3158,15 @@ export class Wallet {
* Accept a refund, return the contract hash for the contract
* that was involved in the refund.
*/
- async acceptRefund(refundUrl: string): Promise<string> {
+ async applyRefund(talerRefundUri: string): Promise<string> {
+ const parseResult = parseRefundUri(talerRefundUri);
+
+ if (!parseResult) {
+ throw Error("invalid refund URI");
+ }
+
+ const refundUrl = parseResult.refundUrl;
+
Wallet.enableTracing && console.log("processing refund");
let resp;
try {
diff --git a/src/webex/pages/pay.tsx b/src/webex/pages/pay.tsx
index d929426c4..1561dd95f 100644
--- a/src/webex/pages/pay.tsx
+++ b/src/webex/pages/pay.tsx
@@ -30,7 +30,7 @@ import { ExchangeRecord, ProposalDownloadRecord } from "../../dbTypes";
import { ContractTerms } from "../../talerTypes";
import { CheckPayResult, PreparePayResult } from "../../walletTypes";
-import { renderAmount } from "../renderHtml";
+import { renderAmount, ProgressButton } from "../renderHtml";
import * as wxApi from "../wxApi";
import React, { useState, useEffect } from "react";
@@ -44,6 +44,7 @@ function TalerPayDialog({ talerPayUri }: { talerPayUri: string }) {
const [payStatus, setPayStatus] = useState<PreparePayResult | undefined>();
const [payErrMsg, setPayErrMsg] = useState<string | undefined>("");
const [numTries, setNumTries] = useState(0);
+ const [loading, setLoading] = useState(false);
let totalFees: Amounts.AmountJson | undefined = undefined;
useEffect(() => {
@@ -99,6 +100,7 @@ function TalerPayDialog({ talerPayUri }: { talerPayUri: string }) {
const doPayment = async () => {
setNumTries(numTries + 1);
try {
+ setLoading(true);
const res = await wxApi.confirmPay(payStatus!.proposalId!, undefined);
document.location.href = res.nextUrl;
} catch (e) {
@@ -140,12 +142,11 @@ function TalerPayDialog({ talerPayUri }: { talerPayUri: string }) {
</div>
) : (
<div>
- <button
- className="pure-button button-success"
- onClick={() => doPayment()}
- >
+ <ProgressButton
+ loading={loading}
+ onClick={() => doPayment()}>
{i18n.str`Confirm payment`}
- </button>
+ </ProgressButton>
</div>
)}
</div>
diff --git a/src/webex/pages/refund.tsx b/src/webex/pages/refund.tsx
index 6bc1a136e..57d740486 100644
--- a/src/webex/pages/refund.tsx
+++ b/src/webex/pages/refund.tsx
@@ -188,8 +188,12 @@ async function main() {
return;
}
- const contractTermsHash = query.contractTermsHash;
- const refundUrl = query.refundUrl;
+ const talerRefundUri = query.talerRefundUri;
+ if (!talerRefundUri) {
+ console.error("taler refund URI requred");
+ return;
+ }
+
ReactDOM.render(<RefundStatusView contractTermsHash={contractTermsHash} refundUrl={refundUrl} />, container);
}
diff --git a/src/webex/pages/tip.tsx b/src/webex/pages/tip.tsx
index a3f5c38c3..0a066053b 100644
--- a/src/webex/pages/tip.tsx
+++ b/src/webex/pages/tip.tsx
@@ -29,35 +29,12 @@ import * as i18n from "../../i18n";
import { acceptTip, getReserveCreationInfo, getTipStatus } from "../wxApi";
-import { WithdrawDetailView, renderAmount } from "../renderHtml";
+import { WithdrawDetailView, renderAmount, ProgressButton } from "../renderHtml";
import * as Amounts from "../../amounts";
import { useState, useEffect } from "react";
import { TipStatus } from "../../walletTypes";
-interface LoadingButtonProps {
- loading: boolean;
-}
-
-function LoadingButton(
- props:
- & React.PropsWithChildren<LoadingButtonProps>
- & React.DetailedHTMLProps<
- React.ButtonHTMLAttributes<HTMLButtonElement>,
- HTMLButtonElement
- >,
-) {
- return (
- <button
- className="pure-button pure-button-primary"
- type="button"
- {...props}
- >
- {props.loading ? <span><object className="svg-icon svg-baseline" data="/img/spinner-bars.svg" /></span> : null}
- {props.children}
- </button>
- );
-}
function TipDisplay(props: { talerTipUri: string }) {
const [tipStatus, setTipStatus] = useState<TipStatus | undefined>(undefined);
@@ -110,9 +87,9 @@ function TipDisplay(props: { talerTipUri: string }) {
operation.
</p>
<form className="pure-form">
- <LoadingButton loading={loading} onClick={() => accept()}>
+ <ProgressButton loading={loading} onClick={() => accept()}>
AcceptTip
- </LoadingButton>
+ </ProgressButton>
{" "}
<button className="pure-button" type="button" onClick={() => discard()}>
Discard tip
diff --git a/src/webex/renderHtml.tsx b/src/webex/renderHtml.tsx
index e4686adee..867fb440f 100644
--- a/src/webex/renderHtml.tsx
+++ b/src/webex/renderHtml.tsx
@@ -316,3 +316,28 @@ export class ExpanderText extends ImplicitStateComponent<ExpanderTextProps> {
}
}
+
+export interface LoadingButtonProps {
+ loading: boolean;
+}
+
+export function ProgressButton(
+ props:
+ & React.PropsWithChildren<LoadingButtonProps>
+ & React.DetailedHTMLProps<
+ React.ButtonHTMLAttributes<HTMLButtonElement>,
+ HTMLButtonElement
+ >,
+) {
+ return (
+ <button
+ className="pure-button pure-button-primary"
+ type="button"
+ {...props}
+ >
+ {props.loading ? <span><object className="svg-icon svg-baseline" data="/img/spinner-bars.svg" /></span> : null}
+ {" "}
+ {props.children}
+ </button>
+ );
+} \ No newline at end of file
diff --git a/src/webex/wxBackend.ts b/src/webex/wxBackend.ts
index 5bff4fe0a..70a7557e3 100644
--- a/src/webex/wxBackend.ts
+++ b/src/webex/wxBackend.ts
@@ -292,7 +292,7 @@ function handleMessage(
case "get-full-refund-fees":
return needsWallet().getFullRefundFees(detail.refundPermissions);
case "accept-refund":
- return needsWallet().acceptRefund(detail.refundUrl);
+ return needsWallet().applyRefund(detail.refundUrl);
case "get-tip-status": {
return needsWallet().getTipStatus(detail.talerTipUri);
}