taler-typescript-core

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

commit 5c3313e86637c2bb8edc0b367e2c43a78c08e50b
parent 69b9d14ed36fb663ca597986f5b6927d88981973
Author: Sebastian <sebasjm@taler-systems.com>
Date:   Fri, 24 Apr 2026 13:12:03 -0300

sync template spec with latest

Diffstat:
Mpackages/merchant-backoffice-ui/src/paths/instance/orders/create/index.tsx | 1+
Mpackages/merchant-backoffice-ui/src/paths/instance/templates/create/CreatePage.tsx | 4+++-
Mpackages/merchant-backoffice-ui/src/paths/instance/templates/update/UpdatePage.tsx | 41++++++++++++++++++++++++++++++++---------
Mpackages/merchant-backoffice-ui/src/paths/instance/templates/use/UsePage.tsx | 40+++++++++++++++++++++++++++++++++++++---
Mpackages/taler-harness/src/index.ts | 3++-
Mpackages/taler-harness/src/integrationtests/test-otp.ts | 2++
Mpackages/taler-harness/src/integrationtests/test-payment-template.ts | 2++
Mpackages/taler-util/src/types-taler-merchant.ts | 319+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------
Mpackages/taler-util/src/types-taler-wallet.ts | 4++--
Mpackages/taler-wallet-core/src/pay-merchant.ts | 2+-
Mpackages/taler-wallet-webextension/src/cta/PaymentTemplate/state.ts | 16+++++++++++-----
11 files changed, 389 insertions(+), 45 deletions(-)

