aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.vscode/tasks.json33
m---------build-system/taler-build-scripts0
-rw-r--r--ci/Containerfile1
-rw-r--r--ci/jobs/0-codespell/dictionary.txt1
-rwxr-xr-xci/jobs/0-codespell/job.sh2
-rw-r--r--package.json3
-rw-r--r--packages/aml-backoffice-ui/package.json2
-rw-r--r--packages/aml-backoffice-ui/tsconfig.json15
-rw-r--r--packages/anastasis-cli/package.json6
-rw-r--r--packages/anastasis-cli/tsconfig.json6
-rw-r--r--packages/anastasis-core/package.json2
-rw-r--r--packages/anastasis-core/src/index.ts1
-rw-r--r--packages/anastasis-core/tsconfig.json2
-rw-r--r--packages/anastasis-webui/package.json2
-rw-r--r--packages/anastasis-webui/tsconfig.json6
-rw-r--r--packages/demobank-ui/package.json2
-rw-r--r--packages/demobank-ui/tsconfig.json15
-rw-r--r--packages/idb-bridge/package.json2
-rw-r--r--packages/idb-bridge/src/SqliteBackend.ts27
-rw-r--r--packages/idb-bridge/src/backend-interface.ts2
-rw-r--r--packages/idb-bridge/src/bridge-idb.ts7
-rw-r--r--packages/idb-bridge/tsconfig.json4
-rw-r--r--packages/merchant-backend-ui/package.json2
-rw-r--r--packages/merchant-backoffice-ui/package.json10
-rw-r--r--packages/merchant-backoffice-ui/src/Application.tsx60
-rw-r--r--packages/merchant-backoffice-ui/src/ApplicationReadyRoutes.tsx86
-rw-r--r--packages/merchant-backoffice-ui/src/InstanceRoutes.tsx219
-rw-r--r--packages/merchant-backoffice-ui/src/components/exception/login.tsx4
-rw-r--r--packages/merchant-backoffice-ui/src/components/form/InputDate.tsx11
-rw-r--r--packages/merchant-backoffice-ui/src/components/form/InputPaytoForm.tsx305
-rw-r--r--packages/merchant-backoffice-ui/src/components/form/InputSearchOnList.tsx (renamed from packages/merchant-backoffice-ui/src/components/form/InputSearchProduct.tsx)97
-rw-r--r--packages/merchant-backoffice-ui/src/components/form/InputToggle.tsx4
-rw-r--r--packages/merchant-backoffice-ui/src/components/instance/DefaultInstanceFormFields.tsx31
-rw-r--r--packages/merchant-backoffice-ui/src/components/menu/SideBar.tsx81
-rw-r--r--packages/merchant-backoffice-ui/src/components/menu/index.tsx58
-rw-r--r--packages/merchant-backoffice-ui/src/components/product/InventoryProductForm.tsx8
-rw-r--r--packages/merchant-backoffice-ui/src/components/product/ProductForm.tsx18
-rw-r--r--packages/merchant-backoffice-ui/src/context/backend.ts52
-rw-r--r--packages/merchant-backoffice-ui/src/declaration.d.ts420
-rw-r--r--packages/merchant-backoffice-ui/src/hooks/backend.ts111
-rw-r--r--packages/merchant-backoffice-ui/src/hooks/bank.ts217
-rw-r--r--packages/merchant-backoffice-ui/src/hooks/index.ts23
-rw-r--r--packages/merchant-backoffice-ui/src/hooks/instance.test.ts6
-rw-r--r--packages/merchant-backoffice-ui/src/hooks/instance.ts5
-rw-r--r--packages/merchant-backoffice-ui/src/hooks/otp.ts223
-rw-r--r--packages/merchant-backoffice-ui/src/hooks/reserve.test.ts114
-rw-r--r--packages/merchant-backoffice-ui/src/hooks/reserves.ts64
-rw-r--r--packages/merchant-backoffice-ui/src/hooks/urls.ts34
-rw-r--r--packages/merchant-backoffice-ui/src/hooks/useSettings.ts37
-rw-r--r--packages/merchant-backoffice-ui/src/paths/admin/create/CreatePage.tsx96
-rw-r--r--packages/merchant-backoffice-ui/src/paths/instance/accounts/create/Create.stories.tsx28
-rw-r--r--packages/merchant-backoffice-ui/src/paths/instance/accounts/create/CreatePage.tsx173
-rw-r--r--packages/merchant-backoffice-ui/src/paths/instance/accounts/create/index.tsx65
-rw-r--r--packages/merchant-backoffice-ui/src/paths/instance/accounts/list/List.stories.tsx28
-rw-r--r--packages/merchant-backoffice-ui/src/paths/instance/accounts/list/ListPage.tsx64
-rw-r--r--packages/merchant-backoffice-ui/src/paths/instance/accounts/list/Table.tsx385
-rw-r--r--packages/merchant-backoffice-ui/src/paths/instance/accounts/list/index.tsx107
-rw-r--r--packages/merchant-backoffice-ui/src/paths/instance/accounts/update/Update.stories.tsx32
-rw-r--r--packages/merchant-backoffice-ui/src/paths/instance/accounts/update/UpdatePage.tsx114
-rw-r--r--packages/merchant-backoffice-ui/src/paths/instance/accounts/update/index.tsx96
-rw-r--r--packages/merchant-backoffice-ui/src/paths/instance/details/DetailPage.tsx10
-rw-r--r--packages/merchant-backoffice-ui/src/paths/instance/details/stories.tsx6
-rw-r--r--packages/merchant-backoffice-ui/src/paths/instance/kyc/list/ListPage.stories.tsx2
-rw-r--r--packages/merchant-backoffice-ui/src/paths/instance/kyc/list/ListPage.tsx6
-rw-r--r--packages/merchant-backoffice-ui/src/paths/instance/orders/create/Create.stories.tsx7
-rw-r--r--packages/merchant-backoffice-ui/src/paths/instance/orders/create/CreatePage.tsx161
-rw-r--r--packages/merchant-backoffice-ui/src/paths/instance/orders/create/index.tsx6
-rw-r--r--packages/merchant-backoffice-ui/src/paths/instance/orders/details/Detail.stories.tsx2
-rw-r--r--packages/merchant-backoffice-ui/src/paths/instance/orders/details/DetailPage.tsx28
-rw-r--r--packages/merchant-backoffice-ui/src/paths/instance/orders/details/Timeline.tsx5
-rw-r--r--packages/merchant-backoffice-ui/src/paths/instance/orders/list/ListPage.tsx42
-rw-r--r--packages/merchant-backoffice-ui/src/paths/instance/orders/list/Table.tsx27
-rw-r--r--packages/merchant-backoffice-ui/src/paths/instance/orders/list/index.tsx18
-rw-r--r--packages/merchant-backoffice-ui/src/paths/instance/products/list/Table.tsx62
-rw-r--r--packages/merchant-backoffice-ui/src/paths/instance/products/list/index.tsx48
-rw-r--r--packages/merchant-backoffice-ui/src/paths/instance/reserves/create/CreatePage.tsx14
-rw-r--r--packages/merchant-backoffice-ui/src/paths/instance/reserves/create/CreatedSuccessfully.tsx22
-rw-r--r--packages/merchant-backoffice-ui/src/paths/instance/reserves/create/index.tsx8
-rw-r--r--packages/merchant-backoffice-ui/src/paths/instance/reserves/details/DetailPage.tsx36
-rw-r--r--packages/merchant-backoffice-ui/src/paths/instance/reserves/details/Details.stories.tsx6
-rw-r--r--packages/merchant-backoffice-ui/src/paths/instance/reserves/details/RewardInfo.tsx (renamed from packages/merchant-backoffice-ui/src/paths/instance/reserves/details/TipInfo.tsx)23
-rw-r--r--packages/merchant-backoffice-ui/src/paths/instance/reserves/list/AutorizeRewardModal.tsx (renamed from packages/merchant-backoffice-ui/src/paths/instance/reserves/list/AutorizeTipModal.tsx)40
-rw-r--r--packages/merchant-backoffice-ui/src/paths/instance/reserves/list/CreatedSuccessfully.tsx18
-rw-r--r--packages/merchant-backoffice-ui/src/paths/instance/reserves/list/List.stories.tsx6
-rw-r--r--packages/merchant-backoffice-ui/src/paths/instance/reserves/list/Table.tsx39
-rw-r--r--packages/merchant-backoffice-ui/src/paths/instance/reserves/list/index.tsx82
-rw-r--r--packages/merchant-backoffice-ui/src/paths/instance/templates/create/CreatePage.tsx127
-rw-r--r--packages/merchant-backoffice-ui/src/paths/instance/templates/list/index.tsx51
-rw-r--r--packages/merchant-backoffice-ui/src/paths/instance/templates/qr/QrPage.tsx62
-rw-r--r--packages/merchant-backoffice-ui/src/paths/instance/templates/qr/index.tsx2
-rw-r--r--packages/merchant-backoffice-ui/src/paths/instance/templates/update/UpdatePage.tsx121
-rw-r--r--packages/merchant-backoffice-ui/src/paths/instance/token/DetailPage.tsx165
-rw-r--r--packages/merchant-backoffice-ui/src/paths/instance/token/index.tsx90
-rw-r--r--packages/merchant-backoffice-ui/src/paths/instance/token/stories.tsx28
-rw-r--r--packages/merchant-backoffice-ui/src/paths/instance/transfers/create/index.tsx3
-rw-r--r--packages/merchant-backoffice-ui/src/paths/instance/transfers/list/Table.tsx10
-rw-r--r--packages/merchant-backoffice-ui/src/paths/instance/transfers/list/index.tsx3
-rw-r--r--packages/merchant-backoffice-ui/src/paths/instance/update/Update.stories.tsx6
-rw-r--r--packages/merchant-backoffice-ui/src/paths/instance/update/UpdatePage.tsx118
-rw-r--r--packages/merchant-backoffice-ui/src/paths/instance/validators/create/Create.stories.tsx28
-rw-r--r--packages/merchant-backoffice-ui/src/paths/instance/validators/create/CreatePage.tsx195
-rw-r--r--packages/merchant-backoffice-ui/src/paths/instance/validators/create/CreatedSuccessfully.tsx104
-rw-r--r--packages/merchant-backoffice-ui/src/paths/instance/validators/create/index.tsx70
-rw-r--r--packages/merchant-backoffice-ui/src/paths/instance/validators/list/List.stories.tsx28
-rw-r--r--packages/merchant-backoffice-ui/src/paths/instance/validators/list/ListPage.tsx64
-rw-r--r--packages/merchant-backoffice-ui/src/paths/instance/validators/list/Table.tsx213
-rw-r--r--packages/merchant-backoffice-ui/src/paths/instance/validators/list/index.tsx106
-rw-r--r--packages/merchant-backoffice-ui/src/paths/instance/validators/update/Update.stories.tsx32
-rw-r--r--packages/merchant-backoffice-ui/src/paths/instance/validators/update/UpdatePage.tsx185
-rw-r--r--packages/merchant-backoffice-ui/src/paths/instance/validators/update/index.tsx102
-rw-r--r--packages/merchant-backoffice-ui/src/paths/instance/webhooks/list/Table.tsx5
-rw-r--r--packages/merchant-backoffice-ui/src/paths/settings/index.tsx103
-rw-r--r--packages/merchant-backoffice-ui/src/schemas/index.ts4
-rw-r--r--packages/merchant-backoffice-ui/tsconfig.json117
-rw-r--r--packages/pogen/package.json2
-rw-r--r--packages/pogen/tsconfig.json2
-rw-r--r--packages/taler-harness/package.json2
-rw-r--r--packages/taler-harness/src/bench2.ts1
-rw-r--r--packages/taler-harness/src/bench3.ts1
-rw-r--r--packages/taler-harness/src/env-full.ts4
-rw-r--r--packages/taler-harness/src/harness/harness.ts438
-rw-r--r--packages/taler-harness/src/harness/helpers.ts358
-rw-r--r--packages/taler-harness/src/harness/libeufin-apis.ts76
-rw-r--r--packages/taler-harness/src/index.ts28
-rw-r--r--packages/taler-harness/src/integrationtests/test-age-restrictions-merchant.ts171
-rw-r--r--packages/taler-harness/src/integrationtests/test-bank-api.ts49
-rw-r--r--packages/taler-harness/src/integrationtests/test-claim-loop.ts27
-rw-r--r--packages/taler-harness/src/integrationtests/test-clause-schnorr.ts34
-rw-r--r--packages/taler-harness/src/integrationtests/test-denom-unoffered.ts17
-rw-r--r--packages/taler-harness/src/integrationtests/test-deposit.ts2
-rw-r--r--packages/taler-harness/src/integrationtests/test-exchange-deposit.ts1
-rw-r--r--packages/taler-harness/src/integrationtests/test-exchange-management.ts42
-rw-r--r--packages/taler-harness/src/integrationtests/test-exchange-timetravel.ts64
-rw-r--r--packages/taler-harness/src/integrationtests/test-fee-regression.ts2
-rw-r--r--packages/taler-harness/src/integrationtests/test-kyc.ts24
-rw-r--r--packages/taler-harness/src/integrationtests/test-libeufin-api-sandbox-camt.ts2
-rw-r--r--packages/taler-harness/src/integrationtests/test-libeufin-basic.ts45
-rw-r--r--packages/taler-harness/src/integrationtests/test-libeufin-nexus-balance.ts2
-rw-r--r--packages/taler-harness/src/integrationtests/test-merchant-exchange-confusion.ts42
-rw-r--r--packages/taler-harness/src/integrationtests/test-merchant-instances-delete.ts14
-rw-r--r--packages/taler-harness/src/integrationtests/test-merchant-instances-urls.ts6
-rw-r--r--packages/taler-harness/src/integrationtests/test-merchant-instances.ts18
-rw-r--r--packages/taler-harness/src/integrationtests/test-merchant-longpolling.ts22
-rw-r--r--packages/taler-harness/src/integrationtests/test-merchant-refund-api.ts97
-rw-r--r--packages/taler-harness/src/integrationtests/test-merchant-spec-public-orders.ts106
-rw-r--r--packages/taler-harness/src/integrationtests/test-pay-paid.ts59
-rw-r--r--packages/taler-harness/src/integrationtests/test-payment-abort.ts48
-rw-r--r--packages/taler-harness/src/integrationtests/test-payment-claim.ts20
-rw-r--r--packages/taler-harness/src/integrationtests/test-payment-expired.ts131
-rw-r--r--packages/taler-harness/src/integrationtests/test-payment-fault.ts50
-rw-r--r--packages/taler-harness/src/integrationtests/test-payment-idempotency.ts12
-rw-r--r--packages/taler-harness/src/integrationtests/test-payment-multiple.ts48
-rw-r--r--packages/taler-harness/src/integrationtests/test-payment-share.ts29
-rw-r--r--packages/taler-harness/src/integrationtests/test-payment-template.ts33
-rw-r--r--packages/taler-harness/src/integrationtests/test-payment-transient.ts28
-rw-r--r--packages/taler-harness/src/integrationtests/test-paywall-flow.ts27
-rw-r--r--packages/taler-harness/src/integrationtests/test-peer-repair.ts2
-rw-r--r--packages/taler-harness/src/integrationtests/test-peer-to-peer-pull.ts2
-rw-r--r--packages/taler-harness/src/integrationtests/test-peer-to-peer-push.ts2
-rw-r--r--packages/taler-harness/src/integrationtests/test-refund-auto.ts18
-rw-r--r--packages/taler-harness/src/integrationtests/test-refund-gone.ts55
-rw-r--r--packages/taler-harness/src/integrationtests/test-refund-incremental.ts23
-rw-r--r--packages/taler-harness/src/integrationtests/test-refund.ts13
-rw-r--r--packages/taler-harness/src/integrationtests/test-revocation.ts80
-rw-r--r--packages/taler-harness/src/integrationtests/test-stored-backups.ts112
-rw-r--r--packages/taler-harness/src/integrationtests/test-timetravel-autorefresh.ts112
-rw-r--r--packages/taler-harness/src/integrationtests/test-timetravel-withdraw.ts40
-rw-r--r--packages/taler-harness/src/integrationtests/test-tipping.ts47
-rw-r--r--packages/taler-harness/src/integrationtests/test-wallet-backup-basic.ts29
-rw-r--r--packages/taler-harness/src/integrationtests/test-wallet-backup-doublespend.ts62
-rw-r--r--packages/taler-harness/src/integrationtests/test-wallet-balance.ts29
-rw-r--r--packages/taler-harness/src/integrationtests/test-wallet-notifications.ts15
-rw-r--r--packages/taler-harness/src/integrationtests/test-wallettesting.ts2
-rw-r--r--packages/taler-harness/src/integrationtests/test-withdrawal-abort-bank.ts34
-rw-r--r--packages/taler-harness/src/integrationtests/test-withdrawal-bank-integrated.ts14
-rw-r--r--packages/taler-harness/src/integrationtests/test-withdrawal-fees.ts28
-rw-r--r--packages/taler-harness/src/integrationtests/test-withdrawal-huge.ts3
-rw-r--r--packages/taler-harness/src/integrationtests/test-withdrawal-manual.ts30
-rw-r--r--packages/taler-harness/src/integrationtests/testrunner.ts74
-rw-r--r--packages/taler-harness/src/lint.ts1
-rw-r--r--packages/taler-harness/tsconfig.json4
-rw-r--r--packages/taler-util/package.json6
-rw-r--r--packages/taler-util/src/MerchantApiClient.ts331
-rw-r--r--packages/taler-util/src/backup-types.ts19
-rw-r--r--packages/taler-util/src/bank-api-client.ts (renamed from packages/taler-wallet-core/src/bank-api-client.ts)265
-rw-r--r--packages/taler-util/src/bitcoin.ts8
-rw-r--r--packages/taler-util/src/http-common.ts7
-rw-r--r--packages/taler-util/src/http-impl.node.ts6
-rw-r--r--packages/taler-util/src/http-impl.qtart.ts6
-rw-r--r--packages/taler-util/src/index.ts3
-rw-r--r--packages/taler-util/src/libeufin-api-types.ts31
-rw-r--r--packages/taler-util/src/merchant-api-types.ts17
-rw-r--r--packages/taler-util/src/payto.ts6
-rw-r--r--packages/taler-util/src/time.ts14
-rw-r--r--packages/taler-util/src/transactions-types.ts17
-rw-r--r--packages/taler-util/src/wallet-types.ts62
-rw-r--r--packages/taler-util/tsconfig.json4
-rw-r--r--packages/taler-wallet-cli/package.json4
-rw-r--r--packages/taler-wallet-cli/src/index.ts61
-rw-r--r--packages/taler-wallet-cli/tsconfig.json4
-rw-r--r--packages/taler-wallet-core/package.json4
-rw-r--r--packages/taler-wallet-core/src/crypto/cryptoImplementation.ts6
-rw-r--r--packages/taler-wallet-core/src/db.ts817
-rw-r--r--packages/taler-wallet-core/src/dbless.ts48
-rw-r--r--packages/taler-wallet-core/src/host-impl.node.ts8
-rw-r--r--packages/taler-wallet-core/src/host-impl.qtart.ts2
-rw-r--r--packages/taler-wallet-core/src/index.ts2
-rw-r--r--packages/taler-wallet-core/src/internal-wallet-state.ts4
-rw-r--r--packages/taler-wallet-core/src/operations/balance.ts31
-rw-r--r--packages/taler-wallet-core/src/operations/common.ts51
-rw-r--r--packages/taler-wallet-core/src/operations/deposits.ts43
-rw-r--r--packages/taler-wallet-core/src/operations/exchanges.ts99
-rw-r--r--packages/taler-wallet-core/src/operations/pay-merchant.ts17
-rw-r--r--packages/taler-wallet-core/src/operations/pay-peer-common.ts12
-rw-r--r--packages/taler-wallet-core/src/operations/pay-peer-pull-credit.ts355
-rw-r--r--packages/taler-wallet-core/src/operations/pay-peer-pull-debit.ts178
-rw-r--r--packages/taler-wallet-core/src/operations/pay-peer-push-credit.ts316
-rw-r--r--packages/taler-wallet-core/src/operations/pay-peer-push-debit.ts321
-rw-r--r--packages/taler-wallet-core/src/operations/pending.ts93
-rw-r--r--packages/taler-wallet-core/src/operations/refresh.ts134
-rw-r--r--packages/taler-wallet-core/src/operations/reward.ts14
-rw-r--r--packages/taler-wallet-core/src/operations/testing.ts4
-rw-r--r--packages/taler-wallet-core/src/operations/transactions.ts175
-rw-r--r--packages/taler-wallet-core/src/operations/withdraw.test.ts22
-rw-r--r--packages/taler-wallet-core/src/operations/withdraw.ts117
-rw-r--r--packages/taler-wallet-core/src/pending-types.ts4
-rw-r--r--packages/taler-wallet-core/src/remote.ts1
-rw-r--r--packages/taler-wallet-core/src/util/coinSelection.ts62
-rw-r--r--packages/taler-wallet-core/src/util/instructedAmountConversion.ts2
-rw-r--r--packages/taler-wallet-core/src/util/query.ts109
-rw-r--r--packages/taler-wallet-core/src/wallet-api-types.ts15
-rw-r--r--packages/taler-wallet-core/src/wallet.ts233
-rw-r--r--packages/taler-wallet-core/tsconfig.json11
-rw-r--r--packages/taler-wallet-embedded/tsconfig.json4
-rw-r--r--packages/taler-wallet-webextension/package.json2
-rw-r--r--packages/taler-wallet-webextension/src/components/ShowFullContractTermPopup.stories.tsx6
-rw-r--r--packages/taler-wallet-webextension/src/components/ShowFullContractTermPopup.tsx8
-rw-r--r--packages/taler-wallet-webextension/src/components/TermsOfService/views.tsx4
-rw-r--r--packages/taler-wallet-webextension/src/cta/InvoicePay/state.ts4
-rw-r--r--packages/taler-wallet-webextension/src/cta/Payment/views.tsx2
-rw-r--r--packages/taler-wallet-webextension/src/cta/Refund/index.ts2
-rw-r--r--packages/taler-wallet-webextension/src/cta/Refund/state.ts2
-rw-r--r--packages/taler-wallet-webextension/src/cta/Refund/stories.tsx26
-rw-r--r--packages/taler-wallet-webextension/src/cta/Refund/views.tsx4
-rw-r--r--packages/taler-wallet-webextension/src/cta/TransferCreate/state.ts8
-rw-r--r--packages/taler-wallet-webextension/src/cta/TransferPickup/state.ts4
-rw-r--r--packages/taler-wallet-webextension/src/cta/Withdraw/state.ts1
-rw-r--r--packages/taler-wallet-webextension/src/hooks/useDiagnostics.ts44
-rw-r--r--packages/taler-wallet-webextension/src/wallet/DeveloperPage.tsx20
-rw-r--r--packages/taler-wallet-webextension/src/wallet/ProviderAddPage.tsx7
-rw-r--r--packages/taler-wallet-webextension/src/wallet/Transaction.stories.tsx36
-rw-r--r--packages/taler-wallet-webextension/src/wallet/Transaction.tsx262
-rw-r--r--packages/taler-wallet-webextension/src/wallet/Welcome.stories.tsx8
-rw-r--r--packages/taler-wallet-webextension/src/wallet/Welcome.tsx8
-rw-r--r--packages/taler-wallet-webextension/src/wxApi.ts4
-rw-r--r--packages/taler-wallet-webextension/src/wxBackend.ts49
-rw-r--r--packages/taler-wallet-webextension/tsconfig.json19
-rw-r--r--packages/web-util/package.json2
-rw-r--r--packages/web-util/tsconfig.json17
-rw-r--r--pnpm-lock.yaml1058
260 files changed, 9203 insertions, 6099 deletions
diff --git a/.vscode/tasks.json b/.vscode/tasks.json
index 00c61e8a4..4c931ad04 100644
--- a/.vscode/tasks.json
+++ b/.vscode/tasks.json
@@ -1,18 +1,17 @@
{
- // See https://go.microsoft.com/fwlink/?LinkId=733558
- // for the documentation about the tasks.json format
- "version": "2.0.0",
- "tasks": [
- {
- "type": "typescript",
- "tsconfig": "tsconfig.build.json",
- "problemMatcher": [
- "$tsc"
- ],
- "group": {
- "kind": "build",
- "isDefault": true,
- },
- }
- ]
-} \ No newline at end of file
+ // See https://go.microsoft.com/fwlink/?LinkId=733558
+ // for the documentation about the tasks.json format
+ "version": "2.0.0",
+ "tasks": [
+ {
+ "type": "typescript",
+ "tsconfig": "tsconfig.build.json",
+ "problemMatcher": ["$tsc"],
+ "group": {
+ "kind": "build",
+ "isDefault": true
+ },
+ "label": "tsc: build - tsconfig.build.json"
+ }
+ ]
+}
diff --git a/build-system/taler-build-scripts b/build-system/taler-build-scripts
-Subproject 23538677f6c6be2a62f38dc6137ecdd1c76b7b1
+Subproject 001f5dd081fc8729ff8def90c4a1c3f93eb8689
diff --git a/ci/Containerfile b/ci/Containerfile
index 4e3369830..39bd740a9 100644
--- a/ci/Containerfile
+++ b/ci/Containerfile
@@ -6,6 +6,7 @@ RUN apt-get update -yq && \
apt-get install -yqq \
git \
python3 \
+ codespell \
python3-distutils \
make \
zip \
diff --git a/ci/jobs/0-codespell/dictionary.txt b/ci/jobs/0-codespell/dictionary.txt
index b4d643349..95f860504 100644
--- a/ci/jobs/0-codespell/dictionary.txt
+++ b/ci/jobs/0-codespell/dictionary.txt
@@ -42,3 +42,4 @@ ths
updateing
wan
wih
+vie
diff --git a/ci/jobs/0-codespell/job.sh b/ci/jobs/0-codespell/job.sh
index 58bd07b5a..cb41388c3 100755
--- a/ci/jobs/0-codespell/job.sh
+++ b/ci/jobs/0-codespell/job.sh
@@ -3,4 +3,4 @@ set -exuo pipefail
job_dir=$(dirname "${BASH_SOURCE[0]}")
-codespell -I "${job_dir}"/dictionary.txt -S "*.bib,*.bst,*.cls,*.json,*.png,*.svg,*.wav,*.gz,*/templating/test?/**,**/auditor/*.sql,**/templating/mustach**,*.fees,*key,*.tag,*.info,*.latexmkrc,*.ecc,*.jpg,*.zkey,*.sqlite,*/contrib/hellos/**,*/vpn/tests/**,*.priv,*.file,*.tgz,*.woff,*.gif,*.odt,*.fee,*.deflate,*.dat,*.jpeg,*.eps,*.odg,*/m4/ax_lib_postgresql.m4,*/m4/libgcrypt.m4,*.rpath,config.status,ABOUT-NLS,*/doc/texinfo.tex,*.PNG,*.??.json,*.docx,*.ods,*.doc,*.docx,*.xcf,*.xlsx,*.ecc,*.ttf,*.woff2,*.eot,*.ttf,*.eot,*.mp4,*.pptx,*.epgz,*.min.js,**/*.map,**/fonts/**,*.pack.js,*.po,*.bbl,*/afl-tests/*,*/.git/**,*.pdf,*.epub,**/signing-key.asc,**/pnpm-lock.yaml,**/*.svg,**/*.cls,**/rfc.bib,**/*.bst,*/cbdc-es.tex,*/cbdc-it.tex,**/ExchangeSelection/example.ts,*/testcurl/test_tricky.c,*/i18n/strings.ts,*/src/anastasis-data.ts,**/doc/flows/main.de.tex"
+codespell -I "${job_dir}"/dictionary.txt -S "*.bib,*.bst,*.cls,*.json,*.png,*.svg,*.wav,*.gz,*/templating/test?/**,**/auditor/*.sql,**/templating/mustach**,*.fees,*key,*.tag,*.info,*.latexmkrc,*.ecc,*.jpg,*.zkey,*.sqlite,*/contrib/hellos/**,*/vpn/tests/**,*.priv,*.file,*.tgz,*.woff,*.gif,*.odt,*.fee,*.deflate,*.dat,*.jpeg,*.eps,*.odg,*/m4/ax_lib_postgresql.m4,*/m4/libgcrypt.m4,*.rpath,config.status,ABOUT-NLS,*/doc/texinfo.tex,*.PNG,*.??.json,*.docx,*.ods,*.doc,*.docx,*.xcf,*.xlsx,*.ecc,*.ttf,*.woff2,*.eot,*.ttf,*.eot,*.mp4,*.pptx,*.epgz,*.min.js,**/*.map,**/fonts/**,*.pack.js,*.po,*.bbl,*/afl-tests/*,*/.git/**,*.pdf,*.epub,**/signing-key.asc,**/pnpm-lock.yaml,**/*.svg,**/*.cls,**/rfc.bib,**/*.bst,*/cbdc-es.tex,*/cbdc-it.tex,**/ExchangeSelection/example.ts,*/testcurl/test_tricky.c,*/i18n/strings.ts,*/src/anastasis-data.ts,**/doc/flows/main.de.tex,*/vendor/**"
diff --git a/package.json b/package.json
index 9ce6d628e..e7b4b52d7 100644
--- a/package.json
+++ b/package.json
@@ -15,6 +15,7 @@
"eslint": "^8.29.0",
"eslint-config-prettier": "^8.5.0",
"nx": "15.0.1",
- "prettier": "^2.8.8"
+ "prettier": "^2.8.8",
+ "typescript": "^5.2.2"
}
}
diff --git a/packages/aml-backoffice-ui/package.json b/packages/aml-backoffice-ui/package.json
index 3fde72b74..a761374df 100644
--- a/packages/aml-backoffice-ui/package.json
+++ b/packages/aml-backoffice-ui/package.json
@@ -59,7 +59,7 @@
"postcss": "^8.4.23",
"postcss-cli": "^10.1.0",
"tailwindcss": "^3.3.2",
- "typescript": "5.1.3"
+ "typescript": "5.2.2"
},
"pogen": {
"domain": "aml-backoffice"
diff --git a/packages/aml-backoffice-ui/tsconfig.json b/packages/aml-backoffice-ui/tsconfig.json
index cc5bdf396..9826fac07 100644
--- a/packages/aml-backoffice-ui/tsconfig.json
+++ b/packages/aml-backoffice-ui/tsconfig.json
@@ -1,12 +1,9 @@
{
"compilerOptions": {
/* Basic Options */
- "target": "ES5",
- "module": "ES6",
- "lib": [
- "DOM",
- "ES2017"
- ],
+ "target": "ES2020",
+ "module": "Node16",
+ "lib": ["DOM", "ES2020"],
"allowJs": true /* Allow javascript files to be compiled. */,
// "checkJs": true, /* Report errors in .js files. */
"jsx": "react" /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */,
@@ -45,7 +42,5 @@
/* Advanced Options */
"skipLibCheck": true /* Skip type checking of declaration files. */
},
- "include": [
- "src/**/*"
- ]
-} \ No newline at end of file
+ "include": ["src/**/*"]
+}
diff --git a/packages/anastasis-cli/package.json b/packages/anastasis-cli/package.json
index 2bbf32c3b..55af7cc06 100644
--- a/packages/anastasis-cli/package.json
+++ b/packages/anastasis-cli/package.json
@@ -33,12 +33,12 @@
"@types/node": "^18.11.17",
"prettier": "^2.8.8",
"rimraf": "^3.0.2",
- "typedoc": "^0.24.8",
- "typescript": "^5.1.3"
+ "typedoc": "^0.25.1",
+ "typescript": "^5.2.2"
},
"dependencies": {
- "@gnu-taler/taler-util": "workspace:*",
"@gnu-taler/anastasis-core": "workspace:*",
+ "@gnu-taler/taler-util": "workspace:*",
"tslib": "^2.5.3"
}
}
diff --git a/packages/anastasis-cli/tsconfig.json b/packages/anastasis-cli/tsconfig.json
index 83da34277..7675edfbc 100644
--- a/packages/anastasis-cli/tsconfig.json
+++ b/packages/anastasis-cli/tsconfig.json
@@ -2,11 +2,11 @@
"compileOnSave": true,
"compilerOptions": {
"composite": true,
- "target": "ES2018",
- "module": "ESNext",
+ "target": "ES2020",
+ "module": "Node16",
"moduleResolution": "Node16",
"sourceMap": true,
- "lib": ["es6"],
+ "lib": ["ES2020"],
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"strict": true,
diff --git a/packages/anastasis-core/package.json b/packages/anastasis-core/package.json
index c3a7106fb..aa182e047 100644
--- a/packages/anastasis-core/package.json
+++ b/packages/anastasis-core/package.json
@@ -18,7 +18,7 @@
"devDependencies": {
"ava": "^4.3.3",
"rimraf": "^3.0.2",
- "typescript": "^5.1.3"
+ "typescript": "^5.2.2"
},
"dependencies": {
"@gnu-taler/taler-util": "workspace:*",
diff --git a/packages/anastasis-core/src/index.ts b/packages/anastasis-core/src/index.ts
index 88f3bdbe2..89cf186dd 100644
--- a/packages/anastasis-core/src/index.ts
+++ b/packages/anastasis-core/src/index.ts
@@ -138,7 +138,6 @@ export * as validators from "./validators.js";
export * from "./challenge-feedback-types.js";
const httpLib = createPlatformHttpLib({
- allowHttp: true,
enableThrottling: false,
});
diff --git a/packages/anastasis-core/tsconfig.json b/packages/anastasis-core/tsconfig.json
index 44a159836..a12f2e641 100644
--- a/packages/anastasis-core/tsconfig.json
+++ b/packages/anastasis-core/tsconfig.json
@@ -3,7 +3,7 @@
"compilerOptions": {
"composite": true,
"target": "ES2020",
- "module": "ESNext",
+ "module": "Node16",
"moduleResolution": "Node16",
"sourceMap": true,
"lib": ["ES2020"],
diff --git a/packages/anastasis-webui/package.json b/packages/anastasis-webui/package.json
index eff5ba04d..efee9633e 100644
--- a/packages/anastasis-webui/package.json
+++ b/packages/anastasis-webui/package.json
@@ -44,6 +44,6 @@
"chai": "^4.3.6",
"mocha": "^9.2.0",
"sass": "1.56.1",
- "typescript": "^5.1.3"
+ "typescript": "^5.2.2"
}
}
diff --git a/packages/anastasis-webui/tsconfig.json b/packages/anastasis-webui/tsconfig.json
index 642c34f5a..9e52f2b7e 100644
--- a/packages/anastasis-webui/tsconfig.json
+++ b/packages/anastasis-webui/tsconfig.json
@@ -1,11 +1,11 @@
{
"compilerOptions": {
/* Basic Options */
- "target": "ES5",
- "module": "ES6",
+ "target": "ES2020",
+ "module": "Node16",
"lib": [
"DOM",
- "ES2017"
+ "ES2020"
],
"allowJs": true /* Allow javascript files to be compiled. */,
// "checkJs": true, /* Report errors in .js files. */
diff --git a/packages/demobank-ui/package.json b/packages/demobank-ui/package.json
index a584bbf35..d33fae709 100644
--- a/packages/demobank-ui/package.json
+++ b/packages/demobank-ui/package.json
@@ -60,7 +60,7 @@
"po2json": "^0.4.5",
"preact-render-to-string": "^5.2.6",
"sass": "1.56.1",
- "typescript": "5.1.3"
+ "typescript": "5.2.2"
},
"pogen": {
"domain": "bank"
diff --git a/packages/demobank-ui/tsconfig.json b/packages/demobank-ui/tsconfig.json
index abb9a9f36..9826fac07 100644
--- a/packages/demobank-ui/tsconfig.json
+++ b/packages/demobank-ui/tsconfig.json
@@ -1,12 +1,9 @@
{
"compilerOptions": {
/* Basic Options */
- "target": "ES5",
- "module": "ES6",
- "lib": [
- "DOM",
- "ES2016"
- ],
+ "target": "ES2020",
+ "module": "Node16",
+ "lib": ["DOM", "ES2020"],
"allowJs": true /* Allow javascript files to be compiled. */,
// "checkJs": true, /* Report errors in .js files. */
"jsx": "react" /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */,
@@ -45,7 +42,5 @@
/* Advanced Options */
"skipLibCheck": true /* Skip type checking of declaration files. */
},
- "include": [
- "src/**/*"
- ]
-} \ No newline at end of file
+ "include": ["src/**/*"]
+}
diff --git a/packages/idb-bridge/package.json b/packages/idb-bridge/package.json
index 1c9c46b02..ce64314ff 100644
--- a/packages/idb-bridge/package.json
+++ b/packages/idb-bridge/package.json
@@ -29,7 +29,7 @@
"ava": "^5.3.1",
"prettier": "^2.8.8",
"rimraf": "^5.0.1",
- "typescript": "^5.1.6"
+ "typescript": "^5.2.2"
},
"dependencies": {
"tslib": "^2.6.0"
diff --git a/packages/idb-bridge/src/SqliteBackend.ts b/packages/idb-bridge/src/SqliteBackend.ts
index c40281861..a25ec0045 100644
--- a/packages/idb-bridge/src/SqliteBackend.ts
+++ b/packages/idb-bridge/src/SqliteBackend.ts
@@ -1882,7 +1882,7 @@ export class SqliteBackend implements Backend {
}
}
- clearObjectStore(
+ async clearObjectStore(
btx: DatabaseTransaction,
objectStoreName: string,
): Promise<void> {
@@ -1906,7 +1906,21 @@ export class SqliteBackend implements Backend {
);
}
- throw new Error("Method not implemented.");
+ this._prep(sqlClearObjectStore).run({
+ object_store_id: scopeInfo.objectStoreId,
+ });
+
+ for (const index of scopeInfo.indexMap.values()) {
+ let stmt: Sqlite3Statement;
+ if (index.unique) {
+ stmt = this._prep(sqlClearUniqueIndexData);
+ } else {
+ stmt = this._prep(sqlClearIndexData);
+ }
+ stmt.run({
+ index_id: index.indexId,
+ });
+ }
}
}
@@ -1963,6 +1977,15 @@ CREATE TABLE IF NOT EXISTS unique_index_data
);
`;
+const sqlClearObjectStore = `
+DELETE FROM object_data WHERE object_store_id=$object_store_id`;
+
+const sqlClearIndexData = `
+DELETE FROM index_data WHERE index_id=$index_id`;
+
+const sqlClearUniqueIndexData = `
+DELETE FROM unique_index_data WHERE index_id=$index_id`;
+
const sqlListDatabases = `
SELECT name, version FROM databases;
`;
diff --git a/packages/idb-bridge/src/backend-interface.ts b/packages/idb-bridge/src/backend-interface.ts
index 3255261e2..690f92f54 100644
--- a/packages/idb-bridge/src/backend-interface.ts
+++ b/packages/idb-bridge/src/backend-interface.ts
@@ -144,7 +144,7 @@ export interface IndexMeta {
unique: boolean;
}
-// FIXME: Instead of refering to an object store by name,
+// FIXME: Instead of referring to an object store by name,
// maybe refer to it via some internal, numeric ID?
// This would simplify renaming.
export interface Backend {
diff --git a/packages/idb-bridge/src/bridge-idb.ts b/packages/idb-bridge/src/bridge-idb.ts
index 8cecba534..f3749c77c 100644
--- a/packages/idb-bridge/src/bridge-idb.ts
+++ b/packages/idb-bridge/src/bridge-idb.ts
@@ -735,7 +735,9 @@ export class BridgeIDBDatabase extends FakeEventTarget implements IDBDatabase {
}
if (this._closePending) {
- throw new InvalidStateError();
+ throw new InvalidStateError(
+ `tried to start transaction on ${this._name}, but a close is pending`,
+ );
}
if (!Array.isArray(storeNames)) {
@@ -930,6 +932,9 @@ export class BridgeIDBFactory {
// http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#dfn-steps-for-running-a-versionchange-transaction
for (const otherConn of this.connections) {
+ if (otherConn._name != db._name) {
+ continue;
+ }
if (otherConn._closePending) {
continue;
}
diff --git a/packages/idb-bridge/tsconfig.json b/packages/idb-bridge/tsconfig.json
index 19e9c2a74..44a27284b 100644
--- a/packages/idb-bridge/tsconfig.json
+++ b/packages/idb-bridge/tsconfig.json
@@ -1,8 +1,8 @@
{
"compilerOptions": {
"composite": true,
- "lib": ["es6"],
- "module": "ES2020",
+ "lib": ["ES2020"],
+ "module": "Node16",
"moduleResolution": "Node16",
"target": "ES2020",
"allowJs": true,
diff --git a/packages/merchant-backend-ui/package.json b/packages/merchant-backend-ui/package.json
index 23e5007e9..03e64308c 100644
--- a/packages/merchant-backend-ui/package.json
+++ b/packages/merchant-backend-ui/package.json
@@ -65,6 +65,6 @@
"sirv-cli": "^1.0.11",
"ts-node": "^10.9.1",
"tslib": "2.5.3",
- "typescript": "5.1.3"
+ "typescript": "5.2.2"
}
}
diff --git a/packages/merchant-backoffice-ui/package.json b/packages/merchant-backoffice-ui/package.json
index 3a0c22adb..12aeb4b0e 100644
--- a/packages/merchant-backoffice-ui/package.json
+++ b/packages/merchant-backoffice-ui/package.json
@@ -1,7 +1,7 @@
{
"private": true,
"name": "@gnu-taler/merchant-backoffice-ui",
- "version": "0.0.5",
+ "version": "0.1.0",
"license": "AGPL-3.0-or-later",
"type": "module",
"scripts": {
@@ -41,7 +41,7 @@
"preact": "10.11.3",
"preact-router": "3.2.1",
"qrcode-generator": "1.4.4",
- "swr": "1.3.0",
+ "swr": "2.2.2",
"yup": "^0.32.9"
},
"devDependencies": {
@@ -75,10 +75,10 @@
"rimraf": "^3.0.2",
"sass": "1.56.1",
"source-map-support": "^0.5.21",
- "typedoc": "^0.24.8",
- "typescript": "5.1.3"
+ "typedoc": "^0.25.1",
+ "typescript": "5.2.2"
},
"pogen": {
"domain": "taler-merchant-backoffice"
}
-}
+} \ No newline at end of file
diff --git a/packages/merchant-backoffice-ui/src/Application.tsx b/packages/merchant-backoffice-ui/src/Application.tsx
index f6a81ff8d..5e82821ae 100644
--- a/packages/merchant-backoffice-ui/src/Application.tsx
+++ b/packages/merchant-backoffice-ui/src/Application.tsx
@@ -19,19 +19,20 @@
* @author Sebastian Javier Marchano (sebasjm)
*/
+import { HttpStatusCode, LibtoolVersion } from "@gnu-taler/taler-util";
import {
ErrorType,
TranslationProvider,
useTranslationContext,
} from "@gnu-taler/web-util/browser";
-import { Fragment, h, VNode } from "preact";
+import { Fragment, VNode, h } from "preact";
import { route } from "preact-router";
-import { useMemo, useState } from "preact/hooks";
+import { useMemo } from "preact/hooks";
import { ApplicationReadyRoutes } from "./ApplicationReadyRoutes.js";
import { Loading } from "./components/exception/loading.js";
import {
- NotificationCard,
- NotYetReadyAppMenu,
+ NotConnectedAppMenu,
+ NotificationCard
} from "./components/menu/index.js";
import {
BackendContextProvider,
@@ -41,23 +42,24 @@ import { ConfigContextProvider } from "./context/config.js";
import { useBackendConfig } from "./hooks/backend.js";
import { strings } from "./i18n/strings.js";
import LoginPage from "./paths/login/index.js";
-import { HttpStatusCode } from "@gnu-taler/taler-util";
-import { Settings } from "./paths/settings/index.js";
export function Application(): VNode {
return (
- // <FetchContextProvider>
<BackendContextProvider>
<TranslationProvider source={strings}>
<ApplicationStatusRoutes />
</TranslationProvider>
</BackendContextProvider>
- // </FetchContextProvider>
);
}
+/**
+ * Check connection testing against /config
+ *
+ * @returns
+ */
function ApplicationStatusRoutes(): VNode {
- const { updateLoginStatus, triedToLog } = useBackendContext();
+ const { url, updateLoginStatus, triedToLog } = useBackendContext();
const result = useBackendConfig();
const { i18n } = useTranslationContext();
@@ -71,19 +73,10 @@ function ApplicationStatusRoutes(): VNode {
: { currency: "unknown", version: "unknown" };
const ctx = useMemo(() => ({ currency, version }), [currency, version]);
- const [showSettings, setShowSettings] = useState(false)
-
- if (showSettings) {
- return <Fragment>
- <NotYetReadyAppMenu onShowSettings={() => setShowSettings(true)} title="UI Settings" />
- <Settings />
- </Fragment>
- }
-
if (!triedToLog) {
return (
<Fragment>
- <NotYetReadyAppMenu title="Welcome!" onShowSettings={() => setShowSettings(true)} />
+ <NotConnectedAppMenu title="Welcome!" />
<LoginPage onConfirm={updateLoginInfoAndGoToRoot} />
</Fragment>
);
@@ -97,7 +90,7 @@ function ApplicationStatusRoutes(): VNode {
) {
return (
<Fragment>
- <NotYetReadyAppMenu title="Login" onShowSettings={() => setShowSettings(true)} />
+ <NotConnectedAppMenu title="Login" />
<LoginPage onConfirm={updateLoginInfoAndGoToRoot} />
</Fragment>
);
@@ -108,7 +101,7 @@ function ApplicationStatusRoutes(): VNode {
) {
return (
<Fragment>
- <NotYetReadyAppMenu title="Error" onShowSettings={() => setShowSettings(true)} />
+ <NotConnectedAppMenu title="Error" />
<NotificationCard
notification={{
message: i18n.str`Server not found`,
@@ -122,7 +115,7 @@ function ApplicationStatusRoutes(): VNode {
}
if (result.type === ErrorType.SERVER) {
<Fragment>
- <NotYetReadyAppMenu title="Error" onShowSettings={() => setShowSettings(true)} />
+ <NotConnectedAppMenu title="Error" />
<NotificationCard
notification={{
message: i18n.str`Server response with an error code`,
@@ -135,7 +128,7 @@ function ApplicationStatusRoutes(): VNode {
}
if (result.type === ErrorType.UNREADABLE) {
<Fragment>
- <NotYetReadyAppMenu title="Error" onShowSettings={() => setShowSettings(true)} />
+ <NotConnectedAppMenu title="Error" />
<NotificationCard
notification={{
message: i18n.str`Response from server is unreadable, http status: ${result.status}`,
@@ -148,7 +141,7 @@ function ApplicationStatusRoutes(): VNode {
}
return (
<Fragment>
- <NotYetReadyAppMenu title="Error" onShowSettings={() => setShowSettings(true)} />
+ <NotConnectedAppMenu title="Error" />
<NotificationCard
notification={{
message: i18n.str`Unexpected Error`,
@@ -161,6 +154,25 @@ function ApplicationStatusRoutes(): VNode {
);
}
+ const SUPPORTED_VERSION = "5:0:1"
+ if (!LibtoolVersion.compare(
+ SUPPORTED_VERSION,
+ result.data.version,
+ )?.compatible) {
+ return <Fragment>
+ <NotConnectedAppMenu title="Error" />
+ <NotificationCard
+ notification={{
+ message: i18n.str`Incompatible version`,
+ type: "ERROR",
+ description: i18n.str`Merchant backend server version ${result.data.version} is not compatible with the supported version ${SUPPORTED_VERSION}`,
+ }}
+ />
+ <LoginPage onConfirm={updateLoginInfoAndGoToRoot} />
+ </Fragment>
+
+ }
+
return (
<div class="has-navbar-fixed-top">
<ConfigContextProvider value={ctx}>
diff --git a/packages/merchant-backoffice-ui/src/ApplicationReadyRoutes.tsx b/packages/merchant-backoffice-ui/src/ApplicationReadyRoutes.tsx
index 277c2b176..46dea98e3 100644
--- a/packages/merchant-backoffice-ui/src/ApplicationReadyRoutes.tsx
+++ b/packages/merchant-backoffice-ui/src/ApplicationReadyRoutes.tsx
@@ -22,7 +22,7 @@ import { ErrorType, useTranslationContext } from "@gnu-taler/web-util/browser";
import { createHashHistory } from "history";
import { Fragment, h, VNode } from "preact";
import { Router, Route, route } from "preact-router";
-import { useState } from "preact/hooks";
+import { useEffect, useState } from "preact/hooks";
import {
NotificationCard,
NotYetReadyAppMenu,
@@ -35,52 +35,55 @@ import { INSTANCE_ID_LOOKUP } from "./utils/constants.js";
import { HttpStatusCode } from "@gnu-taler/taler-util";
import { Settings } from "./paths/settings/index.js";
+/**
+ * Check if admin against /management/instances
+ * @returns
+ */
export function ApplicationReadyRoutes(): VNode {
const { i18n } = useTranslationContext();
+ const [unauthorized, setUnauthorized] = useState(false)
const {
url: backendURL,
- updateLoginStatus,
- clearAllTokens,
+ updateLoginStatus: updateLoginStatus2,
} = useBackendContext();
+ function updateLoginStatus(url: string, token: string | undefined) {
+ console.log("updateing", url, token)
+ updateLoginStatus2(url, token)
+ setUnauthorized(false)
+ }
+
const result = useBackendInstancesTestForAdmin();
const clearTokenAndGoToRoot = () => {
- clearAllTokens();
route("/");
};
const [showSettings, setShowSettings] = useState(false)
+ // useEffect(() => {
+ // setUnauthorized(FF)
+ // }, [FF])
+ const unauthorizedAdmin = !result.loading && !result.ok && result.type === ErrorType.CLIENT && result.status === HttpStatusCode.Unauthorized
if (showSettings) {
return <Fragment>
- <NotYetReadyAppMenu onShowSettings={() => setShowSettings(true)} title="UI Settings" onLogout={clearTokenAndGoToRoot} />
- <Settings/>
+ <NotYetReadyAppMenu onShowSettings={() => setShowSettings(true)} title="UI Settings" onLogout={clearTokenAndGoToRoot} isPasswordOk={false} />
+ <Settings />
</Fragment>
}
- if (result.loading) return <NotYetReadyAppMenu onShowSettings={() => setShowSettings(true)} title="Loading..." />;
- let admin = true;
- let instanceNameByBackendURL;
+ if (result.loading) {
+ return <NotYetReadyAppMenu onShowSettings={() => setShowSettings(true)} title="Loading..." isPasswordOk={false} />;
+ }
- if (!result.ok) {
- if (
- result.type === ErrorType.CLIENT &&
- result.status === HttpStatusCode.Unauthorized
- ) {
- return (
- <Fragment>
- <NotYetReadyAppMenu onShowSettings={() => setShowSettings(true)} title="Login" onLogout={clearTokenAndGoToRoot} />
- <NotificationCard
- notification={{
- message: i18n.str`Access denied`,
- description: i18n.str`Check your token is valid`,
- type: "ERROR",
- }}
- />
- <LoginPage onConfirm={updateLoginStatus} />
- </Fragment>
- );
- }
+ let admin = result.ok || unauthorizedAdmin;
+ let instanceNameByBackendURL: string | undefined;
+
+ if (!admin) {
+ // * the testing against admin endpoint failed and it's not
+ // an authorization problem
+ // * merchant backend will return this SPA under the main
+ // endpoint or /instance/<id> endpoint
+ // => trying to infer the instance id
const path = new URL(backendURL).pathname;
const match = INSTANCE_ID_LOOKUP.exec(path);
if (!match || !match[1]) {
@@ -89,7 +92,7 @@ export function ApplicationReadyRoutes(): VNode {
// does not match our pattern
return (
<Fragment>
- <NotYetReadyAppMenu onShowSettings={() => setShowSettings(true)} title="Error" onLogout={clearTokenAndGoToRoot} />
+ <NotYetReadyAppMenu onShowSettings={() => setShowSettings(true)} title="Error" onLogout={clearTokenAndGoToRoot} isPasswordOk={false} />
<NotificationCard
notification={{
message: i18n.str`Couldn't access the server.`,
@@ -102,10 +105,24 @@ export function ApplicationReadyRoutes(): VNode {
);
}
- admin = false;
instanceNameByBackendURL = match[1];
}
+ console.log(unauthorized, unauthorizedAdmin)
+ if (unauthorized || unauthorizedAdmin) {
+ return <Fragment>
+ <NotYetReadyAppMenu onShowSettings={() => setShowSettings(true)} title="Login" onLogout={clearTokenAndGoToRoot} isPasswordOk={false} />
+ <NotificationCard
+ notification={{
+ message: i18n.str`Access denied`,
+ description: i18n.str`Check your token is valid`,
+ type: "ERROR",
+ }}
+ />
+ <LoginPage onConfirm={updateLoginStatus} />
+ </Fragment>
+ }
+
const history = createHashHistory();
return (
<Router history={history}>
@@ -113,6 +130,11 @@ export function ApplicationReadyRoutes(): VNode {
default
component={DefaultMainRoute}
admin={admin}
+ onUnauthorized={() => setUnauthorized(true)}
+ onLoginPass={() => {
+ console.log("ahora si")
+ setUnauthorized(false)
+ }}
instanceNameByBackendURL={instanceNameByBackendURL}
/>
</Router>
@@ -122,6 +144,8 @@ export function ApplicationReadyRoutes(): VNode {
function DefaultMainRoute({
instance,
admin,
+ onUnauthorized,
+ onLoginPass,
instanceNameByBackendURL,
url, //from preact-router
}: any): VNode {
@@ -133,6 +157,8 @@ function DefaultMainRoute({
<InstanceRoutes
admin={admin}
path={url}
+ onUnauthorized={onUnauthorized}
+ onLoginPass={onLoginPass}
id={instanceName}
setInstanceName={setInstanceName}
/>
diff --git a/packages/merchant-backoffice-ui/src/InstanceRoutes.tsx b/packages/merchant-backoffice-ui/src/InstanceRoutes.tsx
index 1547442ea..ee8db9a9f 100644
--- a/packages/merchant-backoffice-ui/src/InstanceRoutes.tsx
+++ b/packages/merchant-backoffice-ui/src/InstanceRoutes.tsx
@@ -40,6 +40,7 @@ import {
import { useInstanceKYCDetails } from "./hooks/instance.js";
import InstanceCreatePage from "./paths/admin/create/index.js";
import InstanceListPage from "./paths/admin/list/index.js";
+import TokenPage from "./paths/instance/token/index.js";
import ListKYCPage from "./paths/instance/kyc/list/index.js";
import OrderCreatePage from "./paths/instance/orders/create/index.js";
import OrderDetailsPage from "./paths/instance/orders/details/index.js";
@@ -47,6 +48,9 @@ import OrderListPage from "./paths/instance/orders/list/index.js";
import ProductCreatePage from "./paths/instance/products/create/index.js";
import ProductListPage from "./paths/instance/products/list/index.js";
import ProductUpdatePage from "./paths/instance/products/update/index.js";
+import BankAccountCreatePage from "./paths/instance/accounts/create/index.js";
+import BankAccountListPage from "./paths/instance/accounts/list/index.js";
+import BankAccountUpdatePage from "./paths/instance/accounts/update/index.js";
import ReservesCreatePage from "./paths/instance/reserves/create/index.js";
import ReservesDetailsPage from "./paths/instance/reserves/details/index.js";
import ReservesListPage from "./paths/instance/reserves/list/index.js";
@@ -58,6 +62,9 @@ import TemplateUpdatePage from "./paths/instance/templates/update/index.js";
import WebhookCreatePage from "./paths/instance/webhooks/create/index.js";
import WebhookListPage from "./paths/instance/webhooks/list/index.js";
import WebhookUpdatePage from "./paths/instance/webhooks/update/index.js";
+import ValidatorCreatePage from "./paths/instance/validators/create/index.js";
+import ValidatorListPage from "./paths/instance/validators/list/index.js";
+import ValidatorUpdatePage from "./paths/instance/validators/update/index.js";
import TransferCreatePage from "./paths/instance/transfers/create/index.js";
import TransferListPage from "./paths/instance/transfers/list/index.js";
import InstanceUpdatePage, {
@@ -69,11 +76,16 @@ import NotFoundPage from "./paths/notfound/index.js";
import { Notification } from "./utils/types.js";
import { MerchantBackend } from "./declaration.js";
import { Settings } from "./paths/settings/index.js";
+import { dateFormatForSettings, useSettings } from "./hooks/useSettings.js";
export enum InstancePaths {
- // details = '/',
error = "/error",
- update = "/update",
+ server = "/server",
+ token = "/token",
+
+ bank_list = "/bank",
+ bank_update = "/bank/:bid/update",
+ bank_new = "/bank/new",
product_list = "/products",
product_update = "/product/:pid/update",
@@ -102,11 +114,15 @@ export enum InstancePaths {
webhooks_update = "/webhooks/:tid/update",
webhooks_new = "/webhooks/new",
- settings = "/settings",
+ validators_list = "/validators",
+ validators_update = "/validators/:vid/update",
+ validators_new = "/validators/new",
+
+ settings = "/interface",
}
// eslint-disable-next-line @typescript-eslint/no-empty-function
-const noop = () => {};
+const noop = () => { };
export enum AdminPaths {
list_instances = "/instances",
@@ -118,6 +134,8 @@ export interface Props {
id: string;
admin?: boolean;
path: string;
+ onUnauthorized: () => void;
+ onLoginPass: () => void;
setInstanceName: (s: string) => void;
}
@@ -125,40 +143,29 @@ export function InstanceRoutes({
id,
admin,
path,
+ onUnauthorized,
+ onLoginPass,
setInstanceName,
}: Props): VNode {
- const [_, updateDefaultToken] = useBackendDefaultToken();
+ const [defaultToken, updateDefaultToken] = useBackendDefaultToken();
const [token, updateToken] = useBackendInstanceToken(id);
- const {
- updateLoginStatus: changeBackend,
- addTokenCleaner,
- clearAllTokens,
- } = useBackendContext();
- const cleaner = useCallback(() => {
- updateToken(undefined);
- }, [id]);
const { i18n } = useTranslationContext();
type GlobalNotifState = (Notification & { to: string }) | undefined;
const [globalNotification, setGlobalNotification] =
useState<GlobalNotifState>(undefined);
- useEffect(() => {
- addTokenCleaner(cleaner);
- }, [addTokenCleaner, cleaner]);
-
const changeToken = (token?: string) => {
if (admin) {
updateToken(token);
} else {
updateDefaultToken(token);
}
+ onLoginPass()
};
- const updateLoginStatus = (url: string, token?: string) => {
- changeBackend(url);
- if (!token) return;
- changeToken(token);
- };
+ // const updateLoginStatus = (url: string, token?: string) => {
+ // changeToken(token);
+ // };
const value = useMemo(
() => ({ id, token, admin, changeToken }),
@@ -192,18 +199,17 @@ export function InstanceRoutes({
};
}
- const LoginPageAccessDenied = () => (
- <Fragment>
- <NotificationCard
- notification={{
- message: i18n.str`Access denied`,
- description: i18n.str`The access token provided is invalid.`,
- type: "ERROR",
- }}
- />
- <LoginPage onConfirm={updateLoginStatus} />
- </Fragment>
- );
+ // const LoginPageAccessDeniend = onUnauthorized
+ const LoginPageAccessDenied = () => {
+ onUnauthorized()
+ return <NotificationCard
+ notification={{
+ message: i18n.str`Access denied`,
+ description: i18n.str`Redirecting to login page.`,
+ type: "ERROR",
+ }}
+ />
+ }
function IfAdminCreateDefaultOr<T>(Next: FunctionComponent<any>) {
return function IfAdminCreateDefaultOrImpl(props?: T) {
@@ -234,8 +240,10 @@ export function InstanceRoutes({
}
const clearTokenAndGoToRoot = () => {
- clearAllTokens();
route("/");
+ // clear all tokens
+ updateToken(undefined)
+ updateDefaultToken(undefined)
};
return (
@@ -244,11 +252,12 @@ export function InstanceRoutes({
instance={id}
admin={admin}
onShowSettings={() => {
- route("/settings")
+ route(InstancePaths.settings)
}}
path={path}
onLogout={clearTokenAndGoToRoot}
setInstanceName={setInstanceName}
+ isPasswordOk={defaultToken !== undefined}
/>
<KycBanner />
<NotificationCard notification={globalNotification} />
@@ -308,7 +317,7 @@ export function InstanceRoutes({
* Update instance page
*/}
<Route
- path={InstancePaths.update}
+ path={InstancePaths.server}
component={InstanceUpdatePage}
onBack={() => {
route(`/`);
@@ -322,13 +331,26 @@ export function InstanceRoutes({
onLoadError={ServerErrorRedirectTo(InstancePaths.error)}
/>
{/**
+ * Update instance page
+ */}
+ <Route
+ path={InstancePaths.token}
+ component={TokenPage}
+ onChange={() => {
+ route(`/`);
+ }}
+ onNotFound={IfAdminCreateDefaultOr(NotFoundPage)}
+ onUnauthorized={LoginPageAccessDenied}
+ onLoadError={ServerErrorRedirectTo(InstancePaths.error)}
+ />
+ {/**
* Product pages
*/}
<Route
path={InstancePaths.product_list}
component={ProductListPage}
onUnauthorized={LoginPageAccessDenied}
- onLoadError={ServerErrorRedirectTo(InstancePaths.update)}
+ onLoadError={ServerErrorRedirectTo(InstancePaths.server)}
onCreate={() => {
route(InstancePaths.product_new);
}}
@@ -361,6 +383,45 @@ export function InstanceRoutes({
}}
/>
{/**
+ * Bank pages
+ */}
+ <Route
+ path={InstancePaths.bank_list}
+ component={BankAccountListPage}
+ onUnauthorized={LoginPageAccessDenied}
+ onLoadError={ServerErrorRedirectTo(InstancePaths.server)}
+ onCreate={() => {
+ route(InstancePaths.bank_new);
+ }}
+ onSelect={(id: string) => {
+ route(InstancePaths.bank_update.replace(":bid", id));
+ }}
+ onNotFound={IfAdminCreateDefaultOr(NotFoundPage)}
+ />
+ <Route
+ path={InstancePaths.bank_update}
+ component={BankAccountUpdatePage}
+ onUnauthorized={LoginPageAccessDenied}
+ onLoadError={ServerErrorRedirectTo(InstancePaths.product_list)}
+ onConfirm={() => {
+ route(InstancePaths.bank_list);
+ }}
+ onBack={() => {
+ route(InstancePaths.bank_list);
+ }}
+ onNotFound={IfAdminCreateDefaultOr(NotFoundPage)}
+ />
+ <Route
+ path={InstancePaths.bank_new}
+ component={BankAccountCreatePage}
+ onConfirm={() => {
+ route(InstancePaths.bank_list);
+ }}
+ onBack={() => {
+ route(InstancePaths.bank_list);
+ }}
+ />
+ {/**
* Order pages
*/}
<Route
@@ -373,7 +434,7 @@ export function InstanceRoutes({
route(InstancePaths.order_details.replace(":oid", id));
}}
onUnauthorized={LoginPageAccessDenied}
- onLoadError={ServerErrorRedirectTo(InstancePaths.update)}
+ onLoadError={ServerErrorRedirectTo(InstancePaths.server)}
onNotFound={IfAdminCreateDefaultOr(NotFoundPage)}
/>
<Route
@@ -389,8 +450,8 @@ export function InstanceRoutes({
<Route
path={InstancePaths.order_new}
component={OrderCreatePage}
- onConfirm={() => {
- route(InstancePaths.order_list);
+ onConfirm={(orderId: string) => {
+ route(InstancePaths.order_details.replace(":oid", orderId));
}}
onBack={() => {
route(InstancePaths.order_list);
@@ -404,7 +465,7 @@ export function InstanceRoutes({
component={TransferListPage}
onUnauthorized={LoginPageAccessDenied}
onNotFound={IfAdminCreateDefaultOr(NotFoundPage)}
- onLoadError={ServerErrorRedirectTo(InstancePaths.update)}
+ onLoadError={ServerErrorRedirectTo(InstancePaths.server)}
onCreate={() => {
route(InstancePaths.transfers_new);
}}
@@ -427,7 +488,7 @@ export function InstanceRoutes({
component={WebhookListPage}
onUnauthorized={LoginPageAccessDenied}
onNotFound={IfAdminCreateDefaultOr(NotFoundPage)}
- onLoadError={ServerErrorRedirectTo(InstancePaths.update)}
+ onLoadError={ServerErrorRedirectTo(InstancePaths.server)}
onCreate={() => {
route(InstancePaths.webhooks_new);
}}
@@ -459,6 +520,45 @@ export function InstanceRoutes({
}}
/>
{/**
+ * Validator pages
+ */}
+ <Route
+ path={InstancePaths.validators_list}
+ component={ValidatorListPage}
+ onUnauthorized={LoginPageAccessDenied}
+ onNotFound={IfAdminCreateDefaultOr(NotFoundPage)}
+ onLoadError={ServerErrorRedirectTo(InstancePaths.server)}
+ onCreate={() => {
+ route(InstancePaths.validators_new);
+ }}
+ onSelect={(id: string) => {
+ route(InstancePaths.validators_update.replace(":vid", id));
+ }}
+ />
+ <Route
+ path={InstancePaths.validators_update}
+ component={ValidatorUpdatePage}
+ onConfirm={() => {
+ route(InstancePaths.validators_list);
+ }}
+ onUnauthorized={LoginPageAccessDenied}
+ onLoadError={ServerErrorRedirectTo(InstancePaths.validators_list)}
+ onNotFound={IfAdminCreateDefaultOr(NotFoundPage)}
+ onBack={() => {
+ route(InstancePaths.validators_list);
+ }}
+ />
+ <Route
+ path={InstancePaths.validators_new}
+ component={ValidatorCreatePage}
+ onConfirm={() => {
+ route(InstancePaths.validators_list);
+ }}
+ onBack={() => {
+ route(InstancePaths.validators_list);
+ }}
+ />
+ {/**
* Templates pages
*/}
<Route
@@ -466,7 +566,7 @@ export function InstanceRoutes({
component={TemplateListPage}
onUnauthorized={LoginPageAccessDenied}
onNotFound={IfAdminCreateDefaultOr(NotFoundPage)}
- onLoadError={ServerErrorRedirectTo(InstancePaths.update)}
+ onLoadError={ServerErrorRedirectTo(InstancePaths.server)}
onCreate={() => {
route(InstancePaths.templates_new);
}}
@@ -535,7 +635,7 @@ export function InstanceRoutes({
component={ReservesListPage}
onUnauthorized={LoginPageAccessDenied}
onNotFound={IfAdminCreateDefaultOr(NotFoundPage)}
- onLoadError={ServerErrorRedirectTo(InstancePaths.update)}
+ onLoadError={ServerErrorRedirectTo(InstancePaths.server)}
onSelect={(id: string) => {
route(InstancePaths.reserves_details.replace(":rid", id));
}}
@@ -590,7 +690,7 @@ function AdminInstanceUpdatePage({
const { updateLoginStatus: changeBackend } = useBackendContext();
const updateLoginStatus = (url: string, token?: string): void => {
changeBackend(url);
- if (token) changeToken(token);
+ changeToken(token);
};
const value = useMemo(
() => ({ id, token, admin: true, changeToken }),
@@ -607,20 +707,20 @@ function AdminInstanceUpdatePage({
const notif =
error.type === ErrorType.TIMEOUT
? {
- message: i18n.str`The request to the backend take too long and was cancelled`,
- description: i18n.str`Diagnostic from ${error.info.url} is '${error.message}'`,
- type: "ERROR" as const,
- }
+ message: i18n.str`The request to the backend take too long and was cancelled`,
+ description: i18n.str`Diagnostic from ${error.info.url} is '${error.message}'`,
+ type: "ERROR" as const,
+ }
: {
- message: i18n.str`The backend reported a problem: HTTP status #${error.status}`,
- description: i18n.str`Diagnostic from ${error.info.url} is '${error.message}'`,
- details:
- error.type === ErrorType.CLIENT ||
+ message: i18n.str`The backend reported a problem: HTTP status #${error.status}`,
+ description: i18n.str`Diagnostic from ${error.info.url} is '${error.message}'`,
+ details:
+ error.type === ErrorType.CLIENT ||
error.type === ErrorType.SERVER
- ? error.payload.detail
- : undefined,
- type: "ERROR" as const,
- };
+ ? error.payload.detail
+ : undefined,
+ type: "ERROR" as const,
+ };
return (
<Fragment>
<NotificationCard notification={notif} />
@@ -650,7 +750,8 @@ function AdminInstanceUpdatePage({
function KycBanner(): VNode {
const kycStatus = useInstanceKYCDetails();
const { i18n } = useTranslationContext();
- const today = format(new Date(), "yyyy-MM-dd");
+ const [settings] = useSettings();
+ const today = format(new Date(), dateFormatForSettings(settings));
const [lastHide, setLastHide] = useLocalStorage("kyc-last-hide");
const hasBeenHidden = today === lastHide;
const needsToBeShown = kycStatus.ok && kycStatus.data.type === "redirect";
diff --git a/packages/merchant-backoffice-ui/src/components/exception/login.tsx b/packages/merchant-backoffice-ui/src/components/exception/login.tsx
index f2f94a7c5..4fa440fc7 100644
--- a/packages/merchant-backoffice-ui/src/components/exception/login.tsx
+++ b/packages/merchant-backoffice-ui/src/components/exception/login.tsx
@@ -93,7 +93,7 @@ export function LoginModal({ onConfirm, withMessage }: Props): VNode {
<input
class="input"
type="password"
- placeholder={"set new access token"}
+ placeholder={"current access token"}
name="token"
onKeyPress={(e) =>
e.keyCode === 13
@@ -186,7 +186,7 @@ export function LoginModal({ onConfirm, withMessage }: Props): VNode {
<input
class="input"
type="password"
- placeholder={"set new access token"}
+ placeholder={"current access token"}
name="token"
onKeyPress={(e) =>
e.keyCode === 13
diff --git a/packages/merchant-backoffice-ui/src/components/form/InputDate.tsx b/packages/merchant-backoffice-ui/src/components/form/InputDate.tsx
index 1f41c3564..a398629dc 100644
--- a/packages/merchant-backoffice-ui/src/components/form/InputDate.tsx
+++ b/packages/merchant-backoffice-ui/src/components/form/InputDate.tsx
@@ -20,16 +20,18 @@
*/
import { useTranslationContext } from "@gnu-taler/web-util/browser";
import { format } from "date-fns";
-import { h, VNode } from "preact";
+import { ComponentChildren, h, VNode } from "preact";
import { useState } from "preact/hooks";
import { DatePicker } from "../picker/DatePicker.js";
import { InputProps, useField } from "./useField.js";
+import { dateFormatForSettings, useSettings } from "../../hooks/useSettings.js";
export interface Props<T> extends InputProps<T> {
readonly?: boolean;
expand?: boolean;
//FIXME: create separated components InputDate and InputTimestamp
withTimestampSupport?: boolean;
+ side?: ComponentChildren;
}
export function InputDate<T>({
@@ -41,9 +43,11 @@ export function InputDate<T>({
tooltip,
expand,
withTimestampSupport,
+ side,
}: Props<keyof T>): VNode {
const [opened, setOpened] = useState(false);
const { i18n } = useTranslationContext();
+ const [settings] = useSettings()
const { error, required, value, onChange } = useField<T>(name);
@@ -51,14 +55,14 @@ export function InputDate<T>({
if (!value) {
strValue = withTimestampSupport ? "unknown" : "";
} else if (value instanceof Date) {
- strValue = format(value, "yyyy/MM/dd");
+ strValue = format(value, dateFormatForSettings(settings));
} else if (value.t_s) {
strValue =
value.t_s === "never"
? withTimestampSupport
? "never"
: ""
- : format(new Date(value.t_s * 1000), "yyyy/MM/dd");
+ : format(new Date(value.t_s * 1000), dateFormatForSettings(settings));
}
return (
@@ -142,6 +146,7 @@ export function InputDate<T>({
</button>
</span>
)}
+ {side}
</div>
<DatePicker
opened={opened}
diff --git a/packages/merchant-backoffice-ui/src/components/form/InputPaytoForm.tsx b/packages/merchant-backoffice-ui/src/components/form/InputPaytoForm.tsx
index 8d324660e..5cd69a0b3 100644
--- a/packages/merchant-backoffice-ui/src/components/form/InputPaytoForm.tsx
+++ b/packages/merchant-backoffice-ui/src/components/form/InputPaytoForm.tsx
@@ -18,9 +18,9 @@
*
* @author Sebastian Javier Marchano (sebasjm)
*/
+import { parsePaytoUri, PaytoUriGeneric, stringifyPaytoUri } from "@gnu-taler/taler-util";
import { useTranslationContext } from "@gnu-taler/web-util/browser";
import { Fragment, h, VNode } from "preact";
-import { useCallback, useState } from "preact/hooks";
import { COUNTRY_TABLE } from "../../utils/constants.js";
import { undefinedIfEmpty } from "../../utils/table.js";
import { FormErrors, FormProvider } from "./FormProvider.js";
@@ -28,23 +28,23 @@ import { Input } from "./Input.js";
import { InputGroup } from "./InputGroup.js";
import { InputSelector } from "./InputSelector.js";
import { InputProps, useField } from "./useField.js";
-import { InputWithAddon } from "./InputWithAddon.js";
-import { MerchantBackend } from "../../declaration.js";
+import { useEffect, useState } from "preact/hooks";
export interface Props<T> extends InputProps<T> {
isValid?: (e: any) => boolean;
}
+// type Entity = PaytoUriGeneric
// https://datatracker.ietf.org/doc/html/rfc8905
type Entity = {
// iban, bitcoin, x-taler-bank. it defined the format
target: string;
// path1 if the first field to be used
- path1: string;
+ path1?: string;
// path2 if the second field to be used, optional
path2?: string;
- // options of the payto uri
- options: {
+ // params of the payto uri
+ params: {
"receiver-name"?: string;
sender?: string;
message?: string;
@@ -52,13 +52,6 @@ type Entity = {
instruction?: string;
[name: string]: string | undefined;
};
- auth: {
- type: "unset" | "basic" | "none";
- url?: string;
- username?: string;
- password?: string;
- repeat?: string;
- };
};
function isEthereumAddress(address: string) {
@@ -171,14 +164,10 @@ const targets = [
"bitcoin",
"ethereum",
];
-const accountAuthType = ["none", "basic"];
const noTargetValue = targets[0];
-const defaultTarget: Partial<Entity> = {
+const defaultTarget: Entity = {
target: noTargetValue,
- options: {},
- auth: {
- type: "unset" as const,
- },
+ params: {},
};
export function InputPaytoForm<T>({
@@ -187,110 +176,91 @@ export function InputPaytoForm<T>({
label,
tooltip,
}: Props<keyof T>): VNode {
- const { value: paytos, onChange, required } = useField<T>(name);
-
- const [value, valueHandler] = useState<Partial<Entity>>(defaultTarget);
+ const { value: initialValueStr, onChange } = useField<T>(name);
- let payToPath;
- if (value.target === "iban" && value.path1) {
- payToPath = `/${value.path1.toUpperCase()}`;
- } else if (value.path1) {
- if (value.path2) {
- payToPath = `/${value.path1}/${value.path2}`;
- } else {
- payToPath = `/${value.path1}`;
- }
+ const initialPayto = parsePaytoUri(initialValueStr ?? "")
+ const paths = !initialPayto ? [] : initialPayto.targetPath.split("/")
+ const initialPath1 = paths.length >= 1 ? paths[0] : undefined;
+ const initialPath2 = paths.length >= 2 ? paths[1] : undefined;
+ const initial: Entity = initialPayto === undefined ? defaultTarget : {
+ target: initialPayto.targetType,
+ params: initialPayto.params,
+ path1: initialPath1,
+ path2: initialPath2,
}
- const { i18n } = useTranslationContext();
+ const [value, setValue] = useState<Partial<Entity>>(initial)
- const ops = value.options ?? {};
- const url = tryUrl(`payto://${value.target}${payToPath}`);
- if (url) {
- Object.keys(ops).forEach((opt_key) => {
- const opt_value = ops[opt_key];
- if (opt_value) url.searchParams.set(opt_key, opt_value);
- });
- }
- const paytoURL = !url ? "" : url.href;
+ const { i18n } = useTranslationContext();
const errors: FormErrors<Entity> = {
target:
- value.target === noTargetValue && !paytos.length
+ value.target === noTargetValue
? i18n.str`required`
: undefined,
path1: !value.path1
? i18n.str`required`
: value.target === "iban"
- ? validateIBAN(value.path1, i18n)
- : value.target === "bitcoin"
- ? validateBitcoin(value.path1, i18n)
- : value.target === "ethereum"
- ? validateEthereum(value.path1, i18n)
- : undefined,
+ ? validateIBAN(value.path1, i18n)
+ : value.target === "bitcoin"
+ ? validateBitcoin(value.path1, i18n)
+ : value.target === "ethereum"
+ ? validateEthereum(value.path1, i18n)
+ : undefined,
path2:
value.target === "x-taler-bank"
? !value.path2
? i18n.str`required`
: undefined
: undefined,
- options: undefinedIfEmpty({
- "receiver-name": !value.options?.["receiver-name"]
+ params: undefinedIfEmpty({
+ "receiver-name": !value.params?.["receiver-name"]
? i18n.str`required`
: undefined,
}),
- auth: !value.auth
- ? undefined
- : undefinedIfEmpty({
- username:
- value.auth.type === "basic" && !value.auth.username
- ? i18n.str`required`
- : undefined,
- password:
- value.auth.type === "basic" && !value.auth.password
- ? i18n.str`required`
- : undefined,
- repeat:
- value.auth.type === "basic" && !value.auth.repeat
- ? i18n.str`required`
- : value.auth.repeat !== value.auth.password
- ? i18n.str`is not the same`
- : undefined,
- }),
};
const hasErrors = Object.keys(errors).some(
(k) => (errors as any)[k] !== undefined,
);
+ const str = hasErrors || !value.target ? undefined : stringifyPaytoUri({
+ targetType: value.target,
+ targetPath: value.path2 ? `${value.path1}/${value.path2}` : (value.path1 ?? ""),
+ params: value.params ?? {} as any,
+ isKnown: false,
+ })
+ useEffect(() => {
+ onChange(str as any)
+ }, [str])
- const submit = useCallback((): void => {
- const accounts: MerchantBackend.Instances.MerchantBankAccount[] = paytos;
- const alreadyExists =
- accounts.findIndex((x) => x.payto_uri === paytoURL) !== -1;
- if (!alreadyExists) {
- const newValue: MerchantBackend.Instances.MerchantBankAccount = {
- payto_uri: paytoURL,
- };
- if (value.auth) {
- if (value.auth.url) {
- newValue.credit_facade_url = value.auth.url;
- }
- if (value.auth.type === "none") {
- newValue.credit_facade_credentials = {
- type: "none",
- };
- }
- if (value.auth.type === "basic") {
- newValue.credit_facade_credentials = {
- type: "basic",
- username: value.auth.username ?? "",
- password: value.auth.password ?? "",
- };
- }
- }
- onChange([newValue, ...accounts] as any);
- }
- valueHandler(defaultTarget);
- }, [value]);
+ // const submit = useCallback((): void => {
+ // // const accounts: MerchantBackend.BankAccounts.AccountAddDetails[] = paytos;
+ // // const alreadyExists =
+ // // accounts.findIndex((x) => x.payto_uri === paytoURL) !== -1;
+ // // if (!alreadyExists) {
+ // const newValue: MerchantBackend.BankAccounts.AccountAddDetails = {
+ // payto_uri: paytoURL,
+ // };
+ // if (value.auth) {
+ // if (value.auth.url) {
+ // newValue.credit_facade_url = value.auth.url;
+ // }
+ // if (value.auth.type === "none") {
+ // newValue.credit_facade_credentials = {
+ // type: "none",
+ // };
+ // }
+ // if (value.auth.type === "basic") {
+ // newValue.credit_facade_credentials = {
+ // type: "basic",
+ // username: value.auth.username ?? "",
+ // password: value.auth.password ?? "",
+ // };
+ // }
+ // }
+ // onChange(newValue as any);
+ // // }
+ // // valueHandler(defaultTarget);
+ // }, [value]);
//FIXME: translating plural singular
return (
@@ -299,11 +269,11 @@ export function InputPaytoForm<T>({
name="tax"
errors={errors}
object={value}
- valueHandler={valueHandler}
+ valueHandler={setValue}
>
<InputSelector<Entity>
name="target"
- label={i18n.str`Target type`}
+ label={i18n.str`Account type`}
tooltip={i18n.str`Method to use for wire transfer`}
values={targets}
toStr={(v) => (v === noTargetValue ? i18n.str`Choose one...` : v)}
@@ -400,150 +370,15 @@ export function InputPaytoForm<T>({
{value.target !== noTargetValue && (
<Fragment>
<Input
- name="options.receiver-name"
+ name="params.receiver-name"
label={i18n.str`Name`}
tooltip={i18n.str`Bank account owner's name.`}
/>
- <InputWithAddon
- name="auth.url"
- label={i18n.str`Account info URL`}
- help="https://bank.com"
- expand
- tooltip={i18n.str`From where the merchant can download information about incoming wire transfers to this account`}
- />
- <InputSelector
- name="auth.type"
- label={i18n.str`Auth type`}
- tooltip={i18n.str`Choose the authentication type for the account info URL`}
- values={accountAuthType}
- toStr={(str) => {
- // if (str === "unset") {
- // return "Without change";
- // }
- if (str === "none") return "Without authentication";
- return "Username and password";
- }}
- />
- {value.auth?.type === "basic" ? (
- <Fragment>
- <Input
- name="auth.username"
- label={i18n.str`Username`}
- tooltip={i18n.str`Username to access the account information.`}
- />
- <Input
- name="auth.password"
- inputType="password"
- label={i18n.str`Password`}
- tooltip={i18n.str`Password to access the account information.`}
- />
- <Input
- name="auth.repeat"
- inputType="password"
- label={i18n.str`Repeat password`}
- />
- </Fragment>
- ) : undefined}
-
- {/* <InputWithAddon
- name="options.credit_credentials"
- label={i18n.str`Account info`}
- inputType={showKey ? "text" : "password"}
- help="From where the merchant can download information about incoming wire transfers to this account"
- expand
- tooltip={i18n.str`Useful to validate the purchase`}
- fromStr={(v) => v.toUpperCase()}
- addonAfter={
- <span class="icon">
- {showKey ? (
- <i class="mdi mdi-eye" />
- ) : (
- <i class="mdi mdi-eye-off" />
- )}
- </span>
- }
- side={
- <span style={{ display: "flex" }}>
- <button
- data-tooltip={
- showKey
- ? i18n.str`show secret key`
- : i18n.str`hide secret key`
- }
- class="button is-info mr-3"
- onClick={(e) => {
- setShowKey(!showKey);
- }}
- >
- {showKey ? (
- <i18n.Translate>hide</i18n.Translate>
- ) : (
- <i18n.Translate>show</i18n.Translate>
- )}
- </button>
- </span>
- }
- /> */}
</Fragment>
)}
- {/**
- * Show the values in the list
- */}
- <div class="field is-horizontal">
- <div class="field-label is-normal" />
- <div class="field-body" style={{ display: "block" }}>
- {paytos.map(
- (v: MerchantBackend.Instances.MerchantBankAccount, i: number) => (
- <div
- key={i}
- class="tags has-addons mt-3 mb-0 mr-3"
- style={{ flexWrap: "nowrap" }}
- >
- <span
- class="tag is-medium is-info mb-0"
- style={{ maxWidth: "90%" }}
- >
- {v.payto_uri}
- </span>
- <a
- class="tag is-medium is-danger is-delete mb-0"
- onClick={() => {
- onChange(paytos.filter((f: any) => f !== v) as any);
- }}
- />
- </div>
- ),
- )}
- {!paytos.length && i18n.str`No accounts yet.`}
- {required && (
- <span class="icon has-text-danger is-right">
- <i class="mdi mdi-alert" />
- </span>
- )}
- </div>
- </div>
- {value.target !== noTargetValue && (
- <div class="buttons is-right mt-5">
- <button
- class="button is-info"
- data-tooltip={i18n.str`add tax to the tax list`}
- disabled={hasErrors}
- onClick={submit}
- >
- <i18n.Translate>Add</i18n.Translate>
- </button>
- </div>
- )}
</FormProvider>
</InputGroup>
);
}
-function tryUrl(s: string): URL | undefined {
- try {
- return new URL(s);
- } catch (e) {
- return undefined;
- }
-}
diff --git a/packages/merchant-backoffice-ui/src/components/form/InputSearchProduct.tsx b/packages/merchant-backoffice-ui/src/components/form/InputSearchOnList.tsx
index 1c1fcb907..be5800d14 100644
--- a/packages/merchant-backoffice-ui/src/components/form/InputSearchProduct.tsx
+++ b/packages/merchant-backoffice-ui/src/components/form/InputSearchOnList.tsx
@@ -22,32 +22,41 @@ import { useTranslationContext } from "@gnu-taler/web-util/browser";
import { h, VNode } from "preact";
import { useState } from "preact/hooks";
import emptyImage from "../../assets/empty.png";
-import { MerchantBackend, WithId } from "../../declaration.js";
import { FormErrors, FormProvider } from "./FormProvider.js";
import { InputWithAddon } from "./InputWithAddon.js";
+import { TranslatedString } from "@gnu-taler/taler-util";
-type Entity = MerchantBackend.Products.ProductDetail & WithId;
+type Entity = {
+ id: string,
+ description: string;
+ image?: string;
+ extra?: string;
+};
-export interface Props {
- selected?: Entity;
- onChange: (p?: Entity) => void;
- products: (MerchantBackend.Products.ProductDetail & WithId)[];
+export interface Props<T extends Entity> {
+ selected?: T;
+ onChange: (p?: T) => void;
+ label: TranslatedString;
+ list: T[];
+ withImage?: boolean;
}
-interface ProductSearch {
+interface Search {
name: string;
}
-export function InputSearchProduct({
+export function InputSearchOnList<T extends Entity>({
selected,
onChange,
- products,
-}: Props): VNode {
- const [prodForm, setProdName] = useState<Partial<ProductSearch>>({
+ label,
+ list,
+ withImage,
+}: Props<T>): VNode {
+ const [nameForm, setNameForm] = useState<Partial<Search>>({
name: "",
});
- const errors: FormErrors<ProductSearch> = {
+ const errors: FormErrors<Search> = {
name: undefined,
};
const { i18n } = useTranslationContext();
@@ -55,15 +64,17 @@ export function InputSearchProduct({
if (selected) {
return (
<article class="media">
- <figure class="media-left">
- <p class="image is-128x128">
- <img src={selected.image ? selected.image : emptyImage} />
- </p>
- </figure>
+ {withImage &&
+ <figure class="media-left">
+ <p class="image is-128x128">
+ <img src={selected.image ? selected.image : emptyImage} />
+ </p>
+ </figure>
+ }
<div class="media-content">
<div class="content">
<p class="media-meta">
- <i18n.Translate>Product id</i18n.Translate>: <b>{selected.id}</b>
+ <i18n.Translate>ID</i18n.Translate>: <b>{selected.id}</b>
</p>
<p>
<i18n.Translate>Description</i18n.Translate>:{" "}
@@ -84,15 +95,15 @@ export function InputSearchProduct({
}
return (
- <FormProvider<ProductSearch>
+ <FormProvider<Search>
errors={errors}
- object={prodForm}
- valueHandler={setProdName}
+ object={nameForm}
+ valueHandler={setNameForm}
>
- <InputWithAddon<ProductSearch>
+ <InputWithAddon<Search>
name="name"
- label={i18n.str`Product`}
- tooltip={i18n.str`search products by it's description or id`}
+ label={label}
+ tooltip={i18n.str`enter description or id`}
addonAfter={
<span class="icon">
<i class="mdi mdi-magnify" />
@@ -100,13 +111,14 @@ export function InputSearchProduct({
}
>
<div>
- <ProductList
- name={prodForm.name}
- list={products}
+ <DropdownList
+ name={nameForm.name}
+ list={list}
onSelect={(p) => {
- setProdName({ name: "" });
+ setNameForm({ name: "" });
onChange(p);
}}
+ withImage={!!withImage}
/>
</div>
</InputWithAddon>
@@ -114,13 +126,14 @@ export function InputSearchProduct({
);
}
-interface ProductListProps {
+interface DropdownListProps<T extends Entity> {
name?: string;
- onSelect: (p: MerchantBackend.Products.ProductDetail & WithId) => void;
- list: (MerchantBackend.Products.ProductDetail & WithId)[];
+ onSelect: (p: T) => void;
+ list: T[];
+ withImage: boolean;
}
-function ProductList({ name, onSelect, list }: ProductListProps) {
+function DropdownList<T extends Entity>({ name, onSelect, list, withImage }: DropdownListProps<T>) {
const { i18n } = useTranslationContext();
if (!name) {
/* FIXME
@@ -149,7 +162,7 @@ function ProductList({ name, onSelect, list }: ProductListProps) {
{!filtered.length ? (
<div class="dropdown-item">
<i18n.Translate>
- no products found with that description
+ no match found with that description or id
</i18n.Translate>
</div>
) : (
@@ -161,18 +174,20 @@ function ProductList({ name, onSelect, list }: ProductListProps) {
style={{ cursor: "pointer" }}
>
<article class="media">
- <div class="media-left">
- <div class="image" style={{ minWidth: 64 }}>
- <img
- src={p.image ? p.image : emptyImage}
- style={{ width: 64, height: 64 }}
- />
+ {withImage &&
+ <div class="media-left">
+ <div class="image" style={{ minWidth: 64 }}>
+ <img
+ src={p.image ? p.image : emptyImage}
+ style={{ width: 64, height: 64 }}
+ />
+ </div>
</div>
- </div>
+ }
<div class="media-content">
<div class="content">
<p>
- <strong>{p.id}</strong> <small>{p.price}</small>
+ <strong>{p.id}</strong> {p.extra !== undefined ? <small>{p.extra}</small> : undefined}
<br />
{p.description}
</p>
diff --git a/packages/merchant-backoffice-ui/src/components/form/InputToggle.tsx b/packages/merchant-backoffice-ui/src/components/form/InputToggle.tsx
index 61ddf3c84..f95dfcd05 100644
--- a/packages/merchant-backoffice-ui/src/components/form/InputToggle.tsx
+++ b/packages/merchant-backoffice-ui/src/components/form/InputToggle.tsx
@@ -56,7 +56,7 @@ export function InputToggle<T>({
return (
<div class="field is-horizontal">
<div class="field-label is-normal">
- <label class="label" style={{ width: 200 }}>
+ <label class="label" >
{label}
{tooltip && (
<span class="icon has-tooltip-right" data-tooltip={tooltip}>
@@ -65,7 +65,7 @@ export function InputToggle<T>({
)}
</label>
</div>
- <div class="field-body is-flex-grow-1">
+ <div class="field-body is-flex-grow-3">
<div class="field">
<p class={expand ? "control is-expanded" : "control"}>
<label class="toggle" style={{ marginLeft: 4, marginTop: 0 }}>
diff --git a/packages/merchant-backoffice-ui/src/components/instance/DefaultInstanceFormFields.tsx b/packages/merchant-backoffice-ui/src/components/instance/DefaultInstanceFormFields.tsx
index 24380ce98..b75dc83b3 100644
--- a/packages/merchant-backoffice-ui/src/components/instance/DefaultInstanceFormFields.tsx
+++ b/packages/merchant-backoffice-ui/src/components/instance/DefaultInstanceFormFields.tsx
@@ -24,14 +24,13 @@ import { Fragment, h, VNode } from "preact";
import { useBackendContext } from "../../context/backend.js";
import { Entity } from "../../paths/admin/create/CreatePage.js";
import { Input } from "../form/Input.js";
-import { InputCurrency } from "../form/InputCurrency.js";
import { InputDuration } from "../form/InputDuration.js";
import { InputGroup } from "../form/InputGroup.js";
import { InputImage } from "../form/InputImage.js";
import { InputLocation } from "../form/InputLocation.js";
-import { InputPaytoForm } from "../form/InputPaytoForm.js";
-import { InputWithAddon } from "../form/InputWithAddon.js";
import { InputSelector } from "../form/InputSelector.js";
+import { InputToggle } from "../form/InputToggle.js";
+import { InputWithAddon } from "../form/InputWithAddon.js";
export function DefaultInstanceFormFields({
readonlyId,
@@ -85,28 +84,10 @@ export function DefaultInstanceFormFields({
tooltip={i18n.str`Logo image.`}
/>
- <InputPaytoForm<Entity>
- name="accounts"
- label={i18n.str`Bank account`}
- tooltip={i18n.str`URI specifying bank account for crediting revenue.`}
- />
-
- <InputCurrency<Entity>
- name="default_max_deposit_fee"
- label={i18n.str`Default max deposit fee`}
- tooltip={i18n.str`Maximum deposit fees this merchant is willing to pay per order by default.`}
- />
-
- <InputCurrency<Entity>
- name="default_max_wire_fee"
- label={i18n.str`Default max wire fee`}
- tooltip={i18n.str`Maximum wire fees this merchant is willing to pay per wire transfer by default.`}
- />
-
- <Input<Entity>
- name="default_wire_fee_amortization"
- label={i18n.str`Default wire fee amortization`}
- tooltip={i18n.str`Number of orders excess wire transfer fees will be divided by to compute per order surcharge.`}
+ <InputToggle<Entity>
+ name="use_stefan"
+ label={i18n.str`Pay transaction fee`}
+ tooltip={i18n.str`Assume the cost of the transaction of let the user pay for it.`}
/>
<InputGroup
diff --git a/packages/merchant-backoffice-ui/src/components/menu/SideBar.tsx b/packages/merchant-backoffice-ui/src/components/menu/SideBar.tsx
index f3cf80b92..be2f8dde5 100644
--- a/packages/merchant-backoffice-ui/src/components/menu/SideBar.tsx
+++ b/packages/merchant-backoffice-ui/src/components/menu/SideBar.tsx
@@ -25,6 +25,7 @@ import { useBackendContext } from "../../context/backend.js";
import { useConfigContext } from "../../context/config.js";
import { useInstanceKYCDetails } from "../../hooks/instance.js";
import { LangSelector } from "./LangSelector.js";
+import { useCredentialsChecker } from "../../hooks/backend.js";
const GIT_HASH = typeof __GIT_HASH__ !== "undefined" ? __GIT_HASH__ : undefined;
const VERSION = typeof __VERSION__ !== "undefined" ? __VERSION__ : undefined;
@@ -36,6 +37,7 @@ interface Props {
instance: string;
admin?: boolean;
mimic?: boolean;
+ isPasswordOk: boolean;
}
export function Sidebar({
@@ -45,6 +47,7 @@ export function Sidebar({
onLogout,
admin,
mimic,
+ isPasswordOk
}: Props): VNode {
const config = useConfigContext();
const backend = useBackendContext();
@@ -53,7 +56,7 @@ export function Sidebar({
const needKYC = kycStatus.ok && kycStatus.data.type === "redirect";
return (
- <aside class="aside is-placed-left is-expanded">
+ <aside class="aside is-placed-left is-expanded" style={{ overflowY: "scroll" }}>
{mobile && (
<div
class="footer"
@@ -78,10 +81,10 @@ export function Sidebar({
</div>
</div>
<div class="menu is-menu-main">
- {instance ? (
+ {isPasswordOk && instance ? (
<Fragment>
<ul class="menu-list">
- <li>
+ <li>
<a href={"/orders"} class="has-icon">
<span class="icon">
<i class="mdi mdi-cash-register" />
@@ -104,7 +107,7 @@ export function Sidebar({
<li>
<a href={"/transfers"} class="has-icon">
<span class="icon">
- <i class="mdi mdi-bank" />
+ <i class="mdi mdi-arrow-left-right" />
</span>
<span class="menu-item-label">
<i18n.Translate>Transfers</i18n.Translate>
@@ -137,12 +140,22 @@ export function Sidebar({
</p>
<ul class="menu-list">
<li>
- <a href={"/update"} class="has-icon">
+ <a href={"/bank"} class="has-icon">
<span class="icon">
- <i class="mdi mdi-square-edit-outline" />
+ <i class="mdi mdi-bank" />
+ </span>
+ <span class="menu-item-label">
+ <i18n.Translate>Bank account</i18n.Translate>
+ </span>
+ </a>
+ </li>
+ <li>
+ <a href={"/validators"} class="has-icon">
+ <span class="icon">
+ <i class="mdi mdi-lock" />
</span>
<span class="menu-item-label">
- <i18n.Translate>Account</i18n.Translate>
+ <i18n.Translate>Validators</i18n.Translate>
</span>
</a>
</li>
@@ -164,6 +177,26 @@ export function Sidebar({
</span>
</a>
</li>
+ <li>
+ <a href={"/server"} class="has-icon">
+ <span class="icon">
+ <i class="mdi mdi-square-edit-outline" />
+ </span>
+ <span class="menu-item-label">
+ <i18n.Translate>Server</i18n.Translate>
+ </span>
+ </a>
+ </li>
+ <li>
+ <a href={"/token"} class="has-icon">
+ <span class="icon">
+ <i class="mdi mdi-security" />
+ </span>
+ <span class="menu-item-label">
+ <i18n.Translate>Access token</i18n.Translate>
+ </span>
+ </a>
+ </li>
</ul>
</Fragment>
) : undefined}
@@ -174,12 +207,12 @@ export function Sidebar({
<li>
<a class="has-icon is-state-info is-hoverable"
onClick={(): void => onShowSettings()}
- >
+ >
<span class="icon">
<i class="mdi mdi-newspaper" />
</span>
<span class="menu-item-label">
- <i18n.Translate>Settings</i18n.Translate>
+ <i18n.Translate>Interface</i18n.Translate>
</span>
</a>
</li>
@@ -211,7 +244,7 @@ export function Sidebar({
</span>
</div>
</li>
- {admin && !mimic && (
+ {isPasswordOk && admin && !mimic && (
<Fragment>
<p class="menu-label">
<i18n.Translate>Instances</i18n.Translate>
@@ -238,19 +271,21 @@ export function Sidebar({
</li>
</Fragment>
)}
- <li>
- <a
- class="has-icon is-state-info is-hoverable"
- onClick={(): void => onLogout()}
- >
- <span class="icon">
- <i class="mdi mdi-logout default" />
- </span>
- <span class="menu-item-label">
- <i18n.Translate>Log out</i18n.Translate>
- </span>
- </a>
- </li>
+ {isPasswordOk &&
+ <li>
+ <a
+ class="has-icon is-state-info is-hoverable"
+ onClick={(): void => onLogout()}
+ >
+ <span class="icon">
+ <i class="mdi mdi-logout default" />
+ </span>
+ <span class="menu-item-label">
+ <i18n.Translate>Log out</i18n.Translate>
+ </span>
+ </a>
+ </li>
+ }
</ul>
</div>
</aside>
diff --git a/packages/merchant-backoffice-ui/src/components/menu/index.tsx b/packages/merchant-backoffice-ui/src/components/menu/index.tsx
index cdbae4ae0..cb318906f 100644
--- a/packages/merchant-backoffice-ui/src/components/menu/index.tsx
+++ b/packages/merchant-backoffice-ui/src/components/menu/index.tsx
@@ -24,7 +24,7 @@ import { Sidebar } from "./SideBar.js";
function getInstanceTitle(path: string, id: string): string {
switch (path) {
- case InstancePaths.update:
+ case InstancePaths.server:
return `${id}: Settings`;
case InstancePaths.order_list:
return `${id}: Orders`;
@@ -50,6 +50,12 @@ function getInstanceTitle(path: string, id: string): string {
return `${id}: New webhook`;
case InstancePaths.webhooks_update:
return `${id}: Update webhook`;
+ case InstancePaths.validators_list:
+ return `${id}: Validators`;
+ case InstancePaths.validators_new:
+ return `${id}: New validator`;
+ case InstancePaths.validators_update:
+ return `${id}: Update validators`;
case InstancePaths.templates_new:
return `${id}: New template`;
case InstancePaths.templates_update:
@@ -58,6 +64,10 @@ function getInstanceTitle(path: string, id: string): string {
return `${id}: Templates`;
case InstancePaths.templates_use:
return `${id}: Use template`;
+ case InstancePaths.settings:
+ return `${id}: Interface`;
+ case InstancePaths.settings:
+ return `${id}: Interface`;
default:
return "";
}
@@ -77,6 +87,7 @@ interface MenuProps {
onLogout?: () => void;
onShowSettings: () => void;
setInstanceName: (s: string) => void;
+ isPasswordOk: boolean;
}
function WithTitle({
@@ -100,14 +111,15 @@ export function Menu({
path,
admin,
setInstanceName,
+ isPasswordOk
}: MenuProps): VNode {
const [mobileOpen, setMobileOpen] = useState(false);
const titleWithSubtitle = title
? title
: !admin
- ? getInstanceTitle(path, instance)
- : getAdminTitle(path, instance);
+ ? getInstanceTitle(path, instance)
+ : getAdminTitle(path, instance);
const adminInstance = instance === "default";
const mimic = admin && !adminInstance;
return (
@@ -129,14 +141,15 @@ export function Menu({
mimic={mimic}
instance={instance}
mobile={mobileOpen}
+ isPasswordOk={isPasswordOk}
/>
)}
{mimic && (
<nav class="level" style={{
zIndex: 100,
- position:"fixed",
- width:"50%",
+ position: "fixed",
+ width: "50%",
marginLeft: "20%"
}}>
<div class="level-item has-text-centered has-background-warning">
@@ -161,8 +174,9 @@ export function Menu({
interface NotYetReadyAppMenuProps {
title: string;
- onLogout?: () => void;
onShowSettings: () => void;
+ onLogout?: () => void;
+ isPasswordOk: boolean;
}
interface NotifProps {
@@ -181,8 +195,8 @@ export function NotificationCard({
n.type === "ERROR"
? "message is-danger"
: n.type === "WARN"
- ? "message is-warning"
- : "message is-info"
+ ? "message is-warning"
+ : "message is-info"
}
>
<div class="message-header">
@@ -201,10 +215,36 @@ export function NotificationCard({
);
}
+interface NotConnectedAppMenuProps {
+ title: string;
+}
+export function NotConnectedAppMenu({
+ title,
+}: NotConnectedAppMenuProps): VNode {
+ const [mobileOpen, setMobileOpen] = useState(false);
+
+ useEffect(() => {
+ document.title = `Taler Backoffice: ${title}`;
+ }, [title]);
+
+ return (
+ <div
+ class={mobileOpen ? "has-aside-mobile-expanded" : ""}
+ onClick={() => setMobileOpen(false)}
+ >
+ <NavigationBar
+ onMobileMenu={() => setMobileOpen(!mobileOpen)}
+ title={title}
+ />
+ </div>
+ );
+}
+
export function NotYetReadyAppMenu({
onLogout,
onShowSettings,
title,
+ isPasswordOk
}: NotYetReadyAppMenuProps): VNode {
const [mobileOpen, setMobileOpen] = useState(false);
@@ -222,7 +262,7 @@ export function NotYetReadyAppMenu({
title={title}
/>
{onLogout && (
- <Sidebar onShowSettings={onShowSettings} onLogout={onLogout} instance="" mobile={mobileOpen} />
+ <Sidebar onShowSettings={onShowSettings} onLogout={onLogout} instance="" mobile={mobileOpen} isPasswordOk={isPasswordOk} />
)}
</div>
);
diff --git a/packages/merchant-backoffice-ui/src/components/product/InventoryProductForm.tsx b/packages/merchant-backoffice-ui/src/components/product/InventoryProductForm.tsx
index b2ec4dd11..377d9c1ba 100644
--- a/packages/merchant-backoffice-ui/src/components/product/InventoryProductForm.tsx
+++ b/packages/merchant-backoffice-ui/src/components/product/InventoryProductForm.tsx
@@ -20,7 +20,7 @@ import { MerchantBackend, WithId } from "../../declaration.js";
import { ProductMap } from "../../paths/instance/orders/create/CreatePage.js";
import { FormErrors, FormProvider } from "../form/FormProvider.js";
import { InputNumber } from "../form/InputNumber.js";
-import { InputSearchProduct } from "../form/InputSearchProduct.js";
+import { InputSearchOnList } from "../form/InputSearchOnList.js";
type Form = {
product: MerchantBackend.Products.ProductDetail & WithId;
@@ -95,10 +95,12 @@ export function InventoryProductForm({
return (
<FormProvider<Form> errors={errors} object={state} valueHandler={setState}>
- <InputSearchProduct
+ <InputSearchOnList
+ label={i18n.str`Search product`}
selected={state.product}
onChange={(p) => setState((v) => ({ ...v, product: p }))}
- products={inventory}
+ list={inventory}
+ withImage
/>
{state.product && (
<div class="columns mt-5">
diff --git a/packages/merchant-backoffice-ui/src/components/product/ProductForm.tsx b/packages/merchant-backoffice-ui/src/components/product/ProductForm.tsx
index 7956a9ea5..726a94f5e 100644
--- a/packages/merchant-backoffice-ui/src/components/product/ProductForm.tsx
+++ b/packages/merchant-backoffice-ui/src/components/product/ProductForm.tsx
@@ -58,12 +58,12 @@ export function ProductForm({ onSubscribe, initial, alreadyExist }: Props) {
!initial || initial.total_stock === -1
? undefined
: {
- current: initial.total_stock || 0,
- lost: initial.total_lost || 0,
- sold: initial.total_sold || 0,
- address: initial.address,
- nextRestock: initial.next_restock,
- },
+ current: initial.total_stock || 0,
+ lost: initial.total_lost || 0,
+ sold: initial.total_sold || 0,
+ address: initial.address,
+ nextRestock: initial.next_restock,
+ },
});
let errors: FormErrors<Entity> = {};
@@ -148,15 +148,17 @@ export function ProductForm({ onSubscribe, initial, alreadyExist }: Props) {
name="minimum_age"
label={i18n.str`Age restricted`}
tooltip={i18n.str`is this product restricted for customer below certain age?`}
+ help={i18n.str`can be overridden by the order configuration`}
/>
<Input<Entity>
name="unit"
- label={i18n.str`Unit`}
+ label={i18n.str`Unit name`}
tooltip={i18n.str`unit describing quantity of product sold (e.g. 2 kilograms, 5 liters, 3 items, 5 meters) for customers`}
+ help={i18n.str`exajmple: kg, items or liters`}
/>
<InputCurrency<Entity>
name="price"
- label={i18n.str`Price`}
+ label={i18n.str`Price per unit`}
tooltip={i18n.str`sale price for customers, including taxes, for above units of the product`}
/>
<InputStock
diff --git a/packages/merchant-backoffice-ui/src/context/backend.ts b/packages/merchant-backoffice-ui/src/context/backend.ts
index f7f8afea6..43e9e4d27 100644
--- a/packages/merchant-backoffice-ui/src/context/backend.ts
+++ b/packages/merchant-backoffice-ui/src/context/backend.ts
@@ -28,8 +28,8 @@ interface BackendContextType {
token?: string;
triedToLog: boolean;
resetBackend: () => void;
- clearAllTokens: () => void;
- addTokenCleaner: (c: () => void) => void;
+ // clearAllTokens: () => void;
+ // addTokenCleaner: (c: () => void) => void;
updateLoginStatus: (url: string, token?: string) => void;
updateToken: (token?: string) => void;
}
@@ -39,8 +39,8 @@ const BackendContext = createContext<BackendContextType>({
token: undefined,
triedToLog: false,
resetBackend: () => null,
- clearAllTokens: () => null,
- addTokenCleaner: () => null,
+ // clearAllTokens: () => null,
+ // addTokenCleaner: () => null,
updateLoginStatus: () => null,
updateToken: () => null,
});
@@ -56,30 +56,30 @@ function useBackendContextState(
_updateToken(t);
};
- const tokenCleaner = useCallback(() => {
- updateToken(undefined);
- }, []);
- const [cleaners, setCleaners] = useState([tokenCleaner]);
- const addTokenCleaner = (c: () => void) => setCleaners((cs) => [...cs, c]);
- const addTokenCleanerMemo = useCallback(
- (c: () => void) => {
- addTokenCleaner(c);
- },
- [tokenCleaner],
- );
+ // const tokenCleaner = useCallback(() => {
+ // updateToken(undefined);
+ // }, []);
+ // const [cleaners, setCleaners] = useState([tokenCleaner]);
+ // const addTokenCleaner = (c: () => void) => setCleaners((cs) => [...cs, c]);
+ // const addTokenCleanerMemo = useCallback(
+ // (c: () => void) => {
+ // addTokenCleaner(c);
+ // },
+ // [tokenCleaner],
+ // );
- const clearAllTokens = () => {
- cleaners.forEach((c) => c());
- for (let i = 0; i < localStorage.length; i++) {
- const k = localStorage.key(i);
- if (k && /^backend-token/.test(k)) localStorage.removeItem(k);
- }
- resetBackend();
- };
+ // const clearAllTokens = () => {
+ // cleaners.forEach((c) => c());
+ // for (let i = 0; i < localStorage.length; i++) {
+ // const k = localStorage.key(i);
+ // if (k && /^backend-token/.test(k)) localStorage.removeItem(k);
+ // }
+ // resetBackend();
+ // };
const updateLoginStatus = (url: string, token?: string) => {
changeBackend(url);
- if (token) updateToken(token);
+ updateToken(token);
};
return {
@@ -88,9 +88,9 @@ function useBackendContextState(
triedToLog,
updateLoginStatus,
resetBackend,
- clearAllTokens,
+ // clearAllTokens,
updateToken,
- addTokenCleaner: addTokenCleanerMemo,
+ // addTokenCleaner: addTokenCleanerMemo,
};
}
diff --git a/packages/merchant-backoffice-ui/src/declaration.d.ts b/packages/merchant-backoffice-ui/src/declaration.d.ts
index db3122266..5ca9c1e09 100644
--- a/packages/merchant-backoffice-ui/src/declaration.d.ts
+++ b/packages/merchant-backoffice-ui/src/declaration.d.ts
@@ -25,6 +25,8 @@ type EddsaSignature = string;
type WireTransferIdentifierRawP = string;
type RelativeTime = Duration;
type ImageDataUrl = string;
+type MerchantUserType = "business" | "individual";
+
export interface WithId {
id: string;
@@ -312,46 +314,8 @@ export namespace MerchantBackend {
// header.
token?: string;
}
- type FacadeCredentials = NoFacadeCredentials | BasicAuthFacadeCredentials;
-
- interface NoFacadeCredentials {
- type: "none";
- }
-
- interface BasicAuthFacadeCredentials {
- type: "basic";
-
- // Username to use to authenticate
- username: string;
-
- // Password to use to authenticate
- password: string;
- }
-
- interface MerchantBankAccount {
- // The payto:// URI where the wallet will send coins.
- payto_uri: string;
-
- // Optional base URL for a facade where the
- // merchant backend can see incoming wire
- // transfers to reconcile its accounting
- // with that of the exchange. Used by
- // taler-merchant-wirewatch.
- credit_facade_url?: string;
-
- // Credentials for accessing the credit facade.
- credit_facade_credentials?: FacadeCredentials;
- }
//POST /private/instances
interface InstanceConfigurationMessage {
- // Bank accounts of the merchant. A merchant may have
- // multiple accounts, thus this is an array. Note that by
- // removing accounts from this list the respective account is set to
- // inactive and thus unavailable for new contracts, but preserved
- // in the database as existing offers and contracts may still refer
- // to it.
- accounts: MerchantBankAccount[];
-
// Name of the merchant instance to create (will become $INSTANCE).
id: string;
@@ -361,12 +325,16 @@ export namespace MerchantBackend {
// Type of the user (business or individual).
// Defaults to 'business'. Should become mandatory field
// in the future, left as optional for API compatibility for now.
- user_type?: string;
+ user_type?: MerchantUserType;
+
+ // Merchant email for customer contact.
+ email?: string;
+
+ // Merchant public website.
+ website?: string;
- email: string;
- website: string;
- // An optional base64-encoded logo image
- logo: ImageDataUrl;
+ // Merchant logo.
+ logo?: ImageDataUrl;
// "Authentication" header required to authorize management access the instance.
// Optional, if not given authentication will be disabled for
@@ -381,17 +349,10 @@ export namespace MerchantBackend {
// (to be put into contracts).
jurisdiction: Location;
- // Maximum wire fee this instance is willing to pay.
- // Can be overridden by the frontend on a per-order basis.
- default_max_wire_fee: Amount;
-
- // Default factor for wire fee amortization calculations.
- // Can be overridden by the frontend on a per-order basis.
- default_wire_fee_amortization: Integer;
-
- // Maximum deposit fee (sum over all coins) this instance is willing to pay.
- // Can be overridden by the frontend on a per-order basis.
- default_max_deposit_fee: Amount;
+ // Use STEFAN curves to determine default fees?
+ // If false, no fees are allowed by default.
+ // Can always be overridden by the frontend on a per-order basis.
+ use_stefan: boolean;
// If the frontend does NOT specify an execution date, how long should
// we tell the exchange to wait to aggregate transactions before
@@ -406,11 +367,6 @@ export namespace MerchantBackend {
// PATCH /private/instances/$INSTANCE
interface InstanceReconfigurationMessage {
- // Bank accounts of the merchant. A merchant may have
- // multiple accounts, thus this is an array. Note that removing
- // URIs from this list deactivates the specified accounts
- // (they will no longer be used for future contracts).
- accounts: MerchantBankAccount[];
// Merchant name corresponding to this instance.
name: string;
@@ -418,7 +374,16 @@ export namespace MerchantBackend {
// Type of the user (business or individual).
// Defaults to 'business'. Should become mandatory field
// in the future, left as optional for API compatibility for now.
- user_type?: string;
+ user_type?: MerchantUserType;
+
+ // Merchant email for customer contact.
+ email?: string;
+
+ // Merchant public website.
+ website?: string;
+
+ // Merchant logo.
+ logo?: ImageDataUrl;
// The merchant's physical address (to be put into contracts).
address: Location;
@@ -427,17 +392,10 @@ export namespace MerchantBackend {
// (to be put into contracts).
jurisdiction: Location;
- // Maximum wire fee this instance is willing to pay.
- // Can be overridden by the frontend on a per-order basis.
- default_max_wire_fee: Amount;
-
- // Default factor for wire fee amortization calculations.
- // Can be overridden by the frontend on a per-order basis.
- default_wire_fee_amortization: Integer;
-
- // Maximum deposit fee (sum over all coins) this instance is willing to pay.
- // Can be overridden by the frontend on a per-order basis.
- default_max_deposit_fee: Amount;
+ // Use STEFAN curves to determine default fees?
+ // If false, no fees are allowed by default.
+ // Can always be overridden by the frontend on a per-order basis.
+ use_stefan: boolean;
// If the frontend does NOT specify an execution date, how long should
// we tell the exchange to wait to aggregate transactions before
@@ -460,7 +418,14 @@ export namespace MerchantBackend {
// Merchant name corresponding to this instance.
name: string;
- deleted?: boolean;
+ // Type of the user ("business" or "individual").
+ user_type: MerchantUserType;
+
+ // Merchant public website.
+ website?: string;
+
+ // Merchant logo.
+ logo?: ImageDataUrl;
// Merchant instance this response is about ($INSTANCE)
id: string;
@@ -472,8 +437,63 @@ export namespace MerchantBackend {
// specify the desired payment target in /order requests. Note that
// front-ends do not have to support wallets selecting payment targets.
payment_targets: string[];
+
+ // Has this instance been deleted (but not purged)?
+ deleted: boolean;
}
+ //GET /private/instances/$INSTANCE
+ interface QueryInstancesResponse {
+
+ // Merchant name corresponding to this instance.
+ name: string;
+ // Type of the user ("business" or "individual").
+ user_type: MerchantUserType;
+
+ // Merchant email for customer contact.
+ email?: string;
+
+ // Merchant public website.
+ website?: string;
+
+ // Merchant logo.
+ logo?: ImageDataUrl;
+
+ // Public key of the merchant/instance, in Crockford Base32 encoding.
+ merchant_pub: EddsaPublicKey;
+
+ // The merchant's physical address (to be put into contracts).
+ address: Location;
+
+ // The jurisdiction under which the merchant conducts its business
+ // (to be put into contracts).
+ jurisdiction: Location;
+
+ // Use STEFAN curves to determine default fees?
+ // If false, no fees are allowed by default.
+ // Can always be overridden by the frontend on a per-order basis.
+ use_stefan: boolean;
+
+ // If the frontend does NOT specify an execution date, how long should
+ // we tell the exchange to wait to aggregate transactions before
+ // executing the wire transfer? This delay is added to the current
+ // time when we generate the advisory execution time for the exchange.
+ default_wire_transfer_delay: RelativeTime;
+
+ // If the frontend does NOT specify a payment deadline, how long should
+ // offers we make be valid by default?
+ default_pay_delay: RelativeTime;
+
+ // Authentication configuration.
+ // Does not contain the token when token auth is configured.
+ auth: {
+ method: "external" | "token";
+ };
+ }
+ // DELETE /private/instances/$INSTANCE
+ }
+
+ namespace KYC {
//GET /private/instances/$INSTANCE/kyc
interface AccountKycRedirects {
// Array of pending KYCs.
@@ -513,56 +533,76 @@ export namespace MerchantBackend {
exchange_http_status: number;
}
- //GET /private/instances/$INSTANCE
- interface QueryInstancesResponse {
- // The URI where the wallet will send coins. A merchant may have
- // multiple accounts, thus this is an array.
- accounts: MerchantAccount[];
+ }
- // Merchant name corresponding to this instance.
- name: string;
+ namespace BankAccounts {
- // Public key of the merchant/instance, in Crockford Base32 encoding.
- merchant_pub: EddsaPublicKey;
+ interface AccountAddDetails {
- // The merchant's physical address (to be put into contracts).
- address: Location;
+ // payto:// URI of the account.
+ payto_uri: string;
- // The jurisdiction under which the merchant conducts its business
- // (to be put into contracts).
- jurisdiction: Location;
+ // URL from where the merchant can download information
+ // about incoming wire transfers to this account.
+ credit_facade_url?: string;
- // Maximum wire fee this instance is willing to pay.
- // Can be overridden by the frontend on a per-order basis.
- default_max_wire_fee: Amount;
+ // 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;
- // Default factor for wire fee amortization calculations.
- // Can be overridden by the frontend on a per-order basis.
- default_wire_fee_amortization: Integer;
+ }
- // Maximum deposit fee (sum over all coins) this instance is willing to pay.
- // Can be overridden by the frontend on a per-order basis.
- default_max_deposit_fee: Amount;
+ type FacadeCredentials =
+ | NoFacadeCredentials
+ | BasicAuthFacadeCredentials;
- // If the frontend does NOT specify an execution date, how long should
- // we tell the exchange to wait to aggregate transactions before
- // executing the wire transfer? This delay is added to the current
- // time when we generate the advisory execution time for the exchange.
- default_wire_transfer_delay: RelativeTime;
+ interface NoFacadeCredentials {
+ type: "none";
+ }
- // If the frontend does NOT specify a payment deadline, how long should
- // offers we make be valid by default?
- default_pay_delay: RelativeTime;
+ interface BasicAuthFacadeCredentials {
+ type: "basic";
- // Authentication configuration.
- // Does not contain the token when token auth is configured.
- auth: {
- method: "external" | "token";
- token?: string;
- };
+ // Username to use to authenticate
+ username: string;
+
+ // Password to use to authenticate
+ password: string;
+ }
+
+ interface AccountAddResponse {
+ // Hash over the wire details (including over the salt).
+ h_wire: HashCode;
+
+ // Salt used to compute h_wire.
+ salt: HashCode;
+ }
+
+ interface AccountPatchDetails {
+
+ // 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;
}
- interface MerchantAccount {
+
+ interface AccountsSummaryResponse {
+
+ // List of accounts that are known for the instance.
+ accounts: BankAccountEntry[];
+ }
+
+ interface BankAccountEntry {
// payto:// URI of the account.
payto_uri: string;
@@ -587,7 +627,6 @@ export namespace MerchantBackend {
active: boolean;
}
- // DELETE /private/instances/$INSTANCE
}
namespace Products {
@@ -957,6 +996,10 @@ export namespace MerchantBackend {
// high entropy to prevent adversarial claims (like it is
// if the backend auto-generates one). Default is 'true'.
create_token?: boolean;
+
+ // OTP device ID to associate with the order.
+ // This parameter is optional.
+ otp_id?: string;
}
type Order = MinimalOrderDetail | ContractTerms;
@@ -1031,9 +1074,9 @@ export namespace MerchantBackend {
}
}
- namespace Tips {
+ namespace Rewards {
// GET /private/reserves
- interface TippingReserveStatus {
+ interface RewardReserveStatus {
// Array of all known reserves (possibly empty!)
reserves: ReserveStatusEntry[];
}
@@ -1057,7 +1100,7 @@ export namespace MerchantBackend {
// Amount picked up so far.
pickup_amount: Amount;
- // Amount approved for tips that exceeds the pickup_amount.
+ // Amount approved for rewards that exceeds the pickup_amount.
committed_amount: Amount;
// Is this reserve active (false if it was deleted but not purged)
@@ -1068,7 +1111,7 @@ export namespace MerchantBackend {
// Amount that the merchant promises to put into the reserve
initial_balance: Amount;
- // Exchange the merchant intends to use for tipping
+ // Exchange the merchant intends to use for reward
exchange_url: string;
// Desired wire method, for example "iban" or "x-taler-bank"
@@ -1081,30 +1124,30 @@ export namespace MerchantBackend {
// Wire accounts of the exchange where to transfer the funds.
accounts: WireAccount[];
}
- interface TipCreateRequest {
- // Amount that the customer should be tipped
+ interface RewardCreateRequest {
+ // Amount that the customer should be reward
amount: Amount;
- // Justification for giving the tip
+ // Justification for giving the reward
justification: string;
- // URL that the user should be directed to after tipping,
- // will be included in the tip_token.
+ // URL that the user should be directed to after rewarding,
+ // will be included in the reward_token.
next_url: string;
}
- interface TipCreateConfirmation {
- // Unique tip identifier for the tip that was created.
- tip_id: HashCode;
+ interface RewardCreateConfirmation {
+ // Unique reward identifier for the reward that was created.
+ reward_id: HashCode;
- // taler://tip URI for the tip
- taler_tip_uri: string;
+ // taler://reward URI for the reward
+ taler_reward_uri: string;
// URL that will directly trigger processing
- // the tip when the browser is redirected to it
- tip_status_url: string;
+ // the reward when the browser is redirected to it
+ reward_status_url: string;
- // when does the tip expire
- tip_expiration: Timestamp;
+ // when does the reward expire
+ reward_expiration: Timestamp;
}
interface ReserveDetail {
@@ -1124,12 +1167,12 @@ export namespace MerchantBackend {
// Amount picked up so far.
pickup_amount: Amount;
- // Amount approved for tips that exceeds the pickup_amount.
+ // Amount approved for rewards that exceeds the pickup_amount.
committed_amount: Amount;
- // Array of all tips created by this reserves (possibly empty!).
+ // Array of all rewards created by this reserves (possibly empty!).
// Only present if asked for explicitly.
- tips?: TipStatusEntry[];
+ rewards?: RewardStatusEntry[];
// Is this reserve active (false if it was deleted but not purged)?
active: boolean;
@@ -1144,31 +1187,31 @@ export namespace MerchantBackend {
exchange_url: string;
}
- interface TipStatusEntry {
- // Unique identifier for the tip.
- tip_id: HashCode;
+ interface RewardStatusEntry {
+ // Unique identifier for the reward.
+ reward_id: HashCode;
- // Total amount of the tip that can be withdrawn.
+ // Total amount of the reward that can be withdrawn.
total_amount: Amount;
- // Human-readable reason for why the tip was granted.
+ // Human-readable reason for why the reward was granted.
reason: string;
}
- interface TipDetails {
- // Amount that we authorized for this tip.
+ interface RewardDetails {
+ // Amount that we authorized for this reward.
total_authorized: Amount;
// Amount that was picked up by the user already.
total_picked_up: Amount;
- // Human-readable reason given when authorizing the tip.
+ // Human-readable reason given when authorizing the reward.
reason: string;
- // Timestamp indicating when the tip is set to expire (may be in the past).
+ // Timestamp indicating when the reward is set to expire (may be in the past).
expiration: Timestamp;
- // Reserve public key from which the tip is funded.
+ // Reserve public key from which the reward is funded.
reserve_pub: EddsaPublicKey;
// Array showing the pickup operations of the wallet (possibly empty!).
@@ -1239,6 +1282,63 @@ export namespace MerchantBackend {
}
}
+ namespace OTP {
+ interface OtpDeviceAddDetails {
+ // Device ID to use.
+ otp_device_id: string;
+
+ // Human-readable description for the device.
+ otp_description: string;
+
+ // A base64-encoded key
+ otp_key: string;
+
+ // Algorithm for computing the POS confirmation.
+ otp_algorithm: Integer;
+
+ // Counter for counter-based OTP devices.
+ otp_ctr?: Integer;
+ }
+
+ interface OtpDevicePatchDetails {
+ // Human-readable description for the device.
+ otp_description: string;
+
+ // A base64-encoded key
+ otp_key: string | undefined;
+
+ // Algorithm for computing the POS confirmation.
+ otp_algorithm: Integer;
+
+ // Counter for counter-based OTP devices.
+ otp_ctr?: Integer;
+ }
+
+ interface OtpDeviceSummaryResponse {
+ // Array of devices that are present in our backend.
+ otp_devices: OtpDeviceEntry[];
+ }
+ interface OtpDeviceEntry {
+ // Device identifier.
+ otp_device_id: string;
+
+ // Human-readable description for the device.
+ device_description: string;
+ }
+
+ interface OtpDeviceDetails {
+ // Human-readable description for the device.
+ device_description: string;
+
+ // Algorithm for computing the POS confirmation.
+ otp_algorithm: Integer;
+
+ // Counter for counter-based OTP devices.
+ otp_ctr?: Integer;
+ }
+
+
+ }
namespace Template {
interface TemplateAddDetails {
// Template ID to use.
@@ -1247,12 +1347,9 @@ export namespace MerchantBackend {
// Human-readable description for the template.
template_description: string;
- // A base64-encoded key of the point-of-sale.
+ // OTP device ID.
// This parameter is optional.
- pos_key?: string;
-
- // Algorithm for computing the POS confirmation, 0 for none.
- pos_algorithm?: number;
+ otp_id?: string;
// Additional information in a separate template.
template_contract: TemplateContractDetails;
@@ -1276,12 +1373,9 @@ export namespace MerchantBackend {
// Human-readable description for the template.
template_description: string;
- // A base64-encoded key of the point-of-sale.
+ // OTP device ID.
// This parameter is optional.
- pos_key?: string;
-
- // Algorithm for computing the POS confirmation, 0 for none.
- pos_algorithm?: Integer;
+ otp_id?: string;
// Additional information in a separate template.
template_contract: TemplateContractDetails;
@@ -1304,12 +1398,9 @@ export namespace MerchantBackend {
// Human-readable description for the template.
template_description: string;
- // A base64-encoded key of the point-of-sale.
+ // OTP device ID.
// This parameter is optional.
- pos_key?: string;
-
- // Algorithm for computing the POS confirmation, 0 for none.
- pos_algorithm?: Integer;
+ otp_id?: string;
// Additional information in a separate template.
template_contract: TemplateContractDetails;
@@ -1424,21 +1515,6 @@ export namespace MerchantBackend {
// Maximum total deposit fee accepted by the merchant for this contract
max_fee: Amount;
- // Maximum wire fee accepted by the merchant (customer share to be
- // divided by the 'wire_fee_amortization' factor, and further reduced
- // if deposit fees are below 'max_fee'). Default if missing is zero.
- max_wire_fee: Amount;
-
- // Over how many customer transactions does the merchant expect to
- // amortize wire fees on average? If the exchange's wire fee is
- // above 'max_wire_fee', the difference is divided by this number
- // to compute the expected customer's contribution to the wire fee.
- // The customer's contribution may further be reduced by the difference
- // between the 'max_fee' and the sum of the actual deposit fees.
- // Optional, default value if missing is 1. 0 and negative values are
- // invalid and also interpreted as 1.
- wire_fee_amortization: number;
-
// List of products that are part of the purchase (see Product).
products: Product[];
diff --git a/packages/merchant-backoffice-ui/src/hooks/backend.ts b/packages/merchant-backoffice-ui/src/hooks/backend.ts
index 145a366f6..ecd34df6d 100644
--- a/packages/merchant-backoffice-ui/src/hooks/backend.ts
+++ b/packages/merchant-backoffice-ui/src/hooks/backend.ts
@@ -33,8 +33,9 @@ import {
} from "@gnu-taler/web-util/browser";
import { useApiContext } from "@gnu-taler/web-util/browser";
+
export function useMatchMutate(): (
- re: RegExp,
+ re?: RegExp,
value?: unknown,
) => Promise<any> {
const { cache, mutate } = useSWRConfig();
@@ -45,13 +46,19 @@ export function useMatchMutate(): (
);
}
- return function matchRegexMutate(re: RegExp, value?: unknown) {
- const allKeys = Array.from(cache.keys());
- const keys = allKeys.filter((key) => re.test(key));
- const mutations = keys.map((key) => {
- return mutate(key, value, true);
+ return function matchRegexMutate(re?: RegExp) {
+ return mutate((key) => {
+ // evict if no key or regex === all
+ if (!key || !re) return true
+ // match string
+ if (typeof key === 'string' && re.test(key)) return true
+ // record or object have the path at [0]
+ if (typeof key === 'object' && re.test(key[0])) return true
+ //key didn't match regex
+ return false
+ }, undefined, {
+ revalidate: true,
});
- return Promise.all(mutations);
};
}
@@ -106,32 +113,32 @@ interface useBackendInstanceRequestType {
) => Promise<HttpResponseOk<T>>;
fetcher: <T>(endpoint: string) => Promise<HttpResponseOk<T>>;
reserveDetailFetcher: <T>(endpoint: string) => Promise<HttpResponseOk<T>>;
- tipsDetailFetcher: <T>(endpoint: string) => Promise<HttpResponseOk<T>>;
- multiFetcher: <T>(url: string[]) => Promise<HttpResponseOk<T>[]>;
+ rewardsDetailFetcher: <T>(endpoint: string) => Promise<HttpResponseOk<T>>;
+ multiFetcher: <T>(params: [url: string[]]) => Promise<HttpResponseOk<T>[]>;
orderFetcher: <T>(
- endpoint: string,
- paid?: YesOrNo,
- refunded?: YesOrNo,
- wired?: YesOrNo,
- searchDate?: Date,
- delta?: number,
+ params: [endpoint: string,
+ paid?: YesOrNo,
+ refunded?: YesOrNo,
+ wired?: YesOrNo,
+ searchDate?: Date,
+ delta?: number,]
) => Promise<HttpResponseOk<T>>;
transferFetcher: <T>(
- endpoint: string,
- payto_uri?: string,
- verified?: string,
- position?: string,
- delta?: number,
+ params: [endpoint: string,
+ payto_uri?: string,
+ verified?: string,
+ position?: string,
+ delta?: number,]
) => Promise<HttpResponseOk<T>>;
templateFetcher: <T>(
- endpoint: string,
- position?: string,
- delta?: number,
+ params: [endpoint: string,
+ position?: string,
+ delta?: number]
) => Promise<HttpResponseOk<T>>;
webhookFetcher: <T>(
- endpoint: string,
- position?: string,
- delta?: number,
+ params: [endpoint: string,
+ position?: string,
+ delta?: number]
) => Promise<HttpResponseOk<T>>;
}
interface useBackendBaseRequestType {
@@ -147,7 +154,7 @@ export function useCredentialsChecker() {
const { request } = useApiContext();
//check against instance details endpoint
//while merchant backend doesn't have a login endpoint
- return async function testLogin(
+ async function testLogin(
instance: string,
token: string,
): Promise<{
@@ -167,6 +174,7 @@ export function useCredentialsChecker() {
return { valid: false, cause: ErrorType.UNEXPECTED };
}
};
+ return testLogin
}
/**
@@ -212,8 +220,9 @@ export function useBackendInstanceRequest(): useBackendInstanceRequestType {
const multiFetcher = useCallback(
function multiFetcherImpl<T>(
- endpoints: string[],
+ args: [endpoints: string[]],
): Promise<HttpResponseOk<T>[]> {
+ const [endpoints] = args
return Promise.all(
endpoints.map((endpoint) =>
requestHandler<T>(baseUrl, endpoint, { token }),
@@ -232,13 +241,14 @@ export function useBackendInstanceRequest(): useBackendInstanceRequestType {
const orderFetcher = useCallback(
function orderFetcherImpl<T>(
- endpoint: string,
- paid?: YesOrNo,
- refunded?: YesOrNo,
- wired?: YesOrNo,
- searchDate?: Date,
- delta?: number,
+ args: [endpoint: string,
+ paid?: YesOrNo,
+ refunded?: YesOrNo,
+ wired?: YesOrNo,
+ searchDate?: Date,
+ delta?: number,]
): Promise<HttpResponseOk<T>> {
+ const [endpoint, paid, refunded, wired, searchDate, delta] = args
const date_s =
delta && delta < 0 && searchDate
? (searchDate.getTime() / 1000) + 1
@@ -260,7 +270,7 @@ export function useBackendInstanceRequest(): useBackendInstanceRequestType {
): Promise<HttpResponseOk<T>> {
return requestHandler<T>(baseUrl, endpoint, {
params: {
- tips: "yes",
+ rewards: "yes",
},
token,
});
@@ -268,8 +278,8 @@ export function useBackendInstanceRequest(): useBackendInstanceRequestType {
[baseUrl, token],
);
- const tipsDetailFetcher = useCallback(
- function tipsDetailFetcherImpl<T>(
+ const rewardsDetailFetcher = useCallback(
+ function rewardsDetailFetcherImpl<T>(
endpoint: string,
): Promise<HttpResponseOk<T>> {
return requestHandler<T>(baseUrl, endpoint, {
@@ -284,12 +294,13 @@ export function useBackendInstanceRequest(): useBackendInstanceRequestType {
const transferFetcher = useCallback(
function transferFetcherImpl<T>(
- endpoint: string,
- payto_uri?: string,
- verified?: string,
- position?: string,
- delta?: number,
+ args: [endpoint: string,
+ payto_uri?: string,
+ verified?: string,
+ position?: string,
+ delta?: number,]
): Promise<HttpResponseOk<T>> {
+ const [endpoint, payto_uri, verified, position, delta] = args
const params: any = {};
if (payto_uri !== undefined) params.payto_uri = payto_uri;
if (verified !== undefined) params.verified = verified;
@@ -305,10 +316,11 @@ export function useBackendInstanceRequest(): useBackendInstanceRequestType {
const templateFetcher = useCallback(
function templateFetcherImpl<T>(
- endpoint: string,
- position?: string,
- delta?: number,
+ args: [endpoint: string,
+ position?: string,
+ delta?: number,]
): Promise<HttpResponseOk<T>> {
+ const [endpoint, position, delta] = args
const params: any = {};
if (delta !== undefined) {
params.limit = delta;
@@ -322,10 +334,11 @@ export function useBackendInstanceRequest(): useBackendInstanceRequestType {
const webhookFetcher = useCallback(
function webhookFetcherImpl<T>(
- endpoint: string,
- position?: string,
- delta?: number,
+ args: [endpoint: string,
+ position?: string,
+ delta?: number,]
): Promise<HttpResponseOk<T>> {
+ const [endpoint, position, delta] = args
const params: any = {};
if (delta !== undefined) {
params.limit = delta;
@@ -343,7 +356,7 @@ export function useBackendInstanceRequest(): useBackendInstanceRequestType {
multiFetcher,
orderFetcher,
reserveDetailFetcher,
- tipsDetailFetcher,
+ rewardsDetailFetcher,
transferFetcher,
templateFetcher,
webhookFetcher,
diff --git a/packages/merchant-backoffice-ui/src/hooks/bank.ts b/packages/merchant-backoffice-ui/src/hooks/bank.ts
new file mode 100644
index 000000000..03b064646
--- /dev/null
+++ b/packages/merchant-backoffice-ui/src/hooks/bank.ts
@@ -0,0 +1,217 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021-2023 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/>
+ */
+import {
+ HttpResponse,
+ HttpResponseOk,
+ HttpResponsePaginated,
+ RequestError,
+} from "@gnu-taler/web-util/browser";
+import { useEffect, useState } from "preact/hooks";
+import { MerchantBackend } from "../declaration.js";
+import { MAX_RESULT_SIZE, PAGE_SIZE } from "../utils/constants.js";
+import { useBackendInstanceRequest, useMatchMutate } from "./backend.js";
+
+// FIX default import https://github.com/microsoft/TypeScript/issues/49189
+import _useSWR, { SWRHook } from "swr";
+const useSWR = _useSWR as unknown as SWRHook;
+
+// const MOCKED_ACCOUNTS: Record<string, MerchantBackend.BankAccounts.AccountAddDetails> = {
+// "hwire1": {
+// h_wire: "hwire1",
+// payto_uri: "payto://fake/iban/123",
+// salt: "qwe",
+// },
+// "hwire2": {
+// h_wire: "hwire2",
+// payto_uri: "payto://fake/iban/123",
+// salt: "qwe2",
+// },
+// }
+
+export function useBankAccountAPI(): BankAccountAPI {
+ const mutateAll = useMatchMutate();
+ const { request } = useBackendInstanceRequest();
+
+ const createBankAccount = async (
+ data: MerchantBackend.BankAccounts.AccountAddDetails,
+ ): Promise<HttpResponseOk<void>> => {
+ // MOCKED_ACCOUNTS[data.h_wire] = data
+ // return Promise.resolve({ ok: true, data: undefined });
+ const res = await request<void>(`/private/accounts`, {
+ method: "POST",
+ data,
+ });
+ await mutateAll(/.*private\/accounts.*/);
+ return res;
+ };
+
+ const updateBankAccount = async (
+ h_wire: string,
+ data: MerchantBackend.BankAccounts.AccountPatchDetails,
+ ): Promise<HttpResponseOk<void>> => {
+ // MOCKED_ACCOUNTS[h_wire].credit_facade_credentials = data.credit_facade_credentials
+ // MOCKED_ACCOUNTS[h_wire].credit_facade_url = data.credit_facade_url
+ // return Promise.resolve({ ok: true, data: undefined });
+ const res = await request<void>(`/private/accounts/${h_wire}`, {
+ method: "PATCH",
+ data,
+ });
+ await mutateAll(/.*private\/accounts.*/);
+ return res;
+ };
+
+ const deleteBankAccount = async (
+ h_wire: string,
+ ): Promise<HttpResponseOk<void>> => {
+ // delete MOCKED_ACCOUNTS[h_wire]
+ // return Promise.resolve({ ok: true, data: undefined });
+ const res = await request<void>(`/private/accounts/${h_wire}`, {
+ method: "DELETE",
+ });
+ await mutateAll(/.*private\/accounts.*/);
+ return res;
+ };
+
+ return {
+ createBankAccount,
+ updateBankAccount,
+ deleteBankAccount,
+ };
+}
+
+export interface BankAccountAPI {
+ createBankAccount: (
+ data: MerchantBackend.BankAccounts.AccountAddDetails,
+ ) => Promise<HttpResponseOk<void>>;
+ updateBankAccount: (
+ id: string,
+ data: MerchantBackend.BankAccounts.AccountPatchDetails,
+ ) => Promise<HttpResponseOk<void>>;
+ deleteBankAccount: (id: string) => Promise<HttpResponseOk<void>>;
+}
+
+export interface InstanceBankAccountFilter {
+}
+
+export function useInstanceBankAccounts(
+ args?: InstanceBankAccountFilter,
+ updatePosition?: (id: string) => void,
+): HttpResponsePaginated<
+ MerchantBackend.BankAccounts.AccountsSummaryResponse,
+ MerchantBackend.ErrorDetail
+> {
+ // return {
+ // ok: true,
+ // loadMore() { },
+ // loadMorePrev() { },
+ // data: {
+ // accounts: Object.values(MOCKED_ACCOUNTS).map(e => ({
+ // ...e,
+ // active: true,
+ // }))
+ // }
+ // }
+ const { fetcher } = useBackendInstanceRequest();
+
+ const [pageAfter, setPageAfter] = useState(1);
+
+ const totalAfter = pageAfter * PAGE_SIZE;
+ const {
+ data: afterData,
+ error: afterError,
+ isValidating: loadingAfter,
+ } = useSWR<
+ HttpResponseOk<MerchantBackend.BankAccounts.AccountsSummaryResponse>,
+ RequestError<MerchantBackend.ErrorDetail>
+ >([`/private/accounts`], fetcher);
+
+ const [lastAfter, setLastAfter] = useState<
+ HttpResponse<
+ MerchantBackend.BankAccounts.AccountsSummaryResponse,
+ MerchantBackend.ErrorDetail
+ >
+ >({ loading: true });
+ useEffect(() => {
+ if (afterData) setLastAfter(afterData);
+ }, [afterData /*, beforeData*/]);
+
+ if (afterError) return afterError.cause;
+
+ // if the query returns less that we ask, then we have reach the end or beginning
+ const isReachingEnd =
+ afterData && afterData.data.accounts.length < totalAfter;
+ const isReachingStart = false;
+
+ const pagination = {
+ isReachingEnd,
+ isReachingStart,
+ loadMore: () => {
+ if (!afterData || isReachingEnd) return;
+ if (afterData.data.accounts.length < MAX_RESULT_SIZE) {
+ setPageAfter(pageAfter + 1);
+ } else {
+ const from = `${afterData.data.accounts[afterData.data.accounts.length - 1]
+ .h_wire
+ }`;
+ if (from && updatePosition) updatePosition(from);
+ }
+ },
+ loadMorePrev: () => {
+ },
+ };
+
+ const accounts = !afterData ? [] : (afterData || lastAfter).data.accounts;
+ if (loadingAfter /* || loadingBefore */)
+ return { loading: true, data: { accounts } };
+ if (/*beforeData &&*/ afterData) {
+ return { ok: true, data: { accounts }, ...pagination };
+ }
+ return { loading: true };
+}
+
+export function useBankAccountDetails(
+ h_wire: string,
+): HttpResponse<
+ MerchantBackend.BankAccounts.BankAccountEntry,
+ MerchantBackend.ErrorDetail
+> {
+ // return {
+ // ok: true,
+ // data: {
+ // ...MOCKED_ACCOUNTS[h_wire],
+ // active: true,
+ // }
+ // }
+ const { fetcher } = useBackendInstanceRequest();
+
+ const { data, error, isValidating } = useSWR<
+ HttpResponseOk<MerchantBackend.BankAccounts.BankAccountEntry>,
+ RequestError<MerchantBackend.ErrorDetail>
+ >([`/private/accounts/${h_wire}`], fetcher, {
+ refreshInterval: 0,
+ refreshWhenHidden: false,
+ revalidateOnFocus: false,
+ revalidateOnReconnect: false,
+ refreshWhenOffline: false,
+ });
+
+ if (isValidating) return { loading: true, data: data?.data };
+ if (data) {
+ return data;
+ }
+ if (error) return error.cause;
+ return { loading: true };
+}
diff --git a/packages/merchant-backoffice-ui/src/hooks/index.ts b/packages/merchant-backoffice-ui/src/hooks/index.ts
index b77b9dea8..79b22304a 100644
--- a/packages/merchant-backoffice-ui/src/hooks/index.ts
+++ b/packages/merchant-backoffice-ui/src/hooks/index.ts
@@ -19,9 +19,10 @@
* @author Sebastian Javier Marchano (sebasjm)
*/
-import { StateUpdater, useCallback, useState } from "preact/hooks";
+import { StateUpdater, useCallback, useEffect, useState } from "preact/hooks";
import { ValueOrFunction } from "../utils/types.js";
import { useMemoryStorage } from "@gnu-taler/web-util/browser";
+import { useMatchMutate } from "./backend.js";
const calculateRootPath = () => {
const rootPath =
@@ -56,8 +57,22 @@ export function useBackendDefaultToken(
): [string | undefined, ((d: string | undefined) => void)] {
// uncomment for testing
initialValue = "secret-token:secret" as string | undefined
- const { update, value } = useMemoryStorage(`backend-token`, initialValue)
- return [value, update];
+ const { update: setToken, value: token, reset } = useMemoryStorage(`backend-token`, initialValue)
+ const clearCache = useMatchMutate()
+ useEffect(() => {
+ clearCache()
+ }, [token])
+
+ function updateToken(
+ value: (string | undefined)
+ ): void {
+ if (value === undefined) {
+ reset()
+ } else {
+ setToken(value)
+ }
+ }
+ return [token, updateToken];
}
export function useBackendInstanceToken(
@@ -73,14 +88,12 @@ export function useBackendInstanceToken(
function updateToken(
value: (string | undefined)
): void {
- console.log("seeting token", value)
if (value === undefined) {
reset()
} else {
setToken(value)
}
}
- console.log("token", token)
return [token, updateToken];
}
diff --git a/packages/merchant-backoffice-ui/src/hooks/instance.test.ts b/packages/merchant-backoffice-ui/src/hooks/instance.test.ts
index f78de85dd..d15b3f6d7 100644
--- a/packages/merchant-backoffice-ui/src/hooks/instance.test.ts
+++ b/packages/merchant-backoffice-ui/src/hooks/instance.test.ts
@@ -113,7 +113,7 @@ describe("instance api interaction with details", () => {
name: "instance_name",
auth: {
method: "token",
- token: "not-secret",
+ // token: "not-secret",
},
} as MerchantBackend.Instances.QueryInstancesResponse,
});
@@ -154,7 +154,7 @@ describe("instance api interaction with details", () => {
name: "instance_name",
auth: {
method: "token",
- token: "secret",
+ // token: "secret",
},
} as MerchantBackend.Instances.QueryInstancesResponse,
});
@@ -190,7 +190,7 @@ describe("instance api interaction with details", () => {
name: "instance_name",
auth: {
method: "token",
- token: "not-secret",
+ // token: "not-secret",
},
} as MerchantBackend.Instances.QueryInstancesResponse,
});
diff --git a/packages/merchant-backoffice-ui/src/hooks/instance.ts b/packages/merchant-backoffice-ui/src/hooks/instance.ts
index eae65d64c..32ed30c6f 100644
--- a/packages/merchant-backoffice-ui/src/hooks/instance.ts
+++ b/packages/merchant-backoffice-ui/src/hooks/instance.ts
@@ -198,6 +198,7 @@ export function useInstanceDetails(): HttpResponse<
revalidateOnFocus: false,
revalidateOnReconnect: false,
refreshWhenOffline: false,
+ revalidateIfStale: false,
errorRetryCount: 0,
errorRetryInterval: 1,
shouldRetryOnError: false,
@@ -211,7 +212,7 @@ export function useInstanceDetails(): HttpResponse<
type KYCStatus =
| { type: "ok" }
- | { type: "redirect"; status: MerchantBackend.Instances.AccountKycRedirects };
+ | { type: "redirect"; status: MerchantBackend.KYC.AccountKycRedirects };
export function useInstanceKYCDetails(): HttpResponse<
KYCStatus,
@@ -220,7 +221,7 @@ export function useInstanceKYCDetails(): HttpResponse<
const { fetcher } = useBackendInstanceRequest();
const { data, error } = useSWR<
- HttpResponseOk<MerchantBackend.Instances.AccountKycRedirects>,
+ HttpResponseOk<MerchantBackend.KYC.AccountKycRedirects>,
RequestError<MerchantBackend.ErrorDetail>
>([`/private/kyc`], fetcher, {
refreshInterval: 60 * 1000,
diff --git a/packages/merchant-backoffice-ui/src/hooks/otp.ts b/packages/merchant-backoffice-ui/src/hooks/otp.ts
new file mode 100644
index 000000000..3544b4881
--- /dev/null
+++ b/packages/merchant-backoffice-ui/src/hooks/otp.ts
@@ -0,0 +1,223 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021-2023 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/>
+ */
+import {
+ HttpResponse,
+ HttpResponseOk,
+ HttpResponsePaginated,
+ RequestError,
+} from "@gnu-taler/web-util/browser";
+import { useEffect, useState } from "preact/hooks";
+import { MerchantBackend } from "../declaration.js";
+import { MAX_RESULT_SIZE, PAGE_SIZE } from "../utils/constants.js";
+import { useBackendInstanceRequest, useMatchMutate } from "./backend.js";
+
+// FIX default import https://github.com/microsoft/TypeScript/issues/49189
+import _useSWR, { SWRHook } from "swr";
+const useSWR = _useSWR as unknown as SWRHook;
+
+const MOCKED_DEVICES: Record<string, MerchantBackend.OTP.OtpDeviceAddDetails> = {
+ "1": {
+ otp_description: "first device",
+ otp_algorithm: 1,
+ otp_device_id: "1",
+ otp_key: "123",
+ },
+ "2": {
+ otp_description: "second device",
+ otp_algorithm: 0,
+ otp_device_id: "2",
+ otp_key: "456",
+ }
+}
+
+export function useOtpDeviceAPI(): OtpDeviceAPI {
+ const mutateAll = useMatchMutate();
+ const { request } = useBackendInstanceRequest();
+
+ const createOtpDevice = async (
+ data: MerchantBackend.OTP.OtpDeviceAddDetails,
+ ): Promise<HttpResponseOk<void>> => {
+ // MOCKED_DEVICES[data.otp_device_id] = data
+ // return Promise.resolve({ ok: true, data: undefined });
+ const res = await request<void>(`/private/otp-devices`, {
+ method: "POST",
+ data,
+ });
+ await mutateAll(/.*private\/otp-devices.*/);
+ return res;
+ };
+
+ const updateOtpDevice = async (
+ deviceId: string,
+ data: MerchantBackend.OTP.OtpDevicePatchDetails,
+ ): Promise<HttpResponseOk<void>> => {
+ // MOCKED_DEVICES[deviceId].otp_algorithm = data.otp_algorithm
+ // MOCKED_DEVICES[deviceId].otp_ctr = data.otp_ctr
+ // MOCKED_DEVICES[deviceId].otp_device_description = data.otp_device_description
+ // MOCKED_DEVICES[deviceId].otp_key = data.otp_key
+ // return Promise.resolve({ ok: true, data: undefined });
+ const res = await request<void>(`/private/otp-devices/${deviceId}`, {
+ method: "PATCH",
+ data,
+ });
+ await mutateAll(/.*private\/otp-devices.*/);
+ return res;
+ };
+
+ const deleteOtpDevice = async (
+ deviceId: string,
+ ): Promise<HttpResponseOk<void>> => {
+ // delete MOCKED_DEVICES[deviceId]
+ // return Promise.resolve({ ok: true, data: undefined });
+ const res = await request<void>(`/private/otp-devices/${deviceId}`, {
+ method: "DELETE",
+ });
+ await mutateAll(/.*private\/otp-devices.*/);
+ return res;
+ };
+
+ return {
+ createOtpDevice,
+ updateOtpDevice,
+ deleteOtpDevice,
+ };
+}
+
+export interface OtpDeviceAPI {
+ createOtpDevice: (
+ data: MerchantBackend.OTP.OtpDeviceAddDetails,
+ ) => Promise<HttpResponseOk<void>>;
+ updateOtpDevice: (
+ id: string,
+ data: MerchantBackend.OTP.OtpDevicePatchDetails,
+ ) => Promise<HttpResponseOk<void>>;
+ deleteOtpDevice: (id: string) => Promise<HttpResponseOk<void>>;
+}
+
+export interface InstanceOtpDeviceFilter {
+}
+
+export function useInstanceOtpDevices(
+ args?: InstanceOtpDeviceFilter,
+ updatePosition?: (id: string) => void,
+): HttpResponsePaginated<
+ MerchantBackend.OTP.OtpDeviceSummaryResponse,
+ MerchantBackend.ErrorDetail
+> {
+ // return {
+ // ok: true,
+ // loadMore: () => { },
+ // loadMorePrev: () => { },
+ // data: {
+ // otp_devices: Object.values(MOCKED_DEVICES).map(d => ({
+ // device_description: d.otp_device_description,
+ // otp_device_id: d.otp_device_id
+ // }))
+ // }
+ // }
+
+ const { fetcher } = useBackendInstanceRequest();
+
+ const [pageAfter, setPageAfter] = useState(1);
+
+ const totalAfter = pageAfter * PAGE_SIZE;
+ const {
+ data: afterData,
+ error: afterError,
+ isValidating: loadingAfter,
+ } = useSWR<
+ HttpResponseOk<MerchantBackend.OTP.OtpDeviceSummaryResponse>,
+ RequestError<MerchantBackend.ErrorDetail>
+ >([`/private/otp-devices`], fetcher);
+
+ const [lastAfter, setLastAfter] = useState<
+ HttpResponse<
+ MerchantBackend.OTP.OtpDeviceSummaryResponse,
+ MerchantBackend.ErrorDetail
+ >
+ >({ loading: true });
+ useEffect(() => {
+ if (afterData) setLastAfter(afterData);
+ }, [afterData /*, beforeData*/]);
+
+ if (afterError) return afterError.cause;
+
+ // if the query returns less that we ask, then we have reach the end or beginning
+ const isReachingEnd =
+ afterData && afterData.data.otp_devices.length < totalAfter;
+ const isReachingStart = false;
+
+ const pagination = {
+ isReachingEnd,
+ isReachingStart,
+ loadMore: () => {
+ if (!afterData || isReachingEnd) return;
+ if (afterData.data.otp_devices.length < MAX_RESULT_SIZE) {
+ setPageAfter(pageAfter + 1);
+ } else {
+ const from = `${afterData.data.otp_devices[afterData.data.otp_devices.length - 1]
+ .otp_device_id
+ }`;
+ if (from && updatePosition) updatePosition(from);
+ }
+ },
+ loadMorePrev: () => {
+ },
+ };
+
+ const otp_devices = !afterData ? [] : (afterData || lastAfter).data.otp_devices;
+ if (loadingAfter /* || loadingBefore */)
+ return { loading: true, data: { otp_devices } };
+ if (/*beforeData &&*/ afterData) {
+ return { ok: true, data: { otp_devices }, ...pagination };
+ }
+ return { loading: true };
+}
+
+export function useOtpDeviceDetails(
+ deviceId: string,
+): HttpResponse<
+ MerchantBackend.OTP.OtpDeviceDetails,
+ MerchantBackend.ErrorDetail
+> {
+ // return {
+ // ok: true,
+ // data: {
+ // device_description: MOCKED_DEVICES[deviceId].otp_device_description,
+ // otp_algorithm: MOCKED_DEVICES[deviceId].otp_algorithm,
+ // otp_ctr: MOCKED_DEVICES[deviceId].otp_ctr
+ // }
+ // }
+ const { fetcher } = useBackendInstanceRequest();
+
+ const { data, error, isValidating } = useSWR<
+ HttpResponseOk<MerchantBackend.OTP.OtpDeviceDetails>,
+ RequestError<MerchantBackend.ErrorDetail>
+ >([`/private/otp-devices/${deviceId}`], fetcher, {
+ refreshInterval: 0,
+ refreshWhenHidden: false,
+ revalidateOnFocus: false,
+ revalidateOnReconnect: false,
+ refreshWhenOffline: false,
+ });
+
+ if (isValidating) return { loading: true, data: data?.data };
+ if (data) {
+ return data;
+ }
+ if (error) return error.cause;
+ return { loading: true };
+}
diff --git a/packages/merchant-backoffice-ui/src/hooks/reserve.test.ts b/packages/merchant-backoffice-ui/src/hooks/reserve.test.ts
index d2831ecff..b3eecd754 100644
--- a/packages/merchant-backoffice-ui/src/hooks/reserve.test.ts
+++ b/packages/merchant-backoffice-ui/src/hooks/reserve.test.ts
@@ -25,16 +25,16 @@ import {
useInstanceReserves,
useReserveDetails,
useReservesAPI,
- useTipDetails,
+ useRewardDetails,
} from "./reserves.js";
import { ApiMockEnvironment } from "./testing.js";
import {
- API_AUTHORIZE_TIP,
- API_AUTHORIZE_TIP_FOR_RESERVE,
+ API_AUTHORIZE_REWARD,
+ API_AUTHORIZE_REWARD_FOR_RESERVE,
API_CREATE_RESERVE,
API_DELETE_RESERVE,
API_GET_RESERVE_BY_ID,
- API_GET_TIP_BY_ID,
+ API_GET_REWARD_BY_ID,
API_LIST_RESERVES,
} from "./urls.js";
import * as tests from "@gnu-taler/web-util/testing";
@@ -48,7 +48,7 @@ describe("reserve api interaction with listing", () => {
reserves: [
{
reserve_pub: "11",
- } as MerchantBackend.Tips.ReserveStatusEntry,
+ } as MerchantBackend.Rewards.ReserveStatusEntry,
],
},
});
@@ -89,10 +89,10 @@ describe("reserve api interaction with listing", () => {
reserves: [
{
reserve_pub: "11",
- } as MerchantBackend.Tips.ReserveStatusEntry,
+ } as MerchantBackend.Rewards.ReserveStatusEntry,
{
reserve_pub: "22",
- } as MerchantBackend.Tips.ReserveStatusEntry,
+ } as MerchantBackend.Rewards.ReserveStatusEntry,
],
},
});
@@ -115,10 +115,10 @@ describe("reserve api interaction with listing", () => {
reserves: [
{
reserve_pub: "11",
- } as MerchantBackend.Tips.ReserveStatusEntry,
+ } as MerchantBackend.Rewards.ReserveStatusEntry,
{
reserve_pub: "22",
- } as MerchantBackend.Tips.ReserveStatusEntry,
+ } as MerchantBackend.Rewards.ReserveStatusEntry,
],
});
},
@@ -138,13 +138,13 @@ describe("reserve api interaction with listing", () => {
reserves: [
{
reserve_pub: "11",
- } as MerchantBackend.Tips.ReserveStatusEntry,
+ } as MerchantBackend.Rewards.ReserveStatusEntry,
{
reserve_pub: "22",
- } as MerchantBackend.Tips.ReserveStatusEntry,
+ } as MerchantBackend.Rewards.ReserveStatusEntry,
{
reserve_pub: "33",
- } as MerchantBackend.Tips.ReserveStatusEntry,
+ } as MerchantBackend.Rewards.ReserveStatusEntry,
],
},
});
@@ -182,10 +182,10 @@ describe("reserve api interaction with listing", () => {
reserves: [
{
reserve_pub: "22",
- } as MerchantBackend.Tips.ReserveStatusEntry,
+ } as MerchantBackend.Rewards.ReserveStatusEntry,
{
reserve_pub: "33",
- } as MerchantBackend.Tips.ReserveStatusEntry,
+ } as MerchantBackend.Rewards.ReserveStatusEntry,
],
},
});
@@ -213,16 +213,16 @@ describe("reserve api interaction with listing", () => {
});
describe("reserve api interaction with details", () => {
- it("should evict cache when adding a tip for a specific reserve", async () => {
+ it("should evict cache when adding a reward for a specific reserve", async () => {
const env = new ApiMockEnvironment();
env.addRequestExpectation(API_GET_RESERVE_BY_ID("11"), {
response: {
accounts: [{ payto_uri: "payto://here" }],
- tips: [{ reason: "why?", tip_id: "id1", total_amount: "USD:10" }],
- } as MerchantBackend.Tips.ReserveDetail,
+ rewards: [{ reason: "why?", reward_id: "id1", total_amount: "USD:10" }],
+ } as MerchantBackend.Rewards.ReserveDetail,
qparam: {
- tips: "yes",
+ rewards: "yes",
},
});
@@ -246,37 +246,37 @@ describe("reserve api interaction with details", () => {
if (!query.ok) return;
expect(query.data).deep.equals({
accounts: [{ payto_uri: "payto://here" }],
- tips: [{ reason: "why?", tip_id: "id1", total_amount: "USD:10" }],
+ rewards: [{ reason: "why?", reward_id: "id1", total_amount: "USD:10" }],
});
- env.addRequestExpectation(API_AUTHORIZE_TIP_FOR_RESERVE("11"), {
+ env.addRequestExpectation(API_AUTHORIZE_REWARD_FOR_RESERVE("11"), {
request: {
amount: "USD:12",
justification: "not",
next_url: "http://taler.net",
},
response: {
- tip_id: "id2",
- taler_tip_uri: "uri",
- tip_expiration: { t_s: 1 },
- tip_status_url: "url",
+ reward_id: "id2",
+ taler_reward_uri: "uri",
+ reward_expiration: { t_s: 1 },
+ reward_status_url: "url",
},
});
env.addRequestExpectation(API_GET_RESERVE_BY_ID("11"), {
response: {
accounts: [{ payto_uri: "payto://here" }],
- tips: [
- { reason: "why?", tip_id: "id1", total_amount: "USD:10" },
- { reason: "not", tip_id: "id2", total_amount: "USD:12" },
+ rewards: [
+ { reason: "why?", reward_id: "id1", total_amount: "USD:10" },
+ { reason: "not", reward_id: "id2", total_amount: "USD:12" },
],
- } as MerchantBackend.Tips.ReserveDetail,
+ } as MerchantBackend.Rewards.ReserveDetail,
qparam: {
- tips: "yes",
+ rewards: "yes",
},
});
- api.authorizeTipReserve("11", {
+ api.authorizeRewardReserve("11", {
amount: "USD:12",
justification: "not",
next_url: "http://taler.net",
@@ -294,9 +294,9 @@ describe("reserve api interaction with details", () => {
expect(query.data).deep.equals({
accounts: [{ payto_uri: "payto://here" }],
- tips: [
- { reason: "why?", tip_id: "id1", total_amount: "USD:10" },
- { reason: "not", tip_id: "id2", total_amount: "USD:12" },
+ rewards: [
+ { reason: "why?", reward_id: "id1", total_amount: "USD:10" },
+ { reason: "not", reward_id: "id2", total_amount: "USD:12" },
],
});
},
@@ -308,16 +308,16 @@ describe("reserve api interaction with details", () => {
expect(env.assertJustExpectedRequestWereMade()).deep.eq({ result: "ok" });
});
- it("should evict cache when adding a tip for a random reserve", async () => {
+ it("should evict cache when adding a reward for a random reserve", async () => {
const env = new ApiMockEnvironment();
env.addRequestExpectation(API_GET_RESERVE_BY_ID("11"), {
response: {
accounts: [{ payto_uri: "payto://here" }],
- tips: [{ reason: "why?", tip_id: "id1", total_amount: "USD:10" }],
- } as MerchantBackend.Tips.ReserveDetail,
+ rewards: [{ reason: "why?", reward_id: "id1", total_amount: "USD:10" }],
+ } as MerchantBackend.Rewards.ReserveDetail,
qparam: {
- tips: "yes",
+ rewards: "yes",
},
});
@@ -341,37 +341,37 @@ describe("reserve api interaction with details", () => {
if (!query.ok) return;
expect(query.data).deep.equals({
accounts: [{ payto_uri: "payto://here" }],
- tips: [{ reason: "why?", tip_id: "id1", total_amount: "USD:10" }],
+ rewards: [{ reason: "why?", reward_id: "id1", total_amount: "USD:10" }],
});
- env.addRequestExpectation(API_AUTHORIZE_TIP, {
+ env.addRequestExpectation(API_AUTHORIZE_REWARD, {
request: {
amount: "USD:12",
justification: "not",
next_url: "http://taler.net",
},
response: {
- tip_id: "id2",
- taler_tip_uri: "uri",
- tip_expiration: { t_s: 1 },
- tip_status_url: "url",
+ reward_id: "id2",
+ taler_reward_uri: "uri",
+ reward_expiration: { t_s: 1 },
+ reward_status_url: "url",
},
});
env.addRequestExpectation(API_GET_RESERVE_BY_ID("11"), {
response: {
accounts: [{ payto_uri: "payto://here" }],
- tips: [
- { reason: "why?", tip_id: "id1", total_amount: "USD:10" },
- { reason: "not", tip_id: "id2", total_amount: "USD:12" },
+ rewards: [
+ { reason: "why?", reward_id: "id1", total_amount: "USD:10" },
+ { reason: "not", reward_id: "id2", total_amount: "USD:12" },
],
- } as MerchantBackend.Tips.ReserveDetail,
+ } as MerchantBackend.Rewards.ReserveDetail,
qparam: {
- tips: "yes",
+ rewards: "yes",
},
});
- api.authorizeTip({
+ api.authorizeReward({
amount: "USD:12",
justification: "not",
next_url: "http://taler.net",
@@ -387,9 +387,9 @@ describe("reserve api interaction with details", () => {
expect(query.data).deep.equals({
accounts: [{ payto_uri: "payto://here" }],
- tips: [
- { reason: "why?", tip_id: "id1", total_amount: "USD:10" },
- { reason: "not", tip_id: "id2", total_amount: "USD:12" },
+ rewards: [
+ { reason: "why?", reward_id: "id1", total_amount: "USD:10" },
+ { reason: "not", reward_id: "id2", total_amount: "USD:12" },
],
});
},
@@ -402,15 +402,15 @@ describe("reserve api interaction with details", () => {
});
});
-describe("reserve api interaction with tip details", () => {
- it("should list tips", async () => {
+describe("reserve api interaction with reward details", () => {
+ it("should list rewards", async () => {
const env = new ApiMockEnvironment();
- env.addRequestExpectation(API_GET_TIP_BY_ID("11"), {
+ env.addRequestExpectation(API_GET_REWARD_BY_ID("11"), {
response: {
total_picked_up: "USD:12",
reason: "not",
- } as MerchantBackend.Tips.TipDetails,
+ } as MerchantBackend.Rewards.RewardDetails,
qparam: {
pickups: "yes",
},
@@ -418,7 +418,7 @@ describe("reserve api interaction with tip details", () => {
const hookBehavior = await tests.hookBehaveLikeThis(
() => {
- const query = useTipDetails("11");
+ const query = useRewardDetails("11");
return { query };
},
{},
diff --git a/packages/merchant-backoffice-ui/src/hooks/reserves.ts b/packages/merchant-backoffice-ui/src/hooks/reserves.ts
index bb55b2474..b719bfbe6 100644
--- a/packages/merchant-backoffice-ui/src/hooks/reserves.ts
+++ b/packages/merchant-backoffice-ui/src/hooks/reserves.ts
@@ -31,11 +31,11 @@ export function useReservesAPI(): ReserveMutateAPI {
const { request } = useBackendInstanceRequest();
const createReserve = async (
- data: MerchantBackend.Tips.ReserveCreateRequest,
+ data: MerchantBackend.Rewards.ReserveCreateRequest,
): Promise<
- HttpResponseOk<MerchantBackend.Tips.ReserveCreateConfirmation>
+ HttpResponseOk<MerchantBackend.Rewards.ReserveCreateConfirmation>
> => {
- const res = await request<MerchantBackend.Tips.ReserveCreateConfirmation>(
+ const res = await request<MerchantBackend.Rewards.ReserveCreateConfirmation>(
`/private/reserves`,
{
method: "POST",
@@ -49,12 +49,12 @@ export function useReservesAPI(): ReserveMutateAPI {
return res;
};
- const authorizeTipReserve = async (
+ const authorizeRewardReserve = async (
pub: string,
- data: MerchantBackend.Tips.TipCreateRequest,
- ): Promise<HttpResponseOk<MerchantBackend.Tips.TipCreateConfirmation>> => {
- const res = await request<MerchantBackend.Tips.TipCreateConfirmation>(
- `/private/reserves/${pub}/authorize-tip`,
+ data: MerchantBackend.Rewards.RewardCreateRequest,
+ ): Promise<HttpResponseOk<MerchantBackend.Rewards.RewardCreateConfirmation>> => {
+ const res = await request<MerchantBackend.Rewards.RewardCreateConfirmation>(
+ `/private/reserves/${pub}/authorize-reward`,
{
method: "POST",
data,
@@ -67,11 +67,11 @@ export function useReservesAPI(): ReserveMutateAPI {
return res;
};
- const authorizeTip = async (
- data: MerchantBackend.Tips.TipCreateRequest,
- ): Promise<HttpResponseOk<MerchantBackend.Tips.TipCreateConfirmation>> => {
- const res = await request<MerchantBackend.Tips.TipCreateConfirmation>(
- `/private/tips`,
+ const authorizeReward = async (
+ data: MerchantBackend.Rewards.RewardCreateRequest,
+ ): Promise<HttpResponseOk<MerchantBackend.Rewards.RewardCreateConfirmation>> => {
+ const res = await request<MerchantBackend.Rewards.RewardCreateConfirmation>(
+ `/private/rewards`,
{
method: "POST",
data,
@@ -97,33 +97,33 @@ export function useReservesAPI(): ReserveMutateAPI {
return res;
};
- return { createReserve, authorizeTip, authorizeTipReserve, deleteReserve };
+ return { createReserve, authorizeReward, authorizeRewardReserve, deleteReserve };
}
export interface ReserveMutateAPI {
createReserve: (
- data: MerchantBackend.Tips.ReserveCreateRequest,
- ) => Promise<HttpResponseOk<MerchantBackend.Tips.ReserveCreateConfirmation>>;
- authorizeTipReserve: (
+ data: MerchantBackend.Rewards.ReserveCreateRequest,
+ ) => Promise<HttpResponseOk<MerchantBackend.Rewards.ReserveCreateConfirmation>>;
+ authorizeRewardReserve: (
id: string,
- data: MerchantBackend.Tips.TipCreateRequest,
- ) => Promise<HttpResponseOk<MerchantBackend.Tips.TipCreateConfirmation>>;
- authorizeTip: (
- data: MerchantBackend.Tips.TipCreateRequest,
- ) => Promise<HttpResponseOk<MerchantBackend.Tips.TipCreateConfirmation>>;
+ data: MerchantBackend.Rewards.RewardCreateRequest,
+ ) => Promise<HttpResponseOk<MerchantBackend.Rewards.RewardCreateConfirmation>>;
+ authorizeReward: (
+ data: MerchantBackend.Rewards.RewardCreateRequest,
+ ) => Promise<HttpResponseOk<MerchantBackend.Rewards.RewardCreateConfirmation>>;
deleteReserve: (
id: string,
) => Promise<HttpResponse<void, MerchantBackend.ErrorDetail>>;
}
export function useInstanceReserves(): HttpResponse<
- MerchantBackend.Tips.TippingReserveStatus,
+ MerchantBackend.Rewards.RewardReserveStatus,
MerchantBackend.ErrorDetail
> {
const { fetcher } = useBackendInstanceRequest();
const { data, error, isValidating } = useSWR<
- HttpResponseOk<MerchantBackend.Tips.TippingReserveStatus>,
+ HttpResponseOk<MerchantBackend.Rewards.RewardReserveStatus>,
RequestError<MerchantBackend.ErrorDetail>
>([`/private/reserves`], fetcher);
@@ -136,13 +136,13 @@ export function useInstanceReserves(): HttpResponse<
export function useReserveDetails(
reserveId: string,
): HttpResponse<
- MerchantBackend.Tips.ReserveDetail,
+ MerchantBackend.Rewards.ReserveDetail,
MerchantBackend.ErrorDetail
> {
const { reserveDetailFetcher } = useBackendInstanceRequest();
const { data, error, isValidating } = useSWR<
- HttpResponseOk<MerchantBackend.Tips.ReserveDetail>,
+ HttpResponseOk<MerchantBackend.Rewards.ReserveDetail>,
RequestError<MerchantBackend.ErrorDetail>
>([`/private/reserves/${reserveId}`], reserveDetailFetcher, {
refreshInterval: 0,
@@ -158,15 +158,15 @@ export function useReserveDetails(
return { loading: true };
}
-export function useTipDetails(
- tipId: string,
-): HttpResponse<MerchantBackend.Tips.TipDetails, MerchantBackend.ErrorDetail> {
- const { tipsDetailFetcher } = useBackendInstanceRequest();
+export function useRewardDetails(
+ rewardId: string,
+): HttpResponse<MerchantBackend.Rewards.RewardDetails, MerchantBackend.ErrorDetail> {
+ const { rewardsDetailFetcher } = useBackendInstanceRequest();
const { data, error, isValidating } = useSWR<
- HttpResponseOk<MerchantBackend.Tips.TipDetails>,
+ HttpResponseOk<MerchantBackend.Rewards.RewardDetails>,
RequestError<MerchantBackend.ErrorDetail>
- >([`/private/tips/${tipId}`], tipsDetailFetcher, {
+ >([`/private/rewards/${rewardId}`], rewardsDetailFetcher, {
refreshInterval: 0,
refreshWhenHidden: false,
revalidateOnFocus: false,
diff --git a/packages/merchant-backoffice-ui/src/hooks/urls.ts b/packages/merchant-backoffice-ui/src/hooks/urls.ts
index 6b339c05a..00c5e95af 100644
--- a/packages/merchant-backoffice-ui/src/hooks/urls.ts
+++ b/packages/merchant-backoffice-ui/src/hooks/urls.ts
@@ -139,15 +139,15 @@ export const API_DELETE_PRODUCT = (id: string): Query<unknown, unknown> => ({
////////////////////
export const API_CREATE_RESERVE: Query<
- MerchantBackend.Tips.ReserveCreateRequest,
- MerchantBackend.Tips.ReserveCreateConfirmation
+ MerchantBackend.Rewards.ReserveCreateRequest,
+ MerchantBackend.Rewards.ReserveCreateConfirmation
> = {
method: "POST",
url: "http://backend/instances/default/private/reserves",
};
export const API_LIST_RESERVES: Query<
unknown,
- MerchantBackend.Tips.TippingReserveStatus
+ MerchantBackend.Rewards.RewardReserveStatus
> = {
method: "GET",
url: "http://backend/instances/default/private/reserves",
@@ -155,34 +155,34 @@ export const API_LIST_RESERVES: Query<
export const API_GET_RESERVE_BY_ID = (
pub: string,
-): Query<unknown, MerchantBackend.Tips.ReserveDetail> => ({
+): Query<unknown, MerchantBackend.Rewards.ReserveDetail> => ({
method: "GET",
url: `http://backend/instances/default/private/reserves/${pub}`,
});
-export const API_GET_TIP_BY_ID = (
+export const API_GET_REWARD_BY_ID = (
pub: string,
-): Query<unknown, MerchantBackend.Tips.TipDetails> => ({
+): Query<unknown, MerchantBackend.Rewards.RewardDetails> => ({
method: "GET",
- url: `http://backend/instances/default/private/tips/${pub}`,
+ url: `http://backend/instances/default/private/rewards/${pub}`,
});
-export const API_AUTHORIZE_TIP_FOR_RESERVE = (
+export const API_AUTHORIZE_REWARD_FOR_RESERVE = (
pub: string,
): Query<
- MerchantBackend.Tips.TipCreateRequest,
- MerchantBackend.Tips.TipCreateConfirmation
+ MerchantBackend.Rewards.RewardCreateRequest,
+ MerchantBackend.Rewards.RewardCreateConfirmation
> => ({
method: "POST",
- url: `http://backend/instances/default/private/reserves/${pub}/authorize-tip`,
+ url: `http://backend/instances/default/private/reserves/${pub}/authorize-reward`,
});
-export const API_AUTHORIZE_TIP: Query<
- MerchantBackend.Tips.TipCreateRequest,
- MerchantBackend.Tips.TipCreateConfirmation
+export const API_AUTHORIZE_REWARD: Query<
+ MerchantBackend.Rewards.RewardCreateRequest,
+ MerchantBackend.Rewards.RewardCreateConfirmation
> = {
method: "POST",
- url: `http://backend/instances/default/private/tips`,
+ url: `http://backend/instances/default/private/rewards`,
};
export const API_DELETE_RESERVE = (id: string): Query<unknown, unknown> => ({
@@ -211,7 +211,7 @@ export const API_GET_INSTANCE_BY_ID = (
export const API_GET_INSTANCE_KYC_BY_ID = (
id: string,
-): Query<unknown, MerchantBackend.Instances.AccountKycRedirects> => ({
+): Query<unknown, MerchantBackend.KYC.AccountKycRedirects> => ({
method: "GET",
url: `http://backend/management/instances/${id}/kyc`,
});
@@ -263,7 +263,7 @@ export const API_GET_CURRENT_INSTANCE: Query<
export const API_GET_CURRENT_INSTANCE_KYC: Query<
unknown,
- MerchantBackend.Instances.AccountKycRedirects
+ MerchantBackend.KYC.AccountKycRedirects
> = {
method: "GET",
url: `http://backend/instances/default/private/kyc`,
diff --git a/packages/merchant-backoffice-ui/src/hooks/useSettings.ts b/packages/merchant-backoffice-ui/src/hooks/useSettings.ts
index 5c0932f27..7dee9f896 100644
--- a/packages/merchant-backoffice-ui/src/hooks/useSettings.ts
+++ b/packages/merchant-backoffice-ui/src/hooks/useSettings.ts
@@ -19,6 +19,9 @@ import {
Codec,
buildCodecForObject,
codecForBoolean,
+ codecForConstString,
+ codecForEither,
+ codecForString,
} from "@gnu-taler/taler-util";
function parse_json_or_undefined<T>(str: string | undefined): T | undefined {
@@ -31,29 +34,49 @@ function parse_json_or_undefined<T>(str: string | undefined): T | undefined {
}
export interface Settings {
- advanceOrderMode: boolean
+ advanceOrderMode: boolean;
+ dateFormat: "ymd" | "dmy" | "mdy";
}
const defaultSettings: Settings = {
advanceOrderMode: false,
+ dateFormat: "ymd",
}
export const codecForSettings = (): Codec<Settings> =>
buildCodecForObject<Settings>()
.property("advanceOrderMode", codecForBoolean())
+ .property("dateFormat", codecForEither(
+ codecForConstString("ymd"),
+ codecForConstString("dmy"),
+ codecForConstString("mdy"),
+ ))
.build("Settings");
const SETTINGS_KEY = buildStorageKey("merchant-settings", codecForSettings());
export function useSettings(): [
Readonly<Settings>,
- <T extends keyof Settings>(key: T, value: Settings[T]) => void,
+ (s: Settings) => void,
] {
- const { value, update } = useLocalStorage(SETTINGS_KEY);
+ const { value, update } = useLocalStorage(SETTINGS_KEY, defaultSettings);
- const parsed: Settings = value ?? defaultSettings;
- function updateField<T extends keyof Settings>(k: T, v: Settings[T]) {
- update({ ...parsed, [k]: v });
+ // const parsed: Settings = value ?? defaultSettings;
+ // function updateField<T extends keyof Settings>(k: T, v: Settings[T]) {
+ // const next = { ...parsed, [k]: v }
+ // update(next);
+ // }
+ return [value, update];
+}
+
+export function dateFormatForSettings(s: Settings): string {
+ switch (s.dateFormat) {
+ case "ymd": return "yyyy/MM/dd"
+ case "dmy": return "dd/MM/yyyy"
+ case "mdy": return "MM/dd/yyyy"
}
- return [parsed, updateField];
}
+
+export function datetimeFormatForSettings(s: Settings): string {
+ return dateFormatForSettings(s) + " HH:mm:ss"
+} \ No newline at end of file
diff --git a/packages/merchant-backoffice-ui/src/paths/admin/create/CreatePage.tsx b/packages/merchant-backoffice-ui/src/paths/admin/create/CreatePage.tsx
index 14e2fcb46..a8108251d 100644
--- a/packages/merchant-backoffice-ui/src/paths/admin/create/CreatePage.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/admin/create/CreatePage.tsx
@@ -19,7 +19,6 @@
* @author Sebastian Javier Marchano (sebasjm)
*/
-import { Amounts } from "@gnu-taler/taler-util";
import { useTranslationContext } from "@gnu-taler/web-util/browser";
import { h, VNode } from "preact";
import { useState } from "preact/hooks";
@@ -29,9 +28,8 @@ import {
FormProvider,
} from "../../../components/form/FormProvider.js";
import { DefaultInstanceFormFields } from "../../../components/instance/DefaultInstanceFormFields.js";
-import { SetTokenNewInstanceModal } from "../../../components/modal/index.js";
import { MerchantBackend } from "../../../declaration.js";
-import { INSTANCE_ID_REGEX, PAYTO_REGEX } from "../../../utils/constants.js";
+import { INSTANCE_ID_REGEX } from "../../../utils/constants.js";
import { undefinedIfEmpty } from "../../../utils/table.js";
export type Entity = MerchantBackend.Instances.InstanceConfigurationMessage & {
@@ -47,19 +45,19 @@ interface Props {
function with_defaults(id?: string): Partial<Entity> {
return {
id,
- accounts: [],
+ // accounts: [],
user_type: "business",
+ use_stefan: false,
default_pay_delay: { d_us: 2 * 1000 * 60 * 60 * 1000 }, // two hours
- default_wire_fee_amortization: 1,
default_wire_transfer_delay: { d_us: 1000 * 2 * 60 * 60 * 24 * 1000 }, // two days
};
}
export function CreatePage({ onCreate, onBack, forceId }: Props): VNode {
const [value, valueHandler] = useState(with_defaults(forceId));
- const [isTokenSet, updateIsTokenSet] = useState<boolean>(false);
- const [isTokenDialogActive, updateIsTokenDialogActive] =
- useState<boolean>(false);
+ // const [isTokenSet, updateIsTokenSet] = useState<boolean>(false);
+ // const [isTokenDialogActive, updateIsTokenDialogActive] =
+ // useState<boolean>(false);
const { i18n } = useTranslationContext();
@@ -67,42 +65,24 @@ export function CreatePage({ onCreate, onBack, forceId }: Props): VNode {
id: !value.id
? i18n.str`required`
: !INSTANCE_ID_REGEX.test(value.id)
- ? i18n.str`is not valid`
- : undefined,
+ ? i18n.str`is not valid`
+ : undefined,
name: !value.name ? i18n.str`required` : undefined,
user_type: !value.user_type
? i18n.str`required`
: value.user_type !== "business" && value.user_type !== "individual"
- ? i18n.str`should be business or individual`
- : undefined,
- accounts:
- !value.accounts || !value.accounts.length
- ? i18n.str`required`
- : undefinedIfEmpty(
- value.accounts.map((p) => {
- return !PAYTO_REGEX.test(p.payto_uri)
- ? i18n.str`is not valid`
- : undefined;
- }),
- ),
- default_max_deposit_fee: !value.default_max_deposit_fee
- ? i18n.str`required`
- : !Amounts.parse(value.default_max_deposit_fee)
- ? i18n.str`invalid format`
- : undefined,
- default_max_wire_fee: !value.default_max_wire_fee
- ? i18n.str`required`
- : !Amounts.parse(value.default_max_wire_fee)
- ? i18n.str`invalid format`
- : undefined,
- default_wire_fee_amortization:
- value.default_wire_fee_amortization === undefined
- ? i18n.str`required`
- : isNaN(value.default_wire_fee_amortization)
- ? i18n.str`is not a number`
- : value.default_wire_fee_amortization < 1
- ? i18n.str`must be 1 or greater`
+ ? i18n.str`should be business or individual`
: undefined,
+ // accounts:
+ // !value.accounts || !value.accounts.length
+ // ? i18n.str`required`
+ // : undefinedIfEmpty(
+ // value.accounts.map((p) => {
+ // return !PAYTO_REGEX.test(p.payto_uri)
+ // ? i18n.str`is not valid`
+ // : undefined;
+ // }),
+ // ),
default_pay_delay: !value.default_pay_delay
? i18n.str`required`
: undefined,
@@ -129,12 +109,12 @@ export function CreatePage({ onCreate, onBack, forceId }: Props): VNode {
const submit = (): Promise<void> => {
// use conversion instead of this
- const newToken = value.auth_token;
- value.auth_token = undefined;
- value.auth =
- newToken === null || newToken === undefined
- ? { method: "external" }
- : { method: "token", token: `secret-token:${newToken}` };
+ // const newToken = value.auth_token;
+ // value.auth_token = undefined;
+ value.auth = { method: "external" }
+ // newToken === null || newToken === undefined
+ // ? { method: "external" }
+ // : { method: "token", token: `secret-token:${newToken}` };
if (!value.address) value.address = {};
if (!value.jurisdiction) value.jurisdiction = {};
// remove above use conversion
@@ -142,16 +122,16 @@ export function CreatePage({ onCreate, onBack, forceId }: Props): VNode {
return onCreate(value as Entity);
};
- function updateToken(token: string | null) {
- valueHandler((old) => ({
- ...old,
- auth_token: token === null ? undefined : token,
- }));
- }
+ // function updateToken(token: string | null) {
+ // valueHandler((old) => ({
+ // ...old,
+ // auth_token: token === null ? undefined : token,
+ // }));
+ // }
return (
<div>
- <div class="columns">
+ {/* <div class="columns">
<div class="column" />
<div class="column is-four-fifths">
{isTokenDialogActive && (
@@ -174,9 +154,9 @@ export function CreatePage({ onCreate, onBack, forceId }: Props): VNode {
)}
</div>
<div class="column" />
- </div>
+ </div> */}
- <section class="hero is-hero-bar">
+ {/* <section class="hero is-hero-bar">
<div class="hero-body">
<div class="level">
<div class="level-item has-text-centered">
@@ -186,8 +166,8 @@ export function CreatePage({ onCreate, onBack, forceId }: Props): VNode {
!isTokenSet
? "button is-danger has-tooltip-bottom"
: !value.auth_token
- ? "button has-tooltip-bottom"
- : "button is-info has-tooltip-bottom"
+ ? "button has-tooltip-bottom"
+ : "button is-info has-tooltip-bottom"
}
data-tooltip={i18n.str`change authorization configuration`}
onClick={() => updateIsTokenDialogActive(true)}
@@ -228,7 +208,7 @@ export function CreatePage({ onCreate, onBack, forceId }: Props): VNode {
</div>
</div>
</div>
- </section>
+ </section> */}
<section class="section is-main-section">
<div class="columns">
@@ -250,7 +230,7 @@ export function CreatePage({ onCreate, onBack, forceId }: Props): VNode {
)}
<AsyncButton
onClick={submit}
- disabled={!isTokenSet || hasErrors}
+ disabled={hasErrors}
data-tooltip={
hasErrors
? i18n.str`Need to complete marked fields and choose authorization method`
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/accounts/create/Create.stories.tsx b/packages/merchant-backoffice-ui/src/paths/instance/accounts/create/Create.stories.tsx
new file mode 100644
index 000000000..3336c53a4
--- /dev/null
+++ b/packages/merchant-backoffice-ui/src/paths/instance/accounts/create/Create.stories.tsx
@@ -0,0 +1,28 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021-2023 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/>
+ */
+
+/**
+ *
+ * @author Sebastian Javier Marchano (sebasjm)
+ */
+
+import { h, VNode, FunctionalComponent } from "preact";
+import { CreatePage as TestedComponent } from "./CreatePage.js";
+
+export default {
+ title: "Pages/Accounts/Create",
+ component: TestedComponent,
+};
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/accounts/create/CreatePage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/accounts/create/CreatePage.tsx
new file mode 100644
index 000000000..6e4786a47
--- /dev/null
+++ b/packages/merchant-backoffice-ui/src/paths/instance/accounts/create/CreatePage.tsx
@@ -0,0 +1,173 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021-2023 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/>
+ */
+
+/**
+ *
+ * @author Sebastian Javier Marchano (sebasjm)
+ */
+
+import { useTranslationContext } from "@gnu-taler/web-util/browser";
+import { Fragment, h, VNode } from "preact";
+import { useState } from "preact/hooks";
+import { AsyncButton } from "../../../../components/exception/AsyncButton.js";
+import {
+ FormErrors,
+ FormProvider,
+} from "../../../../components/form/FormProvider.js";
+import { Input } from "../../../../components/form/Input.js";
+import { InputPaytoForm } from "../../../../components/form/InputPaytoForm.js";
+import { InputSelector } from "../../../../components/form/InputSelector.js";
+import { MerchantBackend } from "../../../../declaration.js";
+import { undefinedIfEmpty } from "../../../../utils/table.js";
+
+type Entity = MerchantBackend.BankAccounts.AccountAddDetails & { repeatPassword: string };
+
+interface Props {
+ onCreate: (d: Entity) => Promise<void>;
+ onBack?: () => void;
+}
+
+const accountAuthType = ["none", "basic"];
+
+function isValidURL(s: string): boolean {
+ try {
+ const u = new URL(s)
+ return true;
+ } catch (e) {
+ return false;
+ }
+}
+
+export function CreatePage({ onCreate, onBack }: Props): VNode {
+ const { i18n } = useTranslationContext();
+
+ const [state, setState] = useState<Partial<Entity>>({});
+ const errors: FormErrors<Entity> = {
+ payto_uri: !state.payto_uri ? i18n.str`required` : undefined,
+
+ credit_facade_credentials: !state.credit_facade_credentials
+ ? undefined
+ : undefinedIfEmpty({
+ username:
+ state.credit_facade_credentials.type === "basic" && !state.credit_facade_credentials.username
+ ? i18n.str`required`
+ : undefined,
+ password:
+ state.credit_facade_credentials.type === "basic" && !state.credit_facade_credentials.password
+ ? i18n.str`required`
+ : undefined,
+ }),
+ credit_facade_url: !state.credit_facade_url
+ ? undefined
+ : !isValidURL(state.credit_facade_url) ? i18n.str`not valid url`
+ : undefined,
+ repeatPassword:
+ !state.credit_facade_credentials
+ ? undefined
+ : state.credit_facade_credentials.type === "basic" && (!state.credit_facade_credentials.password || state.credit_facade_credentials.password !== state.repeatPassword)
+ ? i18n.str`is not the same`
+ : undefined,
+ };
+
+ const hasErrors = Object.keys(errors).some(
+ (k) => (errors as any)[k] !== undefined,
+ );
+
+ const submitForm = () => {
+ if (hasErrors) return Promise.reject();
+ delete state.repeatPassword
+ return onCreate(state as any);
+ };
+
+ return (
+ <div>
+ <section class="section is-main-section">
+ <div class="columns">
+ <div class="column" />
+ <div class="column is-four-fifths">
+ <FormProvider
+ object={state}
+ valueHandler={setState}
+ errors={errors}
+ >
+ <InputPaytoForm<Entity>
+ name="payto_uri"
+ label={i18n.str`Account`}
+ />
+ <Input<Entity>
+ name="credit_facade_url"
+ label={i18n.str`Account info URL`}
+ help="https://bank.com"
+ expand
+ tooltip={i18n.str`From where the merchant can download information about incoming wire transfers to this account`}
+ />
+ <InputSelector
+ name="credit_facade_credentials.type"
+ label={i18n.str`Auth type`}
+ tooltip={i18n.str`Choose the authentication type for the account info URL`}
+ values={accountAuthType}
+ toStr={(str) => {
+ if (str === "none") return "Without authentication";
+ return "Username and password";
+ }}
+ />
+ {state.credit_facade_credentials?.type === "basic" ? (
+ <Fragment>
+ <Input
+ name="credit_facade_credentials.username"
+ label={i18n.str`Username`}
+ tooltip={i18n.str`Username to access the account information.`}
+ />
+ <Input
+ name="credit_facade_credentials.password"
+ inputType="password"
+ label={i18n.str`Password`}
+ tooltip={i18n.str`Password to access the account information.`}
+ />
+ <Input
+ name="repeatPassword"
+ inputType="password"
+ label={i18n.str`Repeat password`}
+ />
+ </Fragment>
+ ) : undefined}
+ </FormProvider>
+
+ <div class="buttons is-right mt-5">
+ {onBack && (
+ <button class="button" onClick={onBack}>
+ <i18n.Translate>Cancel</i18n.Translate>
+ </button>
+ )}
+ <AsyncButton
+ disabled={hasErrors}
+ data-tooltip={
+ hasErrors
+ ? i18n.str`Need to complete marked fields`
+ : "confirm operation"
+ }
+ onClick={submitForm}
+ >
+ <i18n.Translate>Confirm</i18n.Translate>
+ </AsyncButton>
+ </div>
+ </div>
+ <div class="column" />
+ </div>
+ </section>
+ </div>
+ );
+}
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/accounts/create/index.tsx b/packages/merchant-backoffice-ui/src/paths/instance/accounts/create/index.tsx
new file mode 100644
index 000000000..7d33d25ce
--- /dev/null
+++ b/packages/merchant-backoffice-ui/src/paths/instance/accounts/create/index.tsx
@@ -0,0 +1,65 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021-2023 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/>
+ */
+
+/**
+ *
+ * @author Sebastian Javier Marchano (sebasjm)
+ */
+
+import { useTranslationContext } from "@gnu-taler/web-util/browser";
+import { Fragment, h, VNode } from "preact";
+import { useState } from "preact/hooks";
+import { NotificationCard } from "../../../../components/menu/index.js";
+import { MerchantBackend } from "../../../../declaration.js";
+import { useWebhookAPI } from "../../../../hooks/webhooks.js";
+import { Notification } from "../../../../utils/types.js";
+import { CreatePage } from "./CreatePage.js";
+import { useOtpDeviceAPI } from "../../../../hooks/otp.js";
+import { useBankAccountAPI } from "../../../../hooks/bank.js";
+
+export type Entity = MerchantBackend.BankAccounts.AccountAddDetails;
+interface Props {
+ onBack?: () => void;
+ onConfirm: () => void;
+}
+
+export default function CreateValidator({ onConfirm, onBack }: Props): VNode {
+ const { createBankAccount } = useBankAccountAPI();
+ const [notif, setNotif] = useState<Notification | undefined>(undefined);
+ const { i18n } = useTranslationContext();
+
+ return (
+ <>
+ <NotificationCard notification={notif} />
+ <CreatePage
+ onBack={onBack}
+ onCreate={(request: Entity) => {
+ return createBankAccount(request)
+ .then((d) => {
+ onConfirm()
+ })
+ .catch((error) => {
+ setNotif({
+ message: i18n.str`could not create device`,
+ type: "ERROR",
+ description: error.message,
+ });
+ });
+ }}
+ />
+ </>
+ );
+}
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/accounts/list/List.stories.tsx b/packages/merchant-backoffice-ui/src/paths/instance/accounts/list/List.stories.tsx
new file mode 100644
index 000000000..6b4b63735
--- /dev/null
+++ b/packages/merchant-backoffice-ui/src/paths/instance/accounts/list/List.stories.tsx
@@ -0,0 +1,28 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021-2023 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/>
+ */
+
+/**
+ *
+ * @author Sebastian Javier Marchano (sebasjm)
+ */
+
+import { FunctionalComponent, h } from "preact";
+import { ListPage as TestedComponent } from "./ListPage.js";
+
+export default {
+ title: "Pages/Accounts/List",
+ component: TestedComponent,
+};
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/accounts/list/ListPage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/accounts/list/ListPage.tsx
new file mode 100644
index 000000000..24da755b9
--- /dev/null
+++ b/packages/merchant-backoffice-ui/src/paths/instance/accounts/list/ListPage.tsx
@@ -0,0 +1,64 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021-2023 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/>
+ */
+
+/**
+ *
+ * @author Sebastian Javier Marchano (sebasjm)
+ */
+
+import { h, VNode } from "preact";
+import { MerchantBackend } from "../../../../declaration.js";
+import { useTranslationContext } from "@gnu-taler/web-util/browser";
+import { CardTable } from "./Table.js";
+
+export interface Props {
+ devices: MerchantBackend.BankAccounts.BankAccountEntry[];
+ onLoadMoreBefore?: () => void;
+ onLoadMoreAfter?: () => void;
+ onCreate: () => void;
+ onDelete: (e: MerchantBackend.BankAccounts.BankAccountEntry) => void;
+ onSelect: (e: MerchantBackend.BankAccounts.BankAccountEntry) => void;
+}
+
+export function ListPage({
+ devices,
+ onCreate,
+ onDelete,
+ onSelect,
+ onLoadMoreBefore,
+ onLoadMoreAfter,
+}: Props): VNode {
+ const form = { payto_uri: "" };
+
+ const { i18n } = useTranslationContext();
+ return (
+ <section class="section is-main-section">
+ <CardTable
+ accounts={devices.map((o) => ({
+ ...o,
+ id: String(o.h_wire),
+ }))}
+ onCreate={onCreate}
+ onDelete={onDelete}
+ onSelect={onSelect}
+ onLoadMoreBefore={onLoadMoreBefore}
+ hasMoreBefore={!onLoadMoreBefore}
+ onLoadMoreAfter={onLoadMoreAfter}
+ hasMoreAfter={!onLoadMoreAfter}
+ />
+ </section>
+ );
+}
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/accounts/list/Table.tsx b/packages/merchant-backoffice-ui/src/paths/instance/accounts/list/Table.tsx
new file mode 100644
index 000000000..7d6db0782
--- /dev/null
+++ b/packages/merchant-backoffice-ui/src/paths/instance/accounts/list/Table.tsx
@@ -0,0 +1,385 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021-2023 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/>
+ */
+
+/**
+ *
+ * @author Sebastian Javier Marchano (sebasjm)
+ */
+
+import { useTranslationContext } from "@gnu-taler/web-util/browser";
+import { Fragment, h, VNode } from "preact";
+import { StateUpdater, useState } from "preact/hooks";
+import { MerchantBackend } from "../../../../declaration.js";
+import { parsePaytoUri, PaytoType, PaytoUri, PaytoUriBitcoin, PaytoUriIBAN, PaytoUriTalerBank, PaytoUriUnknown } from "@gnu-taler/taler-util";
+
+type Entity = MerchantBackend.BankAccounts.BankAccountEntry;
+
+interface Props {
+ accounts: Entity[];
+ onDelete: (e: Entity) => void;
+ onSelect: (e: Entity) => void;
+ onCreate: () => void;
+ onLoadMoreBefore?: () => void;
+ hasMoreBefore?: boolean;
+ hasMoreAfter?: boolean;
+ onLoadMoreAfter?: () => void;
+}
+
+export function CardTable({
+ accounts,
+ onCreate,
+ onDelete,
+ onSelect,
+ onLoadMoreAfter,
+ onLoadMoreBefore,
+ hasMoreAfter,
+ hasMoreBefore,
+}: Props): VNode {
+ const [rowSelection, rowSelectionHandler] = useState<string[]>([]);
+
+ const { i18n } = useTranslationContext();
+
+ return (
+ <div class="card has-table">
+ <header class="card-header">
+ <p class="card-header-title">
+ <span class="icon">
+ <i class="mdi mdi-newspaper" />
+ </span>
+ <i18n.Translate>Bank accounts</i18n.Translate>
+ </p>
+ <div class="card-header-icon" aria-label="more options">
+ <span
+ class="has-tooltip-left"
+ data-tooltip={i18n.str`add new accounts`}
+ >
+ <button class="button is-info" type="button" onClick={onCreate}>
+ <span class="icon is-small">
+ <i class="mdi mdi-plus mdi-36px" />
+ </span>
+ </button>
+ </span>
+ </div>
+ </header>
+ <div class="card-content">
+ <div class="b-table has-pagination">
+ <div class="table-wrapper has-mobile-cards">
+ {accounts.length > 0 ? (
+ <Table
+ accounts={accounts}
+ onDelete={onDelete}
+ onSelect={onSelect}
+ rowSelection={rowSelection}
+ rowSelectionHandler={rowSelectionHandler}
+ onLoadMoreAfter={onLoadMoreAfter}
+ onLoadMoreBefore={onLoadMoreBefore}
+ hasMoreAfter={hasMoreAfter}
+ hasMoreBefore={hasMoreBefore}
+ />
+ ) : (
+ <EmptyTable />
+ )}
+ </div>
+ </div>
+ </div>
+ </div>
+ );
+}
+interface TableProps {
+ rowSelection: string[];
+ accounts: Entity[];
+ onDelete: (e: Entity) => void;
+ onSelect: (e: Entity) => void;
+ rowSelectionHandler: StateUpdater<string[]>;
+ onLoadMoreBefore?: () => void;
+ hasMoreBefore?: boolean;
+ hasMoreAfter?: boolean;
+ onLoadMoreAfter?: () => void;
+}
+
+function toggleSelected<T>(id: T): (prev: T[]) => T[] {
+ return (prev: T[]): T[] =>
+ prev.indexOf(id) == -1 ? [...prev, id] : prev.filter((e) => e != id);
+}
+
+function Table({
+ accounts,
+ onLoadMoreAfter,
+ onDelete,
+ onSelect,
+ onLoadMoreBefore,
+ hasMoreAfter,
+ hasMoreBefore,
+}: TableProps): VNode {
+ const { i18n } = useTranslationContext();
+ const emptyList: Record<PaytoType | "unknown", { parsed: PaytoUri, acc: Entity }[]> = { "bitcoin": [], "x-taler-bank": [], "iban": [], "unknown": [], }
+ const accountsByType = accounts.reduce((prev, acc) => {
+ const parsed = parsePaytoUri(acc.payto_uri)
+ if (!parsed) return prev //skip
+ if (parsed.targetType !== "bitcoin" && parsed.targetType !== "x-taler-bank" && parsed.targetType !== "iban") {
+ prev["unknown"].push({ parsed, acc })
+ } else {
+ prev[parsed.targetType].push({ parsed, acc })
+ }
+ return prev
+ }, emptyList)
+
+ const bitcoinAccounts = accountsByType["bitcoin"]
+ const talerbankAccounts = accountsByType["x-taler-bank"]
+ const ibanAccounts = accountsByType["iban"]
+ const unkownAccounts = accountsByType["unknown"]
+
+
+ return (
+ <Fragment>
+
+ {bitcoinAccounts.length > 0 && <div class="table-container">
+ <p class="card-header-title"><i18n.Translate>Bitcoin type accounts</i18n.Translate></p>
+ <table class="table is-fullwidth is-striped is-hoverable is-fullwidth">
+ <thead>
+ <tr>
+ <th>
+ <i18n.Translate>Address</i18n.Translate>
+ </th>
+ <th>
+ <i18n.Translate>Sewgit 1</i18n.Translate>
+ </th>
+ <th>
+ <i18n.Translate>Sewgit 2</i18n.Translate>
+ </th>
+ <th />
+ </tr>
+ </thead>
+ <tbody>
+ {bitcoinAccounts.map(({ parsed, acc }, idx) => {
+ const ac = parsed as PaytoUriBitcoin
+ return (
+ <tr key={idx}>
+ <td
+ onClick={(): void => onSelect(acc)}
+ style={{ cursor: "pointer" }}
+ >
+ {ac.targetPath}
+ </td>
+ <td
+ onClick={(): void => onSelect(acc)}
+ style={{ cursor: "pointer" }}
+ >
+ {ac.segwitAddrs[0]}
+ </td>
+ <td
+ onClick={(): void => onSelect(acc)}
+ style={{ cursor: "pointer" }}
+ >
+ {ac.segwitAddrs[1]}
+ </td>
+ <td class="is-actions-cell right-sticky">
+ <div class="buttons is-right">
+ <button
+ class="button is-danger is-small has-tooltip-left"
+ data-tooltip={i18n.str`delete selected accounts from the database`}
+ onClick={() => onDelete(acc)}
+ >
+ Delete
+ </button>
+ </div>
+ </td>
+ </tr>
+ );
+ })}
+ </tbody>
+ </table>
+ </div>}
+
+
+
+ {talerbankAccounts.length > 0 && <div class="table-container">
+ <p class="card-header-title"><i18n.Translate>Taler type accounts</i18n.Translate></p>
+ <table class="table is-fullwidth is-striped is-hoverable is-fullwidth">
+ <thead>
+ <tr>
+ <th>
+ <i18n.Translate>Host</i18n.Translate>
+ </th>
+ <th>
+ <i18n.Translate>Account name</i18n.Translate>
+ </th>
+ <th />
+ </tr>
+ </thead>
+ <tbody>
+ {talerbankAccounts.map(({ parsed, acc }, idx) => {
+ const ac = parsed as PaytoUriTalerBank
+ return (
+ <tr key={idx}>
+ <td
+ onClick={(): void => onSelect(acc)}
+ style={{ cursor: "pointer" }}
+ >
+ {ac.host}
+ </td>
+ <td
+ onClick={(): void => onSelect(acc)}
+ style={{ cursor: "pointer" }}
+ >
+ {ac.account}
+ </td>
+ <td class="is-actions-cell right-sticky">
+ <div class="buttons is-right">
+ <button
+ class="button is-danger is-small has-tooltip-left"
+ data-tooltip={i18n.str`delete selected accounts from the database`}
+ onClick={() => onDelete(acc)}
+ >
+ Delete
+ </button>
+ </div>
+ </td>
+ </tr>
+ );
+ })}
+ </tbody>
+ </table>
+ </div>}
+
+ {ibanAccounts.length > 0 && <div class="table-container">
+ <p class="card-header-title"><i18n.Translate>IBAN type accounts</i18n.Translate></p>
+ <table class="table is-fullwidth is-striped is-hoverable is-fullwidth">
+ <thead>
+ <tr>
+ <th>
+ <i18n.Translate>Account name</i18n.Translate>
+ </th>
+ <th>
+ <i18n.Translate>IBAN</i18n.Translate>
+ </th>
+ <th>
+ <i18n.Translate>BIC</i18n.Translate>
+ </th>
+ <th />
+ </tr>
+ </thead>
+ <tbody>
+ {ibanAccounts.map(({ parsed, acc }, idx) => {
+ const ac = parsed as PaytoUriIBAN
+ return (
+ <tr key={idx}>
+ <td
+ onClick={(): void => onSelect(acc)}
+ style={{ cursor: "pointer" }}
+ >
+ {ac.params["receiver-name"]}
+ </td>
+ <td
+ onClick={(): void => onSelect(acc)}
+ style={{ cursor: "pointer" }}
+ >
+ {ac.iban}
+ </td>
+ <td
+ onClick={(): void => onSelect(acc)}
+ style={{ cursor: "pointer" }}
+ >
+ {ac.bic ?? ""}
+ </td>
+ <td class="is-actions-cell right-sticky">
+ <div class="buttons is-right">
+ <button
+ class="button is-danger is-small has-tooltip-left"
+ data-tooltip={i18n.str`delete selected accounts from the database`}
+ onClick={() => onDelete(acc)}
+ >
+ Delete
+ </button>
+ </div>
+ </td>
+ </tr>
+ );
+ })}
+ </tbody>
+ </table>
+ </div>}
+
+ {unkownAccounts.length > 0 && <div class="table-container">
+ <p class="card-header-title"><i18n.Translate>Other type accounts</i18n.Translate></p>
+ <table class="table is-fullwidth is-striped is-hoverable is-fullwidth">
+ <thead>
+ <tr>
+ <th>
+ <i18n.Translate>Type</i18n.Translate>
+ </th>
+ <th>
+ <i18n.Translate>Path</i18n.Translate>
+ </th>
+ <th />
+ </tr>
+ </thead>
+ <tbody>
+ {unkownAccounts.map(({ parsed, acc }, idx) => {
+ const ac = parsed as PaytoUriUnknown
+ return (
+ <tr key={idx}>
+ <td
+ onClick={(): void => onSelect(acc)}
+ style={{ cursor: "pointer" }}
+ >
+ {ac.targetType}
+ </td>
+ <td
+ onClick={(): void => onSelect(acc)}
+ style={{ cursor: "pointer" }}
+ >
+ {ac.targetPath}
+ </td>
+ <td class="is-actions-cell right-sticky">
+ <div class="buttons is-right">
+ <button
+ class="button is-danger is-small has-tooltip-left"
+ data-tooltip={i18n.str`delete selected accounts from the database`}
+ onClick={() => onDelete(acc)}
+ >
+ Delete
+ </button>
+ </div>
+ </td>
+ </tr>
+ );
+ })}
+ </tbody>
+ </table>
+ </div>}
+ </Fragment>
+
+ );
+}
+
+function EmptyTable(): VNode {
+ const { i18n } = useTranslationContext();
+ return (
+ <div class="content has-text-grey has-text-centered">
+ <p>
+ <span class="icon is-large">
+ <i class="mdi mdi-emoticon-sad mdi-48px" />
+ </span>
+ </p>
+ <p>
+ <i18n.Translate>
+ There is no accounts yet, add more pressing the + sign
+ </i18n.Translate>
+ </p>
+ </div>
+ );
+}
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/accounts/list/index.tsx b/packages/merchant-backoffice-ui/src/paths/instance/accounts/list/index.tsx
new file mode 100644
index 000000000..9788ce0ec
--- /dev/null
+++ b/packages/merchant-backoffice-ui/src/paths/instance/accounts/list/index.tsx
@@ -0,0 +1,107 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021-2023 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/>
+ */
+
+/**
+ *
+ * @author Sebastian Javier Marchano (sebasjm)
+ */
+
+import { HttpStatusCode } from "@gnu-taler/taler-util";
+import {
+ ErrorType,
+ HttpError,
+ useTranslationContext,
+} from "@gnu-taler/web-util/browser";
+import { Fragment, VNode, h } from "preact";
+import { useState } from "preact/hooks";
+import { Loading } from "../../../../components/exception/loading.js";
+import { NotificationCard } from "../../../../components/menu/index.js";
+import { MerchantBackend } from "../../../../declaration.js";
+import { useInstanceOtpDevices, useOtpDeviceAPI } from "../../../../hooks/otp.js";
+import { Notification } from "../../../../utils/types.js";
+import { ListPage } from "./ListPage.js";
+import { useBankAccountAPI, useInstanceBankAccounts } from "../../../../hooks/bank.js";
+
+interface Props {
+ onUnauthorized: () => VNode;
+ onLoadError: (error: HttpError<MerchantBackend.ErrorDetail>) => VNode;
+ onNotFound: () => VNode;
+ onCreate: () => void;
+ onSelect: (id: string) => void;
+}
+
+export default function ListValidators({
+ onUnauthorized,
+ onLoadError,
+ onCreate,
+ onSelect,
+ onNotFound,
+}: Props): VNode {
+ const [position, setPosition] = useState<string | undefined>(undefined);
+ const { i18n } = useTranslationContext();
+ const [notif, setNotif] = useState<Notification | undefined>(undefined);
+ const { deleteBankAccount } = useBankAccountAPI();
+ const result = useInstanceBankAccounts({ position }, (id) => setPosition(id));
+
+ if (result.loading) return <Loading />;
+ if (!result.ok) {
+ if (
+ result.type === ErrorType.CLIENT &&
+ result.status === HttpStatusCode.Unauthorized
+ )
+ return onUnauthorized();
+ if (
+ result.type === ErrorType.CLIENT &&
+ result.status === HttpStatusCode.NotFound
+ )
+ return onNotFound();
+ return onLoadError(result);
+ }
+
+ return (
+ <Fragment>
+ <NotificationCard notification={notif} />
+
+ <ListPage
+ devices={result.data.accounts}
+ onLoadMoreBefore={
+ result.isReachingStart ? result.loadMorePrev : undefined
+ }
+ onLoadMoreAfter={result.isReachingEnd ? result.loadMore : undefined}
+ onCreate={onCreate}
+ onSelect={(e) => {
+ onSelect(e.h_wire);
+ }}
+ onDelete={(e: MerchantBackend.BankAccounts.BankAccountEntry) =>
+ deleteBankAccount(e.h_wire)
+ .then(() =>
+ setNotif({
+ message: i18n.str`bank account delete successfully`,
+ type: "SUCCESS",
+ }),
+ )
+ .catch((error) =>
+ setNotif({
+ message: i18n.str`could not delete the bank account`,
+ type: "ERROR",
+ description: error.message,
+ }),
+ )
+ }
+ />
+ </Fragment>
+ );
+}
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/accounts/update/Update.stories.tsx b/packages/merchant-backoffice-ui/src/paths/instance/accounts/update/Update.stories.tsx
new file mode 100644
index 000000000..fcb77b820
--- /dev/null
+++ b/packages/merchant-backoffice-ui/src/paths/instance/accounts/update/Update.stories.tsx
@@ -0,0 +1,32 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021-2023 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/>
+ */
+
+/**
+ *
+ * @author Sebastian Javier Marchano (sebasjm)
+ */
+
+import { h, VNode, FunctionalComponent } from "preact";
+import { UpdatePage as TestedComponent } from "./UpdatePage.js";
+
+export default {
+ title: "Pages/Validators/Update",
+ component: TestedComponent,
+ argTypes: {
+ onUpdate: { action: "onUpdate" },
+ onBack: { action: "onBack" },
+ },
+};
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/accounts/update/UpdatePage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/accounts/update/UpdatePage.tsx
new file mode 100644
index 000000000..802f593cf
--- /dev/null
+++ b/packages/merchant-backoffice-ui/src/paths/instance/accounts/update/UpdatePage.tsx
@@ -0,0 +1,114 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021-2023 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/>
+ */
+
+/**
+ *
+ * @author Sebastian Javier Marchano (sebasjm)
+ */
+
+import { useTranslationContext } from "@gnu-taler/web-util/browser";
+import { h, VNode } from "preact";
+import { useState } from "preact/hooks";
+import { AsyncButton } from "../../../../components/exception/AsyncButton.js";
+import {
+ FormErrors,
+ FormProvider,
+} from "../../../../components/form/FormProvider.js";
+import { Input } from "../../../../components/form/Input.js";
+import { MerchantBackend, WithId } from "../../../../declaration.js";
+
+type Entity = MerchantBackend.BankAccounts.AccountPatchDetails & WithId;
+
+interface Props {
+ onUpdate: (d: Entity) => Promise<void>;
+ onBack?: () => void;
+ account: Entity;
+}
+export function UpdatePage({ account, onUpdate, onBack }: Props): VNode {
+ const { i18n } = useTranslationContext();
+
+ const [state, setState] = useState<Partial<Entity>>(account);
+
+ const errors: FormErrors<Entity> = {
+ };
+
+ const hasErrors = Object.keys(errors).some(
+ (k) => (errors as any)[k] !== undefined,
+ );
+
+ const submitForm = () => {
+ if (hasErrors) return Promise.reject();
+ return onUpdate(state as any);
+ };
+
+ return (
+ <div>
+ <section class="section">
+ <section class="hero is-hero-bar">
+ <div class="hero-body">
+ <div class="level">
+ <div class="level-left">
+ <div class="level-item">
+ <span class="is-size-4">
+ Account: <b>{account.id}</b>
+ </span>
+ </div>
+ </div>
+ </div>
+ </div>
+ </section>
+ <hr />
+
+ <section class="section is-main-section">
+ <div class="columns">
+ <div class="column is-four-fifths">
+ <FormProvider
+ object={state}
+ valueHandler={setState}
+ errors={errors}
+ >
+ <Input<Entity>
+ name="credit_facade_url"
+ label={i18n.str`Description`}
+ tooltip={i18n.str`dddd`}
+ />
+ </FormProvider>
+
+ <div class="buttons is-right mt-5">
+ {onBack && (
+ <button class="button" onClick={onBack}>
+ <i18n.Translate>Cancel</i18n.Translate>
+ </button>
+ )}
+ <AsyncButton
+ disabled={hasErrors}
+ data-tooltip={
+ hasErrors
+ ? i18n.str`Need to complete marked fields`
+ : "confirm operation"
+ }
+ onClick={submitForm}
+ >
+ <i18n.Translate>Confirm</i18n.Translate>
+ </AsyncButton>
+ </div>
+ </div>
+ </div>
+ </section>
+ </section>
+ </div>
+ );
+}
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/accounts/update/index.tsx b/packages/merchant-backoffice-ui/src/paths/instance/accounts/update/index.tsx
new file mode 100644
index 000000000..44dee7651
--- /dev/null
+++ b/packages/merchant-backoffice-ui/src/paths/instance/accounts/update/index.tsx
@@ -0,0 +1,96 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021-2023 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/>
+ */
+
+/**
+ *
+ * @author Sebastian Javier Marchano (sebasjm)
+ */
+
+import { HttpStatusCode } from "@gnu-taler/taler-util";
+import {
+ ErrorType,
+ HttpError,
+ useTranslationContext,
+} from "@gnu-taler/web-util/browser";
+import { Fragment, VNode, h } from "preact";
+import { useState } from "preact/hooks";
+import { Loading } from "../../../../components/exception/loading.js";
+import { NotificationCard } from "../../../../components/menu/index.js";
+import { MerchantBackend, WithId } from "../../../../declaration.js";
+import { useBankAccountAPI, useBankAccountDetails } from "../../../../hooks/bank.js";
+import { Notification } from "../../../../utils/types.js";
+import { UpdatePage } from "./UpdatePage.js";
+
+export type Entity = MerchantBackend.BankAccounts.AccountPatchDetails & WithId;
+
+interface Props {
+ onBack?: () => void;
+ onConfirm: () => void;
+ onUnauthorized: () => VNode;
+ onNotFound: () => VNode;
+ onLoadError: (e: HttpError<MerchantBackend.ErrorDetail>) => VNode;
+ bid: string;
+}
+export default function UpdateValidator({
+ bid,
+ onConfirm,
+ onBack,
+ onUnauthorized,
+ onNotFound,
+ onLoadError,
+}: Props): VNode {
+ const { updateBankAccount } = useBankAccountAPI();
+ const result = useBankAccountDetails(bid);
+ const [notif, setNotif] = useState<Notification | undefined>(undefined);
+
+ const { i18n } = useTranslationContext();
+
+ if (result.loading) return <Loading />;
+ if (!result.ok) {
+ if (
+ result.type === ErrorType.CLIENT &&
+ result.status === HttpStatusCode.Unauthorized
+ )
+ return onUnauthorized();
+ if (
+ result.type === ErrorType.CLIENT &&
+ result.status === HttpStatusCode.NotFound
+ )
+ return onNotFound();
+ return onLoadError(result);
+ }
+
+ return (
+ <Fragment>
+ <NotificationCard notification={notif} />
+ <UpdatePage
+ account={{ ...result.data, id: bid }}
+ onBack={onBack}
+ onUpdate={(data) => {
+ return updateBankAccount(bid, data)
+ .then(onConfirm)
+ .catch((error) => {
+ setNotif({
+ message: i18n.str`could not update account`,
+ type: "ERROR",
+ description: error.message,
+ });
+ });
+ }}
+ />
+ </Fragment>
+ );
+}
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/details/DetailPage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/details/DetailPage.tsx
index e5937ab7b..21dadb1e3 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/details/DetailPage.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/details/DetailPage.tsx
@@ -36,14 +36,13 @@ interface Props {
function convert(
from: MerchantBackend.Instances.QueryInstancesResponse,
): Entity {
- const { accounts: allAccounts, ...rest } = from;
- const accounts = allAccounts.filter((a) => a.active);
const defaults = {
default_wire_fee_amortization: 1,
+ use_stefan: true,
default_pay_delay: { d_us: 1000 * 60 * 60 * 1000 }, //one hour
default_wire_transfer_delay: { d_us: 1000 * 60 * 60 * 2 * 1000 }, //two hours
};
- return { ...defaults, ...rest, accounts };
+ return { ...defaults, ...from };
}
export function DetailPage({ selected }: Props): VNode {
@@ -74,11 +73,6 @@ export function DetailPage({ selected }: Props): VNode {
<div class="column is-6">
<FormProvider<Entity> object={value} valueHandler={valueHandler}>
<Input<Entity> name="name" readonly label={i18n.str`Name`} />
- <Input<Entity>
- name="accounts"
- readonly
- label={i18n.str`Account address`}
- />
</FormProvider>
</div>
<div class="column" />
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/details/stories.tsx b/packages/merchant-backoffice-ui/src/paths/instance/details/stories.tsx
index 3a6e0fbfe..367fabce2 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/details/stories.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/details/stories.tsx
@@ -51,17 +51,15 @@ function createExample<Props>(
export const Example = createExample(TestedComponent, {
selected: {
- accounts: [],
name: "name",
auth: { method: "external" },
address: {},
+ user_type: "business",
jurisdiction: {},
- default_max_deposit_fee: "TESTKUDOS:2",
- default_max_wire_fee: "TESTKUDOS:1",
+ use_stefan: true,
default_pay_delay: {
d_us: 1000 * 1000, //one second
},
- default_wire_fee_amortization: 1,
default_wire_transfer_delay: {
d_us: 1000 * 1000, //one second
},
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/kyc/list/ListPage.stories.tsx b/packages/merchant-backoffice-ui/src/paths/instance/kyc/list/ListPage.stories.tsx
index 6f50ac830..d33f64ada 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/kyc/list/ListPage.stories.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/kyc/list/ListPage.stories.tsx
@@ -54,5 +54,5 @@ export const Example = tests.createExample(TestedComponent, {
payto_uri: "payto://iban/de123123123",
},
],
- } as MerchantBackend.Instances.AccountKycRedirects,
+ } as MerchantBackend.KYC.AccountKycRedirects,
});
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/kyc/list/ListPage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/kyc/list/ListPage.tsx
index 67005d3cc..338081886 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/kyc/list/ListPage.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/kyc/list/ListPage.tsx
@@ -24,7 +24,7 @@ import { h, VNode } from "preact";
import { MerchantBackend } from "../../../../declaration.js";
export interface Props {
- status: MerchantBackend.Instances.AccountKycRedirects;
+ status: MerchantBackend.KYC.AccountKycRedirects;
}
export function ListPage({ status }: Props): VNode {
@@ -85,11 +85,11 @@ export function ListPage({ status }: Props): VNode {
);
}
interface PendingTableProps {
- entries: MerchantBackend.Instances.MerchantAccountKycRedirect[];
+ entries: MerchantBackend.KYC.MerchantAccountKycRedirect[];
}
interface TimedOutTableProps {
- entries: MerchantBackend.Instances.ExchangeKycTimeout[];
+ entries: MerchantBackend.KYC.ExchangeKycTimeout[];
}
function PendingTable({ entries }: PendingTableProps): VNode {
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/orders/create/Create.stories.tsx b/packages/merchant-backoffice-ui/src/paths/instance/orders/create/Create.stories.tsx
index fcf611c3c..bd9f65718 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/orders/create/Create.stories.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/orders/create/Create.stories.tsx
@@ -42,12 +42,13 @@ function createExample<Props>(
export const Example = createExample(TestedComponent, {
instanceConfig: {
- default_max_deposit_fee: "",
- default_max_wire_fee: "",
default_pay_delay: {
d_us: 1000 * 1000 * 60 * 60, //one hour
},
- default_wire_fee_amortization: 1,
+ default_wire_transfer_delay: {
+ d_us: 1000 * 1000 * 60 * 60, //one hour
+ },
+ use_stefan: true,
},
instanceInventory: [
{
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/orders/create/CreatePage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/orders/create/CreatePage.tsx
index fa9347c6e..ea2cf849a 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/orders/create/CreatePage.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/orders/create/CreatePage.tsx
@@ -44,6 +44,7 @@ import { OrderCreateSchema as schema } from "../../../../schemas/index.js";
import { rate } from "../../../../utils/amount.js";
import { undefinedIfEmpty } from "../../../../utils/table.js";
import { useSettings } from "../../../../hooks/useSettings.js";
+import { InputToggle } from "../../../../components/form/InputToggle.js";
interface Props {
onCreate: (d: MerchantBackend.Orders.PostOrderRequest) => void;
@@ -52,34 +53,38 @@ interface Props {
instanceInventory: (MerchantBackend.Products.ProductDetail & WithId)[];
}
interface InstanceConfig {
- default_max_wire_fee: string;
- default_max_deposit_fee: string;
- default_wire_fee_amortization: number;
+ use_stefan: boolean;
default_pay_delay: Duration;
+ default_wire_transfer_delay: Duration;
}
-function with_defaults(config: InstanceConfig): Partial<Entity> {
+function with_defaults(config: InstanceConfig, currency: string): Partial<Entity> {
const defaultPayDeadline =
!config.default_pay_delay || config.default_pay_delay.d_us === "forever"
? undefined
: add(new Date(), {
seconds: config.default_pay_delay.d_us / (1000 * 1000),
});
+ const defaultWireDeadline =
+ !config.default_wire_transfer_delay || config.default_wire_transfer_delay.d_us === "forever"
+ ? undefined
+ : add(new Date(), {
+ seconds: config.default_wire_transfer_delay.d_us / (1000 * 1000),
+ });
return {
inventoryProducts: {},
products: [],
pricing: {},
payments: {
- max_wire_fee: config.default_max_wire_fee,
- max_fee: config.default_max_deposit_fee,
- wire_fee_amortization: config.default_wire_fee_amortization,
+ max_fee: undefined,
pay_deadline: defaultPayDeadline,
refund_deadline: defaultPayDeadline,
createToken: true,
+ wire_transfer_deadline: defaultWireDeadline,
},
shipping: {},
- extra: "",
+ extra: {},
};
}
@@ -107,8 +112,6 @@ interface Payments {
wire_transfer_deadline?: Date;
auto_refund_deadline?: Date;
max_fee?: string;
- max_wire_fee?: string;
- wire_fee_amortization?: number;
createToken: boolean;
minimum_age?: number;
}
@@ -118,7 +121,7 @@ interface Entity {
pricing: Partial<Pricing>;
payments: Partial<Payments>;
shipping: Partial<Shipping>;
- extra: string;
+ extra: Record<string, string>;
}
const stringIsValidJSON = (value: string) => {
@@ -136,8 +139,9 @@ export function CreatePage({
instanceConfig,
instanceInventory,
}: Props): VNode {
- const [value, valueHandler] = useState(with_defaults(instanceConfig));
const config = useConfigContext();
+ const instance_default = with_defaults(instanceConfig, config.currency)
+ const [value, valueHandler] = useState(instance_default);
const zero = Amounts.zeroOfCurrency(config.currency);
const [settings] = useSettings()
const inventoryList = Object.values(value.inventoryProducts || {});
@@ -160,10 +164,10 @@ export function CreatePage({
? i18n.str`must be greater than 0`
: undefined,
}),
- extra:
- value.extra && !stringIsValidJSON(value.extra)
- ? i18n.str`not a valid json`
- : undefined,
+ // extra:
+ // value.extra && !stringIsValidJSON(value.extra)
+ // ? i18n.str`not a valid json`
+ // : undefined,
payments: undefinedIfEmpty({
refund_deadline: !value.payments?.refund_deadline
? undefined
@@ -202,6 +206,7 @@ export function CreatePage({
)
? i18n.str`auto refund cannot be after refund deadline`
: undefined,
+
}),
shipping: undefinedIfEmpty({
delivery_date: !value.shipping?.delivery_date
@@ -225,7 +230,7 @@ export function CreatePage({
amount: order.pricing.order_price,
summary: order.pricing.summary,
products: productList,
- extra: value.extra,
+ extra: JSON.stringify(value.extra),
pay_deadline: value.payments.pay_deadline
? {
t_s: Math.floor(value.payments.pay_deadline.getTime() / 1000),
@@ -250,9 +255,7 @@ export function CreatePage({
),
}
: undefined,
- wire_fee_amortization: value.payments.wire_fee_amortization as number,
max_fee: value.payments.max_fee as string,
- max_wire_fee: value.payments.max_wire_fee as string,
delivery_date: value.shipping.delivery_date
? { t_s: value.shipping.delivery_date.getTime() / 1000 }
@@ -326,6 +329,8 @@ export function CreatePage({
const totalAsString = Amounts.stringify(totalPrice.amount);
const allProducts = productList.concat(inventoryList.map(asProduct));
+ const [newField, setNewField] = useState("")
+
useEffect(() => {
valueHandler((v) => {
return {
@@ -486,16 +491,61 @@ export function CreatePage({
name="payments.pay_deadline"
label={i18n.str`Payment deadline`}
tooltip={i18n.str`Deadline for the customer to pay for the offer before it expires. Inventory products will be reserved until this deadline.`}
+ side={
+ <span>
+ <button class="button" onClick={() => {
+ valueHandler({
+ ...value,
+ payments: {
+ ...(value.payments ?? {}),
+ pay_deadline: instance_default.payments?.pay_deadline
+ }
+ })
+ }}>
+ <i18n.Translate>default</i18n.Translate>
+ </button>
+ </span>
+ }
/>
<InputDate
name="payments.refund_deadline"
label={i18n.str`Refund deadline`}
tooltip={i18n.str`Time until which the order can be refunded by the merchant.`}
+ side={
+ <span>
+ <button class="button" onClick={() => {
+ valueHandler({
+ ...value,
+ payments: {
+ ...(value.payments ?? {}),
+ refund_deadline: instance_default.payments?.refund_deadline
+ }
+ })
+ }}>
+ <i18n.Translate>default</i18n.Translate>
+ </button>
+ </span>
+ }
/>
<InputDate
name="payments.wire_transfer_deadline"
label={i18n.str`Wire transfer deadline`}
tooltip={i18n.str`Deadline for the exchange to make the wire transfer.`}
+ side={
+ <span>
+ <button class="button" onClick={() => {
+ valueHandler({
+ ...value,
+ payments: {
+ ...(value.payments ?? {}),
+ wire_transfer_deadline: instance_default.payments?.wire_transfer_deadline
+ }
+ })
+ }}>
+ <i18n.Translate>default</i18n.Translate>
+ </button>
+ </span>
+ }
/>
<InputDate
name="payments.auto_refund_deadline"
@@ -505,23 +555,13 @@ export function CreatePage({
<InputCurrency
name="payments.max_fee"
- label={i18n.str`Maximum deposit fee`}
- tooltip={i18n.str`Maximum deposit fees the merchant is willing to cover for this order. Higher deposit fees must be covered in full by the consumer.`}
+ label={i18n.str`Maximum fee`}
+ tooltip={i18n.str`Maximum fees the merchant is willing to cover for this order. Higher deposit fees must be covered in full by the consumer.`}
/>
- <InputCurrency
- name="payments.max_wire_fee"
- label={i18n.str`Maximum wire fee`}
- tooltip={i18n.str`Maximum aggregate wire fees the merchant is willing to cover for this order. Wire fees exceeding this amount are to be covered by the customers.`}
- />
- <InputNumber
- name="payments.wire_fee_amortization"
- label={i18n.str`Wire fee amortization`}
- tooltip={i18n.str`Factor by which wire fees exceeding the above threshold are divided to determine the share of excess wire fees to be paid explicitly by the consumer.`}
- />
- <InputBoolean
+ <InputToggle
name="payments.createToken"
label={i18n.str`Create token`}
- tooltip={i18n.str`Uncheck this option if the merchant backend generated an order ID with enough entropy to prevent adversarial claims.`}
+ tooltip={i18n.str`If the order ID is easy to guess the token will prevent user to steal orders from others.`}
/>
<InputNumber
name="payments.minimum_age"
@@ -530,7 +570,7 @@ export function CreatePage({
help={
minAgeByProducts > 0
? i18n.str`Min age defined by the producs is ${minAgeByProducts}`
- : undefined
+ : i18n.str`No product with age restriction in this order`
}
/>
</InputGroup>
@@ -542,12 +582,53 @@ export function CreatePage({
label={i18n.str`Additional information`}
tooltip={i18n.str`Custom information to be included in the contract for this order.`}
>
- <Input
- name="extra"
- inputType="multiline"
- label={`Value`}
- tooltip={i18n.str`You must enter a value in JavaScript Object Notation (JSON).`}
- />
+ {Object.keys(value.extra ?? {}).map((key) => {
+
+ return <Input
+ name={`extra.${key}`}
+ inputType="multiline"
+ label={key}
+ tooltip={i18n.str`You must enter a value in JavaScript Object Notation (JSON).`}
+ side={
+ <button class="button" onClick={(e) => {
+ if (value.extra && value.extra[key] !== undefined) {
+ console.log(value.extra)
+ delete value.extra[key]
+ }
+ valueHandler({
+ ...value,
+ })
+ }}>remove</button>
+ }
+ />
+ })}
+ <div class="field is-horizontal">
+ <div class="field-label is-normal">
+ <label class="label">
+ <i18n.Translate>Custom field name</i18n.Translate>
+ <span class="icon has-tooltip-right" data-tooltip={"new extra field"}>
+ <i class="mdi mdi-information" />
+ </span>
+ </label>
+ </div>
+ <div class="field-body is-flex-grow-3">
+ <div class="field">
+ <p class="control">
+ <input class="input " value={newField} onChange={(e) => setNewField(e.currentTarget.value)} />
+ </p>
+ </div>
+ </div>
+ <button class="button" onClick={(e) => {
+ setNewField("")
+ valueHandler({
+ ...value,
+ extra: {
+ ...(value.extra ?? {}),
+ [newField]: ""
+ }
+ })
+ }}>add</button>
+ </div>
</InputGroup>
}
</FormProvider>
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/orders/create/index.tsx b/packages/merchant-backoffice-ui/src/paths/instance/orders/create/index.tsx
index ffefd5302..2474fd042 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/orders/create/index.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/orders/create/index.tsx
@@ -38,7 +38,7 @@ export type Entity = {
};
interface Props {
onBack?: () => void;
- onConfirm: () => void;
+ onConfirm: (id: string) => void;
onUnauthorized: () => VNode;
onNotFound: () => VNode;
onLoadError: (error: HttpError<MerchantBackend.ErrorDetail>) => VNode;
@@ -95,7 +95,9 @@ export default function OrderCreate({
onBack={onBack}
onCreate={(request: MerchantBackend.Orders.PostOrderRequest) => {
createOrder(request)
- .then(onConfirm)
+ .then((r) => {
+ return onConfirm(r.data.order_id)
+ })
.catch((error) => {
setNotif({
message: "could not create order",
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/orders/details/Detail.stories.tsx b/packages/merchant-backoffice-ui/src/paths/instance/orders/details/Detail.stories.tsx
index e430ede56..6e73a01a5 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/orders/details/Detail.stories.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/orders/details/Detail.stories.tsx
@@ -50,13 +50,11 @@ const defaultContractTerm = {
auditors: [],
exchanges: [],
max_fee: "TESTKUDOS:1",
- max_wire_fee: "TESTKUDOS:1",
merchant: {} as any,
merchant_base_url: "http://merchant.url/",
order_id: "2021.165-03GDFC26Y1NNG",
products: [],
summary: "text summary",
- wire_fee_amortization: 1,
wire_transfer_deadline: {
t_s: "never",
},
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/orders/details/DetailPage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/orders/details/DetailPage.tsx
index 8965d41c9..e42adc2ff 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/orders/details/DetailPage.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/orders/details/DetailPage.tsx
@@ -19,7 +19,7 @@
* @author Sebastian Javier Marchano (sebasjm)
*/
-import { AmountJson, Amounts } from "@gnu-taler/taler-util";
+import { AmountJson, Amounts, stringifyRefundUri } from "@gnu-taler/taler-util";
import { useTranslationContext } from "@gnu-taler/web-util/browser";
import { format, formatDistance } from "date-fns";
import { Fragment, h, VNode } from "preact";
@@ -38,6 +38,7 @@ import { MerchantBackend } from "../../../../declaration.js";
import { mergeRefunds } from "../../../../utils/amount.js";
import { RefundModal } from "../list/Table.js";
import { Event, Timeline } from "./Timeline.js";
+import { dateFormatForSettings, datetimeFormatForSettings, useSettings } from "../../../../hooks/useSettings.js";
type Entity = MerchantBackend.Orders.MerchantOrderStatusResponse;
type CT = MerchantBackend.ContractTerms;
@@ -87,18 +88,6 @@ function ContractTerms({ value }: { value: CT }) {
label={i18n.str`Max fee`}
tooltip={i18n.str`maximum total deposit fee accepted by the merchant for this contract`}
/>
- <Input<CT>
- readonly
- name="max_wire_fee"
- label={i18n.str`Max wire fee`}
- tooltip={i18n.str`maximum wire fee accepted by the merchant`}
- />
- <Input<CT>
- readonly
- name="wire_fee_amortization"
- label={i18n.str`Wire fee amortization`}
- tooltip={i18n.str`over how many customer transactions does the merchant expect to amortize wire fees on average`}
- />
<InputDate<CT>
readonly
name="timestamp"
@@ -204,6 +193,7 @@ function ClaimedPage({
const [value, valueHandler] = useState<Partial<Claimed>>(order);
const { i18n } = useTranslationContext();
+ const [settings] = useSettings()
return (
<div>
@@ -249,7 +239,7 @@ function ClaimedPage({
</b>{" "}
{format(
new Date(order.contract_terms.timestamp.t_s * 1000),
- "yyyy-MM-dd HH:mm:ss",
+ datetimeFormatForSettings(settings)
)}
</p>
</div>
@@ -427,9 +417,10 @@ function PaidPage({
const [value, valueHandler] = useState<Partial<Paid>>(order);
const { url } = useBackendContext();
- const refundHost = url.replace(/.*:\/\//, ""); // remove protocol part
- const proto = url.startsWith("http://") ? "taler+http" : "taler";
- const refundurl = `${proto}://refund/${refundHost}/${order.contract_terms.order_id}/`;
+ const refundurl = stringifyRefundUri({
+ merchantBaseUrl: url,
+ orderId: order.contract_terms.order_id
+ })
const refundable =
new Date().getTime() < order.contract_terms.refund_deadline.t_s * 1000;
const { i18n } = useTranslationContext();
@@ -618,6 +609,7 @@ function UnpaidPage({
}) {
const [value, valueHandler] = useState<Partial<Unpaid>>(order);
const { i18n } = useTranslationContext();
+ const [settings] = useSettings()
return (
<div>
<section class="hero is-hero-bar">
@@ -666,7 +658,7 @@ function UnpaidPage({
? "never"
: format(
new Date(order.creation_time.t_s * 1000),
- "yyyy-MM-dd HH:mm:ss",
+ datetimeFormatForSettings(settings)
)}
</p>
</div>
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/orders/details/Timeline.tsx b/packages/merchant-backoffice-ui/src/paths/instance/orders/details/Timeline.tsx
index e68889a92..8c863f386 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/orders/details/Timeline.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/orders/details/Timeline.tsx
@@ -16,6 +16,7 @@
import { format } from "date-fns";
import { h } from "preact";
import { useEffect, useState } from "preact/hooks";
+import { datetimeFormatForSettings, useSettings } from "../../../../hooks/useSettings.js";
interface Props {
events: Event[];
@@ -30,7 +31,7 @@ export function Timeline({ events: e }: Props) {
});
events.sort((a, b) => a.when.getTime() - b.when.getTime());
-
+ const [settings] = useSettings();
const [state, setState] = useState(events);
useEffect(() => {
const handle = setTimeout(() => {
@@ -104,7 +105,7 @@ export function Timeline({ events: e }: Props) {
}
})()}
<div class="timeline-content">
- {e.description !== "now" && <p class="heading">{format(e.when, "yyyy/MM/dd HH:mm:ss")}</p>}
+ {e.description !== "now" && <p class="heading">{format(e.when, datetimeFormatForSettings(settings))}</p>}
<p>{e.description}</p>
</div>
</div>
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/orders/list/ListPage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/orders/list/ListPage.tsx
index 37770d273..c29a6fa6e 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/orders/list/ListPage.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/orders/list/ListPage.tsx
@@ -26,19 +26,24 @@ import { useState } from "preact/hooks";
import { DatePicker } from "../../../../components/picker/DatePicker.js";
import { MerchantBackend, WithId } from "../../../../declaration.js";
import { CardTable } from "./Table.js";
+import { dateFormatForSettings, useSettings } from "../../../../hooks/useSettings.js";
export interface ListPageProps {
errorOrderId: string | undefined;
onShowAll: () => void;
+ onShowNotPaid: () => void;
onShowPaid: () => void;
onShowRefunded: () => void;
onShowNotWired: () => void;
+ onShowWired: () => void;
onCopyURL: (id: string) => void;
isAllActive: string;
isPaidActive: string;
+ isNotPaidActive: string;
isRefundedActive: string;
isNotWiredActive: string;
+ isWiredActive: string;
jumpToDate?: Date;
onSelectDate: (date?: Date) => void;
@@ -66,18 +71,23 @@ export function ListPage({
onCopyURL,
onShowAll,
onShowPaid,
+ onShowNotPaid,
onShowRefunded,
onShowNotWired,
+ onShowWired,
onSelectDate,
isPaidActive,
isRefundedActive,
isNotWiredActive,
onCreate,
+ isNotPaidActive,
+ isWiredActive,
}: ListPageProps): VNode {
const { i18n } = useTranslationContext();
const dateTooltip = i18n.str`select date to show nearby orders`;
const [pickDate, setPickDate] = useState(false);
const [orderId, setOrderId] = useState<string>("");
+ const [settings] = useSettings();
return (
<section class="section is-main-section">
@@ -116,13 +126,13 @@ export function ListPage({
<div class="column is-two-thirds">
<div class="tabs" style={{ overflow: "inherit" }}>
<ul>
- <li class={isAllActive}>
+ <li class={isNotPaidActive}>
<div
class="has-tooltip-right"
- data-tooltip={i18n.str`remove all filters`}
+ data-tooltip={i18n.str`only show paid orders`}
>
- <a onClick={onShowAll}>
- <i18n.Translate>All</i18n.Translate>
+ <a onClick={onShowNotPaid}>
+ <i18n.Translate>New</i18n.Translate>
</a>
</div>
</li>
@@ -156,6 +166,26 @@ export function ListPage({
</a>
</div>
</li>
+ <li class={isWiredActive}>
+ <div
+ class="has-tooltip-left"
+ data-tooltip={i18n.str`only show orders where customers paid, but wire payments from payment provider are still pending`}
+ >
+ <a onClick={onShowWired}>
+ <i18n.Translate>Completed</i18n.Translate>
+ </a>
+ </div>
+ </li>
+ <li class={isAllActive}>
+ <div
+ class="has-tooltip-right"
+ data-tooltip={i18n.str`remove all filters`}
+ >
+ <a onClick={onShowAll}>
+ <i18n.Translate>All</i18n.Translate>
+ </a>
+ </div>
+ </li>
</ul>
</div>
</div>
@@ -180,8 +210,8 @@ export function ListPage({
class="input"
type="text"
readonly
- value={!jumpToDate ? "" : format(jumpToDate, "yyyy/MM/dd")}
- placeholder={i18n.str`date (YYYY/MM/DD)`}
+ value={!jumpToDate ? "" : format(jumpToDate, dateFormatForSettings(settings))}
+ placeholder={i18n.str`date (${dateFormatForSettings(settings)})`}
onClick={() => {
setPickDate(true);
}}
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/orders/list/Table.tsx b/packages/merchant-backoffice-ui/src/paths/instance/orders/list/Table.tsx
index 3c927033b..608c9b20d 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/orders/list/Table.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/orders/list/Table.tsx
@@ -36,6 +36,7 @@ import { ConfirmModal } from "../../../../components/modal/index.js";
import { useConfigContext } from "../../../../context/config.js";
import { MerchantBackend, WithId } from "../../../../declaration.js";
import { mergeRefunds } from "../../../../utils/amount.js";
+import { datetimeFormatForSettings, useSettings } from "../../../../hooks/useSettings.js";
type Entity = MerchantBackend.Orders.OrderHistoryEntry & WithId;
interface Props {
@@ -136,6 +137,7 @@ function Table({
hasMoreBefore,
}: TableProps): VNode {
const { i18n } = useTranslationContext();
+ const [settings] = useSettings();
return (
<div class="table-container">
{onLoadMoreBefore && (
@@ -173,9 +175,9 @@ function Table({
{i.timestamp.t_s === "never"
? "never"
: format(
- new Date(i.timestamp.t_s * 1000),
- "yyyy/MM/dd HH:mm:ss",
- )}
+ new Date(i.timestamp.t_s * 1000),
+ datetimeFormatForSettings(settings),
+ )}
</td>
<td
onClick={(): void => onSelect(i)}
@@ -260,6 +262,7 @@ export function RefundModal({
}: RefundModalProps): VNode {
type State = { mainReason?: string; description?: string; refund?: string };
const [form, setValue] = useState<State>({});
+ const [settings] = useSettings();
const { i18n } = useTranslationContext();
// const [errors, setErrors] = useState<FormErrors<State>>({});
@@ -281,8 +284,8 @@ export function RefundModal({
const totalRefundable = !orderPrice
? Amounts.zeroOfCurrency(totalRefunded.currency)
: refunds.length
- ? Amounts.sub(orderPrice, totalRefunded).amount
- : orderPrice;
+ ? Amounts.sub(orderPrice, totalRefunded).amount
+ : orderPrice;
const isRefundable = Amounts.isNonZero(totalRefundable);
const duplicatedText = i18n.str`duplicated`;
@@ -296,10 +299,10 @@ export function RefundModal({
refund: !form.refund
? i18n.str`required`
: !Amounts.parse(form.refund)
- ? i18n.str`invalid format`
- : Amounts.cmp(totalRefundable, Amounts.parse(form.refund)!) === -1
- ? i18n.str`this value exceed the refundable amount`
- : undefined,
+ ? i18n.str`invalid format`
+ : Amounts.cmp(totalRefundable, Amounts.parse(form.refund)!) === -1
+ ? i18n.str`this value exceed the refundable amount`
+ : undefined,
};
const hasErrors = Object.keys(errors).some(
(k) => (errors as any)[k] !== undefined,
@@ -361,9 +364,9 @@ export function RefundModal({
{r.timestamp.t_s === "never"
? "never"
: format(
- new Date(r.timestamp.t_s * 1000),
- "yyyy-MM-dd HH:mm:ss",
- )}
+ new Date(r.timestamp.t_s * 1000),
+ datetimeFormatForSettings(settings),
+ )}
</td>
<td>{r.amount}</td>
<td>{r.reason}</td>
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/orders/list/index.tsx b/packages/merchant-backoffice-ui/src/paths/instance/orders/list/index.tsx
index 6888eda58..48f77e3d3 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/orders/list/index.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/orders/list/index.tsx
@@ -55,7 +55,7 @@ export default function OrderList({
onSelect,
onNotFound,
}: Props): VNode {
- const [filter, setFilter] = useState<InstanceOrderFilter>({});
+ const [filter, setFilter] = useState<InstanceOrderFilter>({ paid: "no" });
const [orderToBeRefunded, setOrderToBeRefunded] = useState<
MerchantBackend.Orders.OrderHistoryEntry | undefined
>(undefined);
@@ -88,13 +88,15 @@ export default function OrderList({
return onLoadError(result);
}
- const isPaidActive = filter.paid === "yes" ? "is-active" : "";
+ const isNotPaidActive = filter.paid === "no" ? "is-active" : "";
+ const isPaidActive = filter.paid === "yes" && filter.wired === undefined ? "is-active" : "";
const isRefundedActive = filter.refunded === "yes" ? "is-active" : "";
- const isNotWiredActive = filter.wired === "no" ? "is-active" : "";
+ const isNotWiredActive = filter.wired === "no" && filter.paid === "yes" ? "is-active" : "";
+ const isWiredActive = filter.wired === "yes" ? "is-active" : "";
const isAllActive =
filter.paid === undefined &&
- filter.refunded === undefined &&
- filter.wired === undefined
+ filter.refunded === undefined &&
+ filter.wired === undefined
? "is-active"
: "";
@@ -127,7 +129,9 @@ export default function OrderList({
errorOrderId={errorOrderId}
isAllActive={isAllActive}
isNotWiredActive={isNotWiredActive}
+ isWiredActive={isWiredActive}
isPaidActive={isPaidActive}
+ isNotPaidActive={isNotPaidActive}
isRefundedActive={isRefundedActive}
jumpToDate={filter.date}
onCopyURL={(id) =>
@@ -137,9 +141,11 @@ export default function OrderList({
onSearchOrderById={testIfOrderExistAndSelect}
onSelectDate={setNewDate}
onShowAll={() => setFilter({})}
+ onShowNotPaid={() => setFilter({ paid: "no" })}
onShowPaid={() => setFilter({ paid: "yes" })}
onShowRefunded={() => setFilter({ refunded: "yes" })}
- onShowNotWired={() => setFilter({ wired: "no" })}
+ onShowNotWired={() => setFilter({ wired: "no", paid: "yes" })}
+ onShowWired={() => setFilter({ wired: "yes" })}
/>
{orderToBeRefunded && (
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/products/list/Table.tsx b/packages/merchant-backoffice-ui/src/paths/instance/products/list/Table.tsx
index 6bbb89dfa..cbfe1d573 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/products/list/Table.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/products/list/Table.tsx
@@ -32,6 +32,7 @@ import {
import { InputCurrency } from "../../../../components/form/InputCurrency.js";
import { InputNumber } from "../../../../components/form/InputNumber.js";
import { MerchantBackend, WithId } from "../../../../declaration.js";
+import { dateFormatForSettings, useSettings } from "../../../../hooks/useSettings.js";
type Entity = MerchantBackend.Products.ProductDetail & WithId;
@@ -122,6 +123,7 @@ function Table({
onDelete,
}: TableProps): VNode {
const { i18n } = useTranslationContext();
+ const [settings] = useSettings();
return (
<div class="table-container">
<table class="table is-fullwidth is-striped is-hoverable is-fullwidth">
@@ -134,7 +136,7 @@ function Table({
<i18n.Translate>Description</i18n.Translate>
</th>
<th>
- <i18n.Translate>Sell</i18n.Translate>
+ <i18n.Translate>Price per unit</i18n.Translate>
</th>
<th>
<i18n.Translate>Taxes</i18n.Translate>
@@ -156,10 +158,10 @@ function Table({
const restStockInfo = !i.next_restock
? ""
: i.next_restock.t_s === "never"
- ? "never"
- : `restock at ${format(
+ ? "never"
+ : `restock at ${format(
new Date(i.next_restock.t_s * 1000),
- "yyyy/MM/dd",
+ dateFormatForSettings(settings),
)}`;
let stockInfo: ComponentChildren = "";
if (i.total_stock < 0) {
@@ -332,26 +334,35 @@ function FastProductWithInfiniteStockUpdateForm({
/>
</FormProvider>
- <div class="buttons is-right mt-5">
- <button class="button" onClick={onCancel}>
- <i18n.Translate>Cancel</i18n.Translate>
- </button>
- <span
- class="has-tooltip-left"
- data-tooltip={i18n.str`update product with new price`}
- >
- <button
- class="button is-info"
- onClick={() =>
- onUpdate({
- ...product,
- price: value.price,
- })
- }
- >
- <i18n.Translate>Confirm</i18n.Translate>
+ <div class="buttons is-expanded">
+
+ <div class="buttons mt-5">
+
+ <button class="button " onClick={onCancel}>
+ <i18n.Translate>Clone</i18n.Translate>
</button>
- </span>
+ </div>
+ <div class="buttons is-right mt-5">
+ <button class="button" onClick={onCancel}>
+ <i18n.Translate>Cancel</i18n.Translate>
+ </button>
+ <span
+ class="has-tooltip-left"
+ data-tooltip={i18n.str`update product with new price`}
+ >
+ <button
+ class="button is-info"
+ onClick={() =>
+ onUpdate({
+ ...product,
+ price: value.price,
+ })
+ }
+ >
+ <i18n.Translate>Confirm update</i18n.Translate>
+ </button>
+ </span>
+ </div>
</div>
</Fragment>
);
@@ -374,9 +385,8 @@ function FastProductWithManagedStockUpdateForm({
const errors: FormErrors<FastProductUpdate> = {
lost:
currentStock + value.incoming < value.lost
- ? `lost cannot be greater that current + incoming (max ${
- currentStock + value.incoming
- })`
+ ? `lost cannot be greater that current + incoming (max ${currentStock + value.incoming
+ })`
: undefined,
};
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/products/list/index.tsx b/packages/merchant-backoffice-ui/src/paths/instance/products/list/index.tsx
index 87efd1554..85c50e5ed 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/products/list/index.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/products/list/index.tsx
@@ -36,6 +36,7 @@ import {
import { Notification } from "../../../../utils/types.js";
import { CardTable } from "./Table.js";
import { HttpStatusCode } from "@gnu-taler/taler-util";
+import { ConfirmModal, DeleteModal } from "../../../../components/modal/index.js";
interface Props {
onUnauthorized: () => VNode;
@@ -53,6 +54,8 @@ export default function ProductList({
}: Props): VNode {
const result = useInstanceProducts();
const { deleteProduct, updateProduct } = useProductAPI();
+ const [deleting, setDeleting] =
+ useState<MerchantBackend.Products.ProductDetail & WithId | null>(null);
const [notif, setNotif] = useState<Notification | undefined>(undefined);
const { i18n } = useTranslationContext();
@@ -97,22 +100,43 @@ export default function ProductList({
}
onSelect={(product) => onSelect(product.id)}
onDelete={(prod: MerchantBackend.Products.ProductDetail & WithId) =>
- deleteProduct(prod.id)
- .then(() =>
+ setDeleting(prod)
+ }
+ />
+
+ {deleting && (
+ <ConfirmModal
+ label={`Delete product`}
+ description={`Delete the product "${deleting.description}"`}
+ danger
+ active
+ onCancel={() => setDeleting(null)}
+ onConfirm={async (): Promise<void> => {
+ try {
+ await deleteProduct(deleting.id);
setNotif({
- message: i18n.str`product delete successfully`,
+ message: i18n.str`Product "${deleting.description}" (ID: ${deleting.id}) has been deleted`,
type: "SUCCESS",
- }),
- )
- .catch((error) =>
+ });
+ } catch (error) {
setNotif({
- message: i18n.str`could not delete the product`,
+ message: i18n.str`Failed to delete product`,
type: "ERROR",
- description: error.message,
- }),
- )
- }
- />
+ description: error instanceof Error ? error.message : undefined,
+ });
+ }
+ setDeleting(null);
+ }}
+ >
+ <p>
+ If you delete the product named <b>&quot;{deleting.description}&quot;</b> (ID:{" "}
+ <b>{deleting.id}</b>), the stock and related information will be lost
+ </p>
+ <p class="warning">
+ Deleting an product <b>cannot be undone</b>.
+ </p>
+ </ConfirmModal>
+ )}
</section>
);
}
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/reserves/create/CreatePage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/reserves/create/CreatePage.tsx
index fccb20121..2201e75a5 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/reserves/create/CreatePage.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/reserves/create/CreatePage.tsx
@@ -36,7 +36,7 @@ import {
URL_REGEX,
} from "../../../../utils/constants.js";
-type Entity = MerchantBackend.Tips.ReserveCreateRequest;
+type Entity = MerchantBackend.Rewards.ReserveCreateRequest;
interface Props {
onCreate: (d: Entity) => Promise<void>;
@@ -80,15 +80,15 @@ function ViewStep({
initial_balance: !reserve.initial_balance
? "cannot be empty"
: !(parseInt(reserve.initial_balance.split(":")[1], 10) > 0)
- ? i18n.str`it should be greater than 0`
- : undefined,
+ ? i18n.str`it should be greater than 0`
+ : undefined,
exchange_url: !reserve.exchange_url
? i18n.str`cannot be empty`
: !URL_REGEX.test(reserve.exchange_url)
- ? i18n.str`must be a valid URL`
- : !exchangeQueryError
- ? undefined
- : exchangeQueryError,
+ ? i18n.str`must be a valid URL`
+ : !exchangeQueryError
+ ? undefined
+ : exchangeQueryError,
};
const hasErrors = Object.keys(errors).some(
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/reserves/create/CreatedSuccessfully.tsx b/packages/merchant-backoffice-ui/src/paths/instance/reserves/create/CreatedSuccessfully.tsx
index 94fcdaff7..1d512c843 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/reserves/create/CreatedSuccessfully.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/reserves/create/CreatedSuccessfully.tsx
@@ -22,8 +22,8 @@ import { CreatedSuccessfully as Template } from "../../../../components/notifica
import { MerchantBackend, WireAccount } from "../../../../declaration.js";
type Entity = {
- request: MerchantBackend.Tips.ReserveCreateRequest;
- response: MerchantBackend.Tips.ReserveCreateConfirmation;
+ request: MerchantBackend.Rewards.ReserveCreateRequest;
+ response: MerchantBackend.Rewards.ReserveCreateConfirmation;
};
interface Props {
@@ -98,15 +98,15 @@ export function ShowAccountsOfReserveAsQRWithLink({
const accountsInfo = !accounts
? []
: accounts
- .map((acc) => {
- const p = parsePaytoUri(acc.payto_uri);
- if (p) {
- p.params["message"] = message;
- p.params["amount"] = amount;
- }
- return p;
- })
- .filter(isNotUndefined);
+ .map((acc) => {
+ const p = parsePaytoUri(acc.payto_uri);
+ if (p) {
+ p.params["message"] = message;
+ p.params["amount"] = amount;
+ }
+ return p;
+ })
+ .filter(isNotUndefined);
const links = accountsInfo.map((a) => stringifyPaytoUri(a));
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/reserves/create/index.tsx b/packages/merchant-backoffice-ui/src/paths/instance/reserves/create/index.tsx
index 8a4fe1565..4bbaf1459 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/reserves/create/index.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/reserves/create/index.tsx
@@ -39,9 +39,9 @@ export default function CreateReserve({ onBack, onConfirm }: Props): VNode {
const [createdOk, setCreatedOk] = useState<
| {
- request: MerchantBackend.Tips.ReserveCreateRequest;
- response: MerchantBackend.Tips.ReserveCreateConfirmation;
- }
+ request: MerchantBackend.Rewards.ReserveCreateRequest;
+ response: MerchantBackend.Rewards.ReserveCreateConfirmation;
+ }
| undefined
>(undefined);
@@ -54,7 +54,7 @@ export default function CreateReserve({ onBack, onConfirm }: Props): VNode {
<NotificationCard notification={notif} />
<CreatePage
onBack={onBack}
- onCreate={(request: MerchantBackend.Tips.ReserveCreateRequest) => {
+ onCreate={(request: MerchantBackend.Rewards.ReserveCreateRequest) => {
return createReserve(request)
.then((r) => setCreatedOk({ request, response: r.data }))
.catch((error) => {
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/reserves/details/DetailPage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/reserves/details/DetailPage.tsx
index b0173b5d3..d8840eeac 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/reserves/details/DetailPage.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/reserves/details/DetailPage.tsx
@@ -36,11 +36,12 @@ import { InputDate } from "../../../../components/form/InputDate.js";
import { TextField } from "../../../../components/form/TextField.js";
import { SimpleModal } from "../../../../components/modal/index.js";
import { MerchantBackend } from "../../../../declaration.js";
-import { useTipDetails } from "../../../../hooks/reserves.js";
-import { TipInfo } from "./TipInfo.js";
+import { useRewardDetails } from "../../../../hooks/reserves.js";
+import { RewardInfo } from "./RewardInfo.js";
import { ShowAccountsOfReserveAsQRWithLink } from "../create/CreatedSuccessfully.js";
+import { datetimeFormatForSettings, useSettings } from "../../../../hooks/useSettings.js";
-type Entity = MerchantBackend.Tips.ReserveDetail;
+type Entity = MerchantBackend.Rewards.ReserveDetail;
type CT = MerchantBackend.ContractTerms;
interface Props {
@@ -116,14 +117,14 @@ export function DetailPage({ id, selected, onBack }: Props): VNode {
<span class="icon">
<i class="mdi mdi-cash-register" />
</span>
- <i18n.Translate>Tips</i18n.Translate>
+ <i18n.Translate>Rewards</i18n.Translate>
</p>
</header>
<div class="card-content">
<div class="b-table has-pagination">
<div class="table-wrapper has-mobile-cards">
- {selected.tips && selected.tips.length > 0 ? (
- <Table tips={selected.tips} />
+ {selected.rewards && selected.rewards.length > 0 ? (
+ <Table rewards={selected.rewards} />
) : (
<EmptyTable />
)}
@@ -163,7 +164,7 @@ function EmptyTable(): VNode {
</p>
<p>
<i18n.Translate>
- No tips has been authorized from this reserve
+ No reward has been authorized from this reserve
</i18n.Translate>
</p>
</div>
@@ -171,10 +172,10 @@ function EmptyTable(): VNode {
}
interface TableProps {
- tips: MerchantBackend.Tips.TipStatusEntry[];
+ rewards: MerchantBackend.Rewards.RewardStatusEntry[];
}
-function Table({ tips }: TableProps): VNode {
+function Table({ rewards }: TableProps): VNode {
const { i18n } = useTranslationContext();
return (
<div class="table-container">
@@ -196,8 +197,8 @@ function Table({ tips }: TableProps): VNode {
</tr>
</thead>
<tbody>
- {tips.map((t, i) => {
- return <TipRow id={t.tip_id} key={i} entry={t} />;
+ {rewards.map((t, i) => {
+ return <RewardRow id={t.reward_id} key={i} entry={t} />;
})}
</tbody>
</table>
@@ -205,15 +206,16 @@ function Table({ tips }: TableProps): VNode {
);
}
-function TipRow({
+function RewardRow({
id,
entry,
}: {
id: string;
- entry: MerchantBackend.Tips.TipStatusEntry;
+ entry: MerchantBackend.Rewards.RewardStatusEntry;
}) {
const [selected, setSelected] = useState(false);
- const result = useTipDetails(id);
+ const result = useRewardDetails(id);
+ const [settings] = useSettings();
if (result.loading) {
return (
<tr>
@@ -242,11 +244,11 @@ function TipRow({
<Fragment>
{selected && (
<SimpleModal
- description="tip"
+ description="reward"
active
onCancel={() => setSelected(false)}
>
- <TipInfo id={id} amount={info.total_authorized} entity={info} />
+ <RewardInfo id={id} amount={info.total_authorized} entity={info} />
</SimpleModal>
)}
<tr>
@@ -256,7 +258,7 @@ function TipRow({
<td onClick={onSelect}>
{info.expiration.t_s === "never"
? "never"
- : format(info.expiration.t_s * 1000, "yyyy/MM/dd HH:mm:ss")}
+ : format(info.expiration.t_s * 1000, datetimeFormatForSettings(settings))}
</td>
</tr>
</Fragment>
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/reserves/details/Details.stories.tsx b/packages/merchant-backoffice-ui/src/paths/instance/reserves/details/Details.stories.tsx
index 2592e2c6e..41c715f20 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/reserves/details/Details.stories.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/reserves/details/Details.stories.tsx
@@ -92,7 +92,7 @@ export const NotYetFunded = createExample(TestedComponent, {
},
});
-export const FundedWithEmptyTips = createExample(TestedComponent, {
+export const FundedWithEmptyRewards = createExample(TestedComponent, {
id: "THISISTHERESERVEID",
selected: {
active: true,
@@ -115,10 +115,10 @@ export const FundedWithEmptyTips = createExample(TestedComponent, {
},
],
exchange_url: "http://exchange.taler/",
- tips: [
+ rewards: [
{
reason: "asdasd",
- tip_id: "123",
+ reward_id: "123",
total_amount: "TESTKUDOS:1",
},
],
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/reserves/details/TipInfo.tsx b/packages/merchant-backoffice-ui/src/paths/instance/reserves/details/RewardInfo.tsx
index 360d39aba..57a051ed7 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/reserves/details/TipInfo.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/reserves/details/RewardInfo.tsx
@@ -17,8 +17,10 @@ import { format } from "date-fns";
import { Fragment, h, VNode } from "preact";
import { useBackendContext } from "../../../../context/backend.js";
import { MerchantBackend } from "../../../../declaration.js";
+import { datetimeFormatForSettings, useSettings } from "../../../../hooks/useSettings.js";
+import { stringifyRewardUri } from "@gnu-taler/taler-util";
-type Entity = MerchantBackend.Tips.TipDetails;
+type Entity = MerchantBackend.Rewards.RewardDetails;
interface Props {
id: string;
@@ -26,11 +28,10 @@ interface Props {
amount: string;
}
-export function TipInfo({ id, amount, entity }: Props): VNode {
- const { url } = useBackendContext();
- const tipHost = url.replace(/.*:\/\//, ""); // remove protocol part
- const proto = url.startsWith("http://") ? "taler+http" : "taler";
- const tipURL = `${proto}://tip/${tipHost}/${id}`;
+export function RewardInfo({ id: merchantRewardId, amount, entity }: Props): VNode {
+ const { url: merchantBaseUrl } = useBackendContext();
+ const [settings] = useSettings();
+ const rewardURL = stringifyRewardUri({ merchantBaseUrl, merchantRewardId })
return (
<Fragment>
<div class="field is-horizontal">
@@ -52,8 +53,8 @@ export function TipInfo({ id, amount, entity }: Props): VNode {
<div class="field-body is-flex-grow-3">
<div class="field" style={{ overflowWrap: "anywhere" }}>
<p class="control">
- <a target="_blank" rel="noreferrer" href={tipURL}>
- {tipURL}
+ <a target="_blank" rel="noreferrer" href={rewardURL}>
+ {rewardURL}
</a>
</p>
</div>
@@ -73,9 +74,9 @@ export function TipInfo({ id, amount, entity }: Props): VNode {
!entity.expiration || entity.expiration.t_s === "never"
? "never"
: format(
- entity.expiration.t_s * 1000,
- "yyyy/MM/dd HH:mm:ss",
- )
+ entity.expiration.t_s * 1000,
+ datetimeFormatForSettings(settings),
+ )
}
/>
</p>
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/reserves/list/AutorizeTipModal.tsx b/packages/merchant-backoffice-ui/src/paths/instance/reserves/list/AutorizeRewardModal.tsx
index 1882f50d3..e205ee621 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/reserves/list/AutorizeTipModal.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/reserves/list/AutorizeRewardModal.tsx
@@ -34,32 +34,32 @@ import {
ContinueModal,
} from "../../../../components/modal/index.js";
import { MerchantBackend } from "../../../../declaration.js";
-import { AuthorizeTipSchema } from "../../../../schemas/index.js";
+import { AuthorizeRewardSchema } from "../../../../schemas/index.js";
import { CreatedSuccessfully } from "./CreatedSuccessfully.js";
-interface AuthorizeTipModalProps {
+interface AuthorizeRewardModalProps {
onCancel: () => void;
- onConfirm: (value: MerchantBackend.Tips.TipCreateRequest) => void;
- tipAuthorized?: {
- response: MerchantBackend.Tips.TipCreateConfirmation;
- request: MerchantBackend.Tips.TipCreateRequest;
+ onConfirm: (value: MerchantBackend.Rewards.RewardCreateRequest) => void;
+ rewardAuthorized?: {
+ response: MerchantBackend.Rewards.RewardCreateConfirmation;
+ request: MerchantBackend.Rewards.RewardCreateRequest;
};
}
-export function AuthorizeTipModal({
+export function AuthorizeRewardModal({
onCancel,
onConfirm,
- tipAuthorized,
-}: AuthorizeTipModalProps): VNode {
+ rewardAuthorized,
+}: AuthorizeRewardModalProps): VNode {
// const result = useOrderDetails(id)
- type State = MerchantBackend.Tips.TipCreateRequest;
+ type State = MerchantBackend.Rewards.RewardCreateRequest;
const [form, setValue] = useState<Partial<State>>({});
const { i18n } = useTranslationContext();
// const [errors, setErrors] = useState<FormErrors<State>>({})
let errors: FormErrors<State> = {};
try {
- AuthorizeTipSchema.validateSync(form, { abortEarly: false });
+ AuthorizeRewardSchema.validateSync(form, { abortEarly: false });
} catch (err) {
if (err instanceof yup.ValidationError) {
const yupErrors = err.inner as any[];
@@ -77,12 +77,12 @@ export function AuthorizeTipModal({
const validateAndConfirm = () => {
onConfirm(form as State);
};
- if (tipAuthorized) {
+ if (rewardAuthorized) {
return (
- <ContinueModal description="tip" active onConfirm={onCancel}>
+ <ContinueModal description="reward" active onConfirm={onCancel}>
<CreatedSuccessfully
- entity={tipAuthorized.response}
- request={tipAuthorized.request}
+ entity={rewardAuthorized.response}
+ request={rewardAuthorized.request}
onConfirm={onCancel}
/>
</ContinueModal>
@@ -91,7 +91,7 @@ export function AuthorizeTipModal({
return (
<ConfirmModal
- description="tip"
+ description="New reward"
active
onCancel={onCancel}
disabled={hasErrors}
@@ -105,18 +105,18 @@ export function AuthorizeTipModal({
<InputCurrency<State>
name="amount"
label={i18n.str`Amount`}
- tooltip={i18n.str`amount of tip`}
+ tooltip={i18n.str`amount of reward`}
/>
<Input<State>
name="justification"
label={i18n.str`Justification`}
inputType="multiline"
- tooltip={i18n.str`reason for the tip`}
+ tooltip={i18n.str`reason for the reward`}
/>
<Input<State>
name="next_url"
- label={i18n.str`URL after tip`}
- tooltip={i18n.str`URL to visit after tip payment`}
+ label={i18n.str`URL after reward`}
+ tooltip={i18n.str`URL to visit after reward payment`}
/>
</FormProvider>
</ConfirmModal>
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/reserves/list/CreatedSuccessfully.tsx b/packages/merchant-backoffice-ui/src/paths/instance/reserves/list/CreatedSuccessfully.tsx
index 643651b52..b78236bc7 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/reserves/list/CreatedSuccessfully.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/reserves/list/CreatedSuccessfully.tsx
@@ -17,12 +17,13 @@ import { format } from "date-fns";
import { Fragment, h, VNode } from "preact";
import { CreatedSuccessfully as Template } from "../../../../components/notifications/CreatedSuccessfully.js";
import { MerchantBackend } from "../../../../declaration.js";
+import { datetimeFormatForSettings, useSettings } from "../../../../hooks/useSettings.js";
-type Entity = MerchantBackend.Tips.TipCreateConfirmation;
+type Entity = MerchantBackend.Rewards.RewardCreateConfirmation;
interface Props {
entity: Entity;
- request: MerchantBackend.Tips.TipCreateRequest;
+ request: MerchantBackend.Rewards.RewardCreateRequest;
onConfirm: () => void;
onCreateAnother?: () => void;
}
@@ -33,6 +34,7 @@ export function CreatedSuccessfully({
onConfirm,
onCreateAnother,
}: Props): VNode {
+ const [settings] = useSettings();
return (
<Fragment>
<div class="field is-horizontal">
@@ -66,7 +68,7 @@ export function CreatedSuccessfully({
<div class="field-body is-flex-grow-3">
<div class="field">
<p class="control">
- <input readonly class="input" value={entity.tip_status_url} />
+ <input readonly class="input" value={entity.reward_status_url} />
</p>
</div>
</div>
@@ -82,13 +84,13 @@ export function CreatedSuccessfully({
class="input"
readonly
value={
- !entity.tip_expiration ||
- entity.tip_expiration.t_s === "never"
+ !entity.reward_expiration ||
+ entity.reward_expiration.t_s === "never"
? "never"
: format(
- entity.tip_expiration.t_s * 1000,
- "yyyy/MM/dd HH:mm:ss",
- )
+ entity.reward_expiration.t_s * 1000,
+ datetimeFormatForSettings(settings),
+ )
}
/>
</p>
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/reserves/list/List.stories.tsx b/packages/merchant-backoffice-ui/src/paths/instance/reserves/list/List.stories.tsx
index fe305f4fd..b070bbde3 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/reserves/list/List.stories.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/reserves/list/List.stories.tsx
@@ -25,12 +25,6 @@ import { CardTable as TestedComponent } from "./Table.js";
export default {
title: "Pages/Reserve/List",
component: TestedComponent,
- argTypes: {
- onCreate: { action: "onCreate" },
- onDelete: { action: "onDelete" },
- onNewTip: { action: "onNewTip" },
- onSelect: { action: "onSelect" },
- },
};
function createExample<Props>(
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/reserves/list/Table.tsx b/packages/merchant-backoffice-ui/src/paths/instance/reserves/list/Table.tsx
index 1f229d7cb..795e7ec82 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/reserves/list/Table.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/reserves/list/Table.tsx
@@ -23,12 +23,13 @@ import { useTranslationContext } from "@gnu-taler/web-util/browser";
import { format } from "date-fns";
import { Fragment, h, VNode } from "preact";
import { MerchantBackend, WithId } from "../../../../declaration.js";
+import { datetimeFormatForSettings, useSettings } from "../../../../hooks/useSettings.js";
-type Entity = MerchantBackend.Tips.ReserveStatusEntry & WithId;
+type Entity = MerchantBackend.Rewards.ReserveStatusEntry & WithId;
interface Props {
instances: Entity[];
- onNewTip: (id: Entity) => void;
+ onNewReward: (id: Entity) => void;
onSelect: (id: Entity) => void;
onDelete: (id: Entity) => void;
onCreate: () => void;
@@ -38,7 +39,7 @@ export function CardTable({
instances,
onCreate,
onSelect,
- onNewTip,
+ onNewReward,
onDelete,
}: Props): VNode {
const [withoutFunds, withFunds] = instances.reduce((prev, current) => {
@@ -70,7 +71,7 @@ export function CardTable({
<div class="table-wrapper has-mobile-cards">
<TableWithoutFund
instances={withoutFunds}
- onNewTip={onNewTip}
+ onNewReward={onNewReward}
onSelect={onSelect}
onDelete={onDelete}
/>
@@ -108,7 +109,7 @@ export function CardTable({
{withFunds.length > 0 ? (
<Table
instances={withFunds}
- onNewTip={onNewTip}
+ onNewReward={onNewReward}
onSelect={onSelect}
onDelete={onDelete}
/>
@@ -124,13 +125,14 @@ export function CardTable({
}
interface TableProps {
instances: Entity[];
- onNewTip: (id: Entity) => void;
+ onNewReward: (id: Entity) => void;
onDelete: (id: Entity) => void;
onSelect: (id: Entity) => void;
}
-function Table({ instances, onNewTip, onSelect, onDelete }: TableProps): VNode {
+function Table({ instances, onNewReward, onSelect, onDelete }: TableProps): VNode {
const { i18n } = useTranslationContext();
+ const [settings] = useSettings();
return (
<div class="table-container">
<table class="table is-fullwidth is-striped is-hoverable is-fullwidth">
@@ -164,7 +166,7 @@ function Table({ instances, onNewTip, onSelect, onDelete }: TableProps): VNode {
>
{i.creation_time.t_s === "never"
? "never"
- : format(i.creation_time.t_s * 1000, "yyyy/MM/dd HH:mm:ss")}
+ : format(i.creation_time.t_s * 1000, datetimeFormatForSettings(settings))}
</td>
<td
onClick={(): void => onSelect(i)}
@@ -173,9 +175,9 @@ function Table({ instances, onNewTip, onSelect, onDelete }: TableProps): VNode {
{i.expiration_time.t_s === "never"
? "never"
: format(
- i.expiration_time.t_s * 1000,
- "yyyy/MM/dd HH:mm:ss",
- )}
+ i.expiration_time.t_s * 1000,
+ datetimeFormatForSettings(settings),
+ )}
</td>
<td
onClick={(): void => onSelect(i)}
@@ -207,11 +209,11 @@ function Table({ instances, onNewTip, onSelect, onDelete }: TableProps): VNode {
</button>
<button
class="button is-small is-info has-tooltip-left"
- data-tooltip={i18n.str`authorize new tip from selected reserve`}
+ data-tooltip={i18n.str`authorize new reward from selected reserve`}
type="button"
- onClick={(): void => onNewTip(i)}
+ onClick={(): void => onNewReward(i)}
>
- New Tip
+ New Reward
</button>
</div>
</td>
@@ -249,6 +251,7 @@ function TableWithoutFund({
onDelete,
}: TableProps): VNode {
const { i18n } = useTranslationContext();
+ const [settings] = useSettings();
return (
<div class="table-container">
<table class="table is-fullwidth is-striped is-hoverable is-fullwidth">
@@ -276,7 +279,7 @@ function TableWithoutFund({
>
{i.creation_time.t_s === "never"
? "never"
- : format(i.creation_time.t_s * 1000, "yyyy/MM/dd HH:mm:ss")}
+ : format(i.creation_time.t_s * 1000, datetimeFormatForSettings(settings))}
</td>
<td
onClick={(): void => onSelect(i)}
@@ -285,9 +288,9 @@ function TableWithoutFund({
{i.expiration_time.t_s === "never"
? "never"
: format(
- i.expiration_time.t_s * 1000,
- "yyyy/MM/dd HH:mm:ss",
- )}
+ i.expiration_time.t_s * 1000,
+ datetimeFormatForSettings(settings),
+ )}
</td>
<td
onClick={(): void => onSelect(i)}
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/reserves/list/index.tsx b/packages/merchant-backoffice-ui/src/paths/instance/reserves/list/index.tsx
index 14387c2a9..b26ff0000 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/reserves/list/index.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/reserves/list/index.tsx
@@ -34,9 +34,10 @@ import {
useReservesAPI,
} from "../../../../hooks/reserves.js";
import { Notification } from "../../../../utils/types.js";
-import { AuthorizeTipModal } from "./AutorizeTipModal.js";
+import { AuthorizeRewardModal } from "./AutorizeRewardModal.js";
import { CardTable } from "./Table.js";
import { HttpStatusCode } from "@gnu-taler/taler-util";
+import { ConfirmModal } from "../../../../components/modal/index.js";
interface Props {
onUnauthorized: () => VNode;
@@ -46,12 +47,12 @@ interface Props {
onCreate: () => void;
}
-interface TipConfirmation {
- response: MerchantBackend.Tips.TipCreateConfirmation;
- request: MerchantBackend.Tips.TipCreateRequest;
+interface RewardConfirmation {
+ response: MerchantBackend.Rewards.RewardCreateConfirmation;
+ request: MerchantBackend.Rewards.RewardCreateRequest;
}
-export default function ListTips({
+export default function ListRewards({
onUnauthorized,
onLoadError,
onNotFound,
@@ -59,14 +60,16 @@ export default function ListTips({
onCreate,
}: Props): VNode {
const result = useInstanceReserves();
- const { deleteReserve, authorizeTipReserve } = useReservesAPI();
+ const { deleteReserve, authorizeRewardReserve } = useReservesAPI();
const [notif, setNotif] = useState<Notification | undefined>(undefined);
const { i18n } = useTranslationContext();
- const [reserveForTip, setReserveForTip] = useState<string | undefined>(
+ const [reserveForReward, setReserveForReward] = useState<string | undefined>(
undefined,
);
- const [tipAuthorized, setTipAuthorized] = useState<
- TipConfirmation | undefined
+ const [deleting, setDeleting] =
+ useState<MerchantBackend.Rewards.ReserveStatusEntry | null>(null);
+ const [rewardAuthorized, setRewardAuthorized] = useState<
+ RewardConfirmation | undefined
>(undefined);
if (result.loading) return <Loading />;
@@ -88,30 +91,30 @@ export default function ListTips({
<section class="section is-main-section">
<NotificationCard notification={notif} />
- {reserveForTip && (
- <AuthorizeTipModal
+ {reserveForReward && (
+ <AuthorizeRewardModal
onCancel={() => {
- setReserveForTip(undefined);
- setTipAuthorized(undefined);
+ setReserveForReward(undefined);
+ setRewardAuthorized(undefined);
}}
- tipAuthorized={tipAuthorized}
+ rewardAuthorized={rewardAuthorized}
onConfirm={async (request) => {
try {
- const response = await authorizeTipReserve(
- reserveForTip,
+ const response = await authorizeRewardReserve(
+ reserveForReward,
request,
);
- setTipAuthorized({
+ setRewardAuthorized({
request,
response: response.data,
});
} catch (error) {
setNotif({
- message: i18n.str`could not create the tip`,
+ message: i18n.str`could not create the reward`,
type: "ERROR",
description: error instanceof Error ? error.message : undefined,
});
- setReserveForTip(undefined);
+ setReserveForReward(undefined);
}
}}
/>
@@ -122,10 +125,47 @@ export default function ListTips({
.filter((r) => r.active)
.map((o) => ({ ...o, id: o.reserve_pub }))}
onCreate={onCreate}
- onDelete={(reserve) => deleteReserve(reserve.reserve_pub)}
+ onDelete={(reserve) => {
+ setDeleting(reserve)
+ }}
onSelect={(reserve) => onSelect(reserve.id)}
- onNewTip={(reserve) => setReserveForTip(reserve.id)}
+ onNewReward={(reserve) => setReserveForReward(reserve.id)}
/>
+
+ {deleting && (
+ <ConfirmModal
+ label={`Delete reserve`}
+ description={`Delete the reserve`}
+ danger
+ active
+ onCancel={() => setDeleting(null)}
+ onConfirm={async (): Promise<void> => {
+ try {
+ await deleteReserve(deleting.reserve_pub);
+ setNotif({
+ message: i18n.str`Reserve for "${deleting.merchant_initial_amount}" (ID: ${deleting.reserve_pub}) has been deleted`,
+ type: "SUCCESS",
+ });
+ } catch (error) {
+ setNotif({
+ message: i18n.str`Failed to delete reserve`,
+ type: "ERROR",
+ description: error instanceof Error ? error.message : undefined,
+ });
+ }
+ setDeleting(null);
+ }}
+ >
+ <p>
+ If you delete the reserve for <b>&quot;{deleting.merchant_initial_amount}&quot;</b> you won't be able to create more rewards. <br />
+ Reserve ID: <b>{deleting.reserve_pub}</b>
+ </p>
+ <p class="warning">
+ Deleting an template <b>cannot be undone</b>.
+ </p>
+ </ConfirmModal>
+ )}
+
</section>
);
}
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/templates/create/CreatePage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/templates/create/CreatePage.tsx
index e20b9bc27..8629d8dee 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/templates/create/CreatePage.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/templates/create/CreatePage.tsx
@@ -24,7 +24,7 @@ import {
MerchantTemplateContractDetails,
} from "@gnu-taler/taler-util";
import { useTranslationContext } from "@gnu-taler/web-util/browser";
-import { Fragment, h, VNode } from "preact";
+import { Fragment, VNode, h } from "preact";
import { useState } from "preact/hooks";
import { AsyncButton } from "../../../../components/exception/AsyncButton.js";
import {
@@ -35,17 +35,16 @@ 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 { InputSelector } from "../../../../components/form/InputSelector.js";
import { InputWithAddon } from "../../../../components/form/InputWithAddon.js";
import { useBackendContext } from "../../../../context/backend.js";
+import { useInstanceContext } from "../../../../context/instance.js";
import { MerchantBackend } from "../../../../declaration.js";
import {
- isBase32RFC3548Charset,
- randomBase32Key,
+ isBase32RFC3548Charset
} from "../../../../utils/crypto.js";
import { undefinedIfEmpty } from "../../../../utils/table.js";
-import { QR } from "../../../../components/exception/QR.js";
-import { useInstanceContext } from "../../../../context/instance.js";
+import { InputSearchOnList } from "../../../../components/form/InputSearchOnList.js";
+import { useInstanceOtpDevices } from "../../../../hooks/otp.js";
type Entity = MerchantBackend.Template.TemplateAddDetails;
@@ -54,16 +53,11 @@ interface Props {
onBack?: () => void;
}
-const algorithms = [0, 1, 2];
-const algorithmsNames = ["off", "30s 8d TOTP-SHA1", "30s 8d eTOTP-SHA1"];
-
export function CreatePage({ onCreate, onBack }: Props): VNode {
const { i18n } = useTranslationContext();
const backend = useBackendContext();
- const { id: instanceId } = useInstanceContext();
- const issuer = new URL(backend.url).hostname;
+ const devices = useInstanceOtpDevices()
- const [showKey, setShowKey] = useState(false);
const [state, setState] = useState<Partial<Entity>>({
template_contract: {
minimum_age: 0,
@@ -78,7 +72,11 @@ export function CreatePage({ onCreate, onBack }: Props): VNode {
: Amounts.parse(state.template_contract?.amount);
const errors: FormErrors<Entity> = {
- template_id: !state.template_id ? i18n.str`should not be empty` : undefined,
+ template_id: !state.template_id
+ ? i18n.str`should not be empty`
+ : !/[a-zA-Z0-9]*/.test(state.template_id)
+ ? i18n.str`no valid. only characters and numbers`
+ : undefined,
template_description: !state.template_description
? i18n.str`should not be empty`
: undefined,
@@ -104,15 +102,6 @@ export function CreatePage({ onCreate, onBack }: Props): VNode {
? i18n.str`to short`
: undefined,
} as Partial<MerchantTemplateContractDetails>),
- pos_key: !state.pos_key
- ? !state.pos_algorithm
- ? undefined
- : i18n.str`required`
- : !isBase32RFC3548Charset(state.pos_key)
- ? i18n.str`just letters and numbers from 2 to 7`
- : state.pos_key.length !== 32
- ? i18n.str`size of the key should be 32`
- : undefined,
};
const hasErrors = Object.keys(errors).some(
@@ -124,7 +113,7 @@ export function CreatePage({ onCreate, onBack }: Props): VNode {
return onCreate(state as any);
};
- const qrText = `otpauth://totp/${instanceId}/${state.template_id}?issuer=${issuer}&algorithm=SHA1&digits=8&period=30&secret=${state.pos_key}`;
+ const deviceList = !devices.ok ? [] : devices.data.otp_devices
return (
<div>
@@ -139,7 +128,7 @@ export function CreatePage({ onCreate, onBack }: Props): VNode {
>
<InputWithAddon<Entity>
name="template_id"
- help={`${backend.url}/instances/templates/${state.template_id ?? ""}`}
+ help={`${backend.url}/templates/${state.template_id ?? ""}`}
label={i18n.str`Identifier`}
tooltip={i18n.str`Name of the template in URLs.`}
/>
@@ -172,83 +161,21 @@ export function CreatePage({ onCreate, onBack }: Props): VNode {
help=""
tooltip={i18n.str`How much time has the customer to complete the payment once the order was created.`}
/>
- <InputSelector<Entity>
- name="pos_algorithm"
- label={i18n.str`Verification algorithm`}
- tooltip={i18n.str`Algorithm to use to verify transaction in offline mode`}
- values={algorithms}
- toStr={(v) => algorithmsNames[v]}
- fromStr={(v) => Number(v)}
+ <Input<Entity>
+ name="otp_id"
+ label={i18n.str`OTP device`}
+ readonly
+ tooltip={i18n.str`Use to verify transaction in offline mode.`}
+ />
+ <InputSearchOnList
+ label={i18n.str`Search device`}
+ onChange={(p) => setState((v) => ({ ...v, otp_id: p?.id }))}
+ list={deviceList.map(e => ({
+ description: e.device_description,
+ id: e.otp_device_id
+ }))}
/>
- {state.pos_algorithm && state.pos_algorithm > 0 ? (
- <Fragment>
- <InputWithAddon<Entity>
- name="pos_key"
- label={i18n.str`Point-of-sale key`}
- inputType={showKey ? "text" : "password"}
- help="Be sure to be very hard to guess or use the random generator"
- tooltip={i18n.str`Useful to validate the purchase`}
- fromStr={(v) => v.toUpperCase()}
- addonAfter={
- <span class="icon">
- {showKey ? (
- <i class="mdi mdi-eye" />
- ) : (
- <i class="mdi mdi-eye-off" />
- )}
- </span>
- }
- side={
- <span style={{ display: "flex" }}>
- <button
- data-tooltip={i18n.str`generate random secret key`}
- class="button is-info mr-3"
- onClick={(e) => {
- const pos_key = randomBase32Key();
- setState((s) => ({ ...s, pos_key }));
- }}
- >
- <i18n.Translate>random</i18n.Translate>
- </button>
- <button
- data-tooltip={
- showKey
- ? i18n.str`show secret key`
- : i18n.str`hide secret key`
- }
- class="button is-info mr-3"
- onClick={(e) => {
- setShowKey(!showKey);
- }}
- >
- {showKey ? (
- <i18n.Translate>hide</i18n.Translate>
- ) : (
- <i18n.Translate>show</i18n.Translate>
- )}
- </button>
- </span>
- }
- />
- {showKey && (
- <Fragment>
- <QR text={qrText} />
- <div
- style={{
- color: "grey",
- fontSize: "small",
- width: 200,
- textAlign: "center",
- margin: "auto",
- wordBreak: "break-all",
- }}
- >
- {qrText}
- </div>
- </Fragment>
- )}
- </Fragment>
- ) : undefined}
+
</FormProvider>
<div class="buttons is-right mt-5">
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/templates/list/index.tsx b/packages/merchant-backoffice-ui/src/paths/instance/templates/list/index.tsx
index 2f91298bf..3c9bb231c 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/templates/list/index.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/templates/list/index.tsx
@@ -36,6 +36,7 @@ import {
import { Notification } from "../../../../utils/types.js";
import { ListPage } from "./ListPage.js";
import { HttpStatusCode } from "@gnu-taler/taler-util";
+import { ConfirmModal } from "../../../../components/modal/index.js";
interface Props {
onUnauthorized: () => VNode;
@@ -61,6 +62,8 @@ export default function ListTemplates({
const [notif, setNotif] = useState<Notification | undefined>(undefined);
const { deleteTemplate } = useTemplateAPI();
const result = useInstanceTemplates({ position }, (id) => setPosition(id));
+ const [deleting, setDeleting] =
+ useState<MerchantBackend.Template.TemplateEntry | null>(null);
if (result.loading) return <Loading />;
if (!result.ok) {
@@ -97,23 +100,45 @@ export default function ListTemplates({
onQR={(e) => {
onQR(e.template_id);
}}
- onDelete={(e: MerchantBackend.Template.TemplateEntry) =>
- deleteTemplate(e.template_id)
- .then(() =>
+ onDelete={(e: MerchantBackend.Template.TemplateEntry) => {
+ setDeleting(e)
+ }
+ }
+ />
+
+ {deleting && (
+ <ConfirmModal
+ label={`Delete template`}
+ description={`Delete the template "${deleting.template_description}"`}
+ danger
+ active
+ onCancel={() => setDeleting(null)}
+ onConfirm={async (): Promise<void> => {
+ try {
+ await deleteTemplate(deleting.template_id);
setNotif({
- message: i18n.str`template delete successfully`,
+ message: i18n.str`Template "${deleting.template_description}" (ID: ${deleting.template_id}) has been deleted`,
type: "SUCCESS",
- }),
- )
- .catch((error) =>
+ });
+ } catch (error) {
setNotif({
- message: i18n.str`could not delete the template`,
+ message: i18n.str`Failed to delete template`,
type: "ERROR",
- description: error.message,
- }),
- )
- }
- />
+ description: error instanceof Error ? error.message : undefined,
+ });
+ }
+ setDeleting(null);
+ }}
+ >
+ <p>
+ If you delete the template <b>&quot;{deleting.template_description}&quot;</b> (ID:{" "}
+ <b>{deleting.template_id}</b>) you may loose information
+ </p>
+ <p class="warning">
+ Deleting an template <b>cannot be undone</b>.
+ </p>
+ </ConfirmModal>
+ )}
</Fragment>
);
}
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 0f30efafd..c65cf6a19 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
@@ -19,7 +19,7 @@
* @author Sebastian Javier Marchano (sebasjm)
*/
-import { useTranslationContext } from "@gnu-taler/web-util/browser";
+import { HttpError, useTranslationContext } from "@gnu-taler/web-util/browser";
import { h, VNode } from "preact";
import { useState } from "preact/hooks";
import { QR } from "../../../../components/exception/QR.js";
@@ -35,35 +35,32 @@ import { useConfigContext } from "../../../../context/config.js";
import { useInstanceContext } from "../../../../context/instance.js";
import { MerchantBackend } from "../../../../declaration.js";
import { stringifyPayTemplateUri } from "@gnu-taler/taler-util";
+import { useOtpDeviceDetails } from "../../../../hooks/otp.js";
+import { Loading } from "../../../../components/exception/loading.js";
type Entity = MerchantBackend.Template.UsingTemplateDetails;
interface Props {
- template: MerchantBackend.Template.TemplateDetails;
+ contract: MerchantBackend.Template.TemplateContractDetails;
id: string;
onBack?: () => void;
}
-export function QrPage({ template, id: templateId, onBack }: Props): VNode {
+export function QrPage({ contract, id: templateId, onBack }: Props): VNode {
const { i18n } = useTranslationContext();
const { url: backendUrl } = useBackendContext();
const { id: instanceId } = useInstanceContext();
const config = useConfigContext();
- const [setupTOTP, setSetupTOTP] = useState(false);
const [state, setState] = useState<Partial<Entity>>({
- amount: template.template_contract.amount,
- summary: template.template_contract.summary,
+ amount: contract.amount,
+ summary: contract.summary,
});
const errors: FormErrors<Entity> = {};
- const hasErrors = Object.keys(errors).some(
- (k) => (errors as any)[k] !== undefined,
- );
-
- const fixedAmount = !!template.template_contract.amount;
- const fixedSummary = !!template.template_contract.summary;
+ const fixedAmount = !!contract.amount;
+ const fixedSummary = !!contract.summary;
const templateParams: Record<string, string> = {}
if (!fixedAmount) {
@@ -89,40 +86,9 @@ export function QrPage({ template, id: templateId, onBack }: Props): VNode {
const issuer = encodeURIComponent(
`${new URL(backendUrl).host}/${instanceId}`,
);
- const oauthUri = !template.pos_algorithm
- ? undefined
- : template.pos_algorithm === 1
- ? `otpauth://totp/${issuer}:${templateId}?secret=${template.pos_key}&issuer=${issuer}&algorithm=SHA1&digits=8&period=30`
- : template.pos_algorithm === 2
- ? `otpauth://totp/${issuer}:${templateId}?secret=${template.pos_key}&issuer=${issuer}&algorithm=SHA1&digits=8&period=30`
- : undefined;
-
- const keySlice = template.pos_key?.substring(0, 4);
-
- const oauthUriWithoutSecret = !template.pos_algorithm
- ? undefined
- : template.pos_algorithm === 1
- ? `otpauth://totp/${issuer}:${templateId}?secret=${keySlice}...&issuer=${issuer}&algorithm=SHA1&digits=8&period=30`
- : template.pos_algorithm === 2
- ? `otpauth://totp/${issuer}:${templateId}?secret=${keySlice}...&issuer=${issuer}&algorithm=SHA1&digits=8&period=30`
- : undefined;
+
return (
<div>
- {oauthUri && (
- <ConfirmModal
- description="Setup TOTP"
- active={setupTOTP}
- onCancel={() => {
- setSetupTOTP(false);
- }}
- >
- <p>Scan this qr code with your TOTP device</p>
- <QR text={oauthUri} />
- <pre style={{ textAlign: "center" }}>
- <a href={oauthUri}>{oauthUriWithoutSecret}</a>
- </pre>
- </ConfirmModal>
- )}
<section class="section is-main-section">
<div class="columns">
<div class="column" />
@@ -176,14 +142,6 @@ export function QrPage({ template, id: templateId, onBack }: Props): VNode {
>
<i18n.Translate>Print</i18n.Translate>
</button>
- {oauthUri && (
- <button
- class="button is-info"
- onClick={() => setSetupTOTP(true)}
- >
- <i18n.Translate>Setup TOTP</i18n.Translate>
- </button>
- )}
</div>
</div>
<div class="column" />
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/templates/qr/index.tsx b/packages/merchant-backoffice-ui/src/paths/instance/templates/qr/index.tsx
index 1f74afc2b..7db7478f7 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/templates/qr/index.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/templates/qr/index.tsx
@@ -74,7 +74,7 @@ export default function TemplateQrPage({
return (
<>
<NotificationCard notification={notif} />
- <QrPage template={result.data} id={tid} onBack={onBack} />
+ <QrPage contract={result.data.template_contract} id={tid} onBack={onBack} />
</>
);
}
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 30e5502bb..30d47385c 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
@@ -61,10 +61,7 @@ const algorithmsNames = ["off", "30s 8d TOTP-SHA1", "30s 8d eTOTP-SHA1"];
export function UpdatePage({ template, onUpdate, onBack }: Props): VNode {
const { i18n } = useTranslationContext();
const backend = useBackendContext();
- const { id: instanceId } = useInstanceContext();
- const issuer = new URL(backend.url).hostname;
- const [showKey, setShowKey] = useState(false);
const [state, setState] = useState<Partial<Entity>>(template);
const parsedPrice = !state.template_contract?.amount
@@ -78,34 +75,25 @@ export function UpdatePage({ template, onUpdate, onBack }: Props): VNode {
template_contract: !state.template_contract
? undefined
: undefinedIfEmpty({
- amount: !state.template_contract?.amount
- ? undefined
- : !parsedPrice
+ amount: !state.template_contract?.amount
+ ? undefined
+ : !parsedPrice
? i18n.str`not valid`
: Amounts.isZero(parsedPrice)
- ? i18n.str`must be greater than 0`
- : undefined,
- minimum_age:
- state.template_contract.minimum_age < 0
- ? i18n.str`should be greater that 0`
+ ? i18n.str`must be greater than 0`
: undefined,
- pay_duration: !state.template_contract.pay_duration
- ? i18n.str`can't be empty`
- : state.template_contract.pay_duration.d_us === "forever"
+ 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"
? undefined
: state.template_contract.pay_duration.d_us < 1000 * 1000 // less than one second
- ? i18n.str`to short`
- : undefined,
- } as Partial<MerchantTemplateContractDetails>),
- pos_key: !state.pos_key
- ? !state.pos_algorithm
- ? undefined
- : i18n.str`required`
- : !isBase32RFC3548Charset(state.pos_key)
- ? i18n.str`just letters and numbers from 2 to 7`
- : state.pos_key.length !== 32
- ? i18n.str`size of the key should be 32`
- : undefined,
+ ? i18n.str`to short`
+ : undefined,
+ } as Partial<MerchantTemplateContractDetails>),
};
const hasErrors = Object.keys(errors).some(
@@ -117,7 +105,6 @@ export function UpdatePage({ template, onUpdate, onBack }: Props): VNode {
return onUpdate(state as any);
};
- const qrText = `otpauth://totp/${instanceId}/${state.id}?issuer=${issuer}&algorithm=SHA1&digits=8&period=30&secret=${state.pos_key}`;
return (
<div>
@@ -128,7 +115,7 @@ export function UpdatePage({ template, onUpdate, onBack }: Props): VNode {
<div class="level-left">
<div class="level-item">
<span class="is-size-4">
- {backend.url}/instances/template/{template.id}
+ {backend.url}/templates/{template.id}
</span>
</div>
</div>
@@ -182,84 +169,6 @@ export function UpdatePage({ template, onUpdate, onBack }: Props): VNode {
help=""
tooltip={i18n.str`How much time has the customer to complete the payment once the order was created.`}
/>
- <InputSelector<Entity>
- name="pos_algorithm"
- label={i18n.str`Verification algorithm`}
- tooltip={i18n.str`Algorithm to use to verify transaction in offline mode`}
- values={algorithms}
- toStr={(v) => algorithmsNames[v]}
- fromStr={(v) => Number(v)}
- />
- {state.pos_algorithm && state.pos_algorithm > 0 ? (
- <Fragment>
- <InputWithAddon<Entity>
- name="pos_key"
- label={i18n.str`Point-of-sale key`}
- inputType={showKey ? "text" : "password"}
- help="Be sure to be very hard to guess or use the random generator"
- expand
- tooltip={i18n.str`Useful to validate the purchase`}
- fromStr={(v) => v.toUpperCase()}
- addonAfter={
- <span class="icon">
- {showKey ? (
- <i class="mdi mdi-eye" />
- ) : (
- <i class="mdi mdi-eye-off" />
- )}
- </span>
- }
- side={
- <span style={{ display: "flex" }}>
- <button
- data-tooltip={i18n.str`generate random secret key`}
- class="button is-info mr-3"
- onClick={(e) => {
- const pos_key = randomBase32Key();
- setState((s) => ({ ...s, pos_key }));
- }}
- >
- <i18n.Translate>random</i18n.Translate>
- </button>
- <button
- data-tooltip={
- showKey
- ? i18n.str`show secret key`
- : i18n.str`hide secret key`
- }
- class="button is-info mr-3"
- onClick={(e) => {
- setShowKey(!showKey);
- }}
- >
- {showKey ? (
- <i18n.Translate>hide</i18n.Translate>
- ) : (
- <i18n.Translate>show</i18n.Translate>
- )}
- </button>
- </span>
- }
- />
- {showKey && (
- <Fragment>
- <QR text={qrText} />
- <div
- style={{
- color: "grey",
- fontSize: "small",
- width: 200,
- textAlign: "center",
- margin: "auto",
- wordBreak: "break-all",
- }}
- >
- {qrText}
- </div>
- </Fragment>
- )}
- </Fragment>
- ) : undefined}
</FormProvider>
<div class="buttons is-right mt-5">
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/token/DetailPage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/token/DetailPage.tsx
new file mode 100644
index 000000000..984880752
--- /dev/null
+++ b/packages/merchant-backoffice-ui/src/paths/instance/token/DetailPage.tsx
@@ -0,0 +1,165 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021-2023 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/>
+ */
+
+/**
+ *
+ * @author Sebastian Javier Marchano (sebasjm)
+ */
+
+import { useTranslationContext } from "@gnu-taler/web-util/browser";
+import { Fragment, h, VNode } from "preact";
+import { useState } from "preact/hooks";
+import { AsyncButton } from "../../../components/exception/AsyncButton.js";
+import { FormProvider } from "../../../components/form/FormProvider.js";
+import { Input } from "../../../components/form/Input.js";
+import { useInstanceContext } from "../../../context/instance.js";
+
+interface Props {
+ instanceId: string;
+ currentToken: string | undefined;
+ onClearToken: () => void;
+ onNewToken: (s: string) => void;
+ onBack?: () => void;
+}
+
+export function DetailPage({ instanceId, currentToken: oldToken, onBack, onNewToken, onClearToken }: Props): VNode {
+ type State = { old_token: string; new_token: string; repeat_token: string };
+ const [form, setValue] = useState<Partial<State>>({
+ old_token: "",
+ new_token: "",
+ repeat_token: "",
+ });
+ const { i18n } = useTranslationContext();
+
+ const hasOldtoken = !!oldToken
+ const hasInputTheCorrectOldToken = hasOldtoken && oldToken !== form.old_token;
+ const errors = {
+ old_token: hasInputTheCorrectOldToken
+ ? i18n.str`is not the same as the current access token`
+ : undefined,
+ new_token: !form.new_token
+ ? i18n.str`cannot be empty`
+ : form.new_token === form.old_token
+ ? i18n.str`cannot be the same as the old token`
+ : undefined,
+ repeat_token:
+ form.new_token !== form.repeat_token
+ ? i18n.str`is not the same`
+ : undefined,
+ };
+
+ const hasErrors = Object.keys(errors).some(
+ (k) => (errors as any)[k] !== undefined,
+ );
+
+ const instance = useInstanceContext();
+
+ const text = i18n.str`You are updating the access token from instance with id ${instance.id}`;
+
+ async function submitForm() {
+ if (hasErrors) return;
+ onNewToken(form.new_token as any)
+ }
+
+ return (
+ <div>
+ <section class="section">
+ <section class="hero is-hero-bar">
+ <div class="hero-body">
+ <div class="level">
+ <div class="level-left">
+ <div class="level-item">
+ <span class="is-size-4">
+ Instance id: <b>{instanceId}</b>
+ </span>
+ </div>
+ </div>
+ </div>
+ </div>
+ </section>
+ <hr />
+
+ <div class="columns">
+ <div class="column" />
+ <div class="column is-four-fifths">
+ <FormProvider errors={errors} object={form} valueHandler={setValue}>
+ {hasOldtoken && (
+ <Input<State>
+ name="old_token"
+ label={i18n.str`Current access token`}
+ tooltip={i18n.str`access token currently in use`}
+ inputType="password"
+ />
+ )}
+ {!hasInputTheCorrectOldToken && <Fragment>
+ {hasOldtoken && <Fragment>
+ <p>
+ <i18n.Translate>
+ Clearing the access token will mean public access to the instance.
+ </i18n.Translate>
+ </p>
+ <div class="buttons is-right mt-5">
+ <button
+ disabled={!!hasInputTheCorrectOldToken}
+ class="button"
+ onClick={onClearToken}
+ >
+ <i18n.Translate>Clear token</i18n.Translate>
+ </button>
+ </div>
+ </Fragment>
+ }
+
+ <Input<State>
+ name="new_token"
+ label={i18n.str`New access token`}
+ tooltip={i18n.str`next access token to be used`}
+ inputType="password"
+ />
+ <Input<State>
+ name="repeat_token"
+ label={i18n.str`Repeat access token`}
+ tooltip={i18n.str`confirm the same access token`}
+ inputType="password"
+ />
+ </Fragment>}
+ </FormProvider>
+ <div class="buttons is-right mt-5">
+ {onBack && (
+ <button class="button" onClick={onBack}>
+ <i18n.Translate>Cancel</i18n.Translate>
+ </button>
+ )}
+ <AsyncButton
+ disabled={hasErrors}
+ data-tooltip={
+ hasErrors
+ ? i18n.str`Need to complete marked fields`
+ : "confirm operation"
+ }
+ onClick={submitForm}
+ >
+ <i18n.Translate>Confirm change</i18n.Translate>
+ </AsyncButton>
+ </div>
+ </div>
+ <div class="column" />
+ </div>
+
+ </section>
+ </div>
+ );
+}
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/token/index.tsx b/packages/merchant-backoffice-ui/src/paths/instance/token/index.tsx
new file mode 100644
index 000000000..d5910361b
--- /dev/null
+++ b/packages/merchant-backoffice-ui/src/paths/instance/token/index.tsx
@@ -0,0 +1,90 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021-2023 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/>
+ */
+import { HttpStatusCode } from "@gnu-taler/taler-util";
+import { ErrorType, HttpError, useTranslationContext } from "@gnu-taler/web-util/browser";
+import { Fragment, VNode, h } from "preact";
+import { Loading } from "../../../components/exception/loading.js";
+import { MerchantBackend } from "../../../declaration.js";
+import { useInstanceAPI, useInstanceDetails } from "../../../hooks/instance.js";
+import { DetailPage } from "./DetailPage.js";
+import { useInstanceContext } from "../../../context/instance.js";
+import { useState } from "preact/hooks";
+import { NotificationCard } from "../../../components/menu/index.js";
+import { Notification } from "../../../utils/types.js";
+import { useBackendContext } from "../../../context/backend.js";
+
+interface Props {
+ onUnauthorized: () => VNode;
+ onLoadError: (error: HttpError<MerchantBackend.ErrorDetail>) => VNode;
+ onChange: () => void;
+ onNotFound: () => VNode;
+}
+
+const PREFIX = "secret-token:"
+
+export default function Token({
+ onLoadError,
+ onChange,
+ onUnauthorized,
+ onNotFound,
+}: Props): VNode {
+ const { i18n } = useTranslationContext();
+
+ const [notif, setNotif] = useState<Notification | undefined>(undefined);
+ const { clearToken, setNewToken } = useInstanceAPI();
+ const { token: rootToken } = useBackendContext();
+ const { token: instanceToken, id, admin } = useInstanceContext();
+
+ const currentToken = !admin ? rootToken : instanceToken
+ const hasPrefix = currentToken !== undefined && currentToken.startsWith(PREFIX)
+ return (
+ <Fragment>
+ <NotificationCard notification={notif} />
+ <DetailPage
+ instanceId={id}
+ currentToken={hasPrefix ? currentToken.substring(PREFIX.length) : currentToken}
+ onClearToken={async (): Promise<void> => {
+ try {
+ await clearToken();
+ onChange();
+ } catch (error) {
+ if (error instanceof Error) {
+ setNotif({
+ message: i18n.str`Failed to clear token`,
+ type: "ERROR",
+ description: error.message,
+ });
+ }
+ }
+ }}
+ onNewToken={async (newToken): Promise<void> => {
+ try {
+ await setNewToken(`secret-token:${newToken}`);
+ onChange();
+ } catch (error) {
+ if (error instanceof Error) {
+ setNotif({
+ message: i18n.str`Failed to set new token`,
+ type: "ERROR",
+ description: error.message,
+ });
+ }
+ }
+ }}
+ />
+ </Fragment>
+ );
+}
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/token/stories.tsx b/packages/merchant-backoffice-ui/src/paths/instance/token/stories.tsx
new file mode 100644
index 000000000..5f0f56f2d
--- /dev/null
+++ b/packages/merchant-backoffice-ui/src/paths/instance/token/stories.tsx
@@ -0,0 +1,28 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021-2023 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/>
+ */
+
+/**
+ *
+ * @author Sebastian Javier Marchano (sebasjm)
+ */
+
+import { DetailPage as TestedComponent } from "./DetailPage.js";
+
+export default {
+ title: "Pages/Token",
+ component: TestedComponent,
+};
+
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/transfers/create/index.tsx b/packages/merchant-backoffice-ui/src/paths/instance/transfers/create/index.tsx
index f218f4ead..25551a031 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/transfers/create/index.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/transfers/create/index.tsx
@@ -28,6 +28,7 @@ import { useInstanceDetails } from "../../../../hooks/instance.js";
import { useTransferAPI } from "../../../../hooks/transfer.js";
import { Notification } from "../../../../utils/types.js";
import { CreatePage } from "./CreatePage.js";
+import { useBankAccountDetails, useInstanceBankAccounts } from "../../../../hooks/bank.js";
export type Entity = MerchantBackend.Transfers.TransferInformation;
interface Props {
@@ -39,7 +40,7 @@ export default function CreateTransfer({ onConfirm, onBack }: Props): VNode {
const { informTransfer } = useTransferAPI();
const [notif, setNotif] = useState<Notification | undefined>(undefined);
const { i18n } = useTranslationContext();
- const instance = useInstanceDetails();
+ const instance = useInstanceBankAccounts();
const accounts = !instance.ok
? []
: instance.data.accounts.map((a) => a.payto_uri);
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/transfers/list/Table.tsx b/packages/merchant-backoffice-ui/src/paths/instance/transfers/list/Table.tsx
index a2e93d598..1c464cbc7 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/transfers/list/Table.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/transfers/list/Table.tsx
@@ -24,6 +24,7 @@ import { format } from "date-fns";
import { h, VNode } from "preact";
import { StateUpdater, useState } from "preact/hooks";
import { MerchantBackend, WithId } from "../../../../declaration.js";
+import { datetimeFormatForSettings, useSettings } from "../../../../hooks/useSettings.js";
type Entity = MerchantBackend.Transfers.TransferDetails & WithId;
@@ -56,7 +57,7 @@ export function CardTable({
<header class="card-header">
<p class="card-header-title">
<span class="icon">
- <i class="mdi mdi-bank" />
+ <i class="mdi mdi-arrow-left-right" />
</span>
<i18n.Translate>Transfers</i18n.Translate>
</p>
@@ -121,6 +122,7 @@ function Table({
hasMoreBefore,
}: TableProps): VNode {
const { i18n } = useTranslationContext();
+ const [settings] = useSettings();
return (
<div class="table-container">
{onLoadMoreBefore && (
@@ -175,9 +177,9 @@ function Table({
? i.execution_time.t_s == "never"
? i18n.str`never`
: format(
- i.execution_time.t_s * 1000,
- "yyyy/MM/dd HH:mm:ss",
- )
+ i.execution_time.t_s * 1000,
+ datetimeFormatForSettings(settings),
+ )
: i18n.str`unknown`}
</td>
<td>
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/transfers/list/index.tsx b/packages/merchant-backoffice-ui/src/paths/instance/transfers/list/index.tsx
index 29e860342..1bc1673ba 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/transfers/list/index.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/transfers/list/index.tsx
@@ -28,6 +28,7 @@ import { useInstanceDetails } from "../../../../hooks/instance.js";
import { useInstanceTransfers } from "../../../../hooks/transfer.js";
import { ListPage } from "./ListPage.js";
import { HttpStatusCode } from "@gnu-taler/taler-util";
+import { useInstanceBankAccounts } from "../../../../hooks/bank.js";
interface Props {
onUnauthorized: () => VNode;
@@ -51,7 +52,7 @@ export default function ListTransfer({
const [position, setPosition] = useState<string | undefined>(undefined);
- const instance = useInstanceDetails();
+ const instance = useInstanceBankAccounts();
const accounts = !instance.ok
? []
: instance.data.accounts.map((a) => a.payto_uri);
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/update/Update.stories.tsx b/packages/merchant-backoffice-ui/src/paths/instance/update/Update.stories.tsx
index 045c96c2c..817a7025c 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/update/Update.stories.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/update/Update.stories.tsx
@@ -42,17 +42,15 @@ function createExample<Props>(
export const Example = createExample(TestedComponent, {
selected: {
- accounts: [],
name: "name",
auth: { method: "external" },
address: {},
+ user_type: "business",
+ use_stefan: true,
jurisdiction: {},
- default_max_deposit_fee: "TESTKUDOS:2",
- default_max_wire_fee: "TESTKUDOS:1",
default_pay_delay: {
d_us: 1000 * 1000, //one second
},
- default_wire_fee_amortization: 1,
default_wire_transfer_delay: {
d_us: 1000 * 1000, //one second
},
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/update/UpdatePage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/update/UpdatePage.tsx
index 547b40f07..a1c608f15 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/update/UpdatePage.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/update/UpdatePage.tsx
@@ -19,7 +19,6 @@
* @author Sebastian Javier Marchano (sebasjm)
*/
-import { Amounts } from "@gnu-taler/taler-util";
import { useTranslationContext } from "@gnu-taler/web-util/browser";
import { h, VNode } from "preact";
import { useState } from "preact/hooks";
@@ -29,10 +28,8 @@ import {
FormProvider,
} from "../../../components/form/FormProvider.js";
import { DefaultInstanceFormFields } from "../../../components/instance/DefaultInstanceFormFields.js";
-import { UpdateTokenModal } from "../../../components/modal/index.js";
import { useInstanceContext } from "../../../context/instance.js";
import { MerchantBackend } from "../../../declaration.js";
-import { PAYTO_REGEX } from "../../../utils/constants.js";
import { undefinedIfEmpty } from "../../../utils/table.js";
type Entity = MerchantBackend.Instances.InstanceReconfigurationMessage & {
@@ -53,23 +50,23 @@ interface Props {
function convert(
from: MerchantBackend.Instances.QueryInstancesResponse,
): Entity {
- const { accounts: qAccounts, ...rest } = from;
- const accounts = qAccounts
- .filter((a) => a.active)
- .map(
- (a) =>
- ({
- payto_uri: a.payto_uri,
- credit_facade_url: a.credit_facade_url,
- credit_facade_credentials: a.credit_facade_credentials,
- } as MerchantBackend.Instances.MerchantBankAccount),
- );
+ const { ...rest } = from;
+ // const accounts = qAccounts
+ // .filter((a) => a.active)
+ // .map(
+ // (a) =>
+ // ({
+ // payto_uri: a.payto_uri,
+ // credit_facade_url: a.credit_facade_url,
+ // credit_facade_credentials: a.credit_facade_credentials,
+ // } as MerchantBackend.Instances.MerchantBankAccount),
+ // );
const defaults = {
- default_wire_fee_amortization: 1,
+ use_stefan: false,
default_pay_delay: { d_us: 2 * 1000 * 1000 * 60 * 60 }, //two hours
default_wire_transfer_delay: { d_us: 2 * 1000 * 1000 * 60 * 60 * 2 }, //two hours
};
- return { ...defaults, ...rest, accounts };
+ return { ...defaults, ...rest };
}
function getTokenValuePart(t?: string): string | undefined {
@@ -85,21 +82,21 @@ export function UpdatePage({
selected,
onBack,
}: Props): VNode {
- const { id, token } = useInstanceContext();
- const currentTokenValue = getTokenValuePart(token);
-
- function updateToken(token: string | undefined | null) {
- const value =
- token && token.startsWith("secret-token:")
- ? token.substring("secret-token:".length)
- : token;
-
- if (!token) {
- onChangeAuth({ method: "external" });
- } else {
- onChangeAuth({ method: "token", token: `secret-token:${value}` });
- }
- }
+ const { id } = useInstanceContext();
+ // const currentTokenValue = getTokenValuePart(token);
+
+ // function updateToken(token: string | undefined | null) {
+ // const value =
+ // token && token.startsWith("secret-token:")
+ // ? token.substring("secret-token:".length)
+ // : token;
+
+ // if (!token) {
+ // onChangeAuth({ method: "external" });
+ // } else {
+ // onChangeAuth({ method: "token", token: `secret-token:${value}` });
+ // }
+ // }
const [value, valueHandler] = useState<Partial<Entity>>(convert(selected));
@@ -110,35 +107,7 @@ export function UpdatePage({
user_type: !value.user_type
? i18n.str`required`
: value.user_type !== "business" && value.user_type !== "individual"
- ? i18n.str`should be business or individual`
- : undefined,
- accounts:
- !value.accounts || !value.accounts.length
- ? i18n.str`required`
- : undefinedIfEmpty(
- value.accounts.map((p) => {
- return !PAYTO_REGEX.test(p.payto_uri)
- ? i18n.str`is not valid`
- : undefined;
- }),
- ),
- default_max_deposit_fee: !value.default_max_deposit_fee
- ? i18n.str`required`
- : !Amounts.parse(value.default_max_deposit_fee)
- ? i18n.str`invalid format`
- : undefined,
- default_max_wire_fee: !value.default_max_wire_fee
- ? i18n.str`required`
- : !Amounts.parse(value.default_max_wire_fee)
- ? i18n.str`invalid format`
- : undefined,
- default_wire_fee_amortization:
- value.default_wire_fee_amortization === undefined
- ? i18n.str`required`
- : isNaN(value.default_wire_fee_amortization)
- ? i18n.str`is not a number`
- : value.default_wire_fee_amortization < 1
- ? i18n.str`must be 1 or greater`
+ ? i18n.str`should be business or individual`
: undefined,
default_pay_delay: !value.default_pay_delay
? i18n.str`required`
@@ -163,10 +132,11 @@ export function UpdatePage({
const hasErrors = Object.keys(errors).some(
(k) => (errors as any)[k] !== undefined,
);
+
const submit = async (): Promise<void> => {
await onUpdate(value as Entity);
};
- const [active, setActive] = useState(false);
+ // const [active, setActive] = useState(false);
return (
<div>
@@ -181,7 +151,7 @@ export function UpdatePage({
</span>
</div>
</div>
- <div class="level-right">
+ {/* <div class="level-right">
<div class="level-item">
<h1 class="title">
<button
@@ -200,33 +170,11 @@ export function UpdatePage({
</button>
</h1>
</div>
- </div>
+ </div> */}
</div>
</div>
</section>
- <div class="columns">
- <div class="column" />
- <div class="column is-four-fifths">
- {active && (
- <UpdateTokenModal
- oldToken={currentTokenValue}
- onCancel={() => {
- setActive(false);
- }}
- onClear={() => {
- updateToken(null);
- setActive(false);
- }}
- onConfirm={(newToken) => {
- updateToken(newToken);
- setActive(false);
- }}
- />
- )}
- </div>
- <div class="column" />
- </div>
<hr />
<div class="columns">
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/validators/create/Create.stories.tsx b/packages/merchant-backoffice-ui/src/paths/instance/validators/create/Create.stories.tsx
new file mode 100644
index 000000000..56762db7b
--- /dev/null
+++ b/packages/merchant-backoffice-ui/src/paths/instance/validators/create/Create.stories.tsx
@@ -0,0 +1,28 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021-2023 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/>
+ */
+
+/**
+ *
+ * @author Sebastian Javier Marchano (sebasjm)
+ */
+
+import { h, VNode, FunctionalComponent } from "preact";
+import { CreatePage as TestedComponent } from "./CreatePage.js";
+
+export default {
+ title: "Pages/Validators/Create",
+ component: TestedComponent,
+};
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/validators/create/CreatePage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/validators/create/CreatePage.tsx
new file mode 100644
index 000000000..bdc86d226
--- /dev/null
+++ b/packages/merchant-backoffice-ui/src/paths/instance/validators/create/CreatePage.tsx
@@ -0,0 +1,195 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021-2023 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/>
+ */
+
+/**
+ *
+ * @author Sebastian Javier Marchano (sebasjm)
+ */
+
+import { useTranslationContext } from "@gnu-taler/web-util/browser";
+import { Fragment, h, VNode } from "preact";
+import { useState } from "preact/hooks";
+import { AsyncButton } from "../../../../components/exception/AsyncButton.js";
+import {
+ FormErrors,
+ FormProvider,
+} from "../../../../components/form/FormProvider.js";
+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 { useBackendContext } from "../../../../context/backend.js";
+import { MerchantBackend } from "../../../../declaration.js";
+import { InputSelector } from "../../../../components/form/InputSelector.js";
+import { InputWithAddon } from "../../../../components/form/InputWithAddon.js";
+import { isBase32RFC3548Charset, randomBase32Key } from "../../../../utils/crypto.js";
+import { QR } from "../../../../components/exception/QR.js";
+import { useInstanceContext } from "../../../../context/instance.js";
+
+type Entity = MerchantBackend.OTP.OtpDeviceAddDetails;
+
+interface Props {
+ onCreate: (d: Entity) => Promise<void>;
+ onBack?: () => void;
+}
+
+const algorithms = [0, 1, 2];
+const algorithmsNames = ["off", "30s 8d TOTP-SHA1", "30s 8d eTOTP-SHA1"];
+
+
+export function CreatePage({ onCreate, onBack }: Props): VNode {
+ const { i18n } = useTranslationContext();
+ const backend = useBackendContext();
+
+ const [state, setState] = useState<Partial<Entity>>({});
+
+ const [showKey, setShowKey] = useState(false);
+
+ const errors: FormErrors<Entity> = {
+ otp_device_id: !state.otp_device_id ? i18n.str`required`
+ : !/[a-zA-Z0-9]*/.test(state.otp_device_id)
+ ? i18n.str`no valid. only characters and numbers`
+ : undefined,
+ otp_algorithm: !state.otp_algorithm ? i18n.str`required` : undefined,
+ otp_key: !state.otp_key ? i18n.str`required` :
+ !isBase32RFC3548Charset(state.otp_key)
+ ? i18n.str`just letters and numbers from 2 to 7`
+ : state.otp_key.length !== 32
+ ? i18n.str`size of the key should be 32`
+ : undefined,
+ otp_description: !state.otp_description ? i18n.str`required`
+ : !/[a-zA-Z0-9]*/.test(state.otp_description)
+ ? i18n.str`no valid. only characters and numbers`
+ : undefined,
+
+ };
+
+ const hasErrors = Object.keys(errors).some(
+ (k) => (errors as any)[k] !== undefined,
+ );
+
+ const submitForm = () => {
+ if (hasErrors) return Promise.reject();
+ return onCreate(state as any);
+ };
+
+ return (
+ <div>
+ <section class="section is-main-section">
+ <div class="columns">
+ <div class="column" />
+ <div class="column is-four-fifths">
+ <FormProvider
+ object={state}
+ valueHandler={setState}
+ errors={errors}
+ >
+ <Input<Entity>
+ name="otp_device_id"
+ label={i18n.str`ID`}
+ tooltip={i18n.str`Internal id on the system`}
+ />
+ <Input<Entity>
+ name="otp_description"
+ label={i18n.str`Descripiton`}
+ tooltip={i18n.str`Useful to identify the device physically`}
+ />
+ <InputSelector<Entity>
+ name="otp_algorithm"
+ label={i18n.str`Verification algorithm`}
+ tooltip={i18n.str`Algorithm to use to verify transaction in offline mode`}
+ values={algorithms}
+ toStr={(v) => algorithmsNames[v]}
+ fromStr={(v) => Number(v)}
+ />
+ {state.otp_algorithm && state.otp_algorithm > 0 ? (
+ <Fragment>
+ <InputWithAddon<Entity>
+ name="otp_key"
+ label={i18n.str`Device key`}
+ inputType={showKey ? "text" : "password"}
+ help="Be sure to be very hard to guess or use the random generator"
+ tooltip={i18n.str`Your device need to have exactly the same value`}
+ fromStr={(v) => v.toUpperCase()}
+ addonAfter={
+ <span class="icon">
+ {showKey ? (
+ <i class="mdi mdi-eye" />
+ ) : (
+ <i class="mdi mdi-eye-off" />
+ )}
+ </span>
+ }
+ side={
+ <span style={{ display: "flex" }}>
+ <button
+ data-tooltip={i18n.str`generate random secret key`}
+ class="button is-info mr-3"
+ onClick={(e) => {
+ setState((s) => ({ ...s, otp_key: randomBase32Key() }));
+ }}
+ >
+ <i18n.Translate>random</i18n.Translate>
+ </button>
+ <button
+ data-tooltip={
+ showKey
+ ? i18n.str`show secret key`
+ : i18n.str`hide secret key`
+ }
+ class="button is-info mr-3"
+ onClick={(e) => {
+ setShowKey(!showKey);
+ }}
+ >
+ {showKey ? (
+ <i18n.Translate>hide</i18n.Translate>
+ ) : (
+ <i18n.Translate>show</i18n.Translate>
+ )}
+ </button>
+ </span>
+ }
+ />
+ </Fragment>
+ ) : undefined}
+ </FormProvider>
+
+ <div class="buttons is-right mt-5">
+ {onBack && (
+ <button class="button" onClick={onBack}>
+ <i18n.Translate>Cancel</i18n.Translate>
+ </button>
+ )}
+ <AsyncButton
+ disabled={hasErrors}
+ data-tooltip={
+ hasErrors
+ ? i18n.str`Need to complete marked fields`
+ : "confirm operation"
+ }
+ onClick={submitForm}
+ >
+ <i18n.Translate>Confirm</i18n.Translate>
+ </AsyncButton>
+ </div>
+ </div>
+ <div class="column" />
+ </div>
+ </section>
+ </div>
+ );
+}
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/validators/create/CreatedSuccessfully.tsx b/packages/merchant-backoffice-ui/src/paths/instance/validators/create/CreatedSuccessfully.tsx
new file mode 100644
index 000000000..3ad3cb3a3
--- /dev/null
+++ b/packages/merchant-backoffice-ui/src/paths/instance/validators/create/CreatedSuccessfully.tsx
@@ -0,0 +1,104 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021-2023 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/>
+ */
+
+import { useTranslationContext } from "@gnu-taler/web-util/browser";
+import { Fragment, VNode, h } from "preact";
+import { QR } from "../../../../components/exception/QR.js";
+import { CreatedSuccessfully as Template } from "../../../../components/notifications/CreatedSuccessfully.js";
+import { useBackendContext } from "../../../../context/backend.js";
+import { useInstanceContext } from "../../../../context/instance.js";
+import { MerchantBackend } from "../../../../declaration.js";
+
+type Entity = MerchantBackend.OTP.OtpDeviceAddDetails;
+
+interface Props {
+ entity: Entity;
+ onConfirm: () => void;
+}
+
+function isNotUndefined<X>(x: X | undefined): x is X {
+ return !!x;
+}
+
+export function CreatedSuccessfully({
+ entity,
+ onConfirm,
+}: Props): VNode {
+ const { i18n } = useTranslationContext();
+ const backend = useBackendContext();
+ const { id: instanceId } = useInstanceContext();
+ const issuer = new URL(backend.url).hostname;
+ const qrText = `otpauth://totp/${instanceId}/${entity.otp_device_id}?issuer=${issuer}&algorithm=SHA1&digits=8&period=30&secret=${entity.otp_key}`;
+ const qrTextSafe = `otpauth://totp/${instanceId}/${entity.otp_device_id}?issuer=${issuer}&algorithm=SHA1&digits=8&period=30&secret=${entity.otp_key.substring(0, 6)}...`;
+
+ return (
+ <Template onConfirm={onConfirm} >
+ <p class="is-size-5">
+ <i18n.Translate>
+ You can scan the next QR code with your device or safe the key before continue.
+ </i18n.Translate>
+ </p>
+ <div class="field is-horizontal">
+ <div class="field-label is-normal">
+ <label class="label">ID</label>
+ </div>
+ <div class="field-body is-flex-grow-3">
+ <div class="field">
+ <p class="control">
+ <input
+ readonly
+ class="input"
+ value={entity.otp_device_id}
+ />
+ </p>
+ </div>
+ </div>
+ </div>
+ <div class="field is-horizontal">
+ <div class="field-label is-normal">
+ <label class="label"><i18n.Translate>Description</i18n.Translate></label>
+ </div>
+ <div class="field-body is-flex-grow-3">
+ <div class="field">
+ <p class="control">
+ <input
+ class="input"
+ readonly
+ value={entity.otp_description}
+ />
+ </p>
+ </div>
+ </div>
+ </div>
+ <QR
+ text={qrText}
+ />
+ <div
+ style={{
+ color: "grey",
+ fontSize: "small",
+ width: 200,
+ textAlign: "center",
+ margin: "auto",
+ wordBreak: "break-all",
+ }}
+ >
+ {qrTextSafe}
+ </div>
+ </Template>
+ );
+}
+
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/validators/create/index.tsx b/packages/merchant-backoffice-ui/src/paths/instance/validators/create/index.tsx
new file mode 100644
index 000000000..648846793
--- /dev/null
+++ b/packages/merchant-backoffice-ui/src/paths/instance/validators/create/index.tsx
@@ -0,0 +1,70 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021-2023 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/>
+ */
+
+/**
+ *
+ * @author Sebastian Javier Marchano (sebasjm)
+ */
+
+import { useTranslationContext } from "@gnu-taler/web-util/browser";
+import { Fragment, h, VNode } from "preact";
+import { useState } from "preact/hooks";
+import { NotificationCard } from "../../../../components/menu/index.js";
+import { MerchantBackend } from "../../../../declaration.js";
+import { useWebhookAPI } from "../../../../hooks/webhooks.js";
+import { Notification } from "../../../../utils/types.js";
+import { CreatePage } from "./CreatePage.js";
+import { useOtpDeviceAPI } from "../../../../hooks/otp.js";
+import { CreatedSuccessfully } from "./CreatedSuccessfully.js";
+
+export type Entity = MerchantBackend.OTP.OtpDeviceAddDetails;
+interface Props {
+ onBack?: () => void;
+ onConfirm: () => void;
+}
+
+export default function CreateValidator({ onConfirm, onBack }: Props): VNode {
+ const { createOtpDevice } = useOtpDeviceAPI();
+ const [notif, setNotif] = useState<Notification | undefined>(undefined);
+ const { i18n } = useTranslationContext();
+ const [created, setCreated] = useState<MerchantBackend.OTP.OtpDeviceAddDetails | null>(null)
+
+ if (created) {
+ return <CreatedSuccessfully entity={created} onConfirm={onConfirm} />
+ }
+
+ return (
+ <>
+ <NotificationCard notification={notif} />
+ <CreatePage
+ onBack={onBack}
+ onCreate={(request: Entity) => {
+ return createOtpDevice(request)
+ .then((d) => {
+ setCreated(request)
+ })
+ .catch((error) => {
+ setNotif({
+ message: i18n.str`could not create device`,
+ type: "ERROR",
+ description: error.message,
+ });
+ });
+ }}
+ />
+ </>
+ );
+}
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/validators/list/List.stories.tsx b/packages/merchant-backoffice-ui/src/paths/instance/validators/list/List.stories.tsx
new file mode 100644
index 000000000..3aa491c53
--- /dev/null
+++ b/packages/merchant-backoffice-ui/src/paths/instance/validators/list/List.stories.tsx
@@ -0,0 +1,28 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021-2023 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/>
+ */
+
+/**
+ *
+ * @author Sebastian Javier Marchano (sebasjm)
+ */
+
+import { FunctionalComponent, h } from "preact";
+import { ListPage as TestedComponent } from "./ListPage.js";
+
+export default {
+ title: "Pages/Validators/List",
+ component: TestedComponent,
+};
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/validators/list/ListPage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/validators/list/ListPage.tsx
new file mode 100644
index 000000000..4efee9781
--- /dev/null
+++ b/packages/merchant-backoffice-ui/src/paths/instance/validators/list/ListPage.tsx
@@ -0,0 +1,64 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021-2023 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/>
+ */
+
+/**
+ *
+ * @author Sebastian Javier Marchano (sebasjm)
+ */
+
+import { h, VNode } from "preact";
+import { MerchantBackend } from "../../../../declaration.js";
+import { useTranslationContext } from "@gnu-taler/web-util/browser";
+import { CardTable } from "./Table.js";
+
+export interface Props {
+ devices: MerchantBackend.OTP.OtpDeviceEntry[];
+ onLoadMoreBefore?: () => void;
+ onLoadMoreAfter?: () => void;
+ onCreate: () => void;
+ onDelete: (e: MerchantBackend.OTP.OtpDeviceEntry) => void;
+ onSelect: (e: MerchantBackend.OTP.OtpDeviceEntry) => void;
+}
+
+export function ListPage({
+ devices,
+ onCreate,
+ onDelete,
+ onSelect,
+ onLoadMoreBefore,
+ onLoadMoreAfter,
+}: Props): VNode {
+ const form = { payto_uri: "" };
+
+ const { i18n } = useTranslationContext();
+ return (
+ <section class="section is-main-section">
+ <CardTable
+ devices={devices.map((o) => ({
+ ...o,
+ id: String(o.otp_device_id),
+ }))}
+ onCreate={onCreate}
+ onDelete={onDelete}
+ onSelect={onSelect}
+ onLoadMoreBefore={onLoadMoreBefore}
+ hasMoreBefore={!onLoadMoreBefore}
+ onLoadMoreAfter={onLoadMoreAfter}
+ hasMoreAfter={!onLoadMoreAfter}
+ />
+ </section>
+ );
+}
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/validators/list/Table.tsx b/packages/merchant-backoffice-ui/src/paths/instance/validators/list/Table.tsx
new file mode 100644
index 000000000..b639a6134
--- /dev/null
+++ b/packages/merchant-backoffice-ui/src/paths/instance/validators/list/Table.tsx
@@ -0,0 +1,213 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021-2023 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/>
+ */
+
+/**
+ *
+ * @author Sebastian Javier Marchano (sebasjm)
+ */
+
+import { useTranslationContext } from "@gnu-taler/web-util/browser";
+import { h, VNode } from "preact";
+import { StateUpdater, useState } from "preact/hooks";
+import { MerchantBackend } from "../../../../declaration.js";
+
+type Entity = MerchantBackend.OTP.OtpDeviceEntry;
+
+interface Props {
+ devices: Entity[];
+ onDelete: (e: Entity) => void;
+ onSelect: (e: Entity) => void;
+ onCreate: () => void;
+ onLoadMoreBefore?: () => void;
+ hasMoreBefore?: boolean;
+ hasMoreAfter?: boolean;
+ onLoadMoreAfter?: () => void;
+}
+
+export function CardTable({
+ devices,
+ onCreate,
+ onDelete,
+ onSelect,
+ onLoadMoreAfter,
+ onLoadMoreBefore,
+ hasMoreAfter,
+ hasMoreBefore,
+}: Props): VNode {
+ const [rowSelection, rowSelectionHandler] = useState<string[]>([]);
+
+ const { i18n } = useTranslationContext();
+
+ return (
+ <div class="card has-table">
+ <header class="card-header">
+ <p class="card-header-title">
+ <span class="icon">
+ <i class="mdi mdi-newspaper" />
+ </span>
+ <i18n.Translate>OTP Devices</i18n.Translate>
+ </p>
+ <div class="card-header-icon" aria-label="more options">
+ <span
+ class="has-tooltip-left"
+ data-tooltip={i18n.str`add new devices`}
+ >
+ <button class="button is-info" type="button" onClick={onCreate}>
+ <span class="icon is-small">
+ <i class="mdi mdi-plus mdi-36px" />
+ </span>
+ </button>
+ </span>
+ </div>
+ </header>
+ <div class="card-content">
+ <div class="b-table has-pagination">
+ <div class="table-wrapper has-mobile-cards">
+ {devices.length > 0 ? (
+ <Table
+ instances={devices}
+ onDelete={onDelete}
+ onSelect={onSelect}
+ rowSelection={rowSelection}
+ rowSelectionHandler={rowSelectionHandler}
+ onLoadMoreAfter={onLoadMoreAfter}
+ onLoadMoreBefore={onLoadMoreBefore}
+ hasMoreAfter={hasMoreAfter}
+ hasMoreBefore={hasMoreBefore}
+ />
+ ) : (
+ <EmptyTable />
+ )}
+ </div>
+ </div>
+ </div>
+ </div>
+ );
+}
+interface TableProps {
+ rowSelection: string[];
+ instances: Entity[];
+ onDelete: (e: Entity) => void;
+ onSelect: (e: Entity) => void;
+ rowSelectionHandler: StateUpdater<string[]>;
+ onLoadMoreBefore?: () => void;
+ hasMoreBefore?: boolean;
+ hasMoreAfter?: boolean;
+ onLoadMoreAfter?: () => void;
+}
+
+function toggleSelected<T>(id: T): (prev: T[]) => T[] {
+ return (prev: T[]): T[] =>
+ prev.indexOf(id) == -1 ? [...prev, id] : prev.filter((e) => e != id);
+}
+
+function Table({
+ instances,
+ onLoadMoreAfter,
+ onDelete,
+ onSelect,
+ onLoadMoreBefore,
+ hasMoreAfter,
+ hasMoreBefore,
+}: TableProps): VNode {
+ const { i18n } = useTranslationContext();
+ return (
+ <div class="table-container">
+ {onLoadMoreBefore && (
+ <button
+ class="button is-fullwidth"
+ data-tooltip={i18n.str`load more devices before the first one`}
+ disabled={!hasMoreBefore}
+ onClick={onLoadMoreBefore}
+ >
+ <i18n.Translate>load newer devices</i18n.Translate>
+ </button>
+ )}
+ <table class="table is-fullwidth is-striped is-hoverable is-fullwidth">
+ <thead>
+ <tr>
+ <th>
+ <i18n.Translate>ID</i18n.Translate>
+ </th>
+ <th>
+ <i18n.Translate>Description</i18n.Translate>
+ </th>
+ <th />
+ </tr>
+ </thead>
+ <tbody>
+ {instances.map((i) => {
+ return (
+ <tr key={i.otp_device_id}>
+ <td
+ onClick={(): void => onSelect(i)}
+ style={{ cursor: "pointer" }}
+ >
+ {i.otp_device_id}
+ </td>
+ <td
+ onClick={(): void => onSelect(i)}
+ style={{ cursor: "pointer" }}
+ >
+ {i.otp_device_id}
+ </td>
+ <td class="is-actions-cell right-sticky">
+ <div class="buttons is-right">
+ <button
+ class="button is-danger is-small has-tooltip-left"
+ data-tooltip={i18n.str`delete selected devices from the database`}
+ onClick={() => onDelete(i)}
+ >
+ Delete
+ </button>
+ </div>
+ </td>
+ </tr>
+ );
+ })}
+ </tbody>
+ </table>
+ {onLoadMoreAfter && (
+ <button
+ class="button is-fullwidth"
+ data-tooltip={i18n.str`load more devices after the last one`}
+ disabled={!hasMoreAfter}
+ onClick={onLoadMoreAfter}
+ >
+ <i18n.Translate>load older devices</i18n.Translate>
+ </button>
+ )}
+ </div>
+ );
+}
+
+function EmptyTable(): VNode {
+ const { i18n } = useTranslationContext();
+ return (
+ <div class="content has-text-grey has-text-centered">
+ <p>
+ <span class="icon is-large">
+ <i class="mdi mdi-emoticon-sad mdi-48px" />
+ </span>
+ </p>
+ <p>
+ <i18n.Translate>
+ There is no devices yet, add more pressing the + sign
+ </i18n.Translate>
+ </p>
+ </div>
+ );
+}
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/validators/list/index.tsx b/packages/merchant-backoffice-ui/src/paths/instance/validators/list/index.tsx
new file mode 100644
index 000000000..8837c848b
--- /dev/null
+++ b/packages/merchant-backoffice-ui/src/paths/instance/validators/list/index.tsx
@@ -0,0 +1,106 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021-2023 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/>
+ */
+
+/**
+ *
+ * @author Sebastian Javier Marchano (sebasjm)
+ */
+
+import { HttpStatusCode } from "@gnu-taler/taler-util";
+import {
+ ErrorType,
+ HttpError,
+ useTranslationContext,
+} from "@gnu-taler/web-util/browser";
+import { Fragment, VNode, h } from "preact";
+import { useState } from "preact/hooks";
+import { Loading } from "../../../../components/exception/loading.js";
+import { NotificationCard } from "../../../../components/menu/index.js";
+import { MerchantBackend } from "../../../../declaration.js";
+import { useInstanceOtpDevices, useOtpDeviceAPI } from "../../../../hooks/otp.js";
+import { Notification } from "../../../../utils/types.js";
+import { ListPage } from "./ListPage.js";
+
+interface Props {
+ onUnauthorized: () => VNode;
+ onLoadError: (error: HttpError<MerchantBackend.ErrorDetail>) => VNode;
+ onNotFound: () => VNode;
+ onCreate: () => void;
+ onSelect: (id: string) => void;
+}
+
+export default function ListValidators({
+ onUnauthorized,
+ onLoadError,
+ onCreate,
+ onSelect,
+ onNotFound,
+}: Props): VNode {
+ const [position, setPosition] = useState<string | undefined>(undefined);
+ const { i18n } = useTranslationContext();
+ const [notif, setNotif] = useState<Notification | undefined>(undefined);
+ const { deleteOtpDevice } = useOtpDeviceAPI();
+ const result = useInstanceOtpDevices({ position }, (id) => setPosition(id));
+
+ if (result.loading) return <Loading />;
+ if (!result.ok) {
+ if (
+ result.type === ErrorType.CLIENT &&
+ result.status === HttpStatusCode.Unauthorized
+ )
+ return onUnauthorized();
+ if (
+ result.type === ErrorType.CLIENT &&
+ result.status === HttpStatusCode.NotFound
+ )
+ return onNotFound();
+ return onLoadError(result);
+ }
+
+ return (
+ <Fragment>
+ <NotificationCard notification={notif} />
+
+ <ListPage
+ devices={result.data.otp_devices}
+ onLoadMoreBefore={
+ result.isReachingStart ? result.loadMorePrev : undefined
+ }
+ onLoadMoreAfter={result.isReachingEnd ? result.loadMore : undefined}
+ onCreate={onCreate}
+ onSelect={(e) => {
+ onSelect(e.otp_device_id);
+ }}
+ onDelete={(e: MerchantBackend.OTP.OtpDeviceEntry) =>
+ deleteOtpDevice(e.otp_device_id)
+ .then(() =>
+ setNotif({
+ message: i18n.str`validator delete successfully`,
+ type: "SUCCESS",
+ }),
+ )
+ .catch((error) =>
+ setNotif({
+ message: i18n.str`could not delete the validator`,
+ type: "ERROR",
+ description: error.message,
+ }),
+ )
+ }
+ />
+ </Fragment>
+ );
+}
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/validators/update/Update.stories.tsx b/packages/merchant-backoffice-ui/src/paths/instance/validators/update/Update.stories.tsx
new file mode 100644
index 000000000..fcb77b820
--- /dev/null
+++ b/packages/merchant-backoffice-ui/src/paths/instance/validators/update/Update.stories.tsx
@@ -0,0 +1,32 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021-2023 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/>
+ */
+
+/**
+ *
+ * @author Sebastian Javier Marchano (sebasjm)
+ */
+
+import { h, VNode, FunctionalComponent } from "preact";
+import { UpdatePage as TestedComponent } from "./UpdatePage.js";
+
+export default {
+ title: "Pages/Validators/Update",
+ component: TestedComponent,
+ argTypes: {
+ onUpdate: { action: "onUpdate" },
+ onBack: { action: "onBack" },
+ },
+};
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/validators/update/UpdatePage.tsx b/packages/merchant-backoffice-ui/src/paths/instance/validators/update/UpdatePage.tsx
new file mode 100644
index 000000000..585c12e11
--- /dev/null
+++ b/packages/merchant-backoffice-ui/src/paths/instance/validators/update/UpdatePage.tsx
@@ -0,0 +1,185 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021-2023 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/>
+ */
+
+/**
+ *
+ * @author Sebastian Javier Marchano (sebasjm)
+ */
+
+import { useTranslationContext } from "@gnu-taler/web-util/browser";
+import { Fragment, h, VNode } from "preact";
+import { useState } from "preact/hooks";
+import { AsyncButton } from "../../../../components/exception/AsyncButton.js";
+import {
+ FormErrors,
+ FormProvider,
+} from "../../../../components/form/FormProvider.js";
+import { Input } from "../../../../components/form/Input.js";
+import { MerchantBackend, WithId } from "../../../../declaration.js";
+import { InputSelector } from "../../../../components/form/InputSelector.js";
+import { InputWithAddon } from "../../../../components/form/InputWithAddon.js";
+import { randomBase32Key } from "../../../../utils/crypto.js";
+
+type Entity = MerchantBackend.OTP.OtpDevicePatchDetails & WithId;
+
+interface Props {
+ onUpdate: (d: Entity) => Promise<void>;
+ onBack?: () => void;
+ device: Entity;
+}
+const algorithms = [0, 1, 2];
+const algorithmsNames = ["off", "30s 8d TOTP-SHA1", "30s 8d eTOTP-SHA1"];
+export function UpdatePage({ device, onUpdate, onBack }: Props): VNode {
+ const { i18n } = useTranslationContext();
+
+ const [state, setState] = useState<Partial<Entity>>(device);
+ const [showKey, setShowKey] = useState(false);
+
+ const errors: FormErrors<Entity> = {
+ };
+
+ const hasErrors = Object.keys(errors).some(
+ (k) => (errors as any)[k] !== undefined,
+ );
+
+ const submitForm = () => {
+ if (hasErrors) return Promise.reject();
+ return onUpdate(state as any);
+ };
+
+ return (
+ <div>
+ <section class="section">
+ <section class="hero is-hero-bar">
+ <div class="hero-body">
+ <div class="level">
+ <div class="level-left">
+ <div class="level-item">
+ <span class="is-size-4">
+ Device: <b>{device.id}</b>
+ </span>
+ </div>
+ </div>
+ </div>
+ </div>
+ </section>
+ <hr />
+
+ <section class="section is-main-section">
+ <div class="columns">
+ <div class="column is-four-fifths">
+ <FormProvider
+ object={state}
+ valueHandler={setState}
+ errors={errors}
+ >
+ <Input<Entity>
+ name="otp_description"
+ label={i18n.str`Description`}
+ tooltip={i18n.str`dddd`}
+ />
+ <InputSelector<Entity>
+ name="otp_algorithm"
+ label={i18n.str`Verification algorithm`}
+ tooltip={i18n.str`Algorithm to use to verify transaction in offline mode`}
+ values={algorithms}
+ toStr={(v) => algorithmsNames[v]}
+ fromStr={(v) => Number(v)}
+ />
+ {state.otp_algorithm && state.otp_algorithm > 0 ? (
+ <Fragment>
+ <InputWithAddon<Entity>
+ name="otp_key"
+ label={i18n.str`Device key`}
+ readonly={state.otp_key === undefined}
+ inputType={showKey ? "text" : "password"}
+ help={state.otp_key === undefined ? "Not modified" : "Be sure to be very hard to guess or use the random generator"}
+ tooltip={i18n.str`Your device need to have exactly the same value`}
+ fromStr={(v) => v.toUpperCase()}
+ addonAfter={
+ <span class="icon">
+ {showKey ? (
+ <i class="mdi mdi-eye" />
+ ) : (
+ <i class="mdi mdi-eye-off" />
+ )}
+ </span>
+ }
+ side={
+ state.otp_key === undefined ? <button
+
+ onClick={(e) => {
+ setState((s) => ({ ...s, otp_key: "" }));
+ }}
+ class="button">change key</button> :
+ <span style={{ display: "flex" }}>
+ <button
+ data-tooltip={i18n.str`generate random secret key`}
+ class="button is-info mr-3"
+ onClick={(e) => {
+ setState((s) => ({ ...s, otp_key: randomBase32Key() }));
+ }}
+ >
+ <i18n.Translate>random</i18n.Translate>
+ </button>
+ <button
+ data-tooltip={
+ showKey
+ ? i18n.str`show secret key`
+ : i18n.str`hide secret key`
+ }
+ class="button is-info mr-3"
+ onClick={(e) => {
+ setShowKey(!showKey);
+ }}
+ >
+ {showKey ? (
+ <i18n.Translate>hide</i18n.Translate>
+ ) : (
+ <i18n.Translate>show</i18n.Translate>
+ )}
+ </button>
+ </span>
+ }
+ />
+ </Fragment>
+ ) : undefined} </FormProvider>
+
+ <div class="buttons is-right mt-5">
+ {onBack && (
+ <button class="button" onClick={onBack}>
+ <i18n.Translate>Cancel</i18n.Translate>
+ </button>
+ )}
+ <AsyncButton
+ disabled={hasErrors}
+ data-tooltip={
+ hasErrors
+ ? i18n.str`Need to complete marked fields`
+ : "confirm operation"
+ }
+ onClick={submitForm}
+ >
+ <i18n.Translate>Confirm</i18n.Translate>
+ </AsyncButton>
+ </div>
+ </div>
+ </div>
+ </section>
+ </section>
+ </div>
+ );
+}
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/validators/update/index.tsx b/packages/merchant-backoffice-ui/src/paths/instance/validators/update/index.tsx
new file mode 100644
index 000000000..9a27ccfee
--- /dev/null
+++ b/packages/merchant-backoffice-ui/src/paths/instance/validators/update/index.tsx
@@ -0,0 +1,102 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021-2023 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/>
+ */
+
+/**
+ *
+ * @author Sebastian Javier Marchano (sebasjm)
+ */
+
+import {
+ ErrorType,
+ HttpError,
+ useTranslationContext,
+} from "@gnu-taler/web-util/browser";
+import { Fragment, h, VNode } from "preact";
+import { useState } from "preact/hooks";
+import { Loading } from "../../../../components/exception/loading.js";
+import { NotificationCard } from "../../../../components/menu/index.js";
+import { MerchantBackend, WithId } from "../../../../declaration.js";
+import { Notification } from "../../../../utils/types.js";
+import { UpdatePage } from "./UpdatePage.js";
+import { HttpStatusCode } from "@gnu-taler/taler-util";
+import { useOtpDeviceAPI, useOtpDeviceDetails } from "../../../../hooks/otp.js";
+
+export type Entity = MerchantBackend.OTP.OtpDevicePatchDetails & WithId;
+
+interface Props {
+ onBack?: () => void;
+ onConfirm: () => void;
+ onUnauthorized: () => VNode;
+ onNotFound: () => VNode;
+ onLoadError: (e: HttpError<MerchantBackend.ErrorDetail>) => VNode;
+ vid: string;
+}
+export default function UpdateValidator({
+ vid,
+ onConfirm,
+ onBack,
+ onUnauthorized,
+ onNotFound,
+ onLoadError,
+}: Props): VNode {
+ const { updateOtpDevice } = useOtpDeviceAPI();
+ const result = useOtpDeviceDetails(vid);
+ const [notif, setNotif] = useState<Notification | undefined>(undefined);
+
+ const { i18n } = useTranslationContext();
+
+ if (result.loading) return <Loading />;
+ if (!result.ok) {
+ if (
+ result.type === ErrorType.CLIENT &&
+ result.status === HttpStatusCode.Unauthorized
+ )
+ return onUnauthorized();
+ if (
+ result.type === ErrorType.CLIENT &&
+ result.status === HttpStatusCode.NotFound
+ )
+ return onNotFound();
+ return onLoadError(result);
+ }
+
+ return (
+ <Fragment>
+ <NotificationCard notification={notif} />
+ <UpdatePage
+ device={{
+ id: vid,
+ otp_algorithm: result.data.otp_algorithm,
+ otp_description: result.data.device_description,
+ otp_key: undefined,
+ otp_ctr: result.data.otp_ctr
+ }}
+ onBack={onBack}
+ onUpdate={(data) => {
+ return updateOtpDevice(vid, data)
+ .then(onConfirm)
+ .catch((error) => {
+ setNotif({
+ message: i18n.str`could not update template`,
+ type: "ERROR",
+ description: error.message,
+ });
+ });
+ }}
+ />
+ </Fragment>
+ );
+}
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/webhooks/list/Table.tsx b/packages/merchant-backoffice-ui/src/paths/instance/webhooks/list/Table.tsx
index fd7b08875..124ced1f1 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/webhooks/list/Table.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/webhooks/list/Table.tsx
@@ -81,9 +81,6 @@ export function CardTable({
instances={webhooks}
onDelete={onDelete}
onSelect={onSelect}
- onNewOrder={(d) => {
- console.log("test", d);
- }}
rowSelection={rowSelection}
rowSelectionHandler={rowSelectionHandler}
onLoadMoreAfter={onLoadMoreAfter}
@@ -104,7 +101,6 @@ interface TableProps {
rowSelection: string[];
instances: Entity[];
onDelete: (e: Entity) => void;
- onNewOrder: (e: Entity) => void;
onSelect: (e: Entity) => void;
rowSelectionHandler: StateUpdater<string[]>;
onLoadMoreBefore?: () => void;
@@ -122,7 +118,6 @@ function Table({
instances,
onLoadMoreAfter,
onDelete,
- onNewOrder,
onSelect,
onLoadMoreBefore,
hasMoreAfter,
diff --git a/packages/merchant-backoffice-ui/src/paths/settings/index.tsx b/packages/merchant-backoffice-ui/src/paths/settings/index.tsx
index 128450553..0d514f2df 100644
--- a/packages/merchant-backoffice-ui/src/paths/settings/index.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/settings/index.tsx
@@ -1,10 +1,10 @@
-import { VNode, h } from "preact";
-import { LangSelector } from "../../components/menu/LangSelector.js";
import { useLang, useTranslationContext } from "@gnu-taler/web-util/browser";
+import { VNode, h } from "preact";
+import { FormErrors, FormProvider } from "../../components/form/FormProvider.js";
+import { InputSelector } from "../../components/form/InputSelector.js";
import { InputToggle } from "../../components/form/InputToggle.js";
+import { LangSelector } from "../../components/menu/LangSelector.js";
import { Settings, useSettings } from "../../hooks/useSettings.js";
-import { FormErrors, FormProvider } from "../../components/form/FormProvider.js";
-import { useState } from "preact/hooks";
function getBrowserLang(): string | undefined {
if (typeof window === "undefined") return undefined;
@@ -24,7 +24,11 @@ export function Settings(): VNode {
function valueHandler(s: (d: Partial<Settings>) => Partial<Settings>): void {
const next = s(value)
- updateValue("advanceOrderMode", next.advanceOrderMode ?? false)
+ const v: Settings = {
+ advanceOrderMode: next.advanceOrderMode ?? false,
+ dateFormat: next.dateFormat ?? "ymd"
+ }
+ updateValue(v)
}
return <div>
@@ -32,41 +36,64 @@ export function Settings(): VNode {
<div class="columns">
<div class="column" />
<div class="column is-four-fifths">
- <div class="field is-horizontal">
- <div class="field-label is-normal">
- <label class="label" style={{ width: 200 }}>
- <i18n.Translate>Language</i18n.Translate>
- <span class="icon has-tooltip-right" data-tooltip={"Force language setting instance of taking the browser"}>
- <i class="mdi mdi-information" />
- </span>
- </label>
- </div>
- <div class="field has-addons">
- <LangSelector />
- &nbsp;
- {borwserLang !== undefined && <button
- data-tooltip={i18n.str`generate random secret key`}
- class="button is-info mr-3"
- onClick={(e) => {
- update(borwserLang.substring(0, 2))
+ <div>
+
+ <FormProvider<Settings>
+ name="settings"
+ errors={errors}
+ object={value}
+ valueHandler={valueHandler}
+ >
+ <div class="field is-horizontal">
+ <div class="field-label is-normal">
+ <label class="label">
+ <i18n.Translate>Language</i18n.Translate>
+ <span class="icon has-tooltip-right" data-tooltip={"Force language setting instance of taking the browser"}>
+ <i class="mdi mdi-information" />
+ </span>
+ </label>
+ </div>
+ <div class="field field-body has-addons is-flex-grow-3">
+ <LangSelector />
+ &nbsp;
+ {borwserLang !== undefined && <button
+ data-tooltip={i18n.str`generate random secret key`}
+ class="button is-info mr-2"
+ onClick={(e) => {
+ update(borwserLang.substring(0, 2))
+ }}
+ >
+ <i18n.Translate>Set default</i18n.Translate>
+ </button>}
+ </div>
+ </div>
+ <InputToggle<Settings>
+ label={i18n.str`Advance order creation`}
+ tooltip={i18n.str`Shows more options in the order creation form`}
+ name="advanceOrderMode"
+ />
+ <InputSelector<Settings>
+ name="dateFormat"
+ label={i18n.str`Date format`}
+ expand={true}
+ help={
+ value.dateFormat === "dmy" ? "31/12/2001" : value.dateFormat === "mdy" ? "12/31/2001" : value.dateFormat === "ymd" ? "2001/12/31" : ""
+ }
+ toStr={(e) => {
+ if (e === "ymd") return "year month day"
+ if (e === "mdy") return "month day year"
+ if (e === "dmy") return "day month year"
+ return "choose one"
}}
- >
- <i18n.Translate>Set default</i18n.Translate>
- </button>}
- </div>
+ values={[
+ "ymd",
+ "mdy",
+ "dmy",
+ ]}
+ tooltip={i18n.str`how the date is going to be displayed`}
+ />
+ </FormProvider>
</div>
- <FormProvider<Settings>
- name="settings"
- errors={errors}
- object={value}
- valueHandler={valueHandler}
- >
- <InputToggle<Settings>
- label={i18n.str`Advance order creation`}
- tooltip={i18n.str`Shows more options in the order creation form`}
- name="advanceOrderMode"
- />
- </FormProvider>
</div>
diff --git a/packages/merchant-backoffice-ui/src/schemas/index.ts b/packages/merchant-backoffice-ui/src/schemas/index.ts
index 149761c55..4be77595b 100644
--- a/packages/merchant-backoffice-ui/src/schemas/index.ts
+++ b/packages/merchant-backoffice-ui/src/schemas/index.ts
@@ -123,7 +123,7 @@ export const InstanceSchema = yup.object().shape({
export const InstanceUpdateSchema = InstanceSchema.clone().omit(["id"]);
export const InstanceCreateSchema = InstanceSchema.clone();
-export const AuthorizeTipSchema = yup.object().shape({
+export const AuthorizeRewardSchema = yup.object().shape({
justification: yup.string().required(),
amount: yup
.string()
@@ -161,7 +161,7 @@ export const OrderCreateSchema = yup.object().shape({
currencyGreaterThan0,
),
}),
- extra: yup.string().test("extra", "is not a JSON format", stringIsValidJSON),
+ // extra: yup.object().test("extra", "is not a JSON format", stringIsValidJSON),
payments: yup
.object()
.required()
diff --git a/packages/merchant-backoffice-ui/tsconfig.json b/packages/merchant-backoffice-ui/tsconfig.json
index 02c562f96..396f1e9e7 100644
--- a/packages/merchant-backoffice-ui/tsconfig.json
+++ b/packages/merchant-backoffice-ui/tsconfig.json
@@ -1,61 +1,58 @@
{
- "compilerOptions": {
- /* Basic Options */
- "target": "ES6", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', or 'ESNEXT'. */
- "module": "ESNext", /* Specify module code generation: 'none', commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
- "lib": [
- "es2021",
- "dom"
- ], /* Specify library files to be included in the compilation: */
- // "allowJs": true, /* Allow javascript files to be compiled. */
- // "checkJs": true, /* Report errors in .js files. */
- "jsx": "react", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
- "jsxFactory": "h", /* Specify the JSX factory function to use when targeting react JSX emit, e.g. React.createElement or h. */
- "jsxFragmentFactory": "Fragment", // https://www.typescriptlang.org/docs/handbook/release-notes/typescript-4-0.html#custom-jsx-factories
- // "declaration": true, /* Generates corresponding '.d.ts' file. */
- // "sourceMap": true, /* Generates corresponding '.map' file. */
- // "outFile": "./", /* Concatenate and emit output to single file. */
- // "outDir": "./", /* Redirect output structure to the directory. */
- // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
- // "removeComments": true, /* Do not emit comments to output. */
- "noEmit": true, /* Do not emit outputs. */
- // "importHelpers": true, /* Import emit helpers from 'tslib'. */
- // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
- // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
- /* Strict Type-Checking Options */
- "strict": true, /* Enable all strict type-checking options. */
- // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
- // "strictNullChecks": true, /* Enable strict null checks. */
- // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
- // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
- /* Additional Checks */
- // "noUnusedLocals": true, /* Report errors on unused locals. */
- // "noUnusedParameters": true, /* Report errors on unused parameters. */
- // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
- // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
- /* Module Resolution Options */
- "moduleResolution": "node16", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
- "esModuleInterop": true, /* */
- // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
- // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
- // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
- // "typeRoots": [], /* List of folders to include type definitions from. */
- // "types": [], /* Type declaration files to be included in compilation. */
- // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
- // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
- /* Source Map Options */
- // "sourceRoot": "./", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
- // "mapRoot": "./", /* Specify the location where debugger should locate map files instead of generated locations. */
- // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
- // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
- /* Experimental Options */
- // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
- // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
- /* Advanced Options */
- "skipLibCheck": true /* Skip type checking of declaration files. */
- },
- "include": [
- "src/**/*",
- "tests/**/*"
- ]
-} \ No newline at end of file
+ "compilerOptions": {
+ /* Basic Options */
+ "target": "ES2020" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', or 'ESNEXT'. */,
+ "module": "Node16" /* Specify module code generation: 'none', commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */,
+ "lib": [
+ "es2020",
+ "dom"
+ ] /* Specify library files to be included in the compilation: */,
+ // "allowJs": true, /* Allow javascript files to be compiled. */
+ // "checkJs": true, /* Report errors in .js files. */
+ "jsx": "react" /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */,
+ "jsxFactory": "h" /* Specify the JSX factory function to use when targeting react JSX emit, e.g. React.createElement or h. */,
+ "jsxFragmentFactory": "Fragment", // https://www.typescriptlang.org/docs/handbook/release-notes/typescript-4-0.html#custom-jsx-factories
+ // "declaration": true, /* Generates corresponding '.d.ts' file. */
+ // "sourceMap": true, /* Generates corresponding '.map' file. */
+ // "outFile": "./", /* Concatenate and emit output to single file. */
+ // "outDir": "./", /* Redirect output structure to the directory. */
+ // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
+ // "removeComments": true, /* Do not emit comments to output. */
+ "noEmit": true /* Do not emit outputs. */,
+ // "importHelpers": true, /* Import emit helpers from 'tslib'. */
+ // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
+ // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
+ /* Strict Type-Checking Options */
+ "strict": true /* Enable all strict type-checking options. */,
+ // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
+ // "strictNullChecks": true, /* Enable strict null checks. */
+ // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
+ // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
+ /* Additional Checks */
+ // "noUnusedLocals": true, /* Report errors on unused locals. */
+ // "noUnusedParameters": true, /* Report errors on unused parameters. */
+ // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
+ // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
+ /* Module Resolution Options */
+ "moduleResolution": "node16" /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */,
+ "esModuleInterop": true /* */,
+ // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
+ // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
+ // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
+ // "typeRoots": [], /* List of folders to include type definitions from. */
+ // "types": [], /* Type declaration files to be included in compilation. */
+ // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
+ // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
+ /* Source Map Options */
+ // "sourceRoot": "./", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
+ // "mapRoot": "./", /* Specify the location where debugger should locate map files instead of generated locations. */
+ // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
+ // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
+ /* Experimental Options */
+ // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
+ // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
+ /* Advanced Options */
+ "skipLibCheck": true /* Skip type checking of declaration files. */
+ },
+ "include": ["src/**/*", "tests/**/*"]
+}
diff --git a/packages/pogen/package.json b/packages/pogen/package.json
index ae1dfac3b..9160e699c 100644
--- a/packages/pogen/package.json
+++ b/packages/pogen/package.json
@@ -11,7 +11,7 @@
},
"devDependencies": {
"po2json": "^0.4.5",
- "typescript": "^5.1.3"
+ "typescript": "^5.2.2"
},
"dependencies": {
"@types/node": "^18.11.17",
diff --git a/packages/pogen/tsconfig.json b/packages/pogen/tsconfig.json
index 68225832d..74cdc6ad7 100644
--- a/packages/pogen/tsconfig.json
+++ b/packages/pogen/tsconfig.json
@@ -7,7 +7,7 @@
"incremental": true,
"moduleResolution": "node",
"sourceMap": true,
- "lib": ["es6"],
+ "lib": ["ES2020"],
"types": ["node"]
},
"include": ["src/**/*.ts"]
diff --git a/packages/taler-harness/package.json b/packages/taler-harness/package.json
index 8916e5e9e..9f2b0d8e3 100644
--- a/packages/taler-harness/package.json
+++ b/packages/taler-harness/package.json
@@ -35,7 +35,7 @@
"esbuild": "^0.17.7",
"prettier": "^2.8.8",
"rimraf": "^3.0.2",
- "typescript": "^5.1.3"
+ "typescript": "^5.2.2"
},
"dependencies": {
"@gnu-taler/taler-util": "workspace:*",
diff --git a/packages/taler-harness/src/bench2.ts b/packages/taler-harness/src/bench2.ts
index 48ac76b9f..53db6f6c1 100644
--- a/packages/taler-harness/src/bench2.ts
+++ b/packages/taler-harness/src/bench2.ts
@@ -57,7 +57,6 @@ export async function runBench2(configJson: any): Promise<void> {
const http = createPlatformHttpLib({
enableThrottling: false,
- allowHttp: true,
});
const numIter = benchConf.iterations ?? 1;
diff --git a/packages/taler-harness/src/bench3.ts b/packages/taler-harness/src/bench3.ts
index c7eca90a8..0b5371af5 100644
--- a/packages/taler-harness/src/bench3.ts
+++ b/packages/taler-harness/src/bench3.ts
@@ -52,7 +52,6 @@ export async function runBench3(configJson: any): Promise<void> {
const myHttpLib = createPlatformHttpLib({
enableThrottling: false,
- allowHttp: true,
});
const numIter = b3conf.iterations ?? 1;
diff --git a/packages/taler-harness/src/env-full.ts b/packages/taler-harness/src/env-full.ts
index 3a684db0b..210d38e32 100644
--- a/packages/taler-harness/src/env-full.ts
+++ b/packages/taler-harness/src/env-full.ts
@@ -79,7 +79,7 @@ export async function runEnvFull(t: GlobalTestState): Promise<void> {
await merchant.start();
await merchant.pingUntilAvailable();
- await merchant.addInstance({
+ await merchant.addInstanceWithWireAccount({
id: "default",
name: "Default Instance",
paytoUris: [getPayto("merchant-default")],
@@ -88,7 +88,7 @@ export async function runEnvFull(t: GlobalTestState): Promise<void> {
),
});
- await merchant.addInstance({
+ await merchant.addInstanceWithWireAccount({
id: "minst1",
name: "minst1",
paytoUris: [getPayto("minst1")],
diff --git a/packages/taler-harness/src/harness/harness.ts b/packages/taler-harness/src/harness/harness.ts
index 7db9d82bd..24e42099e 100644
--- a/packages/taler-harness/src/harness/harness.ts
+++ b/packages/taler-harness/src/harness/harness.ts
@@ -25,59 +25,45 @@
* Imports
*/
import {
+ AccountAddDetails,
AmountJson,
Amounts,
- AmountString,
- codecForMerchantOrderPrivateStatusResponse,
- codecForMerchantPostOrderResponse,
- codecForMerchantReserveCreateConfirmation,
+ BankAccessApiClient,
Configuration,
CoreApiResponse,
- createEddsaKeyPair,
Duration,
- eddsaGetPublic,
EddsaKeyPair,
+ Logger,
+ MerchantInstanceConfig,
+ PartialMerchantInstanceConfig,
+ TalerError,
+ WalletNotification,
+ createEddsaKeyPair,
+ eddsaGetPublic,
encodeCrock,
hash,
j2s,
- Logger,
- MerchantInstancesResponse,
- MerchantOrderPrivateStatusResponse,
- MerchantPostOrderRequest,
- MerchantPostOrderResponse,
- MerchantReserveCreateConfirmation,
- MerchantTemplateAddDetails,
parsePaytoUri,
stringToBytes,
- TalerError,
- TalerProtocolDuration,
- RewardCreateConfirmation,
- RewardCreateRequest,
- TippingReserveStatus,
- WalletNotification,
- codecForAny,
} from "@gnu-taler/taler-util";
import {
+ HttpRequestLibrary,
createPlatformHttpLib,
expectSuccessResponseOrThrow,
- readSuccessResponseJsonOrThrow,
} from "@gnu-taler/taler-util/http";
import {
- BankApi,
- BankServiceHandle,
- HarnessExchangeBankAccount,
- openPromise,
WalletCoreApiClient,
WalletCoreRequestType,
WalletCoreResponseType,
WalletOperations,
+ openPromise,
} from "@gnu-taler/taler-wallet-core";
import {
+ RemoteWallet,
+ WalletNotificationWaiter,
createRemoteWallet,
getClientFromRemoteWallet,
makeNotificationWaiter,
- RemoteWallet,
- WalletNotificationWaiter,
} from "@gnu-taler/taler-wallet-core/remote";
import { deepStrictEqual } from "assert";
import { ChildProcess, spawn } from "child_process";
@@ -383,7 +369,11 @@ export class GlobalTestState {
logger.warn(`could not start process (${command})`, err);
});
proc.on("exit", (code, signal) => {
- logger.warn(`process ${logName} exited ${j2s({ code, signal })}`);
+ if (code == 0 && signal == null) {
+ logger.info(`process ${logName} exited with success`);
+ } else {
+ logger.warn(`process ${logName} exited ${j2s({ code, signal })}`);
+ }
});
const stderrLogFileName = this.testDir + `/${logName}-stderr.log`;
const stderrLog = fs.createWriteStream(stderrLogFileName, {
@@ -578,6 +568,13 @@ class BankServiceBase {
) {}
}
+export interface HarnessExchangeBankAccount {
+ accountName: string;
+ accountPassword: string;
+ accountPaytoUri: string;
+ wireGatewayApiBaseUrl: string;
+}
+
/**
* Implementation of the bank service using the "taler-fakebank-run" tool.
*/
@@ -587,7 +584,7 @@ export class FakebankService
{
proc: ProcessWrapper | undefined;
- http = createPlatformHttpLib({ allowHttp: true, enableThrottling: false });
+ http = createPlatformHttpLib({ enableThrottling: false });
// We store "created" accounts during setup and
// register them after startup.
@@ -695,13 +692,9 @@ export class FakebankService
"bank",
);
await this.pingUntilAvailable();
+ const bankClient = new BankAccessApiClient(this.bankAccessApiBaseUrl);
for (const acc of this.accounts) {
- await BankApi.registerAccount(
- this,
- acc.accountName,
- acc.accountPassword,
- {},
- );
+ await bankClient.registerAccount(acc.accountName, acc.accountPassword);
}
}
@@ -714,6 +707,11 @@ export class FakebankService
// Use libeufin bank instead of pybank.
const useLibeufinBank = false;
+export interface BankServiceHandle {
+ readonly bankAccessApiBaseUrl: string;
+ readonly http: HttpRequestLibrary;
+}
+
export type BankService = BankServiceHandle;
export const BankService = FakebankService;
@@ -760,19 +758,19 @@ export class ExchangeService implements ExchangeServiceInterface {
return new ExchangeService(gc, ec, cfgFilename, keyPair);
}
- private currentTimetravel: Duration | undefined;
+ private currentTimetravelOffsetMs: number | undefined;
- setTimetravel(t: Duration | undefined): void {
+ setTimetravel(t: number | undefined): void {
if (this.isRunning()) {
throw Error("can't set time travel while the exchange is running");
}
- this.currentTimetravel = t;
+ this.currentTimetravelOffsetMs = t;
}
private get timetravelArg(): string | undefined {
- if (this.currentTimetravel && this.currentTimetravel.d_ms !== "forever") {
+ if (this.currentTimetravelOffsetMs != null) {
// Convert to microseconds
- return `--timetravel=+${this.currentTimetravel.d_ms * 1000}`;
+ return `--timetravel=+${this.currentTimetravelOffsetMs * 1000}`;
}
return undefined;
}
@@ -1334,282 +1332,19 @@ export interface MerchantConfig {
overrideTestDir?: string;
}
-export interface PrivateOrderStatusQuery {
- instance?: string;
- orderId: string;
- sessionId?: string;
-}
-
export interface MerchantServiceInterface {
makeInstanceBaseUrl(instanceName?: string): string;
readonly port: number;
readonly name: string;
}
-export interface DeleteTippingReserveArgs {
- reservePub: string;
- purge?: boolean;
-}
-
/**
* Default HTTP client handle for the integration test harness.
*/
export const harnessHttpLib = createPlatformHttpLib({
- allowHttp: true,
enableThrottling: false,
});
-export class MerchantApiClient {
- constructor(
- private baseUrl: string,
- public readonly auth: MerchantAuthConfiguration,
- ) {}
-
- httpClient = createPlatformHttpLib({ allowHttp: true, enableThrottling: false });
-
- async changeAuth(auth: MerchantAuthConfiguration): Promise<void> {
- const url = new URL("private/auth", this.baseUrl);
- const res = await this.httpClient.fetch(url.href, {
- method: "POST",
- body: auth,
- headers: this.makeAuthHeader(),
- });
- await expectSuccessResponseOrThrow(res);
- }
-
- async deleteTippingReserve(req: DeleteTippingReserveArgs): Promise<void> {
- const url = new URL(`private/reserves/${req.reservePub}`, this.baseUrl);
- if (req.purge) {
- url.searchParams.set("purge", "YES");
- }
- const resp = await this.httpClient.fetch(url.href, {
- method: "DELETE",
- headers: this.makeAuthHeader(),
- });
- logger.info(`delete status: ${resp.status}`);
- return;
- }
-
- async createTippingReserve(
- req: CreateMerchantTippingReserveRequest,
- ): Promise<MerchantReserveCreateConfirmation> {
- const url = new URL("private/reserves", this.baseUrl);
- const resp = await this.httpClient.fetch(url.href, {
- method: "POST",
- body: req,
- headers: this.makeAuthHeader(),
- });
- const respData = readSuccessResponseJsonOrThrow(
- resp,
- codecForMerchantReserveCreateConfirmation(),
- );
- return respData;
- }
-
- async getPrivateInstanceInfo(): Promise<any> {
- console.log(this.makeAuthHeader());
- const url = new URL("private", this.baseUrl);
- logger.info(`request url ${url.href}`);
- const resp = await this.httpClient.fetch(url.href, {
- method: "GET",
- headers: this.makeAuthHeader(),
- });
- return await resp.json();
- }
-
- async getPrivateTipReserves(): Promise<TippingReserveStatus> {
- console.log(this.makeAuthHeader());
- const url = new URL("private/reserves", this.baseUrl);
- const resp = await this.httpClient.fetch(url.href, {
- method: "GET",
- headers: this.makeAuthHeader(),
- });
- // FIXME: Validate!
- return await resp.json();
- }
-
- async deleteInstance(instanceId: string) {
- const url = new URL(`management/instances/${instanceId}`, this.baseUrl);
- const resp = await this.httpClient.fetch(url.href, {
- method: "DELETE",
- headers: this.makeAuthHeader(),
- });
- await expectSuccessResponseOrThrow(resp);
- }
-
- async createInstance(req: MerchantInstanceConfig): Promise<void> {
- const url = new URL("management/instances", this.baseUrl);
- await this.httpClient.fetch(url.href, {
- method: "POST",
- body: req,
- headers: this.makeAuthHeader(),
- });
- }
-
- async getInstances(): Promise<MerchantInstancesResponse> {
- const url = new URL("management/instances", this.baseUrl);
- const resp = await this.httpClient.fetch(url.href, {
- headers: this.makeAuthHeader(),
- });
- return resp.json();
- }
-
- async getInstanceFullDetails(instanceId: string): Promise<any> {
- const url = new URL(`management/instances/${instanceId}`, this.baseUrl);
- try {
- const resp = await this.httpClient.fetch(url.href, {
- headers: this.makeAuthHeader(),
- });
- return resp.json();
- } catch (e) {
- throw e;
- }
- }
-
- makeAuthHeader(): Record<string, string> {
- switch (this.auth.method) {
- case "external":
- return {};
- case "token":
- return {
- Authorization: `Bearer ${this.auth.token}`,
- };
- }
- }
-}
-
-/**
- * FIXME: This should be deprecated in favor of MerchantApiClient
- *
- * @deprecated use MerchantApiClient instead
- */
-export namespace MerchantPrivateApi {
- export async function createOrder(
- merchantService: MerchantServiceInterface,
- instanceName: string,
- req: MerchantPostOrderRequest,
- withAuthorization: WithAuthorization = {},
- ): Promise<MerchantPostOrderResponse> {
- const baseUrl = merchantService.makeInstanceBaseUrl(instanceName);
- let url = new URL("private/orders", baseUrl);
- const resp = await harnessHttpLib.fetch(url.href, {
- method: "POST",
- body: req,
- headers: withAuthorization as Record<string, string>,
- });
- return readSuccessResponseJsonOrThrow(
- resp,
- codecForMerchantPostOrderResponse(),
- );
- }
-
- export async function createTemplate(
- merchantService: MerchantServiceInterface,
- instanceName: string,
- req: MerchantTemplateAddDetails,
- withAuthorization: WithAuthorization = {},
- ) {
- const baseUrl = merchantService.makeInstanceBaseUrl(instanceName);
- let url = new URL("private/templates", baseUrl);
- const resp = await harnessHttpLib.fetch(url.href, {
- method: "POST",
- body: req,
- headers: withAuthorization as Record<string, string>,
- });
- if (resp.status !== 204) {
- throw Error("failed to create template");
- }
- }
-
- export async function queryPrivateOrderStatus(
- merchantService: MerchantServiceInterface,
- query: PrivateOrderStatusQuery,
- withAuthorization: WithAuthorization = {},
- ): Promise<MerchantOrderPrivateStatusResponse> {
- const reqUrl = new URL(
- `private/orders/${query.orderId}`,
- merchantService.makeInstanceBaseUrl(query.instance),
- );
- if (query.sessionId) {
- reqUrl.searchParams.set("session_id", query.sessionId);
- }
- const resp = await harnessHttpLib.fetch(reqUrl.href, {
- headers: withAuthorization as Record<string, string>,
- });
- return readSuccessResponseJsonOrThrow(
- resp,
- codecForMerchantOrderPrivateStatusResponse(),
- );
- }
-
- export async function giveRefund(
- merchantService: MerchantServiceInterface,
- r: {
- instance: string;
- orderId: string;
- amount: string;
- justification: string;
- },
- ): Promise<{ talerRefundUri: string }> {
- const reqUrl = new URL(
- `private/orders/${r.orderId}/refund`,
- merchantService.makeInstanceBaseUrl(r.instance),
- );
- const resp = await harnessHttpLib.fetch(reqUrl.href, {
- method: "POST",
- body: {
- refund: r.amount,
- reason: r.justification,
- },
- });
- const respBody = await resp.json();
- return {
- talerRefundUri: respBody.taler_refund_uri,
- };
- }
-
- export async function queryTippingReserves(
- merchantService: MerchantServiceInterface,
- instance: string,
- ): Promise<TippingReserveStatus> {
- const reqUrl = new URL(
- `private/reserves`,
- merchantService.makeInstanceBaseUrl(instance),
- );
- const resp = await harnessHttpLib.fetch(reqUrl.href);
- // FIXME: validate
- return resp.json();
- }
-
- export async function giveTip(
- merchantService: MerchantServiceInterface,
- instance: string,
- req: RewardCreateRequest,
- ): Promise<RewardCreateConfirmation> {
- const reqUrl = new URL(
- `private/tips`,
- merchantService.makeInstanceBaseUrl(instance),
- );
- const resp = await harnessHttpLib.fetch(reqUrl.href, {
- method: "POST",
- body: req,
- });
- // FIXME: validate
- return resp.json();
- }
-}
-
-export interface CreateMerchantTippingReserveRequest {
- // Amount that the merchant promises to put into the reserve
- initial_balance: AmountString;
-
- // Exchange the merchant intends to use for tipping
- exchange_url: string;
-
- // Desired wire method, for example "iban" or "x-taler-bank"
- wire_method: string;
-}
-
export class MerchantService implements MerchantServiceInterface {
static fromExistingConfig(
gc: GlobalTestState,
@@ -1636,23 +1371,23 @@ export class MerchantService implements MerchantServiceInterface {
private configFilename: string,
) {}
- private currentTimetravel: Duration | undefined;
+ private currentTimetravelOffsetMs: number | undefined;
private isRunning(): boolean {
return !!this.proc;
}
- setTimetravel(t: Duration | undefined): void {
+ setTimetravel(t: number | undefined): void {
if (this.isRunning()) {
throw Error("can't set time travel while the exchange is running");
}
- this.currentTimetravel = t;
+ this.currentTimetravelOffsetMs = t;
}
private get timetravelArg(): string | undefined {
- if (this.currentTimetravel && this.currentTimetravel.d_ms !== "forever") {
+ if (this.currentTimetravelOffsetMs != null) {
// Convert to microseconds
- return `--timetravel=+${this.currentTimetravel.d_ms * 1000}`;
+ return `--timetravel=+${this.currentTimetravelOffsetMs * 1000}`;
}
return undefined;
}
@@ -1759,7 +1494,7 @@ export class MerchantService implements MerchantServiceInterface {
}
async addDefaultInstance(): Promise<void> {
- return await this.addInstance({
+ return await this.addInstanceWithWireAccount({
id: "default",
name: "Default Instance",
paytoUris: [getPayto("merchant-default")],
@@ -1769,13 +1504,16 @@ export class MerchantService implements MerchantServiceInterface {
});
}
- async addInstance(
+ /**
+ * Add an instance together with a wire account.
+ */
+ async addInstanceWithWireAccount(
instanceConfig: PartialMerchantInstanceConfig,
): Promise<void> {
if (!this.proc) {
throw Error("merchant must be running to add instance");
}
- logger.info("adding instance");
+ logger.info(`adding instance '${instanceConfig.id}'`);
const url = `http://localhost:${this.merchantConfig.httpPort}/management/instances`;
const auth = instanceConfig.auth ?? { method: "external" };
@@ -1801,12 +1539,20 @@ export class MerchantService implements MerchantServiceInterface {
instanceConfig.defaultPayDelay ??
Duration.toTalerProtocolDuration(Duration.getForever()),
};
- const httpLib = createPlatformHttpLib({
- allowHttp: true,
- enableThrottling: false,
- });
- const resp = await httpLib.fetch(url, { method: "POST", body });
+ const resp = await harnessHttpLib.fetch(url, { method: "POST", body });
await expectSuccessResponseOrThrow(resp);
+
+ 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 acctResp = await harnessHttpLib.fetch(accountCreateUrl, {
+ method: "POST",
+ body: accountReq,
+ });
+ await expectSuccessResponseOrThrow(acctResp);
+ }
}
makeInstanceBaseUrl(instanceName?: string): string {
@@ -1823,66 +1569,6 @@ export class MerchantService implements MerchantServiceInterface {
}
}
-export interface MerchantAuthConfiguration {
- method: "external" | "token";
- token?: string;
-}
-
-// FIXME: Why do we need this? Describe / fix!
-export interface PartialMerchantInstanceConfig {
- auth?: MerchantAuthConfiguration;
- id: string;
- name: string;
- paytoUris: string[];
- address?: unknown;
- jurisdiction?: unknown;
- defaultWireTransferDelay?: TalerProtocolDuration;
- defaultPayDelay?: TalerProtocolDuration;
-}
-
-// FIXME: Move all these types into merchant-api-types.ts!
-
-type FacadeCredentials = NoFacadeCredentials | BasicAuthFacadeCredentials;
-interface NoFacadeCredentials {
- type: "none";
-}
-interface BasicAuthFacadeCredentials {
- type: "basic";
-
- // Username to use to authenticate
- username: string;
-
- // Password to use to authenticate
- password: string;
-}
-
-interface MerchantBankAccount {
- // The payto:// URI where the wallet will send coins.
- payto_uri: string;
-
- // Optional base URL for a facade where the
- // merchant backend can see incoming wire
- // transfers to reconcile its accounting
- // with that of the exchange. Used by
- // taler-merchant-wirewatch.
- credit_facade_url?: string;
-
- // Credentials for accessing the credit facade.
- credit_facade_credentials?: FacadeCredentials;
-}
-
-export interface MerchantInstanceConfig {
- accounts: MerchantBankAccount[];
- auth: MerchantAuthConfiguration;
- id: string;
- name: string;
- address: unknown;
- jurisdiction: unknown;
- use_stefan: boolean;
- default_wire_transfer_delay: TalerProtocolDuration;
- default_pay_delay: TalerProtocolDuration;
-}
-
type TestStatus = "pass" | "fail" | "skip";
export interface TestRunResult {
diff --git a/packages/taler-harness/src/harness/helpers.ts b/packages/taler-harness/src/harness/helpers.ts
index d1d0ea104..9892e600b 100644
--- a/packages/taler-harness/src/harness/helpers.ts
+++ b/packages/taler-harness/src/harness/helpers.ts
@@ -25,21 +25,18 @@
*/
import {
AmountString,
+ BankAccessApiClient,
ConfirmPayResultType,
- MerchantContractTerms,
Duration,
- PreparePayResultType,
+ Logger,
+ MerchantApiClient,
+ MerchantContractTerms,
NotificationType,
- WalletNotification,
+ PreparePayResultType,
TransactionMajorState,
- Logger,
+ WalletNotification,
} from "@gnu-taler/taler-util";
-import {
- BankAccessApi,
- BankApi,
- HarnessExchangeBankAccount,
- WalletApiOperation,
-} from "@gnu-taler/taler-wallet-core";
+import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
import { CoinConfig, defaultCoinConfig } from "./denomStructures.js";
import {
FaultInjectedExchangeService,
@@ -51,17 +48,17 @@ import {
ExchangeService,
ExchangeServiceInterface,
FakebankService,
- getPayto,
GlobalTestState,
- MerchantPrivateApi,
+ HarnessExchangeBankAccount,
MerchantService,
MerchantServiceInterface,
- setupDb,
- setupSharedDb,
WalletCli,
WalletClient,
WalletService,
WithAuthorization,
+ getPayto,
+ setupDb,
+ setupSharedDb,
} from "./harness.js";
import * as fs from "fs";
@@ -107,114 +104,6 @@ export interface EnvOptions {
additionalBankConfig?(b: BankService): void;
}
-/**
- * Run a test case with a simple TESTKUDOS Taler environment, consisting
- * of one exchange, one bank and one merchant.
- *
- * @deprecated use {@link createSimpleTestkudosEnvironmentV2} instead
- */
-export async function createSimpleTestkudosEnvironment(
- t: GlobalTestState,
- coinConfig: CoinConfig[] = defaultCoinConfig.map((x) => x("TESTKUDOS")),
- opts: EnvOptions = {},
-): Promise<SimpleTestEnvironment> {
- const db = await setupDb(t);
-
- const bank = await BankService.create(t, {
- allowRegistrations: true,
- currency: "TESTKUDOS",
- database: db.connStr,
- httpPort: 8082,
- });
-
- const exchange = ExchangeService.create(t, {
- name: "testexchange-1",
- currency: "TESTKUDOS",
- httpPort: 8081,
- database: db.connStr,
- });
-
- const merchant = await MerchantService.create(t, {
- name: "testmerchant-1",
- currency: "TESTKUDOS",
- httpPort: 8083,
- database: db.connStr,
- });
-
- const exchangeBankAccount = await bank.createExchangeAccount(
- "myexchange",
- "x",
- );
- await exchange.addBankAccount("1", exchangeBankAccount);
-
- bank.setSuggestedExchange(exchange, exchangeBankAccount.accountPaytoUri);
-
- await bank.start();
-
- await bank.pingUntilAvailable();
-
- const ageMaskSpec = opts.ageMaskSpec;
-
- if (ageMaskSpec) {
- exchange.enableAgeRestrictions(ageMaskSpec);
- // Enable age restriction for all coins.
- exchange.addCoinConfigList(
- coinConfig.map((x) => ({
- ...x,
- name: `${x.name}-age`,
- ageRestricted: true,
- })),
- );
- // For mixed age restrictions, we also offer coins without age restrictions
- if (opts.mixedAgeRestriction) {
- exchange.addCoinConfigList(
- coinConfig.map((x) => ({ ...x, ageRestricted: false })),
- );
- }
- } else {
- exchange.addCoinConfigList(coinConfig);
- }
-
- await exchange.start();
- await exchange.pingUntilAvailable();
-
- merchant.addExchange(exchange);
-
- await merchant.start();
- await merchant.pingUntilAvailable();
-
- await merchant.addInstance({
- id: "default",
- name: "Default Instance",
- paytoUris: [getPayto("merchant-default")],
- defaultWireTransferDelay: Duration.toTalerProtocolDuration(
- Duration.fromSpec({ minutes: 1 }),
- ),
- });
-
- await merchant.addInstance({
- id: "minst1",
- name: "minst1",
- paytoUris: [getPayto("minst1")],
- defaultWireTransferDelay: Duration.toTalerProtocolDuration(
- Duration.fromSpec({ minutes: 1 }),
- ),
- });
-
- console.log("setup done!");
-
- const wallet = new WalletCli(t);
-
- return {
- commonDb: db,
- exchange,
- merchant,
- wallet,
- bank,
- exchangeBankAccount,
- };
-}
-
export function getSharedTestDir(): string {
return `/tmp/taler-harness@${process.env.USER}`;
}
@@ -344,7 +233,7 @@ export async function useSharedTestkudosEnvironment(t: GlobalTestState) {
await merchant.pingUntilAvailable();
if (!prevSetupDone) {
- await merchant.addInstance({
+ await merchant.addInstanceWithWireAccount({
id: "default",
name: "Default Instance",
paytoUris: [getPayto("merchant-default")],
@@ -353,7 +242,7 @@ export async function useSharedTestkudosEnvironment(t: GlobalTestState) {
),
});
- await merchant.addInstance({
+ await merchant.addInstanceWithWireAccount({
id: "minst1",
name: "minst1",
paytoUris: [getPayto("minst1")],
@@ -476,7 +365,7 @@ export async function createSimpleTestkudosEnvironmentV2(
await merchant.start();
await merchant.pingUntilAvailable();
- await merchant.addInstance({
+ await merchant.addInstanceWithWireAccount({
id: "default",
name: "Default Instance",
paytoUris: [getPayto("merchant-default")],
@@ -485,7 +374,7 @@ export async function createSimpleTestkudosEnvironmentV2(
),
});
- await merchant.addInstance({
+ await merchant.addInstanceWithWireAccount({
id: "minst1",
name: "minst1",
paytoUris: [getPayto("minst1")],
@@ -554,7 +443,7 @@ export interface FaultyMerchantTestEnvironment {
exchangeBankAccount: HarnessExchangeBankAccount;
merchant: MerchantService;
faultyMerchant: FaultInjectedMerchantService;
- wallet: WalletCli;
+ walletClient: WalletClient;
}
/**
@@ -620,13 +509,13 @@ export async function createFaultInjectedMerchantTestkudosEnvironment(
await merchant.start();
await merchant.pingUntilAvailable();
- await merchant.addInstance({
+ await merchant.addInstanceWithWireAccount({
id: "default",
name: "Default Instance",
paytoUris: [getPayto("merchant-default")],
});
- await merchant.addInstance({
+ await merchant.addInstanceWithWireAccount({
id: "minst1",
name: "minst1",
paytoUris: [getPayto("minst1")],
@@ -634,13 +523,15 @@ export async function createFaultInjectedMerchantTestkudosEnvironment(
console.log("setup done!");
- const wallet = new WalletCli(t);
+ const { walletClient } = await createWalletDaemonWithClient(t, {
+ name: "default",
+ });
return {
commonDb: db,
exchange,
merchant,
- wallet,
+ walletClient,
bank,
exchangeBankAccount,
faultyMerchant,
@@ -648,51 +539,6 @@ export async function createFaultInjectedMerchantTestkudosEnvironment(
};
}
-/**
- * Start withdrawing into the wallet.
- *
- * Only starts the operation, does not wait for it to finish.
- */
-export async function startWithdrawViaBank(
- t: GlobalTestState,
- p: {
- wallet: WalletCli;
- bank: BankService;
- exchange: ExchangeServiceInterface;
- amount: AmountString;
- restrictAge?: number;
- },
-): Promise<void> {
- const { wallet, bank, exchange, amount } = p;
-
- const user = await BankApi.createRandomBankUser(bank);
- const wop = await BankAccessApi.createWithdrawalOperation(bank, user, amount);
-
- // Hand it to the wallet
-
- await wallet.client.call(WalletApiOperation.GetWithdrawalDetailsForUri, {
- talerWithdrawUri: wop.taler_withdraw_uri,
- restrictAge: p.restrictAge,
- });
-
- await wallet.runPending();
-
- // Withdraw (AKA select)
-
- await wallet.client.call(WalletApiOperation.AcceptBankIntegratedWithdrawal, {
- exchangeBaseUrl: exchange.baseUrl,
- talerWithdrawUri: wop.taler_withdraw_uri,
- restrictAge: p.restrictAge,
- });
-
- // Confirm it
-
- await BankApi.confirmWithdrawalOperation(bank, user, wop);
-
- // We do *not* call runPending / runUntilDone on the wallet here.
- // Some tests rely on the final withdraw failing.
-}
-
export interface WithdrawViaBankResult {
withdrawalFinishedCond: Promise<true>;
}
@@ -714,8 +560,10 @@ export async function withdrawViaBankV2(
): Promise<WithdrawViaBankResult> {
const { walletClient: wallet, bank, exchange, amount } = p;
- const user = await BankApi.createRandomBankUser(bank);
- const wop = await BankAccessApi.createWithdrawalOperation(bank, user, amount);
+ const bankClient = new BankAccessApiClient(bank.bankAccessApiBaseUrl);
+
+ const user = await bankClient.createRandomBankUser();
+ const wop = await bankClient.createWithdrawalOperation(user.username, amount);
// Hand it to the wallet
@@ -744,140 +592,44 @@ export async function withdrawViaBankV2(
// Confirm it
- await BankApi.confirmWithdrawalOperation(bank, user, wop);
+ await bankClient.confirmWithdrawalOperation(user.username, wop);
return {
withdrawalFinishedCond,
};
}
-/**
- * Withdraw balance.
- *
- * @deprecated use {@link withdrawViaBankV2 instead}
- */
-export async function withdrawViaBank(
- t: GlobalTestState,
- p: {
- wallet: WalletCli;
- bank: BankService;
- exchange: ExchangeServiceInterface;
- amount: AmountString;
- restrictAge?: number;
- },
-): Promise<void> {
- const { wallet } = p;
-
- await startWithdrawViaBank(t, p);
-
- await wallet.runUntilDone();
-
- // Check balance
-
- await wallet.client.call(WalletApiOperation.GetBalances, {});
-}
-
-export async function applyTimeTravel(
- timetravelDuration: Duration,
+export async function applyTimeTravelV2(
+ timetravelOffsetMs: number,
s: {
exchange?: ExchangeService;
merchant?: MerchantService;
- wallet?: WalletCli;
+ walletClient?: WalletClient;
},
): Promise<void> {
if (s.exchange) {
await s.exchange.stop();
- s.exchange.setTimetravel(timetravelDuration);
+ s.exchange.setTimetravel(timetravelOffsetMs);
await s.exchange.start();
await s.exchange.pingUntilAvailable();
}
if (s.merchant) {
await s.merchant.stop();
- s.merchant.setTimetravel(timetravelDuration);
+ s.merchant.setTimetravel(timetravelOffsetMs);
await s.merchant.start();
await s.merchant.pingUntilAvailable();
}
- if (s.wallet) {
- s.wallet.setTimetravel(timetravelDuration);
+ if (s.walletClient) {
+ await s.walletClient.call(WalletApiOperation.TestingSetTimetravel, {
+ offsetMs: timetravelOffsetMs,
+ });
}
}
/**
* Make a simple payment and check that it succeeded.
- *
- * @deprecated
- */
-export async function makeTestPayment(
- t: GlobalTestState,
- args: {
- merchant: MerchantServiceInterface;
- wallet: WalletCli;
- order: Partial<MerchantContractTerms>;
- instance?: string;
- },
- auth: WithAuthorization = {},
-): Promise<void> {
- // Set up order.
-
- const { wallet, merchant } = args;
- const instance = args.instance ?? "default";
-
- const orderResp = await MerchantPrivateApi.createOrder(
- merchant,
- instance,
- {
- order: args.order,
- },
- auth,
- );
-
- let orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus(
- merchant,
- {
- orderId: orderResp.order_id,
- },
- auth,
- );
-
- t.assertTrue(orderStatus.order_status === "unpaid");
-
- // Make wallet pay for the order
-
- const preparePayResult = await wallet.client.call(
- WalletApiOperation.PreparePayForUri,
- {
- talerPayUri: orderStatus.taler_pay_uri,
- },
- );
-
- t.assertTrue(
- preparePayResult.status === PreparePayResultType.PaymentPossible,
- );
-
- const r2 = await wallet.client.call(WalletApiOperation.ConfirmPay, {
- proposalId: preparePayResult.proposalId,
- });
-
- t.assertTrue(r2.type === ConfirmPayResultType.Done);
-
- // Check if payment was successful.
-
- orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus(
- merchant,
- {
- orderId: orderResp.order_id,
- instance,
- },
- auth,
- );
-
- t.assertTrue(orderStatus.order_status === "paid");
-}
-
-/**
- * Make a simple payment and check that it succeeded.
*/
export async function makeTestPaymentV2(
t: GlobalTestState,
@@ -891,25 +643,19 @@ export async function makeTestPaymentV2(
): Promise<void> {
// Set up order.
- const { walletClient, merchant } = args;
- const instance = args.instance ?? "default";
+ const { walletClient, merchant, instance } = args;
- const orderResp = await MerchantPrivateApi.createOrder(
- merchant,
- instance,
- {
- order: args.order,
- },
- auth,
+ const merchantClient = new MerchantApiClient(
+ merchant.makeInstanceBaseUrl(instance),
);
- let orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus(
- merchant,
- {
- orderId: orderResp.order_id,
- },
- auth,
- );
+ const orderResp = await merchantClient.createOrder({
+ order: args.order,
+ });
+
+ let orderStatus = await merchantClient.queryPrivateOrderStatus({
+ orderId: orderResp.order_id,
+ });
t.assertTrue(orderStatus.order_status === "unpaid");
@@ -934,14 +680,10 @@ export async function makeTestPaymentV2(
// Check if payment was successful.
- orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus(
- merchant,
- {
- orderId: orderResp.order_id,
- instance,
- },
- auth,
- );
+ orderStatus = await merchantClient.queryPrivateOrderStatus({
+ orderId: orderResp.order_id,
+ instance,
+ });
t.assertTrue(orderStatus.order_status === "paid");
}
diff --git a/packages/taler-harness/src/harness/libeufin-apis.ts b/packages/taler-harness/src/harness/libeufin-apis.ts
index 3c57eee07..0193f9252 100644
--- a/packages/taler-harness/src/harness/libeufin-apis.ts
+++ b/packages/taler-harness/src/harness/libeufin-apis.ts
@@ -176,7 +176,7 @@ export interface LibeufinSandboxAddIncomingRequest {
direction: string;
}
-const libeufinHttpLib = createPlatformHttpLib();
+const libeufinHarnessHttpLib = createPlatformHttpLib();
/**
* APIs spread across Legacy and Access, it is therefore
@@ -192,7 +192,7 @@ export namespace LibeufinSandboxApi {
iban: string | null = null,
): Promise<void> {
let url = new URL("testing/register", libeufinSandboxService.baseUrl);
- await libeufinHttpLib.fetch(url.href, {
+ await libeufinHarnessHttpLib.fetch(url.href, {
method: "POST",
body: {
username: username,
@@ -211,7 +211,7 @@ export namespace LibeufinSandboxApi {
): Promise<void> {
// baseUrl should already be pointed to one demobank.
let url = new URL("ebics/subscribers", libeufinSandboxService.baseUrl);
- await libeufinHttpLib.fetch(url.href, {
+ await libeufinHarnessHttpLib.fetch(url.href, {
method: "POST",
body: {
userID: req.userID,
@@ -228,7 +228,7 @@ export namespace LibeufinSandboxApi {
): Promise<void> {
const baseUrl = libeufinSandboxService.baseUrl;
let url = new URL(`admin/ebics/hosts/${hostID}/rotate-keys`, baseUrl);
- await libeufinHttpLib.fetch(url.href, {
+ await libeufinHarnessHttpLib.fetch(url.href, {
method: "POST",
body: {},
});
@@ -239,7 +239,7 @@ export namespace LibeufinSandboxApi {
): Promise<void> {
const baseUrl = libeufinSandboxService.baseUrl;
let url = new URL("admin/ebics/hosts", baseUrl);
- await libeufinHttpLib.fetch(url.href, {
+ await libeufinHarnessHttpLib.fetch(url.href, {
method: "POST",
body: {
hostID,
@@ -255,7 +255,7 @@ export namespace LibeufinSandboxApi {
): Promise<void> {
const baseUrl = libeufinSandboxService.baseUrl;
let url = new URL(`admin/bank-accounts/${req.label}`, baseUrl);
- await libeufinHttpLib.fetch(url.href, {
+ await libeufinHarnessHttpLib.fetch(url.href, {
method: "POST",
body: req,
headers: { Authorization: makeBasicAuthHeader("admin", "secret") },
@@ -272,7 +272,7 @@ export namespace LibeufinSandboxApi {
): Promise<void> {
const baseUrl = libeufinSandboxService.baseUrl;
let url = new URL("admin/ebics/subscribers", baseUrl);
- await libeufinHttpLib.fetch(url.href, {
+ await libeufinHarnessHttpLib.fetch(url.href, {
method: "POST",
body: req,
headers: { Authorization: makeBasicAuthHeader("admin", "secret") },
@@ -289,7 +289,7 @@ export namespace LibeufinSandboxApi {
): Promise<void> {
const baseUrl = libeufinSandboxService.baseUrl;
let url = new URL("admin/ebics/bank-accounts", baseUrl);
- await libeufinHttpLib.fetch(url.href, {
+ await libeufinHarnessHttpLib.fetch(url.href, {
method: "POST",
body: req,
headers: { Authorization: makeBasicAuthHeader("admin", "secret") },
@@ -306,7 +306,7 @@ export namespace LibeufinSandboxApi {
`admin/bank-accounts/${accountLabel}/simulate-incoming-transaction`,
baseUrl,
);
- await libeufinHttpLib.fetch(url.href, {
+ await libeufinHarnessHttpLib.fetch(url.href, {
method: "POST",
body: req,
headers: { Authorization: makeBasicAuthHeader("admin", "secret") },
@@ -322,7 +322,7 @@ export namespace LibeufinSandboxApi {
`admin/bank-accounts/${accountLabel}/transactions`,
baseUrl,
);
- const res = await libeufinHttpLib.fetch(url.href, {
+ const res = await libeufinHarnessHttpLib.fetch(url.href, {
headers: { Authorization: makeBasicAuthHeader("admin", "secret") },
});
return (await res.json()) as SandboxAccountTransactions;
@@ -334,7 +334,7 @@ export namespace LibeufinSandboxApi {
): Promise<any> {
const baseUrl = libeufinSandboxService.baseUrl;
let url = new URL("admin/payments/camt", baseUrl);
- return await libeufinHttpLib.fetch(url.href, {
+ return await libeufinHarnessHttpLib.fetch(url.href, {
method: "POST",
headers: { Authorization: makeBasicAuthHeader("admin", "secret") },
body: {
@@ -350,7 +350,7 @@ export namespace LibeufinSandboxApi {
): Promise<LibeufinSandboxAdminBankAccountBalance> {
const baseUrl = libeufinSandboxService.baseUrl;
let url = new URL(`admin/bank-accounts/${accountLabel}`, baseUrl);
- const res = await libeufinHttpLib.fetch(url.href, {
+ const res = await libeufinHarnessHttpLib.fetch(url.href, {
headers: { Authorization: makeBasicAuthHeader("admin", "secret") },
});
return res.json();
@@ -362,7 +362,7 @@ export namespace LibeufinNexusApi {
nexus: LibeufinNexusServiceInterface,
): Promise<NexusBankConnections> {
let url = new URL("bank-connections", nexus.baseUrl);
- const res = await libeufinHttpLib.fetch(url.href, {
+ const res = await libeufinHarnessHttpLib.fetch(url.href, {
headers: { Authorization: makeBasicAuthHeader("admin", "secret") },
});
return res.json();
@@ -374,7 +374,7 @@ export namespace LibeufinNexusApi {
): Promise<void> {
const baseUrl = libeufinNexusService.baseUrl;
let url = new URL("bank-connections/delete-connection", baseUrl);
- await libeufinHttpLib.fetch(url.href, {
+ await libeufinHarnessHttpLib.fetch(url.href, {
method: "POST",
headers: { Authorization: makeBasicAuthHeader("admin", "secret") },
body: req,
@@ -387,7 +387,7 @@ export namespace LibeufinNexusApi {
): Promise<void> {
const baseUrl = libeufinNexusService.baseUrl;
let url = new URL("bank-connections", baseUrl);
- await libeufinHttpLib.fetch(url.href, {
+ await libeufinHarnessHttpLib.fetch(url.href, {
method: "POST",
headers: { Authorization: makeBasicAuthHeader("admin", "secret") },
body: {
@@ -411,7 +411,7 @@ export namespace LibeufinNexusApi {
): Promise<any> {
const baseUrl = libeufinNexusService.baseUrl;
let url = new URL(`bank-accounts/${accountName}`, baseUrl);
- const resp = await libeufinHttpLib.fetch(url.href, {
+ const resp = await libeufinHarnessHttpLib.fetch(url.href, {
headers: { Authorization: makeBasicAuthHeader("admin", "secret") },
});
return resp.json();
@@ -427,7 +427,7 @@ export namespace LibeufinNexusApi {
`bank-accounts/${accountName}/payment-initiations/${paymentId}/submit`,
baseUrl,
);
- await libeufinHttpLib.fetch(url.href, {
+ await libeufinHarnessHttpLib.fetch(url.href, {
method: "POST",
headers: { Authorization: makeBasicAuthHeader("admin", "secret") },
body: {},
@@ -443,7 +443,7 @@ export namespace LibeufinNexusApi {
`bank-connections/${connectionName}/fetch-accounts`,
baseUrl,
);
- await libeufinHttpLib.fetch(url.href, {
+ await libeufinHarnessHttpLib.fetch(url.href, {
method: "POST",
headers: { Authorization: makeBasicAuthHeader("admin", "secret") },
body: {},
@@ -461,7 +461,7 @@ export namespace LibeufinNexusApi {
`bank-connections/${connectionName}/import-account`,
baseUrl,
);
- await libeufinHttpLib.fetch(url.href, {
+ await libeufinHarnessHttpLib.fetch(url.href, {
method: "POST",
headers: { Authorization: makeBasicAuthHeader("admin", "secret") },
body: {
@@ -477,7 +477,7 @@ export namespace LibeufinNexusApi {
): Promise<void> {
const baseUrl = libeufinNexusService.baseUrl;
let url = new URL(`bank-connections/${connectionName}/connect`, baseUrl);
- await libeufinHttpLib.fetch(url.href, {
+ await libeufinHarnessHttpLib.fetch(url.href, {
method: "POST",
headers: { Authorization: makeBasicAuthHeader("admin", "secret") },
body: {},
@@ -495,7 +495,7 @@ export namespace LibeufinNexusApi {
`/bank-accounts/${accountName}/payment-initiations`,
baseUrl,
);
- let response = await libeufinHttpLib.fetch(url.href, {
+ let response = await libeufinHarnessHttpLib.fetch(url.href, {
headers: { Authorization: makeBasicAuthHeader("admin", "secret") },
});
const respJson = await response.json();
@@ -518,7 +518,7 @@ export namespace LibeufinNexusApi {
for (const [k, v] of Object.entries(params)) {
url.searchParams.set(k, String(v));
}
- let response = await libeufinHttpLib.fetch(url.href, {
+ let response = await libeufinHarnessHttpLib.fetch(url.href, {
headers: { Authorization: makeBasicAuthHeader("admin", "secret") },
});
return response.json();
@@ -534,7 +534,7 @@ export namespace LibeufinNexusApi {
): Promise<LibeufinNexusTransactions> {
const baseUrl = libeufinNexusService.baseUrl;
let url = new URL(`/bank-accounts/${accountName}/transactions`, baseUrl);
- let response = await libeufinHttpLib.fetch(url.href, {
+ let response = await libeufinHarnessHttpLib.fetch(url.href, {
headers: { Authorization: makeBasicAuthHeader("admin", "secret") },
});
return response.json();
@@ -553,7 +553,7 @@ export namespace LibeufinNexusApi {
`/bank-accounts/${accountName}/fetch-transactions`,
baseUrl,
);
- const resp = await libeufinHttpLib.fetch(url.href, {
+ const resp = await libeufinHarnessHttpLib.fetch(url.href, {
method: "POST",
headers: { Authorization: makeBasicAuthHeader("admin", "secret") },
body: {
@@ -572,7 +572,7 @@ export namespace LibeufinNexusApi {
): Promise<void> {
const baseUrl = libeufinNexusService.baseUrl;
let url = new URL(`/users/${username}/password`, baseUrl);
- await libeufinHttpLib.fetch(url.href, {
+ await libeufinHarnessHttpLib.fetch(url.href, {
method: "POST",
headers: { Authorization: makeBasicAuthHeader("admin", "secret") },
body: req,
@@ -585,7 +585,7 @@ export namespace LibeufinNexusApi {
): Promise<NexusUserResponse> {
const baseUrl = libeufinNexusService.baseUrl;
let url = new URL(`/user`, baseUrl);
- const resp = await libeufinHttpLib.fetch(url.href, {
+ const resp = await libeufinHarnessHttpLib.fetch(url.href, {
headers: { Authorization: makeBasicAuthHeader("admin", "secret") },
});
return resp.json();
@@ -597,7 +597,7 @@ export namespace LibeufinNexusApi {
): Promise<void> {
const baseUrl = libeufinNexusService.baseUrl;
let url = new URL(`/users`, baseUrl);
- await libeufinHttpLib.fetch(url.href, {
+ await libeufinHarnessHttpLib.fetch(url.href, {
method: "POST",
headers: { Authorization: makeBasicAuthHeader("admin", "secret") },
body: req,
@@ -609,7 +609,7 @@ export namespace LibeufinNexusApi {
): Promise<NexusGetPermissionsResponse> {
const baseUrl = libeufinNexusService.baseUrl;
let url = new URL(`/permissions`, baseUrl);
- const resp = await libeufinHttpLib.fetch(url.href, {
+ const resp = await libeufinHarnessHttpLib.fetch(url.href, {
headers: { Authorization: makeBasicAuthHeader("admin", "secret") },
});
return resp.json();
@@ -621,7 +621,7 @@ export namespace LibeufinNexusApi {
): Promise<void> {
const baseUrl = libeufinNexusService.baseUrl;
let url = new URL(`/permissions`, baseUrl);
- await libeufinHttpLib.fetch(url.href, {
+ await libeufinHarnessHttpLib.fetch(url.href, {
method: "POST",
headers: { Authorization: makeBasicAuthHeader("admin", "secret") },
body: req,
@@ -634,7 +634,7 @@ export namespace LibeufinNexusApi {
): Promise<NexusTaskCollection> {
const baseUrl = libeufinNexusService.baseUrl;
let url = new URL(`/bank-accounts/${bankAccountName}/schedule`, baseUrl);
- const resp = await libeufinHttpLib.fetch(url.href, {
+ const resp = await libeufinHarnessHttpLib.fetch(url.href, {
headers: { Authorization: makeBasicAuthHeader("admin", "secret") },
});
return resp.json();
@@ -653,7 +653,7 @@ export namespace LibeufinNexusApi {
baseUrl,
);
if (taskName) url = new URL(taskName, `${url.href}/`);
- const resp = await libeufinHttpLib.fetch(url.href, {
+ const resp = await libeufinHarnessHttpLib.fetch(url.href, {
headers: { Authorization: makeBasicAuthHeader("admin", "secret") },
});
return resp.json();
@@ -669,7 +669,7 @@ export namespace LibeufinNexusApi {
`/bank-accounts/${bankAccountName}/schedule/${taskName}`,
baseUrl,
);
- await libeufinHttpLib.fetch(url.href, {
+ await libeufinHarnessHttpLib.fetch(url.href, {
method: "DELETE",
headers: { Authorization: makeBasicAuthHeader("admin", "secret") },
});
@@ -682,7 +682,7 @@ export namespace LibeufinNexusApi {
): Promise<void> {
const baseUrl = libeufinNexusService.baseUrl;
let url = new URL(`/bank-accounts/${bankAccountName}/schedule`, baseUrl);
- await libeufinHttpLib.fetch(url.href, {
+ await libeufinHarnessHttpLib.fetch(url.href, {
method: "POST",
headers: { Authorization: makeBasicAuthHeader("admin", "secret") },
body: req,
@@ -695,7 +695,7 @@ export namespace LibeufinNexusApi {
): Promise<void> {
const baseUrl = libeufinNexusService.baseUrl;
let url = new URL(`facades/${facadeName}`, baseUrl);
- await libeufinHttpLib.fetch(url.href, {
+ await libeufinHarnessHttpLib.fetch(url.href, {
method: "DELETE",
headers: { Authorization: makeBasicAuthHeader("admin", "secret") },
});
@@ -706,7 +706,7 @@ export namespace LibeufinNexusApi {
): Promise<NexusFacadeListResponse> {
const baseUrl = libeufinNexusService.baseUrl;
let url = new URL("facades", baseUrl);
- const resp = await libeufinHttpLib.fetch(url.href, {
+ const resp = await libeufinHarnessHttpLib.fetch(url.href, {
headers: { Authorization: makeBasicAuthHeader("admin", "secret") },
});
// FIXME: Just return validated, typed response here!
@@ -719,7 +719,7 @@ export namespace LibeufinNexusApi {
): Promise<void> {
const baseUrl = libeufinNexusService.baseUrl;
let url = new URL("facades", baseUrl);
- await libeufinHttpLib.fetch(url.href, {
+ await libeufinHarnessHttpLib.fetch(url.href, {
method: "POST",
headers: { Authorization: makeBasicAuthHeader("admin", "secret") },
body: {
@@ -741,7 +741,7 @@ export namespace LibeufinNexusApi {
): Promise<void> {
const baseUrl = libeufinNexusService.baseUrl;
let url = new URL("facades", baseUrl);
- await libeufinHttpLib.fetch(url.href, {
+ await libeufinHarnessHttpLib.fetch(url.href, {
method: "POST",
headers: { Authorization: makeBasicAuthHeader("admin", "secret") },
body: {
@@ -766,7 +766,7 @@ export namespace LibeufinNexusApi {
`/bank-accounts/${accountId}/submit-all-payment-initiations`,
baseUrl,
);
- await libeufinHttpLib.fetch(url.href, {
+ await libeufinHarnessHttpLib.fetch(url.href, {
method: "POST",
headers: { Authorization: makeBasicAuthHeader("admin", "secret") },
body: {},
diff --git a/packages/taler-harness/src/index.ts b/packages/taler-harness/src/index.ts
index cd688ed89..f5d4fd2c2 100644
--- a/packages/taler-harness/src/index.ts
+++ b/packages/taler-harness/src/index.ts
@@ -20,17 +20,18 @@
import {
addPaytoQueryParams,
Amounts,
+ BankAccessApiClient,
Configuration,
decodeCrock,
j2s,
Logger,
+ MerchantApiClient,
rsaBlind,
setGlobalLogLevelFromString,
} from "@gnu-taler/taler-util";
import { clk } from "@gnu-taler/taler-util/clk";
import { createPlatformHttpLib } from "@gnu-taler/taler-util/http";
import {
- BankAccessApiClient,
CryptoDispatcher,
downloadExchangeInfo,
SynchronousCryptoWorkerFactoryPlain,
@@ -45,11 +46,7 @@ import { runBench2 } from "./bench2.js";
import { runBench3 } from "./bench3.js";
import { runEnvFull } from "./env-full.js";
import { runEnv1 } from "./env1.js";
-import {
- GlobalTestState,
- MerchantApiClient,
- runTestWithState,
-} from "./harness/harness.js";
+import { GlobalTestState, runTestWithState } from "./harness/harness.js";
import { getTestInfo, runTests } from "./integrationtests/testrunner.js";
import { lintExchangeDeployment } from "./lint.js";
@@ -239,14 +236,15 @@ deploymentCli
console.log(tipReserveResp);
- const bankAccessApiClient = new BankAccessApiClient({
- baseUrl: args.tipTopup.bankAccessUrl,
- auth: {
- username: args.tipTopup.bankAccount,
- password: args.tipTopup.bankPassword,
+ const bankAccessApiClient = new BankAccessApiClient(
+ args.tipTopup.bankAccessUrl,
+ {
+ auth: {
+ username: args.tipTopup.bankAccount,
+ password: args.tipTopup.bankPassword,
+ },
},
- allowHttp: true,
- });
+ );
const paytoUri = addPaytoQueryParams(tipReserveResp.accounts[0].payto_uri, {
message: `tip-reserve ${tipReserveResp.reserve_pub}`,
@@ -402,7 +400,6 @@ deploymentCli
);
const res = await merchantClient.getPrivateInstanceInfo();
- console.log(res);
const tipRes = await merchantClient.getPrivateTipReserves();
console.log(j2s(tipRes));
@@ -506,9 +503,6 @@ testingCli
if (t.suites.length > 0) {
s += ` (suites: ${t.suites.join(",")})`;
}
- if (t.excludeByDefault) {
- s += ` [excluded by default]`;
- }
if (t.experimental) {
s += ` [experimental]`;
}
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 919097deb..7f936a479 100644
--- a/packages/taler-harness/src/integrationtests/test-age-restrictions-merchant.ts
+++ b/packages/taler-harness/src/integrationtests/test-age-restrictions-merchant.ts
@@ -1,6 +1,6 @@
/*
This file is part of GNU Taler
- (C) 2022 Taler Systems S.A.
+ (C) 2022-2023 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
@@ -17,24 +17,20 @@
/**
* Imports.
*/
-import {
- BankApi,
- WalletApiOperation,
- WireGatewayApiClient,
-} from "@gnu-taler/taler-wallet-core";
+import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
import { defaultCoinConfig } from "../harness/denomStructures.js";
+import { getWireMethodForTest, GlobalTestState } from "../harness/harness.js";
import {
- getWireMethodForTest,
- GlobalTestState,
- MerchantApiClient,
- MerchantPrivateApi,
- WalletCli,
-} from "../harness/harness.js";
-import {
- createSimpleTestkudosEnvironment,
- withdrawViaBank,
- makeTestPayment,
+ createSimpleTestkudosEnvironmentV2,
+ createWalletDaemonWithClient,
+ makeTestPaymentV2,
+ withdrawViaBankV2,
} from "../harness/helpers.js";
+import {
+ BankAccessApiClient,
+ MerchantApiClient,
+ WireGatewayApiClient,
+} from "@gnu-taler/taler-util";
/**
* Run test for basic, bank-integrated withdrawal and payment.
@@ -43,12 +39,12 @@ export async function runAgeRestrictionsMerchantTest(t: GlobalTestState) {
// Set up test environment
const {
- wallet: walletOne,
+ walletClient: walletClientOne,
bank,
exchange,
merchant,
exchangeBankAccount,
- } = await createSimpleTestkudosEnvironment(
+ } = await createSimpleTestkudosEnvironmentV2(
t,
defaultCoinConfig.map((x) => x("TESTKUDOS")),
{
@@ -58,24 +54,34 @@ export async function runAgeRestrictionsMerchantTest(t: GlobalTestState) {
const merchantClient = new MerchantApiClient(
merchant.makeInstanceBaseUrl("default"),
+ );
+
+ const { walletClient: walletClientTwo } = await createWalletDaemonWithClient(
+ t,
{
- method: "external",
+ name: "w2",
},
);
- const walletTwo = new WalletCli(t, "walletTwo");
- const walletThree = new WalletCli(t, "walletThree");
+ const { walletClient: walletClientThree } =
+ await createWalletDaemonWithClient(t, {
+ name: "w3",
+ });
{
- const walletZero = new WalletCli(t, "walletZero");
+ const { walletClient: walletClientZero } =
+ await createWalletDaemonWithClient(t, {
+ name: "w0",
+ });
- await withdrawViaBank(t, {
- wallet: walletZero,
+ const wres = await withdrawViaBankV2(t, {
+ walletClient: walletClientZero,
bank,
exchange,
amount: "TESTKUDOS:20",
restrictAge: 13,
});
+ await wres.withdrawalFinishedCond;
const order = {
summary: "Buy me!",
@@ -84,20 +90,28 @@ export async function runAgeRestrictionsMerchantTest(t: GlobalTestState) {
minimum_age: 9,
};
- await makeTestPayment(t, { wallet: walletZero, merchant, order });
- await walletZero.runUntilDone();
+ await makeTestPaymentV2(t, {
+ walletClient: walletClientZero,
+ merchant,
+ order,
+ });
+ await walletClientZero.call(
+ WalletApiOperation.TestingWaitTransactionsFinal,
+ {},
+ );
}
{
- const wallet = walletOne;
+ const walletClient = walletClientOne;
- await withdrawViaBank(t, {
- wallet,
+ const wres = await withdrawViaBankV2(t, {
+ walletClient,
bank,
exchange,
amount: "TESTKUDOS:20",
restrictAge: 13,
});
+ await wres.withdrawalFinishedCond;
const order = {
summary: "Buy me!",
@@ -106,20 +120,24 @@ export async function runAgeRestrictionsMerchantTest(t: GlobalTestState) {
minimum_age: 9,
};
- await makeTestPayment(t, { wallet, merchant, order });
- await wallet.runUntilDone();
+ await makeTestPaymentV2(t, { walletClient, merchant, order });
+ await walletClient.call(
+ WalletApiOperation.TestingWaitTransactionsFinal,
+ {},
+ );
}
{
- const wallet = walletTwo;
+ const walletClient = walletClientTwo;
- await withdrawViaBank(t, {
- wallet,
+ const wres = await withdrawViaBankV2(t, {
+ walletClient,
bank,
exchange,
amount: "TESTKUDOS:20",
restrictAge: 13,
});
+ await wres.withdrawalFinishedCond;
const order = {
summary: "Buy me!",
@@ -127,19 +145,23 @@ export async function runAgeRestrictionsMerchantTest(t: GlobalTestState) {
fulfillment_url: "taler://fulfillment-success/thx",
};
- await makeTestPayment(t, { wallet, merchant, order });
- await wallet.runUntilDone();
+ await makeTestPaymentV2(t, { walletClient, merchant, order });
+ await walletClient.call(
+ WalletApiOperation.TestingWaitTransactionsFinal,
+ {},
+ );
}
{
- const wallet = walletThree;
+ const walletClient = walletClientThree;
- await withdrawViaBank(t, {
- wallet,
+ const wres = await withdrawViaBankV2(t, {
+ walletClient,
bank,
exchange,
amount: "TESTKUDOS:20",
});
+ await wres.withdrawalFinishedCond;
const order = {
summary: "Buy me!",
@@ -148,32 +170,37 @@ export async function runAgeRestrictionsMerchantTest(t: GlobalTestState) {
minimum_age: 9,
};
- await makeTestPayment(t, { wallet, merchant, order });
- await wallet.runUntilDone();
+ await makeTestPaymentV2(t, { walletClient, merchant, order });
+ await walletClient.call(
+ WalletApiOperation.TestingWaitTransactionsFinal,
+ {},
+ );
}
// Pay with coin from tipping
{
- const mbu = await BankApi.createRandomBankUser(bank);
- const tipReserveResp = await merchantClient.createTippingReserve(
- {
- exchange_url: exchange.baseUrl,
- initial_balance: "TESTKUDOS:10",
- wire_method: getWireMethodForTest(),
- },
- );
+ const bankClient = new BankAccessApiClient(bank.bankAccessApiBaseUrl);
+ const mbu = await bankClient.createRandomBankUser();
+ const tipReserveResp = await merchantClient.createTippingReserve({
+ exchange_url: exchange.baseUrl,
+ initial_balance: "TESTKUDOS:10",
+ wire_method: getWireMethodForTest(),
+ });
t.assertDeepEqual(
tipReserveResp.accounts[0].payto_uri,
exchangeBankAccount.accountPaytoUri,
);
- const wireGatewayApiClient = new WireGatewayApiClient({
- wireGatewayApiBaseUrl: exchangeBankAccount.wireGatewayApiBaseUrl,
- accountName: exchangeBankAccount.accountName,
- accountPassword: exchangeBankAccount.accountPassword,
- allowHttp: true,
- });
+ const wireGatewayApiClient = new WireGatewayApiClient(
+ exchangeBankAccount.wireGatewayApiBaseUrl,
+ {
+ auth: {
+ username: exchangeBankAccount.accountName,
+ password: exchangeBankAccount.accountPassword,
+ },
+ },
+ );
await wireGatewayApiClient.adminAddIncoming({
amount: "TESTKUDOS:10",
@@ -183,23 +210,32 @@ export async function runAgeRestrictionsMerchantTest(t: GlobalTestState) {
await exchange.runWirewatchOnce();
- const tip = await MerchantPrivateApi.giveTip(merchant, "default", {
+ const tip = await merchantClient.giveTip({
amount: "TESTKUDOS:5",
justification: "why not?",
next_url: "https://example.com/after-tip",
});
- const walletTipping = new WalletCli(t, "age-tipping");
+ const { walletClient: walletClientTipping } =
+ await createWalletDaemonWithClient(t, {
+ name: "age-tipping",
+ });
- const ptr = await walletTipping.client.call(WalletApiOperation.PrepareReward, {
- talerRewardUri: tip.taler_reward_uri,
- });
+ const ptr = await walletClientTipping.call(
+ WalletApiOperation.PrepareReward,
+ {
+ talerRewardUri: tip.taler_reward_uri,
+ },
+ );
- await walletTipping.client.call(WalletApiOperation.AcceptReward, {
+ await walletClientTipping.call(WalletApiOperation.AcceptReward, {
walletRewardId: ptr.walletRewardId,
});
- await walletTipping.runUntilDone();
+ await walletClientTipping.call(
+ WalletApiOperation.TestingWaitTransactionsFinal,
+ {},
+ );
const order = {
summary: "Buy me!",
@@ -208,8 +244,15 @@ export async function runAgeRestrictionsMerchantTest(t: GlobalTestState) {
minimum_age: 9,
};
- await makeTestPayment(t, { wallet: walletTipping, merchant, order });
- await walletTipping.runUntilDone();
+ await makeTestPaymentV2(t, {
+ walletClient: walletClientTipping,
+ merchant,
+ order,
+ });
+ await walletClientTipping.call(
+ WalletApiOperation.TestingWaitTransactionsFinal,
+ {},
+ );
}
}
diff --git a/packages/taler-harness/src/integrationtests/test-bank-api.ts b/packages/taler-harness/src/integrationtests/test-bank-api.ts
index 9ac16980b..a13ff63c7 100644
--- a/packages/taler-harness/src/integrationtests/test-bank-api.ts
+++ b/packages/taler-harness/src/integrationtests/test-bank-api.ts
@@ -17,13 +17,13 @@
/**
* Imports.
*/
-import { createEddsaKeyPair, encodeCrock } from "@gnu-taler/taler-util";
import {
- BankAccessApi,
- BankApi,
+ BankAccessApiClient,
CreditDebitIndicator,
WireGatewayApiClient,
-} from "@gnu-taler/taler-wallet-core";
+ createEddsaKeyPair,
+ encodeCrock,
+} from "@gnu-taler/taler-util";
import { defaultCoinConfig } from "../harness/denomStructures.js";
import {
BankService,
@@ -84,32 +84,34 @@ export async function runBankApiTest(t: GlobalTestState) {
await merchant.start();
await merchant.pingUntilAvailable();
- await merchant.addDefaultInstance();
- await merchant.addInstance({
- id: "minst1",
- name: "minst1",
- paytoUris: [getPayto("minst1")],
- });
- await merchant.addInstance({
+ await merchant.addInstanceWithWireAccount({
id: "default",
name: "Default Instance",
paytoUris: [getPayto("merchant-default")],
});
+ await merchant.addInstanceWithWireAccount({
+ id: "minst1",
+ name: "minst1",
+ paytoUris: [getPayto("minst1")],
+ });
+
console.log("setup done!");
- const bankUser = await BankApi.registerAccount(bank, "user1", "pw1", {});
+ const bankClient = new BankAccessApiClient(bank.bankAccessApiBaseUrl);
+
+ const bankUser = await bankClient.registerAccount("user1", "pw1");
// Make sure that registering twice results in a 409 Conflict
{
const e = await t.assertThrowsTalerErrorAsync(async () => {
- await BankApi.registerAccount(bank, "user1", "pw2", {});
+ await bankClient.registerAccount("user1", "pw2");
});
t.assertTrue(e.errorDetail.httpStatusCode === 409);
}
- let balResp = await BankAccessApi.getAccountBalance(bank, bankUser);
+ let balResp = await bankClient.getAccountBalance(bankUser.username);
console.log(balResp);
@@ -121,12 +123,15 @@ export async function runBankApiTest(t: GlobalTestState) {
const res = createEddsaKeyPair();
- const wireGatewayApiClient = new WireGatewayApiClient({
- wireGatewayApiBaseUrl: exchangeBankAccount.wireGatewayApiBaseUrl,
- accountName: exchangeBankAccount.accountName,
- accountPassword: exchangeBankAccount.accountPassword,
- allowHttp: true,
- });
+ const wireGatewayApiClient = new WireGatewayApiClient(
+ exchangeBankAccount.wireGatewayApiBaseUrl,
+ {
+ auth: {
+ username: exchangeBankAccount.accountName,
+ password: exchangeBankAccount.accountPassword,
+ },
+ },
+ );
await wireGatewayApiClient.adminAddIncoming({
amount: "TESTKUDOS:115",
@@ -134,9 +139,11 @@ export async function runBankApiTest(t: GlobalTestState) {
reservePub: encodeCrock(res.eddsaPub),
});
- balResp = await BankAccessApi.getAccountBalance(bank, bankUser);
+ balResp = await bankClient.getAccountBalance(bankUser.username);
t.assertAmountEquals(balResp.balance.amount, "TESTKUDOS:15");
t.assertTrue(
balResp.balance.credit_debit_indicator === CreditDebitIndicator.Debit,
);
}
+
+runBankApiTest.suites = ["fakebank"] \ No newline at end of file
diff --git a/packages/taler-harness/src/integrationtests/test-claim-loop.ts b/packages/taler-harness/src/integrationtests/test-claim-loop.ts
index 32706c28b..a424e0101 100644
--- a/packages/taler-harness/src/integrationtests/test-claim-loop.ts
+++ b/packages/taler-harness/src/integrationtests/test-claim-loop.ts
@@ -19,11 +19,12 @@
*/
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
import { URL } from "url";
-import { GlobalTestState, MerchantPrivateApi } from "../harness/harness.js";
+import { GlobalTestState } from "../harness/harness.js";
import {
createSimpleTestkudosEnvironmentV2,
withdrawViaBankV2,
} from "../harness/helpers.js";
+import { MerchantApiClient } from "@gnu-taler/taler-util";
/**
* Run test for the merchant's order lifecycle.
@@ -44,8 +45,10 @@ export async function runClaimLoopTest(t: GlobalTestState) {
amount: "TESTKUDOS:20",
});
+ const merchantClient = new MerchantApiClient(merchant.makeInstanceBaseUrl());
+
// Set up order.
- const orderResp = await MerchantPrivateApi.createOrder(merchant, "default", {
+ const orderResp = await merchantClient.createOrder({
order: {
summary: "Buy me!",
amount: "TESTKUDOS:5",
@@ -54,12 +57,9 @@ export async function runClaimLoopTest(t: GlobalTestState) {
});
// Query private order status before claiming it.
- let orderStatusBefore = await MerchantPrivateApi.queryPrivateOrderStatus(
- merchant,
- {
- orderId: orderResp.order_id,
- },
- );
+ let orderStatusBefore = await merchantClient.queryPrivateOrderStatus({
+ orderId: orderResp.order_id,
+ });
t.assertTrue(orderStatusBefore.order_status === "unpaid");
let statusUrlBefore = new URL(orderStatusBefore.order_status_url);
@@ -71,13 +71,12 @@ export async function runClaimLoopTest(t: GlobalTestState) {
});
// Query private order status after claiming it.
- let orderStatusAfter = await MerchantPrivateApi.queryPrivateOrderStatus(
- merchant,
- {
- orderId: orderResp.order_id,
- },
- );
+ let orderStatusAfter = await merchantClient.queryPrivateOrderStatus({
+ orderId: orderResp.order_id,
+ });
t.assertTrue(orderStatusAfter.order_status === "claimed");
await t.shutdown();
}
+
+runClaimLoopTest.suites = ["merchant"]; \ No newline at end of file
diff --git a/packages/taler-harness/src/integrationtests/test-clause-schnorr.ts b/packages/taler-harness/src/integrationtests/test-clause-schnorr.ts
index bf42dc4c6..a5ad382a7 100644
--- a/packages/taler-harness/src/integrationtests/test-clause-schnorr.ts
+++ b/packages/taler-harness/src/integrationtests/test-clause-schnorr.ts
@@ -17,12 +17,13 @@
/**
* Imports.
*/
+import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
import { CoinConfig, defaultCoinConfig } from "../harness/denomStructures.js";
import { GlobalTestState } from "../harness/harness.js";
import {
- createSimpleTestkudosEnvironment,
- withdrawViaBank,
- makeTestPayment,
+ createSimpleTestkudosEnvironmentV2,
+ makeTestPaymentV2,
+ withdrawViaBankV2,
} from "../harness/helpers.js";
/**
@@ -53,12 +54,18 @@ export async function runClauseSchnorrTest(t: GlobalTestState) {
name: "rsa_dummy",
});
- const { wallet, bank, exchange, merchant } =
- await createSimpleTestkudosEnvironment(t, coinConfig);
+ const { walletClient, bank, exchange, merchant } =
+ await createSimpleTestkudosEnvironmentV2(t, coinConfig);
// Withdraw digital cash into the wallet.
- await withdrawViaBank(t, { wallet, bank, exchange, amount: "TESTKUDOS:20" });
+ const wres = await withdrawViaBankV2(t, {
+ walletClient,
+ bank,
+ exchange,
+ amount: "TESTKUDOS:20",
+ });
+ await wres.withdrawalFinishedCond;
const order = {
summary: "Buy me!",
@@ -66,8 +73,8 @@ export async function runClauseSchnorrTest(t: GlobalTestState) {
fulfillment_url: "taler://fulfillment-success/thx",
};
- await makeTestPayment(t, { wallet, merchant, order });
- await wallet.runUntilDone();
+ await makeTestPaymentV2(t, { walletClient, merchant, order });
+ await walletClient.call(WalletApiOperation.TestingWaitTransactionsFinal, {});
// Test JSON normalization of contract terms: Does the wallet
// agree with the merchant?
@@ -77,8 +84,8 @@ export async function runClauseSchnorrTest(t: GlobalTestState) {
fulfillment_url: "taler://fulfillment-success/thx",
};
- await makeTestPayment(t, { wallet, merchant, order: order2 });
- await wallet.runUntilDone();
+ await makeTestPaymentV2(t, { walletClient, merchant, order: order2 });
+ await walletClient.call(WalletApiOperation.TestingWaitTransactionsFinal, {});
// Test JSON normalization of contract terms: Does the wallet
// agree with the merchant?
@@ -88,10 +95,9 @@ export async function runClauseSchnorrTest(t: GlobalTestState) {
fulfillment_url: "taler://fulfillment-success/thx",
};
- await makeTestPayment(t, { wallet, merchant, order: order3 });
-
- await wallet.runUntilDone();
+ await makeTestPaymentV2(t, { walletClient, merchant, order: order3 });
+ await walletClient.call(WalletApiOperation.TestingWaitTransactionsFinal, {});
}
runClauseSchnorrTest.suites = ["experimental-wallet"];
-runClauseSchnorrTest.excludeByDefault = true;
+runClauseSchnorrTest.experimental = true;
diff --git a/packages/taler-harness/src/integrationtests/test-denom-unoffered.ts b/packages/taler-harness/src/integrationtests/test-denom-unoffered.ts
index 5a471b9aa..b4268ee42 100644
--- a/packages/taler-harness/src/integrationtests/test-denom-unoffered.ts
+++ b/packages/taler-harness/src/integrationtests/test-denom-unoffered.ts
@@ -17,9 +17,13 @@
/**
* Imports.
*/
-import { PreparePayResultType, TalerErrorCode } from "@gnu-taler/taler-util";
+import {
+ MerchantApiClient,
+ PreparePayResultType,
+ TalerErrorCode,
+} from "@gnu-taler/taler-util";
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
-import { GlobalTestState, MerchantPrivateApi } from "../harness/harness.js";
+import { GlobalTestState } from "../harness/harness.js";
import {
createSimpleTestkudosEnvironmentV2,
withdrawViaBankV2,
@@ -46,13 +50,14 @@ export async function runDenomUnofferedTest(t: GlobalTestState) {
// Effectively we completely reset the exchange,
// but keep the exchange master public key.
+ await merchant.stop();
+
await exchange.stop();
await exchange.purgeDatabase();
await exchange.purgeSecmodKeys();
await exchange.start();
await exchange.pingUntilAvailable();
- await merchant.stop();
await merchant.start();
await merchant.pingUntilAvailable();
@@ -62,11 +67,13 @@ export async function runDenomUnofferedTest(t: GlobalTestState) {
fulfillment_url: "taler://fulfillment-success/thx",
};
- const orderResp = await MerchantPrivateApi.createOrder(merchant, "default", {
+ const merchantClient = new MerchantApiClient(merchant.makeInstanceBaseUrl());
+
+ const orderResp = await merchantClient.createOrder({
order: order,
});
- let orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus(merchant, {
+ let orderStatus = await merchantClient.queryPrivateOrderStatus({
orderId: orderResp.order_id,
});
diff --git a/packages/taler-harness/src/integrationtests/test-deposit.ts b/packages/taler-harness/src/integrationtests/test-deposit.ts
index 8ea3fc12e..7e1bb2a5c 100644
--- a/packages/taler-harness/src/integrationtests/test-deposit.ts
+++ b/packages/taler-harness/src/integrationtests/test-deposit.ts
@@ -102,3 +102,5 @@ export async function runDepositTest(t: GlobalTestState) {
// deposit and wire fees.
t.assertDeepEqual(transactions.transactions[1].amountRaw, "TESTKUDOS:9.79");
}
+
+runDepositTest.suites = ["wallet"];
diff --git a/packages/taler-harness/src/integrationtests/test-exchange-deposit.ts b/packages/taler-harness/src/integrationtests/test-exchange-deposit.ts
index 05bbbfaa1..96255f5b5 100644
--- a/packages/taler-harness/src/integrationtests/test-exchange-deposit.ts
+++ b/packages/taler-harness/src/integrationtests/test-exchange-deposit.ts
@@ -49,7 +49,6 @@ export async function runExchangeDepositTest(t: GlobalTestState) {
const { bank, exchange } = await createSimpleTestkudosEnvironmentV2(t);
const http = createPlatformHttpLib({
- allowHttp: true,
enableThrottling: false,
});
const cryptiDisp = new CryptoDispatcher(
diff --git a/packages/taler-harness/src/integrationtests/test-exchange-management.ts b/packages/taler-harness/src/integrationtests/test-exchange-management.ts
index c67f861e1..9338a8988 100644
--- a/packages/taler-harness/src/integrationtests/test-exchange-management.ts
+++ b/packages/taler-harness/src/integrationtests/test-exchange-management.ts
@@ -18,30 +18,27 @@
* Imports.
*/
import {
- GlobalTestState,
- WalletCli,
- setupDb,
- BankService,
- ExchangeService,
- MerchantService,
- getPayto,
-} from "../harness/harness.js";
-import {
- WalletApiOperation,
- BankApi,
- BankAccessApi,
-} from "@gnu-taler/taler-wallet-core";
-import {
+ BankAccessApiClient,
ExchangesListResponse,
- URL,
TalerErrorCode,
+ URL,
j2s,
} from "@gnu-taler/taler-util";
+import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
+import { defaultCoinConfig } from "../harness/denomStructures.js";
import {
FaultInjectedExchangeService,
FaultInjectionResponseContext,
} from "../harness/faultInjection.js";
-import { defaultCoinConfig } from "../harness/denomStructures.js";
+import {
+ BankService,
+ ExchangeService,
+ GlobalTestState,
+ MerchantService,
+ WalletCli,
+ getPayto,
+ setupDb,
+} from "../harness/harness.js";
/**
* Test if the wallet handles outdated exchange versions correctly.
@@ -105,13 +102,13 @@ export async function runExchangeManagementTest(
await merchant.start();
await merchant.pingUntilAvailable();
- await merchant.addInstance({
+ await merchant.addInstanceWithWireAccount({
id: "default",
name: "Default Instance",
paytoUris: [getPayto("merchant-default")],
});
- await merchant.addInstance({
+ await merchant.addInstanceWithWireAccount({
id: "minst1",
name: "minst1",
paytoUris: [getPayto("minst1")],
@@ -266,10 +263,11 @@ export async function runExchangeManagementTest(
// Create withdrawal operation
- const user = await BankApi.createRandomBankUser(bank);
- const wop = await BankAccessApi.createWithdrawalOperation(
- bank,
- user,
+ const bankClient = new BankAccessApiClient(bank.bankAccessApiBaseUrl);
+
+ const user = await bankClient.createRandomBankUser();
+ const wop = await bankClient.createWithdrawalOperation(
+ user.username,
"TESTKUDOS:10",
);
diff --git a/packages/taler-harness/src/integrationtests/test-exchange-timetravel.ts b/packages/taler-harness/src/integrationtests/test-exchange-timetravel.ts
index 5ae97c3da..2ef7683b3 100644
--- a/packages/taler-harness/src/integrationtests/test-exchange-timetravel.ts
+++ b/packages/taler-harness/src/integrationtests/test-exchange-timetravel.ts
@@ -41,7 +41,11 @@ import {
setupDb,
WalletCli,
} from "../harness/harness.js";
-import { withdrawViaBank } from "../harness/helpers.js";
+import {
+ applyTimeTravelV2,
+ createWalletDaemonWithClient,
+ withdrawViaBankV2,
+} from "../harness/helpers.js";
const logger = new Logger("test-exchange-timetravel.ts");
@@ -89,36 +93,7 @@ function getDenomInfoFromKeys(ek: ExchangeKeysJson): DenomInfo[] {
return denomInfos;
}
-async function applyTimeTravel(
- timetravelDuration: Duration,
- s: {
- exchange?: ExchangeService;
- merchant?: MerchantService;
- wallet?: WalletCli;
- },
-): Promise<void> {
- if (s.exchange) {
- await s.exchange.stop();
- s.exchange.setTimetravel(timetravelDuration);
- await s.exchange.start();
- await s.exchange.pingUntilAvailable();
- }
-
- if (s.merchant) {
- await s.merchant.stop();
- s.merchant.setTimetravel(timetravelDuration);
- await s.merchant.start();
- await s.merchant.pingUntilAvailable();
- }
-
- if (s.wallet) {
- console.log("setting wallet time travel to", timetravelDuration);
- s.wallet.setTimetravel(timetravelDuration);
- }
-}
-
const http = createPlatformHttpLib({
- allowHttp: true,
enableThrottling: false,
});
@@ -173,13 +148,13 @@ export async function runExchangeTimetravelTest(t: GlobalTestState) {
await merchant.start();
await merchant.pingUntilAvailable();
- await merchant.addInstance({
+ await merchant.addInstanceWithWireAccount({
id: "default",
name: "Default Instance",
paytoUris: [getPayto("merchant-default")],
});
- await merchant.addInstance({
+ await merchant.addInstanceWithWireAccount({
id: "minst1",
name: "minst1",
paytoUris: [getPayto("minst1")],
@@ -187,11 +162,19 @@ export async function runExchangeTimetravelTest(t: GlobalTestState) {
console.log("setup done!");
- const wallet = new WalletCli(t);
+ const { walletClient } = await createWalletDaemonWithClient(t, {
+ name: "default",
+ });
// Withdraw digital cash into the wallet.
- await withdrawViaBank(t, { wallet, bank, exchange, amount: "TESTKUDOS:15" });
+ const wres = await withdrawViaBankV2(t, {
+ walletClient,
+ bank,
+ exchange,
+ amount: "TESTKUDOS:15",
+ });
+ await wres.withdrawalFinishedCond;
const keysResp1 = await http.fetch(exchange.baseUrl + "keys");
const keys1 = await readSuccessResponseJsonOrThrow(
@@ -206,11 +189,14 @@ export async function runExchangeTimetravelTest(t: GlobalTestState) {
// Travel into the future, the deposit expiration is two years
// into the future.
console.log("applying first time travel");
- await applyTimeTravel(durationFromSpec({ days: 400 }), {
- wallet,
- exchange,
- merchant,
- });
+ await applyTimeTravelV2(
+ Duration.toMilliseconds(durationFromSpec({ days: 400 })),
+ {
+ walletClient,
+ exchange,
+ merchant,
+ },
+ );
const keysResp2 = await http.fetch(exchange.baseUrl + "keys");
const keys2 = await readSuccessResponseJsonOrThrow(
diff --git a/packages/taler-harness/src/integrationtests/test-fee-regression.ts b/packages/taler-harness/src/integrationtests/test-fee-regression.ts
index e0dc4bc3b..2d84b3a7c 100644
--- a/packages/taler-harness/src/integrationtests/test-fee-regression.ts
+++ b/packages/taler-harness/src/integrationtests/test-fee-regression.ts
@@ -139,7 +139,7 @@ export async function createMyTestkudosEnvironment(
await merchant.pingUntilAvailable();
await merchant.addDefaultInstance();
- await merchant.addInstance({
+ await merchant.addInstanceWithWireAccount({
id: "minst1",
name: "minst1",
paytoUris: [getPayto("minst1")],
diff --git a/packages/taler-harness/src/integrationtests/test-kyc.ts b/packages/taler-harness/src/integrationtests/test-kyc.ts
index 0fe24d708..1f7358b66 100644
--- a/packages/taler-harness/src/integrationtests/test-kyc.ts
+++ b/packages/taler-harness/src/integrationtests/test-kyc.ts
@@ -18,6 +18,7 @@
* Imports.
*/
import {
+ BankAccessApiClient,
Duration,
j2s,
Logger,
@@ -26,11 +27,9 @@ import {
TransactionMinorState,
TransactionType,
} from "@gnu-taler/taler-util";
-import {
- BankAccessApi,
- BankApi,
- WalletApiOperation,
-} from "@gnu-taler/taler-wallet-core";
+import { createPlatformHttpLib } from "@gnu-taler/taler-util/http";
+import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
+import * as http from "node:http";
import { CoinConfig, defaultCoinConfig } from "../harness/denomStructures.js";
import {
BankService,
@@ -43,8 +42,6 @@ import {
WalletService,
} from "../harness/harness.js";
import { EnvOptions, SimpleTestEnvironmentNg } from "../harness/helpers.js";
-import * as http from "node:http";
-import { createPlatformHttpLib } from "@gnu-taler/taler-util/http";
const logger = new Logger("test-kyc.ts");
@@ -162,7 +159,7 @@ export async function createKycTestkudosEnvironment(
await merchant.start();
await merchant.pingUntilAvailable();
- await merchant.addInstance({
+ await merchant.addInstanceWithWireAccount({
id: "default",
name: "Default Instance",
paytoUris: [getPayto("merchant-default")],
@@ -171,7 +168,7 @@ export async function createKycTestkudosEnvironment(
),
});
- await merchant.addInstance({
+ await merchant.addInstanceWithWireAccount({
id: "minst1",
name: "minst1",
paytoUris: [getPayto("minst1")],
@@ -305,9 +302,11 @@ export async function runKycTest(t: GlobalTestState) {
// Withdraw digital cash into the wallet.
+ const bankClient = new BankAccessApiClient(bank.bankAccessApiBaseUrl);
+
const amount = "TESTKUDOS:20";
- const user = await BankApi.createRandomBankUser(bank);
- const wop = await BankAccessApi.createWithdrawalOperation(bank, user, amount);
+ const user = await bankClient.createRandomBankUser();
+ const wop = await bankClient.createWithdrawalOperation(user.username, amount);
// Hand it to the wallet
@@ -332,7 +331,7 @@ export async function runKycTest(t: GlobalTestState) {
// Confirm it
- await BankApi.confirmWithdrawalOperation(bank, user, wop);
+ await bankClient.confirmWithdrawalOperation(user.username, wop);
const kycNotificationCond = walletClient.waitForNotificationCond((x) => {
if (
@@ -376,7 +375,6 @@ export async function runKycTest(t: GlobalTestState) {
// which would usually done in the browser.
const httpLib = createPlatformHttpLib({
- allowHttp: true,
enableThrottling: false,
});
const kycServerResp = await httpLib.fetch(kycUrl);
diff --git a/packages/taler-harness/src/integrationtests/test-libeufin-api-sandbox-camt.ts b/packages/taler-harness/src/integrationtests/test-libeufin-api-sandbox-camt.ts
index 239bab334..22b411dc2 100644
--- a/packages/taler-harness/src/integrationtests/test-libeufin-api-sandbox-camt.ts
+++ b/packages/taler-harness/src/integrationtests/test-libeufin-api-sandbox-camt.ts
@@ -72,5 +72,5 @@ export async function runLibeufinApiSandboxCamtTest(t: GlobalTestState) {
let ret = await LibeufinSandboxApi.getCamt053(sandbox, "mock-account-1");
console.log(ret);
}
-runLibeufinApiSandboxCamtTest.excludeByDefault = true;
+runLibeufinApiSandboxCamtTest.experimental = true;
runLibeufinApiSandboxCamtTest.suites = ["libeufin"];
diff --git a/packages/taler-harness/src/integrationtests/test-libeufin-basic.ts b/packages/taler-harness/src/integrationtests/test-libeufin-basic.ts
index f03b63f50..d87278197 100644
--- a/packages/taler-harness/src/integrationtests/test-libeufin-basic.ts
+++ b/packages/taler-harness/src/integrationtests/test-libeufin-basic.ts
@@ -19,23 +19,24 @@
*/
import {
AbsoluteTime,
- MerchantContractTerms,
Duration,
+ MerchantContractTerms,
} from "@gnu-taler/taler-util";
-import {
- WalletApiOperation,
- HarnessExchangeBankAccount,
-} from "@gnu-taler/taler-wallet-core";
+import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
import { CoinConfig, defaultCoinConfig } from "../harness/denomStructures.js";
import {
DbInfo,
ExchangeService,
GlobalTestState,
+ HarnessExchangeBankAccount,
MerchantService,
+ WalletClient,
setupDb,
- WalletCli,
} from "../harness/harness.js";
-import { makeTestPayment } from "../harness/helpers.js";
+import {
+ createWalletDaemonWithClient,
+ makeTestPaymentV2,
+} from "../harness/helpers.js";
import {
LibeufinNexusApi,
LibeufinNexusService,
@@ -53,7 +54,7 @@ export interface LibeufinTestEnvironment {
exchange: ExchangeService;
exchangeBankAccount: HarnessExchangeBankAccount;
merchant: MerchantService;
- wallet: WalletCli;
+ walletClient: WalletClient;
libeufinSandbox: LibeufinSandboxService;
libeufinNexus: LibeufinNexusService;
}
@@ -69,7 +70,7 @@ export async function createLibeufinTestEnvironment(
const libeufinSandbox = await LibeufinSandboxService.create(t, {
httpPort: 5010,
- databaseJdbcUri: `jdbc:sqlite:${t.testDir}/libeufin-sandbox.sqlite3`,
+ databaseJdbcUri: db.connStr,
});
await libeufinSandbox.start();
@@ -77,7 +78,7 @@ export async function createLibeufinTestEnvironment(
const libeufinNexus = await LibeufinNexusService.create(t, {
httpPort: 5011,
- databaseJdbcUri: `jdbc:sqlite:${t.testDir}/libeufin-nexus.sqlite3`,
+ databaseJdbcUri: db.connStr,
});
await libeufinNexus.start();
@@ -202,7 +203,7 @@ export async function createLibeufinTestEnvironment(
await merchant.start();
await merchant.pingUntilAvailable();
- await merchant.addInstance({
+ await merchant.addInstanceWithWireAccount({
id: "default",
name: "Default Instance",
paytoUris: [`payto://iban/${merchantIban}?receiver-name=Merchant`],
@@ -213,13 +214,15 @@ export async function createLibeufinTestEnvironment(
console.log("setup done!");
- const wallet = new WalletCli(t);
+ const { walletClient } = await createWalletDaemonWithClient(t, {
+ name: "default",
+ });
return {
commonDb: db,
exchange,
merchant,
- wallet,
+ walletClient,
exchangeBankAccount,
libeufinNexus,
libeufinSandbox,
@@ -232,14 +235,14 @@ export async function createLibeufinTestEnvironment(
export async function runLibeufinBasicTest(t: GlobalTestState) {
// Set up test environment
- const { wallet, exchange, merchant, libeufinSandbox, libeufinNexus } =
+ const { walletClient, exchange, merchant, libeufinSandbox, libeufinNexus } =
await createLibeufinTestEnvironment(t);
- await wallet.client.call(WalletApiOperation.AddExchange, {
+ await walletClient.call(WalletApiOperation.AddExchange, {
exchangeBaseUrl: exchange.baseUrl,
});
- const wr = await wallet.client.call(
+ const wr = await walletClient.call(
WalletApiOperation.AcceptManualWithdrawal,
{
exchangeBaseUrl: exchange.baseUrl,
@@ -265,9 +268,9 @@ export async function runLibeufinBasicTest(t: GlobalTestState) {
await exchange.runWirewatchOnce();
- await wallet.runUntilDone();
+ await walletClient.call(WalletApiOperation.TestingWaitTransactionsFinal, {});
- const bal = await wallet.client.call(WalletApiOperation.GetBalances, {});
+ const bal = await walletClient.call(WalletApiOperation.GetBalances, {});
console.log("balances", JSON.stringify(bal, undefined, 2));
t.assertAmountEquals(bal.balances[0].available, "EUR:14.7");
@@ -275,10 +278,12 @@ export async function runLibeufinBasicTest(t: GlobalTestState) {
summary: "Buy me!",
amount: "EUR:5",
fulfillment_url: "taler://fulfillment-success/thx",
- wire_transfer_deadline: AbsoluteTime.toProtocolTimestamp(AbsoluteTime.now()),
+ wire_transfer_deadline: AbsoluteTime.toProtocolTimestamp(
+ AbsoluteTime.now(),
+ ),
};
- await makeTestPayment(t, { wallet, merchant, order });
+ await makeTestPaymentV2(t, { walletClient, merchant, order });
await exchange.runAggregatorOnce();
await exchange.runTransferOnce();
diff --git a/packages/taler-harness/src/integrationtests/test-libeufin-nexus-balance.ts b/packages/taler-harness/src/integrationtests/test-libeufin-nexus-balance.ts
index 68b0174cc..868f93759 100644
--- a/packages/taler-harness/src/integrationtests/test-libeufin-nexus-balance.ts
+++ b/packages/taler-harness/src/integrationtests/test-libeufin-nexus-balance.ts
@@ -114,4 +114,4 @@ export async function runLibeufinNexusBalanceTest(t: GlobalTestState) {
}
runLibeufinNexusBalanceTest.suites = ["libeufin"];
-runLibeufinNexusBalanceTest.excludeByDefault = true;
+runLibeufinNexusBalanceTest.experimental = true;
diff --git a/packages/taler-harness/src/integrationtests/test-merchant-exchange-confusion.ts b/packages/taler-harness/src/integrationtests/test-merchant-exchange-confusion.ts
index e18cd7a0f..2f79041d6 100644
--- a/packages/taler-harness/src/integrationtests/test-merchant-exchange-confusion.ts
+++ b/packages/taler-harness/src/integrationtests/test-merchant-exchange-confusion.ts
@@ -20,6 +20,7 @@
import {
codecForMerchantOrderStatusUnpaid,
ConfirmPayResultType,
+ MerchantApiClient,
PreparePayResultType,
} from "@gnu-taler/taler-util";
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
@@ -35,16 +36,14 @@ import {
getPayto,
GlobalTestState,
harnessHttpLib,
- MerchantPrivateApi,
MerchantService,
setupDb,
- WalletCli,
} from "../harness/harness.js";
import {
+ createWalletDaemonWithClient,
FaultyMerchantTestEnvironment,
- withdrawViaBank,
+ withdrawViaBankV2,
} from "../harness/helpers.js";
-import { createPlatformHttpLib } from "@gnu-taler/taler-util/http";
/**
* Run a test case with a simple TESTKUDOS Taler environment, consisting
@@ -110,13 +109,13 @@ export async function createConfusedMerchantTestkudosEnvironment(
await merchant.start();
await merchant.pingUntilAvailable();
- await merchant.addInstance({
+ await merchant.addInstanceWithWireAccount({
id: "default",
name: "Default Instance",
paytoUris: [getPayto("merchant-default")],
});
- await merchant.addInstance({
+ await merchant.addInstanceWithWireAccount({
id: "minst1",
name: "minst1",
paytoUris: [getPayto("minst1")],
@@ -124,13 +123,15 @@ export async function createConfusedMerchantTestkudosEnvironment(
console.log("setup done!");
- const wallet = new WalletCli(t);
+ const { walletClient } = await createWalletDaemonWithClient(t, {
+ name: "default",
+ });
return {
commonDb: db,
exchange,
merchant,
- wallet,
+ walletClient,
bank,
exchangeBankAccount,
faultyMerchant,
@@ -145,18 +146,20 @@ export async function createConfusedMerchantTestkudosEnvironment(
export async function runMerchantExchangeConfusionTest(t: GlobalTestState) {
// Set up test environment
- const { wallet, bank, faultyExchange, faultyMerchant } =
+ const { walletClient, bank, faultyExchange, faultyMerchant } =
await createConfusedMerchantTestkudosEnvironment(t);
// Withdraw digital cash into the wallet.
- await withdrawViaBank(t, {
- wallet,
+ const wres = await withdrawViaBankV2(t, {
+ walletClient,
bank,
exchange: faultyExchange,
amount: "TESTKUDOS:20",
});
+ await wres.withdrawalFinishedCond;
+
/**
* =========================================================================
* Create an order and let the wallet pay under a session ID
@@ -168,7 +171,9 @@ export async function runMerchantExchangeConfusionTest(t: GlobalTestState) {
const merchant = faultyMerchant;
- let orderResp = await MerchantPrivateApi.createOrder(merchant, "default", {
+ const merchantClient = new MerchantApiClient(merchant.makeInstanceBaseUrl());
+
+ let orderResp = await merchantClient.createOrder({
order: {
summary: "Buy me!",
amount: "TESTKUDOS:5",
@@ -176,7 +181,7 @@ export async function runMerchantExchangeConfusionTest(t: GlobalTestState) {
},
});
- let orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus(merchant, {
+ let orderStatus = await merchantClient.queryPrivateOrderStatus({
orderId: orderResp.order_id,
sessionId: "mysession-one",
});
@@ -200,7 +205,7 @@ export async function runMerchantExchangeConfusionTest(t: GlobalTestState) {
console.log(pubUnpaidStatus);
- let preparePayResp = await wallet.client.call(
+ let preparePayResp = await walletClient.call(
WalletApiOperation.PreparePayForUri,
{
talerPayUri: pubUnpaidStatus.taler_pay_uri,
@@ -231,12 +236,9 @@ export async function runMerchantExchangeConfusionTest(t: GlobalTestState) {
await publicOrderStatusResp.json(),
);
- const confirmPayRes = await wallet.client.call(
- WalletApiOperation.ConfirmPay,
- {
- proposalId: proposalId,
- },
- );
+ const confirmPayRes = await walletClient.call(WalletApiOperation.ConfirmPay, {
+ proposalId: proposalId,
+ });
t.assertTrue(confirmPayRes.type === ConfirmPayResultType.Done);
}
diff --git a/packages/taler-harness/src/integrationtests/test-merchant-instances-delete.ts b/packages/taler-harness/src/integrationtests/test-merchant-instances-delete.ts
index e6e5bff76..ff567d33d 100644
--- a/packages/taler-harness/src/integrationtests/test-merchant-instances-delete.ts
+++ b/packages/taler-harness/src/integrationtests/test-merchant-instances-delete.ts
@@ -17,17 +17,15 @@
/**
* Imports.
*/
-import { TalerError, URL } from "@gnu-taler/taler-util";
+import { MerchantApiClient, TalerError, URL } from "@gnu-taler/taler-util";
import {
ExchangeService,
GlobalTestState,
- MerchantApiClient,
MerchantService,
- setupDb,
getPayto,
harnessHttpLib,
+ setupDb,
} from "../harness/harness.js";
-import { createPlatformHttpLib } from "@gnu-taler/taler-util/http";
/**
* Test instance deletion and authentication for it
@@ -69,13 +67,15 @@ export async function runMerchantInstancesDeleteTest(t: GlobalTestState) {
// Instances should initially be empty
{
- const r = await harnessHttpLib.fetch(new URL("management/instances", baseUrl).href);
+ const r = await harnessHttpLib.fetch(
+ new URL("management/instances", baseUrl).href,
+ );
const data = await r.json();
t.assertDeepEqual(data.instances, []);
}
// Add an instance, no auth!
- await merchant.addInstance({
+ await merchant.addInstanceWithWireAccount({
id: "default",
name: "Default Instance",
paytoUris: [getPayto("merchant-default")],
@@ -85,7 +85,7 @@ export async function runMerchantInstancesDeleteTest(t: GlobalTestState) {
});
// Add an instance, no auth!
- await merchant.addInstance({
+ await merchant.addInstanceWithWireAccount({
id: "myinst",
name: "Second Instance",
paytoUris: [getPayto("merchant-default")],
diff --git a/packages/taler-harness/src/integrationtests/test-merchant-instances-urls.ts b/packages/taler-harness/src/integrationtests/test-merchant-instances-urls.ts
index 18a09c76b..071288b0f 100644
--- a/packages/taler-harness/src/integrationtests/test-merchant-instances-urls.ts
+++ b/packages/taler-harness/src/integrationtests/test-merchant-instances-urls.ts
@@ -17,17 +17,15 @@
/**
* Imports.
*/
-import { Duration } from "@gnu-taler/taler-util";
+import { Duration, MerchantApiClient } from "@gnu-taler/taler-util";
import {
ExchangeService,
GlobalTestState,
- MerchantApiClient,
MerchantService,
- setupDb,
getPayto,
harnessHttpLib,
+ setupDb,
} from "../harness/harness.js";
-import { createPlatformHttpLib } from "@gnu-taler/taler-util/http";
/**
* Do basic checks on instance management and authentication.
diff --git a/packages/taler-harness/src/integrationtests/test-merchant-instances.ts b/packages/taler-harness/src/integrationtests/test-merchant-instances.ts
index f7d89c543..fd7a8ca3a 100644
--- a/packages/taler-harness/src/integrationtests/test-merchant-instances.ts
+++ b/packages/taler-harness/src/integrationtests/test-merchant-instances.ts
@@ -17,17 +17,15 @@
/**
* Imports.
*/
-import { URL } from "@gnu-taler/taler-util";
+import { MerchantApiClient, URL } from "@gnu-taler/taler-util";
import {
ExchangeService,
GlobalTestState,
- MerchantApiClient,
MerchantService,
setupDb,
getPayto,
harnessHttpLib,
} from "../harness/harness.js";
-import { createPlatformHttpLib } from "@gnu-taler/taler-util/http";
/**
* Do basic checks on instance management and authentication.
@@ -77,7 +75,17 @@ export async function runMerchantInstancesTest(t: GlobalTestState) {
}
// Add an instance, no auth!
- await merchant.addInstance({
+ await merchant.addInstanceWithWireAccount({
+ id: "default",
+ name: "Default Instance",
+ paytoUris: [getPayto("merchant-default")],
+ auth: {
+ method: "external",
+ },
+ });
+
+ // Add it again, should be idempotent
+ await merchant.addInstanceWithWireAccount({
id: "default",
name: "Default Instance",
paytoUris: [getPayto("merchant-default")],
@@ -87,7 +95,7 @@ export async function runMerchantInstancesTest(t: GlobalTestState) {
});
// Add an instance, no auth!
- await merchant.addInstance({
+ await merchant.addInstanceWithWireAccount({
id: "myinst",
name: "Second Instance",
paytoUris: [getPayto("merchant-default")],
diff --git a/packages/taler-harness/src/integrationtests/test-merchant-longpolling.ts b/packages/taler-harness/src/integrationtests/test-merchant-longpolling.ts
index 8d271c5d1..bd63a8445 100644
--- a/packages/taler-harness/src/integrationtests/test-merchant-longpolling.ts
+++ b/packages/taler-harness/src/integrationtests/test-merchant-longpolling.ts
@@ -19,17 +19,17 @@
*/
import {
ConfirmPayResultType,
+ MerchantApiClient,
PreparePayResultType,
URL,
codecForMerchantOrderStatusUnpaid,
} from "@gnu-taler/taler-util";
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
-import { GlobalTestState, MerchantPrivateApi, harnessHttpLib } from "../harness/harness.js";
+import { GlobalTestState, harnessHttpLib } from "../harness/harness.js";
import {
createSimpleTestkudosEnvironmentV2,
withdrawViaBankV2,
} from "../harness/helpers.js";
-import { createPlatformHttpLib } from "@gnu-taler/taler-util/http";
/**
* Run test for basic, bank-integrated withdrawal.
@@ -50,6 +50,8 @@ export async function runMerchantLongpollingTest(t: GlobalTestState) {
await wres.withdrawalFinishedCond;
+ const merchantClient = new MerchantApiClient(merchant.makeInstanceBaseUrl());
+
/**
* =========================================================================
* Create an order and let the wallet pay under a session ID
@@ -59,7 +61,7 @@ export async function runMerchantLongpollingTest(t: GlobalTestState) {
* =========================================================================
*/
- let orderResp = await MerchantPrivateApi.createOrder(merchant, "default", {
+ let orderResp = await merchantClient.createOrder({
order: {
summary: "Buy me!",
amount: "TESTKUDOS:5",
@@ -68,7 +70,7 @@ export async function runMerchantLongpollingTest(t: GlobalTestState) {
create_token: false,
});
- let orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus(merchant, {
+ let orderStatus = await merchantClient.queryPrivateOrderStatus({
orderId: orderResp.order_id,
sessionId: "mysession-one",
});
@@ -81,7 +83,9 @@ export async function runMerchantLongpollingTest(t: GlobalTestState) {
// First, request order status without longpolling
{
console.log("requesting", publicOrderStatusUrl.href);
- let publicOrderStatusResp = await harnessHttpLib.fetch(publicOrderStatusUrl.href);
+ let publicOrderStatusResp = await harnessHttpLib.fetch(
+ publicOrderStatusUrl.href,
+ );
if (publicOrderStatusResp.status != 402) {
throw Error(
@@ -94,7 +98,9 @@ export async function runMerchantLongpollingTest(t: GlobalTestState) {
publicOrderStatusUrl.searchParams.set("timeout_ms", "500");
console.log("requesting", publicOrderStatusUrl.href);
- let publicOrderStatusResp = await harnessHttpLib.fetch(publicOrderStatusUrl.href);
+ let publicOrderStatusResp = await harnessHttpLib.fetch(
+ publicOrderStatusUrl.href,
+ );
if (publicOrderStatusResp.status != 402) {
throw Error(
@@ -129,7 +135,9 @@ export async function runMerchantLongpollingTest(t: GlobalTestState) {
preparePayResp.contractTermsHash,
);
- let publicOrderStatusPromise = harnessHttpLib.fetch(publicOrderStatusUrl.href);
+ let publicOrderStatusPromise = harnessHttpLib.fetch(
+ publicOrderStatusUrl.href,
+ );
t.assertTrue(preparePayResp.status === PreparePayResultType.PaymentPossible);
diff --git a/packages/taler-harness/src/integrationtests/test-merchant-refund-api.ts b/packages/taler-harness/src/integrationtests/test-merchant-refund-api.ts
index 8efac1fc1..1b69b9de6 100644
--- a/packages/taler-harness/src/integrationtests/test-merchant-refund-api.ts
+++ b/packages/taler-harness/src/integrationtests/test-merchant-refund-api.ts
@@ -17,43 +17,42 @@
/**
* Imports.
*/
-import { createPlatformHttpLib } from "@gnu-taler/taler-util/http";
import {
+ Duration,
+ MerchantApiClient,
+ PreparePayResultType,
+ URL,
+ durationFromSpec,
+} from "@gnu-taler/taler-util";
+import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
+import {
+ BankServiceHandle,
+ ExchangeServiceInterface,
GlobalTestState,
- MerchantPrivateApi,
MerchantServiceInterface,
- WalletCli,
- ExchangeServiceInterface,
+ WalletClient,
harnessHttpLib,
} from "../harness/harness.js";
import {
- createSimpleTestkudosEnvironment,
- withdrawViaBank,
+ createSimpleTestkudosEnvironmentV2,
+ withdrawViaBankV2,
} from "../harness/helpers.js";
-import {
- URL,
- durationFromSpec,
- PreparePayResultType,
- Duration,
-} from "@gnu-taler/taler-util";
-import {
- WalletApiOperation,
- BankServiceHandle,
-} from "@gnu-taler/taler-wallet-core";
async function testRefundApiWithFulfillmentUrl(
t: GlobalTestState,
env: {
merchant: MerchantServiceInterface;
bank: BankServiceHandle;
- wallet: WalletCli;
+ walletClient: WalletClient;
exchange: ExchangeServiceInterface;
},
): Promise<void> {
- const { wallet, bank, exchange, merchant } = env;
+ const { walletClient, merchant } = env;
+
+ const merchantClient = new MerchantApiClient(merchant.makeInstanceBaseUrl());
// Set up order.
- const orderResp = await MerchantPrivateApi.createOrder(merchant, "default", {
+ const orderResp = await merchantClient.createOrder({
order: {
summary: "Buy me!",
amount: "TESTKUDOS:5",
@@ -64,7 +63,7 @@ async function testRefundApiWithFulfillmentUrl(
),
});
- let orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus(merchant, {
+ let orderStatus = await merchantClient.queryPrivateOrderStatus({
orderId: orderResp.order_id,
});
@@ -75,7 +74,7 @@ async function testRefundApiWithFulfillmentUrl(
// Make wallet pay for the order
- let preparePayResult = await wallet.client.call(
+ let preparePayResult = await walletClient.call(
WalletApiOperation.PreparePayForUri,
{
talerPayUri,
@@ -86,19 +85,19 @@ async function testRefundApiWithFulfillmentUrl(
preparePayResult.status === PreparePayResultType.PaymentPossible,
);
- await wallet.client.call(WalletApiOperation.ConfirmPay, {
+ await walletClient.call(WalletApiOperation.ConfirmPay, {
proposalId: preparePayResult.proposalId,
});
// Check if payment was successful.
- orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus(merchant, {
+ orderStatus = await merchantClient.queryPrivateOrderStatus({
orderId: orderResp.order_id,
});
t.assertTrue(orderStatus.order_status === "paid");
- preparePayResult = await wallet.client.call(
+ preparePayResult = await walletClient.call(
WalletApiOperation.PreparePayForUri,
{
talerPayUri,
@@ -109,14 +108,14 @@ async function testRefundApiWithFulfillmentUrl(
preparePayResult.status === PreparePayResultType.AlreadyConfirmed,
);
- await MerchantPrivateApi.giveRefund(merchant, {
+ await merchantClient.giveRefund({
amount: "TESTKUDOS:5",
instance: "default",
justification: "foo",
orderId: orderResp.order_id,
});
- orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus(merchant, {
+ orderStatus = await merchantClient.queryPrivateOrderStatus({
orderId: orderResp.order_id,
});
@@ -136,7 +135,9 @@ async function testRefundApiWithFulfillmentUrl(
preparePayResult.contractTermsHash,
);
- let publicOrderStatusResp = await harnessHttpLib.fetch(publicOrderStatusUrl.href);
+ let publicOrderStatusResp = await harnessHttpLib.fetch(
+ publicOrderStatusUrl.href,
+ );
const respData = await publicOrderStatusResp.json();
t.assertTrue(publicOrderStatusResp.status === 200);
t.assertAmountEquals(respData.refund_amount, "TESTKUDOS:5");
@@ -158,14 +159,16 @@ async function testRefundApiWithFulfillmentMessage(
env: {
merchant: MerchantServiceInterface;
bank: BankServiceHandle;
- wallet: WalletCli;
+ walletClient: WalletClient;
exchange: ExchangeServiceInterface;
},
): Promise<void> {
- const { wallet, bank, exchange, merchant } = env;
+ const { walletClient, merchant } = env;
+
+ const merchantClient = new MerchantApiClient(merchant.makeInstanceBaseUrl());
// Set up order.
- const orderResp = await MerchantPrivateApi.createOrder(merchant, "default", {
+ const orderResp = await merchantClient.createOrder({
order: {
summary: "Buy me!",
amount: "TESTKUDOS:5",
@@ -176,7 +179,7 @@ async function testRefundApiWithFulfillmentMessage(
),
});
- let orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus(merchant, {
+ let orderStatus = await merchantClient.queryPrivateOrderStatus({
orderId: orderResp.order_id,
});
@@ -187,7 +190,7 @@ async function testRefundApiWithFulfillmentMessage(
// Make wallet pay for the order
- let preparePayResult = await wallet.client.call(
+ let preparePayResult = await walletClient.call(
WalletApiOperation.PreparePayForUri,
{
talerPayUri,
@@ -198,19 +201,19 @@ async function testRefundApiWithFulfillmentMessage(
preparePayResult.status === PreparePayResultType.PaymentPossible,
);
- await wallet.client.call(WalletApiOperation.ConfirmPay, {
+ await walletClient.call(WalletApiOperation.ConfirmPay, {
proposalId: preparePayResult.proposalId,
});
// Check if payment was successful.
- orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus(merchant, {
+ orderStatus = await merchantClient.queryPrivateOrderStatus({
orderId: orderResp.order_id,
});
t.assertTrue(orderStatus.order_status === "paid");
- preparePayResult = await wallet.client.call(
+ preparePayResult = await walletClient.call(
WalletApiOperation.PreparePayForUri,
{
talerPayUri,
@@ -221,14 +224,14 @@ async function testRefundApiWithFulfillmentMessage(
preparePayResult.status === PreparePayResultType.AlreadyConfirmed,
);
- await MerchantPrivateApi.giveRefund(merchant, {
+ await merchantClient.giveRefund({
amount: "TESTKUDOS:5",
instance: "default",
justification: "foo",
orderId: orderResp.order_id,
});
- orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus(merchant, {
+ orderStatus = await merchantClient.queryPrivateOrderStatus({
orderId: orderResp.order_id,
});
@@ -248,7 +251,9 @@ async function testRefundApiWithFulfillmentMessage(
preparePayResult.contractTermsHash,
);
- let publicOrderStatusResp = await harnessHttpLib.fetch(publicOrderStatusUrl.href);
+ let publicOrderStatusResp = await harnessHttpLib.fetch(
+ publicOrderStatusUrl.href,
+ );
let respData = await publicOrderStatusResp.json();
console.log(respData);
t.assertTrue(publicOrderStatusResp.status === 200);
@@ -272,22 +277,28 @@ async function testRefundApiWithFulfillmentMessage(
export async function runMerchantRefundApiTest(t: GlobalTestState) {
// Set up test environment
- const { wallet, bank, exchange, merchant } =
- await createSimpleTestkudosEnvironment(t);
+ const { walletClient, bank, exchange, merchant } =
+ await createSimpleTestkudosEnvironmentV2(t);
// Withdraw digital cash into the wallet.
- await withdrawViaBank(t, { wallet, bank, exchange, amount: "TESTKUDOS:20" });
+ const wres = await withdrawViaBankV2(t, {
+ walletClient,
+ bank,
+ exchange,
+ amount: "TESTKUDOS:20",
+ });
+ await wres.withdrawalFinishedCond;
await testRefundApiWithFulfillmentUrl(t, {
- wallet,
+ walletClient,
bank,
exchange,
merchant,
});
await testRefundApiWithFulfillmentMessage(t, {
- wallet,
+ walletClient,
bank,
exchange,
merchant,
diff --git a/packages/taler-harness/src/integrationtests/test-merchant-spec-public-orders.ts b/packages/taler-harness/src/integrationtests/test-merchant-spec-public-orders.ts
index e959e813b..afae8a899 100644
--- a/packages/taler-harness/src/integrationtests/test-merchant-spec-public-orders.ts
+++ b/packages/taler-harness/src/integrationtests/test-merchant-spec-public-orders.ts
@@ -19,6 +19,7 @@
*/
import {
ConfirmPayResultType,
+ MerchantApiClient,
PreparePayResultType,
URL,
encodeCrock,
@@ -29,14 +30,13 @@ import {
BankService,
ExchangeService,
GlobalTestState,
- MerchantPrivateApi,
MerchantService,
- WalletCli,
harnessHttpLib,
} from "../harness/harness.js";
import {
- createSimpleTestkudosEnvironment,
- withdrawViaBank,
+ createSimpleTestkudosEnvironmentV2,
+ createWalletDaemonWithClient,
+ withdrawViaBankV2,
} from "../harness/helpers.js";
interface Context {
@@ -52,12 +52,21 @@ async function testWithClaimToken(
t: GlobalTestState,
c: Context,
): Promise<void> {
- const wallet = new WalletCli(t, "withclaimtoken");
+ const { walletClient } = await createWalletDaemonWithClient(t, {
+ name: "wct",
+ });
const { bank, exchange } = c;
const { merchant, merchantBaseUrl } = c;
- await withdrawViaBank(t, { wallet, bank, exchange, amount: "TESTKUDOS:20" });
+ const wres = await withdrawViaBankV2(t, {
+ walletClient,
+ bank,
+ exchange,
+ amount: "TESTKUDOS:20",
+ });
+ await wres.withdrawalFinishedCond;
const sessionId = "mysession";
- const orderResp = await MerchantPrivateApi.createOrder(merchant, "default", {
+ const merchantClient = new MerchantApiClient(merchant.makeInstanceBaseUrl());
+ const orderResp = await merchantClient.createOrder({
order: {
summary: "Buy me!",
amount: "TESTKUDOS:5",
@@ -104,7 +113,7 @@ async function testWithClaimToken(
console.log(r);
}
- const preparePayResp = await wallet.client.call(
+ const preparePayResp = await walletClient.call(
WalletApiOperation.PreparePayForUri,
{
talerPayUri,
@@ -166,12 +175,9 @@ async function testWithClaimToken(
t.assertDeepEqual(httpResp.status, 202);
}
- const confirmPayRes = await wallet.client.call(
- WalletApiOperation.ConfirmPay,
- {
- proposalId: proposalId,
- },
- );
+ const confirmPayRes = await walletClient.call(WalletApiOperation.ConfirmPay, {
+ proposalId: proposalId,
+ });
t.assertTrue(confirmPayRes.type === ConfirmPayResultType.Done);
@@ -238,7 +244,7 @@ async function testWithClaimToken(
t.assertDeepEqual(httpResp.status, 200);
}
- const confirmPayRes2 = await wallet.client.call(
+ const confirmPayRes2 = await walletClient.call(
WalletApiOperation.ConfirmPay,
{
proposalId: proposalId,
@@ -249,18 +255,14 @@ async function testWithClaimToken(
t.assertTrue(confirmPayRes2.type === ConfirmPayResultType.Done);
// Create another order with identical fulfillment URL to test the "already paid" flow
- const alreadyPaidOrderResp = await MerchantPrivateApi.createOrder(
- merchant,
- "default",
- {
- order: {
- summary: "Buy me!",
- amount: "TESTKUDOS:5",
- fulfillment_url: "https://example.com/article42",
- public_reorder_url: "https://example.com/article42-share",
- },
+ const alreadyPaidOrderResp = await merchantClient.createOrder({
+ order: {
+ summary: "Buy me!",
+ amount: "TESTKUDOS:5",
+ fulfillment_url: "https://example.com/article42",
+ public_reorder_url: "https://example.com/article42-share",
},
- );
+ });
const apOrderId = alreadyPaidOrderResp.order_id;
const apToken = alreadyPaidOrderResp.token;
@@ -307,12 +309,21 @@ async function testWithoutClaimToken(
t: GlobalTestState,
c: Context,
): Promise<void> {
- const wallet = new WalletCli(t, "withoutct");
+ const { walletClient } = await createWalletDaemonWithClient(t, {
+ name: "wnoct",
+ });
const sessionId = "mysession2";
const { bank, exchange } = c;
const { merchant, merchantBaseUrl } = c;
- await withdrawViaBank(t, { wallet, bank, exchange, amount: "TESTKUDOS:20" });
- const orderResp = await MerchantPrivateApi.createOrder(merchant, "default", {
+ const merchantClient = new MerchantApiClient(merchant.makeInstanceBaseUrl());
+ const wres = await withdrawViaBankV2(t, {
+ walletClient,
+ bank,
+ exchange,
+ amount: "TESTKUDOS:20",
+ });
+ await wres.withdrawalFinishedCond;
+ const orderResp = await merchantClient.createOrder({
order: {
summary: "Buy me!",
amount: "TESTKUDOS:5",
@@ -356,7 +367,7 @@ async function testWithoutClaimToken(
console.log(r);
}
- const preparePayResp = await wallet.client.call(
+ const preparePayResp = await walletClient.call(
WalletApiOperation.PreparePayForUri,
{
talerPayUri,
@@ -422,12 +433,9 @@ async function testWithoutClaimToken(
t.assertDeepEqual(httpResp.status, 402);
}
- const confirmPayRes = await wallet.client.call(
- WalletApiOperation.ConfirmPay,
- {
- proposalId: proposalId,
- },
- );
+ const confirmPayRes = await walletClient.call(WalletApiOperation.ConfirmPay, {
+ proposalId: proposalId,
+ });
t.assertTrue(confirmPayRes.type === ConfirmPayResultType.Done);
@@ -492,7 +500,7 @@ async function testWithoutClaimToken(
t.assertDeepEqual(httpResp.status, 200);
}
- const confirmPayRes2 = await wallet.client.call(
+ const confirmPayRes2 = await walletClient.call(
WalletApiOperation.ConfirmPay,
{
proposalId: proposalId,
@@ -503,18 +511,14 @@ async function testWithoutClaimToken(
t.assertTrue(confirmPayRes2.type === ConfirmPayResultType.Done);
// Create another order with identical fulfillment URL to test the "already paid" flow
- const alreadyPaidOrderResp = await MerchantPrivateApi.createOrder(
- merchant,
- "default",
- {
- order: {
- summary: "Buy me!",
- amount: "TESTKUDOS:5",
- fulfillment_url: "https://example.com/article42",
- public_reorder_url: "https://example.com/article42-share",
- },
+ const alreadyPaidOrderResp = await merchantClient.createOrder({
+ order: {
+ summary: "Buy me!",
+ amount: "TESTKUDOS:5",
+ fulfillment_url: "https://example.com/article42",
+ public_reorder_url: "https://example.com/article42-share",
},
- );
+ });
const apOrderId = alreadyPaidOrderResp.order_id;
const apToken = alreadyPaidOrderResp.token;
@@ -564,7 +568,7 @@ async function testWithoutClaimToken(
* specification of the endpoint.
*/
export async function runMerchantSpecPublicOrdersTest(t: GlobalTestState) {
- const { bank, exchange, merchant } = await createSimpleTestkudosEnvironment(
+ const { bank, exchange, merchant } = await createSimpleTestkudosEnvironmentV2(
t,
);
@@ -572,7 +576,9 @@ export async function runMerchantSpecPublicOrdersTest(t: GlobalTestState) {
const merchantBaseUrl = merchant.makeInstanceBaseUrl();
{
- const httpResp = await httpLib.fetch(new URL("config", merchantBaseUrl).href);
+ const httpResp = await httpLib.fetch(
+ new URL("config", merchantBaseUrl).href,
+ );
const r = await httpResp.json();
console.log(r);
t.assertDeepEqual(r.currency, "TESTKUDOS");
diff --git a/packages/taler-harness/src/integrationtests/test-pay-paid.ts b/packages/taler-harness/src/integrationtests/test-pay-paid.ts
index a377b7237..3d93f6e29 100644
--- a/packages/taler-harness/src/integrationtests/test-pay-paid.ts
+++ b/packages/taler-harness/src/integrationtests/test-pay-paid.ts
@@ -17,19 +17,20 @@
/**
* Imports.
*/
-import { GlobalTestState, MerchantPrivateApi, harnessHttpLib } from "../harness/harness.js";
import {
- withdrawViaBank,
- createFaultInjectedMerchantTestkudosEnvironment,
-} from "../harness/helpers.js";
-import {
- PreparePayResultType,
- codecForMerchantOrderStatusUnpaid,
ConfirmPayResultType,
+ MerchantApiClient,
+ PreparePayResultType,
URL,
+ codecForMerchantOrderStatusUnpaid,
} from "@gnu-taler/taler-util";
-import { FaultInjectionRequestContext } from "../harness/faultInjection.js";
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
+import { FaultInjectionRequestContext } from "../harness/faultInjection.js";
+import { GlobalTestState, harnessHttpLib } from "../harness/harness.js";
+import {
+ createFaultInjectedMerchantTestkudosEnvironment,
+ withdrawViaBankV2,
+} from "../harness/helpers.js";
/**
* Run test for the wallets repurchase detection mechanism
@@ -42,18 +43,20 @@ import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
export async function runPayPaidTest(t: GlobalTestState) {
// Set up test environment
- const { wallet, bank, faultyExchange, faultyMerchant } =
+ const { walletClient, bank, faultyExchange, faultyMerchant } =
await createFaultInjectedMerchantTestkudosEnvironment(t);
// Withdraw digital cash into the wallet.
- await withdrawViaBank(t, {
- wallet,
+ const wres = await withdrawViaBankV2(t, {
+ walletClient,
bank,
exchange: faultyExchange,
amount: "TESTKUDOS:20",
});
+ await wres.withdrawalFinishedCond;
+
/**
* =========================================================================
* Create an order and let the wallet pay under a session ID
@@ -65,7 +68,9 @@ export async function runPayPaidTest(t: GlobalTestState) {
const merchant = faultyMerchant;
- let orderResp = await MerchantPrivateApi.createOrder(merchant, "default", {
+ const merchantClient = new MerchantApiClient(merchant.makeInstanceBaseUrl());
+
+ let orderResp = await merchantClient.createOrder({
order: {
summary: "Buy me!",
amount: "TESTKUDOS:5",
@@ -74,7 +79,7 @@ export async function runPayPaidTest(t: GlobalTestState) {
},
});
- let orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus(merchant, {
+ let orderStatus = await merchantClient.queryPrivateOrderStatus({
orderId: orderResp.order_id,
sessionId: "mysession-one",
});
@@ -98,7 +103,7 @@ export async function runPayPaidTest(t: GlobalTestState) {
console.log(pubUnpaidStatus);
- let preparePayResp = await wallet.client.call(
+ let preparePayResp = await walletClient.call(
WalletApiOperation.PreparePayForUri,
{
talerPayUri: pubUnpaidStatus.taler_pay_uri,
@@ -121,12 +126,9 @@ export async function runPayPaidTest(t: GlobalTestState) {
publicOrderStatusResp.json(),
);
- const confirmPayRes = await wallet.client.call(
- WalletApiOperation.ConfirmPay,
- {
- proposalId: proposalId,
- },
- );
+ const confirmPayRes = await walletClient.call(WalletApiOperation.ConfirmPay, {
+ proposalId: proposalId,
+ });
t.assertTrue(confirmPayRes.type === ConfirmPayResultType.Done);
@@ -147,7 +149,7 @@ export async function runPayPaidTest(t: GlobalTestState) {
* =========================================================================
*/
- orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus(merchant, {
+ orderStatus = await merchantClient.queryPrivateOrderStatus({
orderId: orderResp.order_id,
sessionId: "mysession-two",
});
@@ -174,7 +176,7 @@ export async function runPayPaidTest(t: GlobalTestState) {
},
});
- let orderRespTwo = await MerchantPrivateApi.createOrder(merchant, "default", {
+ let orderRespTwo = await merchantClient.createOrder({
order: {
summary: "Buy me!",
amount: "TESTKUDOS:5",
@@ -183,20 +185,17 @@ export async function runPayPaidTest(t: GlobalTestState) {
},
});
- let orderStatusTwo = await MerchantPrivateApi.queryPrivateOrderStatus(
- merchant,
- {
- orderId: orderRespTwo.order_id,
- sessionId: "mysession-two",
- },
- );
+ let orderStatusTwo = await merchantClient.queryPrivateOrderStatus({
+ orderId: orderRespTwo.order_id,
+ sessionId: "mysession-two",
+ });
t.assertTrue(orderStatusTwo.order_status === "unpaid");
// Pay with new taler://pay URI, which should
// have the new session ID!
// Wallet should now automatically re-play payment.
- preparePayResp = await wallet.client.call(
+ preparePayResp = await walletClient.call(
WalletApiOperation.PreparePayForUri,
{
talerPayUri: orderStatusTwo.taler_pay_uri,
diff --git a/packages/taler-harness/src/integrationtests/test-payment-abort.ts b/packages/taler-harness/src/integrationtests/test-payment-abort.ts
index 05ca7a543..fe7ecbcd7 100644
--- a/packages/taler-harness/src/integrationtests/test-payment-abort.ts
+++ b/packages/taler-harness/src/integrationtests/test-payment-abort.ts
@@ -17,41 +17,46 @@
/**
* Imports.
*/
-import { GlobalTestState, MerchantPrivateApi, harnessHttpLib } from "../harness/harness.js";
import {
- withdrawViaBank,
- createFaultInjectedMerchantTestkudosEnvironment,
-} from "../harness/helpers.js";
-import { FaultInjectionRequestContext } from "../harness/faultInjection.js";
-import {
- codecForMerchantOrderStatusUnpaid,
ConfirmPayResultType,
- j2s,
+ MerchantApiClient,
PreparePayResultType,
TalerErrorCode,
TalerErrorDetail,
URL,
+ codecForMerchantOrderStatusUnpaid,
+ j2s,
} from "@gnu-taler/taler-util";
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
+import { FaultInjectionRequestContext } from "../harness/faultInjection.js";
+import { GlobalTestState, harnessHttpLib } from "../harness/harness.js";
+import {
+ createFaultInjectedMerchantTestkudosEnvironment,
+ withdrawViaBankV2,
+} from "../harness/helpers.js";
export async function runPaymentAbortTest(t: GlobalTestState) {
// Set up test environment
- const { wallet, bank, exchange, faultyMerchant, faultyExchange } =
+ const { walletClient, bank, exchange, faultyMerchant, faultyExchange } =
await createFaultInjectedMerchantTestkudosEnvironment(t);
// Withdraw digital cash into the wallet.
- await withdrawViaBank(t, {
- wallet,
+ const wres = await withdrawViaBankV2(t, {
+ walletClient,
bank,
exchange: faultyExchange,
amount: "TESTKUDOS:20",
});
- const merchant = faultyMerchant;
+ await wres.withdrawalFinishedCond;
- let orderResp = await MerchantPrivateApi.createOrder(merchant, "default", {
+ const merchantClient = new MerchantApiClient(
+ faultyMerchant.makeInstanceBaseUrl(),
+ );
+
+ let orderResp = await merchantClient.createOrder({
order: {
summary: "Buy me!",
amount: "TESTKUDOS:5",
@@ -60,7 +65,7 @@ export async function runPaymentAbortTest(t: GlobalTestState) {
},
});
- let orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus(merchant, {
+ let orderStatus = await merchantClient.queryPrivateOrderStatus({
orderId: orderResp.order_id,
sessionId: "mysession-one",
});
@@ -84,7 +89,7 @@ export async function runPaymentAbortTest(t: GlobalTestState) {
console.log(pubUnpaidStatus);
- let preparePayResp = await wallet.client.call(
+ let preparePayResp = await walletClient.call(
WalletApiOperation.PreparePayForUri,
{
talerPayUri: pubUnpaidStatus.taler_pay_uri,
@@ -124,7 +129,7 @@ export async function runPaymentAbortTest(t: GlobalTestState) {
},
});
- const confirmPayResp = await wallet.client.call(
+ const confirmPayResp = await walletClient.call(
WalletApiOperation.ConfirmPay,
{
proposalId,
@@ -134,19 +139,16 @@ export async function runPaymentAbortTest(t: GlobalTestState) {
// Can't have succeeded yet, but network error results in "pending" state.
t.assertDeepEqual(confirmPayResp.type, ConfirmPayResultType.Pending);
- const txns = await wallet.client.call(WalletApiOperation.GetTransactions, {});
+ const txns = await walletClient.call(WalletApiOperation.GetTransactions, {});
console.log(j2s(txns));
- await wallet.client.call(WalletApiOperation.AbortTransaction, {
+ await walletClient.call(WalletApiOperation.AbortTransaction, {
transactionId: txns.transactions[1].transactionId,
});
- await wallet.runUntilDone();
+ await walletClient.call(WalletApiOperation.TestingWaitTransactionsFinal, {});
- const txns2 = await wallet.client.call(
- WalletApiOperation.GetTransactions,
- {},
- );
+ const txns2 = await walletClient.call(WalletApiOperation.GetTransactions, {});
console.log(j2s(txns2));
const txTypes = txns2.transactions.map((x) => x.type);
diff --git a/packages/taler-harness/src/integrationtests/test-payment-claim.ts b/packages/taler-harness/src/integrationtests/test-payment-claim.ts
index 3e52cb5dd..b5ed89ec3 100644
--- a/packages/taler-harness/src/integrationtests/test-payment-claim.ts
+++ b/packages/taler-harness/src/integrationtests/test-payment-claim.ts
@@ -17,13 +17,13 @@
/**
* Imports.
*/
-import { PreparePayResultType, TalerErrorCode } from "@gnu-taler/taler-util";
-import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
import {
- GlobalTestState,
- MerchantPrivateApi,
- WalletCli,
-} from "../harness/harness.js";
+ MerchantApiClient,
+ PreparePayResultType,
+ TalerErrorCode,
+} from "@gnu-taler/taler-util";
+import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
+import { GlobalTestState, WalletCli } from "../harness/harness.js";
import {
createSimpleTestkudosEnvironmentV2,
withdrawViaBankV2,
@@ -38,6 +38,8 @@ export async function runPaymentClaimTest(t: GlobalTestState) {
const { walletClient, bank, exchange, merchant } =
await createSimpleTestkudosEnvironmentV2(t);
+ const merchantClient = new MerchantApiClient(merchant.makeInstanceBaseUrl());
+
const walletTwo = new WalletCli(t, "two");
// Withdraw digital cash into the wallet.
@@ -53,7 +55,7 @@ export async function runPaymentClaimTest(t: GlobalTestState) {
// Set up order.
- const orderResp = await MerchantPrivateApi.createOrder(merchant, "default", {
+ const orderResp = await merchantClient.createOrder({
order: {
summary: "Buy me!",
amount: "TESTKUDOS:5",
@@ -61,7 +63,7 @@ export async function runPaymentClaimTest(t: GlobalTestState) {
},
});
- let orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus(merchant, {
+ let orderStatus = await merchantClient.queryPrivateOrderStatus({
orderId: orderResp.order_id,
});
@@ -94,7 +96,7 @@ export async function runPaymentClaimTest(t: GlobalTestState) {
// Check if payment was successful.
- orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus(merchant, {
+ orderStatus = await merchantClient.queryPrivateOrderStatus({
orderId: orderResp.order_id,
});
diff --git a/packages/taler-harness/src/integrationtests/test-payment-expired.ts b/packages/taler-harness/src/integrationtests/test-payment-expired.ts
new file mode 100644
index 000000000..176fc74f7
--- /dev/null
+++ b/packages/taler-harness/src/integrationtests/test-payment-expired.ts
@@ -0,0 +1,131 @@
+/*
+ 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/>
+ */
+
+/**
+ * Imports.
+ */
+import {
+ AbsoluteTime,
+ ConfirmPayResultType,
+ Duration,
+ MerchantApiClient,
+ MerchantContractTerms,
+ PreparePayResultType,
+ j2s,
+} from "@gnu-taler/taler-util";
+import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
+import { GlobalTestState } from "../harness/harness.js";
+import {
+ applyTimeTravelV2,
+ createSimpleTestkudosEnvironmentV2,
+ withdrawViaBankV2,
+} from "../harness/helpers.js";
+
+/**
+ * Run a test for the following scenario:
+ *
+ * - Wallet claims an order
+ * - Merchant goes down
+ * - Wallet tried to pay, but it fails as the merchant is unavailable
+ * - The order expires
+ * - The merchant goes back up again
+ * - Instead of trying to get an abort-refund, the wallet notices that
+ * the order is expired, puts the transaction into "failed",
+ * refreshes allocated coins and thus raises the balance again.
+ */
+export async function runPaymentExpiredTest(t: GlobalTestState) {
+ // Set up test environment
+
+ const { walletClient, bank, exchange, merchant } =
+ await createSimpleTestkudosEnvironmentV2(t);
+
+ // Withdraw digital cash into the wallet.
+
+ await withdrawViaBankV2(t, {
+ walletClient,
+ bank,
+ exchange,
+ amount: "TESTKUDOS:20",
+ });
+
+ await walletClient.call(WalletApiOperation.TestingWaitTransactionsFinal, {});
+
+ // Order that can only be paid within five minutes.
+ const order: Partial<MerchantContractTerms> = {
+ summary: "Buy me!",
+ amount: "TESTKUDOS:5",
+ fulfillment_url: "taler://fulfillment-success/thx",
+ pay_deadline: AbsoluteTime.toProtocolTimestamp(
+ AbsoluteTime.addDuration(
+ AbsoluteTime.now(),
+ Duration.fromSpec({ minutes: 5 }),
+ ),
+ ),
+ };
+
+ const merchantClient = new MerchantApiClient(merchant.makeInstanceBaseUrl());
+
+ const orderResp = await merchantClient.createOrder({
+ order,
+ });
+
+ let orderStatus = await merchantClient.queryPrivateOrderStatus({
+ orderId: orderResp.order_id,
+ });
+
+ t.assertTrue(orderStatus.order_status === "unpaid");
+
+ const preparePayResult = await walletClient.call(
+ WalletApiOperation.PreparePayForUri,
+ {
+ talerPayUri: orderStatus.taler_pay_uri,
+ },
+ );
+
+ t.assertDeepEqual(
+ preparePayResult.status,
+ PreparePayResultType.PaymentPossible,
+ );
+
+ await applyTimeTravelV2(
+ Duration.toMilliseconds(Duration.fromSpec({ hours: 1 })),
+ { walletClient, exchange, merchant },
+ );
+
+ const confirmPayResult = await walletClient.call(
+ WalletApiOperation.ConfirmPay,
+ { transactionId: preparePayResult.transactionId },
+ );
+ console.log("confirm pay result:");
+ console.log(j2s(confirmPayResult));
+ t.assertDeepEqual(confirmPayResult.type, ConfirmPayResultType.Pending);
+ await walletClient.call(WalletApiOperation.AbortTransaction, {
+ transactionId: preparePayResult.transactionId,
+ });
+ await walletClient.call(WalletApiOperation.TestingWaitTransactionsFinal, {});
+
+ const bal = await walletClient.call(WalletApiOperation.GetBalances, {});
+ console.log(bal);
+
+ t.assertAmountEquals(bal.balances[0].available, "TESTKUDOS:18.93");
+
+ const txns = await walletClient.call(WalletApiOperation.GetTransactions, {
+ includeRefreshes: true,
+ });
+ console.log(j2s(txns));
+}
+
+runPaymentExpiredTest.suites = ["wallet"];
diff --git a/packages/taler-harness/src/integrationtests/test-payment-fault.ts b/packages/taler-harness/src/integrationtests/test-payment-fault.ts
index c1438a419..e57427fac 100644
--- a/packages/taler-harness/src/integrationtests/test-payment-fault.ts
+++ b/packages/taler-harness/src/integrationtests/test-payment-fault.ts
@@ -22,27 +22,26 @@
* Imports.
*/
import {
- GlobalTestState,
- MerchantService,
- ExchangeService,
- setupDb,
- BankService,
- WalletCli,
- MerchantPrivateApi,
- getPayto,
-} from "../harness/harness.js";
+ BankAccessApiClient,
+ CoreApiResponse,
+ MerchantApiClient,
+} from "@gnu-taler/taler-util";
+import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
+import { defaultCoinConfig } from "../harness/denomStructures.js";
import {
FaultInjectedExchangeService,
FaultInjectionRequestContext,
FaultInjectionResponseContext,
} from "../harness/faultInjection.js";
-import { CoreApiResponse } from "@gnu-taler/taler-util";
-import { defaultCoinConfig } from "../harness/denomStructures.js";
import {
- WalletApiOperation,
- BankApi,
- BankAccessApi,
-} from "@gnu-taler/taler-wallet-core";
+ BankService,
+ ExchangeService,
+ GlobalTestState,
+ MerchantService,
+ WalletCli,
+ getPayto,
+ setupDb,
+} from "../harness/harness.js";
/**
* Run test for basic, bank-integrated withdrawal.
@@ -114,22 +113,25 @@ export async function runPaymentFaultTest(t: GlobalTestState) {
await merchant.start();
await merchant.pingUntilAvailable();
- await merchant.addInstance({
+ await merchant.addInstanceWithWireAccount({
id: "default",
name: "Default Instance",
paytoUris: [getPayto("merchant-default")],
});
+ const merchantClient = new MerchantApiClient(merchant.makeInstanceBaseUrl());
+
console.log("setup done!");
const wallet = new WalletCli(t);
// Create withdrawal operation
- const user = await BankApi.createRandomBankUser(bank);
- const wop = await BankAccessApi.createWithdrawalOperation(
- bank,
- user,
+ const bankClient = new BankAccessApiClient(bank.bankAccessApiBaseUrl);
+
+ const user = await bankClient.createRandomBankUser();
+ const wop = await bankClient.createWithdrawalOperation(
+ user.username,
"TESTKUDOS:20",
);
@@ -151,7 +153,7 @@ export async function runPaymentFaultTest(t: GlobalTestState) {
// Confirm it
- await BankApi.confirmWithdrawalOperation(bank, user, wop);
+ await bankClient.confirmWithdrawalOperation(user.username, wop);
await wallet.runUntilDone();
@@ -161,7 +163,7 @@ export async function runPaymentFaultTest(t: GlobalTestState) {
// Set up order.
- const orderResp = await MerchantPrivateApi.createOrder(merchant, "default", {
+ const orderResp = await merchantClient.createOrder({
order: {
summary: "Buy me!",
amount: "TESTKUDOS:5",
@@ -169,7 +171,7 @@ export async function runPaymentFaultTest(t: GlobalTestState) {
},
});
- let orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus(merchant, {
+ let orderStatus = await merchantClient.queryPrivateOrderStatus({
orderId: orderResp.order_id,
});
@@ -218,7 +220,7 @@ export async function runPaymentFaultTest(t: GlobalTestState) {
// Check if payment was successful.
- orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus(merchant, {
+ orderStatus = await merchantClient.queryPrivateOrderStatus({
orderId: orderResp.order_id,
});
diff --git a/packages/taler-harness/src/integrationtests/test-payment-idempotency.ts b/packages/taler-harness/src/integrationtests/test-payment-idempotency.ts
index 6373c2393..65fd3a562 100644
--- a/packages/taler-harness/src/integrationtests/test-payment-idempotency.ts
+++ b/packages/taler-harness/src/integrationtests/test-payment-idempotency.ts
@@ -17,9 +17,9 @@
/**
* Imports.
*/
-import { PreparePayResultType } from "@gnu-taler/taler-util";
+import { MerchantApiClient, PreparePayResultType } from "@gnu-taler/taler-util";
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
-import { GlobalTestState, MerchantPrivateApi } from "../harness/harness.js";
+import { GlobalTestState } from "../harness/harness.js";
import {
createSimpleTestkudosEnvironmentV2,
withdrawViaBankV2,
@@ -46,9 +46,11 @@ export async function runPaymentIdempotencyTest(t: GlobalTestState) {
await wres.withdrawalFinishedCond;
+ const merchantClient = new MerchantApiClient(merchant.makeInstanceBaseUrl());
+
// Set up order.
- const orderResp = await MerchantPrivateApi.createOrder(merchant, "default", {
+ const orderResp = await merchantClient.createOrder({
order: {
summary: "Buy me!",
amount: "TESTKUDOS:5",
@@ -56,7 +58,7 @@ export async function runPaymentIdempotencyTest(t: GlobalTestState) {
},
});
- let orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus(merchant, {
+ let orderStatus = await merchantClient.queryPrivateOrderStatus({
orderId: orderResp.order_id,
});
@@ -102,7 +104,7 @@ export async function runPaymentIdempotencyTest(t: GlobalTestState) {
// Check if payment was successful.
- orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus(merchant, {
+ orderStatus = await merchantClient.queryPrivateOrderStatus({
orderId: orderResp.order_id,
});
diff --git a/packages/taler-harness/src/integrationtests/test-payment-multiple.ts b/packages/taler-harness/src/integrationtests/test-payment-multiple.ts
index 25e19ae8e..4ef5e3bff 100644
--- a/packages/taler-harness/src/integrationtests/test-payment-multiple.ts
+++ b/packages/taler-harness/src/integrationtests/test-payment-multiple.ts
@@ -17,19 +17,21 @@
/**
* Imports.
*/
+import { MerchantApiClient } from "@gnu-taler/taler-util";
+import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
+import { coin_ct10, coin_u1 } from "../harness/denomStructures.js";
import {
- GlobalTestState,
- setupDb,
BankService,
ExchangeService,
+ GlobalTestState,
MerchantService,
- WalletCli,
- MerchantPrivateApi,
getPayto,
+ setupDb,
} from "../harness/harness.js";
-import { withdrawViaBank } from "../harness/helpers.js";
-import { coin_ct10, coin_u1 } from "../harness/denomStructures.js";
-import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
+import {
+ createWalletDaemonWithClient,
+ withdrawViaBankV2,
+} from "../harness/helpers.js";
async function setupTest(t: GlobalTestState): Promise<{
merchant: MerchantService;
@@ -82,13 +84,13 @@ async function setupTest(t: GlobalTestState): Promise<{
await merchant.start();
await merchant.pingUntilAvailable();
- await merchant.addInstance({
+ await merchant.addInstanceWithWireAccount({
id: "default",
name: "Default Instance",
paytoUris: [getPayto("merchant-default")],
});
- await merchant.addInstance({
+ await merchant.addInstanceWithWireAccount({
id: "minst1",
name: "minst1",
paytoUris: [getPayto("minst1")],
@@ -113,15 +115,26 @@ export async function runPaymentMultipleTest(t: GlobalTestState) {
const { merchant, bank, exchange } = await setupTest(t);
- const wallet = new WalletCli(t);
+ const { walletClient } = await createWalletDaemonWithClient(t, {
+ name: "default",
+ });
+
+ const merchantClient = new MerchantApiClient(merchant.makeInstanceBaseUrl());
// Withdraw digital cash into the wallet.
- await withdrawViaBank(t, { wallet, bank, exchange, amount: "TESTKUDOS:100" });
+ const wres = await withdrawViaBankV2(t, {
+ walletClient,
+ bank,
+ exchange,
+ amount: "TESTKUDOS:100",
+ });
+
+ await wres.withdrawalFinishedCond;
// Set up order.
- const orderResp = await MerchantPrivateApi.createOrder(merchant, "default", {
+ const orderResp = await merchantClient.createOrder({
order: {
summary: "Buy me!",
amount: "TESTKUDOS:80",
@@ -129,7 +142,7 @@ export async function runPaymentMultipleTest(t: GlobalTestState) {
},
});
- let orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus(merchant, {
+ let orderStatus = await merchantClient.queryPrivateOrderStatus({
orderId: orderResp.order_id,
});
@@ -137,18 +150,17 @@ export async function runPaymentMultipleTest(t: GlobalTestState) {
// Make wallet pay for the order
- const r1 = await wallet.client.call(WalletApiOperation.PreparePayForUri, {
+ const r1 = await walletClient.call(WalletApiOperation.PreparePayForUri, {
talerPayUri: orderStatus.taler_pay_uri,
});
- await wallet.client.call(WalletApiOperation.ConfirmPay, {
- // FIXME: should be validated, don't cast!
- proposalId: r1.proposalId,
+ await walletClient.call(WalletApiOperation.ConfirmPay, {
+ transactionId: r1.transactionId,
});
// Check if payment was successful.
- orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus(merchant, {
+ orderStatus = await merchantClient.queryPrivateOrderStatus({
orderId: orderResp.order_id,
});
diff --git a/packages/taler-harness/src/integrationtests/test-payment-share.ts b/packages/taler-harness/src/integrationtests/test-payment-share.ts
index 156571372..c4a82c917 100644
--- a/packages/taler-harness/src/integrationtests/test-payment-share.ts
+++ b/packages/taler-harness/src/integrationtests/test-payment-share.ts
@@ -19,10 +19,11 @@
*/
import {
ConfirmPayResultType,
+ MerchantApiClient,
PreparePayResultType,
} from "@gnu-taler/taler-util";
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
-import { GlobalTestState, MerchantPrivateApi } from "../harness/harness.js";
+import { GlobalTestState } from "../harness/harness.js";
import {
createSimpleTestkudosEnvironmentV2,
createWalletDaemonWithClient,
@@ -41,6 +42,8 @@ export async function runPaymentShareTest(t: GlobalTestState) {
merchant,
} = await createSimpleTestkudosEnvironmentV2(t);
+ const merchantClient = new MerchantApiClient(merchant.makeInstanceBaseUrl());
+
// Withdraw digital cash into the wallet.
await withdrawViaBankV2(t, {
walletClient: firstWallet,
@@ -62,7 +65,7 @@ export async function runPaymentShareTest(t: GlobalTestState) {
});
await secondWallet.call(WalletApiOperation.TestingWaitTransactionsFinal, {});
- //create two orders to pay
+ // create two orders to pay
async function createOrder(amount: string) {
const order = {
summary: "Buy me!",
@@ -70,26 +73,16 @@ export async function runPaymentShareTest(t: GlobalTestState) {
fulfillment_url: "taler://fulfillment-success/thx",
};
- const instance = "default";
const args = { order };
const auth = {};
- const orderResp = await MerchantPrivateApi.createOrder(
- merchant,
- instance,
- {
- order: args.order,
- },
- auth,
- );
+ const orderResp = await merchantClient.createOrder({
+ order: args.order,
+ });
- const orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus(
- merchant,
- {
- orderId: orderResp.order_id,
- },
- auth,
- );
+ const orderStatus = await merchantClient.queryPrivateOrderStatus({
+ orderId: orderResp.order_id,
+ });
t.assertTrue(orderStatus.order_status === "unpaid");
return { id: orderResp.order_id, uri: orderStatus.taler_pay_uri };
diff --git a/packages/taler-harness/src/integrationtests/test-payment-template.ts b/packages/taler-harness/src/integrationtests/test-payment-template.ts
index 707be52e1..e77236a9a 100644
--- a/packages/taler-harness/src/integrationtests/test-payment-template.ts
+++ b/packages/taler-harness/src/integrationtests/test-payment-template.ts
@@ -17,12 +17,17 @@
/**
* Imports.
*/
-import { ConfirmPayResultType, Duration, PreparePayResultType } from "@gnu-taler/taler-util";
+import {
+ ConfirmPayResultType,
+ Duration,
+ MerchantApiClient,
+ PreparePayResultType,
+} from "@gnu-taler/taler-util";
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
-import { GlobalTestState, MerchantPrivateApi } from "../harness/harness.js";
+import { GlobalTestState } from "../harness/harness.js";
import {
createSimpleTestkudosEnvironmentV2,
- withdrawViaBankV2
+ withdrawViaBankV2,
} from "../harness/helpers.js";
/**
@@ -34,7 +39,9 @@ export async function runPaymentTemplateTest(t: GlobalTestState) {
const { walletClient, bank, exchange, merchant } =
await createSimpleTestkudosEnvironmentV2(t);
- await MerchantPrivateApi.createTemplate(merchant, "default", {
+ const merchantClient = new MerchantApiClient(merchant.makeInstanceBaseUrl());
+
+ await merchantClient.createTemplate({
template_id: "template1",
template_description: "my test template",
template_contract: {
@@ -50,7 +57,12 @@ export async function runPaymentTemplateTest(t: GlobalTestState) {
// Withdraw digital cash into the wallet.
- const wres = await withdrawViaBankV2(t, { walletClient, bank, exchange, amount: "TESTKUDOS:20" });
+ const wres = await withdrawViaBankV2(t, {
+ walletClient,
+ bank,
+ exchange,
+ amount: "TESTKUDOS:20",
+ });
await wres.withdrawalFinishedCond;
// Request a template payment
@@ -79,13 +91,10 @@ export async function runPaymentTemplateTest(t: GlobalTestState) {
// Check if payment was successful.
- const orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus(
- merchant,
- {
- orderId: preparePayResult.contractTerms.order_id,
- instance: "default",
- },
- );
+ const orderStatus = await merchantClient.queryPrivateOrderStatus({
+ orderId: preparePayResult.contractTerms.order_id,
+ instance: "default",
+ });
t.assertTrue(orderStatus.order_status === "paid");
await walletClient.call(WalletApiOperation.TestingWaitTransactionsFinal, {});
diff --git a/packages/taler-harness/src/integrationtests/test-payment-transient.ts b/packages/taler-harness/src/integrationtests/test-payment-transient.ts
index c2a8e37c5..1911b5e92 100644
--- a/packages/taler-harness/src/integrationtests/test-payment-transient.ts
+++ b/packages/taler-harness/src/integrationtests/test-payment-transient.ts
@@ -19,6 +19,7 @@
*/
import {
ConfirmPayResultType,
+ MerchantApiClient,
PreparePayResultType,
TalerErrorCode,
TalerErrorDetail,
@@ -27,13 +28,12 @@ import {
} from "@gnu-taler/taler-util";
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
import { FaultInjectionResponseContext } from "../harness/faultInjection.js";
-import { GlobalTestState, MerchantPrivateApi, harnessHttpLib } from "../harness/harness.js";
+import { GlobalTestState, harnessHttpLib } from "../harness/harness.js";
import {
createFaultInjectedMerchantTestkudosEnvironment,
- withdrawViaBank,
+ withdrawViaBankV2,
} from "../harness/helpers.js";
-
/**
* Run test for a payment where the merchant has a transient
* failure in /pay
@@ -41,21 +41,25 @@ import {
export async function runPaymentTransientTest(t: GlobalTestState) {
// Set up test environment
- const { wallet, bank, exchange, faultyMerchant, faultyExchange } =
+ const { walletClient, bank, faultyMerchant, faultyExchange } =
await createFaultInjectedMerchantTestkudosEnvironment(t);
+ const merchantClient = new MerchantApiClient(
+ faultyMerchant.makeInstanceBaseUrl(),
+ );
+
// Withdraw digital cash into the wallet.
- await withdrawViaBank(t, {
- wallet,
+ const wres = await withdrawViaBankV2(t, {
+ walletClient,
bank,
exchange: faultyExchange,
amount: "TESTKUDOS:20",
});
- const merchant = faultyMerchant;
+ await wres.withdrawalFinishedCond;
- let orderResp = await MerchantPrivateApi.createOrder(merchant, "default", {
+ let orderResp = await merchantClient.createOrder({
order: {
summary: "Buy me!",
amount: "TESTKUDOS:5",
@@ -64,7 +68,7 @@ export async function runPaymentTransientTest(t: GlobalTestState) {
},
});
- let orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus(merchant, {
+ let orderStatus = await merchantClient.queryPrivateOrderStatus({
orderId: orderResp.order_id,
sessionId: "mysession-one",
});
@@ -88,7 +92,7 @@ export async function runPaymentTransientTest(t: GlobalTestState) {
console.log(pubUnpaidStatus);
- let preparePayResp = await wallet.client.call(
+ let preparePayResp = await walletClient.call(
WalletApiOperation.PreparePayForUri,
{
talerPayUri: pubUnpaidStatus.taler_pay_uri,
@@ -136,7 +140,7 @@ export async function runPaymentTransientTest(t: GlobalTestState) {
},
});
- const confirmPayResp = await wallet.client.call(
+ const confirmPayResp = await walletClient.call(
WalletApiOperation.ConfirmPay,
{
proposalId,
@@ -148,7 +152,7 @@ export async function runPaymentTransientTest(t: GlobalTestState) {
t.assertTrue(confirmPayResp.type === ConfirmPayResultType.Pending);
t.assertTrue(faultInjected);
- const confirmPayRespTwo = await wallet.client.call(
+ const confirmPayRespTwo = await walletClient.call(
WalletApiOperation.ConfirmPay,
{
proposalId,
diff --git a/packages/taler-harness/src/integrationtests/test-paywall-flow.ts b/packages/taler-harness/src/integrationtests/test-paywall-flow.ts
index 5f63d4fac..247ec9cad 100644
--- a/packages/taler-harness/src/integrationtests/test-paywall-flow.ts
+++ b/packages/taler-harness/src/integrationtests/test-paywall-flow.ts
@@ -17,14 +17,15 @@
/**
* Imports.
*/
-import { GlobalTestState, MerchantPrivateApi, harnessHttpLib } from "../harness/harness.js";
import {
- PreparePayResultType,
- codecForMerchantOrderStatusUnpaid,
ConfirmPayResultType,
+ MerchantApiClient,
+ PreparePayResultType,
URL,
+ codecForMerchantOrderStatusUnpaid,
} from "@gnu-taler/taler-util";
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
+import { GlobalTestState, harnessHttpLib } from "../harness/harness.js";
import {
createSimpleTestkudosEnvironmentV2,
withdrawViaBankV2,
@@ -39,6 +40,8 @@ export async function runPaywallFlowTest(t: GlobalTestState) {
const { walletClient, bank, exchange, merchant } =
await createSimpleTestkudosEnvironmentV2(t);
+ const merchantClient = new MerchantApiClient(merchant.makeInstanceBaseUrl());
+
// Withdraw digital cash into the wallet.
const wres = await withdrawViaBankV2(t, {
@@ -59,7 +62,7 @@ export async function runPaywallFlowTest(t: GlobalTestState) {
* =========================================================================
*/
- let orderResp = await MerchantPrivateApi.createOrder(merchant, "default", {
+ let orderResp = await merchantClient.createOrder({
order: {
summary: "Buy me!",
amount: "TESTKUDOS:5",
@@ -70,7 +73,7 @@ export async function runPaywallFlowTest(t: GlobalTestState) {
const firstOrderId = orderResp.order_id;
- let orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus(merchant, {
+ let orderStatus = await merchantClient.queryPrivateOrderStatus({
orderId: orderResp.order_id,
sessionId: "mysession-one",
});
@@ -82,7 +85,9 @@ export async function runPaywallFlowTest(t: GlobalTestState) {
t.assertTrue(orderStatus.already_paid_order_id === undefined);
let publicOrderStatusUrl = new URL(orderStatus.order_status_url);
- let publicOrderStatusResp = await harnessHttpLib.fetch(publicOrderStatusUrl.href);
+ let publicOrderStatusResp = await harnessHttpLib.fetch(
+ publicOrderStatusUrl.href,
+ );
if (publicOrderStatusResp.status != 402) {
throw Error(
@@ -142,7 +147,7 @@ export async function runPaywallFlowTest(t: GlobalTestState) {
* =========================================================================
*/
- orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus(merchant, {
+ orderStatus = await merchantClient.queryPrivateOrderStatus({
orderId: orderResp.order_id,
sessionId: "mysession-two",
});
@@ -169,7 +174,7 @@ export async function runPaywallFlowTest(t: GlobalTestState) {
* =========================================================================
*/
- orderResp = await MerchantPrivateApi.createOrder(merchant, "default", {
+ orderResp = await merchantClient.createOrder({
order: {
summary: "Buy me!",
amount: "TESTKUDOS:5",
@@ -181,7 +186,7 @@ export async function runPaywallFlowTest(t: GlobalTestState) {
const secondOrderId = orderResp.order_id;
- orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus(merchant, {
+ orderStatus = await merchantClient.queryPrivateOrderStatus({
orderId: secondOrderId,
sessionId: "mysession-three",
});
@@ -206,7 +211,7 @@ export async function runPaywallFlowTest(t: GlobalTestState) {
// The first order should now be paid under "mysession-three",
// as the wallet did re-purchase detection
- orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus(merchant, {
+ orderStatus = await merchantClient.queryPrivateOrderStatus({
orderId: firstOrderId,
sessionId: "mysession-three",
});
@@ -215,7 +220,7 @@ export async function runPaywallFlowTest(t: GlobalTestState) {
// Check that with a completely new session ID, the status would NOT
// be paid.
- orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus(merchant, {
+ orderStatus = await merchantClient.queryPrivateOrderStatus({
orderId: firstOrderId,
sessionId: "mysession-four",
});
diff --git a/packages/taler-harness/src/integrationtests/test-peer-repair.ts b/packages/taler-harness/src/integrationtests/test-peer-repair.ts
index b09bff2dc..d457ce1e5 100644
--- a/packages/taler-harness/src/integrationtests/test-peer-repair.ts
+++ b/packages/taler-harness/src/integrationtests/test-peer-repair.ts
@@ -130,7 +130,7 @@ export async function runPeerRepairTest(t: GlobalTestState) {
);
await wallet2.client.call(WalletApiOperation.ConfirmPeerPushCredit, {
- peerPushPaymentIncomingId: resp2.peerPushPaymentIncomingId,
+ peerPushCreditId: resp2.peerPushCreditId,
});
await peerPushCreditDone1Cond;
diff --git a/packages/taler-harness/src/integrationtests/test-peer-to-peer-pull.ts b/packages/taler-harness/src/integrationtests/test-peer-to-peer-pull.ts
index 5b55b1de1..25c000808 100644
--- a/packages/taler-harness/src/integrationtests/test-peer-to-peer-pull.ts
+++ b/packages/taler-harness/src/integrationtests/test-peer-to-peer-pull.ts
@@ -123,7 +123,7 @@ export async function runPeerToPeerPullTest(t: GlobalTestState) {
);
await wallet2.client.call(WalletApiOperation.ConfirmPeerPullDebit, {
- peerPullPaymentIncomingId: checkResp.peerPullPaymentIncomingId,
+ peerPullDebitId: checkResp.peerPullDebitId,
});
await peerPullCreditDoneCond;
diff --git a/packages/taler-harness/src/integrationtests/test-peer-to-peer-push.ts b/packages/taler-harness/src/integrationtests/test-peer-to-peer-push.ts
index 26f70a5cc..018fa2020 100644
--- a/packages/taler-harness/src/integrationtests/test-peer-to-peer-push.ts
+++ b/packages/taler-harness/src/integrationtests/test-peer-to-peer-push.ts
@@ -126,7 +126,7 @@ export async function runPeerToPeerPushTest(t: GlobalTestState) {
const acceptResp = await w2.walletClient.call(
WalletApiOperation.ConfirmPeerPushCredit,
{
- peerPushPaymentIncomingId: checkResp.peerPushPaymentIncomingId,
+ peerPushCreditId: checkResp.peerPushCreditId,
},
);
diff --git a/packages/taler-harness/src/integrationtests/test-refund-auto.ts b/packages/taler-harness/src/integrationtests/test-refund-auto.ts
index 5648835d5..e8bfecefa 100644
--- a/packages/taler-harness/src/integrationtests/test-refund-auto.ts
+++ b/packages/taler-harness/src/integrationtests/test-refund-auto.ts
@@ -17,9 +17,13 @@
/**
* Imports.
*/
-import { Duration, durationFromSpec } from "@gnu-taler/taler-util";
+import {
+ Duration,
+ MerchantApiClient,
+ durationFromSpec,
+} from "@gnu-taler/taler-util";
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
-import { GlobalTestState, MerchantPrivateApi } from "../harness/harness.js";
+import { GlobalTestState } from "../harness/harness.js";
import {
createSimpleTestkudosEnvironmentV2,
withdrawViaBankV2,
@@ -34,6 +38,8 @@ export async function runRefundAutoTest(t: GlobalTestState) {
const { walletClient, bank, exchange, merchant } =
await createSimpleTestkudosEnvironmentV2(t);
+ const merchantClient = new MerchantApiClient(merchant.makeInstanceBaseUrl());
+
// Withdraw digital cash into the wallet.
const wres = await withdrawViaBankV2(t, {
@@ -46,7 +52,7 @@ export async function runRefundAutoTest(t: GlobalTestState) {
await wres.withdrawalFinishedCond;
// Set up order.
- const orderResp = await MerchantPrivateApi.createOrder(merchant, "default", {
+ const orderResp = await merchantClient.createOrder({
order: {
summary: "Buy me!",
amount: "TESTKUDOS:5",
@@ -60,7 +66,7 @@ export async function runRefundAutoTest(t: GlobalTestState) {
),
});
- let orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus(merchant, {
+ let orderStatus = await merchantClient.queryPrivateOrderStatus({
orderId: orderResp.order_id,
});
@@ -78,13 +84,13 @@ export async function runRefundAutoTest(t: GlobalTestState) {
// Check if payment was successful.
- orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus(merchant, {
+ orderStatus = await merchantClient.queryPrivateOrderStatus({
orderId: orderResp.order_id,
});
t.assertTrue(orderStatus.order_status === "paid");
- const ref = await MerchantPrivateApi.giveRefund(merchant, {
+ const ref = await merchantClient.giveRefund({
amount: "TESTKUDOS:5",
instance: "default",
justification: "foo",
diff --git a/packages/taler-harness/src/integrationtests/test-refund-gone.ts b/packages/taler-harness/src/integrationtests/test-refund-gone.ts
index 5f226c7dd..d50919934 100644
--- a/packages/taler-harness/src/integrationtests/test-refund-gone.ts
+++ b/packages/taler-harness/src/integrationtests/test-refund-gone.ts
@@ -17,18 +17,19 @@
/**
* Imports.
*/
-import { GlobalTestState, MerchantPrivateApi } from "../harness/harness.js";
-import {
- createSimpleTestkudosEnvironment,
- withdrawViaBank,
- applyTimeTravel,
-} from "../harness/helpers.js";
import {
AbsoluteTime,
Duration,
+ MerchantApiClient,
durationFromSpec,
} from "@gnu-taler/taler-util";
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
+import { GlobalTestState } from "../harness/harness.js";
+import {
+ applyTimeTravelV2,
+ createSimpleTestkudosEnvironmentV2,
+ withdrawViaBankV2,
+} from "../harness/helpers.js";
/**
* Run test for basic, bank-integrated withdrawal.
@@ -36,16 +37,25 @@ import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
export async function runRefundGoneTest(t: GlobalTestState) {
// Set up test environment
- const { wallet, bank, exchange, merchant } =
- await createSimpleTestkudosEnvironment(t);
+ const { walletClient, bank, exchange, merchant } =
+ await createSimpleTestkudosEnvironmentV2(t);
+
+ const merchantClient = new MerchantApiClient(merchant.makeInstanceBaseUrl());
// Withdraw digital cash into the wallet.
- await withdrawViaBank(t, { wallet, bank, exchange, amount: "TESTKUDOS:20" });
+ const wres = await withdrawViaBankV2(t, {
+ walletClient,
+ bank,
+ exchange,
+ amount: "TESTKUDOS:20",
+ });
+
+ await wres.withdrawalFinishedCond;
// Set up order.
- const orderResp = await MerchantPrivateApi.createOrder(merchant, "default", {
+ const orderResp = await merchantClient.createOrder({
order: {
summary: "Buy me!",
amount: "TESTKUDOS:5",
@@ -64,7 +74,7 @@ export async function runRefundGoneTest(t: GlobalTestState) {
),
});
- let orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus(merchant, {
+ let orderStatus = await merchantClient.queryPrivateOrderStatus({
orderId: orderResp.order_id,
});
@@ -72,17 +82,17 @@ export async function runRefundGoneTest(t: GlobalTestState) {
// Make wallet pay for the order
- const r1 = await wallet.client.call(WalletApiOperation.PreparePayForUri, {
+ const r1 = await walletClient.call(WalletApiOperation.PreparePayForUri, {
talerPayUri: orderStatus.taler_pay_uri,
});
- const r2 = await wallet.client.call(WalletApiOperation.ConfirmPay, {
- proposalId: r1.proposalId,
+ const r2 = await walletClient.call(WalletApiOperation.ConfirmPay, {
+ transactionId: r1.transactionId,
});
// Check if payment was successful.
- orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus(merchant, {
+ orderStatus = await merchantClient.queryPrivateOrderStatus({
orderId: orderResp.order_id,
});
@@ -90,11 +100,14 @@ export async function runRefundGoneTest(t: GlobalTestState) {
console.log(orderStatus);
- await applyTimeTravel(durationFromSpec({ hours: 1 }), { exchange, wallet });
+ await applyTimeTravelV2(
+ Duration.toMilliseconds(Duration.fromSpec({ hours: 1 })),
+ { exchange, walletClient: walletClient },
+ );
await exchange.runAggregatorOnce();
- const ref = await MerchantPrivateApi.giveRefund(merchant, {
+ const ref = await merchantClient.giveRefund({
amount: "TESTKUDOS:5",
instance: "default",
justification: "foo",
@@ -103,16 +116,16 @@ export async function runRefundGoneTest(t: GlobalTestState) {
console.log(ref);
- await wallet.client.call(WalletApiOperation.StartRefundQuery, {
+ await walletClient.call(WalletApiOperation.StartRefundQuery, {
transactionId: r1.transactionId,
});
- await wallet.runUntilDone();
+ await walletClient.call(WalletApiOperation.TestingWaitTransactionsFinal, {});
- let r = await wallet.client.call(WalletApiOperation.GetBalances, {});
+ let r = await walletClient.call(WalletApiOperation.GetBalances, {});
console.log(JSON.stringify(r, undefined, 2));
- const r3 = await wallet.client.call(WalletApiOperation.GetTransactions, {});
+ const r3 = await walletClient.call(WalletApiOperation.GetTransactions, {});
console.log(JSON.stringify(r3, undefined, 2));
await t.shutdown();
diff --git a/packages/taler-harness/src/integrationtests/test-refund-incremental.ts b/packages/taler-harness/src/integrationtests/test-refund-incremental.ts
index 8ac0948f2..e7e041ce6 100644
--- a/packages/taler-harness/src/integrationtests/test-refund-incremental.ts
+++ b/packages/taler-harness/src/integrationtests/test-refund-incremental.ts
@@ -20,15 +20,12 @@
import {
Amounts,
Duration,
+ MerchantApiClient,
TransactionType,
durationFromSpec,
} from "@gnu-taler/taler-util";
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
-import {
- GlobalTestState,
- MerchantPrivateApi,
- delayMs,
-} from "../harness/harness.js";
+import { GlobalTestState, delayMs } from "../harness/harness.js";
import {
createSimpleTestkudosEnvironmentV2,
withdrawViaBankV2,
@@ -43,6 +40,8 @@ export async function runRefundIncrementalTest(t: GlobalTestState) {
const { walletClient, bank, exchange, merchant } =
await createSimpleTestkudosEnvironmentV2(t);
+ const merchantClient = new MerchantApiClient(merchant.makeInstanceBaseUrl());
+
// Withdraw digital cash into the wallet.
const wres = await withdrawViaBankV2(t, {
@@ -56,7 +55,7 @@ export async function runRefundIncrementalTest(t: GlobalTestState) {
// Set up order.
- const orderResp = await MerchantPrivateApi.createOrder(merchant, "default", {
+ const orderResp = await merchantClient.createOrder({
order: {
summary: "Buy me!",
amount: "TESTKUDOS:10",
@@ -67,7 +66,7 @@ export async function runRefundIncrementalTest(t: GlobalTestState) {
),
});
- let orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus(merchant, {
+ let orderStatus = await merchantClient.queryPrivateOrderStatus({
orderId: orderResp.order_id,
});
@@ -85,13 +84,13 @@ export async function runRefundIncrementalTest(t: GlobalTestState) {
// Check if payment was successful.
- orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus(merchant, {
+ orderStatus = await merchantClient.queryPrivateOrderStatus({
orderId: orderResp.order_id,
});
t.assertTrue(orderStatus.order_status === "paid");
- let ref = await MerchantPrivateApi.giveRefund(merchant, {
+ let ref = await merchantClient.giveRefund({
amount: "TESTKUDOS:2.5",
instance: "default",
justification: "foo",
@@ -120,7 +119,7 @@ export async function runRefundIncrementalTest(t: GlobalTestState) {
// refund will be grouped with the previous one.
await delayMs(1200);
- ref = await MerchantPrivateApi.giveRefund(merchant, {
+ ref = await merchantClient.giveRefund({
amount: "TESTKUDOS:5",
instance: "default",
justification: "bar",
@@ -133,7 +132,7 @@ export async function runRefundIncrementalTest(t: GlobalTestState) {
// refund will be grouped with the previous one.
await delayMs(1200);
- ref = await MerchantPrivateApi.giveRefund(merchant, {
+ ref = await merchantClient.giveRefund({
amount: "TESTKUDOS:10",
instance: "default",
justification: "bar",
@@ -153,7 +152,7 @@ export async function runRefundIncrementalTest(t: GlobalTestState) {
console.log(wr);
}
- orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus(merchant, {
+ orderStatus = await merchantClient.queryPrivateOrderStatus({
orderId: orderResp.order_id,
});
diff --git a/packages/taler-harness/src/integrationtests/test-refund.ts b/packages/taler-harness/src/integrationtests/test-refund.ts
index 44848d88a..aeeb91f38 100644
--- a/packages/taler-harness/src/integrationtests/test-refund.ts
+++ b/packages/taler-harness/src/integrationtests/test-refund.ts
@@ -20,11 +20,12 @@
import {
Duration,
durationFromSpec,
+ MerchantApiClient,
NotificationType,
TransactionMajorState,
} from "@gnu-taler/taler-util";
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
-import { GlobalTestState, MerchantPrivateApi } from "../harness/harness.js";
+import { GlobalTestState } from "../harness/harness.js";
import {
createSimpleTestkudosEnvironmentV2,
withdrawViaBankV2,
@@ -43,6 +44,8 @@ export async function runRefundTest(t: GlobalTestState) {
merchant,
} = await createSimpleTestkudosEnvironmentV2(t);
+ const merchantClient = new MerchantApiClient(merchant.makeInstanceBaseUrl());
+
// Withdraw digital cash into the wallet.
const withdrawalRes = await withdrawViaBankV2(t, {
@@ -56,7 +59,7 @@ export async function runRefundTest(t: GlobalTestState) {
// Set up order.
- const orderResp = await MerchantPrivateApi.createOrder(merchant, "default", {
+ const orderResp = await merchantClient.createOrder({
order: {
summary: "Buy me!",
amount: "TESTKUDOS:5",
@@ -67,7 +70,7 @@ export async function runRefundTest(t: GlobalTestState) {
),
});
- let orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus(merchant, {
+ let orderStatus = await merchantClient.queryPrivateOrderStatus({
orderId: orderResp.order_id,
});
@@ -85,13 +88,13 @@ export async function runRefundTest(t: GlobalTestState) {
// Check if payment was successful.
- orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus(merchant, {
+ orderStatus = await merchantClient.queryPrivateOrderStatus({
orderId: orderResp.order_id,
});
t.assertTrue(orderStatus.order_status === "paid");
- const ref = await MerchantPrivateApi.giveRefund(merchant, {
+ const ref = await merchantClient.giveRefund({
amount: "TESTKUDOS:5",
instance: "default",
justification: "foo",
diff --git a/packages/taler-harness/src/integrationtests/test-revocation.ts b/packages/taler-harness/src/integrationtests/test-revocation.ts
index 04707e51a..0cb6987ad 100644
--- a/packages/taler-harness/src/integrationtests/test-revocation.ts
+++ b/packages/taler-harness/src/integrationtests/test-revocation.ts
@@ -28,20 +28,22 @@ import {
BankService,
delayMs,
getPayto,
+ WalletClient,
} from "../harness/harness.js";
import {
- withdrawViaBank,
- makeTestPayment,
- SimpleTestEnvironment,
+ SimpleTestEnvironmentNg,
+ createWalletDaemonWithClient,
+ makeTestPaymentV2,
+ withdrawViaBankV2,
} from "../harness/helpers.js";
async function revokeAllWalletCoins(req: {
- wallet: WalletCli;
+ walletClient: WalletClient;
exchange: ExchangeService;
merchant: MerchantService;
}): Promise<void> {
- const { wallet, exchange, merchant } = req;
- const coinDump = await wallet.client.call(WalletApiOperation.DumpCoins, {});
+ const { walletClient, exchange, merchant } = req;
+ const coinDump = await walletClient.call(WalletApiOperation.DumpCoins, {});
console.log(coinDump);
const usedDenomHashes = new Set<string>();
for (const coin of coinDump.coins) {
@@ -60,7 +62,7 @@ async function revokeAllWalletCoins(req: {
async function createTestEnvironment(
t: GlobalTestState,
-): Promise<SimpleTestEnvironment> {
+): Promise<SimpleTestEnvironmentNg> {
const db = await setupDb(t);
const bank = await BankService.create(t, {
@@ -120,13 +122,13 @@ async function createTestEnvironment(
await merchant.start();
await merchant.pingUntilAvailable();
- await merchant.addInstance({
+ await merchant.addInstanceWithWireAccount({
id: "default",
name: "Default Instance",
paytoUris: [getPayto("merchant-default")],
});
- await merchant.addInstance({
+ await merchant.addInstanceWithWireAccount({
id: "minst1",
name: "minst1",
paytoUris: [getPayto("minst1")],
@@ -136,11 +138,19 @@ async function createTestEnvironment(
const wallet = new WalletCli(t);
+ const { walletService, walletClient } = await createWalletDaemonWithClient(
+ t,
+ {
+ name: "default",
+ },
+ );
+
return {
commonDb: db,
exchange,
merchant,
- wallet,
+ walletClient,
+ walletService,
bank,
exchangeBankAccount,
};
@@ -152,24 +162,30 @@ async function createTestEnvironment(
export async function runRevocationTest(t: GlobalTestState) {
// Set up test environment
- const { wallet, bank, exchange, merchant } = await createTestEnvironment(t);
+ const { walletClient, bank, exchange, merchant } =
+ await createTestEnvironment(t);
// Withdraw digital cash into the wallet.
- await withdrawViaBank(t, { wallet, bank, exchange, amount: "TESTKUDOS:15" });
+ const wres = await withdrawViaBankV2(t, {
+ walletClient,
+ bank,
+ exchange,
+ amount: "TESTKUDOS:15",
+ });
+ await wres.withdrawalFinishedCond;
console.log("revoking first time");
- await revokeAllWalletCoins({ wallet, exchange, merchant });
+ await revokeAllWalletCoins({ walletClient, exchange, merchant });
// FIXME: this shouldn't be necessary once https://bugs.taler.net/n/6565
// is implemented.
- await wallet.client.call(WalletApiOperation.AddExchange, {
+ await walletClient.call(WalletApiOperation.AddExchange, {
exchangeBaseUrl: exchange.baseUrl,
forceUpdate: true,
});
- await wallet.runUntilDone();
- await wallet.runUntilDone();
- const bal = await wallet.client.call(WalletApiOperation.GetBalances, {});
+ await walletClient.call(WalletApiOperation.TestingWaitTransactionsFinal, {});
+ const bal = await walletClient.call(WalletApiOperation.GetBalances, {});
console.log("wallet balance", bal);
const order = {
@@ -178,39 +194,43 @@ export async function runRevocationTest(t: GlobalTestState) {
fulfillment_url: "taler://fulfillment-success/thx",
};
- await makeTestPayment(t, { wallet, merchant, order });
+ await makeTestPaymentV2(t, { walletClient, merchant, order });
- wallet.deleteDatabase();
+ await walletClient.call(WalletApiOperation.ClearDb, {});
- await withdrawViaBank(t, { wallet, bank, exchange, amount: "TESTKUDOS:15" });
+ await withdrawViaBankV2(t, {
+ walletClient,
+ bank,
+ exchange,
+ amount: "TESTKUDOS:15",
+ });
- const coinDump = await wallet.client.call(WalletApiOperation.DumpCoins, {});
+ const coinDump = await walletClient.call(WalletApiOperation.DumpCoins, {});
console.log(coinDump);
const coinPubList = coinDump.coins.map((x) => x.coin_pub);
- await wallet.client.call(WalletApiOperation.ForceRefresh, {
+ await walletClient.call(WalletApiOperation.ForceRefresh, {
coinPubList,
});
- await wallet.runUntilDone();
+ await walletClient.call(WalletApiOperation.TestingWaitTransactionsFinal, {});
console.log("revoking second time");
- await revokeAllWalletCoins({ wallet, exchange, merchant });
+ await revokeAllWalletCoins({ walletClient, exchange, merchant });
// FIXME: this shouldn't be necessary once https://bugs.taler.net/n/6565
// is implemented.
- await wallet.client.call(WalletApiOperation.AddExchange, {
+ await walletClient.call(WalletApiOperation.AddExchange, {
exchangeBaseUrl: exchange.baseUrl,
forceUpdate: true,
});
- await wallet.runUntilDone();
- await wallet.runUntilDone();
+ await walletClient.call(WalletApiOperation.TestingWaitTransactionsFinal, {});
{
- const bal = await wallet.client.call(WalletApiOperation.GetBalances, {});
+ const bal = await walletClient.call(WalletApiOperation.GetBalances, {});
console.log("wallet balance", bal);
}
- await makeTestPayment(t, { wallet, merchant, order });
+ await makeTestPaymentV2(t, { walletClient, merchant, order });
}
runRevocationTest.timeoutMs = 120000;
runRevocationTest.suites = ["wallet"];
-runRevocationTest.excludeByDefault = true; \ No newline at end of file
+runRevocationTest.experimental = true;
diff --git a/packages/taler-harness/src/integrationtests/test-stored-backups.ts b/packages/taler-harness/src/integrationtests/test-stored-backups.ts
new file mode 100644
index 000000000..a3a5e6ca3
--- /dev/null
+++ b/packages/taler-harness/src/integrationtests/test-stored-backups.ts
@@ -0,0 +1,112 @@
+/*
+ This file is part of GNU Taler
+ (C) 2023 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/>
+ */
+
+/**
+ * Imports.
+ */
+import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
+import { GlobalTestState } from "../harness/harness.js";
+import {
+ withdrawViaBankV2,
+ makeTestPaymentV2,
+ useSharedTestkudosEnvironment,
+} from "../harness/helpers.js";
+
+/**
+ * Test stored backup wallet-core API.
+ */
+export async function runStoredBackupsTest(t: GlobalTestState) {
+ // Set up test environment
+
+ const { walletClient, bank, exchange, merchant } =
+ await useSharedTestkudosEnvironment(t);
+
+ // Withdraw digital cash into the wallet.
+
+ const wres = await withdrawViaBankV2(t, {
+ walletClient,
+ bank,
+ exchange,
+ amount: "TESTKUDOS:20",
+ });
+
+ await wres;
+
+ await walletClient.call(WalletApiOperation.TestingWaitTransactionsFinal, {});
+
+ const sb1Resp = await walletClient.call(
+ WalletApiOperation.CreateStoredBackup,
+ {},
+ );
+ const sbList = await walletClient.call(
+ WalletApiOperation.ListStoredBackups,
+ {},
+ );
+ t.assertTrue(sbList.storedBackups.length === 1);
+ t.assertTrue(sbList.storedBackups[0].name === sb1Resp.name);
+
+ const order = {
+ summary: "Buy me!",
+ amount: "TESTKUDOS:5",
+ fulfillment_url: "taler://fulfillment-success/thx",
+ };
+
+ await makeTestPaymentV2(t, { walletClient, merchant, order });
+ await walletClient.call(WalletApiOperation.TestingWaitTransactionsFinal, {});
+
+ const txn1 = await walletClient.call(WalletApiOperation.GetTransactions, {});
+ t.assertDeepEqual(txn1.transactions.length, 2);
+
+ // Recover from the stored backup now.
+
+ const sb2Resp = await walletClient.call(
+ WalletApiOperation.CreateStoredBackup,
+ {},
+ );
+
+ console.log("recovering backup");
+
+ await walletClient.call(WalletApiOperation.RecoverStoredBackup, {
+ name: sb1Resp.name,
+ });
+
+ console.log("first recovery done");
+
+ // Recovery went well, now we can delete the backup
+ // of the old database we stored before importing.
+ {
+ const sbl1 = await walletClient.call(
+ WalletApiOperation.ListStoredBackups,
+ {},
+ );
+ t.assertTrue(sbl1.storedBackups.length === 2);
+
+ await walletClient.call(WalletApiOperation.DeleteStoredBackup, {
+ name: sb1Resp.name,
+ });
+ const sbl2 = await walletClient.call(
+ WalletApiOperation.ListStoredBackups,
+ {},
+ );
+ t.assertTrue(sbl2.storedBackups.length === 1);
+ }
+
+ const txn2 = await walletClient.call(WalletApiOperation.GetTransactions, {});
+ // We only have the withdrawal after restoring
+ t.assertDeepEqual(txn2.transactions.length, 1);
+}
+
+runStoredBackupsTest.suites = ["wallet"];
diff --git a/packages/taler-harness/src/integrationtests/test-timetravel-autorefresh.ts b/packages/taler-harness/src/integrationtests/test-timetravel-autorefresh.ts
index 50f9cbcd3..b94f7757c 100644
--- a/packages/taler-harness/src/integrationtests/test-timetravel-autorefresh.ts
+++ b/packages/taler-harness/src/integrationtests/test-timetravel-autorefresh.ts
@@ -21,6 +21,7 @@ import {
ConfirmPayResultType,
Duration,
durationFromSpec,
+ MerchantApiClient,
PreparePayResultType,
} from "@gnu-taler/taler-util";
import {
@@ -31,42 +32,16 @@ import { makeNoFeeCoinConfig } from "../harness/denomStructures.js";
import {
BankService,
ExchangeService,
+ getPayto,
GlobalTestState,
- MerchantPrivateApi,
MerchantService,
setupDb,
- WalletCli,
- getPayto,
} from "../harness/harness.js";
-import { startWithdrawViaBank, withdrawViaBank } from "../harness/helpers.js";
-
-async function applyTimeTravel(
- timetravelDuration: Duration,
- s: {
- exchange?: ExchangeService;
- merchant?: MerchantService;
- wallet?: WalletCli;
- },
-): Promise<void> {
- if (s.exchange) {
- await s.exchange.stop();
- s.exchange.setTimetravel(timetravelDuration);
- await s.exchange.start();
- await s.exchange.pingUntilAvailable();
- }
-
- if (s.merchant) {
- await s.merchant.stop();
- s.merchant.setTimetravel(timetravelDuration);
- await s.merchant.start();
- await s.merchant.pingUntilAvailable();
- }
-
- if (s.wallet) {
- console.log("setting wallet time travel to", timetravelDuration);
- s.wallet.setTimetravel(timetravelDuration);
- }
-}
+import {
+ applyTimeTravelV2,
+ createWalletDaemonWithClient,
+ withdrawViaBankV2,
+} from "../harness/helpers.js";
/**
* Basic time travel test.
@@ -119,13 +94,13 @@ export async function runTimetravelAutorefreshTest(t: GlobalTestState) {
await merchant.start();
await merchant.pingUntilAvailable();
- await merchant.addInstance({
+ await merchant.addInstanceWithWireAccount({
id: "default",
name: "Default Instance",
paytoUris: [getPayto("merchant-default")],
});
- await merchant.addInstance({
+ await merchant.addInstanceWithWireAccount({
id: "minst1",
name: "minst1",
paytoUris: [getPayto("minst1")],
@@ -133,54 +108,70 @@ export async function runTimetravelAutorefreshTest(t: GlobalTestState) {
console.log("setup done!");
- const wallet = new WalletCli(t);
+ const { walletClient } = await createWalletDaemonWithClient(t, {
+ name: "w1",
+ });
+
+ const merchantClient = new MerchantApiClient(merchant.makeInstanceBaseUrl());
// Withdraw digital cash into the wallet.
- await withdrawViaBank(t, { wallet, bank, exchange, amount: "TESTKUDOS:15" });
+ const wres = await withdrawViaBankV2(t, {
+ walletClient,
+ bank,
+ exchange,
+ amount: "TESTKUDOS:15",
+ });
+ await wres.withdrawalFinishedCond;
// Travel into the future, the deposit expiration is two years
// into the future.
console.log("applying first time travel");
- await applyTimeTravel(durationFromSpec({ days: 400 }), {
- wallet,
- exchange,
- merchant,
- });
+ await applyTimeTravelV2(
+ Duration.toMilliseconds(durationFromSpec({ days: 400 })),
+ {
+ walletClient,
+ exchange,
+ merchant,
+ },
+ );
- await wallet.runUntilDone();
+ await walletClient.call(WalletApiOperation.TestingWaitTransactionsFinal, {});
let p: PendingOperationsResponse;
- p = await wallet.client.call(WalletApiOperation.GetPendingOperations, {});
+ p = await walletClient.call(WalletApiOperation.GetPendingOperations, {});
console.log("pending operations after first time travel");
console.log(JSON.stringify(p, undefined, 2));
- await startWithdrawViaBank(t, {
- wallet,
+ await withdrawViaBankV2(t, {
+ walletClient,
bank,
exchange,
amount: "TESTKUDOS:20",
});
- await wallet.runUntilDone();
+ await walletClient.call(WalletApiOperation.TestingWaitTransactionsFinal, {});
// Travel into the future, the deposit expiration is two years
// into the future.
console.log("applying second time travel");
- await applyTimeTravel(durationFromSpec({ years: 2, months: 6 }), {
- wallet,
- exchange,
- merchant,
- });
+ await applyTimeTravelV2(
+ Duration.toMilliseconds(durationFromSpec({ years: 2, months: 6 })),
+ {
+ walletClient,
+ exchange,
+ merchant,
+ },
+ );
// At this point, the original coins should've been refreshed.
// It would be too late to refresh them now, as we're past
// the two year deposit expiration.
- await wallet.runUntilDone();
+ await walletClient.call(WalletApiOperation.TestingWaitTransactionsFinal, {});
- const orderResp = await MerchantPrivateApi.createOrder(merchant, "default", {
+ const orderResp = await merchantClient.createOrder({
order: {
fulfillment_url: "http://example.com",
summary: "foo",
@@ -188,17 +179,14 @@ export async function runTimetravelAutorefreshTest(t: GlobalTestState) {
},
});
- const orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus(
- merchant,
- {
- orderId: orderResp.order_id,
- instance: "default",
- },
- );
+ const orderStatus = await merchantClient.queryPrivateOrderStatus({
+ orderId: orderResp.order_id,
+ instance: "default",
+ });
t.assertTrue(orderStatus.order_status === "unpaid");
- const r = await wallet.client.call(WalletApiOperation.PreparePayForUri, {
+ const r = await walletClient.call(WalletApiOperation.PreparePayForUri, {
talerPayUri: orderStatus.taler_pay_uri,
});
@@ -206,7 +194,7 @@ export async function runTimetravelAutorefreshTest(t: GlobalTestState) {
t.assertTrue(r.status === PreparePayResultType.PaymentPossible);
- const cpr = await wallet.client.call(WalletApiOperation.ConfirmPay, {
+ const cpr = await walletClient.call(WalletApiOperation.ConfirmPay, {
proposalId: r.proposalId,
});
diff --git a/packages/taler-harness/src/integrationtests/test-timetravel-withdraw.ts b/packages/taler-harness/src/integrationtests/test-timetravel-withdraw.ts
index ca3e67647..e594d2d72 100644
--- a/packages/taler-harness/src/integrationtests/test-timetravel-withdraw.ts
+++ b/packages/taler-harness/src/integrationtests/test-timetravel-withdraw.ts
@@ -17,13 +17,16 @@
/**
* Imports.
*/
-import { Duration, TransactionMajorState, TransactionType } from "@gnu-taler/taler-util";
+import {
+ Duration,
+ TransactionMajorState,
+ TransactionType,
+} from "@gnu-taler/taler-util";
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
import { GlobalTestState } from "../harness/harness.js";
import {
- createSimpleTestkudosEnvironment,
- startWithdrawViaBank,
- withdrawViaBank,
+ createSimpleTestkudosEnvironmentV2,
+ withdrawViaBankV2,
} from "../harness/helpers.js";
/**
@@ -32,12 +35,18 @@ import {
export async function runTimetravelWithdrawTest(t: GlobalTestState) {
// Set up test environment
- const { wallet, bank, exchange, merchant } =
- await createSimpleTestkudosEnvironment(t);
+ const { walletClient, bank, exchange, merchant } =
+ await createSimpleTestkudosEnvironmentV2(t);
// Withdraw digital cash into the wallet.
- await withdrawViaBank(t, { wallet, bank, exchange, amount: "TESTKUDOS:15" });
+ const wres1 = await withdrawViaBankV2(t, {
+ walletClient,
+ bank,
+ exchange,
+ amount: "TESTKUDOS:15",
+ });
+ await wres1.withdrawalFinishedCond;
// Travel 400 days into the future,
// as the deposit expiration is two years
@@ -47,21 +56,21 @@ export async function runTimetravelWithdrawTest(t: GlobalTestState) {
};
await exchange.stop();
- exchange.setTimetravel(timetravelDuration);
+ exchange.setTimetravel(Duration.toMilliseconds(timetravelDuration));
await exchange.start();
await exchange.pingUntilAvailable();
await exchange.keyup();
await merchant.stop();
- merchant.setTimetravel(timetravelDuration);
+ merchant.setTimetravel(Duration.toMilliseconds(timetravelDuration));
await merchant.start();
await merchant.pingUntilAvailable();
console.log("starting withdrawal via bank");
// This should fail, as the wallet didn't time travel yet.
- await startWithdrawViaBank(t, {
- wallet,
+ await withdrawViaBankV2(t, {
+ walletClient,
bank,
exchange,
amount: "TESTKUDOS:20",
@@ -71,10 +80,7 @@ export async function runTimetravelWithdrawTest(t: GlobalTestState) {
// Check that transactions are correct for the failed withdrawal
{
- console.log("running until done (should run into maxRetries limit)");
- await wallet.runUntilDone({ maxRetries: 5 });
- console.log("wallet done running");
- const transactions = await wallet.client.call(
+ const transactions = await walletClient.call(
WalletApiOperation.GetTransactions,
{},
);
@@ -88,7 +94,9 @@ export async function runTimetravelWithdrawTest(t: GlobalTestState) {
// Now we also let the wallet time travel
- wallet.setTimetravel(timetravelDuration);
+ walletClient.call(WalletApiOperation.TestingSetTimetravel, {
+ offsetMs: Duration.toMilliseconds(timetravelDuration),
+ });
// This doesn't work yet, see https://bugs.taler.net/n/6585
diff --git a/packages/taler-harness/src/integrationtests/test-tipping.ts b/packages/taler-harness/src/integrationtests/test-tipping.ts
index 53d7f08c8..4140311ab 100644
--- a/packages/taler-harness/src/integrationtests/test-tipping.ts
+++ b/packages/taler-harness/src/integrationtests/test-tipping.ts
@@ -19,17 +19,15 @@
*/
import {
BankAccessApiClient,
- WalletApiOperation,
+ MerchantApiClient,
+ TransactionMajorState,
WireGatewayApiClient,
-} from "@gnu-taler/taler-wallet-core";
+} from "@gnu-taler/taler-util";
import {
- GlobalTestState,
- MerchantApiClient,
- MerchantPrivateApi,
- getWireMethodForTest,
-} from "../harness/harness.js";
+ WalletApiOperation,
+} from "@gnu-taler/taler-wallet-core";
+import { GlobalTestState, getWireMethodForTest } from "../harness/harness.js";
import { createSimpleTestkudosEnvironmentV2 } from "../harness/helpers.js";
-import { TransactionMajorState } from "@gnu-taler/taler-util";
/**
* Run test for basic, bank-integrated withdrawal.
@@ -40,18 +38,12 @@ export async function runTippingTest(t: GlobalTestState) {
const { walletClient, bank, exchange, merchant, exchangeBankAccount } =
await createSimpleTestkudosEnvironmentV2(t);
- const bankAccessApiClient = new BankAccessApiClient({
- allowHttp: true,
- baseUrl: bank.bankAccessApiBaseUrl,
- });
+ const bankAccessApiClient = new BankAccessApiClient(
+ bank.bankAccessApiBaseUrl,
+ );
const mbu = await bankAccessApiClient.createRandomBankUser();
- const merchantClient = new MerchantApiClient(
- merchant.makeInstanceBaseUrl("default"),
- {
- method: "external",
- },
- );
+ const merchantClient = new MerchantApiClient(merchant.makeInstanceBaseUrl());
const tipReserveResp = await merchantClient.createTippingReserve({
exchange_url: exchange.baseUrl,
@@ -66,12 +58,15 @@ export async function runTippingTest(t: GlobalTestState) {
exchangeBankAccount.accountPaytoUri,
);
- const wireGatewayApiClient = new WireGatewayApiClient({
- wireGatewayApiBaseUrl: exchangeBankAccount.wireGatewayApiBaseUrl,
- accountName: exchangeBankAccount.accountName,
- accountPassword: exchangeBankAccount.accountPassword,
- allowHttp: true,
- });
+ const wireGatewayApiClient = new WireGatewayApiClient(
+ exchangeBankAccount.wireGatewayApiBaseUrl,
+ {
+ auth: {
+ username: exchangeBankAccount.accountName,
+ password: exchangeBankAccount.accountPassword,
+ },
+ },
+ );
await wireGatewayApiClient.adminAddIncoming({
amount: "TESTKUDOS:10",
@@ -85,7 +80,7 @@ export async function runTippingTest(t: GlobalTestState) {
await merchant.start();
await merchant.pingUntilAvailable();
- const r = await MerchantPrivateApi.queryTippingReserves(merchant, "default");
+ const r = await merchantClient.queryTippingReserves();
console.log("tipping reserves:", JSON.stringify(r, undefined, 2));
t.assertTrue(r.reserves.length === 1);
@@ -94,7 +89,7 @@ export async function runTippingTest(t: GlobalTestState) {
r.reserves[0].merchant_initial_amount,
);
- const tip = await MerchantPrivateApi.giveTip(merchant, "default", {
+ const tip = await merchantClient.giveTip({
amount: "TESTKUDOS:5",
justification: "why not?",
next_url: "https://example.com/after-tip",
diff --git a/packages/taler-harness/src/integrationtests/test-wallet-backup-basic.ts b/packages/taler-harness/src/integrationtests/test-wallet-backup-basic.ts
index 5321cf5c7..21ac662c2 100644
--- a/packages/taler-harness/src/integrationtests/test-wallet-backup-basic.ts
+++ b/packages/taler-harness/src/integrationtests/test-wallet-backup-basic.ts
@@ -21,9 +21,8 @@ import { j2s } from "@gnu-taler/taler-util";
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
import { GlobalTestState, WalletCli } from "../harness/harness.js";
import {
- createSimpleTestkudosEnvironment,
createSimpleTestkudosEnvironmentV2,
- withdrawViaBank,
+ createWalletDaemonWithClient,
withdrawViaBankV2,
} from "../harness/helpers.js";
import { SyncService } from "../harness/sync.js";
@@ -112,40 +111,42 @@ export async function runWalletBackupBasicTest(t: GlobalTestState) {
const txs = await walletClient.call(WalletApiOperation.GetTransactions, {});
console.log(`backed up transactions ${j2s(txs)}`);
- const wallet2 = new WalletCli(t, "wallet2");
+ const { walletClient: walletClient2 } = await createWalletDaemonWithClient(t, {
+ name: "w2"
+ });
// Check that the second wallet is a fresh wallet.
{
- const bal = await wallet2.client.call(WalletApiOperation.GetBalances, {});
+ const bal = await walletClient2.call(WalletApiOperation.GetBalances, {});
t.assertTrue(bal.balances.length === 0);
}
- await wallet2.client.call(WalletApiOperation.ImportBackupRecovery, {
+ await walletClient2.call(WalletApiOperation.ImportBackupRecovery, {
recovery: backupRecovery,
});
- await wallet2.client.call(WalletApiOperation.RunBackupCycle, {});
+ await walletClient2.call(WalletApiOperation.RunBackupCycle, {});
// Check that now the old balance is available!
{
- const bal = await wallet2.client.call(WalletApiOperation.GetBalances, {});
+ const bal = await walletClient2.call(WalletApiOperation.GetBalances, {});
t.assertTrue(bal.balances.length === 1);
console.log(bal);
}
// Now do some basic checks that the restored wallet is still functional
{
- const txs = await wallet2.client.call(
+ const txs = await walletClient2.call(
WalletApiOperation.GetTransactions,
{},
);
console.log(`restored transactions ${j2s(txs)}`);
- const bal1 = await wallet2.client.call(WalletApiOperation.GetBalances, {});
+ const bal1 = await walletClient2.call(WalletApiOperation.GetBalances, {});
t.assertAmountEquals(bal1.balances[0].available, "TESTKUDOS:14.1");
- await withdrawViaBank(t, {
- wallet: wallet2,
+ await withdrawViaBankV2(t, {
+ walletClient: walletClient2,
bank,
exchange,
amount: "TESTKUDOS:10",
@@ -153,15 +154,15 @@ export async function runWalletBackupBasicTest(t: GlobalTestState) {
await exchange.runWirewatchOnce();
- await wallet2.runUntilDone();
+ await walletClient2.call(WalletApiOperation.TestingWaitTransactionsFinal, {});
- const txs2 = await wallet2.client.call(
+ const txs2 = await walletClient2.call(
WalletApiOperation.GetTransactions,
{},
);
console.log(`tx after withdraw after restore ${j2s(txs2)}`);
- const bal2 = await wallet2.client.call(WalletApiOperation.GetBalances, {});
+ const bal2 = await walletClient2.call(WalletApiOperation.GetBalances, {});
t.assertAmountEquals(bal2.balances[0].available, "TESTKUDOS:23.82");
}
diff --git a/packages/taler-harness/src/integrationtests/test-wallet-backup-doublespend.ts b/packages/taler-harness/src/integrationtests/test-wallet-backup-doublespend.ts
index e3e18d5a4..c761c4fb0 100644
--- a/packages/taler-harness/src/integrationtests/test-wallet-backup-doublespend.ts
+++ b/packages/taler-harness/src/integrationtests/test-wallet-backup-doublespend.ts
@@ -17,18 +17,14 @@
/**
* Imports.
*/
-import { PreparePayResultType } from "@gnu-taler/taler-util";
+import { MerchantApiClient, PreparePayResultType } from "@gnu-taler/taler-util";
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
-import {
- GlobalTestState,
- MerchantPrivateApi,
- WalletCli,
-} from "../harness/harness.js";
+import { GlobalTestState } from "../harness/harness.js";
import {
createSimpleTestkudosEnvironmentV2,
+ createWalletDaemonWithClient,
makeTestPaymentV2,
- withdrawViaBank,
- withdrawViaBankV2
+ withdrawViaBankV2,
} from "../harness/helpers.js";
import { SyncService } from "../harness/sync.js";
@@ -38,6 +34,8 @@ export async function runWalletBackupDoublespendTest(t: GlobalTestState) {
const { commonDb, merchant, walletClient, bank, exchange } =
await createSimpleTestkudosEnvironmentV2(t);
+ const merchantClient = new MerchantApiClient(merchant.makeInstanceBaseUrl());
+
const sync = await SyncService.create(t, {
currency: "TESTKUDOS",
annualFee: "TESTKUDOS:0.5",
@@ -58,7 +56,12 @@ export async function runWalletBackupDoublespendTest(t: GlobalTestState) {
name: sync.baseUrl,
});
- await withdrawViaBankV2(t, { walletClient, bank, exchange, amount: "TESTKUDOS:10" });
+ await withdrawViaBankV2(t, {
+ walletClient,
+ bank,
+ exchange,
+ amount: "TESTKUDOS:10",
+ });
await walletClient.call(WalletApiOperation.TestingWaitTransactionsFinal, {});
@@ -71,13 +74,16 @@ export async function runWalletBackupDoublespendTest(t: GlobalTestState) {
{},
);
- const wallet2 = new WalletCli(t, "wallet2");
+ const { walletClient: walletClientTwo } = await createWalletDaemonWithClient(
+ t,
+ { name: "default" },
+ );
- await wallet2.client.call(WalletApiOperation.ImportBackupRecovery, {
+ await walletClientTwo.call(WalletApiOperation.ImportBackupRecovery, {
recovery: backupRecovery,
});
- await wallet2.client.call(WalletApiOperation.RunBackupCycle, {});
+ await walletClientTwo.call(WalletApiOperation.RunBackupCycle, {});
console.log(
"wallet1 balance before spend:",
@@ -103,7 +109,7 @@ export async function runWalletBackupDoublespendTest(t: GlobalTestState) {
{
console.log(
"wallet2 balance:",
- await wallet2.client.call(WalletApiOperation.GetBalances, {}),
+ await walletClientTwo.call(WalletApiOperation.GetBalances, {}),
);
}
@@ -112,7 +118,7 @@ export async function runWalletBackupDoublespendTest(t: GlobalTestState) {
{
const instance = "default";
- const orderResp = await MerchantPrivateApi.createOrder(merchant, instance, {
+ const orderResp = await merchantClient.createOrder({
order: {
amount: "TESTKUDOS:8",
summary: "bla",
@@ -120,12 +126,9 @@ export async function runWalletBackupDoublespendTest(t: GlobalTestState) {
},
});
- let orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus(
- merchant,
- {
- orderId: orderResp.order_id,
- },
- );
+ let orderStatus = await merchantClient.queryPrivateOrderStatus({
+ orderId: orderResp.order_id,
+ });
t.assertTrue(orderStatus.order_status === "unpaid");
@@ -134,11 +137,11 @@ export async function runWalletBackupDoublespendTest(t: GlobalTestState) {
{
console.log(
"wallet2 balance before preparePay:",
- await wallet2.client.call(WalletApiOperation.GetBalances, {}),
+ await walletClientTwo.call(WalletApiOperation.GetBalances, {}),
);
}
- const preparePayResult = await wallet2.client.call(
+ const preparePayResult = await walletClientTwo.call(
WalletApiOperation.PreparePayForUri,
{
talerPayUri: orderStatus.taler_pay_uri,
@@ -150,25 +153,28 @@ export async function runWalletBackupDoublespendTest(t: GlobalTestState) {
PreparePayResultType.PaymentPossible,
);
- const res = await wallet2.client.call(WalletApiOperation.ConfirmPay, {
- proposalId: preparePayResult.proposalId,
+ const res = await walletClientTwo.call(WalletApiOperation.ConfirmPay, {
+ transactionId: preparePayResult.transactionId,
});
console.log(res);
// FIXME: wait for a notification that indicates insufficient funds!
- await withdrawViaBank(t, {
- wallet: wallet2,
+ await withdrawViaBankV2(t, {
+ walletClient: walletClientTwo,
bank,
exchange,
amount: "TESTKUDOS:50",
});
- const bal = await wallet2.client.call(WalletApiOperation.GetBalances, {});
+ const bal = await walletClientTwo.call(WalletApiOperation.GetBalances, {});
console.log("bal", bal);
- await wallet2.runUntilDone();
+ await walletClientTwo.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 15b0fd427..fc2774adf 100644
--- a/packages/taler-harness/src/integrationtests/test-wallet-balance.ts
+++ b/packages/taler-harness/src/integrationtests/test-wallet-balance.ts
@@ -17,9 +17,15 @@
/**
* Imports.
*/
-import { Amounts, PreparePayResultType } from "@gnu-taler/taler-util";
+import {
+ Amounts,
+ Duration,
+ MerchantApiClient,
+ MerchantContractTerms,
+ PreparePayResultType,
+} from "@gnu-taler/taler-util";
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
-import { GlobalTestState, MerchantPrivateApi } from "../harness/harness.js";
+import { GlobalTestState } from "../harness/harness.js";
import {
createSimpleTestkudosEnvironmentV2,
withdrawViaBankV2,
@@ -37,6 +43,19 @@ export async function runWalletBalanceTest(t: GlobalTestState) {
const { merchant, walletClient, exchange, bank } =
await createSimpleTestkudosEnvironmentV2(t);
+ await merchant.addInstanceWithWireAccount({
+ id: "myinst",
+ name: "My Instance",
+ paytoUris: ["payto://void/foo"],
+ defaultWireTransferDelay: Duration.toTalerProtocolDuration(
+ Duration.fromSpec({ minutes: 1 }),
+ ),
+ });
+
+ const merchantClient = new MerchantApiClient(
+ merchant.makeInstanceBaseUrl("myinst"),
+ );
+
// Withdraw digital cash into the wallet.
const wres = await withdrawViaBankV2(t, {
@@ -48,17 +67,17 @@ export async function runWalletBalanceTest(t: GlobalTestState) {
await wres.withdrawalFinishedCond;
- const order = {
+ const order: Partial<MerchantContractTerms> = {
summary: "Buy me!",
amount: "TESTKUDOS:5",
fulfillment_url: "taler://fulfillment-success/thx",
};
- const orderResp = await MerchantPrivateApi.createOrder(merchant, "default", {
+ const orderResp = await merchantClient.createOrder({
order,
});
- let orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus(merchant, {
+ let orderStatus = await merchantClient.queryPrivateOrderStatus({
orderId: orderResp.order_id,
});
diff --git a/packages/taler-harness/src/integrationtests/test-wallet-notifications.ts b/packages/taler-harness/src/integrationtests/test-wallet-notifications.ts
index c70fd51d4..9a0eb77ae 100644
--- a/packages/taler-harness/src/integrationtests/test-wallet-notifications.ts
+++ b/packages/taler-harness/src/integrationtests/test-wallet-notifications.ts
@@ -18,14 +18,12 @@
* Imports.
*/
import {
+ BankAccessApiClient,
Duration,
NotificationType,
TransactionMajorState,
} from "@gnu-taler/taler-util";
-import {
- BankAccessApiClient,
- WalletApiOperation,
-} from "@gnu-taler/taler-wallet-core";
+import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
import { CoinConfig, defaultCoinConfig } from "../harness/denomStructures.js";
import {
ExchangeService,
@@ -92,7 +90,7 @@ export async function runWalletNotificationsTest(t: GlobalTestState) {
// Fakebank uses x-taler-bank, but merchant is configured to only accept sepa!
const label = "mymerchant";
- await merchant.addInstance({
+ await merchant.addInstanceWithWireAccount({
id: "default",
name: "Default Instance",
paytoUris: [
@@ -123,10 +121,9 @@ export async function runWalletNotificationsTest(t: GlobalTestState) {
skipDefaults: true,
});
- const bankAccessApiClient = new BankAccessApiClient({
- allowHttp: true,
- baseUrl: bank.bankAccessApiBaseUrl,
- });
+ const bankAccessApiClient = new BankAccessApiClient(
+ bank.bankAccessApiBaseUrl,
+ );
const user = await bankAccessApiClient.createRandomBankUser();
bankAccessApiClient.setAuth(user);
const wop = await bankAccessApiClient.createWithdrawalOperation(
diff --git a/packages/taler-harness/src/integrationtests/test-wallettesting.ts b/packages/taler-harness/src/integrationtests/test-wallettesting.ts
index a856df79f..4fa870f1c 100644
--- a/packages/taler-harness/src/integrationtests/test-wallettesting.ts
+++ b/packages/taler-harness/src/integrationtests/test-wallettesting.ts
@@ -91,7 +91,7 @@ export async function createMyEnvironment(
await merchant.start();
await merchant.pingUntilAvailable();
- await merchant.addInstance({
+ await merchant.addInstanceWithWireAccount({
id: "default",
name: "Default Instance",
paytoUris: [getPayto("merchant-default")],
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 c3069c317..aa5e2b770 100644
--- a/packages/taler-harness/src/integrationtests/test-withdrawal-abort-bank.ts
+++ b/packages/taler-harness/src/integrationtests/test-withdrawal-abort-bank.ts
@@ -17,13 +17,10 @@
/**
* Imports.
*/
-import { TalerErrorCode } from "@gnu-taler/taler-util";
-import {
- WalletApiOperation,
- BankAccessApiClient,
-} from "@gnu-taler/taler-wallet-core";
+import { BankAccessApiClient, TalerErrorCode } from "@gnu-taler/taler-util";
+import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
import { GlobalTestState } from "../harness/harness.js";
-import { createSimpleTestkudosEnvironment } from "../harness/helpers.js";
+import { createSimpleTestkudosEnvironmentV2 } from "../harness/helpers.js";
/**
* Run test for basic, bank-integrated withdrawal.
@@ -31,14 +28,14 @@ import { createSimpleTestkudosEnvironment } from "../harness/helpers.js";
export async function runWithdrawalAbortBankTest(t: GlobalTestState) {
// Set up test environment
- const { wallet, bank, exchange } = await createSimpleTestkudosEnvironment(t);
+ const { walletClient, bank, exchange } =
+ await createSimpleTestkudosEnvironmentV2(t);
// Create a withdrawal operation
- const bankAccessApiClient = new BankAccessApiClient({
- allowHttp: true,
- baseUrl: bank.bankAccessApiBaseUrl,
- });
+ const bankAccessApiClient = new BankAccessApiClient(
+ bank.bankAccessApiBaseUrl,
+ );
const user = await bankAccessApiClient.createRandomBankUser();
bankAccessApiClient.setAuth(user);
const wop = await bankAccessApiClient.createWithdrawalOperation(
@@ -48,11 +45,11 @@ export async function runWithdrawalAbortBankTest(t: GlobalTestState) {
// Hand it to the wallet
- await wallet.client.call(WalletApiOperation.GetWithdrawalDetailsForUri, {
+ await walletClient.call(WalletApiOperation.GetWithdrawalDetailsForUri, {
talerWithdrawUri: wop.taler_withdraw_uri,
});
- await wallet.runPending();
+ await walletClient.call(WalletApiOperation.TestingWaitTransactionsFinal, {});
// Abort it
@@ -67,13 +64,10 @@ export async function runWithdrawalAbortBankTest(t: GlobalTestState) {
// WHY ?!
//
const e = await t.assertThrowsTalerErrorAsync(async () => {
- await wallet.client.call(
- WalletApiOperation.AcceptBankIntegratedWithdrawal,
- {
- exchangeBaseUrl: exchange.baseUrl,
- talerWithdrawUri: wop.taler_withdraw_uri,
- },
- );
+ await walletClient.call(WalletApiOperation.AcceptBankIntegratedWithdrawal, {
+ exchangeBaseUrl: exchange.baseUrl,
+ talerWithdrawUri: wop.taler_withdraw_uri,
+ });
});
t.assertDeepEqual(
e.errorDetail.code,
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 61687ec02..232b6d7c2 100644
--- a/packages/taler-harness/src/integrationtests/test-withdrawal-bank-integrated.ts
+++ b/packages/taler-harness/src/integrationtests/test-withdrawal-bank-integrated.ts
@@ -17,10 +17,8 @@
/**
* Imports.
*/
-import { GlobalTestState } from "../harness/harness.js";
-import { createSimpleTestkudosEnvironmentV2 } from "../harness/helpers.js";
-import { BankAccessApiClient, WalletApiOperation } from "@gnu-taler/taler-wallet-core";
import {
+ BankAccessApiClient,
j2s,
NotificationType,
TransactionMajorState,
@@ -28,6 +26,9 @@ import {
TransactionType,
WithdrawalType,
} from "@gnu-taler/taler-util";
+import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
+import { GlobalTestState } from "../harness/harness.js";
+import { createSimpleTestkudosEnvironmentV2 } from "../harness/helpers.js";
/**
* Run test for basic, bank-integrated withdrawal.
@@ -40,10 +41,9 @@ export async function runWithdrawalBankIntegratedTest(t: GlobalTestState) {
// Create a withdrawal operation
- const bankAccessApiClient = new BankAccessApiClient({
- allowHttp: true,
- baseUrl: bank.bankAccessApiBaseUrl,
- });
+ const bankAccessApiClient = new BankAccessApiClient(
+ bank.bankAccessApiBaseUrl,
+ );
const user = await bankAccessApiClient.createRandomBankUser();
bankAccessApiClient.setAuth(user);
const wop = await bankAccessApiClient.createWithdrawalOperation(
diff --git a/packages/taler-harness/src/integrationtests/test-withdrawal-fees.ts b/packages/taler-harness/src/integrationtests/test-withdrawal-fees.ts
index 06355b964..bc2946a18 100644
--- a/packages/taler-harness/src/integrationtests/test-withdrawal-fees.ts
+++ b/packages/taler-harness/src/integrationtests/test-withdrawal-fees.ts
@@ -17,22 +17,16 @@
/**
* Imports.
*/
+import { BankAccessApiClient, j2s } from "@gnu-taler/taler-util";
+import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
+import { CoinConfig } from "../harness/denomStructures.js";
import {
+ BankService,
+ ExchangeService,
GlobalTestState,
WalletCli,
setupDb,
- ExchangeService,
- BankService,
} from "../harness/harness.js";
-import {
- BankAccessApi,
- BankAccessApiClient,
- BankApi,
- WalletApiOperation,
-} from "@gnu-taler/taler-wallet-core";
-import { CoinConfig } from "../harness/denomStructures.js";
-import { j2s, URL } from "@gnu-taler/taler-util";
-import { withdrawViaBank } from "../harness/helpers.js";
const coinRsaCommon = {
cipher: "RSA" as const,
@@ -113,13 +107,15 @@ export async function runWithdrawalFeesTest(t: GlobalTestState) {
const amount = "TESTKUDOS:7.5";
- const bankAccessApiClient = new BankAccessApiClient({
- allowHttp: true,
- baseUrl: bank.bankAccessApiBaseUrl,
- });
+ const bankAccessApiClient = new BankAccessApiClient(
+ bank.bankAccessApiBaseUrl,
+ );
const user = await bankAccessApiClient.createRandomBankUser();
bankAccessApiClient.setAuth(user);
- const wop = await bankAccessApiClient.createWithdrawalOperation(user.username, amount);
+ const wop = await bankAccessApiClient.createWithdrawalOperation(
+ user.username,
+ amount,
+ );
// Hand it to the wallet
diff --git a/packages/taler-harness/src/integrationtests/test-withdrawal-huge.ts b/packages/taler-harness/src/integrationtests/test-withdrawal-huge.ts
index a9d544ee0..8777b19e2 100644
--- a/packages/taler-harness/src/integrationtests/test-withdrawal-huge.ts
+++ b/packages/taler-harness/src/integrationtests/test-withdrawal-huge.ts
@@ -114,4 +114,5 @@ export async function runWithdrawalHugeTest(t: GlobalTestState) {
}
runWithdrawalHugeTest.suites = ["wallet-perf"];
-runWithdrawalHugeTest.excludeByDefault = true;
+// FIXME: Should not be "experimental" but "slow" or something similar.
+runWithdrawalHugeTest.experimental = true;
diff --git a/packages/taler-harness/src/integrationtests/test-withdrawal-manual.ts b/packages/taler-harness/src/integrationtests/test-withdrawal-manual.ts
index d49235f89..1d98cd46e 100644
--- a/packages/taler-harness/src/integrationtests/test-withdrawal-manual.ts
+++ b/packages/taler-harness/src/integrationtests/test-withdrawal-manual.ts
@@ -17,12 +17,14 @@
/**
* Imports.
*/
-import { AbsoluteTime, Logger, j2s } from "@gnu-taler/taler-util";
import {
+ AbsoluteTime,
BankAccessApiClient,
- WalletApiOperation,
+ Logger,
WireGatewayApiClient,
-} from "@gnu-taler/taler-wallet-core";
+ j2s,
+} from "@gnu-taler/taler-util";
+import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
import { GlobalTestState } from "../harness/harness.js";
import { createSimpleTestkudosEnvironmentV2 } from "../harness/helpers.js";
@@ -39,10 +41,9 @@ export async function runWithdrawalManualTest(t: GlobalTestState) {
// Create a withdrawal operation
- const bankAccessApiClient = new BankAccessApiClient({
- baseUrl: bank.bankAccessApiBaseUrl,
- allowHttp: true,
- });
+ const bankAccessApiClient = new BankAccessApiClient(
+ bank.bankAccessApiBaseUrl,
+ );
const user = await bankAccessApiClient.createRandomBankUser();
@@ -74,12 +75,15 @@ export async function runWithdrawalManualTest(t: GlobalTestState) {
const reservePub: string = wres.reservePub;
- const wireGatewayApiClient = new WireGatewayApiClient({
- wireGatewayApiBaseUrl: exchangeBankAccount.wireGatewayApiBaseUrl,
- accountName: exchangeBankAccount.accountName,
- accountPassword: exchangeBankAccount.accountPassword,
- allowHttp: true,
- });
+ const wireGatewayApiClient = new WireGatewayApiClient(
+ exchangeBankAccount.wireGatewayApiBaseUrl,
+ {
+ auth: {
+ username: exchangeBankAccount.accountName,
+ password: exchangeBankAccount.accountPassword,
+ },
+ },
+ );
await wireGatewayApiClient.adminAddIncoming({
amount: "TESTKUDOS:10",
diff --git a/packages/taler-harness/src/integrationtests/testrunner.ts b/packages/taler-harness/src/integrationtests/testrunner.ts
index 501af98a4..66bd87a59 100644
--- a/packages/taler-harness/src/integrationtests/testrunner.ts
+++ b/packages/taler-harness/src/integrationtests/testrunner.ts
@@ -14,39 +14,39 @@
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
-import {
- CancellationToken,
- Logger,
- minimatch,
- setGlobalLogLevelFromString,
-} from "@gnu-taler/taler-util";
+import { CancellationToken, Logger, minimatch } from "@gnu-taler/taler-util";
import * as child_process from "child_process";
+import { spawnSync } from "child_process";
import * as fs from "fs";
import * as os from "os";
import * as path from "path";
import url from "url";
import {
GlobalTestState,
- runCommand,
+ TestRunResult,
runTestWithState,
shouldLingerInTest,
- TestRunResult,
} from "../harness/harness.js";
-import { spawnSync } from "child_process";
+import { getSharedTestDir } from "../harness/helpers.js";
import { runAgeRestrictionsMerchantTest } from "./test-age-restrictions-merchant.js";
+import { runAgeRestrictionsMixedMerchantTest } from "./test-age-restrictions-mixed-merchant.js";
+import { runAgeRestrictionsPeerTest } from "./test-age-restrictions-peer.js";
import { runBankApiTest } from "./test-bank-api.js";
import { runClaimLoopTest } from "./test-claim-loop.js";
import { runClauseSchnorrTest } from "./test-clause-schnorr.js";
import { runDenomUnofferedTest } from "./test-denom-unoffered.js";
import { runDepositTest } from "./test-deposit.js";
+import { runExchangeDepositTest } from "./test-exchange-deposit.js";
import { runExchangeManagementTest } from "./test-exchange-management.js";
+import { runExchangePurseTest } from "./test-exchange-purse.js";
import { runExchangeTimetravelTest } from "./test-exchange-timetravel.js";
import { runFeeRegressionTest } from "./test-fee-regression.js";
import { runForcedSelectionTest } from "./test-forced-selection.js";
+import { runKycTest } from "./test-kyc.js";
import { runLibeufinApiBankaccountTest } from "./test-libeufin-api-bankaccount.js";
import { runLibeufinApiBankconnectionTest } from "./test-libeufin-api-bankconnection.js";
-import { runLibeufinApiFacadeTest } from "./test-libeufin-api-facade.js";
import { runLibeufinApiFacadeBadRequestTest } from "./test-libeufin-api-facade-bad-request.js";
+import { runLibeufinApiFacadeTest } from "./test-libeufin-api-facade.js";
import { runLibeufinApiPermissionsTest } from "./test-libeufin-api-permissions.js";
import { runLibeufinApiSandboxCamtTest } from "./test-libeufin-api-sandbox-camt.js";
import { runLibeufinApiSandboxTransactionsTest } from "./test-libeufin-api-sandbox-transactions.js";
@@ -58,62 +58,58 @@ import { runLibeufinC5xTest } from "./test-libeufin-c5x.js";
import { runLibeufinAnastasisFacadeTest } from "./test-libeufin-facade-anastasis.js";
import { runLibeufinKeyrotationTest } from "./test-libeufin-keyrotation.js";
import { runLibeufinNexusBalanceTest } from "./test-libeufin-nexus-balance.js";
-import { runLibeufinRefundTest } from "./test-libeufin-refund.js";
import { runLibeufinRefundMultipleUsersTest } from "./test-libeufin-refund-multiple-users.js";
+import { runLibeufinRefundTest } from "./test-libeufin-refund.js";
import { runLibeufinSandboxWireTransferCliTest } from "./test-libeufin-sandbox-wire-transfer-cli.js";
import { runLibeufinTutorialTest } from "./test-libeufin-tutorial.js";
import { runMerchantExchangeConfusionTest } from "./test-merchant-exchange-confusion.js";
-import { runMerchantInstancesTest } from "./test-merchant-instances.js";
import { runMerchantInstancesDeleteTest } from "./test-merchant-instances-delete.js";
import { runMerchantInstancesUrlsTest } from "./test-merchant-instances-urls.js";
+import { runMerchantInstancesTest } from "./test-merchant-instances.js";
import { runMerchantLongpollingTest } from "./test-merchant-longpolling.js";
import { runMerchantRefundApiTest } from "./test-merchant-refund-api.js";
import { runMerchantSpecPublicOrdersTest } from "./test-merchant-spec-public-orders.js";
import { runPayPaidTest } from "./test-pay-paid.js";
-import { runPaymentTest } from "./test-payment.js";
+import { runPaymentAbortTest } from "./test-payment-abort.js";
import { runPaymentClaimTest } from "./test-payment-claim.js";
+import { runPaymentExpiredTest } from "./test-payment-expired.js";
import { runPaymentFaultTest } from "./test-payment-fault.js";
import { runPaymentForgettableTest } from "./test-payment-forgettable.js";
import { runPaymentIdempotencyTest } from "./test-payment-idempotency.js";
import { runPaymentMultipleTest } from "./test-payment-multiple.js";
+import { runPaymentShareTest } from "./test-payment-share.js";
+import { runPaymentTemplateTest } from "./test-payment-template.js";
import { runPaymentTransientTest } from "./test-payment-transient.js";
import { runPaymentZeroTest } from "./test-payment-zero.js";
+import { runPaymentTest } from "./test-payment.js";
import { runPaywallFlowTest } from "./test-paywall-flow.js";
+import { runPeerRepairTest } from "./test-peer-repair.js";
import { runPeerToPeerPullTest } from "./test-peer-to-peer-pull.js";
import { runPeerToPeerPushTest } from "./test-peer-to-peer-push.js";
-import { runRefundTest } from "./test-refund.js";
import { runRefundAutoTest } from "./test-refund-auto.js";
import { runRefundGoneTest } from "./test-refund-gone.js";
import { runRefundIncrementalTest } from "./test-refund-incremental.js";
+import { runRefundTest } from "./test-refund.js";
import { runRevocationTest } from "./test-revocation.js";
+import { runSimplePaymentTest } from "./test-simple-payment.js";
+import { runStoredBackupsTest } from "./test-stored-backups.js";
import { runTimetravelAutorefreshTest } from "./test-timetravel-autorefresh.js";
import { runTimetravelWithdrawTest } from "./test-timetravel-withdraw.js";
import { runTippingTest } from "./test-tipping.js";
+import { runTermOfServiceFormatTest } from "./test-tos-format.js";
import { runWalletBackupBasicTest } from "./test-wallet-backup-basic.js";
import { runWalletBackupDoublespendTest } from "./test-wallet-backup-doublespend.js";
+import { runWalletBalanceTest } from "./test-wallet-balance.js";
+import { runWalletCryptoWorkerTest } from "./test-wallet-cryptoworker.js";
import { runWalletDblessTest } from "./test-wallet-dbless.js";
+import { runWalletNotificationsTest } from "./test-wallet-notifications.js";
import { runWallettestingTest } from "./test-wallettesting.js";
import { runWithdrawalAbortBankTest } from "./test-withdrawal-abort-bank.js";
import { runWithdrawalBankIntegratedTest } from "./test-withdrawal-bank-integrated.js";
import { runWithdrawalFakebankTest } from "./test-withdrawal-fakebank.js";
-import { runWithdrawalManualTest } from "./test-withdrawal-manual.js";
-import { runAgeRestrictionsPeerTest } from "./test-age-restrictions-peer.js";
-import { runWalletNotificationsTest } from "./test-wallet-notifications.js";
-import { runAgeRestrictionsMixedMerchantTest } from "./test-age-restrictions-mixed-merchant.js";
-import { runWalletCryptoWorkerTest } from "./test-wallet-cryptoworker.js";
-import { runWithdrawalHugeTest } from "./test-withdrawal-huge.js";
-import { runKycTest } from "./test-kyc.js";
-import { runPaymentAbortTest } from "./test-payment-abort.js";
import { runWithdrawalFeesTest } from "./test-withdrawal-fees.js";
-import { runWalletBalanceTest } from "./test-wallet-balance.js";
-import { runPaymentTemplateTest } from "./test-payment-template.js";
-import { runExchangeDepositTest } from "./test-exchange-deposit.js";
-import { runPeerRepairTest } from "./test-peer-repair.js";
-import { runPaymentShareTest } from "./test-payment-share.js";
-import { runSimplePaymentTest } from "./test-simple-payment.js";
-import { runTermOfServiceFormatTest } from "./test-tos-format.js";
-import { runExchangePurseTest } from "./test-exchange-purse.js";
-import { getSharedTestDir } from "../harness/helpers.js";
+import { runWithdrawalHugeTest } from "./test-withdrawal-huge.js";
+import { runWithdrawalManualTest } from "./test-withdrawal-manual.js";
/**
* Test runner.
@@ -126,7 +122,6 @@ const logger = new Logger("testrunner.ts");
interface TestMainFunction {
(t: GlobalTestState): Promise<void>;
timeoutMs?: number;
- excludeByDefault?: boolean;
experimental?: boolean;
suites?: string[];
}
@@ -212,6 +207,8 @@ const allTests: TestMainFunction[] = [
runWithdrawalFeesTest,
runWithdrawalHugeTest,
runTermOfServiceFormatTest,
+ runStoredBackupsTest,
+ runPaymentExpiredTest,
];
export interface TestRunSpec {
@@ -228,7 +225,6 @@ export interface TestRunSpec {
export interface TestInfo {
name: string;
suites: string[];
- excludeByDefault: boolean;
experimental: boolean;
}
@@ -333,19 +329,16 @@ export async function runTests(spec: TestRunSpec) {
continue;
}
+ if (testCase.experimental && !spec.includeExperimental) {
+ continue;
+ }
+
if (suites) {
const ts = new Set(testCase.suites ?? []);
const intersection = new Set([...suites].filter((x) => ts.has(x)));
if (intersection.size === 0) {
continue;
}
- } else {
- if (testCase.excludeByDefault) {
- continue;
- }
- if (testCase.experimental && !spec.includeExperimental) {
- continue;
- }
}
if (spec.dryRun) {
@@ -521,7 +514,6 @@ export function getTestInfo(): TestInfo[] {
return allTests.map((x) => ({
name: getTestName(x),
suites: x.suites ?? [],
- excludeByDefault: x.excludeByDefault ?? false,
experimental: x.experimental ?? false,
}));
}
diff --git a/packages/taler-harness/src/lint.ts b/packages/taler-harness/src/lint.ts
index 6d8e679db..a45e6db9d 100644
--- a/packages/taler-harness/src/lint.ts
+++ b/packages/taler-harness/src/lint.ts
@@ -55,7 +55,6 @@ interface PubkeyConf {
const httpLib = createPlatformHttpLib({
enableThrottling: false,
- allowHttp: true,
});
interface ShellResult {
diff --git a/packages/taler-harness/tsconfig.json b/packages/taler-harness/tsconfig.json
index d022b16e8..ece83a85f 100644
--- a/packages/taler-harness/tsconfig.json
+++ b/packages/taler-harness/tsconfig.json
@@ -3,10 +3,10 @@
"compilerOptions": {
"composite": true,
"target": "ES2018",
- "module": "ESNext",
+ "module": "Node16",
"moduleResolution": "Node16",
"sourceMap": true,
- "lib": ["es6"],
+ "lib": ["ES2020"],
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"strict": true,
diff --git a/packages/taler-util/package.json b/packages/taler-util/package.json
index 6ac9a2689..0bb98767d 100644
--- a/packages/taler-util/package.json
+++ b/packages/taler-util/package.json
@@ -68,14 +68,14 @@
"esbuild": "^0.17.7",
"prettier": "^2.8.8",
"rimraf": "^3.0.2",
- "typescript": "^5.1.3"
+ "typescript": "^5.2.2"
},
"dependencies": {
"big-integer": "^1.6.51",
"fflate": "^0.7.4",
+ "hash-wasm": "^4.9.0",
"jed": "^1.1.1",
- "tslib": "^2.5.3",
- "hash-wasm": "^4.9.0"
+ "tslib": "^2.5.3"
},
"ava": {
"files": [
diff --git a/packages/taler-util/src/MerchantApiClient.ts b/packages/taler-util/src/MerchantApiClient.ts
new file mode 100644
index 000000000..cbdcb9fdf
--- /dev/null
+++ b/packages/taler-util/src/MerchantApiClient.ts
@@ -0,0 +1,331 @@
+/*
+ This file is part of GNU Taler
+ (C) 2023 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/>
+ */
+
+import {
+ createPlatformHttpLib,
+ expectSuccessResponseOrThrow,
+ readSuccessResponseJsonOrThrow,
+} from "./http.js";
+import { FacadeCredentials } from "./libeufin-api-types.js";
+import { Logger } from "./logging.js";
+import {
+ MerchantReserveCreateConfirmation,
+ codecForMerchantReserveCreateConfirmation,
+ TippingReserveStatus,
+ MerchantInstancesResponse,
+ MerchantPostOrderRequest,
+ MerchantPostOrderResponse,
+ codecForMerchantPostOrderResponse,
+ MerchantOrderPrivateStatusResponse,
+ codecForMerchantOrderPrivateStatusResponse,
+ RewardCreateRequest,
+ RewardCreateConfirmation,
+ MerchantTemplateAddDetails,
+} from "./merchant-api-types.js";
+import { AmountString } from "./taler-types.js";
+import { TalerProtocolDuration } from "./time.js";
+
+const logger = new Logger("MerchantApiClient.ts");
+
+export interface MerchantAuthConfiguration {
+ method: "external" | "token";
+ token?: string;
+}
+
+// FIXME: Why do we need this? Describe / fix!
+export interface PartialMerchantInstanceConfig {
+ auth?: MerchantAuthConfiguration;
+ id: string;
+ name: string;
+ paytoUris: string[];
+ address?: unknown;
+ jurisdiction?: unknown;
+ defaultWireTransferDelay?: TalerProtocolDuration;
+ defaultPayDelay?: TalerProtocolDuration;
+}
+
+export interface CreateMerchantTippingReserveRequest {
+ // Amount that the merchant promises to put into the reserve
+ initial_balance: AmountString;
+
+ // Exchange the merchant intends to use for tipping
+ exchange_url: string;
+
+ // Desired wire method, for example "iban" or "x-taler-bank"
+ wire_method: string;
+}
+
+export interface DeleteTippingReserveArgs {
+ reservePub: string;
+ purge?: boolean;
+}
+
+export interface MerchantInstanceConfig {
+ accounts: MerchantBankAccount[];
+ auth: MerchantAuthConfiguration;
+ id: string;
+ name: string;
+ address: unknown;
+ jurisdiction: unknown;
+ use_stefan: boolean;
+ default_wire_transfer_delay: TalerProtocolDuration;
+ default_pay_delay: TalerProtocolDuration;
+}
+
+interface MerchantBankAccount {
+ // The payto:// URI where the wallet will send coins.
+ payto_uri: string;
+
+ // Optional base URL for a facade where the
+ // merchant backend can see incoming wire
+ // transfers to reconcile its accounting
+ // with that of the exchange. Used by
+ // taler-merchant-wirewatch.
+ credit_facade_url?: string;
+
+ // Credentials for accessing the credit facade.
+ credit_facade_credentials?: FacadeCredentials;
+}
+
+export interface MerchantInstanceConfig {
+ accounts: MerchantBankAccount[];
+ auth: MerchantAuthConfiguration;
+ id: string;
+ name: string;
+ address: unknown;
+ jurisdiction: unknown;
+ use_stefan: boolean;
+ default_wire_transfer_delay: TalerProtocolDuration;
+ default_pay_delay: TalerProtocolDuration;
+}
+
+export interface PrivateOrderStatusQuery {
+ instance?: string;
+ orderId: string;
+ sessionId?: string;
+}
+
+/**
+ * Client for the GNU Taler merchant backend.
+ */
+export class MerchantApiClient {
+ /**
+ * Base URL for the particular instance that this merchant API client
+ * is for.
+ */
+ private baseUrl: string;
+
+ readonly auth: MerchantAuthConfiguration;
+
+ constructor(baseUrl: string, auth?: MerchantAuthConfiguration) {
+ this.baseUrl = baseUrl;
+
+ this.auth = auth ?? {
+ method: "external",
+ };
+ }
+
+ httpClient = createPlatformHttpLib();
+
+ async changeAuth(auth: MerchantAuthConfiguration): Promise<void> {
+ const url = new URL("private/auth", this.baseUrl);
+ const res = await this.httpClient.fetch(url.href, {
+ method: "POST",
+ body: auth,
+ headers: this.makeAuthHeader(),
+ });
+ await expectSuccessResponseOrThrow(res);
+ }
+
+ async deleteTippingReserve(req: DeleteTippingReserveArgs): Promise<void> {
+ const url = new URL(`private/reserves/${req.reservePub}`, this.baseUrl);
+ if (req.purge) {
+ url.searchParams.set("purge", "YES");
+ }
+ const resp = await this.httpClient.fetch(url.href, {
+ method: "DELETE",
+ headers: this.makeAuthHeader(),
+ });
+ logger.info(`delete status: ${resp.status}`);
+ return;
+ }
+
+ async createTippingReserve(
+ req: CreateMerchantTippingReserveRequest,
+ ): Promise<MerchantReserveCreateConfirmation> {
+ const url = new URL("private/reserves", this.baseUrl);
+ const resp = await this.httpClient.fetch(url.href, {
+ method: "POST",
+ body: req,
+ headers: this.makeAuthHeader(),
+ });
+ const respData = readSuccessResponseJsonOrThrow(
+ resp,
+ codecForMerchantReserveCreateConfirmation(),
+ );
+ return respData;
+ }
+
+ async getPrivateInstanceInfo(): Promise<any> {
+ const url = new URL("private", this.baseUrl);
+ const resp = await this.httpClient.fetch(url.href, {
+ method: "GET",
+ headers: this.makeAuthHeader(),
+ });
+ return await resp.json();
+ }
+
+ async getPrivateTipReserves(): Promise<TippingReserveStatus> {
+ const url = new URL("private/reserves", this.baseUrl);
+ const resp = await this.httpClient.fetch(url.href, {
+ method: "GET",
+ headers: this.makeAuthHeader(),
+ });
+ // FIXME: Validate!
+ return await resp.json();
+ }
+
+ async deleteInstance(instanceId: string) {
+ const url = new URL(`management/instances/${instanceId}`, this.baseUrl);
+ const resp = await this.httpClient.fetch(url.href, {
+ method: "DELETE",
+ headers: this.makeAuthHeader(),
+ });
+ await expectSuccessResponseOrThrow(resp);
+ }
+
+ async createInstance(req: MerchantInstanceConfig): Promise<void> {
+ const url = new URL("management/instances", this.baseUrl);
+ await this.httpClient.fetch(url.href, {
+ method: "POST",
+ body: req,
+ headers: this.makeAuthHeader(),
+ });
+ }
+
+ async getInstances(): Promise<MerchantInstancesResponse> {
+ const url = new URL("management/instances", this.baseUrl);
+ const resp = await this.httpClient.fetch(url.href, {
+ headers: this.makeAuthHeader(),
+ });
+ return resp.json();
+ }
+
+ async getInstanceFullDetails(instanceId: string): Promise<any> {
+ const url = new URL(`management/instances/${instanceId}`, this.baseUrl);
+ try {
+ const resp = await this.httpClient.fetch(url.href, {
+ headers: this.makeAuthHeader(),
+ });
+ return resp.json();
+ } catch (e) {
+ throw e;
+ }
+ }
+
+ async createOrder(
+ req: MerchantPostOrderRequest,
+ ): Promise<MerchantPostOrderResponse> {
+ 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(),
+ );
+ }
+
+ async queryPrivateOrderStatus(
+ query: PrivateOrderStatusQuery,
+ ): Promise<MerchantOrderPrivateStatusResponse> {
+ const reqUrl = new URL(`private/orders/${query.orderId}`, this.baseUrl);
+ if (query.sessionId) {
+ reqUrl.searchParams.set("session_id", query.sessionId);
+ }
+ const resp = await this.httpClient.fetch(reqUrl.href, {
+ headers: this.makeAuthHeader(),
+ });
+ return readSuccessResponseJsonOrThrow(
+ resp,
+ codecForMerchantOrderPrivateStatusResponse(),
+ );
+ }
+
+ async giveTip(req: RewardCreateRequest): Promise<RewardCreateConfirmation> {
+ const reqUrl = new URL(`private/tips`, this.baseUrl);
+ const resp = await this.httpClient.fetch(reqUrl.href, {
+ method: "POST",
+ body: req,
+ });
+ // FIXME: validate
+ return resp.json();
+ }
+
+ async queryTippingReserves(): Promise<TippingReserveStatus> {
+ const reqUrl = new URL(`private/reserves`, this.baseUrl);
+ const resp = await this.httpClient.fetch(reqUrl.href, {
+ headers: this.makeAuthHeader(),
+ });
+ // FIXME: validate
+ return resp.json();
+ }
+
+ async giveRefund(r: {
+ instance: string;
+ orderId: string;
+ amount: string;
+ justification: string;
+ }): Promise<{ talerRefundUri: string }> {
+ const reqUrl = new URL(`private/orders/${r.orderId}/refund`, this.baseUrl);
+ const resp = await this.httpClient.fetch(reqUrl.href, {
+ method: "POST",
+ body: {
+ refund: r.amount,
+ reason: r.justification,
+ },
+ });
+ const respBody = await resp.json();
+ return {
+ talerRefundUri: respBody.taler_refund_uri,
+ };
+ }
+
+ async createTemplate(req: MerchantTemplateAddDetails) {
+ let url = new URL("private/templates", this.baseUrl);
+ const resp = await this.httpClient.fetch(url.href, {
+ method: "POST",
+ body: req,
+ headers: this.makeAuthHeader(),
+ });
+ if (resp.status !== 204) {
+ throw Error("failed to create template");
+ }
+ }
+
+ private makeAuthHeader(): Record<string, string> {
+ switch (this.auth.method) {
+ case "external":
+ return {};
+ case "token":
+ return {
+ Authorization: `Bearer ${this.auth.token}`,
+ };
+ }
+ }
+}
diff --git a/packages/taler-util/src/backup-types.ts b/packages/taler-util/src/backup-types.ts
index 2eba1e4ca..8c38b70a6 100644
--- a/packages/taler-util/src/backup-types.ts
+++ b/packages/taler-util/src/backup-types.ts
@@ -14,6 +14,8 @@
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
+import { AmountString } from "./taler-types.js";
+
export interface BackupRecovery {
walletRootPriv: string;
providers: {
@@ -21,3 +23,20 @@ export interface BackupRecovery {
url: string;
}[];
}
+
+export class BackupBackupProviderTerms {
+ /**
+ * Last known supported protocol version.
+ */
+ supported_protocol_version: string;
+
+ /**
+ * Last known annual fee.
+ */
+ annual_fee: AmountString;
+
+ /**
+ * Last known storage limit.
+ */
+ storage_limit_in_megabytes: number;
+}
diff --git a/packages/taler-wallet-core/src/bank-api-client.ts b/packages/taler-util/src/bank-api-client.ts
index 3174667f1..cc4123500 100644
--- a/packages/taler-wallet-core/src/bank-api-client.ts
+++ b/packages/taler-util/src/bank-api-client.ts
@@ -58,11 +58,6 @@ export interface BankAccountBalanceResponse {
};
}
-export interface BankServiceHandle {
- readonly bankAccessApiBaseUrl: string;
- readonly http: HttpRequestLibrary;
-}
-
export interface BankUser {
username: string;
password: string;
@@ -75,16 +70,6 @@ export interface WithdrawalOperationInfo {
}
/**
- * FIXME: Rename, this is not part of the integration test harness anymore.
- */
-export interface HarnessExchangeBankAccount {
- accountName: string;
- accountPassword: string;
- accountPaytoUri: string;
- wireGatewayApiBaseUrl: string;
-}
-
-/**
* Helper function to generate the "Authorization" HTTP header.
*/
function makeBasicAuthHeader(username: string, password: string): string {
@@ -99,177 +84,9 @@ const codecForWithdrawalOperationInfo = (): Codec<WithdrawalOperationInfo> =>
.property("taler_withdraw_uri", codecForString())
.build("WithdrawalOperationInfo");
-/**
- * @deprecated Use BankAccessApiClient or WireGatewayApi
- */
-export namespace BankApi {
- // FIXME: Move to BankAccessApi?!
- export async function registerAccount(
- bank: BankServiceHandle,
- username: string,
- password: string,
- options: {
- iban?: string;
- },
- ): Promise<BankUser> {
- const url = new URL("testing/register", bank.bankAccessApiBaseUrl);
- const resp = await bank.http.postJson(url.href, {
- username,
- password,
- iban: options?.iban,
- });
- let paytoUri = `payto://x-taler-bank/localhost/${username}`;
- if (resp.status !== 200 && resp.status !== 202 && resp.status !== 204) {
- logger.error(`${j2s(await resp.json())}`);
- throw TalerError.fromDetail(
- TalerErrorCode.GENERIC_UNEXPECTED_REQUEST_ERROR,
- {
- httpStatusCode: resp.status,
- },
- );
- }
- try {
- // Pybank has no body, thus this might throw.
- const respJson = await resp.json();
- // LibEuFin demobank returns payto URI in response
- if (respJson.paytoUri) {
- paytoUri = respJson.paytoUri;
- }
- } catch (e) {
- // Do nothing
- }
- return {
- password,
- username,
- accountPaytoUri: paytoUri,
- };
- }
-
- // FIXME: Move to BankAccessApi?!
- export async function createRandomBankUser(
- bank: BankServiceHandle,
- ): Promise<BankUser> {
- const username = "user-" + encodeCrock(getRandomBytes(10)).toLowerCase();
- const password = "pw-" + encodeCrock(getRandomBytes(10)).toLowerCase();
- // FIXME: This is just a temporary workaround, because demobank is running out of short IBANs
- const iban = generateIban("DE", 15);
- return await registerAccount(bank, username, password, {
- iban,
- });
- }
-
- export async function confirmWithdrawalOperation(
- bank: BankServiceHandle,
- bankUser: BankUser,
- wopi: WithdrawalOperationInfo,
- ): Promise<void> {
- const url = new URL(
- `accounts/${bankUser.username}/withdrawals/${wopi.withdrawal_id}/confirm`,
- bank.bankAccessApiBaseUrl,
- );
- logger.info(`confirming withdrawal operation via ${url.href}`);
- const resp = await bank.http.postJson(
- url.href,
- {},
- {
- headers: {
- Authorization: makeBasicAuthHeader(
- bankUser.username,
- bankUser.password,
- ),
- },
- },
- );
-
- logger.info(`response status ${resp.status}`);
- const respJson = await readSuccessResponseJsonOrThrow(resp, codecForAny());
-
- // FIXME: We don't check the status here!
- }
-
- export async function abortWithdrawalOperation(
- bank: BankServiceHandle,
- bankUser: BankUser,
- wopi: WithdrawalOperationInfo,
- ): Promise<void> {
- const url = new URL(
- `accounts/${bankUser.username}/withdrawals/${wopi.withdrawal_id}/abort`,
- bank.bankAccessApiBaseUrl,
- );
- const resp = await bank.http.postJson(
- url.href,
- {},
- {
- headers: {
- Authorization: makeBasicAuthHeader(
- bankUser.username,
- bankUser.password,
- ),
- },
- },
- );
- await readSuccessResponseJsonOrThrow(resp, codecForAny());
- }
-}
-
-/**
- * @deprecated use BankAccessApiClient
- */
-export namespace BankAccessApi {
- export async function getAccountBalance(
- bank: BankServiceHandle,
- bankUser: BankUser,
- ): Promise<BankAccountBalanceResponse> {
- const url = new URL(
- `accounts/${bankUser.username}`,
- bank.bankAccessApiBaseUrl,
- );
- const resp = await bank.http.fetch(url.href, {
- headers: {
- Authorization: makeBasicAuthHeader(
- bankUser.username,
- bankUser.password,
- ),
- },
- });
- return await resp.json();
- }
-
- export async function createWithdrawalOperation(
- bank: BankServiceHandle,
- bankUser: BankUser,
- amount: string,
- ): Promise<WithdrawalOperationInfo> {
- const url = new URL(
- `accounts/${bankUser.username}/withdrawals`,
- bank.bankAccessApiBaseUrl,
- );
- const resp = await bank.http.postJson(
- url.href,
- {
- amount,
- },
- {
- headers: {
- Authorization: makeBasicAuthHeader(
- bankUser.username,
- bankUser.password,
- ),
- },
- },
- );
- return readSuccessResponseJsonOrThrow(
- resp,
- codecForWithdrawalOperationInfo(),
- );
- }
-}
-
export interface BankAccessApiClientArgs {
- baseUrl: string;
auth?: { username: string; password: string };
- enableThrottling?: boolean;
- allowHttp?: boolean;
+ httpClient?: HttpRequestLibrary;
}
export interface BankAccessApiCreateTransactionRequest {
@@ -278,11 +95,11 @@ export interface BankAccessApiCreateTransactionRequest {
}
export class WireGatewayApiClientArgs {
- accountName: string;
- accountPassword: string;
- wireGatewayApiBaseUrl: string;
- enableThrottling?: boolean;
- allowHttp?: boolean;
+ auth?: {
+ username: string;
+ password: string;
+ };
+ httpClient?: HttpRequestLibrary;
}
/**
@@ -292,11 +109,21 @@ export class WireGatewayApiClientArgs {
export class WireGatewayApiClient {
httpLib;
- constructor(private args: WireGatewayApiClientArgs) {
- this.httpLib = createPlatformHttpLib({
- enableThrottling: !!args.enableThrottling,
- allowHttp: !!args.allowHttp,
- });
+ constructor(
+ private baseUrl: string,
+ private args: WireGatewayApiClientArgs = {},
+ ) {
+ this.httpLib = args.httpClient ?? createPlatformHttpLib();
+ }
+
+ private makeAuthHeader(): Record<string, string> {
+ const auth = this.args.auth;
+ if (auth) {
+ return {
+ Authorization: makeBasicAuthHeader(auth.username, auth.password),
+ };
+ }
+ return {};
}
async adminAddIncoming(params: {
@@ -304,7 +131,7 @@ export class WireGatewayApiClient {
reservePub: string;
debitAccountPayto: string;
}): Promise<void> {
- let url = new URL(`admin/add-incoming`, this.args.wireGatewayApiBaseUrl);
+ let url = new URL(`admin/add-incoming`, this.baseUrl);
const resp = await this.httpLib.fetch(url.href, {
method: "POST",
body: {
@@ -312,12 +139,7 @@ export class WireGatewayApiClient {
reserve_pub: params.reservePub,
debit_account: params.debitAccountPayto,
},
- headers: {
- Authorization: makeBasicAuthHeader(
- this.args.accountName,
- this.args.accountPassword,
- ),
- },
+ headers: this.makeAuthHeader(),
});
logger.info(`add-incoming response status: ${resp.status}`);
await checkSuccessResponseOrThrow(resp);
@@ -331,11 +153,11 @@ export class WireGatewayApiClient {
export class BankAccessApiClient {
httpLib: HttpRequestLibrary;
- constructor(private args: BankAccessApiClientArgs) {
- this.httpLib = createPlatformHttpLib({
- enableThrottling: !!args.enableThrottling,
- allowHttp: !!args.allowHttp,
- });
+ constructor(
+ private baseUrl: string,
+ private args: BankAccessApiClientArgs = {},
+ ) {
+ this.httpLib = args.httpClient ?? createPlatformHttpLib();
}
setAuth(auth: { username: string; password: string }) {
@@ -355,12 +177,18 @@ export class BankAccessApiClient {
};
}
+ async getAccountBalance(
+ username: string,
+ ): Promise<BankAccountBalanceResponse> {
+ const url = new URL(`accounts/${username}`, this.baseUrl);
+ const resp = await this.httpLib.fetch(url.href, {
+ headers: this.makeAuthHeader(),
+ });
+ return await resp.json();
+ }
+
async getTransactions(username: string): Promise<void> {
- const auth = this.args.auth;
- const reqUrl = new URL(
- `accounts/${username}/transactions`,
- this.args.baseUrl,
- );
+ const reqUrl = new URL(`accounts/${username}/transactions`, this.baseUrl);
const resp = await this.httpLib.fetch(reqUrl.href, {
method: "GET",
headers: {
@@ -376,10 +204,7 @@ export class BankAccessApiClient {
username: string,
req: BankAccessApiCreateTransactionRequest,
): Promise<any> {
- const reqUrl = new URL(
- `accounts/${username}/transactions`,
- this.args.baseUrl,
- );
+ const reqUrl = new URL(`accounts/${username}/transactions`, this.baseUrl);
const resp = await this.httpLib.fetch(reqUrl.href, {
method: "POST",
@@ -395,9 +220,9 @@ export class BankAccessApiClient {
password: string,
options: {
iban?: string;
- },
+ } = {},
): Promise<BankUser> {
- const url = new URL("testing/register", this.args.baseUrl);
+ const url = new URL("testing/register", this.baseUrl);
const resp = await this.httpLib.fetch(url.href, {
method: "POST",
body: {
@@ -447,7 +272,7 @@ export class BankAccessApiClient {
user: string,
amount: string,
): Promise<WithdrawalOperationInfo> {
- const url = new URL(`accounts/${user}/withdrawals`, this.args.baseUrl);
+ const url = new URL(`accounts/${user}/withdrawals`, this.baseUrl);
const resp = await this.httpLib.fetch(url.href, {
method: "POST",
body: {
@@ -467,7 +292,7 @@ export class BankAccessApiClient {
): Promise<void> {
const url = new URL(
`accounts/${username}/withdrawals/${wopi.withdrawal_id}/confirm`,
- this.args.baseUrl,
+ this.baseUrl,
);
logger.info(`confirming withdrawal operation via ${url.href}`);
const resp = await this.httpLib.fetch(url.href, {
@@ -488,7 +313,7 @@ export class BankAccessApiClient {
): Promise<void> {
const url = new URL(
`accounts/${accountName}/withdrawals/${wopi.withdrawal_id}/abort`,
- this.args.baseUrl,
+ this.baseUrl,
);
const resp = await this.httpLib.fetch(url.href, {
method: "POST",
diff --git a/packages/taler-util/src/bitcoin.ts b/packages/taler-util/src/bitcoin.ts
index 8c22ba522..37b7ae6b9 100644
--- a/packages/taler-util/src/bitcoin.ts
+++ b/packages/taler-util/src/bitcoin.ts
@@ -69,10 +69,10 @@ export function generateFakeSegwitAddress(
addr[0] === "t" && addr[1] == "b"
? "tb"
: addr[0] === "b" && addr[1] == "c" && addr[2] === "r" && addr[3] == "t"
- ? "bcrt"
- : addr[0] === "b" && addr[1] == "c"
- ? "bc"
- : undefined;
+ ? "bcrt"
+ : addr[0] === "b" && addr[1] == "c"
+ ? "bc"
+ : undefined;
if (prefix === undefined) throw new Error("unknown bitcoin net");
const addr1 = segwit.default.encode(prefix, 0, first_part);
diff --git a/packages/taler-util/src/http-common.ts b/packages/taler-util/src/http-common.ts
index 93cf9bba0..f25705545 100644
--- a/packages/taler-util/src/http-common.ts
+++ b/packages/taler-util/src/http-common.ts
@@ -16,7 +16,7 @@
SPDX-License-Identifier: AGPL3.0-or-later
*/
-import { CancellationToken } from "./CancellationToken.js";
+import type { CancellationToken } from "./CancellationToken.js";
import { Codec } from "./codec.js";
import { j2s } from "./helpers.js";
import {
@@ -436,7 +436,10 @@ export function getExpiry(
export interface HttpLibArgs {
enableThrottling?: boolean;
- allowHttp?: boolean;
+ /**
+ * Only allow HTTPS connections, not plain http.
+ */
+ requireTls?: boolean;
}
export function encodeBody(body: any): ArrayBuffer {
diff --git a/packages/taler-util/src/http-impl.node.ts b/packages/taler-util/src/http-impl.node.ts
index 07648a28d..528d303be 100644
--- a/packages/taler-util/src/http-impl.node.ts
+++ b/packages/taler-util/src/http-impl.node.ts
@@ -63,11 +63,11 @@ const textDecoder = new TextDecoder();
export class HttpLibImpl implements HttpRequestLibrary {
private throttle = new RequestThrottler();
private throttlingEnabled = true;
- private allowHttp = false;
+ private requireTls = false;
constructor(args?: HttpLibArgs) {
this.throttlingEnabled = args?.enableThrottling ?? false;
- this.allowHttp = args?.allowHttp ?? false;
+ this.requireTls = args?.requireTls ?? false;
}
/**
@@ -94,7 +94,7 @@ export class HttpLibImpl implements HttpRequestLibrary {
`request to origin ${parsedUrl.origin} was throttled`,
);
}
- if (!this.allowHttp && parsedUrl.protocol !== "https:") {
+ if (this.requireTls && parsedUrl.protocol !== "https:") {
throw TalerError.fromDetail(
TalerErrorCode.WALLET_NETWORK_ERROR,
{
diff --git a/packages/taler-util/src/http-impl.qtart.ts b/packages/taler-util/src/http-impl.qtart.ts
index 3e076e96d..fb642ac89 100644
--- a/packages/taler-util/src/http-impl.qtart.ts
+++ b/packages/taler-util/src/http-impl.qtart.ts
@@ -41,11 +41,11 @@ const textDecoder = new TextDecoder();
export class HttpLibImpl implements HttpRequestLibrary {
private throttle = new RequestThrottler();
private throttlingEnabled = true;
- private allowHttp = false;
+ private requireTls = false;
constructor(args?: HttpLibArgs) {
this.throttlingEnabled = args?.enableThrottling ?? false;
- this.allowHttp = args?.allowHttp ?? false;
+ this.requireTls = args?.requireTls ?? false;
}
/**
@@ -72,7 +72,7 @@ export class HttpLibImpl implements HttpRequestLibrary {
`request to origin ${parsedUrl.origin} was throttled`,
);
}
- if (!this.allowHttp && parsedUrl.protocol !== "https:") {
+ if (this.requireTls && parsedUrl.protocol !== "https:") {
throw TalerError.fromDetail(
TalerErrorCode.WALLET_NETWORK_ERROR,
{
diff --git a/packages/taler-util/src/index.ts b/packages/taler-util/src/index.ts
index cfd0f7c47..568e2f438 100644
--- a/packages/taler-util/src/index.ts
+++ b/packages/taler-util/src/index.ts
@@ -39,3 +39,6 @@ export * from "./merchant-api-types.js";
export * from "./errors.js";
export * from "./iban.js";
export * from "./transaction-test-data.js";
+export * from "./libeufin-api-types.js";
+export * from "./MerchantApiClient.js";
+export * from "./bank-api-client.js";
diff --git a/packages/taler-util/src/libeufin-api-types.ts b/packages/taler-util/src/libeufin-api-types.ts
new file mode 100644
index 000000000..aa3d0cb7a
--- /dev/null
+++ b/packages/taler-util/src/libeufin-api-types.ts
@@ -0,0 +1,31 @@
+/*
+ This file is part of GNU Taler
+ (C) 2023 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/>
+ */
+
+export type FacadeCredentials =
+ | NoFacadeCredentials
+ | BasicAuthFacadeCredentials;
+export interface NoFacadeCredentials {
+ type: "none";
+}
+export interface BasicAuthFacadeCredentials {
+ type: "basic";
+
+ // Username to use to authenticate
+ username: string;
+
+ // Password to use to authenticate
+ password: string;
+}
diff --git a/packages/taler-util/src/merchant-api-types.ts b/packages/taler-util/src/merchant-api-types.ts
index 9f00173f2..9933b93dc 100644
--- a/packages/taler-util/src/merchant-api-types.ts
+++ b/packages/taler-util/src/merchant-api-types.ts
@@ -47,6 +47,7 @@ import {
WireAccount,
codecForWireAccount,
codecForList,
+ FacadeCredentials,
} from "@gnu-taler/taler-util";
export interface MerchantPostOrderRequest {
@@ -384,3 +385,19 @@ export const codecForMerchantReserveCreateConfirmation =
.property("accounts", codecForList(codecForWireAccount()))
.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/payto.ts b/packages/taler-util/src/payto.ts
index 2b0af4cc2..60c4ba838 100644
--- a/packages/taler-util/src/payto.ts
+++ b/packages/taler-util/src/payto.ts
@@ -24,7 +24,7 @@ export type PaytoUri =
| PaytoUriBitcoin;
export interface PaytoUriGeneric {
- targetType: string;
+ targetType: PaytoType | string;
targetPath: string;
params: { [name: string]: string };
}
@@ -55,6 +55,8 @@ export interface PaytoUriBitcoin extends PaytoUriGeneric {
const paytoPfx = "payto://";
+export type PaytoType = "iban" | "bitcoin" | "x-taler-bank"
+
export function buildPayto(
type: "iban",
iban: string,
@@ -71,7 +73,7 @@ export function buildPayto(
account: string,
): PaytoUriTalerBank;
export function buildPayto(
- type: "iban" | "bitcoin" | "x-taler-bank",
+ type: PaytoType,
first: string,
second?: string,
): PaytoUriGeneric {
diff --git a/packages/taler-util/src/time.ts b/packages/taler-util/src/time.ts
index 55cda08a5..46ed37637 100644
--- a/packages/taler-util/src/time.ts
+++ b/packages/taler-util/src/time.ts
@@ -154,13 +154,27 @@ export interface TalerProtocolDuration {
readonly d_us: number | "forever";
}
+/**
+ * Timeshift in milliseconds.
+ */
let timeshift = 0;
+/**
+ * Set timetravel offset in milliseconds.
+ *
+ * Use carefully and only for testing.
+ */
export function setDangerousTimetravel(dt: number): void {
timeshift = dt;
}
export namespace Duration {
+ export function toMilliseconds(d: Duration): number {
+ if (d.d_ms === "forever") {
+ return Number.MAX_VALUE;
+ }
+ return d.d_ms;
+ }
export function getRemaining(
deadline: AbsoluteTime,
now = AbsoluteTime.now(),
diff --git a/packages/taler-util/src/transactions-types.ts b/packages/taler-util/src/transactions-types.ts
index 6331bc731..304183ceb 100644
--- a/packages/taler-util/src/transactions-types.ts
+++ b/packages/taler-util/src/transactions-types.ts
@@ -528,22 +528,6 @@ export interface OrderShortInfo {
summary_i18n?: InternationalizedString;
/**
- * List of products that are part of the order
- */
- products: Product[] | undefined;
-
- /**
- * Time indicating when the order should be delivered.
- * May be overwritten by individual products.
- */
- delivery_date?: TalerProtocolTimestamp;
-
- /**
- * Delivery location for (all!) products.
- */
- delivery_location?: Location;
-
- /**
* URL of the fulfillment, given by the merchant
*/
fulfillmentUrl?: string;
@@ -724,7 +708,6 @@ export const codecForOrderShortInfo = (): Codec<OrderShortInfo> =>
.property("fulfillmentUrl", codecOptional(codecForString()))
.property("merchant", codecForMerchantInfo())
.property("orderId", codecForString())
- .property("products", codecOptional(codecForList(codecForProduct())))
.property("summary", codecForString())
.property("summary_i18n", codecOptional(codecForInternationalizedString()))
.build("OrderShortInfo");
diff --git a/packages/taler-util/src/wallet-types.ts b/packages/taler-util/src/wallet-types.ts
index accab746f..c6f19c73f 100644
--- a/packages/taler-util/src/wallet-types.ts
+++ b/packages/taler-util/src/wallet-types.ts
@@ -1076,13 +1076,6 @@ export interface KnownBankAccounts {
accounts: KnownBankAccountsInfo[];
}
-export interface ExchangeTosStatusDetails {
- acceptedVersion?: string;
- currentVersion?: string;
- contentType?: string;
- content?: string;
-}
-
/**
* Wire fee for one wire method
*/
@@ -1253,7 +1246,6 @@ export interface ExchangeFullDetails {
exchangeBaseUrl: string;
currency: string;
paytoUris: string[];
- tos: ExchangeTosStatusDetails;
auditors: ExchangeAuditor[];
wireInfo: WireInfo;
denomFees: DenomOperationMap<FeeDescription[]>;
@@ -1317,14 +1309,6 @@ const codecForExchangeAuditor = (): Codec<ExchangeAuditor> =>
.property("denomination_keys", codecForList(codecForAuditorDenomSig()))
.build("codecForExchangeAuditor");
-const codecForExchangeTos = (): Codec<ExchangeTosStatusDetails> =>
- buildCodecForObject<ExchangeTosStatusDetails>()
- .property("acceptedVersion", codecOptional(codecForString()))
- .property("currentVersion", codecOptional(codecForString()))
- .property("contentType", codecOptional(codecForString()))
- .property("content", codecOptional(codecForString()))
- .build("ExchangeTos");
-
export const codecForFeeDescriptionPair = (): Codec<FeeDescriptionPair> =>
buildCodecForObject<FeeDescriptionPair>()
.property("group", codecForString())
@@ -1357,7 +1341,6 @@ export const codecForExchangeFullDetails = (): Codec<ExchangeFullDetails> =>
.property("currency", codecForString())
.property("exchangeBaseUrl", codecForString())
.property("paytoUris", codecForList(codecForString()))
- .property("tos", codecForExchangeTos())
.property("auditors", codecForList(codecForExchangeAuditor()))
.property("wireInfo", codecForWireInfo())
.property("denomFees", codecForFeesByOperations())
@@ -1465,22 +1448,11 @@ export interface ExchangeWithdrawalDetails {
selectedDenoms: DenomSelectionState;
/**
- * Does the wallet know about an auditor for
- * the exchange that the reserve.
- */
- isAudited: boolean;
-
- /**
* Did the user already accept the current terms of service for the exchange?
*/
termsOfServiceAccepted: boolean;
/**
- * The exchange is trusted directly.
- */
- isTrusted: boolean;
-
- /**
* The earliest deposit expiration of the selected coins.
*/
earliestDepositExpiration: TalerProtocolTimestamp;
@@ -2466,7 +2438,7 @@ export interface PreparePeerPushCreditResponse {
amount: AmountString;
amountRaw: AmountString;
amountEffective: AmountString;
- peerPushPaymentIncomingId: string;
+ peerPushCreditId: string;
transactionId: string;
}
@@ -2481,7 +2453,7 @@ export interface PreparePeerPullDebitResponse {
amountRaw: AmountString;
amountEffective: AmountString;
- peerPullPaymentIncomingId: string;
+ peerPullDebitId: string;
transactionId: string;
}
@@ -2504,7 +2476,7 @@ export interface ConfirmPeerPushCreditRequest {
*
* @deprecated specify transactionId instead!
*/
- peerPushPaymentIncomingId?: string;
+ peerPushCreditId?: string;
transactionId?: string;
}
@@ -2519,7 +2491,7 @@ export interface AcceptPeerPullPaymentResponse {
export const codecForConfirmPeerPushPaymentRequest =
(): Codec<ConfirmPeerPushCreditRequest> =>
buildCodecForObject<ConfirmPeerPushCreditRequest>()
- .property("peerPushPaymentIncomingId", codecOptional(codecForString()))
+ .property("peerPushCreditId", codecOptional(codecForString()))
.property("transactionId", codecOptional(codecForString()))
.build("ConfirmPeerPushCreditRequest");
@@ -2529,7 +2501,7 @@ export interface ConfirmPeerPullDebitRequest {
*
* @deprecated use transactionId instead
*/
- peerPullPaymentIncomingId?: string;
+ peerPullDebitId?: string;
transactionId?: string;
}
@@ -2547,7 +2519,7 @@ export const codecForApplyDevExperiment =
export const codecForAcceptPeerPullPaymentRequest =
(): Codec<ConfirmPeerPullDebitRequest> =>
buildCodecForObject<ConfirmPeerPullDebitRequest>()
- .property("peerPullPaymentIncomingId", codecOptional(codecForString()))
+ .property("peerPullDebitId", codecOptional(codecForString()))
.property("transactionId", codecOptional(codecForString()))
.build("ConfirmPeerPullDebitRequest");
@@ -2673,3 +2645,25 @@ export interface RecoverStoredBackupRequest {
export interface DeleteStoredBackupRequest {
name: string;
}
+
+export const codecForDeleteStoredBackupRequest =
+ (): Codec<DeleteStoredBackupRequest> =>
+ buildCodecForObject<DeleteStoredBackupRequest>()
+ .property("name", codecForString())
+ .build("DeleteStoredBackupRequest");
+
+export const codecForRecoverStoredBackupRequest =
+ (): Codec<RecoverStoredBackupRequest> =>
+ buildCodecForObject<RecoverStoredBackupRequest>()
+ .property("name", codecForString())
+ .build("RecoverStoredBackupRequest");
+
+export interface TestingSetTimetravelRequest {
+ offsetMs: number;
+}
+
+export const codecForTestingSetTimetravelRequest =
+ (): Codec<TestingSetTimetravelRequest> =>
+ buildCodecForObject<TestingSetTimetravelRequest>()
+ .property("offsetMs", codecForNumber())
+ .build("TestingSetTimetravelRequest");
diff --git a/packages/taler-util/tsconfig.json b/packages/taler-util/tsconfig.json
index 34f35d253..2e97142ce 100644
--- a/packages/taler-util/tsconfig.json
+++ b/packages/taler-util/tsconfig.json
@@ -5,8 +5,8 @@
"declaration": true,
"declarationMap": false,
"target": "ES2020",
- "module": "ES2020",
- "moduleResolution": "node16",
+ "module": "Node16",
+ "moduleResolution": "Node16",
"sourceMap": true,
"lib": ["ES2020"],
"types": ["node"],
diff --git a/packages/taler-wallet-cli/package.json b/packages/taler-wallet-cli/package.json
index 06df1da76..6196c8971 100644
--- a/packages/taler-wallet-cli/package.json
+++ b/packages/taler-wallet-cli/package.json
@@ -33,8 +33,8 @@
"@types/node": "^18.11.17",
"prettier": "^2.8.8",
"rimraf": "^3.0.2",
- "typedoc": "^0.24.8",
- "typescript": "^5.1.3"
+ "typedoc": "^0.25.1",
+ "typescript": "^5.2.2"
},
"dependencies": {
"@gnu-taler/taler-util": "workspace:*",
diff --git a/packages/taler-wallet-cli/src/index.ts b/packages/taler-wallet-cli/src/index.ts
index 36e7f7768..3fc86d0b5 100644
--- a/packages/taler-wallet-cli/src/index.ts
+++ b/packages/taler-wallet-cli/src/index.ts
@@ -244,7 +244,7 @@ async function createLocalWallet(
const dbPath = walletCliArgs.wallet.walletDbFile ?? defaultWalletDbPath;
const myHttpLib = createPlatformHttpLib({
enableThrottling: walletCliArgs.wallet.noThrottle ? false : true,
- allowHttp: walletCliArgs.wallet.noHttp ? false : true,
+ requireTls: walletCliArgs.wallet.noHttp,
});
const wallet = await createNativeWalletHost({
persistentStoragePath: dbPath !== ":memory:" ? dbPath : undefined,
@@ -883,7 +883,7 @@ backupCli.subcommand("exportDb", "export-db").action(async (args) => {
});
});
-backupCli.subcommand("storeBackup", "store-backup").action(async (args) => {
+backupCli.subcommand("storeBackup", "store").action(async (args) => {
await withWallet(args, async (wallet) => {
const resp = await wallet.client.call(
WalletApiOperation.CreateStoredBackup,
@@ -893,6 +893,46 @@ backupCli.subcommand("storeBackup", "store-backup").action(async (args) => {
});
});
+backupCli.subcommand("storeBackup", "list-stored").action(async (args) => {
+ await withWallet(args, async (wallet) => {
+ const resp = await wallet.client.call(
+ WalletApiOperation.ListStoredBackups,
+ {},
+ );
+ console.log(JSON.stringify(resp, undefined, 2));
+ });
+});
+
+backupCli
+ .subcommand("storeBackup", "delete-stored")
+ .requiredArgument("name", clk.STRING)
+ .action(async (args) => {
+ await withWallet(args, async (wallet) => {
+ const resp = await wallet.client.call(
+ WalletApiOperation.DeleteStoredBackup,
+ {
+ name: args.storeBackup.name,
+ },
+ );
+ console.log(JSON.stringify(resp, undefined, 2));
+ });
+ });
+
+backupCli
+ .subcommand("recoverBackup", "recover-stored")
+ .requiredArgument("name", clk.STRING)
+ .action(async (args) => {
+ await withWallet(args, async (wallet) => {
+ const resp = await wallet.client.call(
+ WalletApiOperation.RecoverStoredBackup,
+ {
+ name: args.recoverBackup.name,
+ },
+ );
+ console.log(JSON.stringify(resp, undefined, 2));
+ });
+ });
+
backupCli.subcommand("importDb", "import-db").action(async (args) => {
await withWallet(args, async (wallet) => {
const dumpRaw = await read(process.stdin);
@@ -984,14 +1024,14 @@ peerCli
peerCli
.subcommand("confirmIncomingPayPull", "confirm-pull-debit")
- .requiredArgument("peerPullPaymentIncomingId", clk.STRING)
+ .requiredArgument("peerPullDebitId", clk.STRING)
.action(async (args) => {
await withWallet(args, async (wallet) => {
const resp = await wallet.client.call(
WalletApiOperation.ConfirmPeerPullDebit,
{
- peerPullPaymentIncomingId:
- args.confirmIncomingPayPull.peerPullPaymentIncomingId,
+ peerPullDebitId:
+ args.confirmIncomingPayPull.peerPullDebitId,
},
);
console.log(JSON.stringify(resp, undefined, 2));
@@ -1000,14 +1040,14 @@ peerCli
peerCli
.subcommand("confirmIncomingPayPush", "confirm-push-credit")
- .requiredArgument("peerPushPaymentIncomingId", clk.STRING)
+ .requiredArgument("peerPushCreditId", clk.STRING)
.action(async (args) => {
await withWallet(args, async (wallet) => {
const resp = await wallet.client.call(
WalletApiOperation.ConfirmPeerPushCredit,
{
- peerPushPaymentIncomingId:
- args.confirmIncomingPayPush.peerPushPaymentIncomingId,
+ peerPushCreditId:
+ args.confirmIncomingPayPush.peerPushCreditId,
},
);
console.log(JSON.stringify(resp, undefined, 2));
@@ -1219,10 +1259,7 @@ advancedCli
help: "Run the 'bench-internal' benchmark",
})
.action(async (args) => {
- const myHttpLib = createPlatformHttpLib({
- enableThrottling: false,
- allowHttp: true,
- });
+ const myHttpLib = createPlatformHttpLib();
const res = await createNativeWalletHost2({
// No persistent DB storage.
persistentStoragePath: undefined,
diff --git a/packages/taler-wallet-cli/tsconfig.json b/packages/taler-wallet-cli/tsconfig.json
index 100339e43..42f0d88a8 100644
--- a/packages/taler-wallet-cli/tsconfig.json
+++ b/packages/taler-wallet-cli/tsconfig.json
@@ -3,10 +3,10 @@
"compilerOptions": {
"composite": true,
"target": "ES2018",
- "module": "ESNext",
+ "module": "Node16",
"moduleResolution": "Node16",
"sourceMap": true,
- "lib": ["es6"],
+ "lib": ["ES2020"],
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"strict": true,
diff --git a/packages/taler-wallet-core/package.json b/packages/taler-wallet-core/package.json
index 9cd6f6466..fda7b6081 100644
--- a/packages/taler-wallet-core/package.json
+++ b/packages/taler-wallet-core/package.json
@@ -65,8 +65,8 @@
"po2json": "^0.4.5",
"prettier": "^2.8.8",
"rimraf": "^3.0.2",
- "typedoc": "^0.24.8",
- "typescript": "^5.1.3"
+ "typedoc": "^0.25.1",
+ "typescript": "^5.2.2"
},
"dependencies": {
"@gnu-taler/idb-bridge": "workspace:*",
diff --git a/packages/taler-wallet-core/src/crypto/cryptoImplementation.ts b/packages/taler-wallet-core/src/crypto/cryptoImplementation.ts
index c1a761fb6..35777e714 100644
--- a/packages/taler-wallet-core/src/crypto/cryptoImplementation.ts
+++ b/packages/taler-wallet-core/src/crypto/cryptoImplementation.ts
@@ -959,11 +959,7 @@ export const nativeCryptoR: TalerCryptoInterfaceR = {
req: DenominationValidationRequest,
): Promise<ValidationResult> {
const { masterPub, denom } = req;
- const value: AmountJson = {
- currency: denom.currency,
- fraction: denom.amountFrac,
- value: denom.amountVal,
- };
+ const value: AmountJson = Amounts.parseOrThrow(denom.value);
const p = buildSigPS(TalerSignaturePurpose.MASTER_DENOMINATION_KEY_VALIDITY)
.put(decodeCrock(masterPub))
.put(timestampRoundedToBuffer(denom.stampStart))
diff --git a/packages/taler-wallet-core/src/db.ts b/packages/taler-wallet-core/src/db.ts
index b52a503bc..25757ef25 100644
--- a/packages/taler-wallet-core/src/db.ts
+++ b/packages/taler-wallet-core/src/db.ts
@@ -22,59 +22,59 @@ import {
IDBDatabase,
IDBFactory,
IDBObjectStore,
+ IDBRequest,
IDBTransaction,
structuredEncapsulate,
} from "@gnu-taler/idb-bridge";
import {
AgeCommitmentProof,
- AmountJson,
AmountString,
+ Amounts,
+ AttentionInfo,
+ Codec,
CoinEnvelope,
+ CoinPublicKeyString,
CoinRefreshRequest,
CoinStatus,
- MerchantContractTerms,
+ DenomSelectionState,
DenominationInfo,
DenominationPubKey,
- DenomSelectionState,
EddsaPublicKeyString,
EddsaSignatureString,
ExchangeAuditor,
ExchangeGlobalFees,
+ HashCodeString,
InternationalizedString,
- Location,
+ Logger,
+ MerchantContractTerms,
MerchantInfo,
PayCoinSelection,
PeerContractTerms,
- Product,
RefreshReason,
TalerErrorDetail,
+ TalerPreciseTimestamp,
TalerProtocolDuration,
TalerProtocolTimestamp,
TransactionIdStr,
UnblindedSignature,
WireInfo,
- HashCodeString,
- Amounts,
- AttentionInfo,
- Logger,
- CoinPublicKeyString,
- TalerPreciseTimestamp,
+ codecForAny,
} from "@gnu-taler/taler-util";
+import { RetryInfo, TaskIdentifiers } from "./operations/common.js";
import {
DbAccess,
DbReadOnlyTransaction,
DbReadWriteTransaction,
- describeContents,
- describeIndex,
- describeStore,
GetReadWriteAccess,
IndexDescriptor,
- openDatabase,
StoreDescriptor,
StoreNames,
StoreWithIndexes,
+ describeContents,
+ describeIndex,
+ describeStore,
+ openDatabase,
} from "./util/query.js";
-import { RetryInfo, TaskIdentifiers } from "./operations/common.js";
/**
* This file contains the database schema of the Taler wallet together
@@ -99,12 +99,25 @@ import { RetryInfo, TaskIdentifiers } from "./operations/common.js";
*/
/**
+ FIXMEs:
+ - Contract terms can be quite large. We currently tend to read the
+ full contract terms from the DB quite often.
+ Instead, we should probably extract what we need into a separate object
+ store.
+ - More object stores should have an "id" primary key,
+ as this makes referencing less expensive.
+ - Coin selections should probably go into a separate object store.
+ - Some records should be split up into an extra "details" record
+ that we don't always need to iterate over.
+ */
+
+/**
* Name of the Taler database. This is effectively the major
* version of the DB schema. Whenever it changes, custom import logic
* for all previous versions must be written, which should be
* avoided.
*/
-export const TALER_WALLET_MAIN_DB_NAME = "taler-wallet-main-v9";
+export const TALER_WALLET_MAIN_DB_NAME = "taler-wallet-main-v10";
/**
* Name of the metadata database. This database is used
@@ -115,10 +128,17 @@ export const TALER_WALLET_MAIN_DB_NAME = "taler-wallet-main-v9";
export const TALER_WALLET_META_DB_NAME = "taler-wallet-meta";
/**
- * Stored backups, mainly created when manually importing a backup.
+ * Name of the "stored backups" database.
+ * Stored backups are created before manually importing a backup.
+ * We use IndexedDB for this purpose, since we don't have file system
+ * access on some platforms.
*/
-export const TALER_WALLET_STORED_BACKUPS_DB_NAME = "taler-wallet-stored-backups";
+export const TALER_WALLET_STORED_BACKUPS_DB_NAME =
+ "taler-wallet-stored-backups";
+/**
+ * Name of the "meta config" database.
+ */
export const CURRENT_DB_CONFIG_KEY = "currentMainDbName";
/**
@@ -128,26 +148,26 @@ export const CURRENT_DB_CONFIG_KEY = "currentMainDbName";
* backwards-compatible way or object stores and indices
* are added.
*/
-export const WALLET_DB_MINOR_VERSION = 10;
+export const WALLET_DB_MINOR_VERSION = 1;
/**
- * Ranges for operation status fields.
- *
- * All individual enums should make sure that the values they
- * defined are in the right range.
+ * Format of the operation status code: 0x0abc_nnnn
+
+ * a=1: active
+ * 0x0100_nnnn: pending
+ * 0x0101_nnnn: dialog
+ * 0x0102_nnnn: (reserved)
+ * 0x0103_nnnn: aborting
+ * 0x0110_nnnn: suspended
+ * 0x0113_nnnn: suspended-aborting
+ * a=5: final
+ * 0x0500_nnnn: done
+ * 0x0501_nnnn: failed
+ * 0x0502_nnnn: expired
+ * 0x0503_nnnn: aborted
+ *
+ * nnnn=0000 should always be the most generic minor state for the major state
*/
-export enum OperationStatusRange {
- // Operations that need to be actively processed.
- ACTIVE_START = 10,
- ACTIVE_END = 29,
- // Operations that are suspended and might
- // expire, but nothing else can be done.
- SUSPENDED_START = 30,
- SUSPENDED_END = 49,
- // Operations that don't need any attention or processing.
- DORMANT_START = 50,
- DORMANT_END = 69,
-}
/**
* Status of a withdrawal.
@@ -156,71 +176,70 @@ export enum WithdrawalGroupStatus {
/**
* Reserve must be registered with the bank.
*/
- PendingRegisteringBank = 10,
+ PendingRegisteringBank = 0x0100_0001,
+ SuspendedRegisteringBank = 0x0110_0001,
/**
* We've registered reserve's information with the bank
* and are now waiting for the user to confirm the withdraw
* with the bank (typically 2nd factor auth).
*/
- PendingWaitConfirmBank = 11,
+ PendingWaitConfirmBank = 0x0100_0002,
+ SuspendedWaitConfirmBank = 0x0110_0002,
/**
* Querying reserve status with the exchange.
*/
- PendingQueryingStatus = 12,
+ PendingQueryingStatus = 0x0100_0003,
+ SuspendedQueryingStatus = 0x0110_0003,
/**
* Ready for withdrawal.
*/
- PendingReady = 13,
+ PendingReady = 0x0100_0004,
+ SuspendedReady = 0x0110_0004,
/**
* We are telling the bank that we don't want to complete
* the withdrawal!
*/
- AbortingBank = 14,
+ AbortingBank = 0x0103_0001,
+ SuspendedAbortingBank = 0x0113_0001,
/**
* Exchange wants KYC info from the user.
*/
- PendingKyc = 16,
+ PendingKyc = 0x0100_0005,
+ SuspendedKyc = 0x0110_005,
/**
* Exchange is doing AML checks.
*/
- PendingAml = 17,
-
- SuspendedRegisteringBank = 30,
- SuspendedWaitConfirmBank = 31,
- SuspendedQueryingStatus = 32,
- SuspendedReady = 33,
- SuspendedAbortingBank = 34,
- SuspendedKyc = 35,
- SuspendedAml = 36,
+ PendingAml = 0x0100_0006,
+ SuspendedAml = 0x0100_0006,
/**
* The corresponding withdraw record has been created.
* No further processing is done, unless explicitly requested
* by the user.
*/
- Finished = 50,
+ Done = 0x0500_0000,
/**
* The bank aborted the withdrawal.
*/
- FailedBankAborted = 51,
+ FailedBankAborted = 0x0501_0001,
- FailedAbortingBank = 59,
+ FailedAbortingBank = 0x0501_0002,
/**
* Aborted in a state where we were supposed to
* talk to the exchange. Money might have been
* wired or not.
*/
- AbortedExchange = 60,
+ AbortedExchange = 0x0503_0001,
- AbortedBank = 61,
+ AbortedBank = 0x0503_0002,
}
/**
@@ -259,78 +278,23 @@ export interface ReserveBankInfo {
}
/**
- * Record that indicates the wallet trusts
- * a particular auditor.
- */
-export interface AuditorTrustRecord {
- /**
- * Currency that we trust this auditor for.
- */
- currency: string;
-
- /**
- * Base URL of the auditor.
- */
- auditorBaseUrl: string;
-
- /**
- * Public key of the auditor.
- */
- auditorPub: string;
-
- /**
- * UIDs for the operation of adding this auditor
- * as a trusted auditor.
- *
- * (Used for backup/sync merging and tombstones.)
- */
- uids: string[];
-}
-
-/**
- * Record to indicate trust for a particular exchange.
- */
-export interface ExchangeTrustRecord {
- /**
- * Currency that we trust this exchange for.
- */
- currency: string;
-
- /**
- * Canonicalized exchange base URL.
- */
- exchangeBaseUrl: string;
-
- /**
- * Master public key of the exchange.
- */
- exchangeMasterPub: string;
-
- /**
- * UIDs for the operation of adding this exchange
- * as trusted.
- */
- uids: string[];
-}
-
-/**
* Status of a denomination.
*/
export enum DenominationVerificationStatus {
/**
- * Verification was delayed.
+ * Verification was delayed (pending).
*/
- Unverified = OperationStatusRange.ACTIVE_START,
+ Unverified = 0x0100_0000,
/**
* Verified as valid.
*/
- VerifiedGood = OperationStatusRange.DORMANT_START,
+ VerifiedGood = 0x0500_0000,
/**
* Verified as invalid.
*/
- VerifiedBad = OperationStatusRange.DORMANT_START + 1,
+ VerifiedBad = 0x0501_0000,
}
export interface DenomFees {
@@ -359,12 +323,14 @@ export interface DenomFees {
* Denomination record as stored in the wallet's database.
*/
export interface DenominationRecord {
+ /**
+ * Currency of the denomination.
+ *
+ * Stored separately as we have an index on it.
+ */
currency: string;
- // FIXME: Use binary encoding of amount instead?
- amountVal: number;
-
- amountFrac: number;
+ value: AmountString;
/**
* The denomination public key.
@@ -443,14 +409,6 @@ export interface DenominationRecord {
}
export namespace DenominationRecord {
- export function getValue(d: DenominationRecord): AmountJson {
- return {
- currency: d.currency,
- fraction: d.amountFrac,
- value: d.amountVal,
- };
- }
-
export function toDenomInfo(d: DenominationRecord): DenominationInfo {
return {
denomPub: d.denomPub,
@@ -463,7 +421,7 @@ export namespace DenominationRecord {
stampExpireLegal: d.stampExpireLegal,
stampExpireWithdraw: d.stampExpireWithdraw,
stampStart: d.stampStart,
- value: Amounts.stringify(DenominationRecord.getValue(d)),
+ value: Amounts.stringify(d.value),
exchangeBaseUrl: d.exchangeBaseUrl,
};
}
@@ -578,6 +536,7 @@ export enum ExchangeEntryDbRecordStatus {
Used = 3,
}
+// FIXME: Use status ranges for this as well?
export enum ExchangeEntryDbUpdateStatus {
Initial = 1,
InitialUpdate = 2,
@@ -658,9 +617,9 @@ export interface ExchangeEntryRecord {
}
export enum PlanchetStatus {
- Pending = 10 /* ACTIVE_START */,
- KycRequired = 11 /* ACTIVE_START + 1 */,
- WithdrawalDone = 50 /* DORMANT_START */,
+ Pending = 0x0100_0000,
+ KycRequired = 0x0100_0001,
+ WithdrawalDone = 0x0500_000,
}
/**
@@ -933,50 +892,45 @@ export interface RewardRecord {
}
export enum RewardRecordStatus {
- PendingPickup = 10,
-
- SuspendidPickup = 20,
-
- DialogAccept = 30,
-
- Done = 50,
- Aborted = 51,
+ PendingPickup = 0x0100_0000,
+ SuspendedPickup = 0x0110_0000,
+ DialogAccept = 0x0101_0000,
+ Done = 0x0500_0000,
+ Aborted = 0x0500_0000,
}
export enum RefreshCoinStatus {
- Pending = OperationStatusRange.ACTIVE_START,
- Finished = OperationStatusRange.DORMANT_START,
+ Pending = 0x0100_0000,
+ Finished = 0x0500_0000,
/**
* The refresh for this coin has been frozen, because of a permanent error.
* More info in lastErrorPerCoin.
*/
- Failed = OperationStatusRange.DORMANT_START + 1,
-}
-
-export enum OperationStatus {
- Finished = OperationStatusRange.DORMANT_START,
- Pending = OperationStatusRange.ACTIVE_START,
+ Failed = 0x0501_000,
}
export enum RefreshOperationStatus {
- Pending = 10 /* ACTIVE_START */,
- Suspended = 20 /* DORMANT_START + 2 */,
+ Pending = 0x0100_0000,
+ Suspended = 0x0110_0000,
- Finished = 50 /* DORMANT_START */,
- Failed = 51 /* DORMANT_START + 1 */,
+ Finished = 0x0500_000,
+ Failed = 0x0501_000,
}
/**
* Status of a single element of a deposit group.
*/
export enum DepositElementStatus {
- Unknown = 10,
- Accepted = 20,
- KycRequired = 30,
- Wired = 40,
- RefundSuccess = 50,
- RefundFailed = 51,
+ DepositPending = 0x0100_0000,
+ /**
+ * Accepted, but tracking.
+ */
+ Tracking = 0x0100_0001,
+ KycRequired = 0x0100_0002,
+ Wired = 0x0500_0000,
+ RefundSuccess = 0x0503_0000,
+ RefundFailed = 0x0501_0000,
}
/**
@@ -991,16 +945,10 @@ export interface RefreshReasonDetails {
* Group of refresh operations. The refreshed coins do not
* have to belong to the same exchange, but must have the same
* currency.
- *
- * FIXME: Should include the currency as a top-level field,
- * but we need to write a migration for that.
*/
export interface RefreshGroupRecord {
operationStatus: RefreshOperationStatus;
- // FIXME: Put this into a different object store?
- lastErrorPerCoin: { [coinIndex: number]: TalerErrorDetail };
-
/**
* Unique, randomly generated identifier for this group of
* refresh operations.
@@ -1009,8 +957,6 @@ export interface RefreshGroupRecord {
/**
* Currency of this refresh group.
- *
- * FIXME: Write a migration to add this to earlier DB versions.
*/
currency: string;
@@ -1026,13 +972,9 @@ export interface RefreshGroupRecord {
oldCoinPubs: string[];
- // FIXME: Should this go into a separate
- // object store for faster updates?
- refreshSessionPerCoin: (RefreshSessionRecord | undefined)[];
-
inputPerCoin: AmountString[];
- estimatedOutputPerCoin: AmountString[];
+ expectedOutputPerCoin: AmountString[];
/**
* Flag for each coin whether refreshing finished.
@@ -1054,6 +996,13 @@ export interface RefreshGroupRecord {
* Ongoing refresh
*/
export interface RefreshSessionRecord {
+ refreshGroupId: string;
+
+ /**
+ * Index of the coin in the refresh group.
+ */
+ coinIndex: number;
+
/**
* 512-bit secret that can be used to derive
* the other cryptographic material for the refresh session.
@@ -1078,6 +1027,8 @@ export interface RefreshSessionRecord {
* The no-reveal-index after we've done the melting.
*/
norevealIndex?: number;
+
+ lastError?: TalerErrorDetail;
}
export enum RefundReason {
@@ -1106,9 +1057,6 @@ export interface AllowedExchangeInfo {
* processing in the wallet.
*/
export interface WalletContractData {
- products?: Product[];
- summaryI18n: { [lang_tag: string]: string } | undefined;
-
/**
* Fulfillment URL, or the empty string if the order has no fulfillment URL.
*
@@ -1126,6 +1074,7 @@ export interface WalletContractData {
orderId: string;
merchantBaseUrl: string;
summary: string;
+ summaryI18n: { [lang_tag: string]: string } | undefined;
autoRefund: TalerProtocolDuration | undefined;
maxWireFee: AmountString;
wireFeeAmortization: number;
@@ -1137,89 +1086,90 @@ export interface WalletContractData {
wireInfoHash: string;
maxDepositFee: AmountString;
minimumAge?: number;
- deliveryDate: TalerProtocolTimestamp | undefined;
- deliveryLocation: Location | undefined;
}
export enum PurchaseStatus {
/**
* Not downloaded yet.
*/
- PendingDownloadingProposal = 10,
+ PendingDownloadingProposal = 0x0100_0000,
+ SuspendedDownloadingProposal = 0x0110_0000,
/**
* The user has accepted the proposal.
*/
- PendingPaying = 11,
+ PendingPaying = 0x0100_0001,
+ SuspendedPaying = 0x0110_0001,
/**
* Currently in the process of aborting with a refund.
*/
- AbortingWithRefund = 12,
+ AbortingWithRefund = 0x0103_0000,
+ SuspendedAbortingWithRefund = 0x0113_0000,
/**
* Paying a second time, likely with different session ID
*/
- PendingPayingReplay = 13,
+ PendingPayingReplay = 0x0100_0002,
+ SuspendedPayingReplay = 0x0110_0002,
/**
* Query for refunds (until query succeeds).
*/
- PendingQueryingRefund = 14,
+ PendingQueryingRefund = 0x0100_0003,
+ SuspendedQueryingRefund = 0x0110_0003,
/**
* Query for refund (until auto-refund deadline is reached).
*/
- PendingQueryingAutoRefund = 15,
+ PendingQueryingAutoRefund = 0x0100_0004,
+ SuspendedQueryingAutoRefund = 0x0110_0004,
- PendingAcceptRefund = 16,
-
- SuspendedDownloadingProposal = 20,
- SuspendedPaying = 21,
- SuspendedAbortingWithRefund = 22,
- SuspendedPayingReplay = 23,
- SuspendedQueryingRefund = 24,
- SuspendedQueryingAutoRefund = 25,
- SuspendedPendingAcceptRefund = 26,
+ PendingAcceptRefund = 0x0100_0005,
+ SuspendedPendingAcceptRefund = 0x0100_0005,
/**
* Proposal downloaded, but the user needs to accept/reject it.
*/
- DialogProposed = 30,
+ DialogProposed = 0x0101_0000,
+
/**
* Proposal shared to other wallet or read from other wallet
* the user needs to accept/reject it.
*/
- DialogShared = 31,
+ DialogShared = 0x0101_0001,
/**
* The user has rejected the proposal.
*/
- AbortedProposalRefused = 50,
+ AbortedProposalRefused = 0x0503_0000,
/**
* Downloading or processing the proposal has failed permanently.
*/
- FailedClaim = 51,
+ FailedClaim = 0x0501_0000,
+
+ /**
+ * Payment was successful.
+ */
+ Done = 0x0500_0000,
/**
* Downloaded proposal was detected as a re-purchase.
*/
- RepurchaseDetected = 52,
+ DoneRepurchaseDetected = 0x0500_0001,
/**
* The payment has been aborted.
*/
- AbortedIncompletePayment = 53,
+ AbortedIncompletePayment = 0x0503_0000,
/**
- * Payment was successful.
+ * Tried to abort, but aborting failed or was cancelled.
*/
- Done = 54,
+ FailedAbort = 0x0501_0001,
- FailedAbort = 55,
-
- AbortedRefunded = 56,
+ AbortedRefunded = 0x0503_0000,
}
/**
@@ -1334,14 +1284,6 @@ export interface PurchaseRecord {
timestampAccept: TalerPreciseTimestamp | undefined;
/**
- * Pending refunds for the purchase. A refund is pending
- * when the merchant reports a transient error from the exchange.
- *
- * FIXME: Put this into a separate object store?
- */
- // refunds: { [refundKey: string]: WalletRefundItem };
-
- /**
* When was the last refund made?
* Set to 0 if no refund was made on the purchase.
*/
@@ -1404,6 +1346,7 @@ export interface WalletBackupConfState {
lastBackupNonce?: string;
}
+// FIXME: Should these be numeric codes?
export const enum WithdrawalRecordType {
BankManual = "bank-manual",
BankIntegrated = "bank-integrated",
@@ -1428,7 +1371,8 @@ export interface WgInfoBankManual {
export interface WgInfoBankPeerPull {
withdrawalType: WithdrawalRecordType.PeerPullCredit;
- contractTerms: any;
+ // FIXME: include a transaction ID here?
+
/**
* Needed to quickly construct the taler:// URI for the counterparty
* without a join.
@@ -1439,7 +1383,7 @@ export interface WgInfoBankPeerPull {
export interface WgInfoBankPeerPush {
withdrawalType: WithdrawalRecordType.PeerPushCredit;
- contractTerms: any;
+ // FIXME: include a transaction ID here?
}
export interface WgInfoBankRecoup {
@@ -1713,19 +1657,21 @@ export interface BackupProviderRecord {
}
export enum DepositOperationStatus {
- PendingDeposit = 10,
- Aborting = 11,
- PendingTrack = 12,
- PendingKyc = 13,
+ PendingDeposit = 0x0100_0000,
+ PendingTrack = 0x0100_0001,
+ PendingKyc = 0x0100_0002,
+
+ Aborting = 0x0103_0000,
- SuspendedDeposit = 20,
- SuspendedAborting = 21,
- SuspendedTrack = 22,
- SuspendedKyc = 23,
+ SuspendedDeposit = 0x0110_0000,
+ SuspendedTrack = 0x0110_0001,
+ SuspendedKyc = 0x0110_0002,
- Finished = 50,
- Failed = 51,
- Aborted = 52,
+ SuspendedAborting = 0x0113_0000,
+
+ Finished = 0x0500_0000,
+ Failed = 0x0501_0000,
+ Aborted = 0x0503_0000,
}
export interface DepositTrackingInfo {
@@ -1777,10 +1723,8 @@ export interface DepositGroupRecord {
/**
* The counterparty effective deposit amount.
- *
- * FIXME: If possible, rename to counterpartyEffectiveDepositAmount.
*/
- effectiveDepositAmount: AmountString;
+ counterpartyEffectiveDepositAmount: AmountString;
timestampCreated: TalerPreciseTimestamp;
@@ -1788,11 +1732,7 @@ export interface DepositGroupRecord {
operationStatus: DepositOperationStatus;
- // FIXME: Duplication between this and transactionPerCoin!
- depositedPerCoin: boolean[];
-
- // FIXME: Improve name!
- transactionPerCoin: DepositElementStatus[];
+ statusPerCoin: DepositElementStatus[];
/**
* When the deposit transaction was aborted and
@@ -1816,31 +1756,6 @@ export interface DepositKycInfo {
exchangeBaseUrl: string;
}
-/**
- * Record for a deposits that the wallet observed
- * as a result of double spending, but which is not
- * present in the wallet's own database otherwise.
- */
-export interface GhostDepositGroupRecord {
- /**
- * When multiple deposits for the same contract terms hash
- * have a different timestamp, we choose the earliest one.
- */
- timestamp: TalerPreciseTimestamp;
-
- contractTermsHash: string;
-
- deposits: {
- coinPub: string;
- amount: AmountString;
- timestamp: TalerProtocolTimestamp;
- depositFee: AmountString;
- merchantPub: string;
- coinSig: string;
- wireHash: string;
- }[];
-}
-
export interface TombstoneRecord {
/**
* Tombstone ID, with the syntax "tmb:<type>:<key>".
@@ -1848,24 +1763,24 @@ export interface TombstoneRecord {
id: string;
}
-export enum PeerPushPaymentInitiationStatus {
+export enum PeerPushDebitStatus {
/**
* Initiated, but no purse created yet.
*/
- PendingCreatePurse = 10 /* ACTIVE_START */,
- PendingReady = 11,
- AbortingDeletePurse = 12,
- AbortingRefresh = 13,
+ PendingCreatePurse = 0x0100_0000 /* ACTIVE_START */,
+ PendingReady = 0x0100_0001,
+ AbortingDeletePurse = 0x0103_0000,
+ AbortingRefresh = 0x0103_0001,
- SuspendedCreatePurse = 30,
- SuspendedReady = 31,
- SuspendedAbortingDeletePurse = 32,
- SuspendedAbortingRefresh = 33,
+ SuspendedCreatePurse = 0x0110_0000,
+ SuspendedReady = 0x0110_0001,
+ SuspendedAbortingDeletePurse = 0x0113_0000,
+ SuspendedAbortingRefresh = 0x0113_0001,
- Done = 50 /* DORMANT_START */,
- Aborted = 51,
- Failed = 52,
- Expired = 53,
+ Done = 0x0500_0000,
+ Aborted = 0x0503_0000,
+ Failed = 0x0501_0000,
+ Expired = 0x0502_0000,
}
export interface PeerPushPaymentCoinSelection {
@@ -1876,7 +1791,7 @@ export interface PeerPushPaymentCoinSelection {
/**
* Record for a push P2P payment that this wallet initiated.
*/
-export interface PeerPushPaymentInitiationRecord {
+export interface PeerPushDebitRecord {
/**
* What exchange are funds coming from?
*/
@@ -1922,11 +1837,6 @@ export interface PeerPushPaymentInitiationRecord {
*/
contractEncNonce: string;
- /**
- * FIXME: Put those in a different object store!
- */
- contractTerms: PeerContractTerms;
-
purseExpiration: TalerProtocolTimestamp;
timestampCreated: TalerPreciseTimestamp;
@@ -1936,32 +1846,34 @@ export interface PeerPushPaymentInitiationRecord {
/**
* Status of the peer push payment initiation.
*/
- status: PeerPushPaymentInitiationStatus;
+ status: PeerPushDebitStatus;
}
-export enum PeerPullPaymentInitiationStatus {
- PendingCreatePurse = 10 /* ACTIVE_START */,
+export enum PeerPullPaymentCreditStatus {
+ PendingCreatePurse = 0x0100_0000,
/**
* Purse created, waiting for the other party to accept the
* invoice and deposit money into it.
*/
- PendingReady = 11 /* ACTIVE_START + 1 */,
- PendingMergeKycRequired = 12 /* ACTIVE_START + 2 */,
- PendingWithdrawing = 13,
- AbortingDeletePurse = 14,
+ PendingReady = 0x0100_0001,
+ PendingMergeKycRequired = 0x0100_0002,
+ PendingWithdrawing = 0x0100_0003,
+
+ AbortingDeletePurse = 0x0103_0000,
+
+ SuspendedCreatePurse = 0x0110_0000,
+ SuspendedReady = 0x0110_0001,
+ SuspendedMergeKycRequired = 0x0110_0002,
+ SuspendedWithdrawing = 0x0110_0000,
- SuspendedCreatePurse = 30,
- SuspendedReady = 31,
- SuspendedMergeKycRequired = 32,
- SuspendedWithdrawing = 33,
- SuspendedAbortingDeletePurse = 34,
+ SuspendedAbortingDeletePurse = 0x0113_0000,
- Done = 50 /* DORMANT_START */,
- Failed = 51,
- Aborted = 52,
+ Done = 0x0500_0000,
+ Failed = 0x0501_0000,
+ Aborted = 0x0503_0000,
}
-export interface PeerPullPaymentInitiationRecord {
+export interface PeerPullCreditRecord {
/**
* What exchange are we using for the payment request?
*/
@@ -1969,6 +1881,7 @@ export interface PeerPullPaymentInitiationRecord {
/**
* Amount requested.
+ * FIXME: What type of instructed amount is i?
*/
amount: AmountString;
@@ -1999,11 +1912,6 @@ export interface PeerPullPaymentInitiationRecord {
contractEncNonce: string;
- /**
- * FIXME: Put in separate object store!
- */
- contractTerms: PeerContractTerms;
-
mergeTimestamp: TalerPreciseTimestamp;
mergeReserveRowId: number;
@@ -2011,7 +1919,7 @@ export interface PeerPullPaymentInitiationRecord {
/**
* Status of the peer pull payment initiation.
*/
- status: PeerPullPaymentInitiationStatus;
+ status: PeerPullPaymentCreditStatus;
kycInfo?: KycPendingInfo;
@@ -2020,24 +1928,24 @@ export interface PeerPullPaymentInitiationRecord {
withdrawalGroupId: string | undefined;
}
-export enum PeerPushPaymentIncomingStatus {
- PendingMerge = 10 /* ACTIVE_START */,
- PendingMergeKycRequired = 11 /* ACTIVE_START + 1 */,
+export enum PeerPushCreditStatus {
+ PendingMerge = 0x0100_0000,
+ PendingMergeKycRequired = 0x0100_0001,
/**
* Merge was successful and withdrawal group has been created, now
* everything is in the hand of the withdrawal group.
*/
- PendingWithdrawing = 12,
+ PendingWithdrawing = 0x0100_0002,
- SuspendedMerge = 20,
- SuspendedMergeKycRequired = 21,
- SuspendedWithdrawing = 22,
+ SuspendedMerge = 0x0110_0000,
+ SuspendedMergeKycRequired = 0x0110_0001,
+ SuspendedWithdrawing = 0x0110_0002,
- DialogProposed = 30 /* USER_ATTENTION_START */,
+ DialogProposed = 0x0101_0000,
- Done = 50 /* DORMANT_START */,
- Aborted = 51,
- Failed = 52,
+ Done = 0x0500_0000,
+ Aborted = 0x0503_0000,
+ Failed = 0x0501_0000,
}
/**
@@ -2046,7 +1954,7 @@ export enum PeerPushPaymentIncomingStatus {
* Unique: (exchangeBaseUrl, pursePub)
*/
export interface PeerPushPaymentIncomingRecord {
- peerPushPaymentIncomingId: string;
+ peerPushCreditId: string;
exchangeBaseUrl: string;
@@ -2069,7 +1977,7 @@ export interface PeerPushPaymentIncomingRecord {
/**
* Status of the peer push payment incoming initiation.
*/
- status: PeerPushPaymentIncomingStatus;
+ status: PeerPushCreditStatus;
/**
* Associated withdrawal group.
@@ -2090,17 +1998,17 @@ export interface PeerPushPaymentIncomingRecord {
}
export enum PeerPullDebitRecordStatus {
- PendingDeposit = 10 /* ACTIVE_START */,
- AbortingRefresh = 11,
+ PendingDeposit = 0x0100_0001,
+ AbortingRefresh = 0x0103_0001,
- SuspendedDeposit = 20,
- SuspendedAbortingRefresh = 21,
+ SuspendedDeposit = 0x0110_0001,
+ SuspendedAbortingRefresh = 0x0113_0001,
- DialogProposed = 30 /* USER_ATTENTION_START */,
+ DialogProposed = 0x0101_0001,
- DonePaid = 50 /* DORMANT_START */,
- Aborted = 51,
- Failed = 52,
+ Done = 0x0500_0000,
+ Aborted = 0x0503_0000,
+ Failed = 0x0501_0000,
}
export interface PeerPullPaymentCoinSelection {
@@ -2118,7 +2026,7 @@ export interface PeerPullPaymentCoinSelection {
* AKA PeerPullDebit.
*/
export interface PeerPullPaymentIncomingRecord {
- peerPullPaymentIncomingId: string;
+ peerPullDebitId: string;
pursePub: string;
@@ -2184,8 +2092,7 @@ export interface OperationRetryRecord {
*/
export interface CoinAvailabilityRecord {
currency: string;
- amountVal: number;
- amountFrac: number;
+ value: AmountString;
denomPubHash: string;
exchangeBaseUrl: string;
@@ -2263,10 +2170,10 @@ export interface CurrencySettingsRecord {
}
export enum RefundGroupStatus {
- Pending = 10,
- Done = 50,
- Failed = 51,
- Aborted = 52,
+ Pending = 0x0100_0000,
+ Done = 0x0500_0000,
+ Failed = 0x0501_0000,
+ Aborted = 0x0503_0000,
}
/**
@@ -2302,16 +2209,16 @@ export enum RefundItemStatus {
*
* We'll try again!
*/
- Pending = 10,
+ Pending = 0x0100_0000,
/**
* Refund was obtained successfully.
*/
- Done = 50,
+ Done = 0x0500_0000,
/**
* Permanent error reported by the exchange
* for the refund.
*/
- Failed = 51,
+ Failed = 0x0501_0000,
}
/**
@@ -2327,7 +2234,9 @@ export interface RefundItemRecord {
refundGroupId: string;
- // Execution time as claimed by the merchant
+ /**
+ * Execution time as claimed by the merchant
+ */
executionTime: TalerProtocolTimestamp;
/**
@@ -2337,22 +2246,15 @@ export interface RefundItemRecord {
refundAmount: AmountString;
- //refundFee: AmountString;
-
- /**
- * Upper bound on the refresh cost incurred by
- * applying this refund.
- *
- * Might be lower in practice when two refunds on the same
- * coin are refreshed in the same refresh operation.
- */
- //totalRefreshCostBound: AmountString;
-
coinPub: string;
rtxid: number;
}
+export function passthroughCodec<T>(): Codec<T> {
+ return codecForAny();
+}
+
/**
* Schema definition for the IndexedDB
* wallet database.
@@ -2362,7 +2264,6 @@ export const WalletStoresV1 = {
"currencySettings",
describeContents<CurrencySettingsRecord>({
keyPath: ["currency"],
- versionAdded: 3,
}),
{},
),
@@ -2411,42 +2312,11 @@ export const WalletStoresV1 = {
byReservePub: describeIndex("byReservePub", "reservePub", {}),
},
),
- exchangeTos: describeStore(
- "exchangeTos",
- describeContents<ExchangeTosRecord>({
- keyPath: ["exchangeBaseUrl", "etag"],
- }),
- {},
- ),
config: describeStore(
"config",
describeContents<ConfigRecord>({ keyPath: "key" }),
{},
),
- auditorTrust: describeStore(
- "auditorTrust",
- describeContents<AuditorTrustRecord>({
- keyPath: ["currency", "auditorBaseUrl"],
- }),
- {
- byAuditorPub: describeIndex("byAuditorPub", "auditorPub"),
- byUid: describeIndex("byUid", "uids", {
- multiEntry: true,
- }),
- },
- ),
- exchangeTrust: describeStore(
- "exchangeTrust",
- describeContents<ExchangeTrustRecord>({
- keyPath: ["currency", "exchangeBaseUrl"],
- }),
- {
- byExchangeMasterPub: describeIndex(
- "byExchangeMasterPub",
- "exchangeMasterPub",
- ),
- },
- ),
denominations: describeStore(
"denominations",
describeContents<DenominationRecord>({
@@ -2499,6 +2369,13 @@ export const WalletStoresV1 = {
byStatus: describeIndex("byStatus", "operationStatus"),
},
),
+ refreshSessions: describeStore(
+ "refreshSessions",
+ describeContents<RefreshSessionRecord>({
+ keyPath: ["refreshGroupId", "coinIndex"],
+ }),
+ {},
+ ),
recoupGroups: describeStore(
"recoupGroups",
describeContents<RecoupGroupRecord>({
@@ -2607,17 +2484,10 @@ export const WalletStoresV1 = {
}),
{},
),
- ghostDepositGroups: describeStore(
- "ghostDepositGroups",
- describeContents<GhostDepositGroupRecord>({
- keyPath: "contractTermsHash",
- }),
- {},
- ),
- peerPushPaymentIncoming: describeStore(
- "peerPushPaymentIncoming",
+ peerPushCredit: describeStore(
+ "peerPushCredit",
describeContents<PeerPushPaymentIncomingRecord>({
- keyPath: "peerPushPaymentIncomingId",
+ keyPath: "peerPushCreditId",
}),
{
byExchangeAndPurse: describeIndex("byExchangeAndPurse", [
@@ -2628,24 +2498,21 @@ export const WalletStoresV1 = {
"byExchangeAndContractPriv",
["exchangeBaseUrl", "contractPriv"],
{
- versionAdded: 5,
unique: true,
},
),
byWithdrawalGroupId: describeIndex(
"byWithdrawalGroupId",
"withdrawalGroupId",
- {
- versionAdded: 5,
- },
+ {},
),
byStatus: describeIndex("byStatus", "status"),
},
),
- peerPullPaymentIncoming: describeStore(
- "peerPullPaymentIncoming",
+ peerPullDebit: describeStore(
+ "peerPullDebit",
describeContents<PeerPullPaymentIncomingRecord>({
- keyPath: "peerPullPaymentIncomingId",
+ keyPath: "peerPullDebitId",
}),
{
byExchangeAndPurse: describeIndex("byExchangeAndPurse", [
@@ -2656,16 +2523,15 @@ export const WalletStoresV1 = {
"byExchangeAndContractPriv",
["exchangeBaseUrl", "contractPriv"],
{
- versionAdded: 5,
unique: true,
},
),
byStatus: describeIndex("byStatus", "status"),
},
),
- peerPullPaymentInitiations: describeStore(
- "peerPullPaymentInitiations",
- describeContents<PeerPullPaymentInitiationRecord>({
+ peerPullCredit: describeStore(
+ "peerPullCredit",
+ describeContents<PeerPullCreditRecord>({
keyPath: "pursePub",
}),
{
@@ -2673,15 +2539,13 @@ export const WalletStoresV1 = {
byWithdrawalGroupId: describeIndex(
"byWithdrawalGroupId",
"withdrawalGroupId",
- {
- versionAdded: 5,
- },
+ {},
),
},
),
- peerPushPaymentInitiations: describeStore(
- "peerPushPaymentInitiations",
- describeContents<PeerPushPaymentInitiationRecord>({
+ peerPushDebit: describeStore(
+ "peerPushDebit",
+ describeContents<PeerPushDebitRecord>({
keyPath: "pursePub",
}),
{
@@ -2706,7 +2570,6 @@ export const WalletStoresV1 = {
"userAttention",
describeContents<UserAttentionRecord>({
keyPath: ["entityId", "info.type"],
- versionAdded: 2,
}),
{},
),
@@ -2714,20 +2577,16 @@ export const WalletStoresV1 = {
"refundGroups",
describeContents<RefundGroupRecord>({
keyPath: "refundGroupId",
- versionAdded: 7,
}),
{
byProposalId: describeIndex("byProposalId", "proposalId"),
- byStatus: describeIndex("byStatus", "status", {
- versionAdded: 10,
- }),
+ byStatus: describeIndex("byStatus", "status", {}),
},
),
refundItems: describeStore(
"refundItems",
describeContents<RefundItemRecord>({
keyPath: "id",
- versionAdded: 7,
autoIncrement: true,
}),
{
@@ -2742,7 +2601,6 @@ export const WalletStoresV1 = {
"fixups",
describeContents<FixupRecord>({
keyPath: "fixupName",
- versionAdded: 2,
}),
{},
),
@@ -2845,11 +2703,10 @@ export async function exportSingleDb(
dbName,
undefined,
() => {
- // May not happen, since we're not requesting a specific version
- throw Error("unexpected version change");
+ logger.info(`unexpected onversionchange in exportSingleDb of ${dbName}`);
},
() => {
- logger.info("unexpected onupgradeneeded");
+ logger.info(`unexpected onupgradeneeded in exportSingleDb of ${dbName}`);
},
);
@@ -2861,7 +2718,7 @@ export async function exportSingleDb(
return new Promise((resolve, reject) => {
const tx = myDb.transaction(Array.from(myDb.objectStoreNames));
tx.addEventListener("complete", () => {
- myDb.close();
+ //myDb.close();
resolve(singleDbDump);
});
// tslint:disable-next-line:prefer-for-of
@@ -2897,6 +2754,7 @@ export async function exportSingleDb(
if (store.keyPath == null) {
rec.key = structuredEncapsulate(cursor.key);
}
+ storeDump.records.push(rec);
cursor.continue();
}
});
@@ -2925,21 +2783,22 @@ async function recoverFromDump(
db: IDBDatabase,
dbDump: DbDumpDatabase,
): Promise<void> {
- return new Promise((resolve, reject) => {
- const tx = db.transaction(Array.from(db.objectStoreNames), "readwrite");
- tx.addEventListener("complete", () => {
- resolve();
- });
- for (let i = 0; i < db.objectStoreNames.length; i++) {
- const name = db.objectStoreNames[i];
- const storeDump = dbDump.stores[name];
- if (!storeDump) continue;
- for (let rec of storeDump.records) {
- tx.objectStore(name).put(rec.value, rec.key);
- }
+ const tx = db.transaction(Array.from(db.objectStoreNames), "readwrite");
+ const txProm = promiseFromTransaction(tx);
+ const storeNames = db.objectStoreNames;
+ for (let i = 0; i < storeNames.length; i++) {
+ const name = db.objectStoreNames[i];
+ const storeDump = dbDump.stores[name];
+ if (!storeDump) continue;
+ await promiseFromRequest(tx.objectStore(name).clear());
+ logger.info(`importing ${storeDump.records.length} records into ${name}`);
+ for (let rec of storeDump.records) {
+ await promiseFromRequest(tx.objectStore(name).put(rec.value, rec.key));
+ logger.info("importing record done");
}
- tx.commit();
- });
+ }
+ tx.commit();
+ return await txProm;
}
function checkDbDump(x: any): x is DbDump {
@@ -2969,106 +2828,7 @@ export interface FixupDescription {
/**
* Manual migrations between minor versions of the DB schema.
*/
-export const walletDbFixups: FixupDescription[] = [
- {
- name: "RefreshGroupRecord_currency",
- async fn(tx): Promise<void> {
- await tx.refreshGroups.iter().forEachAsync(async (rg) => {
- if (rg.currency) {
- return;
- }
- // Empty refresh group without input coin, delete it!
- if (rg.inputPerCoin.length === 0) {
- await tx.refreshGroups.delete(rg.refreshGroupId);
- return;
- }
- rg.currency = Amounts.parseOrThrow(rg.inputPerCoin[0]).currency;
- await tx.refreshGroups.put(rg);
- });
- },
- },
- {
- name: "DepositGroupRecord_transactionPerCoin",
- async fn(tx): Promise<void> {
- await tx.depositGroups.iter().forEachAsync(async (dg) => {
- if (dg.transactionPerCoin) {
- return;
- }
- dg.transactionPerCoin = dg.depositedPerCoin.map(
- (c) => DepositElementStatus.Unknown,
- );
- await tx.depositGroups.put(dg);
- });
- },
- },
- {
- name: "PeerPullPaymentIncomingRecord_totalCostEstimated_add",
- async fn(tx): Promise<void> {
- await tx.peerPullPaymentIncoming.iter().forEachAsync(async (pi) => {
- if (pi.totalCostEstimated) {
- return;
- }
- // Not really the cost, but a good substitute for older transactions
- // that don't sture the effective cost of the transaction.
- pi.totalCostEstimated = pi.contractTerms.amount;
- await tx.peerPullPaymentIncoming.put(pi);
- });
- },
- },
- {
- name: "PeerPushPaymentIncomingRecord_totalCostEstimated_add",
- async fn(tx): Promise<void> {
- await tx.peerPushPaymentIncoming.iter().forEachAsync(async (pi) => {
- if (pi.estimatedAmountEffective) {
- return;
- }
- const contractTerms = await tx.contractTerms.get(pi.contractTermsHash);
- if (!contractTerms) {
- // Not sure what we can do here!
- } else {
- // Not really the cost, but a good substitute for older transactions
- // that don't sture the effective cost of the transaction.
- pi.estimatedAmountEffective = contractTerms.contractTermsRaw.amount;
- await tx.peerPushPaymentIncoming.put(pi);
- }
- });
- },
- },
- {
- name: "PeerPullPaymentInitiationRecord_estimatedAmountEffective_add",
- async fn(tx): Promise<void> {
- await tx.peerPullPaymentInitiations.iter().forEachAsync(async (pi) => {
- if (pi.estimatedAmountEffective) {
- return;
- }
- pi.estimatedAmountEffective = pi.amount;
- await tx.peerPullPaymentInitiations.put(pi);
- });
- },
- },
- {
- name: "PeerPushPaymentInitiationRecord_ALL_removeLegacyTx",
- async fn(tx): Promise<void> {
- await tx.peerPushPaymentInitiations.iter().forEachAsync(async (pi) => {
- // Remove legacy transactions that don't have the totalCost field yet.
- if (!pi.totalCost) {
- await tx.peerPushPaymentInitiations.delete(pi.pursePub);
- }
- });
- },
- },
- {
- name: "CoinAvailabilityRecord_visibleCoinCount_add",
- async fn(tx): Promise<void> {
- await tx.coinAvailability.iter().forEachAsync(async (r) => {
- if (r.visibleCoinCount == null) {
- r.visibleCoinCount = r.freshCoinCount;
- await tx.coinAvailability.put(r);
- }
- });
- },
- },
-];
+export const walletDbFixups: FixupDescription[] = [];
const logger = new Logger("db.ts");
@@ -3196,6 +2956,17 @@ function promiseFromTransaction(transaction: IDBTransaction): Promise<void> {
});
}
+export function promiseFromRequest(request: IDBRequest): Promise<any> {
+ return new Promise((resolve, reject) => {
+ request.onsuccess = () => {
+ resolve(request.result);
+ };
+ request.onerror = () => {
+ reject(request.error);
+ };
+ });
+}
+
/**
* Purge all data in the given database.
*/
diff --git a/packages/taler-wallet-core/src/dbless.ts b/packages/taler-wallet-core/src/dbless.ts
index 5532345ae..11c6c0f74 100644
--- a/packages/taler-wallet-core/src/dbless.ts
+++ b/packages/taler-wallet-core/src/dbless.ts
@@ -48,24 +48,20 @@ import {
parsePaytoUri,
UnblindedSignature,
} from "@gnu-taler/taler-util";
-import { TalerCryptoInterface } from "./crypto/cryptoImplementation.js";
-import { DenominationRecord } from "./db.js";
-import {
- BankAccessApi,
- BankApi,
- BankServiceHandle,
-} from "./bank-api-client.js";
import {
HttpRequestLibrary,
readSuccessResponseJsonOrThrow,
} from "@gnu-taler/taler-util/http";
+import { BankAccessApiClient } from "../../taler-util/src/bank-api-client.js";
+import { TalerCryptoInterface } from "./crypto/cryptoImplementation.js";
+import { DenominationRecord } from "./db.js";
+import { isWithdrawableDenom } from "./index.js";
+import { ExchangeInfo } from "./operations/exchanges.js";
+import { assembleRefreshRevealRequest } from "./operations/refresh.js";
import {
getBankStatusUrl,
getBankWithdrawalInfo,
} from "./operations/withdraw.js";
-import { ExchangeInfo } from "./operations/exchanges.js";
-import { assembleRefreshRevealRequest } from "./operations/refresh.js";
-import { isWithdrawableDenom, WalletConfig } from "./index.js";
const logger = new Logger("dbless.ts");
@@ -121,14 +117,10 @@ export async function topupReserveWithDemobank(
args: TopupReserveWithDemobankArgs,
) {
const { http, bankAccessApiBaseUrl, amount, exchangeInfo, reservePub } = args;
- const bankHandle: BankServiceHandle = {
- bankAccessApiBaseUrl: bankAccessApiBaseUrl,
- http,
- };
- const bankUser = await BankApi.createRandomBankUser(bankHandle);
- const wopi = await BankAccessApi.createWithdrawalOperation(
- bankHandle,
- bankUser,
+ const bankClient = new BankAccessApiClient(bankAccessApiBaseUrl);
+ const bankUser = await bankClient.createRandomBankUser();
+ const wopi = await bankClient.createWithdrawalOperation(
+ bankUser.username,
amount,
);
const bankInfo = await getBankWithdrawalInfo(http, wopi.taler_withdraw_uri);
@@ -149,7 +141,7 @@ export async function topupReserveWithDemobank(
httpResp,
codecForBankWithdrawalOperationPostResponse(),
);
- await BankApi.confirmWithdrawalOperation(bankHandle, bankUser, wopi);
+ await bankClient.confirmWithdrawalOperation(bankUser.username, wopi);
}
export async function withdrawCoin(args: {
@@ -167,11 +159,7 @@ export async function withdrawCoin(args: {
reservePriv: reserveKeyPair.reservePriv,
reservePub: reserveKeyPair.reservePub,
secretSeed: encodeCrock(getRandomBytes(32)),
- value: {
- currency: denom.currency,
- fraction: denom.amountFrac,
- value: denom.amountVal,
- },
+ value: Amounts.parseOrThrow(denom.value),
});
const reqBody: ExchangeWithdrawRequest = {
@@ -219,11 +207,7 @@ export function findDenomOrThrow(
): DenominationRecord {
const denomselAllowLate = options.denomselAllowLate ?? false;
for (const d of exchangeInfo.keys.currentDenominations) {
- const value: AmountJson = {
- currency: d.currency,
- fraction: d.amountFrac,
- value: d.amountVal,
- };
+ const value: AmountJson = Amounts.parseOrThrow(d.value);
if (
Amounts.cmp(value, amount) === 0 &&
isWithdrawableDenom(d, denomselAllowLate)
@@ -311,11 +295,7 @@ export async function refreshCoin(req: {
denomPub: x.denomPub,
denomPubHash: x.denomPubHash,
feeWithdraw: x.fees.feeWithdraw,
- value: Amounts.stringify({
- currency: x.currency,
- fraction: x.amountFrac,
- value: x.amountVal,
- }),
+ value: x.value,
})),
meltCoinMaxAge: oldCoin.maxAge,
});
diff --git a/packages/taler-wallet-core/src/host-impl.node.ts b/packages/taler-wallet-core/src/host-impl.node.ts
index 0b6539306..a6dae58a1 100644
--- a/packages/taler-wallet-core/src/host-impl.node.ts
+++ b/packages/taler-wallet-core/src/host-impl.node.ts
@@ -52,7 +52,6 @@ interface MakeDbResult {
async function makeFileDb(
args: DefaultNodeWalletArgs = {},
): Promise<MakeDbResult> {
- BridgeIDBFactory.enableTracing = false;
const myBackend = new MemoryBackend();
myBackend.enableTracing = false;
const storagePath = args.persistentStoragePath;
@@ -135,13 +134,16 @@ export async function createNativeWalletHost2(
} else {
myHttpLib = createPlatformHttpLib({
enableThrottling: true,
- allowHttp: args.config?.features?.allowHttp,
+ requireTls: !args.config?.features?.allowHttp,
});
}
let dbResp: MakeDbResult;
- if (args.persistentStoragePath &&args.persistentStoragePath.endsWith(".json")) {
+ if (
+ args.persistentStoragePath &&
+ args.persistentStoragePath.endsWith(".json")
+ ) {
logger.info("using legacy file-based DB backend");
dbResp = await makeFileDb(args);
} else {
diff --git a/packages/taler-wallet-core/src/host-impl.qtart.ts b/packages/taler-wallet-core/src/host-impl.qtart.ts
index 81dbe0acd..85f8df6e5 100644
--- a/packages/taler-wallet-core/src/host-impl.qtart.ts
+++ b/packages/taler-wallet-core/src/host-impl.qtart.ts
@@ -188,7 +188,7 @@ export async function createNativeWalletHost2(
} else {
myHttpLib = createPlatformHttpLib({
enableThrottling: true,
- allowHttp: args.config?.features?.allowHttp,
+ requireTls: !args.config?.features?.allowHttp,
});
}
diff --git a/packages/taler-wallet-core/src/index.ts b/packages/taler-wallet-core/src/index.ts
index d64f7d5e6..643d65620 100644
--- a/packages/taler-wallet-core/src/index.ts
+++ b/packages/taler-wallet-core/src/index.ts
@@ -44,8 +44,6 @@ export * from "./operations/backup/index.js";
export * from "./operations/exchanges.js";
-export * from "./bank-api-client.js";
-
export * from "./operations/withdraw.js";
export * from "./operations/refresh.js";
diff --git a/packages/taler-wallet-core/src/internal-wallet-state.ts b/packages/taler-wallet-core/src/internal-wallet-state.ts
index a189c9cb3..20f8a7511 100644
--- a/packages/taler-wallet-core/src/internal-wallet-state.ts
+++ b/packages/taler-wallet-core/src/internal-wallet-state.ts
@@ -107,10 +107,6 @@ export interface ExchangeOperations {
}>,
exchangeBaseUrl: string,
): Promise<ExchangeDetailsRecord | undefined>;
- getExchangeTrust(
- ws: InternalWalletState,
- exchangeInfo: ExchangeEntryRecord,
- ): Promise<TrustInfo>;
updateExchangeFromUrl(
ws: InternalWalletState,
baseUrl: string,
diff --git a/packages/taler-wallet-core/src/operations/balance.ts b/packages/taler-wallet-core/src/operations/balance.ts
index 0fcab0542..a20ded2af 100644
--- a/packages/taler-wallet-core/src/operations/balance.ts
+++ b/packages/taler-wallet-core/src/operations/balance.ts
@@ -95,14 +95,7 @@ function computeRefreshGroupAvailableAmount(r: RefreshGroupRecord): AmountJson {
return available;
}
for (let i = 0; i < r.oldCoinPubs.length; i++) {
- const session = r.refreshSessionPerCoin[i];
- if (session) {
- // We are always assuming the refresh will succeed, thus we
- // report the output as available balance.
- available = Amounts.add(available, session.amountRefreshOutput).amount;
- } else {
- available = Amounts.add(available, r.estimatedOutputPerCoin[i]).amount;
- }
+ available = Amounts.add(available, r.expectedOutputPerCoin[i]).amount;
}
return available;
}
@@ -140,11 +133,7 @@ export async function getBalancesInsideTransaction(
const b = initBalance(ca.currency);
const count = ca.visibleCoinCount ?? 0;
for (let i = 0; i < count; i++) {
- b.available = Amounts.add(b.available, {
- currency: ca.currency,
- fraction: ca.amountFrac,
- value: ca.amountVal,
- }).amount;
+ b.available = Amounts.add(b.available, ca.value).amount;
}
});
@@ -163,7 +152,7 @@ export async function getBalancesInsideTransaction(
case WithdrawalGroupStatus.AbortedExchange:
case WithdrawalGroupStatus.FailedAbortingBank:
case WithdrawalGroupStatus.FailedBankAborted:
- case WithdrawalGroupStatus.Finished:
+ case WithdrawalGroupStatus.Done:
// Does not count as pendingIncoming
return;
case WithdrawalGroupStatus.PendingReady:
@@ -281,7 +270,7 @@ export async function getAcceptableExchangeBaseUrls(
const acceptableExchangeUrls = new Set<string>();
const depositableExchangeUrls = new Set<string>();
await ws.db
- .mktx((x) => [x.exchanges, x.exchangeDetails, x.auditorTrust])
+ .mktx((x) => [x.exchanges, x.exchangeDetails])
.runReadOnly(async (tx) => {
// FIXME: We should have a DB index to look up all exchanges
// for a particular auditor ...
@@ -415,11 +404,7 @@ export async function getMerchantPaymentBalanceDetails(
if (ca.currency != req.currency) {
return;
}
- const singleCoinAmount: AmountJson = {
- currency: ca.currency,
- fraction: ca.amountFrac,
- value: ca.amountVal,
- };
+ const singleCoinAmount: AmountJson = Amounts.parseOrThrow(ca.value);
const coinAmount: AmountJson = Amounts.mult(
singleCoinAmount,
ca.freshCoinCount,
@@ -537,11 +522,7 @@ export async function getPeerPaymentBalanceDetailsInTx(
) {
return;
}
- const singleCoinAmount: AmountJson = {
- currency: ca.currency,
- fraction: ca.amountFrac,
- value: ca.amountVal,
- };
+ const singleCoinAmount: AmountJson = Amounts.parseOrThrow(ca.value);
const coinAmount: AmountJson = Amounts.mult(
singleCoinAmount,
ca.freshCoinCount,
diff --git a/packages/taler-wallet-core/src/operations/common.ts b/packages/taler-wallet-core/src/operations/common.ts
index e96beb5b2..50dd3dc5c 100644
--- a/packages/taler-wallet-core/src/operations/common.ts
+++ b/packages/taler-wallet-core/src/operations/common.ts
@@ -26,7 +26,6 @@ import {
CoinRefreshRequest,
CoinStatus,
Duration,
- ErrorInfoSummary,
ExchangeEntryStatus,
ExchangeListItem,
ExchangeTosStatus,
@@ -34,9 +33,11 @@ import {
getErrorDetailFromException,
j2s,
Logger,
+ makeErrorDetail,
NotificationType,
OperationErrorInfo,
RefreshReason,
+ TalerError,
TalerErrorCode,
TalerErrorDetail,
TombstoneIdStr,
@@ -44,32 +45,31 @@ import {
TransactionType,
WalletNotification,
} from "@gnu-taler/taler-util";
+import { CryptoApiStoppedError } from "../crypto/workers/crypto-dispatcher.js";
import {
- WalletStoresV1,
+ BackupProviderRecord,
CoinRecord,
+ DepositGroupRecord,
ExchangeDetailsRecord,
+ ExchangeEntryDbRecordStatus,
+ ExchangeEntryDbUpdateStatus,
ExchangeEntryRecord,
- BackupProviderRecord,
- DepositGroupRecord,
+ PeerPullCreditRecord,
PeerPullPaymentIncomingRecord,
- PeerPullPaymentInitiationRecord,
+ PeerPushDebitRecord,
PeerPushPaymentIncomingRecord,
- PeerPushPaymentInitiationRecord,
PurchaseRecord,
RecoupGroupRecord,
RefreshGroupRecord,
RewardRecord,
+ WalletStoresV1,
WithdrawalGroupRecord,
- ExchangeEntryDbUpdateStatus,
- ExchangeEntryDbRecordStatus,
} from "../db.js";
-import { makeErrorDetail, TalerError } from "@gnu-taler/taler-util";
import { InternalWalletState } from "../internal-wallet-state.js";
-import { checkDbInvariant, checkLogicInvariant } from "../util/invariants.js";
-import { GetReadOnlyAccess, GetReadWriteAccess } from "../util/query.js";
-import { CryptoApiStoppedError } from "../crypto/workers/crypto-dispatcher.js";
import { PendingTaskType, TaskId } from "../pending-types.js";
import { assertUnreachable } from "../util/assertUnreachable.js";
+import { checkDbInvariant, checkLogicInvariant } from "../util/invariants.js";
+import { GetReadOnlyAccess, GetReadWriteAccess } from "../util/query.js";
import { constructTransactionIdentifier } from "./transactions.js";
const logger = new Logger("operations/common.ts");
@@ -144,8 +144,7 @@ export async function makeCoinAvailable(
if (!car) {
car = {
maxAge: ageRestriction,
- amountFrac: denom.amountFrac,
- amountVal: denom.amountVal,
+ value: denom.value,
currency: denom.currency,
denomPubHash: denom.denomPubHash,
exchangeBaseUrl: denom.exchangeBaseUrl,
@@ -271,7 +270,7 @@ function convertTaskToTransactionId(
case PendingTaskType.PeerPullDebit:
return constructTransactionIdentifier({
tag: TransactionType.PeerPullDebit,
- peerPullPaymentIncomingId: parsedTaskId.peerPullPaymentIncomingId,
+ peerPullDebitId: parsedTaskId.peerPullDebitId,
});
// FIXME: This doesn't distinguish internal-withdrawal.
// Maybe we should have a different task type for that as well?
@@ -284,7 +283,7 @@ function convertTaskToTransactionId(
case PendingTaskType.PeerPushCredit:
return constructTransactionIdentifier({
tag: TransactionType.PeerPushCredit,
- peerPushPaymentIncomingId: parsedTaskId.peerPushPaymentIncomingId,
+ peerPushCreditId: parsedTaskId.peerPushCreditId,
});
case PendingTaskType.Deposit:
return constructTransactionIdentifier({
@@ -836,9 +835,9 @@ export type ParsedTaskIdentifier =
| { tag: PendingTaskType.Deposit; depositGroupId: string }
| { tag: PendingTaskType.ExchangeCheckRefresh; exchangeBaseUrl: string }
| { tag: PendingTaskType.ExchangeUpdate; exchangeBaseUrl: string }
- | { tag: PendingTaskType.PeerPullDebit; peerPullPaymentIncomingId: string }
+ | { tag: PendingTaskType.PeerPullDebit; peerPullDebitId: string }
| { tag: PendingTaskType.PeerPullCredit; pursePub: string }
- | { tag: PendingTaskType.PeerPushCredit; peerPushPaymentIncomingId: string }
+ | { tag: PendingTaskType.PeerPushCredit; peerPushCreditId: string }
| { tag: PendingTaskType.PeerPushDebit; pursePub: string }
| { tag: PendingTaskType.Purchase; proposalId: string }
| { tag: PendingTaskType.Recoup; recoupGroupId: string }
@@ -865,9 +864,9 @@ export function parseTaskIdentifier(x: string): ParsedTaskIdentifier {
case PendingTaskType.PeerPullCredit:
return { tag: type, pursePub: rest[0] };
case PendingTaskType.PeerPullDebit:
- return { tag: type, peerPullPaymentIncomingId: rest[0] };
+ return { tag: type, peerPullDebitId: rest[0] };
case PendingTaskType.PeerPushCredit:
- return { tag: type, peerPushPaymentIncomingId: rest[0] };
+ return { tag: type, peerPushCreditId: rest[0] };
case PendingTaskType.PeerPushDebit:
return { tag: type, pursePub: rest[0] };
case PendingTaskType.Purchase:
@@ -896,9 +895,9 @@ export function constructTaskIdentifier(p: ParsedTaskIdentifier): TaskId {
case PendingTaskType.ExchangeUpdate:
return `${p.tag}:${p.exchangeBaseUrl}` as TaskId;
case PendingTaskType.PeerPullDebit:
- return `${p.tag}:${p.peerPullPaymentIncomingId}` as TaskId;
+ return `${p.tag}:${p.peerPullDebitId}` as TaskId;
case PendingTaskType.PeerPushCredit:
- return `${p.tag}:${p.peerPushPaymentIncomingId}` as TaskId;
+ return `${p.tag}:${p.peerPushCreditId}` as TaskId;
case PendingTaskType.PeerPullCredit:
return `${p.tag}:${p.pursePub}` as TaskId;
case PendingTaskType.PeerPushDebit:
@@ -950,23 +949,23 @@ export namespace TaskIdentifiers {
return `${PendingTaskType.Backup}:${backupRecord.baseUrl}` as TaskId;
}
export function forPeerPushPaymentInitiation(
- ppi: PeerPushPaymentInitiationRecord,
+ ppi: PeerPushDebitRecord,
): TaskId {
return `${PendingTaskType.PeerPushDebit}:${ppi.pursePub}` as TaskId;
}
export function forPeerPullPaymentInitiation(
- ppi: PeerPullPaymentInitiationRecord,
+ ppi: PeerPullCreditRecord,
): TaskId {
return `${PendingTaskType.PeerPullCredit}:${ppi.pursePub}` as TaskId;
}
export function forPeerPullPaymentDebit(
ppi: PeerPullPaymentIncomingRecord,
): TaskId {
- return `${PendingTaskType.PeerPullDebit}:${ppi.peerPullPaymentIncomingId}` as TaskId;
+ return `${PendingTaskType.PeerPullDebit}:${ppi.peerPullDebitId}` as TaskId;
}
export function forPeerPushCredit(
ppi: PeerPushPaymentIncomingRecord,
): TaskId {
- return `${PendingTaskType.PeerPushCredit}:${ppi.peerPushPaymentIncomingId}` as TaskId;
+ return `${PendingTaskType.PeerPushCredit}:${ppi.peerPushCreditId}` as TaskId;
}
}
diff --git a/packages/taler-wallet-core/src/operations/deposits.ts b/packages/taler-wallet-core/src/operations/deposits.ts
index a8ec859cf..8ea792d91 100644
--- a/packages/taler-wallet-core/src/operations/deposits.ts
+++ b/packages/taler-wallet-core/src/operations/deposits.ts
@@ -510,10 +510,10 @@ async function refundDepositGroup(
ws: InternalWalletState,
depositGroup: DepositGroupRecord,
): Promise<TaskRunResult> {
- const newTxPerCoin = [...depositGroup.transactionPerCoin];
- logger.info(`status per coin: ${j2s(depositGroup.transactionPerCoin)}`);
- for (let i = 0; i < depositGroup.transactionPerCoin.length; i++) {
- const st = depositGroup.transactionPerCoin[i];
+ const newTxPerCoin = [...depositGroup.statusPerCoin];
+ logger.info(`status per coin: ${j2s(depositGroup.statusPerCoin)}`);
+ for (let i = 0; i < depositGroup.statusPerCoin.length; i++) {
+ const st = depositGroup.statusPerCoin[i];
switch (st) {
case DepositElementStatus.RefundFailed:
case DepositElementStatus.RefundSuccess:
@@ -593,7 +593,7 @@ async function refundDepositGroup(
if (!newDg) {
return;
}
- newDg.transactionPerCoin = newTxPerCoin;
+ newDg.statusPerCoin = newTxPerCoin;
const refreshCoins: CoinRefreshRequest[] = [];
for (let i = 0; i < newTxPerCoin.length; i++) {
refreshCoins.push({
@@ -766,7 +766,7 @@ async function processDepositGroupPendingTrack(
cancellationToken?: CancellationToken,
): Promise<TaskRunResult> {
const { depositGroupId } = depositGroup;
- for (let i = 0; i < depositGroup.depositedPerCoin.length; i++) {
+ for (let i = 0; i < depositGroup.statusPerCoin.length; i++) {
const coinPub = depositGroup.payCoinSelection.coinPubs[i];
// FIXME: Make the URL part of the coin selection?
const exchangeBaseUrl = await ws.db
@@ -785,7 +785,7 @@ async function processDepositGroupPendingTrack(
}
| undefined;
- if (depositGroup.transactionPerCoin[i] !== DepositElementStatus.Wired) {
+ if (depositGroup.statusPerCoin[i] !== DepositElementStatus.Wired) {
const track = await trackDeposit(
ws,
depositGroup,
@@ -810,7 +810,7 @@ async function processDepositGroupPendingTrack(
exchangeBaseUrl,
);
} else {
- updatedTxStatus = DepositElementStatus.Accepted;
+ updatedTxStatus = DepositElementStatus.Tracking;
}
} else if (track.type === "wired") {
updatedTxStatus = DepositElementStatus.Wired;
@@ -840,7 +840,7 @@ async function processDepositGroupPendingTrack(
id: track.exchange_sig,
};
} else {
- updatedTxStatus = DepositElementStatus.Unknown;
+ updatedTxStatus = DepositElementStatus.DepositPending;
}
}
@@ -853,7 +853,7 @@ async function processDepositGroupPendingTrack(
return;
}
if (updatedTxStatus !== undefined) {
- dg.transactionPerCoin[i] = updatedTxStatus;
+ dg.statusPerCoin[i] = updatedTxStatus;
}
if (newWiredCoin) {
/**
@@ -885,8 +885,8 @@ async function processDepositGroupPendingTrack(
return undefined;
}
const oldTxState = computeDepositTransactionStatus(dg);
- for (let i = 0; i < depositGroup.depositedPerCoin.length; i++) {
- if (depositGroup.transactionPerCoin[i] !== DepositElementStatus.Wired) {
+ for (let i = 0; i < depositGroup.statusPerCoin.length; i++) {
+ if (depositGroup.statusPerCoin[i] !== DepositElementStatus.Wired) {
allWired = false;
break;
}
@@ -943,7 +943,7 @@ async function processDepositGroupPendingDeposit(
for (let i = 0; i < depositPermissions.length; i++) {
const perm = depositPermissions[i];
- if (depositGroup.depositedPerCoin[i]) {
+ if (depositGroup.statusPerCoin[i] !== DepositElementStatus.DepositPending) {
continue;
}
@@ -980,8 +980,12 @@ async function processDepositGroupPendingDeposit(
if (!dg) {
return;
}
- dg.depositedPerCoin[i] = true;
- await tx.depositGroups.put(dg);
+ const coinStatus = dg.statusPerCoin[i];
+ switch (coinStatus) {
+ case DepositElementStatus.DepositPending:
+ dg.statusPerCoin[i] = DepositElementStatus.Tracking;
+ await tx.depositGroups.put(dg);
+ }
});
}
@@ -1373,16 +1377,15 @@ export async function createDepositGroup(
noncePub: noncePair.pub,
timestampCreated: AbsoluteTime.toPreciseTimestamp(now),
timestampFinished: undefined,
- transactionPerCoin: payCoinSel.coinSel.coinPubs.map(
- () => DepositElementStatus.Unknown,
+ statusPerCoin: payCoinSel.coinSel.coinPubs.map(
+ () => DepositElementStatus.DepositPending,
),
payCoinSelection: payCoinSel.coinSel,
payCoinSelectionUid: encodeCrock(getRandomBytes(32)),
- depositedPerCoin: payCoinSel.coinSel.coinPubs.map(() => false),
merchantPriv: merchantPair.priv,
merchantPub: merchantPair.pub,
totalPayCost: Amounts.stringify(totalDepositCost),
- effectiveDepositAmount: Amounts.stringify(
+ counterpartyEffectiveDepositAmount: Amounts.stringify(
counterpartyEffectiveDepositAmount,
),
wire: {
@@ -1536,7 +1539,7 @@ async function getTotalFeesForDepositAmount(
.iter(coin.exchangeBaseUrl)
.filter((x) =>
Amounts.isSameCurrency(
- DenominationRecord.getValue(x),
+ x.value,
pcs.coinContributions[i],
),
);
diff --git a/packages/taler-wallet-core/src/operations/exchanges.ts b/packages/taler-wallet-core/src/operations/exchanges.ts
index 311a71a6e..43a08ed3b 100644
--- a/packages/taler-wallet-core/src/operations/exchanges.ts
+++ b/packages/taler-wallet-core/src/operations/exchanges.ts
@@ -158,35 +158,6 @@ getExchangeDetails.makeContext = (db: DbAccess<typeof WalletStoresV1>) =>
db.mktx((x) => [x.exchanges, x.exchangeDetails]);
/**
- * Update the database based on the download of the terms of service.
- */
-export async function updateExchangeTermsOfService(
- ws: InternalWalletState,
- exchangeBaseUrl: string,
- tos: ExchangeTosDownloadResult,
-): Promise<void> {
- await ws.db
- .mktx((x) => [x.exchanges, x.exchangeTos, x.exchangeDetails])
- .runReadWrite(async (tx) => {
- const d = await getExchangeDetails(tx, exchangeBaseUrl);
- let tosRecord = await tx.exchangeTos.get([exchangeBaseUrl, tos.tosEtag]);
- if (!tosRecord) {
- tosRecord = {
- etag: tos.tosEtag,
- exchangeBaseUrl,
- termsOfServiceContentType: tos.tosContentType,
- termsOfServiceText: tos.tosText,
- };
- await tx.exchangeTos.put(tosRecord);
- }
- if (d) {
- d.tosCurrentEtag = tos.tosEtag;
- await tx.exchangeDetails.put(d);
- }
- });
-}
-
-/**
* Mark a ToS version as accepted by the user.
*
* @param etag version of the ToS to accept, or current ToS version of not given
@@ -472,8 +443,7 @@ async function downloadExchangeKeysInfo(
exchangeMasterPub: exchangeKeysJsonUnchecked.master_public_key,
isOffered: true,
isRevoked: false,
- amountFrac: value.fraction,
- amountVal: value.value,
+ value: Amounts.stringify(value),
currency: value.currency,
stampExpireDeposit: denomIn.stamp_expire_deposit,
stampExpireLegal: denomIn.stamp_expire_legal,
@@ -740,7 +710,6 @@ export async function updateExchangeFromUrlHandler(
const updated = await ws.db
.mktx((x) => [
x.exchanges,
- x.exchangeTos,
x.exchangeDetails,
x.exchangeSignKeys,
x.denominations,
@@ -801,21 +770,6 @@ export async function updateExchangeFromUrlHandler(
const drRowId = await tx.exchangeDetails.put(newDetails);
checkDbInvariant(typeof drRowId.key === "number");
- let tosRecord = await tx.exchangeTos.get([
- exchangeBaseUrl,
- tosDownload.tosEtag,
- ]);
-
- if (!tosRecord || tosRecord.etag !== existingTosAccepted?.etag) {
- tosRecord = {
- etag: tosDownload.tosEtag,
- exchangeBaseUrl,
- termsOfServiceContentType: tosDownload.tosContentType,
- termsOfServiceText: tosDownload.tosText,
- };
- await tx.exchangeTos.put(tosRecord);
- }
-
for (const sk of keysInfo.signingKeys) {
// FIXME: validate signing keys before inserting them
await tx.exchangeSignKeys.put({
@@ -972,54 +926,3 @@ export async function getExchangePaytoUri(
)}`,
);
}
-
-/**
- * Check if and how an exchange is trusted and/or audited.
- */
-export async function getExchangeTrust(
- ws: InternalWalletState,
- exchangeInfo: ExchangeEntryRecord,
-): Promise<TrustInfo> {
- let isTrusted = false;
- let isAudited = false;
-
- return await ws.db
- .mktx((x) => [
- x.exchanges,
- x.exchangeDetails,
- x.exchangeTrust,
- x.auditorTrust,
- ])
- .runReadOnly(async (tx) => {
- const exchangeDetails = await getExchangeDetails(
- tx,
- exchangeInfo.baseUrl,
- );
-
- if (!exchangeDetails) {
- throw Error(`exchange ${exchangeInfo.baseUrl} details not available`);
- }
- const exchangeTrustRecord =
- await tx.exchangeTrust.indexes.byExchangeMasterPub.get(
- exchangeDetails.masterPublicKey,
- );
- if (
- exchangeTrustRecord &&
- exchangeTrustRecord.uids.length > 0 &&
- exchangeTrustRecord.currency === exchangeDetails.currency
- ) {
- isTrusted = true;
- }
-
- for (const auditor of exchangeDetails.auditors) {
- const auditorTrustRecord =
- await tx.auditorTrust.indexes.byAuditorPub.get(auditor.auditor_pub);
- if (auditorTrustRecord && auditorTrustRecord.uids.length > 0) {
- isAudited = true;
- break;
- }
- }
-
- return { isTrusted, isAudited };
- });
-}
diff --git a/packages/taler-wallet-core/src/operations/pay-merchant.ts b/packages/taler-wallet-core/src/operations/pay-merchant.ts
index 2580c97f5..57367bb20 100644
--- a/packages/taler-wallet-core/src/operations/pay-merchant.ts
+++ b/packages/taler-wallet-core/src/operations/pay-merchant.ts
@@ -174,12 +174,12 @@ export async function getTotalPaymentCost(
.iter(coin.exchangeBaseUrl)
.filter((x) =>
Amounts.isSameCurrency(
- DenominationRecord.getValue(x),
+ x.value,
pcs.coinContributions[i],
),
);
const amountLeft = Amounts.sub(
- DenominationRecord.getValue(denom),
+ denom.value,
pcs.coinContributions[i],
).amount;
const refreshCost = getTotalRefreshCost(
@@ -317,11 +317,8 @@ export function extractContractData(
wireInfoHash: parsedContractTerms.h_wire,
maxDepositFee: Amounts.stringify(parsedContractTerms.max_fee),
merchant: parsedContractTerms.merchant,
- products: parsedContractTerms.products,
summaryI18n: parsedContractTerms.summary_i18n,
minimumAge: parsedContractTerms.minimum_age,
- deliveryDate: parsedContractTerms.delivery_date,
- deliveryLocation: parsedContractTerms.delivery_location,
};
}
@@ -541,7 +538,7 @@ async function processDownloadProposal(
// if original order is refunded.
if (otherPurchase && otherPurchase.refundAmountAwaiting === undefined) {
logger.warn("repurchase detected");
- p.purchaseStatus = PurchaseStatus.RepurchaseDetected;
+ p.purchaseStatus = PurchaseStatus.DoneRepurchaseDetected;
p.repurchaseProposalId = otherPurchase.proposalId;
await tx.purchases.put(p);
} else {
@@ -974,7 +971,7 @@ export async function checkPaymentByProposalId(
if (!proposal) {
throw Error(`could not get proposal ${proposalId}`);
}
- if (proposal.purchaseStatus === PurchaseStatus.RepurchaseDetected) {
+ if (proposal.purchaseStatus === PurchaseStatus.DoneRepurchaseDetected) {
const existingProposalId = proposal.repurchaseProposalId;
if (existingProposalId) {
logger.trace("using existing purchase for same product");
@@ -1527,7 +1524,7 @@ export async function processPurchase(
return processPurchaseDialogShared(ws, purchase);
case PurchaseStatus.FailedClaim:
case PurchaseStatus.Done:
- case PurchaseStatus.RepurchaseDetected:
+ case PurchaseStatus.DoneRepurchaseDetected:
case PurchaseStatus.DialogProposed:
case PurchaseStatus.AbortedProposalRefused:
case PurchaseStatus.AbortedIncompletePayment:
@@ -2099,7 +2096,7 @@ export function computePayMerchantTransactionState(
return {
major: TransactionMajorState.Done,
};
- case PurchaseStatus.RepurchaseDetected:
+ case PurchaseStatus.DoneRepurchaseDetected:
return {
major: TransactionMajorState.Failed,
minor: TransactionMinorState.Repurchase,
@@ -2176,7 +2173,7 @@ export function computePayMerchantTransactionActions(
return [TransactionAction.Delete];
case PurchaseStatus.Done:
return [TransactionAction.Delete];
- case PurchaseStatus.RepurchaseDetected:
+ case PurchaseStatus.DoneRepurchaseDetected:
return [TransactionAction.Delete];
case PurchaseStatus.AbortedIncompletePayment:
return [TransactionAction.Delete];
diff --git a/packages/taler-wallet-core/src/operations/pay-peer-common.ts b/packages/taler-wallet-core/src/operations/pay-peer-common.ts
index 9e05e43d8..6d425289d 100644
--- a/packages/taler-wallet-core/src/operations/pay-peer-common.ts
+++ b/packages/taler-wallet-core/src/operations/pay-peer-common.ts
@@ -108,16 +108,8 @@ export async function getTotalPeerPaymentCost(
}
const allDenoms = await tx.denominations.indexes.byExchangeBaseUrl
.iter(coin.exchangeBaseUrl)
- .filter((x) =>
- Amounts.isSameCurrency(
- DenominationRecord.getValue(x),
- pcs[i].contribution,
- ),
- );
- const amountLeft = Amounts.sub(
- DenominationRecord.getValue(denom),
- pcs[i].contribution,
- ).amount;
+ .filter((x) => Amounts.isSameCurrency(x.value, pcs[i].contribution));
+ const amountLeft = Amounts.sub(denom.value, pcs[i].contribution).amount;
const refreshCost = getTotalRefreshCost(
allDenoms,
DenominationRecord.toDenomInfo(denom),
diff --git a/packages/taler-wallet-core/src/operations/pay-peer-pull-credit.ts b/packages/taler-wallet-core/src/operations/pay-peer-pull-credit.ts
index 29c0fff9e..0355eb152 100644
--- a/packages/taler-wallet-core/src/operations/pay-peer-pull-credit.ts
+++ b/packages/taler-wallet-core/src/operations/pay-peer-pull-credit.ts
@@ -27,6 +27,7 @@ import {
InitiatePeerPullCreditResponse,
Logger,
NotificationType,
+ PeerContractTerms,
TalerErrorCode,
TalerPreciseTimestamp,
TalerProtocolTimestamp,
@@ -55,8 +56,8 @@ import {
import {
KycPendingInfo,
KycUserType,
- PeerPullPaymentInitiationRecord,
- PeerPullPaymentInitiationStatus,
+ PeerPullCreditRecord,
+ PeerPullPaymentCreditStatus,
WithdrawalGroupStatus,
WithdrawalRecordType,
updateExchangeFromUrl,
@@ -90,7 +91,7 @@ const logger = new Logger("pay-peer-pull-credit.ts");
async function queryPurseForPeerPullCredit(
ws: InternalWalletState,
- pullIni: PeerPullPaymentInitiationRecord,
+ pullIni: PeerPullCreditRecord,
cancellationToken: CancellationToken,
): Promise<LongpollResult> {
const purseDepositUrl = new URL(
@@ -143,7 +144,6 @@ async function queryPurseForPeerPullCredit(
amount: Amounts.parseOrThrow(pullIni.amount),
wgInfo: {
withdrawalType: WithdrawalRecordType.PeerPullCredit,
- contractTerms: pullIni.contractTerms,
contractPriv: pullIni.contractPriv,
},
forcedWithdrawalGroupId: pullIni.withdrawalGroupId,
@@ -159,18 +159,18 @@ async function queryPurseForPeerPullCredit(
pursePub: pullIni.pursePub,
});
const transitionInfo = await ws.db
- .mktx((x) => [x.peerPullPaymentInitiations])
+ .mktx((x) => [x.peerPullCredit])
.runReadWrite(async (tx) => {
- const finPi = await tx.peerPullPaymentInitiations.get(pullIni.pursePub);
+ const finPi = await tx.peerPullCredit.get(pullIni.pursePub);
if (!finPi) {
- logger.warn("peerPullPaymentInitiation not found anymore");
+ logger.warn("peerPullCredit not found anymore");
return;
}
const oldTxState = computePeerPullCreditTransactionState(finPi);
- if (finPi.status === PeerPullPaymentInitiationStatus.PendingReady) {
- finPi.status = PeerPullPaymentInitiationStatus.PendingWithdrawing;
+ if (finPi.status === PeerPullPaymentCreditStatus.PendingReady) {
+ finPi.status = PeerPullPaymentCreditStatus.PendingWithdrawing;
}
- await tx.peerPullPaymentInitiations.put(finPi);
+ await tx.peerPullCredit.put(finPi);
const newTxState = computePeerPullCreditTransactionState(finPi);
return { oldTxState, newTxState };
});
@@ -214,22 +214,22 @@ async function longpollKycStatus(
kycStatusRes.status === HttpStatusCode.NoContent
) {
const transitionInfo = await ws.db
- .mktx((x) => [x.peerPullPaymentInitiations])
+ .mktx((x) => [x.peerPullCredit])
.runReadWrite(async (tx) => {
- const peerIni = await tx.peerPullPaymentInitiations.get(pursePub);
+ const peerIni = await tx.peerPullCredit.get(pursePub);
if (!peerIni) {
return;
}
if (
peerIni.status !==
- PeerPullPaymentInitiationStatus.PendingMergeKycRequired
+ PeerPullPaymentCreditStatus.PendingMergeKycRequired
) {
return;
}
const oldTxState = computePeerPullCreditTransactionState(peerIni);
- peerIni.status = PeerPullPaymentInitiationStatus.PendingCreatePurse;
+ peerIni.status = PeerPullPaymentCreditStatus.PendingCreatePurse;
const newTxState = computePeerPullCreditTransactionState(peerIni);
- await tx.peerPullPaymentInitiations.put(peerIni);
+ await tx.peerPullCredit.put(peerIni);
return { oldTxState, newTxState };
});
notifyTransition(ws, transactionId, transitionInfo);
@@ -250,7 +250,7 @@ async function longpollKycStatus(
async function processPeerPullCreditAbortingDeletePurse(
ws: InternalWalletState,
- peerPullIni: PeerPullPaymentInitiationRecord,
+ peerPullIni: PeerPullCreditRecord,
): Promise<TaskRunResult> {
const { pursePub, pursePriv } = peerPullIni;
const transactionId = constructTransactionIdentifier({
@@ -272,24 +272,22 @@ async function processPeerPullCreditAbortingDeletePurse(
const transitionInfo = await ws.db
.mktx((x) => [
- x.peerPullPaymentInitiations,
+ x.peerPullCredit,
x.refreshGroups,
x.denominations,
x.coinAvailability,
x.coins,
])
.runReadWrite(async (tx) => {
- const ppiRec = await tx.peerPullPaymentInitiations.get(pursePub);
+ const ppiRec = await tx.peerPullCredit.get(pursePub);
if (!ppiRec) {
return undefined;
}
- if (
- ppiRec.status !== PeerPullPaymentInitiationStatus.AbortingDeletePurse
- ) {
+ if (ppiRec.status !== PeerPullPaymentCreditStatus.AbortingDeletePurse) {
return undefined;
}
const oldTxState = computePeerPullCreditTransactionState(ppiRec);
- ppiRec.status = PeerPullPaymentInitiationStatus.Aborted;
+ ppiRec.status = PeerPullPaymentCreditStatus.Aborted;
const newTxState = computePeerPullCreditTransactionState(ppiRec);
return {
oldTxState,
@@ -303,7 +301,7 @@ async function processPeerPullCreditAbortingDeletePurse(
async function handlePeerPullCreditWithdrawing(
ws: InternalWalletState,
- pullIni: PeerPullPaymentInitiationRecord,
+ pullIni: PeerPullCreditRecord,
): Promise<TaskRunResult> {
if (!pullIni.withdrawalGroupId) {
throw Error("invalid db state (withdrawing, but no withdrawal group ID");
@@ -315,14 +313,14 @@ async function handlePeerPullCreditWithdrawing(
const wgId = pullIni.withdrawalGroupId;
let finished: boolean = false;
const transitionInfo = await ws.db
- .mktx((x) => [x.peerPullPaymentInitiations, x.withdrawalGroups])
+ .mktx((x) => [x.peerPullCredit, x.withdrawalGroups])
.runReadWrite(async (tx) => {
- const ppi = await tx.peerPullPaymentInitiations.get(pullIni.pursePub);
+ const ppi = await tx.peerPullCredit.get(pullIni.pursePub);
if (!ppi) {
finished = true;
return;
}
- if (ppi.status !== PeerPullPaymentInitiationStatus.PendingWithdrawing) {
+ if (ppi.status !== PeerPullPaymentCreditStatus.PendingWithdrawing) {
finished = true;
return;
}
@@ -333,13 +331,13 @@ async function handlePeerPullCreditWithdrawing(
return undefined;
}
switch (wg.status) {
- case WithdrawalGroupStatus.Finished:
+ case WithdrawalGroupStatus.Done:
finished = true;
- ppi.status = PeerPullPaymentInitiationStatus.Done;
+ ppi.status = PeerPullPaymentCreditStatus.Done;
break;
// FIXME: Also handle other final states!
}
- await tx.peerPullPaymentInitiations.put(ppi);
+ await tx.peerPullCredit.put(ppi);
const newTxState = computePeerPullCreditTransactionState(ppi);
return {
oldTxState,
@@ -357,7 +355,7 @@ async function handlePeerPullCreditWithdrawing(
async function handlePeerPullCreditCreatePurse(
ws: InternalWalletState,
- pullIni: PeerPullPaymentInitiationRecord,
+ pullIni: PeerPullCreditRecord,
): Promise<TaskRunResult> {
const purseFee = Amounts.stringify(Amounts.zeroOfAmount(pullIni.amount));
const pursePub = pullIni.pursePub;
@@ -371,6 +369,18 @@ async function handlePeerPullCreditCreatePurse(
throw Error("merge reserve for peer pull payment not found in database");
}
+ const contractTermsRecord = await ws.db
+ .mktx((x) => [x.contractTerms])
+ .runReadOnly(async (tx) => {
+ return tx.contractTerms.get(pullIni.contractTermsHash);
+ });
+
+ if (!contractTermsRecord) {
+ throw Error("contract terms for peer pull payment not found in database");
+ }
+
+ const contractTerms: PeerContractTerms = contractTermsRecord.contractTermsRaw;
+
const reservePayto = talerPaytoFromExchangeReserve(
pullIni.exchangeBaseUrl,
mergeReserve.reservePub,
@@ -379,19 +389,19 @@ async function handlePeerPullCreditCreatePurse(
const econtractResp = await ws.cryptoApi.encryptContractForDeposit({
contractPriv: pullIni.contractPriv,
contractPub: pullIni.contractPub,
- contractTerms: pullIni.contractTerms,
+ contractTerms: contractTermsRecord,
pursePriv: pullIni.pursePriv,
pursePub: pullIni.pursePub,
nonce: pullIni.contractEncNonce,
});
- const purseExpiration = pullIni.contractTerms.purse_expiration;
+ const purseExpiration = contractTerms.purse_expiration;
const sigRes = await ws.cryptoApi.signReservePurseCreate({
contractTermsHash: pullIni.contractTermsHash,
flags: WalletAccountMergeFlags.CreateWithPurseFee,
mergePriv: pullIni.mergePriv,
mergeTimestamp: TalerPreciseTimestamp.round(pullIni.mergeTimestamp),
- purseAmount: pullIni.contractTerms.amount,
+ purseAmount: pullIni.amount,
purseExpiration: purseExpiration,
purseFee: purseFee,
pursePriv: pullIni.pursePriv,
@@ -410,7 +420,7 @@ async function handlePeerPullCreditCreatePurse(
purse_fee: purseFee,
purse_pub: pullIni.pursePub,
purse_sig: sigRes.purseSig,
- purse_value: pullIni.contractTerms.amount,
+ purse_value: pullIni.amount,
reserve_sig: sigRes.accountSig,
econtract: econtractResp.econtract,
};
@@ -444,15 +454,15 @@ async function handlePeerPullCreditCreatePurse(
});
const transitionInfo = await ws.db
- .mktx((x) => [x.peerPullPaymentInitiations])
+ .mktx((x) => [x.peerPullCredit])
.runReadWrite(async (tx) => {
- const pi2 = await tx.peerPullPaymentInitiations.get(pursePub);
+ const pi2 = await tx.peerPullCredit.get(pursePub);
if (!pi2) {
return;
}
const oldTxState = computePeerPullCreditTransactionState(pi2);
- pi2.status = PeerPullPaymentInitiationStatus.PendingReady;
- await tx.peerPullPaymentInitiations.put(pi2);
+ pi2.status = PeerPullPaymentCreditStatus.PendingReady;
+ await tx.peerPullCredit.put(pi2);
const newTxState = computePeerPullCreditTransactionState(pi2);
return { oldTxState, newTxState };
});
@@ -466,9 +476,9 @@ export async function processPeerPullCredit(
pursePub: string,
): Promise<TaskRunResult> {
const pullIni = await ws.db
- .mktx((x) => [x.peerPullPaymentInitiations])
+ .mktx((x) => [x.peerPullCredit])
.runReadOnly(async (tx) => {
- return tx.peerPullPaymentInitiations.get(pursePub);
+ return tx.peerPullCredit.get(pursePub);
});
if (!pullIni) {
throw Error("peer pull payment initiation not found in database");
@@ -490,10 +500,10 @@ export async function processPeerPullCredit(
logger.trace(`processing ${retryTag}, status=${pullIni.status}`);
switch (pullIni.status) {
- case PeerPullPaymentInitiationStatus.Done: {
+ case PeerPullPaymentCreditStatus.Done: {
return TaskRunResult.finished();
}
- case PeerPullPaymentInitiationStatus.PendingReady:
+ case PeerPullPaymentCreditStatus.PendingReady:
runLongpollAsync(ws, retryTag, async (cancellationToken) =>
queryPurseForPeerPullCredit(ws, pullIni, cancellationToken),
);
@@ -503,7 +513,7 @@ export async function processPeerPullCredit(
return {
type: TaskRunResultType.Longpoll,
};
- case PeerPullPaymentInitiationStatus.PendingMergeKycRequired: {
+ case PeerPullPaymentCreditStatus.PendingMergeKycRequired: {
if (!pullIni.kycInfo) {
throw Error("invalid state, kycInfo required");
}
@@ -515,19 +525,19 @@ export async function processPeerPullCredit(
"individual",
);
}
- case PeerPullPaymentInitiationStatus.PendingCreatePurse:
+ case PeerPullPaymentCreditStatus.PendingCreatePurse:
return handlePeerPullCreditCreatePurse(ws, pullIni);
- case PeerPullPaymentInitiationStatus.AbortingDeletePurse:
+ case PeerPullPaymentCreditStatus.AbortingDeletePurse:
return await processPeerPullCreditAbortingDeletePurse(ws, pullIni);
- case PeerPullPaymentInitiationStatus.PendingWithdrawing:
+ case PeerPullPaymentCreditStatus.PendingWithdrawing:
return handlePeerPullCreditWithdrawing(ws, pullIni);
- case PeerPullPaymentInitiationStatus.Aborted:
- case PeerPullPaymentInitiationStatus.Failed:
- case PeerPullPaymentInitiationStatus.SuspendedAbortingDeletePurse:
- case PeerPullPaymentInitiationStatus.SuspendedCreatePurse:
- case PeerPullPaymentInitiationStatus.SuspendedMergeKycRequired:
- case PeerPullPaymentInitiationStatus.SuspendedReady:
- case PeerPullPaymentInitiationStatus.SuspendedWithdrawing:
+ case PeerPullPaymentCreditStatus.Aborted:
+ case PeerPullPaymentCreditStatus.Failed:
+ case PeerPullPaymentCreditStatus.SuspendedAbortingDeletePurse:
+ case PeerPullPaymentCreditStatus.SuspendedCreatePurse:
+ case PeerPullPaymentCreditStatus.SuspendedMergeKycRequired:
+ case PeerPullPaymentCreditStatus.SuspendedReady:
+ case PeerPullPaymentCreditStatus.SuspendedWithdrawing:
break;
default:
assertUnreachable(pullIni.status);
@@ -538,7 +548,7 @@ export async function processPeerPullCredit(
async function processPeerPullCreditKycRequired(
ws: InternalWalletState,
- peerIni: PeerPullPaymentInitiationRecord,
+ peerIni: PeerPullCreditRecord,
kycPending: WalletKycUuid,
): Promise<TaskRunResult> {
const transactionId = constructTransactionIdentifier({
@@ -570,9 +580,9 @@ async function processPeerPullCreditKycRequired(
const kycStatus = await kycStatusRes.json();
logger.info(`kyc status: ${j2s(kycStatus)}`);
const { transitionInfo, result } = await ws.db
- .mktx((x) => [x.peerPullPaymentInitiations])
+ .mktx((x) => [x.peerPullCredit])
.runReadWrite(async (tx) => {
- const peerInc = await tx.peerPullPaymentInitiations.get(pursePub);
+ const peerInc = await tx.peerPullCredit.get(pursePub);
if (!peerInc) {
return {
transitionInfo: undefined,
@@ -585,10 +595,9 @@ async function processPeerPullCreditKycRequired(
requirementRow: kycPending.requirement_row,
};
peerInc.kycUrl = kycStatus.kyc_url;
- peerInc.status =
- PeerPullPaymentInitiationStatus.PendingMergeKycRequired;
+ peerInc.status = PeerPullPaymentCreditStatus.PendingMergeKycRequired;
const newTxState = computePeerPullCreditTransactionState(peerInc);
- await tx.peerPullPaymentInitiations.put(peerInc);
+ await tx.peerPullCredit.put(peerInc);
// We'll remove this eventually! New clients should rely on the
// kycUrl field of the transaction, not the error code.
const res: TaskRunResult = {
@@ -758,9 +767,9 @@ export async function initiatePeerPullPayment(
);
const transitionInfo = await ws.db
- .mktx((x) => [x.peerPullPaymentInitiations, x.contractTerms])
+ .mktx((x) => [x.peerPullCredit, x.contractTerms])
.runReadWrite(async (tx) => {
- const ppi: PeerPullPaymentInitiationRecord = {
+ const ppi: PeerPullCreditRecord = {
amount: req.partialContractTerms.amount,
contractTermsHash: hContractTerms,
exchangeBaseUrl: exchangeBaseUrl,
@@ -768,8 +777,7 @@ export async function initiatePeerPullPayment(
pursePub: pursePair.pub,
mergePriv: mergePair.priv,
mergePub: mergePair.pub,
- status: PeerPullPaymentInitiationStatus.PendingCreatePurse,
- contractTerms: contractTerms,
+ status: PeerPullPaymentCreditStatus.PendingCreatePurse,
mergeTimestamp,
contractEncNonce,
mergeReserveRowId: mergeReserveRowId,
@@ -778,7 +786,7 @@ export async function initiatePeerPullPayment(
withdrawalGroupId,
estimatedAmountEffective: wi.withdrawalAmountEffective,
};
- await tx.peerPullPaymentInitiations.put(ppi);
+ await tx.peerPullCredit.put(ppi);
const oldTxState: TransactionState = {
major: TransactionMajorState.None,
};
@@ -826,39 +834,38 @@ export async function suspendPeerPullCreditTransaction(
});
stopLongpolling(ws, taskId);
const transitionInfo = await ws.db
- .mktx((x) => [x.peerPullPaymentInitiations])
+ .mktx((x) => [x.peerPullCredit])
.runReadWrite(async (tx) => {
- const pullCreditRec = await tx.peerPullPaymentInitiations.get(pursePub);
+ const pullCreditRec = await tx.peerPullCredit.get(pursePub);
if (!pullCreditRec) {
logger.warn(`peer pull credit ${pursePub} not found`);
return;
}
- let newStatus: PeerPullPaymentInitiationStatus | undefined = undefined;
+ let newStatus: PeerPullPaymentCreditStatus | undefined = undefined;
switch (pullCreditRec.status) {
- case PeerPullPaymentInitiationStatus.PendingCreatePurse:
- newStatus = PeerPullPaymentInitiationStatus.SuspendedCreatePurse;
+ case PeerPullPaymentCreditStatus.PendingCreatePurse:
+ newStatus = PeerPullPaymentCreditStatus.SuspendedCreatePurse;
break;
- case PeerPullPaymentInitiationStatus.PendingMergeKycRequired:
- newStatus = PeerPullPaymentInitiationStatus.SuspendedMergeKycRequired;
+ case PeerPullPaymentCreditStatus.PendingMergeKycRequired:
+ newStatus = PeerPullPaymentCreditStatus.SuspendedMergeKycRequired;
break;
- case PeerPullPaymentInitiationStatus.PendingWithdrawing:
- newStatus = PeerPullPaymentInitiationStatus.SuspendedWithdrawing;
+ case PeerPullPaymentCreditStatus.PendingWithdrawing:
+ newStatus = PeerPullPaymentCreditStatus.SuspendedWithdrawing;
break;
- case PeerPullPaymentInitiationStatus.PendingReady:
- newStatus = PeerPullPaymentInitiationStatus.SuspendedReady;
+ case PeerPullPaymentCreditStatus.PendingReady:
+ newStatus = PeerPullPaymentCreditStatus.SuspendedReady;
break;
- case PeerPullPaymentInitiationStatus.AbortingDeletePurse:
- newStatus =
- PeerPullPaymentInitiationStatus.SuspendedAbortingDeletePurse;
+ case PeerPullPaymentCreditStatus.AbortingDeletePurse:
+ newStatus = PeerPullPaymentCreditStatus.SuspendedAbortingDeletePurse;
break;
- case PeerPullPaymentInitiationStatus.Done:
- case PeerPullPaymentInitiationStatus.SuspendedCreatePurse:
- case PeerPullPaymentInitiationStatus.SuspendedMergeKycRequired:
- case PeerPullPaymentInitiationStatus.SuspendedReady:
- case PeerPullPaymentInitiationStatus.SuspendedWithdrawing:
- case PeerPullPaymentInitiationStatus.Aborted:
- case PeerPullPaymentInitiationStatus.Failed:
- case PeerPullPaymentInitiationStatus.SuspendedAbortingDeletePurse:
+ case PeerPullPaymentCreditStatus.Done:
+ case PeerPullPaymentCreditStatus.SuspendedCreatePurse:
+ case PeerPullPaymentCreditStatus.SuspendedMergeKycRequired:
+ case PeerPullPaymentCreditStatus.SuspendedReady:
+ case PeerPullPaymentCreditStatus.SuspendedWithdrawing:
+ case PeerPullPaymentCreditStatus.Aborted:
+ case PeerPullPaymentCreditStatus.Failed:
+ case PeerPullPaymentCreditStatus.SuspendedAbortingDeletePurse:
break;
default:
assertUnreachable(pullCreditRec.status);
@@ -867,7 +874,7 @@ export async function suspendPeerPullCreditTransaction(
const oldTxState = computePeerPullCreditTransactionState(pullCreditRec);
pullCreditRec.status = newStatus;
const newTxState = computePeerPullCreditTransactionState(pullCreditRec);
- await tx.peerPullPaymentInitiations.put(pullCreditRec);
+ await tx.peerPullCredit.put(pullCreditRec);
return {
oldTxState,
newTxState,
@@ -892,33 +899,33 @@ export async function abortPeerPullCreditTransaction(
});
stopLongpolling(ws, taskId);
const transitionInfo = await ws.db
- .mktx((x) => [x.peerPullPaymentInitiations])
+ .mktx((x) => [x.peerPullCredit])
.runReadWrite(async (tx) => {
- const pullCreditRec = await tx.peerPullPaymentInitiations.get(pursePub);
+ const pullCreditRec = await tx.peerPullCredit.get(pursePub);
if (!pullCreditRec) {
logger.warn(`peer pull credit ${pursePub} not found`);
return;
}
- let newStatus: PeerPullPaymentInitiationStatus | undefined = undefined;
+ let newStatus: PeerPullPaymentCreditStatus | undefined = undefined;
switch (pullCreditRec.status) {
- case PeerPullPaymentInitiationStatus.PendingCreatePurse:
- case PeerPullPaymentInitiationStatus.PendingMergeKycRequired:
- newStatus = PeerPullPaymentInitiationStatus.AbortingDeletePurse;
+ case PeerPullPaymentCreditStatus.PendingCreatePurse:
+ case PeerPullPaymentCreditStatus.PendingMergeKycRequired:
+ newStatus = PeerPullPaymentCreditStatus.AbortingDeletePurse;
break;
- case PeerPullPaymentInitiationStatus.PendingWithdrawing:
+ case PeerPullPaymentCreditStatus.PendingWithdrawing:
throw Error("can't abort anymore");
- case PeerPullPaymentInitiationStatus.PendingReady:
- newStatus = PeerPullPaymentInitiationStatus.AbortingDeletePurse;
+ case PeerPullPaymentCreditStatus.PendingReady:
+ newStatus = PeerPullPaymentCreditStatus.AbortingDeletePurse;
break;
- case PeerPullPaymentInitiationStatus.Done:
- case PeerPullPaymentInitiationStatus.SuspendedCreatePurse:
- case PeerPullPaymentInitiationStatus.SuspendedMergeKycRequired:
- case PeerPullPaymentInitiationStatus.SuspendedReady:
- case PeerPullPaymentInitiationStatus.SuspendedWithdrawing:
- case PeerPullPaymentInitiationStatus.Aborted:
- case PeerPullPaymentInitiationStatus.AbortingDeletePurse:
- case PeerPullPaymentInitiationStatus.Failed:
- case PeerPullPaymentInitiationStatus.SuspendedAbortingDeletePurse:
+ case PeerPullPaymentCreditStatus.Done:
+ case PeerPullPaymentCreditStatus.SuspendedCreatePurse:
+ case PeerPullPaymentCreditStatus.SuspendedMergeKycRequired:
+ case PeerPullPaymentCreditStatus.SuspendedReady:
+ case PeerPullPaymentCreditStatus.SuspendedWithdrawing:
+ case PeerPullPaymentCreditStatus.Aborted:
+ case PeerPullPaymentCreditStatus.AbortingDeletePurse:
+ case PeerPullPaymentCreditStatus.Failed:
+ case PeerPullPaymentCreditStatus.SuspendedAbortingDeletePurse:
break;
default:
assertUnreachable(pullCreditRec.status);
@@ -927,7 +934,7 @@ export async function abortPeerPullCreditTransaction(
const oldTxState = computePeerPullCreditTransactionState(pullCreditRec);
pullCreditRec.status = newStatus;
const newTxState = computePeerPullCreditTransactionState(pullCreditRec);
- await tx.peerPullPaymentInitiations.put(pullCreditRec);
+ await tx.peerPullCredit.put(pullCreditRec);
return {
oldTxState,
newTxState,
@@ -952,30 +959,30 @@ export async function failPeerPullCreditTransaction(
});
stopLongpolling(ws, taskId);
const transitionInfo = await ws.db
- .mktx((x) => [x.peerPullPaymentInitiations])
+ .mktx((x) => [x.peerPullCredit])
.runReadWrite(async (tx) => {
- const pullCreditRec = await tx.peerPullPaymentInitiations.get(pursePub);
+ const pullCreditRec = await tx.peerPullCredit.get(pursePub);
if (!pullCreditRec) {
logger.warn(`peer pull credit ${pursePub} not found`);
return;
}
- let newStatus: PeerPullPaymentInitiationStatus | undefined = undefined;
+ let newStatus: PeerPullPaymentCreditStatus | undefined = undefined;
switch (pullCreditRec.status) {
- case PeerPullPaymentInitiationStatus.PendingCreatePurse:
- case PeerPullPaymentInitiationStatus.PendingMergeKycRequired:
- case PeerPullPaymentInitiationStatus.PendingWithdrawing:
- case PeerPullPaymentInitiationStatus.PendingReady:
- case PeerPullPaymentInitiationStatus.Done:
- case PeerPullPaymentInitiationStatus.SuspendedCreatePurse:
- case PeerPullPaymentInitiationStatus.SuspendedMergeKycRequired:
- case PeerPullPaymentInitiationStatus.SuspendedReady:
- case PeerPullPaymentInitiationStatus.SuspendedWithdrawing:
- case PeerPullPaymentInitiationStatus.Aborted:
- case PeerPullPaymentInitiationStatus.Failed:
+ case PeerPullPaymentCreditStatus.PendingCreatePurse:
+ case PeerPullPaymentCreditStatus.PendingMergeKycRequired:
+ case PeerPullPaymentCreditStatus.PendingWithdrawing:
+ case PeerPullPaymentCreditStatus.PendingReady:
+ case PeerPullPaymentCreditStatus.Done:
+ case PeerPullPaymentCreditStatus.SuspendedCreatePurse:
+ case PeerPullPaymentCreditStatus.SuspendedMergeKycRequired:
+ case PeerPullPaymentCreditStatus.SuspendedReady:
+ case PeerPullPaymentCreditStatus.SuspendedWithdrawing:
+ case PeerPullPaymentCreditStatus.Aborted:
+ case PeerPullPaymentCreditStatus.Failed:
break;
- case PeerPullPaymentInitiationStatus.AbortingDeletePurse:
- case PeerPullPaymentInitiationStatus.SuspendedAbortingDeletePurse:
- newStatus = PeerPullPaymentInitiationStatus.Failed;
+ case PeerPullPaymentCreditStatus.AbortingDeletePurse:
+ case PeerPullPaymentCreditStatus.SuspendedAbortingDeletePurse:
+ newStatus = PeerPullPaymentCreditStatus.Failed;
break;
default:
assertUnreachable(pullCreditRec.status);
@@ -984,7 +991,7 @@ export async function failPeerPullCreditTransaction(
const oldTxState = computePeerPullCreditTransactionState(pullCreditRec);
pullCreditRec.status = newStatus;
const newTxState = computePeerPullCreditTransactionState(pullCreditRec);
- await tx.peerPullPaymentInitiations.put(pullCreditRec);
+ await tx.peerPullCredit.put(pullCreditRec);
return {
oldTxState,
newTxState,
@@ -1009,38 +1016,38 @@ export async function resumePeerPullCreditTransaction(
});
stopLongpolling(ws, taskId);
const transitionInfo = await ws.db
- .mktx((x) => [x.peerPullPaymentInitiations])
+ .mktx((x) => [x.peerPullCredit])
.runReadWrite(async (tx) => {
- const pullCreditRec = await tx.peerPullPaymentInitiations.get(pursePub);
+ const pullCreditRec = await tx.peerPullCredit.get(pursePub);
if (!pullCreditRec) {
logger.warn(`peer pull credit ${pursePub} not found`);
return;
}
- let newStatus: PeerPullPaymentInitiationStatus | undefined = undefined;
+ let newStatus: PeerPullPaymentCreditStatus | undefined = undefined;
switch (pullCreditRec.status) {
- case PeerPullPaymentInitiationStatus.PendingCreatePurse:
- case PeerPullPaymentInitiationStatus.PendingMergeKycRequired:
- case PeerPullPaymentInitiationStatus.PendingWithdrawing:
- case PeerPullPaymentInitiationStatus.PendingReady:
- case PeerPullPaymentInitiationStatus.AbortingDeletePurse:
- case PeerPullPaymentInitiationStatus.Done:
- case PeerPullPaymentInitiationStatus.Failed:
- case PeerPullPaymentInitiationStatus.Aborted:
+ case PeerPullPaymentCreditStatus.PendingCreatePurse:
+ case PeerPullPaymentCreditStatus.PendingMergeKycRequired:
+ case PeerPullPaymentCreditStatus.PendingWithdrawing:
+ case PeerPullPaymentCreditStatus.PendingReady:
+ case PeerPullPaymentCreditStatus.AbortingDeletePurse:
+ case PeerPullPaymentCreditStatus.Done:
+ case PeerPullPaymentCreditStatus.Failed:
+ case PeerPullPaymentCreditStatus.Aborted:
break;
- case PeerPullPaymentInitiationStatus.SuspendedCreatePurse:
- newStatus = PeerPullPaymentInitiationStatus.PendingCreatePurse;
+ case PeerPullPaymentCreditStatus.SuspendedCreatePurse:
+ newStatus = PeerPullPaymentCreditStatus.PendingCreatePurse;
break;
- case PeerPullPaymentInitiationStatus.SuspendedMergeKycRequired:
- newStatus = PeerPullPaymentInitiationStatus.PendingMergeKycRequired;
+ case PeerPullPaymentCreditStatus.SuspendedMergeKycRequired:
+ newStatus = PeerPullPaymentCreditStatus.PendingMergeKycRequired;
break;
- case PeerPullPaymentInitiationStatus.SuspendedReady:
- newStatus = PeerPullPaymentInitiationStatus.PendingReady;
+ case PeerPullPaymentCreditStatus.SuspendedReady:
+ newStatus = PeerPullPaymentCreditStatus.PendingReady;
break;
- case PeerPullPaymentInitiationStatus.SuspendedWithdrawing:
- newStatus = PeerPullPaymentInitiationStatus.PendingWithdrawing;
+ case PeerPullPaymentCreditStatus.SuspendedWithdrawing:
+ newStatus = PeerPullPaymentCreditStatus.PendingWithdrawing;
break;
- case PeerPullPaymentInitiationStatus.SuspendedAbortingDeletePurse:
- newStatus = PeerPullPaymentInitiationStatus.AbortingDeletePurse;
+ case PeerPullPaymentCreditStatus.SuspendedAbortingDeletePurse:
+ newStatus = PeerPullPaymentCreditStatus.AbortingDeletePurse;
break;
default:
assertUnreachable(pullCreditRec.status);
@@ -1049,7 +1056,7 @@ export async function resumePeerPullCreditTransaction(
const oldTxState = computePeerPullCreditTransactionState(pullCreditRec);
pullCreditRec.status = newStatus;
const newTxState = computePeerPullCreditTransactionState(pullCreditRec);
- await tx.peerPullPaymentInitiations.put(pullCreditRec);
+ await tx.peerPullCredit.put(pullCreditRec);
return {
oldTxState,
newTxState,
@@ -1062,67 +1069,67 @@ export async function resumePeerPullCreditTransaction(
}
export function computePeerPullCreditTransactionState(
- pullCreditRecord: PeerPullPaymentInitiationRecord,
+ pullCreditRecord: PeerPullCreditRecord,
): TransactionState {
switch (pullCreditRecord.status) {
- case PeerPullPaymentInitiationStatus.PendingCreatePurse:
+ case PeerPullPaymentCreditStatus.PendingCreatePurse:
return {
major: TransactionMajorState.Pending,
minor: TransactionMinorState.CreatePurse,
};
- case PeerPullPaymentInitiationStatus.PendingMergeKycRequired:
+ case PeerPullPaymentCreditStatus.PendingMergeKycRequired:
return {
major: TransactionMajorState.Pending,
minor: TransactionMinorState.MergeKycRequired,
};
- case PeerPullPaymentInitiationStatus.PendingReady:
+ case PeerPullPaymentCreditStatus.PendingReady:
return {
major: TransactionMajorState.Pending,
minor: TransactionMinorState.Ready,
};
- case PeerPullPaymentInitiationStatus.Done:
+ case PeerPullPaymentCreditStatus.Done:
return {
major: TransactionMajorState.Done,
};
- case PeerPullPaymentInitiationStatus.PendingWithdrawing:
+ case PeerPullPaymentCreditStatus.PendingWithdrawing:
return {
major: TransactionMajorState.Pending,
minor: TransactionMinorState.Withdraw,
};
- case PeerPullPaymentInitiationStatus.SuspendedCreatePurse:
+ case PeerPullPaymentCreditStatus.SuspendedCreatePurse:
return {
major: TransactionMajorState.Suspended,
minor: TransactionMinorState.CreatePurse,
};
- case PeerPullPaymentInitiationStatus.SuspendedReady:
+ case PeerPullPaymentCreditStatus.SuspendedReady:
return {
major: TransactionMajorState.Suspended,
minor: TransactionMinorState.Ready,
};
- case PeerPullPaymentInitiationStatus.SuspendedWithdrawing:
+ case PeerPullPaymentCreditStatus.SuspendedWithdrawing:
return {
major: TransactionMajorState.Pending,
minor: TransactionMinorState.Withdraw,
};
- case PeerPullPaymentInitiationStatus.SuspendedMergeKycRequired:
+ case PeerPullPaymentCreditStatus.SuspendedMergeKycRequired:
return {
major: TransactionMajorState.Suspended,
minor: TransactionMinorState.MergeKycRequired,
};
- case PeerPullPaymentInitiationStatus.Aborted:
+ case PeerPullPaymentCreditStatus.Aborted:
return {
major: TransactionMajorState.Aborted,
};
- case PeerPullPaymentInitiationStatus.AbortingDeletePurse:
+ case PeerPullPaymentCreditStatus.AbortingDeletePurse:
return {
major: TransactionMajorState.Aborting,
minor: TransactionMinorState.DeletePurse,
};
- case PeerPullPaymentInitiationStatus.Failed:
+ case PeerPullPaymentCreditStatus.Failed:
return {
major: TransactionMajorState.Failed,
};
- case PeerPullPaymentInitiationStatus.SuspendedAbortingDeletePurse:
+ case PeerPullPaymentCreditStatus.SuspendedAbortingDeletePurse:
return {
major: TransactionMajorState.Aborting,
minor: TransactionMinorState.DeletePurse,
@@ -1131,34 +1138,34 @@ export function computePeerPullCreditTransactionState(
}
export function computePeerPullCreditTransactionActions(
- pullCreditRecord: PeerPullPaymentInitiationRecord,
+ pullCreditRecord: PeerPullCreditRecord,
): TransactionAction[] {
switch (pullCreditRecord.status) {
- case PeerPullPaymentInitiationStatus.PendingCreatePurse:
+ case PeerPullPaymentCreditStatus.PendingCreatePurse:
return [TransactionAction.Abort, TransactionAction.Suspend];
- case PeerPullPaymentInitiationStatus.PendingMergeKycRequired:
+ case PeerPullPaymentCreditStatus.PendingMergeKycRequired:
return [TransactionAction.Abort, TransactionAction.Suspend];
- case PeerPullPaymentInitiationStatus.PendingReady:
+ case PeerPullPaymentCreditStatus.PendingReady:
return [TransactionAction.Abort, TransactionAction.Suspend];
- case PeerPullPaymentInitiationStatus.Done:
+ case PeerPullPaymentCreditStatus.Done:
return [TransactionAction.Delete];
- case PeerPullPaymentInitiationStatus.PendingWithdrawing:
+ case PeerPullPaymentCreditStatus.PendingWithdrawing:
return [TransactionAction.Abort, TransactionAction.Suspend];
- case PeerPullPaymentInitiationStatus.SuspendedCreatePurse:
+ case PeerPullPaymentCreditStatus.SuspendedCreatePurse:
return [TransactionAction.Resume, TransactionAction.Abort];
- case PeerPullPaymentInitiationStatus.SuspendedReady:
+ case PeerPullPaymentCreditStatus.SuspendedReady:
return [TransactionAction.Abort, TransactionAction.Resume];
- case PeerPullPaymentInitiationStatus.SuspendedWithdrawing:
+ case PeerPullPaymentCreditStatus.SuspendedWithdrawing:
return [TransactionAction.Resume, TransactionAction.Fail];
- case PeerPullPaymentInitiationStatus.SuspendedMergeKycRequired:
+ case PeerPullPaymentCreditStatus.SuspendedMergeKycRequired:
return [TransactionAction.Resume, TransactionAction.Fail];
- case PeerPullPaymentInitiationStatus.Aborted:
+ case PeerPullPaymentCreditStatus.Aborted:
return [TransactionAction.Delete];
- case PeerPullPaymentInitiationStatus.AbortingDeletePurse:
+ case PeerPullPaymentCreditStatus.AbortingDeletePurse:
return [TransactionAction.Suspend, TransactionAction.Fail];
- case PeerPullPaymentInitiationStatus.Failed:
+ case PeerPullPaymentCreditStatus.Failed:
return [TransactionAction.Delete];
- case PeerPullPaymentInitiationStatus.SuspendedAbortingDeletePurse:
+ case PeerPullPaymentCreditStatus.SuspendedAbortingDeletePurse:
return [TransactionAction.Resume, TransactionAction.Fail];
}
}
diff --git a/packages/taler-wallet-core/src/operations/pay-peer-pull-debit.ts b/packages/taler-wallet-core/src/operations/pay-peer-pull-debit.ts
index 0de91bf97..f357c41d5 100644
--- a/packages/taler-wallet-core/src/operations/pay-peer-pull-debit.ts
+++ b/packages/taler-wallet-core/src/operations/pay-peer-pull-debit.ts
@@ -140,10 +140,10 @@ async function handlePurseCreationConflict(
);
await ws.db
- .mktx((x) => [x.peerPullPaymentIncoming])
+ .mktx((x) => [x.peerPullDebit])
.runReadWrite(async (tx) => {
- const myPpi = await tx.peerPullPaymentIncoming.get(
- peerPullInc.peerPullPaymentIncomingId,
+ const myPpi = await tx.peerPullDebit.get(
+ peerPullInc.peerPullDebitId,
);
if (!myPpi) {
return;
@@ -162,7 +162,7 @@ async function handlePurseCreationConflict(
default:
return;
}
- await tx.peerPullPaymentIncoming.put(myPpi);
+ await tx.peerPullDebit.put(myPpi);
});
return TaskRunResult.finished();
}
@@ -171,7 +171,7 @@ async function processPeerPullDebitPendingDeposit(
ws: InternalWalletState,
peerPullInc: PeerPullPaymentIncomingRecord,
): Promise<TaskRunResult> {
- const peerPullPaymentIncomingId = peerPullInc.peerPullPaymentIncomingId;
+ const peerPullDebitId = peerPullInc.peerPullDebitId;
const pursePub = peerPullInc.pursePub;
const coinSel = peerPullInc.coinSel;
@@ -202,7 +202,7 @@ async function processPeerPullDebitPendingDeposit(
const transactionId = constructTransactionIdentifier({
tag: TransactionType.PeerPullDebit,
- peerPullPaymentIncomingId,
+ peerPullDebitId,
});
const httpResp = await ws.http.fetch(purseDepositUrl.href, {
@@ -218,10 +218,10 @@ async function processPeerPullDebitPendingDeposit(
logger.trace(`purse deposit response: ${j2s(resp)}`);
const transitionInfo = await ws.db
- .mktx((x) => [x.peerPullPaymentIncoming])
+ .mktx((x) => [x.peerPullDebit])
.runReadWrite(async (tx) => {
- const pi = await tx.peerPullPaymentIncoming.get(
- peerPullPaymentIncomingId,
+ const pi = await tx.peerPullDebit.get(
+ peerPullDebitId,
);
if (!pi) {
throw Error("peer pull payment not found anymore");
@@ -230,9 +230,9 @@ async function processPeerPullDebitPendingDeposit(
return;
}
const oldTxState = computePeerPullDebitTransactionState(pi);
- pi.status = PeerPullDebitRecordStatus.DonePaid;
+ pi.status = PeerPullDebitRecordStatus.Done;
const newTxState = computePeerPullDebitTransactionState(pi);
- await tx.peerPullPaymentIncoming.put(pi);
+ await tx.peerPullDebit.put(pi);
return { oldTxState, newTxState };
});
notifyTransition(ws, transactionId, transitionInfo);
@@ -241,15 +241,15 @@ async function processPeerPullDebitPendingDeposit(
case HttpStatusCode.Gone: {
const transitionInfo = await ws.db
.mktx((x) => [
- x.peerPullPaymentIncoming,
+ x.peerPullDebit,
x.refreshGroups,
x.denominations,
x.coinAvailability,
x.coins,
])
.runReadWrite(async (tx) => {
- const pi = await tx.peerPullPaymentIncoming.get(
- peerPullPaymentIncomingId,
+ const pi = await tx.peerPullDebit.get(
+ peerPullDebitId,
);
if (!pi) {
throw Error("peer pull payment not found anymore");
@@ -284,7 +284,7 @@ async function processPeerPullDebitPendingDeposit(
pi.status = PeerPullDebitRecordStatus.AbortingRefresh;
pi.abortRefreshGroupId = refresh.refreshGroupId;
const newTxState = computePeerPullDebitTransactionState(pi);
- await tx.peerPullPaymentIncoming.put(pi);
+ await tx.peerPullDebit.put(pi);
return { oldTxState, newTxState };
});
notifyTransition(ws, transactionId, transitionInfo);
@@ -308,15 +308,15 @@ async function processPeerPullDebitAbortingRefresh(
ws: InternalWalletState,
peerPullInc: PeerPullPaymentIncomingRecord,
): Promise<TaskRunResult> {
- const peerPullPaymentIncomingId = peerPullInc.peerPullPaymentIncomingId;
+ const peerPullDebitId = peerPullInc.peerPullDebitId;
const abortRefreshGroupId = peerPullInc.abortRefreshGroupId;
checkLogicInvariant(!!abortRefreshGroupId);
const transactionId = constructTransactionIdentifier({
tag: TransactionType.PeerPullDebit,
- peerPullPaymentIncomingId,
+ peerPullDebitId,
});
const transitionInfo = await ws.db
- .mktx((x) => [x.refreshGroups, x.peerPullPaymentIncoming])
+ .mktx((x) => [x.refreshGroups, x.peerPullDebit])
.runReadWrite(async (tx) => {
const refreshGroup = await tx.refreshGroups.get(abortRefreshGroupId);
let newOpState: PeerPullDebitRecordStatus | undefined;
@@ -335,8 +335,8 @@ async function processPeerPullDebitAbortingRefresh(
}
}
if (newOpState) {
- const newDg = await tx.peerPullPaymentIncoming.get(
- peerPullPaymentIncomingId,
+ const newDg = await tx.peerPullDebit.get(
+ peerPullDebitId,
);
if (!newDg) {
return;
@@ -344,7 +344,7 @@ async function processPeerPullDebitAbortingRefresh(
const oldTxState = computePeerPullDebitTransactionState(newDg);
newDg.status = newOpState;
const newTxState = computePeerPullDebitTransactionState(newDg);
- await tx.peerPullPaymentIncoming.put(newDg);
+ await tx.peerPullDebit.put(newDg);
return { oldTxState, newTxState };
}
return undefined;
@@ -356,12 +356,12 @@ async function processPeerPullDebitAbortingRefresh(
export async function processPeerPullDebit(
ws: InternalWalletState,
- peerPullPaymentIncomingId: string,
+ peerPullDebitId: string,
): Promise<TaskRunResult> {
const peerPullInc = await ws.db
- .mktx((x) => [x.peerPullPaymentIncoming])
+ .mktx((x) => [x.peerPullDebit])
.runReadOnly(async (tx) => {
- return tx.peerPullPaymentIncoming.get(peerPullPaymentIncomingId);
+ return tx.peerPullDebit.get(peerPullDebitId);
});
if (!peerPullInc) {
throw Error("peer pull debit not found");
@@ -380,31 +380,31 @@ export async function confirmPeerPullDebit(
ws: InternalWalletState,
req: ConfirmPeerPullDebitRequest,
): Promise<AcceptPeerPullPaymentResponse> {
- let peerPullPaymentIncomingId: string;
+ let peerPullDebitId: string;
if (req.transactionId) {
const parsedTx = parseTransactionIdentifier(req.transactionId);
if (!parsedTx || parsedTx.tag !== TransactionType.PeerPullDebit) {
throw Error("invalid peer-pull-debit transaction identifier");
}
- peerPullPaymentIncomingId = parsedTx.peerPullPaymentIncomingId;
- } else if (req.peerPullPaymentIncomingId) {
- peerPullPaymentIncomingId = req.peerPullPaymentIncomingId;
+ peerPullDebitId = parsedTx.peerPullDebitId;
+ } else if (req.peerPullDebitId) {
+ peerPullDebitId = req.peerPullDebitId;
} else {
throw Error(
- "invalid request, transactionId or peerPullPaymentIncomingId required",
+ "invalid request, transactionId or peerPullDebitId required",
);
}
const peerPullInc = await ws.db
- .mktx((x) => [x.peerPullPaymentIncoming])
+ .mktx((x) => [x.peerPullDebit])
.runReadOnly(async (tx) => {
- return tx.peerPullPaymentIncoming.get(peerPullPaymentIncomingId);
+ return tx.peerPullDebit.get(peerPullDebitId);
});
if (!peerPullInc) {
throw Error(
- `can't accept unknown incoming p2p pull payment (${req.peerPullPaymentIncomingId})`,
+ `can't accept unknown incoming p2p pull payment (${req.peerPullDebitId})`,
);
}
@@ -437,15 +437,15 @@ export async function confirmPeerPullDebit(
x.coins,
x.denominations,
x.refreshGroups,
- x.peerPullPaymentIncoming,
+ x.peerPullDebit,
x.coinAvailability,
])
.runReadWrite(async (tx) => {
await spendCoins(ws, tx, {
- // allocationId: `txn:peer-pull-debit:${req.peerPullPaymentIncomingId}`,
+ // allocationId: `txn:peer-pull-debit:${req.peerPullDebitId}`,
allocationId: constructTransactionIdentifier({
tag: TransactionType.PeerPullDebit,
- peerPullPaymentIncomingId,
+ peerPullDebitId,
}),
coinPubs: sel.coins.map((x) => x.coinPub),
contributions: sel.coins.map((x) =>
@@ -454,8 +454,8 @@ export async function confirmPeerPullDebit(
refreshReason: RefreshReason.PayPeerPull,
});
- const pi = await tx.peerPullPaymentIncoming.get(
- peerPullPaymentIncomingId,
+ const pi = await tx.peerPullDebit.get(
+ peerPullDebitId,
);
if (!pi) {
throw Error();
@@ -468,7 +468,7 @@ export async function confirmPeerPullDebit(
totalCost: Amounts.stringify(totalAmount),
};
}
- await tx.peerPullPaymentIncoming.put(pi);
+ await tx.peerPullDebit.put(pi);
return pi;
});
@@ -476,7 +476,7 @@ export async function confirmPeerPullDebit(
const transactionId = constructTransactionIdentifier({
tag: TransactionType.PeerPullDebit,
- peerPullPaymentIncomingId,
+ peerPullDebitId,
});
return {
@@ -499,9 +499,9 @@ export async function preparePeerPullDebit(
}
const existingPullIncomingRecord = await ws.db
- .mktx((x) => [x.peerPullPaymentIncoming])
+ .mktx((x) => [x.peerPullDebit])
.runReadOnly(async (tx) => {
- return tx.peerPullPaymentIncoming.indexes.byExchangeAndContractPriv.get([
+ return tx.peerPullDebit.indexes.byExchangeAndContractPriv.get([
uri.exchangeBaseUrl,
uri.contractPriv,
]);
@@ -513,12 +513,12 @@ export async function preparePeerPullDebit(
amountRaw: existingPullIncomingRecord.contractTerms.amount,
amountEffective: existingPullIncomingRecord.totalCostEstimated,
contractTerms: existingPullIncomingRecord.contractTerms,
- peerPullPaymentIncomingId:
- existingPullIncomingRecord.peerPullPaymentIncomingId,
+ peerPullDebitId:
+ existingPullIncomingRecord.peerPullDebitId,
transactionId: constructTransactionIdentifier({
tag: TransactionType.PeerPullDebit,
- peerPullPaymentIncomingId:
- existingPullIncomingRecord.peerPullPaymentIncomingId,
+ peerPullDebitId:
+ existingPullIncomingRecord.peerPullDebitId,
}),
};
}
@@ -553,7 +553,7 @@ export async function preparePeerPullDebit(
codecForExchangePurseStatus(),
);
- const peerPullPaymentIncomingId = encodeCrock(getRandomBytes(32));
+ const peerPullDebitId = encodeCrock(getRandomBytes(32));
let contractTerms: PeerContractTerms;
@@ -588,10 +588,10 @@ export async function preparePeerPullDebit(
);
await ws.db
- .mktx((x) => [x.peerPullPaymentIncoming])
+ .mktx((x) => [x.peerPullDebit])
.runReadWrite(async (tx) => {
- await tx.peerPullPaymentIncoming.add({
- peerPullPaymentIncomingId,
+ await tx.peerPullDebit.add({
+ peerPullDebitId,
contractPriv: contractPriv,
exchangeBaseUrl: exchangeBaseUrl,
pursePub: pursePub,
@@ -607,42 +607,42 @@ export async function preparePeerPullDebit(
amountEffective: Amounts.stringify(totalAmount),
amountRaw: contractTerms.amount,
contractTerms: contractTerms,
- peerPullPaymentIncomingId,
+ peerPullDebitId,
transactionId: constructTransactionIdentifier({
tag: TransactionType.PeerPullDebit,
- peerPullPaymentIncomingId: peerPullPaymentIncomingId,
+ peerPullDebitId: peerPullDebitId,
}),
};
}
export async function suspendPeerPullDebitTransaction(
ws: InternalWalletState,
- peerPullPaymentIncomingId: string,
+ peerPullDebitId: string,
) {
const taskId = constructTaskIdentifier({
tag: PendingTaskType.PeerPullDebit,
- peerPullPaymentIncomingId,
+ peerPullDebitId,
});
const transactionId = constructTransactionIdentifier({
tag: TransactionType.PeerPullDebit,
- peerPullPaymentIncomingId,
+ peerPullDebitId,
});
stopLongpolling(ws, taskId);
const transitionInfo = await ws.db
- .mktx((x) => [x.peerPullPaymentIncoming])
+ .mktx((x) => [x.peerPullDebit])
.runReadWrite(async (tx) => {
- const pullDebitRec = await tx.peerPullPaymentIncoming.get(
- peerPullPaymentIncomingId,
+ const pullDebitRec = await tx.peerPullDebit.get(
+ peerPullDebitId,
);
if (!pullDebitRec) {
- logger.warn(`peer pull debit ${peerPullPaymentIncomingId} not found`);
+ logger.warn(`peer pull debit ${peerPullDebitId} not found`);
return;
}
let newStatus: PeerPullDebitRecordStatus | undefined = undefined;
switch (pullDebitRec.status) {
case PeerPullDebitRecordStatus.DialogProposed:
break;
- case PeerPullDebitRecordStatus.DonePaid:
+ case PeerPullDebitRecordStatus.Done:
break;
case PeerPullDebitRecordStatus.PendingDeposit:
newStatus = PeerPullDebitRecordStatus.SuspendedDeposit;
@@ -665,7 +665,7 @@ export async function suspendPeerPullDebitTransaction(
const oldTxState = computePeerPullDebitTransactionState(pullDebitRec);
pullDebitRec.status = newStatus;
const newTxState = computePeerPullDebitTransactionState(pullDebitRec);
- await tx.peerPullPaymentIncoming.put(pullDebitRec);
+ await tx.peerPullDebit.put(pullDebitRec);
return {
oldTxState,
newTxState,
@@ -678,25 +678,25 @@ export async function suspendPeerPullDebitTransaction(
export async function abortPeerPullDebitTransaction(
ws: InternalWalletState,
- peerPullPaymentIncomingId: string,
+ peerPullDebitId: string,
) {
const taskId = constructTaskIdentifier({
tag: PendingTaskType.PeerPullDebit,
- peerPullPaymentIncomingId,
+ peerPullDebitId,
});
const transactionId = constructTransactionIdentifier({
tag: TransactionType.PeerPullDebit,
- peerPullPaymentIncomingId,
+ peerPullDebitId,
});
stopLongpolling(ws, taskId);
const transitionInfo = await ws.db
- .mktx((x) => [x.peerPullPaymentIncoming])
+ .mktx((x) => [x.peerPullDebit])
.runReadWrite(async (tx) => {
- const pullDebitRec = await tx.peerPullPaymentIncoming.get(
- peerPullPaymentIncomingId,
+ const pullDebitRec = await tx.peerPullDebit.get(
+ peerPullDebitId,
);
if (!pullDebitRec) {
- logger.warn(`peer pull debit ${peerPullPaymentIncomingId} not found`);
+ logger.warn(`peer pull debit ${peerPullDebitId} not found`);
return;
}
let newStatus: PeerPullDebitRecordStatus | undefined = undefined;
@@ -704,7 +704,7 @@ export async function abortPeerPullDebitTransaction(
case PeerPullDebitRecordStatus.DialogProposed:
newStatus = PeerPullDebitRecordStatus.Aborted;
break;
- case PeerPullDebitRecordStatus.DonePaid:
+ case PeerPullDebitRecordStatus.Done:
break;
case PeerPullDebitRecordStatus.PendingDeposit:
newStatus = PeerPullDebitRecordStatus.AbortingRefresh;
@@ -726,7 +726,7 @@ export async function abortPeerPullDebitTransaction(
const oldTxState = computePeerPullDebitTransactionState(pullDebitRec);
pullDebitRec.status = newStatus;
const newTxState = computePeerPullDebitTransactionState(pullDebitRec);
- await tx.peerPullPaymentIncoming.put(pullDebitRec);
+ await tx.peerPullDebit.put(pullDebitRec);
return {
oldTxState,
newTxState,
@@ -739,25 +739,25 @@ export async function abortPeerPullDebitTransaction(
export async function failPeerPullDebitTransaction(
ws: InternalWalletState,
- peerPullPaymentIncomingId: string,
+ peerPullDebitId: string,
) {
const taskId = constructTaskIdentifier({
tag: PendingTaskType.PeerPullDebit,
- peerPullPaymentIncomingId,
+ peerPullDebitId,
});
const transactionId = constructTransactionIdentifier({
tag: TransactionType.PeerPullDebit,
- peerPullPaymentIncomingId,
+ peerPullDebitId,
});
stopLongpolling(ws, taskId);
const transitionInfo = await ws.db
- .mktx((x) => [x.peerPullPaymentIncoming])
+ .mktx((x) => [x.peerPullDebit])
.runReadWrite(async (tx) => {
- const pullDebitRec = await tx.peerPullPaymentIncoming.get(
- peerPullPaymentIncomingId,
+ const pullDebitRec = await tx.peerPullDebit.get(
+ peerPullDebitId,
);
if (!pullDebitRec) {
- logger.warn(`peer pull debit ${peerPullPaymentIncomingId} not found`);
+ logger.warn(`peer pull debit ${peerPullDebitId} not found`);
return;
}
let newStatus: PeerPullDebitRecordStatus | undefined = undefined;
@@ -765,7 +765,7 @@ export async function failPeerPullDebitTransaction(
case PeerPullDebitRecordStatus.DialogProposed:
newStatus = PeerPullDebitRecordStatus.Aborted;
break;
- case PeerPullDebitRecordStatus.DonePaid:
+ case PeerPullDebitRecordStatus.Done:
break;
case PeerPullDebitRecordStatus.PendingDeposit:
break;
@@ -787,7 +787,7 @@ export async function failPeerPullDebitTransaction(
const oldTxState = computePeerPullDebitTransactionState(pullDebitRec);
pullDebitRec.status = newStatus;
const newTxState = computePeerPullDebitTransactionState(pullDebitRec);
- await tx.peerPullPaymentIncoming.put(pullDebitRec);
+ await tx.peerPullDebit.put(pullDebitRec);
return {
oldTxState,
newTxState,
@@ -800,31 +800,31 @@ export async function failPeerPullDebitTransaction(
export async function resumePeerPullDebitTransaction(
ws: InternalWalletState,
- peerPullPaymentIncomingId: string,
+ peerPullDebitId: string,
) {
const taskId = constructTaskIdentifier({
tag: PendingTaskType.PeerPullDebit,
- peerPullPaymentIncomingId,
+ peerPullDebitId,
});
const transactionId = constructTransactionIdentifier({
tag: TransactionType.PeerPullDebit,
- peerPullPaymentIncomingId,
+ peerPullDebitId,
});
stopLongpolling(ws, taskId);
const transitionInfo = await ws.db
- .mktx((x) => [x.peerPullPaymentIncoming])
+ .mktx((x) => [x.peerPullDebit])
.runReadWrite(async (tx) => {
- const pullDebitRec = await tx.peerPullPaymentIncoming.get(
- peerPullPaymentIncomingId,
+ const pullDebitRec = await tx.peerPullDebit.get(
+ peerPullDebitId,
);
if (!pullDebitRec) {
- logger.warn(`peer pull debit ${peerPullPaymentIncomingId} not found`);
+ logger.warn(`peer pull debit ${peerPullDebitId} not found`);
return;
}
let newStatus: PeerPullDebitRecordStatus | undefined = undefined;
switch (pullDebitRec.status) {
case PeerPullDebitRecordStatus.DialogProposed:
- case PeerPullDebitRecordStatus.DonePaid:
+ case PeerPullDebitRecordStatus.Done:
case PeerPullDebitRecordStatus.PendingDeposit:
break;
case PeerPullDebitRecordStatus.SuspendedDeposit:
@@ -846,7 +846,7 @@ export async function resumePeerPullDebitTransaction(
const oldTxState = computePeerPullDebitTransactionState(pullDebitRec);
pullDebitRec.status = newStatus;
const newTxState = computePeerPullDebitTransactionState(pullDebitRec);
- await tx.peerPullPaymentIncoming.put(pullDebitRec);
+ await tx.peerPullDebit.put(pullDebitRec);
return {
oldTxState,
newTxState,
@@ -872,7 +872,7 @@ export function computePeerPullDebitTransactionState(
major: TransactionMajorState.Pending,
minor: TransactionMinorState.Deposit,
};
- case PeerPullDebitRecordStatus.DonePaid:
+ case PeerPullDebitRecordStatus.Done:
return {
major: TransactionMajorState.Done,
};
@@ -910,7 +910,7 @@ export function computePeerPullDebitTransactionActions(
return [];
case PeerPullDebitRecordStatus.PendingDeposit:
return [TransactionAction.Abort, TransactionAction.Suspend];
- case PeerPullDebitRecordStatus.DonePaid:
+ case PeerPullDebitRecordStatus.Done:
return [TransactionAction.Delete];
case PeerPullDebitRecordStatus.SuspendedDeposit:
return [TransactionAction.Resume, TransactionAction.Abort];
diff --git a/packages/taler-wallet-core/src/operations/pay-peer-push-credit.ts b/packages/taler-wallet-core/src/operations/pay-peer-push-credit.ts
index 47e9eaddd..89d9e3b49 100644
--- a/packages/taler-wallet-core/src/operations/pay-peer-push-credit.ts
+++ b/packages/taler-wallet-core/src/operations/pay-peer-push-credit.ts
@@ -55,7 +55,7 @@ import {
KycPendingInfo,
KycUserType,
PeerPushPaymentIncomingRecord,
- PeerPushPaymentIncomingStatus,
+ PeerPushCreditStatus,
PendingTaskType,
WithdrawalGroupStatus,
WithdrawalRecordType,
@@ -99,10 +99,10 @@ export async function preparePeerPushCredit(
}
const existing = await ws.db
- .mktx((x) => [x.contractTerms, x.peerPushPaymentIncoming])
+ .mktx((x) => [x.contractTerms, x.peerPushCredit])
.runReadOnly(async (tx) => {
const existingPushInc =
- await tx.peerPushPaymentIncoming.indexes.byExchangeAndContractPriv.get([
+ await tx.peerPushCredit.indexes.byExchangeAndContractPriv.get([
uri.exchangeBaseUrl,
uri.contractPriv,
]);
@@ -129,12 +129,12 @@ export async function preparePeerPushCredit(
amountEffective: existing.existingPushInc.estimatedAmountEffective,
amountRaw: existing.existingContractTerms.amount,
contractTerms: existing.existingContractTerms,
- peerPushPaymentIncomingId:
- existing.existingPushInc.peerPushPaymentIncomingId,
+ peerPushCreditId:
+ existing.existingPushInc.peerPushCreditId,
transactionId: constructTransactionIdentifier({
tag: TransactionType.PeerPushCredit,
- peerPushPaymentIncomingId:
- existing.existingPushInc.peerPushPaymentIncomingId,
+ peerPushCreditId:
+ existing.existingPushInc.peerPushCreditId,
}),
};
}
@@ -172,7 +172,7 @@ export async function preparePeerPushCredit(
codecForExchangePurseStatus(),
);
- const peerPushPaymentIncomingId = encodeCrock(getRandomBytes(32));
+ const peerPushCreditId = encodeCrock(getRandomBytes(32));
const contractTermsHash = ContractTermsUtil.hashContractTerms(
dec.contractTerms,
@@ -188,17 +188,17 @@ export async function preparePeerPushCredit(
);
await ws.db
- .mktx((x) => [x.contractTerms, x.peerPushPaymentIncoming])
+ .mktx((x) => [x.contractTerms, x.peerPushCredit])
.runReadWrite(async (tx) => {
- await tx.peerPushPaymentIncoming.add({
- peerPushPaymentIncomingId,
+ await tx.peerPushCredit.add({
+ peerPushCreditId,
contractPriv: contractPriv,
exchangeBaseUrl: exchangeBaseUrl,
mergePriv: dec.mergePriv,
pursePub: pursePub,
timestamp: TalerPreciseTimestamp.now(),
contractTermsHash,
- status: PeerPushPaymentIncomingStatus.DialogProposed,
+ status: PeerPushCreditStatus.DialogProposed,
withdrawalGroupId,
currency: Amounts.currencyOf(purseStatus.balance),
estimatedAmountEffective: Amounts.stringify(
@@ -219,28 +219,28 @@ export async function preparePeerPushCredit(
amountEffective: wi.withdrawalAmountEffective,
amountRaw: purseStatus.balance,
contractTerms: dec.contractTerms,
- peerPushPaymentIncomingId,
+ peerPushCreditId,
transactionId: constructTransactionIdentifier({
tag: TransactionType.PeerPushCredit,
- peerPushPaymentIncomingId,
+ peerPushCreditId,
}),
};
}
async function longpollKycStatus(
ws: InternalWalletState,
- peerPushPaymentIncomingId: string,
+ peerPushCreditId: string,
exchangeUrl: string,
kycInfo: KycPendingInfo,
userType: KycUserType,
): Promise<TaskRunResult> {
const transactionId = constructTransactionIdentifier({
tag: TransactionType.PeerPushCredit,
- peerPushPaymentIncomingId,
+ peerPushCreditId,
});
const retryTag = constructTaskIdentifier({
tag: PendingTaskType.PeerPushCredit,
- peerPushPaymentIncomingId,
+ peerPushCreditId,
});
runLongpollAsync(ws, retryTag, async (ct) => {
@@ -261,24 +261,24 @@ async function longpollKycStatus(
kycStatusRes.status === HttpStatusCode.NoContent
) {
const transitionInfo = await ws.db
- .mktx((x) => [x.peerPushPaymentIncoming])
+ .mktx((x) => [x.peerPushCredit])
.runReadWrite(async (tx) => {
- const peerInc = await tx.peerPushPaymentIncoming.get(
- peerPushPaymentIncomingId,
+ const peerInc = await tx.peerPushCredit.get(
+ peerPushCreditId,
);
if (!peerInc) {
return;
}
if (
peerInc.status !==
- PeerPushPaymentIncomingStatus.PendingMergeKycRequired
+ PeerPushCreditStatus.PendingMergeKycRequired
) {
return;
}
const oldTxState = computePeerPushCreditTransactionState(peerInc);
- peerInc.status = PeerPushPaymentIncomingStatus.PendingMerge;
+ peerInc.status = PeerPushCreditStatus.PendingMerge;
const newTxState = computePeerPushCreditTransactionState(peerInc);
- await tx.peerPushPaymentIncoming.put(peerInc);
+ await tx.peerPushCredit.put(peerInc);
return { oldTxState, newTxState };
});
notifyTransition(ws, transactionId, transitionInfo);
@@ -304,9 +304,9 @@ async function processPeerPushCreditKycRequired(
): Promise<TaskRunResult> {
const transactionId = constructTransactionIdentifier({
tag: TransactionType.PeerPushCredit,
- peerPushPaymentIncomingId: peerInc.peerPushPaymentIncomingId,
+ peerPushCreditId: peerInc.peerPushCreditId,
});
- const { peerPushPaymentIncomingId } = peerInc;
+ const { peerPushCreditId } = peerInc;
const userType = "individual";
const url = new URL(
@@ -331,10 +331,10 @@ async function processPeerPushCreditKycRequired(
const kycStatus = await kycStatusRes.json();
logger.info(`kyc status: ${j2s(kycStatus)}`);
const { transitionInfo, result } = await ws.db
- .mktx((x) => [x.peerPushPaymentIncoming])
+ .mktx((x) => [x.peerPushCredit])
.runReadWrite(async (tx) => {
- const peerInc = await tx.peerPushPaymentIncoming.get(
- peerPushPaymentIncomingId,
+ const peerInc = await tx.peerPushCredit.get(
+ peerPushCreditId,
);
if (!peerInc) {
return {
@@ -348,9 +348,9 @@ async function processPeerPushCreditKycRequired(
requirementRow: kycPending.requirement_row,
};
peerInc.kycUrl = kycStatus.kyc_url;
- peerInc.status = PeerPushPaymentIncomingStatus.PendingMergeKycRequired;
+ peerInc.status = PeerPushCreditStatus.PendingMergeKycRequired;
const newTxState = computePeerPushCreditTransactionState(peerInc);
- await tx.peerPushPaymentIncoming.put(peerInc);
+ await tx.peerPushCredit.put(peerInc);
// We'll remove this eventually! New clients should rely on the
// kycUrl field of the transaction, not the error code.
const res: TaskRunResult = {
@@ -379,10 +379,10 @@ async function handlePendingMerge(
peerInc: PeerPushPaymentIncomingRecord,
contractTerms: PeerContractTerms,
): Promise<TaskRunResult> {
- const { peerPushPaymentIncomingId } = peerInc;
+ const { peerPushCreditId } = peerInc;
const transactionId = constructTransactionIdentifier({
tag: TransactionType.PeerPushCredit,
- peerPushPaymentIncomingId,
+ peerPushCreditId,
});
const amount = Amounts.parseOrThrow(contractTerms.amount);
@@ -446,7 +446,6 @@ async function handlePendingMerge(
amount,
wgInfo: {
withdrawalType: WithdrawalRecordType.PeerPushCredit,
- contractTerms,
},
forcedWithdrawalGroupId: peerInc.withdrawalGroupId,
exchangeBaseUrl: peerInc.exchangeBaseUrl,
@@ -460,16 +459,15 @@ async function handlePendingMerge(
const txRes = await ws.db
.mktx((x) => [
x.contractTerms,
- x.peerPushPaymentIncoming,
+ x.peerPushCredit,
x.withdrawalGroups,
x.reserves,
x.exchanges,
x.exchangeDetails,
- x.exchangeTrust,
])
.runReadWrite(async (tx) => {
- const peerInc = await tx.peerPushPaymentIncoming.get(
- peerPushPaymentIncomingId,
+ const peerInc = await tx.peerPushCredit.get(
+ peerPushCreditId,
);
if (!peerInc) {
return undefined;
@@ -477,9 +475,9 @@ async function handlePendingMerge(
let withdrawalTransition: TransitionInfo | undefined;
const oldTxState = computePeerPushCreditTransactionState(peerInc);
switch (peerInc.status) {
- case PeerPushPaymentIncomingStatus.PendingMerge:
- case PeerPushPaymentIncomingStatus.PendingMergeKycRequired: {
- peerInc.status = PeerPushPaymentIncomingStatus.PendingWithdrawing;
+ case PeerPushCreditStatus.PendingMerge:
+ case PeerPushCreditStatus.PendingMergeKycRequired: {
+ peerInc.status = PeerPushCreditStatus.PendingWithdrawing;
const wgRes = await internalPerformCreateWithdrawalGroup(
ws,
tx,
@@ -489,7 +487,7 @@ async function handlePendingMerge(
break;
}
}
- await tx.peerPushPaymentIncoming.put(peerInc);
+ await tx.peerPushCredit.put(peerInc);
const newTxState = computePeerPushCreditTransactionState(peerInc);
return {
peerPushCreditTransition: { oldTxState, newTxState },
@@ -515,21 +513,21 @@ async function handlePendingWithdrawing(
}
const transactionId = constructTransactionIdentifier({
tag: TransactionType.PeerPushCredit,
- peerPushPaymentIncomingId: peerInc.peerPushPaymentIncomingId,
+ peerPushCreditId: peerInc.peerPushCreditId,
});
const wgId = peerInc.withdrawalGroupId;
let finished: boolean = false;
const transitionInfo = await ws.db
- .mktx((x) => [x.peerPushPaymentIncoming, x.withdrawalGroups])
+ .mktx((x) => [x.peerPushCredit, x.withdrawalGroups])
.runReadWrite(async (tx) => {
- const ppi = await tx.peerPushPaymentIncoming.get(
- peerInc.peerPushPaymentIncomingId,
+ const ppi = await tx.peerPushCredit.get(
+ peerInc.peerPushCreditId,
);
if (!ppi) {
finished = true;
return;
}
- if (ppi.status !== PeerPushPaymentIncomingStatus.PendingWithdrawing) {
+ if (ppi.status !== PeerPushCreditStatus.PendingWithdrawing) {
finished = true;
return;
}
@@ -540,13 +538,13 @@ async function handlePendingWithdrawing(
return undefined;
}
switch (wg.status) {
- case WithdrawalGroupStatus.Finished:
+ case WithdrawalGroupStatus.Done:
finished = true;
- ppi.status = PeerPushPaymentIncomingStatus.Done;
+ ppi.status = PeerPushCreditStatus.Done;
break;
// FIXME: Also handle other final states!
}
- await tx.peerPushPaymentIncoming.put(ppi);
+ await tx.peerPushCredit.put(ppi);
const newTxState = computePeerPushCreditTransactionState(ppi);
return {
oldTxState,
@@ -564,14 +562,14 @@ async function handlePendingWithdrawing(
export async function processPeerPushCredit(
ws: InternalWalletState,
- peerPushPaymentIncomingId: string,
+ peerPushCreditId: string,
): Promise<TaskRunResult> {
let peerInc: PeerPushPaymentIncomingRecord | undefined;
let contractTerms: PeerContractTerms | undefined;
await ws.db
- .mktx((x) => [x.contractTerms, x.peerPushPaymentIncoming])
+ .mktx((x) => [x.contractTerms, x.peerPushCredit])
.runReadWrite(async (tx) => {
- peerInc = await tx.peerPushPaymentIncoming.get(peerPushPaymentIncomingId);
+ peerInc = await tx.peerPushCredit.get(peerPushCreditId);
if (!peerInc) {
return;
}
@@ -579,35 +577,35 @@ export async function processPeerPushCredit(
if (ctRec) {
contractTerms = ctRec.contractTermsRaw;
}
- await tx.peerPushPaymentIncoming.put(peerInc);
+ await tx.peerPushCredit.put(peerInc);
});
checkDbInvariant(!!contractTerms);
if (!peerInc) {
throw Error(
- `can't accept unknown incoming p2p push payment (${peerPushPaymentIncomingId})`,
+ `can't accept unknown incoming p2p push payment (${peerPushCreditId})`,
);
}
switch (peerInc.status) {
- case PeerPushPaymentIncomingStatus.PendingMergeKycRequired: {
+ case PeerPushCreditStatus.PendingMergeKycRequired: {
if (!peerInc.kycInfo) {
throw Error("invalid state, kycInfo required");
}
return await longpollKycStatus(
ws,
- peerPushPaymentIncomingId,
+ peerPushCreditId,
peerInc.exchangeBaseUrl,
peerInc.kycInfo,
"individual",
);
}
- case PeerPushPaymentIncomingStatus.PendingMerge:
+ case PeerPushCreditStatus.PendingMerge:
return handlePendingMerge(ws, peerInc, contractTerms);
- case PeerPushPaymentIncomingStatus.PendingWithdrawing:
+ case PeerPushCreditStatus.PendingWithdrawing:
return handlePendingWithdrawing(ws, peerInc);
default:
@@ -620,9 +618,9 @@ export async function confirmPeerPushCredit(
req: ConfirmPeerPushCreditRequest,
): Promise<AcceptPeerPushPaymentResponse> {
let peerInc: PeerPushPaymentIncomingRecord | undefined;
- let peerPushPaymentIncomingId: string;
- if (req.peerPushPaymentIncomingId) {
- peerPushPaymentIncomingId = req.peerPushPaymentIncomingId;
+ let peerPushCreditId: string;
+ if (req.peerPushCreditId) {
+ peerPushCreditId = req.peerPushCreditId;
} else if (req.transactionId) {
const parsedTx = parseTransactionIdentifier(req.transactionId);
if (!parsedTx) {
@@ -631,29 +629,29 @@ export async function confirmPeerPushCredit(
if (parsedTx.tag !== TransactionType.PeerPushCredit) {
throw Error("invalid transaction ID type");
}
- peerPushPaymentIncomingId = parsedTx.peerPushPaymentIncomingId;
+ peerPushCreditId = parsedTx.peerPushCreditId;
} else {
throw Error(
- "no transaction ID (or deprecated peerPushPaymentIncomingId) provided",
+ "no transaction ID (or deprecated peerPushCreditId) provided",
);
}
await ws.db
- .mktx((x) => [x.contractTerms, x.peerPushPaymentIncoming])
+ .mktx((x) => [x.contractTerms, x.peerPushCredit])
.runReadWrite(async (tx) => {
- peerInc = await tx.peerPushPaymentIncoming.get(peerPushPaymentIncomingId);
+ peerInc = await tx.peerPushCredit.get(peerPushCreditId);
if (!peerInc) {
return;
}
- if (peerInc.status === PeerPushPaymentIncomingStatus.DialogProposed) {
- peerInc.status = PeerPushPaymentIncomingStatus.PendingMerge;
+ if (peerInc.status === PeerPushCreditStatus.DialogProposed) {
+ peerInc.status = PeerPushCreditStatus.PendingMerge;
}
- await tx.peerPushPaymentIncoming.put(peerInc);
+ await tx.peerPushCredit.put(peerInc);
});
if (!peerInc) {
throw Error(
- `can't accept unknown incoming p2p push payment (${req.peerPushPaymentIncomingId})`,
+ `can't accept unknown incoming p2p push payment (${req.peerPushCreditId})`,
);
}
@@ -661,7 +659,7 @@ export async function confirmPeerPushCredit(
const transactionId = constructTransactionIdentifier({
tag: TransactionType.PeerPushCredit,
- peerPushPaymentIncomingId,
+ peerPushCreditId,
});
return {
@@ -671,48 +669,48 @@ export async function confirmPeerPushCredit(
export async function suspendPeerPushCreditTransaction(
ws: InternalWalletState,
- peerPushPaymentIncomingId: string,
+ peerPushCreditId: string,
) {
const taskId = constructTaskIdentifier({
tag: PendingTaskType.PeerPushCredit,
- peerPushPaymentIncomingId,
+ peerPushCreditId,
});
const transactionId = constructTransactionIdentifier({
tag: TransactionType.PeerPushCredit,
- peerPushPaymentIncomingId,
+ peerPushCreditId,
});
stopLongpolling(ws, taskId);
const transitionInfo = await ws.db
- .mktx((x) => [x.peerPushPaymentIncoming])
+ .mktx((x) => [x.peerPushCredit])
.runReadWrite(async (tx) => {
- const pushCreditRec = await tx.peerPushPaymentIncoming.get(
- peerPushPaymentIncomingId,
+ const pushCreditRec = await tx.peerPushCredit.get(
+ peerPushCreditId,
);
if (!pushCreditRec) {
- logger.warn(`peer push credit ${peerPushPaymentIncomingId} not found`);
+ logger.warn(`peer push credit ${peerPushCreditId} not found`);
return;
}
- let newStatus: PeerPushPaymentIncomingStatus | undefined = undefined;
+ let newStatus: PeerPushCreditStatus | undefined = undefined;
switch (pushCreditRec.status) {
- case PeerPushPaymentIncomingStatus.DialogProposed:
- case PeerPushPaymentIncomingStatus.Done:
- case PeerPushPaymentIncomingStatus.SuspendedMerge:
- case PeerPushPaymentIncomingStatus.SuspendedMergeKycRequired:
- case PeerPushPaymentIncomingStatus.SuspendedWithdrawing:
+ case PeerPushCreditStatus.DialogProposed:
+ case PeerPushCreditStatus.Done:
+ case PeerPushCreditStatus.SuspendedMerge:
+ case PeerPushCreditStatus.SuspendedMergeKycRequired:
+ case PeerPushCreditStatus.SuspendedWithdrawing:
break;
- case PeerPushPaymentIncomingStatus.PendingMergeKycRequired:
- newStatus = PeerPushPaymentIncomingStatus.SuspendedMergeKycRequired;
+ case PeerPushCreditStatus.PendingMergeKycRequired:
+ newStatus = PeerPushCreditStatus.SuspendedMergeKycRequired;
break;
- case PeerPushPaymentIncomingStatus.PendingMerge:
- newStatus = PeerPushPaymentIncomingStatus.SuspendedMerge;
+ case PeerPushCreditStatus.PendingMerge:
+ newStatus = PeerPushCreditStatus.SuspendedMerge;
break;
- case PeerPushPaymentIncomingStatus.PendingWithdrawing:
+ case PeerPushCreditStatus.PendingWithdrawing:
// FIXME: Suspend internal withdrawal transaction!
- newStatus = PeerPushPaymentIncomingStatus.SuspendedWithdrawing;
+ newStatus = PeerPushCreditStatus.SuspendedWithdrawing;
break;
- case PeerPushPaymentIncomingStatus.Aborted:
+ case PeerPushCreditStatus.Aborted:
break;
- case PeerPushPaymentIncomingStatus.Failed:
+ case PeerPushCreditStatus.Failed:
break;
default:
assertUnreachable(pushCreditRec.status);
@@ -721,7 +719,7 @@ export async function suspendPeerPushCreditTransaction(
const oldTxState = computePeerPushCreditTransactionState(pushCreditRec);
pushCreditRec.status = newStatus;
const newTxState = computePeerPushCreditTransactionState(pushCreditRec);
- await tx.peerPushPaymentIncoming.put(pushCreditRec);
+ await tx.peerPushCredit.put(pushCreditRec);
return {
oldTxState,
newTxState,
@@ -734,51 +732,51 @@ export async function suspendPeerPushCreditTransaction(
export async function abortPeerPushCreditTransaction(
ws: InternalWalletState,
- peerPushPaymentIncomingId: string,
+ peerPushCreditId: string,
) {
const taskId = constructTaskIdentifier({
tag: PendingTaskType.PeerPushCredit,
- peerPushPaymentIncomingId,
+ peerPushCreditId,
});
const transactionId = constructTransactionIdentifier({
tag: TransactionType.PeerPushCredit,
- peerPushPaymentIncomingId,
+ peerPushCreditId,
});
stopLongpolling(ws, taskId);
const transitionInfo = await ws.db
- .mktx((x) => [x.peerPushPaymentIncoming])
+ .mktx((x) => [x.peerPushCredit])
.runReadWrite(async (tx) => {
- const pushCreditRec = await tx.peerPushPaymentIncoming.get(
- peerPushPaymentIncomingId,
+ const pushCreditRec = await tx.peerPushCredit.get(
+ peerPushCreditId,
);
if (!pushCreditRec) {
- logger.warn(`peer push credit ${peerPushPaymentIncomingId} not found`);
+ logger.warn(`peer push credit ${peerPushCreditId} not found`);
return;
}
- let newStatus: PeerPushPaymentIncomingStatus | undefined = undefined;
+ let newStatus: PeerPushCreditStatus | undefined = undefined;
switch (pushCreditRec.status) {
- case PeerPushPaymentIncomingStatus.DialogProposed:
- newStatus = PeerPushPaymentIncomingStatus.Aborted;
+ case PeerPushCreditStatus.DialogProposed:
+ newStatus = PeerPushCreditStatus.Aborted;
break;
- case PeerPushPaymentIncomingStatus.Done:
+ case PeerPushCreditStatus.Done:
break;
- case PeerPushPaymentIncomingStatus.SuspendedMerge:
- case PeerPushPaymentIncomingStatus.SuspendedMergeKycRequired:
- case PeerPushPaymentIncomingStatus.SuspendedWithdrawing:
- newStatus = PeerPushPaymentIncomingStatus.Aborted;
+ case PeerPushCreditStatus.SuspendedMerge:
+ case PeerPushCreditStatus.SuspendedMergeKycRequired:
+ case PeerPushCreditStatus.SuspendedWithdrawing:
+ newStatus = PeerPushCreditStatus.Aborted;
break;
- case PeerPushPaymentIncomingStatus.PendingMergeKycRequired:
- newStatus = PeerPushPaymentIncomingStatus.Aborted;
+ case PeerPushCreditStatus.PendingMergeKycRequired:
+ newStatus = PeerPushCreditStatus.Aborted;
break;
- case PeerPushPaymentIncomingStatus.PendingMerge:
- newStatus = PeerPushPaymentIncomingStatus.Aborted;
+ case PeerPushCreditStatus.PendingMerge:
+ newStatus = PeerPushCreditStatus.Aborted;
break;
- case PeerPushPaymentIncomingStatus.PendingWithdrawing:
- newStatus = PeerPushPaymentIncomingStatus.Aborted;
+ case PeerPushCreditStatus.PendingWithdrawing:
+ newStatus = PeerPushCreditStatus.Aborted;
break;
- case PeerPushPaymentIncomingStatus.Aborted:
+ case PeerPushCreditStatus.Aborted:
break;
- case PeerPushPaymentIncomingStatus.Failed:
+ case PeerPushCreditStatus.Failed:
break;
default:
assertUnreachable(pushCreditRec.status);
@@ -787,7 +785,7 @@ export async function abortPeerPushCreditTransaction(
const oldTxState = computePeerPushCreditTransactionState(pushCreditRec);
pushCreditRec.status = newStatus;
const newTxState = computePeerPushCreditTransactionState(pushCreditRec);
- await tx.peerPushPaymentIncoming.put(pushCreditRec);
+ await tx.peerPushCredit.put(pushCreditRec);
return {
oldTxState,
newTxState,
@@ -800,7 +798,7 @@ export async function abortPeerPushCreditTransaction(
export async function failPeerPushCreditTransaction(
ws: InternalWalletState,
- peerPushPaymentIncomingId: string,
+ peerPushCreditId: string,
) {
// We don't have any "aborting" states!
throw Error("can't run cancel-aborting on peer-push-credit transaction");
@@ -808,47 +806,47 @@ export async function failPeerPushCreditTransaction(
export async function resumePeerPushCreditTransaction(
ws: InternalWalletState,
- peerPushPaymentIncomingId: string,
+ peerPushCreditId: string,
) {
const taskId = constructTaskIdentifier({
tag: PendingTaskType.PeerPushCredit,
- peerPushPaymentIncomingId,
+ peerPushCreditId,
});
const transactionId = constructTransactionIdentifier({
tag: TransactionType.PeerPushCredit,
- peerPushPaymentIncomingId,
+ peerPushCreditId,
});
stopLongpolling(ws, taskId);
const transitionInfo = await ws.db
- .mktx((x) => [x.peerPushPaymentIncoming])
+ .mktx((x) => [x.peerPushCredit])
.runReadWrite(async (tx) => {
- const pushCreditRec = await tx.peerPushPaymentIncoming.get(
- peerPushPaymentIncomingId,
+ const pushCreditRec = await tx.peerPushCredit.get(
+ peerPushCreditId,
);
if (!pushCreditRec) {
- logger.warn(`peer push credit ${peerPushPaymentIncomingId} not found`);
+ logger.warn(`peer push credit ${peerPushCreditId} not found`);
return;
}
- let newStatus: PeerPushPaymentIncomingStatus | undefined = undefined;
+ let newStatus: PeerPushCreditStatus | undefined = undefined;
switch (pushCreditRec.status) {
- case PeerPushPaymentIncomingStatus.DialogProposed:
- case PeerPushPaymentIncomingStatus.Done:
- case PeerPushPaymentIncomingStatus.PendingMergeKycRequired:
- case PeerPushPaymentIncomingStatus.PendingMerge:
- case PeerPushPaymentIncomingStatus.PendingWithdrawing:
- case PeerPushPaymentIncomingStatus.SuspendedMerge:
- newStatus = PeerPushPaymentIncomingStatus.PendingMerge;
+ case PeerPushCreditStatus.DialogProposed:
+ case PeerPushCreditStatus.Done:
+ case PeerPushCreditStatus.PendingMergeKycRequired:
+ case PeerPushCreditStatus.PendingMerge:
+ case PeerPushCreditStatus.PendingWithdrawing:
+ case PeerPushCreditStatus.SuspendedMerge:
+ newStatus = PeerPushCreditStatus.PendingMerge;
break;
- case PeerPushPaymentIncomingStatus.SuspendedMergeKycRequired:
- newStatus = PeerPushPaymentIncomingStatus.PendingMergeKycRequired;
+ case PeerPushCreditStatus.SuspendedMergeKycRequired:
+ newStatus = PeerPushCreditStatus.PendingMergeKycRequired;
break;
- case PeerPushPaymentIncomingStatus.SuspendedWithdrawing:
+ case PeerPushCreditStatus.SuspendedWithdrawing:
// FIXME: resume underlying "internal-withdrawal" transaction.
- newStatus = PeerPushPaymentIncomingStatus.PendingWithdrawing;
+ newStatus = PeerPushCreditStatus.PendingWithdrawing;
break;
- case PeerPushPaymentIncomingStatus.Aborted:
+ case PeerPushCreditStatus.Aborted:
break;
- case PeerPushPaymentIncomingStatus.Failed:
+ case PeerPushCreditStatus.Failed:
break;
default:
assertUnreachable(pushCreditRec.status);
@@ -857,7 +855,7 @@ export async function resumePeerPushCreditTransaction(
const oldTxState = computePeerPushCreditTransactionState(pushCreditRec);
pushCreditRec.status = newStatus;
const newTxState = computePeerPushCreditTransactionState(pushCreditRec);
- await tx.peerPushPaymentIncoming.put(pushCreditRec);
+ await tx.peerPushCredit.put(pushCreditRec);
return {
oldTxState,
newTxState,
@@ -873,50 +871,50 @@ export function computePeerPushCreditTransactionState(
pushCreditRecord: PeerPushPaymentIncomingRecord,
): TransactionState {
switch (pushCreditRecord.status) {
- case PeerPushPaymentIncomingStatus.DialogProposed:
+ case PeerPushCreditStatus.DialogProposed:
return {
major: TransactionMajorState.Dialog,
minor: TransactionMinorState.Proposed,
};
- case PeerPushPaymentIncomingStatus.PendingMerge:
+ case PeerPushCreditStatus.PendingMerge:
return {
major: TransactionMajorState.Pending,
minor: TransactionMinorState.Merge,
};
- case PeerPushPaymentIncomingStatus.Done:
+ case PeerPushCreditStatus.Done:
return {
major: TransactionMajorState.Done,
};
- case PeerPushPaymentIncomingStatus.PendingMergeKycRequired:
+ case PeerPushCreditStatus.PendingMergeKycRequired:
return {
major: TransactionMajorState.Pending,
minor: TransactionMinorState.KycRequired,
};
- case PeerPushPaymentIncomingStatus.PendingWithdrawing:
+ case PeerPushCreditStatus.PendingWithdrawing:
return {
major: TransactionMajorState.Pending,
minor: TransactionMinorState.Withdraw,
};
- case PeerPushPaymentIncomingStatus.SuspendedMerge:
+ case PeerPushCreditStatus.SuspendedMerge:
return {
major: TransactionMajorState.Suspended,
minor: TransactionMinorState.Merge,
};
- case PeerPushPaymentIncomingStatus.SuspendedMergeKycRequired:
+ case PeerPushCreditStatus.SuspendedMergeKycRequired:
return {
major: TransactionMajorState.Suspended,
minor: TransactionMinorState.MergeKycRequired,
};
- case PeerPushPaymentIncomingStatus.SuspendedWithdrawing:
+ case PeerPushCreditStatus.SuspendedWithdrawing:
return {
major: TransactionMajorState.Suspended,
minor: TransactionMinorState.Withdraw,
};
- case PeerPushPaymentIncomingStatus.Aborted:
+ case PeerPushCreditStatus.Aborted:
return {
major: TransactionMajorState.Aborted,
};
- case PeerPushPaymentIncomingStatus.Failed:
+ case PeerPushCreditStatus.Failed:
return {
major: TransactionMajorState.Failed,
};
@@ -929,25 +927,25 @@ export function computePeerPushCreditTransactionActions(
pushCreditRecord: PeerPushPaymentIncomingRecord,
): TransactionAction[] {
switch (pushCreditRecord.status) {
- case PeerPushPaymentIncomingStatus.DialogProposed:
+ case PeerPushCreditStatus.DialogProposed:
return [TransactionAction.Delete];
- case PeerPushPaymentIncomingStatus.PendingMerge:
+ case PeerPushCreditStatus.PendingMerge:
return [TransactionAction.Abort, TransactionAction.Suspend];
- case PeerPushPaymentIncomingStatus.Done:
+ case PeerPushCreditStatus.Done:
return [TransactionAction.Delete];
- case PeerPushPaymentIncomingStatus.PendingMergeKycRequired:
+ case PeerPushCreditStatus.PendingMergeKycRequired:
return [TransactionAction.Abort, TransactionAction.Suspend];
- case PeerPushPaymentIncomingStatus.PendingWithdrawing:
+ case PeerPushCreditStatus.PendingWithdrawing:
return [TransactionAction.Suspend, TransactionAction.Fail];
- case PeerPushPaymentIncomingStatus.SuspendedMerge:
+ case PeerPushCreditStatus.SuspendedMerge:
return [TransactionAction.Resume, TransactionAction.Abort];
- case PeerPushPaymentIncomingStatus.SuspendedMergeKycRequired:
+ case PeerPushCreditStatus.SuspendedMergeKycRequired:
return [TransactionAction.Resume, TransactionAction.Abort];
- case PeerPushPaymentIncomingStatus.SuspendedWithdrawing:
+ case PeerPushCreditStatus.SuspendedWithdrawing:
return [TransactionAction.Resume, TransactionAction.Fail];
- case PeerPushPaymentIncomingStatus.Aborted:
+ case PeerPushCreditStatus.Aborted:
return [TransactionAction.Delete];
- case PeerPushPaymentIncomingStatus.Failed:
+ case PeerPushCreditStatus.Failed:
return [TransactionAction.Delete];
default:
assertUnreachable(pushCreditRecord.status);
diff --git a/packages/taler-wallet-core/src/operations/pay-peer-push-debit.ts b/packages/taler-wallet-core/src/operations/pay-peer-push-debit.ts
index 2349e5c4a..e80ffc059 100644
--- a/packages/taler-wallet-core/src/operations/pay-peer-push-debit.ts
+++ b/packages/taler-wallet-core/src/operations/pay-peer-push-debit.ts
@@ -51,8 +51,8 @@ import {
} from "@gnu-taler/taler-util/http";
import { EncryptContractRequest } from "../crypto/cryptoTypes.js";
import {
- PeerPushPaymentInitiationRecord,
- PeerPushPaymentInitiationStatus,
+ PeerPushDebitRecord,
+ PeerPushDebitStatus,
RefreshOperationStatus,
createRefreshGroup,
} from "../index.js";
@@ -107,7 +107,7 @@ export async function checkPeerPushDebit(
async function handlePurseCreationConflict(
ws: InternalWalletState,
- peerPushInitiation: PeerPushPaymentInitiationRecord,
+ peerPushInitiation: PeerPushDebitRecord,
resp: HttpResponse,
): Promise<TaskRunResult> {
const pursePub = peerPushInitiation.pursePub;
@@ -152,17 +152,15 @@ async function handlePurseCreationConflict(
}
await ws.db
- .mktx((x) => [x.peerPushPaymentInitiations])
+ .mktx((x) => [x.peerPushDebit])
.runReadWrite(async (tx) => {
- const myPpi = await tx.peerPushPaymentInitiations.get(
- peerPushInitiation.pursePub,
- );
+ const myPpi = await tx.peerPushDebit.get(peerPushInitiation.pursePub);
if (!myPpi) {
return;
}
switch (myPpi.status) {
- case PeerPushPaymentInitiationStatus.PendingCreatePurse:
- case PeerPushPaymentInitiationStatus.SuspendedCreatePurse: {
+ case PeerPushDebitStatus.PendingCreatePurse:
+ case PeerPushDebitStatus.SuspendedCreatePurse: {
const sel = coinSelRes.result;
myPpi.coinSel = {
coinPubs: sel.coins.map((x) => x.coinPub),
@@ -173,19 +171,36 @@ async function handlePurseCreationConflict(
default:
return;
}
- await tx.peerPushPaymentInitiations.put(myPpi);
+ await tx.peerPushDebit.put(myPpi);
});
return TaskRunResult.finished();
}
async function processPeerPushDebitCreateReserve(
ws: InternalWalletState,
- peerPushInitiation: PeerPushPaymentInitiationRecord,
+ peerPushInitiation: PeerPushDebitRecord,
): Promise<TaskRunResult> {
- logger.info("processing peer-push-debit pending(create-reserve)");
const pursePub = peerPushInitiation.pursePub;
const purseExpiration = peerPushInitiation.purseExpiration;
const hContractTerms = peerPushInitiation.contractTermsHash;
+ const transactionId = constructTransactionIdentifier({
+ tag: TransactionType.PeerPushDebit,
+ pursePub: pursePub,
+ });
+
+ logger.trace(`processing ${transactionId} pending(create-reserve)`);
+
+ const contractTermsRecord = await ws.db
+ .mktx((x) => [x.contractTerms])
+ .runReadOnly(async (tx) => {
+ return tx.contractTerms.get(hContractTerms);
+ });
+
+ if (!contractTermsRecord) {
+ throw Error(
+ `db invariant failed, contract terms for ${transactionId} missing`,
+ );
+ }
const purseSigResp = await ws.cryptoApi.signPurseCreation({
hContractTerms,
@@ -208,7 +223,7 @@ async function processPeerPushDebitCreateReserve(
});
const encryptContractRequest: EncryptContractRequest = {
- contractTerms: peerPushInitiation.contractTerms,
+ contractTerms: contractTermsRecord.contractTermsRaw,
mergePriv: peerPushInitiation.mergePriv,
pursePriv: peerPushInitiation.pursePriv,
pursePub: peerPushInitiation.pursePub,
@@ -284,8 +299,8 @@ async function processPeerPushDebitCreateReserve(
}
await transitionPeerPushDebitTransaction(ws, pursePub, {
- stFrom: PeerPushPaymentInitiationStatus.PendingCreatePurse,
- stTo: PeerPushPaymentInitiationStatus.PendingReady,
+ stFrom: PeerPushDebitStatus.PendingCreatePurse,
+ stTo: PeerPushDebitStatus.PendingReady,
});
return TaskRunResult.finished();
@@ -293,7 +308,7 @@ async function processPeerPushDebitCreateReserve(
async function processPeerPushDebitAbortingDeletePurse(
ws: InternalWalletState,
- peerPushInitiation: PeerPushPaymentInitiationRecord,
+ peerPushInitiation: PeerPushDebitRecord,
): Promise<TaskRunResult> {
const { pursePub, pursePriv } = peerPushInitiation;
const transactionId = constructTransactionIdentifier({
@@ -318,20 +333,18 @@ async function processPeerPushDebitAbortingDeletePurse(
const transitionInfo = await ws.db
.mktx((x) => [
- x.peerPushPaymentInitiations,
+ x.peerPushDebit,
x.refreshGroups,
x.denominations,
x.coinAvailability,
x.coins,
])
.runReadWrite(async (tx) => {
- const ppiRec = await tx.peerPushPaymentInitiations.get(pursePub);
+ const ppiRec = await tx.peerPushDebit.get(pursePub);
if (!ppiRec) {
return undefined;
}
- if (
- ppiRec.status !== PeerPushPaymentInitiationStatus.AbortingDeletePurse
- ) {
+ if (ppiRec.status !== PeerPushDebitStatus.AbortingDeletePurse) {
return undefined;
}
const currency = Amounts.currencyOf(ppiRec.amount);
@@ -352,9 +365,9 @@ async function processPeerPushDebitAbortingDeletePurse(
coinPubs,
RefreshReason.AbortPeerPushDebit,
);
- ppiRec.status = PeerPushPaymentInitiationStatus.AbortingRefresh;
+ ppiRec.status = PeerPushDebitStatus.AbortingRefresh;
ppiRec.abortRefreshGroupId = refresh.refreshGroupId;
- await tx.peerPushPaymentInitiations.put(ppiRec);
+ await tx.peerPushDebit.put(ppiRec);
const newTxState = computePeerPushDebitTransactionState(ppiRec);
return {
oldTxState,
@@ -367,8 +380,8 @@ async function processPeerPushDebitAbortingDeletePurse(
}
interface SimpleTransition {
- stFrom: PeerPushPaymentInitiationStatus;
- stTo: PeerPushPaymentInitiationStatus;
+ stFrom: PeerPushDebitStatus;
+ stTo: PeerPushDebitStatus;
}
async function transitionPeerPushDebitTransaction(
@@ -381,9 +394,9 @@ async function transitionPeerPushDebitTransaction(
pursePub,
});
const transitionInfo = await ws.db
- .mktx((x) => [x.peerPushPaymentInitiations])
+ .mktx((x) => [x.peerPushDebit])
.runReadWrite(async (tx) => {
- const ppiRec = await tx.peerPushPaymentInitiations.get(pursePub);
+ const ppiRec = await tx.peerPushDebit.get(pursePub);
if (!ppiRec) {
return undefined;
}
@@ -392,7 +405,7 @@ async function transitionPeerPushDebitTransaction(
}
const oldTxState = computePeerPushDebitTransactionState(ppiRec);
ppiRec.status = transitionSpec.stTo;
- await tx.peerPushPaymentInitiations.put(ppiRec);
+ await tx.peerPushDebit.put(ppiRec);
const newTxState = computePeerPushDebitTransactionState(ppiRec);
return {
oldTxState,
@@ -404,7 +417,7 @@ async function transitionPeerPushDebitTransaction(
async function processPeerPushDebitAbortingRefresh(
ws: InternalWalletState,
- peerPushInitiation: PeerPushPaymentInitiationRecord,
+ peerPushInitiation: PeerPushDebitRecord,
): Promise<TaskRunResult> {
const pursePub = peerPushInitiation.pursePub;
const abortRefreshGroupId = peerPushInitiation.abortRefreshGroupId;
@@ -414,33 +427,33 @@ async function processPeerPushDebitAbortingRefresh(
pursePub: peerPushInitiation.pursePub,
});
const transitionInfo = await ws.db
- .mktx((x) => [x.refreshGroups, x.peerPushPaymentInitiations])
+ .mktx((x) => [x.refreshGroups, x.peerPushDebit])
.runReadWrite(async (tx) => {
const refreshGroup = await tx.refreshGroups.get(abortRefreshGroupId);
- let newOpState: PeerPushPaymentInitiationStatus | undefined;
+ let newOpState: PeerPushDebitStatus | undefined;
if (!refreshGroup) {
// Maybe it got manually deleted? Means that we should
// just go into failed.
logger.warn("no aborting refresh group found for deposit group");
- newOpState = PeerPushPaymentInitiationStatus.Failed;
+ newOpState = PeerPushDebitStatus.Failed;
} else {
if (refreshGroup.operationStatus === RefreshOperationStatus.Finished) {
- newOpState = PeerPushPaymentInitiationStatus.Aborted;
+ newOpState = PeerPushDebitStatus.Aborted;
} else if (
refreshGroup.operationStatus === RefreshOperationStatus.Failed
) {
- newOpState = PeerPushPaymentInitiationStatus.Failed;
+ newOpState = PeerPushDebitStatus.Failed;
}
}
if (newOpState) {
- const newDg = await tx.peerPushPaymentInitiations.get(pursePub);
+ const newDg = await tx.peerPushDebit.get(pursePub);
if (!newDg) {
return;
}
const oldTxState = computePeerPushDebitTransactionState(newDg);
newDg.status = newOpState;
const newTxState = computePeerPushDebitTransactionState(newDg);
- await tx.peerPushPaymentInitiations.put(newDg);
+ await tx.peerPushDebit.put(newDg);
return { oldTxState, newTxState };
}
return undefined;
@@ -455,7 +468,7 @@ async function processPeerPushDebitAbortingRefresh(
*/
async function processPeerPushDebitReady(
ws: InternalWalletState,
- peerPushInitiation: PeerPushPaymentInitiationRecord,
+ peerPushInitiation: PeerPushDebitRecord,
): Promise<TaskRunResult> {
logger.info("processing peer-push-debit pending(ready)");
const pursePub = peerPushInitiation.pursePub;
@@ -488,8 +501,8 @@ async function processPeerPushDebitReady(
ws,
peerPushInitiation.pursePub,
{
- stFrom: PeerPushPaymentInitiationStatus.PendingReady,
- stTo: PeerPushPaymentInitiationStatus.Done,
+ stFrom: PeerPushDebitStatus.PendingReady,
+ stTo: PeerPushDebitStatus.Done,
},
);
return {
@@ -501,8 +514,8 @@ async function processPeerPushDebitReady(
ws,
peerPushInitiation.pursePub,
{
- stFrom: PeerPushPaymentInitiationStatus.PendingReady,
- stTo: PeerPushPaymentInitiationStatus.Expired,
+ stFrom: PeerPushDebitStatus.PendingReady,
+ stTo: PeerPushDebitStatus.Expired,
},
);
return {
@@ -528,9 +541,9 @@ export async function processPeerPushDebit(
pursePub: string,
): Promise<TaskRunResult> {
const peerPushInitiation = await ws.db
- .mktx((x) => [x.peerPushPaymentInitiations])
+ .mktx((x) => [x.peerPushDebit])
.runReadOnly(async (tx) => {
- return tx.peerPushPaymentInitiations.get(pursePub);
+ return tx.peerPushDebit.get(pursePub);
});
if (!peerPushInitiation) {
throw Error("peer push payment not found");
@@ -550,13 +563,13 @@ export async function processPeerPushDebit(
}
switch (peerPushInitiation.status) {
- case PeerPushPaymentInitiationStatus.PendingCreatePurse:
+ case PeerPushDebitStatus.PendingCreatePurse:
return processPeerPushDebitCreateReserve(ws, peerPushInitiation);
- case PeerPushPaymentInitiationStatus.PendingReady:
+ case PeerPushDebitStatus.PendingReady:
return processPeerPushDebitReady(ws, peerPushInitiation);
- case PeerPushPaymentInitiationStatus.AbortingDeletePurse:
+ case PeerPushDebitStatus.AbortingDeletePurse:
return processPeerPushDebitAbortingDeletePurse(ws, peerPushInitiation);
- case PeerPushPaymentInitiationStatus.AbortingRefresh:
+ case PeerPushDebitStatus.AbortingRefresh:
return processPeerPushDebitAbortingRefresh(ws, peerPushInitiation);
default: {
const txState = computePeerPushDebitTransactionState(peerPushInitiation);
@@ -626,7 +639,7 @@ export async function initiatePeerPushDebit(
x.coinAvailability,
x.denominations,
x.refreshGroups,
- x.peerPushPaymentInitiations,
+ x.peerPushDebit,
])
.runReadWrite(async (tx) => {
// FIXME: Instead of directly doing a spendCoin here,
@@ -645,7 +658,7 @@ export async function initiatePeerPushDebit(
refreshReason: RefreshReason.PayPeerPush,
});
- const ppi: PeerPushPaymentInitiationRecord = {
+ const ppi: PeerPushDebitRecord = {
amount: Amounts.stringify(instructedAmount),
contractPriv: contractKeyPair.priv,
contractPub: contractKeyPair.pub,
@@ -657,8 +670,7 @@ export async function initiatePeerPushDebit(
pursePriv: pursePair.priv,
pursePub: pursePair.pub,
timestampCreated: TalerPreciseTimestamp.now(),
- status: PeerPushPaymentInitiationStatus.PendingCreatePurse,
- contractTerms: contractTerms,
+ status: PeerPushDebitStatus.PendingCreatePurse,
contractEncNonce,
coinSel: {
coinPubs: sel.coins.map((x) => x.coinPub),
@@ -667,7 +679,7 @@ export async function initiatePeerPushDebit(
totalCost: Amounts.stringify(totalAmount),
};
- await tx.peerPushPaymentInitiations.add(ppi);
+ await tx.peerPushDebit.add(ppi);
await tx.contractTerms.put({
h: hContractTerms,
@@ -701,32 +713,32 @@ export async function initiatePeerPushDebit(
}
export function computePeerPushDebitTransactionActions(
- ppiRecord: PeerPushPaymentInitiationRecord,
+ ppiRecord: PeerPushDebitRecord,
): TransactionAction[] {
switch (ppiRecord.status) {
- case PeerPushPaymentInitiationStatus.PendingCreatePurse:
+ case PeerPushDebitStatus.PendingCreatePurse:
return [TransactionAction.Abort, TransactionAction.Suspend];
- case PeerPushPaymentInitiationStatus.PendingReady:
+ case PeerPushDebitStatus.PendingReady:
return [TransactionAction.Abort, TransactionAction.Suspend];
- case PeerPushPaymentInitiationStatus.Aborted:
+ case PeerPushDebitStatus.Aborted:
return [TransactionAction.Delete];
- case PeerPushPaymentInitiationStatus.AbortingDeletePurse:
+ case PeerPushDebitStatus.AbortingDeletePurse:
return [TransactionAction.Suspend, TransactionAction.Fail];
- case PeerPushPaymentInitiationStatus.AbortingRefresh:
+ case PeerPushDebitStatus.AbortingRefresh:
return [TransactionAction.Suspend, TransactionAction.Fail];
- case PeerPushPaymentInitiationStatus.SuspendedAbortingDeletePurse:
+ case PeerPushDebitStatus.SuspendedAbortingDeletePurse:
return [TransactionAction.Resume, TransactionAction.Fail];
- case PeerPushPaymentInitiationStatus.SuspendedAbortingRefresh:
+ case PeerPushDebitStatus.SuspendedAbortingRefresh:
return [TransactionAction.Resume, TransactionAction.Fail];
- case PeerPushPaymentInitiationStatus.SuspendedCreatePurse:
+ case PeerPushDebitStatus.SuspendedCreatePurse:
return [TransactionAction.Resume, TransactionAction.Abort];
- case PeerPushPaymentInitiationStatus.SuspendedReady:
+ case PeerPushDebitStatus.SuspendedReady:
return [TransactionAction.Suspend, TransactionAction.Abort];
- case PeerPushPaymentInitiationStatus.Done:
+ case PeerPushDebitStatus.Done:
return [TransactionAction.Delete];
- case PeerPushPaymentInitiationStatus.Expired:
+ case PeerPushDebitStatus.Expired:
return [TransactionAction.Delete];
- case PeerPushPaymentInitiationStatus.Failed:
+ case PeerPushDebitStatus.Failed:
return [TransactionAction.Delete];
}
}
@@ -745,32 +757,32 @@ export async function abortPeerPushDebitTransaction(
});
stopLongpolling(ws, taskId);
const transitionInfo = await ws.db
- .mktx((x) => [x.peerPushPaymentInitiations])
+ .mktx((x) => [x.peerPushDebit])
.runReadWrite(async (tx) => {
- const pushDebitRec = await tx.peerPushPaymentInitiations.get(pursePub);
+ const pushDebitRec = await tx.peerPushDebit.get(pursePub);
if (!pushDebitRec) {
logger.warn(`peer push debit ${pursePub} not found`);
return;
}
- let newStatus: PeerPushPaymentInitiationStatus | undefined = undefined;
+ let newStatus: PeerPushDebitStatus | undefined = undefined;
switch (pushDebitRec.status) {
- case PeerPushPaymentInitiationStatus.PendingReady:
- case PeerPushPaymentInitiationStatus.SuspendedReady:
- newStatus = PeerPushPaymentInitiationStatus.AbortingDeletePurse;
+ case PeerPushDebitStatus.PendingReady:
+ case PeerPushDebitStatus.SuspendedReady:
+ newStatus = PeerPushDebitStatus.AbortingDeletePurse;
break;
- case PeerPushPaymentInitiationStatus.SuspendedCreatePurse:
- case PeerPushPaymentInitiationStatus.PendingCreatePurse:
+ case PeerPushDebitStatus.SuspendedCreatePurse:
+ case PeerPushDebitStatus.PendingCreatePurse:
// Network request might already be in-flight!
- newStatus = PeerPushPaymentInitiationStatus.AbortingDeletePurse;
+ newStatus = PeerPushDebitStatus.AbortingDeletePurse;
break;
- case PeerPushPaymentInitiationStatus.SuspendedAbortingRefresh:
- case PeerPushPaymentInitiationStatus.SuspendedAbortingDeletePurse:
- case PeerPushPaymentInitiationStatus.AbortingRefresh:
- case PeerPushPaymentInitiationStatus.Done:
- case PeerPushPaymentInitiationStatus.AbortingDeletePurse:
- case PeerPushPaymentInitiationStatus.Aborted:
- case PeerPushPaymentInitiationStatus.Expired:
- case PeerPushPaymentInitiationStatus.Failed:
+ case PeerPushDebitStatus.SuspendedAbortingRefresh:
+ case PeerPushDebitStatus.SuspendedAbortingDeletePurse:
+ case PeerPushDebitStatus.AbortingRefresh:
+ case PeerPushDebitStatus.Done:
+ case PeerPushDebitStatus.AbortingDeletePurse:
+ case PeerPushDebitStatus.Aborted:
+ case PeerPushDebitStatus.Expired:
+ case PeerPushDebitStatus.Failed:
// Do nothing
break;
default:
@@ -780,7 +792,7 @@ export async function abortPeerPushDebitTransaction(
const oldTxState = computePeerPushDebitTransactionState(pushDebitRec);
pushDebitRec.status = newStatus;
const newTxState = computePeerPushDebitTransactionState(pushDebitRec);
- await tx.peerPushPaymentInitiations.put(pushDebitRec);
+ await tx.peerPushDebit.put(pushDebitRec);
return {
oldTxState,
newTxState,
@@ -805,32 +817,32 @@ export async function failPeerPushDebitTransaction(
});
stopLongpolling(ws, taskId);
const transitionInfo = await ws.db
- .mktx((x) => [x.peerPushPaymentInitiations])
+ .mktx((x) => [x.peerPushDebit])
.runReadWrite(async (tx) => {
- const pushDebitRec = await tx.peerPushPaymentInitiations.get(pursePub);
+ const pushDebitRec = await tx.peerPushDebit.get(pursePub);
if (!pushDebitRec) {
logger.warn(`peer push debit ${pursePub} not found`);
return;
}
- let newStatus: PeerPushPaymentInitiationStatus | undefined = undefined;
+ let newStatus: PeerPushDebitStatus | undefined = undefined;
switch (pushDebitRec.status) {
- case PeerPushPaymentInitiationStatus.AbortingRefresh:
- case PeerPushPaymentInitiationStatus.SuspendedAbortingRefresh:
+ case PeerPushDebitStatus.AbortingRefresh:
+ case PeerPushDebitStatus.SuspendedAbortingRefresh:
// FIXME: What to do about the refresh group?
- newStatus = PeerPushPaymentInitiationStatus.Failed;
+ newStatus = PeerPushDebitStatus.Failed;
break;
- case PeerPushPaymentInitiationStatus.AbortingDeletePurse:
- case PeerPushPaymentInitiationStatus.SuspendedAbortingDeletePurse:
- case PeerPushPaymentInitiationStatus.PendingReady:
- case PeerPushPaymentInitiationStatus.SuspendedReady:
- case PeerPushPaymentInitiationStatus.SuspendedCreatePurse:
- case PeerPushPaymentInitiationStatus.PendingCreatePurse:
- newStatus = PeerPushPaymentInitiationStatus.Failed;
+ case PeerPushDebitStatus.AbortingDeletePurse:
+ case PeerPushDebitStatus.SuspendedAbortingDeletePurse:
+ case PeerPushDebitStatus.PendingReady:
+ case PeerPushDebitStatus.SuspendedReady:
+ case PeerPushDebitStatus.SuspendedCreatePurse:
+ case PeerPushDebitStatus.PendingCreatePurse:
+ newStatus = PeerPushDebitStatus.Failed;
break;
- case PeerPushPaymentInitiationStatus.Done:
- case PeerPushPaymentInitiationStatus.Aborted:
- case PeerPushPaymentInitiationStatus.Failed:
- case PeerPushPaymentInitiationStatus.Expired:
+ case PeerPushDebitStatus.Done:
+ case PeerPushDebitStatus.Aborted:
+ case PeerPushDebitStatus.Failed:
+ case PeerPushDebitStatus.Expired:
// Do nothing
break;
default:
@@ -840,7 +852,7 @@ export async function failPeerPushDebitTransaction(
const oldTxState = computePeerPushDebitTransactionState(pushDebitRec);
pushDebitRec.status = newStatus;
const newTxState = computePeerPushDebitTransactionState(pushDebitRec);
- await tx.peerPushPaymentInitiations.put(pushDebitRec);
+ await tx.peerPushDebit.put(pushDebitRec);
return {
oldTxState,
newTxState,
@@ -865,36 +877,35 @@ export async function suspendPeerPushDebitTransaction(
});
stopLongpolling(ws, taskId);
const transitionInfo = await ws.db
- .mktx((x) => [x.peerPushPaymentInitiations])
+ .mktx((x) => [x.peerPushDebit])
.runReadWrite(async (tx) => {
- const pushDebitRec = await tx.peerPushPaymentInitiations.get(pursePub);
+ const pushDebitRec = await tx.peerPushDebit.get(pursePub);
if (!pushDebitRec) {
logger.warn(`peer push debit ${pursePub} not found`);
return;
}
- let newStatus: PeerPushPaymentInitiationStatus | undefined = undefined;
+ let newStatus: PeerPushDebitStatus | undefined = undefined;
switch (pushDebitRec.status) {
- case PeerPushPaymentInitiationStatus.PendingCreatePurse:
- newStatus = PeerPushPaymentInitiationStatus.SuspendedCreatePurse;
+ case PeerPushDebitStatus.PendingCreatePurse:
+ newStatus = PeerPushDebitStatus.SuspendedCreatePurse;
break;
- case PeerPushPaymentInitiationStatus.AbortingRefresh:
- newStatus = PeerPushPaymentInitiationStatus.SuspendedAbortingRefresh;
+ case PeerPushDebitStatus.AbortingRefresh:
+ newStatus = PeerPushDebitStatus.SuspendedAbortingRefresh;
break;
- case PeerPushPaymentInitiationStatus.AbortingDeletePurse:
- newStatus =
- PeerPushPaymentInitiationStatus.SuspendedAbortingDeletePurse;
+ case PeerPushDebitStatus.AbortingDeletePurse:
+ newStatus = PeerPushDebitStatus.SuspendedAbortingDeletePurse;
break;
- case PeerPushPaymentInitiationStatus.PendingReady:
- newStatus = PeerPushPaymentInitiationStatus.SuspendedReady;
+ case PeerPushDebitStatus.PendingReady:
+ newStatus = PeerPushDebitStatus.SuspendedReady;
break;
- case PeerPushPaymentInitiationStatus.SuspendedAbortingDeletePurse:
- case PeerPushPaymentInitiationStatus.SuspendedAbortingRefresh:
- case PeerPushPaymentInitiationStatus.SuspendedReady:
- case PeerPushPaymentInitiationStatus.SuspendedCreatePurse:
- case PeerPushPaymentInitiationStatus.Done:
- case PeerPushPaymentInitiationStatus.Aborted:
- case PeerPushPaymentInitiationStatus.Failed:
- case PeerPushPaymentInitiationStatus.Expired:
+ case PeerPushDebitStatus.SuspendedAbortingDeletePurse:
+ case PeerPushDebitStatus.SuspendedAbortingRefresh:
+ case PeerPushDebitStatus.SuspendedReady:
+ case PeerPushDebitStatus.SuspendedCreatePurse:
+ case PeerPushDebitStatus.Done:
+ case PeerPushDebitStatus.Aborted:
+ case PeerPushDebitStatus.Failed:
+ case PeerPushDebitStatus.Expired:
// Do nothing
break;
default:
@@ -904,7 +915,7 @@ export async function suspendPeerPushDebitTransaction(
const oldTxState = computePeerPushDebitTransactionState(pushDebitRec);
pushDebitRec.status = newStatus;
const newTxState = computePeerPushDebitTransactionState(pushDebitRec);
- await tx.peerPushPaymentInitiations.put(pushDebitRec);
+ await tx.peerPushDebit.put(pushDebitRec);
return {
oldTxState,
newTxState,
@@ -929,35 +940,35 @@ export async function resumePeerPushDebitTransaction(
});
stopLongpolling(ws, taskId);
const transitionInfo = await ws.db
- .mktx((x) => [x.peerPushPaymentInitiations])
+ .mktx((x) => [x.peerPushDebit])
.runReadWrite(async (tx) => {
- const pushDebitRec = await tx.peerPushPaymentInitiations.get(pursePub);
+ const pushDebitRec = await tx.peerPushDebit.get(pursePub);
if (!pushDebitRec) {
logger.warn(`peer push debit ${pursePub} not found`);
return;
}
- let newStatus: PeerPushPaymentInitiationStatus | undefined = undefined;
+ let newStatus: PeerPushDebitStatus | undefined = undefined;
switch (pushDebitRec.status) {
- case PeerPushPaymentInitiationStatus.SuspendedAbortingDeletePurse:
- newStatus = PeerPushPaymentInitiationStatus.AbortingDeletePurse;
+ case PeerPushDebitStatus.SuspendedAbortingDeletePurse:
+ newStatus = PeerPushDebitStatus.AbortingDeletePurse;
break;
- case PeerPushPaymentInitiationStatus.SuspendedAbortingRefresh:
- newStatus = PeerPushPaymentInitiationStatus.AbortingRefresh;
+ case PeerPushDebitStatus.SuspendedAbortingRefresh:
+ newStatus = PeerPushDebitStatus.AbortingRefresh;
break;
- case PeerPushPaymentInitiationStatus.SuspendedReady:
- newStatus = PeerPushPaymentInitiationStatus.PendingReady;
+ case PeerPushDebitStatus.SuspendedReady:
+ newStatus = PeerPushDebitStatus.PendingReady;
break;
- case PeerPushPaymentInitiationStatus.SuspendedCreatePurse:
- newStatus = PeerPushPaymentInitiationStatus.PendingCreatePurse;
+ case PeerPushDebitStatus.SuspendedCreatePurse:
+ newStatus = PeerPushDebitStatus.PendingCreatePurse;
break;
- case PeerPushPaymentInitiationStatus.PendingCreatePurse:
- case PeerPushPaymentInitiationStatus.AbortingRefresh:
- case PeerPushPaymentInitiationStatus.AbortingDeletePurse:
- case PeerPushPaymentInitiationStatus.PendingReady:
- case PeerPushPaymentInitiationStatus.Done:
- case PeerPushPaymentInitiationStatus.Aborted:
- case PeerPushPaymentInitiationStatus.Failed:
- case PeerPushPaymentInitiationStatus.Expired:
+ case PeerPushDebitStatus.PendingCreatePurse:
+ case PeerPushDebitStatus.AbortingRefresh:
+ case PeerPushDebitStatus.AbortingDeletePurse:
+ case PeerPushDebitStatus.PendingReady:
+ case PeerPushDebitStatus.Done:
+ case PeerPushDebitStatus.Aborted:
+ case PeerPushDebitStatus.Failed:
+ case PeerPushDebitStatus.Expired:
// Do nothing
break;
default:
@@ -967,7 +978,7 @@ export async function resumePeerPushDebitTransaction(
const oldTxState = computePeerPushDebitTransactionState(pushDebitRec);
pushDebitRec.status = newStatus;
const newTxState = computePeerPushDebitTransactionState(pushDebitRec);
- await tx.peerPushPaymentInitiations.put(pushDebitRec);
+ await tx.peerPushDebit.put(pushDebitRec);
return {
oldTxState,
newTxState,
@@ -980,62 +991,62 @@ export async function resumePeerPushDebitTransaction(
}
export function computePeerPushDebitTransactionState(
- ppiRecord: PeerPushPaymentInitiationRecord,
+ ppiRecord: PeerPushDebitRecord,
): TransactionState {
switch (ppiRecord.status) {
- case PeerPushPaymentInitiationStatus.PendingCreatePurse:
+ case PeerPushDebitStatus.PendingCreatePurse:
return {
major: TransactionMajorState.Pending,
minor: TransactionMinorState.CreatePurse,
};
- case PeerPushPaymentInitiationStatus.PendingReady:
+ case PeerPushDebitStatus.PendingReady:
return {
major: TransactionMajorState.Pending,
minor: TransactionMinorState.Ready,
};
- case PeerPushPaymentInitiationStatus.Aborted:
+ case PeerPushDebitStatus.Aborted:
return {
major: TransactionMajorState.Aborted,
};
- case PeerPushPaymentInitiationStatus.AbortingDeletePurse:
+ case PeerPushDebitStatus.AbortingDeletePurse:
return {
major: TransactionMajorState.Aborting,
minor: TransactionMinorState.DeletePurse,
};
- case PeerPushPaymentInitiationStatus.AbortingRefresh:
+ case PeerPushDebitStatus.AbortingRefresh:
return {
major: TransactionMajorState.Aborting,
minor: TransactionMinorState.Refresh,
};
- case PeerPushPaymentInitiationStatus.SuspendedAbortingDeletePurse:
+ case PeerPushDebitStatus.SuspendedAbortingDeletePurse:
return {
major: TransactionMajorState.SuspendedAborting,
minor: TransactionMinorState.DeletePurse,
};
- case PeerPushPaymentInitiationStatus.SuspendedAbortingRefresh:
+ case PeerPushDebitStatus.SuspendedAbortingRefresh:
return {
major: TransactionMajorState.SuspendedAborting,
minor: TransactionMinorState.Refresh,
};
- case PeerPushPaymentInitiationStatus.SuspendedCreatePurse:
+ case PeerPushDebitStatus.SuspendedCreatePurse:
return {
major: TransactionMajorState.Suspended,
minor: TransactionMinorState.CreatePurse,
};
- case PeerPushPaymentInitiationStatus.SuspendedReady:
+ case PeerPushDebitStatus.SuspendedReady:
return {
major: TransactionMajorState.Suspended,
minor: TransactionMinorState.Ready,
};
- case PeerPushPaymentInitiationStatus.Done:
+ case PeerPushDebitStatus.Done:
return {
major: TransactionMajorState.Done,
};
- case PeerPushPaymentInitiationStatus.Failed:
+ case PeerPushDebitStatus.Failed:
return {
major: TransactionMajorState.Failed,
};
- case PeerPushPaymentInitiationStatus.Expired:
+ case PeerPushDebitStatus.Expired:
return {
major: TransactionMajorState.Expired,
};
diff --git a/packages/taler-wallet-core/src/operations/pending.ts b/packages/taler-wallet-core/src/operations/pending.ts
index e37e45c16..6115f848b 100644
--- a/packages/taler-wallet-core/src/operations/pending.ts
+++ b/packages/taler-wallet-core/src/operations/pending.ts
@@ -26,11 +26,10 @@ import {
WalletStoresV1,
BackupProviderStateTag,
RefreshCoinStatus,
- OperationStatusRange,
- PeerPushPaymentInitiationStatus,
+ PeerPushDebitStatus,
PeerPullDebitRecordStatus,
- PeerPushPaymentIncomingStatus,
- PeerPullPaymentInitiationStatus,
+ PeerPushCreditStatus,
+ PeerPullPaymentCreditStatus,
WithdrawalGroupStatus,
RewardRecordStatus,
DepositOperationStatus,
@@ -39,13 +38,15 @@ import {
DepositGroupRecord,
RewardRecord,
PurchaseRecord,
- PeerPullPaymentInitiationRecord,
+ PeerPullCreditRecord,
PeerPullPaymentIncomingRecord,
- PeerPushPaymentInitiationRecord,
+ PeerPushDebitRecord,
PeerPushPaymentIncomingRecord,
RefundGroupRecord,
RefundGroupStatus,
ExchangeEntryDbUpdateStatus,
+ RefreshOperationStatus,
+ DepositElementStatus,
} from "../db.js";
import {
PendingOperationsResponse,
@@ -136,8 +137,8 @@ export async function iterRecordsForRefresh(
let refreshGroups: RefreshGroupRecord[];
if (filter.onlyState === "nonfinal") {
const keyRange = GlobalIDB.KeyRange.bound(
- OperationStatusRange.ACTIVE_START,
- OperationStatusRange.ACTIVE_END,
+ RefreshOperationStatus.Pending,
+ RefreshOperationStatus.Suspended,
);
refreshGroups = await tx.refreshGroups.indexes.byStatus.getAll(keyRange);
} else {
@@ -277,8 +278,8 @@ async function gatherDepositPending(
): Promise<void> {
await iterRecordsForDeposit(tx, { onlyState: "nonfinal" }, async (dg) => {
let deposited = true;
- for (const d of dg.depositedPerCoin) {
- if (!d) {
+ for (const d of dg.statusPerCoin) {
+ if (d === DepositElementStatus.DepositPending) {
deposited = false;
}
}
@@ -470,28 +471,26 @@ async function gatherBackupPending(
export async function iterRecordsForPeerPullInitiation(
tx: GetReadOnlyAccess<{
- peerPullPaymentInitiations: typeof WalletStoresV1.peerPullPaymentInitiations;
+ peerPullCredit: typeof WalletStoresV1.peerPullCredit;
}>,
filter: TransactionRecordFilter,
- f: (r: PeerPullPaymentInitiationRecord) => Promise<void>,
+ f: (r: PeerPullCreditRecord) => Promise<void>,
): Promise<void> {
if (filter.onlyState === "nonfinal") {
const keyRange = GlobalIDB.KeyRange.bound(
- PeerPullPaymentInitiationStatus.PendingCreatePurse,
- PeerPullPaymentInitiationStatus.AbortingDeletePurse,
+ PeerPullPaymentCreditStatus.PendingCreatePurse,
+ PeerPullPaymentCreditStatus.AbortingDeletePurse,
);
- await tx.peerPullPaymentInitiations.indexes.byStatus
- .iter(keyRange)
- .forEachAsync(f);
+ await tx.peerPullCredit.indexes.byStatus.iter(keyRange).forEachAsync(f);
} else {
- await tx.peerPullPaymentInitiations.indexes.byStatus.iter().forEachAsync(f);
+ await tx.peerPullCredit.indexes.byStatus.iter().forEachAsync(f);
}
}
async function gatherPeerPullInitiationPending(
ws: InternalWalletState,
tx: GetReadOnlyAccess<{
- peerPullPaymentInitiations: typeof WalletStoresV1.peerPullPaymentInitiations;
+ peerPullCredit: typeof WalletStoresV1.peerPullCredit;
operationRetries: typeof WalletStoresV1.operationRetries;
}>,
now: AbsoluteTime,
@@ -518,7 +517,7 @@ async function gatherPeerPullInitiationPending(
export async function iterRecordsForPeerPullDebit(
tx: GetReadOnlyAccess<{
- peerPullPaymentIncoming: typeof WalletStoresV1.peerPullPaymentIncoming;
+ peerPullDebit: typeof WalletStoresV1.peerPullDebit;
}>,
filter: TransactionRecordFilter,
f: (r: PeerPullPaymentIncomingRecord) => Promise<void>,
@@ -528,18 +527,16 @@ export async function iterRecordsForPeerPullDebit(
PeerPullDebitRecordStatus.PendingDeposit,
PeerPullDebitRecordStatus.AbortingRefresh,
);
- await tx.peerPullPaymentIncoming.indexes.byStatus
- .iter(keyRange)
- .forEachAsync(f);
+ await tx.peerPullDebit.indexes.byStatus.iter(keyRange).forEachAsync(f);
} else {
- await tx.peerPullPaymentIncoming.indexes.byStatus.iter().forEachAsync(f);
+ await tx.peerPullDebit.indexes.byStatus.iter().forEachAsync(f);
}
}
async function gatherPeerPullDebitPending(
ws: InternalWalletState,
tx: GetReadOnlyAccess<{
- peerPullPaymentIncoming: typeof WalletStoresV1.peerPullPaymentIncoming;
+ peerPullDebit: typeof WalletStoresV1.peerPullDebit;
operationRetries: typeof WalletStoresV1.operationRetries;
}>,
now: AbsoluteTime,
@@ -558,7 +555,7 @@ async function gatherPeerPullDebitPending(
...getPendingCommon(ws, opId, timestampDue),
givesLifeness: true,
retryInfo: retryRecord?.retryInfo,
- peerPullPaymentIncomingId: pi.peerPullPaymentIncomingId,
+ peerPullDebitId: pi.peerPullDebitId,
});
},
);
@@ -566,28 +563,26 @@ async function gatherPeerPullDebitPending(
export async function iterRecordsForPeerPushInitiation(
tx: GetReadOnlyAccess<{
- peerPushPaymentInitiations: typeof WalletStoresV1.peerPushPaymentInitiations;
+ peerPushDebit: typeof WalletStoresV1.peerPushDebit;
}>,
filter: TransactionRecordFilter,
- f: (r: PeerPushPaymentInitiationRecord) => Promise<void>,
+ f: (r: PeerPushDebitRecord) => Promise<void>,
): Promise<void> {
if (filter.onlyState === "nonfinal") {
const keyRange = GlobalIDB.KeyRange.bound(
- PeerPushPaymentInitiationStatus.PendingCreatePurse,
- PeerPushPaymentInitiationStatus.AbortingRefresh,
+ PeerPushDebitStatus.PendingCreatePurse,
+ PeerPushDebitStatus.AbortingRefresh,
);
- await tx.peerPushPaymentInitiations.indexes.byStatus
- .iter(keyRange)
- .forEachAsync(f);
+ await tx.peerPushDebit.indexes.byStatus.iter(keyRange).forEachAsync(f);
} else {
- await tx.peerPushPaymentInitiations.indexes.byStatus.iter().forEachAsync(f);
+ await tx.peerPushDebit.indexes.byStatus.iter().forEachAsync(f);
}
}
async function gatherPeerPushInitiationPending(
ws: InternalWalletState,
tx: GetReadOnlyAccess<{
- peerPushPaymentInitiations: typeof WalletStoresV1.peerPushPaymentInitiations;
+ peerPushDebit: typeof WalletStoresV1.peerPushDebit;
operationRetries: typeof WalletStoresV1.operationRetries;
}>,
now: AbsoluteTime,
@@ -614,36 +609,34 @@ async function gatherPeerPushInitiationPending(
export async function iterRecordsForPeerPushCredit(
tx: GetReadOnlyAccess<{
- peerPushPaymentIncoming: typeof WalletStoresV1.peerPushPaymentIncoming;
+ peerPushCredit: typeof WalletStoresV1.peerPushCredit;
}>,
filter: TransactionRecordFilter,
f: (r: PeerPushPaymentIncomingRecord) => Promise<void>,
): Promise<void> {
if (filter.onlyState === "nonfinal") {
const keyRange = GlobalIDB.KeyRange.bound(
- PeerPushPaymentIncomingStatus.PendingMerge,
- PeerPushPaymentIncomingStatus.PendingWithdrawing,
+ PeerPushCreditStatus.PendingMerge,
+ PeerPushCreditStatus.PendingWithdrawing,
);
- await tx.peerPushPaymentIncoming.indexes.byStatus
- .iter(keyRange)
- .forEachAsync(f);
+ await tx.peerPushCredit.indexes.byStatus.iter(keyRange).forEachAsync(f);
} else {
- await tx.peerPushPaymentIncoming.indexes.byStatus.iter().forEachAsync(f);
+ await tx.peerPushCredit.indexes.byStatus.iter().forEachAsync(f);
}
}
async function gatherPeerPushCreditPending(
ws: InternalWalletState,
tx: GetReadOnlyAccess<{
- peerPushPaymentIncoming: typeof WalletStoresV1.peerPushPaymentIncoming;
+ peerPushCredit: typeof WalletStoresV1.peerPushCredit;
operationRetries: typeof WalletStoresV1.operationRetries;
}>,
now: AbsoluteTime,
resp: PendingOperationsResponse,
): Promise<void> {
const keyRange = GlobalIDB.KeyRange.bound(
- PeerPushPaymentIncomingStatus.PendingMerge,
- PeerPushPaymentIncomingStatus.PendingWithdrawing,
+ PeerPushCreditStatus.PendingMerge,
+ PeerPushCreditStatus.PendingWithdrawing,
);
await iterRecordsForPeerPushCredit(
tx,
@@ -658,7 +651,7 @@ async function gatherPeerPushCreditPending(
...getPendingCommon(ws, opId, timestampDue),
givesLifeness: true,
retryInfo: retryRecord?.retryInfo,
- peerPushPaymentIncomingId: pi.peerPushPaymentIncomingId,
+ peerPushCreditId: pi.peerPushCreditId,
});
},
);
@@ -682,10 +675,10 @@ export async function getPendingOperations(
x.depositGroups,
x.recoupGroups,
x.operationRetries,
- x.peerPullPaymentInitiations,
- x.peerPushPaymentInitiations,
- x.peerPullPaymentIncoming,
- x.peerPushPaymentIncoming,
+ x.peerPullCredit,
+ x.peerPushDebit,
+ x.peerPullDebit,
+ x.peerPushCredit,
])
.runReadWrite(async (tx) => {
const resp: PendingOperationsResponse = {
diff --git a/packages/taler-wallet-core/src/operations/refresh.ts b/packages/taler-wallet-core/src/operations/refresh.ts
index fb356f0fc..75adbc860 100644
--- a/packages/taler-wallet-core/src/operations/refresh.ts
+++ b/packages/taler-wallet-core/src/operations/refresh.ts
@@ -76,7 +76,11 @@ import {
RefreshReasonDetails,
WalletStoresV1,
} from "../db.js";
-import { isWithdrawableDenom, PendingTaskType } from "../index.js";
+import {
+ isWithdrawableDenom,
+ PendingTaskType,
+ RefreshSessionRecord,
+} from "../index.js";
import {
EXCHANGE_COINS_LOCK,
InternalWalletState,
@@ -130,11 +134,7 @@ export function getTotalRefreshCost(
const resultingAmount = Amounts.add(
Amounts.zeroOfCurrency(withdrawAmount.currency),
...withdrawDenoms.selectedDenoms.map(
- (d) =>
- Amounts.mult(
- DenominationRecord.getValue(denomMap[d.denomPubHash]),
- d.count,
- ).amount,
+ (d) => Amounts.mult(denomMap[d.denomPubHash].value, d.count).amount,
),
).amount;
const totalCost = Amounts.sub(amountLeft, resultingAmount).amount;
@@ -170,18 +170,23 @@ function updateGroupStatus(rg: RefreshGroupRecord): { final: boolean } {
/**
* Create a refresh session for one particular coin inside a refresh group.
+ *
+ * If the session already exists, return the existing one.
+ *
+ * If the session doesn't need to be created (refresh group gone or session already
+ * finished), return undefined.
*/
-async function refreshCreateSession(
+async function provideRefreshSession(
ws: InternalWalletState,
refreshGroupId: string,
coinIndex: number,
-): Promise<void> {
+): Promise<RefreshSessionRecord | undefined> {
logger.trace(
`creating refresh session for coin ${coinIndex} in refresh group ${refreshGroupId}`,
);
const d = await ws.db
- .mktx((x) => [x.refreshGroups, x.coins])
+ .mktx((x) => [x.refreshGroups, x.coins, x.refreshSessions])
.runReadWrite(async (tx) => {
const refreshGroup = await tx.refreshGroups.get(refreshGroupId);
if (!refreshGroup) {
@@ -192,21 +197,24 @@ async function refreshCreateSession(
) {
return;
}
- const existingRefreshSession =
- refreshGroup.refreshSessionPerCoin[coinIndex];
- if (existingRefreshSession) {
- return;
- }
+ const existingRefreshSession = await tx.refreshSessions.get([
+ refreshGroupId,
+ coinIndex,
+ ]);
const oldCoinPub = refreshGroup.oldCoinPubs[coinIndex];
const coin = await tx.coins.get(oldCoinPub);
if (!coin) {
throw Error("Can't refresh, coin not found");
}
- return { refreshGroup, coin };
+ return { refreshGroup, coin, existingRefreshSession };
});
if (!d) {
- return;
+ return undefined;
+ }
+
+ if (d.existingRefreshSession) {
+ return d.existingRefreshSession;
}
const { refreshGroup, coin } = d;
@@ -288,17 +296,23 @@ async function refreshCreateSession(
const sessionSecretSeed = encodeCrock(getRandomBytes(64));
// Store refresh session for this coin in the database.
- await ws.db
- .mktx((x) => [x.refreshGroups, x.coins])
+ const newSession = await ws.db
+ .mktx((x) => [x.refreshGroups, x.coins, x.refreshSessions])
.runReadWrite(async (tx) => {
const rg = await tx.refreshGroups.get(refreshGroupId);
if (!rg) {
return;
}
- if (rg.refreshSessionPerCoin[coinIndex]) {
+ const existingSession = await tx.refreshSessions.get([
+ refreshGroupId,
+ coinIndex,
+ ]);
+ if (existingSession) {
return;
}
- rg.refreshSessionPerCoin[coinIndex] = {
+ const newSession: RefreshSessionRecord = {
+ coinIndex,
+ refreshGroupId,
norevealIndex: undefined,
sessionSecretSeed: sessionSecretSeed,
newDenoms: newCoinDenoms.selectedDenoms.map((x) => ({
@@ -307,11 +321,13 @@ async function refreshCreateSession(
})),
amountRefreshOutput: Amounts.stringify(newCoinDenoms.totalCoinValue),
};
- await tx.refreshGroups.put(rg);
+ await tx.refreshSessions.put(newSession);
+ return newSession;
});
logger.trace(
`created refresh session for coin #${coinIndex} in ${refreshGroupId}`,
);
+ return newSession;
}
function getRefreshRequestTimeout(rg: RefreshGroupRecord): Duration {
@@ -326,13 +342,16 @@ async function refreshMelt(
coinIndex: number,
): Promise<void> {
const d = await ws.db
- .mktx((x) => [x.refreshGroups, x.coins, x.denominations])
+ .mktx((x) => [x.refreshGroups, x.refreshSessions, x.coins, x.denominations])
.runReadWrite(async (tx) => {
const refreshGroup = await tx.refreshGroups.get(refreshGroupId);
if (!refreshGroup) {
return;
}
- const refreshSession = refreshGroup.refreshSessionPerCoin[coinIndex];
+ const refreshSession = await tx.refreshSessions.get([
+ refreshGroupId,
+ coinIndex,
+ ]);
if (!refreshSession) {
return;
}
@@ -442,7 +461,12 @@ async function refreshMelt(
if (resp.status === HttpStatusCode.NotFound) {
const errDetails = await readUnexpectedResponseDetails(resp);
const transitionInfo = await ws.db
- .mktx((x) => [x.refreshGroups, x.coins, x.coinAvailability])
+ .mktx((x) => [
+ x.refreshGroups,
+ x.refreshSessions,
+ x.coins,
+ x.coinAvailability,
+ ])
.runReadWrite(async (tx) => {
const rg = await tx.refreshGroups.get(refreshGroupId);
if (!rg) {
@@ -456,12 +480,22 @@ async function refreshMelt(
}
const oldTxState = computeRefreshTransactionState(rg);
rg.statusPerCoin[coinIndex] = RefreshCoinStatus.Failed;
- rg.lastErrorPerCoin[coinIndex] = errDetails;
+ const refreshSession = await tx.refreshSessions.get([
+ refreshGroupId,
+ coinIndex,
+ ]);
+ if (!refreshSession) {
+ throw Error(
+ "db invariant failed: missing refresh session in database",
+ );
+ }
+ refreshSession.lastError = errDetails;
const updateRes = updateGroupStatus(rg);
if (updateRes.final) {
await makeCoinsVisible(ws, tx, transactionId);
}
await tx.refreshGroups.put(rg);
+ await tx.refreshSessions.put(refreshSession);
const newTxState = computeRefreshTransactionState(rg);
return {
oldTxState,
@@ -493,7 +527,7 @@ async function refreshMelt(
refreshSession.norevealIndex = norevealIndex;
await ws.db
- .mktx((x) => [x.refreshGroups])
+ .mktx((x) => [x.refreshGroups, x.refreshSessions])
.runReadWrite(async (tx) => {
const rg = await tx.refreshGroups.get(refreshGroupId);
if (!rg) {
@@ -502,7 +536,7 @@ async function refreshMelt(
if (rg.timestampFinished) {
return;
}
- const rs = rg.refreshSessionPerCoin[coinIndex];
+ const rs = await tx.refreshSessions.get([refreshGroupId, coinIndex]);
if (!rs) {
return;
}
@@ -510,7 +544,7 @@ async function refreshMelt(
return;
}
rs.norevealIndex = norevealIndex;
- await tx.refreshGroups.put(rg);
+ await tx.refreshSessions.put(rs);
});
}
@@ -581,13 +615,16 @@ async function refreshReveal(
`doing refresh reveal for ${refreshGroupId} (old coin ${coinIndex})`,
);
const d = await ws.db
- .mktx((x) => [x.refreshGroups, x.coins, x.denominations])
+ .mktx((x) => [x.refreshGroups, x.refreshSessions, x.coins, x.denominations])
.runReadOnly(async (tx) => {
const refreshGroup = await tx.refreshGroups.get(refreshGroupId);
if (!refreshGroup) {
return;
}
- const refreshSession = refreshGroup.refreshSessionPerCoin[coinIndex];
+ const refreshSession = await tx.refreshSessions.get([
+ refreshGroupId,
+ coinIndex,
+ ]);
if (!refreshSession) {
return;
}
@@ -755,6 +792,7 @@ async function refreshReveal(
x.denominations,
x.coinAvailability,
x.refreshGroups,
+ x.refreshSessions,
])
.runReadWrite(async (tx) => {
const rg = await tx.refreshGroups.get(refreshGroupId);
@@ -762,7 +800,7 @@ async function refreshReveal(
logger.warn("no refresh session found");
return;
}
- const rs = rg.refreshSessionPerCoin[coinIndex];
+ const rs = await tx.refreshSessions.get([refreshGroupId, coinIndex]);
if (!rs) {
return;
}
@@ -858,10 +896,15 @@ async function processRefreshSession(
logger.trace(
`processing refresh session for coin ${coinIndex} of group ${refreshGroupId}`,
);
- let refreshGroup = await ws.db
- .mktx((x) => [x.refreshGroups])
+ let { refreshGroup, refreshSession } = await ws.db
+ .mktx((x) => [x.refreshGroups, x.refreshSessions])
.runReadOnly(async (tx) => {
- return tx.refreshGroups.get(refreshGroupId);
+ const rg = await tx.refreshGroups.get(refreshGroupId);
+ const rs = await tx.refreshSessions.get([refreshGroupId, coinIndex]);
+ return {
+ refreshGroup: rg,
+ refreshSession: rs,
+ };
});
if (!refreshGroup) {
return;
@@ -869,18 +912,9 @@ async function processRefreshSession(
if (refreshGroup.statusPerCoin[coinIndex] === RefreshCoinStatus.Finished) {
return;
}
- if (!refreshGroup.refreshSessionPerCoin[coinIndex]) {
- await refreshCreateSession(ws, refreshGroupId, coinIndex);
- refreshGroup = await ws.db
- .mktx((x) => [x.refreshGroups])
- .runReadOnly(async (tx) => {
- return tx.refreshGroups.get(refreshGroupId);
- });
- if (!refreshGroup) {
- return;
- }
+ if (!refreshSession) {
+ refreshSession = await provideRefreshSession(ws, refreshGroupId, coinIndex);
}
- const refreshSession = refreshGroup.refreshSessionPerCoin[coinIndex];
if (!refreshSession) {
if (refreshGroup.statusPerCoin[coinIndex] !== RefreshCoinStatus.Finished) {
throw Error(
@@ -1058,13 +1092,11 @@ export async function createRefreshGroup(
timestampFinished: undefined,
statusPerCoin: oldCoinPubs.map(() => RefreshCoinStatus.Pending),
oldCoinPubs: oldCoinPubs.map((x) => x.coinPub),
- lastErrorPerCoin: {},
reasonDetails,
reason,
refreshGroupId,
- refreshSessionPerCoin: oldCoinPubs.map(() => undefined),
inputPerCoin: oldCoinPubs.map((x) => x.amount),
- estimatedOutputPerCoin: estimatedOutputPerCoin.map((x) =>
+ expectedOutputPerCoin: estimatedOutputPerCoin.map((x) =>
Amounts.stringify(x),
),
timestampCreated: TalerPreciseTimestamp.now(),
@@ -1164,11 +1196,7 @@ export async function autoRefresh(
if (AbsoluteTime.isExpired(executeThreshold)) {
refreshCoins.push({
coinPub: coin.coinPub,
- amount: Amounts.stringify({
- value: denom.amountVal,
- fraction: denom.amountFrac,
- currency: denom.currency,
- }),
+ amount: denom.value,
});
} else {
const checkThreshold = getAutoRefreshCheckThreshold(denom);
diff --git a/packages/taler-wallet-core/src/operations/reward.ts b/packages/taler-wallet-core/src/operations/reward.ts
index 6f9d3ce85..6ae021174 100644
--- a/packages/taler-wallet-core/src/operations/reward.ts
+++ b/packages/taler-wallet-core/src/operations/reward.ts
@@ -108,7 +108,7 @@ export function computeRewardTransactionStatus(
major: TransactionMajorState.Dialog,
minor: TransactionMinorState.Proposed,
};
- case RewardRecordStatus.SuspendidPickup:
+ case RewardRecordStatus.SuspendedPickup:
return {
major: TransactionMajorState.Pending,
minor: TransactionMinorState.Pickup,
@@ -128,7 +128,7 @@ export function computeTipTransactionActions(
return [TransactionAction.Delete];
case RewardRecordStatus.PendingPickup:
return [TransactionAction.Suspend, TransactionAction.Fail];
- case RewardRecordStatus.SuspendidPickup:
+ case RewardRecordStatus.SuspendedPickup:
return [TransactionAction.Resume, TransactionAction.Fail];
case RewardRecordStatus.DialogAccept:
return [TransactionAction.Abort];
@@ -255,7 +255,7 @@ export async function processTip(
case RewardRecordStatus.Aborted:
case RewardRecordStatus.DialogAccept:
case RewardRecordStatus.Done:
- case RewardRecordStatus.SuspendidPickup:
+ case RewardRecordStatus.SuspendedPickup:
return TaskRunResult.finished();
}
@@ -496,12 +496,12 @@ export async function suspendRewardTransaction(
let newStatus: RewardRecordStatus | undefined = undefined;
switch (tipRec.status) {
case RewardRecordStatus.Done:
- case RewardRecordStatus.SuspendidPickup:
+ case RewardRecordStatus.SuspendedPickup:
case RewardRecordStatus.Aborted:
case RewardRecordStatus.DialogAccept:
break;
case RewardRecordStatus.PendingPickup:
- newStatus = RewardRecordStatus.SuspendidPickup;
+ newStatus = RewardRecordStatus.SuspendedPickup;
break;
default:
@@ -551,7 +551,7 @@ export async function resumeTipTransaction(
case RewardRecordStatus.Aborted:
case RewardRecordStatus.DialogAccept:
break;
- case RewardRecordStatus.SuspendidPickup:
+ case RewardRecordStatus.SuspendedPickup:
newStatus = RewardRecordStatus.PendingPickup;
break;
default:
@@ -608,7 +608,7 @@ export async function abortTipTransaction(
case RewardRecordStatus.PendingPickup:
case RewardRecordStatus.DialogAccept:
break;
- case RewardRecordStatus.SuspendidPickup:
+ case RewardRecordStatus.SuspendedPickup:
newStatus = RewardRecordStatus.Aborted;
break;
default:
diff --git a/packages/taler-wallet-core/src/operations/testing.ts b/packages/taler-wallet-core/src/operations/testing.ts
index 1962c965c..f71d842c7 100644
--- a/packages/taler-wallet-core/src/operations/testing.ts
+++ b/packages/taler-wallet-core/src/operations/testing.ts
@@ -700,7 +700,7 @@ export async function runIntegrationTest2(
});
await confirmPeerPushCredit(ws, {
- peerPushPaymentIncomingId: peerPushCredit.peerPushPaymentIncomingId,
+ peerPushCreditId: peerPushCredit.peerPushCreditId,
});
const peerPullInit = await initiatePeerPullPayment(ws, {
@@ -723,7 +723,7 @@ export async function runIntegrationTest2(
});
await confirmPeerPullDebit(ws, {
- peerPullPaymentIncomingId: peerPullInc.peerPullPaymentIncomingId,
+ peerPullDebitId: peerPullInc.peerPullDebitId,
});
await waitUntilDone(ws);
diff --git a/packages/taler-wallet-core/src/operations/transactions.ts b/packages/taler-wallet-core/src/operations/transactions.ts
index 7f5302b25..7bdb9af5b 100644
--- a/packages/taler-wallet-core/src/operations/transactions.ts
+++ b/packages/taler-wallet-core/src/operations/transactions.ts
@@ -50,10 +50,10 @@ import {
OperationRetryRecord,
PeerPullPaymentIncomingRecord,
PeerPullDebitRecordStatus,
- PeerPullPaymentInitiationRecord,
+ PeerPullCreditRecord,
PeerPushPaymentIncomingRecord,
- PeerPushPaymentIncomingStatus,
- PeerPushPaymentInitiationRecord,
+ PeerPushCreditStatus,
+ PeerPushDebitRecord,
PurchaseRecord,
PurchaseStatus,
RefreshGroupRecord,
@@ -154,7 +154,18 @@ import {
resumePeerPushDebitTransaction,
abortPeerPushDebitTransaction,
} from "./pay-peer-push-debit.js";
-import { iterRecordsForDeposit, iterRecordsForPeerPullDebit, iterRecordsForPeerPullInitiation, iterRecordsForPeerPushCredit, iterRecordsForPeerPushInitiation, iterRecordsForPurchase, iterRecordsForRefresh, iterRecordsForRefund, iterRecordsForReward, iterRecordsForWithdrawal } from "./pending.js";
+import {
+ iterRecordsForDeposit,
+ iterRecordsForPeerPullDebit,
+ iterRecordsForPeerPullInitiation,
+ iterRecordsForPeerPushCredit,
+ iterRecordsForPeerPushInitiation,
+ iterRecordsForPurchase,
+ iterRecordsForRefresh,
+ iterRecordsForRefund,
+ iterRecordsForReward,
+ iterRecordsForWithdrawal,
+} from "./pending.js";
const logger = new Logger("taler-wallet-core:transactions.ts");
@@ -335,11 +346,9 @@ export async function getTransactionById(
}
case TransactionType.PeerPullDebit: {
return await ws.db
- .mktx((x) => [x.peerPullPaymentIncoming])
+ .mktx((x) => [x.peerPullDebit])
.runReadWrite(async (tx) => {
- const debit = await tx.peerPullPaymentIncoming.get(
- parsedTx.peerPullPaymentIncomingId,
- );
+ const debit = await tx.peerPullDebit.get(parsedTx.peerPullDebitId);
if (!debit) throw Error("not found");
return buildTransactionForPullPaymentDebit(debit);
});
@@ -347,11 +356,9 @@ export async function getTransactionById(
case TransactionType.PeerPushDebit: {
return await ws.db
- .mktx((x) => [x.peerPushPaymentInitiations, x.contractTerms])
+ .mktx((x) => [x.peerPushDebit, x.contractTerms])
.runReadWrite(async (tx) => {
- const debit = await tx.peerPushPaymentInitiations.get(
- parsedTx.pursePub,
- );
+ const debit = await tx.peerPushDebit.get(parsedTx.pursePub);
if (!debit) throw Error("not found");
const ct = await tx.contractTerms.get(debit.contractTermsHash);
checkDbInvariant(!!ct);
@@ -363,18 +370,16 @@ export async function getTransactionById(
}
case TransactionType.PeerPushCredit: {
- const peerPushPaymentIncomingId = parsedTx.peerPushPaymentIncomingId;
+ const peerPushCreditId = parsedTx.peerPushCreditId;
return await ws.db
.mktx((x) => [
- x.peerPushPaymentIncoming,
+ x.peerPushCredit,
x.contractTerms,
x.withdrawalGroups,
x.operationRetries,
])
.runReadWrite(async (tx) => {
- const pushInc = await tx.peerPushPaymentIncoming.get(
- peerPushPaymentIncomingId,
- );
+ const pushInc = await tx.peerPushCredit.get(peerPushCreditId);
if (!pushInc) throw Error("not found");
const ct = await tx.contractTerms.get(pushInc.contractTermsHash);
checkDbInvariant(!!ct);
@@ -405,13 +410,13 @@ export async function getTransactionById(
const pursePub = parsedTx.pursePub;
return await ws.db
.mktx((x) => [
- x.peerPullPaymentInitiations,
+ x.peerPullCredit,
x.contractTerms,
x.withdrawalGroups,
x.operationRetries,
])
.runReadWrite(async (tx) => {
- const pushInc = await tx.peerPullPaymentInitiations.get(pursePub);
+ const pushInc = await tx.peerPullCredit.get(pursePub);
if (!pushInc) throw Error("not found");
const ct = await tx.contractTerms.get(pushInc.contractTermsHash);
checkDbInvariant(!!ct);
@@ -442,7 +447,7 @@ export async function getTransactionById(
}
function buildTransactionForPushPaymentDebit(
- pi: PeerPushPaymentInitiationRecord,
+ pi: PeerPushDebitRecord,
contractTerms: PeerContractTerms,
ort?: OperationRetryRecord,
): Transaction {
@@ -490,14 +495,14 @@ function buildTransactionForPullPaymentDebit(
timestamp: pi.timestampCreated,
transactionId: constructTransactionIdentifier({
tag: TransactionType.PeerPullDebit,
- peerPullPaymentIncomingId: pi.peerPullPaymentIncomingId,
+ peerPullDebitId: pi.peerPullDebitId,
}),
...(ort?.lastError ? { error: ort.lastError } : {}),
};
}
function buildTransactionForPeerPullCredit(
- pullCredit: PeerPullPaymentInitiationRecord,
+ pullCredit: PeerPullCreditRecord,
pullCreditOrt: OperationRetryRecord | undefined,
peerContractTerms: PeerContractTerms,
wsr: WithdrawalGroupRecord | undefined,
@@ -532,8 +537,8 @@ function buildTransactionForPeerPullCredit(
// Old transactions don't have it!
timestamp: pullCredit.mergeTimestamp ?? TalerPreciseTimestamp.now(),
info: {
- expiration: wsr.wgInfo.contractTerms.purse_expiration,
- summary: wsr.wgInfo.contractTerms.summary,
+ expiration: peerContractTerms.purse_expiration,
+ summary: peerContractTerms.summary,
},
talerUri: stringifyPayPullUri({
exchangeBaseUrl: wsr.exchangeBaseUrl,
@@ -600,13 +605,13 @@ function buildTransactionForPeerPushCredit(
amountRaw: Amounts.stringify(wsr.instructedAmount),
exchangeBaseUrl: wsr.exchangeBaseUrl,
info: {
- expiration: wsr.wgInfo.contractTerms.purse_expiration,
- summary: wsr.wgInfo.contractTerms.summary,
+ expiration: peerContractTerms.purse_expiration,
+ summary: peerContractTerms.summary,
},
timestamp: wsr.timestampStart,
transactionId: constructTransactionIdentifier({
tag: TransactionType.PeerPushCredit,
- peerPushPaymentIncomingId: pushInc.peerPushPaymentIncomingId,
+ peerPushCreditId: pushInc.peerPushCreditId,
}),
kycUrl: pushInc.kycUrl,
...(wsrOrt?.lastError ? { error: wsrOrt.lastError } : {}),
@@ -629,7 +634,7 @@ function buildTransactionForPeerPushCredit(
timestamp: pushInc.timestamp,
transactionId: constructTransactionIdentifier({
tag: TransactionType.PeerPushCredit,
- peerPushPaymentIncomingId: pushInc.peerPushPaymentIncomingId,
+ peerPushCreditId: pushInc.peerPushCreditId,
}),
...(pushOrt?.lastError ? { error: pushOrt.lastError } : {}),
};
@@ -654,7 +659,7 @@ function buildTransactionForBankIntegratedWithdraw(
reservePub: wgRecord.reservePub,
bankConfirmationUrl: wgRecord.wgInfo.bankInfo.confirmUrl,
reserveIsReady:
- wgRecord.status === WithdrawalGroupStatus.Finished ||
+ wgRecord.status === WithdrawalGroupStatus.Done ||
wgRecord.status === WithdrawalGroupStatus.PendingReady,
},
kycUrl: wgRecord.kycUrl,
@@ -698,7 +703,7 @@ function buildTransactionForManualWithdraw(
reservePub: withdrawalGroup.reservePub,
exchangePaytoUris,
reserveIsReady:
- withdrawalGroup.status === WithdrawalGroupStatus.Finished ||
+ withdrawalGroup.status === WithdrawalGroupStatus.Done ||
withdrawalGroup.status === WithdrawalGroupStatus.PendingReady,
},
kycUrl: withdrawalGroup.kycUrl,
@@ -755,7 +760,7 @@ function buildTransactionForRefresh(
).amount;
const outputAmount = Amounts.sumOrZero(
refreshGroupRecord.currency,
- refreshGroupRecord.estimatedOutputPerCoin,
+ refreshGroupRecord.expectedOutputPerCoin,
).amount;
return {
type: TransactionType.Refresh,
@@ -786,8 +791,8 @@ function buildTransactionForDeposit(
ort?: OperationRetryRecord,
): Transaction {
let deposited = true;
- for (const d of dg.depositedPerCoin) {
- if (!d) {
+ for (const d of dg.statusPerCoin) {
+ if (d == DepositElementStatus.DepositPending) {
deposited = false;
}
}
@@ -796,7 +801,7 @@ function buildTransactionForDeposit(
type: TransactionType.Deposit,
txState: computeDepositTransactionStatus(dg),
txActions: computeDepositTransactionActions(dg),
- amountRaw: Amounts.stringify(dg.effectiveDepositAmount),
+ amountRaw: Amounts.stringify(dg.counterpartyEffectiveDepositAmount),
amountEffective: Amounts.stringify(dg.totalPayCost),
timestamp: dg.timestampCreated,
targetPaytoUri: dg.wire.payto_uri,
@@ -807,11 +812,11 @@ function buildTransactionForDeposit(
}),
wireTransferProgress:
(100 *
- dg.transactionPerCoin.reduce(
+ dg.statusPerCoin.reduce(
(prev, cur) => prev + (cur === DepositElementStatus.Wired ? 1 : 0),
0,
)) /
- dg.transactionPerCoin.length,
+ dg.statusPerCoin.length,
depositGroupId: dg.depositGroupId,
trackingState: Object.values(dg.trackingState ?? {}),
deposited,
@@ -879,7 +884,6 @@ async function buildTransactionForPurchase(
const info: OrderShortInfo = {
merchant: contractData.merchant,
orderId: contractData.orderId,
- products: contractData.products,
summary: contractData.summary,
summary_i18n: contractData.summaryI18n,
contractTermsHash: contractData.contractTermsHash,
@@ -944,10 +948,10 @@ export async function getTransactions(
x.exchangeDetails,
x.exchanges,
x.operationRetries,
- x.peerPullPaymentIncoming,
- x.peerPushPaymentInitiations,
- x.peerPushPaymentIncoming,
- x.peerPullPaymentInitiations,
+ x.peerPullDebit,
+ x.peerPushDebit,
+ x.peerPushCredit,
+ x.peerPullCredit,
x.planchets,
x.purchases,
x.contractTerms,
@@ -985,7 +989,7 @@ export async function getTransactions(
}
if (
pi.status !== PeerPullDebitRecordStatus.PendingDeposit &&
- pi.status !== PeerPullDebitRecordStatus.DonePaid
+ pi.status !== PeerPullDebitRecordStatus.Done
) {
return;
}
@@ -1004,7 +1008,7 @@ export async function getTransactions(
if (shouldSkipSearch(transactionsRequest, [])) {
return;
}
- if (pi.status === PeerPushPaymentIncomingStatus.DialogProposed) {
+ if (pi.status === PeerPushCreditStatus.DialogProposed) {
// We don't report proposed push credit transactions, user needs
// to scan URI again and confirm to see it.
return;
@@ -1033,7 +1037,7 @@ export async function getTransactions(
),
);
});
-
+
await iterRecordsForPeerPullInitiation(tx, filter, async (pi) => {
const currency = Amounts.currencyOf(pi.amount);
if (shouldSkipCurrency(transactionsRequest, currency)) {
@@ -1078,7 +1082,7 @@ export async function getTransactions(
);
transactions.push(buildTransactionForRefund(refundGroup, contractData));
});
-
+
await iterRecordsForRefresh(tx, filter, async (rg) => {
if (shouldSkipCurrency(transactionsRequest, rg.currency)) {
return;
@@ -1099,7 +1103,7 @@ export async function getTransactions(
}
});
- await iterRecordsForWithdrawal(tx, filter ,async (wsr) => {
+ await iterRecordsForWithdrawal(tx, filter, async (wsr) => {
if (
shouldSkipCurrency(
transactionsRequest,
@@ -1268,9 +1272,9 @@ export async function getTransactions(
export type ParsedTransactionIdentifier =
| { tag: TransactionType.Deposit; depositGroupId: string }
| { tag: TransactionType.Payment; proposalId: string }
- | { tag: TransactionType.PeerPullDebit; peerPullPaymentIncomingId: string }
+ | { tag: TransactionType.PeerPullDebit; peerPullDebitId: string }
| { tag: TransactionType.PeerPullCredit; pursePub: string }
- | { tag: TransactionType.PeerPushCredit; peerPushPaymentIncomingId: string }
+ | { tag: TransactionType.PeerPushCredit; peerPushCreditId: string }
| { tag: TransactionType.PeerPushDebit; pursePub: string }
| { tag: TransactionType.Refresh; refreshGroupId: string }
| { tag: TransactionType.Refund; refundGroupId: string }
@@ -1289,9 +1293,9 @@ export function constructTransactionIdentifier(
case TransactionType.PeerPullCredit:
return `txn:${pTxId.tag}:${pTxId.pursePub}` as TransactionIdStr;
case TransactionType.PeerPullDebit:
- return `txn:${pTxId.tag}:${pTxId.peerPullPaymentIncomingId}` as TransactionIdStr;
+ return `txn:${pTxId.tag}:${pTxId.peerPullDebitId}` as TransactionIdStr;
case TransactionType.PeerPushCredit:
- return `txn:${pTxId.tag}:${pTxId.peerPushPaymentIncomingId}` as TransactionIdStr;
+ return `txn:${pTxId.tag}:${pTxId.peerPushCreditId}` as TransactionIdStr;
case TransactionType.PeerPushDebit:
return `txn:${pTxId.tag}:${pTxId.pursePub}` as TransactionIdStr;
case TransactionType.Refresh:
@@ -1337,12 +1341,12 @@ export function parseTransactionIdentifier(
case TransactionType.PeerPullDebit:
return {
tag: TransactionType.PeerPullDebit,
- peerPullPaymentIncomingId: rest[0],
+ peerPullDebitId: rest[0],
};
case TransactionType.PeerPushCredit:
return {
tag: TransactionType.PeerPushCredit,
- peerPushPaymentIncomingId: rest[0],
+ peerPushCreditId: rest[0],
};
case TransactionType.PeerPushDebit:
return { tag: TransactionType.PeerPushDebit, pursePub: rest[0] };
@@ -1455,7 +1459,7 @@ export async function retryTransaction(
case TransactionType.PeerPullDebit: {
const taskId = constructTaskIdentifier({
tag: PendingTaskType.PeerPullDebit,
- peerPullPaymentIncomingId: parsedTx.peerPullPaymentIncomingId,
+ peerPullDebitId: parsedTx.peerPullDebitId,
});
await resetPendingTaskTimeout(ws, taskId);
stopLongpolling(ws, taskId);
@@ -1464,7 +1468,7 @@ export async function retryTransaction(
case TransactionType.PeerPushCredit: {
const taskId = constructTaskIdentifier({
tag: PendingTaskType.PeerPushCredit,
- peerPushPaymentIncomingId: parsedTx.peerPushPaymentIncomingId,
+ peerPushCreditId: parsedTx.peerPushCreditId,
});
await resetPendingTaskTimeout(ws, taskId);
stopLongpolling(ws, taskId);
@@ -1522,10 +1526,10 @@ export async function suspendTransaction(
await suspendPeerPushDebitTransaction(ws, tx.pursePub);
break;
case TransactionType.PeerPullDebit:
- await suspendPeerPullDebitTransaction(ws, tx.peerPullPaymentIncomingId);
+ await suspendPeerPullDebitTransaction(ws, tx.peerPullDebitId);
break;
case TransactionType.PeerPushCredit:
- await suspendPeerPushCreditTransaction(ws, tx.peerPushPaymentIncomingId);
+ await suspendPeerPushCreditTransaction(ws, tx.peerPushCreditId);
break;
case TransactionType.Refund:
throw Error("refund transactions can't be suspended or resumed");
@@ -1568,10 +1572,10 @@ export async function failTransaction(
await failPeerPullCreditTransaction(ws, tx.pursePub);
return;
case TransactionType.PeerPullDebit:
- await failPeerPullDebitTransaction(ws, tx.peerPullPaymentIncomingId);
+ await failPeerPullDebitTransaction(ws, tx.peerPullDebitId);
return;
case TransactionType.PeerPushCredit:
- await failPeerPushCreditTransaction(ws, tx.peerPushPaymentIncomingId);
+ await failPeerPushCreditTransaction(ws, tx.peerPushCreditId);
return;
case TransactionType.PeerPushDebit:
await failPeerPushDebitTransaction(ws, tx.pursePub);
@@ -1613,10 +1617,10 @@ export async function resumeTransaction(
await resumePeerPushDebitTransaction(ws, tx.pursePub);
break;
case TransactionType.PeerPullDebit:
- await resumePeerPullDebitTransaction(ws, tx.peerPullPaymentIncomingId);
+ await resumePeerPullDebitTransaction(ws, tx.peerPullDebitId);
break;
case TransactionType.PeerPushCredit:
- await resumePeerPushCreditTransaction(ws, tx.peerPushPaymentIncomingId);
+ await resumePeerPushCreditTransaction(ws, tx.peerPushCreditId);
break;
case TransactionType.Refund:
throw Error("refund transactions can't be suspended or resumed");
@@ -1641,17 +1645,11 @@ export async function deleteTransaction(
switch (parsedTx.tag) {
case TransactionType.PeerPushCredit: {
- const peerPushPaymentIncomingId = parsedTx.peerPushPaymentIncomingId;
+ const peerPushCreditId = parsedTx.peerPushCreditId;
await ws.db
- .mktx((x) => [
- x.withdrawalGroups,
- x.peerPushPaymentIncoming,
- x.tombstones,
- ])
+ .mktx((x) => [x.withdrawalGroups, x.peerPushCredit, x.tombstones])
.runReadWrite(async (tx) => {
- const pushInc = await tx.peerPushPaymentIncoming.get(
- peerPushPaymentIncomingId,
- );
+ const pushInc = await tx.peerPushCredit.get(peerPushCreditId);
if (!pushInc) {
return;
}
@@ -1668,12 +1666,9 @@ export async function deleteTransaction(
});
}
}
- await tx.peerPushPaymentIncoming.delete(peerPushPaymentIncomingId);
+ await tx.peerPushCredit.delete(peerPushCreditId);
await tx.tombstones.put({
- id:
- TombstoneTag.DeletePeerPushCredit +
- ":" +
- peerPushPaymentIncomingId,
+ id: TombstoneTag.DeletePeerPushCredit + ":" + peerPushCreditId,
});
});
return;
@@ -1682,13 +1677,9 @@ export async function deleteTransaction(
case TransactionType.PeerPullCredit: {
const pursePub = parsedTx.pursePub;
await ws.db
- .mktx((x) => [
- x.withdrawalGroups,
- x.peerPullPaymentInitiations,
- x.tombstones,
- ])
+ .mktx((x) => [x.withdrawalGroups, x.peerPullCredit, x.tombstones])
.runReadWrite(async (tx) => {
- const pullIni = await tx.peerPullPaymentInitiations.get(pursePub);
+ const pullIni = await tx.peerPullCredit.get(pursePub);
if (!pullIni) {
return;
}
@@ -1705,7 +1696,7 @@ export async function deleteTransaction(
});
}
}
- await tx.peerPullPaymentInitiations.delete(pursePub);
+ await tx.peerPullCredit.delete(pursePub);
await tx.tombstones.put({
id: TombstoneTag.DeletePeerPullCredit + ":" + pursePub,
});
@@ -1795,7 +1786,7 @@ export async function deleteTransaction(
case TransactionType.Refund: {
const refundGroupId = parsedTx.refundGroupId;
await ws.db
- .mktx((x) => [x.refundGroups, x.tombstones, x.refundItems])
+ .mktx((x) => [x.refundGroups, x.tombstones])
.runReadWrite(async (tx) => {
const refundRecord = await tx.refundGroups.get(refundGroupId);
if (!refundRecord) {
@@ -1809,15 +1800,13 @@ export async function deleteTransaction(
}
case TransactionType.PeerPullDebit: {
- const peerPullPaymentIncomingId = parsedTx.peerPullPaymentIncomingId;
+ const peerPullDebitId = parsedTx.peerPullDebitId;
await ws.db
- .mktx((x) => [x.peerPullPaymentIncoming, x.tombstones])
+ .mktx((x) => [x.peerPullDebit, x.tombstones])
.runReadWrite(async (tx) => {
- const debit = await tx.peerPullPaymentIncoming.get(
- peerPullPaymentIncomingId,
- );
+ const debit = await tx.peerPullDebit.get(peerPullDebitId);
if (debit) {
- await tx.peerPullPaymentIncoming.delete(peerPullPaymentIncomingId);
+ await tx.peerPullDebit.delete(peerPullDebitId);
await tx.tombstones.put({ id: transactionId });
}
});
@@ -1828,11 +1817,11 @@ export async function deleteTransaction(
case TransactionType.PeerPushDebit: {
const pursePub = parsedTx.pursePub;
await ws.db
- .mktx((x) => [x.peerPushPaymentInitiations, x.tombstones])
+ .mktx((x) => [x.peerPushDebit, x.tombstones])
.runReadWrite(async (tx) => {
- const debit = await tx.peerPushPaymentInitiations.get(pursePub);
+ const debit = await tx.peerPushDebit.get(pursePub);
if (debit) {
- await tx.peerPushPaymentInitiations.delete(pursePub);
+ await tx.peerPushDebit.delete(pursePub);
await tx.tombstones.put({ id: transactionId });
}
});
@@ -1875,10 +1864,10 @@ export async function abortTransaction(
await abortPeerPullCreditTransaction(ws, txId.pursePub);
break;
case TransactionType.PeerPullDebit:
- await abortPeerPullDebitTransaction(ws, txId.peerPullPaymentIncomingId);
+ await abortPeerPullDebitTransaction(ws, txId.peerPullDebitId);
break;
case TransactionType.PeerPushCredit:
- await abortPeerPushCreditTransaction(ws, txId.peerPushPaymentIncomingId);
+ await abortPeerPushCreditTransaction(ws, txId.peerPushCreditId);
break;
case TransactionType.PeerPushDebit:
await abortPeerPushDebitTransaction(ws, txId.pursePub);
diff --git a/packages/taler-wallet-core/src/operations/withdraw.test.ts b/packages/taler-wallet-core/src/operations/withdraw.test.ts
index 5a557b5de..2d9286610 100644
--- a/packages/taler-wallet-core/src/operations/withdraw.test.ts
+++ b/packages/taler-wallet-core/src/operations/withdraw.test.ts
@@ -78,8 +78,7 @@ test("withdrawal selection bug repro", (t) => {
},
verificationStatus: DenominationVerificationStatus.Unverified,
currency: "KUDOS",
- amountFrac: 0,
- amountVal: 1000,
+ value: "KUDOS:1000",
listIssueDate: { t_s: 0 },
},
{
@@ -133,8 +132,7 @@ test("withdrawal selection bug repro", (t) => {
t_s: 1585229388,
},
verificationStatus: DenominationVerificationStatus.Unverified,
- amountFrac: 0,
- amountVal: 10,
+ value: "KUDOS:10",
currency: "KUDOS",
listIssueDate: { t_s: 0 },
},
@@ -188,8 +186,7 @@ test("withdrawal selection bug repro", (t) => {
t_s: 1585229388,
},
verificationStatus: DenominationVerificationStatus.Unverified,
- amountFrac: 0,
- amountVal: 5,
+ value: "KUDOS:5",
currency: "KUDOS",
listIssueDate: { t_s: 0 },
},
@@ -244,8 +241,7 @@ test("withdrawal selection bug repro", (t) => {
t_s: 1585229388,
},
verificationStatus: DenominationVerificationStatus.Unverified,
- amountFrac: 0,
- amountVal: 1,
+ value: "KUDOS:1",
currency: "KUDOS",
listIssueDate: { t_s: 0 },
},
@@ -299,8 +295,11 @@ test("withdrawal selection bug repro", (t) => {
t_s: 1585229388,
},
verificationStatus: DenominationVerificationStatus.Unverified,
- amountFrac: 10000000,
- amountVal: 0,
+ value: Amounts.stringify({
+ currency: "KUDOS",
+ fraction: 10000000,
+ value: 0,
+ }),
currency: "KUDOS",
listIssueDate: { t_s: 0 },
},
@@ -354,8 +353,7 @@ test("withdrawal selection bug repro", (t) => {
t_s: 1585229388,
},
verificationStatus: DenominationVerificationStatus.Unverified,
- amountFrac: 0,
- amountVal: 2,
+ value: "KUDOS:2",
currency: "KUDOS",
listIssueDate: { t_s: 0 },
},
diff --git a/packages/taler-wallet-core/src/operations/withdraw.ts b/packages/taler-wallet-core/src/operations/withdraw.ts
index a3d95fb5c..bae348dc1 100644
--- a/packages/taler-wallet-core/src/operations/withdraw.ts
+++ b/packages/taler-wallet-core/src/operations/withdraw.ts
@@ -123,7 +123,6 @@ import {
import {
getExchangeDetails,
getExchangePaytoUri,
- getExchangeTrust,
updateExchangeFromUrl,
} from "./exchanges.js";
import {
@@ -320,7 +319,7 @@ export async function abortWithdrawalTransaction(
case WithdrawalGroupStatus.AbortingBank:
// No transition needed, but not an error
break;
- case WithdrawalGroupStatus.Finished:
+ case WithdrawalGroupStatus.Done:
case WithdrawalGroupStatus.FailedBankAborted:
case WithdrawalGroupStatus.AbortedExchange:
case WithdrawalGroupStatus.AbortedBank:
@@ -400,7 +399,7 @@ export function computeWithdrawalTransactionStatus(
return {
major: TransactionMajorState.Aborted,
};
- case WithdrawalGroupStatus.Finished:
+ case WithdrawalGroupStatus.Done:
return {
major: TransactionMajorState.Done,
};
@@ -504,7 +503,7 @@ export function computeWithdrawalTransactionActions(
switch (wgRecord.status) {
case WithdrawalGroupStatus.FailedBankAborted:
return [TransactionAction.Delete];
- case WithdrawalGroupStatus.Finished:
+ case WithdrawalGroupStatus.Done:
return [TransactionAction.Delete];
case WithdrawalGroupStatus.PendingRegisteringBank:
return [TransactionAction.Suspend, TransactionAction.Abort];
@@ -726,7 +725,9 @@ interface WithdrawalBatchResult {
batchResp: ExchangeWithdrawBatchResponse;
}
enum AmlStatus {
- normal= 0, pending = 1, fronzen = 2,
+ normal = 0,
+ pending = 1,
+ fronzen = 2,
}
async function handleKycRequired(
@@ -773,7 +774,9 @@ async function handleKycRequired(
const kycStatus = await kycStatusRes.json();
logger.info(`kyc status: ${j2s(kycStatus)}`);
kycUrl = kycStatus.kyc_url;
- } else if (kycStatusRes.status === HttpStatusCode.UnavailableForLegalReasons) {
+ } else if (
+ kycStatusRes.status === HttpStatusCode.UnavailableForLegalReasons
+ ) {
const kycStatus = await kycStatusRes.json();
logger.info(`aml status: ${j2s(kycStatus)}`);
amlStatus = kycStatus.aml_status;
@@ -809,11 +812,15 @@ async function handleKycRequired(
requirementRow: uuidResp.requirement_row,
};
wg2.kycUrl = kycUrl;
- wg2.status = amlStatus === AmlStatus.normal || amlStatus === undefined ? WithdrawalGroupStatus.PendingKyc :
- amlStatus === AmlStatus.pending ? WithdrawalGroupStatus.PendingAml :
- amlStatus === AmlStatus.fronzen ? WithdrawalGroupStatus.SuspendedAml :
- assertUnreachable(amlStatus);
-
+ wg2.status =
+ amlStatus === AmlStatus.normal || amlStatus === undefined
+ ? WithdrawalGroupStatus.PendingKyc
+ : amlStatus === AmlStatus.pending
+ ? WithdrawalGroupStatus.PendingAml
+ : amlStatus === AmlStatus.fronzen
+ ? WithdrawalGroupStatus.SuspendedAml
+ : assertUnreachable(amlStatus);
+
await tx.withdrawalGroups.put(wg2);
const newTxState = computeWithdrawalTransactionStatus(wg2);
return {
@@ -924,31 +931,31 @@ async function processPlanchetExchangeBatchRequest(
// FIXME: handle individual error codes better!
- const reqUrl = new URL(
- `reserves/${withdrawalGroup.reservePub}/batch-withdraw`,
- withdrawalGroup.exchangeBaseUrl,
- ).href;
+ const reqUrl = new URL(
+ `reserves/${withdrawalGroup.reservePub}/batch-withdraw`,
+ withdrawalGroup.exchangeBaseUrl,
+ ).href;
- try {
- const resp = await ws.http.postJson(reqUrl, batchReq);
- if (resp.status === HttpStatusCode.UnavailableForLegalReasons) {
- await handleKycRequired(ws, withdrawalGroup, resp, 0, requestCoinIdxs);
- }
- const r = await readSuccessResponseJsonOrThrow(
- resp,
- codecForWithdrawBatchResponse(),
- );
- return {
- coinIdxs: requestCoinIdxs,
- batchResp: r,
- };
- } catch (e) {
- await storeCoinError(e, requestCoinIdxs[0]);
- return {
- batchResp: { ev_sigs: [] },
- coinIdxs: [],
- };
+ try {
+ const resp = await ws.http.postJson(reqUrl, batchReq);
+ if (resp.status === HttpStatusCode.UnavailableForLegalReasons) {
+ await handleKycRequired(ws, withdrawalGroup, resp, 0, requestCoinIdxs);
}
+ const r = await readSuccessResponseJsonOrThrow(
+ resp,
+ codecForWithdrawBatchResponse(),
+ );
+ return {
+ coinIdxs: requestCoinIdxs,
+ batchResp: r,
+ };
+ } catch (e) {
+ await storeCoinError(e, requestCoinIdxs[0]);
+ return {
+ batchResp: { ev_sigs: [] },
+ coinIdxs: [],
+ };
+ }
}
async function processPlanchetVerifyAndStoreCoin(
@@ -1415,10 +1422,12 @@ async function processWithdrawalGroupPendingKyc(
logger.info(`kyc status: ${j2s(kycStatus)}`);
// FIXME: do we need to update the KYC url, or does it always stay constant?
return { ready: false };
- } else if (kycStatusRes.status === HttpStatusCode.UnavailableForLegalReasons) {
+ } else if (
+ kycStatusRes.status === HttpStatusCode.UnavailableForLegalReasons
+ ) {
const kycStatus = await kycStatusRes.json();
logger.info(`aml status: ${j2s(kycStatus)}`);
- return {ready : false}
+ return { ready: false };
} else {
throw Error(
`unexpected response from kyc-check (${kycStatusRes.status})`,
@@ -1453,7 +1462,7 @@ async function processWithdrawalGroupPendingReady(
return undefined;
}
const txStatusOld = computeWithdrawalTransactionStatus(wg);
- wg.status = WithdrawalGroupStatus.Finished;
+ wg.status = WithdrawalGroupStatus.Done;
wg.timestampFinish = TalerPreciseTimestamp.now();
const txStatusNew = computeWithdrawalTransactionStatus(wg);
await tx.withdrawalGroups.put(wg);
@@ -1551,7 +1560,7 @@ async function processWithdrawalGroupPendingReady(
logger.info(`now withdrawn ${numFinished} of ${numTotalCoins} coins`);
if (wg.timestampFinish === undefined && numFinished === numTotalCoins) {
wg.timestampFinish = TalerPreciseTimestamp.now();
- wg.status = WithdrawalGroupStatus.Finished;
+ wg.status = WithdrawalGroupStatus.Done;
await makeCoinsVisible(ws, tx, transactionId);
}
@@ -1643,7 +1652,7 @@ export async function processWithdrawalGroup(
}
break;
}
- case WithdrawalGroupStatus.Finished:
+ case WithdrawalGroupStatus.Done:
case WithdrawalGroupStatus.FailedBankAborted: {
// FIXME
return TaskRunResult.pending();
@@ -1714,11 +1723,6 @@ export async function getExchangeWithdrawalInfo(
exchangeWireAccounts.push(account.payto_uri);
}
- const { isTrusted, isAudited } = await ws.exchangeOps.getExchangeTrust(
- ws,
- exchange,
- );
-
let hasDenomWithAgeRestriction = false;
logger.trace("computing earliest deposit expiration");
@@ -1797,8 +1801,6 @@ export async function getExchangeWithdrawalInfo(
exchangePaytoUris: paytoUris,
exchangeWireAccounts,
exchangeVersion: exchangeDetails.protocolVersionRange || "unknown",
- isAudited,
- isTrusted,
numOfferedDenoms: possibleDenoms.length,
selectedDenoms,
// FIXME: delete this field / replace by something we can display to the user
@@ -1859,7 +1861,6 @@ export async function getWithdrawalDetailsForUri(
.mktx((x) => [
x.exchanges,
x.exchangeDetails,
- x.exchangeTos,
x.denominations,
x.operationRetries,
])
@@ -2215,8 +2216,6 @@ export interface PrepareCreateWithdrawalGroupResult {
withdrawalGroup: WithdrawalGroupRecord;
transactionId: string;
creationInfo?: {
- isTrusted: boolean;
- isAudited: boolean;
amount: AmountJson;
canonExchange: string;
exchangeDetails: ExchangeDetailsRecord;
@@ -2311,10 +2310,6 @@ export async function internalPrepareCreateWithdrawalGroup(
logger.trace(exchangeDetails);
throw Error("exchange not updated");
}
- const { isAudited, isTrusted } = await getExchangeTrust(
- ws,
- exchangeInfo.exchange,
- );
const transactionId = constructTransactionIdentifier({
tag: TransactionType.Withdrawal,
withdrawalGroupId: withdrawalGroup.withdrawalGroupId,
@@ -2324,8 +2319,6 @@ export async function internalPrepareCreateWithdrawalGroup(
withdrawalGroup,
transactionId,
creationInfo: {
- isAudited,
- isTrusted,
canonExchange,
amount,
exchangeDetails,
@@ -2344,7 +2337,6 @@ export async function internalPerformCreateWithdrawalGroup(
withdrawalGroups: typeof WalletStoresV1.withdrawalGroups;
reserves: typeof WalletStoresV1.reserves;
exchanges: typeof WalletStoresV1.exchanges;
- exchangeTrust: typeof WalletStoresV1.exchangeTrust;
}>,
prep: PrepareCreateWithdrawalGroupResult,
): Promise<PerformCreateWithdrawalGroupResult> {
@@ -2352,7 +2344,7 @@ export async function internalPerformCreateWithdrawalGroup(
if (!prep.creationInfo) {
return { withdrawalGroup, transitionInfo: undefined };
}
- const { isAudited, isTrusted, amount, canonExchange, exchangeDetails } =
+ const { amount, canonExchange, exchangeDetails } =
prep.creationInfo;
await tx.withdrawalGroups.add(withdrawalGroup);
@@ -2368,15 +2360,6 @@ export async function internalPerformCreateWithdrawalGroup(
await tx.exchanges.put(exchange);
}
- if (!isAudited && !isTrusted) {
- await tx.exchangeTrust.put({
- currency: amount.currency,
- exchangeBaseUrl: canonExchange,
- exchangeMasterPub: exchangeDetails.masterPublicKey,
- uids: [encodeCrock(getRandomBytes(32))],
- });
- }
-
const oldTxState = {
major: TransactionMajorState.None,
minor: undefined,
@@ -2422,7 +2405,6 @@ export async function internalCreateWithdrawalGroup(
x.reserves,
x.exchanges,
x.exchangeDetails,
- x.exchangeTrust,
])
.runReadWrite(async (tx) => {
return await internalPerformCreateWithdrawalGroup(ws, tx, prep);
@@ -2568,7 +2550,6 @@ export async function createManualWithdrawal(
x.withdrawalGroups,
x.exchanges,
x.exchangeDetails,
- x.exchangeTrust,
])
.runReadOnly(async (tx) => {
return await getFundingPaytoUris(tx, withdrawalGroup.withdrawalGroupId);
diff --git a/packages/taler-wallet-core/src/pending-types.ts b/packages/taler-wallet-core/src/pending-types.ts
index 82eb542a7..627888b4d 100644
--- a/packages/taler-wallet-core/src/pending-types.ts
+++ b/packages/taler-wallet-core/src/pending-types.ts
@@ -99,14 +99,14 @@ export interface PendingPeerPullInitiationTask {
*/
export interface PendingPeerPullDebitTask {
type: PendingTaskType.PeerPullDebit;
- peerPullPaymentIncomingId: string;
+ peerPullDebitId: string;
}
/**
*/
export interface PendingPeerPushCreditTask {
type: PendingTaskType.PeerPushCredit;
- peerPushPaymentIncomingId: string;
+ peerPushCreditId: string;
}
/**
diff --git a/packages/taler-wallet-core/src/remote.ts b/packages/taler-wallet-core/src/remote.ts
index 89348698e..164f7cfe9 100644
--- a/packages/taler-wallet-core/src/remote.ts
+++ b/packages/taler-wallet-core/src/remote.ts
@@ -65,6 +65,7 @@ export async function createRemoteWallet(
const ctx: RemoteWallet = {
makeCoreApiRequest(operation, payload) {
const id = `req-${nextRequestId}`;
+ nextRequestId += 1;
const req: CoreApiRequestEnvelope = {
operation,
id,
diff --git a/packages/taler-wallet-core/src/util/coinSelection.ts b/packages/taler-wallet-core/src/util/coinSelection.ts
index daba2ead5..ef2f85789 100644
--- a/packages/taler-wallet-core/src/util/coinSelection.ts
+++ b/packages/taler-wallet-core/src/util/coinSelection.ts
@@ -588,22 +588,22 @@ async function selectPayMerchantCandidates(
for (const acc of exchangeDetails.wireInfo.accounts) {
const pp = parsePaytoUri(acc.payto_uri);
checkLogicInvariant(!!pp);
- if (pp.targetType === req.wireMethod) {
- // also check that wire method is supported now
- const wireFeeStr = exchangeDetails.wireInfo.feesForType[
- req.wireMethod
- ]?.find((x) => {
- return AbsoluteTime.isBetween(
- AbsoluteTime.now(),
- AbsoluteTime.fromProtocolTimestamp(x.startStamp),
- AbsoluteTime.fromProtocolTimestamp(x.endStamp),
- );
- })?.wireFee;
- if (wireFeeStr) {
- wireMethodFee = wireFeeStr;
- }
- break;
+ if (pp.targetType !== req.wireMethod) {
+ continue;
+ }
+ const wireFeeStr = exchangeDetails.wireInfo.feesForType[
+ req.wireMethod
+ ]?.find((x) => {
+ return AbsoluteTime.isBetween(
+ AbsoluteTime.now(),
+ AbsoluteTime.fromProtocolTimestamp(x.startStamp),
+ AbsoluteTime.fromProtocolTimestamp(x.endStamp),
+ );
+ })?.wireFee;
+ if (wireFeeStr) {
+ wireMethodFee = wireFeeStr;
}
+ break;
}
if (!wireMethodFee) {
break;
@@ -699,25 +699,17 @@ export function selectWithdrawalDenominations(
let totalWithdrawCost = Amounts.zeroOfCurrency(amountAvailable.currency);
denoms = denoms.filter((d) => isWithdrawableDenom(d, denomselAllowLate));
- denoms.sort((d1, d2) =>
- Amounts.cmp(
- DenominationRecord.getValue(d2),
- DenominationRecord.getValue(d1),
- ),
- );
+ denoms.sort((d1, d2) => Amounts.cmp(d2.value, d1.value));
for (const d of denoms) {
- const cost = Amounts.add(
- DenominationRecord.getValue(d),
- d.fees.feeWithdraw,
- ).amount;
+ const cost = Amounts.add(d.value, d.fees.feeWithdraw).amount;
const res = Amounts.divmod(remaining, cost);
const count = res.quotient;
remaining = Amounts.sub(remaining, Amounts.mult(cost, count).amount).amount;
if (count > 0) {
totalCoinValue = Amounts.add(
totalCoinValue,
- Amounts.mult(DenominationRecord.getValue(d), count).amount,
+ Amounts.mult(d.value, count).amount,
).amount;
totalWithdrawCost = Amounts.add(
totalWithdrawCost,
@@ -766,30 +758,22 @@ export function selectForcedWithdrawalDenominations(
let totalWithdrawCost = Amounts.zeroOfCurrency(amountAvailable.currency);
denoms = denoms.filter((d) => isWithdrawableDenom(d, denomselAllowLate));
- denoms.sort((d1, d2) =>
- Amounts.cmp(
- DenominationRecord.getValue(d2),
- DenominationRecord.getValue(d1),
- ),
- );
+ denoms.sort((d1, d2) => Amounts.cmp(d2.value, d1.value));
for (const fds of forcedDenomSel.denoms) {
const count = fds.count;
const denom = denoms.find((x) => {
- return Amounts.cmp(DenominationRecord.getValue(x), fds.value) == 0;
+ return Amounts.cmp(x.value, fds.value) == 0;
});
if (!denom) {
throw Error(
`unable to find denom for forced selection (value ${fds.value})`,
);
}
- const cost = Amounts.add(
- DenominationRecord.getValue(denom),
- denom.fees.feeWithdraw,
- ).amount;
+ const cost = Amounts.add(denom.value, denom.fees.feeWithdraw).amount;
totalCoinValue = Amounts.add(
totalCoinValue,
- Amounts.mult(DenominationRecord.getValue(denom), count).amount,
+ Amounts.mult(denom.value, count).amount,
).amount;
totalWithdrawCost = Amounts.add(
totalWithdrawCost,
@@ -1002,7 +986,7 @@ export async function selectPeerCoins(
x.coinAvailability,
x.denominations,
x.refreshGroups,
- x.peerPushPaymentInitiations,
+ x.peerPushDebit,
])
.runReadWrite(async (tx) => {
const exchanges = await tx.exchanges.iter().toArray();
diff --git a/packages/taler-wallet-core/src/util/instructedAmountConversion.ts b/packages/taler-wallet-core/src/util/instructedAmountConversion.ts
index bd02e7b22..54c08eee4 100644
--- a/packages/taler-wallet-core/src/util/instructedAmountConversion.ts
+++ b/packages/taler-wallet-core/src/util/instructedAmountConversion.ts
@@ -321,7 +321,7 @@ function buildCoinInfoFromDenom(
AbsoluteTime.fromProtocolTimestamp(denom.stampExpireDeposit),
),
totalAvailable: total,
- value: DenominationRecord.getValue(denom),
+ value: Amounts.parseOrThrow(denom.value),
maxAge,
};
}
diff --git a/packages/taler-wallet-core/src/util/query.ts b/packages/taler-wallet-core/src/util/query.ts
index 1c3ff6a2a..309c17a43 100644
--- a/packages/taler-wallet-core/src/util/query.ts
+++ b/packages/taler-wallet-core/src/util/query.ts
@@ -35,7 +35,7 @@ import {
IDBKeyPath,
IDBKeyRange,
} from "@gnu-taler/idb-bridge";
-import { Logger, j2s } from "@gnu-taler/taler-util";
+import { Codec, Logger, j2s } from "@gnu-taler/taler-util";
const logger = new Logger("query.ts");
@@ -387,11 +387,11 @@ export interface StoreReadWriteAccessor<RecordType, IndexMap> {
export interface StoreWithIndexes<
StoreName extends string,
- SD extends StoreDescriptor<unknown>,
+ RecordType,
IndexMap,
> {
storeName: StoreName;
- store: SD;
+ store: StoreDescriptor<RecordType>;
indexMap: IndexMap;
/**
@@ -401,19 +401,13 @@ export interface StoreWithIndexes<
mark: Symbol;
}
-export type GetRecordType<T> = T extends StoreDescriptor<infer X> ? X : unknown;
-
const storeWithIndexesSymbol = Symbol("StoreWithIndexesMark");
-export function describeStore<
- StoreName extends string,
- SD extends StoreDescriptor<unknown>,
- IndexMap,
->(
+export function describeStore<StoreName extends string, RecordType, IndexMap>(
name: StoreName,
- s: SD,
+ s: StoreDescriptor<RecordType>,
m: IndexMap,
-): StoreWithIndexes<StoreName, SD, IndexMap> {
+): StoreWithIndexes<StoreName, RecordType, IndexMap> {
return {
storeName: name,
store: s,
@@ -422,13 +416,72 @@ export function describeStore<
};
}
+export function describeStoreV2<
+ StoreName extends string,
+ RecordType,
+ IndexMap extends { [x: string]: IndexDescriptor } = {},
+>(args: {
+ storeName: StoreName;
+ recordCodec: Codec<RecordType>;
+ keyPath?: IDBKeyPath | IDBKeyPath[];
+ autoIncrement?: boolean;
+ /**
+ * Database version that this store was added in, or
+ * undefined if added in the first version.
+ */
+ versionAdded?: number;
+ indexes?: IndexMap;
+}): StoreWithIndexes<StoreName, RecordType, IndexMap> {
+ return {
+ storeName: args.storeName,
+ store: {
+ _dummy: undefined as any,
+ autoIncrement: args.autoIncrement,
+ keyPath: args.keyPath,
+ versionAdded: args.versionAdded,
+ },
+ indexMap: args.indexes ?? ({} as IndexMap),
+ mark: storeWithIndexesSymbol,
+ };
+}
+
+type KeyPathComponents = string | number;
+
+/**
+ * Follow a key path (dot-separated) in an object.
+ */
+type DerefKeyPath<T, P> = P extends `${infer PX extends keyof T &
+ KeyPathComponents}`
+ ? T[PX]
+ : P extends `${infer P0 extends keyof T & KeyPathComponents}.${infer Rest}`
+ ? DerefKeyPath<T[P0], Rest>
+ : unknown;
+
+/**
+ * Return a path if it is a valid dot-separate path to an object.
+ * Otherwise, return "never".
+ */
+type ValidateKeyPath<T, P> = P extends `${infer PX extends keyof T &
+ KeyPathComponents}`
+ ? PX
+ : P extends `${infer P0 extends keyof T & KeyPathComponents}.${infer Rest}`
+ ? `${P0}.${ValidateKeyPath<T[P0], Rest>}`
+ : never;
+
+// function foo<T, P>(
+// x: T,
+// p: P extends ValidateKeyPath<T, P> ? P : never,
+// ): void {}
+
+// foo({x: [0,1,2]}, "x.0");
+
export type GetReadOnlyAccess<BoundStores> = {
[P in keyof BoundStores]: BoundStores[P] extends StoreWithIndexes<
- infer SN,
- infer SD,
- infer IM
+ infer StoreName,
+ infer RecordType,
+ infer IndexMap
>
- ? StoreReadOnlyAccessor<GetRecordType<SD>, IM>
+ ? StoreReadOnlyAccessor<RecordType, IndexMap>
: unknown;
};
@@ -446,11 +499,11 @@ export type DbReadOnlyTransaction<
}
? {
[P in Stores]: StoreMap[P] extends StoreWithIndexes<
- infer SN,
- infer SD,
- infer IM
+ infer StoreName,
+ infer RecordType,
+ infer IndexMap
>
- ? StoreReadOnlyAccessor<GetRecordType<SD>, IM>
+ ? StoreReadOnlyAccessor<RecordType, IndexMap>
: unknown;
}
: unknown;
@@ -463,22 +516,22 @@ export type DbReadWriteTransaction<
}
? {
[P in Stores]: StoreMap[P] extends StoreWithIndexes<
- infer SN,
- infer SD,
- infer IM
+ infer StoreName,
+ infer RecordType,
+ infer IndexMap
>
- ? StoreReadWriteAccessor<GetRecordType<SD>, IM>
+ ? StoreReadWriteAccessor<RecordType, IndexMap>
: unknown;
}
: unknown;
export type GetReadWriteAccess<BoundStores> = {
[P in keyof BoundStores]: BoundStores[P] extends StoreWithIndexes<
- infer SN,
- infer SD,
- infer IM
+ infer StoreName,
+ infer RecordType,
+ infer IndexMap
>
- ? StoreReadWriteAccessor<GetRecordType<SD>, IM>
+ ? StoreReadWriteAccessor<RecordType, IndexMap>
: unknown;
};
diff --git a/packages/taler-wallet-core/src/wallet-api-types.ts b/packages/taler-wallet-core/src/wallet-api-types.ts
index 4d9d40c43..d89ad257a 100644
--- a/packages/taler-wallet-core/src/wallet-api-types.ts
+++ b/packages/taler-wallet-core/src/wallet-api-types.ts
@@ -119,8 +119,9 @@ import {
CreateStoredBackupResponse,
RecoverStoredBackupRequest,
DeleteStoredBackupRequest,
+ TestingSetTimetravelRequest,
} from "@gnu-taler/taler-util";
-import { AuditorTrustRecord, WalletContractData } from "./db.js";
+import { WalletContractData } from "./db.js";
import {
AddBackupProviderRequest,
AddBackupProviderResponse,
@@ -215,6 +216,7 @@ export enum WalletApiOperation {
ValidateIban = "validateIban",
TestingWaitTransactionsFinal = "testingWaitTransactionsFinal",
TestingWaitRefreshesFinal = "testingWaitRefreshesFinal",
+ TestingSetTimetravel = "testingSetTimetravel",
GetScopedCurrencyInfo = "getScopedCurrencyInfo",
ListStoredBackups = "listStoredBackups",
CreateStoredBackup = "createStoredBackup",
@@ -258,7 +260,6 @@ export interface WalletConfig {
*/
builtin: {
exchanges: string[];
- auditors: AuditorTrustRecord[];
};
/**
@@ -990,6 +991,15 @@ export type DumpCoinsOp = {
};
/**
+ * Add an offset to the wallet's internal time.
+ */
+export type TestingSetTimetravelOp = {
+ op: WalletApiOperation.TestingSetTimetravel;
+ request: TestingSetTimetravelRequest;
+ response: EmptyObject;
+};
+
+/**
* Wait until all transactions are in a final state.
*/
export type TestingWaitTransactionsFinal = {
@@ -1111,6 +1121,7 @@ export type WalletOperations = {
[WalletApiOperation.ValidateIban]: ValidateIbanOp;
[WalletApiOperation.TestingWaitTransactionsFinal]: TestingWaitTransactionsFinal;
[WalletApiOperation.TestingWaitRefreshesFinal]: TestingWaitRefreshesFinal;
+ [WalletApiOperation.TestingSetTimetravel]: TestingSetTimetravelOp;
[WalletApiOperation.GetScopedCurrencyInfo]: GetScopedCurrencyInfoOp;
[WalletApiOperation.CreateStoredBackup]: CreateStoredBackupsOp;
[WalletApiOperation.ListStoredBackups]: ListStoredBackupsOp;
diff --git a/packages/taler-wallet-core/src/wallet.ts b/packages/taler-wallet-core/src/wallet.ts
index 626409dd6..1a60b148c 100644
--- a/packages/taler-wallet-core/src/wallet.ts
+++ b/packages/taler-wallet-core/src/wallet.ts
@@ -34,7 +34,6 @@ import {
Duration,
ExchangeDetailedResponse,
ExchangeListItem,
- ExchangeTosStatusDetails,
ExchangesListResponse,
FeeDescription,
GetExchangeTosResult,
@@ -121,11 +120,16 @@ import {
GetCurrencyInfoResponse,
codecForGetCurrencyInfoRequest,
CreateStoredBackupResponse,
+ StoredBackupList,
+ codecForDeleteStoredBackupRequest,
+ DeleteStoredBackupRequest,
+ RecoverStoredBackupRequest,
+ codecForRecoverStoredBackupRequest,
+ codecForTestingSetTimetravelRequest,
+ setDangerousTimetravel,
} from "@gnu-taler/taler-util";
-import {
- HttpRequestLibrary,
- readSuccessResponseJsonOrThrow,
-} from "@gnu-taler/taler-util/http";
+import type { HttpRequestLibrary } from "@gnu-taler/taler-util/http";
+import { readSuccessResponseJsonOrThrow } from "@gnu-taler/taler-util/http";
import { TalerCryptoInterface } from "./crypto/cryptoImplementation.js";
import {
CryptoDispatcher,
@@ -194,11 +198,9 @@ import {
downloadTosFromAcceptedFormat,
getExchangeDetails,
getExchangeRequestTimeout,
- getExchangeTrust,
provideExchangeRecordInTx,
updateExchangeFromUrl,
updateExchangeFromUrlHandler,
- updateExchangeTermsOfService,
} from "./operations/exchanges.js";
import { getMerchantInfo } from "./operations/merchants.js";
import {
@@ -352,9 +354,9 @@ async function callOperationHandler(
case PendingTaskType.PeerPullCredit:
return await processPeerPullCredit(ws, pending.pursePub);
case PendingTaskType.PeerPullDebit:
- return await processPeerPullDebit(ws, pending.peerPullPaymentIncomingId);
+ return await processPeerPullDebit(ws, pending.peerPullDebitId);
case PendingTaskType.PeerPushCredit:
- return await processPeerPushCredit(ws, pending.peerPushPaymentIncomingId);
+ return await processPeerPushCredit(ws, pending.peerPushCreditId);
default:
return assertUnreachable(pending);
}
@@ -522,7 +524,7 @@ async function runTaskLoop(
*/
async function fillDefaults(ws: InternalWalletState): Promise<void> {
await ws.db
- .mktx((x) => [x.config, x.auditorTrust, x.exchanges, x.exchangeDetails])
+ .mktx((x) => [x.config, x.exchanges, x.exchangeDetails])
.runReadWrite(async (tx) => {
const appliedRec = await tx.config.get("currencyDefaultsApplied");
let alreadyApplied = appliedRec ? !!appliedRec.value : false;
@@ -530,10 +532,6 @@ async function fillDefaults(ws: InternalWalletState): Promise<void> {
logger.trace("defaults already applied");
return;
}
- logger.info("importing default exchanges and auditors");
- for (const c of ws.config.builtin.auditors) {
- await tx.auditorTrust.put(c);
- }
for (const baseUrl of ws.config.builtin.exchanges) {
await addPresetExchangeEntry(tx, baseUrl);
const now = AbsoluteTime.now();
@@ -557,33 +555,6 @@ async function getExchangeTos(
): Promise<GetExchangeTosResult> {
// FIXME: download ToS in acceptable format if passed!
const { exchangeDetails } = await updateExchangeFromUrl(ws, exchangeBaseUrl);
- const tosDetails = await ws.db
- .mktx((x) => [x.exchangeTos])
- .runReadOnly(async (tx) => {
- return await getExchangeTosStatusDetails(tx, exchangeDetails);
- });
- const content = tosDetails.content;
- const currentEtag = tosDetails.currentVersion;
- const contentType = tosDetails.contentType;
- if (
- content === undefined ||
- currentEtag === undefined ||
- contentType === undefined
- ) {
- throw Error("exchange is in invalid state");
- }
- if (
- acceptedFormat &&
- acceptedFormat.findIndex((f) => f === contentType) !== -1
- ) {
- return {
- acceptedEtag: exchangeDetails.tosAccepted?.etag,
- currentEtag,
- content,
- contentType,
- tosStatus: getExchangeTosStatus(exchangeDetails),
- };
- }
const tosDownload = await downloadTosFromAcceptedFormat(
ws,
@@ -592,17 +563,15 @@ async function getExchangeTos(
acceptedFormat,
);
- if (tosDownload.tosContentType === contentType) {
- return {
- acceptedEtag: exchangeDetails.tosAccepted?.etag,
- currentEtag,
- content,
- contentType,
- tosStatus: getExchangeTosStatus(exchangeDetails),
- };
- }
-
- await updateExchangeTermsOfService(ws, exchangeBaseUrl, tosDownload);
+ await ws.db
+ .mktx((x) => [x.exchanges, x.exchangeDetails])
+ .runReadWrite(async (tx) => {
+ const d = await getExchangeDetails(tx, exchangeBaseUrl);
+ if (d) {
+ d.tosCurrentEtag = tosDownload.tosEtag;
+ await tx.exchangeDetails.put(d);
+ }
+ });
return {
acceptedEtag: exchangeDetails.tosAccepted?.etag,
@@ -683,32 +652,6 @@ async function forgetKnownBankAccounts(
return;
}
-async function getExchangeTosStatusDetails(
- tx: GetReadOnlyAccess<{ exchangeTos: typeof WalletStoresV1.exchangeTos }>,
- exchangeDetails: ExchangeDetailsRecord,
-): Promise<ExchangeTosStatusDetails> {
- let exchangeTos = await tx.exchangeTos.get([
- exchangeDetails.exchangeBaseUrl,
- exchangeDetails.tosCurrentEtag,
- ]);
-
- if (!exchangeTos) {
- exchangeTos = {
- etag: "not-available",
- termsOfServiceContentType: "text/plain",
- termsOfServiceText: "terms of service unavailable",
- exchangeBaseUrl: exchangeDetails.exchangeBaseUrl,
- };
- }
-
- return {
- acceptedVersion: exchangeDetails.tosAccepted?.etag,
- content: exchangeTos.termsOfServiceText,
- contentType: exchangeTos.termsOfServiceContentType,
- currentVersion: exchangeTos.etag,
- };
-}
-
async function getExchanges(
ws: InternalWalletState,
): Promise<ExchangesListResponse> {
@@ -717,7 +660,6 @@ async function getExchanges(
.mktx((x) => [
x.exchanges,
x.exchangeDetails,
- x.exchangeTos,
x.denominations,
x.operationRetries,
])
@@ -742,12 +684,7 @@ async function getExchangeDetailedInfo(
): Promise<ExchangeDetailedResponse> {
//TODO: should we use the forceUpdate parameter?
const exchange = await ws.db
- .mktx((x) => [
- x.exchanges,
- x.exchangeTos,
- x.exchangeDetails,
- x.denominations,
- ])
+ .mktx((x) => [x.exchanges, x.exchangeDetails, x.denominations])
.runReadOnly(async (tx) => {
const ex = await tx.exchanges.get(exchangeBaseurl);
const dp = ex?.detailsPointer;
@@ -769,8 +706,6 @@ async function getExchangeDetailedInfo(
return;
}
- const tos = await getExchangeTosStatusDetails(tx, exchangeDetails);
-
const denominations: DenominationInfo[] = denominationRecords.map((x) =>
DenominationRecord.toDenomInfo(x),
);
@@ -779,7 +714,6 @@ async function getExchangeDetailedInfo(
info: {
exchangeBaseUrl: ex.baseUrl,
currency,
- tos,
paytoUris: exchangeDetails.wireInfo.accounts.map((x) => x.payto_uri),
auditors: exchangeDetails.auditors,
wireInfo: exchangeDetails.wireInfo,
@@ -981,11 +915,7 @@ async function dumpCoins(ws: InternalWalletState): Promise<CoinDumpJson> {
coin_pub: c.coinPub,
denom_pub: denomInfo.denomPub,
denom_pub_hash: c.denomPubHash,
- denom_value: Amounts.stringify({
- value: denom.amountVal,
- currency: denom.currency,
- fraction: denom.amountFrac,
- }),
+ denom_value: denom.value,
exchange_base_url: c.exchangeBaseUrl,
refresh_parent_coin_pub: refreshParentCoinPub,
withdrawal_reserve_pub: withdrawalReservePub,
@@ -1041,6 +971,57 @@ async function createStoredBackup(
};
}
+async function listStoredBackups(
+ ws: InternalWalletState,
+): Promise<StoredBackupList> {
+ const storedBackups: StoredBackupList = {
+ storedBackups: [],
+ };
+ const backupsDb = await openStoredBackupsDatabase(ws.idb);
+ await backupsDb.mktxAll().runReadWrite(async (tx) => {
+ await tx.backupMeta.iter().forEach((x) => {
+ storedBackups.storedBackups.push({
+ name: x.name,
+ });
+ });
+ });
+ return storedBackups;
+}
+
+async function deleteStoredBackup(
+ ws: InternalWalletState,
+ req: DeleteStoredBackupRequest,
+): Promise<void> {
+ const backupsDb = await openStoredBackupsDatabase(ws.idb);
+ await backupsDb.mktxAll().runReadWrite(async (tx) => {
+ await tx.backupData.delete(req.name);
+ await tx.backupMeta.delete(req.name);
+ });
+}
+
+async function recoverStoredBackup(
+ ws: InternalWalletState,
+ req: RecoverStoredBackupRequest,
+): Promise<void> {
+ logger.info(`Recovering stored backup ${req.name}`);
+ const { name } = req;
+ const backupsDb = await openStoredBackupsDatabase(ws.idb);
+ const bd = await backupsDb.mktxAll().runReadWrite(async (tx) => {
+ const backupMeta = tx.backupMeta.get(name);
+ if (!backupMeta) {
+ throw Error("backup not found");
+ }
+ const backupData = await tx.backupData.get(name);
+ if (!backupData) {
+ throw Error("no backup data (DB corrupt)");
+ }
+ return backupData;
+ });
+ logger.info(`backup found, now importing`);
+ await importDb(ws.db.idbHandle(), bd);
+ logger.info(`import done`);
+}
+
/**
* Implementation of the "wallet-core" API.
*/
@@ -1059,12 +1040,18 @@ async function dispatchRequestInternal<Op extends WalletApiOperation>(
switch (operation) {
case WalletApiOperation.CreateStoredBackup:
return createStoredBackup(ws);
- case WalletApiOperation.DeleteStoredBackup:
+ case WalletApiOperation.DeleteStoredBackup: {
+ const req = codecForDeleteStoredBackupRequest().decode(payload);
+ await deleteStoredBackup(ws, req);
return {};
+ }
case WalletApiOperation.ListStoredBackups:
+ return listStoredBackups(ws);
+ case WalletApiOperation.RecoverStoredBackup: {
+ const req = codecForRecoverStoredBackupRequest().decode(payload);
+ await recoverStoredBackup(ws, req);
return {};
- case WalletApiOperation.RecoverStoredBackup:
- return {};
+ }
case WalletApiOperation.InitWallet: {
logger.trace("initializing wallet");
ws.initCalled = true;
@@ -1496,24 +1483,11 @@ async function dispatchRequestInternal<Op extends WalletApiOperation>(
return {};
}
case WalletApiOperation.ListCurrencies: {
- return await ws.db
- .mktx((x) => [x.auditorTrust, x.exchangeTrust])
- .runReadOnly(async (tx) => {
- const trustedAuditors = await tx.auditorTrust.iter().toArray();
- const trustedExchanges = await tx.exchangeTrust.iter().toArray();
- return {
- trustedAuditors: trustedAuditors.map((x) => ({
- currency: x.currency,
- auditorBaseUrl: x.auditorBaseUrl,
- auditorPub: x.auditorPub,
- })),
- trustedExchanges: trustedExchanges.map((x) => ({
- currency: x.currency,
- exchangeBaseUrl: x.exchangeBaseUrl,
- exchangeMasterPub: x.exchangeMasterPub,
- })),
- };
- });
+ // FIXME: Remove / change to scoped currency approach.
+ return {
+ trustedAuditors: [],
+ trustedExchanges: [],
+ };
}
case WalletApiOperation.WithdrawFakebank: {
const req = codecForWithdrawFakebankRequest().decode(payload);
@@ -1612,6 +1586,12 @@ async function dispatchRequestInternal<Op extends WalletApiOperation>(
return await waitUntilDone(ws);
case WalletApiOperation.TestingWaitRefreshesFinal:
return await waitUntilRefreshesDone(ws);
+ case WalletApiOperation.TestingSetTimetravel: {
+ const req = codecForTestingSetTimetravelRequest().decode(payload);
+ setDangerousTimetravel(req.offsetMs);
+ ws.workAvailable.trigger();
+ return {};
+ }
// default:
// assertUnreachable(operation);
}
@@ -1712,14 +1692,6 @@ export class Wallet {
public static defaultConfig: Readonly<WalletConfig> = {
builtin: {
exchanges: ["https://exchange.demo.taler.net/"],
- auditors: [
- {
- currency: "KUDOS",
- auditorPub: "BW9DC48PHQY4NH011SHHX36DZZ3Q22Y6X7FZ1VD1CMZ2PTFZ6PN0",
- auditorBaseUrl: "https://auditor.demo.taler.net/",
- uids: ["5P25XF8TVQP9AW6VYGY2KV47WT5Y3ZXFSJAA570GJPX5SVJXKBVG"],
- },
- ],
},
features: {
allowHttp: false,
@@ -1792,7 +1764,6 @@ class InternalWalletStateImpl implements InternalWalletState {
exchangeOps: ExchangeOperations = {
getExchangeDetails,
- getExchangeTrust,
updateExchangeFromUrl,
};
@@ -1901,35 +1872,27 @@ class InternalWalletStateImpl implements InternalWalletState {
return computeRefundTransactionState(rec);
}
case TransactionType.PeerPullCredit:
- const rec = await tx.peerPullPaymentInitiations.get(
- parsedTxId.pursePub,
- );
+ const rec = await tx.peerPullCredit.get(parsedTxId.pursePub);
if (!rec) {
return undefined;
}
return computePeerPullCreditTransactionState(rec);
case TransactionType.PeerPullDebit: {
- const rec = await tx.peerPullPaymentIncoming.get(
- parsedTxId.peerPullPaymentIncomingId,
- );
+ const rec = await tx.peerPullDebit.get(parsedTxId.peerPullDebitId);
if (!rec) {
return undefined;
}
return computePeerPullDebitTransactionState(rec);
}
case TransactionType.PeerPushCredit: {
- const rec = await tx.peerPushPaymentIncoming.get(
- parsedTxId.peerPushPaymentIncomingId,
- );
+ const rec = await tx.peerPushCredit.get(parsedTxId.peerPushCreditId);
if (!rec) {
return undefined;
}
return computePeerPushCreditTransactionState(rec);
}
case TransactionType.PeerPushDebit: {
- const rec = await tx.peerPushPaymentInitiations.get(
- parsedTxId.pursePub,
- );
+ const rec = await tx.peerPushDebit.get(parsedTxId.pursePub);
if (!rec) {
return undefined;
}
diff --git a/packages/taler-wallet-core/tsconfig.json b/packages/taler-wallet-core/tsconfig.json
index 8de28b3d8..663a4dd98 100644
--- a/packages/taler-wallet-core/tsconfig.json
+++ b/packages/taler-wallet-core/tsconfig.json
@@ -4,12 +4,12 @@
"composite": true,
"declaration": true,
"declarationMap": false,
- "target": "ES2017",
- "module": "ESNext",
+ "target": "ES2020",
+ "module": "Node16",
"moduleResolution": "Node16",
"resolveJsonModule": true,
"sourceMap": true,
- "lib": ["es6"],
+ "lib": ["ES2020"],
"resolvePackageJsonImports": true,
"types": ["node"],
"noImplicitReturns": true,
@@ -33,8 +33,5 @@
"path": "../taler-util/"
}
],
- "include": [
- "src/**/*",
- "src/*.json"
- ]
+ "include": ["src/**/*", "src/*.json", "../taler-util/src/bank-api-client.ts"]
}
diff --git a/packages/taler-wallet-embedded/tsconfig.json b/packages/taler-wallet-embedded/tsconfig.json
index 7b27ca6b7..e8b265fb9 100644
--- a/packages/taler-wallet-embedded/tsconfig.json
+++ b/packages/taler-wallet-embedded/tsconfig.json
@@ -5,10 +5,10 @@
"declaration": true,
"declarationMap": true,
"target": "ES6",
- "module": "ESNext",
+ "module": "Node16",
"moduleResolution": "Node16",
"sourceMap": true,
- "lib": ["es6"],
+ "lib": ["ES2020"],
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"strict": true,
diff --git a/packages/taler-wallet-webextension/package.json b/packages/taler-wallet-webextension/package.json
index 040878529..f442b1509 100644
--- a/packages/taler-wallet-webextension/package.json
+++ b/packages/taler-wallet-webextension/package.json
@@ -64,7 +64,7 @@
"preact-cli": "^3.3.5",
"preact-render-to-string": "^5.1.19",
"rimraf": "^3.0.2",
- "typescript": "5.1.3"
+ "typescript": "5.2.2"
},
"nyc": {
"include": [
diff --git a/packages/taler-wallet-webextension/src/components/ShowFullContractTermPopup.stories.tsx b/packages/taler-wallet-webextension/src/components/ShowFullContractTermPopup.stories.tsx
index 6cf863519..74c92cbc6 100644
--- a/packages/taler-wallet-webextension/src/components/ShowFullContractTermPopup.stories.tsx
+++ b/packages/taler-wallet-webextension/src/components/ShowFullContractTermPopup.stories.tsx
@@ -73,11 +73,11 @@ const cd: WalletContractData = {
country: "ar",
},
},
- products: [],
+ // products: [],
autoRefund: undefined,
summaryI18n: undefined,
- deliveryDate: undefined,
- deliveryLocation: undefined,
+ // deliveryDate: undefined,
+ // deliveryLocation: undefined,
};
export const ShowingSimpleOrder = tests.createExample(ShowView, {
diff --git a/packages/taler-wallet-webextension/src/components/ShowFullContractTermPopup.tsx b/packages/taler-wallet-webextension/src/components/ShowFullContractTermPopup.tsx
index 392a7d0e8..db9b6ebcd 100644
--- a/packages/taler-wallet-webextension/src/components/ShowFullContractTermPopup.tsx
+++ b/packages/taler-wallet-webextension/src/components/ShowFullContractTermPopup.tsx
@@ -259,7 +259,7 @@ export function ShowView({ contractTerms, hideHandler }: States.Show): VNode {
</span>
</td>
</tr>
- <tr>
+ {/* <tr>
<td>
<i18n.Translate>Delivery date</i18n.Translate>
</td>
@@ -291,7 +291,7 @@ export function ShowView({ contractTerms, hideHandler }: States.Show): VNode {
.map((p) => `${p.description} x ${p.quantity}`)
.join(", ")}
</td>
- </tr>
+ </tr> */}
<tr>
<td>
<i18n.Translate>Created at</i18n.Translate>
@@ -334,8 +334,8 @@ export function ShowView({ contractTerms, hideHandler }: States.Show): VNode {
!contractTerms.autoRefund
? Duration.getZero()
: Duration.fromTalerProtocolDuration(
- contractTerms.autoRefund,
- ),
+ contractTerms.autoRefund,
+ ),
)}
format="dd MMMM yyyy, HH:mm"
/>
diff --git a/packages/taler-wallet-webextension/src/components/TermsOfService/views.tsx b/packages/taler-wallet-webextension/src/components/TermsOfService/views.tsx
index 214c4d792..f6c176550 100644
--- a/packages/taler-wallet-webextension/src/components/TermsOfService/views.tsx
+++ b/packages/taler-wallet-webextension/src/components/TermsOfService/views.tsx
@@ -99,7 +99,7 @@ export function ShowButtonsNonAcceptedTosView({
</WarningText>
</section>
)} */}
- {terms.status === ExchangeTosStatus.Pending && (
+ {terms.status === ExchangeTosStatus.Accepted && (
<section style={{ justifyContent: "space-around", display: "flex" }}>
<Button
variant="contained"
@@ -181,7 +181,7 @@ export function ShowTosContentView({
</LinkSuccess>
</section>
)}
- {termsAccepted && terms.status !== ExchangeTosStatus.Proposed && (
+ {termsAccepted && terms.status !== ExchangeTosStatus.Accepted && (
<section style={{ justifyContent: "space-around", display: "flex" }}>
<CheckboxOutlined
name="terms"
diff --git a/packages/taler-wallet-webextension/src/cta/InvoicePay/state.ts b/packages/taler-wallet-webextension/src/cta/InvoicePay/state.ts
index 098f18921..8bae9470f 100644
--- a/packages/taler-wallet-webextension/src/cta/InvoicePay/state.ts
+++ b/packages/taler-wallet-webextension/src/cta/InvoicePay/state.ts
@@ -78,7 +78,7 @@ export function useComponentState({
const {
contractTerms,
- peerPullPaymentIncomingId,
+ peerPullDebitId,
amountEffective,
amountRaw,
} = hook.response.p2p;
@@ -155,7 +155,7 @@ export function useComponentState({
const resp = await api.wallet.call(
WalletApiOperation.ConfirmPeerPullDebit,
{
- peerPullPaymentIncomingId,
+ peerPullDebitId,
},
);
onSuccess(resp.transactionId);
diff --git a/packages/taler-wallet-webextension/src/cta/Payment/views.tsx b/packages/taler-wallet-webextension/src/cta/Payment/views.tsx
index 0bdadef0e..c00e570f9 100644
--- a/packages/taler-wallet-webextension/src/cta/Payment/views.tsx
+++ b/packages/taler-wallet-webextension/src/cta/Payment/views.tsx
@@ -76,7 +76,7 @@ export function BaseView(state: SupportedStates): VNode {
...contractTerms,
orderId: contractTerms.order_id,
contractTermsHash: "",
- products: contractTerms.products!,
+ // products: contractTerms.products!,
}}
proposalId={state.payStatus.proposalId}
/>
diff --git a/packages/taler-wallet-webextension/src/cta/Refund/index.ts b/packages/taler-wallet-webextension/src/cta/Refund/index.ts
index bbb72c328..42e9cc534 100644
--- a/packages/taler-wallet-webextension/src/cta/Refund/index.ts
+++ b/packages/taler-wallet-webextension/src/cta/Refund/index.ts
@@ -49,7 +49,7 @@ export namespace State {
interface BaseInfo {
merchantName: string;
- products: Product[] | undefined;
+ // products: Product[] | undefined;
amount: AmountJson;
// awaitingAmount: AmountJson;
// granted: AmountJson;
diff --git a/packages/taler-wallet-webextension/src/cta/Refund/state.ts b/packages/taler-wallet-webextension/src/cta/Refund/state.ts
index 0ba77a19d..6c0f37471 100644
--- a/packages/taler-wallet-webextension/src/cta/Refund/state.ts
+++ b/packages/taler-wallet-webextension/src/cta/Refund/state.ts
@@ -106,7 +106,7 @@ export function useComponentState({
// granted: Amounts.parseOrThrow(info.response.refund.granted),
// awaitingAmount: Amounts.parseOrThrow(refund.awaiting),
merchantName: purchase.info.merchant.name,
- products: purchase.info.products,
+ // products: purchase.info.products,
error: undefined,
};
diff --git a/packages/taler-wallet-webextension/src/cta/Refund/stories.tsx b/packages/taler-wallet-webextension/src/cta/Refund/stories.tsx
index ef1f76033..03d55ee91 100644
--- a/packages/taler-wallet-webextension/src/cta/Refund/stories.tsx
+++ b/packages/taler-wallet-webextension/src/cta/Refund/stories.tsx
@@ -47,7 +47,7 @@ export const Ready = tests.createExample(ReadyView, {
// awaitingAmount: Amounts.parseOrThrow("USD:1"),
// granted: Amounts.parseOrThrow("USD:0"),
merchantName: "the merchant",
- products: [],
+ // products: [],
orderId: "abcdef",
});
@@ -60,18 +60,18 @@ export const WithAProductList = tests.createExample(ReadyView, {
// awaitingAmount: Amounts.parseOrThrow("USD:1"),
// granted: Amounts.parseOrThrow("USD:0"),
merchantName: "the merchant",
- products: [
- {
- description: "beer",
- image: beer,
- quantity: 2,
- },
- {
- description: "t-shirt",
- price: "EUR:1",
- quantity: 5,
- },
- ],
+ // products: [
+ // {
+ // description: "beer",
+ // image: beer,
+ // quantity: 2,
+ // },
+ // {
+ // description: "t-shirt",
+ // price: "EUR:1",
+ // quantity: 5,
+ // },
+ // ],
orderId: "abcdef",
});
diff --git a/packages/taler-wallet-webextension/src/cta/Refund/views.tsx b/packages/taler-wallet-webextension/src/cta/Refund/views.tsx
index accdab0c3..b6638cf8b 100644
--- a/packages/taler-wallet-webextension/src/cta/Refund/views.tsx
+++ b/packages/taler-wallet-webextension/src/cta/Refund/views.tsx
@@ -104,11 +104,11 @@ export function ReadyView(state: State.Ready): VNode {
kind="positive"
/> */}
</section>
- {state.products && state.products.length ? (
+ {/* {state.products && state.products.length ? (
<section>
<ProductList products={state.products} />
</section>
- ) : undefined}
+ ) : undefined} */}
<section>
<Button
variant="contained"
diff --git a/packages/taler-wallet-webextension/src/cta/TransferCreate/state.ts b/packages/taler-wallet-webextension/src/cta/TransferCreate/state.ts
index dcd41bcc1..77333e15c 100644
--- a/packages/taler-wallet-webextension/src/cta/TransferCreate/state.ts
+++ b/packages/taler-wallet-webextension/src/cta/TransferCreate/state.ts
@@ -118,8 +118,8 @@ export function useComponentState({
subject === undefined
? undefined
: !subject
- ? "Can't be empty"
- : undefined,
+ ? "Can't be empty"
+ : undefined,
value: subject ?? "",
onInput: pushAlertOnError(async (e) => setSubject(e)),
},
@@ -172,6 +172,10 @@ async function checkPeerPushDebitAndCheckMax(
//a good response that allow us to try again
throw e;
}
+ if (Amounts.cmp(newAmount, amount) === 1) {
+ //how can this happen?
+ throw e;
+ }
return checkPeerPushDebitAndCheckMax(api, Amounts.stringify(newAmount));
}
}
diff --git a/packages/taler-wallet-webextension/src/cta/TransferPickup/state.ts b/packages/taler-wallet-webextension/src/cta/TransferPickup/state.ts
index fb9acbe83..47c644736 100644
--- a/packages/taler-wallet-webextension/src/cta/TransferPickup/state.ts
+++ b/packages/taler-wallet-webextension/src/cta/TransferPickup/state.ts
@@ -58,7 +58,7 @@ export function useComponentState({
const {
contractTerms,
- peerPushPaymentIncomingId,
+ peerPushCreditId,
amountEffective,
amountRaw,
} = hook.response;
@@ -72,7 +72,7 @@ export function useComponentState({
const resp = await api.wallet.call(
WalletApiOperation.ConfirmPeerPushCredit,
{
- peerPushPaymentIncomingId,
+ peerPushCreditId,
},
);
onSuccess(resp.transactionId);
diff --git a/packages/taler-wallet-webextension/src/cta/Withdraw/state.ts b/packages/taler-wallet-webextension/src/cta/Withdraw/state.ts
index 72cabe5a4..a3855c2f4 100644
--- a/packages/taler-wallet-webextension/src/cta/Withdraw/state.ts
+++ b/packages/taler-wallet-webextension/src/cta/Withdraw/state.ts
@@ -23,7 +23,6 @@ import {
ExchangeTosStatus,
TalerError,
parseWithdrawExchangeUri,
- stringifyWithdrawUri,
} from "@gnu-taler/taler-util";
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
import { useEffect, useState } from "preact/hooks";
diff --git a/packages/taler-wallet-webextension/src/hooks/useDiagnostics.ts b/packages/taler-wallet-webextension/src/hooks/useDiagnostics.ts
deleted file mode 100644
index fcd31b3c6..000000000
--- a/packages/taler-wallet-webextension/src/hooks/useDiagnostics.ts
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2022 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/>
- */
-
-import { WalletDiagnostics } from "@gnu-taler/taler-util";
-import { useEffect, useState } from "preact/hooks";
-import { useBackendContext } from "../context/backend.js";
-
-export function useDiagnostics(): [WalletDiagnostics | undefined, boolean] {
- const [timedOut, setTimedOut] = useState(false);
- const api = useBackendContext();
- const [diagnostics, setDiagnostics] = useState<WalletDiagnostics | undefined>(
- undefined,
- );
-
- useEffect(() => {
- let gotDiagnostics = false;
- setTimeout(() => {
- if (!gotDiagnostics) {
- console.error("timed out");
- setTimedOut(true);
- }
- }, 1000);
- const doFetch = async (): Promise<void> => {
- const d = await api.background.call("getDiagnostics", undefined);
- gotDiagnostics = true;
- setDiagnostics(d);
- };
- doFetch();
- }, []);
- return [diagnostics, timedOut];
-}
diff --git a/packages/taler-wallet-webextension/src/wallet/DeveloperPage.tsx b/packages/taler-wallet-webextension/src/wallet/DeveloperPage.tsx
index c5e5c3c07..c972f0919 100644
--- a/packages/taler-wallet-webextension/src/wallet/DeveloperPage.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/DeveloperPage.tsx
@@ -27,24 +27,21 @@ import {
PendingTaskInfo,
WalletApiOperation,
} from "@gnu-taler/taler-wallet-core";
+import { useTranslationContext } from "@gnu-taler/web-util/browser";
import { format } from "date-fns";
-import { Fragment, h, VNode } from "preact";
+import { Fragment, VNode, h } from "preact";
import { useEffect, useRef, useState } from "preact/hooks";
-import { Diagnostics } from "../components/Diagnostics.js";
import { SelectList } from "../components/SelectList.js";
-import { NotifyUpdateFadeOut } from "../components/styled/index.js";
import { Time } from "../components/Time.js";
+import { NotifyUpdateFadeOut } from "../components/styled/index.js";
import { useBackendContext } from "../context/backend.js";
-import { useTranslationContext } from "@gnu-taler/web-util/browser";
import { useAsyncAsHook } from "../hooks/useAsyncAsHook.js";
-import { useDiagnostics } from "../hooks/useDiagnostics.js";
import { Button } from "../mui/Button.js";
import { Grid } from "../mui/Grid.js";
import { Paper } from "../mui/Paper.js";
import { TextField } from "../mui/TextField.js";
export function DeveloperPage(): VNode {
- const [status, timedOut] = useDiagnostics();
const listenAllEvents = Array.from<NotificationType>({ length: 1 });
@@ -73,13 +70,11 @@ export function DeveloperPage(): VNode {
response === undefined
? nonResponse
: response.hasError
- ? nonResponse
- : response.response;
+ ? nonResponse
+ : response.response;
return (
<View
- status={status}
- timedOut={timedOut}
operations={operations}
coins={coins}
exchanges={exchanges}
@@ -108,8 +103,6 @@ type SplitedCoinInfo = {
};
export interface Props {
- status: any;
- timedOut: boolean;
operations: PendingTaskInfo[];
coins: CoinsInfo;
exchanges: ExchangeListItem[];
@@ -121,8 +114,6 @@ function hashObjectId(o: any): string {
}
export function View({
- status,
- timedOut,
operations,
coins,
onDownloadDatabase,
@@ -458,7 +449,6 @@ export function View({
);
})}
<br />
- <Diagnostics diagnostics={status} timedOut={timedOut} />
{operations && operations.length > 0 && (
<Fragment>
<p>
diff --git a/packages/taler-wallet-webextension/src/wallet/ProviderAddPage.tsx b/packages/taler-wallet-webextension/src/wallet/ProviderAddPage.tsx
index b32eb831a..6ade0718a 100644
--- a/packages/taler-wallet-webextension/src/wallet/ProviderAddPage.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/ProviderAddPage.tsx
@@ -16,7 +16,6 @@
import {
Amounts,
- BackupBackupProviderTerms,
canonicalizeBaseUrl,
} from "@gnu-taler/taler-util";
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
@@ -41,6 +40,12 @@ interface Props {
onBack: () => Promise<void>;
}
+interface BackupBackupProviderTerms {
+ annual_fee: string;
+ storage_limit_in_megabytes: number;
+ supported_protocol_version: string;
+}
+
export function ProviderAddPage({ onBack }: Props): VNode {
const [verifying, setVerifying] = useState<
| { url: string; name: string; provider: BackupBackupProviderTerms }
diff --git a/packages/taler-wallet-webextension/src/wallet/Transaction.stories.tsx b/packages/taler-wallet-webextension/src/wallet/Transaction.stories.tsx
index 3ba3ac591..f1ae84ed3 100644
--- a/packages/taler-wallet-webextension/src/wallet/Transaction.stories.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/Transaction.stories.tsx
@@ -378,9 +378,9 @@ export const PaymentWithDeliveryDate = tests.createExample(TestedComponent, {
amountRaw: "KUDOS:12",
info: {
...exampleData.payment.info,
- delivery_date: {
- t_s: new Date().getTime() / 1000,
- },
+ // delivery_date: {
+ // t_s: new Date().getTime() / 1000,
+ // },
},
},
});
@@ -391,12 +391,12 @@ export const PaymentWithDeliveryAddr = tests.createExample(TestedComponent, {
amountRaw: "KUDOS:12",
info: {
...exampleData.payment.info,
- delivery_location: {
- country: "Argentina",
- street: "Elm Street",
- district: "CABA",
- post_code: "1101",
- },
+ // delivery_location: {
+ // country: "Argentina",
+ // street: "Elm Street",
+ // district: "CABA",
+ // post_code: "1101",
+ // },
},
},
});
@@ -407,15 +407,15 @@ export const PaymentWithDeliveryFull = tests.createExample(TestedComponent, {
amountRaw: "KUDOS:12",
info: {
...exampleData.payment.info,
- delivery_date: {
- t_s: new Date().getTime() / 1000,
- },
- delivery_location: {
- country: "Argentina",
- street: "Elm Street",
- district: "CABA",
- post_code: "1101",
- },
+ // delivery_date: {
+ // t_s: new Date().getTime() / 1000,
+ // },
+ // delivery_location: {
+ // country: "Argentina",
+ // street: "Elm Street",
+ // district: "CABA",
+ // post_code: "1101",
+ // },
},
},
});
diff --git a/packages/taler-wallet-webextension/src/wallet/Transaction.tsx b/packages/taler-wallet-webextension/src/wallet/Transaction.tsx
index e54137016..3b76558ce 100644
--- a/packages/taler-wallet-webextension/src/wallet/Transaction.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/Transaction.tsx
@@ -237,8 +237,8 @@ function TransactionTemplate({
<Fragment>
<section style={{ padding: 8, textAlign: "center" }}>
{transaction?.error &&
- // FIXME: wallet core should stop sending this error on KYC
- transaction.error.code !==
+ // FIXME: wallet core should stop sending this error on KYC
+ transaction.error.code !==
TalerErrorCode.WALLET_WITHDRAWAL_KYC_REQUIRED ? (
<ErrorAlertView
error={alertFromError(
@@ -464,7 +464,7 @@ export function TransactionView({
{/**FIXME: DD37 check if this holds */}
{transaction.txState.major !==
- TransactionMajorState.Pending ? undefined : transaction
+ TransactionMajorState.Pending ? undefined : transaction
.withdrawalDetails.type === WithdrawalType.ManualTransfer ? (
//manual withdrawal
<BankDetailsByPaytoType
@@ -1073,126 +1073,126 @@ export function MerchantDetails({
);
}
-function DeliveryDetails({
- date,
- location,
-}: {
- date: TalerProtocolTimestamp | undefined;
- location: Location | undefined;
-}): VNode {
- const { i18n } = useTranslationContext();
- return (
- <PurchaseDetailsTable>
- {location && (
- <Fragment>
- {location.country && (
- <tr>
- <td>
- <i18n.Translate>Country</i18n.Translate>
- </td>
- <td>{location.country}</td>
- </tr>
- )}
- {location.address_lines && (
- <tr>
- <td>
- <i18n.Translate>Address lines</i18n.Translate>
- </td>
- <td>{location.address_lines}</td>
- </tr>
- )}
- {location.building_number && (
- <tr>
- <td>
- <i18n.Translate>Building number</i18n.Translate>
- </td>
- <td>{location.building_number}</td>
- </tr>
- )}
- {location.building_name && (
- <tr>
- <td>
- <i18n.Translate>Building name</i18n.Translate>
- </td>
- <td>{location.building_name}</td>
- </tr>
- )}
- {location.street && (
- <tr>
- <td>
- <i18n.Translate>Street</i18n.Translate>
- </td>
- <td>{location.street}</td>
- </tr>
- )}
- {location.post_code && (
- <tr>
- <td>
- <i18n.Translate>Post code</i18n.Translate>
- </td>
- <td>{location.post_code}</td>
- </tr>
- )}
- {location.town_location && (
- <tr>
- <td>
- <i18n.Translate>Town location</i18n.Translate>
- </td>
- <td>{location.town_location}</td>
- </tr>
- )}
- {location.town && (
- <tr>
- <td>
- <i18n.Translate>Town</i18n.Translate>
- </td>
- <td>{location.town}</td>
- </tr>
- )}
- {location.district && (
- <tr>
- <td>
- <i18n.Translate>District</i18n.Translate>
- </td>
- <td>{location.district}</td>
- </tr>
- )}
- {location.country_subdivision && (
- <tr>
- <td>
- <i18n.Translate>Country subdivision</i18n.Translate>
- </td>
- <td>{location.country_subdivision}</td>
- </tr>
- )}
- </Fragment>
- )}
-
- {!location || !date ? undefined : (
- <tr>
- <td colSpan={2}>
- <hr />
- </td>
- </tr>
- )}
- {date && (
- <Fragment>
- <tr>
- <td>
- <i18n.Translate>Date</i18n.Translate>
- </td>
- <td>
- <Time
- timestamp={AbsoluteTime.fromProtocolTimestamp(date)}
- format="dd MMMM yyyy, HH:mm"
- />
- </td>
- </tr>
- </Fragment>
- )}
- </PurchaseDetailsTable>
- );
-}
+// function DeliveryDetails({
+// date,
+// location,
+// }: {
+// date: TalerProtocolTimestamp | undefined;
+// location: Location | undefined;
+// }): VNode {
+// const { i18n } = useTranslationContext();
+// return (
+// <PurchaseDetailsTable>
+// {location && (
+// <Fragment>
+// {location.country && (
+// <tr>
+// <td>
+// <i18n.Translate>Country</i18n.Translate>
+// </td>
+// <td>{location.country}</td>
+// </tr>
+// )}
+// {location.address_lines && (
+// <tr>
+// <td>
+// <i18n.Translate>Address lines</i18n.Translate>
+// </td>
+// <td>{location.address_lines}</td>
+// </tr>
+// )}
+// {location.building_number && (
+// <tr>
+// <td>
+// <i18n.Translate>Building number</i18n.Translate>
+// </td>
+// <td>{location.building_number}</td>
+// </tr>
+// )}
+// {location.building_name && (
+// <tr>
+// <td>
+// <i18n.Translate>Building name</i18n.Translate>
+// </td>
+// <td>{location.building_name}</td>
+// </tr>
+// )}
+// {location.street && (
+// <tr>
+// <td>
+// <i18n.Translate>Street</i18n.Translate>
+// </td>
+// <td>{location.street}</td>
+// </tr>
+// )}
+// {location.post_code && (
+// <tr>
+// <td>
+// <i18n.Translate>Post code</i18n.Translate>
+// </td>
+// <td>{location.post_code}</td>
+// </tr>
+// )}
+// {location.town_location && (
+// <tr>
+// <td>
+// <i18n.Translate>Town location</i18n.Translate>
+// </td>
+// <td>{location.town_location}</td>
+// </tr>
+// )}
+// {location.town && (
+// <tr>
+// <td>
+// <i18n.Translate>Town</i18n.Translate>
+// </td>
+// <td>{location.town}</td>
+// </tr>
+// )}
+// {location.district && (
+// <tr>
+// <td>
+// <i18n.Translate>District</i18n.Translate>
+// </td>
+// <td>{location.district}</td>
+// </tr>
+// )}
+// {location.country_subdivision && (
+// <tr>
+// <td>
+// <i18n.Translate>Country subdivision</i18n.Translate>
+// </td>
+// <td>{location.country_subdivision}</td>
+// </tr>
+// )}
+// </Fragment>
+// )}
+
+// {!location || !date ? undefined : (
+// <tr>
+// <td colSpan={2}>
+// <hr />
+// </td>
+// </tr>
+// )}
+// {date && (
+// <Fragment>
+// <tr>
+// <td>
+// <i18n.Translate>Date</i18n.Translate>
+// </td>
+// <td>
+// <Time
+// timestamp={AbsoluteTime.fromProtocolTimestamp(date)}
+// format="dd MMMM yyyy, HH:mm"
+// />
+// </td>
+// </tr>
+// </Fragment>
+// )}
+// </PurchaseDetailsTable>
+// );
+// }
export function ExchangeDetails({ exchange }: { exchange: string }): VNode {
return (
@@ -1475,10 +1475,10 @@ export function PurchaseDetails({
const total = Amounts.add(price.value, price.fee).amount;
- const hasProducts = info.products && info.products.length > 0;
+ // const hasProducts = info.products && info.products.length > 0;
- const hasShipping =
- info.delivery_date !== undefined || info.delivery_location !== undefined;
+ // const hasShipping =
+ // info.delivery_date !== undefined || info.delivery_location !== undefined;
const showLargePic = (): void => {
return;
@@ -1558,7 +1558,7 @@ export function PurchaseDetails({
</tr>
</Fragment>
)}
- {hasProducts && (
+ {/* {hasProducts && (
<tr>
<td colSpan={2}>
<PartCollapsible
@@ -1586,8 +1586,8 @@ export function PurchaseDetails({
/>
</td>
</tr>
- )}
- {hasShipping && (
+ )} */}
+ {/* {hasShipping && (
<tr>
<td colSpan={2}>
<PartCollapsible
@@ -1602,7 +1602,7 @@ export function PurchaseDetails({
/>
</td>
</tr>
- )}
+ )} */}
<tr>
<td>
<ShowFullContractTermPopup proposalId={proposalId} />
@@ -2008,7 +2008,7 @@ function ShowWithdrawalDetailForBankIntegrated({
/>
)}
{!transaction.withdrawalDetails.confirmed &&
- transaction.withdrawalDetails.bankConfirmationUrl ? (
+ transaction.withdrawalDetails.bankConfirmationUrl ? (
<InfoBox>
<div style={{ display: "block" }}>
<i18n.Translate>
diff --git a/packages/taler-wallet-webextension/src/wallet/Welcome.stories.tsx b/packages/taler-wallet-webextension/src/wallet/Welcome.stories.tsx
index 2cf28b611..dfce1c14b 100644
--- a/packages/taler-wallet-webextension/src/wallet/Welcome.stories.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/Welcome.stories.tsx
@@ -29,17 +29,9 @@ export default {
export const Normal = tests.createExample(TestedComponent, {
permissionToggle: { value: true, button: {} },
- diagnostics: {
- errors: [],
- walletManifestVersion: "1.0",
- walletManifestDisplayVersion: "1.0",
- firefoxIdbProblem: false,
- dbOutdated: false,
- },
});
export const TimedoutDiagnostics = tests.createExample(TestedComponent, {
- timedOut: true,
permissionToggle: { value: true, button: {} },
});
diff --git a/packages/taler-wallet-webextension/src/wallet/Welcome.tsx b/packages/taler-wallet-webextension/src/wallet/Welcome.tsx
index 8d348ca9d..e19152be2 100644
--- a/packages/taler-wallet-webextension/src/wallet/Welcome.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/Welcome.tsx
@@ -25,7 +25,6 @@ import { Fragment, h, VNode } from "preact";
import { Checkbox } from "../components/Checkbox.js";
import { SubTitle, Title } from "../components/styled/index.js";
import { useTranslationContext } from "@gnu-taler/web-util/browser";
-import { useDiagnostics } from "../hooks/useDiagnostics.js";
import { useSettings } from "../hooks/useSettings.js";
import { ToggleHandler } from "../mui/handlers.js";
import { platform } from "../platform/foreground.js";
@@ -34,7 +33,6 @@ import { useAlertContext } from "../context/alert.js";
export function WelcomePage(): VNode {
const [settings, updateSettings] = useSettings();
const { safely } = useAlertContext();
- const [diagnostics, timedOut] = useDiagnostics();
return (
<View
permissionToggle={{
@@ -45,21 +43,15 @@ export function WelcomePage(): VNode {
),
},
}}
- diagnostics={diagnostics}
- timedOut={timedOut}
/>
);
}
export interface ViewProps {
permissionToggle: ToggleHandler;
- diagnostics: WalletDiagnostics | undefined;
- timedOut: boolean;
}
export function View({
permissionToggle,
- diagnostics,
- timedOut,
}: ViewProps): VNode {
const { i18n } = useTranslationContext();
return (
diff --git a/packages/taler-wallet-webextension/src/wxApi.ts b/packages/taler-wallet-webextension/src/wxApi.ts
index 46c9f1b2d..004faad5c 100644
--- a/packages/taler-wallet-webextension/src/wxApi.ts
+++ b/packages/taler-wallet-webextension/src/wxApi.ts
@@ -70,10 +70,6 @@ export interface BackgroundOperations {
request: void;
response: void;
};
- getDiagnostics: {
- request: void;
- response: WalletDiagnostics;
- };
runGarbageCollector: {
request: void;
response: void;
diff --git a/packages/taler-wallet-webextension/src/wxBackend.ts b/packages/taler-wallet-webextension/src/wxBackend.ts
index e7385abe5..40b7077af 100644
--- a/packages/taler-wallet-webextension/src/wxBackend.ts
+++ b/packages/taler-wallet-webextension/src/wxBackend.ts
@@ -51,14 +51,11 @@ import {
importDb,
openPromise,
} from "@gnu-taler/taler-wallet-core";
-import {
- MessageFromBackend,
- MessageFromFrontend,
- MessageResponse,
-} from "./platform/api.js";
+import { MessageFromFrontend, MessageResponse } from "./platform/api.js";
import { platform } from "./platform/background.js";
import { ExtensionOperations } from "./taler-wallet-interaction-loader.js";
import { BackgroundOperations } from "./wxApi.js";
+import { HttpRequestLibrary } from "@gnu-taler/taler-util/http";
/**
* Currently active wallet instance. Might be unloaded and
@@ -79,43 +76,6 @@ const walletInit: OpenedPromise<void> = openPromise<void>();
const logger = new Logger("wxBackend.ts");
-async function getDiagnostics(): Promise<WalletDiagnostics> {
- const manifestData = platform.getWalletWebExVersion();
- const errors: string[] = [];
- let firefoxIdbProblem = false;
- let dbOutdated = false;
- try {
- await walletInit.promise;
- } catch (e) {
- errors.push("Error during wallet initialization: " + e);
- if (
- currentDatabase === undefined &&
- outdatedDbVersion === undefined &&
- platform.isFirefox()
- ) {
- firefoxIdbProblem = true;
- }
- }
- if (!currentWallet) {
- errors.push("Could not create wallet backend.");
- }
- if (!currentDatabase) {
- errors.push("Could not open database");
- }
- if (outdatedDbVersion !== undefined) {
- errors.push(`Outdated DB version: ${outdatedDbVersion}`);
- dbOutdated = true;
- }
- const diagnostics: WalletDiagnostics = {
- walletManifestDisplayVersion: manifestData.version_name || "(undefined)",
- walletManifestVersion: manifestData.version,
- errors,
- firefoxIdbProblem,
- dbOutdated,
- };
- return diagnostics;
-}
-
type BackendHandlerType = {
[Op in keyof BackgroundOperations]: (
req: BackgroundOperations[Op]["request"],
@@ -175,7 +135,6 @@ async function isInjectionEnabled(): Promise<boolean> {
const backendHandlers: BackendHandlerType = {
freeze,
sum,
- getDiagnostics,
resetDb,
runGarbageCollector,
setLoggingLevel,
@@ -297,7 +256,7 @@ async function reinitWallet(): Promise<void> {
}
currentDatabase = undefined;
// setBadgeText({ text: "" });
- let httpLib;
+ let httpLib: HttpRequestLibrary;
let cryptoWorker;
let timer;
@@ -318,7 +277,7 @@ async function reinitWallet(): Promise<void> {
logger.info("Setting up wallet");
const wallet = await Wallet.create(
indexedDB as any,
- httpLib,
+ httpLib as any,
timer,
cryptoWorker,
{
diff --git a/packages/taler-wallet-webextension/tsconfig.json b/packages/taler-wallet-webextension/tsconfig.json
index 773853302..08cbc871a 100644
--- a/packages/taler-wallet-webextension/tsconfig.json
+++ b/packages/taler-wallet-webextension/tsconfig.json
@@ -1,15 +1,12 @@
{
"compilerOptions": {
"composite": true,
- "lib": [
- "es2021",
- "DOM"
- ],
- "jsx": "react", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
- "jsxFactory": "h", /* Specify the JSX factory function to use when targeting react JSX emit, e.g. React.createElement or h. */
+ "lib": ["es2020", "DOM"],
+ "jsx": "react" /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */,
+ "jsxFactory": "h" /* Specify the JSX factory function to use when targeting react JSX emit, e.g. React.createElement or h. */,
"jsxFragmentFactory": "Fragment", // https://www.typescriptlang.org/docs/handbook/release-notes/typescript-4-0.html#custom-jsx-factories
"moduleResolution": "Node16",
- "module": "ES2020",
+ "module": "Node16",
"target": "ES6",
"skipLibCheck": true,
"preserveSymlinks": true,
@@ -23,9 +20,7 @@
"esModuleInterop": true,
"importHelpers": true,
"rootDir": "./src",
- "typeRoots": [
- "./node_modules/@types"
- ]
+ "typeRoots": ["./node_modules/@types"]
},
"references": [
{
@@ -35,7 +30,5 @@
"path": "../taler-util/"
}
],
- "include": [
- "src/**/*"
- ]
+ "include": ["src/**/*"]
}
diff --git a/packages/web-util/package.json b/packages/web-util/package.json
index 6cfe19258..81eee949a 100644
--- a/packages/web-util/package.json
+++ b/packages/web-util/package.json
@@ -56,7 +56,7 @@
"sass": "1.56.1",
"swr": "2.0.3",
"tslib": "^2.5.3",
- "typescript": "^5.1.3",
+ "typescript": "^5.2.2",
"ws": "7.4.5"
},
"dependencies": {
diff --git a/packages/web-util/tsconfig.json b/packages/web-util/tsconfig.json
index fc69cad06..a315dda1c 100644
--- a/packages/web-util/tsconfig.json
+++ b/packages/web-util/tsconfig.json
@@ -3,17 +3,14 @@
"composite": true,
"declaration": true,
"declarationMap": true,
- "target": "ES6",
- "module": "ESNext",
+ "target": "ES2020",
+ "module": "Node16",
"jsx": "react",
"jsxFactory": "h",
"jsxFragmentFactory": "Fragment",
"moduleResolution": "Node16",
"sourceMap": true,
- "lib": [
- "DOM",
- "es6"
- ],
+ "lib": ["DOM", "ES2020"],
"outDir": "lib",
"preserveSymlinks": true,
"skipLibCheck": true,
@@ -27,11 +24,7 @@
"esModuleInterop": true,
"importHelpers": true,
"rootDir": "./src",
- "typeRoots": [
- "./node_modules/@types"
- ]
+ "typeRoots": ["./node_modules/@types"]
},
- "include": [
- "src/**/*"
- ]
+ "include": ["src/**/*"]
}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 5b733353b..347dbf0a5 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -32,6 +32,9 @@ importers:
prettier:
specifier: ^2.8.8
version: 2.8.8
+ typescript:
+ specifier: ^5.2.2
+ version: 5.2.2
packages/aml-backoffice-ui:
dependencies:
@@ -92,7 +95,7 @@ importers:
version: 0.17.19
eslint-config-preact:
specifier: ^1.2.0
- version: 1.3.0(@typescript-eslint/eslint-plugin@5.41.0)(eslint@8.42.0)(typescript@5.1.3)
+ version: 1.3.0(@typescript-eslint/eslint-plugin@5.41.0)(eslint@8.48.0)(typescript@5.2.2)
mocha:
specifier: ^9.2.0
version: 9.2.2
@@ -109,8 +112,8 @@ importers:
specifier: ^3.3.2
version: 3.3.2
typescript:
- specifier: 5.1.3
- version: 5.1.3
+ specifier: 5.2.2
+ version: 5.2.2
packages/anastasis-cli:
dependencies:
@@ -134,11 +137,11 @@ importers:
specifier: ^3.0.2
version: 3.0.2
typedoc:
- specifier: ^0.24.8
- version: 0.24.8(typescript@5.1.3)
+ specifier: ^0.25.1
+ version: 0.25.1(typescript@5.2.2)
typescript:
- specifier: ^5.1.3
- version: 5.1.3
+ specifier: ^5.2.2
+ version: 5.2.2
packages/anastasis-core:
dependencies:
@@ -159,8 +162,8 @@ importers:
specifier: ^3.0.2
version: 3.0.2
typescript:
- specifier: ^5.1.3
- version: 5.1.3
+ specifier: ^5.2.2
+ version: 5.2.2
packages/anastasis-webui:
dependencies:
@@ -220,8 +223,8 @@ importers:
specifier: 1.56.1
version: 1.56.1
typescript:
- specifier: ^5.1.3
- version: 5.1.3
+ specifier: ^5.2.2
+ version: 5.2.2
packages/demobank-ui:
dependencies:
@@ -273,10 +276,10 @@ importers:
version: 18.11.17
'@typescript-eslint/eslint-plugin':
specifier: ^5.41.0
- version: 5.41.0(@typescript-eslint/parser@5.41.0)(eslint@8.42.0)(typescript@5.1.3)
+ version: 5.41.0(@typescript-eslint/parser@5.41.0)(eslint@8.48.0)(typescript@5.2.2)
'@typescript-eslint/parser':
specifier: ^5.41.0
- version: 5.41.0(eslint@8.42.0)(typescript@5.1.3)
+ version: 5.41.0(eslint@8.48.0)(typescript@5.2.2)
bulma:
specifier: ^0.9.4
version: 0.9.4
@@ -294,7 +297,7 @@ importers:
version: 0.17.7
eslint-config-preact:
specifier: ^1.2.0
- version: 1.3.0(@typescript-eslint/eslint-plugin@5.41.0)(eslint@8.42.0)(typescript@5.1.3)
+ version: 1.3.0(@typescript-eslint/eslint-plugin@5.41.0)(eslint@8.48.0)(typescript@5.2.2)
mocha:
specifier: ^9.2.0
version: 9.2.2
@@ -308,8 +311,8 @@ importers:
specifier: 1.56.1
version: 1.56.1
typescript:
- specifier: 5.1.3
- version: 5.1.3
+ specifier: 5.2.2
+ version: 5.2.2
packages/idb-bridge:
dependencies:
@@ -319,7 +322,7 @@ importers:
optionalDependencies:
better-sqlite3:
specifier: ^8.4.0
- version: 8.4.0
+ version: 8.6.0
devDependencies:
'@types/better-sqlite3':
specifier: ^7.6.4
@@ -337,8 +340,8 @@ importers:
specifier: ^5.0.1
version: 5.0.1
typescript:
- specifier: ^5.1.6
- version: 5.1.6
+ specifier: ^5.2.2
+ version: 5.2.2
packages/merchant-backend-ui:
dependencies:
@@ -381,10 +384,10 @@ importers:
version: 4.2.1
'@typescript-eslint/eslint-plugin':
specifier: ^4.22.0
- version: 4.33.0(@typescript-eslint/parser@4.33.0)(eslint@7.32.0)(typescript@5.1.3)
+ version: 4.33.0(@typescript-eslint/parser@4.33.0)(eslint@7.32.0)(typescript@5.2.2)
'@typescript-eslint/parser':
specifier: ^4.22.0
- version: 4.33.0(eslint@7.32.0)(typescript@5.1.3)
+ version: 4.33.0(eslint@7.32.0)(typescript@5.2.2)
babel-loader:
specifier: ^8.2.2
version: 8.2.5(@babel/core@7.18.9)(webpack@4.46.0)
@@ -396,7 +399,7 @@ importers:
version: 7.32.0
eslint-config-preact:
specifier: ^1.1.4
- version: 1.3.0(@typescript-eslint/eslint-plugin@4.33.0)(eslint@7.32.0)(typescript@5.1.3)
+ version: 1.3.0(@typescript-eslint/eslint-plugin@4.33.0)(eslint@7.32.0)(typescript@5.2.2)
eslint-plugin-header:
specifier: ^3.1.1
version: 3.1.1(eslint@7.32.0)
@@ -417,13 +420,13 @@ importers:
version: 1.0.14
ts-node:
specifier: ^10.9.1
- version: 10.9.1(@types/node@20.4.1)(typescript@5.1.3)
+ version: 10.9.1(@types/node@20.5.9)(typescript@5.2.2)
tslib:
specifier: 2.5.3
version: 2.5.3
typescript:
- specifier: 5.1.3
- version: 5.1.3
+ specifier: 5.2.2
+ version: 5.2.2
packages/merchant-backoffice-ui:
dependencies:
@@ -452,8 +455,8 @@ importers:
specifier: 1.4.4
version: 1.4.4
swr:
- specifier: 1.3.0
- version: 1.3.0(react@18.2.0)
+ specifier: 2.2.2
+ version: 2.2.2(react@18.2.0)
yup:
specifier: ^0.32.9
version: 0.32.11
@@ -478,10 +481,10 @@ importers:
version: 18.11.17
'@typescript-eslint/eslint-plugin':
specifier: ^4.22.0
- version: 4.33.0(@typescript-eslint/parser@4.33.0)(eslint@7.32.0)(typescript@5.1.3)
+ version: 4.33.0(@typescript-eslint/parser@4.33.0)(eslint@7.32.0)(typescript@5.2.2)
'@typescript-eslint/parser':
specifier: ^4.22.0
- version: 4.33.0(eslint@7.32.0)(typescript@5.1.3)
+ version: 4.33.0(eslint@7.32.0)(typescript@5.2.2)
base64-inline-loader:
specifier: ^1.1.1
version: 1.1.1(webpack@4.46.0)
@@ -517,7 +520,7 @@ importers:
version: 7.32.0
eslint-config-preact:
specifier: ^1.1.4
- version: 1.3.0(@typescript-eslint/eslint-plugin@4.33.0)(eslint@7.32.0)(typescript@5.1.3)
+ version: 1.3.0(@typescript-eslint/eslint-plugin@4.33.0)(eslint@7.32.0)(typescript@5.2.2)
eslint-plugin-header:
specifier: ^3.1.1
version: 3.1.1(eslint@7.32.0)
@@ -529,7 +532,7 @@ importers:
version: 0.0.10
html-webpack-skip-assets-plugin:
specifier: ^1.0.1
- version: 1.0.3(html-webpack-plugin@5.5.1)(webpack@4.46.0)
+ version: 1.0.3(html-webpack-plugin@5.5.3)(webpack@4.46.0)
inline-chunk-html-plugin:
specifier: ^1.1.1
version: 1.1.1
@@ -549,11 +552,11 @@ importers:
specifier: ^0.5.21
version: 0.5.21
typedoc:
- specifier: ^0.24.8
- version: 0.24.8(typescript@5.1.3)
+ specifier: ^0.25.1
+ version: 0.25.1(typescript@5.2.2)
typescript:
- specifier: 5.1.3
- version: 5.1.3
+ specifier: 5.2.2
+ version: 5.2.2
packages/pogen:
dependencies:
@@ -568,8 +571,8 @@ importers:
specifier: ^0.4.5
version: 0.4.5
typescript:
- specifier: ^5.1.3
- version: 5.1.3
+ specifier: ^5.2.2
+ version: 5.2.2
packages/taler-harness:
dependencies:
@@ -596,8 +599,8 @@ importers:
specifier: ^3.0.2
version: 3.0.2
typescript:
- specifier: ^5.1.3
- version: 5.1.3
+ specifier: ^5.2.2
+ version: 5.2.2
packages/taler-util:
dependencies:
@@ -633,8 +636,8 @@ importers:
specifier: ^3.0.2
version: 3.0.2
typescript:
- specifier: ^5.1.3
- version: 5.1.3
+ specifier: ^5.2.2
+ version: 5.2.2
packages/taler-wallet-cli:
dependencies:
@@ -658,11 +661,11 @@ importers:
specifier: ^3.0.2
version: 3.0.2
typedoc:
- specifier: ^0.24.8
- version: 0.24.8(typescript@5.1.3)
+ specifier: ^0.25.1
+ version: 0.25.1(typescript@5.2.2)
typescript:
- specifier: ^5.1.3
- version: 5.1.3
+ specifier: ^5.2.2
+ version: 5.2.2
packages/taler-wallet-core:
dependencies:
@@ -693,10 +696,10 @@ importers:
version: link:../pogen
'@typescript-eslint/eslint-plugin':
specifier: ^5.36.1
- version: 5.41.0(@typescript-eslint/parser@5.41.0)(eslint@8.26.0)(typescript@5.1.3)
+ version: 5.41.0(@typescript-eslint/parser@5.41.0)(eslint@8.26.0)(typescript@5.2.2)
'@typescript-eslint/parser':
specifier: ^5.36.1
- version: 5.41.0(eslint@8.26.0)(typescript@5.1.3)
+ version: 5.41.0(eslint@8.26.0)(typescript@5.2.2)
ava:
specifier: ^4.3.3
version: 4.3.3(@ava/typescript@4.0.0)
@@ -734,11 +737,11 @@ importers:
specifier: ^3.0.2
version: 3.0.2
typedoc:
- specifier: ^0.24.8
- version: 0.24.8(typescript@5.1.3)
+ specifier: ^0.25.1
+ version: 0.25.1(typescript@5.2.2)
typescript:
- specifier: ^5.1.3
- version: 5.1.3
+ specifier: ^5.2.2
+ version: 5.2.2
packages/taler-wallet-embedded:
dependencies:
@@ -803,10 +806,10 @@ importers:
devDependencies:
'@babel/preset-react':
specifier: ^7.22.3
- version: 7.22.3(@babel/core@7.18.9)
+ version: 7.22.3(@babel/core@7.22.15)
'@babel/preset-typescript':
specifier: 7.18.6
- version: 7.18.6(@babel/core@7.18.9)
+ version: 7.18.6(@babel/core@7.22.15)
'@gnu-taler/pogen':
specifier: workspace:*
version: link:../pogen
@@ -865,8 +868,8 @@ importers:
specifier: ^3.0.2
version: 3.0.2
typescript:
- specifier: 5.1.3
- version: 5.1.3
+ specifier: 5.2.2
+ version: 5.2.2
packages/web-util:
dependencies:
@@ -956,14 +959,19 @@ importers:
specifier: ^2.5.3
version: 2.5.3
typescript:
- specifier: ^5.1.3
- version: 5.1.3
+ specifier: ^5.2.2
+ version: 5.2.2
ws:
specifier: 7.4.5
version: 7.4.5
packages:
+ /@aashutoshrathi/word-wrap@1.2.6:
+ resolution: {integrity: sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==}
+ engines: {node: '>=0.10.0'}
+ dev: true
+
/@alloc/quick-lru@5.2.0:
resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==}
engines: {node: '>=10'}
@@ -975,6 +983,14 @@ packages:
'@jridgewell/gen-mapping': 0.1.1
'@jridgewell/trace-mapping': 0.3.17
+ /@ampproject/remapping@2.2.1:
+ resolution: {integrity: sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==}
+ engines: {node: '>=6.0.0'}
+ dependencies:
+ '@jridgewell/gen-mapping': 0.3.3
+ '@jridgewell/trace-mapping': 0.3.19
+ dev: true
+
/@apideck/better-ajv-errors@0.3.6(ajv@8.11.0):
resolution: {integrity: sha512-P+ZygBLZtkp0qqOAJJVX4oX/sFo5JR3eBWwwuqHHhK0GIgQOKWrAfiAaWX0aArHkRWHMuggFEgAZNxVPwPZYaA==}
engines: {node: '>=10'}
@@ -1014,6 +1030,14 @@ packages:
dependencies:
'@babel/highlight': 7.18.6
+ /@babel/code-frame@7.22.13:
+ resolution: {integrity: sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==}
+ engines: {node: '>=6.9.0'}
+ dependencies:
+ '@babel/highlight': 7.22.13
+ chalk: 2.4.2
+ dev: true
+
/@babel/compat-data@7.19.4:
resolution: {integrity: sha512-CHIGpJcUQ5lU9KrPHTjBMhVwQG6CQjxfg36fGXl3qk/Gik1WwWachaXFuo0uCWJT/mStOKtcbFJCaVLihC1CMw==}
engines: {node: '>=6.9.0'}
@@ -1028,6 +1052,11 @@ packages:
engines: {node: '>=6.9.0'}
dev: true
+ /@babel/compat-data@7.22.9:
+ resolution: {integrity: sha512-5UamI7xkUcJ3i9qVDS+KFDEK8/7oJ55/sJMB1Ge7IEapr7KfdfV/HErR+koZwOfd+SgtFKOKRhRakdg++DcJpQ==}
+ engines: {node: '>=6.9.0'}
+ dev: true
+
/@babel/core@7.13.16:
resolution: {integrity: sha512-sXHpixBiWWFti0AV2Zq7avpTasr6sIAu7Y396c608541qAU2ui4a193m0KSQmfPSKFZLnQ3cvlKDOm3XkuXm3Q==}
engines: {node: '>=6.9.0'}
@@ -1096,6 +1125,29 @@ packages:
- supports-color
dev: true
+ /@babel/core@7.22.15:
+ resolution: {integrity: sha512-PtZqMmgRrvj8ruoEOIwVA3yoF91O+Hgw9o7DAUTNBA6Mo2jpu31clx9a7Nz/9JznqetTR6zwfC4L3LAjKQXUwA==}
+ engines: {node: '>=6.9.0'}
+ dependencies:
+ '@ampproject/remapping': 2.2.1
+ '@babel/code-frame': 7.22.13
+ '@babel/generator': 7.22.15
+ '@babel/helper-compilation-targets': 7.22.15
+ '@babel/helper-module-transforms': 7.22.15(@babel/core@7.22.15)
+ '@babel/helpers': 7.22.15
+ '@babel/parser': 7.22.15
+ '@babel/template': 7.22.15
+ '@babel/traverse': 7.22.15
+ '@babel/types': 7.22.15
+ convert-source-map: 1.9.0
+ debug: 4.3.4
+ gensync: 1.0.0-beta.2
+ json5: 2.2.3
+ semver: 6.3.1
+ transitivePeerDependencies:
+ - supports-color
+ dev: true
+
/@babel/eslint-parser@7.19.1(@babel/core@7.18.9)(eslint@7.32.0):
resolution: {integrity: sha512-AqNf2QWt1rtu2/1rLswy6CDP7H9Oh3mMhk177Y67Rg8d7RD9WfOLLv8CGn6tisFvS2htm86yIe1yLF6I1UDaGQ==}
engines: {node: ^10.13.0 || ^12.13.0 || >=14.0.0}
@@ -1110,7 +1162,7 @@ packages:
semver: 6.3.0
dev: true
- /@babel/eslint-parser@7.19.1(@babel/core@7.18.9)(eslint@8.42.0):
+ /@babel/eslint-parser@7.19.1(@babel/core@7.18.9)(eslint@8.48.0):
resolution: {integrity: sha512-AqNf2QWt1rtu2/1rLswy6CDP7H9Oh3mMhk177Y67Rg8d7RD9WfOLLv8CGn6tisFvS2htm86yIe1yLF6I1UDaGQ==}
engines: {node: ^10.13.0 || ^12.13.0 || >=14.0.0}
peerDependencies:
@@ -1119,7 +1171,7 @@ packages:
dependencies:
'@babel/core': 7.18.9
'@nicolo-ribaudo/eslint-scope-5-internals': 5.1.1-v1
- eslint: 8.42.0
+ eslint: 8.48.0
eslint-visitor-keys: 2.1.0
semver: 6.3.0
dev: true
@@ -1142,6 +1194,16 @@ packages:
'@jridgewell/trace-mapping': 0.3.17
jsesc: 2.5.2
+ /@babel/generator@7.22.15:
+ resolution: {integrity: sha512-Zu9oWARBqeVOW0dZOjXc3JObrzuqothQ3y/n1kUtrjCoCPLkXUwMvOo/F/TCfoHMbWIFlWwpZtkZVb9ga4U2pA==}
+ engines: {node: '>=6.9.0'}
+ dependencies:
+ '@babel/types': 7.22.15
+ '@jridgewell/gen-mapping': 0.3.3
+ '@jridgewell/trace-mapping': 0.3.19
+ jsesc: 2.5.2
+ dev: true
+
/@babel/generator@7.22.3:
resolution: {integrity: sha512-C17MW4wlk//ES/CJDL51kPNwl+qiBQyN7b9SKyVp11BLGFeSPoVaHrv+MNt8jwQFhQWowW88z1eeBx3pFz9v8A==}
engines: {node: '>=6.9.0'}
@@ -1286,6 +1348,17 @@ packages:
semver: 6.3.0
dev: true
+ /@babel/helper-compilation-targets@7.22.15:
+ resolution: {integrity: sha512-y6EEzULok0Qvz8yyLkCvVX+02ic+By2UdOhylwUOvOn9dvYc9mKICJuuU1n1XBI02YWsNsnrY1kc6DVbjcXbtw==}
+ engines: {node: '>=6.9.0'}
+ dependencies:
+ '@babel/compat-data': 7.22.9
+ '@babel/helper-validator-option': 7.22.15
+ browserslist: 4.21.10
+ lru-cache: 5.1.1
+ semver: 6.3.1
+ dev: true
+
/@babel/helper-create-class-features-plugin@7.20.12(@babel/core@7.18.9):
resolution: {integrity: sha512-9OunRkbT0JQcednL0UFvbfXpAsUXiGjUk0a7sN8fUXX7Mue79cUSMjHGDRRi/Vz9vYlpIhLV5fMD5dKoMhhsNQ==}
engines: {node: '>=6.9.0'}
@@ -1324,6 +1397,25 @@ packages:
- supports-color
dev: true
+ /@babel/helper-create-class-features-plugin@7.20.12(@babel/core@7.22.15):
+ resolution: {integrity: sha512-9OunRkbT0JQcednL0UFvbfXpAsUXiGjUk0a7sN8fUXX7Mue79cUSMjHGDRRi/Vz9vYlpIhLV5fMD5dKoMhhsNQ==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0
+ dependencies:
+ '@babel/core': 7.22.15
+ '@babel/helper-annotate-as-pure': 7.18.6
+ '@babel/helper-environment-visitor': 7.18.9
+ '@babel/helper-function-name': 7.19.0
+ '@babel/helper-member-expression-to-functions': 7.20.7
+ '@babel/helper-optimise-call-expression': 7.18.6
+ '@babel/helper-replace-supers': 7.20.7
+ '@babel/helper-skip-transparent-expression-wrappers': 7.20.0
+ '@babel/helper-split-export-declaration': 7.18.6
+ transitivePeerDependencies:
+ - supports-color
+ dev: true
+
/@babel/helper-create-class-features-plugin@7.22.1(@babel/core@7.18.9):
resolution: {integrity: sha512-SowrZ9BWzYFgzUMwUmowbPSGu6CXL5MSuuCkG3bejahSpSymioPmuLdhPxNOc9MjuNGjy7M/HaXvJ8G82Lywlw==}
engines: {node: '>=6.9.0'}
@@ -1455,6 +1547,11 @@ packages:
resolution: {integrity: sha512-Z2tgopurB/kTbidvzeBrc2To3PUP/9i5MUe+fU6QJCQDyPwSH2oRapkLw3KGECDYSjhQZCNxEvNvZlLw8JjGwA==}
engines: {node: '>=6.9.0'}
+ /@babel/helper-environment-visitor@7.22.5:
+ resolution: {integrity: sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q==}
+ engines: {node: '>=6.9.0'}
+ dev: true
+
/@babel/helper-explode-assignable-expression@7.18.6:
resolution: {integrity: sha512-eyAYAsQmB80jNfg4baAtLeWAQHfHFiR483rzFK+BhETlGZaQC9bsfrugfXDCbRHLQbIA7U5NxhhOxN7p/dWIcg==}
engines: {node: '>=6.9.0'}
@@ -1477,12 +1574,27 @@ packages:
'@babel/template': 7.21.9
'@babel/types': 7.22.4
+ /@babel/helper-function-name@7.22.5:
+ resolution: {integrity: sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ==}
+ engines: {node: '>=6.9.0'}
+ dependencies:
+ '@babel/template': 7.22.15
+ '@babel/types': 7.22.15
+ dev: true
+
/@babel/helper-hoist-variables@7.18.6:
resolution: {integrity: sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/types': 7.22.4
+ /@babel/helper-hoist-variables@7.22.5:
+ resolution: {integrity: sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==}
+ engines: {node: '>=6.9.0'}
+ dependencies:
+ '@babel/types': 7.22.15
+ dev: true
+
/@babel/helper-member-expression-to-functions@7.18.9:
resolution: {integrity: sha512-RxifAh2ZoVU67PyKIO4AMi1wTenGfMR/O/ae0CCRqwgBAt5v7xjdtRw7UoSbsreKrQn5t7r89eruK/9JjYHuDg==}
engines: {node: '>=6.9.0'}
@@ -1517,6 +1629,13 @@ packages:
dependencies:
'@babel/types': 7.22.4
+ /@babel/helper-module-imports@7.22.15:
+ resolution: {integrity: sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==}
+ engines: {node: '>=6.9.0'}
+ dependencies:
+ '@babel/types': 7.22.15
+ dev: true
+
/@babel/helper-module-transforms@7.19.6:
resolution: {integrity: sha512-fCmcfQo/KYr/VXXDIyd3CBGZ6AFhPFy1TfSEJ+PilGVlQT6jcbqtHAM4C1EciRqMza7/TpOUZliuSH+U6HAhJw==}
engines: {node: '>=6.9.0'}
@@ -1564,6 +1683,20 @@ packages:
- supports-color
dev: true
+ /@babel/helper-module-transforms@7.22.15(@babel/core@7.22.15):
+ resolution: {integrity: sha512-l1UiX4UyHSFsYt17iQ3Se5pQQZZHa22zyIXURmvkmLCD4t/aU+dvNWHatKac/D9Vm9UES7nvIqHs4jZqKviUmQ==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0
+ dependencies:
+ '@babel/core': 7.22.15
+ '@babel/helper-environment-visitor': 7.22.5
+ '@babel/helper-module-imports': 7.22.15
+ '@babel/helper-simple-access': 7.22.5
+ '@babel/helper-split-export-declaration': 7.22.6
+ '@babel/helper-validator-identifier': 7.22.15
+ dev: true
+
/@babel/helper-optimise-call-expression@7.18.6:
resolution: {integrity: sha512-HP59oD9/fEHQkdcbgFCnbmgH5vIQTJbxh2yf+CdM89/glUNnuzr87Q8GIjGEnOktTROemO0Pe0iPAYbqZuOUiA==}
engines: {node: '>=6.9.0'}
@@ -1665,6 +1798,13 @@ packages:
dependencies:
'@babel/types': 7.22.4
+ /@babel/helper-simple-access@7.22.5:
+ resolution: {integrity: sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==}
+ engines: {node: '>=6.9.0'}
+ dependencies:
+ '@babel/types': 7.22.15
+ dev: true
+
/@babel/helper-skip-transparent-expression-wrappers@7.18.9:
resolution: {integrity: sha512-imytd2gHi3cJPsybLRbmFrF7u5BIEuI2cNheyKi3/iOBC63kNn3q8Crn2xVuESli0aM4KYsyEqKyS7lFL8YVtw==}
engines: {node: '>=6.9.0'}
@@ -1685,6 +1825,13 @@ packages:
dependencies:
'@babel/types': 7.22.4
+ /@babel/helper-split-export-declaration@7.22.6:
+ resolution: {integrity: sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==}
+ engines: {node: '>=6.9.0'}
+ dependencies:
+ '@babel/types': 7.22.15
+ dev: true
+
/@babel/helper-string-parser@7.19.4:
resolution: {integrity: sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==}
engines: {node: '>=6.9.0'}
@@ -1694,10 +1841,20 @@ packages:
resolution: {integrity: sha512-5pTUx3hAJaZIdW99sJ6ZUUgWq/Y+Hja7TowEnLNMm1VivRgZQL3vpBY3qUACVsvw+yQU6+YgfBVmcbLaZtrA1w==}
engines: {node: '>=6.9.0'}
+ /@babel/helper-string-parser@7.22.5:
+ resolution: {integrity: sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==}
+ engines: {node: '>=6.9.0'}
+ dev: true
+
/@babel/helper-validator-identifier@7.19.1:
resolution: {integrity: sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==}
engines: {node: '>=6.9.0'}
+ /@babel/helper-validator-identifier@7.22.15:
+ resolution: {integrity: sha512-4E/F9IIEi8WR94324mbDUMo074YTheJmd7eZF5vITTeYchqAi6sYXRLHUVsmkdmY4QjfKTcB2jB7dVP3NaBElQ==}
+ engines: {node: '>=6.9.0'}
+ dev: true
+
/@babel/helper-validator-option@7.18.6:
resolution: {integrity: sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==}
engines: {node: '>=6.9.0'}
@@ -1707,6 +1864,11 @@ packages:
resolution: {integrity: sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ==}
engines: {node: '>=6.9.0'}
+ /@babel/helper-validator-option@7.22.15:
+ resolution: {integrity: sha512-bMn7RmyFjY/mdECUbgn9eoSY4vqvacUnS9i9vGAGttgFWesO6B4CYWA7XlpbWgBt71iv/hfbPlynohStqnu5hA==}
+ engines: {node: '>=6.9.0'}
+ dev: true
+
/@babel/helper-wrap-function@7.19.0:
resolution: {integrity: sha512-txX8aN8CZyYGTwcLhlk87KRqncAzhh5TpQamZUa0/u3an36NtDpUP6bQgBCBcLeBs09R/OwQu3OjK0k/HwfNDg==}
engines: {node: '>=6.9.0'}
@@ -1740,6 +1902,17 @@ packages:
transitivePeerDependencies:
- supports-color
+ /@babel/helpers@7.22.15:
+ resolution: {integrity: sha512-7pAjK0aSdxOwR+CcYAqgWOGy5dcfvzsTIfFTb2odQqW47MDfv14UaJDY6eng8ylM2EaeKXdxaSWESbkmaQHTmw==}
+ engines: {node: '>=6.9.0'}
+ dependencies:
+ '@babel/template': 7.22.15
+ '@babel/traverse': 7.22.15
+ '@babel/types': 7.22.15
+ transitivePeerDependencies:
+ - supports-color
+ dev: true
+
/@babel/helpers@7.22.3:
resolution: {integrity: sha512-jBJ7jWblbgr7r6wYZHMdIqKc73ycaTcCaWRq4/2LpuPHcx7xMlZvpGQkOYc9HeSjn6rcx15CPlgVcBtZ4WZJ2w==}
engines: {node: '>=6.9.0'}
@@ -1759,6 +1932,15 @@ packages:
chalk: 2.4.2
js-tokens: 4.0.0
+ /@babel/highlight@7.22.13:
+ resolution: {integrity: sha512-C/BaXcnnvBCmHTpz/VGZ8jgtE2aYlW4hxDhseJAWZb7gqGM/qtCK6iZUb0TyKFf7BOUsBH7Q7fkRsDRhg1XklQ==}
+ engines: {node: '>=6.9.0'}
+ dependencies:
+ '@babel/helper-validator-identifier': 7.22.15
+ chalk: 2.4.2
+ js-tokens: 4.0.0
+ dev: true
+
/@babel/parser@7.19.6:
resolution: {integrity: sha512-h1IUp81s2JYJ3mRkdxJgs4UvmSsRvDrx5ICSJbPvtWYv5i1nTBGcBpnog+89rAFMwvvru6E5NUHdBe01UeSzYA==}
engines: {node: '>=6.0.0'}
@@ -1772,6 +1954,14 @@ packages:
dependencies:
'@babel/types': 7.21.5
+ /@babel/parser@7.22.15:
+ resolution: {integrity: sha512-RWmQ/sklUN9BvGGpCDgSubhHWfAx24XDTDObup4ffvxaYsptOg2P3KG0j+1eWKLxpkX0j0uHxmpq2Z1SP/VhxA==}
+ engines: {node: '>=6.0.0'}
+ hasBin: true
+ dependencies:
+ '@babel/types': 7.22.15
+ dev: true
+
/@babel/parser@7.22.4:
resolution: {integrity: sha512-VLLsx06XkEYqBtE5YGPwfSGwfrjnyPP5oiGty3S8pQLFDFLaS8VwWSIxkTXpcvr5zeYLE6+MBNl2npl/YnfofA==}
engines: {node: '>=6.0.0'}
@@ -2432,6 +2622,16 @@ packages:
'@babel/helper-plugin-utils': 7.21.5
dev: true
+ /@babel/plugin-syntax-jsx@7.21.4(@babel/core@7.22.15):
+ resolution: {integrity: sha512-5hewiLct5OKyh6PLKEYaFclcqtIgCb6bmELouxjF6up5q3Sov7rOayW4RwhbaBL0dit8rA80GNfY+UuDp2mBbQ==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+ dependencies:
+ '@babel/core': 7.22.15
+ '@babel/helper-plugin-utils': 7.21.5
+ dev: true
+
/@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.18.9):
resolution: {integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==}
peerDependencies:
@@ -2580,23 +2780,23 @@ packages:
'@babel/helper-plugin-utils': 7.21.5
dev: true
- /@babel/plugin-syntax-typescript@7.20.0(@babel/core@7.18.9):
+ /@babel/plugin-syntax-typescript@7.20.0(@babel/core@7.22.1):
resolution: {integrity: sha512-rd9TkG+u1CExzS4SM1BlMEhMXwFLKVjOAFFCDx9PbX5ycJWDoWMcwdJH9RhkPu1dOgn5TrxLot/Gx6lWFuAUNQ==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
- '@babel/core': 7.18.9
+ '@babel/core': 7.22.1
'@babel/helper-plugin-utils': 7.21.5
dev: true
- /@babel/plugin-syntax-typescript@7.20.0(@babel/core@7.22.1):
+ /@babel/plugin-syntax-typescript@7.20.0(@babel/core@7.22.15):
resolution: {integrity: sha512-rd9TkG+u1CExzS4SM1BlMEhMXwFLKVjOAFFCDx9PbX5ycJWDoWMcwdJH9RhkPu1dOgn5TrxLot/Gx6lWFuAUNQ==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
- '@babel/core': 7.22.1
+ '@babel/core': 7.22.15
'@babel/helper-plugin-utils': 7.21.5
dev: true
@@ -3520,6 +3720,16 @@ packages:
'@babel/helper-plugin-utils': 7.21.5
dev: true
+ /@babel/plugin-transform-react-display-name@7.18.6(@babel/core@7.22.15):
+ resolution: {integrity: sha512-TV4sQ+T013n61uMoygyMRm+xf04Bd5oqFpv2jAEQwSZ8NwQA7zeRPg1LMVg2PWi3zWBz+CLKD+v5bcpZ/BS0aA==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+ dependencies:
+ '@babel/core': 7.22.15
+ '@babel/helper-plugin-utils': 7.21.5
+ dev: true
+
/@babel/plugin-transform-react-jsx-development@7.18.6(@babel/core@7.18.9):
resolution: {integrity: sha512-SA6HEjwYFKF7WDjWcMcMGUimmw/nhNRDWxr+KaLSCrkD/LMDBvWRmHAYgE1HDeF8KUuI8OAu+RT6EOtKxSW2qA==}
engines: {node: '>=6.9.0'}
@@ -3530,6 +3740,16 @@ packages:
'@babel/plugin-transform-react-jsx': 7.22.3(@babel/core@7.18.9)
dev: true
+ /@babel/plugin-transform-react-jsx-development@7.18.6(@babel/core@7.22.15):
+ resolution: {integrity: sha512-SA6HEjwYFKF7WDjWcMcMGUimmw/nhNRDWxr+KaLSCrkD/LMDBvWRmHAYgE1HDeF8KUuI8OAu+RT6EOtKxSW2qA==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+ dependencies:
+ '@babel/core': 7.22.15
+ '@babel/plugin-transform-react-jsx': 7.22.3(@babel/core@7.22.15)
+ dev: true
+
/@babel/plugin-transform-react-jsx@7.19.0(@babel/core@7.22.1):
resolution: {integrity: sha512-UVEvX3tXie3Szm3emi1+G63jyw1w5IcMY0FSKM+CRnKRI5Mr1YbCNgsSTwoTwKphQEG9P+QqmuRFneJPZuHNhg==}
engines: {node: '>=6.9.0'}
@@ -3558,6 +3778,20 @@ packages:
'@babel/types': 7.22.4
dev: true
+ /@babel/plugin-transform-react-jsx@7.22.3(@babel/core@7.22.15):
+ resolution: {integrity: sha512-JEulRWG2f04a7L8VWaOngWiK6p+JOSpB+DAtwfJgOaej1qdbNxqtK7MwTBHjUA10NeFcszlFNqCdbRcirzh2uQ==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+ dependencies:
+ '@babel/core': 7.22.15
+ '@babel/helper-annotate-as-pure': 7.18.6
+ '@babel/helper-module-imports': 7.21.4
+ '@babel/helper-plugin-utils': 7.21.5
+ '@babel/plugin-syntax-jsx': 7.21.4(@babel/core@7.22.15)
+ '@babel/types': 7.22.4
+ dev: true
+
/@babel/plugin-transform-react-pure-annotations@7.18.6(@babel/core@7.18.9):
resolution: {integrity: sha512-I8VfEPg9r2TRDdvnHgPepTKvuRomzA8+u+nhY7qSI1fR2hRNebasZEETLyM5mAUr0Ku56OkXJ0I7NHJnO6cJiQ==}
engines: {node: '>=6.9.0'}
@@ -3569,6 +3803,17 @@ packages:
'@babel/helper-plugin-utils': 7.21.5
dev: true
+ /@babel/plugin-transform-react-pure-annotations@7.18.6(@babel/core@7.22.15):
+ resolution: {integrity: sha512-I8VfEPg9r2TRDdvnHgPepTKvuRomzA8+u+nhY7qSI1fR2hRNebasZEETLyM5mAUr0Ku56OkXJ0I7NHJnO6cJiQ==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+ dependencies:
+ '@babel/core': 7.22.15
+ '@babel/helper-annotate-as-pure': 7.18.6
+ '@babel/helper-plugin-utils': 7.21.5
+ dev: true
+
/@babel/plugin-transform-regenerator@7.18.6(@babel/core@7.18.9):
resolution: {integrity: sha512-poqRI2+qiSdeldcz4wTSTXBRryoq3Gc70ye7m7UD5Ww0nE29IXqMl6r7Nd15WBgRd74vloEMlShtH6CKxVzfmQ==}
engines: {node: '>=6.9.0'}
@@ -3769,30 +4014,30 @@ packages:
'@babel/helper-plugin-utils': 7.21.5
dev: true
- /@babel/plugin-transform-typescript@7.20.13(@babel/core@7.18.9):
+ /@babel/plugin-transform-typescript@7.20.13(@babel/core@7.22.1):
resolution: {integrity: sha512-O7I/THxarGcDZxkgWKMUrk7NK1/WbHAg3Xx86gqS6x9MTrNL6AwIluuZ96ms4xeDe6AVx6rjHbWHP7x26EPQBA==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
- '@babel/core': 7.18.9
- '@babel/helper-create-class-features-plugin': 7.20.12(@babel/core@7.18.9)
+ '@babel/core': 7.22.1
+ '@babel/helper-create-class-features-plugin': 7.20.12(@babel/core@7.22.1)
'@babel/helper-plugin-utils': 7.21.5
- '@babel/plugin-syntax-typescript': 7.20.0(@babel/core@7.18.9)
+ '@babel/plugin-syntax-typescript': 7.20.0(@babel/core@7.22.1)
transitivePeerDependencies:
- supports-color
dev: true
- /@babel/plugin-transform-typescript@7.20.13(@babel/core@7.22.1):
+ /@babel/plugin-transform-typescript@7.20.13(@babel/core@7.22.15):
resolution: {integrity: sha512-O7I/THxarGcDZxkgWKMUrk7NK1/WbHAg3Xx86gqS6x9MTrNL6AwIluuZ96ms4xeDe6AVx6rjHbWHP7x26EPQBA==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
- '@babel/core': 7.22.1
- '@babel/helper-create-class-features-plugin': 7.20.12(@babel/core@7.22.1)
+ '@babel/core': 7.22.15
+ '@babel/helper-create-class-features-plugin': 7.20.12(@babel/core@7.22.15)
'@babel/helper-plugin-utils': 7.21.5
- '@babel/plugin-syntax-typescript': 7.20.0(@babel/core@7.22.1)
+ '@babel/plugin-syntax-typescript': 7.20.0(@babel/core@7.22.15)
transitivePeerDependencies:
- supports-color
dev: true
@@ -4190,30 +4435,45 @@ packages:
'@babel/plugin-transform-react-pure-annotations': 7.18.6(@babel/core@7.18.9)
dev: true
- /@babel/preset-typescript@7.18.6(@babel/core@7.18.9):
+ /@babel/preset-react@7.22.3(@babel/core@7.22.15):
+ resolution: {integrity: sha512-lxDz1mnZ9polqClBCVBjIVUypoB4qV3/tZUDb/IlYbW1kiiLaXaX+bInbRjl+lNQ/iUZraQ3+S8daEmoELMWug==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+ dependencies:
+ '@babel/core': 7.22.15
+ '@babel/helper-plugin-utils': 7.21.5
+ '@babel/helper-validator-option': 7.21.0
+ '@babel/plugin-transform-react-display-name': 7.18.6(@babel/core@7.22.15)
+ '@babel/plugin-transform-react-jsx': 7.22.3(@babel/core@7.22.15)
+ '@babel/plugin-transform-react-jsx-development': 7.18.6(@babel/core@7.22.15)
+ '@babel/plugin-transform-react-pure-annotations': 7.18.6(@babel/core@7.22.15)
+ dev: true
+
+ /@babel/preset-typescript@7.18.6(@babel/core@7.22.1):
resolution: {integrity: sha512-s9ik86kXBAnD760aybBucdpnLsAt0jK1xqJn2juOn9lkOvSHV60os5hxoVJsPzMQxvnUJFAlkont2DvvaYEBtQ==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
- '@babel/core': 7.18.9
+ '@babel/core': 7.22.1
'@babel/helper-plugin-utils': 7.19.0
'@babel/helper-validator-option': 7.18.6
- '@babel/plugin-transform-typescript': 7.20.13(@babel/core@7.18.9)
+ '@babel/plugin-transform-typescript': 7.20.13(@babel/core@7.22.1)
transitivePeerDependencies:
- supports-color
dev: true
- /@babel/preset-typescript@7.18.6(@babel/core@7.22.1):
+ /@babel/preset-typescript@7.18.6(@babel/core@7.22.15):
resolution: {integrity: sha512-s9ik86kXBAnD760aybBucdpnLsAt0jK1xqJn2juOn9lkOvSHV60os5hxoVJsPzMQxvnUJFAlkont2DvvaYEBtQ==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
dependencies:
- '@babel/core': 7.22.1
+ '@babel/core': 7.22.15
'@babel/helper-plugin-utils': 7.19.0
'@babel/helper-validator-option': 7.18.6
- '@babel/plugin-transform-typescript': 7.20.13(@babel/core@7.22.1)
+ '@babel/plugin-transform-typescript': 7.20.13(@babel/core@7.22.15)
transitivePeerDependencies:
- supports-color
dev: true
@@ -4284,6 +4544,15 @@ packages:
'@babel/parser': 7.22.4
'@babel/types': 7.22.4
+ /@babel/template@7.22.15:
+ resolution: {integrity: sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==}
+ engines: {node: '>=6.9.0'}
+ dependencies:
+ '@babel/code-frame': 7.22.13
+ '@babel/parser': 7.22.15
+ '@babel/types': 7.22.15
+ dev: true
+
/@babel/traverse@7.19.6:
resolution: {integrity: sha512-6l5HrUCzFM04mfbG09AagtYyR2P0B71B1wN7PfSPiksDPz2k5H9CBC1tcZpz2M8OxbKTPccByoOJ22rUKbpmQQ==}
engines: {node: '>=6.9.0'}
@@ -4319,6 +4588,24 @@ packages:
transitivePeerDependencies:
- supports-color
+ /@babel/traverse@7.22.15:
+ resolution: {integrity: sha512-DdHPwvJY0sEeN4xJU5uRLmZjgMMDIvMPniLuYzUVXj/GGzysPl0/fwt44JBkyUIzGJPV8QgHMcQdQ34XFuKTYQ==}
+ engines: {node: '>=6.9.0'}
+ dependencies:
+ '@babel/code-frame': 7.22.13
+ '@babel/generator': 7.22.15
+ '@babel/helper-environment-visitor': 7.22.5
+ '@babel/helper-function-name': 7.22.5
+ '@babel/helper-hoist-variables': 7.22.5
+ '@babel/helper-split-export-declaration': 7.22.6
+ '@babel/parser': 7.22.15
+ '@babel/types': 7.22.15
+ debug: 4.3.4
+ globals: 11.12.0
+ transitivePeerDependencies:
+ - supports-color
+ dev: true
+
/@babel/traverse@7.22.4:
resolution: {integrity: sha512-Tn1pDsjIcI+JcLKq1AVlZEr4226gpuAQTsLMorsYg9tuS/kG7nuwwJ4AB8jfQuEgb/COBwR/DqJxmoiYFu5/rQ==}
engines: {node: '>=6.9.0'}
@@ -4353,6 +4640,15 @@ packages:
'@babel/helper-validator-identifier': 7.19.1
to-fast-properties: 2.0.0
+ /@babel/types@7.22.15:
+ resolution: {integrity: sha512-X+NLXr0N8XXmN5ZsaQdm9U2SSC3UbIYq/doL++sueHOTisgZHoKaQtZxGuV2cUPQHMfjKEfg/g6oy7Hm6SKFtA==}
+ engines: {node: '>=6.9.0'}
+ dependencies:
+ '@babel/helper-string-parser': 7.22.5
+ '@babel/helper-validator-identifier': 7.22.15
+ to-fast-properties: 2.0.0
+ dev: true
+
/@babel/types@7.22.4:
resolution: {integrity: sha512-Tx9x3UBHTTsMSW85WB2kphxYQVvrZ/t1FxD88IpSgIjiUJlCm9z+xWIDwyo1vffTwSqteqyznB8ZE9vYYk16zA==}
engines: {node: '>=6.9.0'}
@@ -4792,18 +5088,18 @@ packages:
dev: true
optional: true
- /@eslint-community/eslint-utils@4.4.0(eslint@8.42.0):
+ /@eslint-community/eslint-utils@4.4.0(eslint@8.48.0):
resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
peerDependencies:
eslint: ^6.0.0 || ^7.0.0 || >=8.0.0
dependencies:
- eslint: 8.42.0
- eslint-visitor-keys: 3.4.1
+ eslint: 8.48.0
+ eslint-visitor-keys: 3.4.3
dev: true
- /@eslint-community/regexpp@4.5.1:
- resolution: {integrity: sha512-Z5ba73P98O1KUYCCJTUeVpja9RcGoMdncZ6T49FCUl2lN38JtCJ+3WgIDBv0AuY4WChU5PmtJmOCTlN6FZTFKQ==}
+ /@eslint-community/regexpp@4.8.0:
+ resolution: {integrity: sha512-JylOEEzDiOryeUnFbQz+oViCXS0KsvR1mvHkoMiu5+UiBvy+RYX7tzlIIIEstF/gVa2tj9AQXk3dgnxv6KxhFg==}
engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0}
dev: true
@@ -4841,14 +5137,14 @@ packages:
- supports-color
dev: true
- /@eslint/eslintrc@2.0.3:
- resolution: {integrity: sha512-+5gy6OQfk+xx3q0d6jGZZC3f3KzAkXc/IanVxd1is/VIIziRqqt3ongQz0FiTUXqTk0c7aDB3OaFuKnuSoJicQ==}
+ /@eslint/eslintrc@2.1.2:
+ resolution: {integrity: sha512-+wvgpDsrB1YqAMdEUCcnTlpfVBH7Vqn6A/NT3D8WVXFIaKMlErPIZT3oCIAVCOtarRpMtelZLqJeU3t7WY6X6g==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
dependencies:
ajv: 6.12.6
debug: 4.3.4
- espree: 9.5.2
- globals: 13.20.0
+ espree: 9.6.1
+ globals: 13.21.0
ignore: 5.2.4
import-fresh: 3.3.0
js-yaml: 4.1.0
@@ -4858,8 +5154,8 @@ packages:
- supports-color
dev: true
- /@eslint/js@8.42.0:
- resolution: {integrity: sha512-6SWlXpWU5AvId8Ac7zjzmIOqMOba/JWY8XZ4A7q7Gn1Vlfg/SFFIlrtHXt9nPn4op9ZPAkl91Jao+QQv3r/ukw==}
+ /@eslint/js@8.48.0:
+ resolution: {integrity: sha512-ZSjtmelB7IJfWD2Fvb7+Z+ChTIKWq6kjda95fLcQKNS5aheVHn4IkfgRQE3sIIzTcSLwLcLZUD9UBt+V7+h+Pw==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
dev: true
@@ -4887,8 +5183,8 @@ packages:
react: 18.2.0
dev: false
- /@humanwhocodes/config-array@0.11.10:
- resolution: {integrity: sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==}
+ /@humanwhocodes/config-array@0.11.11:
+ resolution: {integrity: sha512-N2brEuAadi0CcdeMXUkhbZB84eskAc8MEX1By6qEchoVywSgXPIjou4rYsl0V3Hj0ZnuGycGCjdNgockbzeWNA==}
engines: {node: '>=10.10.0'}
dependencies:
'@humanwhocodes/object-schema': 1.2.1
@@ -4978,13 +5274,18 @@ packages:
dependencies:
'@jridgewell/set-array': 1.1.2
'@jridgewell/sourcemap-codec': 1.4.15
- '@jridgewell/trace-mapping': 0.3.18
+ '@jridgewell/trace-mapping': 0.3.19
dev: true
/@jridgewell/resolve-uri@3.1.0:
resolution: {integrity: sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==}
engines: {node: '>=6.0.0'}
+ /@jridgewell/resolve-uri@3.1.1:
+ resolution: {integrity: sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==}
+ engines: {node: '>=6.0.0'}
+ dev: true
+
/@jridgewell/set-array@1.1.2:
resolution: {integrity: sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==}
engines: {node: '>=6.0.0'}
@@ -4996,11 +5297,11 @@ packages:
'@jridgewell/trace-mapping': 0.3.17
dev: true
- /@jridgewell/source-map@0.3.3:
- resolution: {integrity: sha512-b+fsZXeLYi9fEULmfBrhxn4IrPlINf8fiNarzTof004v3lFdntdwa9PF7vFJqm3mg7s+ScJMxXaE3Acp1irZcg==}
+ /@jridgewell/source-map@0.3.5:
+ resolution: {integrity: sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ==}
dependencies:
'@jridgewell/gen-mapping': 0.3.3
- '@jridgewell/trace-mapping': 0.3.18
+ '@jridgewell/trace-mapping': 0.3.19
dev: true
/@jridgewell/sourcemap-codec@1.4.14:
@@ -5016,11 +5317,11 @@ packages:
'@jridgewell/resolve-uri': 3.1.0
'@jridgewell/sourcemap-codec': 1.4.14
- /@jridgewell/trace-mapping@0.3.18:
- resolution: {integrity: sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==}
+ /@jridgewell/trace-mapping@0.3.19:
+ resolution: {integrity: sha512-kf37QtfW+Hwx/buWGMPcR60iF9ziHa6r/CZJIHbmcm4+0qrXiVdxegAH0F6yddEVQ7zdkjcGCgCzUu+BcbhQxw==}
dependencies:
- '@jridgewell/resolve-uri': 3.1.0
- '@jridgewell/sourcemap-codec': 1.4.14
+ '@jridgewell/resolve-uri': 3.1.1
+ '@jridgewell/sourcemap-codec': 1.4.15
dev: true
/@jridgewell/trace-mapping@0.3.9:
@@ -5731,6 +6032,10 @@ packages:
resolution: {integrity: sha512-JIzsAvJeA/5iY6Y/OxZbv1lUcc8dNSE77lb2gnBH+/PJ3lFR1Ccvgwl5JWnHAkNHcRsT0TbpVOsiMKZ1F/yyJg==}
dev: true
+ /@types/node@20.5.9:
+ resolution: {integrity: sha512-PcGNd//40kHAS3sTlzKB9C9XL4K0sTup8nbG5lC14kzEteTNuAFh9u5nA0o5TWnSG2r/JNPRXFVcHJIIeRlmqQ==}
+ dev: true
+
/@types/parse-json@4.0.0:
resolution: {integrity: sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==}
dev: true
@@ -5833,7 +6138,7 @@ packages:
'@types/node': 18.11.17
dev: true
- /@typescript-eslint/eslint-plugin@4.33.0(@typescript-eslint/parser@4.33.0)(eslint@7.32.0)(typescript@5.1.3):
+ /@typescript-eslint/eslint-plugin@4.33.0(@typescript-eslint/parser@4.33.0)(eslint@7.32.0)(typescript@5.2.2):
resolution: {integrity: sha512-aINiAxGVdOl1eJyVjaWn/YcVAq4Gi/Yo35qHGCnqbWVz61g39D0h23veY/MA0rFFGfxK7TySg2uwDeNv+JgVpg==}
engines: {node: ^10.12.0 || >=12.0.0}
peerDependencies:
@@ -5844,8 +6149,8 @@ packages:
typescript:
optional: true
dependencies:
- '@typescript-eslint/experimental-utils': 4.33.0(eslint@7.32.0)(typescript@5.1.3)
- '@typescript-eslint/parser': 4.33.0(eslint@7.32.0)(typescript@5.1.3)
+ '@typescript-eslint/experimental-utils': 4.33.0(eslint@7.32.0)(typescript@5.2.2)
+ '@typescript-eslint/parser': 4.33.0(eslint@7.32.0)(typescript@5.2.2)
'@typescript-eslint/scope-manager': 4.33.0
debug: 4.3.4
eslint: 7.32.0
@@ -5853,13 +6158,13 @@ packages:
ignore: 5.2.0
regexpp: 3.2.0
semver: 7.3.8
- tsutils: 3.21.0(typescript@5.1.3)
- typescript: 5.1.3
+ tsutils: 3.21.0(typescript@5.2.2)
+ typescript: 5.2.2
transitivePeerDependencies:
- supports-color
dev: true
- /@typescript-eslint/eslint-plugin@5.41.0(@typescript-eslint/parser@5.41.0)(eslint@8.26.0)(typescript@5.1.3):
+ /@typescript-eslint/eslint-plugin@5.41.0(@typescript-eslint/parser@5.41.0)(eslint@8.26.0)(typescript@5.2.2):
resolution: {integrity: sha512-DXUS22Y57/LAFSg3x7Vi6RNAuLpTXwxB9S2nIA7msBb/Zt8p7XqMwdpdc1IU7CkOQUPgAqR5fWvxuKCbneKGmA==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
peerDependencies:
@@ -5870,22 +6175,22 @@ packages:
typescript:
optional: true
dependencies:
- '@typescript-eslint/parser': 5.41.0(eslint@8.26.0)(typescript@5.1.3)
+ '@typescript-eslint/parser': 5.41.0(eslint@8.26.0)(typescript@5.2.2)
'@typescript-eslint/scope-manager': 5.41.0
- '@typescript-eslint/type-utils': 5.41.0(eslint@8.26.0)(typescript@5.1.3)
- '@typescript-eslint/utils': 5.41.0(eslint@8.26.0)(typescript@5.1.3)
+ '@typescript-eslint/type-utils': 5.41.0(eslint@8.26.0)(typescript@5.2.2)
+ '@typescript-eslint/utils': 5.41.0(eslint@8.26.0)(typescript@5.2.2)
debug: 4.3.4
eslint: 8.26.0
ignore: 5.2.0
regexpp: 3.2.0
semver: 7.3.8
- tsutils: 3.21.0(typescript@5.1.3)
- typescript: 5.1.3
+ tsutils: 3.21.0(typescript@5.2.2)
+ typescript: 5.2.2
transitivePeerDependencies:
- supports-color
dev: true
- /@typescript-eslint/eslint-plugin@5.41.0(@typescript-eslint/parser@5.41.0)(eslint@8.42.0)(typescript@5.1.3):
+ /@typescript-eslint/eslint-plugin@5.41.0(@typescript-eslint/parser@5.41.0)(eslint@8.48.0)(typescript@5.2.2):
resolution: {integrity: sha512-DXUS22Y57/LAFSg3x7Vi6RNAuLpTXwxB9S2nIA7msBb/Zt8p7XqMwdpdc1IU7CkOQUPgAqR5fWvxuKCbneKGmA==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
peerDependencies:
@@ -5896,22 +6201,22 @@ packages:
typescript:
optional: true
dependencies:
- '@typescript-eslint/parser': 5.41.0(eslint@8.42.0)(typescript@5.1.3)
+ '@typescript-eslint/parser': 5.41.0(eslint@8.48.0)(typescript@5.2.2)
'@typescript-eslint/scope-manager': 5.41.0
- '@typescript-eslint/type-utils': 5.41.0(eslint@8.42.0)(typescript@5.1.3)
- '@typescript-eslint/utils': 5.41.0(eslint@8.42.0)(typescript@5.1.3)
+ '@typescript-eslint/type-utils': 5.41.0(eslint@8.48.0)(typescript@5.2.2)
+ '@typescript-eslint/utils': 5.41.0(eslint@8.48.0)(typescript@5.2.2)
debug: 4.3.4
- eslint: 8.42.0
+ eslint: 8.48.0
ignore: 5.2.0
regexpp: 3.2.0
semver: 7.3.8
- tsutils: 3.21.0(typescript@5.1.3)
- typescript: 5.1.3
+ tsutils: 3.21.0(typescript@5.2.2)
+ typescript: 5.2.2
transitivePeerDependencies:
- supports-color
dev: true
- /@typescript-eslint/experimental-utils@4.33.0(eslint@7.32.0)(typescript@5.1.3):
+ /@typescript-eslint/experimental-utils@4.33.0(eslint@7.32.0)(typescript@5.2.2):
resolution: {integrity: sha512-zeQjOoES5JFjTnAhI5QY7ZviczMzDptls15GFsI6jyUOq0kOf9+WonkhtlIhh0RgHRnqj5gdNxW5j1EvAyYg6Q==}
engines: {node: ^10.12.0 || >=12.0.0}
peerDependencies:
@@ -5920,7 +6225,7 @@ packages:
'@types/json-schema': 7.0.11
'@typescript-eslint/scope-manager': 4.33.0
'@typescript-eslint/types': 4.33.0
- '@typescript-eslint/typescript-estree': 4.33.0(typescript@5.1.3)
+ '@typescript-eslint/typescript-estree': 4.33.0(typescript@5.2.2)
eslint: 7.32.0
eslint-scope: 5.1.1
eslint-utils: 3.0.0(eslint@7.32.0)
@@ -5929,33 +6234,33 @@ packages:
- typescript
dev: true
- /@typescript-eslint/experimental-utils@5.41.0(eslint@7.32.0)(typescript@5.1.3):
+ /@typescript-eslint/experimental-utils@5.41.0(eslint@7.32.0)(typescript@5.2.2):
resolution: {integrity: sha512-/qxT2Kd2q/A22JVIllvws4rvc00/3AT4rAo/0YgEN28y+HPhbJbk6X4+MAHEoZzpNyAOugIT7D/OLnKBW8FfhA==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
peerDependencies:
eslint: ^6.0.0 || ^7.0.0 || ^8.0.0
dependencies:
- '@typescript-eslint/utils': 5.41.0(eslint@7.32.0)(typescript@5.1.3)
+ '@typescript-eslint/utils': 5.41.0(eslint@7.32.0)(typescript@5.2.2)
eslint: 7.32.0
transitivePeerDependencies:
- supports-color
- typescript
dev: true
- /@typescript-eslint/experimental-utils@5.41.0(eslint@8.42.0)(typescript@5.1.3):
+ /@typescript-eslint/experimental-utils@5.41.0(eslint@8.48.0)(typescript@5.2.2):
resolution: {integrity: sha512-/qxT2Kd2q/A22JVIllvws4rvc00/3AT4rAo/0YgEN28y+HPhbJbk6X4+MAHEoZzpNyAOugIT7D/OLnKBW8FfhA==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
peerDependencies:
eslint: ^6.0.0 || ^7.0.0 || ^8.0.0
dependencies:
- '@typescript-eslint/utils': 5.41.0(eslint@8.42.0)(typescript@5.1.3)
- eslint: 8.42.0
+ '@typescript-eslint/utils': 5.41.0(eslint@8.48.0)(typescript@5.2.2)
+ eslint: 8.48.0
transitivePeerDependencies:
- supports-color
- typescript
dev: true
- /@typescript-eslint/parser@4.33.0(eslint@7.32.0)(typescript@5.1.3):
+ /@typescript-eslint/parser@4.33.0(eslint@7.32.0)(typescript@5.2.2):
resolution: {integrity: sha512-ZohdsbXadjGBSK0/r+d87X0SBmKzOq4/S5nzK6SBgJspFo9/CUDJ7hjayuze+JK7CZQLDMroqytp7pOcFKTxZA==}
engines: {node: ^10.12.0 || >=12.0.0}
peerDependencies:
@@ -5967,15 +6272,15 @@ packages:
dependencies:
'@typescript-eslint/scope-manager': 4.33.0
'@typescript-eslint/types': 4.33.0
- '@typescript-eslint/typescript-estree': 4.33.0(typescript@5.1.3)
+ '@typescript-eslint/typescript-estree': 4.33.0(typescript@5.2.2)
debug: 4.3.4
eslint: 7.32.0
- typescript: 5.1.3
+ typescript: 5.2.2
transitivePeerDependencies:
- supports-color
dev: true
- /@typescript-eslint/parser@5.41.0(eslint@8.26.0)(typescript@5.1.3):
+ /@typescript-eslint/parser@5.41.0(eslint@8.26.0)(typescript@5.2.2):
resolution: {integrity: sha512-HQVfix4+RL5YRWZboMD1pUfFN8MpRH4laziWkkAzyO1fvNOY/uinZcvo3QiFJVS/siNHupV8E5+xSwQZrl6PZA==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
peerDependencies:
@@ -5987,15 +6292,15 @@ packages:
dependencies:
'@typescript-eslint/scope-manager': 5.41.0
'@typescript-eslint/types': 5.41.0
- '@typescript-eslint/typescript-estree': 5.41.0(typescript@5.1.3)
+ '@typescript-eslint/typescript-estree': 5.41.0(typescript@5.2.2)
debug: 4.3.4
eslint: 8.26.0
- typescript: 5.1.3
+ typescript: 5.2.2
transitivePeerDependencies:
- supports-color
dev: true
- /@typescript-eslint/parser@5.41.0(eslint@8.42.0)(typescript@5.1.3):
+ /@typescript-eslint/parser@5.41.0(eslint@8.48.0)(typescript@5.2.2):
resolution: {integrity: sha512-HQVfix4+RL5YRWZboMD1pUfFN8MpRH4laziWkkAzyO1fvNOY/uinZcvo3QiFJVS/siNHupV8E5+xSwQZrl6PZA==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
peerDependencies:
@@ -6007,10 +6312,10 @@ packages:
dependencies:
'@typescript-eslint/scope-manager': 5.41.0
'@typescript-eslint/types': 5.41.0
- '@typescript-eslint/typescript-estree': 5.41.0(typescript@5.1.3)
+ '@typescript-eslint/typescript-estree': 5.41.0(typescript@5.2.2)
debug: 4.3.4
- eslint: 8.42.0
- typescript: 5.1.3
+ eslint: 8.48.0
+ typescript: 5.2.2
transitivePeerDependencies:
- supports-color
dev: true
@@ -6031,7 +6336,7 @@ packages:
'@typescript-eslint/visitor-keys': 5.41.0
dev: true
- /@typescript-eslint/type-utils@5.41.0(eslint@8.26.0)(typescript@5.1.3):
+ /@typescript-eslint/type-utils@5.41.0(eslint@8.26.0)(typescript@5.2.2):
resolution: {integrity: sha512-L30HNvIG6A1Q0R58e4hu4h+fZqaO909UcnnPbwKiN6Rc3BUEx6ez2wgN7aC0cBfcAjZfwkzE+E2PQQ9nEuoqfA==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
peerDependencies:
@@ -6041,17 +6346,17 @@ packages:
typescript:
optional: true
dependencies:
- '@typescript-eslint/typescript-estree': 5.41.0(typescript@5.1.3)
- '@typescript-eslint/utils': 5.41.0(eslint@8.26.0)(typescript@5.1.3)
+ '@typescript-eslint/typescript-estree': 5.41.0(typescript@5.2.2)
+ '@typescript-eslint/utils': 5.41.0(eslint@8.26.0)(typescript@5.2.2)
debug: 4.3.4
eslint: 8.26.0
- tsutils: 3.21.0(typescript@5.1.3)
- typescript: 5.1.3
+ tsutils: 3.21.0(typescript@5.2.2)
+ typescript: 5.2.2
transitivePeerDependencies:
- supports-color
dev: true
- /@typescript-eslint/type-utils@5.41.0(eslint@8.42.0)(typescript@5.1.3):
+ /@typescript-eslint/type-utils@5.41.0(eslint@8.48.0)(typescript@5.2.2):
resolution: {integrity: sha512-L30HNvIG6A1Q0R58e4hu4h+fZqaO909UcnnPbwKiN6Rc3BUEx6ez2wgN7aC0cBfcAjZfwkzE+E2PQQ9nEuoqfA==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
peerDependencies:
@@ -6061,12 +6366,12 @@ packages:
typescript:
optional: true
dependencies:
- '@typescript-eslint/typescript-estree': 5.41.0(typescript@5.1.3)
- '@typescript-eslint/utils': 5.41.0(eslint@8.42.0)(typescript@5.1.3)
+ '@typescript-eslint/typescript-estree': 5.41.0(typescript@5.2.2)
+ '@typescript-eslint/utils': 5.41.0(eslint@8.48.0)(typescript@5.2.2)
debug: 4.3.4
- eslint: 8.42.0
- tsutils: 3.21.0(typescript@5.1.3)
- typescript: 5.1.3
+ eslint: 8.48.0
+ tsutils: 3.21.0(typescript@5.2.2)
+ typescript: 5.2.2
transitivePeerDependencies:
- supports-color
dev: true
@@ -6081,7 +6386,7 @@ packages:
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
dev: true
- /@typescript-eslint/typescript-estree@4.33.0(typescript@5.1.3):
+ /@typescript-eslint/typescript-estree@4.33.0(typescript@5.2.2):
resolution: {integrity: sha512-rkWRY1MPFzjwnEVHsxGemDzqqddw2QbTJlICPD9p9I9LfsO8fdmfQPOX3uKfUaGRDFJbfrtm/sXhVXN4E+bzCA==}
engines: {node: ^10.12.0 || >=12.0.0}
peerDependencies:
@@ -6096,13 +6401,13 @@ packages:
globby: 11.1.0
is-glob: 4.0.3
semver: 7.3.8
- tsutils: 3.21.0(typescript@5.1.3)
- typescript: 5.1.3
+ tsutils: 3.21.0(typescript@5.2.2)
+ typescript: 5.2.2
transitivePeerDependencies:
- supports-color
dev: true
- /@typescript-eslint/typescript-estree@5.41.0(typescript@5.1.3):
+ /@typescript-eslint/typescript-estree@5.41.0(typescript@5.2.2):
resolution: {integrity: sha512-SlzFYRwFSvswzDSQ/zPkIWcHv8O5y42YUskko9c4ki+fV6HATsTODUPbRbcGDFYP86gaJL5xohUEytvyNNcXWg==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
peerDependencies:
@@ -6117,13 +6422,13 @@ packages:
globby: 11.1.0
is-glob: 4.0.3
semver: 7.3.8
- tsutils: 3.21.0(typescript@5.1.3)
- typescript: 5.1.3
+ tsutils: 3.21.0(typescript@5.2.2)
+ typescript: 5.2.2
transitivePeerDependencies:
- supports-color
dev: true
- /@typescript-eslint/utils@5.41.0(eslint@7.32.0)(typescript@5.1.3):
+ /@typescript-eslint/utils@5.41.0(eslint@7.32.0)(typescript@5.2.2):
resolution: {integrity: sha512-QlvfwaN9jaMga9EBazQ+5DDx/4sAdqDkcs05AsQHMaopluVCUyu1bTRUVKzXbgjDlrRAQrYVoi/sXJ9fmG+KLQ==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
peerDependencies:
@@ -6133,7 +6438,7 @@ packages:
'@types/semver': 7.3.12
'@typescript-eslint/scope-manager': 5.41.0
'@typescript-eslint/types': 5.41.0
- '@typescript-eslint/typescript-estree': 5.41.0(typescript@5.1.3)
+ '@typescript-eslint/typescript-estree': 5.41.0(typescript@5.2.2)
eslint: 7.32.0
eslint-scope: 5.1.1
eslint-utils: 3.0.0(eslint@7.32.0)
@@ -6143,7 +6448,7 @@ packages:
- typescript
dev: true
- /@typescript-eslint/utils@5.41.0(eslint@8.26.0)(typescript@5.1.3):
+ /@typescript-eslint/utils@5.41.0(eslint@8.26.0)(typescript@5.2.2):
resolution: {integrity: sha512-QlvfwaN9jaMga9EBazQ+5DDx/4sAdqDkcs05AsQHMaopluVCUyu1bTRUVKzXbgjDlrRAQrYVoi/sXJ9fmG+KLQ==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
peerDependencies:
@@ -6153,7 +6458,7 @@ packages:
'@types/semver': 7.3.12
'@typescript-eslint/scope-manager': 5.41.0
'@typescript-eslint/types': 5.41.0
- '@typescript-eslint/typescript-estree': 5.41.0(typescript@5.1.3)
+ '@typescript-eslint/typescript-estree': 5.41.0(typescript@5.2.2)
eslint: 8.26.0
eslint-scope: 5.1.1
eslint-utils: 3.0.0(eslint@8.26.0)
@@ -6163,7 +6468,7 @@ packages:
- typescript
dev: true
- /@typescript-eslint/utils@5.41.0(eslint@8.42.0)(typescript@5.1.3):
+ /@typescript-eslint/utils@5.41.0(eslint@8.48.0)(typescript@5.2.2):
resolution: {integrity: sha512-QlvfwaN9jaMga9EBazQ+5DDx/4sAdqDkcs05AsQHMaopluVCUyu1bTRUVKzXbgjDlrRAQrYVoi/sXJ9fmG+KLQ==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
peerDependencies:
@@ -6173,10 +6478,10 @@ packages:
'@types/semver': 7.3.12
'@typescript-eslint/scope-manager': 5.41.0
'@typescript-eslint/types': 5.41.0
- '@typescript-eslint/typescript-estree': 5.41.0(typescript@5.1.3)
- eslint: 8.42.0
+ '@typescript-eslint/typescript-estree': 5.41.0(typescript@5.2.2)
+ eslint: 8.48.0
eslint-scope: 5.1.1
- eslint-utils: 3.0.0(eslint@8.42.0)
+ eslint-utils: 3.0.0(eslint@8.48.0)
semver: 7.3.8
transitivePeerDependencies:
- supports-color
@@ -6383,20 +6688,20 @@ packages:
acorn: 7.4.1
dev: true
- /acorn-jsx@5.3.2(acorn@8.8.1):
+ /acorn-jsx@5.3.2(acorn@8.10.0):
resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==}
peerDependencies:
acorn: ^6.0.0 || ^7.0.0 || ^8.0.0
dependencies:
- acorn: 8.8.1
+ acorn: 8.10.0
dev: true
- /acorn-jsx@5.3.2(acorn@8.8.2):
+ /acorn-jsx@5.3.2(acorn@8.8.1):
resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==}
peerDependencies:
acorn: ^6.0.0 || ^7.0.0 || ^8.0.0
dependencies:
- acorn: 8.8.2
+ acorn: 8.8.1
dev: true
/acorn-walk@6.2.0:
@@ -6420,6 +6725,12 @@ packages:
engines: {node: '>=0.4.0'}
dev: true
+ /acorn@8.10.0:
+ resolution: {integrity: sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==}
+ engines: {node: '>=0.4.0'}
+ hasBin: true
+ dev: true
+
/acorn@8.8.1:
resolution: {integrity: sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==}
engines: {node: '>=0.4.0'}
@@ -6546,8 +6857,8 @@ packages:
engines: {node: '>=12'}
dev: true
- /ansi-sequence-parser@1.1.0:
- resolution: {integrity: sha512-lEm8mt52to2fT8GhciPCGeCXACSz2UwIN4X2e2LJSnZ5uAbn2/dsYdOmUXq0AtWS5cpAupysIneExOgH0Vd2TQ==}
+ /ansi-sequence-parser@1.1.1:
+ resolution: {integrity: sha512-vJXt3yiaUL4UU546s3rPXlsry/RnM730G1+HkpKE012AN0sx1eOrxSu95oKDIonskeLTijMgqWZ3uDEe3NFvyg==}
dev: true
/ansi-styles@1.0.0:
@@ -6779,8 +7090,8 @@ packages:
engines: {node: '>=8'}
dev: true
- /async-each@1.0.3:
- resolution: {integrity: sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==}
+ /async-each@1.0.6:
+ resolution: {integrity: sha512-c646jH1avxr+aVpndVMeAfYw7wAa6idufrlN3LPA4PmKS0QEGp6PIC9nwz0WQkkvBGAMEki3pFdtxaF39J9vvg==}
requiresBuild: true
dev: true
optional: true
@@ -6805,6 +7116,7 @@ packages:
/atob@2.1.2:
resolution: {integrity: sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==}
engines: {node: '>= 4.5.0'}
+ hasBin: true
dev: true
/autoprefixer@10.4.14(postcss@8.4.23):
@@ -7187,8 +7499,8 @@ packages:
tweetnacl: 0.14.5
dev: true
- /better-sqlite3@8.4.0:
- resolution: {integrity: sha512-NmsNW1CQvqMszu/CFAJ3pLct6NEFlNfuGM6vw72KHkjOD1UDnL96XNN1BMQc1hiHo8vE2GbOWQYIpZ+YM5wrZw==}
+ /better-sqlite3@8.6.0:
+ resolution: {integrity: sha512-jwAudeiTMTSyby+/SfbHDebShbmC2MCH8mU2+DXi0WJfv13ypEJm47cd3kljmy/H130CazEvkf2Li//ewcMJ1g==}
requiresBuild: true
dependencies:
bindings: 1.5.0
@@ -7368,7 +7680,7 @@ packages:
resolution: {integrity: sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==}
dependencies:
cipher-base: 1.0.4
- des.js: 1.0.1
+ des.js: 1.1.0
inherits: 2.0.4
safe-buffer: 5.2.1
dev: true
@@ -7390,7 +7702,7 @@ packages:
elliptic: 6.5.4
inherits: 2.0.4
parse-asn1: 5.1.6
- readable-stream: 3.6.0
+ readable-stream: 3.6.2
safe-buffer: 5.2.1
dev: true
@@ -7400,6 +7712,17 @@ packages:
pako: 1.0.11
dev: true
+ /browserslist@4.21.10:
+ resolution: {integrity: sha512-bipEBdZfVH5/pwrvqc+Ub0kUPVfGUhlKxbvfD+z1BDnPEO/X98ruXGA1WP5ASpAFKan7Qr6j736IacbZQuAlKQ==}
+ engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
+ hasBin: true
+ dependencies:
+ caniuse-lite: 1.0.30001527
+ electron-to-chromium: 1.4.508
+ node-releases: 2.0.13
+ update-browserslist-db: 1.0.11(browserslist@4.21.10)
+ dev: true
+
/browserslist@4.21.4:
resolution: {integrity: sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==}
engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
@@ -7529,7 +7852,7 @@ packages:
chownr: 1.1.4
figgy-pudding: 3.5.2
glob: 7.2.3
- graceful-fs: 4.2.10
+ graceful-fs: 4.2.11
infer-owner: 1.0.4
lru-cache: 5.1.1
mississippi: 3.0.0
@@ -7653,7 +7976,7 @@ packages:
resolution: {integrity: sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==}
dependencies:
pascal-case: 3.1.2
- tslib: 2.6.1
+ tslib: 2.6.2
dev: true
/camelcase-css@2.0.1:
@@ -7682,6 +8005,10 @@ packages:
/caniuse-lite@1.0.30001482:
resolution: {integrity: sha512-F1ZInsg53cegyjroxLNW9DmrEQ1SuGRTO1QlpA0o2/6OpQ0gFeDRoq1yFmnr8Sakn9qwwt9DmbxHB6w167OSuQ==}
+ /caniuse-lite@1.0.30001527:
+ resolution: {integrity: sha512-YkJi7RwPgWtXVSgK4lG9AHH57nSzvvOp9MesgXmw4Q7n0C3H04L0foHqfxcmSAm5AcWb8dW9AYj2tR7/5GnddQ==}
+ dev: true
+
/caseless@0.12.0:
resolution: {integrity: sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==}
dev: true
@@ -7776,7 +8103,7 @@ packages:
requiresBuild: true
dependencies:
anymatch: 2.0.0
- async-each: 1.0.3
+ async-each: 1.0.6
braces: 2.3.2
glob-parent: 3.1.0
inherits: 2.0.4
@@ -7805,7 +8132,7 @@ packages:
normalize-path: 3.0.0
readdirp: 3.6.0
optionalDependencies:
- fsevents: 2.3.2
+ fsevents: 2.3.3
/chownr@1.1.4:
resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==}
@@ -8134,7 +8461,7 @@ packages:
dependencies:
buffer-from: 1.1.2
inherits: 2.0.4
- readable-stream: 2.3.7
+ readable-stream: 2.3.8
typedarray: 0.0.6
dev: true
@@ -8667,8 +8994,8 @@ packages:
array-find-index: 1.0.2
dev: true
- /cyclist@1.0.1:
- resolution: {integrity: sha512-NJGVKPS81XejHcLhaLJS7plab0fK3slPh11mESeeDq2W4ZI5kUKK/LRRdVDvjJseojbPB7ZwjnyOybg3Igea/A==}
+ /cyclist@1.0.2:
+ resolution: {integrity: sha512-0sVXIohTfLqVIW3kb/0n6IiWF3Ifj5nm2XaSrLq2DI6fKIGa2fYAZdk917rUneaeLVpYfFcyXE2ft0fe3remsA==}
dev: true
/damerau-levenshtein@1.0.8:
@@ -8763,8 +9090,8 @@ packages:
engines: {node: '>=10'}
dev: true
- /decode-uri-component@0.2.0:
- resolution: {integrity: sha512-hjf+xovcEn31w/EUYdTXQh/8smFL/dzYjohQGEIgjyNavaJfBY2p5F527Bo1VPATxv0VYTUC2bOcXvqFwk78Og==}
+ /decode-uri-component@0.2.2:
+ resolution: {integrity: sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==}
engines: {node: '>=0.10'}
dev: true
@@ -8909,8 +9236,8 @@ packages:
engines: {node: '>= 0.6.0'}
dev: true
- /des.js@1.0.1:
- resolution: {integrity: sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA==}
+ /des.js@1.1.0:
+ resolution: {integrity: sha512-r17GxjhUCjSRy8aiJpr8/UadFIzMzJGexI3Nmz4ADi9LYSFx4gTBp80+NaX/YsXWWLhpZ7v/v/ubEc/bCNfKwg==}
dependencies:
inherits: 2.0.4
minimalistic-assert: 1.0.1
@@ -9054,7 +9381,7 @@ packages:
resolution: {integrity: sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==}
dependencies:
no-case: 3.0.4
- tslib: 2.6.1
+ tslib: 2.6.2
dev: true
/dot-prop@5.3.0:
@@ -9092,7 +9419,7 @@ packages:
dependencies:
end-of-stream: 1.4.4
inherits: 2.0.4
- readable-stream: 2.3.7
+ readable-stream: 2.3.8
stream-shift: 1.0.1
dev: true
@@ -9128,6 +9455,10 @@ packages:
/electron-to-chromium@1.4.284:
resolution: {integrity: sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA==}
+ /electron-to-chromium@1.4.508:
+ resolution: {integrity: sha512-FFa8QKjQK/A5QuFr2167myhMesGrhlOBD+3cYNxO9/S4XzHEXesyTD/1/xF644gC8buFPz3ca6G1LOQD0tZrrg==}
+ dev: true
+
/elliptic@6.5.4:
resolution: {integrity: sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==}
dependencies:
@@ -9188,7 +9519,7 @@ packages:
resolution: {integrity: sha512-Nv9m36S/vxpsI+Hc4/ZGRs0n9mXqSWGGq49zxb/cJfPAQMbUtttJAlNPS4AQzaBdw/pKskw5bMbekT/Y7W/Wlg==}
engines: {node: '>=6.9.0'}
dependencies:
- graceful-fs: 4.2.10
+ graceful-fs: 4.2.11
memory-fs: 0.5.0
tapable: 1.1.3
dev: true
@@ -9219,6 +9550,7 @@ packages:
/errno@0.1.8:
resolution: {integrity: sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==}
+ hasBin: true
dependencies:
prr: 1.0.1
dev: true
@@ -9412,14 +9744,14 @@ packages:
eslint: ^7.32.0 || ^8.2.0
eslint-plugin-import: ^2.25.3
dependencies:
- '@typescript-eslint/eslint-plugin': 5.41.0(@typescript-eslint/parser@5.41.0)(eslint@8.26.0)(typescript@5.1.3)
- '@typescript-eslint/parser': 5.41.0(eslint@8.26.0)(typescript@5.1.3)
+ '@typescript-eslint/eslint-plugin': 5.41.0(@typescript-eslint/parser@5.41.0)(eslint@8.26.0)(typescript@5.2.2)
+ '@typescript-eslint/parser': 5.41.0(eslint@8.26.0)(typescript@5.2.2)
eslint: 8.26.0
eslint-config-airbnb-base: 15.0.0(eslint-plugin-import@2.26.0)(eslint@8.26.0)
eslint-plugin-import: 2.26.0(@typescript-eslint/parser@5.41.0)(eslint@8.26.0)
dev: true
- /eslint-config-preact@1.3.0(@typescript-eslint/eslint-plugin@4.33.0)(eslint@7.32.0)(typescript@5.1.3):
+ /eslint-config-preact@1.3.0(@typescript-eslint/eslint-plugin@4.33.0)(eslint@7.32.0)(typescript@5.2.2):
resolution: {integrity: sha512-yHYXg5qNzEJd3D/30AmsIW0W8MuY858KpApXp7xxBF08IYUljSKCOqMx+dVucXHQnAm7+11wOnMkgVHIBAechw==}
peerDependencies:
eslint: 6.x || 7.x || 8.x
@@ -9431,7 +9763,7 @@ packages:
'@babel/plugin-syntax-jsx': 7.21.4(@babel/core@7.18.9)
eslint: 7.32.0
eslint-plugin-compat: 4.0.2(eslint@7.32.0)
- eslint-plugin-jest: 25.7.0(@typescript-eslint/eslint-plugin@4.33.0)(eslint@7.32.0)(typescript@5.1.3)
+ eslint-plugin-jest: 25.7.0(@typescript-eslint/eslint-plugin@4.33.0)(eslint@7.32.0)(typescript@5.2.2)
eslint-plugin-react: 7.31.10(eslint@7.32.0)
eslint-plugin-react-hooks: 4.6.0(eslint@7.32.0)
transitivePeerDependencies:
@@ -9441,21 +9773,21 @@ packages:
- typescript
dev: true
- /eslint-config-preact@1.3.0(@typescript-eslint/eslint-plugin@5.41.0)(eslint@8.42.0)(typescript@5.1.3):
+ /eslint-config-preact@1.3.0(@typescript-eslint/eslint-plugin@5.41.0)(eslint@8.48.0)(typescript@5.2.2):
resolution: {integrity: sha512-yHYXg5qNzEJd3D/30AmsIW0W8MuY858KpApXp7xxBF08IYUljSKCOqMx+dVucXHQnAm7+11wOnMkgVHIBAechw==}
peerDependencies:
eslint: 6.x || 7.x || 8.x
dependencies:
'@babel/core': 7.18.9
- '@babel/eslint-parser': 7.19.1(@babel/core@7.18.9)(eslint@8.42.0)
+ '@babel/eslint-parser': 7.19.1(@babel/core@7.18.9)(eslint@8.48.0)
'@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.18.9)
'@babel/plugin-syntax-decorators': 7.19.0(@babel/core@7.18.9)
'@babel/plugin-syntax-jsx': 7.21.4(@babel/core@7.18.9)
- eslint: 8.42.0
- eslint-plugin-compat: 4.0.2(eslint@8.42.0)
- eslint-plugin-jest: 25.7.0(@typescript-eslint/eslint-plugin@5.41.0)(eslint@8.42.0)(typescript@5.1.3)
- eslint-plugin-react: 7.31.10(eslint@8.42.0)
- eslint-plugin-react-hooks: 4.6.0(eslint@8.42.0)
+ eslint: 8.48.0
+ eslint-plugin-compat: 4.0.2(eslint@8.48.0)
+ eslint-plugin-jest: 25.7.0(@typescript-eslint/eslint-plugin@5.41.0)(eslint@8.48.0)(typescript@5.2.2)
+ eslint-plugin-react: 7.31.10(eslint@8.48.0)
+ eslint-plugin-react-hooks: 4.6.0(eslint@8.48.0)
transitivePeerDependencies:
- '@typescript-eslint/eslint-plugin'
- jest
@@ -9502,7 +9834,7 @@ packages:
eslint-import-resolver-webpack:
optional: true
dependencies:
- '@typescript-eslint/parser': 5.41.0(eslint@8.26.0)(typescript@5.1.3)
+ '@typescript-eslint/parser': 5.41.0(eslint@8.26.0)(typescript@5.2.2)
debug: 3.2.7
eslint: 8.26.0
eslint-import-resolver-node: 0.3.6
@@ -9527,7 +9859,7 @@ packages:
semver: 7.3.5
dev: true
- /eslint-plugin-compat@4.0.2(eslint@8.42.0):
+ /eslint-plugin-compat@4.0.2(eslint@8.48.0):
resolution: {integrity: sha512-xqvoO54CLTVaEYGMzhu35Wzwk/As7rCvz/2dqwnFiWi0OJccEtGIn+5qq3zqIu9nboXlpdBN579fZcItC73Ycg==}
engines: {node: '>=9.x'}
peerDependencies:
@@ -9538,7 +9870,7 @@ packages:
browserslist: 4.21.5
caniuse-lite: 1.0.30001482
core-js: 3.26.0
- eslint: 8.42.0
+ eslint: 8.48.0
find-up: 5.0.0
lodash.memoize: 4.1.2
semver: 7.3.5
@@ -9562,7 +9894,7 @@ packages:
'@typescript-eslint/parser':
optional: true
dependencies:
- '@typescript-eslint/parser': 5.41.0(eslint@8.26.0)(typescript@5.1.3)
+ '@typescript-eslint/parser': 5.41.0(eslint@8.26.0)(typescript@5.2.2)
array-includes: 3.1.5
array.prototype.flat: 1.3.0
debug: 2.6.9
@@ -9583,7 +9915,7 @@ packages:
- supports-color
dev: true
- /eslint-plugin-jest@25.7.0(@typescript-eslint/eslint-plugin@4.33.0)(eslint@7.32.0)(typescript@5.1.3):
+ /eslint-plugin-jest@25.7.0(@typescript-eslint/eslint-plugin@4.33.0)(eslint@7.32.0)(typescript@5.2.2):
resolution: {integrity: sha512-PWLUEXeeF7C9QGKqvdSbzLOiLTx+bno7/HC9eefePfEb257QFHg7ye3dh80AZVkaa/RQsBB1Q/ORQvg2X7F0NQ==}
engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0}
peerDependencies:
@@ -9596,15 +9928,15 @@ packages:
jest:
optional: true
dependencies:
- '@typescript-eslint/eslint-plugin': 4.33.0(@typescript-eslint/parser@4.33.0)(eslint@7.32.0)(typescript@5.1.3)
- '@typescript-eslint/experimental-utils': 5.41.0(eslint@7.32.0)(typescript@5.1.3)
+ '@typescript-eslint/eslint-plugin': 4.33.0(@typescript-eslint/parser@4.33.0)(eslint@7.32.0)(typescript@5.2.2)
+ '@typescript-eslint/experimental-utils': 5.41.0(eslint@7.32.0)(typescript@5.2.2)
eslint: 7.32.0
transitivePeerDependencies:
- supports-color
- typescript
dev: true
- /eslint-plugin-jest@25.7.0(@typescript-eslint/eslint-plugin@5.41.0)(eslint@8.42.0)(typescript@5.1.3):
+ /eslint-plugin-jest@25.7.0(@typescript-eslint/eslint-plugin@5.41.0)(eslint@8.48.0)(typescript@5.2.2):
resolution: {integrity: sha512-PWLUEXeeF7C9QGKqvdSbzLOiLTx+bno7/HC9eefePfEb257QFHg7ye3dh80AZVkaa/RQsBB1Q/ORQvg2X7F0NQ==}
engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0}
peerDependencies:
@@ -9617,9 +9949,9 @@ packages:
jest:
optional: true
dependencies:
- '@typescript-eslint/eslint-plugin': 5.41.0(@typescript-eslint/parser@5.41.0)(eslint@8.42.0)(typescript@5.1.3)
- '@typescript-eslint/experimental-utils': 5.41.0(eslint@8.42.0)(typescript@5.1.3)
- eslint: 8.42.0
+ '@typescript-eslint/eslint-plugin': 5.41.0(@typescript-eslint/parser@5.41.0)(eslint@8.48.0)(typescript@5.2.2)
+ '@typescript-eslint/experimental-utils': 5.41.0(eslint@8.48.0)(typescript@5.2.2)
+ eslint: 8.48.0
transitivePeerDependencies:
- supports-color
- typescript
@@ -9665,13 +9997,13 @@ packages:
eslint: 8.26.0
dev: true
- /eslint-plugin-react-hooks@4.6.0(eslint@8.42.0):
+ /eslint-plugin-react-hooks@4.6.0(eslint@8.48.0):
resolution: {integrity: sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==}
engines: {node: '>=10'}
peerDependencies:
eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0
dependencies:
- eslint: 8.42.0
+ eslint: 8.48.0
dev: true
/eslint-plugin-react@7.31.10(eslint@7.32.0):
@@ -9720,7 +10052,7 @@ packages:
string.prototype.matchall: 4.0.7
dev: true
- /eslint-plugin-react@7.31.10(eslint@8.42.0):
+ /eslint-plugin-react@7.31.10(eslint@8.48.0):
resolution: {integrity: sha512-e4N/nc6AAlg4UKW/mXeYWd3R++qUano5/o+t+wnWxIf+bLsOaH3a4q74kX3nDjYym3VBN4HyO9nEn1GcAqgQOA==}
engines: {node: '>=4'}
peerDependencies:
@@ -9729,7 +10061,7 @@ packages:
array-includes: 3.1.5
array.prototype.flatmap: 1.3.0
doctrine: 2.1.0
- eslint: 8.42.0
+ eslint: 8.48.0
estraverse: 5.3.0
jsx-ast-utils: 3.3.3
minimatch: 3.1.2
@@ -9767,8 +10099,8 @@ packages:
estraverse: 5.3.0
dev: true
- /eslint-scope@7.2.0:
- resolution: {integrity: sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw==}
+ /eslint-scope@7.2.2:
+ resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
dependencies:
esrecurse: 4.3.0
@@ -9812,13 +10144,13 @@ packages:
eslint-visitor-keys: 2.1.0
dev: true
- /eslint-utils@3.0.0(eslint@8.42.0):
+ /eslint-utils@3.0.0(eslint@8.48.0):
resolution: {integrity: sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==}
engines: {node: ^10.0.0 || ^12.0.0 || >= 14.0.0}
peerDependencies:
eslint: '>=5'
dependencies:
- eslint: 8.42.0
+ eslint: 8.48.0
eslint-visitor-keys: 2.1.0
dev: true
@@ -9837,8 +10169,8 @@ packages:
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
dev: true
- /eslint-visitor-keys@3.4.1:
- resolution: {integrity: sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA==}
+ /eslint-visitor-keys@3.4.3:
+ resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
dev: true
@@ -9984,16 +10316,16 @@ packages:
- supports-color
dev: true
- /eslint@8.42.0:
- resolution: {integrity: sha512-ulg9Ms6E1WPf67PHaEY4/6E2tEn5/f7FXGzr3t9cBMugOmf1INYvuUwwh1aXQN4MfJ6a5K2iNwP3w4AColvI9A==}
+ /eslint@8.48.0:
+ resolution: {integrity: sha512-sb6DLeIuRXxeM1YljSe1KEx9/YYeZFQWcV8Rq9HfigmdDEugjLEVEa1ozDjL6YDjBpQHPJxJzze+alxi4T3OLg==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
hasBin: true
dependencies:
- '@eslint-community/eslint-utils': 4.4.0(eslint@8.42.0)
- '@eslint-community/regexpp': 4.5.1
- '@eslint/eslintrc': 2.0.3
- '@eslint/js': 8.42.0
- '@humanwhocodes/config-array': 0.11.10
+ '@eslint-community/eslint-utils': 4.4.0(eslint@8.48.0)
+ '@eslint-community/regexpp': 4.8.0
+ '@eslint/eslintrc': 2.1.2
+ '@eslint/js': 8.48.0
+ '@humanwhocodes/config-array': 0.11.11
'@humanwhocodes/module-importer': 1.0.1
'@nodelib/fs.walk': 1.2.8
ajv: 6.12.6
@@ -10002,19 +10334,18 @@ packages:
debug: 4.3.4
doctrine: 3.0.0
escape-string-regexp: 4.0.0
- eslint-scope: 7.2.0
- eslint-visitor-keys: 3.4.1
- espree: 9.5.2
+ eslint-scope: 7.2.2
+ eslint-visitor-keys: 3.4.3
+ espree: 9.6.1
esquery: 1.5.0
esutils: 2.0.3
fast-deep-equal: 3.1.3
file-entry-cache: 6.0.1
find-up: 5.0.0
glob-parent: 6.0.2
- globals: 13.20.0
+ globals: 13.21.0
graphemer: 1.4.0
ignore: 5.2.4
- import-fresh: 3.3.0
imurmurhash: 0.1.4
is-glob: 4.0.3
is-path-inside: 3.0.3
@@ -10024,9 +10355,8 @@ packages:
lodash.merge: 4.6.2
minimatch: 3.1.2
natural-compare: 1.4.0
- optionator: 0.9.1
+ optionator: 0.9.3
strip-ansi: 6.0.1
- strip-json-comments: 3.1.1
text-table: 0.2.0
transitivePeerDependencies:
- supports-color
@@ -10055,13 +10385,13 @@ packages:
eslint-visitor-keys: 3.3.0
dev: true
- /espree@9.5.2:
- resolution: {integrity: sha512-7OASN1Wma5fum5SrNhFMAMJxOUAbhyfQ8dQ//PJaJbNw0URTPWqIghHWt1MmAANKhHZIYOHruW4Kw4ruUWOdGw==}
+ /espree@9.6.1:
+ resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
dependencies:
- acorn: 8.8.2
- acorn-jsx: 5.3.2(acorn@8.8.2)
- eslint-visitor-keys: 3.4.1
+ acorn: 8.10.0
+ acorn-jsx: 5.3.2(acorn@8.10.0)
+ eslint-visitor-keys: 3.4.3
dev: true
/esprima@4.0.1:
@@ -10492,7 +10822,7 @@ packages:
resolution: {integrity: sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w==}
dependencies:
inherits: 2.0.4
- readable-stream: 2.3.7
+ readable-stream: 2.3.8
dev: true
/follow-redirects@1.15.2:
@@ -10607,7 +10937,7 @@ packages:
resolution: {integrity: sha512-OMcX/4IC/uqEPVgGeyfN22LJk6AZrMkRZHxcHBMBvHScDGgwTm2GT2Wkgtocyd3JfZffjj2kYUDXXII0Fk9W0g==}
dependencies:
inherits: 2.0.4
- readable-stream: 2.3.7
+ readable-stream: 2.3.8
dev: true
/fromentries@1.3.2:
@@ -10665,10 +10995,10 @@ packages:
/fs-write-stream-atomic@1.0.10:
resolution: {integrity: sha512-gehEzmPn2nAwr39eay+x3X34Ra+M2QlVUTLhkXPjWdeO8RF9kszk116avgBJM3ZyNHgHXBNx+VmPaFC36k0PzA==}
dependencies:
- graceful-fs: 4.2.10
+ graceful-fs: 4.2.11
iferr: 0.1.5
imurmurhash: 0.1.4
- readable-stream: 2.3.7
+ readable-stream: 2.3.8
dev: true
/fs.realpath@1.0.0:
@@ -10686,8 +11016,8 @@ packages:
dev: true
optional: true
- /fsevents@2.3.2:
- resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==}
+ /fsevents@2.3.3:
+ resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
os: [darwin]
requiresBuild: true
@@ -10921,8 +11251,8 @@ packages:
type-fest: 0.20.2
dev: true
- /globals@13.20.0:
- resolution: {integrity: sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==}
+ /globals@13.21.0:
+ resolution: {integrity: sha512-ybyme3s4yy/t/3s35bewwXKOf7cvzfreG2lH0lZl0JB7I4GxRP2ghxOK/Nb9EkRXdbBXZLfq/p/0W2JUONB/Gg==}
engines: {node: '>=8'}
dependencies:
type-fest: 0.20.2
@@ -10985,6 +11315,10 @@ packages:
resolution: {integrity: sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==}
dev: true
+ /graceful-fs@4.2.11:
+ resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
+ dev: true
+
/grapheme-splitter@1.0.4:
resolution: {integrity: sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==}
dev: true
@@ -11105,7 +11439,7 @@ packages:
engines: {node: '>=4'}
dependencies:
inherits: 2.0.4
- readable-stream: 3.6.0
+ readable-stream: 3.6.2
safe-buffer: 5.2.1
dev: true
@@ -11201,7 +11535,7 @@ packages:
he: 1.2.0
param-case: 3.0.4
relateurl: 0.2.7
- terser: 5.17.7
+ terser: 5.19.4
dev: true
/html-minifier@3.5.21:
@@ -11253,8 +11587,8 @@ packages:
webpack: 4.46.0
dev: true
- /html-webpack-plugin@5.5.1(webpack@4.46.0):
- resolution: {integrity: sha512-cTUzZ1+NqjGEKjmVgZKLMdiFg3m9MdRXkZW2OEe69WYVi5ONLMmlnSZdXzGGMOq0C8jGDrL6EWyEDDUioHO/pA==}
+ /html-webpack-plugin@5.5.3(webpack@4.46.0):
+ resolution: {integrity: sha512-6YrDKTuqaP/TquFH7h4srYWsZx+x6k6+FbsTm0ziCwGHDP78Unr1r9F/H4+sGmMbX08GQcJ+K64x55b+7VM/jg==}
engines: {node: '>=10.13.0'}
peerDependencies:
webpack: ^5.20.0
@@ -11267,13 +11601,13 @@ packages:
webpack: 4.46.0
dev: true
- /html-webpack-skip-assets-plugin@1.0.3(html-webpack-plugin@5.5.1)(webpack@4.46.0):
+ /html-webpack-skip-assets-plugin@1.0.3(html-webpack-plugin@5.5.3)(webpack@4.46.0):
resolution: {integrity: sha512-vpdh+JZGlE1Df3IftH2gw5P7b6yfTsahcOIJnkkkj5iJU9dUStXgzgALoXWwl8+17wWgFm3edcJzeYTJBYfMAw==}
peerDependencies:
html-webpack-plugin: '>=3.0.0'
webpack: '>=3.0.0'
dependencies:
- html-webpack-plugin: 5.5.1(webpack@4.46.0)
+ html-webpack-plugin: 5.5.3(webpack@4.46.0)
minimatch: 3.0.4
webpack: 4.46.0
dev: true
@@ -12137,6 +12471,13 @@ packages:
minimist: 1.2.7
dev: true
+ /json5@1.0.2:
+ resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==}
+ hasBin: true
+ dependencies:
+ minimist: 1.2.8
+ dev: true
+
/json5@2.2.1:
resolution: {integrity: sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==}
engines: {node: '>=6'}
@@ -12319,6 +12660,15 @@ packages:
json5: 1.0.1
dev: true
+ /loader-utils@1.4.2:
+ resolution: {integrity: sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==}
+ engines: {node: '>=4.0.0'}
+ dependencies:
+ big.js: 5.2.2
+ emojis-list: 3.0.0
+ json5: 1.0.2
+ dev: true
+
/loader-utils@2.0.3:
resolution: {integrity: sha512-THWqIsn8QRnvLl0shHYVBN9syumU8pYWEHPTmkiVGd+7K5eFNVSY6AJhRvgGF70gg1Dz+l/k8WicvFCxdEs60A==}
engines: {node: '>=8.9.0'}
@@ -12431,7 +12781,7 @@ packages:
/lower-case@2.0.2:
resolution: {integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==}
dependencies:
- tslib: 2.6.1
+ tslib: 2.6.2
dev: true
/lowercase-keys@1.0.1:
@@ -12483,7 +12833,7 @@ packages:
engines: {node: '>=6'}
dependencies:
pify: 4.0.1
- semver: 5.7.1
+ semver: 5.7.2
dev: true
/make-dir@3.1.0:
@@ -12576,7 +12926,7 @@ packages:
resolution: {integrity: sha512-cda4JKCxReDXFXRqOHPQscuIYg1PvxbE2S2GP45rnwfEK+vZaXC8C1OFvdHIbgw0DLzowXGVoxLaAmlgRy14GQ==}
dependencies:
errno: 0.1.8
- readable-stream: 2.3.7
+ readable-stream: 2.3.8
dev: true
/memory-fs@0.5.0:
@@ -12584,7 +12934,7 @@ packages:
engines: {node: '>=4.3.0 <5.0.0 || >=5.10'}
dependencies:
errno: 0.1.8
- readable-stream: 2.3.7
+ readable-stream: 2.3.8
dev: true
/merge-descriptors@1.0.1:
@@ -12638,6 +12988,7 @@ packages:
/miller-rabin@4.0.1:
resolution: {integrity: sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==}
+ hasBin: true
dependencies:
bn.js: 4.12.0
brorand: 1.1.0
@@ -12744,9 +13095,20 @@ packages:
brace-expansion: 2.0.1
dev: true
+ /minimatch@9.0.3:
+ resolution: {integrity: sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==}
+ engines: {node: '>=16 || 14 >=14.17'}
+ dependencies:
+ brace-expansion: 2.0.1
+ dev: true
+
/minimist@1.2.7:
resolution: {integrity: sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==}
+ /minimist@1.2.8:
+ resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==}
+ dev: true
+
/minipass-collect@1.0.2:
resolution: {integrity: sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==}
engines: {node: '>= 8'}
@@ -12833,8 +13195,9 @@ packages:
/mkdirp@0.5.6:
resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==}
+ hasBin: true
dependencies:
- minimist: 1.2.7
+ minimist: 1.2.8
dev: true
/mkdirp@1.0.4:
@@ -12995,7 +13358,7 @@ packages:
resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==}
dependencies:
lower-case: 2.0.2
- tslib: 2.6.1
+ tslib: 2.6.2
dev: true
/node-abi@3.45.0:
@@ -13049,13 +13412,13 @@ packages:
process: 0.11.10
punycode: 1.4.1
querystring-es3: 0.2.1
- readable-stream: 2.3.7
+ readable-stream: 2.3.8
stream-browserify: 2.0.2
stream-http: 2.8.3
string_decoder: 1.3.0
timers-browserify: 2.0.12
tty-browserify: 0.0.0
- url: 0.11.0
+ url: 0.11.1
util: 0.11.1
vm-browserify: 1.1.2
dev: true
@@ -13070,6 +13433,10 @@ packages:
/node-releases@2.0.10:
resolution: {integrity: sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==}
+ /node-releases@2.0.13:
+ resolution: {integrity: sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==}
+ dev: true
+
/nofilter@3.1.0:
resolution: {integrity: sha512-l2NNj07e9afPnhAhvgVrCD/oy2Ai1yfLpuo3EpiO1jFTsB4sFz6oIfAfSZyQzVpkZQ9xS8ZS5g1jCBgq4Hwo0g==}
engines: {node: '>=12.19'}
@@ -13420,6 +13787,18 @@ packages:
word-wrap: 1.2.3
dev: true
+ /optionator@0.9.3:
+ resolution: {integrity: sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==}
+ engines: {node: '>= 0.8.0'}
+ dependencies:
+ '@aashutoshrathi/word-wrap': 1.2.6
+ deep-is: 0.1.4
+ fast-levenshtein: 2.0.6
+ levn: 0.4.1
+ prelude-ls: 1.2.1
+ type-check: 0.4.0
+ dev: true
+
/ora@5.4.1:
resolution: {integrity: sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==}
engines: {node: '>=10'}
@@ -13571,9 +13950,9 @@ packages:
/parallel-transform@1.2.0:
resolution: {integrity: sha512-P2vSmIu38uIlvdcU7fDkyrxj33gTUy/ABO5ZUbGowxNCopBq/OoD42bP4UmMrJoPyk4Uqf0mu3mtWBhHCZD8yg==}
dependencies:
- cyclist: 1.0.1
+ cyclist: 1.0.2
inherits: 2.0.4
- readable-stream: 2.3.7
+ readable-stream: 2.3.8
dev: true
/param-case@2.1.1:
@@ -13586,7 +13965,7 @@ packages:
resolution: {integrity: sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==}
dependencies:
dot-case: 3.0.4
- tslib: 2.6.1
+ tslib: 2.6.2
dev: true
/parent-module@1.0.1:
@@ -13651,7 +14030,7 @@ packages:
resolution: {integrity: sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==}
dependencies:
no-case: 3.0.4
- tslib: 2.6.1
+ tslib: 2.6.2
dev: true
/pascalcase@0.1.1:
@@ -14895,10 +15274,6 @@ packages:
pump: 2.0.1
dev: true
- /punycode@1.3.2:
- resolution: {integrity: sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw==}
- dev: true
-
/punycode@1.4.1:
resolution: {integrity: sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==}
dev: true
@@ -14931,6 +15306,13 @@ packages:
side-channel: 1.0.4
dev: true
+ /qs@6.11.2:
+ resolution: {integrity: sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA==}
+ engines: {node: '>=0.6'}
+ dependencies:
+ side-channel: 1.0.4
+ dev: true
+
/qs@6.5.3:
resolution: {integrity: sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==}
engines: {node: '>=0.6'}
@@ -14941,11 +15323,6 @@ packages:
engines: {node: '>=0.4.x'}
dev: true
- /querystring@0.2.0:
- resolution: {integrity: sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g==}
- engines: {node: '>=0.4.x'}
- dev: true
-
/querystring@0.2.1:
resolution: {integrity: sha512-wkvS7mL/JMugcup3/rMitHmd9ecIGd2lhFhK9N3UUQ450h66d1r3Y9nvXzQAW1Lq+wyx61k/1pfKS5KuKiyEbg==}
engines: {node: '>=0.4.x'}
@@ -15050,6 +15427,18 @@ packages:
util-deprecate: 1.0.2
dev: true
+ /readable-stream@2.3.8:
+ resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==}
+ dependencies:
+ core-util-is: 1.0.3
+ inherits: 2.0.4
+ isarray: 1.0.0
+ process-nextick-args: 2.0.1
+ safe-buffer: 5.1.2
+ string_decoder: 1.1.1
+ util-deprecate: 1.0.2
+ dev: true
+
/readable-stream@3.6.0:
resolution: {integrity: sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==}
engines: {node: '>= 6'}
@@ -15058,14 +15447,23 @@ packages:
string_decoder: 1.3.0
util-deprecate: 1.0.2
+ /readable-stream@3.6.2:
+ resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==}
+ engines: {node: '>= 6'}
+ dependencies:
+ inherits: 2.0.4
+ string_decoder: 1.3.0
+ util-deprecate: 1.0.2
+ dev: true
+
/readdirp@2.2.1:
resolution: {integrity: sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==}
engines: {node: '>=0.10'}
requiresBuild: true
dependencies:
- graceful-fs: 4.2.10
+ graceful-fs: 4.2.11
micromatch: 3.1.10
- readable-stream: 2.3.7
+ readable-stream: 2.3.8
transitivePeerDependencies:
- supports-color
dev: true
@@ -15315,6 +15713,7 @@ packages:
/resolve-url@0.2.1:
resolution: {integrity: sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg==}
+ deprecated: https://github.com/lydell/resolve-url#deprecated
dev: true
/resolve@1.22.1:
@@ -15420,7 +15819,7 @@ packages:
resolution: {integrity: sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw==}
engines: {node: '>=10.0.0'}
optionalDependencies:
- fsevents: 2.3.2
+ fsevents: 2.3.3
dev: true
/run-parallel@1.2.0:
@@ -15565,9 +15964,19 @@ packages:
hasBin: true
dev: true
+ /semver@5.7.2:
+ resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==}
+ hasBin: true
+ dev: true
+
/semver@6.3.0:
resolution: {integrity: sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==}
+ /semver@6.3.1:
+ resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==}
+ hasBin: true
+ dev: true
+
/semver@7.3.4:
resolution: {integrity: sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==}
engines: {node: '>=10'}
@@ -15689,6 +16098,7 @@ packages:
/sha.js@2.4.11:
resolution: {integrity: sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==}
+ hasBin: true
dependencies:
inherits: 2.0.4
safe-buffer: 5.2.1
@@ -15725,10 +16135,10 @@ packages:
engines: {node: '>=8'}
dev: true
- /shiki@0.14.2:
- resolution: {integrity: sha512-ltSZlSLOuSY0M0Y75KA+ieRaZ0Trf5Wl3gutE7jzLuIcWxLp5i/uEnLoQWNvgKXQ5OMpGkJnVMRLAuzjc0LJ2A==}
+ /shiki@0.14.4:
+ resolution: {integrity: sha512-IXCRip2IQzKwxArNNq1S+On4KPML3Yyn8Zzs/xRgcgOWIr8ntIK3IKzjFPfjy/7kt9ZMjc+FItfqHRBg8b6tNQ==}
dependencies:
- ansi-sequence-parser: 1.1.0
+ ansi-sequence-parser: 1.1.1
jsonc-parser: 3.2.0
vscode-oniguruma: 1.7.0
vscode-textmate: 8.0.0
@@ -15905,9 +16315,10 @@ packages:
/source-map-resolve@0.5.3:
resolution: {integrity: sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==}
+ deprecated: See https://github.com/lydell/source-map-resolve#deprecated
dependencies:
atob: 2.1.2
- decode-uri-component: 0.2.0
+ decode-uri-component: 0.2.2
resolve-url: 0.2.1
source-map-url: 0.4.1
urix: 0.1.0
@@ -16076,7 +16487,7 @@ packages:
resolution: {integrity: sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg==}
dependencies:
inherits: 2.0.4
- readable-stream: 2.3.7
+ readable-stream: 2.3.8
dev: true
/stream-each@1.2.3:
@@ -16091,7 +16502,7 @@ packages:
dependencies:
builtin-status-codes: 3.0.0
inherits: 2.0.4
- readable-stream: 2.3.7
+ readable-stream: 2.3.8
to-arraybuffer: 1.0.1
xtend: 4.0.2
dev: true
@@ -16350,22 +16761,24 @@ packages:
stable: 0.1.8
dev: true
- /swr@1.3.0(react@18.2.0):
- resolution: {integrity: sha512-dkghQrOl2ORX9HYrMDtPa7LTVHJjCTeZoB1dqTbnnEDlSvN8JEKpYIYurDfvbQFUUS8Cg8PceFVZNkW0KNNYPw==}
+ /swr@2.0.3(react@18.2.0):
+ resolution: {integrity: sha512-sGvQDok/AHEWTPfhUWXEHBVEXmgGnuahyhmRQbjl9XBYxT/MSlAzvXEKQpyM++bMPaI52vcWS2HiKNaW7+9OFw==}
+ engines: {pnpm: '7'}
peerDependencies:
react: ^16.11.0 || ^17.0.0 || ^18.0.0
dependencies:
react: 18.2.0
- dev: false
+ use-sync-external-store: 1.2.0(react@18.2.0)
- /swr@2.0.3(react@18.2.0):
- resolution: {integrity: sha512-sGvQDok/AHEWTPfhUWXEHBVEXmgGnuahyhmRQbjl9XBYxT/MSlAzvXEKQpyM++bMPaI52vcWS2HiKNaW7+9OFw==}
- engines: {pnpm: '7'}
+ /swr@2.2.2(react@18.2.0):
+ resolution: {integrity: sha512-CbR41AoMD4TQBQw9ic3GTXspgfM9Y8Mdhb5Ob4uIKXhWqnRLItwA5fpGvB7SmSw3+zEjb0PdhiEumtUvYoQ+bQ==}
peerDependencies:
react: ^16.11.0 || ^17.0.0 || ^18.0.0
dependencies:
+ client-only: 0.0.1
react: 18.2.0
use-sync-external-store: 1.2.0(react@18.2.0)
+ dev: false
/symbol-tree@3.2.4:
resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==}
@@ -16529,8 +16942,9 @@ packages:
/terser@4.8.1:
resolution: {integrity: sha512-4GnLC0x667eJG0ewJTa6z/yXrbLGv80D9Ru6HIpCQmO+Q4PfEtBFi0ObSckqwL6VyQv/7ENJieXHo2ANmdQwgw==}
engines: {node: '>=6.0.0'}
+ hasBin: true
dependencies:
- acorn: 8.8.2
+ acorn: 8.10.0
commander: 2.20.3
source-map: 0.6.1
source-map-support: 0.5.21
@@ -16546,13 +16960,13 @@ packages:
source-map-support: 0.5.21
dev: true
- /terser@5.17.7:
- resolution: {integrity: sha512-/bi0Zm2C6VAexlGgLlVxA0P2lru/sdLyfCVaRMfKVo9nWxbmz7f/sD8VPybPeSUJaJcwmCJis9pBIhcVcG1QcQ==}
+ /terser@5.19.4:
+ resolution: {integrity: sha512-6p1DjHeuluwxDXcuT9VR8p64klWJKo1ILiy19s6C9+0Bh2+NWTX6nD9EPppiER4ICkHDVB1RkVpin/YW2nQn/g==}
engines: {node: '>=10'}
hasBin: true
dependencies:
- '@jridgewell/source-map': 0.3.3
- acorn: 8.8.2
+ '@jridgewell/source-map': 0.3.5
+ acorn: 8.10.0
commander: 2.20.3
source-map-support: 0.5.21
dev: true
@@ -16588,7 +17002,7 @@ packages:
/through2@2.0.5:
resolution: {integrity: sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==}
dependencies:
- readable-stream: 2.3.7
+ readable-stream: 2.3.8
xtend: 4.0.2
dev: true
@@ -16726,7 +17140,7 @@ packages:
tslib: 2.6.1
dev: true
- /ts-node@10.9.1(@types/node@20.4.1)(typescript@5.1.3):
+ /ts-node@10.9.1(@types/node@20.5.9)(typescript@5.2.2):
resolution: {integrity: sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==}
hasBin: true
peerDependencies:
@@ -16745,14 +17159,14 @@ packages:
'@tsconfig/node12': 1.0.11
'@tsconfig/node14': 1.0.3
'@tsconfig/node16': 1.0.3
- '@types/node': 20.4.1
+ '@types/node': 20.5.9
acorn: 8.8.1
acorn-walk: 8.2.0
arg: 4.1.3
create-require: 1.1.1
diff: 4.0.2
make-error: 1.3.6
- typescript: 5.1.3
+ typescript: 5.2.2
v8-compile-cache-lib: 3.0.1
yn: 3.1.1
dev: true
@@ -16797,14 +17211,18 @@ packages:
resolution: {integrity: sha512-t0hLfiEKfMUoqhG+U1oid7Pva4bbDPHYfJNiB7BiIjRkj1pyC++4N3huJfqY6aRH6VTB0rvtzQwjM4K6qpfOig==}
dev: true
- /tsutils@3.21.0(typescript@5.1.3):
+ /tslib@2.6.2:
+ resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==}
+ dev: true
+
+ /tsutils@3.21.0(typescript@5.2.2):
resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==}
engines: {node: '>= 6'}
peerDependencies:
typescript: '>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta'
dependencies:
tslib: 1.14.1
- typescript: 5.1.3
+ typescript: 5.2.2
dev: true
/tty-browserify@0.0.0:
@@ -16877,18 +17295,18 @@ packages:
resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==}
dev: true
- /typedoc@0.24.8(typescript@5.1.3):
- resolution: {integrity: sha512-ahJ6Cpcvxwaxfu4KtjA8qZNqS43wYt6JL27wYiIgl1vd38WW/KWX11YuAeZhuz9v+ttrutSsgK+XO1CjL1kA3w==}
- engines: {node: '>= 14.14'}
+ /typedoc@0.25.1(typescript@5.2.2):
+ resolution: {integrity: sha512-c2ye3YUtGIadxN2O6YwPEXgrZcvhlZ6HlhWZ8jQRNzwLPn2ylhdGqdR8HbyDRyALP8J6lmSANILCkkIdNPFxqA==}
+ engines: {node: '>= 16'}
hasBin: true
peerDependencies:
- typescript: 4.6.x || 4.7.x || 4.8.x || 4.9.x || 5.0.x || 5.1.x
+ typescript: 4.6.x || 4.7.x || 4.8.x || 4.9.x || 5.0.x || 5.1.x || 5.2.x
dependencies:
lunr: 2.3.9
marked: 4.3.0
- minimatch: 9.0.1
- shiki: 0.14.2
- typescript: 5.1.3
+ minimatch: 9.0.3
+ shiki: 0.14.4
+ typescript: 5.2.2
dev: true
/typescript@4.6.4:
@@ -16897,13 +17315,8 @@ packages:
hasBin: true
dev: true
- /typescript@5.1.3:
- resolution: {integrity: sha512-XH627E9vkeqhlZFQuL+UsyAXEnibT0kWR2FWONlr4sTjvxyJYnyefgrkyECLzM5NenmKzRAy2rR/OlYLA1HkZw==}
- engines: {node: '>=14.17'}
- dev: true
-
- /typescript@5.1.6:
- resolution: {integrity: sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==}
+ /typescript@5.2.2:
+ resolution: {integrity: sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==}
engines: {node: '>=14.17'}
hasBin: true
dev: true
@@ -17023,6 +17436,7 @@ packages:
/upath@1.2.0:
resolution: {integrity: sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==}
engines: {node: '>=4'}
+ requiresBuild: true
dev: true
/update-browserslist-db@1.0.10(browserslist@4.21.4):
@@ -17046,6 +17460,17 @@ packages:
escalade: 3.1.1
picocolors: 1.0.0
+ /update-browserslist-db@1.0.11(browserslist@4.21.10):
+ resolution: {integrity: sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA==}
+ hasBin: true
+ peerDependencies:
+ browserslist: '>= 4.21.0'
+ dependencies:
+ browserslist: 4.21.10
+ escalade: 3.1.1
+ picocolors: 1.0.0
+ dev: true
+
/update-notifier@5.1.0:
resolution: {integrity: sha512-ItnICHbeMh9GqUy31hFPrD1kcuZ3rpxDZbf4KUDavXwS0bW5m7SLbDQpGX3UYr072cbrF5hFUs3r5tUsPwjfHw==}
engines: {node: '>=10'}
@@ -17078,6 +17503,7 @@ packages:
/urix@0.1.0:
resolution: {integrity: sha512-Am1ousAhSLBeB9cG/7k7r2R0zj50uDRlZHPGbazid5s9rlF1F/QKYObEKSIunSjIOkJZqwRRLpvewjEkM7pSqg==}
+ deprecated: Please see https://github.com/lydell/urix#deprecated
dev: true
/url-loader@4.1.1(file-loader@6.2.0)(webpack@4.46.0):
@@ -17104,11 +17530,11 @@ packages:
prepend-http: 2.0.0
dev: true
- /url@0.11.0:
- resolution: {integrity: sha512-kbailJa29QrtXnxgq+DdCEGlbTeYM2eJUxsz6vjZavrCYPMIFHMKQmSKYAIuUK2i7hgPm28a8piX5NTUtM/LKQ==}
+ /url@0.11.1:
+ resolution: {integrity: sha512-rWS3H04/+mzzJkv0eZ7vEDGiQbgquI1fGfOad6zKvgYQi1SzMmhl7c/DdRGxhaWrVH6z0qWITo8rpnxK/RfEhA==}
dependencies:
- punycode: 1.3.2
- querystring: 0.2.0
+ punycode: 1.4.1
+ qs: 6.11.2
dev: true
/use-sync-external-store@1.2.0(react@18.2.0):
@@ -17258,7 +17684,7 @@ packages:
/watchpack@1.7.5:
resolution: {integrity: sha512-9P3MWk6SrKjHsGkLT2KHXdQ/9SNkyoJbabxnKOoJepsvJjJG8uYTR3yTPxPQvNDI3w4Nz1xnE0TLHK4RIVe/MQ==}
dependencies:
- graceful-fs: 4.2.10
+ graceful-fs: 4.2.11
neo-async: 2.6.2
optionalDependencies:
chokidar: 3.5.3
@@ -17443,7 +17869,7 @@ packages:
eslint-scope: 4.0.3
json-parse-better-errors: 1.0.2
loader-runner: 2.4.0
- loader-utils: 1.4.0
+ loader-utils: 1.4.2
memory-fs: 0.4.1
micromatch: 3.1.10
mkdirp: 0.5.6