diff --git a/packages/taler-util/src/walletTypes.ts b/packages/taler-util/src/walletTypes.ts index e094bc385..c6367f8ee 100644 --- a/packages/taler-util/src/walletTypes.ts +++ b/packages/taler-util/src/walletTypes.ts @@ -276,6 +276,18 @@ export class ReturnCoinsRequest { static checked: (obj: any) => ReturnCoinsRequest; } +export interface PrepareRefundResult { + proposalId: string; + + applied: number; + failed: number; + total: number; + + amountEffectivePaid: AmountString; + + info: OrderShortInfo; +} + export interface PrepareTipResult { /** * Unique ID for the tip assigned by the wallet. @@ -1003,6 +1015,17 @@ export const codecForForceRefreshRequest = (): Codec => .property("coinPubList", codecForList(codecForString())) .build("ForceRefreshRequest"); + + +export interface PrepareRefundRequest { + talerRefundUri: string; +} + +export const codecForPrepareRefundRequest = (): Codec => + buildCodecForObject() + .property("talerRefundUri", codecForString()) + .build("PrepareRefundRequest"); + export interface PrepareTipRequest { talerTipUri: string; } diff --git a/packages/taler-wallet-core/src/operations/refund.ts b/packages/taler-wallet-core/src/operations/refund.ts index 7ef8076f0..dad8c6001 100644 --- a/packages/taler-wallet-core/src/operations/refund.ts +++ b/packages/taler-wallet-core/src/operations/refund.ts @@ -46,6 +46,8 @@ import { AbsoluteTime, TalerProtocolTimestamp, Duration, + PrepareRefundRequest, + PrepareRefundResult, } from "@gnu-taler/taler-util"; import { AbortStatus, @@ -69,6 +71,72 @@ import { guardOperationException } from "./common.js"; const logger = new Logger("refund.ts"); + +export async function prepareRefund( + ws: InternalWalletState, + talerRefundUri: string, +): Promise { + const parseResult = parseRefundUri(talerRefundUri); + + logger.trace("preparing refund offer", parseResult); + + if (!parseResult) { + throw Error("invalid refund URI"); + } + + const purchase = await ws.db + .mktx((x) => ({ + purchases: x.purchases, + })) + .runReadOnly(async (tx) => { + return tx.purchases.indexes.byMerchantUrlAndOrderId.get([ + parseResult.merchantBaseUrl, + parseResult.orderId, + ]); + }); + + if (!purchase) { + throw Error( + `no purchase for the taler://refund/ URI (${talerRefundUri}) was found`, + ); + } + + const proposalId = purchase.proposalId; + const rfs = Object.values(purchase.refunds) + + let applied = 0; + let failed = 0; + const total = rfs.length; + rfs.forEach((refund) => { + if (refund.type === RefundState.Failed) { + failed = failed + 1; + } + if (refund.type === RefundState.Applied) { + applied = applied + 1; + } + }); + + const { contractData: c } = purchase.download + + return { + proposalId, + amountEffectivePaid: Amounts.stringify(purchase.totalPayCost), + applied, + failed, + total, + info: { + contractTermsHash: c.contractTermsHash, + merchant: c.merchant, + orderId: c.orderId, + products: c.products, + summary: c.summary, + fulfillmentMessage: c.fulfillmentMessage, + summary_i18n: c.summaryI18n, + fulfillmentMessage_i18n: + c.fulfillmentMessageI18n, + }, + } +} /** * Retry querying and applying refunds for an order later. */ diff --git a/packages/taler-wallet-core/src/wallet.ts b/packages/taler-wallet-core/src/wallet.ts index 96722aefb..7760c0bec 100644 --- a/packages/taler-wallet-core/src/wallet.ts +++ b/packages/taler-wallet-core/src/wallet.ts @@ -23,9 +23,7 @@ * Imports. */ import { - AcceptManualWithdrawalResult, - AcceptWithdrawalResponse, - AmountJson, + AbsoluteTime, AcceptManualWithdrawalResult, AmountJson, Amounts, BalancesResponse, codecForAbortPayWithRefundRequest, @@ -48,8 +46,7 @@ import { codecForImportDbRequest, codecForIntegrationTestArgs, codecForListKnownBankAccounts, - codecForPreparePayRequest, - codecForPrepareTipRequest, + codecForPreparePayRequest, codecForPrepareRefundRequest, codecForPrepareTipRequest, codecForRetryTransactionRequest, codecForSetCoinSuspendedRequest, codecForSetWalletDeviceIdRequest, @@ -59,8 +56,7 @@ import { codecForWithdrawFakebankRequest, codecForWithdrawTestBalance, CoinDumpJson, - CoreApiResponse, - durationFromSpec, + CoreApiResponse, Duration, durationFromSpec, durationMin, ExchangeListItem, ExchangesListRespose, @@ -73,14 +69,23 @@ import { parsePaytoUri, PaytoUri, RefreshReason, - TalerErrorCode, - AbsoluteTime, - URL, - WalletNotification, - Duration, - CancellationToken, + TalerErrorCode, URL, + WalletNotification } from "@gnu-taler/taler-util"; -import { timeStamp } from "console"; +import { TalerCryptoInterface } from "./crypto/cryptoImplementation.js"; +import { + CryptoDispatcher, + CryptoWorkerFactory +} from "./crypto/workers/cryptoDispatcher.js"; +import { + AuditorTrustRecord, + CoinSourceType, + exportDb, + importDb, + ReserveRecordStatus, + WalletStoresV1 +} from "./db.js"; +import { getErrorDetailFromException, TalerError } from "./errors.js"; import { DenomInfo, ExchangeOperations, @@ -89,21 +94,8 @@ import { MerchantOperations, NotificationListener, RecoupOperations, - ReserveOperations, + ReserveOperations } from "./internal-wallet-state.js"; -import { - CryptoDispatcher, - CryptoWorkerFactory, -} from "./crypto/workers/cryptoDispatcher.js"; -import { - AuditorTrustRecord, - CoinSourceType, - exportDb, - importDb, - ReserveRecordStatus, - WalletStoresV1, -} from "./db.js"; -import { getErrorDetailFromException, TalerError } from "./errors.js"; import { exportBackup } from "./operations/backup/export.js"; import { addBackupProvider, @@ -115,7 +107,7 @@ import { loadBackupRecovery, processBackupForProvider, removeBackupProvider, - runBackupCycle, + runBackupCycle } from "./operations/backup/index.js"; import { setWalletDeviceId } from "./operations/backup/state.js"; import { getBalances } from "./operations/balance.js"; @@ -123,7 +115,7 @@ import { createDepositGroup, getFeeForDeposit, processDepositGroup, - trackDepositGroup, + trackDepositGroup } from "./operations/deposits.js"; import { acceptExchangeTermsOfService, @@ -132,69 +124,69 @@ import { getExchangeRequestTimeout, getExchangeTrust, updateExchangeFromUrl, - updateExchangeTermsOfService, + updateExchangeTermsOfService } from "./operations/exchanges.js"; import { getMerchantInfo } from "./operations/merchants.js"; import { confirmPay, preparePayForUri, processDownloadProposal, - processPurchasePay, + processPurchasePay } from "./operations/pay.js"; import { getPendingOperations } from "./operations/pending.js"; import { createRecoupGroup, processRecoupGroup } from "./operations/recoup.js"; import { autoRefresh, createRefreshGroup, - processRefreshGroup, + processRefreshGroup } from "./operations/refresh.js"; import { abortFailedPayWithRefund, applyRefund, - processPurchaseQueryRefund, + prepareRefund, + processPurchaseQueryRefund } from "./operations/refund.js"; import { createReserve, createTalerWithdrawReserve, getFundingPaytoUris, - processReserve, + processReserve } from "./operations/reserves.js"; import { runIntegrationTest, testPay, - withdrawTestBalance, + withdrawTestBalance } from "./operations/testing.js"; import { acceptTip, prepareTip, processTip } from "./operations/tip.js"; import { deleteTransaction, getTransactions, - retryTransaction, + retryTransaction } from "./operations/transactions.js"; import { getExchangeWithdrawalInfo, getWithdrawalDetailsForUri, - processWithdrawGroup, + processWithdrawGroup } from "./operations/withdraw.js"; import { PendingOperationsResponse, PendingTaskInfo, - PendingTaskType, + PendingTaskType } from "./pending-types.js"; import { assertUnreachable } from "./util/assertUnreachable.js"; import { AsyncOpMemoMap, AsyncOpMemoSingle } from "./util/asyncMemo.js"; import { HttpRequestLibrary, - readSuccessResponseJsonOrThrow, + readSuccessResponseJsonOrThrow } from "./util/http.js"; import { AsyncCondition, OpenedPromise, - openPromise, + openPromise } from "./util/promiseUtils.js"; import { DbAccess, GetReadWriteAccess } from "./util/query.js"; import { TimerAPI, TimerGroup } from "./util/timer.js"; import { WalletCoreApiClient } from "./wallet-api-types.js"; -import { TalerCryptoInterface } from "./crypto/cryptoImplementation.js"; const builtinAuditors: AuditorTrustRecord[] = [ { @@ -908,6 +900,10 @@ async function dispatchRequestInternal( const req = codecForPrepareTipRequest().decode(payload); return await prepareTip(ws, req.talerTipUri); } + case "prepareRefund": { + const req = codecForPrepareRefundRequest().decode(payload); + return await prepareRefund(ws, req.talerRefundUri); + } case "acceptTip": { const req = codecForAcceptTipRequest().decode(payload); await acceptTip(ws, req.walletTipId);