diff --git a/packages/merchant-backoffice-ui/src/paths/instance/orders/create/index.tsx b/packages/merchant-backoffice-ui/src/paths/instance/orders/create/index.tsx @@ -88,6 +88,7 @@ export default function OrderCreate({ onConfirm, onBack }: Props): VNode { <CreatePage onBack={onBack} + focus onCreated={onConfirm} instanceConfig={detailsResult.body} instanceInventory={inventoryResult.body} diff --git a/packages/merchant-backoffice-ui/src/paths/instance/templates/create/CreatePage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/templates/create/CreatePage.tsx @@ -26,6 +26,7 @@ import { HttpStatusCode, TalerError, TalerMerchantApi, + TemplateType, TranslatedString, assertUnreachable, } from "@gnu-taler/taler-util"; @@ -169,7 +170,8 @@ export function CreatePage({ template_id: state.id!, template_description: state.description!, template_contract: { - minimum_age: state.minimum_age!, + template_type: TemplateType.FIXED_ORDER, + minimum_age: state.minimum_age, pay_duration: Duration.toTalerProtocolDuration(state.pay_duration!), amount: contract_amount, summary: contract_summary, diff --git a/packages/merchant-backoffice-ui/src/paths/instance/templates/update/UpdatePage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/templates/update/UpdatePage.tsx @@ -27,12 +27,14 @@ import { TalerError, TalerMerchantApi, TalerProtocolDuration, + TemplateType, TranslatedString, assertUnreachable, } from "@gnu-taler/taler-util"; import { ButtonBetterBulma, LocalNotificationBannerBulma, + NotificationCardBulma, undefinedIfEmpty, useLocalNotificationBetter, useTranslationContext, @@ -44,23 +46,21 @@ import { FormProvider, } from "../../../../components/form/FormProvider.js"; import { Input } from "../../../../components/form/Input.js"; -import { InputDurationSelector } from "../../../../components/form/InputDurationSelector.js"; +import { InputDurationDropdown } from "../../../../components/form/InputDurationDropdown.js"; import { InputNumber } from "../../../../components/form/InputNumber.js"; import { InputSelector } from "../../../../components/form/InputSelector.js"; import { InputToggle } from "../../../../components/form/InputToggle.js"; import { InputWithAddon } from "../../../../components/form/InputWithAddon.js"; import { TextField } from "../../../../components/form/TextField.js"; -import { NotificationCardBulma } from "@gnu-taler/web-util/browser"; -import { useSessionContext } from "../../../../context/session.js"; -import { WithId } from "../../../../declaration.js"; -import { useInstanceOtpDevices } from "../../../../hooks/otp.js"; -import { UIElement } from "../../../../hooks/preference.js"; import { ComponentPersonaFlag, FragmentPersonaFlag, } from "../../../../components/menu/SideBar.js"; import { Tooltip } from "../../../../components/Tooltip.js"; -import { InputDurationDropdown } from "../../../../components/form/InputDurationDropdown.js"; +import { useSessionContext } from "../../../../context/session.js"; +import { WithId } from "../../../../declaration.js"; +import { useInstanceOtpDevices } from "../../../../hooks/otp.js"; +import { UIElement } from "../../../../hooks/preference.js"; const TALER_SCREEN_ID = 65; @@ -97,14 +97,17 @@ function changeToCurrency( return Amounts.stringify(newAmount); } -export function UpdatePage({ template, onUpdated, onBack }: Props): VNode { +function UpdateFixedOrderPage({ template, onUpdated, onBack }: Props): VNode { const { i18n } = useTranslationContext(); const { config, state: session, lib } = useSessionContext(); const [notification, safeFunctionHandler] = useLocalNotificationBetter(); + if (template.template_contract.template_type !== TemplateType.FIXED_ORDER) { + return <Fragment /> + } + const cList = Object.values(config.currencies).map((d) => d.name); const supportedCurrencies = Object.keys(config.currencies); - const currentAmount = template.editable_defaults?.amount ?? (template.template_contract.amount as AmountString | undefined); @@ -206,6 +209,7 @@ export function UpdatePage({ template, onUpdated, onBack }: Props): VNode { : (state.amount as AmountString); const contract_summary = state.summary_editable ? undefined : state.summary; const template_contract: TalerMerchantApi.TemplateContractDetails = { + template_type: TemplateType.FIXED_ORDER, minimum_age: state.minimum_age!, pay_duration: state.pay_duration ? Duration.toTalerProtocolDuration(state.pay_duration) @@ -414,3 +418,22 @@ export function UpdatePage({ template, onUpdated, onBack }: Props): VNode { </div> ); } +function UpdatePaivanaPage({ template, onUpdated, onBack }: Props): VNode { + return <div>unsupported paivana template</div> +} +function UpdateInventoryPage({ template, onUpdated, onBack }: Props): VNode { + return <div>unsupported inventory template</div> +} + +export function UpdatePage(props: Props): VNode { + switch (props.template.template_contract.template_type) { + case TalerMerchantApi.TemplateType.FIXED_ORDER: + return UpdateFixedOrderPage(props); + case TalerMerchantApi.TemplateType.INVENTORY_CART: + return UpdateInventoryPage(props); + case TalerMerchantApi.TemplateType.PAIVANA: + return UpdatePaivanaPage(props); + default: + assertUnreachable(props.template.template_contract); + } +} diff --git a/packages/merchant-backoffice-ui/src/paths/instance/templates/use/UsePage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/templates/use/UsePage.tsx @@ -25,6 +25,7 @@ import { HttpStatusCode, TalerErrorCode, TalerMerchantApi, + TemplateType, UsingTemplateDetails, } from "@gnu-taler/taler-util"; import { @@ -34,7 +35,7 @@ import { useLocalNotificationBetter, useTranslationContext, } from "@gnu-taler/web-util/browser"; -import { VNode, h } from "preact"; +import { Fragment, VNode, h } from "preact"; import { useState } from "preact/hooks"; import { FormErrors, @@ -47,7 +48,7 @@ import { Tooltip } from "../../../../components/Tooltip.js"; const TALER_SCREEN_ID = 66; -type Entity = TalerMerchantApi.TemplateContractDetails; +type Entity = TalerMerchantApi.TemplateContractFixedOrder; interface Props { id: string; @@ -56,7 +57,36 @@ interface Props { onBack?: () => void; } -export function UsePage({ +export function UsePage(props: Props): VNode { + switch (props.template.template_contract.template_type) { + case TalerMerchantApi.TemplateType.FIXED_ORDER: + return UseFixedOrderPage(props); + case TalerMerchantApi.TemplateType.INVENTORY_CART: + return UseInventoryPage(props); + case TalerMerchantApi.TemplateType.PAIVANA: + return UsePaivanaPage(props); + default: + assertUnreachable(props.template.template_contract); + } +} +function UsePaivanaPage({ + id, + template, + onBack, + onOrderCreated, +}: Props): VNode { + return <div>unsupported paivana template</div>; +} +function UseInventoryPage({ + id, + template, + onBack, + onOrderCreated, +}: Props): VNode { + return <div>unsupported inventory template</div>; +} + +function UseFixedOrderPage({ id, template, onBack, @@ -66,6 +96,10 @@ export function UsePage({ const { lib } = useSessionContext(); const [notification, safeFunctionHandler] = useLocalNotificationBetter(); + if (template.template_contract.template_type !== TemplateType.FIXED_ORDER) { + return <Fragment />; + } + const [state, setState] = useState<Partial<Entity>>({ currency: template.editable_defaults?.currency ?? diff --git a/packages/taler-harness/src/index.ts b/packages/taler-harness/src/index.ts @@ -40,6 +40,7 @@ import { TalerMerchantInstanceHttpClient, TalerMerchantManagementHttpClient, TalerProtocolTimestamp, + TemplateType, TokenAuth, TransactionsResponse, createRFC8959AccessTokenEncoded, @@ -1086,10 +1087,10 @@ deploymentCli template_id: "default", template_description: "First template", template_contract: { + template_type: TemplateType.FIXED_ORDER, pay_duration: Duration.toTalerProtocolDuration( Duration.fromSpec({ hours: 1 }), ), - minimum_age: 0, currency, summary: "Pay me!", }, diff --git a/packages/taler-harness/src/integrationtests/test-otp.ts b/packages/taler-harness/src/integrationtests/test-otp.ts @@ -22,6 +22,7 @@ import { Duration, PreparePayResultType, TalerMerchantInstanceHttpClient, + TemplateType, TransactionType, j2s, randomRfc3548Base32Key, @@ -61,6 +62,7 @@ export async function runOtpTest(t: GlobalTestState) { template_id: "tpl1", otp_id: "mydevice", template_contract: { + template_type: TemplateType.FIXED_ORDER, summary: "test", amount: "TESTKUDOS:1", minimum_age: 0, diff --git a/packages/taler-harness/src/integrationtests/test-payment-template.ts b/packages/taler-harness/src/integrationtests/test-payment-template.ts @@ -23,6 +23,7 @@ import { Duration, PreparePayResultType, TalerMerchantInstanceHttpClient, + TemplateType, succeedOrThrow, } from "@gnu-taler/taler-util"; import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; @@ -52,6 +53,7 @@ export async function runPaymentTemplateTest(t: GlobalTestState) { template_id: "template1", template_description: "my test template", template_contract: { + template_type: TemplateType.FIXED_ORDER, minimum_age: 0, pay_duration: Duration.toTalerProtocolDuration( Duration.fromSpec({ diff --git a/packages/taler-util/src/types-taler-merchant.ts b/packages/taler-util/src/types-taler-merchant.ts @@ -3413,29 +3413,209 @@ export interface TemplateAddDetails { // Since protocol **v13**. editable_defaults?: TemplateContractDetailsDefaults; } -export interface TemplateContractDetails { + +export type TemplateContractDetails = + | TemplateContractFixedOrder + | TemplateContractInventoryCart + | TemplateContractPaivana; + +export enum TemplateType { + FIXED_ORDER = "fixed-order", + INVENTORY_CART = "inventory-cart", + PAIVANA = "paivana", +} +export interface TemplateContractCommon { + // Template type to apply. Defaults to "fixed-order" if omitted. + // Prescribes which interface has to be followed + // Since protocol **v25**. + template_type: TemplateType; + // Human-readable summary for the template. summary?: string; // Required currency for payments to the template. - // The user may specify any amount, but it must be - // in this currency. // This parameter is optional and should not be present // if "amount" is given. currency?: string; + // The time the customer need to pay before his order will be deleted. + // It is deleted if the customer did not pay and if the duration is over. + pay_duration?: RelativeTime; + + // How long will customers have to access / read / pick-up + // the resource they are buying? Will turn into + // max_pickup_time in the contract. Optional, if not given + // the duration is forever. + // Since protocol **v29**. + max_pickup_duration?: RelativeTime; + + // Minimum age buyer must have (in years). Default is 0. + minimum_age?: Integer; + + // Inventory-cart: request a tip during instantiation. + // Since protocol **v25**. + request_tip?: boolean; +} + +export interface TemplateContractFixedOrder extends TemplateContractCommon { + template_type: TemplateType.FIXED_ORDER; + // The price is imposed by the merchant and cannot be changed by the customer. // This parameter is optional. amount?: AmountString; +} - // Minimum age buyer must have (in years). Default is 0. - minimum_age: Integer; +export interface TemplateContractPaivana extends TemplateContractCommon { + template_type: TemplateType.PAIVANA; + // Regular expression over URLs for which + // this template is valid. + // Optional, if not given all URLs are accepted. + // Since protocol **v25**. + website_regex?: string; - // The time the customer need to pay before his order will be deleted. - // It is deleted if the customer did not pay and if the duration is over. - pay_duration: RelativeTime; + // Methods to pay for the contract. + choices: OrderChoice[]; +} + +export interface TemplateContractInventoryCart extends TemplateContractCommon { + template_type: TemplateType.INVENTORY_CART; + + // Inventory-cart: allow any inventory item to be selected. + // Since protocol **v25**. + selected_all?: boolean; + + // Inventory-cart: only products in these categories are selectable. + // Since protocol **v25**. + selected_categories?: Integer[]; + + // Inventory-cart: only these products are selectable. + // Since protocol **v25**. + selected_products?: string[]; + + // Inventory-cart: require exactly one selection entry. + // Since protocol **v25**. + choose_one?: boolean; + + // Inventory-cart: backend-provided payload with selectable data. + // Only present in GET /templates/$TEMPLATE_ID responses. + // Since protocol **v25**. + inventory_payload?: InventoryPayload; +} + +export interface InventoryPayload { + // Inventory products available for selection. + // Since protocol **v25**. + products: InventoryPayloadProduct[]; + + // Categories referenced by the payload products. + // Since protocol **v25**. + categories: InventoryPayloadCategory[]; + + // Custom units referenced by the payload products. + // Since protocol **v25**. + units: InventoryPayloadUnit[]; +} + +export interface InventoryPayloadProduct { + // Product identifier. + // Since protocol **v25**. + product_id: string; + + // Human-readable product name. + // Since protocol **v25**. + product_name: string; + + // Human-readable product description. + // Since protocol **v25**. + description: string; + + // Localized product descriptions. + // Since protocol **v25**. + description_i18n?: { [lang_tag: string]: string }; + + // Unit identifier for the product. + // Since protocol **v25**. + unit: string; + + // Price tiers for the product. + // Since protocol **v25**. + unit_prices: AmountString[]; + + // Whether fractional quantities are allowed for this unit. + // Since protocol **v25**. + unit_allow_fraction: boolean; + + // Maximum fractional precision (0-6) enforced for this unit. + // Since protocol **v25**. + unit_precision_level: Integer; + + // Remaining stock available for selection. + // Since protocol **v25**. + remaining_stock: DecimalQuantity; + + // Category identifiers associated with this product. + // Since protocol **v25**. + categories: Integer[]; + + // Taxes applied to the product. + // Since protocol **v25**. + taxes?: Tax[]; + + // Hash of the product image (if any). + // Since protocol **v25**. + image_hash?: string; +} + +export interface InventoryPayloadCategory { + // Category identifier. + // Since protocol **v25**. + category_id: Integer; + + // Human-readable category name. + // Since protocol **v25**. + category_name: string; + + // Localized category names. + // Since protocol **v25**. + category_name_i18n?: { [lang_tag: string]: string }; } +export interface InventoryPayloadUnit { + // Unit identifier. + // Since protocol **v25**. + unit: string; + + // Human-readable long label. + // Since protocol **v25**. + unit_name_long: string; + + // Localized long labels. + // Since protocol **v25**. + unit_name_long_i18n?: { [lang_tag: string]: string }; + + // Human-readable short label. + // Since protocol **v25**. + unit_name_short: string; + + // Localized short labels. + // Since protocol **v25**. + unit_name_short_i18n?: { [lang_tag: string]: string }; + + // Whether fractional quantities are allowed for this unit. + // Since protocol **v25**. + unit_allow_fraction: boolean; + + // Maximum fractional precision (0-6) enforced for this unit. + // Since protocol **v25**. + unit_precision_level: Integer; +} + +/** + * Key-value pairs matching a subset of the + * fields from template_contract that are + * user-editable defaults for this template. + * Since protocol **v13**. + */ export interface TemplateContractDetailsDefaults { summary?: string; @@ -3478,8 +3658,8 @@ export interface TemplateEntry { template_description: string; } -export interface WalletTemplateDetails { - // Hard-coded information about the contrac terms +export interface WalletTemplateDetailsResponse { + // Hard-coded information about the contract terms // for this template. template_contract: TemplateContractDetails; @@ -3488,6 +3668,17 @@ export interface WalletTemplateDetails { // user-editable defaults for this template. // Since protocol **v13**. editable_defaults?: TemplateContractDetailsDefaults; + + // Required currency for payments. Useful if no + // amount is specified in the template_contract + // but the user should be required to pay in a + // particular currency anyway. Merchant backends + // may reject requests if the template_contract + // or editable_defaults do + // specify an amount in a different currency. + // This parameter is optional. + // Since protocol **v13**. + required_currency?: string; } export interface TemplateDetails { @@ -3506,6 +3697,17 @@ export interface TemplateDetails { // user-editable defaults for this template. // Since protocol **v13**. editable_defaults?: TemplateContractDetailsDefaults; + + // Required currency for payments. Useful if no + // amount is specified in the template_contract + // but the user should be required to pay in a + // particular currency anyway. Merchant backends + // may reject requests if the template_contract + // or editable_defaults do + // specify an amount in a different currency. + // This parameter is optional. + // Since protocol **v13**. + required_currency?: string; } export interface UsingTemplateDetails { // Summary of the template @@ -5030,14 +5232,83 @@ export const codecForTemplateDetails = (): Codec<TemplateDetails> => ) .build("TalerMerchantApi.TemplateDetails"); -export const codecForTemplateContractDetails = - (): Codec<TemplateContractDetails> => - buildCodecForObject<TemplateContractDetails>() +export const codecForTemplateContractPaivana = + (): Codec<TemplateContractPaivana> => + buildCodecForObject<TemplateContractPaivana>() + .property("choices", codecForList(codecForOrderChoice())) + .property("website_regex", codecOptional(codecForString())) + + .property("currency", codecOptional(codecForString())) + .property("max_pickup_duration", codecOptional(codecForDuration)) + .property("minimum_age", codecOptional(codecForNumber())) + .property("pay_duration", codecOptional(codecForDuration)) + .property("request_tip", codecOptional(codecForBoolean())) .property("summary", codecOptional(codecForString())) + .property("template_type", codecForConstString(TemplateType.PAIVANA)) + .build("TalerMerchantApi.TemplateContractPaivana"); + +export const codecForTemplateContractInventoryCart = + (): Codec<TemplateContractInventoryCart> => + buildCodecForObject<TemplateContractInventoryCart>() + .property("choose_one", codecOptional(codecForBoolean())) + .property("selected_all", codecOptional(codecForBoolean())) + .property( + "selected_categories", + codecOptionalDefault(codecForList(codecForNumber()), []), + ) + .property( + "selected_products", + codecOptionalDefault(codecForList(codecForString()), []), + ) + .property("inventory_payload", codecOptional(codecForAny())) // FIXME: validate + .property("currency", codecOptional(codecForString())) + .property("max_pickup_duration", codecOptional(codecForDuration)) + .property("minimum_age", codecOptional(codecForNumber())) + .property("pay_duration", codecOptional(codecForDuration)) + .property("request_tip", codecOptional(codecForBoolean())) + .property("summary", codecOptional(codecForString())) + .property( + "template_type", + codecOptionalDefault( + codecForConstString(TemplateType.INVENTORY_CART), + TemplateType.INVENTORY_CART, + ), + ) + .build("TalerMerchantApi.TemplateContractInventoryCart"); +export const codecForTemplateContractFixedOrder = + (): Codec<TemplateContractFixedOrder> => + buildCodecForObject<TemplateContractFixedOrder>() .property("amount", codecOptional(codecForAmountString())) - .property("minimum_age", codecForNumber()) - .property("pay_duration", codecForDuration) + + .property("currency", codecOptional(codecForString())) + .property("max_pickup_duration", codecOptional(codecForDuration)) + .property("minimum_age", codecOptional(codecForNumber())) + .property("pay_duration", codecOptional(codecForDuration)) + .property("request_tip", codecOptional(codecForBoolean())) + .property("summary", codecOptional(codecForString())) + .property( + "template_type", + codecOptionalDefault( + codecForConstString(TemplateType.FIXED_ORDER), + TemplateType.FIXED_ORDER, + ), + ) + .build("TalerMerchantApi.TemplateContractFixedOrder"); + +export const codecForTemplateContractDetails = + (): Codec<TemplateContractDetails> => + buildCodecForUnion<TemplateContractDetails>() + .discriminateOn("template_type") + .alternative( + TemplateType.FIXED_ORDER, + codecForTemplateContractFixedOrder(), + ) + .alternative( + TemplateType.INVENTORY_CART, + codecForTemplateContractInventoryCart(), + ) + .alternative(TemplateType.PAIVANA, codecForTemplateContractPaivana()) .build("TalerMerchantApi.TemplateContractDetails"); export const codecForTemplateContractDetailsDefaults = @@ -5046,16 +5317,18 @@ export const codecForTemplateContractDetailsDefaults = .property("summary", codecOptional(codecForString())) .property("currency", codecOptional(codecForString())) .property("amount", codecOptional(codecForAmountString())) + .allowExtra() .build("TalerMerchantApi.TemplateContractDetailsDefaults"); -export const codecForWalletTemplateDetails = (): Codec<WalletTemplateDetails> => - buildCodecForObject<WalletTemplateDetails>() - .property("template_contract", codecForTemplateContractDetails()) - .property( - "editable_defaults", - codecOptional(codecForTemplateContractDetailsDefaults()), - ) - .build("TalerMerchantApi.WalletTemplateDetails"); +export const codecForWalletTemplateDetails = + (): Codec<WalletTemplateDetailsResponse> => + buildCodecForObject<WalletTemplateDetailsResponse>() + .property("template_contract", codecForTemplateContractDetails()) + .property( + "editable_defaults", + codecOptional(codecForTemplateContractDetailsDefaults()), + ) + .build("TalerMerchantApi.WalletTemplateDetails"); export const codecForWebhookSummaryResponse = (): Codec<WebhookSummaryResponse> => diff --git a/packages/taler-util/src/types-taler-wallet.ts b/packages/taler-util/src/types-taler-wallet.ts @@ -97,7 +97,7 @@ import { MerchantContractTermsV1, TokenEnvelope, TokenIssuePublicKey, - WalletTemplateDetails, + WalletTemplateDetailsResponse, codecForMerchantContractTerms, codecForMerchantContractTermsV0, } from "./types-taler-merchant.js"; @@ -2673,7 +2673,7 @@ export interface CheckPayTemplateRequest { } export type CheckPayTemplateReponse = { - templateDetails: WalletTemplateDetails; + templateDetails: WalletTemplateDetailsResponse; supportedCurrencies: string[]; }; diff --git a/packages/taler-wallet-core/src/pay-merchant.ts b/packages/taler-wallet-core/src/pay-merchant.ts @@ -2161,7 +2161,7 @@ async function downloadTemplate( wex: WalletExecutionContext, merchantBaseUrl: string, templateId: string, -): Promise<TalerMerchantApi.WalletTemplateDetails> { +): Promise<TalerMerchantApi.WalletTemplateDetailsResponse> { const reqUrl = new URL(`templates/${templateId}`, merchantBaseUrl); const httpReq = await cancelableFetch(wex, reqUrl); const resp = await readSuccessResponseJsonOrThrow( diff --git a/packages/taler-wallet-webextension/src/cta/PaymentTemplate/state.ts b/packages/taler-wallet-webextension/src/cta/PaymentTemplate/state.ts @@ -14,7 +14,7 @@ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> */ -import { Amounts, PreparePayResult } from "@gnu-taler/taler-util"; +import { Amounts, PreparePayResult, TemplateType } from "@gnu-taler/taler-util"; import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; import { useTranslationContext } from "@gnu-taler/web-util/browser"; import { useState } from "preact/hooks"; @@ -44,8 +44,10 @@ export function useComponentState({ { talerPayTemplateUri: talerTemplateUri }, ); const requireMoreInfo = - !templateP.templateDetails.template_contract.amount || - !templateP.templateDetails.template_contract.summary; + templateP.templateDetails.template_contract.template_type === + TemplateType.FIXED_ORDER && + (!templateP.templateDetails.template_contract.amount || + !templateP.templateDetails.template_contract.summary); let payStatus: PreparePayResult | undefined = undefined; if (!requireMoreInfo) { payStatus = await api.wallet.call( @@ -102,7 +104,9 @@ export function useComponentState({ const def = hook.response.templateP.templateDetails.editable_defaults; const fixedAmount = - cfg.amount !== undefined ? Amounts.parseOrThrow(cfg.amount) : undefined; + cfg.template_type === TemplateType.FIXED_ORDER && cfg.amount !== undefined + ? Amounts.parseOrThrow(cfg.amount) + : undefined; const fixedSummary = cfg.summary; const defaultAmount = @@ -122,7 +126,9 @@ export function useComponentState({ ); const [amount, setAmount] = useState(defaultAmount ?? fixedAmount ?? zero); - const [summary, setSummary] = useState(defaultSummary ?? fixedSummary ?? ""); + const [summary, setSummary] = useState( + defaultSummary ?? fixedSummary ?? "", + ); async function createOrder() { try {