wallet-core: expose more info about refund query

This commit is contained in:
Florian Dold 2023-02-14 11:16:58 +01:00
parent 6106caeba9
commit 6a4da88719
No known key found for this signature in database
GPG Key ID: D2E4F00F29D02A4B
6 changed files with 109 additions and 12 deletions

View File

@ -29,6 +29,8 @@ import {
MerchantContractTerms, MerchantContractTerms,
Duration, Duration,
PreparePayResultType, PreparePayResultType,
NotificationType,
WithdrawalGroupFinishedNotification,
} from "@gnu-taler/taler-util"; } from "@gnu-taler/taler-util";
import { import {
BankAccessApi, BankAccessApi,
@ -466,6 +468,53 @@ export async function startWithdrawViaBank(
// Some tests rely on the final withdraw failing. // Some tests rely on the final withdraw failing.
} }
export interface WithdrawViaBankResult {
withdrawalFinishedCond: Promise<WithdrawalGroupFinishedNotification>;
}
export async function withdrawViaBankV2(
t: GlobalTestState,
p: {
walletClient: WalletClient;
bank: BankService;
exchange: ExchangeServiceInterface;
amount: AmountString;
restrictAge?: number;
},
): Promise<WithdrawViaBankResult> {
const { walletClient: wallet, bank, exchange, amount } = p;
const user = await BankApi.createRandomBankUser(bank);
const wop = await BankAccessApi.createWithdrawalOperation(bank, user, amount);
// Hand it to the wallet
await wallet.client.call(WalletApiOperation.GetWithdrawalDetailsForUri, {
talerWithdrawUri: wop.taler_withdraw_uri,
restrictAge: p.restrictAge,
});
const withdrawalFinishedCond = wallet.waitForNotificationCond((x) =>
x.type === NotificationType.WithdrawGroupFinished ? x : false,
);
// Withdraw (AKA select)
await wallet.client.call(WalletApiOperation.AcceptBankIntegratedWithdrawal, {
exchangeBaseUrl: exchange.baseUrl,
talerWithdrawUri: wop.taler_withdraw_uri,
restrictAge: p.restrictAge,
});
// Confirm it
await BankApi.confirmWithdrawalOperation(bank, user, wop);
return {
withdrawalFinishedCond,
};
}
/** /**
* Withdraw balance. * Withdraw balance.
*/ */

View File

@ -17,12 +17,16 @@
/** /**
* Imports. * Imports.
*/ */
import { Duration, durationFromSpec } from "@gnu-taler/taler-util"; import {
Duration,
durationFromSpec,
NotificationType,
} from "@gnu-taler/taler-util";
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
import { GlobalTestState, MerchantPrivateApi } from "../harness/harness.js"; import { GlobalTestState, MerchantPrivateApi } from "../harness/harness.js";
import { import {
createSimpleTestkudosEnvironment, createSimpleTestkudosEnvironmentV2,
withdrawViaBank, withdrawViaBankV2,
} from "../harness/helpers.js"; } from "../harness/helpers.js";
/** /**
@ -31,12 +35,23 @@ import {
export async function runRefundTest(t: GlobalTestState) { export async function runRefundTest(t: GlobalTestState) {
// Set up test environment // Set up test environment
const { wallet, bank, exchange, merchant } = const {
await createSimpleTestkudosEnvironment(t); walletClient: wallet,
bank,
exchange,
merchant,
} = await createSimpleTestkudosEnvironmentV2(t);
// Withdraw digital cash into the wallet. // Withdraw digital cash into the wallet.
await withdrawViaBank(t, { wallet, bank, exchange, amount: "TESTKUDOS:20" }); const withdrawalRes = await withdrawViaBankV2(t, {
walletClient: wallet,
bank,
exchange,
amount: "TESTKUDOS:20",
});
await withdrawalRes.withdrawalFinishedCond;
// Set up order. // Set up order.
@ -85,12 +100,15 @@ export async function runRefundTest(t: GlobalTestState) {
console.log(ref); console.log(ref);
{ {
const refundFinishedCond = wallet.waitForNotificationCond(
(x) => x.type === NotificationType.RefundFinished,
);
const r = await wallet.client.call(WalletApiOperation.ApplyRefund, { const r = await wallet.client.call(WalletApiOperation.ApplyRefund, {
talerRefundUri: ref.talerRefundUri, talerRefundUri: ref.talerRefundUri,
}); });
console.log(r); console.log(r);
await wallet.runUntilDone(); await refundFinishedCond;
} }
{ {
@ -103,6 +121,9 @@ export async function runRefundTest(t: GlobalTestState) {
} }
{ {
const refundQueriedCond = wallet.waitForNotificationCond(
(x) => x.type === NotificationType.RefundQueried,
);
const r3 = await wallet.client.call( const r3 = await wallet.client.call(
WalletApiOperation.ApplyRefundFromPurchaseId, WalletApiOperation.ApplyRefundFromPurchaseId,
{ {
@ -110,11 +131,8 @@ export async function runRefundTest(t: GlobalTestState) {
}, },
); );
console.log(r3); console.log(r3);
await refundQueriedCond;
await wallet.runUntilDone();
} }
await t.shutdown();
} }
runRefundTest.suites = ["wallet"]; runRefundTest.suites = ["wallet"];

View File

@ -96,6 +96,10 @@ export interface RefundStartedNotification {
export interface RefundQueriedNotification { export interface RefundQueriedNotification {
type: NotificationType.RefundQueried; type: NotificationType.RefundQueried;
/**
* Transaction ID of the purchase (NOT the refund transaction).
*/
transactionId: string;
} }
export interface ProposalDownloadedNotification { export interface ProposalDownloadedNotification {
@ -171,6 +175,11 @@ export interface WaitingForRetryNotification {
export interface RefundFinishedNotification { export interface RefundFinishedNotification {
type: NotificationType.RefundFinished; type: NotificationType.RefundFinished;
/**
* Transaction ID of the purchase (NOT the refund transaction).
*/
transactionId: string;
} }
export interface ExchangeAddedNotification { export interface ExchangeAddedNotification {

View File

@ -395,6 +395,11 @@ export interface TransactionPayment extends TransactionCommon {
* Reference to applied refunds * Reference to applied refunds
*/ */
refunds: RefundInfoShort[]; refunds: RefundInfoShort[];
/**
* Is the wallet currently checking for a refund?
*/
refundQueryActive: boolean;
} }
export interface OrderShortInfo { export interface OrderShortInfo {

View File

@ -2415,6 +2415,13 @@ async function acceptRefunds(
p.purchaseStatus = PurchaseStatus.Paid; p.purchaseStatus = PurchaseStatus.Paid;
} }
logger.trace("refund query done"); logger.trace("refund query done");
ws.notify({
type: NotificationType.RefundFinished,
transactionId: makeTransactionId(
TransactionType.Payment,
p.proposalId,
),
});
} else { } else {
// No error, but we need to try again! // No error, but we need to try again!
p.timestampLastRefundStatus = now; p.timestampLastRefundStatus = now;
@ -2426,6 +2433,7 @@ async function acceptRefunds(
ws.notify({ ws.notify({
type: NotificationType.RefundQueried, type: NotificationType.RefundQueried,
transactionId: makeTransactionId(TransactionType.Payment, proposalId),
}); });
} }
@ -2694,6 +2702,13 @@ export async function processPurchaseQueryRefund(
await tx.purchases.put(purchase); await tx.purchases.put(purchase);
}); });
// No new refunds, but we still need to notify
// the wallet client that the query finished.
ws.notify({
type: NotificationType.RefundQueried,
transactionId: makeTransactionId(TransactionType.Payment, proposalId),
});
return OperationAttemptResult.finishedEmpty(); return OperationAttemptResult.finishedEmpty();
} }
} }

View File

@ -58,7 +58,6 @@ import {
WithdrawalGroupStatus, WithdrawalGroupStatus,
} from "../db.js"; } from "../db.js";
import { InternalWalletState } from "../internal-wallet-state.js"; import { InternalWalletState } from "../internal-wallet-state.js";
import { assertUnreachable } from "../util/assertUnreachable.js";
import { checkDbInvariant } from "../util/invariants.js"; import { checkDbInvariant } from "../util/invariants.js";
import { RetryTags } from "../util/retries.js"; import { RetryTags } from "../util/retries.js";
import { import {
@ -846,6 +845,8 @@ async function buildTransactionForPurchase(
), ),
proposalId: purchaseRecord.proposalId, proposalId: purchaseRecord.proposalId,
info, info,
refundQueryActive:
purchaseRecord.purchaseStatus === PurchaseStatus.QueryingRefund,
frozen: frozen:
purchaseRecord.purchaseStatus === PurchaseStatus.PaymentAbortFinished ?? purchaseRecord.purchaseStatus === PurchaseStatus.PaymentAbortFinished ??
false, false,