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:
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);