aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFlorian Dold <florian@dold.me>2024-05-16 13:19:17 +0200
committerFlorian Dold <florian@dold.me>2024-05-16 13:19:23 +0200
commitf0decd3521440d6119ad9333949ce67653d8b2c2 (patch)
tree7e14203552b811448136802fb00eeccadc04071e
parent98c188c1b14f73a6b81f41a0cacd6195bb53208e (diff)
downloadwallet-core-f0decd3521440d6119ad9333949ce67653d8b2c2.tar.xz
wallet-core: query templates, refactor API declarations
-rw-r--r--packages/auditor-backoffice-ui/src/paths/instance/templates/create/CreatePage.tsx107
-rw-r--r--packages/auditor-backoffice-ui/src/paths/instance/templates/qr/QrPage.tsx2
-rw-r--r--packages/auditor-backoffice-ui/src/paths/instance/templates/update/UpdatePage.tsx107
-rw-r--r--packages/merchant-backoffice-ui/src/paths/instance/templates/qr/QrPage.tsx3
-rw-r--r--packages/merchant-backoffice-ui/src/paths/instance/templates/update/UpdatePage.tsx5
-rw-r--r--packages/merchant-backoffice-ui/src/paths/instance/templates/use/UsePage.tsx25
-rw-r--r--packages/taler-harness/src/harness/harness.ts25
-rw-r--r--packages/taler-harness/src/harness/helpers.ts13
-rw-r--r--packages/taler-harness/src/index.ts6
-rw-r--r--packages/taler-harness/src/integrationtests/test-age-restrictions-merchant.ts12
-rw-r--r--packages/taler-harness/src/integrationtests/test-age-restrictions-mixed-merchant.ts8
-rw-r--r--packages/taler-harness/src/integrationtests/test-age-restrictions-peer.ts1
-rw-r--r--packages/taler-harness/src/integrationtests/test-clause-schnorr.ts7
-rw-r--r--packages/taler-harness/src/integrationtests/test-denom-unoffered.ts3
-rw-r--r--packages/taler-harness/src/integrationtests/test-fee-regression.ts22
-rw-r--r--packages/taler-harness/src/integrationtests/test-multiexchange.ts4
-rw-r--r--packages/taler-harness/src/integrationtests/test-payment-expired.ts4
-rw-r--r--packages/taler-harness/src/integrationtests/test-payment-fault.ts18
-rw-r--r--packages/taler-harness/src/integrationtests/test-payment-forgettable.ts5
-rw-r--r--packages/taler-harness/src/integrationtests/test-payment-share.ts3
-rw-r--r--packages/taler-harness/src/integrationtests/test-payment-template.ts6
-rw-r--r--packages/taler-harness/src/integrationtests/test-payment.ts8
-rw-r--r--packages/taler-harness/src/integrationtests/test-revocation.ts29
-rw-r--r--packages/taler-harness/src/integrationtests/test-simple-payment.ts3
-rw-r--r--packages/taler-harness/src/integrationtests/test-stored-backups.ts3
-rw-r--r--packages/taler-harness/src/integrationtests/test-wallet-balance.ts3
-rw-r--r--packages/taler-harness/src/integrationtests/test-wallet-gendb.ts18
-rw-r--r--packages/taler-harness/src/integrationtests/test-wallet-refresh.ts3
-rw-r--r--packages/taler-harness/src/integrationtests/test-wallet-wirefees.ts13
-rw-r--r--packages/taler-harness/src/integrationtests/test-withdrawal-abort-bank.ts2
-rw-r--r--packages/taler-harness/src/integrationtests/test-withdrawal-bank-integrated.ts3
-rw-r--r--packages/taler-harness/src/integrationtests/test-withdrawal-conversion.ts2
-rw-r--r--packages/taler-util/src/MerchantApiClient.ts21
-rw-r--r--packages/taler-util/src/http-client/types.ts96
-rw-r--r--packages/taler-util/src/index.ts9
-rw-r--r--packages/taler-util/src/merchant-api-types.ts352
-rw-r--r--packages/taler-util/src/taler-types.ts6
-rw-r--r--packages/taler-util/src/taleruri.test.ts11
-rw-r--r--packages/taler-util/src/taleruri.ts5
-rw-r--r--packages/taler-util/src/wallet-types.ts10
-rw-r--r--packages/taler-wallet-core/src/pay-merchant.ts55
-rw-r--r--packages/taler-wallet-core/src/wallet-api-types.ts10
-rw-r--r--packages/taler-wallet-core/src/wallet.ts6
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);