taler-typescript-core

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

commit c4ce5227b13fb241ed037f7d486aa91ef05c6a7b
parent 5c3313e86637c2bb8edc0b367e2c43a78c08e50b
Author: Sebastian <sebasjm@taler-systems.com>
Date:   Fri, 24 Apr 2026 17:46:38 -0300

fix #11356

Diffstat:
Mpackages/merchant-backoffice-ui/src/paths/instance/templates/use/UsePage.tsx | 9+++++----
Mpackages/taler-util/src/http-client/merchant.ts | 4++--
Mpackages/taler-util/src/taleruri.ts | 23++++++++++++++++++++---
Mpackages/taler-util/src/types-taler-common.ts | 5-----
Mpackages/taler-util/src/types-taler-merchant.ts | 61++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---
Mpackages/taler-wallet-core/src/pay-merchant.ts | 34+++++++++++++++++++++++++++++++---
Mpackages/taler-wallet-webextension/clean_and_build.sh | 1-
7 files changed, 116 insertions(+), 21 deletions(-)

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 @@ -26,7 +26,7 @@ import { TalerErrorCode, TalerMerchantApi, TemplateType, - UsingTemplateDetails, + UsingTemplateDetailsRequest, } from "@gnu-taler/taler-util"; import { ButtonBetterBulma, @@ -35,7 +35,7 @@ import { useLocalNotificationBetter, useTranslationContext, } from "@gnu-taler/web-util/browser"; -import { Fragment, VNode, h } from "preact"; +import { Fragment, h, VNode } from "preact"; import { useState } from "preact/hooks"; import { FormErrors, @@ -43,8 +43,8 @@ import { } from "../../../../components/form/FormProvider.js"; import { Input } from "../../../../components/form/Input.js"; import { InputWithAddon } from "../../../../components/form/InputWithAddon.js"; -import { useSessionContext } from "../../../../context/session.js"; import { Tooltip } from "../../../../components/Tooltip.js"; +import { useSessionContext } from "../../../../context/session.js"; const TALER_SCREEN_ID = 66; @@ -121,7 +121,8 @@ function UseFixedOrderPage({ /** * If the template already has fields then do not send those. */ - const details: UsingTemplateDetails = { + const details: UsingTemplateDetailsRequest = { + template_type: TemplateType.FIXED_ORDER, amount: template.template_contract.amount ? undefined : state.amount, summary: template.template_contract.summary ? undefined : state.summary, }; diff --git a/packages/taler-util/src/http-client/merchant.ts b/packages/taler-util/src/http-client/merchant.ts @@ -2473,11 +2473,11 @@ export class TalerMerchantInstanceHttpClient { } /** - * https://docs.taler.net/core/api-merchant.html#post-[-instances-$INSTANCES]-templates-$TEMPLATE_ID + * https://docs.taler.net/core/api-merchant.html#post-[-instances-$INSTANCE]-templates-$TEMPLATE_ID */ async useTemplateCreateOrder( templateId: string, - body: TalerMerchantApi.UsingTemplateDetails, + body: TalerMerchantApi.UsingTemplateDetailsRequest, ) { const url = new URL(`templates/${templateId}`, this.baseUrl); diff --git a/packages/taler-util/src/taleruri.ts b/packages/taler-util/src/taleruri.ts @@ -181,11 +181,17 @@ export namespace TalerUris { export function createTalerPayTemplate( merchantBaseUrl: HostPortPath, templateId: string, + opts: { + sessionId?: string; + fulfillmenURL?: string; + } = {}, ): PayTemplateUriResult { return { type: TalerUriAction.PayTemplate, merchantBaseUrl, templateId, + fulfillmentUrl: opts.fulfillmenURL, + sessionId: opts.sessionId, }; } export function createTalerRestore( @@ -464,7 +470,7 @@ export namespace TalerUris { > { // check prefix let isHttp = false; - const prefixCheck = opts.ignoreUppercase ? s.toLowerCase() : s + const prefixCheck = opts.ignoreUppercase ? s.toLowerCase() : s; if ( !prefixCheck.startsWith(TALER_PREFIX) && !(isHttp = prefixCheck.startsWith(TALER_HTTP_PREFIX)) @@ -484,7 +490,9 @@ export namespace TalerUris { firstSlashPos === -1 ? path : path.slice(0, firstSlashPos) ) as TalerUriAction; - const uriType = opts.ignoreUppercase ? uriTypeUncased.toLowerCase() as TalerUriAction : uriTypeUncased; + const uriType = opts.ignoreUppercase + ? (uriTypeUncased.toLowerCase() as TalerUriAction) + : uriTypeUncased; if (!supported_targets[uriType]) { return Result.errorWithDetail(TalerUriParseError.UNSUPPORTED, { @@ -956,6 +964,8 @@ export interface PayTemplateUriResult { type: TalerUriAction.PayTemplate; merchantBaseUrl: HostPortPath; templateId: string; + sessionId?: string; + fulfillmentUrl?: string; } export interface WithdrawUriResult { @@ -1451,6 +1461,8 @@ export function parsePayTemplateUri( type: TalerUriAction.PayTemplate, merchantBaseUrl, templateId, + fulfillmentUrl: q.get("fulfillment_url") ?? undefined, + sessionId: q.get("session_id") ?? undefined, }; } @@ -1803,8 +1815,13 @@ export function stringifyDevExperimentUri({ export function stringifyPayTemplateUri({ merchantBaseUrl, templateId, + fulfillmentUrl, + sessionId, }: Omit<PayTemplateUriResult, "type">): string { - const { proto, path, query } = getUrlInfo(merchantBaseUrl); + const { proto, path, query } = getUrlInfo(merchantBaseUrl, { + session_id: sessionId, + fulfillment_url: fulfillmentUrl, + }); return `${proto}://pay-template/${path}${templateId}${query}`; } diff --git a/packages/taler-util/src/types-taler-common.ts b/packages/taler-util/src/types-taler-common.ts @@ -269,11 +269,6 @@ export type HashCodeString = string; export type WireSalt = string; -export interface MerchantUsingTemplateDetails { - summary?: string; - amount?: AmountString; -} - export type Integer = number; export interface BankConversionInfoConfig { diff --git a/packages/taler-util/src/types-taler-merchant.ts b/packages/taler-util/src/types-taler-merchant.ts @@ -3709,14 +3709,69 @@ export interface TemplateDetails { // Since protocol **v13**. required_currency?: string; } -export interface UsingTemplateDetails { - // Summary of the template + +export type UsingTemplateDetailsRequest = (UsingTemplateFixedOrderRequest | UsingTemplateInventoryCartRequest | UsingTemplatePaivanaRequest) & UsingTemplateCommonRequest; + +export interface UsingTemplateCommonRequest { + + // Type of the template being instantiated. + // Possible values include "fixed-order", + // "inventory-cart" and "paivana". + // Since protocol **v25**. + // Defaults to "fixed-order" while supporting previous + // protocol versions. + template_type: string; + + // Summary to use in the contract. Only if + // not already specified by the template. summary?: string; - // The amount entered by the customer. + // The amount to be paid, including tip. amount?: AmountString; + + // Optional tip amount. Must match the currency of amount or the + // fixed template currency. + // Since protocol **v25**. + tip?: AmountString; + +} + +export interface UsingTemplateFixedOrderRequest { + template_type: TemplateType.FIXED_ORDER; + } +export interface UsingTemplateInventoryCartRequest { + template_type: TemplateType.INVENTORY_CART; + + // Inventory-cart: selected products and quantities. + // Since protocol **v25**. + inventory_selection?: InventorySelectionEntry[]; +} + +export interface InventorySelectionEntry { + // Inventory product to add. + product_id: string; + + // Quantity in "<integer>[.<fraction>]" form using the product unit rules. + quantity: DecimalQuantity; +} + +export interface UsingTemplatePaivanaRequest { + template_type: TemplateType.PAIVANA; + + // Website to which access is being sold. + // Will become the fulfillment URL in the contract. + website: string; + + // Client Paivana ID to grant access to. + // This becomes the "session_id" for session-based + // access control. + paivana_id: string; + +} + + export interface WebhookAddDetails { // Webhook ID to use. webhook_id: string; diff --git a/packages/taler-wallet-core/src/pay-merchant.ts b/packages/taler-wallet-core/src/pay-merchant.ts @@ -75,7 +75,6 @@ import { MerchantContractTermsV1, MerchantContractVersion, MerchantPayResponse, - MerchantUsingTemplateDetails, NotificationType, OrderShortInfo, parsePayTemplateUri, @@ -106,6 +105,8 @@ import { TalerMerchantInstanceHttpClient, TalerPreciseTimestamp, TalerUriAction, + TalerUris, + TemplateType, TokenUseSig, Transaction, TransactionAction, @@ -115,6 +116,7 @@ import { TransactionState, TransactionType, URL, + UsingTemplateDetailsRequest, WalletNotification, } from "@gnu-taler/taler-util"; import { @@ -2215,18 +2217,44 @@ export async function preparePayForTemplate( req: PreparePayTemplateRequest, ): Promise<PreparePayResult> { const parsedUri = parsePayTemplateUri(req.talerPayTemplateUri); + if (!parsedUri) { throw Error("invalid taler-template URI"); } logger.trace(`parsed URI: ${j2s(parsedUri)}`); - const templateDetails: MerchantUsingTemplateDetails = {}; - const templateInfo = await downloadTemplate( wex, parsedUri.merchantBaseUrl, parsedUri.templateId, ); + let templateDetails: UsingTemplateDetailsRequest; + switch (templateInfo.template_contract.template_type) { + case TalerMerchantApi.TemplateType.FIXED_ORDER: { + templateDetails = { + template_type: TemplateType.FIXED_ORDER, + }; + break; + } + + case TalerMerchantApi.TemplateType.INVENTORY_CART: { + templateDetails = { + template_type: TemplateType.INVENTORY_CART, + }; + break; + } + case TalerMerchantApi.TemplateType.PAIVANA: { + templateDetails = { + template_type: TemplateType.PAIVANA, + paivana_id: parsedUri.sessionId!, + website: parsedUri.fulfillmentUrl!, + }; + break; + } + default: + assertUnreachable(templateInfo.template_contract); + } + const templateParamsAmount = req.templateParams?.amount as | AmountString | undefined; diff --git a/packages/taler-wallet-webextension/clean_and_build.sh b/packages/taler-wallet-webextension/clean_and_build.sh @@ -14,5 +14,4 @@ echo testing... pnpm test -- -R dot echo packing... -rm -rf extension/ ./pack.sh dev