commit aec34138e692fe6c267027be7dc27e5c49e398ba
parent 87ae2e9211053ea0b16bb1b33dfbc1231a51a2d2
Author: Florian Dold <dold@taler.net>
Date: Fri, 24 Jul 2026 12:44:44 +0200
wallet-core: remove preparePay v1 implementation
Diffstat:
9 files changed, 65 insertions(+), 709 deletions(-)
diff --git a/packages/taler-util/src/types-taler-wallet.ts b/packages/taler-util/src/types-taler-wallet.ts
@@ -104,7 +104,6 @@ import {
TokenEnvelope,
TokenIssuePublicKey,
WalletTemplateDetailsResponse,
- codecForMerchantContractTerms,
codecForMerchantContractTermsV0,
} from "./types-taler-merchant.js";
import { BackupRecovery } from "./types-taler-sync.js";
@@ -774,29 +773,6 @@ export interface SenderWireInfos {
senderWires: string[];
}
-export enum PreparePayResultType {
- PaymentPossible = "payment-possible",
- InsufficientBalance = "insufficient-balance",
- AlreadyConfirmed = "already-confirmed",
- ChoiceSelection = "choice-selection",
-}
-
-export const codecForPreparePayResultPaymentPossible =
- (): Codec<PreparePayResultPaymentPossible> =>
- buildCodecForObject<PreparePayResultPaymentPossible>()
- .property("amountEffective", codecForAmountString())
- .property("amountRaw", codecForAmountString())
- .property("contractTerms", codecForMerchantContractTermsV0())
- .property("transactionId", codecForTransactionIdStr())
- .property("contractTermsHash", codecForString())
- .property("scopes", codecForList(codecForScopeInfo()))
- .property("talerUri", codecForString())
- .property(
- "status",
- codecForConstString(PreparePayResultType.PaymentPossible),
- )
- .build("PreparePayResultPaymentPossible");
-
export enum InsufficientBalanceHint {
/**
* Merchant doesn't accept money from exchange(s) that the wallet supports.
@@ -1020,180 +996,10 @@ export const codecForPayMerchantInsufficientBalanceDetails =
.deprecatedProperty("balanceReceiverAcceptable")
.build("PayMerchantInsufficientBalanceDetails");
-export const codecForPreparePayResultInsufficientBalance =
- (): Codec<PreparePayResultInsufficientBalance> =>
- buildCodecForObject<PreparePayResultInsufficientBalance>()
- .property("amountRaw", codecForAmountString())
- .property("contractTerms", codecForMerchantContractTermsV0())
- .property("talerUri", codecForString())
- .property("transactionId", codecForTransactionIdStr())
- .property(
- "status",
- codecForConstString(PreparePayResultType.InsufficientBalance),
- )
- .property("scopes", codecForList(codecForScopeInfo()))
- .property(
- "balanceDetails",
- codecForPayMerchantInsufficientBalanceDetails(),
- )
- .build("PreparePayResultInsufficientBalance");
-
-export const codecForPreparePayResultAlreadyConfirmed =
- (): Codec<PreparePayResultAlreadyConfirmed> =>
- buildCodecForObject<PreparePayResultAlreadyConfirmed>()
- .property(
- "status",
- codecForConstString(PreparePayResultType.AlreadyConfirmed),
- )
- .property("amountEffective", codecOptional(codecForAmountString()))
- .property("amountRaw", codecForAmountString())
- .property("scopes", codecForList(codecForScopeInfo()))
- .property("paid", codecForBoolean())
- .property("talerUri", codecForString())
- .property("contractTerms", codecForMerchantContractTerms())
- .property("contractTermsHash", codecForString())
- .property("transactionId", codecForTransactionIdStr())
- .build("PreparePayResultAlreadyConfirmed");
-
-export const codecForPreparePayResultChoiceSelection =
- (): Codec<PreparePayResultChoiceSelection> =>
- buildCodecForObject<PreparePayResultChoiceSelection>()
- .property(
- "status",
- codecForConstString(PreparePayResultType.ChoiceSelection),
- )
- .property("transactionId", codecForTransactionIdStr())
- .property("contractTerms", codecForMerchantContractTerms())
- .property("contractTermsHash", codecForString())
- .property("talerUri", codecForString())
- .build("PreparePayResultChoiceSelection");
-
-export const codecForPreparePayResult = (): Codec<PreparePayResult> =>
- buildCodecForUnion<PreparePayResult>()
- .discriminateOn("status")
- .alternative(
- PreparePayResultType.AlreadyConfirmed,
- codecForPreparePayResultAlreadyConfirmed(),
- )
- .alternative(
- PreparePayResultType.InsufficientBalance,
- codecForPreparePayResultInsufficientBalance(),
- )
- .alternative(
- PreparePayResultType.PaymentPossible,
- codecForPreparePayResultPaymentPossible(),
- )
- .alternative(
- PreparePayResultType.ChoiceSelection,
- codecForPreparePayResultChoiceSelection(),
- )
- .build("PreparePayResult");
-
export interface PreparePayV2Result {
transactionId: TransactionIdStr;
}
-/**
- * Result of a prepare pay operation.
- */
-export type PreparePayResult =
- | PreparePayResultInsufficientBalance
- | PreparePayResultAlreadyConfirmed
- | PreparePayResultPaymentPossible
- | PreparePayResultChoiceSelection;
-
-/**
- * Payment is possible.
- *
- * This response is only returned for v0 contracts
- * or when v1 are not enabled yet.
- */
-export interface PreparePayResultPaymentPossible {
- status: PreparePayResultType.PaymentPossible;
-
- transactionId: TransactionIdStr;
-
- contractTerms: MerchantContractTermsV0;
-
- /**
- * Scopes involved in this transaction.
- */
- scopes: ScopeInfo[];
-
- amountRaw: AmountString;
-
- amountEffective: AmountString;
-
- /**
- * FIXME: Unclear why this is needed. Remove?
- */
- contractTermsHash: string;
-
- /**
- * FIXME: Unclear why this is needed! Remove?
- */
- talerUri: string;
-}
-
-export interface PreparePayResultInsufficientBalance {
- status: PreparePayResultType.InsufficientBalance;
- transactionId: TransactionIdStr;
-
- /**
- * Scopes involved in this transaction.
- *
- * For the insufficient balance response, contains scopes
- * of *possible* payment providers.
- */
- scopes: ScopeInfo[];
-
- contractTerms: MerchantContractTermsV0;
-
- amountRaw: AmountString;
-
- talerUri: string;
-
- balanceDetails: PaymentInsufficientBalanceDetails;
-}
-
-export interface PreparePayResultAlreadyConfirmed {
- status: PreparePayResultType.AlreadyConfirmed;
-
- transactionId: TransactionIdStr;
-
- contractTerms: MerchantContractTerms;
-
- paid: boolean;
-
- amountRaw: AmountString;
-
- amountEffective?: AmountString | undefined;
-
- /**
- * Scopes involved in this transaction.
- */
- scopes: ScopeInfo[];
-
- contractTermsHash: string;
-
- talerUri: string;
-}
-
-/**
- * Unconfirmed contract v1 payment.
- */
-export interface PreparePayResultChoiceSelection {
- status: PreparePayResultType.ChoiceSelection;
-
- transactionId: TransactionIdStr;
-
- contractTerms: MerchantContractTerms;
-
- contractTermsHash: string;
-
- talerUri: string;
-}
-
export interface BankWithdrawDetails {
status: WithdrawalOperationStatusFlag;
currency: string;
diff --git a/packages/taler-wallet-cli/src/index.ts b/packages/taler-wallet-cli/src/index.ts
@@ -23,7 +23,6 @@ import {
AgeRestriction,
Amounts,
AmountString,
- assertUnreachable,
ChoiceSelectionDetailType,
codecForList,
codecForString,
@@ -39,7 +38,6 @@ import {
Logger,
NotificationType,
Paytos,
- PreparePayResultType,
Result,
getSampleWalletCoreTransactions,
setDangerousTimetravel,
@@ -2019,30 +2017,33 @@ advancedCli
.action(async (args) => {
await withWallet(args, { lazyTaskLoop: true }, async (wallet) => {
const res = await wallet.client.call(
- WalletApiOperation.PreparePayForUri,
+ WalletApiOperation.PreparePayForUriV2,
{
talerPayUri: args.payPrepare.url,
},
);
- switch (res.status) {
- case PreparePayResultType.InsufficientBalance:
- console.log("insufficient balance");
- break;
- case PreparePayResultType.AlreadyConfirmed:
- if (res.paid) {
- console.log("already paid!");
- } else {
- console.log("payment in progress");
- }
- break;
- case PreparePayResultType.PaymentPossible:
- console.log("payment possible");
- break;
- case PreparePayResultType.ChoiceSelection:
- console.log("choice selection");
- break;
- default:
- assertUnreachable(res);
+ // The transaction starts out in the "downloading proposal" state, so
+ // wait for it to settle before reporting what the user can do with it.
+ await wallet.client.call(WalletApiOperation.TestingWaitTransactionState, {
+ transactionId: res.transactionId,
+ txState: "nonpending",
+ });
+ const tx = await wallet.client.call(
+ WalletApiOperation.GetTransactionById,
+ {
+ transactionId: res.transactionId,
+ },
+ );
+ console.log(`transaction ${res.transactionId}`);
+ console.log(`state: ${j2s(tx.txState)}`);
+ const choicesRes = await wallet.client.callForResult(
+ WalletApiOperation.GetChoicesForPayment,
+ {
+ transactionId: res.transactionId,
+ },
+ );
+ if (choicesRes.tag === "ok") {
+ console.log(`choices: ${j2s(choicesRes.value.choices)}`);
}
});
});
diff --git a/packages/taler-wallet-core/src/pay-merchant.ts b/packages/taler-wallet-core/src/pay-merchant.ts
@@ -71,8 +71,6 @@ import {
PayCoinSelection,
PaymentInsufficientBalanceDetails,
PayWalletData,
- PreparePayResult,
- PreparePayResultType,
PreparePayTemplateRequest,
PreparePayV2Result,
randomBytes,
@@ -115,7 +113,6 @@ import {
} from "./coinSelection.js";
import {
constructTaskIdentifier,
- genericWaitForState,
genericWaitForStateVal,
getGenericRecordHandle,
LookupFullTransactionOpts,
@@ -157,7 +154,7 @@ import {
getScopeForAllCoins,
getScopeForAllExchanges,
} from "./exchanges.js";
-import { instantiateTemplate, instantiateTemplateRaw } from "./pay-template.js";
+import { instantiateTemplateRaw } from "./pay-template.js";
import { runWithMaybeProgressContext } from "./progress.js";
import {
calculateRefreshOutput,
@@ -1850,364 +1847,6 @@ async function handleInsufficientFunds(
return TaskRunResult.progress();
}
-/**
- * @deprecated only used for legacy compat
- */
-async function lookupProposalOrRepurchase(
- wex: WalletExecutionContext,
- proposalId: string,
-): Promise<
- | {
- proposalId: string;
- purchaseRec: WalletPurchase;
- contractTerms: MerchantContractTerms;
- contractTermsHash: string;
- }
- | undefined
-> {
- return await wex.runWalletDbTx(async (tx) => {
- let purchaseRec = await tx.getPurchase(proposalId);
-
- if (!purchaseRec) {
- return undefined;
- }
-
- if (purchaseRec.purchaseStatus === PurchaseStatus.DoneRepurchaseDetected) {
- const existingProposalId = purchaseRec.repurchaseProposalId;
- if (existingProposalId) {
- logger.trace("using existing purchase for same product");
- const oldProposal = await tx.getPurchase(existingProposalId);
- if (oldProposal) {
- purchaseRec = oldProposal;
- }
- }
- }
-
- let { contractTerms, contractTermsHash } =
- await expectProposalDownloadByIdInTx(wex, tx, proposalId);
-
- proposalId = purchaseRec.proposalId;
-
- return {
- proposalId,
- purchaseRec: purchaseRec,
- contractTerms,
- contractTermsHash,
- };
- });
-}
-
-/**
- * @deprecated only used for legacy compat
- */
-async function checkPaymentByProposalId(
- wex: WalletExecutionContext,
- proposalId: string,
- sessionId?: string,
-): Promise<PreparePayResult> {
- // FIXME: Should take a transaction ID instead of a proposal ID
- // FIXME: Does way more than checking the payment
- // FIXME: Should return immediately.
- const lookupRes = await lookupProposalOrRepurchase(wex, proposalId);
- if (!lookupRes) {
- throw Error(`could not get proposal ${proposalId}`);
- }
- // Might be redirected to another proposal ID due to repurchase detection.
- proposalId = lookupRes.proposalId;
-
- const purchaseRec = lookupRes.purchaseRec;
- let contractTerms = lookupRes.contractTerms;
- const contractTermsHash = lookupRes.contractTermsHash;
-
- const ctx = new PayMerchantTransactionContext(wex, proposalId);
-
- const transactionId = ctx.transactionId;
-
- const talerUri = TalerUris.stringify({
- type: TalerUriAction.Pay,
- merchantBaseUrl: purchaseRec.merchantBaseUrl as HostPortPath, // FIXME: change record type
- orderId: purchaseRec.orderId,
- sessionId:
- sessionId ??
- purchaseRec.lastSessionId ??
- purchaseRec.downloadSessionId ??
- "",
- claimToken: purchaseRec.claimToken,
- });
-
- const scopes = await wex.runWalletDbTx(async (tx) => {
- let exchangeUrls = contractTerms.exchanges.map((x) => x.url);
- return await getScopeForAllExchanges(tx, exchangeUrls);
- });
-
- const handleUnconfirmed = async (): Promise<PreparePayResult> => {
- if (contractTerms.version === MerchantContractVersion.V1) {
- return {
- status: PreparePayResultType.ChoiceSelection,
- transactionId,
- contractTerms: contractTerms,
- contractTermsHash: contractTermsHash,
- talerUri,
- };
- }
-
- const instructedAmount = Amounts.parseOrThrow(contractTerms.amount);
- // If not already paid, check if we could pay for it.
- const res = await selectPayCoins(wex, {
- restrictExchanges: {
- auditors: [],
- exchanges: contractTerms.exchanges.map((ex) => ({
- exchangeBaseUrl: ex.url,
- exchangePub: ex.master_pub,
- })),
- },
- contractTermsAmount: instructedAmount,
- depositFeeLimit: Amounts.parseOrThrow(contractTerms.max_fee),
- prevPayCoins: [],
- requiredMinimumAge: contractTerms.minimum_age,
- restrictWireMethod: contractTerms.wire_method,
- });
-
- let coins: SelectedProspectiveCoin[] | undefined = undefined;
-
- const allowedExchangeUrls = contractTerms.exchanges.map((x) => x.url);
-
- switch (res.type) {
- case "failure": {
- logger.info("not allowing payment, insufficient coins");
- logger.info(
- `insufficient balance details: ${j2s(
- res.insufficientBalanceDetails,
- )}`,
- );
- let scopes = await wex.runWalletDbTx(async (tx) => {
- return getScopeForAllExchanges(tx, allowedExchangeUrls);
- });
- return {
- status: PreparePayResultType.InsufficientBalance,
- contractTerms,
- transactionId,
- amountRaw: Amounts.stringify(contractTerms.amount),
- scopes,
- talerUri,
- balanceDetails: res.insufficientBalanceDetails,
- };
- }
- case "success":
- coins = res.coinSel.coins;
- break;
- default:
- assertUnreachable(res);
- }
-
- const currency = Amounts.currencyOf(contractTerms.amount);
- const totalCost = await getTotalPaymentCost(wex, currency, coins);
- logger.trace("costInfo", totalCost);
- logger.trace("coinsForPayment", res);
-
- const exchanges = new Set<string>(coins.map((x) => x.exchangeBaseUrl));
-
- const scopes = await wex.runWalletDbTx(async (tx) => {
- return await getScopeForAllExchanges(tx, [...exchanges]);
- });
-
- return {
- status: PreparePayResultType.PaymentPossible,
- contractTerms,
- transactionId,
- amountEffective: Amounts.stringify(totalCost),
- amountRaw: Amounts.stringify(instructedAmount),
- scopes,
- contractTermsHash,
- talerUri,
- };
- };
-
- const handlePaid = async (): Promise<PreparePayResult> => {
- logger.trace(
- "automatically re-submitting payment with different session ID",
- );
- logger.trace(`last: ${purchaseRec.lastSessionId}, current: ${sessionId}`);
- await wex.runWalletDbTx(async (tx) => {
- const [p, h] = await ctx.getRecordHandle(tx);
- if (!p) {
- return;
- }
- p.lastSessionId = sessionId;
- p.purchaseStatus = PurchaseStatus.PendingPayingReplay;
- await h.update(p);
- });
-
- await wex.taskScheduler.resetTask(ctx.taskId);
-
- // FIXME: Consider changing the API here so that we don't have to
- // wait inline for the repurchase.
-
- await waitPaymentResult(wex, proposalId, sessionId);
- const download = await expectProposalDownload(wex, purchaseRec);
- const { available, amountRaw } = ContractTermsUtil.extractAmounts(
- download.contractTerms,
- purchaseRec.choiceIndex,
- );
- if (!available) {
- throw Error("choice index not specified for contract v1");
- }
-
- return {
- status: PreparePayResultType.AlreadyConfirmed,
- contractTerms,
- contractTermsHash: download.contractTermsHash,
- paid: true,
- amountRaw: Amounts.stringify(amountRaw),
- amountEffective: purchaseRec.payInfo
- ? Amounts.stringify(purchaseRec.payInfo.totalPayCost)
- : undefined,
- scopes,
- transactionId,
- talerUri,
- };
- };
-
- const handleConfirmed = async (): Promise<PreparePayResult> => {
- const paid = isPurchasePaid(purchaseRec);
-
- const { available, amountRaw } = ContractTermsUtil.extractAmounts(
- contractTerms,
- purchaseRec.choiceIndex,
- );
- if (!available) {
- throw Error("choice index not specified for contract v1");
- }
-
- return {
- status: PreparePayResultType.AlreadyConfirmed,
- contractTerms: contractTerms,
- contractTermsHash: contractTermsHash,
- paid,
- amountRaw: Amounts.stringify(amountRaw),
- amountEffective: purchaseRec.payInfo
- ? Amounts.stringify(purchaseRec.payInfo.totalPayCost)
- : undefined,
- ...(paid ? { nextUrl: contractTerms.order_id } : {}),
- scopes,
- transactionId,
- talerUri,
- };
- };
-
- const handlePaidByOther = async (): Promise<PreparePayResult> => {
- const { amountRaw } = ContractTermsUtil.extractAmounts(
- contractTerms,
- purchaseRec.choiceIndex,
- );
- return {
- status: PreparePayResultType.AlreadyConfirmed,
- contractTerms,
- contractTermsHash: contractTermsHash,
- paid: true,
- amountRaw: amountRaw ? Amounts.stringify(amountRaw) : "UNKNOWN:0",
- amountEffective: purchaseRec.payInfo
- ? Amounts.stringify(purchaseRec.payInfo.totalPayCost)
- : undefined,
- scopes,
- transactionId,
- talerUri,
- };
- };
-
- if (!purchaseRec) {
- return handleUnconfirmed();
- }
-
- switch (purchaseRec?.purchaseStatus) {
- case PurchaseStatus.DialogProposed:
- case PurchaseStatus.DialogShared:
- return await handleUnconfirmed();
- case PurchaseStatus.Done:
- case PurchaseStatus.PendingPayingReplay:
- case PurchaseStatus.DoneRepurchaseDetected:
- return handlePaid();
- case PurchaseStatus.AbortedIncompletePayment:
- case PurchaseStatus.AbortedOrderDeleted:
- case PurchaseStatus.AbortedProposalRefused:
- case PurchaseStatus.AbortedRefunded:
- case PurchaseStatus.AbortingWithRefund:
- case PurchaseStatus.FailedAbort:
- case PurchaseStatus.FailedClaim:
- case PurchaseStatus.Failed:
- case PurchaseStatus.FinalizingQueryingAutoRefund:
- case PurchaseStatus.PendingAcceptRefund:
- case PurchaseStatus.PendingPaying:
- case PurchaseStatus.PendingQueryingAutoRefund:
- case PurchaseStatus.PendingQueryingRefund:
- case PurchaseStatus.SuspendedAbortingWithRefund:
- case PurchaseStatus.SuspendedFinalizingQueryingAutoRefund:
- case PurchaseStatus.SuspendedPaying:
- case PurchaseStatus.SuspendedPayingReplay:
- case PurchaseStatus.SuspendedPendingAcceptRefund:
- case PurchaseStatus.SuspendedQueryingAutoRefund:
- case PurchaseStatus.SuspendedQueryingRefund:
- case PurchaseStatus.Expired:
- return handleConfirmed();
- case PurchaseStatus.FailedPaidByOther:
- return handlePaidByOther();
- case PurchaseStatus.PendingDownloadingProposal:
- case PurchaseStatus.SuspendedDownloadingProposal:
- throw Error("expected proposal to be downloaded");
- default:
- assertUnreachable(purchaseRec.purchaseStatus);
- }
-}
-
-function isPurchasePaid(purchase: WalletPurchase): boolean {
- return (
- purchase.purchaseStatus === PurchaseStatus.Done ||
- purchase.purchaseStatus === PurchaseStatus.PendingQueryingRefund ||
- purchase.purchaseStatus === PurchaseStatus.FinalizingQueryingAutoRefund ||
- purchase.purchaseStatus === PurchaseStatus.PendingQueryingAutoRefund
- );
-}
-
-/**
- * Check if a payment for the given taler://pay/ URI is possible.
- */
-export async function preparePayForUri(
- wex: WalletExecutionContext,
- talerPayUri: string,
-): Promise<PreparePayResult> {
- const uriResult = Result.orUndefined(
- TalerUris.parseRestricted(talerPayUri, TalerUriAction.Pay),
- );
-
- if (!uriResult) {
- throw TalerError.fromDetail(
- TalerErrorCode.WALLET_INVALID_TALER_PAY_URI,
- {
- talerPayUri,
- },
- `invalid taler://pay URI (${talerPayUri})`,
- );
- }
-
- const proposalRes = await createOrReusePurchase(
- wex,
- uriResult.merchantBaseUrl,
- uriResult.orderId,
- uriResult.sessionId,
- uriResult.claimToken,
- uriResult.noncePriv,
- talerPayUri,
- );
-
- await waitProposalDownloaded(wex, proposalRes.proposalId);
-
- return checkPaymentByProposalId(
- wex,
- proposalRes.proposalId,
- uriResult.sessionId,
- );
-}
-
export async function preparePayForUriV2(
wex: WalletExecutionContext,
talerPayUri: string,
@@ -2241,74 +1880,6 @@ export async function preparePayForUriV2(
};
}
-/**
- * Wait until a proposal is at least downloaded.
- */
-async function waitProposalDownloaded(
- wex: WalletExecutionContext,
- proposalId: string,
-): Promise<void> {
- // FIXME: This doesn't support cancellation yet
- const ctx = new PayMerchantTransactionContext(wex, proposalId);
-
- logger.info(`waiting for ${ctx.transactionId} to be downloaded`);
-
- wex.taskScheduler.startShepherdTask(ctx.taskId);
-
- await genericWaitForState(wex, {
- filterNotification(notif) {
- return (
- notif.type === NotificationType.TransactionStateTransition &&
- notif.transactionId === ctx.transactionId
- );
- },
- async checkState() {
- const { purchase, retryInfo } = await ctx.wex.runWalletDbTx(
- async (tx) => {
- return {
- purchase: await tx.getPurchase(ctx.proposalId),
- retryInfo: await tx.getOperationRetry(ctx.taskId),
- };
- },
- );
- if (!purchase) {
- throw Error("purchase does not exist anymore");
- }
- if (purchase.download) {
- return true;
- }
- const exc = purchase.failReason ?? purchase.abortReason;
- if (exc) {
- throw TalerError.fromUncheckedDetail(exc);
- }
- if (retryInfo) {
- // FIXME: This should really be a return status of `preparePay`.
- if (retryInfo.lastError) {
- throw TalerError.fromUncheckedDetail(retryInfo.lastError);
- } else {
- throw Error("transient error while waiting for proposal download");
- }
- }
- return false;
- },
- });
-}
-
-export async function preparePayForTemplate(
- wex: WalletExecutionContext,
- req: PreparePayTemplateRequest,
-): Promise<PreparePayResult> {
- return runWithMaybeProgressContext(
- wex,
- "preparePayForTemplate",
- req.progressToken,
- async () => {
- const payUri = await instantiateTemplate(wex, req);
- return await preparePayForUri(wex, payUri);
- },
- );
-}
-
export async function preparePayForTemplateV2(
wex: WalletExecutionContext,
req: PreparePayTemplateRequest,
diff --git a/packages/taler-wallet-core/src/pay-template.ts b/packages/taler-wallet-core/src/pay-template.ts
@@ -20,7 +20,6 @@ import {
assertUnreachable,
CheckPayTemplateReponse,
CheckPayTemplateRequest,
- HostPortPath,
j2s,
Logger,
PreparePayTemplateRequest,
@@ -315,22 +314,3 @@ export async function instantiateTemplateRaw(
claimToken: resp.token,
};
}
-
-/**
- * Instantiate a pay template.
- *
- * @returns A taler://pay/ URI pointing to the order
- */
-export async function instantiateTemplate(
- wex: WalletExecutionContext,
- req: PreparePayTemplateRequest,
-): Promise<string> {
- const res = await instantiateTemplateRaw(wex, req);
- return TalerUris.stringify({
- type: TalerUriAction.Pay,
- merchantBaseUrl: res.merchantBaseUrl as HostPortPath,
- orderId: res.orderId,
- sessionId: res.sessionId ?? "",
- claimToken: res.claimToken,
- });
-}
diff --git a/packages/taler-wallet-core/src/requests.ts b/packages/taler-wallet-core/src/requests.ts
@@ -325,9 +325,7 @@ import {
import {
confirmPay,
getChoicesForPayment,
- preparePayForTemplate,
preparePayForTemplateV2,
- preparePayForUri,
preparePayForUriV2,
sharePayment,
startQueryRefund,
@@ -2375,14 +2373,6 @@ const handlers: { [T in WalletApiOperation]: HandlerWithValidator<T> } = {
codec: codecForCheckPayTemplateRequest(),
handler: checkPayForTemplate,
},
- [WalletApiOperation.PreparePayForUri]: {
- codec: codecForPreparePayRequest(),
- handler: (wex, req) => preparePayForUri(wex, req.talerPayUri),
- },
- [WalletApiOperation.PreparePayForTemplate]: {
- codec: codecForPreparePayTemplateRequest(),
- handler: preparePayForTemplate,
- },
[WalletApiOperation.PreparePayForUriV2]: {
codec: codecForPreparePayRequest(),
handler: (wex, req) => preparePayForUriV2(wex, req.talerPayUri),
diff --git a/packages/taler-wallet-core/src/testing.ts b/packages/taler-wallet-core/src/testing.ts
@@ -31,6 +31,7 @@ import {
AmountString,
checkLogicInvariant,
CheckPaymentResponse,
+ ChoiceSelectionDetailType,
ConfirmPayResultType,
Duration,
IntegrationTestArgs,
@@ -40,7 +41,6 @@ import {
NotificationType,
Paytos,
Result,
- PreparePayResultType,
succeedOrThrow,
TalerCorebankApiClient,
TalerMerchantInstanceHttpClient,
@@ -63,7 +63,8 @@ import { createDepositGroup } from "./deposits.js";
import { fetchFreshExchange } from "./exchanges.js";
import {
confirmPay,
- preparePayForUri,
+ getChoicesForPayment,
+ preparePayForUriV2,
startRefundQueryForUri,
} from "./pay-merchant.js";
import { initiatePeerPullPayment } from "./pay-peer-pull-credit.js";
@@ -272,16 +273,35 @@ async function makePayment(
throw Error("no taler://pay/ URI in payment response");
}
- const preparePayResult = await preparePayForUri(wex, talerPayUri);
+ const preparePayResult = await preparePayForUriV2(wex, talerPayUri);
logger.trace("prepare pay result", preparePayResult);
- if (preparePayResult.status != "payment-possible") {
+ // V2 does not wait for the proposal to be downloaded, so wait for the
+ // transaction to reach the dialog state before inspecting the choices.
+ await waitTransactionState(wex, {
+ transactionId: preparePayResult.transactionId,
+ txState: {
+ major: TransactionMajorState.Dialog,
+ minor: "*",
+ },
+ });
+
+ const choicesResult = await getChoicesForPayment(
+ wex,
+ preparePayResult.transactionId,
+ );
+
+ if (
+ choicesResult.choices[0]?.status !==
+ ChoiceSelectionDetailType.PaymentPossible
+ ) {
throw Error("payment not possible");
}
const confirmPayResult = await confirmPay(wex, {
transactionId: preparePayResult.transactionId,
+ choiceIndex: 0,
});
logger.trace("confirmPayResult", confirmPayResult);
@@ -941,12 +961,24 @@ export async function testPay(
process.exit(1);
}
logger.trace("taler pay URI:", talerPayUri);
- const result = await preparePayForUri(wex, talerPayUri);
- if (result.status !== PreparePayResultType.PaymentPossible) {
- throw Error(`unexpected prepare pay status: ${result.status}`);
+ const result = await preparePayForUriV2(wex, talerPayUri);
+ // V2 does not wait for the proposal to be downloaded, so wait for the
+ // transaction to reach the dialog state before inspecting the choices.
+ await waitTransactionState(wex, {
+ transactionId: result.transactionId,
+ txState: {
+ major: TransactionMajorState.Dialog,
+ minor: "*",
+ },
+ });
+ const choicesResult = await getChoicesForPayment(wex, result.transactionId);
+ const choice = choicesResult.choices[0];
+ if (choice?.status !== ChoiceSelectionDetailType.PaymentPossible) {
+ throw Error(`unexpected prepare pay status: ${choice?.status}`);
}
const r = await confirmPay(wex, {
transactionId: result.transactionId,
+ choiceIndex: 0,
forcedCoinSel: args.forcedCoinSel,
});
if (r.type != ConfirmPayResultType.Done) {
diff --git a/packages/taler-wallet-core/src/versions.ts b/packages/taler-wallet-core/src/versions.ts
@@ -48,7 +48,7 @@ export const WALLET_BANK_CONVERSION_API_PROTOCOL_VERSION = "2:0:0";
/**
* Libtool version of the wallet-core API.
*/
-export const WALLET_CORE_API_PROTOCOL_VERSION = "7:0:0";
+export const WALLET_CORE_API_PROTOCOL_VERSION = "8:0:0";
/**
* Libtool rules:
diff --git a/packages/taler-wallet-core/src/wallet-api-types.ts b/packages/taler-wallet-core/src/wallet-api-types.ts
@@ -153,7 +153,6 @@ import {
PrepareBankIntegratedWithdrawalRequest,
PrepareBankIntegratedWithdrawalResponse,
PreparePayRequest,
- PreparePayResult,
PreparePayTemplateRequest,
PreparePayV2Result,
PreparePeerPullDebitRequest,
@@ -294,8 +293,6 @@ export enum WalletApiOperation {
// Merchant Payments
GetChoicesForPayment = "getChoicesForPayment",
- PreparePayForUri = "preparePayForUri",
- PreparePayForTemplate = "preparePayForTemplate",
PreparePayForUriV2 = "preparePayForUriV2",
PreparePayForTemplateV2 = "preparePayForTemplateV2",
SharePayment = "sharePayment",
@@ -845,24 +842,6 @@ export type AcceptManualWithdrawalOp = {
/**
* Prepare to make a payment based on a taler://pay/ URI.
*/
-export type PreparePayForUriOp = {
- op: WalletApiOperation.PreparePayForUri;
- request: PreparePayRequest;
- response: PreparePayResult;
-};
-
-/**
- * Prepare to make a payment based on a taler://pay-template/ URI.
- */
-export type PreparePayForTemplateOp = {
- op: WalletApiOperation.PreparePayForTemplate;
- request: PreparePayTemplateRequest;
- response: PreparePayResult;
-};
-
-/**
- * Prepare to make a payment based on a taler://pay/ URI.
- */
export type PreparePayForUriV2Op = {
op: WalletApiOperation.PreparePayForUriV2;
request: PreparePayRequest;
@@ -910,7 +889,7 @@ export type CheckPayForTemplateOp = {
/**
* Confirm a payment that was previously prepared with
- * {@link PreparePayForUriOp}
+ * {@link PreparePayForUriV2Op}
*/
export type ConfirmPayOp = {
op: WalletApiOperation.ConfirmPay;
@@ -1705,8 +1684,6 @@ export type WalletOperations = {
[WalletApiOperation.InitWallet]: InitWalletOp;
[WalletApiOperation.SetWalletRunConfig]: SetWalletRunConfigOp;
[WalletApiOperation.GetVersion]: GetVersionOp;
- [WalletApiOperation.PreparePayForUri]: PreparePayForUriOp;
- [WalletApiOperation.PreparePayForTemplate]: PreparePayForTemplateOp;
[WalletApiOperation.PreparePayForUriV2]: PreparePayForUriV2Op;
[WalletApiOperation.PreparePayForTemplateV2]: PreparePayForTemplateV2Op;
[WalletApiOperation.SharePayment]: SharePaymentOp;
diff --git a/packages/taler-wallet-webextension/src/cta/PaymentTemplate/state.ts b/packages/taler-wallet-webextension/src/cta/PaymentTemplate/state.ts
@@ -16,7 +16,6 @@
import {
Amounts,
- PreparePayResult,
PreparePayV2Result,
TemplateType,
Transaction,