aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFlorian Dold <florian.dold@gmail.com>2020-08-20 11:59:06 +0530
committerFlorian Dold <florian.dold@gmail.com>2020-08-20 11:59:06 +0530
commit57000c22141afde7eb7b13ccbd16f06d6b6eb5cd (patch)
treefc0de2a8a01bdc637325ec735e7a8f68e856532c
parent7ff93d8ef64d8ae832c2267192ce1f97bf914776 (diff)
add auto-refund test case, fix bug detected by it
-rw-r--r--packages/taler-integrationtests/src/test-refund-auto.ts99
-rw-r--r--packages/taler-wallet-core/src/operations/refresh.ts9
-rw-r--r--packages/taler-wallet-core/src/operations/refund.ts4
-rw-r--r--packages/taler-wallet-core/src/operations/transactions.ts2
-rw-r--r--packages/taler-wallet-webextension/src/pages/popup.tsx39
-rw-r--r--packages/taler-wallet-webextension/src/wxApi.ts8
6 files changed, 155 insertions, 6 deletions
diff --git a/packages/taler-integrationtests/src/test-refund-auto.ts b/packages/taler-integrationtests/src/test-refund-auto.ts
new file mode 100644
index 000000000..ed80c1c3f
--- /dev/null
+++ b/packages/taler-integrationtests/src/test-refund-auto.ts
@@ -0,0 +1,99 @@
+/*
+ This file is part of GNU Taler
+ (C) 2020 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/>
+ */
+
+/**
+ * Imports.
+ */
+import { runTest, GlobalTestState, MerchantPrivateApi } from "./harness";
+import { createSimpleTestkudosEnvironment, withdrawViaBank } from "./helpers";
+import { CoreApiResponse } from "taler-wallet-core";
+
+/**
+ * Run test for basic, bank-integrated withdrawal.
+ */
+runTest(async (t: GlobalTestState) => {
+ // Set up test environment
+
+ const {
+ wallet,
+ bank,
+ exchange,
+ merchant,
+ } = await createSimpleTestkudosEnvironment(t);
+
+ // Withdraw digital cash into the wallet.
+
+ await withdrawViaBank(t, { wallet, bank, exchange, amount: "TESTKUDOS:20" });
+
+ // Set up order.
+ const orderResp = await MerchantPrivateApi.createOrder(merchant, "default", {
+ order: {
+ summary: "Buy me!",
+ amount: "TESTKUDOS:5",
+ fulfillment_url: "taler://fulfillment-success/thx",
+ auto_refund: {
+ d_ms: 3000,
+ }
+ },
+ });
+
+ let orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus(merchant, {
+ orderId: orderResp.order_id,
+ });
+
+ t.assertTrue(orderStatus.order_status === "unpaid");
+
+ // Make wallet pay for the order
+
+ const r1 = await wallet.apiRequest("preparePay", {
+ talerPayUri: orderStatus.taler_pay_uri,
+ });
+ t.assertTrue(r1.type === "response");
+
+ const r2 = await wallet.apiRequest("confirmPay", {
+ // FIXME: should be validated, don't cast!
+ proposalId: (r1.result as any).proposalId,
+ });
+ t.assertTrue(r2.type === "response");
+
+ // Check if payment was successful.
+
+ orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus(merchant, {
+ orderId: orderResp.order_id,
+ });
+
+ t.assertTrue(orderStatus.order_status === "paid");
+
+ const ref = await MerchantPrivateApi.giveRefund(merchant, {
+ amount: "TESTKUDOS:5",
+ instance: "default",
+ justification: "foo",
+ orderId: orderResp.order_id,
+ });
+
+ console.log(ref);
+
+ // The wallet should now automatically pick up the refund.
+ await wallet.runUntilDone();
+
+ const transactions = await wallet.getTransactions()
+ console.log(JSON.stringify(transactions, undefined, 2));
+
+ const transactionTypes = transactions.transactions.map((x) => x.type);
+ t.assertDeepEqual(transactionTypes, ["withdrawal", "payment", "refund"]);
+
+ await t.shutdown();
+});
diff --git a/packages/taler-wallet-core/src/operations/refresh.ts b/packages/taler-wallet-core/src/operations/refresh.ts
index 409ae58cc..430675328 100644
--- a/packages/taler-wallet-core/src/operations/refresh.ts
+++ b/packages/taler-wallet-core/src/operations/refresh.ts
@@ -571,10 +571,19 @@ export async function createRefreshGroup(
retryInfo: initRetryInfo(),
};
+ if (oldCoinPubs.length == 0) {
+ logger.warn("created refresh group with zero coins");
+ refreshGroup.timestampFinished = getTimestampNow();
+ }
+
await tx.put(Stores.refreshGroups, refreshGroup);
logger.trace(`created refresh group ${refreshGroupId}`);
+ processRefreshGroup(ws, refreshGroupId).catch((e) => {
+ logger.warn(`processing refresh group ${refreshGroupId} failed`);
+ });
+
return {
refreshGroupId,
};
diff --git a/packages/taler-wallet-core/src/operations/refund.ts b/packages/taler-wallet-core/src/operations/refund.ts
index e9324712e..0d6b9ec86 100644
--- a/packages/taler-wallet-core/src/operations/refund.ts
+++ b/packages/taler-wallet-core/src/operations/refund.ts
@@ -259,7 +259,9 @@ async function acceptRefunds(
}
const refreshCoinsPubs = Object.values(refreshCoinsMap);
- await createRefreshGroup(ws, tx, refreshCoinsPubs, RefreshReason.Refund);
+ if (refreshCoinsPubs.length > 0) {
+ await createRefreshGroup(ws, tx, refreshCoinsPubs, RefreshReason.Refund);
+ }
// Are we done with querying yet, or do we need to do another round
// after a retry delay?
diff --git a/packages/taler-wallet-core/src/operations/transactions.ts b/packages/taler-wallet-core/src/operations/transactions.ts
index e17dfac3a..8d0558dbd 100644
--- a/packages/taler-wallet-core/src/operations/transactions.ts
+++ b/packages/taler-wallet-core/src/operations/transactions.ts
@@ -265,7 +265,7 @@ export async function getTransactions(
refundGroupKeys.forEach((groupKey: string) => {
const refundTransactionId = makeEventId(
- TransactionType.Payment,
+ TransactionType.Refund,
pr.proposalId,
groupKey,
);
diff --git a/packages/taler-wallet-webextension/src/pages/popup.tsx b/packages/taler-wallet-webextension/src/pages/popup.tsx
index e833f5950..d7cdd548e 100644
--- a/packages/taler-wallet-webextension/src/pages/popup.tsx
+++ b/packages/taler-wallet-webextension/src/pages/popup.tsx
@@ -33,9 +33,11 @@ import {
Balance,
classifyTalerUri,
TalerUriType,
+ TransactionsResponse,
+ Transaction,
+ TransactionType,
} from "taler-wallet-core";
-
import { abbrev, renderAmount, PageLink } from "../renderHtml";
import * as wxApi from "../wxApi";
@@ -309,9 +311,37 @@ function formatAndCapitalize(text: string): string {
return text;
}
-const HistoryComponent = (props: any): JSX.Element => {
- return <span>TBD</span>;
-};
+function TransactionItem(props: { tx: Transaction }): JSX.Element {
+ const tx = props.tx;
+ return <pre>{JSON.stringify(tx)}</pre>
+}
+
+function WalletHistory(props: any): JSX.Element {
+ const [transactions, setTransactions] = useState<
+ TransactionsResponse | undefined
+ >();
+
+ useEffect(() => {
+ const fetchData = async (): Promise<void> => {
+ const res = await wxApi.getTransactions();
+ setTransactions(res);
+ };
+ fetchData();
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, []);
+
+ if (!transactions) {
+ return <div>Loading ...</div>;
+ }
+
+ return (
+ <div>
+ {transactions.transactions.map((tx) => (
+ <TransactionItem tx={tx} />
+ ))}
+ </div>
+ );
+}
class WalletSettings extends React.Component<any, any> {
render(): JSX.Element {
@@ -492,6 +522,7 @@ function WalletPopup(): JSX.Element {
<WalletBalanceView route="/balance" default />
<WalletSettings route="/settings" />
<WalletDebug route="/debug" />
+ <WalletHistory route="/history" />
</Router>
</div>
</div>
diff --git a/packages/taler-wallet-webextension/src/wxApi.ts b/packages/taler-wallet-webextension/src/wxApi.ts
index dc78686ad..455d0b8ae 100644
--- a/packages/taler-wallet-webextension/src/wxApi.ts
+++ b/packages/taler-wallet-webextension/src/wxApi.ts
@@ -35,6 +35,7 @@ import {
OperationFailedError,
GetWithdrawalDetailsForUriRequest,
WithdrawUriInfoResponse,
+ TransactionsResponse,
} from "taler-wallet-core";
export interface ExtendedPermissionsResponse {
@@ -123,6 +124,13 @@ export function getBalance(): Promise<BalancesResponse> {
}
/**
+ * Get balances for all currencies/exchanges.
+ */
+export function getTransactions(): Promise<TransactionsResponse> {
+ return callBackend("getTransactions", {});
+}
+
+/**
* Return coins to a bank account.
*/
export function returnCoins(args: {