aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSebastian <sebasjm@gmail.com>2022-05-29 01:23:15 -0300
committerSebastian <sebasjm@gmail.com>2022-05-29 01:23:21 -0300
commit029340469ac661bab1450b1ff4dcc6e717780e5b (patch)
tree0e4495cbc332ed5ed2059b8ba12de9be6c2dc98e
parentced08c502fc36ef90bb34fafcb1e2cbfe82f1280 (diff)
downloadwallet-core-029340469ac661bab1450b1ff4dcc6e717780e5b.tar.xz
show refund info in purchase
notify refund pending with accept button on the purchase details better payto box
-rw-r--r--packages/taler-util/src/transactionsTypes.ts12
-rw-r--r--packages/taler-util/src/walletTypes.ts9
-rw-r--r--packages/taler-wallet-core/src/operations/refund.ts14
-rw-r--r--packages/taler-wallet-core/src/operations/transactions.ts13
-rw-r--r--packages/taler-wallet-core/src/wallet.ts6
-rw-r--r--packages/taler-wallet-webextension/src/components/Part.tsx29
-rw-r--r--packages/taler-wallet-webextension/src/components/TransactionItem.tsx2
-rw-r--r--packages/taler-wallet-webextension/src/wallet/History.stories.tsx1
-rw-r--r--packages/taler-wallet-webextension/src/wallet/History.tsx2
-rw-r--r--packages/taler-wallet-webextension/src/wallet/ReserveCreated.stories.tsx2
-rw-r--r--packages/taler-wallet-webextension/src/wallet/Transaction.stories.tsx30
-rw-r--r--packages/taler-wallet-webextension/src/wallet/Transaction.tsx78
-rw-r--r--packages/taler-wallet-webextension/src/wxApi.ts9
13 files changed, 183 insertions, 24 deletions
diff --git a/packages/taler-util/src/transactionsTypes.ts b/packages/taler-util/src/transactionsTypes.ts
index dcaa56675..f7383f902 100644
--- a/packages/taler-util/src/transactionsTypes.ts
+++ b/packages/taler-util/src/transactionsTypes.ts
@@ -244,6 +244,11 @@ export interface TransactionPayment extends TransactionCommon {
* Amount pending to be picked up
*/
refundPending: AmountString | undefined;
+
+ /**
+ * Reference to applied refunds
+ */
+ refunds: RefundInfoShort[];
}
export interface OrderShortInfo {
@@ -305,6 +310,13 @@ export interface OrderShortInfo {
fulfillmentMessage_i18n?: InternationalizedString;
}
+export interface RefundInfoShort {
+ transactionId: string,
+ timestamp: TalerProtocolTimestamp,
+ amountEffective: AmountString,
+ amountRaw: AmountString,
+}
+
export interface TransactionRefund extends TransactionCommon {
type: TransactionType.Refund;
diff --git a/packages/taler-util/src/walletTypes.ts b/packages/taler-util/src/walletTypes.ts
index fa884c414..00a489861 100644
--- a/packages/taler-util/src/walletTypes.ts
+++ b/packages/taler-util/src/walletTypes.ts
@@ -799,6 +799,15 @@ export const codecForApplyRefundRequest = (): Codec<ApplyRefundRequest> =>
.property("talerRefundUri", codecForString())
.build("ApplyRefundRequest");
+export interface ApplyRefundFromPurchaseIdRequest {
+ purchaseId: string;
+}
+
+export const codecForApplyRefundFromPurchaseIdRequest = (): Codec<ApplyRefundFromPurchaseIdRequest> =>
+ buildCodecForObject<ApplyRefundFromPurchaseIdRequest>()
+ .property("purchaseId", codecForString())
+ .build("ApplyRefundFromPurchaseIdRequest");
+
export interface GetWithdrawalDetailsForUriRequest {
talerWithdrawUri: string;
restrictAge?: number;
diff --git a/packages/taler-wallet-core/src/operations/refund.ts b/packages/taler-wallet-core/src/operations/refund.ts
index 186fbf7d3..28a92286b 100644
--- a/packages/taler-wallet-core/src/operations/refund.ts
+++ b/packages/taler-wallet-core/src/operations/refund.ts
@@ -573,7 +573,7 @@ export async function applyRefund(
throw Error("invalid refund URI");
}
- let purchase = await ws.db
+ const purchase = await ws.db
.mktx((x) => ({
purchases: x.purchases,
}))
@@ -590,7 +590,15 @@ export async function applyRefund(
);
}
- const proposalId = purchase.proposalId;
+ return applyRefundFromPurchaseId(ws, purchase.proposalId)
+}
+
+export async function applyRefundFromPurchaseId(
+ ws: InternalWalletState,
+ proposalId: string,
+): Promise<ApplyRefundResponse> {
+
+ logger.trace("applying refund for purchase", proposalId);
logger.info("processing purchase for refund");
const success = await ws.db
@@ -620,7 +628,7 @@ export async function applyRefund(
});
}
- purchase = await ws.db
+ const purchase = await ws.db
.mktx((x) => ({
purchases: x.purchases,
}))
diff --git a/packages/taler-wallet-core/src/operations/transactions.ts b/packages/taler-wallet-core/src/operations/transactions.ts
index 87b109d98..db282bb68 100644
--- a/packages/taler-wallet-core/src/operations/transactions.ts
+++ b/packages/taler-wallet-core/src/operations/transactions.ts
@@ -24,6 +24,7 @@ import {
Logger,
OrderShortInfo,
PaymentStatus,
+ RefundInfoShort,
Transaction,
TransactionsRequest,
TransactionsResponse,
@@ -306,6 +307,7 @@ export async function getTransactions(
let totalRefundRaw = Amounts.getZero(contractData.amount.currency);
let totalRefundEffective = Amounts.getZero(contractData.amount.currency);
+ const refunds: RefundInfoShort[] = []
for (const groupKey of refundGroupKeys.values()) {
const refundTombstoneId = makeEventId(
@@ -345,6 +347,13 @@ export async function getTransactions(
refund.totalRefreshCostBound,
).amount,
).amount;
+
+ refunds.push({
+ transactionId: refundTransactionId,
+ timestamp: r0.obtainedTime,
+ amountEffective: Amounts.stringify(amountEffective),
+ amountRaw: Amounts.stringify(amountRaw),
+ })
}
}
if (!r0) {
@@ -353,7 +362,6 @@ export async function getTransactions(
totalRefundRaw = Amounts.add(totalRefundRaw, amountRaw).amount;
totalRefundEffective = Amounts.add(totalRefundEffective, amountEffective).amount;
-
transactions.push({
type: TransactionType.Refund,
info,
@@ -382,10 +390,11 @@ export async function getTransactions(
pending:
!pr.timestampFirstSuccessfulPay &&
pr.abortStatus === AbortStatus.None,
+ refunds,
timestamp: pr.timestampAccept,
transactionId: paymentTransactionId,
proposalId: pr.proposalId,
- info: info,
+ info,
frozen: pr.payFrozen ?? false,
...(err ? { error: err } : {}),
});
diff --git a/packages/taler-wallet-core/src/wallet.ts b/packages/taler-wallet-core/src/wallet.ts
index ffceec38f..689e45f3c 100644
--- a/packages/taler-wallet-core/src/wallet.ts
+++ b/packages/taler-wallet-core/src/wallet.ts
@@ -33,6 +33,7 @@ import {
codecForAcceptTipRequest,
codecForAddExchangeRequest,
codecForAny,
+ codecForApplyRefundFromPurchaseIdRequest,
codecForApplyRefundRequest,
codecForConfirmPayRequest,
codecForCreateDepositGroupRequest,
@@ -145,6 +146,7 @@ import {
import {
abortFailedPayWithRefund,
applyRefund,
+ applyRefundFromPurchaseId,
prepareRefund,
processPurchaseQueryRefund
} from "./operations/refund.js";
@@ -839,6 +841,10 @@ async function dispatchRequestInternal(
const req = codecForApplyRefundRequest().decode(payload);
return await applyRefund(ws, req.talerRefundUri);
}
+ case "applyRefundFromPurchaseId": {
+ const req = codecForApplyRefundFromPurchaseIdRequest().decode(payload);
+ return await applyRefundFromPurchaseId(ws, req.purchaseId);
+ }
case "acceptBankIntegratedWithdrawal": {
const req =
codecForAcceptBankIntegratedWithdrawalRequest().decode(payload);
diff --git a/packages/taler-wallet-webextension/src/components/Part.tsx b/packages/taler-wallet-webextension/src/components/Part.tsx
index 58165a349..06e2985cf 100644
--- a/packages/taler-wallet-webextension/src/components/Part.tsx
+++ b/packages/taler-wallet-webextension/src/components/Part.tsx
@@ -92,6 +92,7 @@ const CollasibleBox = styled.div`
}
`;
import arrowDown from "../svg/chevron-down.svg";
+import { useTranslationContext } from "../context/translation.js";
export function PartCollapsible({ text, title, big, showSign }: Props): VNode {
const Text = big ? ExtraLargeText : LargeText;
@@ -137,27 +138,37 @@ interface PropsPayto {
}
export function PartPayto({ payto, kind, big }: PropsPayto): VNode {
const Text = big ? ExtraLargeText : LargeText;
- let text: string | undefined = undefined;
+ let text: VNode | undefined = undefined;
let title = "";
+ const { i18n } = useTranslationContext();
if (payto.isKnown) {
if (payto.targetType === "x-taler-bank") {
- text = payto.account;
- title = "Bank account";
+ text = <Fragment>{payto.account}</Fragment>;
+ title = i18n.str`Bank account`;
} else if (payto.targetType === "bitcoin") {
- text = payto.targetPath;
- title = "Bitcoin addr";
+ text =
+ payto.segwitAddrs && payto.segwitAddrs.length > 0 ? (
+ <ul>
+ <li>{payto.targetPath}</li>
+ <li>{payto.segwitAddrs[0]}</li>
+ <li>{payto.segwitAddrs[1]}</li>
+ </ul>
+ ) : (
+ <Fragment>{payto.targetPath}</Fragment>
+ );
+ title = i18n.str`Bitcoin address`;
} else if (payto.targetType === "iban") {
- text = payto.targetPath;
- title = "IBAN";
+ text = <Fragment>{payto.targetPath}</Fragment>;
+ title = i18n.str`IBAN`;
}
}
if (!text) {
- text = stringifyPaytoUri(payto);
+ text = <Fragment>{stringifyPaytoUri(payto)}</Fragment>;
title = "Payto URI";
}
return (
<div style={{ margin: "1em" }}>
- <SmallLightText style={{ margin: ".5em" }}>{title}</SmallLightText>
+ <SmallBoldText>{title}</SmallBoldText>
<Text
style={{
color:
diff --git a/packages/taler-wallet-webextension/src/components/TransactionItem.tsx b/packages/taler-wallet-webextension/src/components/TransactionItem.tsx
index 985ddf552..bfffa3267 100644
--- a/packages/taler-wallet-webextension/src/components/TransactionItem.tsx
+++ b/packages/taler-wallet-webextension/src/components/TransactionItem.tsx
@@ -207,7 +207,7 @@ function TransactionAmount(props: TransactionAmountProps): VNode {
>
<ExtraLargeText>
{sign}
- {Amounts.stringifyValue(props.amount)}
+ {Amounts.stringifyValue(props.amount, 2)}
</ExtraLargeText>
{props.pending && (
<div>
diff --git a/packages/taler-wallet-webextension/src/wallet/History.stories.tsx b/packages/taler-wallet-webextension/src/wallet/History.stories.tsx
index 3080a866e..004327c94 100644
--- a/packages/taler-wallet-webextension/src/wallet/History.stories.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/History.stories.tsx
@@ -78,6 +78,7 @@ const exampleData = {
summary: "the summary",
fulfillmentMessage: "",
},
+ refunds: [],
refundPending: undefined,
totalRefundEffective: "USD:0",
totalRefundRaw: "USD:0",
diff --git a/packages/taler-wallet-webextension/src/wallet/History.tsx b/packages/taler-wallet-webextension/src/wallet/History.tsx
index 4a435d0cf..59f245522 100644
--- a/packages/taler-wallet-webextension/src/wallet/History.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/History.tsx
@@ -193,7 +193,7 @@ export function HistoryView({
margin: 8,
}}
>
- {Amounts.stringifyValue(currencyAmount)}
+ {Amounts.stringifyValue(currencyAmount, 2)}
</CenteredBoldText>
)}
</div>
diff --git a/packages/taler-wallet-webextension/src/wallet/ReserveCreated.stories.tsx b/packages/taler-wallet-webextension/src/wallet/ReserveCreated.stories.tsx
index 587e24e98..895c301c2 100644
--- a/packages/taler-wallet-webextension/src/wallet/ReserveCreated.stories.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/ReserveCreated.stories.tsx
@@ -45,7 +45,7 @@ export const TalerBank = createExample(TestedComponent, {
export const IBAN = createExample(TestedComponent, {
reservePub: "A05AJGMFNSK4Q62NXR2FKNDB1J4EXTYQTE7VA4M9GZQ4TR06YBNG",
paytoURI: parsePaytoUri(
- "payto://iban/ASDQWEASDZXCASDQWE?amount=COL%3A1&message=Taler+Withdrawal+A05AJGMFNSK4Q62NXR2FKNDB1J4EXTYQTE7VA4M9GZQ4TR06YBNG",
+ "payto://iban/ES8877998399652238?amount=COL%3A1&message=Taler+Withdrawal+A05AJGMFNSK4Q62NXR2FKNDB1J4EXTYQTE7VA4M9GZQ4TR06YBNG",
),
amount: {
currency: "USD",
diff --git a/packages/taler-wallet-webextension/src/wallet/Transaction.stories.tsx b/packages/taler-wallet-webextension/src/wallet/Transaction.stories.tsx
index 493cdd1d7..83848d005 100644
--- a/packages/taler-wallet-webextension/src/wallet/Transaction.stories.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/Transaction.stories.tsx
@@ -93,6 +93,7 @@ const exampleData = {
// address_lines: [""],
// },
},
+ refunds: [],
refundPending: undefined,
totalRefundEffective: "KUDOS:0",
totalRefundRaw: "KUDOS:0",
@@ -199,7 +200,7 @@ export const WithdrawPendingManual = createExample(TestedComponent, () => ({
...exampleData.withdraw,
withdrawalDetails: {
type: WithdrawalType.ManualTransfer,
- exchangePaytoUris: ["payto://iban/asdasdasd"],
+ exchangePaytoUris: ["payto://iban/ES8877998399652238"],
reservePub: "A05AJGMFNSK4Q62NXR2FKNDB1J4EXTYQTE7VA4M9GZQ4TR06YBNG",
} as WithdrawalDetails,
pending: true,
@@ -254,6 +255,14 @@ export const PaymentWithRefund = createExample(TestedComponent, {
amountRaw: "KUDOS:12",
totalRefundEffective: "KUDOS:1",
totalRefundRaw: "KUDOS:1",
+ refunds: [
+ {
+ transactionId: "1123123",
+ amountRaw: "KUDOS:1",
+ amountEffective: "KUDOS:1",
+ timestamp: TalerProtocolTimestamp.fromSeconds(1546546544),
+ },
+ ],
},
});
@@ -410,6 +419,25 @@ export const PaymentWithLongSummary = createExample(TestedComponent, {
export const Deposit = createExample(TestedComponent, {
transaction: exampleData.deposit,
});
+export const DepositTalerBank = createExample(TestedComponent, {
+ transaction: {
+ ...exampleData.deposit,
+ targetPaytoUri: "payto://x-taler-bank/bank.demo.taler.net/Exchange",
+ },
+});
+export const DepositBitcoin = createExample(TestedComponent, {
+ transaction: {
+ ...exampleData.deposit,
+ targetPaytoUri:
+ "payto://bitcoin/bcrt1q6ps8qs6v8tkqrnru4xqqqa6rfwcx5ufpdfqht4?amount=BTC:0.1&subject=0ZSX8SH0M30KHX8K3Y1DAMVGDQV82XEF9DG1HC4QMQ3QWYT4AF00",
+ },
+});
+export const DepositIBAN = createExample(TestedComponent, {
+ transaction: {
+ ...exampleData.deposit,
+ targetPaytoUri: "payto://iban/ES8877998399652238",
+ },
+});
export const DepositError = createExample(TestedComponent, {
transaction: {
diff --git a/packages/taler-wallet-webextension/src/wallet/Transaction.tsx b/packages/taler-wallet-webextension/src/wallet/Transaction.tsx
index 9ccb353a9..8165953ab 100644
--- a/packages/taler-wallet-webextension/src/wallet/Transaction.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/Transaction.tsx
@@ -22,6 +22,8 @@ import {
NotificationType,
parsePaytoUri,
parsePayUri,
+ PaytoUri,
+ stringifyPaytoUri,
TalerProtocolTimestamp,
Transaction,
TransactionDeposit,
@@ -50,6 +52,7 @@ import {
ButtonDestructive,
ButtonPrimary,
CenteredDialog,
+ HistoryRow,
InfoBox,
ListOfProducts,
Overlay,
@@ -83,7 +86,7 @@ async function getTransaction(tid: string): Promise<Transaction> {
export function TransactionPage({ tid, goToWalletHistory }: Props): VNode {
const { i18n } = useTranslationContext();
- const state = useAsyncAsHook(() => getTransaction(tid));
+ const state = useAsyncAsHook(() => getTransaction(tid), [tid]);
useEffect(() => {
wxApi.onUpdateNotification([NotificationType.WithdrawGroupFinished], () => {
@@ -119,6 +122,7 @@ export function TransactionPage({ tid, goToWalletHistory }: Props): VNode {
onRetry={() =>
wxApi.retryTransaction(tid).then(() => goToWalletHistory(currency))
}
+ onRefund={(id) => wxApi.applyRefundFromPurchaseId(id)}
onBack={() => goToWalletHistory(currency)}
/>
);
@@ -128,6 +132,7 @@ export interface WalletTransactionProps {
transaction: Transaction;
onDelete: () => void;
onRetry: () => void;
+ onRefund: (id: string) => void;
onBack: () => void;
}
@@ -143,7 +148,7 @@ export function TransactionView({
transaction,
onDelete,
onRetry,
- onBack,
+ onRefund,
}: WalletTransactionProps): VNode {
const [confirmBeforeForget, setConfirmBeforeForget] = useState(false);
@@ -334,6 +339,40 @@ export function TransactionView({
)}
</Header>
<br />
+ {transaction.refunds.length > 0 ? (
+ <Part
+ title={<i18n.Translate>Refunds</i18n.Translate>}
+ text={
+ <table>
+ {transaction.refunds.map((r, i) => {
+ return (
+ <tr key={i}>
+ <td>
+ {<Amount value={r.amountEffective} />}{" "}
+ <a
+ href={Pages.balance_transaction.replace(
+ ":tid",
+ r.transactionId,
+ )}
+ >
+ was refunded
+ </a>{" "}
+ on{" "}
+ {
+ <Time
+ timestamp={AbsoluteTime.fromTimestamp(r.timestamp)}
+ format="dd MMMM yyyy"
+ />
+ }
+ </td>
+ </tr>
+ );
+ })}
+ </table>
+ }
+ kind="neutral"
+ />
+ ) : undefined}
{pendingRefund !== undefined && Amounts.isNonZero(pendingRefund) && (
<InfoBox>
<i18n.Translate>
@@ -348,7 +387,7 @@ export function TransactionView({
<div>
<div />
<div>
- <ButtonPrimary>
+ <ButtonPrimary onClick={() => onRefund(transaction.proposalId)}>
<i18n.Translate>Accept</i18n.Translate>
</ButtonPrimary>
</div>
@@ -385,9 +424,9 @@ export function TransactionView({
total={total}
kind="negative"
>
- {transaction.targetPaytoUri}
+ {!payto ? transaction.targetPaytoUri : <NicePayto payto={payto} />}
</Header>
- {payto && <PartPayto big payto={payto} kind="neutral" />}
+ {payto && <PartPayto payto={payto} kind="neutral" />}
<Part
title={<i18n.Translate>Details</i18n.Translate>}
text={<DepositDetails transaction={transaction} />}
@@ -669,7 +708,7 @@ function PurchaseDetails({
<tr>
<td>Refunded</td>
<td>
- <Amount value={transaction.totalRefundEffective} />
+ <Amount value={transaction.totalRefundRaw} />
</td>
</tr>
)}
@@ -988,3 +1027,30 @@ function Header({
</div>
);
}
+
+function NicePayto({ payto }: { payto: PaytoUri }): VNode {
+ if (payto.isKnown) {
+ switch (payto.targetType) {
+ case "bitcoin": {
+ return <div>{payto.targetPath.substring(0, 20)}...</div>;
+ }
+ case "x-taler-bank": {
+ const url = new URL("/", `https://${payto.host}`);
+ return (
+ <Fragment>
+ <div>{payto.account}</div>
+ <SmallLightText>
+ <a href={url.href} target="_bank" rel="noreferrer">
+ {url.toString()}
+ </a>
+ </SmallLightText>
+ </Fragment>
+ );
+ }
+ case "iban": {
+ return <div>{payto.targetPath.substring(0, 20)}</div>;
+ }
+ }
+ }
+ return <Fragment>{stringifyPaytoUri(payto)}</Fragment>;
+}
diff --git a/packages/taler-wallet-webextension/src/wxApi.ts b/packages/taler-wallet-webextension/src/wxApi.ts
index dd4eb2cf4..63840017b 100644
--- a/packages/taler-wallet-webextension/src/wxApi.ts
+++ b/packages/taler-wallet-webextension/src/wxApi.ts
@@ -312,6 +312,15 @@ export function applyRefund(
}
/**
+ * Do refund for purchase.
+ */
+export function applyRefundFromPurchaseId(
+ purchaseId: string,
+): Promise<ApplyRefundResponse> {
+ return callBackend("applyRefundFromPurchaseId", { purchaseId });
+}
+
+/**
* Get details about a pay operation.
*/
export function preparePay(talerPayUri: string): Promise<PreparePayResult> {