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:
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) => {