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