taler-typescript-core

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

commit c5b3c8e2172062e91659f10837c60ad51364d5f2
parent 140979d025fbe333092816b1630ee00dcb44db75
Author: Florian Dold <florian@dold.me>
Date:   Mon, 22 Sep 2025 15:05:39 +0200

donau: extend test, remove wrong token assumption

Diffstat:
Mpackages/taler-harness/src/integrationtests/test-donau.ts | 93+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----
Mpackages/taler-util/src/types-taler-merchant.ts | 4++++
Mpackages/taler-util/src/types-taler-wallet.ts | 11+++++++++++
Mpackages/taler-wallet-core/src/pay-merchant.ts | 34++++++++++++++++++----------------
Mpackages/taler-wallet-core/src/wallet-api-types.ts | 13+++++++++++++
Mpackages/taler-wallet-core/src/wallet.ts | 6++++++
6 files changed, 141 insertions(+), 20 deletions(-)

diff --git a/packages/taler-harness/src/integrationtests/test-donau.ts b/packages/taler-harness/src/integrationtests/test-donau.ts @@ -17,11 +17,24 @@ /** * Imports. */ -import { DonauHttpClient, j2s } from "@gnu-taler/taler-util"; -import { createSimpleTestkudosEnvironmentV3 } from "../harness/environments.js"; +import { + ConfirmPayResultType, + DonauHttpClient, + j2s, + OrderOutputType, + OrderVersion, + PreparePayResultType, + succeedOrThrow, + TalerMerchantInstanceHttpClient, +} from "@gnu-taler/taler-util"; +import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; +import { defaultCoinConfig } from "../harness/denomStructures.js"; +import { + createSimpleTestkudosEnvironmentV3, + withdrawViaBankV3, +} from "../harness/environments.js"; import { DonauService } from "../harness/harness-donau.js"; import { GlobalTestState } from "../harness/harness.js"; -import { defaultCoinConfig } from "../harness/denomStructures.js"; export async function runDonauTest(t: GlobalTestState) { // Set up test environment @@ -35,6 +48,14 @@ export async function runDonauTest(t: GlobalTestState) { commonDb, } = await createSimpleTestkudosEnvironmentV3(t); + const wres = await withdrawViaBankV3(t, { + walletClient, + bankClient, + amount: "TESTKUDOS:20", + exchange, + }); + await wres.withdrawalFinishedCond; + const donau = DonauService.create(t, { currency: "KUDOS", database: commonDb.connStr, @@ -54,6 +75,70 @@ export async function runDonauTest(t: GlobalTestState) { const keys = await donauClient.getKeys(); console.log(`keys: ${j2s(keys)}`); + + const merchantClient = new TalerMerchantInstanceHttpClient( + merchant.makeInstanceBaseUrl(), + ); + + const orderResp = succeedOrThrow( + await merchantClient.createOrder(merchantAdminAccessToken, { + order: { + version: OrderVersion.V1, + summary: "Test Donation", + choices: [ + { + amount: "TESTKUDOS:10", + outputs: [ + { + type: OrderOutputType.TaxReceipt, + amount: "TESTKUDOS:5", + donau_urls: [donau.baseUrl], + }, + ], + }, + ], + }, + }), + ); + + console.log(`order resp: ${j2s(orderResp)}`); + + let orderStatus = succeedOrThrow( + await merchantClient.getOrderDetails( + merchantAdminAccessToken, + orderResp.order_id, + ), + ); + + t.assertTrue(orderStatus.order_status === "unpaid"); + + const preparePayResult = await walletClient.call( + WalletApiOperation.PreparePayForUri, + { + talerPayUri: orderStatus.taler_pay_uri, + }, + ); + + t.assertTrue( + preparePayResult.status === PreparePayResultType.PaymentPossible, + ); + + const r2 = await walletClient.call(WalletApiOperation.ConfirmPay, { + transactionId: preparePayResult.transactionId, + }); + + t.assertDeepEqual(r2.type, ConfirmPayResultType.Done); + + // Check if payment was successful. + + orderStatus = succeedOrThrow( + await merchantClient.getOrderDetails( + merchantAdminAccessToken, + orderResp.order_id, + ), + ); + + t.assertDeepEqual(orderStatus.order_status, "paid"); } -runDonauTest.suites = ["wallet"]; +runDonauTest.suites = ["donau"]; diff --git a/packages/taler-util/src/types-taler-merchant.ts b/packages/taler-util/src/types-taler-merchant.ts @@ -46,6 +46,7 @@ import { codecForMap, codecForPaytoString, codecForPreciseTimestamp, + codecForStringURL, codecForTalerUriString, codecOptionalDefault, } from "./index.js"; @@ -3253,6 +3254,8 @@ export interface OrderOutputTaxReceipt { // Total amount that will be on the tax receipt. // Optional, if missing the full amount will be on the receipt. amount?: AmountString; + + donau_urls: string[]; } export interface OrderCommon { @@ -4096,6 +4099,7 @@ export const codecForOrderOutputTaxReceipt = (): Codec<OrderOutputTaxReceipt> => buildCodecForObject<OrderOutputTaxReceipt>() .property("type", codecForConstString(OrderOutputType.TaxReceipt)) .property("amount", codecOptional(codecForAmountString())) + .property("donau_urls", codecForList(codecForStringURL())) .build("TalerMerchantApi.OrderOutputTaxReceipt"); export const codecForProduct = (): Codec<Product> => diff --git a/packages/taler-util/src/types-taler-wallet.ts b/packages/taler-util/src/types-taler-wallet.ts @@ -4213,3 +4213,14 @@ export type CompleteBaseUrlResult = /** Error details in case status is not "ok" */ error: TalerErrorDetail; }; + +export interface SetDonauRequest { + donauBaseUrl: string; + taxPayerId: string; +} + +export const codecForSetDonauRequest = (): Codec<SetDonauRequest> => + buildCodecForObject<SetDonauRequest>() + .property("donauBaseUrl", codecForString()) + .property("taxPayerId", codecForString()) + .build("SetDonauRequest"); diff --git a/packages/taler-wallet-core/src/pay-merchant.ts b/packages/taler-wallet-core/src/pay-merchant.ts @@ -3016,14 +3016,22 @@ export async function confirmPay( ) { const choice = contractTerms.choices[choiceIndex]; for (let j = 0; j < choice.outputs.length; j++) { - await generateSlate( - wex, - proposal, - contractTerms, - d.contractTermsRaw, - choiceIndex, - j, - ); + switch (choice.outputs[j].type) { + case MerchantContractOutputType.Token: { + await generateSlate( + wex, + proposal, + contractTerms, + d.contractTermsRaw, + choiceIndex, + j, + ); + break; + } + case MerchantContractOutputType.TaxReceipt: + logger.warn(`tax receipt output not yet supported`); + break; + } } } @@ -3317,14 +3325,8 @@ async function processPurchasePay( }); }, ); - - if ( - slates.length !== download.contractTerms.choices[index].outputs.length - ) { - throw Error( - `number of slates ${slates.length} doesn't match number of outputs ${download.contractTerms.choices[index].outputs.length}`, - ); - } + // Note that we may have fewer slates that output tokens, + // as there are other output types (e.g. slates). } let depositPermissions: CoinDepositPermission[]; diff --git a/packages/taler-wallet-core/src/wallet-api-types.ts b/packages/taler-wallet-core/src/wallet-api-types.ts @@ -143,6 +143,7 @@ import { RetryTransactionRequest, RunFixupRequest, SetCoinSuspendedRequest, + SetDonauRequest, SetWalletDeviceIdRequest, SharePaymentRequest, SharePaymentResult, @@ -285,6 +286,9 @@ export enum WalletApiOperation { StartExchangeWalletKyc = "startExchangeWalletKyc", GetBankingChoicesForPayto = "getBankingChoicesForPayto", + // Donau + SetDonau = "setDonau", + // Stored backups ListStoredBackups = "listStoredBackups", @@ -385,6 +389,14 @@ export type HintNetworkAvailabilityOp = { response: EmptyObject; }; +// group: Donau + +export type SetDonauOp = { + op: WalletApiOperation.SetDonau; + request: SetDonauRequest; + response: EmptyObject; +}; + // group: Basic Wallet Information /** @@ -1536,6 +1548,7 @@ export type WalletOperations = { [WalletApiOperation.TestingPlanMigrateExchangeBaseUrl]: TestingPlanMigrateExchangeBaseUrlOp; [WalletApiOperation.HintApplicationResumed]: HintApplicationResumedOp; [WalletApiOperation.CompleteExchangeBaseUrl]: CompleteExchangeBaseUrlOp; + [WalletApiOperation.SetDonau]: SetDonauOp; }; export type WalletCoreRequestType< diff --git a/packages/taler-wallet-core/src/wallet.ts b/packages/taler-wallet-core/src/wallet.ts @@ -212,6 +212,7 @@ import { codecForRetryTransactionRequest, codecForRunFixupRequest, codecForSetCoinSuspendedRequest, + codecForSetDonauRequest, codecForSetWalletDeviceIdRequest, codecForSharePaymentRequest, codecForStartExchangeWalletKycRequest, @@ -300,6 +301,7 @@ import { generateDepositGroupTxId, } from "./deposits.js"; import { DevExperimentHttpLib, applyDevExperiment } from "./dev-experiments.js"; +import { handleSetDonau } from "./donau.js"; import { ReadyExchangeSummary, acceptExchangeTermsOfService, @@ -1865,6 +1867,10 @@ const handlers: { [T in WalletApiOperation]: HandlerWithValidator<T> } = { codec: codecForAny(), handler: handleTestingWaitExchangeState, }, + [WalletApiOperation.SetDonau]: { + codec: codecForSetDonauRequest(), + handler: handleSetDonau, + }, [WalletApiOperation.TestingGetDbStats]: { codec: codecForEmptyObject(), handler: async (wex) => {