diff --git a/packages/taler-integrationtests/src/test-tipping.ts b/packages/taler-integrationtests/src/test-tipping.ts index f7840f5da..4c080293e 100644 --- a/packages/taler-integrationtests/src/test-tipping.ts +++ b/packages/taler-integrationtests/src/test-tipping.ts @@ -109,4 +109,13 @@ runTest(async (t: GlobalTestState) => { console.log(bal); t.assertAmountEquals(bal.balances[0].available, "TESTKUDOS:4.85"); + + const txns = await wallet.getTransactions(); + + console.log("Transactions:", JSON.stringify(txns, undefined, 2)); + + t.assertDeepEqual(txns.transactions[0].type, "tip"); + t.assertDeepEqual(txns.transactions[0].pending, false); + t.assertAmountEquals(txns.transactions[0].amountEffective, "TESTKUDOS:4.85"); + t.assertAmountEquals(txns.transactions[0].amountRaw, "TESTKUDOS:5.0"); }); diff --git a/packages/taler-wallet-core/src/operations/pending.ts b/packages/taler-wallet-core/src/operations/pending.ts index 7dda1214d..7338ac77d 100644 --- a/packages/taler-wallet-core/src/operations/pending.ts +++ b/packages/taler-wallet-core/src/operations/pending.ts @@ -351,7 +351,7 @@ async function gatherTipPending( onlyDue = false, ): Promise { await tx.iter(Stores.tips).forEach((tip) => { - if (tip.pickedUp) { + if (tip.pickedUpTimestamp) { return; } resp.nextRetryDelay = updateRetryDelay( diff --git a/packages/taler-wallet-core/src/operations/tip.ts b/packages/taler-wallet-core/src/operations/tip.ts index 6ccd262b0..248ea2cd1 100644 --- a/packages/taler-wallet-core/src/operations/tip.ts +++ b/packages/taler-wallet-core/src/operations/tip.ts @@ -25,11 +25,8 @@ import { import * as Amounts from "../util/amounts"; import { Stores, - PlanchetRecord, - WithdrawalGroupRecord, initRetryInfo, updateRetryInfoTimeout, - WithdrawalSourceType, TipPlanchet, CoinRecord, CoinSourceType, @@ -38,7 +35,6 @@ import { import { getExchangeWithdrawalInfo, selectWithdrawalDenoms, - processWithdrawGroup, denomSelectionInfoToState, } from "./withdraw"; import { updateExchangeFromUrl } from "./exchanges"; @@ -102,15 +98,11 @@ export async function prepareTip( tipRecord = { walletTipId: walletTipId, acceptedTimestamp: undefined, - rejectedTimestamp: undefined, tipAmountRaw: amount, - deadline: tipPickupStatus.expiration, - exchangeUrl: tipPickupStatus.exchange_url, + tipExpiration: tipPickupStatus.expiration, + exchangeBaseUrl: tipPickupStatus.exchange_url, merchantBaseUrl: res.merchantBaseUrl, - nextUrl: undefined, - pickedUp: false, planchets: undefined, - response: undefined, createdTimestamp: getTimestampNow(), merchantTipId: res.merchantTipId, tipAmountEffective: Amounts.sub(amount, Amounts.add( @@ -120,6 +112,7 @@ export async function prepareTip( retryInfo: initRetryInfo(), lastError: undefined, denomsSel: denomSelectionInfoToState(selectedDenoms), + pickedUpTimestamp: undefined, }; await ws.db.put(Stores.tips, tipRecord); } @@ -197,7 +190,7 @@ async function processTipImpl( return; } - if (tipRecord.pickedUp) { + if (tipRecord.pickedUpTimestamp) { logger.warn("tip already picked up"); return; } @@ -302,7 +295,7 @@ async function processTipImpl( denomPub: planchet.denomPub, denomPubHash: planchet.denomPubHash, denomSig: denomSig, - exchangeBaseUrl: tipRecord.exchangeUrl, + exchangeBaseUrl: tipRecord.exchangeBaseUrl, status: CoinStatus.Fresh, suspended: false, }); @@ -315,10 +308,11 @@ async function processTipImpl( if (!tr) { return; } - if (tr.pickedUp) { + if (tr.pickedUpTimestamp) { return; } - tr.pickedUp = true; + tr.pickedUpTimestamp = getTimestampNow(); + tr.lastError = undefined; tr.retryInfo = initRetryInfo(false); await tx.put(Stores.tips, tr); for (const cr of newCoinRecords) { diff --git a/packages/taler-wallet-core/src/operations/transactions.ts b/packages/taler-wallet-core/src/operations/transactions.ts index b5f77a190..026a91ef3 100644 --- a/packages/taler-wallet-core/src/operations/transactions.ts +++ b/packages/taler-wallet-core/src/operations/transactions.ts @@ -38,6 +38,7 @@ import { OrderShortInfo, } from "../types/transactions"; import { getFundingPaytoUris } from "./reserves"; +import { TipResponse } from "../types/talerTypes"; /** * Create an event ID from the type and the primary key for the event. @@ -308,6 +309,29 @@ export async function getTransactions( }); }); }); + + tx.iter(Stores.tips).forEachAsync(async (tipRecord) => { + if ( + shouldSkipCurrency( + transactionsRequest, + tipRecord.tipAmountRaw.currency, + ) + ) { + return; + } + if (!tipRecord.acceptedTimestamp) { + return; + } + transactions.push({ + type: TransactionType.Tip, + amountEffective: Amounts.stringify(tipRecord.tipAmountEffective), + amountRaw: Amounts.stringify(tipRecord.tipAmountRaw), + pending: !tipRecord.pickedUpTimestamp, + timestamp: tipRecord.acceptedTimestamp, + transactionId: makeEventId(TransactionType.Tip, tipRecord.walletTipId), + error: tipRecord.lastError, + }); + }); }, ); diff --git a/packages/taler-wallet-core/src/types/dbTypes.ts b/packages/taler-wallet-core/src/types/dbTypes.ts index 3e24f787b..60a427ef2 100644 --- a/packages/taler-wallet-core/src/types/dbTypes.ts +++ b/packages/taler-wallet-core/src/types/dbTypes.ts @@ -950,16 +950,6 @@ export interface TipRecord { */ acceptedTimestamp: Timestamp | undefined; - /** - * Has the user rejected the tip? - */ - rejectedTimestamp: Timestamp | undefined; - - /** - * Have we picked up the tip record from the merchant already? - */ - pickedUp: boolean; - /** * The tipped amount. */ @@ -970,12 +960,12 @@ export interface TipRecord { /** * Timestamp, the tip can't be picked up anymore after this deadline. */ - deadline: Timestamp; + tipExpiration: Timestamp; /** * The exchange that will sign our coins, chosen by the merchant. */ - exchangeUrl: string; + exchangeBaseUrl: string; /** * Base URL of the merchant that is giving us the tip. @@ -990,12 +980,6 @@ export interface TipRecord { denomsSel: DenomSelectionState; - /** - * Response if the merchant responded, - * undefined otherwise. - */ - response?: TipResponse[]; - /** * Tip ID chosen by the wallet. */ @@ -1006,13 +990,14 @@ export interface TipRecord { */ merchantTipId: string; - /** - * URL to go to once the tip has been accepted. - */ - nextUrl?: string; - createdTimestamp: Timestamp; + /** + * Timestamp for when the wallet finished picking up the tip + * from the merchant. + */ + pickedUpTimestamp: Timestamp | undefined; + /** * Retry info, even present when the operation isn't active to allow indexing * on the next retry timestamp. diff --git a/packages/taler-wallet-core/src/types/transactions.ts b/packages/taler-wallet-core/src/types/transactions.ts index 1d1eb6490..21d7ee181 100644 --- a/packages/taler-wallet-core/src/types/transactions.ts +++ b/packages/taler-wallet-core/src/types/transactions.ts @@ -278,18 +278,6 @@ interface TransactionRefund extends TransactionCommon { interface TransactionTip extends TransactionCommon { type: TransactionType.Tip; - // true if the user still needs to accept/decline this tip - waiting: boolean; - - // true if the user has accepted this top, false otherwise - accepted: boolean; - - // Exchange that the tip will be (or was) withdrawn from - exchangeBaseUrl: string; - - // More information about the merchant that sent the tip - merchant: any; - // Raw amount of the tip, without extra fees that apply amountRaw: AmountString;