diff options
author | Florian Dold <florian@dold.me> | 2024-05-16 13:19:17 +0200 |
---|---|---|
committer | Florian Dold <florian@dold.me> | 2024-05-16 13:19:23 +0200 |
commit | f0decd3521440d6119ad9333949ce67653d8b2c2 (patch) | |
tree | 7e14203552b811448136802fb00eeccadc04071e | |
parent | 98c188c1b14f73a6b81f41a0cacd6195bb53208e (diff) | |
download | wallet-core-f0decd3521440d6119ad9333949ce67653d8b2c2.tar.xz |
wallet-core: query templates, refactor API declarations
43 files changed, 449 insertions, 605 deletions
diff --git a/packages/auditor-backoffice-ui/src/paths/instance/templates/create/CreatePage.tsx b/packages/auditor-backoffice-ui/src/paths/instance/templates/create/CreatePage.tsx index 947f3572c..502cfea08 100644 --- a/packages/auditor-backoffice-ui/src/paths/instance/templates/create/CreatePage.tsx +++ b/packages/auditor-backoffice-ui/src/paths/instance/templates/create/CreatePage.tsx @@ -19,10 +19,7 @@ * @author Sebastian Javier Marchano (sebasjm) */ -import { - Amounts, - MerchantTemplateContractDetails, -} from "@gnu-taler/taler-util"; +import { Amounts, TalerMerchantApi } from "@gnu-taler/taler-util"; import { useTranslationContext } from "@gnu-taler/web-util/browser"; import { Fragment, VNode, h } from "preact"; import { useState } from "preact/hooks"; @@ -36,12 +33,12 @@ import { InputCurrency } from "../../../../components/form/InputCurrency.js"; import { InputDuration } from "../../../../components/form/InputDuration.js"; import { InputNumber } from "../../../../components/form/InputNumber.js"; import { InputSearchOnList } from "../../../../components/form/InputSearchOnList.js"; +import { InputTab } from "../../../../components/form/InputTab.js"; import { InputWithAddon } from "../../../../components/form/InputWithAddon.js"; import { useBackendContext } from "../../../../context/backend.js"; import { MerchantBackend } from "../../../../declaration.js"; import { useInstanceOtpDevices } from "../../../../hooks/otp.js"; import { undefinedIfEmpty } from "../../../../utils/table.js"; -import { InputTab } from "../../../../components/form/InputTab.js"; enum Steps { BOTH_FIXED, @@ -59,8 +56,8 @@ interface Props { export function CreatePage({ onCreate, onBack }: Props): VNode { const { i18n } = useTranslationContext(); - const { url: backendURL } = useBackendContext() - const devices = useInstanceOtpDevices() + const { url: backendURL } = useBackendContext(); + const devices = useInstanceOtpDevices(); const [state, setState] = useState<Partial<Entity>>({ template_contract: { @@ -88,32 +85,37 @@ export function CreatePage({ onCreate, onBack }: Props): VNode { template_contract: !state.template_contract ? undefined : undefinedIfEmpty({ - amount: !(state.type === Steps.FIXED_PRICE || state.type === Steps.BOTH_FIXED) - ? undefined - : !state.template_contract?.amount - ? i18n.str`required` - : !parsedPrice - ? i18n.str`not valid` - : Amounts.isZero(parsedPrice) - ? i18n.str`must be greater than 0` - : undefined, - summary: !(state.type === Steps.FIXED_SUMMARY || state.type === Steps.BOTH_FIXED) - ? undefined - : !state.template_contract?.summary - ? i18n.str`required` - : undefined, - minimum_age: - state.template_contract.minimum_age < 0 - ? i18n.str`should be greater that 0` - : undefined, - pay_duration: !state.template_contract.pay_duration - ? i18n.str`can't be empty` - : state.template_contract.pay_duration.d_us === "forever" + amount: !( + state.type === Steps.FIXED_PRICE || state.type === Steps.BOTH_FIXED + ) ? undefined - : state.template_contract.pay_duration.d_us < 1000 * 1000 //less than one second - ? i18n.str`to short` + : !state.template_contract?.amount + ? i18n.str`required` + : !parsedPrice + ? i18n.str`not valid` + : Amounts.isZero(parsedPrice) + ? i18n.str`must be greater than 0` + : undefined, + summary: !( + state.type === Steps.FIXED_SUMMARY || + state.type === Steps.BOTH_FIXED + ) + ? undefined + : !state.template_contract?.summary + ? i18n.str`required` + : undefined, + minimum_age: + state.template_contract.minimum_age < 0 + ? i18n.str`should be greater that 0` : undefined, - } as Partial<MerchantTemplateContractDetails>), + pay_duration: !state.template_contract.pay_duration + ? i18n.str`can't be empty` + : state.template_contract.pay_duration.d_us === "forever" + ? undefined + : state.template_contract.pay_duration.d_us < 1000 * 1000 //less than one second + ? i18n.str`to short` + : undefined, + } as Partial<TalerMerchantApi.TemplateContractDetails>), }; const hasErrors = Object.keys(errors).some( @@ -132,11 +134,11 @@ export function CreatePage({ onCreate, onBack }: Props): VNode { delete state.template_contract.summary; } } - delete state.type + delete state.type; return onCreate(state as any); }; - const deviceList = !devices.ok ? [] : devices.data.otp_devices + const deviceList = !devices.ok ? [] : devices.data.otp_devices; return ( <div> @@ -166,10 +168,14 @@ export function CreatePage({ onCreate, onBack }: Props): VNode { label={i18n.str`Type`} help={(() => { switch (state.type) { - case Steps.NON_FIXED: return i18n.str`User will be able to input price and summary before payment.` - case Steps.FIXED_PRICE: return i18n.str`User will be able to add a summary before payment.` - case Steps.FIXED_SUMMARY: return i18n.str`User will be able to set the price before payment.` - case Steps.BOTH_FIXED: return i18n.str`User will not be able to change the price or the summary.` + case Steps.NON_FIXED: + return i18n.str`User will be able to input price and summary before payment.`; + case Steps.FIXED_PRICE: + return i18n.str`User will be able to add a summary before payment.`; + case Steps.FIXED_SUMMARY: + return i18n.str`User will be able to set the price before payment.`; + case Steps.BOTH_FIXED: + return i18n.str`User will not be able to change the price or the summary.`; } })()} tooltip={i18n.str`Define what the user be allowed to modify`} @@ -181,28 +187,34 @@ export function CreatePage({ onCreate, onBack }: Props): VNode { ]} toStr={(v: Steps): string => { switch (v) { - case Steps.NON_FIXED: return i18n.str`Simple` - case Steps.FIXED_PRICE: return i18n.str`With price` - case Steps.FIXED_SUMMARY: return i18n.str`With summary` - case Steps.BOTH_FIXED: return i18n.str`With price and summary` + case Steps.NON_FIXED: + return i18n.str`Simple`; + case Steps.FIXED_PRICE: + return i18n.str`With price`; + case Steps.FIXED_SUMMARY: + return i18n.str`With summary`; + case Steps.BOTH_FIXED: + return i18n.str`With price and summary`; } }} /> - {state.type === Steps.BOTH_FIXED || state.type === Steps.FIXED_SUMMARY ? + {state.type === Steps.BOTH_FIXED || + state.type === Steps.FIXED_SUMMARY ? ( <Input name="template_contract.summary" inputType="multiline" label={i18n.str`Fixed summary`} tooltip={i18n.str`If specified, this template will create order with the same summary`} /> - : undefined} - {state.type === Steps.BOTH_FIXED || state.type === Steps.FIXED_PRICE ? + ) : undefined} + {state.type === Steps.BOTH_FIXED || + state.type === Steps.FIXED_PRICE ? ( <InputCurrency name="template_contract.amount" label={i18n.str`Fixed price`} tooltip={i18n.str`If specified, this template will create order with the same price`} /> - : undefined} + ) : undefined} <InputNumber name="template_contract.minimum_age" label={i18n.str`Minimum age`} @@ -224,12 +236,11 @@ export function CreatePage({ onCreate, onBack }: Props): VNode { <InputSearchOnList label={i18n.str`Search device`} onChange={(p) => setState((v) => ({ ...v, otp_id: p?.id }))} - list={deviceList.map(e => ({ + list={deviceList.map((e) => ({ description: e.device_description, - id: e.otp_device_id + id: e.otp_device_id, }))} /> - </FormProvider> <div class="buttons is-right mt-5"> diff --git a/packages/auditor-backoffice-ui/src/paths/instance/templates/qr/QrPage.tsx b/packages/auditor-backoffice-ui/src/paths/instance/templates/qr/QrPage.tsx index 5140aae3a..f2276b0c4 100644 --- a/packages/auditor-backoffice-ui/src/paths/instance/templates/qr/QrPage.tsx +++ b/packages/auditor-backoffice-ui/src/paths/instance/templates/qr/QrPage.tsx @@ -77,7 +77,7 @@ export function QrPage({ contract, id: templateId, onBack }: Props): VNode { const payTemplateUri = stringifyPayTemplateUri({ merchantBaseUrl, templateId, - templateParams + //templateParams }) const issuer = encodeURIComponent( diff --git a/packages/auditor-backoffice-ui/src/paths/instance/templates/update/UpdatePage.tsx b/packages/auditor-backoffice-ui/src/paths/instance/templates/update/UpdatePage.tsx index b578d4664..2b73536fb 100644 --- a/packages/auditor-backoffice-ui/src/paths/instance/templates/update/UpdatePage.tsx +++ b/packages/auditor-backoffice-ui/src/paths/instance/templates/update/UpdatePage.tsx @@ -19,10 +19,7 @@ * @author Sebastian Javier Marchano (sebasjm) */ -import { - Amounts, - MerchantTemplateContractDetails, -} from "@gnu-taler/taler-util"; +import { Amounts, TalerMerchantApi } from "@gnu-taler/taler-util"; import { useTranslationContext } from "@gnu-taler/web-util/browser"; import { Fragment, VNode, h } from "preact"; import { useState } from "preact/hooks"; @@ -35,11 +32,11 @@ import { Input } from "../../../../components/form/Input.js"; import { InputCurrency } from "../../../../components/form/InputCurrency.js"; import { InputDuration } from "../../../../components/form/InputDuration.js"; import { InputNumber } from "../../../../components/form/InputNumber.js"; +import { InputTab } from "../../../../components/form/InputTab.js"; import { InputWithAddon } from "../../../../components/form/InputWithAddon.js"; import { useBackendContext } from "../../../../context/backend.js"; import { MerchantBackend, WithId } from "../../../../declaration.js"; import { undefinedIfEmpty } from "../../../../utils/table.js"; -import { InputTab } from "../../../../components/form/InputTab.js"; enum Steps { BOTH_FIXED, @@ -58,10 +55,11 @@ interface Props { export function UpdatePage({ template, onUpdate, onBack }: Props): VNode { const { i18n } = useTranslationContext(); - const { url: backendURL } = useBackendContext() + const { url: backendURL } = useBackendContext(); const intialStep = - template.template_contract?.amount === undefined && template.template_contract?.summary === undefined + template.template_contract?.amount === undefined && + template.template_contract?.summary === undefined ? Steps.NON_FIXED : template.template_contract?.summary === undefined ? Steps.FIXED_PRICE @@ -69,7 +67,10 @@ export function UpdatePage({ template, onUpdate, onBack }: Props): VNode { ? Steps.FIXED_SUMMARY : Steps.BOTH_FIXED; - const [state, setState] = useState<Partial<Entity & { type: Steps }>>({ ...template, type: intialStep }); + const [state, setState] = useState<Partial<Entity & { type: Steps }>>({ + ...template, + type: intialStep, + }); const parsedPrice = !state.template_contract?.amount ? undefined @@ -82,32 +83,37 @@ export function UpdatePage({ template, onUpdate, onBack }: Props): VNode { template_contract: !state.template_contract ? undefined : undefinedIfEmpty({ - amount: !(state.type === Steps.FIXED_PRICE || state.type === Steps.BOTH_FIXED) - ? undefined - : !state.template_contract?.amount - ? i18n.str`required` - : !parsedPrice - ? i18n.str`not valid` - : Amounts.isZero(parsedPrice) - ? i18n.str`must be greater than 0` - : undefined, - summary: !(state.type === Steps.FIXED_SUMMARY || state.type === Steps.BOTH_FIXED) - ? undefined - : !state.template_contract?.summary - ? i18n.str`required` - : undefined, - minimum_age: - state.template_contract.minimum_age < 0 - ? i18n.str`should be greater that 0` - : undefined, - pay_duration: !state.template_contract.pay_duration - ? i18n.str`can't be empty` - : state.template_contract.pay_duration.d_us === "forever" + amount: !( + state.type === Steps.FIXED_PRICE || state.type === Steps.BOTH_FIXED + ) ? undefined - : state.template_contract.pay_duration.d_us < 1000 * 1000 // less than one second - ? i18n.str`to short` + : !state.template_contract?.amount + ? i18n.str`required` + : !parsedPrice + ? i18n.str`not valid` + : Amounts.isZero(parsedPrice) + ? i18n.str`must be greater than 0` + : undefined, + summary: !( + state.type === Steps.FIXED_SUMMARY || + state.type === Steps.BOTH_FIXED + ) + ? undefined + : !state.template_contract?.summary + ? i18n.str`required` + : undefined, + minimum_age: + state.template_contract.minimum_age < 0 + ? i18n.str`should be greater that 0` : undefined, - } as Partial<MerchantTemplateContractDetails>), + pay_duration: !state.template_contract.pay_duration + ? i18n.str`can't be empty` + : state.template_contract.pay_duration.d_us === "forever" + ? undefined + : state.template_contract.pay_duration.d_us < 1000 * 1000 // less than one second + ? i18n.str`to short` + : undefined, + } as Partial<TalerMerchantApi.TemplateContractDetails>), }; const hasErrors = Object.keys(errors).some( @@ -126,11 +132,10 @@ export function UpdatePage({ template, onUpdate, onBack }: Props): VNode { delete state.template_contract.summary; } } - delete state.type + delete state.type; return onUpdate(state as any); }; - return ( <div> <section class="section"> @@ -176,10 +181,14 @@ export function UpdatePage({ template, onUpdate, onBack }: Props): VNode { label={i18n.str`Type`} help={(() => { switch (state.type) { - case Steps.NON_FIXED: return i18n.str`User will be able to input price and summary before payment.` - case Steps.FIXED_PRICE: return i18n.str`User will be able to add a summary before payment.` - case Steps.FIXED_SUMMARY: return i18n.str`User will be able to set the price before payment.` - case Steps.BOTH_FIXED: return i18n.str`User will not be able to change the price or the summary.` + case Steps.NON_FIXED: + return i18n.str`User will be able to input price and summary before payment.`; + case Steps.FIXED_PRICE: + return i18n.str`User will be able to add a summary before payment.`; + case Steps.FIXED_SUMMARY: + return i18n.str`User will be able to set the price before payment.`; + case Steps.BOTH_FIXED: + return i18n.str`User will not be able to change the price or the summary.`; } })()} tooltip={i18n.str`Define what the user be allowed to modify`} @@ -191,28 +200,34 @@ export function UpdatePage({ template, onUpdate, onBack }: Props): VNode { ]} toStr={(v: Steps): string => { switch (v) { - case Steps.NON_FIXED: return i18n.str`Simple` - case Steps.FIXED_PRICE: return i18n.str`With price` - case Steps.FIXED_SUMMARY: return i18n.str`With summary` - case Steps.BOTH_FIXED: return i18n.str`With price and summary` + case Steps.NON_FIXED: + return i18n.str`Simple`; + case Steps.FIXED_PRICE: + return i18n.str`With price`; + case Steps.FIXED_SUMMARY: + return i18n.str`With summary`; + case Steps.BOTH_FIXED: + return i18n.str`With price and summary`; } }} /> - {state.type === Steps.BOTH_FIXED || state.type === Steps.FIXED_SUMMARY ? + {state.type === Steps.BOTH_FIXED || + state.type === Steps.FIXED_SUMMARY ? ( <Input name="template_contract.summary" inputType="multiline" label={i18n.str`Fixed summary`} tooltip={i18n.str`If specified, this template will create order with the same summary`} /> - : undefined} - {state.type === Steps.BOTH_FIXED || state.type === Steps.FIXED_PRICE ? + ) : undefined} + {state.type === Steps.BOTH_FIXED || + state.type === Steps.FIXED_PRICE ? ( <InputCurrency name="template_contract.amount" label={i18n.str`Fixed price`} tooltip={i18n.str`If specified, this template will create order with the same price`} /> - : undefined} + ) : undefined} <InputNumber name="template_contract.minimum_age" label={i18n.str`Minimum age`} diff --git a/packages/merchant-backoffice-ui/src/paths/instance/templates/qr/QrPage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/templates/qr/QrPage.tsx index 7322ca169..05cced06d 100644 --- a/packages/merchant-backoffice-ui/src/paths/instance/templates/qr/QrPage.tsx +++ b/packages/merchant-backoffice-ui/src/paths/instance/templates/qr/QrPage.tsx @@ -70,7 +70,8 @@ export function QrPage({ id: templateId, onBack }: Props): VNode { const payTemplateUri = stringifyPayTemplateUri({ merchantBaseUrl, templateId, - templateParams: {}, + // FIXME! + //templateParams: {}, }); return ( 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 index 197049486..f8e3ac9b5 100644 --- a/packages/merchant-backoffice-ui/src/paths/instance/templates/update/UpdatePage.tsx +++ b/packages/merchant-backoffice-ui/src/paths/instance/templates/update/UpdatePage.tsx @@ -51,7 +51,7 @@ type Entity = { description?: string; otpId?: string | null; summary?: string; - amount?: AmountString; + amount?: string; minimum_age?: number; pay_duration?: Duration; summary_editable?: boolean; @@ -149,7 +149,8 @@ export function UpdatePage({ template, onUpdate, onBack }: Props): VNode { template_contract: { minimum_age: state.minimum_age!, pay_duration: Duration.toTalerProtocolDuration(state.pay_duration!), - amount: state.amount_editable ? undefined : state.amount, + // FIXME: Check if amount_editable is a plain currency, in that case the user must specify it. + amount: (state.amount_editable ? undefined : state.amount) as AmountString, summary: state.summary_editable ? undefined : state.summary, currency: cList.length > 1 && state.currency_editable 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 index 360c9d373..5b1404b55 100644 --- a/packages/merchant-backoffice-ui/src/paths/instance/templates/use/UsePage.tsx +++ b/packages/merchant-backoffice-ui/src/paths/instance/templates/use/UsePage.tsx @@ -19,9 +19,9 @@ * @author Sebastian Javier Marchano (sebasjm) */ -import { TalerMerchantApi } from "@gnu-taler/taler-util"; +import { AmountString, TalerMerchantApi } from "@gnu-taler/taler-util"; import { useTranslationContext } from "@gnu-taler/web-util/browser"; -import { h, VNode } from "preact"; +import { VNode, h } from "preact"; import { useState } from "preact/hooks"; import { AsyncButton } from "../../../../components/exception/AsyncButton.js"; import { @@ -44,20 +44,19 @@ export function UsePage({ id, template, onCreateOrder, onBack }: Props): VNode { const { i18n } = useTranslationContext(); const [state, setState] = useState<Partial<Entity>>({ - currency: template.editable_defaults?.currency ?? template.template_contract.currency, - amount: template.editable_defaults?.amount ?? template.template_contract.amount, - summary: template.editable_defaults?.summary ?? template.template_contract.summary, + currency: + template.editable_defaults?.currency ?? + template.template_contract.currency, + // FIXME: Add additional check here, editable default might be a plain string! + amount: (template.editable_defaults?.amount ?? + template.template_contract.amount) as AmountString, + summary: + template.editable_defaults?.summary ?? template.template_contract.summary, }); const errors: FormErrors<Entity> = { - amount: - !state.amount - ? i18n.str`Amount is required` - : undefined, - summary: - !state.summary - ? i18n.str`Order summary is required` - : undefined, + amount: !state.amount ? i18n.str`Amount is required` : undefined, + summary: !state.summary ? i18n.str`Order summary is required` : undefined, }; const hasErrors = Object.keys(errors).some( diff --git a/packages/taler-harness/src/harness/harness.ts b/packages/taler-harness/src/harness/harness.ts index fd34fe241..136ec3d15 100644 --- a/packages/taler-harness/src/harness/harness.ts +++ b/packages/taler-harness/src/harness/harness.ts @@ -25,7 +25,6 @@ * Imports */ import { - AccountAddDetails, AccountRestriction, AmountJson, Amounts, @@ -36,8 +35,10 @@ import { Logger, MerchantInstanceConfig, PartialMerchantInstanceConfig, + PaytoString, TalerCorebankApiClient, TalerError, + TalerMerchantApi, WalletNotification, createEddsaKeyPair, eddsaGetPublic, @@ -780,8 +781,16 @@ export class LibeufinBankService config.setString("libeufin-bank", "port", `${bc.httpPort}`); config.setString("libeufin-bank", "serve", "tcp"); config.setString("libeufin-bank", "wire_type", "x-taler-bank"); - config.setString("libeufin-bank", "x_taler_bank_payto_hostname", "localhost"); - config.setString("libeufin-bank", "default_debt_limit", bc.maxDebt ?? `${bc.currency}:100`); + config.setString( + "libeufin-bank", + "x_taler_bank_payto_hostname", + "localhost", + ); + config.setString( + "libeufin-bank", + "default_debt_limit", + bc.maxDebt ?? `${bc.currency}:100`, + ); config.setString( "libeufin-bank", "DEFAULT_DEBT_LIMIT", @@ -896,9 +905,9 @@ export interface BankServiceHandle { readonly corebankApiBaseUrl: string; readonly http: HttpRequestLibrary; - setSuggestedExchange(exchange: ExchangeService, exchangePayto: string): void - start(): Promise<void> - pingUntilAvailable(): Promise<void> + setSuggestedExchange(exchange: ExchangeService, exchangePayto: string): void; + start(): Promise<void>; + pingUntilAvailable(): Promise<void>; } export type BankService = BankServiceHandle; @@ -1779,8 +1788,8 @@ export class MerchantService implements MerchantServiceInterface { const accountCreateUrl = `http://localhost:${this.merchantConfig.httpPort}/instances/${instanceConfig.id}/private/accounts`; for (const paytoUri of instanceConfig.paytoUris) { - const accountReq: AccountAddDetails = { - payto_uri: paytoUri, + const accountReq: TalerMerchantApi.AccountAddDetails = { + payto_uri: paytoUri as PaytoString, }; const acctResp = await harnessHttpLib.fetch(accountCreateUrl, { method: "POST", diff --git a/packages/taler-harness/src/harness/helpers.ts b/packages/taler-harness/src/harness/helpers.ts index efcef8706..4e3ce66b9 100644 --- a/packages/taler-harness/src/harness/helpers.ts +++ b/packages/taler-harness/src/harness/helpers.ts @@ -29,11 +29,11 @@ import { Duration, Logger, MerchantApiClient, - MerchantContractTerms, NotificationType, PartialWalletRunConfig, PreparePayResultType, TalerCorebankApiClient, + TalerMerchantApi, TransactionMajorState, WalletNotification, } from "@gnu-taler/taler-util"; @@ -100,7 +100,7 @@ export interface SimpleTestEnvironmentNg { */ export interface SimpleTestEnvironmentNg3 { commonDb: DbInfo; - bankClient: TalerCorebankApiClient, + bankClient: TalerCorebankApiClient; exchange: ExchangeService; exchangeBankAccount: HarnessExchangeBankAccount; merchant: MerchantService; @@ -808,7 +808,7 @@ export async function withdrawViaBankV3( t: GlobalTestState, p: { walletClient: WalletClient; - bankClient: TalerCorebankApiClient, + bankClient: TalerCorebankApiClient; exchange: ExchangeServiceInterface; amount: AmountString | string; restrictAge?: number; @@ -823,7 +823,10 @@ export async function withdrawViaBankV3( password: user.password, }); - const wop = await bankClient2.createWithdrawalOperation(user.username, amount); + const wop = await bankClient2.createWithdrawalOperation( + user.username, + amount, + ); // Hand it to the wallet @@ -898,7 +901,7 @@ export async function makeTestPaymentV2( args: { merchant: MerchantServiceInterface; walletClient: WalletClient; - order: Partial<MerchantContractTerms>; + order: TalerMerchantApi.Order; instance?: string; }, auth: WithAuthorization = {}, diff --git a/packages/taler-harness/src/index.ts b/packages/taler-harness/src/index.ts index 315173b7f..99b5502d8 100644 --- a/packages/taler-harness/src/index.ts +++ b/packages/taler-harness/src/index.ts @@ -900,6 +900,9 @@ deploymentCli currency, summary: "Pay me!", }, + editable_defaults: { + amount: currency, + }, }, ); if (resp.type === "fail") { @@ -915,9 +918,6 @@ deploymentCli templateURI = stringifyPayTemplateUri({ merchantBaseUrl: instanceURL, templateId: "default", - templateParams: { - amount: currency, - }, }); } diff --git a/packages/taler-harness/src/integrationtests/test-age-restrictions-merchant.ts b/packages/taler-harness/src/integrationtests/test-age-restrictions-merchant.ts index 89f285ae2..85bd96034 100644 --- a/packages/taler-harness/src/integrationtests/test-age-restrictions-merchant.ts +++ b/packages/taler-harness/src/integrationtests/test-age-restrictions-merchant.ts @@ -17,7 +17,7 @@ /** * Imports. */ -import { AmountString, MerchantApiClient } from "@gnu-taler/taler-util"; +import { AmountString, TalerMerchantApi } from "@gnu-taler/taler-util"; import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; import { defaultCoinConfig } from "../harness/denomStructures.js"; import { GlobalTestState } from "../harness/harness.js"; @@ -75,7 +75,7 @@ export async function runAgeRestrictionsMerchantTest(t: GlobalTestState) { }); await wres.withdrawalFinishedCond; - const order = { + const order: TalerMerchantApi.Order = { summary: "Buy me!", amount: "TESTKUDOS:5", fulfillment_url: "taler://fulfillment-success/thx", @@ -100,12 +100,12 @@ export async function runAgeRestrictionsMerchantTest(t: GlobalTestState) { walletClient, bankClient, exchange, - amount: "TESTKUDOS:20" as AmountString, + amount: "TESTKUDOS:20", restrictAge: 13, }); await wres.withdrawalFinishedCond; - const order = { + const order: TalerMerchantApi.Order = { summary: "Buy me!", amount: "TESTKUDOS:5", fulfillment_url: "taler://fulfillment-success/thx", @@ -131,7 +131,7 @@ export async function runAgeRestrictionsMerchantTest(t: GlobalTestState) { }); await wres.withdrawalFinishedCond; - const order = { + const order: TalerMerchantApi.Order = { summary: "Buy me!", amount: "TESTKUDOS:5", fulfillment_url: "taler://fulfillment-success/thx", @@ -155,7 +155,7 @@ export async function runAgeRestrictionsMerchantTest(t: GlobalTestState) { }); await wres.withdrawalFinishedCond; - const order = { + const order: TalerMerchantApi.Order = { summary: "Buy me!", amount: "TESTKUDOS:5", fulfillment_url: "taler://fulfillment-success/thx", diff --git a/packages/taler-harness/src/integrationtests/test-age-restrictions-mixed-merchant.ts b/packages/taler-harness/src/integrationtests/test-age-restrictions-mixed-merchant.ts index 898c7b3f7..e822b15d8 100644 --- a/packages/taler-harness/src/integrationtests/test-age-restrictions-mixed-merchant.ts +++ b/packages/taler-harness/src/integrationtests/test-age-restrictions-mixed-merchant.ts @@ -17,6 +17,7 @@ /** * Imports. */ +import { AmountString, TalerMerchantApi } from "@gnu-taler/taler-util"; import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; import { defaultCoinConfig } from "../harness/denomStructures.js"; import { GlobalTestState } from "../harness/harness.js"; @@ -26,7 +27,6 @@ import { makeTestPaymentV2, withdrawViaBankV3, } from "../harness/helpers.js"; -import { AmountString } from "@gnu-taler/taler-util"; /** * Run test for basic, bank-integrated withdrawal and payment. @@ -92,7 +92,6 @@ export async function runAgeRestrictionsMixedMerchantTest(t: GlobalTestState) { restrictAge: 13, }); - await wres.withdrawalFinishedCond; const order = { @@ -110,13 +109,12 @@ export async function runAgeRestrictionsMixedMerchantTest(t: GlobalTestState) { walletClient: walletThree, bankClient, exchange, - amount: "TESTKUDOS:20" as AmountString, + amount: "TESTKUDOS:20", }); - await wres.withdrawalFinishedCond; - const order = { + const order: TalerMerchantApi.Order = { summary: "Buy me!", amount: "TESTKUDOS:5", fulfillment_url: "taler://fulfillment-success/thx", diff --git a/packages/taler-harness/src/integrationtests/test-age-restrictions-peer.ts b/packages/taler-harness/src/integrationtests/test-age-restrictions-peer.ts index b10ecac24..c9faa586a 100644 --- a/packages/taler-harness/src/integrationtests/test-age-restrictions-peer.ts +++ b/packages/taler-harness/src/integrationtests/test-age-restrictions-peer.ts @@ -22,7 +22,6 @@ import { AmountString, Duration, NotificationType, - TalerUriAction, TransactionMajorState, TransactionMinorState, TransactionType, diff --git a/packages/taler-harness/src/integrationtests/test-clause-schnorr.ts b/packages/taler-harness/src/integrationtests/test-clause-schnorr.ts index adab764ef..c104edc85 100644 --- a/packages/taler-harness/src/integrationtests/test-clause-schnorr.ts +++ b/packages/taler-harness/src/integrationtests/test-clause-schnorr.ts @@ -17,6 +17,7 @@ /** * Imports. */ +import { TalerMerchantApi } from "@gnu-taler/taler-util"; import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; import { CoinConfig, defaultCoinConfig } from "../harness/denomStructures.js"; import { GlobalTestState } from "../harness/harness.js"; @@ -67,7 +68,7 @@ export async function runClauseSchnorrTest(t: GlobalTestState) { }); await wres.withdrawalFinishedCond; - const order = { + const order: TalerMerchantApi.Order = { summary: "Buy me!", amount: "TESTKUDOS:5", fulfillment_url: "taler://fulfillment-success/thx", @@ -78,7 +79,7 @@ export async function runClauseSchnorrTest(t: GlobalTestState) { // Test JSON normalization of contract terms: Does the wallet // agree with the merchant? - const order2 = { + const order2: TalerMerchantApi.Order = { summary: "Testing “unicode” characters", amount: "TESTKUDOS:5", fulfillment_url: "taler://fulfillment-success/thx", @@ -89,7 +90,7 @@ export async function runClauseSchnorrTest(t: GlobalTestState) { // Test JSON normalization of contract terms: Does the wallet // agree with the merchant? - const order3 = { + const order3: TalerMerchantApi.Order = { summary: "Testing\nNewlines\rAnd\tStuff\nHere\b", amount: "TESTKUDOS:5", fulfillment_url: "taler://fulfillment-success/thx", diff --git a/packages/taler-harness/src/integrationtests/test-denom-unoffered.ts b/packages/taler-harness/src/integrationtests/test-denom-unoffered.ts index 9581f9876..8042c0817 100644 --- a/packages/taler-harness/src/integrationtests/test-denom-unoffered.ts +++ b/packages/taler-harness/src/integrationtests/test-denom-unoffered.ts @@ -21,6 +21,7 @@ import { MerchantApiClient, PreparePayResultType, TalerErrorCode, + TalerMerchantApi, TransactionType, } from "@gnu-taler/taler-util"; import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; @@ -62,7 +63,7 @@ export async function runDenomUnofferedTest(t: GlobalTestState) { await merchant.start(); await merchant.pingUntilAvailable(); - const order = { + const order: TalerMerchantApi.Order = { summary: "Buy me!", amount: "TESTKUDOS:5", fulfillment_url: "taler://fulfillment-success/thx", diff --git a/packages/taler-harness/src/integrationtests/test-fee-regression.ts b/packages/taler-harness/src/integrationtests/test-fee-regression.ts index 14eb3d147..6ae7b5de8 100644 --- a/packages/taler-harness/src/integrationtests/test-fee-regression.ts +++ b/packages/taler-harness/src/integrationtests/test-fee-regression.ts @@ -17,9 +17,13 @@ /** * Imports. */ +import { + TalerCorebankApiClient, + TalerMerchantApi, +} from "@gnu-taler/taler-util"; import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; import { - BankService, + BankService, ExchangeService, GlobalTestState, MerchantService, @@ -32,7 +36,6 @@ import { makeTestPaymentV2, withdrawViaBankV3, } from "../harness/helpers.js"; -import { TalerCorebankApiClient } from "@gnu-taler/taler-util"; /** * Run a test case with a simple TESTKUDOS Taler environment, consisting @@ -72,7 +75,10 @@ export async function createMyTestkudosEnvironment( await exchange.addBankAccount("1", { accountName: exchangeBankUsername, accountPassword: exchangeBankPassword, - wireGatewayApiBaseUrl: new URL("accounts/exchange/taler-wire-gateway/", bank.baseUrl).href, + wireGatewayApiBaseUrl: new URL( + "accounts/exchange/taler-wire-gateway/", + bank.baseUrl, + ).href, accountPaytoUri: exchangePaytoUri, }); @@ -184,10 +190,10 @@ export async function createMyTestkudosEnvironment( walletService, bankClient, exchangeBankAccount: { - accountName: '', - accountPassword: '', - accountPaytoUri: '', - wireGatewayApiBaseUrl: '', + accountName: "", + accountPassword: "", + accountPaytoUri: "", + wireGatewayApiBaseUrl: "", }, }; } @@ -217,7 +223,7 @@ export async function runFeeRegressionTest(t: GlobalTestState) { // Make sure we really withdraw one 0.64 and one 1.28 coin. t.assertTrue(coins.coins.length === 2); - const order = { + const order: TalerMerchantApi.Order = { summary: "Buy me!", amount: "TESTKUDOS:1.30", fulfillment_url: "taler://fulfillment-success/thx", diff --git a/packages/taler-harness/src/integrationtests/test-multiexchange.ts b/packages/taler-harness/src/integrationtests/test-multiexchange.ts index 86fe0fb5f..b5cf0770f 100644 --- a/packages/taler-harness/src/integrationtests/test-multiexchange.ts +++ b/packages/taler-harness/src/integrationtests/test-multiexchange.ts @@ -17,7 +17,7 @@ /** * Imports. */ -import { Duration } from "@gnu-taler/taler-util"; +import { Duration, TalerMerchantApi } from "@gnu-taler/taler-util"; import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; import { defaultCoinConfig } from "../harness/denomStructures.js"; import { @@ -157,7 +157,7 @@ export async function runMultiExchangeTest(t: GlobalTestState) { await walletClient.call(WalletApiOperation.TestingWaitTransactionsFinal, {}); - const order = { + const order: TalerMerchantApi.Order = { summary: "Buy me!", amount: "TESTKUDOS:10", fulfillment_url: "taler://fulfillment-success/thx", diff --git a/packages/taler-harness/src/integrationtests/test-payment-expired.ts b/packages/taler-harness/src/integrationtests/test-payment-expired.ts index 73da165d8..3f1f7f2dd 100644 --- a/packages/taler-harness/src/integrationtests/test-payment-expired.ts +++ b/packages/taler-harness/src/integrationtests/test-payment-expired.ts @@ -22,8 +22,8 @@ import { ConfirmPayResultType, Duration, MerchantApiClient, - MerchantContractTerms, PreparePayResultType, + TalerMerchantApi, j2s, } from "@gnu-taler/taler-util"; import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; @@ -64,7 +64,7 @@ export async function runPaymentExpiredTest(t: GlobalTestState) { await walletClient.call(WalletApiOperation.TestingWaitTransactionsFinal, {}); // Order that can only be paid within five minutes. - const order: Partial<MerchantContractTerms> = { + const order: TalerMerchantApi.Order = { summary: "Buy me!", amount: "TESTKUDOS:5", fulfillment_url: "taler://fulfillment-success/thx", diff --git a/packages/taler-harness/src/integrationtests/test-payment-fault.ts b/packages/taler-harness/src/integrationtests/test-payment-fault.ts index abb589477..dabe42a6b 100644 --- a/packages/taler-harness/src/integrationtests/test-payment-fault.ts +++ b/packages/taler-harness/src/integrationtests/test-payment-fault.ts @@ -21,7 +21,11 @@ /** * Imports. */ -import { ConfirmPayResultType, MerchantApiClient, TalerCorebankApiClient } from "@gnu-taler/taler-util"; +import { + ConfirmPayResultType, + MerchantApiClient, + TalerCorebankApiClient, +} from "@gnu-taler/taler-util"; import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; import { defaultCoinConfig } from "../harness/denomStructures.js"; import { @@ -30,7 +34,7 @@ import { FaultInjectionResponseContext, } from "../harness/faultInjection.js"; import { - BankService, + BankService, ExchangeService, GlobalTestState, MerchantService, @@ -72,7 +76,10 @@ export async function runPaymentFaultTest(t: GlobalTestState) { await exchange.addBankAccount("1", { accountName: exchangeBankUsername, accountPassword: exchangeBankPassword, - wireGatewayApiBaseUrl: new URL("accounts/exchange/taler-wire-gateway/", bank.baseUrl).href, + wireGatewayApiBaseUrl: new URL( + "accounts/exchange/taler-wire-gateway/", + bank.baseUrl, + ).href, accountPaytoUri: exchangePaytoUri, }); @@ -82,10 +89,7 @@ export async function runPaymentFaultTest(t: GlobalTestState) { config.setString("exchange", "base_url", "http://localhost:8091/"); }); - bank.setSuggestedExchange( - faultyExchange, - exchangePaytoUri, - ); + bank.setSuggestedExchange(faultyExchange, exchangePaytoUri); await bank.start(); diff --git a/packages/taler-harness/src/integrationtests/test-payment-forgettable.ts b/packages/taler-harness/src/integrationtests/test-payment-forgettable.ts index e47cfe7cd..827c299a4 100644 --- a/packages/taler-harness/src/integrationtests/test-payment-forgettable.ts +++ b/packages/taler-harness/src/integrationtests/test-payment-forgettable.ts @@ -24,6 +24,7 @@ import { makeTestPaymentV2, withdrawViaBankV3, } from "../harness/helpers.js"; +import { TalerMerchantApi } from "@gnu-taler/taler-util"; /** * Run test for payment with a contract that has forgettable fields. @@ -46,7 +47,7 @@ export async function runPaymentForgettableTest(t: GlobalTestState) { await wres.withdrawalFinishedCond; { - const order = { + const order: TalerMerchantApi.Order = { summary: "Buy me!", amount: "TESTKUDOS:5", fulfillment_url: "taler://fulfillment-success/thx", @@ -64,7 +65,7 @@ export async function runPaymentForgettableTest(t: GlobalTestState) { console.log("testing with forgettable field without hash"); { - const order = { + const order: TalerMerchantApi.Order = { summary: "Buy me!", amount: "TESTKUDOS:5", fulfillment_url: "taler://fulfillment-success/thx", diff --git a/packages/taler-harness/src/integrationtests/test-payment-share.ts b/packages/taler-harness/src/integrationtests/test-payment-share.ts index 90d4a6fbb..25cfb50c6 100644 --- a/packages/taler-harness/src/integrationtests/test-payment-share.ts +++ b/packages/taler-harness/src/integrationtests/test-payment-share.ts @@ -18,6 +18,7 @@ * Imports. */ import { + AmountString, ConfirmPayResultType, MerchantApiClient, NotificationType, @@ -80,7 +81,7 @@ export async function runPaymentShareTest(t: GlobalTestState) { async function createOrder(amount: string) { const order = { summary: "Buy me!", - amount, + amount: amount as AmountString, fulfillment_url: "taler://fulfillment-success/thx", }; diff --git a/packages/taler-harness/src/integrationtests/test-payment-template.ts b/packages/taler-harness/src/integrationtests/test-payment-template.ts index 96e4de119..fb69f2571 100644 --- a/packages/taler-harness/src/integrationtests/test-payment-template.ts +++ b/packages/taler-harness/src/integrationtests/test-payment-template.ts @@ -18,6 +18,7 @@ * Imports. */ import { + AmountString, ConfirmPayResultType, Duration, MerchantApiClient, @@ -54,6 +55,9 @@ export async function runPaymentTemplateTest(t: GlobalTestState) { ), summary: "hello, I'm a summary", }, + editable_defaults: { + amount: "TESTKUDOS:1" as AmountString, + }, }); narrowOpSuccessOrThrow("createTemplate", createTemplateRes); @@ -72,7 +76,7 @@ export async function runPaymentTemplateTest(t: GlobalTestState) { const preparePayResult = await walletClient.call( WalletApiOperation.PreparePayForTemplate, { - talerPayTemplateUri: `taler+http://pay-template/localhost:${merchant.port}/template1?amount=TESTKUDOS:1`, + talerPayTemplateUri: `taler+http://pay-template/localhost:${merchant.port}/template1`, templateParams: {}, }, ); diff --git a/packages/taler-harness/src/integrationtests/test-payment.ts b/packages/taler-harness/src/integrationtests/test-payment.ts index 6037c3be2..5da6d608d 100644 --- a/packages/taler-harness/src/integrationtests/test-payment.ts +++ b/packages/taler-harness/src/integrationtests/test-payment.ts @@ -17,7 +17,7 @@ /** * Imports. */ -import { j2s } from "@gnu-taler/taler-util"; +import { TalerMerchantApi, j2s } from "@gnu-taler/taler-util"; import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; import { GlobalTestState } from "../harness/harness.js"; import { @@ -51,7 +51,7 @@ export async function runPaymentTest(t: GlobalTestState) { summary: "Buy me!", amount: "TESTKUDOS:5", fulfillment_url: "taler://fulfillment-success/thx", - }; + } satisfies TalerMerchantApi.Order; await makeTestPaymentV2(t, { walletClient, merchant, order }); await walletClient.call(WalletApiOperation.TestingWaitTransactionsFinal, {}); @@ -62,7 +62,7 @@ export async function runPaymentTest(t: GlobalTestState) { summary: "Testing “unicode” characters: 😁😱😇🥺🫦", amount: "TESTKUDOS:5", fulfillment_url: "taler://fulfillment-success/thx", - }; + } satisfies TalerMerchantApi.Order; await makeTestPaymentV2(t, { walletClient, merchant, order: order2 }); await walletClient.call(WalletApiOperation.TestingWaitTransactionsFinal, {}); @@ -73,7 +73,7 @@ export async function runPaymentTest(t: GlobalTestState) { summary: "Testing\nNewlines\rAnd\tStuff\nHere\b", amount: "TESTKUDOS:5", fulfillment_url: "taler://fulfillment-success/thx", - }; + } satisfies TalerMerchantApi.Order; await makeTestPaymentV2(t, { walletClient, merchant, order: order3 }); await walletClient.call(WalletApiOperation.TestingWaitTransactionsFinal, {}); diff --git a/packages/taler-harness/src/integrationtests/test-revocation.ts b/packages/taler-harness/src/integrationtests/test-revocation.ts index e0f77a445..65aa86f98 100644 --- a/packages/taler-harness/src/integrationtests/test-revocation.ts +++ b/packages/taler-harness/src/integrationtests/test-revocation.ts @@ -17,27 +17,29 @@ /** * Imports. */ +import { + TalerCorebankApiClient, + TalerMerchantApi, +} from "@gnu-taler/taler-util"; import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; import { CoinConfig } from "../harness/denomStructures.js"; import { - GlobalTestState, + BankService, ExchangeService, + GlobalTestState, MerchantService, WalletCli, - setupDb, + WalletClient, delayMs, generateRandomPayto, - WalletClient, - BankService, + setupDb, } from "../harness/harness.js"; import { - SimpleTestEnvironmentNg, SimpleTestEnvironmentNg3, createWalletDaemonWithClient, makeTestPaymentV2, withdrawViaBankV3, } from "../harness/helpers.js"; -import { TalerCorebankApiClient } from "@gnu-taler/taler-util"; async function revokeAllWalletCoins(req: { walletClient: WalletClient; @@ -96,7 +98,10 @@ async function createTestEnvironment( await exchange.addBankAccount("1", { accountName: exchangeBankUsername, accountPassword: exchangeBankPassword, - wireGatewayApiBaseUrl: new URL("accounts/exchange/taler-wire-gateway/", bank.baseUrl).href, + wireGatewayApiBaseUrl: new URL( + "accounts/exchange/taler-wire-gateway/", + bank.baseUrl, + ).href, accountPaytoUri: exchangePaytoUri, }); @@ -176,10 +181,10 @@ async function createTestEnvironment( walletService, bankClient, exchangeBankAccount: { - accountName: '', - accountPassword: '', - accountPaytoUri: '', - wireGatewayApiBaseUrl: '', + accountName: "", + accountPassword: "", + accountPaytoUri: "", + wireGatewayApiBaseUrl: "", }, }; } @@ -219,7 +224,7 @@ export async function runRevocationTest(t: GlobalTestState) { summary: "Buy me!", amount: "TESTKUDOS:10", fulfillment_url: "taler://fulfillment-success/thx", - }; + } satisfies TalerMerchantApi.Order; await makeTestPaymentV2(t, { walletClient, merchant, order }); diff --git a/packages/taler-harness/src/integrationtests/test-simple-payment.ts b/packages/taler-harness/src/integrationtests/test-simple-payment.ts index 58ab61435..846b8c8e1 100644 --- a/packages/taler-harness/src/integrationtests/test-simple-payment.ts +++ b/packages/taler-harness/src/integrationtests/test-simple-payment.ts @@ -24,6 +24,7 @@ import { makeTestPaymentV2, useSharedTestkudosEnvironment, } from "../harness/helpers.js"; +import { TalerMerchantApi } from "@gnu-taler/taler-util"; /** * Run test for basic, bank-integrated withdrawal and payment. @@ -49,7 +50,7 @@ export async function runSimplePaymentTest(t: GlobalTestState) { summary: "Buy me!", amount: "TESTKUDOS:5", fulfillment_url: "taler://fulfillment-success/thx", - }; + } satisfies TalerMerchantApi.Order; await makeTestPaymentV2(t, { walletClient, merchant, order }); await walletClient.call(WalletApiOperation.TestingWaitTransactionsFinal, {}); diff --git a/packages/taler-harness/src/integrationtests/test-stored-backups.ts b/packages/taler-harness/src/integrationtests/test-stored-backups.ts index a3a5e6ca3..732ac0aed 100644 --- a/packages/taler-harness/src/integrationtests/test-stored-backups.ts +++ b/packages/taler-harness/src/integrationtests/test-stored-backups.ts @@ -24,6 +24,7 @@ import { makeTestPaymentV2, useSharedTestkudosEnvironment, } from "../harness/helpers.js"; +import { TalerMerchantApi } from "@gnu-taler/taler-util"; /** * Test stored backup wallet-core API. @@ -62,7 +63,7 @@ export async function runStoredBackupsTest(t: GlobalTestState) { summary: "Buy me!", amount: "TESTKUDOS:5", fulfillment_url: "taler://fulfillment-success/thx", - }; + } satisfies TalerMerchantApi.Order; await makeTestPaymentV2(t, { walletClient, merchant, order }); await walletClient.call(WalletApiOperation.TestingWaitTransactionsFinal, {}); diff --git a/packages/taler-harness/src/integrationtests/test-wallet-balance.ts b/packages/taler-harness/src/integrationtests/test-wallet-balance.ts index 365d9495e..c37a6e482 100644 --- a/packages/taler-harness/src/integrationtests/test-wallet-balance.ts +++ b/packages/taler-harness/src/integrationtests/test-wallet-balance.ts @@ -23,6 +23,7 @@ import { MerchantApiClient, MerchantContractTerms, PreparePayResultType, + TalerMerchantApi, } from "@gnu-taler/taler-util"; import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; import { GlobalTestState } from "../harness/harness.js"; @@ -71,7 +72,7 @@ export async function runWalletBalanceTest(t: GlobalTestState) { console.log("withdrawal finished"); - const order: Partial<MerchantContractTerms> = { + const order: TalerMerchantApi.Order = { summary: "Buy me!", amount: "TESTKUDOS:5", fulfillment_url: "taler://fulfillment-success/thx", diff --git a/packages/taler-harness/src/integrationtests/test-wallet-gendb.ts b/packages/taler-harness/src/integrationtests/test-wallet-gendb.ts index 0c0180181..778f36432 100644 --- a/packages/taler-harness/src/integrationtests/test-wallet-gendb.ts +++ b/packages/taler-harness/src/integrationtests/test-wallet-gendb.ts @@ -17,22 +17,22 @@ /** * Imports. */ -import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; -import { GlobalTestState } from "../harness/harness.js"; -import { - createSimpleTestkudosEnvironmentV3, - withdrawViaBankV3, - makeTestPaymentV2, -} from "../harness/helpers.js"; import { AbsoluteTime, AmountString, Duration, NotificationType, + TalerMerchantApi, TransactionMajorState, TransactionMinorState, - j2s, } from "@gnu-taler/taler-util"; +import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; +import { GlobalTestState } from "../harness/harness.js"; +import { + createSimpleTestkudosEnvironmentV3, + makeTestPaymentV2, + withdrawViaBankV3, +} from "../harness/helpers.js"; /** * Test that creates various transactions and exports the resulting @@ -56,7 +56,7 @@ export async function runWalletGenDbTest(t: GlobalTestState) { await walletClient.call(WalletApiOperation.TestingWaitTransactionsFinal, {}); - const order = { + const order: TalerMerchantApi.Order = { summary: "Buy me!", amount: "TESTKUDOS:10", fulfillment_url: "taler://fulfillment-success/thx", diff --git a/packages/taler-harness/src/integrationtests/test-wallet-refresh.ts b/packages/taler-harness/src/integrationtests/test-wallet-refresh.ts index fbf6fe8b6..93fe94270 100644 --- a/packages/taler-harness/src/integrationtests/test-wallet-refresh.ts +++ b/packages/taler-harness/src/integrationtests/test-wallet-refresh.ts @@ -20,6 +20,7 @@ import { AmountString, NotificationType, + TalerMerchantApi, TransactionIdStr, TransactionMajorState, TransactionType, @@ -56,7 +57,7 @@ export async function runWalletRefreshTest(t: GlobalTestState) { await walletClient.call(WalletApiOperation.TestingWaitTransactionsFinal, {}); - const order = { + const order: TalerMerchantApi.Order = { summary: "Buy me!", amount: "TESTKUDOS:5", fulfillment_url: "taler://fulfillment-success/thx", diff --git a/packages/taler-harness/src/integrationtests/test-wallet-wirefees.ts b/packages/taler-harness/src/integrationtests/test-wallet-wirefees.ts index 3be4088cc..c5a0fd363 100644 --- a/packages/taler-harness/src/integrationtests/test-wallet-wirefees.ts +++ b/packages/taler-harness/src/integrationtests/test-wallet-wirefees.ts @@ -20,15 +20,15 @@ import { Duration, MerchantApiClient, - MerchantContractTerms, PreparePayResultType, TalerCorebankApiClient, + TalerMerchantApi, TransactionMajorState, } from "@gnu-taler/taler-util"; import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; import { CoinConfig, defaultCoinConfig } from "../harness/denomStructures.js"; import { - BankService, + BankService, ExchangeService, GlobalTestState, MerchantService, @@ -79,7 +79,10 @@ export async function runWalletWirefeesTest(t: GlobalTestState) { await exchange.addBankAccount("1", { accountName: exchangeBankUsername, accountPassword: exchangeBankPassword, - wireGatewayApiBaseUrl: new URL("accounts/exchange/taler-wire-gateway/", bank.baseUrl).href, + wireGatewayApiBaseUrl: new URL( + "accounts/exchange/taler-wire-gateway/", + bank.baseUrl, + ).href, accountPaytoUri: exchangePaytoUri, }); @@ -151,13 +154,13 @@ export async function runWalletWirefeesTest(t: GlobalTestState) { await walletClient.call(WalletApiOperation.TestingWaitTransactionsFinal, {}); - const order = { + const order: TalerMerchantApi.Order = { summary: "Buy me!", amount: "TESTKUDOS:1", fulfillment_url: "taler://fulfillment-success/thx", //max_wire_fee: "TESTKUDOS:0.1", max_fee: "TESTKUDOS:0.1", - } satisfies Partial<MerchantContractTerms>; + }; const merchantClient = new MerchantApiClient(merchant.makeInstanceBaseUrl()); diff --git a/packages/taler-harness/src/integrationtests/test-withdrawal-abort-bank.ts b/packages/taler-harness/src/integrationtests/test-withdrawal-abort-bank.ts index 5ae53b69d..b87e67a68 100644 --- a/packages/taler-harness/src/integrationtests/test-withdrawal-abort-bank.ts +++ b/packages/taler-harness/src/integrationtests/test-withdrawal-abort-bank.ts @@ -17,7 +17,7 @@ /** * Imports. */ -import { TalerCorebankApiClient, TalerErrorCode } from "@gnu-taler/taler-util"; +import { TalerErrorCode } from "@gnu-taler/taler-util"; import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; import { GlobalTestState } from "../harness/harness.js"; import { createSimpleTestkudosEnvironmentV3 } from "../harness/helpers.js"; diff --git a/packages/taler-harness/src/integrationtests/test-withdrawal-bank-integrated.ts b/packages/taler-harness/src/integrationtests/test-withdrawal-bank-integrated.ts index fffc6def6..a13095883 100644 --- a/packages/taler-harness/src/integrationtests/test-withdrawal-bank-integrated.ts +++ b/packages/taler-harness/src/integrationtests/test-withdrawal-bank-integrated.ts @@ -18,13 +18,12 @@ * Imports. */ import { - TalerCorebankApiClient, - j2s, NotificationType, TransactionMajorState, TransactionMinorState, TransactionType, WithdrawalType, + j2s, } from "@gnu-taler/taler-util"; import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; import { GlobalTestState } from "../harness/harness.js"; diff --git a/packages/taler-harness/src/integrationtests/test-withdrawal-conversion.ts b/packages/taler-harness/src/integrationtests/test-withdrawal-conversion.ts index a0757e357..615feafa7 100644 --- a/packages/taler-harness/src/integrationtests/test-withdrawal-conversion.ts +++ b/packages/taler-harness/src/integrationtests/test-withdrawal-conversion.ts @@ -101,7 +101,7 @@ async function runTestfakeConversionService(): Promise<TestfakeConversionService cashout_ratio: "1", cashout_rounding_mode: "zero", cashout_tiny_amount: "A:1" as AmountString, - } + }, } satisfies TalerBankConversionApi.IntegrationConfig), ); } else if (path === "/cashin-rate") { diff --git a/packages/taler-util/src/MerchantApiClient.ts b/packages/taler-util/src/MerchantApiClient.ts index c27f1d582..f58757fb5 100644 --- a/packages/taler-util/src/MerchantApiClient.ts +++ b/packages/taler-util/src/MerchantApiClient.ts @@ -19,6 +19,7 @@ import { TalerMerchantApi, codecForMerchantConfig, codecForMerchantOrderPrivateStatusResponse, + codecForPostOrderResponse, } from "./http-client/types.js"; import { HttpStatusCode } from "./http-status-codes.js"; import { @@ -31,13 +32,6 @@ import { FacadeCredentials } from "./libeufin-api-types.js"; import { LibtoolVersion } from "./libtool-version.js"; import { Logger } from "./logging.js"; import { - MerchantInstancesResponse, - MerchantPostOrderRequest, - MerchantPostOrderResponse, - MerchantTemplateAddDetails, - codecForMerchantPostOrderResponse, -} from "./merchant-api-types.js"; -import { FailCasesByMethod, OperationFail, OperationOk, @@ -206,7 +200,7 @@ export class MerchantApiClient { }); } - async getInstances(): Promise<MerchantInstancesResponse> { + async getInstances(): Promise<TalerMerchantApi.InstancesResponse> { const url = new URL("management/instances", this.baseUrl); const resp = await this.httpClient.fetch(url.href, { headers: this.makeAuthHeader(), @@ -227,18 +221,15 @@ export class MerchantApiClient { } async createOrder( - req: MerchantPostOrderRequest, - ): Promise<MerchantPostOrderResponse> { + req: TalerMerchantApi.PostOrderRequest, + ): Promise<TalerMerchantApi.PostOrderResponse> { let url = new URL("private/orders", this.baseUrl); const resp = await this.httpClient.fetch(url.href, { method: "POST", body: req, headers: this.makeAuthHeader(), }); - return readSuccessResponseJsonOrThrow( - resp, - codecForMerchantPostOrderResponse(), - ); + return readSuccessResponseJsonOrThrow(resp, codecForPostOrderResponse()); } async deleteOrder(req: { orderId: string; force?: boolean }): Promise<void> { @@ -292,7 +283,7 @@ export class MerchantApiClient { }; } - async createTemplate(req: MerchantTemplateAddDetails) { + async createTemplate(req: TalerMerchantApi.MerchantTemplateAddDetails) { let url = new URL("private/templates", this.baseUrl); const resp = await this.httpClient.fetch(url.href, { method: "POST", diff --git a/packages/taler-util/src/http-client/types.ts b/packages/taler-util/src/http-client/types.ts index fe8a2ff51..dd95709f9 100644 --- a/packages/taler-util/src/http-client/types.ts +++ b/packages/taler-util/src/http-client/types.ts @@ -1,4 +1,3 @@ -import { deprecate } from "util"; import { codecForAmountString } from "../amounts.js"; import { Codec, @@ -18,7 +17,9 @@ import { import { PaytoString, codecForPaytoString } from "../payto.js"; import { AmountString, + ExchangeWireAccount, InternationalizedString, + codecForExchangeWireAccount, codecForInternationalizedString, codecForLocation, } from "../taler-types.js"; @@ -27,7 +28,6 @@ import { AbsoluteTime, TalerProtocolDuration, TalerProtocolTimestamp, - codecForAbsoluteTime, codecForDuration, codecForTimestamp, } from "../time.js"; @@ -925,7 +925,6 @@ export const codecForTemplateContractDetailsDefaults = .property("currency", codecOptional(codecForString())) .property("amount", codecOptional(codecForAmountString())) .property("minimum_age", codecOptional(codecForNumber())) - .property("pay_duration", codecOptional(codecForDuration)) .build("TalerMerchantApi.TemplateContractDetailsDefaults"); export const codecForWalletTemplateDetails = @@ -1618,6 +1617,21 @@ export const codecForChallengerInfoResponse = .property("expires", codecForTimestamp) .build("ChallengerApi.ChallengerInfoResponse"); +export const codecForTemplateEditableDetails = + (): Codec<TalerMerchantApi.TemplateEditableDetails> => + buildCodecForObject<TalerMerchantApi.TemplateEditableDetails>() + .property("summary", codecOptional(codecForString())) + .property("currency", codecOptional(codecForString())) + .property("amount", codecOptional(codecForAmountString())) + .build("TemplateEditableDetails"); + +export const codecForMerchantReserveCreateConfirmation = + (): Codec<TalerMerchantApi.MerchantReserveCreateConfirmation> => + buildCodecForObject<TalerMerchantApi.MerchantReserveCreateConfirmation>() + .property("accounts", codecForList(codecForExchangeWireAccount())) + .property("reserve_pub", codecForString()) + .build("MerchantReserveCreateConfirmation"); + type EmailAddress = string; type PhoneNumber = string; type EddsaSignature = string; @@ -4258,9 +4272,9 @@ export namespace TalerMerchantApi { otp_id?: string; } - type Order = MinimalOrderDetail | ContractTerms; + export type Order = MinimalOrderDetail & Partial<ContractTerms>; - interface MinimalOrderDetail { + export interface MinimalOrderDetail { // Amount to be paid by the customer. amount: AmountString; @@ -4279,7 +4293,7 @@ export namespace TalerMerchantApi { fulfillment_message?: string; } - interface MinimalInventoryProduct { + export interface MinimalInventoryProduct { // Which product is requested (here mandatory!). product_id: string; @@ -4719,12 +4733,14 @@ export namespace TalerMerchantApi { currency?: string; - amount?: AmountString; + /** + * Amount *or* a plain currency string. + */ + amount?: string; minimum_age?: Integer; - - pay_duration?: RelativeTime; } + export interface TemplatePatchDetails { // Human-readable description for the template. template_description: string; @@ -5250,6 +5266,68 @@ export namespace TalerMerchantApi { // Master public key of the exchange. master_pub: EddsaPublicKey; } + + export interface MerchantReserveCreateConfirmation { + // Public key identifying the reserve. + reserve_pub: EddsaPublicKey; + + // Wire accounts of the exchange where to transfer the funds. + accounts: ExchangeWireAccount[]; + } + + export interface TemplateEditableDetails { + // 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 price is imposed by the merchant and cannot be changed by the customer. + // This parameter is optional. + amount?: AmountString; + } + + export interface MerchantTemplateContractDetails { + // Human-readable summary for the template. + summary?: string; + + // The price is imposed by the merchant and cannot be changed by the customer. + // This parameter is optional. + amount?: string; + + // Minimum age buyer must have (in years). Default is 0. + minimum_age: number; + + // 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: TalerProtocolDuration; + } + + export interface MerchantTemplateAddDetails { + // Template ID to use. + template_id: string; + + // Human-readable description for the template. + template_description: string; + + // A base64-encoded image selected by the merchant. + // This parameter is optional. + // We are not sure about it. + image?: string; + + editable_defaults?: TemplateEditableDetails; + + // Additional information in a separate template. + template_contract: MerchantTemplateContractDetails; + + // OTP device ID. + // This parameter is optional. + otp_id?: string; + } } export namespace ChallengerApi { diff --git a/packages/taler-util/src/index.ts b/packages/taler-util/src/index.ts index 24d6e9950..9f99f2f5a 100644 --- a/packages/taler-util/src/index.ts +++ b/packages/taler-util/src/index.ts @@ -18,18 +18,18 @@ export * from "./contract-terms.js"; export * from "./errors.js"; export { fnutil } from "./fnutils.js"; export * from "./helpers.js"; -export * from "./http-client/bank-conversion.js"; export * from "./http-client/authentication.js"; +export * from "./http-client/bank-conversion.js"; export * from "./http-client/bank-core.js"; -export * from "./http-client/merchant.js"; -export * from "./http-client/challenger.js"; export * from "./http-client/bank-integration.js"; export * from "./http-client/bank-revenue.js"; export * from "./http-client/bank-wire.js"; +export * from "./http-client/challenger.js"; export * from "./http-client/exchange.js"; -export { CacheEvictor } from "./http-client/utils.js"; +export * from "./http-client/merchant.js"; export * from "./http-client/officer-account.js"; export * from "./http-client/types.js"; +export { CacheEvictor } from "./http-client/utils.js"; export * from "./http-status-codes.js"; export * from "./i18n.js"; export * from "./iban.js"; @@ -38,7 +38,6 @@ export * from "./kdf.js"; export * from "./libeufin-api-types.js"; export * from "./libtool-version.js"; export * from "./logging.js"; -export * from "./merchant-api-types.js"; export { crypto_sign_keyPair_fromSeed, randomBytes, diff --git a/packages/taler-util/src/merchant-api-types.ts b/packages/taler-util/src/merchant-api-types.ts deleted file mode 100644 index 639ae8d13..000000000 --- a/packages/taler-util/src/merchant-api-types.ts +++ /dev/null @@ -1,352 +0,0 @@ -/* - This file is part of GNU Taler - (C) 2020 Taler Systems S.A. - - GNU Taler is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> - */ - -/** - * Test harness for various GNU Taler components. - * Also provides a fault-injection proxy. - * - * @author Florian Dold <dold@taler.net> - */ - -/** - * Imports. - */ -import { - AbsoluteTime, - AmountString, - Codec, - CoinPublicKeyString, - EddsaPublicKeyString, - ExchangeWireAccount, - FacadeCredentials, - MerchantContractTerms, - TalerProtocolDuration, - TalerProtocolTimestamp, - buildCodecForObject, - buildCodecForUnion, - codecForAmountString, - codecForAny, - codecForBoolean, - codecForCheckPaymentClaimedResponse, - codecForCheckPaymentUnpaidResponse, - codecForConstString, - codecForExchangeWireAccount, - codecForList, - codecForMerchantContractTerms, - codecForNumber, - codecForString, - codecForTimestamp, - codecOptional, -} from "@gnu-taler/taler-util"; - -export interface MerchantPostOrderRequest { - // The order must at least contain the minimal - // order detail, but can override all - order: Partial<MerchantContractTerms>; - - // if set, the backend will then set the refund deadline to the current - // time plus the specified delay. - refund_delay?: TalerProtocolDuration; - - // specifies the payment target preferred by the client. Can be used - // to select among the various (active) wire methods supported by the instance. - payment_target?: string; - - // FIXME: some fields are missing - - // Should a token for claiming the order be generated? - // False can make sense if the ORDER_ID is sufficiently - // high entropy to prevent adversarial claims (like it is - // if the backend auto-generates one). Default is 'true'. - create_token?: boolean; -} - -export type ClaimToken = string; - -export interface MerchantPostOrderResponse { - order_id: string; - token?: ClaimToken; -} - -export const codecForMerchantPostOrderResponse = - (): Codec<MerchantPostOrderResponse> => - buildCodecForObject<MerchantPostOrderResponse>() - .property("order_id", codecForString()) - .property("token", codecOptional(codecForString())) - .build("PostOrderResponse"); - -export const codecForMerchantRefundDetails = (): Codec<RefundDetails> => - buildCodecForObject<RefundDetails>() - .property("reason", codecForString()) - .property("pending", codecForBoolean()) - .property("amount", codecForAmountString()) - .property("timestamp", codecForTimestamp) - .build("PostOrderResponse"); - -export const codecForMerchantCheckPaymentPaidResponse = - (): Codec<MerchantCheckPaymentPaidResponse> => - buildCodecForObject<MerchantCheckPaymentPaidResponse>() - .property("order_status_url", codecForString()) - .property("order_status", codecForConstString("paid")) - .property("refunded", codecForBoolean()) - .property("wired", codecForBoolean()) - .property("deposit_total", codecForAmountString()) - .property("exchange_ec", codecForNumber()) - .property("exchange_hc", codecForNumber()) - .property("refund_amount", codecForAmountString()) - .property("contract_terms", codecForMerchantContractTerms()) - // FIXME: specify - .property("wire_details", codecForAny()) - .property("wire_reports", codecForAny()) - .property("refund_details", codecForAny()) - .build("CheckPaymentPaidResponse"); - -export type MerchantOrderPrivateStatusResponse = - | MerchantCheckPaymentPaidResponse - | CheckPaymentUnpaidResponse - | CheckPaymentClaimedResponse; - -export interface CheckPaymentClaimedResponse { - // Wallet claimed the order, but didn't pay yet. - order_status: "claimed"; - - contract_terms: MerchantContractTerms; -} - -export interface MerchantCheckPaymentPaidResponse { - // did the customer pay for this contract - order_status: "paid"; - - // Was the payment refunded (even partially) - refunded: boolean; - - // Did the exchange wire us the funds - wired: boolean; - - // Total amount the exchange deposited into our bank account - // for this contract, excluding fees. - deposit_total: AmountString; - - // Numeric error code indicating errors the exchange - // encountered tracking the wire transfer for this purchase (before - // we even got to specific coin issues). - // 0 if there were no issues. - exchange_ec: number; - - // HTTP status code returned by the exchange when we asked for - // information to track the wire transfer for this purchase. - // 0 if there were no issues. - exchange_hc: number; - - // Total amount that was refunded, 0 if refunded is false. - refund_amount: AmountString; - - // Contract terms - contract_terms: MerchantContractTerms; - - // Ihe wire transfer status from the exchange for this order if available, otherwise empty array - wire_details: TransactionWireTransfer[]; - - // Reports about trouble obtaining wire transfer details, empty array if no trouble were encountered. - wire_reports: TransactionWireReport[]; - - // The refund details for this order. One entry per - // refunded coin; empty array if there are no refunds. - refund_details: RefundDetails[]; - - order_status_url: string; -} - -export interface CheckPaymentUnpaidResponse { - order_status: "unpaid"; - - // URI that the wallet must process to complete the payment. - taler_pay_uri: string; - - order_status_url: string; - - // Alternative order ID which was paid for already in the same session. - // Only given if the same product was purchased before in the same session. - already_paid_order_id?: string; - - // We do we NOT return the contract terms here because they may not - // exist in case the wallet did not yet claim them. -} - -export interface RefundDetails { - // Reason given for the refund - reason: string; - - // when was the refund approved - timestamp: TalerProtocolTimestamp; - - // has not been taken yet - pending: boolean; - - // Total amount that was refunded (minus a refund fee). - amount: AmountString; -} - -export interface TransactionWireTransfer { - // Responsible exchange - exchange_url: string; - - // 32-byte wire transfer identifier - wtid: string; - - // execution time of the wire transfer - execution_time: AbsoluteTime; - - // Total amount that has been wire transferred - // to the merchant - amount: AmountString; - - // Was this transfer confirmed by the merchant via the - // POST /transfers API, or is it merely claimed by the exchange? - confirmed: boolean; -} - -export interface TransactionWireReport { - // Numerical error code - code: number; - - // Human-readable error description - hint: string; - - // Numerical error code from the exchange. - exchange_ec: number; - - // HTTP status code received from the exchange. - exchange_hc: number; - - // Public key of the coin for which we got the exchange error. - coin_pub: CoinPublicKeyString; -} - -export interface ReserveStatusEntry { - // Public key of the reserve - reserve_pub: string; - - // Timestamp when it was established - creation_time: AbsoluteTime; - - // Timestamp when it expires - expiration_time: AbsoluteTime; - - // Initial amount as per reserve creation call - merchant_initial_amount: AmountString; - - // Initial amount as per exchange, 0 if exchange did - // not confirm reserve creation yet. - exchange_initial_amount: AmountString; - - // Amount picked up so far. - pickup_amount: AmountString; - - // Amount approved for tips that exceeds the pickup_amount. - committed_amount: AmountString; - - // Is this reserve active (false if it was deleted but not purged) - active: boolean; -} - -export interface MerchantInstancesResponse { - // List of instances that are present in the backend (see Instance) - instances: MerchantInstanceDetail[]; -} - -export interface MerchantInstanceDetail { - // Merchant name corresponding to this instance. - name: string; - - // Merchant instance this response is about ($INSTANCE) - id: string; - - // Public key of the merchant/instance, in Crockford Base32 encoding. - merchant_pub: EddsaPublicKeyString; - - // List of the payment targets supported by this instance. Clients can - // specify the desired payment target in /order requests. Note that - // front-ends do not have to support wallets selecting payment targets. - payment_targets: string[]; -} - -export interface MerchantTemplateContractDetails { - // Human-readable summary for the template. - summary?: string; - - // The price is imposed by the merchant and cannot be changed by the customer. - // This parameter is optional. - amount?: string; - - // Minimum age buyer must have (in years). Default is 0. - minimum_age: number; - - // 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: TalerProtocolDuration; -} - -export interface MerchantTemplateAddDetails { - // Template ID to use. - template_id: string; - - // Human-readable description for the template. - template_description: string; - - // A base64-encoded image selected by the merchant. - // This parameter is optional. - // We are not sure about it. - image?: string; - - // Additional information in a separate template. - template_contract: MerchantTemplateContractDetails; - - // OTP device ID. - // This parameter is optional. - otp_id?: string; -} - -export interface MerchantReserveCreateConfirmation { - // Public key identifying the reserve. - reserve_pub: EddsaPublicKeyString; - - // Wire accounts of the exchange where to transfer the funds. - accounts: ExchangeWireAccount[]; -} - -export const codecForMerchantReserveCreateConfirmation = - (): Codec<MerchantReserveCreateConfirmation> => - buildCodecForObject<MerchantReserveCreateConfirmation>() - .property("accounts", codecForList(codecForExchangeWireAccount())) - .property("reserve_pub", codecForString()) - .build("MerchantReserveCreateConfirmation"); - -export interface AccountAddDetails { - // payto:// URI of the account. - payto_uri: string; - - // URL from where the merchant can download information - // about incoming wire transfers to this account. - credit_facade_url?: string; - - // Credentials to use when accessing the credit facade. - // Never returned on a GET (as this may be somewhat - // sensitive data). Can be set in POST - // or PATCH requests to update (or delete) credentials. - // To really delete credentials, set them to the type: "none". - credit_facade_credentials?: FacadeCredentials; -} diff --git a/packages/taler-util/src/taler-types.ts b/packages/taler-util/src/taler-types.ts index 392e7149c..e2536b74a 100644 --- a/packages/taler-util/src/taler-types.ts +++ b/packages/taler-util/src/taler-types.ts @@ -1329,8 +1329,12 @@ export const codecForDenominationPubKey = () => .alternative(DenomKeyType.ClauseSchnorr, codecForCsDenominationPubKey()) .build("DenominationPubKey"); +export type LitAmountString = `${string}:${number}`; + declare const __amount_str: unique symbol; -export type AmountString = string & { [__amount_str]: true }; +export type AmountString = + | (string & { [__amount_str]: true }) + | LitAmountString; // export type AmountString = string; export type Base32String = string; export type EddsaSignatureString = string; diff --git a/packages/taler-util/src/taleruri.test.ts b/packages/taler-util/src/taleruri.test.ts index b751efa34..b92366fb3 100644 --- a/packages/taler-util/src/taleruri.test.ts +++ b/packages/taler-util/src/taleruri.test.ts @@ -314,7 +314,7 @@ test("taler peer to peer pull URI (stringify)", (t) => { test("taler pay template URI (parsing)", (t) => { const url1 = - "taler://pay-template/merchant.example.com/FEGHYJY48FEGU6WETYIOIDEDE2QW3OCZVY?amount=KUDOS:5"; + "taler://pay-template/merchant.example.com/FEGHYJY48FEGU6WETYIOIDEDE2QW3OCZVY"; const r1 = parsePayTemplateUri(url1); if (!r1) { t.fail(); @@ -322,12 +322,11 @@ test("taler pay template URI (parsing)", (t) => { } t.deepEqual(r1.merchantBaseUrl, "https://merchant.example.com/"); t.deepEqual(r1.templateId, "FEGHYJY48FEGU6WETYIOIDEDE2QW3OCZVY"); - t.deepEqual(r1.templateParams.amount, "KUDOS:5"); }); test("taler pay template URI (parsing, http with port)", (t) => { const url1 = - "taler+http://pay-template/merchant.example.com:1234/FEGHYJY48FEGU6WETYIOIDEDE2QW3OCZVY?amount=KUDOS:5"; + "taler+http://pay-template/merchant.example.com:1234/FEGHYJY48FEGU6WETYIOIDEDE2QW3OCZVY"; const r1 = parsePayTemplateUri(url1); if (!r1) { t.fail(); @@ -335,20 +334,16 @@ test("taler pay template URI (parsing, http with port)", (t) => { } t.deepEqual(r1.merchantBaseUrl, "http://merchant.example.com:1234/"); t.deepEqual(r1.templateId, "FEGHYJY48FEGU6WETYIOIDEDE2QW3OCZVY"); - t.deepEqual(r1.templateParams.amount, "KUDOS:5"); }); test("taler pay template URI (stringify)", (t) => { const url1 = stringifyPayTemplateUri({ merchantBaseUrl: "http://merchant.example.com:1234/", templateId: "FEGHYJY48FEGU6WETYIOIDEDE2QW3OCZVY", - templateParams: { - amount: "KUDOS:5", - }, }); t.deepEqual( url1, - "taler+http://pay-template/merchant.example.com:1234/FEGHYJY48FEGU6WETYIOIDEDE2QW3OCZVY?amount=KUDOS%3A5", + "taler+http://pay-template/merchant.example.com:1234/FEGHYJY48FEGU6WETYIOIDEDE2QW3OCZVY", ); }); diff --git a/packages/taler-util/src/taleruri.ts b/packages/taler-util/src/taleruri.ts index 576736503..54b7525e3 100644 --- a/packages/taler-util/src/taleruri.ts +++ b/packages/taler-util/src/taleruri.ts @@ -83,7 +83,6 @@ export interface PayTemplateUriResult { type: TalerUriAction.PayTemplate; merchantBaseUrl: string; templateId: string; - templateParams: TemplateParams; } export interface WithdrawUriResult { @@ -437,7 +436,6 @@ export function parsePayTemplateUri( type: TalerUriAction.PayTemplate, merchantBaseUrl, templateId, - templateParams: params, }; } @@ -668,9 +666,8 @@ export function stringifyDevExperimentUri({ export function stringifyPayTemplateUri({ merchantBaseUrl, templateId, - templateParams, }: Omit<PayTemplateUriResult, "type">): string { - const { proto, path, query } = getUrlInfo(merchantBaseUrl, templateParams); + const { proto, path, query } = getUrlInfo(merchantBaseUrl); return `${proto}://pay-template/${path}${templateId}${query}`; } diff --git a/packages/taler-util/src/wallet-types.ts b/packages/taler-util/src/wallet-types.ts index 4e2e08a8b..310ca858e 100644 --- a/packages/taler-util/src/wallet-types.ts +++ b/packages/taler-util/src/wallet-types.ts @@ -2031,6 +2031,16 @@ export const codecForSharePaymentResult = (): Codec<SharePaymentResult> => .property("privatePayUri", codecForString()) .build("SharePaymentResult"); +export interface CheckPayTemplateRequest { + talerPayTemplateUri: string; +} + +export const codecForCheckPayTemplateRequest = + (): Codec<CheckPayTemplateRequest> => + buildCodecForObject<CheckPayTemplateRequest>() + .property("talerPayTemplateUri", codecForString()) + .build("CheckPayTemplateRequest"); + export interface PreparePayTemplateRequest { talerPayTemplateUri: string; templateParams?: TemplateParams; diff --git a/packages/taler-wallet-core/src/pay-merchant.ts b/packages/taler-wallet-core/src/pay-merchant.ts index 4a2ef009a..f08db3a6a 100644 --- a/packages/taler-wallet-core/src/pay-merchant.ts +++ b/packages/taler-wallet-core/src/pay-merchant.ts @@ -34,13 +34,15 @@ import { assertUnreachable, AsyncFlag, checkDbInvariant, + CheckPayTemplateRequest, codecForAbortResponse, codecForMerchantContractTerms, codecForMerchantOrderStatusPaid, codecForMerchantPayResponse, - codecForMerchantPostOrderResponse, + codecForPostOrderResponse, codecForProposal, codecForWalletRefundResponse, + codecForWalletTemplateDetails, CoinDepositPermission, CoinRefreshRequest, ConfirmPayResult, @@ -76,6 +78,7 @@ import { TalerError, TalerErrorCode, TalerErrorDetail, + TalerMerchantApi, TalerPreciseTimestamp, TalerProtocolViolationError, TalerUriAction, @@ -1578,18 +1581,56 @@ async function internalWaitProposalDownloaded( } } +async function downloadTemplate( + wex: WalletExecutionContext, + merchantBaseUrl: string, + templateId: string, +): Promise<TalerMerchantApi.WalletTemplateDetails> { + const reqUrl = new URL(`templates/${templateId}`, merchantBaseUrl); + const httpReq = await wex.http.fetch(reqUrl.href, { + method: "GET", + cancellationToken: wex.cancellationToken, + }); + const resp = await readSuccessResponseJsonOrThrow( + httpReq, + codecForWalletTemplateDetails(), + ); + return resp; +} + +export async function checkPayForTemplate( + wex: WalletExecutionContext, + req: CheckPayTemplateRequest, +): Promise<TalerMerchantApi.WalletTemplateDetails> { + const parsedUri = parsePayTemplateUri(req.talerPayTemplateUri); + if (!parsedUri) { + throw Error("invalid taler-template URI"); + } + return await downloadTemplate( + wex, + parsedUri.merchantBaseUrl, + parsedUri.templateId, + ); +} + export async function preparePayForTemplate( wex: WalletExecutionContext, req: PreparePayTemplateRequest, ): Promise<PreparePayResult> { const parsedUri = parsePayTemplateUri(req.talerPayTemplateUri); - const templateDetails: MerchantUsingTemplateDetails = {}; 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, + ); - const amountFromUri = parsedUri.templateParams.amount; + const amountFromUri = templateInfo.editable_defaults?.amount; if (amountFromUri != null) { const templateParamsAmount = req.templateParams?.amount; if (templateParamsAmount != null) { @@ -1605,11 +1646,11 @@ export async function preparePayForTemplate( } } if ( - parsedUri.templateParams.summary !== undefined && - typeof parsedUri.templateParams.summary === "string" + templateInfo.editable_defaults?.summary !== undefined && + typeof templateInfo.editable_defaults?.summary === "string" ) { templateDetails.summary = - req.templateParams?.summary ?? parsedUri.templateParams.summary; + req.templateParams?.summary ?? templateInfo.editable_defaults?.summary; } const reqUrl = new URL( `templates/${parsedUri.templateId}`, @@ -1621,7 +1662,7 @@ export async function preparePayForTemplate( }); const resp = await readSuccessResponseJsonOrThrow( httpReq, - codecForMerchantPostOrderResponse(), + codecForPostOrderResponse(), ); const payUri = stringifyPayUri({ diff --git a/packages/taler-wallet-core/src/wallet-api-types.ts b/packages/taler-wallet-core/src/wallet-api-types.ts index 6fe2422bc..2a1b7d170 100644 --- a/packages/taler-wallet-core/src/wallet-api-types.ts +++ b/packages/taler-wallet-core/src/wallet-api-types.ts @@ -40,6 +40,7 @@ import { BalancesResponse, CanonicalizeBaseUrlRequest, CanonicalizeBaseUrlResponse, + CheckPayTemplateRequest, CheckPeerPullCreditRequest, CheckPeerPullCreditResponse, CheckPeerPushDebitRequest, @@ -121,6 +122,7 @@ import { StartRefundQueryForUriResponse, StartRefundQueryRequest, StoredBackupList, + TalerMerchantApi, TestPayArgs, TestPayResult, TestingGetDenomStatsRequest, @@ -165,6 +167,7 @@ export enum WalletApiOperation { WithdrawTestBalance = "withdrawTestBalance", PreparePayForUri = "preparePayForUri", SharePayment = "sharePayment", + CheckPayForTemplate = "checkPayForTemplate", PreparePayForTemplate = "preparePayForTemplate", GetContractTermsDetails = "getContractTermsDetails", RunIntegrationTest = "runIntegrationTest", @@ -547,6 +550,12 @@ export type SharePaymentOp = { response: SharePaymentResult; }; +export type CheckPayForTemplateOp = { + op: WalletApiOperation.CheckPayForTemplate; + request: CheckPayTemplateRequest; + response: TalerMerchantApi.WalletTemplateDetails; +}; + /** * Prepare to make a payment based on a taler://pay-template/ URI. */ @@ -1237,6 +1246,7 @@ export type WalletOperations = { [WalletApiOperation.GetVersion]: GetVersionOp; [WalletApiOperation.PreparePayForUri]: PreparePayForUriOp; [WalletApiOperation.SharePayment]: SharePaymentOp; + [WalletApiOperation.CheckPayForTemplate]: CheckPayForTemplateOp; [WalletApiOperation.PreparePayForTemplate]: PreparePayForTemplateOp; [WalletApiOperation.GetContractTermsDetails]: GetContractTermsDetailsOp; [WalletApiOperation.WithdrawTestkudos]: WithdrawTestkudosOp; diff --git a/packages/taler-wallet-core/src/wallet.ts b/packages/taler-wallet-core/src/wallet.ts index 9262904b5..336817be9 100644 --- a/packages/taler-wallet-core/src/wallet.ts +++ b/packages/taler-wallet-core/src/wallet.ts @@ -84,6 +84,7 @@ import { codecForAny, codecForApplyDevExperiment, codecForCanonicalizeBaseUrlRequest, + codecForCheckPayTemplateRequest, codecForCheckPeerPullPaymentRequest, codecForCheckPeerPushDebitRequest, codecForConfirmPayRequest, @@ -229,6 +230,7 @@ import { observeTalerCrypto, } from "./observable-wrappers.js"; import { + checkPayForTemplate, confirmPay, getContractTermsDetails, preparePayForTemplate, @@ -1052,6 +1054,10 @@ async function dispatchRequestInternal( const req = codecForPrepareWithdrawExchangeRequest().decode(payload); return handlePrepareWithdrawExchange(wex, req); } + case WalletApiOperation.CheckPayForTemplate: { + const req = codecForCheckPayTemplateRequest().decode(payload); + return await checkPayForTemplate(wex, req); + } case WalletApiOperation.PreparePayForUri: { const req = codecForPreparePayRequest().decode(payload); return await preparePayForUri(wex, req.talerPayUri); |