taler-typescript-core

Wallet core logic and WebUIs for various components
Log | Files | Refs | Submodules | README | LICENSE

commit 2d44d0bd463a691450939d91b5be3c513e41abe3
parent 762e764bc58f789642af156179c2c6be8bfcd9a4
Author: Iván Ávalos <avalos@disroot.org>
Date:   Thu, 12 Mar 2026 13:21:24 +0100

wallet-core: add transactionId field to prepare p2p requests

Diffstat:
Mpackages/taler-util/src/types-taler-wallet.ts | 22++++++++++++++++++----
Mpackages/taler-wallet-core/src/pay-peer-pull-debit.ts | 36++++++++++++++++++++++++++++++------
Mpackages/taler-wallet-core/src/pay-peer-push-credit.ts | 40++++++++++++++++++++++++++++++++--------
3 files changed, 80 insertions(+), 18 deletions(-)

diff --git a/packages/taler-util/src/types-taler-wallet.ts b/packages/taler-util/src/types-taler-wallet.ts @@ -3714,12 +3714,24 @@ export const codecForInitiatePeerPushDebitRequest = .property("restrictScope", codecOptional(codecForScopeInfo())) .build("InitiatePeerPushDebitRequest"); +/** + * Result of initiating a peer-push-credit payment. + * + * Either {@link talerUri} or {@link transactionId} must be specified. + */ export interface PreparePeerPushCreditRequest { - talerUri: string; + talerUri?: string; + transactionId?: string; } +/** + * Result of initiating a peer-pull-debit payment. + * + * Either {@link talerUri} or {@link transactionId} must be specified. + */ export interface PreparePeerPullDebitRequest { - talerUri: string; + talerUri?: string; + transactionId?: string; } export interface PreparePeerPushCreditResponse { @@ -3770,13 +3782,15 @@ export interface PreparePeerPullDebitResponse { export const codecForPreparePeerPushCreditRequest = (): Codec<PreparePeerPushCreditRequest> => buildCodecForObject<PreparePeerPushCreditRequest>() - .property("talerUri", codecForString()) + .property("talerUri", codecOptional(codecForString())) + .property("transactionId", codecOptional(codecForString())) .build("CheckPeerPushPaymentRequest"); export const codecForCheckPeerPullPaymentRequest = (): Codec<PreparePeerPullDebitRequest> => buildCodecForObject<PreparePeerPullDebitRequest>() - .property("talerUri", codecForString()) + .property("talerUri", codecOptional(codecForString())) + .property("transactionId", codecOptional(codecForString())) .build("PreparePeerPullDebitRequest"); export interface ConfirmPeerPushCreditRequest { diff --git a/packages/taler-wallet-core/src/pay-peer-pull-debit.ts b/packages/taler-wallet-core/src/pay-peer-pull-debit.ts @@ -30,6 +30,7 @@ import { HttpStatusCode, Logger, ObservabilityEventType, + PayPullUriResult, PeerContractTerms, PreparePeerPullDebitRequest, PreparePeerPullDebitResponse, @@ -80,7 +81,7 @@ import { timestampPreciseFromDb, timestampPreciseToDb, } from "./db.js"; -import { getExchangeScopeInfo, getScopeForAllExchanges } from "./exchanges.js"; +import { fetchFreshExchange, getExchangeScopeInfo, getScopeForAllExchanges } from "./exchanges.js"; import { getTotalPeerPaymentCost, isPurseDeposited, @@ -824,10 +825,25 @@ export async function preparePeerPullDebit( wex: WalletExecutionContext, req: PreparePeerPullDebitRequest, ): Promise<PreparePeerPullDebitResponse> { - const uri = parsePayPullUri(req.talerUri); + if (!req.talerUri && !req.transactionId) { + throw Error("either talerUri or transactionId must be specified"); + } - if (!uri) { - throw Error("got invalid taler://pay-pull URI"); + let uri: PayPullUriResult | undefined; + if (req.talerUri) { + uri = parsePayPullUri(req.talerUri); + if (!uri) { + throw Error("got invalid taler://pay-push URI"); + } + } + + let parsedTxId: string | undefined; + if (req.transactionId) { + const parsedRes = parseTransactionIdentifier(req.transactionId); + if (!parsedRes || parsedRes.tag !== TransactionType.PeerPullDebit) { + throw Error("got invalid transaction ID"); + } + parsedTxId = parsedRes.peerPullDebitId; } const existing = await wex.db.runReadOnlyTx( @@ -842,11 +858,15 @@ export async function preparePeerPullDebit( ], }, async (tx) => { - const peerPullDebitRecord = - await tx.peerPullDebit.indexes.byExchangeAndContractPriv.get([ + let peerPullDebitRecord: PeerPullPaymentIncomingRecord | undefined; + if (uri) { + peerPullDebitRecord = await tx.peerPullDebit.indexes.byExchangeAndContractPriv.get([ uri.exchangeBaseUrl, uri.contractPriv, ]); + } else if (parsedTxId) { + peerPullDebitRecord = await tx.peerPullDebit.get(parsedTxId); + } if (!peerPullDebitRecord) { return; } @@ -888,6 +908,10 @@ export async function preparePeerPullDebit( }; } + if (!uri) { + throw Error("transaction not found, use talerUri instead"); + } + const exchangeBaseUrl = uri.exchangeBaseUrl; const contractPriv = uri.contractPriv; const contractPub = encodeCrock(eddsaGetPublic(decodeCrock(contractPriv))); diff --git a/packages/taler-wallet-core/src/pay-peer-push-credit.ts b/packages/taler-wallet-core/src/pay-peer-push-credit.ts @@ -26,6 +26,7 @@ import { LegitimizationNeededResponse, Logger, NotificationType, + PayPushUriResult, PeerContractTerms, PreparePeerPushCreditRequest, PreparePeerPushCreditResponse, @@ -94,6 +95,7 @@ import { getMergeReserveInfo, isPurseMerged } from "./pay-peer-common.js"; import { constructTransactionIdentifier, isUnsuccessfulTransaction, + ParsedTransactionIdentifier, parseTransactionIdentifier, } from "./transactions.js"; import { WalletExecutionContext, walletExchangeClient } from "./wallet.js"; @@ -474,24 +476,39 @@ export async function preparePeerPushCredit( wex: WalletExecutionContext, req: PreparePeerPushCreditRequest, ): Promise<PreparePeerPushCreditResponse> { - const uri = parsePayPushUri(req.talerUri); + if (!req.talerUri && !req.transactionId) { + throw Error("either talerUri or transactionId must be specified"); + } - if (!uri) { - throw Error("got invalid taler://pay-push URI"); + let uri: PayPushUriResult | undefined; + if (req.talerUri) { + uri = parsePayPushUri(req.talerUri); + if (!uri) { + throw Error("got invalid taler://pay-push URI"); + } } - // add exchange entry if it doesn't exist already! - const exchangeBaseUrl = uri.exchangeBaseUrl; - const exchange = await fetchFreshExchange(wex, exchangeBaseUrl); + let parsedTxId: string | undefined; + if (req.transactionId) { + const parsedRes = parseTransactionIdentifier(req.transactionId); + if (!parsedRes || parsedRes.tag !== TransactionType.PeerPushCredit) { + throw Error("got invalid transaction ID"); + } + parsedTxId = parsedRes.peerPushCreditId; + } const existing = await wex.db.runReadOnlyTx( { storeNames: ["contractTerms", "peerPushCredit"] }, async (tx) => { - const existingPushInc = - await tx.peerPushCredit.indexes.byExchangeAndContractPriv.get([ + let existingPushInc: PeerPushPaymentIncomingRecord | undefined; + if (uri) { + existingPushInc = await tx.peerPushCredit.indexes.byExchangeAndContractPriv.get([ uri.exchangeBaseUrl, uri.contractPriv, ]); + } else if (parsedTxId) { + existingPushInc = await tx.peerPushCredit.get(parsedTxId); + } if (!existingPushInc) { return; } @@ -511,6 +528,7 @@ export async function preparePeerPushCredit( ); if (existing) { + const exchange = await fetchFreshExchange(wex, existing.existingPushInc.exchangeBaseUrl); const currency = Amounts.currencyOf(existing.existingContractTerms.amount); const exchangeBaseUrl = existing.existingPushInc.exchangeBaseUrl; const scopeInfo = await wex.db.runAllStoresReadOnlyTx( @@ -536,6 +554,12 @@ export async function preparePeerPushCredit( }; } + if (!uri) { + throw Error("transaction not found, use talerUri instead"); + } + + const exchangeBaseUrl = uri.exchangeBaseUrl; + const exchange = await fetchFreshExchange(wex, exchangeBaseUrl); const contractPriv = uri.contractPriv; const contractPub = encodeCrock(eddsaGetPublic(decodeCrock(contractPriv))); const exchangeClient = walletExchangeClient(exchangeBaseUrl, wex);